# JsepTransportController 与 BUNDLE · 协商结果落到传输

> 协商谈定的 ICE 凭据、DTLS 参数，要真正建成传输通道——这是 JsepTransportController 的活。这一章读它怎么为每组媒体建一个 JsepTransport（捆绑 ICE + DTLS + RTP），以及 BUNDLE 如何让多路媒体共用一条传输，省下宝贵的连接与握手。

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

# JsepTransportController 与 BUNDLE · 协商结果落到传输

[会话协商](https://daiw.org/manual/webrtc/offer-answer-handler)谈出了 ICE 凭据、DTLS 指纹、每条 m-line 的编解码器。但这些还只是**纸面约定**。把它们**变成真正的传输通道**——建 ICE 连接、做 DTLS 握手、准备好收发 RTP——是 [`PeerConnection` 的 `transport_controller_` 成员](https://daiw.org/manual/webrtc/peer-connection-object)，即 **`JsepTransportController`** 的职责。这一章读它，以及一个省资源的关键机制：**BUNDLE**。

## 承上启下：协商的下游

回忆[驾驶舱那章](https://daiw.org/manual/webrtc/offer-answer-handler)，`ApplyLocalDescription` / `ApplyRemoteDescription` 会**下推**到传输层。下推的落点，就是 `JsepTransportController`——它也有一对对称的方法（`pc/jsep_transport_controller.h`）：

```cpp
// pc/jsep_transport_controller.h(节选)
RTCError SetLocalDescription(SdpType type, ...);
RTCError SetRemoteDescription(SdpType type, ...);
RtpTransportInternal* GetRtpTransport(absl::string_view mid) const;
```

`SdpOfferAnswerHandler` 的 `SetLocalDescription` 一路调到这里的 `SetLocalDescription`——**同名方法，不同层次**：上面那个管流程与状态机，这个管「把协商参数变成传输对象」。`JsepTransportController` 多在 [network 线程](https://daiw.org/manual/webrtc/threading-model)上运作（传输是收发包的事）。

## JsepTransport:ICE + DTLS + RTP 的捆绑

它为每组媒体建一个 **`JsepTransport`**（`pc/jsep_transport.h`）。这个对象，把一路传输要的三样东西**捆在一起**：

```cpp
// pc/jsep_transport.h:构造时就把三者绑定(节选)
JsepTransport(...,
    std::unique_ptr<RtpTransport> rtp_transport,        // RTP 传输(收发 RTP 包)
    scoped_refptr<DtlsTransport> rtp_dtls_transport,    // DTLS(加密,第 3 部分)
    ...);
// 内部还有 rtcp_dtls_transport(不 BUNDLE 时 RTCP 单独一条)
```

- **ICE 传输**（底层）—— [第 2 部分](https://daiw.org/manual/webrtc/source-map)的 `P2PTransportChannel`，负责**打通一条能过 NAT 的路**。
- **DTLS 传输**（中层）—— `rtp_dtls_transport`，在那条路上[握手拿密钥](https://daiw.org/manual/webrtc/source-map)（[第 3 部分](https://daiw.org/manual/webrtc/source-map)）。
- **RTP 传输**（上层）—— `rtp_transport`，在加密通道上收发 [RTP 包](https://daiw.org/manual/webrtc/source-map)。

三层叠成一条完整的、加密的媒体通路。`JsepTransportController` 就是这些 `JsepTransport` 的**总管**：根据协商结果创建、配置、拆除它们。

## BUNDLE：多路媒体共用一条传输

这是本章的重点，也是 WebRTC 一个至关重要的优化。

<Callout type="info">
  **BUNDLE 让一次通话里的多路媒体（音频、视频、数据）共用同一条底层传输——一个 ICE 连接、一次 DTLS 握手，而不是每路一套。** 不开 BUNDLE 时，3 路媒体要建 3 条 ICE 连接、做 3 次 DTLS 握手、占 3 组端口——又慢又费。开了 BUNDLE，它们**复用同一条 `JsepTransport`**，靠 [RTP 的 payload type / SSRC](https://daiw.org/manual/webrtc/source-map) 在这一条传输上**多路复用（demultiplex）**区分。

  这就是为什么 `GetRtpTransport(mid)` 的注释说「calling GetRtpTransport for multiple MIDs may yield the same object」——**多个 m-line（mid）映射到同一个 RTP 传输**，正是 BUNDLE 生效的标志。省下的不只是端口：少一次 ICE 连通性检查、少一次 DTLS 握手，通话**建连更快**、NAT 穿越**成功率更高**（只需打通一条路）。现代 WebRTC 几乎总是开 BUNDLE（`BundlePolicy`）。协商时，offer 用 `a=group:BUNDLE 0 1 2` 声明「这几条 m-line 想捆一起」，answer 确认后即生效。
</Callout>

`JsepTransportController` 内部有个 bundle 管理逻辑，决定哪些 m-line 归到同一个 `JsepTransport`。协商变化时（比如 [ICE 重启](https://daiw.org/manual/webrtc/source-map)、增删媒体），它还要正确地重建或复用传输。

```mermaid
flowchart TD
    subgraph NOBUNDLE["不开 BUNDLE:每路一套(费)"]
        A1["音频 m-line"] --> T1["JsepTransport A<br/>ICE+DTLS+RTP"]
        A2["视频 m-line"] --> T2["JsepTransport B<br/>ICE+DTLS+RTP"]
        A3["数据 m-line"] --> T3["JsepTransport C<br/>ICE+DTLS+RTP"]
    end
    subgraph BUNDLE["开 BUNDLE:共用一条(省)"]
        B1["音频 m-line"] --> TB["单个 JsepTransport<br/>ICE+DTLS+RTP<br/>(payload/SSRC 多路复用)"]
        B2["视频 m-line"] --> TB
        B3["数据 m-line"] --> TB
    end
```

## 小结与下一章

`JsepTransportController`（[`transport_controller_`](https://daiw.org/manual/webrtc/peer-connection-object)）把[协商](https://daiw.org/manual/webrtc/offer-answer-handler)出的参数**落成真正的传输**：为每组媒体建一个 **`JsepTransport`**，捆绑 **ICE + DTLS + RTP** 三层（打通路 → 加密 → 收发）。**BUNDLE** 是关键优化——让多路媒体**共用一条传输**（一个 ICE、一次 DTLS，靠 payload/SSRC 多路复用），建连更快、穿透率更高，现代通话几乎必开。它多在 network 线程上工作。

至此，会话的**媒体面**从协商到传输落地，主线打通。还剩两块：**数据通道**（不是音视频，而是任意数据，走 SCTP）与**统计收集**（getStats 怎么从各处汇总）。下一章看数据通道的控制面。👉 [DataChannelController 与数据通道](https://daiw.org/manual/webrtc/data-channel-controller)。

## 参考资料

- [1] libwebrtc 源码.`pc/jsep_transport_controller.h` / `.cc`（`SetLocalDescription`、`GetRtpTransport`、bundle 管理）。
- [2] libwebrtc 源码.`pc/jsep_transport.h`（`JsepTransport` 捆绑 RtpTransport / DtlsTransport）。
- [3] IETF RFC 9143「Negotiating Media Multiplexing Using SDP（BUNDLE）」—— BUNDLE 规范。
