vlm.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:37k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * vlm.c: VLM interface plugin
  3.  *****************************************************************************
  4.  * Copyright (C) 2000-2005 the VideoLAN team
  5.  * $Id: d1ef0964fdc58859da19d6c546ac0c21406972ed $
  6.  *
  7.  * Authors: Simon Latapie <garf@videolan.org>
  8.  *          Laurent Aimar <fenrir@videolan.org>
  9.  *          Gildas Bazin <gbazin@videolan.org>
  10.  *
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  24.  *****************************************************************************/
  25. /*****************************************************************************
  26.  * Preamble
  27.  *****************************************************************************/
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <stdio.h>
  33. #include <ctype.h>                                              /* tolower() */
  34. #include <assert.h>
  35. #include <vlc_vlm.h>
  36. #ifndef WIN32
  37. #   include <sys/time.h>                                   /* gettimeofday() */
  38. #endif
  39. #ifdef UNDER_CE
  40. #include <sys/time.h>                                      /* gettimeofday() */
  41. #endif
  42. #include <time.h>                                                 /* ctime() */
  43. #if defined (WIN32) && !defined (UNDER_CE)
  44. #include <sys/timeb.h>                                            /* ftime() */
  45. #endif
  46. #include <vlc_input.h>
  47. #include <vlc_stream.h>
  48. #include "vlm_internal.h"
  49. #include "vlm_event.h"
  50. #include <vlc_vod.h>
  51. #include <vlc_charset.h>
  52. #include <vlc_sout.h>
  53. #include "../stream_output/stream_output.h"
  54. #include "../libvlc.h"
  55. /*****************************************************************************
  56.  * Local prototypes.
  57.  *****************************************************************************/
  58. static void vlm_Destructor( vlm_t *p_vlm );
  59. static void* Manage( void * );
  60. static int vlm_MediaVodControl( void *, vod_media_t *, const char *, int, va_list );
  61. /*****************************************************************************
  62.  * vlm_New:
  63.  *****************************************************************************/
  64. vlm_t *__vlm_New ( vlc_object_t *p_this )
  65. {
  66.     vlc_value_t lockval;
  67.     vlm_t *p_vlm = NULL, **pp_vlm = &(libvlc_priv (p_this->p_libvlc)->p_vlm);
  68.     char *psz_vlmconf;
  69.     static const char vlm_object_name[] = "vlm daemon";
  70.     /* Avoid multiple creation */
  71.     if( var_Create( p_this->p_libvlc, "vlm_mutex", VLC_VAR_MUTEX ) ||
  72.         var_Get( p_this->p_libvlc, "vlm_mutex", &lockval ) )
  73.         return NULL;
  74.     vlc_mutex_lock( lockval.p_address );
  75.     p_vlm = *pp_vlm;
  76.     if( p_vlm )
  77.     {   /* VLM already exists */
  78.         vlc_object_hold( p_vlm );
  79.         vlc_mutex_unlock( lockval.p_address );
  80.         return p_vlm;
  81.     }
  82.     msg_Dbg( p_this, "creating VLM" );
  83.     p_vlm = vlc_custom_create( p_this, sizeof( *p_vlm ), VLC_OBJECT_GENERIC,
  84.                                vlm_object_name );
  85.     if( !p_vlm )
  86.     {
  87.         vlc_mutex_unlock( lockval.p_address );
  88.         return NULL;
  89.     }
  90.     vlc_mutex_init( &p_vlm->lock );
  91.     p_vlm->i_id = 1;
  92.     TAB_INIT( p_vlm->i_media, p_vlm->media );
  93.     TAB_INIT( p_vlm->i_schedule, p_vlm->schedule );
  94.     p_vlm->i_vod = 0;
  95.     p_vlm->p_vod = NULL;
  96.     var_Create( p_vlm, "intf-event", VLC_VAR_ADDRESS );
  97.     vlc_object_attach( p_vlm, p_this->p_libvlc );
  98.     if( vlc_clone( &p_vlm->thread, Manage, p_vlm, VLC_THREAD_PRIORITY_LOW ) )
  99.     {
  100.         vlc_mutex_destroy( &p_vlm->lock );
  101.         vlc_object_release( p_vlm );
  102.         return NULL;
  103.     }
  104.     /* Load our configuration file */
  105.     psz_vlmconf = var_CreateGetString( p_vlm, "vlm-conf" );
  106.     if( psz_vlmconf && *psz_vlmconf )
  107.     {
  108.         vlm_message_t *p_message = NULL;
  109.         char *psz_buffer = NULL;
  110.         msg_Dbg( p_this, "loading VLM configuration" );
  111.         if( asprintf(&psz_buffer, "load %s", psz_vlmconf ) != -1 )
  112.         {
  113.             msg_Dbg( p_this, "%s", psz_buffer );
  114.             if( vlm_ExecuteCommand( p_vlm, psz_buffer, &p_message ) )
  115.                 msg_Warn( p_this, "error while loading the configuration file" );
  116.             vlm_MessageDelete( p_message );
  117.             free( psz_buffer );
  118.         }
  119.     }
  120.     free( psz_vlmconf );
  121.     vlc_object_set_destructor( p_vlm, (vlc_destructor_t)vlm_Destructor );
  122.     *pp_vlm = p_vlm; /* for future reference */
  123.     vlc_mutex_unlock( lockval.p_address );
  124.     return p_vlm;
  125. }
  126. /*****************************************************************************
  127.  * vlm_Delete:
  128.  *****************************************************************************/
  129. void vlm_Delete( vlm_t *p_vlm )
  130. {
  131.     vlc_value_t lockval;
  132.     /* vlm_Delete() is serialized against itself, and against vlm_New().
  133.      * This way, vlm_Destructor () (called from vlc_objet_release() above)
  134.      * is serialized against setting libvlc_priv->p_vlm from vlm_New(). */
  135.     var_Get( p_vlm->p_libvlc, "vlm_mutex", &lockval );
  136.     vlc_mutex_lock( lockval.p_address );
  137.     vlc_object_release( p_vlm );
  138.     vlc_mutex_unlock( lockval.p_address );
  139. }
  140. /*****************************************************************************
  141.  * vlm_Destructor:
  142.  *****************************************************************************/
  143. static void vlm_Destructor( vlm_t *p_vlm )
  144. {
  145.     libvlc_priv (p_vlm->p_libvlc)->p_vlm = NULL;
  146.     vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
  147.     TAB_CLEAN( p_vlm->i_media, p_vlm->media );
  148.     vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
  149.     TAB_CLEAN( p_vlm->schedule, p_vlm->schedule );
  150.     vlc_object_kill( p_vlm );
  151.     /*vlc_cancel( p_vlm->thread ); */
  152.     vlc_join( p_vlm->thread, NULL );
  153.     vlc_mutex_destroy( &p_vlm->lock );
  154. }
  155. /*****************************************************************************
  156.  * vlm_ExecuteCommand:
  157.  *****************************************************************************/
  158. int vlm_ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
  159.                         vlm_message_t **pp_message)
  160. {
  161.     int i_result;
  162.     vlc_mutex_lock( &p_vlm->lock );
  163.     i_result = ExecuteCommand( p_vlm, psz_command, pp_message );
  164.     vlc_mutex_unlock( &p_vlm->lock );
  165.     return i_result;
  166. }
  167. int64_t vlm_Date(void)
  168. {
  169. #if defined (WIN32) && !defined (UNDER_CE)
  170.     struct timeb tm;
  171.     ftime( &tm );
  172.     return ((int64_t)tm.time) * 1000000 + ((int64_t)tm.millitm) * 1000;
  173. #else
  174.     struct timeval tv_date;
  175.     /* gettimeofday() cannot fail given &tv_date is a valid address */
  176.     (void)gettimeofday( &tv_date, NULL );
  177.     return (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec;
  178. #endif
  179. }
  180. /*****************************************************************************
  181.  *
  182.  *****************************************************************************/
  183. static int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
  184.                                 const char *psz_id, int i_query, va_list args )
  185. {
  186.     vlm_t *vlm = (vlm_t *)p_private;
  187.     int i, i_ret;
  188.     const char *psz;
  189.     int64_t id;
  190.     if( !p_private || !p_vod_media )
  191.         return VLC_EGENERIC;
  192.     vlc_mutex_lock( &vlm->lock );
  193.     /* Find media id */
  194.     for( i = 0, id = -1; i < vlm->i_media; i++ )
  195.     {
  196.         if( p_vod_media == vlm->media[i]->vod.p_media )
  197.         {
  198.             id = vlm->media[i]->cfg.id;
  199.             break;
  200.         }
  201.     }
  202.     if( id == -1 )
  203.     {
  204.         vlc_mutex_unlock( &vlm->lock );
  205.         return VLC_EGENERIC;
  206.     }
  207.     switch( i_query )
  208.     {
  209.     case VOD_MEDIA_PLAY:
  210.         psz = (const char *)va_arg( args, const char * );
  211.         i_ret = vlm_ControlInternal( vlm, VLM_START_MEDIA_VOD_INSTANCE, id, psz_id, 0, psz );
  212.         break;
  213.     case VOD_MEDIA_PAUSE:
  214.         i_ret = vlm_ControlInternal( vlm, VLM_PAUSE_MEDIA_INSTANCE, id, psz_id );
  215.         break;
  216.     case VOD_MEDIA_STOP:
  217.         i_ret = vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, id, psz_id );
  218.         break;
  219.     case VOD_MEDIA_SEEK:
  220.     {
  221.         double d_position = (double)va_arg( args, double );
  222.         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position/100.0 );
  223.         break;
  224.     }
  225.     case VOD_MEDIA_REWIND:
  226.     {
  227.         double d_scale = (double)va_arg( args, double );
  228.         double d_position;
  229.         vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
  230.         d_position -= (d_scale / 1000.0);
  231.         if( d_position < 0.0 )
  232.             d_position = 0.0;
  233.         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
  234.         break;
  235.     }
  236.     case VOD_MEDIA_FORWARD:
  237.     {
  238.         double d_scale = (double)va_arg( args, double );
  239.         double d_position;
  240.         vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
  241.         d_position += (d_scale / 1000.0);
  242.         if( d_position > 1.0 )
  243.             d_position = 1.0;
  244.         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
  245.         break;
  246.     }
  247.     default:
  248.         i_ret = VLC_EGENERIC;
  249.         break;
  250.     }
  251.     vlc_mutex_unlock( &vlm->lock );
  252.     return i_ret;
  253. }
  254. /*****************************************************************************
  255.  * Manage:
  256.  *****************************************************************************/
  257. static void* Manage( void* p_object )
  258. {
  259.     vlm_t *vlm = (vlm_t*)p_object;
  260.     int i, j;
  261.     mtime_t i_lastcheck;
  262.     mtime_t i_time;
  263.     int canc = vlc_savecancel ();
  264.     i_lastcheck = vlm_Date();
  265.     while( !vlm->b_die )
  266.     {
  267.         char **ppsz_scheduled_commands = NULL;
  268.         int    i_scheduled_commands = 0;
  269.         vlc_mutex_lock( &vlm->lock );
  270.         /* destroy the inputs that wants to die, and launch the next input */
  271.         for( i = 0; i < vlm->i_media; i++ )
  272.         {
  273.             vlm_media_sys_t *p_media = vlm->media[i];
  274.             for( j = 0; j < p_media->i_instance; )
  275.             {
  276.                 vlm_media_instance_sys_t *p_instance = p_media->instance[j];
  277.                 if( p_instance->p_input && ( p_instance->p_input->b_eof || p_instance->p_input->b_error ) )
  278.                 {
  279.                     int i_new_input_index;
  280.                     /* */
  281.                     i_new_input_index = p_instance->i_index + 1;
  282.                     if( !p_media->cfg.b_vod && p_media->cfg.broadcast.b_loop && i_new_input_index >= p_media->cfg.i_input )
  283.                         i_new_input_index = 0;
  284.                     /* FIXME implement multiple input with VOD */
  285.                     if( p_media->cfg.b_vod || i_new_input_index >= p_media->cfg.i_input )
  286.                         vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, p_instance->psz_name );
  287.                     else
  288.                         vlm_ControlInternal( vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, p_instance->psz_name, i_new_input_index );
  289.                     j = 0;
  290.                 }
  291.                 else
  292.                 {
  293.                     j++;
  294.                 }
  295.             }
  296.         }
  297.         /* scheduling */
  298.         i_time = vlm_Date();
  299.         for( i = 0; i < vlm->i_schedule; i++ )
  300.         {
  301.             mtime_t i_real_date = vlm->schedule[i]->i_date;
  302.             if( vlm->schedule[i]->b_enabled == true )
  303.             {
  304.                 if( vlm->schedule[i]->i_date == 0 ) // now !
  305.                 {
  306.                     vlm->schedule[i]->i_date = (i_time / 1000000) * 1000000 ;
  307.                     i_real_date = i_time;
  308.                 }
  309.                 else if( vlm->schedule[i]->i_period != 0 )
  310.                 {
  311.                     int j = 0;
  312.                     while( vlm->schedule[i]->i_date + j *
  313.                            vlm->schedule[i]->i_period <= i_lastcheck &&
  314.                            ( vlm->schedule[i]->i_repeat > j ||
  315.                              vlm->schedule[i]->i_repeat == -1 ) )
  316.                     {
  317.                         j++;
  318.                     }
  319.                     i_real_date = vlm->schedule[i]->i_date + j *
  320.                         vlm->schedule[i]->i_period;
  321.                 }
  322.                 if( i_real_date <= i_time && i_real_date > i_lastcheck )
  323.                 {
  324.                     for( j = 0; j < vlm->schedule[i]->i_command; j++ )
  325.                     {
  326.                         TAB_APPEND( i_scheduled_commands,
  327.                                     ppsz_scheduled_commands,
  328.                                     strdup(vlm->schedule[i]->command[j] ) );
  329.                     }
  330.                 }
  331.             }
  332.         }
  333.         while( i_scheduled_commands )
  334.         {
  335.             vlm_message_t *message = NULL;
  336.             char *psz_command = ppsz_scheduled_commands[0];
  337.             ExecuteCommand( vlm, psz_command,&message );
  338.             /* for now, drop the message */
  339.             vlm_MessageDelete( message );
  340.             TAB_REMOVE( i_scheduled_commands,
  341.                         ppsz_scheduled_commands,
  342.                         psz_command );
  343.             free( psz_command );
  344.         }
  345.         i_lastcheck = i_time;
  346.         vlc_mutex_unlock( &vlm->lock );
  347.         msleep( 100000 );
  348.     }
  349.     vlc_restorecancel (canc);
  350.     return NULL;
  351. }
  352. /* New API
  353.  */
  354. /*
  355. typedef struct
  356. {
  357.     struct
  358.     {
  359.         int i_connection_count;
  360.         int i_connection_active;
  361.     } vod;
  362.     struct
  363.     {
  364.         int        i_count;
  365.         bool b_playing;
  366.         int        i_playing_index;
  367.     } broadcast;
  368. } vlm_media_status_t;
  369. */
  370. /* */
  371. static vlm_media_sys_t *vlm_ControlMediaGetById( vlm_t *p_vlm, int64_t id )
  372. {
  373.     int i;
  374.     for( i = 0; i < p_vlm->i_media; i++ )
  375.     {
  376.         if( p_vlm->media[i]->cfg.id == id )
  377.             return p_vlm->media[i];
  378.     }
  379.     return NULL;
  380. }
  381. static vlm_media_sys_t *vlm_ControlMediaGetByName( vlm_t *p_vlm, const char *psz_name )
  382. {
  383.     int i;
  384.     for( i = 0; i < p_vlm->i_media; i++ )
  385.     {
  386.         if( !strcmp( p_vlm->media[i]->cfg.psz_name, psz_name ) )
  387.             return p_vlm->media[i];
  388.     }
  389.     return NULL;
  390. }
  391. static int vlm_MediaDescriptionCheck( vlm_t *p_vlm, vlm_media_t *p_cfg )
  392. {
  393.     int i;
  394.     if( !p_cfg || !p_cfg->psz_name ||
  395.         !strcmp( p_cfg->psz_name, "all" ) || !strcmp( p_cfg->psz_name, "media" ) || !strcmp( p_cfg->psz_name, "schedule" ) )
  396.         return VLC_EGENERIC;
  397.     for( i = 0; i < p_vlm->i_media; i++ )
  398.     {
  399.         if( p_vlm->media[i]->cfg.id == p_cfg->id )
  400.             continue;
  401.         if( !strcmp( p_vlm->media[i]->cfg.psz_name, p_cfg->psz_name ) )
  402.             return VLC_EGENERIC;
  403.     }
  404.     return VLC_SUCCESS;
  405. }
  406. /* Called after a media description is changed/added */
  407. static int vlm_OnMediaUpdate( vlm_t *p_vlm, vlm_media_sys_t *p_media )
  408. {
  409.     vlm_media_t *p_cfg = &p_media->cfg;
  410.     /* Check if we need to create/delete a vod media */
  411.     if( p_cfg->b_vod )
  412.     {
  413.         if( !p_cfg->b_enabled && p_media->vod.p_media )
  414.         {
  415.             p_vlm->p_vod->pf_media_del( p_vlm->p_vod, p_media->vod.p_media );
  416.             p_media->vod.p_media = NULL;
  417.         }
  418.         else if( p_cfg->b_enabled && !p_media->vod.p_media && p_cfg->i_input )
  419.         {
  420.             /* Pre-parse the input */
  421.             input_thread_t *p_input;
  422.             char *psz_output;
  423.             char *psz_header;
  424.             char *psz_dup;
  425.             int i;
  426.             vlc_gc_decref( p_media->vod.p_item );
  427.             p_media->vod.p_item = input_item_New( p_vlm, p_cfg->ppsz_input[0],
  428.                 p_cfg->psz_name );
  429.             if( p_cfg->psz_output )
  430.             {
  431.                 if( asprintf( &psz_output, "%s:description", p_cfg->psz_output )  == -1 )
  432.                     psz_output = NULL;
  433.             }
  434.             else
  435.                 psz_output = strdup( "#description" );
  436.             if( psz_output && asprintf( &psz_dup, "sout=%s", psz_output ) != -1 )
  437.             {
  438.                 input_item_AddOption( p_media->vod.p_item, psz_dup, VLC_INPUT_OPTION_TRUSTED );
  439.                 free( psz_dup );
  440.             }
  441.             free( psz_output );
  442.             for( i = 0; i < p_cfg->i_option; i++ )
  443.                 input_item_AddOption( p_media->vod.p_item,
  444.                                       p_cfg->ppsz_option[i], VLC_INPUT_OPTION_TRUSTED );
  445.             if( asprintf( &psz_header, _("Media: %s"), p_cfg->psz_name ) == -1 )
  446.                 psz_header = NULL;
  447.             if( (p_input = input_CreateAndStart( p_vlm->p_libvlc, p_media->vod.p_item, psz_header ) ) )
  448.             {
  449.                 while( !p_input->b_eof && !p_input->b_error )
  450.                     msleep( 100000 );
  451.                 input_Stop( p_input, false );
  452.                 vlc_thread_join( p_input );
  453.                 vlc_object_release( p_input );
  454.             }
  455.             free( psz_header );
  456.             if( p_cfg->vod.psz_mux )
  457.             {
  458.                 input_item_t item;
  459.                 es_format_t es, *p_es = &es;
  460.                 union { char text[5]; uint32_t value; } fourcc;
  461.                 sprintf( fourcc.text, "%4.4s", p_cfg->vod.psz_mux );
  462.                 fourcc.text[0] = tolower(fourcc.text[0]);
  463.                 fourcc.text[1] = tolower(fourcc.text[1]);
  464.                 fourcc.text[2] = tolower(fourcc.text[2]);
  465.                 fourcc.text[3] = tolower(fourcc.text[3]);
  466.                 /* XXX: Don't do it that way, but properly use a new input item ref. */
  467.                 item = *p_media->vod.p_item;
  468.                 item.i_es = 1;
  469.                 item.es = &p_es;
  470.                 es_format_Init( &es, VIDEO_ES, fourcc.value );
  471.                 p_media->vod.p_media =
  472.                     p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, &item );
  473.             }
  474.             else
  475.             {
  476.                 p_media->vod.p_media =
  477.                     p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, p_media->vod.p_item );
  478.             }
  479.         }
  480.     }
  481.     else
  482.     {
  483.         /* TODO start media if needed */
  484.     }
  485.     /* TODO add support of var vlm_media_broadcast/vlm_media_vod */
  486.     vlm_SendEventMediaChanged( p_vlm, p_cfg->id, p_cfg->psz_name );
  487.     return VLC_SUCCESS;
  488. }
  489. static int vlm_ControlMediaChange( vlm_t *p_vlm, vlm_media_t *p_cfg )
  490. {
  491.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, p_cfg->id );
  492.     /* */
  493.     if( !p_media || vlm_MediaDescriptionCheck( p_vlm, p_cfg ) )
  494.         return VLC_EGENERIC;
  495.     if( ( p_media->cfg.b_vod && !p_cfg->b_vod ) || ( !p_media->cfg.b_vod && p_cfg->b_vod ) )
  496.         return VLC_EGENERIC;
  497.     if( 0 )
  498.     {
  499.         /* TODO check what are the changes being done (stop instance if needed) */
  500.     }
  501.     vlm_media_Clean( &p_media->cfg );
  502.     vlm_media_Copy( &p_media->cfg, p_cfg );
  503.     return vlm_OnMediaUpdate( p_vlm, p_media );
  504. }
  505. static int vlm_ControlMediaAdd( vlm_t *p_vlm, vlm_media_t *p_cfg, int64_t *p_id )
  506. {
  507.     vlm_media_sys_t *p_media;
  508.     if( vlm_MediaDescriptionCheck( p_vlm, p_cfg ) || vlm_ControlMediaGetByName( p_vlm, p_cfg->psz_name ) )
  509.     {
  510.         msg_Err( p_vlm, "invalid media description" );
  511.         return VLC_EGENERIC;
  512.     }
  513.     /* Check if we need to load the VOD server */
  514.     if( p_cfg->b_vod && !p_vlm->i_vod )
  515.     {
  516.         p_vlm->p_vod = vlc_custom_create( VLC_OBJECT(p_vlm), sizeof( vod_t ),
  517.                                           VLC_OBJECT_GENERIC, "vod server" );
  518.         vlc_object_attach( p_vlm->p_vod, p_vlm->p_libvlc );
  519.         p_vlm->p_vod->p_module = module_need( p_vlm->p_vod, "vod server", NULL, false );
  520.         if( !p_vlm->p_vod->p_module )
  521.         {
  522.             msg_Err( p_vlm, "cannot find vod server" );
  523.             vlc_object_detach( p_vlm->p_vod );
  524.             vlc_object_release( p_vlm->p_vod );
  525.             p_vlm->p_vod = NULL;
  526.             return VLC_EGENERIC;
  527.         }
  528.         p_vlm->p_vod->p_data = p_vlm;
  529.         p_vlm->p_vod->pf_media_control = vlm_MediaVodControl;
  530.     }
  531.     p_media = calloc( 1, sizeof( vlm_media_sys_t ) );
  532.     if( !p_media )
  533.         return VLC_ENOMEM;
  534.     if( p_cfg->b_vod )
  535.         p_vlm->i_vod++;
  536.     vlm_media_Copy( &p_media->cfg, p_cfg );
  537.     p_media->cfg.id = p_vlm->i_id++;
  538.     /* FIXME do we do something here if enabled is true ? */
  539.     p_media->vod.p_item = input_item_New( p_vlm, NULL, NULL );
  540.     p_media->vod.p_media = NULL;
  541.     TAB_INIT( p_media->i_instance, p_media->instance );
  542.     /* */
  543.     TAB_APPEND( p_vlm->i_media, p_vlm->media, p_media );
  544.     if( p_id )
  545.         *p_id = p_media->cfg.id;
  546.     /* */
  547.     vlm_SendEventMediaAdded( p_vlm, p_media->cfg.id, p_media->cfg.psz_name );
  548.     return vlm_OnMediaUpdate( p_vlm, p_media );
  549. }
  550. static int vlm_ControlMediaDel( vlm_t *p_vlm, int64_t id )
  551. {
  552.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  553.     if( !p_media )
  554.         return VLC_EGENERIC;
  555.     while( p_media->i_instance > 0 )
  556.         vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, id, p_media->instance[0]->psz_name );
  557.     if( p_media->cfg.b_vod )
  558.     {
  559.         p_media->cfg.b_enabled = false;
  560.         vlm_OnMediaUpdate( p_vlm, p_media );
  561.         p_vlm->i_vod--;
  562.     }
  563.     /* */
  564.     vlm_SendEventMediaRemoved( p_vlm, id, p_media->cfg.psz_name );
  565.     vlm_media_Clean( &p_media->cfg );
  566.     vlc_gc_decref( p_media->vod.p_item );
  567.     TAB_REMOVE( p_vlm->i_media, p_vlm->media, p_media );
  568.     free( p_media );
  569.     /* Check if we need to unload the VOD server */
  570.     if( p_vlm->p_vod && p_vlm->i_vod <= 0 )
  571.     {
  572.         module_unneed( p_vlm->p_vod, p_vlm->p_vod->p_module );
  573.         vlc_object_detach( p_vlm->p_vod );
  574.         vlc_object_release( p_vlm->p_vod );
  575.         p_vlm->p_vod = NULL;
  576.     }
  577.     return VLC_SUCCESS;
  578. }
  579. static int vlm_ControlMediaGets( vlm_t *p_vlm, vlm_media_t ***ppp_dsc, int *pi_dsc )
  580. {
  581.     vlm_media_t **pp_dsc;
  582.     int                     i_dsc;
  583.     int i;
  584.     TAB_INIT( i_dsc, pp_dsc );
  585.     for( i = 0; i < p_vlm->i_media; i++ )
  586.     {
  587.         vlm_media_t *p_dsc = vlm_media_Duplicate( &p_vlm->media[i]->cfg );
  588.         TAB_APPEND( i_dsc, pp_dsc, p_dsc );
  589.     }
  590.     *ppp_dsc = pp_dsc;
  591.     *pi_dsc = i_dsc;
  592.     return VLC_SUCCESS;
  593. }
  594. static int vlm_ControlMediaClear( vlm_t *p_vlm )
  595. {
  596.     while( p_vlm->i_media > 0 )
  597.         vlm_ControlMediaDel( p_vlm, p_vlm->media[0]->cfg.id );
  598.     return VLC_SUCCESS;
  599. }
  600. static int vlm_ControlMediaGet( vlm_t *p_vlm, int64_t id, vlm_media_t **pp_dsc )
  601. {
  602.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  603.     if( !p_media )
  604.         return VLC_EGENERIC;
  605.     *pp_dsc = vlm_media_Duplicate( &p_media->cfg );
  606.     return VLC_SUCCESS;
  607. }
  608. static int vlm_ControlMediaGetId( vlm_t *p_vlm, const char *psz_name, int64_t *p_id )
  609. {
  610.     vlm_media_sys_t *p_media = vlm_ControlMediaGetByName( p_vlm, psz_name );
  611.     if( !p_media )
  612.         return VLC_EGENERIC;
  613.     *p_id = p_media->cfg.id;
  614.     return VLC_SUCCESS;
  615. }
  616. static vlm_media_instance_sys_t *vlm_ControlMediaInstanceGetByName( vlm_media_sys_t *p_media, const char *psz_id )
  617. {
  618.     int i;
  619.     for( i = 0; i < p_media->i_instance; i++ )
  620.     {
  621.         const char *psz = p_media->instance[i]->psz_name;
  622.         if( ( psz == NULL && psz_id == NULL ) ||
  623.             ( psz && psz_id && !strcmp( psz, psz_id ) ) )
  624.             return p_media->instance[i];
  625.     }
  626.     return NULL;
  627. }
  628. static vlm_media_instance_sys_t *vlm_MediaInstanceNew( vlm_t *p_vlm, const char *psz_name )
  629. {
  630.     vlm_media_instance_sys_t *p_instance = calloc( 1, sizeof(vlm_media_instance_sys_t) );
  631.     if( !p_instance )
  632.         return NULL;
  633.     p_instance->psz_name = NULL;
  634.     if( psz_name )
  635.         p_instance->psz_name = strdup( psz_name );
  636.     p_instance->p_item = input_item_New( p_vlm, NULL, NULL );
  637.     p_instance->i_index = 0;
  638.     p_instance->b_sout_keep = false;
  639.     p_instance->p_input = NULL;
  640.     p_instance->p_input_resource = NULL;
  641.     return p_instance;
  642. }
  643. static void vlm_MediaInstanceDelete( vlm_t *p_vlm, int64_t id, vlm_media_instance_sys_t *p_instance, const char *psz_name )
  644. {
  645.     input_thread_t *p_input = p_instance->p_input;
  646.     if( p_input )
  647.     {
  648.         input_resource_t *p_resource;
  649.         input_Stop( p_input, true );
  650.         vlc_thread_join( p_input );
  651.         p_resource = input_DetachResource( p_input );
  652.         input_resource_Delete( p_resource );
  653.         vlc_object_release( p_input );
  654.         vlm_SendEventMediaInstanceStopped( p_vlm, id, psz_name );
  655.     }
  656.     if( p_instance->p_input_resource )
  657.         input_resource_Delete( p_instance->p_input_resource );
  658.     vlc_gc_decref( p_instance->p_item );
  659.     free( p_instance->psz_name );
  660.     free( p_instance );
  661. }
  662. static int vlm_ControlMediaInstanceStart( vlm_t *p_vlm, int64_t id, const char *psz_id, int i_input_index, const char *psz_vod_output )
  663. {
  664.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  665.     vlm_media_instance_sys_t *p_instance;
  666.     char *psz_log;
  667.     if( !p_media || !p_media->cfg.b_enabled || p_media->cfg.i_input <= 0 )
  668.         return VLC_EGENERIC;
  669.     /* TODO support multiple input for VOD with sout-keep ? */
  670.     if( ( p_media->cfg.b_vod && !psz_vod_output ) || ( !p_media->cfg.b_vod && psz_vod_output ) )
  671.         return VLC_EGENERIC;
  672.     if( i_input_index < 0 || i_input_index >= p_media->cfg.i_input )
  673.         return VLC_EGENERIC;
  674.     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
  675.     if( !p_instance )
  676.     {
  677.         vlm_media_t *p_cfg = &p_media->cfg;
  678.         int i;
  679.         p_instance = vlm_MediaInstanceNew( p_vlm, psz_id );
  680.         if( !p_instance )
  681.             return VLC_ENOMEM;
  682.         if( p_cfg->psz_output != NULL || psz_vod_output != NULL )
  683.         {
  684.             char *psz_buffer;
  685.             if( asprintf( &psz_buffer, "sout=%s%s%s",
  686.                       p_cfg->psz_output ? p_cfg->psz_output : "",
  687.                       (p_cfg->psz_output && psz_vod_output) ? ":" : psz_vod_output ? "#" : "",
  688.                       psz_vod_output ? psz_vod_output : "" ) != -1 )
  689.             {
  690.                 input_item_AddOption( p_instance->p_item, psz_buffer, VLC_INPUT_OPTION_TRUSTED );
  691.                 free( psz_buffer );
  692.             }
  693.         }
  694.         for( i = 0; i < p_cfg->i_option; i++ )
  695.         {
  696.             if( !strcmp( p_cfg->ppsz_option[i], "sout-keep" ) )
  697.                 p_instance->b_sout_keep = true;
  698.             else if( !strcmp( p_cfg->ppsz_option[i], "nosout-keep" ) || !strcmp( p_cfg->ppsz_option[i], "no-sout-keep" ) )
  699.                 p_instance->b_sout_keep = false;
  700.             else
  701.                 input_item_AddOption( p_instance->p_item, p_cfg->ppsz_option[i], VLC_INPUT_OPTION_TRUSTED );
  702.         }
  703.         TAB_APPEND( p_media->i_instance, p_media->instance, p_instance );
  704.     }
  705.     /* Stop old instance */
  706.     input_thread_t *p_input = p_instance->p_input;
  707.     if( p_input )
  708.     {
  709.         if( p_instance->i_index == i_input_index &&
  710.             !p_input->b_eof && !p_input->b_error )
  711.         {
  712.             if( var_GetInteger( p_input, "state" ) == PAUSE_S )
  713.                 var_SetInteger( p_input, "state",  PLAYING_S );
  714.             return VLC_SUCCESS;
  715.         }
  716.         input_Stop( p_input, !p_input->b_eof && !p_input->b_error );
  717.         vlc_thread_join( p_input );
  718.         p_instance->p_input_resource = input_DetachResource( p_input );
  719.         vlc_object_release( p_input );
  720.         if( !p_instance->b_sout_keep )
  721.             input_resource_TerminateSout( p_instance->p_input_resource );
  722.         input_resource_TerminateVout( p_instance->p_input_resource );
  723.         vlm_SendEventMediaInstanceStopped( p_vlm, id, p_media->cfg.psz_name );
  724.     }
  725.     /* Start new one */
  726.     p_instance->i_index = i_input_index;
  727.     input_item_SetURI( p_instance->p_item, p_media->cfg.ppsz_input[p_instance->i_index] ) ;
  728.     if( asprintf( &psz_log, _("Media: %s"), p_media->cfg.psz_name ) != -1 )
  729.     {
  730.         p_instance->p_input = input_Create( p_vlm->p_libvlc, p_instance->p_item,
  731.                                             psz_log, p_instance->p_input_resource );
  732.         if( p_instance->p_input && input_Start( p_instance->p_input ) )
  733.         {
  734.             vlc_object_release( p_instance->p_input );
  735.             p_instance->p_input = NULL;
  736.         }
  737.         p_instance->p_input_resource = NULL;
  738.         if( !p_instance->p_input )
  739.         {
  740.             TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance );
  741.             vlm_MediaInstanceDelete( p_vlm, id, p_instance, p_media->cfg.psz_name );
  742.         }
  743.         else
  744.         {
  745.             vlm_SendEventMediaInstanceStarted( p_vlm, id, p_media->cfg.psz_name );
  746.         }
  747.         free( psz_log );
  748.     }
  749.     return VLC_SUCCESS;
  750. }
  751. static int vlm_ControlMediaInstanceStop( vlm_t *p_vlm, int64_t id, const char *psz_id )
  752. {
  753.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  754.     vlm_media_instance_sys_t *p_instance;
  755.     if( !p_media )
  756.         return VLC_EGENERIC;
  757.     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
  758.     if( !p_instance )
  759.         return VLC_EGENERIC;
  760.     TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance );
  761.     vlm_MediaInstanceDelete( p_vlm, id, p_instance, p_media->cfg.psz_name );
  762.     return VLC_SUCCESS;
  763. }
  764. static int vlm_ControlMediaInstancePause( vlm_t *p_vlm, int64_t id, const char *psz_id )
  765. {
  766.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  767.     vlm_media_instance_sys_t *p_instance;
  768.     int i_state;
  769.     if( !p_media )
  770.         return VLC_EGENERIC;
  771.     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
  772.     if( !p_instance || !p_instance->p_input )
  773.         return VLC_EGENERIC;
  774.     /* Toggle pause state */
  775.     i_state = var_GetInteger( p_instance->p_input, "state" );
  776.     if( i_state == PAUSE_S )
  777.         var_SetInteger( p_instance->p_input, "state", PLAYING_S );
  778.     else if( i_state == PLAYING_S )
  779.         var_SetInteger( p_instance->p_input, "state", PAUSE_S );
  780.     return VLC_SUCCESS;
  781. }
  782. static int vlm_ControlMediaInstanceGetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t *pi_time, double *pd_position )
  783. {
  784.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  785.     vlm_media_instance_sys_t *p_instance;
  786.     if( !p_media )
  787.         return VLC_EGENERIC;
  788.     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
  789.     if( !p_instance || !p_instance->p_input )
  790.         return VLC_EGENERIC;
  791.     if( pi_time )
  792.         *pi_time = var_GetTime( p_instance->p_input, "time" );
  793.     if( pd_position )
  794.         *pd_position = var_GetFloat( p_instance->p_input, "position" );
  795.     return VLC_SUCCESS;
  796. }
  797. static int vlm_ControlMediaInstanceSetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t i_time, double d_position )
  798. {
  799.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  800.     vlm_media_instance_sys_t *p_instance;
  801.     if( !p_media )
  802.         return VLC_EGENERIC;
  803.     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
  804.     if( !p_instance || !p_instance->p_input )
  805.         return VLC_EGENERIC;
  806.     if( i_time >= 0 )
  807.         return var_SetTime( p_instance->p_input, "time", i_time );
  808.     else if( d_position >= 0 && d_position <= 100 )
  809.         return var_SetFloat( p_instance->p_input, "position", d_position );
  810.     return VLC_EGENERIC;
  811. }
  812. static int vlm_ControlMediaInstanceGets( vlm_t *p_vlm, int64_t id, vlm_media_instance_t ***ppp_idsc, int *pi_instance )
  813. {
  814.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  815.     vlm_media_instance_t **pp_idsc;
  816.     int                              i_idsc;
  817.     int i;
  818.     if( !p_media )
  819.         return VLC_EGENERIC;
  820.     TAB_INIT( i_idsc, pp_idsc );
  821.     for( i = 0; i < p_media->i_instance; i++ )
  822.     {
  823.         vlm_media_instance_sys_t *p_instance = p_media->instance[i];
  824.         vlm_media_instance_t *p_idsc = vlm_media_instance_New();
  825.         if( p_instance->psz_name )
  826.             p_idsc->psz_name = strdup( p_instance->psz_name );
  827.         if( p_instance->p_input )
  828.         {
  829.             p_idsc->i_time = var_GetTime( p_instance->p_input, "time" );
  830.             p_idsc->i_length = var_GetTime( p_instance->p_input, "length" );
  831.             p_idsc->d_position = var_GetFloat( p_instance->p_input, "position" );
  832.             if( var_GetInteger( p_instance->p_input, "state" ) == PAUSE_S )
  833.                 p_idsc->b_paused = true;
  834.             p_idsc->i_rate = var_GetInteger( p_instance->p_input, "rate" );
  835.         }
  836.         TAB_APPEND( i_idsc, pp_idsc, p_idsc );
  837.     }
  838.     *ppp_idsc = pp_idsc;
  839.     *pi_instance = i_idsc;
  840.     return VLC_SUCCESS;
  841. }
  842. static int vlm_ControlMediaInstanceClear( vlm_t *p_vlm, int64_t id )
  843. {
  844.     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
  845.     if( !p_media )
  846.         return VLC_EGENERIC;
  847.     while( p_media->i_instance > 0 )
  848.         vlm_ControlMediaInstanceStop( p_vlm, id, p_media->instance[0]->psz_name );
  849.     return VLC_SUCCESS;
  850. }
  851. static int vlm_ControlScheduleClear( vlm_t *p_vlm )
  852. {
  853.     while( p_vlm->i_schedule > 0 )
  854.         vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0] );
  855.     return VLC_SUCCESS;
  856. }
  857. static int vlm_vaControlInternal( vlm_t *p_vlm, int i_query, va_list args )
  858. {
  859.     vlm_media_t *p_dsc;
  860.     vlm_media_t **pp_dsc;
  861.     vlm_media_t ***ppp_dsc;
  862.     vlm_media_instance_t ***ppp_idsc;
  863.     const char *psz_id;
  864.     const char *psz_vod;
  865.     int64_t *p_id;
  866.     int64_t id;
  867.     int i_int;
  868.     int *pi_int;
  869.     int64_t *pi_i64;
  870.     int64_t i_i64;
  871.     double *pd_double;
  872.     double d_double;
  873.     switch( i_query )
  874.     {
  875.     /* Media control */
  876.     case VLM_GET_MEDIAS:
  877.         ppp_dsc = (vlm_media_t ***)va_arg( args, vlm_media_t *** );
  878.         pi_int = (int *)va_arg( args, int * );
  879.         return vlm_ControlMediaGets( p_vlm, ppp_dsc, pi_int );
  880.     case VLM_CLEAR_MEDIAS:
  881.         return vlm_ControlMediaClear( p_vlm );
  882.     case VLM_CHANGE_MEDIA:
  883.         p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * );
  884.         return vlm_ControlMediaChange( p_vlm, p_dsc );
  885.     case VLM_ADD_MEDIA:
  886.         p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * );
  887.         p_id = (int64_t*)va_arg( args, int64_t * );
  888.         return vlm_ControlMediaAdd( p_vlm, p_dsc, p_id );
  889.     case VLM_DEL_MEDIA:
  890.         id = (int64_t)va_arg( args, int64_t );
  891.         return vlm_ControlMediaDel( p_vlm, id );
  892.     case VLM_GET_MEDIA:
  893.         id = (int64_t)va_arg( args, int64_t );
  894.         pp_dsc = (vlm_media_t **)va_arg( args, vlm_media_t ** );
  895.         return vlm_ControlMediaGet( p_vlm, id, pp_dsc );
  896.     case VLM_GET_MEDIA_ID:
  897.         psz_id = (const char*)va_arg( args, const char * );
  898.         p_id = (int64_t*)va_arg( args, int64_t * );
  899.         return vlm_ControlMediaGetId( p_vlm, psz_id, p_id );
  900.     /* Media instance control */
  901.     case VLM_GET_MEDIA_INSTANCES:
  902.         id = (int64_t)va_arg( args, int64_t );
  903.         ppp_idsc = (vlm_media_instance_t ***)va_arg( args, vlm_media_instance_t *** );
  904.         pi_int = (int *)va_arg( args, int *);
  905.         return vlm_ControlMediaInstanceGets( p_vlm, id, ppp_idsc, pi_int );
  906.     case VLM_CLEAR_MEDIA_INSTANCES:
  907.         id = (int64_t)va_arg( args, int64_t );
  908.         return vlm_ControlMediaInstanceClear( p_vlm, id );
  909.     case VLM_START_MEDIA_BROADCAST_INSTANCE:
  910.         id = (int64_t)va_arg( args, int64_t );
  911.         psz_id = (const char*)va_arg( args, const char* );
  912.         i_int = (int)va_arg( args, int );
  913.         return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, NULL );
  914.     case VLM_START_MEDIA_VOD_INSTANCE:
  915.         id = (int64_t)va_arg( args, int64_t );
  916.         psz_id = (const char*)va_arg( args, const char* );
  917.         i_int = (int)va_arg( args, int );
  918.         psz_vod = (const char*)va_arg( args, const char* );
  919.         if( !psz_vod )
  920.             return VLC_EGENERIC;
  921.         return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, psz_vod );
  922.     case VLM_STOP_MEDIA_INSTANCE:
  923.         id = (int64_t)va_arg( args, int64_t );
  924.         psz_id = (const char*)va_arg( args, const char* );
  925.         return vlm_ControlMediaInstanceStop( p_vlm, id, psz_id );
  926.     case VLM_PAUSE_MEDIA_INSTANCE:
  927.         id = (int64_t)va_arg( args, int64_t );
  928.         psz_id = (const char*)va_arg( args, const char* );
  929.         return vlm_ControlMediaInstancePause( p_vlm, id, psz_id );
  930.     case VLM_GET_MEDIA_INSTANCE_TIME:
  931.         id = (int64_t)va_arg( args, int64_t );
  932.         psz_id = (const char*)va_arg( args, const char* );
  933.         pi_i64 = (int64_t*)va_arg( args, int64_t * );
  934.         return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, pi_i64, NULL );
  935.     case VLM_GET_MEDIA_INSTANCE_POSITION:
  936.         id = (int64_t)va_arg( args, int64_t );
  937.         psz_id = (const char*)va_arg( args, const char* );
  938.         pd_double = (double*)va_arg( args, double* );
  939.         return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, NULL, pd_double );
  940.     case VLM_SET_MEDIA_INSTANCE_TIME:
  941.         id = (int64_t)va_arg( args, int64_t );
  942.         psz_id = (const char*)va_arg( args, const char* );
  943.         i_i64 = (int64_t)va_arg( args, int64_t);
  944.         return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, i_i64, -1 );
  945.     case VLM_SET_MEDIA_INSTANCE_POSITION:
  946.         id = (int64_t)va_arg( args, int64_t );
  947.         psz_id = (const char*)va_arg( args, const char* );
  948.         d_double = (double)va_arg( args, double );
  949.         return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, -1, d_double );
  950.     case VLM_CLEAR_SCHEDULES:
  951.         return vlm_ControlScheduleClear( p_vlm );
  952.     default:
  953.         msg_Err( p_vlm, "unknown VLM query" );
  954.         return VLC_EGENERIC;
  955.     }
  956. }
  957. int vlm_ControlInternal( vlm_t *p_vlm, int i_query, ... )
  958. {
  959.     va_list args;
  960.     int     i_result;
  961.     va_start( args, i_query );
  962.     i_result = vlm_vaControlInternal( p_vlm, i_query, args );
  963.     va_end( args );
  964.     return i_result;
  965. }
  966. int vlm_Control( vlm_t *p_vlm, int i_query, ... )
  967. {
  968.     va_list args;
  969.     int     i_result;
  970.     va_start( args, i_query );
  971.     vlc_mutex_lock( &p_vlm->lock );
  972.     i_result = vlm_vaControlInternal( p_vlm, i_query, args );
  973.     vlc_mutex_unlock( &p_vlm->lock );
  974.     va_end( args );
  975.     return i_result;
  976. }