前面在使用atlas 200开发板进行ffmpeg库进行拉流+dvpp硬件解码+npu进行yuv转码rgb时,进行长时间连续解码和压力测试,会有明显内存增加问题。连续工作21个小时后就会被linux 内核kill掉。
通过逐步注掉代码、和华为技术人员沟通等方式。最后发现内存泄漏有几个地方,这里不讲npu转码部分,只提ffmpeg拉流和解码(本次没用到)可能存在的内存泄漏的风险:
一、av_read_frame的问题
av_read_frame有内存泄漏风险,av_read_frame 每次循环后必须执行av_packet_unref(pkt)进行释放。
对于多个 AVPacket 共享同一个缓存空间,FFmpeg 使用的引用计数的机制来管理(即浅拷贝机制)
AVBuffer
是FFmpeg
中的缓冲区,一开始时AVBuffer
的引用计数(refcount
)初始化为 0- 当有新的
Packet
引用共享的缓存空间时,就将引用计数再 +1 - 当
Packet
释放掉对AVBuffer
这块共享缓存空间的引用时,将引用计数 -1 - 只有当
refcount
为 0 的时候,才会释放掉缓存空间AVBuffer
如果在循环内退出需要释放pkt,如果作为while的条件不成立时需要在while代码块之外也需要释放。最后需要执行av_packet_free和av_free再进行释放packet的外壳。
while (av_read_frame(pa->fmt_ctx, pkt) >= 0)
{
int64_t dts;
if(pkt->stream_index != pa->videoStream)
{
av_packet_unref(pkt);
continue;
}
av_packet_unref(pkt);
}
av_packet_unref(pkt);
if(pa->packet != NULL)
{
av_packet_unref(pa->packet);
}
av_packet_free(&pa->packet);
av_free(pa->packet);
av_packet_alloc
和 av_packet_free
必须要配对使用,否则会造成内存泄漏。av_packet_free
实际是释放AVPacket 的空间。
FFmpeg 中 av_init_packet() 和 av_packet_alloc()av_packet_unref() 三者的区别以及用法
av_packet_alloc 中并没有调用 av_init_packet , 但av_packet_alloc 中调用了 av_packet_unref ,而 av_packet_unref 内部才调用 av_init_packet (老版本的 ffmpeg 中 av_packet_alloc 是直接调用 av_init_packet )
在注意,在 av_init_packet 中初始化并不涉及 data 和 size 成员,它们必须分别初始化。
av_new_packet 的作用是为 pkt 分配一个指定大小的内存。av_new_packet 中为 pkt 的 buf 分配一个大小,且内部还调用了 av_init_packet 对 pkt 进行初始化。而且注意,该函数的返回值并不是引用计数,之前在网上看到有些博客写返回的是引用计数(是不对的),这个函数返回值永远都是 0,没有具体意义。
一般在开发过程中都会遵循以下几步
AVPacket *pkt = NULL;
pkt = av_packet_alloc();
ret = av_new_packet(pkt, MEM_ITEM_SIZE);
av_packet_free(&pkt);
下面通过一个测试示例来应用下这几个 API
#define MEM_ITEM_SIZE (20*1024*102)
void av_packet_test1()
{
AVPacket *pkt = NULL;
int ret = 0;
pkt = av_packet_alloc();
ret = av_new_packet(pkt, MEM_ITEM_SIZE);
//把数据写到data区域, av_init_packet 内部初始化并不是涉及 data 和 size
memccpy(pkt->data,(void*)&av_packet_test1,1,MEM_ITEM_SIZE);
av_packet_free(&pkt);
}
av_packet_alloc 只是为 pkt 分配了内存空间,buf 依然为 NULL。在 av_new_packet 之后真正的为 buf 分配内存空间。
av_packet_free 先把 pkt 中的内容清空,然后再把指针清空,让 pkt 彻底无法使用了,如果需要重新使用,需要重新分配内存。
二、AVFrame* 结构体的释放
AVFrame结构体需要
1、先执行av_frame_unref()释放内部
2、调用av_frame_free(&pa->srcFrame);释放外壳
三、注意释放顺序
内存释放要按顺序进行释放。以防释放掉外壳后,内部就不能进行释放了。
如下面是释放上下文的顺序。
sws_freeContext(pSwsContext); av_frame_free(&pAVFrame); avcodec_close(pAVCodecContext); avformat_close_input(&pAVFormatContext);
四、sws_getContext函数无法释放问题
经过反复查找,发现sws_getContext函数调用后存在内存泄漏问题。非常奇怪的是
程序中已调用了sws_freeContext(pa->sws_ctx)进行释放,内存依然泄漏。
后发现有类型函数 sws_getCachedContext(),替换后发现内存不再泄漏!!!!
参考:FFmpeg开发笔记(四):ffmpeg解码的基本流程详解_ffmpeg3.x解码流程图_长沙红胖子Qt的博客-CSDN博客