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