写参数集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 … …