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 <stdio.h>
11
12 #include <climits>
13 #include <vector>
14 #include "third_party/googletest/src/include/gtest/gtest.h"
15 #include "test/codec_factory.h"
16 #include "test/encode_test_driver.h"
17 #include "test/i420_video_source.h"
18 #include "test/video_source.h"
19 #include "test/util.h"
20
21 // Enable(1) or Disable(0) writing of the compressed bitstream.
22 #define WRITE_COMPRESSED_STREAM 0
23
24 namespace {
25
26 #if WRITE_COMPRESSED_STREAM
mem_put_le16(char * const mem,const unsigned int val)27 static void mem_put_le16(char *const mem, const unsigned int val) {
28 mem[0] = val;
29 mem[1] = val >> 8;
30 }
31
mem_put_le32(char * const mem,const unsigned int val)32 static void mem_put_le32(char *const mem, const unsigned int val) {
33 mem[0] = val;
34 mem[1] = val >> 8;
35 mem[2] = val >> 16;
36 mem[3] = val >> 24;
37 }
38
write_ivf_file_header(const vpx_codec_enc_cfg_t * const cfg,int frame_cnt,FILE * const outfile)39 static void write_ivf_file_header(const vpx_codec_enc_cfg_t *const cfg,
40 int frame_cnt, FILE *const outfile) {
41 char header[32];
42
43 header[0] = 'D';
44 header[1] = 'K';
45 header[2] = 'I';
46 header[3] = 'F';
47 mem_put_le16(header + 4, 0); /* version */
48 mem_put_le16(header + 6, 32); /* headersize */
49 mem_put_le32(header + 8, 0x30395056); /* fourcc (vp9) */
50 mem_put_le16(header + 12, cfg->g_w); /* width */
51 mem_put_le16(header + 14, cfg->g_h); /* height */
52 mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */
53 mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */
54 mem_put_le32(header + 24, frame_cnt); /* length */
55 mem_put_le32(header + 28, 0); /* unused */
56
57 (void)fwrite(header, 1, 32, outfile);
58 }
59
write_ivf_frame_size(FILE * const outfile,const size_t size)60 static void write_ivf_frame_size(FILE *const outfile, const size_t size) {
61 char header[4];
62 mem_put_le32(header, static_cast<unsigned int>(size));
63 (void)fwrite(header, 1, 4, outfile);
64 }
65
write_ivf_frame_header(const vpx_codec_cx_pkt_t * const pkt,FILE * const outfile)66 static void write_ivf_frame_header(const vpx_codec_cx_pkt_t *const pkt,
67 FILE *const outfile) {
68 char header[12];
69 vpx_codec_pts_t pts;
70
71 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) return;
72
73 pts = pkt->data.frame.pts;
74 mem_put_le32(header, static_cast<unsigned int>(pkt->data.frame.sz));
75 mem_put_le32(header + 4, pts & 0xFFFFFFFF);
76 mem_put_le32(header + 8, pts >> 32);
77
78 (void)fwrite(header, 1, 12, outfile);
79 }
80 #endif // WRITE_COMPRESSED_STREAM
81
82 const unsigned int kInitialWidth = 320;
83 const unsigned int kInitialHeight = 240;
84
85 struct FrameInfo {
FrameInfo__anon9dd736bd0111::FrameInfo86 FrameInfo(vpx_codec_pts_t _pts, unsigned int _w, unsigned int _h)
87 : pts(_pts), w(_w), h(_h) {}
88
89 vpx_codec_pts_t pts;
90 unsigned int w;
91 unsigned int h;
92 };
93
ScaleForFrameNumber(unsigned int frame,unsigned int initial_w,unsigned int initial_h,unsigned int * w,unsigned int * h,bool flag_codec,bool smaller_width_larger_size_)94 void ScaleForFrameNumber(unsigned int frame, unsigned int initial_w,
95 unsigned int initial_h, unsigned int *w,
96 unsigned int *h, bool flag_codec,
97 bool smaller_width_larger_size_) {
98 *w = initial_w;
99 *h = initial_h;
100
101 if (smaller_width_larger_size_) {
102 if (frame < 30) {
103 return;
104 }
105 *w = initial_w * 7 / 10;
106 *h = initial_h * 16 / 10;
107 return;
108 }
109 if (frame < 10) {
110 return;
111 }
112 if (frame < 20) {
113 *w = initial_w * 3 / 4;
114 *h = initial_h * 3 / 4;
115 return;
116 }
117 if (frame < 30) {
118 *w = initial_w / 2;
119 *h = initial_h / 2;
120 return;
121 }
122 if (frame < 40) {
123 return;
124 }
125 if (frame < 50) {
126 *w = initial_w * 3 / 4;
127 *h = initial_h * 3 / 4;
128 return;
129 }
130 if (frame < 60) {
131 *w = initial_w / 2;
132 *h = initial_h / 2;
133 return;
134 }
135 if (frame < 70) {
136 return;
137 }
138 if (frame < 80) {
139 *w = initial_w * 3 / 4;
140 *h = initial_h * 3 / 4;
141 return;
142 }
143 if (frame < 90) {
144 *w = initial_w / 2;
145 *h = initial_h / 2;
146 return;
147 }
148 if (frame < 100) {
149 *w = initial_w * 3 / 4;
150 *h = initial_h * 3 / 4;
151 return;
152 }
153 if (frame < 110) {
154 return;
155 }
156 if (frame < 120) {
157 *w = initial_w * 3 / 4;
158 *h = initial_h * 3 / 4;
159 return;
160 }
161 if (frame < 130) {
162 *w = initial_w / 2;
163 *h = initial_h / 2;
164 return;
165 }
166 if (frame < 140) {
167 *w = initial_w * 3 / 4;
168 *h = initial_h * 3 / 4;
169 return;
170 }
171 if (frame < 150) {
172 return;
173 }
174 if (frame < 160) {
175 *w = initial_w * 3 / 4;
176 *h = initial_h * 3 / 4;
177 return;
178 }
179 if (frame < 170) {
180 *w = initial_w / 2;
181 *h = initial_h / 2;
182 return;
183 }
184 if (frame < 180) {
185 *w = initial_w * 3 / 4;
186 *h = initial_h * 3 / 4;
187 return;
188 }
189 if (frame < 190) {
190 return;
191 }
192 if (frame < 200) {
193 *w = initial_w * 3 / 4;
194 *h = initial_h * 3 / 4;
195 return;
196 }
197 if (frame < 210) {
198 *w = initial_w / 2;
199 *h = initial_h / 2;
200 return;
201 }
202 if (frame < 220) {
203 *w = initial_w * 3 / 4;
204 *h = initial_h * 3 / 4;
205 return;
206 }
207 if (frame < 230) {
208 return;
209 }
210 if (frame < 240) {
211 *w = initial_w * 3 / 4;
212 *h = initial_h * 3 / 4;
213 return;
214 }
215 if (frame < 250) {
216 *w = initial_w / 2;
217 *h = initial_h / 2;
218 return;
219 }
220 if (frame < 260) {
221 return;
222 }
223 // Go down very low.
224 if (frame < 270) {
225 *w = initial_w / 4;
226 *h = initial_h / 4;
227 return;
228 }
229 if (flag_codec == 1) {
230 // Cases that only works for VP9.
231 // For VP9: Swap width and height of original.
232 if (frame < 320) {
233 return;
234 }
235 }
236 }
237
238 class ResizingVideoSource : public ::libvpx_test::DummyVideoSource {
239 public:
ResizingVideoSource()240 ResizingVideoSource() {
241 SetSize(kInitialWidth, kInitialHeight);
242 limit_ = 350;
243 smaller_width_larger_size_ = false;
244 }
245 bool flag_codec_;
246 bool smaller_width_larger_size_;
247 ~ResizingVideoSource() override = default;
248
249 protected:
Next()250 void Next() override {
251 ++frame_;
252 unsigned int width = 0;
253 unsigned int height = 0;
254 ScaleForFrameNumber(frame_, kInitialWidth, kInitialHeight, &width, &height,
255 flag_codec_, smaller_width_larger_size_);
256 SetSize(width, height);
257 FillFrame();
258 }
259 };
260
261 class ResizeTest
262 : public ::libvpx_test::EncoderTest,
263 public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
264 protected:
ResizeTest()265 ResizeTest() : EncoderTest(GET_PARAM(0)) {}
266
267 ~ResizeTest() override = default;
268
SetUp()269 void SetUp() override {
270 InitializeConfig();
271 SetMode(GET_PARAM(1));
272 }
273
FramePktHook(const vpx_codec_cx_pkt_t * pkt)274 void FramePktHook(const vpx_codec_cx_pkt_t *pkt) override {
275 ASSERT_NE(static_cast<int>(pkt->data.frame.width[0]), 0);
276 ASSERT_NE(static_cast<int>(pkt->data.frame.height[0]), 0);
277 encode_frame_width_.push_back(pkt->data.frame.width[0]);
278 encode_frame_height_.push_back(pkt->data.frame.height[0]);
279 }
280
GetFrameWidth(size_t idx) const281 unsigned int GetFrameWidth(size_t idx) const {
282 return encode_frame_width_[idx];
283 }
284
GetFrameHeight(size_t idx) const285 unsigned int GetFrameHeight(size_t idx) const {
286 return encode_frame_height_[idx];
287 }
288
DecompressedFrameHook(const vpx_image_t & img,vpx_codec_pts_t pts)289 void DecompressedFrameHook(const vpx_image_t &img,
290 vpx_codec_pts_t pts) override {
291 frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
292 }
293
294 std::vector<FrameInfo> frame_info_list_;
295 std::vector<unsigned int> encode_frame_width_;
296 std::vector<unsigned int> encode_frame_height_;
297 };
298
TEST_P(ResizeTest,TestExternalResizeWorks)299 TEST_P(ResizeTest, TestExternalResizeWorks) {
300 ResizingVideoSource video;
301 video.flag_codec_ = false;
302 video.smaller_width_larger_size_ = false;
303 cfg_.g_lag_in_frames = 0;
304 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
305
306 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
307 info != frame_info_list_.end(); ++info) {
308 const unsigned int frame = static_cast<unsigned>(info->pts);
309 unsigned int expected_w;
310 unsigned int expected_h;
311 const size_t idx = info - frame_info_list_.begin();
312 ASSERT_EQ(info->w, GetFrameWidth(idx));
313 ASSERT_EQ(info->h, GetFrameHeight(idx));
314 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w,
315 &expected_h, video.flag_codec_,
316 video.smaller_width_larger_size_);
317 EXPECT_EQ(expected_w, info->w)
318 << "Frame " << frame << " had unexpected width";
319 EXPECT_EQ(expected_h, info->h)
320 << "Frame " << frame << " had unexpected height";
321 }
322 }
323
324 const unsigned int kStepDownFrame = 3;
325 const unsigned int kStepUpFrame = 6;
326
327 class ResizeInternalTest : public ResizeTest {
328 protected:
329 #if WRITE_COMPRESSED_STREAM
ResizeInternalTest()330 ResizeInternalTest()
331 : ResizeTest(), frame0_psnr_(0.0), outfile_(nullptr), out_frames_(0) {}
332 #else
333 ResizeInternalTest() : ResizeTest(), frame0_psnr_(0.0) {}
334 #endif
335
336 ~ResizeInternalTest() override = default;
337
BeginPassHook(unsigned int)338 void BeginPassHook(unsigned int /*pass*/) override {
339 #if WRITE_COMPRESSED_STREAM
340 outfile_ = fopen("vp90-2-05-resize.ivf", "wb");
341 #endif
342 }
343
EndPassHook()344 void EndPassHook() override {
345 #if WRITE_COMPRESSED_STREAM
346 if (outfile_) {
347 if (!fseek(outfile_, 0, SEEK_SET))
348 write_ivf_file_header(&cfg_, out_frames_, outfile_);
349 fclose(outfile_);
350 outfile_ = nullptr;
351 }
352 #endif
353 }
354
PreEncodeFrameHook(libvpx_test::VideoSource * video,libvpx_test::Encoder * encoder)355 void PreEncodeFrameHook(libvpx_test::VideoSource *video,
356 libvpx_test::Encoder *encoder) override {
357 if (change_config_) {
358 int new_q = 60;
359 if (video->frame() == 0) {
360 struct vpx_scaling_mode mode = { VP8E_ONETWO, VP8E_ONETWO };
361 encoder->Control(VP8E_SET_SCALEMODE, &mode);
362 }
363 if (video->frame() == 1) {
364 struct vpx_scaling_mode mode = { VP8E_NORMAL, VP8E_NORMAL };
365 encoder->Control(VP8E_SET_SCALEMODE, &mode);
366 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = new_q;
367 encoder->Config(&cfg_);
368 }
369 } else {
370 if (video->frame() == kStepDownFrame) {
371 struct vpx_scaling_mode mode = { VP8E_FOURFIVE, VP8E_THREEFIVE };
372 encoder->Control(VP8E_SET_SCALEMODE, &mode);
373 }
374 if (video->frame() == kStepUpFrame) {
375 struct vpx_scaling_mode mode = { VP8E_NORMAL, VP8E_NORMAL };
376 encoder->Control(VP8E_SET_SCALEMODE, &mode);
377 }
378 }
379 }
380
PSNRPktHook(const vpx_codec_cx_pkt_t * pkt)381 void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) override {
382 if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
383 EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 2.0);
384 }
385
386 #if WRITE_COMPRESSED_STREAM
FramePktHook(const vpx_codec_cx_pkt_t * pkt)387 void FramePktHook(const vpx_codec_cx_pkt_t *pkt) override {
388 ++out_frames_;
389
390 // Write initial file header if first frame.
391 if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
392
393 // Write frame header and data.
394 write_ivf_frame_header(pkt, outfile_);
395 (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
396 }
397 #endif
398
399 double frame0_psnr_;
400 bool change_config_;
401 #if WRITE_COMPRESSED_STREAM
402 FILE *outfile_;
403 unsigned int out_frames_;
404 #endif
405 };
406
TEST_P(ResizeInternalTest,TestInternalResizeWorks)407 TEST_P(ResizeInternalTest, TestInternalResizeWorks) {
408 ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
409 30, 1, 0, 10);
410 init_flags_ = VPX_CODEC_USE_PSNR;
411 change_config_ = false;
412
413 // q picked such that initial keyframe on this clip is ~30dB PSNR
414 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
415
416 // If the number of frames being encoded is smaller than g_lag_in_frames
417 // the encoded frame is unavailable using the current API. Comparing
418 // frames to detect mismatch would then not be possible. Set
419 // g_lag_in_frames = 0 to get around this.
420 cfg_.g_lag_in_frames = 0;
421 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
422
423 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
424 info != frame_info_list_.end(); ++info) {
425 const vpx_codec_pts_t pts = info->pts;
426 if (pts >= kStepDownFrame && pts < kStepUpFrame) {
427 ASSERT_EQ(282U, info->w) << "Frame " << pts << " had unexpected width";
428 ASSERT_EQ(173U, info->h) << "Frame " << pts << " had unexpected height";
429 } else {
430 EXPECT_EQ(352U, info->w) << "Frame " << pts << " had unexpected width";
431 EXPECT_EQ(288U, info->h) << "Frame " << pts << " had unexpected height";
432 }
433 }
434 }
435
TEST_P(ResizeInternalTest,TestInternalResizeChangeConfig)436 TEST_P(ResizeInternalTest, TestInternalResizeChangeConfig) {
437 ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
438 30, 1, 0, 10);
439 cfg_.g_w = 352;
440 cfg_.g_h = 288;
441 change_config_ = true;
442 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
443 }
444
445 class ResizeRealtimeTest
446 : public ::libvpx_test::EncoderTest,
447 public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
448 protected:
ResizeRealtimeTest()449 ResizeRealtimeTest() : EncoderTest(GET_PARAM(0)) {}
450 ~ResizeRealtimeTest() override = default;
451
PreEncodeFrameHook(libvpx_test::VideoSource * video,libvpx_test::Encoder * encoder)452 void PreEncodeFrameHook(libvpx_test::VideoSource *video,
453 libvpx_test::Encoder *encoder) override {
454 if (video->frame() == 0) {
455 encoder->Control(VP9E_SET_AQ_MODE, 3);
456 encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
457 }
458
459 if (change_bitrate_ && video->frame() == 120) {
460 change_bitrate_ = false;
461 cfg_.rc_target_bitrate = 500;
462 encoder->Config(&cfg_);
463 }
464 }
465
SetUp()466 void SetUp() override {
467 InitializeConfig();
468 SetMode(GET_PARAM(1));
469 set_cpu_used_ = GET_PARAM(2);
470 }
471
DecompressedFrameHook(const vpx_image_t & img,vpx_codec_pts_t pts)472 void DecompressedFrameHook(const vpx_image_t &img,
473 vpx_codec_pts_t pts) override {
474 frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
475 }
476
MismatchHook(const vpx_image_t * img1,const vpx_image_t * img2)477 void MismatchHook(const vpx_image_t *img1, const vpx_image_t *img2) override {
478 double mismatch_psnr = compute_psnr(img1, img2);
479 mismatch_psnr_ += mismatch_psnr;
480 ++mismatch_nframes_;
481 }
482
FramePktHook(const vpx_codec_cx_pkt_t * pkt)483 void FramePktHook(const vpx_codec_cx_pkt_t *pkt) override {
484 ASSERT_NE(static_cast<int>(pkt->data.frame.width[0]), 0);
485 ASSERT_NE(static_cast<int>(pkt->data.frame.height[0]), 0);
486 encode_frame_width_.push_back(pkt->data.frame.width[0]);
487 encode_frame_height_.push_back(pkt->data.frame.height[0]);
488 }
489
GetMismatchFrames()490 unsigned int GetMismatchFrames() { return mismatch_nframes_; }
491
GetFrameWidth(size_t idx) const492 unsigned int GetFrameWidth(size_t idx) const {
493 return encode_frame_width_[idx];
494 }
495
GetFrameHeight(size_t idx) const496 unsigned int GetFrameHeight(size_t idx) const {
497 return encode_frame_height_[idx];
498 }
499
DefaultConfig()500 void DefaultConfig() {
501 cfg_.rc_buf_initial_sz = 500;
502 cfg_.rc_buf_optimal_sz = 600;
503 cfg_.rc_buf_sz = 1000;
504 cfg_.rc_min_quantizer = 2;
505 cfg_.rc_max_quantizer = 56;
506 cfg_.rc_undershoot_pct = 50;
507 cfg_.rc_overshoot_pct = 50;
508 cfg_.rc_end_usage = VPX_CBR;
509 cfg_.kf_mode = VPX_KF_AUTO;
510 cfg_.g_lag_in_frames = 0;
511 cfg_.kf_min_dist = cfg_.kf_max_dist = 3000;
512 // Enable dropped frames.
513 cfg_.rc_dropframe_thresh = 1;
514 // Enable error_resilience mode.
515 cfg_.g_error_resilient = 1;
516 // Enable dynamic resizing.
517 cfg_.rc_resize_allowed = 1;
518 // Run at low bitrate.
519 cfg_.rc_target_bitrate = 200;
520 }
521
522 std::vector<FrameInfo> frame_info_list_;
523 int set_cpu_used_;
524 bool change_bitrate_;
525 double mismatch_psnr_;
526 int mismatch_nframes_;
527 std::vector<unsigned int> encode_frame_width_;
528 std::vector<unsigned int> encode_frame_height_;
529 };
530
TEST_P(ResizeRealtimeTest,TestExternalResizeWorks)531 TEST_P(ResizeRealtimeTest, TestExternalResizeWorks) {
532 ResizingVideoSource video;
533 video.flag_codec_ = true;
534 video.smaller_width_larger_size_ = false;
535 DefaultConfig();
536 // Disable internal resize for this test.
537 cfg_.rc_resize_allowed = 0;
538 change_bitrate_ = false;
539 mismatch_psnr_ = 0.0;
540 mismatch_nframes_ = 0;
541 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
542
543 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
544 info != frame_info_list_.end(); ++info) {
545 const unsigned int frame = static_cast<unsigned>(info->pts);
546 unsigned int expected_w;
547 unsigned int expected_h;
548 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w,
549 &expected_h, video.flag_codec_,
550 video.smaller_width_larger_size_);
551 EXPECT_EQ(expected_w, info->w)
552 << "Frame " << frame << " had unexpected width";
553 EXPECT_EQ(expected_h, info->h)
554 << "Frame " << frame << " had unexpected height";
555 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
556 }
557 }
558
TEST_P(ResizeRealtimeTest,TestExternalResizeSmallerWidthBiggerSize)559 TEST_P(ResizeRealtimeTest, TestExternalResizeSmallerWidthBiggerSize) {
560 ResizingVideoSource video;
561 video.flag_codec_ = true;
562 video.smaller_width_larger_size_ = true;
563 DefaultConfig();
564 // Disable internal resize for this test.
565 cfg_.rc_resize_allowed = 0;
566 change_bitrate_ = false;
567 mismatch_psnr_ = 0.0;
568 mismatch_nframes_ = 0;
569 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
570
571 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
572 info != frame_info_list_.end(); ++info) {
573 const unsigned int frame = static_cast<unsigned>(info->pts);
574 unsigned int expected_w;
575 unsigned int expected_h;
576 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w,
577 &expected_h, video.flag_codec_,
578 video.smaller_width_larger_size_);
579 EXPECT_EQ(expected_w, info->w)
580 << "Frame " << frame << " had unexpected width";
581 EXPECT_EQ(expected_h, info->h)
582 << "Frame " << frame << " had unexpected height";
583 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
584 }
585 }
586
587 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
588 // Run at low bitrate, with resize_allowed = 1, and verify that we get
589 // one resize down event.
TEST_P(ResizeRealtimeTest,TestInternalResizeDown)590 TEST_P(ResizeRealtimeTest, TestInternalResizeDown) {
591 ::libvpx_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
592 0, 299);
593 DefaultConfig();
594 cfg_.g_w = 640;
595 cfg_.g_h = 480;
596 change_bitrate_ = false;
597 mismatch_psnr_ = 0.0;
598 mismatch_nframes_ = 0;
599 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
600
601 unsigned int last_w = cfg_.g_w;
602 unsigned int last_h = cfg_.g_h;
603 int resize_count = 0;
604 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
605 info != frame_info_list_.end(); ++info) {
606 if (info->w != last_w || info->h != last_h) {
607 // Verify that resize down occurs.
608 ASSERT_LT(info->w, last_w);
609 ASSERT_LT(info->h, last_h);
610 last_w = info->w;
611 last_h = info->h;
612 resize_count++;
613 }
614 }
615
616 #if CONFIG_VP9_DECODER
617 // Verify that we get 1 resize down event in this test.
618 ASSERT_EQ(1, resize_count) << "Resizing should occur.";
619 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
620 #else
621 printf("Warning: VP9 decoder unavailable, unable to check resize count!\n");
622 #endif
623 }
624
625 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
626 // Start at low target bitrate, raise the bitrate in the middle of the clip,
627 // scaling-up should occur after bitrate changed.
TEST_P(ResizeRealtimeTest,TestInternalResizeDownUpChangeBitRate)628 TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRate) {
629 ::libvpx_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
630 0, 400);
631 DefaultConfig();
632 cfg_.g_w = 640;
633 cfg_.g_h = 480;
634 change_bitrate_ = true;
635 mismatch_psnr_ = 0.0;
636 mismatch_nframes_ = 0;
637 // Disable dropped frames.
638 cfg_.rc_dropframe_thresh = 0;
639 // Starting bitrate low.
640 cfg_.rc_target_bitrate = 80;
641 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
642
643 unsigned int last_w = cfg_.g_w;
644 unsigned int last_h = cfg_.g_h;
645 int resize_count = 0;
646 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
647 info != frame_info_list_.end(); ++info) {
648 const size_t idx = info - frame_info_list_.begin();
649 ASSERT_EQ(info->w, GetFrameWidth(idx));
650 ASSERT_EQ(info->h, GetFrameHeight(idx));
651 if (info->w != last_w || info->h != last_h) {
652 resize_count++;
653 if (resize_count <= 2) {
654 // Verify that resize down occurs.
655 ASSERT_LT(info->w, last_w);
656 ASSERT_LT(info->h, last_h);
657 } else if (resize_count > 2) {
658 // Verify that resize up occurs.
659 ASSERT_GT(info->w, last_w);
660 ASSERT_GT(info->h, last_h);
661 }
662 last_w = info->w;
663 last_h = info->h;
664 }
665 }
666
667 #if CONFIG_VP9_DECODER
668 // Verify that we get 4 resize events in this test.
669 ASSERT_EQ(resize_count, 4) << "Resizing should occur twice.";
670 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
671 #else
672 printf("Warning: VP9 decoder unavailable, unable to check resize count!\n");
673 #endif
674 }
675
CspForFrameNumber(int frame)676 vpx_img_fmt_t CspForFrameNumber(int frame) {
677 if (frame < 10) return VPX_IMG_FMT_I420;
678 if (frame < 20) return VPX_IMG_FMT_I444;
679 return VPX_IMG_FMT_I420;
680 }
681
682 class ResizeCspTest : public ResizeTest {
683 protected:
684 #if WRITE_COMPRESSED_STREAM
ResizeCspTest()685 ResizeCspTest()
686 : ResizeTest(), frame0_psnr_(0.0), outfile_(nullptr), out_frames_(0) {}
687 #else
688 ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
689 #endif
690
691 ~ResizeCspTest() override = default;
692
BeginPassHook(unsigned int)693 void BeginPassHook(unsigned int /*pass*/) override {
694 #if WRITE_COMPRESSED_STREAM
695 outfile_ = fopen("vp91-2-05-cspchape.ivf", "wb");
696 #endif
697 }
698
EndPassHook()699 void EndPassHook() override {
700 #if WRITE_COMPRESSED_STREAM
701 if (outfile_) {
702 if (!fseek(outfile_, 0, SEEK_SET))
703 write_ivf_file_header(&cfg_, out_frames_, outfile_);
704 fclose(outfile_);
705 outfile_ = nullptr;
706 }
707 #endif
708 }
709
PreEncodeFrameHook(libvpx_test::VideoSource * video,libvpx_test::Encoder * encoder)710 void PreEncodeFrameHook(libvpx_test::VideoSource *video,
711 libvpx_test::Encoder *encoder) override {
712 if (CspForFrameNumber(video->frame()) != VPX_IMG_FMT_I420 &&
713 cfg_.g_profile != 1) {
714 cfg_.g_profile = 1;
715 encoder->Config(&cfg_);
716 }
717 if (CspForFrameNumber(video->frame()) == VPX_IMG_FMT_I420 &&
718 cfg_.g_profile != 0) {
719 cfg_.g_profile = 0;
720 encoder->Config(&cfg_);
721 }
722 }
723
PSNRPktHook(const vpx_codec_cx_pkt_t * pkt)724 void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) override {
725 if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
726 EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 2.0);
727 }
728
729 #if WRITE_COMPRESSED_STREAM
FramePktHook(const vpx_codec_cx_pkt_t * pkt)730 void FramePktHook(const vpx_codec_cx_pkt_t *pkt) override {
731 ++out_frames_;
732
733 // Write initial file header if first frame.
734 if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
735
736 // Write frame header and data.
737 write_ivf_frame_header(pkt, outfile_);
738 (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
739 }
740 #endif
741
742 double frame0_psnr_;
743 #if WRITE_COMPRESSED_STREAM
744 FILE *outfile_;
745 unsigned int out_frames_;
746 #endif
747 };
748
749 class ResizingCspVideoSource : public ::libvpx_test::DummyVideoSource {
750 public:
ResizingCspVideoSource()751 ResizingCspVideoSource() {
752 SetSize(kInitialWidth, kInitialHeight);
753 limit_ = 30;
754 }
755
756 ~ResizingCspVideoSource() override = default;
757
758 protected:
Next()759 void Next() override {
760 ++frame_;
761 SetImageFormat(CspForFrameNumber(frame_));
762 FillFrame();
763 }
764 };
765
TEST_P(ResizeCspTest,TestResizeCspWorks)766 TEST_P(ResizeCspTest, TestResizeCspWorks) {
767 ResizingCspVideoSource video;
768 init_flags_ = VPX_CODEC_USE_PSNR;
769 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
770 cfg_.g_lag_in_frames = 0;
771 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
772 }
773
774 VP8_INSTANTIATE_TEST_SUITE(ResizeTest, ONE_PASS_TEST_MODES);
775 VP9_INSTANTIATE_TEST_SUITE(ResizeTest, ONE_PASS_TEST_MODES);
776 VP9_INSTANTIATE_TEST_SUITE(ResizeInternalTest,
777 ::testing::Values(::libvpx_test::kOnePassBest));
778 VP9_INSTANTIATE_TEST_SUITE(ResizeRealtimeTest,
779 ::testing::Values(::libvpx_test::kRealTime),
780 ::testing::Range(5, 9));
781 VP9_INSTANTIATE_TEST_SUITE(ResizeCspTest,
782 ::testing::Values(::libvpx_test::kRealTime));
783 } // namespace
784