]> git.sesse.net Git - vlc/blobdiff - src/input/es_out.c
Moved input_EsOutDecodersIsEmpty to es_out_Control and cleaned its usage.
[vlc] / src / input / es_out.c
index 8940e046c60640e2673099d2953b3a7c07583862..fd14795db374a87d18cc0cb89e296737860cb5f9 100644 (file)
@@ -39,8 +39,9 @@
 #include <vlc_aout.h>
 
 #include "input_internal.h"
-#include "input_clock.h"
-#include "input_decoder.h"
+#include "clock.h"
+#include "decoder.h"
+#include "es_out.h"
 
 #include "../stream_output/stream_output.h"
 
@@ -98,6 +99,9 @@ struct es_out_sys_t
 {
     input_thread_t *p_input;
 
+    /* */
+    vlc_mutex_t   lock;
+
     /* all programs */
     int           i_pgrm;
     es_out_pgrm_t **pgrm;
@@ -137,11 +141,17 @@ struct es_out_sys_t
     /* Rate used for clock */
     int         i_rate;
 
+    /* */
+    bool        b_paused;
+
     /* Current preroll */
-    int64_t     i_preroll_end;
+    mtime_t     i_preroll_end;
 
     /* Used for buffering */
     bool        b_buffering;
+    mtime_t     i_buffering_extra_initial;
+    mtime_t     i_buffering_extra_stream;
+    mtime_t     i_buffering_extra_system;
 
     /* Record */
     sout_instance_t *p_sout_record;
@@ -150,9 +160,10 @@ struct es_out_sys_t
 static es_out_id_t *EsOutAdd    ( es_out_t *, es_format_t * );
 static int          EsOutSend   ( es_out_t *, es_out_id_t *, block_t * );
 static void         EsOutDel    ( es_out_t *, es_out_id_t * );
-static void         EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force );
 static int          EsOutControl( es_out_t *, int i_query, va_list );
+static void         EsOutDelete ( es_out_t *out );
 
+static void         EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force );
 static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );
 
 static bool EsIsSelected( es_out_id_t *es );
@@ -161,6 +172,7 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
+static void EsOutProgramsChangeRate( es_out_t *out );
 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
 static char *LanguageGetName( const char *psz_code );
 static char *LanguageGetCode( const char *psz_lang );
@@ -195,10 +207,11 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
     vlc_value_t  val;
     int i;
 
-    es_out_t     *out = malloc( sizeof( es_out_t ) );
-    if( !out ) return NULL;
+    es_out_t     *out = malloc( sizeof( *out ) );
+    if( !out )
+        return NULL;
 
-    es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );
+    es_out_sys_t *p_sys = malloc( sizeof( *p_sys ) );
     if( !p_sys )
     {
         free( out );
@@ -209,9 +222,12 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
     out->pf_send    = EsOutSend;
     out->pf_del     = EsOutDel;
     out->pf_control = EsOutControl;
+    out->pf_destroy = EsOutDelete;
     out->p_sys      = p_sys;
     out->b_sout     = p_input->p->p_sout != NULL;
 
+
+    vlc_mutex_init_recursive( &p_sys->lock );
     p_sys->p_input = p_input;
 
     p_sys->b_active = false;
@@ -279,9 +295,14 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
     p_sys->i_audio_delay= 0;
     p_sys->i_spu_delay  = 0;
 
+    p_sys->b_paused = false;
+
     p_sys->i_rate = i_rate;
 
     p_sys->b_buffering = true;
+    p_sys->i_buffering_extra_initial = 0;
+    p_sys->i_buffering_extra_stream = 0;
+    p_sys->i_buffering_extra_system = 0;
     p_sys->i_preroll_end = -1;
 
     p_sys->p_sout_record = NULL;
@@ -289,107 +310,14 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
     return out;
 }
 
-/*****************************************************************************
- * input_EsOutDelete:
- *****************************************************************************/
-void input_EsOutDelete( es_out_t *out )
-{
-    es_out_sys_t *p_sys = out->p_sys;
-    int i;
-
-    if( p_sys->p_sout_record )
-        input_EsOutSetRecord( out, false );
-
-    for( i = 0; i < p_sys->i_es; i++ )
-    {
-        if( p_sys->es[i]->p_dec )
-            input_DecoderDelete( p_sys->es[i]->p_dec );
-
-        free( p_sys->es[i]->psz_language );
-        free( p_sys->es[i]->psz_language_code );
-        es_format_Clean( &p_sys->es[i]->fmt );
-
-        free( p_sys->es[i] );
-    }
-    if( p_sys->ppsz_audio_language )
-    {
-        for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
-            free( p_sys->ppsz_audio_language[i] );
-        free( p_sys->ppsz_audio_language );
-    }
-    if( p_sys->ppsz_sub_language )
-    {
-        for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
-            free( p_sys->ppsz_sub_language[i] );
-        free( p_sys->ppsz_sub_language );
-    }
-    free( p_sys->es );
-
-    /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
-    for( i = 0; i < p_sys->i_pgrm; i++ )
-    {
-        es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
-        input_clock_Delete( p_pgrm->p_clock );
-        free( p_pgrm->psz_now_playing );
-        free( p_pgrm->psz_publisher );
-        free( p_pgrm->psz_name );
-        if( p_pgrm->p_epg )
-            vlc_epg_Delete( p_pgrm->p_epg );
-
-        free( p_pgrm );
-    }
-    TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
-
-    free( p_sys );
-    free( out );
-}
-
-es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )
-{
-    int i;
-    if( i_id < 0 )
-    {
-        /* Special HACK, -i_id is tha cat of the stream */
-        return (es_out_id_t*)((uint8_t*)NULL-i_id);
-    }
-
-    for( i = 0; i < out->p_sys->i_es; i++ )
-    {
-        if( out->p_sys->es[i]->i_id == i_id )
-            return out->p_sys->es[i];
-    }
-    return NULL;
-}
-
-mtime_t input_EsOutGetWakeup( es_out_t *out )
-{
-    es_out_sys_t   *p_sys = out->p_sys;
-    input_thread_t *p_input = p_sys->p_input;
-
-    if( !p_sys->p_pgrm )
-        return 0;
-
-    /* We do not have a wake up date if the input cannot have its speed
-     * controlled or sout is imposing its own or while buffering
-     *
-     * FIXME for !p_input->b_can_pace_control a wkeup time is still needed to avoid too strong buffering */
-    if( !p_input->b_can_pace_control ||
-        p_input->p->b_out_pace_control ||
-        p_sys->b_buffering )
-        return 0;
-
-    return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
-}
-
 void input_EsOutChangeRate( es_out_t *out, int i_rate )
 {
     es_out_sys_t      *p_sys = out->p_sys;
-    int i;
 
     p_sys->i_rate = i_rate;
 
-    for( i = 0; i < p_sys->i_pgrm; i++ )
-        input_clock_ChangeRate( p_sys->pgrm[i]->p_clock, i_rate );
+    if( !p_sys->b_paused )
+        EsOutProgramsChangeRate( out );
 }
 
 int input_EsOutSetRecord(  es_out_t *out, bool b_record )
@@ -479,6 +407,8 @@ void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
 }
 void input_EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
 {
+    es_out_sys_t *p_sys = out->p_sys;
+
     /* XXX the order is important */
     if( b_paused )
     {
@@ -487,9 +417,32 @@ void input_EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
     }
     else
     {
+        if( p_sys->i_buffering_extra_initial > 0 )
+        {
+            mtime_t i_stream_start;
+            mtime_t i_system_start;
+            mtime_t i_stream_duration;
+            mtime_t i_system_duration;
+            int i_ret;
+            i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
+                                          &i_stream_start, &i_system_start,
+                                          &i_stream_duration, &i_system_duration );
+            if( !i_ret )
+            {
+                /* FIXME pcr != exactly what wanted */
+                const mtime_t i_used = /*(i_stream_duration - p_sys->p_input->i_pts_delay)*/ p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
+                i_date -= i_used;
+            }
+            p_sys->i_buffering_extra_initial = 0;
+            p_sys->i_buffering_extra_stream = 0;
+            p_sys->i_buffering_extra_system = 0;
+        }
         EsOutProgramChangePause( out, false, i_date );
         EsOutDecodersChangePause( out, false, i_date );
+
+        EsOutProgramsChangeRate( out );
     }
+    p_sys->b_paused = b_paused;
 }
 void input_EsOutChangePosition( es_out_t *out )
 {
@@ -512,10 +465,184 @@ void input_EsOutChangePosition( es_out_t *out )
         input_clock_Reset( p_sys->pgrm[i]->p_clock );
 
     p_sys->b_buffering = true;
+    p_sys->i_buffering_extra_initial = 0;
+    p_sys->i_buffering_extra_stream = 0;
+    p_sys->i_buffering_extra_system = 0;
+    p_sys->i_preroll_end = -1;
+}
+
+void input_EsOutFrameNext( es_out_t *out )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+    es_out_id_t *p_es_video = NULL;
+
+    if( p_sys->b_buffering )
+    {
+        msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" );
+        return;
+    }
+
+    assert( p_sys->b_paused );
+
+    for( int i = 0; i < p_sys->i_es; i++ )
+    {
+        es_out_id_t *p_es = p_sys->es[i];
+
+        if( p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
+        {
+            p_es_video = p_es;
+            break;
+        }
+    }
+
+    if( !p_es_video )
+    {
+        msg_Warn( p_sys->p_input, "No video track selected, ignoring 'frame next'" );
+        return;
+    }
+
+    mtime_t i_duration;
+    input_DecoderFrameNext( p_es_video->p_dec, &i_duration );
+
+    msg_Dbg( out->p_sys->p_input, "input_EsOutFrameNext consummed %d ms", (int)(i_duration/1000) );
+
+    if( i_duration <= 0 )
+        i_duration = 40*1000;
+
+    /* FIXME it is not a clean way ? */
+    if( p_sys->i_buffering_extra_initial <= 0 )
+    {
+        mtime_t i_stream_start;
+        mtime_t i_system_start;
+        mtime_t i_stream_duration;
+        mtime_t i_system_duration;
+        int i_ret;
+
+        i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
+                                      &i_stream_start, &i_system_start,
+                                      &i_stream_duration, &i_system_duration );
+        if( i_ret )
+            return;
+
+        p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->p_input->i_pts_delay; /* FIXME < 0 ? */
+        p_sys->i_buffering_extra_system =
+        p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
+    }
+
+    const int i_rate = input_clock_GetRate( p_sys->p_pgrm->p_clock );
+
+    p_sys->b_buffering = true;
+    p_sys->i_buffering_extra_system += i_duration;
+    p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial +
+                                      ( p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial ) *
+                                                INPUT_RATE_DEFAULT / i_rate;
+
     p_sys->i_preroll_end = -1;
 }
 
-bool input_EsOutDecodersIsEmpty( es_out_t *out )
+void input_EsOutLock( es_out_t *out )
+{
+    vlc_mutex_lock( &out->p_sys->lock );
+}
+void input_EsOutUnlock( es_out_t *out )
+{
+    vlc_mutex_unlock( &out->p_sys->lock );
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+static void EsOutDelete( es_out_t *out )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+    int i;
+
+    if( p_sys->p_sout_record )
+        input_EsOutSetRecord( out, false );
+
+    for( i = 0; i < p_sys->i_es; i++ )
+    {
+        if( p_sys->es[i]->p_dec )
+            input_DecoderDelete( p_sys->es[i]->p_dec );
+
+        free( p_sys->es[i]->psz_language );
+        free( p_sys->es[i]->psz_language_code );
+        es_format_Clean( &p_sys->es[i]->fmt );
+
+        free( p_sys->es[i] );
+    }
+    if( p_sys->ppsz_audio_language )
+    {
+        for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
+            free( p_sys->ppsz_audio_language[i] );
+        free( p_sys->ppsz_audio_language );
+    }
+    if( p_sys->ppsz_sub_language )
+    {
+        for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
+            free( p_sys->ppsz_sub_language[i] );
+        free( p_sys->ppsz_sub_language );
+    }
+    free( p_sys->es );
+
+    /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
+    for( i = 0; i < p_sys->i_pgrm; i++ )
+    {
+        es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
+        input_clock_Delete( p_pgrm->p_clock );
+        free( p_pgrm->psz_now_playing );
+        free( p_pgrm->psz_publisher );
+        free( p_pgrm->psz_name );
+        if( p_pgrm->p_epg )
+            vlc_epg_Delete( p_pgrm->p_epg );
+
+        free( p_pgrm );
+    }
+    TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
+    vlc_mutex_destroy( &p_sys->lock );
+
+    free( p_sys );
+    free( out );
+}
+
+static mtime_t EsOutGetWakeup( es_out_t *out )
+{
+    es_out_sys_t   *p_sys = out->p_sys;
+    input_thread_t *p_input = p_sys->p_input;
+
+    if( !p_sys->p_pgrm )
+        return 0;
+
+    /* We do not have a wake up date if the input cannot have its speed
+     * controlled or sout is imposing its own or while buffering
+     *
+     * FIXME for !p_input->b_can_pace_control a wkeup time is still needed to avoid too strong buffering */
+    if( !p_input->b_can_pace_control ||
+        p_input->p->b_out_pace_control ||
+        p_sys->b_buffering )
+        return 0;
+
+    return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
+}
+
+static es_out_id_t *EsOutGetFromID( es_out_t *out, int i_id )
+{
+    int i;
+    if( i_id < 0 )
+    {
+        /* Special HACK, -i_id is the cat of the stream */
+        return (es_out_id_t*)((uint8_t*)NULL-i_id);
+    }
+
+    for( i = 0; i < out->p_sys->i_es; i++ )
+    {
+        if( out->p_sys->es[i]->i_id == i_id )
+            return out->p_sys->es[i];
+    }
+    return NULL;
+}
+
+static bool EsOutDecodersIsEmpty( es_out_t *out )
 {
     es_out_sys_t      *p_sys = out->p_sys;
     int i;
@@ -539,14 +666,7 @@ bool input_EsOutDecodersIsEmpty( es_out_t *out )
     return true;
 }
 
-bool input_EsOutIsBuffering( es_out_t *out )
-{
-    return out->p_sys->b_buffering;
-}
 
-/*****************************************************************************
- *
- *****************************************************************************/
 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
 {
     es_out_sys_t *p_sys = out->p_sys;
@@ -567,9 +687,14 @@ static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
     if( p_sys->i_preroll_end >= 0 )
         i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
 
-    if( i_stream_duration <= p_sys->p_input->i_pts_delay + i_preroll_duration && !b_forced )
+    const mtime_t i_buffering_duration = p_sys->p_input->i_pts_delay +
+                                         i_preroll_duration +
+                                         p_sys->i_buffering_extra_stream;
+
+    if( i_stream_duration <= i_buffering_duration && !b_forced )
     {
-        msg_Dbg( p_sys->p_input, "Buffering %d%%", (int)(100 * i_stream_duration / ( p_sys->p_input->i_pts_delay + i_preroll_duration )) );
+        msg_Dbg( p_sys->p_input, "Buffering %d%%",
+                 (int)(100 * i_stream_duration / i_buffering_duration ) );
         return;
     }
 
@@ -578,6 +703,12 @@ static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
     p_sys->b_buffering = false;
     p_sys->i_preroll_end = -1;
 
+    if( p_sys->i_buffering_extra_initial > 0 )
+    {
+        /* FIXME wrong ? */
+        return;
+    }
+
     const mtime_t i_decoder_buffering_start = mdate();
     for( int i = 0; i < p_sys->i_es; i++ )
     {
@@ -596,7 +727,7 @@ static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
     const mtime_t i_ts_delay = 10*1000 + /* FIXME CLEANUP thread wake up time*/
                                mdate();
     //msg_Dbg( p_sys->p_input, "==> %lld", i_ts_delay - p_sys->p_input->i_pts_delay );
-    input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, i_ts_delay - p_sys->p_input->i_pts_delay - i_preroll_duration );
+    input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, i_ts_delay - i_buffering_duration );
 
     for( int i = 0; i < p_sys->i_es; i++ )
     {
@@ -619,8 +750,6 @@ static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_da
     {
         es_out_id_t *es = p_sys->es[i];
 
-        /* Send a dummy block to let decoder know that
-         * there is a discontinuity */
         if( es->p_dec )
         {
             input_DecoderChangePause( es->p_dec, b_paused, i_date );
@@ -655,7 +784,13 @@ static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
             input_DecoderChangeDelay( p_es->p_dec_record, i_delay );
     }
 }
+static void EsOutProgramsChangeRate( es_out_t *out )
+{
+    es_out_sys_t      *p_sys = out->p_sys;
 
+    for( int i = 0; i < p_sys->i_pgrm; i++ )
+        input_clock_ChangeRate( p_sys->pgrm[i]->p_clock, p_sys->i_rate );
+}
 
 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
                                      bool b_delete )
@@ -1152,19 +1287,21 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
     es_out_sys_t      *p_sys = out->p_sys;
     input_thread_t    *p_input = p_sys->p_input;
 
-    es_out_id_t       *es = malloc( sizeof( es_out_id_t ) );
-    es_out_pgrm_t     *p_pgrm = NULL;
-    int i;
-
-    if( !es ) return NULL;
-
     if( fmt->i_group < 0 )
     {
         msg_Err( p_input, "invalid group number" );
-        free( es );
         return NULL;
     }
 
+    es_out_id_t       *es = malloc( sizeof( *es ) );
+    es_out_pgrm_t     *p_pgrm = NULL;
+    int i;
+
+    if( !es )
+        return NULL;
+
+    vlc_mutex_lock( &p_sys->lock );
+
     /* Search the program */
     for( i = 0; i < p_sys->i_pgrm; i++ )
     {
@@ -1268,6 +1405,8 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
 
     EsOutAddInfo( out, es );
 
+    vlc_mutex_unlock( &p_sys->lock );
+
     return es;
 }
 
@@ -1667,6 +1806,8 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
         vlc_mutex_unlock( &p_input->p->counters.counters_lock );
     }
 
+    vlc_mutex_lock( &p_sys->lock );
+
     /* Mark preroll blocks */
     if( p_sys->i_preroll_end >= 0 )
     {
@@ -1683,6 +1824,7 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
     if( !es->p_dec )
     {
         block_Release( p_block );
+        vlc_mutex_unlock( &p_sys->lock );
         return VLC_SUCCESS;
     }
 
@@ -1730,6 +1872,8 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
     if( b_cc_new )
         var_SetBool( p_sys->p_input, "intf-change", true );
 
+    vlc_mutex_unlock( &p_sys->lock );
+
     return VLC_SUCCESS;
 }
 
@@ -1742,6 +1886,8 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
     bool b_reselect = false;
     int i;
 
+    vlc_mutex_lock( &p_sys->lock );
+
     /* We don't try to reselect */
     if( es->p_dec )
     {
@@ -1788,17 +1934,21 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
 
     /* Re-select another track when needed */
     if( b_reselect )
+    {
         for( i = 0; i < p_sys->i_es; i++ )
         {
             if( es->fmt.i_cat == p_sys->es[i]->fmt.i_cat )
                 EsOutSelect( out, p_sys->es[i], false );
         }
+    }
 
     free( es->psz_language );
     free( es->psz_language_code );
 
     es_format_Clean( &es->fmt );
 
+    vlc_mutex_unlock( &p_sys->lock );
+
     free( es );
 }
 
@@ -1810,7 +1960,7 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
  * \param args a variable list of arguments for the query
  * \return VLC_SUCCESS or an error code
  */
-static int EsOutControl( es_out_t *out, int i_query, va_list args )
+static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
 {
     es_out_sys_t *p_sys = out->p_sys;
     bool  b, *pb;
@@ -1946,7 +2096,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
             return VLC_SUCCESS;
         }
  
-        case ES_OUT_SET_DEFAULT:
+        case ES_OUT_SET_ES_DEFAULT:
         {
             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
 
@@ -2129,11 +2279,59 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
             return EsOutProgramDel( out, i_group );
         }
 
+        case ES_OUT_GET_WAKE_UP:
+        {
+            mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
+            *pi_wakeup = EsOutGetWakeup( out );
+            return VLC_SUCCESS;
+        }
+
+        case ES_OUT_SET_ES_BY_ID:
+        case ES_OUT_RESTART_ES_BY_ID:
+        case ES_OUT_SET_ES_DEFAULT_BY_ID:
+        {
+            const int i_id = (int)va_arg( args, int );
+            es_out_id_t *p_es = EsOutGetFromID( out, i_id );
+            int i_new_query;
+
+            switch( i_query )
+            {
+            case ES_OUT_SET_ES_BY_ID:         i_new_query = ES_OUT_SET_ES; break;
+            case ES_OUT_RESTART_ES_BY_ID:     i_new_query = ES_OUT_RESTART_ES; break;
+            case ES_OUT_SET_ES_DEFAULT_BY_ID: i_new_query = ES_OUT_SET_ES_DEFAULT; break;
+            default:
+              assert(0);
+            }
+            /* TODO if the lock is made non recursive it should be changed */
+            return es_out_Control( out, i_new_query, p_es );
+        }
+
+        case ES_OUT_GET_BUFFERING:
+            pb = (bool *)va_arg( args, bool* );
+            *pb = p_sys->b_buffering;
+            return VLC_SUCCESS;
+
+        case ES_OUT_GET_EMPTY:
+            pb = (bool *)va_arg( args, bool* );
+            *pb = EsOutDecodersIsEmpty( out );
+            return VLC_SUCCESS;
+
         default:
             msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
             return VLC_EGENERIC;
     }
 }
+static int EsOutControl( es_out_t *out, int i_query, va_list args )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+    int i_ret;
+
+    vlc_mutex_lock( &p_sys->lock );
+    i_ret = EsOutControlLocked( out, i_query, args );
+    vlc_mutex_unlock( &p_sys->lock );
+
+    return i_ret;
+}
 
 /****************************************************************************
  * LanguageGetName: try to expend iso639 into plain name