# 源码地图 · 5000 个文件怎么组织

> 面对 libwebrtc 5000+ 个文件，先要一张地图。这一章讲清顶层目录的分工与分层依赖：api/ 是唯一的公共入口，pc/ 实现 PeerConnection，p2p/ 管连接，modules/ 装下所有重活，rtc_base/ 是地基——以及它们谁依赖谁。

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

# 源码地图 · 5000 个文件怎么组织

打开 libwebrtc，第一感觉往往是「无从下手」。但这套代码其实**分层极其清晰**——理解顶层几个目录的分工与依赖方向，你就有了一张能一直用下去的地图。这一章就画这张图。

## 唯一的入口：api/

从**依赖关系**切入最清楚。项目自带的架构文档 `g3doc/index.md` 开宗明义：

> The public API of the WebRTC library consists of the `api/` directory and its subdirectories. **No other files should be depended on by webrtc users.**

翻译过来：**`api/` 是使用者唯一该依赖的目录**，里面全是**接口**（纯虚类，名字多以 `Interface` 结尾）和数据结构——`PeerConnectionInterface`、`RtpSenderInterface`、`VideoEncoderFactory`……它们定义「能做什么」，却不含「怎么做」。`api/` 之外的一切，都是**实现细节**，随时可能变，用户不该直接依赖。

这个「接口与实现分离」的纪律，是理解整个代码库的钥匙：**`api/` 声明契约，其余目录履行契约。**

## 分层依赖

其余目录，大致按这样的层次依赖（上层用下层，下层不知上层）：

```mermaid
flowchart TD
    API["api/ · 公共接口<br/>PeerConnectionInterface 等(用户唯一依赖)"]
    PC["pc/ · 会话层<br/>PeerConnection·SDP 协商·Transceiver·DataChannel"]
    MID["p2p/ · 连接　call/ · 中枢　media/ · 媒体引擎"]
    MOD["modules/ · 功能模块<br/>rtp_rtcp·audio_processing·video_coding·congestion_controller…"]
    BASE["rtc_base/ · 基础设施<br/>Thread·TaskQueue·缓冲·时间·同步"]
    API --> PC
    PC --> MID
    MID --> MOD
    MOD --> BASE
    PC --> MOD
    MID --> BASE
```

越往下越通用、越稳定；越往上越贴近「一次通话」的业务。下面自上而下逐层认识。

## 会话层：pc/

`pc/` 实现 `PeerConnection`——[那个「上帝对象」](https://daiw.org/manual/webrtc/call-lifecycle)。它把一次通话的**控制面**都装在这里：

- `pc/peer_connection.cc` —— `PeerConnection` 本体。
- `pc/sdp_offer_answer.cc` —— `SdpOfferAnswerHandler`，SDP 提议 / 应答协商的状态机。
- `pc/jsep_transport_controller.cc` —— 把协商结果落到底层传输（ICE/DTLS）。
- `pc/rtp_transceiver.cc` / `pc/rtp_sender.cc` / `pc/rtp_receiver.cc` —— 收发的收发器。
- `pc/channel.cc` —— 媒体通道，连接 SDP 与媒体引擎。
- `pc/data_channel_controller.cc` —— 数据通道管理。

这是[第 1 部分](https://daiw.org/manual/webrtc/call-lifecycle)的主战场。

## 连接、中枢、媒体引擎：p2p/ call/ media/

中间这层是「怎么把媒体真正收发起来」：

- **`p2p/`** —— **ICE 连接建立**。核心 `p2p/base/p2p_transport_channel.cc`（候选配对与连通性检查）、`p2p/base/port_allocator.cc`（候选采集）、STUN / TURN 的实现。第 2 部分主角。
- **`call/`** —— **`Call` 中枢对象**（`call/call.cc`）。它管理一次通话里所有的发送流 / 接收流，并持有拥塞控制的总控 `RtpTransportControllerSend`。第 9 部分主角。
- **`media/`** —— **媒体引擎**的胶水层。`media/engine/webrtc_voice_engine.cc`、`media/engine/webrtc_video_engine.cc` 把上层的轨道 / 编解码器接到下层的 RTP 模块。

而**发送 / 接收管线**本身，放在顶层的 `video/` 与 `audio/`：`video/video_stream_encoder.cc`（编码管线）、`video/video_receive_stream2.cc`（接收管线）、`audio/audio_send_stream.cc` 等。

## 重活都在 modules/

`modules/` 是**功能模块的大仓库**，WebRTC 绝大部分「硬核算法」都在这：

| 目录 | 内容 | 本教程 |
| --- | --- | --- |
| `modules/rtp_rtcp/` | RTP / RTCP 打包、发送、反馈 | 第 4 部分 |
| `modules/congestion_controller/` | 拥塞控制 GCC、带宽估计 | 第 5 部分 |
| `modules/pacing/` | 平滑发包 `PacedSender` | 第 5 部分 |
| `modules/video_coding/` | 抖动缓冲、帧组装、FEC、打包 | 第 6 部分 |
| `modules/audio_coding/` | 音频编解码、NetEq 抖动缓冲 | 第 7 部分 |
| `modules/audio_processing/` | APM：回声消除 AEC3、降噪、增益 | 第 7 部分 |
| `modules/desktop_capture/` | 屏幕 / 窗口采集 | 第 12 部分 |

数据通道的 SCTP 实现单独放在 `net/dcsctp/`（第 8 部分）。

## 地基：rtc_base/

最底层的 `rtc_base/`，是**与 WebRTC 业务无关的通用基础设施**——线程与任务队列（`rtc_base/thread.cc`、`TaskQueue`）、同步原语、时间与时钟、缓冲区（`CopyOnWriteBuffer`）、引用计数、速率统计…… 几乎每个上层模块都用到它。第 10 部分专讲。

其余还有：`logging/` 与 `rtc_event_log/`（观测，第 11 部分）、`stats/`（统计报告）、`sdk/`（Android / iOS 平台封装，第 12 部分）。

## 命名约定：一个类，三个名字

读 `pc/` 及以下的代码时，你会**反复**撞见同一个东西的三种命名。记住这个约定能省很多困惑：

<Callout type="info">
  **同一个组件常有三个名字：`XxxInterface`、`XxxProxy`、`Xxx`。** 以 `g3doc/threading_design.md` 的说法：

  - **`XxxInterface`**（在 `api/`）—— 公共接口，用户依赖这个。
  - **`XxxProxy`**（不在 `api/`）—— **代理**，负责把调用**转发到正确的线程**上执行。
  - **`Xxx`**（不在 `api/`）—— 真正干活的**实现**，只能在它的「归属线程」上被创建、调用、销毁。

  比如 `PeerConnectionInterface` / `PeerConnectionProxy` / `PeerConnection`。为什么要多一层 `Proxy`？因为 WebRTC 是[多线程的](https://daiw.org/manual/webrtc/threading-model)，代理负责把你从任意线程发起的调用，安全地[「跳」到组件的归属线程](https://daiw.org/manual/webrtc/proxy-pattern)——这是它保证线程安全的核心手法，我们专门用[两章](https://daiw.org/manual/webrtc/threading-model)讲。
</Callout>

## 一个找路技巧：header guard 就是路径

本教程统一用**上游 canonical 路径**（如 `modules/rtp_rtcp/source/rtp_sender.h`）。当你手上的源码路径和这里对不上时（比如某些工具会重排目录），有个万无一失的办法：**看头文件的 include guard**——它直接编码了 canonical 路径。例如 `rtp_sender.h` 顶部写着：

```cpp
#ifndef MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_
#define MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_
```

把下划线换成斜杠、去掉结尾 `_H_`，就是 `modules/rtp_rtcp/source/rtp_sender.h`。整个代码库严格遵守这个约定，所以**任何文件的规范路径，读它自己的 guard 就知道**。

## 小结与下一章

libwebrtc **分层清晰**：`api/`（唯一公共入口，全是接口）→ `pc/`（会话层，PeerConnection）→ `p2p/` `call/` `media/`（连接、中枢、媒体引擎）→ `modules/`（所有重活：RTP、拥塞控制、音视频编解码、APM）→ `rtc_base/`（通用地基）。命名上一个组件常有 `Interface` / `Proxy` / `Impl` 三副面孔；header guard 则直接编码 canonical 路径。

有了地图，该看**动态**了：一次通话从无到有，这些目录里的对象**按什么顺序、怎么协作**把连接搭起来？👉 [一次通话的一生](https://daiw.org/manual/webrtc/call-lifecycle)。

## 参考资料

- [1] libwebrtc 源码.`g3doc/index.md`（`api/` 作为唯一公共依赖）、`g3doc/threading_design.md`（Interface/Proxy/Impl 命名）。
- [2] libwebrtc 源码.顶层目录 `api/` `pc/` `p2p/` `call/` `media/` `modules/` `rtc_base/`。
- [3] WebRTC 项目.「Development」https://webrtc.googlesource.com/src/ —— 官方源码仓库。
