blob: 72fee059e527535d7f92e8af477479922fe95c05 [file] [edit]
/*
* 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