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