HLS协议的核心设计至少包括三点:大规模分发、多设备兼容和码率自适应。码率自适应包括自适应算法设计以及如何实现切换。HLS协议本身不负责自适应算法,它描述的是如何实现切换。一个基础问题:从a.m3u8切换到b.m3u8时,如何在b.m3u8中找到要播放的切片?
最简单、看起来最合理的方案:在b.m3u8中找匹配的sequence number。可能多数播放器是这么做的。
但是,HLS草案第9版(2012年9月)新增了这么一段描述:
https://datatracker.ietf.org/doc/html/draft-pantos-http-live-streaming-09
used to synchronize the old and new timelines precisely. A client MUST NOT assume that segments with the same media sequence number in different variants or renditions contain matching content.
最终,RFC 8216修改为:
A client MUST NOT assume that segments with the same Media Sequence Number in different Variant Streams or Renditions have the same position in the presentation; Playlists MAY have independent Media Sequence Numbers. Instead, a client MUST use the relative position of each segment on the Playlist timeline and its Discontinuity Sequence Number to locate corresponding segments.
可知,不同流的sequence number是独立的,根据sequence number去匹配是不符合标准的。
那么,按照标准该如何在不同的流之间匹配对应的切片呢?
-
对于点播和event类型直播,对所有切片时长进行累加,根据累加时长在不同流间匹配对应的切片
-
对于live直播,根据切片时间戳做匹配,因为HLS要求
o Matching content in Variant Streams MUST have matching timestamps. This allows clients to synchronize the media.
怎么根据时间戳匹配呢?不管三七二十一,先下载一个切片,解析下时间戳,再根据m3u8里切片的时长,找到对应的切片。运气好一次命中,运气不好得下载俩切片才能实现切码流。
Exoplayer关于此问题有个深入讨论:HLS support for different GOP between variants 。对应的代码提交:Main branch: Ignore segment sequence numbers when switching variants。
另外一个和切码流相关的是EXT-X-DISCONTINUITY-SEQUENCE
,HLS草案12版添加的。HLS标准对不同流里的DISCONTINUITY-SEQUENCE做了限定:
o Matching content in Variant Streams MUST have matching Discontinuity Sequence Numbers (see Section 4.3.3.3).
为什么需要EXT-X-DISCONTINUITY-SEQUENCE
呢?因为出现EXT-X-DISCONTINUITY
的直播场景,EXT-X-DISCONTINUITY
在m3u8刷新过程中会消失,a.m3u8有EXT-X-DISCONTINUITY
,b.m3u8无EXT-X-DISCONTINUITY
,播放器没办法做匹配。EXT-X-DISCONTINUITY-SEQUENCE
起到了对EXT-X-DISCONTINUITY
的计数功能,效果是对切片做了分组。通过时间戳在不同流之间匹配切片时,要先匹配DISCONTINUITY-SEQUENCE。举个例子:当前在a.m3u8 DISCONTINUITY-SEQUENCE为3的某个切片结束时timestamp是600秒,下个切片DISCONTINUITY-SEQUENCE还是3;切换到b.m3u8,先找到对应的DISCONTINUITY-SEQUENCE 3的切片区间,再从中找开始时间为600秒的切片。