怎么读这套源码 · 构建、约定与按图索骥

导论收尾,给一套实用的「读码方法论」:怎么拿到并构建(其实你不必构建)、header guard 与线程注解等路标、.h/.cc/_unittest.cc/BUILD.gn/g3doc 的分工、abseil 与引用计数等风格约定,以及怎么对照本教程按图索骥地读下去。

作者 David更新于 第 6 篇(共 112 篇)

怎么读这套源码 · 构建、约定与按图索骥

导论的最后一章,不讲架构,讲方法:面对 5000 多个文件,怎么高效地读进去、怎么跟着本教程按图索骥。掌握这套约定,后面 100 多篇你都能对着真实源码逐行印证。

拿到源码(你其实不必构建)

WebRTC 用 Google 的 depot_tools 拉取,gclient 管理依赖:

fetch --nohooks webrtc      # 拉取源码(很大,含第三方依赖)
gclient sync                # 同步依赖

构建用 GN + Ninja(根目录有 .gn 与 BUILD.gn 为证):

gn gen out/Default          # GN 生成 Ninja 构建文件
ninja -C out/Default        # Ninja 编译

好消息:跟本教程,你完全不必构建 WebRTC。 它的完整构建很重(拉取几个 GB、编译半天)。我们的目的是读懂它怎么设计与实现,而读源码只需要一份源码树 + 一个能跳转的编辑器 / 代码搜索。想动手实验的读者再去构建不迟;只想读懂的,把源码摊开对照即可。

三个路标(复习)

读 WebRTC,先认这三个反复出现的路标——它们是前几章埋下的:

  1. header guard = canonical 路径:#ifndef MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_ 直接告诉你这文件的规范路径。本教程的路径都按此写。
  2. 线程注解 = 谁在哪跑:RTC_DCHECK_RUN_ON(线程) 和 RTC_GUARDED_BY(线程) 一眼标出线程归属。读一个函数前先扫一眼它的注解。
  3. 命名 = 角色:XxxInterface(契约,在 api/)/ XxxProxy(转发线程)/ Xxx(实现);还有 XxxFactory(工厂)、XxxObserver(回调)。看名字就知道它在架构里的位置。

五种文件,各司其职

一个模块目录里,文件按后缀分工清晰:

文件是什么怎么用
xxx.h接口 / 声明想知道「这个类能干什么」,读它
xxx.cc实现想知道「具体怎么干」,读它
xxx_unittest.cc单元测试(全库 900+ 个)想知道「怎么用、边界行为」,读它
BUILD.gn构建目标与依赖(全库 140+ 个)想知道「这模块依赖谁、被谁用」,读它
g3doc/*.md架构文档(十多个模块自带)先读它,再读代码

两个高价值、却常被忽略的读码入口:单元测试与 g3doc。

  • 单元测试(_unittest.cc)是最好的「用法说明书」。全库有 900 多个。一个类怎么构造、怎么调、在边界情况下期望什么行为——测试里写得明明白白,还保证是最新、可运行的。读不懂一个类的契约时,翻它的测试往往比读实现更快。
  • 模块自带的 g3doc/ 是最好的「设计导览」。modules/audio_coding/neteq/g3doc/、modules/rtp_rtcp/g3doc/、p2p/、pacing/…… 十多个核心模块自带架构文档。它们由模块作者维护,讲清「为什么这么设计」。读一个新模块,先找它的 g3doc——本教程也大量以它们为纲。

几条风格约定

WebRTC 有自己的 C++ 风格(style-guide.md、abseil-in-webrtc.md),读代码前知道几条能少踩坑:

  • 不用 C++ 异常。错误靠返回值传递——你会常见 RTCError、RTCErrorOr<T>(要么带值、要么带错误)。断言用 RTC_CHECK(release 也检查、失败即崩)和 RTC_DCHECK(仅 debug 检查)。
  • 引用计数管理生命周期。跨线程共享的对象多是引用计数的,用 scoped_refptr<T> 持有(类似 shared_ptr)。g3doc/peer_connection.md 里 ConnectionContext 就是「只要工厂或任一 PeerConnection 还在用,它就活着」——这正是引用计数的语义。
  • 精选的 abseil 工具。abseil-in-webrtc.md 列出允许用的 absl 类型,如 absl::AnyInvocable(可移动的函数包装)、absl::InlinedVector(小尺寸内联的 vector)、absl::Cleanup(作用域退出时执行)等。见到 absl:: 前缀不必慌,它们都是标准库的增强版。
  • api/ 是稳定契约,其余可变。跨版本读代码时,api/ 的接口相对稳定,实现细节则常重构——对照本教程时若发现某实现类改了名,先从 api/ 的接口和 g3doc 找回主线。

怎么对照本教程读

给一套可复用的「读码配方」。本教程每讲一个组件,都会给出它的 canonical 路径 + 类名 + 关键函数。拿到后:

  1. 先读 g3doc(若该模块有)——把握设计意图与全景。
  2. 再读 .h——看接口:这个类对外承诺了什么,成员上的线程注解说明它活在哪条线程。
  3. 然后读 .cc 的关键函数——本教程会点名是哪几个,顺着数据流读,别一头扎进细节。
  4. 卡壳了翻 _unittest.cc——看它怎么被构造和调用,契约立刻清晰。

源码可在 webrtc.googlesource.com/src/ 浏览,或用 Chromium 代码搜索 source.chromium.org(路径前缀 third_party/webrtc/)——支持全局跳转,读 WebRTC 极其顺手。

小结与下一部分

读 WebRTC 的方法论:不必构建(读懂即可);认准三个路标(header guard = 路径、线程注解 = 归属、命名 = 角色);用好五种文件(.h 看接口、.cc 看实现、_unittest.cc 看用法、BUILD.gn 看依赖、g3doc 看设计);记住几条风格(无异常、RTCErrorOr、scoped_refptr 引用计数、精选 abseil)。对照本教程时,按「g3doc → .h → .cc 关键函数 → 单测」的顺序读。

导论与全景到此结束——你已经有了 WebRTC 的全局观、源码地图、通话流程、线程模型与代理机制,以及这套读码方法论。接下来,我们正式潜入第一个子系统——PeerConnection 与会话建立:从工厂造出连接,到 SDP 提议 / 应答如何把一次通话协商出来。(第 1 部分陆续更新。)

参考资料

  • [1] libwebrtc 源码.style-guide.md、abseil-in-webrtc.md、native-api.md(编码风格与 API 约定)。
  • [2] libwebrtc 源码.各模块 g3doc/ 与 900+ _unittest.cc(设计文档与用法示例)。
  • [3] WebRTC 项目.「Getting Started / depot_tools / GN build」https://webrtc.googlesource.com/src/+/main/docs/native-code/

本页目录