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