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

概述

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

本指南基于提供的代码片段(包括 rtp_stream_receiver_controller.ccvideo_receive_stream.ccrtp_video_stream_receiver.ccreceive_side_congestion_controller.ccremote_estimator_proxy.ccrtcp_sender.cccall.ccrtp_header_extensions.ccrtp_packet_received.ccrtp_sender_egress.ccrtp_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),用于解析扩展(如 IdentifyExtensionsrtp_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 + 1seq
      • 调用 BuildFeedbackPacket 生成单个反馈包。
      • 清理已反馈的旧记录。
    • 周期触发ReceiveSideCongestionController::Process (实现 Module::Process,每 TimeUntilNextProcess ~5ms 调用) 调用 remote_estimator_proxy_.Process()
      • RemoteEstimatorProxy::Process 检查间隔 (last_process_time_ms_ + send_interval_ms_),调用 SendPeriodicFeedbacks()
  2. 生成反馈包

    • SendPeriodicFeedbacksSendFeedbackOnRequest 调用 BuildFeedbackPacket (remote_estimator_proxy.cc):
      • 输入:基序列号 (base_sequence_number)、迭代器范围 (begin_iteratorend_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. 发送接口

    • SendPeriodicFeedbacksSendFeedbackOnRequest 构建 std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets,调用 feedback_sender_->SendCombinedRtcpPacket(std::move(packets))
    • feedback_sender_RtcpPacketSender 接口(通常由 RtcpSenderrtcp_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,使用 PacketSenderrtcp_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 变更日志验证。

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

相关推荐
拾忆,想起2 小时前
Dubbo跨机房调用实战:从原理到架构的完美解决方案
服务器·网络·网络协议·tcp/ip·架构·dubbo
辻戋2 小时前
HTTP的血泪进化史
网络·网络协议·http
Everbrilliant892 小时前
FFmpeg解码视频数据ANativeWindow播放
ffmpeg·音视频·ffmpeg视频解码·anativewindow·threadsafequeue·解码线程·渲染线程
NiKo_W3 小时前
Linux 数据链路层
linux·服务器·网络·内网穿透·nat·数据链路层
拾忆,想起3 小时前
Dubbo网络延迟全链路排查指南:从微服务“快递”到光速传输
网络·网络协议·微服务·架构·php·dubbo
简鹿办公3 小时前
什么是 MKV 视频格式?MKV 视频怎样批量转为 MP4 格式
音视频
星轨初途3 小时前
数据结构二叉树之链式结构(3)(下)
c语言·网络·数据结构·经验分享·笔记·后端
metaRTC4 小时前
webRTC IPC客户端Flutter版编程指南
flutter·webrtc·ipc
曾经的三心草5 小时前
JavaEE初阶-网络原理1
java·网络·java-ee