Ecrire un programme basé sur libplayer

Hello,

Cet article présente un exemple de l’utilisation de libplayer en C. Cette exemple se veut très simple et n’aborde qu’une petite partie des possibilités offertes par la librairie. Le but ici étant de vous montrer comment initialiser libplayer, comment lui donner quelque chose à lire pour également récupérer quelques informations et puis attendre la fin de la lecture pour quitter proprement. Il est vivement recommandé de consulter le fichier d’en-tête player.h pour une description de chaque fonction.

Initialisation d’un player

Avant toute chose, créons un fichier exemple.c avec la fonction principale main() et les éléments nécessaires pour utiliser libplayer.

static int
event_cb (player_event_t e, void *data)
{
  return 0;
}

int
main (int argc, char **argv)
{
  player_t *player;

  player = player_init (PLAYER_TYPE_MPLAYER,
                        PLAYER_AO_AUTO,
                        PLAYER_VO_AUTO,
                        PLAYER_MSG_WARNING,
                        0, event_cb);
  if (!player)
    return -1;

  /* ... */

  player_uninit (player);
  return 0;
}

‘argv’ nous sera utile pour spécifier à libplayer le fichier audio/video à lire. Le type player_t représente un lecteur audio/video, sans lui vous ne pourrez rien faire du tout. La fonction player_init() va nous permettre de créer une instance de player_t. Il est possible d’utiliser un callback pour récupérer des évènements venant de libplayer, la fonction event_cb() jouera donc ce rôle. player_uninit() va libérer correctement tout ce qui doit l’être. Ne quittez jamais une application sans “uninitialiser” le player!

Paramètres de player_init()

C’est ici que vous allez pouvoir donner quelques paramètres pour créer l’instance. Pour notre exemple on va choisir d’utiliser le wrapper MPlayer. Vous pouvez utiliser PLAYER_TYPE_XINE si vous préférez, mais n’utilisez pas GSTREAMER ou VLC qui ne sont pas encore suffisamment exploitables. Les deux paramètres suivant indiquent la sortie audio et la sortie vidéo à utiliser. Si vous avez défini MPlayer, je vous conseil de laisser en AUTO. Néanmoins avec xine il est nécessaire de les spécifier, un bon choix est PLAYER_AO_ALSA pour la sortie audio et PLAYER_VO_XV pour la sortie vidéo.
Le paramètre qui suit indique le niveau de verbosité. Le meilleur choix est le niveau “warning”, qui permet de se rendre compte des potentiels problèmes sans pour autant remplir le terminal de messages.
L’avant dernier argument permet d’indiquer dans quel fenêtre X11 la vidéo sera affichée. Dans notre cas on en a aucune, alors la valeur 0 indiquera à libplayer de créer une fenêtre en plein écran (désactivez Compiz si nécessaire, les fenêtres créées de cette manière posent un problème majeur avec ce gestionnaire).
Le dernier paramètre permet de transmettre l’adresse de notre callback. Il est optionnel, passez NULL si vous ne voulez pas l’utiliser. Par contre il ne sera plus possible de récupérer l’information de fin de lecture.

Que fait player_init()? Et bien il se passe beaucoup de choses, je vais donc résumer les étapes. Cette fonction va créer une instance de player_t puis va initialiser trois éléments importants. Tout d’abord libplayer étant MT-Safe, il y a un superviseur créé pour chaque instance, qui va sérialiser les actions venant de l’utilisateur de la librairie pour les transmettre au wrapper.
Il y a un gestionnaire d’évènement qui a pour tâche de transmettre certaines informations venant du wrapper ou de l’interne à libplayer, pour l’utilisateur via le callback (event_cb() dans notre cas).
La 3ème étapes importante étant d’initialiser le wrapper, donc MPlayer (fork du process), xine, GStreamer ou VLC.

Initialisation d’un mrl

Un MRL dans libplayer représente un média. Cela peut être un fichier, un DVD, un CD audio, etc,… Dans notre exemple on se contentera des fichiers. Tout d’abord il faut créer une instance pour un MRL puis l’assigner à un player.
En fonction du type de MRL, les arguments peuvent changer. Dans notre cas, il nous faut ceux qui définissent un fichier. Il nous faut également un pointeur pour notre MRL.

mrl_resource_local_args_t *args;
mrl_t *mrl;

Créons ces arguments dynamiquement pour les paramétrer (args sera automatiquement libéré par libplayer en temps voulu).

args = calloc (1, sizeof (mrl_resource_local_args_t));

Et enfin, donnons les arguments et créons l’instance de notre MRL. Si mrl_new() a pu créer l’instance sans erreur, il ne faut surtout pas libérer args!

args->location = strdup (argv[1]);
mrl = mrl_new (player, MRL_RESOURCE_FILE, args);

Détails de mrl_new()

Cette fonction va créer une instance en fonction de la ressource (FILE dans notre cas) puis interroger le player pour récupérer quelques informations. Il n’est pas nécessaire de spécifier si le fichier est uniquement audio, ou vidéo. libplayer devinera automatiquement en fonction des propriétés retournées par le wrapper (MPlayer par exemple).

Assigner le mrl au player

Maintenant que nous avons un mrl de prêt, nous pouvons l’assigner au player puis lancer la lecture.

player_mrl_set (player, mrl);
title = mrl_get_metadata (player, NULL,
                          MRL_METADATA_TITLE);
if (title)
  printf ("Title: %sn", title);
player_playback_start (player);

Quand une instance est assignée au player, elle est automatiquement libérée dés l’appel à player_uninit(). En ce qui concerne mrl_get_metadata(), le fait de passer un mrl NULL indique que nous voulons récupérer l’information depuis le MRL actuellement défini dans player (on aurait pu mettre ‘mrl’ à la place, ce qui reviendrait au même).

Attendre la fin de la lecture

Il y a deux moyens d’attendre la fin de la lecture. Par attente active ou par attente passive. Afin de faire les choses de manière élégante, je vous propose de mettre en œuvre une attente passive via un semaphore initialisé à 0.

Exemple complet

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <player.h>

static sem_t sem;

static int
event_cb (player_event_t e, void *data)
{
  if (e == PLAYER_EVENT_PLAYBACK_FINISHED)
    sem_post (&sem);

  return 0;
}

int
main (int argc, char **argv)
{
  player_t *player;
  mrl_resource_local_args_t *args;
  mrl_t *mrl;
  int res = 0;
  char *title;

  if (argc < 2)
    return -1;

  player = player_init (PLAYER_TYPE_MPLAYER,
                        PLAYER_AO_AUTO,
                        PLAYER_VO_AUTO,
                        PLAYER_MSG_WARNING,
                        0, event_cb);
  if (!player)
    return -1;

  sem_init (&sem, 0, 0);

  args = calloc (1, sizeof (mrl_resource_local_args_t));
  if (!args)
  {
    res = -1;
    goto out;
  }

  args->location = strdup (argv[1]);
  mrl = mrl_new (player, MRL_RESOURCE_FILE, args);
  if (!mrl)
  {
    free (args->location);
    free (args);
    res = -1;
    goto out;
  }

  player_mrl_set (player, mrl);
  title = mrl_get_metadata (player,
                            NULL, MRL_METADATA_TITLE);
  if (title)
  {
    printf ("Title: %s\n", title);
    free (title);
  }
  player_playback_start (player);

  sem_wait (&sem);

 out:
  player_uninit (player);
  sem_destroy (&sem);
  return res;
}

Il est bien clair que cet exemple n’est pas parfait (et pas totalement protégé). Si la lecture ne se termine jamais correctement, le processus reste en attente indéfiniment. Si cela devrait arriver (un fichier illisible par exemple), un kill lui règlera son compte, mais n’oubliez pas de killer également le processus fils “mplayer”. Celui-ci n’étant jamais terminé sans un appel à player_uninit().

Pour compiler cet exemple:

  gcc `pkg-config libplayer --cflags --libs` exemple.c -o exemple

A bientôt,

Mathieu SCHROETER

4 thoughts on “Ecrire un programme basé sur libplayer

  1. Vraiment intéressant cet article, ça donne envi de tester pour voir ce qu’il est possible de faire!

  2. J’arrive un peu en retard, mais il ne vaudrait pas mieux inverser ces deux lignes ?

    52 free (args);
    53 free (args->location);

    en

    52 free (args->location);
    53 free (args);

  3. De rien, j’apprécie énormement le travail que tu fais, au fait :). La publication de tes analyses face aux problèmes rencontrés, l’explication des choix techniques que tu fais …

    Chapeau et longue vie à Geexbox ;), ton code est un régal à lire.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s