blob: 312fde7cb37078aeeaeab6700411121daf78f99f [file] [log] [blame]
Erik Språng7ca375c2019-02-06 16:20:17 +01001/*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "video/encoder_bitrate_adjuster.h"
12
13#include <vector>
14
15#include "absl/memory/memory.h"
16#include "api/units/data_rate.h"
17#include "rtc_base/fake_clock.h"
18#include "rtc_base/numerics/safe_conversions.h"
19#include "test/gtest.h"
20
21namespace webrtc {
22
23class EncoderBitrateAdjusterTest : public ::testing::Test {
24 public:
25 static constexpr int64_t kWindowSizeMs = 3000;
26 static constexpr int kDefaultBitrateBps = 300000;
27 static constexpr int kDefaultFrameRateFps = 30;
28 EncoderBitrateAdjusterTest()
29 : target_bitrate_(DataRate::bps(kDefaultBitrateBps)),
30 target_framerate_fps_(kDefaultFrameRateFps),
31 tl_pattern_idx_{} {}
32
33 protected:
34 void SetUpAdjuster(size_t num_spatial_layers,
35 size_t num_temporal_layers,
36 bool vp9_svc) {
37 // Initialize some default VideoCodec instance with the given number of
38 // layers.
39 if (vp9_svc) {
40 codec_.codecType = VideoCodecType::kVideoCodecVP9;
41 codec_.numberOfSimulcastStreams = 1;
42 codec_.VP9()->numberOfSpatialLayers = num_spatial_layers;
43 codec_.VP9()->numberOfTemporalLayers = num_temporal_layers;
44 for (size_t si = 0; si < num_spatial_layers; ++si) {
45 codec_.spatialLayers[si].minBitrate = 100 * (1 << si);
46 codec_.spatialLayers[si].targetBitrate = 200 * (1 << si);
47 codec_.spatialLayers[si].maxBitrate = 300 * (1 << si);
48 codec_.spatialLayers[si].active = true;
49 codec_.spatialLayers[si].numberOfTemporalLayers = num_temporal_layers;
50 }
51 } else {
52 codec_.codecType = VideoCodecType::kVideoCodecVP8;
53 codec_.numberOfSimulcastStreams = num_spatial_layers;
54 codec_.VP8()->numberOfTemporalLayers = num_temporal_layers;
55 for (size_t si = 0; si < num_spatial_layers; ++si) {
56 codec_.simulcastStream[si].minBitrate = 100 * (1 << si);
57 codec_.simulcastStream[si].targetBitrate = 200 * (1 << si);
58 codec_.simulcastStream[si].maxBitrate = 300 * (1 << si);
59 codec_.simulcastStream[si].active = true;
60 codec_.simulcastStream[si].numberOfTemporalLayers = num_temporal_layers;
61 }
62 }
63
64 for (size_t si = 0; si < num_spatial_layers; ++si) {
65 encoder_info_.fps_allocation[si].resize(num_temporal_layers);
66 double fraction = 1.0;
67 for (int ti = num_temporal_layers - 1; ti >= 0; --ti) {
68 encoder_info_.fps_allocation[si][ti] = static_cast<uint8_t>(
69 VideoEncoder::EncoderInfo::kMaxFramerateFraction * fraction + 0.5);
70 fraction /= 2.0;
71 }
72 }
73
74 adjuster_ = absl::make_unique<EncoderBitrateAdjuster>(codec_);
75 adjuster_->OnEncoderInfo(encoder_info_);
76 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
77 current_input_allocation_, target_framerate_fps_);
78 }
79
80 void InsertFrames(std::vector<std::vector<double>> utilization_factors,
81 int64_t duration_ms) {
82 constexpr size_t kMaxFrameSize = 100000;
83 uint8_t buffer[kMaxFrameSize];
84
85 const int64_t start_us = rtc::TimeMicros();
86 while (rtc::TimeMicros() <
87 start_us + (duration_ms * rtc::kNumMicrosecsPerMillisec)) {
88 clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec /
89 target_framerate_fps_);
90 for (size_t si = 0; si < NumSpatialLayers(); ++si) {
91 const std::vector<int>& tl_pattern =
92 kTlPatterns[NumTemporalLayers(si) - 1];
93 const size_t ti =
94 tl_pattern[(tl_pattern_idx_[si]++) % tl_pattern.size()];
95
96 uint32_t layer_bitrate_bps =
97 current_adjusted_allocation_.GetBitrate(si, ti);
98 double layer_framerate_fps = target_framerate_fps_;
99 if (encoder_info_.fps_allocation[si].size() > ti) {
100 uint8_t layer_fps_fraction = encoder_info_.fps_allocation[si][ti];
101 if (ti > 0) {
102 // We're interested in the frame rate for this layer only, not
103 // cumulative frame rate.
104 layer_fps_fraction -= encoder_info_.fps_allocation[si][ti - 1];
105 }
106 layer_framerate_fps =
107 (target_framerate_fps_ * layer_fps_fraction) /
108 VideoEncoder::EncoderInfo::kMaxFramerateFraction;
109 }
110 double utilization_factor = 1.0;
111 if (utilization_factors.size() > si &&
112 utilization_factors[si].size() > ti) {
113 utilization_factor = utilization_factors[si][ti];
114 }
115 size_t frame_size_bytes = utilization_factor *
116 (layer_bitrate_bps / 8.0) /
117 layer_framerate_fps;
118
119 EncodedImage image(buffer, 0, kMaxFrameSize);
120 image.set_size(frame_size_bytes);
121 image.SetSpatialIndex(si);
122 adjuster_->OnEncodedFrame(image, ti);
123 }
124 }
125 }
126
127 size_t NumSpatialLayers() const {
128 if (codec_.codecType == VideoCodecType::kVideoCodecVP9) {
129 return codec_.VP9().numberOfSpatialLayers;
130 }
131 return codec_.numberOfSimulcastStreams;
132 }
133
134 size_t NumTemporalLayers(int spatial_index) {
135 if (codec_.codecType == VideoCodecType::kVideoCodecVP9) {
136 return codec_.spatialLayers[spatial_index].numberOfTemporalLayers;
137 }
138 return codec_.simulcastStream[spatial_index].numberOfTemporalLayers;
139 }
140
141 void ExpectNear(const VideoBitrateAllocation& expected_allocation,
142 const VideoBitrateAllocation& actual_allocation,
143 double allowed_error_fraction) {
144 for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
145 for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
146 if (expected_allocation.HasBitrate(si, ti)) {
147 EXPECT_TRUE(actual_allocation.HasBitrate(si, ti));
148 uint32_t expected_layer_bitrate_bps =
149 expected_allocation.GetBitrate(si, ti);
150 EXPECT_NEAR(expected_layer_bitrate_bps,
151 actual_allocation.GetBitrate(si, ti),
152 static_cast<uint32_t>(expected_layer_bitrate_bps *
153 allowed_error_fraction));
154 } else {
155 EXPECT_FALSE(actual_allocation.HasBitrate(si, ti));
156 }
157 }
158 }
159 }
160
161 VideoBitrateAllocation MultiplyAllocation(
162 const VideoBitrateAllocation& allocation,
163 double factor) {
164 VideoBitrateAllocation multiplied_allocation;
165 for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
166 for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
167 if (allocation.HasBitrate(si, ti)) {
168 multiplied_allocation.SetBitrate(
169 si, ti,
170 static_cast<uint32_t>(factor * allocation.GetBitrate(si, ti) +
171 0.5));
172 }
173 }
174 }
175 return multiplied_allocation;
176 }
177
178 VideoCodec codec_;
179 VideoEncoder::EncoderInfo encoder_info_;
180 std::unique_ptr<EncoderBitrateAdjuster> adjuster_;
181 VideoBitrateAllocation current_input_allocation_;
182 VideoBitrateAllocation current_adjusted_allocation_;
183 rtc::ScopedFakeClock clock_;
184 DataRate target_bitrate_;
185 double target_framerate_fps_;
186 int tl_pattern_idx_[kMaxSpatialLayers];
187
188 const std::vector<int> kTlPatterns[kMaxTemporalStreams] = {
189 {0},
190 {0, 1},
191 {0, 2, 1, 2},
192 {0, 3, 2, 3, 1, 3, 2, 3}};
193};
194
195TEST_F(EncoderBitrateAdjusterTest, SingleLayerOptimal) {
196 // Single layer, well behaved encoder.
197 current_input_allocation_.SetBitrate(0, 0, 300000);
198 target_framerate_fps_ = 30;
199 SetUpAdjuster(1, 1, false);
200 InsertFrames({{1.0}}, kWindowSizeMs);
201 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
202 current_input_allocation_, target_framerate_fps_);
203 // Adjusted allocation near input. Allow 1% error margin due to rounding
204 // errors etc.
205 ExpectNear(current_input_allocation_, current_adjusted_allocation_, 0.01);
206}
207
208TEST_F(EncoderBitrateAdjusterTest, SingleLayerOveruse) {
209 // Single layer, well behaved encoder.
210 current_input_allocation_.SetBitrate(0, 0, 300000);
211 target_framerate_fps_ = 30;
212 SetUpAdjuster(1, 1, false);
213 InsertFrames({{1.2}}, kWindowSizeMs);
214 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
215 current_input_allocation_, target_framerate_fps_);
216 // Adjusted allocation lowered by 20%.
217 ExpectNear(MultiplyAllocation(current_input_allocation_, 1 / 1.2),
218 current_adjusted_allocation_, 0.01);
219}
220
221TEST_F(EncoderBitrateAdjusterTest, SingleLayerUnderuse) {
222 // Single layer, well behaved encoder.
223 current_input_allocation_.SetBitrate(0, 0, 300000);
224 target_framerate_fps_ = 30;
225 SetUpAdjuster(1, 1, false);
226 InsertFrames({{0.5}}, kWindowSizeMs);
227 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
228 current_input_allocation_, target_framerate_fps_);
229 // Undershoot, adjusted should exactly match input.
230 ExpectNear(current_input_allocation_, current_adjusted_allocation_, 0.00);
231}
232
233TEST_F(EncoderBitrateAdjusterTest, ThreeTemporalLayersOptimalSize) {
234 // Three temporal layers, 60%/20%/20% bps distro, well behaved encoder.
235 current_input_allocation_.SetBitrate(0, 0, 180000);
236 current_input_allocation_.SetBitrate(0, 1, 60000);
237 current_input_allocation_.SetBitrate(0, 2, 60000);
238 target_framerate_fps_ = 30;
239 SetUpAdjuster(1, 3, false);
240 InsertFrames({{1.0, 1.0, 1.0}}, kWindowSizeMs);
241 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
242 current_input_allocation_, target_framerate_fps_);
243 ExpectNear(current_input_allocation_, current_adjusted_allocation_, 0.01);
244}
245
246TEST_F(EncoderBitrateAdjusterTest, ThreeTemporalLayersOvershoot) {
247 // Three temporal layers, 60%/20%/20% bps distro.
248 // 10% overshoot on all layers.
249 current_input_allocation_.SetBitrate(0, 0, 180000);
250 current_input_allocation_.SetBitrate(0, 1, 60000);
251 current_input_allocation_.SetBitrate(0, 2, 60000);
252 target_framerate_fps_ = 30;
253 SetUpAdjuster(1, 3, false);
254 InsertFrames({{1.1, 1.1, 1.1}}, kWindowSizeMs);
255 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
256 current_input_allocation_, target_framerate_fps_);
257 // Adjusted allocation lowered by 10%.
258 ExpectNear(MultiplyAllocation(current_input_allocation_, 1 / 1.1),
259 current_adjusted_allocation_, 0.01);
260}
261
262TEST_F(EncoderBitrateAdjusterTest, ThreeTemporalLayersUndershoot) {
263 // Three temporal layers, 60%/20%/20% bps distro, undershoot all layers.
264 current_input_allocation_.SetBitrate(0, 0, 180000);
265 current_input_allocation_.SetBitrate(0, 1, 60000);
266 current_input_allocation_.SetBitrate(0, 2, 60000);
267 target_framerate_fps_ = 30;
268 SetUpAdjuster(1, 3, false);
269 InsertFrames({{0.8, 0.8, 0.8}}, kWindowSizeMs);
270 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
271 current_input_allocation_, target_framerate_fps_);
272 // Adjusted allocation identical since we don't boost bitrates.
273 ExpectNear(current_input_allocation_, current_adjusted_allocation_, 0.0);
274}
275
276TEST_F(EncoderBitrateAdjusterTest, ThreeTemporalLayersSkewedOvershoot) {
277 // Three temporal layers, 60%/20%/20% bps distro.
278 // 10% overshoot on base layer, 20% on higher layers.
279 current_input_allocation_.SetBitrate(0, 0, 180000);
280 current_input_allocation_.SetBitrate(0, 1, 60000);
281 current_input_allocation_.SetBitrate(0, 2, 60000);
282 target_framerate_fps_ = 30;
283 SetUpAdjuster(1, 3, false);
284 InsertFrames({{1.1, 1.2, 1.2}}, kWindowSizeMs);
285 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
286 current_input_allocation_, target_framerate_fps_);
287 // Expected overshoot is weighted by bitrate:
288 // (0.6 * 1.1 + 0.2 * 1.2 + 0.2 * 1.2) = 1.14
289 ExpectNear(MultiplyAllocation(current_input_allocation_, 1 / 1.14),
290 current_adjusted_allocation_, 0.01);
291}
292
293TEST_F(EncoderBitrateAdjusterTest, FourTemporalLayersSkewedOvershoot) {
294 // Three temporal layers, 40%/30%/15%/15% bps distro.
295 // 10% overshoot on base layer, 20% on higher layers.
296 current_input_allocation_.SetBitrate(0, 0, 120000);
297 current_input_allocation_.SetBitrate(0, 1, 90000);
298 current_input_allocation_.SetBitrate(0, 2, 45000);
299 current_input_allocation_.SetBitrate(0, 3, 45000);
300 target_framerate_fps_ = 30;
301 SetUpAdjuster(1, 4, false);
302 InsertFrames({{1.1, 1.2, 1.2, 1.2}}, kWindowSizeMs);
303 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
304 current_input_allocation_, target_framerate_fps_);
305 // Expected overshoot is weighted by bitrate:
306 // (0.4 * 1.1 + 0.3 * 1.2 + 0.15 * 1.2 + 0.15 * 1.2) = 1.16
307 ExpectNear(MultiplyAllocation(current_input_allocation_, 1 / 1.16),
308 current_adjusted_allocation_, 0.01);
309}
310
311TEST_F(EncoderBitrateAdjusterTest, ThreeTemporalLayersNonLayeredEncoder) {
312 // Three temporal layers, 60%/20%/20% bps allocation, 10% overshoot,
313 // encoder does not actually support temporal layers.
314 current_input_allocation_.SetBitrate(0, 0, 180000);
315 current_input_allocation_.SetBitrate(0, 1, 60000);
316 current_input_allocation_.SetBitrate(0, 2, 60000);
317 target_framerate_fps_ = 30;
318 SetUpAdjuster(1, 1, false);
319 InsertFrames({{1.1}}, kWindowSizeMs);
320 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
321 current_input_allocation_, target_framerate_fps_);
322 // Expect the actual 10% overuse to be detected and the allocation to
323 // only contain the one entry.
324 VideoBitrateAllocation expected_allocation;
325 expected_allocation.SetBitrate(
326 0, 0,
327 static_cast<uint32_t>(current_input_allocation_.get_sum_bps() / 1.10));
328 ExpectNear(expected_allocation, current_adjusted_allocation_, 0.01);
329}
330
331TEST_F(EncoderBitrateAdjusterTest, IgnoredStream) {
332 // Encoder with three temporal layers, but in a mode that does not support
333 // deterministic frame rate. Those are ignored, even if bitrate overshoots.
334 current_input_allocation_.SetBitrate(0, 0, 180000);
335 current_input_allocation_.SetBitrate(0, 1, 60000);
336 target_framerate_fps_ = 30;
337 SetUpAdjuster(1, 1, false);
338 encoder_info_.fps_allocation[0].clear();
339 adjuster_->OnEncoderInfo(encoder_info_);
340
341 InsertFrames({{1.1}}, kWindowSizeMs);
342 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
343 current_input_allocation_, target_framerate_fps_);
344
345 // Values passed through.
346 ExpectNear(current_input_allocation_, current_adjusted_allocation_, 0.00);
347}
348
349TEST_F(EncoderBitrateAdjusterTest, DifferentSpatialOvershoots) {
350 // Two streams, both with three temporal layers.
351 // S0 has 5% overshoot, S1 has 25% overshoot.
352 current_input_allocation_.SetBitrate(0, 0, 180000);
353 current_input_allocation_.SetBitrate(0, 1, 60000);
354 current_input_allocation_.SetBitrate(0, 2, 60000);
355 current_input_allocation_.SetBitrate(1, 0, 400000);
356 current_input_allocation_.SetBitrate(1, 1, 150000);
357 current_input_allocation_.SetBitrate(1, 2, 150000);
358 target_framerate_fps_ = 30;
359 // Run twice, once configured as simulcast and once as VP9 SVC.
360 for (int i = 0; i < 2; ++i) {
361 SetUpAdjuster(2, 3, i == 0);
362 InsertFrames({{1.05, 1.05, 1.05}, {1.25, 1.25, 1.25}}, kWindowSizeMs);
363 current_adjusted_allocation_ = adjuster_->AdjustRateAllocation(
364 current_input_allocation_, target_framerate_fps_);
365 VideoBitrateAllocation expected_allocation;
366 for (size_t ti = 0; ti < 3; ++ti) {
367 expected_allocation.SetBitrate(
368 0, ti,
369 static_cast<uint32_t>(current_input_allocation_.GetBitrate(0, ti) /
370 1.05));
371 expected_allocation.SetBitrate(
372 1, ti,
373 static_cast<uint32_t>(current_input_allocation_.GetBitrate(1, ti) /
374 1.25));
375 }
376 ExpectNear(expected_allocation, current_adjusted_allocation_, 0.01);
377 }
378}
379
380} // namespace webrtc