FFmpeg源码:bytestream_get_byte函数解析

一、引言

FFmpeg源码中经常使用到bytestream_get_byte这个函数,比如使用FFmpeg对BMP图片进行解析,其源码会调用函数bmp_decode_frame,而该函数内部会通过bytestream_get_byte读取BMP 的header。本文讲解函数bytestream_get_byte的作用和内部实现。本文演示用的FFmpeg源码版本为5.0.3,该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译

二、bytestream_get_byte函数内部实现

 FFmpeg源码目录下的libavutil/attributes.h 中存在如下宏定义

#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


#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

其中:__GNUC__ 、__GNUC_MINOR__ 分别代表gcc的主版本号,次版本号。

所以下面这段条件编译指令

#if AV_GCC_VERSION_AT_LEAST(3,1)

#    define av_always_inline __attribute__((always_inline)) inline

的意思是如果gcc主版本号不小于3,次版本号不小于1,就执行

#    define av_always_inline __attribute__((always_inline)) inline

我的gcc版本为10.2.1,满足该条件,所以会定义该宏。__attribute__((always_inline))的意思是强制内联,具体可以参考:《__attribute__((always_inline))

FFmpeg源码目录下的libavutil/intreadwrite.h中存在宏定义:

#define AV_RB8(x)     (((const uint8_t*)(x))[0])
#define AV_WB8(p, d)  do { ((uint8_t*)(p))[0] = (d); } while(0)

libavcodec/bytestream.h 中存在如下宏定义

#define DEF(type, name, bytes, read, write)                                  \
static av_always_inline type bytestream_get_ ## name(const uint8_t **b)        \
{                                                                              \
    (*b) += bytes;                                                             \
    return read(*b - bytes);                                                   \
}
DEF(unsigned int, byte, 1, AV_RB8 , AV_WB8)

语句 static av_always_inline type bytestream_get_ ## name(const uint8_t **b)  中## 为宏定义的操作连接符。具体可以参考:《define的一些骚操作:##操作连接符、#@字符化操作符、#字符串化操作符、\行继续操作

所以宏定义DEF(unsigned int, byte, 1, AV_RB8 , AV_WB8) 等价于:

static __attribute__((always_inline)) inline unsigned int bytestream_get_byte(const uint8_t **b) 
{                                                                              
    (*b) += 1;                                                            
    return (((const uint8_t*)(*b - 1))[0]);                                           
}    

不强制内联,变成普通的函数相当于:

static unsigned int bytestream_get_byte(const uint8_t **b) 
{                                                                              
    (*b) += 1;                                                            
    return (((const uint8_t*)(*b - 1))[0]);                                           
}     

编写测试例子main.c :

#include <stdint.h>
#include "stdio.h"


static unsigned int bytestream_get_byte(const uint8_t **b) 
{                                                                              
    (*b) += 1;                                                            
    return (((const uint8_t*)(*b - 1))[0]);                                           
}        


int main()
{
    const uint8_t *buf = "ABCDEF";
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    return 0;
}

Linux平台下使用gcc编译,输出为:

通过该例子可以很容易看出来,函数bytestream_get_byte作用就是返回(以形参*b为首地址的)缓冲区中的第一个字符,并将地址(*b)加1,这样再次调用函数bytestream_get_byte时就会返回缓冲区的第二个字符。以此类推。比如上述测试例子中,最开始buf指向"ABCDEF"。第一次执行printf("%c\n", bytestream_get_byte(&buf))时,会输出'A',然后buf指向"BCDEF";第二次执行printf("%c\n", bytestream_get_byte(&buf))时,会输出'B',然后buf指向"CDEF",以此类推。

不将FFmpeg的宏定义展开,则上述测试例子可以修改为 main.c:

#include <stdint.h>
#include "stdio.h"

#define av_always_inline __attribute__((always_inline)) inline

#define AV_RB8(x)     (((const uint8_t*)(x))[0])
#define AV_WB8(p, d)  do { ((uint8_t*)(p))[0] = (d); } while(0)

#define DEF(type, name, bytes, read, write)                                  \
static av_always_inline type bytestream_get_ ## name(const uint8_t **b)        \
{                                                                              \
    (*b) += bytes;                                                             \
    return read(*b - bytes);                                                   \
}     

DEF(unsigned int, byte, 1, AV_RB8 , AV_WB8)



int main()
{
    const uint8_t *buf = "ABCDEF";
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    printf("%c\n", bytestream_get_byte(&buf));
    return 0;
}

Linux平台下使用gcc编译,输出为:

  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值