How to Write Packets to a Container in Libavformat
This article explains how to mux encoded data into an output media
container using FFmpeg's libavformat library. You will
learn the correct sequence of API calls required to initialize the
output muxer, write stream headers, convert packet timestamps between
different time bases, write packets sequentially to the file, and
properly finalize the container structure.
1. Initialize the Output Context and Write Headers
Before any packets can be written, you must allocate an
AVFormatContext for your output format, create the
necessary streams using avformat_new_stream(), copy codec
parameters, open the output I/O context using avio_open(),
and write the container header:
// Allocate output format context
AVFormatContext *ofmt_ctx = NULL;
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, "output.mp4");
// Open file if format does not have AVFMT_NOFILE flag
if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_open(&ofmt_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
}
// Write container header
int ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
// Handle error
}2. Rescale Packet Timestamps
Packets received from an encoder operate on the encoder's time base
(AVCodecContext.time_base). Output containers operate on
the stream's time base (AVStream.time_base). You must
rescale the Presentation Timestamp (pts), Decompression
Timestamp (dts), and duration of each AVPacket
before sending it to the muxer.
Use av_packet_rescale_ts() to perform this
conversion:
av_packet_rescale_ts(pkt, codec_ctx->time_base, out_stream->time_base);
pkt->stream_index = out_stream->index;3. Write Packets to the Container
libavformat provides two primary functions for writing
encoded packets:
av_interleaved_write_frame(): Recommended for most containers, especially those containing multiple streams (e.g., audio and video). This function automatically buffers and sorts packets by DTS to ensure proper interleaving. Note that this function takes ownership of the packet data, resetting the passedAVPacket.av_write_frame(): Writes the packet directly without interleaving. This requires that you supply packets strictly in presentation or decode order yourself.
Writing with interleaving:
ret = av_interleaved_write_frame(ofmt_ctx, pkt);
if (ret < 0) {
// Handle write error
}4. Flush the Muxer and Write the Trailer
After encoding all frames and writing all packets, flush any internal
buffers within the muxer and finalize the file format by writing the
container trailer with av_write_trailer():
// Flush remaining interleaved packets by passing NULL
av_interleaved_write_frame(ofmt_ctx, NULL);
// Write container metadata/trailer (e.g., MP4 'moov' atom)
av_write_trailer(ofmt_ctx);5. Clean Up Resources
Finally, close the I/O stream and release the format context:
if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&ofmt_ctx->pb);
}
avformat_free_context(ofmt_ctx);