2 * Copyright (c) 2014 The WebM project authors. All Rights Reserved.
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.
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"
21 #include "test/webm_video_source.h"
26 const int kVideoNameParam
= 1;
27 const char kVP9TestFile
[] = "vp90-2-02-size-lf-1920x1080.webm";
29 struct ExternalFrameBuffer
{
35 // Class to manipulate a list of external frame buffers.
36 class ExternalFrameBufferList
{
38 ExternalFrameBufferList()
42 virtual ~ExternalFrameBufferList() {
43 for (int i
= 0; i
< num_buffers_
; ++i
) {
44 delete [] ext_fb_list_
[i
].data
;
46 delete [] ext_fb_list_
;
49 // Creates the list to hold the external buffers. Returns true on success.
50 bool CreateBufferList(int num_buffers
) {
54 num_buffers_
= num_buffers
;
55 ext_fb_list_
= new ExternalFrameBuffer
[num_buffers_
];
56 EXPECT_TRUE(ext_fb_list_
!= NULL
);
57 memset(ext_fb_list_
, 0, sizeof(ext_fb_list_
[0]) * num_buffers_
);
61 // Searches the frame buffer list for a free frame buffer. Makes sure
62 // that the frame buffer is at least |min_size| in bytes. Marks that the
63 // frame buffer is in use by libvpx. Finally sets |fb| to point to the
64 // external frame buffer. Returns < 0 on an error.
65 int GetFreeFrameBuffer(size_t min_size
, vpx_codec_frame_buffer_t
*fb
) {
66 EXPECT_TRUE(fb
!= NULL
);
67 const int idx
= FindFreeBufferIndex();
68 if (idx
== num_buffers_
)
71 if (ext_fb_list_
[idx
].size
< min_size
) {
72 delete [] ext_fb_list_
[idx
].data
;
73 ext_fb_list_
[idx
].data
= new uint8_t[min_size
];
74 ext_fb_list_
[idx
].size
= min_size
;
77 SetFrameBuffer(idx
, fb
);
81 // Test function that will not allocate any data for the frame buffer.
82 // Returns < 0 on an error.
83 int GetZeroFrameBuffer(size_t min_size
, vpx_codec_frame_buffer_t
*fb
) {
84 EXPECT_TRUE(fb
!= NULL
);
85 const int idx
= FindFreeBufferIndex();
86 if (idx
== num_buffers_
)
89 if (ext_fb_list_
[idx
].size
< min_size
) {
90 delete [] ext_fb_list_
[idx
].data
;
91 ext_fb_list_
[idx
].data
= NULL
;
92 ext_fb_list_
[idx
].size
= min_size
;
95 SetFrameBuffer(idx
, fb
);
99 // Marks the external frame buffer that |fb| is pointing too as free.
100 // Returns < 0 on an error.
101 int ReturnFrameBuffer(vpx_codec_frame_buffer_t
*fb
) {
102 EXPECT_TRUE(fb
!= NULL
);
103 ExternalFrameBuffer
*const ext_fb
=
104 reinterpret_cast<ExternalFrameBuffer
*>(fb
->priv
);
105 EXPECT_TRUE(ext_fb
!= NULL
);
106 EXPECT_EQ(1, ext_fb
->in_use
);
111 // Checks that the ximage data is contained within the external frame buffer
112 // private data passed back in the ximage.
113 void CheckXImageFrameBuffer(const vpx_image_t
*img
) {
114 if (img
->fb_priv
!= NULL
) {
115 const struct ExternalFrameBuffer
*const ext_fb
=
116 reinterpret_cast<ExternalFrameBuffer
*>(img
->fb_priv
);
118 ASSERT_TRUE(img
->planes
[0] >= ext_fb
->data
&&
119 img
->planes
[0] < (ext_fb
->data
+ ext_fb
->size
));
124 // Returns the index of the first free frame buffer. Returns |num_buffers_|
125 // if there are no free frame buffers.
126 int FindFreeBufferIndex() {
128 // Find a free frame buffer.
129 for (i
= 0; i
< num_buffers_
; ++i
) {
130 if (!ext_fb_list_
[i
].in_use
)
136 // Sets |fb| to an external frame buffer. idx is the index into the frame
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
];
148 ExternalFrameBuffer
*ext_fb_list_
;
151 // Callback used by libvpx to request the application to return a frame
152 // buffer of at least |min_size| in bytes.
153 int get_vp9_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
, fb
);
160 // Callback used by libvpx to tell the application that |fb| is not needed
162 int release_vp9_frame_buffer(void *user_priv
,
163 vpx_codec_frame_buffer_t
*fb
) {
164 ExternalFrameBufferList
*const fb_list
=
165 reinterpret_cast<ExternalFrameBufferList
*>(user_priv
);
166 return fb_list
->ReturnFrameBuffer(fb
);
169 // Callback will not allocate data for frame buffer.
170 int get_vp9_zero_frame_buffer(void *user_priv
, size_t min_size
,
171 vpx_codec_frame_buffer_t
*fb
) {
172 ExternalFrameBufferList
*const fb_list
=
173 reinterpret_cast<ExternalFrameBufferList
*>(user_priv
);
174 return fb_list
->GetZeroFrameBuffer(min_size
, fb
);
177 // Callback will allocate one less byte than |min_size|.
178 int get_vp9_one_less_byte_frame_buffer(void *user_priv
, size_t min_size
,
179 vpx_codec_frame_buffer_t
*fb
) {
180 ExternalFrameBufferList
*const fb_list
=
181 reinterpret_cast<ExternalFrameBufferList
*>(user_priv
);
182 return fb_list
->GetFreeFrameBuffer(min_size
- 1, fb
);
185 // Callback will not release the external frame buffer.
186 int do_not_release_vp9_frame_buffer(void *user_priv
,
187 vpx_codec_frame_buffer_t
*fb
) {
193 // Class for testing passing in external frame buffers to libvpx.
194 class ExternalFrameBufferMD5Test
195 : public ::libvpx_test::DecoderTest
,
196 public ::libvpx_test::CodecTestWithParam
<const char*> {
198 ExternalFrameBufferMD5Test()
199 : DecoderTest(GET_PARAM(::libvpx_test::kCodecFactoryParam
)),
203 virtual ~ExternalFrameBufferMD5Test() {
204 if (md5_file_
!= NULL
)
208 virtual void PreDecodeFrameHook(
209 const libvpx_test::CompressedVideoSource
&video
,
210 libvpx_test::Decoder
*decoder
) {
211 if (num_buffers_
> 0 && video
.frame_number() == 0) {
212 // Have libvpx use frame buffers we create.
213 ASSERT_TRUE(fb_list_
.CreateBufferList(num_buffers_
));
214 ASSERT_EQ(VPX_CODEC_OK
,
215 decoder
->SetFrameBufferFunctions(
216 GetVP9FrameBuffer
, ReleaseVP9FrameBuffer
, this));
220 void OpenMD5File(const std::string
&md5_file_name_
) {
221 md5_file_
= libvpx_test::OpenTestDataFile(md5_file_name_
);
222 ASSERT_TRUE(md5_file_
!= NULL
) << "Md5 file open failed. Filename: "
226 virtual void DecompressedFrameHook(const vpx_image_t
&img
,
227 const unsigned int frame_number
) {
228 ASSERT_TRUE(md5_file_
!= NULL
);
229 char expected_md5
[33];
232 // Read correct md5 checksums.
233 const int res
= fscanf(md5_file_
, "%s %s", expected_md5
, junk
);
234 ASSERT_NE(EOF
, res
) << "Read md5 data failed";
235 expected_md5
[32] = '\0';
237 ::libvpx_test::MD5 md5_res
;
239 const char *const actual_md5
= md5_res
.Get();
242 ASSERT_STREQ(expected_md5
, actual_md5
)
243 << "Md5 checksums don't match: frame number = " << frame_number
;
246 // Callback to get a free external frame buffer. Return value < 0 is an
248 static int GetVP9FrameBuffer(void *user_priv
, size_t min_size
,
249 vpx_codec_frame_buffer_t
*fb
) {
250 ExternalFrameBufferMD5Test
*const md5Test
=
251 reinterpret_cast<ExternalFrameBufferMD5Test
*>(user_priv
);
252 return md5Test
->fb_list_
.GetFreeFrameBuffer(min_size
, fb
);
255 // Callback to release an external frame buffer. Return value < 0 is an
257 static int ReleaseVP9FrameBuffer(void *user_priv
,
258 vpx_codec_frame_buffer_t
*fb
) {
259 ExternalFrameBufferMD5Test
*const md5Test
=
260 reinterpret_cast<ExternalFrameBufferMD5Test
*>(user_priv
);
261 return md5Test
->fb_list_
.ReturnFrameBuffer(fb
);
264 void set_num_buffers(int num_buffers
) { num_buffers_
= num_buffers
; }
265 int num_buffers() const { return num_buffers_
; }
270 ExternalFrameBufferList fb_list_
;
274 // Class for testing passing in external frame buffers to libvpx.
275 class ExternalFrameBufferTest
: public ::testing::Test
{
277 ExternalFrameBufferTest()
282 virtual void SetUp() {
283 video_
= new libvpx_test::WebMVideoSource(kVP9TestFile
);
284 ASSERT_TRUE(video_
!= NULL
);
288 vpx_codec_dec_cfg_t cfg
= {0};
289 decoder_
= new libvpx_test::VP9Decoder(cfg
, 0);
290 ASSERT_TRUE(decoder_
!= NULL
);
293 virtual void TearDown() {
298 // Passes the external frame buffer information to libvpx.
299 vpx_codec_err_t
SetFrameBufferFunctions(
301 vpx_get_frame_buffer_cb_fn_t cb_get
,
302 vpx_release_frame_buffer_cb_fn_t cb_release
) {
303 if (num_buffers
> 0) {
304 num_buffers_
= num_buffers
;
305 EXPECT_TRUE(fb_list_
.CreateBufferList(num_buffers_
));
308 return decoder_
->SetFrameBufferFunctions(cb_get
, cb_release
, &fb_list_
);
311 vpx_codec_err_t
DecodeOneFrame() {
312 const vpx_codec_err_t res
=
313 decoder_
->DecodeFrame(video_
->cxdata(), video_
->frame_size());
314 CheckDecodedFrames();
315 if (res
== VPX_CODEC_OK
)
320 vpx_codec_err_t
DecodeRemainingFrames() {
321 for (; video_
->cxdata() != NULL
; video_
->Next()) {
322 const vpx_codec_err_t res
=
323 decoder_
->DecodeFrame(video_
->cxdata(), video_
->frame_size());
324 if (res
!= VPX_CODEC_OK
)
326 CheckDecodedFrames();
332 void CheckDecodedFrames() {
333 libvpx_test::DxDataIterator dec_iter
= decoder_
->GetDxData();
334 const vpx_image_t
*img
= NULL
;
336 // Get decompressed data
337 while ((img
= dec_iter
.Next()) != NULL
) {
338 fb_list_
.CheckXImageFrameBuffer(img
);
342 libvpx_test::WebMVideoSource
*video_
;
343 libvpx_test::VP9Decoder
*decoder_
;
345 ExternalFrameBufferList fb_list_
;
347 #endif // CONFIG_WEBM_IO
349 // This test runs through the set of test vectors, and decodes them.
350 // Libvpx will call into the application to allocate a frame buffer when
351 // needed. The md5 checksums are computed for each frame in the video file.
352 // If md5 checksums match the correct md5 data, then the test is passed.
353 // Otherwise, the test failed.
354 TEST_P(ExternalFrameBufferMD5Test
, ExtFBMD5Match
) {
355 const std::string filename
= GET_PARAM(kVideoNameParam
);
356 libvpx_test::CompressedVideoSource
*video
= NULL
;
358 // Number of buffers equals #VP9_MAXIMUM_REF_BUFFERS +
359 // #VPX_MAXIMUM_WORK_BUFFERS + four jitter buffers.
360 const int jitter_buffers
= 4;
361 const int num_buffers
=
362 VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
+ jitter_buffers
;
363 set_num_buffers(num_buffers
);
365 #if CONFIG_VP8_DECODER
366 // Tell compiler we are not using kVP8TestVectors.
367 (void)libvpx_test::kVP8TestVectors
;
370 // Open compressed video file.
371 if (filename
.substr(filename
.length() - 3, 3) == "ivf") {
372 video
= new libvpx_test::IVFVideoSource(filename
);
375 video
= new libvpx_test::WebMVideoSource(filename
);
377 fprintf(stderr
, "WebM IO is disabled, skipping test vector %s\n",
382 ASSERT_TRUE(video
!= NULL
);
385 // Construct md5 file name.
386 const std::string md5_filename
= filename
+ ".md5";
387 OpenMD5File(md5_filename
);
389 // Decode frame, and check the md5 matching.
390 ASSERT_NO_FATAL_FAILURE(RunLoop(video
));
395 TEST_F(ExternalFrameBufferTest
, MinFrameBuffers
) {
396 // Minimum number of external frame buffers for VP9 is
397 // #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS.
398 const int num_buffers
= VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
;
399 ASSERT_EQ(VPX_CODEC_OK
,
400 SetFrameBufferFunctions(
401 num_buffers
, get_vp9_frame_buffer
, release_vp9_frame_buffer
));
402 ASSERT_EQ(VPX_CODEC_OK
, DecodeRemainingFrames());
405 TEST_F(ExternalFrameBufferTest
, EightJitterBuffers
) {
406 // Number of buffers equals #VP9_MAXIMUM_REF_BUFFERS +
407 // #VPX_MAXIMUM_WORK_BUFFERS + eight jitter buffers.
408 const int jitter_buffers
= 8;
409 const int num_buffers
=
410 VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
+ jitter_buffers
;
411 ASSERT_EQ(VPX_CODEC_OK
,
412 SetFrameBufferFunctions(
413 num_buffers
, get_vp9_frame_buffer
, release_vp9_frame_buffer
));
414 ASSERT_EQ(VPX_CODEC_OK
, DecodeRemainingFrames());
417 TEST_F(ExternalFrameBufferTest
, NotEnoughBuffers
) {
418 // Minimum number of external frame buffers for VP9 is
419 // #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS. Most files will
420 // only use 5 frame buffers at one time.
421 const int num_buffers
= 2;
422 ASSERT_EQ(VPX_CODEC_OK
,
423 SetFrameBufferFunctions(
424 num_buffers
, get_vp9_frame_buffer
, release_vp9_frame_buffer
));
425 ASSERT_EQ(VPX_CODEC_OK
, DecodeOneFrame());
426 ASSERT_EQ(VPX_CODEC_MEM_ERROR
, DecodeRemainingFrames());
429 TEST_F(ExternalFrameBufferTest
, NoRelease
) {
430 const int num_buffers
= VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
;
431 ASSERT_EQ(VPX_CODEC_OK
,
432 SetFrameBufferFunctions(num_buffers
, get_vp9_frame_buffer
,
433 do_not_release_vp9_frame_buffer
));
434 ASSERT_EQ(VPX_CODEC_OK
, DecodeOneFrame());
435 ASSERT_EQ(VPX_CODEC_MEM_ERROR
, DecodeRemainingFrames());
438 TEST_F(ExternalFrameBufferTest
, NullRealloc
) {
439 const int num_buffers
= VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
;
440 ASSERT_EQ(VPX_CODEC_OK
,
441 SetFrameBufferFunctions(num_buffers
, get_vp9_zero_frame_buffer
,
442 release_vp9_frame_buffer
));
443 ASSERT_EQ(VPX_CODEC_MEM_ERROR
, DecodeOneFrame());
446 TEST_F(ExternalFrameBufferTest
, ReallocOneLessByte
) {
447 const int num_buffers
= VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
;
448 ASSERT_EQ(VPX_CODEC_OK
,
449 SetFrameBufferFunctions(
450 num_buffers
, get_vp9_one_less_byte_frame_buffer
,
451 release_vp9_frame_buffer
));
452 ASSERT_EQ(VPX_CODEC_MEM_ERROR
, DecodeOneFrame());
455 TEST_F(ExternalFrameBufferTest
, NullGetFunction
) {
456 const int num_buffers
= VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
;
457 ASSERT_EQ(VPX_CODEC_INVALID_PARAM
,
458 SetFrameBufferFunctions(num_buffers
, NULL
,
459 release_vp9_frame_buffer
));
462 TEST_F(ExternalFrameBufferTest
, NullReleaseFunction
) {
463 const int num_buffers
= VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
;
464 ASSERT_EQ(VPX_CODEC_INVALID_PARAM
,
465 SetFrameBufferFunctions(num_buffers
, get_vp9_frame_buffer
, NULL
));
468 TEST_F(ExternalFrameBufferTest
, SetAfterDecode
) {
469 const int num_buffers
= VP9_MAXIMUM_REF_BUFFERS
+ VPX_MAXIMUM_WORK_BUFFERS
;
470 ASSERT_EQ(VPX_CODEC_OK
, DecodeOneFrame());
471 ASSERT_EQ(VPX_CODEC_ERROR
,
472 SetFrameBufferFunctions(
473 num_buffers
, get_vp9_frame_buffer
, release_vp9_frame_buffer
));
475 #endif // CONFIG_WEBM_IO
477 VP9_INSTANTIATE_TEST_CASE(ExternalFrameBufferMD5Test
,
478 ::testing::ValuesIn(libvpx_test::kVP9TestVectors
,
479 libvpx_test::kVP9TestVectors
+
480 libvpx_test::kNumVP9TestVectors
));