/* * Copyright (c) 2014 The WebM project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree.
*/
struct ExternalFrameBuffer {
uint8_t *data;
size_t size; int in_use;
};
// Class to manipulate a list of external frame buffers. class ExternalFrameBufferList { public:
ExternalFrameBufferList()
: num_buffers_(0), num_used_buffers_(0), ext_fb_list_(nullptr) {}
virtual ~ExternalFrameBufferList() { for (int i = 0; i < num_buffers_; ++i) { delete[] ext_fb_list_[i].data;
} delete[] ext_fb_list_;
}
// Creates the list to hold the external buffers. Returns true on success. bool CreateBufferList(int num_buffers) { if (num_buffers < 0) returnfalse;
// Searches the frame buffer list for a free frame buffer. Makes sure // that the frame buffer is at least |min_size| in bytes. Marks that the // frame buffer is in use by libvpx. Finally sets |fb| to point to the // external frame buffer. Returns < 0 on an error. int GetFreeFrameBuffer(size_t min_size, vpx_codec_frame_buffer_t *fb) {
EXPECT_NE(fb, nullptr); constint idx = FindFreeBufferIndex(); if (idx == num_buffers_) return -1;
if (ext_fb_list_[idx].size < min_size) { delete[] ext_fb_list_[idx].data;
ext_fb_list_[idx].data = new uint8_t[min_size];
memset(ext_fb_list_[idx].data, 0, min_size);
ext_fb_list_[idx].size = min_size;
}
SetFrameBuffer(idx, fb);
num_used_buffers_++; return 0;
}
// Test function that will not allocate any data for the frame buffer. // Returns < 0 on an error. int GetZeroFrameBuffer(size_t min_size, vpx_codec_frame_buffer_t *fb) {
EXPECT_NE(fb, nullptr); constint idx = FindFreeBufferIndex(); if (idx == num_buffers_) return -1;
// Marks the external frame buffer that |fb| is pointing to as free. // Returns < 0 on an error. int ReturnFrameBuffer(vpx_codec_frame_buffer_t *fb) { if (fb == nullptr) {
EXPECT_NE(fb, nullptr); return -1;
}
ExternalFrameBuffer *const ext_fb = reinterpret_cast<ExternalFrameBuffer *>(fb->priv); if (ext_fb == nullptr) {
EXPECT_NE(ext_fb, nullptr); return -1;
}
EXPECT_EQ(1, ext_fb->in_use);
ext_fb->in_use = 0;
num_used_buffers_--; return 0;
}
// Checks that the vpx_image_t data is contained within the external frame // buffer private data passed back in the vpx_image_t. void CheckImageFrameBuffer(const vpx_image_t *img) { if (img->fb_priv != nullptr) { conststruct ExternalFrameBuffer *const ext_fb = reinterpret_cast<ExternalFrameBuffer *>(img->fb_priv);
int num_used_buffers() const { return num_used_buffers_; }
private: // Returns the index of the first free frame buffer. Returns |num_buffers_| // if there are no free frame buffers. int FindFreeBufferIndex() { int i; // Find a free frame buffer. for (i = 0; i < num_buffers_; ++i) { if (!ext_fb_list_[i].in_use) break;
} return i;
}
// Sets |fb| to an external frame buffer. idx is the index into the frame // buffer list. void SetFrameBuffer(int idx, vpx_codec_frame_buffer_t *fb) {
ASSERT_NE(fb, nullptr);
fb->data = ext_fb_list_[idx].data;
fb->size = ext_fb_list_[idx].size;
ASSERT_EQ(0, ext_fb_list_[idx].in_use);
ext_fb_list_[idx].in_use = 1;
fb->priv = &ext_fb_list_[idx];
}
int num_buffers_; int num_used_buffers_;
ExternalFrameBuffer *ext_fb_list_;
};
#if CONFIG_WEBM_IO
// Callback used by libvpx to request the application to return a frame // buffer of at least |min_size| in bytes. int get_vp9_frame_buffer(void *user_priv, size_t min_size,
vpx_codec_frame_buffer_t *fb) {
ExternalFrameBufferList *const fb_list = reinterpret_cast<ExternalFrameBufferList *>(user_priv); return fb_list->GetFreeFrameBuffer(min_size, fb);
}
// Callback used by libvpx to tell the application that |fb| is not needed // anymore. int release_vp9_frame_buffer(void *user_priv, vpx_codec_frame_buffer_t *fb) {
ExternalFrameBufferList *const fb_list = reinterpret_cast<ExternalFrameBufferList *>(user_priv); return fb_list->ReturnFrameBuffer(fb);
}
// Callback will not allocate data for frame buffer. int get_vp9_zero_frame_buffer(void *user_priv, size_t min_size,
vpx_codec_frame_buffer_t *fb) {
ExternalFrameBufferList *const fb_list = reinterpret_cast<ExternalFrameBufferList *>(user_priv); return fb_list->GetZeroFrameBuffer(min_size, fb);
}
// Callback will allocate one less byte than |min_size|. int get_vp9_one_less_byte_frame_buffer(void *user_priv, size_t min_size,
vpx_codec_frame_buffer_t *fb) {
ExternalFrameBufferList *const fb_list = reinterpret_cast<ExternalFrameBufferList *>(user_priv); return fb_list->GetFreeFrameBuffer(min_size - 1, fb);
}
// Callback will not release the external frame buffer. int do_not_release_vp9_frame_buffer(void *user_priv,
vpx_codec_frame_buffer_t *fb) {
(void)user_priv;
(void)fb; return 0;
}
#endif// CONFIG_WEBM_IO
// Class for testing passing in external frame buffers to libvpx. class ExternalFrameBufferMD5Test
: public ::libvpx_test::DecoderTest, public ::libvpx_test::CodecTestWithParam<constchar *> { protected:
ExternalFrameBufferMD5Test()
: DecoderTest(GET_PARAM(::libvpx_test::kCodecFactoryParam)),
md5_file_(nullptr), num_buffers_(0) {}
~ExternalFrameBufferMD5Test() override { if (md5_file_ != nullptr) fclose(md5_file_);
}
void PreDecodeFrameHook(const libvpx_test::CompressedVideoSource &video,
libvpx_test::Decoder *decoder) override { if (num_buffers_ > 0 && video.frame_number() == 0) { // Have libvpx use frame buffers we create.
ASSERT_TRUE(fb_list_.CreateBufferList(num_buffers_));
ASSERT_EQ(VPX_CODEC_OK,
decoder->SetFrameBufferFunctions(GetVP9FrameBuffer,
ReleaseVP9FrameBuffer, this));
}
}
// Class for testing passing in external frame buffers to libvpx. class ExternalFrameBufferTest : public ::testing::Test { protected:
ExternalFrameBufferTest()
: video_(nullptr), decoder_(nullptr), num_buffers_(0) {}
// This test runs through the set of test vectors, and decodes them. // Libvpx will call into the application to allocate a frame buffer when // needed. The md5 checksums are computed for each frame in the video file. // If md5 checksums match the correct md5 data, then the test is passed. // Otherwise, the test failed.
TEST_P(ExternalFrameBufferMD5Test, ExtFBMD5Match) { const std::string filename = GET_PARAM(kVideoNameParam);
// Number of buffers equals #VP9_MAXIMUM_REF_BUFFERS + // #VPX_MAXIMUM_WORK_BUFFERS + four jitter buffers. constint jitter_buffers = 4; constint num_buffers =
VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS + jitter_buffers;
set_num_buffers(num_buffers);
#if CONFIG_VP8_DECODER // Tell compiler we are not using kVP8TestVectors.
(void)libvpx_test::kVP8TestVectors; #endif
// Open compressed video file.
std::unique_ptr<libvpx_test::CompressedVideoSource> video; if (filename.substr(filename.length() - 3, 3) == "ivf") {
video.reset(new libvpx_test::IVFVideoSource(filename));
} else { #if CONFIG_WEBM_IO
video.reset(new libvpx_test::WebMVideoSource(filename)); #else
fprintf(stderr, "WebM IO is disabled, skipping test vector %s\n",
filename.c_str()); return; #endif
}
ASSERT_NE(video.get(), nullptr);
video->Init();
TEST_F(ExternalFrameBufferTest, NotEnoughBuffers) { // Minimum number of external frame buffers for VP9 is // #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS. Most files will // only use 5 frame buffers at one time. constint num_buffers = 2;
ASSERT_EQ(VPX_CODEC_OK,
SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer,
release_vp9_frame_buffer));
ASSERT_EQ(VPX_CODEC_OK, DecodeOneFrame()); // Only run this on long clips. Decoding a very short clip will return // VPX_CODEC_OK even with only 2 buffers.
ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeRemainingFrames());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.