GStreamer et le codec X.264

Date: 10/06/2009 | Catégories: Gstreamer,Image,Open-source,Systeme | Tags: ,,,,,

Souhaitant effectuer un streaming RTP à partir d'une Webcam, voici un billet sur l'optimisation des paramètres du codec libre X.264 (implémentation libre du codec H.264 ou MPEG-4 AVC - WIKI) dont l'équipe de VideoLAN est à l'origine. Pour tester ce codec et ensuite mettre en place le streaming, j'utiliserai le framework GStreamer.

A la recherche des "bons" paramètres

On commence par  créer un fichier vidéo de référence. Ce dernier est à adapter à votre besoin. Pour mes tests, j'ai enregistré une séquence d'une dizaine de secondes devant ma Webcam:

gst-launch v4l2src device="/dev/video1" ! queue ! videoscale method=1 ! video/x-raw-yuv,width=320,height=240 ! avimux ! filesink location=webcam.avi

Ensuite on génère un fichier encodé en H.264 (c'est à dire sans paramètre, avec un encodage à 2 Mbps). Il servira de référence pour la comparaison qualitative:

gst-launch filesrc location=webcam.avi ! decodebin ! queue ! ffmpegcolorspace ! x264enc ! avimux ! filesink location=webcam-x264-10.avi

On peut comparer les deux:


Original vs X.264 (defaut)

On va maintenant jouer avec les paramètres disponibles pour le codec X.264. La liste de ces paramètres est longue et le paramétrage très pointu. Je vous conseille la lecture de ce document et de ce forum.

Nous allons dans un premier temps fixer le débit de compression à 200 Kbps (paramètre bitrate) puis mettre des valeurs moyennes au niveau des paramètres ref et subme. Cela devrait assurer un bon compromis entre vitesse de compression et qualité d'image.

gst-launch filesrc location=webcam.avi ! decodebin ! queue ! x264enc byte-stream=true bitrate=200 ! gst-launch filesrc location=webcam.avi ! decodebin ! queue ! ffmpegcolorspace ! x264enc byte-stream=true bitrate=200 bframes=4 ref=4 me=hex subme=4 weightb=true threads=0 ! avimux ! filesink location=webcam-x264-11.avi

On compare le résultat:


X.264 (defaut) vs X.264 test 11
(
byte-stream=true bitrate=200 bframes=4 ref=4 me=hex subme=4 weightb=true threads=0)

Comme on peut le voir la qualité est moins bonne (surtout quand il y a des mouvements) mais l'on passe d'un débit de 2089 Kbps à 203 Kbps (soit un facteur 10).

En choisissant des options qui permettent une compression plus rapide (ce qui peut être utile pour une utilisation interactive de type videoconference), on perd encore en qualité:

gst-launch filesrc location=webcam.avi ! decodebin ! queue ! ffmpegcolorspace ! x264enc byte-stream=true bitrate=200 bframes=4 ref=1 me=dia subme=1 weightb=true threads=0 ! avimux ! filesink location=webcam-x264-12.avi

On compare le résultat par rapport aux paramètres précédant:

X.264 test 11 vs X.264 test 12
(
byte-stream=true bitrate=200 bframes=4 ref=1 me=dia subme=1 weightb=true threads=0)

La consommation de bande passante entre les deux tests est la même (203 Kbps). Seul le temps d'encodage diffère (gain de 25%).

Enfin on paramètre X.264 pour améliorer la qualité (donc au détriment du temps d'encodage):

gst-launch filesrc location=webcam.avi ! decodebin ! queue ! ffmpegcolorspace ! x264enc byte-stream=true bitrate=200 bframes=4 ref=8 me=umh subme=6 weightb=true threads=0 ! avimux ! filesink location=webcam-x264-13.avi

On compare le résultat par rapport aux paramètres du test 11:

X.264 test 11 vs X.264 test 13
(byte-stream=true bitrate=200 bframes=4 ref=8 me=umh subme=6 weightb=true threads=0)

La qualité est légèrement meilleure (difficile à voir sur cette image), le débit identique, par contre le délais d'encodage bondit de plus de 100%.

Pour résumé, les paramètres suivant sont ceux qui offre le meilleur résultat pour mon besoin:

byte-stream=true bitrate=200 bframes=4 ref=4 me=hex subme=4 weightb=true threads=0

Ratio qualité/bande passante

Maintenant que nous avons identifié les "bons" paramètres pour notre besoin. Il faut jouer sur les paramètres externes pour trouver le bon ratio entre la qualité de l'image et la bande passante consommé lors du streaming de notre Webcam.

Les paramètres sur lesquels on a des leviers sont les suivants:

  • taille de l'image (résolution)
  • nombre d'images par seconde (fps)
  • débit cible au niveau du codec X.264 (bitrate)

Comme je n'aime pas faire des choses répétitives (non non, ce n'est pas de la fainéantise...),  j'ai développé un petit script shell qui prend en entrée un fichier vidéo de référence, une liste de résolution (au format GStreamer), une liste de fps et une liste de débit. Le script va générer automatiquement les vidéos encodés avec ces différents paramètres.

Voici le script en question:

#!/bin/sh

# Je génére le fichier de référence avec la commande:
# sudo gst-launch v4l2src device="/dev/video1" ! queue ! videoscale method=1 ! video/x-raw-yuv,width=704,height=576 ! avimux ! filesink location=videotest.avi

videosrc="./videotest.avi"
reslist="width=704,height=576 width=352,height=288 width=176,height=144 width=128,height=96"
fpslist="24 12 6 3"
bitratelist="400 300 200 100 50"

for res in `echo $reslist`
do
 resname=`echo $res | sed "s/width=//" | sed "s/height=//" | sed "s/,/x/"`
 for fps in `echo $fpslist`
 do
  for bitrate in `echo $bitratelist`
  do
   echo videotest-${resname}-${fps}fps-${bitrate}kbps.avi
   gst-launch filesrc location=$videosrc ! decodebin ! ffmpegcolorspace ! queue ! cairotextoverlay text="${resname} ${fps}fps ${bitrate}kbps" shaded-background=true ! queue ! videorate ! video/x-raw-yuv,framerate=$fps/1 ! queue ! videoscale method=1 !  video/x-raw-yuv,$res ! queue ! ffmpegcolorspace ! x264enc byte-stream=true bitrate=$bitrate bframes=4 ref=4 me=hex subme=4 weightb=true threads=0 ! avimux ! filesink location=videotest-${resname}-${bitrate}kbps-${fps}fps.avi
  done
 done
done

J'obtient les résultats suivants:

videotest-704x576-50kbps-6fps.avi donne un débit moyen de 41 kb/s
videotest-352x288-50kbps-3fps.avi donne un débit moyen de 42 kb/s
videotest-352x288-50kbps-6fps.avi donne un débit moyen de 42 kb/s
videotest-704x576-50kbps-3fps.avi donne un débit moyen de 43 kb/s
videotest-176x144-50kbps-3fps.avi donne un débit moyen de 44 kb/s
videotest-128x96-50kbps-3fps.avi donne un débit moyen de 45 kb/s
videotest-176x144-50kbps-6fps.avi donne un débit moyen de 46 kb/s
videotest-128x96-50kbps-6fps.avi donne un débit moyen de 47 kb/s
videotest-352x288-50kbps-12fps.avi donne un débit moyen de 47 kb/s
videotest-704x576-50kbps-12fps.avi donne un débit moyen de 47 kb/s
videotest-176x144-50kbps-12fps.avi donne un débit moyen de 51 kb/s
videotest-128x96-50kbps-12fps.avi donne un débit moyen de 52 kb/s
videotest-352x288-50kbps-24fps.avi donne un débit moyen de 53 kb/s
videotest-704x576-50kbps-24fps.avi donne un débit moyen de 53 kb/s
videotest-176x144-50kbps-24fps.avi donne un débit moyen de 54 kb/s
videotest-128x96-50kbps-24fps.avi donne un débit moyen de 56 kb/s
videotest-128x96-100kbps-3fps.avi donne un débit moyen de 73 kb/s
videotest-704x576-100kbps-6fps.avi donne un débit moyen de 76 kb/s
videotest-352x288-100kbps-3fps.avi donne un débit moyen de 81 kb/s
videotest-352x288-100kbps-6fps.avi donne un débit moyen de 81 kb/s
videotest-704x576-100kbps-3fps.avi donne un débit moyen de 84 kb/s
videotest-176x144-100kbps-3fps.avi donne un débit moyen de 86 kb/s
videotest-128x96-200kbps-3fps.avi donne un débit moyen de 87 kb/s
videotest-128x96-300kbps-3fps.avi donne un débit moyen de 87 kb/s
videotest-128x96-400kbps-3fps.avi donne un débit moyen de 87 kb/s
videotest-128x96-100kbps-6fps.avi donne un débit moyen de 88 kb/s
videotest-704x576-100kbps-12fps.avi donne un débit moyen de 89 kb/s
videotest-352x288-100kbps-12fps.avi donne un débit moyen de 91 kb/s
videotest-704x576-100kbps-24fps.avi donne un débit moyen de 93 kb/s
videotest-176x144-100kbps-6fps.avi donne un débit moyen de 94 kb/s
videotest-176x144-100kbps-12fps.avi donne un débit moyen de 99 kb/s
videotest-352x288-100kbps-24fps.avi donne un débit moyen de 99 kb/s
videotest-128x96-100kbps-12fps.avi donne un débit moyen de 101 kb/s
videotest-176x144-100kbps-24fps.avi donne un débit moyen de 103 kb/s
videotest-128x96-100kbps-24fps.avi donne un débit moyen de 104 kb/s
videotest-128x96-200kbps-6fps.avi donne un débit moyen de 125 kb/s
videotest-128x96-300kbps-6fps.avi donne un débit moyen de 130 kb/s
videotest-128x96-400kbps-6fps.avi donne un débit moyen de 131 kb/s
videotest-176x144-200kbps-3fps.avi donne un débit moyen de 136 kb/s
videotest-704x576-200kbps-6fps.avi donne un débit moyen de 150 kb/s
videotest-176x144-300kbps-3fps.avi donne un débit moyen de 151 kb/s
videotest-176x144-400kbps-3fps.avi donne un débit moyen de 152 kb/s
videotest-352x288-200kbps-6fps.avi donne un débit moyen de 161 kb/s
videotest-352x288-200kbps-3fps.avi donne un débit moyen de 162 kb/s
videotest-704x576-200kbps-3fps.avi donne un débit moyen de 165 kb/s
videotest-176x144-200kbps-6fps.avi donne un débit moyen de 170 kb/s
videotest-128x96-200kbps-12fps.avi donne un débit moyen de 174 kb/s
videotest-352x288-200kbps-12fps.avi donne un débit moyen de 174 kb/s
videotest-704x576-200kbps-12fps.avi donne un débit moyen de 174 kb/s
videotest-704x576-200kbps-24fps.avi donne un débit moyen de 187 kb/s
videotest-128x96-300kbps-12fps.avi donne un débit moyen de 188 kb/s
videotest-128x96-400kbps-12fps.avi donne un débit moyen de 188 kb/s
videotest-128x96-200kbps-24fps.avi donne un débit moyen de 191 kb/s
videotest-352x288-200kbps-24fps.avi donne un débit moyen de 192 kb/s
videotest-176x144-200kbps-12fps.avi donne un débit moyen de 193 kb/s
videotest-176x144-200kbps-24fps.avi donne un débit moyen de 199 kb/s
videotest-176x144-300kbps-6fps.avi donne un débit moyen de 202 kb/s
videotest-128x96-300kbps-24fps.avi donne un débit moyen de 211 kb/s
videotest-128x96-400kbps-24fps.avi donne un débit moyen de 215 kb/s
videotest-176x144-400kbps-6fps.avi donne un débit moyen de 216 kb/s
videotest-704x576-300kbps-6fps.avi donne un débit moyen de 220 kb/s
videotest-352x288-300kbps-3fps.avi donne un débit moyen de 235 kb/s
videotest-704x576-300kbps-3fps.avi donne un débit moyen de 245 kb/s
videotest-352x288-300kbps-6fps.avi donne un débit moyen de 249 kb/s
videotest-704x576-300kbps-12fps.avi donne un débit moyen de 262 kb/s
videotest-352x288-300kbps-12fps.avi donne un débit moyen de 265 kb/s
videotest-176x144-300kbps-12fps.avi donne un débit moyen de 272 kb/s
videotest-704x576-300kbps-24fps.avi donne un débit moyen de 278 kb/s
videotest-176x144-300kbps-24fps.avi donne un débit moyen de 289 kb/s
videotest-352x288-300kbps-24fps.avi donne un débit moyen de 293 kb/s
videotest-704x576-400kbps-6fps.avi donne un débit moyen de 297 kb/s
videotest-352x288-400kbps-3fps.avi donne un débit moyen de 298 kb/s
videotest-176x144-400kbps-12fps.avi donne un débit moyen de 301 kb/s
videotest-704x576-400kbps-3fps.avi donne un débit moyen de 318 kb/s
videotest-352x288-400kbps-6fps.avi donne un débit moyen de 336 kb/s
videotest-176x144-400kbps-24fps.avi donne un débit moyen de 339 kb/s
videotest-704x576-400kbps-12fps.avi donne un débit moyen de 346 kb/s
videotest-352x288-400kbps-12fps.avi donne un débit moyen de 355 kb/s
videotest-704x576-400kbps-24fps.avi donne un débit moyen de 373 kb/s
videotest-352x288-400kbps-24fps.avi donne un débit moyen de 385 kb/s

Si on se focalise sur un nombre d'images par seconde de 12 et une résolution CIF (ce qui est suffisant pour une besoin de type vidéoconférence), on obtient les qualités suivantes:


Dévit cible: 50 Kbps / Débit mesuré: 47 Kbps


Dévit cible: 100 Kbps / Débit mesuré: 91 Kbps


Dévit cible: 200 Kbps / Débit mesuré: 174 Kbps


Dévit cible: 300 Kbps / Débit mesuré: 265 Kbps


Dévit cible: 400 Kbps / Débit mesuré: 355 Kbps

On peut voir que la qualité augmente moins à partir d'un débit cible de 200 Kbps.

On teste enfin le streaming

Pas la peine de réinventer la roue, j'avais déjà écrit un billet sur le sujet. Si l'on souhaite faire un streaming RTP entre deux machines en prenant en compte l'optimisation X.264 décrite dans les chapitres précédant, il faut saisir les commandes suivantes:

Sur le serveur (192.168.1.1):

gst-launch -v  gstrtpbin name=rtpbin \
v4l2src \
! queue ! videoscale method=1 ! video/x-raw-yuv,width=352,height=288 \
! queue ! videorate ! video/x-raw-yuv,framerate=\(fraction\)12/1 \
! queue ! x264enc byte-stream=true bitrate=200 bframes=4 ref=4 me=hex subme=4 weightb=true threads=0 ! rtph264pay \
! rtpbin.send_rtp_sink_0 \
rtpbin.send_rtp_src_0 ! udpsink port=5000 host=192.168.1.2 \
rtpbin.send_rtcp_src_0 ! udpsink port=5001 host=192.168.1.2 sync=false async=false \
udpsrc port=5002 ! rtpbin.recv_rtcp_sink_0

Puis sur le client (192.168.1.2):

gst-launch -v gstrtpbin name=rtpbin latency=200 \
udpsrc caps="application/x-rtp, media=(string)video,
clock-rate=(int)90000, encoding-name=(string)H264, payload=(int)96" port=5000 \
! rtpbin.recv_rtp_sink_0 \
rtpbin. ! rtph264depay ! ffdec_h264 ! xvimagesink \
udpsrc port=5001 ! rtpbin.recv_rtcp_sink_0 \
rtpbin.send_rtcp_src_0 ! udpsink port=5002 host=192.168.0.1 sync=false async=false

Conclusion

Comme toujours dans ce genre d'exercice, le paramétrage des codecs vidéos dépend de votre source (on n'encode pas de la même manière le flux CIF venant d'une Webcam ou un fichier vidéo HD...). On a ici trouvé un bon compromis entre qualité d'affichage et bande passante consommée sur le réseau. Etant loin d'être un spécialiste sur le sujet, il y a surement des optimisations à faire. Les commentaires sont fait pour ça !