4. 带宽估计与码率控制:GCC、REMB与TWCC

带宽估计与码率控制,说白了就是让RTP流“看路开车”。

网络带宽是动态的,忽快忽慢。如果没有一套机制去感知和调整,视频就会卡顿、花屏,甚至断连。我早期做WebRTC项目时,就吃过这个亏——带宽估计没配好,用户一多,整个会议就崩了。

这一章,我们聚焦Google Congestion Control(GCC),以及它依赖的两个核心协议:REMB和TWCC。我会把发送端和接收端带宽估计的逻辑拆开讲,再结合我踩过的坑,给你一些实战建议。

4.1 GCC的整体架构

GCC是WebRTC默认的拥塞控制算法。它不是一个单一模块,而是一套协作流程。

我习惯把它分成两条腿走路:

  • 接收端带宽估计:基于到达时间差,计算延迟梯度,判断网络是否拥塞。
  • 发送端带宽估计:基于丢包率,做更激进的降码率决策。

两条腿互相配合,最终输出一个“目标码率”,喂给编码器和RTP发送模块。

核心原则:延迟信号用于早期预警,丢包信号用于紧急刹车。两者不能互相替代。

GCC带宽估计与码率控制架构 GCC 带宽估计与码率控制架构 发送端 接收端 丢包统计 码率控制器 到达时间差 延迟梯度 REMB / TWCC 反馈 RTP 流 RTCP 反馈 说明:接收端通过延迟梯度判断拥塞,通过REMB/TWCC反馈给发送端。 发送端结合丢包率,最终决策目标码率。

4.2 接收端带宽估计:延迟梯度与REMB

接收端带宽估计的核心,是计算数据包到达时间的“延迟梯度”。

为什么看延迟?因为网络一旦开始排队,延迟就会增加。这个信号比丢包来得早。我遇到过很多案例,丢包率还没涨,延迟已经翻倍了。

4.2.1 到达时间差与延迟梯度

接收端会记录每个RTP包的到达时间。然后,它计算一组包的“到达时间差”与“发送时间差”的差值。

  • 如果差值 > 0,说明网络排队了,延迟在增加。
  • 如果差值 < 0,说明网络在释放,延迟在减少。

这个差值经过卡尔曼滤波或趋势线拟合,得到一个“延迟梯度”指标。

我的经验:延迟梯度对突发流量特别敏感。我曾经在一个弱网测试中,发现延迟梯度在500ms内就从0飙升到200ms,而丢包率还是0%。这时候如果不降码率,接下来必然丢包。

4.2.2 REMB:接收端反馈带宽估计

REMB(Receiver Estimated Maximum Bitrate)是RTCP的一种扩展反馈报文。接收端算出一个“建议码率”,通过REMB告诉发送端。

REMB报文格式很简单:

// REMB报文结构(简化)
0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P| FMT=15  |   PT=206      |          length             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  SSRC of packet sender                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  SSRC of media source                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Unique identifier 'R' 'E' 'M' 'B'                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Num SSRC     |   br Exp      |  br Mantissa                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   SSRC feedback                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

关键字段:

  • br Exp + br Mantissa:表示建议码率,单位是bps。
  • SSRC feedback:这个码率是针对哪个媒体流的。

注意:REMB是接收端单方面建议。发送端可以采纳,也可以忽略。我见过一些实现,发送端完全不听REMB,只靠自己算丢包,结果延迟爆炸。

4.3 发送端带宽估计:丢包率与码率决策

发送端带宽估计,主要依赖丢包率。为什么?因为丢包是网络过载的“铁证”。

GCC在发送端维护一个状态机:

状态 触发条件 码率调整策略
增加 丢包率 < 2% 按比例增加码率(如+8%)
保持 2% ≤ 丢包率 < 10% 维持当前码率
减少 丢包率 ≥ 10% 按比例减少码率(如-20%)

这个状态机看起来简单,但实际调参很讲究。我曾经在一个项目中,把“减少”阈值设成了5%,结果网络稍微波动就降码率,视频质量忽高忽低。后来改回10%,稳定多了。

4.3.1 丢包率计算

发送端通过RTCP RR(Receiver Report)获取丢包率。RR报文里有一个“fraction lost”字段,表示丢包比例。

// RTCP RR中的丢包率字段
// fraction lost: 8 bits, 单位是 1/256
// 例如:fraction lost = 25,表示丢包率 ≈ 9.8%

避坑指南:我曾经遇到一个问题,fraction lost一直为0,但视频明显卡顿。后来发现是接收端RTCP RR发送间隔太长(默认5秒),丢包信息滞后了。我建议把RTCP间隔缩短到1秒以内,尤其是对实时性要求高的场景。

4.4 TWCC:更精细的传输反馈

TWCC(Transport Wide Congestion Control)是GCC的“增强版”反馈机制。它不像REMB那样只给一个建议码率,而是把每个数据包的到达情况都反馈回来。

TWCC的核心是:

  • 发送端给每个RTP包打上一个“传输序列号”。
  • 接收端收到后,记录每个包的到达时间。
  • 接收端通过RTCP反馈报文,把“包序号+到达时间”列表发给发送端。
  • 发送端自己算延迟梯度,自己做带宽估计。

这样做的好处是:发送端掌握全部信息,决策更精准。坏处是:反馈报文变大,带宽开销增加。

特性 REMB TWCC
反馈内容 建议码率 每个包的到达时间
计算位置 接收端 发送端
带宽开销 低(约20字节/报文) 高(取决于包数量)
精度 中等 高
适用场景 传统WebRTC 新版WebRTC、低延迟场景

我的建议:如果你的网络带宽充裕,优先用TWCC。它能让发送端更早感知到拥塞,码率控制更平滑。如果带宽紧张(比如移动网络),用REMB更省流量。

4.5 实战:GCC参数调优

GCC虽然默认参数能用,但想跑得好,必须调。我总结几个关键参数:

  1. RTCP反馈间隔:默认5秒太长,建议设为200ms-1秒。
  2. 丢包率阈值:减少阈值建议10%,增加阈值建议2%。
  3. 码率增加比例:建议8%-15%,太快容易震荡。
  4. 码率减少比例:建议20%-30%,太慢无法快速响应拥塞。
  5. 延迟梯度敏感度:如果网络抖动大,可以适当降低敏感度。

曾经踩过的坑:我把码率增加比例设成了25%,结果网络稍微好转,码率就猛涨,然后立刻触发丢包,又猛降。视频质量像过山车。后来改成10%,平滑多了。

4.6 小结

带宽估计与码率控制,是RTP部署中最容易出问题的地方,也是最值得优化的地方。

GCC通过接收端延迟梯度(REMB/TWCC)和发送端丢包率,共同决策目标码率。REMB适合带宽受限场景,TWCC适合精度要求高的场景。

调参时,记住一句话:延迟是预警,丢包是铁证。两者结合,才能让RTP流在网络波动中稳如老狗。