- estimator
设计了⼀套基于延迟和丢包反馈的拥塞机制(Google Congestion Control,简称GCC)和带宽调节策略来保证延迟、质量和⽹路速度之间平衡。
图 1 拥塞控制循环示意图
- estimator(评估者)通过RTCP的feedback反馈过来的包到达延迟增量和丢包率信息计算出⽹络拥塞状态并评估出适合当前⽹络传输的码率,根据这个码率改变视频编码器码率,然后改变pacer的码率。
- pacer(定速器)会根据这个码率改变pacer的⽹络发送速度和padding⽐例,并⽤新的⽹络发送速度来定时触发发包事件。
- sender(发送者)收到pacer的发送事件,进⾏RTP报⽂发送。
- receiver(接收者)接收到RTP报⽂,进⾏arrival time统计和丢包统计。
- feedback(反馈)定时对receiver统计的信息进⾏RTCP编码,并反馈到发送端的estimator进⾏新⼀轮的码率评估。
图 2 拥塞控制模块关系图
- estimator
estimator的功能就是通过接收端反馈过来的包到达时刻信息、丢包信息和REMB信息进⾏当前⽹络状态的码率评估,本解决方案拥塞控制有两部分:基于延迟的拥塞控制和基于丢包的拥塞控制。
它是⼀个尽⼒⽽为的拥塞控制算法,牺牲了拥塞控制的公平性换取尽量⼤的吞吐量(尽量⾃⼰多占⽤带宽)。从设计结构来描述向它输⼊延迟和丢包信息,它就会输出⼀个适应当前⽹络状态的码率值。示意图如下:
图 3 estimator输⼊与输出
estimator基于延迟的拥塞控制是通过trendline滤波再进⾏过载判断,最后根据过载情况进⾏aimd(Additive Increase Multiplicative Decrease)码率调控评估出⼀个bwe(Bandwidth Estimation) bitrate码率,这个码率会合丢包评估出来的码率和remb(Receiver Estimated Maximum Bitrate)来决定最后的码率。
1.1 基于延迟的拥塞控制
基于延迟的拥塞控制是通过每组包的到达时间的延迟差(delta delay)的增⻓趋势来判断⽹络是否过载,如果过载进⾏码率下调,如果处于平衡范围维持当前码率,如果是⽹络承载不饱满进⾏码率上调。
1.1.1)包组与延迟
我们在评估延迟差的时候不是对每个包进⾏估算,⽽是采⽤了包组间进⾏延迟评估,这符合视频传输(视频帧是需要切分成多个UDP包)的特点,也减少了频繁计算带来的误差。那么什么是包组呢?就是距包组中第⼀个包的发送时刻t0⼩于5毫秒发送的所有的包成为⼀组,第⼀个超过5毫秒的包作为下⼀个包组第⼀个包。为了更好的说明包组和延迟间的关系,先来看示意图:
图 4 包组与延迟示意图
上图中有两个包组G1和G2, 其中第100号包与103号包的时间差⼩于5毫秒,那么100 ~ 103被划作⼀个包组。104与100之间时间超过5毫秒,那么104就是G2的第⼀个包,它与105、106、107划作⼀个包组。知道了包组的概念,那么我们怎么通过包组的延迟信息得到滤波器要的评估参数呢?滤波器需要的三个参数:
*)发送时刻差值(delta_timestamp)
*)到达时刻差值(delta_arrival)
*)包组数据⼤⼩差值(delta_size)。
1.1.2) 滤波器
我们通过包组信息计算到了delta_timestamp、delta_arrival和delta_size,那么下⼀步就是进⾏数据滤波来评估延迟增⻓趋势。在我们实现了两种滤波器来进⾏延迟增⻓趋势的评估,分别是:kalman filter和trendline filter, 从图2中我们知道kalman filter是运⾏在接收端的。
这⾥介绍trendline filter(运⾏在发送端),我们知道如果平稳⽹速下传输数据的延迟时间就是数据⼤⼩除以速度,如果这数据块很⼤,超过恒定⽹速下延迟上限,这意味着要它要占⽤其他后续数据块的传输时间,那么如此往复,⽹络就产⽣了延迟和拥塞。Trendline filter通过到达时间差、发送时间差和数据⼤⼩来得到⼀个趋势增⻓值,如果这个值越⼤说明⽹络延迟越来越严重,如果这个值越⼩,说明延迟逐步下降。以下是计算这个值的过程。
先计算单个包组传输增⻓的延迟,可以记作:
然后做每个包组的叠加延迟,可以记作:
在通过累积延迟计算⼀个均衡平滑延迟值,alpha=0.9可以记作:
然后统⼀对累计延迟和均衡平滑延迟再求平均,分别记作:
我们将第i个包组的传输持续时间记作:
我们将第i个包组的传输持续时间记作:
趋势斜率分⼦值为:
趋势斜率分⺟值为:
最终的趋势值为:
1.1.3)过载检测
在计算得到trendline值后我们动态阈值gamma_1进⾏判断拥塞程度,trendline乘以周期包组个数就是m_i,以下是判断拥塞程度的伪代码:
通过以上伪代码就可以判断出当前⽹络负载状态是否发⽣了过载,如果发⽣过载,我们通过⼀个有限状态机来进⾏⽹络状态迁徙,关于状态机细节可以参看下图:
图 5 过载检测状态机
从上图可以看出,⽹络状态机的状态迁徙是由于⽹络过载状态发⽣了变化,所以状态迁徙作为了aimd带宽调节的触发事件,aimd根据当前所处的⽹络状态进⾏带宽调节,其过程是处于Hold状态表示维持当前码率,处于Decr状态表示需要进⾏码率递减,处于Incr状态需要进⾏码率递增。那他们是怎么递增和递减的呢?我们采用 aimd算法解决这个问题。、
1.1.4)AIMD码率调节
aimd的全称是Additive Increase Multiplicative Decrease,意思是:和式增加,积式减少。aimdcontroller是TCP底层的码率调节概念,但是我们并没有完全照搬TCP的机制,⽽是设计了套⾃⼰的算法,⽤公式表示为:
*)如果处于Incr状态,增加码率的⽅式分为两种:⼀种是通信会话刚刚开始,相当于TCP慢启动,它会进⾏⼀个倍数增加,当前使⽤的码率乘以系数,系数是1.08;如果是持续在通信状态,其增加的码率值是当前码率在⼀个RTT时间周期所能传输的数据速率。
*)如果处于Decrease状态,递减原则是:过去500ms时间窗内的最⼤acked bitrate乘上系数0.85,acked bitrate通过feedback反馈过来的报⽂序号查找本地发送列表就可以得到。
aimd根据上⾯的规则最终计算到的码率就是基于延迟拥塞评估到的bwe bitrate码率。
-
- 基于丢包的拥塞控制
除了延迟因素外,我们会根据⽹络的丢包率进⾏拥塞控制码率调节,描述如下:
*)当丢包率<2%时,这个时候会将码率(base bitrate)增⻓5%,这个码
率(base bitrate)并不是当前及时码率,⽽是单位时间窗周期内出现的最⼩码率,我们将这个时间窗周期设置在1000毫秒内。因为loss fraction是从接收端反馈过来的,中间会有时间差,这样做的⽬的是防⽌⽹络间歇性统计造成的⽹络码率增⻓过快⽽⽹络反复波动。
*)当2% < 丢包率 < 10%,维持当前的码率值。
*)当丢包率 >= 10%, 按丢包率进⾏当前码率递减,等到新的码率值。
2.pacer
是当视频帧被编码器编码出来后,就⽴即进⾏RTP打包发送,瞬时会发送⼤量的数据到⽹络上,可能会引起⽹络衰减和通信恶化。我们引⼊pacer,pacer会根据estimator评估出来的码率,按照最⼩单位时间(5ms)做时间分⽚进⾏递进发送数据,避免瞬时对⽹络的冲击。pacer的⽬的就是让视频数据按照评估码率均匀的分布在各个时间⽚⾥发送, 所以在弱⽹的WiFi环境,pacer是个⾮常重要的关键步骤。pacer的模型关系:
图 6 pacer 模型图
pacer的流程⽐较清晰,分为三步:
*)如果⼀帧图像被编码和RTP切分打包后,先会将RTP报⽂存在待发送的队列中,并将报⽂元数据(packet id, size, timestamp, 重传标示)送到pacer queue进⾏排队等待发送,插⼊队列的元数据会进⾏优先级排序。
*)pacer timer会触发⼀个定时任务事件来计算budget,budget会算出当前时间⽚⽹络可以发送多少数据,然后从pacer queue当中取出报⽂元数据进⾏⽹络发送。
*)如果pacer queue没有更多待发送的报⽂,但budget却还可以发送更多的数据,这个时候pacer会进⾏padding报⽂补充。
2.1 pace queue与优先级
pace queue是⼀个基于优先级排序的多维链表,它并不是⼀个先进先出的fifo,⽽是⼀个按优先级排序的list。
报⽂优先级规则:
- 优先级⾼的报⽂排在fifo的前⾯,低的排在后⾯。
2. 优先级是最先判断报⽂的QoS等级,等级越⼩的优先级越⾼
3. 其次是判断重发标示,重发的报⽂⽐普通报⽂的优先级更⾼
4. 再次是判断视频帧timestamp,越早的视频帧优先级更⾼。
pacer每次触发发送事件时是先从queue的最前⾯取出优先级最⾼的报⽂进⾏发送,这样做的⽬的是让视频在传输的过程中延迟尽量⼩,重传的报⽂尽快能到达防⽌等待卡顿。pace queue还可以设置最⼤延迟,如果超过最⼤延迟,会计算queue中数据发送所需要的码率,并且会把这个码率替代target bitrate作为budget参考码率来加速发送。
2.2 budget
budget是个评估单位时间内可以发送多少数据量的⼀个机制,因为pacer是会根据pace timer定时来触发发送检查。Budget会根据评估出来的参考码率计算这次定时事件能发送多少字节,可以表示为:
0.005*3Mbps/8 = 1,966
*)delta time是上次检查时间点和这次检查时间点的时间差.
*) target bitrate是pacer的参考码率,是由estimator根据⽹络状态评估出来的.
*) remain_bytes每次触发发包时会减去发送报⽂的⻓度size,如果remain_bytes > 0,继续从pace queue中取下⼀个报⽂进⾏发送,直到remain_bytes <=0 或者 pace queue没有更多的报⽂.
2.3 pacer延迟
那么肯定有⼈会有疑问pacer queue和budget进定量计算来发送⽹络报⽂,相当于cache等待发送,难道不会引起延迟吗?可以肯定的说会引起延迟,但延迟不严重。pacer产⽣的延迟可以表示为:
计算处理⼀帧数据发送所需要的时间 = 帧数据⼤⼩*8/bitrate
假如评估出来的码率是10mbps, ⼀个视频关键帧的⼤⼩是300KB,那么这
个关键帧造成的pacerdelay是240毫秒。从实际应⽤观察到的关键帧引起的pace delay在200 ~ 400毫秒之间,这个值相对于视频传输来说是⽐较⼤的,但是不严重。我们为了减少这个延迟,会评估出尽量⼤的bitrate。那么怎么评估出尽量⼤的码率呢?从前⾯的estimator描述中我们知道要发送出尽量多的数据才能评估尽量⼤的码率,但是视频编码器不会发送多余的数据,引⼊了padding机制来保障发送尽量⼤的数据来探测⽹络带宽上限。限制每路视频的最⼤⽐特率,⽐如3M。对于I帧适当调⾼这个bitrate。
2.4 padding
pace padding除了保障能pace delay尽量⼩外,它可以让有限的带宽获得尽可能好的视频质量。padding的⼯作原理很简单,就是在单位时间⽚内把budget还剩余的空闲⽤padding数据填满。我个⼈认为padding只是适合点对点通信,⼀旦涉及到多点分发,会因为padding占⽤很多服务转发带宽,这并不是⼀件好事情。
3 sender
我们的发送模块和拥塞控制控制相关的主要是增加了附加的RTP扩展来携带便宜接收端统计丢包率和延迟间隔的信息、配合pacer的发包策略、带宽分配和FEC策略的信息。
我们的发送模块和拥塞控制控制相关的主要是增加了附加的RTP扩展来携带便宜 接收端统计丢包率和延迟间隔的信息、配合pacer的发包策略、带宽分配和FEC策略的信息。
3.1 RTP扩展
可以看到有⼀个sequence number字段,⽤于记录RTP包的序列号。⼀般情况下我们⼀个传输通道(PeerConnection)只包含⼀路视频流,这个sequence number能满⾜⼤多数需求。但是在⼀些情况下,我们⼀个连接可能传输多个视频流,这些视频流复⽤⼀个传输通道,例如simulcast或者single PC场景,⼀个PeerConnection可能包含多个不同的视频流。在这些视频流中,RTP报头的sequence number是单独计数的。
这⾥举个例⼦,假设同⼀个PeerConnection下,我们传输两个视频流A与B,它们的RTP包记为Ra(n),Rb(n),n表示sequence number,这样我们观察同⼀个PeerConnection下,视频流按如下形式传输:
Ra(1), Ra(2),Rb(1),Rb(2),Ra(3),Ra(4),Rb(3),Rb(4)
在对某条PeerConnection进⾏带宽估计时,我们需要估计整条PeerConnection下所有视频流,⽽不是单独某个流。这样为了做⼀个RTP session(传输层)级别的带宽估计,原有各个流的sequence number就不能满⾜我们需要了。
为此Transport-cc中,使⽤了RTP报头扩展,⽤于记录transport sequence number,同⼀个PeerConnection连接下的所有流的transport sequence number,使⽤统⼀的计数器进⾏计数,⽅便进⾏同⼀个PeerConnection下的带宽估计。
这⾥我们使⽤前⾯的例⼦,视频流A与B,它们的RTP包记为Ra(n,m),Rb(n,m),n表示sequencenumber,m表示transport sequence number,这样同⼀个PeerConnection下,视频流按如下形式传输:
Ra(1,1), Ra(2,2),Rb(1,3),Rb(2,4),Ra(3,5),Ra(4,6),Rb(3,7),Rb(4,8)
这样进⾏带宽估计时,通过transport sequence number我们就能关⼼到这条传输通道下所有数据包的情况:
我们为了配合接收端进⾏延迟包序列和丢包统计做了下列扩展:
transport sequencenumber传输通道的只增sequence,每次发送报⽂时⾃增长配合接收端统计丢包、通过反馈这个sequence可以计算得到发包的时刻。
TransmissionOffset发送报⽂的相对时刻,这个相对时刻值t是发送报⽂的绝对时刻T1和视频帧时间戳T0差值。我们早期是在接收端进⾏estimate bitrate,所以过载判断是在接收端完成的,这个值就是为了kalman filter计算发包造成的延迟⽤的,新版本还携带这个值以便低版本的能兼容。
3.2 packet cache
packet cache是⼀个key/value结构的包缓冲池,视频帧在进⾏RTP分⽚打包后不会⽴即发送出去,⽽是要等待pacer的发送信号进⾏发送。所以打包后会按[id,packet]键值对插⼊到packet cache中。⼀般packet cache会保存600个分⽚报⽂,最⼤9600个,插⼊新的会将最旧的报⽂删除(超过了覆盖),packet cache这样做的⽬的除了配合pacer发送外,也为了后⾯响应nack的丢包重传。
3.3 NACK与丢包重传
图 7 RTP NACK过程的示意图
我们在评估到收发端之间RTT延迟⽐较⼩的时候会采⽤NACK来进⾏丢包补偿,NACK是⼀个请求重发过程,其流程如上图所示。这个过程有⼀个问题是在⽹络抖动和丢包很厉害的情况下有可能造成同⼀时刻收到很多NACK的重传请求,发送端瞬间把这些重传请求放⼊pacer中进⾏重发,这样pacer的延迟会增⼤,⽽且pace的参考码率会随着pace queue的延迟控制变的很⼤⽽出现间歇性⽹络⻛暴。WebRTC在处理NACK重传时设计了⼀个重传码率控制器,其设计原理是通过统计单位时间窗⼝周期中发送的字节数据来限流,如果这个时间窗内发送的数据的码率⼤于estimator评估的码率,不进⾏当前NACK请求的重传,等待下⼀个NACK。
3.4 FEC与码率分配
WebRTC应对丢包时除了NACK⽅式,在收发端之间RTT很⼤时候会开启FEC来进⾏丢包补偿,我们在这⾥不介绍FEC具体算法,只介绍FEC的码率分配策略。从整个通信机制我们很容易得出这样⼀个共识:
FEC bitrate到底应该设置多⼤呢?它先根据feedback中反馈过来的丢包率(loss fraction)来确定使⽤哪⼀种FEC,在根据每种FEC和丢包率来确定FEC使⽤的码率,但需要满⾜以下条件:
feedback的码率被设定为target bitrate的5%,我们是通过控制feekback的频率来进⾏调控分配的。padding bitrate是通过pacer queue和budget来控制的。Target bitrate减去这些码率之和就是给视频编码器的码率。每次estimator评估出来码率后,会先进⾏这些计算得到最后的video bitrate,并将这个值作为编码器的编码码率,以此来达到防⽌拥塞的⽬的。
4 receiver
receiver模块的⼯作相对来说⽐较简单,它就做三件事情:记录
- 每个报⽂的到达时刻(arrival timestamp)。
- 丢包率(lost fraction)。
3. receiver bitrate。
4.1 报⽂到达时间
图 8 到达报⽂统计图
上图是⼀个统计RTP报⽂到达时刻的序列图,图中的seq是RTP扩展中的transport sequence,接收端⽤⼀个k/v([seq,arrival timestamp])键值对数据结构来保存最近500毫秒未反馈的到达时刻信息,通过时间窗⼝周期来进⾏淘汰⽼的到达时刻记录。
4.2 丢包率计算
丢包率计算过程是这样的,我们把上次统计丢包率时刻的最⼤sequence记着prev_seq, 把当前收到的最⼤sequence记着cur_seq,当前统计丢失的报⽂记着count,我们在RTCP中描述丢包率采⽤的是uint8,为了保证精确度将256记着100%的丢包率,那么很容易得:
这⾥需要提的是WebRTC在统计报⽂是否丢失是通过sequence的连续性和⽹络的jitter时间来确定的,只有落在jitter抖动范围之外的丢包才是算是作丢包。
4.3 接收码率统计
接收端码率统计采⽤的是最近单位时间窗⼝(1000毫秒)周期内收到的的字节数来计算,WebRTC设计了⼀个1毫秒为最⼩单位的窗⼝数组来进⾏统计,每个最⼩单位是数字,这个数字是在这个时刻收到的⽹络数据⼤⼩,⼤致的示意图如下:
图 9 接收码率统计示意图
计算码率只需要将红框中所有的数字加起来,当时间发⽣改变后,就红框就向右移动并且填写新时刻接收到的数据⼤⼩,等下⼀个统计时刻既可。
5 feedback
前⾯介绍的estimator依赖于feedback反馈的报⽂到达时刻和丢包率来进评估码率的,也就是说feedback需要将这些信息及时反馈给接收端,主要是记录的报⽂到达时刻、通道丢包率和remb带宽。因为报⽂到达时刻和丢包率统计都是多个数据项,WebRTC利⽤了report block来进⾏编码存放。为了有效的利⽤RTCP的report block空间,WebRTC采⽤了相对时间转换和位压缩算法来对到
达时间序列做编码压缩。
除了report编码,feedback的周期也很重要,如果是单纯的remb反馈,⼀般是1秒⼀次反馈。但如果是需要反馈报⽂的到达时间,它会根据占⽤5%的target bitrate来计算发送feedback的时间间隔,计算流程如下:
feedback interval需要满⾜⼀个条件:50ms < interval < 250ms,这个条件中的 50ms< internal是为了防⽌interval太⼩造成发送feedback太过频繁⽽消耗⽹络性能,⽽interval < 250ms是为了防⽌feedback频次太低造成estimator反应迟钝(反馈的信息已经迟滞)。