Add frame buffer lru cache.
Add an option for libvpx to return the least recently used frame buffer. Change-Id: I886a96ffb94984f1c42de53086e0131922df3260
This commit is contained in:
parent
d0ee1fd797
commit
fbada948fa
207
test/lru_frame_buffer_test.cc
Normal file
207
test/lru_frame_buffer_test.cc
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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 <queue>
|
||||
#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/util.h"
|
||||
#include "test/webm_video_source.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int kVideoNameParam = 1;
|
||||
|
||||
const char *kLRUTestVectors[] = {
|
||||
"vp90-2-02-size-lf-1920x1080.webm",
|
||||
"vp90-2-05-resize.ivf",
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Class for testing libvpx is using the least recently
|
||||
// used frame buffer when a new buffer is requested.
|
||||
class LRUFrameBufferTest
|
||||
: public ::libvpx_test::DecoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<const char*> {
|
||||
protected:
|
||||
struct FrameBufferMD5Sum {
|
||||
int frame_buffer_index;
|
||||
vpx_image_t img;
|
||||
std::string md5;
|
||||
};
|
||||
|
||||
LRUFrameBufferTest()
|
||||
: DecoderTest(GET_PARAM(::libvpx_test::kCodecFactoryParam)),
|
||||
num_buffers_(0),
|
||||
num_jitter_buffers_(0),
|
||||
frame_buffers_(NULL) {}
|
||||
|
||||
virtual ~LRUFrameBufferTest() {
|
||||
for (int i = 0; i < num_buffers_; ++i) {
|
||||
delete [] frame_buffers_[i].data;
|
||||
}
|
||||
delete [] frame_buffers_;
|
||||
}
|
||||
|
||||
virtual void PreDecodeFrameHook(
|
||||
const libvpx_test::CompressedVideoSource &video,
|
||||
libvpx_test::Decoder *decoder) {
|
||||
// Use external buffers for testing jitter buffers.
|
||||
if (num_jitter_buffers_ > 0 && video.frame_number() == 0) {
|
||||
const int max_reference_buffers = 8;
|
||||
|
||||
// Add 1 for a work buffer.
|
||||
num_buffers_ = max_reference_buffers + 1 + num_jitter_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_);
|
||||
|
||||
decoder->SetExternalFrameBuffers(frame_buffers_, num_buffers_,
|
||||
realloc_vp9_frame_buffer, NULL);
|
||||
}
|
||||
|
||||
// Turn on frame buffer LRU cache.
|
||||
decoder->Control(VP9D_SET_FRAME_BUFFER_LRU_CACHE, 1);
|
||||
}
|
||||
|
||||
virtual void DecompressedFrameHook(const vpx_image_t &img,
|
||||
const unsigned int frame_number) {
|
||||
const uint32_t ximg_y_plane = 0;
|
||||
const uint8_t *const y_buffer = img.planes[ximg_y_plane];
|
||||
|
||||
// Find which external buffer contains the y_buffer.
|
||||
int i = 0;
|
||||
for (i = 0; i < num_buffers_; ++i) {
|
||||
if (y_buffer >= frame_buffers_[i].data &&
|
||||
y_buffer < (frame_buffers_[i].data + frame_buffers_[i].size)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FrameBufferMD5Sum fb_md5;
|
||||
fb_md5.frame_buffer_index = i;
|
||||
fb_md5.img = img;
|
||||
|
||||
libvpx_test::MD5 md5;
|
||||
md5.Add(&img);
|
||||
fb_md5.md5 = md5.Get();
|
||||
jitter_buffer_md5_sums_.push(fb_md5);
|
||||
|
||||
// Check to see if any of the reconstructed image changed.
|
||||
if (jitter_buffer_md5_sums_.size() >
|
||||
static_cast<size_t>(num_jitter_buffers_)) {
|
||||
fb_md5 = jitter_buffer_md5_sums_.front();
|
||||
|
||||
libvpx_test::MD5 md5;
|
||||
md5.Add(&fb_md5.img);
|
||||
const std::string check_str = md5.Get();
|
||||
|
||||
ASSERT_EQ(fb_md5.md5, check_str);
|
||||
jitter_buffer_md5_sums_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
libvpx_test::CompressedVideoSource *OpenCompressedFile(
|
||||
const std::string &filename) {
|
||||
if (filename.substr(filename.length() - 3, 3) == "ivf") {
|
||||
return new libvpx_test::IVFVideoSource(filename);
|
||||
} else if (filename.substr(filename.length() - 4, 4) == "webm") {
|
||||
return new libvpx_test::WebMVideoSource(filename);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void set_num_jitter_buffers(int num_buffers) {
|
||||
num_jitter_buffers_ = num_buffers;
|
||||
}
|
||||
|
||||
private:
|
||||
// Total number of external frame buffers.
|
||||
int num_buffers_;
|
||||
int num_jitter_buffers_;
|
||||
|
||||
// External frame buffers used by libvpx.
|
||||
vpx_codec_frame_buffer_t *frame_buffers_;
|
||||
|
||||
// Save the md5 checksums for later comparison.
|
||||
std::queue<FrameBufferMD5Sum> jitter_buffer_md5_sums_;
|
||||
};
|
||||
|
||||
// This test runs through a 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 after it is
|
||||
// decoded and stored to be checked later. After a jitter frame buffer
|
||||
// has expired, the md5 checksum is computed again for the expired jitter
|
||||
// buffer frame and checked against the md5 checksum after the frame was
|
||||
// decoded. If md5 checksums match, then the test is passed. Otherwise,
|
||||
// the test failed.
|
||||
TEST_P(LRUFrameBufferTest, CheckLRUOneJitterBuffer) {
|
||||
const std::string filename = GET_PARAM(kVideoNameParam);
|
||||
|
||||
set_num_jitter_buffers(1);
|
||||
|
||||
libvpx_test::CompressedVideoSource *const video =
|
||||
OpenCompressedFile(filename);
|
||||
video->Init();
|
||||
|
||||
// Decode frame, and check the md5 matching.
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video));
|
||||
delete video;
|
||||
}
|
||||
|
||||
TEST_P(LRUFrameBufferTest, CheckLRUFourJitterBuffers) {
|
||||
const std::string filename = GET_PARAM(kVideoNameParam);
|
||||
|
||||
set_num_jitter_buffers(4);
|
||||
|
||||
libvpx_test::CompressedVideoSource *const video =
|
||||
OpenCompressedFile(filename);
|
||||
video->Init();
|
||||
|
||||
// Decode frame, and check the md5 matching.
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video));
|
||||
delete video;
|
||||
}
|
||||
|
||||
TEST_P(LRUFrameBufferTest, CheckLRUEightJitterBuffers) {
|
||||
const std::string filename = GET_PARAM(kVideoNameParam);
|
||||
|
||||
set_num_jitter_buffers(8);
|
||||
|
||||
libvpx_test::CompressedVideoSource *const video =
|
||||
OpenCompressedFile(filename);
|
||||
video->Init();
|
||||
|
||||
// Decode frame, and check the md5 matching.
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video));
|
||||
delete video;
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(LRUFrameBufferTest,
|
||||
::testing::ValuesIn(kLRUTestVectors));
|
||||
} // namespace
|
@ -35,6 +35,7 @@ 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
|
||||
LIBVPX_TEST_SRCS-$(CONFIG_VP9_DECODER) += lru_frame_buffer_test.cc
|
||||
|
||||
## WebM Parsing
|
||||
NESTEGG_SRCS += ../nestegg/halloc/halloc.h
|
||||
|
@ -144,6 +144,10 @@ int vp9_alloc_frame_buffers(VP9_COMMON *cm, int width, int height) {
|
||||
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)));
|
||||
if (cm->fb_lru) {
|
||||
CHECK_MEM_ERROR(cm, cm->fb_idx_ref_lru,
|
||||
vpx_calloc(cm->fb_count, sizeof(*cm->fb_idx_ref_lru)));
|
||||
}
|
||||
}
|
||||
|
||||
vp9_free_frame_buffers(cm);
|
||||
@ -214,9 +218,11 @@ void vp9_remove_common(VP9_COMMON *cm) {
|
||||
|
||||
vpx_free(cm->yv12_fb);
|
||||
vpx_free(cm->fb_idx_ref_cnt);
|
||||
vpx_free(cm->fb_idx_ref_lru);
|
||||
|
||||
cm->yv12_fb = NULL;
|
||||
cm->fb_idx_ref_cnt = NULL;
|
||||
cm->fb_idx_ref_lru = NULL;
|
||||
}
|
||||
|
||||
void vp9_initialize_common() {
|
||||
|
@ -218,6 +218,10 @@ typedef struct VP9Common {
|
||||
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.
|
||||
|
||||
int fb_lru; // Flag telling if lru is on/off
|
||||
uint32_t *fb_idx_ref_lru; // Frame buffer lru cache
|
||||
uint32_t fb_idx_ref_lru_count;
|
||||
} VP9_COMMON;
|
||||
|
||||
// ref == 0 => LAST_FRAME
|
||||
@ -233,13 +237,27 @@ 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 < cm->fb_count; i++)
|
||||
if (cm->fb_idx_ref_cnt[i] == 0)
|
||||
break;
|
||||
uint32_t lru_count = cm->fb_idx_ref_lru_count + 1;
|
||||
int free_buffer_idx = cm->fb_count;
|
||||
for (i = 0; i < cm->fb_count; i++) {
|
||||
if (!cm->fb_lru) {
|
||||
if (cm->fb_idx_ref_cnt[i] == 0) {
|
||||
free_buffer_idx = i;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (cm->fb_idx_ref_cnt[i] == 0 && cm->fb_idx_ref_lru[i] < lru_count) {
|
||||
free_buffer_idx = i;
|
||||
lru_count = cm->fb_idx_ref_lru[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < cm->fb_count);
|
||||
cm->fb_idx_ref_cnt[i] = 1;
|
||||
return i;
|
||||
assert(free_buffer_idx < cm->fb_count);
|
||||
cm->fb_idx_ref_cnt[free_buffer_idx] = 1;
|
||||
if (cm->fb_lru)
|
||||
cm->fb_idx_ref_lru[free_buffer_idx] = ++cm->fb_idx_ref_lru_count;
|
||||
return free_buffer_idx;
|
||||
}
|
||||
|
||||
static void ref_cnt_fb(int *buf, int *idx, int new_idx) {
|
||||
|
@ -59,6 +59,7 @@ struct vpx_codec_alg_priv {
|
||||
int img_setup;
|
||||
int img_avail;
|
||||
int invert_tile_order;
|
||||
int fb_lru;
|
||||
|
||||
/* External buffer info to save for VP9 common. */
|
||||
vpx_codec_frame_buffer_t *fb_list; // External frame buffers
|
||||
@ -327,10 +328,16 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx,
|
||||
} else {
|
||||
cm->fb_count = FRAME_BUFFERS;
|
||||
}
|
||||
cm->fb_lru = ctx->fb_lru;
|
||||
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)));
|
||||
if (cm->fb_lru) {
|
||||
CHECK_MEM_ERROR(cm, cm->fb_idx_ref_lru,
|
||||
vpx_calloc(cm->fb_count,
|
||||
sizeof(*cm->fb_idx_ref_lru)));
|
||||
}
|
||||
ctx->pbi = optr;
|
||||
}
|
||||
}
|
||||
@ -737,6 +744,21 @@ static vpx_codec_err_t set_invert_tile_order(vpx_codec_alg_priv_t *ctx,
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t set_frame_buffer_lru_cache(vpx_codec_alg_priv_t *ctx,
|
||||
int ctr_id,
|
||||
va_list args) {
|
||||
VP9D_COMP *const pbi = (VP9D_COMP*)ctx->pbi;
|
||||
|
||||
// Save for later to pass into vp9 common.
|
||||
ctx->fb_lru = va_arg(args, int);
|
||||
|
||||
if (pbi) {
|
||||
VP9_COMMON *const cm = &pbi->common;
|
||||
cm->fb_lru = ctx->fb_lru;
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
static vpx_codec_ctrl_fn_map_t ctf_maps[] = {
|
||||
{VP8_SET_REFERENCE, set_reference},
|
||||
{VP8_COPY_REFERENCE, copy_reference},
|
||||
@ -750,6 +772,7 @@ static vpx_codec_ctrl_fn_map_t ctf_maps[] = {
|
||||
{VP9_GET_REFERENCE, get_reference},
|
||||
{VP9D_GET_DISPLAY_SIZE, get_display_size},
|
||||
{VP9_INVERT_TILE_DECODE_ORDER, set_invert_tile_order},
|
||||
{VP9D_SET_FRAME_BUFFER_LRU_CACHE, set_frame_buffer_lru_cache},
|
||||
{ -1, NULL},
|
||||
};
|
||||
|
||||
|
@ -79,6 +79,13 @@ enum vp8_dec_control_id {
|
||||
/** For testing. */
|
||||
VP9_INVERT_TILE_DECODE_ORDER,
|
||||
|
||||
/** control function to set the vp9 decoder into using the least recently
|
||||
* used frame buffer when a new buffer is requested. Takes an int and if
|
||||
* the value is zero will turn off using lru cache. The value of zero is
|
||||
* the default. If the value is anything besides zero, then that will turn
|
||||
* on lru cache.*/
|
||||
VP9D_SET_FRAME_BUFFER_LRU_CACHE,
|
||||
|
||||
VP8_DECODER_CTRL_ID_MAX
|
||||
};
|
||||
|
||||
@ -110,6 +117,7 @@ VPX_CTRL_USE_TYPE(VP8D_GET_LAST_REF_USED, int *)
|
||||
VPX_CTRL_USE_TYPE(VP8D_SET_DECRYPTOR, vp8_decrypt_init *)
|
||||
VPX_CTRL_USE_TYPE(VP9D_GET_DISPLAY_SIZE, int *)
|
||||
VPX_CTRL_USE_TYPE(VP9_INVERT_TILE_DECODE_ORDER, int)
|
||||
VPX_CTRL_USE_TYPE(VP9D_SET_FRAME_BUFFER_LRU_CACHE, int)
|
||||
|
||||
/*! @} - end defgroup vp8_decoder */
|
||||
|
||||
|
16
vpxdec.c
16
vpxdec.c
@ -91,6 +91,8 @@ 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 fb_lru_arg =
|
||||
ARG_DEF(NULL, "frame-buffers-lru", 1, "Turn on/off frame buffer lru");
|
||||
|
||||
|
||||
#if CONFIG_MD5
|
||||
@ -100,7 +102,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, &fb_arg,
|
||||
&threadsarg, &verbosearg, &scalearg, &fb_arg, &fb_lru_arg,
|
||||
#if CONFIG_MD5
|
||||
&md5arg,
|
||||
#endif
|
||||
@ -456,6 +458,7 @@ int main_loop(int argc, const char **argv_) {
|
||||
vpx_image_t *scaled_img = NULL;
|
||||
int frame_avail, got_data;
|
||||
int num_external_frame_buffers = 0;
|
||||
int fb_lru_cache = 0;
|
||||
vpx_codec_frame_buffer_t *frame_buffers = NULL;
|
||||
|
||||
struct VpxDecInputContext input = {0};
|
||||
@ -518,7 +521,8 @@ int main_loop(int argc, const char **argv_) {
|
||||
do_scale = 1;
|
||||
else if (arg_match(&arg, &fb_arg, argi))
|
||||
num_external_frame_buffers = arg_parse_uint(&arg);
|
||||
|
||||
else if (arg_match(&arg, &fb_lru_arg, argi))
|
||||
fb_lru_cache = arg_parse_uint(&arg);
|
||||
|
||||
#if CONFIG_VP8_DECODER
|
||||
else if (arg_match(&arg, &addnoise_level, argi)) {
|
||||
@ -752,6 +756,14 @@ int main_loop(int argc, const char **argv_) {
|
||||
}
|
||||
}
|
||||
|
||||
if (fb_lru_cache > 0 &&
|
||||
vpx_codec_control(&decoder, VP9D_SET_FRAME_BUFFER_LRU_CACHE,
|
||||
fb_lru_cache)) {
|
||||
fprintf(stderr, "Failed to set frame buffer lru cache: %s\n",
|
||||
vpx_codec_error(&decoder));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
frame_avail = 1;
|
||||
got_data = 0;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user