x264源代码分析(四)
时间:2006-07-17 来源:lzhw_lucky
c) 到这里还没完,我们接着看 void x264_macroblock_encode( x264_t *h ){ …….前面省略. 执行到下面这条语句,看看下面是干啥的.
/* encode chroma */ i_qscale = i_chroma_qp_table[x264_clip3( i_qscale + h->pps->i_chroma_qp_index_offset, 0, 51 )]; if( IS_INTRA( h->mb.i_type ) ) { const int i_mode = h->mb.i_chroma_pred_mode; /* do the right prediction */ h->predict_8x8[i_mode]( h->mb.pic.p_fdec[1], h->mb.pic.i_fdec[1] ); h->predict_8x8[i_mode]( h->mb.pic.p_fdec[2], h->mb.pic.i_fdec[2] );
/* fix the pred mode value */ h->mb.i_chroma_pred_mode = x264_mb_pred_mode8x8_fix[i_mode]; }
/* encode the 8x8 blocks */ x264_mb_encode_8x8( h, !IS_INTRA( h->mb.i_type ), i_qscale );//对色度块进行编码了. 到这我们可以看到原来我们在这前面是对宏块中的亮度系数进行了编码,我们到上面那个函数才开始对色度系数进行编码.进入x264_mb_encode_8x8()函数看到for循环里面有个2可以证明是对2个色度系数进行编码,想法没错. 那下面这些又是干啥的呢?它们是计算cbp系数看需要对残差(包括ac,dc)中的哪个系数进行传输的.
/* Calculate the Luma/Chroma patern and non_zero_count */ if( h->mb.i_type == I_16x16 ) { h->mb.i_cbp_luma = 0x00; for( i = 0; i < 16; i++ ) { const int nz = array_non_zero_count( h->dct.block[i].residual_ac, 15 ); h->mb.cache.non_zero_count[x264_scan8[i]] = nz; if( nz > 0 ) { h->mb.i_cbp_luma = 0x0f; } } } else { h->mb.i_cbp_luma = 0x00; for( i = 0; i < 16; i++ ) { const int nz = array_non_zero_count( h->dct.block[i].luma4x4, 16 );//统计非0个数. h->mb.cache.non_zero_count[x264_scan8[i]] = nz; if( nz > 0 ) { h->mb.i_cbp_luma |= 1 << (i/4);// %16的意义. } } }
/* Calculate the chroma patern *///色度的cbp有3种方式. h->mb.i_cbp_chroma = 0x00; for( i = 0; i < 8; i++ ) { const int nz = array_non_zero_count( h->dct.block[16+i].residual_ac, 15 ); h->mb.cache.non_zero_count[x264_scan8[16+i]] = nz; if( nz > 0 ) { h->mb.i_cbp_chroma = 0x02; /* dc+ac (we can't do only ac) */ } } if( h->mb.i_cbp_chroma == 0x00 && ( array_non_zero_count( h->dct.chroma_dc[0], 4 ) > 0 || array_non_zero_count( h->dct.chroma_dc[1], 4 ) ) > 0 ) { h->mb.i_cbp_chroma = 0x01; /* dc only */ }
if( h->param.b_cabac ) { if( h->mb.i_type == I_16x16 && array_non_zero_count( h->dct.luma16x16_dc, 16 ) > 0 ) i_cbp_dc = 0x01; else i_cbp_dc = 0x00;
if( array_non_zero_count( h->dct.chroma_dc[0], 4 ) > 0 ) i_cbp_dc |= 0x02; if( array_non_zero_count( h->dct.chroma_dc[1], 4 ) > 0 ) i_cbp_dc |= 0x04; }
/* store cbp */ h->mb.cbp[h->mb.i_mb_xy] = (i_cbp_dc << 8) | (h->mb.i_cbp_chroma << 4) | h->mb.i_cbp_luma;
到这,基本上x264_macroblock_encode( h )(定义在Enc/encoder.c)基本上就分析完了.剩下的就是熵编码的部分了.以后的部分更需要的应该是耐心和数学知识吧,相对前面来说应该简单些.
l 总结: 1. 我对代码的理解应该还算比较深入,把代码的主线已经分析了出来,对代码中几个最难理解的地方(最难理解的地方就是帧的类型的判定,参考帧是如何管理的,一个16*16的块是采用到底需不需要分割,分割的话分成什么大小的,子块又采用何种预测方式,这些实际上就是整个编码的主线.)基本上已经明白,但有些过分复杂的函数的实现(或者涉及数学知识较多的地方)还有待深入研究,但我相信沿着这条主线应该能够继续深入下去,自己需要的是更多的时间和耐心. 自己需要的是更多的时间和耐心,争取以后能写出更详细更准确的流程分析,并尽量思考能改进的地方. 2.层次性,就像网络的7层结构一样,每一帧图像也可以分成很多层,只有对每层的语法结构(具体来说就是各个结构体中变量的意思)有了很好的理解,才有可能真正认清代码,这需要对标准认真研习.比如说量化参数,就在3个地方有定义,不读标准根本不会明白意思. 3. 很多过分复杂的东西不容易在本文中表达出来(比如说预测部分),只有通过自己的钻研才能真正悟到,直觉也很重要,还有就是信心了.看这种程序的收获就好像是真地肉眼看到了原子那样. 4.由于代码过分复杂,对某些函数的实现过程还没能彻底理解,比如说x264_macroblock_cache_load()函数的具体实现过程,我只是知道它的功能,实现过程还有待认真理解.dct变换是如何实现的,是如何计算残差的等等,这些都需要很多功夫,当然这里也需要大家的共同学习和交流.实现分工阅读不同代码部分并进行交流,才有可能对代码做到彻底的理解. |