How to Use AVBufferRef in the FFmpeg C API

This article explains how to manage memory efficiently in the FFmpeg C API using the reference-counting mechanism provided by AVBufferRef. You will learn how to allocate buffer references, share data safely across multiple pointers, and release resources properly to avoid memory leaks in your multimedia applications.

Understanding AVBuffer and AVBufferRef

In FFmpeg, memory management for heavy payloads (like raw video frames or audio packets) is handled through a reference-counted buffer system. This system consists of two primary structures:

Multiple AVBufferRef structures can point to the same underlying AVBuffer. The memory is only freed when the reference count drops to zero.

Key APIs for Reference Counting

To use this mechanism, you must interact with the following core functions declared in <libavutil/buffer.h>:

  1. av_buffer_alloc(size_t size): Allocates a new buffer of the specified size and returns an AVBufferRef with a reference count of 1.
  2. av_buffer_create(...): Wraps an existing pre-allocated memory block into an AVBufferRef, allowing you to define a custom callback to free the memory.
  3. av_buffer_ref(AVBufferRef *buf): Creates a new reference to the same underlying buffer, incrementing the reference count by 1.
  4. av_buffer_unref(AVBufferRef **buf): Decrements the reference count by 1. If the count reaches 0, the underlying memory is freed. The pointer passed to this function is automatically set to NULL.
  5. av_buffer_is_writable(const AVBufferRef *buf): Checks if the buffer is writable. A buffer is only writable if the current reference is the only reference (reference count is exactly 1).

Step-by-Step Code Example

Below is a complete C example demonstrating the lifecycle of an AVBufferRef, including allocation, referencing, checking writability, and deallocation.

#include <stdio.h>
#include <string.h>
#include <libavutil/buffer.h>

int main() {
    // 1. Allocate a buffer of 1024 bytes (Initial reference count = 1)
    AVBufferRef *ref1 = av_buffer_alloc(1024);
    if (!ref1) {
        fprintf(stderr, "Failed to allocate buffer\n");
        return 1;
    }
    printf("Buffer allocated. Reference 1 pointer: %p\n", (void*)ref1->data);

    // Write some data to the buffer
    snprintf((char*)ref1->data, ref1->size, "Hello, FFmpeg Buffer!");
    printf("Data: %s\n", (char*)ref1->data);

    // 2. Check if the buffer is writable (should be true, ref count is 1)
    if (av_buffer_is_writable(ref1)) {
        printf("Buffer is writable (ref count is 1)\n");
    }

    // 3. Create a second reference to the same buffer (Reference count = 2)
    AVBufferRef *ref2 = av_buffer_ref(ref1);
    if (!ref2) {
        fprintf(stderr, "Failed to clone buffer reference\n");
        av_buffer_unref(&ref1);
        return 1;
    }
    printf("Buffer referenced. Reference 2 pointer: %p\n", (void*)ref2->data);

    // 4. Check writability again (should be false, ref count is 2)
    if (!av_buffer_is_writable(ref1)) {
        printf("Buffer is no longer writable (ref count is 2)\n");
    }

    // 5. Release the first reference (Reference count decreases to 1)
    av_buffer_unref(&ref1);
    printf("Released reference 1. ref1 is now: %p\n", (void*)ref1);

    // 6. Check writability of ref2 (should be true again, ref count is 1)
    if (av_buffer_is_writable(ref2)) {
        printf("Buffer is writable again through ref2 (ref count is 1)\n");
    }

    // Read the data from the remaining reference
    printf("Data from ref2: %s\n", (char*)ref2->data);

    // 7. Release the final reference (Reference count decreases to 0, memory freed)
    av_buffer_unref(&ref2);
    printf("Released reference 2. ref2 is now: %p\n", (void*)ref2);

    return 0;
}

Best Practices