diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h index 65d57946b9..00e374c4c3 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -509,6 +509,11 @@ class RtpRtcp : public Module { virtual int32_t SetRTCPVoIPMetrics( const RTCPVoIPMetric* VoIPMetric) = 0; + /* + * (XR) Receiver Reference Time Report + */ + virtual void SetRtcpXrRrtrStatus(bool enable) = 0; + /* * (REMB) Receiver Estimated Max Bitrate */ diff --git a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 608fe351ab..355d79699e 100644 --- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -168,6 +168,8 @@ class MockRtpRtcp : public RtpRtcp { int32_t(const uint8_t subType, const uint32_t name, const uint8_t* data, const uint16_t length)); MOCK_METHOD1(SetRTCPVoIPMetrics, int32_t(const RTCPVoIPMetric* VoIPMetric)); + MOCK_METHOD1(SetRtcpXrRrtrStatus, + void(bool enable)); MOCK_CONST_METHOD0(REMB, bool()); MOCK_METHOD1(SetREMBStatus, diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc index 25fa82c658..ddeedccd94 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -50,6 +50,7 @@ RTCPReceiver::RTCPReceiver(const int32_t id, Clock* clock, _lastReceivedSRNTPfrac(0), _lastReceivedXRNTPsecs(0), _lastReceivedXRNTPfrac(0), + xr_rr_rtt_ms_(0), _receivedInfoMap(), _packetTimeOutMS(0), _lastReceivedRrMs(0), @@ -216,6 +217,17 @@ int32_t RTCPReceiver::RTT(uint32_t remoteSSRC, return 0; } +bool RTCPReceiver::GetAndResetXrRrRtt(uint16_t* rtt_ms) { + assert(rtt_ms); + CriticalSectionScoped lock(_criticalSectionRTCPReceiver); + if (xr_rr_rtt_ms_ == 0) { + return false; + } + *rtt_ms = xr_rr_rtt_ms_; + xr_rr_rtt_ms_ = 0; + return true; +} + uint16_t RTCPReceiver::RTT() const { CriticalSectionScoped lock(_criticalSectionRTCPReceiver); if (!_receivedReportBlockMap.empty()) { @@ -897,6 +909,7 @@ void RTCPReceiver::HandleBYE(RTCPUtility::RTCPParserV2& rtcpParser) { delete cnameInfoIt->second; _receivedCnameMap.erase(cnameInfoIt); } + xr_rr_rtt_ms_ = 0; rtcpParser.Iterate(); } @@ -968,13 +981,13 @@ void RTCPReceiver::HandleXrDlrrReportBlockItem( } // The DelayLastRR field is in units of 1/65536 sec. -// uint32_t delay_rr_ms = -// (((packet.XRDLRRReportBlockItem.DelayLastRR & 0x0000ffff) * 1000) >> 16) + -// (((packet.XRDLRRReportBlockItem.DelayLastRR & 0xffff0000) >> 16) * 1000); + uint32_t delay_rr_ms = + (((packet.XRDLRRReportBlockItem.DelayLastRR & 0x0000ffff) * 1000) >> 16) + + (((packet.XRDLRRReportBlockItem.DelayLastRR & 0xffff0000) >> 16) * 1000); - // TODO(asapersson): Not yet used. -// int32_t rtt =_clock->CurrentNtpInMilliseconds() - delay_rr_ms - send_time_ms; -// rtt = std::max(rtt, 1); + int32_t rtt = _clock->CurrentNtpInMilliseconds() - delay_rr_ms - send_time_ms; + + xr_rr_rtt_ms_ = static_cast(std::max(rtt, 1)); rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpXrDlrrReportBlock; } diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h index 0b5c8f1aba..3da3445701 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h @@ -86,6 +86,8 @@ public: int32_t SenderInfoReceived(RTCPSenderInfo* senderInfo) const; + bool GetAndResetXrRrRtt(uint16_t* rtt_ms); + // get statistics int32_t StatisticsReceived( std::vector* receiveBlocks) const; @@ -245,6 +247,8 @@ protected: // Time when the report was received. uint32_t _lastReceivedXRNTPsecs; uint32_t _lastReceivedXRNTPfrac; + // Estimated rtt, zero when there is no valid estimate. + uint16_t xr_rr_rtt_ms_; // Received report blocks. std::map diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc index af969ded27..c33cf239ff 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc @@ -459,6 +459,11 @@ TEST(RtcpUtilityTest, MidNtp) { EXPECT_EQ(kNtpMid, RTCPUtility::MidNtp(kNtpSec, kNtpFrac)); } +TEST_F(RtcpReceiverTest, TestXrRrRttInitiallyFalse) { + uint16_t rtt_ms; + EXPECT_FALSE(rtcp_receiver_->GetAndResetXrRrRtt(&rtt_ms)); +} + TEST_F(RtcpReceiverTest, LastReceivedXrReferenceTimeInfoInitiallyFalse) { RtcpReceiveTimeInfo info; EXPECT_FALSE(rtcp_receiver_->LastReceivedXrReferenceTimeInfo(&info)); diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 812f427346..3369b55198 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -196,13 +196,12 @@ int32_t ModuleRtpRtcpImpl::Process() { default_instance = true; } if (!default_instance) { + bool process_rtt = now >= last_rtt_process_time_ + kRtpRtcpRttProcessTimeMs; if (rtcp_sender_.Sending()) { // Process RTT if we have received a receiver report and we haven't // processed RTT for at least |kRtpRtcpRttProcessTimeMs| milliseconds. if (rtcp_receiver_.LastReceivedReceiverReport() > - last_rtt_process_time_ && now >= last_rtt_process_time_ + - kRtpRtcpRttProcessTimeMs) { - last_rtt_process_time_ = now; + last_rtt_process_time_ && process_rtt) { std::vector receive_blocks; rtcp_receiver_.StatisticsReceived(&receive_blocks); uint16_t max_rtt = 0; @@ -237,7 +236,20 @@ int32_t ModuleRtpRtcpImpl::Process() { rtcp_sender_.SetTargetBitrate(target_bitrate); } } + } else { + // Report rtt from receiver. + if (process_rtt) { + uint16_t rtt_ms; + if (rtt_observer_ && rtcp_receiver_.GetAndResetXrRrRtt(&rtt_ms)) { + rtt_observer_->OnRttUpdate(rtt_ms); + } + } } + + if (process_rtt) { + last_rtt_process_time_ = now; + } + if (rtcp_sender_.TimeToSendRTCPReport()) { RTCPSender::FeedbackState feedback_state(this); rtcp_sender_.SendRTCP(feedback_state, kRtcpReport); @@ -941,6 +953,12 @@ int32_t ModuleRtpRtcpImpl::SetRTCPVoIPMetrics( return rtcp_sender_.SetRTCPVoIPMetrics(voip_metric); } +void ModuleRtpRtcpImpl::SetRtcpXrRrtrStatus(bool enable) { + WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, + "SetRtcpXrRrtrStatus(%s)", enable ? "true" : "false"); + return rtcp_sender_.SendRtcpXrReceiverReferenceTime(enable); +} + int32_t ModuleRtpRtcpImpl::DataCountersRTP( uint32_t* bytes_sent, uint32_t* packets_sent) const { diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h index b97ef1a98c..c2fc498d79 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -255,6 +255,9 @@ class ModuleRtpRtcpImpl : public RtpRtcp { // (XR) VOIP metric. virtual int32_t SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric) OVERRIDE; + // (XR) Receiver reference time report. + virtual void SetRtcpXrRrtrStatus(bool enable) OVERRIDE; + // Audio part. // Set audio packet size, used to determine when it's time to send a DTMF diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc index f9add41840..ff3c32e220 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc @@ -18,6 +18,17 @@ namespace webrtc { namespace { +class RtcpRttStatsTestImpl : public RtcpRttObserver { + public: + RtcpRttStatsTestImpl() : rtt_ms_(0) {} + virtual ~RtcpRttStatsTestImpl() {} + + virtual void OnRttUpdate(uint32_t rtt_ms) { + rtt_ms_ = rtt_ms; + } + uint32_t rtt_ms_; +}; + class SendTransport : public Transport, public NullRtpData { public: @@ -59,6 +70,7 @@ class RtpRtcpImplTest : public ::testing::Test { configuration.clock = &clock_; configuration.outgoing_transport = &transport_; configuration.receive_statistics = receive_statistics_.get(); + configuration.rtt_observer = &rtt_stats_; rtp_rtcp_impl_.reset(new ModuleRtpRtcpImpl(configuration)); transport_.SetRtpRtcpModule(rtp_rtcp_impl_.get()); @@ -68,6 +80,7 @@ class RtpRtcpImplTest : public ::testing::Test { scoped_ptr receive_statistics_; scoped_ptr rtp_rtcp_impl_; SendTransport transport_; + RtcpRttStatsTestImpl rtt_stats_; }; TEST_F(RtpRtcpImplTest, Rtt) { @@ -108,4 +121,25 @@ TEST_F(RtpRtcpImplTest, Rtt) { rtp_rtcp_impl_->RTT(kSsrc + 1, &rtt, &avg_rtt, &min_rtt, &max_rtt)); } +TEST_F(RtpRtcpImplTest, RttForReceiverOnly) { + rtp_rtcp_impl_->SetRtcpXrRrtrStatus(true); + EXPECT_EQ(0, rtp_rtcp_impl_->SetSendingStatus(false)); + EXPECT_EQ(0, rtp_rtcp_impl_->SetRTCPStatus(kRtcpCompound)); + EXPECT_EQ(0, rtp_rtcp_impl_->SetSSRC(0x12345)); + + // A Receiver time reference report (RTRR) should be sent and received. + EXPECT_EQ(0, rtp_rtcp_impl_->SendRTCP(kRtcpReport)); + + // Send new RTRR. A response to the last RTRR should be sent. + clock_.AdvanceTimeMilliseconds(1000); + transport_.SimulateNetworkDelay(100, &clock_); + EXPECT_EQ(0, rtp_rtcp_impl_->SendRTCP(kRtcpReport)); + + // Verify RTT. + EXPECT_EQ(0U, rtt_stats_.rtt_ms_); + + rtp_rtcp_impl_->Process(); + EXPECT_EQ(100U, rtt_stats_.rtt_ms_); +} + } // namespace webrtc