Artículos en profundidad sobre la tecnología que da forma al futuro.

FFmpeg al descubierto: el modelo mental que te falta

Deja de copiar comandos de FFmpeg a ciegas. Entiende contenedores, códecs, streams y filtros con recetas prácticas que realmente usarás.

Máquina separando flujos de película, audio y texto mediante embudos en un contenedor luminoso.

Todos hemos pasado por esto. Necesitas convertir un video, así que buscas en Google y encuentras una respuesta de Stack Overflow con un comando que parece que alguien aporreó el teclado: ffmpeg -i input.mov -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 128k -movflags +faststart output.mp4. Lo ejecutas. Funciona. No tienes ni idea de por qué, y la próxima vez que necesites algo ligeramente distinto, vuelves a Stack Overflow. Lo hice durante años hasta que por fin me senté a entender qué espera FFmpeg de mí.

La cosa es que FFmpeg no es tan complicado. Tiene un modelo mental coherente que, una vez que lo entiendes, hace que casi cualquier comando sea legible. El problema es que nadie explica el modelo; simplemente te lanzan comandos. Así que vamos a arreglar eso.

Contenedores frente a códecs: lo primero que hay que entender

La mayor fuente de confusión con los archivos de video es la diferencia entre contenedores y códecs. Un contenedor es el formato del archivo: .mp4, .mkv, .webm, .mov. Es una caja que contiene cosas. Un códec es la forma en que se comprimen los datos reales de video y audio dentro de esa caja: H.264, H.265, VP9, AV1, AAC, Opus.

Un archivo MP4 puede contener video H.264 con audio AAC, o video H.265 con audio Opus, o docenas de otras combinaciones. Un archivo MKV puede contener prácticamente cualquier cosa. Cuando alguien dice «convertir a MP4», normalmente quiere decir «meter video H.264 y audio AAC en un contenedor MP4», pero son tres decisiones separadas.

# See what's actually inside a file
$ ffprobe -hide_banner video.mkv
Input #0, matroska:
Stream #0:0: Video: hevc (Main), 1920x1080, 23.98 fps
Stream #0:1: Audio: opus, 48000 Hz, stereo
Stream #0:2: Subtitle: subrip
# This file is:
# Container: MKV (Matroska)
# Video codec: HEVC (H.265)
# Audio codec: Opus
# Plus a subtitle stream

Usa ffprobe antes de hacer cualquier cosa. Te dice exactamente con qué estás trabajando. La mitad de las veces, la «conversión» que necesitas es solo remuxar: mover los mismos streams a otro contenedor sin volver a codificar. Eso es casi instantáneo.

La estructura de comandos de FFmpeg

Todos los comandos de FFmpeg siguen el mismo patrón, y una vez que lo ves, no puedes dejar de verlo:

ffmpeg [global options] [input options] -i input [output options] output
# The KEY insight: options apply to the NEXT file.
# Options before -i apply to the input.
# Options after -i (before the output filename) apply to the output.
# This matters! These two commands are different:
ffmpeg -ss 00:01:00 -i video.mp4 output.mp4   # Seeks BEFORE decoding (fast)
ffmpeg -i video.mp4 -ss 00:01:00 output.mp4   # Seeks AFTER decoding (slow, precise)

El orden importa en FFmpeg. Es lo que confunde a todo el mundo. El significado de una opción puede cambiar según aparezca antes o después de -i. El ejemplo de -ss de arriba es el caso más común: si lo pones antes de -i, FFmpeg busca directamente en el archivo de entrada (rápido pero potencialmente impreciso), mientras que si lo pones después, FFmpeg decodifica todo hasta ese punto y luego empieza a escribir la salida (lento pero preciso fotograma a fotograma).

Selección de streams: indicarle a FFmpeg lo que quieres

Los archivos de video suelen contener varios streams: video, audio, subtítulos, a veces varias pistas de audio en distintos idiomas. FFmpeg usa una sintaxis de especificadores de stream para que puedas apuntar a streams concretos:

  • -c:v — códec para streams de video (v = video)
  • -c:a — códec para streams de audio (a = audio)
  • -c:s — códec para streams de subtítulos (s = subtitle)
  • -c copy — copia TODOS los streams sin volver a codificar
  • -c:v copy -c:a aac — copia el video tal cual y recodifica el audio a AAC
  • -map 0:a:1 — selecciona el segundo stream de audio del primer input

El flag -c copy es el más importante de conocer. Le dice a FFmpeg que copie los datos del stream directamente sin volver a codificar. Es básicamente instantáneo y sin pérdida: solo estás moviendo datos de un contenedor a otro. Siempre que no necesites cambiar el códec, usa -c copy.

# Remux MKV to MP4 without re-encoding (instant)
ffmpeg -i input.mkv -c copy output.mp4
# Copy video but re-encode audio (fast — only audio is processed)
ffmpeg -i input.mkv -c:v copy -c:a aac -b:a 192k output.mp4
# Extract just the audio stream
ffmpeg -i video.mp4 -vn -c:a copy audio.aac
# -vn means "no video" — drop the video stream entirely

Recetas prácticas que realmente vas a usar

Estos son los comandos que uso constantemente. Cada uno está explicado para que puedas modificarlo según tus necesidades.

Comprimir un video para la web

ffmpeg -i raw_video.mov \
-c:v libx264 \
-preset slow \
-crf 23 \
-c:a aac -b:a 128k \
-movflags +faststart \
web_video.mp4
# -c:v libx264     → encode video with H.264
# -preset slow     → slower encoding = smaller file (options: ultrafast to veryslow)
# -crf 23          → quality level (18=near lossless, 23=default, 28=low quality)
# -c:a aac         → encode audio as AAC
# -b:a 128k        → audio bitrate 128 kbps
# -movflags +faststart → move metadata to start of file for streaming

El valor de CRF (Constant Rate Factor) es el mando de calidad más importante. Cuanto menor sea el número, mayor será la calidad y más pesado el archivo. Para la mayoría de videos web, CRF 20-25 es el punto ideal. Yo suelo usar 22 para todo lo que tenga que verse bien y 26 para video de fondo o complementario, donde la calidad importa menos.

Recortar un video sin volver a codificar

# Extract from 1:30 to 3:45 — no re-encoding, nearly instant
ffmpeg -ss 00:01:30 -to 00:03:45 -i input.mp4 -c copy trimmed.mp4
# If you need frame-accurate cuts (slower):
ffmpeg -i input.mp4 -ss 00:01:30 -to 00:03:45 \
-c:v libx264 -crf 18 -c:a copy trimmed.mp4

Generar miniaturas

# Single thumbnail at the 10-second mark
ffmpeg -ss 00:00:10 -i video.mp4 -frames:v 1 thumb.jpg
# One thumbnail every 30 seconds
ffmpeg -i video.mp4 -vf "fps=1/30" thumbs/thumb_%04d.jpg
# Resize to 320px wide, maintain aspect ratio
ffmpeg -ss 00:00:10 -i video.mp4 -frames:v 1 \
-vf "scale=320:-1" thumb_small.jpg

Crear un GIF a partir de un clip de video

# Good quality GIF with palette generation (two-pass)
ffmpeg -ss 00:00:05 -t 3 -i video.mp4 \
-vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1]; \
[s0]palettegen[p];[s1][p]paletteuse" \
output.gif
# -t 3              → duration: 3 seconds
# fps=15            → 15 frames per second (keep GIFs small)
# scale=480:-1      → resize to 480px wide
# palettegen/use    → generate optimal color palette (much better quality)

Entendiendo los filtros: la función potente

Los filtros son lo que convierte a FFmpeg de «herramienta útil» en «absurdamente potente». Los flags -vf (filtro de video) y -af (filtro de audio) te permiten encadenar operaciones de procesamiento. Los filtros se separan con comas para el procesamiento secuencial, o con punto y coma para grafos de filtros complejos.

# Chain multiple filters: resize, then add padding for exact dimensions
ffmpeg -i input.mp4 -vf "scale=1280:720:force_original_aspect_ratio=decrease,\
pad=1280:720:(ow-iw)/2:(oh-ih)/2:black" output.mp4
# Normalize audio volume
ffmpeg -i input.mp4 -af "loudnorm" -c:v copy output.mp4
# Speed up a video 2x (with audio pitch correction)
ffmpeg -i input.mp4 -vf "setpts=0.5*PTS" -af "atempo=2.0" fast.mp4
# Add text overlay
ffmpeg -i input.mp4 \
-vf "drawtext=text='DRAFT':fontsize=72:fontcolor=red:x=10:y=10" \
watermarked.mp4

El sistema de filtros es lo bastante profundo como para llenar su propio artículo, pero la idea clave es esta: los filtros procesan fotogramas decodificados. Eso significa que no puedes usar -c copy con filtros: el video tiene que decodificarse, filtrarse y volver a codificarse. Si tu comando usa -vf, reserva tiempo para una recodificación completa.

Aceleración por hardware: usa tu GPU

Si codificas video con regularidad, la aceleración por hardware puede acelerar las cosas entre 5 y 10 veces. Las GPU modernas tienen hardware de codificación dedicado que FFmpeg puede usar.

# NVIDIA GPU (NVENC)
ffmpeg -i input.mp4 -c:v h264_nvenc -preset p4 -crf 23 output.mp4
# Apple Silicon (VideoToolbox)
ffmpeg -i input.mp4 -c:v h264_videotoolbox -b:v 5M output.mp4
# Intel Quick Sync (QSV)
ffmpeg -i input.mp4 -c:v h264_qsv -preset medium output.mp4
# Check what's available on your system:
ffmpeg -encoders 2>/dev/null | grep -E '(nvenc|videotoolbox|qsv|vaapi)'

Una advertencia: los codificadores por hardware son más rápidos, pero suelen producir archivos ligeramente más grandes con la misma calidad visual que la codificación por software (libx264). Para transcodificaciones puntuales donde la calidad importa, la codificación por software sigue siendo mejor. Para procesamiento por lotes o streaming en tiempo real, la aceleración por hardware es la clara ganadora. Yo uso libx264 para las codificaciones finales de producción y NVENC para todo lo demás.

Errores comunes y cómo evitarlos

  • Recodificar cuando no hace falta. Si solo vas a recortar, extraer audio o cambiar de contenedor, usa -c copy. No hay razón para pasar 20 minutos recodificando un video al que solo le quitas 30 segundos.
  • Usar un valor de CRF demasiado bajo. CRF 18 produce archivos enormes con mejoras de calidad que la mayoría de la gente no puede ver. Empieza en 23 y baja solo si ves artefactos. Para la mayoría de contenido web, 23-25 es más que suficiente.
  • Olvidar -movflags +faststart. Sin esto, los metadatos del MP4 quedan al final del archivo, lo que significa que los navegadores no pueden empezar a reproducir hasta que se descarga todo el archivo. Añádelo siempre en video web.
  • No revisar primero la entrada. Ejecuta ffprobe antes de escribir tu comando. Conocer los códecs de entrada, la resolución y la estructura de streams hace todo más fácil.
  • Enviar la salida a ninguna parte. Si FFmpeg parece atascado, puede que esté esperando que respondas a una pregunta (por ejemplo, si quieres sobrescribir un archivo existente). Añade -y para sobrescribir automáticamente, o -n para no sobrescribir nunca.

La chuleta

Esto es lo que guardo en un archivo de texto en mi escritorio. Cubre alrededor del 90% de lo que necesito día a día:

# Probe a file
ffprobe -hide_banner input.mp4
# Remux (change container, no re-encode)
ffmpeg -i input.mkv -c copy output.mp4
# Compress for web
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset medium \
-c:a aac -b:a 128k -movflags +faststart output.mp4
# Extract audio only
ffmpeg -i video.mp4 -vn -c:a copy audio.m4a
# Trim without re-encoding
ffmpeg -ss 00:01:00 -to 00:02:30 -i input.mp4 -c copy clip.mp4
# Resize to 720p
ffmpeg -i input.mp4 -vf "scale=-1:720" -c:a copy output.mp4
# Convert to GIF
ffmpeg -ss 5 -t 3 -i input.mp4 \
-vf "fps=12,scale=400:-1" output.gif
# Concatenate files (same codec)
echo "file 'part1.mp4'" > list.txt
echo "file 'part2.mp4'" >> list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy combined.mp4

FFmpeg no es una herramienta que se domine en un día. Pero una vez que entiendes el modelo mental (los contenedores contienen streams, los streams tienen códecs, las opciones se aplican al siguiente archivo, los filtros requieren recodificar), la documentación empieza a tener sentido. Dejas de copiar comandos a ciegas y empiezas a componerlos con intención. Y, sinceramente, ese paso de «esto es magia» a «ah, eso es lo que hace» es uno de los momentos más satisfactorios que puedes tener como desarrollador.