Finalize Output Files with av_write_trailer in FFmpeg
This article provides a step-by-step guide on how to properly
finalize and close an output multimedia file using the FFmpeg C library.
It covers flushing delayed frames from the encoder, invoking
av_write_trailer to write file headers and indexes, safely
closing the I/O stream, and deallocating associated resources to avoid
memory leaks or corrupted output files.
1. Flush the Encoder
Before writing the container trailer, you must ensure that all cached frames inside the encoder are converted into packets and written to the output file. Modern video encoders often delay frames for B-frame calculations.
Send a NULL frame to the encoder to enter flushing mode,
then read and write all remaining packets:
// Signal end of stream by sending NULL
avcodec_send_frame(codec_ctx, NULL);
AVPacket *pkt = av_packet_alloc();
while (avcodec_receive_packet(codec_ctx, pkt) == 0) {
// Rescale packet timestamps to muxer stream timebase
av_packet_rescale_ts(pkt, codec_ctx->time_base, out_stream->time_base);
pkt->stream_index = out_stream->index;
// Write the flushed packet
av_interleaved_write_frame(format_ctx, pkt);
av_packet_unref(pkt);
}
av_packet_free(&pkt);2. Write the
Stream Trailer with av_write_trailer
The av_write_trailer() function finalizes the container
format. For formats like MP4 or MKV, this step writes essential
metadata, such as the moov atom or index tables, without
which the output file may become unplayable or non-seekable.
int ret = av_write_trailer(format_ctx);
if (ret < 0) {
fprintf(stderr, "Error writing trailer: %s\n", av_err2str(ret));
}av_write_trailer flushes any remaining buffered packets
within the muxer and handles format-specific finalizing routines.
3. Close the I/O Context
If the format does not use the AVFMT_NOFILE flag, the
underlying AVIOContext must be closed to ensure data is
committed to disk and the file handle is released.
if (!(format_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&format_ctx->pb);
}Using avio_closep() is safer than
avio_close() because it closes the file and sets the
pointer to NULL, preventing dangling pointers.
4. Free Allocated Resources
Once the file is written and the I/O stream is closed, free the remaining data structures to prevent memory leaks:
// Free codec contexts
avcodec_free_context(&codec_ctx);
// Free format context
avformat_free_context(format_ctx);Complete Sequence
The complete teardown sequence in standard order:
// 1. Flush encoder
avcodec_send_frame(codec_ctx, NULL);
while (avcodec_receive_packet(codec_ctx, pkt) == 0) {
av_packet_rescale_ts(pkt, codec_ctx->time_base, out_stream->time_base);
pkt->stream_index = out_stream->index;
av_interleaved_write_frame(format_ctx, pkt);
av_packet_unref(pkt);
}
// 2. Finalize muxer/container
av_write_trailer(format_ctx);
// 3. Close output file
if (!(format_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&format_ctx->pb);
}
// 4. Free memory
av_packet_free(&pkt);
avcodec_free_context(&codec_ctx);
avformat_free_context(format_ctx);