Volver al blog
13 de agosto de 2026Sergei Solod13 min de lectura

Cómo reduje un vídeo de producción de ~280 MB a ~50 MB con H.264

Un vídeo real del sistema en producción pasó de unos 280 MB a unos 50 MB después de rehacer la configuración H.264 alrededor de CRF 28, x264 veryslow, un límite de resolución de clase 720p, frecuencias de imagen útiles y requisitos moderados para el decodificador. En una etapa anterior, el mismo ejemplo ya había bajado de unos 350 a 238 MB, y una auditoría de la biblioteca antigua mostró que eran habituales los H.264 de varios megabits por segundo.

H.264FFmpegx264Compresión de vídeoRendimiento en líneaOptimización multimedia

El número que convirtió esta optimización en algo concreto fue muy simple: un vídeo real de producción pasó de aproximadamente 280 MB a 50 MB después de aplicar la nueva política H.264. Eso equivale a unas 5,6 veces menos, con un ahorro aproximado de 230 MB o del 82% del tamaño original.

No conseguí ese resultado cambiando la entrega a AV1, HEVC o VP9. La salida siguió siendo H.264 dentro de MP4. Lo que cambió fue la política alrededor del códec: menos píxeles innecesarios, menos muestras temporales inútiles, un objetivo de calidad mucho menos conservador, más trabajo del codificador una sola vez y un contrato de decodificación deliberadamente acotado.

Era una carga muy concreta: vídeos cortos ilustrados y animados, alrededor del 80% del tráfico desde móviles, ancho de banda como coste recurrente y codificación previa, donde gastar CPU una vez resulta barato frente a servir archivos sobredimensionados una y otra vez.

De forma resumida, las políticas antigua y nueva eran:

configuración anterior
H.264 Main @ Level 4.0
CRF 19
preajuste slow
hasta 1920x1080 / 1080x1920
30 fps
refs = 3
B-frames = 3
GOP ≈ 2 segundos
VBV ≈ 10M / 20M

configuración nueva
H.264 Main @ Level 3.1
CRF 28
preajuste veryslow
límite de clase 720p, sin ampliación
CFR útil, normalmente <= 30 fps
refs = 4
B-frames = 5
GOP ≈ 5 segundos
VBV ≈ 4M / 8M
yuv420p / avc1 / faststart

El resultado medido: de ~280 MB a ~50 MB

Tengo varias mediciones de producción, pero no todas pertenecen al mismo experimento. Mantenerlas separadas es más importante que escoger el porcentaje más espectacular.

MediciónAntesDespuésReducción
Mismo vídeo concreto~280 MB~50 MB~5,6× menor / ~82% menos
Etapa anterior del mismo archivo~350 MB~238 MB~1,47× menor / 32% menos

La primera fila es la evidencia más limpia para el título: el mismo vídeo concreto antes y después de la política nueva, aproximadamente 280 MB frente a 50 MB. En los registros recuperados no quedó una línea de duración y tasa de bits para esa pareja exacta, así que no la invento. El cambio de tamaño por sí solo ya da un factor aproximado de 5,6.

El caso ~350→238 MB fue una fase anterior de optimización del mismo ejemplo de aquella etapa. El resultado era aproximadamente 1264×720, 30 fps, unos 500 segundos, sin sonido y alrededor de 3,8 Mbps. La aritmética coincide: 3,8 Mbps durante unos 500 segundos son aproximadamente 238 MB. Ya era un ahorro del 32% frente a ~350 MB, pero seguía siendo demasiado para mi objetivo de ancho de banda.

La biblioteca antigua también mostraba que los H.264 grandes no eran una anomalía aislada. En una auditoría había 238 vídeos de producción que sumaban 6,37 GB: 117 H.264 y 121 AV1. 101 archivos pesaban al menos 20 MB y 34 al menos 50 MB. Algunos H.264 grandes eran:

TamañoDuraciónTasa de bits media
121,0 MB4:253,83 Mbps
101,2 MB5:192,659 Mbps
92,78 MB4:442,735 Mbps
90,10 MB3:553,206 Mbps
89,74 MB4:412,676 Mbps

Son contenidos distintos, así que la tabla aporta contexto y no constituye una prueba A/B. Lo importante es que el ejemplo antiguo de 3,8 Mbps no era una anomalía: varios H.264 grandes de la biblioteca anterior estaban realmente en el intervalo aproximado de 2,6–3,8 Mbps.

La optimización más importante no era una opción de FFmpeg

El cambio decisivo fue cómo pensaba en el coste.

La codificación ocurre una vez. La transferencia del archivo ocurre en cada reproducción.

En vídeo en tiempo real, gastar mucha más CPU para ahorrar algo de tasa de bits puede ser un mal intercambio. Mis archivos se codifican previamente y luego se sirven una y otra vez. En ese modelo, ahorrar diez minutos en la codificación puede no tener casi valor si esa codificación más rápida hace que cada futura descarga sea mayor.

Por eso -preset veryslow tiene sentido para mí. Estoy dispuesto a gastar CPU una vez si x264 puede usar ese trabajo para encontrar una representación más eficiente. El navegador no repite la búsqueda del codificador; solo decodifica el flujo de bits final.

La regla pasó a ser sencilla: gastar cómputo en el paso que ocurre una vez y ser tacaño con los bytes en el paso que se repite.

Por qué mantuve H.264 en vez de perseguir el códec más nuevo

No afirmo que H.264 sea el códec con mejor compresión disponible. No lo es. Los códecs más nuevos son muy interesantes cuando la arquitectura puede mantener varias versiones y escoger la mejor para cada cliente.

Mi restricción era distinta: una URL, un archivo, un códec y el menor número posible de sorpresas de reproducción en una audiencia muy móvil.

Para ese trabajo, H.264 en MP4 sigue siendo una base muy segura. Apple actualmente indica a los desarrolladores de sitios que usen archivos MP4 codificados con H.264 para vídeo estático en Safari. La documentación actual de Android incluye H.264 en MP4 y exige un decodificador perfil Main a partir de Android 6.0; sus recomendaciones de reproducción H.264 también incluyen 1280×720 a 30 fps para HD, con la advertencia de que HD no está disponible en todos los dispositivos. Véanse los formatos multimedia compatibles de Android.

Eso no significa que los dispositivos modernos estén limitados a perfil Main o nivel 3.1. Apple, por ejemplo, suele preferir perfil High frente a Main o Baseline para HLS. Elegí Main@3.1 porque quería deliberadamente un margen modesto para el decodificador en un único MP4 estático, no porque Apple lo exija.

Dejé de codificar píxeles que no necesitaban existir

La resolución fue una de las palancas más grandes. Mi techo quedó aproximadamente en 1280×720 para horizontal, 720×1280 para vertical y alrededor de 960×960 para material cuadrado o de orientación mixta.

La regla más importante es: no hacer reescalado al alza solo para alcanzar ese techo.

Si una fuente es 900×600, llevarla a 1280×720 no recupera detalle. Solo crea más muestras que el codificador tiene que describir. Una fuente 1920×1080 puede reducirse a la clase 720p, mientras que una de 900×600 puede quedarse cerca de 900×600. El techo es un máximo, no un objetivo.

Suena trivial, pero eliminar píxeles innecesarios puede ahorrar más que muchos ajustes oscuros del codificador.

Elegí ese límite por una razón cuantificable, no porque 720p sea un número redondo. Un fotograma de 1920×1080 contiene 2.073.600 píxeles; uno de 1280×720 contiene 921.600. Pasar de 1080p a 720p elimina alrededor del 55,6% de las muestras espaciales antes incluso de que el codificador empiece a comprimir.

También valoré usar 540p como opción general. Sin embargo, 960×540 contiene solo 518.400 píxeles: un 43,75% menos que 1280×720, por lo que conserva el 56,25% de las muestras de 720p. En material ilustrado esas muestras describen líneas finas, ojos, pelo, dedos, caras y contornos definidos. Si todavía necesito menos bytes, prefiero probar un CRF algo mayor antes de eliminar a ciegas otro 43,75% de información espacial. La cuantización se puede reajustar en otra codificación; el detalle eliminado al reducir la resolución ya no existe.

Por eso la clase 720p es mi límite general prudente, no una afirmación de que 540p sea malo. Para un recurso concreto, una versión 540p medida puede ser la mejor. Simplemente no convierto ese recorte espacial irreversible en una regla para toda la biblioteca sin datos.

Dejé de pagar por fotogramas que la fuente realmente no tenía

La tasa de fotogramas es otro multiplicador. Si una animación contiene aproximadamente 16 estados visuales útiles por segundo, almacenarla a 30 o 60 fps no crea automáticamente un movimiento mejor. A menudo solo añade muestras temporales repetidas o sintetizadas que igualmente deben codificarse.

Mi política es preservar la cadencia útil de la fuente y normalmente no superar 30 fps. Para este tipo de material, 12, 15, 16, 18, 20, 24, 25 o 30 fps pueden ser perfectamente razonables si realmente describen la fuente.

También prefiero un CFR limpio en la salida generada. VFR no está roto por definición; CFR simplemente simplifica marcas de tiempo, conteo de fotogramas, comprobaciones de duración, posicionamiento y validación posterior en mi flujo de procesamiento.

El principio general es más útil que un número concreto: no pagar ancho de banda por información temporal que la fuente no contiene.

CRF 28 es una elección para esta carga de trabajo, no un número mágico

No quería forzar todos los vídeos a la misma tasa de bits objetivo. Una ilustración casi estática y una escena con movimiento complejo no necesitan el mismo número de bits para verse aceptables.

Por eso uso el modo CRF de x264 y, para esta carga de trabajo ilustrada centrada en el ancho de banda, me quedé alrededor de -crf 28. FFmpeg documenta CRF en libx264 como control de tasa con calidad constante; véase la documentación de códecs de FFmpeg.

CRF 28 es deliberadamente agresivo. No lo copiaría a ciegas para grano de película, vídeo de cámara ruidoso, texto diminuto en capturas de pantalla o una carga donde la fidelidad importe más que el ancho de banda.

Tampoco tengo una métrica perceptiva universal que demuestre que CRF 28 es transparente. Lo que sí puedo afirmar para mi material es más limitado: los archivos se hicieron mucho más pequeños y siguieron viéndose normales durante una reproducción corriente. Es una observación práctica, no una afirmación de pérdida visual cero.

Veryslow es caro para el codificador, no automáticamente para el decodificador

Mi preajuste es -preset veryslow. Un preajuste más lento da a x264 más oportunidades de buscar decisiones de predicción y codificación eficientes. El precio es CPU y tiempo de codificación.

La distinción clave es que el esfuerzo del codificador y la complejidad del decodificador no son lo mismo.

Puedo dejar que x264 trabaje mucho y, aun así, limitar por separado el flujo final. Mi contrato conservador de salida es:

H.264, perfil Main
Level 3.1
yuv420p de 8 bits
avc1
refs = 4
fotogramas B = 5
B-pyramid = normal
GOP abierto = desactivado

FFmpeg expone CRF, preajustes, ajuste específico, restricciones de perfil, fotogramas de referencia y B-fotogramas por separado. Así es exactamente como los trato: el codificador puede buscar de forma cara, pero el lado de reproducción debe seguir siendo ordinario.

GOP y VBV son límites, no el control principal de calidad

Para estos vídeos progresivos cortos uso un GOP máximo de aproximadamente cinco segundos: cerca de -g 150 a 30 fps, -g 120 a 24 fps o -g 80 a 16 fps.

Es una elección para mi carga de trabajo, no una regla universal. La transmisión adaptativa tiene otras restricciones; Apple, por ejemplo, recomienda IDR cada dos segundos para HLS. No traslado automáticamente esa regla de HLS a MP4 estáticos y progresivos de pocos segundos.

Además uso aproximadamente:

-maxrate:v 4M
-bufsize:v 8M

Son techos contra picos de tasa de bits inusuales. No significan “codificar todo a 4 Mbps”. CRF sigue controlando la asignación normal de bits, así que los vídeos fáciles pueden seguir quedando muy pequeños.

También mantuve convencional el contenedor MP4

Uso explícitamente avc1. La documentación actual de Apple para HLS recomienda formatos de muestra como avc1 en lugar de avc3. Eso no es lo que redujo mis archivos, pero encaja con el objetivo de producir H.264 en MP4 de forma convencional.

También uso -movflags +faststart. La documentación de formatos de FFmpeg explica que faststart mueve el índice moov del MP4 al principio del archivo. Los requisitos de transmisión por HTTP de Android también indican que en MPEG-4 moov debe preceder a mdat después de ftyp.

ftyp
moov
mdat

Faststart no mejora la compresión. Hace menos problemática la reproducción progresiva por HTTP.

Para SDR normal uso yuv420p de 8 bits y señalizo BT.709 con rango de vídeo limitado. Si el vídeo no necesita sonido, no fabrico una pista de sonido. Para material ilustrado uso además -tune animation; lo considero una elección específica del contenido, no parte del contrato universal de compatibilidad.

El perfil central de FFmpeg

Para una fuente ilustrada de 30 fps, la parte principal del comando se parece aproximadamente a esto:

ffmpeg -i input \
  -c:v libx264 \
  -preset veryslow \
  -tune animation \
  -crf 28 \
  -profile:v main \
  -level:v 3.1 \
  -pix_fmt yuv420p \
  -tag:v avc1 \
  -refs 4 \
  -bf 5 \
  -g 150 \
  -maxrate:v 4M \
  -bufsize:v 8M \
  -x264-params "open-gop=0:b-pyramid=normal:nal-hrd=none" \
  -color_range tv \
  -color_primaries bt709 \
  -color_trc bt709 \
  -colorspace bt709 \
  -an \
  -movflags +faststart \
  output.mp4

El escalado y la tasa de fotogramas no están fijados de forma rígida a propósito. Una fuente 900×600 no debería ampliarse solo porque el techo sea 1280×720, y una animación naturalmente lenta no debería forzarse a 30 fps solo porque el ejemplo use -g 150.

El comando implementa la política; no es la política en sí.

Por qué los archivos pasaron a ocupar varias veces menos

No hubo una opción milagrosa.

La reducción salió de combinar varias decisiones que eliminaban desperdicios distintos: píxeles innecesarios, fotogramas innecesarios, un objetivo de calidad demasiado conservador, ajustes del codificador que favorecían la velocidad frente a la eficiencia, fotogramas clave demasiado frecuentes y flujos que no necesitaba.

Por eso decir “este archivo es H.264” explica sorprendentemente poco sobre su tamaño. Dos codificaciones H.264 de la misma fuente pueden diferir muchísimo porque el nombre del códec no describe resolución, FPS, control de tasa, preajuste, estructura GOP, perfil ni preparación de la fuente.

En mi caso, cambiar esas decisiones alrededor del códec importó más que cambiar el propio códec.

Lo que este resultado no demuestra

No aislé cada ajuste en un experimento controlado, así que no puedo atribuir honestamente un porcentaje exacto del ahorro a veryslow, CRF 28, la reducción de resolución o la reducción de FPS por separado.

Tampoco puedo afirmar que toda salida con CRF 28 sea perceptivamente transparente. “Sin pérdida de calidad evidente” es mi observación para esta carga de trabajo ilustrada a tamaños normales de visualización, no una garantía científica para cualquier vídeo.

Y no sostengo que un único archivo H.264 sea la arquitectura correcta para todos los sitios. Varias versiones, transmisión adaptativa, HDR, 4K y negociación de códecs cambian las compensaciones.

Lo que realmente puedo afirmar es más limitado: en una biblioteca de vídeos cortos ilustrados y animados donde el ancho de banda es prioritario, la mayoría del público usa móvil, el tiempo de codificación es barato y la reproducción predecible importa, este perfil redujo mis archivos varias veces sin que dejaran de verse normales durante una reproducción corriente.

Cómo cambió este resultado mi forma de optimizar

Antes pensaba en optimización de vídeo sobre todo como un problema de parámetros del codificador. Ahora lo veo como un problema de coste de vida útil.

El codificador puede ejecutarse una vez. Los bytes pueden cruzar la red miles o millones de veces.

Eso cambia lo que significa “caro”.

Estoy encantado de gastar CPU una vez. Estoy mucho menos dispuesto a enviar en cada futura petición píxeles creados por reescalado al alza, fotogramas que no añaden movimiento útil o tasa de bits que el contenido no necesita.

El códec siguió siendo aburrido: H.264 en MP4. La optimización ocurrió alrededor de él.

Para mi carga de trabajo, la lección es más útil que cualquier opción concreta de FFmpeg: optimiza el coste que pagas repetidamente, no el que pagas una sola vez.