Commit 14272052 authored by Stefan Holmer's avatar Stefan Holmer Committed by John Koleszar
Browse files

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
parent 9bf3bc9a
......@@ -33,7 +33,7 @@ extern "C"
int postprocess;
int max_threads;
int error_concealment;
int input_partition;
int input_fragments;
} VP8D_CONFIG;
typedef enum
{
......
......@@ -416,144 +416,153 @@ static unsigned int read_partition_size(const unsigned char *cx_size)
return size;
}
static void setup_token_decoder_partition_input(VP8D_COMP *pbi)
static int read_is_valid(const unsigned char *start,
size_t len,
const unsigned char *end)
{
vp8_reader *bool_decoder = &pbi->bc2;
int part_idx = 1;
int num_token_partitions;
return (start + len > start && start + len <= end);
}
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)
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)
{
CHECK_MEM_ERROR(pbi->mbc, vpx_malloc((pbi->num_partitions - 1) *
sizeof(vp8_reader)));
bool_decoder = pbi->mbc;
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;
for (; part_idx < pbi->num_partitions; ++part_idx)
/* 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 (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 (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);
}
#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)
{
return (start + len > start && start + len <= end);
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;
}
}
}
pbi->num_fragments = num_token_partitions + 1;
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);
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. */
......
......@@ -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->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++;
}
}
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->source_sz == 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)
{
/* 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)
{
/* Signal that we have no frame to show. */
cm->show_frame = 0;
/* Signal that we have no frame to show. */
cm->show_frame = 0;
pbi->num_partitions = 0;
pbi->num_fragments = 0;
/* Nothing more to do. */
return 0;
}
/* Nothing more to do. */
return 0;
}
}
#if HAVE_ARMV7
#if CONFIG_RUNTIME_CPU_DETECT
if (cm->rtcd.flags & HAS_NEON)
if (cm->rtcd.flags & HAS_NEON)
#endif
{
vp8_push_neon(dx_store_reg);
}
{
vp8_push_neon(dx_store_reg);
}
#endif
cm->new_fb_idx = get_free_fb (cm);
cm->new_fb_idx = get_free_fb (cm);
if (setjmp(pbi->common.error.jmp))
{
if (setjmp(pbi->common.error.jmp))
{
#if HAVE_ARMV7
#if CONFIG_RUNTIME_CPU_DETECT
if (cm->rtcd.flags & HAS_NEON)
if (cm->rtcd.flags & HAS_NEON)
#endif
{
vp8_pop_neon(dx_store_reg);
}
{
vp8_pop_neon(dx_store_reg);
}
#endif
pbi->common.error.setjmp = 0;
pbi->num_partitions = 0;
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_fragments = 0;
if (cm->fb_idx_ref_cnt[cm->new_fb_idx] > 0)
cm->fb_idx_ref_cnt[cm->new_fb_idx]--;
return -1;
}
/* 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->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
{
......
......@@ -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;
......
......@@ -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; */
......
......@@ -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;
......
......@@ -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 */