Home | History | Annotate | Download | only in test
      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
     27 static void mem_put_le16(char *const mem, const unsigned int val) {
     28   mem[0] = val;
     29   mem[1] = val >> 8;
     30 }
     31 
     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 
     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 
     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 
     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 {
     86   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 
     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:
    248   ResizingVideoSource() {
    249     SetSize(kInitialWidth, kInitialHeight);
    250     limit_ = 350;
    251   }
    252   int flag_codec_;
    253   virtual ~ResizingVideoSource() {}
    254 
    255  protected:
    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:
    271   ResizeTest() : EncoderTest(GET_PARAM(0)) {}
    272 
    273   virtual ~ResizeTest() {}
    274 
    275   virtual void SetUp() {
    276     InitializeConfig();
    277     SetMode(GET_PARAM(1));
    278   }
    279 
    280   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
    281     ASSERT_NE(static_cast<int>(pkt->data.frame.width[0]), 0);
    282     ASSERT_NE(static_cast<int>(pkt->data.frame.height[0]), 0);
    283     encode_frame_width_.push_back(pkt->data.frame.width[0]);
    284     encode_frame_height_.push_back(pkt->data.frame.height[0]);
    285   }
    286 
    287   unsigned int GetFrameWidth(size_t idx) const {
    288     return encode_frame_width_[idx];
    289   }
    290 
    291   unsigned int GetFrameHeight(size_t idx) const {
    292     return encode_frame_height_[idx];
    293   }
    294 
    295   virtual void DecompressedFrameHook(const vpx_image_t &img,
    296                                      vpx_codec_pts_t pts) {
    297     frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
    298   }
    299 
    300   std::vector<FrameInfo> frame_info_list_;
    301   std::vector<unsigned int> encode_frame_width_;
    302   std::vector<unsigned int> encode_frame_height_;
    303 };
    304 
    305 TEST_P(ResizeTest, TestExternalResizeWorks) {
    306   ResizingVideoSource video;
    307   video.flag_codec_ = 0;
    308   cfg_.g_lag_in_frames = 0;
    309   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
    310 
    311   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
    312        info != frame_info_list_.end(); ++info) {
    313     const unsigned int frame = static_cast<unsigned>(info->pts);
    314     unsigned int expected_w;
    315     unsigned int expected_h;
    316     const size_t idx = info - frame_info_list_.begin();
    317     ASSERT_EQ(info->w, GetFrameWidth(idx));
    318     ASSERT_EQ(info->h, GetFrameHeight(idx));
    319     ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w,
    320                         &expected_h, 0);
    321     EXPECT_EQ(expected_w, info->w)
    322         << "Frame " << frame << " had unexpected width";
    323     EXPECT_EQ(expected_h, info->h)
    324         << "Frame " << frame << " had unexpected height";
    325   }
    326 }
    327 
    328 const unsigned int kStepDownFrame = 3;
    329 const unsigned int kStepUpFrame = 6;
    330 
    331 class ResizeInternalTest : public ResizeTest {
    332  protected:
    333 #if WRITE_COMPRESSED_STREAM
    334   ResizeInternalTest()
    335       : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL), out_frames_(0) {}
    336 #else
    337   ResizeInternalTest() : ResizeTest(), frame0_psnr_(0.0) {}
    338 #endif
    339 
    340   virtual ~ResizeInternalTest() {}
    341 
    342   virtual void BeginPassHook(unsigned int /*pass*/) {
    343 #if WRITE_COMPRESSED_STREAM
    344     outfile_ = fopen("vp90-2-05-resize.ivf", "wb");
    345 #endif
    346   }
    347 
    348   virtual void EndPassHook() {
    349 #if WRITE_COMPRESSED_STREAM
    350     if (outfile_) {
    351       if (!fseek(outfile_, 0, SEEK_SET))
    352         write_ivf_file_header(&cfg_, out_frames_, outfile_);
    353       fclose(outfile_);
    354       outfile_ = NULL;
    355     }
    356 #endif
    357   }
    358 
    359   virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
    360                                   libvpx_test::Encoder *encoder) {
    361     if (change_config_) {
    362       int new_q = 60;
    363       if (video->frame() == 0) {
    364         struct vpx_scaling_mode mode = { VP8E_ONETWO, VP8E_ONETWO };
    365         encoder->Control(VP8E_SET_SCALEMODE, &mode);
    366       }
    367       if (video->frame() == 1) {
    368         struct vpx_scaling_mode mode = { VP8E_NORMAL, VP8E_NORMAL };
    369         encoder->Control(VP8E_SET_SCALEMODE, &mode);
    370         cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = new_q;
    371         encoder->Config(&cfg_);
    372       }
    373     } else {
    374       if (video->frame() == kStepDownFrame) {
    375         struct vpx_scaling_mode mode = { VP8E_FOURFIVE, VP8E_THREEFIVE };
    376         encoder->Control(VP8E_SET_SCALEMODE, &mode);
    377       }
    378       if (video->frame() == kStepUpFrame) {
    379         struct vpx_scaling_mode mode = { VP8E_NORMAL, VP8E_NORMAL };
    380         encoder->Control(VP8E_SET_SCALEMODE, &mode);
    381       }
    382     }
    383   }
    384 
    385   virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
    386     if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
    387     EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 2.0);
    388   }
    389 
    390 #if WRITE_COMPRESSED_STREAM
    391   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
    392     ++out_frames_;
    393 
    394     // Write initial file header if first frame.
    395     if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
    396 
    397     // Write frame header and data.
    398     write_ivf_frame_header(pkt, outfile_);
    399     (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
    400   }
    401 #endif
    402 
    403   double frame0_psnr_;
    404   bool change_config_;
    405 #if WRITE_COMPRESSED_STREAM
    406   FILE *outfile_;
    407   unsigned int out_frames_;
    408 #endif
    409 };
    410 
    411 TEST_P(ResizeInternalTest, TestInternalResizeWorks) {
    412   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
    413                                        30, 1, 0, 10);
    414   init_flags_ = VPX_CODEC_USE_PSNR;
    415   change_config_ = false;
    416 
    417   // q picked such that initial keyframe on this clip is ~30dB PSNR
    418   cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
    419 
    420   // If the number of frames being encoded is smaller than g_lag_in_frames
    421   // the encoded frame is unavailable using the current API. Comparing
    422   // frames to detect mismatch would then not be possible. Set
    423   // g_lag_in_frames = 0 to get around this.
    424   cfg_.g_lag_in_frames = 0;
    425   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
    426 
    427   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
    428        info != frame_info_list_.end(); ++info) {
    429     const vpx_codec_pts_t pts = info->pts;
    430     if (pts >= kStepDownFrame && pts < kStepUpFrame) {
    431       ASSERT_EQ(282U, info->w) << "Frame " << pts << " had unexpected width";
    432       ASSERT_EQ(173U, info->h) << "Frame " << pts << " had unexpected height";
    433     } else {
    434       EXPECT_EQ(352U, info->w) << "Frame " << pts << " had unexpected width";
    435       EXPECT_EQ(288U, info->h) << "Frame " << pts << " had unexpected height";
    436     }
    437   }
    438 }
    439 
    440 TEST_P(ResizeInternalTest, TestInternalResizeChangeConfig) {
    441   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
    442                                        30, 1, 0, 10);
    443   cfg_.g_w = 352;
    444   cfg_.g_h = 288;
    445   change_config_ = true;
    446   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
    447 }
    448 
    449 class ResizeRealtimeTest
    450     : public ::libvpx_test::EncoderTest,
    451       public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
    452  protected:
    453   ResizeRealtimeTest() : EncoderTest(GET_PARAM(0)) {}
    454   virtual ~ResizeRealtimeTest() {}
    455 
    456   virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
    457                                   libvpx_test::Encoder *encoder) {
    458     if (video->frame() == 0) {
    459       encoder->Control(VP9E_SET_AQ_MODE, 3);
    460       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
    461     }
    462 
    463     if (change_bitrate_ && video->frame() == 120) {
    464       change_bitrate_ = false;
    465       cfg_.rc_target_bitrate = 500;
    466       encoder->Config(&cfg_);
    467     }
    468   }
    469 
    470   virtual void SetUp() {
    471     InitializeConfig();
    472     SetMode(GET_PARAM(1));
    473     set_cpu_used_ = GET_PARAM(2);
    474   }
    475 
    476   virtual void DecompressedFrameHook(const vpx_image_t &img,
    477                                      vpx_codec_pts_t pts) {
    478     frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
    479   }
    480 
    481   virtual void MismatchHook(const vpx_image_t *img1, const vpx_image_t *img2) {
    482     double mismatch_psnr = compute_psnr(img1, img2);
    483     mismatch_psnr_ += mismatch_psnr;
    484     ++mismatch_nframes_;
    485   }
    486 
    487   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
    488     ASSERT_NE(static_cast<int>(pkt->data.frame.width[0]), 0);
    489     ASSERT_NE(static_cast<int>(pkt->data.frame.height[0]), 0);
    490     encode_frame_width_.push_back(pkt->data.frame.width[0]);
    491     encode_frame_height_.push_back(pkt->data.frame.height[0]);
    492   }
    493 
    494   unsigned int GetMismatchFrames() { return mismatch_nframes_; }
    495 
    496   unsigned int GetFrameWidth(size_t idx) const {
    497     return encode_frame_width_[idx];
    498   }
    499 
    500   unsigned int GetFrameHeight(size_t idx) const {
    501     return encode_frame_height_[idx];
    502   }
    503 
    504   void DefaultConfig() {
    505     cfg_.rc_buf_initial_sz = 500;
    506     cfg_.rc_buf_optimal_sz = 600;
    507     cfg_.rc_buf_sz = 1000;
    508     cfg_.rc_min_quantizer = 2;
    509     cfg_.rc_max_quantizer = 56;
    510     cfg_.rc_undershoot_pct = 50;
    511     cfg_.rc_overshoot_pct = 50;
    512     cfg_.rc_end_usage = VPX_CBR;
    513     cfg_.kf_mode = VPX_KF_AUTO;
    514     cfg_.g_lag_in_frames = 0;
    515     cfg_.kf_min_dist = cfg_.kf_max_dist = 3000;
    516     // Enable dropped frames.
    517     cfg_.rc_dropframe_thresh = 1;
    518     // Enable error_resilience mode.
    519     cfg_.g_error_resilient = 1;
    520     // Enable dynamic resizing.
    521     cfg_.rc_resize_allowed = 1;
    522     // Run at low bitrate.
    523     cfg_.rc_target_bitrate = 200;
    524   }
    525 
    526   std::vector<FrameInfo> frame_info_list_;
    527   int set_cpu_used_;
    528   bool change_bitrate_;
    529   double mismatch_psnr_;
    530   int mismatch_nframes_;
    531   std::vector<unsigned int> encode_frame_width_;
    532   std::vector<unsigned int> encode_frame_height_;
    533 };
    534 
    535 TEST_P(ResizeRealtimeTest, TestExternalResizeWorks) {
    536   ResizingVideoSource video;
    537   video.flag_codec_ = 1;
    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, 1);
    553     EXPECT_EQ(expected_w, info->w)
    554         << "Frame " << frame << " had unexpected width";
    555     EXPECT_EQ(expected_h, info->h)
    556         << "Frame " << frame << " had unexpected height";
    557     EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
    558   }
    559 }
    560 
    561 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
    562 // Run at low bitrate, with resize_allowed = 1, and verify that we get
    563 // one resize down event.
    564 TEST_P(ResizeRealtimeTest, TestInternalResizeDown) {
    565   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
    566                                        30, 1, 0, 299);
    567   DefaultConfig();
    568   cfg_.g_w = 352;
    569   cfg_.g_h = 288;
    570   change_bitrate_ = false;
    571   mismatch_psnr_ = 0.0;
    572   mismatch_nframes_ = 0;
    573   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
    574 
    575   unsigned int last_w = cfg_.g_w;
    576   unsigned int last_h = cfg_.g_h;
    577   int resize_count = 0;
    578   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
    579        info != frame_info_list_.end(); ++info) {
    580     if (info->w != last_w || info->h != last_h) {
    581       // Verify that resize down occurs.
    582       ASSERT_LT(info->w, last_w);
    583       ASSERT_LT(info->h, last_h);
    584       last_w = info->w;
    585       last_h = info->h;
    586       resize_count++;
    587     }
    588   }
    589 
    590 #if CONFIG_VP9_DECODER
    591   // Verify that we get 1 resize down event in this test.
    592   ASSERT_EQ(1, resize_count) << "Resizing should occur.";
    593   EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
    594 #else
    595   printf("Warning: VP9 decoder unavailable, unable to check resize count!\n");
    596 #endif
    597 }
    598 
    599 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
    600 // Start at low target bitrate, raise the bitrate in the middle of the clip,
    601 // scaling-up should occur after bitrate changed.
    602 TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRate) {
    603   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
    604                                        30, 1, 0, 359);
    605   DefaultConfig();
    606   cfg_.g_w = 352;
    607   cfg_.g_h = 288;
    608   change_bitrate_ = true;
    609   mismatch_psnr_ = 0.0;
    610   mismatch_nframes_ = 0;
    611   // Disable dropped frames.
    612   cfg_.rc_dropframe_thresh = 0;
    613   // Starting bitrate low.
    614   cfg_.rc_target_bitrate = 80;
    615   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
    616 
    617   unsigned int last_w = cfg_.g_w;
    618   unsigned int last_h = cfg_.g_h;
    619   int resize_count = 0;
    620   for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
    621        info != frame_info_list_.end(); ++info) {
    622     const size_t idx = info - frame_info_list_.begin();
    623     ASSERT_EQ(info->w, GetFrameWidth(idx));
    624     ASSERT_EQ(info->h, GetFrameHeight(idx));
    625     if (info->w != last_w || info->h != last_h) {
    626       resize_count++;
    627       if (resize_count == 1) {
    628         // Verify that resize down occurs.
    629         ASSERT_LT(info->w, last_w);
    630         ASSERT_LT(info->h, last_h);
    631       } else if (resize_count == 2) {
    632         // Verify that resize up occurs.
    633         ASSERT_GT(info->w, last_w);
    634         ASSERT_GT(info->h, last_h);
    635       }
    636       last_w = info->w;
    637       last_h = info->h;
    638     }
    639   }
    640 
    641 #if CONFIG_VP9_DECODER
    642   // Verify that we get 2 resize events in this test.
    643   ASSERT_EQ(resize_count, 2) << "Resizing should occur twice.";
    644   EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
    645 #else
    646   printf("Warning: VP9 decoder unavailable, unable to check resize count!\n");
    647 #endif
    648 }
    649 
    650 vpx_img_fmt_t CspForFrameNumber(int frame) {
    651   if (frame < 10) return VPX_IMG_FMT_I420;
    652   if (frame < 20) return VPX_IMG_FMT_I444;
    653   return VPX_IMG_FMT_I420;
    654 }
    655 
    656 class ResizeCspTest : public ResizeTest {
    657  protected:
    658 #if WRITE_COMPRESSED_STREAM
    659   ResizeCspTest()
    660       : ResizeTest(), frame0_psnr_(0.0), outfile_(NULL), out_frames_(0) {}
    661 #else
    662   ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
    663 #endif
    664 
    665   virtual ~ResizeCspTest() {}
    666 
    667   virtual void BeginPassHook(unsigned int /*pass*/) {
    668 #if WRITE_COMPRESSED_STREAM
    669     outfile_ = fopen("vp91-2-05-cspchape.ivf", "wb");
    670 #endif
    671   }
    672 
    673   virtual void EndPassHook() {
    674 #if WRITE_COMPRESSED_STREAM
    675     if (outfile_) {
    676       if (!fseek(outfile_, 0, SEEK_SET))
    677         write_ivf_file_header(&cfg_, out_frames_, outfile_);
    678       fclose(outfile_);
    679       outfile_ = NULL;
    680     }
    681 #endif
    682   }
    683 
    684   virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
    685                                   libvpx_test::Encoder *encoder) {
    686     if (CspForFrameNumber(video->frame()) != VPX_IMG_FMT_I420 &&
    687         cfg_.g_profile != 1) {
    688       cfg_.g_profile = 1;
    689       encoder->Config(&cfg_);
    690     }
    691     if (CspForFrameNumber(video->frame()) == VPX_IMG_FMT_I420 &&
    692         cfg_.g_profile != 0) {
    693       cfg_.g_profile = 0;
    694       encoder->Config(&cfg_);
    695     }
    696   }
    697 
    698   virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
    699     if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
    700     EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 2.0);
    701   }
    702 
    703 #if WRITE_COMPRESSED_STREAM
    704   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
    705     ++out_frames_;
    706 
    707     // Write initial file header if first frame.
    708     if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
    709 
    710     // Write frame header and data.
    711     write_ivf_frame_header(pkt, outfile_);
    712     (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
    713   }
    714 #endif
    715 
    716   double frame0_psnr_;
    717 #if WRITE_COMPRESSED_STREAM
    718   FILE *outfile_;
    719   unsigned int out_frames_;
    720 #endif
    721 };
    722 
    723 class ResizingCspVideoSource : public ::libvpx_test::DummyVideoSource {
    724  public:
    725   ResizingCspVideoSource() {
    726     SetSize(kInitialWidth, kInitialHeight);
    727     limit_ = 30;
    728   }
    729 
    730   virtual ~ResizingCspVideoSource() {}
    731 
    732  protected:
    733   virtual void Next() {
    734     ++frame_;
    735     SetImageFormat(CspForFrameNumber(frame_));
    736     FillFrame();
    737   }
    738 };
    739 
    740 TEST_P(ResizeCspTest, TestResizeCspWorks) {
    741   ResizingCspVideoSource video;
    742   init_flags_ = VPX_CODEC_USE_PSNR;
    743   cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
    744   cfg_.g_lag_in_frames = 0;
    745   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
    746 }
    747 
    748 VP8_INSTANTIATE_TEST_CASE(ResizeTest, ONE_PASS_TEST_MODES);
    749 VP9_INSTANTIATE_TEST_CASE(ResizeTest,
    750                           ::testing::Values(::libvpx_test::kRealTime));
    751 VP9_INSTANTIATE_TEST_CASE(ResizeInternalTest,
    752                           ::testing::Values(::libvpx_test::kOnePassBest));
    753 VP9_INSTANTIATE_TEST_CASE(ResizeRealtimeTest,
    754                           ::testing::Values(::libvpx_test::kRealTime),
    755                           ::testing::Range(5, 9));
    756 VP9_INSTANTIATE_TEST_CASE(ResizeCspTest,
    757                           ::testing::Values(::libvpx_test::kRealTime));
    758 }  // namespace
    759