PR #25633 opened by Jun Zhao (mypopydev)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25633
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25633.patch

The DVB subtitle encoder never told the muxers which page it writes, so 
re-encoding to dvbsub produced Matroska
  S_DVBSUB tracks without the 5-byte CodecPrivate (composition page id, 
ancillary page id, subtitling type) required by
  the Matroska spec; players that rely on it — VLC in particular, which refuses 
such a track with "not enough codec data
  for S_DVBSUB" — then show no subtitles at all, while more lenient players 
like mpv do. This sets those five bytes in
  the encoder's extradata (page 1/1, the page it actually uses), so Matroska 
gets a valid CodecPrivate and the MPEG-TS
  subtitling descriptor uses the same values instead of its hardcoded defaults; 
because encoders cannot see stream
  dispositions, the muxer rederives the "hard of hearing" subtitling type (0x1x 
→ 0x2x) from the stream disposition so
  that flag is not lost. MPEG-TS output is byte-identical to before, the 
encoded bitstream is unchanged, and VLC 3.0.24
  now decodes and displays the subtitles.


From cd52be35e65793a6d91526bd4b1aa7ba154bbe5c Mon Sep 17 00:00:00 2001
From: Jun Zhao <[email protected]>
Date: Sat, 31 Oct 2026 11:37:35 +0800
Subject: [PATCH 1/2] avcodec/dvbsubenc: provide the page ids in extradata

The DVB subtitles written by this encoder always use page 1, both as
composition and as ancillary page id, yet this was not communicated to
the muxers: the encoder never set any extradata.

Muxers need it: the Matroska CodecPrivate of an S_DVBSUB track consists
of the composition page id, the ancillary page id and the subtitling
type (five bytes, the same layout the MPEG-TS subtitling descriptor uses
per language) and it is mandatory. Without it the resulting file is
invalid and players that rely on it (e.g. VLC, which refuses to create
such a track and afterwards filters the segments by page id) show no
subtitles at all; more lenient players like mpv are unaffected.

Set the five bytes in the encoder so that every container gets them,
including MPEG-TS, which previously filled in the page ids with the
same hardcoded default.

The encoded packets themselves are unchanged, only the fate-sub-dvb
reference gains the extradata line of the output stream.

Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/25610
Signed-off-by: Jun Zhao <[email protected]>
---
 libavcodec/dvbsubenc.c | 33 ++++++++++++++++++++++++++++++++-
 tests/ref/fate/sub-dvb |  1 +
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index e40199472bf..1c1d72ce5a9 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -23,8 +23,14 @@
 #include "codec_internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/colorspace.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
 #include "libavutil/opt.h"
 
+/* Every display set produced by this encoder uses this single page, i.e.
+ * the composition page id and the ancillary page id are both 1. */
+#define DVB_SUB_PAGE_ID 1
+
 typedef struct DVBSubtitleContext {
     AVClass * class;
     int object_version;
@@ -274,6 +280,30 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
     return len;
 }
 
+static av_cold int dvbsub_encode_init(AVCodecContext *avctx)
+{
+    /* The muxers need the page ids of this stream in order to fill the
+     * Matroska CodecPrivate or the MPEG-TS subtitling descriptor. Their
+     * layout is composition page id, ancillary page id and subtitling type
+     * (0x10: subtitles with no monitor aspect ratio criticality); it is also
+     * what the decoder expects. */
+    uint8_t *extradata = av_mallocz(5 + AV_INPUT_BUFFER_PADDING_SIZE);
+    if (!extradata)
+        return AVERROR(ENOMEM);
+
+    AV_WB16(extradata,     DVB_SUB_PAGE_ID);
+    AV_WB16(extradata + 2, DVB_SUB_PAGE_ID);
+    extradata[4]           = 0x10;
+
+    /* Extradata of the input stream has been copied into the context; it is
+     * of no use here, the page ids are always the ones above. */
+    av_freep(&avctx->extradata);
+    avctx->extradata      = extradata;
+    avctx->extradata_size = 5;
+
+    return 0;
+}
+
 static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
                          const AVSubtitle *h)
 {
@@ -284,7 +314,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t 
*outbuf, int buf_size,
 
     q = outbuf;
 
-    page_id = 1;
+    page_id = DVB_SUB_PAGE_ID;
 
     switch(s->min_bpp) {
     case 2:
@@ -555,6 +585,7 @@ const FFCodec ff_dvbsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
+    .init           = dvbsub_encode_init,
     FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
     .p.priv_class   = &dvbsubenc_class,
 };
diff --git a/tests/ref/fate/sub-dvb b/tests/ref/fate/sub-dvb
index cbd1801d641..8cd92709496 100644
--- a/tests/ref/fate/sub-dvb
+++ b/tests/ref/fate/sub-dvb
@@ -1,3 +1,4 @@
+#extradata 0:        5, 0x00160012
 #tb 0: 1/1000000
 #media_type 0: subtitle
 #codec_id 0: dvb_subtitle
-- 
2.54.0


From 83fd0f773ada2985963a727e64330cade1588bb5 Mon Sep 17 00:00:00 2001
From: Jun Zhao <[email protected]>
Date: Sat, 31 Oct 2026 11:37:35 +0800
Subject: [PATCH 2/2] avformat/mpegtsenc: rederive the hard of hearing
 subtitling type

Now that the DVB subtitle encoder provides a subtitling type in the
extradata, the type written into the subtitling descriptor is taken from
there. Encoders are not aware of dispositions though, so a stream for the
hard of hearing would lose its 0x20 type and be written as a normal
subtitle.

Keep the flag by replacing the normal subtitling type 0x1x with the
matching 0x2x for the hard of hearing (same monitor aspect ratio
criticality) when the stream disposition says so.

Signed-off-by: Jun Zhao <[email protected]>
---
 libavformat/mpegtsenc.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index e57889cb2d3..b46d7233700 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -757,7 +757,14 @@ static int mpegts_write_pmt(AVFormatContext *s, 
MpegTSService *service)
                        language++;
 
                    if (st->codecpar->extradata_size - extradata_copied >= 5) {
-                       *q++ = st->codecpar->extradata[extradata_copied + 4]; 
/* subtitling_type */
+                       /* subtitling_type: encoders are not aware of the hard
+                        * of hearing flag, so rederive it from the disposition:
+                        * 0x1x is a normal subtitle, 0x2x the same subtitle for
+                        * the hard of hearing. */
+                       uint8_t subtitling_type = 
st->codecpar->extradata[extradata_copied + 4];
+                       if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
+                           subtitling_type = 0x20 | (subtitling_type & 0x0F);
+                       *q++ = subtitling_type;
                        memcpy(q, st->codecpar->extradata + extradata_copied, 
4); /* composition_page_id and ancillary_page_id */
                        extradata_copied += 5;
                        q += 4;
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to