2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
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.
14 #include "third_party/googletest/src/googletest/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"
21 // Enable(1) or Disable(0) writing of the compressed bitstream.
22 #define WRITE_COMPRESSED_STREAM 0
26 #if WRITE_COMPRESSED_STREAM
27 static void mem_put_le16(char *const mem
, unsigned int val
) {
32 static void mem_put_le32(char *const mem
, unsigned int val
) {
39 static void write_ivf_file_header(const aom_codec_enc_cfg_t
*const cfg
,
40 int frame_cnt
, FILE *const outfile
) {
47 mem_put_le16(header
+ 4, 0); /* version */
48 mem_put_le16(header
+ 6, 32); /* headersize */
49 mem_put_le32(header
+ 8, 0x30395056); /* fourcc (av1) */
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 */
57 (void)fwrite(header
, 1, 32, outfile
);
60 static void write_ivf_frame_size(FILE *const outfile
, const size_t size
) {
62 mem_put_le32(header
, static_cast<unsigned int>(size
));
63 (void)fwrite(header
, 1, 4, outfile
);
66 static void write_ivf_frame_header(const aom_codec_cx_pkt_t
*const pkt
,
67 FILE *const outfile
) {
71 if (pkt
->kind
!= AOM_CODEC_CX_FRAME_PKT
) return;
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);
78 (void)fwrite(header
, 1, 12, outfile
);
80 #endif // WRITE_COMPRESSED_STREAM
82 const unsigned int kInitialWidth
= 320;
83 const unsigned int kInitialHeight
= 240;
86 FrameInfo(aom_codec_pts_t _pts
, unsigned int _w
, unsigned int _h
)
87 : pts(_pts
), w(_w
), h(_h
) {}
94 void ScaleForFrameNumber(unsigned int frame
, unsigned int initial_w
,
95 unsigned int initial_h
, unsigned int *w
,
96 unsigned int *h
, int flag_codec
) {
103 *w
= initial_w
* 3 / 4;
104 *h
= initial_h
* 3 / 4;
118 *w
= initial_w
* 3 / 4;
119 *h
= initial_h
* 3 / 4;
133 *w
= initial_w
* 3 / 4;
134 *h
= initial_h
* 3 / 4;
143 *w
= initial_w
* 3 / 4;
144 *h
= initial_h
* 3 / 4;
153 *w
= initial_w
* 3 / 4;
154 *h
= initial_h
* 3 / 4;
163 *w
= initial_w
* 3 / 4;
164 *h
= initial_h
* 3 / 4;
173 *w
= initial_w
* 3 / 4;
174 *h
= initial_h
* 3 / 4;
183 *w
= initial_w
* 3 / 4;
184 *h
= initial_h
* 3 / 4;
193 *w
= initial_w
* 3 / 4;
194 *h
= initial_h
* 3 / 4;
203 *w
= initial_w
* 3 / 4;
204 *h
= initial_h
* 3 / 4;
213 *w
= initial_w
* 3 / 4;
214 *h
= initial_h
* 3 / 4;
233 if (flag_codec
== 1) {
234 // Cases that only works for AV1.
235 // For AV1: Swap width and height of original.
246 class ResizingVideoSource
: public ::libaom_test::DummyVideoSource
{
248 ResizingVideoSource() {
249 SetSize(kInitialWidth
, kInitialHeight
);
253 virtual ~ResizingVideoSource() {}
256 virtual void Next() {
260 ScaleForFrameNumber(frame_
, kInitialWidth
, kInitialHeight
, &width
, &height
,
262 SetSize(width
, height
);
268 : public ::libaom_test::CodecTestWithParam
<libaom_test::TestMode
>,
269 public ::libaom_test::EncoderTest
{
271 ResizeTest() : EncoderTest(GET_PARAM(0)) {}
273 virtual ~ResizeTest() {}
275 virtual void SetUp() {
277 SetMode(GET_PARAM(1));
280 virtual void DecompressedFrameHook(const aom_image_t
&img
,
281 aom_codec_pts_t pts
) {
282 frame_info_list_
.push_back(FrameInfo(pts
, img
.d_w
, img
.d_h
));
285 std::vector
<FrameInfo
> frame_info_list_
;
288 TEST_P(ResizeTest
, TestExternalResizeWorks
) {
289 ResizingVideoSource video
;
290 video
.flag_codec_
= 0;
291 cfg_
.g_lag_in_frames
= 0;
292 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
294 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
295 info
!= frame_info_list_
.end(); ++info
) {
296 const unsigned int frame
= static_cast<unsigned>(info
->pts
);
297 unsigned int expected_w
;
298 unsigned int expected_h
;
299 ScaleForFrameNumber(frame
, kInitialWidth
, kInitialHeight
, &expected_w
,
301 EXPECT_EQ(expected_w
, info
->w
)
302 << "Frame " << frame
<< " had unexpected width";
303 EXPECT_EQ(expected_h
, info
->h
)
304 << "Frame " << frame
<< " had unexpected height";
308 const unsigned int kStepDownFrame
= 3;
309 const unsigned int kStepUpFrame
= 6;
311 class ResizeInternalTestLarge
: public ResizeTest
{
313 #if WRITE_COMPRESSED_STREAM
314 ResizeInternalTestLarge()
315 : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL
), out_frames_(0) {}
317 ResizeInternalTestLarge() : ResizeTest(), frame0_psnr_(0.0) {}
320 virtual ~ResizeInternalTestLarge() {}
322 virtual void BeginPassHook(unsigned int /*pass*/) {
323 #if WRITE_COMPRESSED_STREAM
324 outfile_
= fopen("av10-2-05-resize.ivf", "wb");
328 virtual void EndPassHook() {
329 #if WRITE_COMPRESSED_STREAM
331 if (!fseek(outfile_
, 0, SEEK_SET
))
332 write_ivf_file_header(&cfg_
, out_frames_
, outfile_
);
339 virtual void PreEncodeFrameHook(libaom_test::VideoSource
*video
,
340 libaom_test::Encoder
*encoder
) {
341 if (change_config_
) {
343 if (video
->frame() == 0) {
344 struct aom_scaling_mode mode
= { AOME_ONETWO
, AOME_ONETWO
};
345 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
347 if (video
->frame() == 1) {
348 struct aom_scaling_mode mode
= { AOME_NORMAL
, AOME_NORMAL
};
349 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
350 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= new_q
;
351 encoder
->Config(&cfg_
);
354 if (video
->frame() >= kStepDownFrame
&& video
->frame() < kStepUpFrame
) {
355 struct aom_scaling_mode mode
= { AOME_FOURFIVE
, AOME_THREEFIVE
};
356 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
358 if (video
->frame() >= kStepUpFrame
) {
359 struct aom_scaling_mode mode
= { AOME_NORMAL
, AOME_NORMAL
};
360 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
365 virtual void PSNRPktHook(const aom_codec_cx_pkt_t
*pkt
) {
366 if (frame0_psnr_
== 0.) frame0_psnr_
= pkt
->data
.psnr
.psnr
[0];
367 EXPECT_NEAR(pkt
->data
.psnr
.psnr
[0], frame0_psnr_
, 2.5);
370 #if WRITE_COMPRESSED_STREAM
371 virtual void FramePktHook(const aom_codec_cx_pkt_t
*pkt
) {
374 // Write initial file header if first frame.
375 if (pkt
->data
.frame
.pts
== 0) write_ivf_file_header(&cfg_
, 0, outfile_
);
377 // Write frame header and data.
378 write_ivf_frame_header(pkt
, outfile_
);
379 (void)fwrite(pkt
->data
.frame
.buf
, 1, pkt
->data
.frame
.sz
, outfile_
);
385 #if WRITE_COMPRESSED_STREAM
387 unsigned int out_frames_
;
391 TEST_P(ResizeInternalTestLarge
, TestInternalResizeWorks
) {
392 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
394 init_flags_
= AOM_CODEC_USE_PSNR
;
395 change_config_
= false;
397 // q picked such that initial keyframe on this clip is ~30dB PSNR
398 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= 48;
400 // If the number of frames being encoded is smaller than g_lag_in_frames
401 // the encoded frame is unavailable using the current API. Comparing
402 // frames to detect mismatch would then not be possible. Set
403 // g_lag_in_frames = 0 to get around this.
404 cfg_
.g_lag_in_frames
= 0;
405 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
407 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
408 info
!= frame_info_list_
.end(); ++info
) {
410 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
411 info
!= frame_info_list_
.end(); ++info
) {
412 const aom_codec_pts_t pts
= info
->pts
;
413 if (pts
>= kStepDownFrame
&& pts
< kStepUpFrame
) {
414 ASSERT_EQ(282U, info
->w
) << "Frame " << pts
<< " had unexpected width";
415 ASSERT_EQ(173U, info
->h
) << "Frame " << pts
<< " had unexpected height";
417 EXPECT_EQ(352U, info
->w
) << "Frame " << pts
<< " had unexpected width";
418 EXPECT_EQ(288U, info
->h
) << "Frame " << pts
<< " had unexpected height";
423 TEST_P(ResizeInternalTestLarge
, TestInternalResizeChangeConfig
) {
424 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
428 change_config_
= true;
429 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
432 class ResizeRealtimeTest
433 : public ::libaom_test::CodecTestWith2Params
<libaom_test::TestMode
, int>,
434 public ::libaom_test::EncoderTest
{
436 ResizeRealtimeTest() : EncoderTest(GET_PARAM(0)) {}
437 virtual ~ResizeRealtimeTest() {}
439 virtual void PreEncodeFrameHook(libaom_test::VideoSource
*video
,
440 libaom_test::Encoder
*encoder
) {
441 if (video
->frame() == 0) {
442 encoder
->Control(AV1E_SET_AQ_MODE
, 3);
443 encoder
->Control(AOME_SET_CPUUSED
, set_cpu_used_
);
446 if (change_bitrate_
&& video
->frame() == 120) {
447 change_bitrate_
= false;
448 cfg_
.rc_target_bitrate
= 500;
449 encoder
->Config(&cfg_
);
453 virtual void SetUp() {
455 SetMode(GET_PARAM(1));
456 set_cpu_used_
= GET_PARAM(2);
459 virtual void DecompressedFrameHook(const aom_image_t
&img
,
460 aom_codec_pts_t pts
) {
461 frame_info_list_
.push_back(FrameInfo(pts
, img
.d_w
, img
.d_h
));
464 virtual void MismatchHook(const aom_image_t
*img1
, const aom_image_t
*img2
) {
465 double mismatch_psnr
= compute_psnr(img1
, img2
);
466 mismatch_psnr_
+= mismatch_psnr
;
470 unsigned int GetMismatchFrames() { return mismatch_nframes_
; }
472 void DefaultConfig() {
473 cfg_
.rc_buf_initial_sz
= 500;
474 cfg_
.rc_buf_optimal_sz
= 600;
475 cfg_
.rc_buf_sz
= 1000;
476 cfg_
.rc_min_quantizer
= 2;
477 cfg_
.rc_max_quantizer
= 56;
478 cfg_
.rc_undershoot_pct
= 50;
479 cfg_
.rc_overshoot_pct
= 50;
480 cfg_
.rc_end_usage
= AOM_CBR
;
481 cfg_
.kf_mode
= AOM_KF_AUTO
;
482 cfg_
.g_lag_in_frames
= 0;
483 cfg_
.kf_min_dist
= cfg_
.kf_max_dist
= 3000;
484 // Enable dropped frames.
485 cfg_
.rc_dropframe_thresh
= 1;
486 // Enable error_resilience mode.
487 cfg_
.g_error_resilient
= 1;
488 // Run at low bitrate.
489 cfg_
.rc_target_bitrate
= 200;
492 std::vector
<FrameInfo
> frame_info_list_
;
494 bool change_bitrate_
;
495 double mismatch_psnr_
;
496 int mismatch_nframes_
;
499 TEST_P(ResizeRealtimeTest
, TestExternalResizeWorks
) {
500 ResizingVideoSource video
;
501 video
.flag_codec_
= 1;
503 change_bitrate_
= false;
504 mismatch_psnr_
= 0.0;
505 mismatch_nframes_
= 0;
506 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
508 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
509 info
!= frame_info_list_
.end(); ++info
) {
510 const unsigned int frame
= static_cast<unsigned>(info
->pts
);
511 unsigned int expected_w
;
512 unsigned int expected_h
;
513 ScaleForFrameNumber(frame
, kInitialWidth
, kInitialHeight
, &expected_w
,
515 EXPECT_EQ(expected_w
, info
->w
)
516 << "Frame " << frame
<< " had unexpected width";
517 EXPECT_EQ(expected_h
, info
->h
)
518 << "Frame " << frame
<< " had unexpected height";
519 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
523 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
524 // Run at low bitrate, with resize_allowed = 1, and verify that we get
525 // one resize down event.
526 TEST_P(ResizeRealtimeTest
, DISABLED_TestInternalResizeDown
) {
527 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
532 change_bitrate_
= false;
533 mismatch_psnr_
= 0.0;
534 mismatch_nframes_
= 0;
535 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
537 unsigned int last_w
= cfg_
.g_w
;
538 unsigned int last_h
= cfg_
.g_h
;
539 int resize_count
= 0;
540 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
541 info
!= frame_info_list_
.end(); ++info
) {
542 if (info
->w
!= last_w
|| info
->h
!= last_h
) {
543 // Verify that resize down occurs.
544 ASSERT_LT(info
->w
, last_w
);
545 ASSERT_LT(info
->h
, last_h
);
552 #if CONFIG_AV1_DECODER
553 // Verify that we get 1 resize down event in this test.
554 ASSERT_EQ(1, resize_count
) << "Resizing should occur.";
555 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
557 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
561 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
562 // Start at low target bitrate, raise the bitrate in the middle of the clip,
563 // scaling-up should occur after bitrate changed.
564 TEST_P(ResizeRealtimeTest
, DISABLED_TestInternalResizeDownUpChangeBitRate
) {
565 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
570 change_bitrate_
= true;
571 mismatch_psnr_
= 0.0;
572 mismatch_nframes_
= 0;
573 // Disable dropped frames.
574 cfg_
.rc_dropframe_thresh
= 0;
575 // Starting bitrate low.
576 cfg_
.rc_target_bitrate
= 80;
577 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
579 unsigned int last_w
= cfg_
.g_w
;
580 unsigned int last_h
= cfg_
.g_h
;
581 int resize_count
= 0;
582 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
583 info
!= frame_info_list_
.end(); ++info
) {
584 if (info
->w
!= last_w
|| info
->h
!= last_h
) {
586 if (resize_count
== 1) {
587 // Verify that resize down occurs.
588 ASSERT_LT(info
->w
, last_w
);
589 ASSERT_LT(info
->h
, last_h
);
590 } else if (resize_count
== 2) {
591 // Verify that resize up occurs.
592 ASSERT_GT(info
->w
, last_w
);
593 ASSERT_GT(info
->h
, last_h
);
600 #if CONFIG_AV1_DECODER
601 // Verify that we get 2 resize events in this test.
602 ASSERT_EQ(resize_count
, 2) << "Resizing should occur twice.";
603 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
605 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
609 aom_img_fmt_t
CspForFrameNumber(int frame
) {
610 if (frame
< 10) return AOM_IMG_FMT_I420
;
611 if (frame
< 20) return AOM_IMG_FMT_I444
;
612 return AOM_IMG_FMT_I420
;
615 class ResizeCspTest
: public ResizeTest
{
617 #if WRITE_COMPRESSED_STREAM
619 : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL
), out_frames_(0) {}
621 ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
624 virtual ~ResizeCspTest() {}
626 virtual void BeginPassHook(unsigned int /*pass*/) {
627 #if WRITE_COMPRESSED_STREAM
628 outfile_
= fopen("av11-2-05-cspchape.ivf", "wb");
632 virtual void EndPassHook() {
633 #if WRITE_COMPRESSED_STREAM
635 if (!fseek(outfile_
, 0, SEEK_SET
))
636 write_ivf_file_header(&cfg_
, out_frames_
, outfile_
);
643 virtual void PreEncodeFrameHook(libaom_test::VideoSource
*video
,
644 libaom_test::Encoder
*encoder
) {
645 if (CspForFrameNumber(video
->frame()) != AOM_IMG_FMT_I420
&&
646 cfg_
.g_profile
!= 1) {
648 encoder
->Config(&cfg_
);
650 if (CspForFrameNumber(video
->frame()) == AOM_IMG_FMT_I420
&&
651 cfg_
.g_profile
!= 0) {
653 encoder
->Config(&cfg_
);
657 virtual void PSNRPktHook(const aom_codec_cx_pkt_t
*pkt
) {
658 if (frame0_psnr_
== 0.) frame0_psnr_
= pkt
->data
.psnr
.psnr
[0];
659 EXPECT_NEAR(pkt
->data
.psnr
.psnr
[0], frame0_psnr_
, 2.0);
662 #if WRITE_COMPRESSED_STREAM
663 virtual void FramePktHook(const aom_codec_cx_pkt_t
*pkt
) {
666 // Write initial file header if first frame.
667 if (pkt
->data
.frame
.pts
== 0) write_ivf_file_header(&cfg_
, 0, outfile_
);
669 // Write frame header and data.
670 write_ivf_frame_header(pkt
, outfile_
);
671 (void)fwrite(pkt
->data
.frame
.buf
, 1, pkt
->data
.frame
.sz
, outfile_
);
676 #if WRITE_COMPRESSED_STREAM
678 unsigned int out_frames_
;
682 class ResizingCspVideoSource
: public ::libaom_test::DummyVideoSource
{
684 ResizingCspVideoSource() {
685 SetSize(kInitialWidth
, kInitialHeight
);
689 virtual ~ResizingCspVideoSource() {}
692 virtual void Next() {
694 SetImageFormat(CspForFrameNumber(frame_
));
699 #if (defined(DISABLE_TRELLISQ_SEARCH) && DISABLE_TRELLISQ_SEARCH)
700 TEST_P(ResizeCspTest
, DISABLED_TestResizeCspWorks
) {
702 TEST_P(ResizeCspTest
, TestResizeCspWorks
) {
704 ResizingCspVideoSource video
;
705 init_flags_
= AOM_CODEC_USE_PSNR
;
706 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= 48;
707 cfg_
.g_lag_in_frames
= 0;
708 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
711 AV1_INSTANTIATE_TEST_CASE(ResizeTest
,
712 ::testing::Values(::libaom_test::kRealTime
));
713 AV1_INSTANTIATE_TEST_CASE(ResizeInternalTestLarge
,
714 ::testing::Values(::libaom_test::kOnePassGood
));
715 AV1_INSTANTIATE_TEST_CASE(ResizeRealtimeTest
,
716 ::testing::Values(::libaom_test::kRealTime
),
717 ::testing::Range(5, 9));
718 AV1_INSTANTIATE_TEST_CASE(ResizeCspTest
,
719 ::testing::Values(::libaom_test::kRealTime
));