]> git.sesse.net Git - vlc/commitdiff
Added closed captions decoding/extracting from ES data. The CC tracks
authorLaurent Aimar <fenrir@videolan.org>
Wed, 17 Oct 2007 19:51:25 +0000 (19:51 +0000)
committerLaurent Aimar <fenrir@videolan.org>
Wed, 17 Oct 2007 19:51:25 +0000 (19:51 +0000)
are dynamically added when detected.
(Decoder/Packetizer support not yet commited)

include/vlc_codec.h
modules/stream_out/transcode.c
src/input/decoder.c
src/input/es_out.c
src/input/input_internal.h

index e59b2475acb5cbde8053dbacc652be3794662d50..06aa6d669911ffbe3b38bafa6c8adc1031bde034 100644 (file)
@@ -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
      */
index 454cc21e23f5ecc586e5b24663a3d9194ab84e6e..5ef8d47e301e4530f3e6fd5e1b539b0421f460a0 100644 (file)
@@ -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;
index e72e07ae53e89d51199d3db431307492694e0f62..e534407d6acbb1a89f911fc0b9e7bc1d6add117f 100644 (file)
@@ -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 );
index ecf7d8963e2d882ab85598ec68a6608c22fd6d4a..66872ffb1ed41f07de1fe80c54719414c3bd78e8 100644 (file)
@@ -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 );
                 }
index d05056520b304a2c89f1fe3f59614f466cf3b725..06ee78cc0c22ae1bf936f1175d4225ec2f0ce73b 100644 (file)
@@ -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 * );