]> git.sesse.net Git - vlc/blob - src/input/vlm.c
VLM add input events
[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_common.h>
34
35 #include <stdio.h>
36 #include <ctype.h>                                              /* tolower() */
37 #include <assert.h>
38
39 #include <vlc_vlm.h>
40
41 #ifndef WIN32
42 #   include <sys/time.h>                                   /* gettimeofday() */
43 #endif
44
45 #ifdef UNDER_CE
46 #include <sys/time.h>                                      /* gettimeofday() */
47 #endif
48
49 #include <time.h>                                                 /* ctime() */
50 #if defined (WIN32) && !defined (UNDER_CE)
51 #include <sys/timeb.h>                                            /* ftime() */
52 #endif
53
54 #include <vlc_input.h>
55 #include <vlc_stream.h>
56 #include "vlm_internal.h"
57 #include "vlm_event.h"
58 #include <vlc_vod.h>
59 #include <vlc_charset.h>
60 #include <vlc_sout.h>
61 #include "../stream_output/stream_output.h"
62 #include "../libvlc.h"
63
64 /*****************************************************************************
65  * Local prototypes.
66  *****************************************************************************/
67
68 static void vlm_Destructor( vlm_t *p_vlm );
69 static void* Manage( void * );
70 static int vlm_MediaVodControl( void *, vod_media_t *, const char *, int, va_list );
71
72 static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
73                        vlc_value_t oldval, vlc_value_t newval,
74                        void *p_data )
75 {
76     input_thread_t *p_input = (input_thread_t *)p_this;
77     vlm_t *p_vlm = libvlc_priv( p_input->p_libvlc )->p_vlm;
78     vlm_media_sys_t *p_media = p_data;
79     const char *psz_instance_name = NULL;
80
81     if( newval.i_int == INPUT_EVENT_STATE )
82     {
83         for( int i = 0; i < p_media->i_instance; i++ )
84         {
85             if( p_media->instance[i]->p_input == p_input )
86             {
87                 psz_instance_name = p_media->instance[i]->psz_name;
88                 break;
89             }
90         }
91         vlm_SendEventMediaInstanceState( p_vlm, p_media->cfg.id, p_media->cfg.psz_name, psz_instance_name, var_GetInteger( p_input, "state" ) );
92     }
93     return VLC_SUCCESS;
94 }
95
96 /*****************************************************************************
97  * vlm_New:
98  *****************************************************************************/
99 vlm_t *__vlm_New ( vlc_object_t *p_this )
100 {
101     vlc_value_t lockval;
102     vlm_t *p_vlm = NULL, **pp_vlm = &(libvlc_priv (p_this->p_libvlc)->p_vlm);
103     char *psz_vlmconf;
104     static const char vlm_object_name[] = "vlm daemon";
105
106     /* Avoid multiple creation */
107     if( var_Create( p_this->p_libvlc, "vlm_mutex", VLC_VAR_MUTEX ) ||
108         var_Get( p_this->p_libvlc, "vlm_mutex", &lockval ) )
109         return NULL;
110
111     vlc_mutex_lock( lockval.p_address );
112
113     p_vlm = *pp_vlm;
114     if( p_vlm )
115     {   /* VLM already exists */
116         vlc_object_hold( p_vlm );
117         vlc_mutex_unlock( lockval.p_address );
118         return p_vlm;
119     }
120
121     msg_Dbg( p_this, "creating VLM" );
122
123     p_vlm = vlc_custom_create( p_this, sizeof( *p_vlm ), VLC_OBJECT_GENERIC,
124                                vlm_object_name );
125     if( !p_vlm )
126     {
127         vlc_mutex_unlock( lockval.p_address );
128         return NULL;
129     }
130
131     vlc_mutex_init( &p_vlm->lock );
132     p_vlm->i_id = 1;
133     TAB_INIT( p_vlm->i_media, p_vlm->media );
134     TAB_INIT( p_vlm->i_schedule, p_vlm->schedule );
135     p_vlm->i_vod = 0;
136     p_vlm->p_vod = NULL;
137     var_Create( p_vlm, "intf-event", VLC_VAR_ADDRESS );
138     vlc_object_attach( p_vlm, p_this->p_libvlc );
139
140     if( vlc_clone( &p_vlm->thread, Manage, p_vlm, VLC_THREAD_PRIORITY_LOW ) )
141     {
142         vlc_mutex_destroy( &p_vlm->lock );
143         vlc_object_release( p_vlm );
144         return NULL;
145     }
146
147     /* Load our configuration file */
148     psz_vlmconf = var_CreateGetString( p_vlm, "vlm-conf" );
149     if( psz_vlmconf && *psz_vlmconf )
150     {
151         vlm_message_t *p_message = NULL;
152         char *psz_buffer = NULL;
153
154         msg_Dbg( p_this, "loading VLM configuration" );
155         if( asprintf(&psz_buffer, "load %s", psz_vlmconf ) != -1 )
156         {
157             msg_Dbg( p_this, "%s", psz_buffer );
158             if( vlm_ExecuteCommand( p_vlm, psz_buffer, &p_message ) )
159                 msg_Warn( p_this, "error while loading the configuration file" );
160
161             vlm_MessageDelete( p_message );
162             free( psz_buffer );
163         }
164     }
165     free( psz_vlmconf );
166
167     vlc_object_set_destructor( p_vlm, (vlc_destructor_t)vlm_Destructor );
168     *pp_vlm = p_vlm; /* for future reference */
169     vlc_mutex_unlock( lockval.p_address );
170
171     return p_vlm;
172 }
173
174 /*****************************************************************************
175  * vlm_Delete:
176  *****************************************************************************/
177 void vlm_Delete( vlm_t *p_vlm )
178 {
179     vlc_value_t lockval;
180
181     /* vlm_Delete() is serialized against itself, and against vlm_New().
182      * This way, vlm_Destructor () (called from vlc_objet_release() above)
183      * is serialized against setting libvlc_priv->p_vlm from vlm_New(). */
184     var_Get( p_vlm->p_libvlc, "vlm_mutex", &lockval );
185     vlc_mutex_lock( lockval.p_address );
186     vlc_object_release( p_vlm );
187     vlc_mutex_unlock( lockval.p_address );
188 }
189
190 /*****************************************************************************
191  * vlm_Destructor:
192  *****************************************************************************/
193 static void vlm_Destructor( vlm_t *p_vlm )
194 {
195     vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
196     TAB_CLEAN( p_vlm->i_media, p_vlm->media );
197
198     vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
199     TAB_CLEAN( p_vlm->schedule, p_vlm->schedule );
200
201     libvlc_priv(p_vlm->p_libvlc)->p_vlm = NULL;
202     vlc_object_kill( p_vlm );
203     /*vlc_cancel( p_vlm->thread ); */
204     vlc_join( p_vlm->thread, NULL );
205     vlc_mutex_destroy( &p_vlm->lock );
206 }
207
208 /*****************************************************************************
209  * vlm_ExecuteCommand:
210  *****************************************************************************/
211 int vlm_ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
212                         vlm_message_t **pp_message)
213 {
214     int i_result;
215
216     vlc_mutex_lock( &p_vlm->lock );
217     i_result = ExecuteCommand( p_vlm, psz_command, pp_message );
218     vlc_mutex_unlock( &p_vlm->lock );
219
220     return i_result;
221 }
222
223
224 int64_t vlm_Date(void)
225 {
226 #if defined (WIN32) && !defined (UNDER_CE)
227     struct timeb tm;
228     ftime( &tm );
229     return ((int64_t)tm.time) * 1000000 + ((int64_t)tm.millitm) * 1000;
230 #else
231     struct timeval tv_date;
232
233     /* gettimeofday() cannot fail given &tv_date is a valid address */
234     (void)gettimeofday( &tv_date, NULL );
235     return (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec;
236 #endif
237 }
238
239
240 /*****************************************************************************
241  *
242  *****************************************************************************/
243 static int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
244                                 const char *psz_id, int i_query, va_list args )
245 {
246     vlm_t *vlm = (vlm_t *)p_private;
247     int i, i_ret;
248     const char *psz;
249     int64_t id;
250
251     if( !p_private || !p_vod_media )
252         return VLC_EGENERIC;
253
254     vlc_mutex_lock( &vlm->lock );
255
256     /* Find media id */
257     for( i = 0, id = -1; i < vlm->i_media; i++ )
258     {
259         if( p_vod_media == vlm->media[i]->vod.p_media )
260         {
261             id = vlm->media[i]->cfg.id;
262             break;
263         }
264     }
265     if( id == -1 )
266     {
267         vlc_mutex_unlock( &vlm->lock );
268         return VLC_EGENERIC;
269     }
270
271     switch( i_query )
272     {
273     case VOD_MEDIA_PLAY:
274         psz = (const char *)va_arg( args, const char * );
275         i_ret = vlm_ControlInternal( vlm, VLM_START_MEDIA_VOD_INSTANCE, id, psz_id, 0, psz );
276         break;
277
278     case VOD_MEDIA_PAUSE:
279         i_ret = vlm_ControlInternal( vlm, VLM_PAUSE_MEDIA_INSTANCE, id, psz_id );
280         break;
281
282     case VOD_MEDIA_STOP:
283         i_ret = vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, id, psz_id );
284         break;
285
286     case VOD_MEDIA_SEEK:
287     {
288         double d_position = (double)va_arg( args, double );
289         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position/100.0 );
290         break;
291     }
292
293     case VOD_MEDIA_REWIND:
294     {
295         double d_scale = (double)va_arg( args, double );
296         double d_position;
297
298         vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
299         d_position -= (d_scale / 1000.0);
300         if( d_position < 0.0 )
301             d_position = 0.0;
302         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
303         break;
304     }
305
306     case VOD_MEDIA_FORWARD:
307     {
308         double d_scale = (double)va_arg( args, double );
309         double d_position;
310
311         vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
312         d_position += (d_scale / 1000.0);
313         if( d_position > 1.0 )
314             d_position = 1.0;
315         i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
316         break;
317     }
318
319     default:
320         i_ret = VLC_EGENERIC;
321         break;
322     }
323
324     vlc_mutex_unlock( &vlm->lock );
325
326     return i_ret;
327 }
328
329
330 /*****************************************************************************
331  * Manage:
332  *****************************************************************************/
333 static void* Manage( void* p_object )
334 {
335     vlm_t *vlm = (vlm_t*)p_object;
336     int i, j;
337     mtime_t i_lastcheck;
338     mtime_t i_time;
339
340     int canc = vlc_savecancel ();
341     i_lastcheck = vlm_Date();
342
343     while( !vlm->b_die )
344     {
345         char **ppsz_scheduled_commands = NULL;
346         int    i_scheduled_commands = 0;
347
348         vlc_mutex_lock( &vlm->lock );
349
350         /* destroy the inputs that wants to die, and launch the next input */
351         for( i = 0; i < vlm->i_media; i++ )
352         {
353             vlm_media_sys_t *p_media = vlm->media[i];
354
355             for( j = 0; j < p_media->i_instance; )
356             {
357                 vlm_media_instance_sys_t *p_instance = p_media->instance[j];
358
359                 if( p_instance->p_input && ( p_instance->p_input->b_eof || p_instance->p_input->b_error ) )
360                 {
361                     int i_new_input_index;
362
363                     /* */
364                     i_new_input_index = p_instance->i_index + 1;
365                     if( !p_media->cfg.b_vod && p_media->cfg.broadcast.b_loop && i_new_input_index >= p_media->cfg.i_input )
366                         i_new_input_index = 0;
367
368                     /* FIXME implement multiple input with VOD */
369                     if( p_media->cfg.b_vod || i_new_input_index >= p_media->cfg.i_input )
370                         vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, p_instance->psz_name );
371                     else
372                         vlm_ControlInternal( vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, p_instance->psz_name, i_new_input_index );
373
374                     j = 0;
375                 }
376                 else
377                 {
378                     j++;
379                 }
380             }
381         }
382
383         /* scheduling */
384         i_time = vlm_Date();
385
386         for( i = 0; i < vlm->i_schedule; i++ )
387         {
388             mtime_t i_real_date = vlm->schedule[i]->i_date;
389
390             if( vlm->schedule[i]->b_enabled == true )
391             {
392                 if( vlm->schedule[i]->i_date == 0 ) // now !
393                 {
394                     vlm->schedule[i]->i_date = (i_time / 1000000) * 1000000 ;
395                     i_real_date = i_time;
396                 }
397                 else if( vlm->schedule[i]->i_period != 0 )
398                 {
399                     int j = 0;
400                     while( vlm->schedule[i]->i_date + j *
401                            vlm->schedule[i]->i_period <= i_lastcheck &&
402                            ( vlm->schedule[i]->i_repeat > j ||
403                              vlm->schedule[i]->i_repeat == -1 ) )
404                     {
405                         j++;
406                     }
407
408                     i_real_date = vlm->schedule[i]->i_date + j *
409                         vlm->schedule[i]->i_period;
410                 }
411
412                 if( i_real_date <= i_time && i_real_date > i_lastcheck )
413                 {
414                     for( j = 0; j < vlm->schedule[i]->i_command; j++ )
415                     {
416                         TAB_APPEND( i_scheduled_commands,
417                                     ppsz_scheduled_commands,
418                                     strdup(vlm->schedule[i]->command[j] ) );
419                     }
420                 }
421             }
422         }
423         while( i_scheduled_commands )
424         {
425             vlm_message_t *message = NULL;
426             char *psz_command = ppsz_scheduled_commands[0];
427             ExecuteCommand( vlm, psz_command,&message );
428
429             /* for now, drop the message */
430             vlm_MessageDelete( message );
431             TAB_REMOVE( i_scheduled_commands,
432                         ppsz_scheduled_commands,
433                         psz_command );
434             free( psz_command );
435         }
436
437         i_lastcheck = i_time;
438
439         vlc_mutex_unlock( &vlm->lock );
440
441         msleep( 100000 );
442     }
443
444     vlc_restorecancel (canc);
445     return NULL;
446 }
447
448 /* New API
449  */
450 /*
451 typedef struct
452 {
453     struct
454     {
455         int i_connection_count;
456         int i_connection_active;
457     } vod;
458     struct
459     {
460         int        i_count;
461         bool b_playing;
462         int        i_playing_index;
463     } broadcast;
464
465 } vlm_media_status_t;
466 */
467
468 /* */
469 static vlm_media_sys_t *vlm_ControlMediaGetById( vlm_t *p_vlm, int64_t id )
470 {
471     int i;
472
473     for( i = 0; i < p_vlm->i_media; i++ )
474     {
475         if( p_vlm->media[i]->cfg.id == id )
476             return p_vlm->media[i];
477     }
478     return NULL;
479 }
480 static vlm_media_sys_t *vlm_ControlMediaGetByName( vlm_t *p_vlm, const char *psz_name )
481 {
482     int i;
483
484     for( i = 0; i < p_vlm->i_media; i++ )
485     {
486         if( !strcmp( p_vlm->media[i]->cfg.psz_name, psz_name ) )
487             return p_vlm->media[i];
488     }
489     return NULL;
490 }
491 static int vlm_MediaDescriptionCheck( vlm_t *p_vlm, vlm_media_t *p_cfg )
492 {
493     int i;
494
495     if( !p_cfg || !p_cfg->psz_name ||
496         !strcmp( p_cfg->psz_name, "all" ) || !strcmp( p_cfg->psz_name, "media" ) || !strcmp( p_cfg->psz_name, "schedule" ) )
497         return VLC_EGENERIC;
498
499     for( i = 0; i < p_vlm->i_media; i++ )
500     {
501         if( p_vlm->media[i]->cfg.id == p_cfg->id )
502             continue;
503         if( !strcmp( p_vlm->media[i]->cfg.psz_name, p_cfg->psz_name ) )
504             return VLC_EGENERIC;
505     }
506     return VLC_SUCCESS;
507 }
508
509
510 /* Called after a media description is changed/added */
511 static int vlm_OnMediaUpdate( vlm_t *p_vlm, vlm_media_sys_t *p_media )
512 {
513     vlm_media_t *p_cfg = &p_media->cfg;
514     /* Check if we need to create/delete a vod media */
515     if( p_cfg->b_vod )
516     {
517         if( !p_cfg->b_enabled && p_media->vod.p_media )
518         {
519             p_vlm->p_vod->pf_media_del( p_vlm->p_vod, p_media->vod.p_media );
520             p_media->vod.p_media = NULL;
521         }
522         else if( p_cfg->b_enabled && !p_media->vod.p_media && p_cfg->i_input )
523         {
524             /* Pre-parse the input */
525             input_thread_t *p_input;
526             char *psz_output;
527             char *psz_header;
528             char *psz_dup;
529             int i;
530
531             vlc_gc_decref( p_media->vod.p_item );
532             p_media->vod.p_item = input_item_New( p_vlm, p_cfg->ppsz_input[0],
533                 p_cfg->psz_name );
534
535             if( p_cfg->psz_output )
536             {
537                 if( asprintf( &psz_output, "%s:description", p_cfg->psz_output )  == -1 )
538                     psz_output = NULL;
539             }
540             else
541                 psz_output = strdup( "#description" );
542
543             if( psz_output && asprintf( &psz_dup, "sout=%s", psz_output ) != -1 )
544             {
545                 input_item_AddOption( p_media->vod.p_item, psz_dup, VLC_INPUT_OPTION_TRUSTED );
546                 free( psz_dup );
547             }
548             free( psz_output );
549
550             for( i = 0; i < p_cfg->i_option; i++ )
551                 input_item_AddOption( p_media->vod.p_item,
552                                       p_cfg->ppsz_option[i], VLC_INPUT_OPTION_TRUSTED );
553
554             if( asprintf( &psz_header, _("Media: %s"), p_cfg->psz_name ) == -1 )
555                 psz_header = NULL;
556
557             if( (p_input = input_CreateAndStart( p_vlm->p_libvlc, p_media->vod.p_item, psz_header ) ) )
558             {
559                 while( !p_input->b_eof && !p_input->b_error )
560                     msleep( 100000 );
561
562                 input_Stop( p_input, false );
563                 vlc_thread_join( p_input );
564                 vlc_object_release( p_input );
565             }
566             free( psz_header );
567
568             if( p_cfg->vod.psz_mux )
569             {
570                 input_item_t item;
571                 es_format_t es, *p_es = &es;
572                 char fourcc[5];
573
574                 sprintf( fourcc, "%4.4s", p_cfg->vod.psz_mux );
575                 fourcc[0] = tolower(fourcc[0]); fourcc[1] = tolower(fourcc[1]);
576                 fourcc[2] = tolower(fourcc[2]); fourcc[3] = tolower(fourcc[3]);
577
578                 /* XXX: Don't do it that way, but properly use a new input item ref. */
579                 item = *p_media->vod.p_item;
580                 item.i_es = 1;
581                 item.es = &p_es;
582                 es_format_Init( &es, VIDEO_ES, *((int *)fourcc) );
583
584                 p_media->vod.p_media =
585                     p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, &item );
586             }
587             else
588             {
589                 p_media->vod.p_media =
590                     p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, p_media->vod.p_item );
591             }
592         }
593     }
594     else
595     {
596         /* TODO start media if needed */
597     }
598
599     /* TODO add support of var vlm_media_broadcast/vlm_media_vod */
600
601     vlm_SendEventMediaChanged( p_vlm, p_cfg->id, p_cfg->psz_name );
602     return VLC_SUCCESS;
603 }
604 static int vlm_ControlMediaChange( vlm_t *p_vlm, vlm_media_t *p_cfg )
605 {
606     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, p_cfg->id );
607
608     /* */
609     if( !p_media || vlm_MediaDescriptionCheck( p_vlm, p_cfg ) )
610         return VLC_EGENERIC;
611     if( ( p_media->cfg.b_vod && !p_cfg->b_vod ) || ( !p_media->cfg.b_vod && p_cfg->b_vod ) )
612         return VLC_EGENERIC;
613
614     if( 0 )
615     {
616         /* TODO check what are the changes being done (stop instance if needed) */
617     }
618
619     vlm_media_Clean( &p_media->cfg );
620     vlm_media_Copy( &p_media->cfg, p_cfg );
621
622     return vlm_OnMediaUpdate( p_vlm, p_media );
623 }
624
625 static int vlm_ControlMediaAdd( vlm_t *p_vlm, vlm_media_t *p_cfg, int64_t *p_id )
626 {
627     vlm_media_sys_t *p_media;
628
629     if( vlm_MediaDescriptionCheck( p_vlm, p_cfg ) || vlm_ControlMediaGetByName( p_vlm, p_cfg->psz_name ) )
630     {
631         msg_Err( p_vlm, "invalid media description" );
632         return VLC_EGENERIC;
633     }
634     /* Check if we need to load the VOD server */
635     if( p_cfg->b_vod && !p_vlm->i_vod )
636     {
637         p_vlm->p_vod = vlc_custom_create( VLC_OBJECT(p_vlm), sizeof( vod_t ),
638                                           VLC_OBJECT_GENERIC, "vod server" );
639         vlc_object_attach( p_vlm->p_vod, p_vlm->p_libvlc );
640         p_vlm->p_vod->p_module = module_need( p_vlm->p_vod, "vod server", NULL, false );
641         if( !p_vlm->p_vod->p_module )
642         {
643             msg_Err( p_vlm, "cannot find vod server" );
644             vlc_object_detach( p_vlm->p_vod );
645             vlc_object_release( p_vlm->p_vod );
646             p_vlm->p_vod = NULL;
647             return VLC_EGENERIC;
648         }
649
650         p_vlm->p_vod->p_data = p_vlm;
651         p_vlm->p_vod->pf_media_control = vlm_MediaVodControl;
652     }
653
654     p_media = calloc( 1, sizeof( vlm_media_sys_t ) );
655     if( !p_media )
656         return VLC_ENOMEM;
657
658     if( p_cfg->b_vod )
659         p_vlm->i_vod++;
660
661     vlm_media_Copy( &p_media->cfg, p_cfg );
662     p_media->cfg.id = p_vlm->i_id++;
663     /* FIXME do we do something here if enabled is true ? */
664
665     p_media->vod.p_item = input_item_New( p_vlm, NULL, NULL );
666
667     p_media->vod.p_media = NULL;
668     TAB_INIT( p_media->i_instance, p_media->instance );
669
670     /* */
671     TAB_APPEND( p_vlm->i_media, p_vlm->media, p_media );
672
673     if( p_id )
674         *p_id = p_media->cfg.id;
675
676     /* */
677     vlm_SendEventMediaAdded( p_vlm, p_media->cfg.id, p_media->cfg.psz_name );
678     return vlm_OnMediaUpdate( p_vlm, p_media );
679 }
680
681 static int vlm_ControlMediaDel( vlm_t *p_vlm, int64_t id )
682 {
683     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
684
685     if( !p_media )
686         return VLC_EGENERIC;
687
688     while( p_media->i_instance > 0 )
689         vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, id, p_media->instance[0]->psz_name );
690
691     if( p_media->cfg.b_vod )
692     {
693         p_media->cfg.b_enabled = false;
694         vlm_OnMediaUpdate( p_vlm, p_media );
695         p_vlm->i_vod--;
696     }
697
698     /* */
699     vlm_SendEventMediaRemoved( p_vlm, id, p_media->cfg.psz_name );
700
701     vlm_media_Clean( &p_media->cfg );
702
703     vlc_gc_decref( p_media->vod.p_item );
704
705     TAB_REMOVE( p_vlm->i_media, p_vlm->media, p_media );
706
707     free( p_media );
708
709     /* Check if we need to unload the VOD server */
710     if( p_vlm->p_vod && p_vlm->i_vod <= 0 )
711     {
712         module_unneed( p_vlm->p_vod, p_vlm->p_vod->p_module );
713         vlc_object_detach( p_vlm->p_vod );
714         vlc_object_release( p_vlm->p_vod );
715         p_vlm->p_vod = NULL;
716     }
717
718     return VLC_SUCCESS;
719 }
720
721 static int vlm_ControlMediaGets( vlm_t *p_vlm, vlm_media_t ***ppp_dsc, int *pi_dsc )
722 {
723     vlm_media_t **pp_dsc;
724     int                     i_dsc;
725     int i;
726
727     TAB_INIT( i_dsc, pp_dsc );
728     for( i = 0; i < p_vlm->i_media; i++ )
729     {
730         vlm_media_t *p_dsc = vlm_media_Duplicate( &p_vlm->media[i]->cfg );
731         TAB_APPEND( i_dsc, pp_dsc, p_dsc );
732     }
733
734     *ppp_dsc = pp_dsc;
735     *pi_dsc = i_dsc;
736
737     return VLC_SUCCESS;
738 }
739 static int vlm_ControlMediaClear( vlm_t *p_vlm )
740 {
741     while( p_vlm->i_media > 0 )
742         vlm_ControlMediaDel( p_vlm, p_vlm->media[0]->cfg.id );
743
744     return VLC_SUCCESS;
745 }
746 static int vlm_ControlMediaGet( vlm_t *p_vlm, int64_t id, vlm_media_t **pp_dsc )
747 {
748     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
749     if( !p_media )
750         return VLC_EGENERIC;
751
752     *pp_dsc = vlm_media_Duplicate( &p_media->cfg );
753     return VLC_SUCCESS;
754 }
755 static int vlm_ControlMediaGetId( vlm_t *p_vlm, const char *psz_name, int64_t *p_id )
756 {
757     vlm_media_sys_t *p_media = vlm_ControlMediaGetByName( p_vlm, psz_name );
758     if( !p_media )
759         return VLC_EGENERIC;
760
761     *p_id = p_media->cfg.id;
762     return VLC_SUCCESS;
763 }
764
765 static vlm_media_instance_sys_t *vlm_ControlMediaInstanceGetByName( vlm_media_sys_t *p_media, const char *psz_id )
766 {
767     int i;
768
769     for( i = 0; i < p_media->i_instance; i++ )
770     {
771         const char *psz = p_media->instance[i]->psz_name;
772         if( ( psz == NULL && psz_id == NULL ) ||
773             ( psz && psz_id && !strcmp( psz, psz_id ) ) )
774             return p_media->instance[i];
775     }
776     return NULL;
777 }
778 static vlm_media_instance_sys_t *vlm_MediaInstanceNew( vlm_t *p_vlm, const char *psz_name )
779 {
780     vlm_media_instance_sys_t *p_instance = calloc( 1, sizeof(vlm_media_instance_sys_t) );
781     if( !p_instance )
782         return NULL;
783
784     p_instance->psz_name = NULL;
785     if( psz_name )
786         p_instance->psz_name = strdup( psz_name );
787
788     p_instance->p_item = input_item_New( p_vlm, NULL, NULL );
789
790     p_instance->i_index = 0;
791     p_instance->b_sout_keep = false;
792     p_instance->p_input = NULL;
793     p_instance->p_input_resource = NULL;
794
795     return p_instance;
796 }
797 static void vlm_MediaInstanceDelete( vlm_t *p_vlm, int64_t id, vlm_media_instance_sys_t *p_instance, vlm_media_sys_t *p_media )
798 {
799     input_thread_t *p_input = p_instance->p_input;
800     if( p_input )
801     {
802         input_resource_t *p_resource;
803
804         input_Stop( p_input, true );
805         vlc_thread_join( p_input );
806         var_DelCallback( p_instance->p_input, "intf-event", InputEvent, p_media );
807
808         p_resource = input_DetachResource( p_input );
809         input_resource_Delete( p_resource );
810
811         vlc_object_release( p_input );
812
813         vlm_SendEventMediaInstanceStopped( p_vlm, id, p_media->cfg.psz_name );
814     }
815     if( p_instance->p_input_resource )
816         input_resource_Delete( p_instance->p_input_resource );
817
818     vlc_gc_decref( p_instance->p_item );
819     free( p_instance->psz_name );
820     free( p_instance );
821 }
822
823
824 static int vlm_ControlMediaInstanceStart( vlm_t *p_vlm, int64_t id, const char *psz_id, int i_input_index, const char *psz_vod_output )
825 {
826     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
827     vlm_media_instance_sys_t *p_instance;
828     char *psz_log;
829
830     if( !p_media || !p_media->cfg.b_enabled || p_media->cfg.i_input <= 0 )
831         return VLC_EGENERIC;
832
833     /* TODO support multiple input for VOD with sout-keep ? */
834
835     if( ( p_media->cfg.b_vod && !psz_vod_output ) || ( !p_media->cfg.b_vod && psz_vod_output ) )
836         return VLC_EGENERIC;
837
838     if( i_input_index < 0 || i_input_index >= p_media->cfg.i_input )
839         return VLC_EGENERIC;
840
841     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
842     if( !p_instance )
843     {
844         vlm_media_t *p_cfg = &p_media->cfg;
845         int i;
846
847         p_instance = vlm_MediaInstanceNew( p_vlm, psz_id );
848         if( !p_instance )
849             return VLC_ENOMEM;
850
851         if( p_cfg->psz_output != NULL || psz_vod_output != NULL )
852         {
853             char *psz_buffer;
854             if( asprintf( &psz_buffer, "sout=%s%s%s",
855                       p_cfg->psz_output ? p_cfg->psz_output : "",
856                       (p_cfg->psz_output && psz_vod_output) ? ":" : psz_vod_output ? "#" : "",
857                       psz_vod_output ? psz_vod_output : "" ) != -1 )
858             {
859                 input_item_AddOption( p_instance->p_item, psz_buffer, VLC_INPUT_OPTION_TRUSTED );
860                 free( psz_buffer );
861             }
862         }
863
864         for( i = 0; i < p_cfg->i_option; i++ )
865         {
866             if( !strcmp( p_cfg->ppsz_option[i], "sout-keep" ) )
867                 p_instance->b_sout_keep = true;
868             else if( !strcmp( p_cfg->ppsz_option[i], "nosout-keep" ) || !strcmp( p_cfg->ppsz_option[i], "no-sout-keep" ) )
869                 p_instance->b_sout_keep = false;
870             else
871                 input_item_AddOption( p_instance->p_item, p_cfg->ppsz_option[i], VLC_INPUT_OPTION_TRUSTED );
872         }
873         TAB_APPEND( p_media->i_instance, p_media->instance, p_instance );
874     }
875
876     /* Stop old instance */
877     input_thread_t *p_input = p_instance->p_input;
878     if( p_input )
879     {
880         if( p_instance->i_index == i_input_index &&
881             !p_input->b_eof && !p_input->b_error )
882         {
883             if( var_GetInteger( p_input, "state" ) == PAUSE_S )
884                 var_SetInteger( p_input, "state",  PLAYING_S );
885             return VLC_SUCCESS;
886         }
887
888         input_Stop( p_input, !p_input->b_eof && !p_input->b_error );
889         vlc_thread_join( p_input );
890         var_DelCallback( p_instance->p_input, "intf-event", InputEvent, p_media );
891
892         p_instance->p_input_resource = input_DetachResource( p_input );
893
894         vlc_object_release( p_input );
895
896         if( !p_instance->b_sout_keep )
897             input_resource_TerminateSout( p_instance->p_input_resource );
898         input_resource_TerminateVout( p_instance->p_input_resource );
899
900         vlm_SendEventMediaInstanceStopped( p_vlm, id, p_media->cfg.psz_name );
901     }
902
903     /* Start new one */
904     p_instance->i_index = i_input_index;
905     input_item_SetURI( p_instance->p_item, p_media->cfg.ppsz_input[p_instance->i_index] ) ;
906
907     if( asprintf( &psz_log, _("Media: %s"), p_media->cfg.psz_name ) != -1 )
908     {
909         p_instance->p_input = input_Create( p_vlm->p_libvlc, p_instance->p_item,
910                                             psz_log, p_instance->p_input_resource );
911         if( p_instance->p_input )
912         {
913             var_AddCallback( p_instance->p_input, "intf-event", InputEvent, p_media );
914             if( input_Start( p_instance->p_input ) != VLC_SUCCESS )
915             {
916                 var_DelCallback( p_instance->p_input, "intf-event", InputEvent, p_media );
917                 vlc_object_release( p_instance->p_input );
918                 p_instance->p_input = NULL;
919             }
920         }
921         p_instance->p_input_resource = NULL;
922
923         if( !p_instance->p_input )
924         {
925             TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance );
926             vlm_MediaInstanceDelete( p_vlm, id, p_instance, p_media );
927         }
928         else
929         {
930             vlm_SendEventMediaInstanceStarted( p_vlm, id, p_media->cfg.psz_name );
931         }
932         free( psz_log );
933     }
934
935     return VLC_SUCCESS;
936 }
937
938 static int vlm_ControlMediaInstanceStop( vlm_t *p_vlm, int64_t id, const char *psz_id )
939 {
940     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
941     vlm_media_instance_sys_t *p_instance;
942
943     if( !p_media )
944         return VLC_EGENERIC;
945
946     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
947     if( !p_instance )
948         return VLC_EGENERIC;
949
950     TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance );
951
952     vlm_MediaInstanceDelete( p_vlm, id, p_instance, p_media );
953
954     return VLC_SUCCESS;
955 }
956 static int vlm_ControlMediaInstancePause( vlm_t *p_vlm, int64_t id, const char *psz_id )
957 {
958     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
959     vlm_media_instance_sys_t *p_instance;
960     int i_state;
961
962     if( !p_media )
963         return VLC_EGENERIC;
964
965     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
966     if( !p_instance || !p_instance->p_input )
967         return VLC_EGENERIC;
968
969     /* Toggle pause state */
970     i_state = var_GetInteger( p_instance->p_input, "state" );
971     if( i_state == PAUSE_S )
972         var_SetInteger( p_instance->p_input, "state", PLAYING_S );
973     else if( i_state == PLAYING_S )
974         var_SetInteger( p_instance->p_input, "state", PAUSE_S );
975     return VLC_SUCCESS;
976 }
977 static int vlm_ControlMediaInstanceGetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t *pi_time, double *pd_position )
978 {
979     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
980     vlm_media_instance_sys_t *p_instance;
981
982     if( !p_media )
983         return VLC_EGENERIC;
984
985     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
986     if( !p_instance || !p_instance->p_input )
987         return VLC_EGENERIC;
988
989     if( pi_time )
990         *pi_time = var_GetTime( p_instance->p_input, "time" );
991     if( pd_position )
992         *pd_position = var_GetFloat( p_instance->p_input, "position" );
993     return VLC_SUCCESS;
994 }
995 static int vlm_ControlMediaInstanceSetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t i_time, double d_position )
996 {
997     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
998     vlm_media_instance_sys_t *p_instance;
999
1000     if( !p_media )
1001         return VLC_EGENERIC;
1002
1003     p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
1004     if( !p_instance || !p_instance->p_input )
1005         return VLC_EGENERIC;
1006
1007     if( i_time >= 0 )
1008         return var_SetTime( p_instance->p_input, "time", i_time );
1009     else if( d_position >= 0 && d_position <= 100 )
1010         return var_SetFloat( p_instance->p_input, "position", d_position );
1011     return VLC_EGENERIC;
1012 }
1013
1014 static int vlm_ControlMediaInstanceGets( vlm_t *p_vlm, int64_t id, vlm_media_instance_t ***ppp_idsc, int *pi_instance )
1015 {
1016     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
1017     vlm_media_instance_t **pp_idsc;
1018     int                              i_idsc;
1019     int i;
1020
1021     if( !p_media )
1022         return VLC_EGENERIC;
1023
1024     TAB_INIT( i_idsc, pp_idsc );
1025     for( i = 0; i < p_media->i_instance; i++ )
1026     {
1027         vlm_media_instance_sys_t *p_instance = p_media->instance[i];
1028         vlm_media_instance_t *p_idsc = vlm_media_instance_New();
1029
1030         if( p_instance->psz_name )
1031             p_idsc->psz_name = strdup( p_instance->psz_name );
1032         if( p_instance->p_input )
1033         {
1034             p_idsc->i_time = var_GetTime( p_instance->p_input, "time" );
1035             p_idsc->i_length = var_GetTime( p_instance->p_input, "length" );
1036             p_idsc->d_position = var_GetFloat( p_instance->p_input, "position" );
1037             if( var_GetInteger( p_instance->p_input, "state" ) == PAUSE_S )
1038                 p_idsc->b_paused = true;
1039             p_idsc->i_rate = var_GetInteger( p_instance->p_input, "rate" );
1040         }
1041
1042         TAB_APPEND( i_idsc, pp_idsc, p_idsc );
1043     }
1044     *ppp_idsc = pp_idsc;
1045     *pi_instance = i_idsc;
1046     return VLC_SUCCESS;
1047 }
1048
1049 static int vlm_ControlMediaInstanceClear( vlm_t *p_vlm, int64_t id )
1050 {
1051     vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
1052
1053     if( !p_media )
1054         return VLC_EGENERIC;
1055
1056     while( p_media->i_instance > 0 )
1057         vlm_ControlMediaInstanceStop( p_vlm, id, p_media->instance[0]->psz_name );
1058
1059     return VLC_SUCCESS;
1060 }
1061
1062 static int vlm_ControlScheduleClear( vlm_t *p_vlm )
1063 {
1064     while( p_vlm->i_schedule > 0 )
1065         vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0] );
1066
1067     return VLC_SUCCESS;
1068 }
1069
1070 static int vlm_vaControlInternal( vlm_t *p_vlm, int i_query, va_list args )
1071 {
1072     vlm_media_t *p_dsc;
1073     vlm_media_t **pp_dsc;
1074     vlm_media_t ***ppp_dsc;
1075     vlm_media_instance_t ***ppp_idsc;
1076     const char *psz_id;
1077     const char *psz_vod;
1078     int64_t *p_id;
1079     int64_t id;
1080     int i_int;
1081     int *pi_int;
1082
1083     int64_t *pi_i64;
1084     int64_t i_i64;
1085     double *pd_double;
1086     double d_double;
1087
1088     switch( i_query )
1089     {
1090     /* Media control */
1091     case VLM_GET_MEDIAS:
1092         ppp_dsc = (vlm_media_t ***)va_arg( args, vlm_media_t *** );
1093         pi_int = (int *)va_arg( args, int * );
1094         return vlm_ControlMediaGets( p_vlm, ppp_dsc, pi_int );
1095
1096     case VLM_CLEAR_MEDIAS:
1097         return vlm_ControlMediaClear( p_vlm );
1098
1099     case VLM_CHANGE_MEDIA:
1100         p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * );
1101         return vlm_ControlMediaChange( p_vlm, p_dsc );
1102
1103     case VLM_ADD_MEDIA:
1104         p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * );
1105         p_id = (int64_t*)va_arg( args, int64_t * );
1106         return vlm_ControlMediaAdd( p_vlm, p_dsc, p_id );
1107
1108     case VLM_DEL_MEDIA:
1109         id = (int64_t)va_arg( args, int64_t );
1110         return vlm_ControlMediaDel( p_vlm, id );
1111
1112     case VLM_GET_MEDIA:
1113         id = (int64_t)va_arg( args, int64_t );
1114         pp_dsc = (vlm_media_t **)va_arg( args, vlm_media_t ** );
1115         return vlm_ControlMediaGet( p_vlm, id, pp_dsc );
1116
1117     case VLM_GET_MEDIA_ID:
1118         psz_id = (const char*)va_arg( args, const char * );
1119         p_id = (int64_t*)va_arg( args, int64_t * );
1120         return vlm_ControlMediaGetId( p_vlm, psz_id, p_id );
1121
1122
1123     /* Media instance control */
1124     case VLM_GET_MEDIA_INSTANCES:
1125         id = (int64_t)va_arg( args, int64_t );
1126         ppp_idsc = (vlm_media_instance_t ***)va_arg( args, vlm_media_instance_t *** );
1127         pi_int = (int *)va_arg( args, int *);
1128         return vlm_ControlMediaInstanceGets( p_vlm, id, ppp_idsc, pi_int );
1129
1130     case VLM_CLEAR_MEDIA_INSTANCES:
1131         id = (int64_t)va_arg( args, int64_t );
1132         return vlm_ControlMediaInstanceClear( p_vlm, id );
1133
1134
1135     case VLM_START_MEDIA_BROADCAST_INSTANCE:
1136         id = (int64_t)va_arg( args, int64_t );
1137         psz_id = (const char*)va_arg( args, const char* );
1138         i_int = (int)va_arg( args, int );
1139         return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, NULL );
1140
1141     case VLM_START_MEDIA_VOD_INSTANCE:
1142         id = (int64_t)va_arg( args, int64_t );
1143         psz_id = (const char*)va_arg( args, const char* );
1144         i_int = (int)va_arg( args, int );
1145         psz_vod = (const char*)va_arg( args, const char* );
1146         if( !psz_vod )
1147             return VLC_EGENERIC;
1148         return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, psz_vod );
1149
1150     case VLM_STOP_MEDIA_INSTANCE:
1151         id = (int64_t)va_arg( args, int64_t );
1152         psz_id = (const char*)va_arg( args, const char* );
1153         return vlm_ControlMediaInstanceStop( p_vlm, id, psz_id );
1154
1155     case VLM_PAUSE_MEDIA_INSTANCE:
1156         id = (int64_t)va_arg( args, int64_t );
1157         psz_id = (const char*)va_arg( args, const char* );
1158         return vlm_ControlMediaInstancePause( p_vlm, id, psz_id );
1159
1160     case VLM_GET_MEDIA_INSTANCE_TIME:
1161         id = (int64_t)va_arg( args, int64_t );
1162         psz_id = (const char*)va_arg( args, const char* );
1163         pi_i64 = (int64_t*)va_arg( args, int64_t * );
1164         return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, pi_i64, NULL );
1165     case VLM_GET_MEDIA_INSTANCE_POSITION:
1166         id = (int64_t)va_arg( args, int64_t );
1167         psz_id = (const char*)va_arg( args, const char* );
1168         pd_double = (double*)va_arg( args, double* );
1169         return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, NULL, pd_double );
1170
1171     case VLM_SET_MEDIA_INSTANCE_TIME:
1172         id = (int64_t)va_arg( args, int64_t );
1173         psz_id = (const char*)va_arg( args, const char* );
1174         i_i64 = (int64_t)va_arg( args, int64_t);
1175         return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, i_i64, -1 );
1176     case VLM_SET_MEDIA_INSTANCE_POSITION:
1177         id = (int64_t)va_arg( args, int64_t );
1178         psz_id = (const char*)va_arg( args, const char* );
1179         d_double = (double)va_arg( args, double );
1180         return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, -1, d_double );
1181
1182     case VLM_CLEAR_SCHEDULES:
1183         return vlm_ControlScheduleClear( p_vlm );
1184
1185     default:
1186         msg_Err( p_vlm, "unknown VLM query" );
1187         return VLC_EGENERIC;
1188     }
1189 }
1190
1191 int vlm_ControlInternal( vlm_t *p_vlm, int i_query, ... )
1192 {
1193     va_list args;
1194     int     i_result;
1195
1196     va_start( args, i_query );
1197     i_result = vlm_vaControlInternal( p_vlm, i_query, args );
1198     va_end( args );
1199
1200     return i_result;
1201 }
1202
1203 int vlm_Control( vlm_t *p_vlm, int i_query, ... )
1204 {
1205     va_list args;
1206     int     i_result;
1207
1208     va_start( args, i_query );
1209
1210     vlc_mutex_lock( &p_vlm->lock );
1211     i_result = vlm_vaControlInternal( p_vlm, i_query, args );
1212     vlc_mutex_unlock( &p_vlm->lock );
1213
1214     va_end( args );
1215
1216     return i_result;
1217 }
1218