HLS 协议入门指南以及延迟优化策略

HLS是什么

HLS是HTTP Live Streaming 的缩写,是由苹果公司推出的基于HTTP的能自适应的流媒体传输协议,常用于直播和点播。

HLS 的工作原理是当推流端把视频流推到服务端后,服务端会把视频流切割成一个个小的媒体文件(一般是ts文件,也有可能是fmp4文件)。然后服务器会把这些媒体文件的播放地址按照播放顺序罗列到一个索引文件当中,供客户端查找可以的媒体流。这个索引文件也就是我们平时在播放hls视频的时候,最常见到的m3u8文件。

image.png

码率自适应

为了能够适应观众的不同网络环境,服务端会以不同的比特率和分辨率创建多个流,并把不同流的媒体文件地址放到了不同的索引文件(media playlist)当中,并最终把不同流的索引文件的地址放到一个主索引文件中(master playlist)。

image.png

客户端在播放HLS媒体流时,能够基于当前的网络情况,选择当前可流畅播放的最高分辨率的媒体流进行播放。

image.png

M3U8文件详解

对于HLS客户端来讲,最先要面对的并需要播放的媒体文件,而是告知客户端从何获取媒体文件的索引(M3U8)文件。所以对于HLS客户端来说,最首要的事情是对M3U8文件进行解析。

从上面的介绍我们能够知道,索引文件分为两种类型,一种是 master playlist,一种是 media playlist。

media playlist

media playlist 内部存放着一系列媒体片段资源,客户端只要顺序播放这些媒体片段资源,就能够完整地播放服务端提供地媒体资源。

media playlist 文件地格式如下所示:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:11
#EXTINF:10.000,
url_462/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_463/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_464/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_465/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_466/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_467/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_468/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:9.950,
url_469/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.050,
url_470/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_471/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_472/193039199_mp4_h264_aac_hd_7.ts
#EXT-X-ENDLIST

master playlist

master playlist 文件内记录着多个 media playlist 文件的地址,这些 media playlist 文件指向到的媒体文件的码率、格式各不相同。同时,还能够提供不同语言的音频文件,以及不同角度拍摄的视频文件等等。客户端能够根据用户不同的网络环境,或者是不同的语言、兴趣爱好的因素选择合适的媒体资源。

maser playlist 文件的格式如下所示:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=1280x720,NAME="720"
url_0/193039199_mp4_h264_aac_hd_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=246440,CODECS="mp4a.40.5,avc1.42000d",RESOLUTION=320x184,NAME="240"
url_2/193039199_mp4_h264_aac_ld_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=460560,CODECS="mp4a.40.5,avc1.420016",RESOLUTION=512x288,NAME="380"
url_4/193039199_mp4_h264_aac_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=836280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=848x480,NAME="480"
url_6/193039199_mp4_h264_aac_hq_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=6221600,CODECS="mp4a.40.2,avc1.640028",RESOLUTION=1920x1080,NAME="1080"
url_8/193039199_mp4_h264_aac_fhd_7.m3u8

M3U8文件格式解析

playlist 文件定义

M3U8 文件必须以 UTF-8 [ RFC3629 ] 进行编码,不能包含 Byte Order Mark(BOM)字节序, 不能包含 UTF-8 控制字符(U+0000 到 U+001F 和 U+007F 至 U+009F),CR (U+000D) 和 LF 除外(U+000A)。

M3U8 文件的每行的内容只能是以下三种中的其中一种。

  • URI
  • 空行
  • 以 # 开头的字符串

除了明确指定的元素之外不能有空格的存在。

以字符“#”开头的行要么是注释,要么是标签。标签以 #EXT 开头,大小写敏感。其他以“#”开头的行都是注释,客户端解析时应该忽略掉。

属性列表

某些特定的标签的值是一个属性列表。

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=1280x720,NAME="720"

属性列表以逗号作为分隔符,分离出多组不带空格的 AttributeName=AttributeValue 对。如上面的EXT-X-STREAM-INF属性能够分离出:

# AttributeName=AttributeValue
PROGRAM-ID=1,
BANDWIDTH=2149280,
CODECS="mp4a.40.2,avc1.64001f",
RESOLUTION=1280x720,
NAME="720"

标签

标签用于说明 M3U8 文件的全局参数,以及 切片文件和媒体播放列表 的信息。标签可分以下几种:

  • 基础标签(Basic Tags)
  • 媒体片段标签(Media Segment Tags)
  • 媒体播放列表标签(Media Playlist Tags)
  • 主播放列表标签 (Master Playlist Tags)
  • 媒体播放列表或主播放列表都使用的标签(Media or Master Playlist Tags)

下面会分别介绍这几种类型中一些比较重要和常用的标签。

基础标签(Basic Tags)

基础标签既能够媒体播放列表(Media Playlist)中使用,也可以在主播放列表(Master Playlist)中使用。

  • EXTM3U:每个 M3U8 文件必须将该标签放置在第一行,表明该文件是一个 M3U8 文件。
  • EXT-X-VERSION:表示 HLS 协议的版本号,该标签与流媒体的兼容性相关。该标签为一个全局参数,能够在整个 M3U8 文件中进行使用。每个 M3U8 文件内最多只能出现一个版本定义。如果 M3U8 文件内不包含该标签,则默认1。

媒体播放列表标签(Media Playlist Tags)

媒体播放列表标签为 M3U8 文件的全局参数信息并且只能在 M3U8 文件中至多出现一次。媒体播放列表标签只能出现而在媒体播放列表(Media Playlist)当中,而不能出现在主播放列表(Master Playlist)中。

EXT-X-TARGETDURATION

表示所有媒体片段最大的时长(单位秒),为必选标签。

#EXT-X-TARGETDURATION:<s>

<meta charset="utf-8">

EXT-X-MEDIA-SEQUENCE

表示播放列表第一个 URL 片段文件的序列号。每个媒体片段 URL 都拥有一个唯一的整型序列号,每个媒体片段序列号按出现顺序依次加 1。如果该标签未指定,则默认序列号从 0 开始。媒体片段序列号与片段文件名无关。

#EXT-X-MEDIA-SEQUENCE:<number>
EXT-X-ENDLIST

表明 文件的结束,该标签可出现在 M3U8 文件任意位置,一般是结尾。

当media playlist中出现该标签时,则说明改媒体流是一个点播源而不是一个直播源

EXT-X-PLAYLIST-TYPE

用于指明流媒体类型,该标签为可选标签。
其格式为:

#EXT-X-PLAYLIST-TYPE:<type-enum>

其中:type-enum可为:

  • VOD:即 Video on Demand,表示该媒体流为点播源,因此服务器不能更改该 M3U8 文件;

  • EVENT:表示该媒体流为直播源,服务器不能更改或删除该 M3U8 文件任意部分内容,但是可以在文件末尾添加新内容。

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

EXT-X-I-FRAMES-ONLY

该标签全局生效,表示每个媒体片段都是一个 I 帧。I 帧编码时不需要依赖于其他帧,因此可以通过I 帧进行快速播放等操作。

#EXT-X-I-FRAMES-ONLY

如果媒体播放列表设置了 EXT-X-I-FRAMES-ONLY,那么媒体片段的时长(EXTINF 标签的值)即为当前媒体分片的 I 帧开始到下一个 I 帧出现的时长。
媒体资源如果包含I 帧切片,那么必须提供媒体初始化块或者通过 EXT-X-MAP 标签提供媒体初始化块的获取途径,这样客户端就能通过这些I 帧切片以任意顺序进行加载和解码。

媒体片段标签(Media Segment Tags)

每个媒体片段的 URI 前面都有一系列媒体片段标签,用于描述媒体片段的信息。有些片段标签只对其后切片资源有效,有些片段标签对其后所有切片都有效,直到后续遇到另一个相同的标签描述。媒体片段类型标签只能出现而在媒体播放列表(Media Playlist)当中,而不能出现在主播放列表(Master Playlist)中(因为媒体片段URI只会出现在媒体播放列表中)。

EXTINF

表示其后 URI 指定的媒体片段时长(单位为秒)。每个 URL 媒体片段之前必须指定该标签。该标签的使用格式为:

#EXTINF:<duration>,[<title>]

其中,duration是一个十进制的整数或者浮点数,其值必须小于或等于 EXT-X-TARGETDURATION 指定的值。

EXT-X-BYTERANGE

该标签表示接下来的媒体片段资源只是其 URI 指定的媒体片段资源的局部范围(即截取 URI 媒体资源部分内容作为下一个切片)。该标签只对其后一个 URI 起作用。其格式为:

#EXT-X-BYTERANGE:<length>[@<offset>]

其中, length是一个十进制整型,表示截取片段大小(单位:字节)。可选参数offset也是一个十进制整型,指示截取起始位置(以字节表示,在 URI 指定的资源开头移动该字节位置后进行截取)。如果offset未指定,则截取起始位置从上一个该标签截取完成的下一个字节(即上一个length+offset+1)开始截取。

如果没有指定该标签,则表明的切分范围为整个 URI 资源片段。

使用 EXT-X-BYTERANGE 标签要求兼容版本号 EXT-X-VERSION 大于等于 4。

EXT-X-DISCONTINUITY

表明标签之前媒体片段与下一个媒体片段之间存在中断。
当以下任一情况变化时,必须使用该标签:

  1. 文件格式(file format)
  2. 数字(number),类型(type),媒体标识符(identifiers of tracks)
  3. 时间戳序列(timestamp sequence)

当以下任一情况变化时,建议使用该标签:

  1. 编码参数(encoding parameters)
  2. 编码序列(encoding sequence)

EXT-X-DISCONTINUITY 最常用的场景是在媒体流中插入广告。插入广告的例子:

#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
ad0.ts
#EXTINF:8.0,
ad1.ts
#EXT-X-DISCONTINUITY
#EXTINF:10.0,
movieA.ts
#EXTINF:10.0,
movieB.ts
...
#EXT-X-ENDLIST
EXT-X-KEY

该标签用于为加密的媒体片段指定解密方法。
该标签对所有 媒体片段 和 由标签 EXT-X-MAP 声明的围绕其间的所有 媒体初始化块(Meida Initialization Section) 都起作用,直到遇到下一个 EXT-X-KEY(若 m3u8 文件只有一个 EXT-X-KEY 标签,则其作用于所有媒体片段)。多个 EXT-X-KEY 标签如果最终生成的是同样的秘钥,则他们都可作用于同一个媒体片段。

#EXT-X-KEY:<attribute-list>
EXT-X-MAP

用于指明获取媒体初始化块(Meida Initialization Section)的方法。该标签对其后所有媒体片段生效,直至遇到另一个 EXT-X-MAP 标签。拥有改标签的 M3U8 文件内的所有媒体片段必须加上这个初始化片段才能组成一个完整的媒体文件。
其格式为:

#EXT-X-MAP:<attribute-list>

其属性列表取值范围如下:

  • URI:由引号包裹的字符串,指定了包含媒体初始化块的资源的路径。该属性为必选参数。

  • BYTERANGE:由引号包裹的字符串,指定了媒体初始化块在 URI 指定的资源的位置(片段)。
    该属性指定的范围应当只包含媒体初始化块。
    该属性为可选参数,如果未指定,则表示 URI 指定的资源就是全部的媒体初始化块。

EXT-X-PROGRAM-DATE-TIME

该标签使用一个绝对日期/时间表明第一个媒体片段的取样时间。
其格式为:

#EXT-X-PROGRAM-DATE-TIME:<date-time-msec>

其中,date-time-msec是一个 ISO/IEC 8601:2004 规定的日期格式,形如:YYYY-MM-DDThh:mm:ss.SSSZ。
例如:

 #EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00

主播放列表标签 (Master Playlist Tags)

主播放列表(Master Playlist)定义了备份流,多语言翻译流和其他全局参数。主播放列表标签只能出现在主播放列表(Master Playlist )中而不能出现在媒体播放列表(Media Playlist)中。

EXT-X-MEDIA

用于指定相同内容的媒体文件的不同语言翻译的媒体文件列表资源。
比如,通过三个 EXT-X-MEIDA 标签,可以提供包含英文,法语和西班牙语版本的相同内容的音频资源,或者通过两个 EXT-X-MEDIA 提供两个不同拍摄角度的视频资源。

EXT-X-STREAM-INF

该属性指定了一个备份源,并提供了该备份源的相关信息。

EXT-X-I-FRAME-STREAM-INF

该标签表明媒体播放列表文件包含多种媒体资源的 I帧。

EXT-X-SESSION-KEY

该标签允许主播放列表(Master Playlist)指定媒体播放列表(Meida Playlist)的加密密钥。这使得客户端可以预先加载这些密钥,而无需从媒体播放列表中获取。其属性列表与 EXT-X-KEY 相同,除了 METHOD 属性的值必须不为NONE。

媒体播放列表或主播放列表都使用的标签(Media or Master Playlist Tags)

以下标签可同时设置于主播放列表(Master Playlist)和媒体播放列表(Media Playlist)中。但是对于在主播放列表中设置了的标签,不应当再次设置媒体播放列表中。同时出现在两者播放列表的相同标签必须具备相同的值。这些标签在播放列表中不能出现多次(只能使用一次)。

EXT-X-INDEPENDENT-SEGMENTS

该标签对列表内所有媒体片段均有效,用于表明对于一个媒体片段中的所有媒体样本均可独立进行解码,而无须依赖其他媒体片段信息。

#EXT-X-INDEPENDENT-SEGMENTS

如果该标签出现在主播放列表中,则其对所有媒体播放列表的所有媒体片段都生效。

EXT-X-START

该标签为可选标签,用于表示播放列表播放起始位置。默认情况下,客户端应该使用该标签指定的位置进行播放。

#EXT-X-START:<attribute-list>

其参数属性列表的取值范围如下:

  • TIME-OFFSET:该属性值为一个带符号十进制浮点数(单位:秒)。
    一个正数表示以播放列表起始位置开始的时间偏移量。
    一个负数表示播放列表上一个媒体片段最后位置往前的时间偏移量。
    该属性的绝对值应当不超过播放列表的时长。如果超过,则表示到达文件结尾(数值为正数),或者达到文件起始(数值为负数)。
    如果播放列表不包含 EXT-X-ENDLIST 标签,那么 TIME-OFFSET 属性值不应当在播放文件末尾三个切片时长之内。

  • PRECISE:该值为一个可枚举字符串。
    有效的取值为YESNO
    如果值为YES,客户端应当播放包含 TIME-OFFSET 的媒体片段,但不要渲染该块内优先于 TIME-OFFSET 的样本块。
    如果值为NO,客户端应当尝试渲染在媒体片段内的所有样本块。
    该属性为可选参数,未指定则认为NO

HLS直播延迟高的原因

HLS直播延迟主要由以下几个原因导致的:

  1. 客户端一般存在安全缓冲的时长。HLS协议不建议客户端选择开始时间到最后一个分片结束时间间隔小于最后一个分片时长加两倍的 EXT-X-TARGETDURATION 时长的分片作为首个分片进行播放。也就是说,一般而言,客户端应该从 M3U8 文件中倒数第三个或倒数第四个分片开始播放。
image.png
  1. 服务器更新 M3U8 文件的间隔。理论上HLS服务器需要大于等于一个EXT-X-TARGETDURATION的时长去更新 M3U8文件。

  2. CDN缓存机制。若当前源站 M3U8 已经更新到了第四个片段,但是CDN边缘节点还缓存着上一个版本的 M3U8文件(只包含3个片段)。此时就需要等文件的TTL过期,边缘节点才会去获取最新版本的 M3U8 文件。而这个缓存TTL也不能取消,如果每个端上的请求到达CDN边缘节点时都去找源站要最新版本,源站就可能会被流量冲垮。

  3. 客户端下载分片以及解码的时长。

  4. 网络延迟。

优化延迟的方法

我们可以从延迟原因中的第 1 和第 2 点入手,降低我们直播的延迟。

  1. 适当减少客户端的安全缓冲时长。在保证网络环境足够优越的场景下(如内网直播),可以适当降低客户端安全缓冲的时长。如hls.js可以通过修改liveSyncDurationCount(安全缓冲的分片数量,默认为3)和liveSyncDuration(安全缓冲的时长,单位为秒)来调整安全缓冲的时长。
  2. 尽量使用GOP(Group Of Picture)的时长来设置分片的时长,降低服务器更新 M3U8 文件的间隔。

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):