]> git.sesse.net Git - vlc/blobdiff - src/input/es_out.c
Added INPUT_EVENT_TELETEXT.
[vlc] / src / input / es_out.c
index 93a3cb6563ced7960651122af9b4339617287630..8ac7abb7347e6bddbde55c7a3d308df88551f21b 100644 (file)
@@ -42,6 +42,7 @@
 #include "clock.h"
 #include "decoder.h"
 #include "es_out.h"
+#include "event.h"
 
 #include "../stream_output/stream_output.h"
 
@@ -99,6 +100,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;
@@ -138,23 +142,31 @@ 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;
 };
 
-static es_out_id_t *EsOutAdd    ( es_out_t *, es_format_t * );
+static es_out_id_t *EsOutAdd    ( es_out_t *, const 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 * );
 
+static void         EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force );
 static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );
+static int          EsOutSetRecord(  es_out_t *, bool b_record );
 
 static bool EsIsSelected( es_out_id_t *es );
 static void EsSelect( es_out_t *out, es_out_id_t *es );
@@ -162,6 +174,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 );
@@ -196,10 +209,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 );
@@ -210,9 +224,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;
@@ -280,9 +297,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;
@@ -291,15 +313,15 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
 }
 
 /*****************************************************************************
- * input_EsOutDelete:
+ *
  *****************************************************************************/
-void input_EsOutDelete( es_out_t *out )
+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 );
+        EsOutSetRecord( out, false );
 
     for( i = 0; i < p_sys->i_es; i++ )
     {
@@ -340,29 +362,13 @@ void input_EsOutDelete( es_out_t *out )
         free( p_pgrm );
     }
     TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
+    vlc_mutex_destroy( &p_sys->lock );
 
     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 )
+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;
@@ -382,18 +388,61 @@ mtime_t input_EsOutGetWakeup( es_out_t *out )
     return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
 }
 
-void input_EsOutChangeRate( es_out_t *out, int i_rate )
+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;
 
-    p_sys->i_rate = i_rate;
+    if( p_sys->b_buffering && p_sys->p_pgrm )
+    {
+        EsOutDecodersStopBuffering( out, true );
+        if( p_sys->b_buffering )
+            return true;
+    }
 
-    for( i = 0; i < p_sys->i_pgrm; i++ )
-        input_clock_ChangeRate( p_sys->pgrm[i]->p_clock, i_rate );
+    for( i = 0; i < p_sys->i_es; i++ )
+    {
+        es_out_id_t *es = p_sys->es[i];
+
+        if( es->p_dec && !input_DecoderIsEmpty( es->p_dec ) )
+            return false;
+        if( es->p_dec_record && !input_DecoderIsEmpty( es->p_dec_record ) )
+            return false;
+    }
+    return true;
+}
+
+static void EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+
+    if( i_cat == AUDIO_ES )
+        p_sys->i_audio_delay = i_delay;
+    else if( i_cat == SPU_ES )
+        p_sys->i_spu_delay = i_delay;
+
+    for( int i = 0; i < p_sys->i_es; i++ )
+        EsOutDecoderChangeDelay( out, p_sys->es[i] );
 }
 
-int input_EsOutSetRecord(  es_out_t *out, bool b_record )
+static int EsOutSetRecord(  es_out_t *out, bool b_record )
 {
     es_out_sys_t   *p_sys = out->p_sys;
     input_thread_t *p_input = p_sys->p_input;
@@ -466,20 +515,10 @@ int input_EsOutSetRecord(  es_out_t *out, bool b_record )
 
     return VLC_SUCCESS;
 }
-void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
+static void EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
 {
     es_out_sys_t *p_sys = out->p_sys;
 
-    if( i_cat == AUDIO_ES )
-        p_sys->i_audio_delay = i_delay;
-    else if( i_cat == SPU_ES )
-        p_sys->i_spu_delay = i_delay;
-
-    for( int i = 0; i < p_sys->i_es; i++ )
-        EsOutDecoderChangeDelay( out, p_sys->es[i] );
-}
-void input_EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
-{
     /* XXX the order is important */
     if( b_paused )
     {
@@ -488,11 +527,45 @@ 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 )
+
+static void EsOutChangeRate( es_out_t *out, int i_rate )
+{
+    es_out_sys_t      *p_sys = out->p_sys;
+
+    p_sys->i_rate = i_rate;
+
+    if( !p_sys->b_paused )
+        EsOutProgramsChangeRate( out );
+}
+
+static void EsOutChangePosition( es_out_t *out )
 {
     es_out_sys_t      *p_sys = out->p_sys;
 
@@ -513,41 +586,14 @@ 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;
 }
 
-bool input_EsOutDecodersIsEmpty( es_out_t *out )
-{
-    es_out_sys_t      *p_sys = out->p_sys;
-    int i;
-
-    if( p_sys->b_buffering && p_sys->p_pgrm )
-    {
-        EsOutDecodersStopBuffering( out, true );
-        if( p_sys->b_buffering )
-            return true;
-    }
-
-    for( i = 0; i < p_sys->i_es; i++ )
-    {
-        es_out_id_t *es = p_sys->es[i];
 
-        if( es->p_dec && !input_DecoderIsEmpty( es->p_dec ) )
-            return false;
-        if( es->p_dec_record && !input_DecoderIsEmpty( es->p_dec_record ) )
-            return false;
-    }
-    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;
@@ -568,9 +614,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;
     }
 
@@ -579,6 +630,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++ )
     {
@@ -597,7 +654,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++ )
     {
@@ -620,8 +677,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 );
@@ -656,6 +711,83 @@ 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 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, "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;
+}
+
 
 
 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
@@ -666,30 +798,25 @@ static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt,
     const  bool b_teletext = fmt->i_cat == SPU_ES && fmt->i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' );
     vlc_value_t       val, text;
 
-    const char *psz_var;
-
-    if( fmt->i_cat == AUDIO_ES )
-        psz_var = "audio-es";
-    else if( fmt->i_cat == VIDEO_ES )
-        psz_var = "video-es";
-    else if( fmt->i_cat == SPU_ES )
-        psz_var = "spu-es";
-    else
-        return;
-
     if( b_delete )
     {
+        /* TODO it should probably be a list */
         if( b_teletext )
-            var_SetInteger( p_sys->p_input, "teletext-es", -1 );
-
-        val.i_int = i_id;
-        var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
+            input_SendEventTeletext( p_sys->p_input, -1 );
 
-        var_SetBool( p_sys->p_input, "intf-change", true );
+        input_SendEventEsDel( p_input, SPU_ES, i_id );
         return;
     }
 
     /* Get the number of ES already added */
+    const char *psz_var;
+    if( fmt->i_cat == AUDIO_ES )
+        psz_var = "audio-es";
+    else if( fmt->i_cat == VIDEO_ES )
+        psz_var = "video-es";
+    else
+        psz_var = "spu-es";
+
     var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
     if( val.i_int == 0 )
     {
@@ -727,18 +854,16 @@ static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt,
         }
     }
 
-    val.i_int = i_id;
-    var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
+    input_SendEventEsAdd( p_input, fmt->i_cat, i_id, text.psz_string );
 
     free( text.psz_string );
 
     if( b_teletext )
     {
+        /* TODO it should probably be a list */
         if( var_GetInteger( p_sys->p_input, "teletext-es" ) < 0 )
-            var_SetInteger( p_sys->p_input, "teletext-es", i_id );
+            input_SendEventTeletext( p_sys->p_input, i_id );
     }
-
-    var_SetBool( p_sys->p_input, "intf-change", true );
 }
 
 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
@@ -754,7 +879,6 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
 {
     es_out_sys_t      *p_sys = out->p_sys;
     input_thread_t    *p_input = p_sys->p_input;
-    vlc_value_t       val;
     int               i;
 
     if( p_sys->p_pgrm == p_pgrm )
@@ -786,14 +910,16 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
     p_sys->p_pgrm = p_pgrm;
 
     /* Update "program" */
-    val.i_int = p_pgrm->i_id;
-    var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
+    input_SendEventProgramSelect( p_input, p_pgrm->i_id );
 
     /* Update "es-*" */
-    var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
-    var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
-    var_Change( p_input, "spu-es",   VLC_VAR_CLEARCHOICES, NULL, NULL );
+    input_SendEventEsDel( p_input, AUDIO_ES, -1 );
+    input_SendEventEsDel( p_input, VIDEO_ES, -1 );
+    input_SendEventEsDel( p_input, SPU_ES, -1 );
+
+    /* TODO event */
     var_SetInteger( p_input, "teletext-es", -1 );
+
     for( i = 0; i < p_sys->i_es; i++ )
     {
         if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
@@ -807,7 +933,7 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
     input_item_SetPublisher( p_input->p->input.p_item,
                              p_pgrm->psz_publisher );
 
-    var_SetBool( p_sys->p_input, "intf-change", true );
+    input_SendEventMeta( p_input );
 }
 
 /* EsOutAddProgram:
@@ -817,7 +943,6 @@ static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
 {
     es_out_sys_t      *p_sys = out->p_sys;
     input_thread_t    *p_input = p_sys->p_input;
-    vlc_value_t       val;
 
     es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
     if( !p_pgrm )
@@ -842,17 +967,11 @@ static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
     TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
 
     /* Update "program" variable */
-    val.i_int = i_group;
-    var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
+    input_SendEventProgramAdd( p_input, i_group, NULL );
 
     if( i_group == var_GetInteger( p_input, "program" ) )
-    {
         EsOutProgramSelect( out, p_pgrm );
-    }
-    else
-    {
-        var_SetBool( p_sys->p_input, "intf-change", true );
-    }
+
     return p_pgrm;
 }
 
@@ -864,7 +983,6 @@ static int EsOutProgramDel( es_out_t *out, int i_group )
     es_out_sys_t      *p_sys = out->p_sys;
     input_thread_t    *p_input = p_sys->p_input;
     es_out_pgrm_t     *p_pgrm = NULL;
-    vlc_value_t       val;
     int               i;
 
     for( i = 0; i < p_sys->i_pgrm; i++ )
@@ -902,10 +1020,7 @@ static int EsOutProgramDel( es_out_t *out, int i_group )
     free( p_pgrm );
 
     /* Update "program" variable */
-    val.i_int = i_group;
-    var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
-
-    var_SetBool( p_sys->p_input, "intf-change", true );
+    input_SendEventProgramDel( p_input, i_group );
 
     return VLC_SUCCESS;
 }
@@ -967,9 +1082,6 @@ static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
     /* Update the description text of the program */
     if( psz_title && *psz_title )
     {
-        vlc_value_t val;
-        vlc_value_t text;
-
         if( !p_pgrm->psz_name || strcmp( p_pgrm->psz_name, psz_title ) )
         {
             char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
@@ -982,22 +1094,24 @@ static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
         free( p_pgrm->psz_name );
         p_pgrm->psz_name = strdup( psz_title );
 
-        /* ugly but it works */
-        val.i_int = i_group;
-        var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
-
+        char *psz_text;
         if( psz_provider && *psz_provider )
         {
-            if( asprintf( &text.psz_string, "%s [%s]", psz_title, psz_provider ) != -1 )
-            {
-                var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
-                free( text.psz_string );
-            }
+            if( asprintf( &psz_text, "%s [%s]", psz_title, psz_provider ) < 0 )
+                psz_text = NULL;
         }
         else
         {
-            text.psz_string = (char *)psz_title;
-            var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
+            psz_text = strdup( psz_title );
+        }
+
+        /* ugly but it works */
+        if( psz_text )
+        {
+            input_SendEventProgramDel( p_input, i_group );
+            input_SendEventProgramAdd( p_input, i_group, psz_text );
+
+            free( psz_text );
         }
     }
 
@@ -1005,7 +1119,10 @@ static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
     if( psz_provider )
     {
         if( p_sys->p_pgrm == p_pgrm )
+        {
             input_item_SetPublisher( p_input->p->input.p_item, psz_provider );
+            input_SendEventMeta( p_input );
+        }
         input_Control( p_input, INPUT_ADD_INFO, psz_cat, input_MetaTypeToLocalizedString(vlc_meta_Publisher), psz_provider );
     }
     char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags );
@@ -1128,7 +1245,10 @@ static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg )
         p_pgrm->psz_now_playing = strdup( p_epg->p_current->psz_name );
 
     if( p_pgrm == p_sys->p_pgrm )
+    {
         input_item_SetNowPlaying( p_input->p->input.p_item, p_pgrm->psz_now_playing );
+        input_SendEventMeta( p_input );
+    }
 
     if( p_pgrm->psz_now_playing )
     {
@@ -1148,24 +1268,26 @@ static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg )
 /* EsOutAdd:
  *  Add an es_out
  */
-static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
+static es_out_id_t *EsOutAdd( es_out_t *out, const 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++ )
     {
@@ -1185,13 +1307,13 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
     p_pgrm->i_es++;
 
     /* Set up ES */
-    if( fmt->i_id < 0 )
-        fmt->i_id = out->p_sys->i_id;
-    es->i_id = fmt->i_id;
     es->p_pgrm = p_pgrm;
     es_format_Copy( &es->fmt, fmt );
+    if( es->fmt.i_id < 0 )
+        es->fmt.i_id = out->p_sys->i_id;
+    es->i_id = es->fmt.i_id;
 
-    switch( fmt->i_cat )
+    switch( es->fmt.i_cat )
     {
     case AUDIO_ES:
     {
@@ -1222,11 +1344,11 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
 
     case VIDEO_ES:
         es->i_channel = p_sys->i_video;
-        if( fmt->video.i_frame_rate && fmt->video.i_frame_rate_base )
+        if( es->fmt.video.i_frame_rate && es->fmt.video.i_frame_rate_base )
             vlc_ureduce( &es->fmt.video.i_frame_rate,
                          &es->fmt.video.i_frame_rate_base,
-                         fmt->video.i_frame_rate,
-                         fmt->video.i_frame_rate_base, 0 );
+                         es->fmt.video.i_frame_rate,
+                         es->fmt.video.i_frame_rate_base, 0 );
         break;
 
     case SPU_ES:
@@ -1237,8 +1359,8 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
         es->i_channel = 0;
         break;
     }
-    es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */
-    es->psz_language_code = LanguageGetCode( fmt->psz_language );
+    es->psz_language = LanguageGetName( es->fmt.psz_language ); /* remember so we only need to do it once */
+    es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
     es->p_dec = NULL;
     es->p_dec_record = NULL;
     for( i = 0; i < 4; i++ )
@@ -1254,7 +1376,7 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
 
     TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
     p_sys->i_id++;  /* always incremented */
-    switch( fmt->i_cat )
+    switch( es->fmt.i_cat )
     {
         case AUDIO_ES:
             p_sys->i_audio++;
@@ -1269,6 +1391,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;
 }
 
@@ -1333,7 +1457,6 @@ static void EsSelect( es_out_t *out, es_out_id_t *es )
     es_out_sys_t   *p_sys = out->p_sys;
     input_thread_t *p_input = p_sys->p_input;
     vlc_value_t    val;
-    const char     *psz_var;
 
     if( EsIsSelected( es ) )
     {
@@ -1389,28 +1512,14 @@ static void EsSelect( es_out_t *out, es_out_id_t *es )
             return;
     }
 
-    if( es->fmt.i_cat == VIDEO_ES )
-        psz_var = "video-es";
-    else if( es->fmt.i_cat == AUDIO_ES )
-        psz_var = "audio-es";
-    else if( es->fmt.i_cat == SPU_ES )
-        psz_var = "spu-es";
-    else
-        return;
-
     /* Mark it as selected */
-    val.i_int = es->i_id;
-    var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
-
-    var_SetBool( p_sys->p_input, "intf-change", true );
+    input_SendEventEsSelect( p_input, es->fmt.i_cat, es->i_id );
 }
 
 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
 {
     es_out_sys_t   *p_sys = out->p_sys;
     input_thread_t *p_input = p_sys->p_input;
-    vlc_value_t    val;
-    const char     *psz_var;
 
     if( !EsIsSelected( es ) )
     {
@@ -1439,10 +1548,7 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
             if( i_spu_id == es->pp_cc_es[i]->i_id )
             {
                 /* Force unselection of the CC */
-                val.i_int = -1;
-                var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL );
-                if( !b_update )
-                    var_SetBool( p_sys->p_input, "intf-change", true );
+                input_SendEventEsSelect( p_input, SPU_ES, -1 );
             }
             EsOutDel( out, es->pp_cc_es[i] );
 
@@ -1457,20 +1563,9 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
     /* Update var */
     if( es->p_dec == NULL )
         return;
-    if( es->fmt.i_cat == VIDEO_ES )
-        psz_var = "video-es";
-    else if( es->fmt.i_cat == AUDIO_ES )
-        psz_var = "audio-es";
-    else if( es->fmt.i_cat == SPU_ES )
-        psz_var = "spu-es";
-    else
-        return;
 
     /* Mark it as unselected */
-    val.i_int = -1;
-    var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
-
-    var_SetBool( p_sys->p_input, "intf-change", true );
+    input_SendEventEsSelect( p_input, es->fmt.i_cat, -1 );
 }
 
 /**
@@ -1668,6 +1763,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 )
     {
@@ -1684,6 +1781,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;
     }
 
@@ -1698,7 +1796,6 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
 
     /* Check CC status */
     bool pb_cc[4];
-    bool b_cc_new = false;
 
     input_DecoderIsCcPresent( es->p_dec, pb_cc );
     for( int i = 0; i < 4; i++ )
@@ -1726,10 +1823,9 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
 
         /* */
         es->pb_cc_present[i] = true;
-        b_cc_new = true;
     }
-    if( b_cc_new )
-        var_SetBool( p_sys->p_input, "intf-change", true );
+
+    vlc_mutex_unlock( &p_sys->lock );
 
     return VLC_SUCCESS;
 }
@@ -1743,6 +1839,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 )
     {
@@ -1751,6 +1849,8 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
             if( input_DecoderIsEmpty( es->p_dec ) &&
                 ( !es->p_dec_record || input_DecoderIsEmpty( es->p_dec_record ) ))
                 break;
+            /* FIXME there should be a way to have auto deleted es, but there will be
+             * a problem when another codec of the same type is created (mainly video) */
             msleep( 20*1000 );
         }
         EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
@@ -1789,17 +1889,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 );
 }
 
@@ -1811,11 +1915,11 @@ 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;
-    int         i, *pi;
+    int         i;
 
     es_out_id_t *es;
 
@@ -1847,17 +1951,9 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
         {
             b = (bool) va_arg( args, int );
             p_sys->b_active = b;
-            /* Needed ? */
-            if( b )
-                var_SetBool( p_sys->p_input, "intf-change", true );
             return VLC_SUCCESS;
         }
 
-        case ES_OUT_GET_ACTIVE:
-            pb = (bool*) va_arg( args, bool * );
-            *pb = p_sys->b_active;
-            return VLC_SUCCESS;
-
         case ES_OUT_SET_MODE:
             i = (int) va_arg( args, int );
             if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
@@ -1882,11 +1978,6 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
             }
             return VLC_EGENERIC;
 
-        case ES_OUT_GET_MODE:
-            pi = (int*) va_arg( args, int* );
-            *pi = p_sys->i_mode;
-            return VLC_SUCCESS;
-
         case ES_OUT_SET_ES:
         case ES_OUT_RESTART_ES:
         {
@@ -1938,16 +2029,10 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
                     }
                 }
             }
-            if( i_query == ES_OUT_SET_ES )
-            {
-                vlc_event_t event;
-                event.type = vlc_InputSelectedStreamChanged;
-                vlc_event_send( &p_sys->p_input->p->event_manager, &event );
-            }
             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 * );
 
@@ -2014,7 +2099,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
             /* search program
              * TODO do not use mdate() but proper stream acquisition date */
             input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input),
-                                p_sys->p_input->b_can_pace_control, i_pcr, mdate() );
+                                p_sys->p_input->b_can_pace_control || p_sys->b_buffering, i_pcr, mdate() );
             /* Check buffering state on master clock update */
             if( p_sys->b_buffering && p_pgrm == p_sys->p_pgrm )
                 EsOutDecodersStopBuffering( out, false );
@@ -2024,7 +2109,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
 
         case ES_OUT_RESET_PCR:
             msg_Err( p_sys->p_input, "ES_OUT_RESET_PCR called" );
-            input_EsOutChangePosition( out );
+            EsOutChangePosition( out );
             return VLC_SUCCESS;
 
         case ES_OUT_GET_TS:
@@ -2038,14 +2123,6 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
             }
             return VLC_EGENERIC;
 
-        case ES_OUT_GET_GROUP:
-            pi = (int*) va_arg( args, int* );
-            if( p_sys->p_pgrm )
-                *pi = p_sys->p_pgrm->i_id;
-            else
-                *pi = -1;    /* FIXME */
-            return VLC_SUCCESS;
-
         case ES_OUT_SET_GROUP:
         {
             int j;
@@ -2062,7 +2139,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
             return VLC_EGENERIC;
         }
 
-        case ES_OUT_SET_FMT:
+        case ES_OUT_SET_ES_FMT:
         {
             /* This ain't pretty but is need by some demuxers (eg. Ogg )
              * to update the p_extra data */
@@ -2130,11 +2207,108 @@ 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;
+
+        case ES_OUT_SET_DELAY:
+        {
+            const int i_cat = (int)va_arg( args, int );
+            const mtime_t i_delay = (mtime_t)va_arg( args, mtime_t );
+            EsOutSetDelay( out, i_cat, i_delay );
+            return VLC_SUCCESS;
+        }
+
+        case ES_OUT_SET_RECORD_STATE:
+            b = (bool) va_arg( args, int );
+            return EsOutSetRecord( out, b );
+
+        case ES_OUT_SET_PAUSE_STATE:
+        {
+            const bool b_source_paused = (bool)va_arg( args, int );
+            const bool b_paused = (bool)va_arg( args, int );
+            const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
+
+            assert( !b_source_paused == !b_paused );
+            EsOutChangePause( out, b_paused, i_date );
+
+            return VLC_SUCCESS;
+        }
+
+        case ES_OUT_SET_RATE:
+        {
+            const int i_src_rate = (int)va_arg( args, int );
+            const int i_rate = (int)va_arg( args, int );
+
+            assert( i_src_rate == i_rate );
+            EsOutChangeRate( out, i_rate );
+
+            return VLC_SUCCESS;
+        }
+
+        case ES_OUT_SET_TIME:
+        {
+            const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
+
+            assert( i_date == -1 );
+            EsOutChangePosition( out );
+
+            return VLC_SUCCESS;
+        }
+
+        case ES_OUT_SET_FRAME_NEXT:
+            EsOutFrameNext( 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
@@ -2306,6 +2480,7 @@ static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )
         {
             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),
                            _("%u Hz"), fmt->audio.i_rate );
+            /* FIXME that should be removed or improved ! (used by text/strings.c) */
             var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate );
         }
 
@@ -2318,6 +2493,7 @@ static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )
         {
             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),
                            _("%u kb/s"), fmt->i_bitrate / 1000 );
+            /* FIXME that should be removed or improved ! (used by text/strings.c) */
             var_SetInteger( p_input, "bit-rate", fmt->i_bitrate );
         }
         break;