x264源码阅读笔记2

    技术2022-05-11  61

    写参数集x264_sps_write()和x264_pps_write()以及其中基本的bs_write()的过程。

    挺有意思,挺巧妙的。他们就是负责码流写入的过程,这个不同于写字节,直接COPY内存,用C语言实现对位的操作真的显得比较笨拙,但是这里代码还是很巧妙的。

    说基本的,static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits )这个函数的作用就是,向s里写入i_bits流的前i_count位,s当然是以字节为单位了,所以够8个位就写下个,哎呀太麻烦了,引别人写的把,不知道他这个是什么时候版本,但是大概意思差不多。酬和看。

     

    函数bs_write static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits ) {     while( i_count > 0 )     {         if( s->p >= s->p_end )         {             break;         }         i_count--;         if( ( i_bits >> i_count )&0x01 )         {             *s->p |= 1 << ( s->i_left - 1 );         }         else         {             *s->p &= ~( 1 << ( s->i_left - 1 ) );         }         s->i_left--;         if( s->i_left == 0 )         {             s->p++;             s->i_left = 8;         }     } } 函数功能: i_count 是循环的次数,i_bits是要编码的数,i_left是当前空闲码流的位数。 i_bits 编为 i_count 位的码流 ,每次循环,I_count和I_left都会减1,I_count和I_left并不一定相等。 当i_left==0时,s->p指针指向下一个码流单元,i_left更新为8。   函数流程 首先判断I_count是否大于0,如果是,则判断s->p是否大于s->p_end,若是则终止,否则,可以写入码流。这个条件是在判断是否有空闲的存储空间供新的码流写入。 若可以写码流,则I_count--,表明可以进行写一位的操作。注意,写I_bits是逐位写入的。 if ( ( i_bits >> i_count )&0x01 ) 是用来判断当前要写入的I_bits位是0还是1,从而选择不同的算法来写入这个码流。如果当前要写入的是0,则选择*s->p &= ~( 1 << ( s->i_left - 1 ) )来把这个0写入码流;如果当前要写入的是1,则选择*s->p |= 1 << ( s->i_left - 1 )来把这个1写入码流。    写完一位码流后,初始的i_left被新写入的bit占了一位,所以i_left的值-1.    这时判断I_left是否等于0,如果I_left还大于0,表示当前的存储单元中还有剩余的空间供码流写入,则直接进入下一次循环。如果I_left==0时,表示当前存储单元已被写满,所以s->p指针指向下一个存储单元,I_left更新为8。这时再进入下一循环。    在进入下一循环的时候,先判断I_count的值,如果非零,程序继续;如果为0,表示码流已经全部写入,程序终止。   关键语句分析 if ( ( i_bits >> i_count )&0x01 )   定位要写入的I_bits的位,比如I_bits=d(66)=b(0100 0010),I_count=8,首先I_count--,那么I_bits右移7位后就是0,而它与0x01(也就是0000 0001)位与后的值是0,这就确定了要写入的码流是0。再比如I_count=6,则I_bits右移6位后是01,而01和0x01位与后得到的是1,这就表明要写入的码流是1。   *s->p &= ~( 1 << ( s->i_left-1) ) 此算式可以将0写入码流。比如*s->p=1100 1101,I_bits=d(66)=b(0100 0010),I_count=7,I_left=8。 ~(1<<s->I_left-1)= 0 111 1111, 1 100 1101& 0 111 1111 = 0 100 1101 ,这样就把0写入了存储空间,得到码流 0 100 1101 。然后I_left=7   *s->p |= 1 << ( s->i_left - 1 ); 此算式可以将1写入码流。比如*s->p=1100 1101,I_bits=d(66)=b(0100 0010),I_count=6,I_left=7。 1<<(s->I_left-1)0 1 00 0000  0 1 00 1101 | 0 1 00 0000 = 0 1 00 1101 , 这样就把1写入了存储空间,得到码流0100 1101,然后I_left=6.       函数bs_write1 static inline void bs_write1( bs_t *s, uint32_t i_bits ) {     if( s->p < s->p_end )     {            if( i_bits&0x01 )         {             *s->p |= 1 <<( s->i_left-1);         }         else         {             *s->p &= ~( 1 << (s->i_left-1) );         }          s->i_left--;         if( s->i_left == 0 )         {             s->p++;             s->i_left = 8;         }     } }   bs_write1 ()相当于bs_write(bs_t *s,1, uint32_t i_bits) 就是只写入1 bit码流。  

    地址 码流值 十进制值 ASCII 码值 i_bits i_left 0x00890058 11001101 205     8 0x00890058 0 1001101 77 M 0 7 0x00890058 0 0 001101 13 0 6 0x00890058 00 1 01101 45 - 1 5

      可以看到,存储单元的初值为 11011101 , 也就是0xCD,这是VC默认的初始化值。 我们首先要写入的是 0 ,那么只需要把0放入 1 1001101 的最第一位(从左向右数),替换原来的1即可,变为 0 1001101 ,它的十进制值就是77;下一次仍要写入 0 ,则把0放到0 1 001101 的第二位,变为 00 001101 ;最后要写入 1 ,则把1放到00 0 01101 的第三位,变为 001 01101 。就这样依次逐位写入码流,直到I_left=0为止。 下面是哥伦布编码流   bs_write_ue( s, sps->i_id ); 这里ue,是在语法中定义为指数哥伦布码的编码方法,其具体方法见规范第9章。应是一种变长的编码!简举例,0阶指数哥化布编码: Bit string form Range of codeNum 1 0 0 1 x0 1-2 0 0 1 x1 x0 3-6 0 0 0 1 x2 x1 x0 7-14 0 0 0 0 1 x3 x2 x1 x0 15-30 0 0 0 0 0 1 x4 x3 x2 x1 x0 31-62

    最新回复(0)