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