前言:
最近在进行一个PC对PC端的直播功能的研发,主要需要实现从PC端捕获桌面处理成H264视频流,以及采集PC端的扬声器的声音处理成AAC音频流,通过RTMP推流到服务器端中,然后客户端可以从服务器中拉流并播放视频,最终实现直播的功能。该项目主要运用到FFMPEG实现音视频的编解码和拉流,使用SDL进行对音视频的播放。
在实现该功能的过程中,总共可以分为如下几个核心模块:
1、音频的采集、编码;
2、桌面捕获和图像编码;
3、音视频的同步及推流(核心);
4、音视频的拉流及解码;
5、音视频同步播放(核心);
6、释放资源(避免内存泄漏)要点讲解
7、释放资源(避免内存泄漏)补充
其中音视频编解码是项目的基础,音视频的同步是项目的核心,在后续的文章中将分章节讲述该项目实现的过程。
在本章节中主要是对本人在使用ffmpeg时由于使用堆内存不恰当而造成的内存泄漏的情况以及阐述如何改正的方法:
(1)ffmpeg内存泄漏之---av_bsf_receive_packet和av_bsf_send_packet
1.1功能简单介绍:
如下为该对函数的定义,该对函数主要是需要实现对传进来的AVPacket *pkt进行添加头部信息的功能,例如给接收到的h264数据封装必要的头部信息等。
int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt);
int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt);
1.2函数使用需要注意的地方:
(1)在接收函数av_bsf_receive_packet()接收返回的数据时,需要注意这一点尽量对AVPacket *pkt不分配pkt->data的内存,因为在使用av_bsf_receive_packet()函数时,函数会默认为pkt->data分配一段合适的堆内存,这也是特别注意小心的。使用完AVPacket *pkt后需要使用如下的函数对其进行释放额外分配的堆内存:
void av_packet_unref(AVPacket *pkt);
(2)第二个需要注意的地方是,在使用av_bsf_send_packet()时,实际上是将需要处理的数据送入队列中进行排队处理,接着使用av_bsf_receive_packet()接收处理后的数据,此时需要注意一个问题是,在传入一段需要处理的数据后,需要使用循环来将处理后的数据进行处理和释放资源,否则会引起内存泄漏,如下为这个要点的例子:
while (ture)
{
ret = av_bsf_receive_packet(ctx, &pkt_release);
av_packet_unref(&pkt_release);
if (ret < 0)
{
break;
}
}
(3)在使用这对函数时应尽量不要将av_bsf_send_packet()和av_bsf_receive_packet()共用一个AVPacket ,因为共用AVPacket容易由于av_bsf_receive_packet()对AVPacket原有的pkt->data的指针地址重新分配内存,造成原有的pkt->data成为野指针而造成内存泄漏。
如下为给出该对函数的使用样例:
int my_av_bsf_filter(const AVBitStreamFilter *filter, AVPacket *pPacket, const AVCodecParameters *src)
{
int ret;
int pts_tmp = pPacket->pts;
AVBSFContext *ctx = NULL;
if (!filter)
{
WLog(LOG_DEBUG, "filter is null");
return 0;
}
ret = av_bsf_alloc(filter, &ctx);
if (ret < 0)
{
WLog(LOG_DEBUG, "av_bsf_alloc fail");
return ret;
}
ret = avcodec_parameters_copy(ctx->par_in, src);
if (ret < 0)
{
WLog(LOG_DEBUG, "avcodec_parameters_copy fail");
av_bsf_free(&ctx);
return ret;
}
ret = av_bsf_init(ctx);
if (ret < 0)
{
WLog(LOG_DEBUG, "av_bsf_init fail");
av_bsf_free(&ctx);
return ret;
}
AVPacket pkt = { 0 };
pkt.data = pPacket->data;
pkt.size = pPacket->size;
AVPacket pkt_release = { 0 };
pkt_release.data = NULL;
pkt_release.size = 0;;
ret = av_bsf_send_packet(ctx, &pkt);
if (ret < 0)
{
WLog(LOG_DEBUG, "av_bsf_send_packet fail");
av_bsf_free(&ctx);
return ret;
}
ret = av_bsf_receive_packet(ctx, &pkt_release);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
WLog(LOG_DEBUG, "ret == AVERROR(EAGAIN) || ret == AVERROR_EOF");
av_bsf_free(&ctx);
return 0;
}
else if (ret < 0)
{
WLog(LOG_DEBUG, "av_bsf_receive_packet fail");
av_bsf_free(&ctx);
return ret;
}
av_packet_unref(pPacket);
av_new_packet(pPacket, pkt_release.size);
memcpy(pPacket->data, pkt_release.data, pPacket->size);
pPacket->pts = pts_tmp;
av_packet_unref(&pkt_release);
///drain all the remaining packets we cannot return
ret = 0;
while (1) {
ret = av_bsf_receive_packet(ctx, &pkt_release);
av_packet_unref(&pkt_release);
if (ret < 0)
{
break;
}
}
av_bsf_free(&ctx);
return 1;
}
每写一篇文章都不容易,尊重别人的知识产权才是对自己和技术的尊重。为了避免发生知识产权被侵权的情况,我决定做出以下声明:
1.博客中标注原创的文章,版权归原作者 吴豪乐工作室 所有;
2.未经原作者允许不得转载本文内容,否则将视为侵权;
3.转载或者引用本文内容请注明来源及原作者;
4.对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。