广西消防协会电话:X264 中的x264_macroblock_cache_load

来源:百度文库 编辑:偶看新闻 时间:2024/05/09 05:02:21
关于拷贝模块,很复杂。
首先有x264_t结构体中的指针函数
   void (*copy[7])( uint8_t *dst, int, uint8_t *src, int, int i_height );
来自结构体x264_t中的, x264_mc_functions_t;
而这些指针函数的初始化来自
void x264_mc_init( int cpu, x264_mc_functions_t *pf )
{
    pf->copy[PIXEL_16x16] = mc_copy_w16;
    pf->copy[PIXEL_8x8]   = mc_copy_w8;
    pf->copy[PIXEL_4x4]   = mc_copy_w4;

}
mc_copy_w16定义于一个宏
#define MC_COPY(W) \
static void mc_copy_w##W( uint8_t *dst, int i_dst, uint8_t *src, int i_src, int i_height ) \
{ \
    mc_copy( src, i_src, dst, i_dst, W, i_height ); \
}
MC_COPY( 16 )
MC_COPY( 8 )
MC_COPY( 4 )

其中的W为暗含参数
这样追本溯源就可以得到
        
h->mc.copy[i?PIXEL_8x8:PIXEL_16x16]( h->mb.pic.p_fenc[i], FENC_STRIDE,
            &h->fenc->plane[i][ w * (i_mb_x + i_mb_y * i_stride) ], i_stride, w );
memcpy
&h->mb.pic.p_fdec[i][-1-FDEC_STRIDE], &plane_fdec[-1-i_stride], w*3/2+1 );

     这里为什么拷贝 w*3/2+1 个数据?h->mb.pic.p_fdec[i][-1-FDEC_STRIDE]里面应该只能拷贝1个值。

 

解答:看 memcpy() 函数,里面参数是指针。&h->mb.pic.p_fdec[i][-1-FDEC_STRIDE] 只是个首地址。对这毕书P212页,图8-22看(注意:图上只画了4*4的块,而这里操作的是16*16宏块) ,对于Y(U、V的类似)宏块需要上面1行共需要25个数据,需要左边1列共16个数据。

             h->mb.pic.p_fdec[i][-1-FDEC_STRIDE] 对应M,memcpy&h->mb.pic.p_fdec[i][-1-FDEC_STRIDE], &plane_fdec[-1-i_stride], w*3/2+1 );是取上面1行25个数据。

            for( j = 0; j < w; j++ )
                  h->mb.pic.p_fdec[i][-1+j*FDEC_STRIDE] = plane_fdec[-1+j*i_stride]; 是取左边1列16个数据

0(M)  1   2   3   4     5   6   7   8     9   10  11  12    13  14  15  16    17  18  19  20    21  22  23  24

1       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x  

2       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

3       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

4       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

 

5       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

6       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

7       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

8       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

 

9       x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

10     x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

11     x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

12     x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

 

13     x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

14     x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

15     x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 

16     x   x   x   x     x   x   x   x     x    x    x    x      x    x   x   x 



另一个问题

unsigned int i_neighbour8[4];       /* neighbours of each 8x8 or 4x4 block that are available */
unsigned int i_neighbour4[16];      /* at the time the block is coded */

......................................................................................

h->mb.i_neighbour4[0] =
h->mb.i_neighbour8[0] = (h->mb.i_neighbour & (MB_TOP|MB_LEFT|MB_TOPLEFT))
                                        | ((h->mb.i_neighbour & MB_TOP) ? MB_TOPRIGHT : 0);
h->mb.i_neighbour4[4] =
h->mb.i_neighbour4[1] = MB_LEFT

                                        | ((h->mb.i_neighbour & MB_TOP) ? (MB_TOP|MB_TOPLEFT|MB_TOPRIGHT) : 0);
h->mb.i_neighbour4[2] =
h->mb.i_neighbour4[8] =
h->mb.i_neighbour4[10] =
h->mb.i_neighbour8[2] = MB_TOP|MB_TOPRIGHT

                                         | ((h->mb.i_neighbour & MB_LEFT) ? (MB_LEFT|MB_TOPLEFT) : 0);
h->mb.i_neighbour4[3] =
h->mb.i_neighbour4[7] =
h->mb.i_neighbour4[11] =
h->mb.i_neighbour4[13] =
h->mb.i_neighbour4[15] =
h->mb.i_neighbour8[3] = MB_LEFT|MB_TOP|MB_TOPLEFT;
h->mb.i_neighbour4[5] =
h->mb.i_neighbour8[1] = MB_LEFT | (h->mb.i_neighbour & MB_TOPRIGHT)
                                        | ((h->mb.i_neighbour & MB_TOP) ? MB_TOP|MB_TOPLEFT : 0);
h->mb.i_neighbour4[6] =
h->mb.i_neighbour4[9] =
h->mb.i_neighbour4[12] =
h->mb.i_neighbour4[14] = MB_LEFT|MB_TOP|MB_TOPLEFT|MB_TOPRIGHT;

从注释看h->mb.i_neighbour8[]表示可参考的相邻块状态(这个懂,不管它),h->mb.i_neighbour4[]表示当前编码的块,那么我想h->mb.i_neighbour4[0]到h->mb.i_neighbour4[15]值应该是一样的,都表示该块有没有被编码,这里为什么不赋一样的值?

解答:这里不是表示当前编码的块,而是表示的位置信息,这个从赋值关系中可以看出来

先对照宏块里面块的编码数序看
 
0   1   4   5
 2   3   6   7
 8   9   12  13
 10  11  14  15

1h->mb.i_neighbour4[0] =
         h->mb.i_neighbour8[0] = (h->mb.i_neighbour & (MB_TOP|MB_LEFT|MB_TOPLEFT))
                                                  | ((h->mb.i_neighbour & MB_TOP) ? MB_TOPRIGHT : 0);

         0左上右上参考块都不确定.但是为什么把TOPRIGHT单独列出来呢,如上图位置关系,0块就是当TOP有效时,TOPRIGHT才有效。以下同理。

(2)h->mb.i_neighbour4[4] =
         h->mb.i_neighbour4[1] = MB_LEFT

                                              | ((h->mb.i_neighbour & MB_TOP) ? (MB_TOP|MB_TOPLEFT|MB_TOPRIGHT) : 0);

         41参考块确定,而左上右上参考块都不确定

(3)h->mb.i_neighbour4[2] =
         h->mb.i_neighbour4[8] =
         h->mb.i_neighbour4[10] =
         h->mb.i_neighbour8[2] = MB_TOP|MB_TOPRIGHT

                                                 | ((h->mb.i_neighbour & MB_LEFT) ? (MB_LEFT|MB_TOPLEFT) : 0);

        2810右上参考块确定,而左上参考块都不确定

(4)h->mb.i_neighbour4[3] =
         h->mb.i_neighbour4[7] =
         h->mb.i_neighbour4[11] =
         h->mb.i_neighbour4[13] =
         h->mb.i_neighbour4[15] =
         h->mb.i_neighbour8[3] = MB_LEFT|MB_TOP|MB_TOPLEFT;

         这里有点让我看不懂,71315好理解,71315左上参考块都确定,这里不写右上(71315右上0右上是不同的,宏块编码顺序和块的编码顺序也是一样的,0右上右上角的宏块,而71315右上右边的宏块。右边的宏块总是在左边的宏块之后编码。如下图宏块1宏块2之前编码宏块3宏块2之后编码。)

        附宏块的编码顺序(和块是一样的)

                                         0   1  
                                         2   3 

       311有点让人看不懂,311左上右上参考块都确定,为什么和71315放一起呢? 如果看下编码顺序就不难发现了,3右上4,而4是在3之后编码。所以3右上参考块是不确定的,也就将371315放在一起了。(11同理)

(5)h->mb.i_neighbour4[5] =
         h->mb.i_neighbour8[1] = MB_LEFT | (h->mb.i_neighbour & MB_TOPRIGHT)
                                                | ((h->mb.i_neighbour & MB_TOP) ? MB_TOP|MB_TOPLEFT : 0);

        5参考块确定,左上右上参考块都不确定

(6) h->mb.i_neighbour4[6] =
           h->mb.i_neighbour4[9] =
           h->mb.i_neighbour4[12] =
           h->mb.i_neighbour4[14] = MB_LEFT|MB_TOP|MB_TOPLEFT|MB_TOPRIGHT;

          691214左上右上参考块都确定


3 另一个问题

来自264乐园的兄弟“遥远”,问我

      h->mc.copy[i?PIXEL_8x8:PIXEL_16x16]( h->mb.pic.p_fenc[i], FENC_STRIDE,
                      &h->fenc->plane[i][ w * (i_mb_x + i_mb_y * i_stride) ], i_stride, w );

  (1w * (i_mb_x + i_mb_y * i_stride) 不明白为什么要w( 这只考虑i=0情况,即Y情况,U、V类似)

  (2)h->mc.copy这个函数定义在哪?

解答:

1)汗一个,刚开始我也没弄懂,真是惭愧。i_stride=240是像素点Y的步长,所以这里坐标表示的是像素点Y的位置,

(i_mb_x + i_mb_y * i_stride)是每个像素点Y位置的索引,w * (i_mb_x + i_mb_y * i_stride)是每个宏块Y的索引.

(2)根据h->mc.copy找 copy定义 ,发现带5个参数,直接搜索copy,会发现好多带6个参数,那是不对的,是其他的copy函数。这时要利用i?PIXEL_8x8:PIXEL_16x16这个信息,我找到了
    pf->copy[PIXEL_16x16] = mc_copy_w16;
    pf->copy[PIXEL_8x8]   = mc_copy_w8;
    pf->copy[PIXEL_4x4]   = mc_copy_w4;

再根据其中一个
    pf->copy[PIXEL_16x16] = mc_copy_w16;
我找到了
   #define MC_COPY( name, a )                                \
   static void name( uint8_t *src, int i_src,                \
                     uint8_t *dst, int i_dst, int i_height ) \
   {                                                         \
       int y;                                                \
       for( y = 0; y < i_height; y++ )                       \
       {                                                     \
           memcpy( dst, src, a );                            \
           src += i_src;                                     \
           dst += i_dst;                                     \
       }                                                     \
   }
   MC_COPY( mc_copy_w4,  4  )
   MC_COPY( mc_copy_w8,  8  )
   MC_COPY( mc_copy_w16, 16 )
应该就是这个了。

功能:完成将已编码数据参数和待编码数据装入到h->mb.cache中,下图是BUF中存储的数据在以MB为单位的时候的存储顺序

x264_macroblock_cache_load( h, i_mb_x, i_mb_y );//是把当前宏块的up宏块和left宏块的intra4x4_pred_mode,non_zero_count加载进来,放到一个数组里面,这个数组用来直接得到当前宏块的左侧和上面宏块的相关值.要想得到当前块的预测值,要先知道上面,左面的预测值,它的目的是替代getneighbour函数

----------------------计算各种类型宏块的位置-------------------------------

int i_mb_xy = i_mb_y * h->mb.i_mb_stride + i_mb_x;

--得到MB位置,其中h->mb.i_mb_stride为原始数据以MB为单位的宽度

int i_mb_4x4 = 4*(i_mb_y * h->mb.i_b4_stride + i_mb_x);

--得到4x4MB位置,其中h->mb.i_b4_stride为原始数据以4x4MB为单位的宽度

int i_mb_8x8 = 2*(i_mb_y * h->mb.i_b8_stride + i_mb_x);

----得到8x8MB位置,其中h->mb.i_b8_stride为原始数据以8x8MB为单位的宽度

int i_top_y = i_mb_y - (1 << h->mb.b_interlaced);

--得到当前MB上一个MB的Y坐标

int i_top_xy = i_top_y * h->mb.i_mb_stride + i_mb_x;

--得到当前MB上面一个MB的位置

int i_top_4x4 = (4*i_top_y+3) * h->mb.i_b4_stride + 4*i_mb_x;

--得到当前4x4MB上面一个4x4MB的位置

int i_top_8x8 = (2*i_top_y+1) * h->mb.i_b8_stride + 2*i_mb_x;

--得到当前8x8MB上面一个8x8MB的位置

----------------------------装入周围宏块的参数---------------------------------

    --装入TOP-MB的相关参数

       h->mb.i_mb_type_top = i_top_type= h->mb.type[i_top_xy];

--得到TOP-MB的类型

        h->mb.cache.i_cbp_top = h->mb.cbp[i_top_xy];

--得到TOP-MB的CBP

        h->mb.i_neighbour |= MB_TOP;

--标注当前MB的TOP-MB可用

MEM4(&h->mb.cache.intra4x4_pred_mode[x264_scan8[0] - 8]) = MEM4(&h->mb.intra4x4_pred_mode[i_top_xy][0]);

--装入TOP-MB临近当前MB的4个4X4子块的预测模式

        MEM4(&h->mb.cache.non_zero_count[x264_scan8[0] - 8]) = MEM4(&h->mb.non_zero_count[i_top_xy][12]);

--装入TOP-MB临近当前MB的nzc编码值

    }

    else

    {--没有有效的TOP-MB,则相关参数设置为不可用

       h->mb.i_mb_type_top = -1;

        h->mb.cache.i_cbp_top = -1;

        /* load intra4x4 */

        h->mb.cache.intra4x4_pred_mode[x264_scan8[0] - 8] =

        h->mb.cache.intra4x4_pred_mode[x264_scan8[1] - 8] =

        h->mb.cache.intra4x4_pred_mode[x264_scan8[4] - 8] =

        h->mb.cache.intra4x4_pred_mode[x264_scan8[5] - 8] = -1;

        /* load non_zero_count */

        h->mb.cache.non_zero_count[x264_scan8[0] - 8] =

        h->mb.cache.non_zero_count[x264_scan8[1] - 8] =

        h->mb.cache.non_zero_count[x264_scan8[4] - 8] =

        h->mb.cache.non_zero_count[x264_scan8[5] - 8] =

        h->mb.cache.non_zero_count[x264_scan8[16+0] - 8] =

        h->mb.cache.non_zero_count[x264_scan8[16+1] - 8] =

        h->mb.cache.non_zero_count[x264_scan8[16+4+0] - 8] =

        h->mb.cache.non_zero_count[x264_scan8[16+4+1] - 8] = 0x80;

    }

--装入LEFT-MB相关参数

--装入MB_TOPRIGHT相邻MB的信

--装入MB_TOPLEFT相邻MB的信

--------------------------装入编码图像块和参考图像数据----------------------------------

--搬移重建数据到参考数据位置,见《宏块的原始编码数据和重建数据的存储结构》中的说明

行方向的参考数据未搬移

--调用x264_macroblock_load_pic_pointers()函数

完成以下功能:

1    设置象素分量行宽度,指扩展之后的宽度

2   从h->fenc->plane[i][i_pix_offset]中将原始数据读到h->mb.pic.p_fenc[i]中

3、   将行参考数据搬移到h->mb.pic.p_fdec[i]数组中,在数组中的偏移量将《宏块的原始编码数据和重建数据的存储结构》中的说明

4、   从参考图像中得到对应于当前MB的参考宏块数据指针,存储到h->mb.pic.p_fref[0][j][k]中,其中0表示为前向参考图像,J表示是第几个参考图像,K表示为不同的象素分量,推测[0]->Y,[1/2/3]为滤波后的,[4]->cr,[5]->cb;

------------------------运动向量、参考图像装入---------