• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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