如何正确避免avformat_new_stream的avcodec_alloc_context3泄漏?

时间:2017-04-13 10:10:09

标签: c linux ffmpeg

This maddening thread描述了我遇到的问题:由于在调用avformat_new_stream时分配的一些内容导致关闭时内存泄漏。

这是来自泄漏的valgrind堆栈跟踪:

  • 1个街区中的1,447个(1,440个直接,7个间接)字节肯定会在4个损失记录中丢失
  • 在0x4C2FFC6:memalign(在/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so中)
  • by 0x4C300D1:posix_memalign(在/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so中)
  • by 0x690DEFF:av_malloc(mem.c:87
  • by 0x690E09D:av_mallocz(mem.c:224
  • by 0x533D28A:init_context_defaults(options.c:128
  • by 0x533D325:avcodec_alloc_context3(options.c:164
  • by 0x663D09E:avformat_new_stream(utils.c:4384
  • by 0x4204B6:main(test_ffmpeg.cpp:918)

很明显,问题是当AVFormatContext的流的编解码器上下文的priv_data字段以某种方式未被释放时。

我的代码将AVFormatContextavformat_free_context一起释放。这会调用ff_free_stream,它会调用free_stream,这会释放一些流的编解码器上下文字段本身 - 但不会释放priv_data字段!

the corresponding code in avcodec_close进行比较和对比。

线程问题的建议解决方案:“在调用av_format_free_context之前先关闭编解码器”。大概这是指调用avcodec_free_context? - 但我已经这样做了!大致遵循the muxing example中的结构,我有一个由我的代码创建的编码器上下文,用于跟踪未压缩的输入数据。然后是avformat_new_stream内部创建的另一个编码器上下文(如上所述),由FFmpeg在内部使用。我关闭了前者,因为它是使用avcodec_open2打开的,但我没有关闭后者,因为它不是。我正在遵循令人发狂的线索的建议,但我在这里。

此外,使用avcodec_free_context释放AVStream的编解码器上下文之间的行读取是不行的,因为在执行此操作时(a)AVStream's codec field is deprecated,所以这给出了一堆警告,以及(b)there are no NULL checks in free_stream,所以这会在运行时崩溃。

我现在所做的是拖拽avcodec_close的相应代码,并在调用avformat_free_context之前将其放在我自己的代码中:

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
    for(unsigned i=0;i<avf_context->nb_streams;++i) {
        AVStream *st=avf_context->streams[i];

        if(st->codec->priv_data&&
           st->codec->codec&&
           st->codec->codec->priv_class)
        {
            av_opt_free(st->codec->priv_data);
        }

        av_freep(&st->codec->priv_data);
    }
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

这样可以解决漏洞问题,而且(我的眼睛)显然是一个错误,我需要提交错误报告或其他内容。

然而,corresponding bug report被标记为固定和关闭...以及返回到疯狂线程的链接,并且没有进一步的解释。 (这就是令人抓狂的原因!)所以也许我只是错误地使用FFmpeg?

有人可以确认这是否真的是FFmpeg中的错误?

如果它不是一个错误,那么正确的调用序列是什么?

(我正在使用从提交03eb0515c12637dbd20c2e3ca8503d7b47cf583a本地构建的FFmpeg。我从Ubuntu 16包管理器获得的问题看起来有类似问题,这促使我自己用符号构建它等等。 )

1 个答案:

答案 0 :(得分:0)

我认为你错误地使用了libav API。作为API控制机制的一部分,每个库都有一个version.h文件(例如./libavformat/version.h),它定义了一组控制该库的公共API的宏。

typedef struct AVStream {
    int index;    /**< stream index in AVFormatContext */
    /**
     * Format-specific stream ID.
     * decoding: set by libavformat
     * encoding: set by the user, replaced by libavformat if left unset
     */
    int id;
#if FF_API_LAVF_AVCTX
    /**
     * @deprecated use the codecpar struct instead
     */
    attribute_deprecated
    AVCodecContext *codec;
#endif
    void *priv_data;

#if FF_API_LAVF_FRAC
    /**
     * @deprecated this field is unused
     */
    attribute_deprecated
    struct AVFrac pts;
#endif

当您遇到弃用警告时,您应该查找相应的API控件宏并编译libav以及使用libav并将此宏设置为0的代码(例如-DFF_API_LAVF_AVCTX=0字段时使用codec覆盖version.h内容并确保不再使用此弃用的API。