一、HLS 概述

HLS (HTTP Live Streaming), 是由 Apple 公司实现的基于 HTTP 的媒体流传输协议。他跟 DASH 协议的原理非常类似,通过将整条流切割成一个小的可以通过 HTTP 下载的媒体文件,然后提供一个配套的媒体列表文件给客户端,让客户端顺序地拉取这些媒体文件播放, 来实现看上去是在播放一条流的效果。HLS 目前广泛地应用于点播和直播领域。


在 HTML5 页面上使用 HLS 非常简单:


直接:



或者:



1、HLS 的优势


  • 客户端支持简单,只需要支持 HTTP 请求即可,HTTP 协议无状态,只需要按顺序下载媒体片段即可。

  • 使用 HTTP 协议网络兼容性好, HTTP 数据包也可以方便地通过防火墙或者代理服务器,CDN 支持良好。

  • Apple 的全系列产品支持,由于 HLS 是苹果提出的,      所以在 Apple 的全系列产品包括 iphone、 ipad、safari 都不需要安装任何插件就可以原生支持播放 HLS, 现在Android 也加入了对 HLS 的支持。


自带多码率自适应,Apple 在提出 HLS 时,就已经考虑了码流自适应的问题。


2、HLS 的劣势


  • 相比 RTMP 这类长连接协议,延时较高,难以用到互动直播场景。

  • 对于点播服务来说,由于 TS 切片通常较小,海量碎片在文件分发、一致性缓存、存储等方面都有较大挑战。


虽然HLS存在明显劣势,又拍云对此也有相应的解决方案。

二、HLS 协议详解


△HLS 整体架构图


上图可以看出,HLS总共有三个部分: Serve、CDN、Client。HLS 协议的主要内容是关于 M3U8 这个文本协议,其实生成与解析都非常简单,示例如下:


△简单的 Media Playlist


△包含多种比特率的 Master Playlist


HLS 通过 URI(RFC3986) 指向的一个 Playlist 来表示一个媒体流。一个 Playlist 可以是一个 Media Playlist 或者 Master Playlist, 使用 UTF-8 编码的文本文件,包含一些 URI 跟描述性的 tag。一个 Media Playlist 包含一个 Media Segments 列表,当顺序播放时,能播放整个完整的流。要想播放这个 Playlist,客户端需要首先下载他,然后播放里面的每一个 Media Segment。更加复杂的情况是,Playlist 是一个 Master Playlist,包含一个 Variant Stream 集合,通常每个 Variant Stream 里面是同一个流的多个不同版本(如:分辨率,码率不同)。


1、HLS Media Segments

  • 每一个 Media Segment 通过一个 URI 指定,可能包含一个 byte range。

  • 每一个 Media Segment 的 duration 通过 EXTINF      tag 指定。

  • 每一个 Media Segment 有一个唯一的整数 Media Segment Number。

  • 有些媒体格式需要一个 format-specific sequence 来初始化一个 parser,在 Media Segment 被 parse 之前,这个字段叫做 Media Initialization      Section,通过 EXT-X-MAP      tag 来指定。


2、支持Media Segment的格式


l  MPEG-2 Transport Streams


  • 即常见的 TS 文件。

  • RFC: ISO_13818。

  • Media Initialization Section: PAT(Program Association Table) 跟 PMT(Program Map Table)。

  • 每个 TS segment 必须值含一个 MPEG-2 Program。

  • 每一个 TS segment 包含一个 PAT 和 PMT,在 segment 的开始处,或者通过一个 EXT-X-MAP tag 来指定。


l  Fragmented MPEG-4


  • 即常提到的 fMP4。

  • RFC: ISOBMFF。

  • Media Initialization Section: ftyp box(包含一个高于 ios6 的 brand),ftyp box 必须紧跟在 moov box 之后,moov box 必须包含一个 trak box(对于每个 fMP4 segment 里面的 trafbox,包含匹配的 track_ID)。每个 trak box 应该包含一个 sample table,但是他的 sample count 必须为 0。mvhd box 跟 tkhd 的 duration 必须为 0。mvex box 必须跟在上一个 trakbox 后面。

  • 不像普通的 MP4 文件包含一个 moov box(包含 sample tables) 和一个 mdat box(包含对应的 samples),一个 fMP4 包含一个 moof box (包含 sample table 的子集),和一个 mdat box(包含对应的 samples)。

  • 在每一个 fMP4 segment 里面, 每一个 traf box 必须包含一个 tfdt box,fMP4 segment 必须使用 movie-fragment relative addressing。fMP4 segments 绝对不能使用外部的 data references。

  • 每一个 fMP4 segment 必须有一个 EXT-X-MAP tag。


l  Packed Audio


  • 一个 Packed Audio Segment 包含编码的 audio samples 和 ID3 tags。 简单的打包到一起,包含小的 framing,并且没有 per-sample timestamp。

  • 支持的 Packed Audio:AAC with ADTS framing [ISO_13818_7], MP3 [ISO_13818_3],AC-3 [AC_3],Enhanced AC-3 [AC_3]。

  • 一个 Packed Audio Segment 没有 Media Initialization Section。

  • 每一个 Packed Audio Segment 必须在他的第一个 sample 指定 timestamp 通过一个 ID3 PRIV tag。

  • ID3 PRIV owner identifier 必须是 com.apple.streaming.transportStreamTimestamp

  • ID3 payload 必须是一个 33-bit MPEG-2 Program Elementary Stream timestamp 的大端 eight-octet number,高 31 为设置为 0。


l  WebVTT


  • 一个 WebVTT Segment 是一个 WebVTT 文件的一个 section,WebVTT Segment 包含 subtitles。

  • Media Initialization Section:WebVTT heade。

  • 每一个 WebVTT Segment 必须有以一个 WebVTT header 开始,或者有一个 EXT-X-MAP tag 来指定。

  • 每一个 WebVTT header 应该有一个 X-TIMESTAMP-MAP 来保证音视频同步。


l  HLS Playlists


  • Playlist 文件的格式是起源于 M3U,并且继承两个 tag:EXTM3U 和 EXTINF

  • 下面的 tags 通过 BNF-style 语法来指定。

  • 一个 Playlist 文件必须通过 URI(.m3u8 或 m3u) 或者 HTTP Content-Type 来识别(application/vnd.apple.mpegurl 或 audio/mpegurl)。

  • 换行符可以用 \n 或者 \r\n

  • 以 # 开头的是 tag 或者注释, 以 #EXT 开头的是 tag,其余的为注释,在解析时应该忽略。

  • Playlist 里面的 URI 可以用绝对地址或者相对地址,如果使用相对地址,那么是相对于 Playlist 文件的地址。


l  Attribute Lists


  • 有的 tags 的值是 Attribute Lists。

  • 一个 Attribute List 是一个用逗号分隔的 attribute/value 对列表。

  • 格式为: AttributeName=AttributeValue


l  Basic Tags


Basic Tags 可以用在 Media Playlist 和 Master Playlist 里面。

  • EXTM3U:必须在文件的第一行,标识是一个 Extended M3U Playlist 文件。

  • EXT-X-VERSION:表示 Playlist 兼容的版本。


l  Media Segment Tags


每一个 Media Segment 通过一系列的 Media Segment tags 跟一个 URI 来指定。有的 Media Segment tags 只应用与下一个 segment,有的则是应用所有下面的 segments。一个 Media Segment tag 只能出现在 Media Playlist 里面。


  • EXTINF:用于指定 Media Segment 的 duration。

  • EXT-X-BYTERANGE:用于指定 URI 的 sub-range

  • EXT-X-DISCONTINUITY:表示不连续。

  • EXT-X-KEY:表示 Media Segment 已加密,该值用于解密。

  • EXT-X-MAP:用于指定 Media Initialization Section。

  • EXT-X-PROGRAM-DATE-TIME:和 Media Segment 的第一个 sample 一起来确定时间戳。

  • EXT-X-DATERANGE:将一个时间范围和一组属性键值对结合到一起。


l  Media Playlist Tags


Media Playlist tags 描述 Media Playlist 的全局参数。同样地,Media Playlist tags 只能出现在 Media Playlist 里面。


  • EXT-X-TARGETDURATION:用于指定最大的 Media Segment duration。

  • EXT-X-MEDIA-SEQUENCE:用于指定第一个 Media Segment 的 Media Sequence Number。

  • EXT-X-DISCONTINUITY-SEQUENCE:用于不同 Variant Stream 之间同步。

  • EXT-X-ENDLIST:表示结束。

  • EXT-X-PLAYLIST-TYPE:可选,指定整个 Playlist 的类型。

  • EXT-X-I-FRAMES-ONLY:表示每个 Media Segment 描述一个单一的 I-frame。


l  Master Playlist Tags


Master Playlist tags 定义 Variant Streams,Renditions 和 其他显示的全局参数。Master Playlist tags 只能出现在 Master Playlist 中。


  • EXT-X-MEDIA:用于关联同一个内容的多个 Media Playlist 的多种 renditions。

  • EXT-X-STREAM-INF:用于指定一个 Variant Stream。

  • EXT-X-I-FRAME-STREAM-INF:用于指定一个 Media Playlist 包含媒体的 I-frames。

  • EXT-X-SESSION-DATA:存放一些 session 数据。

  • EXT-X-SESSION-KEY:用于解密。


l  Media or Master Playlist Tags


这里的 tags 可以出现在 Media Playlist 或者 Master Playlist 中.,但是如果同时出现在同一个 Master Playlist 和 Media Playlist 中时,必须为相同值。


  • EXT-X-INDEPENDENT-SEGMENTS:表示每个 Media Segment 可以独立解码。

  • EXT-X-START:标识一个优选的点来播放这个 Playlist。


3、采用HLS协议的服务器端与客户端逻辑


不同的播放器客户端以及服务器端的拉取规则都有很多细节差异,以下流程仅供参考。


l  服务器端逻辑


  • 将媒体源切片成 Media Segment,应该优先从可以高效解码的时间点来进行切片(如: I-frame)。

  • 为每一个 Media Segment 生成 URI。

  • Server 需要支持 “gzip” 方式压缩文本内容。

  • 创建一个 Media Playlist 索引文件,EXT-X-VERSION 不要高于他需要的版本,来提供更好的兼容性。

  • Server 不能随便修改 Media Playlist,除了 Append 文本到文件末尾,按顺序移除 Media Segment URIs,增长 EXT-X-MEDIA-SEQUENCE 和 EXT-X-DISCONTINUITY-SEQUENCE,添加 EXT-X-ENDLIST 到文件尾。

  • 在最后添加 EXT-X-ENDLIST tag,来减少 Client reload Playlist 的次数。

  • 注意点播与直播服务器不同的地方是,直播的 m3u8 文件会不断更新,而点播的 m3u8 文件是不会变的,只需要客户端在开始时请求一次即可。


l  客户端逻辑


  • 客户端通过 URI 获取 Playlist,如果是 Master Playlist,客户端可以选择一个 Variant Stream 来播放。

  • 客户端检查 EXT-X-VERSION 版本是否满足。

  • 客户端应该忽略不可识别的 tags,忽略不可识别的属性键值对。

  •  加载 Media Playlist file。

  • 播放 Media Playlist file。

  • 重加载 Media Playlist file。

  • 决定下一次要加载的 Media Segment。

三、HLS优化技术解析

由于客户端每次请求 TS 或 M3U8 有可能都是一个新的连接请求,所以,我们无法有效的标识客户端,一旦出现问题,基本无法有效的定位问题, 因此一般工业级的服务器都会对传统的 HLS 做一些改进。本文主要介绍又拍云的 HLS+。


1、又拍云HLS+


l  Variant HLS


首先,下载一条又拍云的 M3U8 文件:



然后, 打开下载得到的 playlist 文件:



可以看出又拍云的 HLS+ 支持这种 Variant HLS 方式来标识一条 HLS 连接,同时可以看出,又拍云使用 uuid 来表示一条 HLS 连接。


l  HTTP 302


首先,以 HTTP 302 方式来请求播放地址。



打开 playlist 内容:


在跳转之后的地址存放真正的 playlist,同时也能够将 uuid 加入到了连接上。


总地来说,不管通过哪种方式,最终我们都能通过一个唯一的 id 来标识一条流,这样在排查问题时就可以根据这个 id 来定位播放过程中的问题。

四、HLS 延时分析

为了追求低延时效果,可以将切片切的更小,取片间隔做的更小,播放器未取到 3 个片就启动播放。但是这些优化方式都会增加 HLS 不稳定和出现错误的风险。

五、Demo