mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00
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}
This commit is contained in:
parent
340cb5e46a
commit
ae4fb618d7
7 changed files with 266 additions and 410 deletions
|
@ -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();
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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 @@ 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<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
|
||||
|
|
|
@ -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<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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 -
|
||||
|
|
|
@ -47,32 +47,31 @@ class StreamSynchronizationTest : public ::testing::Test {
|
|||
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue