X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Fes_out.c;h=f15c4bed249ed4b6b070f3398f5d16b734a9d763;hb=f1d4df64c430242f395e5aa4db05c092c940d691;hp=fab5fafb84a55876d0af132d25eb758e65ba1254;hpb=0e5207a834a361f2e9950c9c33e409640c8a083a;p=vlc diff --git a/src/input/es_out.c b/src/input/es_out.c index fab5fafb84..f15c4bed24 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -39,6 +39,9 @@ #include #include "input_internal.h" +#include "clock.h" +#include "decoder.h" +#include "es_out.h" #include "../stream_output/stream_output.h" @@ -60,7 +63,7 @@ typedef struct bool b_selected; /* Clock for this program */ - input_clock_t clock; + input_clock_t *p_clock; char *psz_name; char *psz_now_playing; @@ -75,9 +78,6 @@ struct es_out_id_t int i_id; es_out_pgrm_t *p_pgrm; - /* Misc. */ - int64_t i_preroll_end; - /* Channel in the track type */ int i_channel; es_format_t fmt; @@ -99,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; @@ -135,24 +138,43 @@ struct es_out_sys_t int64_t i_audio_delay; int64_t i_spu_delay; - /* Rate used to rescale ES ts */ + /* Rate used for clock */ int i_rate; + /* */ + bool b_paused; + + /* Current preroll */ + 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 ); 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 ); static char **LanguageSplit( const char *psz_langs ); @@ -186,10 +208,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 ); @@ -200,9 +223,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; @@ -270,23 +296,31 @@ 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; return out; } /***************************************************************************** - * 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++ ) { @@ -317,6 +351,7 @@ void input_EsOutDelete( es_out_t *out ) 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 ); @@ -326,17 +361,38 @@ 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 ) +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 tha cat of the stream */ + /* Special HACK, -i_id is the cat of the stream */ return (es_out_id_t*)((uint8_t*)NULL-i_id); } @@ -348,38 +404,44 @@ es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id ) return NULL; } -static void EsOutDiscontinuity( es_out_t *out, bool b_flush, bool b_audio ) +static bool 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]; - /* Send a dummy block to let decoder know that - * there is a discontinuity */ - if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) ) - { - input_DecoderDiscontinuity( es->p_dec, b_flush ); - if( es->p_dec_record ) - input_DecoderDiscontinuity( es->p_dec_record, b_flush ); - } + 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; } -void input_EsOutChangeRate( es_out_t *out, int i_rate ) + +static void EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay ) { - es_out_sys_t *p_sys = out->p_sys; - int i; + es_out_sys_t *p_sys = out->p_sys; - p_sys->i_rate = i_rate; - EsOutDiscontinuity( out, false, false ); + 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( i = 0; i < p_sys->i_pgrm; i++ ) - input_ClockSetRate( &p_sys->pgrm[i]->clock, i_rate ); + 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; @@ -427,7 +489,9 @@ int input_EsOutSetRecord( es_out_t *out, bool b_record ) if( !p_es->p_dec || p_es->p_master ) continue; - p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_sys->p_sout_record ); + p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record ); + if( p_es->p_dec_record && p_sys->b_buffering ) + input_DecoderStartBuffering( p_es->p_dec_record ); } } else @@ -450,60 +514,281 @@ 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; + /* XXX the order is important */ + if( b_paused ) + { + EsOutDecodersChangePause( out, true, i_date ); + EsOutProgramChangePause( out, true, 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; +} + +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 ); } -void input_EsOutChangeState( es_out_t *out ) + +static void EsOutChangePosition( es_out_t *out ) +{ + es_out_sys_t *p_sys = out->p_sys; + + for( int i = 0; i < p_sys->i_es; i++ ) + { + es_out_id_t *p_es = p_sys->es[i]; + + if( !p_es->p_dec ) + continue; + + input_DecoderStartBuffering( p_es->p_dec ); + + if( p_es->p_dec_record ) + input_DecoderStartBuffering( p_es->p_dec ); + } + + for( int i = 0; i < p_sys->i_pgrm; i++ ) + 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; +} + + + +static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced ) { es_out_sys_t *p_sys = out->p_sys; - input_thread_t *p_input = p_sys->p_input; + int i_ret; + + mtime_t i_stream_start; + mtime_t i_system_start; + mtime_t i_stream_duration; + mtime_t i_system_duration; + i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock, + &i_stream_start, &i_system_start, + &i_stream_duration, &i_system_duration ); + assert( !i_ret || b_forced ); + if( i_ret ) + return; - if( p_input->i_state == PAUSE_S ) + mtime_t i_preroll_duration = 0; + if( p_sys->i_preroll_end >= 0 ) + i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 ); + + 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 ) { - /* Send discontinuity to decoders (it will allow them to flush - * * if implemented */ - EsOutDiscontinuity( out, false, false ); + msg_Dbg( p_sys->p_input, "Buffering %d%%", + (int)(100 * i_stream_duration / i_buffering_duration ) ); + return; } - else + + msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)", + (int)(i_stream_duration/1000), (int)(i_system_duration/1000) ); + 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++ ) { - /* Out of pause, reset pcr */ - es_out_Control( out, ES_OUT_RESET_PCR ); + es_out_id_t *p_es = p_sys->es[i]; + + if( !p_es->p_dec ) + continue; + input_DecoderWaitBuffering( p_es->p_dec ); + if( p_es->p_dec_record ) + input_DecoderWaitBuffering( p_es->p_dec_record ); + } + + msg_Dbg( p_sys->p_input, "Decoder buffering done in %d ms", + (int)(mdate() - i_decoder_buffering_start)/1000 ); + + 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 - i_buffering_duration ); + + for( int i = 0; i < p_sys->i_es; i++ ) + { + es_out_id_t *p_es = p_sys->es[i]; + + if( !p_es->p_dec ) + continue; + + input_DecoderStopBuffering( p_es->p_dec ); + if( p_es->p_dec_record ) + input_DecoderStopBuffering( p_es->p_dec ); } } -void input_EsOutChangePosition( es_out_t *out ) +static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date ) { - //es_out_sys_t *p_sys = out->p_sys; + es_out_sys_t *p_sys = out->p_sys; + + /* Pause decoders first */ + for( int i = 0; i < p_sys->i_es; i++ ) + { + es_out_id_t *es = p_sys->es[i]; - es_out_Control( out, ES_OUT_RESET_PCR ); - EsOutDiscontinuity( out, true, false ); + if( es->p_dec ) + { + input_DecoderChangePause( es->p_dec, b_paused, i_date ); + if( es->p_dec_record ) + input_DecoderChangePause( es->p_dec_record, b_paused, i_date ); + } + } } +static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date ) +{ + es_out_sys_t *p_sys = out->p_sys; + + for( int i = 0; i < p_sys->i_pgrm; i++ ) + input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date ); +} + +static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es ) +{ + es_out_sys_t *p_sys = out->p_sys; + + mtime_t i_delay = 0; + if( p_es->fmt.i_cat == AUDIO_ES ) + i_delay = p_sys->i_audio_delay; + else if( p_es->fmt.i_cat == SPU_ES ) + i_delay = p_sys->i_spu_delay; -bool input_EsOutDecodersEmpty( es_out_t *out ) + if( i_delay != 0 ) + { + if( p_es->p_dec ) + input_DecoderChangeDelay( p_es->p_dec, i_delay ); + if( p_es->p_dec_record ) + 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; - int i; - for( i = 0; i < p_sys->i_es; i++ ) + 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 ) { - es_out_id_t *es = p_sys->es[i]; + msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" ); + return; + } - if( es->p_dec && !input_DecoderEmpty( es->p_dec ) ) - return false; - if( es->p_dec_record && !input_DecoderEmpty( es->p_dec_record ) ) - return false; + 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; + } } - return true; + + 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, bool b_delete ) { @@ -579,7 +864,10 @@ static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, free( text.psz_string ); if( b_teletext ) - var_SetInteger( p_sys->p_input, "teletext-es", i_id ); + { + if( var_GetInteger( p_sys->p_input, "teletext-es" ) < 0 ) + var_SetInteger( p_sys->p_input, "teletext-es", i_id ); + } var_SetBool( p_sys->p_input, "intf-change", true ); } @@ -626,11 +914,6 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) p_pgrm->b_selected = true; /* Switch master stream */ - if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master ) - { - p_sys->p_pgrm->clock.b_master = false; - } - p_pgrm->clock.b_master = true; p_sys->p_pgrm = p_pgrm; /* Update "program" */ @@ -668,7 +951,8 @@ static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group ) vlc_value_t val; es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) ); - if( !p_pgrm ) return NULL; + if( !p_pgrm ) + return NULL; /* Init */ p_pgrm->i_id = i_group; @@ -678,7 +962,12 @@ static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group ) p_pgrm->psz_now_playing = NULL; p_pgrm->psz_publisher = NULL; p_pgrm->p_epg = NULL; - input_ClockInit( &p_pgrm->clock, false, p_input->p->input.i_cr_average, p_sys->i_rate ); + p_pgrm->p_clock = input_clock_New( p_input->p->input.i_cr_average, p_sys->i_rate ); + if( !p_pgrm->p_clock ) + { + free( p_pgrm ); + return NULL; + } /* Append it */ TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); @@ -731,7 +1020,10 @@ static int EsOutProgramDel( es_out_t *out, int i_group ) TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); /* If program is selected we need to unselect it */ - if( p_sys->p_pgrm == p_pgrm ) p_sys->p_pgrm = NULL; + if( p_sys->p_pgrm == p_pgrm ) + p_sys->p_pgrm = NULL; + + input_clock_Delete( p_pgrm->p_clock ); free( p_pgrm->psz_name ); free( p_pgrm->psz_now_playing ); @@ -987,24 +1279,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++ ) { @@ -1024,14 +1318,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 ); - es->i_preroll_end = -1; + if( es->fmt.i_id < 0 ) + es->fmt.i_id = out->p_sys->i_id; + es->i_id = fmt->i_id; - switch( fmt->i_cat ) + switch( es->fmt.i_cat ) { case AUDIO_ES: { @@ -1039,8 +1332,8 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) es->i_channel = p_sys->i_audio; - vlc_mutex_lock( &p_input->p->input.p_item->lock ); memset( &rg, 0, sizeof(rg) ); + vlc_mutex_lock( &p_input->p->input.p_item->lock ); vlc_audio_replay_gain_MergeFromMeta( &rg, p_input->p->input.p_item->p_meta ); vlc_mutex_unlock( &p_input->p->input.p_item->lock ); @@ -1062,7 +1355,7 @@ 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, @@ -1077,8 +1370,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++ ) @@ -1094,7 +1387,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++; @@ -1109,6 +1402,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; } @@ -1135,13 +1430,25 @@ static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es ) es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; - p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_input->p->p_sout ); - if( p_es->p_dec && !p_es->p_master && p_sys->p_sout_record ) - p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_sys->p_sout_record ); + p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_input->p->p_sout ); + if( p_es->p_dec ) + { + if( p_sys->b_buffering ) + input_DecoderStartBuffering( p_es->p_dec ); + + if( !p_es->p_master && p_sys->p_sout_record ) + { + p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record ); + if( p_es->p_dec_record && p_sys->b_buffering ) + input_DecoderStartBuffering( p_es->p_dec_record ); + } + } + + EsOutDecoderChangeDelay( out, p_es ); } static void EsDestroyDecoder( es_out_t *out, es_out_id_t *p_es ) { - es_out_sys_t *p_sys = out->p_sys; + VLC_UNUSED(out); if( !p_es->p_dec ) return; @@ -1211,7 +1518,6 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) } } - es->i_preroll_end = -1; EsCreateDecoder( out, es ); if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm ) @@ -1483,20 +1789,11 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force ) */ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) { - es_out_sys_t *p_sys = out->p_sys; - input_thread_t *p_input = p_sys->p_input; - es_out_pgrm_t *p_pgrm = es->p_pgrm; - int64_t i_delay; - int i_total=0; - - if( es->fmt.i_cat == AUDIO_ES ) - i_delay = p_sys->i_audio_delay; - else if( es->fmt.i_cat == SPU_ES ) - i_delay = p_sys->i_spu_delay; - else - i_delay = 0; + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + int i_total = 0; - if( libvlc_stats (p_input) ) + if( libvlc_stats( p_input ) ) { vlc_mutex_lock( &p_input->p->counters.counters_lock ); stats_UpdateInteger( p_input, p_input->p->counters.p_demux_read, @@ -1506,111 +1803,73 @@ 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( es->i_preroll_end >= 0 ) + if( p_sys->i_preroll_end >= 0 ) { int64_t i_date = p_block->i_pts; if( i_date <= 0 ) i_date = p_block->i_dts; - if( i_date < es->i_preroll_end ) + if( i_date < p_sys->i_preroll_end ) p_block->i_flags |= BLOCK_FLAG_PREROLL; - else - es->i_preroll_end = -1; } - if( p_block->i_dts > 0 && (p_block->i_flags&BLOCK_FLAG_PREROLL) ) - { - p_block->i_dts += i_delay; - } - else if( p_block->i_dts > 0 ) - { - p_block->i_dts = - input_ClockGetTS( p_input, &p_pgrm->clock, p_block->i_dts ) + i_delay; - } - if( p_block->i_pts > 0 && (p_block->i_flags&BLOCK_FLAG_PREROLL) ) - { - p_block->i_pts += i_delay; - } - else if( p_block->i_pts > 0 ) + p_block->i_rate = 0; + + if( !es->p_dec ) { - p_block->i_pts = - input_ClockGetTS( p_input, &p_pgrm->clock, p_block->i_pts ) + i_delay; + block_Release( p_block ); + vlc_mutex_unlock( &p_sys->lock ); + return VLC_SUCCESS; } - if ( p_block->i_rate == INPUT_RATE_DEFAULT && - es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) ) + + /* Decode */ + if( es->p_dec_record ) { - mtime_t current_date = mdate(); - if( !p_block->i_pts - || p_block->i_pts > current_date + 10000000 - || current_date > p_block->i_pts ) - { - /* ETSI EN 300 472 Annex A : do not take into account the PTS - * for teletext streams. */ - p_block->i_pts = current_date + 400000 - + p_input->i_pts_delay + i_delay; - } + block_t *p_dup = block_Duplicate( p_block ); + if( p_dup ) + input_DecoderDecode( es->p_dec_record, p_dup ); } + input_DecoderDecode( es->p_dec, p_block ); - p_block->i_rate = p_sys->i_rate; + /* Check CC status */ + bool pb_cc[4]; + bool b_cc_new = false; - /* TODO handle mute */ - if( es->p_dec && - ( es->fmt.i_cat != AUDIO_ES || - ( p_sys->i_rate >= INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE && - p_sys->i_rate <= INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE ) ) ) + input_DecoderIsCcPresent( es->p_dec, pb_cc ); + for( int i = 0; i < 4; i++ ) { - bool pb_cc[4]; - bool b_cc_new = false; - int i; - if( es->p_dec_record ) - { - block_t *p_dup = block_Duplicate( p_block ); - if( p_dup ) - input_DecoderDecode( es->p_dec_record, p_dup ); - } - input_DecoderDecode( es->p_dec, p_block ); + static const vlc_fourcc_t fcc[4] = { + VLC_FOURCC('c', 'c', '1', ' '), + VLC_FOURCC('c', 'c', '2', ' '), + VLC_FOURCC('c', 'c', '3', ' '), + VLC_FOURCC('c', 'c', '4', ' '), + }; + es_format_t fmt; - /* Check CC status */ - input_DecoderIsCcPresent( es->p_dec, pb_cc ); - for( i = 0; i < 4; i++ ) - { - static const vlc_fourcc_t fcc[4] = { - VLC_FOURCC('c', 'c', '1', ' '), - VLC_FOURCC('c', 'c', '2', ' '), - VLC_FOURCC('c', 'c', '3', ' '), - VLC_FOURCC('c', 'c', '4', ' '), - }; - static const char ppsz_description[4][18] = { - N_("Closed captions 1"), - N_("Closed captions 2"), - N_("Closed captions 3"), - N_("Closed captions 4"), - }; - es_format_t fmt; - - if( es->pb_cc_present[i] || !pb_cc[i] ) - continue; - msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id ); - - es_format_Init( &fmt, SPU_ES, fcc[i] ); - fmt.i_group = es->fmt.i_group; - fmt.psz_description = strdup( _(ppsz_description[i] ) ); - es->pp_cc_es[i] = EsOutAdd( out, &fmt ); - es->pp_cc_es[i]->p_master = es; - es_format_Clean( &fmt ); - - /* */ - es->pb_cc_present[i] = true; - b_cc_new = true; - } - if( b_cc_new ) - var_SetBool( p_sys->p_input, "intf-change", true ); - } - else - { - block_Release( p_block ); + if( es->pb_cc_present[i] || !pb_cc[i] ) + continue; + msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id ); + + es_format_Init( &fmt, SPU_ES, fcc[i] ); + fmt.i_group = es->fmt.i_group; + if( asprintf( &fmt.psz_description, + _("Closed captions %u"), 1 + i ) == -1 ) + fmt.psz_description = NULL; + es->pp_cc_es[i] = EsOutAdd( out, &fmt ); + es->pp_cc_es[i]->p_master = es; + es_format_Clean( &fmt ); + + /* */ + 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; } @@ -1624,14 +1883,18 @@ 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 ) { - while( !out->p_sys->p_input->b_die && es->p_dec ) + while( !p_sys->p_input->b_die && !p_sys->b_buffering && es->p_dec ) { - if( input_DecoderEmpty( es->p_dec ) && - ( !es->p_dec_record || input_DecoderEmpty( es->p_dec_record ) )) + 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 ); @@ -1670,17 +1933,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 ); } @@ -1692,7 +1959,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; @@ -1769,50 +2036,26 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) return VLC_SUCCESS; case ES_OUT_SET_ES: + case ES_OUT_RESTART_ES: + { + int i_cat; + es = (es_out_id_t*) va_arg( args, es_out_id_t * ); - /* Special case NULL, NULL+i_cat */ + if( es == NULL ) - { - for( i = 0; i < p_sys->i_es; i++ ) - { - if( EsIsSelected( p_sys->es[i] ) ) - EsUnselect( out, p_sys->es[i], - p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); - } - } + i_cat = UNKNOWN_ES; else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) ) - { - for( i = 0; i < p_sys->i_es; i++ ) - { - if( p_sys->es[i]->fmt.i_cat == AUDIO_ES && - EsIsSelected( p_sys->es[i] ) ) - EsUnselect( out, p_sys->es[i], - p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); - } - } + i_cat = AUDIO_ES; else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) ) - { - for( i = 0; i < p_sys->i_es; i++ ) - { - if( p_sys->es[i]->fmt.i_cat == VIDEO_ES && - EsIsSelected( p_sys->es[i] ) ) - EsUnselect( out, p_sys->es[i], - p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); - } - } + i_cat = VIDEO_ES; else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) ) - { - for( i = 0; i < p_sys->i_es; i++ ) - { - if( p_sys->es[i]->fmt.i_cat == SPU_ES && - EsIsSelected( p_sys->es[i] ) ) - EsUnselect( out, p_sys->es[i], - p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); - } - } + i_cat = SPU_ES; else + i_cat = -1; + + for( i = 0; i < p_sys->i_es; i++ ) { - for( i = 0; i < p_sys->i_es; i++ ) + if( i_cat == -1 ) { if( es == p_sys->es[i] ) { @@ -1820,15 +2063,39 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) break; } } + else + { + if( i_cat == UNKNOWN_ES || p_sys->es[i]->fmt.i_cat == i_cat ) + { + if( EsIsSelected( p_sys->es[i] ) ) + { + if( i_query == ES_OUT_RESTART_ES ) + { + if( p_sys->es[i]->p_dec ) + { + EsDestroyDecoder( out, p_sys->es[i] ); + EsCreateDecoder( out, p_sys->es[i] ); + } + } + else + { + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + } + } } + 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 * ); @@ -1892,14 +2159,20 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ i_pcr = (int64_t)va_arg( args, int64_t ); - /* search program */ - input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock, i_pcr ); + /* 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() ); + /* Check buffering state on master clock update */ + if( p_sys->b_buffering && p_pgrm == p_sys->p_pgrm ) + EsOutDecodersStopBuffering( out, false ); + return VLC_SUCCESS; } case ES_OUT_RESET_PCR: - for( i = 0; i < p_sys->i_pgrm; i++ ) - input_ClockResetPCR( &p_sys->pgrm[i]->clock ); + msg_Err( p_sys->p_input, "ES_OUT_RESET_PCR called" ); + EsOutChangePosition( out ); return VLC_SUCCESS; case ES_OUT_GET_TS: @@ -1907,8 +2180,8 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) { int64_t i_ts = (int64_t)va_arg( args, int64_t ); int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * ); - *pi_ts = input_ClockGetTS( p_sys->p_input, - &p_sys->p_pgrm->clock, i_ts ); + *pi_ts = input_clock_GetTS( p_sys->p_pgrm->p_clock, NULL, + p_sys->p_input->i_pts_delay, i_ts ); return VLC_SUCCESS; } return VLC_EGENERIC; @@ -1973,18 +2246,12 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_NEXT_DISPLAY_TIME: { - int64_t i_date; - - es = (es_out_id_t*) va_arg( args, es_out_id_t * ); - i_date = (int64_t)va_arg( args, int64_t ); + const int64_t i_date = (int64_t)va_arg( args, int64_t ); - if( !es || !es->p_dec ) + if( i_date < 0 ) return VLC_EGENERIC; - /* XXX We should call input_ClockGetTS but PCR has been reseted - * and it will return 0, so we won't call input_ClockGetTS on all preroll samples - * but that's ugly(more time discontinuity), it need to be improved -- fenrir */ - es->i_preroll_end = i_date; + p_sys->i_preroll_end = i_date; return VLC_SUCCESS; } @@ -2011,11 +2278,116 @@ 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; + + case ES_OUT_LOCK: + vlc_mutex_lock( &p_sys->lock ); + return VLC_SUCCESS; + + case ES_OUT_UNLOCK: + vlc_mutex_unlock( &p_sys->lock ); + 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