Commit a0429d00 authored by Laurent Aimar's avatar Laurent Aimar

Added closed captions decoding/extracting from ES data. The CC tracks

are dynamically added when detected.
(Decoder/Packetizer support not yet commited)
parent 2cdbf636
......@@ -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
*/
......
......@@ -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;
......
......@@ -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 );
......
......@@ -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 &&