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