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码率, 延迟估计码率, 丢包率调整后的码率)

为什么取最小值?

你想想看,网络通信最怕什么?最怕“高估”。高估带宽会导致大量丢包、延迟飙升,甚至通话中断。取最小值,虽然保守,但安全。

具体决策流程是这样的:

  1. 先看丢包率:如果丢包率超过阈值(比如5%),直接按比例降低码率
  2. 再看延迟估计:如果延迟梯度显示拥塞,按延迟估计的值降低码率
  3. 最后看ACK码率:如果上面两个都没触发,就用ACK码率作为上限
  4. 综合取最小值:最终码率 = min(上面三个值)

我的经验:在实际调优中,我会给每个指标加一个“权重”。比如丢包率超过10%时,直接忽略其他两个指标,强制降码率。因为这时候网络已经快断了,保连接比保画质更重要。

20.5 整体架构图

下面这张图,展示了带宽估计器的核心流程:

WebRTC带宽估计器核心流程 发送端数据包 接收端反馈 RTCP报告 Acknowledged Bitrate 基于确认字节数计算 Loss Rate 基于RTCP丢包统计 Delay-based Estimate 基于延迟梯度计算 最终码率决策 取最小值:min(ACK, Loss, Delay) 输出:目标码率 ACK码率 丢包率 延迟估计

从图中可以看到,三个输入源分别进入三个处理模块,最终汇聚到决策模块。决策模块取最小值,输出目标码率。

20.6 实际调优中的几个坑

最后,分享几个我在实际调优中踩过的坑:

  • 坑一:ACK码率滞后导致码率下降慢
    现象:网络突然变差,但ACK码率还在高位,导致视频卡顿。
    解决办法:给ACK码率加一个“衰减因子”,让它下降得更快。
  • 坑二:丢包率阈值太敏感
    现象:偶尔丢一个包,码率就猛降,画质忽好忽坏。
    解决办法:丢包率用滑动窗口平均,不要用瞬时值。
  • 坑三:延迟估计在长肥网络中失效
    现象:跨洲链路延迟大,延迟梯度一直为正,码率被压得很低。
    解决办法:增加一个“基线延迟”校准机制,排除固定延迟的影响。

总结一下:带宽估计器没有银弹。ACK码率、丢包率、延迟估计各有优缺点。实际项目中,我通常会根据网络类型(Wi-Fi、4G、有线)调整三个指标的权重。比如Wi-Fi下丢包率权重高一些,4G下延迟估计权重高一些。

嗯,调优是个细活,慢慢来。


公众号:蓝海资料掘金营,微信deep3321