X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Fes_out.c;h=47887b523c270420342d5b13c83ce95337738a22;hb=22aae0460221c4c129e651cefed0e4d3196335dd;hp=9cbc987de7f05a38701bdf672d535a07fbe6b1a7;hpb=42195685323f11ea92176ac02c366444d908f154;p=vlc diff --git a/src/input/es_out.c b/src/input/es_out.c index 9cbc987de7..47887b523c 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -1,8 +1,8 @@ /***************************************************************************** * es_out.c: Es Out handler for input. ***************************************************************************** - * Copyright (C) 2003 VideoLAN - * $Id: es_out.c,v 1.10 2003/12/22 02:24:50 sam Exp $ + * Copyright (C) 2003-2004 VideoLAN + * $Id$ * * Authors: Laurent Aimar * @@ -30,25 +30,53 @@ #include #include -#include "codecs.h" +#include "input_internal.h" + +#include "vlc_playlist.h" +#include "iso_lang.h" /***************************************************************************** * Local prototypes *****************************************************************************/ +typedef struct +{ + /* Program ID */ + int i_id; + + /* Number of es for this pgrm */ + int i_es; + + vlc_bool_t b_selected; + + /* Clock for this program */ + input_clock_t clock; + +} es_out_pgrm_t; + struct es_out_id_t { - int i_channel; - es_descriptor_t *p_es; + /* ES ID */ + int i_id; + es_out_pgrm_t *p_pgrm; + + /* Channel in the track type */ + int i_channel; + es_format_t fmt; + char *psz_description; + decoder_t *p_dec; }; struct es_out_sys_t { input_thread_t *p_input; - vlc_bool_t b_pcr_set; + + /* all programs */ + int i_pgrm; + es_out_pgrm_t **pgrm; + es_out_pgrm_t *p_pgrm; /* Master program */ /* all es */ int i_id; - int i_es; es_out_id_t **es; @@ -69,13 +97,23 @@ struct es_out_sys_t es_out_id_t *p_es_audio; es_out_id_t *p_es_video; es_out_id_t *p_es_sub; + + /* delay */ + int64_t i_audio_delay; + int64_t i_spu_delay; }; static es_out_id_t *EsOutAdd ( es_out_t *, es_format_t * ); static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * ); static void EsOutDel ( es_out_t *, es_out_id_t * ); +static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ); static int EsOutControl( es_out_t *, int i_query, va_list ); +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 ); /***************************************************************************** * input_EsOutNew: @@ -93,13 +131,16 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) out->p_sys = p_sys; p_sys->p_input = p_input; - p_sys->b_pcr_set = VLC_FALSE; p_sys->b_active = VLC_FALSE; p_sys->i_mode = ES_OUT_MODE_AUTO; - p_sys->i_id = 1; + p_sys->i_pgrm = 0; + p_sys->pgrm = NULL; + p_sys->p_pgrm = NULL; + + p_sys->i_id = 0; p_sys->i_es = 0; p_sys->es = NULL; @@ -117,6 +158,9 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) p_sys->p_es_video = NULL; p_sys->p_es_sub = NULL; + p_sys->i_audio_delay= 0; + p_sys->i_spu_delay = 0; + return out; } @@ -130,296 +174,310 @@ void input_EsOutDelete( es_out_t *out ) for( i = 0; i < p_sys->i_es; i++ ) { + if( p_sys->es[i]->p_dec ) + { + input_DecoderDelete( p_sys->es[i]->p_dec ); + } + if( p_sys->es[i]->psz_description ) + free( p_sys->es[i]->psz_description ); + es_format_Clean( &p_sys->es[i]->fmt ); + free( p_sys->es[i] ); } if( p_sys->es ) - { free( p_sys->es ); + + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + free( p_sys->pgrm[i] ); } + if( p_sys->pgrm ) + free( p_sys->pgrm ); + free( p_sys ); free( out ); } +es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id ) +{ + int i; + if( i_id < 0 ) + { + /* Special HACK, -i_id is tha cat of the stream */ + return (es_out_id_t*)((uint8_t*)NULL-i_id); + } + + for( i = 0; i < out->p_sys->i_es; i++ ) + { + if( out->p_sys->es[i]->i_id == i_id ) + return out->p_sys->es[i]; + } + return NULL; +} + +void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio ) +{ + es_out_sys_t *p_sys = out->p_sys; + int i; + + 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 ); + } + } +} + +void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay ) +{ + es_out_sys_t *p_sys = out->p_sys; + + if( i_cat == AUDIO_ES ) + p_sys->i_audio_delay = i_delay; + else if( i_cat == SPU_ES ) + p_sys->i_spu_delay = i_delay; +} + +vlc_bool_t input_EsOutDecodersEmpty( es_out_t *out ) +{ + es_out_sys_t *p_sys = out->p_sys; + int i; + + for( i = 0; i < p_sys->i_es; i++ ) + { + es_out_id_t *es = p_sys->es[i]; + + if( es->p_dec && !input_DecoderEmpty( es->p_dec ) ) + return VLC_FALSE; + } + return VLC_TRUE; +} + /***************************************************************************** - * EsOutSelect: Select an ES given the current mode - * XXX: you need to take a the lock before (stream.stream_lock) + * *****************************************************************************/ -static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) +static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, + vlc_bool_t b_delete ) { es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; + vlc_value_t val, text; - int i_cat = es->p_es->i_cat; + char *psz_var; + + if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; - if( !p_sys->b_active || ( !b_force && es->p_es->fmt.i_priority < 0 ) ) + if( b_delete ) { + val.i_int = es->i_id; + var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL ); + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); return; } - if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force ) + /* Get the number of ES already added */ + var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL ); + if( val.i_int == 0 ) { - input_SelectES( p_input, es->p_es ); + vlc_value_t val2; + + /* First one, we need to add the "Disable" choice */ + val2.i_int = -1; text.psz_string = _("Disable"); + var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text ); + val.i_int++; } - else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) + + /* Take care of the ES description */ + if( es->psz_description && *es->psz_description ) { - int i_wanted = -1; + text.psz_string = strdup( es->psz_description ); + } + else + { + text.psz_string = malloc( strlen( _("Track %i") ) + 20 ); + sprintf( text.psz_string, _("Track %i"), val.i_int ); + } - if( i_cat == AUDIO_ES ) - { - if( p_sys->p_es_audio && - p_sys->p_es_audio->p_es->fmt.i_priority >= - es->p_es->fmt.i_priority ) - { - return; - } - i_wanted = p_sys->i_audio_last >= 0 ? - p_sys->i_audio_last : es->i_channel; - } - else if( i_cat == SPU_ES ) - { - if( p_sys->p_es_sub && - p_sys->p_es_sub->p_es->fmt.i_priority >= - es->p_es->fmt.i_priority ) - { - return; - } - i_wanted = p_sys->i_sub_last; - } - else if( i_cat == VIDEO_ES ) - { - i_wanted = es->i_channel; - } + val.i_int = es->i_id; + var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text ); - if( i_wanted == es->i_channel && es->p_es->p_dec == NULL ) + free( text.psz_string ); + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); +} + +/* EsOutProgramSelect: + * Select a program and update the object variable + */ +static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + int i; + + if( p_sys->p_pgrm == p_pgrm ) + return; /* Nothing to do */ + + 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 ); + + for( i = 0; i < p_sys->i_es; i++ ) { - input_SelectES( p_input, es->p_es ); + if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec && + p_sys->i_mode != ES_OUT_MODE_ALL ) + EsUnselect( out, p_sys->es[i], VLC_TRUE ); } } - /* FIXME TODO handle priority here */ - if( es->p_es->p_dec ) + msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id ); + + /* Mark it selected */ + p_pgrm->b_selected = VLC_TRUE; + + /* Switch master stream */ + if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master ) { - if( i_cat == AUDIO_ES ) - { - if( p_sys->i_mode == ES_OUT_MODE_AUTO && - p_sys->p_es_audio && p_sys->p_es_audio->p_es->p_dec ) - { - input_UnselectES( p_input, p_sys->p_es_audio->p_es ); - } - p_sys->p_es_audio = es; - } - else if( i_cat == SPU_ES ) - { - if( p_sys->i_mode == ES_OUT_MODE_AUTO && - p_sys->p_es_sub && p_sys->p_es_sub->p_es->p_dec ) - { - input_UnselectES( p_input, p_sys->p_es_sub->p_es ); - } - p_sys->p_es_sub = es; - } - else if( i_cat == VIDEO_ES ) - { - p_sys->p_es_video = es; - } + p_sys->p_pgrm->clock.b_master = VLC_FALSE; + } + p_pgrm->clock.b_master = VLC_TRUE; + p_sys->p_pgrm = p_pgrm; + + /* Update "program" */ + val.i_int = p_pgrm->i_id; + var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL ); + + /* Update "es-*" */ + var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + for( i = 0; i < p_sys->i_es; i++ ) + { + EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE ); + EsOutSelect( out, p_sys->es[i], VLC_FALSE ); } + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); } -/***************************************************************************** - * EsOutAdd: - *****************************************************************************/ -static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) +/* EsOutAddProgram: + * Add a program + */ +static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group ) { es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; - es_out_id_t *es = malloc( sizeof( es_out_id_t ) ); - pgrm_descriptor_t *p_prgm = NULL; - char psz_cat[sizeof( "Stream " ) + 10]; - input_info_category_t *p_cat; + vlc_value_t val; - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( fmt->i_group >= 0 ) - { - /* search program */ - p_prgm = input_FindProgram( p_input, fmt->i_group ); + es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) ); - if( p_prgm == NULL ) - { - /* create it */ - p_prgm = input_AddProgram( p_input, fmt->i_group, 0 ); + /* Init */ + 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 ); - /* Select the first by default */ - if( p_input->stream.p_selected_program == NULL ) - { - p_input->stream.p_selected_program = p_prgm; - } - } - } + /* Append it */ + TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); - es->p_es = input_AddES( p_input, - p_prgm, - out->p_sys->i_id, - fmt->i_cat, - fmt->psz_language, 0 ); - es->p_es->i_stream_id = out->p_sys->i_id; - es->p_es->i_fourcc = fmt->i_codec; + /* Update "program" variable */ + val.i_int = i_group; + var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL ); - switch( fmt->i_cat ) + if( i_group == var_GetInteger( p_input, "program" ) ) { - case AUDIO_ES: - { - WAVEFORMATEX *p_wf = - malloc( sizeof( WAVEFORMATEX ) + fmt->i_extra); - - p_wf->wFormatTag = WAVE_FORMAT_UNKNOWN; - p_wf->nChannels = fmt->audio.i_channels; - p_wf->nSamplesPerSec = fmt->audio.i_rate; - p_wf->nAvgBytesPerSec = fmt->i_bitrate / 8; - p_wf->nBlockAlign = fmt->audio.i_blockalign; - p_wf->wBitsPerSample = fmt->audio.i_bitspersample; - p_wf->cbSize = fmt->i_extra; - if( fmt->i_extra > 0 ) - { - memcpy( &p_wf[1], fmt->p_extra, fmt->i_extra ); - } - es->p_es->p_waveformatex = p_wf; - - es->i_channel = p_sys->i_audio; - break; - } - case VIDEO_ES: - { - BITMAPINFOHEADER *p_bih = malloc( sizeof( BITMAPINFOHEADER ) + - fmt->i_extra ); - p_bih->biSize = sizeof(BITMAPINFOHEADER) + fmt->i_extra; - p_bih->biWidth = fmt->video.i_width; - p_bih->biHeight = fmt->video.i_height; - p_bih->biPlanes = 1; - p_bih->biBitCount = 24; - p_bih->biCompression = fmt->i_codec; - p_bih->biSizeImage = fmt->video.i_width * - fmt->video.i_height; - p_bih->biXPelsPerMeter = 0; - p_bih->biYPelsPerMeter = 0; - p_bih->biClrUsed = 0; - p_bih->biClrImportant = 0; - - if( fmt->i_extra > 0 ) - { - memcpy( &p_bih[1], fmt->p_extra, fmt->i_extra ); - } - es->p_es->p_bitmapinfoheader = p_bih; + EsOutProgramSelect( out, p_pgrm ); + } + else + { + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); + } + return p_pgrm; +} - es->i_channel = p_sys->i_video; - break; - } - case SPU_ES: - { - subtitle_data_t *p_sub = malloc( sizeof( subtitle_data_t ) ); - memset( p_sub, 0, sizeof( subtitle_data_t ) ); - if( fmt->i_extra > 0 ) - { - p_sub->psz_header = malloc( fmt->i_extra + 1 ); - memcpy( p_sub->psz_header, fmt->p_extra , fmt->i_extra ); - /* just to be sure */ - ((uint8_t*)fmt->p_extra)[fmt->i_extra] = '\0'; - } - /* FIXME beuuuuuurk */ - es->p_es->p_demux_data = p_sub; +/* EsOutAdd: + * Add an es_out + */ +static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; - es->i_channel = p_sys->i_sub; - break; - } + es_out_id_t *es = malloc( sizeof( es_out_id_t ) ); + es_out_pgrm_t *p_pgrm = NULL; + int i; - default: - es->i_channel = 0; - break; + if( fmt->i_group < 0 ) + { + msg_Err( p_input, "invakud group number" ); + return NULL; } - sprintf( psz_cat, _("Stream %d"), out->p_sys->i_id - 1 ); - if( ( p_cat = input_InfoCategory( p_input, psz_cat ) ) ) + /* Search the program */ + for( i = 0; i < p_sys->i_pgrm; i++ ) { - /* Add information */ - switch( fmt->i_cat ) + if( fmt->i_group == p_sys->pgrm[i]->i_id ) { - case AUDIO_ES: - if( fmt->psz_description ) - { - input_AddInfo( p_cat, _("Description"), "%s", - fmt->psz_description ); - } - input_AddInfo( p_cat, _("Codec"), "%.4s", - (char*)&fmt->i_codec ); - input_AddInfo( p_cat, _("Type"), _("Audio") ); - if( fmt->audio.i_channels > 0 ) - { - input_AddInfo( p_cat, _("Channels"), "%d", - fmt->audio.i_channels ); - } - if( fmt->psz_language ) - { - input_AddInfo( p_cat, _("Language"), "%s", - fmt->psz_language ); - } - if( fmt->audio.i_rate > 0 ) - { - input_AddInfo( p_cat, _("Sample rate"), _("%d Hz"), - fmt->audio.i_rate ); - } - if( fmt->i_bitrate > 0 ) - { - input_AddInfo( p_cat, _("Bitrate"), _("%d bps"), - fmt->i_bitrate ); - } - if( fmt->audio.i_bitspersample ) - { - input_AddInfo( p_cat, _("Bits per sample"), "%d", - fmt->audio.i_bitspersample ); - } - break; - case VIDEO_ES: - if( fmt->psz_description ) - { - input_AddInfo( p_cat, _("Description"), "%s", - fmt->psz_description ); - } - input_AddInfo( p_cat, _("Type"), _("Video") ); - input_AddInfo( p_cat, _("Codec"), "%.4s", - (char*)&fmt->i_codec ); - if( fmt->video.i_width > 0 && fmt->video.i_height > 0 ) - { - input_AddInfo( p_cat, _("Resolution"), "%dx%d", - fmt->video.i_width, fmt->video.i_height ); - } - if( fmt->video.i_visible_width > 0 && - fmt->video.i_visible_height > 0 ) - { - input_AddInfo( p_cat, _("Display resolution"), "%dx%d", - fmt->video.i_visible_width, - fmt->video.i_visible_height); - } - break; - case SPU_ES: - input_AddInfo( p_cat, _("Type"), _("Subtitle") ); - input_AddInfo( p_cat, _("Codec"), "%.4s", - (char*)&fmt->i_codec ); - break; - default: - - break; + p_pgrm = p_sys->pgrm[i]; + break; } } + if( p_pgrm == NULL ) + { + /* Create a new one */ + p_pgrm = EsOutProgramAdd( out, fmt->i_group ); + } + /* Increase ref count for program */ + p_pgrm->i_es++; - /* Apply mode - * XXX change that when we do group too */ - if( 1 ) + /* 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 ); + switch( fmt->i_cat ) { - EsOutSelect( out, es, VLC_FALSE ); + case AUDIO_ES: + es->i_channel = p_sys->i_audio; + break; + + case VIDEO_ES: + es->i_channel = p_sys->i_video; + break; + + case SPU_ES: + es->i_channel = p_sys->i_sub; + break; + + default: + es->i_channel = 0; + break; } + es->psz_description = LanguageGetName( fmt->psz_language ); + es->p_dec = NULL; + + if( es->p_pgrm == p_sys->p_pgrm ) + EsOutESVarUpdate( out, es, VLC_FALSE ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); + /* Select it if needed */ + EsOutSelect( out, es, VLC_FALSE ); - es->p_es->fmt = *fmt; TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es ); p_sys->i_id++; /* always incremented */ @@ -436,44 +494,245 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) break; } + EsOutAddInfo( out, es ); + return es; } -/***************************************************************************** - * EsOutSend: - *****************************************************************************/ -static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) +static void EsSelect( es_out_t *out, es_out_id_t *es ) { - es_out_sys_t *p_sys = out->p_sys; + 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; - if( p_sys->b_pcr_set && p_sys->p_input->stream.p_selected_program ) + if( es->p_dec ) { - input_thread_t *p_input = p_sys->p_input; + msg_Warn( p_input, "ES 0x%x is already selected", es->i_id ); + return; + } - if( p_block->i_dts > 0 ) + 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_block->i_dts = - input_ClockGetTS( p_input, p_input->stream.p_selected_program, - p_block->i_dts * 9 / 100 ); + msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", + es->i_id ); + return; } - if( p_block->i_pts > 0 ) + } + else if( es->fmt.i_cat == AUDIO_ES ) + { + var_Get( p_input, "audio", &val ); + if( !var_GetBool( p_input, "audio" ) || + ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) ) { - p_block->i_pts = - input_ClockGetTS( p_input, p_input->stream.p_selected_program, - p_block->i_pts * 9 / 100 ); + msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", + es->i_id ); + return; } } - vlc_mutex_lock( &out->p_sys->p_input->stream.stream_lock ); - if( es->p_es->p_dec ) + es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE ); + if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm ) + return; + + if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; + + /* Mark it as selected */ + val.i_int = es->i_id; + var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); + + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); +} + +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; + + if( es->p_dec == NULL ) + { + msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id ); + return; + } + + input_DecoderDelete( es->p_dec ); + es->p_dec = NULL; + + if( !b_update ) + return; + + /* Update var */ + if( es->p_dec == NULL ) + return; + if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; + + /* Mark it as selected */ + val.i_int = -1; + var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); +} + +/** + * Select an ES given the current mode + * XXX: you need to take a the lock before (stream.stream_lock) + * + * \param out The es_out structure + * \param es es_out_id structure + * \param b_force ... + * \return nothing + */ +static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) +{ + es_out_sys_t *p_sys = out->p_sys; + + int i_cat = es->fmt.i_cat; + + if( !p_sys->b_active || + ( !b_force && es->fmt.i_priority < 0 ) ) + { + return; + } + + if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force ) + { + if( !es->p_dec ) + EsSelect( out, es ); + } + else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) + { + int i_wanted = -1; + + if( es->p_pgrm != p_sys->p_pgrm ) + return; + + if( i_cat == AUDIO_ES ) + { + if( p_sys->p_es_audio && + p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority ) + { + return; + } + i_wanted = p_sys->i_audio_last >= 0 ? + p_sys->i_audio_last : es->i_channel; + } + else if( i_cat == SPU_ES ) + { + if( p_sys->p_es_sub && + p_sys->p_es_sub->fmt.i_priority >= + es->fmt.i_priority ) + { + return; + } + i_wanted = p_sys->i_sub_last; + } + else if( i_cat == VIDEO_ES ) + { + i_wanted = es->i_channel; + } + + if( i_wanted == es->i_channel && es->p_dec == NULL ) + EsSelect( out, es ); + } + + /* FIXME TODO handle priority here */ + if( es->p_dec ) { - input_DecodeBlock( es->p_es->p_dec, p_block ); + if( i_cat == AUDIO_ES ) + { + if( p_sys->i_mode == ES_OUT_MODE_AUTO && + p_sys->p_es_audio && + p_sys->p_es_audio != es && + p_sys->p_es_audio->p_dec ) + { + EsUnselect( out, p_sys->p_es_audio, VLC_FALSE ); + } + p_sys->p_es_audio = es; + } + else if( i_cat == SPU_ES ) + { + if( p_sys->i_mode == ES_OUT_MODE_AUTO && + p_sys->p_es_sub && + p_sys->p_es_sub != es && + p_sys->p_es_sub->p_dec ) + { + EsUnselect( out, p_sys->p_es_sub, VLC_FALSE ); + } + p_sys->p_es_sub = es; + } + else if( i_cat == VIDEO_ES ) + { + p_sys->p_es_video = es; + } + } +} + +/** + * Send a block for the given es_out + * + * \param out the es_out to send from + * \param es the es_out_id + * \param p_block the data block to send + */ +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; + + 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; + + /* +11 -> avoid null value with non null dts/pts */ + if( p_block->i_dts > 0 ) + { + p_block->i_dts = + input_ClockGetTS( p_input, &p_pgrm->clock, + ( p_block->i_dts + 11 ) * 9 / 100 ) + i_delay; + } + if( p_block->i_pts > 0 ) + { + p_block->i_pts = + input_ClockGetTS( p_input, &p_pgrm->clock, + ( p_block->i_pts + 11 ) * 9 / 100 ) + i_delay; + } + + p_block->i_rate = p_input->i_rate; + + /* TODO handle mute */ + if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES || p_input->i_rate == INPUT_RATE_DEFAULT ) ) + { + input_DecoderDecode( es->p_dec, p_block ); } else { block_Release( p_block ); } - vlc_mutex_unlock( &out->p_sys->p_input->stream.stream_lock ); return VLC_SUCCESS; } @@ -485,9 +744,27 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) { es_out_sys_t *p_sys = out->p_sys; + /* We don't try to reselect */ + if( es->p_dec ) + EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm ); + + if( es->p_pgrm == p_sys->p_pgrm ) + EsOutESVarUpdate( out, es, VLC_TRUE ); + TAB_REMOVE( p_sys->i_es, p_sys->es, es ); - switch( es->p_es->i_cat ) + 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 ?" ); + } + + 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; + + switch( es->fmt.i_cat ) { case AUDIO_ES: p_sys->i_audio--; @@ -500,86 +777,61 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) break; } - /* We don't try to reselect */ - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); - if( es->p_es->p_dec ) - { - input_UnselectES( p_sys->p_input, es->p_es ); - } + if( es->psz_description ) + free( es->psz_description ); - if( es->p_es->p_waveformatex ) - { - free( es->p_es->p_waveformatex ); - es->p_es->p_waveformatex = NULL; - } - if( es->p_es->p_bitmapinfoheader ) - { - free( es->p_es->p_bitmapinfoheader ); - es->p_es->p_bitmapinfoheader = NULL; - } - input_DelES( p_sys->p_input, es->p_es ); - - 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; - - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + es_format_Clean( &es->fmt ); free( es ); } -/***************************************************************************** - * EsOutControl: - *****************************************************************************/ +/** + * Control query handler + * + * \param out the es_out to control + * \param i_query A es_out query as defined in include/ninput.h + * \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 ) { es_out_sys_t *p_sys = out->p_sys; vlc_bool_t b, *pb; int i, *pi; - int i_group; - int64_t i_pcr; es_out_id_t *es; switch( i_query ) { case ES_OUT_SET_ES_STATE: - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); es = (es_out_id_t*) va_arg( args, es_out_id_t * ); b = (vlc_bool_t) va_arg( args, vlc_bool_t ); - if( b && es->p_es->p_dec == NULL ) + if( b && es->p_dec == NULL ) { - input_SelectES( p_sys->p_input, es->p_es ); - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); - return es->p_es->p_dec ? VLC_SUCCESS : VLC_EGENERIC; + EsSelect( out, es ); + return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC; } - else if( !b && es->p_es->p_dec ) + else if( !b && es->p_dec ) { - input_UnselectES( p_sys->p_input, es->p_es ); - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm ); return VLC_SUCCESS; } - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); return VLC_SUCCESS; case ES_OUT_GET_ES_STATE: es = (es_out_id_t*) va_arg( args, es_out_id_t * ); pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); - *pb = es->p_es->p_dec ? VLC_TRUE : VLC_FALSE; + *pb = es->p_dec ? VLC_TRUE : VLC_FALSE; return VLC_SUCCESS; case ES_OUT_SET_ACTIVE: { b = (vlc_bool_t) va_arg( args, vlc_bool_t ); p_sys->b_active = b; - + /* Needed ? */ if( b ) - { - vlc_value_t val; - val.b_bool = VLC_TRUE; - var_Set( p_sys->p_input, "intf-change", val ); - } + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); return VLC_SUCCESS; } @@ -593,28 +845,21 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL || i == ES_OUT_MODE_AUTO ) { - vlc_value_t val; - p_sys->i_mode = i; /* Reapply policy mode */ - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); for( i = 0; i < p_sys->i_es; i++ ) { - if( p_sys->es[i]->p_es->p_dec ) + if( p_sys->es[i]->p_dec ) { - input_UnselectES( p_sys->p_input, p_sys->es[i]->p_es ); + 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], VLC_FALSE ); } - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_sys->p_input, "intf-change", val ); - return VLC_SUCCESS; } return VLC_EGENERIC; @@ -626,54 +871,271 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_ES: 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++ ) { - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); - if( p_sys->es[i]->p_es->p_dec ) - { - input_UnselectES( p_sys->p_input, p_sys->es[i]->p_es ); - } - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + if( p_sys->es[i]->p_dec ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + 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]->p_dec && + p_sys->es[i]->fmt.i_cat == AUDIO_ES ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + 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]->p_dec && + p_sys->es[i]->fmt.i_cat == VIDEO_ES ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + 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]->p_dec && + p_sys->es[i]->fmt.i_cat == SPU_ES ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } } else { - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); - EsOutSelect( out, es, VLC_TRUE ); - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + for( i = 0; i < p_sys->i_es; i++ ) + { + if( es == p_sys->es[i] ) + { + EsOutSelect( out, es, VLC_TRUE ); + break; + } + } } return VLC_SUCCESS; case ES_OUT_SET_PCR: + case ES_OUT_SET_GROUP_PCR: { - pgrm_descriptor_t *p_prgm = NULL; + es_out_pgrm_t *p_pgrm = NULL; + int i_group = 0; + int64_t i_pcr; - i_group = (int)va_arg( args, int ); - i_pcr = (int64_t)va_arg( args, int64_t ); + if( i_query == ES_OUT_SET_PCR ) + { + p_pgrm = p_sys->p_pgrm; + } + 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; + } + } + } + if( p_pgrm == NULL ) + p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ + i_pcr = (int64_t)va_arg( args, int64_t ); /* search program */ - if( ( p_prgm = input_FindProgram( p_sys->p_input, i_group ) ) ) + /* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */ + input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock, + (i_pcr + 11 ) * 9 / 100); + return VLC_SUCCESS; + } + + case ES_OUT_RESET_PCR: + for( i = 0; i < p_sys->i_pgrm; i++ ) { - input_ClockManageRef( p_sys->p_input, p_prgm, i_pcr ); + p_sys->pgrm[i]->clock.i_synchro_state = SYNCHRO_REINIT; + p_sys->pgrm[i]->clock.last_pts = 0; } - p_sys->b_pcr_set = VLC_TRUE; return VLC_SUCCESS; + + case ES_OUT_GET_GROUP: + pi = (int*) va_arg( args, int* ); + if( p_sys->p_pgrm ) + *pi = p_sys->p_pgrm->i_id; + else + *pi = -1; /* FIXME */ + return VLC_SUCCESS; + + case ES_OUT_SET_GROUP: + { + int j; + i = (int) va_arg( args, int ); + for( j = 0; j < p_sys->i_pgrm; j++ ) + { + es_out_pgrm_t *p_pgrm = p_sys->pgrm[j]; + if( p_pgrm->i_id == i ) + { + EsOutProgramSelect( out, p_pgrm ); + return VLC_SUCCESS; + } + } + return VLC_EGENERIC; } - case ES_OUT_RESET_PCR: - /* FIXME do it for all program */ - if( p_sys->p_input->stream.p_selected_program ) + case ES_OUT_SET_FMT: + { + /* 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 * ); + if( es == NULL ) return VLC_EGENERIC; + + if( p_fmt->i_extra ) { - p_sys->p_input->stream.p_selected_program->i_synchro_state = - SYNCHRO_REINIT; + es->fmt.i_extra = p_fmt->i_extra; + es->fmt.p_extra = realloc( 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 ) return VLC_SUCCESS; + + 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 ); } - p_sys->b_pcr_set = VLC_TRUE; + return VLC_SUCCESS; + } default: msg_Err( p_sys->p_input, "unknown query in es_out_Control" ); return VLC_EGENERIC; } } + +/**************************************************************************** + * LanguageGetName: try to expend iso639 into plain name + ****************************************************************************/ +static char *LanguageGetName( const char *psz_code ) +{ + const iso639_lang_t *pl; + + if( psz_code == NULL ) + { + return strdup( "" ); + } + + if( strlen( psz_code ) == 2 ) + { + pl = GetLang_1( psz_code ); + } + else if( strlen( psz_code ) == 3 ) + { + pl = GetLang_2B( psz_code ); + if( !strcmp( pl->psz_iso639_1, "??" ) ) + { + pl = GetLang_2T( psz_code ); + } + } + else + { + return strdup( psz_code ); + } + + if( !strcmp( pl->psz_iso639_1, "??" ) ) + { + return strdup( psz_code ); + } + else + { + if( *pl->psz_native_name ) + { + return strdup( pl->psz_native_name ); + } + return strdup( pl->psz_eng_name ); + } +} + +/**************************************************************************** + * EsOutAddInfo: + * - add meta info to the playlist item + ****************************************************************************/ +static void EsOutAddInfo( 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; + es_format_t *fmt = &es->fmt; + + char psz_cat[strlen(_("Stream %d")) + 12]; + + /* Add stream info */ + sprintf( psz_cat, _("Stream %d"), out->p_sys->i_id - 1 ); + + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"), + "%.4s", (char*)&fmt->i_codec ); + + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"), + "%s", es->psz_description ); + + /* Add information */ + switch( fmt->i_cat ) + { + case AUDIO_ES: + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Type"), _("Audio") ); + + if( fmt->audio.i_channels > 0 ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"), + "%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 ); + + if( fmt->audio.i_bitspersample > 0 ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Bits per sample"), "%d", + 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 ); + break; + + case VIDEO_ES: + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Type"), _("Video") ); + + if( fmt->video.i_width > 0 && fmt->video.i_height > 0 ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Resolution"), "%dx%d", + 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"), "%dx%d", + fmt->video.i_visible_width, + fmt->video.i_visible_height); + break; + + case SPU_ES: + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Type"), _("Subtitle") ); + break; + + default: + break; + } +}