[转贴]x264_macroblock_analyse模块分析

本文对x264_macroblock_analyse()及其子函数的流程进行详尽的分析。希望对大家有所帮助。

参考x264_050530版本。

1、void x264_macroblock_analyse( x264_t *h )

首先初始化函数,然后进入一个选择语句

if( h->sh.i_type == SLICE_TYPE_I )

{

       ……..

}

else if( h->sh.i_type == SLICE_TYPE_P )

{

       ……..

}

else if( h->sh.i_type == SLICE_TYPE_B )

{

       ………

}

很明显,这是对不同类型的块采用不同的处理步骤,我们就进入SLICE_TYPE_P深入分析一下吧,因为P类型包含有I模式的检测,SLICE_TYPE_B只是SLICE_TYPE_P的简单延伸而已。

if( h->param.analyse.b_fast_pskip )

        {

            if( h->param.analyse.i_subpel_refine >= 3 )

                analysis.b_try_pskip = 1;

            else if( h->mb.i_mb_type_left == P_SKIP ||

                     h->mb.i_mb_type_top == P_SKIP ||

                     h->mb.i_mb_type_topleft == P_SKIP ||

                     h->mb.i_mb_type_topright == P_SKIP )

                b_skip = x264_macroblock_probe_pskip( h );

        }

条件进入后首先判断skip模式,如果满足skip模式则判断结束,否则继续下面的判断。


x264_mb_analyse_inter_p16x16( h, &analysis );

            if( flags & X264_ANALYSE_PSUB16x16 )

            {

                if( h->param.analyse.b_mixed_references )

                    x264_mb_analyse_inter_p8x8_mixed_ref( h, &analysis );                else

                    x264_mb_analyse_inter_p8x8( h, &analysis );

            }

分析16×16和8×8模式。模式代价值分别保存在和analysis.l0.me16x16.cost 、analysis.l0.i_cost8x8中。


if( ( flags & X264_ANALYSE_PSUB16x16 ) && analysis.l0.i_cost8x8 < analysis.l0.me16x16.cost )

                     {

                            …….

}

如果8×8代价值小于16×16,则进行子宏块分割的判断。


for( i = 0; i < 4; i++ )

                    {

                        x264_mb_analyse_inter_p4x4( h, &analysis, i );

                        if( analysis.l0.i_cost4x4[i] < analysis.l0.me8x8[i].cost )

                        {

                                                        ………

}

依次对4个子宏块(8×8)进行处理,x264_mb_analyse_inter_p4x4()函数实际上是得到4个4×4块的代价和analysis.l0.i_cost4x4[i]。如果4×4模式优于8×8模式,才进行8×8块的细分割。细分割代码分析略。


if( ( flags & X264_ANALYSE_PSUB16x16 ) && analysis.l0.i_cost8x8 < analysis.l0.me16x16.cost + i_thresh16x8 )

            {

                x264_mb_analyse_inter_p16x8( h, &analysis );

                ……..

                x264_mb_analyse_inter_p8x16( h, &analysis );

                ……...

            }

紧接着检测16×8和8×16模式


x264_me_refine_qpel( h, &analysis.l0.me16x16 );

……..

帧间模式选择后,对该模式进行亚象素精细搜索。以进一步减少误差。值得注意的是,在前面每个模式的检测时,也要进行亚象素搜索,见x264_me_search_ref()函数的最后几行。这里的亚象素搜索是在前面基础上再进行精细搜索的。二者亚象素搜索(包括半象素和1/4象素)的次数由subpel_iterations[i][4]确定,而i由编译参数subme确定,看运行帮助:

-m, --subme        Subpixel motion estimation quality: 1=fast, 5=best

实际上subme就是决定模式选择前后亚象素估计的点数。Subme越大,压缩效率越好,计算量越大。


x264_mb_analyse_intra( h, &analysis, i_cost );

            if( h->mb.b_chroma_me && !analysis.b_mbrd &&

                ( analysis.i_sad_i16x16 < i_cost

               || analysis.i_sad_i8x8 < i_cost

               || analysis.i_sad_i4x4 < i_cost ))

            {

                x264_mb_analyse_intra_chroma( h, &analysis );

                analysis.i_sad_i16x16 += analysis.i_sad_i8x8chroma;

                analysis.i_sad_i8x8 += analysis.i_sad_i8x8chroma;

                analysis.i_sad_i4x4 += analysis.i_sad_i8x8chroma;

            }

分析宏块的帧内编码,包括亮度和色度。亮度的16×16、8×8、4×4代价分别存在analysis.i_sad_i16x16、analysis.i_sad_i8x8、analysis.i_sad_i4x4中,色度代价存在analysis.i_sad_i8x8chroma中。


i_intra_type = I_16x16;

            i_intra_cost = analysis.i_sad_i16x16;

            if( analysis.i_sad_i8x8 < i_intra_cost )

            {

                i_intra_type = I_8x8;

                i_intra_cost = analysis.i_sad_i8x8;

            }

            if( analysis.i_sad_i4x4 < i_intra_cost )

            {

                i_intra_type = I_4x4;

                i_intra_cost = analysis.i_sad_i4x4;

            }

            if( i_intra_cost < i_cost )

            {

                i_type = i_intra_type;

                i_cost = i_intra_cost;

            }

比较得到最佳的帧内预测模式。


if( i_intra_cost < i_cost )

            {

                i_type = i_intra_type;

                i_cost = i_intra_cost;

            }

帧内代价与帧间代价比较,得到最佳的预测模式。


整个P类型就分析完了,然后看看条件跳出以后执行什么

if( !analysis.b_mbrd )

        x264_mb_analyse_transform( h );

判断变换的时候是采用8×8变换还是4×4变换。


这样,怎么函数就分析完了^_^,下面看看调用的重要子函数。

2、static inline int x264_macroblock_probe_pskip( x264_t *h )

该函数直接调用了x264_macroblock_probe_skip(h, 0);看看里边有什么东东。

if( !b_bidir )

    {

        x264_mb_predict_mv_pskip( h, mvp );

        mvp[0] = x264_clip3( mvp[0], h->mb.mv_min[0], h->mb.mv_max[0] );

        mvp[1] = x264_clip3( mvp[1], h->mb.mv_min[1], h->mb.mv_max[1] );

        h->mc.mc_luma( h->mb.pic.p_fref[0][0], h->mb.pic.i_stride[0],

                       h->mb.pic.p_fdec[0],    FDEC_STRIDE,

                       mvp[0], mvp[1], 16, 16 );

    }

先得到预测矢量MVp,然后对MVp进行饱和处理,再进行相应的运动补偿。


h->dctf.sub16x16_dct( dct4x4, h->mb.pic.p_fenc[0], FENC_STRIDE,

                                  h->mb.pic.p_fdec[0], FDEC_STRIDE );

for( i8x8 = 0, i_decimate_mb = 0; i8x8 < 4; i8x8++ )

    {

        /* encode one 4x4 block */

        for( i4x4 = 0; i4x4 < 4; i4x4++ )

        {

            const int idx = i8x8 * 4 + i4x4;


            quant_4x4( h, dct4x4[idx], (int(*)[4][4])def_quant4_mf, i_qp, 0 );

            scan_zigzag_4x4full( dctscan, dct4x4[idx] );


            i_decimate_mb += x264_mb_decimate_score( dctscan, 16 );


            if( i_decimate_mb >= 6 )

            {

                /* not as P_SKIP */

                return 0;

            }

        }

    }

进行dct变换(注意是4×4变换,不是8×8!!!),然后对每个8×8块中的4×4对进行量化,zigzag扫描,得到8×8块的i_decimate_mb值。如果量化后系数中只有零星的非零系数,且都是1或-1,i_decimate_mb就比较小。if(i_decimate_mb<6),可以将系数全变为0。注意,其他模式下的残差编码也用到了该处理过程。

程序后面是对色度进行处理,与亮度类似,不进行讨论。


3、static void x264_mb_analyse_inter_p16x16 ( x264_t *h, x264_mb_analysis_t *a )

直接看核心吧。

for( i_ref = 0; i_ref < h->i_ref0; i_ref++ )

    {

循环搜索搜索每个参考帧


x264_mb_predict_mv_16x16( h, 0, i_ref, m.mvp );

得到MVp,


x264_mb_predict_mv_ref16x16( h, 0, i_ref, mvc, &i_mvc );

得到邻块的MV、前一帧对应位置的MV,可用来预测搜索起点,加速运动估计。


x264_me_search_ref( h, &m, mvc, i_mvc, p_halfpel_thresh );

运动估计函数。下面将详细讨论

再接着就是一个多参考帧的中止判断,略。


4、void x264_me_search_ref( x264_t *h, x264_me_t *m, int (*mvc)[2], int i_mvc, int *p_halfpel_thresh )


bmx = pmx = x264_clip3( ( m->mvp[0] + 2 ) >> 2, mv_x_min, mv_x_max );

    bmy = pmy = x264_clip3( ( m->mvp[1] + 2 ) >> 2, mv_y_min, mv_y_max );

    bcost = COST_MAX;

    COST_MV( pmx, pmy );

    bcost -= p_cost_mvx[ bmx<<2 ] + p_cost_mvy[ bmy<<2 ];

    for( i = 0; i < i_mvc; i++ )

    {

        const int mx = x264_clip3( ( mvc[i][0] + 2 ) >> 2, mv_x_min, mv_x_max );

        const int my = x264_clip3( ( mvc[i][1] + 2 ) >> 2, mv_y_min, mv_y_max );

        if( mx != bmx || my != bmy )

            COST_MV( mx, my );

    }

    COST_MV( 0, 0 );

先检测MVp点,再检测其他预测矢量,最后检测原点(0,0)。注意mvp[0

]保留的是1/4精度,所以除以4就变成了整象素精度。


然后就是具体的搜索算法。代码不贴,直接解释吧。^_^

case 菱形搜索:用小菱形模板反复搜索。菱形算法还有大模板搜索,这里没用到。

case 六边形:先用六边形模板反复搜索,粗匹配。

            然后用小菱形模板搜索一次,得到最终的整象素运动矢量

case UMHexagonS:看我主页中的注释,呵呵,是基于JM9.5的。

case 连续消除法(SEA) 全搜索法的快速运算。这里不介绍了。


if( h->mb.i_subpel_refine >= 2 )

    {

        int hpel = subpel_iterations[h->mb.i_subpel_refine][2];

        int qpel = subpel_iterations[h->mb.i_subpel_refine][3];

        refine_subpel( h, m, hpel, qpel, p_halfpel_thresh, 0 );

    }

亚象素搜索,在x264_macroblock_analyse()函数我已经介绍过了


5、void x264_me_refine_qpel( x264_t *h, x264_me_t *m )

该函数一开始得到半象素、1/4象素搜索的次数(菱形小模板),分别为hpel、q hpel,然后调用refine_subpel(),去看看!

if( hpel_iters )

    {

        int mx = x264_clip3( m->mvp[0], h->mb.mv_min_spel[0], h->mb.mv_max_spel[0] );

        int my = x264_clip3( m->mvp[1], h->mb.mv_min_spel[1], h->mb.mv_max_spel[1] );

        if( mx != bmx || my != bmy )

            COST_MV_SAD( mx, my, -1 );

    }

检测MVp的小数精度。


for( i = hpel_iters; i > 0; i-- )

    {

        odir = bdir;

        omx = bmx;

        omy = bmy;

        COST_MV_SAD( omx, omy - 2, 0 );

        COST_MV_SAD( omx, omy + 2, 1 );

        COST_MV_SAD( omx - 2, omy, 2 );

        COST_MV_SAD( omx + 2, omy, 3 );

        if( bmx == omx && bmy == omy )

            break;

    }

对半象素精度进行hpel_iters次小菱形搜索。后面有1/4象素精度的qpel_iters次小模板搜索,略。


6、static uint8_t *get_ref( uint8_t *src[4], int i_src_stride, uint8_t *dst,    int * i_dst_stride, int mvx,int mvy, int i_width, int i_height )

该函数得到亚象素搜索时参考块的指针。

src1、src2分别指向半象素精度块。1/4搜索时需要临时插值,就是pixel_avg()函数的只能功能。

值得注意的是变量correction的作用,当作是1/4插值时的偏移量吧。N个人问过我,其实结合1/4象素插值,仔细推导一下就出来了。


7、static void x264_mb_analyse_intra( x264_t *h, x264_mb_analysis_t *a, int i_cost_inter )


依次检测Intra_16x16、Intra4x4、Intra8x8的最佳模式。

值得注意,if(subme<=1)

h->pixf.mbcmp是求sad值

else

h->pixf.mbcmp是求satd值

另外,对与Intra4x4、Intra8x8,此时就要进行真正的变换量化、反变换反量化、重建,因为要为后续的块做参考。而且,就算其系数值很小,也不能改变cbp,切记切记。

具体分析略。


8、static inline void x264_mb_analyse_transform( x264_t *h )

就是对残差进行4x4、8x8的satd变换,比较绝对和值,值较小对应的尺寸用于变换的尺寸。

你可能感兴趣的:(视频编解码)