关于ffmpeg解码内存增加解决方案-替换解码代码

Linux环境:Ubuntu16.4
ffmpeg库版本:ffmpeg-3.4.1
问题:最近在弄ffmpeg视频解码,由于项目的需要,需要一直重复播放链表中挂在的图片,一直循环,但是遇到一个问题是,每次调用ffmpeg图像解码函数,使用top命令查看程序所占内存大小,发现每调用一次内存就增加一点,最后占掉了系统所有的内存,被系统防护机制杀掉了。
尝试解决:
1、遇到上述问题之后,我开始怀疑是不是我忘记释放申请的内存,然后一直找啊找,发现申请的该释放的已经释放,然后在最初的视频解码代码修修改改还是不行。
2、然后我使用一块固定内存存放packet,outbuffer,就不再每播放一次申请一次,但后面也是失败了。
问题发现:在论坛博客找了许久,应该是av_read_frame(pFormatCtx, packet)的问题,其自动会为其分配内存,所以问题也出现在下面的一段代码:
packet = (AVPacket *) av_malloc(sizeof(AVPacket));
av_new_packet(packet, unFrameSize)  //这里其实不用的,如果加了这里那么将分配一个没用到的内存,因为av_read_frame()会重新分配一块内存挂在Pframe上,所以这段内存将没有被用到,也不能被回收,所以解码没调用一次就内存就升高一点
解决方案:
1、删除掉:
av_new_packet(packet, unFrameSize)
2、使用ffmpeg新的解码方案:
/*************************************************
  Function:      
  Description:   
  Input:      
                 
  Output:         
  Return:     	 -1-失败   0-成功
  Author:        
  Date:          2018-01-01

  Modification History
  Author:
  Date:
  Description:
*************************************************/

INT32 videoPlay(char* fim)
{
    AVPacket packet;
    AVCodec *pCodec = NULL;
    AVCodecContext *origin_ctx = NULL, *ctx= NULL;
    AVFrame *pFrame = NULL;
    AVFormatContext *fmt_ctx = NULL;
	
    int video_stream;
    int got_frame = 0;
    int byte_buffer_size;
    int i = 0;
    int result;
    int end_of_stream = 0;
	

		
		/*打开输入文件*/
		result = avformat_open_input(&fmt_ctx, fileName, NULL, NULL);
    	if (result < 0) {
			   Console_Error("Couldn't open input Picture.\n");
			   continue;
		}
		
		/*获取文件视频流信息*/
		result = avformat_find_stream_info(fmt_ctx, NULL);
    	if (result < 0) {
			   Console_Error("Couldn't find stream information.\n");
			   continue;
		}

		/*获取视频编码格式*/
		video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    	if (video_stream < 0) {
			Console_Error("Didn't find a video stream.\n");
			continue;
		}

		origin_ctx = fmt_ctx->streams[video_stream]->codec;
		
		/*查找解码器*/
		pCodec = avcodec_find_decoder(origin_ctx->codec_id);
    	if (!pCodec) {
			Console_Error("Codec not found.\n");
			continue;
		}

		ctx = avcodec_alloc_context3(pCodec);
   	 	if (!ctx) {
			Console_Error("avcodec_alloc_context3.\n");
			continue;
		}

		result = avcodec_copy_context(ctx, origin_ctx);
    	if (result) {
			Console_Error("avcodec_alloc_context3.\n");
			continue;
		}

		/*打开解码器*/
		result = avcodec_open2(ctx, pCodec, NULL);
    	if (result < 0) {
			Console_Error("Could not avcodec_open2.\n");
			continue;
		}

		pFrame = av_frame_alloc();
    	if (!pFrame) {
			Console_Error("Could not av_frame_alloc .\n");
			continue;
		}

		/*指定格式图像所需的大小*/
		byte_buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, ctx->coded_width, ctx->coded_height, 1);
	    byte_buffer = av_malloc(byte_buffer_size);
	    if (!byte_buffer) {
			Console_Error("Could not av_malloc_byte_buffer .\n");
			continue;
		}
		
    	        i = 0;
		av_init_packet(&packet);
		
	do 
	{
	        if (!end_of_stream)
        	{
			if (av_read_frame(fmt_ctx, &packet) < 0)
	                end_of_stream = 1;
        	}
	           
	        if (end_of_stream) {
	            packet.data = NULL;
	            packet.size = 0;
	        }
	        if (packet.stream_index == video_stream || end_of_stream) 
		{
	            got_frame = 0;
				
	            if (packet.pts == AV_NOPTS_VALUE)
	                packet.pts = packet.dts = i;
				
				
				
	            result = avcodec_decode_video2(ctx, pFrame, &got_frame, &packet);
	            if (result < 0) {
	                printf("Error decoding frame\n");
	                continue ;
	            }
	            if (got_frame) {
			byte_buffer_size = av_image_copy_to_buffer(byte_buffer, byte_buffer_size,
					(const UINT8* const *)pFrame->data, (const int*) pFrame->linesize,
						AV_PIX_FMT_YUV420P, ctx->coded_width, ctx->coded_height, 1);
			if (byte_buffer_size < 0) {
				printf("Can't copy image to buffer\n");
				return byte_buffer_size;
			}                                                                                                                   //dosomethinghere
	            }
	            av_packet_unref(&packet);
	            av_init_packet(&packet);
	        }
	        i++;
    } while (!end_of_stream || got_frame);

	av_packet_unref(&packet);
    av_frame_free(&pFrame);
    avcodec_close(ctx);
    avformat_close_input(&fmt_ctx);
    avcodec_free_context(&ctx);
    av_freep(&byte_buffer);
    }
    return 0;
}

一些说明:在使用 av_packet_alloc 创建packet的时候,并没有给数据域分配空间,数据域的空间实在 av_read_frame 内分配的,所以在每次循环的结束不能忘记调用 av_packet_unref 减少数据域的引用技术,当引用技术减为0时,会自动释放数据域所占用的空间。在循环结束后,调用 av_packet_free 来释放 AVPacket 本身所占用的空间。

附:音视频解码播放代码,100%解决了内存增加问题,完整释放各种动态申请的变量:



INT32 videoPlay(void)
{
	AVPacket packet;
    AVFormatContext *pfmt_ctx = NULL;

	AVFrame *pFrame = NULL;
	AVCodec *pCodec = NULL;
    AVCodecContext *porigin_ctx = NULL, *pctx= NULL;
    
	AVFrame *paudioFrame = NULL;
	AVCodecContext  *paudioCodecCtxOrig 	= NULL;
	AVCodecContext  *paudioCodecCtx			= NULL;
	AVCodec         *paudioCodec 			= NULL;

    INT32 nvideo_stream =-1;
	INT32 naudio_stream =-1;
	
    INT32 ngot_frame = 0;
    INT32 nbyte_buffer_size = 0;
	INT32 number_of_written_bytes = 0;
    INT32 ni 		= 0;
    INT32 nresult 	=-1;
    INT32 nend_of_stream 		= 0;
	UINT32 nCnt = 0,naudioCnt = 0;
	INT8* ptmp = NULL;
	UINT8 *pucbyte_buffer = NULL;
	ULONG unTmpFrameSize 	= 0;
	ULONG unFrameSize 		= 0;


	/*打开输入文件*/
	nresult = avformat_open_input(&pfmt_ctx, g_Advers.ucCurrentPlayAvderName, NULL, NULL);
        if (nresult < 0) {
		   Console_Error("Couldn't open input stream.\n");
		   /*进入这里表名文件已更改,所以需要重新更新广告列表*/
		   ni = 0;
		   adver_listUpdate(ptmp,ptmp,ni);
		   return -1;
	 }

	/*获取文件视频流信息*/
	nresult = avformat_find_stream_info(pfmt_ctx, NULL);
	if (nresult < 0) {
		   Console_Error("Couldn't find stream information.\n");
		   return -1;
	}

	/*获取视频编码格式*/
	nvideo_stream = av_find_best_stream(pfmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	naudio_stream = av_find_best_stream(pfmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
	if (nvideo_stream < 0|| naudio_stream < 0) {
		Console_Error("Didn't find a video stream.\n");
		return -1;
	}

	/*查找解码器*/
	porigin_ctx = pfmt_ctx->streams[nvideo_stream]->codec;
	paudioCodecCtxOrig = pfmt_ctx->streams[naudio_stream]->codec;

        pCodec = avcodec_find_decoder(porigin_ctx->codec_id);
	paudioCodec = avcodec_find_decoder(paudioCodecCtxOrig->codec_id);
        if (!pCodec||!paudioCodec) {
		Console_Error("Codec not found.\n");
		return -1;
	}

	pctx = avcodec_alloc_context3(pCodec);
	paudioCodecCtx = avcodec_alloc_context3(paudioCodec);
       if (!pctx||!paudioCodecCtx) {
		Console_Error("avcodec_alloc_context3 error.\n");
		return -1;
	}

	nresult = avcodec_copy_context(pctx, porigin_ctx);
	if (nresult) {
		Console_Error("avcodec_copy_context error.\n");
		return -1;
	}
	
	nresult = avcodec_copy_context(paudioCodecCtx, paudioCodecCtxOrig);
       if (nresult) {
		Console_Error("avcodec_copy_context error.\n");
		return -1;
	}

	/*打开解码器*/
	nresult = avcodec_open2(pctx, pCodec, NULL);
       if (nresult < 0) {
		Console_Error("videoCodec_open2 error.\n");
		return -1;
	}
	
	nresult = avcodec_open2(paudioCodecCtx, paudioCodec, NULL);
       if (nresult < 0) {
		Console_Error("audioCodec_open2 error.\n");
		return -1;
	}

	pFrame = av_frame_alloc();
	paudioFrame = av_frame_alloc();
        if (!pFrame||!paudioFrame) {
		Console_Error("av_frame_alloc error.\n");
		return -1;
	}

	/*指定格式图像所需的大小*/
	nbyte_buffer_size = av_image_get_buffer_size(pctx->pix_fmt, pctx->coded_width, pctx->coded_height, 1);
        pucbyte_buffer = av_malloc(nbyte_buffer_size);
       if (!pucbyte_buffer) {
		Console_Error("Could not av_malloc_byte_buffer .\n");
		return -1;
	}

	avpicture_fill((AVPicture *) pFrame, pucbyte_buffer, pctx->pix_fmt,
				pctx->coded_width, pctx->coded_height);
	
        av_init_packet(&packet);
	/*读取视频*/
	do 
	{
		if (!nend_of_stream)
		{
			 if (av_read_frame(pfmt_ctx, &packet) < 0)
				nend_of_stream = 1;
		}
		if (nend_of_stream) {
			packet.data = NULL;
			packet.size = 0;
		}
		if (packet.stream_index == nvideo_stream || nend_of_stream) 
		{
			ngot_frame = 0;
			
			if (packet.pts == AV_NOPTS_VALUE)
				packet.pts = packet.dts = ni;
			
			nresult = avcodec_decode_video2(pctx, pFrame, &ngot_frame, &packet);
			if (nresult < 0) {
				printf("Error decoding frame\n");
				continue ;
			}
			if (ngot_frame) {
				 number_of_written_bytes = av_image_copy_to_buffer(pucbyte_buffer, nbyte_buffer_size,
	                                        (const UINT8* const *)pFrame->data, (const int*) pFrame->linesize,
	                                        pctx->pix_fmt, pctx->coded_width, pctx->coded_height, 1);
	                if (number_of_written_bytes < 0) {
	                    printf("Can't copy image to buffer\n");
	                    continue;
	                }
				/*将解码出来的视频流传送到显示模块*/
				//displayWrite(ADVER_HANDLE_ID,pucbyte_buffer,&picFrameInfo);
			}
			av_packet_unref(&packet);
			av_init_packet(&packet);
		}
		else if(packet.stream_index == naudio_stream||nend_of_stream)
		{
			
		       /*在这里给帧提供paudioCodecCtx和paudioFrame,已经解好码了,等音频模块对解码后的数据进行加工*/
			nresult = audioplaystream(paudioCodecCtx, &packet);

			av_packet_unref(&packet);
			av_init_packet(&packet);
		}

	} while (!nend_of_stream || ngot_frame);
	
	
	/*释放资源*/
	av_packet_unref(&packet);
	av_free_packet(&packet);
        av_frame_free(&pFrame);
	av_frame_free(&paudioFrame);
	
        avcodec_close(pctx);
	avcodec_close(porigin_ctx);
	avcodec_close(paudioCodecCtx);
	avcodec_close(paudioCodecCtxOrig);
	
	avcodec_free_context(&pctx);
	avcodec_free_context(&paudioCodecCtx);
	
        avformat_close_input(&pfmt_ctx);
	avformat_free_context(pfmt_ctx);
	
        av_freep(&pucbyte_buffer);
	return 0;
}




FFmpeg是一个开源的跨平台音视频处理库,它提供了丰富的功能和接口,包括解码、编码、转码、过滤等。在使用FFmpeg进行解码时,内存泄漏是一个常见的问题内存泄漏指的是程序在运行过程中分配的内存没有被正确释放,导致内存占用不断增加,最终可能导致程序崩溃或者系统资源耗尽。在mpeg解码过程中,内存泄漏可能出现在以下几个方面: 1. 未释放AVPacket和AVFrame:在解码过程中,AVPacket用于存储解码前的数据,AVFrame用于存储解码后的数据。如果在使用完AVPacket和AVFrame后没有正确释放它们,就会导致内存泄漏。 2. 未释放解码器上下文:解码器上下文(AVCodecContext)是用于存储解码器相关的信息和状态的结构体。如果在解码完成后没有正确释放解码器上下文,就会导致内存泄漏。 3. 未释放解码器:解码器(AVCodec)是用于实际进行解码操作的结构体。如果在使用完解码器后没有正确释放它,就会导致内存泄漏。 为了避免内存泄漏,可以采取以下几个措施: 1. 在使用完AVPacket和AVFrame后,调用相应的释放函数进行内存释放,如av_packet_unref()和av_frame_free()。 2. 在解码完成后,调用avcodec_free_context()函数释放解码器上下文。 3. 在不再需要解码器时,调用avcodec_free_context()函数释放解码器。 4. 在程序退出前,确保所有的内存都被正确释放。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值