Disable error concealment until first key frame is decoded
When error concealment is enabled the first key frame must
be successfully received before error concealment is activated.
Error concealment will be activated when the delta following
delta frame is received.
Also fixed a couple of bugs related to error tracking in
multi-threading. And avoiding decoding corrupt residual
when we have multiple non-resilient partitions.
Change-Id: I45c4bb296e2f05f57624aef500a874faf431a60d
diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c
index 5ddf095..ddb0970 100644
--- a/vp8/decoder/decodframe.c
+++ b/vp8/decoder/decodframe.c
@@ -183,6 +183,7 @@
unsigned int mb_idx)
{
int eobtotal = 0;
+ int throw_residual = 0;
MB_PREDICTION_MODE mode;
int i;
@@ -203,7 +204,8 @@
mode = xd->mode_info_context->mbmi.mode;
- if (eobtotal == 0 && mode != B_PRED && mode != SPLITMV)
+ if (eobtotal == 0 && mode != B_PRED && mode != SPLITMV &&
+ !vp8dx_bool_error(xd->current_bc))
{
/* Special case: Force the loopfilter to skip when eobtotal and
* mb_skip_coeff are zero.
@@ -235,14 +237,21 @@
vp8_build_inter_predictors_mb(xd);
}
+ /* When we have independent partitions we can apply residual even
+ * though other partitions within the frame are corrupt.
+ */
+ throw_residual = (!pbi->independent_partitions &&
+ pbi->frame_corrupt_residual);
+ throw_residual = (throw_residual || vp8dx_bool_error(xd->current_bc));
+
#if CONFIG_ERROR_CONCEALMENT
- if (pbi->ec_enabled &&
- (mb_idx >= pbi->mvs_corrupt_from_mb ||
- vp8dx_bool_error(xd->current_bc)))
+ if (pbi->ec_active &&
+ (mb_idx >= pbi->mvs_corrupt_from_mb || throw_residual))
{
/* MB with corrupt residuals or corrupt mode/motion vectors.
* Better to use the predictor as reconstruction.
*/
+ pbi->frame_corrupt_residual = 1;
vpx_memset(xd->qcoeff, 0, sizeof(xd->qcoeff));
vp8_conceal_corrupt_mb(xd);
return;
@@ -376,22 +385,28 @@
xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3;
#if CONFIG_ERROR_CONCEALMENT
- if (pbi->ec_enabled &&
- xd->mode_info_context->mbmi.ref_frame == INTRA_FRAME &&
- vp8dx_bool_error(xd->current_bc))
{
- /* We have an intra block with corrupt coefficients, better to
- * conceal with an inter block. Interpolate MVs from neighboring MBs
- *
- * Note that for the first mb with corrupt residual in a frame,
- * we might not discover that before decoding the residual. That
- * happens after this check, and therefore no inter concealment will
- * be done.
- */
- vp8_interpolate_motion(xd,
- mb_row, mb_col,
- pc->mb_rows, pc->mb_cols,
- pc->mode_info_stride);
+ int corrupt_residual = (!pbi->independent_partitions &&
+ pbi->frame_corrupt_residual) ||
+ vp8dx_bool_error(xd->current_bc);
+ if (pbi->ec_active &&
+ xd->mode_info_context->mbmi.ref_frame == INTRA_FRAME &&
+ corrupt_residual)
+ {
+ /* We have an intra block with corrupt coefficients, better to
+ * conceal with an inter block. Interpolate MVs from neighboring
+ * MBs.
+ *
+ * Note that for the first mb with corrupt residual in a frame,
+ * we might not discover that before decoding the residual. That
+ * happens after this check, and therefore no inter concealment
+ * will be done.
+ */
+ vp8_interpolate_motion(xd,
+ mb_row, mb_col,
+ pc->mb_rows, pc->mb_cols,
+ pc->mode_info_stride);
+ }
}
#endif
@@ -519,7 +534,7 @@
(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_enabled || !vp8dx_bool_error(&pbi->bc))
+ if (!pbi->ec_active || !vp8dx_bool_error(&pbi->bc))
pc->multi_token_partition = multi_token_partition;
num_part = 1 << pc->multi_token_partition;
@@ -551,7 +566,7 @@
{
if (read_is_valid(partition_size_ptr, 3, user_data_end))
partition_size = read_partition_size(partition_size_ptr);
- else if(pbi->ec_enabled)
+ else if (pbi->ec_active)
partition_size = bytes_left;
else
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
@@ -566,7 +581,7 @@
*/
if (!read_is_valid(partition, partition_size, user_data_end))
{
- if(pbi->ec_enabled)
+ if (pbi->ec_active)
partition_size = bytes_left;
else
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
@@ -659,6 +674,9 @@
xd->subpixel_predict8x8 = SUBPIX_INVOKE(RTCD_VTABLE(subpix), bilinear8x8);
xd->subpixel_predict16x16 = SUBPIX_INVOKE(RTCD_VTABLE(subpix), bilinear16x16);
}
+
+ if (pbi->decoded_key_frame && pbi->ec_enabled && !pbi->ec_active)
+ pbi->ec_active = 1;
}
xd->left_context = &pc->left_context;
@@ -681,6 +699,8 @@
int mb_row;
int i, j, k, l;
const int *const mb_feature_data_bits = vp8_mb_feature_data_bits;
+ int corrupt_tokens = 0;
+ int prev_independent_partitions = pbi->independent_partitions;
if (pbi->input_partition)
{
@@ -694,7 +714,7 @@
if (data_end - data < 3)
{
- if (pbi->ec_enabled)
+ if (pbi->ec_active)
{
/* Declare the missing frame as an inter frame since it will
be handled as an inter frame when we have estimated its
@@ -719,7 +739,7 @@
(data[0] | (data[1] << 8) | (data[2] << 16)) >> 5;
data += 3;
- if (!pbi->ec_enabled && (data + first_partition_length_in_bytes > data_end
+ if (!pbi->ec_active && (data + first_partition_length_in_bytes > data_end
|| data + first_partition_length_in_bytes < data))
vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME,
"Truncated packet or corrupt partition 0 length");
@@ -734,7 +754,7 @@
/* When error concealment is enabled we should only check the sync
* code if we have enough bits available
*/
- if (!pbi->ec_enabled || data + 3 < data_end)
+ if (!pbi->ec_active || data + 3 < data_end)
{
if (data[0] != 0x9d || data[1] != 0x01 || data[2] != 0x2a)
vpx_internal_error(&pc->error, VPX_CODEC_UNSUP_BITSTREAM,
@@ -745,7 +765,7 @@
* if we have enough data. Otherwise we will end up with the wrong
* size.
*/
- if (!pbi->ec_enabled || data + 6 < data_end)
+ if (!pbi->ec_active || data + 6 < data_end)
{
pc->Width = (data[3] | (data[4] << 8)) & 0x3fff;
pc->horiz_scale = data[4] >> 6;
@@ -944,7 +964,7 @@
#if CONFIG_ERROR_CONCEALMENT
/* Assume we shouldn't refresh golden if the bit is missing */
xd->corrupted |= vp8dx_bool_error(bc);
- if (pbi->ec_enabled && xd->corrupted)
+ if (pbi->ec_active && xd->corrupted)
pc->refresh_golden_frame = 0;
#endif
@@ -952,7 +972,7 @@
#if CONFIG_ERROR_CONCEALMENT
/* Assume we shouldn't refresh altref if the bit is missing */
xd->corrupted |= vp8dx_bool_error(bc);
- if (pbi->ec_enabled && xd->corrupted)
+ if (pbi->ec_active && xd->corrupted)
pc->refresh_alt_ref_frame = 0;
#endif
@@ -982,7 +1002,7 @@
#if CONFIG_ERROR_CONCEALMENT
/* Assume we should refresh the last frame if the bit is missing */
xd->corrupted |= vp8dx_bool_error(bc);
- if (pbi->ec_enabled && xd->corrupted)
+ if (pbi->ec_active && xd->corrupted)
pc->refresh_last_frame = 1;
#endif
@@ -1000,6 +1020,8 @@
}
{
+ pbi->independent_partitions = 1;
+
/* read coef probability tree */
for (i = 0; i < BLOCK_TYPES; i++)
for (j = 0; j < COEF_BANDS; j++)
@@ -1014,6 +1036,9 @@
*p = (vp8_prob)vp8_read_literal(bc, 8);
}
+ if (k > 0 && *p != pc->fc.coef_probs[i][j][k-1][l])
+ pbi->independent_partitions = 0;
+
}
}
@@ -1040,7 +1065,7 @@
vp8_decode_mode_mvs(pbi);
#if CONFIG_ERROR_CONCEALMENT
- if (pbi->ec_enabled &&
+ if (pbi->ec_active &&
pbi->mvs_corrupt_from_mb < (unsigned int)pc->mb_cols * pc->mb_rows)
{
/* Motion vectors are missing in this frame. We will try to estimate
@@ -1054,14 +1079,19 @@
#if CONFIG_MULTITHREAD
if (pbi->b_multithreaded_rd && pc->multi_token_partition != ONE_PARTITION)
{
+ int i;
+ pbi->frame_corrupt_residual = 0;
vp8mt_decode_mb_rows(pbi, xd);
vp8_yv12_extend_frame_borders_ptr(&pc->yv12_fb[pc->new_fb_idx]); /*cm->frame_to_show);*/
+ for (i = 0; i < pbi->decoding_thread_count; ++i)
+ corrupt_tokens |= pbi->mb_row_di[i].mbd.corrupted;
}
else
#endif
{
int ibc = 0;
int num_part = 1 << pc->multi_token_partition;
+ pbi->frame_corrupt_residual = 0;
/* Decode the individual macro block */
for (mb_row = 0; mb_row < pc->mb_rows; mb_row++)
@@ -1078,17 +1108,26 @@
decode_mb_row(pbi, pc, mb_row, xd);
}
+ corrupt_tokens |= xd->corrupted;
}
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);
+ 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;
+ pc->yv12_fb[pc->new_fb_idx].corrupted |= corrupt_tokens;
+
+ if (!pbi->decoded_key_frame)
+ {
+ if (pc->frame_type == KEY_FRAME &&
+ !pc->yv12_fb[pc->new_fb_idx].corrupted)
+ pbi->decoded_key_frame = 1;
+ else
+ vpx_internal_error(&pbi->common.error, VPX_CODEC_CORRUPT_FRAME,
+ "A stream must start with a complete key frame");
+ }
/* vpx_log("Decoder: Frame Decoded, Size Roughly:%d bytes \n",bc->pos+pbi->bc2.pos); */
@@ -1102,6 +1141,7 @@
if (pc->refresh_entropy_probs == 0)
{
vpx_memcpy(&pc->fc, &pc->lfc, sizeof(pc->fc));
+ pbi->independent_partitions = prev_independent_partitions;
}
#ifdef PACKET_TESTING
diff --git a/vp8/decoder/onyxd_if.c b/vp8/decoder/onyxd_if.c
index 2246194..db6528c 100644
--- a/vp8/decoder/onyxd_if.c
+++ b/vp8/decoder/onyxd_if.c
@@ -101,9 +101,21 @@
#else
pbi->ec_enabled = 0;
#endif
+ /* Error concealment is activated after a key frame has been
+ * decoded without errors when error concealment is enabled.
+ */
+ pbi->ec_active = 0;
+
+ pbi->decoded_key_frame = 0;
pbi->input_partition = oxcf->input_partition;
+ /* Independent partitions is activated when a frame updates the
+ * token probability table to have equal probabilities over the
+ * PREV_COEF context.
+ */
+ pbi->independent_partitions = 0;
+
return (VP8D_PTR) pbi;
}
@@ -346,11 +358,15 @@
/* If error concealment is disabled we won't signal missing frames to
* the decoder.
*/
- if (!pbi->ec_enabled)
+ if (!pbi->ec_active)
{
/* Signal that we have no frame to show. */
cm->show_frame = 0;
+ pbi->num_partitions = 0;
+ if (pbi->input_partition)
+ pbi->common.multi_token_partition = 0;
+
/* Nothing more to do. */
return 0;
}
@@ -379,6 +395,10 @@
#endif
pbi->common.error.setjmp = 0;
+ pbi->num_partitions = 0;
+ if (pbi->input_partition)
+ pbi->common.multi_token_partition = 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.
diff --git a/vp8/decoder/onyxd_int.h b/vp8/decoder/onyxd_int.h
index eac57ab..4ece431 100644
--- a/vp8/decoder/onyxd_int.h
+++ b/vp8/decoder/onyxd_int.h
@@ -132,7 +132,11 @@
unsigned int mvs_corrupt_from_mb;
#endif
int ec_enabled;
+ int ec_active;
int input_partition;
+ int decoded_key_frame;
+ int independent_partitions;
+ int frame_corrupt_residual;
} VP8D_COMP;
diff --git a/vp8/decoder/threading.c b/vp8/decoder/threading.c
index 1e03302..fdde04a 100644
--- a/vp8/decoder/threading.c
+++ b/vp8/decoder/threading.c
@@ -93,6 +93,7 @@
static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, int mb_row, int mb_col)
{
int eobtotal = 0;
+ int throw_residual = 0;
int i, do_clamp = xd->mode_info_context->mbmi.need_to_clamp_mvs;
if (xd->mode_info_context->mbmi.mb_skip_coeff)
@@ -112,7 +113,7 @@
eobtotal |= (xd->mode_info_context->mbmi.mode == B_PRED ||
xd->mode_info_context->mbmi.mode == SPLITMV);
- if (!eobtotal)
+ if (!eobtotal && !vp8dx_bool_error(xd->current_bc))
{
/* Special case: Force the loopfilter to skip when eobtotal and
* mb_skip_coeff are zero.
@@ -154,14 +155,22 @@
vp8_build_inter_predictors_mb(xd);
}
+ /* When we have independent partitions we can apply residual even
+ * though other partitions within the frame are corrupt.
+ */
+ throw_residual = (!pbi->independent_partitions &&
+ pbi->frame_corrupt_residual);
+ throw_residual = (throw_residual || vp8dx_bool_error(xd->current_bc));
+
#if CONFIG_ERROR_CONCEALMENT
- if (pbi->ec_enabled &&
+ if (pbi->ec_active &&
(mb_row * pbi->common.mb_cols + mb_col >= pbi->mvs_corrupt_from_mb ||
- vp8dx_bool_error(xd->current_bc)))
+ throw_residual))
{
/* MB with corrupt residuals or corrupt mode/motion vectors.
* Better to use the predictor as reconstruction.
*/
+ pbi->frame_corrupt_residual = 1;
vpx_memset(xd->qcoeff, 0, sizeof(xd->qcoeff));
vp8_conceal_corrupt_mb(xd);
return;
@@ -314,25 +323,32 @@
xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3;
#if CONFIG_ERROR_CONCEALMENT
- if (pbi->ec_enabled &&
- (xd->mode_info_context->mbmi.ref_frame ==
- INTRA_FRAME) &&
- vp8dx_bool_error(xd->current_bc))
{
- /* We have an intra block with corrupt coefficients,
- * better to conceal with an inter block.
- * Interpolate MVs from neighboring MBs
- *
- * Note that for the first mb with corrupt residual
- * in a frame, we might not discover that before
- * decoding the residual. That happens after this
- * check, and therefore no inter concealment will be
- * done.
- */
- vp8_interpolate_motion(xd,
- mb_row, mb_col,
- pc->mb_rows, pc->mb_cols,
- pc->mode_info_stride);
+ int corrupt_residual =
+ (!pbi->independent_partitions &&
+ pbi->frame_corrupt_residual) ||
+ vp8dx_bool_error(xd->current_bc);
+ if (pbi->ec_active &&
+ (xd->mode_info_context->mbmi.ref_frame ==
+ INTRA_FRAME) &&
+ corrupt_residual)
+ {
+ /* We have an intra block with corrupt
+ * coefficients, better to conceal with an inter
+ * block.
+ * Interpolate MVs from neighboring MBs
+ *
+ * Note that for the first mb with corrupt
+ * residual in a frame, we might not discover
+ * that before decoding the residual. That
+ * happens after this check, and therefore no
+ * inter concealment will be done.
+ */
+ vp8_interpolate_motion(xd,
+ mb_row, mb_col,
+ pc->mb_rows, pc->mb_cols,
+ pc->mode_info_stride);
+ }
}
#endif
@@ -355,9 +371,19 @@
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);
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)
{
int skip_lf = (xd->mode_info_context->mbmi.mode != B_PRED &&
@@ -803,23 +829,28 @@
xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3;
#if CONFIG_ERROR_CONCEALMENT
- if (pbi->ec_enabled &&
- (xd->mode_info_context->mbmi.ref_frame == INTRA_FRAME) &&
- vp8dx_bool_error(xd->current_bc))
{
- /* We have an intra block with corrupt coefficients, better
- * to conceal with an inter block. Interpolate MVs from
- * neighboring MBs
- *
- * Note that for the first mb with corrupt residual in a
- * frame, we might not discover that before decoding the
- * residual. That happens after this check, and therefore no
- * inter concealment will be done.
- */
- vp8_interpolate_motion(xd,
- mb_row, mb_col,
- pc->mb_rows, pc->mb_cols,
- pc->mode_info_stride);
+ int corrupt_residual = (!pbi->independent_partitions &&
+ pbi->frame_corrupt_residual) ||
+ vp8dx_bool_error(xd->current_bc);
+ if (pbi->ec_active &&
+ (xd->mode_info_context->mbmi.ref_frame == INTRA_FRAME) &&
+ corrupt_residual)
+ {
+ /* We have an intra block with corrupt coefficients,
+ * better to conceal with an inter block. Interpolate
+ * MVs from neighboring MBs
+ *
+ * Note that for the first mb with corrupt residual in a
+ * frame, we might not discover that before decoding the
+ * residual. That happens after this check, and
+ * therefore no inter concealment will be done.
+ */
+ vp8_interpolate_motion(xd,
+ mb_row, mb_col,
+ pc->mb_rows, pc->mb_cols,
+ pc->mode_info_stride);
+ }
}
#endif