El cluster C8 de fqmpeg es la caja de herramientas de composición: 13 verbos que ponen algo encima de (o al lado de) tu video. Cinco dibujan gráficos o texto estático (watermark, text, drawbox, timecode, border). Cuatro combinan varios videos en un solo lienzo (pip, pip-grid, blend, stack). Uno crea un video a partir de una imagen fija más audio (picture). Tres queman información analítica en el cuadro para revisión (video-info-overlay, histogram-overlay, progress).
Esta guía recorre cada verbo contra su fuente en src/commands/ de fqmpeg 3.0.3: el filtro FFmpeg subyacente, los defaults, el nombre del archivo de salida, y las trampas que --help por sí solo no muestra (text auto-escapa : y ' para que los pases literales; pip-grid auto-detecta 2x1/2x2/3x3 según la cantidad de inputs; progress necesita un argumento de duración porque FFmpeg no permite referenciar la duración de la fuente desde una expresión de filtro; la posición y el tamaño de histogram-overlay están hardcodeados).
Qué te llevas de esta guía
- Una tabla de decisión para los 13 verbos por tarea (gráficos / multi-video / imagen-a-video / analítico)
- La invocación FFmpeg exacta que genera cada verbo (verificada con
--dry-run) - Defaults, rangos, palabras-clave de posición y nombres de salida para cada comando
- Tres recetas de extremo a extremo — brandear un clip de YouTube, armar un reel de comparación, documentar una grabación de pantalla
Los 13 Verbos de un Vistazo
El cluster se divide en cuatro grupos de tarea. Elige el grupo, luego el verbo.
| Grupo | Verbos | Qué hacen |
|---|---|---|
| Gráficos y texto estático | watermark, text, drawbox, timecode, border | Quemar logo, texto, rectángulo, timestamp en vivo o marco sólido alrededor del video |
| Composición multi-video | pip, pip-grid, blend, stack | Picture-in-picture, grids de 2-9 inputs, blends por opacidad o stacks lado-a-lado / arriba-abajo |
| Imagen a video | picture | Promueve una imagen fija + audio a un MP4 reproducible (podcast, song-art video) |
| Overlays analíticos | video-info-overlay, histogram-overlay, progress | Quemar número de frame / PTS / picture-type, un histograma o waveform, o una barra de progreso ligada al tiempo |
Tres cosas que vale saber antes de seguir:
textauto-escapa:y'. El filtrodrawtexttrata:como separador de opciones y'como delimitador de string, así que unTime: 12:34literal normalmente requeriría escape manual. fqmpeg pre-escapa estos en el wrapper JS ('→'\\\\'',:→\:), así que pasas el string tal cual. La contraparte: si intentaras inyectar opciones dedrawtexta través del string, fqmpeg lo bloquea — por diseño.pip-gridauto-detecta el layout según la cantidad de inputs. Pasa 2 inputs y obtienes2x1; con 3-4 sale2x2; con 5-9 sale3x3. Las ranuras vacías se rellenan repitiendo el último input — pasa 5 videos a un3x3y las ranuras 6-9 son clones del input 5. Forza con--layout 2x2(etc.) cuando quieras una forma específica.progressnecesita un argumento posicional<duration>. Eldrawboxde FFmpeg puede referenciart(tiempo actual) dentro de una expresión, pero no la duración total de la fuente. fqmpeg lo resuelve tomando la duración como argumento explícito y horneándola en la fórmulaiw*t/<duration>. Si no sabes la duración, correnpx fqmpeg duration input.mp4antes.
Gráficos y Texto Estático
watermark — Overlay de imagen (logo PNG, etc.)
Superpone una imagen sobre un video en una de nueve posiciones predefinidas. El audio queda intacto.
- Fuente:
src/commands/watermark.js - Filtro:
overlay=<x>:<y>donde<x>:<y>se calcula desde--posy--margin - Audio:
-c:a copy(sin tocar)
| Argumento / Opción | Default | Opciones / Notas |
|---|---|---|
<input> | requerido | Video de entrada |
<image> | requerido | Imagen de marca de agua (PNG con alfa recomendado) |
--pos <position> | bottom-right | top-left, top, top-right, left, center, right, bottom-left, bottom, bottom-right |
--margin <n> | 10 | Píxeles desde el borde elegido |
-o, --output <path> | <input-stem>-watermarked.<ext> | — |
$ npx fqmpeg watermark input.mp4 logo.png --pos bottom-right --margin 10 --dry-run
ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=W-w-10:H-h-10 -c:a copy input-watermarked.mp4
Se usa el tamaño nativo de la imagen — no hay --scale aquí. Si tu logo es muy grande, redimensiónalo antes con un editor o pre-procesa con ffmpeg -i logo.png -vf scale=120:-1 logo-small.png. Las posiciones centradas (top, bottom, center, left, right) calculan el offset desde el centro del cuadro, así que --margin solo afecta al eje perpendicular (ej. bottom usa el margen desde el borde inferior pero queda horizontalmente centrado independientemente del margen).
text — Dibujar texto / título sobre video
Quema un string de texto en una de siete posiciones predefinidas, con caja de fondo opcional y ventana de tiempo activable.
- Fuente:
src/commands/text.js - Filtro:
drawtext=text='<escapado>':fontsize=N:fontcolor=C:<pos>[:box=1:boxcolor=...][:enable='...'] - Auto-escape:
'y:del string de entrada se escapan antes de inlinear
| Argumento / Opción | Default | Notas |
|---|---|---|
<input> | requerido | Video de entrada |
<string> | requerido | Texto a dibujar (posicional, encierra los caracteres especiales del shell tú mismo) |
--pos <position> | center | top-left, top, top-right, center, bottom-left, bottom, bottom-right |
--font-size <n> | 48 | Entero positivo |
--color <name> | white | Cualquier nombre de color FFmpeg o 0xRRGGBB |
--bg <color> | (ninguno) | Color de la caja de fondo, ej. black@0.5 para opacidad 50% |
--start <sec> | (ninguno) | Mostrar texto desde este tiempo |
--end <sec> | (ninguno) | Ocultar texto después de este tiempo |
-o, --output <path> | <input-stem>-text.<ext> | — |
$ npx fqmpeg text input.mp4 "Hola, Mundo" --pos top --font-size 64 --color yellow --bg black@0.5 --dry-run
ffmpeg -i input.mp4 -vf "drawtext=text='Hola, Mundo':fontsize=64:fontcolor=yellow:x=(w-text_w)/2:y=20:box=1:boxcolor=black@0.5:boxborderw=8" -c:a copy input-text.mp4
Pasa strings con dos puntos y apóstrofes literales — --bg "black@0.5" y text "Time: 12:34" funcionan ambos sin escape manual. El boxborderw=8 (8 píxeles de padding dentro de la caja) está hardcodeado; si necesitas una caja más estrecha o más ancha, copia el --dry-run y edítalo. Para texto de varias líneas, drawtext de FFmpeg no envuelve — usa \n literal dentro del string y el filtro lo respetará en la mayoría de builds, pero el espaciado entre líneas puede requerir un line_spacing=N ajustado a mano (otra vez, copia el --dry-run).
--start y --end activan la visualización por ventana de tiempo vía la expresión enable de drawtext. Solo uno usa gte(t,N) o lte(t,N); ambos juntos producen between(t,A,B).
drawbox — Dibujar un rectángulo / borde sobre video
Dibuja un rectángulo (de contorno o relleno) en una posición fija. Útil para resaltar un área en un tutorial, censurar una región fija o marcar una grabación de pantalla.
- Fuente:
src/commands/drawbox.js - Filtro:
drawbox=x=X:y=Y:w=W:h=H:color=C:t=T[:enable='...'] - Formato de región:
x:y:w:hregex estricto — cualquier otra cosa sale conError: region must be x:y:w:h
| Argumento / Opción | Default | Notas |
|---|---|---|
<input> | requerido | Video de entrada |
<region> | requerido | x:y:w:h en píxeles (origen arriba-izquierda) |
--color <name> | red | Nombre de color FFmpeg |
--thickness <n> | 3 | Entero positivo, o el string literal fill para una caja sólida rellena |
--start <sec> | (ninguno) | Mostrar desde este tiempo |
--end <sec> | (ninguno) | Ocultar después de este tiempo |
-o, --output <path> | <input-stem>-boxed.<ext> | — |
$ npx fqmpeg drawbox input.mp4 100:50:200:150 --color yellow --thickness fill --dry-run
ffmpeg -i input.mp4 -vf drawbox=x=100:y=50:w=200:h=150:color=yellow:t=fill -c:a copy input-boxed.mp4
--thickness fill es el patrón de censura: caja de color sólido cubriendo la región. Para un highlight de solo contorno, usa un entero positivo (3–8 se lee bien en 1080p). Combina con --start/--end para resaltar solo los segundos donde algo importa en un tutorial. La región usa coordenadas absolutas en píxeles — si quieres un enfoque por porcentaje del cuadro, primero necesitas el tamaño del cuadro (npx fqmpeg info input.mp4).
timecode — Quemar código de tiempo / timestamp en vivo
Superpone la posición de reproducción actual como texto HH:MM:SS.mmm en una caja semitransparente negra en una esquina.
- Fuente:
src/commands/timecode.js - Filtro:
drawtext=text='%{pts\\:hms}':fontsize=N:fontcolor=C:<pos>:box=1:boxcolor=black@0.5:boxborderw=4 - Formato:
%{pts:hms}— el formateador HMS built-in de FFmpeg (hardcodeado — no se exponen otros formatos)
| Argumento / Opción | Default | Notas |
|---|---|---|
<input> | requerido | Video de entrada |
--pos <position> | top-left | top-left, top-right, bottom-left, bottom-right (solo esquinas) |
--font-size <n> | 24 | Entero positivo |
--color <name> | white | Color FFmpeg |
-o, --output <path> | <input-stem>-timecode.<ext> | — |
$ npx fqmpeg timecode input.mp4 --pos top-right --font-size 32 --dry-run
ffmpeg -i input.mp4 -vf drawtext=text='%{pts\:hms}':fontsize=32:fontcolor=white:x=w-text_w-10:y=10:box=1:boxcolor=black@0.5:boxborderw=4 -c:a copy input-timecode.mp4
Útil para reels de revisión — los clientes citan tiempos del código quemado, así que feedback como "en 0:01:22.500 el corte queda duro" llega preciso. La caja negra está hardcodeada para legibilidad sobre fuentes brillantes. Si quieres un formato distinto (número de frame, código de tiempo de fuente, prefijo custom), usa text con las expresiones %{pts} / %{n} / %{localtime} de FFmpeg — mira video-info-overlay abajo para la variante multi-campo.
border — Marco decorativo alrededor del video
Aplica padding al cuadro por los cuatro lados con un color sólido, agrandando las dimensiones de salida en 2 * width por cada eje.
- Fuente:
src/commands/border.js - Filtro:
pad=iw+W*2:ih+W*2:W:W:color=C
| Argumento / Opción | Default | Notas |
|---|---|---|
<input> | requerido | Video de entrada |
--width <px> | 20 | Grosor del borde en píxeles (sumado a cada lado) |
--color <name> | white | Color FFmpeg |
-o, --output <path> | <input-stem>-bordered.<ext> | — |
$ npx fqmpeg border input.mp4 --width 30 --color black --dry-run
ffmpeg -i input.mp4 -vf pad=iw+30*2:ih+30*2:30:30:color=black -c:a copy input-bordered.mp4
Las dimensiones de salida crecen — una fuente de 1920x1080 con --width 30 queda en 1980x1140. Algunas plataformas (posts cuadrados de Instagram, YouTube Shorts con aspect fijo) recortan tamaños no estándar, así que verifica el spec de destino antes de meter borde. Para un marco cuadrado alrededor de un clip vertical, calcula --width como la mitad de la diferencia entre el ancho de la fuente y el lado cuadrado deseado, y combina con crop antes para mejor control.
Composición Multi-Video
pip — Picture-in-picture
Escala un video "small" y lo superpone sobre un video "main" en una de cuatro esquinas.
- Fuente:
src/commands/pip.js - Filtro:
[1]scale=iw*S:ih*S[pip];[0][pip]overlay=<xy> - Audio: Sin manejo explícito — el audio del video main pasa (comportamiento default de FFmpeg para el primer input)
| Argumento / Opción | Default | Rango | Notas |
|---|---|---|---|
<main> | requerido | — | Video de fondo |
<small> | requerido | — | Video del overlay |
--pos <position> | bottom-right | — | top-left, top-right, bottom-left, bottom-right |
--scale <n> | 0.25 | 0.1–1.0 | Tamaño del overlay relativo al main |
--margin <n> | 10 | entero positivo | Píxeles desde el borde |
-o, --output <path> | <main-stem>-pip.<ext> | — | — |
$ npx fqmpeg pip main.mp4 webcam.mp4 --pos top-right --scale 0.3 --dry-run
ffmpeg -i main.mp4 -i webcam.mp4 -filter_complex [1]scale=iw*0.3:ih*0.3[pip];[0][pip]overlay=main_w-overlay_w-10:10 main-pip.mp4
El uso clásico es una grabación de pantalla main con una face-cam small al 25% de escala en una esquina. La salida usa la duración del video main — si webcam.mp4 es más corto, se congela en su último frame; si es más largo, se corta. Para extender con el último frame del clip más corto, pre-procesa con tpad=stop_mode=clone o usa el verbo repeat-frame sobre el small input antes.
pip-grid — Layout en grid multi-input
Acomoda 2-9 videos en un grid (2x1, 1x2, 2x2, o 3x3). El layout se auto-detecta por la cantidad de inputs si --layout se omite.
- Fuente:
src/commands/pip-grid.js - Filtro: Por input
scale=iw/cols:ih/rows, luegohstackpor fila, despuésvstackde filas - Padding: Las ranuras vacías se rellenan repitiendo el último input
- Audio: Copiado del input 0 (
-c:a copy)
| Argumento / Opción | Default | Notas |
|---|---|---|
<inputs...> | requerido | 2-9 videos de entrada (posicional, variádico) |
--layout <grid> | auto | 2x1, 1x2, 2x2, 3x3. Auto: 2→2x1, 3-4→2x2, 5-9→3x3 |
-o, --output <path> | <input0-stem>-grid<layout>.<ext> | — |
$ npx fqmpeg pip-grid clip1.mp4 clip2.mp4 clip3.mp4 clip4.mp4 --layout 2x2 --dry-run
ffmpeg -i clip1.mp4 -i clip2.mp4 -i clip3.mp4 -i clip4.mp4 \
-filter_complex '[0:v]scale=iw/2:ih/2[v0];
[1:v]scale=iw/2:ih/2[v1];
[2:v]scale=iw/2:ih/2[v2];
[3:v]scale=iw/2:ih/2[v3];
[v0][v1]hstack=inputs=2[row0];
[v2][v3]hstack=inputs=2[row1];
[row0][row1]vstack=inputs=2[out]' \
-map '[out]' -c:a copy clip1-grid2x2.mp4
Pasa 5 inputs a 3x3 y las ranuras 6-9 son clones del input 5 — útil para "5 tomas de una sola línea, grid de 9 para revisión", pero no esperado cuando pasas inputs de más / de menos accidentalmente. Usa --layout explícito si quieres 3x3 con esquinas vacías intencionales (tendrías que sumar videos de pantalla negra para llenarlos; fqmpeg no sintetiza espacios en blanco). Todos los inputs se escalan por iw/cols:ih/rows, así que aspect ratios distintos terminan estirados de forma no uniforme — pre-normaliza con resize o crop si te importa.
blend — Blend por opacidad de dos videos
Combina dos videos dibujando el segundo sobre el primero con un alfa configurable.
- Fuente:
src/commands/blend.js - Filtro:
[1]format=yuva420p,colorchannelmixer=aa=O[over];[0][over]overlay - Audio: Re-encoded (sin
-c:a copyexplícito)
| Argumento / Opción | Default | Rango | Notas |
|---|---|---|---|
<input1> | requerido | — | Video base (debajo) |
<input2> | requerido | — | Video overlay (arriba) |
--opacity <n> | 0.5 | 0–1 | Alfa del input 2 |
-o, --output <path> | <input1-stem>-blend.<ext> | — | — |
$ npx fqmpeg blend input1.mp4 input2.mp4 --opacity 0.5 --dry-run
ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex [1]format=yuva420p,colorchannelmixer=aa=0.5[over];[0][over]overlay input1-blend.mp4
Los dos videos tienen que coincidir espacialmente — blend superpone en 0:0, así que resoluciones distintas producen un compuesto desalineado. Redimensiona el overlay para que coincida con la base antes si hace falta. La duración de salida sigue el default de overlay de FFmpeg — generalmente la del más corto. Para efectos de "estela fantasma", --opacity 0.3-0.4. Por encima de 0.7 y el video base apenas se ve.
stack — Lado a lado o arriba/abajo
Une dos videos en un solo cuadro, horizontalmente (lado a lado) o verticalmente (arriba/abajo). Los dos videos tienen que coincidir en dimensiones del eje de unión (mismo alto para horizontal, mismo ancho para vertical).
- Fuente:
src/commands/stack.js - Filtro:
[0:v][1:v]hstack=inputs=2ovstack=inputs=2 - Audio:
-c:a copy(track del input 0)
| Argumento / Opción | Default | Opciones | Notas |
|---|---|---|---|
<input1> | requerido | — | Video izquierdo/superior |
<input2> | requerido | — | Video derecho/inferior |
--direction <dir> | horizontal | horizontal, vertical | — |
-o, --output <path> | <input1-stem>-stacked.<ext> | — | — |
$ npx fqmpeg stack original.mp4 graded.mp4 --direction horizontal --dry-run
ffmpeg -i original.mp4 -i graded.mp4 -filter_complex [0:v][1:v]hstack=inputs=2 -c:a copy original-stacked.mp4
El reel clásico de antes/después — color graded vs sin gradar, estabilizado vs fuente. Si los dos clips no coinciden en alto (para hstack), FFmpeg falla con Input 1 height 720 does not match input 0 height 1080. Pre-redimensiona el más chico con npx fqmpeg resize input.mp4 --height 1080. El audio viene solo del input1 — el del segundo video se descarta.
Imagen a Video
picture — Imagen fija + audio → video
Loopea una imagen fija por la duración de un archivo de audio y codifica el resultado como MP4. Uso común: convertir un episodio de podcast o una canción en un video subible a YouTube.
- Fuente:
src/commands/picture.js - Códec:
libx264+aac@192k+yuv420p+-tune stillimage+-shortest - Stem de salida: Derivado del nombre del audio (no de la imagen), como
<audio-stem>-video.mp4
| Argumento / Opción | Default | Notas |
|---|---|---|
<image> | requerido | Cover JPG / PNG |
<audio> | requerido | MP3 / WAV / etc. |
-o, --output <path> | <audio-dir>/<audio-stem>-video.mp4 | Nota la fuente del stem |
$ npx fqmpeg picture cover.png episode.mp3 --dry-run
ffmpeg -loop 1 -i cover.png -i episode.mp3 -c:v libx264 -tune stillimage -c:a aac -b:a 192k -pix_fmt yuv420p -shortest episode-video.mp4
-tune stillimage le dice a x264 que optimice para contenido estático (calidad muy alta a bitrate muy bajo, ya que solo importa un frame). -shortest termina la salida cuando el input más corto termina — como la imagen looped es infinita, eso siempre es la duración del audio. El stem de salida espeja el nombre del audio, lo que matchea el flujo típico ("convertir este episodio en un video"). Si necesitas otro nombre, sobrescribe con -o.
Para un cover de podcast con marcadores de capítulos, corre picture primero y luego encadena embed-thumbnail y trabajos de metadata — fqmpeg los mantiene como verbos separados porque son decisiones independientes.
Overlays Analíticos
Estos verbos queman datos diagnósticos en el cuadro — útil para sesiones de QC, color correction, o para hacer un tutorial que muestre lo que está pasando frame a frame. No son para output de entrega final.
video-info-overlay — Número de frame, PTS, picture-type
Quema un overlay de texto por frame mostrando el índice (%{n}), el timestamp (%{pts:hms}) y el picture-type (%{pict_type} — I/P/B en H.264).
- Fuente:
src/commands/video-info-overlay.js - Filtro:
drawtext=text='frame %{n} | pts %{pts\\:hms} | type %{pict_type}':x=10:y=10:fontsize=16:fontcolor=white:box=1:boxcolor=black@0.5 - Posición, fuente, color: Todo hardcodeado (sin opciones)
| Argumento / Opción | Default | Notas |
|---|---|---|
<input> | requerido | Video de entrada |
-o, --output <path> | <input-stem>-info-overlay.<ext> | — |
$ npx fqmpeg video-info-overlay input.mp4 --dry-run
ffmpeg -i input.mp4 -vf "drawtext=text='frame %{n} | pts %{pts\:hms} | type %{pict_type}':x=10:y=10:fontsize=16:fontcolor=white:box=1:boxcolor=black@0.5" -c:a copy input-info-overlay.mp4
Útil para QC ("este glitch está en el frame 4523, type P") y para mostrar la estructura del GOP visualmente en tutoriales. El overlay queda arriba-izquierda fijo a 16px — chico pero legible en 1080p. Para variantes con otro tamaño o reposicionadas, copia el --dry-run y edita, o arma una invocación custom con text y las mismas expresiones %{...}.
histogram-overlay — Overlay RGB / waveform / parade
Agrega un display de histograma 320x240 (o waveform/parade) en la esquina inferior derecha — útil para color correction donde quieres ver la distribución de niveles junto al cuadro.
- Fuente:
src/commands/histogram-overlay.js - Filtro (levels):
split[main][hist];[hist]histogram,scale=320:240[h];[main][h]overlay=W-w-10:H-h-10 - Filtro (waveform):
split[main][wave];[wave]waveform=mode=column,scale=320:240[w];[main][w]overlay=W-w-10:H-h-10 - Filtro (parade):
split[main][par];[par]waveform=mode=column:display=parade,scale=320:240[p];[main][p]overlay=W-w-10:H-h-10 - Posición y tamaño: Hardcodeados — abajo-derecha con margen de 10px, panel 320x240
| Argumento / Opción | Default | Opciones | Notas |
|---|---|---|---|
<input> | requerido | — | Video de entrada |
--mode <mode> | levels | levels, waveform, parade | — |
-o, --output <path> | <input-stem>-histogram.<ext> | — | — |
$ npx fqmpeg histogram-overlay input.mp4 --mode parade --dry-run
ffmpeg -i input.mp4 -filter_complex split[main][par];[par]waveform=mode=column:display=parade,scale=320:240[p];[main][p]overlay=W-w-10:H-h-10 -c:a copy input-histogram.mp4
levels es el clásico histograma RGB de 256 bins. waveform muestra luma vs posición horizontal como un scope de columnas (estilo broadcast). parade es waveform separado por canal — tres columnas (R/G/B) lado a lado. Para revisión DIY de color grading donde quieres ver cómo cambia la distribución mientras hacés scrub, parade es lo más informativo.
progress — Barra de progreso ligada al tiempo
Quema una barra horizontal arriba o abajo del cuadro cuyo ancho crece linealmente de 0 al máximo conforme avanza el tiempo.
- Fuente:
src/commands/progress.js - Filtro:
drawbox=x=0:y=<y>:w='iw*t/<duration>':h=H:color=C:t=fill - Por qué la duración es posicional: Las expresiones
drawboxde FFmpeg pueden referenciart(tiempo actual) pero no la duración total de la fuente — fqmpeg compensa horneando la duración en la fórmula
| Argumento / Opción | Default | Notas |
|---|---|---|
<input> | requerido | Video de entrada |
<duration> | requerido | Duración total del video en segundos (mídela con npx fqmpeg duration input.mp4) |
--pos <position> | bottom | top, bottom |
--color <name> | red | Color FFmpeg |
--height <px> | 5 | Entero positivo |
-o, --output <path> | <input-stem>-progress.<ext> | — |
$ npx fqmpeg progress input.mp4 90 --pos bottom --color cyan --height 8 --dry-run
ffmpeg -i input.mp4 -vf drawbox=x=0:y=ih-8:w='iw*t/90':h=8:color=cyan:t=fill -c:a copy input-progress.mp4
Pasa la duración en segundos — 90 para 1m30s, 3600 para una hora. La barra llega al ancho completo exactamente en t = duration; si pasas una duración equivocada la barra se pasa o se queda corta. Wrapper típico de shell: dur=$(npx fqmpeg duration input.mp4) && npx fqmpeg progress input.mp4 $dur. Útil para tutoriales donde los espectadores quieren un indicador visual de cuánto queda.
Recetas del Mundo Real
Cada receta encadena varios verbos en un workflow que usarías de verdad.
Receta 1: Brandear un clip de YouTube
Tienes un video terminado y querés agregar marca de agua en una esquina, un título de apertura que se desvanezca después de 3 segundos y una barra de progreso fina abajo.
# Step 1: marca de agua en esquina (PNG con alfa)
npx fqmpeg watermark final.mp4 logo.png --pos bottom-right --margin 20
# → final-watermarked.mp4
# Step 2: título de apertura mostrado los primeros 3 segundos
npx fqmpeg text final-watermarked.mp4 "Episodio 12 — Color Grading" \
--pos top --font-size 56 --color white --bg "black@0.5" \
--start 0 --end 3
# → final-watermarked-text.mp4
# Step 3: barra de progreso fina (asumiendo episodio de 8 minutos = 480s)
npx fqmpeg progress final-watermarked-text.mp4 480 --pos bottom --color "white@0.6" --height 4
# → final-watermarked-text-progress.mp4
Tres pasadas significan tres rondas de re-encoding H.264, lo cual es tolerable pero suma pérdida generacional. Si quieres una sola pasada, copia los tres strings de filtro de las salidas --dry-run y ensámblalos con ;/, en una sola invocación de FFmpeg. Para reruns frecuentes (un podcast semanal), el flujo de tres pasos está bien — la conveniencia le gana al drop de calidad despreciable.
Receta 2: Reel de comparación lado a lado con timecode
Querés mostrar un "antes/después" de un clip color graded, con un overlay de timecode para que los reviewers puedan referir momentos exactos.
# Step 1: stack de los dos clips horizontalmente (deben coincidir en alto)
npx fqmpeg stack original.mp4 graded.mp4 --direction horizontal
# → original-stacked.mp4 (3840x1080 para dos fuentes 1920x1080)
# Step 2: quema timecode arriba a la izquierda como referencia
npx fqmpeg timecode original-stacked.mp4 --pos top-left --font-size 32
# → original-stacked-timecode.mp4
# Step 3 (opcional): etiquetar cada lado con un overlay de texto estático
npx fqmpeg text original-stacked-timecode.mp4 "ORIGINAL GRADED" \
--pos bottom --font-size 48 --color white --bg "black@0.5"
El truco de la etiqueta en el step 3 usa espacios para empujar "GRADED" al lado derecho — burdo pero efectivo. Para etiquetas pixel-perfect correrías dos invocaciones de text con posiciones custom (copia el --dry-run y reemplaza el x= derivado de --pos con coordenadas absolutas).
Receta 3: Documentar una grabación de pantalla con face-cam y progreso
Tienes una grabación de pantalla de 12 minutos (screen.mp4) y una webcam (webcam.mp4) y querés un deliverable de tutorial: face-cam en la esquina, barra de progreso ligada al tiempo y un título semi-transparente en los primeros 5 segundos.
# Step 1: face-cam picture-in-picture
npx fqmpeg pip screen.mp4 webcam.mp4 --pos bottom-right --scale 0.2 --margin 20
# → screen-pip.mp4
# Step 2: barra de progreso (12 minutos = 720 segundos)
npx fqmpeg progress screen-pip.mp4 720 --pos top --color "red@0.7" --height 6
# → screen-pip-progress.mp4
# Step 3: título de apertura
npx fqmpeg text screen-pip-progress.mp4 "Configurando Vercel + Next.js" \
--pos center --font-size 64 --color white --bg "black@0.7" \
--start 0 --end 5
# → screen-pip-progress-text.mp4
Si la webcam tiene un aspect ratio distinto al screen capture (webcam cuadrada, pantalla 16:9), el overlay pip escala relativo a las dimensiones del video main — un 0.2 de escala en una pantalla de 1920 de ancho produce un overlay de 384 de ancho independientemente de las dimensiones de la fuente del webcam. Para preservar el aspecto propio de la webcam, pre-procésala con npx fqmpeg crop webcam.mp4 ... antes del paso pip.
Preguntas Frecuentes
¿Cómo paso dos puntos o apóstrofes dentro de un string de text?
Inclúyelos tal cual — npx fqmpeg text input.mp4 "Time: it's 12:34" funciona. El wrapper de fqmpeg escapa : y ' antes de inlinearlos en el filtro drawtext de FFmpeg (' queda como '\\\\'' y : como \:). No necesitas escapar nada tú salvo los caracteres especiales del shell (las comillas que rodean el string en tu shell ya lo manejan).
pip-grid aceptó mis 5 inputs y la fila de abajo está duplicada — ¿es lo previsto?
Sí — cuando la cantidad de inputs no llena el grid, fqmpeg rellena las ranuras vacías con el último input. Pasa 5 a un 3x3 (auto-detectado) y las ranuras 6-9 son clones del input 5. Para usar 2x2 con 4 inputs (saltando el padding), pasa 4 inputs (auto-detecta 2x2) o especifica --layout 2x2 explícito. Para dejar ranuras vacías a propósito, suministra un input de pantalla negra — fqmpeg no sintetiza uno por ti.
progress requiere una duración — ¿por qué no la auto-detecta?
La expresión del filtro drawbox de FFmpeg puede referenciar t (tiempo actual del frame en curso) pero no tiene expresión built-in para "duración total de la fuente". Las únicas formas de meter la duración total en el filtro son (1) hornearla como número literal, que es lo que hace fqmpeg; (2) sondear la fuente antes, lo que significaría correr ffprobe desde dentro del wrapper JS — fqmpeg mantiene los verbos single-purpose y no sondea de forma transparente. Usa dur=$(npx fqmpeg duration input.mp4) y luego pasa $dur como argumento.
¿Qué diferencia hay entre watermark y picture?
watermark superpone una imagen estática sobre un video existente — el input es un video y la marca de agua es una imagen pequeña. picture hace lo opuesto — crea un video a partir de una imagen fija y una pista de audio (loopeando la imagen por toda la duración del audio). Comparten plumbing interno de libavfilter pero resuelven problemas distintos: branding (watermark) vs creación de video desde una sola imagen (picture).
¿Puedo cambiar la posición o el tamaño del panel de histogram-overlay?
No vía las flags de fqmpeg — ambas están hardcodeadas (overlay=W-w-10:H-h-10 para abajo-derecha con margen de 10px, size=320x240). Para un layout custom, copia el string de filtro del --dry-run y edita los parámetros overlay=... y size=... antes de correr FFmpeg directamente. Edits comunes: overlay=10:H-h-10 para abajo-izquierda, size=480x270 para un panel más grande.
stack falló con "Input 1 height ... does not match" — ¿y ahora?
hstack requiere que todos los inputs compartan el mismo alto; vstack requiere el mismo ancho. Pre-redimensiona el input que no coincide: npx fqmpeg resize smaller.mp4 --height 1080 (igualando el alto del otro clip). Si los aspect ratios difieren y no querés cropear, paddea con pad primero para igualar dimensiones y luego apila — usar border es un workaround rápido ya que agrega un marco de ancho configurable a cada lado.
Agregar un border me deja un tamaño de salida no estándar — ¿importa?
Sí, en plataformas con requisitos estrictos de aspect ratio (posts cuadrados de Instagram a 1:1, YouTube Shorts a 9:16). Una fuente 1920x1080 con --width 30 queda en 1980x1140, que la mayoría de players letterboxean y algunas redes sociales recortan. Dos workarounds: (1) usa crop primero para encoger el video interno y luego border para volver al tamaño original; (2) dejá que la plataforma haga lo suyo y aceptá que el borde puede ser cropeado o letterboxed.
¿Puedo evitar la pérdida generacional de H.264 al encadenar varios verbos overlay?
Cada invocación de fqmpeg re-encodea — la pérdida se acumula. Dos formas de mitigarla: (1) sube la calidad intermedia a -crf 18 o lossless editando el output de --dry-run; (2) baipasea fqmpeg para la cadena combinando los strings de filtro de cada --dry-run en una sola invocación de FFmpeg con -vf "filter1,filter2,filter3" (o -filter_complex si algún verbo usa varios inputs). Para 2-3 pasadas la pérdida rara vez se nota en contenido real; para 5+ pasadas suma.
Conclusión
Los 13 verbos de C8 cubren las operaciones de composición que usás después de editar pero antes del output final:
- Gráficos y texto estático (
watermark,text,drawbox,timecode,border) — quemar logos, títulos, highlights, timestamps, marcos - Composición multi-video (
pip,pip-grid,blend,stack) — picture-in-picture, grids de 2-9 inputs, blends por opacidad, stacks antes/después - Imagen a video (
picture) — promover una imagen fija + audio a un MP4 reproducible - Overlays analíticos (
video-info-overlay,histogram-overlay,progress) — quemar metadata de frame, scopes de color y progreso del tiempo para revisión
Cada verbo imprime su invocación FFmpeg subyacente bajo --dry-run, así que cuando los defaults de fqmpeg no encajan (una posición custom de histogram-overlay, un formato custom de drawtext, una cadena multi-filtro de una sola pasada para evitar pérdida generacional), copia el comando, persónalízalo y corre FFmpeg directo. Para el mapa más amplio de fqmpeg, ve la guía completa de fqmpeg.