libavutil is FFmpeg's foundational utility library — it provides memory allocation, timestamp math, error handling, logging, and pixel/sample format helpers that every other FFmpeg library depends on. If you're writing C code against FFmpeg's API, you'll include libavutil headers in virtually every file.
This article covers the functions you'll use most when building media applications with FFmpeg's C API, with complete code examples you can compile and run.
What you'll learn
- The role of libavutil in the FFmpeg ecosystem
- Memory management with
av_mallocandav_free - Timestamp conversion with
av_rescale_q - Error handling with
av_strerror - Logging with
av_log - Working with AVFrame and AVDictionary
- Hash computation for file integrity checks
- Installation and build setup
What is libavutil?
Overview
libavutil is FFmpeg's core utility library. It provides the low-level building blocks that all other FFmpeg libraries (libavcodec, libavformat, libavfilter, and others) depend on.
Key capabilities:
- Memory allocation and buffer management
- Mathematical operations and scaling
- Pixel format and sample format utilities
- Error code to string conversion
- Structured logging with level control
- Cryptographic hash functions (MD5, SHA1, HMAC)
- Rational number arithmetic
- Reference-counted frames (
AVFrame) - Key-value metadata dictionaries (
AVDictionary)
As of FFmpeg 8.1 (released March 2026), libavutil remains stable with the same core API surface. The official Doxygen documentation lists 10 module categories spanning 148 header files.
Where libavutil fits in the FFmpeg library stack
| Library | Primary role |
|---|---|
| libavcodec | Audio and video encoding/decoding |
| libavformat | Media container parsing and muxing |
| libavfilter | Video and audio filtering |
| libavutil | Utility functions: memory, math, logging, error handling |
| libswscale | Video rescaling and color space conversion |
| libswresample | Audio resampling and format conversion |
libavutil is the foundation layer. You rarely use it in isolation, but it's always present when working with FFmpeg's C API.
Memory Management
FFmpeg handles large amounts of data during media processing, making proper memory management critical. Segfaults from mismatched av_malloc/free calls are one of the most common pitfalls in FFmpeg development.
Core functions
av_malloc(size_t size)— allocate memoryav_free(void *ptr)— free allocated memoryav_mallocz(size_t size)— allocate and zero-initialize memoryav_realloc(void *ptr, size_t size)— resize allocated memoryav_freep(void **ptr)— free and set pointer to NULL (safer)
Basic usage
#include <libavutil/mem.h>
#include <stdio.h>
int main() {
// Allocate an array of 10 integers
int *data = (int *) av_malloc(sizeof(int) * 10);
if (!data) {
fprintf(stderr, "Memory allocation failed\n");
return 1;
}
for (int i = 0; i < 10; i++) {
data[i] = i * i;
}
printf("data[5] = %d\n", data[5]); // 25
av_free(data);
return 0;
}
Why use av_malloc instead of malloc?
FFmpeg internally uses aligned memory allocation to optimize SIMD operations. The alignment size depends on your CPU's capabilities:
| CPU Feature | Alignment |
|---|---|
| AVX-512 | 64 bytes |
| AVX / AVX2 | 32 bytes |
| SSE / NEON | 16 bytes |
On a typical x86_64 build with AVX2, av_malloc returns 32-byte aligned memory. This matters because FFmpeg's internal DSP routines use SIMD instructions that crash on unaligned data. Standard malloc only guarantees 16-byte alignment on most platforms.
Using regular malloc for frame buffers can cause random segfaults that only show up with certain video resolutions. Switching to av_malloc fixes them — the AVX2 code paths need 32-byte alignment, and malloc doesn't provide it.
av_freep: The safer alternative
av_freep frees memory and sets the pointer to NULL in one step, preventing use-after-free bugs:
#include <libavutil/mem.h>
#include <stdio.h>
int main() {
uint8_t *buffer = (uint8_t *) av_malloc(1024);
if (!buffer) return 1;
// ... use buffer ...
av_freep(&buffer);
// buffer is now NULL — safe to check before use
printf("buffer after freep: %p\n", (void *)buffer); // (nil)
return 0;
}
Mathematical Operations and Time Calculation
Time handling in FFmpeg is based on timebase — a rational number that defines what one "tick" represents. For example, a timebase of 1/90000 means each PTS (Presentation Timestamp) unit is 1/90000 of a second.
Different streams can have different timebases, so converting between them is a common operation. If you're working with HLS streaming or muxing multiple streams, you'll use av_rescale_q constantly.
Core functions
av_rescale(int64_t a, int64_t b, int64_t c)— computea * b / cwith overflow protectionav_rescale_q(int64_t a, AVRational bq, AVRational cq)— scale a timestamp between two timebasesav_log2(unsigned v)— log base 2
Timestamp conversion example
#include <libavutil/mathematics.h>
#include <libavutil/rational.h>
#include <inttypes.h>
#include <stdio.h>
int main() {
// Convert frame number in 30fps timebase to milliseconds
AVRational src_timebase = {1, 30}; // 30 fps (one unit = 1/30 second)
AVRational dst_timebase = {1, 1000}; // milliseconds (one unit = 1ms)
int64_t frame_number = 300; // frame 300 at 30fps = 10 seconds
int64_t milliseconds = av_rescale_q(frame_number, src_timebase, dst_timebase);
printf("Frame 300 at 30fps = %" PRId64 " ms\n", milliseconds); // 10000 ms
return 0;
}
This is essential when mixing streams with different timebases — for example, combining video (typically {1, 90000}) with audio (typically {1, 48000} for 48kHz).
Why av_rescale instead of plain multiplication?
A naive a * b / c can overflow int64_t with large timestamps. av_rescale uses 128-bit intermediate arithmetic to avoid this. For example, when processing a multi-hour livestream recording, timestamps can overflow and produce completely wrong seek positions in the output.
Simple scaling example
#include <libavutil/mathematics.h>
#include <inttypes.h>
#include <stdio.h>
int main() {
// av_rescale: computes a * b / c with correct rounding
int64_t result = av_rescale(1000, 3, 2);
printf("1000 * 3 / 2 = %" PRId64 "\n", result); // 1500
return 0;
}
AVFrame and AVDictionary
These two structures show up everywhere in FFmpeg code, and both rely on libavutil.
AVFrame — decoded media data
AVFrame holds raw decoded video or audio data. It's reference-counted via libavutil's AVBuffer system, so copying a frame is cheap (it increments a refcount, not duplicating pixel data).
#include <libavutil/frame.h>
#include <libavutil/imgutils.h>
#include <stdio.h>
int main() {
AVFrame *frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate frame\n");
return 1;
}
frame->format = AV_PIX_FMT_YUV420P;
frame->width = 1920;
frame->height = 1080;
// Allocate the actual pixel buffer
int ret = av_frame_get_buffer(frame, 0); // 0 = default alignment
if (ret < 0) {
fprintf(stderr, "Could not allocate frame buffer\n");
av_frame_free(&frame);
return 1;
}
printf("Frame: %dx%d, format=%d, linesize[0]=%d\n",
frame->width, frame->height, frame->format, frame->linesize[0]);
av_frame_free(&frame);
return 0;
}
Key points:
- Always use
av_frame_alloc()/av_frame_free()— never stack-allocate an AVFrame av_frame_get_buffer()allocates pixel/sample data with proper alignmentav_frame_ref()creates a new reference to the same data (cheap copy)av_frame_unref()releases a reference
AVDictionary — key-value metadata
AVDictionary is FFmpeg's general-purpose key-value store. It's used for container metadata, codec options, and format options.
#include <libavutil/dict.h>
#include <stdio.h>
int main() {
AVDictionary *dict = NULL;
// Add entries
av_dict_set(&dict, "title", "My Video", 0);
av_dict_set(&dict, "artist", "32blog", 0);
av_dict_set_int(&dict, "track", 1, 0);
// Look up an entry
const AVDictionaryEntry *entry = av_dict_get(dict, "title", NULL, 0);
if (entry) {
printf("Title: %s\n", entry->value); // "My Video"
}
// Iterate all entries
const AVDictionaryEntry *e = NULL;
while ((e = av_dict_iterate(dict, e))) {
printf("%s = %s\n", e->key, e->value);
}
av_dict_free(&dict);
return 0;
}
You'll encounter AVDictionary when reading file metadata, passing options to codecs (avcodec_open2), and configuring format contexts.
Image and Audio Format Utilities
Pixel format helpers
#include <libavutil/pixdesc.h>
#include <stdio.h>
int main() {
// Get the name of a pixel format
enum AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P;
const char *name = av_get_pix_fmt_name(pix_fmt);
printf("Pixel format: %s\n", name); // yuv420p
// Look up a pixel format by name
enum AVPixelFormat found = av_get_pix_fmt("yuv420p");
printf("Found format enum: %d\n", found);
// Get detailed format description
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
printf("Components: %d, bits per pixel: %d\n",
desc->nb_components,
av_get_bits_per_pixel(desc));
return 0;
}
The pixel format reference lists all available formats. You'll use these when setting up encoding or working with GPU-accelerated encoding where format conversion is needed.
Audio buffer size calculation
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
#include <stdio.h>
int main() {
// Calculate buffer size needed for 1024 samples,
// 2 channels (stereo), 16-bit signed integer format
int buffer_size = av_samples_get_buffer_size(
NULL, // linesize output (NULL = don't need it)
2, // channels
1024, // samples per channel
AV_SAMPLE_FMT_S16, // sample format (16-bit signed integer)
0 // alignment (0 = default)
);
printf("Buffer size: %d bytes\n", buffer_size); // 4096 bytes
return 0;
}
Error Handling and Logging
Error code to string conversion
FFmpeg functions return negative integers for errors. av_strerror converts these to human-readable messages.
#include <libavutil/error.h>
#include <stdio.h>
int main() {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
// AVERROR(EINVAL) = -22 (Invalid argument)
int errnum = AVERROR(EINVAL);
av_strerror(errnum, errbuf, sizeof(errbuf));
printf("Error: %s\n", errbuf); // "Invalid argument"
// AVERROR_EOF = end of file
av_strerror(AVERROR_EOF, errbuf, sizeof(errbuf));
printf("Error: %s\n", errbuf); // "End of file"
return 0;
}
Structured logging
#include <libavutil/log.h>
int main() {
// Set minimum log level (messages below this level are suppressed)
av_log_set_level(AV_LOG_DEBUG);
// Log at different severity levels
av_log(NULL, AV_LOG_ERROR, "This is an error\n");
av_log(NULL, AV_LOG_WARNING, "This is a warning\n");
av_log(NULL, AV_LOG_INFO, "This is info\n");
av_log(NULL, AV_LOG_DEBUG, "This is debug output\n");
return 0;
}
Log levels from most to least severe:
AV_LOG_QUIET(suppresses all output)AV_LOG_PANICAV_LOG_FATALAV_LOG_ERRORAV_LOG_WARNINGAV_LOG_INFOAV_LOG_VERBOSEAV_LOG_DEBUG
During development, set AV_LOG_DEBUG to see detailed internal FFmpeg logs. For production, AV_LOG_WARNING or AV_LOG_ERROR keeps output manageable.
Custom log callback
You can redirect FFmpeg's log output to your own logging system:
#include <libavutil/log.h>
#include <stdio.h>
#include <stdarg.h>
static void custom_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
if (level > av_log_get_level()) return;
char line[1024];
vsnprintf(line, sizeof(line), fmt, vl);
// Route to your logging system
fprintf(stderr, "[FFmpeg][%d] %s", level, line);
}
int main() {
av_log_set_callback(custom_log_callback);
av_log_set_level(AV_LOG_INFO);
av_log(NULL, AV_LOG_INFO, "This goes through our custom callback\n");
return 0;
}
Installation and Setup
If you haven't installed FFmpeg yet, see the complete FFmpeg installation guide for platform-specific instructions. Below is the quick version for C API development.
Ubuntu / Debian
sudo apt update
sudo apt install ffmpeg libavutil-dev
For the latest version (FFmpeg 8.1 as of March 2026), build from source:
git clone https://git.ffmpeg.org/ffmpeg.git
cd ffmpeg
git checkout release/8.1
./configure
make -j$(nproc)
sudo make install
macOS
brew install ffmpeg
To set up pkg-config path for development:
export PKG_CONFIG_PATH="$(brew --prefix)/lib/pkgconfig"
Windows
Download the development libraries (includes headers and .lib files) from ffmpeg.org/download.html and configure your IDE to link against them. For a proper build toolchain, MSYS2 with MinGW is recommended.
Compiling a program with libavutil
gcc -o my_program my_program.c $(pkg-config --cflags --libs libavutil)
If you also need format/codec support (most real projects do):
gcc -o my_program my_program.c \
$(pkg-config --cflags --libs libavutil libavformat libavcodec)
Common Errors and Solutions
undefined reference to 'av_malloc'
Cause: The library isn't being linked.
Fix: Add the library to your linker flags:
gcc -o my_program my_program.c $(pkg-config --cflags --libs libavutil)
# or manually:
gcc -o my_program my_program.c -lavutil
libavutil.so.XX: cannot open shared object file
Cause: The shared library path isn't in LD_LIBRARY_PATH.
Fix:
# Check which library is missing
ldd $(which ffmpeg) | grep libavutil
# Add the library path
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
# Register the library with the system linker
sudo ldconfig
Segmentation fault
Cause: Memory corruption — usually from accessing freed memory, using uninitialized pointers, or mismatched av_malloc/free calls.
Fix:
- Ensure every
av_mallocis paired withav_free - Never call
av_freeon a pointer you didn't get fromav_malloc - Use
av_freepto auto-null pointers after freeing - Use AddressSanitizer for quick detection:
gcc -fsanitize=address -g -o my_program my_program.c \
$(pkg-config --cflags --libs libavutil)
./my_program
Or use GDB to identify the crash location:
gdb --args ./my_program
run
bt
Practical Example: File Integrity Check with MD5
libavutil includes cryptographic hash functions. Here's a complete example computing an MD5 hash:
#include <libavutil/md5.h>
#include <stdio.h>
#include <string.h>
int main() {
AVMD5 *md5 = av_md5_alloc();
if (!md5) {
fprintf(stderr, "Failed to allocate MD5 context\n");
return 1;
}
const char *data = "Hello, FFmpeg!";
unsigned char digest[16];
av_md5_init(md5);
av_md5_update(md5, (const uint8_t*)data, strlen(data));
av_md5_final(md5, digest);
printf("MD5 of \"%s\": ", data);
for (int i = 0; i < 16; i++) {
printf("%02x", digest[i]);
}
printf("\n");
av_free(md5);
return 0;
}
Practical Example: Safe Error Handling Pattern
Here's a robust error-handling pattern for FFmpeg applications:
#include <libavutil/error.h>
#include <libavformat/avformat.h>
#include <stdio.h>
// Macro for consistent error checking
#define CHECK(ret, msg) \
do { \
if ((ret) < 0) { \
char errbuf[AV_ERROR_MAX_STRING_SIZE]; \
av_strerror(ret, errbuf, sizeof(errbuf)); \
fprintf(stderr, "%s: %s\n", msg, errbuf); \
return (ret); \
} \
} while (0)
int inspect_media_file(const char *filename) {
AVFormatContext *fmt_ctx = NULL;
int ret;
ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL);
CHECK(ret, "Could not open file");
ret = avformat_find_stream_info(fmt_ctx, NULL);
CHECK(ret, "Could not find stream info");
printf("Format: %s\n", fmt_ctx->iformat->name);
printf("Duration: %ld seconds\n", fmt_ctx->duration / AV_TIME_BASE);
printf("Number of streams: %u\n", fmt_ctx->nb_streams);
// Print metadata using AVDictionary iteration
const AVDictionaryEntry *tag = NULL;
while ((tag = av_dict_iterate(fmt_ctx->metadata, tag))) {
printf(" %s: %s\n", tag->key, tag->value);
}
avformat_close_input(&fmt_ctx);
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <input_file>\n", argv[0]);
return 1;
}
return inspect_media_file(argv[1]);
}
This pattern makes it easy to pinpoint exactly which step failed and why. Compile it with:
gcc -o inspect inspect.c $(pkg-config --cflags --libs libavutil libavformat)
Related Articles
- FFmpeg Installation Guide — Get FFmpeg installed on any platform
- FFmpeg Commands Guide — Using FFmpeg from the command line instead of the C API
- GPU Encoding with NVENC and QSV — Hardware-accelerated encoding that benefits from libavutil's
hwcontextAPI - FFmpeg Video Compression Guide — Practical compression techniques using the codecs libavutil supports
- Automating FFmpeg with Python — Use Python's
subprocessto drive FFmpeg without touching the C API - FFmpeg.wasm: Video Processing in the Browser — libavutil compiled to WebAssembly for client-side processing
- FFmpeg Commercial License Guide — LGPL vs GPL implications when linking libavutil in commercial products
FAQ
What is the difference between libavutil and libavcodec?
libavutil provides low-level utility functions (memory management, math, logging, error handling) that all other FFmpeg libraries depend on. libavcodec specifically handles encoding and decoding of audio and video codecs. You cannot use libavcodec without libavutil, but you can use libavutil on its own.
Can I use libavutil without the rest of FFmpeg?
Yes. libavutil has no dependencies on other FFmpeg libraries. You can link just -lavutil and use its memory functions, math utilities, hash functions, and logging. This is useful for small tools that need aligned memory allocation or timestamp arithmetic without full media processing.
Is libavutil thread-safe?
Most libavutil functions are thread-safe. Memory allocation (av_malloc, av_free), math functions (av_rescale_q), and error handling (av_strerror) can be called from multiple threads without synchronization. However, av_log_set_level and av_log_set_callback modify global state and should only be called during initialization.
What alignment does av_malloc use?
It depends on your build's SIMD support: 64 bytes for AVX-512, 32 bytes for AVX/AVX2, and 16 bytes for SSE/NEON. On a standard x86_64 Linux build, it's typically 32 bytes. This alignment ensures FFmpeg's SIMD-optimized routines don't crash on unaligned access.
How do I check which version of libavutil I have installed?
Use pkg-config or check programmatically:
pkg-config --modversion libavutil
Or in C code:
#include <libavutil/version.h>
printf("libavutil %d.%d.%d\n",
LIBAVUTIL_VERSION_MAJOR,
LIBAVUTIL_VERSION_MINOR,
LIBAVUTIL_VERSION_MICRO);
Should I use av_frame_clone or av_frame_ref?
Use av_frame_ref() when you want a new reference to the same underlying data (cheap, just increments refcount). Use av_frame_clone() when you need a completely independent copy. In most cases, av_frame_ref is what you want — it avoids unnecessary data copying.
What is AV_TIME_BASE and when do I use it?
AV_TIME_BASE is FFmpeg's internal time unit, defined as 1,000,000 (microseconds). Container-level timestamps like AVFormatContext.duration use this base. To convert to seconds, divide by AV_TIME_BASE. For stream-level timestamps, use the stream's own time_base with av_rescale_q.
How do I handle deprecated libavutil functions?
FFmpeg marks deprecated functions with attribute_deprecated and removes them in the next major version. Check the API changes documentation for migration paths. Compile with -Wdeprecated-declarations to catch usage of deprecated APIs early.
Wrapping Up
libavutil provides the low-level plumbing that makes FFmpeg development manageable:
- Memory: Use
av_malloc/av_freefor all FFmpeg-related allocations — alignment matters for SIMD - Time: Use
av_rescale_qfor timestamp conversion between timebases — never do manual multiplication - Frames: Use
AVFramewithav_frame_alloc/av_frame_freefor all decoded media data - Metadata: Use
AVDictionaryfor key-value pairs (options, metadata, codec settings) - Errors: Use
av_strerrorto convert error codes to readable messages - Logging: Use
av_logwith appropriate levels for debugging and production - Hashing: Use
av_md5_*functions for file integrity verification
Understanding libavutil is the foundation for working effectively with FFmpeg's C API. Every higher-level operation — decoding frames, applying filters, writing output — relies on the utilities this library provides.