xref: /aosp_15_r20/external/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "system_wrappers/include/rtp_to_ntp_estimator.h"
12 
13 #include <stddef.h>
14 
15 #include <cmath>
16 #include <vector>
17 
18 #include "api/array_view.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/logging.h"
21 #include "rtc_base/numerics/safe_conversions.h"
22 
23 namespace webrtc {
24 namespace {
25 // Maximum number of RTCP SR reports to use to map between RTP and NTP.
26 constexpr size_t kNumRtcpReportsToUse = 20;
27 // Don't allow NTP timestamps to jump more than 1 hour. Chosen arbitrary as big
28 // enough to not affect normal use-cases. Yet it is smaller than RTP wrap-around
29 // half-period (90khz RTP clock wrap-arounds every 13.25 hours). After half of
30 // wrap-around period it is impossible to unwrap RTP timestamps correctly.
31 constexpr uint64_t kMaxAllowedRtcpNtpInterval = uint64_t{60 * 60} << 32;
32 }  // namespace
33 
UpdateParameters()34 void RtpToNtpEstimator::UpdateParameters() {
35   size_t n = measurements_.size();
36   if (n < 2)
37     return;
38 
39   // Run linear regression:
40   // Given x[] and y[] writes out such k and b that line y=k*x+b approximates
41   // given points in the best way (Least Squares Method).
42   auto x = [](const RtcpMeasurement& m) {
43     return static_cast<double>(m.unwrapped_rtp_timestamp);
44   };
45   auto y = [](const RtcpMeasurement& m) {
46     return static_cast<double>(static_cast<uint64_t>(m.ntp_time));
47   };
48 
49   double avg_x = 0;
50   double avg_y = 0;
51   for (const RtcpMeasurement& m : measurements_) {
52     avg_x += x(m);
53     avg_y += y(m);
54   }
55   avg_x /= n;
56   avg_y /= n;
57 
58   double variance_x = 0;
59   double covariance_xy = 0;
60   for (const RtcpMeasurement& m : measurements_) {
61     double normalized_x = x(m) - avg_x;
62     double normalized_y = y(m) - avg_y;
63     variance_x += normalized_x * normalized_x;
64     covariance_xy += normalized_x * normalized_y;
65   }
66 
67   if (std::fabs(variance_x) < 1e-8)
68     return;
69 
70   double k = covariance_xy / variance_x;
71   double b = avg_y - k * avg_x;
72   params_ = {{.slope = k, .offset = b}};
73 }
74 
UpdateMeasurements(NtpTime ntp,uint32_t rtp_timestamp)75 RtpToNtpEstimator::UpdateResult RtpToNtpEstimator::UpdateMeasurements(
76     NtpTime ntp,
77     uint32_t rtp_timestamp) {
78   int64_t unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp);
79 
80   RtcpMeasurement new_measurement = {
81       .ntp_time = ntp, .unwrapped_rtp_timestamp = unwrapped_rtp_timestamp};
82 
83   for (const RtcpMeasurement& measurement : measurements_) {
84     // Use || since two equal timestamps will result in zero frequency.
85     if (measurement.ntp_time == ntp ||
86         measurement.unwrapped_rtp_timestamp == unwrapped_rtp_timestamp) {
87       return kSameMeasurement;
88     }
89   }
90 
91   if (!new_measurement.ntp_time.Valid())
92     return kInvalidMeasurement;
93 
94   uint64_t ntp_new = static_cast<uint64_t>(new_measurement.ntp_time);
95   bool invalid_sample = false;
96   if (!measurements_.empty()) {
97     int64_t old_rtp_timestamp = measurements_.front().unwrapped_rtp_timestamp;
98     uint64_t old_ntp = static_cast<uint64_t>(measurements_.front().ntp_time);
99     if (ntp_new <= old_ntp || ntp_new > old_ntp + kMaxAllowedRtcpNtpInterval) {
100       invalid_sample = true;
101     } else if (unwrapped_rtp_timestamp <= old_rtp_timestamp) {
102       RTC_LOG(LS_WARNING)
103           << "Newer RTCP SR report with older RTP timestamp, dropping";
104       invalid_sample = true;
105     } else if (unwrapped_rtp_timestamp - old_rtp_timestamp > (1 << 25)) {
106       // Sanity check. No jumps too far into the future in rtp.
107       invalid_sample = true;
108     }
109   }
110 
111   if (invalid_sample) {
112     ++consecutive_invalid_samples_;
113     if (consecutive_invalid_samples_ < kMaxInvalidSamples) {
114       return kInvalidMeasurement;
115     }
116     RTC_LOG(LS_WARNING) << "Multiple consecutively invalid RTCP SR reports, "
117                            "clearing measurements.";
118     measurements_.clear();
119     params_ = absl::nullopt;
120   }
121   consecutive_invalid_samples_ = 0;
122 
123   // Insert new RTCP SR report.
124   if (measurements_.size() == kNumRtcpReportsToUse)
125     measurements_.pop_back();
126 
127   measurements_.push_front(new_measurement);
128 
129   // List updated, calculate new parameters.
130   UpdateParameters();
131   return kNewMeasurement;
132 }
133 
Estimate(uint32_t rtp_timestamp) const134 NtpTime RtpToNtpEstimator::Estimate(uint32_t rtp_timestamp) const {
135   if (!params_)
136     return NtpTime();
137 
138   double estimated =
139       static_cast<double>(unwrapper_.Unwrap(rtp_timestamp)) * params_->slope +
140       params_->offset + 0.5f;
141 
142   return NtpTime(rtc::saturated_cast<uint64_t>(estimated));
143 }
144 
EstimatedFrequencyKhz() const145 double RtpToNtpEstimator::EstimatedFrequencyKhz() const {
146   if (!params_.has_value()) {
147     return 0.0;
148   }
149   static constexpr double kNtpUnitPerMs = 4.294967296E6;  // 2^32 / 1000.
150   return kNtpUnitPerMs / params_->slope;
151 }
152 
153 }  // namespace webrtc
154