*
* Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of FFmpeg.
+ * This file is part of Libav.
*
- * FFmpeg is free software; you can redistribute it and/or
+ * Libav 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.
*
- * FFmpeg is distributed in the hope that it will be useful,
+ * Libav 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 FFmpeg; if not, write to the Free Software
+ * License along with Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
- * @file libavcodec/error_resilience.c
+ * @file
* Error resilience / concealment.
*/
#include "avcodec.h"
#include "dsputil.h"
#include "mpegvideo.h"
+#include "h264.h"
+#include "rectangle.h"
-static void decode_mb(MpegEncContext *s){
- s->dest[0] = s->current_picture.data[0] + (s->mb_y * 16* s->linesize ) + s->mb_x * 16;
- s->dest[1] = s->current_picture.data[1] + (s->mb_y * 8 * s->uvlinesize) + s->mb_x * 8;
- s->dest[2] = s->current_picture.data[2] + (s->mb_y * 8 * s->uvlinesize) + s->mb_x * 8;
+/*
+ * H264 redefines mb_intra so it is not mistakely used (its uninitialized in h264)
+ * but error concealment must support both h264 and h263 thus we must undo this
+ */
+#undef mb_intra
+static void decode_mb(MpegEncContext *s, int ref){
+ s->dest[0] = s->current_picture.data[0] + (s->mb_y * 16* s->linesize ) + s->mb_x * 16;
+ s->dest[1] = s->current_picture.data[1] + (s->mb_y * (16>>s->chroma_y_shift) * s->uvlinesize) + s->mb_x * (16>>s->chroma_x_shift);
+ s->dest[2] = s->current_picture.data[2] + (s->mb_y * (16>>s->chroma_y_shift) * s->uvlinesize) + s->mb_x * (16>>s->chroma_x_shift);
+
+ if(CONFIG_H264_DECODER && s->codec_id == CODEC_ID_H264){
+ H264Context *h= (void*)s;
+ h->mb_xy= s->mb_x + s->mb_y*s->mb_stride;
+ memset(h->non_zero_count_cache, 0, sizeof(h->non_zero_count_cache));
+ assert(ref>=0);
+ if(ref >= h->ref_count[0]) //FIXME it is posible albeit uncommon that slice references differ between slices, we take the easy approuch and ignore it for now. If this turns out to have any relevance in practice then correct remapping should be added
+ ref=0;
+ fill_rectangle(&s->current_picture.ref_index[0][4*h->mb_xy], 2, 2, 2, ref, 1);
+ fill_rectangle(&h->ref_cache[0][scan8[0]], 4, 4, 8, ref, 1);
+ fill_rectangle(h->mv_cache[0][ scan8[0] ], 4, 4, 8, pack16to32(s->mv[0][0][0],s->mv[0][0][1]), 4);
+ assert(!FRAME_MBAFF);
+ ff_h264_hl_decode_mb(h);
+ }else{
+ assert(ref==0);
MPV_decode_mb(s, s->block);
+ }
+}
+
+/**
+ * @param stride the number of MVs to get to the next row
+ * @param mv_step the number of MVs per row or column in a macroblock
+ */
+static void set_mv_strides(MpegEncContext *s, int *mv_step, int *stride){
+ if(s->codec_id == CODEC_ID_H264){
+ H264Context *h= (void*)s;
+ assert(s->quarter_sample);
+ *mv_step= 4;
+ *stride= h->b_stride;
+ }else{
+ *mv_step= 2;
+ *stride= s->b8_stride;
+ }
}
/**
* @param h height in 8 pixel blocks
*/
static void h_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int stride, int is_luma){
- int b_x, b_y;
+ int b_x, b_y, mvx_stride, mvy_stride;
uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
+ set_mv_strides(s, &mvx_stride, &mvy_stride);
+ mvx_stride >>= is_luma;
+ mvy_stride *= mvx_stride;
for(b_y=0; b_y<h; b_y++){
for(b_x=0; b_x<w-1; b_x++){
int left_damage = left_status&(DC_ERROR|AC_ERROR|MV_ERROR);
int right_damage= right_status&(DC_ERROR|AC_ERROR|MV_ERROR);
int offset= b_x*8 + b_y*stride*8;
- int16_t *left_mv= s->current_picture.motion_val[0][s->b8_stride*(b_y<<(1-is_luma)) + ( b_x <<(1-is_luma))];
- int16_t *right_mv= s->current_picture.motion_val[0][s->b8_stride*(b_y<<(1-is_luma)) + ((b_x+1)<<(1-is_luma))];
+ int16_t *left_mv= s->current_picture.motion_val[0][mvy_stride*b_y + mvx_stride* b_x ];
+ int16_t *right_mv= s->current_picture.motion_val[0][mvy_stride*b_y + mvx_stride*(b_x+1)];
if(!(left_damage||right_damage)) continue; // both undamaged
* @param h height in 8 pixel blocks
*/
static void v_block_filter(MpegEncContext *s, uint8_t *dst, int w, int h, int stride, int is_luma){
- int b_x, b_y;
+ int b_x, b_y, mvx_stride, mvy_stride;
uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
+ set_mv_strides(s, &mvx_stride, &mvy_stride);
+ mvx_stride >>= is_luma;
+ mvy_stride *= mvx_stride;
for(b_y=0; b_y<h-1; b_y++){
for(b_x=0; b_x<w; b_x++){
int top_damage = top_status&(DC_ERROR|AC_ERROR|MV_ERROR);
int bottom_damage= bottom_status&(DC_ERROR|AC_ERROR|MV_ERROR);
int offset= b_x*8 + b_y*stride*8;
- int16_t *top_mv= s->current_picture.motion_val[0][s->b8_stride*( b_y <<(1-is_luma)) + (b_x<<(1-is_luma))];
- int16_t *bottom_mv= s->current_picture.motion_val[0][s->b8_stride*((b_y+1)<<(1-is_luma)) + (b_x<<(1-is_luma))];
+ int16_t *top_mv= s->current_picture.motion_val[0][mvy_stride* b_y + mvx_stride*b_x];
+ int16_t *bottom_mv= s->current_picture.motion_val[0][mvy_stride*(b_y+1) + mvx_stride*b_x];
if(!(top_damage||bottom_damage)) continue; // both undamaged
const int mb_width = s->mb_width;
const int mb_height= s->mb_height;
int i, depth, num_avail;
- int mb_x, mb_y;
+ int mb_x, mb_y, mot_step, mot_stride;
+
+ set_mv_strides(s, &mot_step, &mot_stride);
num_avail=0;
for(i=0; i<s->mb_num; i++){
if(IS_INTRA(s->current_picture.mb_type[mb_xy])) continue;
if(!(s->error_status_table[mb_xy]&MV_ERROR)) continue;
- s->mv_dir = MV_DIR_FORWARD;
+ s->mv_dir = s->last_picture.data[0] ? MV_DIR_FORWARD : MV_DIR_BACKWARD;
s->mb_intra=0;
s->mv_type = MV_TYPE_16X16;
s->mb_skipped=0;
s->mb_y= mb_y;
s->mv[0][0][0]= 0;
s->mv[0][0][1]= 0;
- decode_mb(s);
+ decode_mb(s, 0);
}
}
return;
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_stride= s->b8_stride;
- const int mot_index= mb_x*2 + mb_y*2*mot_stride;
+ const int mot_index= (mb_x + mb_y*mot_stride) * mot_step;
int prev_x= s->current_picture.motion_val[0][mot_index][0];
int prev_y= s->current_picture.motion_val[0][mot_index][1];
none_left=0;
if(mb_x>0 && fixed[mb_xy-1]){
- mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index - 2][0];
- mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index - 2][1];
+ mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index - mot_step][0];
+ mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index - mot_step][1];
+ ref [pred_count] = s->current_picture.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->current_picture.motion_val[0][mot_index + 2][0];
- mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index + 2][1];
+ mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index + mot_step][0];
+ mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index + mot_step][1];
+ ref [pred_count] = s->current_picture.ref_index[0][4*(mb_xy+1)];
pred_count++;
}
if(mb_y>0 && fixed[mb_xy-mb_stride]){
- mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index - mot_stride*2][0];
- mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index - mot_stride*2][1];
+ mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index - mot_stride*mot_step][0];
+ mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index - mot_stride*mot_step][1];
+ ref [pred_count] = s->current_picture.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->current_picture.motion_val[0][mot_index + mot_stride*2][0];
- mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index + mot_stride*2][1];
+ mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index + mot_stride*mot_step][0];
+ mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index + mot_stride*mot_step][1];
+ ref [pred_count] = s->current_picture.ref_index[0][4*(mb_xy+s->mb_stride)];
pred_count++;
}
if(pred_count==0) continue;
if(pred_count>1){
- int sum_x=0, sum_y=0;
- int max_x, max_y, min_x, min_y;
+ 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= 99999;
- max_y= max_x=-99999;
+ min_y= min_x= min_r= 99999;
+ max_y= max_x= max_r=-99999;
}else{
- min_x=min_y=max_x=max_y=0;
+ 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;
}
+skip_mean_and_median:
/* zero MV */
pred_count++;
/* last MV */
mv_predictor[pred_count][0]= s->current_picture.motion_val[0][mot_index][0];
mv_predictor[pred_count][1]= s->current_picture.motion_val[0][mot_index][1];
+ ref [pred_count] = s->current_picture.ref_index[0][4*mb_xy];
pred_count++;
s->mv_dir = MV_DIR_FORWARD;
s->current_picture.motion_val[0][mot_index][0]= s->mv[0][0][0]= mv_predictor[j][0];
s->current_picture.motion_val[0][mot_index][1]= s->mv[0][0][1]= mv_predictor[j][1];
- decode_mb(s);
+ if(ref[j]<0) //predictor intra or otherwise not available
+ continue;
+
+ decode_mb(s, ref[j]);
if(mb_x>0 && fixed[mb_xy-1]){
int k;
}
}
score_sum+= best_score;
-//FIXME no need to set s->current_picture.motion_val[0][mot_index][0] explicit
- s->current_picture.motion_val[0][mot_index][0]= s->mv[0][0][0]= mv_predictor[best_pred][0];
- s->current_picture.motion_val[0][mot_index][1]= s->mv[0][0][1]= mv_predictor[best_pred][1];
+ s->mv[0][0][0]= mv_predictor[best_pred][0];
+ s->mv[0][0][1]= mv_predictor[best_pred][1];
- decode_mb(s);
+ for(i=0; i<mot_step; i++)
+ for(j=0; j<mot_step; j++){
+ s->current_picture.motion_val[0][mot_index+i+j*mot_stride][0]= s->mv[0][0][0];
+ s->current_picture.motion_val[0][mot_index+i+j*mot_stride][1]= s->mv[0][0][1];
+ }
+
+ decode_mb(s, ref[best_pred]);
if(s->mv[0][0][0] != prev_x || s->mv[0][0][1] != prev_y){
undamaged_count++;
}
+ if(s->codec_id == CODEC_ID_H264){
+ H264Context *h= (void*)s;
+ if(h->ref_count[0] <= 0 || !h->ref_list[0][0].data[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_MPEG_XVMC_DECODER && s->avctx->xvmc_acceleration && s->pict_type == FF_I_TYPE)
+ if(CONFIG_MPEG_XVMC_DECODER && s->avctx->xvmc_acceleration && s->pict_type == AV_PICTURE_TYPE_I)
return 1;
skip_amount= FFMAX(undamaged_count/50, 1); //check only upto 50 MBs
j++;
if((j%skip_amount) != 0) continue; //skip a few to speed things up
- if(s->pict_type==FF_I_TYPE){
+ if(s->pict_type==AV_PICTURE_TYPE_I){
uint8_t *mb_ptr = s->current_picture.data[0] + mb_x*16 + mb_y*16*s->linesize;
uint8_t *last_mb_ptr= s->last_picture.data [0] + mb_x*16 + mb_y*16*s->linesize;
const int end_xy = s->mb_index2xy[end_i];
int mask= -1;
+ if(s->avctx->hwaccel)
+ return;
+
if(start_i > end_i || start_xy > end_xy){
av_log(s->avctx, AV_LOG_ERROR, "internal error, slice end before start\n");
return;
Picture *pic= s->current_picture_ptr;
if(!s->error_recognition || s->error_count==0 || s->avctx->lowres ||
+ s->avctx->hwaccel ||
s->avctx->codec->capabilities&CODEC_CAP_HWACCEL_VDPAU ||
+ s->picture_structure != PICT_FRAME || // we dont support ER of field pictures yet, though it should not crash if enabled
s->error_count==3*s->mb_width*(s->avctx->skip_top + s->avctx->skip_bottom)) return;
if(s->current_picture.motion_val[0] == NULL){
av_log(s->avctx, AV_LOG_ERROR, "Warning MVs not available\n");
for(i=0; i<2; i++){
- pic->ref_index[i]= av_mallocz(size * sizeof(uint8_t));
+ pic->ref_index[i]= av_mallocz(s->mb_stride * s->mb_height * 4 * sizeof(uint8_t));
pic->motion_val_base[i]= av_mallocz((size+4) * 2 * sizeof(uint16_t));
pic->motion_val[i]= pic->motion_val_base[i]+4;
}
s->current_picture= *s->current_picture_ptr;
}
- for(i=0; i<2; i++){
- if(pic->ref_index[i])
- memset(pic->ref_index[i], 0, size * sizeof(uint8_t));
- }
-
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++){
}
}
-#if 1
/* handle overlapping slices */
for(error_type=1; error_type<=3; error_type++){
int end_ok=0;
end_ok=0;
}
}
-#endif
-#if 1
+
/* handle slices with partitions of different length */
if(s->partitioned_frame){
int end_ok=0;
end_ok=0;
}
}
-#endif
+
/* handle missing slices */
if(s->error_recognition>=4){
int end_ok=1;
}
}
-#if 1
/* backward mark errors */
distance=9999999;
for(error_type=1; error_type<=3; error_type++){
distance= 9999999;
}
}
-#endif
/* forward mark errors */
error=0;
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++){
s->error_status_table[mb_xy]= error;
}
}
-#endif
dc_error= ac_error= mv_error=0;
for(i=0; i<s->mb_num; i++){
s->current_picture.mb_type[mb_xy]= MB_TYPE_16x16 | MB_TYPE_L0;
}
+ // change inter to intra blocks if no reference frames are available
+ if (!s->last_picture.data[0] && !s->next_picture.data[0])
+ for(i=0; i<s->mb_num; i++){
+ const int mb_xy= s->mb_index2xy[i];
+ if(!IS_INTRA(s->current_picture.mb_type[mb_xy]))
+ s->current_picture.mb_type[mb_xy]= MB_TYPE_INTRA4x4;
+ }
+
/* handle inter blocks with damaged AC */
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;
const int mb_type= s->current_picture.mb_type[mb_xy];
+ int dir = !s->last_picture.data[0];
error= s->error_status_table[mb_xy];
if(IS_INTRA(mb_type)) continue; //intra
if(error&MV_ERROR) continue; //inter with damaged MV
if(!(error&AC_ERROR)) continue; //undamaged inter
- s->mv_dir = MV_DIR_FORWARD;
+ s->mv_dir = dir ? MV_DIR_BACKWARD : MV_DIR_FORWARD;
s->mb_intra=0;
s->mb_skipped=0;
if(IS_8X8(mb_type)){
int j;
s->mv_type = MV_TYPE_8X8;
for(j=0; j<4; j++){
- s->mv[0][j][0] = s->current_picture.motion_val[0][ mb_index + (j&1) + (j>>1)*s->b8_stride ][0];
- s->mv[0][j][1] = s->current_picture.motion_val[0][ mb_index + (j&1) + (j>>1)*s->b8_stride ][1];
+ s->mv[0][j][0] = s->current_picture.motion_val[dir][ mb_index + (j&1) + (j>>1)*s->b8_stride ][0];
+ s->mv[0][j][1] = s->current_picture.motion_val[dir][ mb_index + (j&1) + (j>>1)*s->b8_stride ][1];
}
}else{
s->mv_type = MV_TYPE_16X16;
- s->mv[0][0][0] = s->current_picture.motion_val[0][ mb_x*2 + mb_y*2*s->b8_stride ][0];
- s->mv[0][0][1] = s->current_picture.motion_val[0][ mb_x*2 + mb_y*2*s->b8_stride ][1];
+ s->mv[0][0][0] = s->current_picture.motion_val[dir][ mb_x*2 + mb_y*2*s->b8_stride ][0];
+ s->mv[0][0][1] = s->current_picture.motion_val[dir][ mb_x*2 + mb_y*2*s->b8_stride ][1];
}
s->dsp.clear_blocks(s->block[0]);
s->mb_x= mb_x;
s->mb_y= mb_y;
- decode_mb(s);
+ decode_mb(s, 0/*FIXME h264 partitioned slices need this set*/);
}
}
/* guess MVs */
- if(s->pict_type==FF_B_TYPE){
+ if(s->pict_type==AV_PICTURE_TYPE_B){
for(mb_y=0; mb_y<s->mb_height; mb_y++){
for(mb_x=0; mb_x<s->mb_width; mb_x++){
int xy= mb_x*2 + mb_y*2*s->b8_stride;
if(!(error&AC_ERROR)) continue; //undamaged inter
s->mv_dir = MV_DIR_FORWARD|MV_DIR_BACKWARD;
+ if(!s->last_picture.data[0]) s->mv_dir &= ~MV_DIR_FORWARD;
+ if(!s->next_picture.data[0]) s->mv_dir &= ~MV_DIR_BACKWARD;
s->mb_intra=0;
s->mv_type = MV_TYPE_16X16;
s->mb_skipped=0;
s->dsp.clear_blocks(s->block[0]);
s->mb_x= mb_x;
s->mb_y= mb_y;
- decode_mb(s);
+ decode_mb(s, 0);
}
}
}else
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);
-#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++){
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 */
const int mb_xy= s->mb_index2xy[i];
int error= s->error_status_table[mb_xy];
- if(s->pict_type!=FF_B_TYPE && (error&(DC_ERROR|MV_ERROR|AC_ERROR))){
+ if(s->pict_type!=AV_PICTURE_TYPE_B && (error&(DC_ERROR|MV_ERROR|AC_ERROR))){
s->mbskip_table[mb_xy]=0;
}
s->mbintra_table[mb_xy]=1;