PeerConnectionFactory 与 ConnectionContext · 一切的起点
每个 PeerConnection 都由工厂造出。这一章读真实的 PeerConnectionFactoryDependencies 依赖注入结构,和被所有连接共享的 ConnectionContext——它持有三大线程、媒体引擎、网络与 SCTP 工厂,以引用计数保证「只要还有人用就活着」。这是整个会话建立的起点。
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(工厂 / 观察者模式、两级定制)。