Pipeline ffmpeg
Los scripts auxiliares se encuentran en scripts/ junto a este archivo. Ejecútalos con python <directorio_del_skill>/scripts/<nombre>.py.
probe.py ENTRADA...— resumen de una línea por archivo (tamaño, fps, rotación, HDR, duración, audio).export.py ENTRADA SALIDA --preset instagram|tiktok|shorts|youtube|master|preview— codificación final con la configuración correcta.frames.py ENTRADA DIR_SALIDA --times 0.5,3,7.2o--every 1— captura fotogramas de control.
1. Sondear y normalizar primero
Trampas del metraje de teléfonos:
- Rotación: iPhone almacena píxeles en horizontal + un indicador de rotación. ffmpeg auto-rotación al decodificar (
-noautorotatelo desactiva). OpenCVVideoCapturepuede no hacerlo — compruebaframe.shapey rota tú mismo. - VFR: los teléfonos graban con velocidad de fotogramas variable. Convierte a CFR al extraer:
-vf fps=30(o-r 30a la salida) para que el cálculo de fotogramas sea exacto. - HDR:
color_transfer=arib-std-b67(HLG) osmpte2084(PQ). Sin tratar, se ve lavado/gris en SDR. Mapea el tono:
# HDR (HLG o PQ) -> SDR Rec.709, ruta zscale por CPU
-vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p"
hable = cinematográfico; mobius conserva más saturación. Si falta zscale, usa libplacebo (-vf libplacebo=tonemapping=bt.2390:colorspace=bt709:color_primaries=bt709:color_trc=bt709).
- Apple Log / log de cámara: aplica primero el LUT Log → Rec.709 del fabricante (
lut3d=AppleLog_to_Rec709.cube), luego etalonaje. - Fuentes de 10 bits: mantén 10 bits en los intermediarios (
-pix_fmt yuv422p10leProRes oyuv420p10lex265) cuando etalones mucho; convierte a 8 bitsyuv420psolo en la exportación final para redes sociales.
2. Arquitectura de un script de compilación
sources/ (nunca modificar)
editar/
build.py # regenera todo a partir de fuentes + archivos de datos
edl.json # [{src, in, out, speed, grade, transition}]
segments/ # un archivo normalizado y etalonado por entrada EDL (sin pérdidas)
overlays/ # PNG / secuencia PNG / ProRes 4444 / WebM alfa
preview.mp4, final.mp4
Reglas:
- Extracción por segmento con todo el trabajo por clip (recorte, velocidad, escalado/recorte, etalonaje, fps) en una pasada → intermedio a
-crf 10(x264) o ProRes 422 HQ. - Concatenar segmentos con el demuxer concat y
-c copycuando no se necesiten transiciones (todos los segmentos deben compartir códec, resolución, fps, pix_fmt, timebase). - Superposiciones y texto en una pasada final sobre el video concatenado, luego codifica una vez a la especificación de entrega.
- El audio se construye por separado (música + SFX + diálogo → un WAV), luego se multiplexa en la pasada final.
Recorte preciso
# -ss antes de -i es rápido y preciso en fotogramas al recodificar (ffmpeg >= 2.1)
ffmpeg -ss 12.40 -i ENTRADA.mov -t 2.333 -vf "fps=30,scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920" \
-c:v libx264 -crf 10 -preset medium -pix_fmt yuv420p -an seg_003.mp4
Nunca uses -c copy para recortes que deban ser precisos en fotogramas (se ajusta a los fotogramas clave).
Rellenar 9:16 desde 16:9
- Recorte (cubrir):
scale=-2:1920,crop=1080:1920:(iw-1080)*X:0con X 0..1 para elegir la posición del sujeto (rastréalo si el sujeto se mueve). - Fondo borroso (contener):
split[a][b];[a]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=40:2[bg];[b]scale=1080:-2[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2
Concatenar
# list.txt: file 'segments/seg_001.mp4' (uno por línea)
ffmpeg -f concat -safe 0 -i list.txt -c copy unido.mp4
Transiciones con xfade
ffmpeg -i a.mp4 -i b.mp4 -filter_complex \
"[0:v][1:v]xfade=transition=fade:duration=0.5:offset=4.5,format=yuv420p[v]; \
[0:a][1:a]acrossfade=d=0.5[a]" -map "[v]" -map "[a]" salida.mp4
offset = duración del primer clip − duración de la transición. Tipos: fade, fadeblack, fadewhite, wipeleft, slideup, smoothleft, circleopen, radial, zoomin, hblur, pixelize, dissolve… Encadena los offsets acumulativamente para muchos clips (cada xfade acorta el total en su duración).
Superposiciones
# PNG estático (pantalla completa, transparente) para todo el video — no necesita -loop
[0:v][1:v]overlay=0:0:format=auto
# con límite de tiempo
overlay=0:0:enable='between(t,2.0,5.5)'
# clip de superposición animado que comienza en T en la salida
[1:v]setpts=PTS-STARTPTS+T/TB[ov];[0:v][ov]overlay=0:0:eof_action=pass
Superposiciones con canal alfa: secuencia PNG (-framerate 30 -i ov/%04d.png), ProRes 4444 (-c:v prores_ks -profile:v 4444 -pix_fmt yuva444p10le) o VP9 WebM (-c:v libvpx-vp9 -pix_fmt yuva420p).
No uses -loop 1 con un PNG RGBA 4K para videos largos; consume mucha RAM. Una entrada de una sola imagen se repite automáticamente mediante overlay.
Tubería de fotogramas sin procesar desde Python (composición PIL/OpenCV) a ffmpeg:
p = subprocess.Popen(["ffmpeg","-y","-f","rawvideo","-pix_fmt","rgb24","-s",f"{W}x{H}","-r","30","-i","-",
"-c:v","libx264","-crf","12","-pix_fmt","yuv420p","salida.mp4"], stdin=subprocess.PIPE)
p.stdin.write(frame.tobytes()) # numpy uint8 HxWx3, por fotograma
Lee fotogramas fuente de la misma manera (-f rawvideo -pix_fmt rgb24 -), lo que evita problemas de rotación y códec de OpenCV.
Velocidad
- Constante:
setpts=PTS/2(2x) +atempo=2.0(rango atempo 0.5-100; encadena para más). - Cámara lenta suave desde 30 fps:
minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:vsbmc=1, luegosetpts=2*PTS. Verifica si hay distorsión en bordes/manos. - Rampas de velocidad variable: renderiza fotograma a fotograma a partir de un mapa de tiempo (consulta
beat-sync), nunca apiles muchas expresionessetpts.
Subtítulos (último)
-vf "subtitles=subtitulos.ass:fontsdir=fuentes" # ASS da control total de estilo
3. Preajustes de exportación
| Destino | Video | Audio | Notas |
|---|---|---|---|
| Instagram Reels / TikTok / Shorts | 1080x1920 (o 2160x3840) H.264 High, 30 fps, CRF 16-18, -maxrate 25M -bufsize 50M, yuv420p | AAC 48 kHz 256-320k | -movflags +faststart; las plataformas recodifican, así que alimenta un archivo de alta calidad |
| YouTube 16:9 | 3840x2160 o 1920x1080, H.264 CRF 16 o HEVC CRF 18, fps de la fuente | AAC 48 kHz 320k | sube 4K incluso para contenido a 1080p para obtener la mejor codificación VP9/AV1 |
| Maestro | ProRes 422 HQ o x264 CRF 10 / x265 CRF 14 -tag:v hvc1 | PCM 24 bits 48 kHz | archivo |
| Vista previa | 720p, CRF 28, -preset veryfast | AAC 128k | iteración rápida |
Añade siempre -pix_fmt yuv420p para redes sociales (10 bits o 4:2:2 pueden fallar o verse mal después de la subida) y -color_primaries bt709 -color_trc bt709 -colorspace bt709 para etiquetar SDR correctamente.
4. Verificar
ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,pix_fmt -show_entries format=duration -of compact salida.mp4
ffmpeg -i salida.mp4 -af ebur128=peak=true -f null - 2>&1 | tail -12
ffmpeg -i salida.mp4 -vf "select='gt(scene,0.3)',showinfo" -f null - 2>&1 | grep pts_time # dónde están realmente los cortes