我们直接从decode_frame开始跟踪(相信从这个地方开始不会有人有啥疑问吧,怎么跟踪到这里的已经有很多资料介绍的了),decode_frame —> decode_nal_units —> decode_slice_header —> ff_h264_frame_start —> ff_er_frame_start,ff_er_frame_start中对error_status_table,error_count进行了初始化。
接着从decode_nal_units继续,decode_nal_units会调用execute_decode_slices进行解码,该函数包含了单线程和多线程两种情况,不妨仅考虑单线程情况,则该函数调用decode_slice进行实际的解码,在decode_slice中,解码完成后,会调用ff_er_add_slice根据解码情况对error_status_table进行相应设置,作为以后错误隐藏的依据。
最后,退回到decode_frame中来,在前面调用decode_nal_units完成解码后,会调用field_end,而field_end则会调用ff_er_frame_end,该函数就是真正进行错误隐藏的地方。ff_er_frame_end中有两个最重要的函数guess_dc和guess_mv,简单地讲,前者用于intra宏块(实际上还不止这种宏块),后者用于P slice的非intra宏块(B slice的隐藏方法更为简单,不会调用guess_mv)
static void guess_mv(MpegEncContext *s){
uint8_t fixed[s->mb_stride * s->mb_height];
#define MV_FROZEN 3
#define MV_CHANGED 2
#define MV_UNCHANGED 1
const int mb_stride = s->mb_stride;
const int mb_width = s->mb_width;
const int mb_height= s->mb_height;
int i, depth, num_avail;
int mb_x, mb_y, mot_step, mot_stride;
set_mv_strides(s, &mot_step, &mot_stride); //!< mot_step = 4, mot_stride = s->b4_stride
num_avail=0;
for(i=0; i<s->mb_num; i++){
const int mb_xy= s->mb_index2xy[ i ];
int f=0;
int error= s->error_status_table[mb_xy];
if(IS_INTRA(s->current_picture.mb_type[mb_xy])) f=MV_FROZEN; //intra //FIXME check
if(!(error&MV_ERROR)) f=MV_FROZEN; //inter with undamaged MV
fixed[mb_xy]= f;
if(f==MV_FROZEN)
num_avail++;
}
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_x=0; mb_x<s->mb_width; mb_x++){
const int mb_xy= mb_x + mb_y*s->mb_stride;
if(IS_INTRA(s->current_picture.mb_type[mb_xy])) continue;
if(!(s->error_status_table[mb_xy]&MV_ERROR)) continue; //!< mv ok
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->dsp.clear_blocks(s->block[0]);
s->mb_x= mb_x;
s->mb_y= mb_y;
s->mv[0][0][0]= 0;
s->mv[0][0][1]= 0;
decode_mb(s, 0);
}
}
return;
}
for(depth=0;; depth++){
int changed, pass, none_left;
none_left=1;
changed=1;
for(pass=0; (changed || pass<2) && pass<10; pass++){ //!< pass和changed用于控制迭代次数:changed || pass < 2,如果经历了pass=0
int mb_x, mb_y; //!< 和pass=1两次迭代后(刚好做完一整帧的错误隐藏),changed仍为0,即实际没有宏块的mv被修正,
int score_sum=0; //!< 则没有继续下一轮迭代的必要;pass < 10,控制总迭代次数不超过10次
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= s->current_picture.motion_val[0][mot_index][0];
int prev_y= s->current_picture.motion_val[0][mot_index][1];
if((mb_x^mb_y^pass)&1) continue; //!< 这句话控制着宏块错误隐藏的顺序,pass=0时,先扫描第一行序号为偶数的宏块,
//!< 第二行序号为奇数的宏块,第三行序号为偶数的宏块,以此类推,即每行交错扫描,
//!< pass=1时,重新从第一行开始,改为扫描剩余的序号为奇数的宏块,第二行扫描
//!< 序号为偶数的宏块,以此类推... ...(通过实际打印出扫描宏块序号来确定这一扫描顺序)
if(fixed[mb_xy]==MV_FROZEN) continue; //!< intra or MV ok
assert(!IS_INTRA(s->current_picture.mb_type[mb_xy]));
assert(s->last_picture_ptr && s->last_picture_ptr->data[0]);
j=0;
if(mb_x>0 && fixed[mb_xy-1 ]==MV_FROZEN) j=1; //!< left block
if(mb_x+1<mb_width && fixed[mb_xy+1 ]==MV_FROZEN) j=1; //!< right block
if(mb_y>0 && fixed[mb_xy-mb_stride]==MV_FROZEN) j=1; //!< top block
if(mb_y+1<mb_height && fixed[mb_xy+mb_stride]==MV_FROZEN) j=1; //!< bottom block
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; //!< 邻块可用但其MV没被修改过,且已经进行过一次一整帧的恢复,在这种情况下,即使对该宏块再做一次恢复,结果也肯定与上次一样,故可跳过不做
none_left=0;
if(mb_x>0 && fixed[mb_xy-1]){ //!< 保存左邻块的mv
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
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
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
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; //!< 至少要有一个邻块的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++){
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){ //!< sum_x - max_x - min_x后,如果pred_count为4的话,那么该结果实际为除去最大、最小的两个中间mv,故需要除以2
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->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;
for(j=0; j<pred_count; j++){ //!< 遍历所有预测mv,边界匹配
int score=0;
uint8_t *src= s->current_picture.data[0] + mb_x*16 + mb_y*16*s->linesize;
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];
if(ref[j]<0) //predictor intra or otherwise not available
continue;
decode_mb(s, ref[j]); //!< 基于得到的mv和ref进行解码
if(mb_x>0 && fixed[mb_xy-1]){ //!< left
int k;
for(k=0; k<16; k++)
score += FFABS(src[k*s->linesize-1 ]-src[k*s->linesize ]);
}
if(mb_x+1<mb_width && fixed[mb_xy+1]){ //!< right
int k;
for(k=0; k<16; k++)
score += FFABS(src[k*s->linesize+15]-src[k*s->linesize+16]);
}
if(mb_y>0 && fixed[mb_xy-mb_stride]){ //!< top
int k;
for(k=0; k<16; k++)
score += FFABS(src[k-s->linesize ]-src[k ]);
}
if(mb_y+1<mb_height && fixed[mb_xy+mb_stride]){ //!< bottom
int k;
for(k=0; k<16; k++)
score += FFABS(src[k+s->linesize*15]-src[k+s->linesize*16]);
}
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->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]); //!< 基于前面边界匹配得到的最优mv和ref进行最终的解码
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;
}
}
// printf(".%d/%d", changed, score_sum); fflush(stdout);
}
if(none_left) //!< 只要有一个邻块可用,none_left就会被置0;换句话说,假如none_left仍为1,则说明遍历宏块后,没有一个宏块的邻块可用
return;
for(i=0; i<s->mb_num; i++){
int mb_xy= s->mb_index2xy[i];
if(fixed[mb_xy])
fixed[mb_xy]=MV_FROZEN;
}
// printf(":"); fflush(stdout);
}
}