32blogby StudioMitsu
Archive8 min read

FFmpeg libavutil Guide: Functions, Usage, and Code Examples

A deep dive into FFmpeg's libavutil library. Covers memory management, timestamp conversion, error handling, and pixel format utilities with complete C code examples.

If you're building applications on top of FFmpeg's C API, you'll encounter libavutil constantly. It's the foundational library that every other FFmpeg component depends on — providing memory management, math utilities, time calculations, pixel format helpers, and error handling.

This article explains what libavutil does, when to use it, and how to use its most important functions, with complete working code examples.

What you'll learn

  • The role of libavutil in the FFmpeg ecosystem
  • Memory management with av_malloc and av_free
  • Timestamp conversion with av_rescale_q
  • Error handling with av_strerror
  • Logging with av_log
  • 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

Where libavutil fits in the FFmpeg library stack

LibraryPrimary role
libavcodecAudio and video encoding/decoding
libavformatMedia container parsing and muxing
libavfilterVideo and audio filtering
libavutilUtility functions: memory, math, logging, error handling
libswscaleVideo rescaling and color space conversion
libswresampleAudio 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.

Core functions

  • av_malloc(size_t size) — allocate memory
  • av_free(void *ptr) — free allocated memory
  • av_mallocz(size_t size) — allocate and zero-initialize memory
  • av_realloc(void *ptr, size_t size) — resize allocated memory

Basic usage

c
#include <libavutil/mem.h>
#include <stdio.h>

int main() {
    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 (SSE, AVX). av_malloc ensures the correct alignment requirements are met. When passing buffers to FFmpeg's internal APIs, always use av_malloc to avoid alignment mismatches.

av_free must always be called on memory allocated with av_malloc. Mixing av_malloc with free, or malloc with av_free, is undefined behavior.


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.

Core functions

  • av_rescale(int64_t a, int64_t b, int64_t c) — compute a * b / c with overflow protection
  • av_rescale_q(int64_t a, AVRational bq, AVRational cq) — scale a timestamp between two timebases
  • av_log2(unsigned v) — log base 2

Timestamp conversion example

c
#include <libavutil/mathematics.h>
#include <libavutil/rational.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 = %ld ms\n", milliseconds); // 10000 ms = 10 seconds

    return 0;
}

This is essential when mixing streams with different timebases — for example, combining video (typically 1/timebase) with audio (typically 1/sample_rate).

Simple scaling example

c
#include <libavutil/mathematics.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 = %ld\n", result); // 1500

    return 0;
}

Image and Audio Format Utilities

Pixel format helpers

c
#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);

    return 0;
}

Audio buffer size calculation

c
#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.

c
#include <libavutil/error.h>
#include <stdio.h>

int main() {
    char errbuf[128];

    // 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

c
#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_PANIC
  • AV_LOG_FATAL
  • AV_LOG_ERROR
  • AV_LOG_WARNING
  • AV_LOG_INFO
  • AV_LOG_VERBOSE
  • AV_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.


Installation and Setup

Ubuntu / Debian

bash
sudo apt update
sudo apt install ffmpeg libavutil-dev

For the latest version, build from source:

bash
git clone https://git.ffmpeg.org/ffmpeg.git
cd ffmpeg
./configure
make -j$(nproc)
sudo make install

macOS

bash
brew install ffmpeg

To set up pkg-config path for development:

bash
export PKG_CONFIG_PATH=/usr/local/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

bash
gcc -o my_program my_program.c $(pkg-config --cflags --libs libavutil)

Common Errors and Solutions

undefined reference to 'av_malloc'

Cause: The library isn't being linked.

Fix: Add the library to your linker flags:

bash
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:

bash
# 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:

  1. Ensure every av_malloc is paired with av_free
  2. Never call av_free on a pointer you didn't get from av_malloc
  3. Use gdb to identify the crash location:
bash
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:

c
#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:

c
#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[128]; \
            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);

    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.


Summary

libavutil provides the low-level plumbing that makes FFmpeg development manageable:

  • Memory: Use av_malloc/av_free for all FFmpeg-related allocations
  • Time: Use av_rescale_q for timestamp conversion between timebases
  • Errors: Use av_strerror to convert error codes to readable messages
  • Logging: Use av_log with 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.