Encoder des vidéos avec le codec VP9 sur Ubuntu Trusty, en utilisant Libav (et Docker pour le fun)

Comme j’auto-héberge mes vidéos, j’ai besoin de les ré-encoder avant de les mettre en ligne. Ma source est en H.264 1080p, et je veux les réencoder avec des codecs libres, et dans une résolution et un bitrate adaptés à l’auto-hébergement.

Encoder des vidéos en WebM avec les codecs VP8 et Vorbis est assez facile sur Ubuntu, en utilisant libav.

Mais comment faire pour encoder avec les codecs VP9 et Opus, qui promettent un rapport qualité/poids deux fois meilleur?

vp9-opus

Sommaire

DISCLAIMER : je ne suis pas un spécialiste de l’encodage vidéo. Je me suis contenté des paramètres par défaut de libav. Il est probable qu’on puisse avoir de meilleurs résultats avec un paramétrage + fin de l’encodage (je suis preneur de suggestions). D’autre part, je me suis concentré sur la partie vidéo plutôt que la partie audio (dont je néglige ici la taille et la consommation de CPU)

VP8+Vorbis avec libav

D’abord, quand je dis que c’est facile en VP8+Vorbis, ça n’a pas toujours été vrai : il y avait un bug que j’ai rapporté dans la version de libav de Ubuntu 14.04 (Trusty) qui empêchait la lecture des fichiers dans Firefox : https://bugs.launchpad.net/ubuntu/+source/firefox/+bug/1323822

Heureusement, c’est corrigé depuis mi-juillet 2014, en passant à la version 9.14, au lieu de 9.13.

Sur Ubuntu 12.04 (Precise), pas de soucis, j’ai encodé pendant 2 ans dans ce format.

VP9+Opus avec libav

Pour le VP9, la version 9.x de libav (fournie avec Trusty) ne le prend hélas pas encore en charge. J’ai fait la demande de passer à la version 10 avant la sortie de Trusty, mais c’était déjà trop tard : https://bugs.launchpad.net/ubuntu/+source/libav/+bug/1296378

Le VP9 est bien disponible dans la version suivante d’Ubuntu (Utopic – 14.10) car ils sont passés à la version 10.3 de libav.

Mais ce n’est pas si simple : il manque encore un patch pour que avconv accepte d’utiliser ce codec VP9 (et Opus) dans un conteneur WebM. Et ce patch n’est commité que dans la branche 11.x de libav (qui n’est qu’en beta pour l’instant).

J’ai essayé de backporter le patch en 10.4 : ça compile sans problèmes, et je peux générer du WebM avec le codec VP9.

Mais bref, plutôt que de demander un backport du patch sur Utopic, le plus simple serait que Utopic passe sur libav 11.x (dès qu’il sera sorti). C’est envisagé ici : https://bugs.launchpad.net/ubuntu/+source/libav/+bug/1360026

Et, d’ici là, il « suffit » de compiler libav 11.x (actuellement en beta 1) :

wget https://libav.org/releases/libav-11_beta1.tar.gz
tar xvzf libav-11_beta1.tar.gz
cd libav-11_beta1
sudo apt-get install build-essential libvpx-dev yasm libvorbis-dev libmp3lame-dev pkg-config libopus-dev
./configure --enable-libvpx --enable-libmp3lame --enable-libvorbis --enable-libopus
make -j4

Le point important à retenir pour la compilation est d’installer le package pkg-config : j’ai eu du mal à trouver que c’était ça qui me manquait pour compiler avec le support de l’opus.

Il est probablement possible d’utiliser un PPA pour installer libav 11, mais je ne voulais pas « polluer » ma machine avec : certains logiciels sur ma machine dépendent de libav et pourraient dysfonctionner avec cette version.

Utiliser Docker pour faire les conversions sous Trusty

docker

Allez, j’avoue, j’ai trouvé une excuse bidon pour essayer Docker : compiler libav sur Trusty est + simple que ce que j’explique ci-dessous…

Cela dit, cela pourrait servir plus tard :

  • quand libav sera passé en version 11 dans Utopic (ou version ultérieure), cela évitera de le compiler à la main. Edité le 06/09/2014 : c’est fait
  • et ça peut permettre de profiter des dernières améliorations/corrections des codecs vpx et opus (qui sont a priori dépendants de la version d’Ubuntu)

Mais bref, avec Docker, l’idée ici est de pouvoir utiliser les librairies d’un autre OS sans polluer l’hôte. Au lieu de lancer une machine virtuelle complète, on utilise un conteneur léger (Docker) à la place.

NB : ne pas confondre la notion de conteneur Docker et de conteneur audio/video comme le WebM : ce sont deux choses complètement différentes.

Configuration avec Docker

D’abord installer Docker :

sudo apt-get install docker.io
sudo ln -sf /usr/bin/docker.io /usr/bin/docker

Puis créer le conteneur et se connecter dedans :

sudo docker pull ubuntu:utopic
sudo docker run -i -t ubuntu:utopic /bin/bash

Puis y installer libav (quand il sera en version 11, sinon on peut le compiler avec la procédure ci-dessus) :

sudo apt-get update
sudo apt-get install libav-tools opus-tools

Dans un autre terminal, enregistrer l’état du conteneur, pour pouvoir s’en re-servir par la suite :

sudo docker ps -a
sudo docker commit xxxxxxx utopic-libav11

(en remplaçant xxxxxx par l’id du conteneur que vous venez de lancer, et qui s’est affiché avec la commande « ps » du dessus)

Script de lancement

Se faire un petit script shell « convertir_webm_docker.sh » qui lance la conversion dans le conteneur Docker :

# Nécessite le package realpath
FULLPATH=`realpath "$1"`
# remove all the prefix until "/" character
FILENAME=${FULLPATH##*/}
# remove all the prefix until "." character
FILEEXTENSION=${FILENAME##*.}
# remove a suffix, in our case, the filename, this will return the name of the directory that contains this file
BASEDIRECTORY=${FULLPATH%$FILENAME}
# File name without extension
BASEFILENAME=`echo ${FILENAME%.*}`
sudo docker run -i -t -v "$BASEDIRECTORY":/host utopic-libav11 avconv -y -i "/host/$FILENAME" -vf scale=-1:724 -threads auto -b:v 1000k $2 "/host/$BASEFILENAME-720p-1Mbps.webm"
sudo chown mosssroy:mossroy "$BASEDIRECTORY/$BASEFILENAME-720p-1Mbps.webm"
sleep 2
# Un peu bourrin : ça fonctionne tant qu'on n'utilise pas docker pour autre chose! Il y a probablement une meilleure manière de faire (suggestions bienvenues)
sudo docker stop $(sudo docker ps -a -q --no-trunc)
sudo docker rm $(sudo docker ps -a -q --no-trunc)

Il faut installer le package realpath :

sudo apt-get install realpath

Et lancer la conversion comme suit :

convertir_webm_docker.sh fichier_source parametres_eventuels

Ca démarre en un clin d’œil, et convertit aussi vite qu’en-dehors de Docker (en tous cas, je n’ai pas vu la différence).

Lancement du script par clic-droit dans Nautilus

Pour plus de facilité d’utilisation, j’ai configuré ce script pour qu’il soit accessible en clic-droit depuis Nautilus (le navigateur de fichiers d’Ubuntu) :

Créer un fichier « convertir_en_webm.desktop » dans le répertoire ~/.local/share/applications avec le contenu suivant :

[Desktop Entry]
Categories=
Comment=Convertir en WebM
Comment[fr]=Convertir en WebM
Encoding=UTF-8
Exec=/bin/sh /home/mossroy/convertir_webm_docker.sh %U
GenericName=Convertir en WebM
GenericName[fr]=Convertir en WebM
Icon=
MimeType=video/quicktime;video/mp4;video/mpeg
Name=Convertir en WebM
Name[fr]=Convertir en WebM
Path=/home/mossroy
ServiceTypes=
SwallowExec=
SwallowTitle=
Terminal=true
TerminalOptions=
Type=Application
URL=
Name[fr_FR]=Convertir en WebM

Et ajouter à la fin du fichier ~/.local/share/applications/mimeapps.list les lignes suivantes pour l’associer aux types MIMEs souhaités :

video/mpeg=convertir_en_webm.desktop;
video/mp4=convertir_en_webm.desktop;
video/quicktime=convertir_en_webm.desktop;

Ensuite, dans Nautilus, un « clic-droit->Ouvrir avec » sur un fichier vidéo d’un de ces types MIME devrait proposer de le convertir. Et le fichier webm devrait apparaitre dans le même répertoire, à la fin de la conversion.

Exemples de vidéos générées

Plutôt que de vous mettre mes vidéos de vacances, je suis parti de la vidéo libre « Tears of Steel », en 4K H.264 (6.7 Go, bitrate de 73Mbps). J’ai d’abord pris un extrait de 20s avec une scène calme suivie de scènes d’action :

avconv -i tearsofsteel_4k.mov -c:v copy -c:a copy -ss 00:07:40 -t 00:00:20 extrait_tears_of_steel_4k.mov

Je sais, ce n’est pas très juste de partir d’une vidéo déjà compressée pour comparer des résultats de compression. Cela introduit un biais dû à la compression initiale. Pour limiter ce biais, je suis parti d’une vidéo d’excellente qualité (3840×1714 à 73 Mbps), dont je réduis fortement la définition et le bitrate (1622×724 à 1Mbps).

Pourquoi un bitrate de 1Mbps? C’est l’upload maximum d’une ligne ADSL standard, et ça permet d’avoir des fichiers de taille raisonnable. J’ai trouvé que c’était un bon compromis pour de l’auto-hébergement.

Voici le résultat (les moins bons au début, les meilleurs à la fin). Mettez les vidéos en plein écran pour vous faire une idée.

VP8+Vorbis

./avconv -i extrait_tears_of_steel_4k.mov -c:v libvpx -c:a libvorbis -vf scale=-1:724 -threads auto -b:v 1000k extrait_tears_of_steel_720p_vp8_vorbis_1Mbps.webm

Conversion en 1min 58s, qui génère un fichier de 3.3Mo :

VP8+Vorbis encodé en 2 passes

./avconv -i extrait_tears_of_steel_4k.mov -c:v libvpx -c:a libvorbis -vf scale=-1:724 -threads auto -b:v 1000k -pass 1 -passlogfile logfile_vp8.fpf extrait_tears_of_steel_720p_vp8_vorbis_1Mbps_2passes.webm
./avconv -i extrait_tears_of_steel_4k.mov -c:v libvpx -c:a libvorbis -vf scale=-1:724 -threads auto -b:v 1000k -pass 2 -y -passlogfile logfile_vp8.fpf extrait_tears_of_steel_720p_vp8_vorbis_1Mbps_2passes.webm

Conversion en 1min 58s, qui génère un fichier de 2.7Mo :

VP9+Opus

./avconv -i extrait_tears_of_steel_4k.mov -c:v libvpx-vp9 -c:a libopus -vf scale=-1:724 -threads auto -b:v 1000k extrait_tears_of_steel_720p_vp9_opus_1Mbps.webm

Conversion en 6min 24s, qui génère un fichier de 2.7Mo :

VP9+Opus encodé en 2 passes

./avconv -i extrait_tears_of_steel_4k.mov -c:v libvpx-vp9 -c:a libopus -vf scale=-1:724 -threads auto -b:v 1000k -pass 1 -passlogfile logfile_vp9.fpf extrait_tears_of_steel_720p_vp9_opus_1Mbps_2passes.webm
./avconv -i extrait_tears_of_steel_4k.mov -c:v libvpx-vp9 -c:a libopus -vf scale=-1:724 -threads auto -b:v 1000k -pass 2 -y -passlogfile logfile_vp9.fpf extrait_tears_of_steel_720p_vp9_opus_1Mbps_2passes.webm

Conversion en 6min 52s , qui génère un fichier de 2.8Mo :

La différence de qualité me parait très nette. Le VP9 vaut vraiment le coup par rapport au VP8.

L’encodage en 2 passes améliore encore sensiblement la qualité. Ca donne un résultat vraiment correct pour du 720p sur seulement 1 Mbps.

NB : il me reste un souci avec l’encodage en 2 passes : quand j’utilise des vidéos issues de mon appareil photo, le bitrate que je demande n’est pas respecté : https://bugzilla.libav.org/show_bug.cgi?id=731. Edité le 05/10/2014 : le bug est maintenant identifié. Un workaround consiste à fixer le framerate avec le paramètre « -r »

Performances en encodage/décodage

Premier constat : l’encodage est très consommateur en CPU. 3 fois plus que pour le VP8 dans l’exemple ci-dessus (mais le décodage de la source en 4K biaise ce calcul : avec une source en « seulement » 1080p, c’est plutôt 4 à 5 fois plus long que le VP8).

Ce n’est pas une surprise mais je ne m’attendais pas à une différence si importante. D’autre part, dans la version 11 beta1 de libav, l’encodage n’exploite pas tous les coeurs/processeurs de la machine (pas de multi-thread). Il me semble que c’était le cas dans d’autres versions, donc ça sera peut-être activé dans la version finale. Edité le 05/10/2014 : cela dépend de la version de libvpx, ce n’est pas encore implémenté dans la version actuelle (1.3.0), mais cela semble en cours de développement dans une branche dédiée.

Deuxième constat : le décodage est également + consommateur, mais dans une moindre mesure. Très grossièrement, à bitrate égal, il faut environ 50% de CPU en plus pour le décodage VP9 :

  • VP9+Opus sous Firefox = 60 à 70% du CPU d’un coeur d’un Core i5 3450S
  • VP8+Vorbis sous Firefox = 40 à 50% du CPU
  • VP9+Opus sous Totem = 26% du CPU (avec piste Opus non reconnue)
  • VP8+Vorbis sous Totem  = 18% du CPU

Troisième constat : Firefox consomme 2 fois plus de CPU que Totem pour décoder. Idem pour Chromium (mais dans une moindre mesure). J’ai rapporté le problème chez Mozilla. Edité le 05/10/2014 : apparemment cela dépend des plateformes matérielles. Sur d’autres machines, il n’y a pas de différence notable. Cela ne serait pas dû au décodage vidéo, mais à la composition des fenêtres de Firefox, qui ne serait pas accélérée matériellement.

Sur mon vieux netbook (Atom N570 double coeur), ces différences se voient : les vidéos en 720p 1Mbps se lisent très bien sous Totem. Sous Firefox (ou Chromium), le VP8 se lit avec quelques saccades, alors que le VP9 se bloque au bout de quelques frames.

Support logiciel en décodage

Le codec VP9 est bien supporté sous Trusty : en décodage par Gstreamer (le backend de Totem), Firefox et Chromium, mais pas encore par VLC (v2.1.4), ni par XBMC/Kodi (v13).

Le codec Opus n’est pas encore très bien supporté, en tous cas dans un conteneur WebM, et dans les versions fournies actuellement avec Trusty :

  • GStreamer ne semble pas le reconnaître du tout dans un conteneur WebM (il le prend pour du « audio/x-unknown »), alors qu’il le reconnaît s’il est seul dans un conteneur Ogg (testé avec le fichier ehren-paper_lights-96.opus). Idem pour VLC (il le prend pour du « undf » dans le WebM). Il est possible que cela vienne d’un bug dans libav lui-même. Cela dit, mkvinfo reconnaît bien la piste Opus, et j’ai le même comportement avec une autre vidéo VP9/Opus.
  • Aucun problème avec Firefox, sur les cas que j’ai testés
  • Un petit piège sur Chromium/Chrome : ils refusent apparemment les largeur/hauteur impairs (ou peut-être qui ne sont pas multiples de 4?). Donc il faut parfois changer un peu les dimensions pour tomber sur des valeurs compatibles : c’est pour cette raison que j’ai mis le paramètre « -vf scale=-1:724 » au lieu de « -vf scale=-1:720 » (qui me donnait une largeur impair sur cette vidéo : à adapter pour chaque vidéo)

Mes conclusions personnelles :

  • Le codec VP9 a de l’avenir!
  • Vivement l’accélération matérielle (surtout pour les périphériques mobiles) et/ou des processeurs plus puissants et/ou des optimisations dans l’implémentation (notamment le support du multi-thread)
  • Si votre matériel est suffisamment puissant (pour l’encodage et pour le décodage), et que vous ne diffusez les vidéos que via un navigateur, vous pouvez y aller : Firefox et Chromium/Chrome sont prêts (mais bien sûr pas IE ni Safari, qui boudent et préfèrent les codecs non libres). Côté mobile, c’est supporté par Firefox OS>=1.3 (décodage logiciel donc il faut du matériel puissant), je n’ai pas regardé pour les autres OS : cela prendra probablement un peu de temps pour que cela fonctionne (compatibilité logicielle et matérielle). Edité le 05/10/2014 : apparemment ça fonctionne avec Chrome sur Android (au moins sur les Nexus 4)
  • Si vous voulez que les vidéos soient facilement lisibles sur Trusty (en-dehors du navigateur), mieux vaut garder le codec audio Vorbis pour l’instant. Cela dit, il est probable que ce problème de compatibilité soit corrigé bientôt
  • Si vous restez en VP8, et avec un bitrate de 1 Mbps, le 720p me parait bien trop pixelisé. Il vaut mieux choisir une définition plus faible que le 720p. J’utilise du 480p, voire 360p.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *