概述
首先,在AVS3的代码中,编码器和解码器用的不同的比特流结构
- 编码器比特流结构体: COM_BSW=_COM_BSW(BitStream Writer)
- 解码器比特流结构体: COM_BSR=_COM_BSR(BitStream Reader)
AVS3中编码器比特流结构体的定义
struct _COM_BSW
{
/* buffer */
u32 code;
/* bits left in buffer */
int leftbits;
/*! address of current writing position */
u8 * cur;
/*! address of bitstream buffer end */
u8 * end;
/*! address of bitstream buffer begin */
u8 * beg;
/*! address of temporal buffer for demulation begin */
u8 * buftmp;
/*! size of bitstream buffer in byte */
int size;
/*! address of function for flush */
COM_BSW_FN_FLUSH fn_flush;
/*! arbitrary data, if needs */
int ndata[4];
/*! arbitrary address, if needs */
void * pdata[4];
};
对于这个结构体,其理解方式如下:
Function: 编码器的比特流结构体, Bitstream structure for encoder
理解: 比特流用3个u8类型的指针来表示,分别是: beg, cur, end. 其中beg表示比特流的起始地址,end表示比特流的终止地址, cur表示比特流当前的“位流指针”所指向的地址(i.e. 下一次要写入的地址);
size表示比特流总共的字节大小(因此,end=beg+size-1)
除了比特流以外,还需要维护一个总共空间为32位的buffer, buffer的内容用u32类型的code来表示, leftbits表示buffer中剩余可写入的比特位数, code中的高leftbits位是buffer中的有效内容,且buffer中的最高有效位是比特流中的低位(i.e. 优先向比特流中写入buffer中的高位)
e.g. code=0xFFFF FFF0, leftbits=4, 则buffer=[1111 1111 1111 1111 1111 1111 1111], 其中code的高28(=32-4)位是有效buffer位.
使用方法: 你想要写比特流,就先要写入buffer, 然后等时机合适,再用fn_flush将buffer的内容写入到比特流中(i.e 更新位流指针cur的位置)
比特流结构体的初始化
void com_bsw_init(COM_BSW * bs, u8 * buf, u8 * buftmp, int size, COM_BSW_FN_FLUSH fn_flush)
{
bs->size = size;
bs->beg = buf;
bs->cur = buf;
bs->buftmp = buftmp;
bs->end = buf + size - 1;
bs->code = 0;
bs->leftbits = 32;
bs->fn_flush = (fn_flush == NULL ? com_bsw_flush : fn_flush); //* bs->fn_flush = com_bsw_flush
}
Function: 初始化比特流对象bs,用参数buf,size,fn_flush初始化bs,将fn_flush赋值为com_bsw_flush这个函数,bitstream reader initialization
Logic: 将bs中的size、cur、beg、end、code等信息都赋值为传入的参数值(e.g. buf, size, fn_flush)
并将bs中的缓冲区buffer置成0(空), leftbits置成32(当前buffer中没有内容,剩余可写入的内容有32位)
com_bsw_flush函数
com_bsw_flush函数(fn_flush函数)的定义如下:
/* number of bytes to be sunk */
#define COM_BSW_GET_SINK_BYTE(bs) ((32 - (bs)->leftbits + 7) >> 3)
static int com_bsw_flush(COM_BSW * bs)
{
int bytes = COM_BSW_GET_SINK_BYTE(bs);
while(bytes--)
{
*bs->cur++ = (bs->code >> 24) & 0xFF;
bs->code <<= 8;
}
return 0;
}
Function: 将比特流结构体对象bs中的buffer内容写入到bs中的比特流中。
执行逻辑
首先用COM_BSW_GET_SINK_BYTE这个函数计算出当前buffer中有几个字节(8 bit),假设为bytes个字节。
然后循环bytes次,依次将每一个字节的内容写到比特流中(优先写入code中的高字节数据)
e.g. code=0x1111 1110, leftbits = 4, 则buffer=[1111 1111 1111 1111 1111 1111 1111 0000],则bytes=4
循环执行一次,(bs->code>>24 & 0xFF)等价于 取出 bs->code中的高8位
让 ∗ * ∗ (bs->cur++) = bs->code中的高8位(0XFF)
然后bs->code<<=8, 此时bs->code = 0xFF FFF000.
循环执行第二次,让 ∗ * ∗(bs->cur++) = bs->code中的高8位(0XFF)
然后bs->code<<=8, 此时bs->code = 0xFFF00000.
循环执行第三次,让 ∗ * ∗(bs->cur++) = bs->code中的高8位(0XFF)
然后bs->code<<=8, 此时bs->code = 0xF0000000.
循环执行第四次,让 ∗ * ∗ (bs->cur++) = bs->code中的高8位(0XF0)
然后bs->code<<=8, 此时bs->code = 0x00000000.
结束这个循环,这个函数向比特流中写入了: FF FF FF F0
注意: 执行fn_flush 并不会将buffer中的内容清空!
向比特流写入数据
首先,向比特流写入数据有以下几个常用的函数:
- com_bsw_write1: 向比特流写入1位数据
- com_bsw_write: 向比特流写入一个长度为len位的数据
com_bsw_write1函数
com_bsw_write1函数是用于向比特流写入1位数据
int com_bsw_write1(COM_BSW * bs, int val)
{
com_assert(bs);
bs->leftbits--;
bs->code |= ((val & 0x1) << bs->leftbits);
if(bs->leftbits == 0)
{
com_assert_rv(bs->cur <= bs->end, -1);
bs->fn_flush(bs);
bs->code = 0;
bs->leftbits = 32;
}
return 0;
}
参数解读: bs 比特流结构体对象,val表示你要向比特流写入的1位数据(理论上val应该是0或者1,但是如果你传入大于1的数字,也只会向比特流写入最低位的那个数据)
因此,以下我们不妨假设val是一位数据(0或者1)
code中有效的位数是(32-bs->leftbits),i.e. code的高(32-bs->leftbits)是有效的,其余的低bs->leftbits位是无效的.
现在要向buffer里面写入val,那么应该写入的位置就是第(bs->leftbits-1)位(下标从0开始计数)
首先将bs->leftbits 减一,因为你向buffer里面写入了一位数据,buffer的剩余空闲位置自然就减少1位, 然后让 code|= (val << bs->leftbits) 更新buffer.
如果此时buffer满了(bs->leftbits == 0), 那么就调用bs->fn_flush(bs), 将buffer的内容写到bs的比特流中, 然后再清空buffer(bs->code = 0; bs->leftbits = 32;)
com_bsw_write函数
com_bsw_write函数是用于向比特流写入一个长度为len位的数字val
int com_bsw_write(COM_BSW * bs, u32 val, int len) /* len(1 ~ 32) */
{
int leftbits;
com_assert(bs);
leftbits = bs->leftbits;
val <<= (32 - len);
bs->code |= (val >> (32 - leftbits));
if(len < leftbits)
{
bs->leftbits -= len;
}
else
{
com_assert_rv(bs->cur + 4 <= bs->end, -1);
bs->leftbits = 0;
bs->fn_flush(bs);
#if defined(X86F)
/* on X86 machine, shift operation works properly when the value of the
right operand is less than the number of bits of the left operand. */
bs->code = (leftbits < 32 ? val << leftbits : 0);
#else
bs->code = (val << leftbits);
#endif
bs->leftbits = 32 - (len - leftbits);
}
return 0;
}
参数解读: bs 比特流结构体对象,val表示你要向比特流写入的len位数据, len表示写入数据的位数
code中有效的位数是(32-bs->leftbits),i.e. code的高(32-bs->leftbits)是有效的,其余的低bs->leftbits位是无效的.
执行逻辑:
现在要向buffer里面写入val,那么应该写入的位置就是第(bs->leftbits-1)~(bs->leftbits-len)位
代码中val <<= (32 - len), (val >> (32 - leftbits)) 就相当于令val左移 (leftbits - len)位(并且把bs->leftbits位以及更高的位给直接“去掉”/“置零”了), 因此,此时val有效的位数就是第(bs->leftbits-1)~(bs->leftbits-len)位。
用bs->code |= val 来更新buffer.
以上就是主要的执行逻辑。
接下来再考虑一些边界情况,i.e. 如果写入数据后,超出buffer的大小怎么办
- 如果len< bs->leftbits, 此时写入数据后并没有超出buffer的空间,因此此时无需额外处理
- 如果len>= bs->leftbits, 此时写入数据后超出buffer的空间,多出来(len - leftbits) 位,此时需要额外处理。
额外处理: 首先用fn_flush将buffer中的所有内容全都写入到比特流中,然后将多出来的(len - leftbits) 位, 写入到buffer中, 令bs->leftbits= 32- (len - leftbits).