AVS3比特流结构体的理解

概述

首先,在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).
  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值