Add 'superframe' index

A 'superframe' is a group of frames that share the same PTS, but have a
defined decoding order. This commit adds the ability to append an index
to such a group of frames, allowing for random access to the constituent
frames. This could be useful for frame-level parallelism or partial
decoding in a multilayer scenario.

Decoding the stream serially without such an index should work as a
fallback, and VP9/TestSuperframeIndexIsOptional verifies that.

Change-Id: Idff83b7560e1a7077d8fb067bfbc45b567e78b1c
This commit is contained in:
John Koleszar 2013-03-05 12:23:34 -08:00
parent 4209bba462
commit 522d4bf852
7 changed files with 227 additions and 2 deletions

View File

@ -169,6 +169,7 @@ void EncoderTest::RunLoop(VideoSource *video) {
bool has_cxdata = false;
bool has_dxdata = false;
while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) {
pkt = MutateEncoderOutputHook(pkt);
again = true;
switch (pkt->kind) {
case VPX_CODEC_CX_FRAME_PKT:

View File

@ -203,6 +203,12 @@ class EncoderTest {
virtual void DecompressedFrameHook(const vpx_image_t& img,
vpx_codec_pts_t pts) {}
// Hook that can modify the encoder's output data
virtual const vpx_codec_cx_pkt_t * MutateEncoderOutputHook(
const vpx_codec_cx_pkt_t *pkt) {
return pkt;
}
bool abort_;
vpx_codec_enc_cfg_t cfg_;
unsigned int passes_;

100
test/superframe_test.cc Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2012 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 <climits>
#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
#include "test/util.h"
namespace {
class SuperframeTest : public ::libvpx_test::EncoderTest,
public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
protected:
SuperframeTest() : EncoderTest(GET_PARAM(0)), modified_buf_(NULL),
last_sf_pts_(0) {}
virtual void SetUp() {
InitializeConfig();
SetMode(GET_PARAM(1));
sf_count_ = 0;
sf_count_max_ = INT_MAX;
}
virtual void TearDown() {
delete modified_buf_;
}
virtual bool Continue() const {
return !HasFatalFailure() && !abort_;
}
virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
libvpx_test::Encoder *encoder) {
if (video->frame() == 1) {
encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1);
}
}
virtual const vpx_codec_cx_pkt_t * MutateEncoderOutputHook(
const vpx_codec_cx_pkt_t *pkt) {
if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
return pkt;
const uint8_t *buffer = reinterpret_cast<uint8_t*>(pkt->data.frame.buf);
const uint8_t marker = buffer[pkt->data.frame.sz - 1];
const int frames = (marker & 0x7) + 1;
const int mag = ((marker >> 3) & 3) + 1;
const unsigned int index_sz = 2 + mag * frames;
if ((marker & 0xf0) == 0xc0 &&
pkt->data.frame.sz >= index_sz &&
buffer[pkt->data.frame.sz - index_sz] == marker) {
// frame is a superframe. strip off the index.
if (modified_buf_)
delete modified_buf_;
modified_buf_ = new uint8_t[pkt->data.frame.sz - index_sz];
memcpy(modified_buf_, pkt->data.frame.buf,
pkt->data.frame.sz - index_sz);
modified_pkt_ = *pkt;
modified_pkt_.data.frame.buf = modified_buf_;
modified_pkt_.data.frame.sz -= index_sz;
sf_count_++;
last_sf_pts_ = pkt->data.frame.pts;
return &modified_pkt_;
}
// Make sure we do a few frames after the last SF
abort_ |= sf_count_ > sf_count_max_ &&
pkt->data.frame.pts - last_sf_pts_ >= 5;
return pkt;
}
int sf_count_;
int sf_count_max_;
vpx_codec_cx_pkt_t modified_pkt_;
uint8_t *modified_buf_;
vpx_codec_pts_t last_sf_pts_;
};
TEST_P(SuperframeTest, TestSuperframeIndexIsOptional) {
sf_count_max_ = 0; // early exit on successful test.
cfg_.g_lag_in_frames = 25;
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
30, 1, 0, 40);
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
EXPECT_EQ(sf_count_, 1);
}
VP9_INSTANTIATE_TEST_CASE(SuperframeTest, ::testing::Values(
::libvpx_test::kTwoPassGood));
} // namespace

View File

@ -68,6 +68,7 @@ LIBVPX_TEST_SRCS-yes += vp9_boolcoder_test.cc
# IDCT test currently depends on FDCT function
LIBVPX_TEST_SRCS-yes += idct8x8_test.cc
LIBVPX_TEST_SRCS-yes += superframe_test.cc
LIBVPX_TEST_SRCS-yes += tile_independence_test.cc
endif

View File

@ -4009,7 +4009,7 @@ int vp9_get_preview_raw_frame(VP9_PTR comp, YV12_BUFFER_CONFIG *dest,
vp9_ppflags_t *flags) {
VP9_COMP *cpi = (VP9_COMP *) comp;
if (cpi->refresh_alt_ref_frame)
if (!cpi->common.show_frame)
return -1;
else {
int ret;

View File

@ -77,6 +77,9 @@ struct vpx_codec_alg_priv {
unsigned int cx_data_sz;
unsigned char *pending_cx_data;
unsigned int pending_cx_data_sz;
int pending_frame_count;
uint32_t pending_frame_sizes[8];
uint32_t pending_frame_magnitude;
vpx_image_t preview_img;
vp8_postproc_cfg_t preview_ppcfg;
vpx_codec_pkt_list_decl(64) pkt_list; // changed to accomendate the maximum number of lagged frames allowed
@ -579,6 +582,45 @@ static void pick_quickcompress_mode(vpx_codec_alg_priv_t *ctx,
}
static void write_superframe_index(vpx_codec_alg_priv_t *ctx) {
uint8_t marker = 0xc0;
int mag, mask, index_sz;
assert(ctx->pending_frame_count);
assert(ctx->pending_frame_count <= 8);
/* Add the number of frames to the marker byte */
marker |= ctx->pending_frame_count - 1;
/* Choose the magnitude */
for (mag = 0, mask = 0xff; mag < 4; mag++) {
if (ctx->pending_frame_magnitude < mask)
break;
mask <<= 8;
mask |= 0xff;
}
marker |= mag << 3;
/* Write the index */
index_sz = 2 + (mag + 1) * ctx->pending_frame_count;
if (ctx->pending_cx_data_sz + index_sz < ctx->cx_data_sz) {
uint8_t *x = ctx->pending_cx_data + ctx->pending_cx_data_sz;
int i, j;
*x++ = marker;
for (i = 0; i < ctx->pending_frame_count; i++) {
int this_sz = ctx->pending_frame_sizes[i];
for (j = 0; j <= mag; j++) {
*x++ = this_sz & 0xff;
this_sz >>= 8;
}
}
*x++ = marker;
ctx->pending_cx_data_sz += index_sz;
}
}
static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx,
const vpx_image_t *img,
vpx_codec_pts_t pts,
@ -712,6 +754,8 @@ static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx,
if (!ctx->pending_cx_data)
ctx->pending_cx_data = cx_data;
ctx->pending_cx_data_sz += size;
ctx->pending_frame_sizes[ctx->pending_frame_count++] = size;
ctx->pending_frame_magnitude |= size;
cx_data += size;
cx_data_sz -= size;
continue;
@ -771,10 +815,16 @@ static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx,
else*/
{
if (ctx->pending_cx_data) {
ctx->pending_frame_sizes[ctx->pending_frame_count++] = size;
ctx->pending_frame_magnitude |= size;
ctx->pending_cx_data_sz += size;
write_superframe_index(ctx);
pkt.data.frame.buf = ctx->pending_cx_data;
pkt.data.frame.sz = ctx->pending_cx_data_sz + size;
pkt.data.frame.sz = ctx->pending_cx_data_sz;
ctx->pending_cx_data = NULL;
ctx->pending_cx_data_sz = 0;
ctx->pending_frame_count = 0;
ctx->pending_frame_magnitude = 0;
} else {
pkt.data.frame.buf = cx_data;
pkt.data.frame.sz = size;

View File

@ -425,6 +425,39 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx,
return res;
}
static void parse_superframe_index(const uint8_t *data,
size_t data_sz,
uint32_t sizes[8],
int *count) {
uint8_t marker;
assert(data_sz);
marker = data[data_sz - 1];
*count = 0;
if ((marker & 0xf0) == 0xc0) {
const int frames = (marker & 0x7) + 1;
const int mag = ((marker >> 3) & 3) + 1;
const int index_sz = 2 + mag * frames;
if (data_sz >= index_sz && data[data_sz - index_sz] == marker) {
// found a valid superframe index
int i, j;
const uint8_t *x = data + data_sz - index_sz + 1;
for (i = 0; i < frames; i++) {
int this_sz = 0;
for (j = 0; j < mag; j++)
this_sz |= (*x++) << (j * 8);
sizes[i] = this_sz;
}
*count = frames;
}
}
}
static vpx_codec_err_t vp9_decode(vpx_codec_alg_priv_t *ctx,
const uint8_t *data,
unsigned int data_sz,
@ -433,8 +466,42 @@ static vpx_codec_err_t vp9_decode(vpx_codec_alg_priv_t *ctx,
const uint8_t *data_start = data;
const uint8_t *data_end = data + data_sz;
vpx_codec_err_t res = 0;
uint32_t sizes[8];
int frames_this_pts, frame_count = 0;
parse_superframe_index(data, data_sz, sizes, &frames_this_pts);
do {
// Skip over the superframe index, if present
if (data_sz && (*data_start & 0xf0) == 0xc0) {
const uint8_t marker = *data_start;
const int frames = (marker & 0x7) + 1;
const int mag = ((marker >> 3) & 3) + 1;
const int index_sz = 2 + mag * frames;
if (data_sz >= index_sz && data_start[index_sz - 1] == marker) {
data_start += index_sz;
data_sz -= index_sz;
if (data_start < data_end)
continue;
else
break;
}
}
// Use the correct size for this frame, if an index is present.
if (frames_this_pts) {
uint32_t this_sz = sizes[frame_count];
if (data_sz < this_sz) {
ctx->base.err_detail = "Invalid frame size in index";
return VPX_CODEC_CORRUPT_FRAME;
}
data_sz = this_sz;
frame_count++;
}
res = decode_one(ctx, &data_start, data_sz, user_priv, deadline);
assert(data_start >= data);
assert(data_start <= data_end);