google 在webrtc上的拥塞控制rfc总结
1 实时媒体的拥塞控制的 挑战/困难点/特点:
1)媒体通常是被编码为格式以致于不能很快的调整来适应多变的带宽,
并且带宽要求经常只能进行离散的改变而不是大步调整。
2) 参与者可能会对如何响应有具体的期望,这可能不会减少流所需的带宽当发现拥塞时。
3) 编码通常对丢包敏感,实时传输要求尽量避免出现重传修复丢包的情况。
2 分类:两种拥塞控制算法;
gcc认为,这两种拥塞控制,
一种是基于丢包的控制器 - 进行丢包率测量、往返时间测量和 REMB 消息,并计算目标发送比特率。并以此判定拥塞情况,做拥塞控制。
另一种是 基于延迟的控制器 - 获取数据包到达信息,或者在 RTP 接收器,或从 RTP 发送器接收到的反馈,并计算它传递给基于丢包的控制器的最大比特率。
通过延迟的变化和具体情况,来判定拥塞情况,做拥塞控制。
gcc 是基于丢包的控制器和基于延迟的控制器一起实现拥塞控制算法。并提供了两种方式:
1) 一种是两个控制器都在发送端运行:
具体:
第一个版本可以通过使用 [I-D.holmer-rmcat-transport-wide-cc-extensions] 中描述的每包反馈协议来实现。
在这里,RTP 接收器将记录每个接收到的数据包的到达时间和传输范围的seqnum,这些信息将使用传输范围的反馈消息定期发送回发送方。
推荐的反馈间隔是每接收到一个视频帧一次,如果是纯音频或多流,则至少每 30 毫秒一次。 如果需要限制反馈开销,该间隔可以增加到 100 毫秒。
发送方将接收到的{序列号,到达时间}对映射到反馈报告涵盖的每个数据包的发送时间,并将这些时间戳提供给基于延迟的控制器。 它还将根据反馈消息中的序列号计算丢失率。
2)另一种是基于延迟的控制器在接收端运行,基于丢包的控制器在发送端运行。
第二个版本可以通过在接收端有一个基于延迟的控制器来实现,监控和处理传入数据包的到达时间和大小。
发送方应该使用 abs-send-time RTP 头扩展 [abs-send-time] 使接收方能够计算组间延迟变化。 基于延迟的控制器的输出将是一个比特率,它将使用 REMB 反馈消息 [I-D.alvestrand-rmcat-remb] 发送回发送方。
丢包率通过 RTCP 接收器报告发回。 在发送方,REMB 消息中的比特率和丢失的数据包部分被送入基于丢失的控制器,后者输出最终目标比特率。 建议在检测到拥塞时立即发送 REMB 消息,否则至少每秒发送一次。
不管怎样最终都是发送方根据信息得到最后的发送bitrate,并调整速率进行拥塞控制;
3 详细阐述基于延迟的控制器:注意是接收方才能统计到延迟
- 组成:
基于延迟的控制算法可以进一步分解为三部分:到达时间滤波器、过度使用检测器和速率控制器。
2)Arrival-time model 到达时间模型
抖动的定义:组间延迟变化
我们将到达间隔时间 t(i) - t(i-1) 定义为两个数据包或两组数据包的到达时间差。
相应地,出发间隔时间T(i)-T(i-1)被定义为两个数据包或两组数据包的出发时间差。 最后,组间延迟变化 d(i) 被定义为到达间隔时间和出发间隔时间之间的差值。
或者不同的解释,作为第 i 组和第 i-1 组延迟之间的差异。
d(i) = t(i) - t(i-1) - (T(i) - T(i-1))
不过我个人觉得,写成这样好理解些,是等价的:
d(i) = t(i) - T(i) - (t(i-1) - T(i-1)) 意义是两组数据包在空中停留的时间差异,也是组间延迟差异。如何选择一组的时间范围?如何定义ti, Ti
在接收端,我们观察传入的数据包组,
其中一组数据包定义如下:在 burst_time 间隔内发送的数据包序列组成一个群体。 Burst_time 的推荐值为 5 ms。
此外,具有小于burst_time的到达间隔时间和小于0的组间延迟变化d(i)的任何分组也被认为是当前分组分组的一部分。
将这些数据包包含在组中的原因是为了更好地处理延迟瞬变,这是由于数据包因与拥塞无关的原因排队而引起的。 例如,在许多 Wi-Fi 和无线网络上都观察到了这种情况。Ti:
连续组之间的出发间隔时间计算为 T(i) - T(i-1),其中 T(i) 是当前正在处理的分组组中最后一个分组的出发时间戳。
任何无序接收的数据包都会被到达时间模型忽略。ti:
每个组被分配一个接收时间 t(i),它对应于收到组的最后一个数据包的时间。延迟的变化di:
如果 t(i) - t(i-1)> T(i) - T(i-1),即d(i)大于0,则一组是相对于其前任延迟,
即,如果到达间隔时间大于出发间隔时间基于容量C(类似于带宽), 组数据包大小,时间建模:
由于在容量为 C 的路径上发送一组大小为 L 的数据包的时间 ts 大致为ts = L/C,类似路程/速度=时间
我们可以将组间延迟变化建模为:1
2
3
4
5
6d(i) = L(i)/C(i) - L(i-1)/C(i-1) + w(i) =
L(i)-L(i-1)
= -------------- + w(i) = dL(i)/C(i) + w(i)
C(i)Here, w(i) is a sample from a stochastic process W, which is a
function of the capacity C(i), the current cross traffic, and the
current sent bitrate. C is modeled as being constant as we expect it
to vary more slowly than other parameters of this model. We model W
as a white Gaussian process. If we are over-using the channel we
expect the mean of w(i) to increase, and if a queue on the network
path is being emptied, the mean of w(i) will decrease; otherwise the
mean of w(i) will be zero.
这里,w(i) 是来自随机过程 W 的样本,它是容量 C(i)、当前交叉流量和当前发送比特率的函数。
C 被建模为常数,因为我们预计它比该模型的其他参数变化得更慢。 我们将 W 建模为白高斯过程。
如果我们过度使用通道,我们预计 w(i) 的均值会增加,如果网络路径上的队列被清空,则 w(i) 的均值会减少; 否则 w(i) 的平均值将为零。
Breaking out the mean, m(i), from w(i) to make the process zero mean,
从 w(i) 中取出均值 m(i) ,使过程均值为零,
我们得到
等式 1
d(i) = dL(i)/C(i) + m(i) + v(i)
这是我们的基本模型,我们考虑到一大群数据包比一小群数据包需要更多的时间来遍历链路,因此到达时具有更高的相对延迟。
噪声项表示模型未捕获的网络抖动和其他延迟效应。
//https://www.cnblogs.com/ishen/p/15000909.html#!comments
3) 到达时间过滤:Arrival-time filter
上面建模后,其实我们想估计的是C(i), m(i) ,即当前合适的发送速率。
参数 d(i) 和 dL(i) 可用于每组数据包,i>1,我们想估计 C(i) 和 m(i) 并使用它们
估计以检测瓶颈链路是否被过度使用。这些参数可以通过任何自适应滤波器来估计——我们是使用卡尔曼滤波器。
这里是通过数据包传输的时延这个间接值对带宽进行估算。
1 | Let |
where f_max = max {1/(T(j) - T(j-1))} for j in i-K+1,…,i is the
highest rate at which the last K packet groups have been received and
chi is a filter coefficient typically chosen as a number in the
interval [0.1, 0.001]. Since our assumption that v(i) should be zero
mean WGN is less accurate in some cases, we have introduced an
additional outlier filter around the updates of var_v_hat. If z(i) >
3sqrt(var_v_hat) the filter is updated with 3sqrt(var_v_hat) rather
than z(i). For instance v(i) will not be white in situations where
packets are sent at a higher rate than the channel capacity, in which
case they will be queued behind each other.
4)过量使用检测器:Over-use detector
- 检测1:
作为到达时间滤波器的输出获得的偏移估计 m(i) 与一个阈值 gamma_1(i) 进行比较。 高于阈值的估计被视为过度使用的迹象。
这种指示不足以使检测器向速率控制子系统发出过度使用信号。 只有在至少 gamma_2 毫秒内检测到过度使用时,才会发出明确的过度使用信号。
但是,如果 m(i) < m(i-1),即使满足上述所有条件,也不会发出过度使用信号。 类似地,当 m(i) < -gamma_1(i) 时,检测到相反的状态,即使用不足。
如果没有检测到过度使用和使用不足,则探测器将处于正常状态。阈值 gamma_1 对算法的整体动态和性能有显着影响。 特别是,已经表明,
使用静态阈值 gamma_1,由所提出的算法控制的流可能会被并发 TCP 流 [Pv13] 饿死。 可以通过将阈值 gamma_1 增加到足够大的值来避免这种饥饿。
上面的整理为:
检测到,m(i)>m(i-1) && m(i)>gamma_1(i) 表示过度使用,在gamma_2 毫秒内检测到过度使用时,发出过度使用信号。
为什么?
原因是,通过使用较大的 gamma_1 值,可以容忍较大的排队延迟,而对于较小的 gamma_1,
过度使用检测器通过生成过度使用快速响应偏移估计 m(i) 的小幅增加 - 使用减少可用带宽 A_hat 的基于延迟的估计的信号(参见第 4.4 节)。
因此,有必要动态调整阈值 gamma_1 以在最常见的场景中获得良好的性能,例如与基于损失的流竞争时。
出于这个原因,我们建议改变阈值 gamma_1(i)
根据以下动态方程:
1 | gamma_1(i) = gamma_1(i-1) + (t(i)-t(i-1)) * K(i) * (|m(i)|-gamma_1(i-1)) |
基本原理是当 m(i) 超出范围 [-gamma_1(i-1),gamma_1(i-1)] 时增加 gamma_1(i),而当偏移估计值 m(i) 回落到 范围内,gamma_1 减小。
通过这种方式,当 m(i) 增加时,例如由于 TCP 流进入同一瓶颈,gamma_1(i) 会增加并避免过度使用信号的不受控制的生成,
这可能导致所提出的算法控制的流饥饿 [Pv13]。
此外,如果此条件成立,则不应更新 gamma_1(i): |m(i)| - gamma_1(i) > 15
还建议将 gamma_1(i) 限制在 [6, 600] 范围内,因为太小 gamma_1(i) 会导致检测器变得过于敏感。
另一方面,当 m(i) 回落到范围 [-gamma_1(i-1),gamma_1(i-1)] 时,阈值 gamma_1(i) 会降低,从而可以实现较低的排队延迟。
建议选择 K_u > K_d,以便 gamma_1 增加的速率高于其减少的速率。
通过此设置,可以在并发 TCP 流的情况下增加阈值并防止饥饿以及强制执行协议内公平性。
1 | gamma_1(0)、gamma_2、K_u 和 K_d 的推荐值分别为 12.5 ms、10 ms、0.01 和 0.00018。 |
那么发出过度使用后,速率等如何调整呢?
5)速度控制:Rate control
- 组成:
速率控制分为两部分,一部分控制基于延迟的带宽估计,另一部分控制基于丢包的带宽估计。
只要没有检测到拥塞,两者都旨在增加对可用带宽 A_hat 的估计,并确保我们最终将匹配信道的可用带宽并检测过度使用。
一旦检测到过度使用,由基于延迟的控制器估计的可用带宽减少。这样我们获得可用带宽的递归和自适应估计。
过程:
在本文档中,我们假设速率控制子系统定期执行,并且这个周期是恒定的。
速率控制子系统有 3 种状态:增加、减少和保持。
“增加”是没有检测到拥塞时的状态;
“减少”是检测到拥塞的状态,
“保持”是一种状态在“增加”之前等待直到已建立的队列耗尽。
这个过程有点类似于bbr状态转换:
状态转换(空白字段表示“保持状态”)1
2
3
4
5
6
7
8
9
10
11+----+--------+-----------+------------+--------+
| \ State | Hold | Increase |Decrease|
| \ | | | |
| Signal\ | | | |
+--------+----+-----------+------------+--------+
| Over-use | Decrease | Decrease | |
+-------------+-----------+------------+--------+
| Normal | Increase | | Hold |
+-------------+-----------+------------+--------+
| Under-use | | Hold | Hold |
+-------------+-----------+------------+--------+状态处理过程:
子系统以增加状态开始,它将一直保持到检测器子系统检测到过度使用或使用不足。在每次更新时,可用带宽的基于延迟的估计增加,乘法或加法,取决于它的当前状态。
如果当前带宽估计看起来离收敛很远,则系统进行乘法增加, 这里的收敛,我的理解是在卡尔曼滤波上的方差还比较大,速率还在递增
而如果看起来更接近收敛,则系统进行加法增加。
如果当前传入的比特率 R_hat(i) 接近我们之前处于降低状态时的传入比特率的平均值,我们假设我们接近收敛。
“Close” 定义为围绕该平均值的三个标准偏差。 建议使用平滑因子为 0.95 的指数移动平均来测量此平均值和标准偏差,因为预计此平均值涵盖我们处于减少状态的多个场合。
当这些统计数据的有效估计不可用时,我们假设我们还没有接近收敛,因此仍处于乘法增加状态。
如果 R_hat(i) 增加到平均最大比特率的三个标准偏差以上,我们假设当前的拥塞程度已经改变,此时我们重置平均最大比特率并返回乘法递增状态。
R_hat(i) 是基于延迟的控制器在 T 秒窗口内测量的传入比特率:
1 | R_hat(i) = 1/T * sum(L(j)) for j from 1 to N(i) |
- 在乘法增加期间,估计最多增加每秒 8%。
1
2eta = 1.08^min(time_since_last_update_ms / 1000, 1.0)
A_hat(i) = eta * A_hat(i-1) //这里的A_hat(i)是可用带宽 - 在加法增加期间,每个 response_time 间隔最多增加半个数据包的估计值。 response_time 间隔估计为往返时间加上 100 ms 作为过度使用估计器和检测器反应时间的估计。
1
2
3
4
5
6
7
8response_time_ms = 100 + rtt_ms
beta = 0.5 * min(time_since_last_update_ms / response_time_ms, 1.0)
A_hat(i) = A_hat(i-1) + max(1000, beta * expected_packet_size_bits)
Expected_packet_size_bits 用于在较低比特率下获得略微较慢的附加增加斜率。 例如,它可以通过假设每秒 30 帧的帧速率从当前比特率计算:
bits_per_frame = A_hat(i-1) / 30
packets_per_frame = ceil(bits_per_frame / (1200 * 8))
avg_packet_size_bits = bits_per_frame / packets_per_frame
由于系统依赖于过度使用信道来验证当前可用带宽估计,我们必须确保我们的估计不会偏离发送方实际发送的速率。
因此,如果发送方无法以拥塞控制器要求的比特率生成比特流,则可用带宽估计应保持在给定范围内。 因此我们引入了一个阈值
1 | A_hat(i) < 1.5 * R_hat(i) A_hat(i)是估计出来的可用带宽 |
- 当检测到过度使用时,系统过渡到减少状态,其中基于延迟的可用带宽估计是减少到当前传入比特率的倍数。当探测器向速率控制子系统发送正在使用的信号时,我们知道网络路径中的队列正在被清空,这表明我们的可用带宽估计 A_hat 低于实际可用带宽。
1
2
3A_hat(i) = alpha * R_hat(i),//当前传入的比特率 R_hat(i)
alpha 通常选择在区间 [0.8, 0.95] 中,0.85 是推荐值。
根据该信号,速率控制子系统将进入保持状态,在等待队列稳定在较低水平的同时,接收端可用带宽估计将保持不变——这是一种保持延迟尽可能低的方法。
在由于过度使用而减少估计值之后立即需要并预期这种延迟减少,但如果某些链路上的交叉流量减少,也会发生这种情况。
建议更新 A_hat(i) 的例程至少在每个 response_time 间隔运行一次。
个人理解:所以这里其实对检测到拥塞的情况,不是很强调,而是判断收敛情况和卡尔曼滤波的结果,来间接判断是否拥塞,
从而来得到当前是否应该乘性增加,加法增加还是维持,减少。减少只有在判断是否过度使用。
6) Parameters settings
1 | +------------+-------------------------------------+----------------+ |
Table 1: RECOMMENDED values for delay based controller
4 基于丢包的控制:Loss-based control
简介:
拥塞控制器的第二部分基于从基于延迟的控制器接收到的往返时间、数据包丢失和可用带宽估计 A_hat 做出决定。
由基于丢包的控制器计算的可用带宽估计用 As_hat 表示。
仅当沿路径的队列大小足够大时,基于延迟的控制器产生的可用带宽估计 A_hat 才是可靠的。
如果队列非常短,过度使用只会通过数据包丢失可见,而基于延迟的控制器不会使用这些数据包。
每次收到来自接收器的反馈时,基于丢包的控制器都应该运行。过程:
o If 2-10% of the packets have been lost since the previous report
from the receiver, the sender available bandwidth estimate
As_hat(i) will be kept unchanged. As_hat(i) = As_hat(i-1) 不变 2%<p<10%
o If more than 10% of the packets have been lost a new estimate is
calculated as As_hat(i) = As_hat(i-1)(1-0.5p), where p is the loss
ratio. -> As_hat(i) = As_hat(i-1)(1-0.5p) p为丢包率 p>10%
o As long as less than 2% of the packets have been lost As_hat(i)
will be increased as As_hat(i) = 1.05(As_hat(i-1))
-> As_hat(i) = 1.05(As_hat(i-1)) p<2%上下限控制:
新的带宽估计由 TCP 友好速率控制公式 [RFC3448] 下限,上限由可用带宽 A_hat(i) 的基于延迟的估计确定,其中基于延迟的估计具有优先权:1
2
3
4
58 s
As_hat(i) >= ---------------------------------------------------------
R*sqrt(2*b*p/3) + (t_RTO*(3*sqrt(3*b*p/8)*p*(1+32*p^2)))
As_hat(i) <= A_hat(i)其中
b 是单个 TCP 确认的数据包数确认(根据 TFRC 建议设置为 1),
t_RTO 是 TCP以秒为单位的重传超时值(设置为 4*R),
s 是平均数据包大小(以字节为单位)。
R 是以秒为单位的往返时间。
(乘以 8 是因为 TFRC 在计算带宽
字节,而本文档以位为单位计算带宽。)
换句话说:基于丢包的估计永远不会大于基于延迟的估计,并且永远不会低于来自TFRC 公式,除非基于延迟的估计低于TFRC 估计。
我们通过注意到如果传输通道由于过度使用而有少量数据包丢失来激发丢包阈值,如果发送方不调整他的比特率,该数量将很快增加。
因此,我们将很快达到 10% 以上的阈值并调整 As_hat(i)。 但是,如果丢包率没有增加,则丢包可能与自己造成的拥塞无关,因此我们不应对其做出反应。
5 其他:
该算法已在开源WebRTC中实现
项目,自 M23 以来一直在 Chrome 中使用,并且正在被使用
webrtc中的remb和transport cc
- remb:
接收端的延时拥塞控制算法由两大部分组成:发送端用于控制码流的发送,接收端用于拥塞评估和码流计算。而接收端的拥塞评估就是基于上述的kalman等过程进行的。 - transport cc:
是对remb的改进,具体如下:
- 将拥塞评估算法从接收端移动到了发送端,只是借助接收端将源延迟等数据发送到发送端。这样评估和控制合为一体,代码处理更简洁方便。
- 将卡尔曼滤波换成TrendLine滤波器(最小二乘法滤波器,通过斜率的增大或减小来判断当前网络的拥塞情况)
TrendLine滤波器:
基本思想:线性回归
认为在网络不拥塞的情况下,di(组包延迟)应该是不断减小或者是维持稳定的,当持续变大时,认为拥塞,而这个可以通过线性函数斜率的变化来体现:
将累计的di作为yi纵轴,则斜率变大说明拥塞,否则说明没有,具体如下:过程:
设xi ,表示当前包组最后一个包的接收时间与传输开始时第一个包的接收时间之差,即ti-first_arrival,这个值会逐渐增大,但是没事后面的值都是相对值。
设yi为 累计的di,x_bar 为xi到xi+n的平均值,y_bar为yi到yi+n的平均值假设从(0,0)触发,有一条直线:y=kx+b 使得从测量值(xi,yi)到该直线的误差平方和最小。
根据公式:在20大小的窗口内:
ki= 对(xi-x_bar)*(yi-y_bar) 20个求和 / 对(xi-x_bar)^2 20个求和
注意这里求出来的ki,替换了之前的mi,具体:
https://blog.jianchihu.net/webrtc-research-trendlineestimator.html
next:代码研究;