Cleanup RtpToNtpEstimator
- Use NtpTime instead of pair of uint32_t to represent ntp time
- Increase precision estimate with NtpTime precision instead of ms precision
- Hide helper structs as private types
- Modernize interface to prefer return values over output parameters
- embed LinearRegression helper into the only user: UpdateParameters
Bug: webrtc:13757
Change-Id: I0a62a03e2869b2ae1eacaa15253accc43ba0a598
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/254780
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36232}
diff --git a/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc b/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc
index 723064e..b1602e5d 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 @@
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 @@
}
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 1750633..3b62b78 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<Parameters> params() const;
-
- static const int kMaxInvalidSamples = 3;
-
- private:
void UpdateParameters();
- int consecutive_invalid_samples_;
+ int consecutive_invalid_samples_ = 0;
std::list<RtcpMeasurement> measurements_;
absl::optional<Parameters> 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 d0b0ad4..ef5d9a7 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<RtpToNtpEstimator::RtcpMeasurement>& 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<const double> x,
- rtc::ArrayView<const double> 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<double>(m.unwrapped_rtp_timestamp);
+ };
+ auto y = [](const RtcpMeasurement& m) {
+ return static_cast<double>(static_cast<uint64_t>(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<double>(covariance_xy / variance_x);
- *b = static_cast<double>(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<double> x;
- std::vector<double> 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<uint64_t>(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<uint64_t>(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 @@
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 @@
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<double>(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<double>(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<uint64_t>(estimated));
}
-const absl::optional<RtpToNtpEstimator::Parameters> 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 14bc6e0..29de761 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;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+ ntp_raw += kOneMsInNtp;
timestamp += kTimestampTicksPerMs;
- EXPECT_TRUE(
- estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr));
- ntp_frac += kOneMsInNtpFrac;
- 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<int>(kOneMsInNtpFrac),
- static_cast<int>(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<uint64_t>(estimated_ntp), 2 * kOneMsInNtp);
}
}
diff --git a/video/rtp_streams_synchronizer2.cc b/video/rtp_streams_synchronizer2.cc
index 4096fce..0fbb391 100644
--- a/video/rtp_streams_synchronizer2.cc
+++ b/video/rtp_streams_synchronizer2.cc
@@ -29,10 +29,10 @@
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 @@
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 d5c77c1..d86cc79 100644
--- a/video/stream_synchronization.cc
+++ b/video/stream_synchronization.cc
@@ -35,19 +35,19 @@
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)) {
+ NtpTime video_last_capture_time =
+ video_measurement.rtp_to_ntp.Estimate(video_measurement.latest_timestamp);
+ if (!video_last_capture_time.Valid()) {
return false;
}
- if (video_last_capture_time_ms < 0) {
- 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 5c6c79f..b733a1d 100644
--- a/video/stream_synchronization_unittest.cc
+++ b/video/stream_synchronization_unittest.cc
@@ -47,32 +47,31 @@
static_cast<int>(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);