diff options
author | Frank Galligan <fgalligan@google.com> | 2013-12-11 09:06:35 -0800 |
---|---|---|
committer | Frank Galligan <fgalligan@google.com> | 2013-12-15 18:45:46 -0800 |
commit | 10f891696bc4c972c13cc9fde2c53470501a03e2 (patch) | |
tree | 21d8f81ea2df7408f5e1e16e547c2113d0c99012 | |
parent | 52b2d50db4d194b7484e8ca0bec4c8c71bf94eaa (diff) | |
download | libvpx-10f891696bc4c972c13cc9fde2c53470501a03e2.tar.gz |
Add support to pass in external frame buffers.
VP9 decoder can now use frame buffers passed in by the application.
Change-Id: I599527ec85c577f3f5552831d79a693884fafb73
-rw-r--r-- | libs.mk | 1 | ||||
-rw-r--r-- | test/codec_factory.h | 2 | ||||
-rw-r--r-- | test/decode_test_driver.h | 10 | ||||
-rw-r--r-- | test/external_frame_buffer_test.cc | 309 | ||||
-rw-r--r-- | test/test.mk | 1 | ||||
-rw-r--r-- | vp8/vp8_dx_iface.c | 1 | ||||
-rw-r--r-- | vp9/common/vp9_alloccommon.c | 22 | ||||
-rw-r--r-- | vp9/common/vp9_onyxc_int.h | 13 | ||||
-rw-r--r-- | vp9/common/vp9_reconinter.c | 2 | ||||
-rw-r--r-- | vp9/decoder/vp9_decodeframe.c | 18 | ||||
-rw-r--r-- | vp9/encoder/vp9_onyx_if.c | 10 | ||||
-rw-r--r-- | vp9/vp9_dx_iface.c | 59 | ||||
-rw-r--r-- | vpx/exports_dec | 1 | ||||
-rw-r--r-- | vpx/internal/vpx_codec_internal.h | 33 | ||||
-rw-r--r-- | vpx/src/vpx_decoder.c | 19 | ||||
-rw-r--r-- | vpx/vpx_codec.mk | 2 | ||||
-rw-r--r-- | vpx/vpx_decoder.h | 48 | ||||
-rw-r--r-- | vpx/vpx_external_frame_buffer.h | 53 | ||||
-rw-r--r-- | vpx_scale/generic/yv12config.c | 57 | ||||
-rw-r--r-- | vpx_scale/yv12config.h | 14 | ||||
-rw-r--r-- | vpxdec.c | 54 |
21 files changed, 690 insertions, 39 deletions
@@ -183,6 +183,7 @@ CODEC_EXPORTS-$(CONFIG_DECODERS) += vpx/exports_dec INSTALL-LIBS-yes += include/vpx/vpx_codec.h INSTALL-LIBS-yes += include/vpx/vpx_image.h +INSTALL-LIBS-yes += include/vpx/vpx_external_frame_buffer.h INSTALL-LIBS-yes += include/vpx/vpx_integer.h INSTALL-LIBS-$(CONFIG_DECODERS) += include/vpx/vpx_decoder.h INSTALL-LIBS-$(CONFIG_ENCODERS) += include/vpx/vpx_encoder.h diff --git a/test/codec_factory.h b/test/codec_factory.h index cc7b53f06..2ca6ff086 100644 --- a/test/codec_factory.h +++ b/test/codec_factory.h @@ -26,6 +26,8 @@ extern "C" { #include "test/encode_test_driver.h" namespace libvpx_test { +const int kCodecFactoryParam = 0; + class CodecFactory { public: CodecFactory() {} diff --git a/test/decode_test_driver.h b/test/decode_test_driver.h index ddaed9fa0..79db6e1bc 100644 --- a/test/decode_test_driver.h +++ b/test/decode_test_driver.h @@ -76,6 +76,16 @@ class Decoder { return detail ? detail : vpx_codec_error(&decoder_); } + // Passes the external frame buffer information to libvpx. + vpx_codec_err_t SetExternalFrameBuffers( + vpx_codec_frame_buffer_t *fb_list, int fb_count, + vpx_realloc_frame_buffer_cb_fn_t cb, void *user_priv) { + InitOnce(); + return vpx_codec_set_frame_buffers(&decoder_, + fb_list, fb_count, + cb, user_priv); + } + protected: virtual const vpx_codec_iface_t* CodecInterface() const = 0; diff --git a/test/external_frame_buffer_test.cc b/test/external_frame_buffer_test.cc new file mode 100644 index 000000000..874d1997c --- /dev/null +++ b/test/external_frame_buffer_test.cc @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2013 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"; + +// Callback used by libvpx to request the application to allocate a frame +// buffer of at least |new_size| in bytes. +int realloc_vp9_frame_buffer(void *user_priv, size_t new_size, + vpx_codec_frame_buffer_t *fb) { + (void)user_priv; + if (fb == NULL) + return -1; + + delete [] fb->data; + fb->data = new uint8_t[new_size]; + fb->size = new_size; + return 0; +} + +// Callback will not allocate data for frame buffer. +int zero_realloc_vp9_frame_buffer(void *user_priv, size_t new_size, + vpx_codec_frame_buffer_t *fb) { + (void)user_priv; + if (fb == NULL) + return -1; + + delete [] fb->data; + fb->data = NULL; + fb->size = new_size; + return 0; +} + +// Callback will allocate one less byte. +int one_less_byte_realloc_vp9_frame_buffer(void *user_priv, size_t new_size, + vpx_codec_frame_buffer_t *fb) { + (void)user_priv; + if (fb == NULL) + return -1; + + delete [] fb->data; + + const size_t error_size = new_size - 1; + fb->data = new uint8_t[error_size]; + fb->size = error_size; + 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), + frame_buffers_(NULL) {} + + virtual ~ExternalFrameBufferMD5Test() { + for (int i = 0; i < num_buffers_; ++i) { + delete [] frame_buffers_[i].data; + } + delete [] frame_buffers_; + + 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. + frame_buffers_ = new vpx_codec_frame_buffer_t[num_buffers_]; + memset(frame_buffers_, 0, sizeof(frame_buffers_[0]) * num_buffers_); + + ASSERT_EQ(VPX_CODEC_OK, + decoder->SetExternalFrameBuffers( + frame_buffers_, num_buffers_, + realloc_vp9_frame_buffer, NULL)); + } + } + + 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; + } + + 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_; + vpx_codec_frame_buffer_t *frame_buffers_; +}; + +class ExternalFrameBufferTest : public ::testing::Test { + protected: + ExternalFrameBufferTest() + : video_(NULL), + decoder_(NULL), + num_buffers_(0), + frame_buffers_(NULL) {} + + virtual void SetUp() { + video_ = new libvpx_test::WebMVideoSource(kVP9TestFile); + video_->Init(); + video_->Begin(); + + vpx_codec_dec_cfg_t cfg = {0}; + decoder_ = new libvpx_test::VP9Decoder(cfg, 0); + } + + virtual void TearDown() { + for (int i = 0; i < num_buffers_; ++i) { + delete [] frame_buffers_[i].data; + } + delete [] frame_buffers_; + delete decoder_; + delete video_; + } + + // Passes the external frame buffer information to libvpx. + vpx_codec_err_t SetExternalFrameBuffers( + int num_buffers, + vpx_realloc_frame_buffer_cb_fn_t cb) { + if (num_buffers > 0) { + num_buffers_ = num_buffers; + + // Have libvpx use frame buffers we create. + frame_buffers_ = new vpx_codec_frame_buffer_t[num_buffers_]; + memset(frame_buffers_, 0, sizeof(frame_buffers_[0]) * num_buffers_); + } + + return decoder_->SetExternalFrameBuffers(frame_buffers_, num_buffers_, + cb, NULL); + } + + // Pass Null frame buffer list to libvpx. + vpx_codec_err_t SetNullFrameBuffers( + int num_buffers, + vpx_realloc_frame_buffer_cb_fn_t cb) { + return decoder_->SetExternalFrameBuffers(NULL, num_buffers, + cb, NULL); + } + + vpx_codec_err_t DecodeOneFrame() { + const vpx_codec_err_t res = + decoder_->DecodeFrame(video_->cxdata(), video_->frame_size()); + if (res == VPX_CODEC_OK) + video_->Next(); + return res; + } + + vpx_codec_err_t DecodeRemainingFrames() { + for (; video_->cxdata(); video_->Next()) { + const vpx_codec_err_t res = + decoder_->DecodeFrame(video_->cxdata(), video_->frame_size()); + if (res != VPX_CODEC_OK) + return res; + + libvpx_test::DxDataIterator dec_iter = decoder_->GetDxData(); + const vpx_image_t *img = NULL; + + // Get decompressed data + while ((img = dec_iter.Next())) { + } + } + return VPX_CODEC_OK; + } + + libvpx_test::WebMVideoSource *video_; + libvpx_test::VP9Decoder *decoder_; + int num_buffers_; + vpx_codec_frame_buffer_t *frame_buffers_; +}; + + +// 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 number of possible reference buffers(8), plus + // one working buffer, plus four jitter buffers. + const int num_buffers = 13; + set_num_buffers(num_buffers); + + // Tell compiler we are not using kVP8TestVectors. + (void)libvpx_test::kVP8TestVectors; + + // Open compressed video file. + if (filename.substr(filename.length() - 3, 3) == "ivf") { + video = new libvpx_test::IVFVideoSource(filename); + } else if (filename.substr(filename.length() - 4, 4) == "webm") { + video = new libvpx_test::WebMVideoSource(filename); + } + 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, EightFrameBuffers) { + // Minimum number of reference buffers for VP9 is 8. + const int num_buffers = 8; + ASSERT_EQ(VPX_CODEC_OK, + SetExternalFrameBuffers(num_buffers, realloc_vp9_frame_buffer)); + ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames()); +} + +TEST_F(ExternalFrameBufferTest, EightJitterBuffers) { + // Number of buffers equals number of possible reference buffers(8), plus + // one working buffer, plus eight jitter buffers. + const int num_buffers = 17; + ASSERT_EQ(VPX_CODEC_OK, + SetExternalFrameBuffers(num_buffers, realloc_vp9_frame_buffer)); + ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames()); +} + +TEST_F(ExternalFrameBufferTest, NotEnoughBuffers) { + // Minimum number of reference buffers for VP9 is 8. + const int num_buffers = 7; + ASSERT_EQ(VPX_CODEC_INVALID_PARAM, + SetExternalFrameBuffers(num_buffers, realloc_vp9_frame_buffer)); +} + +TEST_F(ExternalFrameBufferTest, NullFrameBufferList) { + // Number of buffers equals number of possible reference buffers(8), plus + // one working buffer, plus four jitter buffers. + const int num_buffers = 13; + ASSERT_EQ(VPX_CODEC_INVALID_PARAM, + SetNullFrameBuffers(num_buffers, realloc_vp9_frame_buffer)); +} + +TEST_F(ExternalFrameBufferTest, NullRealloc) { + // Number of buffers equals number of possible reference buffers(8), plus + // one working buffer, plus four jitter buffers. + const int num_buffers = 13; + ASSERT_EQ(VPX_CODEC_OK, + SetExternalFrameBuffers(num_buffers, + zero_realloc_vp9_frame_buffer)); + ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeOneFrame()); +} + +TEST_F(ExternalFrameBufferTest, ReallocOneLessByte) { + // Number of buffers equals number of possible reference buffers(8), plus + // one working buffer, plus four jitter buffers. + const int num_buffers = 13; + ASSERT_EQ(VPX_CODEC_OK, + SetExternalFrameBuffers(num_buffers, + one_less_byte_realloc_vp9_frame_buffer)); + ASSERT_EQ(VPX_CODEC_MEM_ERROR, DecodeOneFrame()); +} + +VP9_INSTANTIATE_TEST_CASE(ExternalFrameBufferMD5Test, + ::testing::ValuesIn(libvpx_test::kVP9TestVectors)); +} // namespace diff --git a/test/test.mk b/test/test.mk index 743d0a431..63df488f9 100644 --- a/test/test.mk +++ b/test/test.mk @@ -34,6 +34,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 diff --git a/vp8/vp8_dx_iface.c b/vp8/vp8_dx_iface.c index 871b8d385..0b4c4cbbf 100644 --- a/vp8/vp8_dx_iface.c +++ b/vp8/vp8_dx_iface.c @@ -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, diff --git a/vp9/common/vp9_alloccommon.c b/vp9/common/vp9_alloccommon.c index f56784071..6c333c5ef 100644 --- a/vp9/common/vp9_alloccommon.c +++ b/vp9/common/vp9_alloccommon.c @@ -34,7 +34,7 @@ void vp9_update_mode_info_border(VP9_COMMON *cm, MODE_INFO *mi) { void vp9_free_frame_buffers(VP9_COMMON *cm) { int i; - for (i = 0; i < FRAME_BUFFERS; i++) + for (i = 0; i < cm->fb_count; i++) vp9_free_frame_buffer(&cm->yv12_fb[i]); vp9_free_frame_buffer(&cm->post_proc_buffer); @@ -86,7 +86,7 @@ int vp9_resize_frame_buffers(VP9_COMMON *cm, int width, int height) { int mi_size; if (vp9_realloc_frame_buffer(&cm->post_proc_buffer, width, height, ss_x, ss_y, - VP9BORDERINPIXELS) < 0) + VP9BORDERINPIXELS, NULL, NULL, NULL) < 0) goto fail; set_mb_mi(cm, aligned_width, aligned_height); @@ -138,16 +138,24 @@ int vp9_alloc_frame_buffers(VP9_COMMON *cm, int width, int height) { const int ss_y = cm->subsampling_y; int mi_size; + if (cm->fb_count == 0) { + cm->fb_count = FRAME_BUFFERS; + CHECK_MEM_ERROR(cm, cm->yv12_fb, + vpx_calloc(cm->fb_count, sizeof(*cm->yv12_fb))); + CHECK_MEM_ERROR(cm, cm->fb_idx_ref_cnt, + vpx_calloc(cm->fb_count, sizeof(*cm->fb_idx_ref_cnt))); + } + vp9_free_frame_buffers(cm); - for (i = 0; i < FRAME_BUFFERS; i++) { + for (i = 0; i < cm->fb_count; i++) { cm->fb_idx_ref_cnt[i] = 0; if (vp9_alloc_frame_buffer(&cm->yv12_fb[i], width, height, ss_x, ss_y, VP9BORDERINPIXELS) < 0) goto fail; } - cm->new_fb_idx = FRAME_BUFFERS - 1; + cm->new_fb_idx = cm->fb_count - 1; cm->fb_idx_ref_cnt[cm->new_fb_idx] = 1; for (i = 0; i < REFS_PER_FRAME; i++) @@ -203,6 +211,12 @@ void vp9_create_common(VP9_COMMON *cm) { void vp9_remove_common(VP9_COMMON *cm) { vp9_free_frame_buffers(cm); + + vpx_free(cm->yv12_fb); + vpx_free(cm->fb_idx_ref_cnt); + + cm->yv12_fb = NULL; + cm->fb_idx_ref_cnt = NULL; } void vp9_initialize_common() { diff --git a/vp9/common/vp9_onyxc_int.h b/vp9/common/vp9_onyxc_int.h index 975a1d52a..db1ca3db3 100644 --- a/vp9/common/vp9_onyxc_int.h +++ b/vp9/common/vp9_onyxc_int.h @@ -113,8 +113,8 @@ typedef struct VP9Common { YV12_BUFFER_CONFIG *frame_to_show; - YV12_BUFFER_CONFIG yv12_fb[FRAME_BUFFERS]; - int fb_idx_ref_cnt[FRAME_BUFFERS]; /* reference counts */ + YV12_BUFFER_CONFIG *yv12_fb; + int *fb_idx_ref_cnt; /* reference counts */ int ref_frame_map[REF_FRAMES]; /* maps fb_idx to reference slot */ // TODO(jkoleszar): could expand active_ref_idx to 4, with 0 as intra, and @@ -213,6 +213,11 @@ typedef struct VP9Common { int frame_parallel_decoding_mode; int log2_tile_cols, log2_tile_rows; + + vpx_codec_frame_buffer_t *fb_list; // External frame buffers + int fb_count; // Total number of frame buffers + vpx_realloc_frame_buffer_cb_fn_t realloc_fb_cb; + void *user_priv; // Private data associated with the external frame buffers. } VP9_COMMON; // ref == 0 => LAST_FRAME @@ -228,11 +233,11 @@ static YV12_BUFFER_CONFIG *get_frame_new_buffer(VP9_COMMON *cm) { static int get_free_fb(VP9_COMMON *cm) { int i; - for (i = 0; i < FRAME_BUFFERS; i++) + for (i = 0; i < cm->fb_count; i++) if (cm->fb_idx_ref_cnt[i] == 0) break; - assert(i < FRAME_BUFFERS); + assert(i < cm->fb_count); cm->fb_idx_ref_cnt[i] = 1; return i; } diff --git a/vp9/common/vp9_reconinter.c b/vp9/common/vp9_reconinter.c index e8247679c..24f7b597f 100644 --- a/vp9/common/vp9_reconinter.c +++ b/vp9/common/vp9_reconinter.c @@ -288,7 +288,7 @@ void vp9_setup_scale_factors(VP9_COMMON *cm, int i) { const int ref = cm->active_ref_idx[i]; struct scale_factors *const sf = &cm->active_ref_scale[i]; struct scale_factors_common *const sfc = &cm->active_ref_scale_comm[i]; - if (ref >= FRAME_BUFFERS) { + if (ref >= cm->fb_count) { vp9_zero(*sf); vp9_zero(*sfc); } else { diff --git a/vp9/decoder/vp9_decodeframe.c b/vp9/decoder/vp9_decodeframe.c index 07c5401d6..9452b194a 100644 --- a/vp9/decoder/vp9_decodeframe.c +++ b/vp9/decoder/vp9_decodeframe.c @@ -700,9 +700,21 @@ static void apply_frame_size(VP9D_COMP *pbi, int width, int height) { vp9_update_frame_size(cm); } - vp9_realloc_frame_buffer(get_frame_new_buffer(cm), cm->width, cm->height, - cm->subsampling_x, cm->subsampling_y, - VP9BORDERINPIXELS); + if (cm->fb_list != NULL) { + vpx_codec_frame_buffer_t *const ext_fb = &cm->fb_list[cm->new_fb_idx]; + if (vp9_realloc_frame_buffer(get_frame_new_buffer(cm), + cm->width, cm->height, + cm->subsampling_x, cm->subsampling_y, + VP9BORDERINPIXELS, ext_fb, + cm->realloc_fb_cb, cm->user_priv)) { + vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, + "Failed to allocate external frame buffer"); + } + } else { + vp9_realloc_frame_buffer(get_frame_new_buffer(cm), cm->width, cm->height, + cm->subsampling_x, cm->subsampling_y, + VP9BORDERINPIXELS, NULL, NULL, NULL); + } } static void setup_frame_size(VP9D_COMP *pbi, diff --git a/vp9/encoder/vp9_onyx_if.c b/vp9/encoder/vp9_onyx_if.c index ce12b4322..ce87a76cb 100644 --- a/vp9/encoder/vp9_onyx_if.c +++ b/vp9/encoder/vp9_onyx_if.c @@ -920,7 +920,7 @@ static void alloc_raw_frame_buffers(VP9_COMP *cpi) { if (vp9_realloc_frame_buffer(&cpi->alt_ref_buffer, cpi->oxcf.width, cpi->oxcf.height, cm->subsampling_x, cm->subsampling_y, - VP9BORDERINPIXELS)) + VP9BORDERINPIXELS, NULL, NULL, NULL)) vpx_internal_error(&cpi->common.error, VPX_CODEC_MEM_ERROR, "Failed to allocate altref buffer"); } @@ -988,14 +988,14 @@ static void update_frame_size(VP9_COMP *cpi) { if (vp9_realloc_frame_buffer(&cpi->last_frame_uf, cm->width, cm->height, cm->subsampling_x, cm->subsampling_y, - VP9BORDERINPIXELS)) + VP9BORDERINPIXELS, NULL, NULL, NULL)) vpx_internal_error(&cpi->common.error, VPX_CODEC_MEM_ERROR, "Failed to reallocate last frame buffer"); if (vp9_realloc_frame_buffer(&cpi->scaled_source, cm->width, cm->height, cm->subsampling_x, cm->subsampling_y, - VP9BORDERINPIXELS)) + VP9BORDERINPIXELS, NULL, NULL, NULL)) vpx_internal_error(&cpi->common.error, VPX_CODEC_MEM_ERROR, "Failed to reallocate scaled source buffer"); @@ -2563,7 +2563,7 @@ static void scale_references(VP9_COMP *cpi) { vp9_realloc_frame_buffer(&cm->yv12_fb[new_fb], cm->width, cm->height, cm->subsampling_x, cm->subsampling_y, - VP9BORDERINPIXELS); + VP9BORDERINPIXELS, NULL, NULL, NULL); scale_and_extend_frame(ref, &cm->yv12_fb[new_fb]); cpi->scaled_ref_idx[i] = new_fb; } else { @@ -3554,7 +3554,7 @@ int vp9_get_compressed_data(VP9_PTR ptr, unsigned int *frame_flags, vp9_realloc_frame_buffer(get_frame_new_buffer(cm), cm->width, cm->height, cm->subsampling_x, cm->subsampling_y, - VP9BORDERINPIXELS); + VP9BORDERINPIXELS, NULL, NULL, NULL); // Calculate scaling factors for each of the 3 available references for (i = 0; i < REFS_PER_FRAME; ++i) { diff --git a/vp9/vp9_dx_iface.c b/vp9/vp9_dx_iface.c index fde73e4ab..7fdd5ef5c 100644 --- a/vp9/vp9_dx_iface.c +++ b/vp9/vp9_dx_iface.c @@ -59,6 +59,12 @@ struct vpx_codec_alg_priv { int img_setup; int img_avail; int invert_tile_order; + + /* External buffer info to save for VP9 common. */ + vpx_codec_frame_buffer_t *fb_list; // External frame buffers + int fb_count; // Total number of frame buffers + vpx_realloc_frame_buffer_cb_fn_t realloc_fb_cb; + void *user_priv; // Private data associated with the external frame buffers. }; static unsigned long priv_sz(const vpx_codec_dec_cfg_t *si, @@ -307,10 +313,26 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx, ctx->postproc_cfg.noise_level = 0; } - if (!optr) + if (!optr) { res = VPX_CODEC_ERROR; - else + } else { + VP9D_COMP *const pbi = (VP9D_COMP*)optr; + VP9_COMMON *const cm = &pbi->common; + if (ctx->fb_list != NULL && ctx->realloc_fb_cb != NULL && + ctx->fb_count > 0) { + cm->fb_list = ctx->fb_list; + cm->fb_count = ctx->fb_count; + cm->realloc_fb_cb = ctx->realloc_fb_cb; + cm->user_priv = ctx->user_priv; + } else { + cm->fb_count = FRAME_BUFFERS; + } + CHECK_MEM_ERROR(cm, cm->yv12_fb, + vpx_calloc(cm->fb_count, sizeof(*cm->yv12_fb))); + CHECK_MEM_ERROR(cm, cm->fb_idx_ref_cnt, + vpx_calloc(cm->fb_count, sizeof(*cm->fb_idx_ref_cnt))); ctx->pbi = optr; + } } ctx->decoder_init = 1; @@ -347,7 +369,7 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx, } if (vp9_receive_compressed_data(ctx->pbi, data_sz, data, deadline)) { - VP9D_COMP *pbi = (VP9D_COMP *)ctx->pbi; + VP9D_COMP *pbi = (VP9D_COMP*)ctx->pbi; res = update_error_state(ctx, &pbi->common.error); } @@ -475,6 +497,27 @@ static vpx_image_t *vp9_get_frame(vpx_codec_alg_priv_t *ctx, return img; } +static vpx_codec_err_t vp9_set_frame_buffers( + vpx_codec_alg_priv_t *ctx, + vpx_codec_frame_buffer_t *fb_list, int fb_count, + vpx_realloc_frame_buffer_cb_fn_t cb, void *user_priv) { + if (fb_count < REF_FRAMES) { + /* The application must pass in at least REF_FRAMES frame buffers. */ + return VPX_CODEC_INVALID_PARAM; + } else if (!ctx->pbi) { + /* If the decoder has already been initialized, do not accept external + * frame buffers. + */ + ctx->fb_list = fb_list; + ctx->fb_count = fb_count; + ctx->realloc_fb_cb = cb; + ctx->user_priv = user_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) { @@ -639,7 +682,7 @@ static vpx_codec_err_t get_last_ref_updates(vpx_codec_alg_priv_t *ctx, int ctrl_id, va_list args) { int *update_info = va_arg(args, int *); - VP9D_COMP *pbi = (VP9D_COMP *)ctx->pbi; + VP9D_COMP *pbi = (VP9D_COMP*)ctx->pbi; if (update_info) { *update_info = pbi->refresh_frame_flags; @@ -657,7 +700,7 @@ static vpx_codec_err_t get_frame_corrupted(vpx_codec_alg_priv_t *ctx, int *corrupted = va_arg(args, int *); if (corrupted) { - VP9D_COMP *pbi = (VP9D_COMP *)ctx->pbi; + VP9D_COMP *pbi = (VP9D_COMP*)ctx->pbi; if (pbi) *corrupted = pbi->common.frame_to_show->corrupted; else @@ -674,7 +717,7 @@ static vpx_codec_err_t get_display_size(vpx_codec_alg_priv_t *ctx, int *const display_size = va_arg(args, int *); if (display_size) { - const VP9D_COMP *const pbi = (VP9D_COMP *)ctx->pbi; + const VP9D_COMP *const pbi = (VP9D_COMP*)ctx->pbi; if (pbi) { display_size[0] = pbi->common.display_width; display_size[1] = pbi->common.display_height; @@ -717,7 +760,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; */ @@ -729,6 +773,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_frame_buffers, /* vpx_codec_set_frame_buffers_fn_t set_fb; */ }, { // NOLINT /* encoder functions */ diff --git a/vpx/exports_dec b/vpx/exports_dec index ed121f7ec..d058c9bdb 100644 --- a/vpx/exports_dec +++ b/vpx/exports_dec @@ -7,3 +7,4 @@ text vpx_codec_peek_stream_info text vpx_codec_register_put_frame_cb text vpx_codec_register_put_slice_cb text vpx_codec_set_mem_map +text vpx_codec_set_frame_buffers diff --git a/vpx/internal/vpx_codec_internal.h b/vpx/internal/vpx_codec_internal.h index 05fed977e..75b4a1877 100644 --- a/vpx/internal/vpx_codec_internal.h +++ b/vpx/internal/vpx_codec_internal.h @@ -56,7 +56,7 @@ * 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; @@ -215,6 +215,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 a given function to be called when the current frame to + * decode will be bigger than the external frame buffer size. This + * function must be called before the first call to decode or libvpx + * will assume the default behavior of allocating frame buffers internally. + * Frame buffers with a size of 0 are valid. + * + * \param[in] ctx Pointer to this instance's context + * \param[in] fb_list Pointer to array of frame buffers + * \param[in] fb_count Number of elements in frame buffer array + * \param[in] cb Pointer to the callback function + * \param[in] user_priv User's private data + * + * \retval #VPX_CODEC_OK + * External frame buffers will be used by libvpx. + * \retval #VPX_CODEC_INVALID_PARAM + * fb_count was less than the value needed by the codec. + * \retval #VPX_CODEC_ERROR + * Decoder context not initialized, or algorithm not capable of + * using external frame buffers. + * + * \note + * When decoding VP9, the application must pass in at least 8 external + * frame buffers, as VP9 can have up to 8 reference frames. + */ +typedef vpx_codec_err_t (*vpx_codec_set_frame_buffers_fn_t)( + vpx_codec_alg_priv_t *ctx, + vpx_codec_frame_buffer_t *fb_list, int fb_count, + vpx_realloc_frame_buffer_cb_fn_t cb, void *user_priv); /*\brief eXternal Memory Allocation memory map get iterator * @@ -305,6 +335,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_frame_buffers_fn_t set_fb; /**< \copydoc ::vpx_codec_set_frame_buffers_fn_t */ } dec; struct vpx_codec_enc_iface { vpx_codec_enc_cfg_map_t *cfg_maps; /**< \copydoc ::vpx_codec_enc_cfg_map_t */ diff --git a/vpx/src/vpx_decoder.c b/vpx/src/vpx_decoder.c index a99e48f88..39fd217ea 100644 --- a/vpx/src/vpx_decoder.c +++ b/vpx/src/vpx_decoder.c @@ -226,3 +226,22 @@ 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_buffers( + vpx_codec_ctx_t *ctx, + vpx_codec_frame_buffer_t *fb_list, int fb_count, + vpx_realloc_frame_buffer_cb_fn_t cb, void *user_priv) { + vpx_codec_err_t res; + + if (!ctx || !fb_list || fb_count <= 0 || !cb) { + 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(ctx->priv->alg_priv, fb_list, fb_count, + cb, user_priv); + } + + return SAVE_STATUS(ctx, res); +} diff --git a/vpx/vpx_codec.mk b/vpx/vpx_codec.mk index 549c24908..df3ff6ef1 100644 --- a/vpx/vpx_codec.mk +++ b/vpx/vpx_codec.mk @@ -27,6 +27,7 @@ API_DOC_SRCS-yes += vpx_codec.h API_DOC_SRCS-yes += vpx_decoder.h API_DOC_SRCS-yes += vpx_encoder.h API_DOC_SRCS-yes += vpx_image.h +API_DOC_SRCS-yes += vpx_external_frame_buffer.h API_SRCS-yes += src/vpx_decoder.c API_SRCS-yes += vpx_decoder.h @@ -38,4 +39,5 @@ API_SRCS-yes += src/vpx_image.c API_SRCS-yes += vpx_codec.h API_SRCS-yes += vpx_codec.mk API_SRCS-yes += vpx_image.h +API_SRCS-yes += vpx_external_frame_buffer.h API_SRCS-$(BUILD_LIBVPX) += vpx_integer.h diff --git a/vpx/vpx_decoder.h b/vpx/vpx_decoder.h index 2dcd024cc..08f7f434e 100644 --- a/vpx/vpx_decoder.h +++ b/vpx/vpx_decoder.h @@ -30,6 +30,7 @@ extern "C" { #endif #include "vpx_codec.h" +#include "vpx_external_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,49 @@ 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 a given function to be called when the current frame to + * decode will be bigger than the external frame buffer size. This + * function must be called before the first call to decode or libvpx + * will assume the default behavior of allocating frame buffers internally. + * Frame buffers with a size of 0 are valid. + * + * \param[in] ctx Pointer to this instance's context + * \param[in] fb_list Pointer to array of frame buffers + * \param[in] fb_count Number of elements in frame buffer array + * \param[in] cb Pointer to the callback function + * \param[in] user_priv User's private data + * + * \retval #VPX_CODEC_OK + * External frame buffers passed into the decoder. + * \retval #VPX_CODEC_ERROR + * Decoder context not initialized, or algorithm not capable of + * using external frame buffers. + * + * \note + * When decoding VP9, the application must pass in at least 8 external + * frame buffers, as VP9 can have up to 8 reference frames. + */ + vpx_codec_err_t vpx_codec_set_frame_buffers( + vpx_codec_ctx_t *ctx, + vpx_codec_frame_buffer_t *fb_list, int fb_count, + vpx_realloc_frame_buffer_cb_fn_t cb, void *user_priv); + + /*!@} - end defgroup cap_external_frame_buffer */ + /*!@} - end defgroup decoder*/ #ifdef __cplusplus } diff --git a/vpx/vpx_external_frame_buffer.h b/vpx/vpx_external_frame_buffer.h new file mode 100644 index 000000000..adf133042 --- /dev/null +++ b/vpx/vpx_external_frame_buffer.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef VPX_VPX_EXTERNAL_FRAME_BUFFER_H_ +#define VPX_VPX_EXTERNAL_FRAME_BUFFER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "vpx/vpx_integer.h" + +/*!\brief External frame buffer + * + * This structure is used to hold external frame buffers passed into the + * decoder by the application. + */ +typedef struct vpx_codec_frame_buffer { + uint8_t *data; /**< Pointer to the data buffer */ + size_t size; /**< Size of data in bytes */ + void *frame_priv; /**< Frame's private data */ +} vpx_codec_frame_buffer_t; + +/*!\brief realloc frame buffer callback prototype + * + * This callback is invoked by the decoder to notify the application one + * of the external frame buffers must increase in size, in order for the + * decode call to complete. The callback must allocate at least new_size in + * bytes and assign it to fb->data. Then the callback must set fb->size to + * the allocated size. The application does not need to align the allocated + * data. The callback is usually triggered by a frame size change. On success + * the callback must return 0. Any failure the callback must return a value + * less than 0. + * + * \param[in] user_priv User's private data + * \param[in] new_size Size in bytes needed by the buffer. + * \param[in/out] fb Pointer to frame buffer to increase size. + */ +typedef int (*vpx_realloc_frame_buffer_cb_fn_t)( + void *user_priv, size_t new_size, vpx_codec_frame_buffer_t *fb); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // VPX_VPX_EXTERNAL_FRAME_BUFFER_H_ diff --git a/vpx_scale/generic/yv12config.c b/vpx_scale/generic/yv12config.c index 7c3f7ece9..a020e19b7 100644 --- a/vpx_scale/generic/yv12config.c +++ b/vpx_scale/generic/yv12config.c @@ -19,10 +19,18 @@ /**************************************************************************** * ****************************************************************************/ + +#define yv12_align_addr(addr, align) \ + (void*)(((size_t)(addr) + ((align) - 1)) & (size_t)-(align)) + int vp8_yv12_de_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf) { if (ybf) { - vpx_free(ybf->buffer_alloc); + // If libvpx is using external frame buffers then buffer_alloc_sz must + // not be set. + if (ybf->buffer_alloc_sz > 0) { + vpx_free(ybf->buffer_alloc); + } /* buffer_alloc isn't accessed by most functions. Rather y_buffer, u_buffer and v_buffer point to buffer_alloc and are used. Clear out @@ -108,7 +116,9 @@ int vp8_yv12_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int vp9_free_frame_buffer(YV12_BUFFER_CONFIG *ybf) { if (ybf) { - vpx_free(ybf->buffer_alloc); + if (ybf->buffer_alloc_sz > 0) { + vpx_free(ybf->buffer_alloc); + } /* buffer_alloc isn't accessed by most functions. Rather y_buffer, u_buffer and v_buffer point to buffer_alloc and are used. Clear out @@ -123,7 +133,10 @@ int vp9_free_frame_buffer(YV12_BUFFER_CONFIG *ybf) { int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, - int ss_x, int ss_y, int border) { + int ss_x, int ss_y, int border, + vpx_codec_frame_buffer_t *ext_fb, + vpx_realloc_frame_buffer_cb_fn_t cb, + void *user_priv) { if (ybf) { const int aligned_width = (width + 7) & ~7; const int aligned_height = (height + 7) & ~7; @@ -148,15 +161,36 @@ int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, #else const int frame_size = yplane_size + 2 * uvplane_size; #endif - if (frame_size > ybf->buffer_alloc_sz) { - // Allocation to hold larger frame, or first allocation. - if (ybf->buffer_alloc) - vpx_free(ybf->buffer_alloc); - ybf->buffer_alloc = vpx_memalign(32, frame_size); - ybf->buffer_alloc_sz = frame_size; + + if (ext_fb != NULL) { + const int align_addr_extra_size = 31; + const int external_frame_size = frame_size + align_addr_extra_size; + if (external_frame_size > ext_fb->size) { + // Allocation to hold larger frame, or first allocation. + if (cb(user_priv, external_frame_size, ext_fb) < 0) { + return -1; + } + + if (ext_fb->data == NULL || ext_fb->size < external_frame_size) { + return -1; + } + + ybf->buffer_alloc = yv12_align_addr(ext_fb->data, 32); + } + } else { + if (frame_size > ybf->buffer_alloc_sz) { + // Allocation to hold larger frame, or first allocation. + if (ybf->buffer_alloc) + vpx_free(ybf->buffer_alloc); + ybf->buffer_alloc = vpx_memalign(32, frame_size); + ybf->buffer_alloc_sz = frame_size; + } + + if (ybf->buffer_alloc_sz < frame_size) + return -1; } - if (!ybf->buffer_alloc || ybf->buffer_alloc_sz < frame_size) + if (!ybf->buffer_alloc) return -1; /* Only support allocating buffers that have a border that's a multiple @@ -206,7 +240,8 @@ int vp9_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int ss_x, int ss_y, int border) { if (ybf) { vp9_free_frame_buffer(ybf); - return vp9_realloc_frame_buffer(ybf, width, height, ss_x, ss_y, border); + return vp9_realloc_frame_buffer(ybf, width, height, ss_x, ss_y, border, + NULL, NULL, NULL); } return -2; } diff --git a/vpx_scale/yv12config.h b/vpx_scale/yv12config.h index 0e950fb14..f23e1163c 100644 --- a/vpx_scale/yv12config.h +++ b/vpx_scale/yv12config.h @@ -15,6 +15,7 @@ extern "C" { #endif +#include "vpx/vpx_external_frame_buffer.h" #include "vpx/vpx_integer.h" #define VP8BORDERINPIXELS 32 @@ -64,9 +65,20 @@ extern "C" { int vp9_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, int ss_x, int ss_y, int border); + + // Updates the yv12 buffer config with the frame buffer. If ext_fb is not + // NULL then libvpx is using external frame buffers. The function will + // check if the frame buffer is big enough to fit the decoded frame and + // try to reallocate the frame buffer. If ext_fb is not NULL and the frame + // buffer is not big enough libvpx will call cb with minimum size in bytes. + // + // Returns 0 on success. Returns < 0 on failure. int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, int ss_x, int ss_y, - int border); + int border, + vpx_codec_frame_buffer_t *ext_fb, + vpx_realloc_frame_buffer_cb_fn_t cb, + void *user_priv); int vp9_free_frame_buffer(YV12_BUFFER_CONFIG *ybf); #ifdef __cplusplus @@ -89,6 +89,8 @@ static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0, "Enable decoder error-concealment"); 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"); #if CONFIG_MD5 @@ -98,7 +100,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, #if CONFIG_MD5 &md5arg, #endif @@ -314,6 +316,31 @@ void show_progress(int frame_in, int frame_out, unsigned long dx_time) { (float)frame_out * 1000000.0 / (float)dx_time); } +// Called by libvpx if the frame buffer size needs to increase. +// +// Parameters: +// user_priv Data passed into libvpx. +// new_size Minimum size needed by libvpx to decompress the next frame. +// fb Pointer to the frame buffer to update. +// +// Returns 0 on success. Returns < 0 on failure. +int realloc_vp9_frame_buffer(void *user_priv, size_t new_size, + vpx_codec_frame_buffer_t *fb) { + (void)user_priv; + if (!fb) + return -1; + + free(fb->data); + fb->data = (uint8_t*)malloc(new_size); + if (!fb->data) { + fb->size = 0; + return -1; + } + + fb->size = new_size; + 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) { @@ -428,6 +455,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; + vpx_codec_frame_buffer_t *frame_buffers = NULL; struct VpxDecInputContext input = {0}; struct VpxInputContext vpx_input_ctx = {0}; @@ -487,6 +516,9 @@ 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)) { @@ -704,6 +736,22 @@ int main_loop(int argc, const char **argv_) { arg_skip--; } + if (num_external_frame_buffers > 0) { + // Allocate the frame buffer list, setting all of the values to 0. + // Including the size of frame buffers. Libvpx will request the + // application to realloc the frame buffer data if the size is too small. + frame_buffers = (vpx_codec_frame_buffer_t*)calloc( + num_external_frame_buffers, sizeof(*frame_buffers)); + if (vpx_codec_set_frame_buffers(&decoder, frame_buffers, + num_external_frame_buffers, + realloc_vp9_frame_buffer, + NULL)) { + fprintf(stderr, "Failed to configure external frame buffers: %s\n", + vpx_codec_error(&decoder)); + return EXIT_FAILURE; + } + } + frame_avail = 1; got_data = 0; @@ -878,6 +926,10 @@ fail: free(buf); if (scaled_img) vpx_img_free(scaled_img); + for (i = 0; i < num_external_frame_buffers; ++i) { + free(frame_buffers[i].data); + } + free(frame_buffers); fclose(infile); free(argv); |