# 带宽探测 · 主动试探能不能更快

> 光被动测量，涨得太慢——通话开始或网络变好时，得主动发探测包快速试探可用带宽。这一章读 goog_cc 的 ProbeController：探测怎么用成串的包测出瞬时吞吐、padding 与 RTX 两种探测手段，以及探测为什么能让码率快速爬坡。

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

# 带宽探测 · 主动试探能不能更快

[延迟based](https://daiw.org/manual/webrtc/delay-based-bwe)的 [AIMD 加性增](https://daiw.org/manual/webrtc/delay-based-bwe)很稳，但也很**慢**——小步慢涨，要好久才能爬到高带宽。通话刚开始（从零起步）或网络突然变好时，慢涨意味着**画质半天上不来**。所以 GoogCC 还有一招**主动**的：**带宽探测（probing）**，由 `goog_cc/probe_controller.h` 的 `ProbeController` 负责。

## 思路：成串发包，看它们怎么到

<Callout type="info">
  **探测 = 故意在短时间内发一串（cluster）包，看它们到达接收端时被“压”成什么样，从而直接测出瞬时可用带宽。** 原理：如果你以 5 Mbps 的速率发一串包，而链路只有 3 Mbps，这串包会在瓶颈处**排队、被拉开间隔**——到达端一算它们的到达速率，就知道「实际只能跑 3 Mbps」。反之若它们几乎原速到达，说明链路**至少**有 5 Mbps，可以放心涨上去。

  这比 [AIMD 慢慢试](https://daiw.org/manual/webrtc/delay-based-bwe)快得多：AIMD 要很多个 RTT 一点点涨，探测**一串包、一个来回**就能测出「能不能到 5 Mbps」，让码率**直接跳上去**。代价是探测包本身占带宽（万一链路不够，这串包还加重了瞬时拥塞），所以要**用得克制**——只在该探测的时机探。
</Callout>

## 什么时候探测

`ProbeController` 决定何时发探测，典型时机：

- **通话启动** —— 从一个初始猜测（如 300 kbps）**快速爬坡**到真实带宽。没有探测，新通话的画质要好几秒才能起来；有探测，一两秒就能冲上去。
- **码率被[降](https://daiw.org/manual/webrtc/delay-based-bwe)后网络恢复** —— 之前因拥塞降了码率，现在延迟 / 丢包都好转，探测一下「是不是能涨回去了」，避免长期被压在低码率。
- **达到当前估计上限** —— [AIMD](https://daiw.org/manual/webrtc/delay-based-bwe) 已经涨到当前估计的天花板，探测「天花板之上还有没有空间」。

## 探测包哪来：padding 与 RTX

探测要发「额外的包」，这些包哪来？[PT 分配那章](https://daiw.org/manual/webrtc/payload-types)提过，`g3doc/rtp.md` 讲了两种手段：

| 手段 | 是什么 | 特点 |
| --- | --- | --- |
| **padding 包** | 纯 [RTP 填充](https://daiw.org/manual/webrtc/payload-types)（无媒体） | 收端直接丢弃；但单包 payload ≤256 字节，要发很多包 |
| **[RTX](https://daiw.org/manual/webrtc/nack-rtx) 包** | 重发[历史里](https://daiw.org/manual/webrtc/rtp-sender)未确认的旧包 | **更优**：万一那包真丢了还兼做冗余，不纯浪费 |

<Callout type="info">
  **`g3doc/rtp.md` 明说 RTX 探测优于 padding 探测。** 因为 padding 包**纯属浪费**（收端丢弃），且单包填充上限 256 字节，要凑够探测流量得发**一大堆**小包——从[拥塞控制](https://daiw.org/manual/webrtc/delay-based-bwe)角度看，包数太多不理想。而 [RTX 探测](https://daiw.org/manual/webrtc/nack-rtx)重发的是**真实的旧媒体包**：它们既完成了探测，万一对应的原包真丢了，这次重发还**顺便修复了丢包**——一举两得，不浪费。所以优先用 RTX 探测，只在 [RTX 没协商](https://daiw.org/manual/webrtc/nack-rtx)（如纯音频）或找不到合适的旧包时，才退回 padding。**连“浪费在探测上的带宽”都想办法让它有用**——这种精打细算，是成熟实时系统的特征。
</Callout>

## 探测让码率快速爬坡

有了探测，码率曲线的形状就好看多了：

```mermaid
flowchart LR
    START["通话启动:初始码率(低)"] -->|"探测:一串包直接测出可用带宽"| JUMP["码率跳到探出的水平"]
    JUMP -->|"之后 AIMD 缓慢微调"| STABLE["稳定贴着网络容量"]
    STABLE -->|"网络变好 → 再探测"| UP["再跳一档"]
```

**探测负责“快速跳到大致水平”，[AIMD 负责“精细微调”](https://daiw.org/manual/webrtc/delay-based-bwe)**——一个粗调快、一个细调稳，配合起来，码率既能快速起来、又能稳定收敛。这是 GoogCC 用户体验好的一大原因：视频通话接通没几秒画质就清晰了，靠的正是探测的快速爬坡。

## 小结与下一章

**带宽探测**主动发一串包、测它们的到达速率，**直接测出瞬时可用带宽**——比 [AIMD 慢涨](https://daiw.org/manual/webrtc/delay-based-bwe)快得多。`ProbeController` 在**通话启动、码率恢复、触及上限**等时机探测。探测包用 **[padding](https://daiw.org/manual/webrtc/payload-types)**（浪费、量大）或 **[RTX](https://daiw.org/manual/webrtc/nack-rtx)**（重发旧包，兼修丢包，更优）。探测**快速跳档** + [AIMD 精细微调](https://daiw.org/manual/webrtc/delay-based-bwe)，让码率又快又稳地贴住网络容量。

估计出了目标码率，但包不能**一股脑猛发**——那会造成瞬时拥塞。得**平滑地、按节奏**吐出去，这是 **pacing** 的活。下一章。👉 [Pacing 平滑发包](https://daiw.org/manual/webrtc/pacing)。

## 参考资料

- [1] libwebrtc 源码.`modules/congestion_controller/goog_cc/probe_controller.h`、`probe_bitrate_estimator.h`。
- [2] libwebrtc 源码.`g3doc/rtp.md`（padding / RTX 探测对比）。
- [3] IETF「GoogCC」draft —— probing 部分。
