HTTP Live Streaming,也就是我们常说的HLS。HLS是苹果公司提出的基于HTTP的流媒体网络传输协议。类似于MPEG-DASH,但是HLS更加简洁,它的基本原理也是服务端把文件或媒体流按照不同的码率切分成一个个小片段进行传输,客户端在播放码流时,可以根据自身的带宽及性能限制,在同一视频内容的不同码率的备用源中,选择合适码率的码流进行下载播放。在传输会话开始时,客户端首先需要下载描述不同码流元数据的M3U8索引文件(类似于DASH中的MPD文件)。
与基于UDP的RTP协议不同,HLS请求仅使用HTTP传输,因此可以穿过任何允许HTTP数据通过的防火墙或代理服务器。这也便于使用传统的HTTP服务器作为源,并广泛使用基于HTTP的内容分发网络来传输媒体流。
虽然HLS有上述优势,但也同时存在延迟过大的劣势。采用HLS直播的视频流延时一般在10秒以上,使用推荐配置时延迟大概在30s,而RTMP直播的延迟最低可达到3、4秒,因此,在对实时性要求较高的场合,如互动直播,就要慎用HLS了。
HLS的格式可简单归结如下:
网络协议 |
HTTP |
封装格式 |
MEPG-2 TS |
编码格式 |
视频编码格式为H.264,音频编码格式为MP3、AAC、AC-3或EC-3 |
索引文件 |
M3U8 |
需要说明的是,目前已有厂家实现了H.265的HLS编码。在封装层面,除了MPEG-2 TS封装外,在WWDC2016上,苹果宣布了HLS对分段MP4(fMP4)文件字节寻址的支持,为HLS向MPEG-DASH的兼容提供了可能。
根据媒体流的生成及流向,HLS的结构可划分为如下几个部分:
序号 |
说明 |
1 |
Audio/Video inputs视频源可以是任意格式,可以是离线文件或实时码流。 |
2 |
Server接收到视频源后,Media encoder将源视频转码成HLS支持的编码格式和封装格式,根据需求可输出多个码率分别送至Stream segmenter,在segmenter中被切分成指定大小或时间长度的TS切片,并生成索引文件M3U8。 |
3 |
Distribution是一个HTTP文件服务器,负责将流媒体文件推送出去或响应客户端的请求。客户端只要访问一级M3U8文件路径就能自动播放HLS视频流了。 |
M3U文件中可以包含多个tag,每个tag的功能和属性如下:
标签 |
说明 |
#EXTM3U |
每个M3U文件第一行必须是这个tag,起到标示作用。其格式如下:
#EXTM3U |
#EXTINF |
指定每个媒体段(ts)的持续时间,这个仅对其后面的URI有效,每两个媒体段URI间被这个tag分隔开,其格式如下:
#EXTINF: (a) duration表示持续的时间(秒)必须是整数,如果版本在3以上可以是浮点数。 (b) title是下载资源的url。
示例: #EXTINF:9.009, http://media.example.com/first.ts |
#EXT-X-BYTERANGE |
表示媒体段是一个媒体URI资源中的一段,只对其后的media URI有效,格式如下:
#EXT-X-BYTERANGE: (a) 其中n表示这个区间的大小。 (b) o表在URI中的offset。 这个标签出现在版本4中。 |
#EXT-X-TARGETDURATION |
指定最大的媒体段时间长(秒)。所以#EXTINF中指定的时间长度必须小于或是等于这个最大值。这个tag在整个PlayList文件中只能出现一 次(在嵌套的情况下,一般有真正ts url的m3u8才会出现该tag)。格式如下:
#EXT-X-TARGETDURATION: (a) s表示最大的秒数。
示例: #EXT-X-TARGETDURATION:10 |
#EXT-X-MEDIA-SEQUENCE |
每一个media URI 在 PlayList中只有唯一的序号,相邻之间序号+1。格式如下:
#EXT-X-MEDIA-SEQUENCE: (a) 一个media URI并不是必须要包含的,如果没有,默认为0。
示例: #EXT-X-MEDIA-SEQUENCE:2680 |
#EXT-X-KEY |
表示怎么对media segments进行解码。其作用范围是下次该tag出现前的所有media URI,格式如下:
#EXT-X-KEY: (a) NONE 或者 AES-128。
如果是NONE,则URI以及IV属性必须不存在,如果是AES-128(Advanced Encryption Standard),则URI必须存在,IV可以不存在。
对于AES-128的情况,keytag和URI属性共同表示了一个key文件,通过URI可以获得这个key,如果没有IV(Initialization Vector),则使用序列号作为IV进行编解码,将序列号的高位赋到16个字节的buffer中,左边补0;如果有IV,则将改值当成16个字节的16进制数。
示例: #EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52" |
#EXT-X-PROGRAM-DATE-TIME |
将一个绝对时间或是日期和一个媒体段中的第一个sample相关联,只对下一个meida URI有效,格式如下:
#EXT-X-PROGRAM-DATE-TIME:
示例: #EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00 |
#EXT-X-ALLOW-CACHE |
是否允许做cache,这个可以在PlayList文件中任意地方出现,并且最多出现一次,作用效果是所有的媒体段。格式如下:
#EXT-X-ALLOW-CACHE: |
#EXT-X-PLAYLIST-TYPE |
提供关于PlayList的可变性的信息, 这个对整个PlayList文件有效,是可选的,格式如下:
#EXT-X-PLAYLIST-TYPE: (a) 如果是VOD,则服务器不能改变PlayList 文件。 (b) 如果是EVENT,则服务器不能改变或是删除PlayList文件中的任何部分,但是可以向该文件中增加新的一行内容。 |
#EXT-X-ENDLIST |
表示PlayList的末尾了,它可以在PlayList中任意位置出现,但是只能出现一个,格式如下:
#EXT-X-ENDLIST |
EXT-X-MEDIA |
被用来在PlayList中表示相同内容的不用语种/译文的版本,比如可以通过使用3个这种tag表示3中不用语音的音频,或者用2个这个tag表示不同角度的video在PlayLists中。这个标签是独立存在的,其格式如下:
#EXT-X-MEDIA:
(a) URI:如果没有,则表示这个tag描述的可选择版本在主PlayList的EXT-X-STREAM-INF中存在; (b) TYPE:AUDIO and VIDEO; (c) GROUP-ID:具有相同ID的MEDIA tag,组成一组样式; (d) LANGUAGE:确定使用的主要语言。 (e) NAME:人类可读的语言的翻译。 (f) DEFAULT: YES或是NO,默认是No,如果是YES,则客户端会以这种选项来播放,除非用户自己进行选择。 (g) AUTOSELECT:YES或是NO,默认是No,如果是YES,则客户端会根据当前播放环境来进行选择(用户没有根据自己偏好进行选择的前提下)。 |
#EXT-X-STREAM-INF |
指定一个包含多媒体信息的 media URI 作为PlayList,一般做M3U8的嵌套使用,它只对紧跟后面的URI有效,格式如下:
#EXT-X-STREAM-INF:
有以下属性: (a) BANDWIDTH:带宽,必须有。 (b) PROGRAM-ID:该值是一个十进制整数,惟一地标识一个在PlayList文件范围内的特定的描述。一个PlayList 文件中可能包含多个有相同ID的此tag。(这个属性在后面的协议版本废除了) (c) CODECS:指定流的编码类型,不是必须的。 (d) RESOLUTION:分辨率。 (e) AUDIO:这个值必须和AUDIO类别的“EXT-X-MEDIA”标签中“GROUP-ID”属性值相匹配。 (f) VIDEO:这个值必须和VIDEO类别的“EXT-X-MEDIA”标签中“GROUP-ID”属性值相匹配。
示例: #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=500000, RESOLUTION=720x480 mid_video_index.M3U8 |
M3U8文件其实就是以UTF-8编码的M3U文件,该文件本身不能播放,只是用于存放待播放视频流的基本信息。
如果你的视频具备流切换功能,处于不同的带宽、不同的网速播放不同清晰度的视频流,这样只能的流切换可以保证用户感觉到非常流畅的观影体验,同时不同的设备也可以作为选择的条件,比如视网膜屏可以在网速良好的情况下播放清晰度更高的视频流。
这种功能的实现在于,索引文件的特殊结构。
如上图所示,客户端播放HLS视频流的逻辑其实非常简单,先下载一级Index file,它里面记录了二级索引文件(Alternate-A、Alternate-B、Alternate-C)的地址,然后客户端再去下载二级索引文件,二级索引文件中又记录了TS文件的下载地址,这样客户端就可以按顺序下载TS视频文件并连续播放。
(1) HLS有两级索引:
(a) 第一级索引存放的是不同码率的HLS源的M3U8地址,也就是二级索引文件的地址。
(b) 第二级索引则记录了同一码率下TS切片序列的下载地址。
客户端获取一级M3U8文件后,根据自己的带宽,去下载相应码率的二级索引文件,然后再按二级索引文件的切片顺序下载并播放TS文件序列。
模式 |
说明 |
VOD |
点播VOD的特点就是当前时间点可以获取到所有index文件和ts文件,二级index文件中记录了所有ts文件的地址。这种模式允许客户端访问全部内容。 |
LIVE |
Live 模式就是实时生成M3u8和ts文件。它的索引文件一直处于动态变化的,播放的时候需要不断下载二级index文件,以获得最新生成的ts文件播放视频。如果一个二级index文件的末尾没有#EXT-X-ENDLIST标志,说明它是一个Live视频流。
这种类型通过向索引文件添加媒体地址可以很容易的转化为VOD类型。在转化时不要移除原来旧的源,而是通过添加一个 #ET-X-ENDLIST标记来终止实时事件。转化时如果你的索引文件中包含 EXT-X-PLAYLIST-TYPE标签,你需要将值从EVENT改为 VOD。 |
客户端在播放VOD模式的视频时其实只需要下载一次一级index文件和二级index文件就可以得到所有ts文件的下载地址,除非客户端进行比特率切换,否则无需再下载任何index文件,只需顺序下载ts文件并播放就可以了。但是Live模式下略有不同,因为播放的同时,新ts文件也在被生成中,所以客户端实际上是下载一次二级index文件,然后下载ts文件,再下载二级index文件(这个时候这个二级index文件已经被重写,记录了新生成的ts文件的下载地址),再下载新ts文件,如此反复进行播放。
第一级索引存放的是不同码率的HLS源的M3U8地址,也就是二级索引文件的地址。
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=500000, RESOLUTION=720x480
mid_video_index.M3U8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=800000, RESOLUTION=1280x720
wifi_video_index.M3U8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=3000000, CODECS="avc1.4d001e,mp4a.40.5", RESOLUTION=1920x1080
h264main_heaac_index.M3U8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=64000, CODECS="mp4a.40.5"
aacaudio_index.M3U8
#EXTM3U |
格式标签,标明该文件是一个Extended M3U播放列表文件,必须存在于一二级列表的第一行。 |
#EXT-X-STREAM-INF |
特定流标签,指示了该流的格式信息: (a) PROGRAM-ID节目ID,一般不用考虑; (b) BANDWIDTH:指定流的带宽; (c) RESOLUTION:视频分辨率;如果存在音频分级,则需指定音频的编码格式CODECS,如上例中的最后两项; (d) STREAM-INF的下一行是二级index文件的路径,可以用相对路径也可以用绝对路径。例子中用的是相对路径。这个文件中记录了不同比特率视频流的二级index文件路径,客户端可以自己判断自己的现行网络带宽,来决定播放哪一个视频流。也可以在网络带宽变化的时候平滑切换到和带宽匹配的视频流。 |
第二级索引则记录了同一码率下TS切片序列的下载地址。
(1) VOD形式
点播VOD的特点就是当前时间点可以获取到所有index文件和ts文件,二级index文件中记录了所有ts文件的地址。这种模式允许客户端访问全部内容。
下面的例子中就是一个点播模式下的m3u8的结构。
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#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
#EXTM3U |
格式标签,标明该文件是一个Extended M3U播放列表文件,必须存在于一二级列表的第一行。 |
#EXT-X-PLAYLIST-TYPE |
#EXT-X-PLAYLIST-TYPE:VOD的意思是当前的视频流并不是一个直播流,而是点播流,换句话说就是该视频的全部的ts文件已经被生成好了。 |
#EXT-X-TARGETDURATION |
表示切片的最大时长,单位是秒。#EXT-X-TARGETDURATION:10表示列表中表示的每个切片时长不超过10秒。 |
#EXT-X-VERSION |
表示协议兼容性版本。 |
#EXTINF |
切片的实际时长,若要求取整,则其数值不能大于EXT-X-TARGETDURATION的值。 |
http://media.example.com/first.ts |
对应的切片文件(路径),可以是绝对路径,也可以是相对路径。 |
#EXT-X-ENDLIST |
表示整个码流的结束,不再向后附加新的切片列表。有这个标志同时也说明当前的流是一个非直播流。 |
(2) Live形式
Live 模式就是实时生成M3u8和ts文件。它的索引文件一直处于动态变化的,播放的时候需要不断下载二级index文件,以获得最新生成的ts文件播放视频。如果一个二级index文件的末尾没有#EXT-X-ENDLIST标志,说明它是一个Live视频流。
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:2680
#EXTINF:7.975,
https://priv.example.com/fileSequence2680.ts
#EXTINF:7.941,
https://priv.example.com/fileSequence2681.ts
#EXTINF:7.975,
https://priv.example.com/fileSequence2682.ts
#EXT-X-MEDIA-SEQUENCE |
媒体序列号,表示出现在当前M3U8文件中的第一个Segment的序列号。 |
Live形式的M3U8一般用于直播,列表中的文件数有限制,推荐配置中是3个,服务端会实时更新该列表,删除最开始的Segment,并向后面添加新生成的Segment。因此,这种模式下,当网络带宽不足时,客户端来不及下载新的M3U8和对应的切片文件,会导致切片丢失,播放卡顿。
(3) 加密形式:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:7794
#EXT-X-TARGETDURATION:15
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52"
#EXTINF:2.833,
http://media.example.com/fileSequence52-A.ts
#EXTINF:15.0,
http://media.example.com/fileSequence52-B.ts
#EXTINF:13.333,
http://media.example.com/fileSequence52-C.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=53"
#EXTINF:15.0,
http://media.example.com/fileSequence53-A.ts
HLS可以被加密。客户端通过读取M3U8文件中的#EXT-X_KEY标签可以获取解密的密钥。EXT-X-KEY指示的密钥信息作用于当前EXT-X-KEY标签至下一个EXT-X-KEY标签(若没有下一个,则到最后一个Segment)之间的所有Segments。METHOD指示加密算法,可用的取值有NONE、AES-128和SAMPLE-AES,NONE表示不加密。URI指示获取密钥的路径,该项不可省略,除非METHOD为NONE。
https://www.vitamio.org/docs/Basic/2013/0508/14.html