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