| /* |
| * Copyright (c) 2026 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 "rtc_base/clock_aligner.h" |
| |
| #include <algorithm> |
| #include <optional> |
| |
| #include "api/environment/environment.h" |
| #include "api/sequence_checker.h" |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "rtc_base/checks.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| constexpr TimeDelta kMaxStaleness = TimeDelta::Millis(500); |
| constexpr TimeDelta kMaxIdleGap = TimeDelta::Seconds(1); |
| constexpr double kMaxDriftRate = 0.001; |
| |
| } // namespace |
| |
| ClockAligner::ClockAligner(const Environment& env) |
| : env_(env), |
| fix_non_monotonic_clock_( |
| env_.field_trials().IsEnabled("WebRTC-ClockAligner")) {} |
| |
| Timestamp ClockAligner::Align(Timestamp time) { |
| RTC_DCHECK_RUN_ON(&sequence_checker_); |
| Timestamp current_time = env_.clock().CurrentTime(); |
| if (fix_non_monotonic_clock_) { |
| return AlignNonMonotonicClock(time, current_time); |
| } |
| return AlignAssumingMonotonicClock(time, current_time); |
| } |
| |
| Timestamp ClockAligner::AlignAssumingMonotonicClock(Timestamp time, |
| Timestamp current_time) { |
| if (!time_offset_.has_value() || time + *time_offset_ > current_time) { |
| // Estimate timestamp offset from first packet arrival time. |
| // This may be wrong if packets have been buffered in the socket before |
| // we read the first packet and `time_offset_` may then have to |
| // be set again to ensure no arrival times are set in the future. |
| time_offset_ = current_time - time; |
| } |
| Timestamp arrival_time = time + *time_offset_; |
| RTC_DCHECK_LE(arrival_time, current_time); |
| return arrival_time; |
| } |
| |
| Timestamp ClockAligner::AlignNonMonotonicClock(Timestamp time, |
| Timestamp current_time) { |
| TimeDelta sample_offset = current_time - time; |
| if (!time_offset_.has_value() || !last_current_time_.has_value() || |
| !last_time_.has_value()) { |
| time_offset_ = sample_offset; |
| } else { |
| // Userspace monotonic elapsed time since last packet read. |
| TimeDelta delta_mono = |
| std::max(TimeDelta::Zero(), current_time - *last_current_time_); |
| // Inter-arrival time from external clock. Not necessarily monotonic. |
| TimeDelta delta_time = time - *last_time_; |
| |
| if (delta_time < TimeDelta::Zero()) { |
| time_offset_ = sample_offset; |
| } else if (sample_offset < *time_offset_) { |
| time_offset_ = sample_offset; |
| } else if (delta_mono > kMaxIdleGap) { |
| time_offset_ = sample_offset; |
| } else if (sample_offset - *time_offset_ > kMaxStaleness) { |
| time_offset_ = sample_offset; |
| } else { |
| // Keep offset steady to preserve inter-arrival spacing, |
| // while allowing slow positive drift tracking bounded by max_drift_rate. |
| TimeDelta max_drift = delta_mono * kMaxDriftRate; |
| *time_offset_ = std::min(sample_offset, *time_offset_ + max_drift); |
| } |
| } |
| last_current_time_ = current_time; |
| last_time_ = time; |
| Timestamp arrival_time = time + *time_offset_; |
| RTC_DCHECK_LE(arrival_time, current_time); |
| return arrival_time; |
| } |
| |
| } // namespace webrtc |