How to Allocate and Populate an AVFrame in FFmpeg

In FFmpeg, the AVFrame structure is the primary container used to hold uncompressed audio or video data, such as raw YUV/RGB video frames or PCM audio samples. Programmatically working with an AVFrame requires allocating the structure, defining frame properties such as dimensions and pixel format, allocating data buffers, and safely populating those buffers with raw data. This guide provides a step-by-step walkthrough and a clean C code example demonstrating how to allocate, configure, fill, and free an AVFrame using the modern FFmpeg API.

1. Allocate the AVFrame Structure

The first step is allocating memory for the AVFrame struct itself using av_frame_alloc(). This allocates the wrapper but does not allocate any memory for the underlying image or audio payload.

AVFrame *frame = av_frame_alloc();
if (!frame) {
    fprintf(stderr, "Could not allocate AVFrame\n");
    return -1;
}

2. Configure Frame Properties

Before allocating memory for the underlying image data, define the frame's basic metadata:

  • format: Pixel format (e.g., AV_PIX_FMT_YUV420P, AV_PIX_FMT_RGB24).
  • width: Image width in pixels.
  • height: Image height in pixels.
frame->format = AV_PIX_FMT_YUV420P;
frame->width  = 1920;
frame->height = 1080;

3. Allocate Data Buffers

Call av_frame_get_buffer() to allocate internal memory buffers according to the configured resolution and pixel format. The second parameter specifies alignment, where 0 lets FFmpeg select optimal alignment (often 32 or 64 bytes for SIMD operations).

int ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
    fprintf(stderr, "Could not allocate frame data buffers\n");
    av_frame_free(&frame);
    return ret;
}

4. Ensure Buffer Writability

If an AVFrame is shared or reference-counted, verify that its buffers are writable prior to modifying the data by calling av_frame_make_writable():

ret = av_frame_make_writable(frame);
if (ret < 0) {
    fprintf(stderr, "Frame is not writable\n");
    av_frame_free(&frame);
    return ret;
}

5. Populate Data Planes

Once memory is allocated, populate the frame by accessing its data pointers (frame->data) and stride values (frame->linesize).

For planar formats like AV_PIX_FMT_YUV420P:

  • frame->data[0]: Y (Luminance) plane
  • frame->data[1]: U (Chrominance) plane
  • frame->data[2]: V (Chrominance) plane

Always respect frame->linesize[n] when moving across rows to account for row padding:

/* Fill Y plane */
for (int y = 0; y < frame->height; y++) {
    for (int x = 0; x < frame->width; x++) {
        frame->data[0][y * frame->linesize[0] + x] = x + y;
    }
}

/* Fill Cb and Cr planes (half resolution for YUV420P) */
for (int y = 0; y < frame->height / 2; y++) {
    for (int x = 0; x < frame->width / 2; x++) {
        frame->data[1][y * frame->linesize[1] + x] = 128;
        frame->data[2][y * frame->linesize[2] + x] = 64;
    }
}

For packed formats (such as AV_PIX_FMT_RGB24), all color components reside consecutively within frame->data[0], where each pixel occupies multiple bytes across row strides defined by frame->linesize[0].

6. Set Timestamps (Optional)

If passing the frame to an encoder or multiplexer, assign the presentation timestamp (pts):

frame->pts = 0; // Increment this monotonically for subsequent frames

7. Free the Frame

When processing is complete, release the data buffers and the frame struct using av_frame_free(). Passing a double pointer resets your frame pointer to NULL.

av_frame_free(&frame);