# 采集与编码器输入 · Source、Sink 与 VideoStreamEncoder

> 摄像头怎么把帧喂给编码器？靠 source/sink 接口。这一章读 video/video_stream_encoder.h:VideoStreamEncoder 作为帧的 sink 接收采集、SetSource 绑定视频源、以及它在把帧交给编码器前做的第一道处理（丢帧、格式转换）。

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

# 采集与编码器输入 · Source、Sink 与 VideoStreamEncoder

[视频帧](https://daiw.org/manual/webrtc/video-frame)从摄像头产出后，怎么流到[编码器](https://daiw.org/manual/webrtc/video-encoder-abstraction)？WebRTC 用一对简洁的接口连接「帧的生产者」和「消费者」，而消费的核心是 **`VideoStreamEncoder`**（`video/video_stream_encoder.h`）。这一章看这个入口。

## Source 与 Sink：生产者与消费者

WebRTC 用 **source / sink** 模式连接视频的生产和消费（`api/video/`）：

- **`VideoSourceInterface<VideoFrame>`（源）** —— 帧的**生产者**：摄像头、屏幕采集、测试图案生成器……凡是「能产出帧」的都是 source。
- **`VideoSinkInterface<VideoFrame>`（汇）** —— 帧的**消费者**：通过 `OnFrame(frame)` 接收帧。[编码器](https://daiw.org/manual/webrtc/video-encoder-abstraction)、本地预览都是 sink。

`VideoStreamEncoder` 本身就是一个 **sink**——它 `SetSource` 绑定一个视频源，之后源每产一帧，就通过 `OnFrame` 推给它：

```cpp
// video/video_stream_encoder.h(节选)
void SetSource(VideoSourceInterface<VideoFrame>* source,
               const DegradationPreference& degradation_preference) override;
```

<Callout type="info">
  **注意 `SetSource` 的第二个参数 `DegradationPreference`——它一开始就定下了「网络 / CPU 不够时，这条流优先牺牲什么」。** [降分辨率还是降帧率](https://daiw.org/manual/webrtc/video-adaptation)，取决于内容：讲课 / 共享屏幕（细节重要）宁可[降帧率保清晰](https://daiw.org/manual/webrtc/video-adaptation)，视频通话（动作重要）宁可[降分辨率保流畅](https://daiw.org/manual/webrtc/video-adaptation)。这个偏好在绑定源时就传入，是[下一步自适应](https://daiw.org/manual/webrtc/video-adaptation)的依据。source/sink 这层抽象还带来一个好处：**采集源可以随意替换**（摄像头、屏幕、虚拟背景处理后的帧……），`VideoStreamEncoder` 不关心帧从哪来，只管消费——又是 WebRTC [解耦](https://daiw.org/manual/webrtc/source-map)哲学的体现。
</Callout>

## 进编码器前的第一道处理

帧到了 `VideoStreamEncoder`，不是直接扔进[编码器](https://daiw.org/manual/webrtc/video-encoder-abstraction)，中间还有几道处理：

- **帧率控制 / 丢帧** —— 如果[目标帧率](https://daiw.org/manual/webrtc/video-adaptation)低于采集帧率（比如摄像头 30fps，但当前只编 15fps），就**丢掉一半的帧**，别浪费 CPU 编了又不发。源头丢帧比编码后丢经济得多。
- **格式转换** —— 把采集的格式（NV12、RGB 等）转成[编码器要的 I420](https://daiw.org/manual/webrtc/video-frame)。
- **分辨率缩放** —— 若[自适应](https://daiw.org/manual/webrtc/video-adaptation)要求降分辨率，在这里把帧缩小。
- **过载保护** —— 如果[编码器忙不过来](https://daiw.org/manual/webrtc/video-adaptation)（CPU 跟不上），丢帧或降质，别让队列堆积、延迟爆炸。

<Callout type="info">
  **`VideoStreamEncoder` 是「编码前的调度中枢」——它决定每一帧是编、是丢、还是缩，再交给底层编码器。** 这个位置极关键：它把「摄像头恒定产帧」和「[网络 / CPU 能承受多少](https://daiw.org/manual/webrtc/rate-control)」之间的鸿沟填平。摄像头不管网络好坏都按 30fps 产帧，但网络可能只够 15fps、CPU 可能只够编 720p——`VideoStreamEncoder` 就在这里做取舍：丢掉多余的帧、缩小过大的帧、跳过编不过来的帧。**它是[码率 / 分辨率 / 帧率自适应](https://daiw.org/manual/webrtc/video-adaptation)的执行现场**，下一章专讲这套自适应逻辑。
</Callout>

## 在管线里的位置

```mermaid
flowchart LR
    SRC["VideoSource(摄像头/屏幕)"] -->|"OnFrame(每帧)"| VSE["VideoStreamEncoder(sink)"]
    VSE --> DROP{"该编吗?<br/>(帧率/过载)"}
    DROP -->|"丢帧"| X["丢弃(省CPU)"]
    DROP -->|"编"| CONV["格式转 I420 + 按需缩放"]
    CONV --> ENC["VideoEncoder 编码(下一章)"]
    RATE["拥塞控制目标码率"] -.->|"该多清晰"| VSE
```

`VideoStreamEncoder` 一手连着[采集源](https://daiw.org/manual/webrtc/video-frame)，一手连着[编码器](https://daiw.org/manual/webrtc/video-encoder-abstraction)，中间接收[拥塞控制的码率指令](https://daiw.org/manual/webrtc/rate-control)，做出「这一帧怎么处理」的决策。

## 小结与下一章

WebRTC 用 **source/sink** 连接帧的生产与消费：[视频源](https://daiw.org/manual/webrtc/video-frame) `VideoSourceInterface` 产帧，`VideoStreamEncoder`（一个 **sink**）`SetSource` 绑定源、`OnFrame` 收帧，绑定时传入 **`DegradationPreference`**（[定优先降什么](https://daiw.org/manual/webrtc/video-adaptation)）。进[编码器](https://daiw.org/manual/webrtc/video-encoder-abstraction)前，`VideoStreamEncoder` 做**帧率控制 / 丢帧、格式转 [I420](https://daiw.org/manual/webrtc/video-frame)、缩放、过载保护**——它是「编码前的调度中枢」，填平「摄像头恒定产帧」与「[网络 / CPU 能承受多少](https://daiw.org/manual/webrtc/rate-control)」的鸿沟。

帧准备好了，交给编码器。下一章看统一的 `VideoEncoder` 抽象，以及 VP8/VP9/AV1/H264/H265 这些编解码器。👉 [编码器抽象与编解码器](https://daiw.org/manual/webrtc/video-encoder-abstraction)。

## 参考资料

- [1] libwebrtc 源码.`video/video_stream_encoder.h`（`SetSource`、`OnFrame`）、`api/video/video_source_interface.h`、`video_sink_interface.h`。
- [2] libwebrtc 源码.`api/video/video_stream_encoder_interface.h`。
- [3] 关联[自适应](https://daiw.org/manual/webrtc/video-adaptation)、`g3doc/adaptation.md`。
