X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Fes_out.c;h=3062d8efde1ea17c65b7b95fdf6f1a4c9bd6da58;hb=cdc9281c4841e62f066328e7bdd56398755a3077;hp=afb2d4b6f5870888e7448877ddde3bc2281b7d03;hpb=40ef630d52dc11e37c70bfa2aa2eca7203754143;p=vlc diff --git a/src/input/es_out.c b/src/input/es_out.c index afb2d4b6f5..3062d8efde 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -37,12 +37,14 @@ #include #include #include +#include #include "input_internal.h" #include "clock.h" #include "decoder.h" #include "es_out.h" #include "event.h" +#include "info.h" #include "../stream_output/stream_output.h" @@ -62,6 +64,7 @@ typedef struct int i_es; bool b_selected; + bool b_scrambled; /* Clock for this program */ input_clock_t *p_clock; @@ -69,8 +72,6 @@ typedef struct char *psz_name; char *psz_now_playing; char *psz_publisher; - - vlc_epg_t *p_epg; } es_out_pgrm_t; struct es_out_id_t @@ -79,6 +80,9 @@ struct es_out_id_t int i_id; es_out_pgrm_t *p_pgrm; + /* */ + bool b_scrambled; + /* Channel in the track type */ int i_channel; es_format_t fmt; @@ -170,6 +174,7 @@ static void EsOutDel ( es_out_t *, es_out_id_t * ); static int EsOutControl( es_out_t *, int i_query, va_list ); static void EsOutDelete ( es_out_t * ); +static void EsOutTerminate( es_out_t * ); static void EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force ); static void EsOutUpdateInfo( es_out_t *, es_out_id_t *es, const es_format_t *, const vlc_meta_t * ); static int EsOutSetRecord( es_out_t *, bool b_record ); @@ -182,9 +187,10 @@ static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_da 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 ); +static char **LanguageSplit( const char *psz_langs, bool b_default_any ); static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang ); static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm ); @@ -205,16 +211,16 @@ static inline int EsOutGetClosedCaptionsChannel( vlc_fourcc_t fcc ) } return -1; } - +static inline bool EsFmtIsTeletext( const es_format_t *p_fmt ) +{ + return p_fmt->i_cat == SPU_ES && p_fmt->i_codec == VLC_CODEC_TELETEXT; +} /***************************************************************************** * input_EsOutNew: *****************************************************************************/ 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( *out ) ); if( !out ) return NULL; @@ -232,14 +238,12 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate ) 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; - p_sys->i_mode = ES_OUT_MODE_AUTO; + p_sys->i_mode = ES_OUT_MODE_NONE; TAB_INIT( p_sys->i_pgrm, p_sys->pgrm ); @@ -254,35 +258,35 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate ) p_sys->i_sub = 0; /* */ - var_Get( p_input, "audio-track", &val ); - p_sys->i_audio_last = val.i_int; + p_sys->i_audio_last = var_GetInteger( p_input, "audio-track" ); - var_Get( p_input, "sub-track", &val ); - p_sys->i_sub_last = val.i_int; + p_sys->i_sub_last = var_GetInteger( p_input, "sub-track" ); p_sys->i_default_sub_id = -1; if( !p_input->b_preparsing ) { - var_Get( p_input, "audio-language", &val ); - p_sys->ppsz_audio_language = LanguageSplit(val.psz_string); + char *psz_string; + + psz_string = var_GetString( p_input, "audio-language" ); + p_sys->ppsz_audio_language = LanguageSplit( psz_string, true ); if( p_sys->ppsz_audio_language ) { - for( i = 0; p_sys->ppsz_audio_language[i]; i++ ) + for( int i = 0; p_sys->ppsz_audio_language[i]; i++ ) msg_Dbg( p_input, "selected audio language[%d] %s", i, p_sys->ppsz_audio_language[i] ); } - free( val.psz_string ); + free( psz_string ); - var_Get( p_input, "sub-language", &val ); - p_sys->ppsz_sub_language = LanguageSplit(val.psz_string); + psz_string = var_GetString( p_input, "sub-language" ); + p_sys->ppsz_sub_language = LanguageSplit( psz_string, false ); if( p_sys->ppsz_sub_language ) { - for( i = 0; p_sys->ppsz_sub_language[i]; i++ ) + for( int i = 0; p_sys->ppsz_sub_language[i]; i++ ) msg_Dbg( p_input, "selected subtitle language[%d] %s", i, p_sys->ppsz_sub_language[i] ); } - free( val.psz_string ); + free( psz_string ); } else { @@ -290,11 +294,9 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate ) p_sys->ppsz_audio_language = NULL; } - var_Get( p_input, "audio-track-id", &val ); - p_sys->i_audio_id = val.i_int; + p_sys->i_audio_id = var_GetInteger( p_input, "audio-track-id" ); - var_Get( p_input, "sub-track-id", &val ); - p_sys->i_sub_id = val.i_int; + p_sys->i_sub_id = var_GetInteger( p_input, "sub-track-id" ); p_sys->p_es_audio = NULL; p_sys->p_es_video = NULL; @@ -327,12 +329,35 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate ) static void EsOutDelete( es_out_t *out ) { es_out_sys_t *p_sys = out->p_sys; - int i; + + assert( !p_sys->i_es && !p_sys->i_pgrm && !p_sys->p_pgrm ); + if( p_sys->ppsz_audio_language ) + { + for( int 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( int i = 0; p_sys->ppsz_sub_language[i]; i++ ) + free( p_sys->ppsz_sub_language[i] ); + free( p_sys->ppsz_sub_language ); + } + + vlc_mutex_destroy( &p_sys->lock ); + + free( p_sys ); + free( out ); +} + +static void EsOutTerminate( es_out_t *out ) +{ + es_out_sys_t *p_sys = out->p_sys; if( p_sys->p_sout_record ) EsOutSetRecord( out, false ); - for( i = 0; i < p_sys->i_es; i++ ) + for( int i = 0; i < p_sys->i_es; i++ ) { if( p_sys->es[i]->p_dec ) input_DecoderDelete( p_sys->es[i]->p_dec ); @@ -343,38 +368,25 @@ static void EsOutDelete( es_out_t *out ) 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 ); + TAB_CLEAN( p_sys->i_es, p_sys->es ); /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */ - for( i = 0; i < p_sys->i_pgrm; i++ ) + for( int 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 ); + p_sys->p_pgrm = NULL; + + input_item_SetEpgOffline( p_sys->p_input->p->p_item ); + input_SendEventMetaEpg( p_sys->p_input ); } static mtime_t EsOutGetWakeup( es_out_t *out ) @@ -460,11 +472,15 @@ static int EsOutSetRecord( es_out_t *out, bool b_record ) if( b_record ) { - char *psz_path = var_CreateGetString( p_input, "input-record-path" ); - if( !psz_path || *psz_path == '\0' ) + char *psz_path = var_CreateGetNonEmptyString( p_input, "input-record-path" ); + if( !psz_path ) { - free( psz_path ); - psz_path = strdup( config_GetHomeDir() ); + if( var_CountChoices( p_input, "video-es" ) ) + psz_path = config_GetUserDir( VLC_VIDEOS_DIR ); + else if( var_CountChoices( p_input, "audio-es" ) ) + psz_path = config_GetUserDir( VLC_MUSIC_DIR ); + else + psz_path = config_GetUserDir( VLC_DOWNLOAD_DIR ); } char *psz_sout = NULL; // TODO conf @@ -570,9 +586,7 @@ 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 ); + EsOutProgramsChangeRate( out ); } static void EsOutChangePosition( es_out_t *out ) @@ -667,13 +681,14 @@ static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced ) (int)(mdate() - i_decoder_buffering_start)/1000 ); /* Here is a good place to destroy unused vout with every demuxer */ - input_ressource_TerminateVout( p_sys->p_input->p->p_ressource ); + input_resource_TerminateVout( p_sys->p_input->p->p_resource ); /* */ const mtime_t i_wakeup_delay = 10*1000; /* FIXME CLEANUP thread wake up time*/ const mtime_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : mdate(); - input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, i_current_date + i_wakeup_delay - i_buffering_duration ); + input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, true, + i_current_date + i_wakeup_delay - i_buffering_duration ); for( int i = 0; i < p_sys->i_es; i++ ) { @@ -704,6 +719,32 @@ static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_da } } } + +static bool EsOutIsExtraBufferingAllowed( es_out_t *out ) +{ + es_out_sys_t *p_sys = out->p_sys; + + size_t i_size = 0; + 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 ) + i_size += input_DecoderGetFifoSize( p_es->p_dec ); + if( p_es->p_dec_record ) + i_size += input_DecoderGetFifoSize( p_es->p_dec_record ); + } + //fprintf( stderr, "----- EsOutIsExtraBufferingAllowed =% 5d kbytes -- ", i_size / 1024 ); + + /* TODO maybe we want to be able to tune it ? */ +#if defined(OPTIMIZE_MEMORY) + const size_t i_level_high = 500000; /* 0.5 Mbytes */ +#else + const size_t i_level_high = 10000000; /* 10 Mbytes */ +#endif + return i_size < i_level_high; +} + static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date ) { es_out_sys_t *p_sys = out->p_sys; @@ -853,19 +894,18 @@ static mtime_t EsOutGetBuffering( es_out_t *out ) return i_delay; } -static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language, +static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, + const es_format_t *fmt, const char *psz_language, bool b_delete ) { es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; - const bool b_teletext = fmt->i_cat == SPU_ES && fmt->i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ); vlc_value_t val, text; if( b_delete ) { - /* TODO it should probably be a list */ - if( b_teletext ) - input_SendEventTeletext( p_sys->p_input, -1 ); + if( EsFmtIsTeletext( fmt ) ) + input_SendEventTeletextDel( p_sys->p_input, i_id ); input_SendEventEsDel( p_input, fmt->i_cat, i_id ); return; @@ -896,10 +936,9 @@ static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, { if( psz_language && *psz_language ) { - text.psz_string = malloc( strlen( fmt->psz_description) + - strlen( psz_language ) + 10 ); - sprintf( text.psz_string, "%s - [%s]", fmt->psz_description, - psz_language ); + if( asprintf( &text.psz_string, "%s - [%s]", fmt->psz_description, + psz_language ) == -1 ) + text.psz_string = NULL; } else text.psz_string = strdup( fmt->psz_description ); } @@ -918,15 +957,17 @@ static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, } input_SendEventEsAdd( p_input, fmt->i_cat, i_id, text.psz_string ); - - free( text.psz_string ); - - if( b_teletext ) + if( EsFmtIsTeletext( fmt ) ) { - /* TODO it should probably be a list */ - if( var_GetInteger( p_sys->p_input, "teletext-es" ) < 0 ) - input_SendEventTeletext( p_sys->p_input, i_id ); + char psz_page[3+1]; + snprintf( psz_page, sizeof(psz_page), "%d%2.2x", + fmt->subs.teletext.i_magazine, + fmt->subs.teletext.i_page ); + input_SendEventTeletextAdd( p_sys->p_input, + i_id, fmt->subs.teletext.i_magazine >= 0 ? psz_page : NULL ); } + + free( text.psz_string ); } static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, @@ -979,6 +1020,8 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) input_SendEventEsDel( p_input, AUDIO_ES, -1 ); input_SendEventEsDel( p_input, VIDEO_ES, -1 ); input_SendEventEsDel( p_input, SPU_ES, -1 ); + input_SendEventTeletextDel( p_input, -1 ); + input_SendEventProgramScrambled( p_input, p_pgrm->i_id, p_pgrm->b_scrambled ); /* TODO event */ var_SetInteger( p_input, "teletext-es", -1 ); @@ -1013,19 +1056,20 @@ static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group ) p_pgrm->i_id = i_group; p_pgrm->i_es = 0; p_pgrm->b_selected = false; + p_pgrm->b_scrambled = false; p_pgrm->psz_name = NULL; p_pgrm->psz_now_playing = NULL; p_pgrm->psz_publisher = NULL; - p_pgrm->p_epg = NULL; p_pgrm->p_clock = input_clock_New( p_sys->i_rate ); if( !p_pgrm->p_clock ) { free( p_pgrm ); return NULL; } + if( p_sys->b_paused ) + input_clock_ChangePause( p_pgrm->p_clock, p_sys->b_paused, p_sys->i_pause_date ); input_clock_SetJitter( p_pgrm->p_clock, p_sys->i_pts_delay, p_sys->i_cr_average ); - /* Append it */ TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); @@ -1078,8 +1122,6 @@ static int EsOutProgramDel( es_out_t *out, int i_group ) free( p_pgrm->psz_name ); free( p_pgrm->psz_now_playing ); free( p_pgrm->psz_publisher ); - if( p_pgrm->p_epg ) - vlc_epg_Delete( p_pgrm->p_epg ); free( p_pgrm ); /* Update "program" variable */ @@ -1088,6 +1130,20 @@ static int EsOutProgramDel( es_out_t *out, int i_group ) return VLC_SUCCESS; } +/* EsOutProgramFind + */ +static es_out_pgrm_t *EsOutProgramFind( es_out_t *p_out, int i_group ) +{ + es_out_sys_t *p_sys = p_out->p_sys; + + for( int i = 0; i < p_sys->i_pgrm; i++ ) + { + if( p_sys->pgrm[i]->i_id == i_group ) + return p_sys->pgrm[i]; + } + return EsOutProgramAdd( p_out, i_group ); +} + /* EsOutProgramMeta: */ static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm ) @@ -1106,12 +1162,11 @@ static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm ) return psz; } -static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta ) +static void EsOutProgramMeta( es_out_t *out, int i_group, const vlc_meta_t *p_meta ) { es_out_sys_t *p_sys = out->p_sys; - es_out_pgrm_t *p_pgrm = NULL; + es_out_pgrm_t *p_pgrm; input_thread_t *p_input = p_sys->p_input; - char *psz_cat; const char *psz_title = NULL; const char *psz_provider = NULL; int i; @@ -1122,21 +1177,14 @@ static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta ) if( !vlc_meta_Get( p_meta, vlc_meta_Title) && !vlc_meta_Get( p_meta, vlc_meta_NowPlaying) && !vlc_meta_Get( p_meta, vlc_meta_Publisher) && - vlc_dictionary_keys_count( &p_meta->extra_tags ) <= 0 ) + vlc_meta_GetExtraCount( p_meta ) <= 0 ) { return; } /* Find program */ - for( i = 0; i < p_sys->i_pgrm; i++ ) - { - if( p_sys->pgrm[i]->i_id == i_group ) - { - p_pgrm = p_sys->pgrm[i]; - break; - } - } - if( p_pgrm == NULL ) - p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ + p_pgrm = EsOutProgramFind( out, i_group ); + if( !p_pgrm ) + return; /* */ psz_title = vlc_meta_Get( p_meta, vlc_meta_Title); @@ -1151,7 +1199,8 @@ static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta ) /* Remove old entries */ input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL ); - /* TODO update epg name */ + /* TODO update epg name ? + * TODO update scrambled info name ? */ free( psz_cat ); } free( p_pgrm->psz_name ); @@ -1173,138 +1222,91 @@ static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta ) { input_SendEventProgramDel( p_input, i_group ); input_SendEventProgramAdd( p_input, i_group, psz_text ); - + if( p_sys->p_pgrm == p_pgrm ) + input_SendEventProgramSelect( p_input, i_group ); free( psz_text ); } } - psz_cat = EsOutProgramGetMetaName( p_pgrm ); - if( psz_provider ) + /* */ + char **ppsz_all_keys = vlc_meta_CopyExtraNames(p_meta ); + + info_category_t *p_cat = NULL; + if( psz_provider || *ppsz_all_keys[0] ) { - if( p_sys->p_pgrm == p_pgrm ) - { - input_item_SetPublisher( p_input->p->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 *psz_cat = EsOutProgramGetMetaName( p_pgrm ); + if( psz_cat ) + p_cat = info_category_New( psz_cat ); + free( psz_cat ); } - char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags ); + for( i = 0; ppsz_all_keys[i]; i++ ) { - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(ppsz_all_keys[i]), - vlc_dictionary_value_for_key( &p_meta->extra_tags, ppsz_all_keys[i] ) ); + if( p_cat ) + info_category_AddInfo( p_cat, vlc_gettext(ppsz_all_keys[i]), "%s", + vlc_meta_GetExtra( p_meta, ppsz_all_keys[i] ) ); free( ppsz_all_keys[i] ); } free( ppsz_all_keys ); - free( psz_cat ); -} - -static void vlc_epg_Merge( vlc_epg_t *p_dst, const vlc_epg_t *p_src ) -{ - int i; - - /* Add new event */ - for( i = 0; i < p_src->i_event; i++ ) + if( psz_provider ) { - vlc_epg_event_t *p_evt = p_src->pp_event[i]; - bool b_add = true; - int j; - - for( j = 0; j < p_dst->i_event; j++ ) - { - if( p_dst->pp_event[j]->i_start == p_evt->i_start && p_dst->pp_event[j]->i_duration == p_evt->i_duration ) - { - b_add = false; - break; - } - if( p_dst->pp_event[j]->i_start > p_evt->i_start ) - break; - } - if( b_add ) + if( p_sys->p_pgrm == p_pgrm ) { - vlc_epg_event_t *p_copy = calloc( 1, sizeof(vlc_epg_event_t) ); - if( !p_copy ) - break; - p_copy->i_start = p_evt->i_start; - p_copy->i_duration = p_evt->i_duration; - p_copy->psz_name = p_evt->psz_name ? strdup( p_evt->psz_name ) : NULL; - p_copy->psz_short_description = p_evt->psz_short_description ? strdup( p_evt->psz_short_description ) : NULL; - p_copy->psz_description = p_evt->psz_description ? strdup( p_evt->psz_description ) : NULL; - TAB_INSERT( p_dst->i_event, p_dst->pp_event, p_copy, j ); + input_item_SetPublisher( p_input->p->p_item, psz_provider ); + input_SendEventMeta( p_input ); } + if( p_cat ) + info_category_AddInfo( p_cat, vlc_meta_TypeToLocalizedString(vlc_meta_Publisher), + "%s",psz_provider ); } - /* Update current */ - vlc_epg_SetCurrent( p_dst, p_src->p_current ? p_src->p_current->i_start : -1 ); - - /* Keep only 1 old event */ - if( p_dst->p_current ) - { - while( p_dst->i_event > 1 && p_dst->pp_event[0] != p_dst->p_current && p_dst->pp_event[1] != p_dst->p_current ) - TAB_REMOVE( p_dst->i_event, p_dst->pp_event, p_dst->pp_event[0] ); - } + if( p_cat ) + input_Control( p_input, INPUT_MERGE_INFOS, p_cat ); } -static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg ) +static void EsOutProgramEpg( es_out_t *out, int i_group, const vlc_epg_t *p_epg ) { 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; + input_item_t *p_item = p_input->p->p_item; + es_out_pgrm_t *p_pgrm; char *psz_cat; - int i; /* Find program */ - for( i = 0; i < p_sys->i_pgrm; i++ ) - { - if( p_sys->pgrm[i]->i_id == i_group ) - { - p_pgrm = p_sys->pgrm[i]; - break; - } - } - if( p_pgrm == NULL ) - p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ - - /* Merge EPG */ - if( !p_pgrm->p_epg ) - p_pgrm->p_epg = vlc_epg_New( p_pgrm->psz_name ); - vlc_epg_Merge( p_pgrm->p_epg, p_epg ); + p_pgrm = EsOutProgramFind( out, i_group ); + if( !p_pgrm ) + return; /* Update info */ psz_cat = EsOutProgramGetMetaName( p_pgrm ); -#ifdef HAVE_LOCALTIME_R - char *psz_epg; - if( asprintf( &psz_epg, "EPG %s", psz_cat ) == -1 ) - psz_epg = NULL; - input_Control( p_input, INPUT_DEL_INFO, psz_epg, NULL ); - msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, p_pgrm->p_epg->psz_name ); - for( i = 0; i < p_pgrm->p_epg->i_event; i++ ) - { - const vlc_epg_event_t *p_evt = p_pgrm->p_epg->pp_event[i]; - time_t t_start = (time_t)p_evt->i_start; - struct tm tm_start; - char psz_start[128]; - - localtime_r( &t_start, &tm_start ); - - snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d:%2.2d", tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec ); - if( p_evt->psz_short_description || p_evt->psz_description ) - input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s", - p_evt->psz_name, - p_evt->i_duration/60/60, (p_evt->i_duration/60)%60, - p_evt->psz_short_description ? p_evt->psz_short_description : p_evt->psz_description ); - else - input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d)", - p_evt->psz_name, - p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 ); - } - free( psz_epg ); -#endif + msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, psz_cat ); + + /* Merge EPG */ + vlc_epg_t epg; + + epg = *p_epg; + epg.psz_name = psz_cat; + + input_item_SetEpg( p_item, &epg ); + input_SendEventMetaEpg( p_sys->p_input ); + /* Update now playing */ free( p_pgrm->psz_now_playing ); p_pgrm->psz_now_playing = NULL; - if( p_epg->p_current && p_epg->p_current->psz_name && *p_epg->p_current->psz_name ) - p_pgrm->psz_now_playing = strdup( p_epg->p_current->psz_name ); + + vlc_mutex_lock( &p_item->lock ); + for( int i = 0; i < p_item->i_epg; i++ ) + { + const vlc_epg_t *p_tmp = p_item->pp_epg[i]; + + if( p_tmp->psz_name && !strcmp(p_tmp->psz_name, psz_cat) ) + { + if( p_tmp->p_current && p_tmp->p_current->psz_name && *p_tmp->p_current->psz_name ) + p_pgrm->psz_now_playing = strdup( p_tmp->p_current->psz_name ); + break; + } + } + vlc_mutex_unlock( &p_item->lock ); if( p_pgrm == p_sys->p_pgrm ) { @@ -1315,18 +1317,98 @@ static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg ) if( p_pgrm->psz_now_playing ) { input_Control( p_input, INPUT_ADD_INFO, psz_cat, - input_MetaTypeToLocalizedString(vlc_meta_NowPlaying), + vlc_meta_TypeToLocalizedString(vlc_meta_NowPlaying), p_pgrm->psz_now_playing ); } else { input_Control( p_input, INPUT_DEL_INFO, psz_cat, - input_MetaTypeToLocalizedString(vlc_meta_NowPlaying) ); + vlc_meta_TypeToLocalizedString(vlc_meta_NowPlaying) ); } free( psz_cat ); } +static void EsOutProgramUpdateScrambled( es_out_t *p_out, es_out_pgrm_t *p_pgrm ) +{ + es_out_sys_t *p_sys = p_out->p_sys; + input_thread_t *p_input = p_sys->p_input; + bool b_scrambled = false; + + for( int i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->p_pgrm == p_pgrm && p_sys->es[i]->b_scrambled ) + { + b_scrambled = true; + break; + } + } + if( !p_pgrm->b_scrambled == !b_scrambled ) + return; + + p_pgrm->b_scrambled = b_scrambled; + char *psz_cat = EsOutProgramGetMetaName( p_pgrm ); + + if( b_scrambled ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Scrambled"), _("Yes") ); + else + input_Control( p_input, INPUT_DEL_INFO, psz_cat, _("Scrambled") ); + + input_SendEventProgramScrambled( p_input, p_pgrm->i_id, b_scrambled ); +} + +static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta ) +{ + es_out_sys_t *p_sys = p_out->p_sys; + input_thread_t *p_input = p_sys->p_input; + + input_item_t *p_item = input_GetItem( p_input ); + + char *psz_title = NULL; + char *psz_arturl = input_item_GetArtURL( p_item ); + + vlc_mutex_lock( &p_item->lock ); + + if( vlc_meta_Get( p_meta, vlc_meta_Title ) && !p_item->b_fixed_name ) + psz_title = strdup( vlc_meta_Get( p_meta, vlc_meta_Title ) ); + + vlc_meta_Merge( p_item->p_meta, p_meta ); + + if( !psz_arturl || *psz_arturl == '\0' ) + { + const char *psz_tmp = vlc_meta_Get( p_item->p_meta, vlc_meta_ArtworkURL ); + if( psz_tmp ) + psz_arturl = strdup( psz_tmp ); + } + vlc_mutex_unlock( &p_item->lock ); + + if( psz_arturl && *psz_arturl ) + { + input_item_SetArtURL( p_item, psz_arturl ); + + if( !strncmp( psz_arturl, "attachment://", strlen("attachment") ) ) + { + /* Don't look for art cover if sout + * XXX It can change when sout has meta data support */ + if( p_input->p->p_sout && !p_input->b_preparsing ) + input_item_SetArtURL( p_item, "" ); + else + input_ExtractAttachmentAndCacheArt( p_input ); + } + } + free( psz_arturl ); + + if( psz_title ) + { + input_item_SetName( p_item, psz_title ); + free( psz_title ); + } + input_item_SetPreparsed( p_item, true ); + + input_SendEventMeta( p_input ); + /* TODO handle sout meta ? */ +} + /* EsOutAdd: * Add an es_out */ @@ -1341,8 +1423,8 @@ static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt ) return NULL; } - es_out_id_t *es = malloc( sizeof( *es ) ); - es_out_pgrm_t *p_pgrm = NULL; + es_out_id_t *es = malloc( sizeof( *es ) ); + es_out_pgrm_t *p_pgrm; int i; if( !es ) @@ -1351,18 +1433,12 @@ static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt ) vlc_mutex_lock( &p_sys->lock ); /* Search the program */ - for( i = 0; i < p_sys->i_pgrm; i++ ) - { - if( fmt->i_group == p_sys->pgrm[i]->i_id ) - { - p_pgrm = p_sys->pgrm[i]; - break; - } - } - if( p_pgrm == NULL ) + p_pgrm = EsOutProgramFind( out, fmt->i_group ); + if( !p_pgrm ) { - /* Create a new one */ - p_pgrm = EsOutProgramAdd( out, fmt->i_group ); + vlc_mutex_unlock( &p_sys->lock ); + free( es ); + return NULL; } /* Increase ref count for program */ @@ -1373,8 +1449,13 @@ static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt ) es_format_Copy( &es->fmt, fmt ); if( es->fmt.i_id < 0 ) es->fmt.i_id = out->p_sys->i_id; + if( !es->fmt.i_original_fourcc ) + es->fmt.i_original_fourcc = es->fmt.i_codec; + es->fmt.i_codec = vlc_fourcc_GetCodec( es->fmt.i_cat, es->fmt.i_codec ); + es->i_id = es->fmt.i_id; es->i_meta_id = out->p_sys->i_id; + es->b_scrambled = false; switch( es->fmt.i_cat ) { @@ -1454,6 +1535,9 @@ static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt ) EsOutUpdateInfo( out, es, &es->fmt, NULL ); + if( es->b_scrambled ) + EsOutProgramUpdateScrambled( out, es->p_pgrm ); + vlc_mutex_unlock( &p_sys->lock ); return es; @@ -1519,7 +1603,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; if( EsIsSelected( es ) ) { @@ -1539,9 +1622,10 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) } else { + const bool b_sout = p_input->p->p_sout != NULL; if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES ) { - if( !var_GetBool( p_input, out->b_sout ? "sout-video" : "video" ) ) + if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) ) { msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", es->i_id ); @@ -1550,8 +1634,7 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) } else if( es->fmt.i_cat == AUDIO_ES ) { - var_Get( p_input, "audio", &val ); - if( !var_GetBool( p_input, out->b_sout ? "sout-audio" : "audio" ) ) + if( !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) ) { msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", es->i_id ); @@ -1560,8 +1643,7 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) } if( es->fmt.i_cat == SPU_ES ) { - var_Get( p_input, "spu", &val ); - if( !var_GetBool( p_input, out->b_sout ? "sout-spu" : "spu" ) ) + if( !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) ) { msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x", es->i_id ); @@ -1577,6 +1659,7 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) /* Mark it as selected */ input_SendEventEsSelect( p_input, es->fmt.i_cat, es->i_id ); + input_SendEventTeletextSelect( p_input, EsFmtIsTeletext( &es->fmt ) ? es->i_id : -1 ); } static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update ) @@ -1623,12 +1706,10 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update ) if( !b_update ) return; - /* Update var */ - if( es->p_dec == NULL ) - return; - /* Mark it as unselected */ input_SendEventEsSelect( p_input, es->fmt.i_cat, -1 ); + if( EsFmtIsTeletext( &es->fmt ) ) + input_SendEventTeletextSelect( p_input, -1 ); } /** @@ -1671,7 +1752,7 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force ) break; } } - var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL ); + var_FreeList( &val, NULL ); } else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) { @@ -1823,6 +1904,19 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) p_block->i_buffer, &i_total ); stats_UpdateFloat( p_input , p_input->p->counters.p_demux_bitrate, (float)i_total, NULL ); + + /* Update number of corrupted data packats */ + if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) + { + stats_UpdateInteger( p_input, p_input->p->counters.p_demux_corrupted, + 1, NULL ); + } + /* Update number of discontinuities */ + if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY ) + { + stats_UpdateInteger( p_input, p_input->p->counters.p_demux_discontinuity, + 1, NULL ); + } vlc_mutex_unlock( &p_input->p->counters.counters_lock ); } @@ -1832,7 +1926,7 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) if( p_sys->i_preroll_end >= 0 ) { int64_t i_date = p_block->i_pts; - if( i_date <= 0 ) + if( p_block->i_pts <= VLC_TS_INVALID ) i_date = p_block->i_dts; if( i_date < p_sys->i_preroll_end ) @@ -1848,14 +1942,34 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) return VLC_SUCCESS; } + /* Check for sout mode */ + if( p_input->p->p_sout ) + { + /* FIXME review this, proper lock may be missing */ + if( p_input->p->p_sout->i_out_pace_nocontrol > 0 && + p_input->p->b_out_pace_control ) + { + msg_Dbg( p_input, "switching to sync mode" ); + p_input->p->b_out_pace_control = false; + } + else if( p_input->p->p_sout->i_out_pace_nocontrol <= 0 && + !p_input->p->b_out_pace_control ) + { + msg_Dbg( p_input, "switching to async mode" ); + p_input->p->b_out_pace_control = true; + } + } + /* Decode */ 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_record, p_dup, + p_input->p->b_out_pace_control ); } - input_DecoderDecode( es->p_dec, p_block ); + input_DecoderDecode( es->p_dec, p_block, + p_input->p->b_out_pace_control ); es_format_t fmt_dsc; vlc_meta_t *p_meta_dsc; @@ -1874,19 +1988,13 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) input_DecoderIsCcPresent( es->p_dec, pb_cc ); for( int 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', ' '), - }; 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] ); + es_format_Init( &fmt, SPU_ES, EsOutFourccClosedCaptions[i] ); fmt.i_group = es->fmt.i_group; if( asprintf( &fmt.psz_description, _("Closed captions %u"), 1 + i ) == -1 ) @@ -1935,12 +2043,15 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) TAB_REMOVE( p_sys->i_es, p_sys->es, es ); + /* Update program */ es->p_pgrm->i_es--; if( es->p_pgrm->i_es == 0 ) - { msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" ); - } + if( es->b_scrambled ) + EsOutProgramUpdateScrambled( out, es->p_pgrm ); + + /* */ if( p_sys->p_es_audio == es || p_sys->p_es_video == es || p_sys->p_es_sub == es ) b_reselect = true; @@ -1992,16 +2103,13 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) 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; - - es_out_id_t *es; switch( i_query ) { case ES_OUT_SET_ES_STATE: - es = (es_out_id_t*) va_arg( args, es_out_id_t * ); - b = (bool) va_arg( args, int ); + { + es_out_id_t *es = va_arg( args, es_out_id_t * ); + bool b = va_arg( args, int ); if( b && !EsIsSelected( es ) ) { EsSelect( out, es ); @@ -2013,22 +2121,30 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) return VLC_SUCCESS; } return VLC_SUCCESS; + } case ES_OUT_GET_ES_STATE: - es = (es_out_id_t*) va_arg( args, es_out_id_t * ); - pb = (bool*) va_arg( args, bool * ); + { + es_out_id_t *es = va_arg( args, es_out_id_t * ); + bool *pb = va_arg( args, bool * ); *pb = EsIsSelected( es ); return VLC_SUCCESS; + } - case ES_OUT_SET_ACTIVE: + case ES_OUT_SET_MODE: { - b = (bool) va_arg( args, int ); - if( b && !p_sys->b_active && p_sys->i_es > 0 ) + const int i_mode = va_arg( args, int ); + assert( i_mode == ES_OUT_MODE_NONE || i_mode == ES_OUT_MODE_ALL || + i_mode == ES_OUT_MODE_AUTO || i_mode == ES_OUT_MODE_PARTIAL || + i_mode == ES_OUT_MODE_END ); + + if( i_mode != ES_OUT_MODE_NONE && !p_sys->b_active && p_sys->i_es > 0 ) { /* XXX Terminate vout if there are tracks but no video one. * This one is not mandatory but is he earliest place where it * can be done */ + int i; for( i = 0; i < p_sys->i_es; i++ ) { es_out_id_t *p_es = p_sys->es[i]; @@ -2036,43 +2152,31 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) break; } if( i >= p_sys->i_es ) - input_ressource_TerminateVout( p_sys->p_input->p->p_ressource ); + input_resource_TerminateVout( p_sys->p_input->p->p_resource ); } - p_sys->b_active = b; - return VLC_SUCCESS; - } + p_sys->b_active = i_mode != ES_OUT_MODE_NONE; + p_sys->i_mode = i_mode; - case ES_OUT_SET_MODE: - i = (int) va_arg( args, int ); - if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL || - i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL ) + /* Reapply policy mode */ + for( int i = 0; i < p_sys->i_es; i++ ) { - p_sys->i_mode = i; - - /* Reapply policy mode */ - 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 ); - } - } - for( i = 0; i < p_sys->i_es; i++ ) - { - EsOutSelect( out, p_sys->es[i], false ); - } - return VLC_SUCCESS; + if( EsIsSelected( p_sys->es[i] ) ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } - return VLC_EGENERIC; + for( int i = 0; i < p_sys->i_es; i++ ) + EsOutSelect( out, p_sys->es[i], false ); + if( i_mode == ES_OUT_MODE_END ) + EsOutTerminate( out ); + 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 * ); + es_out_id_t *es = va_arg( args, es_out_id_t * ); + int i_cat; if( es == NULL ) i_cat = UNKNOWN_ES; else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) ) @@ -2084,7 +2188,7 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) else i_cat = -1; - for( i = 0; i < p_sys->i_es; i++ ) + for( int i = 0; i < p_sys->i_es; i++ ) { if( i_cat == -1 ) { @@ -2122,7 +2226,7 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_ES_DEFAULT: { - es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + es_out_id_t *es = va_arg( args, es_out_id_t * ); if( es == NULL ) { @@ -2163,35 +2267,65 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) int i_group = 0; int64_t i_pcr; + /* Search program */ if( i_query == ES_OUT_SET_PCR ) { p_pgrm = p_sys->p_pgrm; + if( !p_pgrm ) + p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ } else { - int i; i_group = (int)va_arg( args, int ); - for( i = 0; i < p_sys->i_pgrm; i++ ) - { - if( p_sys->pgrm[i]->i_id == i_group ) - { - p_pgrm = p_sys->pgrm[i]; - break; - } - } + p_pgrm = EsOutProgramFind( out, i_group ); } - if( p_pgrm == NULL ) - p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ + if( !p_pgrm ) + return VLC_EGENERIC; i_pcr = (int64_t)va_arg( args, int64_t ); - /* search program - * TODO do not use mdate() but proper stream acquisition date */ + if( i_pcr <= VLC_TS_INVALID ) + { + msg_Err( p_sys->p_input, "Invalid PCR value in ES_OUT_SET_(GROUP_)PCR !" ); + return VLC_EGENERIC; + } + + /* TODO do not use mdate() but proper stream acquisition date */ + bool b_late; input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input), - p_sys->p_input->p->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 ); + &b_late, + p_sys->p_input->p->b_can_pace_control || p_sys->b_buffering, + EsOutIsExtraBufferingAllowed( out ), + i_pcr, mdate() ); + + if( p_pgrm == p_sys->p_pgrm ) + { + if( p_sys->b_buffering ) + { + /* Check buffering state on master clock update */ + EsOutDecodersStopBuffering( out, false ); + } + else if( b_late && ( !p_sys->p_input->p->p_sout || + !p_sys->p_input->p->b_out_pace_control ) ) + { + mtime_t i_pts_delay = input_clock_GetJitter( p_pgrm->p_clock ); + + /* Avoid dangerously high value */ + const mtime_t i_pts_delay_max = 30000000; + if( i_pts_delay > i_pts_delay_max ) + i_pts_delay = __MAX( i_pts_delay_max, p_sys->i_pts_delay ); + /* Force a rebufferization when we are too late */ + msg_Err( p_sys->p_input, + "ES_OUT_SET_(GROUP_)PCR is called too late, increasing pts_delay to %d ms", + (int)(i_pts_delay/1000) ); + + /* It is not really good, as we throw away already buffered data + * TODO have a mean to correctly reenter bufferization */ + es_out_Control( out, ES_OUT_RESET_PCR ); + + es_out_Control( out, ES_OUT_SET_JITTER, i_pts_delay, p_sys->i_cr_average ); + } + } return VLC_SUCCESS; } @@ -2202,9 +2336,8 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_GROUP: { - int j; - i = (int) va_arg( args, int ); - for( j = 0; j < p_sys->i_pgrm; j++ ) + int i = va_arg( args, int ); + for( int j = 0; j < p_sys->i_pgrm; j++ ) { es_out_pgrm_t *p_pgrm = p_sys->pgrm[j]; if( p_pgrm->i_id == i ) @@ -2220,16 +2353,15 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) { /* This ain't pretty but is need by some demuxers (eg. Ogg ) * to update the p_extra data */ - es_format_t *p_fmt; - es = (es_out_id_t*) va_arg( args, es_out_id_t * ); - p_fmt = (es_format_t*) va_arg( args, es_format_t * ); + es_out_id_t *es = va_arg( args, es_out_id_t * ); + es_format_t *p_fmt = va_arg( args, es_format_t * ); if( es == NULL ) return VLC_EGENERIC; if( p_fmt->i_extra ) { es->fmt.i_extra = p_fmt->i_extra; - es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra ); + es->fmt.p_extra = xrealloc( es->fmt.p_extra, p_fmt->i_extra ); memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra ); if( !es->p_dec ) @@ -2241,7 +2373,7 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) #else es->p_dec->fmt_in.i_extra = p_fmt->i_extra; es->p_dec->fmt_in.p_extra = - realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra ); + xrealloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra ); memcpy( es->p_dec->fmt_in.p_extra, p_fmt->p_extra, p_fmt->i_extra ); #endif @@ -2250,6 +2382,19 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) return VLC_SUCCESS; } + case ES_OUT_SET_ES_SCRAMBLED_STATE: + { + es_out_id_t *es = va_arg( args, es_out_id_t * ); + bool b_scrambled = (bool)va_arg( args, int ); + + if( !es->b_scrambled != !b_scrambled ) + { + es->b_scrambled = b_scrambled; + EsOutProgramUpdateScrambled( out, es->p_pgrm ); + } + return VLC_SUCCESS; + } + case ES_OUT_SET_NEXT_DISPLAY_TIME: { const int64_t i_date = (int64_t)va_arg( args, int64_t ); @@ -2264,7 +2409,7 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_GROUP_META: { int i_group = (int)va_arg( args, int ); - vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * ); + const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * ); EsOutProgramMeta( out, i_group, p_meta ); return VLC_SUCCESS; @@ -2272,11 +2417,12 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_GROUP_EPG: { int i_group = (int)va_arg( args, int ); - vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * ); + const vlc_epg_t *p_epg = va_arg( args, const vlc_epg_t * ); EsOutProgramEpg( out, i_group, p_epg ); return VLC_SUCCESS; } + case ES_OUT_DEL_GROUP: { int i_group = (int)va_arg( args, int ); @@ -2284,6 +2430,14 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) return EsOutProgramDel( out, i_group ); } + case ES_OUT_SET_META: + { + const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * ); + + EsOutMeta( out, p_meta ); + return VLC_SUCCESS; + } + case ES_OUT_GET_WAKE_UP: { mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* ); @@ -2313,19 +2467,51 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) /* Clean up vout after user action (in active mode only). * FIXME it does not work well with multiple video windows */ if( p_sys->b_active ) - input_ressource_TerminateVout( p_sys->p_input->p->p_ressource ); + input_resource_TerminateVout( p_sys->p_input->p->p_resource ); return i_ret; } + case ES_OUT_GET_ES_OBJECTS_BY_ID: + { + const int i_id = va_arg( args, int ); + es_out_id_t *p_es = EsOutGetFromID( out, i_id ); + if( !p_es ) + return VLC_EGENERIC; + + vlc_object_t **pp_decoder = va_arg( args, vlc_object_t ** ); + vout_thread_t **pp_vout = va_arg( args, vout_thread_t ** ); + aout_instance_t **pp_aout = va_arg( args, aout_instance_t ** ); + if( p_es->p_dec ) + { + if( pp_decoder ) + *pp_decoder = vlc_object_hold( p_es->p_dec ); + input_DecoderGetObjects( p_es->p_dec, pp_vout, pp_aout ); + } + else + { + if( pp_decoder ) + *pp_decoder = NULL; + if( pp_vout ) + *pp_vout = NULL; + if( pp_aout ) + *pp_aout = NULL; + } + return VLC_SUCCESS; + } + case ES_OUT_GET_BUFFERING: - pb = (bool *)va_arg( args, bool* ); + { + bool *pb = va_arg( args, bool* ); *pb = p_sys->b_buffering; return VLC_SUCCESS; + } case ES_OUT_GET_EMPTY: - pb = (bool *)va_arg( args, bool* ); + { + bool *pb = va_arg( args, bool* ); *pb = EsOutDecodersIsEmpty( out ); return VLC_SUCCESS; + } case ES_OUT_SET_DELAY: { @@ -2336,8 +2522,10 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) } case ES_OUT_SET_RECORD_STATE: - b = (bool) va_arg( args, int ); + { + bool b = va_arg( args, int ); return EsOutSetRecord( out, b ); + } case ES_OUT_SET_PAUSE_STATE: { @@ -2382,20 +2570,30 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) mtime_t i_time = (mtime_t)va_arg( args, mtime_t ); mtime_t i_length = (mtime_t)va_arg( args, mtime_t ); - /* Fix for buffering delay */ - const mtime_t i_delay = EsOutGetBuffering( out ); + input_SendEventLength( p_sys->p_input, i_length ); - i_time -= i_delay; - if( i_time < 0 ) - i_time = 0; + if( !p_sys->b_buffering ) + { + mtime_t i_delay; - if( i_length > 0 ) - f_position -= (double)i_delay / i_length; - if( f_position < 0 ) - f_position = 0; + /* Fix for buffering delay */ + if( !p_sys->p_input->p->p_sout || + !p_sys->p_input->p->b_out_pace_control ) + i_delay = EsOutGetBuffering( out ); + else + i_delay = 0; - if( !p_sys->b_buffering ) - input_SendEventTimes( p_sys->p_input, f_position, i_time, i_length ); + i_time -= i_delay; + if( i_time < 0 ) + i_time = 0; + + if( i_length > 0 ) + f_position -= (double)i_delay / i_length; + if( f_position < 0 ) + f_position = 0; + + input_SendEventPosition( p_sys->p_input, f_position, i_time ); + } return VLC_SUCCESS; } case ES_OUT_SET_JITTER: @@ -2416,6 +2614,36 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) return VLC_SUCCESS; } + case ES_OUT_GET_PCR_SYSTEM: + { + if( p_sys->b_buffering ) + return VLC_EGENERIC; + + es_out_pgrm_t *p_pgrm = p_sys->p_pgrm; + if( !p_pgrm ) + return VLC_EGENERIC; + + mtime_t *pi_system = va_arg( args, mtime_t *); + mtime_t *pi_delay = va_arg( args, mtime_t *); + input_clock_GetSystemOrigin( p_pgrm->p_clock, pi_system, pi_delay ); + return VLC_SUCCESS; + } + + case ES_OUT_MODIFY_PCR_SYSTEM: + { + if( p_sys->b_buffering ) + return VLC_EGENERIC; + + es_out_pgrm_t *p_pgrm = p_sys->p_pgrm; + if( !p_pgrm ) + return VLC_EGENERIC; + + const bool b_absolute = va_arg( args, int ); + const mtime_t i_system = va_arg( args, mtime_t ); + input_clock_ChangeSystemOrigin( p_pgrm->p_clock, b_absolute, i_system ); + return VLC_SUCCESS; + } + default: msg_Err( p_sys->p_input, "unknown query in es_out_Control" ); return VLC_EGENERIC; @@ -2484,7 +2712,7 @@ static char *LanguageGetCode( const char *psz_lang ) if( psz_lang == NULL || *psz_lang == '\0' ) return strdup("??"); - for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ ) + for( pl = p_languages; pl->psz_eng_name != NULL; pl++ ) { if( !strcasecmp( pl->psz_eng_name, psz_lang ) || !strcasecmp( pl->psz_native_name, psz_lang ) || @@ -2494,13 +2722,13 @@ static char *LanguageGetCode( const char *psz_lang ) break; } - if( pl->psz_iso639_1 != NULL ) + if( pl->psz_eng_name != NULL ) return strdup( pl->psz_iso639_1 ); return strdup("??"); } -static char **LanguageSplit( const char *psz_langs ) +static char **LanguageSplit( const char *psz_langs, bool b_default_any ) { char *psz_dup; char *psz_parser; @@ -2523,6 +2751,10 @@ static char **LanguageSplit( const char *psz_langs ) { TAB_APPEND( i_psz, ppsz, strdup("any") ); } + else if( !strcmp( psz_parser, "none" ) ) + { + TAB_APPEND( i_psz, ppsz, strdup("none") ); + } else { psz_code = LanguageGetCode( psz_parser ); @@ -2541,6 +2773,8 @@ static char **LanguageSplit( const char *psz_langs ) if( i_psz ) { + if( b_default_any && strcmp( ppsz[i_psz - 1], "none" ) ) + TAB_APPEND( i_psz, ppsz, strdup("any") ); TAB_APPEND( i_psz, ppsz, NULL ); } @@ -2558,9 +2792,9 @@ static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang ) { if( !strcasecmp( ppsz_langs[i], psz_lang ) || !strcasecmp( ppsz_langs[i], "any" ) ) - { return i; - } + if( !strcasecmp( ppsz_langs[i], "none" ) ) + break; } return -1; @@ -2575,16 +2809,15 @@ static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t * es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; const es_format_t *p_fmt_es = &es->fmt; - char *psz_cat; lldiv_t div; - /* Create category name */ - if( asprintf( &psz_cat, _("Stream %d"), es->i_meta_id ) == -1 ) + /* Create category */ + char psz_cat[128]; + snprintf( psz_cat, sizeof(psz_cat),_("Stream %d"), es->i_meta_id ); + info_category_t *p_cat = info_category_New( psz_cat ); + if( !p_cat ) return; - /* Remove previous information */ - input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL ); - /* Add informations */ const char *psz_type; switch( fmt->i_cat ) @@ -2604,39 +2837,45 @@ static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t * } if( psz_type ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Type"), psz_type ); + info_category_AddInfo( p_cat, _("Type"), "%s", psz_type ); if( es->i_meta_id != es->i_id ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Original ID"), + info_category_AddInfo( p_cat, _("Original ID"), "%d", es->i_id ); - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"), - "%.4s", (char*)&p_fmt_es->i_codec ); + const char *psz_codec_description = + vlc_fourcc_GetDescription( p_fmt_es->i_cat, p_fmt_es->i_codec ); + const vlc_fourcc_t i_codec_fourcc = p_fmt_es->i_original_fourcc ?: p_fmt_es->i_codec; + if( psz_codec_description ) + info_category_AddInfo( p_cat, _("Codec"), "%s (%.4s)", + psz_codec_description, (char*)&i_codec_fourcc ); + else + info_category_AddInfo( p_cat, _("Codec"), "%.4s", + (char*)&i_codec_fourcc ); if( es->psz_language && *es->psz_language ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"), - "%s", es->psz_language ); + info_category_AddInfo( p_cat, _("Language"), "%s", + es->psz_language ); if( fmt->psz_description && *fmt->psz_description ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Description"), - "%s", fmt->psz_description ); + info_category_AddInfo( p_cat, _("Description"), "%s", + fmt->psz_description ); switch( fmt->i_cat ) { case AUDIO_ES: - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Type"), _("Audio") ); + info_category_AddInfo( p_cat, _("Type"), _("Audio") ); if( fmt->audio.i_physical_channels & AOUT_CHAN_PHYSMASK ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"), - "%s", aout_FormatPrintChannels( &fmt->audio ) ); + info_category_AddInfo( p_cat, _("Channels"), "%s", + _( aout_FormatPrintChannels( &fmt->audio ) ) ); else if( fmt->audio.i_channels > 0 ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"), - "%u", fmt->audio.i_channels ); + info_category_AddInfo( p_cat, _("Channels"), "%u", + fmt->audio.i_channels ); if( fmt->audio.i_rate > 0 ) { - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"), - _("%u Hz"), fmt->audio.i_rate ); + info_category_AddInfo( p_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 ); } @@ -2645,14 +2884,13 @@ static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t * if( i_bitspersample <= 0 ) i_bitspersample = aout_BitsPerSample( p_fmt_es->i_codec ); if( i_bitspersample > 0 ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Bits per sample"), "%u", - i_bitspersample ); + info_category_AddInfo( p_cat, _("Bits per sample"), "%u", + i_bitspersample ); if( fmt->i_bitrate > 0 ) { - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"), - _("%u kb/s"), fmt->i_bitrate / 1000 ); + info_category_AddInfo( p_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 ); } @@ -2666,26 +2904,23 @@ static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t * psz_name = _("Track replay gain"); else psz_name = _("Album replay gain"); - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - psz_name, _("%.2f dB"), p_rg->pf_gain[i] ); + info_category_AddInfo( p_cat, psz_name, _("%.2f dB"), + p_rg->pf_gain[i] ); } break; case VIDEO_ES: - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Type"), _("Video") ); + info_category_AddInfo( p_cat, _("Type"), _("Video") ); if( fmt->video.i_width > 0 && fmt->video.i_height > 0 ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Resolution"), "%ux%u", - fmt->video.i_width, fmt->video.i_height ); + info_category_AddInfo( p_cat, _("Resolution"), "%ux%u", + fmt->video.i_width, fmt->video.i_height ); if( fmt->video.i_visible_width > 0 && fmt->video.i_visible_height > 0 ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Display resolution"), "%ux%u", - fmt->video.i_visible_width, - fmt->video.i_visible_height); + info_category_AddInfo( p_cat, _("Display resolution"), "%ux%u", + fmt->video.i_visible_width, + fmt->video.i_visible_height); if( fmt->video.i_frame_rate > 0 && fmt->video.i_frame_rate_base > 0 ) { @@ -2693,18 +2928,16 @@ static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t * fmt->video.i_frame_rate_base * 1000000, 1000000 ); if( div.rem > 0 ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Frame rate"), "%"PRId64".%06u", - div.quot, (unsigned int )div.rem ); + info_category_AddInfo( p_cat, _("Frame rate"), "%"PRId64".%06u", + div.quot, (unsigned int )div.rem ); else - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Frame rate"), "%"PRId64, div.quot ); + info_category_AddInfo( p_cat, _("Frame rate"), "%"PRId64, + div.quot ); } break; case SPU_ES: - input_Control( p_input, INPUT_ADD_INFO, psz_cat, - _("Type"), _("Subtitle") ); + info_category_AddInfo( p_cat, _("Type"), _("Subtitle") ); break; default: @@ -2714,18 +2947,20 @@ static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t * /* Append generic meta */ if( p_meta ) { - char **ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags ); + char **ppsz_all_keys = vlc_meta_CopyExtraNames( p_meta ); for( int i = 0; ppsz_all_keys && ppsz_all_keys[i]; i++ ) { char *psz_key = ppsz_all_keys[i]; - char *psz_value = vlc_dictionary_value_for_key( &p_meta->extra_tags, psz_key ); + const char *psz_value = vlc_meta_GetExtra( p_meta, psz_key ); if( psz_value ) - input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(psz_key), _(psz_value) ); + info_category_AddInfo( p_cat, vlc_gettext(psz_key), "%s", + vlc_gettext(psz_value) ); free( psz_key ); } free( ppsz_all_keys ); } - - free( psz_cat ); + /* */ + input_Control( p_input, INPUT_REPLACE_INFOS, p_cat ); } +