projects:opengl_framebuffer_capture printable version

Mémo: Capture d'un framebuffer OpenGL, mise en place d'un flux vidéo et diffusion en streaming avec VLC

2007-11-05 : premier test de capture du framebuffer et écriture des pixels dans un device v4l

Choix de la scène OpenGL pour les tests

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 ?

Notes diverses

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.

Remarques & liens

  • glxgears est un portage de gears en utilisant GLX, mais ce portage n'affiche pas les fps par défault:
    $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/

  • http://www.geuz.org/gl2ps/ est un projet dont les sources peuvent être intéressantes, GL2PS qui permet de récupérer un contexte openGL pour l'envoyer à une imprimante. Export en EPS, PS, PDF et SVG. Cette librairie a été intégrée dans VTK. D'après la doc, GL2PS fonctionne en capturant le feedback buffer.
  • Mesa n'est pas spécifique à Linux, il peut être installé sous Windows.

Vloopback

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.

Code de v4lglxgears-0.1

v4lglxgears-0.1.tar.gz

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:

  1. faire les includes nécessaires (notamment #include <linux/videodev.h>)
  2. initialiser les variables nécessaires à l'écriture dans l'entrée du pipe
  3. écrire les pixels du buffer image avec par 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)

Première version du code de test

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.

Optimisations envisageables et évolutions possibles pour une version 0.2

Évolutions possibles

Pour optimiser au niveau de l'obtention des pixels

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.

Pour optimiser au niveau de l'écriture dans l'entrée du pipe
  • Utiliser un segment de mémoire partagée (shm, shared memory segment) pour la copie du buffer image, et un second processus dont le travail est de lire le segment de mémoire partagée et d'écrire dans l'entrée du pipe vloopback. Ainsi, le code de la scène OpenGL n'a qu'a obtenir les pixels et les stocker dans le segment de mémoire partagée, c'est un autre processus qui écrit dans l'entrée du pipe. Avec une synchronisation éventuellement par sémaphore, pour la gestion des accès concurrent sur le shm.
  • Utiliser un thread qui recopie dans l'entrée du pipe la variable globale qui contient la copie du buffer image.

Captures écran

capture écran, une instance de VLC en lecture sur le device

capture écran, plusieurs instances de VLC en lecture sur le device

2007-14-05 : v4lglxgears-0.2

notes d'update entre la v0.1 et v0.2

Problèmes observés dans la version 0.1:

  1. La capture des pixels ne fonctionne pas comme je pensais!
    Les pixels capturés sont ceux de la fenêtre au niveau écran, une image valant mieux qu'un long discours:
    Cela doit venir de la fonction OpenGL utilisée, il doit avoir d'autres fonctions OpenGL pour obtenir le résultat de la projection de la scène 3d pour l'affichage.
  2. la chute du nombre de fps lorsque:
    1. on récupère les pixels
    2. un VLC lit dans la sortie du pipe vloopback.
  3. nécessité d'avoir un vrai device pour obtenir des infos de flux vidéo qui soient correct.
  4. il y a des lags au niveau de l'affichage de la scène dans VLC (probablement dû à une absence d'encodage).
  5. VLC n'arrive pas à constuire un flux de streaming (également dû une absence d'encodage).

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.

Lancer VLC avec argument en console, le script vlc_stream.sh

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:

Evolutions possibles

  • utiliser un thread pour la copie dans l'entrée du pipe, et une variable globale, cela pourrait relever un peu le fps
  • trouver quelle fonction utilser pour capturer réellement le buffer image d'OpenGL issu de la projection de la scène 3d
  • existe t-il une option de VLC pour lancer une création de streaming en ligne de commande (sans aucune fenêtre, par exemple lorsque qu'un VLC est compilé sans wxwidget…)

2007-15-05 : v4lglxgears-0.3

VLC en mode terminal

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

  • sans argument : pour lancer VLC sans affichage du flux dans la fenêtre
  • avec l'argument -withdisp : pour lancer VLC avec affichage du flux dans la fenêtre
  • avec l'argument -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.

La capture des pixels du buffer image GL_BACK

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!

Relever le nombre de fps de l'application OpenGL

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.

Evolutions possibles

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:

  1. charger le module vloopback
  2. lancer l'application OpenGL
  3. lancer le processus d'écriture dans le pipe
  4. lancer le VLC serveur de streaming
  5. voir les modules d'interface de VLC, le paramètre --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).

Quelques points à propos de JVLC

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.

 
projects/opengl_framebuffer_capture.txt · Last modified: 2010/11/12 15:40 by njames

 © Nicolas James 2009-2011

 Valid XHTML 1.0 Transitional Valid CSS! DokuWiki