您的位置:首页 > 编程语言

关于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)的问题,其自动会为其分配内存,所以问题也出现在下面的一段代码:
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;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息