#include "chromecast/media/audio/rate_adjuster.h"
#include <algorithm>
#include <cmath>
#include <utility>
#include "base/check.h"
namespace chromecast {
namespace media {
namespace {
constexpr auto kMaxRateChangeInterval = base::Minutes(5);
}
RateAdjuster::RateAdjuster(const Config& config,
RateChangeCallback change_clock_rate,
double current_clock_rate)
: config_(config),
change_clock_rate_(std::move(change_clock_rate)),
linear_error_(config_.linear_regression_window.InMicroseconds()),
current_clock_rate_(current_clock_rate) {
DCHECK(change_clock_rate_);
}
RateAdjuster::~RateAdjuster() = default;
void RateAdjuster::Reserve(int count) {
linear_error_.Reserve(count);
}
void RateAdjuster::Reset() {
linear_error_.Reset();
initialized_ = false;
clock_rate_start_timestamp_ = 0;
initial_timestamp_ = 0;
clock_rate_error_base_ = 0.0;
}
void RateAdjuster::AddError(int64_t error, int64_t timestamp) {
if (!initialized_) {
clock_rate_start_timestamp_ = timestamp;
clock_rate_error_base_ = 0.0;
initial_timestamp_ = timestamp;
initialized_ = true;
}
int64_t x = timestamp - initial_timestamp_;
int64_t time_at_current_clock_rate = timestamp - clock_rate_start_timestamp_;
double correction = clock_rate_error_base_ +
(1.0 - current_clock_rate_) * time_at_current_clock_rate;
int64_t corrected_error = error - correction;
linear_error_.AddSample(x, corrected_error, 1.0);
if (time_at_current_clock_rate <
config_.rate_change_interval.InMicroseconds()) {
return;
}
int64_t offset;
double slope;
double e;
if (!linear_error_.EstimateY(x, &offset, &e) ||
!linear_error_.EstimateSlope(&slope, &e)) {
return;
}
int64_t smoothed_error = offset + correction;
double offset_correction =
static_cast<double>(smoothed_error) /
(config_.rate_change_interval.InMicroseconds() * 2);
if (std::abs(smoothed_error) < config_.max_ignored_current_error) {
offset_correction = offset_correction / 4;
}
offset_correction =
std::clamp(offset_correction, -config_.max_current_error_correction,
config_.max_current_error_correction);
double new_rate = (1.0 + slope) + offset_correction;
if (std::fabs(new_rate - current_clock_rate_) > config_.min_rate_change ||
time_at_current_clock_rate > kMaxRateChangeInterval.InMicroseconds()) {
current_clock_rate_ =
change_clock_rate_.Run(new_rate, slope, smoothed_error);
clock_rate_start_timestamp_ = timestamp;
clock_rate_error_base_ = correction;
}
}
}
}