FFmpeg中内存分配和释放相关的源码:av_malloc函数、av_mallocz函数、av_free函数和av_freep函数分析

一、av_malloc函数分析

(一)av_malloc函数的声明

av_malloc函数的声明放在在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3,该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译)的头文件libavutil/mem.h中:

/*** Allocate a memory block with alignment suitable for all memory accesses* (including vectors if available on the CPU).** @param size Size in bytes for the memory block to be allocated* @return Pointer to the allocated block, or `NULL` if the block cannot*         be allocated* @see av_mallocz()*/
void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);

根据注释,可以了解到该函数作用是分配一个适合所有内存访问的内存块(包括动态数组,如果CPU上可用)。形参size:要分配的内存块的字节大小。返回值:指向分配块的指针;如果无法申请内存,则返回NULL。

宏定义av_malloc_attrib也被定义在libavutil/mem.h中:

/*** @def av_malloc_attrib* Function attribute denoting a malloc-like function.** @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bmalloc_007d-function-attribute-3251">Function attribute `malloc` in GCC's documentation</a>*/#if AV_GCC_VERSION_AT_LEAST(3,1)#define av_malloc_attrib __attribute__((__malloc__))
#else#define av_malloc_attrib
#endif

这里用attribute指定__malloc__属性,表示这样标记的函数(av_malloc函数)返回的块不得包含任何指向其他对象的指针。这样做的目的是帮助编译器估计哪些指针可能指向同一个对象:该属性告诉GCC,它不必担心函数返回的对象可能包含指向它正在跟踪的其他对象的指针。具体可以参考:GCC: __attribute__((malloc))

宏定义av_malloc_attrib也被定义在libavutil/mem.h中:

/*** @def av_alloc_size(...)* Function attribute used on a function that allocates memory, whose size is* given by the specified parameter(s).** @code{.c}* void *av_malloc(size_t size) av_alloc_size(1);* void *av_calloc(size_t nmemb, size_t size) av_alloc_size(1, 2);* @endcode** @param ... One or two parameter indexes, separated by a comma** @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007balloc_005fsize_007d-function-attribute-3220">Function attribute `alloc_size` in GCC's documentation</a>*/#if AV_GCC_VERSION_AT_LEAST(4,3)#define av_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__)))
#else#define av_alloc_size(...)
#endif

这里用attribute指定alloc_size属性,用于告诉编译器函数返回值指向的内存,其大小是由alloc_size中的参数指定,主要用于提高__builtin_object_size的正确性。具体参考:GCC __atrribute__

然后宏定义用到了变参__VA_ARGS__,可以参考:C 语言 define 变参__VA_ARGS__使用

所以函数声明void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1) 等价于:

void *av_malloc(size_t size) __attribute__((__malloc__)) __attribute__((alloc_size(1)))

简单来讲,__attribute__只是让代码优化得更好而已,我们可以忽略。

(二)av_malloc函数的实现原理

av_malloc函数定义在libavutil/mem.c中:

#define HAVE_POSIX_MEMALIGN 1
#define HAVE_ALIGNED_MALLOC 0
#define HAVE_MEMALIGN 1
#define HAVE_AVX512 0
#define HAVE_AVX 0
#define CONFIG_MEMORY_POISONING 0
#define ALIGN (HAVE_AVX512 ? 64 : (HAVE_AVX ? 32 : 16))static atomic_size_t max_alloc_size = ATOMIC_VAR_INIT(INT_MAX);void *av_malloc(size_t size)
{void *ptr = NULL;if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed))return NULL;#if HAVE_POSIX_MEMALIGNif (size) //OS X on SDK 10.6 has a broken posix_memalign implementationif (posix_memalign(&ptr, ALIGN, size))ptr = NULL;
#elif HAVE_ALIGNED_MALLOCptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__ptr = memalign(ALIGN, size);
#elseptr = memalign(size, ALIGN);
#endif/* Why 64?* Indeed, we should align it:*   on  4 for 386*   on 16 for 486*   on 32 for 586, PPro - K6-III*   on 64 for K7 (maybe for P3 too).* Because L1 and L2 caches are aligned on those values.* But I don't want to code such logic here!*//* Why 32?* For AVX ASM. SSE / NEON needs only 16.* Why not larger? Because I did not see a difference in benchmarks ...*//* benchmarks with P3* memalign(64) + 1          3071, 3051, 3032* memalign(64) + 2          3051, 3032, 3041* memalign(64) + 4          2911, 2896, 2915* memalign(64) + 8          2545, 2554, 2550* memalign(64) + 16         2543, 2572, 2563* memalign(64) + 32         2546, 2545, 2571* memalign(64) + 64         2570, 2533, 2558** BTW, malloc seems to do 8-byte alignment by default here.*/
#elseptr = malloc(size);
#endifif(!ptr && !size) {size = 1;ptr= av_malloc(1);}
#if CONFIG_MEMORY_POISONINGif (ptr)memset(ptr, FF_MEMORY_POISON, size);
#endifreturn ptr;
}

去掉一大堆其它东西,av_malloc函数的核心实现就是:

void *av_malloc(size_t size)
{//...void *ptr = NULL;if (posix_memalign(&ptr, ALIGN, size)){ptr = NULL;}//...return ptr;}	

可以看到其本质就是调用了posix_memalign函数,该函数是Linux下内存对齐的函数,其作用是分配size大小的字节,并将分配的内存地址存放在ptr中。分配的内存的地址将是ALIGN的倍数,且必须是2的幂次方和sizeof(void*)的倍数。如果size为0,则函数返回NULL或一个唯一的指针值,以便可以成功传递给free函数。如果分配成功返回0该函数跟malloc函数相近。mallocposix_memalign函数跟mallocmalloc函数的区别是: malloc函数总是返回8字节对齐的内存地址,在64bits上是16字节对齐;而对于更大的边界,例如页面,需要动态的对齐的时候就可以选择posix_memalign函数。具体可以参考:posix_memalign(3) — Linux manual page

总结:av_malloc函数作用是分配一个适合所有内存访问的内存块,形参size为 要分配的内存块的字节大小。其底层实现就是调用了posix_memalign函数。

二、av_mallocz函数分析

(一)av_mallocz函数的声明

av_mallocz函数的声明放在在FFmpeg源码的头文件libavutil/mem.h中:

/*** Allocate a memory block with alignment suitable for all memory accesses* (including vectors if available on the CPU) and zero all the bytes of the* block.** @param size Size in bytes for the memory block to be allocated* @return Pointer to the allocated block, or `NULL` if it cannot be allocated* @see av_malloc()*/
void *av_mallocz(size_t size) av_malloc_attrib av_alloc_size(1);

根据注释,可以了解到该函数作用是分配一个适合所有内存访问的内存块
(包括CPU上可用的动态数组)并将块的所有字节归零。形参size:要分配的内存块的字节大小。
返回值指向已分配块的指针,如果不能分配,则为NULL。

(二)av_mallocz函数的实现原理

av_mallocz函数定义在libavutil/mem.c中:

void *av_mallocz(size_t size)
{void *ptr = av_malloc(size);if (ptr)memset(ptr, 0, size);return ptr;
}

可以看到其核心就是调用了av_malloc函数分配内存块,随后调用memset函数将该内存块的所有字节清零。因为av_mallocz函数会将内存块清零(相当于对内存块进行一个初始化操作)。所以在FFmpeg源码中一般使用av_mallocz,而不会直接使用av_malloc函数。

三、av_free函数分析

(一)av_free函数的声明

av_free函数的声明放在在FFmpeg源码的头文件libavutil/mem.h中:

/*** Free a memory block which has been allocated with a function of av_malloc()* or av_realloc() family.** @param ptr Pointer to the memory block which should be freed.** @note `ptr = NULL` is explicitly allowed.* @note It is recommended that you use av_freep() instead, to prevent leaving*       behind dangling pointers.* @see av_freep()*/
void av_free(void *ptr);

根据注释,可以了解到该函数作用是释放av_malloc()/av_mallocz()函数分配的内存块。形参ptr:指向应该释放的内存块的指针。
 

(二)av_free函数的实现原理

av_free函数定义在libavutil/mem.c中:

void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC_aligned_free(ptr);
#elsefree(ptr);
#endif
}

可以看到其核心就是调用free函数释放空间。

四、av_freep函数分析

(一)av_freep函数的声明

av_freep函数的声明放在在FFmpeg源码的头文件libavutil/mem.h中:

/*** Free a memory block which has been allocated with a function of av_malloc()* or av_realloc() family, and set the pointer pointing to it to `NULL`.** @code{.c}* uint8_t *buf = av_malloc(16);* av_free(buf);* // buf now contains a dangling pointer to freed memory, and accidental* // dereference of buf will result in a use-after-free, which may be a* // security risk.** uint8_t *buf = av_malloc(16);* av_freep(&buf);* // buf is now NULL, and accidental dereference will only result in a* // NULL-pointer dereference.* @endcode** @param ptr Pointer to the pointer to the memory block which should be freed* @note `*ptr = NULL` is safe and leads to no action.* @see av_free()*/
void av_freep(void *ptr);

根据注释,可以了解到该函数作用是释放av_malloc()/av_mallocz()函数分配的内存块,并设置指向它的指针为NULL 。

(二)av_freep函数的实现原理

av_freep函数定义在libavutil/mem.c中:

void av_freep(void *arg)
{void *val;memcpy(&val, arg, sizeof(val));memcpy(arg, &(void *){ NULL }, sizeof(val));av_free(val);
}

可以看到其核心就是调用了av_free函数释放空间。然后av_freep函数可以避免仅使用av_free函数释放空间后出现的悬挂指针,避免安全风险。所以av_freep函数比仅使用av_free函数更安全。

五、编写测试例子,来理解av_mallocz函数和av_freep函数的使用

通过上述的讲解,相信大家已经了解了FFmpeg源码中这几个内存相关的函数,也理解了FFmpeg 源码中C语言的设计艺术。就拿FFmpeg的内存相关的函数来讲,其设计艺术在于:av_mallocz函数申请、分配内存后,还会对该内存块进行初始化清零的操作;av_freep函数在释放内存块空间后会把指针指向NULL。我们可以把av_mallocz函数和av_freep函数从FFmpeg源码中抽取出来,移植到我们自己的C语言代码中使用。这样在不依赖FFmpeg库文件的情况下,我们也能使用FFmpeg里面的函数。这就是最简单的对FFmpeg裁剪。

编写测试例子main.c,在CentOS 7.5上通过10.2.1版本的gcc可以成功编译 :

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdatomic.h>
#include <limits.h>#define HAVE_POSIX_MEMALIGN 1
#define HAVE_ALIGNED_MALLOC 0
#define HAVE_MEMALIGN 1
#define HAVE_AVX512 0
#define HAVE_AVX 0
#define CONFIG_MEMORY_POISONING 0
#define ALIGN (HAVE_AVX512 ? 64 : (HAVE_AVX ? 32 : 16))static atomic_size_t max_alloc_size = ATOMIC_VAR_INIT(INT_MAX);#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#if AV_GCC_VERSION_AT_LEAST(3,1)#define av_malloc_attrib __attribute__((__malloc__))
#else#define av_malloc_attrib
#endif#if AV_GCC_VERSION_AT_LEAST(4,3)#define av_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__)))
#else#define av_alloc_size(...)
#endifvoid *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
void *av_mallocz(size_t size) av_malloc_attrib av_alloc_size(1);
void av_free(void *ptr);
void av_freep(void *ptr);void *av_malloc(size_t size)
{void *ptr = NULL;if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed))return NULL;#if HAVE_POSIX_MEMALIGNif (size) //OS X on SDK 10.6 has a broken posix_memalign implementationif (posix_memalign(&ptr, ALIGN, size))ptr = NULL;
#elif HAVE_ALIGNED_MALLOCptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__ptr = memalign(ALIGN, size);
#elseptr = memalign(size, ALIGN);
#endif/* Why 64?* Indeed, we should align it:*   on  4 for 386*   on 16 for 486*   on 32 for 586, PPro - K6-III*   on 64 for K7 (maybe for P3 too).* Because L1 and L2 caches are aligned on those values.* But I don't want to code such logic here!*//* Why 32?* For AVX ASM. SSE / NEON needs only 16.* Why not larger? Because I did not see a difference in benchmarks ...*//* benchmarks with P3* memalign(64) + 1          3071, 3051, 3032* memalign(64) + 2          3051, 3032, 3041* memalign(64) + 4          2911, 2896, 2915* memalign(64) + 8          2545, 2554, 2550* memalign(64) + 16         2543, 2572, 2563* memalign(64) + 32         2546, 2545, 2571* memalign(64) + 64         2570, 2533, 2558** BTW, malloc seems to do 8-byte alignment by default here.*/
#elseptr = malloc(size);
#endifif(!ptr && !size) {size = 1;ptr= av_malloc(1);}
#if CONFIG_MEMORY_POISONINGif (ptr)memset(ptr, FF_MEMORY_POISON, size);
#endifreturn ptr;
}void *av_mallocz(size_t size)
{void *ptr = av_malloc(size);if (ptr)memset(ptr, 0, size);return ptr;
}void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC_aligned_free(ptr);
#elsefree(ptr);
#endif
}void av_freep(void *arg)
{void *val;memcpy(&val, arg, sizeof(val));memcpy(arg, &(void *){ NULL }, sizeof(val));av_free(val);
}int main()
{uint8_t *pBuf = av_mallocz(128);if(pBuf){strcpy(pBuf, "hello world");printf("%s\n", pBuf);av_freep(&pBuf);if(!pBuf){printf("pBuf is free\n");}}return 0;
}

使用gcc编译,运行,输出如下:

六、参考文章

《FFmpeg5.0源码阅读——内存分配和释放》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/352281.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu网络管理命令:ifconfig

安装Ubuntu桌面系统&#xff08;虚拟机&#xff09;_虚拟机安装ubuntu桌面版-CSDN博客 关于ifconfig命令&#xff0c;在11.1节已经介绍过了。通过该命令可以查看和配置网络接口。ifconfig是一个比较古老的命令&#xff0c;在Ubuntu 22以及其他的许多发行版中&#xff0c;已经不…

【前端项目笔记】2 主页布局

主页布局 element-ui提供的组件名称就是它的类名 ☆☆ CSS选择器&#xff1a; &#xff08;1&#xff09;基本选择器 类型选择器 p/span/div…… 类选择器 (.classname) ID选择器 (#idname) 通配选择器 ( * ) &#xff08;2&#xff09;属性选择器 选择具有特定属性或属性值的…

最新下载:CrossOver 2024【软件附加安装教程】

CrossOver不像Parallels或VMware的模拟器&#xff0c;而是实实在在Mac OS X系统上运行的一个软件。CrossOvers能够直接在Mac上运行Windows软件与游戏&#xff0c;而不需虚拟机。它为Windows软件提供所需的资源&#xff0c;以达到在Mac OS X系统上运行Windows程序的目的。 安 装…

【网络安全学习】使用Kali做渗透情报收集-01-<域名信息主机信息>

1.收集开源情报 开源情报(Open Source Intelligence&#xff0c;OSINT)是指从各种公开的渠道中寻找和获取有价值的信息 如&#xff1a;互联网、媒体、社交网络、公共数据库等开源情报具有以下特点&#xff1a; - 丰富性&#xff1a;开源情报涵盖了各种类型和领域的信息 - 可…

linux centos consul1.15.2一键安装部署

consul原理、作用、安装相关内容 一、理论部分二、安装下载版本地址三、安装consul服务 一、理论部分 1、consul的原理 Consul的原理及作用可以归纳为以下几点&#xff1a; ①、基于Gossip协议的通信&#xff1a;Consul使用了基于Gossip协议的Serf实现来进行通信。 Gossip协议…

矩阵乘法的直觉

矩阵乘法是什么意思&#xff1f; 一种常见的观点是矩阵乘法缩放/旋转/倾斜几何平面&#xff1a; NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜…

分数限制下,选好专业还是选好学校?

目录 分数限制下&#xff0c;选好专业还是选好学校&#xff1f; 方向一&#xff1a;专业解析 1. 专业选择的重要性 2. 不同专业的优势与挑战 3. 个人专业选择经验分享 4. 实际场景下的“专业VS学校”选择方案 方向二&#xff1a;名校效应分析 1. 名校声誉与品牌效应 2…

Unity 使用TextMeshPro实现图文混排

最后实现出的效果是这样的 开始实现 准备两张图 选中图片右键->Create->TextMeshPro->Sprite Asset 然后文件夹内就会出现一个同名的这个文件 新建一个Text Inspector面板 点击最底下的Extra Settings 然后把刚刚创建的SpriteAsset拖过来 放到对应的地方 然后…

李沐:用随机梯度下降来优化人生!

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 今天我们来聊聊达叔 6 大核心算法之 —— 优化 算法。吴恩达&#xff1a;机器学习的六个核心算法&#xff01; 梯度下降优化算法是机器…

HarmoneyOS星河版 安装和启动

一、下载和安装DevEco Studio 官网链接&#xff1a;OpenAtom OpenHarmony 1.1 找到对应的操作系统进行下载 创建安装Harmony的文件夹&#xff1a; 1.2 下载后进行安装 1.3 分别安装Node、Ohpm、SDK 分别安装Node、Ohpm和SDK 二、.创建一个新项目并运行 2.1 选择[OpenHarmon…

当OpenHarmony遇上OpenEuler

1、 安装openEuler 虚拟机、物理机器当然都可以安装。虚拟机又可以使用WSL、或者VMWare、VirtualBox虚拟机软件&#xff0c;如果需要安装最新版本&#xff0c;建议使用后者。当前WSL只支持OpenEuler 20.03。 1.1 WSL openEuler WSL的安装都是程序员的必备技能了&#xff0c;…

大学课设项目,Windows端基于UDP的网络聊天程序的服务端和客户端

文章目录 前言项目需求介绍一、服务端1.对Udp套接字进行一个封装2. UdpServer的编写3. Task.h4.protocol.h的编写5.线程池的编写6.main.cc 二、客户端1. Socket.h2.protocol.h3.UdpClient4.menu.h5.main.cpp 三、运行图 前言 本次项目可以作为之前内容的一个扩展&#xff0c;学…

《数据安全产品及服务购买决策参考》

“新全球化”下的数据安全威胁态势与挑战 随着中国企业数字化转型和数字经济的高速发展&#xff0c;数据要素和数据安全的战略价值正不断提升。 同时&#xff0c;在“脱钩”与“新全球化”的全球政治经济博弈中&#xff0c;中国作为全球重要的数据安全市场之一&#xff0c;其…

LeetCode esay mid 记录

1486. 数组异或操作 感觉一般也用不到 emmm 灵茶山艾府传送门 推导过程可以结合官网部分观看 重点由两部分的结合 将特定部分转换为常见部分 0到n的异或和表示 2595. 奇偶位数 0x555是十六进制数&#xff0c;转换为二进制为 0101 0101 0101 class Solution {public int[…

90. 子集 II

90. 子集 II 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;_90子集II_递归法求子集_90子集II_迭代法求子集 错误经验吸取 原题链接&#xff1a; 90. 子集 II https://leetcode.cn/problems/subsets-ii/ 完成情况&#xff1a; 解题思路…

代码解读 | Hybrid Transformers for Music Source Separation[07]

一、背景 0、Hybrid Transformer 论文解读 1、代码复现|Demucs Music Source Separation_demucs架构原理-CSDN博客 2、Hybrid Transformer 各个模块对应的代码具体在工程的哪个地方 3、Hybrid Transformer 各个模块的底层到底是个啥&#xff08;初步感受&#xff09;&#xff1…

Vue48-ref属性

一、需求&#xff1a;操作DOM元素 1-1、使用原生的id属性 不太好&#xff01; 1-2、使用 ref属性 原生HTML中&#xff0c;用id属性给元素打标识&#xff0c;vue里面用ref属性。 给哪个元素加了ref属性&#xff0c;vc实例对象就收集哪个元素&#xff01;&#xff01;&#xff0…

WebSocket 详解--spring boot简单使用案例

一、什么是WebSocket WebSocket 是一种网络通信协议&#xff0c;专为在单个 TCP 连接上进行全双工通信而设计。WebSocket 允许客户端和服务器之间的消息能够实时双向传输。这与传统的 HTTP 请求-响应模式有很大的不同。 二、WebSocket 的关键特性 双向通信&#xff1a;WebSocke…

Novartis诺华制药社招综合能力性格动机问卷入职测评笔试题库答案及包过助攻

【华东同舟求职】由资深各行业从业者建立的一站式人才服务网络平台&#xff0c;现阶段目标是“提升全市场各行业岗位信息的流动性和透明度”。我们接受众多行业机构的直接委托发布&#xff0c;并尽力通过各种方法搜寻高价值岗位信息。事实上&#xff0c;我们以发现不为人知的优…

网站改成HTTPS方法

网站改成HTTPS只要网站没有特殊性的要求&#xff0c;绝大部分网站很轻松的就可以完成&#xff0c;尤其是CMS类似的网站系统或者自助搭建的网站&#xff08;比如&#xff1a;这种网站可以在网站后台一次性安装并且生效&#xff09;。 基本要求 将网站改成HTTPS有2个前提&#…