8

HLS及M3U8介绍

 3 years ago
source link: https://zhangferry.com/2020/09/20/hls_and_m3u8/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

HLS及M3U8介绍

20200919231216.png

MP4是我们常见的视频格式,往往我们在播放服务器视频时直接就是请求的MP4视频源。但其实这样并不好,MP4头文件[ftyp+moov]较大,初始化的播放需要下载完整的头文件并进行解析,之后再下载一定长度的可播视频片段才能进行播放。另外随着视频尺寸的增大头文件也会不断变大,这个初始播放时间也会更长。针对这种情况需要一种能加快视频初始解析的方法,HLS就是苹果提出的用于解决这种问题的方案。

HLS为HTTP Live Streaming的缩写,是由苹果公司提出的基于HTTP的流媒体网络传输协议,它可以同时支持直播和点播,还支持多清晰度、音视频双轨、字幕等功能。它的原理是将一整条视频分成多段小的视频,完整的播放是由这一个个片段拼接而成的。

HLS在移动端使用很广泛,当前支持HLS协议的客户端有:

  • iOS 3.0及以上,AVPlayer原生支持HLS
  • Android 3.0及以上
  • Adobe Flash Player 11.0及以上

它的大致原理是这样的:

20200919102604.png

1、采集音视频

2、在服务器编码音视频

3、编码后以MPEG-2的传输串流形式交由切片器(Stream Segmenter)

4、切片器创建索引文件和ts播放列表,索引文件用于指示音视频位置,ts为真实的多媒体片段

5、将上一步资源放到HTTP服务器上

6、客户端请求该索引文件进行播放,可以通过索引文件找到播放内容

参考资料:HTTP Live Streaming Document

实现HLS的一个关键步骤是上面的第四步,即索引文件和ts播放列表的组织。这里用到的就是M3U8格式。M3U8是Unicode版本的M3U,8代表使用的是UTF-8编码,M3U和M3U8都是多媒体列表的文件格式。

接下来我们以一条WWDC里的视频为例,看下M3U8格式是什么样子的,下文展示的并非M3U8格式所有字段,但会包含常用的字段,也足以帮助我们理解M3U8这一格式。

播放页面为:https://developer.apple.com/videos/play/wwdc2019/507 ,通过Charles进行抓包,我们可以得到视频播放过程中的M3U8文件。

20200919163827.png

在分析这个路径格式前,我们需要知道 M3U8 是有两种格式的,一种是作为主播放列表(Master Playlist)存在,它里面包含了音视频、字幕的一些说明和路径,主列表指示的路径是另一个M3U8文件,即另一个格式,作为播放存在的,它里面也有路径,指示的是片段(ts)文件,片段文件是真正的多媒体内容。

看抓包内容,hls_vod_mvp.m3u8为主列表文件,上面的0640.m3u8为视频列表文件。

M3U8格式说明

有时做测试,或者一些特殊情况时我们可能需要手动修改M3U8文件内容,所以需要对它的格式有一定的了解。该格式的定义写在RFC 8216号文件里,以下是一些注意事项:

  • M3U8文件必须以UTF-8进行编码,不能使用 Byte Order Mark(BOM)字节序, 不能包含 utf-8 控制字符(U+0000 ~ U_001F 和 U+007F ~ u+009F)
  • M3U8文件内容的每一行要么是空行,要么是一个URI,要么是以#开头的字符串,不能出现空白字符。
  • 内置标签都是#EXT开头的字符串,大小写敏感。
  • URI为内容路径,可以是相对路径也可以是绝对路径

Master M3U8 列表文件

20200919164450.png

主M3U8索引文件,一般用于指定多个索引源。我们先分析下该主m3u8文件hls_vod_mvp.m3u8的内容,它的头部是这样的

1
2
3
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS

#EXTM3U表明该文件是一个M3U格式,所有的M3U格式文件都应该把该内容放置到第一行。

#EXT-X-VERSIOn指示播放列表的兼容版本,当前为7。

#EXT-X-INDEPENDENT-SEGMENTS该标签表明对于一个媒体片段中的所有媒体样本均可独立进行解码,而无须依赖其他媒体片段信息。

再往下的内容是一些字幕说明,字幕内容不是必须的。

1
2
3
4
5
6
7
8
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/eng/prog_index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subsC",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/engc/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Japanese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="jpn",URI="subtitles/jpn/prog_index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subsC",NAME="Japanese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="jpn",URI="subtitles/jpnc/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Chinese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="zho",URI="subtitles/zho/prog_index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subsC",NAME="Chinese",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="zho",URI="subtitles/zhoc/prog_index.m3u8"

#EXT-X-MEDIA用于指定相同内容的多语言媒体列表资源。

TYPE为资源类型,可选内容有:AUDIO、VIDEO、SUBTITLES、CLOSED-CAPTIONS。

上面内容设置的是TYPE=SUBTITLES,即为字幕类型。

GROUP-ID为多语言翻译所属组,为必选参数

NAME为翻译流可读的描述信息,该值对应AVMediaSelectionOptiondisplayName

DEFAULTAUTOSELECTFORCED为三个BOOL值分别对应如果缺少必要信息时是否默认选中该翻译流,用户没有显示设置时播放该播放流,FORCED只针对字幕类型有效,用于标记当前自动选择该翻译流。

LANGUAGE用于指定语言类型,它是根据[ISO 639 语言码](https://www.w3.org/WAI/ER/WD-AERT/iso639.htm “ISO 639 语言码”)标准设置的。系统默认的播放器在选择字幕时,展示的字幕列表名称是根据这个值设定的。

URI为该资源的定位信息,在这里其对应的是一条字幕的M3U8文件。subtitles/eng/prog_index.m3u8是一个相对路径,

通过以上信息,我们可以分析出上面内容的含义为:当前视频支持三种字幕:英文,日文,中文。但每种语言都有两条EXT-X-MEDIA信息,他们的区别是分组不同,一个在subs分组,一个在subsC分组。为啥有两个分组,这个后面再说。

再往下看,为视频内容的索引:

1
2
3
#EXT-X-STREAM-INF:BANDWIDTH=827299,AVERAGE-BANDWIDTH=747464,CODECS="avc1.64001f,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="program_audio",SUBTITLES="subs"
0640/0640.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=360849,AVERAGE-BANDWIDTH=320932,CODECS="avc1.64001f",RESOLUTION=640x360,URI="0640/0640_I-Frame.m3u8"

EXT-X-STREAM-INF:该属性指定了一个备份源,即视频播放路径和一些视频的信息,以下是对应内容的配置:
BANDWIDTH为峰值比特率, 827299,为827299bit/s,即最高峰值时每秒消耗流量101KB。

AVERAGE-BANDWIDTH为平均比特率,747464

CODECS为编码信息,avc1.64001f,mp4a.40.2,avc代表的是h264编码格式,后面的64001f,是由16进制表示的编码参数,64,00,1f分别代表三个不同的参数值。mp4a是一种音频编码格式,后面的40.2代表音频的编码参数。

RESOLUTION为视频分辨率,当前一条视频源分辨率为640x360。

FRAME-RATE为最大帧率,29.970 代表当前播放的最大帧率为每秒29.970帧。

AUDIO是音频所在组,program_audio为对应音频组的名称。

SUBTITLES指示对应的字幕分组,subs为对应字幕组的名称。上面的字幕信息有个GROUP-ID,该值与之对应。

URI为内容路径,0640/0640.m3u8对应的就是该视频源的m3u8文件路径。这个可以在抓包信息里看到。

EXT-X-STREAM-INF下面是EXT-X-I-FRAME-STREAM-INF,表示播放列表文件中包含的多媒体资源的I帧(关键帧)。因为I帧只是一个画面,所以它不包含音频内容,其余参数跟视频内容格式一致。

在之后就是对应不同分别率的视频源,1920x1080、1280x720、960x540、480x270,因为HLS会根据网络情况自行切换清晰度,所以一般会准备多个清晰度以供选择。根据抓包数据分析,播放的第一个片段是640清晰度的,之后的第2-8个片段为480清晰度,再之后又切换到了640清晰度。

再往下看是对应音频的索引

1
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="program_audio",LANGUAGE="eng",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="audio1/audio1.m3u8"

#EXT-X-MEDIA上面出现过,为多语言没提列表。

TYPE=AUDIO,这次类型为音频。

GROUP-ID为分组ID,对应EXT-X-STREAM-INF里的AUDIO内容。

URI=audio1/audio1.m3u8对应音频路径。

不同编码格式的备用源

在该主M3U8文件中我们还能看到一条640分辨率的视频源,它与上面的640分辨率还不一样,它的内容是这样的:

1
2
3
4
5
#EXT-X-STREAM-INF:BANDWIDTH=1922391,AVERAGE-BANDWIDTH=1276855,VIDEO-RANGE=SDR,CODECS="hvc1.2.4.H150.B0,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="program_audio_0",SUBTITLES="subsC"
0640c/prog_index.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=1922391,AVERAGE-BANDWIDTH=1276855,CODECS="hvc1.2.4.H150.B0",RESOLUTION=640x360,URI="0640c/iframe_index.m3u8"

#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="program_audio_0",LANGUAGE="eng",NAME="Alternate Audio",AUTOSELECT=YES,DEFAULT=YES,URI="audioc/prog_index.m3u8"

CODECS编码格式为hvc1.2.4.H150.B0,mp4a.40.2,音频编码格式没变,但视频编码格式变了。hvc1是HEVC(H265)编码格式里的一种,它是由苹果推出的新一代视频编码格式,因为兼容性问题很多客户端还无法解析该格式,所以并不是很普及,该格式的视频源出现在这里应该是一种备用。对比相同分辨率的两条内容,还能发现hvc1格式会比avc1格式比特率更高,这说明相同分辨率下hvc1的内容更大,avc1的压缩比更高。

对应hvc1格式的视频源,它的字幕内容分组和音频内容分组也都变了,这也是为什么上面的字幕同一语种会有两份,他们分别对应avc1和hvc1格式的视频源。

M3U8的主列表就这些内容了,该条内容的音视频是分开处理的,其实也可以合在一起。

包含媒体资料的m3u8文件

0640.m3u8这个文件为例

1
2
3
4
5
6
7
8
9
10
11
12
13
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:6.006,
0640_00001.ts
#EXTINF:6.006,
0640_00002.ts
#EXTINF:6.006,
0640_00003.ts
....
#EXT-X-ENDLIST

#EXTM3U#EXT-X-VERSION,分别为M3U文件头和兼容版本号,这种格式是早期的所以版本号比主文件低一些。

EXT-X-TARGETDURATION代表每个播放片段的最大时长,7,代表7秒,该目录下的片段不能超过7s。

EXT-X-MEDIA-SEQUENCE代表播放列表的第一个片段序号,1,代表播放片段是从1开始的。

#EXTINF代表片段的时长,6.006表示当前片段为6.006s。视频总时长的信息是通过该值累加获取的。

0640_00001.ts为片段的相对路径,ts文件代表一段视频或者音频,它可以是ts,mp4,aac等格式。因为前面已经指定了从1开始,所以这里序号是0640_00001。

#EXT-X-ENDLIST为媒体内容的结束标识,因为m3u8即可以表示点播也可以表示直播,区分点播还是直播就看文件末尾是否有这个标识符。如果没有的话就代表直播,播放会一直持续下去。

音频文件audio1.m3u8,字幕文件pro_index.m3u8的内容也是类似的,区别在于他们的切片内容一个是acc的音频文件,一个是webvtt的字幕文件。

包含切片内容的M3U8也可以作为独立的视频链接存在,这时切片内容就需要同时包含音视频内容了。

HLS协议支持加密,如果索引文件中包含了一个密钥文件的信息,那接下来的媒体文件就必须使用密钥解密后才能解密打开了。当前的 HLS 支持使用16-octet 类型密钥的 AES-128 加密。这个密钥格式是一个由这在二进制格式中的16个八进制组的数组打包而成的。

加密的配置模式通常包含三种:

模式一:允许你在磁盘上制定一个密钥文件路径,切片器会在索引文件中插入存在的密钥文件的 URL。所有的媒体文件都使用该密钥进行加密。

模式二:切片器会生成一个随机密钥文件,将它保存在指定的路径,并在索引文件中引用它。所有的媒体文件都会使用这个随机密钥进行加密。

模式三:每 n 个片段生成一个随机密钥文件,并保存到指定的位置,在索引中引用它。这个模式的密钥处于轮流加密状态。每一组 n 个片段文件会使用不同的密钥加密。

参考:HLS-iOS视频播放服务架构深入探究(一)

wechat_official.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK