]> git.sesse.net Git - vlc/blobdiff - src/input/input.c
Remove uninitialized and unsynchronized global stats
[vlc] / src / input / input.c
index 8c6d25a6beeeabcb9a705a45e296c5a6a5fa1139..7d05ae2f1d105ac19673d0d35292028d33253635 100644 (file)
@@ -43,6 +43,7 @@
 #include "demux.h"
 #include "stream.h"
 #include "item.h"
+#include "ressource.h"
 
 #include <vlc_sout.h>
 #include "../stream_output/stream_output.h"
@@ -65,15 +66,14 @@ static  void *Run            ( vlc_object_t *p_this );
 static  void *RunAndDestroy  ( vlc_object_t *p_this );
 
 static input_thread_t * Create  ( vlc_object_t *, input_item_t *,
-                                  const char *, bool, sout_instance_t * );
+                                  const char *, bool, input_ressource_t * );
 static  int             Init    ( input_thread_t *p_input );
-static void             WaitDie   ( input_thread_t *p_input );
 static void             End     ( input_thread_t *p_input );
 static void             MainLoop( input_thread_t *p_input );
 
 static void ObjectKillChildrens( input_thread_t *, vlc_object_t * );
 
-static inline int ControlPopNoLock( input_thread_t *, int *, vlc_value_t *, mtime_t i_deadline );
+static inline int ControlPop( input_thread_t *, int *, vlc_value_t *, mtime_t i_deadline );
 static void       ControlReduce( input_thread_t * );
 static bool Control( input_thread_t *, int, vlc_value_t );
 
@@ -98,8 +98,9 @@ static void SlaveSeek( input_thread_t *p_input );
 
 static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta );
 static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta );
-static char *InputGetExtraFiles( input_thread_t *p_input,
-                                 const char *psz_access, const char *psz_path );
+static void InputGetExtraFiles( input_thread_t *p_input,
+                                int *pi_list, char ***pppsz_list,
+                                const char *psz_access, const char *psz_path );
 
 static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
                               int i_new, input_attachment_t **pp_new );
@@ -116,7 +117,7 @@ static void input_ChangeState( input_thread_t *p_input, int i_state ); /* TODO f
  *****************************************************************************/
 static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
                                const char *psz_header, bool b_quick,
-                               sout_instance_t *p_sout )
+                               input_ressource_t *p_ressource )
 {
     static const char input_name[] = "input";
     input_thread_t *p_input = NULL;                 /* thread descriptor */
@@ -148,31 +149,20 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     if( !p_input->p )
         return NULL;
 
-    /* One "randomly" selected input thread is responsible for computing
-     * the global stats. Check if there is already someone doing this */
-    if( p_input->p_libvlc->p_stats && !b_quick )
-    {
-        libvlc_priv_t *p_private = libvlc_priv( p_input->p_libvlc );
-        vlc_mutex_lock( &p_input->p_libvlc->p_stats->lock );
-        if( p_private->p_stats_computer == NULL )
-            p_private->p_stats_computer = p_input;
-        vlc_mutex_unlock( &p_input->p_libvlc->p_stats->lock );
-    }
-
     p_input->b_preparsing = b_quick;
     p_input->psz_header = psz_header ? strdup( psz_header ) : NULL;
 
     /* Init Common fields */
     p_input->b_eof = false;
-    p_input->b_can_pace_control = true;
+    p_input->p->b_can_pace_control = true;
     p_input->p->i_start = 0;
-    p_input->i_time     = 0;
+    p_input->p->i_time  = 0;
     p_input->p->i_stop  = 0;
     p_input->p->i_run   = 0;
     p_input->p->i_title = 0;
     p_input->p->title = NULL;
     p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
-    p_input->i_state = INIT_S;
+    p_input->p->i_state = INIT_S;
     p_input->p->i_rate = INPUT_RATE_DEFAULT;
     p_input->p->b_recording = false;
     memset( &p_input->p->bookmark, 0, sizeof(p_input->p->bookmark) );
@@ -182,8 +172,6 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     p_input->p->p_es_out = NULL;
     p_input->p->p_sout   = NULL;
     p_input->p->b_out_pace_control = false;
-    p_input->i_pts_delay = 0;
-    p_input->p->i_cr_average = 0;
 
     vlc_gc_incref( p_item ); /* Released in Destructor() */
     p_input->p->p_item = p_item;
@@ -211,6 +199,13 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     p_input->p->i_slave = 0;
     p_input->p->slave   = NULL;
 
+    /* */
+    if( p_ressource )
+        p_input->p->p_ressource = p_ressource;
+    else
+        p_input->p->p_ressource = input_ressource_New();
+    input_ressource_SetInput( p_input->p->p_ressource, p_input );
+
     /* Init control buffer */
     vlc_mutex_init( &p_input->p->lock_control );
     vlc_cond_init( &p_input->p->wait_control );
@@ -231,8 +226,6 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     input_ControlVarInit( p_input );
 
     /* */
-    p_input->p->i_cr_average = var_GetInteger( p_input, "cr-average" );
-
     if( !p_input->b_preparsing )
     {
         var_Get( p_input, "bookmarks", &val );
@@ -292,9 +285,6 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
         p_input->i_flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT;
 
     /* */
-    if( p_sout )
-        p_input->p->p_sout = p_sout;
-
     memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
     vlc_mutex_init( &p_input->p->counters.counters_lock );
 
@@ -320,10 +310,10 @@ static void Destructor( input_thread_t * p_input )
 
     stats_TimerDump( p_input, STATS_TIMER_INPUT_LAUNCHING );
     stats_TimerClean( p_input, STATS_TIMER_INPUT_LAUNCHING );
-#ifdef ENABLE_SOUT
-    if( p_input->p->p_sout )
-        sout_DeleteInstance( p_input->p->p_sout );
-#endif
+
+    if( p_input->p->p_ressource )
+        input_ressource_Delete( p_input->p->p_ressource );
+
     vlc_gc_decref( p_input->p->p_item );
 
     vlc_mutex_destroy( &p_input->p->counters.counters_lock );
@@ -350,17 +340,17 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
 /* */
 input_thread_t *__input_CreateThreadExtended( vlc_object_t *p_parent,
                                               input_item_t *p_item,
-                                              const char *psz_log, sout_instance_t *p_sout )
+                                              const char *psz_log, input_ressource_t *p_ressource )
 {
     input_thread_t *p_input;
 
-    p_input = Create( p_parent, p_item, psz_log, false, p_sout );
+    p_input = Create( p_parent, p_item, psz_log, false, p_ressource );
     if( !p_input )
         return NULL;
 
     /* Create thread and wait for its readiness. */
     if( vlc_thread_create( p_input, "input", Run,
-                           VLC_THREAD_PRIORITY_INPUT, false ) )
+                           VLC_THREAD_PRIORITY_INPUT ) )
     {
         input_ChangeState( p_input, ERROR_S );
         msg_Err( p_input, "cannot create input thread" );
@@ -398,7 +388,7 @@ int __input_Read( vlc_object_t *p_parent, input_item_t *p_item,
     else
     {
         if( vlc_thread_create( p_input, "input", RunAndDestroy,
-                               VLC_THREAD_PRIORITY_INPUT, false ) )
+                               VLC_THREAD_PRIORITY_INPUT ) )
         {
             input_ChangeState( p_input, ERROR_S );
             msg_Err( p_input, "cannot create input thread" );
@@ -411,13 +401,13 @@ int __input_Read( vlc_object_t *p_parent, input_item_t *p_item,
 
 /**
  * Initialize an input and initialize it to preparse the item
- * This function is blocking. It will only accept to parse files
+ * This function is blocking. It will only accept parsing regular files.
  *
  * \param p_parent a vlc_object_t
  * \param p_item an input item
  * \return VLC_SUCCESS or an error
  */
-int __input_Preparse( vlc_object_t *p_parent, input_item_t *p_item )
+int input_Preparse( vlc_object_t *p_parent, input_item_t *p_item )
 {
     input_thread_t *p_input;
 
@@ -450,13 +440,17 @@ void input_StopThread( input_thread_t *p_input )
     input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
 }
 
-sout_instance_t *input_DetachSout( input_thread_t *p_input )
+input_ressource_t *input_DetachRessource( input_thread_t *p_input )
 {
     assert( p_input->b_dead );
-    sout_instance_t *p_sout = p_input->p->p_sout;
-    vlc_object_detach( p_sout );
+
+    input_ressource_t *p_ressource = p_input->p->p_ressource;
+    input_ressource_SetInput( p_ressource, NULL );
+
+    p_input->p->p_ressource = NULL;
     p_input->p->p_sout = NULL;
-    return p_sout;
+
+    return p_ressource;
 }
 
 /**
@@ -479,9 +473,11 @@ static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj )
     vlc_list_t *p_list;
     int i;
 
-    if( p_obj->i_object_type == VLC_OBJECT_VOUT ||
-        p_obj->i_object_type == VLC_OBJECT_AOUT ||
-        p_obj == VLC_OBJECT(p_input->p->p_sout) )
+    /* FIXME ObjectKillChildrens seems a very bad idea in fact */
+    i = vlc_internals( p_obj )->i_object_type;
+    if( i == VLC_OBJECT_VOUT ||i == VLC_OBJECT_AOUT ||
+        p_obj == VLC_OBJECT(p_input->p->p_sout) ||
+        i == VLC_OBJECT_DECODER || i == VLC_OBJECT_PACKETIZER )
         return;
 
     vlc_object_kill( p_obj );
@@ -503,23 +499,16 @@ static void *Run( vlc_object_t *p_this )
     const int canc = vlc_savecancel();
 
     if( Init( p_input ) )
-    {
-        /* If we failed, wait before we are killed, and exit */
-        WaitDie( p_input );
         goto exit;
-    }
 
     MainLoop( p_input );
 
-    /* Wait until we are asked to die */
-    if( !p_input->b_die )
-        WaitDie( p_input );
-
     /* Clean up */
     End( p_input );
 
 exit:
-    p_input->b_dead = true;
+    /* Tell we're dead */
+    input_SendEventDead( p_input );
     vlc_restorecancel( canc );
     return NULL;
 }
@@ -563,7 +552,7 @@ static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed, mtime_t *p
 
     *pb_changed = false;
 
-    if( ( p_input->p->i_stop > 0 && p_input->i_time >= p_input->p->i_stop ) ||
+    if( ( p_input->p->i_stop > 0 && p_input->p->i_time >= p_input->p->i_stop ) ||
         ( p_input->p->i_run > 0 && *pi_start_mdate+p_input->p->i_run < mdate() ) )
         i_ret = 0; /* EOF */
     else
@@ -677,6 +666,7 @@ static void MainLoopInterface( input_thread_t *p_input )
     if( demux_Control( p_input->p->input.p_demux,
                        DEMUX_GET_TIME, &i_time ) )
         i_time = 0;
+    p_input->p->i_time = i_time;
 
     if( demux_Control( p_input->p->input.p_demux,
                        DEMUX_GET_LENGTH, &i_length ) )
@@ -699,12 +689,6 @@ static void MainLoopInterface( input_thread_t *p_input )
 static void MainLoopStatistic( input_thread_t *p_input )
 {
     stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
-    /* Are we the thread responsible for computing global stats ? */
-    if( libvlc_priv( p_input->p_libvlc )->p_stats_computer == p_input )
-    {
-        stats_ComputeGlobalStats( p_input->p_libvlc,
-                                  p_input->p_libvlc->p_stats );
-    }
     input_SendEventStatistics( p_input );
 }
 
@@ -734,10 +718,10 @@ static void MainLoop( input_thread_t *p_input )
         /* Demux data */
         b_force_update = false;
         i_wakeup = 0;
-        /* FIXME if p_input->i_state == PAUSE_S the access/access_demux
+        /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
          * is paused -> this may cause problem with some of them
          * The same problem can be seen when seeking while paused */
-        b_paused = p_input->i_state == PAUSE_S &&
+        b_paused = p_input->p->i_state == PAUSE_S &&
                    !es_out_GetBuffering( p_input->p->p_es_out );
 
         if( !b_paused )
@@ -766,16 +750,15 @@ static void MainLoop( input_thread_t *p_input )
                 i_deadline = __MIN( i_intf_update, i_statistic_update );
 
             /* Handle control */
-            vlc_mutex_lock( &p_input->p->lock_control );
             ControlReduce( p_input );
-            while( !ControlPopNoLock( p_input, &i_type, &val, i_deadline ) )
+            while( !ControlPop( p_input, &i_type, &val, i_deadline ) )
             {
+
                 msg_Dbg( p_input, "control type=%d", i_type );
 
                 if( Control( p_input, i_type, val ) )
                     b_force_update = true;
             }
-            vlc_mutex_unlock( &p_input->p->lock_control );
 
             /* Update interface and statistics */
             i_current = mdate();
@@ -839,46 +822,21 @@ static void InitStatistics( input_thread_t * p_input )
 #ifdef ENABLE_SOUT
 static int InitSout( input_thread_t * p_input )
 {
-    char *psz;
-
-    if( p_input->b_preparsing ) return VLC_SUCCESS;
+    if( p_input->b_preparsing )
+        return VLC_SUCCESS;
 
     /* Find a usable sout and attach it to p_input */
-    psz = var_GetNonEmptyString( p_input, "sout" );
+    char *psz = var_GetNonEmptyString( p_input, "sout" );
     if( psz && strncasecmp( p_input->p->p_item->psz_uri, "vlc:", 4 ) )
     {
-        /* Check the validity of the provided sout */
-        if( p_input->p->p_sout )
-        {
-            if( strcmp( p_input->p->p_sout->psz_sout, psz ) )
-            {
-                msg_Dbg( p_input, "destroying unusable sout" );
-
-                sout_DeleteInstance( p_input->p->p_sout );
-                p_input->p->p_sout = NULL;
-            }
-        }
-
-        if( p_input->p->p_sout )
-        {
-            /* Reuse it */
-            msg_Dbg( p_input, "sout keep: reusing sout" );
-            msg_Dbg( p_input, "sout keep: you probably want to use "
-                              "gather stream_out" );
-            vlc_object_attach( p_input->p->p_sout, p_input );
-        }
-        else
+        p_input->p->p_sout  = input_ressource_RequestSout( p_input->p->p_ressource, NULL, psz );
+        if( !p_input->p->p_sout )
         {
-            /* Create a new one */
-            p_input->p->p_sout = sout_NewInstance( p_input, psz );
-            if( !p_input->p->p_sout )
-            {
-                input_ChangeState( p_input, ERROR_S );
-                msg_Err( p_input, "cannot start stream output instance, " \
-                                  "aborting" );
-                free( psz );
-                return VLC_EGENERIC;
-            }
+            input_ChangeState( p_input, ERROR_S );
+            msg_Err( p_input, "cannot start stream output instance, " \
+                              "aborting" );
+            free( psz );
+            return VLC_EGENERIC;
         }
         if( libvlc_stats( p_input ) )
         {
@@ -890,12 +848,9 @@ static int InitSout( input_thread_t * p_input )
                          1000000;
         }
     }
-    else if( p_input->p->p_sout )
+    else
     {
-        msg_Dbg( p_input, "destroying useless sout" );
-
-        sout_DeleteInstance( p_input->p->p_sout );
-        p_input->p->p_sout = NULL;
+        input_ressource_RequestSout( p_input->p->p_ressource, NULL, NULL );
     }
     free( psz );
 
@@ -919,29 +874,13 @@ static void InitTitle( input_thread_t * p_input )
     {
         /* Setup variables */
         input_ControlVarNavigation( p_input );
-        input_ControlVarTitle( p_input, 0 );
+        input_SendEventTitle( p_input, 0 );
     }
 
     /* Global flag */
-    p_input->b_can_pace_control    = p_master->b_can_pace_control;
+    p_input->p->b_can_pace_control    = p_master->b_can_pace_control;
     p_input->p->b_can_pause        = p_master->b_can_pause;
     p_input->p->b_can_rate_control = p_master->b_can_rate_control;
-
-    /* Fix pts delay */
-    if( p_input->i_pts_delay < 0 )
-        p_input->i_pts_delay = 0;
-
-    /* If the desynchronisation requested by the user is < 0, we need to
-     * cache more data. */
-    const int i_desynch = var_GetInteger( p_input, "audio-desync" );
-    if( i_desynch < 0 )
-        p_input->i_pts_delay -= i_desynch * 1000;
-
-    /* Update cr_average depending on the caching */
-    p_input->p->i_cr_average *= (10 * p_input->i_pts_delay / 200000);
-    p_input->p->i_cr_average /= 10;
-    if( p_input->p->i_cr_average < 10 )
-        p_input->p->i_cr_average = 10;
 }
 
 static void StartTitle( input_thread_t * p_input )
@@ -992,6 +931,7 @@ static void StartTitle( input_thread_t * p_input )
         msg_Warn( p_input, "invalid stop-time ignored" );
         p_input->p->i_stop = 0;
     }
+    p_input->p->b_fast_seek = var_GetBool( p_input, "input-fast-seek" );
 }
 
 static void LoadSubtitles( input_thread_t *p_input )
@@ -1082,11 +1022,40 @@ static void LoadSlaves( input_thread_t *p_input )
     free( psz_org );
 }
 
+static void UpdatePtsDelay( input_thread_t *p_input )
+{
+    input_thread_private_t *p_sys = p_input->p;
+
+    /* Get max pts delay from input source */
+    mtime_t i_pts_delay = p_sys->input.i_pts_delay;
+    for( int i = 0; i < p_sys->i_slave; i++ )
+        i_pts_delay = __MAX( i_pts_delay, p_sys->slave[i]->i_pts_delay );
+
+    if( i_pts_delay < 0 )
+        i_pts_delay = 0;
+
+    /* Take care of audio/spu delay */
+    const mtime_t i_audio_delay = var_GetTime( p_input, "audio-delay" );
+    const mtime_t i_spu_delay   = var_GetTime( p_input, "spu-delay" );
+    const mtime_t i_extra_delay = __MIN( i_audio_delay, i_spu_delay );
+    if( i_extra_delay < 0 )
+        i_pts_delay -= i_extra_delay;
+
+    /* Update cr_average depending on the caching */
+    const int i_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;
+
+    /* */
+    es_out_SetJitter( p_input->p->p_es_out, i_pts_delay, i_cr_average );
+}
+
 static void InitPrograms( input_thread_t * p_input )
 {
     int i_es_out_mode;
     vlc_value_t val;
 
+    /* Compute correct pts_delay */
+    UpdatePtsDelay( p_input );
+
     /* Set up es_out */
     es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ACTIVE, true );
     i_es_out_mode = ES_OUT_MODE_AUTO;
@@ -1156,7 +1125,7 @@ static int Init( input_thread_t * p_input )
 #ifdef ENABLE_SOUT
     ret = InitSout( p_input );
     if( ret != VLC_SUCCESS )
-        return ret; /* FIXME: goto error; should be better here */
+        goto error_stats;
 #endif
 
     /* Create es out */
@@ -1203,7 +1172,7 @@ static int Init( input_thread_t * p_input )
     {
         p_input->p->b_out_pace_control = (p_input->p->p_sout->i_out_pace_nocontrol > 0);
 
-        if( p_input->b_can_pace_control && p_input->p->b_out_pace_control )
+        if( p_input->p->b_can_pace_control && p_input->p->b_out_pace_control )
         {
             /* We don't want a high input priority here or we'll
              * end-up sucking up all the CPU time */
@@ -1231,12 +1200,8 @@ static int Init( input_thread_t * p_input )
         InputUpdateMeta( p_input, p_meta );
     }
 
-    if( !p_input->b_preparsing )
-    {
-        msg_Dbg( p_input, "`%s' successfully opened",
-                 p_input->p->p_item->psz_uri );
-
-    }
+    msg_Dbg( p_input, "`%s' successfully opened",
+             p_input->p->p_item->psz_uri );
 
     /* initialization is complete */
     input_ChangeState( p_input, PLAYING_S );
@@ -1250,14 +1215,17 @@ error:
         es_out_Delete( p_input->p->p_es_out );
     if( p_input->p->p_es_out_display )
         es_out_Delete( p_input->p->p_es_out_display );
-#ifdef ENABLE_SOUT
-    if( p_input->p->p_sout )
+    if( p_input->p->p_ressource )
     {
-        vlc_object_detach( p_input->p->p_sout );
-        sout_DeleteInstance( p_input->p->p_sout );
+        if( p_input->p->p_sout )
+            input_ressource_RequestSout( p_input->p->p_ressource,
+                                         p_input->p->p_sout, NULL );
+        input_ressource_SetInput( p_input->p->p_ressource, NULL );
     }
-#endif
 
+#ifdef ENABLE_SOUT
+error_stats:
+#endif
     if( !p_input->b_preparsing && libvlc_stats( p_input ) )
     {
 #define EXIT_COUNTER( c ) do { if( p_input->p->counters.p_##c ) \
@@ -1296,22 +1264,6 @@ error:
     return VLC_EGENERIC;
 }
 
-/*****************************************************************************
- * WaitDie: Wait until we are asked to die.
- *****************************************************************************
- * This function is called when an error occurred during thread main's loop.
- *****************************************************************************/
-static void WaitDie( input_thread_t *p_input )
-{
-    input_ChangeState( p_input, p_input->b_error ? ERROR_S : END_S );
-
-    /* Wait a die order */
-    vlc_object_lock( p_input );
-    while( vlc_object_alive( p_input ) )
-        vlc_object_wait( p_input );
-    vlc_object_unlock( p_input );
-}
-
 /*****************************************************************************
  * End: end the input thread
  *****************************************************************************/
@@ -1355,13 +1307,6 @@ static void End( input_thread_t * p_input )
 
             /* make sure we are up to date */
             stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
-            if( p_private->p_stats_computer == p_input )
-            {
-                stats_ComputeGlobalStats( p_input->p_libvlc,
-                                          p_input->p_libvlc->p_stats );
-                /* FIXME how can it be thread safe ? */
-                p_private->p_stats_computer = NULL;
-            }
             CL_CO( read_bytes );
             CL_CO( read_packets );
             CL_CO( demux_read );
@@ -1382,8 +1327,6 @@ static void End( input_thread_t * p_input )
             CL_CO( sout_sent_packets );
             CL_CO( sout_sent_bytes );
             CL_CO( sout_send_bitrate );
-
-            vlc_object_detach( p_input->p->p_sout );
         }
 #undef CL_CO
     }
@@ -1395,8 +1338,10 @@ static void End( input_thread_t * p_input )
         TAB_CLEAN( p_input->p->i_attachment, p_input->p->attachment );
     }
 
-    /* Tell we're dead */
-    p_input->b_dead = true;
+    /* */
+    input_ressource_RequestSout( p_input->p->p_ressource,
+                                 p_input->p->p_sout, NULL );
+    input_ressource_SetInput( p_input->p->p_ressource, NULL );
 }
 
 /*****************************************************************************
@@ -1433,49 +1378,47 @@ void input_ControlPush( input_thread_t *p_input,
     vlc_mutex_unlock( &p_input->p->lock_control );
 }
 
-static inline int ControlPopNoLock( input_thread_t *p_input,
-                                    int *pi_type, vlc_value_t *p_val,
-                                    mtime_t i_deadline )
+static inline int ControlPop( input_thread_t *p_input,
+                              int *pi_type, vlc_value_t *p_val,
+                              mtime_t i_deadline )
 {
+    input_thread_private_t *p_sys = p_input->p;
 
-    while( p_input->p->i_control <= 0 )
+    vlc_mutex_lock( &p_sys->lock_control );
+    while( p_sys->i_control <= 0 )
     {
-        if( !vlc_object_alive( p_input ) )
-            return VLC_EGENERIC;
-
-        if( i_deadline < 0 )
+        if( !vlc_object_alive( p_input ) || i_deadline < 0 )
+        {
+            vlc_mutex_unlock( &p_sys->lock_control );
             return VLC_EGENERIC;
+        }
 
-        if( vlc_cond_timedwait( &p_input->p->wait_control, &p_input->p->lock_control, i_deadline ) )
+        if( vlc_cond_timedwait( &p_sys->wait_control, &p_sys->lock_control,
+                                i_deadline ) )
+        {
+            vlc_mutex_unlock( &p_sys->lock_control );
             return VLC_EGENERIC;
+        }
     }
 
-    *pi_type = p_input->p->control[0].i_type;
-    *p_val   = p_input->p->control[0].val;
-
-    p_input->p->i_control--;
-    if( p_input->p->i_control > 0 )
-    {
-        int i;
+    /* */
+    *pi_type = p_sys->control[0].i_type;
+    *p_val   = p_sys->control[0].val;
 
-        for( i = 0; i < p_input->p->i_control; i++ )
-        {
-            p_input->p->control[i].i_type = p_input->p->control[i+1].i_type;
-            p_input->p->control[i].val    = p_input->p->control[i+1].val;
-        }
-    }
+    p_sys->i_control--;
+    if( p_sys->i_control > 0 )
+        memmove( &p_sys->control[0], &p_sys->control[1],
+                 sizeof(*p_sys->control) * p_sys->i_control );
+    vlc_mutex_unlock( &p_sys->lock_control );
 
     return VLC_SUCCESS;
 }
 
 static void ControlReduce( input_thread_t *p_input )
 {
-    int i;
-
-    if( !p_input )
-        return;
+    vlc_mutex_lock( &p_input->p->lock_control );
 
-    for( i = 1; i < p_input->p->i_control; i++ )
+    for( int i = 1; i < p_input->p->i_control; i++ )
     {
         const int i_lt = p_input->p->control[i-1].i_type;
         const int i_ct = p_input->p->control[i].i_type;
@@ -1511,6 +1454,7 @@ static void ControlReduce( input_thread_t *p_input )
                 */
         }
     }
+    vlc_mutex_unlock( &p_input->p->lock_control );
 }
 /* Pause input */
 static void ControlPause( input_thread_t *p_input, mtime_t i_control_date )
@@ -1530,7 +1474,7 @@ static void ControlPause( input_thread_t *p_input, mtime_t i_control_date )
         if( i_ret )
         {
             msg_Warn( p_input, "cannot set pause state" );
-            i_state = p_input->i_state;
+            i_state = p_input->p->i_state;
         }
     }
 
@@ -1541,7 +1485,7 @@ static void ControlPause( input_thread_t *p_input, mtime_t i_control_date )
         if( i_ret )
         {
             msg_Warn( p_input, "cannot set pause state at es_out level" );
-            i_state = p_input->i_state;
+            i_state = p_input->p->i_state;
         }
     }
 
@@ -1566,9 +1510,7 @@ static void ControlUnpause( input_thread_t *p_input, mtime_t i_control_date )
         {
             /* FIXME What to do ? */
             msg_Warn( p_input, "cannot unset pause -> EOF" );
-            vlc_mutex_unlock( &p_input->p->lock_control );
             input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
-            vlc_mutex_lock( &p_input->p->lock_control );
         }
     }
 
@@ -1619,7 +1561,7 @@ static bool Control( input_thread_t *p_input, int i_type,
             /* Reset the decoders states and clock sync (before calling the demuxer */
             es_out_SetTime( p_input->p->p_es_out, -1 );
             if( demux_Control( p_input->p->input.p_demux, DEMUX_SET_POSITION,
-                                f_pos ) )
+                                f_pos, !p_input->p->b_fast_seek ) )
             {
                 msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
                          "%2.1f%% failed", f_pos * 100 );
@@ -1658,7 +1600,8 @@ static bool Control( input_thread_t *p_input, int i_type,
             es_out_SetTime( p_input->p->p_es_out, -1 );
 
             i_ret = demux_Control( p_input->p->input.p_demux,
-                                    DEMUX_SET_TIME, i_time );
+                                   DEMUX_SET_TIME, i_time,
+                                   !p_input->p->b_fast_seek );
             if( i_ret )
             {
                 int64_t i_length;
@@ -1670,7 +1613,8 @@ static bool Control( input_thread_t *p_input, int i_type,
                 {
                     double f_pos = (double)i_time / (double)i_length;
                     i_ret = demux_Control( p_input->p->input.p_demux,
-                                            DEMUX_SET_POSITION, f_pos );
+                                            DEMUX_SET_POSITION, f_pos,
+                                            !p_input->p->b_fast_seek );
                 }
             }
             if( i_ret )
@@ -1690,14 +1634,14 @@ static bool Control( input_thread_t *p_input, int i_type,
         }
 
         case INPUT_CONTROL_SET_STATE:
-            if( ( val.i_int == PLAYING_S && p_input->i_state == PAUSE_S ) ||
-                ( val.i_int == PAUSE_S && p_input->i_state == PAUSE_S ) )
+            if( ( val.i_int == PLAYING_S && p_input->p->i_state == PAUSE_S ) ||
+                ( val.i_int == PAUSE_S && p_input->p->i_state == PAUSE_S ) )
             {
                 ControlUnpause( p_input, i_control_date );
 
                 b_force_update = true;
             }
-            else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S /* &&
+            else if( val.i_int == PAUSE_S && p_input->p->i_state == PLAYING_S /* &&
                      p_input->p->b_can_pause */ )
             {
                 ControlPause( p_input, i_control_date );
@@ -1709,7 +1653,7 @@ static bool Control( input_thread_t *p_input, int i_type,
                 b_force_update = true;
 
                 /* Correct "state" value */
-                input_ChangeState( p_input, p_input->i_state );
+                input_ChangeState( p_input, p_input->p->i_state );
             }
             else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S )
             {
@@ -1811,7 +1755,7 @@ static bool Control( input_thread_t *p_input, int i_type,
                 i_rate = INPUT_RATE_DEFAULT;
             }
             if( i_rate != p_input->p->i_rate &&
-                !p_input->b_can_pace_control && p_input->p->b_can_rate_control )
+                !p_input->p->b_can_pace_control && p_input->p->b_can_rate_control )
             {
                 int i_ret;
                 if( p_input->p->input.p_access )
@@ -1841,7 +1785,7 @@ static bool Control( input_thread_t *p_input, int i_type,
 
                 if( p_input->p->input.b_rescale_ts )
                 {
-                    const int i_rate_source = (p_input->b_can_pace_control || p_input->p->b_can_rate_control ) ? i_rate : INPUT_RATE_DEFAULT;
+                    const int i_rate_source = (p_input->p->b_can_pace_control || p_input->p->b_can_rate_control ) ? i_rate : INPUT_RATE_DEFAULT;
                     es_out_SetRate( p_input->p->p_es_out, i_rate_source, i_rate );
                 }
 
@@ -1870,12 +1814,18 @@ static bool Control( input_thread_t *p_input, int i_type,
 
         case INPUT_CONTROL_SET_AUDIO_DELAY:
             if( !es_out_SetDelay( p_input->p->p_es_out_display, AUDIO_ES, val.i_time ) )
+            {
                 input_SendEventAudioDelay( p_input, val.i_time );
+                UpdatePtsDelay( p_input );
+            }
             break;
 
         case INPUT_CONTROL_SET_SPU_DELAY:
             if( !es_out_SetDelay( p_input->p->p_es_out_display, SPU_ES, val.i_time ) )
+            {
                 input_SendEventSubtitleDelay( p_input, val.i_time );
+                UpdatePtsDelay( p_input );
+            }
             break;
 
         case INPUT_CONTROL_SET_TITLE:
@@ -1906,7 +1856,7 @@ static bool Control( input_thread_t *p_input, int i_type,
                     es_out_SetTime( p_input->p->p_es_out, -1 );
 
                     demux_Control( p_demux, DEMUX_SET_TITLE, i_title );
-                    input_ControlVarTitle( p_input, i_title );
+                    input_SendEventTitle( p_input, i_title );
                 }
             }
             else if( p_input->p->input.i_title > 0 )
@@ -1927,6 +1877,7 @@ static bool Control( input_thread_t *p_input, int i_type,
 
                     stream_Control( p_input->p->input.p_stream, STREAM_CONTROL_ACCESS,
                                     ACCESS_SET_TITLE, i_title );
+                    input_SendEventTitle( p_input, i_title );
                 }
             }
             break;
@@ -1972,6 +1923,7 @@ static bool Control( input_thread_t *p_input, int i_type,
                     es_out_SetTime( p_input->p->p_es_out, -1 );
 
                     demux_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint );
+                    input_SendEventSeekpoint( p_input, p_demux->info.i_title, i_seekpoint );
                 }
             }
             else if( p_input->p->input.i_title > 0 )
@@ -2006,6 +1958,7 @@ static bool Control( input_thread_t *p_input, int i_type,
 
                     stream_Control( p_input->p->input.p_stream, STREAM_CONTROL_ACCESS,
                                     ACCESS_SET_SEEKPOINT, i_seekpoint );
+                    input_SendEventSeekpoint( p_input, p_access->info.i_title, i_seekpoint );
                 }
             }
             break;
@@ -2042,7 +1995,7 @@ static bool Control( input_thread_t *p_input, int i_type,
                         break;
                     }
                     if( demux_Control( slave->p_demux,
-                                       DEMUX_SET_TIME, i_time ) )
+                                       DEMUX_SET_TIME, i_time, true ) )
                     {
                         msg_Err( p_input, "seek failed for new slave" );
                         InputSourceClean( slave );
@@ -2095,11 +2048,11 @@ static bool Control( input_thread_t *p_input, int i_type,
             break;
 
         case INPUT_CONTROL_SET_FRAME_NEXT:
-            if( p_input->i_state == PAUSE_S )
+            if( p_input->p->i_state == PAUSE_S )
             {
                 es_out_SetFrameNext( p_input->p->p_es_out );
             }
-            else if( p_input->i_state == PLAYING_S )
+            else if( p_input->p->i_state == PLAYING_S )
             {
                 ControlPause( p_input, i_control_date );
             }
@@ -2377,12 +2330,9 @@ static int InputSourceInit( input_thread_t *p_input,
 
     if( in->p_demux )
     {
-        int64_t i_pts_delay;
-
         /* Get infos from access_demux */
         demux_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 );
+                        DEMUX_GET_PTS_DELAY, &in->i_pts_delay );
 
         in->b_title_demux = true;
         if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
@@ -2424,8 +2374,6 @@ static int InputSourceInit( input_thread_t *p_input,
     }
     else
     {
-        int64_t i_pts_delay;
-
         /* Now try a real access */
         in->p_access = access_New( p_input, psz_access, psz_demux, psz_path );
 
@@ -2455,8 +2403,7 @@ static int InputSourceInit( input_thread_t *p_input,
         if( !p_input->b_preparsing )
         {
             access_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 );
+                             ACCESS_GET_PTS_DELAY, &in->i_pts_delay );
 
             in->b_title_demux = false;
             if( access_Control( in->p_access, ACCESS_GET_TITLE_INFO,
@@ -2482,27 +2429,55 @@ static int InputSourceInit( input_thread_t *p_input,
             var_Set( p_input, "can-seek", val );
         }
 
-        /* TODO (maybe)
-         * do not let stream_AccessNew access input-list
-         * but give it a list of access ? */
+        /* */
+        int  i_input_list;
+        char **ppsz_input_list;
+
+        TAB_INIT( i_input_list, ppsz_input_list );
+
+        /* On master stream only, use input-list */
+        if( &p_input->p->input == in )
+        {
+            char *psz_list;
+            char *psz_parser;
+
+            psz_list =
+            psz_parser = var_CreateGetNonEmptyString( p_input, "input-list" );
 
+            while( psz_parser && *psz_parser )
+            {
+                char *p = strchr( psz_parser, ',' );
+                if( p )
+                    *p++ = '\0';
+
+                if( *psz_parser )
+                {
+                    char *psz_name = strdup( psz_parser );
+                    if( psz_name )
+                        TAB_APPEND( i_input_list, ppsz_input_list, psz_name );
+                }
+
+                psz_parser = p;
+            }
+            free( psz_list );
+        }
         /* Autodetect extra files if none specified */
-        char *psz_input_list = var_CreateGetNonEmptyString( p_input, "input-list" );
-        if( !psz_input_list )
+        if( i_input_list <= 0 )
         {
-            char *psz_extra_files = InputGetExtraFiles( p_input, psz_access, psz_path );
-            if( psz_extra_files )
-                var_SetString( p_input, "input-list", psz_extra_files );
-            free( psz_extra_files );
+            InputGetExtraFiles( p_input, &i_input_list, &ppsz_input_list,
+                                psz_access, psz_path );
         }
+        if( i_input_list > 0 )
+            TAB_APPEND( i_input_list, ppsz_input_list, NULL );
 
         /* Create the stream_t */
-        in->p_stream = stream_AccessNew( in->p_access, p_input->b_preparsing );
-
-        /* Restore old value */
-        if( !psz_input_list )
-            var_SetString( p_input, "input-list", "" );
-        free( psz_input_list );
+        in->p_stream = stream_AccessNew( in->p_access, ppsz_input_list );
+        if( ppsz_input_list )
+        {
+            for( int i = 0; ppsz_input_list[i] != NULL; i++ )
+                free( ppsz_input_list[i] );
+            TAB_CLEAN( i_input_list, ppsz_input_list );
+        }
 
         if( in->p_stream == NULL )
         {
@@ -2525,13 +2500,13 @@ static int InputSourceInit( input_thread_t *p_input,
         }
 
         {
-            /* Take access redirections into account */
+            /* Take access/stream redirections into account */
             char *psz_real_path;
             char *psz_buf = NULL;
-            if( in->p_access->psz_path )
+            if( in->p_stream->psz_path )
             {
                 const char *psz_a, *psz_d;
-                psz_buf = strdup( in->p_access->psz_path );
+                psz_buf = strdup( in->p_stream->psz_path );
                 input_SplitMRL( &psz_a, &psz_d, &psz_real_path, psz_buf );
             }
             else
@@ -2703,12 +2678,11 @@ static void SlaveDemux( input_thread_t *p_input )
 {
     int64_t i_time;
     int i;
-    bool b_set_time = true;
 
     if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_TIME, &i_time ) )
     {
-        /* msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" ); */
-        b_set_time = false;
+        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
+        return;
     }
 
     for( i = 0; i < p_input->p->i_slave; i++ )
@@ -2719,7 +2693,7 @@ static void SlaveDemux( input_thread_t *p_input )
         if( in->b_eof )
             continue;
 
-        if( b_set_time && demux_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
+        if( demux_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
         {
             for( ;; )
             {
@@ -2767,7 +2741,7 @@ static void SlaveSeek( input_thread_t *p_input )
     {
         input_source_t *in = p_input->p->slave[i];
 
-        if( demux_Control( in->p_demux, DEMUX_SET_TIME, i_time ) )
+        if( demux_Control( in->p_demux, DEMUX_SET_TIME, i_time, true ) )
         {
             if( !in->b_eof )
                 msg_Err( p_input, "seek failed for slave %d -> EOF", i );
@@ -2824,24 +2798,30 @@ static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta )
 
     vlc_meta_Delete( p_meta );
 
+    if( !psz_arturl || *psz_arturl == '\0' )
+    {
+        const char *psz_tmp = vlc_meta_Get( p_item->p_meta, vlc_meta_ArtworkURL );
+        if( psz_tmp )
+            psz_arturl = strdup( psz_tmp );
+    }
+    vlc_mutex_unlock( &p_item->lock );
+
     if( psz_arturl && *psz_arturl )
     {
-        vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, psz_arturl );
+        input_item_SetArtURL( p_item, psz_arturl );
 
         if( !strncmp( psz_arturl, "attachment://", strlen("attachment") ) )
         {
             /* Don't look for art cover if sout
              * XXX It can change when sout has meta data support */
             if( p_input->p->p_sout && !p_input->b_preparsing )
-                vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, "" );
+                input_item_SetArtURL( p_item, "" );
             else
                 input_ExtractAttachmentAndCacheArt( p_input );
         }
     }
     free( psz_arturl );
 
-    vlc_mutex_unlock( &p_item->lock );
-
     if( psz_title )
     {
         input_item_SetName( p_item, psz_title );
@@ -2875,64 +2855,101 @@ static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_atta
  * InputGetExtraFiles
  *  Autodetect extra input list
  *****************************************************************************/
-static char *InputGetExtraFiles( input_thread_t *p_input,
-                                 const char *psz_access, const char *psz_path )
+static void InputGetExtraFilesPattern( input_thread_t *p_input,
+                                       int *pi_list, char ***pppsz_list,
+                                       const char *psz_path,
+                                       const char *psz_match,
+                                       const char *psz_format,
+                                       int i_start, int i_stop )
 {
-    char *psz_list = NULL;
-
-    if( ( psz_access && *psz_access && strcmp( psz_access, "file" ) ) || !psz_path )
-        return NULL;
+    int i_list;
+    char **ppsz_list;
 
+    TAB_INIT( i_list, ppsz_list );
 
-    const char *psz_ext = strrchr( psz_path, '.' );
-    if( !psz_ext || strcmp( psz_ext, ".001" ) )
-        return NULL;
+    char *psz_base = strdup( psz_path );
+    if( !psz_base )
+        goto exit;
 
-    char *psz_file = strdup( psz_path );
-    if( !psz_file )
-        return NULL;
+    /* Remove the extension */
+    char *psz_end = &psz_base[strlen(psz_base)-strlen(psz_match)];
+    assert( psz_end >= psz_base);
+    *psz_end = '\0';
 
-    /* Try to list .xyz files */
-    for( int i = 2; i < 999; i++ )
+    /* Try to list files */
+    for( int i = i_start; i <= i_stop; i++ )
     {
-        char *psz_ext = strrchr( psz_file, '.' );
         struct stat st;
+        char *psz_file;
 
-        snprintf( psz_ext, 5, ".%.3d", i );
+        if( asprintf( &psz_file, psz_format, psz_base, i ) < 0 )
+            break;
 
-        if( utf8_stat( psz_file, &st )
-         || !S_ISREG( st.st_mode ) || !st.st_size )
-            continue;
+        if( utf8_stat( psz_file, &st ) || !S_ISREG( st.st_mode ) || !st.st_size )
+        {
+            free( psz_file );
+            break;
+        }
 
         msg_Dbg( p_input, "Detected extra file `%s'", psz_file );
+        TAB_APPEND( i_list, ppsz_list, psz_file );
+    }
+    free( psz_base );
+exit:
+    *pi_list = i_list;
+    *pppsz_list = ppsz_list;
+}
 
-        if( psz_list )
-        {
-            char *psz_old = psz_list;
-            /* FIXME how to handle file with ',' ?*/
-            if( asprintf( &psz_list, "%s,%s", psz_old, psz_file ) < 0 )
-            {
-                psz_list = psz_old;
-                break;
-            }
-        }
-        else
+static void InputGetExtraFiles( input_thread_t *p_input,
+                                int *pi_list, char ***pppsz_list,
+                                const char *psz_access, const char *psz_path )
+{
+    static const struct
+    {
+        const char *psz_match;
+        const char *psz_format;
+        int i_start;
+        int i_stop;
+    } p_pattern[] = {
+        /* XXX the order is important */
+        { ".001",         "%s.%.3d",        2, 999 },
+        { ".part1.rar",   "%s.part%.1d.rar",2, 9 },
+        { ".part01.rar",  "%s.part%.2d.rar",2, 99, },
+        { ".part001.rar", "%s.part%.3d.rar",2, 999 },
+        { ".rar",         "%s.r%.2d",       1, 99 },
+        { NULL, NULL, 0, 0 }
+    };
+
+    TAB_INIT( *pi_list, *pppsz_list );
+
+    if( ( psz_access && *psz_access && strcmp( psz_access, "file" ) ) || !psz_path )
+        return;
+
+    const size_t i_path = strlen(psz_path);
+
+    for( int i = 0; p_pattern[i].psz_match != NULL; i++ )
+    {
+        const size_t i_ext = strlen(p_pattern[i].psz_match );
+
+        if( i_path < i_ext )
+            continue;
+        if( !strcmp( &psz_path[i_path-i_ext], p_pattern[i].psz_match ) )
         {
-            psz_list = strdup( psz_file );
+            InputGetExtraFilesPattern( p_input, pi_list, pppsz_list,
+                                       psz_path,
+                                       p_pattern[i].psz_match, p_pattern[i].psz_format,
+                                       p_pattern[i].i_start, p_pattern[i].i_stop );
+            return;
         }
     }
-    free( psz_file );
-
-    return psz_list;
 }
 
-
 /* */
 static void input_ChangeState( input_thread_t *p_input, int i_state )
 {
-    const bool b_changed = p_input->i_state != i_state;
+    const bool b_changed = p_input->p->i_state != i_state;
 
-    p_input->i_state = i_state;
+    p_input->p->i_state = i_state;
     if( i_state == ERROR_S )
         p_input->b_error = true;
     else if( i_state == END_S )
@@ -3144,7 +3161,7 @@ static void SubtitleAdd( input_thread_t *p_input, char *psz_subtitle, bool b_for
 void input_UpdateStatistic( input_thread_t *p_input,
                             input_statistic_t i_type, int i_delta )
 {
-    assert( p_input->i_state != INIT_S );
+    assert( p_input->p->i_state != INIT_S );
 
     vlc_mutex_lock( &p_input->p->counters.counters_lock);
     switch( i_type )