一、概述

HLS(HTTP Live Streaming)是苹果公司基于HTTP提出来的一种自适应码率的流媒体传输协议,编号RFC8216,苹果公司为HLS建立的官网是:HTTP Live Streaming (HLS) - Apple Developer,有需求的同学可以在其官网了解HLS的使用方法、功能扩展、性能优化等相关信息。HLS主要内容由HTTP + M3U8 + TS这三个部分共同组成,其中HTTP是最常见的应用层协议了,我们日常基本的浏览器上网就是基于HTTP协议;而TS是音视频编码标准MPEG-2中定义的一种媒体流封装复用格式,TS相关的详情可以参考我的上一篇文章《MPEG-2 TS流结构浅析》;既然HTTP和TS都是其他协议或标准中规定的内容,那么HLS的核心就只能是这个M3U8了。

直播模型架构全链路

在实际应用场景中,M3U8是以文件的形式出现,即xxx.m3u8或xxx.m3u这种文件格式。以如上图所示的直播模型架构链路图为例:

  1. 采集端将采集到的音视频媒体数据经过编码压缩、文件封装后,一般采用时延更低的RTMP协议推流到业务服务器;

  2. 业务服务器从RTMP提取到数据流后,为了适应播放端采用的HLS拉流协议,会将数据流分割成一个一个很小TS分片(一般每个分片在10秒左右);

  3. 播放端要播放某个直播内容时,服务器会先下发一个一级M3U8文件给客户端,这个一级M3U8文件中根据不同的网络环境(带宽),提供了不同码率的媒体流地址;

  4. 播放端选择并请求某个码率的媒体流地址后,服务器会下发一个二级M3U8文件,该文件中按顺序列出了一个个TS分片的下载地址;

  5. 播放端解析这个M3U8文件后,将这一个个TS分片下载下来,然后将它们拼接、解封装、解码,最终完成播放;

与其他的流媒体传输协议相比,HLS存在如下优势:

  1. 对比RTMP协议,HLS播放端不依赖指定插件,只需支持HTTP即可;

  2. 对比RTP协议,HLS协议的网络兼容性好,其基于HTTP的数据包可以方便地通过防火墙或者代理服务器,且容易使用内容分发网络CDN来完成数据流高效传输;

  3. 其自适应码率的特性,使其可以应用在不同的网络环境,提高观感体验;

劣势:

从本质上来说,HLS是以微观的点播实现宏观的直播,相比其他实时流形式的传输协议,其存在较高的时延;

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

二、M3U8

M3U8文件中的内容主要由标签(TAG)、属性列表(Attribute Lists)、播放地址(URI)构成。

其中播放地址是比较简单的,其一般单独占用一行。该地址一般由.m3u8、.m3u、.ts结尾(实际应用中这些后缀后面还可能会带着由问号(?)分割的其他应用参数),前两种结尾的播放地址代表当前是一个一级M3U8文件,这些地址指向的是一个个二级M3U8文件;而以.ts结尾的播放地址代表当前是一个二级M3U8文件,这些地址指向的是一个个具体的ts分片下载地址。

实际应用中访问或请求M3U8文件的方式有两种,一种是直接请求.m3u8或.m3u结尾的路径地址,另一种则是需要设置HTTP的Content-Type字段值为"application/vnd.apple.mpegurl" 或 "audio/mpegurl"。

2.1、属性列表

在一些标签后面跟随的是属性列表,属性列表是用逗号分割开来的键值对列表。如下面的语法格式所示,属性列表中的属性名、'='、属性值之间不能有空格,属性名可以是由数字[0,9]、大写字母[A,Z]、和'-'这些字符构成。

属性名=属性值

属性值的取值类型如下:

  • decimal-integer,十进制整数:由数字[0,9]组成的不带引号的数字串表示一个十进制整数,该数字串长度不能超过20位,取值则位于0到2^64-1之间;

  • hexadecimal-sequence,十六进制序列:由 数字[0,9]、大写字母[A,F]、且前缀为 0x 或 0X 组合成的不带引号的字符串序列,其长度取决于前面的属性名;

  • decimal-floating-point,无符号十进制浮点数:由数字[0,9]和小数点'.'组成的不带引号的数字串表示一个非负十进制浮点数;

  • signed-decimal-floating-point,有符号十进制浮点数:由数字[0,9]、负号'-'、和小数点'.'组成的不带引号的数字串表示一个十进制浮点数;

  • quoted-string,带引号字符串:携带一对双引号的字符串,双引号之间不能再出现回车符、换行符、双引号;

  • enumerated-string,枚举字符串:由属性名显式定义的一个不带引号的字符串,该类型中不能出现双引号、逗号、和空格;

  • decimal-resolution,十进制分辨率:由字符'x'分割的两个十进制整数,第一个整数表示水平像素尺寸,第二个整数表示垂直像素尺寸;

2.2、标签

在M3U8文件中,标签以#号开头,形如#EXTM3U。根据HLS文档的中定义,标签有如下四种分类:

  1. Basic Tags,基本标签:该类标签必须出现在每个M3U8文件中;

  2. Media Segment Tags,媒体片段标签:该类标签和其跟随的URI一起指定一个媒体片段。有些媒体片段标签仅作用于其下一行的片段,而其他的媒体片段标签作用于所有子序列片段,直到另外一个相同标签实例出现。一个媒体片段标签一定不能出现在主播放列表中;

  3. Media Playlist Tags,媒体播放列表标签:该类标签是描述媒体播放列表的全局参数。一个媒体播放列表中,每种类型的媒体播放列表标签只能出现一次。媒体播放列表标签不能出现在主播放列表中;

  4. Master Playlist Tags,主播放列表标签:主播放标签定义了Variant Stream、Rendition和其他显示的全局参数,主播放列表标签不能出现在媒体播放列表中;

其中的媒体片段(Media Segment)可以理解为一个TS分片;主播放列表(Master Playlist)可以理解为一级M3U8;媒体播放列表(Media Playlist)可以理解为二级M3U8;Variant Stream可翻译理解为可变流,其是针对不同的网络环境(带宽),提供的不同码率的媒体源;Rendition可翻译理解为译本(翻译的版本),其可以是对同一段视频提供的不同语种的音频或字幕,或者同一段视频不同拍摄视角的显示形式;

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

       注:关于最后的媒体播放列表标签或主播放列表标签类型,如果其中一个标签出现在主播放列表中,则不应出现在该主播放列表引用的任何媒体播放列表中,而且这些标签不能在播放列表中出现一次以上。

2.3、举例说明

        2.3.1、主播放列表

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000
http://example.com/low.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000
http://example.com/mid.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000
http://example.com/hi.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"
http://example.com/audio-only.m3u8

如上所示的代码段就是一个主播放列表(即一级M3U8)的简单例子,其中的#EXTM3U标签是每个M3U8文件所必须的基本标签,代表当前是一个媒体播放列表文件。之后的三个#EXT-X-STREAM-INF标签根据不同网络带宽,对同一个内容提供了高、中、低的三个不同码率的播放地址(即三个可变流)。最后一个#EXT-X-STREAM-INF标签则是通过CODECS属性指定了一个内容编码或封装格式是"mp4a.40.5"的纯音频播放地址。

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1280000
low/audio-video.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86000,URI="low/iframe.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=2560000
mid/audio-video.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=150000,URI="mid/iframe.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=7680000
hi/audio-video.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=550000,URI="hi/iframe.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"
audio-only.m3u8

  如上所示,第二个M3U8文件内容主要是在第一个M3U8文件基础上,对每一个可变流都额外提供了一个携带I帧的播放地址。

#EXTM3U
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="English", \
DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en", \
URI="main/english-audio.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Deutsch", \
DEFAULT=NO,AUTOSELECT=YES,LANGUAGE="de", \
URI="main/german-audio.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Commentary", \
DEFAULT=NO,AUTOSELECT=NO,LANGUAGE="en", \
URI="commentary/audio-only.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",AUDIO="aac"
low/video-only.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",AUDIO="aac"
mid/video-only.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",AUDIO="aac"
hi/video-only.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5",AUDIO="aac"
main/english-audio.m3u8

如上所示的第三个M3U8文件内容中,#EXT-X-STREAM-INF标签提供的四个播放源由于携带了"AUDIO="aac"这个属性列表,于是在文件开头的三个#EXT-X-MEDIA标签中,通过指定TYPE属性为AUDIO、GROUP-ID属性为"aac"、LANGUAGE属性取不同值,来为这四个播放源提供不同语言的音频译本。其中第一个标签中属性列表【DEFAULT=YES,AUTOSELECT=YES】表示默认情况下选择LANGUAGE="en"的这个音频译本,即默认是英文音频。

#EXTM3U
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", \
DEFAULT=YES,URI="low/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", \
DEFAULT=NO,URI="low/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", \
DEFAULT=NO,URI="low/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low"
low/main/audio-video.m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", \
DEFAULT=YES,URI="mid/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", \
DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", \
DEFAULT=NO,URI="mid/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid"
mid/main/audio-video.m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", \
DEFAULT=YES,URI="hi/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", \
DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", \
DEFAULT=NO,URI="hi/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi"
hi/main/audio-video.m3u8

如上所示的第四个M3U8文件,则是根据#EXT-X-STREAM-INF标签指定的三个可变流的属性列表VIDEO="low"、VIDEO="mid"、VIDEO="hi",对每种可变流都由#EXT-X-MEDIA标签指定三个不同的视频译本。此时#EXT-X-MEDIA标签的TYPE属性必须是VIDEO、GROUP-ID属性必须和可变流的VIDEO属性值相同,然后取不同的NAME属性以区分不同的视频译本。

2.3.2、媒体播放列表

#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:3
#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
#EXT-X-ENDLIST

如上所示的代码段是一个媒体播放列表(即二级M3U8)文件的例子,其中标签#EXT-X-TARGETDURATION指定当前M3U8文件中,所有TS分片的最长时长为10秒;标签#EXT-X-VERSION指定当前M3U8使用的HLS协议版本号是3;标签#EXTINF指定每个TS分片的时长,其下一行跟随当前TS分片的访问地址;最后的#EXT-X-ENDLIST标签则是用来结束当前M3U8文件;

需要说明的是只有点播内容的媒体播放列表结尾才会有#EXT-X-ENDLIST标签,且该媒体播放列表所在的M3U8文件内容是不能更改的,客户端只需要请求一次该M3U8文件;直播内容媒体播放列表所在的M3U8文件末尾没有#EXT-X-ENDLIST标签,HLS直播的服务器只能不断更新这个M3U8文件,以在文件末尾追加新的直播TS分片,因而客户端需要每隔一段时间就重新请求一下该直播的M3U8文件,以下载后来追加的TS的分片;

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

三、对服务端、客户端的要求

3.1、服务端

这里的服务端指的是文章一开头直播链路图中的业务服务器,业务服务器相当于一个中转站,其输入是采集端推流来的媒体流,经过整理、转码、分割等操作,将媒体流根据不同流媒体传输协议输出给播放端。

3.1.1、对媒体片段的要求

将采集端推来的媒体流分割成一个个小的片段,分割时应以播放端能有效解码为依据,例如在PES包边缘或关键帧的地方进行分割;

针对每个媒体片段,都应该提供一个能有效访问的URI,使播放端能够通过HTTP协议有效访问和立即下载每个媒体片段;

如果服务器支持HTTP Range字段,即能够对服务器资源进行部分请求,那么HLS协议中就可以使用#EXT-X-BYTERANGE标签将媒体片段指定为较大媒体源的子范围;

3.1.2、对播放列表的要求

对于每个媒体源都应该创建一个媒体播放列表(即M3U8文件),文件中按顺序罗列每个携带有效URI的媒体片段;

#EXT-X-PLAYLIST-TYPE标签值为VOD时代表点播媒体源,点播媒体源播放列表所在的M3U8文件是不能更改的,且该文件末尾应该提供#EXT-X-ENDLIST标签;#EXT-X-PLAYLIST-TYPE标签值为EVENT时代表直播媒体流,直播媒体流文件末尾不能有#EXT-X-ENDLIST标签。但需要服务端每隔一段时间对媒体播放列表进行更新,且更新动作只能是在文件末尾至少追加一个新的媒体片段。每更新一次媒体播放列表后,新版本的媒体播放列表的有效时间不能小于上一个版本有效时间的一半,不能大于上一版本的1.5倍,这样设定是为了约束播放列表的更新频率周期,使客户端能够以一个固定周期重新请求播放列表,以达到有效利用网络的目的。从播放端角度来看,这个更新动作需要是原子操作,否则会出现客户端与服务端媒体播放列表不一致;

如果服务器要下架或删除某个媒体源(访问会出现404或410),那么服务器需要在删除时,确保客户端下载的媒体播放列表在其有效时间范围内,能正常访问播放列表中的所有媒体片段,以防止客户端正在播放的内容突然中断;

3.1.4、对于部分标签的要求

关于序列号标签:无论是直播还是点播,播放列表中的#EXT-X-MEDIA-SEQUENCE标签或#EXT-X- DISCONTINUITY-SEQUENCE标签的值都应该是递增的;

关于目标时长标签:媒体播放列表中#EXT-X-TARGETDURATION标签的值是不能更改的,这个值一般取10秒;

关于不连续序列号标签:媒体播放列表中的每个媒体片段都有一个整数不连续序列号,这个序列号可以用来同步同一段内容的不同译本。一个媒体片段的不连续序列号的值 = #EXT-X-DISCONTINUITY-SEQUENCE标签的值(如果没有就取0)+ 该媒体片段URL行之前的#EXT-X-DISCONTINUITY标签的数量;

关于节目日期和时间标签:服务器可以通过使用#EXT-X-PROGRAM-DATE-TIME标签将绝对日期和时间与媒体片段关联起来,其主要是完成自然时间与媒体片段起始的时间映射。如果服务器提供了这种映射,那么就应该对每个使用了#EXT-X-PROGRAM-DATE-TIME标签的片段应用#EXT-X-DISCONTINUITY标签。需要注意的是#EXT-X-PROGRAM-DATE-TIME是一个媒体片段标签,所以他不能出现在媒体播放列表中,即M3U8文件开头的部分;

关于时间范围标签:如果一个播放列表范围内有任何的日期与时间映射到媒体片段,那么就不能从当前播放列表中删除#EXT-X-DATERANGE标签,且一个播放列表中各个#EXT-X-DATERANGE标签的ID属性不能重复。如果#EXT-X-DATERANGE标签携带了PLANNED-DURATION属性,即划定了DATERANGE的有效时长,那么就应该提供这个有效时间结束时的信号。比如添加另外一个具有相同ID属性的、相同DURATION或END-DATE属性的#EXT-X-DATERANGE标签。或者,如果当前#EXT-X-DATERANGE标签携带END-ON-NEXT=YES属性,那么就可以通过提供下一个新的DATERANGE的方式来结束前一个时间范围;

3.1.5、直播场景下的要求

直播场景下,服务器有可能会删除媒体播放列表中一些媒体片段的URI以限制其可用性,此时这个播放列表中必须包含#EXT-X-MEDIA-SEQUENCE标签。这些媒体片段必须按照它们在播放列表中出现的顺序从播放列表文件中删除,且他们的#EXT-X-MEDIA-SEQUENCE标签值必须是按顺序递增1。

当服务器从列表中删除一个媒体片段时,这个媒体片段必须在一段时间内对客户端保持可用,该时间等于该片段段的持续时间加上包含该片段的最长播放列表文件的持续时间。

一个媒体播放列表没有以#EXT-X-ENDLIST标签结束(即直播)的情境下,如果当前播放列表的总时长小于该列表中#EXT-X-TARGETDURATION标签指定的目标时长的三倍,那么就不能再删除播放列表中的任何媒体片段了;

如果服务器计划在通过HTTP传输到客户端后删除某个媒体片段,那么应该确保HTTP响应包含一个Expires头,以指示这个媒体片段的过期时间;

如果服务器希望从包含#EXT-X-DISCONTINUITY标签的媒体播放列表中删除媒体片段,那么这个媒体播放列表中必须包含#EXT-X-DISCONTINUITY-SEQUENCE标签,否则客户端将无法在各个译本中找到对应的媒体片段,即无法同步。

如果服务器从媒体播放列表中删除了EXT-X-DISCONTINUITY标签,那么就必须增加#EXT-X-DISCONTINUITY-SEQUENCE标签的值,以保证播放列表中仍然存留的媒体片段的不连续序列号保持不变;

3.1.6、加密相关的要求

媒体片段是可以加密的,每个加密的媒体片段必须有一个#EXT-X-KEY标签和一个URI,客户端可以根据URI获取一个解密密钥文件;

一个媒体片段只能用一个加密算法加密,即使用一个加密密钥和IV加密。但服务器可通过提供多个拥有不同KEYFORMAT属性值的#EXT-X-KEY标签,来提供多种检索密钥的方法;

服务器可以在密钥响应中设置HTTP Expires头来指定密钥可以缓存的时间;

在播放列表中,如果一个未加密的媒体片段前面有一个加密了的媒体片段,那么这个未加密的媒体片段需要添加一个METHOD属性为NONE的#EXT-X-KEY标签,以防止客户端将其误解为加密的媒体片段;

如果一个播放列表中包含任何一个携带#EXT-X-KEY标签的媒体片段,那么服务器就不能从播放列表文件中删除该标签,否则随后加载该播放列表的客户端将无法解密这些媒体片段;

3.1.7、可变流的要求

如果一个服务器可以提供多个媒体播放列表文件来为相同的显示内容提供不同的码率效果,那么也应该提供一个列出每个可变流的主播放列表文件,以允许客户端在不同编码之间动态切换。为了尽可能达到无缝切换,需要满足如下要求:

每个可变流必须显示相同的内容,且各个可变流中时间戳、不连续序列号、目标时长必须相同;

在一个可变流中会出现、但在另外一个可变流中不会出现的内容,必须出现在播放列表的开头或结尾的位置,且时长不能超过播放列表的目标时长;

如果任何一个媒体播放列表中有一个#EXT-X-PLAYLIST-TYPE标签,那么所有的媒体播放列表都必须具有相同值的#EXT-X-PLAYLIST-TYPE标签。如果播放列表包含一个带有VOD值的#EXT-X-PLAYLIST-TYPE标签,则每个可变流中的每个媒体播放列表的第一个片段必须从相同的媒体时间戳开始;

如果主播放列表中的任何一个媒体播放列表包含EXT-X-PROGRAM-DATE-TIME标签,那么该主播放列表中的所有媒体播放列表都必须包含这个具有日期和时间到媒体时间戳一致映射的标签;

每个可变流必须包含相同的时间范围,这个范围由具有相同的ID属性值的、包含相同属性列表的#EXT-X-DATERANGE标签标识;

3.2、客户端

3.2.1、加载媒体播放列表

客户端通过HTTP协议根据URI从服务器下载媒体播放列表,如果是一个主播放列表,则需要在提供的可变流中选择一个媒体播放列表进行下载、解析,解析媒体播放列表文件时需要忽略任何无法识别的标签和属性列表;

如果媒体播放列表包含#EXT-X-MEDIA-SEQUENCE标签,客户端应该假设在播放列表文件加载的时间及其持续时间内,其中的每个媒体片段都是不可用的;

当同一个播放列表重新加载时,客户端可以使用序列号来跟踪播放列表中媒体片段的位置。但因为不同可变流的媒体播放列表中可能有独立的媒体序列号,所以客户端不能假设不同变体流或译本中具有相同媒体序列号的片段在显示内容中具有相同的位置。此时,客户端应该使用每个片段在播放列表时间轴上的相对位置和它的不连续序列号来定位相应的片段;

客户端必须加载每个播放选择的译本的媒体播放列表文件,以便定位该播放译本的媒体。但是,为了防止服务器上不必要的加载,客户端不应该加载任何其他译本的播放列表文件。对于某些变体流,可以选择不包含#EXT-X-STREAM-INF标签指定的译本;

客户端必须在每次加载或重新加载媒体播放列表文件时检查该文件,以确定下一个要加载的媒体片段。第一个加载的片段通常是客户端选择先播放的片段,而一般情况下,下一个要加载的媒体片段是媒体序列号最低的那个,它大于上一个加载的媒体片段的媒体序列号;

3.2.2、播放媒体播放列表

开始播放时,客户端应该从媒体播放列表中选择首先播放哪个媒体片段。正常播放情况下,可以通过按媒体片段在播放列表中出现的顺序来播放。如果播放列表中不存在EXT-X-ENDLIST标签,为了正常播放,客户端不应该选择一个从起始位置到播放列表末尾总时长小于三倍目标时长(媒体播放列表的Target Duration)的片段开始播放;

在一个媒体片段中以及在一个媒体播放列表中的多个媒体段中,样本的编码参数应该保持一致。 然而,客户应该在遇到编码变化时处理它们,例如,通过缩放视频内容来适应分辨率的变化。 如果可变流包含分辨率属性,客户端应该在矩形框内显示所有与该分辨率属性值相同的视频;

客户端应该准备好处理特定类型的多个轨道(例如音频或视频),没有其他偏好的客户端应该选择其可以播放的最低数字轨道标识符的轨道;

客户端应该忽略传输流中他们不认识的私有流,虽然私有流可以用来支持相同流的不同设备,但是也会增加额外的网络负载;

在播放带有#EXT-X-DISCONTINUITY标签的媒体段之前,客户端必须准备好重置其解析器和解码器;

客户端应该尝试在媒体片段需要不间断播放之前加载它们,以补偿延迟和吞吐量的临时变化;

客户端可以使用#EXT-X-PROGRAM-DATE-TIME标签的值向用户展示节目起始时间,如果该时间包含时区信息,客户端可以考虑展示,否则应该展示本地时间;需要注意的是,播放列表中的日期与时间是指内容产生的时间(或其他时间),其与播放时间无关;

如果播放列表中的第一个#EXT-X-PROGRAM-DATE-TIME标签出现在一个或多个媒体片段URI之后,客户端应该使用#EXTINF时长和/或媒体时间戳从该标签向后(时间轴较早的一端)推断,以将日期与这些段关联起来。如果要将一个日期与任何其他没有直接应用#EXT-X-PROGRAM-DATE-TIME标签的媒体片段关联,客户端应该从播放列表中出现在该片段之前的最后一个#EXT-X-PROGRAM-DATE-TIME标签向前(时间轴较晚的一端)推断;

3.2.3、重新加载媒体播放列表

客户端必须定期重新加载媒体播放列表文件,以了解哪些媒体是当前可用的。除非该播放列表包含一个值为VOD的#EXT-X-PLAYLIST-TYPE标签,或者包含一个值为EVENT的#EXT-X-PLAYLIST-TYPE标签并且EXT-X-ENDLIST标签也同时存在。即点播情况下无需重新加载,直播才需要重新加载;

当客户端首次或重新加载一个播放列表文件时,如果发现该播放列表文件自最后一次加载后已经发生了改变,那么自客户端最后一次加载播放列表文件后开始,需要至少等待目标持续时间(Target Duration)后,再重新尝试加载该播放列表文件;如果发现它没有改变,那么必须等待目标持续时间的一半后再重新尝试。除此以外更高的重新加载频率会增加服务端的网络压力;

在重新加载媒体播放列表后,客户端应该验证其中的每个媒体片段与前一个媒体播放列表中具有相同媒体序列号的媒体片段是否具有相同的URI(和字节范围,如果指定了的话)。如果不相同,客户端应该停止播放,因为这通常表明服务端发生了错误;

为了减少服务器负载,客户端不应该重新加载当前未播放的可变流或可选译本的播放列表文件。 如果客户端决定切换到一个不同的可变流播放,那么就应该停止重新加载旧可变流的播放列表,并且开始加载新可变流的播放列表;

四、协议版本兼容情况

协议兼容性由#EXT-X-VERSION标签指定,默认为1, 包含与协议版本1不兼容的标签或属性的播放列表必须包含#EXT-X-VERSION标签。不同版本内容更新迭代情况如下:

Version 1:最低默认版本;

Version 2:#EXT-X-KEY标签新增了IV属性;

Version 3:#EXTINF标签的时长数值新增了浮点类型;

Version 4:新增了#EXT-X-BYTERANGE标签和#EXT-X-I-FRAMES-ONLY标签;

Version 5:新增了#EXT-X-MAP标签,以及#EXT-X-KEY标签新增了KEYFORMAT和KEYFORMATVERSIONS属性;

Version 6:媒体播放列表中不再包含EXT-X-I-FRAMES-ONLY的#EXT-X-MAP标签、删除了#EXT-X-STREAM-INF标签的PROGRAM-ID属性、删除了#EXT-X-I-FRAME-STREAM-INF标签;

Version 7:#EXT-X-MEDIA标签的INSTREAM-ID属性新增一个值:"SERVICE"、删除了#EXT-X-ALLOW-CACHE标签;

如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

Logo

音视频技术社区,一个全球开发者共同探讨、分享、学习音视频技术的平台,加入我们,与全球开发者一起创造更加优秀的音视频产品!

更多推荐