最近刚好看到有人问,所以会以码率控制为主线,陆续分享一些源码分析。
// Find the total AC energy of the block in all planes.
//! 返回宏块的能量
static NOINLINE uint32_t x264_ac_energy_mb( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame )
{
/* This function contains annoying hacks because GCC has a habit of reordering emms
* and putting it after floating point ops. As a result, we put the emms at the end of the
* function and make sure that its always called before the float math. Noinline makes
* sure no reordering goes on. */
uint32_t var;
x264_prefetch_fenc( h, frame, mb_x, mb_y );
if( h->mb.b_adaptive_mbaff )
{
/* We don't know the super-MB mode we're going to pick yet, so
* simply try both and pick the lower of the two. */
uint32_t var_interlaced, var_progressive;
var_interlaced = ac_energy_plane( h, mb_x, mb_y, frame, 0, 0, 1, 1 );
var_progressive = ac_energy_plane( h, mb_x, mb_y, frame, 0, 0, 0, 0 );
if( CHROMA444 )
{
var_interlaced += ac_energy_plane( h, mb_x, mb_y, frame, 1, 0, 1, 1 );
var_progressive += ac_energy_plane( h, mb_x, mb_y, frame, 1, 0, 0, 0 );
var_interlaced += ac_energy_plane( h, mb_x, mb_y, frame, 2, 0, 1, 1 );
var_progressive += ac_energy_plane( h, mb_x, mb_y, frame, 2, 0, 0, 0 );
}
else
{
var_interlaced += ac_energy_plane( h, mb_x, mb_y, frame, 1, 1, 1, 1 );
var_progressive += ac_energy_plane( h, mb_x, mb_y, frame, 1, 1, 0, 0 );
}
var = X264_MIN( var_interlaced, var_progressive );
}
else ///<
{
var = ac_energy_plane( h, mb_x, mb_y, frame, 0, 0, PARAM_INTERLACED, 1 ); // 亮度
if( CHROMA444 )
{
var += ac_energy_plane( h, mb_x, mb_y, frame, 1, 0, PARAM_INTERLACED, 1 );
var += ac_energy_plane( h, mb_x, mb_y, frame, 2, 0, PARAM_INTERLACED, 1 );
}
else
var += ac_energy_plane( h, mb_x, mb_y, frame, 1, 1, PARAM_INTERLACED, 1 ); // 色度
}
x264_emms();
return var;
}
//! 返回宏块亮度或色度分量的能量
static ALWAYS_INLINE uint32_t ac_energy_plane( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame, int i, int b_chroma, int b_field, int b_store )
{
int height = b_chroma ? 16>>CHROMA_V_SHIFT : 16;
int stride = frame->i_stride[i];
int offset = b_field
? 16 * mb_x + height * (mb_y&~1) * stride + (mb_y&1) * stride
: 16 * mb_x + height * mb_y * stride;
stride <<= b_field;
if( b_chroma )
{
ALIGNED_ARRAY_N( pixel, pix,[FENC_STRIDE*16] );
int chromapix = h->luma2chroma_pixel[PIXEL_16x16]; // 420: 8x8
int shift = 7 - CHROMA_V_SHIFT; // 420: 6
h->mc.load_deinterleave_chroma_fenc( pix, frame->plane[1] + offset, stride, height );
return ac_energy_var( h->pixf.var[chromapix]( pix, FENC_STRIDE ), shift, frame, 1, b_store )
+ ac_energy_var( h->pixf.var[chromapix]( pix+FENC_STRIDE/2, FENC_STRIDE ), shift, frame, 2, b_store );
}
else
return ac_energy_var( h->pixf.var[PIXEL_16x16]( frame->plane[i] + offset, stride ), 8, frame, i, b_store );
}
h->pixf.var分别获取亮度和色度分量的ssd与sum
对于yuv420:亮度分量16x16,色度分量是8x8
对于yuv444:亮度分量16x16,色度分量是16x16
//! 返回分量i的能量
static ALWAYS_INLINE uint32_t ac_energy_var( uint64_t sum_ssd, int shift, x264_frame_t *frame, int i, int b_store )
{ //! 64bit结果分解为32bit ssd, 32bit sum
uint32_t sum = sum_ssd;
uint32_t ssd = sum_ssd >> 32;
if( b_store )
{
frame->i_pixel_sum[i] += sum;
frame->i_pixel_ssd[i] += ssd;
}
return ssd - ((uint64_t)sum * sum >> shift); // ssd - sum * avg
}
//! 按照32bit ssd | 32bit sum返回
static uint64_t x264_pixel_var_16x16( pixel *pix, intptr_t i_stride )
{
uint32_t sum = 0, sqr = 0;
for( int y = 0; y < 16; y++ )
{
for( int x = 0; x < 16; x++ )
{
sum += pix[x];
sqr += pix[x] * pix[x];
}
pix += i_stride;
}
return sum + ((uint64_t)sqr << 32); ///< 32bit sqr + 32bit sum
}