ffmpeg中的位操作

最近重构代码, 移植了ffmpeg源码, 有人问了两个问题:
1. MIN_CACHE_BITS的含义, 读取码值时为什么要判断该值
2. get_ue_golomb_long()的含义
这里简要分析下代码(原理性东西比如什么是哥伦布编码就不说了).

先来看下show_bits_long()(defined in libavcodec/get_bits.h), 为什么此处需要判断n的位数并走入两条分支?

 1 static inline unsigned int show_bits_long(GetBitContext *s, int n) 
 2 { 
 3     if (n <= MIN_CACHE_BITS) { 
 4         return show_bits(s, n); 
 5     } else { 
 6         GetBitContext gb = *s; 
 7         return get_bits_long(&gb, n); 
 8     } 
 9 } 
10 static inline unsigned int show_bits(GetBitContext *s, int n) 
11 { 
12     register int tmp; 
13     OPEN_READER_NOSIZE(re, s); 
14     av_assert2(n>0 && n<=25); 
15     UPDATE_CACHE(re, s); 
16     tmp = SHOW_UBITS(re, s, n); 
17     return tmp; 
18 } 
19 static inline unsigned int get_bits_long(GetBitContext *s, int n) 
20 { 
21     av_assert2(n>=0 && n<=32); 
22     if (!n) { 
23         return 0; 
24     } else if (n <= MIN_CACHE_BITS) { 
25         return get_bits(s, n); 
26     } else { 
27 #ifdef BITSTREAM_READER_LE 
28         unsigned ret = get_bits(s, 16); 
29         return ret | (get_bits(s, n - 16) << 16); 
30 #else 
31         unsigned ret = get_bits(s, 16) << (n - 16); 
32         return ret | get_bits(s, n - 16); 
33 #endif 
34    } 
35 } 

 

因为show_bits()一次读入int(4byte, 32bit)大小数据, 再根据当前读取bit数做移位操作(去除已读取的bit). 最坏的情况下需要左移7位(如果8位以上就是字节偏移了), 因此最少能保证读取25bit有效数据. 因此在已知读取位数少于25bit时走上面的fast path, 如果读取位数大于25bit呢? 那就走下面的slow path, 即先取地址两个字节, 再取高地址剩余的位. 那变长编码如何得知我要读取的位数呢? 再看下get_ue_golomb_long()(defined in libavcodec/golomb.h):

1 static inline unsigned get_ue_golomb_long(GetBitContext *gb) 
2 { 
3     unsigned buf, log; 
4     buf = show_bits_long(gb, 32); 
5     log = 31 - av_log2(buf); 
6     skip_bits_long(gb, log); 
7     return get_bits_long(gb, log + 1) - 1; 
8 } 

 

get_ue_golomb_long()先调用show_bits_long(), 传入的长度是32, 其逻辑是假定不存在32个连零的情况(否则实际值最小也是0xFFFFFFFF), 返回的值是当前读取位所在字节起始的连续四字节组成的unsigned int. av_log2()(defined in libavutil/intmath.h)是解析前导零个数的关键, 其思想分两步: 通过二分查找最高有效位所在的字节, 再通过查表得知最高有效位所在位的位数. 通过一个256字节的数组(ff_log2_tab[])节约了循环查找的时间开销. 最后用31减去最高位所在位数即得到前导零的个数.

 1 #define av_log2 ff_log2 
 2 #define ff_log2 ff_log2_c 
 3 static av_always_inline av_const int ff_log2_c(unsigned int v) 
 4 { 
 5     int n = 0; 
 6     if (v & 0xffff0000) { 
 7         v >>= 16; 
 8         n += 16; 
 9     } 
10     if (v & 0xff00) { 
11         v >>= 8; 
12         n += 8; 
13     } 
14     n += ff_log2_tab[v]; 
15     return n; 
16 } 

 

回到get_ue_golomb_long(), 得到前导零个数后将其跳过再读n+1位即得到码值, 减去1即实际哥伦布编码数据.

 

posted @ 2018-02-22 15:57  Five100Miles  阅读(729)  评论(0编辑  收藏  举报