# 音视频与 WebRTC 详解

> 一套从真实源码出发的 WebRTC 深度教程。我们不讲玩具示例，而是打开 Google 的 libwebrtc（C++）最新 main 分支，一篇一个专题，把 PeerConnection、ICE、DTLS/SRTP、RTP/RTCP、拥塞控制、视频与音频管线、数据通道，直到 rtc_base 基础设施，一层层拆到源码级。预计 100~200 篇。

- 作者：David（道雾轩）
- 最后更新：2026-08-03
- 原文：https://daiw.org/manual/webrtc
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 序 · 一套从源码出发的 WebRTC 教程

一通视频通话看起来理所当然：点开、接通、看见对方。可这背后，是**软件工程里最复杂的实时系统之一**——采集、编码、封装、加密、穿越 NAT、对抗丢包与抖动、估计带宽、解码、渲染，几十个子系统在几十毫秒的预算里协同工作。而把这一切**开源、工业级地**实现出来的，正是 Google 的 **libwebrtc**——浏览器和无数原生应用共用的那套 C++ 库。

这个专栏，就是要**打开这套源码，一层一层读进去**。

<Callout type="info">
  **这不是一份「WebRTC 入门」，而是一次「源码级纵深」。** 我们基于 libwebrtc 的**最新 `main` 分支源码**，一篇一个专题，把每个子系统拆到**真实的类、真实的文件、真实的数据流**。全程遵循一个原则：**凡有结论，必有源码为证**——我们会指名道姓地给出文件路径（如 `pc/peer_connection.cc`）、类名（如 `SdpOfferAnswerHandler`）、关键函数，让你能对着真实代码逐行验证。

  **规模**：WebRTC 太大了，一两篇讲不清任何一个子系统。这个教程预计 **100~200 篇**，分为若干「部分」，**每篇聚焦一个专题**——一个类、一个协议环节、一个算法。它会很长，但每一篇都能独立读。
</Callout>

## 读者与前置

- **面向谁**：有 C++ 基础、想真正搞懂 WebRTC **内部如何运转**的工程师——做音视频、做实时通信、做浏览器 / 客户端，或纯粹好奇「这套东西到底怎么实现」的人。
- **需要什么**：能读 C++（不必精通模板元编程）；了解基本的网络概念（IP、UDP、TCP）。协议细节（ICE、DTLS、RTP……）我们会**从零讲起**，不预设你懂。
- **不需要什么**：你**不必**能编译 WebRTC（它的构建很重）。我们把源码摊开给你看，对照阅读即可；想动手编译的，[第一部分](https://daiw.org/manual/webrtc/reading-the-source)会给出路径。

## 这套源码有多大

先给个体量感，好让你知道我们要征服的是什么。libwebrtc 有 **5000+ 个 C++ 源文件**，几个最重的子系统：

| 子系统 | 大致文件数 | 是什么 |
| --- | --- | --- |
| `modules/audio_processing/` | ~520 | 音频处理（回声消除 AEC3、降噪、增益） |
| `modules/audio_coding/` | ~325 | 音频编解码与 NetEq |
| `modules/rtp_rtcp/` | ~310 | RTP / RTCP 实时传输核心 |
| `modules/video_coding/` | ~240 | 视频抖动缓冲、FEC、打包 |
| `net/dcsctp/` | ~240 | 数据通道的 SCTP 实现 |
| `modules/desktop_capture/` | ~230 | 屏幕 / 窗口采集 |
| `p2p/` | ~130 | ICE 连接建立 |
| `modules/congestion_controller/` | ~100 | 拥塞控制与带宽估计 |

光是「回声消除」一个模块就有五百多个文件——这就是为什么它值得 100 篇以上。

## 全景路线图

我们会按这条路线，从「一次通话怎么建立」一路深入到每个子系统的算法内核：

- **第 0 部分 · 导论与全景（本部分）** —— [WebRTC 是什么](https://daiw.org/manual/webrtc/what-is-webrtc)、[源码地图](https://daiw.org/manual/webrtc/source-map)、[一次通话的一生](https://daiw.org/manual/webrtc/call-lifecycle)、[三大线程模型](https://daiw.org/manual/webrtc/threading-model)、[代理模式与线程安全](https://daiw.org/manual/webrtc/proxy-pattern)、[怎么读这套源码](https://daiw.org/manual/webrtc/reading-the-source)。
- **第 1 部分 · PeerConnection 与会话建立** —— `PeerConnectionFactory`、`PeerConnection` 这个「上帝对象」、SDP 与 JSEP、`SdpOfferAnswerHandler` 协商状态机、`RtpTransceiver` / `RtpSender` / `RtpReceiver`、`DataChannelController`、统计收集。
- **第 2 部分 · ICE 与连接建立（`p2p/`）** —— 候选采集、`P2PTransportChannel`、`PortAllocator`、STUN 与 TURN、连通性检查、提名、ICE 重启、网络监测。
- **第 3 部分 · 安全：DTLS 与 SRTP** —— DTLS 握手、`DtlsSrtpTransport`、SRTP 密钥导出与加解密、证书与指纹、BoringSSL 集成。
- **第 4 部分 · RTP / RTCP** —— 包结构与头扩展、`RtpSenderEgress`、发送与接收侧、RTCP 反馈、NACK / RTX、FEC（ULPFEC / FlexFEC）、Transport-CC。
- **第 5 部分 · 拥塞控制与带宽估计** —— GCC（`goog_cc`）、延迟based 估计（trendline）、丢包based 估计、探测、`PacedSender` 平滑发包、目标码率。
- **第 6 部分 · 视频管线** —— `VideoStreamEncoder`、接收侧 `VideoReceiveStream2`、抖动缓冲与帧组装、编解码器抽象（VP8/VP9/AV1/H264/H265）、SVC 与联播、质量 / 帧率自适应、码率分配。
- **第 7 部分 · 音频管线** —— `AudioSendStream` / `AudioReceiveStream`、ACM、**NetEq**（自适应抖动缓冲）、**APM**（音频处理：**AEC3 回声消除**、AGC 增益、NS 降噪、VAD）。
- **第 8 部分 · 数据通道与 SCTP** —— `SctpDataChannel`、从零实现的 **dcSCTP**、握手、可靠 / 有序传输、部分可靠、流控与拥塞控制。
- **第 9 部分 · Call 与媒体引擎** —— `Call` 中枢、`RtpTransportControllerSend`、`BitrateAllocator`、媒体引擎（`WebRtcVoiceEngine` / `WebRtcVideoEngine`）。
- **第 10 部分 · 基础设施（`rtc_base/`）** —— `Thread` 与 `TaskQueue`、同步原语、时间与时钟、缓冲区、引用计数、速率统计。
- **第 11 部分 · 观测与调试** —— `RtcEventLog`、`RTCStatsReport`、事件日志可视化、field trials 实验框架、指标直方图。
- **第 12 部分 · 平台集成** —— Android（JNI）与 iOS / macOS(ObjC)SDK、音视频设备模块、桌面采集。

## 约定

- **源码版本**：基于 libwebrtc `main` 分支的一份新近快照。WebRTC 演进很快，个别类名 / 路径可能随版本微调，但**架构与核心算法长期稳定**——这正是我们着力的地方。
- **路径写法**：我们统一用**上游 canonical 路径**（如 `modules/rtp_rtcp/source/rtp_sender.cc`）。一个小技巧：每个头文件的 include guard 就编码了它的完整路径——`MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_` 对应的正是那个路径。[怎么读这套源码](https://daiw.org/manual/webrtc/reading-the-source)一章会细说。
- **代码引用**：文中的代码片段来自真实源码，可能为聚焦主线而**省略**次要分支、错误处理、日志（用 `...` 标注），但**不虚构**——关键逻辑忠于原文。

准备好了吗？我们从最根本的问题开始：**WebRTC 到底是什么，这套 C++ 库在整个生态里处于什么位置。** 👉 [WebRTC 是什么](https://daiw.org/manual/webrtc/what-is-webrtc)。

## 本专栏篇目

- [音视频与 WebRTC 详解](https://daiw.org/manual/webrtc.md): 一套从真实源码出发的 WebRTC 深度教程。我们不讲玩具示例，而是打开 Google 的 libwebrtc（C++）最新 main 分支，一篇一个专题，把 PeerConnection、ICE、DTLS/SRTP、RTP/RTCP、拥塞控制、视频与音频管线、数据通道，直到 rtc_base 基础设施，一层层拆到源码级。预计 100~200 篇。

### 第 0 部分 · 导论与全景

- [WebRTC 是什么 · 一个标准，一套协议，一份实现](https://daiw.org/manual/webrtc/what-is-webrtc.md): WebRTC 有两副面孔——W3C 定义的浏览器 API，和 IETF 定义的一整套实时通信协议。而把它们工业级地实现出来的，是 Google 的 libwebrtc（C++）。这一章讲清这三者的关系、WebRTC 解决什么问题，以及它的协议栈全貌。
- [源码地图 · 5000 个文件怎么组织](https://daiw.org/manual/webrtc/source-map.md): 面对 libwebrtc 5000+ 个文件，先要一张地图。这一章讲清顶层目录的分工与分层依赖：api/ 是唯一的公共入口，pc/ 实现 PeerConnection，p2p/ 管连接，modules/ 装下所有重活，rtc_base/ 是地基——以及它们谁依赖谁。
- [一次通话的一生 · 从 createOffer 到媒体流](https://daiw.org/manual/webrtc/call-lifecycle.md): 把整个教程的骨架先走一遍——一次 WebRTC 通话从无到有：建工厂、加轨道、createOffer 生成 SDP、交换、ICE 打通、DTLS 握手拿密钥、SRTP 媒体开跑。每一步都对应源码里一个具体的对象，这一章把它们串成一条线。
- [三大线程模型 · network / worker / signaling](https://daiw.org/manual/webrtc/threading-model.md): WebRTC 是重度多线程的，而它的世界几乎完全由三条线程组织：signaling（控制）、worker（媒体处理）、network（收发包）。搞懂谁在哪条线程上跑，是读懂任何模块的前提。这一章讲清三线程的分工、TaskQueue 抽象，以及源码里那些 RTC_DCHECK_RUN_ON 注解。
- [代理模式与线程安全 · Proxy 如何抹平线程](https://daiw.org/manual/webrtc/proxy-pattern.md): 应用可能从任意线程调 API，组件却只能在归属线程上被碰——WebRTC 用「代理对象」抹平这个矛盾：每次调用都被自动、阻塞地转发到正确的线程执行。这一章拆开 pc/proxy.h 的宏魔法，讲清 Interface / Proxy / Impl 三层如何协作保证线程安全。
- [怎么读这套源码 · 构建、约定与按图索骥](https://daiw.org/manual/webrtc/reading-the-source.md): 导论收尾，给一套实用的「读码方法论」：怎么拿到并构建（其实你不必构建）、header guard 与线程注解等路标、.h/.cc/_unittest.cc/BUILD.gn/g3doc 的分工、abseil 与引用计数等风格约定，以及怎么对照本教程按图索骥地读下去。

### 第 1 部分 · PeerConnection 与会话建立

- [PeerConnectionFactory 与 ConnectionContext · 一切的起点](https://daiw.org/manual/webrtc/factory-and-context.md): 每个 PeerConnection 都由工厂造出。这一章读真实的 PeerConnectionFactoryDependencies 依赖注入结构，和被所有连接共享的 ConnectionContext——它持有三大线程、媒体引擎、网络与 SCTP 工厂，以引用计数保证「只要还有人用就活着」。这是整个会话建立的起点。
- [PeerConnection 这个上帝对象 · 内部构造](https://daiw.org/manual/webrtc/peer-connection-object.md): 打开 pc/peer_connection.h，看这个「上帝对象」真实拥有哪些下属组件：sdp_handler_、rtp_manager_、transport_controller_、call_、data_channel_controller_、stats。每个 unique_ptr 成员对应一块职责与后续一个部分，而线程注解揭示它们各自活在哪条线程。
- [SDP 与 JSEP · 会话怎么被描述和协商](https://daiw.org/manual/webrtc/sdp-and-jsep.md): SDP 是一段描述「这通电话怎么打」的文本，JSEP 是围绕它的提议/应答模型。这一章讲清 SDP 长什么样、SdpType 的四种取值（offer/pranswer/answer/rollback）、api 层的 SessionDescriptionInterface 与内部 SessionDescription 的区别，以及那个由 6 个状态构成的信令状态机。
- [SdpOfferAnswerHandler · 协商的驾驶舱](https://daiw.org/manual/webrtc/offer-answer-handler.md): pc/sdp_offer_answer.cc 有 6400 多行，是 pc/ 里最庞大的文件——一次通话的协商大脑。这一章读它的真实结构：CreateOffer/SetLocalDescription 等入口如何经 OperationsChain 串行化，ApplyLocalDescription 又如何推进信令状态机、把协商结果下推给传输与收发器。
- [媒体协商 · MediaSessionDescriptionFactory 怎么取编解码器交集](https://daiw.org/manual/webrtc/media-session.md): SDP 协商里最实质的一步，是「双方支持的编解码器取交集」。这活儿由 pc/media_session.cc 的 MediaSessionDescriptionFactory 干：CreateOfferOrError 列出我方能力，CreateAnswerOrError 挑出双方都支持的。这一章读它怎么把「能力清单」协商成「一致约定」。
- [收发器 · RtpTransceiver 与 Unified Plan](https://daiw.org/manual/webrtc/transceivers.md): Unified Plan 下，一个 RtpTransceiver 就是一条 m-line、一路双向媒体，把一个发送器和一个接收器捆在一起。这一章读 pc/rtp_transceiver.cc：方向枚举（sendrecv/sendonly/…）、desired 与 current 两种方向的区别、mid 如何绑定 m-line，以及 RtpTransmissionManager 怎么统管这些收发器。
- [JsepTransportController 与 BUNDLE · 协商结果落到传输](https://daiw.org/manual/webrtc/jsep-transport-controller.md): 协商谈定的 ICE 凭据、DTLS 参数，要真正建成传输通道——这是 JsepTransportController 的活。这一章读它怎么为每组媒体建一个 JsepTransport（捆绑 ICE + DTLS + RTP），以及 BUNDLE 如何让多路媒体共用一条传输，省下宝贵的连接与握手。
- [DataChannelController 与数据通道 · 传任意数据](https://daiw.org/manual/webrtc/data-channel-controller.md): WebRTC 不止传音视频，还能传任意数据——文件、游戏状态、聊天消息。这一章读 pc/data_channel_controller.cc：数据通道的控制面，SctpDataChannel 如何架在 SCTP-over-DTLS 之上，以及可靠性/有序性怎么配置。SCTP 协议本身留到第 8 部分深挖。
- [RtcStatsCollector · 统计怎么跨线程汇总](https://daiw.org/manual/webrtc/stats-collector.md): getStats（） 返回的那份报告，数据散落在不同对象、不同线程里。这一章读 pc/rtc_stats_collector.cc：它如何在 signaling 与 network 线程上分别采集「部分结果」再合并、如何用短缓存避免重复劳动，以及 RTCStatsReport 为什么是一堆类型化的统计对象。第 1 部分收尾。

### 第 2 部分 · ICE 与连接建立

- [ICE 总览 · 怎么在两个 NAT 后打通一条路](https://daiw.org/manual/webrtc/ice-overview.md): 两个都藏在 NAT 后的端点，怎么找到一条能互通的路？这就是 ICE 要解决的核心难题。这一章讲清 NAT 穿越的困境、ICE 的「收集候选→配对→连通性检查→选路」四步思路，以及 p2p/ 里那张核心类地图：P2PTransportChannel、Port、Candidate、Connection、PortAllocator、IceController。
- [候选 · host / srflx / prflx / relay](https://daiw.org/manual/webrtc/candidates.md): ICE 的第一步是收集候选——每一个「我可能被联系到的地址」。这一章读 api/candidate.h 的 IceCandidateType，讲清四种候选：主机候选、服务器反射候选、对端反射候选、中继候选，它们各自怎么来、优先级如何，以及为什么要同时准备这么多条备选。
- [PortAllocator 与候选采集 · 四种 Port](https://daiw.org/manual/webrtc/port-allocator.md): 候选由 Port 采集，Port 由 PortAllocator 创建。这一章读 p2p/ 的 PortAllocator/BasicPortAllocator 与 PortAllocatorSession，以及四种 Port——UDPPort、StunPort、TcpPort、TurnPort，它们分别产出哪种候选，以及怎么用 flag 开关它们。
- [STUN 协议 · ICE 的万能工具](https://daiw.org/manual/webrtc/stun-protocol.md): STUN 是 ICE 里出场最多的协议，身兼两职：问出自己的公网地址（srflx 候选），以及给候选对做连通性检查（STUN ping）。这一章读 p2p 的 stun.cc，讲清 STUN 消息结构、Binding 请求/响应、XOR-MAPPED-ADDRESS 怎么让你「照镜子」看到公网地址。
- [TURN 中继 · 直连不通时的保底](https://daiw.org/manual/webrtc/turn.md): 对称 NAT 会让 STUN 直连失败，这时只能让媒体经服务器转发——这就是 TURN。这一章读 base/turn_port.h，讲清 TURN 的 Allocate 分配中继地址、Permission 授权机制、媒体怎么绕道服务器，以及为什么它虽是保底方案却不可或缺。
- [Connection 与连通性检查 · 一个候选对通不通](https://daiw.org/manual/webrtc/connection-and-checks.md): 一个 Connection 就是一个候选对——本地候选配远端候选，是 ICE 里「连通性」的最小单元。这一章读 base/connection.h 真实的 WriteState 状态机（WRITABLE/UNRELIABLE/INIT/TIMEOUT）、writable 与 receiving 的区别，以及 Ping 怎么用 STUN 探测把一条路验证成「可用」。
- [P2PTransportChannel · ICE 总指挥](https://daiw.org/manual/webrtc/p2p-transport-channel.md): P2PTransportChannel 是 ICE 的总指挥，把候选采集、配对、连通性检查、选路串成一台状态机。这一章读 base/p2p_transport_channel.h：它如何实现 IceTransportInternal、MaybeStartGathering 与 AddRemoteCandidate 两个入口、以及 gathering 与 transport 两套状态怎么演进到 connected/completed。
- [IceController · 选路的大脑](https://daiw.org/manual/webrtc/ice-controller.md): 一堆候选对里，用哪条传媒体、什么时候切换、下一个 ping 谁？这些决策抽出成了 IceController。这一章读 base/ice_controller_interface.h 的 ShouldSwitchConnection/SortAndSwitchConnection，以及 controlling/controlled 两种 ICE 角色如何分工——一方拍板，一方跟随。
- [提名与 ICE 重启 · 拍板与重来](https://daiw.org/manual/webrtc/nomination-and-restart.md): 控制方怎么「拍板」选定一条路？靠提名——在 STUN 探测里带上 USE-CANDIDATE。而网络剧变、连接彻底断掉时，ICE 还能整个重启重来。这一章讲常规提名 vs 激进提名、ICE 重启换新凭据重新采集，以及持续采集如何支撑移动场景。
- [网络监测 · NetworkManager 与移动性](https://daiw.org/manual/webrtc/network-monitoring.md): ICE 的移动性，眼睛是 NetworkManager——它枚举本机所有网卡、监测网络变化。这一章读 rtc_base 的 network.h:GetNetworks 列出接口、StartUpdating 与 SignalNetworksChanged 感知变化、网卡类型与网络成本，以及网络切换如何触发连接迁移让通话活下去。收尾第 2 部分。

### 第 3 部分 · 安全:DTLS 与 SRTP

- [安全总览 · 强制加密与 DTLS-SRTP](https://daiw.org/manual/webrtc/security-overview.md): WebRTC 强制加密媒体，没有明文选项。这一章讲清它的双层安全模型：DTLS 负责握手协商密钥，SRTP 负责加密每个媒体包（DTLS-SRTP,RFC 5764）；为什么废弃了旧的 SDES；支持哪些加密套件；以及 DTLS 在 WebRTC 里的两个用途。
- [DTLS 握手 · 在 ICE 通路上建安全信道](https://daiw.org/manual/webrtc/dtls-handshake.md): DTLS 是「跑在 UDP 上的 TLS」。这一章读 g3doc/dtls_transport.md 与 dtls 源码：DtlsTransport 如何在 ICE 变 writable 后握手、怎么在同一条路上把 DTLS/SRTP/STUN 三种包多路复用区分开，以及握手产出的两样东西——SRTP 密钥材料与远端证书指纹。
- [证书与指纹 · 防中间人的关键](https://daiw.org/manual/webrtc/certificates-fingerprints.md): DTLS 握手用自签名证书，可自签名怎么防中间人？靠指纹——把证书的哈希写进 SDP 的 a=fingerprint 行，握手后比对远端证书的真实指纹。这一章读 rtc_certificate.h，讲清证书生成、指纹如何把 DTLS 安全和信令绑定，以及 setup 角色谁当 DTLS 客户端。
- [DTLS-SRTP 密钥导出 · 从握手到 SRTP 密钥](https://daiw.org/manual/webrtc/dtls-srtp-key-export.md): DTLS 握手不直接加密媒体，而是导出一份「密钥材料」交给 SRTP。这一章读 pc/dtls_srtp_transport.h:DtlsSrtpTransport 如何在握手完成时用 ExportKeyingMaterial 抽取密钥、配置给 SrtpSession，以及为什么它「握手完成后才变 writable」。
- [SRTP 加密媒体 · SrtpSession 与 libsrtp](https://daiw.org/manual/webrtc/srtp-encryption.md): 密钥就位，SRTP 开始加密每一个媒体包。这一章读 pc/srtp_session.h 与 srtp_transport.h:SrtpSession 如何封装 libsrtp、ProtectRtp/UnprotectRtp 就地加解密、SrtpTransport 怎么管 RTP 与 RTCP 两个会话，以及 rtcp-mux 如何省掉一个。
- [BoringSSL 集成 · SSL 库怎么接进来](https://daiw.org/manual/webrtc/boringssl-integration.md): DTLS 握手、证书、密钥导出，底层都靠一个密码库——BoringSSL。这一章读 rtc_base 的 openssl_stream_adapter.h：WebRTC 用 SSLStreamAdapter 接口抽象 SSL 库，OpenSSLStreamAdapter 是 BoringSSL 的落地，以及 openssl_adapter 怎么服务 TURN-over-TLS。
- [SFrame 端到端加密 · 连服务器都看不见](https://daiw.org/manual/webrtc/sframe.md): SRTP 是跳到跳加密——媒体过 SFU 服务器时，服务器能解密。要做真正的端到端加密（连服务器都看不见），需要 SFrame。这一章讲清 hop-by-hop 与 E2EE 的区别、SFrame 怎么加密媒体帧本身，以及 WebRTC 的 FrameEncryptor 钩子与 Insertable Streams。收尾第 3 部分。

### 第 4 部分 · RTP / RTCP

- [RTP 包 · 实时媒体的载体](https://daiw.org/manual/webrtc/rtp-packet.md): RTP 是音视频在网络上传输的基本载体。这一章读 modules/rtp_rtcp/source/rtp_packet.h，讲清 RTP 包的头部字段——SSRC、序号、时间戳、payload type、marker，它们各自解决什么问题，以及 RtpPacketToSend 与 RtpPacketReceived 两个方向的封装。
- [RTP 头扩展 · 每个包捎带的元数据](https://daiw.org/manual/webrtc/rtp-header-extensions.md): 标准 RTP 头就那几个字段，但拥塞控制、音视频同步、分流都需要更多信息——靠头扩展捎带。这一章讲清头扩展的机制（URI 经 SDP 协商成本地 ID）、one-byte/two-byte 格式，以及几个关键扩展：transport-cc 序号、abs-send-time、mid。
- [Payload Type 分配 · 一个数字的讲究](https://daiw.org/manual/webrtc/payload-types.md): RTP 包用 payload type 这个数字标明「负载是哪种编解码器」。它怎么分配竟大有讲究：动态范围 96-127、回退 35-63、避开 64-95，还有 BUNDLE 下的冲突消解。这一章读 g3doc/rtp.md 与 PayloadTypePicker，把这个小数字讲透。
- [RtpRtcp 模块 · 每条流的收发中枢](https://daiw.org/manual/webrtc/rtp-rtcp-module.md): modules/rtp_rtcp 的核心是 ModuleRtpRtcpImpl2——每条媒体流一个，统管这条流的 RTP 发送与 RTCP 收发。这一章讲清它在收发管线里的位置、它把 RtpSender 与 RtcpSender/RtcpReceiver 捆在一起的角色，以及一条流为什么需要这么一个中枢。
- [RtpSender 与发送路径 · 打包、发出、留底](https://daiw.org/manual/webrtc/rtp-sender.md): 发送一个 RTP 包不只是「发出去」——还要分配序号、经 pacing 平滑、并把包留一份底以备重传。这一章读 modules/rtp_rtcp/source 的 RtpSenderEgress 与 RtpPacketHistory：发送路径的分工，以及「发过的包留一份」为什么是 NACK 重传和带宽探测的基础。
- [RTCP 报告 · SR 与 RR，通话的仪表盘](https://daiw.org/manual/webrtc/rtcp-reports.md): RTP 传媒体，RTCP 传反馈。这一章讲清 RTCP 的两种基本报告：发送方报告 SR（带 NTP/RTP 时间戳映射，是音视频同步的关键）和接收方报告 RR（报丢包率、抖动、往返时延），以及它们怎么构成一次通话的「仪表盘」。
- [RTCP 反馈 · NACK / PLI / REMB](https://daiw.org/manual/webrtc/rtcp-feedback.md): 除了周期性报告，RTCP 还有一类主动的反馈报文：请求重传（NACK）、请求关键帧（PLI/FIR）、报告估计带宽（REMB）。这一章读 rtcp_packet/ 下的 Nack/Pli/TransportFeedback，讲清 RTPFB 与 PSFB 两大类反馈，它们是让接收端「指挥」发送端的通道。
- [NACK 与 RTX 重传 · 丢包了就重发](https://daiw.org/manual/webrtc/nack-rtx.md): UDP 会丢包，WebRTC 用 NACK+重传补救：接收端发现序号缺失就发 NACK，发送端从历史重发。这一章讲清 NackTracker 怎么决定请求哪些包、RTX 为什么用独立的流承载重传，以及重传的适用边界——RTT 太大时重传就来不及了。
- [FEC 前向纠错 · 不用往返就能恢复](https://daiw.org/manual/webrtc/fec.md): 重传要等一个往返，高延迟场景来不及。FEC 换个思路：提前发冗余包，收端丢了包能就地算回来，无需往返。这一章讲清 FEC 的异或恢复原理、ULPFEC 与 FlexFEC 两种实现、音频的 RED，以及 FEC 与重传「费带宽 vs 费往返」的权衡。
- [Transport-CC 反馈 · 通往拥塞控制的桥](https://daiw.org/manual/webrtc/transport-cc.md): 现代 WebRTC 拥塞控制的信号源，是 Transport-CC 反馈：发送端给每个包打一个全局序号，接收端如实报回每个包的到达时间，发送端据此算出延迟变化。这一章讲清这套反馈怎么运转、为什么把带宽估计放在发送端，以及它如何连接第 4 与第 5 部分。
- [RtpDemuxer 与接收侧分流 · 一条流上的包各归各家](https://daiw.org/manual/webrtc/rtp-demuxer.md): BUNDLE 把多条流挤进一条传输，收到一个 RTP 包，怎么知道它属于哪条接收流？这就是 RtpDemuxer 的活。这一章讲清接收侧怎么按 SSRC、mid、payload type 把包分流到正确的接收器，以及帧变换钩子在接收管线的位置。收尾第 4 部分。

### 第 5 部分 · 拥塞控制与带宽估计

- [拥塞控制总览 · GoogCC 与该发多快](https://daiw.org/manual/webrtc/congestion-control-overview.md): 该发多快？发太快网络堵、太慢浪费。WebRTC 的答案是 GoogCC——一个跑在发送端的拥塞控制器。这一章讲清它的双控制器结构（延迟based + 丢包based 取小）、以 Transport-CC 反馈为输入、产出目标码率的闭环，以及 GoogCcNetworkController 这个总成。
- [延迟based 带宽估计 · trendline 与过载检测](https://daiw.org/manual/webrtc/delay-based-bwe.md): GoogCC 的主控制器，靠「延迟趋势」在丢包前就嗅到拥塞。这一章读 goog_cc 的 DelayBasedBwe 与 TrendlineEstimator：怎么从包的到达时间算出延迟变化、用线性回归判断趋势、over-use detector 如何输出 kNormal/kOveruse/kUnderusing，以及 AIMD 如何据此加减码率。
- [丢包based 带宽估计 · 兜底的另一只眼](https://daiw.org/manual/webrtc/loss-based-bwe.md): 延迟based 有盲区，丢包based 作为另一只眼补上。这一章读 goog_cc 的 LossBasedBweV2：它怎么用丢包率估带宽、为什么和延迟based 取小、以及从早期简单丢包阈值到 v2 的演进——把丢包信号用得更精细。
- [带宽探测 · 主动试探能不能更快](https://daiw.org/manual/webrtc/probing.md): 光被动测量，涨得太慢——通话开始或网络变好时，得主动发探测包快速试探可用带宽。这一章读 goog_cc 的 ProbeController：探测怎么用成串的包测出瞬时吞吐、padding 与 RTX 两种探测手段，以及探测为什么能让码率快速爬坡。
- [Pacing 平滑发包 · 别一股脑猛发](https://daiw.org/manual/webrtc/pacing.md): 编码器一帧可能出一大批包，若一股脑发出去会造成瞬时拥塞、干扰带宽估计。Pacing 把包按目标码率的节奏平滑吐出。这一章读 pacing/pacing_controller.h 的 EnqueuePacket/ProcessPackets，讲清 pacer 的队列、发包节奏，以及它为什么是拥塞控制不可或缺的一环。
- [拥塞窗口 · 在途数据别太多](https://daiw.org/manual/webrtc/congestion-window.md): 除了控制发送速率，还要限制「已发出但还没被确认」的在途数据量——这是拥塞窗口。这一章读 goog_cc 的 congestion_window_pushback_controller，讲清在途数据为什么要有上限、超限时怎么「回压」减速，以及它和 pacing、码率控制如何配合防止缓冲区膨胀。
- [码率控制与闭环 · 估计怎么变成画质](https://daiw.org/manual/webrtc/rate-control.md): 带宽估计的终点，是把「网络能承受多少」变成「编码器该编多清晰」。这一章讲清 GoogCC 的输出 NetworkControlUpdate 怎么流向编码器与 pacer、目标码率如何在多条流间分配的引子，以及整个拥塞控制闭环怎么每隔几十毫秒转一圈。收尾第 5 部分。

### 第 6 部分 · 视频管线

- [视频管线总览 · 从采集到渲染](https://daiw.org/manual/webrtc/video-pipeline-overview.md): 一帧视频从摄像头到对方屏幕，要走采集→自适应→编码→打包→传输→抖动缓冲→组帧→解码→渲染一长串。这一章画出发送侧 VideoStreamEncoder 与接收侧 VideoReceiveStream2 两条管线的全貌，以及 video/ 与 modules/video_coding/ 的分工，为第 6 部分建立地图。
- [视频帧 · VideoFrame 与像素缓冲](https://daiw.org/manual/webrtc/video-frame.md): 一帧视频在内存里是什么样？这一章讲清 VideoFrame 的结构、I420（YUV）像素格式为什么是视频的通用货币、VideoFrameBuffer 的引用计数与零拷贝，以及帧上的时间戳、旋转等元数据。
- [采集与编码器输入 · Source、Sink 与 VideoStreamEncoder](https://daiw.org/manual/webrtc/video-source-encoder.md): 摄像头怎么把帧喂给编码器？靠 source/sink 接口。这一章读 video/video_stream_encoder.h:VideoStreamEncoder 作为帧的 sink 接收采集、SetSource 绑定视频源、以及它在把帧交给编码器前做的第一道处理（丢帧、格式转换）。
- [编码器抽象与编解码器 · VP8/VP9/AV1/H264/H265](https://daiw.org/manual/webrtc/video-encoder-abstraction.md): WebRTC 用统一的 VideoEncoder 接口抽象各种编解码器，底下接 VP8、VP9、AV1、H264、H265。这一章读 video_codecs/video_encoder.h 的 InitEncode/Encode/SetRates，讲清这层抽象、编码器工厂、以及各编解码器的取舍。
- [视频自适应 · 降分辨率还是降帧率](https://daiw.org/manual/webrtc/video-adaptation.md): 网络或 CPU 不够时，视频要主动降质保流畅——降分辨率还是降帧率？这一章读 g3doc/adaptation.md:Resource 如何发出 overuse/underuse 信号、QualityScaler 盯 QP、编码耗时当 CPU 代理、以及 DegradationPreference 如何决定牺牲哪个维度。
- [联播与 SVC · 一次编码，多档服务](https://daiw.org/manual/webrtc/simulcast-svc.md): 会议里有人网好有人网差，发一档视频没法同时讨好所有人。联播（发多条独立流）和 SVC（一条流内含多层）让 SFU 能按各接收者的网况转发合适的画质。这一章讲清两者的原理、区别，以及它们为群组通话解决的核心问题。
- [视频码率分配 · 总预算分给各层](https://daiw.org/manual/webrtc/video-bitrate-allocation.md): 拥塞控制给了总码率预算，联播/SVC 有多条流多层，怎么分？这一章读 video/video_bitrate_allocator.h:VideoBitrateAllocator 如何把总码率分配到各层、遵循「先喂饱低层再给高层」的原则，以及码率不足时怎么砍掉高层。
- [视频打包 · 压缩帧怎么切成 RTP 包](https://daiw.org/manual/webrtc/video-packetization.md): 一帧压缩视频可能几十 KB，而一个 RTP 包只能装一千多字节——得切。这一章讲清各编解码器专属的 RTP 打包格式（VP8/VP9/AV1/H264 各不同）、为什么打包要懂编解码器的内部结构、以及 marker 位如何标记一帧的结束。收尾发送侧。
- [视频抖动缓冲与组帧 · 把破碎的包流重建成帧](https://daiw.org/manual/webrtc/video-jitter-buffer.md): 收到的包乱序、有缺、时快时慢，得先缓冲、排序、拼成完整帧，才能解码。这一章读 video/frame_buffer.h：抖动缓冲怎么吸收网络抖动、组帧怎么等齐一帧的所有包，以及「缓冲大小」在流畅和延迟之间的根本权衡。
- [帧引用查找 · 这帧依赖哪些帧](https://daiw.org/manual/webrtc/frame-reference-finder.md): 视频靠帧间预测压缩，一帧解码常依赖之前的帧。接收端必须理清这层依赖，才知道一帧能不能解、丢帧毁了哪些后续。这一章读 video_coding/rtp_frame_reference_finder.h，讲清帧引用怎么被还原、以及它如何决定丢包后的可解码性。
- [渲染时机 VCMTiming · 一帧该什么时候显示](https://daiw.org/manual/webrtc/video-timing.md): 帧解好了不能立刻显示——要在正确的时刻渲染，才能流畅且与音频同步。这一章读 video_coding/timing/timing.h 的 VCMTiming::RenderTime，讲清渲染时刻怎么由抖动、解码耗时、音视频同步共同决定，以及它如何把参差的到达变成匀速的画面。
- [解码与丢包恢复 · 花屏了怎么办](https://daiw.org/manual/webrtc/video-decode-recovery.md): 帧到点解码、渲染；但丢包会让参考链断裂、画面花屏。这一章讲清 VideoDecoder 解码、关键帧作为恢复锚点、PLI 请求关键帧的时机，以及「冻结等关键帧」还是「显示花屏」的取舍。收尾第 6 部分。

### 第 7 部分 · 音频管线(NetEq / APM)

- [音频管线总览 · 比视频更精细的实时](https://daiw.org/manual/webrtc/audio-pipeline-overview.md): 音频数据量小，但对延迟和瑕疵更敏感——丢一小段、飘一下都刺耳。这一章画出音频的发送/接收管线：采集→APM 处理→编码→传输→NetEq→解码→播放，介绍 AudioSendStream/ReceiveStream、ACM，以及 WebRTC 音频的两颗明珠 NetEq 和 AEC3。
- [音频编解码器 · Opus 与它的超能力](https://daiw.org/manual/webrtc/audio-codecs.md): WebRTC 音频几乎都用 Opus——一个万能编解码器：码率 6 到 510 kbps 无级可调、内建 FEC 抗丢包、静音时 DTX 省带宽、语音音乐通吃。这一章讲清 Opus 的这些超能力、AudioEncoder/AudioDecoder 抽象，以及为什么它是实时音频的理想选择。
- [ACM 音频编码模块 · 编解码的统管者](https://daiw.org/manual/webrtc/acm.md): ACM（Audio Coding Module）是音频编解码的统管层，连接上层的流对象和底层的编解码器与 NetEq。这一章讲清 ACM 在发送侧驱动编码、在接收侧把包送进 NetEq 的角色，以及它作为「音频编解码门面」的定位。
- [NetEq 总览 · 自适应抖动缓冲 + 丢包隐藏](https://daiw.org/manual/webrtc/neteq-overview.md): NetEq 是 WebRTC 音频接收侧的明珠——自适应抖动缓冲兼丢包隐藏器。这一章读 modules/audio_coding/neteq 的 g3doc：它 InsertPacket 收包、GetAudio 出音频的两个核心接口，以及它「平滑播放、低瑕疵、低延迟」三个相互冲突的目标怎么权衡。
- [NetEq 缓冲与延迟 · 该攒多少音频](https://daiw.org/manual/webrtc/neteq-buffer-delay.md): NetEq 自适应的核心，是「目标延迟」——该攒多少音频才能既抗抖动又不太延迟。这一章讲清包缓冲区怎么按时间戳存包、延迟管理器怎么从包间到达时间估出抖动、进而算出动态的目标缓冲水平，以及这个自适应如何随网络实时调整。
- [NetEq 的六种操作 · 每 10ms 的抉择](https://daiw.org/manual/webrtc/neteq-operations.md): 每次声卡要音频，NetEq 从六种操作里选一个：Normal、Acceleration、Preemptive expand、Expand、Merge、Comfort noise。这一章依据 g3doc 讲清这六种操作各是什么、DecisionLogic 怎么根据缓冲状态选择，以及它们如何合力把混乱的网络变成流畅的声音。
- [NetEq 丢包隐藏 · 凭空编造以假乱真的音频](https://daiw.org/manual/webrtc/neteq-plc.md): 音频包丢了、声卡却要音频，NetEq 靠 Expand 操作「编造」一段填充——分析最近的波形、找出基音周期、外推续上、再逐渐淡出。这一章讲清丢包隐藏的原理、为什么语音能这么编，以及 Merge 如何把编造的填充和真实音频无缝拼接。
- [NetEq 时间伸缩 · 变速不变调](https://daiw.org/manual/webrtc/neteq-timestretch.md): NetEq 靠加速/减速播放来微调缓冲，但快进磁带会变尖、慢放会变低沉——怎么变速却不变调？靠 WSOLA：找相似波形、去掉或插入一个基音周期、交叉叠加。这一章讲清这个让 NetEq 悄悄调节延迟的核心技术。
- [APM 音频处理总览 · 让你的声音干净](https://daiw.org/manual/webrtc/apm-overview.md): 麦克风采的音里混着回声、噪声、忽大忽小的音量——APM（音频处理模块）是一条净化流水线，依次做回声消除、降噪、增益、检测。这一章读 audio_processing.h:ProcessStream 处理近端、ProcessReverseStream 喂入远端参考，以及处理链的各个环节。
- [回声问题 · 为什么你会听到自己](https://daiw.org/manual/webrtc/aec3-echo-problem.md): 不用耳机开免提，对方会听到自己的声音延迟传回——这就是声学回声。这一章讲清回声怎么产生（扬声器→房间→麦克风）、为什么这条声学路径难以捉摸、以及「双讲」为什么让回声消除难上加难。为下一章的 AEC3 算法铺垫。
- [AEC3 回声消除内核 · 估计回声再减掉](https://daiw.org/manual/webrtc/aec3-internals.md): AEC3 是 WebRTC 的回声消除引擎。它分两级：自适应线性滤波器建模声学路径、从远端参考预测回声并减掉；残余回声抑制器再把线性滤波没除净的部分压下去。这一章讲清这两级、延迟估计、以及 AEC3 为什么是 WebRTC 音频的明珠。
- [自动增益控制 · 音量忽大忽小怎么办](https://daiw.org/manual/webrtc/agc.md): 你离麦克风远近、说话大小声，音量忽高忽低——AGC 自动把它拉到稳定水平。这一章讲清 AGC 的目标电平、模拟增益（调麦克风）与数字增益（AGC2）的两级、以及它如何在「够响」和「不削顶」之间平衡。
- [降噪与语音检测 · NS 与 VAD](https://daiw.org/manual/webrtc/ns-vad.md): 降噪（NS）把风扇、空调这类稳态背景噪声压下去；语音活动检测（VAD）判断此刻有没有人在说话。这一章讲清 NS 的频域降噪原理、VAD 的用途（DTX、增益门控、舒适噪声），以及它们在 APM 链条里的角色。收尾第 7 部分的处理链。
- [音频设备与混音 · ADM 与 AudioMixer](https://daiw.org/manual/webrtc/audio-device-mixer.md): 音频的两头是硬件：采集靠麦克风、播放靠扬声器。ADM 抽象各平台的音频设备，AudioMixer 把会议里多路远端音频混成一路播放。这一章讲清这两个「两头」组件，收尾第 7 部分。

### 第 8 部分 · 数据通道与 SCTP

- [数据通道与 SCTP 总览 · 传任意数据的那条路](https://daiw.org/manual/webrtc/data-channel-overview.md): 数据通道传任意数据，底层走 SCTP over DTLS。这一章读 g3doc/sctp_transport.md:SctpTransport 如何封装一个 SCTP association、它和 DTLS 传输的关系、以及为什么 WebRTC 选 SCTP 而非直接用 TCP 或 UDP。为深入 dcSCTP 铺垫。
- [dcSCTP 从零实现 · sans-IO 的优雅](https://daiw.org/manual/webrtc/dcsctp-intro.md): WebRTC 把 SCTP 从零重写成 dcSCTP，核心是「sans-IO」设计——协议栈不碰网络，只吃字节、吐字节和回调，纯逻辑。这一章读 dcsctp/socket/dcsctp_socket.h 的 DcSctpSocket:ReceivePacket/Send/Connect，以及 sans-IO 为什么让协议栈更易测、更安全。
- [SCTP association 与握手 · 四次握手和 Cookie](https://daiw.org/manual/webrtc/sctp-association.md): 一个 SCTP association 是两端间的一条连接，建立它要四次握手——比 TCP 多一步，为的是用 Cookie 抵御 DoS 攻击。这一章讲清 association 是什么、四次握手 INIT/INIT-ACK/COOKIE-ECHO/COOKIE-ACK 的流程，以及 Cookie 机制的巧妙。
- [SCTP 包与 chunk · 数据的组织](https://daiw.org/manual/webrtc/sctp-chunks-packets.md): SCTP 的一个包里可以装多个「chunk」，每个 chunk 是一种功能单元——DATA 载数据、SACK 确认、INIT 握手……这一章讲清 SCTP 包与 chunk 的结构、常见 chunk 类型、以及「一包多块」的捆绑如何提升效率。
- [SCTP 可靠传输 · TSN、SACK 与重传](https://daiw.org/manual/webrtc/sctp-reliability.md): SCTP 默认提供可靠、有序的传输——每块数据编 TSN，接收方用 SACK 选择性确认，缺的块重传。这一章讲清 TSN 序号、SACK 比 TCP 累积确认精细在哪、以及 dcSCTP 的重传队列如何补回丢失的数据块。
- [SCTP 部分可靠 · 可靠性可调的杀手锏](https://daiw.org/manual/webrtc/sctp-partial-reliability.md): 数据通道能设「最多重传 3 次」或「50ms 内才重传」——这种介于 TCP 和 UDP 之间的部分可靠，是 SCTP 相对两者的杀手锏。这一章读 RFC 3758，讲清部分可靠怎么实现、FORWARD-TSN 如何跳过放弃的数据，以及它对应数据通道的哪些配置。
- [SCTP 流控与拥塞控制 · 数据通道的发多快](https://daiw.org/manual/webrtc/sctp-flow-congestion.md): SCTP 也要管「发多快」——流控（rwnd）防止压垮接收方，拥塞控制（cwnd）防止压垮网络。这一章讲清 SCTP 这套类 TCP 的机制、它和媒体侧 GoogCC 拥塞控制的关系，以及数据通道为什么需要独立的一套。
- [SCTP 流 · 多流复用与队头阻塞](https://daiw.org/manual/webrtc/sctp-streams.md): 一个 association 里跑多条独立的流，流内有序、流间互不阻塞——这是 SCTP 为「一条连接承载多路数据」量身设计的能力。这一章讲清流 id 的奇偶分配、按流的有序性、无序流、流数量协商、流重置，以及一个常被忽略的真相：普通 DATA 其实挡不住大消息造成的流间阻塞，要靠 I-DATA 才行。
- [数据通道建立 · DCEP](https://daiw.org/manual/webrtc/sctp-streams-dcep.md): 每条数据通道对应一个 SCTP 流 id，而通道怎么让两端就「叫什么、怎么配」达成一致，靠 DCEP（RFC 8832）的 DATA_CHANNEL_OPEN / ACK 握手。这一章讲清数据通道到流的映射、DCEP 带内协商，以及它与 SDP negotiated 模式的取舍。收尾第 8 部分。

### 第 9 部分 · Call 与媒体引擎

- [Call 对象 · 一次通话的媒体中枢](https://daiw.org/manual/webrtc/call-object.md): PeerConnection 管控制面，Call 管媒体面——它是所有发送流/接收流的中枢，也是拥塞控制的宿主。这一章读 call/call.h：Call 怎么创建和管理 AudioSendStream/VideoReceiveStream，以及它为什么活在 worker 线程。
- [RtpTransportControllerSend · 发送侧的神经中枢](https://daiw.org/manual/webrtc/rtp-transport-controller-send.md): Call 持有的发送侧总控——它把拥塞控制、pacer、包路由、反馈适配捆在一起，是所有发送流共享这套机制的地方。这一章读 rtp_transport_controller_send.h，讲清它怎么承接 Transport-CC 反馈、驱动 GoogCC、并把码率决策分发下去。
- [BitrateAllocator · 总带宽分给各条流](https://daiw.org/manual/webrtc/bitrate-allocator.md): 拥塞控制估出一个总带宽，BitrateAllocator 把它分给各条发送流——音频优先保住，视频拿剩下的。这一章读 call/bitrate_allocator.h：流作为 observer 注册、带 min/max 约束，分配器如何在总量变化时重新分配。
- [媒体引擎 · WebRtcVoiceEngine 与 WebRtcVideoEngine](https://daiw.org/manual/webrtc/media-engine.md): 媒体引擎是「协商出的媒体配置」和「实际编解码 + Call 流」之间的胶水层。这一章读 media/engine 的 WebRtcVoiceEngine/WebRtcVideoEngine：它们怎么创建媒体通道、把编解码器工厂接进来，以及在架构里承上启下的位置。
- [媒体通道 · BaseChannel 连接协商与媒体](https://daiw.org/manual/webrtc/media-channels.md): BaseChannel（pc/channel.cc）是 SDP 协商和媒体引擎之间那道最关键的胶水——SetLocalContent/SetRemoteContent 把协商结果落到媒体通道，它同时挂在 worker 和 network 线程上、向 RTP 解复用器注册收包。这一章读真实的 channel.h，看这道缝怎么缝起来。
- [第 9 部分回顾 · 控制面与媒体面如何合拢](https://daiw.org/manual/webrtc/call-recap.md): 第 9 部分把前八部分焊成一个整体——Call 是媒体中枢、RtpTransportControllerSend 是发送侧神经中枢、BitrateAllocator 分带宽、媒体引擎提供编解码、BaseChannel 缝合 SDP 与媒体。这一章串起这条主线，并给出「一个包的一生」全景。

### 第 10 部分 · 基础设施 rtc_base

- [第 10 部分导论 · 引擎底下的地基 rtc_base](https://daiw.org/manual/webrtc/rtc-base-overview.md): 前九部分讲的是 WebRTC 的引擎，但引擎底下有一层到处都在用、却很少被单独讲的地基——rtc_base。这一章讲清它是什么、为什么 WebRTC 要自造一套基础库，以及第 10 部分要拆开看的那些「零件」。
- [线程与任务队列 · Thread 其实是个 TaskQueue](https://daiw.org/manual/webrtc/thread-and-taskqueue.md): WebRTC 的并发模型建在 Thread 和 TaskQueueBase 上，而一个惊人的事实是——Thread 本身就继承自 TaskQueueBase。这一章读真实的 thread.h，讲清 PostTask/BlockingCall 怎么工作、为什么「线程就是任务队列」这个设计撑起了整个三线程模型。
- [引用计数 · scoped_refptr 与对象的生死](https://daiw.org/manual/webrtc/scoped-refptr.md): WebRTC 的对象在多线程间传来传去，谁负责销毁？答案是引用计数。这一章读 scoped_refptr.h / ref_counted_object.h，讲清 RefCountInterface、RefCountedObject、make_ref_counted、scoped_refptr 这套机制，以及它为什么是异步多线程代码的生命线。
- [类型安全的单位 · Timestamp、TimeDelta、DataRate](https://daiw.org/manual/webrtc/units.md): WebRTC 不用裸 int64 存时间和码率，而是用一整套类型安全的物理量：Timestamp、TimeDelta、DataRate、DataSize、Frequency。这一章读 units/ 目录，讲清这套 CRTP 单位体系怎么在编译期防止「毫秒当微秒用」的 bug，并顺带看它正在被移植到 Rust 的现状。
- [零拷贝缓冲 · Buffer 与 CopyOnWriteBuffer](https://daiw.org/manual/webrtc/buffers.md): 一个 RTP 包在系统里要流经打包、加密、pacing、发送好几站，每站都复制一份负载会很浪费。这一章读 buffer.h / copy_on_write_buffer.h，讲清 WebRTC 怎么用引用计数实现「写时复制」缓冲，让数据在多个环节间零拷贝地共享。
- [同步与线程注解 · Mutex、SequenceChecker、RTC_GUARDED_BY](https://daiw.org/manual/webrtc/synchronization.md): 读 WebRTC 源码满眼 RTC_GUARDED_BY、RTC_RUN_ON——这些不是注释，是编译器能检查的线程安全注解。这一章讲 Mutex/Event/SequenceChecker 这些同步原语，以及 WebRTC 那套「用线程归属代替锁、让编译器帮你抓竞争」的独特哲学。
- [速率统计 · RateStatistics 滑动窗口测速率](https://daiw.org/manual/webrtc/rate-statistics.md): 「当前码率是多少」这个问题在 WebRTC 里被问了无数次——pacer、带宽估计、统计报告都要。这一章读 rate_statistics.h，讲清 RateStatistics 这个滑动窗口速率估计器怎么工作，以及为什么「测速率」这么个小工具值得单独一个类。
- [第 10 部分回顾 · 地基如何塑造引擎](https://daiw.org/manual/webrtc/rtc-base-recap.md): 第 10 部分拆解了 rtc_base 的六样核心零件——线程/引用计数/单位/缓冲/同步/速率统计。这一章串起它们，点明一个贯穿始终的观念：地基的设计不是「底层细节」，而是直接决定了上层引擎能长成什么样。

### 第 11 部分 · 观测与调试

- [第 11 部分导论 · 看不见的通话怎么调试](https://daiw.org/manual/webrtc/observability-overview.md): WebRTC 的问题往往在线上、在别人的网络里、还无法复现——这让「可观测性」成了刚需。这一章讲清 WebRTC 可观测性的两大支柱：getStats（实时指标快照）与 RtcEventLog（事后逐事件回放），以及它们各自回答什么问题。
- [getStats 与 RTCStatsReport · 标准化的指标快照](https://daiw.org/manual/webrtc/stats-report.md): getStats 返回的 RTCStatsReport 是一份 W3C 标准化的指标快照——一堆带 id/type/timestamp 的 RTCStats 字典。这一章读 stats/ 源码，讲清这个数据模型、几个最有用的标准 stats 类型（inbound/outbound-rtp、candidate-pair、transport），以及怎么用它们读出通话质量。
- [统计的收集 · 跨线程汇集成一份报告](https://daiw.org/manual/webrtc/stats-collection.md): getStats 这一次调用看着简单，背后却要从多条线程上的多个子系统各取一部分指标、合并成一份报告，还要缓存以防频繁调用。这一章读 rtc_stats_collector.h，讲清 RTCStatsCollector 的异步收集、分线程产出、合并与缓存机制。
- [RtcEventLog · 通话的黑匣子](https://daiw.org/manual/webrtc/rtc-event-log.md): RtcEventLog 是 WebRTC 的黑匣子——把通话期间每个关键事件（RTP 头、RTCP、ICE、带宽估计变化）逐条记进一个二进制日志，事后能完整回放。这一章读 rtc_event_log.h，讲清它记什么、怎么开关、二进制格式为何不该当 protobuf 直接读。
- [可视化与排障 · 把黑匣子画成图](https://daiw.org/manual/webrtc/event-log-visualizer.md): 抓到的 event log 是二进制，得靠 event_log_visualizer 画成一堆时间序列图才看得懂。这一章讲这个官方工具怎么用（--plot/--list_plots）、能画出哪些关键曲线，以及一套「看图定位子系统」的实战排障方法论。
- [第 11 部分回顾 · 可观测的通话](https://daiw.org/manual/webrtc/observability-recap.md): 第 11 部分讲了两大观测支柱——getStats 快照与 RtcEventLog 黑匣子。这一章串起它们的配合闭环，并补上另外几件调试利器：RTC_LOG 日志、field trials 实验开关、metrics 直方图，以及 chrome：//webrtc-internals 一站式面板。

### 第 12 部分 · 平台集成与收尾

- [第 12 部分导论 · 一套核心，服务所有平台](https://daiw.org/manual/webrtc/platform-overview.md): 前十一部分讲的 C++ 引擎，怎么落到浏览器、Android、iOS、桌面？这一章讲清 WebRTC 的跨平台策略：api/ 目录是所有平台共享的原生基座，各平台在其上加一层语言绑定和平台相关的采集/渲染，核心引擎一份代码到处复用。
- [Android SDK · JNI 把 Java 接到 C++ 核心](https://daiw.org/manual/webrtc/android-sdk.md): Android 上的 WebRTC 是一层 JNI 桥：Java 的 PeerConnection 对象背后握着一个 native 指针，方法调用经 JNI 转到 C++ 核心。这一章读 android/ 的 native_api，讲清 JNI 桥的机制、scoped_java_ref/jvm 这些工具，以及跨语言时线程和引用管理的坑。
- [iOS SDK · Objective-C 封装 C++ 核心](https://daiw.org/manual/webrtc/ios-sdk.md): iOS/macOS 上的 WebRTC 是一层 Objective-C 封装：RTCPeerConnection 等 RTC 前缀类包着 C++ 的 api/ 对象。这一章读 objc/，讲清 ObjC++ 为什么让这层绑定比 Android 的 JNI 更顺滑，以及 ARC 和 C++ 引用计数怎么在边界上握手。
- [桌面采集 · DesktopCapturer 抓屏幕与窗口](https://daiw.org/manual/webrtc/desktop-capture.md): 屏幕共享的第一步是把屏幕/窗口的像素抓出来，这是 desktop_capture 模块的活。这一章读 desktop_capturer.h，讲清 DesktopCapturer 的拉取式采集模型（Start/CaptureFrame/回调）、屏幕与窗口的选择，以及跨平台后端和「只传变化区域」的优化。
- [摄像头采集 · VideoCaptureModule 与推送式帧源](https://daiw.org/manual/webrtc/video-capture.md): 摄像头是最常见的视频来源。这一章读 video_capture 模块的 VideoCaptureModule/Factory/DeviceInfo，讲清摄像头的推送式采集（和桌面拉取式的对比）、设备枚举与能力协商，以及采集帧怎么经 source→track 接入 PeerConnection。
- [构建 libwebrtc · depot_tools、GN 与 Ninja](https://daiw.org/manual/webrtc/building-webrtc.md): 把 libwebrtc 这个庞然大物编译出来本身就是一门学问——它用 Chromium 的构建体系：depot_tools 拉代码、GN 生成构建、Ninja 编译。这一章讲清这套工具链、为什么构建这么重（自带整套依赖与工具链的 hermetic 设计），以及实际的构建步骤。
- [全专栏回顾与结语 · 我们读完了 WebRTC](https://daiw.org/manual/webrtc/webrtc-recap.md): 十三个部分、一百多章，从「什么是 WebRTC」到「怎么构建它」，我们完整走过了 libwebrtc 的源码全景。这一章回顾整条主线，提炼 WebRTC 的本质矛盾与设计哲学，并给出继续深入的路径——为整个专栏画上句号。
