x264源代码分析(三)
时间:2006-07-17 来源:lzhw_lucky
H. x264_slice_write()(定义在\ENCODER\encoder.c中),这里面是编码的最主要部分,下面仔细分析.
前面不说,看下面这个循环,它是采用for循环对一帧图像的所有块依次进行编码.
for( mb_xy = 0, i_skip = 0; mb_xy < h->sps->i_mb_width * h->sps->i_mb_height; mb_xy++ )//h->sps->i_mb_width指的是从宽度上说有多少个宏快.对于宽度也就是288 / 16 = 18
{
const int i_mb_y = mb_xy / h->sps->i_mb_width;
const int i_mb_x = mb_xy % h->sps->i_mb_width;//这两个变量是定义宏块的位置.而不是指宏块中元素的位置.
/* load cache */
x264_macroblock_cache_load( h, i_mb_x, i_mb_y );//是把当前宏块的up宏块和left宏块的intra4x4_pred_mode,non_zero_count加载进来,放到一个数组里面,这个数组用来直接得到当前宏块的左侧和上面宏块的相关值.要想得到当前块的预测值,要先知道上面,左面的预测值,它的目的是替代getneighbour函数.
/* analyse parameters
* Slice I: choose I_4x4 or I_16x16 mode
* Slice P: choose between using P mode or intra (4x4 or 16x16)
* */
TIMER_START( i_mtime_analyse );
x264_macroblock_analyse( h );//定义在analyse.h中.
TIMER_STOP( i_mtime_analyse );
/* encode this macrobock -> be carefull it can change the mb type to P_SKIP if needed */
TIMER_START( i_mtime_encode );
x264_macroblock_encode( h );//定义在Enc/encoder.c中.
TIMER_STOP( i_mtime_encode );
截止到这就已经完成编码的主要过程了,后面就是熵编码的过程了(我也没看到那,但认为前面才是编码的主要过程).下面对这个过程进行分析.
A. x264_macroblock_cache_load( h, i_mb_x, i_mb_y );它是将要编码的宏块的周围的宏块的值读进来, 要想得到当前块的预测值,要先知道上面,左面的预测值,它的作用相当于jm93中的getneighbour函数.
B. 进入x264_macroblock_analyse( h )函数(定义在\Enc\analyse.c中,这里涉及到了函数指针数组,需要好好复习,个人认为这也是x264代码最为复杂的一个地方了).既然已经将该宏块周围的宏块的值读了出来,我们就可以对该宏块进行分析了(其实主要就是通过计算sad值分析是否要将16*16的宏块进行分割和采用哪种分割方式合适).
看似很复杂,但我们只要把握一个东西就有利于理解了:
举个生活中的例子来说:
如果你有2元钱,你可以去买2袋1元钱的瓜子,也可以买一袋2元钱的瓜子,如果2袋1元钱的瓜子数量加起来比1袋2元钱的瓜子数量多,你肯定会买2袋1元的.反之你会去买那2元1袋的.
具体来说,对于一个16*16的块,
如果它是I帧的块,我们可以将它分割成16个4*4的块,如果这16个块的sad加起来小于按16*16的方式计算出来的sad值,我们就将这个16*16的块分成16个4*4的块进行编码(在计算每个4*4的块的最小sad值时已经知道它采用何种编码方式最佳了),否则采用16*16的方式编码(同样我们也已知道对它采用哪种编码方式最为合适了.
如果它是P帧或B帧的块,同样是循环套循环,但更为复杂了,可以看我在analyse.c中的注释.
这里还要注意的是提到了
x264_predict_t predict_16x16[4+3];
typedef void (*x264_predict_t)( uint8_t *src, int i_stride );
这是函数指针数组,有很多对它的调用.
C. 退出x264_macroblock_analyse( h )函数,进入x264_macroblock_encode( )函数(定义在\ENCODER\macroblock.c中).
我拿宏块类型为I_16*16为例.
if( h->mb.i_type == I_16x16 )
{
const int i_mode = h->mb.i_intra16x16_pred_mode;
/* do the right prediction */
h->predict_16x16[i_mode]( h->mb.pic.p_fdec[0], h->mb.pic.i_fdec[0] );//这两个参数的关系.
//涉及到x264_predict_t(函数指针数组),声明在core/predict.h中,core/predict.c里有不同定义.
/* encode the 16x16 macroblock */
x264_mb_encode_i16x16( h, i_qscale );//
/* fix the pred mode value */
… }
我们看到h->predict_16x16[i_mode]( h->mb.pic.p_fdec[0], h->mb.pic.i_fdec[0] );只调用了一次,这是因为在x264_macroblock_analyse( )中我们已经确定了采用4种方式中的哪种最合适.而在x264_macroblock_analyse( )中判定一个块是否为I_16*16,我们调用了四次.这是因为当时我们需要拿最小的sad值进行比较.
继续,是x264_mb_encode_i16x16( h, i_qscale )函数(定义在\ENCODER\macroblock.c中).在这个函数中我们就可以看到量化,zig-扫描等函数了,这些都是直来直去的,需要的只是我们的细心和对数学知识的掌握了