您的位置:首页 > 其它

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

2016-12-18 09:51 344 查看
指数哥伦布编码属于变长编码,其基本原理是用短码字表示出现频率较高的信息,用长码字表示出现频率较低的信息。     

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才是其真正表示的数值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息