流媒体协议之HLS

流媒体(Streaming media)是指将一连串的多媒体资料压缩后,经过互联网分段发送资料,在互联网上即时传输影音以供观赏的一种技术与过程,此技术使得资料数据包得以像流水一样发送,如果不使用此技术,就必须在使用前下载整个媒体文件。实时流媒体是指互联网内容的实时传输,就像电视直播通过电视信号在电波上播放内容一样。互联网流媒体直播需要一种形式的源媒体(如摄像机、音频接口、屏幕捕捉软件)、将内容数字化的编码器、媒体发布者和内容传输网络来分发和传递内容。

HLS简介

HTTP Live Streaming,缩写为HLS,是由苹果公司提出基于HTTP的流媒体网络传输协议。是苹果公司QuickTime X和iPhone软件系统的一部分。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。在开始一个流媒体会话时,客户端会下载一个包含元数据的扩展 M3U (m3u8) 播放列表文件,用于寻找可用的媒体流。 —— https://zh.wikipedia.org/wiki/HTTP_Live_Streaming

上面是维基百科对HLS的解释,其实不难理解,当我们在爱奇艺或者腾讯视频中追剧、看电影或者刷B站,都是随时可以调整进度的,有时候一个视频特别大,比如一个高清的电影要是下载下来得十多个GB甚至高达上百GB,但是在观看视频的时候却可以手动拖动进度条调整播放进度,很显然不可能把整个视频都下载下来再进行调整,所以在这种场景下,服务器分段发送资料就显得尤为重要,只发送用户当前即将要观看的一小段视频,而不是传输整个视频。

HLS支持以下内容:

  • 直播和预录内容(视频点播或VOD
  • 具有不同码率的多个备用流
  • 根据网络带宽变化对流进行智能切换
  • 媒体加密和用户身份验证

下图显示了HTTP Live Stream的组件:

所以用比较通俗的话来讲,HLS的基本实现原理为将一个大的媒体文件进行分片,将该分片文件资源路径记录于m3u8文件内,所以m3u8文件是记录索引的纯文本文件,打开它时播放软件并不是播放它,而是根据它的索引找到对应的音视频文件的网络地址进行在线播放,而且m3u8文件其中附带一些额外描述(比如该资源的多带宽信息···)用于提供给客户端,客户端依据该m3u8文件即可获取对应的媒体资源,进行播放,如下图所示: 我们观看视频的时候,往往可以根据网速选择360P、720P、1080P等清晰度,其实切换清晰度的本质就是切换不同的流而已,对于爱奇艺这类视频网站往往也会准备不同码率的媒体流,用于给用户在网络情况较差的时候也能有流畅的观看体验:

m3u8文件内容

m3u8文件实质是一个视频点播列表,或者实时播放列表、或者是一个主播放列表,或者活动播放列表。无论是哪种播放列表,其内部文字使用的都是UTF-8编码。在看m3u8的文件内容的时候需要注意以下几点:

1、m3u8 文件都是UTF-8编码。

2、m3u8 文件的每一行要么是一个URL,要么是空行,要么就是以#开头的字符串。不能出现空白字符,除了显示声明的元素。m3u8 文件中以#开头的字符串要么是注释,要么就是标签。标签以 #EXT 开头,大小写敏感。

点播列表

当m3u8文件作为点播列表时,其内部信息记录的是一系列媒体片段资源的URL,客户端只需按顺序下载这些片段资源,依次进行播放即可,下面的代码是视频点播播放列表的示例:

 1#EXTM3U
 2#EXT-X-PLAYLIST-TYPE:VOD
 3#EXT-X-TARGETDURATION:10
 4#EXT-X-VERSION:4
 5#EXT-X-MEDIA-SEQUENCE:0
 6#EXTINF:10.0,
 7http://example.com/movie1/fileSequenceA.ts
 8#EXTINF:10.0,
 9http://example.com/movie1/fileSequenceB.ts
10#EXTINF:10.0,
11http://example.com/movie1/fileSequenceC.ts
12#EXTINF:9.0,
13http://example.com/movie1/fileSequenceD.ts
14#EXT-X-ENDLIST

以下是视频点播播放列表示例中使用的标签:

**EXTM3U:**表明该文件是一个扩展的M3U文件,即m3u8文件,每个M3U文件必须将该标签放置在第一行。

**EXT-X-PLAYLIST-TYPE:**提供适用于整个播放列表文件的可变性信息。该标签的值可能是EVENTVOD。如果标签存在并且值为EVENT,则服务器不得更改或删除播放列表文件的任何部分(尽管它可以在其后添加行)。如果标签存在并且值为VOD,则播放列表文件不得更改。

**EXT-X-TARGETDURATION:**指定每个视频段最大的时长(以秒为单位)

**EXT-X-VERSION:**表示播放列表文件的兼容版本。播放列表媒体及其服务器必须遵守定义该协议版本的HTTP Live Streaming规范的IETF Internet-Draft最新版本的所有规定。

**EXT-X-MEDIA-SEQUENCE:**指示出现在播放列表文件中的第一个URL的序列号。播放列表中的每个媒体文件URL都有一个唯一的整数序列号。URL的序列号比其前面的URL的序列号高1,媒体序号与文件名无关。

**EXTINF:**记录标记,描述由紧随其后的URL标识的媒体文件。每个媒体文件URL必须带有一个EXTINF标记。该标签包含一个duration属性,该属性是一个整数或浮点数,以十进制位置表示法指定视频段的时长(以秒为单位)。该值必须小于或等于EXT-X-TARGETDURATION中规定的时间。

重点:建议始终使用浮点型指定时长,这可以让客户端在定位流时,减少四舍五入错误。但是如果兼容版本号EXT-X-VERSION小于3,那么必须使用整型。

EXT-X-ENDLIST:表示不会再有媒体文件添加到播放列表文件,也就是m3u8文件的结尾。点播列表类型的m3u8通常会带有EXT-X-ENDLIST标签,因为其视频片段不会改变;而直播类型的m3u8初始化时一般不会有EXT-X-ENDLIST标签,暗示有新的文件会添加到播放列表末尾,因此也需要客户端定时获取该m3u8文件,以获取新的媒体片段资源,直到访问到EXT-X-ENDLIST标签才停止。

上面的播放列表示例为媒体文件播放列表条目使用了完整路径名,虽然这是可以的,但使用相对路径名更好一些。因为相对路径名比绝对路径名更容易移植,并且相对于播放列表文件的URL,对单个播放列表条目使用完整路径名通常会比使用相对路径名产生更多文本。下面是与上面相同的播放列表的相对路径表示的m3u8文件:

 1#EXTM3U
 2#EXT-X-PLAYLIST-TYPE:VOD
 3#EXT-X-TARGETDURATION:10
 4#EXT-X-VERSION:4
 5#EXT-X-MEDIA-SEQUENCE:0
 6#EXTINF:10.0,
 7fileSequenceA.ts
 8#EXTINF:10.0,
 9fileSequenceB.ts
10#EXTINF:10.0,
11fileSequenceC.ts
12#EXTINF:9.0,
13fileSequenceD.ts
14#EXT-X-ENDLIST

实时播放列表

实时播放列表通常用于直播场景,通过在创建新媒体文件并在其可用时从文件中删除媒体URI来更新索引文件,这个时候m3u8文件中就不存在EXT-X-ENDLIST标签,新的视频媒体文件就可以被添加到m3u8文件中,下面是一个实时播放列表(直播)的m3u8文件示例:

 1#EXTM3U
 2#EXT-X-TARGETDURATION:10
 3#EXT-X-VERSION:4
 4#EXT-X-MEDIA-SEQUENCE:1
 5#EXTINF:10.0,
 6fileSequence1.ts
 7#EXTINF:10.0,
 8fileSequence2.ts
 9#EXTINF:10.0,
10fileSequence3.ts
11#EXTINF:10.0,
12fileSequence4.ts
13#EXTINF:10.0,
14fileSequence5.ts

这里面在点播列表里出现的标签就不再介绍了,如#EXTM3U#EXT-X-TARGETDURATION#EXT-X-VERSION#EXTINF#EXT-X-MEDIA-SEQUENCE等标签。

对于EXT-X-MEDIA-SEQUENCE标签需要注意一点:从播放列表文件中删除的每个媒体URI,标记值必须增加1。媒体URI必须按照它们在播放列表中出现的顺序从播放列表文件中删除。更新的索引文件将移动窗口呈现为连续的流。看下面两个示例就明白了,从上面的m3u8更新到下面的m3u8文件:

 1#EXTM3U
 2#EXT-X-TARGETDURATION:10
 3#EXT-X-VERSION:4
 4#EXT-X-MEDIA-SEQUENCE:2
 5#EXTINF:10.0,
 6fileSequence2.ts
 7#EXTINF:10.0,
 8fileSequence3.ts
 9#EXTINF:10.00,
10fileSequence4.ts
11#EXTINF:10.00,
12fileSequence5.ts
13#EXTINF:10.0,
14fileSequence6.ts

随着添加新媒体URI,播放列表将继续更新:

 1#EXTM3U
 2#EXT-X-TARGETDURATION:10
 3#EXT-X-VERSION:4
 4#EXT-X-MEDIA-SEQUENCE:4
 5#EXTINF:10.00,
 6fileSequence4.ts
 7#EXTINF:10.00,
 8fileSequence5.ts
 9#EXTINF:10.0,
10fileSequence6.ts,
11#EXTINF:10.0,
12fileSequence7.ts,
13#EXTINF:10.0,
14fileSequence8.ts,
15#EXTINF:10.0,
16fileSequence9.ts

活动播放列表

#EXT-X-PLAYLIST-TYPE的值为EVENT的时候,那么就表示这是一个活动播放列表。当允许用户搜索事件中的任何点(例如音乐会或体育赛事)时,通常会使用事件播放列表。当#EXT-X-PLAYLIST-TYPE的值为EVENT的时候,就无法从播放列表中删除任何内容;只能在文件末尾附加新的句段。新的段将添加到文件的末尾,直到事件结束为止,此时将添加EXT-X-ENDLIST标签。以下示例显示了用新的媒体URI更新的播放列表,并且该事件已结束:

 1#EXTM3U
 2#EXT-X-PLAYLIST-TYPE:EVENT
 3#EXT-X-TARGETDURATION:10
 4#EXT-X-VERSION:4
 5#EXT-X-MEDIA-SEQUENCE:0
 6#EXTINF:10.0,
 7fileSequence0.ts
 8#EXTINF:10.0,
 9fileSequence1.ts
10#EXTINF:10.0,
11fileSequence2.ts
12#EXTINF:10.0,
13fileSequence3.ts
14#EXTINF:10.0,
15fileSequence4.ts
16
17// List of files between 4 and 120 go here.
18
19#EXTINF:10.0,
20fileSequence120.ts
21#EXTINF:10.0,
22fileSequence121.ts
23#EXT-X-ENDLIST

主播放列表

提供多个播放列表文件以提供相同内容的不同码率,主播放列表描述了内容的所有可用的不同码率的媒体源。每个媒体源都是具有特定码率的版本,并包含在单独的播放列表中。客户端根据测得的网速切换到最合适的媒体源。调整客户端的播放器以最大程度地减少播放停顿,从而为用户提供最佳的流媒体体验。

主播放列表不会重新读取。客户端解析完主播放列表后,便会假设不同码率的媒体源未发生变化。一旦客户端在各个媒体源播放列表之一上看到EXT-X-ENDLIST标签,流就会结束。以下示例显示了一个主播放列表,它定义了五个不同的媒体源:

 1#EXTM3U
 2#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
 3http://example.com/low/index.m3u8
 4#EXT-X-STREAM-INF:BANDWIDTH=240000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
 5http://example.com/lo_mid/index.m3u8
 6#EXT-X-STREAM-INF:BANDWIDTH=440000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
 7http://example.com/hi_mid/index.m3u8
 8#EXT-X-STREAM-INF:BANDWIDTH=640000,RESOLUTION=640x360,CODECS="avc1.42e00a,mp4a.40.2"
 9http://example.com/high/index.m3u8
10#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS="mp4a.40.5"
11http://example.com/audio/index.m3u8

同样的,在之前出现的标签在这里不在赘述。

**EXT-X-STREAM-INF:**该属性指定了一个媒体源,该属性值提供了该备份源的相关信息。

AVERAGE-BANDWIDTH:(可选,但建议使用)表示该媒体流的平均速率,单位:每秒传输的比特数。 BANDWIDTH:(必需)表示该媒体流的最大速率,单位:每秒传输的比特数。 FRAME-RATE:(可选,但建议使用)一个浮点值,它描述该媒体流中的最大帧率。 HDCP-LEVEL:(可选)指示使用的加密类型。有效值为TYPE-0NONE。输出受HDCP保护的话需要设置TYPE-0,否则流无法播放。 RESOLUTION:(可选,但建议使用)可选的显示尺寸,以像素为单位,以该尺寸显示播放列表中的所有视频。任何包含视频的流都应包含此参数。 VIDEO-RANGE:(需要,取决于编码)值为SDRPQ的字符串。如果未指定传输特性代码1、16或18,则必须省略此参数。 CODECS:(可选,但建议使用)带引号的字符串,其中包含用逗号分隔的格式列表,其中每种格式均指定播放列表文件中媒体片段中存在的媒体样本类型。有效格式标识符是RFC 6381 [RFC6381]定义的ISO文件格式名称空间中的标识符。

尽管CODECS参数是可选的,但每个EXT-X-STREAM-INF标记都应包含该属性。 此属性提供解码特定流所需的编解码器的完整列表。 它允许客户端区分仅是音频的变体和同时具有音频和视频的媒体源,则客户端在切换流时会利用此信息来提供更好的用户体验。

参考资料

1、 Example Playlists for HTTP Live Streaming

2、 HTTP Live Streaming

2、 https://zh.wikipedia.org/wiki/HTTP_Live_Streaming