Articles Tagués ‘X11’

h1

Quoi de neuf sur les libs

2010.01.03

Hello,

maintenant qu’Enna 0.4.0 est dehors et que les paquets sont disponibles, cet article me donne l’occasion de faire un peu la synthèse des modifications apportées à libvalhalla et à libplayer depuis les versions 1.0.0. A noter que la première version d’Enna concerne uniquement les versions 1.0.0, et en aucun cas ce que je liste ci-dessous.

libplayer

Concernant libplayer il n’y a pas grand chose de neuf. La plupart des patchs se rapportent à des fix mineurs dans les scripts et Makefiles. Ainsi que le support (en théorie) de Darwin. Je n’ai pas la moindre idée si la compilation pour MacOS X fonctionne car je n’ai jamais eu de Mac à ma disposition. A part une image émulée d’une Tiger qui n’est pas spécialement rapide à l’utilisation.

Mise à part ça, la modification la plus importante est le remplacement de Xlib par XCB. Xlib a toujours été un problème néanmoins une astuce permettait de le rendre utilisable dans le contexte de libplayer. X11 dans libplayer a deux raisons d’être. Tout d’abord c’est un bon moyen pour créer une fenêtre X pour MPlayer. Cette fenêtre étant entièrement contrôlée par libplayer, cela évite d’avoir des problèmes avec les événements X11 et il est facile de l’embarquer dans Enna. Ensuite cette fenêtre est indispensable pour xine-lib.

Le problème d’Xlib intervient au niveau des threads. Afin de garantir d’être thread-safe il est nécessaire d’appeler la fonction XInitThreads() avant n’importe quel autres fonctions d’Xlib. Ou alors, il faut gérer les locks sur les appels Xlib sois-même. J’ai donc opté pour la deuxième solution (dans le cas de libplayer-1.0.0). Des mécanismes dans xine-lib permettent d’appeler les locks créés par libplayer. Mais il y a un point à soulever. Selon les dires des développeurs de xine, ces mécanismes ne sont pas exempt de bugs.
Et l’utilisation d’XInitThreads() est impossible dans le cas de libplayer. La raison est très simple. Étant donné que ce doit être la première fonction à appeler, il faudrait être sûr que par exemple avec Evas, Qt ou GTK (si libplayer serait utilisé dans une application qui en dépend tel qu’Enna avec Evas), que libplayer soit initialisé avant ces libs. Ou alors qu’Evas fasse lui même un XInitThreads(). Ou encore que celui qui développe le GUI (tel qu’Enna) fasse un XInitThreads() dans son main() (avant toutes les autres initialisations). Et donc l’utilisation d’une telle bibliothèque deviendrait très contraignante.

La solution c’est XCB. Il n’y a aucun besoin d’initialiser quoi que se sois pour les threads contrairement à Xlib. XCB peut donc être utilisé de manière sûre dans libplayer et sans fournir de locks supplémentaires. C’est sur XCB que les développeurs de xine se tournent également. Car il y a au moins les sorties video Xshm et Xv qui sont portées pour XCB.

La prochaine release de libplayer utilisera donc XCB. Pour être plus précis, le fait d’utiliser XCB dans libplayer n’empêche pas d’utiliser Xlib pour un wrapper de libplayer, pour autant que les mécanismes pour garantir le thread-safe soient implémentés.

VDPAU

NVidia fait des efforts pour Linux depuis longtemps. VDPAU en est un exemple parmi d’autres. Mais tout ceci reste qu’en même du code fermé avec tous les désavantages qui en découlent. Dans le cas de libplayer, il est aujourd’hui impossible de supporter VDPAU avec xine. Non pas que xine ne peut pas l’utiliser, mais plutôt que xine est obligé de passer par Xlib pour pouvoir l’exploiter.

On pourrait croire alors que je n’aurais pas du changer Xlib pour XCB dans libplayer, mais en réalité ça n’a strictement rien à voir. Le problème est au niveau de VDPAU. Celui-ci étant basé sur Xlib, il est nécessaire d’avoir l’initialisation des locks. Le seul moyen actuel est de devoir faire appel à XInitThreads(). Ainsi xine refuse de charger VDPAU dans les seuls cas thread-safe tel que XCB ou la variante Xlib (soit disant "buggée" qui fonctionne qu’en même bien par rapport à ce que libplayet-1.0.0 en fait).

Pour être honnête, il y a un moyen mais c’est un hack. En ajoutant un XInitThreads() dans Enna avant l’initialisation d’Evas ainsi qu’une modification dans le wrapper xine de libplayer pour lui dire qu’il doit travailler avec Xlib.

Tout ceci ne concerne pas le wrapper MPlayer, qui peut parfaitement utiliser VDPAU pour la sortie vidéo comme pour les codecs, étant donné que c’est un processus "forké".

libvalhalla

C’est sur cette bibliothèque où j’ai le plus travaillé depuis la 1.0.0. Mise à part des correctifs sur les scripts et Makefiles comme pour libplayer et Darwin, il y a aussi de nouvelles fonctionnalités.

API

Tout d’abord l’API publique est maintenant plus facile à étendre sans la casser à l’ajout de nouveaux paramètres à l’initialisation par exemple. J’ai également factorisé toutes les fonctions qui permettent de configurer libvalhalla en une seule fonction variadique.

Statistiques

Avec libvalhalla-1.0.0 il y a déjà quelques statistiques. Par exemple les résultats et les temps utilisés par les grabbers, ou encore un résumé des actions qui ont été faites sur la base de donnée. Néanmoins ces informations ne sont pas disponibles depuis l’API. Ce qui change dans le prochain libvalhalla c’est que toute les statistiques de ce type sont récupérables facilement. Il y en a également plus qu’avant. Je dois encore en ajouter sur certaines parties tel que le scanner. Mais les ajouts n’affecteront en rien l’API publique.

Dans le cas d’Enna ça pourra être utilisé pour montrer (pour le fun) l’état des différentes parties de libvalhalla dans une fenêtre d’information. Les statistiques peuvent être interrogées à n’importe quel instant, ce qui permet de suivre l’évolution.

Événements globaux

Il est possible d’avoir des événements globaux comme par exemple une information qui prévient que tous les fichiers  (pour une passe complète du scanner) ont été traités. Il n’y a pas beaucoup d’événements pour l’instant. Ils ne sont pas des plus utiles, mais dans le cas d’Enna il permettront d’avoir une notification. Il est également facile d’en ajouter des nouveaux.

Metadata callback

C’est un callback qui a été ajouté suite à une proposition d’un tiers pour une demande assez spécifique. Le but est de pouvoir récupérer depuis l’API publique toutes les metadata en même temps qu’elles sont récupérées par les parsers et grabbers. C’est donc un moyen d’avoir accès aux données sans passer par la base de donnée. Je ne recommande pas son utilisation pour plusieurs raisons. Tout d’abord si le callback est bloqué relativement longtemps pour chaque metadata, l’utilisation mémoire va augmenter en fonction (le blocage du callback ne bloque pas le reste de la bibliothèque). Il faut absolument veiller à traiter les données aussi vite que possible. La deuxième raison est que SQLite est beaucoup plus performant pour rendre toutes les metadata. Les fonctions de sélections sont relativement haut-niveaux et permettent de récupérer les informations de manière efficaces et ordonnée. Une de ces fonctions a également été un peu améliorée dans le cadre des modifications depuis la version 1.0.0.

J’ai qu’en même rajouté la fonctionnalité dans la bibliothèque car elle n’est pas intrusive et ne peut pas introduire des régressions ou des ralentissements.

Grabbers parallélisés

C’est la plus grosse nouveauté pour la prochaine release. Dans le cas de libvalhalla-1.0.0, beaucoup d’éléments travaillent en parallèle, à l’exception des grabbers (entre eux). Il est possible maintenant d’avoir "autant" de grabbers que l’on veut simultanément. Cette fonctionnalité permet d’économiser environ 30% du temps selon mes essais. L’intérêt est également que les fichiers vidéos ne sont plus bloqués sur les grabbers dédiés à l’audio et inversement. Par exemple le grabber LyricWiki qui est spécialement lent, était un vrai goulot d’étranglement pour les fichiers vidéos (aussi pour les autres fichier audio, ce qui est implicite). La parallélisation permet à ces fichiers (non-audio) de se terminer indépendamment de ce grabber (et des autres).

J’ai mis la limite maximum à 16 grabbers en parallèle (ce qui est plus que le nombre de grabbers différents qui existent). Un choix efficace et d’opter pour 3 ou 4. L’utilisation de la RAM n’est pas spécialement affectée car la plupart utilisent des services web qui sont plutôt gourmand en temps.

Il y a deux autres effets à noter:

  • Les priorités ne sont plus complètement respectées si on défini plus d’un grabber à la configuration. Ce qui veut dire que par exemple un fichier peut être envoyé dans un grabber qui devrait (selon les priorités) être interrogé dans les derniers. Il est donc possible de continuer à travailler comme libvalhalla-1.0.0 en forçant explicitement le nombre à 1.
  • Le second effet est lié au ondemand. Celui-ci est plus rapide car il y a plusieurs threads de grabbers et donc il peut rapidement avoir toutes ces demandes sur tous les threads (à cause des priorités du ondemand).

Le fonctionnement en quelques mots

La parallélisation des grabbers n’est pas comparable à celle des parsers. Lorsqu’un fichier doit être traité par un parser, il attend simplement dans la queue jusqu’à ce qu’un parser le POP et le traite vraiment. Il n’y a pas de raison de faire autrement car tous les parsers font le même travail. Les grabbers sont différents car chacun d’eux fait un travail spécifique. Ainsi lorsqu’un fichier se retrouve dans la queue et qu’un thread de grabber le POP, il va chercher un grabber de libre. Le cas échéant il renvoi le fichier dans la queue et réessaye avec le suivant. Il n’y a donc jamais de longue attente, ce qui permet de bien enchaîner tous les fichiers.

Il y a aussi quelques conditions. Un même fichier n’est jamais parallélisé entre les différents threads des grabbers.  Et un même grabber travail que dans un seul thread à la fois. Ce qui veut dire par exemple, que s’il y a 10 threads; pour que les 10 soient actifs il faut au moins 10 grabbers différents (FFmpeg, LyricWiki, Amazon, etc,…), et 10 fichiers différents.

Il est ainsi évident que d’avoir beaucoup de threads n’apportent absolument plus rien au delà d’une certaine limite que j’estime à environ 4. Mais ceci dépend fortement des types de fichiers différents, des types de grabbers compilés et leur nombre.

Bonne année,

Mathieu SCHROETER

h1

La vidéo par libplayer avec Enna

2008.08.24

Hello,

cet article tente d’expliquer techniquement et sans partir dans trop de détails, comment il est possible d’afficher la vidéo de MPlayer (ou même d’un autre wrapper comme xine) directement dans la fenêtre de Enna.

Avant la révision 801 de libplayer

Un problème majeur persistait avec libplayer depuis toujours. La fenêtre X11 créée pour la sortie vidéo avec Enna n’était pas bien adaptée à un environnement géré par un Window Manager. Cela veut dire que cette fenêtre ne peut pas être manipulée par l’utilisateur (invisible dans la barre des applications, elle ne peut pas être attrapée avec la souris, …), comme si elle n’existait pas.

Techniquement, la raison vient du flag override_redirect de la fenêtre principale (il faut comprendre par "principale" que la fenêtre a pour parent le root), mit à 1, qui veut dire qu’elle doit être ignorée du Window Manager. On peut alors légitimement se poser la question:

Pourquoi donc ne pas créer une fenêtre sans l’override_redirect et ainsi pouvoir la manipuler commes les autres?

Et bien la réalité n’est pas aussi simple. Une fenêtre atteignable par l’utilisateur est une fenêtre qui peut attraper des évènements (clavier et souris par exemple). Et dans le cas contraire, les évènements ignorés par la fenêtre sont soient propagés à la fenêtre parente (propagation ascendante), soient détruits (pour les lecteurs qui froncent les sourcils, comprenez que je ne parle pas des cas particuliers).
L’illustration ci-contre montre une fenêtre principale A qui a un enfant B qui lui à un enfant C. Les évènements étant alors propagés de C à A. On pourrait bien sûr imaginer de détruire les évènements dans B pour ne pas les propager sur A. Tout est possible (même rediriger les évènements ailleurs), mais l’idée présentée ici fait office de comportement par défaut.

C’est donc là que l’avantage principal de la fenêtre en override_redirect intervient. Celle-ci étant ignorée du Window Manager, aucun évènement ne lui parvient car ils sont naturellement envoyés à la fenêtre qui à le focus (logique non?). Prenons un exemple.. Vous avez une petite application basée sur libplayer qui fonctionne en mode console. Vous ouvrez votre terminal GNOME et lancez un film via cette application. libplayer va créer une fenêtre en override_redirect et en plein écran, les évènements seront donc envoyés au terminal (la fenêtre vidéo ne pouvant pas avoir le focus). Ainsi l’exposition de la fenêtre devant celle du terminal n’a aucun impacte.
Mais il y a un piège! Si vous changez de fenêtre à l’aide de ALT-TAB (par exemple), le focus sera dirigé ailleurs mais la vidéo restera toujours en avant plan. Il ne vous est plus possible de piloter votre programme vidéo car les évènements seront renvoyés à la fenêtre attrapée avec ALT-TAB jusqu’à ce que vous rendiez le focus au terminal.

Cette exemple peut être testé à l’aide de l’application test-player compilée avec libplayer.

$test-player -p mplayer video.mpg
action> p

Mouais, alors que faire?

Le fait de travailler sur Enna m’a forcé à trouver une solution propre. Le problème cité précédemment n’aurait pas d’impact direct sur GeeXboX car la seule application active dans X11 serait Enna. Mais libplayer doit être universel, alors un changement s’imposait de toute façon.

Les fenêtres qui se listent dans votre barre d’applications sont des fenêtres dont le parent est le root du display X11 en cours (grosso-modo). Mais une application peut elle-même être composée de sous-fenêtres, et elles ne sont pas toujours visibles directement par l’utilisateur (imaginez des calques qui se superposent comme dans un logiciel de dessin).
Il faut bien comprendre que le langage du serveur X est relativement bas niveau. Si vous utilisez GNOME, les fenêtres sont gérées par GTK. Ce que vous voyez, c’est uniquement ce que GTK veut bien vous montrer. Et c’est exactement la même chose avec Qt. Mais que se sois GTK ou Qt, derrière il y a toujours le serveur X avec son langage, exploitable via les librairies Xlib et XCB. libplayer utilisant Xlib (implicitement XCB par wrapping), il est donc facile de dialoguer avec le serveur X sans se soucier du Window Manager. Et pour ce faire, il faut connaître l’identifiant de la fenêtre avec laquelle on désire y faire des manipulations. Il est très facile de retrouver cet identifiant avec les EFL (cf. r275 de Enna).

Une solution se profile

Mon but était de créer la fenêtre non plus par rapport au root de X11, mais par rapport à la fenêtre de Enna (je connais son ID). La fenêtre de libplayer n’est donc plus sœur avec la fenêtre de Enna, mais enfant. Ainsi, les évènements envoyés sur la fenêtre de libplayer sont renvoyés sur Enna sans la moindre écriture de code. La propagation ascendante de X11 fait simplement son job. Il est donc désormais possible de changer de fenêtre avec ALT-TAB et de revenir sur Enna comme avec n’importe quel application. Car la vidéo n’étant plus rattachée au root, elle suivra implicitement sa fenêtre parente "Enna" comme si elle faisait partie intégrante du programme.

On peut alors se demander qu’est-ce qui se passe au niveau de Enna. Et bien Ecore_Evas ne rentre pas en compte car il ignore l’existence de cette fenêtre enfant.

A bientôt,

Mathieu SCHROETER

Suivre

Recevez les nouvelles publications par mail.

%d bloggers like this: