FFmpeg Memory Management: av_packet_free and av_frame_free

In FFmpeg C API development, improper management of AVPacket and AVFrame lifecycles is a leading cause of memory leaks and application crashes. This guide explains how to properly allocate, reference, and release memory using av_packet_free and av_frame_free, ensuring your multimedia applications remain stable and leak-free.

Understanding the Allocation Model

To prevent leaks, you must understand that both AVPacket (which holds compressed encoded data) and AVFrame (which holds uncompressed raw data) consist of two parts: 1. The Wrapper (Struct): The metadata container (e.g., width, height, pts, dts). 2. The Payload (Data Buffer): The actual pixel data or audio samples.

Both must be freed correctly. Doing this manually by freeing struct members is highly discouraged. Instead, you must use FFmpeg’s dedicated lifecycle APIs.


Proper Allocation and Deallocation

Always allocate packets and frames on the heap using their respective allocation functions. Never allocate them on the stack (e.g., AVPacket pkt;), as this bypasses FFmpeg’s internal initialization and reference-counting mechanisms.

AVFrame *frame = av_frame_alloc();
AVPacket *pkt = av_packet_alloc();

if (!frame || !pkt) {
    // Handle memory allocation failure
}

To free both the underlying data buffers and the container structs, pass the address of your pointer to av_frame_free and av_packet_free.

av_frame_free(&frame);
av_packet_free(&pkt);

Why pass a double pointer (&frame)? Passing a double pointer allows FFmpeg to do two things: 1. Internally call unref functions to free the underlying data buffers. 2. Free the container struct itself and automatically set your pointer (frame or pkt) to NULL. This prevents accidental double-free errors and dangling pointer bugs.


Managing Loops and Reference Counting

When processing video or audio in a loop (such as decoding or demuxing), allocating and freeing a new packet/frame on every iteration is highly inefficient. Instead, allocate once outside the loop, reuse the container inside the loop, and free it once at the end.

To do this safely without leaking memory, you must use reference counting via av_packet_unref and av_frame_unref.

The Loop Reuse Pattern

AVPacket *pkt = av_packet_alloc();
if (!pkt) return;

while (av_read_frame(format_context, pkt) >= 0) {
    // Process the packet (e.g., send to decoder)
    
    // Wipe the packet data and decrement the reference count,
    // preparing the struct to be reused in the next iteration.
    av_packet_unref(pkt);
}

// Clean up the container when the loop finishes
av_packet_free(&pkt);

Note: Functions like av_read_frame and avcodec_receive_frame will automatically call unref on the destination object before writing new data. However, explicitly calling unref in your cleanup paths or when skipping packets prevents leaks in error-handling branches.


Best Practices to Prevent Memory Leaks

  1. Avoid Stack Allocation: Never use AVPacket pkt; or AVFrame frame;. Stack-allocated structs do not utilize modern FFmpeg reference counting and will cause undefined behavior or memory leaks when passed to APIs.
  2. Use the goto Cleanup Pattern: In C, handle errors by jumping to a cleanup label at the end of your function. Ensure your cleanup block safely calls av_packet_free(&pkt) and av_frame_free(&frame). Because these functions accept NULL pointers safely, you do not need to check if the pointer is valid before calling them.
  3. Handle Ownership Transfers: Be aware of functions that take ownership of data. For example, av_packet_move_ref(dst, src) moves the reference from src to dst and blanks src. If you manually free src afterward, it is safe, but you must ensure dst is eventually freed.