[vlc-commits] [Git][videolan/vlc][master] 9 commits: test: input_decoder: check that a CC can be selected via command line

Steve Lhomme (@robUx4) gitlab at videolan.org
Tue Oct 17 05:36:00 UTC 2023



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
a6ea06ea by Thomas Guillem at 2023-10-17T05:15:17+00:00
test: input_decoder: check that a CC can be selected via command line

To assure the next commit won't break it.

- - - - -
dd750a2b by Thomas Guillem at 2023-10-17T05:15:17+00:00
decoder: es_out: rework CC tracks creation

Make it more generic so that any kind of decoders can create any kind of
SubEses.

Add struct vlc_input_decoder_subdec_desc to internal headers, a struct
containing a list of es_format_t that will be used by the es_out to
create an ES per format.

- - - - -
ece69ea3 by Thomas Guillem at 2023-10-17T05:15:17+00:00
decoder: fill subs.cc.i_reorder_depth in subdec_desc

It is also set when creating a CC decoder from
vlc_input_decoder_SetCcState(), but this function will be reworked in a
next commit.

- - - - -
a5410cbd by Thomas Guillem at 2023-10-17T05:15:17+00:00
decoder: remove duplicate clock init

The clock is already set from the vlc_input_decoder_cfg

- - - - -
cdd20820 by Thomas Guillem at 2023-10-17T05:15:17+00:00
decoder: remove useless flush on CC creation fail

This is nothing to flush since the dec module is not loaded.

- - - - -
b711cd1b by Thomas Guillem at 2023-10-17T05:15:17+00:00
decoder: return early when destroying the ccdec

No functional changes, this will avoid a major diff in a upcoming commit.

- - - - -
57ef25d0 by Thomas Guillem at 2023-10-17T05:15:17+00:00
es_out: return early when creating a CC dec

No functional changes, pave the way for the next commit.

- - - - -
4b91ca7e by Thomas Guillem at 2023-10-17T05:15:17+00:00
decoder: es_out rework CC ES usage

Remove all CC related code in es_out.c. CC ESes are now treated as
general Sub ESes with one major difference: it reference a master ES and create
a decoder via the new function `vlc_input_decoder_CreateSubDec()`.

Therefore, only the creation of the Sub Es differs from the main ES.

The code deleting all sub decoders from vlc_input_decoder_Delete() has
been removed since the es_out is already taking care of removing all
SubESes related to one main ES. To enforce it, the main decoder will now
assert that all sub decoders are removed on deletion.

All vlc_input_decoder_ functions are now called, regardless of the ES
type (main or sub), so this fix the following issues:
 - CC decoders are now drained
 - CC decoders use their own clocks
 - CC decoders get the correct delay/rate/pause_state

vlc_input_decoder_StartWait(), vlc_input_decoder_StopWait() and
vlc_input_decoder_Wait() need a special check since sub decoders pacing
depends on their master decoder.

- - - - -
a6a3860b by Thomas Guillem at 2023-10-17T05:15:17+00:00
decoder: rename some CC variables

Seperate CC related variables with subdecs related ones.

- - - - -


4 changed files:

- src/input/decoder.c
- src/input/decoder.h
- src/input/es_out.c
- test/src/input/decoder/input_decoder_scenarios.c


Changes:

=====================================
src/input/decoder.c
=====================================
@@ -44,6 +44,7 @@
 #include <vlc_decoder.h>
 #include <vlc_picture_pool.h>
 #include <vlc_tracer.h>
+#include <vlc_list.h>
 
 #include "audio_output/aout_internal.h"
 #include "stream_output/stream_output.h"
@@ -96,7 +97,7 @@
  * content with `decoder_QueueCc`.
  *
  * In the `input/decoder.c` code, the access to the sub-decoders in the
- * cc.pp_decoders table is protected through the `cc.lock` mutex.
+ * subdecs.list table is protected through the `subdecs.lock` mutex.
  * Taking this lock ensures that the sub-decoder won't get
  * asynchronously removed while using it, and any mutex from the
  * sub-decoder can then be taken under this lock.
@@ -212,20 +213,30 @@ struct vlc_input_decoder_t
     bool b_idle;
     bool aborting;
 
+    /* Sub decs */
+    struct
+    {
+        vlc_mutex_t lock;
+        struct vlc_list list;
+    } subdecs;
+
     /* CC */
-#define MAX_CC_DECODERS 64 /* The es_out only creates one type of es */
     struct
     {
+        /* All members guarded by subdecs.lock */
         vlc_fourcc_t selected_codec;
-        vlc_mutex_t lock;
         bool b_supported;
         decoder_cc_desc_t desc;
-        vlc_input_decoder_t *pp_decoder[MAX_CC_DECODERS];
+        bool desc_changed;
         bool b_sout_created;
         sout_packetizer_input_t *p_sout_input;
         char *sout_es_id;
     } cc;
 
+    vlc_input_decoder_t *master_dec;
+
+    struct vlc_list node;
+
     /* Mouse event */
     vlc_mutex_t     mouse_lock;
     vlc_mouse_event mouse_event;
@@ -1192,6 +1203,13 @@ static void GetCcChannels(vlc_input_decoder_t *owner, size_t *max_channels,
     }
 }
 
+static bool SubDecoderIsCc(vlc_input_decoder_t *subdec)
+{
+    return subdec->dec.fmt_in->i_cat == SPU_ES &&
+            (subdec->dec.fmt_in->i_codec == VLC_CODEC_CEA608 ||
+             subdec->dec.fmt_in->i_codec == VLC_CODEC_CEA708);
+}
+
 /* */
 static void DecoderPlayCcLocked( vlc_input_decoder_t *p_owner, vlc_frame_t *p_cc,
                                  const decoder_cc_desc_t *p_desc )
@@ -1203,34 +1221,39 @@ static void DecoderPlayCcLocked( vlc_input_decoder_t *p_owner, vlc_frame_t *p_cc
         return;
     }
 
-    vlc_mutex_lock(&p_owner->cc.lock);
-    p_owner->cc.desc = *p_desc;
-
-    uint64_t i_bitmap;
-    GetCcChannels(p_owner, NULL, &i_bitmap);
+    vlc_mutex_lock(&p_owner->subdecs.lock);
+    if (p_owner->cc.desc.i_608_channels != p_desc->i_608_channels ||
+        p_owner->cc.desc.i_708_channels != p_desc->i_708_channels ||
+        p_owner->cc.desc.i_reorder_depth != p_desc->i_reorder_depth)
+    {
+        p_owner->cc.desc = *p_desc;
+        p_owner->cc.desc_changed = true;
+    }
 
-    for( int i=0; i_bitmap > 0; i_bitmap >>= 1, i++ )
+    bool first_cc = true;
+    vlc_input_decoder_t *it;
+    vlc_list_foreach(it, &p_owner->subdecs.list, node)
     {
-        vlc_input_decoder_t *p_ccowner = p_owner->cc.pp_decoder[i];
-        if( !p_ccowner )
+        if (!SubDecoderIsCc(it))
             continue;
 
-        if( i_bitmap > 1 )
+        if (first_cc)
         {
-            block_t *dup = block_Duplicate(p_cc);
-            if (dup == NULL)
-                break;
-            block_FifoPut(p_ccowner->p_fifo, dup);
+            block_FifoPut(it->p_fifo, p_cc);
+            first_cc = false;
         }
         else
         {
-            block_FifoPut( p_ccowner->p_fifo, p_cc );
-            p_cc = NULL; /* was last dec */
+            block_t *dup = block_Duplicate(p_cc);
+            if (dup == NULL)
+                break;
+            block_FifoPut(it->p_fifo, dup);
         }
     }
-    vlc_mutex_unlock(&p_owner->cc.lock);
 
-    if( p_cc ) /* can have bitmap set but no created decs */
+    vlc_mutex_unlock(&p_owner->subdecs.lock);
+
+    if (first_cc) /* can have bitmap set but no created decs */
         block_Release( p_cc );
 }
 
@@ -1720,21 +1743,6 @@ static void DecoderThread_Flush( vlc_input_decoder_t *p_owner )
 
     if ( p_dec->pf_flush != NULL )
         p_dec->pf_flush( p_dec );
-
-    /* flush CC sub decoders */
-    vlc_mutex_lock(&p_owner->cc.lock);
-    if( p_owner->cc.b_supported )
-    {
-        for( int i=0; i<MAX_CC_DECODERS; i++ )
-        {
-            vlc_input_decoder_t *p_ccowner = p_owner->cc.pp_decoder[i];
-            if(p_ccowner == NULL)
-                continue;
-
-            vlc_input_decoder_Flush(p_ccowner);
-        }
-    }
-    vlc_mutex_unlock(&p_owner->cc.lock);
 }
 
 /**
@@ -2031,18 +2039,18 @@ CreateDecoder( vlc_object_t *p_parent, const struct vlc_input_decoder_cfg *cfg )
     }
 
     /* */
-    vlc_mutex_init(&p_owner->cc.lock);
+    vlc_mutex_init(&p_owner->subdecs.lock);
     p_owner->cc.selected_codec = cfg->cc_decoder == 708 ?
                                  VLC_CODEC_CEA708 : VLC_CODEC_CEA608;
     p_owner->cc.b_supported = ( cfg->sout == NULL );
 
     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;
+    vlc_list_init(&p_owner->subdecs.list);
     p_owner->cc.p_sout_input = NULL;
     p_owner->cc.sout_es_id = NULL;
     p_owner->cc.b_sout_created = false;
+    p_owner->master_dec = NULL;
     return p_owner;
 }
 
@@ -2142,20 +2150,6 @@ static void DeleteDecoder( vlc_input_decoder_t *p_owner, enum es_format_category
     decoder_Destroy( &p_owner->dec );
 }
 
-static void DeleteCcDecoder(vlc_input_decoder_t *owner, size_t index)
-{
-    assert(vlc_mutex_held(&owner->cc.lock));
-
-    vlc_input_decoder_t *cc = owner->cc.pp_decoder[index];
-    owner->cc.pp_decoder[index] = NULL;
-
-    if (cc != NULL)
-    {
-        vlc_input_decoder_Flush(cc);
-        vlc_input_decoder_Delete(cc);
-    }
-}
-
 /* */
 static void DecoderUnsupportedCodec( decoder_t *p_dec, const es_format_t *fmt, bool b_decoding )
 {
@@ -2259,8 +2253,28 @@ vlc_input_decoder_Create( vlc_object_t *p_parent, const es_format_t *fmt, const
     return decoder_New( p_parent, &cfg );
 }
 
+static void RemoveCcDecoder(vlc_input_decoder_t *owner,
+                            vlc_input_decoder_t *subdec)
+{
+    vlc_mutex_lock(&owner->subdecs.lock);
+
+    vlc_input_decoder_t *it;
+    vlc_list_foreach(it, &owner->subdecs.list, node)
+        if (it == subdec)
+        {
+            vlc_list_remove(&it->node);
+            vlc_mutex_unlock(&owner->subdecs.lock);
+            return;
+        }
+
+    vlc_assert_unreachable();
+}
+
 void vlc_input_decoder_Delete( vlc_input_decoder_t *p_owner )
 {
+    if (p_owner->master_dec != NULL)
+        RemoveCcDecoder(p_owner->master_dec, p_owner);
+
     vlc_fifo_Lock( p_owner->p_fifo );
     p_owner->aborting = true;
     p_owner->b_waiting = false;
@@ -2272,21 +2286,77 @@ void vlc_input_decoder_Delete( vlc_input_decoder_t *p_owner )
 
     if( !vlc_input_decoder_IsSynchronous( p_owner ) )
         vlc_join( p_owner->thread, NULL );
-    /* */
-    if( p_owner->cc.b_supported )
-    {
-        size_t max_channels;
-        GetCcChannels(p_owner, &max_channels, NULL);
-        vlc_mutex_lock(&p_owner->cc.lock);
-        for (size_t i = 0; i < max_channels; i++)
-            DeleteCcDecoder(p_owner, i);
-        vlc_mutex_unlock(&p_owner->cc.lock);
-    }
+
+#ifndef NDEBUG
+    vlc_mutex_lock(&p_owner->subdecs.lock);
+    assert(vlc_list_is_empty(&p_owner->subdecs.list));
+    vlc_mutex_unlock(&p_owner->subdecs.lock);
+#endif
 
     /* Delete decoder */
     DeleteDecoder(p_owner, p_owner->dec_fmt_in.i_cat);
 }
 
+void vlc_subdec_desc_Clean(struct vlc_subdec_desc *desc)
+{
+    for (size_t i = 0; i < desc->fmt_count; ++i)
+        es_format_Clean(&desc->fmt_array[i]);
+    free(desc->fmt_array);
+}
+
+static void GetCCDescLocked(vlc_input_decoder_t *owner,
+                            struct vlc_subdec_desc *desc)
+{
+    vlc_fifo_Assert(owner->p_fifo);
+
+    if (!owner->cc.desc_changed)
+    {
+        desc->fmt_array = NULL;
+        desc->fmt_count = 0;
+        return;
+    }
+    owner->cc.desc_changed = false;
+
+    size_t max_channels;
+    uint64_t bitmap;
+    GetCcChannels(owner, &max_channels, &bitmap);
+
+    int count = vlc_popcount(bitmap);
+    if (count == 0)
+    {
+        desc->fmt_array = NULL;
+        desc->fmt_count = 0;
+        return;
+    }
+
+    desc->fmt_array = vlc_alloc(count, sizeof(es_format_t));
+    if (unlikely(desc->fmt_array == NULL))
+    {
+        desc->fmt_count = 0;
+        return;
+    }
+    desc->fmt_count = count;
+
+    static const char CEA608_fmtdesc[] = N_("Closed captions %u");
+    static const char CEA708_fmtdesc[] = N_("DTVCC Closed captions %u");
+    const char *fmtdesc = owner->cc.selected_codec == VLC_CODEC_CEA608 ?
+                          CEA608_fmtdesc : CEA708_fmtdesc;
+
+    size_t array_idx = 0;
+    for (int i = 0; bitmap > 0; bitmap >>= 1, i++)
+    {
+        if ((bitmap & 1) == 0)
+            continue;
+        es_format_t *fmt = &desc->fmt_array[array_idx++];
+        es_format_Init(fmt, SPU_ES, owner->cc.selected_codec);
+        fmt->i_id = i + 1;
+        fmt->subs.cc.i_channel = i;
+        fmt->subs.cc.i_reorder_depth = owner->cc.desc.i_reorder_depth;
+        if (asprintf(&fmt->psz_description, fmtdesc, fmt->i_id) == -1)
+            fmt->psz_description = NULL;
+    }
+}
+
 static void GetStatusLocked(vlc_input_decoder_t *p_owner,
                             struct vlc_input_decoder_status *status)
 {
@@ -2313,7 +2383,7 @@ static void GetStatusLocked(vlc_input_decoder_t *p_owner,
                 vlc_meta_Merge( status->format.meta, p_owner->p_description );
         }
     }
-    status->cc.desc = p_owner->cc.desc;
+    GetCCDescLocked(p_owner, &status->subdec_desc);
 }
 
 void vlc_input_decoder_DecodeWithStatus(vlc_input_decoder_t *p_owner, vlc_frame_t *frame,
@@ -2469,90 +2539,39 @@ void vlc_input_decoder_Flush( vlc_input_decoder_t *p_owner )
     }
 }
 
-static bool vlc_input_decoder_HasCCChanFlag( vlc_input_decoder_t *p_owner,
-                                             int i_channel )
+vlc_input_decoder_t *
+vlc_input_decoder_CreateSubDec(vlc_input_decoder_t *p_owner,
+                               const struct vlc_input_decoder_cfg *cfg)
 {
-    size_t i_max_channels;
-    uint64_t i_bitmap;
-    GetCcChannels(p_owner, &i_max_channels, &i_bitmap);
+    decoder_t *p_dec = &p_owner->dec;
 
-    return ( i_channel >= 0 && (size_t) i_channel < i_max_channels &&
-             ( i_bitmap & ((uint64_t)1 << i_channel) ) );
-}
+    vlc_mutex_lock(&p_owner->subdecs.lock);
 
-int vlc_input_decoder_SetCcState( vlc_input_decoder_t *p_owner,
-                                  int i_channel, bool b_decode )
-{
-    decoder_t *p_dec = &p_owner->dec;
+    vlc_input_decoder_t *p_ccowner;
 
-    vlc_mutex_lock(&p_owner->cc.lock);
-    if( !vlc_input_decoder_HasCCChanFlag( p_owner, i_channel ) )
+    p_ccowner = vlc_input_decoder_New( VLC_OBJECT(p_dec), cfg );
+    if( !p_ccowner )
     {
-        vlc_mutex_unlock(&p_owner->cc.lock);
-        return VLC_EGENERIC;
+        msg_Err( p_dec, "could not create decoder" );
+        vlc_dialog_display_error( p_dec,
+            _("Streaming / Transcoding failed"), "%s",
+            _("VLC could not open the decoder module.") );
+        vlc_mutex_unlock(&p_owner->subdecs.lock);
+        return NULL;
     }
-
-    if( b_decode )
+    else if( !p_ccowner->dec.p_module )
     {
-        vlc_input_decoder_t *p_ccowner;
-        es_format_t fmt;
-
-        es_format_Init( &fmt, SPU_ES, p_owner->cc.selected_codec );
-        fmt.subs.cc.i_channel = i_channel;
-        fmt.subs.cc.i_reorder_depth = p_owner->cc.desc.i_reorder_depth;
-
-        const struct vlc_input_decoder_cfg cfg = {
-            .fmt = &fmt,
-            .str_id = p_owner->psz_id,
-            .clock = p_owner->p_clock,
-            .resource = p_owner->p_resource,
-            .sout = p_owner->p_sout,
-            .input_type = INPUT_TYPE_NONE,
-            .cbs = NULL, .cbs_data = NULL,
-        };
-
-        p_ccowner = vlc_input_decoder_New( VLC_OBJECT(p_dec), &cfg );
-        if( !p_ccowner )
-        {
-            msg_Err( p_dec, "could not create decoder" );
-            vlc_dialog_display_error( p_dec,
-                _("Streaming / Transcoding failed"), "%s",
-                _("VLC could not open the decoder module.") );
-            vlc_mutex_unlock(&p_owner->cc.lock);
-            return VLC_EGENERIC;
-        }
-        else if( !p_ccowner->dec.p_module )
-        {
-            DecoderUnsupportedCodec( p_dec, &fmt, true );
-            vlc_input_decoder_Flush(p_ccowner);
-            vlc_input_decoder_Delete(p_ccowner);
-            vlc_mutex_unlock(&p_owner->cc.lock);
-            return VLC_EGENERIC;
-        }
-        p_ccowner->p_clock = p_owner->p_clock;
-
-        p_owner->cc.pp_decoder[i_channel] = p_ccowner;
+        DecoderUnsupportedCodec( p_dec, cfg->fmt, true );
+        vlc_input_decoder_Delete(p_ccowner);
+        vlc_mutex_unlock(&p_owner->subdecs.lock);
+        return NULL;
     }
-    else
-        DeleteCcDecoder(p_owner, i_channel);
-
-    vlc_mutex_unlock(&p_owner->cc.lock);
-    return VLC_SUCCESS;
-}
 
-int vlc_input_decoder_GetCcState( vlc_input_decoder_t *p_owner,
-                                  int i_channel, bool *pb_decode )
-{
-    vlc_mutex_lock(&p_owner->cc.lock);
-    if( !vlc_input_decoder_HasCCChanFlag( p_owner, i_channel ) )
-    {
-        vlc_mutex_unlock(&p_owner->cc.lock);
-        return VLC_EGENERIC;
-    }
+    vlc_list_append(&p_ccowner->node, &p_owner->subdecs.list);
+    p_ccowner->master_dec = p_owner;
 
-    *pb_decode = p_owner->cc.pp_decoder[i_channel] != NULL;
-    vlc_mutex_unlock(&p_owner->cc.lock);
-    return VLC_SUCCESS;
+    vlc_mutex_unlock(&p_owner->subdecs.lock);
+    return p_ccowner;
 }
 
 void vlc_input_decoder_ChangePause( vlc_input_decoder_t *p_owner,
@@ -2585,7 +2604,8 @@ void vlc_input_decoder_ChangeDelay( vlc_input_decoder_t *owner, vlc_tick_t delay
 
 void vlc_input_decoder_StartWait( vlc_input_decoder_t *p_owner )
 {
-    if( vlc_input_decoder_IsSynchronous( p_owner ) )
+    if( p_owner->master_dec != NULL /* SubDecs are paced by their master */
+     || vlc_input_decoder_IsSynchronous( p_owner ) )
         return;
 
     assert( !p_owner->b_waiting );
@@ -2600,7 +2620,8 @@ void vlc_input_decoder_StartWait( vlc_input_decoder_t *p_owner )
 
 void vlc_input_decoder_StopWait( vlc_input_decoder_t *p_owner )
 {
-    if( vlc_input_decoder_IsSynchronous( p_owner ) )
+    if( p_owner->master_dec != NULL /* SubDecs are paced by their master */
+     || vlc_input_decoder_IsSynchronous( p_owner ) )
         return;
 
     vlc_fifo_Lock(p_owner->p_fifo);
@@ -2612,7 +2633,8 @@ void vlc_input_decoder_StopWait( vlc_input_decoder_t *p_owner )
 
 void vlc_input_decoder_Wait( vlc_input_decoder_t *p_owner )
 {
-    if( vlc_input_decoder_IsSynchronous( p_owner ) )
+    if( p_owner->master_dec != NULL /* SubDecs are paced by their master */
+     || vlc_input_decoder_IsSynchronous( p_owner ) )
     {
         /* Nothing to wait for. There's no decoder thread running. */
         return;


=====================================
src/input/decoder.h
=====================================
@@ -104,21 +104,28 @@ void vlc_input_decoder_StopWait( vlc_input_decoder_t * );
 bool vlc_input_decoder_IsEmpty( vlc_input_decoder_t * );
 
 /**
- * This function activates the request closed caption channel.
- */
-int vlc_input_decoder_SetCcState( vlc_input_decoder_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.
+ * This function Creates and adds the requested SubDec.
+ *
+ * The sub decoder returned by this function must be deleted with
+ * vlc_input_decoder_Delete() before the parent is deleted.
  */
-int vlc_input_decoder_GetCcState( vlc_input_decoder_t *, int i_channel, bool *pb_decode );
+vlc_input_decoder_t *
+vlc_input_decoder_CreateSubDec(vlc_input_decoder_t *dec,
+                               const struct vlc_input_decoder_cfg *cfg);
 
 /**
  * This function forces the display of the next picture
  */
 void vlc_input_decoder_FrameNext( vlc_input_decoder_t *p_dec );
 
+struct vlc_subdec_desc
+{
+    es_format_t *fmt_array;
+    size_t fmt_count;
+};
+
+void vlc_subdec_desc_Clean(struct vlc_subdec_desc *desc);
+
 struct vlc_input_decoder_status
 {
     struct {
@@ -133,9 +140,7 @@ struct vlc_input_decoder_status
         vlc_meta_t *meta;
     } format;
 
-    struct {
-        decoder_cc_desc_t desc;
-    } cc;
+    struct vlc_subdec_desc subdec_desc;
 };
 
 /**


=====================================
src/input/es_out.c
=====================================
@@ -142,15 +142,10 @@ struct es_out_id_t
     vlc_tick_t i_pts_level;
     vlc_tick_t delay;
 
-    /* Fields for Video with CC */
-    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;
+    /* Fields for ES created by decoders */
+    struct VLC_VECTOR(es_out_id_t *) sub_es_vec;
 
-    /* Field for CC track from a master video */
+    /* Field for a sub track from a master ES */
     es_out_id_t *p_master;
 
     struct vlc_list node;
@@ -315,15 +310,6 @@ default_val:
     return VLC_CLOCK_MASTER_AUTO;
 }
 
-static inline int EsOutGetClosedCaptionsChannel( const es_format_t *p_fmt )
-{
-    if ((p_fmt->i_codec == VLC_CODEC_CEA608 && p_fmt->subs.cc.i_channel < 4)
-     || (p_fmt->i_codec == VLC_CODEC_CEA708 && p_fmt->subs.cc.i_channel < 64))
-        return p_fmt->subs.cc.i_channel;
-
-    return -1;
-}
-
 #define foreach_es_then_es_slaves( pos ) \
     for( int fetes_i=0; fetes_i<2; fetes_i++ ) \
         vlc_list_foreach( pos, (!fetes_i ? &p_sys->es : &p_sys->es_slaves), node )
@@ -669,18 +655,6 @@ static vlc_tick_t EsOutGetWakeup( es_out_t *out )
 
 static es_out_id_t es_cat[DATA_ES];
 
-static es_out_id_t *EsOutGetSelectedCat( es_out_t *out,
-                                         enum es_format_category_e cat )
-{
-    es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
-    es_out_id_t *es;
-
-    foreach_es_then_es_slaves( es )
-        if( es->fmt.i_cat == cat && EsIsSelected( es ) )
-            return es;
-    return NULL;
-}
-
 static bool EsOutDecodersIsEmpty( es_out_t *out )
 {
     es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
@@ -2151,8 +2125,7 @@ static es_out_id_t *EsOutAddLocked( es_out_t *out, input_source_t *source,
     es->p_dec_record = NULL;
     es->p_clock = NULL;
     es->master = false;
-    es->cc.type = 0;
-    es->cc.i_bitmap = 0;
+    vlc_vector_init(&es->sub_es_vec);
     es->p_master = p_master;
     es->mouse_event_cb = NULL;
     es->mouse_event_userdata = NULL;
@@ -2190,20 +2163,7 @@ static es_out_id_t *EsOutAdd( es_out_t *out, input_source_t *source, const es_fo
 
 static bool EsIsSelected( es_out_id_t *es )
 {
-    /* Some tracks, especially closed-captions, are extracted from an
-     * existing elementary stream through the packetizer or the decoder,
-     * which must be selected for the extracted track to be selected.
-     * In every other cases, we don't need to consider that. */
-    if (es->p_master == NULL)
-        return es->p_dec != NULL;
-
-    bool b_decode = false;
-    if( es->p_master->p_dec )
-    {
-        int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
-        vlc_input_decoder_GetCcState( es->p_master->p_dec, i_channel, &b_decode );
-    }
-    return b_decode;
+    return es->p_dec != NULL;
 }
 
 static void ClockUpdate(vlc_tick_t system_ts, vlc_tick_t ts, double rate,
@@ -2281,7 +2241,15 @@ static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )
         .cbs = &decoder_cbs,
         .cbs_data = p_es,
     };
-    dec = vlc_input_decoder_New( VLC_OBJECT(p_input), &cfg );
+    if (p_es->p_master != NULL)
+    {
+        assert(p_es->p_master->p_dec != NULL);
+        dec = vlc_input_decoder_CreateSubDec(p_es->p_master->p_dec, &cfg);
+    }
+    else
+    {
+        dec = vlc_input_decoder_New( VLC_OBJECT(p_input), &cfg );
+    }
     if( dec != NULL )
     {
         vlc_input_decoder_ChangeRate( dec, p_sys->rate );
@@ -2331,6 +2299,19 @@ static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )
 
     EsOutUpdateDelayJitter(out);
 }
+
+static void EsOutDeleteSubESes(es_out_t *out, es_out_id_t *parent)
+{
+    for (size_t i = 0; i < parent->sub_es_vec.size; ++i)
+    {
+        es_out_id_t *subes = parent->sub_es_vec.data[i];
+        assert(subes != NULL);
+        EsOutDelLocked(out, subes);
+    }
+
+    vlc_vector_clear(&parent->sub_es_vec);
+}
+
 static void EsOutDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
 {
     VLC_UNUSED(out);
@@ -2340,13 +2321,18 @@ static void EsOutDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
 
     assert( p_es->p_pgrm );
 
+    EsOutDeleteSubESes(out, p_es);
+
     vlc_input_decoder_Flush(p_es->p_dec);
     vlc_input_decoder_Delete( p_es->p_dec );
     p_es->p_dec = NULL;
     if( p_es->p_pgrm->p_master_es_clock == p_es->p_clock )
         p_es->p_pgrm->p_master_es_clock = NULL;
-    vlc_clock_Delete( p_es->p_clock );
-    p_es->p_clock = NULL;
+    if( p_es->p_clock != NULL ) /* SubEs have their own clocks */
+    {
+        vlc_clock_Delete( p_es->p_clock );
+        p_es->p_clock = NULL;
+    }
 
     if( p_es->p_dec_record )
     {
@@ -2373,61 +2359,46 @@ static void EsOutSelectEs( es_out_t *out, es_out_id_t *es, bool b_force )
     if( !es->p_pgrm )
         return;
 
-    if( es->p_master )
-    {
-        int i_channel;
-        if( !es->p_master->p_dec )
-            return;
-
-        i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
-
-        if( i_channel == -1 ||
-            vlc_input_decoder_SetCcState( es->p_master->p_dec, i_channel, true ) )
-            return;
-    }
-    else
+    const bool b_sout = input_priv(p_input)->p_sout != NULL;
+    /* If b_forced, the ES is specifically requested by the user, so bypass
+     * the following vars check. */
+    if( !b_force )
     {
-        const bool b_sout = input_priv(p_input)->p_sout != NULL;
-        /* If b_forced, the ES is specifically requested by the user, so bypass
-         * the following vars check. */
-        if( !b_force )
+        if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
         {
-            if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
+            if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) )
             {
-                if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) )
-                {
-                    msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
-                             es->fmt.i_id );
-                    return;
-                }
+                msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
+                         es->fmt.i_id );
+                return;
             }
-            else if( es->fmt.i_cat == AUDIO_ES )
+        }
+        else if( es->fmt.i_cat == AUDIO_ES )
+        {
+            if( b_thumbnailing
+             || !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) )
             {
-                if( b_thumbnailing
-                 || !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) )
-                {
-                    msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
-                             es->fmt.i_id );
-                    return;
-                }
+                msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
+                         es->fmt.i_id );
+                return;
             }
-            if( es->fmt.i_cat == SPU_ES )
+        }
+        if( es->fmt.i_cat == SPU_ES )
+        {
+            if( b_thumbnailing
+             || !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) )
             {
-                if( b_thumbnailing
-                 || !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) )
-                {
-                    msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
-                             es->fmt.i_id );
-                    return;
-                }
+                msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
+                         es->fmt.i_id );
+                return;
             }
         }
+    }
 
-        EsOutCreateDecoder( out, es );
+    EsOutCreateDecoder( out, es );
 
-        if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
-            return;
-    }
+    if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
+        return;
 
     /* Mark it as selected */
     EsOutSendEsEvent(out, es, VLC_INPUT_ES_SELECTED, b_force);
@@ -2446,44 +2417,18 @@ static void EsOutSelectEs( es_out_t *out, es_out_id_t *es, bool b_force )
     }
 }
 
-static void EsOutDrainCCChannels( es_out_id_t *parent )
-{
-    /* Drain captions sub ES as well */
-    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] ||
-            !parent->cc.pp_es[i]->p_dec )
-            continue;
-        vlc_input_decoder_Drain( parent->cc.pp_es[i]->p_dec );
-    }
-}
-
-static void EsDeleteCCChannels( es_out_t *out, es_out_id_t *parent )
+static void EsOutDrainSubESes(es_out_id_t *parent)
 {
-    if( parent->cc.type == 0 )
-        return;
-
-    es_out_id_t *spu_es = EsOutGetSelectedCat( out, SPU_ES );
-    const int i_spu_id = spu_es ? spu_es->fmt.i_id : -1;
-
-    uint64_t i_bitmap = parent->cc.i_bitmap;
-    for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1 )
+    /* Drain sub ES as well */
+    for (size_t i = 0; i < parent->sub_es_vec.size; ++i)
     {
-        if( (i_bitmap & 1) == 0 || !parent->cc.pp_es[i] )
+        es_out_id_t *es = parent->sub_es_vec.data[i];
+        assert(es != NULL);
+        if (es->p_dec == NULL)
             continue;
 
-        if( i_spu_id == parent->cc.pp_es[i]->fmt.i_id )
-        {
-            /* Force unselection of the CC */
-            EsOutSendEsEvent(out, parent->cc.pp_es[i], VLC_INPUT_ES_UNSELECTED,
-                             false);
-        }
-        EsOutDelLocked( out, parent->cc.pp_es[i] );
+        vlc_input_decoder_Drain(es->p_dec);
     }
-
-    parent->cc.i_bitmap = 0;
-    parent->cc.type = 0;
 }
 
 static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update )
@@ -2500,21 +2445,7 @@ static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update )
     if( p_sys->p_next_frame_es == es )
         EsOutStopNextFrame( out );
 
-    if( es->p_master )
-    {
-        if( es->p_master->p_dec )
-        {
-            int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
-            if( i_channel != -1 )
-                vlc_input_decoder_SetCcState( es->p_master->p_dec,
-                                              i_channel, false );
-        }
-    }
-    else
-    {
-        EsDeleteCCChannels( out, es );
-        EsOutDestroyDecoder( out, es );
-    }
+    EsOutDestroyDecoder( out, es );
 
     if( !b_update )
         return;
@@ -2809,44 +2740,49 @@ static void EsOutSelectList( es_out_t *out, enum es_format_category_e cat,
     }
 }
 
-static void EsOutCreateCCChannels( es_out_t *out, vlc_fourcc_t codec, uint64_t i_bitmap,
-                                   const char *psz_descfmt, es_out_id_t *parent )
+static void EsOutCreateSubESes(es_out_t *out,
+                               struct vlc_subdec_desc *desc,
+                               es_out_id_t *parent)
 {
-    es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
-    input_thread_t *p_input = p_sys->p_input;
+    es_out_sys_t *sys = container_of(out, es_out_sys_t, out);
 
-    /* Only one type of captions is allowed ! */
-    if( parent->cc.type && parent->cc.type != codec )
+    size_t prev_count = parent->sub_es_vec.size;
+    bool success = vlc_vector_reserve(&parent->sub_es_vec, desc->fmt_count);
+    if (!success)
         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;
-
-        es_format_Init( &fmt, SPU_ES, codec );
-        fmt.i_id = i + 1;
-        fmt.subs.cc.i_channel = i;
-        fmt.i_group = parent->fmt.i_group;
-        if( asprintf( &fmt.psz_description, psz_descfmt, fmt.i_id ) == -1 )
-            fmt.psz_description = NULL;
+    /* Initialize new data to NULL */
+    for (size_t i = prev_count; i < desc->fmt_count; ++i)
+        parent->sub_es_vec.data[i] = NULL;
 
-        msg_Dbg( p_input, "Adding CC track %d for es[%d]", fmt.i_id, parent->fmt.i_id );
+    for (size_t i = 0; i < desc->fmt_count; ++i)
+    {
+        es_format_t *fmt = &desc->fmt_array[i];
+        fmt->i_group = parent->fmt.i_group;
 
-        es_out_id_t **pp_es = &parent->cc.pp_es[i];
-        *pp_es = EsOutAddLocked( out, parent->p_pgrm->source, &fmt, parent );
-        es_format_Clean( &fmt );
+        es_out_id_t *es = parent->sub_es_vec.data[i];
+        if (es != NULL)
+            continue; /* XXX: Update or Del/Add the new FMT (if same codec) */
 
-        /* */
-        parent->cc.i_bitmap |= (1ULL << i);
-        parent->cc.type = codec;
+        es = EsOutAddLocked(out, parent->p_pgrm->source, fmt, parent);
+        if (es == NULL)
+        {
+            parent->sub_es_vec.size = i;
+            break;
+        }
+        success = vlc_vector_insert(&parent->sub_es_vec, i, es);
+        assert(success); /* vlc_vector_reserve() already checked */
 
-        /* Enable if user specified on command line */
-        if (p_sys->sub.i_channel == i)
-            EsOutSelect(out, *pp_es, true);
+        switch (fmt->i_cat)
+        {
+            case SPU_ES:
+                /* Enable if user specified on command line */
+                if (sys->sub.i_channel == fmt->subs.cc.i_channel)
+                    EsOutSelect(out, es, true);
+                break;
+            default:
+                break;
+        }
     }
 }
 
@@ -2963,6 +2899,7 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
     }
 
     /* Decode */
+    assert(es->p_master == NULL);
     if( es->p_dec_record )
     {
         block_t *p_dup = block_Duplicate( p_block );
@@ -2989,13 +2926,11 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
             vlc_meta_Delete( status.format.meta );
     }
 
-    /* Check CC status */
-    if( p_sys->cc_decoder == 708 )
-        EsOutCreateCCChannels( out, VLC_CODEC_CEA708, status.cc.desc.i_708_channels,
-                               _("DTVCC Closed captions %u"), es );
-    else
-        EsOutCreateCCChannels( out, VLC_CODEC_CEA608, status.cc.desc.i_608_channels,
-                               _("Closed captions %u"), es );
+    /* Check subdec status */
+    if (status.subdec_desc.fmt_count > 0)
+        EsOutCreateSubESes(out, &status.subdec_desc, es);
+
+    vlc_subdec_desc_Clean(&status.subdec_desc);
 
     vlc_mutex_unlock( &p_sys->lock );
 
@@ -3009,7 +2944,7 @@ EsOutDrainDecoder(es_out_t *out, es_out_id_t *es, bool wait)
     assert( es->p_dec );
 
     vlc_input_decoder_Drain( es->p_dec );
-    EsOutDrainCCChannels( es );
+    EsOutDrainSubESes(es);
 
     if (!wait)
         return;
@@ -3497,7 +3432,6 @@ static int EsOutVaControlLocked( es_out_t *out, input_source_t *source,
         if( es->p_dec )
         {
             EsOutDrainDecoder(out, es, true);
-            EsDeleteCCChannels( out, es );
             EsOutDestroyDecoder( out, es );
         }
 


=====================================
test/src/input/decoder/input_decoder_scenarios.c
=====================================
@@ -579,6 +579,31 @@ static void player_setup_select_ccdec_708_1064(vlc_player_t *player)
         "video/0/cc/spu/10,video/0/cc/spu/64");
 }
 
+static vlc_frame_t *packetizer_getcc_cea608_02(decoder_t *dec, decoder_cc_desc_t *cc_desc)
+{
+    (void)dec;
+
+    cc_desc->i_608_channels = 1 << 1;
+    cc_desc->i_708_channels = 0;
+    cc_desc->i_reorder_depth = 4;
+
+    return create_cc_frame(VLC_TICK_0);
+}
+
+static void cc_decoder_setup_608(decoder_t *dec)
+{
+    assert(dec->fmt_in->i_codec == VLC_CODEC_CEA608);
+
+    dec->fmt_out.i_codec = VLC_CODEC_TEXT;
+}
+
+static void cc_text_renderer_render_608_02(filter_t *filter,
+                                             subpicture_region_t *region_in)
+{
+    assert(strcmp(region_in->p_text->psz_text, "cc02_dec") == 0);
+    vlc_sem_post(&scenario_data.wait_stop);
+}
+
 static const vlc_fourcc_t subpicture_chromas[] = {
     VLC_CODEC_RGBA, 0
 };
@@ -717,6 +742,20 @@ struct input_decoder_scenario input_decoder_scenarios[] =
     .text_renderer_render = cc_text_renderer_render_708_1064,
     .player_setup_before_start = player_setup_select_ccdec_708_1064,
 },
+{
+    /* The "--sub-track" option use channel starting with 0 whereas CC tracks
+     * indexes start with 1. Ensure channel 1 match with the CC track 2. */
+    .name = "A cea608 track can be selected via command line",
+    .source = source_800_600 ";video_packetized=false",
+    .item_option = ":sub-track=1",
+    .packetizer_getcc = packetizer_getcc_cea608_02,
+    .decoder_setup = decoder_i420_800_600_update,
+    .decoder_decode = decoder_decode,
+    .cc_decoder_setup = cc_decoder_setup_608,
+    .cc_decoder_decode = cc_decoder_decode_channel,
+    .display_prepare = display_prepare_noop,
+    .text_renderer_render = cc_text_renderer_render_608_02,
+},
 };
 
 size_t input_decoder_scenarios_count = ARRAY_SIZE(input_decoder_scenarios);



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7745bb446f6f8dfd51787fe664fa92dde147e095...a6a3860b67b69e246a256fdba533938fb3069d43

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7745bb446f6f8dfd51787fe664fa92dde147e095...a6a3860b67b69e246a256fdba533938fb3069d43
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list