一、引言
由《音视频入门基础: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
#endif
typedef struct GetBitContext {
const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READER
uint64_t cache;
unsigned bits_left;
#endif
int 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_LE
return init_get_bits_xe(s, buffer, bit_size, 1);
#else
return 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_READER
if (!s->bits_left)
#ifdef BITSTREAM_READER_LE
refill_64(s, 1);
#else
refill_64(s, 0);
#endif
#ifdef BITSTREAM_READER_LE
return get_val(s, 1, 1);
#else
return get_val(s, 1, 0);
#endif
#else
unsigned int index = s->index;
uint8_t result = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LE
result >>= index & 7;
result &= 1;
#else
result <<= index & 7;
result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READER
if (s->index < s->size_in_bits_plus8)
#endif
index++;
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_READER
av_assert2(n>0 && n<=32);
if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LE
refill_32(s, 1);
#else
refill_32(s, 0);
#endif
if (s->bits_left < 32)
s->bits_left = n;
}
#ifdef BITSTREAM_READER_LE
tmp = get_val(s, n, 1);
#else
tmp = get_val(s, n, 0);
#endif
#else
OPEN_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);
#endif
av_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);
}
#endif
union 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)
#endif
typedef struct GetBitContext {
const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READER
uint64_t cache;
unsigned bits_left;
#endif
int 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_READER
s->cache = 0;
s->bits_left = 0;
refill_64(s, is_le);
#endif
return 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_LE
return init_get_bits_xe(s, buffer, bit_size, 1);
#else
return init_get_bits_xe(s, buffer, bit_size, 0);
#endif
}
static inline unsigned int get_bits1(GetBitContext *s)
{
#if CACHED_BITSTREAM_READER
if (!s->bits_left)
#ifdef BITSTREAM_READER_LE
refill_64(s, 1);
#else
refill_64(s, 0);
#endif
#ifdef BITSTREAM_READER_LE
return get_val(s, 1, 1);
#else
return get_val(s, 1, 0);
#endif
#else
unsigned int index = s->index;
uint8_t result = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LE
result >>= index & 7;
result &= 1;
#else
result <<= index & 7;
result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READER
if (s->index < s->size_in_bits_plus8)
#endif
index++;
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_READER
av_assert2(n>0 && n<=32);
if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LE
refill_32(s, 1);
#else
refill_32(s, 0);
#endif
if (s->bits_left < 32)
s->bits_left = n;
}
#ifdef BITSTREAM_READER_LE
tmp = get_val(s, n, 1);
#else
tmp = get_val(s, n, 0);
#endif
#else
OPEN_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);
#endif
av_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。