HLS====> HTTP LIVE STREAMING 是苹果公司2009推出的一种流媒体协议, 从推出到现在, 得到了非常广泛的应用, 不管在点播还是直播中, 使用的公司非常多, 点播的情况下不必多说. 我们都知道直播场景下, RTMP使用的显然比HLS更加广泛, 肯定是HLS存在某些缺陷才会导致在直播场景下应用不太广泛.
RTMP | HTTP-FLV | HLS | |
---|---|---|---|
全称 | Real Time Message Protocol | RTMP over HTTP | HTTP Live Streaming |
所在层 | 传输层 | 网络层 | 网络层 |
是否长链接 | 是 | 是 | 否 |
延时 | 1 ~ 3s | 1 ~ 3s | 10s以上 |
兼容性 | 部分平台不一定支持 | 全平台支持 | 全平台支持 |
扩展性 | 差,Adobe已经不维护了 | 差,Adobe已经不维护了 | Apple全力支持, 扩展性强 |
显而易见, RTMP在传输时延方面确实有很大的优势, 这是目前直播用RTMP的主要原因, 但是苹果公司也不是吃素的, 他们也在积极努力, 改进HLS的时延, 降低直播的耗时, 改善直播观看体验.
经过多年的努力, 2019年苹果公司推出LL-HLS====> Low Latency HLS来着重解决这类问题.
1.为什么HLS这么慢
首先看看HLS 标准协议文档中是怎么介绍的? ====> https://tools.ietf.org/html/rfc8216#section-6.3.3
简而言之, 必须至少加载3个分片视频, 当前的分片才能被启动播放, HLS标准的分片时长是10s, 加载3个分片, 也就说标准的时延要达到30s, 这在正常直播场景中是无法忍受的.
LL-HLS将大的分片切分为一个个较小的分片, 这种切分方式不是简单的将源分片等分, 而是结合fMP4封装和#EXT-X-MAP规则, 将整视频的头部和内容分开, 而且内容源被划分的很细, 例如原来一个分片6s左右, 可能被切分为30个200ms的fMP4分片, 这些分片使用#EXT-X-PART来标注:
#EXTINF:6.003,
LLHLS_Video1_67750710.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:20:29.482Z
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750711.0.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750711.1.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750711.2.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750711.3.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750711.4.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750711.5.mp4",INDEPENDENT=YES
一个整分片LLHLS_Video1_67750710.mp4被切分为6个小分片, 每一个小分片用 #EXT-X-PART 标准 这样的好处是原来要把一整个分片请求下来才能播放, 现在不需要了, 我只要请求一两个小分片就可以播放可, 时间上肯定是大大减少了.
直播过程中, M3U8索引文件是不断更新的, M3U8索引中会有每一分片的时间戳和真实的时间戳, 这样我们明确知道当前播放到什么问题, 这段分片视频是什么时候下发的, 直播过程中如果出现网络不好, 累积的时延会越来越大, 但是有了时间戳的校验, 网络差的情况下我们也会实时追上最新的播放点. ====> #EXT-X-SERVER-CONTROL 会告诉你那些分片会被丢弃调.
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.150,CAN-SKIP-UNTIL=36.000
CAN-SKIP-UNTIL=36.000 说明之前36的视频都是可以舍弃的.
例如紧接着的文件描述为:
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.150,CAN-SKIP-UNTIL=36.000
#EXT-X-MEDIA-SEQUENCE:67750702
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:19:35.479Z
#EXTINF:6.000,
LLHLS_Video1_67750702.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:19:41.479Z
#EXTINF:6.000,
LLHLS_Video1_67750703.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:19:47.479Z
#EXTINF:6.000,
LLHLS_Video1_67750704.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:19:53.479Z
#EXTINF:6.000,
LLHLS_Video1_67750705.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:19:59.479Z
#EXTINF:6.000,
LLHLS_Video1_67750706.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:20:05.479Z
#EXTINF:6.000,
LLHLS_Video1_67750707.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:20:11.479Z
#EXTINF:6.000,
LLHLS_Video1_67750708.mp4
#EXT-X-PROGRAM-DATE-TIME:2021-03-18T09:20:17.479Z
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750709.0.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750709.1.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750709.2.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750709.3.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750709.4.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.000,URI="LLHLS_Video1_67750709.5.mp4",INDEPENDENT=YES
服务器明确告知我们, 36s之前的内容都是可以SKIP的, 36s之后开始再切片, 之前之所以不切片因为之前的内容可能比较老了, 没有比较再切片.
36s之后如果发现#EXT-X-SKIP 说明也是可以丢弃的, 这些都是为了解决直播的实时性问题.
阻止播放列表重新加载, 直播中M3U8索引文件不断更新, 每隔一段时间重新请求以获取最新的M3U8索引列表, 但是重新请求可能浪费的时间更多, 现在采用的方式是在LL-HLS中加入一些设置指定未来要请求的特定片段. 我们在M3U8中会加入一个MSN===> Media Sequence Number来表示即将请求的MSN是哪一个, 这样可以不用重新加载M3U8索引文件, 就提前知道要请求哪一个分片, 哪一个索引文件.
预加载的支持是通过#EXT-X-PRELOAD-HINT来表示:
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="LLHLS_Video1_67750712.5.mp4"
通常在加载完一个分片之后, 即将要加载某一个分片之前, 标注一下未来要请求哪一个分片, 这种在索引文件中提前预告的行为确实能为我们省下很多时间.
LL-HLS播放过程中有时候会遇到#EXT-X-RENDITION-REPORT, 这说明接下来需要加载不一样的类型的视频了, 可能是分辨率/码率/格式发生了变化, LAST-MSN表示是在哪一个MSN结束之后开始加载这个新的索引文件.
#EXT-X-RENDITION-REPORT:URI="LLHLS_Video2.m3u8",LAST-MSN=67750884,LAST-PART=3
举一个LL-HLS的例子:
https://d18lkalz24uryj.cloudfront.net/LLHLS_Video1.m3u8