20、WebRTC中的带宽估计器(Bandwidth Estimator):Acknowledged Bitrate、Loss Rate、Delay-based Estimate、最终码率决策
带宽估计器,说白了就是WebRTC视频通话的“油门控制器”。
我刚开始接触WebRTC时,总觉得这玩意儿就是个简单的限速器。后来在一次线上故障排查中,发现用户网络抖动时视频直接卡成PPT,我才意识到——带宽估计器要是没写好,整个通话体验就全毁了。
今天咱们就把它拆开来看。WebRTC的带宽估计器,核心由四个部分组成:
- Acknowledged Bitrate(确认码率)
- Loss Rate(丢包率)
- Delay-based Estimate(延迟估计)
- 最终码率决策
这四个部分协同工作,最终输出一个“当前可用带宽”的估计值。编码器拿到这个值,就知道该压码率还是该提码率了。
20.1 Acknowledged Bitrate:最朴素的“实测值”
Acknowledged Bitrate,简称ACK码率。它的逻辑很简单:
你发出去多少数据,对方确认收到了多少,用这个“确认量”除以时间,就是ACK码率。
核心公式:
ACK Bitrate = 已确认的字节数 × 8 / 时间窗口
单位:bps(比特每秒)
我在项目中遇到过一种情况:发送端疯狂推流,但接收端反馈的ACK包很少。一查才发现,是中间路由器在悄悄丢包。ACK码率瞬间掉到原来的三分之一。嗯,这就是最真实的“网络现状”。
ACK码率有几个特点:
- 滞后性:它反映的是过去几百毫秒的情况,不是当前瞬间
- 下限保证:它是最保守的估计,永远不会高估带宽
- 抗突发:短时间的突发丢包,不会让ACK码率剧烈抖动
个人习惯:我一般把ACK码率作为“底线”。如果其他估计算法给出的值比ACK码率还低,我会怀疑是不是算法出了bug。
20.2 Loss Rate:丢包率,最直接的“报警信号”
丢包率,顾名思义,就是发送的包中有多少没被确认。
WebRTC里计算丢包率的方式,是基于RTCP反馈的。接收端每收到一定数量的包,就会发一个RTCP报告,里面包含了累计丢包数。
// 伪代码示意
loss_rate = (累计丢包数 - 上次累计丢包数) / (期望收到的包数 - 上次期望收到的包数)
丢包率这个指标,说实话,很粗暴。但它非常有效。
我曾经遇到过一个场景:用户Wi-Fi信号弱,丢包率飙到15%。这时候ACK码率还没反应过来,但丢包率已经报警了。带宽估计器立刻把码率砍了一半,通话虽然模糊了,但至少没断。
注意:丢包率不能单独使用。为什么?
- 如果网络有重传机制,丢包率可能被“美化”
- 如果发送端本身码率就低,丢包率参考价值不大
- 有些丢包是突发的,持续几毫秒就恢复了
所以,丢包率通常和ACK码率、延迟估计一起用。
20.3 Delay-based Estimate:延迟估计,最“聪明”的算法
延迟估计,是WebRTC带宽估计器里最核心、也最复杂的一块。
它的思路是这样的:
- 如果网络带宽充足,数据包发送和接收的延迟应该是稳定的
- 如果网络开始拥塞,数据包会在路由器队列里排队,延迟就会增加
- 延迟增加的趋势,就是拥塞的前兆
WebRTC里用的是Google Congestion Control(GCC)算法,它通过计算“单向延迟梯度”来判断网络状态。
// 延迟梯度计算(简化版)
delay_gradient = (接收时间差 - 发送时间差) / 发送时间差
if (delay_gradient > 阈值) {
// 网络拥塞,降低码率
} else if (delay_gradient < 负阈值) {
// 网络空闲,可以提升码率
} else {
// 保持当前码率
}
我个人觉得,延迟估计是带宽估计器里最“优雅”的部分。它不需要依赖丢包,就能提前感知拥塞。就像你开车时,看到前面刹车灯亮了,你不需要等撞上去才知道要减速。
避坑指南:我曾经在跨洲链路上遇到过一个问题——延迟梯度一直在正负之间震荡,导致码率忽高忽低。后来发现是链路本身的抖动太大,阈值设得太敏感了。解决办法是:把阈值调大,或者增加一个“稳定期”判断。
20.4 最终码率决策:三个指标如何“投票”
好了,现在我们有三个指标:
- ACK码率(实测下限)
- 丢包率(报警信号)
- 延迟估计(拥塞预判)
最终码率怎么定?
WebRTC的做法是:取最小值。
最终码率 = min(ACK码率, 延迟估计码率, 丢包率调整后的码率)
为什么取最小值?
你想想看,网络通信最怕什么?最怕“高估”。高估带宽会导致大量丢包、延迟飙升,甚至通话中断。取最小值,虽然保守,但安全。
具体决策流程是这样的:
- 先看丢包率:如果丢包率超过阈值(比如5%),直接按比例降低码率
- 再看延迟估计:如果延迟梯度显示拥塞,按延迟估计的值降低码率
- 最后看ACK码率:如果上面两个都没触发,就用ACK码率作为上限
- 综合取最小值:最终码率 = min(上面三个值)
我的经验:在实际调优中,我会给每个指标加一个“权重”。比如丢包率超过10%时,直接忽略其他两个指标,强制降码率。因为这时候网络已经快断了,保连接比保画质更重要。
20.5 整体架构图
下面这张图,展示了带宽估计器的核心流程:
从图中可以看到,三个输入源分别进入三个处理模块,最终汇聚到决策模块。决策模块取最小值,输出目标码率。
20.6 实际调优中的几个坑
最后,分享几个我在实际调优中踩过的坑:
- 坑一:ACK码率滞后导致码率下降慢
现象:网络突然变差,但ACK码率还在高位,导致视频卡顿。
解决办法:给ACK码率加一个“衰减因子”,让它下降得更快。 - 坑二:丢包率阈值太敏感
现象:偶尔丢一个包,码率就猛降,画质忽好忽坏。
解决办法:丢包率用滑动窗口平均,不要用瞬时值。 - 坑三:延迟估计在长肥网络中失效
现象:跨洲链路延迟大,延迟梯度一直为正,码率被压得很低。
解决办法:增加一个“基线延迟”校准机制,排除固定延迟的影响。
总结一下:带宽估计器没有银弹。ACK码率、丢包率、延迟估计各有优缺点。实际项目中,我通常会根据网络类型(Wi-Fi、4G、有线)调整三个指标的权重。比如Wi-Fi下丢包率权重高一些,4G下延迟估计权重高一些。
嗯,调优是个细活,慢慢来。