本文所给出的是在使用AVAudioFifo处理buffer时出现内存泄漏问题的解决办法
受项目的限制,FFmpeg的版本为2.4.6
没有验证新版本是否还有这样的问题,但可以用做参考。
首先以下公共的部分不变:
AVCodecContext* m_pCodecCtx; //AVCodecContext的初始化部分省略
AVAudioFifo* m_pAudioFifo;
void init_frame(AVFrame* &pFrame)
{
//初始化AVFrame
pFrame = av_frame_alloc();
pFrame->format = m_pCodecCtx->sample_fmt;
pFrame->channels = m_pCodecCtx->channels;
pFrame->channel_layout = m_pCodecCtx->channel_layout;
pFrame->nb_samples = m_pCodecCtx->frame_size;
av_frame_get_buffer(pFrame, 0);
}
以下是有内存泄漏的代码:
void FifoDemo(const SW_UINT8* pInputData, SW_UINT32 iSize)
{
//避免重复定义AVFrame
AVFrame* pFrame;
init_frame(pFrame);
while(1){
//将输入数据保存到fifo
av_audio_fifo_write(m_pAudioFifo, (void**)pInputData, nDataSize);
//释放内存
if (pInputData)
av_freep(&pInputData[0]);
av_freep(pInputData);
if (av_audio_fifo_size(m_pAudioFifo) < m_pCodecCtx->frame_size)
break;
//从fifo中读取数据到AVFrame
av_audio_fifo_read(m_pAudioFifo, (void**)pFrame->data, m_pCodecCtx->frame_size);
//初始化AVPacket
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//将AVFrame中的数据编码到AVPacket
int got_pkt = 0;
int ret = avcodec_encode_audio2(m_pCodecCtx, &pkt, pFrame, &got_pkt);
if (ret < 0 || got_pkt != 1) {
av_packet_unref(&pkt);
return false;
}
//重置AVFrame
av_frame_unref(&pFrame);
/*使用AVPacket
......
......*/
//释放AVPacket
av_packet_unref(&pkt);
}
av_audio_fifo_free(m_pAudioFifo);
}
以下是没有内存泄漏的代码:
void FifoDemo(const SW_UINT8* pInputData, SW_UINT32 iSize)
{
while(1){
//将输入数据保存到fifo
av_audio_fifo_write(m_pAudioFifo, (void**)pInputData, nDataSize);
//释放内存
if (pInputData)
av_freep(&pInputData[0]);
av_freep(pInputData);
if (av_audio_fifo_size(m_pAudioFifo) < m_pCodecCtx->frame_size)
break;
//从fifo中读取数据到AVFrame
AVFrame* pFrame;
init_frame(pFrame);
av_audio_fifo_read(m_pAudioFifo, (void**)pFrame->data, m_pCodecCtx->frame_size);
//初始化AVPacket
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//将AVFrame中的数据编码到AVPacket
int got_pkt = 0;
int ret = avcodec_encode_audio2(m_pCodecCtx, &pkt, pFrame, &got_pkt);
if (ret < 0 || got_pkt != 1) {
av_packet_unref(&pkt);
return false;
}
//释放AVFrame
av_frame_free(&pFrame);
/*使用AVPacket
......
......*/
//释放AVPacket
av_packet_unref(&pkt);
}
av_audio_fifo_free(m_pAudioFifo);
}
通过对比可以看出,不同的点只有两处:
1、AVFrame的定义
有内存泄漏:为了避免重复定义和初始化AVFrame,所以只定义了一次,然后每次用完重置。
没有内存泄漏:每次执行av_audio_fifo_read前都重新定义和初始化AVFrame。
2、AVFrame的释放
有内存泄漏:使用av_frame_unref()进行重置。
没有内存泄漏:使用av_frame_free()进行释放。
题主在发现内存泄漏进行排查时,发现问题出在av_audio_fifo_read,前后绕了很久,对比了官网的样例程序才发现上述的使用区别,验证后确认内存泄漏的确出在这里。
虽然内存泄漏的量不会很大,但如果持续运行的时间较长(例如几分钟以上),还是能够很明显的观察到内存泄漏的现象,
而且出于程序开发的严谨性,也应该尽量避免内存泄漏这样危险的现象发生。
参考文档
https://ffmpeg.org/doxygen/2.4/transcode_aac_8c-example.html