X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Fes_out.c;h=98f4fa0656cbdb227b1ad760e598c71b0ed3829c;hb=886975814e0cb1a2688d063f24e665168b2aae85;hp=8d7e76ff5991bb344aa3b29ee7cf8dcc0277d0a7;hpb=2ba646a5ea654996b0e627d589bd712acebb2bf8;p=vlc diff --git a/src/input/es_out.c b/src/input/es_out.c index 8d7e76ff59..98f4fa0656 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -1,10 +1,11 @@ /***************************************************************************** * es_out.c: Es Out handler for input. ***************************************************************************** - * Copyright (C) 2003-2004 VideoLAN + * Copyright (C) 2003-2004 the VideoLAN team * $Id$ * * Authors: Laurent Aimar + * Jean-Paul Saman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,22 +19,27 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ +#include + +#include #include -#include -#include -#include +#include +#include +#include #include "input_internal.h" #include "vlc_playlist.h" #include "iso_lang.h" +/* FIXME we should find a better way than including that */ +#include "../text/iso-639_def.h" /***************************************************************************** * Local prototypes @@ -51,6 +57,8 @@ typedef struct /* Clock for this program */ input_clock_t clock; + char *psz_now_playing; + } es_out_pgrm_t; struct es_out_id_t @@ -59,10 +67,17 @@ struct es_out_id_t int i_id; es_out_pgrm_t *p_pgrm; + /* Signal a discontinuity in the timeline for every PID */ + vlc_bool_t b_discontinuity; + + /* Misc. */ + int64_t i_preroll_end; + /* Channel in the track type */ int i_channel; es_format_t fmt; - char *psz_description; + char *psz_language; + char *psz_language_code; decoder_t *p_dec; }; @@ -73,6 +88,7 @@ struct es_out_sys_t /* all programs */ int i_pgrm; es_out_pgrm_t **pgrm; + es_out_pgrm_t **pp_selected_pgrm; /* --programs */ es_out_pgrm_t *p_pgrm; /* Master program */ /* all es */ @@ -90,8 +106,10 @@ struct es_out_sys_t int i_sub; /* es to select */ - int i_audio_last; - int i_sub_last; + int i_audio_last, i_audio_id; + int i_sub_last, i_sub_id; + char **ppsz_audio_language; + char **ppsz_sub_language; /* current main es */ es_out_id_t *p_es_audio; @@ -114,6 +132,9 @@ static void EsOutAddInfo( es_out_t *, 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, vlc_bool_t b_update ); static char *LanguageGetName( const char *psz_code ); +static char *LanguageGetCode( const char *psz_lang ); +static char **LanguageSplit( const char *psz_langs ); +static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang ); /***************************************************************************** * input_EsOutNew: @@ -123,12 +144,14 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) es_out_t *out = malloc( sizeof( es_out_t ) ); es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) ); vlc_value_t val; + int i; out->pf_add = EsOutAdd; out->pf_send = EsOutSend; out->pf_del = EsOutDel; out->pf_control = EsOutControl; out->p_sys = p_sys; + out->b_sout = (p_input->p->p_sout != NULL ? VLC_TRUE : VLC_FALSE); p_sys->p_input = p_input; @@ -148,12 +171,47 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) p_sys->i_video = 0; p_sys->i_sub = 0; - var_Get( p_input, "audio-channel", &val ); + /* */ + var_Get( p_input, "audio-track", &val ); p_sys->i_audio_last = val.i_int; - var_Get( p_input, "spu-channel", &val ); + var_Get( p_input, "sub-track", &val ); p_sys->i_sub_last = val.i_int; + if( !p_input->b_preparsing ) + { + var_Get( p_input, "audio-language", &val ); + p_sys->ppsz_audio_language = LanguageSplit(val.psz_string); + if( p_sys->ppsz_audio_language ) + { + for( 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] ); + } + if( val.psz_string ) free( val.psz_string ); + + var_Get( p_input, "sub-language", &val ); + p_sys->ppsz_sub_language = LanguageSplit(val.psz_string); + if( p_sys->ppsz_sub_language ) + { + for( 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] ); + } + if( val.psz_string ) free( val.psz_string ); + } + else + { + p_sys->ppsz_sub_language = NULL; + p_sys->ppsz_audio_language = NULL; + } + + var_Get( p_input, "audio-track-id", &val ); + p_sys->i_audio_id = val.i_int; + + var_Get( p_input, "sub-track-id", &val ); + p_sys->i_sub_id = val.i_int; + p_sys->p_es_audio = NULL; p_sys->p_es_video = NULL; p_sys->p_es_sub = NULL; @@ -178,17 +236,34 @@ void input_EsOutDelete( es_out_t *out ) { input_DecoderDelete( p_sys->es[i]->p_dec ); } - if( p_sys->es[i]->psz_description ) - free( p_sys->es[i]->psz_description ); + if( p_sys->es[i]->psz_language ) + free( p_sys->es[i]->psz_language ); + if( p_sys->es[i]->psz_language_code ) + free( p_sys->es[i]->psz_language_code ); es_format_Clean( &p_sys->es[i]->fmt ); free( p_sys->es[i] ); } + if( p_sys->ppsz_audio_language ) + { + for( i = 0; p_sys->ppsz_audio_language[i]; i++ ) + free( p_sys->ppsz_audio_language[i] ); + free( p_sys->ppsz_audio_language ); + } + if( p_sys->ppsz_sub_language ) + { + for( i = 0; p_sys->ppsz_sub_language[i]; i++ ) + free( p_sys->ppsz_sub_language[i] ); + free( p_sys->ppsz_sub_language ); + } + if( p_sys->es ) free( p_sys->es ); for( i = 0; i < p_sys->i_pgrm; i++ ) { + if( p_sys->pgrm[i]->psz_now_playing ) + free( p_sys->pgrm[i]->psz_now_playing ); free( p_sys->pgrm[i] ); } if( p_sys->pgrm ) @@ -223,6 +298,7 @@ void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio ) for( i = 0; i < p_sys->i_es; i++ ) { es_out_id_t *es = p_sys->es[i]; + es->b_discontinuity = VLC_TRUE; /* signal discontinuity */ /* Send a dummy block to let decoder know that * there is a discontinuity */ @@ -268,7 +344,7 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, input_thread_t *p_input = p_sys->p_input; vlc_value_t val, text; - char *psz_var; + const char *psz_var; if( es->fmt.i_cat == AUDIO_ES ) psz_var = "audio-es"; @@ -300,14 +376,33 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, } /* Take care of the ES description */ - if( es->psz_description && *es->psz_description ) + if( es->fmt.psz_description && *es->fmt.psz_description ) { - text.psz_string = strdup( es->psz_description ); + if( es->psz_language && *es->psz_language ) + { + text.psz_string = malloc( strlen( es->fmt.psz_description) + + strlen( es->psz_language ) + 10 ); + sprintf( text.psz_string, "%s - [%s]", es->fmt.psz_description, + es->psz_language ); + } + else text.psz_string = strdup( es->fmt.psz_description ); } else { - text.psz_string = malloc( strlen( _("Track %i") ) + 20 ); - sprintf( text.psz_string, _("Track %i"), val.i_int ); + if( es->psz_language && *es->psz_language ) + { + char *temp; + text.psz_string = malloc( strlen( _("Track %i") )+ + strlen( es->psz_language ) + 30 ); + asprintf( &temp, _("Track %i"), val.i_int ); + sprintf( text.psz_string, "%s - [%s]", temp, es->psz_language ); + free( temp ); + } + else + { + text.psz_string = malloc( strlen( _("Track %i") ) + 20 ); + sprintf( text.psz_string, _("Track %i"), val.i_int ); + } } val.i_int = es->i_id; @@ -334,7 +429,7 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) if( p_sys->p_pgrm ) { es_out_pgrm_t *old = p_sys->p_pgrm; - msg_Dbg( p_input, "Unselecting program id=%d", old->i_id ); + msg_Dbg( p_input, "unselecting program id=%d", old->i_id ); for( i = 0; i < p_sys->i_es; i++ ) { @@ -342,9 +437,13 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) p_sys->i_mode != ES_OUT_MODE_ALL ) EsUnselect( out, p_sys->es[i], VLC_TRUE ); } + + p_sys->p_es_audio = NULL; + p_sys->p_es_sub = NULL; + p_sys->p_es_video = NULL; } - msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id ); + msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id ); /* Mark it selected */ p_pgrm->b_selected = VLC_TRUE; @@ -367,10 +466,23 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); for( i = 0; i < p_sys->i_es; i++ ) { - EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE ); + if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm ) + EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE ); EsOutSelect( out, p_sys->es[i], VLC_FALSE ); } + /* Update now playing if defined per program */ + if( p_pgrm->psz_now_playing ) + { + char *psz_cat = malloc( strlen(_("Program")) + 10 ); + + sprintf( psz_cat, "%s %d", _("Program"), p_pgrm->i_id ); + input_Control( p_input, INPUT_ADD_INFO, _(VLC_META_INFO_CAT), + _(VLC_META_NOW_PLAYING), "%s", p_pgrm->psz_now_playing ); + free( psz_cat ); + } + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); } @@ -389,7 +501,8 @@ 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 = VLC_FALSE; - input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average ); + p_pgrm->psz_now_playing = NULL; + input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->p->input.i_cr_average ); /* Append it */ TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); @@ -409,6 +522,130 @@ static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group ) return p_pgrm; } +/* EsOutDelProgram: + * Delete a program + */ +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++ ) + { + if( p_sys->pgrm[i]->i_id == i_group ) + { + p_pgrm = p_sys->pgrm[i]; + break; + } + } + + if( p_pgrm == NULL ) + return VLC_EGENERIC; + + if( p_pgrm->i_es ) + { + msg_Dbg( p_input, "can't delete program %d which still has %i ES", + i_group, p_pgrm->i_es ); + return VLC_EGENERIC; + } + + 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 = 0; + + if( p_pgrm->psz_now_playing ) free( p_pgrm->psz_now_playing ); + 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", VLC_TRUE ); + + return VLC_SUCCESS; +} + +/* EsOutProgramMeta: + */ +static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta ) +{ + es_out_sys_t *p_sys = out->p_sys; + es_out_pgrm_t *p_pgrm = NULL; + input_thread_t *p_input = p_sys->p_input; + char *psz_cat = malloc( strlen(_("Program")) + 10 ); + char *psz_title = NULL; + char *psz_now_playing = NULL; + char *psz_provider = NULL; + int i; + + msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group ); + sprintf( psz_cat, "%s %d", _("Program"), i_group ); + + if( p_meta->psz_title ) psz_title = p_meta->psz_title; + if( p_meta->psz_publisher ) psz_provider = p_meta->psz_publisher; + if( p_meta->psz_nowplaying ) psz_now_playing = p_meta->psz_nowplaying; + + if( !psz_title && !psz_now_playing ) + { + free( psz_cat ); + return; + } + + 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 ) { + free( psz_cat ); + msg_Dbg( p_input, "Trying to add meta for non-existing program" ); + return; + } + + /* Update the description text of the program */ + if( psz_title && *psz_title ) + { + vlc_value_t val; + vlc_value_t text; + + /* ugly but it works */ + val.i_int = i_group; + var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL ); + + if( psz_provider && *psz_provider ) + { + asprintf( &text.psz_string, "%s [%s]", psz_title, psz_provider ); + var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text ); + free( text.psz_string ); + } + else + { + text.psz_string = psz_title; + var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text ); + } + } + if( psz_now_playing ) + { + if( p_pgrm->psz_now_playing ) free( p_pgrm->psz_now_playing ); + p_pgrm->psz_now_playing = strdup(psz_now_playing); + + if( p_sys->p_pgrm == p_pgrm ) + { + vlc_meta_SetNowPlaying( p_input->p->input.p_item->p_meta, + psz_now_playing ); + } + } + free( psz_cat ); +} + /* EsOutAdd: * Add an es_out */ @@ -423,7 +660,7 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) if( fmt->i_group < 0 ) { - msg_Err( p_input, "invakud group number" ); + msg_Err( p_input, "invalid group number" ); return NULL; } @@ -451,6 +688,9 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) es->i_id = fmt->i_id; es->p_pgrm = p_pgrm; es_format_Copy( &es->fmt, fmt ); + es->i_preroll_end = -1; + es->b_discontinuity = VLC_FALSE; + switch( fmt->i_cat ) { case AUDIO_ES: @@ -459,6 +699,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 ) + 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 ); break; case SPU_ES: @@ -469,7 +714,8 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) es->i_channel = 0; break; } - es->psz_description = LanguageGetName( fmt->psz_language ); + 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->p_dec = NULL; if( es->p_pgrm == p_sys->p_pgrm ) @@ -504,7 +750,7 @@ 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; - char *psz_var; + const char *psz_var; if( es->p_dec ) { @@ -515,7 +761,7 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES ) { if( !var_GetBool( p_input, "video" ) || - ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) ) + ( p_input->p->p_sout && !var_GetBool( p_input, "sout-video" ) ) ) { msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", es->i_id ); @@ -526,14 +772,26 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) { var_Get( p_input, "audio", &val ); if( !var_GetBool( p_input, "audio" ) || - ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) ) + ( p_input->p->p_sout && !var_GetBool( p_input, "sout-audio" ) ) ) { msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", es->i_id ); return; } } + if( es->fmt.i_cat == SPU_ES ) + { + var_Get( p_input, "spu", &val ); + if( !var_GetBool( p_input, "spu" ) || + ( p_input->p->p_sout && !var_GetBool( p_input, "sout-spu" ) ) ) + { + msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x", + es->i_id ); + return; + } + } + es->i_preroll_end = -1; es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE ); if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm ) return; @@ -560,7 +818,7 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update ) es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; vlc_value_t val; - char *psz_var; + const char *psz_var; if( es->p_dec == NULL ) { @@ -619,6 +877,22 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) if( !es->p_dec ) EsSelect( out, es ); } + else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL ) + { + vlc_value_t val; + int i; + var_Get( p_sys->p_input, "programs", &val ); + for ( i = 0; i < val.p_list->i_count; i++ ) + { + if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force ) + { + if( !es->p_dec ) + EsSelect( out, es ); + break; + } + } + var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL ); + } else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) { int i_wanted = -1; @@ -628,23 +902,79 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) if( i_cat == AUDIO_ES ) { + int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language, + es->psz_language_code ); + if( p_sys->p_es_audio && p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority ) { - return; + int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language, + p_sys->p_es_audio->psz_language_code ); + + if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) ) + return; + i_wanted = es->i_channel; + } + else + { + /* Select audio if (no audio selected yet) + * - no audio-language + * - no audio code for the ES + * - audio code in the requested list */ + if( idx1 >= 0 || + !strcmp( es->psz_language_code, "??" ) || + !p_sys->ppsz_audio_language ) + i_wanted = es->i_channel; + } + + if( p_sys->i_audio_last >= 0 ) + i_wanted = p_sys->i_audio_last; + + if( p_sys->i_audio_id >= 0 ) + { + if( es->i_id == p_sys->i_audio_id ) + i_wanted = es->i_channel; + else + return; } - i_wanted = p_sys->i_audio_last >= 0 ? - p_sys->i_audio_last : es->i_channel; } else if( i_cat == SPU_ES ) { + int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language, + es->psz_language_code ); + if( p_sys->p_es_sub && - p_sys->p_es_sub->fmt.i_priority >= - es->fmt.i_priority ) + p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority ) { - return; + int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language, + p_sys->p_es_sub->psz_language_code ); + + msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)", + idx1, es->psz_language_code, idx2, + p_sys->p_es_sub->psz_language_code ); + + if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) ) + return; + /* We found a SPU that matches our language request */ + i_wanted = es->i_channel; + } + else if( idx1 >= 0 ) + { + msg_Dbg( p_sys->p_input, "idx1=%d(%s)", + idx1, es->psz_language_code ); + + i_wanted = es->i_channel; + } + if( p_sys->i_sub_last >= 0 ) + i_wanted = p_sys->i_sub_last; + + if( p_sys->i_sub_id >= 0 ) + { + if( es->i_id == p_sys->i_sub_id ) + i_wanted = es->i_channel; + else + return; } - i_wanted = p_sys->i_sub_last; } else if( i_cat == VIDEO_ES ) { @@ -700,6 +1030,7 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) 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; @@ -708,6 +1039,29 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) else i_delay = 0; + if( p_input->p_libvlc->b_stats ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( p_input, p_input->p->counters.p_demux_read, + p_block->i_buffer, &i_total ); + stats_UpdateFloat( p_input , p_input->p->counters.p_demux_bitrate, + (float)i_total, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + } + + /* Mark preroll blocks */ + if( es->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 ) + p_block->i_flags |= BLOCK_FLAG_PREROLL; + else + es->i_preroll_end = -1; + } + /* +11 -> avoid null value with non null dts/pts */ if( p_block->i_dts > 0 ) { @@ -721,11 +1075,25 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) input_ClockGetTS( p_input, &p_pgrm->clock, ( p_block->i_pts + 11 ) * 9 / 100 ) + i_delay; } + if ( es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) ) + { + 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; + } + } - p_block->i_rate = p_input->i_rate; + p_block->i_rate = p_input->p->i_rate; /* TODO handle mute */ - if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES || p_input->i_rate == INPUT_RATE_DEFAULT ) ) + if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES || + p_input->p->i_rate == INPUT_RATE_DEFAULT ) ) { input_DecoderDecode( es->p_dec, p_block ); } @@ -743,6 +1111,8 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) static void EsOutDel( es_out_t *out, es_out_id_t *es ) { es_out_sys_t *p_sys = out->p_sys; + vlc_bool_t b_reselect = VLC_FALSE; + int i; /* We don't try to reselect */ if( es->p_dec ) @@ -756,10 +1126,12 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) es->p_pgrm->i_es--; if( es->p_pgrm->i_es == 0 ) { - msg_Warn( p_sys->p_input, "Program doesn't contain anymore ES, " - "TODO cleaning ?" ); + msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" ); } + if( p_sys->p_es_audio == es || p_sys->p_es_video == es || + p_sys->p_es_sub == es ) b_reselect = VLC_TRUE; + if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL; if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL; if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL; @@ -777,8 +1149,18 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) break; } - if( es->psz_description ) - free( es->psz_description ); + /* 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], VLC_FALSE ); + } + + if( es->psz_language ) + free( es->psz_language ); + if( es->psz_language_code ) + free( es->psz_language_code ); es_format_Clean( &es->fmt ); @@ -843,7 +1225,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) 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_AUTO || i == ES_OUT_MODE_PARTIAL ) { p_sys->i_mode = i; @@ -922,6 +1304,14 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) } } } + { + playlist_t * p_playlist = pl_Yield( p_sys->p_input ); + PL_LOCK; + p_playlist->gc_date = mdate(); + vlc_cond_signal( &p_playlist->object_wait ); + PL_UNLOCK; + pl_Release( p_playlist ); + } return VLC_SUCCESS; case ES_OUT_SET_PCR: @@ -967,6 +1357,18 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) } return VLC_SUCCESS; + case ES_OUT_GET_TS: + if( p_sys->p_pgrm ) + { + 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 + 11 ) * 9 / 100 ); + return VLC_SUCCESS; + } + return VLC_EGENERIC; + case ES_OUT_GET_GROUP: pi = (int*) va_arg( args, int* ); if( p_sys->p_pgrm ) @@ -1008,16 +1410,53 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) if( !es->p_dec ) return VLC_SUCCESS; +#if 1 + input_DecoderDelete( es->p_dec ); + es->p_dec = input_DecoderNew( p_sys->p_input, + &es->fmt, VLC_FALSE ); + +#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 ); memcpy( es->p_dec->fmt_in.p_extra, p_fmt->p_extra, p_fmt->i_extra ); +#endif } return VLC_SUCCESS; } + 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 ); + + if( !es || !es->p_dec ) + return VLC_EGENERIC; + + es->i_preroll_end = i_date; + input_DecoderPreroll( es->p_dec, i_date ); + + return VLC_SUCCESS; + } + 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 * ); + + EsOutProgramMeta( out, i_group, p_meta ); + return VLC_SUCCESS; + } + case ES_OUT_DEL_GROUP: + { + int i_group = (int)va_arg( args, int ); + + return EsOutProgramDel( out, i_group ); + } + default: msg_Err( p_sys->p_input, "unknown query in es_out_Control" ); return VLC_EGENERIC; @@ -1067,6 +1506,79 @@ static char *LanguageGetName( const char *psz_code ) } } +/* Get a 2 char code */ +static char *LanguageGetCode( const char *psz_lang ) +{ + const iso639_lang_t *pl; + + if( psz_lang == NULL || *psz_lang == '\0' ) + return strdup("??"); + + for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ ) + { + if( !strcasecmp( pl->psz_eng_name, psz_lang ) || + !strcasecmp( pl->psz_native_name, psz_lang ) || + !strcasecmp( pl->psz_iso639_1, psz_lang ) || + !strcasecmp( pl->psz_iso639_2T, psz_lang ) || + !strcasecmp( pl->psz_iso639_2B, psz_lang ) ) + break; + } + + if( pl->psz_iso639_1 != NULL ) + return strdup( pl->psz_iso639_1 ); + + return strdup("??"); +} + +static char **LanguageSplit( const char *psz_langs ) +{ + char *psz_dup; + char *psz_parser; + char **ppsz = NULL; + int i_psz = 0; + + if( psz_langs == NULL ) return NULL; + + psz_parser = psz_dup = strdup(psz_langs); + + while( psz_parser && *psz_parser ) + { + char *psz; + char *psz_code; + + psz = strchr(psz_parser, ',' ); + if( psz ) *psz++ = '\0'; + + psz_code = LanguageGetCode( psz_parser ); + if( strcmp( psz_code, "??" ) ) + { + TAB_APPEND( i_psz, ppsz, psz_code ); + } + + psz_parser = psz; + } + + if( i_psz ) + { + TAB_APPEND( i_psz, ppsz, NULL ); + } + + free( psz_dup ); + return ppsz; +} + +static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang ) +{ + int i; + + if( !ppsz_langs || !psz_lang ) return -1; + + for( i = 0; ppsz_langs[i]; i++ ) + if( !strcasecmp( ppsz_langs[i], psz_lang ) ) return i; + + return -1; +} + /**************************************************************************** * EsOutAddInfo: * - add meta info to the playlist item @@ -1077,6 +1589,7 @@ static void EsOutAddInfo( es_out_t *out, es_out_id_t *es ) input_thread_t *p_input = p_sys->p_input; es_format_t *fmt = &es->fmt; char *psz_cat; + lldiv_t div; /* Add stream info */ asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 ); @@ -1085,7 +1598,7 @@ static void EsOutAddInfo( es_out_t *out, es_out_id_t *es ) "%.4s", (char*)&fmt->i_codec ); input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"), - "%s", es->psz_description ); + "%s", es->psz_language ); /* Add information */ switch( fmt->i_cat ) @@ -1099,8 +1612,11 @@ static void EsOutAddInfo( es_out_t *out, es_out_id_t *es ) "%d", fmt->audio.i_channels ); if( fmt->audio.i_rate > 0 ) + { input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"), _("%d Hz"), fmt->audio.i_rate ); + var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate ); + } if( fmt->audio.i_bitspersample > 0 ) input_Control( p_input, INPUT_ADD_INFO, psz_cat, @@ -1108,8 +1624,11 @@ static void EsOutAddInfo( es_out_t *out, es_out_id_t *es ) fmt->audio.i_bitspersample ); if( fmt->i_bitrate > 0 ) + { input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"), _("%d kb/s"), fmt->i_bitrate / 1000 ); + var_SetInteger( p_input, "bit-rate", fmt->i_bitrate ); + } break; case VIDEO_ES: @@ -1127,7 +1646,17 @@ static void EsOutAddInfo( es_out_t *out, es_out_id_t *es ) _("Display resolution"), "%dx%d", fmt->video.i_visible_width, fmt->video.i_visible_height); - break; + if( fmt->video.i_frame_rate > 0 && + fmt->video.i_frame_rate_base > 0 ) + { + div = lldiv( (float)fmt->video.i_frame_rate / + fmt->video.i_frame_rate_base * 1000000, + 1000000 ); + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Frame rate"), I64Fd".%06u", + div.quot, (unsigned int )div.rem ); + } + break; case SPU_ES: input_Control( p_input, INPUT_ADD_INFO, psz_cat,