32blogby Studio Mitsu

Guía de FFmpeg libavutil: Funciones, uso y ejemplos de código

Un análisis profundo de la biblioteca libavutil de FFmpeg. Cubre gestión de memoria, conversión de marcas de tiempo, manejo de errores y utilidades de formato de píxel con ejemplos completos en C.

by omitsu17 min read
FFmpeglibavutilClibraryerror-handlingmemory management
Contenido

libavutil es la biblioteca base de FFmpeg — proporciona gestión de memoria, matemáticas de marcas de tiempo, manejo de errores, logging y helpers de formato de píxel/muestra de los que dependen todas las demás bibliotecas de FFmpeg. Si escribes código C contra la API de FFmpeg, incluirás headers de libavutil en prácticamente cada archivo.

Este artículo cubre las funciones que más usarás al construir aplicaciones de procesamiento de medios con la API en C de FFmpeg, con ejemplos de código completos que puedes compilar y ejecutar.

Lo que aprenderás

  • El rol de libavutil en el ecosistema FFmpeg
  • Gestión de memoria con av_malloc y av_free
  • Conversión de marcas de tiempo con av_rescale_q
  • Manejo de errores con av_strerror
  • Logging con av_log
  • Trabajo con AVFrame y AVDictionary
  • Cálculo de hash para verificación de integridad de archivos
  • Instalación y configuración de compilación

¿Qué es libavutil?

Resumen

libavutil es la biblioteca de utilidades central de FFmpeg. Proporciona los bloques de construcción de bajo nivel de los que dependen todas las demás bibliotecas de FFmpeg (libavcodec, libavformat, libavfilter y otras).

Capacidades clave:

  • Asignación de memoria y gestión de buffers
  • Operaciones matemáticas y escalado
  • Utilidades de formato de píxel y formato de muestra
  • Conversión de código de error a cadena de texto
  • Logging estructurado con control de nivel
  • Funciones de hash criptográfico (MD5, SHA1, HMAC)
  • Aritmética de números racionales
  • Frames con conteo de referencias (AVFrame)
  • Diccionarios de metadatos clave-valor (AVDictionary)

A partir de FFmpeg 8.1 (lanzado en marzo de 2026), la API central de libavutil permanece estable. La documentación oficial de Doxygen enumera 10 categorías de módulos que abarcan 148 archivos de cabecera.

Dónde encaja libavutil en la pila de bibliotecas de FFmpeg

BibliotecaRol principal
libavcodecCodificación y decodificación de audio y video
libavformatParsing y muxing de contenedores de medios
libavfilterFiltrado de video y audio
libavutilFunciones de utilidad: memoria, matemáticas, logging, manejo de errores
libswscaleReescalado de video y conversión de espacio de color
libswresampleRemuestreo de audio y conversión de formato

libavutil es la capa de cimientos. Rara vez la usas de forma aislada, pero siempre está presente cuando trabajas con la API en C de FFmpeg.

libavutilMemoria / Matemáticas / ErroresCimientoslibavcodecCodificar / DecodificarDepende delibavformatE/S de contenedoresLlamaAplicaciónTu código

Gestión de memoria

FFmpeg maneja grandes cantidades de datos durante el procesamiento de medios, haciendo que la gestión adecuada de memoria sea crítica. Los segfaults por mezclar llamadas av_malloc/free son uno de los problemas más comunes en el desarrollo con FFmpeg.

Funciones principales

  • av_malloc(size_t size) — asignar memoria
  • av_free(void *ptr) — liberar memoria asignada
  • av_mallocz(size_t size) — asignar e inicializar a cero
  • av_realloc(void *ptr, size_t size) — redimensionar memoria
  • av_freep(void **ptr) — liberar y establecer puntero a NULL (más seguro)

Uso básico

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

int main() {
    // Asignar un array de 10 enteros
    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;
}

¿Por qué usar av_malloc en lugar de malloc?

FFmpeg internamente usa asignación de memoria alineada para optimizar operaciones SIMD. El tamaño de alineación depende de las capacidades de tu CPU:

Característica CPUAlineación
AVX-51264 bytes
AVX / AVX232 bytes
SSE / NEON16 bytes

En un build típico x86_64 con AVX2, av_malloc devuelve memoria alineada a 32 bytes. Esto importa porque las rutinas DSP internas de FFmpeg usan instrucciones SIMD que crashean con datos no alineados. El malloc estándar solo garantiza alineación de 16 bytes en la mayoría de plataformas.

Usar malloc regular para los buffers de frames puede causar segfaults aleatorios que solo aparecen con ciertas resoluciones de video. Cambiar a av_malloc los resuelve — las rutas de código AVX2 necesitan alineación de 32 bytes, y malloc no la proporciona.

av_freep: La alternativa más segura

av_freep libera memoria y establece el puntero a NULL en un solo paso, previniendo bugs de use-after-free:

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

int main() {
    uint8_t *buffer = (uint8_t *) av_malloc(1024);
    if (!buffer) return 1;

    // ... usar buffer ...

    av_freep(&buffer);
    // buffer ahora es NULL — seguro verificar antes de usar
    printf("buffer after freep: %p\n", (void *)buffer); // (nil)

    return 0;
}

Operaciones matemáticas y cálculo de tiempo

El manejo de tiempo en FFmpeg está basado en timebase — un número racional que define lo que un "tick" representa. Por ejemplo, un timebase de 1/90000 significa que cada unidad PTS (Presentation Timestamp) es 1/90000 de segundo.

Diferentes streams pueden tener diferentes timebases, así que la conversión entre ellos es una operación común. Si estás trabajando con streaming HLS o muxing de múltiples streams, usarás av_rescale_q constantemente.

Funciones principales

  • av_rescale(int64_t a, int64_t b, int64_t c) — calcular a * b / c con protección contra desbordamiento
  • av_rescale_q(int64_t a, AVRational bq, AVRational cq) — escalar una marca de tiempo entre dos timebases
  • av_log2(unsigned v) — logaritmo base 2

Ejemplo de conversión de marca de tiempo

c
#include <libavutil/mathematics.h>
#include <libavutil/rational.h>
#include <inttypes.h>
#include <stdio.h>

int main() {
    // Convertir número de frame en timebase de 30fps a milisegundos
    AVRational src_timebase = {1, 30};    // 30 fps (una unidad = 1/30 segundo)
    AVRational dst_timebase = {1, 1000};  // milisegundos (una unidad = 1ms)

    int64_t frame_number = 300; // frame 300 a 30fps = 10 segundos

    int64_t milliseconds = av_rescale_q(frame_number, src_timebase, dst_timebase);
    printf("Frame 300 a 30fps = %" PRId64 " ms\n", milliseconds); // 10000 ms

    return 0;
}

Esto es esencial cuando mezclas streams con diferentes timebases — por ejemplo, combinando video (típicamente {1, 90000}) con audio (típicamente {1, 48000} para 48kHz).

¿Por qué av_rescale en lugar de multiplicación simple?

Un a * b / c ingenuo puede desbordar int64_t con marcas de tiempo grandes. av_rescale usa aritmética intermedia de 128 bits para evitar esto. Por ejemplo, al procesar una grabación de varias horas de livestream, las marcas de tiempo pueden desbordar y producir posiciones de seek completamente incorrectas en la salida.

Ejemplo de escalado simple

c
#include <libavutil/mathematics.h>
#include <inttypes.h>
#include <stdio.h>

int main() {
    // av_rescale: calcula a * b / c con redondeo correcto
    int64_t result = av_rescale(1000, 3, 2);
    printf("1000 * 3 / 2 = %" PRId64 "\n", result); // 1500

    return 0;
}

AVFrame y AVDictionary

Estas dos estructuras aparecen en todas partes del código FFmpeg, y ambas pertenecen a libavutil.

AVFrame — datos de medios decodificados

AVFrame contiene datos crudos de video o audio decodificados. Está gestionado por conteo de referencias a través del sistema AVBuffer de libavutil, así que copiar un frame es barato (incrementa un refcount, no duplica datos de píxeles).

c
#include <libavutil/frame.h>
#include <libavutil/imgutils.h>
#include <stdio.h>

int main() {
    AVFrame *frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "No se pudo asignar el frame\n");
        return 1;
    }

    frame->format = AV_PIX_FMT_YUV420P;
    frame->width  = 1920;
    frame->height = 1080;

    // Asignar el buffer de píxeles real
    int ret = av_frame_get_buffer(frame, 0); // 0 = alineación por defecto
    if (ret < 0) {
        fprintf(stderr, "No se pudo asignar el buffer del frame\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;
}

Puntos clave:

  • Siempre usa av_frame_alloc() / av_frame_free() — nunca pongas un AVFrame en el stack
  • av_frame_get_buffer() asigna datos de píxeles/muestras con alineación correcta
  • av_frame_ref() crea una nueva referencia a los mismos datos (copia barata)
  • av_frame_unref() libera una referencia

AVDictionary — metadatos clave-valor

AVDictionary es el almacén clave-valor de propósito general de FFmpeg. Se usa para metadatos de contenedores, opciones de códec y opciones de formato.

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

int main() {
    AVDictionary *dict = NULL;

    // Agregar entradas
    av_dict_set(&dict, "title", "My Video", 0);
    av_dict_set(&dict, "artist", "32blog", 0);
    av_dict_set_int(&dict, "track", 1, 0);

    // Buscar una entrada
    const AVDictionaryEntry *entry = av_dict_get(dict, "title", NULL, 0);
    if (entry) {
        printf("Title: %s\n", entry->value); // "My Video"
    }

    // Iterar todas las entradas
    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;
}

Encontrarás AVDictionary al leer metadatos de archivos, pasar opciones a códecs (avcodec_open2) y configurar contextos de formato.


Utilidades de formato de imagen y audio

Helpers de formato de píxel

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

int main() {
    // Obtener el nombre de un formato de píxel
    enum AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P;
    const char *name = av_get_pix_fmt_name(pix_fmt);
    printf("Formato de píxel: %s\n", name); // yuv420p

    // Buscar un formato de píxel por nombre
    enum AVPixelFormat found = av_get_pix_fmt("yuv420p");
    printf("Enum de formato encontrado: %d\n", found);

    // Obtener descripción detallada del formato
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
    printf("Componentes: %d, bits por píxel: %d\n",
           desc->nb_components,
           av_get_bits_per_pixel(desc));

    return 0;
}

La referencia de formatos de píxel lista todos los formatos disponibles. Los usarás al configurar la codificación o al trabajar con codificación acelerada por GPU donde se necesita conversión de formato.

Cálculo de tamaño de buffer de audio

c
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
#include <stdio.h>

int main() {
    // Calcular el tamaño de buffer necesario para 1024 muestras,
    // 2 canales (estéreo), formato entero con signo de 16 bits
    int buffer_size = av_samples_get_buffer_size(
        NULL,               // salida de linesize (NULL = no lo necesitamos)
        2,                  // canales
        1024,               // muestras por canal
        AV_SAMPLE_FMT_S16,  // formato de muestra (entero con signo de 16 bits)
        0                   // alineación (0 = por defecto)
    );
    printf("Tamaño del buffer: %d bytes\n", buffer_size); // 4096 bytes
    return 0;
}

Manejo de errores y logging

Conversión de código de error a cadena de texto

Las funciones de FFmpeg devuelven enteros negativos para errores. av_strerror los convierte a mensajes legibles.

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

int main() {
    char errbuf[AV_ERROR_MAX_STRING_SIZE];

    // AVERROR(EINVAL) = -22 (Argumento inválido)
    int errnum = AVERROR(EINVAL);
    av_strerror(errnum, errbuf, sizeof(errbuf));
    printf("Error: %s\n", errbuf); // "Invalid argument"

    // AVERROR_EOF = fin de archivo
    av_strerror(AVERROR_EOF, errbuf, sizeof(errbuf));
    printf("Error: %s\n", errbuf); // "End of file"

    return 0;
}

Logging estructurado

c
#include <libavutil/log.h>

int main() {
    // Establecer nivel mínimo de log (mensajes por debajo se suprimen)
    av_log_set_level(AV_LOG_DEBUG);

    // Registrar en diferentes niveles de severidad
    av_log(NULL, AV_LOG_ERROR,   "Esto es un error\n");
    av_log(NULL, AV_LOG_WARNING, "Esto es una advertencia\n");
    av_log(NULL, AV_LOG_INFO,    "Esto es info\n");
    av_log(NULL, AV_LOG_DEBUG,   "Esto es salida de depuración\n");

    return 0;
}

Niveles de log de mayor a menor severidad:

  • AV_LOG_QUIET (suprime toda la salida)
  • AV_LOG_PANIC
  • AV_LOG_FATAL
  • AV_LOG_ERROR
  • AV_LOG_WARNING
  • AV_LOG_INFO
  • AV_LOG_VERBOSE
  • AV_LOG_DEBUG

Durante el desarrollo, establece AV_LOG_DEBUG para ver los registros internos detallados de FFmpeg. Para producción, AV_LOG_WARNING o AV_LOG_ERROR mantiene la salida manejable.

Callback de log personalizado

Puedes redirigir la salida de log de FFmpeg a tu propio sistema de logging:

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

    // Redirigir a tu sistema de logging
    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, "Esto pasa por nuestro callback personalizado\n");
    return 0;
}

Instalación y configuración

Si aún no tienes FFmpeg instalado, consulta la guía completa de instalación de FFmpeg para instrucciones específicas por plataforma. Aquí va la versión rápida para desarrollo con la API en C.

Ubuntu / Debian

bash
sudo apt update
sudo apt install ffmpeg libavutil-dev

Para la última versión (FFmpeg 8.1 a marzo de 2026), compila desde el código fuente:

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

macOS

bash
brew install ffmpeg

Para configurar la ruta de pkg-config para desarrollo:

bash
export PKG_CONFIG_PATH="$(brew --prefix)/lib/pkgconfig"

Windows

Descarga las bibliotecas de desarrollo (incluye headers y archivos .lib) desde ffmpeg.org/download.html y configura tu IDE para enlazar contra ellas. Para una cadena de herramientas de compilación adecuada, se recomienda MSYS2 con MinGW.

Compilando un programa con libavutil

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

Si también necesitas soporte de formato/códec (la mayoría de proyectos reales lo necesitan):

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

Errores comunes y soluciones

undefined reference to 'av_malloc'

Causa: La biblioteca no se está enlazando.

Solución: Agrega la biblioteca a tus flags del enlazador:

bash
gcc -o my_program my_program.c $(pkg-config --cflags --libs libavutil)
# o manualmente:
gcc -o my_program my_program.c -lavutil

libavutil.so.XX: cannot open shared object file

Causa: La ruta de la biblioteca compartida no está en LD_LIBRARY_PATH.

Solución:

bash
# Verificar qué biblioteca falta
ldd $(which ffmpeg) | grep libavutil

# Agregar la ruta de la biblioteca
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

# Registrar la biblioteca con el enlazador del sistema
sudo ldconfig

Fallo de segmentación (Segmentation fault)

Causa: Corrupción de memoria — usualmente por acceder a memoria liberada, usar punteros no inicializados o mezclar llamadas av_malloc/free.

Solución:

  1. Asegúrate de que cada av_malloc esté emparejado con av_free
  2. Nunca llames av_free sobre un puntero que no obtuviste de av_malloc
  3. Usa av_freep para auto-nullificar punteros después de liberar
  4. Usa AddressSanitizer para detección rápida:
bash
gcc -fsanitize=address -g -o my_program my_program.c \
    $(pkg-config --cflags --libs libavutil)
./my_program

O usa GDB para identificar la ubicación del crash:

bash
gdb --args ./my_program
run
bt

Ejemplo práctico: Verificación de integridad de archivo con MD5

libavutil incluye funciones de hash criptográfico. Aquí hay un ejemplo completo calculando un hash MD5:

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 de \"%s\": ", data);
    for (int i = 0; i < 16; i++) {
        printf("%02x", digest[i]);
    }
    printf("\n");

    av_free(md5);
    return 0;
}

Ejemplo práctico: Patrón seguro de manejo de errores

Aquí está un patrón robusto de manejo de errores para aplicaciones FFmpeg:

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

// Macro para verificación de errores consistente
#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, "No se pudo abrir el archivo");

    ret = avformat_find_stream_info(fmt_ctx, NULL);
    CHECK(ret, "No se pudo encontrar info de streams");

    printf("Formato: %s\n", fmt_ctx->iformat->name);
    printf("Duración: %ld segundos\n", fmt_ctx->duration / AV_TIME_BASE);
    printf("Número de streams: %u\n", fmt_ctx->nb_streams);

    // Imprimir metadatos usando iteración de AVDictionary
    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("Uso: %s <archivo_entrada>\n", argv[0]);
        return 1;
    }
    return inspect_media_file(argv[1]);
}

Este patrón facilita identificar exactamente qué paso falló y por qué. Compílalo con:

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

Artículos relacionados


FAQ

¿Cuál es la diferencia entre libavutil y libavcodec?

libavutil proporciona funciones de utilidad de bajo nivel (gestión de memoria, matemáticas, logging, manejo de errores) de las que dependen todas las demás bibliotecas de FFmpeg. libavcodec se encarga específicamente de la codificación y decodificación de códecs de audio y video. No puedes usar libavcodec sin libavutil, pero sí puedes usar libavutil por sí solo.

¿Puedo usar libavutil sin el resto de FFmpeg?

Sí. libavutil no tiene dependencias de otras bibliotecas de FFmpeg. Puedes enlazar solo -lavutil y usar sus funciones de memoria, utilidades matemáticas, funciones de hash y logging. Esto es útil para herramientas pequeñas que necesitan asignación de memoria alineada o aritmética de marcas de tiempo sin procesamiento completo de medios.

¿Es libavutil thread-safe?

La mayoría de las funciones de libavutil son thread-safe. La asignación de memoria (av_malloc, av_free), las funciones matemáticas (av_rescale_q) y el manejo de errores (av_strerror) se pueden llamar desde múltiples hilos sin sincronización. Sin embargo, av_log_set_level y av_log_set_callback modifican estado global y solo deberían llamarse durante la inicialización.

¿Qué alineación usa av_malloc?

Depende del soporte SIMD de tu build: 64 bytes para AVX-512, 32 bytes para AVX/AVX2 y 16 bytes para SSE/NEON. En un build estándar de Linux x86_64, típicamente son 32 bytes. Esta alineación asegura que las rutinas optimizadas con SIMD de FFmpeg no crasheen por acceso no alineado.

¿Cómo verifico qué versión de libavutil tengo instalada?

Usa pkg-config o verifica programáticamente:

bash
pkg-config --modversion libavutil

O en código C:

c
#include <libavutil/version.h>
printf("libavutil %d.%d.%d\n",
       LIBAVUTIL_VERSION_MAJOR,
       LIBAVUTIL_VERSION_MINOR,
       LIBAVUTIL_VERSION_MICRO);

¿Debo usar av_frame_clone o av_frame_ref?

Usa av_frame_ref() cuando quieras una nueva referencia a los mismos datos subyacentes (barato, solo incrementa el refcount). Usa av_frame_clone() cuando necesites una copia completamente independiente. En la mayoría de los casos, av_frame_ref es lo que quieres — evita copias de datos innecesarias.

¿Qué es AV_TIME_BASE y cuándo lo uso?

AV_TIME_BASE es la unidad de tiempo interna de FFmpeg, definida como 1,000,000 (microsegundos). Las marcas de tiempo a nivel de contenedor como AVFormatContext.duration usan esta base. Para convertir a segundos, divide por AV_TIME_BASE. Para marcas de tiempo a nivel de stream, usa el time_base propio del stream con av_rescale_q.

¿Cómo manejo funciones deprecadas de libavutil?

FFmpeg marca las funciones deprecadas con attribute_deprecated y las elimina en la siguiente versión mayor. Consulta la documentación de cambios de API para rutas de migración. Compila con -Wdeprecated-declarations para detectar el uso de APIs deprecadas tempranamente.


Conclusión

libavutil proporciona la fontanería de bajo nivel que hace manejable el desarrollo con FFmpeg:

  • Memoria: Usa av_malloc/av_free para todas las asignaciones relacionadas con FFmpeg — la alineación importa para SIMD
  • Tiempo: Usa av_rescale_q para conversión de marcas de tiempo entre timebases — nunca hagas multiplicación manual
  • Frames: Usa AVFrame con av_frame_alloc/av_frame_free para todos los datos de medios decodificados
  • Metadatos: Usa AVDictionary para pares clave-valor (opciones, metadatos, configuración de códec)
  • Errores: Usa av_strerror para convertir códigos de error a mensajes legibles
  • Logging: Usa av_log con niveles apropiados para depuración y producción
  • Hashing: Usa las funciones av_md5_* para verificación de integridad de archivos

Entender libavutil es la base para trabajar efectivamente con la API en C de FFmpeg. Cada operación de nivel superior — decodificación de fotogramas, aplicación de filtros, escritura de salida — depende de las utilidades que esta biblioteca proporciona.