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

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

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

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

会话协商谈出了 ICE 凭据、DTLS 指纹、每条 m-line 的编解码器。但这些还只是纸面约定。把它们变成真正的传输通道——建 ICE 连接、做 DTLS 握手、准备好收发 RTP——是 PeerConnection 的 transport_controller_ 成员,即 JsepTransportController 的职责。这一章读它,以及一个省资源的关键机制:BUNDLE。

承上启下:协商的下游

回忆驾驶舱那章,ApplyLocalDescription / ApplyRemoteDescription 会下推到传输层。下推的落点,就是 JsepTransportController——它也有一对对称的方法(pc/jsep_transport_controller.h):

// 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 线程上运作(传输是收发包的事)。

JsepTransport:ICE + DTLS + RTP 的捆绑

它为每组媒体建一个 JsepTransport(pc/jsep_transport.h)。这个对象,把一路传输要的三样东西捆在一起:

// 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 部分的 P2PTransportChannel,负责打通一条能过 NAT 的路。
  • DTLS 传输(中层)—— rtp_dtls_transport,在那条路上握手拿密钥(第 3 部分)。
  • RTP 传输(上层)—— rtp_transport,在加密通道上收发 RTP 包。

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

BUNDLE:多路媒体共用一条传输

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

BUNDLE 让一次通话里的多路媒体(音频、视频、数据)共用同一条底层传输——一个 ICE 连接、一次 DTLS 握手,而不是每路一套。 不开 BUNDLE 时,3 路媒体要建 3 条 ICE 连接、做 3 次 DTLS 握手、占 3 组端口——又慢又费。开了 BUNDLE,它们复用同一条 JsepTransport,靠 RTP 的 payload type / SSRC 在这一条传输上**多路复用(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 确认后即生效。

JsepTransportController 内部有个 bundle 管理逻辑,决定哪些 m-line 归到同一个 JsepTransport。协商变化时(比如 ICE 重启、增删媒体),它还要正确地重建或复用传输。

图表加载中…

小结与下一章

JsepTransportController(transport_controller_)把协商出的参数落成真正的传输:为每组媒体建一个 JsepTransport,捆绑 ICE + DTLS + RTP 三层(打通路 → 加密 → 收发)。BUNDLE 是关键优化——让多路媒体共用一条传输(一个 ICE、一次 DTLS,靠 payload/SSRC 多路复用),建连更快、穿透率更高,现代通话几乎必开。它多在 network 线程上工作。

至此,会话的媒体面从协商到传输落地,主线打通。还剩两块:数据通道(不是音视频,而是任意数据,走 SCTP)与统计收集(getStats 怎么从各处汇总)。下一章看数据通道的控制面。👉 DataChannelController 与数据通道。

参考资料

  • [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 规范。

本页目录