How to Use avformat_write_header in FFmpeg C API

Writing container headers is a crucial step in muxing multimedia streams using the FFmpeg C API. This article explains how to initialize an output format context, configure output streams with valid codec parameters, open the output I/O context, and successfully call avformat_write_header to write the file header and initialize the muxer.

1. Initialize the Output Format Context

Before writing headers, allocate the AVFormatContext for the output container using avformat_alloc_output_context2:

AVFormatContext *out_ctx = NULL;
int ret = avformat_alloc_output_context2(&out_ctx, NULL, NULL, "output.mp4");
if (!out_ctx || ret < 0) {
    // Handle error
}

2. Add and Configure Streams

Containers require knowledge of the elementary streams they encapsulate. You must add at least one stream via avformat_new_stream and populate its codecpar (AVCodecParameters):

AVStream *out_stream = avformat_new_stream(out_ctx, NULL);
if (!out_stream) {
    // Handle error
}

// Populate codec parameters from an existing decoder context or manually
ret = avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);
if (ret < 0) {
    // Handle error
}

// Alternatively, copy from an input stream during remuxing
// avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);

Ensure out_stream->time_base is set appropriately for the muxer.

3. Open the Output File

Unless the output format flag specifies AVFMT_NOFILE, you must open the output target using avio_open2:

if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) {
    ret = avio_open(&out_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
    if (ret < 0) {
        // Handle I/O error
    }
}

4. Call avformat_write_header

Once streams and I/O are prepared, call avformat_write_header. You can pass a pointer to an AVDictionary containing muxer-specific options, or NULL if no custom options are required.

AVDictionary *muxer_opts = NULL;
av_dict_set(&muxer_opts, "movflags", "faststart", 0);

ret = avformat_write_header(out_ctx, &muxer_opts);
if (ret < 0) {
    char err_buf[AV_ERROR_MAX_STRING_SIZE];
    av_strerror(ret, err_buf, sizeof(err_buf));
    fprintf(stderr, "Error writing header: %s\n", err_buf);
    // Handle error and clean up
}

// Free any options that were not consumed by the muxer
av_dict_free(&muxer_opts);

Return Values and Post-Processing

  • Success: avformat_write_header returns 0 or AVSTREAM_INIT_IN_WRITE_HEADER (1) on success.
  • Failure: A negative AVERROR code indicates failure. Always check the return value before proceeding to write packets with av_interleaved_write_frame.
  • Resource Management: Any options consumed by the muxer are removed from the options dictionary. Always free the remaining dictionary with av_dict_free.
  • Finalization: Any successful call to avformat_write_header must eventually be paired with av_write_trailer(out_ctx) after all packets are written to finalize the container structure properly.