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

news2024/11/25 20:55:39

一、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_MEMALIGN
    if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation
    if (posix_memalign(&ptr, ALIGN, size))
        ptr = NULL;
#elif HAVE_ALIGNED_MALLOC
    ptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__
    ptr = memalign(ALIGN, size);
#else
    ptr = 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.
     */
#else
    ptr = malloc(size);
#endif
    if(!ptr && !size) {
        size = 1;
        ptr= av_malloc(1);
    }
#if CONFIG_MEMORY_POISONING
    if (ptr)
        memset(ptr, FF_MEMORY_POISON, size);
#endif
    return 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);
#else
    free(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(...)
#endif


void *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_MEMALIGN
    if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation
    if (posix_memalign(&ptr, ALIGN, size))
        ptr = NULL;
#elif HAVE_ALIGNED_MALLOC
    ptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__
    ptr = memalign(ALIGN, size);
#else
    ptr = 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.
     */
#else
    ptr = malloc(size);
#endif
    if(!ptr && !size) {
        size = 1;
        ptr= av_malloc(1);
    }
#if CONFIG_MEMORY_POISONING
    if (ptr)
        memset(ptr, FF_MEMORY_POISON, size);
#endif
    return 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);
#else
    free(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.coloradmin.cn/o/1832840.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nvm 报错https://npm.taobao.org/mirrors/node/index.json 淘宝镜像更换

文章目录 一、问题背景二、解决问题1. 获取配置文件的位置2. 修改配置文件中的镜像源配置3. 修改 npm 镜像源 一、问题背景 使用nvm的时候报错: Could not retrieve https://npm.taobao.org/mirrors/node/index.json. 由于淘宝的镜像域名更换&#xff0c;npm.taobao.org 域名…

Airflow任务流调度

0 前言 Airflow是Airbnb内部发起的一个工作流管理平台。使用Python编写实现的任务管理、调度、监控工作流平台。Airflow的调度依赖于crontab命令&#xff0c;与crontab相比&#xff0c;Airflow可以方便地查看任务的执行状况&#xff08;执行是否成功、执行时间、执行依赖等&…

Elixir学习笔记——自定义符号

Elixir 提供双引号字符串以及一个称为 charlists 的概念&#xff0c;它们使用 ~c“hello world”符号语法定义。在本章中&#xff0c;我们将了解有关符号的更多信息以及如何定义我们自己的符号。 Elixir 的目标之一是可扩展性&#xff1a;开发人员应该能够扩展语言以适应任何特…

Vatee万腾平台,让智能更懂你

在数字化浪潮席卷全球的今天&#xff0c;智能科技已经渗透到我们生活的方方面面。然而&#xff0c;真正的智能不仅仅是技术的堆砌&#xff0c;更是对人性需求的深刻理解和满足。Vatee万腾平台&#xff0c;正是这样一个让智能更懂你的平台&#xff0c;它以其独特的方式&#xff…

select 下拉框不可选

select 下拉框不可选 disabled和readonlyselect 下拉框不可选择CSS pointer-events 属性 通常情况下&#xff0c;设置表单输入框不可操作的时候会选择使用disabled或者readonly&#xff0c;那么disabled和readonly有什么区别呢&#xff1f; disabled和readonly 首先来说这两个…

vscode插件开发之 - 消息通信

在开发vscode插件过程中&#xff0c;有一个典型场景是webview与extension.ts进行通信&#xff0c;例如&#xff0c;webview上的某些信息发送改变时&#xff0c;需要发送消息传递给extension.ts. 如果使用react框架构建vscode插件的webview&#xff0c;如何实现webview与extensi…

怎么把两个音频合成一个?将两个音频合成一个的四种方法

怎么把两个音频合成一个&#xff1f;在当今数字化的时代&#xff0c;音频处理已经成为我们生活中不可或缺的一部分。有时候&#xff0c;我们会希望将两段音频合成为一个&#xff0c;无论是为了制作音乐混音、创作声音效果&#xff0c;还是为了编辑播客节目或视频配音。合成音频…

硕思闪客精灵软件最新版下载及详细安装教程

闪客精灵&#xff08;Sothink SWF Decompiler&#xff09;是一款先进的SWF反编译软件&#xff0c;它不但能捕捉、反编译、查看和提取Shock Wave Flash影片&#xff08;.swf和.exe格式文件&#xff09;&#xff0c;而且可以将SWF格式文件转化为FLA格式文件。 安 装 包 获 取 地 …

CentOS搭建kubernetes集群详细过程(yum安装方式)

kubernetes集群搭建详细过程&#xff08;yum安装方式&#xff09; Kubernetes&#xff0c;也被称为K8s&#xff0c;是一个多功能的容器管理工具&#xff0c;它不仅能够协调和调度容器的部署&#xff0c;而且还能监控容器的健康状况并自动修复常见问题。这个平台是在谷歌十多年…

性能测试、负载测试、压力测试、稳定性测试简单区分【超详细】

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 性能测试是一个总称&#xff0c;可细分为性能测试、负载测试、压力测试、稳定性测试。 性能测试…

XZ后门故事:初始分析

2024年3月29日&#xff0c;Openwall OSS安全邮件列表上的一条消息“炸醒”了整个信息安全、开源和Linux社区&#xff1a;XZ出现了一个CVSS评分10.0的恶意后门。 这个后门库的特殊危险在于OpenSSH服务器进程sshd使用它。在多个基于systemd的发行版上&#xff08;包括Ubuntu、De…

二叉树-根据先序遍历和中序遍历序列重建二叉树

目录 一、问题描述 二、解题思路 1.首先明确先序遍历和中序遍历的性质&#xff1a; 2.确定根节点及左右子树 3.对子树进行递归操作 4.递归返回条件 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 1.首先明确先序遍历和中序遍历的性质&#xff1a; 先序遍历&am…

基于ChatGPT-4o自然科学研究全流程实践技术应用

自然科学研究遵循严谨的科学方法论&#xff0c;包括文献调研、问题综述、试验设计、提出假设、数据清洗、统计诊断、大数据分析、经典统计模型&#xff08;回归模型、混合效应模型、结构方程模型、Meta分析模型&#xff09;、参数优化、机器/深度学习、大尺度模型构建与模拟、论…

Centos7 安装oracle 11.2.0.4

荆轲刺秦王 1. 准备工作 需要下载 Oracle 11g 安装包 2.HostName修改&#xff1a; hostnamectl set-hostname oracle 3. 配置hostname&#xff08;本机IP映射&#xff09;注意&#xff1a;192.168.116.129 需要换乘本地ip vi /etc/hosts 192.168.116.129 oracle # 测试hos…

企业用户使用OV SSL证书趋势增长

随着网络安全的需求度日益提高&#xff0c;https证书也成为了当下最受欢迎的数字证书之一&#xff0c;主要是用于保护网站和应用程序的安全&#xff0c;并提升用户对网站的信任度&#xff0c;且只有企业或组织才可申请。 OV SSL证书全称Organization Validation SSL(组织验证性…

【前端】Nesj 学习笔记

1、前置知识 1.1 装饰器 装饰器的类型 declare type ClassDecorator <TFunction extends Function>(target: TFunction) > TFunction | void; declare type PropertyDecorator (target: Object, propertyKey: string | symbol) > void; declare type MethodDe…

CMSIS-RTOS2简介

本文介绍CMSIS-RTOS2。 1.引入 CMSIS-RTOS2在基于Arm Cortex处理器的设备上运行的实时操作系统内核上指定了通用RTOS接口。应用程序和中间件组件可以使用CMSIS-RTOS2 API在各种软件生态系统中实现更好的代码重用和更简单的集成。 CMSIS-RTOS2还指定了RTOS内核使用的标准OS T…

机械师电脑文件丢失怎么办?6个恢复方法,希望能帮到您

机械师电脑作为高性能的计算机品牌&#xff0c;受到众多用户的青睐。然而&#xff0c;即便是品质卓越的电脑&#xff0c;也难免会遇到文件丢失的困扰。无论是由于误操作、系统故障还是硬盘损坏&#xff0c;文件丢失都可能给用户带来不小的麻烦。当您发现机械师电脑上的文件突然…

使用Midjourney为产品创建出色效果图-关键词

使用MJ为产品创建效果图并不难&#xff0c;可以使用这个固定提示词公式。 Mockup empty, blank [ product ], [ decorating items ] [ background or context ], [ 1- 3 descriptive style], [ color palette ] 创建产品形象 首先&#xff0c;你需要准备一个透明背景的产品。…

基于JSP的二手车交易网站

开头语&#xff1a; 你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果你对二手车交易网站感兴趣或有相关开发需求&#xff0c;欢迎随时联系我。我的联系方式可以在文末找到。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSPJava 工具&#…