diff options
Diffstat (limited to 'libavcodec/error_resilience.c')
-rw-r--r-- | libavcodec/error_resilience.c | 806 |
1 files changed, 484 insertions, 322 deletions
diff --git a/libavcodec/error_resilience.c b/libavcodec/error_resilience.c index 54b7b3c..25e54a5 100644 --- a/libavcodec/error_resilience.c +++ b/libavcodec/error_resilience.c @@ -3,20 +3,20 @@ * * Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -35,6 +35,7 @@ #include "mpegvideo.h" #include "rectangle.h" #include "thread.h" +#include "version.h" /** * @param stride the number of MVs to get to the next row @@ -43,7 +44,7 @@ static void set_mv_strides(ERContext *s, ptrdiff_t *mv_step, ptrdiff_t *stride) { if (s->avctx->codec_id == AV_CODEC_ID_H264) { - assert(s->quarter_sample); + av_assert0(s->quarter_sample); *mv_step = 4; *stride = s->mb_width * 4; } else { @@ -82,6 +83,8 @@ static void put_dc(ERContext *s, uint8_t *dest_y, uint8_t *dest_cb, dcv = 0; else if (dcv > 2040) dcv = 2040; + + if (dest_cr) for (y = 0; y < 8; y++) { int x; for (x = 0; x < 8; x++) { @@ -136,11 +139,73 @@ static void guess_dc(ERContext *s, int16_t *dc, int w, int h, ptrdiff_t stride, int is_luma) { int b_x, b_y; + int16_t (*col )[4] = av_malloc_array(stride, h*sizeof( int16_t)*4); + uint32_t (*dist)[4] = av_malloc_array(stride, h*sizeof(uint32_t)*4); + + if(!col || !dist) { + av_log(s->avctx, AV_LOG_ERROR, "guess_dc() is out of memory\n"); + goto fail; + } + + for(b_y=0; b_y<h; b_y++){ + int color= 1024; + int distance= -1; + for(b_x=0; b_x<w; b_x++){ + int mb_index_j= (b_x>>is_luma) + (b_y>>is_luma)*s->mb_stride; + int error_j= s->error_status_table[mb_index_j]; + int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); + if(intra_j==0 || !(error_j&ER_DC_ERROR)){ + color= dc[b_x + b_y*stride]; + distance= b_x; + } + col [b_x + b_y*stride][1]= color; + dist[b_x + b_y*stride][1]= distance >= 0 ? b_x-distance : 9999; + } + color= 1024; + distance= -1; + for(b_x=w-1; b_x>=0; b_x--){ + int mb_index_j= (b_x>>is_luma) + (b_y>>is_luma)*s->mb_stride; + int error_j= s->error_status_table[mb_index_j]; + int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); + if(intra_j==0 || !(error_j&ER_DC_ERROR)){ + color= dc[b_x + b_y*stride]; + distance= b_x; + } + col [b_x + b_y*stride][0]= color; + dist[b_x + b_y*stride][0]= distance >= 0 ? distance-b_x : 9999; + } + } + for(b_x=0; b_x<w; b_x++){ + int color= 1024; + int distance= -1; + for(b_y=0; b_y<h; b_y++){ + int mb_index_j= (b_x>>is_luma) + (b_y>>is_luma)*s->mb_stride; + int error_j= s->error_status_table[mb_index_j]; + int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); + if(intra_j==0 || !(error_j&ER_DC_ERROR)){ + color= dc[b_x + b_y*stride]; + distance= b_y; + } + col [b_x + b_y*stride][3]= color; + dist[b_x + b_y*stride][3]= distance >= 0 ? b_y-distance : 9999; + } + color= 1024; + distance= -1; + for(b_y=h-1; b_y>=0; b_y--){ + int mb_index_j= (b_x>>is_luma) + (b_y>>is_luma)*s->mb_stride; + int error_j= s->error_status_table[mb_index_j]; + int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); + if(intra_j==0 || !(error_j&ER_DC_ERROR)){ + color= dc[b_x + b_y*stride]; + distance= b_y; + } + col [b_x + b_y*stride][2]= color; + dist[b_x + b_y*stride][2]= distance >= 0 ? distance-b_y : 9999; + } + } for (b_y = 0; b_y < h; b_y++) { for (b_x = 0; b_x < w; b_x++) { - int color[4] = { 1024, 1024, 1024, 1024 }; - int distance[4] = { 9999, 9999, 9999, 9999 }; int mb_index, error, j; int64_t guess, weight_sum; mb_index = (b_x >> is_luma) + (b_y >> is_luma) * s->mb_stride; @@ -151,66 +216,21 @@ static void guess_dc(ERContext *s, int16_t *dc, int w, if (!(error & ER_DC_ERROR)) continue; // dc-ok - /* right block */ - for (j = b_x + 1; j < w; j++) { - int mb_index_j = (j >> is_luma) + (b_y >> is_luma) * s->mb_stride; - int error_j = s->error_status_table[mb_index_j]; - int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); - if (intra_j == 0 || !(error_j & ER_DC_ERROR)) { - color[0] = dc[j + b_y * stride]; - distance[0] = j - b_x; - break; - } - } - - /* left block */ - for (j = b_x - 1; j >= 0; j--) { - int mb_index_j = (j >> is_luma) + (b_y >> is_luma) * s->mb_stride; - int error_j = s->error_status_table[mb_index_j]; - int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); - if (intra_j == 0 || !(error_j & ER_DC_ERROR)) { - color[1] = dc[j + b_y * stride]; - distance[1] = b_x - j; - break; - } - } - - /* bottom block */ - for (j = b_y + 1; j < h; j++) { - int mb_index_j = (b_x >> is_luma) + (j >> is_luma) * s->mb_stride; - int error_j = s->error_status_table[mb_index_j]; - int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); - - if (intra_j == 0 || !(error_j & ER_DC_ERROR)) { - color[2] = dc[b_x + j * stride]; - distance[2] = j - b_y; - break; - } - } - - /* top block */ - for (j = b_y - 1; j >= 0; j--) { - int mb_index_j = (b_x >> is_luma) + (j >> is_luma) * s->mb_stride; - int error_j = s->error_status_table[mb_index_j]; - int intra_j = IS_INTRA(s->cur_pic.mb_type[mb_index_j]); - if (intra_j == 0 || !(error_j & ER_DC_ERROR)) { - color[3] = dc[b_x + j * stride]; - distance[3] = b_y - j; - break; - } - } - weight_sum = 0; guess = 0; for (j = 0; j < 4; j++) { - int64_t weight = 256 * 256 * 256 * 16 / distance[j]; - guess += weight * (int64_t) color[j]; + int64_t weight = 256 * 256 * 256 * 16 / FFMAX(dist[b_x + b_y*stride][j], 1); + guess += weight*(int64_t)col[b_x + b_y*stride][j]; weight_sum += weight; } guess = (guess + weight_sum / 2) / weight_sum; dc[b_x + b_y * stride] = guess; } } + +fail: + av_freep(&col); + av_freep(&dist); } /** @@ -354,23 +374,46 @@ static void v_block_filter(ERContext *s, uint8_t *dst, int w, int h, } } +#define MV_FROZEN 8 +#define MV_CHANGED 4 +#define MV_UNCHANGED 2 +#define MV_LISTED 1 +static av_always_inline void add_blocklist(int (*blocklist)[2], int *blocklist_length, uint8_t *fixed, int mb_x, int mb_y, int mb_xy) +{ + if (fixed[mb_xy]) + return; + fixed[mb_xy] = MV_LISTED; + blocklist[ *blocklist_length ][0] = mb_x; + blocklist[(*blocklist_length)++][1] = mb_y; +} + static void guess_mv(ERContext *s) { - uint8_t *fixed = s->er_temp_buffer; -#define MV_FROZEN 3 -#define MV_CHANGED 2 -#define MV_UNCHANGED 1 + int (*blocklist)[2], (*next_blocklist)[2]; + uint8_t *fixed; const ptrdiff_t mb_stride = s->mb_stride; const int mb_width = s->mb_width; - const int mb_height = s->mb_height; + int mb_height = s->mb_height; int i, depth, num_avail; int mb_x, mb_y; ptrdiff_t mot_step, mot_stride; + int blocklist_length, next_blocklist_length; + + if (s->last_pic.f && s->last_pic.f->data[0]) + mb_height = FFMIN(mb_height, (s->last_pic.f->height+15)>>4); + if (s->next_pic.f && s->next_pic.f->data[0]) + mb_height = FFMIN(mb_height, (s->next_pic.f->height+15)>>4); + + blocklist = (int (*)[2])s->er_temp_buffer; + next_blocklist = blocklist + s->mb_stride * s->mb_height; + fixed = (uint8_t *)(next_blocklist + s->mb_stride * s->mb_height); set_mv_strides(s, &mot_step, &mot_stride); num_avail = 0; - for (i = 0; i < s->mb_num; i++) { + if (s->last_pic.motion_val[0]) + ff_thread_await_progress(s->last_pic.tf, mb_height-1, 0); + for (i = 0; i < mb_width * mb_height; i++) { const int mb_xy = s->mb_index2xy[i]; int f = 0; int error = s->error_status_table[mb_xy]; @@ -383,11 +426,19 @@ static void guess_mv(ERContext *s) fixed[mb_xy] = f; if (f == MV_FROZEN) num_avail++; + else if(s->last_pic.f->data[0] && s->last_pic.motion_val[0]){ + const int mb_y= mb_xy / s->mb_stride; + const int mb_x= mb_xy % s->mb_stride; + const int mot_index= (mb_x + mb_y*mot_stride) * mot_step; + s->cur_pic.motion_val[0][mot_index][0]= s->last_pic.motion_val[0][mot_index][0]; + s->cur_pic.motion_val[0][mot_index][1]= s->last_pic.motion_val[0][mot_index][1]; + s->cur_pic.ref_index[0][4*mb_xy] = s->last_pic.ref_index[0][4*mb_xy]; + } } if ((!(s->avctx->error_concealment&FF_EC_GUESS_MVS)) || num_avail <= mb_width / 2) { - for (mb_y = 0; mb_y < s->mb_height; mb_y++) { + for (mb_y = 0; mb_y < mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { const int mb_xy = mb_x + mb_y * s->mb_stride; int mv_dir = (s->last_pic.f && s->last_pic.f->data[0]) ? MV_DIR_FORWARD : MV_DIR_BACKWARD; @@ -406,252 +457,265 @@ static void guess_mv(ERContext *s) return; } + blocklist_length = 0; + for (mb_y = 0; mb_y < mb_height; mb_y++) { + for (mb_x = 0; mb_x < mb_width; mb_x++) { + const int mb_xy = mb_x + mb_y * mb_stride; + if (fixed[mb_xy] == MV_FROZEN) { + if (mb_x) add_blocklist(blocklist, &blocklist_length, fixed, mb_x - 1, mb_y, mb_xy - 1); + if (mb_y) add_blocklist(blocklist, &blocklist_length, fixed, mb_x, mb_y - 1, mb_xy - mb_stride); + if (mb_x+1 < mb_width) add_blocklist(blocklist, &blocklist_length, fixed, mb_x + 1, mb_y, mb_xy + 1); + if (mb_y+1 < mb_height) add_blocklist(blocklist, &blocklist_length, fixed, mb_x, mb_y + 1, mb_xy + mb_stride); + } + } + } + for (depth = 0; ; depth++) { int changed, pass, none_left; + int blocklist_index; none_left = 1; changed = 1; for (pass = 0; (changed || pass < 2) && pass < 10; pass++) { - int mb_x, mb_y; int score_sum = 0; changed = 0; - for (mb_y = 0; mb_y < s->mb_height; mb_y++) { - for (mb_x = 0; mb_x < s->mb_width; mb_x++) { - const int mb_xy = mb_x + mb_y * s->mb_stride; - int mv_predictor[8][2] = { { 0 } }; - int ref[8] = { 0 }; - int pred_count = 0; - int j; - int best_score = 256 * 256 * 256 * 64; - int best_pred = 0; - const int mot_index = (mb_x + mb_y * mot_stride) * mot_step; - int prev_x = 0, prev_y = 0, prev_ref = 0; - - if ((mb_x ^ mb_y ^ pass) & 1) - continue; + for (blocklist_index = 0; blocklist_index < blocklist_length; blocklist_index++) { + const int mb_x = blocklist[blocklist_index][0]; + const int mb_y = blocklist[blocklist_index][1]; + const int mb_xy = mb_x + mb_y * mb_stride; + int mv_predictor[8][2]; + int ref[8]; + int pred_count; + int j; + int best_score; + int best_pred; + int mot_index; + int prev_x, prev_y, prev_ref; - if (fixed[mb_xy] == MV_FROZEN) - continue; + if ((mb_x ^ mb_y ^ pass) & 1) + continue; + av_assert2(fixed[mb_xy] != MV_FROZEN); - j = 0; - if (mb_x > 0 && fixed[mb_xy - 1] == MV_FROZEN) - j = 1; - if (mb_x + 1 < mb_width && fixed[mb_xy + 1] == MV_FROZEN) - j = 1; - if (mb_y > 0 && fixed[mb_xy - mb_stride] == MV_FROZEN) - j = 1; - if (mb_y + 1 < mb_height && fixed[mb_xy + mb_stride] == MV_FROZEN) - j = 1; - if (j == 0) - continue; - j = 0; - if (mb_x > 0 && fixed[mb_xy - 1 ] == MV_CHANGED) - j = 1; - if (mb_x + 1 < mb_width && fixed[mb_xy + 1 ] == MV_CHANGED) - j = 1; - if (mb_y > 0 && fixed[mb_xy - mb_stride] == MV_CHANGED) - j = 1; - if (mb_y + 1 < mb_height && fixed[mb_xy + mb_stride] == MV_CHANGED) - j = 1; - if (j == 0 && pass > 1) - continue; + av_assert1(!IS_INTRA(s->cur_pic.mb_type[mb_xy])); + av_assert1(s->last_pic.f && s->last_pic.f->data[0]); - none_left = 0; + j = 0; + if (mb_x > 0) + j |= fixed[mb_xy - 1]; + if (mb_x + 1 < mb_width) + j |= fixed[mb_xy + 1]; + if (mb_y > 0) + j |= fixed[mb_xy - mb_stride]; + if (mb_y + 1 < mb_height) + j |= fixed[mb_xy + mb_stride]; - if (mb_x > 0 && fixed[mb_xy - 1]) { - mv_predictor[pred_count][0] = - s->cur_pic.motion_val[0][mot_index - mot_step][0]; - mv_predictor[pred_count][1] = - s->cur_pic.motion_val[0][mot_index - mot_step][1]; - ref[pred_count] = - s->cur_pic.ref_index[0][4 * (mb_xy - 1)]; - pred_count++; - } - if (mb_x + 1 < mb_width && fixed[mb_xy + 1]) { - mv_predictor[pred_count][0] = - s->cur_pic.motion_val[0][mot_index + mot_step][0]; - mv_predictor[pred_count][1] = - s->cur_pic.motion_val[0][mot_index + mot_step][1]; - ref[pred_count] = - s->cur_pic.ref_index[0][4 * (mb_xy + 1)]; - pred_count++; - } - if (mb_y > 0 && fixed[mb_xy - mb_stride]) { - mv_predictor[pred_count][0] = - s->cur_pic.motion_val[0][mot_index - mot_stride * mot_step][0]; - mv_predictor[pred_count][1] = - s->cur_pic.motion_val[0][mot_index - mot_stride * mot_step][1]; - ref[pred_count] = - s->cur_pic.ref_index[0][4 * (mb_xy - s->mb_stride)]; - pred_count++; - } - if (mb_y + 1<mb_height && fixed[mb_xy + mb_stride]) { - mv_predictor[pred_count][0] = - s->cur_pic.motion_val[0][mot_index + mot_stride * mot_step][0]; - mv_predictor[pred_count][1] = - s->cur_pic.motion_val[0][mot_index + mot_stride * mot_step][1]; - ref[pred_count] = - s->cur_pic.ref_index[0][4 * (mb_xy + s->mb_stride)]; - pred_count++; - } - if (pred_count == 0) - continue; + av_assert2(j & MV_FROZEN); - if (pred_count > 1) { - int sum_x = 0, sum_y = 0, sum_r = 0; - int max_x, max_y, min_x, min_y, max_r, min_r; - - for (j = 0; j < pred_count; j++) { - sum_x += mv_predictor[j][0]; - sum_y += mv_predictor[j][1]; - sum_r += ref[j]; - if (j && ref[j] != ref[j - 1]) - goto skip_mean_and_median; - } - - /* mean */ - mv_predictor[pred_count][0] = sum_x / j; - mv_predictor[pred_count][1] = sum_y / j; - ref[pred_count] = sum_r / j; - - /* median */ - if (pred_count >= 3) { - min_y = min_x = min_r = 99999; - max_y = max_x = max_r = -99999; - } else { - min_x = min_y = max_x = max_y = min_r = max_r = 0; - } - for (j = 0; j < pred_count; j++) { - max_x = FFMAX(max_x, mv_predictor[j][0]); - max_y = FFMAX(max_y, mv_predictor[j][1]); - max_r = FFMAX(max_r, ref[j]); - min_x = FFMIN(min_x, mv_predictor[j][0]); - min_y = FFMIN(min_y, mv_predictor[j][1]); - min_r = FFMIN(min_r, ref[j]); - } - mv_predictor[pred_count + 1][0] = sum_x - max_x - min_x; - mv_predictor[pred_count + 1][1] = sum_y - max_y - min_y; - ref[pred_count + 1] = sum_r - max_r - min_r; - - if (pred_count == 4) { - mv_predictor[pred_count + 1][0] /= 2; - mv_predictor[pred_count + 1][1] /= 2; - ref[pred_count + 1] /= 2; - } - pred_count += 2; - } + if (!(j & MV_CHANGED) && pass > 1) + continue; -skip_mean_and_median: - /* zero MV */ + none_left = 0; + pred_count = 0; + mot_index = (mb_x + mb_y * mot_stride) * mot_step; + + if (mb_x > 0 && fixed[mb_xy - 1] > 1) { + mv_predictor[pred_count][0] = + s->cur_pic.motion_val[0][mot_index - mot_step][0]; + mv_predictor[pred_count][1] = + s->cur_pic.motion_val[0][mot_index - mot_step][1]; + ref[pred_count] = + s->cur_pic.ref_index[0][4 * (mb_xy - 1)]; pred_count++; - - if (!fixed[mb_xy]) { - if (s->avctx->codec_id == AV_CODEC_ID_H264) { - // FIXME - } else { - ff_thread_await_progress(s->last_pic.tf, - mb_y, 0); - } - if (!s->last_pic.motion_val[0] || - !s->last_pic.ref_index[0]) - goto skip_last_mv; - prev_x = s->last_pic.motion_val[0][mot_index][0]; - prev_y = s->last_pic.motion_val[0][mot_index][1]; - prev_ref = s->last_pic.ref_index[0][4 * mb_xy]; - } else { - prev_x = s->cur_pic.motion_val[0][mot_index][0]; - prev_y = s->cur_pic.motion_val[0][mot_index][1]; - prev_ref = s->cur_pic.ref_index[0][4 * mb_xy]; - } - - /* last MV */ - mv_predictor[pred_count][0] = prev_x; - mv_predictor[pred_count][1] = prev_y; - ref[pred_count] = prev_ref; + } + if (mb_x + 1 < mb_width && fixed[mb_xy + 1] > 1) { + mv_predictor[pred_count][0] = + s->cur_pic.motion_val[0][mot_index + mot_step][0]; + mv_predictor[pred_count][1] = + s->cur_pic.motion_val[0][mot_index + mot_step][1]; + ref[pred_count] = + s->cur_pic.ref_index[0][4 * (mb_xy + 1)]; + pred_count++; + } + if (mb_y > 0 && fixed[mb_xy - mb_stride] > 1) { + mv_predictor[pred_count][0] = + s->cur_pic.motion_val[0][mot_index - mot_stride * mot_step][0]; + mv_predictor[pred_count][1] = + s->cur_pic.motion_val[0][mot_index - mot_stride * mot_step][1]; + ref[pred_count] = + s->cur_pic.ref_index[0][4 * (mb_xy - s->mb_stride)]; + pred_count++; + } + if (mb_y + 1<mb_height && fixed[mb_xy + mb_stride] > 1) { + mv_predictor[pred_count][0] = + s->cur_pic.motion_val[0][mot_index + mot_stride * mot_step][0]; + mv_predictor[pred_count][1] = + s->cur_pic.motion_val[0][mot_index + mot_stride * mot_step][1]; + ref[pred_count] = + s->cur_pic.ref_index[0][4 * (mb_xy + s->mb_stride)]; pred_count++; + } + if (pred_count == 0) + continue; -skip_last_mv: + if (pred_count > 1) { + int sum_x = 0, sum_y = 0, sum_r = 0; + int max_x, max_y, min_x, min_y, max_r, min_r; for (j = 0; j < pred_count; j++) { - int *linesize = s->cur_pic.f->linesize; - int score = 0; - uint8_t *src = s->cur_pic.f->data[0] + - mb_x * 16 + mb_y * 16 * linesize[0]; + sum_x += mv_predictor[j][0]; + sum_y += mv_predictor[j][1]; + sum_r += ref[j]; + if (j && ref[j] != ref[j - 1]) + goto skip_mean_and_median; + } - s->cur_pic.motion_val[0][mot_index][0] = - s->mv[0][0][0] = mv_predictor[j][0]; - s->cur_pic.motion_val[0][mot_index][1] = - s->mv[0][0][1] = mv_predictor[j][1]; - - // predictor intra or otherwise not available - if (ref[j] < 0) - continue; - - s->decode_mb(s->opaque, ref[j], MV_DIR_FORWARD, - MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0); - - if (mb_x > 0 && fixed[mb_xy - 1]) { - int k; - for (k = 0; k < 16; k++) - score += FFABS(src[k * linesize[0] - 1] - - src[k * linesize[0]]); - } - if (mb_x + 1 < mb_width && fixed[mb_xy + 1]) { - int k; - for (k = 0; k < 16; k++) - score += FFABS(src[k * linesize[0] + 15] - - src[k * linesize[0] + 16]); - } - if (mb_y > 0 && fixed[mb_xy - mb_stride]) { - int k; - for (k = 0; k < 16; k++) - score += FFABS(src[k - linesize[0]] - src[k]); - } - if (mb_y + 1 < mb_height && fixed[mb_xy + mb_stride]) { - int k; - for (k = 0; k < 16; k++) - score += FFABS(src[k + linesize[0] * 15] - - src[k + linesize[0] * 16]); - } - - if (score <= best_score) { // <= will favor the last MV - best_score = score; - best_pred = j; - } + /* mean */ + mv_predictor[pred_count][0] = sum_x / j; + mv_predictor[pred_count][1] = sum_y / j; + ref[pred_count] = sum_r / j; + + /* median */ + if (pred_count >= 3) { + min_y = min_x = min_r = 99999; + max_y = max_x = max_r = -99999; + } else { + min_x = min_y = max_x = max_y = min_r = max_r = 0; } - score_sum += best_score; - s->mv[0][0][0] = mv_predictor[best_pred][0]; - s->mv[0][0][1] = mv_predictor[best_pred][1]; + for (j = 0; j < pred_count; j++) { + max_x = FFMAX(max_x, mv_predictor[j][0]); + max_y = FFMAX(max_y, mv_predictor[j][1]); + max_r = FFMAX(max_r, ref[j]); + min_x = FFMIN(min_x, mv_predictor[j][0]); + min_y = FFMIN(min_y, mv_predictor[j][1]); + min_r = FFMIN(min_r, ref[j]); + } + mv_predictor[pred_count + 1][0] = sum_x - max_x - min_x; + mv_predictor[pred_count + 1][1] = sum_y - max_y - min_y; + ref[pred_count + 1] = sum_r - max_r - min_r; + + if (pred_count == 4) { + mv_predictor[pred_count + 1][0] /= 2; + mv_predictor[pred_count + 1][1] /= 2; + ref[pred_count + 1] /= 2; + } + pred_count += 2; + } - for (i = 0; i < mot_step; i++) - for (j = 0; j < mot_step; j++) { - s->cur_pic.motion_val[0][mot_index + i + j * mot_stride][0] = s->mv[0][0][0]; - s->cur_pic.motion_val[0][mot_index + i + j * mot_stride][1] = s->mv[0][0][1]; - } +skip_mean_and_median: + /* zero MV */ + mv_predictor[pred_count][0] = + mv_predictor[pred_count][1] = + ref[pred_count] = 0; + pred_count++; + + prev_x = s->cur_pic.motion_val[0][mot_index][0]; + prev_y = s->cur_pic.motion_val[0][mot_index][1]; + prev_ref = s->cur_pic.ref_index[0][4 * mb_xy]; + + /* last MV */ + mv_predictor[pred_count][0] = prev_x; + mv_predictor[pred_count][1] = prev_y; + ref[pred_count] = prev_ref; + pred_count++; + + best_pred = 0; + best_score = 256 * 256 * 256 * 64; + for (j = 0; j < pred_count; j++) { + int *linesize = s->cur_pic.f->linesize; + int score = 0; + uint8_t *src = s->cur_pic.f->data[0] + + mb_x * 16 + mb_y * 16 * linesize[0]; + + s->cur_pic.motion_val[0][mot_index][0] = + s->mv[0][0][0] = mv_predictor[j][0]; + s->cur_pic.motion_val[0][mot_index][1] = + s->mv[0][0][1] = mv_predictor[j][1]; + + // predictor intra or otherwise not available + if (ref[j] < 0) + continue; - s->decode_mb(s->opaque, ref[best_pred], MV_DIR_FORWARD, + s->decode_mb(s->opaque, ref[j], MV_DIR_FORWARD, MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0); + if (mb_x > 0 && fixed[mb_xy - 1] > 1) { + int k; + for (k = 0; k < 16; k++) + score += FFABS(src[k * linesize[0] - 1] - + src[k * linesize[0]]); + } + if (mb_x + 1 < mb_width && fixed[mb_xy + 1] > 1) { + int k; + for (k = 0; k < 16; k++) + score += FFABS(src[k * linesize[0] + 15] - + src[k * linesize[0] + 16]); + } + if (mb_y > 0 && fixed[mb_xy - mb_stride] > 1) { + int k; + for (k = 0; k < 16; k++) + score += FFABS(src[k - linesize[0]] - src[k]); + } + if (mb_y + 1 < mb_height && fixed[mb_xy + mb_stride] > 1) { + int k; + for (k = 0; k < 16; k++) + score += FFABS(src[k + linesize[0] * 15] - + src[k + linesize[0] * 16]); + } - if (s->mv[0][0][0] != prev_x || s->mv[0][0][1] != prev_y) { - fixed[mb_xy] = MV_CHANGED; - changed++; - } else - fixed[mb_xy] = MV_UNCHANGED; + if (score <= best_score) { // <= will favor the last MV + best_score = score; + best_pred = j; + } } + score_sum += best_score; + s->mv[0][0][0] = mv_predictor[best_pred][0]; + s->mv[0][0][1] = mv_predictor[best_pred][1]; + + for (i = 0; i < mot_step; i++) + for (j = 0; j < mot_step; j++) { + s->cur_pic.motion_val[0][mot_index + i + j * mot_stride][0] = s->mv[0][0][0]; + s->cur_pic.motion_val[0][mot_index + i + j * mot_stride][1] = s->mv[0][0][1]; + } + + s->decode_mb(s->opaque, ref[best_pred], MV_DIR_FORWARD, + MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0); + + + if (s->mv[0][0][0] != prev_x || s->mv[0][0][1] != prev_y) { + fixed[mb_xy] = MV_CHANGED; + changed++; + } else + fixed[mb_xy] = MV_UNCHANGED; } } if (none_left) return; - for (i = 0; i < s->mb_num; i++) { - int mb_xy = s->mb_index2xy[i]; - if (fixed[mb_xy]) + next_blocklist_length = 0; + + for (blocklist_index = 0; blocklist_index < blocklist_length; blocklist_index++) { + const int mb_x = blocklist[blocklist_index][0]; + const int mb_y = blocklist[blocklist_index][1]; + const int mb_xy = mb_x + mb_y * mb_stride; + + if (fixed[mb_xy] & (MV_CHANGED|MV_UNCHANGED|MV_FROZEN)) { fixed[mb_xy] = MV_FROZEN; + if (mb_x > 0) + add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x - 1, mb_y, mb_xy - 1); + if (mb_y > 0) + add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x, mb_y - 1, mb_xy - mb_stride); + if (mb_x + 1 < mb_width) + add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x + 1, mb_y, mb_xy + 1); + if (mb_y + 1 < mb_height) + add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x, mb_y + 1, mb_xy + mb_stride); + } } + av_assert0(next_blocklist_length <= mb_height * mb_width); + FFSWAP(int , blocklist_length, next_blocklist_length); + FFSWAP(void*, blocklist, next_blocklist); } } @@ -662,6 +726,9 @@ static int is_intra_more_likely(ERContext *s) if (!s->last_pic.f || !s->last_pic.f->data[0]) return 1; // no previous frame available -> use spatial prediction + if (s->avctx->error_concealment & FF_EC_FAVOR_INTER) + return 0; + undamaged_count = 0; for (i = 0; i < s->mb_num; i++) { const int mb_xy = s->mb_index2xy[i]; @@ -670,12 +737,15 @@ static int is_intra_more_likely(ERContext *s) undamaged_count++; } - if (s->avctx->codec_id == AV_CODEC_ID_H264 && s->ref_count <= 0) - return 1; - if (undamaged_count < 5) return 0; // almost all MBs damaged -> use temporal prediction + // prevent dsp.sad() check, that requires access to the image + if (CONFIG_XVMC && + s->avctx->hwaccel && s->avctx->hwaccel->decode_mb && + s->cur_pic.f->pict_type == AV_PICTURE_TYPE_I) + return 1; + skip_amount = FFMAX(undamaged_count / 50, 1); // check only up to 50 MBs is_intra_likely = 0; @@ -708,6 +778,7 @@ static int is_intra_more_likely(ERContext *s) } is_intra_likely += s->mecc.sad[0](NULL, last_mb_ptr, mb_ptr, linesize[0], 16); + // FIXME need await_progress() here is_intra_likely -= s->mecc.sad[0](NULL, last_mb_ptr, last_mb_ptr + linesize[0] * 16, linesize[0], 16); @@ -719,6 +790,7 @@ static int is_intra_more_likely(ERContext *s) } } } +// av_log(NULL, AV_LOG_ERROR, "is_intra_likely: %d type:%d\n", is_intra_likely, s->pict_type); return is_intra_likely > 0; } @@ -734,10 +806,21 @@ void ff_er_frame_start(ERContext *s) memset(s->error_status_table, ER_MB_ERROR | VP_START | ER_MB_END, s->mb_stride * s->mb_height * sizeof(uint8_t)); - s->error_count = 3 * s->mb_num; + atomic_init(&s->error_count, 3 * s->mb_num); s->error_occurred = 0; } +static int er_supported(ERContext *s) +{ + if(s->avctx->hwaccel && s->avctx->hwaccel->decode_slice || + !s->cur_pic.f || + s->cur_pic.field_picture || + s->avctx->profile == FF_PROFILE_MPEG4_SIMPLE_STUDIO + ) + return 0; + return 1; +} + /** * Add a slice. * @param endx x component of the last macroblock, can be -1 @@ -754,7 +837,7 @@ void ff_er_add_slice(ERContext *s, int startx, int starty, const int end_xy = s->mb_index2xy[end_i]; int mask = -1; - if (s->avctx->hwaccel) + if (s->avctx->hwaccel && s->avctx->hwaccel->decode_slice) return; if (start_i > end_i || start_xy > end_xy) { @@ -769,20 +852,20 @@ void ff_er_add_slice(ERContext *s, int startx, int starty, mask &= ~VP_START; if (status & (ER_AC_ERROR | ER_AC_END)) { mask &= ~(ER_AC_ERROR | ER_AC_END); - s->error_count -= end_i - start_i + 1; + atomic_fetch_add(&s->error_count, start_i - end_i - 1); } if (status & (ER_DC_ERROR | ER_DC_END)) { mask &= ~(ER_DC_ERROR | ER_DC_END); - s->error_count -= end_i - start_i + 1; + atomic_fetch_add(&s->error_count, start_i - end_i - 1); } if (status & (ER_MV_ERROR | ER_MV_END)) { mask &= ~(ER_MV_ERROR | ER_MV_END); - s->error_count -= end_i - start_i + 1; + atomic_fetch_add(&s->error_count, start_i - end_i - 1); } if (status & ER_MB_ERROR) { s->error_occurred = 1; - s->error_count = INT_MAX; + atomic_store(&s->error_count, INT_MAX); } if (mask == ~0x7F) { @@ -795,7 +878,7 @@ void ff_er_add_slice(ERContext *s, int startx, int starty, } if (end_i == s->mb_num) - s->error_count = INT_MAX; + atomic_store(&s->error_count, INT_MAX); else { s->error_status_table[end_xy] &= mask; s->error_status_table[end_xy] |= status; @@ -803,41 +886,92 @@ void ff_er_add_slice(ERContext *s, int startx, int starty, s->error_status_table[start_xy] |= VP_START; - if (start_xy > 0 && s->avctx->thread_count <= 1 && - s->avctx->skip_top * s->mb_width < start_i) { + if (start_xy > 0 && !(s->avctx->active_thread_type & FF_THREAD_SLICE) && + er_supported(s) && s->avctx->skip_top * s->mb_width < start_i) { int prev_status = s->error_status_table[s->mb_index2xy[start_i - 1]]; prev_status &= ~ VP_START; - if (prev_status != (ER_MV_END | ER_DC_END | ER_AC_END)) - s->error_count = INT_MAX; + if (prev_status != (ER_MV_END | ER_DC_END | ER_AC_END)) { + s->error_occurred = 1; + atomic_store(&s->error_count, INT_MAX); + } } } void ff_er_frame_end(ERContext *s) { - int *linesize = s->cur_pic.f->linesize; + int *linesize = NULL; int i, mb_x, mb_y, error, error_type, dc_error, mv_error, ac_error; int distance; int threshold_part[4] = { 100, 100, 100 }; int threshold = 50; int is_intra_likely; + int size = s->b8_stride * 2 * s->mb_height; /* We do not support ER of field pictures yet, * though it should not crash if enabled. */ - if (!s->avctx->error_concealment || s->error_count == 0 || - s->avctx->hwaccel || - !s->cur_pic.f || - s->cur_pic.field_picture || - s->error_count == 3 * s->mb_width * + if (!s->avctx->error_concealment || !atomic_load(&s->error_count) || + s->avctx->lowres || + !er_supported(s) || + atomic_load(&s->error_count) == 3 * s->mb_width * (s->avctx->skip_top + s->avctx->skip_bottom)) { return; - }; + } + linesize = s->cur_pic.f->linesize; + for (mb_x = 0; mb_x < s->mb_width; mb_x++) { + int status = s->error_status_table[mb_x + (s->mb_height - 1) * s->mb_stride]; + if (status != 0x7F) + break; + } - if (!s->cur_pic.motion_val[0] || !s->cur_pic.ref_index[0]) { - av_log(s->avctx, AV_LOG_ERROR, "MVs not available, ER not possible.\n"); + if ( mb_x == s->mb_width + && s->avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO + && (FFALIGN(s->avctx->height, 16)&16) + && atomic_load(&s->error_count) == 3 * s->mb_width * (s->avctx->skip_top + s->avctx->skip_bottom + 1) + ) { + av_log(s->avctx, AV_LOG_DEBUG, "ignoring last missing slice\n"); return; } + if (s->last_pic.f) { + if (s->last_pic.f->width != s->cur_pic.f->width || + s->last_pic.f->height != s->cur_pic.f->height || + s->last_pic.f->format != s->cur_pic.f->format) { + av_log(s->avctx, AV_LOG_WARNING, "Cannot use previous picture in error concealment\n"); + memset(&s->last_pic, 0, sizeof(s->last_pic)); + } + } + if (s->next_pic.f) { + if (s->next_pic.f->width != s->cur_pic.f->width || + s->next_pic.f->height != s->cur_pic.f->height || + s->next_pic.f->format != s->cur_pic.f->format) { + av_log(s->avctx, AV_LOG_WARNING, "Cannot use next picture in error concealment\n"); + memset(&s->next_pic, 0, sizeof(s->next_pic)); + } + } + + if (!s->cur_pic.motion_val[0] || !s->cur_pic.ref_index[0]) { + av_log(s->avctx, AV_LOG_ERROR, "Warning MVs not available\n"); + + for (i = 0; i < 2; i++) { + s->ref_index_buf[i] = av_buffer_allocz(s->mb_stride * s->mb_height * 4 * sizeof(uint8_t)); + s->motion_val_buf[i] = av_buffer_allocz((size + 4) * 2 * sizeof(uint16_t)); + if (!s->ref_index_buf[i] || !s->motion_val_buf[i]) + break; + s->cur_pic.ref_index[i] = s->ref_index_buf[i]->data; + s->cur_pic.motion_val[i] = (int16_t (*)[2])s->motion_val_buf[i]->data + 4; + } + if (i < 2) { + for (i = 0; i < 2; i++) { + av_buffer_unref(&s->ref_index_buf[i]); + av_buffer_unref(&s->motion_val_buf[i]); + s->cur_pic.ref_index[i] = NULL; + s->cur_pic.motion_val[i] = NULL; + } + return; + } + } + if (s->avctx->debug & FF_DEBUG_ER) { for (mb_y = 0; mb_y < s->mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { @@ -849,6 +983,7 @@ void ff_er_frame_end(ERContext *s) } } +#if 1 /* handle overlapping slices */ for (error_type = 1; error_type <= 3; error_type++) { int end_ok = 0; @@ -869,7 +1004,8 @@ void ff_er_frame_end(ERContext *s) end_ok = 0; } } - +#endif +#if 1 /* handle slices with partitions of different length */ if (s->partitioned_frame) { int end_ok = 0; @@ -892,7 +1028,7 @@ void ff_er_frame_end(ERContext *s) end_ok = 0; } } - +#endif /* handle missing slices */ if (s->avctx->err_recognition & AV_EF_EXPLODE) { int end_ok = 1; @@ -919,6 +1055,7 @@ void ff_er_frame_end(ERContext *s) } } +#if 1 /* backward mark errors */ distance = 9999999; for (error_type = 1; error_type <= 3; error_type++) { @@ -926,7 +1063,7 @@ void ff_er_frame_end(ERContext *s) const int mb_xy = s->mb_index2xy[i]; int error = s->error_status_table[mb_xy]; - if (s->mbskip_table && !s->mbskip_table[mb_xy]) // FIXME partition specific + if (!s->mbskip_table || !s->mbskip_table[mb_xy]) // FIXME partition specific distance++; if (error & (1 << error_type)) distance = 0; @@ -943,6 +1080,7 @@ void ff_er_frame_end(ERContext *s) distance = 9999999; } } +#endif /* forward mark errors */ error = 0; @@ -957,22 +1095,23 @@ void ff_er_frame_end(ERContext *s) s->error_status_table[mb_xy] |= error; } } - +#if 1 /* handle not partitioned case */ if (!s->partitioned_frame) { for (i = 0; i < s->mb_num; i++) { const int mb_xy = s->mb_index2xy[i]; - error = s->error_status_table[mb_xy]; + int error = s->error_status_table[mb_xy]; if (error & ER_MB_ERROR) error |= ER_MB_ERROR; s->error_status_table[mb_xy] = error; } } +#endif dc_error = ac_error = mv_error = 0; for (i = 0; i < s->mb_num; i++) { const int mb_xy = s->mb_index2xy[i]; - error = s->error_status_table[mb_xy]; + int error = s->error_status_table[mb_xy]; if (error & ER_DC_ERROR) dc_error++; if (error & ER_AC_ERROR) @@ -980,15 +1119,15 @@ void ff_er_frame_end(ERContext *s) if (error & ER_MV_ERROR) mv_error++; } - av_log(s->avctx, AV_LOG_INFO, "concealing %d DC, %d AC, %d MV errors\n", - dc_error, ac_error, mv_error); + av_log(s->avctx, AV_LOG_INFO, "concealing %d DC, %d AC, %d MV errors in %c frame\n", + dc_error, ac_error, mv_error, av_get_picture_type_char(s->cur_pic.f->pict_type)); is_intra_likely = is_intra_more_likely(s); /* set unknown mb-type to most likely */ for (i = 0; i < s->mb_num; i++) { const int mb_xy = s->mb_index2xy[i]; - error = s->error_status_table[mb_xy]; + int error = s->error_status_table[mb_xy]; if (!((error & ER_DC_ERROR) && (error & ER_MV_ERROR))) continue; @@ -1016,7 +1155,7 @@ void ff_er_frame_end(ERContext *s) const int mv_dir = dir ? MV_DIR_BACKWARD : MV_DIR_FORWARD; int mv_type; - error = s->error_status_table[mb_xy]; + int error = s->error_status_table[mb_xy]; if (IS_INTRA(mb_type)) continue; // intra @@ -1053,7 +1192,7 @@ void ff_er_frame_end(ERContext *s) const int mb_type = s->cur_pic.mb_type[mb_xy]; int mv_dir = MV_DIR_FORWARD | MV_DIR_BACKWARD; - error = s->error_status_table[mb_xy]; + int error = s->error_status_table[mb_xy]; if (IS_INTRA(mb_type)) continue; @@ -1071,6 +1210,7 @@ void ff_er_frame_end(ERContext *s) int time_pp = s->pp_time; int time_pb = s->pb_time; + av_assert0(s->avctx->codec_id != AV_CODEC_ID_H264); ff_thread_await_progress(s->next_pic.tf, mb_y, 0); s->mv[0][0][0] = s->next_pic.motion_val[0][xy][0] * time_pb / time_pp; @@ -1091,6 +1231,9 @@ void ff_er_frame_end(ERContext *s) } else guess_mv(s); + /* the filters below manipulate raw image, skip them */ + if (CONFIG_XVMC && s->avctx->hwaccel && s->avctx->hwaccel->decode_mb) + goto ec_clean; /* fill DC for inter blocks */ for (mb_y = 0; mb_y < s->mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { @@ -1100,7 +1243,7 @@ void ff_er_frame_end(ERContext *s) const int mb_xy = mb_x + mb_y * s->mb_stride; const int mb_type = s->cur_pic.mb_type[mb_xy]; - error = s->error_status_table[mb_xy]; + // error = s->error_status_table[mb_xy]; if (IS_INTRA(mb_type) && s->partitioned_frame) continue; @@ -1123,6 +1266,9 @@ void ff_er_frame_end(ERContext *s) dc_ptr[(n & 1) + (n >> 1) * s->b8_stride] = (dc + 4) >> 3; } + if (!s->cur_pic.f->data[2]) + continue; + dcu = dcv = 0; for (y = 0; y < 8; y++) { int x; @@ -1135,15 +1281,17 @@ void ff_er_frame_end(ERContext *s) s->dc_val[2][mb_x + mb_y * s->mb_stride] = (dcv + 4) >> 3; } } - +#if 1 /* guess DC for damaged blocks */ - guess_dc(s, s->dc_val[0], s->mb_width * 2, s->mb_height * 2, s->b8_stride, 1); - guess_dc(s, s->dc_val[1], s->mb_width, s->mb_height, s->mb_stride, 0); - guess_dc(s, s->dc_val[2], s->mb_width, s->mb_height, s->mb_stride, 0); + guess_dc(s, s->dc_val[0], s->mb_width*2, s->mb_height*2, s->b8_stride, 1); + guess_dc(s, s->dc_val[1], s->mb_width , s->mb_height , s->mb_stride, 0); + guess_dc(s, s->dc_val[2], s->mb_width , s->mb_height , s->mb_stride, 0); +#endif /* filter luma DC */ filter181(s->dc_val[0], s->mb_width * 2, s->mb_height * 2, s->b8_stride); +#if 1 /* render DC only intra */ for (mb_y = 0; mb_y < s->mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { @@ -1151,7 +1299,7 @@ void ff_er_frame_end(ERContext *s) const int mb_xy = mb_x + mb_y * s->mb_stride; const int mb_type = s->cur_pic.mb_type[mb_xy]; - error = s->error_status_table[mb_xy]; + int error = s->error_status_table[mb_xy]; if (IS_INTER(mb_type)) continue; @@ -1161,29 +1309,36 @@ void ff_er_frame_end(ERContext *s) dest_y = s->cur_pic.f->data[0] + mb_x * 16 + mb_y * 16 * linesize[0]; dest_cb = s->cur_pic.f->data[1] + mb_x * 8 + mb_y * 8 * linesize[1]; dest_cr = s->cur_pic.f->data[2] + mb_x * 8 + mb_y * 8 * linesize[2]; + if (!s->cur_pic.f->data[2]) + dest_cb = dest_cr = NULL; put_dc(s, dest_y, dest_cb, dest_cr, mb_x, mb_y); } } +#endif if (s->avctx->error_concealment & FF_EC_DEBLOCK) { /* filter horizontal block boundaries */ h_block_filter(s, s->cur_pic.f->data[0], s->mb_width * 2, s->mb_height * 2, linesize[0], 1); - h_block_filter(s, s->cur_pic.f->data[1], s->mb_width, - s->mb_height, linesize[1], 0); - h_block_filter(s, s->cur_pic.f->data[2], s->mb_width, - s->mb_height, linesize[2], 0); /* filter vertical block boundaries */ v_block_filter(s, s->cur_pic.f->data[0], s->mb_width * 2, s->mb_height * 2, linesize[0], 1); - v_block_filter(s, s->cur_pic.f->data[1], s->mb_width, - s->mb_height, linesize[1], 0); - v_block_filter(s, s->cur_pic.f->data[2], s->mb_width, - s->mb_height, linesize[2], 0); + + if (s->cur_pic.f->data[2]) { + h_block_filter(s, s->cur_pic.f->data[1], s->mb_width, + s->mb_height, linesize[1], 0); + h_block_filter(s, s->cur_pic.f->data[2], s->mb_width, + s->mb_height, linesize[2], 0); + v_block_filter(s, s->cur_pic.f->data[1], s->mb_width, + s->mb_height, linesize[1], 0); + v_block_filter(s, s->cur_pic.f->data[2], s->mb_width, + s->mb_height, linesize[2], 0); + } } +ec_clean: /* clean a few tables */ for (i = 0; i < s->mb_num; i++) { const int mb_xy = s->mb_index2xy[i]; @@ -1197,6 +1352,13 @@ void ff_er_frame_end(ERContext *s) s->mbintra_table[mb_xy] = 1; } + for (i = 0; i < 2; i++) { + av_buffer_unref(&s->ref_index_buf[i]); + av_buffer_unref(&s->motion_val_buf[i]); + s->cur_pic.ref_index[i] = NULL; + s->cur_pic.motion_val[i] = NULL; + } + memset(&s->cur_pic, 0, sizeof(ERPicture)); memset(&s->last_pic, 0, sizeof(ERPicture)); memset(&s->next_pic, 0, sizeof(ERPicture)); |