Video 8M
proyecto | |
---|---|
nombre | Video 8M |
mantenedor | Looper |
palabrasclave | video, audio, ffmpeg, linux, concurso |
descripcion | Comprimir cualquier video (incluso episodios de series o películas) en solo 8 MB. |
fechainicio | 2022/01/01 00:00 |
estado | Completado |
Este proyecto es una pequeña competencia. Las personas toman un video cualquiera (puede ser un episodio de una serie o incluso una película completa) y la comprimen para que esta pueda entrar en el límite de 8 MB de Discord.
¿Por qué?
Para distraerme :)
La razón más interesante es para aprender cómo funcionan los codecs de audio y video. Cuando las personas trabajan con video y tienen hardware capaz, no tendrán problemas y terminan trabajando con archivos de video que son excesivamente pesados, y que bien podrían ser bastante más livianos. Esto tiene especial importancia si tu video será distribuido en Internet: un peso razonable puede ahorrar muchos datos y traer más calidad por un consumo similar.
Algunos resultados
Galería
Inventario
Aquí van cosas que forman parte del proyecto.
Artefacto | Características y/o capacidades | Descripción |
---|---|---|
PC | AMD Ryzen 5600G | Manjaro, ffmpeg-libfdk_aac |
… | … | … |
Curiosidades aprendidas o relevantes
- Códecs de video del mejor al mayor: AV1 > H.265 > VP9 > H.264 > VP8 > Theora > MPEG4 Xvid > MPEG4 > MPEG2
- Velocidad de trabajo: lo mismo que arriba pero de forma inversa (AV1 es el más lento)
- Si tu máquina es multicore, scripts como Av1an pueden aprovechar mejor la capacidad de CPU.
- Códecs de audio del mejor al mayor: Opus (libopus) > AAC-LC (libfdk_aac) ~ Ogg Vorbis (libvorbis) > MP3 (LAME)
- AV1, VP9, VP8, Theora son codecs de video libres. Opus y Ogg Vorbis son codecs de audio libres.
- Todos usan el códec H.264 (.mp4) pero pocos son los que saben producir archivos de buena calidad y óptimo peso.
Bitácora
Un códec es un algoritmo que usualmente comprime datos, sean video o audio. Hay códecs sencillos y complejos, así como hay libres o propietarios. Los códecs complejos suelen ser más difíciles de implementar en código o más lentos al ejecutarse, pero pueden comprimir más el peso de un video manteniendo una calidad similar.
Si queremos trabajar con video, podemos definir nuestros parámetros como un triángulo:
- Complejidad
- Calidad
- Peso
Solo podemos elegir priorizar dos, en detrimento de la tercera. Por ejemplo: ahora el códec MPEG2 (DVD) es antiguo y menos complejo. Por lo tanto, se llevará bien en máquinas con muy bajos recursos. Si queremos un mejor (menor) peso, nuestra calidad sufrirá. O podemos elegir mejor calidad: esto significará mayor peso.
Nosotros ordenaremos nuestras exigencias así:
- Peso (no podemos superar los 8 MB de ninguna manera)
- Complejidad (no tenemos vía libre para cualquier códec: debe ser uno soportado)
- Calidad (si el video se ve bien, es un bonus deseable)
Formatos de video soportados por Discord
Si queremos mejor calidad con un mejor (menor) peso, nuestro códec debe ser más complejo. Como es un requisito que el video se pueda visualizar en Discord (una app en Electron), nuestra tabla de formatos es similar a la tabla de soporte de Google Chrome / Chromium, ordenadas por complejidad:
- AV1 / webm (en beta, activable con flags de Electron)
- VP9 / webm
- H.264 Main/High Profile / mp4
- VP8 / webm
- H.264 Baseline Profile / mp4
Como en nuestros requisitos podemos optar por un códec complejo, en esta ocasión utilizaremos VP9: es el mejor códec que funciona actualmente sin tener que habilitar flags experimentales o cosas así. Igual, sabiendo modificar parámetros de x264 (para H.264), podemos obtener teóricamente algo menos complejo pero que aún se pueda distinguir.
Utilizando ffmpeg
ffmpeg
es "la navaja suiza del audio y video": puedes convertir videos de un formato a otro, multiplexar o demultiplexar, manipular el video en sí (cortarlo, agregar subtítulos, obtener índices de calidad, cambiarlo de tamaño), manipular audio, subtítulos y datos. Prácticamente todo se puede hacer con él, y es una de mis herramientas favoritas.
Nuestro comando empieza de forma sencilla: ffmpeg -i episodiooriginal.mkv 8mb.webm
. Como ya le pasamos la extensión (webm), el software elegirá los códecs indicados y convertirá el video para nosotros. Y aunque funciona, el peso es demasiado grande (supera los 100MB, lo cancelé). El solo usar un formato superior no es suficiente: necesitamos ver las opciones que nos ofrece para tunearlo más.
Reduciendo la resolución
1920x1080 es demasiado. Si reducimos la resolución a algo más manejable, podemos reducir mucho del peso.
ffmpeg -i episodiooriginal.mkv -vcodec libvpx-vp9 -s 426x240 -an 8mb.webm
Reduciendo el framerate
Cada segundo del video contiene ~24 "imágenes" comprimidas, que llamaremos frames desde ahora. Lo usual es ver rates de 23.976 (cine), 24, 25 (TV PAL), 29.97 o 30 (smartphones y TV NTSC), 60 (videocámaras con video entrelazado, y gaming) en video normal.
El video suele usar estos tipos de imágenes:
- I-Frames: (intra, key) son frames completas, uno tras otro. Piensa que puedes juntar cientos de JPEGs y volver el producto un video. Solo es eso, algo sencillo.
- P-Frames: (predicted) son frames que no guardan toda la imagen sino la diferencia respecto a su anterior (predicción temporal). Por ejemplo: si te grabas lanzando un balón, un I-Frame tiene la imagen de todo el lugar, y los P-Frames que le siguen contienen solo el balón en movimiento. Recalcular toda la imagen combinando los frames puede ser más complejo, pero nuestro peso se reducirá mucho.
- B-Frames: (bidirectional prediction) imágenes que contienen referencias no solo a frames pasados, sino a frames futuros. Más complejo todavía, pero reduce aún más el peso.
Podemos tunear a mano la cantidad de I-Frames para reducirlos, pero el codificador ya hace un buen trabajo identificando cambios de escena y usando los I-Frames necesarios. Nosotros podemos contribuir a eso reduciendo la cantidad de frames en general:
ffmpeg -i episodiooriginal.mkv -vcodec libvpx-vp9 -s 426x240 -r 12 -an 8mb.webm
Un framerate de 12 será suficiente por ahora. Si lo reducimos más, la calidad podría mejorar un poquito pero el video pasaría a verse más "robótico".
Reduciendo la calidad
VP9 (bueno, libvpx-vp9) nos permite varios parámetros interesantes:
- CBR (bitrate constante). En este caso le damos una velocidad de bits (cuántos bits usaremos por segundo). Más bits es mejor calidad, pero más peso. CRF tratará de usar esa cantidad de bits todo el tiempo, aunque en algunos momentos sea o no sea necesario.
- CRF (calidad constante). En este caso le damos un "número" que signifique una cierta calidad, y CRF tratará de conseguir esa calidad cuando sea posible. Si debe reducir el bitrate en escenas sencillas lo hará, y en escenas complejas subirá el uso de bits.
Como el video contiene cantidades similares de escenas planas y/o quietas (fáciles de codificar), CRF nos conviene. Pero queremos la capacidad de CBR de darle un límite y que este no sea sobrepasado, asi que podemos usar una combinación:
ffmpeg -i episodiooriginal.mkv -vcodec libvpx-vp9 -s 426x240 -crf 62 -r 12 -b:v 48k -an 8mb.webm
CRF se puede mover desde 1 (mejor calidad) a 63 (peor calidad). Elegimos 62 y un límite de 48 Kbps: (48 Kbps*(22min*60seg))/8=7920 KB
será el peso máximo del archivo, pero al ser CRF esperamos no tener que usar 48 Kbps todo el tiempo.
Audio
Necesitamos audio. Por suerte, VP9 se suele utilizar con códecs de audio de alta eficiencia, en nuestro caso Opus. Este códec es tan genial que puede traer calidad buena a bitrates realmente miserables para audio complejo (música), como 48 y 64 Kbps (equivalente parcialmente a un MP3 de 128 Kbps).
Leyendo información sobre el funcionamiento de Opus, y necesitando usar 2 MB o menos del peso del archivo, tendremos que usar bitrates muy bajos y solo un canal de audio, en mono.
ffmpeg -i episodiooriginal.mkv -vcodec libvpx-vp9 -s 426x240 -crf 62 -r 12 -b:v 48k -acodec libopus -b:a 14k -ac 1 8mb.webm
14Kbps según la tabla nos da audio full-band (los 20KHz completos de espectro): no sonará prístino, pero será superior al audio de calidad telefónica (4KHz) típica de estos bitrates.
La cereza del pastel: codificación en dos pasos
Ya tenemos un archivo con el peso indicado. Pero podemos mejorar la calidad.
CRF funciona dando más datos a escenas más complejas, y menos datos a escenas sencillas. Pero en un proceso normal (codificación en un solo paso) CRF no puede ver muy adelante y es posible que no siempre tome buenas decisiones. Con la codificación en dos pasos, en el primer paso se analiza todo el video para descubrir qué escenas son de qué tipo y hasta dónde duran, y en el segundo paso CRF puede hacer una repartición de datos más óptima, elegir mejor sus I-Frames, etc.
ffmpeg -i episodiooriginal.mkv -vcodec libvpx-vp9 -s 426x240 -crf 62 -r 12 -b:v 48k -pass 1 -an -f /dev/null
ffmpeg -i episodiooriginal.mkv -vcodec libvpx-vp9 -s 426x240 -crf 62 -r 12 -b:v 48k -pass 2 -acodec libopus -b:a 14k -ac 1 8mb.webm
¡Listo!
Cosas para el futuro
Ya pudimos obtener el peso deseado, y una calidad razonable. Si estamos realmente aburridos, podemos seguir mejorando más:
- Podemos probar opciones de alto nivel como
deadline
: usamos good que es por defecto, pero best quizás dé alguna mejora. - Podemos probar opciones de bajo nivel como apagar la codificación multi hilo por tiles, o incluso trabajar directamente con macrobloques y cosas así.
VP9 tiene varias opciones que valen la pena mirar.