external_frame_buffer_test.cc 16 KB

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