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 "aom_dsp/aom_dsp_common.h"
15 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
16 #include "test/codec_factory.h"
17 #include "test/encode_test_driver.h"
18 #include "test/i420_video_source.h"
19 #include "test/video_source.h"
20 #include "test/util.h"
22 // Enable(1) or Disable(0) writing of the compressed bitstream.
23 #define WRITE_COMPRESSED_STREAM 0
27 #if WRITE_COMPRESSED_STREAM
28 static void mem_put_le16(char *const mem
, unsigned int val
) {
33 static void mem_put_le32(char *const mem
, unsigned int val
) {
40 static void write_ivf_file_header(const aom_codec_enc_cfg_t
*const cfg
,
41 int frame_cnt
, FILE *const outfile
) {
48 mem_put_le16(header
+ 4, 0); /* version */
49 mem_put_le16(header
+ 6, 32); /* headersize */
50 mem_put_le32(header
+ 8, 0x30395056); /* fourcc (av1) */
51 mem_put_le16(header
+ 12, cfg
->g_w
); /* width */
52 mem_put_le16(header
+ 14, cfg
->g_h
); /* height */
53 mem_put_le32(header
+ 16, cfg
->g_timebase
.den
); /* rate */
54 mem_put_le32(header
+ 20, cfg
->g_timebase
.num
); /* scale */
55 mem_put_le32(header
+ 24, frame_cnt
); /* length */
56 mem_put_le32(header
+ 28, 0); /* unused */
58 (void)fwrite(header
, 1, 32, outfile
);
61 static void write_ivf_frame_size(FILE *const outfile
, const size_t size
) {
63 mem_put_le32(header
, static_cast<unsigned int>(size
));
64 (void)fwrite(header
, 1, 4, outfile
);
67 static void write_ivf_frame_header(const aom_codec_cx_pkt_t
*const pkt
,
68 FILE *const outfile
) {
72 if (pkt
->kind
!= AOM_CODEC_CX_FRAME_PKT
) return;
74 pts
= pkt
->data
.frame
.pts
;
75 mem_put_le32(header
, static_cast<unsigned int>(pkt
->data
.frame
.sz
));
76 mem_put_le32(header
+ 4, pts
& 0xFFFFFFFF);
77 mem_put_le32(header
+ 8, pts
>> 32);
79 (void)fwrite(header
, 1, 12, outfile
);
81 #endif // WRITE_COMPRESSED_STREAM
83 const unsigned int kInitialWidth
= 320;
84 const unsigned int kInitialHeight
= 240;
87 FrameInfo(aom_codec_pts_t _pts
, unsigned int _w
, unsigned int _h
)
88 : pts(_pts
), w(_w
), h(_h
) {}
95 void ScaleForFrameNumber(unsigned int frame
, unsigned int initial_w
,
96 unsigned int initial_h
, unsigned int *w
,
97 unsigned int *h
, int flag_codec
) {
104 *w
= initial_w
* 3 / 4;
105 *h
= initial_h
* 3 / 4;
119 *w
= initial_w
* 3 / 4;
120 *h
= initial_h
* 3 / 4;
134 *w
= initial_w
* 3 / 4;
135 *h
= initial_h
* 3 / 4;
144 *w
= initial_w
* 3 / 4;
145 *h
= initial_h
* 3 / 4;
159 if (flag_codec
== 1) {
160 // Cases that only works for AV1.
161 // For AV1: Swap width and height of original.
172 class ResizingVideoSource
: public ::libaom_test::DummyVideoSource
{
174 ResizingVideoSource() {
175 SetSize(kInitialWidth
, kInitialHeight
);
179 virtual ~ResizingVideoSource() {}
182 virtual void Next() {
186 ScaleForFrameNumber(frame_
, kInitialWidth
, kInitialHeight
, &width
, &height
,
188 SetSize(width
, height
);
194 : public ::libaom_test::CodecTestWithParam
<libaom_test::TestMode
>,
195 public ::libaom_test::EncoderTest
{
197 ResizeTest() : EncoderTest(GET_PARAM(0)) {}
199 virtual ~ResizeTest() {}
201 virtual void SetUp() {
203 SetMode(GET_PARAM(1));
206 virtual void DecompressedFrameHook(const aom_image_t
&img
,
207 aom_codec_pts_t pts
) {
208 frame_info_list_
.push_back(FrameInfo(pts
, img
.d_w
, img
.d_h
));
211 std::vector
<FrameInfo
> frame_info_list_
;
214 TEST_P(ResizeTest
, TestExternalResizeWorks
) {
215 ResizingVideoSource video
;
216 video
.flag_codec_
= 0;
217 cfg_
.g_lag_in_frames
= 0;
218 // We use max(kInitialWidth, kInitialHeight) because during the test
219 // the width and height of the frame are swapped
220 cfg_
.g_forced_max_frame_width
= cfg_
.g_forced_max_frame_height
=
221 AOMMAX(kInitialWidth
, kInitialHeight
);
222 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
224 // Check we decoded the same number of frames as we attempted to encode
225 ASSERT_EQ(frame_info_list_
.size(), video
.limit());
227 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
228 info
!= frame_info_list_
.end(); ++info
) {
229 const unsigned int frame
= static_cast<unsigned>(info
->pts
);
230 unsigned int expected_w
;
231 unsigned int expected_h
;
232 ScaleForFrameNumber(frame
, kInitialWidth
, kInitialHeight
, &expected_w
,
234 EXPECT_EQ(expected_w
, info
->w
)
235 << "Frame " << frame
<< " had unexpected width";
236 EXPECT_EQ(expected_h
, info
->h
)
237 << "Frame " << frame
<< " had unexpected height";
241 const unsigned int kStepDownFrame
= 3;
242 const unsigned int kStepUpFrame
= 6;
244 class ResizeInternalTestLarge
: public ResizeTest
{
246 #if WRITE_COMPRESSED_STREAM
247 ResizeInternalTestLarge()
248 : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL
), out_frames_(0) {}
250 ResizeInternalTestLarge() : ResizeTest(), frame0_psnr_(0.0) {}
253 virtual ~ResizeInternalTestLarge() {}
255 virtual void BeginPassHook(unsigned int /*pass*/) {
256 #if WRITE_COMPRESSED_STREAM
257 outfile_
= fopen("av10-2-05-resize.ivf", "wb");
261 virtual void EndPassHook() {
262 #if WRITE_COMPRESSED_STREAM
264 if (!fseek(outfile_
, 0, SEEK_SET
))
265 write_ivf_file_header(&cfg_
, out_frames_
, outfile_
);
272 virtual void PreEncodeFrameHook(libaom_test::VideoSource
*video
,
273 libaom_test::Encoder
*encoder
) {
274 if (change_config_
) {
276 if (video
->frame() == 0) {
277 struct aom_scaling_mode mode
= { AOME_ONETWO
, AOME_ONETWO
};
278 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
280 if (video
->frame() == 1) {
281 struct aom_scaling_mode mode
= { AOME_NORMAL
, AOME_NORMAL
};
282 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
283 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= new_q
;
284 encoder
->Config(&cfg_
);
287 if (video
->frame() >= kStepDownFrame
&& video
->frame() < kStepUpFrame
) {
288 struct aom_scaling_mode mode
= { AOME_FOURFIVE
, AOME_THREEFIVE
};
289 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
291 if (video
->frame() >= kStepUpFrame
) {
292 struct aom_scaling_mode mode
= { AOME_NORMAL
, AOME_NORMAL
};
293 encoder
->Control(AOME_SET_SCALEMODE
, &mode
);
298 virtual void PSNRPktHook(const aom_codec_cx_pkt_t
*pkt
) {
299 if (frame0_psnr_
== 0.) frame0_psnr_
= pkt
->data
.psnr
.psnr
[0];
300 EXPECT_NEAR(pkt
->data
.psnr
.psnr
[0], frame0_psnr_
, 2.5);
303 #if WRITE_COMPRESSED_STREAM
304 virtual void FramePktHook(const aom_codec_cx_pkt_t
*pkt
) {
307 // Write initial file header if first frame.
308 if (pkt
->data
.frame
.pts
== 0) write_ivf_file_header(&cfg_
, 0, outfile_
);
310 // Write frame header and data.
311 write_ivf_frame_header(pkt
, outfile_
);
312 (void)fwrite(pkt
->data
.frame
.buf
, 1, pkt
->data
.frame
.sz
, outfile_
);
318 #if WRITE_COMPRESSED_STREAM
320 unsigned int out_frames_
;
324 TEST_P(ResizeInternalTestLarge
, TestInternalResizeWorks
) {
325 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
327 init_flags_
= AOM_CODEC_USE_PSNR
;
328 change_config_
= false;
330 // q picked such that initial keyframe on this clip is ~30dB PSNR
331 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= 48;
333 // If the number of frames being encoded is smaller than g_lag_in_frames
334 // the encoded frame is unavailable using the current API. Comparing
335 // frames to detect mismatch would then not be possible. Set
336 // g_lag_in_frames = 0 to get around this.
337 cfg_
.g_lag_in_frames
= 0;
338 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
340 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
341 info
!= frame_info_list_
.end(); ++info
) {
343 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
344 info
!= frame_info_list_
.end(); ++info
) {
345 const aom_codec_pts_t pts
= info
->pts
;
346 if (pts
>= kStepDownFrame
&& pts
< kStepUpFrame
) {
347 ASSERT_EQ(282U, info
->w
) << "Frame " << pts
<< " had unexpected width";
348 ASSERT_EQ(173U, info
->h
) << "Frame " << pts
<< " had unexpected height";
350 EXPECT_EQ(352U, info
->w
) << "Frame " << pts
<< " had unexpected width";
351 EXPECT_EQ(288U, info
->h
) << "Frame " << pts
<< " had unexpected height";
356 TEST_P(ResizeInternalTestLarge
, TestInternalResizeChangeConfig
) {
357 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
361 change_config_
= true;
362 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
365 class ResizeRealtimeTest
366 : public ::libaom_test::CodecTestWith2Params
<libaom_test::TestMode
, int>,
367 public ::libaom_test::EncoderTest
{
369 ResizeRealtimeTest() : EncoderTest(GET_PARAM(0)) {}
370 virtual ~ResizeRealtimeTest() {}
372 virtual void PreEncodeFrameHook(libaom_test::VideoSource
*video
,
373 libaom_test::Encoder
*encoder
) {
374 if (video
->frame() == 0) {
375 encoder
->Control(AV1E_SET_AQ_MODE
, 3);
376 encoder
->Control(AOME_SET_CPUUSED
, set_cpu_used_
);
379 if (change_bitrate_
&& video
->frame() == 120) {
380 change_bitrate_
= false;
381 cfg_
.rc_target_bitrate
= 500;
382 encoder
->Config(&cfg_
);
386 virtual void SetUp() {
388 SetMode(GET_PARAM(1));
389 set_cpu_used_
= GET_PARAM(2);
392 virtual void DecompressedFrameHook(const aom_image_t
&img
,
393 aom_codec_pts_t pts
) {
394 frame_info_list_
.push_back(FrameInfo(pts
, img
.d_w
, img
.d_h
));
397 virtual void MismatchHook(const aom_image_t
*img1
, const aom_image_t
*img2
) {
398 double mismatch_psnr
= compute_psnr(img1
, img2
);
399 mismatch_psnr_
+= mismatch_psnr
;
403 unsigned int GetMismatchFrames() { return mismatch_nframes_
; }
405 void DefaultConfig() {
406 cfg_
.rc_buf_initial_sz
= 500;
407 cfg_
.rc_buf_optimal_sz
= 600;
408 cfg_
.rc_buf_sz
= 1000;
409 cfg_
.rc_min_quantizer
= 2;
410 cfg_
.rc_max_quantizer
= 56;
411 cfg_
.rc_undershoot_pct
= 50;
412 cfg_
.rc_overshoot_pct
= 50;
413 cfg_
.rc_end_usage
= AOM_CBR
;
414 cfg_
.kf_mode
= AOM_KF_AUTO
;
415 cfg_
.g_lag_in_frames
= 0;
416 cfg_
.kf_min_dist
= cfg_
.kf_max_dist
= 3000;
417 // Enable dropped frames.
418 cfg_
.rc_dropframe_thresh
= 1;
419 // Disable error_resilience mode.
420 cfg_
.g_error_resilient
= 0;
421 // Run at low bitrate.
422 cfg_
.rc_target_bitrate
= 200;
423 // We use max(kInitialWidth, kInitialHeight) because during the test
424 // the width and height of the frame are swapped
425 cfg_
.g_forced_max_frame_width
= cfg_
.g_forced_max_frame_height
=
426 AOMMAX(kInitialWidth
, kInitialHeight
);
429 std::vector
<FrameInfo
> frame_info_list_
;
431 bool change_bitrate_
;
432 double mismatch_psnr_
;
433 int mismatch_nframes_
;
436 TEST_P(ResizeRealtimeTest
, TestExternalResizeWorks
) {
437 ResizingVideoSource video
;
438 video
.flag_codec_
= 1;
440 change_bitrate_
= false;
441 mismatch_psnr_
= 0.0;
442 mismatch_nframes_
= 0;
443 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
445 // Check we decoded the same number of frames as we attempted to encode
446 ASSERT_EQ(frame_info_list_
.size(), video
.limit());
448 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
449 info
!= frame_info_list_
.end(); ++info
) {
450 const unsigned int frame
= static_cast<unsigned>(info
->pts
);
451 unsigned int expected_w
;
452 unsigned int expected_h
;
453 ScaleForFrameNumber(frame
, kInitialWidth
, kInitialHeight
, &expected_w
,
455 EXPECT_EQ(expected_w
, info
->w
)
456 << "Frame " << frame
<< " had unexpected width";
457 EXPECT_EQ(expected_h
, info
->h
)
458 << "Frame " << frame
<< " had unexpected height";
459 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
463 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
464 // Run at low bitrate, with resize_allowed = 1, and verify that we get
465 // one resize down event.
466 TEST_P(ResizeRealtimeTest
, DISABLED_TestInternalResizeDown
) {
467 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
472 change_bitrate_
= false;
473 mismatch_psnr_
= 0.0;
474 mismatch_nframes_
= 0;
475 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
477 unsigned int last_w
= cfg_
.g_w
;
478 unsigned int last_h
= cfg_
.g_h
;
479 int resize_count
= 0;
480 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
481 info
!= frame_info_list_
.end(); ++info
) {
482 if (info
->w
!= last_w
|| info
->h
!= last_h
) {
483 // Verify that resize down occurs.
484 ASSERT_LT(info
->w
, last_w
);
485 ASSERT_LT(info
->h
, last_h
);
492 #if CONFIG_AV1_DECODER
493 // Verify that we get 1 resize down event in this test.
494 ASSERT_EQ(1, resize_count
) << "Resizing should occur.";
495 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
497 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
501 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
502 // Start at low target bitrate, raise the bitrate in the middle of the clip,
503 // scaling-up should occur after bitrate changed.
504 TEST_P(ResizeRealtimeTest
, DISABLED_TestInternalResizeDownUpChangeBitRate
) {
505 ::libaom_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
510 change_bitrate_
= true;
511 mismatch_psnr_
= 0.0;
512 mismatch_nframes_
= 0;
513 // Disable dropped frames.
514 cfg_
.rc_dropframe_thresh
= 0;
515 // Starting bitrate low.
516 cfg_
.rc_target_bitrate
= 80;
517 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
519 unsigned int last_w
= cfg_
.g_w
;
520 unsigned int last_h
= cfg_
.g_h
;
521 int resize_count
= 0;
522 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
523 info
!= frame_info_list_
.end(); ++info
) {
524 if (info
->w
!= last_w
|| info
->h
!= last_h
) {
526 if (resize_count
== 1) {
527 // Verify that resize down occurs.
528 ASSERT_LT(info
->w
, last_w
);
529 ASSERT_LT(info
->h
, last_h
);
530 } else if (resize_count
== 2) {
531 // Verify that resize up occurs.
532 ASSERT_GT(info
->w
, last_w
);
533 ASSERT_GT(info
->h
, last_h
);
540 #if CONFIG_AV1_DECODER
541 // Verify that we get 2 resize events in this test.
542 ASSERT_EQ(resize_count
, 2) << "Resizing should occur twice.";
543 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
545 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
549 aom_img_fmt_t
CspForFrameNumber(int frame
) {
550 if (frame
< 10) return AOM_IMG_FMT_I420
;
551 if (frame
< 20) return AOM_IMG_FMT_I444
;
552 return AOM_IMG_FMT_I420
;
555 class ResizeCspTest
: public ResizeTest
{
557 #if WRITE_COMPRESSED_STREAM
559 : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL
), out_frames_(0) {}
561 ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
564 virtual ~ResizeCspTest() {}
566 virtual void BeginPassHook(unsigned int /*pass*/) {
567 #if WRITE_COMPRESSED_STREAM
568 outfile_
= fopen("av11-2-05-cspchape.ivf", "wb");
572 virtual void EndPassHook() {
573 #if WRITE_COMPRESSED_STREAM
575 if (!fseek(outfile_
, 0, SEEK_SET
))
576 write_ivf_file_header(&cfg_
, out_frames_
, outfile_
);
583 virtual void PreEncodeFrameHook(libaom_test::VideoSource
*video
,
584 libaom_test::Encoder
*encoder
) {
585 if (CspForFrameNumber(video
->frame()) != AOM_IMG_FMT_I420
&&
586 cfg_
.g_profile
!= 1) {
588 encoder
->Config(&cfg_
);
590 if (CspForFrameNumber(video
->frame()) == AOM_IMG_FMT_I420
&&
591 cfg_
.g_profile
!= 0) {
593 encoder
->Config(&cfg_
);
597 virtual void PSNRPktHook(const aom_codec_cx_pkt_t
*pkt
) {
598 if (frame0_psnr_
== 0.) frame0_psnr_
= pkt
->data
.psnr
.psnr
[0];
599 EXPECT_NEAR(pkt
->data
.psnr
.psnr
[0], frame0_psnr_
, 2.0);
602 #if WRITE_COMPRESSED_STREAM
603 virtual void FramePktHook(const aom_codec_cx_pkt_t
*pkt
) {
606 // Write initial file header if first frame.
607 if (pkt
->data
.frame
.pts
== 0) write_ivf_file_header(&cfg_
, 0, outfile_
);
609 // Write frame header and data.
610 write_ivf_frame_header(pkt
, outfile_
);
611 (void)fwrite(pkt
->data
.frame
.buf
, 1, pkt
->data
.frame
.sz
, outfile_
);
616 #if WRITE_COMPRESSED_STREAM
618 unsigned int out_frames_
;
622 class ResizingCspVideoSource
: public ::libaom_test::DummyVideoSource
{
624 ResizingCspVideoSource() {
625 SetSize(kInitialWidth
, kInitialHeight
);
629 virtual ~ResizingCspVideoSource() {}
632 virtual void Next() {
634 SetImageFormat(CspForFrameNumber(frame_
));
639 #if (defined(DISABLE_TRELLISQ_SEARCH) && DISABLE_TRELLISQ_SEARCH)
640 TEST_P(ResizeCspTest
, DISABLED_TestResizeCspWorks
) {
642 TEST_P(ResizeCspTest
, TestResizeCspWorks
) {
644 ResizingCspVideoSource video
;
645 init_flags_
= AOM_CODEC_USE_PSNR
;
646 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= 48;
647 cfg_
.g_lag_in_frames
= 0;
648 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
650 // Check we decoded the same number of frames as we attempted to encode
651 ASSERT_EQ(frame_info_list_
.size(), video
.limit());
654 AV1_INSTANTIATE_TEST_CASE(ResizeTest
,
655 ::testing::Values(::libaom_test::kRealTime
));
656 AV1_INSTANTIATE_TEST_CASE(ResizeInternalTestLarge
,
657 ::testing::Values(::libaom_test::kOnePassGood
));
658 AV1_INSTANTIATE_TEST_CASE(ResizeRealtimeTest
,
659 ::testing::Values(::libaom_test::kRealTime
),
660 ::testing::Range(5, 9));
661 AV1_INSTANTIATE_TEST_CASE(ResizeCspTest
,
662 ::testing::Values(::libaom_test::kRealTime
));