Faire les tests avec une application OpenGL simple: glxgears
Cette application est livrée avec l'implémentation d'OpenGL utilisé sous linux: Mesa3d, elle est souvent utilisé pour tester l'installation de Mesa ou pour savoir si le DRI est activé sur une machine (en regardant le fps indiqué par glxgears par la commande $glxgears -printfps).
Les sources de glxgears sont sur sourceforge, télécharger l'archive MesaDemos: http://sourceforge.net/project/showfiles.php?group_id=3.
Les sources se trouvent dans Mesa-X/progs/xdemos/.
Personnellement, je n'arrive pas à comprendre comment compiler toutes les sources à partir du Makefile, il fait référence à un répertoire ../configs/current qui n'existe pas.
Ce répertoire existe pourtant dans l'archive complète des source de MesaLib, mais même avec cette archive cela ne fonctionne pas, est-ce un oubli de la part des développeurs ?
fglrx est le ATI video driver (ne pas s'attendre à le trouver sur des configurations munis de NVidia!)
entity-gl = XML-based GUI builder for GTK+ with opengl binding (opengl-area), c'est intéressant ça, ça fait penser à la glade… par contre glade propose des bindings OpenGL.
GLUT = Mark Kilgard
GLUT for windows = Nate Robins
The glxgears is the re-implementation of gears by Brian Paul with GLX.GLX is used on Unix OpenGL implementation to manage interaction with the X Window System and to encode OpenGL onto the X protocol stream for remote rendering.
⇒ il y aurait-il quelque chose à voir aussi du côté de GLX ??? bien entendu, cela est plutôt bas niveau, et nécessiterait un client spécifique pour faire une visualisation distante.
DRI is the Direct Rendering Infrastructure for coordinating the linux kernel, X Window System, 3d graphics hardware and an OpenGL-based rendering engine.
$glxgears -printfps
avec éventuellement le paramètre
-display :0.0
pour afficher sur un écran distant. Sous Linux, glxgears est apporté par le paquet mesa-utils. glxgears n'est pas considéré comme un bon outil pour faire un benchmark opengl, pour cela il existe glean de Allen Akin, c'est un framework de test pour OpenGL http://glean.sourceforge.net/
Site officiel du projet, et liens pour obtenir les sources: http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice.
La compilation des sources se déroule bien sur ma Ubuntu7.4, ne pas oublier de se loguer en root, c'est un module que l'on compile!
Pour charger le module: $modprobe vloopback.
Pour voir les modules chargés: $lsmod.
Et un $lsmod|grep vloopback est du coup très utile pour savoir si notre module est chargé!
Autre point très utile, les sources de vloopback apportent quelques exemples, le plus simple est feed.
Si vous avez une webcam de branchée en /dev/video0 (voir avec $vlc v4l:/dev/video0), et que vous avez chargé vloopback après, vous avez /dev/video1 pour entrée du pipe et /dev/video2 pour sortie du pipe.
Alors, pour lancer l'exemple feed: $feed /dev/video0 /dev/video1 320x240 rgb
puis VLC peut lire la sortie du pipe: $vlc v4l:/dev/video2
Par défaut, vloopback crée deux devices dans /dev, une entrée et une sortie de pipe.A noter que pour un processus utilisant vloopback, la sortie du flux vidéo pour ce processus doit être l'entrée du pipe, et le flux vidéo en entrée pour ce processus, peut être n'importe quoi, une webcam par exemple, ou même rien du tout, le processus écrivant tout simplement sur l'entrée du pipe.
VLC lit la sortie du pipe, lancer VLC depuis une ligne de commande avec les options de debuggages est assez pratique:$vlc -vvv v4l:/dev/videoN
et remplacer N par le device v4l virtuel qu'est la sortie du pipe.
Pour le premier code de test, j'ai utilisé l'exemple feed, avec une webcam pour l'entrée du flux, et j'ai simplement modifié l'écriture des pixels dans l'entrée du pipe.
J'ai donc ajouté le code nécessaire à glxgears pour:
#include <linux/videodev.h>)glReadPixels(), mais attention au double-buffering, donc positionner le buffer avec glReadBuffer() à GL_FRONT, attention aussi au stockage des pixels avec glPixelStorei().
A noter que en OpenGL le point (0,0) est dans l'angle inférieur gauche, tandis que pour les images du flux vidéo, ce point doit être dans l'angle supérieur gauche, j'ai donc fait une transformation de la copie du buffer image, un miroir horizontal (mais ligne par ligne et avec un memcpy, ce qui est le plus rapide je pense pour une méthode naïve).
Autre point à noter à ce propos, j'ai bien essayer d'écrire les lignes en partant du bas de l'image, mais cela ne fonctionne pas!Il est obligatoire de constituer l'image miroir, puis d'écrire en un appel système write l'image.La copie ligne à ligne ne fonctionne pas, je suppose que cela a à voir avec l'implémentation de video4linux (peut être qu'un appel à write correspond à une image… à voir)
Déjà ça fonctionne, c'est déjà pas mal.Mais c'est assez lent au rendu, en chiffre:
Avec un $glxgears -printfps standard de Mesa3d j'obtient:
16808 frames in 5.0 seconds = 3361.410 FPS
et avec v4lglxgears-0.1:
⇒sans lecture du device par VLC
674 frames in 5.0 seconds = 134.618 FPS
710 frames in 5.0 seconds = 141.831 FPS
513 frames in 5.0 seconds = 102.586 FPS
⇒avec lecture du device par VLC
301 frames in 5.2 seconds = 58.252 FPS
320 frames in 5.0 seconds = 63.971 FPS
297 frames in 5.2 seconds = 57.228 FPS
324 frames in 5.0 seconds = 64.771 FPS
Et le nombre d'instance de VLC en lecture sur le device de change rien sur les performances du rendu de la scène OpenGL.
Mais, et il y a là une inconnue qu'il va falloir résoudre, quelle est la cause de cette baisse dans le rendu dès qu'un player VLC est en lecture sur le device.Peut être que le noyau met au point une sorte de synchro input/output sur le device que lorsque un processus accède en lecture sur le device (et que tant qu'il n'y en a pas, alors pas de controle de synchro).
Il peut aussi être intéressant de savoir comment vloopback gère t-il les accès concurrent sur le fichier device.
Il existe peut être dans OpenGL d'autres fonctions pour obtenir les pixels, plus bas-niveau peut être, et plus rapide.Mais on pourrait aussi surcharger une fonction OpenGL d'obtention des pixels, à la manière de http://bennyben.free.fr/projects/gla/index.html, cela pourrait être plus rapide, c'est à tester.
capture écran, une instance de VLC en lecture sur le device
capture écran, plusieurs instances de VLC en lecture sur le device
Problèmes observés dans la version 0.1:

Problèmes résolus dans la version 0.2:
⇒ se passer d'un vrai device en entrée
⇒ VLC peut créer un flux de streaming: en fait, VLC n'arrive pas à créer un flux de streaming avec les données brutes du flux vidéo issues de vloopback, ce qui est je pense normal, étant donné que ce flux est une séquence d'image assez discontinue temporellement, on le voit avec les infos de déboguage de VLC $vlc -vvv, qu'il lui manque des frames, comme si les images n'arrivaient pas assez vite, par exemple voici un extrait d'informations de debuggage de vlc:
[00000339] main video output warning: late picture skipped (1812)
[00000339] main video output warning: late picture skipped (2140)
[00000339] main video output warning: late picture skipped (29956)
[00000339] main video output warning: late picture skipped (22637)
[00000339] main video output warning: late picture skipped (15273)
[00000339] main video output warning: late picture skipped (2285)
[00000339] main video output warning: late picture skipped (-912)
[00000339] main video output warning: late picture skipped (28037)
[00000339] main video output warning: late picture skipped (20836)
Cependant en demandant à VLC de transcoder le flux (le coder en WMV1 par exemple), le streaming devient alors possible.
J'ai ajouté un script de lancement de VLC avec les options nécessaires pour cela dans les sources, vlc_stream.sh, il suffit de lancer le script et une instance de VLC est lancé avec un streaming multicast sur 224.0.0.0.
Un requis est que VLC soit compilé avec de module video4linux (il est désactivé par défaut dans le configure des sources de VLC).Pour lancer un VLC qui récupère la sortie du pipe, par exemple /dev/video1, encoder dans un format vidéo, puis dupliquer le flux pour 1. l'afficher dans la fenêtre de VLC, 2. le diffuser en streaming multicast sur le groupe 224.0.0.0:
$vlc v4l:/dev/video1 --sout '#transcode{vcodec=mp4v,vb=800,deinterlace}\:duplicate{dst=display,dst=standard{access=udp,mux=ts,dst=224.0.0.0,sap,name=“v4lglxgears”}}'
A noter: l'affichage de la fenêtre du VLC qui réalise le streaming ne fait pas que ralentir très nettement le rendu de la scène OpenGL.En effet, quand la fenêtre du VLC qui crée le stream est visible sur l'écran, on observe que la scène opengl n'est plus continue, comme si elle était sur un appel de fonction bloquante (qui dure moins d'1 seconde sur ma configuration matérielle).Et ceci est remarqué, que la fenêtre du VLC qui crée le stream affiche le stream (avec un duplique dans la ligne de commande de VLC) dans sa fenêtre ou non.Quand la fenêtre de VLC n'est pas affichée à l'écran, cela fonctionne, mais que si VLC est lancée pour afficher la vidéo dans sa fenêtre (même si on la voit pas… plutôt suspect…).Pour un fonctionnement correct, il faut lancer $vlc_stream.sh -disp et réduire la fenêtre de VLC.
Le nombre de fps perdue quand un VLC lit la sortie du pipe et stream est de l'ordre de 15% (toujours donc, avec la fenêtre du VLC qui créee le stream non affichée à l'écran).
Liens sur la configuration de VLC:
Il existe trois mode de VLC pour une utilisation en console, voir http://wiki.videolan.org/Console.
Le script vlc_stream.sh a été modifié: $sh vlc_stream.sh server [-withdisp|-rc]
Ce script lance un VLC qui stream la sortie du pipe sur un multicast 224.0.0.0
-withdisp : pour lancer VLC avec affichage du flux dans la fenêtre-rc : pour lancer VLC en console
Pour lancer un client VLC sur le multicast 224.0.0.0: $sh vlc_stream.sh client
Avec v4lglxgears-0.2, le problème de la discontinuité dans l'affichage de la scène 3d, lorsque la fenêtre de VLC est visible à l'écran apparait aussi.Cependant, ce problème n'apparait pas avec v4lglxgears-0.3 qui utilise un thread pour la copie du buffer image dans l'entrée du pipe.
Le buffer image GL_FRONT doit être connecté sur la mémoire écran, c'est pour cela que lors du glReadPixels(), j'obtenais comme une capture de l'espace des pixels correspondant à la position de la fenêtre sur l'écran (le rectangle écran qui correspond à la fenêtre de mon application OpenGL).En utilisant le buffer image GL_BACK, le problème est réglé.A retenir: GL_FRONT est probablement connecté directement sur la mémoire écran!
Première méthode pour reveler le fps de l'application OpenGL, utiliser un thread, qui s'occupe d'écrire une copie du buffer image sur l'entrée du pipe (et d'effectué le miroir de l'image).De cette façon, l'application OpenGL ne s'occupe que de faire un glReadPixels().On a donc un seul processus, et un fils d'exécution secondaire qui a accès au tas du processus, qui contient la valeur de l'adresse de la copie du bufferimage.Les résultats sont satisfaisant, le nombre de fps est en moyenne multiplié par 4 par rapport à v4lglxgears-0.2.De plus, le problème vis à vis du VLC qui crée le stream et de sa fenêtre n'est plus rencontré, je ne sais pas pourquoi il a disparut.
Du point de vue de la conception, le thread apporte un peu plus de clarté, il peut être vu comme une brique logicielle qui apporte les images dans le pipe, avec éventuellement un pré-traitement.Il pourrait utiliser un buffer d'image et jouer sur le débit des images dans le pipe.
Faire deux processus différent plutôt que deux thread.Utiliser un segment de mémoire partagée pour que le processus d'écriture dans l'entrée du pipe ait accès à une copie du buffer image.Ainsi, déporté l'affichage du buffer image se résumerait au protocole opératoire suivant:
--extraintf <module> de VLC qui permet de choisir un module d'affichage supplémentaire lancé en plus du module principal => intéressant pour les interfaces de contrôle (pour faire du dialogue client/serveur).
Pour faire une interface de manipulation sur l'objet rendu 3d distant comme étant un module d'interface VLC (qui échange donc des données avec un serveur qui lui est annexe de l'application de rendu), on peut utiliser une interface module de VLC (avec --extraintf <module>. Mais on pourrait aussi inclure VLC dans un code Java avec JVLC.
JVLC est un bindings java pour VLC.A priori, il devrait bien fonctionner (il existe apparemment tout un tas de projet qui utilise JVLC).
Les bindings sont disponibles dans les sources de VLC, il suffit de recompiler VLC. Cependant, cela ne compile que les bindings, il manque les libraries libjvlc.so et jvlc.so.Ces dites libraries doivent se trouvées dans les packages disponibles dans la section download du site de JVLC.
Cependant, sur la page du projet http://trac.videolan.org/jvlc/wiki, la doc est plutôt succinte, et depuis la page de download http://trac.videolan.org/jvlc/wiki/download les binaires proposés ne sont que windows ou linux_x86_64.
J'ai bien trouvé un binaire pour linux içi http://jvlc.ihack.it/releases/, mais il est assez vieux maintenant (august 2006).
Que ce soit sous windows ou sous linux, je n'arrive pas à lancer un des 2 exemples qui se trouve dans les sources de VLC (dans le répertoire ./bindings).
Ne pas oublier -Djava.library.path=path_to_jvlc.lib, voir http://forum.videolan.org/viewtopic.php?p=112500&sid=fa77799f48cc43ddf6f7b3c2fc887a7c.