]> git.sesse.net Git - vlc/blob - src/input/input.c
5061e33cf248919203c3dfd110a271a97b9e4d3b
[vlc] / src / input / input.c
1 /*****************************************************************************
2  * input.c: input thread
3  * Read a stream, demultiplex and parse it before sending it to
4  * decoders.
5  *****************************************************************************
6  * Copyright (C) 1998-2004 VideoLAN
7  * $Id$
8  *
9  * Authors: Christophe Massiot <massiot@via.ecp.fr>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>
30
31 #include <vlc/vlc.h>
32 #include <vlc/input.h>
33 #include <vlc/decoder.h>
34 #include <vlc/vout.h>
35
36 #ifdef HAVE_SYS_TIMES_H
37 #   include <sys/times.h>
38 #endif
39
40 #include "stream_output.h"
41
42 #include "vlc_interface.h"
43 #include "codecs.h"
44 #include "vlc_meta.h"
45 #include "../../modules/demux/util/sub.h"
46
47 /*****************************************************************************
48  * Local prototypes
49  *****************************************************************************/
50 struct input_thread_sys_t
51 {
52     /* subtitles */
53     int              i_sub;
54     subtitle_demux_t **sub;
55
56     int64_t          i_stop_time;
57 };
58
59 static  int RunThread  ( input_thread_t *p_input );
60 static  int InitThread ( input_thread_t *p_input );
61 static void ErrorThread( input_thread_t *p_input );
62 static void EndThread  ( input_thread_t *p_input );
63
64 static void ParseOption( input_thread_t *p_input, const char *psz_option );
65
66 static void DecodeUrl  ( char * );
67
68 /*****************************************************************************
69  * Callbacks
70  *****************************************************************************/
71 static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd,
72                              vlc_value_t oldval, vlc_value_t newval, void *p_data );
73 static int TimeCallback    ( vlc_object_t *p_this, char const *psz_cmd,
74                              vlc_value_t oldval, vlc_value_t newval, void *p_data );
75 static int StateCallback   ( vlc_object_t *p_this, char const *psz_cmd,
76                              vlc_value_t oldval, vlc_value_t newval, void *p_data );
77 static int RateCallback    ( vlc_object_t *p_this, char const *psz_cmd,
78                              vlc_value_t oldval, vlc_value_t newval, void *p_data );
79 static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd,
80                              vlc_value_t oldval, vlc_value_t newval, void *p_data );
81
82 /*****************************************************************************
83  * input_CreateThread: creates a new input thread
84  *****************************************************************************
85  * This function creates a new input, and returns a pointer
86  * to its description. On error, it returns NULL.
87  *****************************************************************************/
88 input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
89                                       input_item_t *p_item )
90
91 {
92     input_thread_t *p_input;                        /* thread descriptor */
93     vlc_value_t     val;
94     int             i;
95
96     /* Allocate descriptor */
97     p_input = vlc_object_create( p_parent, VLC_OBJECT_INPUT );
98     if( p_input == NULL )
99     {
100         msg_Err( p_parent, "out of memory" );
101         return NULL;
102     }
103
104     /* Store pointer to input item descriptor */
105     p_input->p_item = p_item;
106
107     /* Parse input options */
108     vlc_mutex_lock( &p_item->lock );
109     for( i = 0; i < p_item->i_options; i++ )
110     {
111         msg_Dbg( p_input, "option: %s", p_item->ppsz_options[i] );
112         ParseOption( p_input, p_item->ppsz_options[i] );
113     }
114     vlc_mutex_unlock( &p_item->lock );
115
116     /* Create a few object variables we'll need later on */
117     var_Create( p_input, "video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
118     var_Create( p_input, "audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
119     var_Create( p_input, "audio-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
120     var_Create( p_input, "spu-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
121     var_Create( p_input, "sub-file", VLC_VAR_FILE | VLC_VAR_DOINHERIT );
122     var_Create( p_input, "sub-autodetect-file", VLC_VAR_BOOL |
123                 VLC_VAR_DOINHERIT );
124     var_Create( p_input, "sub-autodetect-path", VLC_VAR_STRING |
125                 VLC_VAR_DOINHERIT );
126     var_Create( p_input, "sub-autodetect-fuzzy", VLC_VAR_INTEGER |
127                 VLC_VAR_DOINHERIT );
128
129     var_Create( p_input, "sout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
130     var_Create( p_input, "sout-all",   VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
131     var_Create( p_input, "sout-audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
132     var_Create( p_input, "sout-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
133     var_Create( p_input, "sout-keep",  VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
134
135     /* repeat variable */
136     var_Create( p_input, "input-repeat", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
137
138     /* start/stop time */
139     var_Create( p_input, "start-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
140     var_Create( p_input, "stop-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
141
142     /* decoders */
143     var_Create( p_input, "minimize-threads", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
144
145     /* play status */
146
147     /* position variable */
148     var_Create( p_input, "position",  VLC_VAR_FLOAT );  /* position 0.0->1.0 */
149     var_Create( p_input, "position-offset",  VLC_VAR_FLOAT );  /* relative */
150     val.f_float = 0.0;
151     var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
152     var_AddCallback( p_input, "position", PositionCallback, NULL );
153     var_AddCallback( p_input, "position-offset", PositionCallback, NULL );
154
155     /* time variable */
156     var_Create( p_input, "time",  VLC_VAR_TIME );
157     var_Create( p_input, "time-offset",  VLC_VAR_TIME );    /* relative */
158     val.i_time = 0;
159     var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
160     var_AddCallback( p_input, "time", TimeCallback, NULL );
161     var_AddCallback( p_input, "time-offset", TimeCallback, NULL );
162
163     /* length variable */
164     var_Create( p_input, "length",  VLC_VAR_TIME );
165     val.i_time = 0;
166     var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
167
168     /* rate variable */
169     var_Create( p_input, "rate", VLC_VAR_INTEGER );
170     var_Create( p_input, "rate-slower", VLC_VAR_VOID );
171     var_Create( p_input, "rate-faster", VLC_VAR_VOID );
172     val.i_int = DEFAULT_RATE;
173     var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL );
174     var_AddCallback( p_input, "rate", RateCallback, NULL );
175     var_AddCallback( p_input, "rate-slower", RateCallback, NULL );
176     var_AddCallback( p_input, "rate-faster", RateCallback, NULL );
177
178     /* state variable */
179     var_Create( p_input, "state", VLC_VAR_INTEGER );
180     val.i_int = INIT_S;
181     var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
182     var_AddCallback( p_input, "state", StateCallback, NULL );
183
184     /* state variable */
185     var_Create( p_input, "demuxed-id3", VLC_VAR_BOOL );
186     val.b_bool = VLC_FALSE;
187     var_Change( p_input, "demuxed-id3", VLC_VAR_SETVALUE, &val, NULL );
188
189     /* Initialize thread properties */
190     p_input->b_eof      = 0;
191     p_input->b_out_pace_control = VLC_FALSE;
192     p_input->p_sys      = NULL;
193
194     /* Set target */
195     vlc_mutex_lock( &p_item->lock );
196     p_input->psz_source = strdup( p_item->psz_uri );
197     vlc_mutex_unlock( &p_item->lock );
198
199     /* Stream */
200     p_input->s = NULL;
201
202     /* es out */
203     p_input->p_es_out = NULL;
204
205     /* Demux */
206     p_input->p_demux   = NULL;
207     p_input->pf_demux  = NULL;
208     p_input->pf_rewind = NULL;
209     p_input->pf_demux_control = demux_vaControlDefault;
210     p_input->i_cr_average = config_GetInt( p_input, "cr-average" );
211
212     /* Access */
213     p_input->p_access = NULL;
214
215     p_input->i_bufsize = 0;
216     p_input->i_mtu = 0;
217     p_input->i_pts_delay = DEFAULT_PTS_DELAY;
218
219     /* Initialize statistics */
220     p_input->c_loops                    = 0;
221     p_input->stream.c_packets_read      = 0;
222     p_input->stream.c_packets_trashed   = 0;
223
224     /* Set locks. */
225     vlc_mutex_init( p_input, &p_input->stream.stream_lock );
226     vlc_cond_init( p_input, &p_input->stream.stream_wait );
227     vlc_mutex_init( p_input, &p_input->stream.control.control_lock );
228
229     /* Initialize stream description */
230     p_input->stream.b_changed = 0;
231     p_input->stream.i_es_number = 0;
232     p_input->stream.i_selected_es_number = 0;
233     p_input->stream.i_pgrm_number = 0;
234     p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
235     p_input->stream.b_new_mute = MUTE_NO_CHANGE;
236     p_input->stream.i_mux_rate = 0;
237     p_input->stream.b_seekable = 0;
238     p_input->stream.p_sout = NULL;
239
240     /* no stream, no program, no area, no es */
241     p_input->stream.p_new_program = NULL;
242
243     p_input->stream.i_area_nb = 0;
244     p_input->stream.pp_areas = NULL;
245     p_input->stream.p_selected_area = NULL;
246     p_input->stream.p_new_area = NULL;
247
248     p_input->stream.pp_selected_es = NULL;
249     p_input->stream.p_removed_es = NULL;
250     p_input->stream.p_newly_selected_es = NULL;
251
252     /* By default there is one area in a stream */
253     input_AddArea( p_input, 0, 1 );
254     p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
255
256     /* Initialize stream control properties. */
257     p_input->stream.control.i_status = INIT_S;
258     p_input->stream.control.i_rate = DEFAULT_RATE;
259     p_input->stream.control.b_mute = 0;
260     p_input->stream.control.b_grayscale = config_GetInt( p_input, "grayscale");
261
262     msg_Info( p_input, "playlist item `%s'", p_input->psz_source );
263
264     /* Bookmarks */
265     var_Create( p_input, "bookmarks", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
266     var_Create( p_input, "bookmark", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE |
267                 VLC_VAR_ISCOMMAND );
268     val.psz_string = _("Bookmark");
269     var_Change( p_input, "bookmark", VLC_VAR_SETTEXT, &val, NULL );
270     var_AddCallback( p_input, "bookmark", BookmarkCallback, NULL );
271
272     p_input->i_bookmarks = 0;
273     p_input->pp_bookmarks = NULL;
274
275     var_Get( p_input, "bookmarks", &val );
276     if( val.psz_string )
277     {
278         /* FIXME: have a common cfg parsing routine used by sout and others */
279         char *psz_parser, *psz_start, *psz_end;
280         psz_parser = val.psz_string;
281         while( (psz_start = strchr( psz_parser, '{' ) ) )
282         {
283             seekpoint_t seekpoint;
284             char backup;
285             psz_start++;
286             psz_end = strchr( psz_start, '}' );
287             if( !psz_end ) break;
288             psz_parser = psz_end + 1;
289             backup = *psz_parser;
290             *psz_parser = 0;
291             *psz_end = ',';
292
293             seekpoint.psz_name = 0;
294             seekpoint.i_byte_offset = 0;
295             seekpoint.i_time_offset = 0;
296             while( (psz_end = strchr( psz_start, ',' ) ) )
297             {
298                 *psz_end = 0;
299                 if( !strncmp( psz_start, "name=", 5 ) )
300                 {
301                     seekpoint.psz_name = psz_start + 5;
302                 }
303                 else if( !strncmp( psz_start, "bytes=", 6 ) )
304                 {
305                     seekpoint.i_byte_offset = atoll(psz_start + 6);
306                 }
307                 else if( !strncmp( psz_start, "time=", 5 ) )
308                 {
309                     seekpoint.i_time_offset = atoll(psz_start + 5) * 1000000;
310                 }
311                 psz_start = psz_end + 1;
312             }
313             msg_Dbg( p_input, "adding bookmark: %s, bytes="I64Fd", time="I64Fd,
314                      seekpoint.psz_name, seekpoint.i_byte_offset,
315                      seekpoint.i_time_offset );
316             input_Control( p_input, INPUT_ADD_BOOKMARK, &seekpoint );
317             *psz_parser = backup;
318         }
319         free( val.psz_string );
320     }
321
322     vlc_object_attach( p_input, p_parent );
323
324     /* Create thread and wait for its readiness. */
325     if( vlc_thread_create( p_input, "input", RunThread,
326                            VLC_THREAD_PRIORITY_INPUT, VLC_TRUE ) )
327     {
328         msg_Err( p_input, "cannot create input thread" );
329         free( p_input );
330         return NULL;
331     }
332
333     return p_input;
334 }
335
336 /*****************************************************************************
337  * input_StopThread: mark an input thread as zombie
338  *****************************************************************************
339  * This function should not return until the thread is effectively cancelled.
340  *****************************************************************************/
341 void input_StopThread( input_thread_t *p_input )
342 {
343     demux_t *p_demux;
344
345     /* Make the thread exit from a possible vlc_cond_wait() */
346     vlc_mutex_lock( &p_input->stream.stream_lock );
347
348     /* Request thread destruction */
349
350     /* Temporary demux2 hack */
351     p_demux = (demux_t *)vlc_object_find( p_input, VLC_OBJECT_DEMUX,
352                                           FIND_CHILD );
353     if( p_demux )
354     {
355         p_demux->b_die = 1;
356         vlc_object_release( p_demux );
357     }
358
359     p_input->b_die = 1;
360
361     vlc_cond_signal( &p_input->stream.stream_wait );
362     vlc_mutex_unlock( &p_input->stream.stream_lock );
363 }
364
365 /*****************************************************************************
366  * input_DestroyThread: mark an input thread as zombie
367  *****************************************************************************
368  * This function should not return until the thread is effectively cancelled.
369  *****************************************************************************/
370 void input_DestroyThread( input_thread_t *p_input )
371 {
372     /* Join the thread */
373     vlc_thread_join( p_input );
374
375     /* Destroy Mutex locks */
376     vlc_mutex_destroy( &p_input->stream.control.control_lock );
377     vlc_cond_destroy( &p_input->stream.stream_wait );
378     vlc_mutex_destroy( &p_input->stream.stream_lock );
379 }
380
381 /*****************************************************************************
382  * RunThread: main thread loop
383  *****************************************************************************
384  * Thread in charge of processing the network packets and demultiplexing.
385  *****************************************************************************/
386 static int RunThread( input_thread_t *p_input )
387 {
388     vlc_value_t  val;
389     mtime_t      i_update_next = -1;
390
391     /* Signal right now, otherwise we'll get stuck in a peek */
392     vlc_thread_ready( p_input );
393
394     if( InitThread( p_input ) )
395     {
396         /* If we failed, wait before we are killed, and exit */
397         p_input->b_error = 1;
398
399         ErrorThread( p_input );
400
401         /* Tell we're dead */
402         p_input->b_dead = 1;
403
404         return 0;
405     }
406
407     /* initialization is complete */
408     vlc_mutex_lock( &p_input->stream.stream_lock );
409     p_input->stream.b_changed        = 1;
410     p_input->stream.control.i_status = PLAYING_S;
411     vlc_mutex_unlock( &p_input->stream.stream_lock );
412
413     val.i_int = PLAYING_S;
414     var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
415
416     while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
417     {
418         unsigned int i, i_count;
419
420         p_input->c_loops++;
421
422         vlc_mutex_lock( &p_input->stream.stream_lock );
423
424         if( p_input->stream.p_new_program )
425         {
426             if( p_input->pf_set_program != NULL )
427             {
428
429                 /* Reinitialize buffer manager. */
430                 input_AccessReinit( p_input );
431
432                 p_input->pf_set_program( p_input,
433                                          p_input->stream.p_new_program );
434
435                 /* Escape all decoders for the stream discontinuity they
436                  * will encounter. */
437                 input_EscapeDiscontinuity( p_input );
438
439                 for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
440                 {
441                     pgrm_descriptor_t * p_pgrm
442                                             = p_input->stream.pp_programs[i];
443
444                     /* Reinitialize synchro. */
445                     p_pgrm->i_synchro_state = SYNCHRO_REINIT;
446                 }
447             }
448             p_input->stream.p_new_program = NULL;
449         }
450
451         if( p_input->stream.p_new_area )
452         {
453             if( p_input->stream.b_seekable && p_input->pf_set_area != NULL )
454             {
455                 input_AccessReinit( p_input );
456
457                 p_input->pf_set_area( p_input, p_input->stream.p_new_area );
458
459                 /* Escape all decoders for the stream discontinuity they
460                  * will encounter. */
461                 input_EscapeDiscontinuity( p_input );
462
463                 for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
464                 {
465                     pgrm_descriptor_t * p_pgrm
466                                             = p_input->stream.pp_programs[i];
467
468                     /* Reinitialize synchro. */
469                     p_pgrm->i_synchro_state = SYNCHRO_REINIT;
470                 }
471             }
472             p_input->stream.p_new_area = NULL;
473         }
474
475         if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
476         {
477             if( p_input->stream.p_selected_area->i_size > 0 )
478             {
479                 unsigned int i;
480                 mtime_t      i_time;
481                 double f = (double)p_input->stream.p_selected_area->i_seek /
482                            (double)p_input->stream.p_selected_area->i_size;
483
484                 vlc_mutex_unlock( &p_input->stream.stream_lock );
485                 demux_Control( p_input, DEMUX_SET_POSITION, f );
486                 vlc_mutex_lock( &p_input->stream.stream_lock );
487
488                 /* Escape all decoders for the stream discontinuity they
489                  * will encounter. */
490                 input_EscapeDiscontinuity( p_input );
491
492                 for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
493                 {
494                     pgrm_descriptor_t * p_pgrm=p_input->stream.pp_programs[i];
495
496                     /* Reinitialize synchro. */
497                     p_pgrm->i_synchro_state = SYNCHRO_REINIT;
498                 }
499
500                 vlc_mutex_unlock( &p_input->stream.stream_lock );
501                 if( !demux_Control( p_input, DEMUX_GET_TIME, &i_time ) )
502                 {
503                     int i;
504                     vlc_value_t val;
505
506                     /* Help in bar display */
507                     val.i_time = i_time;
508                     var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
509
510                     /* Seek subs */
511                     for( i = 0; i < p_input->p_sys->i_sub; i++ )
512                     {
513                         subtitle_Seek( p_input->p_sys->sub[i], i_time );
514                     }
515                 }
516                 if( !demux_Control( p_input, DEMUX_GET_POSITION, &f ) )
517                 {
518                     val.f_float = (float)f;
519                     var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
520                 }
521                 vlc_mutex_lock( &p_input->stream.stream_lock );
522             }
523             p_input->stream.p_selected_area->i_seek = NO_SEEK;
524         }
525
526         if( p_input->stream.p_removed_es )
527         {
528             input_UnselectES( p_input, p_input->stream.p_removed_es );
529             p_input->stream.p_removed_es = NULL;
530         }
531
532         if( p_input->stream.p_newly_selected_es )
533         {
534             input_SelectES( p_input, p_input->stream.p_newly_selected_es );
535             p_input->stream.p_newly_selected_es = NULL;
536         }
537
538         if( p_input->stream.b_new_mute != MUTE_NO_CHANGE )
539         {
540             if( p_input->stream.b_new_mute )
541             {
542                 input_EscapeAudioDiscontinuity( p_input );
543             }
544
545             vlc_mutex_lock( &p_input->stream.control.control_lock );
546             p_input->stream.control.b_mute = p_input->stream.b_new_mute;
547             vlc_mutex_unlock( &p_input->stream.control.control_lock );
548
549             p_input->stream.b_new_mute = MUTE_NO_CHANGE;
550         }
551
552         vlc_mutex_unlock( &p_input->stream.stream_lock );
553
554         /* Read and demultiplex some data. */
555         i_count = p_input->pf_demux( p_input );
556
557         if( i_count == 0 )
558         {
559             vlc_value_t repeat;
560
561             var_Get( p_input, "input-repeat", &repeat );
562             if( repeat.i_int == 0 || p_input->stream.i_area_nb <= 0 )
563             {
564                 /* End of file - we do not set b_die because only the
565                  * playlist is allowed to do so. */
566                 msg_Info( p_input, "EOF reached" );
567                 p_input->b_eof = 1;
568             }
569             else
570             {
571                 msg_Dbg( p_input, "repeating the same input (%d)", repeat.i_int );
572                 if( repeat.i_int > 0 )
573                 {
574                     repeat.i_int--;
575                     var_Set( p_input, "input-repeat", repeat );
576                 }
577
578                 p_input->stream.p_new_area = p_input->stream.pp_areas[0];
579                 p_input->stream.p_new_area->i_seek = 0;
580             }
581         }
582         else if( i_count < 0 )
583         {
584             p_input->b_error = 1;
585         }
586
587         if( !p_input->b_error && !p_input->b_eof && i_update_next < mdate() )
588         {
589             int i;
590             mtime_t i_time;
591             mtime_t i_length;
592             double  d_pos;
593
594             /* update input status variables */
595             if( !demux_Control( p_input, DEMUX_GET_POSITION, &d_pos ) )
596             {
597                 val.f_float = (float)d_pos;
598                 var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
599             }
600             if( !demux_Control( p_input, DEMUX_GET_TIME, &i_time ) )
601             {
602                 val.i_time = i_time;
603                 var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
604             }
605             if( !demux_Control( p_input, DEMUX_GET_LENGTH, &i_length ) )
606             {
607                 val.i_time = i_length;
608                 var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
609             }
610
611             /* Check stop-time */
612             if( p_input->p_sys->i_stop_time > 0 && p_input->p_sys->i_stop_time < i_time )
613             {
614                 msg_Warn( p_input, "EOF reached because of stop-time" );
615                 p_input->b_eof = 1;
616             }
617
618             /* update subs */
619             for( i = 0; i < p_input->p_sys->i_sub; i++ )
620             {
621                 subtitle_Demux( p_input->p_sys->sub[i], i_time );
622             }
623
624             i_update_next = mdate() + I64C(150000);
625         }
626     }
627
628     if( p_input->b_error || p_input->b_eof )
629     {
630         ErrorThread( p_input );
631     }
632
633     EndThread( p_input );
634
635     return 0;
636 }
637
638 /*****************************************************************************
639  * InitThread: init the input Thread
640  *****************************************************************************/
641 static int InitThread( input_thread_t * p_input )
642 {
643     vlc_meta_t *p_meta = NULL, *p_meta_user = NULL;
644 //    float f_fps;
645     double f_fps;
646     mtime_t i_length;
647
648     /* Parse source string. Syntax : [[<access>][/<demux>]:][<source>] */
649     char * psz_parser = p_input->psz_dupsource = strdup(p_input->psz_source);
650     vlc_value_t val, val1;
651     int64_t i_microsecondperframe;
652
653     subtitle_demux_t *p_sub_toselect = NULL;
654     char             *psz_sub_file = NULL;
655
656     /* Skip the plug-in names */
657     while( *psz_parser && *psz_parser != ':' )
658     {
659         psz_parser++;
660     }
661 #if defined( WIN32 ) || defined( UNDER_CE )
662     if( psz_parser - p_input->psz_dupsource == 1 )
663     {
664         msg_Warn( p_input, "drive letter %c: found in source string",
665                            p_input->psz_dupsource[0] ) ;
666         psz_parser = "";
667     }
668 #endif
669
670     if( !*psz_parser )
671     {
672         p_input->psz_access = p_input->psz_demux = "";
673         p_input->psz_name = p_input->psz_source;
674         free( p_input->psz_dupsource );
675         p_input->psz_dupsource = NULL;
676     }
677     else
678     {
679         *psz_parser++ = '\0';
680
681         /* let's skip '//' */
682         if( psz_parser[0] == '/' && psz_parser[1] == '/' )
683         {
684             psz_parser += 2 ;
685         }
686
687         p_input->psz_name = psz_parser ;
688
689         /* Come back to parse the access and demux plug-ins */
690         psz_parser = p_input->psz_dupsource;
691
692         if( !*psz_parser )
693         {
694             /* No access */
695             p_input->psz_access = "";
696         }
697         else if( *psz_parser == '/' )
698         {
699             /* No access */
700             p_input->psz_access = "";
701             psz_parser++;
702         }
703         else
704         {
705             p_input->psz_access = psz_parser;
706
707             while( *psz_parser && *psz_parser != '/' )
708             {
709                 psz_parser++;
710             }
711
712             if( *psz_parser == '/' )
713             {
714                 *psz_parser++ = '\0';
715             }
716         }
717
718         if( !*psz_parser )
719         {
720             /* No demux */
721             p_input->psz_demux = "";
722         }
723         else
724         {
725             p_input->psz_demux = psz_parser;
726         }
727     }
728
729     msg_Dbg( p_input, "access `%s', demux `%s', name `%s'",
730              p_input->psz_access, p_input->psz_demux, p_input->psz_name );
731
732     if( input_AccessInit( p_input ) == -1 )
733     {
734         free( p_input->psz_source );
735         if( p_input->psz_dupsource != NULL )
736         {
737             free( p_input->psz_dupsource );
738         }
739
740         return VLC_EGENERIC;
741     }
742
743     /* Initialize optional stream output. (before demuxer)*/
744     var_Get( p_input, "sout", &val );
745     if( val.psz_string != NULL )
746     {
747         if ( *val.psz_string && (p_input->stream.p_sout =
748              sout_NewInstance( p_input, val.psz_string )) == NULL )
749         {
750             msg_Err( p_input, "cannot start stream output instance, aborting" );
751             free( val.psz_string );
752
753             input_AccessEnd( p_input );
754             free( p_input->psz_source );
755             if( p_input->psz_dupsource != NULL )
756             {
757                 free( p_input->psz_dupsource );
758             }
759             return VLC_EGENERIC;
760         }
761         free( val.psz_string );
762     }
763
764     p_input->p_es_out = input_EsOutNew( p_input );
765     es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_FALSE );
766     es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
767
768     /* Find and open appropriate access module */
769     p_input->p_access = module_Need( p_input, "access",
770                                      p_input->psz_access, VLC_TRUE );
771
772     /* Maybe we had an encoded url */
773     if( !p_input->p_access && strchr( p_input->psz_name, '%' ) )
774     {
775         DecodeUrl( p_input->psz_name );
776
777         msg_Dbg( p_input, "retying with %s", p_input->psz_name );
778         p_input->p_access = module_Need( p_input, "access",
779                                          p_input->psz_access, VLC_TRUE );
780     }
781 #ifndef WIN32      /* Remove this gross hack from the win32 build as colons
782                     * are forbidden in filenames on Win32. */
783
784     /* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
785     if ( p_input->p_access == NULL
786           && (*p_input->psz_demux || *p_input->psz_access) )
787     {
788         p_input->psz_access = p_input->psz_demux = "";
789         p_input->psz_name = p_input->psz_source;
790         free( p_input->psz_dupsource);
791         p_input->psz_dupsource = NULL;
792
793         p_input->p_access = module_Need( p_input, "access",
794                                          p_input->psz_access, VLC_TRUE );
795     }
796 #endif
797     if( p_input->p_access == NULL )
798     {
799         msg_Err( p_input, "no suitable access module for `%s/%s://%s'",
800                  p_input->psz_access, p_input->psz_demux, p_input->psz_name );
801         if ( p_input->stream.p_sout != NULL )
802         {
803             sout_DeleteInstance( p_input->stream.p_sout );
804         }
805
806         input_AccessEnd( p_input );
807         free( p_input->psz_source );
808         if( p_input->psz_dupsource != NULL )
809         {
810             free( p_input->psz_dupsource );
811         }
812         input_EsOutDelete( p_input->p_es_out );
813         return VLC_EGENERIC;
814     }
815
816     /* Waiting for stream. */
817     if( p_input->i_mtu )
818     {
819         p_input->i_bufsize = p_input->i_mtu;
820     }
821     else
822     {
823         p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
824     }
825
826     /* If the desynchronisation requested by the user is < 0, we need to
827      * cache more data. */
828     var_Create( p_input, "audio-desync", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
829     var_Get( p_input, "audio-desync", &val );
830     if( val.i_int < 0 )
831         p_input->i_pts_delay -= (val.i_int * 1000);
832
833     if( p_input->p_current_data == NULL && p_input->pf_read != NULL )
834     {
835         while( !input_FillBuffer( p_input ) )
836         {
837             if( p_input->b_die || p_input->b_error || p_input->b_eof )
838             {
839                 module_Unneed( p_input, p_input->p_access );
840                 if ( p_input->stream.p_sout != NULL )
841                 {
842                     sout_DeleteInstance( p_input->stream.p_sout );
843                 }
844                 input_AccessEnd( p_input );
845                 free( p_input->psz_source );
846                 if( p_input->psz_dupsource != NULL )
847                 {
848                     free( p_input->psz_dupsource );
849                 }
850                 input_EsOutDelete( p_input->p_es_out );
851                 return VLC_EGENERIC;
852             }
853         }
854     }
855
856     /* Create the stream_t facilities */
857     p_input->s = input_StreamNew( p_input );
858     if( p_input->s == NULL )
859     {
860         /* should never occur yet */
861
862         msg_Err( p_input, "cannot create stream_t" );
863
864         module_Unneed( p_input, p_input->p_access );
865         if ( p_input->stream.p_sout != NULL )
866         {
867             sout_DeleteInstance( p_input->stream.p_sout );
868         }
869         input_AccessEnd( p_input );
870         free( p_input->psz_source );
871         if( p_input->psz_dupsource != NULL )
872         {
873             free( p_input->psz_dupsource );
874         }
875         input_EsOutDelete( p_input->p_es_out );
876         return VLC_EGENERIC;
877     }
878
879     /* Find and open appropriate demux module */
880     p_input->p_demux =
881         module_Need( p_input, "demux",
882                      (p_input->psz_demux && *p_input->psz_demux) ?
883                      p_input->psz_demux : "$demux",
884                      (p_input->psz_demux && *p_input->psz_demux) ?
885                      VLC_TRUE : VLC_FALSE );
886
887     if( p_input->p_demux == NULL )
888     {
889         msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
890                  p_input->psz_access, p_input->psz_demux, p_input->psz_name );
891
892         input_StreamDelete( p_input->s );
893         module_Unneed( p_input, p_input->p_access );
894         if ( p_input->stream.p_sout != NULL )
895         {
896             sout_DeleteInstance( p_input->stream.p_sout );
897         }
898         input_AccessEnd( p_input );
899         free( p_input->psz_source );
900         if( p_input->psz_dupsource != NULL )
901         {
902             free( p_input->psz_dupsource );
903         }
904         input_EsOutDelete( p_input->p_es_out );
905         return VLC_EGENERIC;
906     }
907
908     /* Init input_thread_sys_t */
909     p_input->p_sys = malloc( sizeof( input_thread_sys_t ) );
910     p_input->p_sys->i_sub = 0;
911     p_input->p_sys->sub   = NULL;
912
913     p_input->p_sys->i_stop_time = 0;
914
915     /* Get meta information from user */
916     var_Create( p_input, "meta-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
917     var_Create( p_input, "meta-author", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
918     var_Create( p_input, "meta-artist", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
919     var_Create( p_input, "meta-genre", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
920     var_Create( p_input, "meta-copyright", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
921     var_Create( p_input, "meta-description", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
922     var_Create( p_input, "meta-date", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
923     var_Create( p_input, "meta-url", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
924     if( (p_meta_user = vlc_meta_New()) )
925     {
926         vlc_value_t val;
927
928         var_Get( p_input, "meta-title", &val );
929         if( val.psz_string && *val.psz_string )
930             vlc_meta_Add( p_meta_user, VLC_META_TITLE, val.psz_string );
931         if( val.psz_string ) free( val.psz_string );
932         var_Get( p_input, "meta-author", &val );
933         if( val.psz_string && *val.psz_string )
934             vlc_meta_Add( p_meta_user, VLC_META_AUTHOR, val.psz_string );
935         if( val.psz_string ) free( val.psz_string );
936         var_Get( p_input, "meta-artist", &val );
937         if( val.psz_string && *val.psz_string )
938             vlc_meta_Add( p_meta_user, VLC_META_ARTIST, val.psz_string );
939         if( val.psz_string ) free( val.psz_string );
940         var_Get( p_input, "meta-genre", &val );
941         if( val.psz_string && *val.psz_string )
942             vlc_meta_Add( p_meta_user, VLC_META_GENRE, val.psz_string );
943         if( val.psz_string ) free( val.psz_string );
944         var_Get( p_input, "meta-copyright", &val );
945         if( val.psz_string && *val.psz_string )
946             vlc_meta_Add( p_meta_user, VLC_META_COPYRIGHT, val.psz_string );
947         if( val.psz_string ) free( val.psz_string );
948         var_Get( p_input, "meta-description", &val );
949         if( val.psz_string && *val.psz_string )
950             vlc_meta_Add( p_meta_user, VLC_META_DESCRIPTION, val.psz_string );
951         if( val.psz_string ) free( val.psz_string );
952         var_Get( p_input, "meta-date", &val );
953         if( val.psz_string && *val.psz_string )
954             vlc_meta_Add( p_meta_user, VLC_META_DATE, val.psz_string );
955         if( val.psz_string ) free( val.psz_string );
956         var_Get( p_input, "meta-url", &val );
957         if( val.psz_string && *val.psz_string )
958             vlc_meta_Add( p_meta_user, VLC_META_URL, val.psz_string );
959         if( val.psz_string ) free( val.psz_string );
960     }
961
962     /* Get meta informations from demuxer */
963     if( !demux_Control( p_input, DEMUX_GET_META, &p_meta ) ||
964         ( p_meta_user && p_meta_user->i_meta ) )
965     {
966         int i;
967
968         /* Merge demux and user metadata */
969         if( !p_meta ){ p_meta = p_meta_user; p_meta_user = NULL; }
970         else if( p_meta && p_meta_user ) vlc_meta_Merge( p_meta, p_meta_user );
971
972         msg_Dbg( p_input, "meta informations:" );
973         if( p_meta->i_meta > 0 )
974         {
975             for( i = 0; i < p_meta->i_meta; i++ )
976             {
977                 msg_Dbg( p_input, "  - '%s' = '%s'", _(p_meta->name[i]),
978                          p_meta->value[i] );
979                 if( !strcmp( p_meta->name[i], VLC_META_TITLE ) &&
980                     p_meta->value[i] )
981                     input_Control( p_input, INPUT_SET_NAME, p_meta->value[i] );
982
983                 if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
984                     input_Control( p_input, INPUT_ADD_INFO, _("General"),
985                                    _("Author"), p_meta->value[i] );
986
987                 input_Control( p_input, INPUT_ADD_INFO, _("File"),
988                               _(p_meta->name[i]), "%s", p_meta->value[i] );
989             }
990         }
991         for( i = 0; i < p_meta->i_track; i++ )
992         {
993             vlc_meta_t *tk = p_meta->track[i];
994             int j;
995
996             msg_Dbg( p_input, "  - track[%d]:", i );
997             if( tk->i_meta > 0 )
998             {
999                 char *psz_cat = malloc( strlen(_("Stream")) + 10 );
1000                 sprintf( psz_cat, "%s %d", _("Stream"), i );
1001
1002                 for( j = 0; j < tk->i_meta; j++ )
1003                 {
1004                     msg_Dbg( p_input, "     - '%s' = '%s'", _(tk->name[j]),
1005                              tk->value[j] );
1006
1007                     input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1008                                    _(tk->name[j]), "%s", tk->value[j] );
1009                 }
1010             }
1011         }
1012
1013         if( p_input->stream.p_sout && p_input->stream.p_sout->p_meta == NULL )
1014         {
1015             p_input->stream.p_sout->p_meta = p_meta;
1016         }
1017         else
1018         {
1019             vlc_meta_Delete( p_meta );
1020         }
1021     }
1022     if( p_meta_user ) vlc_meta_Delete( p_meta_user );
1023
1024     /* Get length */
1025     if( !demux_Control( p_input, DEMUX_GET_LENGTH, &i_length ) &&
1026         i_length > 0 )
1027     {
1028         char psz_buffer[MSTRTIME_MAX_SIZE];
1029
1030         vlc_mutex_lock( &p_input->p_item->lock );
1031         p_input->p_item->i_duration = i_length;
1032         vlc_mutex_unlock( &p_input->p_item->lock );
1033
1034         input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"),
1035                        msecstotimestr( psz_buffer, i_length / 1000 ) );
1036
1037         /* Set start time */
1038         var_Get( p_input, "start-time", &val );
1039         if(  val.i_int > 0 )
1040         {
1041             double f_pos = val.i_int * I64C(1000000) / (double)i_length;
1042
1043             if( f_pos >= 1.0 )
1044             {
1045                 msg_Warn( p_input, "invalid start-time, ignored (start-time "
1046                           ">= media length)" );
1047             }
1048             else
1049             {
1050                 p_input->stream.p_selected_area->i_seek =
1051                     (int64_t)( f_pos * (double)p_input->stream.p_selected_area->i_size );
1052
1053                 msg_Dbg( p_input, "start-time %ds (%2.2f)", val.i_int, f_pos );
1054             }
1055         }
1056     }
1057
1058     /* Set stop-time and check validity */
1059     var_Get( p_input, "stop-time", &val );
1060     if( val.i_int > 0 )
1061     {
1062         vlc_value_t start;
1063
1064         var_Get( p_input, "start-time", &start );
1065         if( start.i_int >= val.i_int )
1066         {
1067             msg_Warn( p_input, "invalid stop-time, ignored (stop-time < "
1068                       "start-time)" );
1069         }
1070         else
1071         {
1072             p_input->p_sys->i_stop_time = (int64_t)val.i_int * I64C(1000000);
1073             msg_Dbg( p_input, "stop-time %ds", val.i_int );
1074         }
1075     }
1076
1077     /* Get fps */
1078     if( demux_Control( p_input, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
1079     {
1080         i_microsecondperframe = 0;
1081     }
1082     else
1083     {
1084         i_microsecondperframe = (int64_t)( (double)1000000.0 / (double)f_fps );
1085     }
1086
1087     /* Look for and add subtitle files */
1088     var_Get( p_input, "sub-file", &val );
1089     if( val.psz_string && *val.psz_string )
1090     {
1091         subtitle_demux_t *p_sub;
1092
1093         msg_Dbg( p_input, "force subtitle: %s", val.psz_string );
1094         if( ( p_sub = subtitle_New( p_input, strdup(val.psz_string),
1095                                     i_microsecondperframe ) ) )
1096         {
1097             p_sub_toselect = p_sub;
1098             TAB_APPEND( p_input->p_sys->i_sub, p_input->p_sys->sub, p_sub );
1099         }
1100     }
1101     psz_sub_file = val.psz_string;
1102
1103     var_Get( p_input, "sub-autodetect-file", &val );
1104     var_Get( p_input, "sub-autodetect-path", &val1 );
1105     if( val.b_bool )
1106     {
1107         subtitle_demux_t *p_sub;
1108         int i;
1109         char **tmp = subtitles_Detect( p_input, val1.psz_string, p_input->psz_name );
1110         char **tmp2 = tmp;
1111         for( i = 0; *tmp2 != NULL; i++ )
1112         {
1113             if( psz_sub_file == NULL || strcmp( psz_sub_file, *tmp2 ) )
1114             {
1115                 if( ( p_sub = subtitle_New( p_input, *tmp2,
1116                                             i_microsecondperframe ) ) )
1117                 {
1118                     TAB_APPEND( p_input->p_sys->i_sub, p_input->p_sys->sub,
1119                                 p_sub );
1120                 }
1121             }
1122             free( *tmp2++ );
1123         }
1124         free( tmp );
1125         free( val1.psz_string );
1126     }
1127     if( psz_sub_file ) free( psz_sub_file );
1128
1129     es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE );
1130     val.b_bool =  VLC_FALSE;
1131     if( p_input->stream.p_sout )
1132     {
1133         var_Get( p_input, "sout-all", &val );
1134     }
1135     es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE,
1136                     val.b_bool ? ES_OUT_MODE_ALL : ES_OUT_MODE_AUTO );
1137     if( p_sub_toselect )
1138     {
1139         es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
1140                         p_sub_toselect->p_es, VLC_TRUE );
1141     }
1142
1143     if( p_input->stream.p_sout )
1144     {
1145         if( p_input->stream.p_sout->i_out_pace_nocontrol > 0 )
1146         {
1147             p_input->b_out_pace_control = VLC_FALSE;
1148         }
1149         else
1150         {
1151             p_input->b_out_pace_control = VLC_TRUE;
1152         }
1153         msg_Dbg( p_input, "starting in %s mode",
1154                  p_input->b_out_pace_control ? "asynch" : "synch" );
1155     }
1156
1157     return VLC_SUCCESS;
1158 }
1159
1160 /*****************************************************************************
1161  * ErrorThread: RunThread() error loop
1162  *****************************************************************************
1163  * This function is called when an error occured during thread main's loop.
1164  *****************************************************************************/
1165 static void ErrorThread( input_thread_t *p_input )
1166 {
1167     while( !p_input->b_die )
1168     {
1169         /* Sleep a while */
1170         msleep( INPUT_IDLE_SLEEP );
1171     }
1172 }
1173
1174 /*****************************************************************************
1175  * EndThread: end the input thread
1176  *****************************************************************************/
1177 static void EndThread( input_thread_t * p_input )
1178 {
1179     int i;
1180 #ifdef HAVE_SYS_TIMES_H
1181     /* Display statistics */
1182     struct tms  cpu_usage;
1183     times( &cpu_usage );
1184
1185     msg_Dbg( p_input, "%ld loops consuming user: %ld, system: %ld",
1186              p_input->c_loops, cpu_usage.tms_utime, cpu_usage.tms_stime );
1187 #else
1188     msg_Dbg( p_input, "%ld loops", p_input->c_loops );
1189 #endif
1190
1191     input_DumpStream( p_input );
1192
1193     /* Free demultiplexer's data */
1194     if( p_input->p_demux ) module_Unneed( p_input, p_input->p_demux );
1195
1196     /* Free all ES and destroy all decoder threads */
1197     input_EndStream( p_input );
1198
1199     /* Close optional stream output instance */
1200     if( p_input->stream.p_sout )
1201     {
1202         vlc_object_t *p_pl =
1203             vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1204         vlc_value_t keep;
1205
1206         if( var_Get( p_input, "sout-keep", &keep ) >= 0 && keep.b_bool && p_pl )
1207         {
1208             /* attach sout to the playlist */
1209             msg_Warn( p_input, "keeping sout" );
1210             vlc_object_detach( p_input->stream.p_sout );
1211             vlc_object_attach( p_input->stream.p_sout, p_pl );
1212         }
1213         else
1214         {
1215             msg_Warn( p_input, "destroying sout" );
1216             sout_DeleteInstance( p_input->stream.p_sout );
1217         }
1218         if( p_pl )
1219         {
1220             vlc_object_release( p_pl );
1221         }
1222     }
1223
1224     /* Destroy subtitles demuxers */
1225     if( p_input->p_sys )
1226     {
1227         for( i = 0; i < p_input->p_sys->i_sub; i++ )
1228         {
1229             subtitle_Close( p_input->p_sys->sub[i] );
1230         }
1231         if( p_input->p_sys->i_sub > 0 )
1232         {
1233             free( p_input->p_sys->sub );
1234         }
1235
1236         /* Free input_thread_sys_t */
1237         free( p_input->p_sys );
1238     }
1239
1240     /* Destroy the stream_t facilities */
1241     if( p_input->s ) input_StreamDelete( p_input->s );
1242
1243     /* Destroy es out */
1244     if( p_input->p_es_out ) input_EsOutDelete( p_input->p_es_out );
1245
1246     /* Close the access plug-in */
1247     if( p_input->p_access ) module_Unneed( p_input, p_input->p_access );
1248
1249     input_AccessEnd( p_input );
1250
1251     /* Free info structures XXX destroy es before 'cause vorbis */
1252     msg_Dbg( p_input, "freeing info structures...");
1253
1254     free( p_input->psz_source );
1255     if( p_input->psz_dupsource != NULL ) free( p_input->psz_dupsource );
1256
1257     /* Tell we're dead */
1258     p_input->b_dead = 1;
1259 }
1260 /*****************************************************************************
1261  * DecodeUrl: decode a given encoded url
1262  *****************************************************************************/
1263 static void DecodeUrl( char *psz )
1264 {
1265     char *dup = strdup( psz );
1266     char *p = dup;
1267
1268     while( *p )
1269     {
1270         if( *p == '%' )
1271         {
1272             char val[3];
1273             p++;
1274             if( !*p )
1275             {
1276                 break;
1277             }
1278
1279             val[0] = *p++;
1280             val[1] = *p++;
1281             val[2] = '\0';
1282
1283             *psz++ = strtol( val, NULL, 16 );
1284         }
1285         else if( *p == '+' )
1286         {
1287             *psz++ = ' ';
1288             p++;
1289         }
1290         else
1291         {
1292             *psz++ = *p++;
1293         }
1294     }
1295     *psz++  ='\0';
1296     free( dup );
1297 }
1298
1299 /*****************************************************************************
1300  * ParseOption: parses the options for the input
1301  *****************************************************************************
1302  * This function parses the input (config) options and creates their associated
1303  * object variables.
1304  * Options are of the form "[no[-]]foo[=bar]" where foo is the option name and
1305  * bar is the value of the option.
1306  *****************************************************************************/
1307 static void ParseOption( input_thread_t *p_input, const char *psz_option )
1308 {
1309     char *psz_name = (char *)psz_option;
1310     char *psz_value = strchr( psz_option, '=' );
1311     int  i_name_len, i_type;
1312     vlc_bool_t b_isno = VLC_FALSE;
1313     vlc_value_t val;
1314
1315     if( psz_value ) i_name_len = psz_value - psz_option;
1316     else i_name_len = strlen( psz_option );
1317
1318     /* It's too much of an hassle to remove the ':' when we parse
1319      * the cmd line :) */
1320     if( i_name_len && *psz_name == ':' )
1321     {
1322         psz_name++;
1323         i_name_len--;
1324     }
1325
1326     if( i_name_len == 0 ) return;
1327
1328     psz_name = strndup( psz_name, i_name_len );
1329     if( psz_value ) psz_value++;
1330
1331     i_type = config_GetType( p_input, psz_name );
1332
1333     if( !i_type && !psz_value )
1334     {
1335         /* check for "no-foo" or "nofoo" */
1336         if( !strncmp( psz_name, "no-", 3 ) )
1337         {
1338             memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 );
1339         }
1340         else if( !strncmp( psz_name, "no", 2 ) )
1341         {
1342             memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 );
1343         }
1344         else goto cleanup;           /* Option doesn't exist */
1345
1346         b_isno = VLC_TRUE;
1347         i_type = config_GetType( p_input, psz_name );
1348
1349         if( !i_type ) goto cleanup;  /* Option doesn't exist */
1350     }
1351     else if( !i_type ) goto cleanup; /* Option doesn't exist */
1352
1353     if( ( i_type != VLC_VAR_BOOL ) &&
1354         ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */
1355
1356     /* Create the variable in the input object.
1357      * Children of the input object will be able to retreive this value
1358      * thanks to the inheritance property of the object variables. */
1359     var_Create( p_input, psz_name, i_type );
1360
1361     switch( i_type )
1362     {
1363     case VLC_VAR_BOOL:
1364         val.b_bool = !b_isno;
1365         break;
1366
1367     case VLC_VAR_INTEGER:
1368         val.i_int = atoi( psz_value );
1369         break;
1370
1371     case VLC_VAR_FLOAT:
1372         val.f_float = atof( psz_value );
1373         break;
1374
1375     case VLC_VAR_STRING:
1376     case VLC_VAR_MODULE:
1377     case VLC_VAR_FILE:
1378     case VLC_VAR_DIRECTORY:
1379         val.psz_string = psz_value;
1380         break;
1381
1382     default:
1383         goto cleanup;
1384         break;
1385     }
1386
1387     var_Set( p_input, psz_name, val );
1388
1389     msg_Dbg( p_input, "set input option: %s to %s", psz_name, psz_value );
1390
1391   cleanup:
1392     if( psz_name ) free( psz_name );
1393     return;
1394 }
1395
1396 /*****************************************************************************
1397  * Callbacks  (position, time, state, rate )
1398  *****************************************************************************/
1399 static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd,
1400                              vlc_value_t oldval, vlc_value_t newval,
1401                              void *p_data )
1402 {
1403     input_thread_t *p_input = (input_thread_t *)p_this;
1404
1405     msg_Dbg( p_input, "cmd=%s old=%f new=%f", psz_cmd,
1406              oldval.f_float, newval.f_float );
1407
1408     if( !strcmp( psz_cmd, "position-offset" ) )
1409     {
1410         vlc_value_t val;
1411         var_Get( p_input, "position", &val );
1412
1413         newval.f_float += val.f_float;
1414     }
1415
1416     vlc_mutex_lock( &p_input->stream.stream_lock );
1417     p_input->stream.p_selected_area->i_seek =
1418         (int64_t)( newval.f_float *
1419                    (double)p_input->stream.p_selected_area->i_size );
1420
1421     if( p_input->stream.p_selected_area->i_seek < 0 )
1422     {
1423         p_input->stream.p_selected_area->i_seek = 0;
1424     }
1425     vlc_mutex_unlock( &p_input->stream.stream_lock );
1426
1427     return VLC_SUCCESS;
1428 }
1429
1430 static int TimeCallback( vlc_object_t *p_this, char const *psz_cmd,
1431                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1432 {
1433     input_thread_t *p_input = (input_thread_t *)p_this;
1434     vlc_value_t     val;
1435
1436     /* FIXME TODO FIXME */
1437     msg_Dbg( p_input, "cmd=%s old=%lld new=%lld", psz_cmd,
1438              oldval.i_time, newval.i_time );
1439
1440     var_Get( p_input, "length", &val );
1441     if( val.i_time > 0 )
1442     {
1443         val.f_float = (double)newval.i_time / (double)val.i_time;
1444         if( !strcmp( psz_cmd, "time-offset" ) )
1445         {
1446             var_Set( p_input, "position-offset", val );
1447         }
1448         else
1449         {
1450             var_Set( p_input, "position", val );
1451         }
1452     }
1453     else
1454     {
1455         msg_Warn( p_input, "TimeCallback: length <= 0 -> can't seek" );
1456     }
1457     return VLC_SUCCESS;
1458 }
1459
1460 static int StateCallback( vlc_object_t *p_this, char const *psz_cmd,
1461                           vlc_value_t oldval, vlc_value_t newval,
1462                           void *p_data )
1463 {
1464     input_thread_t *p_input = (input_thread_t *)p_this;
1465
1466     msg_Dbg( p_input, "cmd=%s old=%d new=%d",
1467              psz_cmd, oldval.i_int, newval.i_int );
1468
1469     switch( newval.i_int )
1470     {
1471         case PLAYING_S:
1472             input_SetStatus( p_input, INPUT_STATUS_PLAY );
1473             return VLC_SUCCESS;
1474         case PAUSE_S:
1475             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
1476             return VLC_SUCCESS;
1477         case END_S:
1478             input_SetStatus( p_input, INPUT_STATUS_END );
1479             return VLC_SUCCESS;
1480         default:
1481             msg_Err( p_input, "cannot set new state (invalid)" );
1482             return VLC_EGENERIC;
1483     }
1484 }
1485
1486 static int RateCallback( vlc_object_t *p_this, char const *psz_cmd,
1487                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1488 {
1489     input_thread_t *p_input = (input_thread_t *)p_this;
1490
1491     if( !strcmp( psz_cmd, "rate-slower" ) )
1492     {
1493         input_SetStatus( p_input, INPUT_STATUS_SLOWER );
1494     }
1495     else if( !strcmp( psz_cmd, "rate-faster" ) )
1496     {
1497         input_SetStatus( p_input, INPUT_STATUS_FASTER );
1498     }
1499     else
1500     {
1501         msg_Dbg( p_input, "cmd=%s old=%d new=%d",
1502                  psz_cmd, oldval.i_int, newval.i_int );
1503         input_SetRate( p_input, newval.i_int );
1504     }
1505     return VLC_SUCCESS;
1506 }
1507
1508 static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd,
1509                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1510 {
1511     input_thread_t *p_input = (input_thread_t *)p_this;
1512     return input_Control( p_input, INPUT_SET_BOOKMARK, newval );
1513 }