]> git.sesse.net Git - vlc/blobdiff - src/input/es_out_timeshift.c
Privatized ES_OUT_SET_ACTIVE/MODE and removed ES_OUT_GET_ACTIVE/MODE/GROUP.
[vlc] / src / input / es_out_timeshift.c
index d86628402dd229d17ab32a8470e6b157839150ca..7d3aff311e0ca6b60d220b7f397b3fb2e32cbb95 100644 (file)
@@ -61,43 +61,78 @@ enum
     C_CONTROL,
 };
 
+typedef struct
+{
+    es_out_id_t *p_es;
+    es_format_t *p_fmt;
+} ts_cmd_add_t;
+
+typedef struct
+{
+    es_out_id_t *p_es;
+} ts_cmd_del_t;
+
+typedef struct
+{
+    es_out_id_t *p_es;
+    block_t *p_block;
+} ts_cmd_send_t;
+
+typedef struct
+{
+    int  i_query;
+
+    bool b_bool;
+    int  i_int;
+    int64_t i_i64;
+    vlc_meta_t *p_meta;
+    vlc_epg_t *p_epg;
+    es_out_id_t *p_es;
+    es_format_t *p_fmt;
+} ts_cmd_control_t;
+
 typedef struct
 {
     int     i_type;
     mtime_t i_date;
     union
     {
-        struct
-        {
-            es_out_id_t *p_es;
-            es_format_t *p_fmt;
-        } add;
-        struct
-        {
-            es_out_id_t *p_es;
-        } del;
-        struct
-        {
-            es_out_id_t *p_es;
-            block_t *p_block;
-        } send;
-        struct
-        {
-            int  i_query;
-
-            bool b_bool;
-            bool *pb_bool;
-            int  i_int;
-            int  *pi_int;
-            int64_t i_i64;
-            vlc_meta_t *p_meta;
-            vlc_epg_t *p_epg;
-            es_out_id_t *p_es;
-            es_format_t *p_fmt;
-        } control;
+        ts_cmd_add_t     add;
+        ts_cmd_del_t     del;
+        ts_cmd_send_t    send;
+        ts_cmd_control_t control;
     };
 } ts_cmd_t;
 
+typedef struct
+{
+    VLC_COMMON_MEMBERS
+
+    /* */
+    input_thread_t *p_input;
+    es_out_t       *p_out;
+
+    /* Lock for all following fields */
+    vlc_mutex_t    lock;
+    vlc_cond_t     wait;
+
+    /* */
+    bool           b_paused;
+    mtime_t        i_pause_date;
+
+    /* */
+    int            i_rate;
+    int            i_rate_source;
+    mtime_t        i_rate_date;
+    mtime_t        i_rate_delay;
+
+    /* */
+    int            i_cmd;
+    ts_cmd_t       **pp_cmd;
+    mtime_t        i_cmd_delay;
+
+} ts_thread_t;
+
 struct es_out_id_t
 {
     es_out_id_t *p_es;
@@ -114,28 +149,20 @@ struct es_out_sys_t
 
     /* Lock for all following fields */
     vlc_mutex_t    lock;
-    vlc_cond_t     wait;
 
     /* */
     bool           b_delayed;
-    vlc_object_t   *p_thread;
-
-    bool           b_paused;
-    mtime_t        i_pause_date;
+    ts_thread_t   *p_thread;
 
-    int            i_rate;
-    int            i_rate_source;
-    mtime_t        i_rate_date;
-    mtime_t        i_rate_delay;
+    /* */
+    bool           b_input_paused;
+    bool           b_input_paused_source;
+    int            i_input_rate;
+    int            i_input_rate_source;
 
     /* */
     int            i_es;
     es_out_id_t    **pp_es;
-
-    /* */
-    int            i_cmd;
-    ts_cmd_t       **pp_cmd;
-    mtime_t        i_cmd_delay;
 };
 
 static es_out_id_t *Add    ( es_out_t *, const es_format_t * );
@@ -145,11 +172,18 @@ static int          Control( es_out_t *, int i_query, va_list );
 static void         Destroy( es_out_t * );
 
 static int          TsStart( es_out_t * );
-static void         TsStop( es_out_t * );
+static void         TsAutoStop( es_out_t * );
+
+static void         TsStop( ts_thread_t * );
+static void         TsPushCmd( ts_thread_t *, const ts_cmd_t * );
+static int          TsPopCmdLocked( ts_thread_t *, ts_cmd_t * );
+static bool         TsHasCmd( ts_thread_t * );
+static bool         TsIsUnused( ts_thread_t * );
+static int          TsChangePause( ts_thread_t *, bool b_source_paused, bool b_paused, mtime_t i_date );
+static int          TsChangeRate( ts_thread_t *, int i_src_rate, int i_rate );
+
 static void         *TsRun( vlc_object_t * );
 
-static void CmdPush( es_out_t *, const ts_cmd_t * );
-static int  CmdPop( es_out_t *, ts_cmd_t * );
 static void CmdClean( ts_cmd_t * );
 static void cmd_cleanup_routine( void *p ) { CmdClean( p ); }
 
@@ -158,15 +192,18 @@ static void CmdInitSend   ( ts_cmd_t *, es_out_id_t *, block_t * );
 static int  CmdInitDel    ( ts_cmd_t *, es_out_id_t * );
 static int  CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
 
+/* */
+static void CmdCleanAdd    ( ts_cmd_t * );
+static void CmdCleanSend   ( ts_cmd_t * );
+static void CmdCleanControl( ts_cmd_t *p_cmd );
+
+/* XXX these functions will take the destination es_out_t */
 static void CmdExecuteAdd    ( es_out_t *, ts_cmd_t * );
 static int  CmdExecuteSend   ( es_out_t *, ts_cmd_t * );
 static void CmdExecuteDel    ( es_out_t *, ts_cmd_t * );
 static int  CmdExecuteControl( es_out_t *, ts_cmd_t * );
 
-static void CmdCleanAdd    ( ts_cmd_t * );
-static void CmdCleanSend   ( ts_cmd_t * );
-static void CmdCleanControl( ts_cmd_t *p_cmd );
-
+/* File helpers */
 static char *GetTmpPath( char *psz_path );
 static FILE *GetTmpFile( const char *psz_path );
 
@@ -196,23 +233,19 @@ es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out
     p_out->b_sout     = p_input->p->p_sout != NULL;
 
     /* */
+    p_sys->b_input_paused = false;
+    p_sys->b_input_paused_source = false;
     p_sys->p_input = p_input;
+    p_sys->i_input_rate = i_rate;
+    p_sys->i_input_rate_source = i_rate;
+
     p_sys->p_out = p_next_out;
     vlc_mutex_init_recursive( &p_sys->lock );
-    vlc_cond_init( &p_sys->wait );
 
     p_sys->b_delayed = false;
     p_sys->p_thread = NULL;
-    p_sys->b_paused = false;
-    p_sys->i_pause_date = -1;
-    p_sys->i_rate_source =
-    p_sys->i_rate        = i_rate;
-    p_sys->i_rate_date = -1;
-    p_sys->i_rate_delay = 0;
-    p_sys->i_cmd_delay = 0;
 
     TAB_INIT( p_sys->i_es, p_sys->pp_es );
-    TAB_INIT( p_sys->i_cmd, p_sys->pp_cmd );
 
     /* TODO config
      * timeshift-granularity
@@ -232,16 +265,16 @@ static void Destroy( es_out_t *p_out )
     es_out_sys_t *p_sys = p_out->p_sys;
 
     if( p_sys->b_delayed )
-        TsStop( p_out );
-
-    TAB_CLEAN( p_sys->i_cmd, p_sys->pp_cmd  );
+    {
+        TsStop( p_sys->p_thread );
+        p_sys->b_delayed = false;
+    }
 
     while( p_sys->i_es > 0 )
         Del( p_out, p_sys->pp_es[0] );
     TAB_CLEAN( p_sys->i_es, p_sys->pp_es  );
 
     free( p_sys->psz_tmp_path );
-    vlc_cond_destroy( &p_sys->wait );
     vlc_mutex_destroy( &p_sys->lock );
     free( p_sys );
     free( p_out );
@@ -258,6 +291,8 @@ static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
 
     vlc_mutex_lock( &p_sys->lock );
 
+    TsAutoStop( p_out );
+
     if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
     {
         vlc_mutex_unlock( &p_sys->lock );
@@ -268,9 +303,9 @@ static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
     TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
 
     if( p_sys->b_delayed )
-        CmdPush( p_out, &cmd );
+        TsPushCmd( p_sys->p_thread, &cmd );
     else
-        CmdExecuteAdd( p_out, &cmd );
+        CmdExecuteAdd( p_sys->p_out, &cmd );
 
     vlc_mutex_unlock( &p_sys->lock );
 
@@ -284,11 +319,13 @@ static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
 
     vlc_mutex_lock( &p_sys->lock );
 
+    TsAutoStop( p_out );
+
     CmdInitSend( &cmd, p_es, p_block );
     if( p_sys->b_delayed )
-        CmdPush( p_out, &cmd );
+        TsPushCmd( p_sys->p_thread, &cmd );
     else
-        i_ret = CmdExecuteSend( p_out, &cmd) ;
+        i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
 
     vlc_mutex_unlock( &p_sys->lock );
 
@@ -301,11 +338,13 @@ static void Del( es_out_t *p_out, es_out_id_t *p_es )
 
     vlc_mutex_lock( &p_sys->lock );
 
+    TsAutoStop( p_out );
+
     CmdInitDel( &cmd, p_es );
     if( p_sys->b_delayed )
-        CmdPush( p_out, &cmd );
+        TsPushCmd( p_sys->p_thread, &cmd );
     else
-        CmdExecuteDel( p_out, &cmd );
+        CmdExecuteDel( p_sys->p_out, &cmd );
 
     TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
 
@@ -316,7 +355,7 @@ static int ControlLockedGetEmpty( es_out_t *p_out, bool *pb_empty )
 {
     es_out_sys_t *p_sys = p_out->p_sys;
 
-    if( p_sys->b_delayed && p_sys->i_cmd > 0 )
+    if( p_sys->b_delayed && TsHasCmd( p_sys->p_thread ) )
         *pb_empty = false;
     else
         *pb_empty = es_out_GetEmpty( p_sys->p_out );
@@ -353,81 +392,73 @@ static int ControlLockedGetBuffering( es_out_t *p_out, bool *pb_buffering )
 static int ControlLockedSetPauseState( es_out_t *p_out, bool b_source_paused, bool b_paused, mtime_t i_date )
 {
     es_out_sys_t *p_sys = p_out->p_sys;
-
-    if( !p_sys->b_delayed &&
-        !b_source_paused == !b_paused )
-    {
-        return es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
-    }
-    if( p_sys->p_input->b_can_pace_control )
-    {
-        /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
-         * and so be able to advertize correctly pace control property in access
-         * module */
-        msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
-        return VLC_EGENERIC;
-    }
-
     int i_ret;
-    if( b_paused )
-    {
-        assert( !b_source_paused );
-
-        if( !p_sys->b_delayed )
-            TsStart( p_out );
 
-        i_ret = es_out_SetPauseState( p_sys->p_out, true, true, i_date );
+    if( !p_sys->b_delayed && !b_source_paused == !b_paused )
+    {
+        i_ret = es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
     }
     else
     {
-        i_ret = es_out_SetPauseState( p_sys->p_out, false, false, i_date );
+        i_ret = VLC_EGENERIC;
+        if( !p_sys->p_input->b_can_pace_control )
+        {
+            if( !p_sys->b_delayed )
+                TsStart( p_out );
+            if( p_sys->b_delayed )
+                i_ret = TsChangePause( p_sys->p_thread, b_source_paused, b_paused, i_date );
+        }
+        else
+        {
+            /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
+             * and so be able to advertize correctly pace control property in access
+             * module */
+            msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
+        }
     }
 
     if( !i_ret )
     {
-        if( !b_paused )
-        {
-            assert( p_sys->i_pause_date > 0 );
-
-            p_sys->i_cmd_delay += i_date - p_sys->i_pause_date;
-        }
-
-        p_sys->b_paused = b_paused;
-        p_sys->i_pause_date = i_date;
-
-        vlc_cond_signal( &p_sys->wait );
+        p_sys->b_input_paused_source = b_source_paused;
+        p_sys->b_input_paused = b_paused;
     }
     return i_ret;
 }
 static int ControlLockedSetRate( es_out_t *p_out, int i_src_rate, int i_rate )
 {
     es_out_sys_t *p_sys = p_out->p_sys;
+    int i_ret;
 
-    if( !p_sys->b_delayed &&
-        i_src_rate == i_rate )
+    if( !p_sys->b_delayed && i_src_rate == i_rate )
     {
-        return es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
+        i_ret = es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
     }
-    if( p_sys->p_input->b_can_pace_control )
+    else
     {
-        /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
-         * and so be able to advertize correctly pace control property in access
-         * module */
-        msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
-        return VLC_EGENERIC;
-    }
-
-    p_sys->i_cmd_delay += p_sys->i_rate_delay;
-
-    p_sys->i_rate_date = -1;
-    p_sys->i_rate_delay = 0;
-    p_sys->i_rate = i_rate;
-    p_sys->i_rate_source = i_src_rate;
+        i_ret = VLC_EGENERIC;
+        if( !p_sys->p_input->b_can_pace_control )
+        {
+            if( !p_sys->b_delayed )
+                TsStart( p_out );
+            if( p_sys->b_delayed )
+                i_ret = TsChangeRate( p_sys->p_thread, i_src_rate, i_rate );
+        }
+        else
+        {
+            /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
+             * and so be able to advertize correctly pace control property in access
+             * module */
+            msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
+        }
 
-    if( !p_sys->b_delayed )
-        TsStart( p_out );
+    }
 
-    return es_out_SetRate( p_sys->p_out, i_rate, i_rate );
+    if( !i_ret )
+    {
+        p_sys->i_input_rate_source = i_src_rate;
+        p_sys->i_input_rate = i_rate;
+    }
+    return i_ret;
 }
 static int ControlLockedSetTime( es_out_t *p_out, mtime_t i_date )
 {
@@ -473,11 +504,8 @@ static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
 
     /* Pass-through control */
     case ES_OUT_SET_ACTIVE:
-    case ES_OUT_GET_ACTIVE:
     case ES_OUT_SET_MODE:
-    case ES_OUT_GET_MODE:
     case ES_OUT_SET_GROUP:
-    case ES_OUT_GET_GROUP:
     case ES_OUT_SET_PCR:
     case ES_OUT_SET_GROUP_PCR:
     case ES_OUT_RESET_PCR:
@@ -489,7 +517,6 @@ static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
     case ES_OUT_RESTART_ES:
     case ES_OUT_SET_ES_DEFAULT:
     case ES_OUT_SET_ES_STATE:
-    case ES_OUT_GET_ES_STATE:
     case ES_OUT_SET_ES_FMT:
     {
         ts_cmd_t cmd;
@@ -497,13 +524,27 @@ static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
             return VLC_EGENERIC;
         if( p_sys->b_delayed )
         {
-            CmdPush( p_out, &cmd );
+            TsPushCmd( p_sys->p_thread, &cmd );
             return VLC_SUCCESS;
         }
-        return CmdExecuteControl( p_out, &cmd );
+        return CmdExecuteControl( p_sys->p_out, &cmd );
     }
 
-    /* Special control */
+    /* Special control when delayed */
+    case ES_OUT_GET_ES_STATE:
+    {
+        es_out_id_t *p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
+        bool *pb_enabled = (bool*)va_arg( args, bool* );
+
+        if( p_sys->b_delayed )
+        {
+            *pb_enabled = true;
+            return VLC_SUCCESS;
+        }
+        return es_out_Control( p_sys->p_out, ES_OUT_GET_ES_STATE, p_es, pb_enabled );
+    }
+
+    /* Special internal input control */
     case ES_OUT_GET_EMPTY:
     {
         bool *pb_empty = (bool*)va_arg( args, bool* );
@@ -557,7 +598,11 @@ static int Control( es_out_t *p_out, int i_query, va_list args )
     int i_ret;
 
     vlc_mutex_lock( &p_sys->lock );
+
+    TsAutoStop( p_out );
+
     i_ret = ControlLocked( p_out, i_query, args );
+
     vlc_mutex_unlock( &p_sys->lock );
 
     return i_ret;
@@ -566,24 +611,47 @@ static int Control( es_out_t *p_out, int i_query, va_list args )
 /*****************************************************************************
  *
  *****************************************************************************/
+static void TsDestructor( vlc_object_t *p_this )
+{
+    ts_thread_t *p_ts = (ts_thread_t*)p_this;
+
+    vlc_cond_destroy( &p_ts->wait );
+    vlc_mutex_destroy( &p_ts->lock );
+}
 static int TsStart( es_out_t *p_out )
 {
     es_out_sys_t *p_sys = p_out->p_sys;
+    ts_thread_t *p_ts;
 
     assert( !p_sys->b_delayed );
 
-    p_sys->p_thread = vlc_custom_create( p_sys->p_input, sizeof(vlc_object_t),
-                                         VLC_OBJECT_GENERIC, "es out timeshift" );
-    if( !p_sys->p_thread )
+    p_sys->p_thread = p_ts = vlc_custom_create( p_sys->p_input, sizeof(ts_thread_t),
+                                                VLC_OBJECT_GENERIC, "es out timeshift" );
+    if( !p_ts )
         return VLC_EGENERIC;
 
+    p_ts->p_input = p_sys->p_input;
+    p_ts->p_out = p_sys->p_out;
+    vlc_mutex_init( &p_ts->lock );
+    vlc_cond_init( &p_ts->wait );
+    p_ts->b_paused = p_sys->b_input_paused && !p_sys->b_input_paused_source;
+    p_ts->i_pause_date = p_ts->b_paused ? mdate() : -1;
+    p_ts->i_rate_source = p_sys->i_input_rate_source;
+    p_ts->i_rate        = p_sys->i_input_rate;
+    p_ts->i_rate_date = -1;
+    p_ts->i_rate_delay = 0;
+    p_ts->i_cmd_delay = 0;
+    TAB_INIT( p_ts->i_cmd, p_ts->pp_cmd );
+
+    vlc_object_set_destructor( p_ts, TsDestructor );
+
     p_sys->b_delayed = true;
-    p_sys->p_thread->p_private = p_out;
-    if( vlc_thread_create( p_sys->p_thread, "es out timeshift",
+    if( vlc_thread_create( p_ts, "es out timeshift",
                            TsRun, VLC_THREAD_PRIORITY_INPUT, false ) )
     {
         msg_Err( p_sys->p_input, "cannot create input thread" );
-        vlc_object_release( p_sys->p_thread );
+
+        vlc_object_release( p_ts );
 
         p_sys->b_delayed = false;
         return VLC_EGENERIC;
@@ -591,31 +659,142 @@ static int TsStart( es_out_t *p_out )
 
     return VLC_SUCCESS;
 }
-static void TsStop( es_out_t *p_out )
+static void TsAutoStop( es_out_t *p_out )
 {
     es_out_sys_t *p_sys = p_out->p_sys;
 
-    assert( p_sys->b_delayed );
+    if( !p_sys->b_delayed || !TsIsUnused( p_sys->p_thread ) )
+        return;
 
-    vlc_object_kill( p_sys->p_thread );
-    vlc_thread_join( p_sys->p_thread );
+    msg_Warn( p_sys->p_input, "es out timeshift: auto stop" );
+    TsStop( p_sys->p_thread );
+
+    p_sys->b_delayed = false;
+}
+static void TsStop( ts_thread_t *p_ts )
+{
+    vlc_object_kill( p_ts );
+    vlc_thread_join( p_ts );
 
+    vlc_mutex_lock( &p_ts->lock );
     for( ;; )
     {
         ts_cmd_t cmd;
 
-        if( CmdPop( p_out, &cmd ) )
+        if( TsPopCmdLocked( p_ts, &cmd ) )
             break;
 
         CmdClean( &cmd );
     }
+    vlc_mutex_unlock( &p_ts->lock );
+    TAB_CLEAN( p_ts->i_cmd, p_ts->pp_cmd  );
 
-    p_sys->b_delayed = false;
+    vlc_object_release( p_ts );
+}
+static void TsPushCmd( ts_thread_t *p_ts, const ts_cmd_t *p_cmd )
+{
+    ts_cmd_t *p_dup = malloc( sizeof(*p_dup) );
+
+    if( p_dup )
+    {
+        *p_dup = *p_cmd;
+
+        vlc_mutex_lock( &p_ts->lock );
+
+        TAB_APPEND( p_ts->i_cmd, p_ts->pp_cmd, p_dup );
+        vlc_cond_signal( &p_ts->wait );
+
+        vlc_mutex_unlock( &p_ts->lock );
+    }
+}
+static int TsPopCmdLocked( ts_thread_t *p_ts, ts_cmd_t *p_cmd )
+{
+    vlc_assert_locked( &p_ts->lock );
+
+    if( p_ts->i_cmd <= 0 )
+        return VLC_EGENERIC;
+
+    *p_cmd = *p_ts->pp_cmd[0];
+
+    free( p_ts->pp_cmd[0] );
+    TAB_REMOVE( p_ts->i_cmd, p_ts->pp_cmd, p_ts->pp_cmd[0] );
+    return VLC_SUCCESS;
+}
+static bool TsHasCmd( ts_thread_t *p_ts )
+{
+    bool b_cmd;
+
+    vlc_mutex_lock( &p_ts->lock );
+    b_cmd =  p_ts->i_cmd > 0;
+    vlc_mutex_unlock( &p_ts->lock );
+
+    return b_cmd;
+}
+static bool TsIsUnused( ts_thread_t *p_ts )
+{
+    bool b_unused;
+
+    vlc_mutex_lock( &p_ts->lock );
+    b_unused = !p_ts->b_paused &&
+               p_ts->i_rate == p_ts->i_rate_source &&
+               p_ts->i_cmd <= 0;
+    vlc_mutex_unlock( &p_ts->lock );
+
+    return b_unused;
+}
+static int TsChangePause( ts_thread_t *p_ts, bool b_source_paused, bool b_paused, mtime_t i_date )
+{
+    vlc_mutex_lock( &p_ts->lock );
+
+    int i_ret;
+    if( b_paused )
+    {
+        assert( !b_source_paused );
+        i_ret = es_out_SetPauseState( p_ts->p_out, true, true, i_date );
+    }
+    else
+    {
+        i_ret = es_out_SetPauseState( p_ts->p_out, false, false, i_date );
+    }
+
+    if( !i_ret )
+    {
+        if( !b_paused )
+        {
+            assert( p_ts->i_pause_date > 0 );
+
+            p_ts->i_cmd_delay += i_date - p_ts->i_pause_date;
+        }
+
+        p_ts->b_paused = b_paused;
+        p_ts->i_pause_date = i_date;
+
+        vlc_cond_signal( &p_ts->wait );
+    }
+    vlc_mutex_unlock( &p_ts->lock );
+    return i_ret;
+}
+static int TsChangeRate( ts_thread_t *p_ts, int i_src_rate, int i_rate )
+{
+    int i_ret;
+
+    vlc_mutex_lock( &p_ts->lock );
+    p_ts->i_cmd_delay += p_ts->i_rate_delay;
+
+    p_ts->i_rate_date = -1;
+    p_ts->i_rate_delay = 0;
+    p_ts->i_rate = i_rate;
+    p_ts->i_rate_source = i_src_rate;
+
+    i_ret = es_out_SetRate( p_ts->p_out, i_rate, i_rate );
+    vlc_mutex_unlock( &p_ts->lock );
+
+    return i_ret;
 }
+
 static void *TsRun( vlc_object_t *p_thread )
 {
-    es_out_t *p_out = p_thread->p_private;
-    es_out_sys_t *p_sys = p_out->p_sys;
+    ts_thread_t *p_ts = (ts_thread_t*)p_thread;
 
     for( ;; )
     {
@@ -623,81 +802,80 @@ static void *TsRun( vlc_object_t *p_thread )
         mtime_t  i_deadline;
 
         /* Pop a command to execute */
-        vlc_mutex_lock( &p_sys->lock );
-        mutex_cleanup_push( &p_sys->lock );
+        vlc_mutex_lock( &p_ts->lock );
+        mutex_cleanup_push( &p_ts->lock );
 
-        while( p_sys->b_paused || CmdPop( p_out, &cmd ) )
-            vlc_cond_wait( &p_sys->wait, &p_sys->lock );
+        while( p_ts->b_paused || TsPopCmdLocked( p_ts, &cmd ) )
+            vlc_cond_wait( &p_ts->wait, &p_ts->lock );
 
-        if( p_sys->i_rate_date < 0 )
-            p_sys->i_rate_date = cmd.i_date;
+        if( p_ts->i_rate_date < 0 )
+            p_ts->i_rate_date = cmd.i_date;
 
-        p_sys->i_rate_delay = 0;
-        if( p_sys->i_rate_source != p_sys->i_rate )
+        p_ts->i_rate_delay = 0;
+        if( p_ts->i_rate_source != p_ts->i_rate )
         {
-            const mtime_t i_duration = cmd.i_date - p_sys->i_rate_date;
-            p_sys->i_rate_delay = i_duration * p_sys->i_rate / p_sys->i_rate_source - i_duration;
+            const mtime_t i_duration = cmd.i_date - p_ts->i_rate_date;
+            p_ts->i_rate_delay = i_duration * p_ts->i_rate / p_ts->i_rate_source - i_duration;
         }
-        if( p_sys->i_cmd_delay + p_sys->i_rate_delay < 0 )
+        if( p_ts->i_cmd_delay + p_ts->i_rate_delay < 0 )
         {
+            const int canc = vlc_savecancel();
+
             /* Auto reset to rate 1.0 */
-            msg_Warn( p_sys->p_input, "es out timeshift: auto reset rate to %d", p_sys->i_rate_source );
+            msg_Warn( p_ts->p_input, "es out timeshift: auto reset rate to %d", p_ts->i_rate_source );
 
-            p_sys->i_cmd_delay = 0;
+            p_ts->i_cmd_delay = 0;
 
-            p_sys->i_rate_date = -1;
-            p_sys->i_rate_delay = 0;
-            p_sys->i_rate = p_sys->i_rate_source;
+            p_ts->i_rate_date = -1;
+            p_ts->i_rate_delay = 0;
+            p_ts->i_rate = p_ts->i_rate_source;
 
-            if( !es_out_SetRate( p_sys->p_out, p_sys->i_rate_source, p_sys->i_rate ) )
+            if( !es_out_SetRate( p_ts->p_out, p_ts->i_rate_source, p_ts->i_rate ) )
             {
-                vlc_value_t val = { .i_int = p_sys->i_rate };
+                vlc_value_t val = { .i_int = p_ts->i_rate };
                 /* Warn back input
                  * FIXME it is perfectly safe BUT it is ugly as it may hide a
                  * rate change requested by user */
-                input_ControlPush( p_sys->p_input, INPUT_CONTROL_SET_RATE, &val );
+                input_ControlPush( p_ts->p_input, INPUT_CONTROL_SET_RATE, &val );
             }
+
+            vlc_restorecancel( canc );
         }
-        i_deadline = cmd.i_date + p_sys->i_cmd_delay + p_sys->i_rate_delay;
+        i_deadline = cmd.i_date + p_ts->i_cmd_delay + p_ts->i_rate_delay;
 
         vlc_cleanup_run();
 
         /* Regulate the speed of command processing to the same one than
          * reading  */
-        //fprintf( stderr, "d=%d - %d\n", (int)(p_sys->i_cmd_delay/1000), (int)( (mdate() - cmd.i_date - p_sys->i_cmd_delay)/1000) );
         vlc_cleanup_push( cmd_cleanup_routine, &cmd );
 
         mwait( i_deadline );
 
         vlc_cleanup_pop();
 
-        /* Execute the command */
+        /* Execute the command  */
         const int canc = vlc_savecancel();
-
-        vlc_mutex_lock( &p_sys->lock );
         switch( cmd.i_type )
         {
         case C_ADD:
-            CmdExecuteAdd( p_out, &cmd );
+            CmdExecuteAdd( p_ts->p_out, &cmd );
             CmdCleanAdd( &cmd );
             break;
         case C_SEND:
-            CmdExecuteSend( p_out, &cmd );
+            CmdExecuteSend( p_ts->p_out, &cmd );
             CmdCleanSend( &cmd );
             break;
         case C_CONTROL:
-            CmdExecuteControl( p_out, &cmd );
+            CmdExecuteControl( p_ts->p_out, &cmd );
             CmdCleanControl( &cmd );
             break;
         case C_DEL:
-            CmdExecuteDel( p_out, &cmd );
+            CmdExecuteDel( p_ts->p_out, &cmd );
             break;
         default:
             assert(0);
             break;
         }
-        vlc_mutex_unlock( &p_sys->lock );
-
         vlc_restorecancel( canc );
     }
 
@@ -707,33 +885,6 @@ static void *TsRun( vlc_object_t *p_thread )
 /*****************************************************************************
  *
  *****************************************************************************/
-static void CmdPush( es_out_t *p_out, const ts_cmd_t *p_cmd )
-{
-    es_out_sys_t *p_sys = p_out->p_sys;
-    ts_cmd_t *p_dup = malloc( sizeof(*p_dup) );
-
-    if( p_dup )
-    {
-        *p_dup = *p_cmd;
-        TAB_APPEND( p_sys->i_cmd, p_sys->pp_cmd, p_dup );
-
-        vlc_cond_signal( &p_sys->wait );
-    }
-}
-static int CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd )
-{
-    es_out_sys_t *p_sys = p_out->p_sys;
-
-    if( p_sys->i_cmd <= 0 )
-        return VLC_EGENERIC;
-
-    *p_cmd = *p_sys->pp_cmd[0];
-
-    free( p_sys->pp_cmd[0] );
-    TAB_REMOVE( p_sys->i_cmd, p_sys->pp_cmd, p_sys->pp_cmd[0] );
-    return VLC_SUCCESS;
-}
-
 static void CmdClean( ts_cmd_t *p_cmd )
 {
     switch( p_cmd->i_type )
@@ -775,9 +926,7 @@ static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_
 }
 static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
 {
-    es_out_sys_t *p_sys = p_out->p_sys;
-
-    p_cmd->add.p_es->p_es = es_out_Add( p_sys->p_out, p_cmd->add.p_fmt );
+    p_cmd->add.p_es->p_es = es_out_Add( p_out, p_cmd->add.p_fmt );
 }
 static void CmdCleanAdd( ts_cmd_t *p_cmd )
 {
@@ -794,7 +943,6 @@ static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
 }
 static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
 {
-    es_out_sys_t *p_sys = p_out->p_sys;
     block_t *p_block = p_cmd->send.p_block;
 
     p_cmd->send.p_block = NULL;
@@ -802,7 +950,7 @@ static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
     if( p_block )
     {
         if( p_cmd->send.p_es->p_es )
-            return es_out_Send( p_sys->p_out, p_cmd->send.p_es->p_es, p_block );
+            return es_out_Send( p_out, p_cmd->send.p_es->p_es, p_block );
         block_Release( p_block );
     }
     return VLC_EGENERIC;
@@ -822,10 +970,8 @@ static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
 }
 static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
 {
-    es_out_sys_t *p_sys = p_out->p_sys;
-
     if( p_cmd->del.p_es->p_es )
-        es_out_Del( p_sys->p_out, p_cmd->del.p_es->p_es );
+        es_out_Del( p_out, p_cmd->del.p_es->p_es );
     free( p_cmd->del.p_es );
 }
 
@@ -845,21 +991,12 @@ static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_co
         p_cmd->control.b_bool = (bool)va_arg( args, int );
         break;
 
-    case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
-        p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
-        break;
-
     case ES_OUT_SET_MODE:    /* arg1= int                            */
     case ES_OUT_SET_GROUP:   /* arg1= int                            */
     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
         p_cmd->control.i_int = (int)va_arg( args, int );
         break;
 
-    case ES_OUT_GET_MODE:    /* arg2= int*                           */
-    case ES_OUT_GET_GROUP:   /* arg1= int*                           */
-        p_cmd->control.pi_int = (int*)va_arg( args, int * );
-        break;
-
     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
         p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
@@ -933,11 +1070,6 @@ static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_co
         p_cmd->control.b_bool = (bool)va_arg( args, int );
         break;
 
-    case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
-        p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
-        p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
-        break;
-
     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
     {
         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
@@ -966,61 +1098,49 @@ static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_co
 }
 static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
 {
-    es_out_sys_t *p_sys = p_out->p_sys;
     const int i_query = p_cmd->control.i_query;
 
     switch( i_query )
     {
     /* Pass-through control */
     case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.b_bool );
-
-    case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pb_bool );
+        return es_out_Control( p_out, i_query, p_cmd->control.b_bool );
 
     case ES_OUT_SET_MODE:    /* arg1= int                            */
     case ES_OUT_SET_GROUP:   /* arg1= int                            */
     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int );
-
-    case ES_OUT_GET_MODE:    /* arg2= int*                           */
-    case ES_OUT_GET_GROUP:   /* arg1= int*                           */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pi_int );
+        return es_out_Control( p_out, i_query, p_cmd->control.i_int );
 
     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_i64 );
+        return es_out_Control( p_out, i_query, p_cmd->control.i_i64 );
 
     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.i_i64 );
+        return es_out_Control( p_out, i_query, p_cmd->control.i_int, p_cmd->control.i_i64 );
 
     case ES_OUT_RESET_PCR:           /* no arg */
-        return es_out_Control( p_sys->p_out, i_query );
+        return es_out_Control( p_out, i_query );
 
     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_meta );
+        return es_out_Control( p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_meta );
 
     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_epg );
+        return es_out_Control( p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_epg );
 
     /* Modified control */
     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es );
+        return es_out_Control( p_out, i_query, p_cmd->control.p_es->p_es );
 
     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.b_bool );
-
-    case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.pb_bool );
+        return es_out_Control( p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.b_bool );
 
     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
-        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.p_fmt );
+        return es_out_Control( p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.p_fmt );
 
     default:
         assert(0);
-        msg_Err( p_sys->p_input, "Unknown es_out_Control query in CmdExecuteControl!" );
         return VLC_EGENERIC;
     }
 }