前言:
总结发送端 SSRC(Synchronization Source Identifier)的设置流程。
SSRC 是 RTP/RTCP 包的关键标识,通常在 SDP 协商或配置阶段生成或设置,如果未指定则随机生成(非零 32 位值)。流程从 SDP 内容更新开始,到 RTP 发送模块初始化结束。报告具体到每个接口,包括调用关系、代码分析。
看不懂的朋友也可以跟着了解下发送Stream创建流程,特别懂的朋友也可以跟着看我有没有胡说八道,咱们都吃不了亏,上不了当,闲言少叙,走起!!!
1. 流程概述
发送端 SSRC 设置主要发生在 SDP 协商阶段(本地 offer/answer 处理)和 RTP 模块初始化阶段。核心逻辑:
- 如果 SDP 中 StreamParams (sp) 已指定 SSRC,则直接使用。
- 如果未指定(e.g., 使用 RIDs 而非 SSRCs),则在 UpdateLocalStreams_w 中调用 GenerateSsrcs 生成随机 SSRC。
- 生成的 SSRC 传递到下层(如 RtpVideoSender、VideoSendStreamImpl),最终设置到 RTPSender 和 RTCPSender 中。
- 数据流向:SDP 更新 → 通道配置 → 流添加 → RTP 配置 → 发送模块初始化。
- 设计原则:确保 SSRC 唯一、非零,支持 simulcast(多 SSRC)和 RID(无 SSRC 协商)。
2. 详细接口流程
以下按调用顺序描述每个接口,包括输入/输出、代码片段和分析。流程从 SDP 处理开始。
2.1 接口:VideoChannel::SetLocalContent_w
-
描述:处理本地 SDP 视频内容描述,更新接收/发送参数,并调用 UpdateLocalStreams_w 设置本地流(包括 SSRC)。
-
调用关系:上层 SDP 协商(如 PeerConnection)调用此接口处理本地 offer/answer。
-
输入:MediaContentDescription* content(SDP 视频描述,包括 streams),SdpType type(offer/answer),error_desc。
-
输出:bool(成功/失败)。
-
SSRC 设置逻辑:不直接生成 SSRC,但调用 UpdateLocalStreams_w 处理 streams。如果 streams 无 SSRC,则在下层生成。
-
核心代码分析 :
cppbool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, SdpType type, std::string* error_desc) { // ... 提取 video 描述,更新 recv/send params if (!UpdateLocalStreams_w(video->streams(), type, error_desc)) { // 调用 UpdateLocalStreams_w SafeSetError("Failed to set local video description streams.", error_desc); return false; } // ... 更新方向和状态 return true; }
- 分析:如果 streams (video->streams()) 无 SSRC 或 RIDs,则 UpdateLocalStreams_w 会生成。type=answer 时,检查 codec 兼容性,但不影响 SSRC 生成。
2.2 接口:BaseChannel::UpdateLocalStreams_w
-
描述:更新本地发送流列表,包括添加/移除流。如果流无 SSRC,则调用 GenerateSsrcs 生成。
-
调用关系:由 SetLocalContent_w 调用。
-
输入:std::vector streams(SDP 中的流参数,可能无 SSRC),SdpType type,error_desc。
-
输出:bool(成功/失败)。
-
SSRC 设置逻辑:遍历 streams,如果新流无 SSRC/RIDs,则跳过;否则调用 GenerateSsrcs 生成 SSRC(基于 RIDs 数量,支持 RTX/FEC)。然后调用 media_channel()->AddSendStream 添加流。
-
核心代码分析 :
cppbool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams, SdpType type, std::string* error_desc) { // ... 检查移除旧流 std::vector<StreamParams> all_streams; for (const StreamParams& stream : streams) { StreamParams* existing = GetStream(local_streams_, StreamFinder(&stream)); if (existing) { all_streams.push_back(*existing); continue; } all_streams.push_back(stream); StreamParams& new_stream = all_streams.back(); if (!new_stream.has_ssrcs() && !new_stream.has_rids()) continue; // 无 SSRC/RIDs 跳过 RTC_DCHECK(new_stream.has_ssrcs() || new_stream.has_rids()); if (new_stream.has_ssrcs() && new_stream.has_rids()) { // 不能同时有 // ... 错误处理 } // 生成 SSRC if (!new_stream.has_ssrcs()) { new_stream.GenerateSsrcs(new_stream.rids().size(), /* rtx = */ true, /* flex_fec = */ false, ssrc_generator_); // 调用 GenerateSsrcs } // 添加流 if (media_channel()->AddSendStream(new_stream)) { RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.ssrcs[0]; } else { // ... 错误处理 } } local_streams_ = all_streams; return true; }
- 分析:GenerateSsrcs(rids.size(), true, false, ssrc_generator_) 生成主 SSRC + RTX SSRC(如果启用)。ssrc_generator_ 是 SsrcGenerator 实例,确保唯一随机 SSRC。如果 SDP 已指定 SSRC,则直接使用。
2.3 接口:WebRtcVideoChannel::AddSendStream
-
描述:添加发送流,创建 WebRtcVideoSendStream,并调用 Call::CreateVideoSendStream 生成 VideoSendStream。
-
调用关系:由 UpdateLocalStreams_w 调用 media_channel()->AddSendStream(new_stream)。
-
输入:StreamParams& sp(包含 SSRCs)。
-
输出:bool(成功/失败)。
-
SSRC 设置逻辑:使用 sp 中的 SSRC 创建 config.rtp.ssrcs,然后调用 CreateVideoSendStream。
-
核心代码分析 :
cppbool WebRtcVideoChannel::AddSendStream(const StreamParams& sp) { // ... 验证 sp WebRtcVideoSendStream* stream = new WebRtcVideoSendStream( call_, sp, std::move(config), default_send_options_, video_config_.enable_cpu_adaptation, bitrate_config_.max_bitrate_bps, send_codec_, send_rtp_extensions_, send_params_); uint32_t ssrc = sp.first_ssrc(); // 使用 sp.first_ssrc() send_streams_[ssrc] = stream; // 存储到 send_streams_ if (sending_) stream->SetSend(true); // 如果已发送,启用 return true; }
- 分析:sp.ssrcs 来自上层生成。stream 持有 SSRC,用于 RTP/RTCP。
2.4 接口:Call::CreateVideoSendStream
-
描述:创建 VideoSendStream,设置 config.rtp.ssrcs,并调用 RtpVideoSender 构造函数。
-
调用关系:由 AddSendStream 调用。
-
输入:VideoSendStream::Config config(包含 rtp.ssrcs),VideoEncoderConfig 等。
-
输出:VideoSendStream*。
-
SSRC 设置逻辑:直接使用 config.rtp.ssrcs(来自 sp.ssrcs)。
-
核心代码分析 :
cppVideoSendStream* Call::CreateVideoSendStream(webrtc::VideoSendStream::Config config, VideoEncoderConfig encoder_config, std::unique_ptr<FecController> fec_controller) { // ... 记录 SSRCs for (size_t ssrc_index = 0; ssrc_index < config.rtp.ssrcs.size(); ++ssrc_index) { // ... 事件日志 } // 创建 VideoSendStream VideoSendStream* send_stream = new VideoSendStream(/*...*/, config.rtp.ssrcs, std::move(config), std::move(encoder_config), /*...*/); // ... 添加到 video_send_ssrcs_ 和 video_send_streams_ return send_stream; }
- 分析:config.rtp.ssrcs 来自上层传入,已生成。send_stream 持有 SSRC,用于内部模块。
2.5 接口:VideoSendStream::VideoSendStream
-
描述:创建 VideoSendStreamImpl,并调用 RtpVideoSender::RtpVideoSender。
-
调用关系:由 CreateVideoSendStream 调用。
-
输入:Config config(包含 rtp.ssrcs),EncoderConfig 等。
-
输出:无(构造函数)。
-
SSRC 设置逻辑:使用 config.rtp.ssrcs 传入 RtpVideoSender。
-
核心代码分析 :
cppVideoSendStream::VideoSendStream(/*...*/, VideoSendStream::Config config, /*...*/) : // ... 初始化 rtp_video_sender_(transport_->CreateRtpVideoSender( suspended_ssrcs, suspended_payload_states, config.rtp, /*...*/)) { // 传入 config.rtp // ... 配置 }
- 分析:rtp_video_sender_ 持有 SSRC,用于 RTP 发送。
2.6 接口:RtpVideoSender::RtpVideoSender
-
描述:创建 RtpStreamSenders,并调用 RtpRtcp::Configuration 设置 ssrc。
-
调用关系:由 VideoSendStream 构造函数调用。
-
输入:Config rtp_config(包含 rtp.ssrcs)。
-
输出:无(构造函数)。
-
SSRC 设置逻辑:使用 rtp_config.ssrcs 设置 configuration.local_media_ssrc。
-
核心代码分析 :
cppRtpVideoSender::RtpVideoSender(/*...*/, const RtpConfig& rtp_config, /*...*/) : // ... 初始化 rtp_streams_(CreateRtpStreamSenders(clock, rtp_config, /*...*/)) { // 传入 rtp_config // ... 配置 }
- 分析:CreateRtpStreamSenders 遍历 rtp_config.ssrcs,创建 RtpRtcp 模块,每个模块的 configuration.local_media_ssrc = ssrc。
2.7 接口:CreateRtpStreamSenders (RtpVideoSender 内)
-
描述:创建多个 RtpStreamSender,每个对应一个 SSRC。
-
调用关系:由 RtpVideoSender 构造函数调用。
-
输入:RtpConfig& rtp_config(包含 ssrcs)。
-
输出:std::vector。
-
SSRC 设置逻辑:为每个 ssrc 创建 RtpRtcp 模块,设置 configuration.local_media_ssrc = ssrc。
-
核心代码分析 :
cpp// 推断代码(基于片段) std::vector<RtpStreamSender> CreateRtpStreamSenders(/*...*/, const RtpConfig& rtp_config, /*...*/) { std::vector<RtpStreamSender> streams; for (uint32_t ssrc : rtp_config.ssrcs) { RtpRtcp::Configuration configuration; configuration.local_media_ssrc = ssrc; // 设置本地 SSRC // ... 创建 RtpRtcp 模块 streams.emplace_back(std::make_unique<RtpRtcp>(configuration), /*...*/); } return streams; }
- 分析:这是最终设置点。SSRC 传入 RtpRtcp 模块,用于 RTP/RTCP 包头。
3. 总结
- 问题:如果 SDP 无 SSRC/RIDs,GenerateSsrcs 使用 ssrc_generator_ 生成随机值,确保唯一。
- 建议:在 SDP 协商中优先使用协商 SSRC;测试时检查 config.rtp.ssrcs 非零。
- 参考:WebRTC 官方 SsrcGeneratorImpl 使用 rtc::CreateRandomNonZeroId() 生成。