1 /*
2 * Copyright (c) 2021 The WebM 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 <fstream> // NOLINT
12 #include <string>
13
14 #include "./vpx_config.h"
15 #include "third_party/googletest/src/include/gtest/gtest.h"
16 #include "test/codec_factory.h"
17 #include "test/encode_test_driver.h"
18 #include "test/i420_video_source.h"
19 #include "test/util.h"
20 #include "test/video_source.h"
21 #include "vp8/vp8_ratectrl_rtc.h"
22 #include "vpx/vpx_codec.h"
23 #include "vpx_ports/bitops.h"
24
25 namespace {
26
27 struct Vp8RCTestVideo {
28 Vp8RCTestVideo() = default;
Vp8RCTestVideo__anondd2254110111::Vp8RCTestVideo29 Vp8RCTestVideo(const char *name_, int width_, int height_,
30 unsigned int frames_)
31 : name(name_), width(width_), height(height_), frames(frames_) {}
32
operator <<(std::ostream & os,const Vp8RCTestVideo & video)33 friend std::ostream &operator<<(std::ostream &os,
34 const Vp8RCTestVideo &video) {
35 os << video.name << " " << video.width << " " << video.height << " "
36 << video.frames;
37 return os;
38 }
39 const char *name;
40 int width;
41 int height;
42 unsigned int frames;
43 };
44
45 const Vp8RCTestVideo kVp8RCTestVectors[] = {
46 Vp8RCTestVideo("niklas_640_480_30.yuv", 640, 480, 470),
47 Vp8RCTestVideo("desktop_office1.1280_720-020.yuv", 1280, 720, 300),
48 };
49
50 class Vp8RcInterfaceTest
51 : public ::libvpx_test::EncoderTest,
52 public ::libvpx_test::CodecTestWith2Params<int, Vp8RCTestVideo> {
53 public:
Vp8RcInterfaceTest()54 Vp8RcInterfaceTest()
55 : EncoderTest(GET_PARAM(0)), key_interval_(3000), encoder_exit_(false),
56 frame_drop_thresh_(0) {}
57 ~Vp8RcInterfaceTest() override = default;
58
59 protected:
SetUp()60 void SetUp() override {
61 InitializeConfig();
62 SetMode(::libvpx_test::kRealTime);
63 }
64
65 // From error_resilience_test.cc
SetFrameFlags(int frame_num,int num_temp_layers)66 int SetFrameFlags(int frame_num, int num_temp_layers) {
67 int frame_flags = 0;
68 if (num_temp_layers == 2) {
69 if (frame_num % 2 == 0) {
70 // Layer 0: predict from L and ARF, update L.
71 frame_flags =
72 VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
73 } else {
74 // Layer 1: predict from L, G and ARF, and update G.
75 frame_flags = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
76 VP8_EFLAG_NO_UPD_ENTROPY;
77 }
78 } else if (num_temp_layers == 3) {
79 if (frame_num % 4 == 0) {
80 // Layer 0: predict from L, update L.
81 frame_flags = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
82 VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF;
83 } else if ((frame_num - 2) % 4 == 0) {
84 // Layer 1: predict from L, G, update G.
85 frame_flags =
86 VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_REF_ARF;
87 } else if ((frame_num - 1) % 2 == 0) {
88 // Layer 2: predict from L, G, ARF; update ARG.
89 frame_flags = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_LAST;
90 }
91 }
92 return frame_flags;
93 }
94
SetLayerId(int frame_num,int num_temp_layers)95 int SetLayerId(int frame_num, int num_temp_layers) {
96 int layer_id = 0;
97 if (num_temp_layers == 2) {
98 if (frame_num % 2 == 0) {
99 layer_id = 0;
100 } else {
101 layer_id = 1;
102 }
103 } else if (num_temp_layers == 3) {
104 if (frame_num % 4 == 0) {
105 layer_id = 0;
106 } else if ((frame_num - 2) % 4 == 0) {
107 layer_id = 1;
108 } else if ((frame_num - 1) % 2 == 0) {
109 layer_id = 2;
110 }
111 }
112 return layer_id;
113 }
114
PreEncodeFrameHook(::libvpx_test::VideoSource * video,::libvpx_test::Encoder * encoder)115 void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
116 ::libvpx_test::Encoder *encoder) override {
117 if (rc_cfg_.ts_number_layers > 1) {
118 const int layer_id = SetLayerId(video->frame(), cfg_.ts_number_layers);
119 const int frame_flags =
120 SetFrameFlags(video->frame(), cfg_.ts_number_layers);
121 frame_params_.temporal_layer_id = layer_id;
122 if (video->frame() > 0) {
123 encoder->Control(VP8E_SET_TEMPORAL_LAYER_ID, layer_id);
124 encoder->Control(VP8E_SET_FRAME_FLAGS, frame_flags);
125 }
126 } else {
127 if (video->frame() == 0) {
128 encoder->Control(VP8E_SET_CPUUSED, -6);
129 encoder->Control(VP8E_SET_RTC_EXTERNAL_RATECTRL, 1);
130 encoder->Control(VP8E_SET_MAX_INTRA_BITRATE_PCT, 1000);
131 } else if (frame_params_.frame_type == libvpx::RcFrameType::kInterFrame) {
132 // Disable golden frame update.
133 frame_flags_ |= VP8_EFLAG_NO_UPD_GF;
134 frame_flags_ |= VP8_EFLAG_NO_UPD_ARF;
135 }
136 }
137 frame_params_.frame_type = video->frame() % key_interval_ == 0
138 ? libvpx::RcFrameType::kKeyFrame
139 : libvpx::RcFrameType::kInterFrame;
140 encoder_exit_ = video->frame() == test_video_.frames;
141 }
142
PostEncodeFrameHook(::libvpx_test::Encoder * encoder)143 void PostEncodeFrameHook(::libvpx_test::Encoder *encoder) override {
144 if (encoder_exit_) {
145 return;
146 }
147 int qp;
148 encoder->Control(VP8E_GET_LAST_QUANTIZER, &qp);
149 if (rc_api_->ComputeQP(frame_params_) == libvpx::FrameDropDecision::kOk) {
150 ASSERT_EQ(rc_api_->GetQP(), qp);
151 } else {
152 num_drops_++;
153 }
154 }
155
FramePktHook(const vpx_codec_cx_pkt_t * pkt)156 void FramePktHook(const vpx_codec_cx_pkt_t *pkt) override {
157 rc_api_->PostEncodeUpdate(pkt->data.frame.sz);
158 }
159
RunOneLayer()160 void RunOneLayer() {
161 test_video_ = GET_PARAM(2);
162 target_bitrate_ = GET_PARAM(1);
163 SetConfig();
164 rc_api_ = libvpx::VP8RateControlRTC::Create(rc_cfg_);
165 ASSERT_TRUE(rc_api_->UpdateRateControl(rc_cfg_));
166
167 ::libvpx_test::I420VideoSource video(test_video_.name, test_video_.width,
168 test_video_.height, 30, 1, 0,
169 test_video_.frames);
170
171 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
172 }
173
RunOneLayerDropFrames()174 void RunOneLayerDropFrames() {
175 test_video_ = GET_PARAM(2);
176 target_bitrate_ = GET_PARAM(1);
177 frame_drop_thresh_ = 30;
178 num_drops_ = 0;
179 // Use lower target_bitrate and max_quantizer to trigger drops.
180 target_bitrate_ = target_bitrate_ >> 2;
181 SetConfig();
182 rc_cfg_.max_quantizer = 56;
183 cfg_.rc_max_quantizer = 56;
184 rc_api_ = libvpx::VP8RateControlRTC::Create(rc_cfg_);
185 ASSERT_TRUE(rc_api_->UpdateRateControl(rc_cfg_));
186
187 ::libvpx_test::I420VideoSource video(test_video_.name, test_video_.width,
188 test_video_.height, 30, 1, 0,
189 test_video_.frames);
190
191 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
192 // Check that some frames were dropped, otherwise test has no value.
193 ASSERT_GE(num_drops_, 1);
194 }
195
RunPeriodicKey()196 void RunPeriodicKey() {
197 test_video_ = GET_PARAM(2);
198 target_bitrate_ = GET_PARAM(1);
199 key_interval_ = 100;
200 frame_drop_thresh_ = 30;
201 SetConfig();
202 rc_api_ = libvpx::VP8RateControlRTC::Create(rc_cfg_);
203 ASSERT_TRUE(rc_api_->UpdateRateControl(rc_cfg_));
204
205 ::libvpx_test::I420VideoSource video(test_video_.name, test_video_.width,
206 test_video_.height, 30, 1, 0,
207 test_video_.frames);
208
209 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
210 }
211
RunTemporalLayers2TL()212 void RunTemporalLayers2TL() {
213 test_video_ = GET_PARAM(2);
214 target_bitrate_ = GET_PARAM(1);
215 SetConfigTemporalLayers(2);
216 rc_api_ = libvpx::VP8RateControlRTC::Create(rc_cfg_);
217 ASSERT_TRUE(rc_api_->UpdateRateControl(rc_cfg_));
218
219 ::libvpx_test::I420VideoSource video(test_video_.name, test_video_.width,
220 test_video_.height, 30, 1, 0,
221 test_video_.frames);
222
223 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
224 }
225
RunTemporalLayers3TL()226 void RunTemporalLayers3TL() {
227 test_video_ = GET_PARAM(2);
228 target_bitrate_ = GET_PARAM(1);
229 SetConfigTemporalLayers(3);
230 rc_api_ = libvpx::VP8RateControlRTC::Create(rc_cfg_);
231 ASSERT_TRUE(rc_api_->UpdateRateControl(rc_cfg_));
232
233 ::libvpx_test::I420VideoSource video(test_video_.name, test_video_.width,
234 test_video_.height, 30, 1, 0,
235 test_video_.frames);
236
237 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
238 }
239
RunTemporalLayers3TLDropFrames()240 void RunTemporalLayers3TLDropFrames() {
241 test_video_ = GET_PARAM(2);
242 target_bitrate_ = GET_PARAM(1);
243 frame_drop_thresh_ = 30;
244 num_drops_ = 0;
245 // Use lower target_bitrate and max_quantizer to trigger drops.
246 target_bitrate_ = target_bitrate_ >> 2;
247 SetConfigTemporalLayers(3);
248 rc_cfg_.max_quantizer = 56;
249 cfg_.rc_max_quantizer = 56;
250 rc_api_ = libvpx::VP8RateControlRTC::Create(rc_cfg_);
251 ASSERT_TRUE(rc_api_->UpdateRateControl(rc_cfg_));
252
253 ::libvpx_test::I420VideoSource video(test_video_.name, test_video_.width,
254 test_video_.height, 30, 1, 0,
255 test_video_.frames);
256
257 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
258 // Check that some frames were dropped, otherwise test has no value.
259 ASSERT_GE(num_drops_, 1);
260 }
261
262 private:
SetConfig()263 void SetConfig() {
264 rc_cfg_.width = test_video_.width;
265 rc_cfg_.height = test_video_.height;
266 rc_cfg_.max_quantizer = 60;
267 rc_cfg_.min_quantizer = 2;
268 rc_cfg_.target_bandwidth = target_bitrate_;
269 rc_cfg_.buf_initial_sz = 600;
270 rc_cfg_.buf_optimal_sz = 600;
271 rc_cfg_.buf_sz = target_bitrate_;
272 rc_cfg_.undershoot_pct = 50;
273 rc_cfg_.overshoot_pct = 50;
274 rc_cfg_.max_intra_bitrate_pct = 1000;
275 rc_cfg_.framerate = 30.0;
276 rc_cfg_.layer_target_bitrate[0] = target_bitrate_;
277 rc_cfg_.frame_drop_thresh = frame_drop_thresh_;
278
279 // Encoder settings for ground truth.
280 cfg_.g_w = test_video_.width;
281 cfg_.g_h = test_video_.height;
282 cfg_.rc_undershoot_pct = 50;
283 cfg_.rc_overshoot_pct = 50;
284 cfg_.rc_buf_initial_sz = 600;
285 cfg_.rc_buf_optimal_sz = 600;
286 cfg_.rc_buf_sz = target_bitrate_;
287 cfg_.rc_dropframe_thresh = 0;
288 cfg_.rc_min_quantizer = 2;
289 cfg_.rc_max_quantizer = 60;
290 cfg_.rc_end_usage = VPX_CBR;
291 cfg_.g_lag_in_frames = 0;
292 cfg_.g_error_resilient = 1;
293 cfg_.rc_target_bitrate = target_bitrate_;
294 cfg_.kf_min_dist = key_interval_;
295 cfg_.kf_max_dist = key_interval_;
296 cfg_.rc_dropframe_thresh = frame_drop_thresh_;
297 }
298
SetConfigTemporalLayers(int temporal_layers)299 void SetConfigTemporalLayers(int temporal_layers) {
300 rc_cfg_.width = test_video_.width;
301 rc_cfg_.height = test_video_.height;
302 rc_cfg_.max_quantizer = 60;
303 rc_cfg_.min_quantizer = 2;
304 rc_cfg_.target_bandwidth = target_bitrate_;
305 rc_cfg_.buf_initial_sz = 600;
306 rc_cfg_.buf_optimal_sz = 600;
307 rc_cfg_.buf_sz = target_bitrate_;
308 rc_cfg_.undershoot_pct = 50;
309 rc_cfg_.overshoot_pct = 50;
310 rc_cfg_.max_intra_bitrate_pct = 1000;
311 rc_cfg_.framerate = 30.0;
312 rc_cfg_.frame_drop_thresh = frame_drop_thresh_;
313 if (temporal_layers == 2) {
314 rc_cfg_.layer_target_bitrate[0] = 60 * target_bitrate_ / 100;
315 rc_cfg_.layer_target_bitrate[1] = target_bitrate_;
316 rc_cfg_.ts_rate_decimator[0] = 2;
317 rc_cfg_.ts_rate_decimator[1] = 1;
318 } else if (temporal_layers == 3) {
319 rc_cfg_.layer_target_bitrate[0] = 40 * target_bitrate_ / 100;
320 rc_cfg_.layer_target_bitrate[1] = 60 * target_bitrate_ / 100;
321 rc_cfg_.layer_target_bitrate[2] = target_bitrate_;
322 rc_cfg_.ts_rate_decimator[0] = 4;
323 rc_cfg_.ts_rate_decimator[1] = 2;
324 rc_cfg_.ts_rate_decimator[2] = 1;
325 }
326
327 rc_cfg_.ts_number_layers = temporal_layers;
328
329 // Encoder settings for ground truth.
330 cfg_.g_w = test_video_.width;
331 cfg_.g_h = test_video_.height;
332 cfg_.rc_undershoot_pct = 50;
333 cfg_.rc_overshoot_pct = 50;
334 cfg_.rc_buf_initial_sz = 600;
335 cfg_.rc_buf_optimal_sz = 600;
336 cfg_.rc_buf_sz = target_bitrate_;
337 cfg_.rc_dropframe_thresh = 0;
338 cfg_.rc_min_quantizer = 2;
339 cfg_.rc_max_quantizer = 60;
340 cfg_.rc_end_usage = VPX_CBR;
341 cfg_.g_lag_in_frames = 0;
342 cfg_.g_error_resilient = 1;
343 cfg_.rc_target_bitrate = target_bitrate_;
344 cfg_.kf_min_dist = key_interval_;
345 cfg_.kf_max_dist = key_interval_;
346 cfg_.rc_dropframe_thresh = frame_drop_thresh_;
347 // 2 Temporal layers, no spatial layers, CBR mode.
348 cfg_.ss_number_layers = 1;
349 cfg_.ts_number_layers = temporal_layers;
350 if (temporal_layers == 2) {
351 cfg_.ts_rate_decimator[0] = 2;
352 cfg_.ts_rate_decimator[1] = 1;
353 cfg_.ts_periodicity = 2;
354 cfg_.ts_target_bitrate[0] = 60 * cfg_.rc_target_bitrate / 100;
355 cfg_.ts_target_bitrate[1] = cfg_.rc_target_bitrate;
356 } else if (temporal_layers == 3) {
357 cfg_.ts_rate_decimator[0] = 4;
358 cfg_.ts_rate_decimator[1] = 2;
359 cfg_.ts_rate_decimator[2] = 1;
360 cfg_.ts_periodicity = 4;
361 cfg_.ts_target_bitrate[0] = 40 * cfg_.rc_target_bitrate / 100;
362 cfg_.ts_target_bitrate[1] = 60 * cfg_.rc_target_bitrate / 100;
363 cfg_.ts_target_bitrate[2] = cfg_.rc_target_bitrate;
364 }
365 }
366
367 std::unique_ptr<libvpx::VP8RateControlRTC> rc_api_;
368 libvpx::VP8RateControlRtcConfig rc_cfg_;
369 int key_interval_;
370 int target_bitrate_;
371 Vp8RCTestVideo test_video_;
372 libvpx::VP8FrameParamsQpRTC frame_params_;
373 bool encoder_exit_;
374 int frame_drop_thresh_;
375 int num_drops_;
376 };
377
TEST_P(Vp8RcInterfaceTest,OneLayer)378 TEST_P(Vp8RcInterfaceTest, OneLayer) { RunOneLayer(); }
379
TEST_P(Vp8RcInterfaceTest,OneLayerDropFrames)380 TEST_P(Vp8RcInterfaceTest, OneLayerDropFrames) { RunOneLayerDropFrames(); }
381
TEST_P(Vp8RcInterfaceTest,OneLayerPeriodicKey)382 TEST_P(Vp8RcInterfaceTest, OneLayerPeriodicKey) { RunPeriodicKey(); }
383
TEST_P(Vp8RcInterfaceTest,TemporalLayers2TL)384 TEST_P(Vp8RcInterfaceTest, TemporalLayers2TL) { RunTemporalLayers2TL(); }
385
TEST_P(Vp8RcInterfaceTest,TemporalLayers3TL)386 TEST_P(Vp8RcInterfaceTest, TemporalLayers3TL) { RunTemporalLayers3TL(); }
387
TEST_P(Vp8RcInterfaceTest,TemporalLayers3TLDropFrames)388 TEST_P(Vp8RcInterfaceTest, TemporalLayers3TLDropFrames) {
389 RunTemporalLayers3TLDropFrames();
390 }
391
392 VP8_INSTANTIATE_TEST_SUITE(Vp8RcInterfaceTest,
393 ::testing::Values(200, 400, 1000),
394 ::testing::ValuesIn(kVp8RCTestVectors));
395
396 } // namespace
397