chromium/chromecast/media/audio/playback_rate_shifter_unittest.cc

// 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/playback_rate_shifter.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace chromecast {
namespace media {

namespace {
constexpr int kSampleRate = 48000;
constexpr int64_t kMaxTimestampError = 50;
constexpr int kReadSize = 512;
}  // namespace

class PlaybackRateShifterTest : public testing::Test, public AudioProvider {
 public:
  PlaybackRateShifterTest()
      : rate_shifter_(this,
                      ::media::CHANNEL_LAYOUT_MONO,
                      1,
                      kSampleRate,
                      kReadSize) {
    srand(12345);
  }

  ~PlaybackRateShifterTest() override = default;

  int FillFrames(int num_frames,
                 int64_t playout_timestamp,
                 float* const* channel_data) override {
    if (expected_playout_timestamp_ != INT64_MIN) {
      int64_t error = playout_timestamp - expected_playout_timestamp_;
      EXPECT_LT(std::abs(error), kMaxTimestampError)
          << "Playout timestamp error from BufferedFrames() is " << error;
      expected_playout_timestamp_ = INT64_MIN;
    }

    if (next_playout_timestamp_ != INT64_MIN) {
      int64_t error = playout_timestamp - next_playout_timestamp_;
      EXPECT_LT(std::abs(error), kMaxTimestampError)
          << "Playout timestamp error from FillFrames() is " << error;
    }

    next_playout_timestamp_ =
        playout_timestamp +
        FramesToTime(num_frames) / rate_shifter_.playback_rate();
    float* channel = channel_data[0];
    for (int i = 0; i < num_frames; ++i) {
      channel[i] = base::RandDouble();
    }
    filled_ += num_frames;
    return num_frames;
  }

  size_t num_channels() const override { return 1; }
  int sample_rate() const override { return kSampleRate; }

  void Read(int num_frames) {
    int64_t request_timestamp = FramesToTime(num_read_);
    double buffered = rate_shifter_.BufferedFrames();
    int64_t filled_before = filled_;
    // No more than 50ms of buffer (plus 1 read).
    EXPECT_LT(buffered,
              (kSampleRate / 20 + kReadSize) / rate_shifter_.playback_rate());

    expected_playout_timestamp_ = request_timestamp + FramesToTime(buffered);
    float buffer[num_frames];
    float* data[1] = {buffer};
    int read = rate_shifter_.FillFrames(num_frames, request_timestamp, data);
    EXPECT_EQ(read, num_frames);
    num_read_ += read;

    int64_t filled_during_read = filled_ - filled_before;
    int64_t expected_next_playout_timestamp =
        request_timestamp +
        FramesToTime(buffered +
                     filled_during_read / rate_shifter_.playback_rate());
    int64_t next_playout_timestamp =
        FramesToTime(num_read_ + rate_shifter_.BufferedFrames());
    int64_t error = expected_next_playout_timestamp - next_playout_timestamp;
    EXPECT_LT(std::abs(error), kMaxTimestampError)
        << "Playout timestamp error before/after read is " << error;
  }

  void ReadAll(int total_frames) {
    int read = 0;
    while (read < total_frames) {
      int frames = (rand() % 32 + 8) * 16;
      Read(frames);
      read += frames;
    }
  }

  int64_t FramesToTime(int64_t frames) {
    return frames * 1000000 / kSampleRate;
  }

  void SetPlaybackRate(double rate) {
    LOG(INFO) << "Set rate to " << rate;
    rate_shifter_.SetPlaybackRate(rate);
    next_playout_timestamp_ = INT64_MIN;
  }

 protected:
  PlaybackRateShifter rate_shifter_;

  int64_t next_playout_timestamp_ = INT64_MIN;
  int64_t num_read_ = 0;
  int64_t filled_ = 0;

  int64_t expected_playout_timestamp_ = INT64_MIN;
  int64_t next_expected_playout_timestamp_ = INT64_MIN;
};

TEST_F(PlaybackRateShifterTest, Normal) {
  SetPlaybackRate(1.0);
  ReadAll(kSampleRate);
}

TEST_F(PlaybackRateShifterTest, Slower) {
  SetPlaybackRate(0.7);
  ReadAll(kSampleRate);
}

TEST_F(PlaybackRateShifterTest, Slower2) {
  SetPlaybackRate(0.5);
  ReadAll(kSampleRate);
}

TEST_F(PlaybackRateShifterTest, Faster) {
  SetPlaybackRate(1.5);
  ReadAll(kSampleRate);
}

TEST_F(PlaybackRateShifterTest, Faster2) {
  SetPlaybackRate(2.0);
  ReadAll(kSampleRate);
}

TEST_F(PlaybackRateShifterTest, IncreaseTo1) {
  SetPlaybackRate(0.7);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.0);
  ReadAll(kSampleRate / 2);
}

TEST_F(PlaybackRateShifterTest, IncreaseFrom1) {
  SetPlaybackRate(1.0);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.5);
  ReadAll(kSampleRate / 2);
}

TEST_F(PlaybackRateShifterTest, DecreaseTo1) {
  SetPlaybackRate(1.3);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.0);
  ReadAll(kSampleRate / 2);
}

TEST_F(PlaybackRateShifterTest, DecreaseFrom1) {
  SetPlaybackRate(1.0);
  ReadAll(kSampleRate);
  SetPlaybackRate(0.6);
  ReadAll(kSampleRate / 2);
}

TEST_F(PlaybackRateShifterTest, Increasing) {
  SetPlaybackRate(0.5);
  ReadAll(kSampleRate);
  SetPlaybackRate(0.7);
  ReadAll(kSampleRate);
  SetPlaybackRate(0.99);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.0);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.01);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.4);
  ReadAll(kSampleRate);
  SetPlaybackRate(2.0);
  ReadAll(kSampleRate);
}

TEST_F(PlaybackRateShifterTest, Decreasing) {
  SetPlaybackRate(2.0);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.6);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.01);
  ReadAll(kSampleRate);
  SetPlaybackRate(1.0);
  ReadAll(kSampleRate);
  SetPlaybackRate(0.99);
  ReadAll(kSampleRate);
  SetPlaybackRate(0.64);
  ReadAll(kSampleRate);
  SetPlaybackRate(0.5);
  ReadAll(kSampleRate);
}

}  // namespace media
}  // namespace chromecast