跳转到内容

GCC (WebRTC) — 让视频通话不卡的拥塞控制算法

待复核

GCC(Google Congestion Control)是 WebRTC 默认的拥塞控制算法——它的工作是在视频通话时自动调节发送速率,让画面流畅、不卡顿、也不把网络塞爆。

日常类比:你在高峰期开车上高速公路。如果你一直猛踩油门,路很快堵死,所有人都走不动。聪明的做法是感知前面车距变化——车距在缩小(时延在涨),赶紧减速;车距重新拉大了,再慢慢加速。GCC 就是这套”看车距调油门”的策略,只不过感知的是网络包的单向时延变化。

GCC 的核心思路:不等丢包再减速(那太晚了),而是一发现”延迟在增长”就主动降速。这让它特别适合视频通话这种对延迟极度敏感的场景——丢一帧可以容忍,但积累 500ms 的缓冲区就会让对话变成”对讲机体验”。

技术上,GCC 属于 AIMD(加法增、乘法减)家族的变体:网络空闲时加法增速,网络过载时乘法降速。但与经典 TCP AIMD 不同,GCC 的触发信号不是丢包,而是单向延迟梯度——更灵敏,也更适合实时媒体。

论文全称是 “Analysis and Design of the Google Congestion Control for Web Real-time Communication”,发表于 2016 年 ACM MMSys 会议。作者不是 Google 员工,而是意大利 Politecnico di Bari 的研究者——他们对 Google 开源实现做了逆向分析和数学建模。

不理解 GCC,下面这些事没法解释:

  • 为什么用浏览器打 Google Meet 等 WebRTC 通话时,网络变差画面会降清晰度而不是直接卡住——底层是 GCC(或同类延迟感知拥塞控制)在自动降码率
  • 为什么 WebRTC 能在 UDP 上跑实时视频却不把同一 Wi-Fi 下别人的网也搞崩——修正后的 GCC 以 TCP-friendly 为目标
  • 为什么视频通话比 TCP 文件传输对拥塞控制有完全不同的需求——TCP 追求吞吐量最大化,GCC 追求延迟最小化
  • 为什么 IETF 花了好几年才把 WebRTC 拥塞控制标准化——GCC 论文揭示了早期版本的数学缺陷并给出了修正
  • 为什么网络条件变化时视频码率能”柔和”过渡而不是剧烈抖动——GCC 的状态机和滤波器平滑了控制信号

GCC 分成两个独立的控制器,在接收端和发送端各跑一个,最终取它们给出的较小值:

1. 基于延迟的控制器(接收端)

接收端测量每对连续包到达的单向延迟变化(叫 inter-arrival delay gradient)。具体来说:如果包 A 和包 B 的发送间隔是 10ms,但到达间隔变成了 15ms,那梯度就是 +5ms——说明路上有东西在排队。把这些梯度喂进一个卡尔曼滤波器,估计出网络排队延迟的趋势。趋势超过阈值就判定”过载”,低于阈值就判定”空闲”。

  • 过载 → 指数降速(乘 0.85)
  • 空闲 → 加法增速(每秒加固定量)
  • 正常 → 保持

关键创新:阈值不是固定的,是自适应的——当网络长期处于过载边缘,阈值会慢慢往上调,避免误判。论文证明如果用固定阈值(比如 12.5ms),在高带宽链路上算法会”卡在低速”,因为排队梯度信号太弱了,永远达不到触发加速的条件。自适应阈值解决了这个”高带宽饥饿”问题。

2. 基于丢包的控制器(发送端)

发送端根据 RTCP 反馈的丢包率调速:丢包率超过 10% 就降速(乘以 1 - 丢包率),低于 2% 就加速。这是 GCC 的”安全网”——万一延迟控制器没及时反应,丢包控制器兜底。

两者协作:最终发送码率 = min(延迟控制器的估计, 丢包控制器的估计)。

状态机模型:延迟控制器内部维护三个状态——Increase / Decrease / Hold。状态之间的跳转由过载检测器的输出驱动:

  • Increase 状态:加法增速(避免突然灌入太多流量),每个 RTT 加一个固定增量
  • Decrease 状态:乘法降速(当前码率乘 0.85),快速退让以释放排队
  • Hold 状态:保持当前码率观望,等待更多测量数据确认趋势

这套”慢上快下”的非对称设计正是 AIMD 的经典哲学。

卡尔曼滤波器的作用:网络延迟测量本身噪声很大(操作系统调度、Wi-Fi 重传、交叉流量都会引入抖动)。卡尔曼滤波器把这些噪声平滑掉,提取出”排队延迟的真实趋势”。你可以把它想象成一个聪明的移动平均——它不仅看历史数据,还会根据自己对网络模型的”信念”来加权。

案例 1:视频通话遇到 Wi-Fi 波动

Section titled “案例 1:视频通话遇到 Wi-Fi 波动”

你在咖啡店用 WebRTC 视频通话,隔壁桌有人开始下载大文件。路由器开始排队,GCC 的延迟控制器检测到 inter-arrival delay gradient 持续为正(包越来越慢到),判定过载,把码率从 2Mbps 降到 1.2Mbps。画面从 720p 降到 480p,但延迟稳定在 150ms,对话体验依然自然。

整个过程对用户透明——你只是觉得”画面模糊了一下然后恢复了”,而不是”卡住 3 秒然后一大堆画面涌过来”。这种体验差异就是 GCC 的价值所在。

GCC 的设计目标之一是 TCP-friendly:它不能贪心抢光带宽,也不能太保守被 TCP 挤到零。论文的实验表明,修正后的 GCC 在与 TCP 共享瓶颈链路时,能拿到自己”公平份额”的带宽,不会饿死也不会撑死。

具体实验设置:一条 1Mbps 瓶颈链路上同时跑一个 GCC 视频流和一个 TCP 下载。早期 GCC 只能拿到 100kbps 左右(TCP 太激进把它挤走了)。论文修正自适应阈值后,GCC 稳定在 400-500kbps,接近公平的 50% 分配。

在实际 WebRTC 实现中,接收端把估计的可用带宽通过 RTCP REMB(Receiver Estimated Maximum Bitrate)消息告诉发送端。发送端看到这个值后,取 min(REMB, 自己丢包控制器的估计) 作为最终码率上限。

你可以用 Chrome 的 chrome://webrtc-internals 页面实时观察 REMB 值的变化。在通话过程中人为制造网络波动(比如开始下载大文件),你会看到 REMB 曲线迅速下降,发送码率跟着下降,几秒后网络恢复,REMB 慢慢回升。

  1. 阈值固定会导致延迟黑洞:GCC 早期版本用固定过载阈值 12.5ms。论文证明这会让算法在高带宽链路上永远达不到满速(因为排队延迟梯度太小触发不了”空闲”判定)。修正方案是让阈值动态自适应。

  2. 与 TCP 抢带宽时饿死:早期 GCC 遇到并发 TCP 流时容易”退让过多”,码率被压到几乎为零。论文分析根因是加法增速太慢而乘法降速太激进,修正后的参数平衡了公平性。

  3. 卡尔曼滤波器对突发丢包敏感:如果网络出现突发丢包(比如 Wi-Fi 切信道),丢失的包不再产生延迟梯度样本,滤波器可能误判为”网络变好了”而加速,导致短暂的拥塞震荡。

  4. REMB 反馈延迟导致超调:RTCP 反馈间隔通常 1 秒,在这 1 秒内发送端可能已经把码率拉高了很多。等收到”过载”反馈时已经往网络灌了太多数据,造成延迟尖峰。后续演进(如 Transport-wide CC)改用更频繁的逐包反馈来缓解。这个问题的本质是控制系统中的”测量延迟”——反馈回路越慢,系统越容易震荡。

适用

  • 实时音视频通话(WebRTC、视频会议、直播连麦)——延迟敏感、可以容忍画质波动
  • 需要与 TCP 流公平共存的 UDP 应用——GCC 有意做到 TCP-friendly
  • 带宽中等波动的网络环境(Wi-Fi、4G)——自适应阈值能跟上变化
  • 端到端延迟要求 < 400ms 的交互场景——GCC 的设计目标就是把排队延迟压到最低

不适用

  • 大文件传输——TCP 的拥塞控制(如 BBR、CUBIC)吞吐量更高,GCC 太保守
  • 极端低带宽环境(<100kbps)——GCC 的探测机制在极低码率下震荡明显
  • 多路复用场景(一条连接跑多路视频)——GCC 按单流设计,多流需要调度层(如 NADA/SCReAM)
  • 纯音频场景——码率本来就低,拥塞控制收益有限,简单的固定码率 + FEC 往往够用
  • 高度对称的双向大带宽场景(如屏幕共享 + 摄像头双流)——需要更复杂的多流调度策略
  • 2011 年:Google 在 Chrome 中推出 WebRTC,内置了第一版 GCC。当时没有公开论文,只有 IETF draft 和 Chromium 源码。
  • 2012-2014 年:IETF 成立 RMCAT(RTP Media Congestion Avoidance Techniques)工作组,想把 GCC 标准化。但社区发现 GCC 在与 TCP 共存时表现不佳,公平性存在问题。
  • 2016 年:Carlucci 等人发表本篇论文,系统分析了 GCC 的数学模型,指出固定阈值的缺陷,提出自适应阈值修正方案。这篇论文让 GCC 从”Google 的黑盒”变成了可以被学术界分析和改进的公开算法。
  • 2017 年至今:Chrome 逐步切换到 Transport-wide Congestion Control(基于逐包反馈),是 GCC 理念的工程演进。

这段历史的启示:工业界先跑起来(Chrome 出 GCC),学术界再回过头做严谨分析和修正(本论文),然后工业界再吸收修正方案推出新版本。这种”工程先行 → 学术验证 → 工程改进”的循环在网络协议领域非常典型。

  1. 时延比丢包是更早的拥塞信号——等到丢包已经晚了,排队延迟增长是”路快堵了”的前兆
  2. 自适应阈值是关键——固定参数在变化的网络里一定会失灵,动态调整才能在”利用率”和”低延迟”之间走钢丝
  3. 实时系统的拥塞控制目标和批量传输完全不同——TCP 追求填满管道,GCC 追求管道”刚好不满”
  4. 控制论思维——GCC 本质是一个反馈控制系统(测量 → 估计 → 决策 → 执行 → 再测量),理解控制论能看懂大量系统设计
  5. 论文的价值在于”把黑盒打开”——GCC 作为工业代码已经跑了好几年,但直到有人做数学分析才发现隐藏的公平性 bug
  6. 两个控制器取 min 是安全设计模式——宁可保守也不超发,在分布式系统中”多重守门人取最严”是通用模式
  • 原论文 PDF:ACM Digital Library(MMSys 2016,10 页,实验图表值得细看)
  • WebRTC 官方拥塞控制 draft:draft-ietf-rmcat-gcc(IETF 标准化文档,对照论文看)
  • BBR 拥塞控制对比:Google 2017 年发布的 BBR 是面向吞吐量场景的,思路和 GCC 互补
  • Transport-wide Congestion Control:GCC 的后续演进,用逐包反馈替代 REMB,Chrome 已默认启用
  • WebRTC for the Curious(免费电子书,拥塞控制章节零基础友好)
  • Chromium 源码中的 GCC 实现:webrtc/modules/congestion_controller/(想看真实工程代码可以从这里入手)
  • 对比算法:NADA(Network-Assisted Dynamic Adaptation)和 SCReAM(Self-Clocked Rate Adaptation for Multimedia)是 RMCAT 工作组里 GCC 的竞品,各有取舍
  • 视频讲解:YouTube — WebRTC Congestion Control Explained(搜索合集,多角度理解)
  • tcp —— TCP 的拥塞控制是 GCC 的对标物;GCC 追求 TCP-friendly 但优化目标完全不同
  • bbr-2017 —— 同为 Google 出品的拥塞控制,但 BBR 面向吞吐量场景,GCC 面向低延迟场景
  • quic —— QUIC 在 UDP 上做可靠传输,GCC 在 UDP 上做拥塞控制;两者常在 WebRTC 体系内配合
  • rtp-rfc-1889 —— GCC 运行在 RTP/RTCP 之上,RTCP 的反馈报文是 GCC 的输入数据来源
  • tcp-vegas-1995 —— Vegas 也是基于延迟的拥塞控制先驱,GCC 延续了”用延迟而非丢包判拥塞”的哲学
  • coturn —— TURN 中继服务器是 WebRTC NAT 穿越的关键组件,GCC 需要适应经过 TURN 中继的额外延迟
  • cubic-2008 —— Linux 默认的 TCP 拥塞控制算法,GCC 的公平性测试对手之一
  • x264 —— x264 — H.264/AVC 编码器
  • x265 —— x265 — HEVC/H.265 编码器