Home | History | Annotate | Download | only in test
      1 /*
      2  *  Copyright (c) 2014 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 
     11 #include <string>
     12 
     13 #include "./vpx_config.h"
     14 #include "test/codec_factory.h"
     15 #include "test/decode_test_driver.h"
     16 #include "test/ivf_video_source.h"
     17 #include "test/md5_helper.h"
     18 #include "test/test_vectors.h"
     19 #include "test/util.h"
     20 #if CONFIG_WEBM_IO
     21 #include "test/webm_video_source.h"
     22 #endif
     23 
     24 namespace {
     25 
     26 const int kVideoNameParam = 1;
     27 
     28 struct ExternalFrameBuffer {
     29   uint8_t *data;
     30   size_t size;
     31   int in_use;
     32 };
     33 
     34 // Class to manipulate a list of external frame buffers.
     35 class ExternalFrameBufferList {
     36  public:
     37   ExternalFrameBufferList() : num_buffers_(0), ext_fb_list_(NULL) {}
     38 
     39   virtual ~ExternalFrameBufferList() {
     40     for (int i = 0; i < num_buffers_; ++i) {
     41       delete[] ext_fb_list_[i].data;
     42     }
     43     delete[] ext_fb_list_;
     44   }
     45 
     46   // Creates the list to hold the external buffers. Returns true on success.
     47   bool CreateBufferList(int num_buffers) {
     48     if (num_buffers < 0) return false;
     49 
     50     num_buffers_ = num_buffers;
     51     ext_fb_list_ = new ExternalFrameBuffer[num_buffers_];
     52     EXPECT_TRUE(ext_fb_list_ != NULL);
     53     memset(ext_fb_list_, 0, sizeof(ext_fb_list_[0]) * num_buffers_);
     54     return true;
     55   }
     56 
     57   // Searches the frame buffer list for a free frame buffer. Makes sure
     58   // that the frame buffer is at least |min_size| in bytes. Marks that the
     59   // frame buffer is in use by libvpx. Finally sets |fb| to point to the
     60   // external frame buffer. Returns < 0 on an error.
     61   int GetFreeFrameBuffer(size_t min_size, vpx_codec_frame_buffer_t *fb) {
     62     EXPECT_TRUE(fb != NULL);
     63     const int idx = FindFreeBufferIndex();
     64     if (idx == num_buffers_) return -1;
     65 
     66     if (ext_fb_list_[idx].size < min_size) {
     67       delete[] ext_fb_list_[idx].data;
     68       ext_fb_list_[idx].data = new uint8_t[min_size];
     69       memset(ext_fb_list_[idx].data, 0, min_size);
     70       ext_fb_list_[idx].size = min_size;
     71     }
     72 
     73     SetFrameBuffer(idx, fb);
     74     return 0;
     75   }
     76 
     77   // Test function that will not allocate any data for the frame buffer.
     78   // Returns < 0 on an error.
     79   int GetZeroFrameBuffer(size_t min_size, vpx_codec_frame_buffer_t *fb) {
     80     EXPECT_TRUE(fb != NULL);
     81     const int idx = FindFreeBufferIndex();
     82     if (idx == num_buffers_) return -1;
     83 
     84     if (ext_fb_list_[idx].size < min_size) {
     85       delete[] ext_fb_list_[idx].data;
     86       ext_fb_list_[idx].data = NULL;
     87       ext_fb_list_[idx].size = min_size;
     88     }
     89 
     90     SetFrameBuffer(idx, fb);
     91     return 0;
     92   }
     93 
     94   // Marks the external frame buffer that |fb| is pointing to as free.
     95   // Returns < 0 on an error.
     96   int ReturnFrameBuffer(vpx_codec_frame_buffer_t *fb) {
     97     if (fb == NULL) {
     98       EXPECT_TRUE(fb != NULL);
     99       return -1;
    100     }
    101     ExternalFrameBuffer *const ext_fb =
    102         reinterpret_cast<ExternalFrameBuffer *>(fb->priv);
    103     if (ext_fb == NULL) {
    104       EXPECT_TRUE(ext_fb != NULL);
    105       return -1;
    106     }
    107     EXPECT_EQ(1, ext_fb->in_use);
    108     ext_fb->in_use = 0;
    109     return 0;
    110   }
    111 
    112   // Checks that the ximage data is contained within the external frame buffer
    113   // private data passed back in the ximage.
    114   void CheckXImageFrameBuffer(const vpx_image_t *img) {
    115     if (img->fb_priv != NULL) {
    116       const struct ExternalFrameBuffer *const ext_fb =
    117           reinterpret_cast<ExternalFrameBuffer *>(img->fb_priv);
    118 
    119       ASSERT_TRUE(img->planes[0] >= ext_fb->data &&
    120                   img->planes[0] < (ext_fb->data + ext_fb->size));
    121     }
    122   }
    123 
    124  private:
    125   // Returns the index of the first free frame buffer. Returns |num_buffers_|
    126   // if there are no free frame buffers.
    127   int FindFreeBufferIndex() {
    128     int i;
    129     // Find a free frame buffer.
    130     for (i = 0; i < num_buffers_; ++i) {
    131       if (!ext_fb_list_[i].in_use) break;
    132     }
    133     return i;
    134   }
    135 
    136   // Sets |fb| to an external frame buffer. idx is the index into the frame
    137   // buffer list.
    138   void SetFrameBuffer(int idx, vpx_codec_frame_buffer_t *fb) {
    139     ASSERT_TRUE(fb != NULL);
    140     fb->data = ext_fb_list_[idx].data;
    141     fb->size = ext_fb_list_[idx].size;
    142     ASSERT_EQ(0, ext_fb_list_[idx].in_use);
    143     ext_fb_list_[idx].in_use = 1;
    144     fb->priv = &ext_fb_list_[idx];
    145   }
    146 
    147   int num_buffers_;
    148   ExternalFrameBuffer *ext_fb_list_;
    149 };
    150 
    151 #if CONFIG_WEBM_IO
    152 
    153 // Callback used by libvpx to request the application to return a frame
    154 // buffer of at least |min_size| in bytes.
    155 int get_vp9_frame_buffer(void *user_priv, size_t min_size,
    156                          vpx_codec_frame_buffer_t *fb) {
    157   ExternalFrameBufferList *const fb_list =
    158       reinterpret_cast<ExternalFrameBufferList *>(user_priv);
    159   return fb_list->GetFreeFrameBuffer(min_size, fb);
    160 }
    161 
    162 // Callback used by libvpx to tell the application that |fb| is not needed
    163 // anymore.
    164 int release_vp9_frame_buffer(void *user_priv, vpx_codec_frame_buffer_t *fb) {
    165   ExternalFrameBufferList *const fb_list =
    166       reinterpret_cast<ExternalFrameBufferList *>(user_priv);
    167   return fb_list->ReturnFrameBuffer(fb);
    168 }
    169 
    170 // Callback will not allocate data for frame buffer.
    171 int get_vp9_zero_frame_buffer(void *user_priv, size_t min_size,
    172                               vpx_codec_frame_buffer_t *fb) {
    173   ExternalFrameBufferList *const fb_list =
    174       reinterpret_cast<ExternalFrameBufferList *>(user_priv);
    175   return fb_list->GetZeroFrameBuffer(min_size, fb);
    176 }
    177 
    178 // Callback will allocate one less byte than |min_size|.
    179 int get_vp9_one_less_byte_frame_buffer(void *user_priv, size_t min_size,
    180                                        vpx_codec_frame_buffer_t *fb) {
    181   ExternalFrameBufferList *const fb_list =
    182       reinterpret_cast<ExternalFrameBufferList *>(user_priv);
    183   return fb_list->GetFreeFrameBuffer(min_size - 1, fb);
    184 }
    185 
    186 // Callback will not release the external frame buffer.
    187 int do_not_release_vp9_frame_buffer(void *user_priv,
    188                                     vpx_codec_frame_buffer_t *fb) {
    189   (void)user_priv;
    190   (void)fb;
    191   return 0;
    192 }
    193 
    194 #endif  // CONFIG_WEBM_IO
    195 
    196 // Class for testing passing in external frame buffers to libvpx.
    197 class ExternalFrameBufferMD5Test
    198     : public ::libvpx_test::DecoderTest,
    199       public ::libvpx_test::CodecTestWithParam<const char *> {
    200  protected:
    201   ExternalFrameBufferMD5Test()
    202       : DecoderTest(GET_PARAM(::libvpx_test::kCodecFactoryParam)),
    203         md5_file_(NULL), num_buffers_(0) {}
    204 
    205   virtual ~ExternalFrameBufferMD5Test() {
    206     if (md5_file_ != NULL) fclose(md5_file_);
    207   }
    208 
    209   virtual void PreDecodeFrameHook(
    210       const libvpx_test::CompressedVideoSource &video,
    211       libvpx_test::Decoder *decoder) {
    212     if (num_buffers_ > 0 && video.frame_number() == 0) {
    213       // Have libvpx use frame buffers we create.
    214       ASSERT_TRUE(fb_list_.CreateBufferList(num_buffers_));
    215       ASSERT_EQ(VPX_CODEC_OK,
    216                 decoder->SetFrameBufferFunctions(GetVP9FrameBuffer,
    217                                                  ReleaseVP9FrameBuffer, this));
    218     }
    219   }
    220 
    221   void OpenMD5File(const std::string &md5_file_name_) {
    222     md5_file_ = libvpx_test::OpenTestDataFile(md5_file_name_);
    223     ASSERT_TRUE(md5_file_ != NULL) << "Md5 file open failed. Filename: "
    224                                    << md5_file_name_;
    225   }
    226 
    227   virtual void DecompressedFrameHook(const vpx_image_t &img,
    228                                      const unsigned int frame_number) {
    229     ASSERT_TRUE(md5_file_ != NULL);
    230     char expected_md5[33];
    231     char junk[128];
    232 
    233     // Read correct md5 checksums.
    234     const int res = fscanf(md5_file_, "%s  %s", expected_md5, junk);
    235     ASSERT_NE(EOF, res) << "Read md5 data failed";
    236     expected_md5[32] = '\0';
    237 
    238     ::libvpx_test::MD5 md5_res;
    239     md5_res.Add(&img);
    240     const char *const actual_md5 = md5_res.Get();
    241 
    242     // Check md5 match.
    243     ASSERT_STREQ(expected_md5, actual_md5)
    244         << "Md5 checksums don't match: frame number = " << frame_number;
    245   }
    246 
    247   // Callback to get a free external frame buffer. Return value < 0 is an
    248   // error.
    249   static int GetVP9FrameBuffer(void *user_priv, size_t min_size,
    250                                vpx_codec_frame_buffer_t *fb) {
    251     ExternalFrameBufferMD5Test *const md5Test =
    252         reinterpret_cast<ExternalFrameBufferMD5Test *>(user_priv);
    253     return md5Test->fb_list_.GetFreeFrameBuffer(min_size, fb);
    254   }
    255 
    256   // Callback to release an external frame buffer. Return value < 0 is an
    257   // error.
    258   static int ReleaseVP9FrameBuffer(void *user_priv,
    259                                    vpx_codec_frame_buffer_t *fb) {
    260     ExternalFrameBufferMD5Test *const md5Test =
    261         reinterpret_cast<ExternalFrameBufferMD5Test *>(user_priv);
    262     return md5Test->fb_list_.ReturnFrameBuffer(fb);
    263   }
    264 
    265   void set_num_buffers(int num_buffers) { num_buffers_ = num_buffers; }
    266   int num_buffers() const { return num_buffers_; }
    267 
    268  private:
    269   FILE *md5_file_;
    270   int num_buffers_;
    271   ExternalFrameBufferList fb_list_;
    272 };
    273 
    274 #if CONFIG_WEBM_IO
    275 const char kVP9TestFile[] = "vp90-2-02-size-lf-1920x1080.webm";
    276 
    277 // Class for testing passing in external frame buffers to libvpx.
    278 class ExternalFrameBufferTest : public ::testing::Test {
    279  protected:
    280   ExternalFrameBufferTest() : video_(NULL), decoder_(NULL), num_buffers_(0) {}
    281 
    282   virtual void SetUp() {
    283     video_ = new libvpx_test::WebMVideoSource(kVP9TestFile);
    284     ASSERT_TRUE(video_ != NULL);
    285     video_->Init();
    286     video_->Begin();
    287 
    288     vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
    289     decoder_ = new libvpx_test::VP9Decoder(cfg, 0);
    290     ASSERT_TRUE(decoder_ != NULL);
    291   }
    292 
    293   virtual void TearDown() {
    294     delete decoder_;
    295     delete video_;
    296   }
    297 
    298   // Passes the external frame buffer information to libvpx.
    299   vpx_codec_err_t SetFrameBufferFunctions(
    300       int num_buffers, vpx_get_frame_buffer_cb_fn_t cb_get,
    301       vpx_release_frame_buffer_cb_fn_t cb_release) {
    302     if (num_buffers > 0) {
    303       num_buffers_ = num_buffers;
    304       EXPECT_TRUE(fb_list_.CreateBufferList(num_buffers_));
    305     }
    306 
    307     return decoder_->SetFrameBufferFunctions(cb_get, cb_release, &fb_list_);
    308   }
    309 
    310   vpx_codec_err_t DecodeOneFrame() {
    311     const vpx_codec_err_t res =
    312         decoder_->DecodeFrame(video_->cxdata(), video_->frame_size());
    313     CheckDecodedFrames();
    314     if (res == VPX_CODEC_OK) video_->Next();
    315     return res;
    316   }
    317 
    318   vpx_codec_err_t DecodeRemainingFrames() {
    319     for (; video_->cxdata() != NULL; video_->Next()) {
    320       const vpx_codec_err_t res =
    321           decoder_->DecodeFrame(video_->cxdata(), video_->frame_size());
    322       if (res != VPX_CODEC_OK) return res;
    323       CheckDecodedFrames();
    324     }
    325     return VPX_CODEC_OK;
    326   }
    327 
    328  private:
    329   void CheckDecodedFrames() {
    330     libvpx_test::DxDataIterator dec_iter = decoder_->GetDxData();
    331     const vpx_image_t *img = NULL;
    332 
    333     // Get decompressed data
    334     while ((img = dec_iter.Next()) != NULL) {
    335       fb_list_.CheckXImageFrameBuffer(img);
    336     }
    337   }
    338 
    339   libvpx_test::WebMVideoSource *video_;
    340   libvpx_test::VP9Decoder *decoder_;
    341   int num_buffers_;
    342   ExternalFrameBufferList fb_list_;
    343 };
    344 #endif  // CONFIG_WEBM_IO
    345 
    346 // This test runs through the set of test vectors, and decodes them.
    347 // Libvpx will call into the application to allocate a frame buffer when
    348 // needed. The md5 checksums are computed for each frame in the video file.
    349 // If md5 checksums match the correct md5 data, then the test is passed.
    350 // Otherwise, the test failed.
    351 TEST_P(ExternalFrameBufferMD5Test, ExtFBMD5Match) {
    352   const std::string filename = GET_PARAM(kVideoNameParam);
    353 
    354   // Number of buffers equals #VP9_MAXIMUM_REF_BUFFERS +
    355   // #VPX_MAXIMUM_WORK_BUFFERS + four jitter buffers.
    356   const int jitter_buffers = 4;
    357   const int num_buffers =
    358       VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS + jitter_buffers;
    359   set_num_buffers(num_buffers);
    360 
    361 #if CONFIG_VP8_DECODER
    362   // Tell compiler we are not using kVP8TestVectors.
    363   (void)libvpx_test::kVP8TestVectors;
    364 #endif
    365 
    366   // Open compressed video file.
    367   testing::internal::scoped_ptr<libvpx_test::CompressedVideoSource> video;
    368   if (filename.substr(filename.length() - 3, 3) == "ivf") {
    369     video.reset(new libvpx_test::IVFVideoSource(filename));
    370   } else {
    371 #if CONFIG_WEBM_IO
    372     video.reset(new libvpx_test::WebMVideoSource(filename));
    373 #else
    374     fprintf(stderr, "WebM IO is disabled, skipping test vector %s\n",
    375             filename.c_str());
    376     return;
    377 #endif
    378   }
    379   ASSERT_TRUE(video.get() != NULL);
    380   video->Init();
    381 
    382   // Construct md5 file name.
    383   const std::string md5_filename = filename + ".md5";
    384   OpenMD5File(md5_filename);
    385 
    386   // Decode frame, and check the md5 matching.
    387   ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
    388 }
    389 
    390 #if CONFIG_WEBM_IO
    391 TEST_F(ExternalFrameBufferTest, MinFrameBuffers) {
    392   // Minimum number of external frame buffers for VP9 is
    393   // #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS.
    394   const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
    395   ASSERT_EQ(VPX_CODEC_OK,
    396             SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer,
    397                                     release_vp9_frame_buffer));
    398   ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames());
    399 }
    400 
    401 TEST_F(ExternalFrameBufferTest, EightJitterBuffers) {
    402   // Number of buffers equals #VP9_MAXIMUM_REF_BUFFERS +
    403   // #VPX_MAXIMUM_WORK_BUFFERS + eight jitter buffers.
    404   const int jitter_buffers = 8;
    405   const int num_buffers =
    406       VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS + jitter_buffers;
    407   ASSERT_EQ(VPX_CODEC_OK,
    408             SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer,
    409                                     release_vp9_frame_buffer));
    410   ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames());
    411 }
    412 
    413 TEST_F(ExternalFrameBufferTest, NotEnoughBuffers) {
    414   // Minimum number of external frame buffers for VP9 is
    415   // #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS. Most files will
    416   // only use 5 frame buffers at one time.
    417   const int num_buffers = 2;
    418   ASSERT_EQ(VPX_CODEC_OK,
    419             SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer,
    420                                     release_vp9_frame_buffer));
    421   ASSERT_EQ(VPX_CODEC_OK, DecodeOneFrame());
    422   ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeRemainingFrames());
    423 }
    424 
    425 TEST_F(ExternalFrameBufferTest, NoRelease) {
    426   const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
    427   ASSERT_EQ(VPX_CODEC_OK,
    428             SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer,
    429                                     do_not_release_vp9_frame_buffer));
    430   ASSERT_EQ(VPX_CODEC_OK, DecodeOneFrame());
    431   ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeRemainingFrames());
    432 }
    433 
    434 TEST_F(ExternalFrameBufferTest, NullRealloc) {
    435   const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
    436   ASSERT_EQ(VPX_CODEC_OK,
    437             SetFrameBufferFunctions(num_buffers, get_vp9_zero_frame_buffer,
    438                                     release_vp9_frame_buffer));
    439   ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeOneFrame());
    440 }
    441 
    442 TEST_F(ExternalFrameBufferTest, ReallocOneLessByte) {
    443   const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
    444   ASSERT_EQ(VPX_CODEC_OK, SetFrameBufferFunctions(
    445                               num_buffers, get_vp9_one_less_byte_frame_buffer,
    446                               release_vp9_frame_buffer));
    447   ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeOneFrame());
    448 }
    449 
    450 TEST_F(ExternalFrameBufferTest, NullGetFunction) {
    451   const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
    452   ASSERT_EQ(
    453       VPX_CODEC_INVALID_PARAM,
    454       SetFrameBufferFunctions(num_buffers, NULL, release_vp9_frame_buffer));
    455 }
    456 
    457 TEST_F(ExternalFrameBufferTest, NullReleaseFunction) {
    458   const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
    459   ASSERT_EQ(VPX_CODEC_INVALID_PARAM,
    460             SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer, NULL));
    461 }
    462 
    463 TEST_F(ExternalFrameBufferTest, SetAfterDecode) {
    464   const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
    465   ASSERT_EQ(VPX_CODEC_OK, DecodeOneFrame());
    466   ASSERT_EQ(VPX_CODEC_ERROR,
    467             SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer,
    468                                     release_vp9_frame_buffer));
    469 }
    470 #endif  // CONFIG_WEBM_IO
    471 
    472 VP9_INSTANTIATE_TEST_CASE(
    473     ExternalFrameBufferMD5Test,
    474     ::testing::ValuesIn(libvpx_test::kVP9TestVectors,
    475                         libvpx_test::kVP9TestVectors +
    476                             libvpx_test::kNumVP9TestVectors));
    477 }  // namespace
    478