# Pacing 平滑发包 · 别一股脑猛发

> 编码器一帧可能出一大批包，若一股脑发出去会造成瞬时拥塞、干扰带宽估计。Pacing 把包按目标码率的节奏平滑吐出。这一章读 pacing/pacing_controller.h 的 EnqueuePacket/ProcessPackets，讲清 pacer 的队列、发包节奏，以及它为什么是拥塞控制不可或缺的一环。

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

# Pacing 平滑发包 · 别一股脑猛发

[拥塞控制估出了目标码率](https://daiw.org/manual/webrtc/probing)，但这只是「平均该发多快」。还有个问题：**怎么把包吐出去？** 如果[编码器](https://daiw.org/manual/webrtc/source-map)出一帧就把它的一大批包**一股脑**全发出，哪怕平均码率没超，**瞬时**也会形成一个突发（burst），造成短暂拥塞、还干扰[带宽估计](https://daiw.org/manual/webrtc/delay-based-bwe)。解决它的，是 **pacing（平滑发包）**——`modules/pacing/` 的 `PacingController`。

## 问题：突发有害

<Callout type="info">
  **一帧视频可能被拆成几十个 [RTP 包](https://daiw.org/manual/webrtc/rtp-packet)，如果编码器一出帧就把它们全塞进网络，就是一个瞬时突发。** 突发的害处：

  - **瞬时超过链路容量** —— 哪怕平均码率合规，这一瞬间的速率远超链路，包在瓶颈处**排队**，增加[延迟](https://daiw.org/manual/webrtc/transport-cc)、甚至丢包。
  - **干扰[带宽估计](https://daiw.org/manual/webrtc/delay-based-bwe)** —— 突发让包的到达时间乱成一团，[延迟趋势](https://daiw.org/manual/webrtc/delay-based-bwe)、[探测](https://daiw.org/manual/webrtc/probing)测量都被污染，估不准。

  想象水管：你要平稳的水流，而不是一桶一桶地猛倒。**pacing 就是把“一桶水”变成“平稳的细流”**——把一帧的包，按目标码率的节奏，**均匀地**在时间上摊开发出。
</Callout>

## PacingController：排队 + 按节奏吐

`PacingController`（`modules/pacing/pacing_controller.h`）的接口就两个核心动作：

```cpp
// modules/pacing/pacing_controller.h(节选)
void EnqueuePacket(std::unique_ptr<RtpPacketToSend> packet);  // 包入队
void ProcessPackets();                                         // 到点就吐一批出去
```

<Callout type="info">
  **pacer 是一个“按节奏放行”的队列：[打包好的包](https://daiw.org/manual/webrtc/rtp-sender)先 `EnqueuePacket` 进队，不立即发；`ProcessPackets` 被**周期性**调用，每次按当前的 pacing 速率**放行**恰当数量的包出去。** 于是无论编码器怎么“一阵一阵”地出帧，发到网络上的包流都是**平滑均匀**的。pacing 速率通常略高于[目标码率](https://daiw.org/manual/webrtc/probing)（留点余量让队列不至于无限堆积），由[拥塞控制](https://daiw.org/manual/webrtc/congestion-control-overview)设定。

  pacer 还兼顾**优先级**：[重传包](https://daiw.org/manual/webrtc/nack-rtx)、音频包（对延迟敏感）可以优先放行，普通视频包次之。它也是[探测包的发射器](https://daiw.org/manual/webrtc/probing)——[ProbeController](https://daiw.org/manual/webrtc/probing) 要发探测，就是通过 pacer 按探测速率吐出一串包。所以 pacer 站在**发送路径的咽喉**：所有出网的包都从它这儿按节奏走，它是[拥塞控制把“决策”变成“实际发包行为”](https://daiw.org/manual/webrtc/congestion-control-overview)的执行点。
</Callout>

现代实现是 `TaskQueuePacedSender`（`pacing/task_queue_paced_sender.h`）——把 pacer 跑在一个 [TaskQueue](https://daiw.org/manual/webrtc/threading-model) 上，用[任务调度](https://daiw.org/manual/webrtc/threading-model)驱动 `ProcessPackets` 的周期调用，契合 [WebRTC 的线程模型](https://daiw.org/manual/webrtc/threading-model)。

## 在发送路径的位置

把 pacer 放进[发送路径](https://daiw.org/manual/webrtc/rtp-sender)看，它夹在「打包」和「发出」之间：

```mermaid
flowchart TD
    ENC["编码器出帧(一阵一阵,突发)"] --> PACK["RTPSender 打包"]
    PACK -->|"EnqueuePacket"| PACER["PacingController 队列<br/>(按目标码率节奏放行)"]
    PACER -->|"ProcessPackets 平滑吐出"| EG["RtpSenderEgress → SRTP → ICE"]
    CC["拥塞控制:设 pacing 速率"] -.->|"该多快"| PACER
    PROBE["ProbeController"] -.->|"发探测包"| PACER
```

编码器的突发进 pacer 的队列，出来的是平稳的包流——**pacer 把“内容生产的节奏”和“网络发送的节奏”解耦了**。这也回答了[发送路径那章](https://daiw.org/manual/webrtc/rtp-sender)为什么「打包」和「发出」要分两段：中间正是留给 pacer。

## 小结与下一章

**Pacing 把突发的发包变平滑**：[编码器一帧的一批包](https://daiw.org/manual/webrtc/rtp-packet)若一股脑发出会造成瞬时拥塞、[干扰估计](https://daiw.org/manual/webrtc/delay-based-bwe)。`PacingController`（`EnqueuePacket` 入队、`ProcessPackets` 按节奏放行）把包**均匀摊开**发出，速率由[拥塞控制](https://daiw.org/manual/webrtc/congestion-control-overview)设定（略高于[目标码率](https://daiw.org/manual/webrtc/probing)），兼顾优先级、兼发[探测](https://daiw.org/manual/webrtc/probing)。现代用 `TaskQueuePacedSender`（[跑在 TaskQueue 上](https://daiw.org/manual/webrtc/threading-model)）。它站在发送咽喉，是[拥塞控制决策的执行点](https://daiw.org/manual/webrtc/congestion-control-overview)。

pacing 控制「按节奏发」，但还有一个更硬的约束——**在途未确认的数据量**不能太多。这是拥塞窗口的活。下一章。👉 [拥塞窗口](https://daiw.org/manual/webrtc/congestion-window)。

## 参考资料

- [1] libwebrtc 源码.`modules/pacing/pacing_controller.h`、`task_queue_paced_sender.h`（`EnqueuePacket` / `ProcessPackets`）。
- [2] libwebrtc 源码.`modules/pacing/`（pacer 与探测发射）。
- [3] 关联[拥塞控制总览](https://daiw.org/manual/webrtc/congestion-control-overview)——pacer 是其执行端。
