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