媒体协商 · MediaSessionDescriptionFactory 怎么取编解码器交集

SDP 协商里最实质的一步,是「双方支持的编解码器取交集」。这活儿由 pc/media_session.cc 的 MediaSessionDescriptionFactory 干:CreateOfferOrError 列出我方能力,CreateAnswerOrError 挑出双方都支持的。这一章读它怎么把「能力清单」协商成「一致约定」。

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

媒体协商 · MediaSessionDescriptionFactory 怎么取编解码器交集

上一章的 SdpOfferAnswerHandler 是协商的驾驶舱,但它把「具体怎么生成一段媒体描述、怎么和对端的能力取交集」这件脏活,委托给了另一个组件——pc/media_session.cc 里的 MediaSessionDescriptionFactory。这一章看协商最实质的一步:编解码器怎么谈拢。

分工:驾驶舱调度,工厂造描述

先厘清上一章和这一章的关系:

  • SdpOfferAnswerHandler(pc/sdp_offer_answer.cc)—— 管流程与状态机:什么时候该生成 offer、应用描述后推进到哪个状态、下推给谁。
  • MediaSessionDescriptionFactory(pc/media_session.cc)—— 管内容:把「我方有哪些轨道、支持哪些编解码器」这份能力,构造成一个结构化的 SessionDescription(那个内部的、拆开信封的版本)。

它的两个核心方法,和 JSEP 的提议 / 应答严丝合缝(pc/media_session.h):

// pc/media_session.h:MediaSessionDescriptionFactory(节选)
RTCErrorOr<std::unique_ptr<SessionDescription>> CreateOfferOrError(
    const MediaSessionOptions& options, const SessionDescription* current_desc);
RTCErrorOr<std::unique_ptr<SessionDescription>> CreateAnswerOrError(
    const SessionDescription* offer, const MediaSessionOptions& options,
    const SessionDescription* current_desc);

CreateOfferOrError 生成 offer(「我能做这些」),CreateAnswerOrError 吃着对端的 offer 生成 answer(「这些里我也支持的,是这些」)。二者都吃一个 MediaSessionOptions(每条 m-line 想怎么配),吐一个 SessionDescription。

Offer:摊开我方全部能力

生成 offer 时,逻辑是「把我方支持的都列出来」。对每条要协商的媒体(一路音频、一路视频……),工厂生成一条 m-line,里面列上我方支持的所有编解码器——比如音频列 Opus、G.711;视频列 VP8、VP9、AV1、H264。每个编解码器分配一个 payload type(SDP 里 a=rtpmap 那个数字)。offer 是「能力的完整清单」:我全都能,你挑吧。

Answer:取交集,这是协商的实质

生成 answer 才是协商真正发生的地方。工厂拿着对端的 offer,对每条 m-line 做一件事——求交集:

协商编解码器 = 求两方能力的交集。 生成 answer 时,MediaSessionDescriptionFactory 对每条媒体,把「对端 offer 里列的编解码器」和「我方支持的编解码器」取交集,只保留双方都有的。源码里这一步由 GetNegotiatedCodecsForAnswer(pc/media_session.cc 通过一个 codec vendor 辅助对象)完成。

举例:对端 offer 视频列了 [VP8, VP9, H264],我方支持 [VP8, AV1],交集就是 [VP8]——这通电话的视频最终用 VP8。如果交集为空(一个共同编解码器都没有),这条媒体就没法协商,会被拒绝(端口设为 0)。这就是为什么两个 WebRTC 端点总能通:只要有一个共同编解码器,交集非空,协商就成。

交集之外,还要对齐payload type(双方对同一编解码器可能用了不同的数字,answer 要采纳 offer 的编号)、协商 RTP 头扩展(第 4 部分)、RTCP 反馈类型(支不支持 NACK、Transport-CC……)等一系列细节。每一样都是「取双方都支持的」。

不止编解码器:一条 m-line 要谈的东西

一条媒体行协商的,远不止编解码器。工厂在构造每条 media content 时,还要定下(第 2、3 部分会展开):

要素谈什么归哪部分
编解码器取交集 + 对齐 payload type本章
方向sendrecv / sendonly / recvonly / inactive收发器
RTP 头扩展双方都支持的扩展及其 ID第 4 部分
RTCP 反馈NACK / PLI / Transport-CC 支不支持第 4/5 部分
ICE 凭据ufrag / pwd第 2 部分
DTLS 指纹与角色fingerprint / setup第 3 部分
BUNDLE这些 m-line 能否共用一条传输传输控制器

MediaSessionDescriptionFactory 就是把这一大堆,逐条 m-line 地谈拢、写进 SessionDescription。难怪 SdpOfferAnswerHandler 要把这活儿单独委托出去。

一张流程图

图表加载中…

小结与下一章

媒体协商的实质,是求两方能力的交集,由 pc/media_session.cc 的 MediaSessionDescriptionFactory 完成:CreateOfferOrError 摊开我方全部能力,CreateAnswerOrError 吃对端 offer、逐条 m-line 取交集(编解码器用 GetNegotiatedCodecsForAnswer,还要对齐 payload type、谈头扩展 / RTCP 反馈 / ICE / DTLS / BUNDLE)。交集非空,这路媒体就能通。SdpOfferAnswerHandler 调度流程,它负责造内容。

协商结果里的「方向」(sendrecv 等)和「这一路媒体归谁收发」,落在一个核心抽象上——收发器 RtpTransceiver。它是 Unified Plan 的基石。下一章读它。👉 收发器:RtpTransceiver 与 Unified Plan。

参考资料

  • [1] libwebrtc 源码.pc/media_session.h / .cc(MediaSessionDescriptionFactory、CreateOfferOrError / CreateAnswerOrError、GetNegotiatedCodecsForAnswer)。
  • [2] libwebrtc 源码.pc/session_description.h(SessionDescription 与 MediaContentDescription)。
  • [3] IETF RFC 8829「JSEP」§ 编解码器协商;RFC 3264「An Offer/Answer Model with SDP」。

本页目录