videoffmpeglibavformatmuxer

How can libavformat be used without using other libav libraries?


I would like a simple working example of using just libavformat to mux video. There are nice examples (doc/examples/muxing.c) that show encoding with libavcodec, muxing with libavformat and saving the data with libavio. However, there is no example I know of that uses libavformat by itself, feeding in encoded data in a buffer and getting muxed data in a buffer.

The difficulty is two-fold: one, adding a stream with avformat_new_stream(AVFormatContext *s, const AVCodec *c) requires a reference to the codec; and two, the output from muxing is passed to AVFormatContext->pb which is an AVIOContext*. Thus there seems to be no (obvious) way to extricate libavformat from the other libav libraries.

See also: This question mentions a way to not use libavio: Get TS packets into buffer from libavformat


Solution

  • You can avoid dependencies on libavcodec library, but you need the header files (for example, avcodec.h).

    The scheme is as follows:

    AVOutputFormat * oFmt= ::av_guess_format("mp4", NULL, NULL);
    AVFormatContext *oFmtCtx = NULL;
    ::avformat_alloc_output_context2(&oFmtCtx, oFmt, NULL, NULL);
    AVStream * oStrm = ::avformat_new_stream(oFmtCtx, NULL);
    AVCodecContext * strmCodec = oFmtCtx->streams[0]->codec;
    
    // Fill the required properties for codec context.
    // *from the documentation:
    // *The user sets codec information, the muxer writes it to the output.
    // *Mandatory fields as specified in AVCodecContext
    // *documentation must be set even if this AVCodecContext is
    // *not actually used for encoding.
    my_tune_codec(strmCodec); 
    
    if (oFmtCtx->oformat->flags & AVFMT_NOFILE)
    {
      ::avio_open2(&oFmtCtx->pb, fileName, AVIO_FLAG_WRITE, NULL, NULL);
    }
    ::avformat_write_header(oFmtCtx, NULL);
    // .....
    // writing loop
    // .....
    ::av_write_trailer(oFmtCtx);
    ::avio_close(oFmtCtx->pb);
    ::avformat_free_context(oFmtCtx);
    

    To get the output, you always have to use the concept of AVIOContext. You can avoid using the built-in protocols. To do this you need to create your own AVIOContext (::avio_alloc_context).

    UPD To create your own AVIOContext, you have to do something like this

    #include <stdio.h>
    extern "C" {
    #include <libavformat/avio.h>
    #include <libavformat/avformat.h>
    }
    
    static const int kBufferSize = 32768;
    
    class my_iocontext_private
    {
    public:
        my_iocontext_private(FILE * f) : buffer_size_(kBufferSize),
            buffer_(static_cast<unsigned char*>(::av_malloc(buffer_size_))), f_(f) {
            ctx_ = ::avio_alloc_context(buffer_, buffer_size_, AVIO_FLAG_WRITE, this, 
                &my_iocontext_private::read, &my_iocontext_private::write, &my_iocontext_private::seek);
        }
    
        ~my_iocontext_private()    { av_free(buffer_); }
    
        static int read(void *opaque, unsigned char *buf, int buf_size) {
            my_iocontext_private* h = static_cast<my_iocontext_private*>(opaque);
            return fread(buf, 1, buf_size, h->f_);
        }
    
        static int write(void *opaque, unsigned char *buf, int buf_size) {
            my_iocontext_private* h = static_cast<my_iocontext_private*>(opaque);
            return fwrite(buf, 1, buf_size, h->f_);
        }
    
        static int64_t seek(void *opaque, int64_t offset, int whence) {
            my_iocontext_private* h = static_cast<my_iocontext_private*>(opaque);
    
            // use lseeki64 instead of fseek
            return fseek(h->f_, static_cast<long>(offset), whence);        
        }
        ::AVIOContext *get_avio() { return ctx_; }
    
    private:
        int buffer_size_;
        unsigned char * buffer_;  
        FILE * f_;
        ::AVIOContext * ctx_;
    };
    
    int main(int argc, char* argv[])
    {
        FILE * f = fopen("myfile.dmp", "wb");    
        my_iocontext_private priv_ctx(f); 
    
        AVFormatContext * ctx = ::avformat_alloc_context();
        ctx->pb = priv_ctx.get_avio();
    
        /// using ctx
    
        fclose(f); 
        return 0;
    }