一、引言
由《音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB》可以知道,H.264 码流中的操作单位是位(bit),而不是字节。因为视频的传输和存贮是十分在乎体积的,对于每一个比特(bit)都要格外珍惜。用普通的指针是无法达到“位”的操作粒度的。FFmpeg源码中使用GetBitContext结构体来对“位”进行操作。
二、GetBitContext结构体定义
GetBitContext结构体定义在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3,该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译)的头文件libavcodec/get_bits.h中:
#ifndef CACHED_BITSTREAM_READER
#define CACHED_BITSTREAM_READER 0
#endiftypedef struct GetBitContext {const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READERuint64_t cache;unsigned bits_left;
#endifint index;int size_in_bits;int size_in_bits_plus8;
} GetBitContext;
三、init_get_bits函数定义
init_get_bits函数定义在libavcodec/get_bits.h 中:
/*** Initialize GetBitContext.* @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes* larger than the actual read bits because some optimized bitstream* readers read 32 or 64 bit at once and could read over the end* @param bit_size the size of the buffer in bits* @return 0 on success, AVERROR_INVALIDDATA if the buffer_size would overflow.*/
static inline int init_get_bits(GetBitContext *s, const uint8_t *buffer,int bit_size)
{
#ifdef BITSTREAM_READER_LEreturn init_get_bits_xe(s, buffer, bit_size, 1);
#elsereturn init_get_bits_xe(s, buffer, bit_size, 0);
#endif
}
其作用是初始化GetBitContext结构体。
形参s:输出型参数。指向要被初始化的GetBitContext类型的变量。
形参buffer:输入型参数。指向某个缓冲区,该缓冲区存放NALU Header + RBSP。
形参bit_size:输入型参数。NALU Header + SODB的位数,单位为bit。
返回值:返回0表示成功,返回AVERROR_INVALIDDATA表示失败。
执行init_get_bits函数初始化后,如果初始化成功:
s->buffer指向存放NALU Header + RBSP 的缓冲区。
s->buffer_end指向上述缓冲区的末尾,也就是RBSP的最后一个字节。
s->index的值等于0。
s->size_in_bit 的值等于NALU Header + SODB的位数,单位为bit。
s->size_in_bits_plus8的值等于 s->size_in_bit 的值 加 8。
四、get_bits1函数定义
get_bits1函数定义在 libavcodec/get_bits.h 中:
static inline unsigned int get_bits1(GetBitContext *s)
{
#if CACHED_BITSTREAM_READERif (!s->bits_left)
#ifdef BITSTREAM_READER_LErefill_64(s, 1);
#elserefill_64(s, 0);
#endif#ifdef BITSTREAM_READER_LEreturn get_val(s, 1, 1);
#elsereturn get_val(s, 1, 0);
#endif
#elseunsigned int index = s->index;uint8_t result = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LEresult >>= index & 7;result &= 1;
#elseresult <<= index & 7;result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READERif (s->index < s->size_in_bits_plus8)
#endifindex++;s->index = index;return result;
#endif
}
该函数在使用init_get_bits函数初始化后,才能被调用。其作用是读取s->buffer指向的缓冲区(存放NALU Header + RBSP)中的1位(bit)数据。读取完后,s->index的值会加1(所以s->index实际上是用来标记当前读取到第几位了)。
形参s:既是输入型参数也是输出型参数。指向已经被初始化的GetBitContext类型的变量。
返回值:被读取到的1位(bit)的数据。
五、get_bits函数定义
get_bits函数定义在 libavcodec/get_bits.h 中:
/*** Read 1-25 bits.*/
static inline unsigned int get_bits(GetBitContext *s, int n)
{register unsigned int tmp;
#if CACHED_BITSTREAM_READERav_assert2(n>0 && n<=32);if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LErefill_32(s, 1);
#elserefill_32(s, 0);
#endifif (s->bits_left < 32)s->bits_left = n;}#ifdef BITSTREAM_READER_LEtmp = get_val(s, n, 1);
#elsetmp = get_val(s, n, 0);
#endif
#elseOPEN_READER(re, s);av_assert2(n>0 && n<=25);UPDATE_CACHE(re, s);tmp = SHOW_UBITS(re, s, n);LAST_SKIP_BITS(re, s, n);CLOSE_READER(re, s);
#endifav_assert2(tmp < UINT64_C(1) << n);return tmp;
}
该函数在使用init_get_bits函数初始化后,才能被调用。其作用是读取s->buffer指向的缓冲区(存放NALU Header + RBSP)中的n位(bit)数据。读取完后,s->index的值会加n。
形参s:既是输入型参数也是输出型参数。指向已经被初始化的GetBitContext类型的变量。
返回值:被读取到的n位(bit)的数据。
六、编写测试例子,来理解get_bits1函数和get_bits函数的使用
编写测试例子main.c,在CentOS 7.5上通过10.2.1版本的gcc可以成功编译 :
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>#define CONFIG_SAFE_BITSTREAM_READER 1#ifndef UNCHECKED_BITSTREAM_READER
#define UNCHECKED_BITSTREAM_READER !CONFIG_SAFE_BITSTREAM_READER
#endif#ifndef CACHED_BITSTREAM_READER
#define CACHED_BITSTREAM_READER 0
#endif#if defined(__GNUC__) || defined(__clang__)
# define av_unused __attribute__((unused))
#else
# define av_unused
#endif#ifdef __GNUC__
# define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
# define AV_GCC_VERSION_AT_MOST(x,y) (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y))
#else
# define AV_GCC_VERSION_AT_LEAST(x,y) 0
# define AV_GCC_VERSION_AT_MOST(x,y) 0
#endif#define av_alias __attribute__((may_alias))#ifndef av_always_inline
#if AV_GCC_VERSION_AT_LEAST(3,1)
# define av_always_inline __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
# define av_always_inline __forceinline
#else
# define av_always_inline inline
#endif
#endif#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__)
# define av_const __attribute__((const))
#else
# define av_const
#endif#define AV_BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff))
#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16))#ifndef av_bswap32
static av_always_inline av_const uint32_t av_bswap32(uint32_t x)
{return AV_BSWAP32C(x);
}
#endifunion unaligned_32 { uint32_t l; } __attribute__((packed)) av_alias;# define AV_RN(s, p) (((const union unaligned_##s *) (p))->l)
# define AV_RB(s, p) av_bswap##s(AV_RN##s(p))#ifndef AV_RB32
# define AV_RB32(p) AV_RB(32, p)
#endif#ifndef AV_RN32
# define AV_RN32(p) AV_RN(32, p)
#endif#ifndef NEG_USR32
# define NEG_USR32(a,s) (((uint32_t)(a))>>(32-(s)))
#endif/*** assert() equivalent, that does lie in speed critical code.*/
#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
#define av_assert2(cond) av_assert0(cond)
#define av_assert2_fpu() av_assert0_fpu()
#else
#define av_assert2(cond) ((void)0)
#define av_assert2_fpu() ((void)0)
#endif/*** @ingroup lavc_decoding* Required number of additionally allocated bytes at the end of the input bitstream for decoding.* This is mainly needed because some optimized bitstream readers read* 32 or 64 bit at once and could read over the end.<br>* Note: If the first 23 bits of the additional bytes are not 0, then damaged* MPEG bitstreams could cause overread and segfault.*/
#define AV_INPUT_BUFFER_PADDING_SIZE 64#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))
#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d))
#define AVERROR_INVALIDDATA FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input#if CACHED_BITSTREAM_READER
# define MIN_CACHE_BITS 64
#elif defined LONG_BITSTREAM_READER
# define MIN_CACHE_BITS 32
#else
# define MIN_CACHE_BITS 25
#endif#if !CACHED_BITSTREAM_READER#define OPEN_READER_NOSIZE(name, gb) \unsigned int name ## _index = (gb)->index; \unsigned int av_unused name ## _cache#if UNCHECKED_BITSTREAM_READER
#define OPEN_READER(name, gb) OPEN_READER_NOSIZE(name, gb)#define BITS_AVAILABLE(name, gb) 1
#else
#define OPEN_READER(name, gb) \OPEN_READER_NOSIZE(name, gb); \unsigned int name ## _size_plus8 = (gb)->size_in_bits_plus8#define BITS_AVAILABLE(name, gb) name ## _index < name ## _size_plus8
#endif#define CLOSE_READER(name, gb) (gb)->index = name ## _index# ifdef LONG_BITSTREAM_READER# define UPDATE_CACHE_LE(name, gb) name ## _cache = \AV_RL64((gb)->buffer + (name ## _index >> 3)) >> (name ## _index & 7)# define UPDATE_CACHE_BE(name, gb) name ## _cache = \AV_RB64((gb)->buffer + (name ## _index >> 3)) >> (32 - (name ## _index & 7))#else# define UPDATE_CACHE_LE(name, gb) name ## _cache = \AV_RL32((gb)->buffer + (name ## _index >> 3)) >> (name ## _index & 7)# define UPDATE_CACHE_BE(name, gb) name ## _cache = \AV_RB32((gb)->buffer + (name ## _index >> 3)) << (name ## _index & 7)#endif#ifdef BITSTREAM_READER_LE# define UPDATE_CACHE(name, gb) UPDATE_CACHE_LE(name, gb)# define SKIP_CACHE(name, gb, num) name ## _cache >>= (num)#else# define UPDATE_CACHE(name, gb) UPDATE_CACHE_BE(name, gb)# define SKIP_CACHE(name, gb, num) name ## _cache <<= (num)#endif#if UNCHECKED_BITSTREAM_READER
# define SKIP_COUNTER(name, gb, num) name ## _index += (num)
#else
# define SKIP_COUNTER(name, gb, num) \name ## _index = FFMIN(name ## _size_plus8, name ## _index + (num))
#endif#define BITS_LEFT(name, gb) ((int)((gb)->size_in_bits - name ## _index))#define SKIP_BITS(name, gb, num) \do { \SKIP_CACHE(name, gb, num); \SKIP_COUNTER(name, gb, num); \} while (0)#define LAST_SKIP_BITS(name, gb, num) SKIP_COUNTER(name, gb, num)#define SHOW_UBITS_LE(name, gb, num) zero_extend(name ## _cache, num)
#define SHOW_SBITS_LE(name, gb, num) sign_extend(name ## _cache, num)#define SHOW_UBITS_BE(name, gb, num) NEG_USR32(name ## _cache, num)
#define SHOW_SBITS_BE(name, gb, num) NEG_SSR32(name ## _cache, num)#ifdef BITSTREAM_READER_LE
# define SHOW_UBITS(name, gb, num) SHOW_UBITS_LE(name, gb, num)
# define SHOW_SBITS(name, gb, num) SHOW_SBITS_LE(name, gb, num)
#else
# define SHOW_UBITS(name, gb, num) SHOW_UBITS_BE(name, gb, num)
# define SHOW_SBITS(name, gb, num) SHOW_SBITS_BE(name, gb, num)
#endif#define GET_CACHE(name, gb) ((uint32_t) name ## _cache)#endiftypedef struct GetBitContext {const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READERuint64_t cache;unsigned bits_left;
#endifint index;int size_in_bits;int size_in_bits_plus8;
} GetBitContext;static inline int init_get_bits_xe(GetBitContext *s, const uint8_t *buffer,int bit_size, int is_le)
{int buffer_size;int ret = 0;if (bit_size >= INT_MAX - FFMAX(7, AV_INPUT_BUFFER_PADDING_SIZE*8) || bit_size < 0 || !buffer) {bit_size = 0;buffer = NULL;ret = AVERROR_INVALIDDATA;}buffer_size = (bit_size + 7) >> 3;s->buffer = buffer;s->size_in_bits = bit_size;s->size_in_bits_plus8 = bit_size + 8;s->buffer_end = buffer + buffer_size;s->index = 0;#if CACHED_BITSTREAM_READERs->cache = 0;s->bits_left = 0;refill_64(s, is_le);
#endifreturn ret;
}/*** Initialize GetBitContext.* @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes* larger than the actual read bits because some optimized bitstream* readers read 32 or 64 bit at once and could read over the end* @param bit_size the size of the buffer in bits* @return 0 on success, AVERROR_INVALIDDATA if the buffer_size would overflow.*/
static inline int init_get_bits(GetBitContext *s, const uint8_t *buffer,int bit_size)
{
#ifdef BITSTREAM_READER_LEreturn init_get_bits_xe(s, buffer, bit_size, 1);
#elsereturn init_get_bits_xe(s, buffer, bit_size, 0);
#endif
}static inline unsigned int get_bits1(GetBitContext *s)
{
#if CACHED_BITSTREAM_READERif (!s->bits_left)
#ifdef BITSTREAM_READER_LErefill_64(s, 1);
#elserefill_64(s, 0);
#endif#ifdef BITSTREAM_READER_LEreturn get_val(s, 1, 1);
#elsereturn get_val(s, 1, 0);
#endif
#elseunsigned int index = s->index;uint8_t result = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LEresult >>= index & 7;result &= 1;
#elseresult <<= index & 7;result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READERif (s->index < s->size_in_bits_plus8)
#endifindex++;s->index = index;return result;
#endif
}/*** Read 1-25 bits.*/
static inline unsigned int get_bits(GetBitContext *s, int n)
{register unsigned int tmp;
#if CACHED_BITSTREAM_READERav_assert2(n>0 && n<=32);if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LErefill_32(s, 1);
#elserefill_32(s, 0);
#endifif (s->bits_left < 32)s->bits_left = n;}#ifdef BITSTREAM_READER_LEtmp = get_val(s, n, 1);
#elsetmp = get_val(s, n, 0);
#endif
#elseOPEN_READER(re, s);av_assert2(n>0 && n<=25);UPDATE_CACHE(re, s);tmp = SHOW_UBITS(re, s, n);LAST_SKIP_BITS(re, s, n);CLOSE_READER(re, s);
#endifav_assert2(tmp < UINT64_C(1) << n);return tmp;
}int main()
{GetBitContext gb;uint8_t *data = (uint8_t *)malloc(sizeof(uint8_t) * 3);if(data){data[0] = 0x12;data[1] = 0x34;data[2] = 0x80;int ret = init_get_bits(&gb, data, 16);for(int i=0; i<gb.size_in_bits; i++){printf("value:%u, index:%d\n", get_bits1(&gb), gb.index);}printf("\n______________________________________________\n\n");ret = init_get_bits(&gb, data, 16);printf("value:%u, index:%d\n", get_bits1(&gb), gb.index);printf("value:%u, index:%d\n", get_bits(&gb, 2), gb.index);printf("value:%u, index:%d\n", get_bits(&gb, 5), gb.index);free(data);data = NULL;}return 0;
}
使用gcc编译,运行,输出如下:
本测试例子中,首先通过uint8_t *data = (uint8_t *)malloc(sizeof(uint8_t) * 3); 分配3个字节的内存。然后通过data[0] = 0x12;data[1] = 0x34;data[2] = 0x80; 对该缓冲区进行赋值,使该缓冲区的第一个字节为0x12,第二个字节为0x34,第三个字节为0x80。使用该缓冲区来模拟存放的是H.264中的“NALU Header + RBSP”。
由于第三个字节为0x80,转换为2进制为10000000。所以可以认为第三个字节的0x80是RBSP中的stop bit + rbsp_alignment_zero_bit。所以NALU Header + SODB的位数应该是去掉第三个字节的长度,也就是2个字节,等于16位。
所以:int ret = init_get_bits(&gb, data, 16);中的第三个形参为16。使用init_get_bits初始化GetBitContext结构体类型的变量gb。
然后在for(int i=0; i<gb.size_in_bits; i++)循环中不断通过get_bits1函数,打印gb->buffer指向的缓冲区中的每个位的值。由于0x12转换成2进制是00010010,0x34转换成2进制是00110100。
所以打印为:
打印完后初始化变量gb,重新通过get_bits1和get_bits函数打印。2进制00010010的第0位是0,所以printf("value:%u, index:%d\n", get_bits1(&gb), gb.index);的输出为“value:0, index:0”。此时由于打印了1位,所以index的值变为1。再执行printf("value:%u, index:%d\n", get_bits(&gb, 2), gb.index);由于2进制00010010的第1、第2位都是0,也就是0b00,所以输出是:value:0, index:1,此时由于又打印了2位,所以index的值变为3。再执行printf("value:%u, index:%d\n", get_bits(&gb, 5), gb.index);由于2进制00010010的第3到第7 位是0b10010,转成10进制是18,所以输出是:value:18, index:3。