VLC一--零阶指数哥伦布码解码原理和实现

指数哥伦布编码属于变长编码,其基本原理是用短码字表示出现频率较高的信息,用长码字表示出现频率较低的信息。     

1.1.  指数哥伦布编解码原理

指数哥伦布编码也是变长编码的一种,指数哥伦布编码也是由前缀和后缀组成。K阶指数哥伦布码的组成如图1(a)所示:分为m个前缀0,一个比特1和m+k个后缀。解析时首先从比特流当前位置开始寻找第一个非零比特,并将找到的0比特个数记为m,第一个非零比特之后的m+k个二进制串的十进制值记为Value,如图1(b)所示。由于k阶指数哥伦布码中有一步是:去掉最低的k个比特,之后加1,然后将最低k比特恢复,相当于Value的值中包含了一个额外添加的2^k;同时,在进行码流解析时,m个前导0之后的第一个非零比特没有被计算在Value值内。

 

     

 (a) 指数哥伦布解析形式               (b) 指数哥伦布值的计算

图1. 指数哥伦布编码

因此解码值CodeNum的计算方式如下:

CodeNum = 2^(m+k) – 2^(k) + Value

 

在H264/AVC和HEVC在参数集的变长编码中常用的是0阶指数哥伦布编码,分为无符号0阶哥伦布指数编码和有符号数0级哥伦布指数编码,如表1-1所示,其中CodeNum表示解码值,有符号所对应的列表示无符号编码是所对应的值,有符号表示采用有符号0阶指数哥伦布编码时所对应的十进制数。

表1-1 0阶有符号和无符号指数哥伦布编码

码字

CodeNum

无符号

有符号

1

0

0

0

010

1

1

1

011

2

2

-1

00100

3

3

2

00101

4

4

-2

00110

5

5

3

00111

6

6

-3

1.2.  零阶指数哥伦布解码标准实现

零阶指数哥伦布解码时,k=0,所以标准中的实现如下图2所示。从中可以看出,标准中的实现方法是采用一个比特一个比特地进行操作。

leadingZeroBits = .1

for( b = 0; !b; leadingZeroBits++ )

b = read_bits( 1 )

codeNum = 2leadingZeroBits -1 + read_bits( leadingZeroBits )

图2. 标准零阶指数哥伦布解码实现

1.3.  零阶指数哥伦布解码FFmpeg实现

在FFmpeg中采用了查表和计算相结合的方法,对码长不超过9比特的码字制作了ff_golomb_vlc_len和ff_ue_golomb_vlc_code查找表计算码长和码字,如图3和4所示,否则通过计算的方式得出码长和码字。

const uint8_t ff_golomb_vlc_len[512]={

19,17,15,15,13,13,13,13,11,11,11,11,11,11,11,11,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,

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,

5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,

5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,

3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,

3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,

3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,

3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};

图3. ff_golomb_vlc_len码长查找表

const uint8_t ff_ue_golomb_vlc_code[512]={

32,32,32,32,32,32,32,32,31,32,32,32,32,32,32,32,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,

 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,14,

 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 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,

 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

图4. ff_ue_golomb_vlc_code码字查找表

 

       当码长不超过9比特时,可以分为如下几种情况:

1)       00001xxxx,码长9bit,前缀4比特,一个比特1和4位后缀,根据公式CodeNum = 2^(m+k) – 2^(k) + Value,其中k=0,m=4,也就是CodeNum = 2^m– 1 + Value=15+Value可知,解码结果为[15,30]。二进制串00001xxxx表示十进制值idx∈[16,31],查找表ff_golomb_vlc_len[idx]=9,查找表ff_ue_golomb_vlc_code[idx] ∈[15,30];

2)       0001xxxnn,码长7比特,前缀3比特,一个比特1和3个后缀,根据公式CodeNum = 2^m – 1 + Value=7+Value解码结果为[7,14],n表示不属于该指数哥伦布码的比特。二进制串0001xxxnn表示的十进制值idx∈[32,63],查找表ff_golomb_vlc_len[idx]=7,查找表ff_ue_golomb_vlc_code[idx] ∈[7,14];

3)       001xxnnnn,码长5比特,前缀2比特,一个比特1和2个后缀,根据公式CodeNum = 2^m – 1 + Value=3+Value解码结果为[3,6],n表示不属于该指数哥伦布码的比特。二进制串001xxnnnn表示的十进制值idx∈[64,127],查找表ff_golomb_vlc_len[idx]=5,查找表ff_ue_golomb_vlc_code[idx] ∈[3,6];

4)       01xnnnnnn,码长3比特,前缀1比特,一个比特1和1个后缀,根据公式CodeNum = 2^m – 1 + Value=1+Value解码结果为[1,2],n表示不属于该指数哥伦布码的比特。二进制串01xnnnnnn表示的十进制值idx∈[128,255],查找表ff_golomb_vlc_len[idx]=3,查找表ff_ue_golomb_vlc_code[idx] ∈[1,2];

5)       1nnnnnnnn,码长1比特,前缀0比特,一个比特1和0个后缀,根据公式CodeNum = 2^m – 1 + Value=0+Value解码结果为0,n表示不属于该指数哥伦布码的比特。二进制串1nnnnnnnn表示的十进制值idx∈[256,511],查找表ff_golomb_vlc_len[idx]=1,查找表ff_ue_golomb_vlc_code[idx] =0。

 

static inline int get_ue_golomb(GetBitContext *gb)
{
	unsigned int buf;
	unsigned int re_index = (gb)->index; unsigned int av_unused re_cache;
	re_cache = AV_RB32((gb)->buffer + (re_index >> 3)) << (re_index & 7);
	buf = ((uint32_t)re_cache);
	if (buf >= (1 << 27)) {
		buf >>= 32 - 9;
		re_index += (ff_golomb_vlc_len[buf]);
		(gb)->index = re_index;
		return ff_ue_golomb_vlc_code[buf];
	}
	else {
		int log = 2 * av_log2(buf) - 31;
		re_index += (32 - log);
		(gb)->index = re_index;
		if (log < 7) {
			av_log(NULL, AV_LOG_ERROR, "Invalid UE golomb code\n");
			return AVERROR_INVALIDDATA;
		}
		buf >>= log;
		buf--;
		return buf;
	}
}
图5. 零阶指数哥伦布解码FFmpeg实现

FFmpeg实现如图5所示,第4行获取当前码流指针的位置,假设re_index=51,则表示当前已经解析到该码流的第51个比特。第5行,先从(gb)->buffer+ (re_index >> 3)位置读取32比特(re_index右移3,表示的是当前已经处理完成的字节数,比如51>>3=6,表示当前已经处理完成6字节,并且第7字节已经处理了3比特,由于只能从整数字节处开始读取数据,所以要从(gb)->buffer + (re_index >> 3)读取4字节),其中AV_RB32((gb)->buffer+ (re_index >> 3))表示对读取的4字节数进行大小端数转换,假设(gb)->buffer+ (re_index >> 3)处的4个二进制串为0x62620000,由于将其读成了整数,其值将被解析成0x00006262,通过AV_RB32((gb)->buffer+ (re_index >> 3))操作可以将其还原为0x62620000,AV_RB32((gb)->buffer+ (re_index >> 3)) << (re_index & 7)将转换后的4字节数字左移(re_index&7)=(51&7)=3比特(之所以要移位,是因为上次已经处理到了51比特的位置,但是现在读取数据的位置是从第6字节也就是48比特的位置开始读取的,所以还需要将数字左移3比特),左移后的数字将变成0x13100000。第7行判断当前读取的码长是否大于9比特,比如0x13100000的二进制串为0001 0011 0001 0000 0000 0000 0000 0000,从左往右算,可知该码字的前9比特位为0001 0011 0符合上面所述的第二种情况0001xxxnn;码长不大于9比特时,其前缀0的个数最多为4个,也就是上面的第一种情况00001xxxx,将其扩展为32位为:00001xxxx nnnn nnnn nnnn nnnn nnnn nnnn nnn,可以知道,当buf>=(1<<27)时,其码长一定小于等于9比特,否则大于9比特。

8~11行,当码长小于等于9比特时,先将buf右移32-9=23比特,然后根据其值查找ff_golomb_vlc_len和ff_ue_golomb_vlc_code表,变可得到当前值对应的码长和码字。

14~23行处理码长大于9比特时的情况。第14行中av_log2(buf)计算数值buf对数,比如一个整数buf=0x00080000,log2(buf)=19,因此前前缀0的个数为31-av_log2(buf)=31-19=12,根据零阶指数哥伦布编码规则可知,码长为2*(31-av_log2(buf))+1=2*12+1=25,所以32-(2*(31-av_log2(buf))+1)=2* av_log2(buf)-31=2*19-31=7表示的是buf需要右移的位数。第15行表示的也就是当前buf中的二进制串的码长。第21行移除buf中不属于指数哥伦布编码部分的比特,也就是buf=0x00080000>>7=0x00001000,第22行将buf减1主要是由于指数哥伦布编码的过程中加上了一个1,所以需要减去1才是其真正表示的数值。
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值