HLS协议学习总结

来源:互联网 发布:微信模板消息 java 编辑:程序博客网 时间:2024/05/21 09:27

HLS简介:

hls(HTTP Live Streaming),顾名思义,即通过http协议在互联网上传输媒体流,但是事实上,这里说的媒体流已经被切分成多个时间跨度基本相同的分片文件,因此也使得HLS服务器可以部署在任何http服务器上,这一点不像rtsp、rtmp等协议还需要针对服务器进行定制,与hls协议类似的还有dash协议,都是对于http服务器类型无感知的,理论上只要能提供基本的文件下载服务就可以了(因为可能还要包含分片老化、索引刷新等问题)。
hls协议支持的媒体类型主要是MPEG-2 TS,其他的还比如(MPEG-2 PS、MPEG-2 audio elementary stream,对这两个协议不太了解),在V7版本也增加了对MP4的支持;另外也支持媒体分片的加密,但是加密方式和DASH略有不同,HLS是使用AES对整个分片文件加密,但是DASH协议的加密粒度更细致一些,DASH的MP4加密是针对分片文件里的帧级别进行加密,这个加密工作一般是由编码器完成,另外一些cdn厂商也可能提供这种能力。

HLS协议主要工作流程:

Created with Raphaël 2.1.0UEUEServerServer请求顶级索引(可选),包含不同码率的流返回顶级索引内容根据网络情况请求合适的的码率索引返回对应码率索引对应的分片列表依次请求分片列表,并播放直播场景间隔一定时间重新请求Server,刷新码率索引,并请求新分片点播或者录制(其实也就是点播)场景遇到EXT-X-ENDLIST结束播放

下面开始当协议的翻译官,并结合一些自己的理解,大家如果想认真学习,最好还是看原版的协议(文章最后有自己写的一个脚本,抓取所有版本的协议)

HLS V1:

播放列表文件(也就是索引)是由M3U文件(这个文件格式还不太清楚,以后有必要再研究)扩展而来,因此列表文件里的所有标签都是以EXT开头。换行可以使用\r\n或者是\n都行,这一点比较贴心。文件里的每一行必须是uri、空行或者#开头的标签(uri表示的是播放列表文件的uri,空行需要由播放器进行忽略)。列表文件需要使用UTF8进行编码,文件名需要以.m3u8为后缀或者标识MIME类型为application/x-mpegURL(这个不太清楚);同时列表文件还要以EXTM3U作为第一行内容。

标签类型:

V1版本总共有7个标签,下面一一说明

  • EXT-X-TARGETDURATION
    格式:#EXT-X-TARGETDURATION: seconds
    V1版本中此标签的含义说明了下一个播放分片的近似长度,此值允许有一点误差,但是此标签必须要在列表文件中。
    此标签在V2及之后的版本中的含义已经发生变化,表示的是列表文件中所有分片的时间跨度最大值。

  • EXT-X-MEDIA-SEQUENCE
    格式:EXT-X-MEDIA-SEQUENCE:number
    标明了每个分片的序列号,列表文件中的此标签表示此列表文件中第一个分片的序列号,其他的分片序列号都由前一个分片的序列号加1得到。如果此标签不存在,则第一个分片的序列号为1。

  • EXT-X-KEY
    格式:#EXT-X-KEY:METHOD=method,URI=”URI”]
    标明分片加密的方式,METHOD字段在V1的版本支持两个字段ONE and
    AES-128,URI字段标识了获取解密分片的key。在遇到下一个EXT-X-KEY标签之前,当前列表中的分片都使用相同的加密方式和解密key。如果没有此标签,标识分片都是未加密的。
    注:AES加密方式都是对称加密,即加密key和解密key是相同的。

  • EXT-X-PROGRAM-DATE-TIME
    格式:#EXT-X-PROGRAM-DATE-TIME:YYYY-MM-DDThh:mm:ssZ
    标识了其后所描述的分片开始绝对时间,需要注意此处带着时区

  • EXT-X-ALLOW-CACHE
    格式:#EXT-X-ALLOW-CACHE:YES|NO
    是否允许客户端缓存已经下载下来的媒体分片,从而实现之后的重播

  • EXT-X-ENDLIST
    格式:#EXT-X-ENDLIST
    表示已经没有更多的媒体分片,正常来说点播、录制等媒体内容才会有此标签,直播是没有这个标签的。

  • EXT-X-STREAM-INF
    格式:#EXT-X-STREAM-INF:[attribute=value][,attribute=value]*
    表示其后所跟的URI是一个列表文件,其中attribute字段可能是以下几个字段,BANDWIDTH、PROGRAM-ID、CODECS。
    BANDWIDTH表示此路流的码率最大值。
    PROGRAM-ID唯一标识内容,同一个播放列表中可能包含多个不同的EXT-X-STREAM-INF标签,并且他们使用相同的PROGRAM-ID,因此标识他们是同一份内容,但是他们可能是不同的码率。
    CODECS:表示此路流的编码方式。


媒体格式

hls协议支持的媒体类型主要是MPEG-2 TS,其他的还比如(MPEG-2 PS、MPEG-2 audio elementary stream,对这两个协议不太了解)。同一路流中的帧编码参数应该保持一致(事实上从V2开始,可以通过EXT-X-DISCONTINUITY标识后续的视频分片编码方式等发生变化了),但是V1的协议里同时也说明了客户端也需要适配编码参数发生变化的场景(如分辨率发生变化),此处其实没咋理解。


加解密

EXT-X-KEY标签中URI参数说明了媒体分片解密key的地址,加密使用16位8进制数,即总共128个bit,服务器只需要将这个16个8进制数打包成二进制数组返回即可。
AES-128解密的时候还需要16位8进制的IV,从而提高加密强度。
在V1中,媒体分片加密和解密的时候使用媒体分片的序列号作为IV,大端序,左侧不足补0即可。AES-CBC加密算法下,因为后一个要加密的分组依赖于前一个加密的分组,因此协议规定这种分组依赖不能跨媒体分片。
服务端如果想使用非加密的流,那么需要使用EXT-X-KEY标签将METHOD设置为NONE。


客户端和服务端行为

服务端:

服务端在生成分片的时候要尽量保证分片的时长相等,并且尽量在关键帧的边界进行切割。需要播放的分片要在播放列表文件中体现,服务端要保证返回给客户端的列表文件中的媒体分片URI都是可以获取到的(这一点需要注意,在直播场景下,播放列表文件随着时间一直在变化,因此在媒体分片URI已经从列表文件中移除时,服务端经过一段时间后可能会老化这个分片,具体多长时间,下文会说明)。EXT-X-TARGETDURATION标签一定要在列表文件中出现,并且近似等于真实值,官方推荐的值是10秒。如果内容播放结束,需要在列表文件的结尾添加EXT-X-ENDLIST标签。当服务端想停止某个内容的播放时,需要将列表文件设置成不可用,但是在列表中所有分片的时长和的时间内,媒体分片的服务要保持可用。

服务端要保证媒体分片添加到列表中的顺序和从列表中移除的顺序是一致的,其实就是一个队列。当媒体分片从播放列表中移除的时候,还需要在一定时间内保持可用,时间=此分片时长+此分片所在列表文件时长的最大值,列表文件的时长=列表文件中的所有分片时长的和,因为此分片可能出现在多个列表文件中(随着时间变化),所以要取最大值,这样就可以保证媒体分片在可能被播放的时间内总是可用的。可能会有人会怀疑在播放暂停场景下,会导致客户端请求分片失败,此处先不解释,因为协议中对客户端的行为也进行了约束,保证了正常播放和暂停场景下都是正常的。

服务端的列表文件中至少需要保持3个媒体分片(包含EXT-X-ENDLIST标签的列表文件除外)。以10s的分片时长为例,直播刚开始的30s时间内,列表文件是不可用。(其实也一定程度说明了hls的直播至少会有30s的延时)。

多码率流:
同一份内容提供不同的流时,需要使用相同的PROGRAM-ID。同一份内容在生成不同路的流时候需要满足以下几个约束:

  • 不同流的内容需要保持一致,
  • 不同的流需要保持最小粒度的时长一致(这里应该说的是分片时长要完全一致)
  • 不同流的TS或者PS中的timestamps需要保持一致(这里应该是指DTS和PTS)
  • 不同流中的音频编码方式需要保持一致,为了防止声音错误(这里不太理解)

客户端:

客户端根据URI获取播放列表,如果获取到的播放列表中包含多路流(即包含EXT-X-STREAM-INF标签),客户端还需要从多路流中选取一个合适的播放列表。具体如何选取还需要读到之后的协议才知道。尴尬。。。
读到时再补充吧。

加载播放列表:
获取到播放列表时,客户端需要检查列表文件是否以EXTM3U开始,以确认是否继续加载。如果列表中包含EXT-X-MEDIA-SEQUENCE标签,那么需要客户端需要做好这样的准备:这个列表中的所有媒体分片在列表文件时长之后会老化掉,即这些分片在服务端不可访问。

播放播放列表:
如果列表中包含EXT-X-ENDLIST标签,那么客户端可以从列表中的任意分片开始播放;如果不包含此标签,那么客户端需要至少从倒数第三个开始播放。播放开始后,播放列表中的其他分片需要按照他们在列表中的顺序依次获取和加载。

重新加载播放列表
如果播放列表中不包含EXT-X-ENDLIST标签,那么客户端需要周期性地重新读取播放列表。这个频率不能超过下面的范畴。先来个一个定义,
初始最小重载延时:客户端首次加载列表或者客户端发现播放列表已经发生变化时所等待的时间。这个时间设置为列表中最后一个分片的时长(EXTINF),或者target duration的3倍(无论这个值的大小)。
如果客户端发现播放列表并没有发生变化,那么延时时间为初始最小重载延时*倍数或者target duration的3倍。这个倍数在第一次是0.5,第二次是1.5,之后是3。

确定之后加载的分片
如果播放列表中不包含EXT-X-MEDIA-SEQUENCE标签,客户端需要校验最后加载的媒体分片是否在当前播放列表中。如果播放列表中包含EXT-X-MEDIA-SEQUENCE标签,那么客户端只需要加载超过最后加载的媒体分片sequence number的最小分片即可。(具体为什么这么干还没想明白)

再提供一些播放列表的样例

  • 仅包含一个分片,一级索引,一路流
#EXTM3U#EXT-X-TARGETDURATION:10#EXTINF:5220,http://media.example.com/entire.ts#EXT-X-ENDLIST
  • 滑动窗口列表,https协议
#EXTM3U#EXT-X-TARGETDURATION:8#EXT-X-MEDIA-SEQUENCE:2680#EXTINF:8,https://priv.example.com/fileSequence2680.ts#EXTINF:8,https://priv.example.com/fileSequence2681.ts#EXTINF:8,https://priv.example.com/fileSequence2682.ts
  • 分片加密场景
#EXTM3U#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:15,http://media.example.com/fileSequence7794.ts#EXTINF:15,http://media.example.com/fileSequence7795.ts#EXTINF:15,http://media.example.com/fileSequence7796.ts#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=53"#EXTINF:15,http://media.example.com/fileSequence7797.ts
  • 多路流场景
#EXTM3U#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000http://example.com/low.m3u8#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000http://example.com/mid.m3u8#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000http://example.com/hi.m3u8#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS="mp4a.40.5"http://example.com/audio-only.m3u8

最后分享一个爬取hls协议pdf文件的脚本,大家需要的自取:

#!/bin/shbase_url="https://tools.ietf.org/pdf/"version_prefix="draft-pantos-http-live-streaming-"url_prefix="$base_url$version_prefix"suffix=".pdf"for i in `seq 0 2`; do    for j in `seq 0 9`; do        if [ $i = 2 -a $j = 4 ]; then            break        fi        file_name="${version_prefix}$i$j$suffix"        url="${base_url}$file_name"        curl "$url" -o $file_name        if [ $? -eq 0 ]; then            echo "download $file_name success, url is $url"        else            echo "download $file_name failed, url is $url"        fi    donedone
原创粉丝点击