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