]> git.sesse.net Git - vlc/blob - src/input/input.c
* src/input/*, include/vlc_input.h: the MRL is now parsed for titles/chapters directl...
[vlc] / src / input / input.c
1 /*****************************************************************************
2  * input.c: input thread
3  *****************************************************************************
4  * Copyright (C) 1998-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32 #include <vlc/decoder.h>
33 #include <vlc/vout.h>
34
35 #include "input_internal.h"
36
37 #include "stream_output.h"
38
39 #include "vlc_interface.h"
40 #include "vlc_meta.h"
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static  int Run  ( input_thread_t *p_input );
46
47 static  int Init ( input_thread_t *p_input );
48 static void Error( input_thread_t *p_input );
49 static void End  ( input_thread_t *p_input );
50
51 static inline int ControlPopNoLock( input_thread_t *, int *, vlc_value_t * );
52 static void       ControlReduce( input_thread_t * );
53 static vlc_bool_t Control( input_thread_t *, int, vlc_value_t );
54
55
56 static int  UpdateFromAccess( input_thread_t * );
57 static int  UpdateFromDemux( input_thread_t * );
58
59 static void UpdateItemLength( input_thread_t *, int64_t i_length );
60
61 static void ParseOption( input_thread_t *p_input, const char *psz_option );
62
63 static void DecodeUrl  ( char * );
64 static void MRLSplit( input_thread_t *, char *, char **, char **, char ** );
65 static void MRLSections( input_thread_t *, char *, int *, int *, int *, int *);
66
67 static input_source_t *InputSourceNew( input_thread_t *);
68 static int  InputSourceInit( input_thread_t *, input_source_t *,
69                              char *, char *psz_forced_demux );
70 static void InputSourceClean( input_thread_t *, input_source_t * );
71
72 static void SlaveDemux( input_thread_t *p_input );
73 static void SlaveSeek( input_thread_t *p_input );
74
75 static vlc_meta_t *InputMetaUser( input_thread_t *p_input );
76
77 /*****************************************************************************
78  * input_CreateThread: creates a new input thread
79  *****************************************************************************
80  * This function creates a new input, and returns a pointer
81  * to its description. On error, it returns NULL.
82  *
83  * Variables for _public_ use:
84  * * Get and Set:
85  *  - state
86  *  - rate,rate-slower, rate-faster
87  *  - position, position-offset
88  *  - time, time-offset
89  *  - title,title-next,title-prev
90  *  - chapter,chapter-next, chapter-prev
91  *  - program, audio-es, video-es, spu-es
92  *  - audio-delay, spu-delay
93  *  - bookmark
94  * * Get only:
95  *  - length
96  *  - bookmarks
97  *  - seekable (if you can seek, it doesn't say if 'bar display' has be shown or not, for that check position != 0.0)
98  * * For intf callback upon changes
99  *  - intf-change
100  * TODO explain when Callback is called
101  * TODO complete this list (?)
102  *****************************************************************************/
103 input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
104                                       input_item_t *p_item )
105
106 {
107     input_thread_t *p_input;                        /* thread descriptor */
108     vlc_value_t val;
109     int i;
110
111     /* Allocate descriptor */
112     p_input = vlc_object_create( p_parent, VLC_OBJECT_INPUT );
113     if( p_input == NULL )
114     {
115         msg_Err( p_parent, "out of memory" );
116         return NULL;
117     }
118
119     /* Init Common fields */
120     p_input->b_eof = VLC_FALSE;
121     p_input->b_can_pace_control = VLC_TRUE;
122     p_input->i_start = 0;
123     p_input->i_time  = 0;
124     p_input->i_stop  = 0;
125     p_input->i_title = 0;
126     p_input->title   = NULL;
127     p_input->i_title_offset = p_input->i_seekpoint_offset = 0;
128     p_input->i_state = INIT_S;
129     p_input->i_rate  = INPUT_RATE_DEFAULT;
130     p_input->i_bookmark = 0;
131     p_input->bookmark = NULL;
132     p_input->p_es_out = NULL;
133     p_input->p_sout  = NULL;
134     p_input->b_out_pace_control = VLC_FALSE;
135     p_input->i_pts_delay = 0;
136
137     /* Init Input fields */
138     p_input->input.p_item = p_item;
139     p_input->input.p_access = NULL;
140     p_input->input.p_stream = NULL;
141     p_input->input.p_demux  = NULL;
142     p_input->input.b_title_demux = VLC_FALSE;
143     p_input->input.i_title  = 0;
144     p_input->input.title    = NULL;
145     p_input->input.i_title_offset = p_input->input.i_seekpoint_offset = 0;
146     p_input->input.b_can_pace_control = VLC_TRUE;
147     p_input->input.b_eof = VLC_FALSE;
148     p_input->input.i_cr_average = 0;
149
150     /* No slave */
151     p_input->i_slave = 0;
152     p_input->slave   = NULL;
153
154     /* Init control buffer */
155     vlc_mutex_init( p_input, &p_input->lock_control );
156     p_input->i_control = 0;
157
158     /* Parse input options */
159     vlc_mutex_lock( &p_item->lock );
160     for( i = 0; i < p_item->i_options; i++ )
161     {
162         msg_Dbg( p_input, "option: %s", p_item->ppsz_options[i] );
163         ParseOption( p_input, p_item->ppsz_options[i] );
164     }
165     vlc_mutex_unlock( &p_item->lock );
166
167     /* Create Object Variables for private use only */
168     input_ConfigVarInit( p_input );
169
170     /* Create Objects variables for public Get and Set */
171     input_ControlVarInit( p_input );
172     p_input->input.i_cr_average = var_GetInteger( p_input, "cr-average" );
173
174     /* TODO */
175     var_Get( p_input, "bookmarks", &val );
176     if( val.psz_string )
177     {
178         /* FIXME: have a common cfg parsing routine used by sout and others */
179         char *psz_parser, *psz_start, *psz_end;
180         psz_parser = val.psz_string;
181         while( (psz_start = strchr( psz_parser, '{' ) ) )
182         {
183             seekpoint_t seekpoint;
184             char backup;
185             psz_start++;
186             psz_end = strchr( psz_start, '}' );
187             if( !psz_end ) break;
188             psz_parser = psz_end + 1;
189             backup = *psz_parser;
190             *psz_parser = 0;
191             *psz_end = ',';
192
193             seekpoint.psz_name = 0;
194             seekpoint.i_byte_offset = 0;
195             seekpoint.i_time_offset = 0;
196             while( (psz_end = strchr( psz_start, ',' ) ) )
197             {
198                 *psz_end = 0;
199                 if( !strncmp( psz_start, "name=", 5 ) )
200                 {
201                     seekpoint.psz_name = psz_start + 5;
202                 }
203                 else if( !strncmp( psz_start, "bytes=", 6 ) )
204                 {
205                     seekpoint.i_byte_offset = atoll(psz_start + 6);
206                 }
207                 else if( !strncmp( psz_start, "time=", 5 ) )
208                 {
209                     seekpoint.i_time_offset = atoll(psz_start + 5) * 1000000;
210                 }
211                 psz_start = psz_end + 1;
212             }
213             msg_Dbg( p_input, "adding bookmark: %s, bytes="I64Fd", time="I64Fd,
214                      seekpoint.psz_name, seekpoint.i_byte_offset,
215                      seekpoint.i_time_offset );
216             input_Control( p_input, INPUT_ADD_BOOKMARK, &seekpoint );
217             *psz_parser = backup;
218         }
219         free( val.psz_string );
220     }
221
222     /* Now we can attach our new input */
223     vlc_object_attach( p_input, p_parent );
224
225     /* Create thread and wait for its readiness. */
226     if( vlc_thread_create( p_input, "input", Run,
227                            VLC_THREAD_PRIORITY_INPUT, VLC_TRUE ) )
228     {
229         msg_Err( p_input, "cannot create input thread" );
230         vlc_object_detach( p_input );
231         vlc_object_destroy( p_input );
232         return NULL;
233     }
234
235     return p_input;
236 }
237
238 /*****************************************************************************
239  * input_StopThread: mark an input thread as zombie
240  *****************************************************************************
241  * This function should not return until the thread is effectively cancelled.
242  *****************************************************************************/
243 void input_StopThread( input_thread_t *p_input )
244 {
245     vlc_list_t *p_list;
246     int i;
247
248     /* Set die for input */
249     p_input->b_die = VLC_TRUE;
250
251     /* We cannot touch p_input fields directly (we can from another thread),
252      * so use the vlc_object_find way, it's perfectly safe */
253
254     /* Set die for all access */
255     p_list = vlc_list_find( p_input, VLC_OBJECT_ACCESS, FIND_CHILD );
256     for( i = 0; i < p_list->i_count; i++ )
257     {
258         p_list->p_values[i].p_object->b_die = VLC_TRUE;
259     }
260     vlc_list_release( p_list );
261
262     /* Set die for all stream */
263     p_list = vlc_list_find( p_input, VLC_OBJECT_STREAM, FIND_CHILD );
264     for( i = 0; i < p_list->i_count; i++ )
265     {
266         p_list->p_values[i].p_object->b_die = VLC_TRUE;
267     }
268     vlc_list_release( p_list );
269
270     /* Set die for all demux */
271     p_list = vlc_list_find( p_input, VLC_OBJECT_DEMUX, FIND_CHILD );
272     for( i = 0; i < p_list->i_count; i++ )
273     {
274         p_list->p_values[i].p_object->b_die = VLC_TRUE;
275     }
276     vlc_list_release( p_list );
277
278     input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
279 }
280
281 /*****************************************************************************
282  * input_DestroyThread: mark an input thread as zombie
283  *****************************************************************************
284  * This function should not return until the thread is effectively cancelled.
285  *****************************************************************************/
286 void input_DestroyThread( input_thread_t *p_input )
287 {
288     /* Join the thread */
289     vlc_thread_join( p_input );
290
291     /* Delete input lock (only after thread joined) */
292     vlc_mutex_destroy( &p_input->lock_control );
293
294     /* TODO: maybe input_DestroyThread should also delete p_input instead
295      * of the playlist but I'm not sure if it's possible */
296 }
297
298 /*****************************************************************************
299  * Run: main thread loop
300  *****************************************************************************
301  * Thread in charge of processing the network packets and demultiplexing.
302  *
303  * TODO:
304  *  read subtitle support (XXX take care of spu-delay in the right way).
305  *  multi-input support (XXX may be done with subs)
306  *****************************************************************************/
307 static int Run( input_thread_t *p_input )
308 {
309     int64_t i_intf_update = 0;
310
311     /* Signal that the thread is launched */
312     vlc_thread_ready( p_input );
313
314     if( Init( p_input ) )
315     {
316         /* If we failed, wait before we are killed, and exit */
317         p_input->b_error = VLC_TRUE;
318
319         Error( p_input );
320
321         /* Tell we're dead */
322         p_input->b_dead = VLC_TRUE;
323
324         return 0;
325     }
326
327     /* Main loop */
328     while( !p_input->b_die && !p_input->b_error && !p_input->input.b_eof )
329     {
330         vlc_bool_t b_force_update = VLC_FALSE;
331         int i_ret;
332         int i_type;
333         vlc_value_t val;
334
335         /* Do the read */
336         if( p_input->i_state != PAUSE_S  )
337         {
338             if( p_input->i_stop <= 0 || p_input->i_time < p_input->i_stop )
339                 i_ret=p_input->input.p_demux->pf_demux(p_input->input.p_demux);
340             else
341                 i_ret = 0;  /* EOF */
342
343             if( i_ret > 0 )
344             {
345                 /* TODO */
346                 if( p_input->input.b_title_demux &&
347                     p_input->input.p_demux->info.i_update )
348                 {
349                     i_ret = UpdateFromDemux( p_input );
350                     b_force_update = VLC_TRUE;
351                 }
352                 else if( !p_input->input.b_title_demux &&
353                           p_input->input.p_access &&
354                           p_input->input.p_access->info.i_update )
355                 {
356                     i_ret = UpdateFromAccess( p_input );
357                     b_force_update = VLC_TRUE;
358                 }
359             }
360
361             if( i_ret == 0 )    /* EOF */
362             {
363                 vlc_value_t repeat;
364
365                 var_Get( p_input, "input-repeat", &repeat );
366                 if( repeat.i_int == 0 )
367                 {
368                     /* End of file - we do not set b_die because only the
369                      * playlist is allowed to do so. */
370                     msg_Dbg( p_input, "EOF reached" );
371                     p_input->input.b_eof = VLC_TRUE;
372                 }
373                 else
374                 {
375                     msg_Dbg( p_input, "repeating the same input (%d)",
376                              repeat.i_int );
377                     if( repeat.i_int > 0 )
378                     {
379                         repeat.i_int--;
380                         var_Set( p_input, "input-repeat", repeat );
381                     }
382
383                     /* Seek to start title/seekpoint */
384                     val.i_int = p_input->input.i_title_start -
385                         p_input->input.i_title_offset;
386                     if( val.i_int < 0 || val.i_int >= p_input->input.i_title )
387                         val.i_int = 0;
388                     input_ControlPush( p_input,
389                                        INPUT_CONTROL_SET_TITLE, &val );
390
391                     val.i_int = p_input->input.i_seekpoint_start -
392                         p_input->input.i_seekpoint_offset;
393                     if( val.i_int > 0 /* TODO: check upper boundary */ )
394                         input_ControlPush( p_input,
395                                            INPUT_CONTROL_SET_SEEKPOINT, &val );
396
397                     /* Seek to start position */
398                     if( p_input->i_start > 0 )
399                     {
400                         val.i_time = p_input->i_start;
401                         input_ControlPush( p_input, INPUT_CONTROL_SET_TIME,
402                                            &val );
403                     }
404                     else
405                     {
406                         val.f_float = 0.0;
407                         input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION,
408                                            &val );
409                     }
410                 }
411             }
412             else if( i_ret < 0 )
413             {
414                 p_input->b_error = VLC_TRUE;
415             }
416
417             if( i_ret > 0 && p_input->i_slave > 0 )
418             {
419                 SlaveDemux( p_input );
420             }
421         }
422         else
423         {
424             /* Small wait */
425             msleep( 10*1000 );
426         }
427
428         /* Handle control */
429         vlc_mutex_lock( &p_input->lock_control );
430         ControlReduce( p_input );
431         while( !ControlPopNoLock( p_input, &i_type, &val ) )
432         {
433             msg_Dbg( p_input, "control type=%d", i_type );
434             if( Control( p_input, i_type, val ) )
435                 b_force_update = VLC_TRUE;
436         }
437         vlc_mutex_unlock( &p_input->lock_control );
438
439         if( b_force_update || i_intf_update < mdate() )
440         {
441             vlc_value_t val;
442             double f_pos;
443             int64_t i_time, i_length;
444             /* update input status variables */
445             if( !demux2_Control( p_input->input.p_demux,
446                                  DEMUX_GET_POSITION, &f_pos ) )
447             {
448                 val.f_float = (float)f_pos;
449                 var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
450             }
451             if( !demux2_Control( p_input->input.p_demux,
452                                  DEMUX_GET_TIME, &i_time ) )
453             {
454                 p_input->i_time = i_time;
455                 val.i_time = i_time;
456                 var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
457             }
458             if( !demux2_Control( p_input->input.p_demux,
459                                  DEMUX_GET_LENGTH, &i_length ) )
460             {
461                 vlc_value_t old_val;
462                 var_Get( p_input, "length", &old_val );
463                 val.i_time = i_length;
464                 var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
465
466                 if( old_val.i_time != val.i_time )
467                 {
468                     UpdateItemLength( p_input, i_length );
469                 }
470             }
471
472             var_SetBool( p_input, "intf-change", VLC_TRUE );
473             i_intf_update = mdate() + I64C(150000);
474         }
475     }
476
477     if( !p_input->b_eof && !p_input->b_error && p_input->input.b_eof )
478     {
479         /* We have finish to demux data but not to play them */
480         while( !p_input->b_die )
481         {
482             if( input_EsOutDecodersEmpty( p_input->p_es_out ) )
483                 break;
484
485             msg_Dbg( p_input, "waiting decoder fifos to empty" );
486
487             msleep( INPUT_IDLE_SLEEP );
488         }
489
490         /* We have finished */
491         p_input->b_eof = VLC_TRUE;
492     }
493
494     /* Wait we are asked to die */
495     if( !p_input->b_die )
496     {
497         Error( p_input );
498     }
499
500     /* Clean up */
501     End( p_input );
502
503     return 0;
504 }
505
506 /*****************************************************************************
507  * Init: init the input Thread
508  *****************************************************************************/
509 static int Init( input_thread_t * p_input )
510 {
511     char *psz;
512     char *psz_subtitle;
513     vlc_value_t val;
514     double f_fps;
515     vlc_meta_t *p_meta, *p_meta_user;
516     int i;
517
518     /* Initialize optional stream output. (before access/demuxer) */
519     psz = var_GetString( p_input, "sout" );
520     if( *psz )
521     {
522         p_input->p_sout = sout_NewInstance( p_input, psz );
523         if( p_input->p_sout == NULL )
524         {
525             msg_Err( p_input, "cannot start stream output instance, aborting" );
526             free( psz );
527             return VLC_EGENERIC;
528         }
529     }
530     free( psz );
531
532     /* Create es out */
533     p_input->p_es_out = input_EsOutNew( p_input );
534     es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_FALSE );
535     es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
536
537     if( InputSourceInit( p_input, &p_input->input,
538                          p_input->input.p_item->psz_uri, NULL ) )
539     {
540         goto error;
541     }
542
543     /* Create global title (from master) */
544     p_input->i_title = p_input->input.i_title;
545     p_input->title   = p_input->input.title;
546     p_input->i_title_offset = p_input->input.i_title_offset;
547     p_input->i_seekpoint_offset = p_input->input.i_seekpoint_offset;
548     if( p_input->i_title > 0 )
549     {
550         /* Setup variables */
551         input_ControlVarNavigation( p_input );
552         input_ControlVarTitle( p_input, 0 );
553     }
554
555     /* Global flag */
556     p_input->b_can_pace_control = p_input->input.b_can_pace_control;
557     p_input->b_can_pause        = p_input->input.b_can_pause;
558
559     /* Fix pts delay */
560     if( p_input->i_pts_delay <= 0 )
561         p_input->i_pts_delay = DEFAULT_PTS_DELAY;
562
563     /* If the desynchronisation requested by the user is < 0, we need to
564      * cache more data. */
565     var_Get( p_input, "audio-desync", &val );
566     if( val.i_int < 0 ) p_input->i_pts_delay -= (val.i_int * 1000);
567
568     /* Load master infos */
569     /* Init length */
570     if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH,
571                          &val.i_time ) && val.i_time > 0 )
572     {
573         var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
574
575         UpdateItemLength( p_input, val.i_time );
576     }
577
578     /* Start title/chapter */
579     val.i_int = p_input->input.i_title_start -
580         p_input->input.i_title_offset;
581     if( val.i_int > 0 && val.i_int < p_input->input.i_title )
582         input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val );
583     val.i_int = p_input->input.i_seekpoint_start -
584         p_input->input.i_seekpoint_offset;
585     if( val.i_int > 0 /* TODO: check upper boundary */ )
586         input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT, &val );
587
588     /* Start time*/
589     /* Set start time */
590     p_input->i_start = (int64_t)var_GetInteger( p_input, "start-time" ) *
591                        I64C(1000000);
592     p_input->i_stop  = (int64_t)var_GetInteger( p_input, "stop-time" ) *
593                        I64C(1000000);
594
595     if( p_input->i_start > 0 )
596     {
597         if( p_input->i_start >= val.i_time )
598         {
599             msg_Warn( p_input, "invalid start-time ignored" );
600         }
601         else
602         {
603             vlc_value_t s;
604
605             msg_Dbg( p_input, "start-time: %ds",
606                      (int)( p_input->i_start / I64C(1000000) ) );
607
608             s.i_time = p_input->i_start;
609             input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
610         }
611     }
612     if( p_input->i_stop > 0 && p_input->i_stop <= p_input->i_start )
613     {
614         msg_Warn( p_input, "invalid stop-time ignored" );
615         p_input->i_stop = 0;
616     }
617
618
619     /* Load subtitles */
620     /* Get fps and set it if not already set */
621     if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_FPS, &f_fps ) &&
622         f_fps > 1.0 )
623     {
624         vlc_value_t fps;
625
626         if( var_Get( p_input, "sub-fps", &fps ) )
627         {
628             var_Create( p_input, "sub-fps", VLC_VAR_FLOAT| VLC_VAR_DOINHERIT );
629             var_SetFloat( p_input, "sub-fps", f_fps );
630         }
631     }
632
633     /* Look for and add subtitle files */
634     psz_subtitle = var_GetString( p_input, "sub-file" );
635     if( *psz_subtitle )
636     {
637         input_source_t *sub;
638         vlc_value_t count;
639         vlc_value_t list;
640
641         msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle );
642
643         var_Change( p_input, "spu-es", VLC_VAR_CHOICESCOUNT, &count, NULL );
644
645         /* */
646         sub = InputSourceNew( p_input );
647         if( !InputSourceInit( p_input, sub, psz_subtitle, "subtitle" ) )
648         {
649             TAB_APPEND( p_input->i_slave, p_input->slave, sub );
650
651             /* Select the ES */
652             if( !var_Change( p_input, "spu-es", VLC_VAR_GETLIST, &list, NULL ) )
653             {
654                 if( count.i_int == 0 )
655                     count.i_int++;  /* if it was first one, there is disable too */
656
657                 if( count.i_int < list.p_list->i_count )
658                 {
659                     input_ControlPush( p_input, INPUT_CONTROL_SET_ES,
660                                        &list.p_list->p_values[count.i_int] );
661                 }
662                 var_Change( p_input, "spu-es", VLC_VAR_FREELIST, &list, NULL );
663             }
664         }
665     }
666
667     var_Get( p_input, "sub-autodetect-file", &val );
668     if( val.b_bool )
669     {
670         char *psz_autopath = var_GetString( p_input, "sub-autodetect-path" );
671         char **subs = subtitles_Detect( p_input, psz_autopath,
672                                         p_input->input.p_item->psz_uri );
673         input_source_t *sub;
674
675         for( i = 0; subs[i] != NULL; i++ )
676         {
677             if( strcmp( psz_subtitle, subs[i] ) )
678             {
679                 sub = InputSourceNew( p_input );
680                 if( !InputSourceInit( p_input, sub, subs[i], "subtitle" ) )
681                 {
682                     TAB_APPEND( p_input->i_slave, p_input->slave, sub );
683                 }
684             }
685             free( subs[i] );
686         }
687         free( subs );
688         free( psz_autopath );
689     }
690     free( psz_subtitle );
691
692     /* Look for slave */
693     psz = var_GetString( p_input, "input-slave" );
694     if( *psz )
695     {
696         char *psz_delim = strchr( psz, '#' );
697
698         for( ;; )
699         {
700             input_source_t *slave;
701
702             if( psz_delim )
703             {
704                 *psz_delim++ = '\0';
705             }
706
707             if( *psz == '\0' )
708             {
709                 if( psz_delim )
710                     continue;
711                 else
712                     break;
713             }
714
715             msg_Dbg( p_input, "adding slave '%s'", psz );
716             slave = InputSourceNew( p_input );
717             if( !InputSourceInit( p_input, slave, psz, NULL ) )
718             {
719                 TAB_APPEND( p_input->i_slave, p_input->slave, slave );
720             }
721             if( !psz_delim )
722                 break;
723         }
724     }
725     free( psz );
726
727     /* Set up es_out */
728     es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE );
729     val.b_bool =  VLC_FALSE;
730     if( p_input->p_sout )
731     {
732         var_Get( p_input, "sout-all", &val );
733     }
734     es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE,
735                     val.b_bool ? ES_OUT_MODE_ALL : ES_OUT_MODE_AUTO );
736
737     if( p_input->p_sout )
738     {
739         if( p_input->p_sout->i_out_pace_nocontrol > 0 )
740         {
741             p_input->b_out_pace_control = VLC_FALSE;
742         }
743         else
744         {
745             p_input->b_out_pace_control = VLC_TRUE;
746         }
747         msg_Dbg( p_input, "starting in %s mode",
748                  p_input->b_out_pace_control ? "asynch" : "synch" );
749     }
750
751     /* Get meta data from users */
752     p_meta_user = InputMetaUser( p_input );
753
754     /* Get meta data from master input */
755     if( demux2_Control( p_input->input.p_demux, DEMUX_GET_META, &p_meta ) )
756         p_meta = NULL;
757
758     /* Merge them */
759     if( p_meta == NULL )
760     {
761         p_meta = p_meta_user;
762     }
763     else if( p_meta_user )
764     {
765         vlc_meta_Merge( p_meta, p_meta_user );
766         vlc_meta_Delete( p_meta_user );
767     }
768
769     /* Get meta data from slave input */
770     for( i = 0; i < p_input->i_slave; i++ )
771     {
772         vlc_meta_t *p_meta_slave;
773
774         if( !demux2_Control( p_input->slave[i]->p_demux, DEMUX_GET_META, &p_meta_slave ) )
775         {
776             if( p_meta == NULL )
777             {
778                 p_meta = p_meta_slave;
779             }
780             else if( p_meta_slave )
781             {
782                 vlc_meta_Merge( p_meta, p_meta_slave );
783                 vlc_meta_Delete( p_meta_slave );
784             }
785         }
786     }
787
788     if( p_meta && p_meta->i_meta > 0 )
789     {
790         msg_Dbg( p_input, "meta information:" );
791         for( i = 0; i < p_meta->i_meta; i++ )
792         {
793             msg_Dbg( p_input, "  - '%s' = '%s'",
794                     _(p_meta->name[i]), p_meta->value[i] );
795
796             if( !strcmp(p_meta->name[i], VLC_META_TITLE) && p_meta->value[i] )
797                 input_Control( p_input, INPUT_SET_NAME, p_meta->value[i] );
798
799             if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
800                 input_Control( p_input, INPUT_ADD_INFO, _("General"),
801                                _("Author"), p_meta->value[i] );
802
803             input_Control( p_input, INPUT_ADD_INFO, _("Meta-information"),
804                           _(p_meta->name[i]), "%s", p_meta->value[i] );
805         }
806
807         for( i = 0; i < p_meta->i_track; i++ )
808         {
809             vlc_meta_t *tk = p_meta->track[i];
810             int j;
811
812             if( tk->i_meta > 0 )
813             {
814                 char *psz_cat = malloc( strlen(_("Stream")) + 10 );
815
816                 msg_Dbg( p_input, "  - track[%d]:", i );
817
818                 sprintf( psz_cat, "%s %d", _("Stream"), i );
819                 for( j = 0; j < tk->i_meta; j++ )
820                 {
821                     msg_Dbg( p_input, "     - '%s' = '%s'", _(tk->name[j]),
822                              tk->value[j] );
823
824                     input_Control( p_input, INPUT_ADD_INFO, psz_cat,
825                                    _(tk->name[j]), "%s", tk->value[j] );
826                 }
827             }
828         }
829
830         if( p_input->p_sout && p_input->p_sout->p_meta == NULL )
831         {
832             p_input->p_sout->p_meta = p_meta;
833         }
834         else
835         {
836             vlc_meta_Delete( p_meta );
837         }
838     }
839
840     msg_Dbg( p_input, "`%s' sucessfully opened",
841              p_input->input.p_item->psz_uri );
842
843     /* initialization is complete */
844     p_input->i_state = PLAYING_S;
845
846     val.i_int = PLAYING_S;
847     var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
848
849     return VLC_SUCCESS;
850
851 error:
852     if( p_input->p_es_out )
853         input_EsOutDelete( p_input->p_es_out );
854
855     if( p_input->p_sout )
856         sout_DeleteInstance( p_input->p_sout );
857
858     /* Mark them deleted */
859     p_input->input.p_demux = NULL;
860     p_input->input.p_stream = NULL;
861     p_input->input.p_access = NULL;
862     p_input->p_es_out = NULL;
863     p_input->p_sout = NULL;
864
865     return VLC_EGENERIC;
866 }
867
868 /*****************************************************************************
869  * Error: RunThread() error loop
870  *****************************************************************************
871  * This function is called when an error occured during thread main's loop.
872  *****************************************************************************/
873 static void Error( input_thread_t *p_input )
874 {
875     while( !p_input->b_die )
876     {
877         /* Sleep a while */
878         msleep( INPUT_IDLE_SLEEP );
879     }
880 }
881
882 /*****************************************************************************
883  * End: end the input thread
884  *****************************************************************************/
885 static void End( input_thread_t * p_input )
886 {
887     vlc_value_t val;
888     int i;
889
890     msg_Dbg( p_input, "closing `%s'",
891              p_input->input.p_item->psz_uri );
892
893     /* We are at the end */
894     p_input->i_state = END_S;
895
896     val.i_int = END_S;
897     var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
898
899     /* Clean control variables */
900     input_ControlVarClean( p_input );
901
902     /* Clean up master */
903     InputSourceClean( p_input, &p_input->input );
904
905     /* Delete slave */
906     for( i = 0; i < p_input->i_slave; i++ )
907     {
908         InputSourceClean( p_input, p_input->slave[i] );
909         free( p_input->slave[i] );
910     }
911     if( p_input->slave ) free( p_input->slave );
912
913     /* Unload all modules */
914     if( p_input->p_es_out )
915         input_EsOutDelete( p_input->p_es_out );
916
917     /* Close optional stream output instance */
918     if( p_input->p_sout )
919     {
920         vlc_object_t *p_pl =
921             vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
922         vlc_value_t keep;
923
924         if( var_Get( p_input, "sout-keep", &keep ) >= 0 && keep.b_bool && p_pl )
925         {
926             /* attach sout to the playlist */
927             msg_Warn( p_input, "keeping sout" );
928             vlc_object_detach( p_input->p_sout );
929             vlc_object_attach( p_input->p_sout, p_pl );
930         }
931         else
932         {
933             msg_Warn( p_input, "destroying sout" );
934             sout_DeleteInstance( p_input->p_sout );
935         }
936         if( p_pl )
937             vlc_object_release( p_pl );
938     }
939
940     /* Tell we're dead */
941     p_input->b_dead = VLC_TRUE;
942 }
943
944 /*****************************************************************************
945  * Control
946  *****************************************************************************/
947 static inline int ControlPopNoLock( input_thread_t *p_input,
948                                     int *pi_type, vlc_value_t *p_val )
949 {
950     if( p_input->i_control <= 0 )
951     {
952         return VLC_EGENERIC;
953     }
954
955     *pi_type = p_input->control[0].i_type;
956     *p_val   = p_input->control[0].val;
957
958     p_input->i_control--;
959     if( p_input->i_control > 0 )
960     {
961         int i;
962
963         for( i = 0; i < p_input->i_control; i++ )
964         {
965             p_input->control[i].i_type = p_input->control[i+1].i_type;
966             p_input->control[i].val    = p_input->control[i+1].val;
967         }
968     }
969
970     return VLC_SUCCESS;
971 }
972
973 static void ControlReduce( input_thread_t *p_input )
974 {
975     int i;
976     for( i = 1; i < p_input->i_control; i++ )
977     {
978         const int i_lt = p_input->control[i-1].i_type;
979         const int i_ct = p_input->control[i].i_type;
980
981         /* XXX We can't merge INPUT_CONTROL_SET_ES */
982         msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->i_control,
983                  i_lt, i_ct );
984         if( i_lt == i_ct &&
985             ( i_ct == INPUT_CONTROL_SET_STATE ||
986               i_ct == INPUT_CONTROL_SET_RATE ||
987               i_ct == INPUT_CONTROL_SET_POSITION ||
988               i_ct == INPUT_CONTROL_SET_TIME ||
989               i_ct == INPUT_CONTROL_SET_PROGRAM ||
990               i_ct == INPUT_CONTROL_SET_TITLE ||
991               i_ct == INPUT_CONTROL_SET_SEEKPOINT ||
992               i_ct == INPUT_CONTROL_SET_BOOKMARK ) )
993         {
994             int j;
995             msg_Dbg( p_input, "merged at %d", i );
996             /* Remove the i-1 */
997             for( j = i; j <  p_input->i_control; j++ )
998                 p_input->control[j-1] = p_input->control[j];
999             p_input->i_control--;
1000         }
1001         else
1002         {
1003             /* TODO but that's not that important
1004                 - merge SET_X with SET_X_CMD
1005                 - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
1006                 - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
1007                 - ?
1008                 */
1009         }
1010     }
1011 }
1012
1013 static vlc_bool_t Control( input_thread_t *p_input, int i_type,
1014                            vlc_value_t val )
1015 {
1016     vlc_bool_t b_force_update = VLC_FALSE;
1017
1018     switch( i_type )
1019     {
1020         case INPUT_CONTROL_SET_DIE:
1021             msg_Dbg( p_input, "control: INPUT_CONTROL_SET_DIE proceed" );
1022             /* Mark all submodules to die */
1023             if( p_input->input.p_access )
1024                 p_input->input.p_access->b_die = VLC_TRUE;
1025             if( p_input->input.p_stream )
1026                 p_input->input.p_stream->b_die = VLC_TRUE;
1027             p_input->input.p_demux->b_die = VLC_TRUE;
1028
1029             p_input->b_die = VLC_TRUE;
1030             break;
1031
1032         case INPUT_CONTROL_SET_POSITION:
1033         case INPUT_CONTROL_SET_POSITION_OFFSET:
1034         {
1035             double f_pos;
1036             if( i_type == INPUT_CONTROL_SET_POSITION )
1037             {
1038                 f_pos = val.f_float;
1039             }
1040             else
1041             {
1042                 /* Should not fail */
1043                 demux2_Control( p_input->input.p_demux,
1044                                 DEMUX_GET_POSITION, &f_pos );
1045                 f_pos += val.f_float;
1046             }
1047             if( f_pos < 0.0 ) f_pos = 0.0;
1048             if( f_pos > 1.0 ) f_pos = 1.0;
1049             if( demux2_Control( p_input->input.p_demux, DEMUX_SET_POSITION,
1050                                 f_pos ) )
1051             {
1052                 msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
1053                          "%2.1f%% failed", f_pos * 100 );
1054             }
1055             else
1056             {
1057                 if( p_input->i_slave > 0 )
1058                     SlaveSeek( p_input );
1059
1060                 input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
1061                 es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1062                 b_force_update = VLC_TRUE;
1063             }
1064             break;
1065         }
1066
1067         case INPUT_CONTROL_SET_TIME:
1068         case INPUT_CONTROL_SET_TIME_OFFSET:
1069         {
1070             int64_t i_time;
1071             int i_ret;
1072
1073             if( i_type == INPUT_CONTROL_SET_TIME )
1074             {
1075                 i_time = val.i_time;
1076             }
1077             else
1078             {
1079                 /* Should not fail */
1080                 demux2_Control( p_input->input.p_demux,
1081                                 DEMUX_GET_TIME, &i_time );
1082                 i_time += val.i_time;
1083             }
1084             if( i_time < 0 ) i_time = 0;
1085             i_ret = demux2_Control( p_input->input.p_demux,
1086                                     DEMUX_SET_TIME, i_time );
1087             if( i_ret )
1088             {
1089                 int64_t i_length;
1090                 /* Emulate it with a SET_POS */
1091
1092                 demux2_Control( p_input->input.p_demux,
1093                                 DEMUX_GET_LENGTH, &i_length );
1094                 if( i_length > 0 )
1095                 {
1096                     double f_pos = (double)i_time / (double)i_length;
1097                     i_ret = demux2_Control( p_input->input.p_demux,
1098                                             DEMUX_SET_POSITION, f_pos );
1099                 }
1100             }
1101             if( i_ret )
1102             {
1103                 msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) "I64Fd
1104                          " failed", i_time );
1105             }
1106             else
1107             {
1108                 if( p_input->i_slave > 0 )
1109                     SlaveSeek( p_input );
1110
1111                 input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
1112
1113                 es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1114                 b_force_update = VLC_TRUE;
1115             }
1116             break;
1117         }
1118
1119         case INPUT_CONTROL_SET_STATE:
1120             if( ( val.i_int == PLAYING_S && p_input->i_state == PAUSE_S ) ||
1121                 ( val.i_int == PAUSE_S && p_input->i_state == PAUSE_S ) )
1122             {
1123                 int i_ret;
1124                 if( p_input->input.p_access )
1125                     i_ret = access2_Control( p_input->input.p_access,
1126                                              ACCESS_SET_PAUSE_STATE, VLC_FALSE );
1127                 else
1128                     i_ret = demux2_Control( p_input->input.p_demux,
1129                                             DEMUX_SET_PAUSE_STATE, VLC_FALSE );
1130
1131                 if( i_ret )
1132                 {
1133                     /* FIXME What to do ? */
1134                     msg_Warn( p_input, "cannot unset pause -> EOF" );
1135                     input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
1136                 }
1137
1138                 b_force_update = VLC_TRUE;
1139
1140                 /* Switch to play */
1141                 p_input->i_state = PLAYING_S;
1142                 val.i_int = PLAYING_S;
1143                 var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
1144
1145                 /* Reset clock */
1146                 es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1147             }
1148             else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S &&
1149                      p_input->b_can_pause )
1150             {
1151                 int i_ret;
1152                 if( p_input->input.p_access )
1153                     i_ret = access2_Control( p_input->input.p_access,
1154                                              ACCESS_SET_PAUSE_STATE, VLC_TRUE );
1155                 else
1156                     i_ret = demux2_Control( p_input->input.p_demux,
1157                                             DEMUX_SET_PAUSE_STATE, VLC_TRUE );
1158
1159                 b_force_update = VLC_TRUE;
1160
1161                 if( i_ret )
1162                 {
1163                     msg_Warn( p_input, "cannot set pause state" );
1164                     val.i_int = p_input->i_state;
1165                 }
1166                 else
1167                 {
1168                     val.i_int = PAUSE_S;
1169                 }
1170
1171                 /* Switch to new state */
1172                 p_input->i_state = val.i_int;
1173                 var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
1174             }
1175             else if( val.i_int == PAUSE_S && !p_input->b_can_pause )
1176             {
1177                 b_force_update = VLC_TRUE;
1178
1179                 /* Correct "state" value */
1180                 val.i_int = p_input->i_state;
1181                 var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
1182             }
1183             else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S )
1184             {
1185                 msg_Err( p_input, "invalid state in INPUT_CONTROL_SET_STATE" );
1186             }
1187             break;
1188
1189         case INPUT_CONTROL_SET_RATE:
1190         case INPUT_CONTROL_SET_RATE_SLOWER:
1191         case INPUT_CONTROL_SET_RATE_FASTER:
1192         {
1193             int i_rate;
1194
1195             if( i_type == INPUT_CONTROL_SET_RATE_SLOWER )
1196                 i_rate = p_input->i_rate * 2;
1197             else if( i_type == INPUT_CONTROL_SET_RATE_FASTER )
1198                 i_rate = p_input->i_rate / 2;
1199             else
1200                 i_rate = val.i_int;
1201
1202             if( i_rate < INPUT_RATE_MIN )
1203             {
1204                 msg_Dbg( p_input, "cannot set rate faster" );
1205                 i_rate = INPUT_RATE_MIN;
1206             }
1207             else if( i_rate > INPUT_RATE_MAX )
1208             {
1209                 msg_Dbg( p_input, "cannot set rate slower" );
1210                 i_rate = INPUT_RATE_MAX;
1211             }
1212             if( i_rate != INPUT_RATE_DEFAULT &&
1213                 ( !p_input->b_can_pace_control ||
1214                   ( p_input->p_sout && !p_input->b_out_pace_control ) ) )
1215             {
1216                 msg_Dbg( p_input, "cannot change rate" );
1217                 i_rate = INPUT_RATE_DEFAULT;
1218             }
1219             if( i_rate != p_input->i_rate )
1220             {
1221                 p_input->i_rate  = i_rate;
1222                 val.i_int = i_rate;
1223                 var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL );
1224
1225                 /* We haven't send data to decoder when rate != default */
1226                 if( i_rate == INPUT_RATE_DEFAULT )
1227                     input_EsOutDiscontinuity( p_input->p_es_out, VLC_TRUE );
1228
1229                 /* Reset clock */
1230                 es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1231
1232                 b_force_update = VLC_TRUE;
1233             }
1234             break;
1235         }
1236
1237         case INPUT_CONTROL_SET_PROGRAM:
1238             /* No need to force update, es_out does it if needed */
1239             es_out_Control( p_input->p_es_out,
1240                             ES_OUT_SET_GROUP, val.i_int );
1241             break;
1242
1243         case INPUT_CONTROL_SET_ES:
1244             /* No need to force update, es_out does it if needed */
1245             es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
1246                             input_EsOutGetFromID( p_input->p_es_out,
1247                                                   val.i_int ) );
1248             break;
1249
1250         case INPUT_CONTROL_SET_AUDIO_DELAY:
1251             input_EsOutSetDelay( p_input->p_es_out,
1252                                  AUDIO_ES, val.i_time );
1253             var_Change( p_input, "audio-delay", VLC_VAR_SETVALUE, &val, NULL );
1254             break;
1255
1256         case INPUT_CONTROL_SET_SPU_DELAY:
1257             input_EsOutSetDelay( p_input->p_es_out,
1258                                  SPU_ES, val.i_time );
1259             var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, &val, NULL );
1260             break;
1261
1262         case INPUT_CONTROL_SET_TITLE:
1263         case INPUT_CONTROL_SET_TITLE_NEXT:
1264         case INPUT_CONTROL_SET_TITLE_PREV:
1265             if( p_input->input.b_title_demux &&
1266                 p_input->input.i_title > 0 )
1267             {
1268                 /* TODO */
1269                 /* FIXME handle demux title */
1270                 demux_t *p_demux = p_input->input.p_demux;
1271                 int i_title;
1272
1273                 if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
1274                     i_title = p_demux->info.i_title - 1;
1275                 else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
1276                     i_title = p_demux->info.i_title + 1;
1277                 else
1278                     i_title = val.i_int;
1279
1280                 if( i_title >= 0 && i_title < p_input->input.i_title )
1281                 {
1282                     demux2_Control( p_demux, DEMUX_SET_TITLE, i_title );
1283
1284                     input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
1285                     es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1286
1287                     input_ControlVarTitle( p_input, i_title );
1288                 }
1289             }
1290             else if( p_input->input.i_title > 0 )
1291             {
1292                 access_t *p_access = p_input->input.p_access;
1293                 int i_title;
1294
1295                 if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
1296                     i_title = p_access->info.i_title - 1;
1297                 else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
1298                     i_title = p_access->info.i_title + 1;
1299                 else
1300                     i_title = val.i_int;
1301
1302                 if( i_title >= 0 && i_title < p_input->input.i_title )
1303                 {
1304                     access2_Control( p_access, ACCESS_SET_TITLE, i_title );
1305                     stream_AccessReset( p_input->input.p_stream );
1306
1307                     input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
1308                     es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1309                 }
1310             }
1311             break;
1312         case INPUT_CONTROL_SET_SEEKPOINT:
1313         case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
1314         case INPUT_CONTROL_SET_SEEKPOINT_PREV:
1315             if( p_input->input.b_title_demux &&
1316                 p_input->input.i_title > 0 )
1317             {
1318                 demux_t *p_demux = p_input->input.p_demux;
1319                 int i_seekpoint;
1320
1321                 if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
1322                     i_seekpoint = p_demux->info.i_seekpoint - 1;
1323                 else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
1324                     i_seekpoint = p_demux->info.i_seekpoint + 1;
1325                 else
1326                     i_seekpoint = val.i_int;
1327
1328                 if( i_seekpoint >= 0 && i_seekpoint <
1329                     p_input->input.title[p_demux->info.i_title]->i_seekpoint )
1330                 {
1331                     demux2_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint );
1332
1333                     input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
1334                     es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1335                 }
1336             }
1337             else if( p_input->input.i_title > 0 )
1338             {
1339                 access_t *p_access = p_input->input.p_access;
1340                 int i_seekpoint;
1341
1342                 if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
1343                     i_seekpoint = p_access->info.i_seekpoint - 1;
1344                 else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
1345                     i_seekpoint = p_access->info.i_seekpoint + 1;
1346                 else
1347                     i_seekpoint = val.i_int;
1348
1349                 if( i_seekpoint >= 0 && i_seekpoint <
1350                     p_input->input.title[p_access->info.i_title]->i_seekpoint )
1351                 {
1352                     access2_Control( p_access, ACCESS_SET_SEEKPOINT, i_seekpoint );
1353                     stream_AccessReset( p_input->input.p_stream );
1354
1355                     input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
1356                     es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
1357                 }
1358             }
1359             break;
1360
1361         case INPUT_CONTROL_SET_BOOKMARK:
1362         default:
1363             msg_Err( p_input, "not yet implemented" );
1364             break;
1365     }
1366
1367     return b_force_update;
1368 }
1369
1370 /*****************************************************************************
1371  * UpdateFromDemux:
1372  *****************************************************************************/
1373 static int UpdateFromDemux( input_thread_t *p_input )
1374 {
1375     demux_t *p_demux = p_input->input.p_demux;
1376     vlc_value_t v;
1377
1378     if( p_demux->info.i_update & INPUT_UPDATE_TITLE )
1379     {
1380         v.i_int = p_demux->info.i_title;
1381         var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL );
1382
1383         input_ControlVarTitle( p_input, p_demux->info.i_title );
1384
1385         p_demux->info.i_update &= ~INPUT_UPDATE_TITLE;
1386     }
1387     if( p_demux->info.i_update & INPUT_UPDATE_SEEKPOINT )
1388     {
1389         v.i_int = p_demux->info.i_seekpoint;
1390         var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL);
1391
1392         p_demux->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
1393     }
1394     p_demux->info.i_update &= ~INPUT_UPDATE_SIZE;
1395
1396     /* Hmmm only works with master input */
1397     if( p_input->input.p_demux == p_demux )
1398     {
1399         int i_title_end = p_input->input.i_title_end -
1400             p_input->input.i_title_offset;
1401         int i_seekpoint_end = p_input->input.i_seekpoint_end -
1402             p_input->input.i_seekpoint_offset;
1403
1404         if( i_title_end >= 0 && i_seekpoint_end >=0 )
1405         {
1406             if( p_demux->info.i_title > i_title_end ||
1407                 ( p_demux->info.i_title == i_title_end &&
1408                   p_demux->info.i_seekpoint > i_seekpoint_end ) ) return 0;
1409         }
1410         else if( i_seekpoint_end >=0 )
1411         {
1412             if( p_demux->info.i_seekpoint > i_seekpoint_end ) return 0;
1413         }
1414         else if( i_title_end >= 0 )
1415         {
1416             if( p_demux->info.i_title > i_title_end ) return 0;
1417         }
1418     }
1419
1420     return 1;
1421 }
1422
1423 /*****************************************************************************
1424  * UpdateFromAccess:
1425  *****************************************************************************/
1426 static int UpdateFromAccess( input_thread_t *p_input )
1427 {
1428     access_t *p_access = p_input->input.p_access;
1429     vlc_value_t v;
1430
1431     if( p_access->info.i_update & INPUT_UPDATE_TITLE )
1432     {
1433         v.i_int = p_access->info.i_title;
1434         var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL );
1435
1436         input_ControlVarTitle( p_input, p_access->info.i_title );
1437
1438         stream_AccessUpdate( p_input->input.p_stream );
1439
1440         p_access->info.i_update &= ~INPUT_UPDATE_TITLE;
1441     }
1442     if( p_access->info.i_update & INPUT_UPDATE_SEEKPOINT )
1443     {
1444         v.i_int = p_access->info.i_seekpoint;
1445         var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL);
1446
1447         p_access->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
1448     }
1449     p_access->info.i_update &= ~INPUT_UPDATE_SIZE;
1450
1451     /* Hmmm only works with master input */
1452     if( p_input->input.p_access == p_access )
1453     {
1454         int i_title_end = p_input->input.i_title_end -
1455             p_input->input.i_title_offset;
1456         int i_seekpoint_end = p_input->input.i_seekpoint_end -
1457             p_input->input.i_seekpoint_offset;
1458
1459         if( i_title_end >= 0 && i_seekpoint_end >=0 )
1460         {
1461             if( p_access->info.i_title > i_title_end ||
1462                 ( p_access->info.i_title == i_title_end &&
1463                   p_access->info.i_seekpoint > i_seekpoint_end ) ) return 0;
1464         }
1465         else if( i_seekpoint_end >=0 )
1466         {
1467             if( p_access->info.i_seekpoint > i_seekpoint_end ) return 0;
1468         }
1469         else if( i_title_end >= 0 )
1470         {
1471             if( p_access->info.i_title > i_title_end ) return 0;
1472         }
1473     }
1474
1475     return 1;
1476 }
1477
1478 /*****************************************************************************
1479  * UpdateItemLength:
1480  *****************************************************************************/
1481 static void UpdateItemLength( input_thread_t *p_input, int64_t i_length )
1482 {
1483     char psz_buffer[MSTRTIME_MAX_SIZE];
1484
1485     vlc_mutex_lock( &p_input->input.p_item->lock );
1486     p_input->input.p_item->i_duration = i_length;
1487     vlc_mutex_unlock( &p_input->input.p_item->lock );
1488
1489     input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"),
1490                    msecstotimestr( psz_buffer, i_length / 1000 ) );
1491 }
1492
1493 /*****************************************************************************
1494  * InputSourceNew:
1495  *****************************************************************************/
1496 static input_source_t *InputSourceNew( input_thread_t *p_input )
1497 {
1498     input_source_t *in = malloc( sizeof( input_source_t ) );
1499
1500     in->p_item   = NULL;
1501     in->p_access = NULL;
1502     in->p_stream = NULL;
1503     in->p_demux  = NULL;
1504     in->b_title_demux = VLC_FALSE;
1505     in->i_title  = 0;
1506     in->title    = NULL;
1507     in->b_can_pace_control = VLC_TRUE;
1508     in->b_eof = VLC_FALSE;
1509     in->i_cr_average = 0;
1510
1511     return in;
1512 }
1513
1514 /*****************************************************************************
1515  * InputSourceInit:
1516  *****************************************************************************/
1517 static int InputSourceInit( input_thread_t *p_input,
1518                             input_source_t *in, char *psz_mrl,
1519                             char *psz_forced_demux )
1520 {
1521     char *psz_dup = strdup( psz_mrl );
1522     char *psz_access;
1523     char *psz_demux;
1524     char *psz_path;
1525     vlc_value_t val;
1526
1527     /* Split uri */
1528     MRLSplit( p_input, psz_dup, &psz_access, &psz_demux, &psz_path );
1529
1530     msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
1531              psz_mrl, psz_access, psz_demux, psz_path );
1532
1533     /* Find optional titles and seekpoints */
1534     MRLSections( p_input, psz_path, &in->i_title_start, &in->i_title_end,
1535                  &in->i_seekpoint_start, &in->i_seekpoint_end );
1536
1537     if( psz_forced_demux && *psz_forced_demux )
1538         psz_demux = psz_forced_demux;
1539
1540     /* Try access_demux if no demux given */
1541     if( *psz_demux == '\0' )
1542     {
1543         in->p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
1544                                   NULL, p_input->p_es_out );
1545     }
1546
1547     if( in->p_demux )
1548     {
1549         int64_t i_pts_delay;
1550
1551         /* Get infos from access_demux */
1552         demux2_Control( in->p_demux,
1553                         DEMUX_GET_PTS_DELAY, &i_pts_delay );
1554         p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );
1555
1556         in->b_title_demux = VLC_TRUE;
1557         if( demux2_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
1558                             &in->title, &in->i_title,
1559                             &in->i_title_offset, &in->i_seekpoint_offset ) )
1560         {
1561             in->i_title = 0;
1562             in->title   = NULL;
1563         }
1564         demux2_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE,
1565                         &in->b_can_pace_control );
1566         demux2_Control( in->p_demux, DEMUX_CAN_PAUSE,
1567                         &in->b_can_pause );
1568
1569         /* FIXME todo
1570         demux2_Control( in->p_demux, DEMUX_CAN_SEEK,
1571                         &val.b_bool );
1572         */
1573     }
1574     else
1575     {
1576         int64_t i_pts_delay;
1577
1578         /* Now try a real access */
1579         in->p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
1580
1581         /* Access failed, URL encoded ? */
1582         if( in->p_access == NULL && strchr( psz_path, '%' ) )
1583         {
1584             DecodeUrl( psz_path );
1585
1586             msg_Dbg( p_input, "retying with access `%s' demux `%s' path `%s'",
1587                      psz_access, psz_demux, psz_path );
1588
1589             in->p_access = access2_New( p_input,
1590                                         psz_access, psz_demux, psz_path );
1591         }
1592 #ifndef WIN32      /* Remove this gross hack from the win32 build as colons
1593                         * are forbidden in filenames on Win32. */
1594
1595         /* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
1596         if( in->p_access == NULL &&
1597             *psz_access == '\0' && ( *psz_demux || *psz_path ) )
1598         {
1599             free( psz_dup );
1600             psz_dup = strdup( psz_mrl );
1601             psz_access = "";
1602             psz_demux = "";
1603             psz_path = psz_dup;
1604
1605             in->p_access = access2_New( p_input,
1606                                         psz_access, psz_demux, psz_path );
1607         }
1608 #endif
1609
1610         if( in->p_access == NULL )
1611         {
1612             msg_Err( p_input, "no suitable access module for `%s'", psz_mrl );
1613             goto error;
1614         }
1615
1616         /* Get infos from access */
1617         access2_Control( in->p_access,
1618                          ACCESS_GET_PTS_DELAY, &i_pts_delay );
1619         p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );
1620
1621         in->b_title_demux = VLC_FALSE;
1622         if( access2_Control( in->p_access, ACCESS_GET_TITLE_INFO,
1623                              &in->title, &in->i_title,
1624                              &in->i_title_offset, &in->i_seekpoint_offset ) )
1625
1626         {
1627             in->i_title = 0;
1628             in->title   = NULL;
1629         }
1630         access2_Control( in->p_access, ACCESS_CAN_CONTROL_PACE,
1631                          &in->b_can_pace_control );
1632         access2_Control( in->p_access, ACCESS_CAN_PAUSE,
1633                          &in->b_can_pause );
1634         access2_Control( in->p_access, ACCESS_CAN_SEEK,
1635                          &val.b_bool );
1636         var_Set( p_input, "seekable", val );
1637
1638         /* Create the stream_t */
1639         in->p_stream = stream_AccessNew( in->p_access );
1640         if( in->p_stream == NULL )
1641         {
1642             msg_Warn( p_input, "cannot create a stream_t from access" );
1643             goto error;
1644         }
1645
1646         /* Open a demuxer */
1647         if( *psz_demux == '\0' && *in->p_access->psz_demux )
1648         {
1649             psz_demux = in->p_access->psz_demux;
1650         }
1651         in->p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
1652                                   in->p_stream, p_input->p_es_out );
1653         if( in->p_demux == NULL )
1654         {
1655             msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
1656                      psz_access, psz_demux, psz_path );
1657             goto error;
1658         }
1659
1660         /* TODO get title from demux */
1661         if( in->i_title <= 0 )
1662         {
1663             if( demux2_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
1664                                 &in->title, &in->i_title,
1665                                 &in->i_title_offset, &in->i_seekpoint_offset ))
1666             {
1667                 in->i_title = 0;
1668                 in->title   = NULL;
1669             }
1670             else
1671             {
1672                 in->b_title_demux = VLC_TRUE;
1673             }
1674         }
1675     }
1676     free( psz_dup );
1677     return VLC_SUCCESS;
1678
1679 error:
1680     if( in->p_demux )
1681         demux2_Delete( in->p_demux );
1682
1683     if( in->p_stream )
1684         stream_AccessDelete( in->p_stream );
1685
1686     if( in->p_access )
1687         access2_Delete( in->p_access );
1688     free( psz_dup );
1689
1690     return VLC_EGENERIC;
1691 }
1692
1693 /*****************************************************************************
1694  * InputSourceClean:
1695  *****************************************************************************/
1696 static void InputSourceClean( input_thread_t *p_input, input_source_t *in )
1697 {
1698     if( in->p_demux )
1699         demux2_Delete( in->p_demux );
1700
1701     if( in->p_stream )
1702         stream_AccessDelete( in->p_stream );
1703
1704     if( in->p_access )
1705         access2_Delete( in->p_access );
1706
1707     if( in->i_title > 0 )
1708     {
1709         int i;
1710         for( i = 0; i < in->i_title; i++ )
1711         {
1712             vlc_input_title_Delete( in->title[i] );
1713         }
1714         free( in->title );
1715     }
1716 }
1717
1718 static void SlaveDemux( input_thread_t *p_input )
1719 {
1720     int64_t i_time;
1721     int i;
1722     if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
1723     {
1724         msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
1725         return;
1726     }
1727
1728     for( i = 0; i < p_input->i_slave; i++ )
1729     {
1730         input_source_t *in = p_input->slave[i];
1731         int i_ret = 1;
1732
1733         if( in->b_eof )
1734             continue;
1735
1736         if( demux2_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
1737         {
1738             for( ;; )
1739             {
1740                 int64_t i_stime;
1741                 if( demux2_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) )
1742                 {
1743                     msg_Err( p_input, "slave[%d] doesn't like "
1744                              "DEMUX_GET_TIME -> EOF", i );
1745                     i_ret = 0;
1746                     break;
1747                 }
1748
1749                 if( i_stime >= i_time )
1750                     break;
1751
1752                 if( ( i_ret = in->p_demux->pf_demux( in->p_demux ) ) <= 0 )
1753                     break;
1754             }
1755         }
1756         else
1757         {
1758             i_ret = in->p_demux->pf_demux( in->p_demux );
1759         }
1760
1761         if( i_ret <= 0 )
1762         {
1763             msg_Dbg( p_input, "slave %d EOF", i );
1764             in->b_eof = VLC_TRUE;
1765         }
1766     }
1767 }
1768
1769 static void SlaveSeek( input_thread_t *p_input )
1770 {
1771     int64_t i_time;
1772     int i;
1773
1774     if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
1775     {
1776         msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
1777         return;
1778     }
1779
1780     for( i = 0; i < p_input->i_slave; i++ )
1781     {
1782         input_source_t *in = p_input->slave[i];
1783
1784         if( demux2_Control( in->p_demux, DEMUX_SET_TIME, i_time ) )
1785         {
1786             msg_Err( p_input, "seek failed for slave %d -> EOF", i );
1787             in->b_eof = VLC_TRUE;
1788         }
1789     }
1790 }
1791 /*****************************************************************************
1792  * InputMetaUser:
1793  *****************************************************************************/
1794 static vlc_meta_t *InputMetaUser( input_thread_t *p_input )
1795 {
1796     vlc_meta_t *p_meta;
1797     vlc_value_t val;
1798
1799     if( ( p_meta = vlc_meta_New() ) == NULL )
1800         return NULL;
1801
1802     /* Get meta information from user */
1803 #define GET_META( c, s ) \
1804     var_Get( p_input, (s), &val );  \
1805     if( *val.psz_string )       \
1806         vlc_meta_Add( p_meta, c, val.psz_string ); \
1807     free( val.psz_string )
1808
1809     GET_META( VLC_META_TITLE, "meta-title" );
1810     GET_META( VLC_META_AUTHOR, "meta-author" );
1811     GET_META( VLC_META_ARTIST, "meta-artist" );
1812     GET_META( VLC_META_GENRE, "meta-genre" );
1813     GET_META( VLC_META_COPYRIGHT, "meta-copyright" );
1814     GET_META( VLC_META_DESCRIPTION, "meta-description" );
1815     GET_META( VLC_META_DATE, "meta-date" );
1816     GET_META( VLC_META_URL, "meta-url" );
1817 #undef GET_META
1818
1819     return p_meta;
1820 }
1821
1822 /*****************************************************************************
1823  * DecodeUrl: decode a given encoded url
1824  *****************************************************************************/
1825 static void DecodeUrl( char *psz )
1826 {
1827     char *dup = strdup( psz );
1828     char *p = dup;
1829
1830     while( *p )
1831     {
1832         if( *p == '%' )
1833         {
1834             char val[3];
1835             p++;
1836             if( !*p )
1837             {
1838                 break;
1839             }
1840
1841             val[0] = *p++;
1842             val[1] = *p++;
1843             val[2] = '\0';
1844
1845             *psz++ = strtol( val, NULL, 16 );
1846         }
1847         else if( *p == '+' )
1848         {
1849             *psz++ = ' ';
1850             p++;
1851         }
1852         else
1853         {
1854             *psz++ = *p++;
1855         }
1856     }
1857     *psz++  ='\0';
1858     free( dup );
1859 }
1860
1861 /*****************************************************************************
1862  * ParseOption: parses the options for the input
1863  *****************************************************************************
1864  * This function parses the input (config) options and creates their associated
1865  * object variables.
1866  * Options are of the form "[no[-]]foo[=bar]" where foo is the option name and
1867  * bar is the value of the option.
1868  *****************************************************************************/
1869 static void ParseOption( input_thread_t *p_input, const char *psz_option )
1870 {
1871     char *psz_name = (char *)psz_option;
1872     char *psz_value = strchr( psz_option, '=' );
1873     int  i_name_len, i_type;
1874     vlc_bool_t b_isno = VLC_FALSE;
1875     vlc_value_t val;
1876
1877     if( psz_value ) i_name_len = psz_value - psz_option;
1878     else i_name_len = strlen( psz_option );
1879
1880     /* It's too much of an hassle to remove the ':' when we parse
1881      * the cmd line :) */
1882     if( i_name_len && *psz_name == ':' )
1883     {
1884         psz_name++;
1885         i_name_len--;
1886     }
1887
1888     if( i_name_len == 0 ) return;
1889
1890     psz_name = strndup( psz_name, i_name_len );
1891     if( psz_value ) psz_value++;
1892
1893     i_type = config_GetType( p_input, psz_name );
1894
1895     if( !i_type && !psz_value )
1896     {
1897         /* check for "no-foo" or "nofoo" */
1898         if( !strncmp( psz_name, "no-", 3 ) )
1899         {
1900             memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 );
1901         }
1902         else if( !strncmp( psz_name, "no", 2 ) )
1903         {
1904             memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 );
1905         }
1906         else goto cleanup;           /* Option doesn't exist */
1907
1908         b_isno = VLC_TRUE;
1909         i_type = config_GetType( p_input, psz_name );
1910
1911         if( !i_type ) goto cleanup;  /* Option doesn't exist */
1912     }
1913     else if( !i_type ) goto cleanup; /* Option doesn't exist */
1914
1915     if( ( i_type != VLC_VAR_BOOL ) &&
1916         ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */
1917
1918     /* Create the variable in the input object.
1919      * Children of the input object will be able to retreive this value
1920      * thanks to the inheritance property of the object variables. */
1921     var_Create( p_input, psz_name, i_type );
1922
1923     switch( i_type )
1924     {
1925     case VLC_VAR_BOOL:
1926         val.b_bool = !b_isno;
1927         break;
1928
1929     case VLC_VAR_INTEGER:
1930         val.i_int = atoi( psz_value );
1931         break;
1932
1933     case VLC_VAR_FLOAT:
1934         val.f_float = atof( psz_value );
1935         break;
1936
1937     case VLC_VAR_STRING:
1938     case VLC_VAR_MODULE:
1939     case VLC_VAR_FILE:
1940     case VLC_VAR_DIRECTORY:
1941         val.psz_string = psz_value;
1942         break;
1943
1944     default:
1945         goto cleanup;
1946         break;
1947     }
1948
1949     var_Set( p_input, psz_name, val );
1950
1951     msg_Dbg( p_input, "set input option: %s to %s", psz_name, psz_value );
1952
1953   cleanup:
1954     if( psz_name ) free( psz_name );
1955     return;
1956 }
1957
1958 /*****************************************************************************
1959  * MRLSplit: parse the access, demux and url part of the
1960  *           Media Resource Locator.
1961  *****************************************************************************/
1962 static void MRLSplit( input_thread_t *p_input, char *psz_dup,
1963                       char **ppsz_access, char **ppsz_demux, char **ppsz_path )
1964 {
1965     char *psz_access = NULL;
1966     char *psz_demux  = NULL;
1967     char *psz_path   = NULL;
1968     char *psz;
1969
1970     psz = strchr( psz_dup, ':' );
1971
1972 #if defined( WIN32 ) || defined( UNDER_CE )
1973     if( psz - psz_dup == 1 )
1974     {
1975         msg_Warn( p_input, "drive letter %c: found in source string",
1976                   psz_dup[0] );
1977         psz_path = psz_dup;
1978     }
1979     else
1980 #endif
1981
1982     if( psz )
1983     {
1984         *psz++ = '\0';
1985         if( psz[0] == '/' && psz[1] == '/' )
1986             psz += 2;
1987
1988         psz_path = psz;
1989
1990         psz = strchr( psz_dup, '/' );
1991         if( psz )
1992         {
1993             *psz++ = '\0';
1994             psz_demux = psz;
1995         }
1996
1997         psz_access = psz_dup;
1998     }
1999     else
2000     {
2001         psz_path = psz_dup;
2002     }
2003
2004     if( psz_access == NULL )
2005         *ppsz_access = "";
2006     else
2007         *ppsz_access = psz_access;
2008
2009     if( psz_demux == NULL )
2010         *ppsz_demux = "";
2011     else
2012         *ppsz_demux = psz_demux;
2013
2014     if( psz_path == NULL )
2015         *ppsz_path = "";
2016     else
2017         *ppsz_path = psz_path;
2018 }
2019
2020 /*****************************************************************************
2021  * MRLSections: parse title and seekpoint info from the Media Resource Locator.
2022  *
2023  * Syntax:
2024  * [url][@[title-start][,chapter-start][-[title-end][,chapter-end]]]
2025  *****************************************************************************/
2026 static void MRLSections( input_thread_t *p_input, char *psz_source,
2027                          int *pi_title_start, int *pi_title_end,
2028                          int *pi_chapter_start, int *pi_chapter_end )
2029 {
2030     char *psz, *psz_end, *psz_next;
2031
2032     *pi_title_start = *pi_title_end = -1;
2033     *pi_chapter_start = *pi_chapter_end = -1;
2034
2035     /* Start by parsing titles and chapters */
2036     if( !psz_source || !( psz = strrchr( psz_source, '@' ) ) ) return;
2037
2038     *psz++ = 0;
2039
2040     /* Separate start and end */
2041     if( ( psz_end = strchr( psz, '-' ) ) ) *psz_end++ = 0;
2042
2043     /* Look for the start title */
2044     *pi_title_start = strtol( psz, &psz_next, 0 );
2045     if( !*pi_title_start && psz == psz_next ) *pi_title_start = -1;
2046     *pi_title_end = *pi_title_start;
2047     psz = psz_next;
2048
2049     /* Look for the start chapter */
2050     if( *psz ) psz++;
2051     *pi_chapter_start = strtol( psz, &psz_next, 0 );
2052     if( !*pi_chapter_start && psz == psz_next ) *pi_chapter_start = -1;
2053     *pi_chapter_end = *pi_chapter_start;
2054
2055     if( psz_end )
2056     {
2057         /* Look for the end title */
2058         *pi_title_end = strtol( psz_end, &psz_next, 0 );
2059         if( !*pi_title_end && psz_end == psz_next ) *pi_title_end = -1;
2060         psz_end = psz_next;
2061
2062         /* Look for the end chapter */
2063         if( *psz_end ) psz_end++;
2064         *pi_chapter_end = strtol( psz_end, &psz_next, 0 );
2065         if( !*pi_chapter_end && psz_end == psz_next ) *pi_chapter_end = -1;
2066     }
2067
2068     msg_Dbg( p_input, "source=`%s' title=%d/%d seekpoint=%d/%d",
2069              psz_source, *pi_title_start, *pi_chapter_start,
2070              *pi_title_end, *pi_chapter_end );
2071 }