h264编码 码率控制有一个重要的函数,J(cost) = D + lambda*R;(D一般为ssd,R为本宏块预计消耗的bits数目)
lambda为各种情况下的系数,该系数值和当前宏块qp值,具体看下面函数实现
static int rd_cost_mb( x264_t *h, int i_lambda2 )
{
int b_transform_bak = h->mb.b_transform_8x8;
int i_ssd;
int i_bits;
int type_bak = h->mb.i_type;
x264_macroblock_encode( h );
if( h->mb.b_deblock_rdo )
x264_macroblock_deblock( h );
i_ssd = ssd_mb( h );//计算当前宏块ssd,
if( IS_SKIP( h->mb.i_type ) )
{
i_bits = (1 * i_lambda2 + 128) >> 8;// skip宏块只有一个bit
}
else if( h->param.b_cabac )
{
x264_cabac_t cabac_tmp;
COPY_CABAC;
macroblock_size_cabac( h, &cabac_tmp );
i_bits = ( (uint64_t)cabac_tmp.f8_bits_encoded * i_lambda2 + 32768 ) >> 16;//计算lambda * R的结果
}
else
{
macroblock_size_cavlc( h );
i_bits = ( (uint64_t)h->out.bs.i_bits_encoded * i_lambda2 + 128 ) >> 8;
}
h->mb.b_transform_8x8 = b_transform_bak;
h->mb.i_type = type_bak;
return X264_MIN( i_ssd + i_bits, COST_MAX );//J = lambda*R + D (ssd);//对应公式。
}
跟一下来看看,i_lambda值如何得到的。
static void mb_analyse_init_qp( x264_t *h, x264_mb_analysis_t *a, int qp )
{//宏块分析初始化
int effective_chroma_qp = h->chroma_qp_table[SPEC_QP(qp)] + X264_MAX( qp - QP_MAX_SPEC, 0 );
a->i_lambda = x264_lambda_tab[qp];
a->i_lambda2 = x264_lambda2_tab[qp];//得到本宏块预计的lambda值,后面我们再分析下,宏块的初始qp如何得到的。
h->mb.b_trellis = h->param.analyse.i_trellis > 1 && a->i_mbrd;
if( h->param.analyse.i_trellis )
{
h->mb.i_trellis_lambda2[0][0] = x264_trellis_lambda2_tab[0][qp];
h->mb.i_trellis_lambda2[0][1] = x264_trellis_lambda2_tab[1][qp];
h->mb.i_trellis_lambda2[1][0] = x264_trellis_lambda2_tab[0][effective_chroma_qp];
h->mb.i_trellis_lambda2[1][1] = x264_trellis_lambda2_tab[1][effective_chroma_qp];
}
h->mb.i_psy_rd_lambda = a->i_lambda;
/* Adjusting chroma lambda based on QP offset hurts PSNR but improves visual quality. */
int chroma_offset_idx = X264_MIN( qp-effective_chroma_qp+12, MAX_CHROMA_LAMBDA_OFFSET );
h->mb.i_chroma_lambda2_offset = h->param.analyse.b_psy ? x264_chroma_lambda2_offset_tab[chroma_offset_idx] : 256;
if( qp > QP_MAX_SPEC )
{
h->nr_offset = h->nr_offset_emergency[qp-QP_MAX_SPEC-1];
h->nr_residual_sum = h->nr_residual_sum_buf[1];
h->nr_count = h->nr_count_buf[1];
h->mb.b_noise_reduction = 1;
qp = QP_MAX_SPEC; /* Out-of-spec QPs are just used for calculating lambda values. */
}
else
{
h->nr_offset = h->nr_offset_denoise;
h->nr_residual_sum = h->nr_residual_sum_buf[0];
h->nr_count = h->nr_count_buf[0];
h->mb.b_noise_reduction = 0;
}
a->i_qp = h->mb.i_qp = qp;
h->mb.i_chroma_qp = h->chroma_qp_table[qp];
}
//接下来看看SSD如何得到的。
static inline int ssd_mb( x264_t *h )
{
int i_ssd = ssd_plane( h, PIXEL_16x16, 0, 0, 0 );
if( CHROMA_FORMAT )
{
int chroma_size = h->luma2chroma_pixel[PIXEL_16x16];
int chroma_ssd = ssd_plane( h, chroma_size, 1, 0, 0 ) + ssd_plane( h, chroma_size, 2, 0, 0 );
i_ssd += ((uint64_t)chroma_ssd * h->mb.i_chroma_lambda2_offset + 128) >> 8;
}
return i_ssd;
}//计算得到本宏块的SSD
static inline int ssd_plane( x264_t *h, int size, int p, int x, int y )
{//size 块大小,x ,y 块的坐标位置 p通道号,0亮度 1 U 2 V
int satd = 0;
pixel *fdec = h->mb.pic.p_fdec[p] + x + y*FDEC_STRIDE;
pixel *fenc = h->mb.pic.p_fenc[p] + x + y*FENC_STRIDE;
if( p == 0 && h->mb.i_psy_rd )//如果开了亮度,并且开了psy_rd
{
/* If the plane is smaller than 8x8, we can't do an SA8D; this probably isn't a big problem. */
if( size <= PIXEL_8x8 )
{
uint64_t fdec_acs = h->pixf.hadamard_ac[size]( fdec, FDEC_STRIDE );
uint64_t fenc_acs = cached_hadamard( h, size, x, y );//已经缓存了的哈德曼区块
satd = abs((int32_t)fdec_acs - (int32_t)fenc_acs)
+ abs((int32_t)(fdec_acs>>32) - (int32_t)(fenc_acs>>32));
satd >>= 1;
}
else
{//lambda为码率 失真 权重比
int dc = h->pixf.sad[size]( fdec, FDEC_STRIDE, (pixel*)x264_zero, 0 ) >> 1;
satd = abs(h->pixf.satd[size]( fdec, FDEC_STRIDE, (pixel*)x264_zero, 0 )
- dc - cached_satd( h, size, x, y ));//已经缓存了satd的量
}
satd = (satd * h->mb.i_psy_rd * h->mb.i_psy_rd_lambda + 128) >> 8;//psy 这里用到了psy-rd系数,这里可以看出来,
//这里psy-rd弱化了satd 对ssd最终结果的影响,psy-rd 越小,计算出来的satd越小,
}
return h->pixf.ssd[size](fenc, FENC_STRIDE, fdec, FDEC_STRIDE) + satd;
}
以上公式展开就是:
cost = SSD + SATD*psy + lambda*R;
rdo算法试图找到一个COST最小的,lambda是固定值,这个公式的意义是,如果psy很大,SATD权重大,psy大的时候,这个
宏块越不容易被选中为参考块,因为cost也大。这个时候cost回偏向选择SATD小的,这种情况是在意失真的,为什么说这里是在意失真,cost要选择SATD小的块,qp值是一定的,这样SATD小直接就被选择了,而忽略了码率R的影响,会导致一个结果,就是这个块码率可能会很大,这样一来别的块就不够码率。 反之,如果psy很小,satd对最终cost计算结果影响就小,这个时候cost偏向于选择R码率小的宏块。但是这样会导致失真大,因为码率小,satd如何,这里没考虑,会压缩得更厉害。
实践证明,在码率充足的时候,psy设置比较大是没问题,范围0-10,默认是1。 码率不足的时候,最好设置0-1 ,这样能把码率节约下来。