【问题标题】:ffmpeg memory leak in the avcodec_open2 methodavcodec_open2方法中的ffmpeg内存泄漏
【发布时间】:2018-06-11 12:35:38
【问题描述】:

我开发了一个处理实时视频流的应用程序。问题是它应该作为服务运行,随着时间的推移,我注意到内存有所增加。当我使用 valgrind 检查应用程序时 - 它没有发现任何与泄漏相关的问题。 所以我用谷歌个人资料工具检查了它。这是运行大约 6 小时后的结果(从最新转储中减去第一个转储):

   30.0  35.7%  35.7%     30.0  35.7% av_malloc
    28.9  34.4%  70.2%     28.9  34.4% av_reallocp
    24.5  29.2%  99.4%     24.5  29.2% x264_malloc

当我检查图表上的内存时,我发现这些分配与 avcodec_open2 有关。客户端代码是:

`           g_EncoderMutex.lock();
            ffmpeg_encoder_start(OutFileName.c_str(), AV_CODEC_ID_H264, m_FPS, width, height);
            for (pts = 0; pts < VideoImages.size(); pts++) {                
                m_frame->pts = pts;
                ffmpeg_encoder_encode_frame(VideoImages[pts].RGBimage[0]);
            }
            ffmpeg_encoder_finish();
            g_EncoderMutex.unlock()

ffmpeg_encoder_start方法是:

 void VideoEncoder::ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height)
        {
            int ret;
            m_FPS=fps;
            AVOutputFormat * fmt = av_guess_format(filename, NULL, NULL);
            m_oc = NULL;
            avformat_alloc_output_context2(&m_oc, NULL, NULL, filename);

            m_stream = avformat_new_stream(m_oc, 0);
            AVCodec *codec=NULL;

            codec =  avcodec_find_encoder(codec_id);    
            if (!codec) 
            {
                fprintf(stderr, "Codec not found\n");
                return; //-1
            }

            m_c=m_stream->codec;

            avcodec_get_context_defaults3(m_c, codec);

            m_c->bit_rate = 400000;
            m_c->width = width;
            m_c->height = height;
            m_c->time_base.num = 1;
            m_c->time_base.den = m_FPS;
            m_c->gop_size = 10;
            m_c->max_b_frames = 1;
            m_c->pix_fmt = AV_PIX_FMT_YUV420P;
            if (codec_id == AV_CODEC_ID_H264)
                av_opt_set(m_c->priv_data, "preset", "ultrafast", 0);

            if (m_oc->oformat->flags & AVFMT_GLOBALHEADER) 
                m_c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
            avcodec_open2( m_c, codec, NULL );

            m_stream->time_base=(AVRational){1, m_FPS};

            if (avio_open(&m_oc->pb, filename, AVIO_FLAG_WRITE) < 0)
            {
                printf( "Could not open '%s'\n", filename);
                exit(1);
            }            

            avformat_write_header(m_oc, NULL);
            m_frame = av_frame_alloc();
            if (!m_frame) {
                printf( "Could not allocate video frame\n");
                exit(1);
            }
            m_frame->format = m_c->pix_fmt;
            m_frame->width  = m_c->width;
            m_frame->height = m_c->height;
            ret = av_image_alloc(m_frame->data, m_frame->linesize, m_c->width, m_c->height, m_c->pix_fmt, 32);
            if (ret < 0) {
                printf("Could not allocate raw picture buffer\n");
                exit(1);
            }
        }

ffmpeg_encoder_encode_frame 是:

void VideoEncoder::ffmpeg_encoder_encode_frame(uint8_t *rgb) 
{
    int ret, got_output;
    ffmpeg_encoder_set_frame_yuv_from_rgb(rgb);
    av_init_packet(&m_pkt);
    m_pkt.data = NULL;
    m_pkt.size = 0;

    ret = avcodec_encode_video2(m_c, &m_pkt, m_frame, &got_output);
    if (ret < 0) {
        printf("Error encoding frame\n");
        exit(1);
    }
    if (got_output) 
    {

         av_packet_rescale_ts(&m_pkt,
                        (AVRational){1, m_FPS}, m_stream->time_base);
        m_pkt.stream_index = m_stream->index;
        int ret = av_interleaved_write_frame(m_oc, &m_pkt);

        av_packet_unref(&m_pkt);

    }

}

ffmpeg_encoder_finish 代码为:

void VideoEncoder::ffmpeg_encoder_finish(void) 
        {
            int got_output, ret;

            do {

                ret = avcodec_encode_video2(m_c, &m_pkt, NULL, &got_output);
                if (ret < 0) {
                    printf( "Error encoding frame\n");
                    exit(1);
                }
                if (got_output) {

                    av_packet_rescale_ts(&m_pkt,
                                (AVRational){1, m_FPS}, m_stream->time_base);
                    m_pkt.stream_index = m_stream->index;
                    int ret = av_interleaved_write_frame(m_oc, &m_pkt);

                    av_packet_unref(&m_pkt);
                }
            } while (got_output);

            av_write_trailer(m_oc);
            avio_closep(&m_oc->pb);

            avformat_free_context(m_oc);

            av_freep(&m_frame->data[0]);
            av_frame_free(&m_frame);

            av_packet_unref(&m_pkt);
            sws_freeContext(m_sws_context);
        }

此代码在循环中运行多次。 所以我的问题是——我做错了什么?也许 ffmpeg 正在使用某种内部缓冲?如果是这样,如何禁用它?因为这样的内存使用量增加是完全不能接受的。

【问题讨论】:

  • 哪些 API 返回指向您负责释放的内存的指针?

标签: c++ memory-leaks ffmpeg


【解决方案1】:

您没有关闭编码器上下文。将avcodec_close(m_c) 添加到ffmpeg_encoder_finish()

ffmpeg.org

用户需要调用 avcodec_close() 和 avformat_free_context() 来清理 avformat_new_stream() 的分配。

另外我看不到m_c 是如何分配的。通常它是用avcodec_alloc_context 分配的,并且必须用av_free 释放(当然在关闭之后)。

【讨论】:

    【解决方案2】:

    不要使用 valgrind 来检查您自己的项目的内存泄漏,使用消毒剂,通过这些您可以查明泄漏的来源。看看这个:Multi-Threaded Video Decoder Leaks Memory

    希望对您有所帮助。

    【讨论】:

    • 感谢您的回答,我已经尝试过消毒剂方法,但是当我使用该标志时,应用程序通常会因总线错误而失败。
    • 好的,启用消毒剂在gdb内运行程序,当它出现故障时,使用backtrace查看它发生在哪里。
    • 事情是我能够关闭我的应用程序的那部分 - 没有它,内存不会增加。但是我仍然无法解决这个 ffmpeg 问题。似乎只有我一个拥有它们。
    【解决方案3】:

    调用'avcodec_free_context(m_c)'就足够了,这个过程调用'avcodec_close'并取消分配'extradata'(如果已分配)和'subtitle_header'(如果已分配)。

    【讨论】:

      猜你喜欢
      • 2012-04-13
      • 2019-06-14
      • 1970-01-01
      • 1970-01-01
      • 2015-03-11
      • 2011-05-11
      • 1970-01-01
      • 2015-03-24
      • 1970-01-01
      相关资源
      最近更新 更多