# PeerConnectionFactory 与 ConnectionContext · 一切的起点

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

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

# 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` 里，字段之多令人印象深刻：

```cpp
// 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 等等
};
```

<Callout type="info">
  **这个结构是 WebRTC 的「可替换点清单」。** 几乎每个内部子系统，都能从这里换成你自己的实现：

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

  这就是为什么 WebRTC 能被 Chrome、被各种 SFU、被无数原生 App 按需裁剪——**它把「怎么做某件事」的决定权，通过依赖注入交给了使用者**。三条线程也在这里传入：**不给，库就自己创建**（见[线程模型](https://daiw.org/manual/webrtc/threading-model)）。
</Callout>

## ConnectionContext：被共享的家底

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

```cpp
// 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 部分](https://daiw.org/manual/webrtc/source-map)）、网络管理（枚举网卡地址，给 [ICE](https://daiw.org/manual/webrtc/source-map) 用）、套接字工厂、SCTP 工厂（[第 8 部分](https://daiw.org/manual/webrtc/source-map)）——一次通话要用的**公共资源**，全在这里。

## 引用计数：只要还有人用，就活着

`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 部分](https://daiw.org/manual/webrtc/reading-the-source)会讲 `scoped_refptr`）。工厂持有一份引用，每个 `PeerConnection` 也各持一份。于是——**哪怕你把工厂销毁了，只要还有一个 `PeerConnection` 活着，这份共享上下文就不会被回收**。这解决了一个棘手的生命周期问题：共享资源不能因为某一个持有者（工厂）走了就消失。

```mermaid
flowchart TD
    DEP["PeerConnectionFactoryDependencies<br/>(依赖注入:线程 + 各种工厂)"] --> F["PeerConnectionFactory"]
    F -->|拥有 + 引用| CTX["ConnectionContext<br/>(三线程·媒体引擎·网络·SCTP 工厂)"]
    F -->|"CreatePeerConnectionOrError()"| PC1["PeerConnection #1"]
    F -->|"CreatePeerConnectionOrError()"| PC2["PeerConnection #2"]
    PC1 -->|共享引用| CTX
    PC2 -->|共享引用| CTX
```

## 两级定制：工厂级 vs 连接级

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

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

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

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

`PeerConnectionDependencies` 里最重要的成员，就是[前面见过的](https://daiw.org/manual/webrtc/call-lifecycle) `PeerConnectionObserver`——ICE 候选、连接状态、远端轨道的回调入口。

## 小结与下一章

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

工厂造出了 `PeerConnection`。下一章推开它的大门，看这个「上帝对象」**内部到底由哪些下属组件构成、怎么初始化**。👉 [PeerConnection 这个上帝对象](https://daiw.org/manual/webrtc/peer-connection-object)。

## 参考资料

- [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`（工厂 / 观察者模式、两级定制）。
