# 视频管线总览 · 从采集到渲染

> 一帧视频从摄像头到对方屏幕，要走采集→自适应→编码→打包→传输→抖动缓冲→组帧→解码→渲染一长串。这一章画出发送侧 VideoStreamEncoder 与接收侧 VideoReceiveStream2 两条管线的全貌，以及 video/ 与 modules/video_coding/ 的分工，为第 6 部分建立地图。

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

# 视频管线总览 · 从采集到渲染

拿到[拥塞控制给的目标码率](https://daiw.org/manual/webrtc/rate-control)，视频这一路终于要动起来了。一帧视频从你的摄像头，到对方的屏幕，要穿过**一长串环节**——任何一环出问题，就是「卡、糊、花、飘」。第 6 部分拆解这条管线（`video/` 与 `modules/video_coding/`）。先建地图。

## 两条管线，一条链路

视频有**发送**和**接收**两条管线，首尾相接成一条完整链路：

```mermaid
flowchart LR
    subgraph SEND["发送侧(video/)"]
        CAM["摄像头采集"] --> ADAPT["自适应<br/>(降分辨率/帧率)"]
        ADAPT --> ENC["VideoStreamEncoder<br/>编码"]
        ENC --> PACK["按编解码器打包 RTP"]
    end
    PACK -->|"Pacing→SRTP→ICE(第3-5部分)"| NET["网络"]
    NET --> RECV
    subgraph RECV["接收侧(video/ + video_coding/)"]
        JB["抖动缓冲<br/>组帧"] --> REF["帧引用查找"]
        REF --> DEC["解码"]
        DEC --> REND["按时间戳渲染"]
    end
```

<Callout type="info">
  **发送侧的核心是 [`VideoStreamEncoder`](https://daiw.org/manual/webrtc/video-source-encoder)（`video/video_stream_encoder.h`），接收侧的核心是 `VideoReceiveStream2`（`video/video_receive_stream2.h`）。** 一发一收，各领一条管线：

  - **发送**：摄像头出[原始帧](https://daiw.org/manual/webrtc/video-frame) → 按网络状况[自适应地降分辨率 / 帧率](https://daiw.org/manual/webrtc/video-adaptation) → [编码](https://daiw.org/manual/webrtc/video-encoder-abstraction)成压缩帧 → [按编解码器格式打成 RTP 包](https://daiw.org/manual/webrtc/video-packetization) → 交给[第 3-5 部分传输](https://daiw.org/manual/webrtc/rate-control)。
  - **接收**：[收包](https://daiw.org/manual/webrtc/rtp-demuxer) → [抖动缓冲 + 组帧](https://daiw.org/manual/webrtc/video-jitter-buffer)（把乱序的包拼回完整帧） → [理清帧的参考关系](https://daiw.org/manual/webrtc/frame-reference-finder) → [解码](https://daiw.org/manual/webrtc/video-decode-recovery) → 按 [`VCMTiming` 算出的时刻](https://daiw.org/manual/webrtc/video-timing)渲染。

  接收侧比发送侧还复杂——因为它要**对抗网络的混乱**（[乱序、丢包、抖动](https://daiw.org/manual/webrtc/rtp-packet)），把破碎的包流重建成流畅的画面。`VideoReceiveStream2` 那个 [`2`](https://daiw.org/manual/webrtc/rtp-rtcp-module) 又是现代重写的标记。
</Callout>

## 视频为什么这么难

音频和视频都要实时传，但视频的挑战大一个量级，原因值得先说清：

- **数据量巨大** —— 一路 1080p 视频未压缩每秒几百 MB，必须**重度压缩**（几百倍），这就引入了编解码的全部复杂性。
- **帧间有依赖** —— 视频压缩靠**帧间预测**（这一帧只编码「和上一帧的差异」）。于是[丢一个包可能毁掉后续一串帧](https://daiw.org/manual/webrtc/video-decode-recovery)（参考链断了），这是视频对丢包格外敏感的根源。
- **要适配多变的网络与算力** —— 带宽、CPU 都在变，视频要能[实时降质保流畅](https://daiw.org/manual/webrtc/video-adaptation)、[分档服务不同接收者](https://daiw.org/manual/webrtc/simulcast-svc)。

这三点，决定了视频管线的每个环节都不简单。

## 分工：video/ 与 video_coding/

第 6 部分的代码横跨两个目录：

| 目录 | 管什么 |
| --- | --- |
| `video/` | **管线编排**：[`VideoStreamEncoder`](https://daiw.org/manual/webrtc/video-source-encoder)、`VideoReceiveStream2`、[自适应](https://daiw.org/manual/webrtc/video-adaptation)、[码率分配](https://daiw.org/manual/webrtc/video-bitrate-allocation) |
| `modules/video_coding/` | **编解码支撑**：[抖动缓冲 / 组帧](https://daiw.org/manual/webrtc/video-jitter-buffer)、[帧引用](https://daiw.org/manual/webrtc/frame-reference-finder)、[FEC](https://daiw.org/manual/webrtc/fec)、[打包](https://daiw.org/manual/webrtc/video-packetization)、[timing](https://daiw.org/manual/webrtc/video-timing) |

具体的**编解码器实现**（VP8/VP9/AV1/H264/H265）则在 `modules/video_coding/codecs/`，通过[统一的 `VideoEncoder` 抽象](https://daiw.org/manual/webrtc/video-encoder-abstraction)接入。

## 本部分路线

第 6 部分按「发送 → 接收」走一遍这条管线：

1. [视频帧](https://daiw.org/manual/webrtc/video-frame)（数据长什么样）→ [采集与编码器输入](https://daiw.org/manual/webrtc/video-source-encoder) → [编码器抽象与编解码器](https://daiw.org/manual/webrtc/video-encoder-abstraction) → [自适应](https://daiw.org/manual/webrtc/video-adaptation) → [联播与 SVC](https://daiw.org/manual/webrtc/simulcast-svc) → [码率分配](https://daiw.org/manual/webrtc/video-bitrate-allocation) → [打包](https://daiw.org/manual/webrtc/video-packetization)。
2. 接收：[抖动缓冲与组帧](https://daiw.org/manual/webrtc/video-jitter-buffer) → [帧引用查找](https://daiw.org/manual/webrtc/frame-reference-finder) → [渲染时机](https://daiw.org/manual/webrtc/video-timing) → [解码与丢包恢复](https://daiw.org/manual/webrtc/video-decode-recovery)。

## 小结与下一章

视频有**发送**（[`VideoStreamEncoder`](https://daiw.org/manual/webrtc/video-source-encoder)：采集→[自适应](https://daiw.org/manual/webrtc/video-adaptation)→[编码](https://daiw.org/manual/webrtc/video-encoder-abstraction)→[打包](https://daiw.org/manual/webrtc/video-packetization)）和**接收**（`VideoReceiveStream2`：[组帧](https://daiw.org/manual/webrtc/video-jitter-buffer)→[理引用](https://daiw.org/manual/webrtc/frame-reference-finder)→[解码](https://daiw.org/manual/webrtc/video-decode-recovery)→[按时渲染](https://daiw.org/manual/webrtc/video-timing)）两条管线。视频难在**数据量大**（须重压缩）、**帧间依赖**（丢包毁一串）、**要适配多变网络与算力**。代码横跨 `video/`（编排）与 `modules/video_coding/`（编解码支撑）。

一切从「一帧视频在内存里长什么样」开始。下一章看 `VideoFrame` 与它的像素缓冲。👉 [视频帧](https://daiw.org/manual/webrtc/video-frame)。

## 参考资料

- [1] libwebrtc 源码.`video/video_stream_encoder.h`、`video/video_receive_stream2.h`。
- [2] libwebrtc 源码.`modules/video_coding/`（编解码支撑）、`g3doc/adaptation.md`（自适应）。
- [3] 关联[第 5 部分拥塞控制](https://daiw.org/manual/webrtc/rate-control)——视频码率的来源。
