[vlc-commits] [Git][videolan/vlc][master] 10 commits: es_format: add b_forced flag to subs_format_t

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Mon Jun 29 12:53:10 UTC 2026



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
d01d4aaf by Felix Paul Kühne at 2026-06-29T14:42:45+02:00
es_format: add b_forced flag to subs_format_t

- - - - -
f6324486 by Janek Krüger at 2026-06-29T14:42:45+02:00
spudec: only show forced subtitles on a forced track

Co-authored-by: Saifelden Mohamed Ismail <saifeldenmi at gmail.com>

- - - - -
3fe8dc8d by Janek Krüger at 2026-06-29T14:42:45+02:00
avcodec: only show forced subtitles on a forced track (pgssub)

Co-authored-by: Saifelden Mohamed Ismail <saifeldenmi at gmail.com>

- - - - -
698e0c3c by Felix Paul Kühne at 2026-06-29T14:42:45+02:00
dvdnav/dvdread: expose DVD audio/subtitle code_extension as track description

Co-authored-by: Saifelden Mohamed Ismail <saifeldenmi at gmail.com>

- - - - -
7f47e56b by Felix Paul Kühne at 2026-06-29T14:42:45+02:00
dvdnav/dvdread: mark forced-caption SPU tracks as forced

Co-authored-by: Saifelden Mohamed Ismail <saifeldenmi at gmail.com>

- - - - -
87f3b9c4 by Felix Paul Kühne at 2026-06-29T14:42:45+02:00
mp4: simplify forced SPU handling

The flag previously lived duplicated on track_config_t and mp4_track_t.
With b_forced on es_format_t, this is redundant.

The auto-default behavior for forced tx3g tracks is preserved.

- - - - -
7e242296 by Saifelden Mohamed Ismail at 2026-06-29T14:42:45+02:00
dvdread: describe tracks from code_extension

- - - - -
05224f0e by Saifelden Mohamed Ismail at 2026-06-29T14:42:45+02:00
dvdread: treat forced-caption subtitles as forced

- - - - -
77774232 by Saifelden Mohamed Ismail at 2026-06-29T14:42:45+02:00
dvdnav: ignore the hidden flag when matching the active subtitle

- - - - -
60e4617d by Saifelden Mohamed Ismail at 2026-06-29T14:42:45+02:00
dvdnav: show forced subtitles when the subpicture stream is hidden

- - - - -


15 changed files:

- include/vlc_es.h
- modules/access/Makefile.am
- + modules/access/dvd_description.h
- modules/access/dvdnav.c
- modules/access/dvdread.c
- modules/access/dvdread.h
- modules/access/dvdread_video.c
- modules/codec/avcodec/subtitle.c
- modules/codec/spudec/parse.c
- modules/codec/spudec/spudec.c
- modules/codec/spudec/spudec.h
- modules/demux/mp4/essetup.c
- modules/demux/mp4/mp4.c
- modules/demux/mp4/mp4.h
- po/POTFILES.in


Changes:

=====================================
include/vlc_es.h
=====================================
@@ -580,6 +580,9 @@ struct subs_format_t
         /* Reorder depth of transport video, -1 for no reordering */
         int i_reorder_depth;
     } cc;
+
+    /* track marked as forced */
+    bool b_forced;
 };
 
 /**


=====================================
modules/access/Makefile.am
=====================================
@@ -279,7 +279,7 @@ endif
 EXTRA_LTLIBRARIES += libvcd_plugin.la
 access_PLUGINS += $(LTLIBvcd)
 
-libdvdnav_plugin_la_SOURCES = access/disc_helper.h access/dvdnav.c demux/mpeg/ps.h demux/mpeg/pes.h
+libdvdnav_plugin_la_SOURCES = access/disc_helper.h access/dvd_description.h access/dvdnav.c demux/mpeg/ps.h demux/mpeg/pes.h
 libdvdnav_plugin_la_CFLAGS = $(AM_CFLAGS) $(DVDNAV_CFLAGS)
 libdvdnav_plugin_la_LIBADD = $(DVDNAV_LIBS)
 libdvdnav_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(access_RPATH)
@@ -289,7 +289,7 @@ endif
 access_PLUGINS += $(LTLIBdvdnav)
 EXTRA_LTLIBRARIES += libdvdnav_plugin.la
 
-libdvdread_plugin_la_SOURCES = access/disc_helper.h access/dvdread.h access/dvdread.c access/dvdread_video.c access/dvdread_audio.c access/dvdread_vr.c demux/mpeg/ps.h demux/mpeg/pes.h \
+libdvdread_plugin_la_SOURCES = access/disc_helper.h access/dvd_description.h access/dvdread.h access/dvdread.c access/dvdread_video.c access/dvdread_audio.c access/dvdread_vr.c demux/mpeg/ps.h demux/mpeg/pes.h \
                                demux/moving_avg.h demux/timestamps_filter.h
 libdvdread_plugin_la_CFLAGS = $(AM_CFLAGS) $(DVDREAD_CFLAGS)
 libdvdread_plugin_la_LIBADD = $(DVDREAD_LIBS)


=====================================
modules/access/dvd_description.h
=====================================
@@ -0,0 +1,61 @@
+/*****************************************************************************
+ * dvd_description.h: DVD audio/subtitle code_extension description tables
+ *****************************************************************************
+ * Copyright (C) 2026 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include <dvdread/version.h>
+
+/**
+ * DVD audio track code_extension values as defined in the DVD-Video spec.
+ * Maps audio_attr_t.code_extension to translatable description strings.
+ */
+static const char *const dvd_audio_code_ext[] = {
+    /* 0 unspecified */ NULL,
+    /* 1 normal */      NULL,
+    /* 2 */ N_("Audio for visually impaired"),
+    /* 3 */ N_("Director's comments"),
+    /* 4 */ N_("Alternate director's comments"),
+};
+
+/* older libdvdread does not have this enum yet */
+#if DVDREAD_VERSION < DVDREAD_VERSION_CODE(7, 1, 0)
+# define DVD_SUBP_CODE_EXT_FORCED 9
+#endif
+
+/**
+ * DVD subtitle track code_extension values as defined in the DVD-Video spec.
+ * Maps subp_attr_t.code_extension to translatable description strings.
+ */
+static const char *const dvd_spu_code_ext[] = {
+    /* 0 unspecified */ NULL,
+    /* 1 normal */      NULL,
+    /* 2 */ N_("Large"),
+    /* 3 */ N_("For children"),
+    /* 4 */ NULL,
+    /* 5 */ N_("Closed caption"),
+    /* 6 */ N_("Closed caption (large)"),
+    /* 7 */ N_("Closed caption (children)"),
+    /* 8 */ NULL,
+    /* 9 */ N_("Forced caption"),
+    /* 10 */ NULL,
+    /* 11 */ NULL,
+    /* 12 */ NULL,
+    /* 13 */ N_("Director's comments"),
+    /* 14 */ N_("Director's comments (large)"),
+    /* 15 */ N_("Director's comments (children)"),
+};


=====================================
modules/access/dvdnav.c
=====================================
@@ -65,6 +65,7 @@ dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t *, uint64_t, int32_t);
 #include "../demux/timestamps_filter.h"
 
 #include "disc_helper.h"
+#include "dvd_description.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -80,6 +81,10 @@ dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t *, uint64_t, int32_t);
 
 #define LANGUAGE_DEFAULT ("en")
 
+/* dvdnav_get_active_spu_stream sets this bit when subtitles are hidden
+   and only forced captions should show */
+#define DVDNAV_SPU_HIDDEN 0x80
+
 static int  AccessDemuxOpen ( vlc_object_t * );
 static void Close( vlc_object_t * );
 
@@ -1675,6 +1680,17 @@ static void ESNew( demux_t *p_demux, int i_id )
             {
                 b_select = true;
             }
+
+            /* Audio track description from code_extension */
+            audio_attr_t audio_attr;
+            if( dvdnav_get_audio_attr( p_sys->dvdnav, i_audio, &audio_attr )
+                == DVDNAV_STATUS_OK )
+            {
+                    if( audio_attr.code_extension < ARRAY_SIZE(dvd_audio_code_ext)
+                    && dvd_audio_code_ext[audio_attr.code_extension] )
+                    tk->fmt.psz_description =
+                        strdup( vlc_gettext( dvd_audio_code_ext[audio_attr.code_extension] ) );
+            }
         }
     }
     else if( tk->fmt.i_cat == SPU_ES )
@@ -1682,6 +1698,20 @@ static void ESNew( demux_t *p_demux, int i_id )
         int32_t i_title, i_part;
         i_lang = dvdnav_spu_stream_to_lang( p_sys->dvdnav, i_id&0x1f );
 
+        /* Subtitle track description from code_extension */
+        subp_attr_t subp_attr;
+        if( dvdnav_get_spu_attr( p_sys->dvdnav, i_id&0x1f, &subp_attr )
+            == DVDNAV_STATUS_OK )
+        {
+            if( subp_attr.code_extension < ARRAY_SIZE(dvd_spu_code_ext)
+                && dvd_spu_code_ext[subp_attr.code_extension] )
+                tk->fmt.psz_description =
+                    strdup( vlc_gettext( dvd_spu_code_ext[subp_attr.code_extension] ) );
+
+            if( subp_attr.code_extension == DVD_SUBP_CODE_EXT_FORCED )
+                tk->fmt.subs.b_forced = true;
+        }
+
         /* Palette */
         tk->fmt.subs.spu.b_palette = true;
         static_assert(sizeof(tk->fmt.subs.spu.palette) == sizeof(p_sys->clut),
@@ -1689,11 +1719,14 @@ static void ESNew( demux_t *p_demux, int i_id )
         memcpy( tk->fmt.subs.spu.palette, p_sys->clut, sizeof( p_sys->clut ) );
 
         /* We select only when we are not in the menu */
+        int i_active_spu = dvdnav_get_active_spu_stream( p_sys->dvdnav );
         if( dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part ) == DVDNAV_STATUS_OK &&
-            i_title > 0 &&
-            dvdnav_get_active_spu_stream( p_sys->dvdnav ) == (i_id&0x1f) )
+            i_title > 0 && i_active_spu != -1 &&
+            (i_active_spu & 0x1f) == (i_id&0x1f) )
         {
             b_select = true;
+            if( i_active_spu & DVDNAV_SPU_HIDDEN )
+                tk->fmt.subs.b_forced = true;
         }
     }
 


=====================================
modules/access/dvdread.c
=====================================
@@ -48,6 +48,7 @@
 #include <vlc_input.h>
 #include <vlc_charset.h>
 #include "disc_helper.h"
+#include "dvd_description.h"
 
 
 /*****************************************************************************
@@ -860,7 +861,7 @@ static int DemuxBlock( demux_t *p_demux, const uint8_t *p, int len )
 
                 if( !tk->b_configured )
                 {
-                    DvdReadESNew( p_demux, i_id, 0 );
+                    DvdReadESNew( p_demux, i_id, 0, 0 );
                 }
                 if( tk->es &&
                     !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) )
@@ -914,7 +915,7 @@ static int DemuxBlock( demux_t *p_demux, const uint8_t *p, int len )
 /*****************************************************************************
  * ESNew: register a new elementary stream
  *****************************************************************************/
-void DvdReadESNew( demux_t *p_demux, int i_id, int i_lang )
+void DvdReadESNew( demux_t *p_demux, int i_id, int i_lang, int i_code_ext )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     ps_track_t  *tk = &p_sys->tk[ps_id_to_tk(i_id)];
@@ -966,6 +967,11 @@ void DvdReadESNew( demux_t *p_demux, int i_id, int i_lang )
 #endif
 
         if( psz_language[0] ) tk->fmt.psz_language = strdup( psz_language );
+
+        if( (size_t) i_code_ext < ARRAY_SIZE(dvd_audio_code_ext)
+            && dvd_audio_code_ext[i_code_ext] )
+            tk->fmt.psz_description =
+                strdup( vlc_gettext( dvd_audio_code_ext[i_code_ext] ) );
     }
     else if( tk->fmt.i_cat == SPU_ES )
     {
@@ -976,6 +982,14 @@ void DvdReadESNew( demux_t *p_demux, int i_id, int i_lang )
         memcpy( tk->fmt.subs.spu.palette, p_sys->clut, sizeof( p_sys->clut ) );
 
         if( psz_language[0] ) tk->fmt.psz_language = strdup( psz_language );
+
+        if( (size_t) i_code_ext < ARRAY_SIZE(dvd_spu_code_ext)
+            && dvd_spu_code_ext[i_code_ext] )
+            tk->fmt.psz_description =
+                strdup( vlc_gettext( dvd_spu_code_ext[i_code_ext] ) );
+
+        if( i_code_ext == DVD_SUBP_CODE_EXT_FORCED )
+            tk->fmt.subs.b_forced = true;
     }
 
     tk->es = es_out_Add( p_demux->out, &tk->fmt );


=====================================
modules/access/dvdread.h
=====================================
@@ -190,7 +190,7 @@ int OpenAudio( vlc_object_t * );
 uint32_t   DvdVRGetProgramSectorSpan( const demux_sys_t *, const vobu_map_t * );
 #endif
 
-void DvdReadESNew( demux_t *p_demux, int i_id, int i_lang );
+void DvdReadESNew( demux_t *p_demux, int i_id, int i_lang, int i_code_ext );
 void DvdReadHandleDSI( demux_t *p_demux, uint8_t *p_data );
 
 extern const dvdread_ops_t DvdReadVideoOps;


=====================================
modules/access/dvdread_video.c
=====================================
@@ -678,7 +678,7 @@ static int DvdReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
         /* TODO: re-add angles */
 
 
-        DvdReadESNew( p_demux, 0xe0, 0 ); /* Video, FIXME ? */
+        DvdReadESNew( p_demux, 0xe0, 0, 0 ); /* Video, FIXME ? */
         const video_attr_t *p_attr = &p_vts->vtsi_mat->vts_video_attr;
         int i_video_height = p_attr->video_format != 0 ? 576 : 480;
         int i_video_width;
@@ -752,7 +752,9 @@ static int DvdReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
                 }
 
                 DvdReadESNew( p_demux, i_id, p_sys->p_vts_file->vtsi_mat->
-                       vts_audio_attr[i - 1].lang_code );
+                       vts_audio_attr[i - 1].lang_code,
+                       p_sys->p_vts_file->vtsi_mat->
+                       vts_audio_attr[i - 1].code_extension );
             }
         }
 
@@ -798,7 +800,9 @@ static int DvdReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
                 i_id = (0x20 + i_position) | PS_PACKET_ID_MASK_VOB;
 
                 DvdReadESNew( p_demux, i_id, p_sys->p_vts_file->vtsi_mat->
-                       vts_subp_attr[i - 1].lang_code );
+                       vts_subp_attr[i - 1].lang_code,
+                       p_sys->p_vts_file->vtsi_mat->
+                       vts_subp_attr[i - 1].code_extension );
             }
         }
 


=====================================
modules/codec/avcodec/subtitle.c
=====================================
@@ -34,6 +34,7 @@
 
 #include <libavcodec/avcodec.h>
 #include <libavutil/mem.h>
+#include <libavutil/opt.h>
 
 #include "avcodec.h"
 
@@ -65,6 +66,11 @@ int InitSubtitleDec(vlc_object_t *obj)
     /* */
     switch (codec->id) {
     case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
+        /* a track marked forced shows only its forced captions */
+        if (dec->fmt_in->subs.b_forced) {
+            av_opt_set_int(context->priv_data, "forced_subs_only", 1, 0);
+        }
+        break;
     case AV_CODEC_ID_XSUB:
     case AV_CODEC_ID_DVB_SUBTITLE:
         break;


=====================================
modules/codec/spudec/parse.c
=====================================
@@ -47,6 +47,7 @@ typedef struct
     vlc_tick_t i_stop;
     bool b_ephemer;
     bool b_subtitle;
+    bool b_forced;
 } spu_properties_t;
 
 typedef struct
@@ -390,6 +391,7 @@ static int ParseControlSeq( decoder_t *p_dec, vlc_tick_t i_pts,
     spu_properties.i_start = VLC_TICK_INVALID;
     spu_properties.i_stop = VLC_TICK_INVALID;
     spu_properties.b_subtitle = true;
+    spu_properties.b_forced = false;
 
     for( i_index = 4 + p_sys->i_rle_size; i_index < p_sys->i_spu_size ; )
     {
@@ -434,6 +436,7 @@ static int ParseControlSeq( decoder_t *p_dec, vlc_tick_t i_pts,
              * works around non displayable (offset by few ms)
              * spu menu over still frame in SPU_Select */
             spu_properties.b_subtitle = false;
+            spu_properties.b_forced = true;
             i_index += 1;
             break;
 
@@ -569,7 +572,7 @@ static int ParseControlSeq( decoder_t *p_dec, vlc_tick_t i_pts,
             i_index += 1;
 
             if( Validate( p_dec, i_index, i_cur_seq, i_next_seq,
-                          &spu_data, &spu_properties ) == VLC_SUCCESS )
+                          &spu_data, &spu_properties ) == VLC_SUCCESS && (!p_sys->b_forcedonly || spu_properties.b_forced))
                 OutputPicture( p_dec, &spu_data, &spu_properties, pf_queue );
 
             break;


=====================================
modules/codec/spudec/spudec.c
=====================================
@@ -81,6 +81,8 @@ static int OpenCommon( vlc_object_t *p_this, bool b_packetizer )
 
     p_sys->b_packetizer = b_packetizer;
     p_sys->b_disabletrans = var_InheritBool( p_dec, "dvdsub-transparency" );
+    /* a track marked forced shows only its forced captions */
+    p_sys->b_forcedonly = p_dec->fmt_in->subs.b_forced;
     p_sys->i_spu_size = 0;
     p_sys->buffer     = NULL;
     p_sys->buffer_size = 0;


=====================================
modules/codec/spudec/spudec.h
=====================================
@@ -26,6 +26,7 @@ typedef struct
 {
     bool b_packetizer;
     bool b_disabletrans;
+    bool b_forcedonly;
 
     vlc_tick_t i_pts;
     unsigned int i_spu_size;


=====================================
modules/demux/mp4/essetup.c
=====================================
@@ -1417,7 +1417,8 @@ int SetupSpuES( demux_t *p_demux, const mp4_track_t *p_track,
             if( p_text->i_data > 4 && GetDWBE(p_text->p_data) & 0xC0000000 )
             {
                 p_fmt->i_priority = ES_PRIORITY_SELECTABLE_MIN + 1;
-                p_cfg->b_forced_spu = true;
+                p_fmt->subs.b_forced = true;
+                p_fmt->psz_description = strdup( _("Forced caption") );
             }
 
             CopyExtradata( p_text->p_data, p_text->i_data, p_fmt );


=====================================
modules/demux/mp4/mp4.c
=====================================
@@ -465,12 +465,11 @@ static const MP4_Box_data_sbgp_entry_t *
     return p_sampleentry;
 }
 
-static es_out_id_t * MP4_CreateES( es_out_t *out, const es_format_t *p_fmt,
-                                   bool b_forced_spu )
+static es_out_id_t * MP4_CreateES( es_out_t *out, const es_format_t *p_fmt )
 {
     es_out_id_t *p_es = es_out_Add( out, p_fmt );
     /* Force SPU which isn't selected/defaulted */
-    if( p_fmt->i_cat == SPU_ES && p_es && b_forced_spu )
+    if( p_fmt->i_cat == SPU_ES && p_es && p_fmt->subs.b_forced )
         es_out_Control( out, ES_OUT_SET_ES_DEFAULT, p_es );
 
     return p_es;
@@ -3196,7 +3195,6 @@ static void TrackConfigApply( const track_config_t *p_cfg,
      memcpy( p_track->rgi_chans_reordering, p_cfg->rgi_chans_reordering,
              AOUT_CHAN_MAX * sizeof(p_cfg->rgi_chans_reordering[0]) );
      p_track->i_chans_to_reorder = p_cfg->i_chans_to_reorder;
-     p_track->b_forced_spu = p_cfg->b_forced_spu;
      p_track->i_block_flags = p_cfg->i_block_flags;
      p_track->b_ignore_implicit_pts = p_cfg->b_ignore_implicit_pts;
 }
@@ -3406,7 +3404,7 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
     p_track->p_sample = p_sample;
 
     if( pp_es )
-        *pp_es = MP4_CreateES( p_demux->out, p_fmt, p_track->b_forced_spu );
+        *pp_es = MP4_CreateES( p_demux->out, p_fmt );
 
     return ( !pp_es || *pp_es ) ? VLC_SUCCESS : VLC_EGENERIC;
 }
@@ -3724,7 +3722,7 @@ static int TrackUpdateFormat( demux_t *p_demux, mp4_track_t *p_track,
 
             es_out_Del( p_demux->out, p_track->p_es );
 
-            p_track->p_es = MP4_CreateES( p_demux->out, &tmpfmt, cfg.b_forced_spu );
+            p_track->p_es = MP4_CreateES( p_demux->out, &tmpfmt );
             if( !p_track->p_es )
             {
                 msg_Err( p_demux, "cannot create es for track[Id 0x%x]",


=====================================
modules/demux/mp4/mp4.h
=====================================
@@ -90,7 +90,6 @@ typedef struct
     uint8_t     rgi_chans_reordering[AOUT_CHAN_MAX];
     uint8_t     i_chans_to_reorder;
 
-    bool b_forced_spu; /* forced track selection (never done by default/priority) */
     bool b_ignore_implicit_pts;
 
     uint32_t    i_block_flags;
@@ -106,7 +105,6 @@ typedef struct
     bool b_selected;  /* is the trak being played */
     int i_use_flags;  /* !=0 Set when track is referenced by specific reference types.
                          You'll need to lookup other tracks tref to know the ref source */
-    bool b_forced_spu; /* forced track selection (never done by default/priority) */
     uint32_t i_switch_group;
 
     bool b_mac_encoding;


=====================================
po/POTFILES.in
=====================================
@@ -157,6 +157,7 @@ modules/access/dc1394.c
 modules/access/dcp/dcp.cpp
 modules/access/decklink.cpp
 modules/access/disc_helper.h
+modules/access/dvd_description.h
 modules/access/dshow/dshow.cpp
 modules/access/dsm/access.c
 modules/access/dsm/sd.c



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f95729fbeb1c72593715d013a5d6765ce06c5252...60e4617ddbd2db845520afb0edc982f0c3da2a5c

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f95729fbeb1c72593715d013a5d6765ce06c5252...60e4617ddbd2db845520afb0edc982f0c3da2a5c
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list