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