收到 RTCP 包后分音频视频传递给相应 ReceiveStream 处理。
// /call/call.cc
PacketReceiver::DeliveryStatus Call::DeliverRtcp(MediaType media_type,
const uint8_t* packet,
size_t length) {
// media_type == MediaType::VIDEO
video_receive_stream_->DeliverRtcp(packet, length);
// media)type == MediaType::AUDIO
audio_receive_stream->DeliverRtcp(packet, length);
}
这里主要关注视频流,继续向下传递 RTCP 包。
// /video/video_receive_stream.cc
bool VideoReceiveStream::DeliverRtcp(const uint8_t* packet, size_t length) {
return rtp_video_stream_receiver_.DeliverRtcp(packet, length);
}
RtpVideoStreamReceiver 通知 RtpRtcp 类解析 RTCP 包后,获得 RTT 等信息。
// /video/rtp_video_stream_receiver.cc
bool RtpVideoStreamReceiver::DeliverRtcp(const uint8_t* rtcp_packet,
size_t rtcp_packet_length) {
rtp_rtcp_->IncomingRtcpPacket(rtcp_packet, rtcp_packet_length);
}
ModuleRtpRtcpImpl 类实现了 RtpRtcp,相应上面的调用,通知 RTCPReceiver 解析 RTCP 包。
// /modules/rtp_rtcp/source/rtp_rtcp_impl.cc
void ModuleRtpRtcpImpl::IncomingRtcpPacket(const uint8_t* rtcp_packet,
const size_t length) {
rtcp_receiver_.IncomingPacket(rtcp_packet, length);
}
RTCPReceiver 类负责分解复合 RTCP 包,并触发相应回调函数。
// /modules/rtp_rtcp/souce/rtcp_receiver.cc
void RTCPReceiver::IncomingPacket(const uint8_t* packet, size_t packet_size) {
PacketInformation packet_information;
if (!ParseCompoundPacket(packet, packet + packet_size, &packet_information))
return;
TriggerCallbacksFromRtcpPacket(packet_information);
}
RTCP 包的类型有:SR/RR/SDE/PLI/REMB/FIR等等,具体见 RFC3550。
bool RTCPReceiver::ParseCompoundPacket(const uint8_t* packet_begin,
const uint8_t* packet_end,
PacketInformation* packet_information) {
switch (rtcp_block.type()) {
case rtcp::SenderReport::kPacketType:
HandleSenderReport(rtcp_block, packet_information);
break;
case rtcp::ReceiverReport::kPacketType:
HandleReceiverReport(rtcp_block, packet_information);
break;
case rtcp::Sdes::kPacketType:
HandleSdes(rtcp_block, packet_information);
break;
case rtcp::ExtendedReports::kPacketType:
HandleXr(rtcp_block, packet_information);
break;
case rtcp::Bye::kPacketType:
HandleBye(rtcp_block);
break;
case rtcp::Rtpfb::kPacketType:
switch (rtcp_block.fmt()) {
case rtcp::Nack::kFeedbackMessageType:
HandleNack(rtcp_block, packet_information);
break;
case rtcp::Tmmbr::kFeedbackMessageType:
HandleTmmbr(rtcp_block, packet_information);
break;
case rtcp::Tmmbn::kFeedbackMessageType:
HandleTmmbn(rtcp_block, packet_information);
break;
case rtcp::RapidResyncRequest::kFeedbackMessageType:
HandleSrReq(rtcp_block, packet_information);
break;
case rtcp::TransportFeedback::kFeedbackMessageType:
HandleTransportFeedback(rtcp_block, packet_information);
break;
default:
++num_skipped_packets_;
break;
}
break;
case rtcp::Psfb::kPacketType:
switch (rtcp_block.fmt()) {
case rtcp::Pli::kFeedbackMessageType:
HandlePli(rtcp_block, packet_information);
break;
case rtcp::Fir::kFeedbackMessageType:
HandleFir(rtcp_block, packet_information);
break;
case rtcp::Remb::kFeedbackMessageType:
HandlePsfbApp(rtcp_block, packet_information);
break;
default:
++num_skipped_packets_;
break;
}
break;
case rtcp::App::kPacketType:
switch (rtcp_block.fmt()) {
case rtcp::App::kNtpSyncResponseType:
HandleSyncNtp(rtcp_block, packet_information);
break;
default:
++num_skipped_packets_;
break;
}
break;
default:
++num_skipped_packets_;
break;
}
}
例如:GCC 输入就来自 TransportFeedback 类型的 RTCP 包,上面的函数传给 TransportFeedback 来解读 RTCP 包携带的信息(序列号、时间戳等)。
// /modules/rtp_rtcp/source/rtcp_receiver.cc
void RTCPReceiver::HandleTransportFeedback(
const CommonHeader& rtcp_block,
PacketInformation* packet_information) {
transport_feedback->Parse(rtcp_block);
}
// /modules/rtp_rtcp/souce/rtcp_packet/transport_feedback.cc
bool TransportFeedback::Parse(const CommonHeader& packet) {
const uint8_t* const payload = packet.payload();
ParseCommonFeedback(payload);
base_seq_no_ = ByteReader<uint16_t>::ReadBigEndian(&payload[8]);
uint16_t status_count = ByteReader<uint16_t>::ReadBigEndian(&payload[10]);
base_time_ticks_ = ByteReader<int32_t, 3>::ReadBigEndian(&payload[12]);
feedback_seq_ = payload[15];
Clear();
size_t index = 16;
const size_t end_index = packet.payload_size_bytes();
// ...
}
分解了复合包之后就触发相应的回调函数。比如 TransportFeedback包就回调 TransportFeedbackObserver 来使用 RTCP 包的信息,开始 GCC 的流程。
// /modules/rtp_rtcp/source/rtcp_receiver.cc
void RTCPReceiver::TriggerCallbacksFromRtcpPacket(
const PacketInformation& packet_information) {
if (transport_feedback_observer_ &&
(packet_information.packet_type_flags & kRtcpTransportFeedback)) {
uint32_t media_source_ssrc =
packet_information.transport_feedback->media_ssrc();
if (media_source_ssrc == local_ssrc ||
registered_ssrcs.find(media_source_ssrc) != registered_ssrcs.end()) {
transport_feedback_observer_->OnTransportFeedback(
*packet_information.transport_feedback);
}
}
}