1 /*
2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12 #include <climits>
13 #include <vector>
14 #include "aom_dsp/aom_dsp_common.h"
15 #include "common/tools_common.h"
16 #include "av1/encoder/encoder.h"
17 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
18 #include "test/codec_factory.h"
19 #include "test/encode_test_driver.h"
20 #include "test/i420_video_source.h"
21 #include "test/video_source.h"
22 #include "test/util.h"
23 #include "test/y4m_video_source.h"
24
25 // Enable(1) or Disable(0) writing of the compressed bitstream.
26 #define WRITE_COMPRESSED_STREAM 0
27
28 namespace {
29
30 #if WRITE_COMPRESSED_STREAM
mem_put_le16(char * const mem,unsigned int val)31 static void mem_put_le16(char *const mem, unsigned int val) {
32 mem[0] = val;
33 mem[1] = val >> 8;
34 }
35
mem_put_le32(char * const mem,unsigned int val)36 static void mem_put_le32(char *const mem, unsigned int val) {
37 mem[0] = val;
38 mem[1] = val >> 8;
39 mem[2] = val >> 16;
40 mem[3] = val >> 24;
41 }
42
write_ivf_file_header(const aom_codec_enc_cfg_t * const cfg,int frame_cnt,FILE * const outfile)43 static void write_ivf_file_header(const aom_codec_enc_cfg_t *const cfg,
44 int frame_cnt, FILE *const outfile) {
45 char header[32];
46
47 header[0] = 'D';
48 header[1] = 'K';
49 header[2] = 'I';
50 header[3] = 'F';
51 mem_put_le16(header + 4, 0); /* version */
52 mem_put_le16(header + 6, 32); /* headersize */
53 mem_put_le32(header + 8, AV1_FOURCC); /* fourcc (av1) */
54 mem_put_le16(header + 12, cfg->g_w); /* width */
55 mem_put_le16(header + 14, cfg->g_h); /* height */
56 mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */
57 mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */
58 mem_put_le32(header + 24, frame_cnt); /* length */
59 mem_put_le32(header + 28, 0); /* unused */
60
61 (void)fwrite(header, 1, 32, outfile);
62 }
63
write_ivf_frame_size(FILE * const outfile,const size_t size)64 static void write_ivf_frame_size(FILE *const outfile, const size_t size) {
65 char header[4];
66 mem_put_le32(header, static_cast<unsigned int>(size));
67 (void)fwrite(header, 1, 4, outfile);
68 }
69
write_ivf_frame_header(const aom_codec_cx_pkt_t * const pkt,FILE * const outfile)70 static void write_ivf_frame_header(const aom_codec_cx_pkt_t *const pkt,
71 FILE *const outfile) {
72 char header[12];
73 aom_codec_pts_t pts;
74
75 if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) return;
76
77 pts = pkt->data.frame.pts;
78 mem_put_le32(header, static_cast<unsigned int>(pkt->data.frame.sz));
79 mem_put_le32(header + 4, pts & 0xFFFFFFFF);
80 mem_put_le32(header + 8, pts >> 32);
81
82 (void)fwrite(header, 1, 12, outfile);
83 }
84 #endif // WRITE_COMPRESSED_STREAM
85
86 const unsigned int kInitialWidth = 320;
87 const unsigned int kInitialHeight = 240;
88
89 struct FrameInfo {
FrameInfo__anon52f4c5770111::FrameInfo90 FrameInfo(aom_codec_pts_t _pts, unsigned int _w, unsigned int _h)
91 : pts(_pts), w(_w), h(_h) {}
92
93 aom_codec_pts_t pts;
94 unsigned int w;
95 unsigned int h;
96 };
97
ScaleForFrameNumber(unsigned int frame,unsigned int initial_w,unsigned int initial_h,unsigned int * w,unsigned int * h,int flag_codec)98 void ScaleForFrameNumber(unsigned int frame, unsigned int initial_w,
99 unsigned int initial_h, unsigned int *w,
100 unsigned int *h, int flag_codec) {
101 if (frame < 10) {
102 *w = initial_w;
103 *h = initial_h;
104 return;
105 }
106 if (frame < 20) {
107 *w = initial_w * 3 / 4;
108 *h = initial_h * 3 / 4;
109 return;
110 }
111 if (frame < 30) {
112 *w = initial_w / 2;
113 *h = initial_h / 2;
114 return;
115 }
116 if (frame < 40) {
117 *w = initial_w;
118 *h = initial_h;
119 return;
120 }
121 if (frame < 50) {
122 *w = initial_w * 3 / 4;
123 *h = initial_h * 3 / 4;
124 return;
125 }
126 if (frame < 60) {
127 *w = initial_w / 2;
128 *h = initial_h / 2;
129 return;
130 }
131 if (frame < 70) {
132 *w = initial_w;
133 *h = initial_h;
134 return;
135 }
136 if (frame < 80) {
137 *w = initial_w * 3 / 4;
138 *h = initial_h * 3 / 4;
139 return;
140 }
141 if (frame < 90) {
142 *w = initial_w / 2;
143 *h = initial_h / 2;
144 return;
145 }
146 if (frame < 100) {
147 *w = initial_w * 3 / 4;
148 *h = initial_h * 3 / 4;
149 return;
150 }
151 if (frame < 110) {
152 *w = initial_w;
153 *h = initial_h;
154 return;
155 }
156 // Go down very low
157 if (frame < 120) {
158 *w = initial_w / 4;
159 *h = initial_h / 4;
160 return;
161 }
162 if (flag_codec == 1) {
163 // Cases that only works for AV1.
164 // For AV1: Swap width and height of original.
165 if (frame < 140) {
166 *w = initial_h;
167 *h = initial_w;
168 return;
169 }
170 }
171 *w = initial_w;
172 *h = initial_h;
173 }
174
175 class ResizingVideoSource : public ::libaom_test::DummyVideoSource {
176 public:
ResizingVideoSource()177 ResizingVideoSource() {
178 SetSize(kInitialWidth, kInitialHeight);
179 limit_ = 150;
180 }
181 int flag_codec_;
~ResizingVideoSource()182 virtual ~ResizingVideoSource() {}
183
184 protected:
Next()185 virtual void Next() {
186 ++frame_;
187 unsigned int width;
188 unsigned int height;
189 ScaleForFrameNumber(frame_, kInitialWidth, kInitialHeight, &width, &height,
190 flag_codec_);
191 SetSize(width, height);
192 FillFrame();
193 }
194 };
195
196 class ResizeTest
197 : public ::libaom_test::CodecTestWithParam<libaom_test::TestMode>,
198 public ::libaom_test::EncoderTest {
199 protected:
ResizeTest()200 ResizeTest() : EncoderTest(GET_PARAM(0)) {}
201
~ResizeTest()202 virtual ~ResizeTest() {}
203
SetUp()204 virtual void SetUp() { InitializeConfig(GET_PARAM(1)); }
205
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)206 virtual void PreEncodeFrameHook(libaom_test::VideoSource *video,
207 libaom_test::Encoder *encoder) {
208 if (video->frame() == 0) {
209 if (GET_PARAM(1) == ::libaom_test::kRealTime) {
210 encoder->Control(AV1E_SET_AQ_MODE, 3);
211 encoder->Control(AOME_SET_CPUUSED, 5);
212 encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
213 }
214 }
215 }
216
DecompressedFrameHook(const aom_image_t & img,aom_codec_pts_t pts)217 virtual void DecompressedFrameHook(const aom_image_t &img,
218 aom_codec_pts_t pts) {
219 frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
220 }
221
222 std::vector<FrameInfo> frame_info_list_;
223 };
224
TEST_P(ResizeTest,TestExternalResizeWorks)225 TEST_P(ResizeTest, TestExternalResizeWorks) {
226 ResizingVideoSource video;
227 video.flag_codec_ = 0;
228 cfg_.g_lag_in_frames = 0;
229 // We use max(kInitialWidth, kInitialHeight) because during the test
230 // the width and height of the frame are swapped
231 cfg_.g_forced_max_frame_width = cfg_.g_forced_max_frame_height =
232 AOMMAX(kInitialWidth, kInitialHeight);
233 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
234
235 // Check we decoded the same number of frames as we attempted to encode
236 ASSERT_EQ(frame_info_list_.size(), video.limit());
237
238 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
239 info != frame_info_list_.end(); ++info) {
240 const unsigned int frame = static_cast<unsigned>(info->pts);
241 unsigned int expected_w;
242 unsigned int expected_h;
243 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w,
244 &expected_h, 0);
245 EXPECT_EQ(expected_w, info->w)
246 << "Frame " << frame << " had unexpected width";
247 EXPECT_EQ(expected_h, info->h)
248 << "Frame " << frame << " had unexpected height";
249 }
250 }
251
252 const unsigned int kStepDownFrame = 3;
253 const unsigned int kStepUpFrame = 6;
254
255 #if !CONFIG_REALTIME_ONLY
256 class ResizeInternalTestLarge : public ResizeTest {
257 protected:
258 #if WRITE_COMPRESSED_STREAM
ResizeInternalTestLarge()259 ResizeInternalTestLarge()
260 : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL), out_frames_(0) {}
261 #else
262 ResizeInternalTestLarge() : ResizeTest(), frame0_psnr_(0.0) {}
263 #endif
264
~ResizeInternalTestLarge()265 virtual ~ResizeInternalTestLarge() {}
266
BeginPassHook(unsigned int)267 virtual void BeginPassHook(unsigned int /*pass*/) {
268 #if WRITE_COMPRESSED_STREAM
269 outfile_ = fopen("av10-2-05-resize.ivf", "wb");
270 #endif
271 }
272
EndPassHook()273 virtual void EndPassHook() {
274 #if WRITE_COMPRESSED_STREAM
275 if (outfile_) {
276 if (!fseek(outfile_, 0, SEEK_SET))
277 write_ivf_file_header(&cfg_, out_frames_, outfile_);
278 fclose(outfile_);
279 outfile_ = NULL;
280 }
281 #endif
282 }
283
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)284 virtual void PreEncodeFrameHook(libaom_test::VideoSource *video,
285 libaom_test::Encoder *encoder) {
286 if (change_config_) {
287 int new_q = 60;
288 if (video->frame() == 0) {
289 struct aom_scaling_mode mode = { AOME_ONETWO, AOME_ONETWO };
290 encoder->Control(AOME_SET_SCALEMODE, &mode);
291 }
292 if (video->frame() == 1) {
293 struct aom_scaling_mode mode = { AOME_NORMAL, AOME_NORMAL };
294 encoder->Control(AOME_SET_SCALEMODE, &mode);
295 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = new_q;
296 encoder->Config(&cfg_);
297 }
298 } else {
299 if (video->frame() >= kStepDownFrame && video->frame() < kStepUpFrame) {
300 struct aom_scaling_mode mode = { AOME_FOURFIVE, AOME_THREEFIVE };
301 encoder->Control(AOME_SET_SCALEMODE, &mode);
302 }
303 if (video->frame() >= kStepUpFrame) {
304 struct aom_scaling_mode mode = { AOME_NORMAL, AOME_NORMAL };
305 encoder->Control(AOME_SET_SCALEMODE, &mode);
306 }
307 }
308 }
309
PSNRPktHook(const aom_codec_cx_pkt_t * pkt)310 virtual void PSNRPktHook(const aom_codec_cx_pkt_t *pkt) {
311 if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
312 EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 4.1);
313 }
314
315 #if WRITE_COMPRESSED_STREAM
FramePktHook(const aom_codec_cx_pkt_t * pkt)316 virtual void FramePktHook(const aom_codec_cx_pkt_t *pkt) {
317 ++out_frames_;
318
319 // Write initial file header if first frame.
320 if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
321
322 // Write frame header and data.
323 write_ivf_frame_header(pkt, outfile_);
324 (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
325 }
326 #endif
327
328 double frame0_psnr_;
329 bool change_config_;
330 #if WRITE_COMPRESSED_STREAM
331 FILE *outfile_;
332 unsigned int out_frames_;
333 #endif
334 };
335
TEST_P(ResizeInternalTestLarge,TestInternalResizeWorks)336 TEST_P(ResizeInternalTestLarge, TestInternalResizeWorks) {
337 ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
338 30, 1, 0, 10);
339 init_flags_ = AOM_CODEC_USE_PSNR;
340 change_config_ = false;
341
342 // q picked such that initial keyframe on this clip is ~30dB PSNR
343 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
344
345 // If the number of frames being encoded is smaller than g_lag_in_frames
346 // the encoded frame is unavailable using the current API. Comparing
347 // frames to detect mismatch would then not be possible. Set
348 // g_lag_in_frames = 0 to get around this.
349 cfg_.g_lag_in_frames = 0;
350 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
351
352 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
353 info != frame_info_list_.end(); ++info) {
354 }
355 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
356 info != frame_info_list_.end(); ++info) {
357 const aom_codec_pts_t pts = info->pts;
358 if (pts >= kStepDownFrame && pts < kStepUpFrame) {
359 ASSERT_EQ(282U, info->w) << "Frame " << pts << " had unexpected width";
360 ASSERT_EQ(173U, info->h) << "Frame " << pts << " had unexpected height";
361 } else {
362 EXPECT_EQ(352U, info->w) << "Frame " << pts << " had unexpected width";
363 EXPECT_EQ(288U, info->h) << "Frame " << pts << " had unexpected height";
364 }
365 }
366 }
367
TEST_P(ResizeInternalTestLarge,TestInternalResizeChangeConfig)368 TEST_P(ResizeInternalTestLarge, TestInternalResizeChangeConfig) {
369 ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
370 30, 1, 0, 10);
371 cfg_.g_w = 352;
372 cfg_.g_h = 288;
373 change_config_ = true;
374 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
375 }
376
377 AV1_INSTANTIATE_TEST_SUITE(ResizeInternalTestLarge,
378 ::testing::Values(::libaom_test::kOnePassGood));
379 #endif
380
381 class ResizeRealtimeTest
382 : public ::libaom_test::CodecTestWith2Params<libaom_test::TestMode, int>,
383 public ::libaom_test::EncoderTest {
384 protected:
ResizeRealtimeTest()385 ResizeRealtimeTest()
386 : EncoderTest(GET_PARAM(0)), set_scale_mode_(false),
387 set_scale_mode2_(false) {}
~ResizeRealtimeTest()388 virtual ~ResizeRealtimeTest() {}
389
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)390 virtual void PreEncodeFrameHook(libaom_test::VideoSource *video,
391 libaom_test::Encoder *encoder) {
392 if (video->frame() == 0) {
393 encoder->Control(AV1E_SET_AQ_MODE, 3);
394 encoder->Control(AV1E_SET_ALLOW_WARPED_MOTION, 0);
395 encoder->Control(AV1E_SET_ENABLE_GLOBAL_MOTION, 0);
396 encoder->Control(AV1E_SET_ENABLE_OBMC, 0);
397 encoder->Control(AOME_SET_CPUUSED, set_cpu_used_);
398 encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
399 }
400 if (set_scale_mode_) {
401 struct aom_scaling_mode mode;
402 if (video->frame() <= 20)
403 mode = { AOME_ONETWO, AOME_ONETWO };
404 else if (video->frame() <= 40)
405 mode = { AOME_ONEFOUR, AOME_ONEFOUR };
406 else if (video->frame() > 40)
407 mode = { AOME_NORMAL, AOME_NORMAL };
408 encoder->Control(AOME_SET_SCALEMODE, &mode);
409 } else if (set_scale_mode2_) {
410 struct aom_scaling_mode mode;
411 if (video->frame() <= 20)
412 mode = { AOME_ONEFOUR, AOME_ONEFOUR };
413 else if (video->frame() <= 40)
414 mode = { AOME_ONETWO, AOME_ONETWO };
415 else if (video->frame() > 40)
416 mode = { AOME_THREEFOUR, AOME_THREEFOUR };
417 encoder->Control(AOME_SET_SCALEMODE, &mode);
418 }
419
420 if (change_bitrate_ && video->frame() == frame_change_bitrate_) {
421 change_bitrate_ = false;
422 cfg_.rc_target_bitrate = 500;
423 encoder->Config(&cfg_);
424 }
425 }
426
SetUp()427 virtual void SetUp() {
428 InitializeConfig(GET_PARAM(1));
429 set_cpu_used_ = GET_PARAM(2);
430 }
431
DecompressedFrameHook(const aom_image_t & img,aom_codec_pts_t pts)432 virtual void DecompressedFrameHook(const aom_image_t &img,
433 aom_codec_pts_t pts) {
434 frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
435 }
436
MismatchHook(const aom_image_t * img1,const aom_image_t * img2)437 virtual void MismatchHook(const aom_image_t *img1, const aom_image_t *img2) {
438 double mismatch_psnr = compute_psnr(img1, img2);
439 mismatch_psnr_ += mismatch_psnr;
440 ++mismatch_nframes_;
441 }
442
GetMismatchFrames()443 unsigned int GetMismatchFrames() { return mismatch_nframes_; }
444
DefaultConfig()445 void DefaultConfig() {
446 cfg_.rc_buf_initial_sz = 500;
447 cfg_.rc_buf_optimal_sz = 600;
448 cfg_.rc_buf_sz = 1000;
449 cfg_.rc_min_quantizer = 2;
450 cfg_.rc_max_quantizer = 56;
451 cfg_.rc_undershoot_pct = 50;
452 cfg_.rc_overshoot_pct = 50;
453 cfg_.rc_end_usage = AOM_CBR;
454 cfg_.kf_mode = AOM_KF_AUTO;
455 cfg_.g_lag_in_frames = 0;
456 cfg_.kf_min_dist = cfg_.kf_max_dist = 3000;
457 // Enable dropped frames.
458 cfg_.rc_dropframe_thresh = 1;
459 // Disable error_resilience mode.
460 cfg_.g_error_resilient = 0;
461 // Run at low bitrate.
462 cfg_.rc_target_bitrate = 200;
463 // We use max(kInitialWidth, kInitialHeight) because during the test
464 // the width and height of the frame are swapped
465 cfg_.g_forced_max_frame_width = cfg_.g_forced_max_frame_height =
466 AOMMAX(kInitialWidth, kInitialHeight);
467 if (set_scale_mode_ || set_scale_mode2_) {
468 cfg_.rc_dropframe_thresh = 0;
469 cfg_.g_forced_max_frame_width = 1280;
470 cfg_.g_forced_max_frame_height = 1280;
471 }
472 }
473
474 std::vector<FrameInfo> frame_info_list_;
475 int set_cpu_used_;
476 bool change_bitrate_;
477 unsigned int frame_change_bitrate_;
478 double mismatch_psnr_;
479 int mismatch_nframes_;
480 bool set_scale_mode_;
481 bool set_scale_mode2_;
482 };
483
484 // Check the AOME_SET_SCALEMODE control by downsizing to
485 // 1/2, then 1/4, and then back up to originsal.
TEST_P(ResizeRealtimeTest,TestInternalResizeSetScaleMode1)486 TEST_P(ResizeRealtimeTest, TestInternalResizeSetScaleMode1) {
487 ::libaom_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
488 cfg_.g_w = 1280;
489 cfg_.g_h = 720;
490 set_scale_mode_ = true;
491 set_scale_mode2_ = false;
492 DefaultConfig();
493 change_bitrate_ = false;
494 mismatch_nframes_ = 0;
495 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
496 // Check we decoded the same number of frames as we attempted to encode
497 ASSERT_EQ(frame_info_list_.size(), video.limit());
498 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
499 info != frame_info_list_.end(); ++info) {
500 const auto frame = static_cast<unsigned>(info->pts);
501 unsigned int expected_w = 1280 >> 1;
502 unsigned int expected_h = 720 >> 1;
503 if (frame > 40) {
504 expected_w = 1280;
505 expected_h = 720;
506 } else if (frame > 20 && frame <= 40) {
507 expected_w = 1280 >> 2;
508 expected_h = 720 >> 2;
509 }
510 EXPECT_EQ(expected_w, info->w)
511 << "Frame " << frame << " had unexpected width";
512 EXPECT_EQ(expected_h, info->h)
513 << "Frame " << frame << " had unexpected height";
514 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
515 }
516 }
517
518 // Check the AOME_SET_SCALEMODE control by downsizing to
519 // 1/2, then 1/4, and then back up to originsal.
TEST_P(ResizeRealtimeTest,TestInternalResizeSetScaleMode1QVGA)520 TEST_P(ResizeRealtimeTest, TestInternalResizeSetScaleMode1QVGA) {
521 ::libaom_test::I420VideoSource video("desktop1.320_180.yuv", 320, 180, 30, 1,
522 0, 80);
523 cfg_.g_w = 320;
524 cfg_.g_h = 180;
525 set_scale_mode_ = true;
526 set_scale_mode2_ = false;
527 DefaultConfig();
528 change_bitrate_ = false;
529 mismatch_nframes_ = 0;
530 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
531 // Check we decoded the same number of frames as we attempted to encode
532 ASSERT_EQ(frame_info_list_.size(), video.limit());
533 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
534 info != frame_info_list_.end(); ++info) {
535 const auto frame = static_cast<unsigned>(info->pts);
536 unsigned int expected_w = 320 >> 1;
537 unsigned int expected_h = 180 >> 1;
538 if (frame > 40) {
539 expected_w = 320;
540 expected_h = 180;
541 } else if (frame > 20 && frame <= 40) {
542 expected_w = 320 >> 2;
543 expected_h = 180 >> 2;
544 }
545 EXPECT_EQ(expected_w, info->w)
546 << "Frame " << frame << " had unexpected width";
547 EXPECT_EQ(expected_h, info->h)
548 << "Frame " << frame << " had unexpected height";
549 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
550 }
551 }
552
553 // Check the AOME_SET_SCALEMODE control by downsizing to
554 // 1/4, then 1/2, and then up to 3/4.
TEST_P(ResizeRealtimeTest,TestInternalResizeSetScaleMode2)555 TEST_P(ResizeRealtimeTest, TestInternalResizeSetScaleMode2) {
556 ::libaom_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
557 cfg_.g_w = 1280;
558 cfg_.g_h = 720;
559 set_scale_mode_ = false;
560 set_scale_mode2_ = true;
561 DefaultConfig();
562 change_bitrate_ = false;
563 mismatch_nframes_ = 0;
564 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
565 // Check we decoded the same number of frames as we attempted to encode
566 ASSERT_EQ(frame_info_list_.size(), video.limit());
567 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
568 info != frame_info_list_.end(); ++info) {
569 const auto frame = static_cast<unsigned>(info->pts);
570 unsigned int expected_w = 1280 >> 2;
571 unsigned int expected_h = 720 >> 2;
572 if (frame > 40) {
573 expected_w = (3 * 1280) >> 2;
574 expected_h = (3 * 720) >> 2;
575 } else if (frame > 20 && frame <= 40) {
576 expected_w = 1280 >> 1;
577 expected_h = 720 >> 1;
578 }
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
TEST_P(ResizeRealtimeTest,TestExternalResizeWorks)587 TEST_P(ResizeRealtimeTest, TestExternalResizeWorks) {
588 ResizingVideoSource video;
589 video.flag_codec_ = 1;
590 change_bitrate_ = false;
591 set_scale_mode_ = false;
592 set_scale_mode2_ = false;
593 mismatch_psnr_ = 0.0;
594 mismatch_nframes_ = 0;
595 DefaultConfig();
596 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
597
598 // Check we decoded the same number of frames as we attempted to encode
599 ASSERT_EQ(frame_info_list_.size(), video.limit());
600
601 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
602 info != frame_info_list_.end(); ++info) {
603 const unsigned int frame = static_cast<unsigned>(info->pts);
604 unsigned int expected_w;
605 unsigned int expected_h;
606 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w,
607 &expected_h, 1);
608 EXPECT_EQ(expected_w, info->w)
609 << "Frame " << frame << " had unexpected width";
610 EXPECT_EQ(expected_h, info->h)
611 << "Frame " << frame << " had unexpected height";
612 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
613 }
614 }
615
616 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
617 // Run at low bitrate, with resize_allowed = 1, and verify that we get
618 // one resize down event.
TEST_P(ResizeRealtimeTest,TestInternalResizeDown)619 TEST_P(ResizeRealtimeTest, TestInternalResizeDown) {
620 ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
621 0, 400);
622 cfg_.g_w = 640;
623 cfg_.g_h = 480;
624 change_bitrate_ = false;
625 set_scale_mode_ = false;
626 set_scale_mode2_ = false;
627 mismatch_psnr_ = 0.0;
628 mismatch_nframes_ = 0;
629 DefaultConfig();
630 // Disable dropped frames.
631 cfg_.rc_dropframe_thresh = 0;
632 // Starting bitrate low.
633 cfg_.rc_target_bitrate = 150;
634 cfg_.rc_resize_mode = RESIZE_DYNAMIC;
635 cfg_.g_forced_max_frame_width = 1280;
636 cfg_.g_forced_max_frame_height = 1280;
637 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
638
639 unsigned int last_w = cfg_.g_w;
640 unsigned int last_h = cfg_.g_h;
641 int resize_down_count = 0;
642 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
643 info != frame_info_list_.end(); ++info) {
644 if (info->w != last_w || info->h != last_h) {
645 // Verify that resize down occurs.
646 if (info->w < last_w && info->h < last_h) {
647 resize_down_count++;
648 }
649 last_w = info->w;
650 last_h = info->h;
651 }
652 }
653
654 #if CONFIG_AV1_DECODER
655 // Verify that we get at lease 1 resize down event in this test.
656 ASSERT_GE(resize_down_count, 1) << "Resizing should occur.";
657 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
658 #else
659 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
660 #endif
661 }
662
663 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
664 // Start at low target bitrate, raise the bitrate in the middle of the clip
665 // (at frame# = frame_change_bitrate_), scaling-up should occur after bitrate
666 // is increased.
TEST_P(ResizeRealtimeTest,TestInternalResizeDownUpChangeBitRate)667 TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRate) {
668 ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
669 0, 400);
670 cfg_.g_w = 640;
671 cfg_.g_h = 480;
672 change_bitrate_ = true;
673 frame_change_bitrate_ = 120;
674 set_scale_mode_ = false;
675 set_scale_mode2_ = false;
676 mismatch_psnr_ = 0.0;
677 mismatch_nframes_ = 0;
678 DefaultConfig();
679 // Disable dropped frames.
680 cfg_.rc_dropframe_thresh = 0;
681 // Starting bitrate low.
682 cfg_.rc_target_bitrate = 150;
683 cfg_.rc_resize_mode = RESIZE_DYNAMIC;
684 cfg_.g_forced_max_frame_width = 1280;
685 cfg_.g_forced_max_frame_height = 1280;
686 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
687
688 unsigned int last_w = cfg_.g_w;
689 unsigned int last_h = cfg_.g_h;
690 unsigned int frame_number = 0;
691 int resize_down_count = 0;
692 int resize_up_count = 0;
693 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
694 info != frame_info_list_.end(); ++info) {
695 if (info->w != last_w || info->h != last_h) {
696 if (frame_number < frame_change_bitrate_) {
697 // Verify that resize down occurs, before bitrate is increased.
698 ASSERT_LT(info->w, last_w);
699 ASSERT_LT(info->h, last_h);
700 resize_down_count++;
701 } else {
702 // Verify that resize up occurs, after bitrate is increased.
703 ASSERT_GT(info->w, last_w);
704 ASSERT_GT(info->h, last_h);
705 resize_up_count++;
706 }
707 last_w = info->w;
708 last_h = info->h;
709 }
710 frame_number++;
711 }
712
713 #if CONFIG_AV1_DECODER
714 // Verify that we get at least 2 resize events in this test.
715 ASSERT_GE(resize_up_count, 1) << "Resizing up should occur at lease once.";
716 ASSERT_GE(resize_down_count, 1)
717 << "Resizing down should occur at lease once.";
718 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
719 #else
720 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
721 #endif
722 }
723
724 class ResizeCspTest : public ResizeTest {
725 protected:
726 #if WRITE_COMPRESSED_STREAM
ResizeCspTest()727 ResizeCspTest()
728 : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL), out_frames_(0) {}
729 #else
730 ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
731 #endif
732
~ResizeCspTest()733 virtual ~ResizeCspTest() {}
734
BeginPassHook(unsigned int)735 virtual void BeginPassHook(unsigned int /*pass*/) {
736 #if WRITE_COMPRESSED_STREAM
737 outfile_ = fopen("av11-2-05-cspchape.ivf", "wb");
738 #endif
739 }
740
EndPassHook()741 virtual void EndPassHook() {
742 #if WRITE_COMPRESSED_STREAM
743 if (outfile_) {
744 if (!fseek(outfile_, 0, SEEK_SET))
745 write_ivf_file_header(&cfg_, out_frames_, outfile_);
746 fclose(outfile_);
747 outfile_ = NULL;
748 }
749 #endif
750 }
751
PSNRPktHook(const aom_codec_cx_pkt_t * pkt)752 virtual void PSNRPktHook(const aom_codec_cx_pkt_t *pkt) {
753 if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
754 EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 2.0);
755 }
756
757 #if WRITE_COMPRESSED_STREAM
FramePktHook(const aom_codec_cx_pkt_t * pkt)758 virtual void FramePktHook(const aom_codec_cx_pkt_t *pkt) {
759 ++out_frames_;
760
761 // Write initial file header if first frame.
762 if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
763
764 // Write frame header and data.
765 write_ivf_frame_header(pkt, outfile_);
766 (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
767 }
768 #endif
769
770 double frame0_psnr_;
771 #if WRITE_COMPRESSED_STREAM
772 FILE *outfile_;
773 unsigned int out_frames_;
774 #endif
775 };
776
777 class ResizingCspVideoSource : public ::libaom_test::DummyVideoSource {
778 public:
ResizingCspVideoSource(aom_img_fmt_t image_format)779 explicit ResizingCspVideoSource(aom_img_fmt_t image_format) {
780 SetSize(kInitialWidth, kInitialHeight);
781 SetImageFormat(image_format);
782 limit_ = 30;
783 }
784
~ResizingCspVideoSource()785 virtual ~ResizingCspVideoSource() {}
786 };
787
788 #if (defined(DISABLE_TRELLISQ_SEARCH) && DISABLE_TRELLISQ_SEARCH)
TEST_P(ResizeCspTest,DISABLED_TestResizeCspWorks)789 TEST_P(ResizeCspTest, DISABLED_TestResizeCspWorks) {
790 #else
791 TEST_P(ResizeCspTest, TestResizeCspWorks) {
792 #endif
793 const aom_img_fmt_t image_formats[] = { AOM_IMG_FMT_I420, AOM_IMG_FMT_I444 };
794 for (const aom_img_fmt_t &img_format : image_formats) {
795 ResizingCspVideoSource video(img_format);
796 init_flags_ = AOM_CODEC_USE_PSNR;
797 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
798 cfg_.g_lag_in_frames = 0;
799 cfg_.g_profile = (img_format == AOM_IMG_FMT_I420) ? 0 : 1;
800 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
801
802 // Check we decoded the same number of frames as we attempted to encode
803 ASSERT_EQ(frame_info_list_.size(), video.limit());
804 frame_info_list_.clear();
805 }
806 }
807
808 #if !CONFIG_REALTIME_ONLY
809 // This class is used to check if there are any fatal
810 // failures while encoding with resize-mode > 0
811 class ResizeModeTestLarge
812 : public ::libaom_test::CodecTestWith5Params<libaom_test::TestMode, int,
813 int, int, int>,
814 public ::libaom_test::EncoderTest {
815 protected:
816 ResizeModeTestLarge()
817 : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)),
818 resize_mode_(GET_PARAM(2)), resize_denominator_(GET_PARAM(3)),
819 resize_kf_denominator_(GET_PARAM(4)), cpu_used_(GET_PARAM(5)) {}
820 virtual ~ResizeModeTestLarge() {}
821
822 virtual void SetUp() {
823 InitializeConfig(encoding_mode_);
824 const aom_rational timebase = { 1, 30 };
825 cfg_.g_timebase = timebase;
826 cfg_.rc_end_usage = AOM_VBR;
827 cfg_.g_threads = 1;
828 cfg_.g_lag_in_frames = 35;
829 cfg_.rc_target_bitrate = 1000;
830 cfg_.rc_resize_mode = resize_mode_;
831 cfg_.rc_resize_denominator = resize_denominator_;
832 cfg_.rc_resize_kf_denominator = resize_kf_denominator_;
833 init_flags_ = AOM_CODEC_USE_PSNR;
834 }
835
836 virtual void PreEncodeFrameHook(::libaom_test::VideoSource *video,
837 ::libaom_test::Encoder *encoder) {
838 if (video->frame() == 0) {
839 encoder->Control(AOME_SET_CPUUSED, cpu_used_);
840 encoder->Control(AOME_SET_ENABLEAUTOALTREF, 1);
841 }
842 }
843
844 ::libaom_test::TestMode encoding_mode_;
845 int resize_mode_;
846 int resize_denominator_;
847 int resize_kf_denominator_;
848 int cpu_used_;
849 };
850
851 TEST_P(ResizeModeTestLarge, ResizeModeTest) {
852 ::libaom_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 30);
853 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
854 }
855
856 // TODO(anyone): Enable below test once resize issues are fixed
857 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ResizeModeTestLarge);
858 // AV1_INSTANTIATE_TEST_SUITE(
859 // ResizeModeTestLarge,
860 // ::testing::Values(::libaom_test::kOnePassGood,
861 // ::libaom_test::kTwoPassGood),
862 // ::testing::Values(1, 2), ::testing::Values(8, 12, 16),
863 // ::testing::Values(8, 12, 16), ::testing::Range(2, 7));
864 #endif // !CONFIG_REALTIME_ONLY
865
866 AV1_INSTANTIATE_TEST_SUITE(ResizeTest,
867 ::testing::Values(::libaom_test::kRealTime));
868 AV1_INSTANTIATE_TEST_SUITE(ResizeRealtimeTest,
869 ::testing::Values(::libaom_test::kRealTime),
870 ::testing::Range(6, 10));
871 AV1_INSTANTIATE_TEST_SUITE(ResizeCspTest,
872 ::testing::Values(::libaom_test::kRealTime));
873
874 } // namespace
875