1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
media / gpu / hrd_buffer_unittest.cc [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 <array>
#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 auto kBufferShrinkingValues = std::to_array<size_t>({
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