diff --git a/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc b/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc index 723064eeba..b1602e5dcd 100644 --- a/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc +++ b/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc @@ -40,21 +40,21 @@ bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(int64_t rtt, uint32_t ntp_secs, uint32_t ntp_frac, uint32_t rtp_timestamp) { - bool new_rtcp_sr = false; - if (!rtp_to_ntp_.UpdateMeasurements(ntp_secs, ntp_frac, rtp_timestamp, - &new_rtcp_sr)) { - return false; - } - if (!new_rtcp_sr) { - // No new RTCP SR since last time this function was called. - return true; + NtpTime sender_send_time(ntp_secs, ntp_frac); + switch (rtp_to_ntp_.UpdateMeasurements(sender_send_time, rtp_timestamp)) { + case RtpToNtpEstimator::kInvalidMeasurement: + return false; + case RtpToNtpEstimator::kSameMeasurement: + // No new RTCP SR since last time this function was called. + return true; + case RtpToNtpEstimator::kNewMeasurement: + break; } // Update extrapolator with the new arrival time. // The extrapolator assumes the ntp time. int64_t receiver_arrival_time_ms = clock_->CurrentNtpInMilliseconds(); - int64_t sender_send_time_ms = NtpTime(ntp_secs, ntp_frac).ToMs(); - int64_t sender_arrival_time_ms = sender_send_time_ms + rtt / 2; + int64_t sender_arrival_time_ms = sender_send_time.ToMs() + rtt / 2; int64_t remote_to_local_clocks_offset = receiver_arrival_time_ms - sender_arrival_time_ms; ntp_clocks_offset_estimator_.Insert(remote_to_local_clocks_offset); @@ -62,10 +62,11 @@ bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(int64_t rtt, } int64_t RemoteNtpTimeEstimator::Estimate(uint32_t rtp_timestamp) { - int64_t sender_capture_ntp_ms = 0; - if (!rtp_to_ntp_.Estimate(rtp_timestamp, &sender_capture_ntp_ms)) { + NtpTime sender_capture = rtp_to_ntp_.Estimate(rtp_timestamp); + if (!sender_capture.Valid()) { return -1; } + int64_t sender_capture_ntp_ms = sender_capture.ToMs(); int64_t remote_to_local_clocks_offset = ntp_clocks_offset_estimator_.GetFilteredValue(); diff --git a/system_wrappers/include/rtp_to_ntp_estimator.h b/system_wrappers/include/rtp_to_ntp_estimator.h index 175063351a..3b62b78608 100644 --- a/system_wrappers/include/rtp_to_ntp_estimator.h +++ b/system_wrappers/include/rtp_to_ntp_estimator.h @@ -18,60 +18,51 @@ #include "absl/types/optional.h" #include "modules/include/module_common_types_public.h" #include "rtc_base/checks.h" -#include "rtc_base/numerics/moving_median_filter.h" #include "system_wrappers/include/ntp_time.h" namespace webrtc { -// Class for converting an RTP timestamp to the NTP domain in milliseconds. + +// Converts an RTP timestamp to the NTP domain. // The class needs to be trained with (at least 2) RTP/NTP timestamp pairs from // RTCP sender reports before the convertion can be done. class RtpToNtpEstimator { public: - RtpToNtpEstimator(); - ~RtpToNtpEstimator(); + static constexpr int kMaxInvalidSamples = 3; + + RtpToNtpEstimator() = default; + RtpToNtpEstimator(const RtpToNtpEstimator&) = delete; + RtpToNtpEstimator& operator=(const RtpToNtpEstimator&) = delete; + ~RtpToNtpEstimator() = default; + + enum UpdateResult { kInvalidMeasurement, kSameMeasurement, kNewMeasurement }; + // Updates measurements with RTP/NTP timestamp pair from a RTCP sender report. + UpdateResult UpdateMeasurements(NtpTime ntp, uint32_t rtp_timestamp); + + // Converts an RTP timestamp to the NTP domain. + // Returns invalid NtpTime (i.e. NtpTime(0)) on failure. + NtpTime Estimate(uint32_t rtp_timestamp) const; + + // Returns estimated rtp_timestamp frequency, or 0 on failure. + double EstimatedFrequencyKhz() const; + + private: + // Estimated parameters from RTP and NTP timestamp pairs in `measurements_`. + // Defines linear estimation: NtpTime (in units of 1s/2^32) = + // `Parameters::slope` * rtp_timestamp + `Parameters::offset`. + struct Parameters { + double slope; + double offset; + }; // RTP and NTP timestamp pair from a RTCP SR report. struct RtcpMeasurement { - RtcpMeasurement(uint32_t ntp_secs, - uint32_t ntp_frac, - int64_t unwrapped_timestamp); - bool IsEqual(const RtcpMeasurement& other) const; - NtpTime ntp_time; int64_t unwrapped_rtp_timestamp; }; - // Estimated parameters from RTP and NTP timestamp pairs in `measurements_`. - struct Parameters { - Parameters() : frequency_khz(0.0), offset_ms(0.0) {} - - Parameters(double frequency_khz, double offset_ms) - : frequency_khz(frequency_khz), offset_ms(offset_ms) {} - - double frequency_khz; - double offset_ms; - }; - - // Updates measurements with RTP/NTP timestamp pair from a RTCP sender report. - // `new_rtcp_sr` is set to true if a new report is added. - bool UpdateMeasurements(uint32_t ntp_secs, - uint32_t ntp_frac, - uint32_t rtp_timestamp, - bool* new_rtcp_sr); - - // Converts an RTP timestamp to the NTP domain in milliseconds. - // Returns true on success, false otherwise. - bool Estimate(int64_t rtp_timestamp, int64_t* ntp_timestamp_ms) const; - - // Returns estimated rtp to ntp linear transform parameters. - const absl::optional params() const; - - static const int kMaxInvalidSamples = 3; - - private: void UpdateParameters(); - int consecutive_invalid_samples_; + int consecutive_invalid_samples_ = 0; std::list measurements_; absl::optional params_; mutable TimestampUnwrapper unwrapper_; diff --git a/system_wrappers/source/rtp_to_ntp_estimator.cc b/system_wrappers/source/rtp_to_ntp_estimator.cc index d0b0ad447f..ef5d9a7508 100644 --- a/system_wrappers/source/rtp_to_ntp_estimator.cc +++ b/system_wrappers/source/rtp_to_ntp_estimator.cc @@ -18,132 +18,85 @@ #include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" namespace webrtc { namespace { // Maximum number of RTCP SR reports to use to map between RTP and NTP. -const size_t kNumRtcpReportsToUse = 20; +constexpr size_t kNumRtcpReportsToUse = 20; // Don't allow NTP timestamps to jump more than 1 hour. Chosen arbitrary as big // enough to not affect normal use-cases. Yet it is smaller than RTP wrap-around // half-period (90khz RTP clock wrap-arounds every 13.25 hours). After half of // wrap-around period it is impossible to unwrap RTP timestamps correctly. -const int kMaxAllowedRtcpNtpIntervalMs = 60 * 60 * 1000; +constexpr uint64_t kMaxAllowedRtcpNtpInterval = uint64_t{60 * 60} << 32; +} // namespace -bool Contains(const std::list& measurements, - const RtpToNtpEstimator::RtcpMeasurement& other) { - for (const auto& measurement : measurements) { - if (measurement.IsEqual(other)) - return true; - } - return false; -} - -// Given x[] and y[] writes out such k and b that line y=k*x+b approximates -// given points in the best way (Least Squares Method). -bool LinearRegression(rtc::ArrayView x, - rtc::ArrayView y, - double* k, - double* b) { - size_t n = x.size(); +void RtpToNtpEstimator::UpdateParameters() { + size_t n = measurements_.size(); if (n < 2) - return false; + return; - if (y.size() != n) - return false; + // Run linear regression: + // Given x[] and y[] writes out such k and b that line y=k*x+b approximates + // given points in the best way (Least Squares Method). + auto x = [](const RtcpMeasurement& m) { + return static_cast(m.unwrapped_rtp_timestamp); + }; + auto y = [](const RtcpMeasurement& m) { + return static_cast(static_cast(m.ntp_time)); + }; double avg_x = 0; double avg_y = 0; - for (size_t i = 0; i < n; ++i) { - avg_x += x[i]; - avg_y += y[i]; + for (const RtcpMeasurement& m : measurements_) { + avg_x += x(m); + avg_y += y(m); } avg_x /= n; avg_y /= n; double variance_x = 0; double covariance_xy = 0; - for (size_t i = 0; i < n; ++i) { - double normalized_x = x[i] - avg_x; - double normalized_y = y[i] - avg_y; + for (const RtcpMeasurement& m : measurements_) { + double normalized_x = x(m) - avg_x; + double normalized_y = y(m) - avg_y; variance_x += normalized_x * normalized_x; covariance_xy += normalized_x * normalized_y; } if (std::fabs(variance_x) < 1e-8) - return false; - - *k = static_cast(covariance_xy / variance_x); - *b = static_cast(avg_y - (*k) * avg_x); - return true; -} - -} // namespace - -RtpToNtpEstimator::RtcpMeasurement::RtcpMeasurement(uint32_t ntp_secs, - uint32_t ntp_frac, - int64_t unwrapped_timestamp) - : ntp_time(ntp_secs, ntp_frac), - unwrapped_rtp_timestamp(unwrapped_timestamp) {} - -bool RtpToNtpEstimator::RtcpMeasurement::IsEqual( - const RtcpMeasurement& other) const { - // Use || since two equal timestamps will result in zero frequency and in - // RtpToNtpMs, `rtp_timestamp_ms` is estimated by dividing by the frequency. - return (ntp_time == other.ntp_time) || - (unwrapped_rtp_timestamp == other.unwrapped_rtp_timestamp); -} - -// Class for converting an RTP timestamp to the NTP domain. -RtpToNtpEstimator::RtpToNtpEstimator() : consecutive_invalid_samples_(0) {} - -RtpToNtpEstimator::~RtpToNtpEstimator() {} - -void RtpToNtpEstimator::UpdateParameters() { - if (measurements_.size() < 2) return; - std::vector x; - std::vector y; - x.reserve(measurements_.size()); - y.reserve(measurements_.size()); - for (auto it = measurements_.begin(); it != measurements_.end(); ++it) { - x.push_back(it->unwrapped_rtp_timestamp); - y.push_back(it->ntp_time.ToMs()); - } - double slope, offset; - - if (!LinearRegression(x, y, &slope, &offset)) { - return; - } - - params_.emplace(1 / slope, offset); + double k = covariance_xy / variance_x; + double b = avg_y - k * avg_x; + params_ = {{.slope = k, .offset = b}}; } -bool RtpToNtpEstimator::UpdateMeasurements(uint32_t ntp_secs, - uint32_t ntp_frac, - uint32_t rtp_timestamp, - bool* new_rtcp_sr) { - *new_rtcp_sr = false; - +RtpToNtpEstimator::UpdateResult RtpToNtpEstimator::UpdateMeasurements( + NtpTime ntp, + uint32_t rtp_timestamp) { int64_t unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp); - RtcpMeasurement new_measurement(ntp_secs, ntp_frac, unwrapped_rtp_timestamp); + RtcpMeasurement new_measurement = { + .ntp_time = ntp, .unwrapped_rtp_timestamp = unwrapped_rtp_timestamp}; - if (Contains(measurements_, new_measurement)) { - // RTCP SR report already added. - return true; + for (const RtcpMeasurement& measurement : measurements_) { + // Use || since two equal timestamps will result in zero frequency. + if (measurement.ntp_time == ntp || + measurement.unwrapped_rtp_timestamp == unwrapped_rtp_timestamp) { + return kSameMeasurement; + } } if (!new_measurement.ntp_time.Valid()) - return false; + return kInvalidMeasurement; - int64_t ntp_ms_new = new_measurement.ntp_time.ToMs(); + uint64_t ntp_new = static_cast(new_measurement.ntp_time); bool invalid_sample = false; if (!measurements_.empty()) { int64_t old_rtp_timestamp = measurements_.front().unwrapped_rtp_timestamp; - int64_t old_ntp_ms = measurements_.front().ntp_time.ToMs(); - if (ntp_ms_new <= old_ntp_ms || - ntp_ms_new > old_ntp_ms + kMaxAllowedRtcpNtpIntervalMs) { + uint64_t old_ntp = static_cast(measurements_.front().ntp_time); + if (ntp_new <= old_ntp || ntp_new > old_ntp + kMaxAllowedRtcpNtpInterval) { invalid_sample = true; } else if (unwrapped_rtp_timestamp <= old_rtp_timestamp) { RTC_LOG(LS_WARNING) @@ -158,7 +111,7 @@ bool RtpToNtpEstimator::UpdateMeasurements(uint32_t ntp_secs, if (invalid_sample) { ++consecutive_invalid_samples_; if (consecutive_invalid_samples_ < kMaxInvalidSamples) { - return false; + return kInvalidMeasurement; } RTC_LOG(LS_WARNING) << "Multiple consecutively invalid RTCP SR reports, " "clearing measurements."; @@ -172,37 +125,29 @@ bool RtpToNtpEstimator::UpdateMeasurements(uint32_t ntp_secs, measurements_.pop_back(); measurements_.push_front(new_measurement); - *new_rtcp_sr = true; // List updated, calculate new parameters. UpdateParameters(); - return true; + return kNewMeasurement; } -bool RtpToNtpEstimator::Estimate(int64_t rtp_timestamp, - int64_t* ntp_timestamp_ms) const { +NtpTime RtpToNtpEstimator::Estimate(uint32_t rtp_timestamp) const { if (!params_) - return false; + return NtpTime(); - int64_t rtp_timestamp_unwrapped = unwrapper_.Unwrap(rtp_timestamp); + double estimated = + static_cast(unwrapper_.Unwrap(rtp_timestamp)) * params_->slope + + params_->offset + 0.5f; - // params_calculated_ should not be true unless ms params.frequency_khz has - // been calculated to something non zero. - RTC_DCHECK_NE(params_->frequency_khz, 0.0); - double rtp_ms = - static_cast(rtp_timestamp_unwrapped) / params_->frequency_khz + - params_->offset_ms + 0.5f; - - if (rtp_ms < 0) - return false; - - *ntp_timestamp_ms = rtp_ms; - - return true; + return NtpTime(rtc::saturated_cast(estimated)); } -const absl::optional RtpToNtpEstimator::params() - const { - return params_; +double RtpToNtpEstimator::EstimatedFrequencyKhz() const { + if (!params_.has_value()) { + return 0.0; + } + static constexpr double kNtpUnitPerMs = 4.294967296E6; // 2^32 / 1000. + return kNtpUnitPerMs / params_->slope; } + } // namespace webrtc diff --git a/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc b/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc index 14bc6e0a89..29de76178b 100644 --- a/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc +++ b/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc @@ -17,332 +17,249 @@ namespace webrtc { namespace { -const uint32_t kOneMsInNtpFrac = 4294967; -const uint32_t kOneHourInNtpSec = 60 * 60; -const uint32_t kTimestampTicksPerMs = 90; +constexpr uint64_t kOneMsInNtp = 4294967; +constexpr uint64_t kOneHourInNtp = uint64_t{60 * 60} << 32; +constexpr uint32_t kTimestampTicksPerMs = 90; } // namespace TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp) { RtpToNtpEstimator estimator; - bool new_sr; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 1; - uint32_t timestamp = 0; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(kOneMsInNtp), 0), + RtpToNtpEstimator::kNewMeasurement); // No wraparound will be detected, since we are not allowed to wrap below 0, // but there will be huge rtp timestamp jump, e.g. old_timestamp = 0, // new_timestamp = 4294967295, which should be detected. - EXPECT_FALSE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 * kOneMsInNtp), + -kTimestampTicksPerMs), + RtpToNtpEstimator::kInvalidMeasurement); } TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp_Wraparound_Detected) { RtpToNtpEstimator estimator; - bool new_sr; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 1; - uint32_t timestamp = 0xFFFFFFFE; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += 2 * kOneMsInNtpFrac; - timestamp += 2 * kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFE), + RtpToNtpEstimator::kNewMeasurement); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + 2 * kOneMsInNtp), + 0xFFFFFFFE + 2 * kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); // Expected to fail since the older RTCP has a smaller RTP timestamp than the // newer (old:10, new:4294967206). - EXPECT_FALSE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + 3 * kOneMsInNtp), + 0xFFFFFFFE + kTimestampTicksPerMs), + RtpToNtpEstimator::kInvalidMeasurement); } TEST(WrapAroundTests, NewRtcpWrapped) { RtpToNtpEstimator estimator; - bool new_sr; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 1; - uint32_t timestamp = 0xFFFFFFFF; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - int64_t timestamp_ms = -1; - EXPECT_TRUE(estimator.Estimate(0xFFFFFFFF, ×tamp_ms)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF), + RtpToNtpEstimator::kNewMeasurement); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp), + 0xFFFFFFFF + kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); // Since this RTP packet has the same timestamp as the RTCP packet constructed // at time 0 it should be mapped to 0 as well. - EXPECT_EQ(0, timestamp_ms); + EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1)); } TEST(WrapAroundTests, RtpWrapped) { RtpToNtpEstimator estimator; - bool new_sr; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 1; - uint32_t timestamp = 0xFFFFFFFF - 2 * kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), + 0xFFFFFFFF - 2 * kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp), + 0xFFFFFFFF - kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); - int64_t timestamp_ms = -1; - EXPECT_TRUE( - estimator.Estimate(0xFFFFFFFF - 2 * kTimestampTicksPerMs, ×tamp_ms)); // Since this RTP packet has the same timestamp as the RTCP packet constructed // at time 0 it should be mapped to 0 as well. - EXPECT_EQ(0, timestamp_ms); + EXPECT_EQ(estimator.Estimate(0xFFFFFFFF - 2 * kTimestampTicksPerMs), + NtpTime(1)); // Two kTimestampTicksPerMs advanced. - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); - EXPECT_EQ(2, timestamp_ms); + EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1 + 2 * kOneMsInNtp)); // Wrapped rtp. - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); - EXPECT_EQ(3, timestamp_ms); + EXPECT_EQ(estimator.Estimate(0xFFFFFFFF + kTimestampTicksPerMs), + NtpTime(1 + 3 * kOneMsInNtp)); } TEST(WrapAroundTests, OldRtp_RtcpsWrapped) { RtpToNtpEstimator estimator; - bool new_sr; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 1; - uint32_t timestamp = 0xFFFFFFFF; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - timestamp -= 2 * kTimestampTicksPerMs; - int64_t timestamp_ms = 0xFFFFFFFF; - EXPECT_FALSE(estimator.Estimate(timestamp, ×tamp_ms)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF), + RtpToNtpEstimator::kNewMeasurement); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp), + 0xFFFFFFFF + kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); + + EXPECT_FALSE(estimator.Estimate(0xFFFFFFFF - kTimestampTicksPerMs).Valid()); } TEST(WrapAroundTests, OldRtp_NewRtcpWrapped) { RtpToNtpEstimator estimator; - bool new_sr; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 1; - uint32_t timestamp = 0xFFFFFFFF; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - timestamp -= kTimestampTicksPerMs; - int64_t timestamp_ms = -1; - EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF), + RtpToNtpEstimator::kNewMeasurement); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp), + 0xFFFFFFFF + kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); + // Constructed at the same time as the first RTCP and should therefore be // mapped to zero. - EXPECT_EQ(0, timestamp_ms); + EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1)); } TEST(WrapAroundTests, GracefullyHandleRtpJump) { RtpToNtpEstimator estimator; - bool new_sr; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 1; - uint32_t timestamp = 0; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; - int64_t timestamp_ms = -1; - EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF), + RtpToNtpEstimator::kNewMeasurement); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp), + 0xFFFFFFFF + kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); + // Constructed at the same time as the first RTCP and should therefore be // mapped to zero. - EXPECT_EQ(0, timestamp_ms); + EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1)); - timestamp -= 0xFFFFF; + uint32_t timestamp = 0xFFFFFFFF - 0xFFFFF; + uint64_t ntp_raw = 1 + 2 * kOneMsInNtp; for (int i = 0; i < RtpToNtpEstimator::kMaxInvalidSamples - 1; ++i) { - EXPECT_FALSE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp), + RtpToNtpEstimator::kInvalidMeasurement); + ntp_raw += kOneMsInNtp; timestamp += kTimestampTicksPerMs; } - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - ntp_frac += kOneMsInNtpFrac; + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp), + RtpToNtpEstimator::kNewMeasurement); + ntp_raw += kOneMsInNtp; timestamp += kTimestampTicksPerMs; + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp), + RtpToNtpEstimator::kNewMeasurement); - timestamp_ms = -1; - EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); - // 6 milliseconds has passed since the start of the test. - EXPECT_EQ(6, timestamp_ms); + EXPECT_EQ(estimator.Estimate(timestamp), NtpTime(ntp_raw)); } TEST(UpdateRtcpMeasurementTests, FailsForZeroNtp) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 0; - uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_FALSE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_FALSE(new_sr); + + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(0), 0x12345678), + RtpToNtpEstimator::kInvalidMeasurement); } TEST(UpdateRtcpMeasurementTests, FailsForEqualNtp) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 699925050; + NtpTime ntp(0, 699925050); uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); + + EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp), + RtpToNtpEstimator::kNewMeasurement); // Ntp time already added, list not updated. - ++timestamp; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_FALSE(new_sr); + EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp + 1), + RtpToNtpEstimator::kSameMeasurement); } TEST(UpdateRtcpMeasurementTests, FailsForOldNtp) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 1; - uint32_t ntp_frac = 699925050; + uint64_t ntp_raw = 699925050; + NtpTime ntp(ntp_raw); uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); + EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp), + RtpToNtpEstimator::kNewMeasurement); + // Old ntp time, list not updated. - ntp_frac -= kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_FALSE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw - kOneMsInNtp), + timestamp + kTimestampTicksPerMs), + RtpToNtpEstimator::kInvalidMeasurement); } TEST(UpdateRtcpMeasurementTests, FailsForTooNewNtp) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 1; - uint32_t ntp_frac = 699925050; + + uint64_t ntp_raw = 699925050; uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp), + RtpToNtpEstimator::kNewMeasurement); + // Ntp time from far future, list not updated. - ntp_sec += kOneHourInNtpSec * 2; - timestamp += kTimestampTicksPerMs * 10; - EXPECT_FALSE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw + 2 * kOneHourInNtp), + timestamp + 10 * kTimestampTicksPerMs), + RtpToNtpEstimator::kInvalidMeasurement); } TEST(UpdateRtcpMeasurementTests, FailsForEqualTimestamp) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 2; + uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2), timestamp), + RtpToNtpEstimator::kNewMeasurement); // Timestamp already added, list not updated. - ++ntp_frac; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_FALSE(new_sr); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(3), timestamp), + RtpToNtpEstimator::kSameMeasurement); } TEST(UpdateRtcpMeasurementTests, FailsForOldRtpTimestamp) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 2; uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); + + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2), timestamp), + RtpToNtpEstimator::kNewMeasurement); // Old timestamp, list not updated. - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; - EXPECT_FALSE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_FALSE(new_sr); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 + kOneMsInNtp), + timestamp - kTimestampTicksPerMs), + RtpToNtpEstimator::kInvalidMeasurement); } TEST(UpdateRtcpMeasurementTests, VerifyParameters) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 1; - uint32_t ntp_frac = 2; uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); - EXPECT_FALSE(estimator.params()); + + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(kOneMsInNtp), timestamp), + RtpToNtpEstimator::kNewMeasurement); + + EXPECT_DOUBLE_EQ(estimator.EstimatedFrequencyKhz(), 0.0); + // Add second report, parameters should be calculated. - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(estimator.params()); - EXPECT_DOUBLE_EQ(90.0, estimator.params()->frequency_khz); - EXPECT_NE(0.0, estimator.params()->offset_ms); + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 * kOneMsInNtp), + timestamp + kTimestampTicksPerMs), + RtpToNtpEstimator::kNewMeasurement); + + EXPECT_NEAR(estimator.EstimatedFrequencyKhz(), kTimestampTicksPerMs, 0.01); } TEST(RtpToNtpTests, FailsForNoParameters) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 1; - uint32_t ntp_frac = 2; uint32_t timestamp = 0x12345678; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); + + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), timestamp), + RtpToNtpEstimator::kNewMeasurement); // Parameters are not calculated, conversion of RTP to NTP time should fail. - EXPECT_FALSE(estimator.params()); - int64_t timestamp_ms = -1; - EXPECT_FALSE(estimator.Estimate(timestamp, ×tamp_ms)); + EXPECT_DOUBLE_EQ(estimator.EstimatedFrequencyKhz(), 0.0); + EXPECT_FALSE(estimator.Estimate(timestamp).Valid()); } TEST(RtpToNtpTests, AveragesErrorOut) { RtpToNtpEstimator estimator; - uint32_t ntp_sec = 1; - uint32_t ntp_frac = 90000000; // More than 1 ms. + uint64_t ntp_raw = 90000000; // More than 1 ms. + ASSERT_GT(ntp_raw, kOneMsInNtp); uint32_t timestamp = 0x12345678; - const int kNtpSecStep = 1; // 1 second. - const int kRtpTicksPerMs = 90; - const int kRtpStep = kRtpTicksPerMs * 1000; - bool new_sr; - EXPECT_TRUE( - estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); - EXPECT_TRUE(new_sr); + constexpr uint64_t kNtpSecStep = uint64_t{1} << 32; // 1 second. + constexpr int kRtpTicksPerMs = 90; + constexpr int kRtpStep = kRtpTicksPerMs * 1000; + + EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp), + RtpToNtpEstimator::kNewMeasurement); Random rand(1123536L); for (size_t i = 0; i < 1000; i++) { // Advance both timestamps by exactly 1 second. - ntp_sec += kNtpSecStep; + ntp_raw += kNtpSecStep; timestamp += kRtpStep; // Add upto 1ms of errors to NTP and RTP timestamps passed to estimator. - EXPECT_TRUE(estimator.UpdateMeasurements( - ntp_sec, - ntp_frac + rand.Rand(-static_cast(kOneMsInNtpFrac), - static_cast(kOneMsInNtpFrac)), - timestamp + rand.Rand(-kRtpTicksPerMs, kRtpTicksPerMs), &new_sr)); - EXPECT_TRUE(new_sr); + EXPECT_EQ( + estimator.UpdateMeasurements( + NtpTime(ntp_raw + rand.Rand(-int{kOneMsInNtp}, int{kOneMsInNtp})), + timestamp + rand.Rand(-kRtpTicksPerMs, kRtpTicksPerMs)), + RtpToNtpEstimator::kNewMeasurement); - int64_t estimated_ntp_ms; - EXPECT_TRUE(estimator.Estimate(timestamp, &estimated_ntp_ms)); + NtpTime estimated_ntp = estimator.Estimate(timestamp); + EXPECT_TRUE(estimated_ntp.Valid()); // Allow upto 2 ms of error. - EXPECT_NEAR(NtpTime(ntp_sec, ntp_frac).ToMs(), estimated_ntp_ms, 2); + EXPECT_NEAR(ntp_raw, static_cast(estimated_ntp), 2 * kOneMsInNtp); } } diff --git a/video/rtp_streams_synchronizer2.cc b/video/rtp_streams_synchronizer2.cc index 4096fceb99..0fbb3916cb 100644 --- a/video/rtp_streams_synchronizer2.cc +++ b/video/rtp_streams_synchronizer2.cc @@ -29,10 +29,10 @@ bool UpdateMeasurements(StreamSynchronization::Measurements* stream, const Syncable::Info& info) { stream->latest_timestamp = info.latest_received_capture_timestamp; stream->latest_receive_time_ms = info.latest_receive_time_ms; - bool new_rtcp_sr = false; return stream->rtp_to_ntp.UpdateMeasurements( - info.capture_time_ntp_secs, info.capture_time_ntp_frac, - info.capture_time_source_clock, &new_rtcp_sr); + NtpTime(info.capture_time_ntp_secs, info.capture_time_ntp_frac), + info.capture_time_source_clock) != + RtpToNtpEstimator::kInvalidMeasurement; } } // namespace @@ -183,32 +183,35 @@ bool RtpStreamsSynchronizer::GetStreamSyncOffsetInMs( return false; } - int64_t latest_audio_ntp; - if (!audio_measurement_.rtp_to_ntp.Estimate(audio_rtp_timestamp, - &latest_audio_ntp)) { + NtpTime latest_audio_ntp = + audio_measurement_.rtp_to_ntp.Estimate(audio_rtp_timestamp); + if (!latest_audio_ntp.Valid()) { return false; } + int64_t latest_audio_ntp_ms = latest_audio_ntp.ToMs(); - syncable_audio_->SetEstimatedPlayoutNtpTimestampMs(latest_audio_ntp, time_ms); + syncable_audio_->SetEstimatedPlayoutNtpTimestampMs(latest_audio_ntp_ms, + time_ms); - int64_t latest_video_ntp; - if (!video_measurement_.rtp_to_ntp.Estimate(rtp_timestamp, - &latest_video_ntp)) { + NtpTime latest_video_ntp = + video_measurement_.rtp_to_ntp.Estimate(rtp_timestamp); + if (!latest_video_ntp.Valid()) { return false; } + int64_t latest_video_ntp_ms = latest_video_ntp.ToMs(); // Current audio ntp. int64_t now_ms = rtc::TimeMillis(); - latest_audio_ntp += (now_ms - time_ms); + latest_audio_ntp_ms += (now_ms - time_ms); // Remove video playout delay. int64_t time_to_render_ms = render_time_ms - now_ms; if (time_to_render_ms > 0) - latest_video_ntp -= time_to_render_ms; + latest_video_ntp_ms -= time_to_render_ms; - *video_playout_ntp_ms = latest_video_ntp; - *stream_offset_ms = latest_audio_ntp - latest_video_ntp; - *estimated_freq_khz = video_measurement_.rtp_to_ntp.params()->frequency_khz; + *video_playout_ntp_ms = latest_video_ntp_ms; + *stream_offset_ms = latest_audio_ntp_ms - latest_video_ntp_ms; + *estimated_freq_khz = video_measurement_.rtp_to_ntp.EstimatedFrequencyKhz(); return true; } diff --git a/video/stream_synchronization.cc b/video/stream_synchronization.cc index d5c77c1eca..d86cc79203 100644 --- a/video/stream_synchronization.cc +++ b/video/stream_synchronization.cc @@ -35,19 +35,19 @@ bool StreamSynchronization::ComputeRelativeDelay( const Measurements& audio_measurement, const Measurements& video_measurement, int* relative_delay_ms) { - int64_t audio_last_capture_time_ms; - if (!audio_measurement.rtp_to_ntp.Estimate(audio_measurement.latest_timestamp, - &audio_last_capture_time_ms)) { + NtpTime audio_last_capture_time = + audio_measurement.rtp_to_ntp.Estimate(audio_measurement.latest_timestamp); + if (!audio_last_capture_time.Valid()) { return false; } - int64_t video_last_capture_time_ms; - if (!video_measurement.rtp_to_ntp.Estimate(video_measurement.latest_timestamp, - &video_last_capture_time_ms)) { - return false; - } - if (video_last_capture_time_ms < 0) { + NtpTime video_last_capture_time = + video_measurement.rtp_to_ntp.Estimate(video_measurement.latest_timestamp); + if (!video_last_capture_time.Valid()) { return false; } + int64_t audio_last_capture_time_ms = audio_last_capture_time.ToMs(); + int64_t video_last_capture_time_ms = video_last_capture_time.ToMs(); + // Positive diff means that video_measurement is behind audio_measurement. *relative_delay_ms = video_measurement.latest_receive_time_ms - diff --git a/video/stream_synchronization_unittest.cc b/video/stream_synchronization_unittest.cc index 5c6c79f6f1..b733a1d2cf 100644 --- a/video/stream_synchronization_unittest.cc +++ b/video/stream_synchronization_unittest.cc @@ -47,32 +47,31 @@ class StreamSynchronizationTest : public ::testing::Test { static_cast(kDefaultVideoFrequency * video_clock_drift_ + 0.5); // Generate NTP/RTP timestamp pair for both streams corresponding to RTCP. - bool new_sr; StreamSynchronization::Measurements audio; StreamSynchronization::Measurements video; NtpTime ntp_time = clock_sender_.CurrentNtpTime(); uint32_t rtp_timestamp = clock_sender_.CurrentTime().ms() * audio_frequency / 1000; - EXPECT_TRUE(audio.rtp_to_ntp.UpdateMeasurements( - ntp_time.seconds(), ntp_time.fractions(), rtp_timestamp, &new_sr)); + EXPECT_EQ(audio.rtp_to_ntp.UpdateMeasurements(ntp_time, rtp_timestamp), + RtpToNtpEstimator::kNewMeasurement); clock_sender_.AdvanceTimeMilliseconds(100); clock_receiver_.AdvanceTimeMilliseconds(100); ntp_time = clock_sender_.CurrentNtpTime(); rtp_timestamp = clock_sender_.CurrentTime().ms() * video_frequency / 1000; - EXPECT_TRUE(video.rtp_to_ntp.UpdateMeasurements( - ntp_time.seconds(), ntp_time.fractions(), rtp_timestamp, &new_sr)); + EXPECT_EQ(video.rtp_to_ntp.UpdateMeasurements(ntp_time, rtp_timestamp), + RtpToNtpEstimator::kNewMeasurement); clock_sender_.AdvanceTimeMilliseconds(900); clock_receiver_.AdvanceTimeMilliseconds(900); ntp_time = clock_sender_.CurrentNtpTime(); rtp_timestamp = clock_sender_.CurrentTime().ms() * audio_frequency / 1000; - EXPECT_TRUE(audio.rtp_to_ntp.UpdateMeasurements( - ntp_time.seconds(), ntp_time.fractions(), rtp_timestamp, &new_sr)); + EXPECT_EQ(audio.rtp_to_ntp.UpdateMeasurements(ntp_time, rtp_timestamp), + RtpToNtpEstimator::kNewMeasurement); clock_sender_.AdvanceTimeMilliseconds(100); clock_receiver_.AdvanceTimeMilliseconds(100); ntp_time = clock_sender_.CurrentNtpTime(); rtp_timestamp = clock_sender_.CurrentTime().ms() * video_frequency / 1000; - EXPECT_TRUE(video.rtp_to_ntp.UpdateMeasurements( - ntp_time.seconds(), ntp_time.fractions(), rtp_timestamp, &new_sr)); + EXPECT_EQ(video.rtp_to_ntp.UpdateMeasurements(ntp_time, rtp_timestamp), + RtpToNtpEstimator::kNewMeasurement); clock_sender_.AdvanceTimeMilliseconds(900); clock_receiver_.AdvanceTimeMilliseconds(900);