From 67fb3a5155bd7dcb6aacbc7e3bb05be308040367 Mon Sep 17 00:00:00 2001 From: Henrik Lundin Date: Thu, 16 Dec 2010 16:46:31 +0100 Subject: [PATCH] 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 --- vp8/common/blockd.h | 2 ++ vp8/decoder/dboolhuff.h | 25 +++++++++++++++++++++++++ vp8/decoder/decodframe.c | 21 +++++++++++++++++++++ vp8/decoder/onyxd_if.c | 24 ++++++++++++++++++++++++ vp8/decoder/threading.c | 9 +++++++++ vp8/vp8_dx_iface.c | 20 ++++++++++++++++++++ vpx/src/vpx_decoder.c | 4 +++- vpx/vp8dx.h | 2 ++ vpx_scale/generic/yv12config.c | 2 ++ vpx_scale/yv12config.h | 2 ++ 10 files changed, 110 insertions(+), 1 deletion(-) diff --git a/vp8/common/blockd.h b/vp8/common/blockd.h index a38f0b72b..5a8991e65 100644 --- a/vp8/common/blockd.h +++ b/vp8/common/blockd.h @@ -282,6 +282,8 @@ typedef struct void *current_bc; + int corrupted; + #if CONFIG_RUNTIME_CPU_DETECT struct VP8_COMMON_RTCD *rtcd; #endif diff --git a/vp8/decoder/dboolhuff.h b/vp8/decoder/dboolhuff.h index c851aa7e5..d14f4dceb 100644 --- a/vp8/decoder/dboolhuff.h +++ b/vp8/decoder/dboolhuff.h @@ -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 diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c index 4702faeed..2ce1b86b9 100644 --- a/vp8/decoder/decodframe.c +++ b/vp8/decoder/decodframe.c @@ -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 */ diff --git a/vp8/decoder/onyxd_if.c b/vp8/decoder/onyxd_if.c index aa2709f5b..d32b8cd65 100644 --- a/vp8/decoder/onyxd_if.c +++ b/vp8/decoder/onyxd_if.c @@ -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; diff --git a/vp8/decoder/threading.c b/vp8/decoder/threading.c index fea4e1cc1..2e10c1059 100644 --- a/vp8/decoder/threading.c +++ b/vp8/decoder/threading.c @@ -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 */ diff --git a/vp8/vp8_dx_iface.c b/vp8/vp8_dx_iface.c index cf32d1f38..7d4d7f65b 100644 --- a/vp8/vp8_dx_iface.c +++ b/vp8/vp8_dx_iface.c @@ -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}, }; diff --git a/vpx/src/vpx_decoder.c b/vpx/src/vpx_decoder.c index b52470b51..27049a51e 100644 --- a/vpx/src/vpx_decoder.c +++ b/vpx/src/vpx_decoder.c @@ -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; diff --git a/vpx/vp8dx.h b/vpx/vp8dx.h index 9feab7cc2..16bc07c60 100644 --- a/vpx/vp8dx.h +++ b/vpx/vp8dx.h @@ -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 */ diff --git a/vpx_scale/generic/yv12config.c b/vpx_scale/generic/yv12config.c index d9d228551..e7c5b189c 100644 --- a/vpx_scale/generic/yv12config.c +++ b/vpx_scale/generic/yv12config.c @@ -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 { diff --git a/vpx_scale/yv12config.h b/vpx_scale/yv12config.h index 5dcee818a..0b08db3ef 100644 --- a/vpx_scale/yv12config.h +++ b/vpx_scale/yv12config.h @@ -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);