add a first implementation of an MPlayer wrapper
authorMathieu Schroeter <mathieu.schroeter@mycable.ch>
Thu Jul 26 13:02:44 2007 +0200 (2 years ago)
changeset 44956ecc1085d7
parent 433b8f98d39a16
child 45499063c1d9f2
add a first implementation of an MPlayer wrapper
configure
regtest-libplayer.c
src/Makefile
src/player.c
src/player.h
src/wrapper_mplayer.c
src/wrapper_mplayer.h
       1 --- a/configure	Wed Jul 25 17:52:00 2007 +0200
       2 +++ b/configure	Thu Jul 26 13:02:44 2007 +0200
       3 @@ -35,6 +35,8 @@
       4    echo "  --enable-xine               build xine wrapper"
       5    echo "  --disable-xine              do not build xine wrapper"
       6    echo "  --with-xine-dir=DIR         check for libxine installed in DIR"
       7 +  echo "  --enable-mplayer            build MPlayer wrapper"
       8 +  echo "  --disable-mplayer           do not build MPlayer wrapper"
       9    echo "  --enable-vlc                build VLC wrapper"
      10    echo "  --disable-vlc               do not build VLC wrapper"
      11    echo "  --with-vlc-dir=DIR          check for libvlc installed in DIR"
      12 @@ -300,6 +302,7 @@
      13  installstrip="-s"
      14  cross_compile="no"
      15  wrapper_xine="yes"
      16 +wrapper_mplayer="yes"
      17  wrapper_vlc="yes"
      18  INSTALL="install"
      19  
      20 @@ -395,6 +398,10 @@
      21    --disable-xine) wrapper_xine="no";
      22    ;;
      23    --with-xine-dir=*) libxinedir="$optval";
      24 +  ;;
      25 +  --enable-mplayer) wrapper_mplayer="yes";
      26 +  ;;
      27 +  --disable-mplayer) wrapper_mplayer="no";
      28    ;;
      29    --enable-vlc) wrapper_vlc="yes";
      30    ;;
      31 @@ -601,6 +608,14 @@
      32  fi
      33  
      34  #################################################
      35 +#   check for mplayer
      36 +#################################################
      37 +if test "$wrapper_mplayer" = "yes"; then
      38 +  add_cflags -DHAVE_MPLAYER
      39 +  add_extralibs -lpthread
      40 +fi
      41 +
      42 +#################################################
      43  #   check for libvlc
      44  #################################################
      45  if test "$wrapper_vlc" = "yes"; then
      46 @@ -668,6 +683,7 @@
      47  
      48  append_config "DEBUG=$debug"
      49  append_config "WRAPPER_XINE=$wrapper_xine"
      50 +append_config "WRAPPER_MPLAYER=$wrapper_mplayer"
      51  append_config "WRAPPER_VLC=$wrapper_vlc"
      52  
      53  clean
     1.1 --- a/regtest-libplayer.c	Wed Jul 25 17:52:00 2007 +0200
     1.2 +++ b/regtest-libplayer.c	Thu Jul 26 13:02:44 2007 +0200
     1.3 @@ -34,6 +34,7 @@
     1.4  typedef enum player_id {
     1.5    PLAYER_ID_ALL = 0,
     1.6    PLAYER_ID_XINE,
     1.7 +  PLAYER_ID_MPLAYER,
     1.8    PLAYER_ID_VLC,
     1.9    PLAYER_ID_DUMMY
    1.10  } player_id_t;
    1.11 @@ -108,6 +109,14 @@
    1.12    }
    1.13  #endif /* HAVE_XINE */
    1.14  
    1.15 +#ifdef HAVE_MPLAYER
    1.16 +  if (player_id == PLAYER_ID_MPLAYER || player_id == PLAYER_ID_ALL)
    1.17 +  {
    1.18 +    printf ("\n--- MPlayer ---\n");
    1.19 +    player_run_test (PLAYER_TYPE_MPLAYER);
    1.20 +  }
    1.21 +#endif /* HAVE_MPLAYER */
    1.22 +
    1.23  #ifdef HAVE_VLC
    1.24    if (player_id == PLAYER_ID_VLC || player_id == PLAYER_ID_ALL)
    1.25    {
    1.26 @@ -133,6 +142,10 @@
    1.27      else if (!strcmp (argv[1], "xine"))
    1.28        player_id = PLAYER_ID_XINE;
    1.29  #endif /* HAVE_XINE */
    1.30 +#ifdef HAVE_MPLAYER
    1.31 +    else if (!strcmp (argv[1], "mplayer"))
    1.32 +      player_id = PLAYER_ID_MPLAYER;
    1.33 +#endif /* HAVE_MPLAYER */
    1.34  #ifdef HAVE_VLC
    1.35      else if (!strcmp (argv[1], "vlc"))
    1.36        player_id = PLAYER_ID_VLC;
     2.1 --- a/src/Makefile	Wed Jul 25 17:52:00 2007 +0200
     2.2 +++ b/src/Makefile	Thu Jul 26 13:02:44 2007 +0200
     2.3 @@ -15,6 +15,10 @@
     2.4  
     2.5  ifeq ($(WRAPPER_VLC),yes)
     2.6    SRCS += wrapper_vlc.c
     2.7 +endif
     2.8 +
     2.9 +ifeq ($(WRAPPER_MPLAYER),yes)
    2.10 +  SRCS += wrapper_mplayer.c
    2.11  endif
    2.12  
    2.13  OBJS = $(SRCS:.c=.o)
     3.1 --- a/src/player.c	Wed Jul 25 17:52:00 2007 +0200
     3.2 +++ b/src/player.c	Thu Jul 26 13:02:44 2007 +0200
     3.3 @@ -32,6 +32,9 @@
     3.4  #ifdef HAVE_XINE
     3.5  #include "wrapper_xine.h"
     3.6  #endif /* HAVE_XINE */
     3.7 +#ifdef HAVE_MPLAYER
     3.8 +#include "wrapper_mplayer.h"
     3.9 +#endif /* HAVE_MPLAYER */
    3.10  #ifdef HAVE_VLC
    3.11  #include "wrapper_vlc.h"
    3.12  #endif /* HAVE_VLC */
    3.13 @@ -69,6 +72,12 @@
    3.14      player->priv = register_private_xine ();
    3.15      break;
    3.16  #endif /* HAVE_XINE */
    3.17 +#ifdef HAVE_MPLAYER
    3.18 +  case PLAYER_TYPE_MPLAYER:
    3.19 +    player->funcs = register_functions_mplayer ();
    3.20 +    player->priv = register_private_mplayer ();
    3.21 +    break;
    3.22 +#endif /* HAVE_MPLAYER */
    3.23  #ifdef HAVE_VLC
    3.24    case PLAYER_TYPE_VLC:
    3.25      player->funcs = register_functions_vlc ();
     4.1 --- a/src/player.h	Wed Jul 25 17:52:00 2007 +0200
     4.2 +++ b/src/player.h	Thu Jul 26 13:02:44 2007 +0200
     4.3 @@ -28,6 +28,7 @@
     4.4  
     4.5  typedef enum player_type {
     4.6    PLAYER_TYPE_XINE,
     4.7 +  PLAYER_TYPE_MPLAYER,
     4.8    PLAYER_TYPE_VLC,
     4.9    PLAYER_TYPE_DUMMY
    4.10  } player_type_t;
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/wrapper_mplayer.c	Thu Jul 26 13:02:44 2007 +0200
     5.3 @@ -0,0 +1,913 @@
     5.4 +/*
     5.5 + * GeeXboX libplayer: a multimedia A/V abstraction layer API.
     5.6 + * Copyright (C) 2007 Mathieu Schroeter <mathieu.schroeter@gamesover.ch>
     5.7 + *
     5.8 + * This file is part of libplayer.
     5.9 + *
    5.10 + * libplayer is free software; you can redistribute it and/or
    5.11 + * modify it under the terms of the GNU Lesser General Public
    5.12 + * License as published by the Free Software Foundation; either
    5.13 + * version 2.1 of the License, or (at your option) any later version.
    5.14 + *
    5.15 + * libplayer is distributed in the hope that it will be useful,
    5.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    5.18 + * Lesser General Public License for more details.
    5.19 + *
    5.20 + * You should have received a copy of the GNU Lesser General Public
    5.21 + * License along with libplayer; if not, write to the Free Software
    5.22 + * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    5.23 + */
    5.24 +
    5.25 +#include <stdlib.h>
    5.26 +#include <inttypes.h>
    5.27 +#include <stdio.h>
    5.28 +#include <string.h>       /* strstr strlen memcpy strdup */
    5.29 +#include <stdarg.h>       /* va_start va_end */
    5.30 +#include <unistd.h>       /* pipe fork close dup2 */
    5.31 +#include <math.h>         /* rintf */
    5.32 +#include <sys/wait.h>     /* waitpid */
    5.33 +#include <sys/types.h>
    5.34 +#include <sys/stat.h>     /* stat */
    5.35 +#include <signal.h>       /* sigaction */
    5.36 +
    5.37 +#include "player.h"
    5.38 +#include "player_internals.h"
    5.39 +#include "logs.h"
    5.40 +#include "wrapper_mplayer.h"
    5.41 +
    5.42 +#define MODULE_NAME "mplayer"
    5.43 +
    5.44 +#define SLAVE_CMD_BUFFER 256
    5.45 +
    5.46 +/* Status of MPlayer child */
    5.47 +typedef enum mplayer_status {
    5.48 +  MPLAYER_IS_IDLE,
    5.49 +  MPLAYER_IS_DEAD
    5.50 +} mplayer_status_t;
    5.51 +
    5.52 +/* player specific structure */
    5.53 +typedef struct mplayer_s {
    5.54 +  mplayer_status_t status;
    5.55 +  pid_t pid;          /* process pid */
    5.56 +  int pipe_in[2];     /* pipe for send commands to MPlayer */
    5.57 +  int pipe_out[2];    /* pipe for receive results */
    5.58 +  FILE *fifo_in;      /* fifo on the pipe_in  (write only) */
    5.59 +  FILE *fifo_out;     /* fifo on the pipe_out (read only) */
    5.60 +} mplayer_t;
    5.61 +
    5.62 +/* slave commands */
    5.63 +typedef enum slave_cmd {
    5.64 +  SLAVE_GET_PROPERTY, /* get_property string */
    5.65 +  SLAVE_LOADFILE,     /* loadfile string [int] */
    5.66 +  SLAVE_PAUSE,        /* pause */
    5.67 +  SLAVE_SEEK,         /* seek float [int] */
    5.68 +  SLAVE_SET_PROPERTY, /* set_property string string */
    5.69 +  SLAVE_STOP
    5.70 +} slave_cmd_t;
    5.71 +
    5.72 +/* slave properties */
    5.73 +typedef enum slave_property {
    5.74 +  PROPERTY_AUDIO_BITRATE,
    5.75 +  PROPERTY_AUDIO_CODEC,
    5.76 +  PROPERTY_CHANNELS,
    5.77 +  PROPERTY_HEIGHT,
    5.78 +  PROPERTY_LOOP,
    5.79 +  PROPERTY_METADATA_ALBUM,
    5.80 +  PROPERTY_METADATA_ARTIST,
    5.81 +  PROPERTY_METADATA_GENRE,
    5.82 +  PROPERTY_METADATA_TITLE,
    5.83 +  PROPERTY_METADATA_TRACK,
    5.84 +  PROPERTY_METADATA_YEAR,
    5.85 +  PROPERTY_MUTE,
    5.86 +  PROPERTY_SAMPLERATE,
    5.87 +  PROPERTY_VIDEO_BITRATE,
    5.88 +  PROPERTY_VIDEO_CODEC,
    5.89 +  PROPERTY_VOLUME,
    5.90 +  PROPERTY_WIDTH
    5.91 +} slave_property_t;
    5.92 +
    5.93 +
    5.94 +static const struct {
    5.95 +  slave_property_t property;
    5.96 +  char *text;
    5.97 +} g_slave_props[] = {
    5.98 +  {PROPERTY_AUDIO_BITRATE,    "audio_bitrate"},
    5.99 +  {PROPERTY_AUDIO_CODEC,      "audio_codec"},
   5.100 +  {PROPERTY_CHANNELS,         "channels"},
   5.101 +  {PROPERTY_HEIGHT,           "height"},
   5.102 +  {PROPERTY_LOOP,             "loop"},
   5.103 +  {PROPERTY_METADATA_ALBUM,   "metadata/album"},
   5.104 +  {PROPERTY_METADATA_ARTIST,  "metadata/artist"},
   5.105 +  {PROPERTY_METADATA_GENRE,   "metadata/genre"},
   5.106 +  {PROPERTY_METADATA_TITLE,   "metadata/title"},
   5.107 +  {PROPERTY_METADATA_TRACK,   "metadata/track"},
   5.108 +  {PROPERTY_METADATA_YEAR,    "metadata/year"},
   5.109 +  {PROPERTY_MUTE,             "mute"},
   5.110 +  {PROPERTY_SAMPLERATE,       "samplerate"},
   5.111 +  {PROPERTY_VIDEO_BITRATE,    "video_bitrate"},
   5.112 +  {PROPERTY_VIDEO_CODEC,      "video_codec"},
   5.113 +  {PROPERTY_VOLUME,           "volume"},
   5.114 +  {PROPERTY_WIDTH,            "width"}
   5.115 +};
   5.116 +
   5.117 +
   5.118 +static void
   5.119 +sig_handler (int signal)
   5.120 +{
   5.121 +  if (signal == SIGPIPE)
   5.122 +    plog (MODULE_NAME, "SIGPIPE detected by the death of MPlayer");
   5.123 +}
   5.124 +
   5.125 +/**
   5.126 + * Get the text for a specific property in the g_slave_props. This function
   5.127 + * should never return NULL. If that is the case, then a property is missed
   5.128 + * in the table.
   5.129 + */
   5.130 +static char *
   5.131 +get_prop (slave_property_t property)
   5.132 +{
   5.133 +  int i;
   5.134 +
   5.135 +  for (i = 0; i < sizeof (g_slave_props) / sizeof (g_slave_props[0]); i++)
   5.136 +    if (g_slave_props[i].property == property)
   5.137 +      return g_slave_props[i].text;
   5.138 +
   5.139 +  return NULL;
   5.140 +}
   5.141 +
   5.142 +/**
   5.143 + * Send a formatted command to the MPlayer's slave. fifo_in is a file
   5.144 + * descriptor on a pipe for the MPlayer's stdin.
   5.145 + */
   5.146 +static void
   5.147 +send_to_slave (mplayer_t *mplayer, const char *format, ...)
   5.148 +{
   5.149 +  va_list va;
   5.150 +
   5.151 +  if (!mplayer || !mplayer->fifo_in)
   5.152 +    return;
   5.153 +
   5.154 +  va_start (va, format);
   5.155 +  vfprintf (mplayer->fifo_in, format, va);
   5.156 +  fprintf (mplayer->fifo_in, "\n");
   5.157 +  fflush (mplayer->fifo_in);
   5.158 +  va_end (va);
   5.159 +}
   5.160 +
   5.161 +/**
   5.162 + * Get results of commands sent to MPlayer. The MPlayer's stdout and sterr
   5.163 + * are connected to fifo_out. Because MPlayer doesn't confirm all commands
   5.164 + * (for example, a get_property with no stream return nothing), that is
   5.165 + * necessary for send an other command with an output in all cases.
   5.166 + * Here, this command create an error captured in the stderr for confirm
   5.167 + * the end of the fifo_out.
   5.168 + */
   5.169 +static char *
   5.170 +slave_result (slave_property_t property, player_t *player)
   5.171 +{
   5.172 +  char str[SLAVE_CMD_BUFFER];
   5.173 +  char *buffer;
   5.174 +  char *prop;
   5.175 +  char *ret = NULL;
   5.176 +  char *its, *ite;
   5.177 +  mplayer_t *mplayer = NULL;
   5.178 +
   5.179 +  if (!player)
   5.180 +    return NULL;
   5.181 +
   5.182 +  mplayer = (mplayer_t *) player->priv;
   5.183 +
   5.184 +  if (!mplayer || !mplayer->fifo_in || !mplayer->fifo_out)
   5.185 +    return NULL;
   5.186 +
   5.187 +  /* get the right property in the global list */
   5.188 +  prop = get_prop (property);
   5.189 +  if (prop)
   5.190 +    sprintf (str, "ANS_%s=", prop);
   5.191 +  else
   5.192 +    /* should never going here */
   5.193 +    return NULL;
   5.194 +
   5.195 +  /* NOTE: /!\ That is ugly but it works pretty well :o)
   5.196 +   * Because MPlayer doesn't confirm commands, that is necessary for
   5.197 +   * have a text in the fifo_out for know if there is no new message.
   5.198 +   * Else, the fgets (read) will block on the pipe. NONBLOCK is not a
   5.199 +   * solution, better for have an information that will to confirm if
   5.200 +   * MPlayer has no result and then to stop the read.
   5.201 +   *
   5.202 +   * An error is created by the command 'loadfile' without arguments. This
   5.203 +   * error is got with fgets() and the while is break if there is no
   5.204 +   * result for the real command in var 'str'.
   5.205 +   */
   5.206 +  send_to_slave (mplayer, "loadfile");
   5.207 +
   5.208 +  if ((buffer = malloc (SLAVE_CMD_BUFFER))) {
   5.209 +    /* MPlayer's stdout parser */
   5.210 +    while (fgets (buffer, SLAVE_CMD_BUFFER, mplayer->fifo_out))
   5.211 +    {
   5.212 +      /* search the result */
   5.213 +      if ((its = strstr(buffer, str))) {
   5.214 +        /* value start */
   5.215 +        its += strlen (str);
   5.216 +        ite = its;
   5.217 +        while (*ite != '\0' && *ite != '\n')
   5.218 +          ite++;
   5.219 +
   5.220 +        /* value end */
   5.221 +        *ite = '\0';
   5.222 +
   5.223 +        if ((ret = malloc (strlen (its) + 1))) {
   5.224 +          memcpy (ret, its, strlen (its));
   5.225 +          *(ret + strlen (its)) = '\0';
   5.226 +        }
   5.227 +      }
   5.228 +      /* If this error (from stderr) exists, then we can go out
   5.229 +       * because there is no result for the real command.
   5.230 +       */
   5.231 +      else if (strstr (buffer, "Command loadfile"))
   5.232 +        break;
   5.233 +
   5.234 +      *buffer = '\0';
   5.235 +    }
   5.236 +
   5.237 +    free (buffer);
   5.238 +  }
   5.239 +
   5.240 +  return ret;
   5.241 +}
   5.242 +
   5.243 +static int
   5.244 +slave_result_int (player_t *player, slave_property_t property)
   5.245 +{
   5.246 +  int value = -1;
   5.247 +  char *result;
   5.248 +
   5.249 +  result = slave_result (property, player);
   5.250 +
   5.251 +  if (result) {
   5.252 +    /* string to float and round to int */
   5.253 +    value = (int) rintf (atof (result));
   5.254 +    free (result);
   5.255 +  }
   5.256 +
   5.257 +  return value;
   5.258 +}
   5.259 +
   5.260 +static inline char *
   5.261 +slave_result_str (player_t *player, slave_property_t property)
   5.262 +{
   5.263 +  return slave_result (property, player);
   5.264 +}
   5.265 +
   5.266 +/**
   5.267 + * Set a value for a property. Currently, only integer can be used as value.
   5.268 + * If a string must be set a day, then this function must be split like
   5.269 + * slave_get_property() and use two inlined functions for set int or str.
   5.270 + */
   5.271 +static void
   5.272 +slave_set_property_int (player_t *player, slave_property_t property, int value)
   5.273 +{
   5.274 +  mplayer_t *mplayer = NULL;
   5.275 +  char *prop;
   5.276 +  char cmd[SLAVE_CMD_BUFFER];
   5.277 +
   5.278 +  if (!player)
   5.279 +    return;
   5.280 +
   5.281 +  mplayer = (mplayer_t *) player->priv;
   5.282 +
   5.283 +  if (!mplayer || !mplayer->fifo_in)
   5.284 +    return;
   5.285 +
   5.286 +  /* get the right property in the global list */
   5.287 +  prop = get_prop (property);
   5.288 +  if (prop)
   5.289 +    sprintf (cmd, "set_property %s", prop);
   5.290 +  else
   5.291 +    /* should never going here */
   5.292 +    return;
   5.293 +
   5.294 +  switch (property) {
   5.295 +  case PROPERTY_LOOP:
   5.296 +  case PROPERTY_MUTE:
   5.297 +    send_to_slave (mplayer, "%s %i", cmd, value);
   5.298 +    break;
   5.299 +
   5.300 +  case PROPERTY_VOLUME:
   5.301 +    send_to_slave (mplayer, "%s %.2f", cmd, (float) value);
   5.302 +    break;
   5.303 +
   5.304 +  default:
   5.305 +    return;
   5.306 +  }
   5.307 +}
   5.308 +
   5.309 +/**
   5.310 + * Send a command to slave for get a property. That will return nothing
   5.311 + * because the response is grabbed with slave_result().
   5.312 + */
   5.313 +static void
   5.314 +slave_get_property (player_t *player, slave_property_t property)
   5.315 +{
   5.316 +  mplayer_t *mplayer = NULL;
   5.317 +  char *prop;
   5.318 +
   5.319 +  if (!player)
   5.320 +    return;
   5.321 +
   5.322 +  mplayer = (mplayer_t *) player->priv;
   5.323 +
   5.324 +  if (!mplayer || !mplayer->fifo_in)
   5.325 +    return;
   5.326 +
   5.327 +  /* get the right property in the global list */
   5.328 +  prop = get_prop (property);
   5.329 +  if (prop)
   5.330 +    send_to_slave (mplayer, "get_property %s", prop);
   5.331 +  else
   5.332 +    /* should never going here */
   5.333 +    return;
   5.334 +}
   5.335 +
   5.336 +static inline int
   5.337 +slave_get_property_int (player_t *player, slave_property_t property)
   5.338 +{
   5.339 +  slave_get_property (player, property);
   5.340 +  return slave_result_int (player, property);
   5.341 +}
   5.342 +
   5.343 +static inline char *
   5.344 +slave_get_property_str (player_t *player, slave_property_t property)
   5.345 +{
   5.346 +  slave_get_property (player, property);
   5.347 +  return slave_result_str (player, property);
   5.348 +}
   5.349 +
   5.350 +static void
   5.351 +slave_action (player_t *player, slave_cmd_t cmd, void *value)
   5.352 +{
   5.353 +  mplayer_t *mplayer = NULL;
   5.354 +
   5.355 +  if (!player)
   5.356 +    return;
   5.357 +
   5.358 +  mplayer = (mplayer_t *) player->priv;
   5.359 +
   5.360 +  if (!mplayer || !mplayer->fifo_in)
   5.361 +    return;
   5.362 +
   5.363 +  switch (cmd) {
   5.364 +  case SLAVE_LOADFILE:
   5.365 +    if (player->mrl->name)
   5.366 +      send_to_slave (mplayer, "loadfile \"%s\" %i",
   5.367 +                     player->mrl->name, *((int *) value));
   5.368 +    break;
   5.369 +
   5.370 +  case SLAVE_PAUSE:
   5.371 +    send_to_slave (mplayer, "pause");
   5.372 +    break;
   5.373 +
   5.374 +  case SLAVE_SEEK:
   5.375 +    send_to_slave (mplayer, "seek %.2f 0", *((float *) value));
   5.376 +    break;
   5.377 +
   5.378 +  case SLAVE_STOP:
   5.379 +    slave_set_property_int (player, PROPERTY_LOOP, -1);
   5.380 +    send_to_slave (mplayer, "seek 100.00 1");
   5.381 +    break;
   5.382 +
   5.383 +  default:
   5.384 +    return;
   5.385 +  }
   5.386 +}
   5.387 +
   5.388 +static inline void
   5.389 +slave_cmd (player_t *player, slave_cmd_t cmd)
   5.390 +{
   5.391 +  slave_action (player, cmd, NULL);
   5.392 +}
   5.393 +
   5.394 +static inline void
   5.395 +slave_cmd_int (player_t *player, slave_cmd_t cmd, int value)
   5.396 +{
   5.397 +  slave_action (player, cmd, &value);
   5.398 +}
   5.399 +
   5.400 +
   5.401 +/* Use only these commands for speak with MPlayer!
   5.402 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   5.403 + * void  slave_cmd              (player_t, slave_cmd_t)
   5.404 + * void  slave_cmd_int          (player_t, slave_cmd_t,      int)
   5.405 + * int   slave_get_property_int (player_t, slave_property_t)
   5.406 + * char *slave_get_property_str (player_t, slave_property_t)
   5.407 + * void  slave_set_property_int (player_t, slave_property_t, int)
   5.408 + */
   5.409 +
   5.410 +/**
   5.411 + * Init MPlayer as a forked process (son) and speak with libplayer (father)
   5.412 + * throught two pipes. One for send command to slave mode and a second
   5.413 + * for get results. MPlayer must not dead, else the pipes are broken and a
   5.414 + * SIGPIPE is sent to libplayer. In this case, a new init is necessary.
   5.415 + */
   5.416 +static init_status_t
   5.417 +mplayer_init (player_t *player)
   5.418 +{
   5.419 +  struct sigaction action;
   5.420 +  mplayer_t *mplayer = NULL;
   5.421 +  char *params[32];
   5.422 +  int pp = 0;
   5.423 +
   5.424 +  plog (MODULE_NAME, "init");
   5.425 +
   5.426 +  if (!player)
   5.427 +    return PLAYER_INIT_ERROR;
   5.428 +
   5.429 +  mplayer = (mplayer_t *) player->priv;
   5.430 +
   5.431 +  if (!mplayer)
   5.432 +    return PLAYER_INIT_ERROR;
   5.433 +
   5.434 +  /* action for SIGPIPE */
   5.435 +  action.sa_handler = sig_handler;
   5.436 +  sigemptyset (&action.sa_mask);
   5.437 +  action.sa_flags = 0;
   5.438 +  sigaction (SIGPIPE, &action, NULL);
   5.439 +
   5.440 +  /* create pipes */
   5.441 +  if (pipe (mplayer->pipe_in) != -1) {
   5.442 +    if (pipe (mplayer->pipe_out) != -1) {
   5.443 +      /* create the fork */
   5.444 +      mplayer->pid = fork ();
   5.445 +
   5.446 +      switch (mplayer->pid) {
   5.447 +      /* the son (a new hope) */
   5.448 +      case 0:
   5.449 +        close (mplayer->pipe_in[1]);
   5.450 +        close (mplayer->pipe_out[0]);
   5.451 +
   5.452 +        /* connect the pipe to MPlayer's stdin */
   5.453 +        dup2 (mplayer->pipe_in[0], STDIN_FILENO);
   5.454 +        /* connect stdout and stderr to the second pipe */
   5.455 +        dup2 (mplayer->pipe_out[1], STDERR_FILENO);
   5.456 +        dup2 (mplayer->pipe_out[1], STDOUT_FILENO);
   5.457 +
   5.458 +        /* default MPlayer arguments */
   5.459 +        params[pp++] = "mplayer";
   5.460 +        params[pp++] = "-slave";            /* work in slave mode */
   5.461 +        params[pp++] = "-quiet";            /* reduce output messages */
   5.462 +        params[pp++] = "-idle";             /* MPlayer stays always alive */
   5.463 +        params[pp++] = "-fs";               /* fullscreen (if possible) */
   5.464 +        params[pp++] = "-ontop";            /* ontop (if possible) */
   5.465 +        params[pp++] = "-noborder";         /* no border decoration */
   5.466 +        params[pp++] = "-nolirc";
   5.467 +        params[pp++] = "-nojoystick";
   5.468 +        params[pp++] = "-nomouseinput";
   5.469 +        params[pp++] = "-nograbpointer";
   5.470 +        params[pp++] = "-noconsolecontrols";
   5.471 +
   5.472 +        /* select the video output */
   5.473 +        params[pp++] = "-vo";
   5.474 +        /* TODO: possibility to add parameters for each video output */
   5.475 +        switch (player->vo) {
   5.476 +        case PLAYER_VO_NULL:
   5.477 +          params[pp++] = "null";
   5.478 +          break;
   5.479 +
   5.480 +        case PLAYER_VO_X11:
   5.481 +          params[pp++] = "x11";
   5.482 +          break;
   5.483 +
   5.484 +        /* with MPlayer, SDL is not specific to X11 */
   5.485 +        case PLAYER_VO_X11_SDL:
   5.486 +          params[pp++] = "sdl";
   5.487 +          break;
   5.488 +
   5.489 +        case PLAYER_VO_XV:
   5.490 +          params[pp++] = "xv";
   5.491 +          break;
   5.492 +
   5.493 +        case PLAYER_VO_FB:
   5.494 +          params[pp++] = "fbdev";
   5.495 +        }
   5.496 +
   5.497 +        /* select the audio output */
   5.498 +        params[pp++] = "-ao";
   5.499 +        /* TODO: possibility to add parameters for each audio output */
   5.500 +        switch (player->ao) {
   5.501 +        /* 'null' output seems to be bugged (MPlayer crash in some cases) */
   5.502 +        case PLAYER_AO_NULL:
   5.503 +          params[pp++] = "null";
   5.504 +          break;
   5.505 +
   5.506 +        case PLAYER_AO_ALSA:
   5.507 +          params[pp++] = "alsa";
   5.508 +          break;
   5.509 +
   5.510 +        case PLAYER_AO_OSS:
   5.511 +          params[pp++] = "oss";
   5.512 +        }
   5.513 +
   5.514 +        params[pp] = NULL;
   5.515 +
   5.516 +        /* launch MPlayer, if execvp returns there is an error */
   5.517 +        execvp ("mplayer", params);
   5.518 +
   5.519 +      case -1:
   5.520 +        break;
   5.521 +
   5.522 +      /* I'm your father */
   5.523 +      default:
   5.524 +        close (mplayer->pipe_in[0]);
   5.525 +        close (mplayer->pipe_out[1]);
   5.526 +
   5.527 +        mplayer->fifo_in = fdopen (mplayer->pipe_in[1], "w");
   5.528 +        mplayer->fifo_out = fdopen (mplayer->pipe_out[0], "r");
   5.529 +
   5.530 +        plog (MODULE_NAME, "MPlayer child loaded");
   5.531 +
   5.532 +        mplayer->status = MPLAYER_IS_IDLE;
   5.533 +
   5.534 +        return PLAYER_INIT_OK;
   5.535 +      }
   5.536 +    }
   5.537 +  }
   5.538 +
   5.539 +  return PLAYER_INIT_ERROR;
   5.540 +}
   5.541 +
   5.542 +static void
   5.543 +mplayer_uninit (void *priv)
   5.544 +{
   5.545 +  mplayer_t *mplayer = NULL;
   5.546 +
   5.547 +  plog (MODULE_NAME, "uninit");
   5.548 +
   5.549 +  if (!priv)
   5.550 +    return;
   5.551 +
   5.552 +  mplayer = (mplayer_t *) priv;
   5.553 +
   5.554 +  if (mplayer && mplayer->fifo_in) {
   5.555 +    /* suicide of MPlayer */
   5.556 +    send_to_slave (mplayer, "quit");
   5.557 +
   5.558 +    /* wait the death of MPlayer */
   5.559 +    waitpid (mplayer->pid, NULL, 0);
   5.560 +
   5.561 +    mplayer->status = MPLAYER_IS_DEAD;
   5.562 +
   5.563 +    /* close pipes */
   5.564 +    close (mplayer->pipe_in[1]);
   5.565 +    close (mplayer->pipe_out[0]);
   5.566 +
   5.567 +    /* close fifos */
   5.568 +    fclose (mplayer->fifo_in);
   5.569 +    fclose (mplayer->fifo_out);
   5.570 +
   5.571 +    plog (MODULE_NAME, "MPlayer child terminated");
   5.572 +  }
   5.573 +
   5.574 +  free (mplayer);
   5.575 +}
   5.576 +
   5.577 +static void
   5.578 +mplayer_mrl_get_audio_properties (player_t *player,
   5.579 +                                  mrl_properties_audio_t *audio)
   5.580 +{
   5.581 +  char *buffer_c;
   5.582 +  int buffer_i;
   5.583 +  mplayer_t *mplayer = NULL;
   5.584 +
   5.585 +  if (!player || !audio)
   5.586 +    return;
   5.587 +
   5.588 +  mplayer = (mplayer_t *) player->priv;
   5.589 +  /* FIXME: test for know if it's playing or not will be better */
   5.590 +  if (mplayer->status == MPLAYER_IS_DEAD)
   5.591 +    return;
   5.592 +
   5.593 +  buffer_c = slave_get_property_str (player, PROPERTY_AUDIO_CODEC);
   5.594 +  if (buffer_c) {
   5.595 +    audio->codec = strdup (buffer_c);
   5.596 +    free (buffer_c);
   5.597 +  }
   5.598 +  if (audio->codec)
   5.599 +    plog (MODULE_NAME, "Audio Codec: %s", audio->codec);
   5.600 +
   5.601 +  buffer_i = slave_get_property_int (player, PROPERTY_AUDIO_BITRATE);
   5.602 +  if (buffer_i > -1) {
   5.603 +    audio->bitrate = buffer_i;
   5.604 +    plog (MODULE_NAME, "Audio Bitrate: %i kbps", audio->bitrate / 1000);
   5.605 +  }
   5.606 +
   5.607 +  // TODO: autio->bits
   5.608 +
   5.609 +  buffer_i = slave_get_property_int (player, PROPERTY_CHANNELS);
   5.610 +  if (buffer_i > -1) {
   5.611 +    audio->channels = buffer_i;
   5.612 +    plog (MODULE_NAME, "Audio Channels: %i", audio->channels);
   5.613 +  }
   5.614 +
   5.615 +  buffer_i = slave_get_property_int (player, PROPERTY_SAMPLERATE);
   5.616 +  if (buffer_i > -1) {
   5.617 +    audio->samplerate = buffer_i;
   5.618 +    plog (MODULE_NAME, "Audio Sample Rate: %i Hz", audio->samplerate);
   5.619 +  }
   5.620 +}
   5.621 +
   5.622 +static void
   5.623 +mplayer_mrl_get_video_properties (player_t *player,
   5.624 +                                  mrl_properties_video_t *video)
   5.625 +{
   5.626 +  char *buffer_c;
   5.627 +  int buffer_i;
   5.628 +  mplayer_t *mplayer = NULL;
   5.629 +
   5.630 +  if (!player || !video)
   5.631 +    return;
   5.632 +
   5.633 +  mplayer = (mplayer_t *) player->priv;
   5.634 +  /* FIXME: test for know if it's playing or not will be better */
   5.635 +  if (mplayer->status == MPLAYER_IS_DEAD)
   5.636 +    return;
   5.637 +
   5.638 +  buffer_c = slave_get_property_str (player, PROPERTY_VIDEO_CODEC);
   5.639 +  if (buffer_c) {
   5.640 +    video->codec = strdup (buffer_c);
   5.641 +    free (buffer_c);
   5.642 +  }
   5.643 +  if (video->codec)
   5.644 +    plog (MODULE_NAME, "Video Codec: %s", video->codec);
   5.645 +
   5.646 +  buffer_i = slave_get_property_int (player, PROPERTY_VIDEO_BITRATE);
   5.647 +  if (buffer_i > -1) {
   5.648 +    video->bitrate = buffer_i;
   5.649 +    plog (MODULE_NAME, "Video Bitrate: %i kbps", video->bitrate / 1000);
   5.650 +  }
   5.651 +
   5.652 +  buffer_i = slave_get_property_int (player, PROPERTY_WIDTH);
   5.653 +  if (buffer_i > -1) {
   5.654 +    video->width = buffer_i;
   5.655 +    plog (MODULE_NAME, "Video Width: %i", video->width);
   5.656 +  }
   5.657 +
   5.658 +  buffer_i = slave_get_property_int (player, PROPERTY_HEIGHT);
   5.659 +  if (buffer_i > -1) {
   5.660 +    video->height = buffer_i;
   5.661 +    plog (MODULE_NAME, "Video Height: %i", video->height);
   5.662 +  }
   5.663 +
   5.664 +  // TODO: audio->channels and audio->streams
   5.665 +}
   5.666 +
   5.667 +static void
   5.668 +mplayer_mrl_get_properties (player_t *player)
   5.669 +{
   5.670 +  mplayer_t *mplayer = NULL;
   5.671 +  struct stat st;
   5.672 +  mrl_t *mrl;
   5.673 +
   5.674 +  plog (MODULE_NAME, "mrl_get_properties");
   5.675 +
   5.676 +  mrl = player->mrl;
   5.677 +
   5.678 +  if (!player || !mrl || !mrl->prop)
   5.679 +    return;
   5.680 +
   5.681 +  mplayer = (mplayer_t *) player->priv;
   5.682 +  if (mplayer->status == MPLAYER_IS_DEAD)
   5.683 +    return;
   5.684 +
   5.685 +  /* now fetch properties */
   5.686 +  stat (mrl->name, &st);
   5.687 +  mrl->prop->size = st.st_size;
   5.688 +  plog (MODULE_NAME, "File Size: %.2f MB",
   5.689 +        (float) mrl->prop->size / 1024 / 1024);
   5.690 +
   5.691 +  /* FIXME: no idea how implement */
   5.692 +  // mrl->prop->seekable =
   5.693 +
   5.694 +  mrl->prop->audio = mrl_properties_audio_new ();
   5.695 +  mplayer_mrl_get_audio_properties (player, mrl->prop->audio);
   5.696 +
   5.697 +  mrl->prop->video = mrl_properties_video_new ();
   5.698 +  mplayer_mrl_get_video_properties (player, mrl->prop->video);
   5.699 +}
   5.700 +
   5.701 +static void
   5.702 +mplayer_mrl_get_metadata (player_t *player)
   5.703 +{
   5.704 +  char *buffer_c;
   5.705 +  mplayer_t *mplayer = NULL;
   5.706 +  mrl_t *mrl;
   5.707 +
   5.708 +  plog (MODULE_NAME, "mrl_get_metadata");
   5.709 +
   5.710 +  mrl = player->mrl;
   5.711 +
   5.712 +  if (!player || !mrl || !mrl->meta)
   5.713 +    return;
   5.714 +
   5.715 +  mplayer = (mplayer_t *) player->priv;
   5.716 +  if (mplayer->status == MPLAYER_IS_DEAD)
   5.717 +    return;
   5.718 +
   5.719 +  /* now fetch metadata */
   5.720 +  buffer_c = slave_get_property_str (player, PROPERTY_METADATA_TITLE);
   5.721 +  if (buffer_c) {
   5.722 +    mrl->meta->title = strdup (buffer_c);
   5.723 +    free (buffer_c);
   5.724 +  }
   5.725 +  if (mrl->meta->title)
   5.726 +    plog (MODULE_NAME, "Meta Title: %s", mrl->meta->title);
   5.727 +
   5.728 +  buffer_c = slave_get_property_str (player, PROPERTY_METADATA_ARTIST);
   5.729 +  if (buffer_c) {
   5.730 +    mrl->meta->artist = strdup (buffer_c);
   5.731 +    free (buffer_c);
   5.732 +  }
   5.733 +  if (mrl->meta->artist)
   5.734 +    plog (MODULE_NAME, "Meta Artist: %s", mrl->meta->artist);
   5.735 +
   5.736 +  buffer_c = slave_get_property_str (player, PROPERTY_METADATA_GENRE);
   5.737 +  if (buffer_c) {
   5.738 +    mrl->meta->genre = strdup (buffer_c);
   5.739 +    free (buffer_c);
   5.740 +  }
   5.741 +  if (mrl->meta->genre)
   5.742 +    plog (MODULE_NAME, "Meta Genre: %s", mrl->meta->genre);
   5.743 +
   5.744 +  buffer_c = slave_get_property_str (player, PROPERTY_METADATA_ALBUM);
   5.745 +  if (buffer_c) {
   5.746 +    mrl->meta->album = strdup (buffer_c);
   5.747 +    free (buffer_c);
   5.748 +  }
   5.749 +  if (mrl->meta->album)
   5.750 +    plog (MODULE_NAME, "Meta Album: %s", mrl->meta->album);
   5.751 +
   5.752 +  buffer_c = slave_get_property_str (player, PROPERTY_METADATA_YEAR);
   5.753 +  if (buffer_c) {
   5.754 +    mrl->meta->year = strdup (buffer_c);
   5.755 +    free (buffer_c);
   5.756 +  }
   5.757 +  if (mrl->meta->year)
   5.758 +    plog (MODULE_NAME, "Meta Year: %s", mrl->meta->year);
   5.759 +
   5.760 +  buffer_c = slave_get_property_str (player, PROPERTY_METADATA_TRACK);
   5.761 +  if (buffer_c) {
   5.762 +    mrl->meta->track = strdup (buffer_c);
   5.763 +    free (buffer_c);
   5.764 +  }
   5.765 +  if (mrl->meta->track)
   5.766 +    plog (MODULE_NAME, "Meta Track: %s", mrl->meta->track);
   5.767 +}
   5.768 +
   5.769 +static playback_status_t
   5.770 +mplayer_playback_start (player_t *player)
   5.771 +{
   5.772 +  plog (MODULE_NAME, "playback_start");
   5.773 +
   5.774 +  if (!player)
   5.775 +    return PLAYER_PB_FATAL;
   5.776 +
   5.777 +  // FIXME: playback error if not loaded
   5.778 +  /* 0: new play, 1: append to the current playlist */
   5.779 +  slave_cmd_int (player, SLAVE_LOADFILE, 0);
   5.780 +
   5.781 +  return PLAYER_PB_OK;
   5.782 +}
   5.783 +
   5.784 +static void
   5.785 +mplayer_playback_stop (player_t *player)
   5.786 +{
   5.787 +  plog (MODULE_NAME, "playback_stop");
   5.788 +
   5.789 +  if (!player)
   5.790 +    return;
   5.791 +
   5.792 +  slave_cmd (player, SLAVE_STOP);
   5.793 +}
   5.794 +
   5.795 +static playback_status_t
   5.796 +mplayer_playback_pause (player_t *player)
   5.797 +{
   5.798 +  plog (MODULE_NAME, "playback_pause");
   5.799 +
   5.800 +  if (!player)
   5.801 +    return PLAYER_PB_FATAL;
   5.802 +
   5.803 +  slave_cmd (player, SLAVE_PAUSE);
   5.804 +
   5.805 +  return PLAYER_PB_OK;
   5.806 +}
   5.807 +
   5.808 +static void
   5.809 +mplayer_playback_seek (player_t *player, int value)
   5.810 +{
   5.811 +  plog (MODULE_NAME, "playback_seek: %d", value);
   5.812 +
   5.813 +  if (!player)
   5.814 +    return;
   5.815 +
   5.816 +  slave_cmd_int (player, SLAVE_SEEK, value);
   5.817 +}
   5.818 +
   5.819 +static int
   5.820 +mplayer_get_volume (player_t *player)
   5.821 +{
   5.822 +  int volume = 0;
   5.823 +
   5.824 +  plog (MODULE_NAME, "get_volume");
   5.825 +
   5.826 +  if (!player)
   5.827 +    return volume;
   5.828 +
   5.829 +  volume = slave_get_property_int (player, PROPERTY_VOLUME);
   5.830 +
   5.831 +  if (volume < 0)
   5.832 +    return -1;
   5.833 +
   5.834 +  return volume;
   5.835 +}
   5.836 +
   5.837 +static player_mute_t
   5.838 +mplayer_get_mute (player_t *player)
   5.839 +{
   5.840 +  plog (MODULE_NAME, "get_mute");
   5.841 +
   5.842 +  if (!player)
   5.843 +    return PLAYER_MUTE_UNKNOWN;
   5.844 +
   5.845 +  if (slave_get_property_int (player, PROPERTY_MUTE))
   5.846 +    return PLAYER_MUTE_ON;
   5.847 +
   5.848 +  return PLAYER_MUTE_OFF;
   5.849 +}
   5.850 +
   5.851 +static void
   5.852 +mplayer_set_volume (player_t *player, int value)
   5.853 +{
   5.854 +  plog (MODULE_NAME, "set_volume: %d", value);
   5.855 +
   5.856 +  if (!player)
   5.857 +    return;
   5.858 +
   5.859 +  slave_set_property_int (player, PROPERTY_VOLUME, value);
   5.860 +}
   5.861 +
   5.862 +static void
   5.863 +mplayer_set_mute (player_t *player, player_mute_t value)
   5.864 +{
   5.865 +  int mute = 0;
   5.866 +
   5.867 +  if (value == PLAYER_MUTE_UNKNOWN)
   5.868 +    return;
   5.869 +
   5.870 +  if (value == PLAYER_MUTE_ON)
   5.871 +    mute = 1;
   5.872 +
   5.873 +  plog (MODULE_NAME, "set_mute: %s", mute ? "on" : "off");
   5.874 +
   5.875 +  if (!player)
   5.876 +    return;
   5.877 +
   5.878 +  slave_set_property_int (player, PROPERTY_MUTE, mute);
   5.879 +}
   5.880 +
   5.881 +/* public API */
   5.882 +player_funcs_t *
   5.883 +register_functions_mplayer (void)
   5.884 +{
   5.885 +  player_funcs_t *funcs = NULL;
   5.886 +
   5.887 +  funcs = (player_funcs_t *) malloc (sizeof (player_funcs_t));
   5.888 +  funcs->init = mplayer_init;
   5.889 +  funcs->uninit = mplayer_uninit;
   5.890 +  funcs->mrl_get_props = mplayer_mrl_get_properties;
   5.891 +  funcs->mrl_get_meta = mplayer_mrl_get_metadata;
   5.892 +  funcs->pb_start = mplayer_playback_start;
   5.893 +  funcs->pb_stop = mplayer_playback_stop;
   5.894 +  funcs->pb_pause = mplayer_playback_pause;
   5.895 +  funcs->pb_seek = mplayer_playback_seek;
   5.896 +  funcs->get_volume = mplayer_get_volume;
   5.897 +  funcs->get_mute = mplayer_get_mute;
   5.898 +  funcs->set_volume = mplayer_set_volume;
   5.899 +  funcs->set_mute = mplayer_set_mute;
   5.900 +
   5.901 +  return funcs;
   5.902 +}
   5.903 +
   5.904 +void *
   5.905 +register_private_mplayer (void)
   5.906 +{
   5.907 +  mplayer_t *mplayer = NULL;
   5.908 +
   5.909 +  mplayer = (mplayer_t *) malloc (sizeof (mplayer_t));
   5.910 +
   5.911 +  mplayer->status = MPLAYER_IS_DEAD;
   5.912 +  mplayer->fifo_in = NULL;
   5.913 +  mplayer->fifo_out = NULL;
   5.914 +
   5.915 +  return mplayer;
   5.916 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/wrapper_mplayer.h	Thu Jul 26 13:02:44 2007 +0200
     6.3 @@ -0,0 +1,28 @@
     6.4 +/*
     6.5 + * GeeXboX libplayer: a multimedia A/V abstraction layer API.
     6.6 + * Copyright (C) 2007 Mathieu Schroeter <mathieu.schroeter@gamesover.ch>
     6.7 + *
     6.8 + * This file is part of libplayer.
     6.9 + *
    6.10 + * libplayer is free software; you can redistribute it and/or
    6.11 + * modify it under the terms of the GNU Lesser General Public
    6.12 + * License as published by the Free Software Foundation; either
    6.13 + * version 2.1 of the License, or (at your option) any later version.
    6.14 + *
    6.15 + * libplayer is distributed in the hope that it will be useful,
    6.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    6.18 + * Lesser General Public License for more details.
    6.19 + *
    6.20 + * You should have received a copy of the GNU Lesser General Public
    6.21 + * License along with libplayer; if not, write to the Free Software
    6.22 + * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    6.23 + */
    6.24 +
    6.25 +#ifndef _WRAPPER_MPLAYER_H_
    6.26 +#define _WRAPPER_MPLAYER_H_
    6.27 +
    6.28 +player_funcs_t * register_functions_mplayer (void);
    6.29 +void *register_private_mplayer (void);
    6.30 +
    6.31 +#endif /* _WRAPPER_MPLAYER_H_ */