我们知道,x264的主要压缩策略是,计算待编码宏块与各种模式下生成的参考宏块的差值,也就是代码里cost(损耗)的意思。然后比较各种模式下的损耗,选择损耗最小的一种预测模式。用一个不太恰当的简化的等式,如下:
帧内编码:cost = 待编码宏块 - 参考宏块
帧间编码:cost = 待编码宏块 - 参考宏块 + 运动向量
之所以说不太恰当,是因为cost实际上是待编码宏块与参考宏块之间的sad或satd值。目前x264默认是satd值,前面文章中有过描述。
x264_slicetype_mb_cost函数主要就是来完成上述的工作。当待编码帧是I帧时,只有帧内编码,当待编码帧是P帧或B帧时,还需要计算帧间编码的损耗及运动向量(mv)本身的损耗。本文主要介绍帧内编码损耗。源码如下:
static void x264_slicetype_mb_cost( x264_t *h, x264_mb_analysis_t *a,
x264_frame_t **frames, int p0, int p1, int b,
int dist_scale_factor, int do_search[2], const x264_weight_t *w,
int *output_inter, int *output_intra )
{
//省略
/////////////////////////
if( p0 == p1 ) // 前向参考帧=后向参考帧, 则进行I帧处理
goto lowres_intra_mb;
//省略
/////////////////////////
lowres_intra_mb:
if( !fenc->b_intra_calculated )
{
ALIGNED_ARRAY_16( pixel, edge,[36] ); //pixel edge[36]
pixel *pix = &pix1[8+FDEC_STRIDE]; //pix1指向一个9*32的内存块
pixel *src = &fenc->lowres[0][i_pel_offset]; //i_pel_offset = 8 * (i_mb_x + i_mb_y * i_stride);说明src指向待编码帧的一个宏块
const int intra_penalty = 5 * a->i_lambda;
int satds[3];
int pixoff = 4 / sizeof(pixel); //pixoff等于4;
/* Avoid store forwarding stalls by writing larger chunks */
memcpy( pix-FDEC_STRIDE, src-i_stride, 16 * sizeof(pixel) ); //1.拷贝宏块的正上方一行的像素到pix
for( int i = -1; i < 8; i++ ) //2.拷贝宏块左边边缘的像素到pix
M32( &pix[i*FDEC_STRIDE-pixoff] ) = M32( &src[i*i_stride-pixoff] );
h->pixf.intra_mbcmp_x3_8x8c( h->mb.pic.p_fenc[0], pix, satds ); //3.生成DC,H,V三种模式的宏块,并计算它们与待编码宏块的satd,保存到satds数组中
int i_icost = X264_MIN3( satds[0], satds[1], satds[2] ); //选出最小损耗赋给i_icost
if( h->param.analyse.i_subpel_refine > 1 )
{
h->predict_8x8c[I_PRED_CHROMA_P]( pix ); //4.生成plane模式预测宏块
int satd = h->pixf.mbcmp[PIXEL_8x8]( h->mb.pic.p_fenc[0], FENC_STRIDE, pix, FDEC_STRIDE ); //计算plane宏块与待编码宏块的satd
i_icost = X264_MIN( i_icost, satd );
//5.待编码宏块的左方和上方的像素值进行滤波,然后拷贝到edge数组
h->predict_8x8_filter( pix, edge, ALL_NEIGHBORS, ALL_NEIGHBORS );
for( int i = 3; i < 9; i++ ) //6.生成模式3到模式8的预测宏块,并计算出最小损耗的模式
{
h->predict_8x8[i]( pix, edge );
satd = h->pixf.mbcmp[PIXEL_8x8]( h->mb.pic.p_fenc[0], FENC_STRIDE, pix, FDEC_STRIDE );
i_icost = X264_MIN( i_icost, satd );
}
}
i_icost = ((i_icost + intra_penalty) >> (BIT_DEPTH - 8)) + lowres_penalty;
fenc->i_intra_cost[i_mb_xy] = i_icost; //7.保存i_mb_xy坐标点对应的宏块的损耗
int i_icost_aq = i_icost;
if( h->param.rc.i_aq_mode )
i_icost_aq = (i_icost_aq * fenc->i_inv_qscale_factor[i_mb_xy] + 128) >> 8;
output_intra[ROW_SATD] += i_icost_aq;
if( b_frame_score_mb )
{
output_intra[COST_EST] += i_icost;
output_intra[COST_EST_AQ] += i_icost_aq;
}
}
i_bcost = (i_bcost >> (BIT_DEPTH - 8)) + lowres_penalty;
//省略
/////////////////////////
}
如注释中所示,我把大致过程分成了7步,再列一下:
1和2:拷贝宏块的正上方一行的像素和左边边缘像素到pix,这是为了预测宏块生成而准备的;
3.生成DC,H,V三种模式的宏块,并计算它们与待编码宏块的satd,选出最小的cost值赋给i_icost。
4.生成plane模式预测宏块,计算plane宏块与待编码宏块的satd,选出最小的cost值赋给i_icost。
5.待编码宏块的左方和上方的像素值进行滤波,然后拷贝到edge数组
6.生成模式3到模式8的预测宏块,并计算出最小损耗的模式,赋给i_icost。
7.将i_icost保存到fenc->i_intra_cost[i_mb_xy]。
其中1到4步,主要是对dc,h,v,plane四种预测模式进行计算,在前面的(四)(五)(六)(七)这几篇文章中已有描述。第5和6,主要是对剩下的3到8预测模式进行计算,下面再列一下帧内预测模式:

具体原理可参看h264白皮书,这里不再赘述了。
网友评论