VLC一--零阶指数哥伦布码解码原理和实现
2016-12-18 09:51
344 查看
指数哥伦布编码属于变长编码,其基本原理是用短码字表示出现频率较高的信息,用长码字表示出现频率较低的信息。
(a) 指数哥伦布解析形式 (b) 指数哥伦布值的计算
图1. 指数哥伦布编码
因此解码值CodeNum的计算方式如下:
CodeNum = 2^(m+k) – 2^(k) + Value
在H264/AVC和HEVC在参数集的变长编码中常用的是0阶指数哥伦布编码,分为无符号0阶哥伦布指数编码和有符号数0级哥伦布指数编码,如表1-1所示,其中CodeNum表示解码值,有符号所对应的列表示无符号编码是所对应的值,有符号表示采用有符号0阶指数哥伦布编码时所对应的十进制数。
表1-1 0阶有符号和无符号指数哥伦布编码
图2. 标准零阶指数哥伦布解码实现
图3. ff_golomb_vlc_len码长查找表
图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。
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.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 ) |
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}; |
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}; |
当码长不超过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才是其真正表示的数值。
相关文章推荐
- Golomb及指数哥伦布编码原理介绍及实现
- Golomb及指数哥伦布编码原理介绍及实现
- 无符号哥伦布编码的解码原理以及C++实现
- Golomb及指数哥伦布编码原理介绍及实现
- 【知识笔记】Base64编码解码原理及手动实现(C#)
- 基于FFmpeg实现MP3自组织seek策略偶发解码后Resample出错问题原理释义
- URL编码 百分号编码和解码 URLEncoder和URLDecoder的大致实现原理
- Base64编码原理及编码、解码实现
- Base64编、解码原理,及代码实现(一)
- H.264之指数哥伦布编解码分析++自己的理解
- java 二维码原理以及用java实现的二维码的生成、解码
- URL编码 百分号编码和解码 URLEncoder和URLDecoder的大致实现原理
- [原创]桓泽学音频编解码(4):MP3 和 AAC 中反量化原理,优化设计与参考代码中实现
- H.264之指数哥伦布编解码分析
- java 二维码原理以及用java实现的二维码的生成、解码
- java 二维码原理以及用java实现的二维码的生成、解码
- [原创]桓泽学音频编解码(7):MP3 和 AAC 中huffman解码原理,优化设计与参考代码中实现
- [原创]桓泽学音频编解码(5):MP3 和 AAC 中IMDCT算法的原理,优化设计与参考代码中实现
- 指数哥伦布编码-无符号0阶的解码
- Linux工具开发[01]---网络协议中常见编码的原理及其解码函数的实现