NOTE: Cet article a été publié dans le magazine "Login:" (site web: www.loginmag.com) daté de Septembre 2002. Il est reproduit ici avec l'aimable permission de ce magazine.

VDR - Les plugins

VDR supporte les plugins depuis la version 1.1.x. Grâce à eux, il est possible d'accomplir des fonctions aussi variées que la lecture de DVD, le décodage de pages télétextes ou bien même de jouer à tetris pour patienter pendant les pubs.

Avant la version 1.1.x de VDR, les fonctions supplémentaires étaient ajoutées par des patchs. Ce qui posait des problèmes de maintenance dès que le code de VDR changeait. L'auteur a donc rajouté le support des plugins. Si il est maintenant bien plus simple de rajouter des fonctionnalités, des notions de C++ restent tout de même requises ! Voyons comment les plugins fonctionnent et comment implémenter les nôtres.

Structure d'un plugin

Un plugin VDR est un "shared object" (extension .so) chargé dynamiquement. Par défaut, le code source d'un plugin est situé dans le répertoire "VDR/PLUGINS/SRC/" alors que le SO se trouve dans le répertoire "VDR/PLUGINS/lib/". Pour nous faciliter la tâche, une commande appelée "newplugin" permet de créer la structure d'un nouveau plugin. Le résultat se trouve dans le répertoire "VDR/PLUGINS/SRC/monplugin".

Hello world

Et bien si, on vous refait le coup du "hello world" ! Ce plugin ne fait rien d'autre que d'afficher un message sur l'OSD (On Screen Display) de VDR pendant un temps configurable par menu . Ce message est également dépendant de la langue paramétrée dans VDR (internationalisation).
Le plugin dérive un objet de la classe "cPlugin". Cette classe est entièrement définie dans la documentation sur les plugins que l'on trouve en HTML : "VDR/PLUGINS.html". Elle comporte les méthodes permettant de donner un nom au plugin (SetName), d'afficher une aide (CommandLineHelp), de récupérer des arguments venant de la ligne de commande (ProcessArgs), de démarrer le plugin (Start), d'accomplir des tâches périodiquement (Housekeeping), de définir les actions à effectuer lorsque le plugin est appelé à partir du menu principal (MainMenuAction), de définir le menu de configuration (SetupMenu), de récupérer (SetupParse) ou de stocker (SetupStore) les paramètres stockés dans le fichier de configuration de VDR, d'enregistrer les traductions des textes utilisés par le plugin (RegisterI18n) et, enfin, de récupérer le chemin du répertoire de configuration de (ConfigDirectory).
Dans le cas du plugin "hello", nous implémentons principalement trois fonctions : Start pour initialiser l'environnement du plugin, MainMenuAction pour afficher notre fameux message et SetupMenu pour contrôler les paramètres du plugin.

VDR, dernière version
VDR, dernière version

Start

Cette fonction est exécutée au chargement du plugin. On s'en sert pour lancer un thread tournant en tâche de fond ou enregistrer le plugin pour l'internationalisation. Cette dernière fonctionnalité est nécessaire afin que les utilisateurs de VDR puissent profiter d'une interface dans leur propre langue (rappelons que VDR est destiné à être utilisé par tous, de 7 à 77 ans !). Pour ce faire, il faut créer un tableau contenant les traductions des chaînes utilisées par le plugin. Il suffit ensuite d'utiliser tr("Ma chaine") pour traduire tout ce qui touche à l'interface utilisateur suivant la langue de l'interface. Et comme il y a peu de chance que vous puissiez traduire vous même votre plugin dans les 15 (!) langues supportées par VDR, vous trouverez tout le support nécessaire grâce à la mailing list de VDR (vdr@linuxtv.org).

bool cPluginHello::Start(void)
{
  // Start any background activities the plugin shall perform.
  RegisterI18n(Phrases);
  return true;
}

Un boitier VDR de toute beauté avec LCD
Un boitier VDR de toute beauté avec LCD

SetupMenu

Dans notre exemple, le menu de configuration est un objet cMenuSetupHello derive de la classe cMenuSetupPage. Il implémente le constructeur ainsi que la fonction virtuelle Store. Cet objet est créé lors de l'appel à la méthode SetupMenu du plugin.

MenuSetupPage *cPluginHello::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return new cMenuSetupHello;
}

cMenuSetupHello::cMenuSetupHello(void)
{
  newGreetingTime = GreetingTime;
  newUseAlternateGreeting = UseAlternateGreeting;
  Add(new cMenuEditIntItem( tr("Greeting time (s)"), &newGreetingTime));
  Add(new cMenuEditBoolItem(tr("Use alternate greeting"), &newUseAlternateGreeting));
}

void cMenuSetupHello::Store(void)
{
  SetupStore("GreetingTime", GreetingTime = newGreetingTime);
  SetupStore("UseAlternateGreeting", UseAlternateGreeting = newUseAlternateGreeting);
}

La fonction Store est appelée au moment où l'utilisateur confirme les modifications faites dans le menu en appuyant sur "OK". Grâce à la méthode SetupStore, les paramètres sont stockés dans le fichier de configuration de VDR, "setup.conf".

bool cPluginHello::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  if      (!strcasecmp(Name, "GreetingTime"))         GreetingTime = atoi(Value);
  else if (!strcasecmp(Name, "UseAlternateGreeting")) UseAlternateGreeting = atoi(Value);
  else
     return false;
  return true;
}

La méthode SetupParse est appelée au moment de la création de l'objet cPluginHello pour chaque paramètre précédemment stocké.

MainMenuAction

La méthode MainMenuAction est appelée lorsque l'entrée correspondant au plugin est sélectionnée dans le menu principal. Si son contenu est trivial pour notre exemple, c'est généralement ici que réside le coeur du plugin. Comme l'appel à cette méthode est préemptif, il est nécessaire d'en sortir le plus vite possible en créant si nécessaire un nouveau thread.

cOsdMenu *cPluginHello::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  Interface->Confirm(UseAlternateGreeting ? tr("Howdy folks!") : tr("Hello world!"), GreetingTime);
  return NULL;
}

Si, au lieu de retourner NULL, on retourne un pointeur sur un objet de type cOsdMenu, cela aura pour effet d'afficher un sous-menu du menu principal qui peut lui-même appeler diverses fonctions.

Déjà beaucoup de plugins disponibles
Déjà beaucoup de plugins disponibles

Compilation / exécution

Pour compiler le plugin, il faut se placer dans le répertoire de VDR et lancer "make plugins". Pour lancer VDR avec le plugin "hello", il faut rajouter l'option "-P hello" (le nom du plugin) à la ligne de commande d'appel de VDR.

Les classes à votre disposition

D'autres classes sont à votre disposition pour créer vos propres plugins. La classe "cStatus" permet de récupérer les événements associés à VDR, comme le changement de chaîne, le début d'un enregistrement (automatique ou non), l'affichage des informations EPG (Extended Program Guide). Cette classe est notamment utilisée par plusieurs plugins pilotants des écrans LCD.
La classe "cPlayer" permet de jouer un fichier vidéo ou audio pour peu que celui-ci soit au format DVB (PES). Elle est généralement associée à la classe cControl qui permet de récupérer les actions effectuées par l'utilisateur (provenant du clavier ou d'une télécommande).
La classe cReceiver permet de recevoir des données d'un cDevice. Elle est notamment utilisée dans un plugin implémentant le décodage télétexte. Elle pourrait aussi être utilisée pour interpréter les données MediaHighway (utilisées pour les services étendus de Canal Satellite) ou OpenTV (l'équivalent, mais pour TPS). La classe cOsd permet de contrôler l'OSD des cartes DVB. Cet OSD est affiché en superposition à l'émission en cours. Les fonctions prenant en charge l'affichage de texte, de rectangles, de pixels ou directement de bitmap sont disponibles. Cette classe est utilisée par un plugin permettant de jouer à tetris à la télécommande tout en regardant l'émission en cours.
La classe cDevice permet d'intégrer de nouveaux périphériques (d'acquisition comme de restitution) autres que les cartes DVB à VDR.
La classe cAudio est destinée à tout ce qui peut utiliser le son. Elle sert notamment à restituer les flux AC3 (Dolby digital 5.1) vers la prise numérique (S/PDIF) de la carte DVB ou d'une carte son en disposant. La classe cRemote permet d'implémenter un nouveau type de télécommande pour contrôler VDR. 3 types de "télécommandes" sont déjà inclues dans VDR ; le clavier, un module RCU (Remote Control Unit) et un module LIRC. On peut imaginer par exemple une télécommande utilisant un browser WAP ou une reconnaissance vocale.

De quoi s'amuser tout en regardant la télévision
De quoi s'amuser tout en regardant la télévision