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() 生成。
相关推荐
H3C-Navigator3 小时前
RPC在分布式存储系统中的应用
分布式·网络协议·rpc
DIY机器人工房3 小时前
NAT 模式、命令行版、桥接模式方式给ubuntu虚拟机配网步骤:
linux·网络协议·ubuntu·嵌入式·桥接模式·diy机器人工房
老坛程序员3 小时前
抓包解析MCP协议:基于JSON-RPC的MCP host与MCP server的交互
人工智能·网络协议·rpc·json·交互
wanhengidc4 小时前
云手机和网盘之间的关系
网络·游戏·智能手机·架构·云计算
歪歪1006 小时前
Http与WebSocket网络通信协议的对比
网络·websocket·网络协议·计算机网络·http·网络安全·信息与通信
ningmengjing_7 小时前
爬虫逆向——RPC技术
网络·网络协议·rpc
David WangYang7 小时前
便宜的自制 30 MHz - 6 GHz 矢量网络分析仪
开发语言·网络·php
njxiejing8 小时前
网桥(交换机)地址学习与转发流程案例分析(一文掌握)
网络
AORO20259 小时前
三防手机是什么?有哪些值得购入的三防手机?
网络·5g·安全·智能手机·信息与通信