From: Laurent Aimar Date: Wed, 17 Oct 2007 19:51:25 +0000 (+0000) Subject: Added closed captions decoding/extracting from ES data. The CC tracks X-Git-Tag: 0.9.0-test0~4989 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=a0429d0043a4cbf10f226879138bb8125eeb91f4;p=vlc Added closed captions decoding/extracting from ES data. The CC tracks are dynamically added when detected. (Decoder/Packetizer support not yet commited) --- diff --git a/include/vlc_codec.h b/include/vlc_codec.h index e59b2475ac..06aa6d6699 100644 --- a/include/vlc_codec.h +++ b/include/vlc_codec.h @@ -71,11 +71,20 @@ struct decoder_t /* Tell the decoder if it is allowed to drop frames */ vlc_bool_t b_pace_control; + /* */ picture_t * ( * pf_decode_video )( decoder_t *, block_t ** ); aout_buffer_t * ( * pf_decode_audio )( decoder_t *, block_t ** ); subpicture_t * ( * pf_decode_sub) ( decoder_t *, block_t ** ); block_t * ( * pf_packetize ) ( decoder_t *, block_t ** ); + /* Closed Caption (CEA 608/708) extraction. + * If set, it *may* be called after pf_decode_video/pf_packetize + * returned data. It should return CC for the pictures returned by the + * last pf_packetize/pf_decode_video call only, + * pb_present will be used to known which cc channel are present (but + * globaly, not necessary for the current packet */ + block_t * ( * pf_get_cc ) ( decoder_t *, vlc_bool_t pb_present[4] ); + /* * Buffers allocation */ diff --git a/modules/stream_out/transcode.c b/modules/stream_out/transcode.c index 454cc21e23..5ef8d47e30 100644 --- a/modules/stream_out/transcode.c +++ b/modules/stream_out/transcode.c @@ -1679,6 +1679,7 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) id->p_decoder->fmt_out.i_extra = 0; id->p_decoder->fmt_out.p_extra = 0; id->p_decoder->pf_decode_video = 0; + id->p_decoder->pf_get_cc = 0; id->p_decoder->pf_vout_buffer_new = video_new_buffer_decoder; id->p_decoder->pf_vout_buffer_del = video_del_buffer_decoder; id->p_decoder->pf_picture_link = video_link_picture_decoder; diff --git a/src/input/decoder.c b/src/input/decoder.c index e72e07ae53..e534407d6a 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c @@ -89,8 +89,26 @@ struct decoder_owner_sys_t /* fifo */ block_fifo_t *p_fifo; + + /* CC */ + vlc_bool_t b_cc_supported; + vlc_mutex_t lock_cc; + vlc_bool_t pb_cc_present[4]; + decoder_t *pp_cc[4]; }; +/* */ +static void DecoderUnsupportedCodec( decoder_t *p_dec, vlc_fourcc_t codec ) +{ + msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'.\n" + "VLC probably does not support this sound or video format.", + (char*)&codec ); + intf_UserFatal( p_dec, VLC_FALSE, _("No suitable decoder module " + "for format"), _("VLC probably does not support the \"%4.4s\" " + "audio or video format. Unfortunately there is no way for you " + "to fix this."), (char*)&codec ); +} + /* decoder_GetInputAttachment: */ input_attachment_t *decoder_GetInputAttachment( decoder_t *p_dec, @@ -158,13 +176,7 @@ decoder_t *input_DecoderNew( input_thread_t *p_input, if( !p_dec->p_module ) { - msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'.\n" - "VLC probably does not support this sound or video format.", - (char*)&p_dec->fmt_in.i_codec ); - intf_UserFatal( p_dec, VLC_FALSE, _("No suitable decoder module " - "for format"), _("VLC probably does not support the \"%4.4s\" " - "audio or video format. Unfortunately there is no way for you " - "to fix this."), (char*)&p_dec->fmt_in.i_codec ); + DecoderUnsupportedCodec( p_dec, fmt->i_codec ); DeleteDecoder( p_dec ); vlc_object_destroy( p_dec ); @@ -237,6 +249,14 @@ void input_DecoderDelete( decoder_t *p_dec ) module_Unneed( p_dec, p_dec->p_module ); } + /* */ + if( p_dec->p_owner->b_cc_supported ) + { + int i; + for( i = 0; i < 4; i++ ) + input_DecoderSetCcState( p_dec, VLC_FALSE, i ); + } + /* Delete decoder configuration */ DeleteDecoder( p_dec ); @@ -316,6 +336,89 @@ vlc_bool_t input_DecoderEmpty( decoder_t * p_dec ) return VLC_TRUE; } +void input_DecoderIsCcPresent( decoder_t *p_dec, vlc_bool_t pb_present[4] ) +{ + int i; + + vlc_mutex_lock( &p_dec->p_owner->lock_cc ); + for( i = 0; i < 4; i++ ) + pb_present[i] = p_dec->p_owner->pb_cc_present[i]; + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); +} +int input_DecoderSetCcState( decoder_t *p_dec, vlc_bool_t b_decode, int i_channel ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + //msg_Warn( p_dec, "input_DecoderSetCcState: %d @%d", b_decode, i_channel ); + + if( i_channel < 0 || i_channel >= 4 || !p_owner->pb_cc_present[i_channel] ) + return VLC_EGENERIC; + + if( b_decode ) + { + 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', ' '), + }; + decoder_t *p_cc; + es_format_t fmt; + + es_format_Init( &fmt, SPU_ES, fcc[i_channel] ); + p_cc = CreateDecoder( p_owner->p_input, &fmt, VLC_OBJECT_DECODER ); + if( !p_cc ) + { + msg_Err( p_dec, "could not create decoder" ); + intf_UserFatal( p_dec, VLC_FALSE, _("Streaming / Transcoding failed"), + _("VLC could not open the decoder module.") ); + return VLC_EGENERIC; + } + else if( !p_cc->p_module ) + { + DecoderUnsupportedCodec( p_dec, fcc[i_channel] ); + DeleteDecoder( p_cc ); + vlc_object_destroy( p_cc ); + return VLC_EGENERIC; + } + + vlc_mutex_lock( &p_owner->lock_cc ); + p_dec->p_owner->pp_cc[i_channel] = p_cc; + vlc_mutex_unlock( &p_owner->lock_cc ); + } + else + { + decoder_t *p_cc; + + vlc_mutex_lock( &p_owner->lock_cc ); + p_cc = p_dec->p_owner->pp_cc[i_channel]; + p_dec->p_owner->pp_cc[i_channel] = NULL; + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + + if( p_cc ) + { + vlc_object_kill( p_cc ); + module_Unneed( p_cc, p_cc->p_module ); + DeleteDecoder( p_cc ); + vlc_object_destroy( p_cc ); + } + } + return VLC_SUCCESS; +} +int input_DecoderGetCcState( decoder_t *p_dec, vlc_bool_t *pb_decode, int i_channel ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + *pb_decode = VLC_FALSE; + if( i_channel < 0 || i_channel >= 4 || !p_owner->pb_cc_present[i_channel] ) + return VLC_EGENERIC; + + vlc_mutex_lock( &p_owner->lock_cc ); + *pb_decode = p_dec->p_owner->pp_cc[i_channel] != NULL; + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + return VLC_EGENERIC; +} + /** * Create a decoder object * @@ -328,6 +431,8 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, es_format_t *fmt, int i_object_type ) { decoder_t *p_dec; + decoder_owner_sys_t *p_owner; + int i; p_dec = vlc_object_create( p_input, i_object_type ); if( p_dec == NULL ) @@ -339,6 +444,7 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, p_dec->pf_decode_audio = 0; p_dec->pf_decode_video = 0; p_dec->pf_decode_sub = 0; + p_dec->pf_get_cc = 0; p_dec->pf_packetize = 0; /* Initialize the decoder fifo */ @@ -349,7 +455,7 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, es_format_Copy( &p_dec->fmt_out, &null_es_format ); /* Allocate our private structure for the decoder */ - p_dec->p_owner = malloc( sizeof( decoder_owner_sys_t ) ); + p_dec->p_owner = p_owner = malloc( sizeof( decoder_owner_sys_t ) ); if( p_dec->p_owner == NULL ) { msg_Err( p_dec, "out of memory" ); @@ -424,7 +530,6 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, /* Copy ourself the input replay gain */ if( fmt->i_cat == AUDIO_ES ) { - int i; for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) { if( !p_dec->fmt_out.audio_replay_gain.pb_peak[i] ) @@ -439,6 +544,22 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, } } } + /* */ + p_owner->b_cc_supported = VLC_FALSE; + if( i_object_type == VLC_OBJECT_DECODER ) + { + if( p_owner->p_packetizer && p_owner->p_packetizer->pf_get_cc ) + p_owner->b_cc_supported = VLC_TRUE; + if( p_dec->pf_get_cc ) + p_owner->b_cc_supported = VLC_TRUE; + } + + vlc_mutex_init( p_dec, &p_owner->lock_cc ); + for( i = 0; i < 4; i++ ) + { + p_owner->pb_cc_present[i] = VLC_FALSE; + p_owner->pp_cc[i] = NULL; + } return p_dec; } @@ -519,6 +640,44 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block ) p_aout_buf, i_rate ); } } +static void DecoderGetCc( decoder_t *p_dec, decoder_t *p_dec_cc ) +{ + block_t *p_cc; + vlc_bool_t pb_present[4]; + int i; + int i_cc_decoder; + + assert( p_dec_cc->pf_get_cc != NULL ); + + /* Do not try retreiving CC if not wanted (sout) or cannot be retreived */ + if( !p_dec->p_owner->b_cc_supported ) + return; + + p_cc = p_dec_cc->pf_get_cc( p_dec_cc, pb_present ); + if( !p_cc ) + return; + + vlc_mutex_lock( &p_dec->p_owner->lock_cc ); + for( i = 0, i_cc_decoder = 0; i < 4; i++ ) + { + p_dec->p_owner->pb_cc_present[i] |= pb_present[i]; + if( p_dec->p_owner->pp_cc[i] ) + i_cc_decoder++; + } + + for( i = 0; i < 4; i++ ) + { + if( !p_dec->p_owner->pp_cc[i] ) + continue; + + if( i_cc_decoder > 1 ) + DecoderDecode( p_dec->p_owner->pp_cc[i], block_Duplicate( p_cc ) ); + else + DecoderDecode( p_dec->p_owner->pp_cc[i], p_cc ); + i_cc_decoder--; + } + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); +} static void VoutDisplayedPicture( vout_thread_t *p_vout, picture_t *p_pic ) { vlc_mutex_lock( &p_vout->picture_lock ); @@ -584,6 +743,9 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) p_dec->p_owner->i_preroll_end = -1; } + if( ( !p_dec->p_owner->p_packetizer || !p_dec->p_owner->p_packetizer->pf_get_cc ) && p_dec->pf_get_cc ) + DecoderGetCc( p_dec, p_dec ); + vout_DatePicture( p_dec->p_owner->p_vout, p_pic, p_pic->date ); vout_DisplayPicture( p_dec->p_owner->p_vout, p_pic ); @@ -731,6 +893,8 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) es_format_Clean( &p_dec->fmt_in ); es_format_Copy( &p_dec->fmt_in, &p_packetizer->fmt_out ); } + if( p_packetizer->pf_get_cc ) + DecoderGetCc( p_dec, p_packetizer ); while( p_packetized_block ) { @@ -863,6 +1027,8 @@ static void DeleteDecoder( decoder_t * p_dec ) vlc_object_destroy( p_dec->p_owner->p_packetizer ); } + vlc_mutex_destroy( &p_dec->p_owner->lock_cc ); + vlc_object_detach( p_dec ); free( p_dec->p_owner ); diff --git a/src/input/es_out.c b/src/input/es_out.c index ecf7d8963e..66872ffb1e 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -78,7 +78,15 @@ struct es_out_id_t es_format_t fmt; char *psz_language; char *psz_language_code; + decoder_t *p_dec; + + /* Fields for Video with CC */ + vlc_bool_t pb_cc_present[4]; + es_out_id_t *pp_cc_es[4]; + + /* Field for CC track from a master video */ + es_out_id_t *p_master; }; struct es_out_sys_t @@ -130,6 +138,7 @@ static int EsOutControl( es_out_t *, int i_query, va_list ); static void EsOutAddInfo( es_out_t *, es_out_id_t *es ); +static vlc_bool_t EsIsSelected( es_out_t *out, 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 ); @@ -380,8 +389,8 @@ vlc_bool_t input_EsOutDecodersEmpty( es_out_t *out ) /***************************************************************************** * *****************************************************************************/ -static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, - vlc_bool_t b_delete ) +static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language, + vlc_bool_t b_delete ) { es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; @@ -389,18 +398,18 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, const char *psz_var; - if( es->fmt.i_cat == AUDIO_ES ) + if( fmt->i_cat == AUDIO_ES ) psz_var = "audio-es"; - else if( es->fmt.i_cat == VIDEO_ES ) + else if( fmt->i_cat == VIDEO_ES ) psz_var = "video-es"; - else if( es->fmt.i_cat == SPU_ES ) + else if( fmt->i_cat == SPU_ES ) psz_var = "spu-es"; else return; if( b_delete ) { - val.i_int = es->i_id; + val.i_int = i_id; var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL ); var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); return; @@ -419,26 +428,26 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, } /* Take care of the ES description */ - if( es->fmt.psz_description && *es->fmt.psz_description ) + if( fmt->psz_description && *fmt->psz_description ) { - if( es->psz_language && *es->psz_language ) + if( psz_language && *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 ); + text.psz_string = malloc( strlen( fmt->psz_description) + + strlen( psz_language ) + 10 ); + sprintf( text.psz_string, "%s - [%s]", fmt->psz_description, + psz_language ); } - else text.psz_string = strdup( es->fmt.psz_description ); + else text.psz_string = strdup( fmt->psz_description ); } else { - if( es->psz_language && *es->psz_language ) + if( psz_language && *psz_language ) { char *temp; text.psz_string = malloc( strlen( _("Track %i") )+ - strlen( es->psz_language ) + 30 ); + strlen( psz_language ) + 30 ); asprintf( &temp, _("Track %i"), val.i_int ); - sprintf( text.psz_string, "%s - [%s]", temp, es->psz_language ); + sprintf( text.psz_string, "%s - [%s]", temp, psz_language ); free( temp ); } else @@ -448,7 +457,7 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, } } - val.i_int = es->i_id; + val.i_int = i_id; var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text ); free( text.psz_string ); @@ -456,6 +465,12 @@ static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); } +static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, + vlc_bool_t b_delete ) +{ + EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete ); +} + /* EsOutProgramSelect: * Select a program and update the object variable */ @@ -476,7 +491,7 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) for( i = 0; i < p_sys->i_es; i++ ) { - if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec && + if( p_sys->es[i]->p_pgrm == old && EsIsSelected( out, p_sys->es[i] ) && p_sys->i_mode != ES_OUT_MODE_ALL ) EsUnselect( out, p_sys->es[i], VLC_TRUE ); } @@ -932,6 +947,9 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) 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; + for( i = 0; i < 4; i++ ) + es->pb_cc_present[i] = VLC_FALSE; + es->p_master = VLC_FALSE; if( es->p_pgrm == p_sys->p_pgrm ) EsOutESVarUpdate( out, es, VLC_FALSE ); @@ -960,6 +978,21 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) return es; } +static vlc_bool_t EsIsSelected( es_out_t *out, es_out_id_t *es ) +{ + if( es->p_master ) + { + vlc_bool_t b_decode = VLC_FALSE; + if( es->p_master->p_dec ) + input_DecoderGetCcState( es->p_master->p_dec, &b_decode, + es->fmt.i_codec == VLC_FOURCC('c','c','1',' ') ? 0 : 1 ); + return b_decode; + } + else + { + return es->p_dec != NULL; + } +} static void EsSelect( es_out_t *out, es_out_id_t *es ) { es_out_sys_t *p_sys = out->p_sys; @@ -967,49 +1000,61 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) vlc_value_t val; const char *psz_var; - if( es->p_dec ) + if( EsIsSelected( out, es ) ) { msg_Warn( p_input, "ES 0x%x is already selected", es->i_id ); return; } - if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES ) + if( es->p_master ) { - if( !var_GetBool( p_input, "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 ); + if( !es->p_master->p_dec ) + return; + + if( input_DecoderSetCcState( es->p_master->p_dec, VLC_TRUE, + es->fmt.i_codec == VLC_FOURCC('c','c','1',' ') ? 0 : 1 ) ) return; - } } - else if( es->fmt.i_cat == AUDIO_ES ) + else { - var_Get( p_input, "audio", &val ); - if( !var_GetBool( p_input, "audio" ) || - ( p_input->p->p_sout && !var_GetBool( p_input, "sout-audio" ) ) ) + if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES ) { - msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", - es->i_id ); - return; + if( !var_GetBool( p_input, "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 ); + 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" ) ) ) + else if( es->fmt.i_cat == AUDIO_ES ) { - msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x", - es->i_id ); - return; + var_Get( p_input, "audio", &val ); + if( !var_GetBool( p_input, "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; + 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; + } if( es->fmt.i_cat == VIDEO_ES ) psz_var = "video-es"; @@ -1024,7 +1069,6 @@ static void EsSelect( es_out_t *out, es_out_id_t *es ) 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 ); } @@ -1035,14 +1079,41 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update ) vlc_value_t val; const char *psz_var; - if( es->p_dec == NULL ) + if( !EsIsSelected( out, es ) ) { msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id ); return; } - input_DecoderDelete( es->p_dec ); - es->p_dec = NULL; + if( es->p_master ) + { + if( es->p_master->p_dec ) + input_DecoderSetCcState( es->p_master->p_dec, VLC_FALSE, es->fmt.i_codec == VLC_FOURCC('c','c','1',' ') ? 0 : 1 ); + } + else + { + const int i_spu_id = var_GetInteger( p_input, "spu-es"); + int i; + for( i = 0; i < 4; i++ ) + { + if( !es->pb_cc_present[i] || !es->pp_cc_es[i] ) + continue; + + if( i_spu_id == es->pp_cc_es[i]->i_id ) + { + /* Force unselection of the CC */ + val.i_int = -1; + var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL ); + if( !b_update ) + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); + } + EsOutDel( out, es->pp_cc_es[i] ); + + es->pb_cc_present[i] = VLC_FALSE; + } + input_DecoderDelete( es->p_dec ); + es->p_dec = NULL; + } if( !b_update ) return; @@ -1059,7 +1130,7 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update ) else return; - /* Mark it as selected */ + /* Mark it as unselected */ val.i_int = -1; var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); @@ -1089,7 +1160,7 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force ) { - if( !es->p_dec ) + if( !EsIsSelected( out, es ) ) EsSelect( out, es ); } else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL ) @@ -1101,7 +1172,7 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) { if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force ) { - if( !es->p_dec ) + if( !EsIsSelected( out, es ) ) EsSelect( out, es ); break; } @@ -1202,19 +1273,19 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) i_wanted = es->i_channel; } - if( i_wanted == es->i_channel && es->p_dec == NULL ) + if( i_wanted == es->i_channel && !EsIsSelected( out, es ) ) EsSelect( out, es ); } /* FIXME TODO handle priority here */ - if( es->p_dec ) + if( EsIsSelected( out, es ) ) { 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 ) + EsIsSelected( out, p_sys->p_es_audio ) ) { EsUnselect( out, p_sys->p_es_audio, VLC_FALSE ); } @@ -1225,7 +1296,7 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) 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 ) + EsIsSelected( out, p_sys->p_es_sub ) ) { EsUnselect( out, p_sys->p_es_sub, VLC_FALSE ); } @@ -1323,7 +1394,46 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) ( p_input->p->i_rate >= INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE && p_input->p->i_rate <= INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE ) ) ) { + vlc_bool_t pb_cc[4]; + vlc_bool_t b_cc_new = VLC_FALSE; + int i; input_DecoderDecode( es->p_dec, p_block ); + + /* Check CC status */ + input_DecoderIsCcPresent( es->p_dec, pb_cc ); + for( i = 0; i < 4; i++ ) + { + static const vlc_fourcc_t fcc[4] = { + VLC_FOURCC('c', 'c', '1', ' '), + VLC_FOURCC('c', 'c', '2', ' '), + VLC_FOURCC('c', 'c', '3', ' '), + VLC_FOURCC('c', 'c', '4', ' '), + }; + static const char *ppsz_description[4] = { + N_("Closed captions 1"), + N_("Closed captions 2"), + N_("Closed captions 3"), + N_("Closed captions 4"), + }; + es_format_t fmt; + + if( es->pb_cc_present[i] || !pb_cc[i] ) + continue; + msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id ); + + es_format_Init( &fmt, SPU_ES, fcc[i] ); + fmt.i_group = es->fmt.i_group; + fmt.psz_description = strdup( _(ppsz_description[i] ) ); + es->pp_cc_es[i] = EsOutAdd( out, &fmt ); + es->pp_cc_es[i]->p_master = es; + es_format_Clean( &fmt ); + + /* */ + es->pb_cc_present[i] = VLC_TRUE; + b_cc_new = VLC_TRUE; + } + if( b_cc_new ) + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); } else { @@ -1424,12 +1534,12 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_ES_STATE: 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_dec == NULL ) + if( b && !EsIsSelected( out, es ) ) { EsSelect( out, es ); - return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC; + return EsIsSelected( out, es ) ? VLC_SUCCESS : VLC_EGENERIC; } - else if( !b && es->p_dec ) + else if( !b && EsIsSelected( out, es ) ) { EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm ); return VLC_SUCCESS; @@ -1440,7 +1550,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) 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_dec ? VLC_TRUE : VLC_FALSE; + *pb = EsIsSelected( out, es ); return VLC_SUCCESS; case ES_OUT_SET_ACTIVE: @@ -1468,7 +1578,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) /* Reapply policy mode */ for( i = 0; i < p_sys->i_es; i++ ) { - if( p_sys->es[i]->p_dec ) + if( EsIsSelected( out, p_sys->es[i] ) ) { EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); @@ -1494,7 +1604,7 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) { for( i = 0; i < p_sys->i_es; i++ ) { - if( p_sys->es[i]->p_dec ) + if( EsIsSelected( out, p_sys->es[i] ) ) EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } @@ -1503,8 +1613,8 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) { 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 ) + if( p_sys->es[i]->fmt.i_cat == AUDIO_ES && + EsIsSelected( out, p_sys->es[i] ) ) EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } @@ -1513,8 +1623,8 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) { 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 ) + if( p_sys->es[i]->fmt.i_cat == VIDEO_ES && + EsIsSelected( out, p_sys->es[i] ) ) EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } @@ -1523,8 +1633,8 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) { 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 ) + if( p_sys->es[i]->fmt.i_cat == SPU_ES && + EsIsSelected( out, p_sys->es[i] ) ) EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } diff --git a/src/input/input_internal.h b/src/input/input_internal.h index d05056520b..06ee78cc0c 100644 --- a/src/input/input_internal.h +++ b/src/input/input_internal.h @@ -259,6 +259,9 @@ void stream_AccessUpdate( stream_t *s ); /* decoder.c */ void input_DecoderDiscontinuity( decoder_t * p_dec, vlc_bool_t b_flush ); vlc_bool_t input_DecoderEmpty( decoder_t * p_dec ); +int input_DecoderSetCcState( decoder_t *, vlc_bool_t b_decode, int i_channel ); +int input_DecoderGetCcState( decoder_t *, vlc_bool_t *pb_decode, int i_channel ); +void input_DecoderIsCcPresent( decoder_t *, vlc_bool_t pb_present[4] ); /* es_out.c */ es_out_t *input_EsOutNew( input_thread_t * );