[vlc-devel] [PATCH 2/2] codec/esout: enable both 608 and 708 captions

Francois Cartegnie fcvlcdev at free.fr
Tue Sep 26 22:59:51 CEST 2017


Extends the get_cc hack to make embedded captions work
also with CEA708.
CEA708 replaces EIA608 when it's possible and set in prefs.
(as long as CEA708 is not stable)

---
 include/vlc_codec.h              |  26 ++++---
 modules/codec/avcodec/video.c    |   6 +-
 modules/codec/cc.h               |  16 ++---
 modules/codec/libmpeg2.c         |   6 +-
 modules/demux/ty.c               |   8 ++-
 modules/packetizer/h264.c        |   6 +-
 modules/packetizer/hevc.c        |   6 +-
 modules/packetizer/hxxx_common.c |  17 +++--
 modules/packetizer/hxxx_common.h |   3 +-
 modules/packetizer/mpegvideo.c   |  13 ++--
 modules/packetizer/vc1.c         |  12 ++--
 src/input/decoder.c              | 118 ++++++++++++++++++-------------
 src/input/decoder.h              |   9 ++-
 src/input/es_out.c               | 145 +++++++++++++++++++++++++--------------
 src/libvlc-module.c              |   9 +++
 15 files changed, 244 insertions(+), 156 deletions(-)

diff --git a/include/vlc_codec.h b/include/vlc_codec.h
index c62ccd0..3499ede 100644
--- a/include/vlc_codec.h
+++ b/include/vlc_codec.h
@@ -45,6 +45,8 @@
 
 typedef struct decoder_owner_sys_t decoder_owner_sys_t;
 
+typedef struct decoder_cc_desc_t decoder_cc_desc_t;
+
 /*
  * BIG FAT WARNING : the code relies in the first 4 members of filter_t
  * and decoder_t to be the same, so if you have anything to add, do it
@@ -127,10 +129,10 @@ struct decoder_t
     /* Closed Caption (CEA 608/708) extraction.
      * If set, it *may* be called after pf_packetize returned data. It should
      * return CC for the pictures returned by the last pf_packetize call only,
-     * pb_present will be used to known which cc channel are present (but
+     * channel bitmaps will be used to known which cc channel are present (but
      * globaly, not necessary for the current packet. Video decoders should use
      * the decoder_QueueCc() function to pass closed captions. */
-    block_t *           ( * pf_get_cc )      ( decoder_t *, bool pb_present[4], int * );
+    block_t *           ( * pf_get_cc )      ( decoder_t *, decoder_cc_desc_t * );
 
     /* Meta data at codec level
      *  The decoder owner set it back to NULL once it has retreived what it needs.
@@ -178,7 +180,7 @@ struct decoder_t
     /* XXX use decoder_QueueAudio */
     int             (*pf_queue_audio)( decoder_t *, block_t * );
     /* XXX use decoder_QueueCC */
-    int             (*pf_queue_cc)( decoder_t *, block_t *, bool p_cc_present[4], int );
+    int             (*pf_queue_cc)( decoder_t *, block_t *, const decoder_cc_desc_t * );
     /* XXX use decoder_QueueSub */
     int             (*pf_queue_sub)( decoder_t *, subpicture_t *);
     void             *p_queue_ctx;
@@ -187,6 +189,15 @@ struct decoder_t
     decoder_owner_sys_t *p_owner;
 };
 
+/* struct for packetizer get_cc polling/decoder queue_cc
+ * until we have a proper metadata way */
+struct decoder_cc_desc_t
+{
+    uint8_t i_608_channels;  /* 608 channels bitmap */
+    uint64_t i_708_channels; /* 708 */
+    int i_reorder_depth;     /* reorder depth, -1 for no reorder, 0 for old P/B flag based */
+};
+
 /**
  * @}
  */
@@ -311,21 +322,18 @@ static inline int decoder_QueueVideo( decoder_t *dec, picture_t *p_pic )
  *
  * \param dec the decoder object
  * \param p_cc the closed-caption to queue
- * \param p_cc_present array-of-bool where each entry indicates whether the
- *                     given channel is present or not
- * \param i_depth the closed-caption to queue reorder depth, or simply 0
- *                     if using the old mpgv block flag tagging
+ * \param p_desc decoder_cc_desc_t description structure
  * \return 0 if queued, -1 on error
  */
 static inline int decoder_QueueCc( decoder_t *dec, block_t *p_cc,
-                                   bool p_cc_present[4], int i_depth )
+                                   const decoder_cc_desc_t *p_desc )
 {
     if( dec->pf_queue_cc == NULL )
     {
         block_Release( p_cc );
         return -1;
     }
-    return dec->pf_queue_cc( dec, p_cc, p_cc_present, i_depth );
+    return dec->pf_queue_cc( dec, p_cc, p_desc );
 }
 
 /**
diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c
index e243191..02af2ae 100644
--- a/modules/codec/avcodec/video.c
+++ b/modules/codec/avcodec/video.c
@@ -864,7 +864,11 @@ static void DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p
                     p_cc->i_dts = p_cc->i_pts = p_pic->date;
                 else
                     p_cc->i_pts = p_cc->i_dts;
-                decoder_QueueCc( p_dec, p_cc, p_sys->cc.pb_present, 4 );
+                decoder_cc_desc_t desc;
+                desc.i_608_channels = p_sys->cc.i_608channels;
+                desc.i_708_channels = p_sys->cc.i_708channels;
+                desc.i_reorder_depth = 4;
+                decoder_QueueCc( p_dec, p_cc, &desc );
             }
             cc_Flush( &p_sys->cc );
         }
diff --git a/modules/codec/cc.h b/modules/codec/cc.h
index 4562e53..fb80d17 100644
--- a/modules/codec/cc.h
+++ b/modules/codec/cc.h
@@ -42,8 +42,8 @@ enum cc_payload_type_e
 typedef struct
 {
     /* Which channel are present */
-    bool pb_present[4];
-    int64_t i_708_flags;
+    uint64_t i_708channels;
+    uint8_t  i_608channels;
 
     /* */
     bool b_reorder;
@@ -63,9 +63,8 @@ typedef struct
 
 static inline void cc_Init( cc_data_t *c )
 {
-    for( int i = 0; i < 4; i++ )
-        c->pb_present[i] = false;
-    c->i_708_flags = 0;
+    c->i_608channels = 0;
+    c->i_708channels = 0;
     c->i_data = 0;
     c->b_reorder = false;
     c->i_payload_type = CC_PAYLOAD_NONE;
@@ -85,10 +84,9 @@ static inline void cc_AppendData( cc_data_t *c, uint8_t cc_preamble, const uint8
 {
     uint8_t i_field = cc_preamble & 0x03;
     if( i_field == 0 || i_field == 1 )
-    {
-        c->pb_present[2*i_field+0] =
-        c->pb_present[2*i_field+1] = true;
-    }
+        c->i_608channels |= (3 << (2 * i_field));
+    else
+        c->i_708channels |= 1;
 
     c->p_data[c->i_data++] = cc_preamble;
     c->p_data[c->i_data++] = cc[0];
diff --git a/modules/codec/libmpeg2.c b/modules/codec/libmpeg2.c
index 2cbc96c..f58972a 100644
--- a/modules/codec/libmpeg2.c
+++ b/modules/codec/libmpeg2.c
@@ -717,7 +717,11 @@ static void SendCc( decoder_t *p_dec )
         p_cc->i_dts =
         p_cc->i_pts = p_sys->cc.b_reorder ? p_sys->i_cc_pts : p_sys->i_cc_dts;
         p_cc->i_flags = p_sys->i_cc_flags & BLOCK_FLAG_TYPE_MASK;
-        decoder_QueueCc( p_dec, p_cc, p_sys->cc.pb_present, p_sys->cc.b_reorder ? 0 : -1 );
+        decoder_cc_desc_t desc;
+        desc.i_608_channels = p_sys->cc.i_608channels;
+        desc.i_708_channels = p_sys->cc.i_708channels;
+        desc.i_reorder_depth = p_sys->cc.b_reorder ? 0 : -1;
+        decoder_QueueCc( p_dec, p_cc, &desc );
     }
     cc_Flush( &p_sys->cc );
     return;
diff --git a/modules/demux/ty.c b/modules/demux/ty.c
index 67f7aca..d96ea13 100644
--- a/modules/demux/ty.c
+++ b/modules/demux/ty.c
@@ -759,8 +759,12 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
     }
 
     /* Register the CC decoders when needed */
-    for( i = 0; i < 4; i++ )
+    uint64_t i_chans = p_sys->cc.i_608channels;
+    for( i = 0; i_chans > 0; i++, i_chans >>= 1 )
     {
+        if( (i_chans & 1) == 0 || p_sys->p_cc[i] )
+            continue;
+
         static const char *ppsz_description[4] = {
             N_("Closed captions 1"),
             N_("Closed captions 2"),
@@ -770,8 +774,6 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
 
         es_format_t fmt;
 
-        if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
-            continue;
 
         es_format_Init( &fmt, SPU_ES, VLC_CODEC_CEA608 );
         fmt.subs.cc.i_channel = i;
diff --git a/modules/packetizer/h264.c b/modules/packetizer/h264.c
index a681422..8cca41f 100644
--- a/modules/packetizer/h264.c
+++ b/modules/packetizer/h264.c
@@ -135,7 +135,7 @@ struct decoder_sys_t
 
 static block_t *Packetize( decoder_t *, block_t ** );
 static block_t *PacketizeAVC1( decoder_t *, block_t ** );
-static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int * );
+static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t * );
 static void PacketizeFlush( decoder_t * );
 
 static void PacketizeReset( void *p_private, bool b_broken );
@@ -486,9 +486,9 @@ static block_t *PacketizeAVC1( decoder_t *p_dec, block_t **pp_block )
 /*****************************************************************************
  * GetCc:
  *****************************************************************************/
-static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int *pi_reorder_depth )
+static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t *p_desc )
 {
-    return cc_storage_get_current( p_dec->p_sys->p_ccs, pb_present, pi_reorder_depth );
+    return cc_storage_get_current( p_dec->p_sys->p_ccs, p_desc );
 }
 
 /****************************************************************************
diff --git a/modules/packetizer/hevc.c b/modules/packetizer/hevc.c
index 9bdca10..40cc79e 100644
--- a/modules/packetizer/hevc.c
+++ b/modules/packetizer/hevc.c
@@ -71,7 +71,7 @@ static block_t *PacketizeParse(void *p_private, bool *pb_ts_used, block_t *);
 static block_t *ParseNALBlock(decoder_t *, bool *pb_ts_used, block_t *);
 static int PacketizeValidate(void *p_private, block_t *);
 static bool ParseSEICallback( const hxxx_sei_data_t *, void * );
-static block_t *GetCc( decoder_t *, bool pb_present[4], int * );
+static block_t *GetCc( decoder_t *, decoder_cc_desc_t * );
 
 struct decoder_sys_t
 {
@@ -304,9 +304,9 @@ static void PacketizeFlush( decoder_t *p_dec )
 /*****************************************************************************
  * GetCc:
  *****************************************************************************/
-static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int *pi_reorder_depth )
+static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t *p_desc )
 {
-    return cc_storage_get_current( p_dec->p_sys->p_ccs, pb_present, pi_reorder_depth );
+    return cc_storage_get_current( p_dec->p_sys->p_ccs, p_desc );
 }
 
 /****************************************************************************
diff --git a/modules/packetizer/hxxx_common.c b/modules/packetizer/hxxx_common.c
index 0eecf9a..45b33fd 100644
--- a/modules/packetizer/hxxx_common.c
+++ b/modules/packetizer/hxxx_common.c
@@ -21,10 +21,11 @@
 # include "config.h"
 #endif
 
-#include "hxxx_common.h"
-
+#include <vlc_common.h>
 #include <vlc_block.h>
 #include <vlc_codec.h>
+
+#include "hxxx_common.h"
 #include "../codec/cc.h"
 
 /****************************************************************************
@@ -80,16 +81,10 @@ void cc_storage_commit( cc_storage_t *p_ccs, block_t *p_pic )
     cc_Flush( &p_ccs->next );
 }
 
-block_t * cc_storage_get_current( cc_storage_t *p_ccs, bool pb_present[4],
-                                  int *pi_reorder_depth )
+block_t * cc_storage_get_current( cc_storage_t *p_ccs, decoder_cc_desc_t *p_desc )
 {
     block_t *p_block;
 
-    *pi_reorder_depth = p_ccs->current.b_reorder ? 4 : -1;
-
-    for( int i = 0; i < 4; i++ )
-        pb_present[i] = p_ccs->current.pb_present[i];
-
     if( !p_ccs->current.b_reorder && p_ccs->current.i_data <= 0 )
         return NULL;
 
@@ -100,6 +95,10 @@ block_t * cc_storage_get_current( cc_storage_t *p_ccs, bool pb_present[4],
         p_block->i_dts =
         p_block->i_pts = p_ccs->current.b_reorder ? p_ccs->i_pts : p_ccs->i_dts;
         p_block->i_flags = p_ccs->i_flags & BLOCK_FLAG_TYPE_MASK;
+
+        p_desc->i_608_channels = p_ccs->current.i_608channels;
+        p_desc->i_708_channels = p_ccs->current.i_708channels;
+        p_desc->i_reorder_depth = p_ccs->current.b_reorder ? 4 : -1;
     }
     cc_Flush( &p_ccs->current );
 
diff --git a/modules/packetizer/hxxx_common.h b/modules/packetizer/hxxx_common.h
index 6e7cd09..eff2f0b 100644
--- a/modules/packetizer/hxxx_common.h
+++ b/modules/packetizer/hxxx_common.h
@@ -33,8 +33,7 @@ void cc_storage_append( cc_storage_t *p_ccs, bool b_top_field_first,
                                       const uint8_t *p_buf, size_t i_buf );
 void cc_storage_commit( cc_storage_t *p_ccs, block_t *p_pic );
 
-block_t * cc_storage_get_current( cc_storage_t *p_ccs, bool pb_present[4],
-                                  int *pi_reorder_depth );
+block_t * cc_storage_get_current( cc_storage_t *p_ccs, decoder_cc_desc_t * );
 
 /* */
 
diff --git a/modules/packetizer/mpegvideo.c b/modules/packetizer/mpegvideo.c
index 71126d8..703b1d7 100644
--- a/modules/packetizer/mpegvideo.c
+++ b/modules/packetizer/mpegvideo.c
@@ -177,7 +177,7 @@ struct decoder_sys_t
 
 static block_t *Packetize( decoder_t *, block_t ** );
 static void PacketizeFlush( decoder_t * );
-static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int * );
+static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t * );
 
 static void PacketizeReset( void *p_private, bool b_broken );
 static block_t *PacketizeParse( void *p_private, bool *pb_ts_used, block_t * );
@@ -312,15 +312,10 @@ static void PacketizeFlush( decoder_t *p_dec )
 /*****************************************************************************
  * GetCc:
  *****************************************************************************/
-static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int *pi_reorder_depth )
+static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t *p_desc )
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
     block_t *p_cc;
-    int i;
-    *pi_reorder_depth = p_sys->cc.b_reorder ? 0 : -1;
-
-    for( i = 0; i < 4; i++ )
-        pb_present[i] = p_sys->cc.pb_present[i];
 
     if( !p_sys->cc.b_reorder && p_sys->cc.i_data <= 0 )
         return NULL;
@@ -332,6 +327,10 @@ static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int *pi_reorder_dep
         p_cc->i_dts = 
         p_cc->i_pts = p_sys->cc.b_reorder ? p_sys->i_cc_pts : p_sys->i_cc_dts;
         p_cc->i_flags = p_sys->i_cc_flags & BLOCK_FLAG_TYPE_MASK;
+
+        p_desc->i_608_channels = p_sys->cc.i_608channels;
+        p_desc->i_708_channels = p_sys->cc.i_708channels;
+        p_desc->i_reorder_depth = p_sys->cc.b_reorder ? 0 : -1;
     }
     cc_Flush( &p_sys->cc );
     return p_cc;
diff --git a/modules/packetizer/vc1.c b/modules/packetizer/vc1.c
index b075299..901e2c0 100644
--- a/modules/packetizer/vc1.c
+++ b/modules/packetizer/vc1.c
@@ -129,7 +129,7 @@ static block_t *PacketizeParse( void *p_private, bool *pb_ts_used, block_t * );
 static int PacketizeValidate( void *p_private, block_t * );
 
 static block_t *ParseIDU( decoder_t *p_dec, bool *pb_ts_used, block_t *p_frag );
-static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int * );
+static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t * );
 
 static const uint8_t p_vc1_startcode[3] = { 0x00, 0x00, 0x01 };
 /*****************************************************************************
@@ -762,14 +762,10 @@ static block_t *ParseIDU( decoder_t *p_dec, bool *pb_ts_used, block_t *p_frag )
 /*****************************************************************************
  * GetCc:
  *****************************************************************************/
-static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int *pi_reorder_depth )
+static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t *p_desc )
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
     block_t *p_cc;
-    *pi_reorder_depth = p_sys->cc.b_reorder ? 4 : -1;
-
-    for( int i = 0; i < 4; i++ )
-        pb_present[i] = p_sys->cc.pb_present[i];
 
     p_cc = block_Alloc( p_sys->cc.i_data);
     if( p_cc )
@@ -778,6 +774,10 @@ static block_t *GetCc( decoder_t *p_dec, bool pb_present[4], int *pi_reorder_dep
         p_cc->i_dts =
         p_cc->i_pts = p_sys->cc.b_reorder ? p_sys->i_cc_pts : p_sys->i_cc_dts;
         p_cc->i_flags = p_sys->i_cc_flags & BLOCK_FLAG_TYPE_MASK;
+
+        p_desc->i_608_channels = p_sys->cc.i_608channels;
+        p_desc->i_708_channels = p_sys->cc.i_708channels;
+        p_desc->i_reorder_depth = p_sys->cc.b_reorder ? 4 : -1;
     }
     cc_Flush( &p_sys->cc );
     return p_cc;
diff --git a/src/input/decoder.c b/src/input/decoder.c
index 54eec0f..f5aaefb 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -131,12 +131,12 @@ struct decoder_owner_sys_t
     bool b_idle;
 
     /* CC */
+#define MAX_CC_DECODERS 64 /* The es_out only creates one type of es */
     struct
     {
         bool b_supported;
-        bool pb_present[4];
-        uint8_t i_reorder_depth;
-        decoder_t *pp_decoder[4];
+        decoder_cc_desc_t desc;
+        decoder_t *pp_decoder[MAX_CC_DECODERS];
     } cc;
 
     /* Delay */
@@ -907,35 +907,39 @@ static void DecoderProcessSout( decoder_t *p_dec, block_t *p_block )
 #endif
 
 static void DecoderPlayCc( decoder_t *p_dec, block_t *p_cc,
-                           bool pb_present[4], int i_reorder_depth )
+                           const decoder_cc_desc_t *p_desc )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
-    bool b_processed = false;
-    int i_cc_decoder = 0;
 
     vlc_mutex_lock( &p_owner->lock );
-    for( int i = 0; i < 4; i++ )
-    {
-        p_owner->cc.pb_present[i] |= pb_present[i];
-        if( p_owner->cc.pp_decoder[i] )
-            i_cc_decoder++;
-    }
-    p_owner->cc.i_reorder_depth = i_reorder_depth;
 
-    for( int i = 0; i < 4; i++ )
+    p_owner->cc.desc = *p_desc;
+
+    /* Fanout data to all decoders. We do not know if es_out
+       selected 608 or 708. */
+    uint64_t i_bitmap = p_owner->cc.desc.i_608_channels |
+                        p_owner->cc.desc.i_708_channels;
+
+    for( int i=0; i_bitmap > 0; i_bitmap >>= 1, i++ )
     {
-        if( !p_owner->cc.pp_decoder[i] )
+        decoder_t *p_ccdec = p_owner->cc.pp_decoder[i];
+        if( !p_ccdec )
             continue;
 
-        block_FifoPut( p_owner->cc.pp_decoder[i]->p_owner->p_fifo,
-            (i_cc_decoder > 1) ? block_Duplicate(p_cc) : p_cc);
-
-        i_cc_decoder--;
-        b_processed = true;
+        if( i_bitmap > 1 )
+        {
+            block_FifoPut( p_ccdec->p_owner->p_fifo, block_Duplicate(p_cc) );
+        }
+        else
+        {
+            block_FifoPut( p_ccdec->p_owner->p_fifo, p_cc );
+            p_cc = NULL; /* was last dec */
+        }
     }
+
     vlc_mutex_unlock( &p_owner->lock );
 
-    if( !b_processed )
+    if( p_cc ) /* can have bitmap set but no created decs */
         block_Release( p_cc );
 }
 
@@ -943,7 +947,7 @@ static void PacketizerGetCc( decoder_t *p_dec, decoder_t *p_dec_cc )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
     block_t *p_cc;
-    bool pb_present[4];
+    decoder_cc_desc_t desc;
 
     /* Do not try retreiving CC if not wanted (sout) or cannot be retreived */
     if( !p_owner->cc.b_supported )
@@ -951,15 +955,14 @@ static void PacketizerGetCc( decoder_t *p_dec, decoder_t *p_dec_cc )
 
     assert( p_dec_cc->pf_get_cc != NULL );
 
-    int i_reorder_depth;
-    p_cc = p_dec_cc->pf_get_cc( p_dec_cc, pb_present, &i_reorder_depth );
+    p_cc = p_dec_cc->pf_get_cc( p_dec_cc, &desc );
     if( !p_cc )
         return;
-    DecoderPlayCc( p_dec, p_cc, pb_present, i_reorder_depth );
+    DecoderPlayCc( p_dec, p_cc, &desc );
 }
 
 static int DecoderQueueCc( decoder_t *p_videodec, block_t *p_cc,
-                           bool p_cc_present[4], int i_reorder_depth )
+                           const decoder_cc_desc_t *p_desc )
 {
     decoder_owner_sys_t *p_owner = p_videodec->p_owner;
 
@@ -967,7 +970,7 @@ static int DecoderQueueCc( decoder_t *p_videodec, block_t *p_cc,
     {
         if( p_owner->cc.b_supported &&
            ( !p_owner->p_packetizer || !p_owner->p_packetizer->pf_get_cc ) )
-            DecoderPlayCc( p_videodec, p_cc, p_cc_present, i_reorder_depth );
+            DecoderPlayCc( p_videodec, p_cc, p_desc );
         else
             block_Release( p_cc );
     }
@@ -1467,7 +1470,7 @@ static void DecoderProcessFlush( decoder_t *p_dec )
     /* flush CC sub decoders */
     if( p_owner->cc.b_supported )
     {
-        for( int i=0; i<4; i++ )
+        for( int i=0; i<MAX_CC_DECODERS; i++ )
         {
             decoder_t *p_subdec = p_owner->cc.pp_decoder[i];
             if( p_subdec && p_subdec->pf_flush )
@@ -1773,12 +1776,10 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     /* */
     p_owner->cc.b_supported = ( p_sout == NULL );
 
-    for( unsigned i = 0; i < 4; i++ )
-    {
-        p_owner->cc.pb_present[i] = false;
+    p_owner->cc.desc.i_608_channels = 0;
+    p_owner->cc.desc.i_708_channels = 0;
+    for( unsigned i = 0; i < MAX_CC_DECODERS; i++ )
         p_owner->cc.pp_decoder[i] = NULL;
-    }
-    p_owner->cc.i_reorder_depth = 0;
     p_owner->i_ts_delay = 0;
     return p_dec;
 }
@@ -1994,8 +1995,8 @@ void input_DecoderDelete( decoder_t *p_dec )
     /* */
     if( p_dec->p_owner->cc.b_supported )
     {
-        for( int i = 0; i < 4; i++ )
-            input_DecoderSetCcState( p_dec, false, i );
+        for( int i = 0; i < MAX_CC_DECODERS; i++ )
+            input_DecoderSetCcState( p_dec, VLC_CODEC_CEA708, i, false );
     }
 
     /* Delete decoder */
@@ -2121,23 +2122,46 @@ void input_DecoderFlush( decoder_t *p_dec )
     vlc_fifo_Unlock( p_owner->p_fifo );
 }
 
-void input_DecoderIsCcPresent( decoder_t *p_dec, bool pb_present[4] )
+void input_DecoderGetCcDesc( decoder_t *p_dec, decoder_cc_desc_t *p_desc )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
     vlc_mutex_lock( &p_owner->lock );
-    for( int i = 0; i < 4; i++ )
-        pb_present[i] =  p_owner->cc.pb_present[i];
+    *p_desc = p_owner->cc.desc;
     vlc_mutex_unlock( &p_owner->lock );
 }
 
-int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel )
+static bool input_DecoderHasCCChanFlag( decoder_t *p_dec,
+                                        vlc_fourcc_t codec, int i_channel )
+{
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
+    int i_max_channels;
+    uint64_t i_bitmap;
+    if( codec == VLC_CODEC_CEA608 )
+    {
+        i_max_channels = 4;
+        i_bitmap = p_owner->cc.desc.i_608_channels;
+    }
+    else if( codec == VLC_CODEC_CEA708 )
+    {
+        i_max_channels = 64;
+        i_bitmap = p_owner->cc.desc.i_708_channels;
+    }
+    else return false;
+
+    return ( i_channel >= 0 && i_channel < i_max_channels &&
+             ( i_bitmap & ((uint64_t)1 << i_channel) ) );
+}
+
+int input_DecoderSetCcState( decoder_t *p_dec, vlc_fourcc_t codec,
+                             int i_channel, bool b_decode )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
-    //msg_Warn( p_dec, "input_DecoderSetCcState: %d @%d", b_decode, i_channel );
+    //msg_Warn( p_dec, "input_DecoderSetCcState: %d @%x", b_decode, i_channel );
 
-    if( i_channel < 0 || i_channel >= 4 || !p_owner->cc.pb_present[i_channel] )
+    if( !input_DecoderHasCCChanFlag( p_dec, codec, i_channel ) )
         return VLC_EGENERIC;
 
     if( b_decode )
@@ -2145,9 +2169,9 @@ int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel )
         decoder_t *p_cc;
         es_format_t fmt;
 
-        es_format_Init( &fmt, SPU_ES, VLC_CODEC_CEA608 );
+        es_format_Init( &fmt, SPU_ES, codec );
         fmt.subs.cc.i_channel = i_channel;
-        fmt.subs.cc.i_reorder_depth = p_owner->cc.i_reorder_depth;
+        fmt.subs.cc.i_reorder_depth = p_owner->cc.desc.i_reorder_depth;
         p_cc = input_DecoderNew( p_owner->p_input, &fmt,
                               p_dec->p_owner->p_clock, p_owner->p_sout );
         if( !p_cc )
@@ -2185,18 +2209,18 @@ int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel )
     return VLC_SUCCESS;
 }
 
-int input_DecoderGetCcState( decoder_t *p_dec, bool *pb_decode, int i_channel )
+int input_DecoderGetCcState( decoder_t *p_dec, vlc_fourcc_t codec,
+                             int i_channel, bool *pb_decode )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
-    *pb_decode = false;
-    if( i_channel < 0 || i_channel >= 4 || !p_owner->cc.pb_present[i_channel] )
+    if( !input_DecoderHasCCChanFlag( p_dec, codec, i_channel ) )
         return VLC_EGENERIC;
 
     vlc_mutex_lock( &p_owner->lock );
     *pb_decode = p_owner->cc.pp_decoder[i_channel] != NULL;
     vlc_mutex_unlock( &p_owner->lock );
-    return VLC_EGENERIC;
+    return VLC_SUCCESS;
 }
 
 void input_DecoderChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date )
diff --git a/src/input/decoder.h b/src/input/decoder.h
index 226ecde..ccfe8ec 100644
--- a/src/input/decoder.h
+++ b/src/input/decoder.h
@@ -66,19 +66,18 @@ bool input_DecoderIsEmpty( decoder_t * );
 /**
  * This function activates the request closed caption channel.
  */
-int input_DecoderSetCcState( decoder_t *, bool b_decode, int i_channel );
+int input_DecoderSetCcState( decoder_t *, vlc_fourcc_t, int i_channel, bool b_decode );
 
 /**
  * This function returns an error if the requested channel does not exist and
  * set pb_decode to the channel status(active or not) otherwise.
  */
-int input_DecoderGetCcState( decoder_t *, bool *pb_decode, int i_channel );
+int input_DecoderGetCcState( decoder_t *, vlc_fourcc_t, int i_channel, bool *pb_decode );
 
 /**
- * This function set each pb_present entry to true if the corresponding channel
- * exists or false otherwise.
+ * This function get cc channels descriptions
  */
-void input_DecoderIsCcPresent( decoder_t *, bool pb_present[4] );
+void input_DecoderGetCcDesc( decoder_t *, decoder_cc_desc_t * );
 
 /**
  * This function force the display of the next picture and fills the stream
diff --git a/src/input/es_out.c b/src/input/es_out.c
index b19c500..a081e60 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -93,8 +93,12 @@ struct es_out_id_t
     decoder_t   *p_dec_record;
 
     /* Fields for Video with CC */
-    bool  pb_cc_present[4];
-    es_out_id_t  *pp_cc_es[4];
+    struct
+    {
+        vlc_fourcc_t type;
+        uint64_t     i_bitmap;    /* channels bitmap */
+        es_out_id_t  *pp_es[64]; /* a max of 64 chans for CEA708 */
+    } cc;
 
     /* Field for CC track from a master video */
     es_out_id_t *p_master;
@@ -209,6 +213,8 @@ static inline int EsOutGetClosedCaptionsChannel( const es_format_t *p_fmt )
     int i_channel;
     if( p_fmt->i_codec == VLC_CODEC_CEA608 && p_fmt->subs.cc.i_channel < 4 )
         i_channel = p_fmt->subs.cc.i_channel;
+    else if( p_fmt->i_codec == VLC_CODEC_CEA708 && p_fmt->subs.cc.i_channel < 64 )
+        i_channel = p_fmt->subs.cc.i_channel;
     else
         i_channel = -1;
     return i_channel;
@@ -1624,8 +1630,8 @@ static es_out_id_t *EsOutAddSlave( es_out_t *out, const es_format_t *fmt, es_out
     es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
     es->p_dec = NULL;
     es->p_dec_record = NULL;
-    for( i = 0; i < 4; i++ )
-        es->pb_cc_present[i] = false;
+    es->cc.type = 0;
+    es->cc.i_bitmap = 0;
     es->p_master = p_master;
 
     TAB_APPEND( p_sys->i_es, p_sys->es, es );
@@ -1660,8 +1666,8 @@ static bool EsIsSelected( es_out_id_t *es )
         if( es->p_master->p_dec )
         {
             int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
-            if( i_channel != -1 )
-                input_DecoderGetCcState( es->p_master->p_dec, &b_decode, i_channel );
+            input_DecoderGetCcState( es->p_master->p_dec, es->fmt.i_codec,
+                                     i_channel, &b_decode );
         }
         return b_decode;
     }
@@ -1727,7 +1733,9 @@ static void EsSelect( es_out_t *out, es_out_id_t *es )
 
         i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
 
-        if( i_channel == -1 || input_DecoderSetCcState( es->p_master->p_dec, true, i_channel ) )
+        if( i_channel == -1 ||
+            input_DecoderSetCcState( es->p_master->p_dec, es->fmt.i_codec,
+                                     i_channel, true ) )
             return;
     }
     else
@@ -1772,6 +1780,34 @@ static void EsSelect( es_out_t *out, es_out_id_t *es )
     input_SendEventTeletextSelect( p_input, EsFmtIsTeletext( &es->fmt ) ? es->i_id : -1 );
 }
 
+static void EsDeleteCCChannels( es_out_t *out, es_out_id_t *parent )
+{
+    es_out_sys_t   *p_sys = out->p_sys;
+    input_thread_t *p_input = p_sys->p_input;
+
+    if( parent->cc.type == 0 )
+        return;
+
+    const int i_spu_id = var_GetInteger( p_input, "spu-es");
+
+    uint64_t i_bitmap = parent->cc.i_bitmap;
+    for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1 )
+    {
+        if( (i_bitmap & 1) == 0 || !parent->cc.pp_es[i] )
+            continue;
+
+        if( i_spu_id == parent->cc.pp_es[i]->i_id )
+        {
+            /* Force unselection of the CC */
+            input_SendEventEsSelect( p_input, SPU_ES, -1 );
+        }
+        EsOutDel( out, parent->cc.pp_es[i] );
+    }
+
+    parent->cc.i_bitmap = 0;
+    parent->cc.type = 0;
+}
+
 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
 {
     es_out_sys_t   *p_sys = out->p_sys;
@@ -1789,27 +1825,13 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
         {
             int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
             if( i_channel != -1 )
-                input_DecoderSetCcState( es->p_master->p_dec, false, i_channel );
+                input_DecoderSetCcState( es->p_master->p_dec, es->fmt.i_codec,
+                                         i_channel, false );
         }
     }
     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 */
-                input_SendEventEsSelect( p_input, SPU_ES, -1 );
-            }
-            EsOutDel( out, es->pp_cc_es[i] );
-
-            es->pb_cc_present[i] = false;
-        }
+        EsDeleteCCChannels( out, es );
         EsDestroyDecoder( out, es );
     }
 
@@ -1965,6 +1987,46 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
     }
 }
 
+static void EsOutCreateCCChannels( es_out_t *out, vlc_fourcc_t codec, uint64_t i_bitmap,
+                                   const char *psz_descfmt, es_out_id_t *parent )
+{
+    es_out_sys_t   *p_sys = out->p_sys;
+    input_thread_t *p_input = p_sys->p_input;
+
+    /* Only one type of captions is allowed ! */
+    if( parent->cc.type && parent->cc.type != codec )
+        return;
+
+    uint64_t i_existingbitmap = parent->cc.i_bitmap;
+    for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1, i_existingbitmap >>= 1 )
+    {
+        es_format_t fmt;
+
+        if( (i_bitmap & 1) == 0 || (i_existingbitmap & 1) )
+            continue;
+
+        msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, parent->i_id );
+
+        es_format_Init( &fmt, SPU_ES, codec );
+        fmt.subs.cc.i_channel = i;
+        fmt.i_group = parent->fmt.i_group;
+        if( asprintf( &fmt.psz_description, psz_descfmt, 1 + i ) == -1 )
+            fmt.psz_description = NULL;
+
+        es_out_id_t **pp_es = &parent->cc.pp_es[i];
+        *pp_es = EsOutAddSlave( out, &fmt, parent );
+        es_format_Clean( &fmt );
+
+        /* */
+        parent->cc.i_bitmap |= (1ULL << i);
+        parent->cc.type = codec;
+
+        /* Enable if user specified on command line */
+        if (p_sys->sub.i_channel == i)
+            EsOutSelect(out, *pp_es, true);
+    }
+}
+
 /**
  * Send a block for the given es_out
  *
@@ -2060,33 +2122,14 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
     }
 
     /* Check CC status */
-    bool pb_cc[4];
-
-    input_DecoderIsCcPresent( es->p_dec, pb_cc );
-    for( int i = 0; i < 4; i++ )
-    {
-        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, VLC_CODEC_CEA608 );
-        fmt.subs.cc.i_channel = i;
-        fmt.i_group = es->fmt.i_group;
-        if( asprintf( &fmt.psz_description,
-                      _("Closed captions %u"), 1 + i ) == -1 )
-            fmt.psz_description = NULL;
-        es->pp_cc_es[i] = EsOutAddSlave( out, &fmt, es );
-        es_format_Clean( &fmt );
-
-        /* */
-        es->pb_cc_present[i] = true;
-
-        /* Enable if user specified on command line */
-        if (p_sys->sub.i_channel == i)
-            EsOutSelect(out, es->pp_cc_es[i], true);
-    }
+    decoder_cc_desc_t desc;
+
+    input_DecoderGetCcDesc( es->p_dec, &desc );
+    if( var_InheritInteger( p_input, "captions" ) == 708 )
+        EsOutCreateCCChannels( out, VLC_CODEC_CEA708, desc.i_708_channels,
+                               _("DTVCC Closed captions %u"), es );
+    EsOutCreateCCChannels( out, VLC_CODEC_CEA608, desc.i_608_channels,
+                           _("Closed captions %u"), es );
 
     vlc_mutex_unlock( &p_sys->lock );
 
diff --git a/src/libvlc-module.c b/src/libvlc-module.c
index ae6825f..24712fee 100644
--- a/src/libvlc-module.c
+++ b/src/libvlc-module.c
@@ -611,6 +611,11 @@ static const char *const ppsz_clock_descriptions[] =
 #define INPUT_SUBTRACK_ID_LONGTEXT N_( \
     "Stream ID of the subtitle track to use.")
 
+#define INPUT_CAPTIONS_TEXT N_(N_("Closed Captions decoder"))
+#define INPUT_CAPTIONS_LONGTEXT N_("Preferred closed captions decoder")
+static const int pi_captions[] = { 608, 708 };
+static const char *const ppsz_captions[] = { N_("EIA/CEA 608"), N_("CEA 708") };
+
 #define INPUT_PREFERREDRESOLUTION_TEXT N_("Preferred video resolution")
 #define INPUT_PREFERREDRESOLUTION_LONGTEXT N_( \
     "When several video formats are available, select one whose " \
@@ -1728,6 +1733,10 @@ vlc_module_begin ()
     add_integer( "sub-track-id", -1,
                  INPUT_SUBTRACK_ID_TEXT, INPUT_SUBTRACK_ID_LONGTEXT, true )
         change_safe ()
+    add_integer( "captions", 608,
+                 INPUT_CAPTIONS_TEXT, INPUT_CAPTIONS_LONGTEXT, true )
+        change_integer_list( pi_captions, ppsz_captions )
+        change_safe ()
     add_integer( "preferred-resolution", -1, INPUT_PREFERREDRESOLUTION_TEXT,
                  INPUT_PREFERREDRESOLUTION_LONGTEXT, false )
         change_safe ()
-- 
2.9.5



More information about the vlc-devel mailing list