From 810a06b12caf7206bf8ed78c8abfb04620b2a90c Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Fri, 5 Aug 2011 12:27:25 -0400 Subject: [PATCH 1/3] Fix potential OOB read with Error Concealment This patch fixes an OOB read when error concealment is enabled and the partition sizes are corrupt. The partition size read from the bitstream was not being validated in EC mode. Change-Id: Ia81dfd4bce1ab29ee78e42320abe52cee8318974 --- vp8/decoder/decodframe.c | 47 ++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c index 9b35ffd66..5ddf09567 100644 --- a/vp8/decoder/decodframe.c +++ b/vp8/decoder/decodframe.c @@ -495,6 +495,15 @@ static void setup_token_decoder_partition_input(VP8D_COMP *pbi) #endif } + +static int read_is_valid(const unsigned char *start, + size_t len, + const unsigned char *end) +{ + return (start + len > start && start + len <= end); +} + + static void setup_token_decoder(VP8D_COMP *pbi, const unsigned char *cx_data) { @@ -529,25 +538,41 @@ static void setup_token_decoder(VP8D_COMP *pbi, for (i = 0; i < num_part; i++) { const unsigned char *partition_size_ptr = cx_data + i * 3; - ptrdiff_t partition_size; + ptrdiff_t partition_size, bytes_left; + + bytes_left = user_data_end - partition; /* Calculate the length of this partition. The last partition - * size is implicit. + * 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) { - partition_size = read_partition_size(partition_size_ptr); + if (read_is_valid(partition_size_ptr, 3, user_data_end)) + partition_size = read_partition_size(partition_size_ptr); + else if(pbi->ec_enabled) + partition_size = bytes_left; + else + vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME, + "Truncated partition size data"); } else - { - partition_size = user_data_end - partition; - } + partition_size = bytes_left; - if (!pbi->ec_enabled && (partition + partition_size > user_data_end - || partition + partition_size < partition)) - vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME, - "Truncated packet or corrupt partition " - "%d length", i + 1); + /* 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)) + { + if(pbi->ec_enabled) + partition_size = bytes_left; + else + vpx_internal_error(&pc->error, VPX_CODEC_CORRUPT_FRAME, + "Truncated packet or corrupt partition " + "%d length", i + 1); + } if (vp8dx_start_decode(bool_decoder, partition, partition_size)) vpx_internal_error(&pc->error, VPX_CODEC_MEM_ERROR, From 3e10be93f2e9f8707e5b4ce235081fdd2471c31d Mon Sep 17 00:00:00 2001 From: Stefan Holmer Date: Mon, 8 Aug 2011 10:56:20 +0200 Subject: [PATCH 2/3] 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 --- vp8/decoder/decodframe.c | 108 +++++++++++++++++++++++++++------------ vp8/decoder/onyxd_if.c | 22 +++++++- vp8/decoder/onyxd_int.h | 4 ++ vp8/decoder/threading.c | 105 +++++++++++++++++++++++-------------- 4 files changed, 167 insertions(+), 72 deletions(-) diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c index 5ddf09567..ddb09703b 100644 --- a/vp8/decoder/decodframe.c +++ b/vp8/decoder/decodframe.c @@ -183,6 +183,7 @@ static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, unsigned int mb_idx) { int eobtotal = 0; + int throw_residual = 0; MB_PREDICTION_MODE mode; int i; @@ -203,7 +204,8 @@ static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, 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 @@ static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, 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 @@ decode_mb_row(VP8D_COMP *pbi, VP8_COMMON *pc, int mb_row, MACROBLOCKD *xd) 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 @@ static void setup_token_decoder(VP8D_COMP *pbi, (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 @@ static void setup_token_decoder(VP8D_COMP *pbi, { 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 @@ static void setup_token_decoder(VP8D_COMP *pbi, */ 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 @@ static void init_frame(VP8D_COMP *pbi) 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 vp8_decode_frame(VP8D_COMP *pbi) 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 @@ int vp8_decode_frame(VP8D_COMP *pbi) 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 @@ int vp8_decode_frame(VP8D_COMP *pbi) (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 @@ int vp8_decode_frame(VP8D_COMP *pbi) /* 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 @@ int vp8_decode_frame(VP8D_COMP *pbi) * 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 @@ int vp8_decode_frame(VP8D_COMP *pbi) #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 @@ int vp8_decode_frame(VP8D_COMP *pbi) #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 @@ int vp8_decode_frame(VP8D_COMP *pbi) #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 @@ int vp8_decode_frame(VP8D_COMP *pbi) } { + 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 @@ int vp8_decode_frame(VP8D_COMP *pbi) *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 @@ int vp8_decode_frame(VP8D_COMP *pbi) 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 @@ int vp8_decode_frame(VP8D_COMP *pbi) #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 @@ int vp8_decode_frame(VP8D_COMP *pbi) 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 @@ int vp8_decode_frame(VP8D_COMP *pbi) 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 224619480..db6528c80 100644 --- a/vp8/decoder/onyxd_if.c +++ b/vp8/decoder/onyxd_if.c @@ -101,9 +101,21 @@ VP8D_PTR vp8dx_create_decompressor(VP8D_CONFIG *oxcf) #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 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign /* 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 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign #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 eac57ab35..4ece4312a 100644 --- a/vp8/decoder/onyxd_int.h +++ b/vp8/decoder/onyxd_int.h @@ -132,7 +132,11 @@ typedef struct VP8Decompressor 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 1e0330277..fdde04a88 100644 --- a/vp8/decoder/threading.c +++ b/vp8/decoder/threading.c @@ -93,6 +93,7 @@ static void setup_decoding_thread_data(VP8D_COMP *pbi, MACROBLOCKD *xd, MB_ROW_D 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 @@ static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, int mb_row, int m 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 @@ static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, int mb_row, int m 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 @@ static THREAD_FUNCTION thread_decoding_proc(void *p_data) 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 @@ static THREAD_FUNCTION thread_decoding_proc(void *p_data) 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 @@ void vp8mt_decode_mb_rows( VP8D_COMP *pbi, MACROBLOCKD *xd) 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 From 91206793c238871b3eaac48261584fed9dabb4c1 Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Fri, 12 Aug 2011 11:30:54 -0400 Subject: [PATCH 3/3] Propagate macroblock MV to subblocks for error concealment EC expects the subblock MVs to be populated, but f1d6cc79e43f0066632f19c1854ca365086b712b removed this code. This commit restores it, protected by CONFIG_ERROR_CONCEALMENT. May move this to the EC code more directly in the future. Change-Id: I44f8f985720cb9a1bf222e59143f9e69abf56ad2 --- vp8/decoder/decodemv.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/vp8/decoder/decodemv.c b/vp8/decoder/decodemv.c index 0a7942d89..54547d95c 100644 --- a/vp8/decoder/decodemv.c +++ b/vp8/decoder/decodemv.c @@ -400,18 +400,18 @@ static void read_mb_modes_mv(VP8D_COMP *pbi, MODE_INFO *mi, MB_MODE_INFO *mbmi, /* Clip "next_nearest" so that it does not extend to far out of image */ vp8_clamp_mv(mv, mb_to_left_edge, mb_to_right_edge, mb_to_top_edge, mb_to_bottom_edge); - break; + goto propagate_mv; case NEARESTMV: mv->as_int = nearest.as_int; /* Clip "next_nearest" so that it does not extend to far out of image */ vp8_clamp_mv(mv, mb_to_left_edge, mb_to_right_edge, mb_to_top_edge, mb_to_bottom_edge); - break; + goto propagate_mv; case ZEROMV: mv->as_int = 0; - break; + goto propagate_mv; case NEWMV: read_mv(bc, &mv->as_mv, (const MV_CONTEXT *) mvc); @@ -428,8 +428,30 @@ static void read_mb_modes_mv(VP8D_COMP *pbi, MODE_INFO *mi, MB_MODE_INFO *mbmi, mb_to_right_edge, mb_to_top_edge, mb_to_bottom_edge); - break; + propagate_mv: /* same MV throughout */ +#if CONFIG_ERROR_CONCEALMENT + if(pbi->ec_enabled) + { + mi->bmi[ 0].mv.as_int = + mi->bmi[ 1].mv.as_int = + mi->bmi[ 2].mv.as_int = + mi->bmi[ 3].mv.as_int = + mi->bmi[ 4].mv.as_int = + mi->bmi[ 5].mv.as_int = + mi->bmi[ 6].mv.as_int = + mi->bmi[ 7].mv.as_int = + mi->bmi[ 8].mv.as_int = + mi->bmi[ 9].mv.as_int = + mi->bmi[10].mv.as_int = + mi->bmi[11].mv.as_int = + mi->bmi[12].mv.as_int = + mi->bmi[13].mv.as_int = + mi->bmi[14].mv.as_int = + mi->bmi[15].mv.as_int = mv->as_int; + } +#endif + break; default:; #if CONFIG_DEBUG assert(0);