• 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__anon90f106ec0111::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,int flag_codec)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) {
97   if (frame < 10) {
98     *w = initial_w;
99     *h = initial_h;
100     return;
101   }
102   if (frame < 20) {
103     *w = initial_w * 3 / 4;
104     *h = initial_h * 3 / 4;
105     return;
106   }
107   if (frame < 30) {
108     *w = initial_w / 2;
109     *h = initial_h / 2;
110     return;
111   }
112   if (frame < 40) {
113     *w = initial_w;
114     *h = initial_h;
115     return;
116   }
117   if (frame < 50) {
118     *w = initial_w * 3 / 4;
119     *h = initial_h * 3 / 4;
120     return;
121   }
122   if (frame < 60) {
123     *w = initial_w / 2;
124     *h = initial_h / 2;
125     return;
126   }
127   if (frame < 70) {
128     *w = initial_w;
129     *h = initial_h;
130     return;
131   }
132   if (frame < 80) {
133     *w = initial_w * 3 / 4;
134     *h = initial_h * 3 / 4;
135     return;
136   }
137   if (frame < 90) {
138     *w = initial_w / 2;
139     *h = initial_h / 2;
140     return;
141   }
142   if (frame < 100) {
143     *w = initial_w * 3 / 4;
144     *h = initial_h * 3 / 4;
145     return;
146   }
147   if (frame < 110) {
148     *w = initial_w;
149     *h = initial_h;
150     return;
151   }
152   if (frame < 120) {
153     *w = initial_w * 3 / 4;
154     *h = initial_h * 3 / 4;
155     return;
156   }
157   if (frame < 130) {
158     *w = initial_w / 2;
159     *h = initial_h / 2;
160     return;
161   }
162   if (frame < 140) {
163     *w = initial_w * 3 / 4;
164     *h = initial_h * 3 / 4;
165     return;
166   }
167   if (frame < 150) {
168     *w = initial_w;
169     *h = initial_h;
170     return;
171   }
172   if (frame < 160) {
173     *w = initial_w * 3 / 4;
174     *h = initial_h * 3 / 4;
175     return;
176   }
177   if (frame < 170) {
178     *w = initial_w / 2;
179     *h = initial_h / 2;
180     return;
181   }
182   if (frame < 180) {
183     *w = initial_w * 3 / 4;
184     *h = initial_h * 3 / 4;
185     return;
186   }
187   if (frame < 190) {
188     *w = initial_w;
189     *h = initial_h;
190     return;
191   }
192   if (frame < 200) {
193     *w = initial_w * 3 / 4;
194     *h = initial_h * 3 / 4;
195     return;
196   }
197   if (frame < 210) {
198     *w = initial_w / 2;
199     *h = initial_h / 2;
200     return;
201   }
202   if (frame < 220) {
203     *w = initial_w * 3 / 4;
204     *h = initial_h * 3 / 4;
205     return;
206   }
207   if (frame < 230) {
208     *w = initial_w;
209     *h = initial_h;
210     return;
211   }
212   if (frame < 240) {
213     *w = initial_w * 3 / 4;
214     *h = initial_h * 3 / 4;
215     return;
216   }
217   if (frame < 250) {
218     *w = initial_w / 2;
219     *h = initial_h / 2;
220     return;
221   }
222   if (frame < 260) {
223     *w = initial_w;
224     *h = initial_h;
225     return;
226   }
227   // Go down very low.
228   if (frame < 270) {
229     *w = initial_w / 4;
230     *h = initial_h / 4;
231     return;
232   }
233   if (flag_codec == 1) {
234     // Cases that only works for VP9.
235     // For VP9: Swap width and height of original.
236     if (frame < 320) {
237       *w = initial_h;
238       *h = initial_w;
239       return;
240     }
241   }
242   *w = initial_w;
243   *h = initial_h;
244 }
245 
246 class ResizingVideoSource : public ::libvpx_test::DummyVideoSource {
247  public:
ResizingVideoSource()248   ResizingVideoSource() {
249     SetSize(kInitialWidth, kInitialHeight);
250     limit_ = 350;
251   }
252   int flag_codec_;
~ResizingVideoSource()253   virtual ~ResizingVideoSource() {}
254 
255  protected:
Next()256   virtual void Next() {
257     ++frame_;
258     unsigned int width;
259     unsigned int height;
260     ScaleForFrameNumber(frame_, kInitialWidth, kInitialHeight, &width, &height,
261                         flag_codec_);
262     SetSize(width, height);
263     FillFrame();
264   }
265 };
266 
267 class ResizeTest
268     : public ::libvpx_test::EncoderTest,
269       public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
270  protected:
ResizeTest()271   ResizeTest() : EncoderTest(GET_PARAM(0)) {}
272 
~ResizeTest()273   virtual ~ResizeTest() {}
274 
SetUp()275   virtual void SetUp() {
276     InitializeConfig();
277     SetMode(GET_PARAM(1));
278   }
279 
DecompressedFrameHook(const vpx_image_t & img,vpx_codec_pts_t pts)280   virtual void DecompressedFrameHook(const vpx_image_t &img,
281                                      vpx_codec_pts_t pts) {
282     frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
283   }
284 
285   std::vector<FrameInfo> frame_info_list_;
286 };
287 
TEST_P(ResizeTest,TestExternalResizeWorks)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));
293 
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,
300                         &expected_h, 0);
301     EXPECT_EQ(expected_w, info->w) << "Frame " << frame
302                                    << " had unexpected width";
303     EXPECT_EQ(expected_h, info->h) << "Frame " << frame
304                                    << " had unexpected height";
305   }
306 }
307 
308 const unsigned int kStepDownFrame = 3;
309 const unsigned int kStepUpFrame = 6;
310 
311 class ResizeInternalTest : public ResizeTest {
312  protected:
313 #if WRITE_COMPRESSED_STREAM
ResizeInternalTest()314   ResizeInternalTest()
315       : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL), out_frames_(0) {}
316 #else
317   ResizeInternalTest() : ResizeTest(), frame0_psnr_(0.0) {}
318 #endif
319 
~ResizeInternalTest()320   virtual ~ResizeInternalTest() {}
321 
BeginPassHook(unsigned int)322   virtual void BeginPassHook(unsigned int /*pass*/) {
323 #if WRITE_COMPRESSED_STREAM
324     outfile_ = fopen("vp90-2-05-resize.ivf", "wb");
325 #endif
326   }
327 
EndPassHook()328   virtual void EndPassHook() {
329 #if WRITE_COMPRESSED_STREAM
330     if (outfile_) {
331       if (!fseek(outfile_, 0, SEEK_SET))
332         write_ivf_file_header(&cfg_, out_frames_, outfile_);
333       fclose(outfile_);
334       outfile_ = NULL;
335     }
336 #endif
337   }
338 
PreEncodeFrameHook(libvpx_test::VideoSource * video,libvpx_test::Encoder * encoder)339   virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
340                                   libvpx_test::Encoder *encoder) {
341     if (change_config_) {
342       int new_q = 60;
343       if (video->frame() == 0) {
344         struct vpx_scaling_mode mode = { VP8E_ONETWO, VP8E_ONETWO };
345         encoder->Control(VP8E_SET_SCALEMODE, &mode);
346       }
347       if (video->frame() == 1) {
348         struct vpx_scaling_mode mode = { VP8E_NORMAL, VP8E_NORMAL };
349         encoder->Control(VP8E_SET_SCALEMODE, &mode);
350         cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = new_q;
351         encoder->Config(&cfg_);
352       }
353     } else {
354       if (video->frame() == kStepDownFrame) {
355         struct vpx_scaling_mode mode = { VP8E_FOURFIVE, VP8E_THREEFIVE };
356         encoder->Control(VP8E_SET_SCALEMODE, &mode);
357       }
358       if (video->frame() == kStepUpFrame) {
359         struct vpx_scaling_mode mode = { VP8E_NORMAL, VP8E_NORMAL };
360         encoder->Control(VP8E_SET_SCALEMODE, &mode);
361       }
362     }
363   }
364 
PSNRPktHook(const vpx_codec_cx_pkt_t * pkt)365   virtual void PSNRPktHook(const vpx_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.0);
368   }
369 
370 #if WRITE_COMPRESSED_STREAM
FramePktHook(const vpx_codec_cx_pkt_t * pkt)371   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
372     ++out_frames_;
373 
374     // Write initial file header if first frame.
375     if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
376 
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_);
380   }
381 #endif
382 
383   double frame0_psnr_;
384   bool change_config_;
385 #if WRITE_COMPRESSED_STREAM
386   FILE *outfile_;
387   unsigned int out_frames_;
388 #endif
389 };
390 
TEST_P(ResizeInternalTest,TestInternalResizeWorks)391 TEST_P(ResizeInternalTest, TestInternalResizeWorks) {
392   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
393                                        30, 1, 0, 10);
394   init_flags_ = VPX_CODEC_USE_PSNR;
395   change_config_ = false;
396 
397   // q picked such that initial keyframe on this clip is ~30dB PSNR
398   cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
399 
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));
406 
407   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
408        info != frame_info_list_.end(); ++info) {
409     const vpx_codec_pts_t pts = info->pts;
410     if (pts >= kStepDownFrame && pts < kStepUpFrame) {
411       ASSERT_EQ(282U, info->w) << "Frame " << pts << " had unexpected width";
412       ASSERT_EQ(173U, info->h) << "Frame " << pts << " had unexpected height";
413     } else {
414       EXPECT_EQ(352U, info->w) << "Frame " << pts << " had unexpected width";
415       EXPECT_EQ(288U, info->h) << "Frame " << pts << " had unexpected height";
416     }
417   }
418 }
419 
TEST_P(ResizeInternalTest,TestInternalResizeChangeConfig)420 TEST_P(ResizeInternalTest, TestInternalResizeChangeConfig) {
421   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
422                                        30, 1, 0, 10);
423   cfg_.g_w = 352;
424   cfg_.g_h = 288;
425   change_config_ = true;
426   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
427 }
428 
429 class ResizeRealtimeTest
430     : public ::libvpx_test::EncoderTest,
431       public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
432  protected:
ResizeRealtimeTest()433   ResizeRealtimeTest() : EncoderTest(GET_PARAM(0)) {}
~ResizeRealtimeTest()434   virtual ~ResizeRealtimeTest() {}
435 
PreEncodeFrameHook(libvpx_test::VideoSource * video,libvpx_test::Encoder * encoder)436   virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
437                                   libvpx_test::Encoder *encoder) {
438     if (video->frame() == 0) {
439       encoder->Control(VP9E_SET_AQ_MODE, 3);
440       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
441     }
442 
443     if (change_bitrate_ && video->frame() == 120) {
444       change_bitrate_ = false;
445       cfg_.rc_target_bitrate = 500;
446       encoder->Config(&cfg_);
447     }
448   }
449 
SetUp()450   virtual void SetUp() {
451     InitializeConfig();
452     SetMode(GET_PARAM(1));
453     set_cpu_used_ = GET_PARAM(2);
454   }
455 
DecompressedFrameHook(const vpx_image_t & img,vpx_codec_pts_t pts)456   virtual void DecompressedFrameHook(const vpx_image_t &img,
457                                      vpx_codec_pts_t pts) {
458     frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
459   }
460 
MismatchHook(const vpx_image_t * img1,const vpx_image_t * img2)461   virtual void MismatchHook(const vpx_image_t *img1, const vpx_image_t *img2) {
462     double mismatch_psnr = compute_psnr(img1, img2);
463     mismatch_psnr_ += mismatch_psnr;
464     ++mismatch_nframes_;
465   }
466 
GetMismatchFrames()467   unsigned int GetMismatchFrames() { return mismatch_nframes_; }
468 
DefaultConfig()469   void DefaultConfig() {
470     cfg_.rc_buf_initial_sz = 500;
471     cfg_.rc_buf_optimal_sz = 600;
472     cfg_.rc_buf_sz = 1000;
473     cfg_.rc_min_quantizer = 2;
474     cfg_.rc_max_quantizer = 56;
475     cfg_.rc_undershoot_pct = 50;
476     cfg_.rc_overshoot_pct = 50;
477     cfg_.rc_end_usage = VPX_CBR;
478     cfg_.kf_mode = VPX_KF_AUTO;
479     cfg_.g_lag_in_frames = 0;
480     cfg_.kf_min_dist = cfg_.kf_max_dist = 3000;
481     // Enable dropped frames.
482     cfg_.rc_dropframe_thresh = 1;
483     // Enable error_resilience mode.
484     cfg_.g_error_resilient = 1;
485     // Enable dynamic resizing.
486     cfg_.rc_resize_allowed = 1;
487     // Run at low bitrate.
488     cfg_.rc_target_bitrate = 200;
489   }
490 
491   std::vector<FrameInfo> frame_info_list_;
492   int set_cpu_used_;
493   bool change_bitrate_;
494   double mismatch_psnr_;
495   int mismatch_nframes_;
496 };
497 
TEST_P(ResizeRealtimeTest,TestExternalResizeWorks)498 TEST_P(ResizeRealtimeTest, TestExternalResizeWorks) {
499   ResizingVideoSource video;
500   video.flag_codec_ = 1;
501   DefaultConfig();
502   // Disable internal resize for this test.
503   cfg_.rc_resize_allowed = 0;
504   change_bitrate_ = false;
505   mismatch_psnr_ = 0.0;
506   mismatch_nframes_ = 0;
507   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
508 
509   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
510        info != frame_info_list_.end(); ++info) {
511     const unsigned int frame = static_cast<unsigned>(info->pts);
512     unsigned int expected_w;
513     unsigned int expected_h;
514     ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w,
515                         &expected_h, 1);
516     EXPECT_EQ(expected_w, info->w) << "Frame " << frame
517                                    << " had unexpected width";
518     EXPECT_EQ(expected_h, info->h) << "Frame " << frame
519                                    << " had unexpected height";
520     EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
521   }
522 }
523 
524 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
525 // Run at low bitrate, with resize_allowed = 1, and verify that we get
526 // one resize down event.
TEST_P(ResizeRealtimeTest,TestInternalResizeDown)527 TEST_P(ResizeRealtimeTest, TestInternalResizeDown) {
528   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
529                                        30, 1, 0, 299);
530   DefaultConfig();
531   cfg_.g_w = 352;
532   cfg_.g_h = 288;
533   change_bitrate_ = false;
534   mismatch_psnr_ = 0.0;
535   mismatch_nframes_ = 0;
536   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
537 
538   unsigned int last_w = cfg_.g_w;
539   unsigned int last_h = cfg_.g_h;
540   int resize_count = 0;
541   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
542        info != frame_info_list_.end(); ++info) {
543     if (info->w != last_w || info->h != last_h) {
544       // Verify that resize down occurs.
545       ASSERT_LT(info->w, last_w);
546       ASSERT_LT(info->h, last_h);
547       last_w = info->w;
548       last_h = info->h;
549       resize_count++;
550     }
551   }
552 
553 #if CONFIG_VP9_DECODER
554   // Verify that we get 1 resize down event in this test.
555   ASSERT_EQ(1, resize_count) << "Resizing should occur.";
556   EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
557 #else
558   printf("Warning: VP9 decoder unavailable, unable to check resize count!\n");
559 #endif
560 }
561 
562 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
563 // Start at low target bitrate, raise the bitrate in the middle of the clip,
564 // scaling-up should occur after bitrate changed.
TEST_P(ResizeRealtimeTest,TestInternalResizeDownUpChangeBitRate)565 TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRate) {
566   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
567                                        30, 1, 0, 359);
568   DefaultConfig();
569   cfg_.g_w = 352;
570   cfg_.g_h = 288;
571   change_bitrate_ = true;
572   mismatch_psnr_ = 0.0;
573   mismatch_nframes_ = 0;
574   // Disable dropped frames.
575   cfg_.rc_dropframe_thresh = 0;
576   // Starting bitrate low.
577   cfg_.rc_target_bitrate = 80;
578   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
579 
580   unsigned int last_w = cfg_.g_w;
581   unsigned int last_h = cfg_.g_h;
582   int resize_count = 0;
583   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
584        info != frame_info_list_.end(); ++info) {
585     if (info->w != last_w || info->h != last_h) {
586       resize_count++;
587       if (resize_count == 1) {
588         // Verify that resize down occurs.
589         ASSERT_LT(info->w, last_w);
590         ASSERT_LT(info->h, last_h);
591       } else if (resize_count == 2) {
592         // Verify that resize up occurs.
593         ASSERT_GT(info->w, last_w);
594         ASSERT_GT(info->h, last_h);
595       }
596       last_w = info->w;
597       last_h = info->h;
598     }
599   }
600 
601 #if CONFIG_VP9_DECODER
602   // Verify that we get 2 resize events in this test.
603   ASSERT_EQ(resize_count, 2) << "Resizing should occur twice.";
604   EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
605 #else
606   printf("Warning: VP9 decoder unavailable, unable to check resize count!\n");
607 #endif
608 }
609 
CspForFrameNumber(int frame)610 vpx_img_fmt_t CspForFrameNumber(int frame) {
611   if (frame < 10) return VPX_IMG_FMT_I420;
612   if (frame < 20) return VPX_IMG_FMT_I444;
613   return VPX_IMG_FMT_I420;
614 }
615 
616 class ResizeCspTest : public ResizeTest {
617  protected:
618 #if WRITE_COMPRESSED_STREAM
ResizeCspTest()619   ResizeCspTest()
620       : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL), out_frames_(0) {}
621 #else
622   ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
623 #endif
624 
~ResizeCspTest()625   virtual ~ResizeCspTest() {}
626 
BeginPassHook(unsigned int)627   virtual void BeginPassHook(unsigned int /*pass*/) {
628 #if WRITE_COMPRESSED_STREAM
629     outfile_ = fopen("vp91-2-05-cspchape.ivf", "wb");
630 #endif
631   }
632 
EndPassHook()633   virtual void EndPassHook() {
634 #if WRITE_COMPRESSED_STREAM
635     if (outfile_) {
636       if (!fseek(outfile_, 0, SEEK_SET))
637         write_ivf_file_header(&cfg_, out_frames_, outfile_);
638       fclose(outfile_);
639       outfile_ = NULL;
640     }
641 #endif
642   }
643 
PreEncodeFrameHook(libvpx_test::VideoSource * video,libvpx_test::Encoder * encoder)644   virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
645                                   libvpx_test::Encoder *encoder) {
646     if (CspForFrameNumber(video->frame()) != VPX_IMG_FMT_I420 &&
647         cfg_.g_profile != 1) {
648       cfg_.g_profile = 1;
649       encoder->Config(&cfg_);
650     }
651     if (CspForFrameNumber(video->frame()) == VPX_IMG_FMT_I420 &&
652         cfg_.g_profile != 0) {
653       cfg_.g_profile = 0;
654       encoder->Config(&cfg_);
655     }
656   }
657 
PSNRPktHook(const vpx_codec_cx_pkt_t * pkt)658   virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
659     if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
660     EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 2.0);
661   }
662 
663 #if WRITE_COMPRESSED_STREAM
FramePktHook(const vpx_codec_cx_pkt_t * pkt)664   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
665     ++out_frames_;
666 
667     // Write initial file header if first frame.
668     if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
669 
670     // Write frame header and data.
671     write_ivf_frame_header(pkt, outfile_);
672     (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
673   }
674 #endif
675 
676   double frame0_psnr_;
677 #if WRITE_COMPRESSED_STREAM
678   FILE *outfile_;
679   unsigned int out_frames_;
680 #endif
681 };
682 
683 class ResizingCspVideoSource : public ::libvpx_test::DummyVideoSource {
684  public:
ResizingCspVideoSource()685   ResizingCspVideoSource() {
686     SetSize(kInitialWidth, kInitialHeight);
687     limit_ = 30;
688   }
689 
~ResizingCspVideoSource()690   virtual ~ResizingCspVideoSource() {}
691 
692  protected:
Next()693   virtual void Next() {
694     ++frame_;
695     SetImageFormat(CspForFrameNumber(frame_));
696     FillFrame();
697   }
698 };
699 
TEST_P(ResizeCspTest,TestResizeCspWorks)700 TEST_P(ResizeCspTest, TestResizeCspWorks) {
701   ResizingCspVideoSource video;
702   init_flags_ = VPX_CODEC_USE_PSNR;
703   cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
704   cfg_.g_lag_in_frames = 0;
705   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
706 }
707 
708 VP8_INSTANTIATE_TEST_CASE(ResizeTest, ONE_PASS_TEST_MODES);
709 VP9_INSTANTIATE_TEST_CASE(ResizeTest,
710                           ::testing::Values(::libvpx_test::kRealTime));
711 VP9_INSTANTIATE_TEST_CASE(ResizeInternalTest,
712                           ::testing::Values(::libvpx_test::kOnePassBest));
713 VP9_INSTANTIATE_TEST_CASE(ResizeRealtimeTest,
714                           ::testing::Values(::libvpx_test::kRealTime),
715                           ::testing::Range(5, 9));
716 VP9_INSTANTIATE_TEST_CASE(ResizeCspTest,
717                           ::testing::Values(::libvpx_test::kRealTime));
718 }  // namespace
719