Changing decoder input partition API to input fragments.

Adding support for several partitions within one input fragment.
This is necessary to fully support all possible packetization
combinations in the VP8 RTP profile. Several partitions can
be transmitted in the same packet, and they can only be split
by reading the partition lengths from the bitstream.

Change-Id: If7d7ea331cc78cb7efd74c4a976b720c9a655463
This commit is contained in:
Stefan Holmer 2011-09-29 09:17:09 +02:00 committed by John Koleszar
parent 9bf3bc9a72
commit 1427205215
7 changed files with 221 additions and 232 deletions

View File

@ -33,7 +33,7 @@ extern "C"
int postprocess;
int max_threads;
int error_concealment;
int input_partition;
int input_fragments;
} VP8D_CONFIG;
typedef enum
{

View File

@ -416,49 +416,6 @@ static unsigned int read_partition_size(const unsigned char *cx_size)
return size;
}
static void setup_token_decoder_partition_input(VP8D_COMP *pbi)
{
vp8_reader *bool_decoder = &pbi->bc2;
int part_idx = 1;
int num_token_partitions;
TOKEN_PARTITION multi_token_partition =
(TOKEN_PARTITION)vp8_read_literal(&pbi->bc, 2);
if (!vp8dx_bool_error(&pbi->bc))
pbi->common.multi_token_partition = multi_token_partition;
num_token_partitions = 1 << pbi->common.multi_token_partition;
if (num_token_partitions + 1 > pbi->num_partitions)
vpx_internal_error(&pbi->common.error, VPX_CODEC_CORRUPT_FRAME,
"Partitions missing");
assert(vp8dx_bool_error(&pbi->bc) ||
multi_token_partition == pbi->common.multi_token_partition);
if (pbi->num_partitions > 2)
{
CHECK_MEM_ERROR(pbi->mbc, vpx_malloc((pbi->num_partitions - 1) *
sizeof(vp8_reader)));
bool_decoder = pbi->mbc;
}
for (; part_idx < pbi->num_partitions; ++part_idx)
{
if (vp8dx_start_decode(bool_decoder,
pbi->partitions[part_idx],
pbi->partition_sizes[part_idx]))
vpx_internal_error(&pbi->common.error, VPX_CODEC_MEM_ERROR,
"Failed to allocate bool decoder %d",
part_idx);
bool_decoder++;
}
#if CONFIG_MULTITHREAD
/* Clamp number of decoder threads */
if (pbi->decoding_thread_count > pbi->num_partitions - 1)
pbi->decoding_thread_count = pbi->num_partitions - 1;
#endif
}
static int read_is_valid(const unsigned char *start,
size_t len,
const unsigned char *end)
@ -466,94 +423,146 @@ static int read_is_valid(const unsigned char *start,
return (start + len > start && start + len <= end);
}
static unsigned int read_available_partition_size(
VP8D_COMP *pbi,
const unsigned char *token_part_sizes,
const unsigned char *fragment_start,
const unsigned char *first_fragment_end,
const unsigned char *fragment_end,
int i,
int num_part)
{
VP8_COMMON* pc = &pbi->common;
const unsigned char *partition_size_ptr = token_part_sizes + i * 3;
unsigned int partition_size;
ptrdiff_t bytes_left = fragment_end - fragment_start;
/* Calculate the length of this partition. The last partition
* size is implicit. If the partition size can't be read, then
* either use the remaining data in the buffer (for EC mode)
* or throw an error.
*/
if (i < num_part - 1)
{
if (read_is_valid(partition_size_ptr, 3, first_fragment_end))
partition_size = read_partition_size(partition_size_ptr);
else if (pbi->ec_active)
partition_size = bytes_left;
else
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
"Truncated partition size data");
}
else
partition_size = bytes_left;
/* Validate the calculated partition length. If the buffer
* described by the partition can't be fully read, then restrict
* it to the portion that can be (for EC mode) or throw an error.
*/
if (!read_is_valid(fragment_start, partition_size, fragment_end))
{
if (pbi->ec_active)
partition_size = bytes_left;
else
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
"Truncated packet or corrupt partition "
"%d length", i + 1);
}
return partition_size;
}
static void setup_token_decoder(VP8D_COMP *pbi,
const unsigned char *cx_data)
const unsigned char* token_part_sizes)
{
int num_part;
int i;
VP8_COMMON *pc = &pbi->common;
const unsigned char *user_data_end = pbi->Source + pbi->source_sz;
vp8_reader *bool_decoder;
const unsigned char *partition;
vp8_reader *bool_decoder = &pbi->bc2;
int fragment_idx, partition_idx;
int num_token_partitions;
const unsigned char *first_fragment_end = pbi->fragments[0] +
pbi->fragment_sizes[0];
/* Parse number of token partitions to use */
const TOKEN_PARTITION multi_token_partition =
TOKEN_PARTITION multi_token_partition =
(TOKEN_PARTITION)vp8_read_literal(&pbi->bc, 2);
/* Only update the multi_token_partition field if we are sure the value
* is correct. */
if (!pbi->ec_active || !vp8dx_bool_error(&pbi->bc))
pc->multi_token_partition = multi_token_partition;
num_part = 1 << pc->multi_token_partition;
/* Set up pointers to the first partition */
partition = cx_data;
bool_decoder = &pbi->bc2;
if (num_part > 1)
if (!vp8dx_bool_error(&pbi->bc))
pbi->common.multi_token_partition = multi_token_partition;
num_token_partitions = 1 << pbi->common.multi_token_partition;
if (num_token_partitions > 1)
{
CHECK_MEM_ERROR(pbi->mbc, vpx_malloc(num_part * sizeof(vp8_reader)));
CHECK_MEM_ERROR(pbi->mbc, vpx_malloc(num_token_partitions *
sizeof(vp8_reader)));
bool_decoder = pbi->mbc;
partition += 3 * (num_part - 1);
}
for (i = 0; i < num_part; i++)
/* Check for partitions within the fragments and unpack the fragments
* so that each fragment pointer points to its corresponding partition. */
for (fragment_idx = 0; fragment_idx < pbi->num_fragments; ++fragment_idx)
{
const unsigned char *partition_size_ptr = cx_data + i * 3;
ptrdiff_t partition_size, bytes_left;
bytes_left = user_data_end - partition;
/* Calculate the length of this partition. The last partition
* size is implicit. If the partition size can't be read, then
* either use the remaining data in the buffer (for EC mode)
* or throw an error.
*/
if (i < num_part - 1)
unsigned int fragment_size = pbi->fragment_sizes[fragment_idx];
const unsigned char *fragment_end = pbi->fragments[fragment_idx] +
fragment_size;
/* Special case for handling the first partition since we have already
* read its size. */
if (fragment_idx == 0)
{
if (read_is_valid(partition_size_ptr, 3, user_data_end))
partition_size = read_partition_size(partition_size_ptr);
else if (pbi->ec_active)
partition_size = bytes_left;
else
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
"Truncated partition size data");
/* Size of first partition + token partition sizes element */
ptrdiff_t ext_first_part_size = token_part_sizes -
pbi->fragments[0] + 3 * (num_token_partitions - 1);
fragment_size -= ext_first_part_size;
if (fragment_size > 0)
{
pbi->fragment_sizes[0] = ext_first_part_size;
/* The fragment contains an additional partition. Move to
* next. */
fragment_idx++;
pbi->fragments[fragment_idx] = pbi->fragments[0] +
pbi->fragment_sizes[0];
}
}
else
partition_size = bytes_left;
/* Validate the calculated partition length. If the buffer
* described by the partition can't be fully read, then restrict
* it to the portion that can be (for EC mode) or throw an error.
*/
if (!read_is_valid(partition, partition_size, user_data_end))
/* Split the chunk into partitions read from the bitstream */
while (fragment_size > 0)
{
if (pbi->ec_active)
partition_size = bytes_left;
else
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
"Truncated packet or corrupt partition "
"%d length", i + 1);
ptrdiff_t partition_size = read_available_partition_size(
pbi,
token_part_sizes,
pbi->fragments[fragment_idx],
first_fragment_end,
fragment_end,
fragment_idx - 1,
num_token_partitions);
pbi->fragment_sizes[fragment_idx] = partition_size;
fragment_size -= partition_size;
assert(fragment_idx <= num_token_partitions);
if (fragment_size > 0)
{
/* The fragment contains an additional partition.
* Move to next. */
fragment_idx++;
pbi->fragments[fragment_idx] =
pbi->fragments[fragment_idx - 1] + partition_size;
}
}
}
if (vp8dx_start_decode(bool_decoder, partition, partition_size))
vpx_internal_error(&pc->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate bool decoder %d", i + 1);
pbi->num_fragments = num_token_partitions + 1;
for (partition_idx = 1; partition_idx < pbi->num_fragments; ++partition_idx)
{
if (vp8dx_start_decode(bool_decoder,
pbi->fragments[partition_idx],
pbi->fragment_sizes[partition_idx]))
vpx_internal_error(&pbi->common.error, VPX_CODEC_MEM_ERROR,
"Failed to allocate bool decoder %d",
partition_idx);
/* Advance to the next partition */
partition += partition_size;
bool_decoder++;
}
#if CONFIG_MULTITHREAD
/* Clamp number of decoder threads */
if (pbi->decoding_thread_count > num_part - 1)
pbi->decoding_thread_count = num_part - 1;
if (pbi->decoding_thread_count > num_token_partitions - 1)
pbi->decoding_thread_count = num_token_partitions - 1;
#endif
}
static void stop_token_decoder(VP8D_COMP *pbi)
{
VP8_COMMON *pc = &pbi->common;
@ -645,8 +654,8 @@ int vp8_decode_frame(VP8D_COMP *pbi)
vp8_reader *const bc = & pbi->bc;
VP8_COMMON *const pc = & pbi->common;
MACROBLOCKD *const xd = & pbi->mb;
const unsigned char *data = (const unsigned char *)pbi->Source;
const unsigned char *data_end = data + pbi->source_sz;
const unsigned char *data = pbi->fragments[0];
const unsigned char *data_end = data + pbi->fragment_sizes[0];
ptrdiff_t first_partition_length_in_bytes;
int mb_row;
@ -655,12 +664,6 @@ int vp8_decode_frame(VP8D_COMP *pbi)
int corrupt_tokens = 0;
int prev_independent_partitions = pbi->independent_partitions;
if (pbi->input_partition)
{
data = pbi->partitions[0];
data_end = data + pbi->partition_sizes[0];
}
/* start with no corruption of current frame */
xd->corrupted = 0;
pc->yv12_fb[pc->new_fb_idx].corrupted = 0;
@ -877,14 +880,8 @@ int vp8_decode_frame(VP8D_COMP *pbi)
}
}
if (pbi->input_partition)
{
setup_token_decoder_partition_input(pbi);
}
else
{
setup_token_decoder(pbi, data + first_partition_length_in_bytes);
}
setup_token_decoder(pbi, data + first_partition_length_in_bytes);
xd->current_bc = &pbi->bc2;
/* Read the default quantizers. */

View File

@ -108,7 +108,8 @@ VP8D_PTR vp8dx_create_decompressor(VP8D_CONFIG *oxcf)
pbi->decoded_key_frame = 0;
pbi->input_partition = oxcf->input_partition;
pbi->input_fragments = oxcf->input_fragments;
pbi->num_fragments = 0;
/* Independent partitions is activated when a frame updates the
* token probability table to have equal probabilities over the
@ -319,115 +320,107 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
pbi->common.error.error_code = VPX_CODEC_OK;
if (pbi->input_partition && !(source == NULL && size == 0))
if (pbi->num_fragments == 0)
{
/* Store a pointer to this partition and return. We haven't
/* New frame, reset fragment pointers and sizes */
vpx_memset(pbi->fragments, 0, sizeof(pbi->fragments));
vpx_memset(pbi->fragment_sizes, 0, sizeof(pbi->fragment_sizes));
}
if (pbi->input_fragments && !(source == NULL && size == 0))
{
/* Store a pointer to this fragment and return. We haven't
* received the complete frame yet, so we will wait with decoding.
*/
assert(pbi->num_partitions < MAX_PARTITIONS);
pbi->partitions[pbi->num_partitions] = source;
pbi->partition_sizes[pbi->num_partitions] = size;
pbi->source_sz += size;
pbi->num_partitions++;
if (pbi->num_partitions > (1 << EIGHT_PARTITION) + 1)
assert(pbi->num_fragments < MAX_PARTITIONS);
pbi->fragments[pbi->num_fragments] = source;
pbi->fragment_sizes[pbi->num_fragments] = size;
pbi->num_fragments++;
if (pbi->num_fragments > (1 << EIGHT_PARTITION) + 1)
{
pbi->common.error.error_code = VPX_CODEC_UNSUP_BITSTREAM;
pbi->common.error.setjmp = 0;
pbi->num_partitions = 0;
pbi->num_fragments = 0;
return -1;
}
return 0;
}
else
if (!pbi->input_fragments)
{
if (!pbi->input_partition)
pbi->fragments[0] = source;
pbi->fragment_sizes[0] = size;
pbi->num_fragments = 1;
}
assert(pbi->common.multi_token_partition <= EIGHT_PARTITION);
if (pbi->num_fragments == 0)
{
pbi->num_fragments = 1;
pbi->fragments[0] = NULL;
pbi->fragment_sizes[0] = 0;
}
if (pbi->num_fragments <= 1 && pbi->fragment_sizes[0] == 0)
{
/* This is used to signal that we are missing frames.
* We do not know if the missing frame(s) was supposed to update
* any of the reference buffers, but we act conservative and
* mark only the last buffer as corrupted.
*/
cm->yv12_fb[cm->lst_fb_idx].corrupted = 1;
/* If error concealment is disabled we won't signal missing frames
* to the decoder.
*/
if (!pbi->ec_active)
{
pbi->Source = source;
pbi->source_sz = size;
}
else
{
assert(pbi->common.multi_token_partition <= EIGHT_PARTITION);
if (pbi->num_partitions == 0)
{
pbi->num_partitions = 1;
pbi->partitions[0] = NULL;
pbi->partition_sizes[0] = 0;
}
while (pbi->num_partitions < (1 << pbi->common.multi_token_partition) + 1)
{
// Reset all missing partitions
pbi->partitions[pbi->num_partitions] =
pbi->partitions[pbi->num_partitions - 1] +
pbi->partition_sizes[pbi->num_partitions - 1];
pbi->partition_sizes[pbi->num_partitions] = 0;
pbi->num_partitions++;
}
/* Signal that we have no frame to show. */
cm->show_frame = 0;
pbi->num_fragments = 0;
/* Nothing more to do. */
return 0;
}
}
if (pbi->source_sz == 0)
{
/* This is used to signal that we are missing frames.
* We do not know if the missing frame(s) was supposed to update
* any of the reference buffers, but we act conservative and
* mark only the last buffer as corrupted.
*/
cm->yv12_fb[cm->lst_fb_idx].corrupted = 1;
#if HAVE_ARMV7
#if CONFIG_RUNTIME_CPU_DETECT
if (cm->rtcd.flags & HAS_NEON)
#endif
{
vp8_push_neon(dx_store_reg);
}
#endif
/* If error concealment is disabled we won't signal missing frames to
* the decoder.
*/
if (!pbi->ec_active)
{
/* Signal that we have no frame to show. */
cm->show_frame = 0;
pbi->num_partitions = 0;
/* Nothing more to do. */
return 0;
}
}
cm->new_fb_idx = get_free_fb (cm);
if (setjmp(pbi->common.error.jmp))
{
#if HAVE_ARMV7
#if CONFIG_RUNTIME_CPU_DETECT
if (cm->rtcd.flags & HAS_NEON)
#endif
{
vp8_push_neon(dx_store_reg);
vp8_pop_neon(dx_store_reg);
}
#endif
pbi->common.error.setjmp = 0;
cm->new_fb_idx = get_free_fb (cm);
pbi->num_fragments = 0;
if (setjmp(pbi->common.error.jmp))
{
#if HAVE_ARMV7
#if CONFIG_RUNTIME_CPU_DETECT
if (cm->rtcd.flags & HAS_NEON)
#endif
{
vp8_pop_neon(dx_store_reg);
}
#endif
pbi->common.error.setjmp = 0;
/* We do not know if the missing frame(s) was supposed to update
* any of the reference buffers, but we act conservative and
* mark only the last buffer as corrupted.
*/
cm->yv12_fb[cm->lst_fb_idx].corrupted = 1;
pbi->num_partitions = 0;
/* We do not know if the missing frame(s) was supposed to update
* any of the reference buffers, but we act conservative and
* mark only the last buffer as corrupted.
*/
cm->yv12_fb[cm->lst_fb_idx].corrupted = 1;
if (cm->fb_idx_ref_cnt[cm->new_fb_idx] > 0)
cm->fb_idx_ref_cnt[cm->new_fb_idx]--;
return -1;
}
pbi->common.error.setjmp = 1;
if (cm->fb_idx_ref_cnt[cm->new_fb_idx] > 0)
cm->fb_idx_ref_cnt[cm->new_fb_idx]--;
return -1;
}
pbi->common.error.setjmp = 1;
retcode = vp8_decode_frame(pbi);
if (retcode < 0)
@ -442,7 +435,7 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
#endif
pbi->common.error.error_code = VPX_CODEC_ERROR;
pbi->common.error.setjmp = 0;
pbi->num_partitions = 0;
pbi->num_fragments = 0;
if (cm->fb_idx_ref_cnt[cm->new_fb_idx] > 0)
cm->fb_idx_ref_cnt[cm->new_fb_idx]--;
return retcode;
@ -463,7 +456,7 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
#endif
pbi->common.error.error_code = VPX_CODEC_ERROR;
pbi->common.error.setjmp = 0;
pbi->num_partitions = 0;
pbi->num_fragments = 0;
return -1;
}
} else
@ -481,7 +474,7 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
#endif
pbi->common.error.error_code = VPX_CODEC_ERROR;
pbi->common.error.setjmp = 0;
pbi->num_partitions = 0;
pbi->num_fragments = 0;
return -1;
}
@ -500,7 +493,7 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
/* swap the mode infos to storage for future error concealment */
if (pbi->ec_enabled && pbi->common.prev_mi)
{
const MODE_INFO* tmp = pbi->common.prev_mi;
MODE_INFO* tmp = pbi->common.prev_mi;
int row, col;
pbi->common.prev_mi = pbi->common.mi;
pbi->common.mi = tmp;
@ -525,8 +518,7 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
pbi->ready_for_new_data = 0;
pbi->last_time_stamp = time_stamp;
pbi->num_partitions = 0;
pbi->source_sz = 0;
pbi->num_fragments = 0;
#if 0
{

View File

@ -54,11 +54,9 @@ typedef struct VP8Decompressor
VP8D_CONFIG oxcf;
const unsigned char *Source;
unsigned int source_sz;
const unsigned char *partitions[MAX_PARTITIONS];
unsigned int partition_sizes[MAX_PARTITIONS];
unsigned int num_partitions;
const unsigned char *fragments[MAX_PARTITIONS];
unsigned int fragment_sizes[MAX_PARTITIONS];
unsigned int num_fragments;
#if CONFIG_MULTITHREAD
/* variable for threading */
@ -112,7 +110,7 @@ typedef struct VP8Decompressor
#endif
int ec_enabled;
int ec_active;
int input_partition;
int input_fragments;
int decoded_key_frame;
int independent_partitions;
int frame_corrupt_residual;

View File

@ -398,8 +398,8 @@ static vpx_codec_err_t vp8_decode(vpx_codec_alg_priv_t *ctx,
oxcf.max_threads = ctx->cfg.threads;
oxcf.error_concealment =
(ctx->base.init_flags & VPX_CODEC_USE_ERROR_CONCEALMENT);
oxcf.input_partition =
(ctx->base.init_flags & VPX_CODEC_USE_INPUT_PARTITION);
oxcf.input_fragments =
(ctx->base.init_flags & VPX_CODEC_USE_INPUT_FRAGMENTS);
optr = vp8dx_create_decompressor(&oxcf);
@ -741,7 +741,7 @@ CODEC_INTERFACE(vpx_codec_vp8_dx) =
"WebM Project VP8 Decoder" VERSION_STRING,
VPX_CODEC_INTERNAL_ABI_VERSION,
VPX_CODEC_CAP_DECODER | VP8_CAP_POSTPROC | VP8_CAP_ERROR_CONCEALMENT |
VPX_CODEC_CAP_INPUT_PARTITION,
VPX_CODEC_CAP_INPUT_FRAGMENTS,
/* vpx_codec_caps_t caps; */
vp8_init, /* vpx_codec_init_fn_t init; */
vp8_destroy, /* vpx_codec_destroy_fn_t destroy; */

View File

@ -39,8 +39,8 @@ vpx_codec_err_t vpx_codec_dec_init_ver(vpx_codec_ctx_t *ctx,
else if ((flags & VPX_CODEC_USE_ERROR_CONCEALMENT) &&
!(iface->caps & VPX_CODEC_CAP_ERROR_CONCEALMENT))
res = VPX_CODEC_INCAPABLE;
else if ((flags & VPX_CODEC_USE_INPUT_PARTITION) &&
!(iface->caps & VPX_CODEC_CAP_INPUT_PARTITION))
else if ((flags & VPX_CODEC_USE_INPUT_FRAGMENTS) &&
!(iface->caps & VPX_CODEC_CAP_INPUT_FRAGMENTS))
res = VPX_CODEC_INCAPABLE;
else if (!(iface->caps & VPX_CODEC_CAP_DECODER))
res = VPX_CODEC_INCAPABLE;

View File

@ -55,8 +55,8 @@ extern "C" {
#define VPX_CODEC_CAP_POSTPROC 0x40000 /**< Can postprocess decoded frame */
#define VPX_CODEC_CAP_ERROR_CONCEALMENT 0x80000 /**< Can conceal errors due to
packet loss */
#define VPX_CODEC_CAP_INPUT_PARTITION 0x100000 /**< Can receive encoded frames
one partition at a time */
#define VPX_CODEC_CAP_INPUT_FRAGMENTS 0x100000 /**< Can receive encoded frames
one fragment at a time */
/*! \brief Initialization-time Feature Enabling
*
@ -68,9 +68,9 @@ extern "C" {
#define VPX_CODEC_USE_POSTPROC 0x10000 /**< Postprocess decoded frame */
#define VPX_CODEC_USE_ERROR_CONCEALMENT 0x20000 /**< Conceal errors in decoded
frames */
#define VPX_CODEC_USE_INPUT_PARTITION 0x40000 /**< The input frame should be
#define VPX_CODEC_USE_INPUT_FRAGMENTS 0x40000 /**< The input frame should be
passed to the decoder one
partition at a time */
fragment at a time */
/*!\brief Stream properties
*
@ -189,11 +189,13 @@ extern "C" {
* generated, as appropriate. Encoded data \ref MUST be passed in DTS (decode
* time stamp) order. Frames produced will always be in PTS (presentation
* time stamp) order.
* If the decoder is configured with VPX_CODEC_USE_INPUT_PARTITION enabled,
* data and data_sz must contain at most one encoded partition. When no more
* data is available, this function should be called with NULL as data and 0
* as data_sz. The memory passed to this function must be available until
* the frame has been decoded.
* If the decoder is configured with VPX_CODEC_USE_INPUT_FRAGMENTS enabled,
* data and data_sz can contain a fragment of the encoded frame. Fragment #n
* must contain at least partition #n, but can also contain subsequent
* partitions (#n+1 - #n+i), and if so, fragments #n+1, .., #n+i must be
* empty. When no more data is available, this function should be called
* with NULL as data and 0 as data_sz. The memory passed to this function
* must be available until the frame has been decoded.
*
* \param[in] ctx Pointer to this instance's context
* \param[in] data Pointer to this block of new coded data. If