WebRTC 码率预估(1) - 接收端 TransportFeedback 生成和发送流程指南

概述

在 WebRTC 中,Transport-CC (TCC) 机制用于发送端带宽估计 (Sender-Side BWE)。接收端负责收集 RTP 包的到达信息(如序列号和时间戳),生成 RTCP TransportFeedback 报文,并发送回发送端,帮助发送端计算网络拥塞、带宽和延迟。

本指南基于提供的代码片段(包括 rtp_stream_receiver_controller.cc、video_receive_stream.cc、rtp_video_stream_receiver.cc、receive_side_congestion_controller.cc、remote_estimator_proxy.cc、rtcp_sender.cc、call.cc、rtp_header_extensions.cc、rtp_packet_received.cc、rtp_sender_egress.cc、rtp_sender.cc 等)梳理接收端从收到 RTP 包到生成并发送 TransportFeedback 的完整流程。流程假设 TCC 已启用(RTP 包携带 Transport Sequence Number 扩展头),并聚焦于接收端的关键模块。

适用场景:视频接收流(如 H.264 RTP 包),使用 sender-side BWE。REMB(接收端 BWE)流程不在此指南中详细展开。

关键概念:

  • TransportFeedback:RTCP 报文,包含接收包的序列号、到达时间 delta 和丢失信息。
  • 触发方式:请求触发(发送端请求)或周期触发(默认每 25ms)。
  • 数据结构 :packet_arrival_times_(map<int64_t, int64_t>)记录 unwrap 后的序列号和到达时间。

RTP 扩展头启用:

  • 在发送端:通过 RtpHeaderExtensionMap 注册扩展(如 rtp_header_extension_map_.Register<TransportSequenceNumber>(kRtpExtensionTransportSequenceNumber) 在 rtp_sender.cc 中的 RTPSender 构造函数中)。
  • 在接收端:RtpPacketReceived 构造函数中传入 ExtensionManager(继承自 RtpPacket),用于解析扩展(如 IdentifyExtensions 在 rtp_packet_received.cc 中)。扩展 URI 和 ID 定义在 rtp_header_extensions.h/cc(e.g., TransportSequenceNumber::kId = kRtpExtensionTransportSequenceNumber)。TCC 依赖 TransportSequenceNumber 扩展(URI: "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"),在配置中启用 sender-side BWE 时自动注册。

关键组件及其角色

  • Call (call.cc): 通话管理器,RTP/RTCP 包的入口。提供 Receiver() 接口(返回 RtpStreamReceiverController),处理包分发和 BWE 通知。
  • RtpStreamReceiverController (rtp_stream_receiver_controller.cc): RTP 包入口,解析并分发包到 Demuxer,根据 SSRC 路由到接收流(如 RtpVideoStreamReceiver)。
  • RtpDemuxer (rtp_demuxer.cc): 根据 SSRC 或 MID 分发 RTP 包到 Sink(e.g., RtpVideoStreamReceiver)。
  • RtpVideoStreamReceiver (rtp_video_stream_receiver.cc): 视频流接收器,处理 RTP 包(解析头),并更新接收统计。
  • ReceiveStatisticsImpl (receive_statistics_impl.cc): 更新接收统计(如丢失率、抖动),但不直接生成反馈。
  • ReceiveSideCongestionController (receive_side_congestion_controller.cc): 接收端拥塞控制器,协调 REMB 和 TCC。TCC 部分通过 RemoteEstimatorProxy 处理。
  • RemoteEstimatorProxy (remote_estimator_proxy.cc): TCC 核心,记录包到达时间,生成 TransportFeedback 包,并通过 RtcpSender 发送。
  • RtcpSender (rtcp_sender.cc): RTCP 包发送器,序列化 TransportFeedback 并通过 Transport 发送。
  • TransportFeedback (transport_feedback.cc): RTCP 包构建类,添加接收包信息、计算 delta,并序列化。
  • RtpPacketReceived (rtp_packet_received.cc): 接收的 RTP 包结构,包含元数据(如 arrival_time_ms、capture_ntp_time),用于解析头和扩展。
  • RTPSender (rtp_sender.cc): 发送端 RTP 发送器(辅助理解扩展启用),但本指南焦点在接收端。
  • RtpSenderEgress (rtp_sender_egress.cc): 发送端出口处理(辅助理解发送统计),但非接收端核心。

详细流程

流程分为三个阶段:RTP 包接收与记录 、反馈触发与生成 、反馈发送。每个步骤标注调用函数、代码文件和关键逻辑。

1. RTP 包接收与记录

RTP 包从网络到达后,经过 Call 分发、Demux 和统计更新,最终记录到 packet_arrival_times_ 用于反馈生成。

  1. 网络入口:

    • RTP 包从 Transport(如 UDP Socket)到达,调用 Call::Receiver()->DeliverPacket(MediaType media_type, rtc::CopyOnWriteBuffer packet, int64_t packet_time_us) (call.cc)。
    • 内部:根据 media_type 调用 DeliverRtp(packet, media_type) 或 DeliverRtcp。
  2. BWE 通知和初步处理:

    • Call::DeliverRtp(const RtpPacketReceived& packet, MediaType media_type) (call.cc):
      • 调用 NotifyBweOfReceivedPacket(packet, media_type) 通知 BWE。
      • 内部:查找 receive_rtp_config_,检查 use_send_side_bwe(TCC 启用时为 true),提取 RTPHeader,构建 ReceivedPacket 消息,调用 transport_send_ptr_->OnReceivedPacket(packet_msg)。
      • 然后调用 receive_side_cc_.OnReceivedPacket(arrival_time_ms, payload_size + padding_size, header)(如果 TCC 或音频)。
  3. 拥塞控制器处理:

    • ReceiveSideCongestionController::OnReceivedPacket(int64_t arrival_time_ms, size_t payload_size, const RTPHeader& header) (receive_side_congestion_controller.cc):
      • 调用 remote_estimator_proxy_.IncomingPacket(arrival_time_ms, payload_size, header)。
  4. 记录到达信息:

    • RemoteEstimatorProxy::IncomingPacket (remote_estimator_proxy.cc):
      • 检查 arrival_time_ms 有效性。
      • 从 RTP 扩展提取 Transport Sequence Number (seq),unwrap 为长序列号 (unwrapper_.Unwrap(seq))。
      • 忽略重复包(检查 packet_arrival_times_)。
      • 插入记录:packet_arrival_times_.emplace(seq, arrival_time_ms)。
      • 如果有反馈请求 (header.extension.hasFeedbackRequest),立即调用 SendFeedbackOnRequest(seq, feedback_request)。
      • 更新 periodic_window_start_seq_ 和清理旧包 (packet_arrival_times_.erase 超过窗口的条目)。
  5. 分发到接收流:

    • Call::DeliverRtp 继续调用 video_receiver_controller_.OnRtpPacket(parsed_packet)(或音频)(call.cc)。
    • RtpStreamReceiverController::OnRtpPacket (rtp_stream_receiver_controller.cc):锁保护下,调用 demuxer_.OnRtpPacket(packet) 分发包。
  6. Demux 和流处理:

    • RtpDemuxer::OnRtpPacket 根据 SSRC 查找 Sink(e.g., RtpVideoStreamReceiver),调用 RtpVideoStreamReceiver::OnRtpPacket(packet) (rtp_video_stream_receiver.cc)。
    • 逻辑:调用 ParseAndHandleEncapsulatingHeader(packet) 处理封装头(e.g., RTX 或 FEC 解封装)。
    • 然后调用 receive_statistics_->OnRtpPacket(packet) 更新统计(无 UpdateStats 函数)。
  7. 统计更新:

    • ReceiveStatisticsImpl::OnRtpPacket (receive_statistics_impl.cc):获取或创建 StreamStatisticianImpl,调用 StreamStatisticianImpl::UpdateCounters(packet) 更新丢失率、抖动等(e.g., cumulative_loss_、jitter_q4_)。

2. 反馈触发与生成

反馈通过请求或周期触发生成。生成使用 TransportFeedback 类构建包。

  1. 触发条件:

    • 请求触发 :在 IncomingPacket 中,如果有反馈请求,调用 RemoteEstimatorProxy::SendFeedbackOnRequest(seq, feedback_request)。
      • 构建序列范围:seq - feedback_request.sequence_count + 1 到 seq。
      • 调用 BuildFeedbackPacket 生成单个反馈包。
      • 清理已反馈的旧记录。
    • 周期触发 :ReceiveSideCongestionController::Process (实现 Module::Process,每 TimeUntilNextProcess ~5ms 调用) 调用 remote_estimator_proxy_.Process()。
      • RemoteEstimatorProxy::Process 检查间隔 (last_process_time_ms_ + send_interval_ms_),调用 SendPeriodicFeedbacks()。
  2. 生成反馈包:

    • SendPeriodicFeedbacks 或 SendFeedbackOnRequest 调用 BuildFeedbackPacket (remote_estimator_proxy.cc):
      • 输入:基序列号 (base_sequence_number)、迭代器范围 (begin_iterator 到 end_iterator)。
      • 创建 rtcp::TransportFeedback feedback_packet (transport_feedback.cc)。
      • 设置 SSRC (SetMediaSsrc(media_ssrc_))、基序列号和时间 (SetBase(base_sequence_number, first_arrival_time * 1000))。
      • 循环添加包:对于每个序列号,调用 feedback_packet->AddReceivedPacket(seq, arrival_time * 1000)。
        • AddReceivedPacket 计算 delta (相对于基时间,单位 250us),处理丢失(序列不连续),更新 chunk (压缩编码,使用 LastChunk)。
      • 如果包满 (超过大小限制),停止添加。
      • 返回下一个序列号,清理 packet_arrival_times_。
    • 配置:间隔 send_interval_ms_ (默认 25ms,可通过 SetSendPeriodicFeedback 启用周期反馈)。

3. 反馈发送

生成的反馈包序列化并发送。

  1. 发送接口:

    • SendPeriodicFeedbacks 或 SendFeedbackOnRequest 构建 std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets,调用 feedback_sender_->SendCombinedRtcpPacket(std::move(packets))。
    • feedback_sender_ 是 RtcpPacketSender 接口(通常由 RtcpSender 在 rtcp_sender.cc 实现)。
  2. 序列化和发送:

    • RtcpSender::SendCombinedRtcpPacket (rtcp_sender.cc):
      • 设置 Sender SSRC。
      • 使用 PacketSender 序列化每个包:调用 TransportFeedback::Create (transport_feedback.cc) 转为字节缓冲 (包含头、基序列号 base_seq_no_、包计数 num_seq_no_、基准时间 base_time_ticks_)。
      • 组合成复合 RTCP 报文 (compound packet,使用 PacketSender 在 rtcp_sender.cc 中处理)。
      • 调用 transport_->SendRtcp(packet.data(), packet.size()) 发送到网络。
    • 限制:包大小不超过 max_packet_size_ (默认 IP_PACKET_SIZE)。

流程图

以下是 序列图,描述调用流程。
Network (UDP/Transport) Call RtpStreamReceiverController RtpDemuxer RtpVideoStreamReceiver ReceiveSideCongestionController RemoteEstimatorProxy ReceiveStatistics RtcpSender RTP Packet arrives (DeliverPacket) DeliverRtp(packet, media_type) NotifyBweOfReceivedPacket → OnReceivedPacket(arrival_time, payload_size, header) IncomingPacket(...) Record in packet_arrival_times_ If feedback_request, SendFeedbackOnRequest() SendCombinedRtcpPacket(TransportFeedback) video_receiver_controller_.OnRtpPacket(parsed_packet) OnRtpPacket(packet) OnRtpPacket(packet) via Sink ParseAndHandleEncapsulatingHeader(packet) receive_statistics_->>OnRtpPacket(packet) UpdateCounters (stats like loss, jitter) Process() SendPeriodicFeedbacks() SendCombinedRtcpPacket(TransportFeedback) loop Periodic Process (every \~25ms) Send RTCP Feedback to Sender Network (UDP/Transport) Call RtpStreamReceiverController RtpDemuxer RtpVideoStreamReceiver ReceiveSideCongestionController RemoteEstimatorProxy ReceiveStatistics RtcpSender

图解释:

  • 左侧:RTP 包接收链路,从 Transport 到 Call。
  • 中间:BWE 通知、记录和触发反馈(TCC 部分先于 Demux)。
  • 右侧:分发到流、更新统计和发送反馈。
  • 循环:表示周期性反馈。

数据流转图 (简化文本表示)

css 复制代码
RTP Packet (Network/Transport) 
  ↓
Call::DeliverPacket → DeliverRtp(packet, media_type)
  ↓ (BWE 通知)
NotifyBweOfReceivedPacket → ReceiveSideCongestionController::OnReceivedPacket → RemoteEstimatorProxy::IncomingPacket 
  ↓ (记录)
packet_arrival_times_ (map<seq, arrival_time>)
  ↓ (触发生成)
SendPeriodicFeedbacks / SendFeedbackOnRequest → BuildFeedbackPacket → TransportFeedback (add packets, deltas)
  ↓ (发送)
RtcpSender::SendCombinedRtcpPacket → Serialize & SendRtcp (Network)

并行分发:
Call::DeliverRtp → RtpStreamReceiverController::OnRtpPacket → RtpDemuxer::OnRtpPacket → RtpVideoStreamReceiver::OnRtpPacket 
  ↓
ParseAndHandleEncapsulatingHeader → ReceiveStatisticsImpl::OnRtpPacket → StreamStatisticianImpl::UpdateCounters (loss, jitter)

注意事项

  • 配置 :TCC 需启用 sender-side BWE (GetRemoteBitrateEstimator(true))。反馈间隔通过 TransportWideFeedbackConfig 配置 (e.g., min_interval=50ms)。扩展头在发送端 RTPSender 中注册,接收端通过 RtpPacketReceived 解析。
  • 线程安全 :大多数操作在 Worker Thread 上 (e.g., decode_queue_),使用锁 (rtc::CriticalSection) 保护共享数据。
  • 异常处理:时间戳无效或包重复时忽略。反馈包大小受限 (1-2KB),大范围分多个包。
  • 与 REMB 的区别 :REMB 使用 WrappingBitrateEstimator 直接估计比特率,TCC 发送原始数据让发送端估计。
  • 测试与调试 :参考 receive_side_congestion_controller_unittest.cc,模拟包到达测试反馈生成。
  • 版本注意:基于 WebRTC 主分支代码,具体实现可能因版本略有差异。建议结合 Chromium 的 WebRTC 变更日志验证。

此指南可作为代码阅读起点,如需深入某个模块。

相关推荐
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
wuyk5553 天前
《WiFi 嵌入式物联网开发全套实战》| 第 16 章 ESP32 AP+STA 双模共存原理与工程坑点
网络·stm32·物联网
2601_955662464 天前
短剧多人对白怎么配?2026年多角色配音工具对比
大数据·人工智能·音视频·语音识别
开发笔记-阿牛4 天前
蓝牙音乐灯拆解,又一次拆到蓝牙音乐灯芯片CK6865L
人工智能·单片机·嵌入式硬件·音视频·音频
QYRdata4 天前
年均增速24.2%!机器人数据湖未来六年增长动能强劲
网络·机器人·服务发现
CHENKONG_CK4 天前
破解制鞋打磨痛点:RFID赋能去毛刺工序自动化升级
网络·单片机·嵌入式硬件·网络协议·tcp/ip
旺仔小馒头wang4 天前
AI 智能手机发布会:赋予人类的五种新能力
人工智能·学习·音视频
chshang19924 天前
工业路由器是什么?浅谈5G工业网络中的IR602
网络·物联网·5g·智能路由器
萧瑟余晖4 天前
Netty 核心组件与 Reactor 模型详解
网络·架构