Merge "Add VP9 decoder support for external frame buffers"
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
#include "test/encode_test_driver.h"
|
||||
namespace libvpx_test {
|
||||
|
||||
const int kCodecFactoryParam = 0;
|
||||
|
||||
class CodecFactory {
|
||||
public:
|
||||
CodecFactory() {}
|
||||
|
@@ -76,6 +76,15 @@ class Decoder {
|
||||
return detail ? detail : vpx_codec_error(&decoder_);
|
||||
}
|
||||
|
||||
// Passes the external frame buffer information to libvpx.
|
||||
vpx_codec_err_t SetFrameBufferFunctions(
|
||||
vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release, void *user_priv) {
|
||||
InitOnce();
|
||||
return vpx_codec_set_frame_buffer_functions(
|
||||
&decoder_, cb_get, cb_release, user_priv);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual vpx_codec_iface_t* CodecInterface() const = 0;
|
||||
|
||||
|
466
test/external_frame_buffer_test.cc
Normal file
466
test/external_frame_buffer_test.cc
Normal file
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/ivf_video_source.h"
|
||||
#include "test/md5_helper.h"
|
||||
#include "test/test_vectors.h"
|
||||
#include "test/util.h"
|
||||
#include "test/webm_video_source.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int kVideoNameParam = 1;
|
||||
const char kVP9TestFile[] = "vp90-2-02-size-lf-1920x1080.webm";
|
||||
|
||||
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),
|
||||
ext_fb_list_(NULL) {}
|
||||
|
||||
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)
|
||||
return false;
|
||||
|
||||
num_buffers_ = num_buffers;
|
||||
ext_fb_list_ = new ExternalFrameBuffer[num_buffers_];
|
||||
EXPECT_TRUE(ext_fb_list_ != NULL);
|
||||
memset(ext_fb_list_, 0, sizeof(ext_fb_list_[0]) * num_buffers_);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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_TRUE(fb != NULL);
|
||||
const int 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];
|
||||
ext_fb_list_[idx].size = min_size;
|
||||
}
|
||||
|
||||
SetFrameBuffer(idx, fb);
|
||||
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_TRUE(fb != NULL);
|
||||
const int 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 = NULL;
|
||||
ext_fb_list_[idx].size = min_size;
|
||||
}
|
||||
|
||||
SetFrameBuffer(idx, fb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Marks the external frame buffer that |fb| is pointing too as free.
|
||||
// Returns < 0 on an error.
|
||||
int ReturnFrameBuffer(vpx_codec_frame_buffer_t *fb) {
|
||||
EXPECT_TRUE(fb != NULL);
|
||||
ExternalFrameBuffer *const ext_fb =
|
||||
reinterpret_cast<ExternalFrameBuffer*>(fb->priv);
|
||||
EXPECT_TRUE(ext_fb != NULL);
|
||||
EXPECT_EQ(1, ext_fb->in_use);
|
||||
ext_fb->in_use = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Checks that the ximage data is contained within the external frame buffer
|
||||
// private data passed back in the ximage.
|
||||
void CheckXImageFrameBuffer(const vpx_image_t *img) {
|
||||
if (img->fb_priv != NULL) {
|
||||
const struct ExternalFrameBuffer *const ext_fb =
|
||||
reinterpret_cast<ExternalFrameBuffer*>(img->fb_priv);
|
||||
|
||||
ASSERT_TRUE(img->planes[0] >= ext_fb->data &&
|
||||
img->planes[0] < (ext_fb->data + ext_fb->size));
|
||||
}
|
||||
}
|
||||
|
||||
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_TRUE(fb != NULL);
|
||||
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_;
|
||||
ExternalFrameBuffer *ext_fb_list_;
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Class for testing passing in external frame buffers to libvpx.
|
||||
class ExternalFrameBufferMD5Test
|
||||
: public ::libvpx_test::DecoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<const char*> {
|
||||
protected:
|
||||
ExternalFrameBufferMD5Test()
|
||||
: DecoderTest(GET_PARAM(::libvpx_test::kCodecFactoryParam)),
|
||||
md5_file_(NULL),
|
||||
num_buffers_(0) {}
|
||||
|
||||
virtual ~ExternalFrameBufferMD5Test() {
|
||||
if (md5_file_ != NULL)
|
||||
fclose(md5_file_);
|
||||
}
|
||||
|
||||
virtual void PreDecodeFrameHook(
|
||||
const libvpx_test::CompressedVideoSource &video,
|
||||
libvpx_test::Decoder *decoder) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void OpenMD5File(const std::string &md5_file_name_) {
|
||||
md5_file_ = libvpx_test::OpenTestDataFile(md5_file_name_);
|
||||
ASSERT_TRUE(md5_file_ != NULL) << "Md5 file open failed. Filename: "
|
||||
<< md5_file_name_;
|
||||
}
|
||||
|
||||
virtual void DecompressedFrameHook(const vpx_image_t &img,
|
||||
const unsigned int frame_number) {
|
||||
ASSERT_TRUE(md5_file_ != NULL);
|
||||
char expected_md5[33];
|
||||
char junk[128];
|
||||
|
||||
// Read correct md5 checksums.
|
||||
const int res = fscanf(md5_file_, "%s %s", expected_md5, junk);
|
||||
ASSERT_NE(EOF, res) << "Read md5 data failed";
|
||||
expected_md5[32] = '\0';
|
||||
|
||||
::libvpx_test::MD5 md5_res;
|
||||
md5_res.Add(&img);
|
||||
const char *const actual_md5 = md5_res.Get();
|
||||
|
||||
// Check md5 match.
|
||||
ASSERT_STREQ(expected_md5, actual_md5)
|
||||
<< "Md5 checksums don't match: frame number = " << frame_number;
|
||||
}
|
||||
|
||||
// Callback to get a free external frame buffer. Return value < 0 is an
|
||||
// error.
|
||||
static int GetVp9FrameBuffer(void *user_priv, size_t min_size,
|
||||
vpx_codec_frame_buffer_t *fb) {
|
||||
ExternalFrameBufferMD5Test *const md5Test =
|
||||
reinterpret_cast<ExternalFrameBufferMD5Test*>(user_priv);
|
||||
return md5Test->fb_list_.GetFreeFrameBuffer(min_size, fb);
|
||||
}
|
||||
|
||||
// Callback to release an external frame buffer. Return value < 0 is an
|
||||
// error.
|
||||
static int ReleaseVP9FrameBuffer(void *user_priv,
|
||||
vpx_codec_frame_buffer_t *fb) {
|
||||
ExternalFrameBufferMD5Test *const md5Test =
|
||||
reinterpret_cast<ExternalFrameBufferMD5Test*>(user_priv);
|
||||
return md5Test->fb_list_.ReturnFrameBuffer(fb);
|
||||
}
|
||||
|
||||
void set_num_buffers(int num_buffers) { num_buffers_ = num_buffers; }
|
||||
int num_buffers() const { return num_buffers_; }
|
||||
|
||||
private:
|
||||
FILE *md5_file_;
|
||||
int num_buffers_;
|
||||
ExternalFrameBufferList fb_list_;
|
||||
};
|
||||
|
||||
// Class for testing passing in external frame buffers to libvpx.
|
||||
class ExternalFrameBufferTest : public ::testing::Test {
|
||||
protected:
|
||||
ExternalFrameBufferTest()
|
||||
: video_(NULL),
|
||||
decoder_(NULL),
|
||||
num_buffers_(0) {}
|
||||
|
||||
virtual void SetUp() {
|
||||
video_ = new libvpx_test::WebMVideoSource(kVP9TestFile);
|
||||
ASSERT_TRUE(video_ != NULL);
|
||||
video_->Init();
|
||||
video_->Begin();
|
||||
|
||||
vpx_codec_dec_cfg_t cfg = {0};
|
||||
decoder_ = new libvpx_test::VP9Decoder(cfg, 0);
|
||||
ASSERT_TRUE(decoder_ != NULL);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete decoder_;
|
||||
delete video_;
|
||||
}
|
||||
|
||||
// Passes the external frame buffer information to libvpx.
|
||||
vpx_codec_err_t SetFrameBufferFunctions(
|
||||
int num_buffers,
|
||||
vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release) {
|
||||
if (num_buffers > 0) {
|
||||
num_buffers_ = num_buffers;
|
||||
EXPECT_TRUE(fb_list_.CreateBufferList(num_buffers_));
|
||||
}
|
||||
|
||||
return decoder_->SetFrameBufferFunctions(cb_get, cb_release, &fb_list_);
|
||||
}
|
||||
|
||||
vpx_codec_err_t DecodeOneFrame() {
|
||||
const vpx_codec_err_t res =
|
||||
decoder_->DecodeFrame(video_->cxdata(), video_->frame_size());
|
||||
CheckDecodedFrames();
|
||||
if (res == VPX_CODEC_OK)
|
||||
video_->Next();
|
||||
return res;
|
||||
}
|
||||
|
||||
vpx_codec_err_t DecodeRemainingFrames() {
|
||||
for (; video_->cxdata() != NULL; video_->Next()) {
|
||||
const vpx_codec_err_t res =
|
||||
decoder_->DecodeFrame(video_->cxdata(), video_->frame_size());
|
||||
if (res != VPX_CODEC_OK)
|
||||
return res;
|
||||
CheckDecodedFrames();
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void CheckDecodedFrames() {
|
||||
libvpx_test::DxDataIterator dec_iter = decoder_->GetDxData();
|
||||
const vpx_image_t *img = NULL;
|
||||
|
||||
// Get decompressed data
|
||||
while ((img = dec_iter.Next()) != NULL) {
|
||||
fb_list_.CheckXImageFrameBuffer(img);
|
||||
}
|
||||
}
|
||||
|
||||
libvpx_test::WebMVideoSource *video_;
|
||||
libvpx_test::VP9Decoder *decoder_;
|
||||
int num_buffers_;
|
||||
ExternalFrameBufferList fb_list_;
|
||||
};
|
||||
|
||||
// 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);
|
||||
libvpx_test::CompressedVideoSource *video = NULL;
|
||||
|
||||
// Number of buffers equals #VP9_MAXIMUM_REF_BUFFERS +
|
||||
// #VPX_MAXIMUM_WORK_BUFFERS + four jitter buffers.
|
||||
const int jitter_buffers = 4;
|
||||
const int 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.
|
||||
if (filename.substr(filename.length() - 3, 3) == "ivf") {
|
||||
video = new libvpx_test::IVFVideoSource(filename);
|
||||
} else {
|
||||
video = new libvpx_test::WebMVideoSource(filename);
|
||||
}
|
||||
ASSERT_TRUE(video != NULL);
|
||||
video->Init();
|
||||
|
||||
// Construct md5 file name.
|
||||
const std::string md5_filename = filename + ".md5";
|
||||
OpenMD5File(md5_filename);
|
||||
|
||||
// Decode frame, and check the md5 matching.
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video));
|
||||
delete video;
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, MinFrameBuffers) {
|
||||
// Minimum number of external frame buffers for VP9 is
|
||||
// #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS.
|
||||
const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
|
||||
ASSERT_EQ(VPX_CODEC_OK,
|
||||
SetFrameBufferFunctions(
|
||||
num_buffers, get_vp9_frame_buffer, release_vp9_frame_buffer));
|
||||
ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames());
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, EightJitterBuffers) {
|
||||
// Number of buffers equals #VP9_MAXIMUM_REF_BUFFERS +
|
||||
// #VPX_MAXIMUM_WORK_BUFFERS + eight jitter buffers.
|
||||
const int jitter_buffers = 8;
|
||||
const int num_buffers =
|
||||
VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS + jitter_buffers;
|
||||
ASSERT_EQ(VPX_CODEC_OK,
|
||||
SetFrameBufferFunctions(
|
||||
num_buffers, get_vp9_frame_buffer, release_vp9_frame_buffer));
|
||||
ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames());
|
||||
}
|
||||
|
||||
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.
|
||||
const int 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());
|
||||
ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeRemainingFrames());
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, NoRelease) {
|
||||
const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
|
||||
ASSERT_EQ(VPX_CODEC_OK,
|
||||
SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer,
|
||||
do_not_release_vp9_frame_buffer));
|
||||
ASSERT_EQ(VPX_CODEC_OK, DecodeOneFrame());
|
||||
ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeRemainingFrames());
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, NullRealloc) {
|
||||
const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
|
||||
ASSERT_EQ(VPX_CODEC_OK,
|
||||
SetFrameBufferFunctions(num_buffers, get_vp9_zero_frame_buffer,
|
||||
release_vp9_frame_buffer));
|
||||
ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeOneFrame());
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, ReallocOneLessByte) {
|
||||
const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
|
||||
ASSERT_EQ(VPX_CODEC_OK,
|
||||
SetFrameBufferFunctions(
|
||||
num_buffers, get_vp9_one_less_byte_frame_buffer,
|
||||
release_vp9_frame_buffer));
|
||||
ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeOneFrame());
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, NullGetFunction) {
|
||||
const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
|
||||
ASSERT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
SetFrameBufferFunctions(num_buffers, NULL,
|
||||
release_vp9_frame_buffer));
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, NullReleaseFunction) {
|
||||
const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
|
||||
ASSERT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
SetFrameBufferFunctions(num_buffers, get_vp9_frame_buffer, NULL));
|
||||
}
|
||||
|
||||
TEST_F(ExternalFrameBufferTest, SetAfterDecode) {
|
||||
const int num_buffers = VP9_MAXIMUM_REF_BUFFERS + VPX_MAXIMUM_WORK_BUFFERS;
|
||||
ASSERT_EQ(VPX_CODEC_OK, DecodeOneFrame());
|
||||
ASSERT_EQ(VPX_CODEC_ERROR,
|
||||
SetFrameBufferFunctions(
|
||||
num_buffers, get_vp9_frame_buffer, release_vp9_frame_buffer));
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(ExternalFrameBufferMD5Test,
|
||||
::testing::ValuesIn(libvpx_test::kVP9TestVectors));
|
||||
} // namespace
|
@@ -36,6 +36,7 @@ LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ../md5_utils.h ../md5_utils.c
|
||||
LIBVPX_TEST_SRCS-yes += decode_test_driver.cc
|
||||
LIBVPX_TEST_SRCS-yes += decode_test_driver.h
|
||||
LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ivf_video_source.h
|
||||
LIBVPX_TEST_SRCS-$(CONFIG_VP9_DECODER) += external_frame_buffer_test.cc
|
||||
|
||||
## WebM Parsing
|
||||
NESTEGG_SRCS += ../nestegg/halloc/halloc.h
|
||||
|
@@ -929,6 +929,7 @@ CODEC_INTERFACE(vpx_codec_vp8_dx) =
|
||||
vp8_get_si, /* vpx_codec_get_si_fn_t get_si; */
|
||||
vp8_decode, /* vpx_codec_decode_fn_t decode; */
|
||||
vp8_get_frame, /* vpx_codec_frame_get_fn_t frame_get; */
|
||||
NOT_IMPLEMENTED,
|
||||
},
|
||||
{ /* encoder functions */
|
||||
NOT_IMPLEMENTED,
|
||||
|
@@ -42,7 +42,7 @@ int vp9_get_frame_buffer(void *cb_priv, size_t min_size,
|
||||
int i;
|
||||
InternalFrameBufferList *const int_fb_list =
|
||||
(InternalFrameBufferList *)cb_priv;
|
||||
if (int_fb_list == NULL || fb == NULL)
|
||||
if (int_fb_list == NULL)
|
||||
return -1;
|
||||
|
||||
// Find a free frame buffer.
|
||||
@@ -73,12 +73,8 @@ int vp9_get_frame_buffer(void *cb_priv, size_t min_size,
|
||||
}
|
||||
|
||||
int vp9_release_frame_buffer(void *cb_priv, vpx_codec_frame_buffer_t *fb) {
|
||||
InternalFrameBuffer *int_fb;
|
||||
InternalFrameBuffer *const int_fb = (InternalFrameBuffer *)fb->priv;
|
||||
(void)cb_priv;
|
||||
if (fb == NULL)
|
||||
return -1;
|
||||
|
||||
int_fb = (InternalFrameBuffer *)fb->priv;
|
||||
int_fb->in_use = 0;
|
||||
return 0;
|
||||
}
|
||||
|
@@ -60,6 +60,11 @@ struct vpx_codec_alg_priv {
|
||||
int img_setup;
|
||||
int img_avail;
|
||||
int invert_tile_order;
|
||||
|
||||
// External frame buffer info to save for VP9 common.
|
||||
void *ext_priv; // Private data associated with the external frame buffers.
|
||||
vpx_get_frame_buffer_cb_fn_t get_ext_fb_cb;
|
||||
vpx_release_frame_buffer_cb_fn_t release_ext_fb_cb;
|
||||
};
|
||||
|
||||
static unsigned long priv_sz(const vpx_codec_dec_cfg_t *si,
|
||||
@@ -300,16 +305,22 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx,
|
||||
VP9D_COMP *const pbi = (VP9D_COMP*)optr;
|
||||
VP9_COMMON *const cm = &pbi->common;
|
||||
|
||||
cm->get_fb_cb = vp9_get_frame_buffer;
|
||||
cm->release_fb_cb = vp9_release_frame_buffer;
|
||||
|
||||
// Set index to not initialized.
|
||||
cm->new_fb_idx = -1;
|
||||
|
||||
if (ctx->get_ext_fb_cb != NULL && ctx->release_ext_fb_cb != NULL) {
|
||||
cm->get_fb_cb = ctx->get_ext_fb_cb;
|
||||
cm->release_fb_cb = ctx->release_ext_fb_cb;
|
||||
cm->cb_priv = ctx->ext_priv;
|
||||
} else {
|
||||
cm->get_fb_cb = vp9_get_frame_buffer;
|
||||
cm->release_fb_cb = vp9_release_frame_buffer;
|
||||
|
||||
if (vp9_alloc_internal_frame_buffers(&cm->int_frame_buffers))
|
||||
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
|
||||
"Failed to initialize internal frame buffers");
|
||||
cm->cb_priv = &cm->int_frame_buffers;
|
||||
}
|
||||
|
||||
ctx->pbi = optr;
|
||||
}
|
||||
@@ -350,7 +361,11 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx,
|
||||
|
||||
if (!res && 0 == vp9_get_raw_frame(ctx->pbi, &sd, &time_stamp,
|
||||
&time_end_stamp, &flags)) {
|
||||
VP9D_COMP *const pbi = (VP9D_COMP*)ctx->pbi;
|
||||
VP9_COMMON *const cm = &pbi->common;
|
||||
yuvconfig2image(&ctx->img, &sd, user_priv);
|
||||
|
||||
ctx->img.fb_priv = cm->frame_bufs[cm->new_fb_idx].raw_frame_buffer.priv;
|
||||
ctx->img_avail = 1;
|
||||
}
|
||||
}
|
||||
@@ -470,6 +485,24 @@ static vpx_image_t *vp9_get_frame(vpx_codec_alg_priv_t *ctx,
|
||||
return img;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t vp9_set_fb_fn(
|
||||
vpx_codec_alg_priv_t *ctx,
|
||||
vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release, void *cb_priv) {
|
||||
if (cb_get == NULL || cb_release == NULL) {
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
} else if (ctx->pbi == NULL) {
|
||||
// If the decoder has already been initialized, do not accept changes to
|
||||
// the frame buffer functions.
|
||||
ctx->get_ext_fb_cb = cb_get;
|
||||
ctx->release_ext_fb_cb = cb_release;
|
||||
ctx->ext_priv = cb_priv;
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
return VPX_CODEC_ERROR;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t vp9_xma_get_mmap(const vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_mmap_t *mmap,
|
||||
vpx_codec_iter_t *iter) {
|
||||
@@ -703,7 +736,8 @@ static vpx_codec_ctrl_fn_map_t ctf_maps[] = {
|
||||
CODEC_INTERFACE(vpx_codec_vp9_dx) = {
|
||||
"WebM Project VP9 Decoder" VERSION_STRING,
|
||||
VPX_CODEC_INTERNAL_ABI_VERSION,
|
||||
VPX_CODEC_CAP_DECODER | VP9_CAP_POSTPROC,
|
||||
VPX_CODEC_CAP_DECODER | VP9_CAP_POSTPROC |
|
||||
VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER,
|
||||
/* vpx_codec_caps_t caps; */
|
||||
vp9_init, /* vpx_codec_init_fn_t init; */
|
||||
vp9_destroy, /* vpx_codec_destroy_fn_t destroy; */
|
||||
@@ -715,6 +749,7 @@ CODEC_INTERFACE(vpx_codec_vp9_dx) = {
|
||||
vp9_get_si, /* vpx_codec_get_si_fn_t get_si; */
|
||||
vp9_decode, /* vpx_codec_decode_fn_t decode; */
|
||||
vp9_get_frame, /* vpx_codec_frame_get_fn_t frame_get; */
|
||||
vp9_set_fb_fn, /* vpx_codec_set_fb_fn_t set_fb_fn; */
|
||||
},
|
||||
{ // NOLINT
|
||||
/* encoder functions */
|
||||
|
@@ -6,4 +6,5 @@ text vpx_codec_get_stream_info
|
||||
text vpx_codec_peek_stream_info
|
||||
text vpx_codec_register_put_frame_cb
|
||||
text vpx_codec_register_put_slice_cb
|
||||
text vpx_codec_set_frame_buffer_functions
|
||||
text vpx_codec_set_mem_map
|
||||
|
@@ -59,7 +59,7 @@ extern "C" {
|
||||
* types, removing or reassigning enums, adding/removing/rearranging
|
||||
* fields to structures
|
||||
*/
|
||||
#define VPX_CODEC_INTERNAL_ABI_VERSION (4) /**<\hideinitializer*/
|
||||
#define VPX_CODEC_INTERNAL_ABI_VERSION (5) /**<\hideinitializer*/
|
||||
|
||||
typedef struct vpx_codec_alg_priv vpx_codec_alg_priv_t;
|
||||
typedef struct vpx_codec_priv_enc_mr_cfg vpx_codec_priv_enc_mr_cfg_t;
|
||||
@@ -218,6 +218,36 @@ typedef vpx_codec_err_t (*vpx_codec_decode_fn_t)(vpx_codec_alg_priv_t *ctx,
|
||||
typedef vpx_image_t *(*vpx_codec_get_frame_fn_t)(vpx_codec_alg_priv_t *ctx,
|
||||
vpx_codec_iter_t *iter);
|
||||
|
||||
/*!\brief Pass in external frame buffers for the decoder to use.
|
||||
*
|
||||
* Registers functions to be called when libvpx needs a frame buffer
|
||||
* to decode the current frame and a function to be called when libvpx does
|
||||
* not internally reference the frame buffer. This set function must
|
||||
* be called before the first call to decode or libvpx will assume the
|
||||
* default behavior of allocating frame buffers internally.
|
||||
*
|
||||
* \param[in] ctx Pointer to this instance's context
|
||||
* \param[in] cb_get Pointer to the get callback function
|
||||
* \param[in] cb_release Pointer to the release callback function
|
||||
* \param[in] cb_priv Callback's private data
|
||||
*
|
||||
* \retval #VPX_CODEC_OK
|
||||
* External frame buffers will be used by libvpx.
|
||||
* \retval #VPX_CODEC_INVALID_PARAM
|
||||
* One or more of the callbacks were NULL.
|
||||
* \retval #VPX_CODEC_ERROR
|
||||
* Decoder context not initialized, or algorithm not capable of
|
||||
* using external frame buffers.
|
||||
*
|
||||
* \note
|
||||
* When decoding VP9, the application may be required to pass in at least
|
||||
* #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS external frame
|
||||
* buffers.
|
||||
*/
|
||||
typedef vpx_codec_err_t (*vpx_codec_set_fb_fn_t)(
|
||||
vpx_codec_alg_priv_t *ctx,
|
||||
vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release, void *cb_priv);
|
||||
|
||||
/*\brief eXternal Memory Allocation memory map get iterator
|
||||
*
|
||||
@@ -308,6 +338,7 @@ struct vpx_codec_iface {
|
||||
vpx_codec_get_si_fn_t get_si; /**< \copydoc ::vpx_codec_get_si_fn_t */
|
||||
vpx_codec_decode_fn_t decode; /**< \copydoc ::vpx_codec_decode_fn_t */
|
||||
vpx_codec_get_frame_fn_t get_frame; /**< \copydoc ::vpx_codec_get_frame_fn_t */
|
||||
vpx_codec_set_fb_fn_t set_fb_fn; /**< \copydoc ::vpx_codec_set_fb_fn_t */
|
||||
} dec;
|
||||
struct vpx_codec_enc_iface {
|
||||
vpx_codec_enc_cfg_map_t *cfg_maps; /**< \copydoc ::vpx_codec_enc_cfg_map_t */
|
||||
|
@@ -226,3 +226,21 @@ vpx_codec_err_t vpx_codec_set_mem_map(vpx_codec_ctx_t *ctx,
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_codec_set_frame_buffer_functions(
|
||||
vpx_codec_ctx_t *ctx, vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release, void *cb_priv) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx || !cb_get || !cb_release) {
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
} else if (!ctx->iface || !ctx->priv ||
|
||||
!(ctx->iface->caps & VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER)) {
|
||||
res = VPX_CODEC_ERROR;
|
||||
} else {
|
||||
res = ctx->iface->dec.set_fb_fn(ctx->priv->alg_priv, cb_get, cb_release,
|
||||
cb_priv);
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "./vpx_codec.h"
|
||||
#include "./vpx_frame_buffer.h"
|
||||
|
||||
/*!\brief Current ABI version number
|
||||
*
|
||||
@@ -39,7 +40,7 @@ extern "C" {
|
||||
* types, removing or reassigning enums, adding/removing/rearranging
|
||||
* fields to structures
|
||||
*/
|
||||
#define VPX_DECODER_ABI_VERSION (2 + VPX_CODEC_ABI_VERSION) /**<\hideinitializer*/
|
||||
#define VPX_DECODER_ABI_VERSION (3 + VPX_CODEC_ABI_VERSION) /**<\hideinitializer*/
|
||||
|
||||
/*! \brief Decoder capabilities bitfield
|
||||
*
|
||||
@@ -66,6 +67,8 @@ extern "C" {
|
||||
*/
|
||||
#define VPX_CODEC_CAP_FRAME_THREADING 0x200000 /**< Can support frame-based
|
||||
multi-threading */
|
||||
#define VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER 0x400000 /**< Can support external
|
||||
frame buffers */
|
||||
|
||||
#define VPX_CODEC_USE_POSTPROC 0x10000 /**< Postprocess decoded frame */
|
||||
#define VPX_CODEC_USE_ERROR_CONCEALMENT 0x20000 /**< Conceal errors in decoded
|
||||
@@ -326,6 +329,51 @@ extern "C" {
|
||||
|
||||
/*!@} - end defgroup cap_put_slice*/
|
||||
|
||||
/*!\defgroup cap_external_frame_buffer External Frame Buffer Functions
|
||||
*
|
||||
* The following section is required to be implemented for all decoders
|
||||
* that advertise the VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER capability.
|
||||
* Calling this function for codecs that don't advertise this capability
|
||||
* will result in an error code being returned, usually VPX_CODEC_ERROR.
|
||||
*
|
||||
* \note
|
||||
* Currently this only works with VP9.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!\brief Pass in external frame buffers for the decoder to use.
|
||||
*
|
||||
* Registers functions to be called when libvpx needs a frame buffer
|
||||
* to decode the current frame and a function to be called when libvpx does
|
||||
* not internally reference the frame buffer. This set function must
|
||||
* be called before the first call to decode or libvpx will assume the
|
||||
* default behavior of allocating frame buffers internally.
|
||||
*
|
||||
* \param[in] ctx Pointer to this instance's context
|
||||
* \param[in] cb_get Pointer to the get callback function
|
||||
* \param[in] cb_release Pointer to the release callback function
|
||||
* \param[in] cb_priv Callback's private data
|
||||
*
|
||||
* \retval #VPX_CODEC_OK
|
||||
* External frame buffers will be used by libvpx.
|
||||
* \retval #VPX_CODEC_INVALID_PARAM
|
||||
* One or more of the callbacks were NULL.
|
||||
* \retval #VPX_CODEC_ERROR
|
||||
* Decoder context not initialized, or algorithm not capable of
|
||||
* using external frame buffers.
|
||||
*
|
||||
* \note
|
||||
* When decoding VP9, the application may be required to pass in at least
|
||||
* #VP9_MAXIMUM_REF_BUFFERS + #VPX_MAXIMUM_WORK_BUFFERS external frame
|
||||
* buffers.
|
||||
*/
|
||||
vpx_codec_err_t vpx_codec_set_frame_buffer_functions(
|
||||
vpx_codec_ctx_t *ctx,
|
||||
vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release, void *cb_priv);
|
||||
|
||||
/*!@} - end defgroup cap_external_frame_buffer */
|
||||
|
||||
/*!@} - end defgroup decoder*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -45,8 +45,9 @@ typedef struct vpx_codec_frame_buffer {
|
||||
* decoder needs a frame buffer to decode a compressed image into. This
|
||||
* function may be called more than once for every call to vpx_codec_decode.
|
||||
* The application may set fb->priv to some data which will be passed
|
||||
* back in the ximage and the release function call. On success the callback
|
||||
* must return 0. Any failure the callback must return a value less than 0.
|
||||
* back in the ximage and the release function call. |fb| is guaranteed to
|
||||
* not be NULL. On success the callback must return 0. Any failure the
|
||||
* callback must return a value less than 0.
|
||||
*
|
||||
* \param[in] priv Callback's private data
|
||||
* \param[in] new_size Size in bytes needed by the buffer
|
||||
@@ -58,8 +59,9 @@ typedef int (*vpx_get_frame_buffer_cb_fn_t)(
|
||||
/*!\brief release frame buffer callback prototype
|
||||
*
|
||||
* This callback is invoked by the decoder when the frame buffer is not
|
||||
* referenced by any other buffers. On success the callback must return 0.
|
||||
* Any failure the callback must return a value less than 0.
|
||||
* referenced by any other buffers. |fb| is guaranteed to not be NULL. On
|
||||
* success the callback must return 0. Any failure the callback must return
|
||||
* a value less than 0.
|
||||
*
|
||||
* \param[in] priv Callback's private data
|
||||
* \param[in] fb Pointer to vpx_codec_frame_buffer_t
|
||||
|
@@ -28,7 +28,7 @@ extern "C" {
|
||||
* types, removing or reassigning enums, adding/removing/rearranging
|
||||
* fields to structures
|
||||
*/
|
||||
#define VPX_IMAGE_ABI_VERSION (1) /**<\hideinitializer*/
|
||||
#define VPX_IMAGE_ABI_VERSION (2) /**<\hideinitializer*/
|
||||
|
||||
|
||||
#define VPX_IMG_FMT_PLANAR 0x100 /**< Image is a planar format */
|
||||
@@ -139,6 +139,8 @@ extern "C" {
|
||||
unsigned char *img_data; /**< private */
|
||||
int img_data_owner; /**< private */
|
||||
int self_allocd; /**< private */
|
||||
|
||||
void *fb_priv; /**< Frame buffer data associated with the image. */
|
||||
} vpx_image_t; /**< alias for struct vpx_image */
|
||||
|
||||
/**\brief Representation of a rectangle on a surface */
|
||||
|
88
vpxdec.c
88
vpxdec.c
@@ -75,6 +75,8 @@ static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0,
|
||||
static const arg_def_t scalearg = ARG_DEF("S", "scale", 0,
|
||||
"Scale output frames uniformly");
|
||||
|
||||
static const arg_def_t fb_arg =
|
||||
ARG_DEF(NULL, "frame-buffers", 1, "Number of frame buffers to use");
|
||||
|
||||
static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
|
||||
"Compute the MD5 sum of the decoded frame");
|
||||
@@ -82,7 +84,7 @@ static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
|
||||
static const arg_def_t *all_args[] = {
|
||||
&codecarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg,
|
||||
&progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile,
|
||||
&threadsarg, &verbosearg, &scalearg,
|
||||
&threadsarg, &verbosearg, &scalearg, &fb_arg,
|
||||
&md5arg,
|
||||
&error_concealment,
|
||||
NULL
|
||||
@@ -302,6 +304,68 @@ void show_progress(int frame_in, int frame_out, unsigned long dx_time) {
|
||||
(float)frame_out * 1000000.0 / (float)dx_time);
|
||||
}
|
||||
|
||||
struct ExternalFrameBuffer {
|
||||
uint8_t* data;
|
||||
size_t size;
|
||||
int in_use;
|
||||
};
|
||||
|
||||
struct ExternalFrameBufferList {
|
||||
int num_external_frame_buffers;
|
||||
struct ExternalFrameBuffer *ext_fb;
|
||||
};
|
||||
|
||||
// Callback used by libvpx to request an external frame buffer. |cb_priv|
|
||||
// Application private data passed into the set function. |min_size| is the
|
||||
// minimum size in bytes needed to decode the next frame. |fb| pointer to the
|
||||
// frame buffer.
|
||||
int get_vp9_frame_buffer(void *cb_priv, size_t min_size,
|
||||
vpx_codec_frame_buffer_t *fb) {
|
||||
int i;
|
||||
struct ExternalFrameBufferList *const ext_fb_list =
|
||||
(struct ExternalFrameBufferList *)cb_priv;
|
||||
if (ext_fb_list == NULL)
|
||||
return -1;
|
||||
|
||||
// Find a free frame buffer.
|
||||
for (i = 0; i < ext_fb_list->num_external_frame_buffers; ++i) {
|
||||
if (!ext_fb_list->ext_fb[i].in_use)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ext_fb_list->num_external_frame_buffers)
|
||||
return -1;
|
||||
|
||||
if (ext_fb_list->ext_fb[i].size < min_size) {
|
||||
free(ext_fb_list->ext_fb[i].data);
|
||||
ext_fb_list->ext_fb[i].data = (uint8_t *)malloc(min_size);
|
||||
if (!ext_fb_list->ext_fb[i].data)
|
||||
return -1;
|
||||
|
||||
ext_fb_list->ext_fb[i].size = min_size;
|
||||
}
|
||||
|
||||
fb->data = ext_fb_list->ext_fb[i].data;
|
||||
fb->size = ext_fb_list->ext_fb[i].size;
|
||||
ext_fb_list->ext_fb[i].in_use = 1;
|
||||
|
||||
// Set the frame buffer's private data to point at the external frame buffer.
|
||||
fb->priv = &ext_fb_list->ext_fb[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Callback used by libvpx when there are no references to the frame buffer.
|
||||
// |cb_priv| user private data passed into the set function. |fb| pointer
|
||||
// to the frame buffer.
|
||||
int release_vp9_frame_buffer(void *cb_priv,
|
||||
vpx_codec_frame_buffer_t *fb) {
|
||||
struct ExternalFrameBuffer *const ext_fb =
|
||||
(struct ExternalFrameBuffer *)fb->priv;
|
||||
(void)cb_priv;
|
||||
ext_fb->in_use = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void generate_filename(const char *pattern, char *out, size_t q_len,
|
||||
unsigned int d_w, unsigned int d_h,
|
||||
unsigned int frame_in) {
|
||||
@@ -447,6 +511,8 @@ int main_loop(int argc, const char **argv_) {
|
||||
int do_scale = 0;
|
||||
vpx_image_t *scaled_img = NULL;
|
||||
int frame_avail, got_data;
|
||||
int num_external_frame_buffers = 0;
|
||||
struct ExternalFrameBufferList ext_fb_list = {0};
|
||||
|
||||
const char *outfile_pattern = NULL;
|
||||
char outfile_name[PATH_MAX] = {0};
|
||||
@@ -505,6 +571,8 @@ int main_loop(int argc, const char **argv_) {
|
||||
quiet = 0;
|
||||
else if (arg_match(&arg, &scalearg, argi))
|
||||
do_scale = 1;
|
||||
else if (arg_match(&arg, &fb_arg, argi))
|
||||
num_external_frame_buffers = arg_parse_uint(&arg);
|
||||
|
||||
#if CONFIG_VP8_DECODER
|
||||
else if (arg_match(&arg, &addnoise_level, argi)) {
|
||||
@@ -691,6 +759,19 @@ int main_loop(int argc, const char **argv_) {
|
||||
arg_skip--;
|
||||
}
|
||||
|
||||
if (num_external_frame_buffers > 0) {
|
||||
ext_fb_list.num_external_frame_buffers = num_external_frame_buffers;
|
||||
ext_fb_list.ext_fb = (struct ExternalFrameBuffer *)calloc(
|
||||
num_external_frame_buffers, sizeof(*ext_fb_list.ext_fb));
|
||||
if (vpx_codec_set_frame_buffer_functions(
|
||||
&decoder, get_vp9_frame_buffer, release_vp9_frame_buffer,
|
||||
&ext_fb_list)) {
|
||||
fprintf(stderr, "Failed to configure external frame buffers: %s\n",
|
||||
vpx_codec_error(&decoder));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
frame_avail = 1;
|
||||
got_data = 0;
|
||||
|
||||
@@ -863,6 +944,11 @@ fail:
|
||||
|
||||
if (scaled_img) vpx_img_free(scaled_img);
|
||||
|
||||
for (i = 0; i < ext_fb_list.num_external_frame_buffers; ++i) {
|
||||
free(ext_fb_list.ext_fb[i].data);
|
||||
}
|
||||
free(ext_fb_list.ext_fb);
|
||||
|
||||
fclose(infile);
|
||||
free(argv);
|
||||
|
||||
|
Reference in New Issue
Block a user