一、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 里面的traf
box,包含匹配的track_ID
)。每个trak
box 应该包含一个 sample table,但是他的 sample count 必须为 0。mvhd
box 跟tkhd
的 duration 必须为 0。mvex
box 必须跟在上一个trak
box 后面。 -
不像普通的 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 不稳定和出现错误的风险。