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