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:
parent
4209bba462
commit
522d4bf852
@ -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:
|
||||
|
@ -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
100
test/superframe_test.cc
Normal 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
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user