Tom Seddon
Tom Seddon

Reputation: 2748

How does one correctly avoid the avcodec_alloc_context3 leak from avformat_new_stream?

This maddening thread describes the problem I'm having: a memory leak on shutdown due to some stuff allocated when avformat_new_stream is called.

Here's the valgrind stack trace from the leak:

So clearly the problem is that when a AVFormatContext's stream's codec context's priv_data field is somehow not being freed.

My code frees the AVFormatContext with avformat_free_context. This calls ff_free_stream, which calls free_stream, which frees a few of the stream's codec context fields itself - but not the priv_data field!

Compared and contrast with the corresponding code in avcodec_close.

The suggested solution to the problem from the thread: "close the codec firstly before calling av_format_free_context". Presumably this refers to calling avcodec_free_context? - but I'm already doing this! Roughly following the structure in the muxing example, I have an encoder context created by my code, that's used to track the uncompressed input data. Then there's another encoder context created internally by avformat_new_stream (as above), which is used internally by FFmpeg. I close the former, because it was opened using avcodec_open2, but I don't close the latter, because it wasn't. I am following the maddening thread's advice, and yet here I am.

Furthermore, reading between the lines, using avcodec_free_context to free the AVStream's codec context is no good anyway, because when doing this (a) AVStream's codec field is deprecated, so this gives a bunch of warnings, and (b) there are no NULL checks in free_stream, so this crashes at runtime.

What I have done for now is drag in the appropriate bit of code from avcodec_close, and put it in my own code just ahead of the call to 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

So that fixes the leak, and it's clearly (to my eyes) a bug, and I need to file a bug report or something.

However the corresponding bug report is marked as fixed and closed... along with a link back to the maddening thread, and no further explanation. (This is why it is maddening!) So maybe I'm just using FFmpeg wrongly?

Can anybody confirm whether this is actually a bug in FFmpeg or not?

If it isn't a bug, what's the right sequence of calls to make?

(I'm using FFmpeg built locally from commit 03eb0515c12637dbd20c2e3ca8503d7b47cf583a. I had similar-looking problems with the one you get from the Ubuntu 16 package manager, which prompted me to build it myself with symbols and so on.)

Upvotes: 5

Views: 2712

Answers (1)

user7860670
user7860670

Reputation: 37600

I think you are using libav API incorrectly. As part of API control mechanism each library has a version.h file (for example ./libavformat/version.h) that defines a set of macros controlling public API of that library.

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

When you encounter deprecation warnings you should look for corresponding API control macro and compile libav and your code that uses libav with this macro set to 0 (with -DFF_API_LAVF_AVCTX=0 in case of codec field for example) overriding version.h content and ensuring that this deprecated API is not used anymore.

Upvotes: 0

Related Questions