PR #25133 opened by Kacper Michajłow (kasper93) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25133 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25133.patch
From f2776bdf14ec2be32543ffa4c2d9658f99cc8794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 17 Oct 2026 01:37:54 +0200 Subject: [PATCH 1/7] tests/fate/demux: clean up only its own files after the HLS WebVTT test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The globs also matched the data of other tests named hls-webvtt-*. Signed-off-by: Kacper Michajłow <[email protected]> --- tests/fate/demux.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fate/demux.mak b/tests/fate/demux.mak index d4ac666f81..a86110bc6c 100644 --- a/tests/fate/demux.mak +++ b/tests/fate/demux.mak @@ -204,7 +204,7 @@ tests/data/hls-webvtt.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data FATE_FFMPEG-$(call FILTERDEMDECENCMUX, AEVALSRC ARESAMPLE, HLS MPEGTS WEBVTT, MP2 PCM_F64LE, MP2FIXED, HLS MPEGTS NULL WEBVTT, LAVFI_INDEV MPEGAUDIO_PARSER PIPE_PROTOCOL) += fate-hls-webvtt-demux fate-hls-webvtt-demux: tests/data/hls-webvtt.m3u8 -fate-hls-webvtt-demux: CLEANFILES = tests/data/hls-webvtt*.m3u8 tests/data/hls-webvtt-*.ts tests/data/hls-webvtt-*.vtt +fate-hls-webvtt-demux: CLEANFILES = tests/data/hls-webvtt.m3u8 tests/data/hls-webvtt-a.m3u8 tests/data/hls-webvtt-s.m3u8 tests/data/hls-webvtt-a[0-9]*.ts tests/data/hls-webvtt-s[0-9]*.vtt fate-hls-webvtt-demux: CMD = fmtstdout webvtt -copyts -i $(TARGET_PATH)/tests/data/hls-webvtt.m3u8 -map 0:a -c copy -f null - -map 0:s -c copy tests/data/hls-self-variant.m3u8: TAG = GEN -- 2.54.0 From 42b7963407988e3b3005105c4939da308401676d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 17 Oct 2026 01:37:54 +0200 Subject: [PATCH 2/7] tests/fate/demux: add HLS WebVTT timestamp map and seek tests Audio from 10 s with WebVTT segments mapping cue time 0 to 10 s, a cue that spans three segments and a seek into the second segment. --- tests/fate/demux.mak | 26 ++++++++++++++++++++++++++ tests/ref/fate/hls-webvtt-map-demux | 1 + tests/ref/fate/hls-webvtt-seek-demux | 1 + 3 files changed, 28 insertions(+) create mode 100644 tests/ref/fate/hls-webvtt-map-demux create mode 100644 tests/ref/fate/hls-webvtt-seek-demux diff --git a/tests/fate/demux.mak b/tests/fate/demux.mak index a86110bc6c..5021a01fb7 100644 --- a/tests/fate/demux.mak +++ b/tests/fate/demux.mak @@ -207,6 +207,32 @@ fate-hls-webvtt-demux: tests/data/hls-webvtt.m3u8 fate-hls-webvtt-demux: CLEANFILES = tests/data/hls-webvtt.m3u8 tests/data/hls-webvtt-a.m3u8 tests/data/hls-webvtt-s.m3u8 tests/data/hls-webvtt-a[0-9]*.ts tests/data/hls-webvtt-s[0-9]*.vtt fate-hls-webvtt-demux: CMD = fmtstdout webvtt -copyts -i $(TARGET_PATH)/tests/data/hls-webvtt.m3u8 -map 0:a -c copy -f null - -map 0:s -c copy +define HLS_WEBVTT_MAP_DATA +tests/data/$(1).m3u8: TAG = GEN +tests/data/$(1).m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data + $$(M)$$(TARGET_EXEC) $$(TARGET_PATH)/$$< -nostdin \ + -f lavfi -i "aevalsrc=0:d=6" -c:a mp2fixed -flags +bitexact -muxdelay 0 -muxpreload 0 -output_ts_offset 10 \ + -f hls -hls_time 2 -hls_list_size 0 \ + -hls_segment_filename $$(TARGET_PATH)/tests/data/$(1)-a%d.ts \ + $$(TARGET_PATH)/tests/data/$(1)-a.m3u8 2>/dev/null + $$(Q)printf '#EXTM3U\n#EXT-X-TARGETDURATION:2\n#EXTINF:2,\n$(1)-s0.vtt\n#EXTINF:2,\n$(1)-s1.vtt\n#EXTINF:2,\n$(1)-s2.vtt\n#EXT-X-ENDLIST\n' > tests/data/$(1)-s.m3u8 + $$(Q)printf 'WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:00.500 --> 00:01.500\nfirst\n\n00:01.000 --> 00:04.500 line:1\nlong\n' > tests/data/$(1)-s0.vtt + $$(Q)printf 'WEBVTT\nX-TIMESTAMP-MAP=LOCAL:00:00:02.000,MPEGTS:1080000\n\n00:01.000 --> 00:04.500 line:1\nlong\n\n00:03.000 --> 00:03.900 align:start\nsecond\n' > tests/data/$(1)-s1.vtt + $$(Q)printf 'WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:01.000 --> 00:04.500 line:1\nlong\n\n00:04.500 --> 00:05.500\nthird\n' > tests/data/$(1)-s2.vtt + $$(Q)printf '#EXTM3U\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="s",NAME="en",URI="$(1)-s.m3u8"\n#EXT-X-STREAM-INF:BANDWIDTH=64000,SUBTITLES="s"\n$(1)-a.m3u8\n' > $$@ + +fate-$(1)-demux: tests/data/$(1).m3u8 +fate-$(1)-demux: CLEANFILES = tests/data/$(1).m3u8 tests/data/$(1)-a.m3u8 tests/data/$(1)-s.m3u8 tests/data/$(1)-a[0-9]*.ts tests/data/$(1)-s[0-9]*.vtt +endef + +$(eval $(call HLS_WEBVTT_MAP_DATA,hls-webvtt-map)) +$(eval $(call HLS_WEBVTT_MAP_DATA,hls-webvtt-seek)) + +FATE_HLS_WEBVTT_MAP-$(call FILTERDEMDECENCMUX, AEVALSRC ARESAMPLE, HLS MPEGTS WEBVTT, MP2 PCM_F64LE, MP2FIXED, HLS MPEGTS NULL WEBVTT, LAVFI_INDEV MPEGAUDIO_PARSER PIPE_PROTOCOL) += fate-hls-webvtt-map-demux fate-hls-webvtt-seek-demux +FATE_FFMPEG += $(FATE_HLS_WEBVTT_MAP-yes) +fate-hls-webvtt-map-demux: CMD = fmtstdout webvtt -copyts -i $(TARGET_PATH)/tests/data/hls-webvtt-map.m3u8 -map 0:a -c copy -f null - -map 0:s -c copy +fate-hls-webvtt-seek-demux: CMD = fmtstdout webvtt -copyts -ss 2.9 -i $(TARGET_PATH)/tests/data/hls-webvtt-seek.m3u8 -map 0:s -c copy + tests/data/hls-self-variant.m3u8: TAG = GEN tests/data/hls-self-variant.m3u8: | tests/data $(Q)printf '#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=64000\nhls-self-variant.m3u8\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="s",NAME="en",URI="hls-self-variant-missing.m3u8"\n#EXTINF:2,\nhls-self-variant.vtt\n' > $@ diff --git a/tests/ref/fate/hls-webvtt-map-demux b/tests/ref/fate/hls-webvtt-map-demux new file mode 100644 index 0000000000..5ea461665c --- /dev/null +++ b/tests/ref/fate/hls-webvtt-map-demux @@ -0,0 +1 @@ +WEBVTT diff --git a/tests/ref/fate/hls-webvtt-seek-demux b/tests/ref/fate/hls-webvtt-seek-demux new file mode 100644 index 0000000000..5ea461665c --- /dev/null +++ b/tests/ref/fate/hls-webvtt-seek-demux @@ -0,0 +1 @@ +WEBVTT -- 2.54.0 From 20a862c6caeb39719e10269e0274dfacc15e7672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 17 Oct 2026 01:37:53 +0200 Subject: [PATCH 3/7] avformat/webvttdec: add an option to apply the X-TIMESTAMP-MAP header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HLS WebVTT segments carry an X-TIMESTAMP-MAP header that maps a cue time to an MPEG-TS time of the other renditions (RFC 8216, 3.5). With timestamp_map set, add the offset it gives to the cue timestamps. Skip the header when it comes in a block of its own instead of ending the parsing there. Signed-off-by: Kacper Michajłow <[email protected]> --- doc/demuxers.texi | 17 ++++++++++++++++ libavformat/webvttdec.c | 43 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 7610055193..f076710f04 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -1255,4 +1255,21 @@ color hint will be used, otherwise transparent black will be used for the background. Default value is false. @end table +@section webvtt + +WebVTT subtitle demuxer. + +It accepts the following options: + +@table @option +@item kind +Set the kind of the track: @samp{subtitles} (default), @samp{captions}, +@samp{descriptions} or @samp{metadata}. + +@item timestamp_map +Apply the @code{X-TIMESTAMP-MAP} header of HLS WebVTT segments to the cue +timestamps, which moves them onto the MPEG-TS timeline of the other +renditions. Default value is false. +@end table + @c man end DEMUXERS diff --git a/libavformat/webvttdec.c b/libavformat/webvttdec.c index 4ca1e939b1..6ec316fd06 100644 --- a/libavformat/webvttdec.c +++ b/libavformat/webvttdec.c @@ -36,6 +36,7 @@ typedef struct { const AVClass *class; FFDemuxSubtitlesQueue q; int kind; + int timestamp_map; } WebVTTContext; static int webvtt_probe(const AVProbeData *p) @@ -58,11 +59,40 @@ static int64_t read_ts(const char *s) return AV_NOPTS_VALUE; } +/* X-TIMESTAMP-MAP=LOCAL:<cue time>,MPEGTS:<90 kHz time>, in either order. + * Sets *offset to the value to add to the cue times, in milliseconds. */ +static int parse_timestamp_map(AVFormatContext *s, const char *header, + int64_t *offset) +{ + const char *p = strstr(header, "X-TIMESTAMP-MAP="); + int64_t local = AV_NOPTS_VALUE, mpegts = -1; + + if (!p) + return 0; + p += strlen("X-TIMESTAMP-MAP="); + while (*p && *p != '\r' && *p != '\n') { + if (!strncmp(p, "LOCAL:", 6)) + local = read_ts(p + 6); + else if (!strncmp(p, "MPEGTS:", 7)) + sscanf(p + 7, "%"SCNd64, &mpegts); + p += strcspn(p, ",\r\n"); + if (*p == ',') + p++; + } + if (local == AV_NOPTS_VALUE || mpegts < 0) { + av_log(s, AV_LOG_WARNING, "Invalid X-TIMESTAMP-MAP header\n"); + return 0; + } + *offset = av_rescale(mpegts, 1000, 90000) - local; + return 1; +} + static int webvtt_read_header(AVFormatContext *s) { WebVTTContext *webvtt = s->priv_data; AVBPrint cue; int res = 0; + int64_t ts_offset = 0; AVStream *st = avformat_new_stream(s, NULL); if (!st) @@ -92,10 +122,16 @@ static int webvtt_read_header(AVFormatContext *s) p = identifier = cue.str; pos = avio_tell(s->pb); - /* ignore header chunk */ + /* header chunk */ if (!strncmp(p, "\xEF\xBB\xBFWEBVTT", 9) || !strncmp(p, "WEBVTT", 6) || - !strncmp(p, "STYLE", 5) || + !strncmp(p, "X-TIMESTAMP-MAP", 15)) { + if (webvtt->timestamp_map) + parse_timestamp_map(s, p, &ts_offset); + continue; + } + + if (!strncmp(p, "STYLE", 5) || !strncmp(p, "REGION", 6) || !strncmp(p, "NOTE", 4)) continue; @@ -148,7 +184,7 @@ static int webvtt_read_header(AVFormatContext *s) goto end; } sub->pos = pos; - sub->pts = ts_start; + sub->pts = ts_start + ts_offset; sub->duration = ts_end - ts_start; #define SET_SIDE_DATA(name, type) do { \ @@ -203,6 +239,7 @@ static const AVOption options[] = { { "captions", "WebVTT captions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, INT_MIN, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" }, { "descriptions", "WebVTT descriptions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, INT_MIN, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" }, { "metadata", "WebVTT metadata kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, INT_MIN, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" }, + { "timestamp_map", "Apply the X-TIMESTAMP-MAP header to the cue timestamps", OFFSET(timestamp_map), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, KIND_FLAGS }, { NULL } }; -- 2.54.0 From 760db079127643b5ae269d05eaa95bae11165648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 17 Oct 2026 01:37:53 +0200 Subject: [PATCH 4/7] avformat/hls: apply the X-TIMESTAMP-MAP header of WebVTT segments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the timestamp_map option of the webvtt demuxer for the subtitle segments in place of prefer_hls_mpegts_pts, which no demuxer has, so the cue times land on the MPEG-TS timeline of the audio and video. Signed-off-by: Kacper Michajłow <[email protected]> --- libavformat/hls.c | 2 +- tests/ref/fate/hls-webvtt-map-demux | 18 ++++++++++++++++++ tests/ref/fate/hls-webvtt-seek-demux | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index 766f9a6274..c05e0d50dc 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -2568,7 +2568,7 @@ static int hls_read_header(AVFormatContext *s) highest_cur_seq_no = FFMAX(highest_cur_seq_no, pls->cur_seq_no); } - av_dict_set(&c->seg_format_opts, "prefer_hls_mpegts_pts", "1", 0); + av_dict_set(&c->seg_format_opts, "timestamp_map", "1", 0); /* Open the demuxer for each playlist */ for (i = 0; i < c->n_playlists; i++) { diff --git a/tests/ref/fate/hls-webvtt-map-demux b/tests/ref/fate/hls-webvtt-map-demux index 5ea461665c..b82bd1435f 100644 --- a/tests/ref/fate/hls-webvtt-map-demux +++ b/tests/ref/fate/hls-webvtt-map-demux @@ -1 +1,19 @@ WEBVTT + +00:10.500 --> 00:11.500 +first + +00:11.000 --> 00:14.500 line:1 +long + +00:11.000 --> 00:14.500 line:1 +long + +00:13.000 --> 00:13.900 align:start +second + +00:13.000 --> 00:16.500 line:1 +long + +00:14.500 --> 00:15.500 +third diff --git a/tests/ref/fate/hls-webvtt-seek-demux b/tests/ref/fate/hls-webvtt-seek-demux index 5ea461665c..3314f620ac 100644 --- a/tests/ref/fate/hls-webvtt-seek-demux +++ b/tests/ref/fate/hls-webvtt-seek-demux @@ -1 +1,10 @@ WEBVTT + +00:13.000 --> 00:13.900 align:start +second + +00:13.000 --> 00:16.500 line:1 +long + +00:14.500 --> 00:15.500 +third -- 2.54.0 From a357b1d49cb0edc22bad457cf59ccf14f7645ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 17 Oct 2026 01:37:53 +0200 Subject: [PATCH 5/7] avformat/hls: keep subtitle playlists on the shared timeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cue times of a subtitle playlist are on the MPEG-TS timeline, and the first cue of a segment says nothing about where the segment starts, so use an offset of 0 instead of deriving one from the first cue, which made a seek skip the cues within that offset. Signed-off-by: Kacper Michajłow <[email protected]> --- libavformat/hls.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index c05e0d50dc..985f2e2928 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -2922,7 +2922,11 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) fill_timing_for_id3_timestamped_stream(pls); } - if (pls->pkt->dts != AV_NOPTS_VALUE && + if (pls->is_subtitle) { + /* the cues are on the shared timeline */ + if (pls->ts_offset == AV_NOPTS_VALUE) + pls->ts_offset = 0; + } else if (pls->pkt->dts != AV_NOPTS_VALUE && (pls->ts_offset == AV_NOPTS_VALUE || (!c->first_timestamp_locked && c->first_timestamp_pls == pls && -- 2.54.0 From 0308ef236142933d7e55c0a72b38c1d862c9f9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 17 Oct 2026 01:37:54 +0200 Subject: [PATCH 6/7] avformat/hls: keep the subtitle cues still shown at the seek target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discard the cues of a subtitle playlist by their end time when seeking or when the playlist is enabled during playback, so a cue that is on screen at the target is delivered instead of the next one starting after it. Signed-off-by: Kacper Michajłow <[email protected]> --- libavformat/hls.c | 8 +++++++- tests/ref/fate/hls-webvtt-seek-demux | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index 985f2e2928..7177d6faba 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -2994,8 +2994,14 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) break; } + int64_t pkt_ts = pls->pkt->dts; + + /* a cue stays on screen until its end */ + if (pls->is_subtitle && pls->pkt->duration > 0) + pkt_ts += pls->pkt->duration; + tb = get_timebase(pls); - ts_diff = av_rescale_q_rnd(pls->pkt->dts, tb, + ts_diff = av_rescale_q_rnd(pkt_ts, tb, AV_TIME_BASE_Q, AV_ROUND_DOWN) - pls->seek_timestamp; if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY || diff --git a/tests/ref/fate/hls-webvtt-seek-demux b/tests/ref/fate/hls-webvtt-seek-demux index 3314f620ac..cfd97a413b 100644 --- a/tests/ref/fate/hls-webvtt-seek-demux +++ b/tests/ref/fate/hls-webvtt-seek-demux @@ -1,5 +1,8 @@ WEBVTT +00:11.000 --> 00:14.500 line:1 +long + 00:13.000 --> 00:13.900 align:start second -- 2.54.0 From 9f74bcaf6c8e5eecf92a49357adb7cbd41731c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 17 Oct 2026 01:37:54 +0200 Subject: [PATCH 7/7] avformat/hls: drop WebVTT cues repeated across segment boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A WebVTT segment contains every cue shown during its period (RFC 8216, 3.5), so a cue that reaches into the next segment appears in both. Keep the cues of the previous segment and drop the ones the current segment repeats, so each cue is delivered once. Signed-off-by: Kacper Michajłow <[email protected]> --- libavformat/hls.c | 77 ++++++++++++++++++++++++++-- tests/ref/fate/hls-webvtt-map-demux | 6 --- tests/ref/fate/hls-webvtt-seek-demux | 3 -- 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index 7177d6faba..a9c87ca6c5 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -117,6 +117,10 @@ struct playlist { int index; AVFormatContext *ctx; AVPacket *pkt; + AVPacket **prev_cues; + int n_prev_cues; + AVPacket **cur_cues; + int n_cur_cues; int has_noheader_flag; /* main demuxer streams associated with this playlist @@ -313,6 +317,20 @@ static void free_init_section_list(struct playlist *pls) pls->n_init_sections = 0; } +static void free_cue_list(AVPacket ***cues, int *n_cues) +{ + for (int i = 0; i < *n_cues; i++) + av_packet_free(&(*cues)[i]); + av_freep(cues); + *n_cues = 0; +} + +static void free_subtitle_cues(struct playlist *pls) +{ + free_cue_list(&pls->prev_cues, &pls->n_prev_cues); + free_cue_list(&pls->cur_cues, &pls->n_cur_cues); +} + static void free_playlist_list(HLSContext *c) { int i; @@ -320,6 +338,7 @@ static void free_playlist_list(HLSContext *c) struct playlist *pls = c->playlists[i]; free_segment_list(pls); free_init_section_list(pls); + free_subtitle_cues(pls); av_freep(&pls->main_streams); av_freep(&pls->renditions); av_freep(&pls->id3_buf); @@ -2087,6 +2106,38 @@ static int init_subtitle_context(struct playlist *pls) return ret; } +static int cue_repeated(const struct playlist *v, const AVPacket *pkt) +{ + for (int i = 0; i < v->n_prev_cues; i++) { + const AVPacket *prev = v->prev_cues[i]; + const uint8_t *settings, *prev_settings; + size_t size, prev_size; + + if (prev->pts != pkt->pts || prev->duration != pkt->duration || + prev->size != pkt->size || memcmp(prev->data, pkt->data, pkt->size)) + continue; + settings = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_SETTINGS, &size); + prev_settings = av_packet_get_side_data(prev, AV_PKT_DATA_WEBVTT_SETTINGS, + &prev_size); + if (size == prev_size && (!size || !memcmp(settings, prev_settings, size))) + return 1; + } + return 0; +} + +static int remember_cue(struct playlist *v, const AVPacket *pkt) +{ + AVPacket *cue = av_packet_clone(pkt); + + if (!cue) + return AVERROR(ENOMEM); + if (av_dynarray_add_nofree(&v->cur_cues, &v->n_cur_cues, cue) < 0) { + av_packet_free(&cue); + return AVERROR(ENOMEM); + } + return 0; +} + static int read_subtitle_packet(struct playlist *v, AVPacket *pkt) { HLSContext *c = v->parent->priv_data; @@ -2106,9 +2157,17 @@ restart: return ret; } - ret = av_read_frame(v->ctx, v->pkt); - if (!ret) { - return ret; + while (!(ret = av_read_frame(v->ctx, v->pkt))) { + int repeated = cue_repeated(v, v->pkt); + + ret = remember_cue(v, v->pkt); + if (ret < 0) { + av_packet_unref(v->pkt); + return ret; + } + if (!repeated) + return 0; + av_packet_unref(v->pkt); } ff_format_io_close(v->parent, &v->input); v->cur_seq_no++; @@ -2116,6 +2175,10 @@ restart: avformat_close_input(&v->ctx); + free_cue_list(&v->prev_cues, &v->n_prev_cues); + FFSWAP(AVPacket **, v->prev_cues, v->cur_cues); + FFSWAP(int, v->n_prev_cues, v->n_cur_cues); + goto restart; } @@ -2814,8 +2877,10 @@ static int recheck_discard_flags(AVFormatContext *s, int first) pls->input_read_done = 0; ff_format_io_close(pls->parent, &pls->input_next); pls->input_next_requested = 0; - if (pls->is_subtitle) + if (pls->is_subtitle) { avformat_close_input(&pls->ctx); + free_subtitle_cues(pls); + } pls->needed = 0; changed = 1; av_log(s, AV_LOG_INFO, "No longer receiving playlist %d\n", i); @@ -3222,8 +3287,10 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, for (j = 0; j < (int)pls->ctx->nb_streams; j++) ffstream(pls->ctx->streams[j])->pts_wrap_reference = AV_NOPTS_VALUE; } - if (pls->is_subtitle) + if (pls->is_subtitle) { avformat_close_input(&pls->ctx); + free_subtitle_cues(pls); + } /* Reset the init segment so it's re-fetched and served appropriately */ pls->cur_init_section = NULL; diff --git a/tests/ref/fate/hls-webvtt-map-demux b/tests/ref/fate/hls-webvtt-map-demux index b82bd1435f..284413facf 100644 --- a/tests/ref/fate/hls-webvtt-map-demux +++ b/tests/ref/fate/hls-webvtt-map-demux @@ -6,14 +6,8 @@ first 00:11.000 --> 00:14.500 line:1 long -00:11.000 --> 00:14.500 line:1 -long - 00:13.000 --> 00:13.900 align:start second -00:13.000 --> 00:16.500 line:1 -long - 00:14.500 --> 00:15.500 third diff --git a/tests/ref/fate/hls-webvtt-seek-demux b/tests/ref/fate/hls-webvtt-seek-demux index cfd97a413b..12af5c285e 100644 --- a/tests/ref/fate/hls-webvtt-seek-demux +++ b/tests/ref/fate/hls-webvtt-seek-demux @@ -6,8 +6,5 @@ long 00:13.000 --> 00:13.900 align:start second -00:13.000 --> 00:16.500 line:1 -long - 00:14.500 --> 00:15.500 third -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
