PeerConnection深度解析一:CreateOffer
源码路径:
api/peer_connection_interface.h
pc/peer_connection.cc
pc/sdp_offer_answer.cc
pc/webrtc_session_description_factory.hhttps://chromium.googlesource.com/external/webrtc main 分支
对应规范:W3C WebRTC 1.0 §4.4.1
createOffer 在整个信令流程中有什么作用或者说为什么需要调用这个函数,刚开始了解 webrtc的人都会有这个疑问?
想象你要发起一场视频会议,CreateOffer 就是起草一份会议邀请函的过程。
这份"邀请函"里写了什么
我支持的视频格式:H.264、VP8、VP9我支持的音频格式:Opus、G711
我的网络入口地址:(等 ICE 收集)
我的身份证明:(DTLS 证书指纹)
我们用同一条通道传音视频吗:是(BUNDLE)
我打算:既发送也接收(sendrecv)
这就是 SDP Offer 的本质——一份本端能力的自我声明。
简单来说CreateOffer 干了四件事:
排队, 确保不和其他信令操作撞车
收集能力, 遍历所有 Track/Transceiver我要发什么?收什么?
等证书, DTLS 证书没好就排队 证书好了才能写入指纹
拼SDP, 把所有信息组装成文本通过 OnSuccess 回调返回
下面我们一起详细了解下CreateOffer。
1、W3C 规范怎么定义 CreateOffer
W3C 规范对 createOffer() 的定义非常简洁:
生成一个 SDP Offer,包含本端支持的媒体能力配置,用于发起一次新的 WebRTC 会话,或者修改一个已建立的会话。
但"简洁"的背后,规范要求实现方必须处理相当多的细节:
收集当前所有
RtpTransceiver的媒体描述根据
RTCOfferAnswerOptions决定 m= section 的方向携带本端的 ICE 凭据(ufrag / pwd)
携带 DTLS 证书指纹(
a=fingerprint)处理 ICE Restart 逻辑
整个操作必须串行化,不能与其他信令操作并发
这些细节,在 libwebrtc 里都有对应的实现,我们一层一层来看。
2、接口签名
// api/peer_connection_interface.h // See: https://www.w3.org/TR/webrtc/#idl-def-rtcofferansweroptions // Create a new offer. // The CreateSessionDescriptionObserver callback will be called when done. virtual void CreateOffer(CreateSessionDescriptionObserver* observer, const RTCOfferAnswerOptions& options) = 0;两个参数,设计非常清晰:
① CreateSessionDescriptionObserver
结果通过 Observer 异步回调,而不是直接返回。 原因是 SDP 生成可能涉及异步操作(最典型的是 DTLS 证书的异步生成),不能同步返回。回调只有两个接口:
// api/jsep.h class CreateSessionDescriptionObserver { // 成功:拿到 SDP,下一步调用 SetLocalDescription virtual void OnSuccess(SessionDescriptionInterface* desc) = 0; // 失败:收到错误原因 virtual void OnFailure(RTCError error) = 0; };② RTCOfferAnswerOptions 控制 SDP 生成行为的参数集:
// api/peer_connection_interface.h // See: https://www.w3.org/TR/webrtc/#idl-def-rtcofferansweroptions struct RTCOfferAnswerOptions { // Plan B 遗留选项:是否在 Offer 中包含音/视频接收方向 // Unified Plan 用户应使用 AddTransceiver,不应设置这两个字段 int offer_to_receive_video = kUndefined; int offer_to_receive_audio = kUndefined; // 是否开启语音活动检测(VAD) bool voice_activity_detection = true; // 是否强制 ICE Restart(重新生成 ICE 凭据) bool ice_restart = false; // 是否开启 BUNDLE(音视频复用同一传输通道,推荐 true) bool use_rtp_mux = true; };深度细节:offer_to_receive_audio/video 是 Plan B 语义的遗留字段。 在 Unified Plan 下,媒体方向由 RtpTransceiver::direction 控制, 这两个字段会触发 HandleLegacyOfferOptions 做兼容处理, 内部实际上是在操作 Transceiver 的 direction,而不是直接写 SDP。
3、调用链:一次 CreateOffer 经历了什么
完整调用链如下:
应用层调用 pc->CreateOffer(observer, options) │ ▼ [Signaling Thread] PeerConnection::CreateOffer() │ 直接转发给 sdp_handler_ ▼ SdpOfferAnswerHandler::CreateOffer() │ 封装进 OperationsChain 串行队列 ▼ SdpOfferAnswerHandler::DoCreateOffer() │ 前置校验 → GetOptionsForOffer() ▼ WebRtcSessionDescriptionFactory::CreateOffer() │ 等待 DTLS 证书就绪(可能异步) ▼ MediaSessionDescriptionFactory::CreateOffer() │ 真正生成 cricket::SessionDescription ▼ observer->OnSuccess(SessionDescriptionInterface*)4、第一层:PeerConnection 调用CreateOffer
// pc/peer_connection.cc void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer, const RTCOfferAnswerOptions& options) { RTC_DCHECK_RUN_ON(signaling_thread()); sdp_handler_->CreateOffer(observer, options); }PeerConnection::CreateOffer 本身什么都不做, 只是做了一个线程断言(必须在 signaling_thread 上调用), 然后把工作全部委托给 SdpOfferAn
