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