概述
在 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_ 用于反馈生成。
-
网络入口:
- 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。
- RTP 包从 Transport(如 UDP Socket)到达,调用
-
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 或音频)。
- 调用
-
拥塞控制器处理:
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)。
- 调用
-
记录到达信息:
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超过窗口的条目)。
- 检查
-
分发到接收流:
Call::DeliverRtp继续调用video_receiver_controller_.OnRtpPacket(parsed_packet)(或音频)(call.cc)。RtpStreamReceiverController::OnRtpPacket(rtp_stream_receiver_controller.cc):锁保护下,调用demuxer_.OnRtpPacket(packet)分发包。
-
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函数)。
-
统计更新:
ReceiveStatisticsImpl::OnRtpPacket(receive_statistics_impl.cc):获取或创建StreamStatisticianImpl,调用StreamStatisticianImpl::UpdateCounters(packet)更新丢失率、抖动等(e.g.,cumulative_loss_、jitter_q4_)。
2. 反馈触发与生成
反馈通过请求或周期触发生成。生成使用 TransportFeedback 类构建包。
-
触发条件:
- 请求触发 :在
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()。
- 请求触发 :在
-
生成反馈包:
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. 反馈发送
生成的反馈包序列化并发送。
-
发送接口:
SendPeriodicFeedbacks或SendFeedbackOnRequest构建std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets,调用feedback_sender_->SendCombinedRtcpPacket(std::move(packets))。feedback_sender_是RtcpPacketSender接口(通常由RtcpSender在rtcp_sender.cc实现)。
-
序列化和发送:
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 变更日志验证。
此指南可作为代码阅读起点,如需深入某个模块。