此图是接收端码率控制整体结构图分成3个部分。
第一部分采集和发送:camera encode通过Pacer并结合fec发送。
第二部分基于延时的码率计算(卡尔曼模型)。
第三部分是结合丢包和延时计算出综合码率发送编码器。
notes:发送端带宽估计是一种算法。
算法通过5步评估出基于延时的码率.1->2->3->4&5。
到达时间模型:计算包组延时。
到达时间滤波器:trendline即y=bx+a;x,y带入公式,计算斜率b,就是延时趋势.通过延时趋势计算延时。
过载检测器:通过延时趋势trendline即协议b与门限比较,获得当前网络状态overuse,underuse, or normal。
自适应阀值(门限):阀值也是每次更新。
速率控制:根据过载检测器计算的网络状态结合当前码率状态,根据上面的流程图,获取新的码率状态并计算新的码率(不变,加,乘,减)。
transport-cc是接收端发送给发送端的recv ts,是该算法的输入。
卡尔曼滤波和trendline滤波类似,只是将第二步trendline滤波改成了卡尔曼滤波。
接收端维护两个计数器,每收到一个RTP包都更新:
transmitted,接收到的RTP包的总数;
retransmitted,接收到重传RTP包的数量;
某时刻收到的有序包的数量Count = transmitted-retransmitte,当前时刻为Count2,上一时刻为Count1;
接收端以一定的频率发送RTCP包(RR、REMB、NACK等)时,会统计两次发送间隔之间(fraction)的接收包信息。
接收端发送的RR包中包含两个丢包:
一个是fraction_lost,是两次统计间隔间的丢包率(以256为基数换算成8bit)。
一个是cumulative number of packets lost,是总的累积丢包。
1) 两次发送间隔之间理论上应该收到的包数量=当前收到的最大包序号-上个时刻最大有序包序号。
2) 两次发送间隔之间实际接收到有序包的数量=当前时刻收到的有序包的数量-上一个时刻收到的有序包的数量 uint32_t rec_since_last = Count2 - Count1。
3) 丢包数=理论上应收的包数-实际收到的包数。
4)int32_t missing = exp_since_last - rec_since_last,missing即为两次发送间隔之间的丢包数量,会累加并通过RR包通知发送端。
基于丢包的拥塞控制比较简单,其基本思想是根据丢包的多少来判断网络的拥塞程度.
丢包越多则认为网络越拥塞,那么我们就要降低发送速率来缓解网络拥塞;
如果没有丢包,这说明网络状况很好,这时候就可以提高发送码率,向上探测是否有更多的带宽可用.
实现该算法有两点:一是获得接收端的丢包率,一是确定降低码率和提升码率的阈值。
发送端收到RTCP RR报文并接收丢包率后,根据公式计算发送端码率:当丢包率大于0.1时,说明网络发生拥塞,此时降低发送端码率,当丢包率小于0.02时,说明网络状况良好,此时增大发送端码率,其他情况下,发送端码率保持不变。
Gcc算法在发送端基于丢包率控制发送码率,其基本思想是:丢包率反映网络拥塞状况。如果丢包率很小或者为0,说明网络状况良好,在不超过预设最大码率的情况下,可以增大发送码率;反之如果丢包率变大,说明网络状况变差,此时应减少发送端码率,在其他的情况下,发送端码率保持不变。
Gcc使用的丢包率根据接收端RTP接收统计信息计算得到,通过RTCP RR报文返回给发送端,RTCP RR报文统计接收端RTP接收信息,如Packet Loss,Jitter,DLSR等等。
最终码率会作用于Encoder,RTP, Pacedsender 模块,用于在编码器内部调整码率和平滑发送码率。
接收端会通过rtcp rr将丢包率和丢包累计值反馈给发送端,发送端据此更新丢包率和rtt,具体逻辑后面会介绍 。
在发送端带宽估计由3个元素结合决定:
基于丢包率估算的带宽(丢包率通过rtcp rr得到)、接收端的remb反馈的带宽、发送端带宽估,取三者带宽的最小值作为最终带宽估计值。
从上图可以看出,estimator基于延迟的拥塞控制是通过trendline滤波再进行过载判断,经过overuse detector,最后根据过载情况进行aimd码率调控评估出一个bwe bitrate码率.
bwe bitrate码率会合丢包评估出来的码率和remb来决定最后的码率。
notes:remb bitrate 仅做向下兼容使用。兼容老版本的KalmanFilter算法。
https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
总结来看发送端会在session的层面统一对rtp包进行计算transport-cc sequence num,区别于之前的sequence num,原因是transport-cc目的是计算两点之间的带宽,两点通信可以传输多个视频流。
包组
在 WebRTC 中,延迟梯度不是一个个包来计算的,而是通过将包分组。然后计算这些包组之间的延迟,这样做可以减少计算次数,同时减少误差。
在一个burst_time间隔内的一系列的包构成一个组。建议burst_time为5ms。
任意到达时间间隔小于 burst_time 并且组间延迟差 d(i)< 0 的组都被考虑做为当前组的一部分。
比如:下图中有两个包组G1和G2,其中第100号包与103号包的时间差小于5毫秒,那么100 ~ 103被划作一个包组.104与100之间超过5毫秒,那么104就是G2的第一个包,它与105,106,107划作一个包组。
首先我们通过一张图看下时延梯度的计算:
包组时间差计算-InterArrival,主要包含几个包组时间差计算的概念:
notes: Trendline 算法计算延时梯度是基于包组的,不是基于包的。而RTT延时计算是基于单个包的.
Trendline是接收端计算延时,RTT是发起端计算延时,不要混淆。
3.3 Trendline滤波器
最新版本webrtc将kalman filter改成了trendline filter,总体流程:终端通过服务器发送的transport cc中,获取recv time-->计算包组延时-->trendline filter(线性回归,根据N组xy,计算斜率)-->过载检测器(与阀值比较)-->动态阈值(计算新阀值)-->计算新码率(根据state计算加减码率的量)。
1 ) 获取接收时间 Transport-cc:
Send-side码率评估,需要receive side将包的接收时间发给send-side,以便计算包组延时。
Receive side 使用RTCP反馈信息transport-cc-feedback,返回接收包的时间。
该消息负责反馈接收端,收到的所有媒体包的到达时间。
接收端根据包间的接收延迟和发送间隔可以计算出延迟梯度,从而估计带宽。
2) 时延梯度趋势计算:i
获取(arrival_time_ms, smoothed_delay)时间对之后,WebRTC用到了线性回归这个数学方法进行时延梯度趋势预测.通过最小二乘法求得拟合直线斜率,根据斜率判断增长趋势。
计算过程:
(1) 先计算单个包组传输增长的延迟,可以记作:
(2) 做每个包组的叠加延迟,可以记作:
在通过累积延迟计算一个均衡平滑延迟值,alpha=0.9可以记作:
notes:方法和RTT评估一样,根据上次和这次两次的结果,乘以不同权重得出结果 。
(3) 统一对累计延迟和均衡平滑延迟再求平均,分别记作:
(4) Trendline 计算:
我们将第i个包组的传输持续时间记作:
趋势斜率分子值为:
趋势斜率分母值为:
最终的趋势值为:
总之:对于一堆样本点(x,y),拟合直线方程y=bx+a的斜率b按如下公式计算:
该函数使用最小二乘法求解线性回归,输入 window_size_ 个样本点(arrival_time_ms, smoothed_delay),输出延迟梯度变化趋势的拟合直线斜率 trendline_slope。
3.5 过载检测器:Detector函数(overuse,underuse,normal)
该函数主要根据时延变化增长趋势,计算当前网络状态。
在Detect函数内部,会根据前面计算得到的斜率得到一个调整后的斜率值:modified_trend
:
1 // 乘以包组数量以及阈值增益,因为计算得到的trend常常是一个非常小的值const double
2 modified_trend = std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_;
通过增益放大trend. 然后与一个动态阈值threshold_
做对比,从而得到网络状态 :
modified_trend > threshold_
,且持续一段时间,同时这段时间内,modified_trend
没有变小趋势,认为处于overuse状态(网络发生拥塞)。modified_trend < -threshold_
,认为处于underuse状态(当前带宽未充分利用)。-threshold_ <= modified_trend <= threshold_
,认为处于normal状态。modified_trend
就是trendline算出斜率trendline。 注意:延时趋势和延时的区别:trendline计算的斜率不是延时,而是延时趋势。
0
如下图所示,上下两条红色曲线表示动态阈值,蓝色曲线表示调整后的斜率值,阈值随时间动态变化,调整后的斜率值也动态变化,这样网络状态也动态变化。
前几节所介绍的内容总结来看:通过延时梯度+trendline滤波器,估算延时梯度斜率值,并与自适应阈值比较得出当前网络状态(underuse、overuse、normal)。
本节则是以此作为输入事件,再结合当前网络控制状态(decr、incr、hold)+当前码率值,得出估算码率值。callladder:transportcc arrive time===>trendline滤波(延时趋势&延时m(ti))===>网络状态(动态阀值)(通过m(ti)与r(ti)比较===>underuse/overuse/normal->kd/ku 阀值)===>amid网络控制状态(加or乘(decr,incr,hold)当前码率)=新码率)。
3.8速率控制状态机 (Amid)(kd/ku 是对阀值增减,amid的阿尔法和兰姆达才是对码率增减)。
Aimd的全称是Additive Increase Multiplicative Decrease,意思是:和式增加,积式减少。
总体的思想是根据当前接收方的接收码率,结合当前的网络负载情况,进行AIMD码率调整:
notes:GCC的码率控制类似于tcp 网络检查方案。tcp方案是距离临界点远的时候,乘性增加。距离临界点近的时候,加性增加。网络过载则码率评估折半。
该模块以过载检测器给出的当前网络状态s(过载检测器:Detector函数)为输入,首先根据图X所示的有限状态机判断当前码率的变化趋势,然后根据图X所示的公式计算目标码率Ar。
下面是三个信号和三个状态的关系(难点):
三个估算的目标码率状态:hold、increase、decrease 。
三个网络带宽状态:underuse、overuse、normal。
从按照路由器buffer角度看这3个信号:
overuse-->buffer再堆积
underuse-->buffer再减少 (可用带宽估计值 A_hat 低于实际的可用带宽)
normal--->没有buffer的状态
最难理解的是网速的underuse状态:
这种状态发送一个包的时间,接收端收到两个包,带宽处于underuse,通常认为应该增加发送量。实际这个状态是路由器排空过程中(使通路不排队,恢复畅通,恢复期不适合提高码率),发送量应该保持不变即状态保持Hold不变,不增加发送量。
当路由器的buffer排空之后,发送一个包,接收一个包,带宽处于normal。此时状态变成Increase,增加发送量,测试最大码率。
notes:发送一个包,接收一个包的意思是:比如:上一个包接收时间是5ms,这个包也是5ms.
发送一个包,接收两个包的意思是:比如:上一个包接收时间是10ms,这个包接收的时间是5ms。
说明路由器queue在变小,排空queue中。好比:上一个包时路由器queue新包之前有100个包排队,这次新包之前有50个包排队。
这种调整是非常正确的,不排空会出现,变成路由buffer全部满,造成瞬间断网。而且会一会一断.
排空不会出现断网,会在某个区间动态变化,比如:好的时候增加到100M,差的时候退回到50M,不会出现断网,用户看视频等也不耽误,只是标清和高清变化。
先通过延时趋势trendline即协议b与门限比较,获得当前网络状态overuse,underuse, or normal.然后结合当前码率状态,根据上面的流程图,获取新的码率状态并计算新的码率(不变,加,乘,减)。
根据不同网络带宽状态,目标码率状态转化 :
目标码率Ar变化趋势有限状态机(流程图) :
流程图说明:
目标码率处于Decrease状态,当前网络过载时,目标码率将继续处于Decrease状态;(目标码 率将继续减小,发送量将继续减少。
目标码率处于Decrease状态,当前网络低载或者正常时,目标码率从Decrease状态变成Hold状态;(目标码率将保存不变,发送量不变)。
目标码率处于Hold状态,当前网络低载,目标码率将继续处于Hold状态(目标码率不变,发送量不变,为了排空路由buffer)。
目标码率处于Hold状态,当前网络normal时,目标码率将变成Increase状态(目标码率将增加,发送量增加)。
目标码率处于Increase状态,当前网络normal时,目标码率将继续处于Increase状态(目标码率将增加,发送量增大)。
目标码率处于Increase状态,当前网络underuse时,目标码率将变成Hold状态.(目标码率将停止增长,发送量保存不变,有underuse说明,发的少收的多,正在排空路由器。如果路由buffer没有包,则不可能进入underuse,只能是hold)。
目标码率处于Increase状态,当前网络overuse时,目标码率将变成Decrease状态.(目标码率将减少,发送量减少)。
4.0 状态机的二维表如下
4.1计算目标码率
当判断出码率变化趋势后,根据图所示公式进行计算目标码率。
目标码率Ar计算公式:
当码率变化趋势为Increase时,当前码率为上次码率乘上系数1.08;
当码率变化趋势为Decrease,当前码率为过去500ms内的最大接收码率乘上系数0.85。
当码率变化趋势为Hold时,当前码率保持不变
基于延时的带宽估计可以在网络链路发生丢包以前就监测到网络拥塞,它可以通过侦测数据包接收的时延来预测未来可能的拥塞。它是基于链路上的路由器都有一定的缓存,在数据包开始被丢弃之前,先发生数据在缓存里堆积的事件,所以时延相比于丢包,对拥塞的反应更加灵敏。
notes:基于丢包判断网上,已经产生了丢包的后果。基于延时判断,则是发现路由器已经缓存,说明网速在下降,减少发送量,这样不会丢包。
基于延时梯度的带宽估计是WebRTC GCC最为核心的拥塞控制算法,此算法在最新版的WebRTC有两种实现方式,一种是在接收端、另一种在发送端。
Send-side BWE. Rtp 扩展 transport sequence number
Receive-side BWE. Rtp 扩展 abs-send-time函数
两者唯一区别是到达时间滤波器不同,接收端是卡尔曼滤波器,发送端是trendline滤波器,其他完全相同。
这个算法有4个模块组成:
每个模块的作用:
Trendline |
计算延时斜率. 斜率*包组数就是延时 |
Dectector |
与动态阀值比较. overuse,underuse,normal |
DelayBaseRate |
基于接收端网络延迟,估算出的码率 |
AMID rate |
加法或者乘法调整码率 |
这个算法一个难点:
三个码率状态和网络带宽状态 decrease,hold,Increase;normal,underuse,overuse关系
一个注意的地方:判断状态的时候跟动态门限比较,更新状态之后,门限也要更新(门限不是固定的)。
基于延时评估有五个关键技术:包组延迟评估,滤波器趋势判断,过载检测,动态更新门限阀值和码率调节。
1) DelayBasedBwe::IncomingPacketFeedbackVector
2)elayBasedBwe::IncomingPacketFeedback
3)DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate
4)DelayBasedBwe::UpdateEstimate
5) AimdRateControl::Update
6)AimdRateControl::ChangeBitrate
7.1)TrendlineEstimator::Update(包组评估延时和滤波趋势判断)
7.2)TrendlineEstimator::Update(包组评估延时和滤波趋势判断)
参数说明:recv_delta_ms:包组接收时间差;send_delta_ms:包组发送时间差;send_time_ms:当前处理的RTP的包发送时间;arrival_time_ms:当前处理的RTP的包到达时间;packet_size:当前处理的RTP包的大小。
此函数重点关注下面三件事:
计算当前的延迟梯度累计值,如代码里的accumulated_delay_。
计算延迟梯度累计值的一次指数平滑值,如代码里的smoothed_delay_,同时也是最小二乘法的y。
LinearFitSlope:滤波器趋势判断。
最小二乘法线性回归求延迟梯度趋势斜率a,如代码里的trendline_。
预测的斜率值 trendline_可以表征网络的拥塞程度(网络缓冲区,即路由器数据包排队的消涨情况)。
trendline_过大,表征d(i)>0网络拥塞。
trendline_适中,表征d(i)=0网络正常。
trendline_过小,表征d(i)<0网络空闲
7. 3) TrendlineEstimator::Detect函数(过载检测)
通过计算得到的时延变化趋势拟合直线斜率,发送时间差,到达时间判断网络状态:
(三个网络状态,三个码率变化状态关系) 7.4 UpdateThreshold函数(更新动态阀值计算公式)
阈值threshold_动态调整为了改变算法对时延梯度的敏感度。根据[1]主要有以下两方面原因:
1)时延梯度是变化的,有时很大,有时很小,如果阈值是固定的,对于时延梯度来说可能太大或者太小,这样就会出现检测不够敏感,无法检测到网络拥塞,或者过于敏感,导致一直检测为网络拥塞;
2)固定的阈值会导致与TCP(采用基于丢包的拥塞控制)的竞争中被饿死。
这个阈值根据如下公式计算:
每处理一个新包组信息,就会更新一次阈值,其中ΔT表示距离上次阈值更新经历的时间,m(ti)是前面说到的调整后的斜率值modified_trend
。
kd与ku分别决定阈值增加以及减小的速度。
detalT是包组到达的时间差.
当m(ti)在阈值范围内时(-r~r),减小阈值,反之则增大阈值,kr决定了阈值增大和减小的速度,【Gcc-analysis】建议kd=0.00018,ku=0.01,【Gcc-analysis】有详细的推导过程。阈值上涨速度要大于阈值下降速度,保证和TCP竞争的公平性。
AimdRateControl::ChangeBitrate(码率调节)
(1)增加
1)加性增加
2)乘性增加
2) 降低
AimdRateControl::UpdateMaxBitRateEstimate
小结:1 通过延时梯度+trendline滤波器,估算延时梯度斜率值。
2 与自适应阈值比较得出当前网络状态(underuse、overuse、normal)。
3 以2的结果作为输入事件,再结合当前网络控制状态(decr,incr,hold)+当前码率值,得出估算码率
webrtc 发送端码率控制:
八、总结
WebRTC的带宽估计会参考3个因素,丢包率、接收端带宽估计、发送端带宽估计,然后取三者最小值作为最终的估计码率。WebRTC的带宽估计是整个QoS优化的非常重要的一个环节,在弱网优化场景,会根据带宽估计作为输入,执行大小流、SVC、带宽分配、优先级等弱网降级策略。
其他:
早期的 KalmanFilter 算法是在接收端根据网络延时,计算出合适的带宽,通过 REMB RTCP 报文反馈给发送端,让发送端按照该码率发送视频数据。
在此种实现中,为了准确计算延迟梯度,WebRTC添加了一种RTP扩展头部abs-send-time, 用来表示每个RTP包的精确发送时间,从而避免发送端延迟给网络传播延迟的估计带来误差.
kalman filter 参见:
WebRTC视频接收缓冲区基于KalmanFilter的延迟模型 - 简书
可能是讲解最清楚的Kalman filter - 知乎
其他优秀参考文献
WebRTC基于TransportCC和Trendline Filter的发送端码率估计(Sendside-BWE) - 简书
WebRTC基于GCC的拥塞控制(上) - 算法分析 - 简书
webrtc视频接收端带宽预测(二)---卡尔曼滤波 - 简书