Codificación en vivo con VP9 usando FFmpeg

Parámetros de codificación

VP9 proporciona una variedad de parámetros para optimizar la codificación en vivo. Algunos principios generales se analizan en Modos de tasa de bits.

Ejemplo de codificación de VP9 con FFmpeg

En la siguiente tabla, se describen los parámetros de una llamada ffmpeg de ejemplo para la codificación de VP9.

Parámetro Descripción
-quality realtime realtime es fundamental para la transmisión en vivo y para velocidades superiores a 5.
-speed 6 La velocidad 5 a 8 se debe usar para la codificación en vivo o en tiempo real. Los números más bajos (5 o 6) son de mayor calidad, pero requieren más potencia de CPU. Los números más altos (7 o 8) tendrán una calidad inferior, pero serán más fáciles de administrar para los casos de uso de menor latencia y también para los dispositivos con menor potencia de CPU, como los dispositivos móviles.
-tile-columns 4 La división en mosaicos divide el video en regiones rectangulares, lo que permite el procesamiento de subprocesos múltiples para la codificación y decodificación. La cantidad de segmentos siempre es una potencia de dos. 0 = 1 mosaico, 1 = 2, 2 = 4, 3 = 8, 4 = 16, 5 = 32.
-frame-parallel 1 Habilita las funciones de decodificación paralela.
-threads 8 Es la cantidad máxima de subprocesos que se pueden usar.
-static-thresh 0 Es el umbral de detección de movimiento.
-max-intra-rate 300 Tasa de bits máxima de I-frame (porcentaje)
-deadline realtime Versión alternativa (heredada) de -quality realtime
-lag-in-frames 0 Cantidad máxima de fotogramas que se pueden retrasar
-qmin 4 -qmax 48 Son los valores mínimo y máximo del cuantificador. Los valores que se muestran aquí son solo una sugerencia, y ajustarlos ayudará a aumentar o disminuir la calidad del video a expensas de la eficiencia de la compresión.
-row-mt 1 Habilita el subprocesamiento múltiple de filas. Permite usar hasta 2 veces el subproceso como columnas de mosaicos. 0 = apagado, 1 = encendido.
-error-resilient 1 Habilita las funciones de tolerancia a errores.

Cómo elegir parámetros de codificación

La siguiente información utiliza la codificación de tasa de bits constante (CBR) para la transmisión en vivo con tasa de bits adaptable (ABR), en la que cada tasa objetivo se establece de forma explícita en el manifiesto del empaquetador. Esto generará un "cambio" más limpio entre las tarifas para los clientes. La codificación de tasa de bits variable (VBR) y el modo CQ también son opciones si la tasa de bits puede ser más flexible o si la codificación se realiza en fragmentos. El modo Q tendrá dificultades con la codificación en tiempo real que se requiere para el video en vivo. Consulta Modos de tasa de bits para obtener más información.

Para obtener más detalles sobre cómo manipular VP9, también vale la pena consultar el artículo complementario sobre la configuración de VOD, pero teniendo en cuenta un enfoque en CBR.

Sugerencias y trucos

Recuerda que, cuando transmites en vivo, todo se limita a una velocidad de codificación en tiempo real mínima de 1x (FFmpeg informa la velocidad de codificación a medida que avanza). Si la velocidad de codificación disminuye a menos de 1x, el proceso de codificación no podrá seguir el ritmo de la entrada de video en vivo, y los usuarios experimentarán almacenamiento en búfer y pausas en la transmisión, lo que hará que el flujo sea inutilizable durante la transmisión en vivo (aunque el archivo será generalmente utilizable).

Ejemplos de parámetros de codificación en acción

A continuación, se muestra la utilización de la CPU a 25 FPS para varios tamaños de fotogramas en una computadora de escritorio i5 de cuatro núcleos y 3.6 GHz que ejecuta Linux:

Resolución objetivo Parámetros de VP9 de FFmpeg CPU / velocidad (ejemplo)
3840 x 2160 (2160p) -r 30 -g 90 -s 3840x2160 -quality realtime -speed 5 -threads 16 -row-mt 1 -tile-columns 3 -frame-parallel 1 -qmin 4 -qmax 48 -b:v 7800k ~88% 0.39x
2560 x 1440 (1440p) -r 30 -g 90 -s 2560x1440 -quality realtime -speed 5 -threads 16 -row-mt 1 -tile-columns 3 -frame-parallel 1 -qmin 4 -qmax 48 -b:v 6000k ~86% 0.68x
1920 x 1080 (1080p) -r 30 -g 90 -s 1920x1080 -quality realtime -speed 5 -threads 8 -row-mt 1 -tile-columns 2 -frame-parallel 1 -qmin 4 -qmax 48 -b:v 4500k ~82% 1.04x
1280 x 720 (720 p) -r 30 -g 90 -s 1280x720 -quality realtime -speed 5 -threads 8 -row-mt 1 -tile-columns 2 -frame-parallel 1 -qmin 4 -qmax 48 -b:v 3000k ~78% 1.77x
854 x 480 (480p) -r 30 -g 90 -s 854x480 -quality realtime -speed 6 -threads 4 -row-mt 1 -tile-columns 1 -frame-parallel 1 -qmin 4 -qmax 48 -b:v 1800k ~64% 3.51x
640 x 360 (360p) -r 30 -g 90 -s 640x360 -quality realtime -speed 7 -threads 4 -row-mt 1 -tile-columns 1 -frame-parallel 0 -qmin 4 -qmax 48 -b:v 730k ~62% 5.27x
426 x 240 (240p) -r 30 -g 90 -s 426x240 -quality realtime -speed 8 -threads 2 -row-mt 1 -tile-columns 0 -frame-parallel 0 -qmin 4 -qmax 48 -b:v 365k ~66% 8.27x

Un ejemplo de FFmpeg podría verse así:

ffmpeg -stream_loop 100 -i /home/id3as/Videos/120s_tears_of_steel_1080p.webm \
  -r 30 -g 90 -s 3840x2160 -quality realtime -speed 5 -threads 16 -row-mt 1 \
  -tile-columns 3 -frame-parallel 1 -qmin 4 -qmax 48 -b:v 7800k -c:v libvpx-vp9 \
  -b:a 128k -c:a libopus -f webm pipe1

Sugerencias y trucos

  • Ten en cuenta que aquí generamos la salida en una canalización FIFO ("pipe1"), que se debe crear antes de la ejecución, antes de ejecutar el comando FFmpeg. Para ello, ejecuta el comando mkfifo pipe1 en tu directorio de trabajo. Cuando uses Shaka Packager, escuchará esa canalización como su fuente de entrada para la transmisión determinada. Otros modelos de empaquetado pueden requerir un método diferente.

  • Para asegurarte de que se reconozcan los comandos de -row-mt, usa la versión estable más reciente de FFmpeg (actualmente, la 3.3.3) desde https://www.ffmpeg.org/download.html.

Ejemplo de conjunto de tasas de bits adaptables

Según la potencia de la máquina que ejecuta la codificación de FFmpeg, es posible que no se puedan entregar todas las siguientes codificaciones al mismo tiempo, por lo que se debe seleccionar un subconjunto adecuado para los recursos disponibles y los públicos objetivo de la lista.

Conjunto completo de ABR de FFmpeg

En una situación ideal, combinamos los ejemplos de codificación que se describen en la sección anterior para crear un solo comando que los produzca todos al mismo tiempo:

ffmpeg -stream_loop 100 -i lakes1080p.mp4 \
  -y -r 25 -g 75 -s 3840x2160 -quality realtime -speed 5 -threads 8 \
  -tile-columns 2 -frame-parallel 1 \
  -b:v 7800k -c:v libvpx-vp9 -b:a 196k -c:a libopus -f webm pipe1 \
  -y -r 25 -g 75 -s 2560x1440 -quality realtime -speed 5 -threads 8 \
  -tile-columns 2 -frame-parallel 1 \
  -b:v 6000k -c:v libvpx-vp9 -b:a 196k -c:a libopus -f webm pipe2 \
  -y -r 25 -g 75 -s 1920x1080 -quality realtime -speed 5 -threads 4 \
  -tile-columns 2 -frame-parallel 1 \
  -b:v 4500k -c:v libvpx-vp9 -b:a 196k -c:a libopus -f webm pipe3 \
  -y -r 25 -g 75 -s 1280x720 -quality realtime -speed 5 -threads 4 \
  -tile-columns 2 -frame-parallel 1 \
  -b:v 3000k -c:v libvpx-vp9 -b:a 196k -c:a libopus -f webm pipe4 \
  -y -r 25 -g 75 -s 854x480 -quality realtime -speed 6 -threads 4 \
  -tile-columns 2 -frame-parallel 1 \
  -b:v 2000k -c:v libvpx-vp9 -b:a 196k -c:a libopus -f webm pipe5 \
  -y -r 25 -g 75 -s 640x360 -quality realtime -speed 7 -threads 2 \
  -tile-columns 1 -frame-parallel 0 \
  -b:v 730k -c:v libvpx-vp9 -b:a 128k -c:a libopus -f webm pipe6 \
  -y -r 25 -g 75 -s 426x240 -quality realtime -speed 8 -threads 2 \
  -tile-columns 1 -frame-parallel 0 \
  -b:v 365k -c:v libvpx-vp9 -b:a 64k -c:a libopus -f webm pipe7

Sin embargo, el conjunto completo anterior requerirá una CPU muy potente o, posiblemente, compatibilidad con la descarga de GPU de hardware, como la que proporcionan cada vez más algunos chipsets. Intel Kabylake (y versiones posteriores) tiene una canalización de codificación de hardware completa. (Ten en cuenta que la GPU Kabylake puede codificar VP9 de 8 bits, pero no de 10 bits).

Un ejemplo práctico para computadoras de escritorio con Shaka Packager

Un ejemplo más práctico para las computadoras de escritorio comunes podría usar Shaka Packager. Una forma sencilla de configurar Shaka es instalarlo dentro de un contenedor de Docker con la imagen de DockerHub de Google. Aquí encontrarás las instrucciones:

https://github.com/google/shaka-packager#using-docker-for-testing--development

Para este ejemplo, usamos una máquina con la siguiente configuración:

Sistema Host: obs Kernel: 4.4.0-91-lowlatency x86_64 (64 bits)
Computadora Xfce 4.12.3 Distro: OS: https://ubuntustudio.org/2016/10/ubuntu-studio-16-10-released/
CPU Intel Core i5-6500 (-MCP-) de cuatro núcleos
caché: 6144 KB
velocidades de reloj: máx.: 3600 MHz 1: 800 MHz 2: 800 MHz 3: 800 MHz 4: 800 MHz
Tarjeta gráfica Gráficos integrados Intel Skylake
Memoria 8 GB de RAM

En la práctica, esta máquina podría producir de manera óptima el siguiente rango útil de codificaciones ABR, con FFmpeg informando de manera constante una velocidad de codificación de 1x:

ffmpeg -stream_loop 100 -i 120s_tears_of_steel_1080p.webm \
  -y -r 30 -g 90 -s 1920x1080 -quality realtime -speed 7 -threads 8 \
  -row-mt 1 -tile-columns 2 -frame-parallel 1 -qmin 4 -qmax 48 \
  -b:v 4500k -c:v libvpx-vp9 -b:a 128k -c:a libopus -f webm pipe1 \
  -y -r 30 -g 90 -s 1280x720 -quality realtime -speed 8 -threads 6 \
  -row-mt 1 -tile-columns 2 -frame-parallel 1 -qmin 4 -qmax 48 \
  -b:v 3000k -c:v libvpx-vp9 -b:a 128k -c:a libopus -f webm pipe2 \
  -y -r 30 -g 90 -s 640x360 -quality realtime -speed 8 -threads 2 \
  -row-mt 1 -tile-columns 1 -frame-parallel 1 -qmin 4 -qmax 48 \
  -b:v 730k -c:v libvpx-vp9 -b:a 128k -c:a libopus -f webm pipe3

Ten en cuenta que la configuración de -speed es bastante alta. Estos parámetros de configuración se establecieron de forma experimental y variarán de una máquina a otra.

Sobrecarga de Shaka Packager

El empaquetado no es una actividad que requiera mucha CPU. Shaka Packager se puede configurar para que escuche todas las salidas, incluso si FFmpeg solo entrega un subconjunto. Estos son los parámetros de configuración del empaquetador que se probaron en la máquina descrita anteriormente:

packager \
  in=pipe1,stream=audio,init_segment=livehd-audio-1080.webm,segment_template=livehd-audio-1080-\$Number\$.webm \
  in=pipe1,stream=video,init_segment=livehd-video-1080.webm,template=livehd-video-1080-\$Number\$.webm \
  in=pipe2,stream=audio,init_segment=livehd-audio-720.webm,segment_template=livehd-audio-720-\$Number\$.webm \
  in=pipe2,stream=video,init_segment=livehd-video-720.webm,template=livehd-video-720-\$Number\$.webm \
  in=pipe3,stream=audio,init_segment=livehd-audio-360.webm,segment_template=livehd-audio-360-\$Number\$.webm \
  in=pipe3,stream=video,init_segment=livehd-video-360.webm,template=livehd-video-360-\$Number\$.webm \
  --mpd_output livehd.mpd --dump_stream_info --min_buffer_time=10 --time_shift_buffer_depth=300 \
  --segment_duration=3 --io_block_size 65536