]> git.sesse.net Git - vlc/blob - src/input/vlm.c
We know where VLM is, no need to find it.
[vlc] / src / input / vlm.c
1 /*****************************************************************************
2  * vlm.c: VLM interface plugin
3  *****************************************************************************
4  * Copyright (C) 2000-2005 the VideoLAN team
5  * $Id$
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 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc/vlc.h>
34
35 #include <stdio.h>
36 #include <ctype.h>                                              /* tolower() */
37 #include <assert.h>
38
39 #include <vlc_vlm.h>
40
41 #ifdef ENABLE_VLM
42
43 #ifndef WIN32
44 #   include <sys/time.h>                                   /* gettimeofday() */
45 #endif
46
47 #ifdef HAVE_TIME_H
48 #   include <time.h>                                              /* ctime() */
49 #   include <sys/timeb.h>                                         /* ftime() */
50 #endif
51
52 #include <vlc_input.h>
53 #include "input_internal.h"
54 #include <vlc_stream.h>
55 #include "vlm_internal.h"
56 #include <vlc_vod.h>
57 #include <vlc_charset.h>
58 #include <vlc_sout.h>
59 #include "../stream_output/stream_output.h"
60 #include "../libvlc.h"
61
62 /*****************************************************************************
63  * Local prototypes.
64  *****************************************************************************/
65
66 /* ugly kludge to avoid "null format string" warnings,
67  * even if we handle NULL format string in vlm_MessageNew() */
68 static const char *vlm_NULL = NULL;
69
70 static void vlm_Destructor( vlm_t *p_vlm );
71
72 /* */
73 static int vlm_ControlInternal( vlm_t *, int, ... );
74
75 /* */
76 static vlm_message_t *vlm_Show( vlm_t *, vlm_media_sys_t *, vlm_schedule_sys_t *, const char * );
77
78 static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *, const char * );
79
80 static char *Save( vlm_t * );
81 static int Load( vlm_t *, char * );
82
83 static int ExecuteCommand( vlm_t *, const char *, vlm_message_t ** );
84
85 static int Manage( vlc_object_t * );
86
87 static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name );
88 static void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_sys_t *sched );
89 static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd,
90                               const char *psz_value );
91
92 static int vlm_MediaVodControl( void *, vod_media_t *, const char *, int, va_list );
93
94 /* */
95 static vlm_media_sys_t *vlm_MediaSearch( vlm_t *, const char *);
96
97 /*****************************************************************************
98  * vlm_New:
99  *****************************************************************************/
100 vlm_t *__vlm_New ( vlc_object_t *p_this )
101 {
102     vlc_value_t lockval;
103     vlm_t *p_vlm = NULL, **pp_vlm = &(libvlc_priv (p_this->p_libvlc)->p_vlm);
104     char *psz_vlmconf;
105     static const char vlm_object_name[] = "vlm daemon";
106
107     /* Avoid multiple creation */
108     if( var_Create( p_this->p_libvlc, "vlm_mutex", VLC_VAR_MUTEX ) ||
109         var_Get( p_this->p_libvlc, "vlm_mutex", &lockval ) )
110         return NULL;
111
112     vlc_mutex_lock( lockval.p_address );
113
114     p_vlm = *pp_vlm;
115     if( p_vlm )
116     {   /* VLM already exists */
117         vlc_object_yield( p_vlm );
118         vlc_mutex_unlock( lockval.p_address );
119         return p_vlm;
120     }
121
122     msg_Dbg( p_this, "creating VLM" );
123
124     p_vlm = vlc_custom_create( p_this, sizeof( *p_vlm ), VLC_OBJECT_GENERIC,
125                                vlm_object_name );
126     if( !p_vlm )
127     {
128         vlc_mutex_unlock( lockval.p_address );
129         return NULL;
130     }
131
132     vlc_mutex_init( &p_vlm->lock );
133     p_vlm->i_id = 1;
134     TAB_INIT( p_vlm->i_media, p_vlm->media );
135     TAB_INIT( p_vlm->i_schedule, p_vlm->schedule );
136     p_vlm->i_vod = 0;
137     p_vlm->p_vod = NULL;
138     vlc_object_attach( p_vlm, p_this->p_libvlc );
139
140     if( vlc_thread_create( p_vlm, "vlm thread",
141                            Manage, VLC_THREAD_PRIORITY_LOW, false ) )
142     {
143         vlc_mutex_destroy( &p_vlm->lock );
144         vlc_object_release( p_vlm );
145         return NULL;
146     }
147
148     /* Load our configuration file */
149     psz_vlmconf = var_CreateGetString( p_vlm, "vlm-conf" );
150     if( psz_vlmconf && *psz_vlmconf )
151     {
152         vlm_message_t *p_message = NULL;
153         char *psz_buffer = NULL;
154
155         msg_Dbg( p_this, "loading VLM configuration" );
156         asprintf(&psz_buffer, "load %s", psz_vlmconf );
157         if( psz_buffer )
158         {
159             msg_Dbg( p_this, psz_buffer );
160             if( vlm_ExecuteCommand( p_vlm, psz_buffer, &p_message ) )
161                 msg_Warn( p_this, "error while loading the configuration file" );
162
163             vlm_MessageDelete(p_message);
164             free(psz_buffer);
165         }
166     }
167     free(psz_vlmconf);
168
169     vlc_object_set_destructor( p_vlm, (vlc_destructor_t)vlm_Destructor );
170     *pp_vlm = p_vlm; /* for future reference */
171     vlc_mutex_unlock( lockval.p_address );
172
173     return p_vlm;
174 }
175
176 /*****************************************************************************
177  * vlm_Delete:
178  *****************************************************************************/
179 void vlm_Delete( vlm_t *p_vlm )
180 {
181     vlc_object_release( p_vlm );
182 }
183
184 /*****************************************************************************
185  * vlm_Destructor:
186  *****************************************************************************/
187 static void vlm_Destructor( vlm_t *p_vlm )
188 {
189     vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
190     TAB_CLEAN( p_vlm->i_media, p_vlm->media );
191
192     vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
193     TAB_CLEAN( p_vlm->schedule, p_vlm->schedule );
194
195     vlc_mutex_destroy( &p_vlm->lock );
196 }
197
198 /*****************************************************************************
199  * vlm_ExecuteCommand:
200  *****************************************************************************/
201 int vlm_ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
202                         vlm_message_t **pp_message)
203 {
204     int i_result;
205
206     vlc_mutex_lock( &p_vlm->lock );
207     i_result = ExecuteCommand( p_vlm, psz_command, pp_message );
208     vlc_mutex_unlock( &p_vlm->lock );
209
210     return i_result;
211 }
212
213
214 static const char quotes[] = "\"'";
215 /**
216  * FindCommandEnd: look for the end of a possibly quoted string
217  * @return NULL on mal-formatted string,
218  * pointer past the last character otherwise.
219  */
220 static const char *FindCommandEnd( const char *psz_sent )
221 {
222     char c, quote = 0;
223
224     while( (c = *psz_sent) != '\0' )
225     {
226         if( !quote )
227         {
228             if( strchr(quotes,c) )   // opening quote
229                 quote = c;
230             else if( isspace(c) )         // non-escaped space
231                 return psz_sent;
232             else if( c == '\\' )
233             {
234                 psz_sent++;         // skip escaped character
235                 if( *psz_sent == '\0' )
236                     return psz_sent;
237             }
238         }
239         else
240         {
241             if( c == quote )         // non-escaped matching quote
242                 quote = 0;
243             else if( (quote == '"') && (c == '\\') )
244             {
245                 psz_sent++;         // skip escaped character
246                 if (*psz_sent == '\0')
247                     return NULL;    // error, closing quote missing
248             }
249         }
250         psz_sent++;
251     }
252
253     // error (NULL) if we could not find a matching quote
254     return quote ? NULL : psz_sent;
255 }
256
257
258 /**
259  * Unescape a nul-terminated string.
260  * Note that in and out can be identical.
261  *
262  * @param out output buffer (at least <strlen (in) + 1> characters long)
263  * @param in nul-terminated string to be unescaped
264  *
265  * @return 0 on success, -1 on error.
266  */
267 static int Unescape( char *out, const char *in )
268 {
269     char c, quote = 0;
270
271     while( (c = *in++) != '\0' )
272     {
273         if( !quote )
274         {
275             if (strchr(quotes,c))   // opening quote
276             {
277                 quote = c;
278                 continue;
279             }
280             else if( c == '\\' )
281             {
282                 switch (c = *in++)
283                 {
284                     case '"':
285                     case '\'':
286                     case '\\':
287                         *out++ = c;
288                         continue;
289
290                     case '\0':
291                         *out = '\0';
292                         return 0;
293                 }
294                 if( isspace(c) )
295                 {
296                     *out++ = c;
297                     continue;
298                 }
299                 /* None of the special cases - copy the backslash */
300                 *out++ = '\\';
301             }
302         }
303         else
304         {
305             if( c == quote )         // non-escaped matching quote
306             {
307                 quote = 0;
308                 continue;
309             }
310             if( (quote == '"') && (c == '\\') )
311             {
312                 switch( c = *in++ )
313                 {
314                     case '"':
315                     case '\\':
316                         *out++ = c;
317                         continue;
318
319                     case '\0':   // should never happen
320                         *out = '\0';
321                         return -1;
322                 }
323                 /* None of the special cases - copy the backslash */
324                 *out++ = '\\';
325             }
326         }
327         *out++ = c;
328     }
329
330     *out = '\0';
331     return 0;
332 }
333
334
335 /*****************************************************************************
336  * ExecuteCommand: The main state machine
337  *****************************************************************************
338  * Execute a command which ends with '\0' (string)
339  *****************************************************************************/
340 static int ExecuteSyntaxError( const char *psz_cmd, vlm_message_t **pp_status )
341 {
342     *pp_status = vlm_MessageNew( psz_cmd, "Wrong command syntax" );
343     return VLC_EGENERIC;
344 }
345
346 static bool ExecuteIsMedia( vlm_t *p_vlm, const char *psz_name )
347 {
348     int64_t id;
349
350     if( !psz_name || vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) )
351         return false;
352     return true;
353 }
354 static bool ExecuteIsSchedule( vlm_t *p_vlm, const char *psz_name )
355 {
356     if( !psz_name || !vlm_ScheduleSearch( p_vlm, psz_name ) )
357         return false;
358     return true;
359 }
360
361 static int ExecuteDel( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status )
362 {
363     vlm_media_sys_t *p_media;
364     vlm_schedule_sys_t *p_schedule;
365
366     p_media = vlm_MediaSearch( p_vlm, psz_name );
367     p_schedule = vlm_ScheduleSearch( p_vlm, psz_name );
368
369     if( p_schedule != NULL )
370     {
371         vlm_ScheduleDelete( p_vlm, p_schedule );
372     }
373     else if( p_media != NULL )
374     {
375         vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_media->cfg.id );
376     }
377     else if( !strcmp(psz_name, "media") )
378     {
379         vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
380     }
381     else if( !strcmp(psz_name, "schedule") )
382     {
383         vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
384     }
385     else if( !strcmp(psz_name, "all") )
386     {
387         vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
388         vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
389     }
390     else
391     {
392         *pp_status = vlm_MessageNew( "del", "%s: media unknown", psz_name );
393         return VLC_EGENERIC;
394     }
395
396     *pp_status = vlm_MessageNew( "del", vlm_NULL );
397     return VLC_SUCCESS;
398 }
399
400 static int ExecuteShow( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status )
401 {
402     vlm_media_sys_t *p_media;
403     vlm_schedule_sys_t *p_schedule;
404
405     if( !psz_name )
406     {
407         *pp_status = vlm_Show( p_vlm, NULL, NULL, NULL );
408         return VLC_SUCCESS;
409     }
410
411     p_media = vlm_MediaSearch( p_vlm, psz_name );
412     p_schedule = vlm_ScheduleSearch( p_vlm, psz_name );
413
414     if( p_schedule != NULL )
415         *pp_status = vlm_Show( p_vlm, NULL, p_schedule, NULL );
416     else if( p_media != NULL )
417         *pp_status = vlm_Show( p_vlm, p_media, NULL, NULL );
418     else
419         *pp_status = vlm_Show( p_vlm, NULL, NULL, psz_name );
420
421     return VLC_SUCCESS;
422 }
423
424 static int ExecuteHelp( vlm_message_t **pp_status )
425 {
426     vlm_message_t *message_child;
427
428 #define MessageAdd( a ) \
429         vlm_MessageAdd( *pp_status, vlm_MessageNew( a, vlm_NULL ) );
430 #define MessageAddChild( a ) \
431         vlm_MessageAdd( message_child, vlm_MessageNew( a, vlm_NULL ) );
432
433     *pp_status = vlm_MessageNew( "help", vlm_NULL );
434
435     message_child = MessageAdd( "Commands Syntax:" );
436     MessageAddChild( "new (name) vod|broadcast|schedule [properties]" );
437     MessageAddChild( "setup (name) (properties)" );
438     MessageAddChild( "show [(name)|media|schedule]" );
439     MessageAddChild( "del (name)|all|media|schedule" );
440     MessageAddChild( "control (name) [instance_name] (command)" );
441     MessageAddChild( "save (config_file)" );
442     MessageAddChild( "export" );
443     MessageAddChild( "load (config_file)" );
444
445     message_child = MessageAdd( "Media Proprieties Syntax:" );
446     MessageAddChild( "input (input_name)" );
447     MessageAddChild( "inputdel (input_name)|all" );
448     MessageAddChild( "inputdeln input_number" );
449     MessageAddChild( "output (output_name)" );
450     MessageAddChild( "option (option_name)[=value]" );
451     MessageAddChild( "enabled|disabled" );
452     MessageAddChild( "loop|unloop (broadcast only)" );
453     MessageAddChild( "mux (mux_name)" );
454
455     message_child = MessageAdd( "Schedule Proprieties Syntax:" );
456     MessageAddChild( "enabled|disabled" );
457     MessageAddChild( "append (command_until_rest_of_the_line)" );
458     MessageAddChild( "date (year)/(month)/(day)-(hour):(minutes):"
459                      "(seconds)|now" );
460     MessageAddChild( "period (years_aka_12_months)/(months_aka_30_days)/"
461                      "(days)-(hours):(minutes):(seconds)" );
462     MessageAddChild( "repeat (number_of_repetitions)" );
463
464     message_child = MessageAdd( "Control Commands Syntax:" );
465     MessageAddChild( "play [input_number]" );
466     MessageAddChild( "pause" );
467     MessageAddChild( "stop" );
468     MessageAddChild( "seek [+-](percentage) | [+-](seconds)s | [+-](miliseconds)ms" );
469
470     return VLC_SUCCESS;
471 }
472
473 static int ExecuteControl( vlm_t *p_vlm, const char *psz_name, const int i_arg, char ** ppsz_arg, vlm_message_t **pp_status )
474 {
475     vlm_media_sys_t *p_media;
476     const char *psz_control = NULL;
477     const char *psz_instance = NULL;
478     const char *psz_argument = NULL;
479     int i_index;
480     int i_result;
481
482     if( !ExecuteIsMedia( p_vlm, psz_name ) )
483     {
484         *pp_status = vlm_MessageNew( "control", "%s: media unknown", psz_name );
485         return VLC_EGENERIC;
486     }
487
488     assert( i_arg > 0 );
489
490 #define IS(txt) ( !strcmp( ppsz_arg[i_index], (txt) ) )
491     i_index = 0;
492     if( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") )
493     {
494         i_index = 1;
495         psz_instance = ppsz_arg[0];
496
497         if( i_index >= i_arg || ( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") ) )
498             return ExecuteSyntaxError( "control", pp_status );
499     }
500 #undef IS
501     psz_control = ppsz_arg[i_index];
502
503     if( i_index+1 < i_arg )
504         psz_argument = ppsz_arg[i_index+1];
505
506     p_media = vlm_MediaSearch( p_vlm, psz_name );
507     assert( p_media );
508
509     if( !strcmp( psz_control, "play" ) )
510     {
511         int i_input_index = 0;
512         int i;
513
514         if( ( psz_argument && sscanf(psz_argument, "%d", &i) == 1 ) && i > 0 && i-1 < p_media->cfg.i_input )
515         {
516             i_input_index = i-1;
517         }
518         else if( psz_argument )
519         {
520             int j;
521             vlm_media_t *p_cfg = &p_media->cfg;
522             for ( j=0; j < p_cfg->i_input; j++)
523             {
524                 if( !strcmp( p_cfg->ppsz_input[j], psz_argument ) )
525                 {
526                     i_input_index = j;
527                     break;
528                 }
529             }
530         }
531
532         if( p_media->cfg.b_vod )
533             i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_VOD_INSTANCE, p_media->cfg.id, psz_instance, i_input_index, NULL );    // we should get here now
534         else
535             i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, psz_instance, i_input_index );
536     }
537     else if( !strcmp( psz_control, "seek" ) )
538     {
539         if( psz_argument )
540         {
541             bool b_relative;
542             if( psz_argument[0] == '+' || psz_argument[0] == '-' )
543                 b_relative = true;
544             else
545                 b_relative = false;
546
547             if( strstr( psz_argument, "ms" ) || strstr( psz_argument, "s" ) )
548             {
549                 /* Time (ms or s) */
550                 int64_t i_new_time;
551
552                 if( strstr( psz_argument, "ms" ) )
553                     i_new_time =  1000 * (int64_t)atoi( psz_argument );
554                 else
555                     i_new_time = 1000000 * (int64_t)atoi( psz_argument );
556
557                 if( b_relative )
558                 {
559                     int64_t i_time = 0;
560                     vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, &i_time );
561                     i_new_time += i_time;
562                 }
563                 if( i_new_time < 0 )
564                     i_new_time = 0;
565                 i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, i_new_time );
566             }
567             else
568             {
569                 /* Percent */
570                 double d_new_position = i18n_atof( psz_argument ) / 100.0;
571
572                 if( b_relative )
573                 {
574                     double d_position = 0.0;
575
576                     vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position );
577                     d_new_position += d_position;
578                 }
579                 if( d_new_position < 0.0 )
580                     d_new_position = 0.0;
581                 else if( d_new_position > 1.0 )
582                     d_new_position = 1.0;
583                 i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_new_position );
584             }
585         }
586         else
587         {
588             i_result = VLC_EGENERIC;
589         }
590     }
591     else if( !strcmp( psz_control, "rewind" ) )
592     {
593         if( psz_argument )
594         {
595             const double d_scale = i18n_atof( psz_argument );
596             double d_position;
597
598             vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position );
599             d_position -= (d_scale / 1000.0);
600             if( d_position < 0.0 )
601                 d_position = 0.0;
602             i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position );
603         }
604         else
605         {
606             i_result = VLC_EGENERIC;
607         }
608     }
609     else if( !strcmp( psz_control, "forward" ) )
610     {
611         if( psz_argument )
612         {
613             const double d_scale = i18n_atof( psz_argument );
614             double d_position;
615
616             vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position );
617             d_position += (d_scale / 1000.0);
618             if( d_position > 1.0 )
619                 d_position = 1.0;
620             i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position );
621
622         }
623         else
624         {
625             i_result = VLC_EGENERIC;
626         }
627     }
628     else if( !strcmp( psz_control, "stop" ) )
629     {
630         i_result = vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, psz_instance );
631     }
632     else if( !strcmp( psz_control, "pause" ) )
633     {
634         i_result = vlm_ControlInternal( p_vlm, VLM_PAUSE_MEDIA_INSTANCE, p_media->cfg.id, psz_instance );
635     }
636     else
637     {
638         i_result = VLC_EGENERIC;
639     }
640
641     if( i_result )
642     {
643         *pp_status = vlm_MessageNew( "control", "unknown error" );
644         return VLC_SUCCESS;
645     }
646     *pp_status = vlm_MessageNew( "control", vlm_NULL );
647     return VLC_SUCCESS;
648 }
649
650 static int ExecuteExport( vlm_t *p_vlm, vlm_message_t **pp_status )
651 {
652     char *psz_export = Save( p_vlm );
653
654     *pp_status = vlm_MessageNew( "export", psz_export );
655     free( psz_export );
656     return VLC_SUCCESS;
657 }
658
659 static int ExecuteSave( vlm_t *p_vlm, const char *psz_file, vlm_message_t **pp_status )
660 {
661     FILE *f = utf8_fopen( psz_file, "wt" );
662     char *psz_save;
663
664     if( !f )
665         goto error;
666
667     psz_save = Save( p_vlm );
668     if( psz_save == NULL )
669     {
670         fclose( f );
671         goto error;
672     }
673     fwrite( psz_save, strlen( psz_save ), 1, f );
674     free( psz_save );
675     fclose( f );
676
677     *pp_status = vlm_MessageNew( "save", vlm_NULL );
678     return VLC_SUCCESS;
679
680 error:
681     *pp_status = vlm_MessageNew( "save", "Unable to save to file" );
682     return VLC_EGENERIC;
683 }
684
685 static int ExecuteLoad( vlm_t *p_vlm, const char *psz_url, vlm_message_t **pp_status )
686 {
687     stream_t *p_stream = stream_UrlNew( p_vlm, psz_url );
688     int64_t i_size;
689     char *psz_buffer;
690
691     if( !p_stream )
692     {
693         *pp_status = vlm_MessageNew( "load", "Unable to load from file" );
694         return VLC_EGENERIC;
695     }
696
697     /* FIXME needed ? */
698     if( stream_Seek( p_stream, 0 ) != 0 )
699     {
700         stream_Delete( p_stream );
701
702         *pp_status = vlm_MessageNew( "load", "Read file error" );
703         return VLC_EGENERIC;
704     }
705
706     i_size = stream_Size( p_stream );
707
708     psz_buffer = malloc( i_size + 1 );
709     if( !psz_buffer )
710     {
711         stream_Delete( p_stream );
712
713         *pp_status = vlm_MessageNew( "load", "Read file error" );
714         return VLC_EGENERIC;
715     }
716
717     stream_Read( p_stream, psz_buffer, i_size );
718     psz_buffer[i_size] = '\0';
719
720     stream_Delete( p_stream );
721
722     if( Load( p_vlm, psz_buffer ) )
723     {
724         free( psz_buffer );
725
726         *pp_status = vlm_MessageNew( "load", "Error while loading file" );
727         return VLC_EGENERIC;
728     }
729
730     free( psz_buffer );
731
732     *pp_status = vlm_MessageNew( "load", vlm_NULL );
733     return VLC_SUCCESS;
734 }
735
736 static int ExecuteScheduleProperty( vlm_t *p_vlm, vlm_schedule_sys_t *p_schedule, bool b_new,
737                                     const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
738 {
739     const char *psz_cmd = b_new ? "new" : "setup";
740     int i;
741
742     for( i = 0; i < i_property; i++ )
743     {
744         if( !strcmp( ppsz_property[i], "enabled" ) ||
745             !strcmp( ppsz_property[i], "disabled" ) )
746         {
747             vlm_ScheduleSetup( p_schedule, ppsz_property[i], NULL );
748         }
749         else if( !strcmp( ppsz_property[i], "append" ) )
750         {
751             char *psz_line;
752             int j;
753             /* Beware: everything behind append is considered as
754              * command line */
755
756             if( ++i >= i_property )
757                 break;
758
759             psz_line = strdup( ppsz_property[i] );
760             for( j = i+1; j < i_property; j++ )
761             {
762                 psz_line = realloc( psz_line, strlen(psz_line) + strlen(ppsz_property[j]) + 1 + 1 );
763                 strcat( psz_line, " " );
764                 strcat( psz_line, ppsz_property[j] );
765             }
766
767             vlm_ScheduleSetup( p_schedule, "append", psz_line );
768             break;
769         }
770         else
771         {
772             if( i + 1 >= i_property )
773             {
774                 if( b_new )
775                     vlm_ScheduleDelete( p_vlm, p_schedule );
776                 return ExecuteSyntaxError( psz_cmd, pp_status );
777             }
778
779             vlm_ScheduleSetup( p_schedule, ppsz_property[i], ppsz_property[i+1] );
780             i++;
781         }
782     }
783     *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL );
784     return VLC_SUCCESS;
785 }
786
787 static int ExecuteMediaProperty( vlm_t *p_vlm, int64_t id, bool b_new,
788                                  const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
789 {
790     const char *psz_cmd = b_new ? "new" : "setup";
791     vlm_media_t *p_cfg = NULL;
792     int i_result;
793     int i;
794
795 #undef ERROR
796 #undef MISSING
797 #define ERROR( txt ) do { *pp_status = vlm_MessageNew( psz_cmd, txt); goto error; } while(0)
798     if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA, id, &p_cfg ) )
799         ERROR( "unknown media" );
800
801 #define MISSING(cmd) do { if( !psz_value ) ERROR( "missing argument for " cmd ); } while(0)
802     for( i = 0; i < i_property; i++ )
803     {
804         const char *psz_option = ppsz_property[i];
805         const char *psz_value = i+1 < i_property ? ppsz_property[i+1] :  NULL;
806
807         if( !strcmp( psz_option, "enabled" ) )
808         {
809             p_cfg->b_enabled = true;
810         }
811         else if( !strcmp( psz_option, "disabled" ) )
812         {
813             p_cfg->b_enabled = false;
814         }
815         else if( !strcmp( psz_option, "input" ) )
816         {
817             MISSING( "input" );
818             TAB_APPEND( p_cfg->i_input, p_cfg->ppsz_input, strdup(psz_value) );
819             i++;
820         }
821         else if( !strcmp( psz_option, "inputdel" ) && psz_value && !strcmp( psz_value, "all" ) )
822         {
823             while( p_cfg->i_input > 0 )
824                 TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[0] );
825             i++;
826         }
827         else if( !strcmp( psz_option, "inputdel" ) )
828         {
829             int j;
830
831             MISSING( "inputdel" );
832
833             for( j = 0; j < p_cfg->i_input; j++ )
834             {
835                 if( !strcmp( p_cfg->ppsz_input[j], psz_value ) )
836                 {
837                     TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[j] );
838                     break;
839                 }
840             }
841             i++;
842         }
843         else if( !strcmp( psz_option, "inputdeln" ) )
844         {
845             int i_index;
846
847             MISSING( "inputdeln" );
848  
849             i_index = atoi( psz_value );
850             if( i_index > 0 && i_index <= p_cfg->i_input )
851                 TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[i_index-1] );
852             i++;
853         }
854         else if( !strcmp( psz_option, "output" ) )
855         {
856             MISSING( "output" );
857
858             free( p_cfg->psz_output );
859             p_cfg->psz_output = *psz_value ? strdup( psz_value ) : NULL;
860             i++;
861         }
862         else if( !strcmp( psz_option, "option" ) )
863         {
864             MISSING( "option" );
865
866             TAB_APPEND( p_cfg->i_option, p_cfg->ppsz_option, strdup( psz_value ) );
867             i++;
868         }
869         else if( !strcmp( psz_option, "loop" ) )
870         {
871             if( p_cfg->b_vod )
872                 ERROR( "invalid loop option for vod" );
873             p_cfg->broadcast.b_loop = true;
874         }
875         else if( !strcmp( psz_option, "unloop" ) )
876         {
877             if( p_cfg->b_vod )
878                 ERROR( "invalid unloop option for vod" );
879             p_cfg->broadcast.b_loop = false;
880         }
881         else if( !strcmp( psz_option, "mux" ) )
882         {
883             MISSING( "mux" );
884             if( !p_cfg->b_vod )
885                 ERROR( "invalid mux option for broadcast" );
886
887             free( p_cfg->vod.psz_mux );
888             p_cfg->vod.psz_mux = *psz_value ? strdup( psz_value ) : NULL;
889             i++;
890         }
891         else
892         {
893             fprintf( stderr, "PROP: name=%s unknown\n", psz_option );
894             ERROR( "Wrong command syntax" );
895         }
896     }
897 #undef MISSING
898 #undef ERROR
899
900     /* */
901     i_result = vlm_ControlInternal( p_vlm, VLM_CHANGE_MEDIA, p_cfg );
902     vlm_media_Delete( p_cfg );
903
904     *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL );
905     return i_result;
906
907 error:
908     if( p_cfg )
909     {
910         if( b_new )
911             vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_cfg->id );
912         vlm_media_Delete( p_cfg );
913     }
914     return VLC_EGENERIC;
915 }
916
917 static int ExecuteNew( vlm_t *p_vlm, const char *psz_name, const char *psz_type, const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
918 {
919     /* Check name */
920     if( !strcmp( psz_name, "all" ) || !strcmp( psz_name, "media" ) || !strcmp( psz_name, "schedule" ) )
921     {
922         *pp_status = vlm_MessageNew( "new", "\"all\", \"media\" and \"schedule\" are reserved names" );
923         return VLC_EGENERIC;
924     }
925     if( ExecuteIsMedia( p_vlm, psz_name ) || ExecuteIsSchedule( p_vlm, psz_name ) )
926     {
927         *pp_status = vlm_MessageNew( "new", "%s: Name already in use", psz_name );
928         return VLC_EGENERIC;
929     }
930     /* */
931     if( !strcmp( psz_type, "schedule" ) )
932     {
933         vlm_schedule_sys_t *p_schedule = vlm_ScheduleNew( p_vlm, psz_name );
934         if( !p_schedule )
935         {
936             *pp_status = vlm_MessageNew( "new", "could not create schedule" );
937             return VLC_EGENERIC;
938         }
939         return ExecuteScheduleProperty( p_vlm, p_schedule, true, i_property, ppsz_property, pp_status );
940     }
941     else if( !strcmp( psz_type, "vod" ) || !strcmp( psz_type, "broadcast" ) )
942     {
943         vlm_media_t cfg;
944         int64_t id;
945
946         vlm_media_Init( &cfg );
947         cfg.psz_name = strdup( psz_name );
948         cfg.b_vod = !strcmp( psz_type, "vod" );
949
950         if( vlm_ControlInternal( p_vlm, VLM_ADD_MEDIA, &cfg, &id ) )
951         {
952             vlm_media_Clean( &cfg );
953             *pp_status = vlm_MessageNew( "new", "could not create media" );
954             return VLC_EGENERIC;
955         }
956         vlm_media_Clean( &cfg );
957         return ExecuteMediaProperty( p_vlm, id, true, i_property, ppsz_property, pp_status );
958     }
959     else
960     {
961         *pp_status = vlm_MessageNew( "new", "%s: Choose between vod, broadcast or schedule", psz_type );
962         return VLC_EGENERIC;
963     }
964 }
965
966 static int ExecuteSetup( vlm_t *p_vlm, const char *psz_name, const int i_property, char *ppsz_property[], vlm_message_t **pp_status )
967 {
968     if( ExecuteIsSchedule( p_vlm, psz_name ) )
969     {
970         vlm_schedule_sys_t *p_schedule = vlm_ScheduleSearch( p_vlm, psz_name );
971         return ExecuteScheduleProperty( p_vlm, p_schedule, false, i_property, ppsz_property, pp_status );
972     }
973     else if( ExecuteIsMedia( p_vlm, psz_name ) )
974     {
975         int64_t id;
976         if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) )
977             goto error;
978         return ExecuteMediaProperty( p_vlm, id, false, i_property, ppsz_property, pp_status );
979     }
980
981 error:
982     *pp_status = vlm_MessageNew( "setup", "%s unknown", psz_name );
983     return VLC_EGENERIC;
984 }
985
986 static int ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
987                            vlm_message_t **pp_message )
988 {
989     size_t i_command = 0;
990     char buf[strlen (psz_command) + 1], *psz_buf = buf;
991     char *ppsz_command[3+sizeof (buf) / 2];
992     vlm_message_t *p_message = NULL;
993
994     /* First, parse the line and cut it */
995     while( *psz_command != '\0' )
996     {
997         const char *psz_temp;
998
999         if(isspace (*psz_command))
1000         {
1001             psz_command++;
1002             continue;
1003         }
1004
1005         /* support for comments */
1006         if( i_command == 0 && *psz_command == '#')
1007         {
1008             p_message = vlm_MessageNew( "", vlm_NULL );
1009             goto success;
1010         }
1011
1012         psz_temp = FindCommandEnd( psz_command );
1013
1014         if( psz_temp == NULL )
1015         {
1016             p_message = vlm_MessageNew( "Incomplete command", psz_command );
1017             goto error;
1018         }
1019
1020         assert (i_command < (sizeof (ppsz_command) / sizeof (ppsz_command[0])));
1021
1022         ppsz_command[i_command] = psz_buf;
1023         memcpy (psz_buf, psz_command, psz_temp - psz_command);
1024         psz_buf[psz_temp - psz_command] = '\0';
1025
1026         Unescape (psz_buf, psz_buf);
1027
1028         i_command++;
1029         psz_buf += psz_temp - psz_command + 1;
1030         psz_command = psz_temp;
1031
1032         assert (buf + sizeof (buf) >= psz_buf);
1033     }
1034
1035     /*
1036      * And then Interpret it
1037      */
1038
1039 #define IF_EXECUTE( name, check, cmd ) if( !strcmp(ppsz_command[0], name ) ) { if( (check) ) goto syntax_error;  if( (cmd) ) goto error; goto success; }
1040     if( i_command == 0 )
1041     {
1042         p_message = vlm_MessageNew( "", vlm_NULL );
1043         goto success;
1044     }
1045     else IF_EXECUTE( "del",     (i_command != 2),   ExecuteDel(p_vlm, ppsz_command[1], &p_message) )
1046     else IF_EXECUTE( "show",    (i_command > 2),    ExecuteShow(p_vlm, i_command > 1 ? ppsz_command[1] : NULL, &p_message) )
1047     else IF_EXECUTE( "help",    (i_command != 1),   ExecuteHelp( &p_message ) )
1048     else IF_EXECUTE( "control", (i_command < 3),    ExecuteControl(p_vlm, ppsz_command[1], i_command - 2, &ppsz_command[2], &p_message) )
1049     else IF_EXECUTE( "save",    (i_command != 2),   ExecuteSave(p_vlm, ppsz_command[1], &p_message) )
1050     else IF_EXECUTE( "export",  (i_command != 1),   ExecuteExport(p_vlm, &p_message) )
1051     else IF_EXECUTE( "load",    (i_command != 2),   ExecuteLoad(p_vlm, ppsz_command[1], &p_message) )
1052     else IF_EXECUTE( "new",     (i_command < 3),    ExecuteNew(p_vlm, ppsz_command[1], ppsz_command[2], i_command-3, &ppsz_command[3], &p_message) )
1053     else IF_EXECUTE( "setup",   (i_command < 2),    ExecuteSetup(p_vlm, ppsz_command[1], i_command-2, &ppsz_command[2], &p_message) )
1054     else
1055     {
1056         p_message = vlm_MessageNew( ppsz_command[0], "Unknown command" );
1057         goto error;
1058     }
1059 #undef IF_EXECUTE
1060
1061 success:
1062     *pp_message = p_message;
1063     return VLC_SUCCESS;
1064
1065 syntax_error:
1066     return ExecuteSyntaxError( ppsz_command[0], pp_message );
1067
1068 error:
1069     *pp_message = p_message;
1070     return VLC_EGENERIC;
1071 }
1072
1073 /*****************************************************************************
1074  * Media handling
1075  *****************************************************************************/
1076 vlm_media_sys_t *vlm_MediaSearch( vlm_t *vlm, const char *psz_name )
1077 {
1078     int i;
1079
1080     for( i = 0; i < vlm->i_media; i++ )
1081     {
1082         if( strcmp( psz_name, vlm->media[i]->cfg.psz_name ) == 0 )
1083             return vlm->media[i];
1084     }
1085
1086     return NULL;
1087 }
1088
1089 /*****************************************************************************
1090  * Schedule handling
1091  *****************************************************************************/
1092 static int64_t vlm_Date(void)
1093 {
1094 #ifdef WIN32
1095     struct timeb tm;
1096     ftime( &tm );
1097     return ((int64_t)tm.time) * 1000000 + ((int64_t)tm.millitm) * 1000;
1098 #else
1099     struct timeval tv_date;
1100
1101     /* gettimeofday() cannot fail given &tv_date is a valid address */
1102     (void)gettimeofday( &tv_date, NULL );
1103     return (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec;
1104 #endif
1105 }
1106
1107 static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name )
1108 {
1109     vlm_schedule_sys_t *p_sched = malloc( sizeof( vlm_schedule_sys_t ) );
1110
1111     if( !p_sched )
1112     {
1113         return NULL;
1114     }
1115
1116     if( !psz_name )
1117     {
1118         return NULL;
1119     }
1120
1121     p_sched->psz_name = strdup( psz_name );
1122     p_sched->b_enabled = false;
1123     p_sched->i_command = 0;
1124     p_sched->command = NULL;
1125     p_sched->i_date = 0;
1126     p_sched->i_period = 0;
1127     p_sched->i_repeat = -1;
1128
1129     TAB_APPEND( vlm->i_schedule, vlm->schedule, p_sched );
1130
1131     return p_sched;
1132 }
1133
1134 /* for now, simple delete. After, del with options (last arg) */
1135 static void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_sys_t *sched )
1136 {
1137     if( sched == NULL ) return;
1138
1139     TAB_REMOVE( vlm->i_schedule, vlm->schedule, sched );
1140
1141     if( vlm->i_schedule == 0 ) free( vlm->schedule );
1142     free( sched->psz_name );
1143     while( sched->i_command )
1144     {
1145         char *psz_cmd = sched->command[0];
1146         TAB_REMOVE( sched->i_command, sched->command, psz_cmd );
1147         free( psz_cmd );
1148     }
1149     free( sched );
1150 }
1151
1152 static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *vlm, const char *psz_name )
1153 {
1154     int i;
1155
1156     for( i = 0; i < vlm->i_schedule; i++ )
1157     {
1158         if( strcmp( psz_name, vlm->schedule[i]->psz_name ) == 0 )
1159         {
1160             return vlm->schedule[i];
1161         }
1162     }
1163
1164     return NULL;
1165 }
1166
1167 /* Ok, setup schedule command will be able to support only one (argument value) at a time  */
1168 static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd,
1169                        const char *psz_value )
1170 {
1171     if( !strcmp( psz_cmd, "enabled" ) )
1172     {
1173         schedule->b_enabled = true;
1174     }
1175     else if( !strcmp( psz_cmd, "disabled" ) )
1176     {
1177         schedule->b_enabled = false;
1178     }
1179 #if !defined( UNDER_CE )
1180     else if( !strcmp( psz_cmd, "date" ) )
1181     {
1182         struct tm time;
1183         const char *p;
1184         time_t date;
1185
1186         time.tm_sec = 0;         /* seconds */
1187         time.tm_min = 0;         /* minutes */
1188         time.tm_hour = 0;        /* hours */
1189         time.tm_mday = 0;        /* day of the month */
1190         time.tm_mon = 0;         /* month */
1191         time.tm_year = 0;        /* year */
1192         time.tm_wday = 0;        /* day of the week */
1193         time.tm_yday = 0;        /* day in the year */
1194         time.tm_isdst = -1;       /* daylight saving time */
1195
1196         /* date should be year/month/day-hour:minutes:seconds */
1197         p = strchr( psz_value, '-' );
1198
1199         if( !strcmp( psz_value, "now" ) )
1200         {
1201             schedule->i_date = 0;
1202         }
1203         else if( (p == NULL) && sscanf( psz_value, "%d:%d:%d", &time.tm_hour,
1204                                         &time.tm_min, &time.tm_sec ) != 3 )
1205                                         /* it must be a hour:minutes:seconds */
1206         {
1207             return 1;
1208         }
1209         else
1210         {
1211             unsigned i,j,k;
1212
1213             switch( sscanf( p + 1, "%u:%u:%u", &i, &j, &k ) )
1214             {
1215                 case 1:
1216                     time.tm_sec = i;
1217                     break;
1218                 case 2:
1219                     time.tm_min = i;
1220                     time.tm_sec = j;
1221                     break;
1222                 case 3:
1223                     time.tm_hour = i;
1224                     time.tm_min = j;
1225                     time.tm_sec = k;
1226                     break;
1227                 default:
1228                     return 1;
1229             }
1230
1231             switch( sscanf( psz_value, "%d/%d/%d", &i, &j, &k ) )
1232             {
1233                 case 1:
1234                     time.tm_mday = i;
1235                     break;
1236                 case 2:
1237                     time.tm_mon = i - 1;
1238                     time.tm_mday = j;
1239                     break;
1240                 case 3:
1241                     time.tm_year = i - 1900;
1242                     time.tm_mon = j - 1;
1243                     time.tm_mday = k;
1244                     break;
1245                 default:
1246                     return 1;
1247             }
1248
1249             date = mktime( &time );
1250             schedule->i_date = ((mtime_t) date) * 1000000;
1251         }
1252     }
1253     else if( !strcmp( psz_cmd, "period" ) )
1254     {
1255         struct tm time;
1256         const char *p;
1257         const char *psz_time = NULL, *psz_date = NULL;
1258         time_t date;
1259         unsigned i,j,k;
1260
1261         /* First, if date or period are modified, repeat should be equal to -1 */
1262         schedule->i_repeat = -1;
1263
1264         time.tm_sec = 0;         /* seconds */
1265         time.tm_min = 0;         /* minutes */
1266         time.tm_hour = 0;        /* hours */
1267         time.tm_mday = 0;        /* day of the month */
1268         time.tm_mon = 0;         /* month */
1269         time.tm_year = 0;        /* year */
1270         time.tm_wday = 0;        /* day of the week */
1271         time.tm_yday = 0;        /* day in the year */
1272         time.tm_isdst = -1;       /* daylight saving time */
1273
1274         /* date should be year/month/day-hour:minutes:seconds */
1275         p = strchr( psz_value, '-' );
1276         if( p )
1277         {
1278             psz_date = psz_value;
1279             psz_time = p + 1;
1280         }
1281         else
1282         {
1283             psz_time = psz_value;
1284         }
1285
1286         switch( sscanf( psz_time, "%u:%u:%u", &i, &j, &k ) )
1287         {
1288             case 1:
1289                 time.tm_sec = i;
1290                 break;
1291             case 2:
1292                 time.tm_min = i;
1293                 time.tm_sec = j;
1294                 break;
1295             case 3:
1296                 time.tm_hour = i;
1297                 time.tm_min = j;
1298                 time.tm_sec = k;
1299                 break;
1300             default:
1301                 return 1;
1302         }
1303         if( psz_date )
1304         {
1305             switch( sscanf( psz_date, "%u/%u/%u", &i, &j, &k ) )
1306             {
1307                 case 1:
1308                     time.tm_mday = i;
1309                     break;
1310                 case 2:
1311                     time.tm_mon = i;
1312                     time.tm_mday = j;
1313                     break;
1314                 case 3:
1315                     time.tm_year = i;
1316                     time.tm_mon = j;
1317                     time.tm_mday = k;
1318                     break;
1319                 default:
1320                     return 1;
1321             }
1322         }
1323
1324         /* ok, that's stupid... who is going to schedule streams every 42 years ? */
1325         date = (((( time.tm_year * 12 + time.tm_mon ) * 30 + time.tm_mday ) * 24 + time.tm_hour ) * 60 + time.tm_min ) * 60 + time.tm_sec ;
1326         schedule->i_period = ((mtime_t) date) * 1000000;
1327     }
1328 #endif /* UNDER_CE */
1329     else if( !strcmp( psz_cmd, "repeat" ) )
1330     {
1331         int i;
1332
1333         if( sscanf( psz_value, "%d", &i ) == 1 )
1334         {
1335             schedule->i_repeat = i;
1336         }
1337         else
1338         {
1339             return 1;
1340         }
1341     }
1342     else if( !strcmp( psz_cmd, "append" ) )
1343     {
1344         char *command = strdup( psz_value );
1345
1346         TAB_APPEND( schedule->i_command, schedule->command, command );
1347     }
1348     else
1349     {
1350         return 1;
1351     }
1352     return 0;
1353 }
1354
1355 /*****************************************************************************
1356  * Message handling functions
1357  *****************************************************************************/
1358 vlm_message_t *vlm_MessageNew( const char *psz_name,
1359                                const char *psz_format, ... )
1360 {
1361     vlm_message_t *p_message;
1362     va_list args;
1363
1364     if( !psz_name ) return NULL;
1365
1366     p_message = malloc( sizeof(vlm_message_t) );
1367     if( !p_message)
1368     {
1369         return NULL;
1370     }
1371
1372     p_message->psz_value = 0;
1373
1374     if( psz_format )
1375     {
1376         va_start( args, psz_format );
1377         if( vasprintf( &p_message->psz_value, psz_format, args ) == -1 )
1378         {
1379             va_end( args );
1380             free( p_message );
1381             return NULL;
1382         }
1383         va_end( args );
1384     }
1385
1386     p_message->psz_name = strdup( psz_name );
1387     p_message->i_child = 0;
1388     p_message->child = NULL;
1389
1390     return p_message;
1391 }
1392
1393 void vlm_MessageDelete( vlm_message_t *p_message )
1394 {
1395     free( p_message->psz_name );
1396     free( p_message->psz_value );
1397     while( p_message->i_child-- )
1398         vlm_MessageDelete( p_message->child[p_message->i_child] );
1399     free( p_message->child );
1400     free( p_message );
1401 }
1402
1403 /* Add a child */
1404 vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message,
1405                                vlm_message_t *p_child )
1406 {
1407     if( p_message == NULL ) return NULL;
1408
1409     if( p_child )
1410     {
1411         TAB_APPEND( p_message->i_child, p_message->child, p_child );
1412     }
1413
1414     return p_child;
1415 }
1416
1417 /*****************************************************************************
1418  * Misc utility functions
1419  *****************************************************************************/
1420 static vlm_message_t *vlm_ShowMedia( vlm_media_sys_t *p_media )
1421 {
1422     vlm_media_t *p_cfg = &p_media->cfg;
1423     vlm_message_t *p_msg;
1424     vlm_message_t *p_msg_sub;
1425     int i;
1426
1427     p_msg = vlm_MessageNew( p_cfg->psz_name, vlm_NULL );
1428     vlm_MessageAdd( p_msg,
1429                     vlm_MessageNew( "type", p_cfg->b_vod ? "vod" : "broadcast" ) );
1430     vlm_MessageAdd( p_msg,
1431                     vlm_MessageNew( "enabled", p_cfg->b_enabled ? "yes" : "no" ) );
1432
1433     if( p_cfg->b_vod )
1434         vlm_MessageAdd( p_msg,
1435                         vlm_MessageNew( "mux", p_cfg->vod.psz_mux ) );
1436     else
1437         vlm_MessageAdd( p_msg,
1438                         vlm_MessageNew( "loop", p_cfg->broadcast.b_loop ? "yes" : "no" ) );
1439
1440     p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "inputs", vlm_NULL ) );
1441     for( i = 0; i < p_cfg->i_input; i++ )
1442     {
1443         char *psz_tmp;
1444         asprintf( &psz_tmp, "%d", i+1 );
1445         vlm_MessageAdd( p_msg_sub,
1446                         vlm_MessageNew( psz_tmp, p_cfg->ppsz_input[i] ) );
1447         free( psz_tmp );
1448     }
1449
1450     vlm_MessageAdd( p_msg,
1451                     vlm_MessageNew( "output", p_cfg->psz_output ? p_cfg->psz_output : "" ) );
1452
1453     p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "options", vlm_NULL ) );
1454     for( i = 0; i < p_cfg->i_option; i++ )
1455         vlm_MessageAdd( p_msg_sub, vlm_MessageNew( p_cfg->ppsz_option[i], vlm_NULL ) );
1456
1457     p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "instances", vlm_NULL ) );
1458     for( i = 0; i < p_media->i_instance; i++ )
1459     {
1460         vlm_media_instance_sys_t *p_instance = p_media->instance[i];
1461         vlc_value_t val;
1462         vlm_message_t *p_msg_instance;
1463         char *psz_tmp;
1464
1465         val.i_int = END_S;
1466         if( p_instance->p_input )
1467             var_Get( p_instance->p_input, "state", &val );
1468
1469         p_msg_instance = vlm_MessageAdd( p_msg_sub, vlm_MessageNew( "instance" , vlm_NULL ) );
1470
1471         vlm_MessageAdd( p_msg_instance,
1472                         vlm_MessageNew( "name" , p_instance->psz_name ? p_instance->psz_name : "default" ) );
1473         vlm_MessageAdd( p_msg_instance,
1474                         vlm_MessageNew( "state",
1475                             val.i_int == PLAYING_S ? "playing" :
1476                             val.i_int == PAUSE_S ? "paused" :
1477                             "stopped" ) );
1478
1479         /* FIXME should not do that this way */
1480         if( p_instance->p_input )
1481         {
1482 #define APPEND_INPUT_INFO( a, format, type ) \
1483             asprintf( &psz_tmp, format, \
1484                       var_Get ## type( p_instance->p_input, a ) ); \
1485             vlm_MessageAdd( p_msg_instance, vlm_MessageNew( a, psz_tmp ) ); \
1486             free( psz_tmp );
1487             APPEND_INPUT_INFO( "position", "%f", Float );
1488             APPEND_INPUT_INFO( "time", "%"PRIi64, Time );
1489             APPEND_INPUT_INFO( "length", "%"PRIi64, Time );
1490             APPEND_INPUT_INFO( "rate", "%d", Integer );
1491             APPEND_INPUT_INFO( "title", "%d", Integer );
1492             APPEND_INPUT_INFO( "chapter", "%d", Integer );
1493             APPEND_INPUT_INFO( "seekable", "%d", Bool );
1494         }
1495 #undef APPEND_INPUT_INFO
1496         asprintf( &psz_tmp, "%d", p_instance->i_index + 1 );
1497         vlm_MessageAdd( p_msg_instance, vlm_MessageNew( "playlistindex", psz_tmp ) );
1498         free( psz_tmp );
1499     }
1500     return p_msg;
1501 }
1502
1503 static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_sys_t *media,
1504                                 vlm_schedule_sys_t *schedule,
1505                                 const char *psz_filter )
1506 {
1507     if( media != NULL )
1508     {
1509         vlm_message_t *p_msg = vlm_MessageNew( "show", vlm_NULL );
1510         if( p_msg )
1511             vlm_MessageAdd( p_msg, vlm_ShowMedia( media ) );
1512         return p_msg;
1513     }
1514
1515     else if( schedule != NULL )
1516     {
1517         int i;
1518         vlm_message_t *msg;
1519         vlm_message_t *msg_schedule;
1520         vlm_message_t *msg_child;
1521         char buffer[100];
1522
1523         msg = vlm_MessageNew( "show", vlm_NULL );
1524         msg_schedule =
1525             vlm_MessageAdd( msg, vlm_MessageNew( schedule->psz_name, vlm_NULL ) );
1526
1527         vlm_MessageAdd( msg_schedule, vlm_MessageNew("type", "schedule") );
1528
1529         vlm_MessageAdd( msg_schedule,
1530                         vlm_MessageNew( "enabled", schedule->b_enabled ?
1531                                         "yes" : "no" ) );
1532
1533 #if !defined( UNDER_CE )
1534         if( schedule->i_date != 0 )
1535         {
1536             struct tm date;
1537             time_t i_time = (time_t)( schedule->i_date / 1000000 );
1538             char *psz_date;
1539
1540             localtime_r( &i_time, &date);
1541             asprintf( &psz_date, "%d/%d/%d-%d:%d:%d",
1542                       date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1543                       date.tm_hour, date.tm_min, date.tm_sec );
1544
1545             vlm_MessageAdd( msg_schedule,
1546                             vlm_MessageNew( "date", psz_date ) );
1547             free( psz_date );
1548         }
1549         else
1550         {
1551             vlm_MessageAdd( msg_schedule, vlm_MessageNew("date", "now") );
1552         }
1553
1554         if( schedule->i_period != 0 )
1555         {
1556             time_t i_time = (time_t) ( schedule->i_period / 1000000 );
1557             struct tm date;
1558
1559             date.tm_sec = (int)( i_time % 60 );
1560             i_time = i_time / 60;
1561             date.tm_min = (int)( i_time % 60 );
1562             i_time = i_time / 60;
1563             date.tm_hour = (int)( i_time % 24 );
1564             i_time = i_time / 24;
1565             date.tm_mday = (int)( i_time % 30 );
1566             i_time = i_time / 30;
1567             /* okay, okay, months are not always 30 days long */
1568             date.tm_mon = (int)( i_time % 12 );
1569             i_time = i_time / 12;
1570             date.tm_year = (int)i_time;
1571
1572             sprintf( buffer, "%d/%d/%d-%d:%d:%d", date.tm_year, date.tm_mon,
1573                      date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
1574
1575             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", buffer) );
1576         }
1577         else
1578         {
1579             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", "0") );
1580         }
1581 #endif /* UNDER_CE */
1582
1583         sprintf( buffer, "%d", schedule->i_repeat );
1584         vlm_MessageAdd( msg_schedule, vlm_MessageNew( "repeat", buffer ) );
1585
1586         msg_child =
1587             vlm_MessageAdd( msg_schedule, vlm_MessageNew("commands", vlm_NULL ) );
1588
1589         for( i = 0; i < schedule->i_command; i++ )
1590         {
1591            vlm_MessageAdd( msg_child,
1592                            vlm_MessageNew( schedule->command[i], vlm_NULL ) );
1593         }
1594
1595         return msg;
1596
1597     }
1598
1599     else if( psz_filter && !strcmp( psz_filter, "media" ) )
1600     {
1601         vlm_message_t *p_msg;
1602         vlm_message_t *p_msg_child;
1603         int i_vod = 0, i_broadcast = 0;
1604         int i;
1605         char *psz_count;
1606
1607         for( i = 0; i < vlm->i_media; i++ )
1608         {
1609             if( vlm->media[i]->cfg.b_vod )
1610                 i_vod++;
1611             else
1612                 i_broadcast++;
1613         }
1614
1615         asprintf( &psz_count, "( %d broadcast - %d vod )", i_broadcast, i_vod);
1616
1617         p_msg = vlm_MessageNew( "show", vlm_NULL );
1618         p_msg_child = vlm_MessageAdd( p_msg, vlm_MessageNew( "media", psz_count ) );
1619         free( psz_count );
1620
1621         for( i = 0; i < vlm->i_media; i++ )
1622             vlm_MessageAdd( p_msg_child, vlm_ShowMedia( vlm->media[i] ) );
1623
1624         return p_msg;
1625     }
1626
1627     else if( psz_filter && !strcmp( psz_filter, "schedule" ) )
1628     {
1629         int i;
1630         vlm_message_t *msg;
1631         vlm_message_t *msg_child;
1632
1633         msg = vlm_MessageNew( "show", vlm_NULL );
1634         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "schedule", vlm_NULL ) );
1635
1636         for( i = 0; i < vlm->i_schedule; i++ )
1637         {
1638             vlm_schedule_sys_t *s = vlm->schedule[i];
1639             vlm_message_t *msg_schedule;
1640             mtime_t i_time, i_next_date;
1641
1642             msg_schedule = vlm_MessageAdd( msg_child,
1643                                            vlm_MessageNew( s->psz_name, vlm_NULL ) );
1644             vlm_MessageAdd( msg_schedule,
1645                             vlm_MessageNew( "enabled", s->b_enabled ?
1646                                             "yes" : "no" ) );
1647
1648             /* calculate next date */
1649             i_time = vlm_Date();
1650             i_next_date = s->i_date;
1651
1652             if( s->i_period != 0 )
1653             {
1654                 int j = 0;
1655                 while( s->i_date + j * s->i_period <= i_time &&
1656                        s->i_repeat > j )
1657                 {
1658                     j++;
1659                 }
1660
1661                 i_next_date = s->i_date + j * s->i_period;
1662             }
1663
1664             if( i_next_date > i_time )
1665             {
1666                 time_t i_date = (time_t) (i_next_date / 1000000) ;
1667
1668 #if !defined( UNDER_CE )
1669 #ifdef HAVE_CTIME_R
1670                 char psz_date[500];
1671                 ctime_r( &i_date, psz_date );
1672 #else
1673                 char *psz_date = ctime( &i_date );
1674 #endif
1675
1676                 vlm_MessageAdd( msg_schedule,
1677                                 vlm_MessageNew( "next launch", psz_date ) );
1678 #endif
1679             }
1680         }
1681
1682         return msg;
1683     }
1684
1685     else if( ( psz_filter == NULL ) && ( media == NULL ) && ( schedule == NULL ) )
1686     {
1687         vlm_message_t *show1 = vlm_Show( vlm, NULL, NULL, "media" );
1688         vlm_message_t *show2 = vlm_Show( vlm, NULL, NULL, "schedule" );
1689
1690         vlm_MessageAdd( show1, show2->child[0] );
1691
1692         /* We must destroy the parent node "show" of show2
1693          * and not the children */
1694         free( show2->psz_name );
1695         free( show2 );
1696
1697         return show1;
1698     }
1699
1700     else
1701     {
1702         return vlm_MessageNew( "show", vlm_NULL );
1703     }
1704 }
1705
1706 /*****************************************************************************
1707  * Config handling functions
1708  *****************************************************************************/
1709 static int Load( vlm_t *vlm, char *file )
1710 {
1711     char *pf = file;
1712     int  i_line = 1;
1713
1714     while( *pf != '\0' )
1715     {
1716         vlm_message_t *message = NULL;
1717         int i_end = 0;
1718
1719         while( pf[i_end] != '\n' && pf[i_end] != '\0' && pf[i_end] != '\r' )
1720         {
1721             i_end++;
1722         }
1723
1724         if( pf[i_end] == '\r' || pf[i_end] == '\n' )
1725         {
1726             pf[i_end] = '\0';
1727             i_end++;
1728             if( pf[i_end] == '\n' ) i_end++;
1729         }
1730
1731         if( *pf && ExecuteCommand( vlm, pf, &message ) )
1732         {
1733             if( message )
1734             {
1735                 if( message->psz_value )
1736                     msg_Err( vlm, "Load error on line %d: %s: %s",
1737                              i_line, message->psz_name, message->psz_value );
1738                 vlm_MessageDelete( message );
1739             }
1740             return 1;
1741         }
1742         if( message ) vlm_MessageDelete( message );
1743
1744         pf += i_end;
1745         i_line++;
1746     }
1747
1748     return 0;
1749 }
1750
1751 static char *Save( vlm_t *vlm )
1752 {
1753     char *save = NULL;
1754     char psz_header[] = "\n"
1755                         "# VLC media player VLM command batch\n"
1756                         "# http://www.videolan.org/vlc/\n\n" ;
1757     char *p;
1758     int i,j;
1759     int i_length = strlen( psz_header );
1760
1761     for( i = 0; i < vlm->i_media; i++ )
1762     {
1763         vlm_media_sys_t *media = vlm->media[i];
1764         vlm_media_t *p_cfg = &media->cfg;
1765
1766         if( p_cfg->b_vod )
1767             i_length += strlen( "new * vod " ) + strlen(p_cfg->psz_name);
1768         else
1769             i_length += strlen( "new * broadcast " ) + strlen(p_cfg->psz_name);
1770
1771         if( p_cfg->b_enabled == true )
1772             i_length += strlen( "enabled" );
1773         else
1774             i_length += strlen( "disabled" );
1775
1776         if( !p_cfg->b_vod && p_cfg->broadcast.b_loop == true )
1777             i_length += strlen( " loop\n" );
1778         else
1779             i_length += strlen( "\n" );
1780
1781         for( j = 0; j < p_cfg->i_input; j++ )
1782             i_length += strlen( "setup * input \"\"\n" ) + strlen( p_cfg->psz_name ) + strlen( p_cfg->ppsz_input[j] );
1783
1784         if( p_cfg->psz_output != NULL )
1785             i_length += strlen( "setup * output \n" ) + strlen(p_cfg->psz_name) + strlen(p_cfg->psz_output);
1786
1787         for( j = 0; j < p_cfg->i_option; j++ )
1788             i_length += strlen("setup * option \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->ppsz_option[j]);
1789
1790         if( p_cfg->b_vod && p_cfg->vod.psz_mux )
1791             i_length += strlen("setup * mux \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->vod.psz_mux);
1792     }
1793
1794     for( i = 0; i < vlm->i_schedule; i++ )
1795     {
1796         vlm_schedule_sys_t *schedule = vlm->schedule[i];
1797
1798         i_length += strlen( "new  schedule " ) + strlen( schedule->psz_name );
1799
1800         if( schedule->b_enabled == true )
1801         {
1802             i_length += strlen( "date //-:: enabled\n" ) + 14;
1803         }
1804         else
1805         {
1806             i_length += strlen( "date //-:: disabled\n" ) + 14;
1807         }
1808
1809
1810         if( schedule->i_period != 0 )
1811         {
1812             i_length += strlen( "setup  " ) + strlen( schedule->psz_name ) +
1813                 strlen( "period //-::\n" ) + 14;
1814         }
1815
1816         if( schedule->i_repeat >= 0 )
1817         {
1818             char buffer[12];
1819
1820             sprintf( buffer, "%d", schedule->i_repeat );
1821             i_length += strlen( "setup  repeat \n" ) +
1822                 strlen( schedule->psz_name ) + strlen( buffer );
1823         }
1824         else
1825         {
1826             i_length++;
1827         }
1828
1829         for( j = 0; j < schedule->i_command; j++ )
1830         {
1831             i_length += strlen( "setup  append \n" ) +
1832                 strlen( schedule->psz_name ) + strlen( schedule->command[j] );
1833         }
1834
1835     }
1836
1837     /* Don't forget the '\0' */
1838     i_length++;
1839     /* now we have the length of save */
1840
1841     p = save = malloc( i_length );
1842     if( !save ) return NULL;
1843     *save = '\0';
1844
1845     p += sprintf( p, "%s", psz_header );
1846
1847     /* finally we can write in it */
1848     for( i = 0; i < vlm->i_media; i++ )
1849     {
1850         vlm_media_sys_t *media = vlm->media[i];
1851         vlm_media_t *p_cfg = &media->cfg;
1852
1853         if( p_cfg->b_vod )
1854             p += sprintf( p, "new %s vod ", p_cfg->psz_name );
1855         else
1856             p += sprintf( p, "new %s broadcast ", p_cfg->psz_name );
1857
1858         if( p_cfg->b_enabled )
1859             p += sprintf( p, "enabled" );
1860         else
1861             p += sprintf( p, "disabled" );
1862
1863         if( !p_cfg->b_vod && p_cfg->broadcast.b_loop )
1864             p += sprintf( p, " loop\n" );
1865         else
1866             p += sprintf( p, "\n" );
1867
1868         for( j = 0; j < p_cfg->i_input; j++ )
1869             p += sprintf( p, "setup %s input \"%s\"\n", p_cfg->psz_name, p_cfg->ppsz_input[j] );
1870
1871         if( p_cfg->psz_output )
1872             p += sprintf( p, "setup %s output %s\n", p_cfg->psz_name, p_cfg->psz_output );
1873
1874         for( j = 0; j < p_cfg->i_option; j++ )
1875             p += sprintf( p, "setup %s option %s\n", p_cfg->psz_name, p_cfg->ppsz_option[j] );
1876
1877         if( p_cfg->b_vod && p_cfg->vod.psz_mux )
1878             p += sprintf( p, "setup %s mux %s\n", p_cfg->psz_name, p_cfg->vod.psz_mux );
1879     }
1880
1881     /* and now, the schedule scripts */
1882 #if !defined( UNDER_CE )
1883     for( i = 0; i < vlm->i_schedule; i++ )
1884     {
1885         vlm_schedule_sys_t *schedule = vlm->schedule[i];
1886         struct tm date;
1887         time_t i_time = (time_t) ( schedule->i_date / 1000000 );
1888
1889         localtime_r( &i_time, &date);
1890         p += sprintf( p, "new %s schedule ", schedule->psz_name);
1891
1892         if( schedule->b_enabled == true )
1893         {
1894             p += sprintf( p, "date %d/%d/%d-%d:%d:%d enabled\n",
1895                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1896                           date.tm_hour, date.tm_min, date.tm_sec );
1897         }
1898         else
1899         {
1900             p += sprintf( p, "date %d/%d/%d-%d:%d:%d disabled\n",
1901                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1902                           date.tm_hour, date.tm_min, date.tm_sec);
1903         }
1904
1905         if( schedule->i_period != 0 )
1906         {
1907             p += sprintf( p, "setup %s ", schedule->psz_name );
1908
1909             i_time = (time_t) ( schedule->i_period / 1000000 );
1910
1911             date.tm_sec = (int)( i_time % 60 );
1912             i_time = i_time / 60;
1913             date.tm_min = (int)( i_time % 60 );
1914             i_time = i_time / 60;
1915             date.tm_hour = (int)( i_time % 24 );
1916             i_time = i_time / 24;
1917             date.tm_mday = (int)( i_time % 30 );
1918             i_time = i_time / 30;
1919             /* okay, okay, months are not always 30 days long */
1920             date.tm_mon = (int)( i_time % 12 );
1921             i_time = i_time / 12;
1922             date.tm_year = (int)i_time;
1923
1924             p += sprintf( p, "period %d/%d/%d-%d:%d:%d\n",
1925                           date.tm_year, date.tm_mon, date.tm_mday,
1926                           date.tm_hour, date.tm_min, date.tm_sec);
1927         }
1928
1929         if( schedule->i_repeat >= 0 )
1930         {
1931             p += sprintf( p, "setup %s repeat %d\n",
1932                           schedule->psz_name, schedule->i_repeat );
1933         }
1934         else
1935         {
1936             p += sprintf( p, "\n" );
1937         }
1938
1939         for( j = 0; j < schedule->i_command; j++ )
1940         {
1941             p += sprintf( p, "setup %s append %s\n",
1942                           schedule->psz_name, schedule->command[j] );
1943         }
1944
1945     }
1946 #endif /* UNDER_CE */
1947
1948     return save;
1949 }
1950
1951 /*****************************************************************************
1952  *
1953  *****************************************************************************/
1954 static int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
1955                                 const char *psz_id, int i_query, va_list args )
1956 {
1957     vlm_t *vlm = (vlm_t *)p_private;
1958     int i, i_ret;
1959     const char *psz;
1960     int64_t id;
1961
1962     if( !p_private || !p_vod_media )
1963         return VLC_EGENERIC;
1964
1965     vlc_mutex_lock( &vlm->lock );
1966
1967     /* Find media id */
1968     for( i = 0, id = -1; i < vlm->i_media; i++ )
1969     {
1970         if( p_vod_media == vlm->media[i]->vod.p_media )
1971         {
1972             id = vlm->media[i]->cfg.id;
1973             break;
1974         }
1975     }
1976     if( id == -1 )
1977     {
1978         vlc_mutex_unlock( &vlm->lock );
1979         return VLC_EGENERIC;
1980     }
1981
1982     switch( i_query )
1983     {
1984     case VOD_MEDIA_PLAY:
1985         psz = (const char *)va_arg( args, const char * );
1986         i_ret = vlm_ControlInternal( vlm, VLM_START_MEDIA_VOD_INSTANCE, id, psz_id, 0, psz );
1987         break;
1988
1989     case VOD_MEDIA_PAUSE:
1990         i_ret = vlm_ControlInternal( vlm, VLM_PAUSE_MEDIA_INSTANCE, id, psz_id );
1991         break;
1992
1993     case VOD_MEDIA_STOP:
1994         i_ret = vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, id, psz_id );
1995         break;
1996
1997     case VOD_MEDIA_SEEK:
1998     {
1999         double d_position = (double)va_arg( args, double );
2000         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position/100.0 );
2001         break;
2002     }
2003
2004     case VOD_MEDIA_REWIND:
2005     {
2006         double d_scale = (double)va_arg( args, double );
2007         double d_position;
2008
2009         vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
2010         d_position -= (d_scale / 1000.0);
2011         if( d_position < 0.0 )
2012             d_position = 0.0;
2013         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
2014         break;
2015     }
2016
2017     case VOD_MEDIA_FORWARD:
2018     {
2019         double d_scale = (double)va_arg( args, double );
2020         double d_position;
2021
2022         vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
2023         d_position += (d_scale / 1000.0);
2024         if( d_position > 1.0 )
2025             d_position = 1.0;
2026         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
2027         break;
2028     }
2029
2030     default:
2031         i_ret = VLC_EGENERIC;
2032         break;
2033     }
2034
2035     vlc_mutex_unlock( &vlm->lock );
2036
2037     return i_ret;
2038 }
2039
2040
2041 /*****************************************************************************
2042  * Manage:
2043  *****************************************************************************/
2044 static int Manage( vlc_object_t* p_object )
2045 {
2046     vlm_t *vlm = (vlm_t*)p_object;
2047     int i, j;
2048     mtime_t i_lastcheck;
2049     mtime_t i_time;
2050
2051     i_lastcheck = vlm_Date();
2052
2053     while( !vlm->b_die )
2054     {
2055         char **ppsz_scheduled_commands = NULL;
2056         int    i_scheduled_commands = 0;
2057
2058         vlc_mutex_lock( &vlm->lock );
2059
2060         /* destroy the inputs that wants to die, and launch the next input */
2061         for( i = 0; i < vlm->i_media; i++ )
2062         {
2063             vlm_media_sys_t *p_media = vlm->media[i];
2064
2065             for( j = 0; j < p_media->i_instance; )
2066             {
2067                 vlm_media_instance_sys_t *p_instance = p_media->instance[j];
2068
2069                 if( p_instance->p_input && ( p_instance->p_input->b_eof || p_instance->p_input->b_error ) )
2070                 {
2071                     int i_new_input_index;
2072
2073                     /* */
2074                     i_new_input_index = p_instance->i_index + 1;
2075                     if( !p_media->cfg.b_vod && p_media->cfg.broadcast.b_loop && i_new_input_index >= p_media->cfg.i_input )
2076                         i_new_input_index = 0;
2077
2078                     /* FIXME implement multiple input with VOD */
2079                     if( p_media->cfg.b_vod || i_new_input_index >= p_media->cfg.i_input )
2080                         vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, p_instance->psz_name );
2081                     else
2082                         vlm_ControlInternal( vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, p_instance->psz_name, i_new_input_index );
2083
2084                     j = 0;
2085                 }
2086                 else
2087                 {
2088                     j++;
2089                 }
2090             }
2091         }
2092
2093         /* scheduling */
2094         i_time = vlm_Date();
2095
2096         for( i = 0; i < vlm->i_schedule; i++ )
2097         {
2098             mtime_t i_real_date = vlm->schedule[i]->i_date;
2099
2100             if( vlm->schedule[i]->b_enabled == true )
2101             {
2102                 if( vlm->schedule[i]->i_date == 0 ) // now !
2103                 {
2104                     vlm->schedule[i]->i_date = (i_time / 1000000) * 1000000 ;
2105                     i_real_date = i_time;
2106                 }
2107                 else if( vlm->schedule[i]->i_period != 0 )
2108                 {
2109                     int j = 0;
2110                     while( vlm->schedule[i]->i_date + j *
2111                            vlm->schedule[i]->i_period <= i_lastcheck &&
2112                            ( vlm->schedule[i]->i_repeat > j ||
2113                              vlm->schedule[i]->i_repeat == -1 ) )
2114                     {
2115                         j++;
2116                     }
2117
2118                     i_real_date = vlm->schedule[i]->i_date + j *
2119                         vlm->schedule[i]->i_period;
2120                 }
2121
2122                 if( i_real_date <= i_time && i_real_date > i_lastcheck )
2123                 {
2124                     for( j = 0; j < vlm->schedule[i]->i_command; j++ )
2125                     {
2126                         TAB_APPEND( i_scheduled_commands,
2127                                     ppsz_scheduled_commands,
2128                                     strdup(vlm->schedule[i]->command[j] ) );
2129                     }
2130                 }
2131             }
2132         }
2133         while( i_scheduled_commands )
2134         {
2135             vlm_message_t *message = NULL;
2136             char *psz_command = ppsz_scheduled_commands[0];
2137             ExecuteCommand( vlm, psz_command,&message );
2138
2139             /* for now, drop the message */
2140             vlm_MessageDelete( message );
2141             TAB_REMOVE( i_scheduled_commands,
2142                         ppsz_scheduled_commands,
2143                         psz_command );
2144             free( psz_command );
2145         }
2146
2147         i_lastcheck = i_time;
2148
2149         vlc_mutex_unlock( &vlm->lock );
2150
2151         msleep( 100000 );
2152     }
2153
2154     return VLC_SUCCESS;
2155 }
2156
2157 /* New API
2158  */
2159 /*
2160 typedef struct
2161 {
2162     struct
2163     {
2164         int i_connection_count;
2165         int i_connection_active;
2166     } vod;
2167     struct
2168     {
2169         int        i_count;
2170         bool b_playing;
2171         int        i_playing_index;
2172     } broadcast;
2173
2174 } vlm_media_status_t;
2175 */
2176
2177 /* */
2178 static vlm_media_sys_t *vlm_ControlMediaGetById( vlm_t *p_vlm, int64_t id )
2179 {
2180     int i;
2181
2182     for( i = 0; i < p_vlm->i_media; i++ )
2183     {
2184         if( p_vlm->media[i]->cfg.id == id )
2185             return p_vlm->media[i];
2186     }
2187     return NULL;
2188 }
2189 static vlm_media_sys_t *vlm_ControlMediaGetByName( vlm_t *p_vlm, const char *psz_name )
2190 {
2191     int i;
2192
2193     for( i = 0; i < p_vlm->i_media; i++ )
2194     {
2195         if( !strcmp( p_vlm->media[i]->cfg.psz_name, psz_name ) )
2196             return p_vlm->media[i];
2197     }
2198     return NULL;
2199 }
2200 static int vlm_MediaDescriptionCheck( vlm_t *p_vlm, vlm_media_t *p_cfg )
2201 {
2202     int i;
2203
2204     if( !p_cfg || !p_cfg->psz_name ||
2205         !strcmp( p_cfg->psz_name, "all" ) || !strcmp( p_cfg->psz_name, "media" ) || !strcmp( p_cfg->psz_name, "schedule" ) )
2206         return VLC_EGENERIC;
2207
2208     for( i = 0; i < p_vlm->i_media; i++ )
2209     {
2210         if( p_vlm->media[i]->cfg.id == p_cfg->id )
2211             continue;
2212         if( !strcmp( p_vlm->media[i]->cfg.psz_name, p_cfg->psz_name ) )
2213             return VLC_EGENERIC;
2214     }
2215     return VLC_SUCCESS;
2216 }
2217
2218
2219 /* Called after a media description is changed/added */
2220 static int vlm_OnMediaUpdate( vlm_t *p_vlm, vlm_media_sys_t *p_media )
2221 {
2222     vlm_media_t *p_cfg = &p_media->cfg;
2223     /* Check if we need to create/delete a vod media */
2224     if( p_cfg->b_vod )
2225     {
2226         if( !p_cfg->b_enabled && p_media->vod.p_media )
2227         {
2228             p_vlm->p_vod->pf_media_del( p_vlm->p_vod, p_media->vod.p_media );
2229             p_media->vod.p_media = NULL;
2230         }
2231         else if( p_cfg->b_enabled && !p_media->vod.p_media && p_cfg->i_input )
2232         {
2233             /* Pre-parse the input */
2234             input_thread_t *p_input;
2235             char *psz_output;
2236             char *psz_header;
2237             char *psz_dup;
2238             int i;
2239
2240             vlc_gc_decref( p_media->vod.p_item );
2241             p_media->vod.p_item = input_ItemNew( p_vlm, p_cfg->ppsz_input[0],
2242                 p_cfg->psz_name );
2243
2244             if( p_cfg->psz_output )
2245                 asprintf( &psz_output, "%s:description", p_cfg->psz_output );
2246             else
2247                 asprintf( &psz_output, "#description" );
2248
2249             asprintf( &psz_dup, "sout=%s", psz_output);
2250             input_ItemAddOption( p_media->vod.p_item, psz_dup );
2251             free( psz_dup );
2252             for( i = 0; i < p_cfg->i_option; i++ )
2253                 input_ItemAddOption( p_media->vod.p_item,
2254                                      p_cfg->ppsz_option[i] );
2255             input_ItemAddOption( p_media->vod.p_item, "no-sout-keep" );
2256
2257             asprintf( &psz_header, _("Media: %s"), p_cfg->psz_name );
2258
2259             if( (p_input = input_CreateThreadExtended( p_vlm, p_media->vod.p_item, psz_header, NULL ) ) )
2260             {
2261                 while( !p_input->b_eof && !p_input->b_error )
2262                     msleep( 100000 );
2263
2264                 input_StopThread( p_input );
2265                 vlc_object_release( p_input );
2266             }
2267             free( psz_output );
2268             free( psz_header );
2269
2270             if( p_cfg->vod.psz_mux )
2271             {
2272                 input_item_t item;
2273                 es_format_t es, *p_es = &es;
2274                 char fourcc[5];
2275
2276                 sprintf( fourcc, "%4.4s", p_cfg->vod.psz_mux );
2277                 fourcc[0] = tolower(fourcc[0]); fourcc[1] = tolower(fourcc[1]);
2278                 fourcc[2] = tolower(fourcc[2]); fourcc[3] = tolower(fourcc[3]);
2279
2280                 /* XXX: Don't do it that way, but properly use a new input item ref. */
2281                 item = *p_media->vod.p_item;
2282                 item.i_es = 1;
2283                 item.es = &p_es;
2284                 es_format_Init( &es, VIDEO_ES, *((int *)fourcc) );
2285
2286                 p_media->vod.p_media =
2287                     p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, &item );
2288             }
2289             else
2290             {
2291                 p_media->vod.p_media =
2292                     p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, p_media->vod.p_item );
2293             }
2294         }
2295     }
2296     else
2297     {
2298         /* TODO start media if needed */
2299     }
2300
2301     /* TODO add support of var vlm_media_broadcast/vlm_media_vod */
2302
2303     return VLC_SUCCESS;
2304 }
2305 static int vlm_ControlMediaChange( vlm_t *p_vlm, vlm_media_t *p_cfg )
2306 {
2307     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, p_cfg->id );
2308
2309     /* */
2310     if( !p_media || vlm_MediaDescriptionCheck( p_vlm, p_cfg ) )
2311         return VLC_EGENERIC;
2312     if( ( p_media->cfg.b_vod && !p_cfg->b_vod ) || ( !p_media->cfg.b_vod && p_cfg->b_vod ) )
2313         return VLC_EGENERIC;
2314
2315     if( 0 )
2316     {
2317         /* TODO check what are the changes being done (stop instance if needed) */
2318     }
2319
2320     vlm_media_Clean( &p_media->cfg );
2321     vlm_media_Copy( &p_media->cfg, p_cfg );
2322
2323     return vlm_OnMediaUpdate( p_vlm, p_media );
2324 }
2325
2326 static int vlm_ControlMediaAdd( vlm_t *p_vlm, vlm_media_t *p_cfg, int64_t *p_id )
2327 {
2328     vlm_media_sys_t *p_media;
2329
2330     if( vlm_MediaDescriptionCheck( p_vlm, p_cfg ) || vlm_ControlMediaGetByName( p_vlm, p_cfg->psz_name ) )
2331     {
2332         msg_Err( p_vlm, "invalid media description" );
2333         return VLC_EGENERIC;
2334     }
2335     /* Check if we need to load the VOD server */
2336     if( p_cfg->b_vod && !p_vlm->i_vod )
2337     {
2338         p_vlm->p_vod = vlc_custom_create( VLC_OBJECT(p_vlm), sizeof( vod_t ),
2339                                           VLC_OBJECT_GENERIC, "vod server" );
2340         vlc_object_attach( p_vlm->p_vod, p_vlm );
2341         p_vlm->p_vod->p_module = module_Need( p_vlm->p_vod, "vod server", 0, 0 );
2342         if( !p_vlm->p_vod->p_module )
2343         {
2344             msg_Err( p_vlm, "cannot find vod server" );
2345             vlc_object_detach( p_vlm->p_vod );
2346             vlc_object_release( p_vlm->p_vod );
2347             p_vlm->p_vod = 0;
2348             return VLC_EGENERIC;
2349         }
2350
2351         p_vlm->p_vod->p_data = p_vlm;
2352         p_vlm->p_vod->pf_media_control = vlm_MediaVodControl;
2353     }
2354
2355     p_media = malloc( sizeof( vlm_media_sys_t ) );
2356     if( !p_media )
2357     {
2358         msg_Err( p_vlm, "out of memory" );
2359         return VLC_ENOMEM;
2360     }
2361     memset( p_media, 0, sizeof(vlm_media_sys_t) );
2362
2363     if( p_cfg->b_vod )
2364         p_vlm->i_vod++;
2365
2366     vlm_media_Copy( &p_media->cfg, p_cfg );
2367     p_media->cfg.id = p_vlm->i_id++;
2368     /* FIXME do we do something here if enabled is true ? */
2369
2370     p_media->vod.p_item = input_ItemNew( p_vlm, NULL, NULL );
2371
2372     p_media->vod.p_media = NULL;
2373     TAB_INIT( p_media->i_instance, p_media->instance );
2374
2375     /* */
2376     TAB_APPEND( p_vlm->i_media, p_vlm->media, p_media );
2377
2378     if( p_id )
2379         *p_id = p_media->cfg.id;
2380
2381     return vlm_OnMediaUpdate( p_vlm, p_media );
2382 }
2383
2384 static int vlm_ControlMediaDel( vlm_t *p_vlm, int64_t id )
2385 {
2386     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2387
2388     if( !p_media )
2389         return VLC_EGENERIC;
2390
2391     while( p_media->i_instance > 0 )
2392         vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, id, p_media->instance[0]->psz_name );
2393
2394     if( p_media->cfg.b_vod )
2395     {
2396         p_media->cfg.b_enabled = false;
2397         vlm_OnMediaUpdate( p_vlm, p_media );
2398         p_vlm->i_vod--;
2399     }
2400
2401     vlm_media_Clean( &p_media->cfg );
2402
2403     vlc_gc_decref( p_media->vod.p_item );
2404
2405     TAB_REMOVE( p_vlm->i_media, p_vlm->media, p_media );
2406
2407     free( p_media );
2408
2409     /* Check if we need to unload the VOD server */
2410     if( p_vlm->p_vod && p_vlm->i_vod <= 0 )
2411     {
2412         module_Unneed( p_vlm->p_vod, p_vlm->p_vod->p_module );
2413         vlc_object_detach( p_vlm->p_vod );
2414         vlc_object_release( p_vlm->p_vod );
2415         p_vlm->p_vod = NULL;
2416     }
2417     return VLC_SUCCESS;
2418 }
2419
2420 static int vlm_ControlMediaGets( vlm_t *p_vlm, vlm_media_t ***ppp_dsc, int *pi_dsc )
2421 {
2422     vlm_media_t **pp_dsc;
2423     int                     i_dsc;
2424     int i;
2425
2426     TAB_INIT( i_dsc, pp_dsc );
2427     for( i = 0; i < p_vlm->i_media; i++ )
2428     {
2429         vlm_media_t *p_dsc = vlm_media_Duplicate( &p_vlm->media[i]->cfg );
2430         TAB_APPEND( i_dsc, pp_dsc, p_dsc );
2431     }
2432
2433     *ppp_dsc = pp_dsc;
2434     *pi_dsc = i_dsc;
2435
2436     return VLC_SUCCESS;
2437 }
2438 static int vlm_ControlMediaClear( vlm_t *p_vlm )
2439 {
2440     while( p_vlm->i_media > 0 )
2441         vlm_ControlMediaDel( p_vlm, p_vlm->media[0]->cfg.id );
2442
2443     return VLC_SUCCESS;
2444 }
2445 static int vlm_ControlMediaGet( vlm_t *p_vlm, int64_t id, vlm_media_t **pp_dsc )
2446 {
2447     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2448     if( !p_media )
2449         return VLC_EGENERIC;
2450
2451     *pp_dsc = vlm_media_Duplicate( &p_media->cfg );
2452     return VLC_SUCCESS;
2453 }
2454 static int vlm_ControlMediaGetId( vlm_t *p_vlm, const char *psz_name, int64_t *p_id )
2455 {
2456     vlm_media_sys_t *p_media = vlm_ControlMediaGetByName( p_vlm, psz_name );
2457     if( !p_media )
2458         return VLC_EGENERIC;
2459
2460     *p_id = p_media->cfg.id;
2461     return VLC_SUCCESS;
2462 }
2463
2464 static vlm_media_instance_sys_t *vlm_ControlMediaInstanceGetByName( vlm_media_sys_t *p_media, const char *psz_id )
2465 {
2466     int i;
2467
2468     for( i = 0; i < p_media->i_instance; i++ )
2469     {
2470         const char *psz = p_media->instance[i]->psz_name;
2471         if( ( psz == NULL && psz_id == NULL ) ||
2472             ( psz && psz_id && !strcmp( psz, psz_id ) ) )
2473             return p_media->instance[i];
2474     }
2475     return NULL;
2476 }
2477 static vlm_media_instance_sys_t *vlm_MediaInstanceNew( vlm_t *p_vlm, const char *psz_name )
2478 {
2479     vlm_media_instance_sys_t *p_instance = malloc( sizeof(vlm_media_instance_sys_t) );
2480     if( !p_instance )
2481         return NULL;
2482
2483     memset( p_instance, 0, sizeof(vlm_media_instance_sys_t) );
2484
2485     p_instance->psz_name = NULL;
2486     if( psz_name )
2487         p_instance->psz_name = strdup( psz_name );
2488
2489     p_instance->p_item = input_ItemNew( p_vlm, NULL, NULL );
2490
2491     p_instance->i_index = 0;
2492     p_instance->b_sout_keep = false;
2493     p_instance->p_input = NULL;
2494     p_instance->p_sout = NULL;
2495
2496     return p_instance;
2497 }
2498 static void vlm_MediaInstanceDelete( vlm_media_instance_sys_t *p_instance )
2499 {
2500     if( p_instance->p_input )
2501     {
2502         input_StopThread( p_instance->p_input );
2503         p_instance->p_sout = input_DetachSout( p_instance->p_input );
2504         vlc_object_release( p_instance->p_input );
2505     }
2506     if( p_instance->p_sout )
2507         sout_DeleteInstance( p_instance->p_sout );
2508
2509     vlc_gc_decref( p_instance->p_item );
2510     free( p_instance->psz_name );
2511     free( p_instance );
2512 }
2513
2514
2515 static int vlm_ControlMediaInstanceStart( vlm_t *p_vlm, int64_t id, const char *psz_id, int i_input_index, const char *psz_vod_output )
2516 {
2517     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2518     vlm_media_instance_sys_t *p_instance;
2519     char *psz_log;
2520
2521     if( !p_media || !p_media->cfg.b_enabled || p_media->cfg.i_input <= 0 )
2522         return VLC_EGENERIC;
2523
2524     /* TODO support multiple input for VOD with sout-keep ? */
2525
2526     if( ( p_media->cfg.b_vod && !psz_vod_output ) || ( !p_media->cfg.b_vod && psz_vod_output ) )
2527         return VLC_EGENERIC;
2528
2529     if( i_input_index < 0 || i_input_index >= p_media->cfg.i_input )
2530         return VLC_EGENERIC;
2531
2532     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
2533     if( !p_instance )
2534     {
2535         vlm_media_t *p_cfg = &p_media->cfg;
2536         const char *psz_keep;
2537         int i;
2538
2539         p_instance = vlm_MediaInstanceNew( p_vlm, psz_id );
2540         if( !p_instance )
2541             return VLC_ENOMEM;
2542
2543         if( p_cfg->psz_output != NULL || psz_vod_output != NULL )
2544         {
2545             char *psz_buffer;
2546             asprintf( &psz_buffer, "sout=%s%s%s",
2547                       p_cfg->psz_output ? p_cfg->psz_output : "",
2548                       (p_cfg->psz_output && psz_vod_output) ? ":" : psz_vod_output ? "#" : "",
2549                       psz_vod_output ? psz_vod_output : "" );
2550             input_ItemAddOption( p_instance->p_item, psz_buffer );
2551             free( psz_buffer );
2552         }
2553
2554         for( i = 0; i < p_cfg->i_option; i++ )
2555         {
2556             if( !strcmp( p_cfg->ppsz_option[i], "sout-keep" ) )
2557                 p_instance->b_sout_keep = true;
2558             else if( !strcmp( p_cfg->ppsz_option[i], "nosout-keep" ) || !strcmp( p_cfg->ppsz_option[i], "no-sout-keep" ) )
2559                 p_instance->b_sout_keep = false;
2560             else
2561                 input_ItemAddOption( p_instance->p_item, p_cfg->ppsz_option[i] );
2562         }
2563         /* We force the right sout-keep value (avoid using the sout-keep from the global configuration)
2564          * FIXME implement input list for VOD (need sout-keep)
2565          * */
2566         if( !p_cfg->b_vod && p_instance->b_sout_keep )
2567             psz_keep = "sout-keep";
2568         else
2569             psz_keep = "no-sout-keep";
2570         input_ItemAddOption( p_instance->p_item, psz_keep );
2571
2572         TAB_APPEND( p_media->i_instance, p_media->instance, p_instance );
2573     }
2574
2575     /* Stop old instance */
2576     if( p_instance->p_input )
2577     {
2578         if( p_instance->i_index == i_input_index &&
2579             !p_instance->p_input->b_eof && !p_instance->p_input->b_error )
2580         {
2581             if( var_GetInteger( p_instance->p_input, "state" ) == PAUSE_S )
2582                 var_SetInteger( p_instance->p_input, "state",  PLAYING_S );
2583             return VLC_SUCCESS;
2584         }
2585
2586         input_StopThread( p_instance->p_input );
2587         p_instance->p_sout = input_DetachSout( p_instance->p_input );
2588         vlc_object_release( p_instance->p_input );
2589         if( !p_instance->b_sout_keep && p_instance->p_sout )
2590         {
2591             sout_DeleteInstance( p_instance->p_sout );
2592             p_instance->p_sout = NULL;
2593         }
2594     }
2595
2596     /* Start new one */
2597     p_instance->i_index = i_input_index;
2598     input_item_SetURI( p_instance->p_item, p_media->cfg.ppsz_input[p_instance->i_index] ) ;
2599
2600     asprintf( &psz_log, _("Media: %s"), p_media->cfg.psz_name );
2601     p_instance->p_input = input_CreateThreadExtended( p_vlm, p_instance->p_item, psz_log, p_instance->p_sout );
2602     if( !p_instance->p_input )
2603     {
2604         TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance );
2605         vlm_MediaInstanceDelete( p_instance );
2606     }
2607     free( psz_log );
2608
2609     return VLC_SUCCESS;
2610 }
2611
2612 static int vlm_ControlMediaInstanceStop( vlm_t *p_vlm, int64_t id, const char *psz_id )
2613 {
2614     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2615     vlm_media_instance_sys_t *p_instance;
2616
2617     if( !p_media )
2618         return VLC_EGENERIC;
2619
2620     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
2621     if( !p_instance )
2622         return VLC_EGENERIC;
2623
2624     TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance );
2625
2626     vlm_MediaInstanceDelete( p_instance );
2627
2628     return VLC_SUCCESS;
2629 }
2630 static int vlm_ControlMediaInstancePause( vlm_t *p_vlm, int64_t id, const char *psz_id )
2631 {
2632     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2633     vlm_media_instance_sys_t *p_instance;
2634     int i_state;
2635
2636     if( !p_media )
2637         return VLC_EGENERIC;
2638
2639     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
2640     if( !p_instance || !p_instance->p_input )
2641         return VLC_EGENERIC;
2642
2643     /* Toggle pause state */
2644     i_state = var_GetInteger( p_instance->p_input, "state" );
2645     if( i_state == PAUSE_S )
2646         var_SetInteger( p_instance->p_input, "state", PLAYING_S );
2647     else if( i_state == PLAYING_S )
2648         var_SetInteger( p_instance->p_input, "state", PAUSE_S );
2649     return VLC_SUCCESS;
2650 }
2651 static int vlm_ControlMediaInstanceGetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t *pi_time, double *pd_position )
2652 {
2653     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2654     vlm_media_instance_sys_t *p_instance;
2655
2656     if( !p_media )
2657         return VLC_EGENERIC;
2658
2659     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
2660     if( !p_instance || !p_instance->p_input )
2661         return VLC_EGENERIC;
2662
2663     if( pi_time )
2664         *pi_time = var_GetTime( p_instance->p_input, "time" );
2665     if( pd_position )
2666         *pd_position = var_GetFloat( p_instance->p_input, "position" );
2667     return VLC_SUCCESS;
2668 }
2669 static int vlm_ControlMediaInstanceSetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t i_time, double d_position )
2670 {
2671     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2672     vlm_media_instance_sys_t *p_instance;
2673
2674     if( !p_media )
2675         return VLC_EGENERIC;
2676
2677     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
2678     if( !p_instance || !p_instance->p_input )
2679         return VLC_EGENERIC;
2680
2681     if( i_time >= 0 )
2682         return var_SetTime( p_instance->p_input, "time", i_time );
2683     else if( d_position >= 0 && d_position <= 100 )
2684         return var_SetFloat( p_instance->p_input, "position", d_position );
2685     return VLC_EGENERIC;
2686 }
2687
2688 static int vlm_ControlMediaInstanceGets( vlm_t *p_vlm, int64_t id, vlm_media_instance_t ***ppp_idsc, int *pi_instance )
2689 {
2690     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2691     vlm_media_instance_t **pp_idsc;
2692     int                              i_idsc;
2693     int i;
2694
2695     if( !p_media )
2696         return VLC_EGENERIC;
2697
2698     TAB_INIT( i_idsc, pp_idsc );
2699     for( i = 0; i < p_media->i_instance; i++ )
2700     {
2701         vlm_media_instance_sys_t *p_instance = p_media->instance[i];
2702         vlm_media_instance_t *p_idsc = vlm_media_instance_New();
2703
2704         if( p_instance->psz_name )
2705             p_idsc->psz_name = strdup( p_instance->psz_name );
2706         if( p_instance->p_input )
2707         {
2708             p_idsc->i_time = var_GetTime( p_instance->p_input, "time" );
2709             p_idsc->i_length = var_GetTime( p_instance->p_input, "length" );
2710             p_idsc->d_position = var_GetFloat( p_instance->p_input, "position" );
2711             if( var_GetInteger( p_instance->p_input, "state" ) == PAUSE_S )
2712                 p_idsc->b_paused = true;
2713             p_idsc->i_rate = var_GetInteger( p_instance->p_input, "rate" );
2714         }
2715
2716         TAB_APPEND( i_idsc, pp_idsc, p_idsc );
2717     }
2718     *ppp_idsc = pp_idsc;
2719     *pi_instance = i_idsc;
2720     return VLC_SUCCESS;
2721 }
2722 static int vlm_ControlMediaInstanceClear( vlm_t *p_vlm, int64_t id )
2723 {
2724     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
2725
2726     if( !p_media )
2727         return VLC_EGENERIC;
2728
2729     while( p_media->i_instance > 0 )
2730         vlm_ControlMediaInstanceStop( p_vlm, id, p_media->instance[0]->psz_name );
2731
2732     return VLC_SUCCESS;
2733 }
2734
2735 static int vlm_ControlScheduleClear( vlm_t *p_vlm )
2736 {
2737     while( p_vlm->i_schedule > 0 )
2738         vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0] );
2739
2740     return VLC_SUCCESS;
2741 }
2742 static int vlm_vaControlInternal( vlm_t *p_vlm, int i_query, va_list args )
2743 {
2744     vlm_media_t *p_dsc;
2745     vlm_media_t **pp_dsc;
2746     vlm_media_t ***ppp_dsc;
2747     vlm_media_instance_t ***ppp_idsc;
2748     const char *psz_id;
2749     const char *psz_vod;
2750     int64_t *p_id;
2751     int64_t id;
2752     int i_int;
2753     int *pi_int;
2754
2755     int64_t *pi_i64;
2756     int64_t i_i64;
2757     double *pd_double;
2758     double d_double;
2759
2760     switch( i_query )
2761     {
2762     /* Media control */
2763     case VLM_GET_MEDIAS:
2764         ppp_dsc = (vlm_media_t ***)va_arg( args, vlm_media_t *** );
2765         pi_int = (int *)va_arg( args, int * );
2766         return vlm_ControlMediaGets( p_vlm, ppp_dsc, pi_int );
2767
2768     case VLM_CLEAR_MEDIAS:
2769         return vlm_ControlMediaClear( p_vlm );
2770
2771     case VLM_CHANGE_MEDIA:
2772         p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * );
2773         return vlm_ControlMediaChange( p_vlm, p_dsc );
2774
2775     case VLM_ADD_MEDIA:
2776         p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * );
2777         p_id = (int64_t*)va_arg( args, int64_t * );
2778         return vlm_ControlMediaAdd( p_vlm, p_dsc, p_id );
2779
2780     case VLM_DEL_MEDIA:
2781         id = (int64_t)va_arg( args, int64_t );
2782         return vlm_ControlMediaDel( p_vlm, id );
2783
2784     case VLM_GET_MEDIA:
2785         id = (int64_t)va_arg( args, int64_t );
2786         pp_dsc = (vlm_media_t **)va_arg( args, vlm_media_t ** );
2787         return vlm_ControlMediaGet( p_vlm, id, pp_dsc );
2788
2789     case VLM_GET_MEDIA_ID:
2790         psz_id = (const char*)va_arg( args, const char * );
2791         p_id = (int64_t*)va_arg( args, int64_t * );
2792         return vlm_ControlMediaGetId( p_vlm, psz_id, p_id );
2793
2794
2795     /* Media instance control */
2796     case VLM_GET_MEDIA_INSTANCES:
2797         id = (int64_t)va_arg( args, int64_t );
2798         ppp_idsc = (vlm_media_instance_t ***)va_arg( args, vlm_media_instance_t *** );
2799         pi_int = (int *)va_arg( args, int *);
2800         return vlm_ControlMediaInstanceGets( p_vlm, id, ppp_idsc, pi_int );
2801
2802     case VLM_CLEAR_MEDIA_INSTANCES:
2803         id = (int64_t)va_arg( args, int64_t );
2804         return vlm_ControlMediaInstanceClear( p_vlm, id );
2805
2806
2807     case VLM_START_MEDIA_BROADCAST_INSTANCE:
2808         id = (int64_t)va_arg( args, int64_t );
2809         psz_id = (const char*)va_arg( args, const char* );
2810         i_int = (int)va_arg( args, int );
2811         return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, NULL );
2812
2813     case VLM_START_MEDIA_VOD_INSTANCE:
2814         id = (int64_t)va_arg( args, int64_t );
2815         psz_id = (const char*)va_arg( args, const char* );
2816         i_int = (int)va_arg( args, int );
2817         psz_vod = (const char*)va_arg( args, const char* );
2818         if( !psz_vod )
2819             return VLC_EGENERIC;
2820         return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, psz_vod );
2821
2822     case VLM_STOP_MEDIA_INSTANCE:
2823         id = (int64_t)va_arg( args, int64_t );
2824         psz_id = (const char*)va_arg( args, const char* );
2825         return vlm_ControlMediaInstanceStop( p_vlm, id, psz_id );
2826
2827     case VLM_PAUSE_MEDIA_INSTANCE:
2828         id = (int64_t)va_arg( args, int64_t );
2829         psz_id = (const char*)va_arg( args, const char* );
2830         return vlm_ControlMediaInstancePause( p_vlm, id, psz_id );
2831
2832     case VLM_GET_MEDIA_INSTANCE_TIME:
2833         id = (int64_t)va_arg( args, int64_t );
2834         psz_id = (const char*)va_arg( args, const char* );
2835         pi_i64 = (int64_t*)va_arg( args, int64_t * );
2836         return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, pi_i64, NULL );
2837     case VLM_GET_MEDIA_INSTANCE_POSITION:
2838         id = (int64_t)va_arg( args, int64_t );
2839         psz_id = (const char*)va_arg( args, const char* );
2840         pd_double = (double*)va_arg( args, double* );
2841         return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, NULL, pd_double );
2842
2843     case VLM_SET_MEDIA_INSTANCE_TIME:
2844         id = (int64_t)va_arg( args, int64_t );
2845         psz_id = (const char*)va_arg( args, const char* );
2846         i_i64 = (int64_t)va_arg( args, int64_t);
2847         return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, i_i64, -1 );
2848     case VLM_SET_MEDIA_INSTANCE_POSITION:
2849         id = (int64_t)va_arg( args, int64_t );
2850         psz_id = (const char*)va_arg( args, const char* );
2851         d_double = (double)va_arg( args, double );
2852         return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, -1, d_double );
2853
2854     case VLM_CLEAR_SCHEDULES:
2855         return vlm_ControlScheduleClear( p_vlm );
2856
2857     default:
2858         msg_Err( p_vlm, "unknown VLM query" );
2859         return VLC_EGENERIC;
2860     }
2861 }
2862 static int vlm_ControlInternal( vlm_t *p_vlm, int i_query, ... )
2863 {
2864     va_list args;
2865     int     i_result;
2866
2867     va_start( args, i_query );
2868     i_result = vlm_vaControlInternal( p_vlm, i_query, args );
2869     va_end( args );
2870
2871     return i_result;
2872 }
2873
2874 int vlm_Control( vlm_t *p_vlm, int i_query, ... )
2875 {
2876     va_list args;
2877     int     i_result;
2878
2879     va_start( args, i_query );
2880
2881     vlc_mutex_lock( &p_vlm->lock );
2882     i_result = vlm_vaControlInternal( p_vlm, i_query, args );
2883     vlc_mutex_unlock( &p_vlm->lock );
2884
2885     va_end( args );
2886
2887     return i_result;
2888 }
2889
2890 #else /* ENABLE_VLM */
2891
2892 /* We just define an empty wrapper */
2893 vlm_t *__vlm_New( vlc_object_t *a )
2894 {
2895     msg_Err( a, "VideoLAN manager support is disabled" );
2896     return NULL;
2897 }
2898
2899 void vlm_Delete( vlm_t *a )
2900 {
2901     (void)a;
2902 }
2903
2904 int vlm_ExecuteCommand( vlm_t *a, const char *b, vlm_message_t **c )
2905 {
2906     abort();
2907 }
2908
2909 vlm_message_t *vlm_MessageNew( const char *psz_name,
2910                                const char *psz_format, ... )
2911 {
2912     (void)psz_name; (void)psz_format;
2913     return NULL;
2914 }
2915
2916 vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message,
2917                                vlm_message_t *p_child )
2918 {
2919     abort();
2920 }
2921
2922 void vlm_MessageDelete( vlm_message_t *a )
2923 {
2924     (void)a;
2925 }
2926
2927 int vlm_Control( vlm_t *p_vlm, int i_query, ... )
2928 {
2929     (void)p_vlm; (void)i_query;
2930     return VLC_EGENERIC;
2931 }
2932
2933 #endif /* ENABLE_VLM */