Si alguna vez has intentado convertir un vídeo a formato Telegram, cortar un fragmento de un stream, o descargar un vídeo de YouTube y te has encontrado con errores crípticos como Invalid data found when processing input o Unable to find a suitable output format, sabes de lo que hablo. La sintaxis de ffmpeg es brutal: cientos de flags, formatos de entrada y salida, códecs con nombres que parecen matemáticas pura. Y yt-dlp, aunque mucho más amigable, también tiene sus trampas cuando necesitas calidad específica o merge de formatos.
Este post no es un manual exhaustivo de las 3000 opciones de ffmpeg (para eso está la documentación oficial). Es una recopilación práctica de los comandos que realmente uso: descargar vídeos con calidad específica, convertir entre formatos, cortar sin perder calidad, comprimir para Telegram, y manejar streams en vivo. Cada sección explica qué necesitas resolver, por qué ese comando funciona y cómo adaptarlo a tu caso. Si necesitas instalar estas herramientas con Docker en WSL/Ubuntu, primero consulta el post Docker: ffmpeg y yt-dlp en Windows (WSL) y Ubuntu.
Referencias oficiales
| Herramienta | Documentación oficial |
|---|---|
| ffmpeg | ffmpeg.org/documentation.html |
| ffmpeg Wiki | trac.ffmpeg.org/wiki |
| ffmpeg H.264 | trac.ffmpeg.org/wiki/Encode/H.264 |
| ffmpeg H.265 | trac.ffmpeg.org/wiki/Encode/H.265 |
| yt-dlp | github.com/yt-dlp/yt-dlp |
| yt-dlp Wiki | github.com/yt-dlp/yt-dlp/wiki |
yt-dlp: Quick Start
Descarga básica
# Descargar vídeo con mejor calidad disponible
yt-dlp "URL"
# Listar todos los formatos disponibles
yt-dlp -F "URL"
# Descargar con mejor vídeo + mejor audio (requiere ffmpeg para unir)
yt-dlp -f "bv+ba/b" "URL"
# Forzar merge a mp4
yt-dlp -f "bv+ba/b" --merge-output-format mp4 "URL"
# Simular sin descargar (mostraría qué haría)
yt-dlp --simulate "URL"
# Verificar versión
yt-dlp --version
# Actualizar yt-dlp
yt-dlp -U
Selección de formato y calidad
# Máxima calidad disponible
yt-dlp -f "bestvideo+bestaudio/best" "URL"
# Cap a 1080p máximo
yt-dlp -f "bestvideo[height<=1080]+bestaudio/best" "URL"
# Forzar 4K si está disponible
yt-dlp -f "bestvideo[height>=2160]+bestaudio/best" "URL"
# Preferir H.264/MP4 (máxima compatibilidad)
yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" "URL"
# Preferir VP9 (mejor calidad en mismo bitrate)
yt-dlp -f "bestvideo[vcodec^=vp9]+bestaudio/best" "URL"
# Calidad específica (ejemplo: formato 22 = 720p mp4)
yt-dlp -f 22 "URL"
# Solo vídeo sin audio
yt-dlp -f "bestvideo" "URL"
# Solo audio sin vídeo
yt-dlp -f "bestaudio" "URL"
Extraer solo audio
# Extraer audio y convertir a mp3
yt-dlp -x --audio-format mp3 "URL"
# Extraer audio a calidad máxima (0 = mejor)
yt-dlp -x --audio-format mp3 --audio-quality 0 "URL"
# Extraer audio a 128k
yt-dlp -x --audio-format mp3 --audio-quality 128k "URL"
# Extraer audio como opus (menor tamaño, mejor calidad)
yt-dlp -x --audio-format opus "URL"
# Extraer audio como aac
yt-dlp -x --audio-format m4a "URL"
# Extraer audio sin re-codificar (si ya es mp3, solo extraer)
yt-dlp -x --audio-format mp3 --audio-quality -1 "URL"
# Extraer audio a flac (lossless)
yt-dlp -x --audio-format flac "URL"
Playlists
# Descargar playlist completa
yt-dlp "URL_PLAYLIST"
# Descargar solo los primeros 5 vídeos
yt-dlp --playlist-items 1-5 "URL_PLAYLIST"
# Descargar del vídeo 3 al 10
yt-dlp --playlist-items 3-10 "URL_PLAYLIST"
# Solo vídeos nuevos (guardar en archivo de archive)
yt-dlp --download-archive watched.txt "URL_PLAYLIST"
# Descargar solo vídeos de los últimos 30 días
yt-dlp --dateafter today-30days "URL_PLAYLIST"
# No descargar, solo listar títulos
yt-dlp --flat-playlist --print title "URL_PLAYLIST"
# Mantener orden de la playlist
yt-dlp --yes-playlist "URL_PLAYLIST"
# Invertir orden de descarga
yt-dlp --playlist-reverse "URL_PLAYLIST"
# Mezclar orden aleatorio
yt-dlp --playlist-random "URL_PLAYLIST"
Subtítulos
# Descargar subtítulos automáticos
yt-dlp --write-auto-subs "URL"
# Descargar subtítulos en español e inglés
yt-dlp --write-subs --sub-lang es,en "URL"
# Solo subtítulos (sin vídeo)
yt-dlp --write-subs --sub-lang es --skip-download "URL"
# Formato específico de subtítulos
yt-dlp --write-subs --sub-format srt "URL"
# Incrustar subtítulos en el vídeo (requiere ffmpeg)
yt-dlp --embed-subs --sub-lang es "URL"
# Subtítulos en todos los idiomas disponibles
yt-dlp --all-subs "URL"
Metadatos y thumbnails
# Incrustar thumbnail en el archivo
yt-dlp --embed-thumbnail "URL"
# Incrustar metadata (título, autor, etc.)
yt-dlp --embed-metadata "URL"
# Incrustar todo (thumbnail + metadata + subtítulos)
yt-dlp --embed-thumbnail --embed-metadata --embed-subs "URL"
# Guardar thumbnail como archivo separado
yt-dlp --write-thumbnail "URL"
# Guardar descripción del vídeo
yt-dlp --write-description "URL"
# Guardar información en formato JSON
yt-dlp -j "URL" > info.json
Cookies y autenticación
# Usar cookies del navegador (para vídeos privados o con restricción de edad)
yt-dlp --cookies-from-browser chrome "URL"
# Usar cookies de Firefox
yt-dlp --cookies-from-browser firefox "URL"
# Usar cookies de Edge
yt-dlp --cookies-from-browser edge "URL"
# Usar archivo de cookies (Netscape format)
yt-dlp --cookies cookies.txt "URL"
# Login con usuario y contraseña (no recomendado, usar cookies)
yt-dlp --username usuario --password contraseña "URL"
Rendimiento y red
# Fragmentos paralelos (más rápido en conexiones buenas)
yt-dlp -N 4 "URL"
# Limitar velocidad de descarga
yt-dlp --limit-rate 5M "URL"
# Limitar a 500KB/s
yt-dlp --limit-rate 500K "URL"
# Reintentos en fallos
yt-dlp --retries 10 --fragment-retries 10 "URL"
# Intervalo aleatorio entre descargas (evitar bans)
yt-dlp --sleep-interval 5 --max-sleep-interval 10 "URL"
# Usar proxy
yt-dlp --proxy "socks5://127.0.0.1:1080" "URL"
# Forzar IPv4
yt-dlp -4 "URL"
# Forzar IPv6
yt-dlp -6 "URL"
# Timeout de conexión
yt-dlp --socket-timeout 30 "URL"
Plantillas de salida
# Estructura por canal
yt-dlp -o "%(channel)s/%(title)s.%(ext)s" "URL"
# Con fecha
yt-dlp -o "%(channel)s/%(upload_date>%Y-%m-%d)s-%(title)s.%(ext)s" "URL"
# Con índice de playlist
yt-dlp -o "%(playlist_index)02d-%(title)s.%(ext)s" "URL_PLAYLIST"
# Variables disponibles:
# %(title)s → Título del vídeo
# %(ext)s → Extensión del archivo
# %(id)s → ID del vídeo
# %(upload_date)s → Fecha (YYYYMMDD)
# %(channel)s → Nombre del canal
# %(duration)s → Duración en segundos
# %(view_count)s → Número de vistas
Post-procesamiento
# Re-codificar a mp4 después de descargar
yt-dlp --recode-video mp4 "URL"
# Ejecutar comando post-descarga
yt-dlp --exec "echo Descargado: {}" "URL"
# Abrir con reproductor después de descargar
yt-dlp --exec "xdg-open" "URL"
# No re-codificar, solo cambiar contenedor
yt-dlp --merge-output-format mp4 "URL"
# Mover archivo a carpeta específica
yt-dlp --exec "mv {} ~/Videos/" "URL"
Grabación de livestream
# Grabar livestream desde el principio
yt-dlp --live-from-start "URL"
# Grabar fragmento específico de un directo
yt-dlp --live-from-start --live-fragments-start 100 "URL"
# Grabar solo el segmento actual (no desde el inicio)
yt-dlp "URL_LIVE"
Archivo de configuración
Crea ~/.config/yt-dlp/config:
--format bv*+ba
--merge-output-format mp4
--embed-thumbnail
--embed-metadata
--write-subs
--sub-lang es,en
--output ~/Videos/%(channel)s/%(title)s.%(ext)s
Se ejecuta automáticamente al usar yt-dlp. Puedes especificar otra ubicación:
yt-dlp --config-location ~/mi_config.txt "URL"
ffmpeg: Quick Start
Conversión de formatos
# Convertir a mp4
ffmpeg -i input.avi output.mp4
# Convertir a mkv
ffmpeg -i input.mp4 output.mkv
# Convertir a webm (VP9)
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 output.webm
# Convertir a mov (Apple)
ffmpeg -i input.mp4 output.mov
# Sin re-codificar (solo cambiar contenedor)
ffmpeg -i input.mkv -c copy output.mp4
# Cambiar extensión sin tocar codec
ffmpeg -i input.ts -c copy output.mp4
Extracción de audio
# Extraer audio sin re-codificar
ffmpeg -i video.mp4 -vn -c:a copy audio.aac
# Extraer y convertir a mp3
ffmpeg -i video.mp4 -vn -c:a libmp3lame -b:a 192k audio.mp3
# Extraer a wav (sin compresión)
ffmpeg -i video.mp4 -vn -c:a pcm_s16le audio.wav
# Extraer a flac (lossless)
ffmpeg -i video.mp4 -vn -c:a flac audio.flac
# Extraer solo un canal (mono)
ffmpeg -i video.mp4 -vn -ac 1 audio.mp3
# Extraer a 22050 Hz (para voz/podcasts)
ffmpeg -i video.mp4 -vn -ar 22050 audio.mp3
# Extraer solo un segmento de audio
ffmpeg -i video.mp4 -ss 00:01:00 -to 00:02:00 -vn -c:a libmp3lame audio.mp3
Corte de vídeo
# Cortar por tiempo (rápido, sin re-codificar)
ffmpeg -i input.mp4 -ss 00:01:00 -to 00:02:00 -c copy output.mp4
# Cortar con re-codificación (preciso)
ffmpeg -i input.mp4 -ss 00:01:00 -to 00:02:00 -c:v libx264 -c:a aac output.mp4
# Cortar los primeros 30 segundos
ffmpeg -i input.mp4 -t 00:00:30 -c copy output.mp4
# Cortar desde el segundo 60 hasta el final
ffmpeg -i input.mp4 -ss 00:01:00 -c copy output.mp4
# Cortar con precisión (para streaming)
ffmpeg -i input.mp4 -ss 00:05:00 -t 00:10:00 -c:v libx264 -c:a aac -avoid_negative_ts make_zero output.mp4
Concatenación de vídeos
# Método 1: filter_complex (mismos formatos)
ffmpeg -i video1.mp4 -i video2.mp4 -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" output.mp4
# Método 2: archivo de lista (para muchos archivos)
# Crear archivo lista.txt:
# file 'video1.mp4'
# file 'video2.mp4'
# file 'video3.mp4'
ffmpeg -f concat -safe 0 -i lista.txt -c copy output.mp4
# Concatenar con transición de fade
ffmpeg -i v1.mp4 -i v2.mp4 -filter_complex "[0:v]fade=t=out:st=4:d=1[v0];[1:v]fade=t=in:st=0:d=1[v1];[v0][v1]concat=n=2:v=1:a=0" output.mp4
Concatenación inteligente (con auto-detección)
Cuando los archivos tienen codecs, resoluciones o FPS distintos, el concat básico falla o produce corruptos. La solución es detectar compatibilidad y usar stream copy o re-encode automáticamente:
# Detectar si dos archivos son compatibles para concat
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,r_frame_rate -of csv=p=0 video1.mp4
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,r_frame_rate -of csv=p=0 video2.mp4
# Si la salida es idéntica → stream copy (rápido)
# Si difiere → re-encode con filter_complex concat
# Crossfade entre dos vídeos (transición suave)
ffmpeg -i v1.mp4 -i v2.mp4 \
-filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=9[v]; \
[0:a][1:a]acrossfade=d=1[a]" \
-map "[v]" -map "[a]" -c:v libx264 -preset fast -c:a aac output.mp4
Redimensionado y escalado
# Escalar a 1280x720
ffmpeg -i input.mp4 -vf scale=1280:720 output.mp4
# Manteniendo aspect ratio (con padding negro)
ffmpeg -i input.mp4 -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" output.mp4
# Escalar al 50%
ffmpeg -i input.mp4 -vf scale=iw/2:ih/2 output.mp4
# Escalar por ancho (alto automático)
ffmpeg -i input.mp4 -vf "scale=1280:-2" output.mp4
# Cropping (recortar)
ffmpeg -i input.mp4 -vf "crop=640:480:100:50" output.mp4
# crop=ancho:alto:x:y
# Cropping centrado
ffmpeg -i input.mp4 -vf "crop=640:480:(iw-640)/2:(ih-480)/2" output.mp4
Video a GIF
# GIF básico
ffmpeg -i input.mp4 output.gif
# GIF con calidad (paleta optimizada)
ffmpeg -i input.mp4 -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gif
# GIF de un segmento específico
ffmpeg -i input.mp4 -ss 00:01:00 -t 5 -vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gif
Marca de agua
# Imagen como marca de agua (esquina inferior derecha)
ffmpeg -i video.mp4 -i logo.png -filter_complex "overlay=W-w-10:H-h-10" output.mp4
# Imagen en esquina superior izquierda
ffmpeg -i video.mp4 -i logo.png -filter_complex "overlay=10:10" output.mp4
# Texto como marca de agua
ffmpeg -i video.mp4 -vf "drawtext=text='Mi Marca':fontsize=24:fontcolor=white:x=10:y=10" output.mp4
# Texto con sombra
ffmpeg -i video.mp4 -vf "drawtext=text='© 2026':fontsize=20:fontcolor=white:borderw=2:bordercolor=black:x=W-tw-10:y=H-th-10" output.mp4
# Marca de agua semi-transparente
ffmpeg -i video.mp4 -i logo.png -filter_complex "format=rgba,colorchannelmixer=aa=0.3[wm];[0:v][wm]overlay=W-w-10:H-h-10" output.mp4
Filtros de vídeo
# Ajustar brillo (0.0 a 2.0, 1.0 = normal)
ffmpeg -i input.mp4 -vf "eq=brightness=0.1" output.mp4
# Ajustar contraste (0.0 a 2.0, 1.0 = normal)
ffmpeg -i input.mp4 -vf "eq=contrast=1.2" output.mp4
# Ajustar saturación
ffmpeg -i input.mp4 -vf "eq=saturation=1.5" output.mp4
# Filtro de nitidez
ffmpeg -i input.mp4 -vf "unsharp=5:5:1.0" output.mp4
# Voltear horizontalmente
ffmpeg -i input.mp4 -vf "hflip" output.mp4
# Rotar 90 grados
ffmpeg -i input.mp4 -vf "transpose=1" output.mp4
# Rotar 180 grados
ffmpeg -i input.mp4 -vf "transpose=2,transpose=2" output.mp4
Subtítulos
# Incrustar subtítulos SRT en el vídeo
ffmpeg -i video.mp4 -vf "subtitles=subtitles.srt" output.mp4
# Incrustar con estilo personalizado
ffmpeg -i video.mp4 -vf "subtitles=subtitles.srt:force_style='FontSize=24,PrimaryColour=&H00FFFFFF'" output.mp4
# Incrustar subtítulos ASS
ffmpeg -i video.mp4 -vf "ass=subtitles.ass" output.mp4
Normalización de audio
# Normalización simple (loudnorm)
ffmpeg -i input.mp4 -af "loudnorm" output.mp4
# Normalización dinámica
ffmpeg -i input.mp4 -af "dynaudnorm" output.mp4
# Ajustar volumen (2x más fuerte)
ffmpeg -i input.mp4 -af "volume=2.0" output.mp4
# Ajustar volumen (50% más bajo)
ffmpeg -i input.mp4 -af "volume=0.5" output.mp4
# Ecualización (ejemplo: realzar graves)
ffmpeg -i input.mp4 -af "equalizer=f=100:t=q:w=1:g=5" output.mp4
# Fade in de audio
ffmpeg -i input.mp4 -af "afade=t=in:st=0:d=2" output.mp4
# Fade out de audio
ffmpeg -i input.mp4 -af "afade=t=out:st=58:d=2" output.mp4
Dos pasadas (two-pass) para mejor calidad
# Pasada 1: análisis
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -pass 1 -f null /dev/null
# Pasada 2: codificación
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -pass 2 output.mp4
# Para H.265/HEVC
ffmpeg -i input.mp4 -c:v libx265 -b:v 3M -pass 1 -f null /dev/null
ffmpeg -i input.mp4 -c:v libx265 -b:v 3M -pass 2 output.mp4
Streaming HLS
# Preparar para streaming HLS
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f hls -hls_time 4 -hls_playlist_type vod output.m3u8
# Multi-bitrate (adaptativo)
ffmpeg -i input.mp4 \
-map 0 -b:v:0 500k -s:v:0 640x360 -c:v libx264 \
-b:v:1 1000k -s:v:1 1280:720 -c:v libx264 \
-b:v:2 2000k -s:v:2 1920:1080 -c:v libx264 \
-hls_time 4 -hls_playlist_type vod \
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" \
stream_%v.m3u8
Procesamiento por lotes
# Convertir todos los avi a mp4
for file in *.avi; do ffmpeg -i "$file" "${file%.avi}.mp4"; done
# Convertir todos los mkv a mp4 sin re-codificar
for file in *.mkv; do ffmpeg -i "$file" -c copy "${file%.mkv}.mp4"; done
# Redimensionar todos los vídeos a 720p
for file in *.mp4; do ffmpeg -i "$file" -vf scale=1280:720 "${file%.mp4}_720p.mp4"; done
# Extraer audio de todos los vídeos
for file in *.mp4; do ffmpeg -i "$file" -vn -c:a libmp3lame "${file%.mp4}.mp3"; done
Opciones útiles
# Sobreescribir sin preguntar
ffmpeg -y -i input.mp4 output.mp4
# No sobreescribir
ffmpeg -n -i input.mp4 output.mp4
# Ocultar banner de información
ffmpeg -hide_banner -i input.mp4
# Ver información del archivo
ffprobe -v error -show_format -show_streams input.mp4
# Ver duración
ffprobe -v error -show_entries format=duration -of csv=p=0 input.mp4
# Ver codec de vídeo
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 input.mp4
# Ver codec de audio
ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 input.mp4
# Ver resolución
ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 input.mp4
# Ver FPS
ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of csv=p=0 input.mp4
Mejores prácticas de encoding
Para una guía completa de compresión de vídeo, consulta la FFmpeg Video Compression Best Practices Guide.
Tabla de CRF por codec y uso
CRF (Constant Rate Factor) controla la calidad. Menor valor = mejor calidad = archivo más grande.
| Caso de uso | H.264 CRF | H.265 CRF | Notas |
|---|---|---|---|
| Pérdida cercana a cero | 0–18 | 0–20 | Para archivado, backups |
| Alta calidad | 18–22 | 22–26 | Entrega de calidad |
| Web / uso general | 23–26 | 27–30 | Streaming, publicación |
| Streaming / móvil | 27–32 | 30–35 | Bandwidth limitado |
| Screencasts / presentaciones | 28–35 | 32–38 | Poco movimiento |
Regla práctica: Cada +6 en CRF aproxima duplica el tamaño del archivo.
Presets: velocidad vs compresión
El parámetro -preset controla cuánto tiempo dedica el encoder a optimizar. Más lento = mejor compresión.
| Preset | Velocidad relativa | Tamaño archivo | Uso recomendado |
|---|---|---|---|
ultrafast | ~15x más rápido | ~40-50% más grande | Testing, previews |
veryfast | ~8x más rápido | ~25% más grande | Tiempo real |
fast | ~3x más rápido | ~10% más grande | Lotes donde velocidad importa |
medium | 1x (default) | Default | Uso general |
slow | ~0.5x | ~5% más pequeño | Renders finales, contenido importante |
veryslow | ~0.1x | ~10-15% más pequeño | Archivado, raramente vale la pena |
Recomendación: medium para uso general, fast para lotes grandes, slow para contenido que se servirá muchas veces.
Comparación de códecs
| Códec | Tamaño relativo | Velocidad encoding | Compatibilidad (2026) |
|---|---|---|---|
| H.264 (libx264) | 100% (baseline) | Rápido | 99%+ (universal) |
| H.265 (libx265) | 55-65% | 3-5x más lento | iOS, Android 5+, Smart TVs modernos |
| VP9 (libvpx-vp9) | 55-65% | 5-10x más lento | Chrome, Firefox, Edge |
| AV1 (SVT-AV1) | 40-50% | 3-8x más lento | Chrome 17+, Firefox, Safari 17+ |
¿Qué códec usar?
- Máxima compatibilidad: H.264
- Archivos más pequeños (Apple/móvil): H.265
- Web, royalty-free: VP9
- Futuro, mejor compresión: AV1
Comandos de ejemplo por códec
# H.264 — máxima compatibilidad
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4
# H.265 — 40% más pequeño
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset medium -c:a aac -b:a 128k output.mp4
# VP9 — royalty-free, bueno para web
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -row-mt 1 -c:a libopus -b:a 128k output.webm
# AV1 (SVT-AV1) — mejor compresión
ffmpeg -i input.mp4 -c:v libsvtav1 -crf 30 -preset 6 -c:a libopus -b:a 128k output.mp4
Resolución y bitrate recomendado
| Resolución | Bitrate típico H.264 | Bitrate típico H.265 |
|---|---|---|
| 4K (3840x2160) | 15-30 Mbps | 8-15 Mbps |
| 1440p (2560x1440) | 8-15 Mbps | 4-8 Mbps |
| 1080p (1920x1080) | 4-8 Mbps | 2-4 Mbps |
| 720p (1280x720) | 2-4 Mbps | 1-2 Mbps |
| 480p (854x480) | 1-2 Mbps | 0.5-1 Mbps |
| 360p (640x360) | 0.5-1 Mbps | 0.3-0.5 Mbps |
¿Cuándo usar CRF vs Two-pass?
- CRF: Cuando priorizas calidad constante (web, archivado). Más simple, una sola pasada.
- Two-pass: Cuando necesitas un tamaño de archivo específico (ej: Discord 25MB, límite de email). Dos pasadas, más lento pero preciso.
¿Cuándo usar -c copy?
Cuando el vídeo ya está en el códec correcto (H.264+AAC) y solo necesitas cambiar el contenedor. Esto es 100x más rápido que re-codificar:
# Cambiar mkv a mp4 sin re-codificar
ffmpeg -i input.mkv -c copy output.mp4
Para detectar si puedes usar -c copy:
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 input.mp4
ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 input.mp4
Si el vídeo es h264 y el audio es aac, puedes usar -c copy.
Combinados: yt-dlp + ffmpeg
Descargar y reconvertir
# Descargar y convertir a mp4 con ffmpeg
yt-dlp -f "bv+ba/b" "URL" --exec "ffmpeg -i {} -c:v libx264 -c:a aac {}.mp4"
# Descargar y redimensionar a 720p
yt-dlp -f "bv+ba/b" "URL" --exec "ffmpeg -i {} -vf scale=1280:720 -c:v libx264 -c:a aac {}_720p.mp4"
# Descargar y comprimir a tamaño específico
yt-dlp -f "bv+ba/b" "URL" --exec "ffmpeg -i {} -c:v libx264 -b:v 1M -c:a aac {}_compressed.mp4"
Descargar solo audio y convertir
# Descargar audio directamente con yt-dlp
yt-dlp -x --audio-format mp3 "URL"
# Descargar audio y convertir con ffmpeg (más control)
yt-dlp -x --audio-format wav "URL" --exec "ffmpeg -i {} -c:a libmp3lame -b:a 320k {}.mp3"
# Descargar playlist como mp3
yt-dlp -x --audio-format mp3 --yes-playlist "URL_PLAYLIST"
# Descargar y normalizar audio
yt-dlp -x --audio-format mp3 "URL" --exec "ffmpeg -i {} -af loudnorm {}_normalized.mp3"
Pipeline completo: descargar → comprimir → organizar
#!/bin/bash
# Script para descargar y comprimir vídeos
URL="$1"
CARPETA="$HOME/Videos/$(date +%Y-%m-%d)"
mkdir -p "$CARPETA"
# Descargar
yt-dlp -f "bv+ba/b" --merge-output-format mp4 -o "$CARPETA/%(title)s.%(ext)s" "URL"
# Comprimir todos los vídeos nuevos
for file in "$CARPETA"/*.mp4; do
ffmpeg -i "$file" -c:v libx264 -crf 28 -preset fast -c:a aac -b:a 128k "${file%.mp4}_compressed.mp4"
rm "$file"
done
Workflow de lotes: descargar playlist + comprimir
#!/bin/bash
# Descargar playlist y comprimir cada vídeo
PLAYLIST_URL="$1"
OUTPUT_DIR="$HOME/Videos/comprimidos"
mkdir -p "$OUTPUT_DIR"
# Descargar playlist
yt-dlp -f "bv+ba/b" --merge-output-format mp4 \
-o "$OUTPUT_DIR/%(playlist_index)02d-%(title)s.%(ext)s" \
--yes-playlist "$PLAYLIST_URL"
# Comprimir cada vídeo
for file in "$OUTPUT_DIR"/*.mp4; do
echo "Comprimiendo: $file"
ffmpeg -i "$file" \
-c:v libx264 -crf 28 -preset fast \
-c:a aac -b:a 128k \
-movflags +faststart \
"${file%.mp4}_small.mp4"
rm "$file"
done
echo "¡Listo! Vídeos comprimidos en $OUTPUT_DIR"
Descargar segmento específico y comprimir
# Descargar solo un trozo de un vídeo
yt-dlp --external-downloader ffmpeg \
--external-downloader-args "ffmpeg_i:-ss 00:10:00 -to 00:20:00" \
-f "bv+ba/b" "URL"
# Descargar y comprimir ese segmento
yt-dlp --external-downloader ffmpeg \
--external-downloader-args "ffmpeg_i:-ss 00:10:00 -to 00:20:00" \
-f "bv+ba/b" "URL" \
--exec "ffmpeg -i {} -c:v libx264 -crf 28 -c:a aac {}.mp4"
Descargar canal completo y organizar por carpeta
# Descargar canal organizado por carpetas
yt-dlp -o "%(channel)s/%(playlist_title)s/%(title)s.%(ext)s" "URL_CANAL"
# Descargar canal con límite de calidad
yt-dlp -f "bestvideo[height<=1080]+bestaudio/best" \
-o "%(channel)s/%(title)s.%(ext)s" "URL_CANAL"
# Descargar canal y comprimir
yt-dlp -f "bv+ba/b" --yes-playlist \
-o "%(channel)s/%(title)s.%(ext)s" "URL_CANAL" \
--exec "ffmpeg -i {} -c:v libx264 -crf 28 -c:a aac {}_compressed.mp4"
Tabla resumen de opciones rápidas
yt-dlp
| Opción | Descripción |
|---|---|
-f "best" | Mejor calidad disponible |
-F | Listar formatos |
-x --audio-format mp3 | Extraer solo audio |
-o "patron" | Plantilla de salida |
--embed-thumbnail | Incrustar thumbnail |
--embed-metadata | Incrustar metadata |
--write-subs | Descargar subtítulos |
-N 4 | Fragmentos paralelos |
--limit-rate 5M | Limitar velocidad |
--cookies-from-browser | Usar cookies del navegador |
--yes-playlist | Descargar playlist |
--download-archive | Guardar historial |
--dateafter | Filtrar por fecha |
-U | Actualizar yt-dlp |
ffmpeg
| Opción | Descripción |
|---|---|
-i input | Archivo de entrada |
-c copy | Sin re-codificar |
-c:v libx264 | Códec de vídeo H.264 |
-c:a aac | Códec de audio AAC |
-b:v 5M | Bitrate de vídeo |
-b:a 128k | Bitrate de audio |
-vf scale=W:H | Redimensionar |
-ss / -to / -t | Corte por tiempo |
-vn | Sin vídeo (extraer audio) |
-y | Sobreescribir |
-movflags +faststart | Optimizar para web |
-crf 28 | Calidad (menor = mejor) |
-preset fast | Velocidad de codificación |
-filter_complex | Filtros complejos |
-map | Seleccionar pistas |
Casos de uso específicos
WhatsApp (límite 16MB)
# Comprimir para WhatsApp (máximo 16MB)
ffmpeg -i input.mp4 -c:v libx264 -b:v 800k -c:a aac -b:a 64k \
-fs 16M -movflags +faststart whatsapp.mp4
# Script para detectar duración y ajustar bitrate
#!/bin/bash
INPUT="$1"
DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$INPUT" | cut -d. -f1)
# 16MB = 131072 kbits, restamos audio (64k * duración)
VIDEO_BITRATE=$(( (131072 - (64 * DURATION)) / DURATION ))
ffmpeg -i "$INPUT" -c:v libx264 -b:v "${VIDEO_BITRATE}k" -c:a aac -b:a 64k \
-movflags +faststart "whatsapp_${INPUT}"
Telegram (límite 2GB)
# Comprimir para Telegram (máximo 2GB)
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset fast \
-c:a aac -b:a 128k -movflags +faststart telegram.mp4
# Para máxima compresión (calidad aceptable)
ffmpeg -i input.mp4 -c:v libx264 -crf 32 -preset fast \
-c:a aac -b:a 96k -movflags +faststart telegram_small.mp4
# Instagram Feed (máximo 60 segundos, 1080x1080)
ffmpeg -i input.mp4 -ss 0 -t 60 -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -b:v 3500k -c:a aac -b:a 128k instagram_feed.mp4
# Instagram Reels (9:16, máximo 90 segundos)
ffmpeg -i input.mp4 -ss 0 -t 90 -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -b:v 3500k -c:a aac -b:a 128k instagram_reel.mp4
# Instagram Stories (9:16, máximo 15 segundos)
ffmpeg -i input.mp4 -ss 0 -t 15 -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -b:v 3500k -c:a aac -b:a 128k instagram_story.mp4
TikTok
# TikTok (9:16, máximo 3 minutos)
ffmpeg -i input.mp4 -ss 0 -t 180 -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -b:v 3000k -c:a aac -b:a 128k tiktok.mp4
# TikTok con texto superpuesto
ffmpeg -i input.mp4 -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2,drawtext=text='Mi TikTok':fontsize=36:fontcolor=white:x=(w-tw)/2:y=50" \
-c:v libx264 -b:v 3000k -c:a aac -b:a 128k tiktok_texto.mp4
YouTube
# YouTube (recomendado 1080p o 4K)
ffmpeg -i input.mp4 -c:v libx264 -crf 18 -preset slow \
-c:a aac -b:a 192k -movflags +faststart youtube_1080p.mp4
# YouTube 4K
ffmpeg -i input.mp4 -c:v libx264 -crf 18 -preset slow \
-vf scale=3840:2160 -c:a aac -b:a 256k -movflags +faststart youtube_4k.mp4
# YouTube Shorts (9:16, máximo 60 segundos)
ffmpeg -i input.mp4 -ss 0 -t 60 -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -crf 18 -c:a aac -b:a 128k youtube_short.mp4
Twitter/X
# Twitter (máximo 140 segundos, 1280x720 máximo)
ffmpeg -i input.mp4 -ss 0 -t 140 -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -b:v 5000k -c:a aac -b:a 128k twitter.mp4
Backup de canales de YouTube
#!/bin/bash
# Script para backup de canal de YouTube
CANAL_URL="$1"
BACKUP_DIR="$HOME/Backups/YouTube/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# Descargar canal completo con metadata
yt-dlp \
-f "bestvideo[height<=1080]+bestaudio/best" \
--merge-output-format mp4 \
--embed-thumbnail \
--embed-metadata \
--write-description \
--write-info-json \
-o "$BACKUP_DIR/%(upload_date>%Y-%m-%d)s-%(title)s.%(ext)s" \
--yes-playlist \
"$CANAL_URL/videos"
echo "Backup completado en: $BACKUP_DIR"
Organizar biblioteca de vídeos
#!/bin/bash
# Script para organizar vídeos descargados
CARPETA="$1"
OUTPUT="$HOME/Videos/Organizados"
mkdir -p "$OUTPUT"
for file in "$CARPETA"/*.mp4; do
# Extraer metadata
TITLE=$(ffprobe -v error -show_entries format_tags=title -of csv=p=0 "$file" 2>/dev/null)
DATE=$(ffprobe -v error -show_entries format_tags=date -of csv=p=0 "$file" 2>/dev/null)
if [ -z "$TITLE" ]; then
TITLE=$(basename "$file" .mp4)
fi
if [ -z "$DATE" ]; then
DATE="SinFecha"
fi
# Crear carpeta por año-mes
CARPETA_DEST="$OUTPUT/$DATE"
mkdir -p "$CARPETA_DEST"
# Mover archivo
mv "$file" "$CARPETA_DEST/$TITLE.mp4"
echo "Movido: $TITLE → $CARPETA_DEST"
done
Scripts completos de automatización
Script de monitoreo de descargas
#!/bin/bash
# Monitorear carpeta y descargar automáticamente
CARPETA="$HOME/Downloads/pendientes"
LOG="$HOME/Downloads/log_descargas.txt"
echo "Monitoreando: $CARPETA" | tee -a "$LOG"
while true; do
for file in "$CARPETA"/*.url; do
if [ -f "$file" ]; then
URL=$(cat "$file")
echo "$(date): Descargando: $URL" | tee -a "$LOG"
yt-dlp \
-f "bv+ba/b" \
--merge-output-format mp4 \
-o "$HOME/Videos/%(channel)s/%(title)s.%(ext)s" \
--embed-thumbnail \
--embed-metadata \
"$URL"
if [ $? -eq 0 ]; then
echo "$(date): Completado: $URL" | tee -a "$LOG"
rm "$file"
else
echo "$(date): ERROR: $URL" | tee -a "$LOG"
fi
fi
done
sleep 60
done
Script de compresión automática
#!/bin/bash
# Comprimir vídeos nuevos automáticamente
CARPETA="$HOME/Videos/para_comprimir"
OUTPUT="$HOME/Videos/comprimidos"
mkdir -p "$OUTPUT"
inotifywait -m -r -e create "$CARPETA" |
while read path action file; do
if [[ "$file" == *.mp4 ]]; then
echo "Comprimiendo: $file"
# Esperar a que el archivo esté listo
sleep 5
ffmpeg -i "$path$file" \
-c:v libx264 -crf 28 -preset fast \
-c:a aac -b:a 128k \
-movflags +faststart \
"$OUTPUT/$file"
if [ $? -eq 0 ]; then
echo "Completado: $file"
rm "$path$file"
else
echo "ERROR: $file"
fi
fi
done
Script de backup automático
#!/bin/bash
# Backup automático de canales de YouTube
CANALES=(
"https://www.youtube.com/@canal1"
"https://www.youtube.com/@canal2"
"https://www.youtube.com/@canal3"
)
BACKUP_DIR="$HOME/Backups/YouTube"
LOG="$BACKUP_DIR/log.txt"
mkdir -p "$BACKUP_DIR"
for canal in "${CANALES[@]}"; do
NOMBRE=$(echo "$canal" | sed 's/.*@//')
CARPETA="$BACKUP_DIR/$NOMBRE"
mkdir -p "$CARPETA"
echo "$(date): Descargando: $NOMBRE" | tee -a "$LOG"
yt-dlp \
-f "bestvideo[height<=1080]+bestaudio/best" \
--merge-output-format mp4 \
--download-archive "$CARPETA/.archive" \
-o "$CARPETA/%(upload_date>%Y-%m-%d)s-%(title)s.%(ext)s" \
--yes-playlist \
"$canal/videos"
echo "$(date): Completado: $NOMBRE" | tee -a "$LOG"
done
echo "$(date): Backup general completado" | tee -a "$LOG"
Cron para ejecución automática
# Editar crontab
crontab -e
# Añadir estas líneas:
# Backup diario a las 3 AM
0 3 * * * /home/usuario/scripts/backup_youtube.sh
# Comprimir vídeos nuevos cada hora
0 * * * * /home/usuario/scripts/comprimir_nuevos.sh
# Limpiar caché de yt-dlp semanalmente (domingos a las 4 AM)
0 4 * * 0 rm -rf ~/.cache/yt-dlp/*
Systemd service para monitoreo
# /etc/systemd/system/video-monitor.service
[Unit]
Description=Monitor de vídeos para comprimir
After=network.target
[Service]
Type=simple
User=usuario
ExecStart=/home/usuario/scripts/comprimir_nuevos.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Activar:
sudo systemctl enable video-monitor
sudo systemctl start video-monitor
Aceleración por hardware (GPU)
ffmpeg puede usar la GPU para acelerar la codificación de vídeo. Los encoders principales:
| GPU | Encoder | Comando |
|---|---|---|
| NVIDIA | NVENC | -c:v h264_nvenc -preset fast |
| Intel/AMD | VAAPI | -vaapi_device /dev/dri/renderD128 -c:v h264_vaapi |
| CPU | libx264 | -c:v libx264 -preset fast |
# NVIDIA: detectar si hay GPU disponible
nvidia-smi
# Codificar con NVENC (2-5x más rápido)
ffmpeg -i input.mp4 -c:v h264_nvenc -preset fast -c:a copy output.mp4
# Codificar con NVENC HEVC (mejor compresión)
ffmpeg -i input.mp4 -c:v hevc_nvenc -preset fast -c:a copy output.mp4
# Intel/AMD: codificar con VAAPI
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
-vf 'format=nv12,hwupload' -c:v h264_vaapi -c:a copy output.mp4
# Detectar GPU automáticamente (script)
if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null 2>&1; then
ENCODER="h264_nvenc"
elif [[ -e /dev/dri/renderD128 ]] && command -v vainfo &>/dev/null; then
ENCODER="h264_vaapi"
else
ENCODER="libx264"
fi
ffmpeg -i input.mp4 -c:v "$ENCODER" -preset fast -c:a copy output.mp4
Speedup típico:
| Operación | CPU (libx264) | GPU (NVENC) | Speedup |
|---|---|---|---|
| Convertir 1080p | 30 fps | 120 fps | 4x |
| Estabilizar | 15 fps | 60 fps | 4x |
| Denoise | 10 fps | 40 fps | 4x |
Pipeline encadenado con ffmpeg
Ejecutar múltiples operaciones en un solo comando, evitando archivos intermedios:
# Cortar + convertir + fade en un solo pass
ffmpeg -i input.mp4 \
-ss 00:01:00 -t 00:04:00 \
-vf "scale=1280:720,fade=t=in:st=0:d=2,fade=t=out:st=178:d=2" \
-c:v libx264 -preset fast -c:a aac -b:a 128k \
-movflags +faststart output.mp4
# Rotar + denoise + enfocar
ffmpeg -i input.mp4 \
-vf "transpose=1,nlmeans=s=3:p=3:r=9,unsharp=5:5:3:5:5:0" \
-c:v libx264 -preset fast -c:a copy output.mp4
# Pipeline completo: descargar + cortar + convertir (yt-dlp + ffmpeg)
yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]" -o "temp.mp4" "URL" && \
ffmpeg -i temp.mp4 -ss 00:01:00 -t 00:05:00 \
-vf "scale=1280:720" -c:v libx264 -preset fast -c:a aac \
-movflags +faststart output.mp4 && \
rm temp.mp4
Solución de problemas
Errores comunes de yt-dlp
| Error | Causa | Solución |
|---|---|---|
ERROR: Unable to extract | YouTube cambió su estructura | yt-dlp -U (actualizar) |
HTTP Error 403 | Bloqueo de rate limiting | --sleep-interval 5 |
ERROR: Sign in to confirm | Necesita cookies | --cookies-from-browser chrome |
ERROR: PoToken | Token de prueba falla | --cookies-from-browser chrome |
ERROR: File already exists | Archivo existente | --no-overwrites |
ERROR: Network unreachable | Problema de red | --proxy o -4 |
ERROR: Fragment download failed | Fragmento corrupto | --fragment-retries 10 |
ERROR: Requested format not available | Formato no existe | yt-dlp -F "URL" (listar formatos) |
Errores comunes de ffmpeg
| Error | Causa | Solución |
|---|---|---|
Invalid argument | Parámetro mal formado | Revisar comillas y escapes |
No such file | Archivo no existe | Verificar ruta |
Error opening input | Formato no soportado | Actualizar ffmpeg |
Encoder not found | Códec no disponible | Compilar con libx264/libx265 |
Cannot write | Permisos o disco lleno | Verificar permisos/espacio |
Solución a PoToken (YouTube 2026)
YouTube requiere un Proof-of-Origin Token en 2026. Soluciones:
# Opción 1: Usar cookies de navegador (recomendado)
yt-dlp --cookies-from-browser chrome "URL"
# Opción 2: Forzar extractor específico
yt-dlp --extractor-args "youtube:player_client=web" "URL"
# Opción 3: Usar bgutil-ytdlp-pot-provider
# Instalar: pip install bgutil-ytdlp-pot-provider
# Luego usar normalmente
yt-dlp "URL"