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:
AVBuffer: An internal, opaque structure that represents the actual data buffer and tracks its current reference count.AVBufferRef: A public structure that represents a reference to anAVBuffer. It contains a pointer to the actual data (data) and the size of the buffer (size).
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>:
av_buffer_alloc(size_t size): Allocates a new buffer of the specified size and returns anAVBufferRefwith a reference count of 1.av_buffer_create(...): Wraps an existing pre-allocated memory block into anAVBufferRef, allowing you to define a custom callback to free the memory.av_buffer_ref(AVBufferRef *buf): Creates a new reference to the same underlying buffer, incrementing the reference count by 1.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 toNULL.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
- Always pass by reference to unref: When calling
av_buffer_unref, pass the address of the pointer (e.g.,&ref1). This allows the function to set your local pointer variable toNULL, preventing accidental double-free bugs. - Thread Safety: The reference counting operations
(
av_buffer_refandav_buffer_unref) are thread-safe and use atomic operations under the hood. However, writing to the underlying data pointer itself is not thread-safe and must be synchronized externally if shared across threads. - Use
av_buffer_make_writable: If you need to write to a buffer but are unsure of its reference count, useav_buffer_make_writable(&buf). If the reference count is greater than 1, this function automatically allocates a new buffer, copies the data over, decrements the old reference, and points your reference to the new exclusive buffer.