// 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.
#include "chromecast/media/audio/rate_adjuster.h"
#include "base/rand_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromecast {
namespace media {
class RateAdjusterTest : public testing::Test {
public:
RateAdjusterTest() = default;
~RateAdjusterTest() override = default;
double clock_rate() const { return clock_rate_; }
int64_t rate_change_count() const { return rate_change_count_; }
void set_underlying_rate(double rate) { underlying_rate_ = rate; }
void Configure(const RateAdjuster::Config& config) {
rate_adjuster_ = std::make_unique<RateAdjuster>(
config,
base::BindRepeating(&RateAdjusterTest::ChangeRate,
base::Unretained(this)),
clock_rate_);
}
void AddSample(int64_t timestamp, int max_jitter) {
int64_t delta = timestamp - base_timestamp_;
// Clock rate < 1 causes the events to happen later than they should.
int64_t value = base_value_ + delta / (clock_rate_ * underlying_rate_);
last_timestamp_ = timestamp;
last_value_ = value;
int64_t jittered_value = value + base::RandInt(-max_jitter, max_jitter);
rate_adjuster_->AddError(jittered_value - timestamp, timestamp);
}
private:
double ChangeRate(double desired_clock_rate,
double error_slope,
double current_error) {
++rate_change_count_;
clock_rate_ = desired_clock_rate;
base_timestamp_ = last_timestamp_;
base_value_ = last_value_;
return clock_rate_;
}
int64_t base_timestamp_ = 0;
int64_t last_timestamp_ = 0;
int64_t base_value_ = 0;
int64_t last_value_ = 0;
double clock_rate_ = 1.0;
double underlying_rate_ = 1.0;
int64_t rate_change_count_ = 0;
std::unique_ptr<RateAdjuster> rate_adjuster_;
};
TEST_F(RateAdjusterTest, NoError) {
RateAdjuster::Config config;
config.max_ignored_current_error = 0;
config.min_rate_change = 0;
Configure(config);
for (int i = 0; i < 600; ++i) {
AddSample(i * 100000, 0);
}
EXPECT_EQ(clock_rate(), 1.0);
EXPECT_EQ(rate_change_count(), 0);
}
TEST_F(RateAdjusterTest, MaxInterval) {
RateAdjuster::Config config;
config.max_ignored_current_error = 0;
config.min_rate_change = 0;
Configure(config);
for (int i = 0; i < 302; ++i) {
AddSample(i * 1000000, 0);
}
EXPECT_EQ(clock_rate(), 1.0);
EXPECT_EQ(rate_change_count(), 1);
}
TEST_F(RateAdjusterTest, SlowUnderlying) {
constexpr double kUnderlyingRate = 0.9995;
set_underlying_rate(kUnderlyingRate);
RateAdjuster::Config config;
config.max_ignored_current_error = 0;
config.min_rate_change = 0;
Configure(config);
for (int i = 0; i < 600; ++i) {
AddSample(i * 20000, 0);
}
EXPECT_NEAR(clock_rate() * kUnderlyingRate, 1.0, 1e-5);
EXPECT_GT(rate_change_count(), 1);
}
TEST_F(RateAdjusterTest, FastUnderlyingWithJitter) {
constexpr double kUnderlyingRate = 1.0005;
set_underlying_rate(kUnderlyingRate);
RateAdjuster::Config config;
config.max_ignored_current_error = 0;
config.min_rate_change = 0;
Configure(config);
// Need more samples to converge.
for (int i = 0; i < 3000; ++i) {
AddSample(i * 20000, 200);
}
EXPECT_NEAR(clock_rate() * kUnderlyingRate, 1.0, 5e-5);
EXPECT_GT(rate_change_count(), 1);
}
} // namespace media
} // namespace chromecast