]> git.sesse.net Git - vlc/blobdiff - src/input/input.c
libvlccore: push threads cancellation down vlc_thread_create
[vlc] / src / input / input.c
index 7f85afbd5ba1158d696c0352b0e2deb318ff10bc..86acdd63847b3833defcb39af948f00b62de1947 100644 (file)
@@ -29,7 +29,7 @@
 # include "config.h"
 #endif
 
-#include <vlc/vlc.h>
+#include <vlc_common.h>
 
 #include <ctype.h>
 #include <limits.h>
@@ -43,7 +43,7 @@
 #include <vlc_interface.h>
 #include <vlc_url.h>
 #include <vlc_charset.h>
-#include <vlc_playlist.h>
+#include <vlc_strings.h>
 
 #ifdef HAVE_SYS_STAT_H
 #   include <sys/stat.h>
  *****************************************************************************/
 static void Destructor( input_thread_t * p_input );
 
-static  int Run  ( input_thread_t *p_input );
-static  int RunAndDestroy  ( input_thread_t *p_input );
+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 * );
 static  int             Init    ( input_thread_t *p_input );
-static void             Error   ( 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 );
 
@@ -87,9 +87,6 @@ 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 sout_instance_t *SoutFind( vlc_object_t *p_parent, input_item_t *p_item, bool * );
-static void SoutKeep( sout_instance_t * );
-
 static void DemuxMeta( input_thread_t *p_input, vlc_meta_t *p_meta, demux_t *p_demux );
 static void AccessMeta( input_thread_t * p_input, vlc_meta_t *p_meta );
 static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
@@ -116,6 +113,8 @@ static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_atta
  *  - seekable (if you can seek, it doesn't say if 'bar display' has be shown
  *    or not, for that check position != 0.0)
  *  - can-pause
+ *  - can-record (if a stream can be recorded while playing)
+ *  - teletext-es to get the index of spu track that is teletext --1 if no teletext)
  * * For intf callback upon changes
  *  - intf-change
  *  - rate-change for when playback rate changes
@@ -135,27 +134,46 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     p_input = vlc_custom_create( p_parent, sizeof( *p_input ),
                                  VLC_OBJECT_INPUT, input_name );
     if( p_input == NULL )
-    {
-        msg_Err( p_parent, "out of memory" );
         return NULL;
-    }
+
+    /* Construct a nice name for the input timer */
+    char psz_timer_name[255];
+    char * psz_name = input_item_GetName( p_item );
+    snprintf( psz_timer_name, sizeof(psz_timer_name),
+              "input launching for '%s'", psz_name );
+
+    msg_Dbg( p_input, "Creating an input for '%s'", psz_name);
+
+    free( psz_name );
+
+    /* Start a timer to mesure how long it takes
+     * to launch an input */
+    stats_TimerStart( p_input, psz_timer_name,
+        STATS_TIMER_INPUT_LAUNCHING );
+
     MALLOC_NULL( p_input->p, input_thread_private_t );
+    memset( p_input->p, 0, sizeof( input_thread_private_t ) );
 
     /* 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 *priv = libvlc_priv (p_input->p_libvlc);
         vlc_mutex_lock( &p_input->p_libvlc->p_stats->lock );
-        if( p_input->p_libvlc->p_stats_computer == NULL )
-        {
-            p_input->p_libvlc->p_stats_computer = p_input;
-        }
+        if( priv->p_stats_computer == NULL )
+            priv->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 events */
+    vlc_event_manager_t * p_em = &p_input->p->event_manager;
+    vlc_event_manager_init_with_vlc_object( p_em, p_input );
+    vlc_event_manager_register_event_type( p_em, vlc_InputStateChanged );
+    vlc_event_manager_register_event_type( p_em, vlc_InputSelectedStreamChanged );
+
     /* Init Common fields */
     p_input->b_eof = false;
     p_input->b_can_pace_control = true;
@@ -168,12 +186,11 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
     p_input->i_state = INIT_S;
     p_input->p->i_rate  = INPUT_RATE_DEFAULT;
+    p_input->p->b_recording = false;
     TAB_INIT( p_input->p->i_bookmark, p_input->p->bookmark );
     TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
     p_input->p->p_es_out = NULL;
     p_input->p->p_sout  = NULL;
-    p_input->p->b_owns_its_sout = true;
-    p_input->p->b_sout_keep  = false;
     p_input->p->b_out_pace_control = false;
     p_input->i_pts_delay = 0;
 
@@ -204,12 +221,12 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     p_input->p->slave   = NULL;
 
     /* Init control buffer */
-    vlc_mutex_init( p_input, &p_input->p->lock_control );
+    vlc_mutex_init( &p_input->p->lock_control );
     p_input->p->i_control = 0;
 
     /* Parse input options */
     vlc_mutex_lock( &p_item->lock );
-    assert( p_item->optflagc == p_item->i_options );
+    assert( (int)p_item->optflagc == p_item->i_options );
     for( i = 0; i < p_item->i_options; i++ )
         var_OptionParse( VLC_OBJECT(p_input), p_item->ppsz_options[i],
                          !!(p_item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED) );
@@ -221,6 +238,8 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     /* Create Objects variables for public Get and Set */
     input_ControlVarInit( p_input );
 
+    /* */
+    p_input->p->pts_adjust.b_auto_adjust = var_GetBool( p_input, "auto-adjust-pts-delay" );
     p_input->p->input.i_cr_average = var_GetInteger( p_input, "cr-average" );
 
     if( !p_input->b_preparsing )
@@ -260,7 +279,7 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
                      }
                      psz_start = psz_end + 1;
                 }
-                msg_Dbg( p_input, "adding bookmark: %s, bytes="I64Fd", time="I64Fd,
+                msg_Dbg( p_input, "adding bookmark: %s, bytes=%"PRId64", time=%"PRId64,
                                   p_seekpoint->psz_name, p_seekpoint->i_byte_offset,
                                   p_seekpoint->i_time_offset );
                 input_Control( p_input, INPUT_ADD_BOOKMARK, p_seekpoint );
@@ -272,9 +291,6 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     }
 
     /* Remove 'Now playing' info as it is probably outdated */
-    input_Control( p_input, INPUT_DEL_INFO,
-        _(VLC_META_INFO_CAT),
-        _(VLC_META_NOW_PLAYING) );
     input_item_SetNowPlaying( p_item, NULL );
 
     /* */
@@ -283,20 +299,17 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
 
     /* */
     if( p_sout )
-    {
         p_input->p->p_sout = p_sout;
-        p_input->p->b_owns_its_sout = false;
-    }
 
     memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
-    vlc_mutex_init( p_input, &p_input->p->counters.counters_lock );
-
-    /* Attach only once we are ready */
-    vlc_object_attach( p_input, p_parent );
+    vlc_mutex_init( &p_input->p->counters.counters_lock );
 
     /* Set the destructor when we are sure we are initialized */
     vlc_object_set_destructor( p_input, (vlc_destructor_t)Destructor );
 
+    /* Attach only once we are ready */
+    vlc_object_attach( p_input, p_parent );
+
     return p_input;
 }
 
@@ -307,14 +320,20 @@ static void Destructor( input_thread_t * p_input )
 {
     input_thread_private_t *priv = p_input->p;
 
-    if( priv->b_owns_its_sout && priv->p_sout )
-    {
-        if( priv->b_sout_keep )
-            SoutKeep( priv->p_sout );
-        else
-            sout_DeleteInstance( priv->p_sout );
-    }
+#ifndef NDEBUG
+    char * psz_name = input_item_GetName( p_input->p->input.p_item );
+    msg_Dbg( p_input, "Destroying the input for '%s'", psz_name);
+    free( psz_name );
+#endif
 
+    vlc_event_manager_fini( &p_input->p->event_manager );
+
+    stats_TimerDump( p_input, STATS_TIMER_INPUT_LAUNCHING );
+    stats_TimerClean( p_input, STATS_TIMER_INPUT_LAUNCHING );
+#ifdef ENABLE_SOUT
+    if( priv->p_sout )
+        sout_DeleteInstance( priv->p_sout );
+#endif
     vlc_gc_decref( p_input->p->input.p_item );
 
     vlc_mutex_destroy( &p_input->p->counters.counters_lock );
@@ -334,15 +353,7 @@ static void Destructor( input_thread_t * p_input )
 input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
                                       input_item_t *p_item )
 {
-    bool b_sout_keep;
-    sout_instance_t *p_sout = SoutFind( p_parent, p_item, &b_sout_keep );
-    input_thread_t *p_input =  __input_CreateThreadExtended( p_parent, p_item, NULL, p_sout );
-
-    if( !p_input && p_sout )
-        SoutKeep( p_sout );
-
-    p_input->p->b_sout_keep = b_sout_keep;
-    return p_input;
+    return __input_CreateThreadExtended( p_parent, p_item, NULL, NULL );
 }
 
 /* */
@@ -371,7 +382,7 @@ input_thread_t *__input_CreateThreadExtended( vlc_object_t *p_parent,
 }
 
 /**
- * Initialize an input thread and run it. This thread will clean after himself,
+ * Initialize an input thread and run it. This thread will clean after itself,
  * you can forget about it. It can work either in blocking or non-blocking mode
  *
  * \param p_parent a vlc_object
@@ -382,21 +393,15 @@ input_thread_t *__input_CreateThreadExtended( vlc_object_t *p_parent,
 int __input_Read( vlc_object_t *p_parent, input_item_t *p_item,
                    bool b_block )
 {
-    bool b_sout_keep;
-    sout_instance_t *p_sout = SoutFind( p_parent, p_item, &b_sout_keep );
     input_thread_t *p_input;
 
-    p_input = Create( p_parent, p_item, NULL, false, p_sout );
-    if( !p_input && p_sout )
-    {
-        SoutKeep( p_sout );
+    p_input = Create( p_parent, p_item, NULL, false, NULL );
+    if( !p_input )
         return VLC_EGENERIC;
-    }
-    p_input->p->b_sout_keep = b_sout_keep;
 
     if( b_block )
     {
-        RunAndDestroy( p_input );
+        RunAndDestroy( VLC_OBJECT(p_input) );
         return VLC_SUCCESS;
     }
     else
@@ -444,49 +449,39 @@ int __input_Preparse( vlc_object_t *p_parent, input_item_t *p_item )
  *
  * \param the input thread to stop
  */
-void input_StopThread( input_thread_t *p_input )
+static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj )
 {
     vlc_list_t *p_list;
     int i;
 
-    /* Set die for input */
-    vlc_object_kill( p_input );
-    /* FIXME: seems to be duplicated in ControlPush(INPUT_CONTROL_SET_DIE) */
-
-    /* We cannot touch p_input fields directly (we come from another thread),
-     * so use the vlc_object_find way, it's perfectly safe */
-
-    /* Set die for all access */
-    p_list = vlc_list_find( p_input, VLC_OBJECT_ACCESS, FIND_CHILD );
-    for( i = 0; i < p_list->i_count; i++ )
-    {
-        vlc_object_kill( p_list->p_values[i].p_object );
-    }
-    vlc_list_release( p_list );
+    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) )
+        return;
 
-    /* Set die for all stream */
-    p_list = vlc_list_find( p_input, VLC_OBJECT_STREAM, FIND_CHILD );
-    for( i = 0; i < p_list->i_count; i++ )
-    {
-        vlc_object_kill( p_list->p_values[i].p_object );
-    }
-    vlc_list_release( p_list );
+    vlc_object_kill( p_obj );
 
-    /* Set die for all demux */
-    p_list = vlc_list_find( p_input, VLC_OBJECT_DEMUX, FIND_CHILD );
+    p_list = vlc_list_children( p_obj );
     for( i = 0; i < p_list->i_count; i++ )
-    {
-        vlc_object_kill( p_list->p_values[i].p_object );
-    }
+        ObjectKillChildrens( p_input, p_list->p_values[i].p_object );
     vlc_list_release( p_list );
+}
+void input_StopThread( input_thread_t *p_input )
+{
+    /* Set die for input and ALL of this childrens (even (grand-)grand-childrens)
+     * It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to
+     * unlock the control loop */
+    ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
 
     input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
 }
 
 sout_instance_t * input_DetachSout( input_thread_t *p_input )
 {
-    p_input->p->b_owns_its_sout = false;
-    return p_input->p->p_sout;
+    sout_instance_t *p_sout = p_input->p->p_sout;
+    vlc_object_detach( p_sout );
+    p_input->p->p_sout = NULL;
+    return p_sout;
 }
 
 /*****************************************************************************
@@ -494,8 +489,11 @@ sout_instance_t * input_DetachSout( input_thread_t *p_input )
  * This is the "normal" thread that spawns the input processing chain,
  * reads the stream, cleans up and waits
  *****************************************************************************/
-static int Run( input_thread_t *p_input )
+static void* Run( vlc_object_t *p_this )
 {
+    input_thread_t *p_input = (input_thread_t *)p_this;
+    int canc = vlc_savecancel ();
+
     /* Signal that the thread is launched */
     vlc_thread_ready( p_input );
 
@@ -504,21 +502,12 @@ static int Run( input_thread_t *p_input )
         /* If we failed, wait before we are killed, and exit */
         p_input->b_error = true;
 
-        /* FIXME: we don't want to depend on the playlist */
-        playlist_t * p_playlist = vlc_object_find( p_input,
-            VLC_OBJECT_PLAYLIST, FIND_PARENT );
-        if( p_playlist )
-        {
-            playlist_Signal( p_playlist );
-            vlc_object_release( p_playlist );
-        }
-
-        Error( p_input );
+        WaitDie( p_input );
 
         /* Tell we're dead */
         p_input->b_dead = true;
 
-        return 0;
+        return NULL;
     }
 
     MainLoop( p_input );
@@ -538,19 +527,19 @@ static int Run( input_thread_t *p_input )
 
         /* We have finished */
         p_input->b_eof = true;
-        playlist_Signal( pl_Get( p_input ) );
+        input_ChangeState( p_input, END_S );
     }
 
     /* Wait until we are asked to die */
     if( !p_input->b_die )
     {
-        Error( p_input );
+        WaitDie( p_input );
     }
 
     /* Clean up */
     End( p_input );
-
-    return 0;
+    vlc_restorecancel (canc);
+    return NULL;
 }
 
 /*****************************************************************************
@@ -558,10 +547,14 @@ static int Run( input_thread_t *p_input )
  * This is the "just forget me" thread that spawns the input processing chain,
  * reads the stream, cleans up and releases memory
  *****************************************************************************/
-static int RunAndDestroy( input_thread_t *p_input )
+static void* RunAndDestroy( vlc_object_t *p_this )
 {
+    input_thread_t *p_input = (input_thread_t *)p_this;
+    int canc;
+
     /* Signal that the thread is launched */
     vlc_thread_ready( p_input );
+    canc = vlc_savecancel ();
 
     if( Init( p_input ) )
         goto exit;
@@ -591,6 +584,7 @@ static int RunAndDestroy( input_thread_t *p_input )
 exit:
     /* Release memory */
     vlc_object_release( p_input );
+    vlc_restorecancel (canc);
     return 0;
 }
 
@@ -603,6 +597,9 @@ static void MainLoop( input_thread_t *p_input )
     int64_t i_intf_update = 0;
     int i_updates = 0;
 
+    /* Stop the timer */
+    stats_TimerStop( p_input, STATS_TIMER_INPUT_LAUNCHING );
+
     while( !p_input->b_die && !p_input->b_error && !p_input->p->input.b_eof )
     {
         bool b_force_update = false;
@@ -611,7 +608,7 @@ static void MainLoop( input_thread_t *p_input )
         vlc_value_t val;
 
         /* Do the read */
-        if( p_input->i_state != PAUSE_S  )
+        if( p_input->i_state != PAUSE_S )
         {
             if( ( p_input->p->i_stop > 0 && p_input->i_time >= p_input->p->i_stop ) ||
                 ( p_input->p->i_run > 0 && i_start_mdate+p_input->p->i_run < mdate() ) )
@@ -646,7 +643,6 @@ static void MainLoop( input_thread_t *p_input )
                 {
                     /* End of file - we do not set b_die because only the
                      * playlist is allowed to do so. */
-                    input_ChangeState( p_input, END_S );
                     msg_Dbg( p_input, "EOF reached" );
                     p_input->p->input.b_eof = true;
                 }
@@ -753,17 +749,17 @@ static void MainLoop( input_thread_t *p_input )
             }
 
             var_SetBool( p_input, "intf-change", true );
-            i_intf_update = mdate() + I64C(150000);
+            i_intf_update = mdate() + INT64_C(150000);
         }
         /* 150ms * 8 = ~ 1 second */
         if( ++i_updates % 8 == 0 )
         {
             stats_ComputeInputStats( p_input, p_input->p->input.p_item->p_stats );
             /* Are we the thread responsible for computing global stats ? */
-            if( p_input->p_libvlc->p_stats_computer == p_input )
+            if( libvlc_priv (p_input->p_libvlc)->p_stats_computer == p_input )
             {
                 stats_ComputeGlobalStats( p_input->p_libvlc,
-                                     p_input->p_libvlc->p_stats );
+                                          p_input->p_libvlc->p_stats );
             }
         }
     }
@@ -776,7 +772,7 @@ static void InitStatistics( input_thread_t * p_input )
     /* Prepare statistics */
 #define INIT_COUNTER( c, type, compute ) p_input->p->counters.p_##c = \
  stats_CounterCreate( p_input, VLC_VAR_##type, STATS_##compute);
-    if( p_input->p_libvlc->b_stats )
+    if( libvlc_stats (p_input) )
     {
         INIT_COUNTER( read_bytes, INTEGER, COUNTER );
         INIT_COUNTER( read_packets, INTEGER, COUNTER );
@@ -800,6 +796,7 @@ static void InitStatistics( input_thread_t * p_input )
     }
 }
 
+#ifdef ENABLE_SOUT
 static int InitSout( input_thread_t * p_input )
 {
     char *psz;
@@ -843,7 +840,7 @@ static int InitSout( input_thread_t * p_input )
                 return VLC_EGENERIC;
             }
         }
-        if( p_input->p_libvlc->b_stats )
+        if( libvlc_stats (p_input) )
         {
             INIT_COUNTER( sout_sent_packets, INTEGER, COUNTER );
             INIT_COUNTER (sout_sent_bytes, INTEGER, COUNTER );
@@ -864,6 +861,7 @@ static int InitSout( input_thread_t * p_input )
 
     return VLC_SUCCESS;
 }
+#endif
 
 static void InitTitle( input_thread_t * p_input )
 {
@@ -910,6 +908,7 @@ static void StartTitle( input_thread_t * p_input )
     int i, i_delay;
     char *psz;
     char *psz_subtitle;
+    int64_t i_length;
 
     /* Start title/chapter */
 
@@ -930,9 +929,10 @@ static void StartTitle( input_thread_t * p_input )
 
     /* Start time*/
     /* Set start time */
-    p_input->p->i_start = I64C(1000000) * var_GetInteger( p_input, "start-time" );
-    p_input->p->i_stop  = I64C(1000000) * var_GetInteger( p_input, "stop-time" );
-    p_input->p->i_run   = I64C(1000000) * var_GetInteger( p_input, "run-time" );
+    p_input->p->i_start = INT64_C(1000000) * var_GetInteger( p_input, "start-time" );
+    p_input->p->i_stop  = INT64_C(1000000) * var_GetInteger( p_input, "stop-time" );
+    p_input->p->i_run   = INT64_C(1000000) * var_GetInteger( p_input, "run-time" );
+    i_length = var_GetTime( p_input, "length" );
     if( p_input->p->i_run < 0 )
     {
         msg_Warn( p_input, "invalid run-time ignored" );
@@ -941,7 +941,7 @@ static void StartTitle( input_thread_t * p_input )
 
     if( p_input->p->i_start > 0 )
     {
-        if( p_input->p->i_start >= val.i_time )
+        if( p_input->p->i_start >= i_length )
         {
             msg_Warn( p_input, "invalid start-time ignored" );
         }
@@ -950,7 +950,7 @@ static void StartTitle( input_thread_t * p_input )
             vlc_value_t s;
 
             msg_Dbg( p_input, "starting at time: %ds",
-                              (int)( p_input->p->i_start / I64C(1000000) ) );
+                              (int)( p_input->p->i_start / INT64_C(1000000) ) );
 
             s.i_time = p_input->p->i_start;
             input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
@@ -1140,10 +1140,11 @@ static int Init( input_thread_t * p_input )
     }
 
     InitStatistics( p_input );
+#ifdef ENABLE_SOUT
     ret = InitSout( p_input );
-
     if( ret != VLC_SUCCESS )
         return ret; /* FIXME: goto error; should be better here */
+#endif
 
     /* Create es out */
     p_input->p->p_es_out = input_EsOutNew( p_input, p_input->p->i_rate );
@@ -1209,6 +1210,8 @@ static int Init( input_thread_t * p_input )
     /* Access_file does not give any meta, and there are no slave */
     AccessMeta( p_input, p_meta );
 
+    InputUpdateMeta( p_input, p_meta );
+
     if( !p_input->b_preparsing )
     {
         msg_Dbg( p_input, "`%s' successfully opened",
@@ -1226,15 +1229,15 @@ error:
 
     if( p_input->p->p_es_out )
         input_EsOutDelete( p_input->p->p_es_out );
-
+#ifdef ENABLE_SOUT
     if( p_input->p->p_sout )
     {
         vlc_object_detach( p_input->p->p_sout );
         sout_DeleteInstance( p_input->p->p_sout );
     }
+#endif
 
-
-    if( !p_input->b_preparsing && p_input->p_libvlc->b_stats )
+    if( !p_input->b_preparsing && libvlc_stats (p_input) )
     {
 #define EXIT_COUNTER( c ) do { if( p_input->p->counters.p_##c ) \
                                    stats_CounterClean( p_input->p->counters.p_##c );\
@@ -1272,13 +1275,13 @@ error:
 }
 
 /*****************************************************************************
- * Error: RunThread() error loop
+ * WaitDie: Wait until we are asked to die.
  *****************************************************************************
  * This function is called when an error occurred during thread main's loop.
  *****************************************************************************/
-static void Error( input_thread_t *p_input )
+static void WaitDie( input_thread_t *p_input )
 {
-    input_ChangeState( p_input, ERROR_S );
+    input_ChangeState( p_input, p_input->b_error ? ERROR_S : END_S );
     while( !p_input->b_die )
     {
         /* Sleep a while */
@@ -1297,7 +1300,7 @@ static void End( input_thread_t * p_input )
     input_ChangeState( p_input, END_S );
 
     /* Clean control variables */
-    input_ControlVarClean( p_input );
+    input_ControlVarStop( p_input );
 
     /* Clean up master */
     InputSourceClean( &p_input->p->input );
@@ -1317,15 +1320,17 @@ static void End( input_thread_t * p_input )
     if( !p_input->b_preparsing )
     {
 #define CL_CO( c ) stats_CounterClean( p_input->p->counters.p_##c ); p_input->p->counters.p_##c = NULL;
-        if( p_input->p_libvlc->b_stats )
+        if( libvlc_stats (p_input) )
         {
+            libvlc_priv_t *priv = libvlc_priv (p_input->p_libvlc);
+
             /* make sure we are up to date */
             stats_ComputeInputStats( p_input, p_input->p->input.p_item->p_stats );
-            if( p_input->p_libvlc->p_stats_computer == p_input )
+            if( priv->p_stats_computer == p_input )
             {
                 stats_ComputeGlobalStats( p_input->p_libvlc,
                                           p_input->p_libvlc->p_stats );
-                p_input->p_libvlc->p_stats_computer = NULL;
+                priv->p_stats_computer = NULL;
             }
             CL_CO( read_bytes );
             CL_CO( read_packets );
@@ -1364,83 +1369,6 @@ static void End( input_thread_t * p_input )
     p_input->b_dead = true;
 }
 
-static sout_instance_t *SoutFind( vlc_object_t *p_parent, input_item_t *p_item, bool *pb_sout_keep )
-{
-    bool b_keep_sout = var_CreateGetBool( p_parent, "sout-keep" );
-    sout_instance_t *p_sout = NULL;
-    int i;
-
-    /* Search sout-keep options
-     * XXX it has to be done here, but it is duplicated work :( */
-    vlc_mutex_lock( &p_item->lock );
-    for( i = 0; i < p_item->i_options; i++ )
-    {
-        const char *psz_option = p_item->ppsz_options[i];
-        if( !psz_option )
-            continue;
-        if( *psz_option == ':' )
-            psz_option++;
-
-        if( !strcmp( psz_option, "sout-keep" ) )
-            b_keep_sout = true;
-        else if( !strcmp( psz_option, "no-sout-keep" ) || !strcmp( psz_option, "nosout-keep" ) )
-            b_keep_sout = false;
-    }
-    vlc_mutex_unlock( &p_item->lock );
-
-    /* Find a potential sout to reuse
-     * XXX it might be unusable but this will be checked later */
-    if( b_keep_sout )
-    {
-        /* Remove the sout from the playlist garbage collector */
-        /* FIXME: we don't want to depend on the playlist, sout
-         * should be attached to libvlc */
-        playlist_t * p_playlist = vlc_object_find( p_parent,
-            VLC_OBJECT_PLAYLIST, FIND_PARENT );
-        if( p_playlist )
-        {
-            vlc_mutex_lock( &p_playlist->gc_lock );
-            p_sout = vlc_object_find( p_playlist, VLC_OBJECT_SOUT, FIND_CHILD );
-            if( p_sout )
-            {
-                if( p_sout->p_parent != VLC_OBJECT(p_playlist) )
-                {
-                    vlc_object_release( p_sout );
-                    p_sout = NULL;
-                }
-                else
-                {
-                    vlc_object_detach( p_sout );    /* Remove it from the GC */
-
-                    vlc_object_release( p_sout );
-                }
-            }
-            vlc_mutex_unlock( &p_playlist->gc_lock );
-
-            vlc_object_release( p_playlist );
-        }
-    }
-
-    if( pb_sout_keep )
-        *pb_sout_keep = b_keep_sout;
-
-    return p_sout;
-}
-
-static void SoutKeep( sout_instance_t *p_sout )
-{
-    /* FIXME: we don't want to depend on the playlist, sout
-     * should be attached to libvlc */
-    playlist_t * p_playlist = vlc_object_find( p_sout, VLC_OBJECT_PLAYLIST,
-                                               FIND_PARENT );
-    if( !p_playlist ) return;
-
-    msg_Dbg( p_sout, "sout has been kept" );
-    vlc_object_attach( p_sout, p_playlist );
-
-    pl_Release( p_sout );
-}
-
 /*****************************************************************************
  * Control
  *****************************************************************************/
@@ -1526,20 +1454,21 @@ static bool Control( input_thread_t *p_input, int i_type,
     {
         case INPUT_CONTROL_SET_DIE:
             msg_Dbg( p_input, "control: stopping input" );
-            /* Mark all submodules to die */
-            if( p_input->p->input.p_access )
-                vlc_object_kill( p_input->p->input.p_access );
-            if( p_input->p->input.p_stream )
-                vlc_object_kill( p_input->p->input.p_stream );
-            vlc_object_kill( p_input->p->input.p_demux );
 
-            vlc_object_kill( p_input );
+            /* Mark all submodules to die */
+            ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
             break;
 
         case INPUT_CONTROL_SET_POSITION:
         case INPUT_CONTROL_SET_POSITION_OFFSET:
         {
             double f_pos;
+
+            if( p_input->p->b_recording )
+            {
+                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) ignored while recording" );
+                break;
+            }
             if( i_type == INPUT_CONTROL_SET_POSITION )
             {
                 f_pos = val.f_float;
@@ -1577,6 +1506,12 @@ static bool Control( input_thread_t *p_input, int i_type,
             int64_t i_time;
             int i_ret;
 
+            if( p_input->p->b_recording )
+            {
+                msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) ignored while recording" );
+                break;
+            }
+
             if( i_type == INPUT_CONTROL_SET_TIME )
             {
                 i_time = val.i_time;
@@ -1611,7 +1546,7 @@ static bool Control( input_thread_t *p_input, int i_type,
             }
             if( i_ret )
             {
-                msg_Warn( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) "I64Fd
+                msg_Warn( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) %"PRId64
                          " failed or not possible", i_time );
             }
             else
@@ -1648,9 +1583,7 @@ static bool Control( input_thread_t *p_input, int i_type,
                 b_force_update = true;
 
                 /* Switch to play */
-                p_input->i_state = PLAYING_S;
-                val.i_int = PLAYING_S;
-                var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+                input_ChangeStateWithVarCallback( p_input, PLAYING_S, false );
 
                 /* */
                 if( !i_ret )
@@ -1659,7 +1592,7 @@ static bool Control( input_thread_t *p_input, int i_type,
             else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S &&
                      p_input->p->b_can_pause )
             {
-                int i_ret;
+                int i_ret, state;
                 if( p_input->p->input.p_access )
                     i_ret = access_Control( p_input->p->input.p_access,
                                              ACCESS_SET_PAUSE_STATE, true );
@@ -1672,16 +1605,15 @@ static bool Control( input_thread_t *p_input, int i_type,
                 if( i_ret )
                 {
                     msg_Warn( p_input, "cannot set pause state" );
-                    val.i_int = p_input->i_state;
+                    state = p_input->i_state;
                 }
                 else
                 {
-                    val.i_int = PAUSE_S;
+                    state = PAUSE_S;
                 }
 
                 /* Switch to new state */
-                p_input->i_state = val.i_int;
-                var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+                input_ChangeStateWithVarCallback( p_input, state, false );
 
                 /* */
                 if( !i_ret )
@@ -1692,8 +1624,7 @@ static bool Control( input_thread_t *p_input, int i_type,
                 b_force_update = true;
 
                 /* Correct "state" value */
-                val.i_int = p_input->i_state;
-                var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+                input_ChangeStateWithVarCallback( p_input, p_input->i_state, false );
             }
             else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S )
             {
@@ -1836,6 +1767,11 @@ static bool Control( input_thread_t *p_input, int i_type,
         case INPUT_CONTROL_SET_TITLE:
         case INPUT_CONTROL_SET_TITLE_NEXT:
         case INPUT_CONTROL_SET_TITLE_PREV:
+            if( p_input->p->b_recording )
+            {
+                msg_Err( p_input, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
+                break;
+            }
             if( p_input->p->input.b_title_demux &&
                 p_input->p->input.i_title > 0 )
             {
@@ -1883,6 +1819,12 @@ static bool Control( input_thread_t *p_input, int i_type,
         case INPUT_CONTROL_SET_SEEKPOINT:
         case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
         case INPUT_CONTROL_SET_SEEKPOINT_PREV:
+            if( p_input->p->b_recording )
+            {
+                msg_Err( p_input, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
+                break;
+            }
+
             if( p_input->p->input.b_title_demux &&
                 p_input->p->input.i_title > 0 )
             {
@@ -2010,6 +1952,28 @@ static bool Control( input_thread_t *p_input, int i_type,
             }
             break;
 
+        case INPUT_CONTROL_SET_RECORD_STATE:
+            if( !!p_input->p->b_recording != !!val.b_bool )
+            {
+                if( p_input->p->input.b_can_stream_record )
+                {
+                    if( demux_Control( p_input->p->input.p_demux,
+                                       DEMUX_SET_RECORD_STATE, val.b_bool ) )
+                        val.b_bool = false;
+                }
+                else
+                {
+                    if( input_EsOutSetRecord( p_input->p->p_es_out, val.b_bool ) )
+                        val.b_bool = false;
+                }
+                p_input->p->b_recording = val.b_bool;
+
+                var_Change( p_input, "record", VLC_VAR_SETVALUE, &val, NULL );
+
+                b_force_update = true;
+            }
+            break;
+
         case INPUT_CONTROL_SET_BOOKMARK:
         default:
             msg_Err( p_input, "not yet implemented" );
@@ -2103,7 +2067,6 @@ static int UpdateFromAccess( input_thread_t *p_input )
         vlc_meta_t *p_meta = vlc_meta_New();
         access_Control( p_input->p->input.p_access,ACCESS_GET_META, p_meta );
         InputUpdateMeta( p_input, p_meta );
-        var_SetInteger( pl_Get( p_input ), "item-change", p_input->p->input.p_item->i_id );
         p_access->info.i_update &= ~INPUT_UPDATE_META;
     }
 
@@ -2142,14 +2105,6 @@ static int UpdateFromAccess( input_thread_t *p_input )
 static void UpdateItemLength( input_thread_t *p_input, int64_t i_length )
 {
     input_item_SetDuration( p_input->p->input.p_item, (mtime_t) i_length );
-
-    if( !p_input->b_preparsing )
-    {
-        pl_Yield( p_input );
-        var_SetInteger( pl_Get( p_input ), "item-change",
-                        p_input->p->input.p_item->i_id );
-        pl_Release( p_input );
-    }
 }
 
 /*****************************************************************************
@@ -2157,28 +2112,10 @@ static void UpdateItemLength( input_thread_t *p_input, int64_t i_length )
  *****************************************************************************/
 static input_source_t *InputSourceNew( input_thread_t *p_input )
 {
-    input_source_t *in = (input_source_t*) malloc( sizeof( input_source_t ) );
-
-    if( !in )
-    {
-        msg_Err( p_input, "out of memory for new input source" );
-        return NULL;
-    }
-
-    in->p_item   = NULL;
-    in->p_access = NULL;
-    in->p_stream = NULL;
-    in->p_demux  = NULL;
-    in->b_title_demux = false;
-    TAB_INIT( in->i_title, in->title );
-    in->b_can_pause = true;
-    in->b_can_pace_control = true;
-    in->b_can_rate_control = true;
-    in->b_rescale_ts = true;
-    in->b_eof = false;
-    in->f_fps = 0.0;
-    in->i_cr_average = 0;
-
+    VLC_UNUSED(p_input);
+    input_source_t *in = malloc( sizeof( input_source_t ) );
+    if( in )
+        memset( in, 0, sizeof( input_source_t ) );
     return in;
 }
 
@@ -2189,6 +2126,8 @@ static int InputSourceInit( input_thread_t *p_input,
                             input_source_t *in, const char *psz_mrl,
                             const char *psz_forced_demux )
 {
+    const bool b_master = in == &p_input->p->input;
+
     char psz_dup[strlen (psz_mrl) + 1];
     const char *psz_access;
     const char *psz_demux;
@@ -2204,7 +2143,7 @@ static int InputSourceInit( input_thread_t *p_input,
     if( !p_input ) return VLC_EGENERIC;
 
     /* Split uri */
-    MRLSplit( psz_dup, &psz_access, &psz_demux, &psz_path );
+    input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup );
 
     msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
              psz_mrl, psz_access, psz_demux, psz_path );
@@ -2309,7 +2248,8 @@ static int InputSourceInit( input_thread_t *p_input,
     {
         int64_t i_pts_delay;
 
-        input_ChangeState( p_input, OPENING_S );
+        if( b_master )
+            input_ChangeState( p_input, OPENING_S );
 
         /* Now try a real access */
         in->p_access = access_New( p_input, psz_access, psz_demux, psz_path );
@@ -2387,7 +2327,8 @@ static int InputSourceInit( input_thread_t *p_input,
             var_Set( p_input, "seekable", val );
         }
 
-        input_ChangeState( p_input, BUFFERING_S );
+        if( b_master )
+            input_ChangeState( p_input, BUFFERING_S );
 
         /* Create the stream_t */
         in->p_stream = stream_AccessNew( in->p_access, p_input->b_preparsing );
@@ -2411,7 +2352,7 @@ static int InputSourceInit( input_thread_t *p_input,
             {
                 const char *psz_a, *psz_d;
                 psz_buf = strdup( in->p_access->psz_path );
-                MRLSplit( psz_buf, &psz_a, &psz_d, &psz_real_path );
+                input_SplitMRL( &psz_a, &psz_d, &psz_real_path, psz_buf );
             }
             else
             {
@@ -2431,10 +2372,19 @@ static int InputSourceInit( input_thread_t *p_input,
             intf_UserFatal( VLC_OBJECT( p_input ), false,
                             _("VLC can't recognize the input's format"),
                             _("The format of '%s' cannot be detected. "
-                            "Have a look the log for details."), psz_mrl );
+                            "Have a look at the log for details."), psz_mrl );
             goto error;
         }
 
+        if( demux_Control( in->p_demux, DEMUX_CAN_RECORD, &in->b_can_stream_record ) )
+            in->b_can_stream_record = false;
+#ifdef ENABLE_SOUT
+        if( !var_CreateGetBool( p_input, "input-record-native" ) )
+            in->b_can_stream_record = false;
+        var_SetBool( p_input, "can-record", true );
+#else
+        var_SetBool( p_input, "can-record", in->b_can_stream_record );
+#endif
         /* Get title from demux */
         if( !p_input->b_preparsing && in->i_title <= 0 )
         {
@@ -2479,7 +2429,8 @@ static int InputSourceInit( input_thread_t *p_input,
     return VLC_SUCCESS;
 
 error:
-    input_ChangeState( p_input, ERROR_S );
+    if( b_master )
+        input_ChangeState( p_input, ERROR_S );
 
     if( in->p_demux )
         demux_Delete( in->p_demux );
@@ -2629,7 +2580,6 @@ static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta )
     input_item_t *p_item = p_input->p->input.p_item;
     char * psz_arturl = NULL;
     char *psz_title = NULL;
-    int i;
     int i_arturl_event = false;
 
     if( !p_meta )
@@ -2693,19 +2643,6 @@ static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta )
         free( psz_title );
     }
 
-    if( p_meta )
-    {
-        char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags );
-        for( i = 0; ppsz_all_keys[i]; i++ )
-        {
-            input_Control( p_input, INPUT_ADD_INFO, _(VLC_META_INFO_CAT), _(ppsz_all_keys[i]),
-                    vlc_dictionary_value_for_key( &p_meta->extra_tags, ppsz_all_keys[i] ) );
-            free( ppsz_all_keys[i] );
-        }
-        free( ppsz_all_keys );
-        vlc_meta_Delete( p_meta );
-    }
-
     /** \todo handle sout meta */
 }
 
@@ -2807,8 +2744,8 @@ static void DemuxMeta( input_thread_t *p_input, vlc_meta_t *p_meta, demux_t *p_d
  * MRLSplit: parse the access, demux and url part of the
  *           Media Resource Locator.
  *****************************************************************************/
-void MRLSplit( char *psz_dup, const char **ppsz_access, const char **ppsz_demux,
-               char **ppsz_path )
+void input_SplitMRL( const char **ppsz_access, const char **ppsz_demux, char **ppsz_path,
+                     char *psz_dup )
 {
     char *psz_access = NULL;
     char *psz_demux  = NULL;
@@ -2827,15 +2764,34 @@ void MRLSplit( char *psz_dup, const char **ppsz_access, const char **ppsz_demux,
         psz_demux = strchr( psz_access, '/' );
         if( psz_demux )
             *psz_demux++ = '\0';
+
+        /* We really don't want module name substitution here! */
+        if( psz_access[0] == '$' )
+            psz_access++;
+        if( psz_demux && psz_demux[0] == '$' )
+            psz_demux++;
     }
     else
     {
         psz_path = psz_dup;
     }
+    *ppsz_access = psz_access ? psz_access : (char*)"";
+    *ppsz_demux = psz_demux ? psz_demux : (char*)"";
+    *ppsz_path = psz_path;
+}
 
-    *ppsz_access = psz_access ? psz_access : "";
-    *ppsz_demux = psz_demux ? psz_demux : "";
-    *ppsz_path = psz_path ? psz_path : "";
+static inline bool next(char ** src)
+{
+    char *end;
+    errno = 0;
+    long result = strtol( *src, &end, 0 );
+    if( errno != 0 || result >= LONG_MAX || result <= LONG_MIN ||
+        end == *src )
+    {
+        return false;
+    }
+    *src = end;
+    return true;
 }
 
 /*****************************************************************************
@@ -2856,19 +2812,30 @@ static void MRLSections( input_thread_t *p_input, char *psz_source,
     /* Start by parsing titles and chapters */
     if( !psz_source || !( psz = strrchr( psz_source, '@' ) ) ) return;
 
+
     /* Check we are really dealing with a title/chapter section */
     psz_check = psz + 1;
     if( !*psz_check ) return;
-    if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+    if( isdigit(*psz_check) )
+        if(!next(&psz_check)) return;
     if( *psz_check != ':' && *psz_check != '-' && *psz_check ) return;
     if( *psz_check == ':' && ++psz_check )
-        if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+    {
+        if( isdigit(*psz_check) )
+            if(!next(&psz_check)) return;
+    }
     if( *psz_check != '-' && *psz_check ) return;
     if( *psz_check == '-' && ++psz_check )
-        if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+    {
+        if( isdigit(*psz_check) )
+            if(!next(&psz_check)) return;
+    }
     if( *psz_check != ':' && *psz_check ) return;
     if( *psz_check == ':' && ++psz_check )
-        if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+    {
+        if( isdigit(*psz_check) )
+            if(!next(&psz_check)) return;
+    }
     if( *psz_check ) return;
 
     /* Separate start and end */
@@ -2970,3 +2937,45 @@ bool input_AddSubtitles( input_thread_t *p_input, char *psz_subtitle,
 
     return true;
 }
+
+/*****************************************************************************
+ * input_get_event_manager
+ *****************************************************************************/
+vlc_event_manager_t *input_get_event_manager( input_thread_t *p_input )
+{
+    return &p_input->p->event_manager;
+}
+
+/**/
+/* TODO FIXME nearly the same logic that snapshot code */
+char *input_CreateFilename( vlc_object_t *p_obj, const char *psz_path, const char *psz_prefix, const char *psz_extension )
+{
+    char *psz_file;
+    DIR *path;
+
+    path = utf8_opendir( psz_path );
+    if( path )
+    {
+        closedir( path );
+
+        char *psz_tmp = str_format( p_obj, psz_prefix );
+        if( !psz_tmp )
+            return NULL;
+
+        filename_sanitize( psz_tmp );
+        if( asprintf( &psz_file, "%s"DIR_SEP"%s%s%s",
+                      psz_path, psz_tmp,
+                      psz_extension ? "." : "",
+                      psz_extension ? psz_extension : "" ) < 0 )
+            psz_file = NULL;
+        free( psz_tmp );
+        return psz_file;
+    }
+    else
+    {
+        psz_file = str_format( p_obj, psz_path );
+        path_sanitize( psz_file );
+        return psz_file;
+    }
+}
+