diff --git a/examples.mk b/examples.mk index a30205d31..534440272 100644 --- a/examples.mk +++ b/examples.mk @@ -77,6 +77,11 @@ GEN_EXAMPLES-$(CONFIG_ENCODERS) += decode_with_drops.c endif decode_with_drops.GUID = CE5C53C4-8DDA-438A-86ED-0DDD3CDB8D26 decode_with_drops.DESCRIPTION = Drops frames while decoding +ifeq ($(CONFIG_DECODERS),yes) +GEN_EXAMPLES-$(CONFIG_ENCODERS) += decode_with_partial_drops.c +endif +decode_partial_with_drops.GUID = CE5C53C4-8DDA-438A-86ED-0DDD3CDB8D27 +decode_partial_with_drops.DESCRIPTION = Drops parts of frames while decoding GEN_EXAMPLES-$(CONFIG_ENCODERS) += error_resilient.c error_resilient.GUID = DF5837B9-4145-4F92-A031-44E4F832E00C error_resilient.DESCRIPTION = Error Resiliency Feature diff --git a/examples/decode_with_partial_drops.txt b/examples/decode_with_partial_drops.txt new file mode 100644 index 000000000..45baee2e8 --- /dev/null +++ b/examples/decode_with_partial_drops.txt @@ -0,0 +1,213 @@ +@TEMPLATE decoder_tmpl.c +Decode With Drops Example +========================= +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTRODUCTION +This is an example utility which drops a series of frames, as specified +on the command line. This is useful for observing the error recovery +features of the codec. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTRODUCTION + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_INCLUDES +#include +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_INCLUDES + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS +struct parsed_header +{ + char key_frame; + int version; + char show_frame; + int first_part_size; +}; + +int next_packet(struct parsed_header* hdr, int pos, int length, int mtu) +{ + int size = 0; + int remaining = length - pos; + /* Uncompressed part is 3 bytes for P frames and 10 bytes for I frames */ + int uncomp_part_size = (hdr->key_frame ? 10 : 3); + /* number of bytes yet to send from header and the first partition */ + int remainFirst = uncomp_part_size + hdr->first_part_size - pos; + if (remainFirst > 0) + { + if (remainFirst <= mtu) + { + size = remainFirst; + } + else + { + size = mtu; + } + + return size; + } + + /* second partition; just slot it up according to MTU */ + if (remaining <= mtu) + { + size = remaining; + return size; + } + return mtu; +} + +void throw_packets(unsigned char* frame, int* size, int loss_rate, int* thrown, int* kept) +{ + unsigned char loss_frame[256*1024]; + int pkg_size = 1; + int count = 0; + int pos = 0; + int loss_pos = 0; + struct parsed_header hdr; + unsigned int tmp; + int mtu = 100; + + if (*size < 3) + { + return; + } + putc('|', stdout); + /* parse uncompressed 3 bytes */ + tmp = (frame[2] << 16) | (frame[1] << 8) | frame[0]; + hdr.key_frame = !(tmp & 0x1); /* inverse logic */ + hdr.version = (tmp >> 1) & 0x7; + hdr.show_frame = (tmp >> 4) & 0x1; + hdr.first_part_size = (tmp >> 5) & 0x7FFFF; + + /* don't drop key frames */ + if (hdr.key_frame) + { + int i; + *kept = *size/mtu + ((*size % mtu > 0) ? 1 : 0); /* approximate */ + for (i=0; i < *kept; i++) + putc('.', stdout); + return; + } + + while ((pkg_size = next_packet(&hdr, pos, *size, mtu)) > 0) + { + int loss_event = ((rand() + 1.0)/(RAND_MAX + 1.0) < loss_rate/100.0); + if (*thrown == 0 && !loss_event) + { + memcpy(loss_frame + loss_pos, frame + pos, pkg_size); + loss_pos += pkg_size; + (*kept)++; + putc('.', stdout); + } + else + { + (*thrown)++; + putc('X', stdout); + } + pos += pkg_size; + } + memcpy(frame, loss_frame, loss_pos); + memset(frame + loss_pos, 0, *size - loss_pos); + *size = loss_pos; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS + +Usage +----- +This example adds a single argument to the `simple_decoder` example, +which specifies the range or pattern of frames to drop. The parameter is +parsed as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USAGE +if(argc!=4 && argc != 5) + die("Usage: %s \n", argv[0]); +{ + char *nptr; + n = strtol(argv[3], &nptr, 0); + mode = (*nptr == '\0' || *nptr == ',') ? 2 : (*nptr == '-') ? 1 : 0; + + m = strtol(nptr+1, NULL, 0); + if((!n && !m) || (*nptr != '-' && *nptr != '/' && + *nptr != '\0' && *nptr != ',')) + die("Couldn't parse pattern %s\n", argv[3]); +} +seed = (m > 0) ? m : (unsigned int)time(NULL); +srand(seed);thrown_frame = 0; +printf("Seed: %u\n", seed); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USAGE + + +Dropping A Range Of Frames +-------------------------- +To drop a range of frames, specify the starting frame and the ending +frame to drop, separated by a dash. The following command will drop +frames 5 through 10 (base 1). + + $ ./decode_with_drops in.ivf out.i420 5-10 + + +Dropping A Pattern Of Frames +---------------------------- +To drop a pattern of frames, specify the number of frames to drop and +the number of frames after which to repeat the pattern, separated by +a forward-slash. The following command will drop 3 of 7 frames. +Specifically, it will decode 4 frames, then drop 3 frames, and then +repeat. + + $ ./decode_with_drops in.ivf out.i420 3/7 + + +Extra Variables +--------------- +This example maintains the pattern passed on the command line in the +`n`, `m`, and `is_range` variables: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_VARS +int n, m, mode; // +unsigned int seed; +int thrown=0, kept=0; +int thrown_frame=0, kept_frame=0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_VARS + + +Making The Drop Decision +------------------------ +The example decides whether to drop the frame based on the current +frame number, immediately before decoding the frame. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PRE_DECODE +/* Decide whether to throw parts of the frame or the whole frame + depending on the drop mode */ +thrown_frame = 0; +kept_frame = 0; +switch (mode) +{ +case 0: + if (m - (frame_cnt-1)%m <= n) + { + frame_sz = 0; + } + break; +case 1: + if (frame_cnt >= n && frame_cnt <= m) + { + frame_sz = 0; + } + break; +case 2: + throw_packets(frame, &frame_sz, n, &thrown_frame, &kept_frame); + break; +default: break; +} +if (mode < 2) +{ + if (frame_sz == 0) + { + putc('X', stdout); + thrown_frame++; + } + else + { + putc('.', stdout); + kept_frame++; + } +} +thrown += thrown_frame; +kept += kept_frame; +fflush(stdout); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PRE_DECODE diff --git a/examples/decoder_tmpl.c b/examples/decoder_tmpl.c index 26b745d34..66b67fb93 100644 --- a/examples/decoder_tmpl.c +++ b/examples/decoder_tmpl.c @@ -42,6 +42,8 @@ static void die(const char *fmt, ...) { @DIE_CODEC +@HELPERS + int main(int argc, char **argv) { FILE *infile, *outfile; vpx_codec_ctx_t codec; diff --git a/vp8/common/alloccommon.c b/vp8/common/alloccommon.c index bbff62b2f..a315a7c52 100644 --- a/vp8/common/alloccommon.c +++ b/vp8/common/alloccommon.c @@ -12,6 +12,7 @@ #include "vpx_ports/config.h" #include "blockd.h" #include "vpx_mem/vpx_mem.h" +#include "error_concealment.h" #include "onyxc_int.h" #include "findnearmv.h" #include "entropymode.h" @@ -28,6 +29,9 @@ void vp8_update_mode_info_border(MODE_INFO *mi, int rows, int cols) for (i = 0; i < rows; i++) { + /* TODO(holmer): Bug? This updates the last element of each row + * rather than the border element! + */ vpx_memset(&mi[i*cols-1], 0, sizeof(MODE_INFO)); } } @@ -124,7 +128,6 @@ int vp8_alloc_frame_buffers(VP8_COMMON *oci, int width, int height) oci->prev_mi = oci->prev_mip + oci->mode_info_stride + 1; - oci->above_context = vpx_calloc(sizeof(ENTROPY_CONTEXT_PLANES) * oci->mb_cols, 1); if (!oci->above_context) diff --git a/vp8/decoder/decodemv.c b/vp8/decoder/decodemv.c index 5d710606d..3358a5521 100644 --- a/vp8/decoder/decodemv.c +++ b/vp8/decoder/decodemv.c @@ -413,7 +413,7 @@ void vp8_read_mb_modes_mv(VP8D_COMP *pbi, MODE_INFO *mi, MB_MODE_INFO *mbmi, do { mi->bmi[ *fill_offset] = bmi; - fill_offset++; + fill_offset++; }while (--fill_count); } diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c index 4d2d292f6..0a240bf8e 100644 --- a/vp8/decoder/decodframe.c +++ b/vp8/decoder/decodframe.c @@ -227,8 +227,8 @@ void vp8_decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, unsigned int mb_idx) } /* TODO(holmer): change when we have MB level error tracking */ - if (xd->mode_info_context->mbmi.ref_frame != INTRA_FRAME && - (xd->corrupted || mb_idx >= pbi->mvs_corrupt_from_mb)) + if (pbi->ec_enabled && xd->mode_info_context->mbmi.ref_frame != INTRA_FRAME + && (xd->corrupted || mb_idx >= pbi->mvs_corrupt_from_mb)) { vp8_conceal_corrupt_block(xd); return; @@ -664,6 +664,11 @@ int vp8_decode_frame(VP8D_COMP *pbi) vpx_internal_error(&pc->error, VPX_CODEC_MEM_ERROR, "Failed to allocate frame buffers"); + if (vp8_alloc_overlap_lists(pbi)) + vpx_internal_error(&pc->error, VPX_CODEC_MEM_ERROR, + "Failed to allocate overlap lists for " + "error concealment"); + #if CONFIG_MULTITHREAD if (pbi->b_multithreaded_rd) vp8mt_alloc_temp_buffers(pbi, pc->Width, prev_mb_rows); @@ -890,7 +895,8 @@ int vp8_decode_frame(VP8D_COMP *pbi) vp8_decode_mode_mvs(pbi); - if (pbi->ec_enabled && pbi->mvs_corrupt_from_mb < (unsigned int)pc->mb_cols * pc->mb_rows) + if (pbi->ec_enabled && + pbi->mvs_corrupt_from_mb < (unsigned int)pc->mb_cols * pc->mb_rows) { vp8_estimate_missing_mvs(pbi); } diff --git a/vp8/decoder/error_concealment.c b/vp8/decoder/error_concealment.c index 0d73fe17c..38b12e09f 100644 --- a/vp8/decoder/error_concealment.c +++ b/vp8/decoder/error_concealment.c @@ -12,61 +12,384 @@ #include "onyxd_int.h" #include "vpx_mem/vpx_mem.h" +#include + +#define MIN(x,y) (((x)<(y))?(x):(y)) +#define MAX(x,y) (((x)>(y))?(x):(y)) + +#define FLOOR(x,q) ((x) & -(1 << (q))) + +int vp8_alloc_overlap_lists(VP8D_COMP *pbi) +{ + if (pbi->overlaps != NULL) + { + vpx_free(pbi->overlaps); + pbi->overlaps = NULL; + } + pbi->overlaps = vpx_calloc(pbi->common.mb_rows * pbi->common.mb_cols, + sizeof(MB_OVERLAP)); + vpx_memset(pbi->overlaps, 0, + sizeof(MB_OVERLAP) * pbi->common.mb_rows * pbi->common.mb_cols); + if (pbi->overlaps == NULL) + return -1; + return 0; +} + +void vp8_de_alloc_overlap_lists(VP8D_COMP *pbi) +{ + if (pbi->overlaps != NULL) + { + vpx_free(pbi->overlaps); + pbi->overlaps = NULL; + } +} + +void vp8_assign_overlap(OVERLAP_NODE* overlaps, + B_MODE_INFO *bmi, + MV_REFERENCE_FRAME ref_frame, + int overlap) +{ + int i; + if (overlap <= 0) + return; + for (i = 0; i < MAX_OVERLAPS; i++) + { + if (overlaps[i].bmi == NULL) + { + overlaps[i].bmi = bmi; + overlaps[i].ref_frame = ref_frame; + overlaps[i].overlap = overlap; + break; + } + } +} + +int vp8_block_overlap(int b1_row, int b1_col, int b2_row, int b2_col) +{ + const int int_top = MAX(b1_row, b2_row); // top + const int int_left = MAX(b1_col, b2_col); // left + const int int_right = MIN(b1_col + (4<<3), b2_col + (4<<3)); // right + const int int_bottom = MIN(b1_row + (4<<3), b2_row + (4<<3)); // bottom + return (int_bottom - int_top) * (int_right - int_left); +} + +void vp8_calculate_overlaps_mb(B_OVERLAP *b_overlaps, B_MODE_INFO *bmi, + MV_REFERENCE_FRAME ref_frame, + int new_row, int new_col, + int first_ol_mb_row, int first_ol_mb_col, + int first_ol_blk_row, int first_ol_blk_col) +{ + /* find the blocks it's overlapping */ + const int rel_ol_blk_row = first_ol_blk_row - first_ol_mb_row * 4; + const int rel_ol_blk_col = first_ol_blk_col - first_ol_mb_col * 4; + const int blk_idx = MAX(rel_ol_blk_row,0) * 4 + MAX(rel_ol_blk_col,0); + /* Upper left overlapping block */ + B_OVERLAP *b_ol_ul = &(b_overlaps[blk_idx]); + + /* Calculate and assign overlaps for all blocks in this MB + * which the motion compensated block overlaps + */ + int row, col; + int end_row = MIN(4 + first_ol_mb_row * 4 - first_ol_blk_row, 2); + int end_col = MIN(4 + first_ol_mb_col * 4 - first_ol_blk_col, 2); + + /* Check if new_row and new_col are evenly divisible by 4 (Q3), + * and if so we shouldn't check neighboring blocks + */ + if (new_row >= 0 && (new_row & 0x1F) == 0) + end_row = 1; + if (new_col >= 0 && (new_col & 0x1F) == 0) + end_col = 1; + + /* Avoid calculating overlap for blocks in the previous MB */ + if (new_row < (first_ol_mb_row*16)<<3) + end_row = 1; + if (new_col < (first_ol_mb_col*16)<<3) + end_col = 1; + + for (row = 0; row < end_row; ++row) + { + for (col = 0; col < end_col; ++col) + { + /* input in Q3, result in Q6 */ + const int overlap = vp8_block_overlap(new_row, new_col, + (((first_ol_blk_row + row) * + 4) << 3), + (((first_ol_blk_col + col) * + 4) << 3)); + vp8_assign_overlap(b_ol_ul[row * 4 + col].overlaps, + bmi, + ref_frame, + overlap); + } + } +} + +void vp8_calculate_overlaps_submb(MB_OVERLAP *overlap_ul, + int mb_rows, int mb_cols, + B_MODE_INFO *bmi, + MV_REFERENCE_FRAME ref_frame, + int b_row, int b_col) +{ + MB_OVERLAP *mb_overlap; + int row, col, rel_row, rel_col; + int new_row, new_col; + int new_row_pos, new_col_pos; + int end_row, end_col; + int overlap_b_row, overlap_b_col; + int overlap_mb_row, overlap_mb_col; + int i; + B_MODE_INFO *obmi; + int overlap; + + if (ref_frame == INTRA_FRAME) + return; + + /* mb subpixel position */ + row = (4 * b_row) << 3; /* Q3 */ + col = (4 * b_col) << 3; /* Q3 */ + + /* reverse compensate for motion */ + new_row = row - bmi->mv.as_mv.row; + new_col = col - bmi->mv.as_mv.col; + + if (new_row >= ((16*mb_rows) << 3) || new_col >= ((16*mb_cols) << 3)) + { + /* the new block ended up outside the frame */ + return; + } + + if (new_row <= (-4 << 3) || new_col <= (-4 << 3)) + { + /* outside the frame */ + return; + } + /* overlapping block's position in blocks */ + overlap_b_row = FLOOR(new_row / 4, 3) >> 3; + overlap_b_col = FLOOR(new_col / 4, 3) >> 3; + + /* overlapping block's MB position in MBs + * operations are done in Q3 + */ + overlap_mb_row = FLOOR((overlap_b_row << 3) / 4, 3) >> 3; + overlap_mb_col = FLOOR((overlap_b_col << 3) / 4, 3) >> 3; + + end_row = MIN(mb_rows - overlap_mb_row, 2); + end_col = MIN(mb_cols - overlap_mb_col, 2); + + /* Don't calculate overlap for MBs we don't overlap */ + /* Check if the new block row starts at the last block row of the MB */ + if (abs(new_row - ((16*overlap_mb_row) << 3)) < ((3*4) << 3)) + end_row = 1; + /* Check if the new block col starts at the last block col of the MB */ + if (abs(new_col - ((16*overlap_mb_col) << 3)) < ((3*4) << 3)) + end_col = 1; + /* Check if our MV is even (only overlapping one block) */ +// if (abs(bmi->mv.as_mv.row) % (4 << 3) == 0) +// end_row = 1; +// if (abs(bmi->mv.as_mv.col) % (4 << 3) == 0) +// end_col = 1; + + /* find the MB(s) this block is overlapping */ + for (rel_row = 0; rel_row < end_row; ++rel_row) + { + for (rel_col = 0; rel_col < end_col; ++rel_col) + { + if (overlap_mb_row + rel_row < 0 || + overlap_mb_col + rel_col < 0) + continue; + mb_overlap = overlap_ul + (overlap_mb_row + rel_row) * mb_cols + + overlap_mb_col + rel_col; + + vp8_calculate_overlaps_mb(mb_overlap->overlaps, bmi, ref_frame, + new_row, new_col, + overlap_mb_row + rel_row, + overlap_mb_col + rel_col, + overlap_b_row + rel_row, + overlap_b_col + rel_col); + } + } +} + +MV_REFERENCE_FRAME vp8_largest_overlap_type(const B_OVERLAP *block_overlaps) +{ + int i, j; + int overlap_per_type[MAX_REF_FRAMES] = {0}; + int largest_overlap = 0; + MV_REFERENCE_FRAME largest_overlap_type = LAST_FRAME; + for (i=0; i < 16; ++i) + { + const OVERLAP_NODE *overlaps = block_overlaps->overlaps; + for (j=0; j < MAX_OVERLAPS; ++j) + { + if (overlaps[j].bmi != NULL) + { + overlap_per_type[overlaps[j].ref_frame] += overlaps[j].overlap; + if (overlap_per_type[overlaps[j].ref_frame] > largest_overlap) + { + largest_overlap = overlap_per_type[overlaps[j].ref_frame]; + largest_overlap_type = overlaps[j].ref_frame; + } + assert(overlaps[j].overlap < (16*16)<<6); + } + } + ++block_overlaps; + } + return largest_overlap_type; +} + +void vp8_estimate_mv(const OVERLAP_NODE *overlaps, B_MODE_INFO *bmi, + MV_REFERENCE_FRAME type) +{ + int i; + int overlap_sum = 0; + int row_acc = 0; + int col_acc = 0; + + bmi->mv.as_int = 0; + for (i=0; i < MAX_OVERLAPS; ++i) + { + if (overlaps[i].bmi != NULL && + overlaps[i].ref_frame == type) + { + col_acc += overlaps[i].overlap * overlaps[i].bmi->mv.as_mv.col; + row_acc += overlaps[i].overlap * overlaps[i].bmi->mv.as_mv.row; + overlap_sum += overlaps[i].overlap; + } + } + if (overlap_sum > 0) + { + /* Q9 / Q6 = Q3 */ + bmi->mv.as_mv.col = col_acc / overlap_sum; + bmi->mv.as_mv.row = row_acc / overlap_sum; + /* TODO(holmer): Get the mode from the most overlapping block? + * Or is the mode only used when decoding the MVs? + */ + bmi->mode = NEW4X4; + } + else + { + bmi->mv.as_mv.col = 0; + bmi->mv.as_mv.row = 0; + bmi->mode = NEW4X4; + } +} + +void vp8_estimate_mb_mvs(const B_OVERLAP *block_overlaps, + B_MODE_INFO *bmi, + MV_REFERENCE_FRAME type, + MV* filtered_mv) +{ + int i; + int non_zero_count = 0; + filtered_mv->col = 0; + filtered_mv->row = 0; + for (i = 0; i < 16; ++i) + { + /* TODO(holmer): How can we be certain that all blocks refer + * to the same frame buffer? We can't + */ + /* Estimate vectors for all blocks which are overlapped by this + * type + */ + /* Interpolate/extrapolate the rest of the block's MVs */ + vp8_estimate_mv(block_overlaps[i].overlaps, bmi + i, type); + if (bmi[i].mv.as_int != 0) + { + ++non_zero_count; + filtered_mv->col += bmi[i].mv.as_mv.col; + filtered_mv->row += bmi[i].mv.as_mv.row; + } + } + if (non_zero_count > 0) + { + filtered_mv->col /= non_zero_count; + filtered_mv->row /= non_zero_count; + } +} + void vp8_estimate_missing_mvs(VP8D_COMP *pbi) { - VP8_COMMON *const pc = &pbi->common; - unsigned int first_corrupt = pbi->mvs_corrupt_from_mb; - const unsigned int num_mbs = pc->mb_rows * pc->mb_cols; - if (first_corrupt < num_mbs) + VP8_COMMON * const pc = &pbi->common; + vp8_estimate_missing_mvs_ex(pbi->overlaps, + pc->mi, pc->prev_mi, + pc->mb_rows, pc->mb_cols, + pbi->mvs_corrupt_from_mb); +} + +void vp8_estimate_missing_mvs_ex(MB_OVERLAP *overlaps, + MODE_INFO *mi, MODE_INFO *prev_mi, + int mb_rows, int mb_cols, + unsigned int first_corrupt) +{ + const unsigned int num_mbs = mb_rows * mb_cols; + int mb_row, mb_col; + vpx_memset(overlaps, 0, sizeof(MB_OVERLAP) * mb_rows * mb_cols); + for (mb_row = 0; mb_row < mb_rows; ++mb_row) { - MODE_INFO *mi = pc->mi; - MODE_INFO *correct_mi; - const int num_corrupt = num_mbs - first_corrupt; - int i; - int mb_row, mb_col; - if (first_corrupt == 0) + for (mb_col = 0; mb_col < mb_cols; ++mb_col) { - /* if the first MB is corrupt we just copy from it - the previous frame */ - mi[0].mbmi.mv.as_int = 0; - mi[0].mbmi.mode = ZEROMV; - mi[0].mbmi.uv_mode = ZEROMV; - mi[0].mbmi.ref_frame = LAST_FRAME; - first_corrupt = 1; - correct_mi = mi + 1; - } - for (mb_row = 0; mb_row < pc->mb_rows; ++mb_row) - { - for (mb_col = 0; mb_col < pc->mb_cols; ++mb_col) + int sub_row; + int sub_col; + for (sub_row = 0; sub_row < 4; ++sub_row) { - int mb_idx = mb_row * pc->mb_cols + mb_col; - if (mb_idx >= first_corrupt) + for (sub_col = 0; sub_col < 4; ++sub_col) { - *mi = *correct_mi; + vp8_calculate_overlaps_submb( + overlaps, mb_rows, mb_cols, + &(prev_mi->bmi[sub_row * 4 + sub_col]), + prev_mi->mbmi.ref_frame, + 4 * mb_row + sub_row, + 4 * mb_col + sub_col); } - else if (mb_idx == first_corrupt - 1) - { - correct_mi = mi; - } - ++mi; } + ++prev_mi; + } + ++prev_mi; + } + + mb_row = first_corrupt / mb_cols; + mb_col = first_corrupt - mb_row * mb_cols; + mi += mb_row*(mb_cols + 1) + mb_col; + for (; mb_row < mb_rows; ++mb_row) + { + for (; mb_col < mb_cols; ++mb_col) + { + int i; + MV_REFERENCE_FRAME type = LAST_FRAME; + int largest_overlap = 0; + const B_OVERLAP *block_overlaps = + overlaps[mb_row*mb_cols + mb_col].overlaps; + /* Find largest overlap and its type */ + mi->mbmi.ref_frame = vp8_largest_overlap_type(block_overlaps); + vp8_estimate_mb_mvs(block_overlaps, + mi->bmi, + mi->mbmi.ref_frame, + &mi->mbmi.mv.as_mv); + mi->mbmi.uv_mode = SPLITMV; + + mi->mbmi.mb_skip_coeff = 1; + /* TODO(holmer): should this be enabled, when? */ + mi->mbmi.need_to_clamp_mvs = 1; ++mi; } + mb_col = 0; + ++mi; } } void vp8_conceal_corrupt_block(MACROBLOCKD *xd) { /* this macroblock has corrupt residual, use the motion compensated - image for concealment */ + image for concealment */ int i; - for (i=0; i < 16; i++) - vpx_memcpy(xd->dst.y_buffer + i*xd->dst.y_stride, - xd->predictor + i*16, 16); - for (i=0; i < 8; i++) - vpx_memcpy(xd->dst.u_buffer + i*xd->dst.uv_stride, - xd->predictor + 256 + i*8, 8); - for (i=0; i < 8; i++) - vpx_memcpy(xd->dst.v_buffer + i*xd->dst.uv_stride, - xd->predictor + 320 + i*8, 8); + for (i = 0; i < 16; i++) + vpx_memcpy(xd->dst.y_buffer + i * xd->dst.y_stride, + xd->predictor + i * 16, 16); + for (i = 0; i < 8; i++) + vpx_memcpy(xd->dst.u_buffer + i * xd->dst.uv_stride, + xd->predictor + 256 + i * 8, 8); + for (i = 0; i < 8; i++) + vpx_memcpy(xd->dst.v_buffer + i * xd->dst.uv_stride, + xd->predictor + 320 + i * 8, 8); } diff --git a/vp8/decoder/error_concealment.h b/vp8/decoder/error_concealment.h index 0661e6cd2..bbf2deb93 100644 --- a/vp8/decoder/error_concealment.h +++ b/vp8/decoder/error_concealment.h @@ -14,7 +14,31 @@ #include "onyxd_int.h" -void vp8_estimate_missing_mvs(VP8D_COMP *); +int vp8_alloc_overlap_lists(VP8D_COMP *pbi); +void vp8_de_alloc_overlap_lists(VP8D_COMP *pbi); + +void vp8_assign_overlap(OVERLAP_NODE* overlaps, + B_MODE_INFO *bmi, + MV_REFERENCE_FRAME ref_frame, + int overlap); +int vp8_block_overlap(int b1_row, int b1_col, int b2_row, int b2_col); +void vp8_calculate_overlaps_mb(B_OVERLAP *b_overlaps, B_MODE_INFO *bmi, + MV_REFERENCE_FRAME ref_frame, + int new_row, int new_col, + int first_ol_mb_row, int first_ol_mb_col, + int first_ol_blk_row, int first_ol_blk_col); +MV_REFERENCE_FRAME vp8_largest_overlap_type(const B_OVERLAP *block_overlaps); +void vp8_estimate_mv(const OVERLAP_NODE *overlaps, B_MODE_INFO *bmi, + MV_REFERENCE_FRAME type); +void vp8_estimate_mb_mvs(const B_OVERLAP *block_overlaps, + B_MODE_INFO *bmi, + MV_REFERENCE_FRAME type, + MV* filtered_mv); +void vp8_estimate_missing_mvs(VP8D_COMP *pbi); +void vp8_estimate_missing_mvs_ex(MB_OVERLAP *overlaps, + MODE_INFO *mi, MODE_INFO *prev_mi, + int mb_rows, int mb_cols, + unsigned int first_corrupt); void vp8_conceal_corrupt_block(MACROBLOCKD *); #endif diff --git a/vp8/decoder/onyxd_if.c b/vp8/decoder/onyxd_if.c index 44620f851..378ddca69 100644 --- a/vp8/decoder/onyxd_if.c +++ b/vp8/decoder/onyxd_if.c @@ -150,6 +150,7 @@ void vp8dx_remove_decompressor(VP8D_PTR ptr) if (pbi->b_multithreaded_rd) vp8mt_de_alloc_temp_buffers(pbi, pbi->common.mb_rows); #endif + vp8_de_alloc_overlap_lists(pbi); vp8_decoder_remove_threads(pbi); vp8_remove_common(&pbi->common); vpx_free(pbi); @@ -318,7 +319,7 @@ int vp8dx_receive_compressed_data(VP8D_PTR ptr, unsigned long size, const unsign VP8_COMMON *cm = &pbi->common; int retcode = 0; struct vpx_usec_timer timer; - + pbi->ec_enabled = 1; /*if(pbi->ready_for_new_data == 0) diff --git a/vp8/decoder/onyxd_int.h b/vp8/decoder/onyxd_int.h index 5d87ef1e4..ac04f00a4 100644 --- a/vp8/decoder/onyxd_int.h +++ b/vp8/decoder/onyxd_int.h @@ -18,6 +18,25 @@ #include "threading.h" #include "dequantize.h" +#define MAX_OVERLAPS 16 + +typedef struct overlap_node +{ + int overlap; + B_MODE_INFO *bmi; + MV_REFERENCE_FRAME ref_frame; +} OVERLAP_NODE; + +typedef struct +{ + OVERLAP_NODE overlaps[MAX_OVERLAPS]; +} B_OVERLAP; + +typedef struct +{ + B_OVERLAP overlaps[16]; +} MB_OVERLAP; + typedef struct { int ithread; @@ -134,6 +153,7 @@ typedef struct VP8Decompressor vp8_prob prob_gf; vp8_prob prob_skip_false; + MB_OVERLAP *overlaps; unsigned int mvs_corrupt_from_mb; int ec_enabled;