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]
