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 "third_party/googletest/src/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_codec.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 
~DatarateTestLarge()27   virtual ~DatarateTestLarge() {}
28 
29  protected:
SetUp()30   virtual void SetUp() {
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   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
51                                   ::libvpx_test::Encoder *encoder) {
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   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
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(void)124   virtual void EndPassHook(void) {
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 
263   vpx_codec_pts_t last_pts_;
264   int64_t bits_in_buffer_model_;
265   double timebase_;
266   int frame_number_;
267   vpx_codec_pts_t first_drop_;
268   int64_t bits_total_;
269   double duration_;
270   double file_datarate_;
271   double effective_datarate_;
272   int64_t bits_in_last_frame_;
273   int denoiser_on_;
274   int denoiser_offon_test_;
275   int denoiser_offon_period_;
276   int set_cpu_used_;
277   int gf_boost_;
278   bool use_roi_;
279   vpx_roi_map_t roi_;
280 };
281 
282 #if CONFIG_TEMPORAL_DENOISING
283 // Check basic datarate targeting, for a single bitrate, but loop over the
284 // various denoiser settings.
TEST_P(DatarateTestLarge,DenoiserLevels)285 TEST_P(DatarateTestLarge, DenoiserLevels) { DenoiserLevelsTest(); }
286 
287 // Check basic datarate targeting, for a single bitrate, when denoiser is off
288 // and on.
TEST_P(DatarateTestLarge,DenoiserOffOn)289 TEST_P(DatarateTestLarge, DenoiserOffOn) { DenoiserOffOnTest(); }
290 #endif  // CONFIG_TEMPORAL_DENOISING
291 
TEST_P(DatarateTestLarge,BasicBufferModel)292 TEST_P(DatarateTestLarge, BasicBufferModel) { BasicBufferModelTest(); }
293 
TEST_P(DatarateTestLarge,ChangingDropFrameThresh)294 TEST_P(DatarateTestLarge, ChangingDropFrameThresh) {
295   ChangingDropFrameThreshTest();
296 }
297 
TEST_P(DatarateTestLarge,DropFramesMultiThreads)298 TEST_P(DatarateTestLarge, DropFramesMultiThreads) {
299   DropFramesMultiThreadsTest();
300 }
301 
302 class DatarateTestRealTime : public DatarateTestLarge {
303  public:
~DatarateTestRealTime()304   virtual ~DatarateTestRealTime() {}
305 };
306 
307 #if CONFIG_TEMPORAL_DENOISING
308 // Check basic datarate targeting, for a single bitrate, but loop over the
309 // various denoiser settings.
TEST_P(DatarateTestRealTime,DenoiserLevels)310 TEST_P(DatarateTestRealTime, DenoiserLevels) { DenoiserLevelsTest(); }
311 
312 // Check basic datarate targeting, for a single bitrate, when denoiser is off
313 // and on.
TEST_P(DatarateTestRealTime,DenoiserOffOn)314 TEST_P(DatarateTestRealTime, DenoiserOffOn) {}
315 #endif  // CONFIG_TEMPORAL_DENOISING
316 
TEST_P(DatarateTestRealTime,BasicBufferModel)317 TEST_P(DatarateTestRealTime, BasicBufferModel) { BasicBufferModelTest(); }
318 
TEST_P(DatarateTestRealTime,ChangingDropFrameThresh)319 TEST_P(DatarateTestRealTime, ChangingDropFrameThresh) {
320   ChangingDropFrameThreshTest();
321 }
322 
TEST_P(DatarateTestRealTime,DropFramesMultiThreads)323 TEST_P(DatarateTestRealTime, DropFramesMultiThreads) {
324   DropFramesMultiThreadsTest();
325 }
326 
TEST_P(DatarateTestRealTime,RegionOfInterest)327 TEST_P(DatarateTestRealTime, RegionOfInterest) {
328   denoiser_on_ = 0;
329   cfg_.rc_buf_initial_sz = 500;
330   cfg_.rc_dropframe_thresh = 0;
331   cfg_.rc_max_quantizer = 56;
332   cfg_.rc_end_usage = VPX_CBR;
333   // Encode using multiple threads.
334   cfg_.g_threads = 2;
335 
336   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
337                                        30, 1, 0, 300);
338   cfg_.rc_target_bitrate = 450;
339   cfg_.g_w = 352;
340   cfg_.g_h = 288;
341 
342   ResetModel();
343 
344   // Set ROI parameters
345   use_roi_ = true;
346   memset(&roi_, 0, sizeof(roi_));
347 
348   roi_.rows = (cfg_.g_h + 15) / 16;
349   roi_.cols = (cfg_.g_w + 15) / 16;
350 
351   roi_.delta_q[0] = 0;
352   roi_.delta_q[1] = -20;
353   roi_.delta_q[2] = 0;
354   roi_.delta_q[3] = 0;
355 
356   roi_.delta_lf[0] = 0;
357   roi_.delta_lf[1] = -20;
358   roi_.delta_lf[2] = 0;
359   roi_.delta_lf[3] = 0;
360 
361   roi_.static_threshold[0] = 0;
362   roi_.static_threshold[1] = 1000;
363   roi_.static_threshold[2] = 0;
364   roi_.static_threshold[3] = 0;
365 
366   // Use 2 states: 1 is center square, 0 is the rest.
367   roi_.roi_map =
368       (uint8_t *)calloc(roi_.rows * roi_.cols, sizeof(*roi_.roi_map));
369   for (unsigned int i = 0; i < roi_.rows; ++i) {
370     for (unsigned int j = 0; j < roi_.cols; ++j) {
371       if (i > (roi_.rows >> 2) && i < ((roi_.rows * 3) >> 2) &&
372           j > (roi_.cols >> 2) && j < ((roi_.cols * 3) >> 2)) {
373         roi_.roi_map[i * roi_.cols + j] = 1;
374       }
375     }
376   }
377 
378   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
379   ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
380       << " The datarate for the file exceeds the target!";
381 
382   ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
383       << " The datarate for the file missed the target!";
384 
385   free(roi_.roi_map);
386 }
387 
TEST_P(DatarateTestRealTime,GFBoost)388 TEST_P(DatarateTestRealTime, GFBoost) {
389   denoiser_on_ = 0;
390   cfg_.rc_buf_initial_sz = 500;
391   cfg_.rc_dropframe_thresh = 0;
392   cfg_.rc_max_quantizer = 56;
393   cfg_.rc_end_usage = VPX_CBR;
394   cfg_.g_error_resilient = 0;
395 
396   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
397                                        30, 1, 0, 300);
398   cfg_.rc_target_bitrate = 300;
399   ResetModel();
400   // Apply a gf boost.
401   gf_boost_ = 50;
402 
403   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
404   ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95)
405       << " The datarate for the file exceeds the target!";
406 
407   ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4)
408       << " The datarate for the file missed the target!";
409 }
410 
411 VP8_INSTANTIATE_TEST_CASE(DatarateTestLarge, ALL_TEST_MODES,
412                           ::testing::Values(0));
413 VP8_INSTANTIATE_TEST_CASE(DatarateTestRealTime,
414                           ::testing::Values(::libvpx_test::kRealTime),
415                           ::testing::Values(-6, -12));
416 }  // namespace
417