关于ffmpeg解码内存增加解决方案-替换解码代码
2018-01-18 10:29
369 查看
Linux环境:Ubuntu16.4
ffmpeg库版本:ffmpeg-3.4.1问题:最近在弄ffmpeg视频解码,由于项目的需要,需要一直重复播放链表中挂在的图片,一直循环,但是遇到一个问题是,每次调用ffmpeg图像解码函数,使用top命令查看程序所占内存大小,发现每调用一次内存就增加一点,最后占掉了系统所有的内存,被系统防护机制杀掉了。尝试解决:1、遇到上述问题之后,我开始怀疑是不是我忘记释放申请的内存,然后一直找啊找,发现申请的该释放的已经释放,然后在最初的视频解码代码修修改改还是不行。2、然后我使用一块固定内存存放packet,outbuffer,就不再每播放一次申请一次,但后面也是失败了。问题发现:在论坛博客找了许久,应该是av_read_frame(pFormatCtx, packet)的问题,其自动会为其分配内存,所以问题也出现在下面的一段代码:
一些说明:在使用
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");
/*进
b5f8
入这里表名文件已更改,所以需要重新更新广告列表*/
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-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");
/*进
b5f8
入这里表名文件已更改,所以需要重新更新广告列表*/
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有用的笔记,关于增加新解码方式
- iOS关于使用七牛SDK上传多张图片内存不断增加问题的解决方案
- 联想扬天A4680R台式电脑增加内存不识别的解决方案
- 利用ffmpeg解码h264流的代码
- extjs4.2一种导致内存泄 4000 露的代码写法及解决方案
- cordova angular 中动态增加的 js和html代码 不可用的解决方案
- iOS NSSting 关于字符串的查找、截取、拆分、替换、验证、改变大小写、编码解码等
- extjs4.2一种导致内存泄露的代码写法及解决方案
- asp中HTML编码和解码,用来替换输入内容中的HTML代码
- 一段关于C内存分配的奇怪代码
- 关于代码调用SSP获取UserProfile出错的解决方案(转)
- 关于eclipse修改代码重启问题解决方案
- MFC 关于自动更新问题的解决方案和代码
- 联想扬天A4680R台式电脑增加内存不识别的解决方案
- 关于处理java从服务器读取图片然后上传替换后无法更新缓存的解决方案
- 关于代码调用SSP获取UserProfile出错的解决方案
- 浅谈一个线程通信代码的内存泄露及解决方案
- 关于平方根的最大下取整数的java代码解决方案
- mysql官方关于2013和2006错误代码产生原因和解决方案
- 关于内存对齐,写代码中需要注意的事项