# 码率控制与闭环 · 估计怎么变成画质

> 带宽估计的终点，是把「网络能承受多少」变成「编码器该编多清晰」。这一章讲清 GoogCC 的输出 NetworkControlUpdate 怎么流向编码器与 pacer、目标码率如何在多条流间分配的引子，以及整个拥塞控制闭环怎么每隔几十毫秒转一圈。收尾第 5 部分。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/rate-control
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 码率控制与闭环 · 估计怎么变成画质

第 5 部分前面讲了怎么**估**带宽（[延迟](https://daiw.org/manual/webrtc/delay-based-bwe)、[丢包](https://daiw.org/manual/webrtc/loss-based-bwe)、[探测](https://daiw.org/manual/webrtc/probing)）、怎么**发**（[pacing](https://daiw.org/manual/webrtc/pacing)、[窗口](https://daiw.org/manual/webrtc/congestion-window)）。这最后一章把它们收成一个**闭环**，并回答：估出的那个数字，最终**怎么变成用户看到的画质**？

## 输出：NetworkControlUpdate

[`GoogCcNetworkController`](https://daiw.org/manual/webrtc/congestion-control-overview) 每处理一次[反馈](https://daiw.org/manual/webrtc/transport-cc)（`OnTransportPacketsFeedback`），产出一个 **`NetworkControlUpdate`**——它是拥塞控制的**决策书**，主要内容：

- **目标码率（target bitrate）** —— “综合[延迟 / 丢包估计取小](https://daiw.org/manual/webrtc/loss-based-bwe)、[探测](https://daiw.org/manual/webrtc/probing)、[窗口回压](https://daiw.org/manual/webrtc/congestion-window)后，现在该用多少码率”。
- **pacing 速率** —— 交给 [pacer](https://daiw.org/manual/webrtc/pacing) 的放行速率。
- **拥塞窗口** —— 交给[窗口控制器](https://daiw.org/manual/webrtc/congestion-window)的在途上限。

这个 update 顺着 [`RtpTransportControllerSend`](https://daiw.org/manual/webrtc/source-map)（[第 9 部分](https://daiw.org/manual/webrtc/source-map)）分发给各执行端：pacer 拿到新的放行速率，而**目标码率**流向最终的消费者——[编码器](https://daiw.org/manual/webrtc/source-map)。

## 目标码率 → 编码器 → 画质

<Callout type="info">
  **目标码率的终点，是告诉[编码器](https://daiw.org/manual/webrtc/source-map)“你有多少带宽预算，请把画质 / 帧率编到这个预算之内”。** 这是拥塞控制**闭环的出口**：网络能承受 2 Mbps，就让视频编码器按 2 Mbps 编——分辨率、帧率、量化参数据此调整。带宽宽裕 → 高清高帧率；带宽紧张 → 降分辨率 / 降帧率保流畅。**“网络状况”就这样一路传导成了“用户看到的画质”**——这是[视频管线自适应](https://daiw.org/manual/webrtc/source-map)（[第 6 部分](https://daiw.org/manual/webrtc/source-map)）的输入，也是整个 WebRTC 体验的关键传导链。

  中间还有一步分配：一次通话可能有[多条流](https://daiw.org/manual/webrtc/transceivers)（音频、视频、[联播](https://daiw.org/manual/webrtc/source-map)的多档），总目标码率要**在它们之间分配**——音频优先保住（语音比画面重要）、剩下的给视频。这活儿由 `BitrateAllocator`（[第 9 部分](https://daiw.org/manual/webrtc/source-map)）做，是连接“总带宽”和“每条流码率”的桥。这里先记住：**拥塞控制给出总预算，分配器再把预算分到各条流的编码器**。
</Callout>

## 完整闭环

把整个第 5 部分（和它与[第 4](https://daiw.org/manual/webrtc/transport-cc)、[第 6 部分](https://daiw.org/manual/webrtc/source-map)的连接）收成一张闭环图：

```mermaid
flowchart TD
    ENC["视频编码器(按目标码率编)"] --> SEND["打包 → Pacing → SRTP → 发出"]
    SEND --> NET["网络"]
    NET --> RECV["对端接收"]
    RECV -->|"Transport-CC 反馈(每包到达时间)"| GCC["GoogCcNetworkController"]
    GCC -->|"延迟based + 丢包based 取小"| EST["带宽估计"]
    GCC -->|"探测快速爬坡"| EST
    EST --> UPD["NetworkControlUpdate:目标码率 / pacing / 窗口"]
    UPD -->|"目标码率"| ALLOC["BitrateAllocator 分配到各流"]
    ALLOC -->|"这条流的预算"| ENC
    UPD -.->|"pacing 速率"| SEND
```

<Callout type="info">
  **这个闭环每隔几十毫秒转一圈，是 WebRTC 实时适应网络的心脏。** 发包 → 收[反馈](https://daiw.org/manual/webrtc/transport-cc) → [估带宽](https://daiw.org/manual/webrtc/delay-based-bwe) → 定[目标码率](https://daiw.org/manual/webrtc/rate-control) → [分配](https://daiw.org/manual/webrtc/source-map)到编码器 → 编码器调画质 → 再发包。网络稍有风吹草动（带宽变、[延迟涨](https://daiw.org/manual/webrtc/delay-based-bwe)、[丢包起](https://daiw.org/manual/webrtc/loss-based-bwe)），这个环就在几十毫秒内感知并调整。**正是这个飞速旋转的闭环，让 WebRTC 能在电梯里、地铁上、拥挤的 Wi-Fi 下，把通话维持得尽量流畅。** 它不追求“最高画质”，而追求“在当前网络下的最佳体验”——这是实时通信和离线视频的根本区别。
</Callout>

## 小结与第 5 部分回顾

**码率控制是拥塞控制的出口**：[`GoogCcNetworkController`](https://daiw.org/manual/webrtc/congestion-control-overview) 产出 **`NetworkControlUpdate`**（目标码率 / pacing 速率 / [窗口](https://daiw.org/manual/webrtc/congestion-window)），目标码率经 `BitrateAllocator`（[第 9 部分](https://daiw.org/manual/webrtc/source-map)）[分配到各流](https://daiw.org/manual/webrtc/transceivers)的[编码器](https://daiw.org/manual/webrtc/source-map)，转化成**画质 / 帧率**。整个系统是一个**每几十毫秒转一圈的闭环**：发包→反馈→估带宽→定码率→调编码→再发包。

**第 5 部分「拥塞控制」到此完整**：[总览（GoogCC 双控制器）](https://daiw.org/manual/webrtc/congestion-control-overview) → [延迟based（trendline/过载）](https://daiw.org/manual/webrtc/delay-based-bwe) → [丢包based](https://daiw.org/manual/webrtc/loss-based-bwe) → [探测](https://daiw.org/manual/webrtc/probing) → [pacing](https://daiw.org/manual/webrtc/pacing) → [拥塞窗口](https://daiw.org/manual/webrtc/congestion-window) → 码率控制闭环。WebRTC 怎么“聪明地决定发多快”已经讲透。

目标码率交到了[编码器](https://daiw.org/manual/webrtc/source-map)手里——那编码器、以及整条**视频管线**（采集、编码、发送、接收、抖动缓冲、解码、渲染）是怎么运转的？第 6 部分进入 `video/` 和 `modules/video_coding/`。👉 第 6 部分即将展开。

## 参考资料

- [1] libwebrtc 源码.`api/transport/network_control.h`（`NetworkControlUpdate`）、`goog_cc_network_control.cc`（产出决策）。
- [2] libwebrtc 源码.`call/rtp_transport_controller_send.h`（分发决策）、`call/bitrate_allocator.h`（码率分配，第 9 部分）。
- [3] IETF「GoogCC」draft —— 完整算法。
