=================================================================
AVPacket结构体和其相关的函数分析:
FFmpeg存放压缩后的音视频数据的结构体:AVPacket简介
FFmpeg源码:av_init_packet、get_packet_defaults、av_packet_alloc函数分析
FFmpeg源码:av_packet_free_side_data、av_packet_unref、av_packet_free函数分析
FFmpeg源码:packet_alloc、av_new_packet、av_shrink_packet、av_grow_packet函数分析
FFmpeg源码:av_packet_move_ref、av_packet_make_refcounted函数分析
FFmpeg源码:PacketList结构体、avpriv_packet_list_put、avpriv_packet_list_get、avpriv_packet_list_free函数分析
FFmpeg源码:append_packet_chunked、av_get_packet、av_append_packet函数分析
FFmpeg中调用av_read_frame函数导致的内存泄漏问题
=================================================================
使用FFmpeg的av_read_frame函数后,每读完一个packet,必须调用av_packet_unref函数进行内存释放,否则会导致内存释泄漏。
在vs(博主所用的ffmpeg版本是3.4.2,vs版本是vs2015)中编译运行如下代码:
#include <stdio.h>
#include <iostream>
#include <windows.h>
extern "C" {
#include "libavformat/avformat.h"
}
int main()
{
const char *path = "video1.mp4"; //需要读取的本地媒体文件相对路径为video1.mp4
av_register_all(); //初始化所有组件,只有调用了该函数,才能使用复用器和编解码器。否则,调用函数avformat_open_input会失败,无法获取媒体文件的信息
avformat_network_init(); //打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
AVDictionary *opts = NULL;
AVFormatContext *ic = NULL;
int re = avformat_open_input(&ic, path, NULL, &opts); //媒体打开函数,调用该函数可以获得路径为path的媒体文件的信息,并把这些信息保存到指针ic指向的空间中(调用该函数后会分配一个空间,让指针ic指向该空间)
if (re != 0) //如果打开媒体文件失败,打印失败原因
{
char buf[1024] = { 0 };
av_strerror(re, buf, sizeof(buf) - 1);
printf("open %s failed!:%s", path, buf);
}
else //打开媒体文件成功
{
printf("open %s success!\n", path);
avformat_find_stream_info(ic, NULL); //调用该函数可以进一步读取一部分视音频数据并且获得一些相关的信息
av_dump_format(ic, 0, path, 0); //输出媒体文件的信息到控制台上
AVPacket *pkt = av_packet_alloc();
while (1)
{
int ret = av_read_frame(ic, pkt);
if (ret != 0)
{
printf("\n--------------------------end--------------------------");
break;
}
//av_packet_unref(pkt);
Sleep(2); //睡眠2ms,避免程序执行过快影响观察效果。
}
av_packet_free(&pkt);
}
if (ic)
{
avformat_close_input(&ic);
}
getchar();
return 0;
}
可以看到上述代码的第38行将av_packet_unref(pkt);这一行注释掉了,所以会导致内存泄漏。运行效果如下图所示。可以在vs2015的诊断工具里面观察到进程运行的过程中(在控制台打印"--------------------------end--------------------------"之前)占用的内存越来越大。在任务管理器中观察到该进程内存占用也是越来大。
我们把上述代码的第38行的av_packet_unref(pkt);加入到代码中,再次编译运行。运行效果如下图所示,可以观察到,进程运行过程中,内存基本上是不变的。
综上所述:使用FFmpeg要注意内存泄漏的问题,av_read_frame中会申请内存,需要在外面进行释放,所以每读完一个packet,需要调用av_packet_unref进行内存释放。 如果不熟悉FFmpeg的函数,可以使用vs诊断工具帮助检测定位其内存泄漏,具体方法可以参考博主的另一篇文章《使用vs诊断工具检测FFmpeg的内存泄漏问题》。