Cálculo empírico do melhor valor de bitrate para compressão de vídeos

Na minha obsessão compulsiva por minimizar as coisas, de tempos em tempos tento aumentar, ao máximo, o espaço livre em minhas unidades de armazenamento. Um dos tipos de arquivo que consomem um espaço considerável em disco são os vídeos e, para diminuir esses tamanhos existem algumas coisas que você pode fazer:

  1. Usar codecs mais eficientes;
  2. Diminuir a resolução gráfica;
  3. Diminuir o framerate;
  4. Diminuir o bitrate.

No primeiro caso, o codec de vídeo mais eficiente (que cria arquivos mais “compactados”), atualmente, é o H.265 (também chamado de HEVC [High Efficiency Video Codec]), mas há um problema: Nem todo dispositivo suporta esse codec e, quando suporta, nem todos suportam com aceleração de hardware. Assim, o segundo codec mais eficiente, atualmente, é o H.264 (também chamado de AVC [Advanced Video Codec]). Então, não considero a primeira possibilidade muito interessante (a não ser que você só assista seus vídeos no computador e tenha uma placa de vídeo mais “moderna”, como a nVidia GTX1070 em diante, por exemplo.

O segundo caso, diminuir a resolução, não é realmente desejável… O terceiro pode implicar em problemas com sincronismo de fontes externas ao vídeo, como legendas, por exemplo, mas é uma boa ideia limitar o framerate em até 30 fps, já que a maioria das TVs não são capazes de taxas maiores que essa.

O quarto case é a taxa de transferência entre o stream de vídeo e o dispositivo. Em teoria, quanto maior a taxa, melhor a qualidade. Se for muito pequeno o dispositivo não terá alternativa senão fazer um “downsampling”, diminuindo a qualidade do vídeo. Se for muito alta, ele fará um “oversampling”, tentando aumentar a qualidade (o que muitas vezes só pode ser feito por interpolação – que, possivelmente, adicionará “artefatos” nos frames, piorando a qualidade!). Então, temos que achar um valor ideal, mínimo, para garantir a qualidade de cada frame e ter uma taxa de transferência boa. Infelizmente, a fórmula para o cálculo desse bitrate é empírica e pode depender do codec em uso. Eu achei uma fórmula ideal para as minhas necessidades:

\displaystyle vbr=\frac{width\cdot height\cdot framerate}{13824}\quad\texttt{[in kb/s]}

Eu poderia te dizer que esse valor 13824 é um valor obtido a partir de alguma relação esquisita entre o tamanho de um pixel (em bits), o aspect ratio e mais um monte de propriedades do vídeo, mas eu estaria mentindo… O fato é que eu acho que um bitrate de 2 Mb/s (2000 kb/s) para uma resolução de 720p, num framerate de 30 Hz, funciona muito bem e extrapolei a relação para qualquer outra resolução.

Assim, um pequenino script para calcular o bitrate de um vídeo:

#!/bin/bash
# ovbr.sh

if [ -z "$1" ]; then
  echo -e "\e[33;1mUsage\e[m: `basename $0` <video>\n\
\n
  Calculates the optimal bitrate, based on video resolution."
  exit 1
fi

W=`mediainfo --Inform="Video;%Width%" "$1"`
H=`mediainfo --Inform="Video;%Height%" "$1"`
F=`mediainfo --Inform="Video;%FrameRate%" "$1"`

# 13824 - empirical value (1280x720@30Hz -> 2000k bitrate)
BR="`bc <<< "scale=0; ($W * $H * $F ) / 13824"`"

echo "${BR}k"
exit 0

Quanto ao áudio, eu prefiro manter padronizado em cerca de 64 kb/s por canal. Assim, supondo que um vídeo tenha áudio e esse não seja monofônico, posso convertê-lo para um tamanho razoável usando:

$ BR=$(ovbr.sh video.mp4)
$ ffmpeg -i video.mp4 \
  -c:v libx264 -maxrate $BR -bufsize $BR -b:v $BR \
  -c:a ac3 -b:a 128k -ac 2 video_out.mp4

Você pode adicionar a opção -async 1 se quiser sincronizar o áudio, mas, como não estamos mudando nada a não ser o bitrate, isso não é necessário. Se o vídeo tiver um framerate maior que 30, ai sim é prudente a opção junto -r 30 para mudar o taxa de frames.