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