FFmpeg内存管理

时间:2021-10-24 00:33:24

FFmpeg 是一个 C程序的项目,C语言是需要手动管理内存的。内存管理有一个技巧,只要你分清楚这个变量是在栈上,还是在堆上的就可以了。在栈上的变量不需要手动释放,而在堆上的变量需要自己释放。

下面通过一个实例, 通过 反汇编 来讲解一下 栈变量 为什么不需要自己释放,代码如下:

int add_something(int a, int b, int c) {
int d = 4;
int e = 5;
int f = 6;
return a + b + c + d + e + f;
}

int sub_something(int a, int b, int c) {
int g = 7;
int h = 8;
int j = 9;
return a + b + c - g - h - j;
}

int main() {
int a = 1;
int b = 2;
int c = 3;
int total;
total = add_something(a, b, c);
total = sub_something(a, b, c);
return total;
}

首先,一个线程在大多数系统上会默认有1M的栈内存,这个大小可以通过配置修改。也就是一个线程创建的时候,默认就会先申请1M的栈内存。

有些同学可能会觉得,1M 是不是有点小,如果定义了太多的局部变量,用完这 1M 怎么办?用完了就会报错,如果你的程序访问了 1M 地址之外的内存,就会报 segment fault 错误。

补充:在 Windows 系统下,可能会根据需要动态调整线程的栈内存大小,就是不够用了会动态扩大,具体我需要测试一下验证。

我们知道,函数调用会先 压栈,然后再弹栈,但是还有一个地方也会用到栈内存,就是局部变量,你每创建一个局部变量,栈就会变得更小一点。

首先,栈内存的访问通常是由两个寄存器控制的。如下:

1,​​esp​​ ,栈寄存器,存储的是当前的栈地址,会随着代码运行不断地变化

2,​​ebp​​,栈顶寄存器,存储的是栈顶的地址,变化较少。

现在反汇编一下上面的代码,如下:

FFmpeg内存管理

可以看到,进入 ​​add_something​​ 函数的时候,入口那里,有个 ​​sub​​ 指令,这个指令会直接切走一块栈内存,给 局部变量用。也不仅仅是给局部变量用,也有其他用途。

我们注意看,现在 EBP 寄存器的值是 0x00B5F630,也就是 d 变量的地址是 0x00B5F630 - 4 = 0x00B5F62C ,为什么我们不需要用 free 函数释放 0x00B5F62C 地址的内存呢?我们继续断点,进入 ​​sub_something​​ 函数,就清楚了,如下:

FFmpeg内存管理

可以看到,g 变量的内存地址也是 0x00B5F630 - 4 = 0x00B5F62C,所以当 ​​sub_something​​ 执行的时候,之前 ​​add_something​​ 函数里面的 局部变量 d,e,f 指向的内存就会被覆盖。

所以栈内存,是一个可以不断重复使用的内存。所以局部变量不需要释放,只要离开了函数的作用域,之前的局部变量内存就会被重新覆盖使用。

扩展知识:函数调用,压栈,弹栈,会不断修改恢复 ​​ebp​​ 跟 ​​esp​​。所以栈内存能不断被重复使用。​​main​​ 也是一个函数,​​main​​ 的外面其实还有一层函数调它。


下面开始介绍一下 FFmpeg 跟内存相关的函数,如下:

1,​​av_frame_alloc​​,创建一个 ​​AVFrame​​ 结构的堆内存。对应 ​​av_frame_free​

2,​​av_packet_alloc​​,创建一个 ​​AVPacket​​ 结构的堆内存。对应 ​​av_packet_free​

3,​​avformat_alloc_context​​,创建一个 ​​AVFormatContext​​ 结构的堆内存。对应 ​​avformat_close_input​​ 或者 ​​avformat_free_context​

4,​​avcodec_alloc_context3​​,创建一个 ​​AVCodecContext​​ 结构的堆内存。对应 ​​avcodec_close​​ 或者 ​​avcodec_free_context​

5,​​av_malloc​​,申请内存。对应 ​​av_free​​ 跟 ​​av_freep​​ ,其中 ​​av_freep​​ 函数接受的是二级指针,所以会把指针置为 NULL。这样做可以减少错误。