PeerConnectionFactory 与 ConnectionContext · 一切的起点

每个 PeerConnection 都由工厂造出。这一章读真实的 PeerConnectionFactoryDependencies 依赖注入结构,和被所有连接共享的 ConnectionContext——它持有三大线程、媒体引擎、网络与 SCTP 工厂,以引用计数保证「只要还有人用就活着」。这是整个会话建立的起点。

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

PeerConnectionFactory 与 ConnectionContext · 一切的起点

第 1 部分,我们潜入 pc/ 目录,看一次通话的控制面是怎么搭起来的。起点不是 PeerConnection 本身,而是造它的工厂,以及工厂背后那个被所有连接共享的上下文对象。理解这两者的依赖注入设计,你就明白了 WebRTC 为什么如此可定制。

工厂:造一次,用很多次

g3doc/peer_connection.md 说明,PeerConnection 走的是经典的工厂 + 观察者模式:

  • PeerConnectionFactory:用静态 Create 方法创建,吃一个 PeerConnectionFactoryDependencies(列出非默认的线程与各种工厂),供所有由它创建的连接共用。文档特别叮嘱:应避免创建多个工厂——它开销不小。
  • PeerConnection:由工厂的 CreatePeerConnectionOrError 方法造出,吃一个 RTCConfiguration 和一个 PeerConnectionDependencies。

一个工厂,多个连接。工厂是重的、共享的;连接是轻的、每通话一个。

依赖注入:几乎一切都能替换

WebRTC 极其可定制,核心手法就是 PeerConnectionFactoryDependencies 这个依赖注入结构。它真实地列在 api/peer_connection_interface.h 里,字段之多令人印象深刻:

// api/peer_connection_interface.h:PeerConnectionFactoryDependencies(节选)
struct PeerConnectionFactoryDependencies {
  Thread* network_thread = nullptr;      // 三大线程,不给就自己建
  Thread* worker_thread = nullptr;
  Thread* signaling_thread = nullptr;
  std::optional<Environment> env;
  std::unique_ptr<PacketSocketFactory> packet_socket_factory;
  std::unique_ptr<RtcEventLogFactoryInterface> event_log_factory;
  std::unique_ptr<FecControllerFactoryInterface> fec_controller_factory;
  std::unique_ptr<NetworkControllerFactoryInterface> network_controller_factory;
  std::unique_ptr<NetworkManager> network_manager;
  std::unique_ptr<NetEqFactory> neteq_factory;
  std::unique_ptr<SctpTransportFactoryInterface> sctp_factory;
  scoped_refptr<AudioDeviceModule> adm;
  scoped_refptr<AudioEncoderFactory> audio_encoder_factory;
  scoped_refptr<AudioDecoderFactory> audio_decoder_factory;
  // …还有视频编解码工厂、音频混音器、Metronome 等等
};

这个结构是 WebRTC 的「可替换点清单」。 几乎每个内部子系统,都能从这里换成你自己的实现:

  • 想换拥塞控制算法?给 network_controller_factory(第 5 部分)。
  • 想换音频抖动缓冲?给 neteq_factory(第 7 部分)。
  • 想换前向纠错?给 fec_controller_factory(第 4 部分)。
  • 想用自定义音频采集?给 adm(音频设备模块)。
  • 想支持某个编解码器?给 audio_encoder_factory / 视频编解码工厂(第 6/7 部分)。

这就是为什么 WebRTC 能被 Chrome、被各种 SFU、被无数原生 App 按需裁剪——它把「怎么做某件事」的决定权,通过依赖注入交给了使用者。三条线程也在这里传入:不给,库就自己创建(见线程模型)。

ConnectionContext:被共享的家底

工厂创建时,会把这些依赖收拢进一个对象——ConnectionContext(pc/connection_context.cc),然后把它传给每一个 PeerConnection。它是「工厂和所有连接共享的家底」。看它真实持有什么(pc/connection_context.h):

// pc/connection_context.h:成员(节选)
Thread* const network_thread_;                 // 三大线程
AlwaysValidPointer<Thread> const worker_thread_;
Thread* const signaling_thread_;
std::unique_ptr<MediaEngineInterface> media_engine_;      // 媒体引擎
std::unique_ptr<NetworkManager> default_network_manager_; // 网络管理
std::unique_ptr<PacketSocketFactory> default_socket_factory_;
std::unique_ptr<SctpTransportFactoryInterface> const sctp_factory_;  // SCTP 工厂

三大线程、媒体引擎(接音视频编解码,第 9 部分)、网络管理(枚举网卡地址,给 ICE 用)、套接字工厂、SCTP 工厂(第 8 部分)——一次通话要用的公共资源,全在这里。

引用计数:只要还有人用,就活着

ConnectionContext 的生命周期很讲究。g3doc/peer_connection.md 点明:

PeerConnectionFactory owns an object called ConnectionContext, and a reference to this is passed to each PeerConnection. It is referenced via an webrtc::scoped_refptr, which means that it is guaranteed to be alive as long as either the factory or one of the PeerConnections is using it.

也就是说,ConnectionContext 是引用计数的(第 10 部分会讲 scoped_refptr)。工厂持有一份引用,每个 PeerConnection 也各持一份。于是——哪怕你把工厂销毁了,只要还有一个 PeerConnection 活着,这份共享上下文就不会被回收。这解决了一个棘手的生命周期问题:共享资源不能因为某一个持有者(工厂)走了就消失。

图表加载中…

两级定制:工厂级 vs 连接级

最后厘清一个常被搞混的点。g3doc/index.md 说明 WebRTC 有两级定制:

级别通过作用范围
工厂级PeerConnectionFactoryDependencies所有由该工厂创建的连接,共用同一套
连接级RTCConfiguration + PeerConnectionDependencies只作用于单个 PeerConnection

工厂级定的是「用哪些子系统实现」(编解码器、拥塞控制算法……),一次定好、全局生效;连接级定的是「这一通电话怎么配」(ICE 服务器地址、加密要求、这个连接的观察者……)。造连接的 CreatePeerConnectionOrError 就吃后者:

// 每个连接:RTCConfiguration(ICE 服务器等)+ PeerConnectionDependencies(观察者等)
RTCErrorOr<scoped_refptr<PeerConnectionInterface>>
    CreatePeerConnectionOrError(const RTCConfiguration& configuration,
                                PeerConnectionDependencies dependencies);

PeerConnectionDependencies 里最重要的成员,就是前面见过的 PeerConnectionObserver——ICE 候选、连接状态、远端轨道的回调入口。

小结与下一章

一次通话从工厂起步:PeerConnectionFactory 造一次、复用多次,吃 PeerConnectionFactoryDependencies 这个依赖注入清单(三线程 + 几乎所有内部工厂都可替换——这是 WebRTC 高度可定制的根源)。工厂把依赖收进 ConnectionContext(三线程、媒体引擎、网络、SCTP 工厂),以引用计数共享给每个 PeerConnection,保证「只要还有人用就活着」。定制分工厂级(所有连接共用)与连接级(RTCConfiguration + PeerConnectionDependencies,单连接)。

工厂造出了 PeerConnection。下一章推开它的大门,看这个「上帝对象」内部到底由哪些下属组件构成、怎么初始化。👉 PeerConnection 这个上帝对象。

参考资料

  • [1] libwebrtc 源码.api/peer_connection_interface.h(PeerConnectionFactoryDependencies / CreatePeerConnectionOrError)。
  • [2] libwebrtc 源码.pc/connection_context.h / .cc(共享上下文的成员与引用计数)。
  • [3] libwebrtc 源码.g3doc/peer_connection.md、g3doc/index.md(工厂 / 观察者模式、两级定制)。

本页目录