xref: /aosp_15_r20/external/libvpx/test/vp8_datarate_test.cc (revision fb1b10ab9aebc7c7068eedab379b749d7e3900be)
1 /*
2  *  Copyright (c) 2012 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 #include "./vpx_config.h"
11 #include "gtest/gtest.h"
12 #include "test/codec_factory.h"
13 #include "test/encode_test_driver.h"
14 #include "test/i420_video_source.h"
15 #include "test/util.h"
16 #include "test/y4m_video_source.h"
17 #include "vpx/vpx_encoder.h"
18 
19 namespace {
20 
21 class DatarateTestLarge
22     : public ::libvpx_test::EncoderTest,
23       public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
24  public:
DatarateTestLarge()25   DatarateTestLarge() : EncoderTest(GET_PARAM(0)) {}
26 
27   ~DatarateTestLarge() override = default;
28 
29  protected:
SetUp()30   void SetUp() override {
31     InitializeConfig();
32     SetMode(GET_PARAM(1));
33     set_cpu_used_ = GET_PARAM(2);
34     ResetModel();
35   }
36 
ResetModel()37   virtual void ResetModel() {
38     last_pts_ = 0;
39     bits_in_buffer_model_ = cfg_.rc_target_bitrate * cfg_.rc_buf_initial_sz;
40     frame_number_ = 0;
41     first_drop_ = 0;
42     bits_total_ = 0;
43     duration_ = 0.0;
44     denoiser_offon_test_ = 0;
45     denoiser_offon_period_ = -1;
46     gf_boost_ = 0;
47     use_roi_ = false;
48   }
49 
PreEncodeFrameHook(::libvpx_test::VideoSource * video,::libvpx_test::Encoder * encoder)50   void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
51                           ::libvpx_test::Encoder *encoder) override {
52     if (video->frame() == 0) {
53       encoder->Control(VP8E_SET_NOISE_SENSITIVITY, denoiser_on_);
54       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
55       encoder->Control(VP8E_SET_GF_CBR_BOOST_PCT, gf_boost_);
56     }
57 
58     if (use_roi_) {
59       encoder->Control(VP8E_SET_ROI_MAP, &roi_);
60     }
61 
62     if (denoiser_offon_test_) {
63       ASSERT_GT(denoiser_offon_period_, 0)
64           << "denoiser_offon_period_ is not positive.";
65       if ((video->frame() + 1) % denoiser_offon_period_ == 0) {
66         // Flip denoiser_on_ periodically
67         denoiser_on_ ^= 1;
68       }
69       encoder->Control(VP8E_SET_NOISE_SENSITIVITY, denoiser_on_);
70     }
71 
72     const vpx_rational_t tb = video->timebase();
73     timebase_ = static_cast<double>(tb.num) / tb.den;
74     duration_ = 0;
75   }
76 
FramePktHook(const vpx_codec_cx_pkt_t * pkt)77   void FramePktHook(const vpx_codec_cx_pkt_t *pkt) override {
78     // Time since last timestamp = duration.
79     vpx_codec_pts_t duration = pkt->data.frame.pts - last_pts_;
80 
81     // TODO(jimbankoski): Remove these lines when the issue:
82     // http://code.google.com/p/webm/issues/detail?id=496 is fixed.
83     // For now the codec assumes buffer starts at starting buffer rate
84     // plus one frame's time.
85     if (last_pts_ == 0) duration = 1;
86 
87     // Add to the buffer the bits we'd expect from a constant bitrate server.
88     bits_in_buffer_model_ += static_cast<int64_t>(
89         duration * timebase_ * cfg_.rc_target_bitrate * 1000);
90 
91     /* Test the buffer model here before subtracting the frame. Do so because
92      * the way the leaky bucket model works in libvpx is to allow the buffer to
93      * empty - and then stop showing frames until we've got enough bits to
94      * show one. As noted in comment below (issue 495), this does not currently
95      * apply to key frames. For now exclude key frames in condition below. */
96     const bool key_frame =
97         (pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? true : false;
98     if (!key_frame) {
99       ASSERT_GE(bits_in_buffer_model_, 0)
100           << "Buffer Underrun at frame " << pkt->data.frame.pts;
101     }
102 
103     const int64_t frame_size_in_bits = pkt->data.frame.sz * 8;
104 
105     // Subtract from the buffer the bits associated with a played back frame.
106     bits_in_buffer_model_ -= frame_size_in_bits;
107 
108     // Update the running total of bits for end of test datarate checks.
109     bits_total_ += frame_size_in_bits;
110 
111     // If first drop not set and we have a drop set it to this time.
112     if (!first_drop_ && duration > 1) first_drop_ = last_pts_ + 1;
113 
114     // Update the most recent pts.
115     last_pts_ = pkt->data.frame.pts;
116 
117     // We update this so that we can calculate the datarate minus the last
118     // frame encoded in the file.
119     bits_in_last_frame_ = frame_size_in_bits;
120 
121     ++frame_number_;
122   }
123 
EndPassHook()124   void EndPassHook() override {
125     if (bits_total_) {
126       const double file_size_in_kb = bits_total_ / 1000.;  // bits per kilobit
127 
128       duration_ = (last_pts_ + 1) * timebase_;
129 
130       // Effective file datarate includes the time spent prebuffering.
131       effective_datarate_ = (bits_total_ - bits_in_last_frame_) / 1000.0 /
132                             (cfg_.rc_buf_initial_sz / 1000.0 + duration_);
133 
134       file_datarate_ = file_size_in_kb / duration_;
135     }
136   }
137 
DenoiserLevelsTest()138   virtual void DenoiserLevelsTest() {
139     cfg_.rc_buf_initial_sz = 500;
140     cfg_.rc_dropframe_thresh = 1;
141     cfg_.rc_max_quantizer = 56;
142     cfg_.rc_end_usage = VPX_CBR;
143     ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
144                                          288, 30, 1, 0, 140);
145     for (int j = 1; j < 5; ++j) {
146       // Run over the denoiser levels.
147       // For the temporal denoiser (#if CONFIG_TEMPORAL_DENOISING) the level j
148       // refers to the 4 denoiser modes: denoiserYonly, denoiserOnYUV,
149       // denoiserOnAggressive, and denoiserOnAdaptive.
150       denoiser_on_ = j;
151       cfg_.rc_target_bitrate = 300;
152       ResetModel();
153       ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
154       ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
155           << " The datarate for the file exceeds the target!";
156 
157       ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
158           << " The datarate for the file missed the target!";
159     }
160   }
161 
DenoiserOffOnTest()162   virtual void DenoiserOffOnTest() {
163     cfg_.rc_buf_initial_sz = 500;
164     cfg_.rc_dropframe_thresh = 1;
165     cfg_.rc_max_quantizer = 56;
166     cfg_.rc_end_usage = VPX_CBR;
167     ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
168                                          288, 30, 1, 0, 299);
169     cfg_.rc_target_bitrate = 300;
170     ResetModel();
171     // The denoiser is off by default.
172     denoiser_on_ = 0;
173     // Set the offon test flag.
174     denoiser_offon_test_ = 1;
175     denoiser_offon_period_ = 100;
176     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
177     ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
178         << " The datarate for the file exceeds the target!";
179     ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
180         << " The datarate for the file missed the target!";
181   }
182 
BasicBufferModelTest()183   virtual void BasicBufferModelTest() {
184     denoiser_on_ = 0;
185     cfg_.rc_buf_initial_sz = 500;
186     cfg_.rc_dropframe_thresh = 1;
187     cfg_.rc_max_quantizer = 56;
188     cfg_.rc_end_usage = VPX_CBR;
189     // 2 pass cbr datarate control has a bug hidden by the small # of
190     // frames selected in this encode. The problem is that even if the buffer is
191     // negative we produce a keyframe on a cutscene. Ignoring datarate
192     // constraints
193     // TODO(jimbankoski): ( Fix when issue
194     // http://code.google.com/p/webm/issues/detail?id=495 is addressed. )
195     ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
196                                          288, 30, 1, 0, 140);
197 
198     // There is an issue for low bitrates in real-time mode, where the
199     // effective_datarate slightly overshoots the target bitrate.
200     // This is same the issue as noted about (#495).
201     // TODO(jimbankoski/marpan): Update test to run for lower bitrates (< 100),
202     // when the issue is resolved.
203     for (int i = 100; i < 800; i += 200) {
204       cfg_.rc_target_bitrate = i;
205       ResetModel();
206       ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
207       ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
208           << " The datarate for the file exceeds the target!";
209       ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
210           << " The datarate for the file missed the target!";
211     }
212   }
213 
ChangingDropFrameThreshTest()214   virtual void ChangingDropFrameThreshTest() {
215     denoiser_on_ = 0;
216     cfg_.rc_buf_initial_sz = 500;
217     cfg_.rc_max_quantizer = 36;
218     cfg_.rc_end_usage = VPX_CBR;
219     cfg_.rc_target_bitrate = 200;
220     cfg_.kf_mode = VPX_KF_DISABLED;
221 
222     const int frame_count = 40;
223     ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
224                                          288, 30, 1, 0, frame_count);
225 
226     // Here we check that the first dropped frame gets earlier and earlier
227     // as the drop frame threshold is increased.
228 
229     const int kDropFrameThreshTestStep = 30;
230     vpx_codec_pts_t last_drop = frame_count;
231     for (int i = 1; i < 91; i += kDropFrameThreshTestStep) {
232       cfg_.rc_dropframe_thresh = i;
233       ResetModel();
234       ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
235       ASSERT_LE(first_drop_, last_drop)
236           << " The first dropped frame for drop_thresh " << i
237           << " > first dropped frame for drop_thresh "
238           << i - kDropFrameThreshTestStep;
239       last_drop = first_drop_;
240     }
241   }
242 
DropFramesMultiThreadsTest()243   virtual void DropFramesMultiThreadsTest() {
244     denoiser_on_ = 0;
245     cfg_.rc_buf_initial_sz = 500;
246     cfg_.rc_dropframe_thresh = 30;
247     cfg_.rc_max_quantizer = 56;
248     cfg_.rc_end_usage = VPX_CBR;
249     cfg_.g_threads = 2;
250 
251     ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
252                                          288, 30, 1, 0, 140);
253     cfg_.rc_target_bitrate = 200;
254     ResetModel();
255     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
256     ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
257         << " The datarate for the file exceeds the target!";
258 
259     ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
260         << " The datarate for the file missed the target!";
261   }
262 
MultiThreadsPSNRTest()263   virtual void MultiThreadsPSNRTest() {
264     denoiser_on_ = 0;
265     cfg_.rc_buf_initial_sz = 500;
266     cfg_.rc_dropframe_thresh = 0;
267     cfg_.rc_max_quantizer = 56;
268     cfg_.rc_end_usage = VPX_CBR;
269     cfg_.g_threads = 4;
270     init_flags_ = VPX_CODEC_USE_PSNR;
271 
272     ::libvpx_test::I420VideoSource video("desktop_office1.1280_720-020.yuv",
273                                          1280, 720, 30, 1, 0, 30);
274     cfg_.rc_target_bitrate = 1000;
275     ResetModel();
276     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
277     ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.5)
278         << " The datarate for the file exceeds the target!";
279 
280     ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 2.0)
281         << " The datarate for the file missed the target!";
282   }
283 
284   vpx_codec_pts_t last_pts_;
285   int64_t bits_in_buffer_model_;
286   double timebase_;
287   int frame_number_;
288   vpx_codec_pts_t first_drop_;
289   int64_t bits_total_;
290   double duration_;
291   double file_datarate_;
292   double effective_datarate_;
293   int64_t bits_in_last_frame_;
294   int denoiser_on_;
295   int denoiser_offon_test_;
296   int denoiser_offon_period_;
297   int set_cpu_used_;
298   int gf_boost_;
299   bool use_roi_;
300   vpx_roi_map_t roi_;
301 };
302 
303 #if CONFIG_TEMPORAL_DENOISING
304 // Check basic datarate targeting, for a single bitrate, but loop over the
305 // various denoiser settings.
TEST_P(DatarateTestLarge,DenoiserLevels)306 TEST_P(DatarateTestLarge, DenoiserLevels) { DenoiserLevelsTest(); }
307 
308 // Check basic datarate targeting, for a single bitrate, when denoiser is off
309 // and on.
TEST_P(DatarateTestLarge,DenoiserOffOn)310 TEST_P(DatarateTestLarge, DenoiserOffOn) { DenoiserOffOnTest(); }
311 #endif  // CONFIG_TEMPORAL_DENOISING
312 
TEST_P(DatarateTestLarge,BasicBufferModel)313 TEST_P(DatarateTestLarge, BasicBufferModel) { BasicBufferModelTest(); }
314 
TEST_P(DatarateTestLarge,ChangingDropFrameThresh)315 TEST_P(DatarateTestLarge, ChangingDropFrameThresh) {
316   ChangingDropFrameThreshTest();
317 }
318 
TEST_P(DatarateTestLarge,DropFramesMultiThreads)319 TEST_P(DatarateTestLarge, DropFramesMultiThreads) {
320   DropFramesMultiThreadsTest();
321 }
322 
323 class DatarateTestRealTime : public DatarateTestLarge {
324  public:
325   ~DatarateTestRealTime() override = default;
326 };
327 
328 #if CONFIG_TEMPORAL_DENOISING
329 // Check basic datarate targeting, for a single bitrate, but loop over the
330 // various denoiser settings.
TEST_P(DatarateTestRealTime,DenoiserLevels)331 TEST_P(DatarateTestRealTime, DenoiserLevels) { DenoiserLevelsTest(); }
332 
333 // Check basic datarate targeting, for a single bitrate, when denoiser is off
334 // and on.
TEST_P(DatarateTestRealTime,DenoiserOffOn)335 TEST_P(DatarateTestRealTime, DenoiserOffOn) {}
336 #endif  // CONFIG_TEMPORAL_DENOISING
337 
TEST_P(DatarateTestRealTime,BasicBufferModel)338 TEST_P(DatarateTestRealTime, BasicBufferModel) { BasicBufferModelTest(); }
339 
TEST_P(DatarateTestRealTime,ChangingDropFrameThresh)340 TEST_P(DatarateTestRealTime, ChangingDropFrameThresh) {
341   ChangingDropFrameThreshTest();
342 }
343 
TEST_P(DatarateTestRealTime,DropFramesMultiThreads)344 TEST_P(DatarateTestRealTime, DropFramesMultiThreads) {
345   DropFramesMultiThreadsTest();
346 }
347 
TEST_P(DatarateTestRealTime,MultiThreadsPSNR)348 TEST_P(DatarateTestRealTime, MultiThreadsPSNR) { MultiThreadsPSNRTest(); }
349 
TEST_P(DatarateTestRealTime,RegionOfInterest)350 TEST_P(DatarateTestRealTime, RegionOfInterest) {
351   denoiser_on_ = 0;
352   cfg_.rc_buf_initial_sz = 500;
353   cfg_.rc_dropframe_thresh = 0;
354   cfg_.rc_max_quantizer = 56;
355   cfg_.rc_end_usage = VPX_CBR;
356   // Encode using multiple threads.
357   cfg_.g_threads = 2;
358 
359   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
360                                        30, 1, 0, 300);
361   cfg_.rc_target_bitrate = 450;
362   cfg_.g_w = 352;
363   cfg_.g_h = 288;
364 
365   ResetModel();
366 
367   // Set ROI parameters
368   use_roi_ = true;
369   memset(&roi_, 0, sizeof(roi_));
370 
371   roi_.rows = (cfg_.g_h + 15) / 16;
372   roi_.cols = (cfg_.g_w + 15) / 16;
373 
374   roi_.delta_q[0] = 0;
375   roi_.delta_q[1] = -20;
376   roi_.delta_q[2] = 0;
377   roi_.delta_q[3] = 0;
378 
379   roi_.delta_lf[0] = 0;
380   roi_.delta_lf[1] = -20;
381   roi_.delta_lf[2] = 0;
382   roi_.delta_lf[3] = 0;
383 
384   roi_.static_threshold[0] = 0;
385   roi_.static_threshold[1] = 1000;
386   roi_.static_threshold[2] = 0;
387   roi_.static_threshold[3] = 0;
388 
389   // Use 2 states: 1 is center square, 0 is the rest.
390   roi_.roi_map =
391       (uint8_t *)calloc(roi_.rows * roi_.cols, sizeof(*roi_.roi_map));
392   for (unsigned int i = 0; i < roi_.rows; ++i) {
393     for (unsigned int j = 0; j < roi_.cols; ++j) {
394       if (i > (roi_.rows >> 2) && i < ((roi_.rows * 3) >> 2) &&
395           j > (roi_.cols >> 2) && j < ((roi_.cols * 3) >> 2)) {
396         roi_.roi_map[i * roi_.cols + j] = 1;
397       }
398     }
399   }
400 
401   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
402   ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
403       << " The datarate for the file exceeds the target!";
404 
405   ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
406       << " The datarate for the file missed the target!";
407 
408   free(roi_.roi_map);
409 }
410 
TEST_P(DatarateTestRealTime,GFBoost)411 TEST_P(DatarateTestRealTime, GFBoost) {
412   denoiser_on_ = 0;
413   cfg_.rc_buf_initial_sz = 500;
414   cfg_.rc_dropframe_thresh = 0;
415   cfg_.rc_max_quantizer = 56;
416   cfg_.rc_end_usage = VPX_CBR;
417   cfg_.g_error_resilient = 0;
418 
419   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
420                                        30, 1, 0, 300);
421   cfg_.rc_target_bitrate = 300;
422   ResetModel();
423   // Apply a gf boost.
424   gf_boost_ = 50;
425 
426   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
427   ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
428       << " The datarate for the file exceeds the target!";
429 
430   ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
431       << " The datarate for the file missed the target!";
432 }
433 
TEST_P(DatarateTestRealTime,NV12)434 TEST_P(DatarateTestRealTime, NV12) {
435   denoiser_on_ = 0;
436   cfg_.rc_buf_initial_sz = 500;
437   cfg_.rc_dropframe_thresh = 0;
438   cfg_.rc_max_quantizer = 56;
439   cfg_.rc_end_usage = VPX_CBR;
440   cfg_.g_error_resilient = 0;
441   ::libvpx_test::YUVVideoSource video("hantro_collage_w352h288_nv12.yuv",
442                                       VPX_IMG_FMT_NV12, 352, 288, 30, 1, 0,
443                                       100);
444 
445   cfg_.rc_target_bitrate = 200;
446   ResetModel();
447 
448   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
449   ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
450       << " The datarate for the file exceeds the target!";
451 
452   ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
453       << " The datarate for the file missed the target!";
454 }
455 
456 VP8_INSTANTIATE_TEST_SUITE(DatarateTestLarge, ALL_TEST_MODES,
457                            ::testing::Values(0));
458 VP8_INSTANTIATE_TEST_SUITE(DatarateTestRealTime,
459                            ::testing::Values(::libvpx_test::kRealTime),
460                            ::testing::Values(-6, -12));
461 }  // namespace
462