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