]> git.sesse.net Git - vlc/blobdiff - src/input/input.c
* it's information, not informations (grep -r)
[vlc] / src / input / input.c
index 2374b9fac7890ba4878755f670e440bc64bccf7f..3cd6b71322ba47a334f439758aa841a2f78143af 100644 (file)
@@ -59,6 +59,17 @@ static void UpdateFromDemux( input_thread_t * );
 static void ParseOption( input_thread_t *p_input, const char *psz_option );
 
 static void DecodeUrl  ( char * );
+static void MRLSplit( input_thread_t *, char *, char **, char **, char ** );
+
+static input_source_t *InputSourceNew( input_thread_t *);
+static int  InputSourceInit( input_thread_t *, input_source_t *,
+                             char *, char *psz_forced_demux );
+static void InputSourceClean( input_thread_t *, input_source_t * );
+
+static void SlaveDemux( input_thread_t *p_input );
+static void SlaveSeek( input_thread_t *p_input );
+
+static vlc_meta_t *InputMetaUser( input_thread_t *p_input );
 
 /*****************************************************************************
  * input_CreateThread: creates a new input thread
@@ -75,10 +86,12 @@ static void DecodeUrl  ( char * );
  *  - title,title-next,title-prev
  *  - chapter,chapter-next, chapter-prev
  *  - program, audio-es, video-es, spu-es
+ *  - audio-delay, spu-delay
  *  - bookmark
  * * Get only:
  *  - length
  *  - bookmarks
+ *  - seekable (if you can seek, it doesn't say if 'bar display' has be shown or not, for that check position != 0.0)
  * * For intf callback upon changes
  *  - intf-change
  * TODO explain when Callback is called
@@ -129,10 +142,13 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
     p_input->input.b_eof = VLC_FALSE;
     p_input->input.i_cr_average = 0;
 
+    /* No slave */
+    p_input->i_slave = 0;
+    p_input->slave   = NULL;
+
     /* Init control buffer */
     vlc_mutex_init( p_input, &p_input->lock_control );
     p_input->i_control = 0;
-    p_input->p_sys = NULL;
 
     /* Parse input options */
     vlc_mutex_lock( &p_item->lock );
@@ -280,6 +296,10 @@ void input_DestroyThread( input_thread_t *p_input )
  * Run: main thread loop
  *****************************************************************************
  * Thread in charge of processing the network packets and demultiplexing.
+ *
+ * TODO:
+ *  read subtitle support (XXX take care of spu-delay in the right way).
+ *  multi-input support (XXX may be done with subs)
  *****************************************************************************/
 static int Run( input_thread_t *p_input )
 {
@@ -302,7 +322,7 @@ static int Run( input_thread_t *p_input )
     }
 
     /* Main loop */
-    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
+    while( !p_input->b_die && !p_input->b_error && !p_input->input.b_eof )
     {
         vlc_bool_t b_force_update = VLC_FALSE;
         int i_ret;
@@ -344,12 +364,12 @@ static int Run( input_thread_t *p_input )
                     /* End of file - we do not set b_die because only the
                      * playlist is allowed to do so. */
                     msg_Dbg( p_input, "EOF reached" );
-                    p_input->b_eof = VLC_TRUE;
                     p_input->input.b_eof = VLC_TRUE;
                 }
                 else
                 {
-                    msg_Dbg( p_input, "repeating the same input (%d)", repeat.i_int );
+                    msg_Dbg( p_input, "repeating the same input (%d)",
+                             repeat.i_int );
                     if( repeat.i_int > 0 )
                     {
                         repeat.i_int--;
@@ -377,6 +397,11 @@ static int Run( input_thread_t *p_input )
             {
                 p_input->b_error = VLC_TRUE;
             }
+
+            if( i_ret > 0 && p_input->i_slave > 0 )
+            {
+                SlaveDemux( p_input );
+            }
         }
         else
         {
@@ -402,18 +427,21 @@ static int Run( input_thread_t *p_input )
             double f_pos;
             int64_t i_time, i_length;
             /* update input status variables */
-            if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_POSITION, &f_pos ) )
+            if( !demux2_Control( p_input->input.p_demux,
+                                 DEMUX_GET_POSITION, &f_pos ) )
             {
                 val.f_float = (float)f_pos;
                 var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
             }
-            if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
+            if( !demux2_Control( p_input->input.p_demux,
+                                 DEMUX_GET_TIME, &i_time ) )
             {
                 p_input->i_time = i_time;
                 val.i_time = i_time;
                 var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
             }
-            if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH, &i_length ) )
+            if( !demux2_Control( p_input->input.p_demux,
+                                 DEMUX_GET_LENGTH, &i_length ) )
             {
                 vlc_value_t old_val;
                 var_Get( p_input, "length", &old_val );
@@ -422,17 +450,15 @@ static int Run( input_thread_t *p_input )
 
                 if( old_val.i_time != val.i_time )
                 {
-                    /* TODO */
-#if 0
                     char psz_buffer[MSTRTIME_MAX_SIZE];
 
-                    vlc_mutex_lock( &p_input->p_item->lock );
-                    p_input->p_item->i_duration = i_length;
-                    vlc_mutex_unlock( &p_input->p_item->lock );
+                    vlc_mutex_lock( &p_input->input.p_item->lock );
+                    p_input->input.p_item->i_duration = i_length;
+                    vlc_mutex_unlock( &p_input->input.p_item->lock );
 
-                    input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"),
-                       msecstotimestr( psz_buffer, i_length / 1000 ) );
-#endif
+                    input_Control( p_input, INPUT_ADD_INFO,
+                                   _("General"), _("Duration"),
+                    msecstotimestr( psz_buffer, i_length / 1000 ) );
                 }
             }
 
@@ -441,139 +467,33 @@ static int Run( input_thread_t *p_input )
         }
     }
 
-    /* Wait we are asked to die */
-    if( !p_input->b_die )
-    {
-        Error( p_input );
-    }
-
-    /* Clean up */
-    End( p_input );
-
-    return 0;
-
-#if 0
-    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
+    if( !p_input->b_eof && !p_input->b_error && p_input->input.b_eof )
     {
-        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
+        /* We have finish to demux data but not to play them */
+        while( !p_input->b_die )
         {
-            if( p_input->stream.p_selected_area->i_size > 0 )
-            {
-                unsigned int i;
-                mtime_t      i_time;
-                double f = (double)p_input->stream.p_selected_area->i_seek /
-                           (double)p_input->stream.p_selected_area->i_size;
-
-                vlc_mutex_unlock( &p_input->stream.stream_lock );
-                demux_Control( p_input, DEMUX_SET_POSITION, f );
-                vlc_mutex_lock( &p_input->stream.stream_lock );
-
-                /* Escape all decoders for the stream discontinuity they
-                 * will encounter. */
-                input_EscapeDiscontinuity( p_input );
-
-                for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
-                {
-                    pgrm_descriptor_t * p_pgrm=p_input->stream.pp_programs[i];
-
-                    /* Reinitialize synchro. */
-                    p_pgrm->i_synchro_state = SYNCHRO_REINIT;
-                }
-
-                vlc_mutex_unlock( &p_input->stream.stream_lock );
-                if( !demux_Control( p_input, DEMUX_GET_TIME, &i_time ) )
-                {
-                    int i;
-                    vlc_value_t val;
+            if( input_EsOutDecodersEmpty( p_input->p_es_out ) )
+                break;
 
-                    /* Help in bar display */
-                    val.i_time = i_time;
-                    var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
+            msg_Dbg( p_input, "waiting decoder fifos to empty" );
 
-                    /* Seek subs */
-                    for( i = 0; i < p_input->p_sys->i_sub; i++ )
-                    {
-                        subtitle_Seek( p_input->p_sys->sub[i], i_time );
-                    }
-                }
-                if( !demux_Control( p_input, DEMUX_GET_POSITION, &f ) )
-                {
-                    val.f_float = (float)f;
-                    var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
-                }
-                vlc_mutex_lock( &p_input->stream.stream_lock );
-            }
-            p_input->stream.p_selected_area->i_seek = NO_SEEK;
+            msleep( INPUT_IDLE_SLEEP );
         }
 
-        /* Read and demultiplex some data. */
-        i_count = p_input->pf_demux( p_input );
-
-        XXXXX
-
-        if( !p_input->b_error && !p_input->b_eof && i_update_next < mdate() )
-        {
-            int i;
-            mtime_t i_time;
-            mtime_t i_length;
-            double  d_pos;
-
-            /* update input status variables */
-            if( !demux_Control( p_input, DEMUX_GET_POSITION, &d_pos ) )
-            {
-                val.f_float = (float)d_pos;
-                var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
-            }
-            if( !demux_Control( p_input, DEMUX_GET_TIME, &i_time ) )
-            {
-                val.i_time = i_time;
-                var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
-            }
-            if( !demux_Control( p_input, DEMUX_GET_LENGTH, &i_length ) )
-            {
-                vlc_value_t old_val;
-                var_Get( p_input, "length", &old_val );
-                val.i_time = i_length;
-                var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
-                if( old_val.i_time != val.i_time )
-                {
-                    char psz_buffer[MSTRTIME_MAX_SIZE];
-
-                    vlc_mutex_lock( &p_input->p_item->lock );
-                    p_input->p_item->i_duration = i_length;
-                    vlc_mutex_unlock( &p_input->p_item->lock );
-
-                    input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"),
-                       msecstotimestr( psz_buffer, i_length / 1000 ) );
-                }
-            }
-
-            /* Check stop-time */
-            if( p_input->p_sys->i_stop_time > 0 && p_input->p_sys->i_stop_time < i_time )
-            {
-                msg_Warn( p_input, "EOF reached because of stop-time" );
-                p_input->b_eof = 1;
-            }
-
-            /* update subs */
-            for( i = 0; i < p_input->p_sys->i_sub; i++ )
-            {
-                subtitle_Demux( p_input->p_sys->sub[i], i_time );
-            }
-
-            i_update_next = mdate() + I64C(150000);
-        }
+        /* We have finished */
+        p_input->b_eof = VLC_TRUE;
     }
 
-    if( p_input->b_error || p_input->b_eof )
+    /* Wait we are asked to die */
+    if( !p_input->b_die )
     {
-        ErrorThread( p_input );
+        Error( p_input );
     }
 
-    EndThread( p_input );
+    /* Clean up */
+    End( p_input );
 
     return 0;
-#endif
 }
 
 /*****************************************************************************
@@ -581,51 +501,12 @@ static int Run( input_thread_t *p_input )
  *****************************************************************************/
 static int Init( input_thread_t * p_input )
 {
-    char *psz_dup = strdup( p_input->input.p_item->psz_uri );
-    char *psz_access = NULL;
-    char *psz_demux  = NULL;
-    char *psz_path   = NULL;
     char *psz;
+    char *psz_subtitle;
     vlc_value_t val;
-
-    /* Open access/stream/demux */
-    psz = strchr( psz_dup, ':' );
-#if defined( WIN32 ) || defined( UNDER_CE )
-    if( psz - psz_dup == 1 )
-    {
-        msg_Warn( p_input, "drive letter %c: found in source string", psz_dup[0] );
-    }
-    else
-#endif
-    if( psz )
-    {
-        *psz++ = '\0';
-        if( psz[0] == '/' && psz[1] == '/' )
-            psz += 2;
-
-        psz_path = psz;
-
-        psz = strchr( psz_dup, '/' );
-        if( psz )
-        {
-            *psz++ = '\0';
-            psz_demux = psz;
-        }
-
-        psz_access = psz_dup;
-    }
-    else
-    {
-        psz_path = psz_dup;
-    }
-
-    if( psz_access == NULL ) psz_access = "";
-    if( psz_demux == NULL )  psz_demux = "";
-    if( psz_path == NULL )   psz_path = "";
-
-    msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
-            p_input->input.p_item->psz_uri,
-             psz_access, psz_demux, psz_path );
+    double f_fps;
+    vlc_meta_t *p_meta, *p_meta_user;
+    int i;
 
     /* Initialize optional stream output. (before access/demuxer) */
     psz = var_GetString( p_input, "sout" );
@@ -636,8 +517,6 @@ static int Init( input_thread_t * p_input )
         {
             msg_Err( p_input, "cannot start stream output instance, aborting" );
             free( psz );
-            free( psz_dup );
-
             return VLC_EGENERIC;
         }
     }
@@ -648,139 +527,22 @@ static int Init( input_thread_t * p_input )
     es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_FALSE );
     es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
 
-    /* Try access_demux if no demux given */
-    if( *psz_access && *psz_demux == '\0' )
+    if( InputSourceInit( p_input, &p_input->input,
+                         p_input->input.p_item->psz_uri, NULL ) )
     {
-        p_input->input.p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
-                                             NULL, p_input->p_es_out );
+        goto error;
     }
 
-    if( p_input->input.p_demux )
-    {
-        /* Get infos from access_demux */
-        demux2_Control( p_input->input.p_demux,
-                        DEMUX_GET_PTS_DELAY, &p_input->i_pts_delay );
-        p_input->input.b_title_demux = VLC_TRUE;
-        if( demux2_Control( p_input->input.p_demux,
-                            DEMUX_GET_TITLE_INFO,
-                            &p_input->input.title, &p_input->input.i_title ) )
-        {
-            p_input->input.i_title = 0;
-            p_input->input.title   = NULL;
-        }
-        demux2_Control( p_input->input.p_demux, DEMUX_CAN_CONTROL_PACE,
-                        &p_input->input.b_can_pace_control );
-        demux2_Control( p_input->input.p_demux, DEMUX_CAN_PAUSE,
-                        &p_input->input.b_can_pause );
-    }
-    else
-    {
-        /* Now try a real access */
-        p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
-
-        /* Access failed, URL encoded ? */
-        if( p_input->input.p_access == NULL && strchr( psz_path, '%' ) )
-        {
-            DecodeUrl( psz_path );
-
-            msg_Dbg( p_input, "retying with access `%s' demux `%s' path `%s'",
-                     psz_access, psz_demux, psz_path );
-
-            p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
-        }
-#ifndef WIN32      /* Remove this gross hack from the win32 build as colons
-                        * are forbidden in filenames on Win32. */
-
-        /* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
-        if( p_input->input.p_access == NULL &&
-            *psz_access == '\0' && ( *psz_demux || *psz_path ) )
-        {
-            free( psz_dup );
-            psz_dup = strdup( p_input->input.p_item->psz_uri );
-            psz_access = "";
-            psz_demux = "";
-            psz_path = psz_dup;
-
-            p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
-        }
-#endif
-
-        if( p_input->input.p_access == NULL )
-        {
-            msg_Err( p_input, "no suitable access module for `%s'",
-                     p_input->input.p_item->psz_uri );
-            goto error;
-        }
-
-        /* Get infos from access */
-        access2_Control( p_input->input.p_access,
-                         ACCESS_GET_PTS_DELAY, &p_input->i_pts_delay );
-        p_input->input.b_title_demux = VLC_FALSE;
-        if( access2_Control( p_input->input.p_access,
-                             ACCESS_GET_TITLE_INFO,
-                             &p_input->input.title, &p_input->input.i_title ) )
-        {
-            p_input->input.i_title = 0;
-            p_input->input.title   = NULL;
-        }
-        access2_Control( p_input->input.p_access, ACCESS_CAN_CONTROL_PACE,
-                         &p_input->input.b_can_pace_control );
-        access2_Control( p_input->input.p_access, ACCESS_CAN_PAUSE,
-                         &p_input->input.b_can_pace_control );
-
-        /* Create the stream_t */
-        p_input->input.p_stream = stream_AccessNew( p_input->input.p_access );
-        if( p_input->input.p_stream == NULL )
-        {
-            msg_Warn( p_input, "cannot create a stream_t from access" );
-            goto error;
-        }
-
-        /* Open a demuxer */
-        if( *psz_demux == '\0' && *p_input->input.p_access->psz_demux )
-        {
-            psz_demux = p_input->input.p_access->psz_demux;
-        }
-        p_input->input.p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
-                                             p_input->input.p_stream,
-                                             p_input->p_es_out );
-        if( p_input->input.p_demux == NULL )
-        {
-            msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
-                     psz_access, psz_demux, psz_path );
-            goto error;
-        }
-
-        /* TODO get title from demux */
-        if( p_input->input.i_title <= 0 )
-        {
-            if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TITLE_INFO,
-                                &p_input->input.title, &p_input->input.i_title ) )
-            {
-                p_input->input.i_title = 0;
-                p_input->input.title   = NULL;
-            }
-            else
-            {
-                p_input->input.b_title_demux = VLC_TRUE;
-            }
-        }
-    }
-    /* Create global title (for now, just a copy) */
+    /* Create global title (from master) */
     p_input->i_title = p_input->input.i_title;
+    p_input->title   = p_input->input.title;
     if( p_input->i_title > 0 )
     {
-        int i;
-        p_input->title = malloc( sizeof( input_title_t *) * p_input->i_title );
-        for( i = 0; i < p_input->i_title; i++ )
-        {
-            p_input->title[i] = vlc_input_title_Duplicate( p_input->input.title[i] );
-        }
-
         /* Setup variables */
         input_ControlVarNavigation( p_input );
         input_ControlVarTitle( p_input, 0 );
     }
+
     /* Global flag */
     p_input->b_can_pace_control = p_input->input.b_can_pace_control;
     p_input->b_can_pause        = p_input->input.b_can_pause;
@@ -795,17 +557,10 @@ static int Init( input_thread_t * p_input )
     if( val.i_int < 0 )
         p_input->i_pts_delay -= (val.i_int * 1000);
 
-    /* Init input_thread_sys_t */
-    p_input->p_sys = malloc( sizeof( input_thread_sys_t ) );
-    p_input->p_sys->i_sub = 0;
-    p_input->p_sys->sub   = NULL;
-
-    /* TODO: check meta data from users */
-
-    /* TODO: get meta data from demuxer */
-
+    /* Load master infos */
     /* Init length */
-    if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH, &val.i_time ) && val.i_time > 0 )
+    if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH,
+                         &val.i_time ) && val.i_time > 0 )
     {
         var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
         /* TODO update playlist meta data */
@@ -841,8 +596,113 @@ static int Init( input_thread_t * p_input )
     }
 
 
-    /* TODO: do subtitle loading */
+    /* Load subtitles */
+    /* Get fps and set it if not already set */
+    if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_FPS, &f_fps ) &&
+        f_fps > 1.0 )
+    {
+        vlc_value_t fps;
 
+        if( var_Get( p_input, "sub-fps", &fps ) )
+        {
+            var_Create( p_input, "sub-fps", VLC_VAR_FLOAT| VLC_VAR_DOINHERIT );
+            var_SetFloat( p_input, "sub-fps", f_fps );
+        }
+    }
+
+    /* Look for and add subtitle files */
+    psz_subtitle = var_GetString( p_input, "sub-file" );
+    if( *psz_subtitle )
+    {
+        input_source_t *sub;
+        vlc_value_t count;
+        vlc_value_t list;
+
+        msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle );
+
+        var_Change( p_input, "spu-es", VLC_VAR_CHOICESCOUNT, &count, NULL );
+
+        /* */
+        sub = InputSourceNew( p_input );
+        if( !InputSourceInit( p_input, sub, psz_subtitle, "subtitle" ) )
+        {
+            TAB_APPEND( p_input->i_slave, p_input->slave, sub );
+
+            /* Select the ES */
+            if( !var_Change( p_input, "spu-es", VLC_VAR_GETLIST, &list, NULL ) )
+            {
+                if( count.i_int == 0 )
+                    count.i_int++;  /* if it was first one, there is disable too */
+
+                if( count.i_int < list.p_list->i_count )
+                {
+                    input_ControlPush( p_input, INPUT_CONTROL_SET_ES,
+                                       &list.p_list->p_values[count.i_int] );
+                }
+                var_Change( p_input, "spu-es", VLC_VAR_FREELIST, &list, NULL );
+            }
+        }
+    }
+
+    var_Get( p_input, "sub-autodetect-file", &val );
+    if( val.b_bool )
+    {
+        char *psz_autopath = var_GetString( p_input, "sub-autodetect-path" );
+        char **subs = subtitles_Detect( p_input, psz_autopath,
+                                        p_input->input.p_item->psz_uri );
+        input_source_t *sub;
+
+        for( i = 0; subs[i] != NULL; i++ )
+        {
+            if( strcmp( psz_subtitle, subs[i] ) )
+            {
+                sub = InputSourceNew( p_input );
+                if( !InputSourceInit( p_input, sub, subs[i], "subtitle" ) )
+                {
+                    TAB_APPEND( p_input->i_slave, p_input->slave, sub );
+                }
+            }
+            free( subs[i] );
+        }
+        free( subs );
+        free( psz_autopath );
+    }
+    free( psz_subtitle );
+
+    /* Look for slave */
+    psz = var_GetString( p_input, "input-slave" );
+    if( *psz )
+    {
+        char *psz_delim = strchr( psz, '#' );
+
+        for( ;; )
+        {
+            input_source_t *slave;
+
+            if( psz_delim )
+            {
+                *psz_delim++ = '\0';
+            }
+
+            if( *psz == '\0' )
+            {
+                if( psz_delim )
+                    continue;
+                else
+                    break;
+            }
+
+            msg_Dbg( p_input, "adding slave '%s'", psz );
+            slave = InputSourceNew( p_input );
+            if( !InputSourceInit( p_input, slave, psz, NULL ) )
+            {
+                TAB_APPEND( p_input->i_slave, p_input->slave, slave );
+            }
+            if( !psz_delim )
+                break;
+        }
+    }
+    free( psz );
 
     /* Set up es_out */
     es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE );
@@ -854,15 +714,6 @@ static int Init( input_thread_t * p_input )
     es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE,
                     val.b_bool ? ES_OUT_MODE_ALL : ES_OUT_MODE_AUTO );
 
-    /* TODO select forced subs */
-#if 0
-    if( p_sub_toselect )
-    {
-        es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
-                        p_sub_toselect->p_es, VLC_TRUE );
-    }
-#endif
-
     if( p_input->p_sout )
     {
         if( p_input->p_sout->i_out_pace_nocontrol > 0 )
@@ -877,146 +728,74 @@ static int Init( input_thread_t * p_input )
                  p_input->b_out_pace_control ? "asynch" : "synch" );
     }
 
-    msg_Dbg( p_input, "`%s' sucessfully opened",
-             p_input->input.p_item->psz_uri );
-
-    /* initialization is complete */
-    p_input->i_state = PLAYING_S;
-
-    val.i_int = PLAYING_S;
-    var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
-
-    return VLC_SUCCESS;
-
-error:
-    if( p_input->input.p_demux )
-        demux2_Delete( p_input->input.p_demux );
-
-    if( p_input->input.p_stream )
-        stream_AccessDelete( p_input->input.p_stream );
-
-    if( p_input->input.p_access )
-        access2_Delete( p_input->input.p_access );
-
-    if( p_input->p_es_out )
-        input_EsOutDelete( p_input->p_es_out );
-
-    if( p_input->p_sout )
-        sout_DeleteInstance( p_input->p_sout );
-
-    /* Mark them deleted */
-    p_input->input.p_demux = NULL;
-    p_input->input.p_stream = NULL;
-    p_input->input.p_access = NULL;
-    p_input->p_es_out = NULL;
-    p_input->p_sout = NULL;
-
-    return VLC_EGENERIC;
-
-#if 0
-    vlc_meta_t *p_meta = NULL, *p_meta_user = NULL;
-//    float f_fps;
-    double f_fps;
-    mtime_t i_length;
+    /* Get meta data from users */
+    p_meta_user = InputMetaUser( p_input );
 
-    FIXME
-    p_input->input.i_cr_average = config_GetInt( p_input, "cr-average" );
-    p_input->stream.control.i_status = INIT_S;
-    p_input->stream.control.i_rate = DEFAULT_RATE;
+    /* Get meta data from master input */
+    if( demux2_Control( p_input->input.p_demux, DEMUX_GET_META, &p_meta ) )
+        p_meta = NULL;
 
-
-    /* Init input_thread_sys_t */
-    p_input->p_sys = malloc( sizeof( input_thread_sys_t ) );
-    p_input->p_sys->i_sub = 0;
-    p_input->p_sys->sub   = NULL;
-
-    /* Get meta information from user */
-    var_Create( p_input, "meta-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
-    var_Create( p_input, "meta-author", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
-    var_Create( p_input, "meta-artist", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
-    var_Create( p_input, "meta-genre", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
-    var_Create( p_input, "meta-copyright", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
-    var_Create( p_input, "meta-description", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
-    var_Create( p_input, "meta-date", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
-    var_Create( p_input, "meta-url", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
-    if( (p_meta_user = vlc_meta_New()) )
+    /* Merge them */
+    if( p_meta == NULL )
     {
-        vlc_value_t val;
-
-        var_Get( p_input, "meta-title", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_TITLE, val.psz_string );
-        free( val.psz_string );
-        var_Get( p_input, "meta-author", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_AUTHOR, val.psz_string );
-        free( val.psz_string );
-        var_Get( p_input, "meta-artist", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_ARTIST, val.psz_string );
-        free( val.psz_string );
-        var_Get( p_input, "meta-genre", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_GENRE, val.psz_string );
-        free( val.psz_string );
-        var_Get( p_input, "meta-copyright", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_COPYRIGHT, val.psz_string );
-        free( val.psz_string );
-        var_Get( p_input, "meta-description", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_DESCRIPTION, val.psz_string );
-        free( val.psz_string );
-        var_Get( p_input, "meta-date", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_DATE, val.psz_string );
-        free( val.psz_string );
-        var_Get( p_input, "meta-url", &val );
-        if( *val.psz_string )
-            vlc_meta_Add( p_meta_user, VLC_META_URL, val.psz_string );
-        free( val.psz_string );
+        p_meta = p_meta_user;
     }
-
-    /* Get meta informations from demuxer */
-    if( !demux_Control( p_input, DEMUX_GET_META, &p_meta ) ||
-        ( p_meta_user && p_meta_user->i_meta ) )
+    else if( p_meta_user )
     {
-        int i;
+        vlc_meta_Merge( p_meta, p_meta_user );
+        vlc_meta_Delete( p_meta_user );
+    }
 
-        /* Merge demux and user metadata */
-        if( !p_meta ){ p_meta = p_meta_user; p_meta_user = NULL; }
-        else if( p_meta && p_meta_user ) vlc_meta_Merge( p_meta, p_meta_user );
+    /* Get meta data from slave input */
+    for( i = 0; i < p_input->i_slave; i++ )
+    {
+        vlc_meta_t *p_meta_slave;
 
-        msg_Dbg( p_input, "meta informations:" );
-        if( p_meta->i_meta > 0 )
+        if( !demux2_Control( p_input->slave[i]->p_demux, DEMUX_GET_META, &p_meta_slave ) )
         {
-            for( i = 0; i < p_meta->i_meta; i++ )
+            if( p_meta == NULL )
+            {
+                p_meta = p_meta_slave;
+            }
+            else if( p_meta_slave )
             {
-                msg_Dbg( p_input, "  - '%s' = '%s'", _(p_meta->name[i]),
-                         p_meta->value[i] );
-                if( !strcmp( p_meta->name[i], VLC_META_TITLE ) &&
-                    p_meta->value[i] )
-                    input_Control( p_input, INPUT_SET_NAME, p_meta->value[i] );
-
-                if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
-                    input_Control( p_input, INPUT_ADD_INFO, _("General"),
-                                   _("Author"), p_meta->value[i] );
-
-                input_Control( p_input, INPUT_ADD_INFO, _("File"),
-                              _(p_meta->name[i]), "%s", p_meta->value[i] );
+                vlc_meta_Merge( p_meta, p_meta_slave );
+                vlc_meta_Delete( p_meta_slave );
             }
         }
+    }
+
+    if( p_meta && p_meta->i_meta > 0 )
+    {
+        msg_Dbg( p_input, "meta information:" );
+        for( i = 0; i < p_meta->i_meta; i++ )
+        {
+            msg_Dbg( p_input, "  - '%s' = '%s'",
+                    _(p_meta->name[i]), p_meta->value[i] );
+
+            if( !strcmp(p_meta->name[i], VLC_META_TITLE) && p_meta->value[i] )
+                input_Control( p_input, INPUT_SET_NAME, p_meta->value[i] );
+
+            if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
+                input_Control( p_input, INPUT_ADD_INFO, _("General"),
+                               _("Author"), p_meta->value[i] );
+
+            input_Control( p_input, INPUT_ADD_INFO, _("Meta-information"),
+                          _(p_meta->name[i]), "%s", p_meta->value[i] );
+        }
+
         for( i = 0; i < p_meta->i_track; i++ )
         {
             vlc_meta_t *tk = p_meta->track[i];
             int j;
 
-            msg_Dbg( p_input, "  - track[%d]:", i );
             if( tk->i_meta > 0 )
             {
                 char *psz_cat = malloc( strlen(_("Stream")) + 10 );
-                sprintf( psz_cat, "%s %d", _("Stream"), i );
 
+                msg_Dbg( p_input, "  - track[%d]:", i );
+
+                sprintf( psz_cat, "%s %d", _("Stream"), i );
                 for( j = 0; j < tk->i_meta; j++ )
                 {
                     msg_Dbg( p_input, "     - '%s' = '%s'", _(tk->name[j]),
@@ -1028,117 +807,42 @@ error:
             }
         }
 
-        if( p_input->stream.p_sout && p_input->stream.p_sout->p_meta == NULL )
+        if( p_input->p_sout && p_input->p_sout->p_meta == NULL )
         {
-            p_input->stream.p_sout->p_meta = p_meta;
+            p_input->p_sout->p_meta = p_meta;
         }
         else
         {
             vlc_meta_Delete( p_meta );
         }
     }
-    if( p_meta_user ) vlc_meta_Delete( p_meta_user );
 
-    /* Get length */
-    if( !demux_Control( p_input, DEMUX_GET_LENGTH, &i_length ) &&
-        i_length > 0 )
-    {
-        char psz_buffer[MSTRTIME_MAX_SIZE];
-
-        vlc_mutex_lock( &p_input->p_item->lock );
-        p_input->p_item->i_duration = i_length;
-        vlc_mutex_unlock( &p_input->p_item->lock );
-
-        input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"),
-                       msecstotimestr( psz_buffer, i_length / 1000 ) );
-
-        /* Set start time */
-        var_Get( p_input, "start-time", &val );
-        if(  val.i_int > 0 )
-        {
-            double f_pos = val.i_int * I64C(1000000) / (double)i_length;
+    msg_Dbg( p_input, "`%s' sucessfully opened",
+             p_input->input.p_item->psz_uri );
 
-            if( f_pos >= 1.0 )
-            {
-                msg_Warn( p_input, "invalid start-time, ignored (start-time "
-                          ">= media length)" );
-            }
-            else
-            {
-                p_input->stream.p_selected_area->i_seek =
-                    (int64_t)( f_pos * (double)p_input->stream.p_selected_area->i_size );
+    /* initialization is complete */
+    p_input->i_state = PLAYING_S;
 
-                msg_Dbg( p_input, "start-time %ds (%2.2f)", val.i_int, f_pos );
-            }
-        }
-    }
+    val.i_int = PLAYING_S;
+    var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
 
-    /* Get fps */
-    if( demux_Control( p_input, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
-    {
-        i_microsecondperframe = 0;
-    }
-    else
-    {
-        i_microsecondperframe = (int64_t)( (double)1000000.0 / (double)f_fps );
-    }
+    return VLC_SUCCESS;
 
-    /* Look for and add subtitle files */
-    var_Get( p_input, "sub-file", &val );
-    if( val.psz_string && *val.psz_string )
-    {
-        subtitle_demux_t *p_sub;
+error:
+    if( p_input->p_es_out )
+        input_EsOutDelete( p_input->p_es_out );
 
-        msg_Dbg( p_input, "force subtitle: %s", val.psz_string );
-        if( ( p_sub = subtitle_New( p_input, strdup(val.psz_string),
-                                    i_microsecondperframe ) ) )
-        {
-            p_sub_toselect = p_sub;
-            TAB_APPEND( p_input->p_sys->i_sub, p_input->p_sys->sub, p_sub );
-        }
-    }
-    psz_sub_file = val.psz_string;
+    if( p_input->p_sout )
+        sout_DeleteInstance( p_input->p_sout );
 
-    var_Get( p_input, "sub-autodetect-file", &val );
-    var_Get( p_input, "sub-autodetect-path", &val1 );
-    if( val.b_bool )
-    {
-        subtitle_demux_t *p_sub;
-        int i;
-        char **tmp = subtitles_Detect( p_input, val1.psz_string, p_input->psz_name );
-        char **tmp2 = tmp;
-        for( i = 0; *tmp2 != NULL; i++ )
-        {
-            if( psz_sub_file == NULL || strcmp( psz_sub_file, *tmp2 ) )
-            {
-                if( ( p_sub = subtitle_New( p_input, *tmp2,
-                                            i_microsecondperframe ) ) )
-                {
-                    TAB_APPEND( p_input->p_sys->i_sub, p_input->p_sys->sub,
-                                p_sub );
-                }
-            }
-            free( *tmp2++ );
-        }
-        free( tmp );
-        free( val1.psz_string );
-    }
-    if( psz_sub_file ) free( psz_sub_file );
+    /* Mark them deleted */
+    p_input->input.p_demux = NULL;
+    p_input->input.p_stream = NULL;
+    p_input->input.p_access = NULL;
+    p_input->p_es_out = NULL;
+    p_input->p_sout = NULL;
 
-    es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE );
-    val.b_bool =  VLC_FALSE;
-    if( p_input->stream.p_sout )
-    {
-        var_Get( p_input, "sout-all", &val );
-    }
-    es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE,
-                    val.b_bool ? ES_OUT_MODE_ALL : ES_OUT_MODE_AUTO );
-    if( p_sub_toselect )
-    {
-        es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
-                        p_sub_toselect->p_es, VLC_TRUE );
-    }
-#endif
+    return VLC_EGENERIC;
 }
 
 /*****************************************************************************
@@ -1161,6 +865,7 @@ static void Error( input_thread_t *p_input )
 static void End( input_thread_t * p_input )
 {
     vlc_value_t val;
+    int i;
 
     msg_Dbg( p_input, "closing `%s'",
              p_input->input.p_item->psz_uri );
@@ -1174,16 +879,18 @@ static void End( input_thread_t * p_input )
     /* Clean control variables */
     input_ControlVarClean( p_input );
 
-    /* Unload all modules */
-    if( p_input->input.p_demux )
-        demux2_Delete( p_input->input.p_demux );
-
-    if( p_input->input.p_stream )
-        stream_AccessDelete( p_input->input.p_stream );
+    /* Clean up master */
+    InputSourceClean( p_input, &p_input->input );
 
-    if( p_input->input.p_access )
-        access2_Delete( p_input->input.p_access );
+    /* Delete slave */
+    for( i = 0; i < p_input->i_slave; i++ )
+    {
+        InputSourceClean( p_input, p_input->slave[i] );
+        free( p_input->slave[i] );
+    }
+    if( p_input->slave ) free( p_input->slave );
 
+    /* Unload all modules */
     if( p_input->p_es_out )
         input_EsOutDelete( p_input->p_es_out );
 
@@ -1210,26 +917,6 @@ static void End( input_thread_t * p_input )
             vlc_object_release( p_pl );
     }
 
-    /* TODO subs */
-#if 0
-    /* Destroy subtitles demuxers */
-    if( p_input->p_sys )
-    {
-        for( i = 0; i < p_input->p_sys->i_sub; i++ )
-        {
-            subtitle_Close( p_input->p_sys->sub[i] );
-        }
-        if( p_input->p_sys->i_sub > 0 )
-        {
-            free( p_input->p_sys->sub );
-        }
-
-    }
-#endif
-
-    /* Free input_thread_sys_t */
-    free( p_input->p_sys );
-
     /* Tell we're dead */
     p_input->b_dead = VLC_TRUE;
 }
@@ -1272,7 +959,8 @@ static void ControlReduce( input_thread_t *p_input )
         const int i_ct = p_input->control[i].i_type;
 
         /* XXX We can't merge INPUT_CONTROL_SET_ES */
-        msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->i_control, i_lt, i_ct );
+        msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->i_control,
+                 i_lt, i_ct );
         if( i_lt == i_ct &&
             ( i_ct == INPUT_CONTROL_SET_STATE ||
               i_ct == INPUT_CONTROL_SET_RATE ||
@@ -1302,7 +990,8 @@ static void ControlReduce( input_thread_t *p_input )
     }
 }
 
-static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val )
+static vlc_bool_t Control( input_thread_t *p_input, int i_type,
+                           vlc_value_t val )
 {
     vlc_bool_t b_force_update = VLC_FALSE;
 
@@ -1331,18 +1020,23 @@ static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val
             else
             {
                 /* Should not fail */
-                demux2_Control( p_input->input.p_demux, DEMUX_GET_POSITION, &f_pos );
+                demux2_Control( p_input->input.p_demux,
+                                DEMUX_GET_POSITION, &f_pos );
                 f_pos += val.f_float;
             }
             if( f_pos < 0.0 ) f_pos = 0.0;
             if( f_pos > 1.0 ) f_pos = 1.0;
-            if( demux2_Control( p_input->input.p_demux, DEMUX_SET_POSITION, f_pos ) )
+            if( demux2_Control( p_input->input.p_demux, DEMUX_SET_POSITION,
+                                f_pos ) )
             {
-                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) %2.1f%% failed",
-                         f_pos * 100 );
+                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
+                         "%2.1f%% failed", f_pos * 100 );
             }
             else
             {
+                if( p_input->i_slave > 0 )
+                    SlaveSeek( p_input );
+
                 input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
                 es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
                 b_force_update = VLC_TRUE;
@@ -1386,11 +1080,14 @@ static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val
             }
             if( i_ret )
             {
-                msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) %lld failed",
-                         i_time );
+                msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) "I64Fd
+                         " failed", i_time );
             }
             else
             {
+                if( p_input->i_slave > 0 )
+                    SlaveSeek( p_input );
+
                 input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
 
                 es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
@@ -1428,7 +1125,8 @@ static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val
                 /* Reset clock */
                 es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
             }
-            else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S && p_input->b_can_pause )
+            else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S &&
+                     p_input->b_can_pause )
             {
                 int i_ret;
                 if( p_input->input.p_access )
@@ -1492,7 +1190,8 @@ static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val
                 i_rate = INPUT_RATE_MAX;
             }
             if( i_rate != INPUT_RATE_DEFAULT &&
-                ( !p_input->b_can_pace_control || !p_input->b_out_pace_control ) )
+                ( !p_input->b_can_pace_control ||
+                  !p_input->b_out_pace_control ) )
             {
                 msg_Dbg( p_input, "cannot change rate" );
                 i_rate = INPUT_RATE_DEFAULT;
@@ -1523,8 +1222,21 @@ static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val
 
         case INPUT_CONTROL_SET_ES:
             /* No need to force update, es_out does it if needed */
-            es_out_Control( p_input->p_es_out,
-                            ES_OUT_SET_ES, input_EsOutGetFromID( p_input->p_es_out, val.i_int ) );
+            es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
+                            input_EsOutGetFromID( p_input->p_es_out,
+                                                  val.i_int ) );
+            break;
+
+        case INPUT_CONTROL_SET_AUDIO_DELAY:
+            input_EsOutSetDelay( p_input->p_es_out,
+                                 AUDIO_ES, val.i_time );
+            var_Change( p_input, "audio-delay", VLC_VAR_SETVALUE, &val, NULL );
+            break;
+
+        case INPUT_CONTROL_SET_SPU_DELAY:
+            input_EsOutSetDelay( p_input->p_es_out,
+                                 SPU_ES, val.i_time );
+            var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, &val, NULL );
             break;
 
         case INPUT_CONTROL_SET_TITLE:
@@ -1588,13 +1300,13 @@ static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val
 
                 if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
                     i_seekpoint = p_demux->info.i_seekpoint - 1;
-                else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
+                else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
                     i_seekpoint = p_demux->info.i_seekpoint + 1;
                 else
                     i_seekpoint = val.i_int;
 
-                if( i_seekpoint >= 0 &&
-                    i_seekpoint < p_input->input.title[p_demux->info.i_title]->i_seekpoint )
+                if( i_seekpoint >= 0 && i_seekpoint <
+                    p_input->input.title[p_demux->info.i_title]->i_seekpoint )
                 {
                     demux2_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint );
 
@@ -1614,8 +1326,8 @@ static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val
                 else
                     i_seekpoint = val.i_int;
 
-                if( i_seekpoint >= 0 &&
-                    i_seekpoint < p_input->input.title[p_access->info.i_title]->i_seekpoint )
+                if( i_seekpoint >= 0 && i_seekpoint <
+                    p_input->input.title[p_access->info.i_title]->i_seekpoint )
                 {
                     access2_Control( p_access, ACCESS_SET_SEEKPOINT, i_seekpoint );
                     stream_AccessReset( p_input->input.p_stream );
@@ -1689,6 +1401,329 @@ static void UpdateFromAccess( input_thread_t *p_input )
     p_access->info.i_update &= ~INPUT_UPDATE_SIZE;
 }
 
+/*****************************************************************************
+ * InputSourceNew:
+ *****************************************************************************/
+static input_source_t *InputSourceNew( input_thread_t *p_input )
+{
+    input_source_t *in = malloc( sizeof( input_source_t ) );
+
+    in->p_item   = NULL;
+    in->p_access = NULL;
+    in->p_stream = NULL;
+    in->p_demux  = NULL;
+    in->b_title_demux = VLC_FALSE;
+    in->i_title  = 0;
+    in->title    = NULL;
+    in->b_can_pace_control = VLC_TRUE;
+    in->b_eof = VLC_FALSE;
+    in->i_cr_average = 0;
+
+    return in;
+}
+
+/*****************************************************************************
+ * InputSourceInit:
+ *****************************************************************************/
+static int InputSourceInit( input_thread_t *p_input,
+                            input_source_t *in, char *psz_mrl,
+                            char *psz_forced_demux )
+{
+    char *psz_dup = strdup( psz_mrl );
+    char *psz_access;
+    char *psz_demux;
+    char *psz_path;
+    vlc_value_t val;
+
+    /* Split uri */
+    MRLSplit( p_input, psz_dup, &psz_access, &psz_demux, &psz_path );
+
+    msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
+             psz_mrl, psz_access, psz_demux, psz_path );
+
+    if( psz_forced_demux && *psz_forced_demux )
+        psz_demux = psz_forced_demux;
+
+    /* Try access_demux if no demux given */
+    if( *psz_access && *psz_demux == '\0' )
+    {
+        in->p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
+                                  NULL, p_input->p_es_out );
+    }
+
+    if( in->p_demux )
+    {
+        int64_t i_pts_delay;
+
+        /* Get infos from access_demux */
+        demux2_Control( in->p_demux,
+                        DEMUX_GET_PTS_DELAY, &i_pts_delay );
+        p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );
+
+        in->b_title_demux = VLC_TRUE;
+        if( demux2_Control( in->p_demux,
+                            DEMUX_GET_TITLE_INFO,
+                            &in->title, &in->i_title ) )
+        {
+            in->i_title = 0;
+            in->title   = NULL;
+        }
+        demux2_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE,
+                        &in->b_can_pace_control );
+        demux2_Control( in->p_demux, DEMUX_CAN_PAUSE,
+                        &in->b_can_pause );
+
+        /* FIXME todo
+        demux2_Control( in->p_demux, DEMUX_CAN_SEEK,
+                        &val.b_bool );
+        */
+    }
+    else
+    {
+        int64_t i_pts_delay;
+
+        /* Now try a real access */
+        in->p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
+
+        /* Access failed, URL encoded ? */
+        if( in->p_access == NULL && strchr( psz_path, '%' ) )
+        {
+            DecodeUrl( psz_path );
+
+            msg_Dbg( p_input, "retying with access `%s' demux `%s' path `%s'",
+                     psz_access, psz_demux, psz_path );
+
+            in->p_access = access2_New( p_input,
+                                        psz_access, psz_demux, psz_path );
+        }
+#ifndef WIN32      /* Remove this gross hack from the win32 build as colons
+                        * are forbidden in filenames on Win32. */
+
+        /* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
+        if( in->p_access == NULL &&
+            *psz_access == '\0' && ( *psz_demux || *psz_path ) )
+        {
+            free( psz_dup );
+            psz_dup = strdup( psz_mrl );
+            psz_access = "";
+            psz_demux = "";
+            psz_path = psz_dup;
+
+            in->p_access = access2_New( p_input,
+                                        psz_access, psz_demux, psz_path );
+        }
+#endif
+
+        if( in->p_access == NULL )
+        {
+            msg_Err( p_input, "no suitable access module for `%s'", psz_mrl );
+            goto error;
+        }
+
+        /* Get infos from access */
+        access2_Control( in->p_access,
+                         ACCESS_GET_PTS_DELAY, &i_pts_delay );
+        p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );
+
+        in->b_title_demux = VLC_FALSE;
+        if( access2_Control( in->p_access,
+                             ACCESS_GET_TITLE_INFO,
+                             &in->title, &in->i_title ) )
+        {
+            in->i_title = 0;
+            in->title   = NULL;
+        }
+        access2_Control( in->p_access, ACCESS_CAN_CONTROL_PACE,
+                         &in->b_can_pace_control );
+        access2_Control( in->p_access, ACCESS_CAN_PAUSE,
+                         &in->b_can_pause );
+        access2_Control( in->p_access, ACCESS_CAN_SEEK,
+                         &val.b_bool );
+        var_Set( p_input, "seekable", val );
+
+        /* Create the stream_t */
+        in->p_stream = stream_AccessNew( in->p_access );
+        if( in->p_stream == NULL )
+        {
+            msg_Warn( p_input, "cannot create a stream_t from access" );
+            goto error;
+        }
+
+        /* Open a demuxer */
+        if( *psz_demux == '\0' && *in->p_access->psz_demux )
+        {
+            psz_demux = in->p_access->psz_demux;
+        }
+        in->p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
+                                  in->p_stream, p_input->p_es_out );
+        if( in->p_demux == NULL )
+        {
+            msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
+                     psz_access, psz_demux, psz_path );
+            goto error;
+        }
+
+        /* TODO get title from demux */
+        if( in->i_title <= 0 )
+        {
+            if( demux2_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
+                                &in->title, &in->i_title ) )
+            {
+                in->i_title = 0;
+                in->title   = NULL;
+            }
+            else
+            {
+                in->b_title_demux = VLC_TRUE;
+            }
+        }
+    }
+    free( psz_dup );
+    return VLC_SUCCESS;
+
+error:
+    if( in->p_demux )
+        demux2_Delete( in->p_demux );
+
+    if( in->p_stream )
+        stream_AccessDelete( in->p_stream );
+
+    if( in->p_access )
+        access2_Delete( in->p_access );
+    free( psz_dup );
+
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * InputSourceClean:
+ *****************************************************************************/
+static void InputSourceClean( input_thread_t *p_input, input_source_t *in )
+{
+    if( in->p_demux )
+        demux2_Delete( in->p_demux );
+
+    if( in->p_stream )
+        stream_AccessDelete( in->p_stream );
+
+    if( in->p_access )
+        access2_Delete( in->p_access );
+
+    if( in->i_title > 0 )
+    {
+        int i;
+        for( i = 0; i < in->i_title; i++ )
+        {
+            vlc_input_title_Delete( in->title[i] );
+        }
+        free( in->title );
+    }
+}
+
+static void SlaveDemux( input_thread_t *p_input )
+{
+    int64_t i_time;
+    int i;
+    if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
+    {
+        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
+        return;
+    }
+
+    for( i = 0; i < p_input->i_slave; i++ )
+    {
+        input_source_t *in = p_input->slave[i];
+        int i_ret = 1;
+
+        if( in->b_eof )
+            continue;
+
+        if( demux2_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
+        {
+            for( ;; )
+            {
+                int64_t i_stime;
+                if( demux2_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) )
+                {
+                    msg_Err( p_input, "slave[%d] doesn't like "
+                             "DEMUX_GET_TIME -> EOF", i );
+                    i_ret = 0;
+                    break;
+                }
+
+                if( i_stime >= i_time )
+                    break;
+
+                if( ( i_ret = in->p_demux->pf_demux( in->p_demux ) ) <= 0 )
+                    break;
+            }
+        }
+        else
+        {
+            i_ret = in->p_demux->pf_demux( in->p_demux );
+        }
+
+        if( i_ret <= 0 )
+        {
+            msg_Dbg( p_input, "slave %d EOF", i );
+            in->b_eof = VLC_TRUE;
+        }
+    }
+}
+
+static void SlaveSeek( input_thread_t *p_input )
+{
+    int64_t i_time;
+    int i;
+
+    if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
+    {
+        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
+        return;
+    }
+
+    for( i = 0; i < p_input->i_slave; i++ )
+    {
+        input_source_t *in = p_input->slave[i];
+
+        if( demux2_Control( in->p_demux, DEMUX_SET_TIME, i_time ) )
+        {
+            msg_Err( p_input, "seek failed for slave %d -> EOF", i );
+            in->b_eof = VLC_TRUE;
+        }
+    }
+}
+/*****************************************************************************
+ * InputMetaUser:
+ *****************************************************************************/
+static vlc_meta_t *InputMetaUser( input_thread_t *p_input )
+{
+    vlc_meta_t *p_meta;
+    vlc_value_t val;
+
+    if( ( p_meta = vlc_meta_New() ) == NULL )
+        return NULL;
+
+    /* Get meta information from user */
+#define GET_META( c, s ) \
+    var_Get( p_input, (s), &val );  \
+    if( *val.psz_string )       \
+        vlc_meta_Add( p_meta, c, val.psz_string ); \
+    free( val.psz_string )
+
+    GET_META( VLC_META_TITLE, "meta-title" );
+    GET_META( VLC_META_AUTHOR, "meta-author" );
+    GET_META( VLC_META_ARTIST, "meta-artist" );
+    GET_META( VLC_META_GENRE, "meta-genre" );
+    GET_META( VLC_META_COPYRIGHT, "meta-copyright" );
+    GET_META( VLC_META_DESCRIPTION, "meta-description" );
+    GET_META( VLC_META_DATE, "meta-date" );
+    GET_META( VLC_META_URL, "meta-url" );
+#undef GET_META
+
+    return p_meta;
+}
+
 /*****************************************************************************
  * DecodeUrl: decode a given encoded url
  *****************************************************************************/
@@ -1825,4 +1860,60 @@ static void ParseOption( input_thread_t *p_input, const char *psz_option )
     return;
 }
 
+static void MRLSplit( input_thread_t *p_input, char *psz_dup,
+                      char **ppsz_access, char **ppsz_demux, char **ppsz_path )
+{
+    char *psz_access = NULL;
+    char *psz_demux  = NULL;
+    char *psz_path   = NULL;
+    char *psz;
+
+    psz = strchr( psz_dup, ':' );
+
+#if defined( WIN32 ) || defined( UNDER_CE )
+    if( psz - psz_dup == 1 )
+    {
+        msg_Warn( p_input, "drive letter %c: found in source string",
+                  psz_dup[0] );
+        psz_path = psz_dup;
+    }
+    else
+#endif
+
+    if( psz )
+    {
+        *psz++ = '\0';
+        if( psz[0] == '/' && psz[1] == '/' )
+            psz += 2;
+
+        psz_path = psz;
+
+        psz = strchr( psz_dup, '/' );
+        if( psz )
+        {
+            *psz++ = '\0';
+            psz_demux = psz;
+        }
+
+        psz_access = psz_dup;
+    }
+    else
+    {
+        psz_path = psz_dup;
+    }
+
+    if( psz_access == NULL )
+        *ppsz_access = "";
+    else
+        *ppsz_access = psz_access;
+
+    if( psz_demux == NULL )
+        *ppsz_demux = "";
+    else
+        *ppsz_demux = psz_demux;
 
+    if( psz_path == NULL )
+        *ppsz_path = "";
+    else
+        *ppsz_path = psz_path;
+}