blob: d5bcfd6ed83f1e86b08a38c4a4aae8462b14d032 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/hrd_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
constexpr int kCommonFps = 30;
constexpr base::TimeDelta kCommonBufferDelay = base::Milliseconds(1000);
constexpr uint32_t kCommonAvgBitrate = 1000000; // bits per second
constexpr uint32_t kCommonPeakBitrate = 2000000; // bits per second
// Test HRDBufferTest runs the test frame size sequence in various
// scenarios and check whether the component behaves as expected.
class HRDBufferTest : public testing::Test {
public:
HRDBufferTest() = default;
void SetUp() override {
hrd_buffer_ = std::make_unique<HRDBuffer>(0, 0);
EXPECT_EQ(0u, hrd_buffer_->buffer_size());
EXPECT_EQ(0u, hrd_buffer_->average_bitrate());
}
protected:
// Runs a loop of 60 frames with two intra encoded frames in the sequence.
// Returns the index of the last frame.
int RunTestSequence(uint32_t avg_bitrate, int fps, int start_frame_index) {
// Generate steady encoded frame sizes aligned with the requested bitrate.
constexpr int kFrameCount = 60;
constexpr int kFirstIntraFrameIndex = 0;
constexpr int kSecondIntraFrameIndex = 30;
size_t frame_size = avg_bitrate / 8 / fps;
std::vector<size_t> frames;
for (int i = 0; i < kFrameCount; ++i) {
frames.push_back(frame_size);
}
frames[kFirstIntraFrameIndex] = frames[kFirstIntraFrameIndex] * 3;
frames[kSecondIntraFrameIndex] = frames[kSecondIntraFrameIndex] * 3;
base::TimeDelta timestamp = base::Microseconds(
start_frame_index * base::Time::kMicrosecondsPerSecond / fps);
for (size_t encoded_size : frames) {
hrd_buffer_->Shrink(timestamp);
hrd_buffer_->AddFrameBytes(encoded_size, timestamp);
timestamp += base::Microseconds(base::Time::kMicrosecondsPerSecond / fps);
}
return start_frame_index + kFrameCount;
}
// Size of HRD buffer calculated from the buffer delay is in milliseconds.
int GetBufferSizeFromDelay(uint32_t avg_bitrate,
base::TimeDelta buffer_delay) const {
return static_cast<int>(avg_bitrate * buffer_delay.InSecondsF() / 8);
}
int GetBufferFullness(base::TimeDelta timestamp) const {
return 100 * hrd_buffer_->GetBytesAtTime(timestamp) /
hrd_buffer_->buffer_size();
}
std::unique_ptr<HRDBuffer> hrd_buffer_;
};
// Test Cases
// Running a simple sequence of frames and taking a snapshot of the parameters
// after the last frame is added. The parameters must strictly satisfy the
// predefined state.
TEST_F(HRDBufferTest, RunBasicBufferTest) {
constexpr int kExpectedBufferFullness = 13;
constexpr int kExpectedBufferBytes = 16601;
constexpr int kExpectedBufferBytesRemaining = 108399;
constexpr int kExpectedLastFrameBufferBytes = 20771;
constexpr int kExpectedFrameOvershooting = false;
constexpr int kExpectedBufferFullnessBadTimestamp = 16;
size_t buffer_size =
GetBufferSizeFromDelay(kCommonAvgBitrate, kCommonBufferDelay);
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate, kCommonPeakBitrate,
false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
int start_frame_index = 0;
int last_frame_index =
RunTestSequence(kCommonAvgBitrate, kCommonFps, start_frame_index);
base::TimeDelta timestamp = base::Microseconds(
last_frame_index * base::Time::kMicrosecondsPerSecond / kCommonFps);
EXPECT_EQ(kExpectedBufferFullness, GetBufferFullness(timestamp));
EXPECT_EQ(kExpectedBufferBytes, hrd_buffer_->GetBytesAtTime(timestamp));
EXPECT_EQ(kExpectedBufferBytesRemaining,
hrd_buffer_->GetBytesRemainingAtTime(timestamp));
EXPECT_EQ(kExpectedLastFrameBufferBytes,
hrd_buffer_->last_frame_buffer_bytes());
EXPECT_EQ(kExpectedFrameOvershooting, hrd_buffer_->frame_overshooting());
// Check behavior when invalid timestamp is provided.
timestamp =
base::Microseconds(last_frame_index * base::Time::kMicrosecondsPerSecond /
kCommonFps) -
base::Microseconds(60000);
EXPECT_EQ(kExpectedBufferFullnessBadTimestamp, GetBufferFullness(timestamp));
}
// The test runs the predefined test sequence three times using different buffer
// parameters. A snapshot of the buffer state is taken after each sequence
// run. The snapshot must strictly satisfy the predefined state.
TEST_F(HRDBufferTest, CheckBufferParameterChange) {
constexpr int kExpectedBufferFullness1 = 13;
constexpr int kExpectedBufferBytes1 = 16601;
constexpr int kExpectedBufferBytesRemaining1 = 108399;
constexpr int kExpectedLastFrameBufferBytes1 = 20771;
constexpr int kExpectedBufferFullness2 = 0;
constexpr int kExpectedBufferBytes2 = 0;
constexpr int kExpectedBufferBytesRemaining2 = 125000;
constexpr int kExpectedLastFrameBufferBytes2 = 4166;
constexpr int kExpectedBufferFullness3 = 81;
constexpr int kExpectedBufferBytes3 = 101328;
constexpr int kExpectedBufferBytesRemaining3 = 23672;
constexpr int kExpectedLastFrameBufferBytes3 = 104108;
size_t buffer_size =
GetBufferSizeFromDelay(kCommonAvgBitrate, kCommonBufferDelay);
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate, kCommonPeakBitrate,
false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
int start_frame_index = 0;
int last_frame_index =
RunTestSequence(kCommonAvgBitrate, kCommonFps, start_frame_index);
base::TimeDelta timestamp = base::Microseconds(
last_frame_index * base::Time::kMicrosecondsPerSecond / kCommonFps);
EXPECT_EQ(kExpectedBufferFullness1, GetBufferFullness(timestamp));
EXPECT_EQ(kExpectedBufferBytes1, hrd_buffer_->GetBytesAtTime(timestamp));
EXPECT_EQ(kExpectedBufferBytesRemaining1,
hrd_buffer_->GetBytesRemainingAtTime(timestamp));
EXPECT_EQ(kExpectedLastFrameBufferBytes1,
hrd_buffer_->last_frame_buffer_bytes());
// Increase average bitrate 50%.
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate * 3 / 2,
kCommonPeakBitrate * 3 / 2, false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate * 3 / 2, hrd_buffer_->average_bitrate());
start_frame_index = last_frame_index;
last_frame_index =
RunTestSequence(kCommonAvgBitrate, kCommonFps, start_frame_index);
timestamp = base::Microseconds(
last_frame_index * base::Time::kMicrosecondsPerSecond / kCommonFps);
EXPECT_EQ(kExpectedBufferFullness2, GetBufferFullness(timestamp));
EXPECT_EQ(kExpectedBufferBytes2, hrd_buffer_->GetBytesAtTime(timestamp));
EXPECT_EQ(kExpectedBufferBytesRemaining2,
hrd_buffer_->GetBytesRemainingAtTime(timestamp));
EXPECT_EQ(kExpectedLastFrameBufferBytes2,
hrd_buffer_->last_frame_buffer_bytes());
// Decrease average bitrate 33%.
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate * 2 / 3,
kCommonPeakBitrate * 2 / 3, false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate * 2 / 3, hrd_buffer_->average_bitrate());
start_frame_index = last_frame_index;
last_frame_index =
RunTestSequence(kCommonAvgBitrate, kCommonFps, start_frame_index);
timestamp = base::Microseconds(
last_frame_index * base::Time::kMicrosecondsPerSecond / kCommonFps);
EXPECT_EQ(kExpectedBufferFullness3, GetBufferFullness(timestamp));
EXPECT_EQ(kExpectedBufferBytes3, hrd_buffer_->GetBytesAtTime(timestamp));
EXPECT_EQ(kExpectedBufferBytesRemaining3,
hrd_buffer_->GetBytesRemainingAtTime(timestamp));
EXPECT_EQ(kExpectedLastFrameBufferBytes3,
hrd_buffer_->last_frame_buffer_bytes());
}
// The test uses extended HRD buffer constructor which initiates the buffer
// internal state with the provided parameters. After running the first test
// sequence, a new buffer is created with predefined state and another sequence
// is run after that. A snapshot of the buffer state is checked stirictly
// against predefined values after each sequence run.
TEST_F(HRDBufferTest, CheckSettingBufferState) {
constexpr int kExpectedBufferFullness1 = 0;
constexpr int kExpectedBufferBytes1 = 0;
constexpr int kExpectedBufferBytesRemaining1 = 125000;
constexpr int kExpectedLastFrameBufferBytes1 = 0;
constexpr base::TimeDelta kExpectedLastFrameTimestamp1 =
base::Microseconds(-1);
constexpr int kExpectedBufferFullness2 = 12;
constexpr int kExpectedBufferBytes2 = 15875;
constexpr int kExpectedBufferBytesRemaining2 = 109125;
constexpr int kExpectedLastFrameBufferBytes2 = 20000;
constexpr base::TimeDelta kExpectedLastFrameTimestamp2 =
base::Microseconds(99000);
size_t buffer_size =
GetBufferSizeFromDelay(kCommonAvgBitrate, kCommonBufferDelay);
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate, kCommonPeakBitrate,
false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
base::TimeDelta timestamp = base::Microseconds(0);
EXPECT_EQ(kExpectedBufferFullness1, GetBufferFullness(timestamp));
EXPECT_EQ(kExpectedBufferBytes1, hrd_buffer_->GetBytesAtTime(timestamp));
EXPECT_EQ(kExpectedBufferBytesRemaining1,
hrd_buffer_->GetBytesRemainingAtTime(timestamp));
EXPECT_EQ(kExpectedLastFrameBufferBytes1,
hrd_buffer_->last_frame_buffer_bytes());
EXPECT_EQ(kExpectedLastFrameTimestamp1, hrd_buffer_->last_frame_timestamp());
constexpr int kLastFrameBufferBytes2 = 20000;
constexpr base::TimeDelta kCurrFrameTimestamp2 = base::Microseconds(132000);
constexpr base::TimeDelta kLastFrameTimestamp2 = base::Microseconds(99000);
hrd_buffer_ =
std::make_unique<HRDBuffer>(buffer_size, kCommonAvgBitrate,
kLastFrameBufferBytes2, kLastFrameTimestamp2);
timestamp = kCurrFrameTimestamp2;
EXPECT_EQ(kExpectedBufferFullness2, GetBufferFullness(timestamp));
EXPECT_EQ(kExpectedBufferBytes2, hrd_buffer_->GetBytesAtTime(timestamp));
EXPECT_EQ(kExpectedBufferBytesRemaining2,
hrd_buffer_->GetBytesRemainingAtTime(timestamp));
EXPECT_EQ(kExpectedLastFrameBufferBytes2,
hrd_buffer_->last_frame_buffer_bytes());
EXPECT_EQ(kExpectedLastFrameTimestamp2, hrd_buffer_->last_frame_timestamp());
}
// Checks the last frame timestamp parameter after a frame is added to the
// buffer.
TEST_F(HRDBufferTest, CheckBufferLastFrameTimestamp) {
size_t buffer_size =
GetBufferSizeFromDelay(kCommonAvgBitrate, kCommonBufferDelay);
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate, kCommonPeakBitrate,
false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
base::TimeDelta timestamp = base::Microseconds(100000);
size_t encoded_size(10000);
hrd_buffer_->AddFrameBytes(encoded_size, timestamp);
EXPECT_EQ(timestamp, hrd_buffer_->last_frame_timestamp());
}
// Checks the buffer fullness parameter when the size of the buffer is being
// reduced. The size should follow strictly the predefined buffer size values.
TEST_F(HRDBufferTest, CheckBufferShrinking) {
constexpr int kFrameSequenceValues[] = {10000, 10000, 10000, 10000, 10000,
10000, 10000, 10000, 10000, 10000};
constexpr size_t kBufferShrinkingValues[] = {122917, 120834, 118751, 116668,
114585, 112502, 110419, 108336,
106253, 104170};
const size_t buffer_size =
GetBufferSizeFromDelay(kCommonAvgBitrate, kCommonBufferDelay);
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate, kCommonPeakBitrate,
false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
base::TimeDelta timestamp = base::Microseconds(0);
for (size_t encoded_size : kFrameSequenceValues) {
hrd_buffer_->AddFrameBytes(encoded_size, timestamp);
timestamp +=
base::Microseconds(base::Time::kMicrosecondsPerSecond / kCommonFps);
}
hrd_buffer_->SetParameters(buffer_size / 2, kCommonAvgBitrate,
kCommonPeakBitrate, true);
// The size of the buffer remains the same, since it will be reduced
// gradually.
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
int frame_index = 0;
for (size_t encoded_size : kFrameSequenceValues) {
hrd_buffer_->Shrink(timestamp);
hrd_buffer_->AddFrameBytes(encoded_size, timestamp);
EXPECT_EQ(kBufferShrinkingValues[frame_index], hrd_buffer_->buffer_size());
++frame_index;
timestamp +=
base::Microseconds(base::Time::kMicrosecondsPerSecond / kCommonFps);
}
hrd_buffer_->SetParameters(buffer_size / 3, kCommonAvgBitrate,
static_cast<uint32_t>(kCommonAvgBitrate * 1.2f),
true);
// The size of the buffer changes immeditely since the peak bitrate is less
// than 1.5 of the average bitrate.
EXPECT_EQ(buffer_size / 3, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
}
// Checks the buffer overshoot condition. After running the test frame sequence
// the buffer should overshoot at the predefined frame index.
TEST_F(HRDBufferTest, CheckBufferOvershoot) {
constexpr int kFrameSequenceValues[] = {30000, 10000, 10000, 10000, 10000,
10000, 10000, 10000, 10000, 10000};
constexpr int kExpectedFrameOvershootingIndex = 6;
const size_t buffer_size =
GetBufferSizeFromDelay(kCommonAvgBitrate, kCommonBufferDelay / 2);
hrd_buffer_->SetParameters(buffer_size, kCommonAvgBitrate, kCommonPeakBitrate,
false);
EXPECT_EQ(buffer_size, hrd_buffer_->buffer_size());
EXPECT_EQ(kCommonAvgBitrate, hrd_buffer_->average_bitrate());
base::TimeDelta timestamp = base::Microseconds(0);
int frame_index = 0;
for (size_t encoded_size : kFrameSequenceValues) {
hrd_buffer_->AddFrameBytes(encoded_size, timestamp);
if (!hrd_buffer_->frame_overshooting()) {
EXPECT_GT(kExpectedFrameOvershootingIndex, frame_index);
} else {
EXPECT_LE(kExpectedFrameOvershootingIndex, frame_index);
}
++frame_index;
timestamp +=
base::Microseconds(base::Time::kMicrosecondsPerSecond / kCommonFps);
}
}
} // namespace
} // namespace media