Commit 67fb3a51 authored by Henrik Lundin's avatar Henrik Lundin

Implement error tracking in the decoder

A new vpx_codec_control called VP8D_GET_FRAME_CORRUPTED. The output
from the function is non-zero if the last decoded frame contains
corruption due to packet losses.

The decoder is also modified to accept encoded frames of zero length.
A zero length frame indicates to the decoder that one or more frames
have been completely lost. This will mark the last decoded reference
buffer as corrupted. The data pointer can be NULL if the length is
zero.

Change-Id: Ic5902c785a281c6e05329deea958554b7a6c75ce
parent 2a87491f
......@@ -282,6 +282,8 @@ typedef struct
void *current_bc;
int corrupted;
#if CONFIG_RUNTIME_CPU_DETECT
struct VP8_COMMON_RTCD *rtcd;
#endif
......
......@@ -206,4 +206,29 @@ static int vp8_decode_value(BOOL_DECODER *br, int bits)
return z;
}
static int vp8dx_bool_error(BOOL_DECODER *br)
{
/* Check if we have reached the end of the buffer.
*
* Variable 'count' stores the number of bits in the 'value' buffer,
* minus 8. So if count == 8, there are 16 bits available to be read.
* Normally, count is filled with 8 and one byte is filled into the
* value buffer. When we reach the end of the buffer, count is instead
* filled with VP8_LOTS_OF_BITS, 8 of which represent the last 8 real
* bits from the bitstream. So the last bit in the bitstream will be
* represented by count == VP8_LOTS_OF_BITS - 16.
*/
if ((br->count > VP8_BD_VALUE_SIZE)
&& (br->count <= VP8_LOTS_OF_BITS - 16))
{
/* We have tried to decode bits after the end of
* stream was encountered.
*/
return 1;
}
/* No error. */
return 0;
}
#endif
......@@ -381,6 +381,12 @@ void vp8_decode_mb_row(VP8D_COMP *pbi,
xd->pre.u_buffer = pc->yv12_fb[ref_fb_idx].u_buffer + recon_uvoffset;
xd->pre.v_buffer = pc->yv12_fb[ref_fb_idx].v_buffer + recon_uvoffset;
if (xd->mode_info_context->mbmi.ref_frame != INTRA_FRAME)
{
/* propagate errors from reference frames */
xd->corrupted |= pc->yv12_fb[ref_fb_idx].corrupted;
}
vp8_build_uvmvs(xd, pc->full_pixel);
/*
......@@ -391,6 +397,8 @@ void vp8_decode_mb_row(VP8D_COMP *pbi,
*/
vp8_decode_macroblock(pbi, xd);
/* check if the boolean decoder has suffered an error */
xd->corrupted |= vp8dx_bool_error(xd->current_bc);
recon_yoffset += 16;
recon_uvoffset += 8;
......@@ -556,6 +564,7 @@ static void init_frame(VP8D_COMP *pbi)
xd->frame_type = pc->frame_type;
xd->mode_info_context->mbmi.mode = DC_PRED;
xd->mode_info_stride = pc->mode_info_stride;
xd->corrupted = 0; /* init without corruption */
}
int vp8_decode_frame(VP8D_COMP *pbi)
......@@ -571,6 +580,10 @@ int vp8_decode_frame(VP8D_COMP *pbi)
int i, j, k, l;
const int *const mb_feature_data_bits = vp8_mb_feature_data_bits;
/* start with no corruption of current frame */
xd->corrupted = 0;
pc->yv12_fb[pc->new_fb_idx].corrupted = 0;
if (data_end - data < 3)
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
"Truncated packet");
......@@ -892,6 +905,14 @@ int vp8_decode_frame(VP8D_COMP *pbi)
stop_token_decoder(pbi);
/* Collect information about decoder corruption. */
/* 1. Check first boolean decoder for errors. */
pc->yv12_fb[pc->new_fb_idx].corrupted =
vp8dx_bool_error(bc);
/* 2. Check the macroblock information */
pc->yv12_fb[pc->new_fb_idx].corrupted |=
xd->corrupted;
/* vpx_log("Decoder: Frame Decoded, Size Roughly:%d bytes \n",bc->pos+pbi->bc2.pos); */
/* If this was a kf or Gf note the Q used */
......
......@@ -334,6 +334,23 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
pbi->common.error.error_code = VPX_CODEC_OK;
if (size == 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;
/* Signal that we have no frame to show. */
cm->show_frame = 0;
/* Nothing more to do. */
return 0;
}
#if HAVE_ARMV7
#if CONFIG_RUNTIME_CPU_DETECT
if (cm->rtcd.flags & HAS_NEON)
......@@ -356,6 +373,13 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign
}
#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;
if (cm->fb_idx_ref_cnt[cm->new_fb_idx] > 0)
cm->fb_idx_ref_cnt[cm->new_fb_idx]--;
return -1;
......
......@@ -893,9 +893,18 @@ void vp8mt_decode_mb_rows( VP8D_COMP *pbi, MACROBLOCKD *xd)
xd->pre.u_buffer = pc->yv12_fb[ref_fb_idx].u_buffer + recon_uvoffset;
xd->pre.v_buffer = pc->yv12_fb[ref_fb_idx].v_buffer + recon_uvoffset;
if (xd->mode_info_context->mbmi.ref_frame != INTRA_FRAME)
{
/* propagate errors from reference frames */
xd->corrupted |= pc->yv12_fb[ref_fb_idx].corrupted;
}
vp8_build_uvmvs(xd, pc->full_pixel);
vp8mt_decode_macroblock(pbi, xd, mb_row, mb_col);
/* check if the boolean decoder has suffered an error */
xd->corrupted |= vp8dx_bool_error(xd->current_bc);
if (pbi->common.filter_level)
{
/* Save decoded MB last row data for next-row decoding */
......
......@@ -708,6 +708,25 @@ static vpx_codec_err_t vp8_get_last_ref_updates(vpx_codec_alg_priv_t *ctx,
}
static vpx_codec_err_t vp8_get_frame_corrupted(vpx_codec_alg_priv_t *ctx,
int ctrl_id,
va_list args)
{
int *corrupted = va_arg(args, int *);
if (corrupted)
{
VP8D_COMP *pbi = (VP8D_COMP *)ctx->pbi;
*corrupted = pbi->common.frame_to_show->corrupted;
return VPX_CODEC_OK;
}
else
return VPX_CODEC_INVALID_PARAM;
}
vpx_codec_ctrl_fn_map_t vp8_ctf_maps[] =
{
{VP8_SET_REFERENCE, vp8_set_reference},
......@@ -718,6 +737,7 @@ vpx_codec_ctrl_fn_map_t vp8_ctf_maps[] =
{VP8_SET_DBG_COLOR_B_MODES, vp8_set_dbg_options},
{VP8_SET_DBG_DISPLAY_MV, vp8_set_dbg_options},
{VP8D_GET_LAST_REF_UPDATES, vp8_get_last_ref_updates},
{VP8D_GET_FRAME_CORRUPTED, vp8_get_frame_corrupted},
{ -1, NULL},
};
......
......@@ -118,7 +118,9 @@ vpx_codec_err_t vpx_codec_decode(vpx_codec_ctx_t *ctx,
{
vpx_codec_err_t res;
if (!ctx || !data || !data_sz)
/* Sanity checks */
/* NULL data ptr allowed if data_sz is 0 too */
if (!ctx || (!data && data_sz))
res = VPX_CODEC_INVALID_PARAM;
else if (!ctx->iface || !ctx->priv)
res = VPX_CODEC_ERROR;
......
......@@ -45,6 +45,7 @@ enum vp8d_dec_control_id
VP8_DECODER_CTRL_ID_START = 256,
VP8D_GET_LAST_REF_UPDATES, /**< control function to get info on which reference frames were updated
by the last decode */
VP8D_GET_FRAME_CORRUPTED, /**< check if the indicated frame is corrupted */
VP8_DECODER_CTRL_ID_MAX
} ;
......@@ -58,6 +59,7 @@ enum vp8d_dec_control_id
VPX_CTRL_USE_TYPE(VP8D_GET_LAST_REF_UPDATES, int *)
VPX_CTRL_USE_TYPE(VP8D_GET_FRAME_CORRUPTED, int *)
/*! @} - end defgroup vp8_decoder */
......
......@@ -81,6 +81,8 @@ vp8_yv12_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, int
ybf->u_buffer = ybf->buffer_alloc + yplane_size + (border / 2 * ybf->uv_stride) + border / 2;
ybf->v_buffer = ybf->buffer_alloc + yplane_size + uvplane_size + (border / 2 * ybf->uv_stride) + border / 2;
ybf->corrupted = 0; /* assume not currupted by errors */
}
else
{
......
......@@ -57,6 +57,8 @@ extern "C"
int border;
int frame_size;
YUV_TYPE clrtype;
int corrupted;
} YV12_BUFFER_CONFIG;
int vp8_yv12_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, int border);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment