diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc index d6313ae1cd..64e034a2e5 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc @@ -67,6 +67,10 @@ bool RtcpTransceiverConfig::Validate() const { RTC_LOG(LS_ERROR) << debug_id << "unsupported rtcp mode"; return false; } + if (non_sender_rtt_measurement && !rtt_observer) + RTC_LOG(LS_WARNING) << debug_id + << "Enabled special feature to calculate rtt, but no " + "rtt observer is provided."; // TODO(danilchap): Remove or update the warning when RtcpTransceiver supports // send-only sessions. if (receive_statistics == nullptr) diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.h b/modules/rtp_rtcp/source/rtcp_transceiver_config.h index 07f4c51523..18efd82644 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.h @@ -52,6 +52,10 @@ struct RtcpTransceiverConfig { // Rtcp report block generator for outgoing receiver reports. ReceiveStatisticsProvider* receive_statistics = nullptr; + // Callback to pass result of rtt calculation. Should outlive RtcpTransceiver. + // Callbacks will be invoked on the task_queue. + RtcpRttStats* rtt_observer = nullptr; + // Configures if sending should // enforce compound packets: https://tools.ietf.org/html/rfc4585#section-3.1 // or allow reduced size packets: https://tools.ietf.org/html/rfc5506 @@ -70,6 +74,9 @@ struct RtcpTransceiverConfig { // Flags for features and experiments. // bool schedule_periodic_compound_packets = true; + // Estimate RTT as non-sender as described in + // https://tools.ietf.org/html/rfc3611#section-4.4 and #section-4.5 + bool non_sender_rtt_measurement = false; }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc index deb8c0df9c..5378b2d3ef 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -17,6 +17,7 @@ #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" #include "modules/rtp_rtcp/source/rtcp_packet/fir.h" #include "modules/rtp_rtcp/source/rtcp_packet/nack.h" #include "modules/rtp_rtcp/source/rtcp_packet/pli.h" @@ -163,17 +164,47 @@ void RtcpTransceiverImpl::HandleReceivedPacket( const rtcp::CommonHeader& rtcp_packet_header, int64_t now_us) { switch (rtcp_packet_header.type()) { - case rtcp::SenderReport::kPacketType: { - rtcp::SenderReport sender_report; - if (!sender_report.Parse(rtcp_packet_header)) - return; - rtc::Optional& last = - remote_senders_[sender_report.sender_ssrc()] - .last_received_sender_report; - last.emplace(); - last->local_received_time_us = now_us; - last->remote_sent_time = sender_report.ntp(); + case rtcp::SenderReport::kPacketType: + HandleSenderReport(rtcp_packet_header, now_us); break; + case rtcp::ExtendedReports::kPacketType: + HandleExtendedReports(rtcp_packet_header, now_us); + break; + } +} + +void RtcpTransceiverImpl::HandleSenderReport( + const rtcp::CommonHeader& rtcp_packet_header, + int64_t now_us) { + rtcp::SenderReport sender_report; + if (!sender_report.Parse(rtcp_packet_header)) + return; + rtc::Optional& last = + remote_senders_[sender_report.sender_ssrc()].last_received_sender_report; + last.emplace(); + last->local_received_time_us = now_us; + last->remote_sent_time = sender_report.ntp(); +} + +void RtcpTransceiverImpl::HandleExtendedReports( + const rtcp::CommonHeader& rtcp_packet_header, + int64_t now_us) { + rtcp::ExtendedReports extended_reports; + if (!extended_reports.Parse(rtcp_packet_header)) + return; + if (extended_reports.dlrr() && config_.non_sender_rtt_measurement && + config_.rtt_observer) { + // Delay and last_rr are transferred using 32bit compact ntp resolution. + // Convert packet arrival time to same format through 64bit ntp format. + uint32_t receive_time_ntp = CompactNtp(TimeMicrosToNtp(now_us)); + for (const rtcp::ReceiveTimeInfo& rti : + extended_reports.dlrr().sub_blocks()) { + if (rti.ssrc != config_.feedback_ssrc) + continue; + uint32_t rtt_ntp = + receive_time_ntp - rti.delay_since_last_rr - rti.last_rr; + int64_t rtt_ms = CompactNtpRttToMs(rtt_ntp); + config_.rtt_observer->OnRttUpdate(rtt_ms); } } } @@ -220,9 +251,10 @@ void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) { void RtcpTransceiverImpl::CreateCompoundPacket(PacketSender* sender) { RTC_DCHECK(sender->IsEmpty()); const uint32_t sender_ssrc = config_.feedback_ssrc; + int64_t now_us = rtc::TimeMicros(); rtcp::ReceiverReport receiver_report; receiver_report.SetSenderSsrc(sender_ssrc); - receiver_report.SetReportBlocks(CreateReportBlocks()); + receiver_report.SetReportBlocks(CreateReportBlocks(now_us)); sender->AppendPacket(receiver_report); if (!config_.cname.empty()) { @@ -236,6 +268,19 @@ void RtcpTransceiverImpl::CreateCompoundPacket(PacketSender* sender) { remb_->SetSenderSsrc(sender_ssrc); sender->AppendPacket(*remb_); } + // TODO(bugs.webrtc.org/8239): Do not send rrtr if this packet starts with + // SenderReport instead of ReceiverReport + // when RtcpTransceiver supports rtp senders. + if (config_.non_sender_rtt_measurement) { + rtcp::ExtendedReports xr; + + rtcp::Rrtr rrtr; + rrtr.SetNtp(TimeMicrosToNtp(now_us)); + xr.SetRrtr(rrtr); + + xr.SetSenderSsrc(sender_ssrc); + sender->AppendPacket(xr); + } } void RtcpTransceiverImpl::SendPeriodicCompoundPacket() { @@ -260,7 +305,8 @@ void RtcpTransceiverImpl::SendImmediateFeedback( ReschedulePeriodicCompoundPackets(); } -std::vector RtcpTransceiverImpl::CreateReportBlocks() { +std::vector RtcpTransceiverImpl::CreateReportBlocks( + int64_t now_us) { if (!config_.receive_statistics) return {}; // TODO(danilchap): Support sending more than @@ -276,7 +322,7 @@ std::vector RtcpTransceiverImpl::CreateReportBlocks() { *it->second.last_received_sender_report; report_block.SetLastSr(CompactNtp(last_sender_report.remote_sent_time)); report_block.SetDelayLastSr(SaturatedUsToCompactNtp( - rtc::TimeMicros() - last_sender_report.local_received_time_us)); + now_us - last_sender_report.local_received_time_us)); } return report_blocks; } diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h index c3193d058b..cfd5de7a1c 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -54,6 +54,10 @@ class RtcpTransceiverImpl { void HandleReceivedPacket(const rtcp::CommonHeader& rtcp_packet_header, int64_t now_us); + void HandleSenderReport(const rtcp::CommonHeader& rtcp_packet_header, + int64_t now_us); + void HandleExtendedReports(const rtcp::CommonHeader& rtcp_packet_header, + int64_t now_us); void ReschedulePeriodicCompoundPackets(); void SchedulePeriodicCompoundPackets(int64_t delay_ms); @@ -64,7 +68,7 @@ class RtcpTransceiverImpl { void SendPeriodicCompoundPacket(); void SendImmediateFeedback(const rtcp::RtcpPacket& rtcp_packet); // Generate Report Blocks to be send in Sender or Receiver Report. - std::vector CreateReportBlocks(); + std::vector CreateReportBlocks(int64_t now_us); const RtcpTransceiverConfig config_; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc index b5f36b18b4..de0582b16c 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -13,6 +13,7 @@ #include #include "modules/rtp_rtcp/include/receive_statistics.h" +#include "modules/rtp_rtcp/mocks/mock_rtcp_rtt_stats.h" #include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/event.h" #include "rtc_base/fakeclock.h" @@ -32,10 +33,13 @@ using ::testing::Return; using ::testing::SizeIs; using ::webrtc::CompactNtp; using ::webrtc::CompactNtpRttToMs; +using ::webrtc::MockRtcpRttStats; using ::webrtc::MockTransport; using ::webrtc::NtpTime; using ::webrtc::RtcpTransceiverConfig; using ::webrtc::RtcpTransceiverImpl; +using ::webrtc::SaturatedUsToCompactNtp; +using ::webrtc::TimeMicrosToNtp; using ::webrtc::rtcp::ReportBlock; using ::webrtc::rtcp::SenderReport; using ::webrtc::test::RtcpPacketParser; @@ -580,4 +584,92 @@ TEST(RtcpTransceiverImplTest, KeyFrameRequestCreatesReducedSizePacket) { EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 0); } +TEST(RtcpTransceiverImplTest, SendsXrRrtrWhenEnabled) { + const uint32_t kSenderSsrc = 4321; + rtc::ScopedFakeClock clock; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.schedule_periodic_compound_packets = false; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.non_sender_rtt_measurement = true; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SendCompoundPacket(); + NtpTime ntp_time_now = TimeMicrosToNtp(rtc::TimeMicros()); + + EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1); + EXPECT_EQ(rtcp_parser.xr()->sender_ssrc(), kSenderSsrc); + ASSERT_TRUE(rtcp_parser.xr()->rrtr()); + EXPECT_EQ(rtcp_parser.xr()->rrtr()->ntp(), ntp_time_now); +} + +TEST(RtcpTransceiverImplTest, SendsNoXrRrtrWhenDisabled) { + RtcpTransceiverConfig config; + config.schedule_periodic_compound_packets = false; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.non_sender_rtt_measurement = false; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(transport.num_packets(), 1); + // Extended reports rtcp packet might be included for another reason, + // but it shouldn't contain rrtr block. + EXPECT_FALSE(rtcp_parser.xr()->rrtr()); +} + +TEST(RtcpTransceiverImplTest, CalculatesRoundTripTimeOnDlrr) { + const uint32_t kSenderSsrc = 4321; + MockRtcpRttStats rtt_observer; + MockTransport null_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.schedule_periodic_compound_packets = false; + config.outgoing_transport = &null_transport; + config.non_sender_rtt_measurement = true; + config.rtt_observer = &rtt_observer; + RtcpTransceiverImpl rtcp_transceiver(config); + + int64_t time_us = 12345678; + webrtc::rtcp::ReceiveTimeInfo rti; + rti.ssrc = kSenderSsrc; + rti.last_rr = CompactNtp(TimeMicrosToNtp(time_us)); + rti.delay_since_last_rr = SaturatedUsToCompactNtp(10 * 1000); + webrtc::rtcp::ExtendedReports xr; + xr.AddDlrrItem(rti); + auto raw_packet = xr.Build(); + + EXPECT_CALL(rtt_observer, OnRttUpdate(100 /* rtt_ms */)); + rtcp_transceiver.ReceivePacket(raw_packet, time_us + 110 * 1000); +} + +TEST(RtcpTransceiverImplTest, IgnoresUnknownSsrcInDlrr) { + const uint32_t kSenderSsrc = 4321; + const uint32_t kUnknownSsrc = 4322; + MockRtcpRttStats rtt_observer; + MockTransport null_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.schedule_periodic_compound_packets = false; + config.outgoing_transport = &null_transport; + config.non_sender_rtt_measurement = true; + config.rtt_observer = &rtt_observer; + RtcpTransceiverImpl rtcp_transceiver(config); + + int64_t time_us = 12345678; + webrtc::rtcp::ReceiveTimeInfo rti; + rti.ssrc = kUnknownSsrc; + rti.last_rr = CompactNtp(TimeMicrosToNtp(time_us)); + webrtc::rtcp::ExtendedReports xr; + xr.AddDlrrItem(rti); + auto raw_packet = xr.Build(); + + EXPECT_CALL(rtt_observer, OnRttUpdate(_)).Times(0); + rtcp_transceiver.ReceivePacket(raw_packet, time_us + 100000); +} + } // namespace