Commit fbada948 authored by Frank Galligan's avatar Frank Galligan Committed by James Zern

Add frame buffer lru cache.

Add an option for libvpx to return the least recently used
frame buffer.

Change-Id: I886a96ffb94984f1c42de53086e0131922df3260
parent d0ee1fd7
/*
* 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 */
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment