ref: 522d4bf852c484579de0591879a1d006abf08959
parent: 4209bba4627bb148fce5b8b6caa4de5ac526fa92
author: John Koleszar <jkoleszar@google.com>
date: Tue Mar 5 07:23:34 EST 2013
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
--- a/test/encode_test_driver.cc
+++ b/test/encode_test_driver.cc
@@ -169,6 +169,7 @@
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:
--- a/test/encode_test_driver.h
+++ b/test/encode_test_driver.h
@@ -203,6 +203,12 @@
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_;
--- /dev/null
+++ b/test/superframe_test.cc
@@ -1,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
--- a/test/test.mk
+++ b/test/test.mk
@@ -68,6 +68,7 @@
# 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
--- a/vp9/encoder/vp9_onyx_if.c
+++ b/vp9/encoder/vp9_onyx_if.c
@@ -4009,7 +4009,7 @@
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;
--- a/vp9/vp9_cx_iface.c
+++ b/vp9/vp9_cx_iface.c
@@ -77,6 +77,9 @@
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 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 @@
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 @@
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;
--- a/vp9/vp9_dx_iface.c
+++ b/vp9/vp9_dx_iface.c
@@ -425,6 +425,39 @@
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 @@
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);
--
⑨