summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator.cc
blob: ef5d9a7508be6d89e937e70023ccba37ff41694a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "system_wrappers/include/rtp_to_ntp_estimator.h"

#include <stddef.h>

#include <cmath>
#include <vector>

#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.
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.
constexpr uint64_t kMaxAllowedRtcpNtpInterval = uint64_t{60 * 60} << 32;
}  // namespace

void RtpToNtpEstimator::UpdateParameters() {
  size_t n = measurements_.size();
  if (n < 2)
    return;

  // 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 (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 (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;

  double k = covariance_xy / variance_x;
  double b = avg_y - k * avg_x;
  params_ = {{.slope = k, .offset = b}};
}

RtpToNtpEstimator::UpdateResult RtpToNtpEstimator::UpdateMeasurements(
    NtpTime ntp,
    uint32_t rtp_timestamp) {
  int64_t unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp);

  RtcpMeasurement new_measurement = {
      .ntp_time = ntp, .unwrapped_rtp_timestamp = unwrapped_rtp_timestamp};

  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 kInvalidMeasurement;

  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;
    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)
          << "Newer RTCP SR report with older RTP timestamp, dropping";
      invalid_sample = true;
    } else if (unwrapped_rtp_timestamp - old_rtp_timestamp > (1 << 25)) {
      // Sanity check. No jumps too far into the future in rtp.
      invalid_sample = true;
    }
  }

  if (invalid_sample) {
    ++consecutive_invalid_samples_;
    if (consecutive_invalid_samples_ < kMaxInvalidSamples) {
      return kInvalidMeasurement;
    }
    RTC_LOG(LS_WARNING) << "Multiple consecutively invalid RTCP SR reports, "
                           "clearing measurements.";
    measurements_.clear();
    params_ = absl::nullopt;
  }
  consecutive_invalid_samples_ = 0;

  // Insert new RTCP SR report.
  if (measurements_.size() == kNumRtcpReportsToUse)
    measurements_.pop_back();

  measurements_.push_front(new_measurement);

  // List updated, calculate new parameters.
  UpdateParameters();
  return kNewMeasurement;
}

NtpTime RtpToNtpEstimator::Estimate(uint32_t rtp_timestamp) const {
  if (!params_)
    return NtpTime();

  double estimated =
      static_cast<double>(unwrapper_.Unwrap(rtp_timestamp)) * params_->slope +
      params_->offset + 0.5f;

  return NtpTime(rtc::saturated_cast<uint64_t>(estimated));
}

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