前言
obs系列文章入口:obs-studio项目简介和架构
FFmpeg高级输出内存泄露
已解决
PR: obs-ffmpeg: Fix ffmpeg_output memory leak
obs高级输出的介绍: 【obs-studio开源项目从入门到放弃】obs 的高级输出 ffmpeg-output
最近长时间推流测试发现,obs的高级输出存在内存泄露。泄露速度不是很快,大约每分钟泄露1M多内存,12个小时泄露700多M。但是如果长时间录制或者使用FFmpeg高级输出推流,会因内存泄露导致崩溃。
内存泄露原因
obs-ffmpeg-output.c 在 receive_video() 和 receive_audio() 都调用了av_packet_alloc() 来申请 AVPacket 内存空间,但是在process_packet() 使用完 AVpacket后,并没有调用 av_packet_free() 来释放内存,因此会产生内存泄露。
解决办法
修改源码 obs-ffmpeg-output.c av_interleaved_write_frame 使用完 packet后,调用 av_packet_free() 释放 packet的内存。
static int process_packet(struct ffmpeg_output *output)
{
AVPacket *packet;
bool new_packet = false;
int ret;
pthread_mutex_lock(&output->write_mutex);
if (output->packets.num) {
packet = output->packets.array[0];
da_erase(output->packets, 0);
new_packet = true;
}
pthread_mutex_unlock(&output->write_mutex);
if (!new_packet)
return 0;
/*blog(LOG_DEBUG, "size = %d, flags = %lX, stream = %d, "
"packets queued: %lu",
packet.size, packet.flags,
packet.stream_index, output->packets.num);*/
if (stopping(output)) {
uint64_t sys_ts = get_packet_sys_dts(output, packet);
if (sys_ts >= output->stop_ts)
return 0;
}
output->total_bytes += packet->size;
ret = av_interleaved_write_frame(output->ff_data.output, packet);
if (ret < 0) {
//av_packet_free(&packet); //此处注释掉
ffmpeg_log_error(LOG_WARNING, &output->ff_data,
"process_packet: Error writing packet: %s",
av_err2str(ret));
return ret;
}
//=== 解决内存泄露:释放 packet 内存===
av_packet_free(&packet);
//===================================
return 0;
}
av_packet_alloc 和 av_packet_free 源码
/**
* Allocate an AVPacket and set its fields to default values. The resulting
* struct must be freed using av_packet_free().
*
* @return An AVPacket filled with default values or NULL on failure.
*
* @note this only allocates the AVPacket itself, not the data buffers. Those
* must be allocated through other means such as av_new_packet.
*
* @see av_new_packet
*/
AVPacket *av_packet_alloc(void)
{
AVPacket *pkt = av_malloc(sizeof(AVPacket));
if (!pkt)
return pkt;
get_packet_defaults(pkt);
return pkt;
}
/**
* Free the packet, if the packet is reference counted, it will be
* unreferenced first.
*
* @param pkt packet to be freed. The pointer will be set to NULL.
* @note passing NULL is a no-op.
*/
void av_packet_free(AVPacket **pkt)
{
if (!pkt || !*pkt)
return;
av_packet_unref(*pkt);
av_freep(pkt);
}