当前位置: 首页 > news >正文

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编译,输出为:

相关文章:

  • linux中sysfs创建设备节点的方法和DEVICE_ATTR
  • Linux安装刻录软件
  • SpringBoot前置知识01-SPI接口
  • 谓词逻辑(一)
  • Vue3:可以使用.value获取ref()包裹的值,为何还要存在unref()
  • 基于Vue3 + js-tool-big-box工具库实现3个随机数字的小游戏动画,快来挑战你的非凡手气!
  • 列表的创建和删除
  • 别说废话!说话说到点上,项目高效沟通的底层逻辑揭秘
  • 。。。。。
  • 面试八股之MySQL篇2——索引篇
  • 31.@Anonymous
  • 运行Android项目时,提示错误: 程序包javax.annotation.processing不存在
  • PersonalLLM——探索LLM是否能根据五大人格特质重新塑造一个新的角色?
  • 组播协议简介
  • javascript --对象构造器和class的区别
  • es6
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • javascript面向对象之创建对象
  • JavaScript实现分页效果
  • java多线程
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • python 装饰器(一)
  • 阿里云应用高可用服务公测发布
  • 记录一下第一次使用npm
  • 蓝海存储开关机注意事项总结
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 入手阿里云新服务器的部署NODE
  • 思否第一天
  • 算法-插入排序
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 云大使推广中的常见热门问题
  • 《码出高效》学习笔记与书中错误记录
  • 《天龙八部3D》Unity技术方案揭秘
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • NLPIR智能语义技术让大数据挖掘更简单
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​人工智能书单(数学基础篇)
  • # 计算机视觉入门
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • #mysql 8.0 踩坑日记
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (03)光刻——半导体电路的绘制
  • (1)(1.11) SiK Radio v2(一)
  • (7)摄像机和云台
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (pojstep1.3.1)1017(构造法模拟)
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (六)激光线扫描-三维重建
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等