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