diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index d7e7cf83f..ff9761d55 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -199,6 +199,7 @@ 'rtp_rtcp/source/nack_rtx_unittest.cc', 'rtp_rtcp/source/producer_fec_unittest.cc', 'rtp_rtcp/source/receive_statistics_unittest.cc', + 'rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc', 'rtp_rtcp/source/rtcp_format_remb_unittest.cc', 'rtp_rtcp/source/rtcp_packet_unittest.cc', 'rtp_rtcp/source/rtcp_receiver_unittest.cc', diff --git a/webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h b/webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h new file mode 100644 index 000000000..25f0f2ecf --- /dev/null +++ b/webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_INTERFACE_REMOTE_NTP_TIME_ESTIMATOR_H_ +#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_REMOTE_NTP_TIME_ESTIMATOR_H_ + +#include "webrtc/system_wrappers/interface/rtp_to_ntp.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class Clock; +class RtpRtcp; +class TimestampExtrapolator; + +// RemoteNtpTimeEstimator can be used to estimate a given RTP timestamp's NTP +// time in local timebase. +// Note that it needs to be trained with at least 2 RTCP SR (by calling +// |UpdateRtcpTimestamp|) before it can be used. +class RemoteNtpTimeEstimator { + public: + explicit RemoteNtpTimeEstimator(Clock* clock); + + ~RemoteNtpTimeEstimator(); + + // Updates the estimator with the timestamp from newly received RTCP SR for + // |ssrc|. The RTCP SR is read from |rtp_rtcp|. + bool UpdateRtcpTimestamp(uint32_t ssrc, RtpRtcp* rtp_rtcp); + + // Estimates the NTP timestamp in local timebase from |rtp_timestamp|. + // Returns the NTP timestamp in ms when success. -1 if failed. + int64_t Estimate(uint32_t rtp_timestamp); + + private: + Clock* clock_; + scoped_ptr ts_extrapolator_; + RtcpList rtcp_list_; + DISALLOW_COPY_AND_ASSIGN(RemoteNtpTimeEstimator); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_REMOTE_NTP_TIME_ESTIMATOR_H_ diff --git a/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc b/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc new file mode 100644 index 000000000..0d71c26b6 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h" + +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/timestamp_extrapolator.h" + +namespace webrtc { + +// TODO(wu): Refactor this class so that it can be shared with +// vie_sync_module.cc. +RemoteNtpTimeEstimator::RemoteNtpTimeEstimator(Clock* clock) + : clock_(clock), + ts_extrapolator_( + new TimestampExtrapolator(clock_->TimeInMilliseconds())) { +} + +RemoteNtpTimeEstimator::~RemoteNtpTimeEstimator() {} + +bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(uint32_t ssrc, + RtpRtcp* rtp_rtcp) { + assert(rtp_rtcp); + uint16_t rtt = 0; + rtp_rtcp->RTT(ssrc, &rtt, NULL, NULL, NULL); + if (rtt == 0) { + // Waiting for valid rtt. + return true; + } + // Update RTCP list + uint32_t ntp_secs = 0; + uint32_t ntp_frac = 0; + uint32_t rtp_timestamp = 0; + if (0 != rtp_rtcp->RemoteNTP(&ntp_secs, + &ntp_frac, + NULL, + NULL, + &rtp_timestamp)) { + // Waiting for RTCP. + return true; + } + bool new_rtcp_sr = false; + if (!UpdateRtcpList( + ntp_secs, ntp_frac, rtp_timestamp, &rtcp_list_, &new_rtcp_sr)) { + return false; + } + if (!new_rtcp_sr) { + // No new RTCP SR since last time this function was called. + return true; + } + // Update extrapolator with the new arrival time. + // The extrapolator assumes the TimeInMilliseconds time. + int64_t receiver_arrival_time_ms = clock_->TimeInMilliseconds(); + int64_t sender_send_time_ms = Clock::NtpToMs(ntp_secs, ntp_frac); + int64_t sender_arrival_time_90k = (sender_send_time_ms + rtt / 2) * 90; + ts_extrapolator_->Update(receiver_arrival_time_ms, sender_arrival_time_90k); + return true; +} + +int64_t RemoteNtpTimeEstimator::Estimate(uint32_t rtp_timestamp) { + if (rtcp_list_.size() < 2) { + // We need two RTCP SR reports to calculate NTP. + return -1; + } + int64_t sender_capture_ntp_ms = 0; + if (!RtpToNtpMs(rtp_timestamp, rtcp_list_, &sender_capture_ntp_ms)) { + return -1; + } + uint32_t timestamp = sender_capture_ntp_ms * 90; + int64_t receiver_capture_ms = + ts_extrapolator_->ExtrapolateLocalTime(timestamp); + int64_t ntp_offset = + clock_->CurrentNtpInMilliseconds() - clock_->TimeInMilliseconds(); + return receiver_capture_ms + ntp_offset; +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc b/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc new file mode 100644 index 000000000..63cedf03a --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc @@ -0,0 +1,112 @@ +/* +* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +* +* Use of this source code is governed by a BSD-style license +* that can be found in the LICENSE file in the root of the source +* tree. An additional intellectual property rights grant can be found +* in the file PATENTS. All contributing project authors may +* be found in the AUTHORS file in the root of the source tree. +*/ + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h" +#include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; + +namespace webrtc { + +static const int kTestRtt = 10; +static const int64_t kLocalClockInitialTimeMs = 123; +static const int64_t kRemoteClockInitialTimeMs = 345; +static const uint32_t kTimestampOffset = 567; +static const int kTestSsrc = 789; + +class RemoteNtpTimeEstimatorTest : public ::testing::Test { + protected: + RemoteNtpTimeEstimatorTest() + : local_clock_(kLocalClockInitialTimeMs * 1000), + remote_clock_(kRemoteClockInitialTimeMs * 1000), + estimator_(&local_clock_) {} + ~RemoteNtpTimeEstimatorTest() {} + + void AdvanceTimeMilliseconds(int64_t ms) { + local_clock_.AdvanceTimeMilliseconds(ms); + remote_clock_.AdvanceTimeMilliseconds(ms); + } + + uint32_t GetRemoteTimestamp() { + return static_cast(remote_clock_.TimeInMilliseconds()) * 90 + + kTimestampOffset; + } + + void SendRtcpSr() { + uint32_t rtcp_timestamp = GetRemoteTimestamp(); + uint32_t ntp_seconds; + uint32_t ntp_fractions; + remote_clock_.CurrentNtp(ntp_seconds, ntp_fractions); + + AdvanceTimeMilliseconds(kTestRtt / 2); + ReceiveRtcpSr(rtcp_timestamp, ntp_seconds, ntp_fractions); + } + + void UpdateRtcpTimestamp(MockRtpRtcp* rtp_rtcp, bool expected_result) { + if (rtp_rtcp) { + EXPECT_CALL(*rtp_rtcp, RTT(_, _, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(kTestRtt), + Return(0))); + } + EXPECT_EQ(expected_result, + estimator_.UpdateRtcpTimestamp(kTestSsrc, rtp_rtcp)); + } + + void ReceiveRtcpSr(uint32_t rtcp_timestamp, + uint32_t ntp_seconds, + uint32_t ntp_fractions) { + EXPECT_CALL(rtp_rtcp_, RemoteNTP(_, _, _, _, _)) + .WillOnce(DoAll(SetArgPointee<0>(ntp_seconds), + SetArgPointee<1>(ntp_fractions), + SetArgPointee<4>(rtcp_timestamp), + Return(0))); + + UpdateRtcpTimestamp(&rtp_rtcp_, true); + } + + SimulatedClock local_clock_; + SimulatedClock remote_clock_; + MockRtpRtcp rtp_rtcp_; + RemoteNtpTimeEstimator estimator_; +}; + +TEST_F(RemoteNtpTimeEstimatorTest, Estimate) { + // Failed without any RTCP SR, where RemoteNTP returns without valid NTP. + EXPECT_CALL(rtp_rtcp_, RemoteNTP(_, _, _, _, _)).WillOnce(Return(0)); + UpdateRtcpTimestamp(&rtp_rtcp_, false); + + AdvanceTimeMilliseconds(1000); + // Remote peer sends first RTCP SR. + SendRtcpSr(); + + // Remote sends a RTP packet. + AdvanceTimeMilliseconds(15); + uint32_t rtp_timestamp = GetRemoteTimestamp(); + int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds(); + + // Local peer needs at least 2 RTCP SR to calculate the capture time. + const int64_t kNotEnoughRtcpSr = -1; + EXPECT_EQ(kNotEnoughRtcpSr, estimator_.Estimate(rtp_timestamp)); + + AdvanceTimeMilliseconds(800); + // Remote sends second RTCP SR. + SendRtcpSr(); + + // Local peer gets enough RTCP SR to calculate the capture time. + EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp)); +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi b/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi index 0a8c901e5..dcd659880 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi @@ -20,6 +20,7 @@ # Common '../interface/fec_receiver.h', '../interface/receive_statistics.h', + '../interface/remote_ntp_time_estimator.h', '../interface/rtp_header_parser.h', '../interface/rtp_payload_registry.h', '../interface/rtp_receiver.h', @@ -32,6 +33,7 @@ 'fec_receiver_impl.h', 'receive_statistics_impl.cc', 'receive_statistics_impl.h', + 'remote_ntp_time_estimator.cc', 'rtp_header_parser.cc', 'rtp_rtcp_config.h', 'rtp_rtcp_impl.cc', diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc index 0fd6cda41..62f4f7e1d 100644 --- a/webrtc/video_engine/vie_receiver.cc +++ b/webrtc/video_engine/vie_receiver.cc @@ -15,6 +15,7 @@ #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" +#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h" @@ -25,6 +26,7 @@ #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/system_wrappers/interface/timestamp_extrapolator.h" +#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { @@ -45,9 +47,7 @@ ViEReceiver::ViEReceiver(const int32_t channel_id, rtp_rtcp_(NULL), vcm_(module_vcm), remote_bitrate_estimator_(remote_bitrate_estimator), - clock_(Clock::GetRealTimeClock()), - ts_extrapolator_( - new TimestampExtrapolator(clock_->TimeInMilliseconds())), + ntp_estimator_(new RemoteNtpTimeEstimator(Clock::GetRealTimeClock())), rtp_dump_(NULL), receiving_(false), restored_packet_in_use_(false), @@ -175,7 +175,8 @@ int32_t ViEReceiver::OnReceivedPayloadData( const uint8_t* payload_data, const uint16_t payload_size, const WebRtcRTPHeader* rtp_header) { WebRtcRTPHeader rtp_header_with_ntp = *rtp_header; - CalculateCaptureNtpTime(&rtp_header_with_ntp); + rtp_header_with_ntp.ntp_time_ms = + ntp_estimator_->Estimate(rtp_header->header.timestamp); if (vcm_->IncomingPacket(payload_data, payload_size, rtp_header_with_ntp) != 0) { @@ -185,26 +186,6 @@ int32_t ViEReceiver::OnReceivedPayloadData( return 0; } -void ViEReceiver::CalculateCaptureNtpTime(WebRtcRTPHeader* rtp_header) { - if (rtcp_list_.size() < 2) { - // We need two RTCP SR reports to calculate NTP. - return; - } - - int64_t sender_capture_ntp_ms = 0; - if (!RtpToNtpMs(rtp_header->header.timestamp, - rtcp_list_, - &sender_capture_ntp_ms)) { - return; - } - uint32_t timestamp = sender_capture_ntp_ms * 90; - int64_t receiver_capture_ms = - ts_extrapolator_->ExtrapolateLocalTime(timestamp); - int64_t ntp_offset = - clock_->CurrentNtpInMilliseconds() - clock_->TimeInMilliseconds(); - rtp_header->ntp_time_ms = receiver_capture_ms + ntp_offset; -} - bool ViEReceiver::OnRecoveredPacket(const uint8_t* rtp_packet, int rtp_packet_length) { RTPHeader header; @@ -352,56 +333,13 @@ int ViEReceiver::InsertRTCPPacket(const uint8_t* rtcp_packet, return ret; } - if (!GetRtcpTimestamp()) { + if (!ntp_estimator_->UpdateRtcpTimestamp(rtp_receiver_->SSRC(), rtp_rtcp_)) { LOG(LS_WARNING) << "Failed to retrieve timestamp information from RTCP SR."; } return 0; } -bool ViEReceiver::GetRtcpTimestamp() { - uint16_t rtt = 0; - rtp_rtcp_->RTT(rtp_receiver_->SSRC(), &rtt, NULL, NULL, NULL); - if (rtt == 0) { - // Waiting for valid rtt. - return true; - } - - // Update RTCP list - uint32_t ntp_secs = 0; - uint32_t ntp_frac = 0; - uint32_t rtp_timestamp = 0; - if (0 != rtp_rtcp_->RemoteNTP(&ntp_secs, - &ntp_frac, - NULL, - NULL, - &rtp_timestamp)) { - return false; - } - - bool new_rtcp_sr = false; - if (!UpdateRtcpList(ntp_secs, - ntp_frac, - rtp_timestamp, - &rtcp_list_, - &new_rtcp_sr)) { - return false; - } - - if (!new_rtcp_sr) { - // No new RTCP SR since last time this function was called. - return true; - } - - // Update extrapolator with the new arrival time. - // The extrapolator assumes the TimeInMilliseconds time. - int64_t receiver_arrival_time = clock_->TimeInMilliseconds(); - int64_t sender_send_time_ms = Clock::NtpToMs(ntp_secs, ntp_frac); - int64_t sender_arrival_time_90k = (sender_send_time_ms + rtt / 2) * 90; - ts_extrapolator_->Update(receiver_arrival_time, sender_arrival_time_90k); - return true; -} - void ViEReceiver::StartReceive() { CriticalSectionScoped cs(receive_cs_.get()); receiving_ = true; diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h index dfd4459ac..db27fc96d 100644 --- a/webrtc/video_engine/vie_receiver.h +++ b/webrtc/video_engine/vie_receiver.h @@ -16,7 +16,6 @@ #include "webrtc/engine_configurations.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" -#include "webrtc/system_wrappers/interface/rtp_to_ntp.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" #include "webrtc/video_engine/include/vie_network.h" @@ -26,6 +25,7 @@ namespace webrtc { class CriticalSectionWrapper; class FecReceiver; +class RemoteNtpTimeEstimator; class ReceiveStatistics; class RemoteBitrateEstimator; class RtpDump; @@ -33,7 +33,6 @@ class RtpHeaderParser; class RTPPayloadRegistry; class RtpReceiver; class RtpRtcp; -class TimestampExtrapolator; class VideoCodingModule; struct ReceiveBandwidthEstimatorStats; @@ -105,9 +104,6 @@ class ViEReceiver : public RtpData { bool IsPacketInOrder(const RTPHeader& header) const; bool IsPacketRetransmitted(const RTPHeader& header, bool in_order) const; - bool GetRtcpTimestamp(); - void CalculateCaptureNtpTime(WebRtcRTPHeader* rtp_header); - scoped_ptr receive_cs_; scoped_ptr rtp_header_parser_; scoped_ptr rtp_payload_registry_; @@ -119,9 +115,7 @@ class ViEReceiver : public RtpData { VideoCodingModule* vcm_; RemoteBitrateEstimator* remote_bitrate_estimator_; - Clock* clock_; - scoped_ptr ts_extrapolator_; - RtcpList rtcp_list_; + scoped_ptr ntp_estimator_; RtpDump* rtp_dump_; bool receiving_;