共计 8589 个字符,预计需要花费 22 分钟才能阅读完成。
应用 DASH 直播时个别会有几十秒的直播提早,对于互动直播这么高的提早基本互不动。要升高直播提早个别会缩小视频分段时长。
上图中展现了不同时长的视频片段对应的提早,减小视频片段时长尽管能够升高提早,然而也会减少资源耗费和视频码率,而且就算应用 1 秒的视频分段,提早也会比上面介绍的 LLDASH 计划高。
介绍
LLDASH(Low Latency DASH)最早在 2017 年提出并成立工作组,在 2019 年 DVB 公布了 DVB-DASH with low latency 标准,基于 DVB 和 DASH IF 联合开发的这份报告 DVB and DASH-IF in 2017 on Low-Latency DASH 在 2020 年 DASH IF 公布了 Low-latency Modes for DASH 标准。
DVB(Digital Video Broadcasting)数字视频播送,是一系列数字电视国内凋谢规范,由 DVB Project 保护。DVB Project 是一个由 300 多个成员组成的工业组织,它是由欧洲电信标准化组织、欧洲电子标准化组织和欧洲播送联盟联结组成的联结专家组发动的。
DASH IF(DASH Industry Forum)DASH 行业论坛,它次要由流媒体公司组成,如 Akamai、谷歌、微软等。DASH IF 次要标准化互操作性,促成 MPEG-DASH 倒退,并帮忙其从标准过渡到真正的业务。
所以目前一共有 DVB 和 DASH IF 两套 LLDASH 标准,这两套低提早计划十分类似只有一点不同,因为 DASH IF 较晚公布所以在 DASH IF 标准中也阐明了与 DVB 不同的局部。而且这两个标准是齐全向下兼容一般 DASH 直播的。
CMAF
尽管 MPEG-DASH 标准并没有限度内容格局,然而两种 LLDASH 标准中都是应用 CMAF 格局。这容易让人产生 CMAF 和低提早划等号的误会,CMAF 自身并不会升高提早,例如 HLS 反对 MPEG-TS 和 CMAF 两种格局,如果将一般 HLS 直播 MPEG-TS 分片换成 CMAF 分片,这并不会升高直播提早。CMAF 最大的作用是对立播放格局,从而节俭存储空间。不过 CMAF 提供了一些工具使低提早 DASH 成为可能。
原理
LLDASH 与上篇文章介绍的 LHLS 十分类似,都是将一个分片变成一个个小 Chunk,这些小 Chunk 能够在分片齐全生成之前被播放器应用 HTTP/1.1 的 Chunked transfer encoding 性能下载并缓存,从而升高直播提早。
如上图所示,一般 DASH 直播中一个 MP4 分段只有齐全编码后能力输入被申请。LLDASH 中将视频片段宰割为一个个小 Chunk,编码器能够每生成一个 Chunk 就输入,传递给播放器缓存播放。
CMAF 中 ftyp 和 moov 盒子组成初始化分段,每一个 Chunk 由 moof 和 mdat 盒子组成。播放器会先申请初始化分段,而后申请最新的媒体分段,服务器会将分段的一个个 Chunk 返回给播放器播放。
播放器申请拉流时,可能如上图所示,一个视频片段被分为 3 个 Chunk。以后播放器发送申请给服务器时,视频片段还没被齐全生成,服务器会放弃连接不断开,每当生成一个 Chunk 就立马推送给播放器。
标准实现
对于应用 DASH IF 低提早标准的 MPD,应该增加 http://www.dashif.org/guideli… 到 MPD@profiles 属性中进行标识,上面是一个合乎 DASH IF 低提早标准的 MPD 例子。
<?xml version="1.0" encoding="utf-8"?> | |
<MPD | |
xmlns="urn:mpeg:dash:schema:mpd:2011" | |
availabilityStartTime="1970-01-01T00:00:00Z" | |
id="Config part of url maybe?" | |
maxSegmentDuration="PT8S" | |
minBufferTime="PT1S" | |
minimumUpdatePeriod="P100Y" | |
profiles="urn:mpeg:dash:profile:full:2011,http://www.dashif.org/guidelines/low-latency-live-v5" | |
publishTime="2021-09-14T05:25:57Z" | |
timeShiftBufferDepth="PT5M" | |
type="dynamic" | |
> | |
<BaseURL> https://livesim.dashif.org/livesim/sts_1631597157/sid_a736b022/chunkdur_1/ato_7/testpic4_8s/ | |
</BaseURL> | |
<ServiceDescription id="0"> | |
<Latency max="6000" min="2000" referenceId="0" target="4000" /> | |
<PlaybackRate max="1.04" min="0.96" /> | |
</ServiceDescription> | |
<Period id="p0" start="PT0S"> | |
<AdaptationSet contentType="audio" lang="eng" segmentAlignment="true"> | |
<ProducerReferenceTime id="0" presentationTime="0" type="encoder" wallClockTime="1970-01-01T00:00:00"> | |
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-iso:2014" value="http://time.akamai.com/?iso" /> | |
</ProducerReferenceTime> | |
<SegmentTemplate | |
availabilityTimeComplete="false" | |
availabilityTimeOffset="7.000000" | |
duration="384000" | |
initialization="$RepresentationID$/init.mp4" | |
media="$RepresentationID$/$Number$.m4s" | |
startNumber="0" | |
timescale="48000" | |
/> | |
<Representation audioSamplingRate="48000" bandwidth="36997" codecs="mp4a.40.2" id="A48" mimeType="audio/mp4" startWithSAP="1"> | |
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" /> | |
</Representation> | |
</AdaptationSet> | |
<AdaptationSet contentType="video" maxFrameRate="30" maxHeight="360" maxWidth="640" par="16:9" segmentAlignment="true"> | |
<ProducerReferenceTime id="0" presentationTime="0" type="encoder" wallClockTime="1970-01-01T00:00:00"> | |
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-iso:2014" value="http://time.akamai.com/?iso" /> | |
</ProducerReferenceTime> | |
<SegmentTemplate | |
availabilityTimeComplete="false" | |
availabilityTimeOffset="7.000000" | |
duration="122880" | |
initialization="$RepresentationID$/init.mp4" | |
media="$RepresentationID$/$Number$.m4s" | |
startNumber="0" | |
timescale="15360" | |
/> | |
<Representation bandwidth="303780" codecs="avc1.64001e" frameRate="30" height="360" id="V300" mimeType="video/mp4" sar="1:1" startWithSAP="1" width="640" /> | |
</AdaptationSet> | |
</Period> | |
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-iso:2014" value="http://time.akamai.com/?iso" /> | |
</MPD> |
判断是否是 Chunked 低提早
DASH IF 低提早标准中定义了两种低提早直播办法,一种是不应用下面提到的 Chunked transfer encoding 性能,而是将媒体分段切分的十分短来升高提早,这种办法能够一般的 DASH 直播并没有什么区别,这里不再过多介绍。
另一种低提早办法就是应用下面提到的 HTTP Chunked transfer encoding 性能,也就是当一个媒体还没齐全生成好时,播放器能够申请该分段下载并缓存曾经创立的 Chunk,而不是 404 申请报错。这个就是上面要介绍的低提早模式。
有两种办法能够判断是否是低提早模式。
- 依据 SegmentTemplate@availabilityTimeComplete 属性。DASH IF 低提早标准中对于 Chunked AdaptationSet,须要将 availabilityTimeComplete 属性设置为 false,所以如果 availabilityTimeComplete 为 false 时,则能够认为该媒体流是低提早模式。
- DVB 还定义了形容低提早的 EssentialProperty 和 SupplementalProperty 元素,如果存在其中一个,并且它的 schemeIdUri 属性等于 urn:dvb:dash:lowlatency:critical:2019,value 属性等于 true,则也能够认为该媒体流是低提早模式。
提早和播放速率
LLDASH 中定义了播放提早和播放速率信息,这些信息在 ServiceDescription 元素中。
<ServiceDescription id="0"> | |
<Scope schemeIdUri="urn:dvb:dash:lowlatency:scope:2019" /> | |
<Latency target="3000" max="6000" min="1500" /> | |
<PlaybackRate max="1.5" min="0.5" /> | |
</ServiceDescription> |
- Latency 元素定义直播提早相干信息,单位是毫秒。Latency@target 为直播指标提早。Latency@max 为容许的最大提早,当提早超过该值时播放器应该间接 seek 到提早地位。如果以后提早小于 Latency@min 播放器应该慢放。
- PlaybackRate 元素定义了最大和最小播放速率,失常速率是 1。当提早超过指标提早时播放器会进行快放追赶,当缓存过少时播放器可能进行慢放。
DVB 低提早标准中还定义了 Scope 元素,它的 schemeIdUri 属性为 urn:dvb:dash:lowlatency:scope:2019,DASH IF 标准中没有定义该元素。
时钟同步
为了获取精确的媒体分段和直播提早,LLDASH 标准定义 MPD 中起码存在一个 UTCTiming 元素,用于客户端与服务端时钟同步。UTCTiming@schemeIdUri 属性须要是上面 3 个中的一个。
- urn:mpeg:dash:utc:http-xsdate:2014
- urn:mpeg:dash:utc:http-iso:2014
- urn:mpeg:dash:utc:http-ntp:2014(浏览器中不反对)
UTCTiming@value 属性是服务器工夫服务地址。
<UTCTiming | |
schemeIdUri="urn:mpeg:dash:utc:http-xsdate:2014" | |
value="https://time.example.com" | |
/> |
一些老标准中应用的是 2012,播放器也应该反对上面两个 schemeIdUri。
- urn:mpeg:dash:utc:http-xsdate:2012
- urn:mpeg:dash:utc:http-iso:2012
如果 MPD 中没有 UTCTiming 元素或者时钟同步服务不可拜访时,播放器能够降级为应用本地时钟。
媒体分段
LLDASH 中定义了两种获取媒体分段的办法。
- SegmentTemplate@media(应用 $Number$)加 SegmentTemplate@duration
- SegmentTemplate@media($Time$ 和 $Number$)加 SegmentTimeline 元素
DVB-DASH 更举荐应用第一种办法。DASH IF 没有做举荐。
SegmentTemplate + SegmentTemplate@duration
上面是应用第一种办法的例子。
<Representation id="0" mimeType="video/mp4" codecs="avc1.42c028" bandwidth="6000000" width="1920" height="1080" sar="1:1"> | |
<SegmentTemplate | |
timescale="1000000" | |
duration="2002000" | |
availabilityTimeOffset="1.969" | |
availabilityTimeComplete="false" | |
initialization="1630889228/init-stream_$RepresentationID$.m4s" | |
media="1630889228/chunk-stream_$RepresentationID$-$Number%05d$.m4s" | |
startNumber="1" | |
></SegmentTemplate> | |
</Representation> |
下面例子中咱们首先将 SegmentTemplate@initialization 中的 $RepresentationID$ 替换成 Representation@id 获取到初始化分段的地址 1630889228/init-stream_0.m4s。(初始化分段还可能用其余形式提供,如应用 Initialization 元素。)
获取到初始化分段 URL 后,还须要确定第一个媒体分段的 URL。假如 NOW 变量是与服务器时钟同步后的以后工夫。那么咱们就能够用上面式子获取到合乎指标提早的最新残缺分段地址的 $Number$。
targetNumber = Math.floor(((NOW - MPD@availabilityStartTime - Latency@target) / 1000 - Period@start) / | |
(SegmentTemplate@duration / SegmentTemplate@timescale) | |
) + SegmentTemplate@startNumber |
而后再把 SegmentTemplate@media 中的 $Number$ 替换成 targetNumber,就结构好了第一个媒体分段的 URL 了。
SegmentTemplate + SegmentTimeline
<AdaptationSet | |
id="0" | |
mimeType="video/mp4" | |
width="512" | |
height="288" | |
par="16:9" | |
frameRate="30" | |
segmentAlignment="true" | |
startWithSAP="1"> | |
<SegmentTemplate | |
timescale="90000" media="segment_ctvideo_cfm4s_rid$RepresentationID$_cs$Time$_w743518253_mpd.m4s" initialization="segment_ctvideo_cfm4s_rid$RepresentationID$_cinit_w743518253_mpd.m4s" | |
> | |
<SegmentTimeline> | |
<S t="1614755160" d="900000"/> | |
<S d="900000"/> | |
<S d="900000"/> | |
<S d="900000"/> | |
<S d="900000"/> | |
</SegmentTimeline> | |
</SegmentTemplate> | |
<Representation id="p0va0br601091" codecs="avc1.42c015" sar="1:1" bandwidth="601091" /> | |
</AdaptationSet> |
SegmentTimeline 用来示意各个媒体分段的媒体工夫和时长,用来替换 SegmentTemplate@duration 属性。SegmentTimeline 有一堆 S 子元素,S 元素次要有 S@t、S@d 和 S@r 三个属性。
属性名 | 形容 |
---|---|
S@t | 分段媒体工夫,如果不存在则等于它上一个分段 Sp 的 Sp@t + Sp@d |
S@d | 分段时长 |
S@r | 与该分段雷同时长分段的的反复次数,默认为 0,如果为正数则示意反复到下一个 S 元素或 Period 完结 |
和第一种办法一样首先咱们须要去申请初始化分段,这里不在具体介绍。
要计算出合乎指标提早的最新残缺分段的地址,首先须要计算出指标 S@t 值,再在 SegmentTimeline 找到这个分段并计算出它的 URL。
targetT = (NOW - MPD@availabilityStartTime - Latency@target) / 1000 - | |
Period@start + (SegmentTemplate@presentationTimeOffset / SegmentTemplate@timescale) |
计算出指标 targetT 后,咱们就能够迭代 SegmentTimeline 的 S 元素,如果存在 S@r 属性则须要进行开展,找到 (S@t + S@d) / SegmentTemplate@timescale > targetT 的 S 元素,这个 S 元素就是咱们要找的指标 S 元素。
而后把 SegmentTemplate@media 中的 $Time$ 替换成指标 S 元素的 S@t 属性,$Number$ 替换成指标 S 元素在开展后的 SegmentTimeline 元素中的下标再加上 SegmentTemplate@startNumber。这样就结构好了指标媒体分段的 URL 了。
Resync
Resync 被定义与 MPEG DASH ISO/IEC 23009-1:2020/Amd.1 标准中。它定义了分段的同步点信息,通过它播放器能够进行疾速随机拜访,可是实现相似与 LLHLS 中的 EXT-X-PART。
它能够放在 AdaptationSet 或 Representation 元素下。
<Resync type="2" dT="1000000" dImin="0.1" dImax="0.15”marker="TRUE"/>
- type 等于 2 示意能够被随机拜访,0 示意 CMAF Chunk 不保障能随机拜访
- dT 示意在 timescale 下最大随机拜访点的工夫间隔。
- dImin 示意最小两个随机点之间的字节间隔 dImin * bandwidth
- dImax 示意最大两个随机点之间的字节间隔 dImax * bandwidth
- marker 为 true,示意播放器能够解析 CMAF 盒子找到同步点
低提早 ABR 算法
ABR(自适应码率)是 DASH 播放器的一个要害性能,它能够让视频在简单的网络条件下动静切换码率和播放速率,而不是中断播放升高用户体验。
在低提早下,一些基于带宽估算的 ABR 算法都不太好用。这是因为应用 Chunked transfer encoding 时,一个视频分段并没有被齐全生成,对于一个 5 秒的视频分段,一个 http 申请可能须要破费 5 秒,这个工夫并不是精确的下载工夫。在 2020 年 Twitch 和 ACM 单干举办了低提早下的 ABR 算法大挑战 Adaptation Algorithms for Near-Second Latency。较量的第一名是 Unified Streaming 的 L2A-LL(Learn2Adapt-LowLatency)算法,第二名是新加坡国立大学的 LoL(Low-on-Latency)算法。因为 Twitch 播放器不是开源的,较量是基于 dash.js 播放器,目前 dash.js 也集成了这两种 ABR 算法。
总结
LLDASH 和 LHLS 十分类似,都是应用 HTTP/1.1 的 Chunked transfer encoding 性能来升高提早提供 1 到 6 秒的低提早直播,而且能够复用现有的 CDN 网络反对大规模用户观看直播。不过 Chunked transfer encoding 性能须要浏览器反对 fetch API,所以在 IE 上会降级为应用 XHR 的一般 DASH 直播。