Write Encoded Packets to Container with libavformat
This article provides a practical guide on how to write encoded audio
or video packets into a media container file using FFmpeg’s
libavformat API. We will cover the complete lifecycle of
container writing, including allocating the output context, adding
streams, writing the container header, interleaving and writing packets,
and properly finalizing the file structure to prevent data
corruption.
Step 1: Allocate the Output Format Context
Before writing any data, you must initialize an
AVFormatContext for the output container. Use
avformat_alloc_output_context2 to automatically detect the
container format based on the file extension or format name.
AVFormatContext *out_ctx = NULL;
int ret = avformat_alloc_output_context2(&out_ctx, NULL, NULL, "output.mp4");
if (ret < 0) {
// Handle allocation error
}Step 2: Create Output Streams and Copy Parameters
You need to create a new AVStream inside the format
context for every track (video, audio, subtitles) you want to write. You
must also copy the codec configuration parameters from your encoder
context (AVCodecContext) to the stream’s parameters
(AVCodecParameters).
AVStream *out_stream = avformat_new_stream(out_ctx, NULL);
if (!out_stream) {
// Handle stream creation error
}
// Copy codec parameters from your encoder context
ret = avcodec_parameters_from_context(out_stream->codecpar, encoder_ctx);
if (ret < 0) {
// Handle copy parameters error
}
// Ensure the stream has a defined timebase
out_stream->time_base = encoder_ctx->time_base;Step 3: Open the Output File
If the chosen output container format writes to a physical file
(which is determined by checking the AVFMT_NOFILE flag),
you must open the target destination using FFmpeg’s I/O layer
(avio_open).
if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&out_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
if (ret < 0) {
// Handle file open error
}
}Step 4: Write the Container Header
Write the file header and initial structural metadata to the output
context using avformat_write_header. This function
processes any format options and configures the stream variables.
ret = avformat_write_header(out_ctx, NULL);
if (ret < 0) {
// Handle write header error
}Step 5: Rescale Timestamps and Write Packets
When writing packets (AVPacket), you must translate
their Presentation Timestamp (PTS), Decompression Timestamp (DTS), and
duration from the encoder’s time base to the output stream’s time
base.
Use av_interleaved_write_frame to write the packets.
This function automatically manages interleaving multiple streams (like
mixing audio and video packets) chronologically, which is required by
most container formats.
AVPacket *pkt = av_packet_alloc();
// ... Retrieve your encoded packet from the encoder ...
// Rescale timestamps from encoder time_base to stream time_base
av_packet_rescale_ts(pkt, encoder_ctx->time_base, out_stream->time_base);
pkt->stream_index = out_stream->index;
// Write the packet to the output file
ret = av_interleaved_write_frame(out_ctx, pkt);
if (ret < 0) {
// Handle write error
}
av_packet_unref(pkt);Step 6: Flush and Close the File
Once all media packets have been written, write the trailer to finalize the container structure (this writes index tables, metadata, and duration markers to the file). Finally, close the file I/O context and free the format context structures.
// Write the trailer
av_write_trailer(out_ctx);
// Close the file output stream if opened
if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&out_ctx->pb);
}
// Free the format context
avformat_free_context(out_ctx);