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,表示码流已经全部写入,程序终止。
注:该函数是采用一个while循环,一次写入一个位,循环i_count次将i_bits的前i_count位写入s中。
2)static inline void bs_write1( bs_t *s, uint32_t i_bits )
该函数的作用是:将i_bits流的后1位写入s。
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码流。
函数流程:
首先判断s->p是否大于s->p_end,若是则终止,否则,可以写入码流。
if( i_bits &0x01 )是用来判断当前要写入的i_bits位是0还是1,如果当前要写入的是1,则选择*s->p |= 1 <<( s->i_left-1)来把这个1写入码流;如果当前要写入的是0,则选择 *s->p &= ~( 1 << (s->i_left-1) )来把这个0写入码流。
写完一位码流后,初始的i_left被新写入的bit占了一位,所以i_left的值-1.
这时判断I_left是否等于0,如果i_left==0时,表示当前存储单元已被写满,所以s->p指针指向下一个存储单元,i_left更新为8。这时再进入下一循环。
注:上面两个write函数是从网上找的,一次写入一个位数,先判断要写入的位是0或1,再直接写入0或1,比较繁琐但是便于理解,故此附上以供参考。以下介绍的都是来自x264中的源码。
3)static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits )
该函数的作用是:向s里写入i_bits流的后i_count位。
static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits )
{
if( s->p >= s->p_end - 4 )
return;
while( i_count > 0 )
{
if( i_count < 32 )
i_bits &= (1<<i_count)-1;
if( i_count < s->i_left )
{
*s->p = (*s->p << i_count) | i_bits;
s->i_left -= i_count;
break;
}
else
{
*s->p = (*s->p << s->i_left) | (i_bits >> (i_count - s->i_left));
i_count -= s->i_left;
s->p++;
s->i_left = 8;
}
}
}
流程简介:
首先判断是否有空闲的存储空间供新的码流写入,若剩余不足4个字节的空间,则认为空间不够,直接返回(uint32_t是32位,要4个字节的存储空间); i_count是否大于0,如果是,则可以写入码流。这个条件是在判断是否该开始或继续写入码流。
若可以写码流,若i_count<32,则i_bits &= (1<<i_count)-1,将不需要写入的位置0。注意,该函数写i_bits不是逐位写入的。
如果要写入的位数i_count < 本字节空余的位数s->i_left,则一次性将i_count位写入: *s->p = (*s->p << i_count) | i_bits且s->i_left -= i_count,然后完成写入break;
否则,则先写入i_left位:*s->p = (*s->p << s->i_left) | (i_bits >> (i_count - s->i_left)),i_count -= s->i_left(要写入的数减少),s->p++(进入s的下一个字节),i_left重新置位为8。
while循环直至写入i_count位。
注:该函数同1)小节中介绍的函数作用一样,不过效率要高。后面函数流程就不再详细介绍了,只说明大概思路。
4)static inline void bs_write1( bs_t *s, uint32_t i_bit )
该函数的作用是:将i_bits流的后1位写入s。
static inline void bs_write1( bs_t *s, uint32_t i_bit )
{
if( s->p < s->p_end )
{
*s->p <<= 1;
*s->p |= i_bit;
s->i_left--;
if( s->i_left == 0 )
{
s->p++;
s->i_left = 8;
}
}
}
思路:直接写入*s->p <<= 1;
*s->p |= i_bit;
2 读出码流函数bs_read
1)static inline uint32_t bs_read( bs_t *s, int i_count )
该函数的作用是:从s中读出i_count位,并将其做为uint32_t类型返回。
static inline uint32_t bs_read( bs_t *s, int i_count )
{
static uint32_t i_mask[33] ={0x00,
0x01, 0x03, 0x07, 0x0f,
0x1f, 0x3f, 0x7f, 0xff,
0x1ff, 0x3ff, 0x7ff, 0xfff,
0x1fff, 0x3fff, 0x7fff, 0xffff,
0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
0x1fffff, 0x3fffff, 0x7fffff, 0xffffff,
0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};
int i_shr;
uint32_t i_result = 0;
while( i_count > 0 )
{
if( s->p >= s->p_end )
{
break;
}
if( ( i_shr = s->i_left - i_count ) >= 0 )
{
/* more in the buffer than requested */
i_result |= ( *s->p >> i_shr )&i_mask[i_count];
s->i_left -= i_count;
if( s->i_left == 0 )
{
s->p++;
s->i_left = 8;
}
return( i_result );
}
else
{
/* less in the buffer than requested */
i_result |= (*s->p&i_mask[s->i_left]) << -i_shr;
i_count -= s->i_left;
s->p++;
s->i_left = 8;
}
}
return( i_result );
}
思路:若i_count>0且s流并未结束,则开始或继续读取码流;
若s当前字节中剩余位数大于等于要读取的位数i_count,则直接读取;
若s当前字节中剩余位数小于要读取的位数i_count,则读取剩余位,进入s下一字节继续读取。
注:写入s时,i_left表示s当前字节还没被写入的位,若一个新的字节,则i_left=8;
读取s时,i_left表示s当前字节还没被读取的位,若一个新的字节,则i_left=8。
注意两者的区别和联系。
2)static inline uint32_t bs_read1( bs_t *s )
该函数的作用是:从s中读出1位,并将其做为uint32_t类型返回。
static inline uint32_t bs_read1( bs_t *s )
{
if( s->p < s->p_end )
{
unsigned int i_result;
s->i_left--;
i_result = ( *s->p >> s->i_left )&0x01;
if( s->i_left == 0 )
{
s->p++;
s->i_left = 8;
}
return i_result;
}
return 0;
}
思路:若s流并未结束,则读取一位。
3 指数哥伦布编码ue(v)
1)static inline void bs_write_ue( bs_t *s, unsigned int val )
该函数的作用是:将val以哥伦布编码ue(v)方式编码并写入s中。
static inline void bs_write_ue( bs_t *s, unsigned int val )
{
int i_size = 0;
static const int i_size0_255[256] =
{
1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
};
if( val == 0 )
{
bs_write1( s, 1 );
}
else
{
unsigned int tmp = ++val;
if( tmp >= 0x00010000 )//2^16=256*256
{
i_size += 16;
tmp >>= 16;
}
if( tmp >= 0x100 )//2^8=256
{
i_size += 8;
tmp >>= 8;
}
i_size += i_size0_255[tmp];
bs_write( s, 2 * i_size - 1, val );
}
}
思路:当val=0时,写入1;
当val!=0时:该函数主要是通过i_size0_255[256]表计算出一个i_size,然后写入val的2*i_size-1位,注意
tmp=++val,实际写入的val比传入的val大1。
例:when val=9 ,val!=0, so tmp=10;
tmp<0x00010000 and tmp<0x100, so i_size=0+i_size0_255[10]=4;
tmp=10=00001010 and 2*i_size-1=7 ,so bs_write(s,7,00001010);
由上所述:当val=9时,写入s的字串为:0001010
2)static inline int bs_read_ue( bs_t *s )
该函数的作用是:从s中解码并读出一个语法元素值。
static inline int bs_read_ue( bs_t *s )
{
int i = 0;
while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )
{
i++;
}
return( ( 1 << i) - 1 + bs_read( s, i ) );
}
思路:从s的当前位读取并计数,直至读取到1为止;
while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )这个循环用i记录了s当前位置到1为止的0的个数,并丢弃读到的第一个1;
返回2^i-1+bs_read(s,i)。
例:当s字节中存放的是0001010时,1前有3个0,所以i=3;
返回的是:2^i-1+bs_read(s,i)即:8-1+010=9
3)static inline int bs_size_ue( unsigned int val )
该函数的作用是:计算val经过ue(v)编码后所对应的位。
static inline int bs_size_ue( unsigned int val )
{
static const int i_size0_254[255] =
{
1, 3, 3, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
11,11,11,11,11,11,11,11,11,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
13,13,13,13,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
};
if( val < 255 )
{
return i_size0_254[val];
}
else
{
int i_size = 0;
val++;
if( val >= 0x10000 )
{
i_size += 32;
val = (val >> 16) - 1;
}
if( val >= 0x100 )
{
i_size += 16;
val = (val >> 8) - 1;
}
return i_size0_254[val] + i_size;
}
}
思路:其实就是计算bs_write_ue( bs_t *s, unsigned int val )函数中的2*i_size-1的值。
4 指数哥伦布编码se(v)
1)static inline void bs_write_se( bs_t *s, int val )
该函数的作用是:通过ue(v)编码实现se(v)编码的写入。
static inline void bs_write_se( bs_t *s, int val )
{
bs_write_ue( s, val <= 0 ? -val * 2 : val * 2 - 1);
}
思路:se(v)表示有符号指数哥伦布编码,当val<=0时,codenum=-val*2;否则,codenum=val*2-1。
然后,bs_write_ue(s,codenum);
注:在标准中的codenum只是一个假设的中间值,就像在函数中设置的一个变量一样,val才是要得到的语法元素值。
在ue(v)编码中,val=codenum,标准中也这样描述:“如果语法元素编码为ue(v),语法元素值等于codeNum。”
在se(v)编码中,val与codenum的关系如下:当val<=0时,codenum=-val*2;否则,codenum=val*2-1(参看标准9.1.1小节中的表9-3)。
2)static inline int bs_read_se( bs_t *s )
该函数的作用是:通过ue(v)编码实现se(v)编码的读取。
static inline int bs_read_se( bs_t *s )
{
int val = bs_read_ue( s );
return val&0x01 ? (val+1)/2 : -(val/2);
}
思路:直接bs_read_ue( s )读取出来的实际上是codenum的值,因为“当val<=0时,codenum=-val*2;否则,codenum=val*2-1。
”,所以当codenum为奇数即 codenum&0x01>0 时,val=(codenum+1)/2,否则val=-(codenum/2)。
3)static inline int bs_size_se( int val )
该函数的作用是:通过ue(v)编码计算位数的方式实现se(v)编码的位数计算。
static inline int bs_size_se( int val )
{
return bs_size_ue( val <= 0 ? -val * 2 : val * 2 - 1);
}
注:原理同ue(v)。
5 指数哥伦布编码te(v)
1)static inline void bs_write_te( bs_t *s, int x, int val )
该函数的作用是:通过ue(v)编码实现te(v)编码的写入。
static inline void bs_write_te( bs_t *s, int x, int val )
{
if( x == 1 )
{
bs_write1( s, 1&~val );
}
else if( x > 1 )
{
bs_write_ue( s, val );
}
}
思路:当x=1时,将val最后一位的取反,然后写入;
当x>1时,编码方式同ue(v)。
注:x表示语法元素的范围。注意参考标准中关于te(v)与ue(v)关系的描述。
2)static inline int bs_read_te( bs_t *s, int x )
该函数的作用是:通过ue(v)编码实现te(v)编码的读取。
static inline int bs_read_te( bs_t *s, int x )
{
if( x == 1 )
{
return 1 - bs_read1( s );
}
else if( x > 1 )
{
return bs_read_ue( s );
}
return 0;
}
思路:当x=1时,直接读出一位,然后取反;
当x>1时,读取方式同ue(v)。
3)static inline int bs_size_te( int x, int val )
该函数的作用是:通过ue(v)编码计算位数的方式实现te(v)编码的位数计算。
static inline int bs_size_te( int x, int val )
{
if( x == 1 )
{
return 1;
}
else if( x > 1 )
{
return bs_size_ue( val );
}
return 0;
}
思路:当x=1时,直接为1;
当x>1时,同ue(v)。