chromium/chromecast/media/audio/rate_adjuster.h

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMECAST_MEDIA_AUDIO_RATE_ADJUSTER_H_
#define CHROMECAST_MEDIA_AUDIO_RATE_ADJUSTER_H_

#include <cstdint>

#include "base/functional/callback.h"
#include "base/time/time.h"
#include "chromecast/base/statistics/weighted_moving_linear_regression.h"

namespace chromecast {
namespace media {

// RateAdjuster handles adjusting a clock rate to minimize errors over time.
// This can be used for A/V sync, for example. The RateAdjuster smooths the
// error samples over time using a moving linear regression. It attempts to
// adjust the clock rate to correct the current absolute error, and the slope
// of the change in error, with the goal of maintaining as close as possible to
// 0 errors over time.
class RateAdjuster {
 public:
  struct Config {
    // The minimum interval between clock rate changes.
    base::TimeDelta rate_change_interval = base::Seconds(1);

    // How long to make the linear regression window for smoothing errors.
    base::TimeDelta linear_regression_window = base::Seconds(10);

    // The maximum current error to ignore, in microseconds.
    int64_t max_ignored_current_error = 0;

    // The maximum current absolute error that should be adjusted for in a
    // single clock rate adjustment.
    double max_current_error_correction = 2.5e-4;

    // The minimum difference between the optimal clock rate and the current
    // clock rate required to actually change the rate.
    double min_rate_change = 0;
  };

  // Called to change the clock rate. Returns the actual new clock rate. The
  // |error_slope| and |current_error| are provided for debugging/logging
  // purposes.
  using RateChangeCallback =
      base::RepeatingCallback<double(double desired_clock_rate,
                                     double error_slope,
                                     double current_error)>;

  RateAdjuster(const Config& config,
               RateChangeCallback change_clock_rate,
               double current_clock_rate);
  ~RateAdjuster();

  // Adds an error sample at the given |timestamp|. Both the |error| and the
  // |timestamp| are in microseconds. The error of some event should be
  // calculated as <actual time of event> - <desired time of event>. Timestamps
  // should increase monotonically. The clock rate will be adjusted
  // synchronously within this method via the callback, if necessary.
  void AddError(int64_t error, int64_t timestamp);

  // Reserves space for |count| error samples, to reduce memory allocation
  // during use.
  void Reserve(int count);

  // Resets to initial state.
  void Reset();

 private:
  const Config config_;
  RateChangeCallback change_clock_rate_;

  WeightedMovingLinearRegression linear_error_;
  bool initialized_ = false;
  int64_t clock_rate_start_timestamp_ = 0;
  int64_t initial_timestamp_ = 0;
  double clock_rate_error_base_ = 0.0;
  double current_clock_rate_;
};

}  // namespace media
}  // namespace chromecast

#endif  // CHROMECAST_MEDIA_AUDIO_RATE_ADJUSTER_H_