WebRTC 发送端 SSRC 生成流程总结

前言:

总结发送端 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,则在下层生成。

  • 核心代码分析

    cpp 复制代码
    bool 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 添加流。

  • 核心代码分析

    cpp 复制代码
    bool 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。

  • 核心代码分析

    cpp 复制代码
    bool 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)。

  • 核心代码分析

    cpp 复制代码
    VideoSendStream* 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。

  • 核心代码分析

    cpp 复制代码
    VideoSendStream::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。

  • 核心代码分析

    cpp 复制代码
    RtpVideoSender::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() 生成。
相关推荐
揽秀亭长11 小时前
做视频时如何快速完成视频转文字?几种方法实测
人工智能·音视频
Shadow(⊙o⊙)11 小时前
Linux网络——文件与网络的连接桥梁struct sock {
linux·运维·网络
李永奉11 小时前
中科蓝讯SDK开发-提示音音频文件格式转换和压缩教程
网络·单片机·嵌入式硬件·mcu·物联网
sunoo-22912 小时前
【网络编程 + 数据库】select 与 epoll 核心区别详解 + SQLite 入门到 C 接口全攻略
linux·网络·数据库·vscode·学习·sqlite
长脖鹿Johnny12 小时前
游戏输入系统框架设计(三):网络输入权威与可验证性
网络·游戏·游戏开发·架构设计·输入系统·网络同步
可乐鸡翅yeah_12 小时前
hls.js 多实例并发踩坑,多视频页面播放器内存与冲突问题解决
开发语言·前端·javascript·音视频·hls·m3u8·m3u8在线播放
AI服务老曹12 小时前
算法任务配置完整流程:明厨亮灶项目从0到1怎么做 | AI视频分析算法实践
人工智能·算法·音视频
AI创界者12 小时前
【开源实战】FaceFusionFree 5.3 部署与进阶:修复内存模式条纹 Bug 与帧率对齐逻辑解析
人工智能·aigc·音视频
Mortalbreeze12 小时前
深入理解 Linux IO 模型(二):多路复用——select
linux·运维·服务器·网络·tcp/ip
声浪数字12 小时前
Vlog、测评、剧情、混剪:四类视频的音效网站各去哪找
音视频