FFmpeg 解码内存泄漏汇总,sws_getContext函数无法释放问题

前面在使用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 使用的引用计数的机制来管理(即浅拷贝机制)

  • AVBufferFFmpeg中的缓冲区,一开始时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博客

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
sws_getContextFFmpeg中用于图像换的函数,其函数原型为: ``` struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param); ``` 参数说明: - srcW:源图像宽度 - srcH:源图像高度 - srcFormat:源图像像素格式 - dstW:目标图像宽度 - dstH:目标图像高度 - dstFormat:目标图像像素格式 - flags:换标志,可以为0或SWS_FAST_BILINEAR等 - srcFilter:源图像滤波器 - dstFilter:目标图像滤波器 - param:其它参数 使用示例: ``` // 初始化源图像和目标图像的宽度、高度和像素格式 int src_width = 640; int src_height = 480; AVPixelFormat src_pix_fmt = AV_PIX_FMT_RGB24; int dst_width = 320; int dst_height = 240; AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P; // 分配输入图像和输出图像所需的内存 uint8_t *src_data[4]; int src_linesize[4]; av_image_alloc(src_data, src_linesize, src_width, src_height, src_pix_fmt, 1); uint8_t *dst_data[4]; int dst_linesize[4]; av_image_alloc(dst_data, dst_linesize, dst_width, dst_height, dst_pix_fmt, 1); // 创建SwsContext struct SwsContext *sws_ctx = sws_getContext(src_width, src_height, src_pix_fmt, dst_width, dst_height, dst_pix_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); // 换图像 sws_scale(sws_ctx, src_data, src_linesize, 0, src_height, dst_data, dst_linesize); // 释放资源 sws_freeContext(sws_ctx); av_freep(&src_data[0]); av_freep(&dst_data[0]); ``` 该示例代码中,首先初始化源图像和目标图像的宽度、高度和像素格式,并分配输入图像和输出图像所需的内存。然后创建SwsContext,调用sws_scale函数实现图像换,并最后释放资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值