PR #25842 opened by Romain Beauxis (toots)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25842
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25842.patch

Since 843e83ed2d, every seek moves back by the audio pre-roll, so a seek on 
video lands one keyframe earlier. That suits a caller that decodes, but a 
caller that copies the packets keeps the extra GOP, as with a concat `inpoint`.

This series adds `AVSEEK_FLAG_PREROLL`, and a `seek2preroll` option like 
`seek2any`, for callers that decode. Without it, a seek lands where it asked 
again, and only the priming is discarded. The pre-roll tests opt in when the 
flag is added, concat stops applying the pre-roll twice, and ffplay sets the 
option last.

The first commit adds a test with the current output, and the later commits 
update it. For 9.1 we revert the pre-roll commits instead.

Fixes #25672.


>From 9101d923808468df531b9757f79b490c0064bd3f Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 7 Nov 2026 09:32:45 -0600
Subject: [PATCH 1/4] tests/fate/preroll: check a concat inpoint on a video
 keyframe

An inpoint on a keyframe of a Matroska file with AAC must start the
cut on that keyframe.

The reference shows the current behaviour, which starts on the
keyframe before.
---
 tests/fate/preroll.mak                    | 4 ++++
 tests/ref/fate/preroll-aac-concat-inpoint | 4 ++++
 2 files changed, 8 insertions(+)
 create mode 100644 tests/ref/fate/preroll-aac-concat-inpoint

diff --git a/tests/fate/preroll.mak b/tests/fate/preroll.mak
index 243b9d07317..8af2aeb9b71 100644
--- a/tests/fate/preroll.mak
+++ b/tests/fate/preroll.mak
@@ -66,6 +66,10 @@ FATE_PREROLL_SEEK-$(call ALLYES, CONCAT_DEMUXER MOV_DEMUXER 
AAC_DECODER FILE_PRO
 fate-preroll-aac-concat-seek: 
tests/data/samples/gapless/102400samples_qt-lc-aac.m4a
 fate-preroll-aac-concat-seek: CMD = seek_concat 
$(SRC_PATH)/tests/preroll.ffconcat 
../samples/gapless_102400samples_qt-lc-aac.m4a -seekback 530000 -frames 3 
-duration 3
 
+# An inpoint on a video keyframe: the cut starts at that keyframe.
+FATE_PREROLL_FFPROBE-$(call ALLYES, CONCAT_DEMUXER MATROSKA_DEMUXER 
H264_DECODER AAC_DECODER FILE_PROTOCOL PIPE_PROTOCOL) += 
fate-preroll-aac-concat-inpoint
+fate-preroll-aac-concat-inpoint: CMD = printf "ffconcat version 1.0\nfile 
file:%s\ninpoint 3\n" $(TARGET_SAMPLES)/mkv/test7_cut.mkv | run 
ffprobe$(PROGSSUF)$(EXESUF) -protocol_whitelist pipe,file -safe 0 -f concat -i 
pipe: -read_intervals %+\#4 -show_entries 
packet=stream_index,pts,flags:packet_side_data=skip_samples -print_format 
compact
+
 # A seek close to the start of a stream whose start time is unknown.
 FATE_PREROLL_SEEK-$(call DEMDEC, AAC, AAC) += fate-preroll-aac-adts-seek-start
 fate-preroll-aac-adts-seek-start: CMD = run libavformat/tests/seek$(EXESUF) 
$(TARGET_SAMPLES)/aac/id3v2.aac -seekback 10000 -frames 2 -duration 3
diff --git a/tests/ref/fate/preroll-aac-concat-inpoint 
b/tests/ref/fate/preroll-aac-concat-inpoint
new file mode 100644
index 00000000000..e9f9d75572a
--- /dev/null
+++ b/tests/ref/fate/preroll-aac-concat-inpoint
@@ -0,0 +1,4 @@
+packet|stream_index=0|pts=-1000|flags=K__
+packet|stream_index=1|pts=-986|flags=K__|side_datum/skip_samples:skip_samples=1024
+packet|stream_index=1|pts=-965|flags=K__
+packet|stream_index=1|pts=-944|flags=K__
-- 
2.54.0


>From 87e905ae49627b9cf34c617c2a3c7963b598c879 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 7 Nov 2026 11:40:09 -0600
Subject: [PATCH 2/4] avformat: add AVSEEK_FLAG_PREROLL

A caller that decodes after a seek needs the audio pre-roll before
the target, while a caller that copies the packets needs the seek to
land where it asked.

Add AVSEEK_FLAG_PREROLL, and the seek2preroll option to set it on
every seek, for the caller to say so.

The pre-roll tests set it, as the pre-roll is still left after
every seek.
---
 doc/APIchanges              |  4 ++++
 libavformat/avformat.h      |  8 ++++++++
 libavformat/options_table.h |  1 +
 libavformat/seek.c          |  2 ++
 libavformat/version.h       |  2 +-
 tests/fate/preroll.mak      | 23 ++++++++++++-----------
 6 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 36f95c8df2b..76398921963 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,10 @@ The last version increases of all libraries were on 2026-06-23.
 
 API changes, most recent first:
 
+2026-11-xx - xxxxxxxxxx - lavf 63.19.100 - avformat.h
+  Add AVSEEK_FLAG_PREROLL and AVFormatContext.seek2preroll, for
+  avformat_seek_file() to leave the audio pre-roll before the target.
+
 2026-11-xx - xxxxxxxxxx - lavc 63.26.100 - defs.h
   Add AV_PROFILE_CAVS_JIZHUN and AV_PROFILE_CAVS_BROADCASTING.
 
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 679d15676c5..9f6d475f7db 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -2056,6 +2056,13 @@ typedef struct AVFormatContext {
      * - demuxing: Set by user
      */
     int recursion_limit;
+
+    /**
+     * Add AVSEEK_FLAG_PREROLL to every avformat_seek_file().
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    int seek2preroll;
 } AVFormatContext;
 
 /**
@@ -2731,6 +2738,7 @@ void avformat_close_input(AVFormatContext **s);
 #define AVSEEK_FLAG_BYTE     2 ///< seeking based on position in bytes
 #define AVSEEK_FLAG_ANY      4 ///< seek to any frame, even non-keyframes
 #define AVSEEK_FLAG_FRAME    8 ///< seeking based on frame number
+#define AVSEEK_FLAG_PREROLL 16 ///< leave the audio pre-roll before the 
target, see avformat_seek_file()
 
 /**
  * @addtogroup lavf_encoding
diff --git a/libavformat/options_table.h b/libavformat/options_table.h
index 537870c5c99..541645bf98f 100644
--- a/libavformat/options_table.h
+++ b/libavformat/options_table.h
@@ -56,6 +56,7 @@ static const AVOption avformat_options[] = {
 {"legacy_id3v2_comm_keys", "also export id3v2 COMM descriptors as bare 
metadata keys", 0, AV_OPT_TYPE_CONST, { .i64 = 
AVFMT_FLAG_LEGACY_ID3V2_COMM_KEYS }, 0, 0, D|AV_OPT_FLAG_DEPRECATED, .unit = 
"fflags" },
 #endif
 {"seek2any", "allow seeking to non-keyframes on demuxer level when supported", 
OFFSET(seek2any), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, D},
+{"seek2preroll", "leave the audio pre-roll before the target of a seek, for 
decoding", OFFSET(seek2preroll), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, D},
 {"analyzeduration", "specify how many microseconds are analyzed to probe the 
input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, 
(double)INT64_MAX, D},
 {"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 
0, 0, D},
 {"indexmem", "max memory used for timestamp index (per stream)", 
OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D},
diff --git a/libavformat/seek.c b/libavformat/seek.c
index 4a09d013353..93d63387e95 100644
--- a/libavformat/seek.c
+++ b/libavformat/seek.c
@@ -767,6 +767,8 @@ int avformat_seek_file(AVFormatContext *s, int 
stream_index, int64_t min_ts,
 
     if (s->seek2any > 0)
         flags |= AVSEEK_FLAG_ANY;
+    if (s->seek2preroll > 0)
+        flags |= AVSEEK_FLAG_PREROLL;
     flags &= ~AVSEEK_FLAG_BACKWARD;
 
     if (!(flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))) {
diff --git a/libavformat/version.h b/libavformat/version.h
index 6a80f3ac4e6..e063e12b983 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@
 
 #include "version_major.h"
 
-#define LIBAVFORMAT_VERSION_MINOR  18
+#define LIBAVFORMAT_VERSION_MINOR  19
 #define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
diff --git a/tests/fate/preroll.mak b/tests/fate/preroll.mak
index 8af2aeb9b71..9fde7ed40ee 100644
--- a/tests/fate/preroll.mak
+++ b/tests/fate/preroll.mak
@@ -8,16 +8,17 @@
 # bit-exact across platforms.
 
 PREROLL_SEEK_TS = 0.53
+PREROLL_OPT = -seek2preroll 1
 
 # 1: test name, 2: demuxer, 3: decoder, 4: sample
 define FATE_PREROLL_TEST
 FATE_PREROLL_FFPROBE-$(call DEMDEC, $(2), $(3)) += fate-preroll-$(1)-declared 
fate-preroll-$(1)-landing
 fate-preroll-$(1)-declared: CMD = run ffprobe$(PROGSSUF)$(EXESUF) 
-select_streams a:0 -show_entries 
stream=codec_name,sample_rate,initial_padding,seek_preroll -print_format 
compact $(TARGET_SAMPLES)/$(4)
-fate-preroll-$(1)-landing: CMD = run ffprobe$(PROGSSUF)$(EXESUF) 
-select_streams a:0 -read_intervals $(PREROLL_SEEK_TS)%+\#3 -show_entries 
packet=pts,dts,duration -print_format compact $(TARGET_SAMPLES)/$(4)
+fate-preroll-$(1)-landing: CMD = run ffprobe$(PROGSSUF)$(EXESUF) 
$(PREROLL_OPT) -select_streams a:0 -read_intervals $(PREROLL_SEEK_TS)%+\#3 
-show_entries packet=pts,dts,duration -print_format compact 
$(TARGET_SAMPLES)/$(4)
 FATE_PREROLL_FFMPEG-$(call FRAMECRC, $(2), $(3), PCM_S16LE_ENCODER 
ARESAMPLE_FILTER VOLUME_FILTER) += fate-preroll-$(1)-decode
-fate-preroll-$(1)-decode: CMD = framecrc -ss $(PREROLL_SEEK_TS) -t 0.15 -i 
$(TARGET_SAMPLES)/$(4) -map 0:a:0 -af volume=0,aresample -c:a pcm_s16le
+fate-preroll-$(1)-decode: CMD = framecrc $(PREROLL_OPT) -ss $(PREROLL_SEEK_TS) 
-t 0.15 -i $(TARGET_SAMPLES)/$(4) -map 0:a:0 -af volume=0,aresample -c:a 
pcm_s16le
 FATE_PREROLL_FFMPEG-$(call FRAMECRC, $(2), $(3), PCM_S16LE_ENCODER 
ARESAMPLE_FILTER VOLUME_FILTER) += fate-preroll-$(1)-decode-noaccurate
-fate-preroll-$(1)-decode-noaccurate: CMD = framecrc -noaccurate_seek -ss 
$(PREROLL_SEEK_TS) -t 0.15 -i $(TARGET_SAMPLES)/$(4) -map 0:a:0 -af 
volume=0,aresample -c:a pcm_s16le
+fate-preroll-$(1)-decode-noaccurate: CMD = framecrc $(PREROLL_OPT) 
-noaccurate_seek -ss $(PREROLL_SEEK_TS) -t 0.15 -i $(TARGET_SAMPLES)/$(4) -map 
0:a:0 -af volume=0,aresample -c:a pcm_s16le
 endef
 
 $(eval $(call 
FATE_PREROLL_TEST,aac-mov,MOV,AAC,gapless/102400samples_qt-lc-aac.m4a))
@@ -37,34 +38,34 @@ $(eval $(call 
FATE_PREROLL_TEST,mp3-itunes,MP3,MP3FLOAT,gapless/gapless-itunes.m
 # A seek that lands inside the priming, which here spans the first three
 # packets: the rest of the priming, or the pre-roll if longer, is skipped.
 FATE_PREROLL_FFMPEG-$(call FRAMECRC, CAF, AAC, PCM_S16LE_ENCODER 
ARESAMPLE_FILTER VOLUME_FILTER) += fate-preroll-aac-caf-decode-in-priming
-fate-preroll-aac-caf-decode-in-priming: CMD = framecrc -noaccurate_seek -ss 
0.01 -t 0.1 -i $(TARGET_SAMPLES)/caf/aac.caf -af volume=0,aresample -c:a 
pcm_s16le
+fate-preroll-aac-caf-decode-in-priming: CMD = framecrc $(PREROLL_OPT) 
-noaccurate_seek -ss 0.01 -t 0.1 -i $(TARGET_SAMPLES)/caf/aac.caf -af 
volume=0,aresample -c:a pcm_s16le
 
 # A forward seek, as ffplay does: the range starts before the target and is
 # open at the end.
 FATE_PREROLL_SEEK-$(call DEMDEC, MOV, AAC) += fate-preroll-aac-mov-seek-forward
-fate-preroll-aac-mov-seek-forward: CMD = run libavformat/tests/seek$(EXESUF) 
$(TARGET_SAMPLES)/gapless/102400samples_qt-lc-aac.m4a -seekforw 530000 -seekmin 
400000 -frames 2 -duration 3
+fate-preroll-aac-mov-seek-forward: CMD = run libavformat/tests/seek$(EXESUF) 
$(TARGET_SAMPLES)/gapless/102400samples_qt-lc-aac.m4a -seekforw 530000 -seekmin 
400000 -frames 2 -duration 3 $(PREROLL_OPT)
 
 # A seek past the end lands on the last packet, which carries the padding
 # to discard at the end of the stream.
 FATE_PREROLL_FFPROBE-$(call DEMDEC, CAF, AAC) += 
fate-preroll-aac-caf-landing-end
-fate-preroll-aac-caf-landing-end: CMD = run ffprobe$(PROGSSUF)$(EXESUF) 
-select_streams a:0 -read_intervals 5.45%+\#2 -show_entries 
packet=pts,duration:packet_side_data=skip_samples,discard_padding -print_format 
compact $(TARGET_SAMPLES)/caf/aac.caf
+fate-preroll-aac-caf-landing-end: CMD = run ffprobe$(PROGSSUF)$(EXESUF) 
$(PREROLL_OPT) -select_streams a:0 -read_intervals 5.45%+\#2 -show_entries 
packet=pts,duration:packet_side_data=skip_samples,discard_padding -print_format 
compact $(TARGET_SAMPLES)/caf/aac.caf
 FATE_PREROLL_FFPROBE-$(call DEMDEC, MATROSKA, OPUS) += 
fate-preroll-opus-mkv-landing-end
-fate-preroll-opus-mkv-landing-end: CMD = run ffprobe$(PROGSSUF)$(EXESUF) 
-select_streams a:0 -read_intervals 1.1%+\#2 -show_entries 
packet=pts,duration:packet_side_data=skip_samples,discard_padding -print_format 
compact $(TARGET_SAMPLES)/mkv/codec_delay_opus.mkv
+fate-preroll-opus-mkv-landing-end: CMD = run ffprobe$(PROGSSUF)$(EXESUF) 
$(PREROLL_OPT) -select_streams a:0 -read_intervals 1.1%+\#2 -show_entries 
packet=pts,duration:packet_side_data=skip_samples,discard_padding -print_format 
compact $(TARGET_SAMPLES)/mkv/codec_delay_opus.mkv
 
 # A seek on the video stream, just after a keyframe: the audio pre-roll
 # still needs room.
 FATE_PREROLL_SEEK-$(call DEMDEC, MOV, AAC H264) += 
fate-preroll-aac-mov-seek-video
-fate-preroll-aac-mov-seek-video: CMD = run libavformat/tests/seek$(EXESUF) 
$(TARGET_SAMPLES)/h264/twofields_packet.mp4 -stream_id 0 -seekback 182000 
-frames 4 -duration 3
+fate-preroll-aac-mov-seek-video: CMD = run libavformat/tests/seek$(EXESUF) 
$(TARGET_SAMPLES)/h264/twofields_packet.mp4 -stream_id 0 -seekback 182000 
-frames 4 -duration 3 $(PREROLL_OPT)
 
 # A seek on the stream that starts later, close enough to its start that
 # the seek targets it: the other stream is already past its own start.
 FATE_PREROLL_SEEK-$(call ALLYES, MOV_DEMUXER MOV_MUXER AAC_DECODER 
FILE_PROTOCOL) += fate-preroll-aac-mov-seek-staggered
-fate-preroll-aac-mov-seek-staggered: CMD = seek_staggered 
$(TARGET_SAMPLES)/gapless/102400samples_qt-lc-aac.m4a 1 -stream_id 1 -seekback 
42500 -frames 4 -duration 3
+fate-preroll-aac-mov-seek-staggered: CMD = seek_staggered 
$(TARGET_SAMPLES)/gapless/102400samples_qt-lc-aac.m4a 1 -stream_id 1 -seekback 
42500 -frames 4 -duration 3 $(PREROLL_OPT)
 
 # A seek through the concat demuxer, which seeks in the file it reads.
 FATE_PREROLL_SEEK-$(call ALLYES, CONCAT_DEMUXER MOV_DEMUXER AAC_DECODER 
FILE_PROTOCOL) += fate-preroll-aac-concat-seek
 fate-preroll-aac-concat-seek: 
tests/data/samples/gapless/102400samples_qt-lc-aac.m4a
-fate-preroll-aac-concat-seek: CMD = seek_concat 
$(SRC_PATH)/tests/preroll.ffconcat 
../samples/gapless_102400samples_qt-lc-aac.m4a -seekback 530000 -frames 3 
-duration 3
+fate-preroll-aac-concat-seek: CMD = seek_concat 
$(SRC_PATH)/tests/preroll.ffconcat 
../samples/gapless_102400samples_qt-lc-aac.m4a -seekback 530000 -frames 3 
-duration 3 $(PREROLL_OPT)
 
 # An inpoint on a video keyframe: the cut starts at that keyframe.
 FATE_PREROLL_FFPROBE-$(call ALLYES, CONCAT_DEMUXER MATROSKA_DEMUXER 
H264_DECODER AAC_DECODER FILE_PROTOCOL PIPE_PROTOCOL) += 
fate-preroll-aac-concat-inpoint
@@ -72,7 +73,7 @@ fate-preroll-aac-concat-inpoint: CMD = printf "ffconcat 
version 1.0\nfile file:%
 
 # A seek close to the start of a stream whose start time is unknown.
 FATE_PREROLL_SEEK-$(call DEMDEC, AAC, AAC) += fate-preroll-aac-adts-seek-start
-fate-preroll-aac-adts-seek-start: CMD = run libavformat/tests/seek$(EXESUF) 
$(TARGET_SAMPLES)/aac/id3v2.aac -seekback 10000 -frames 2 -duration 3
+fate-preroll-aac-adts-seek-start: CMD = run libavformat/tests/seek$(EXESUF) 
$(TARGET_SAMPLES)/aac/id3v2.aac -seekback 10000 -frames 2 -duration 3 
$(PREROLL_OPT)
 
 $(FATE_PREROLL_SEEK-yes): libavformat/tests/seek$(EXESUF)
 
-- 
2.54.0


>From 6a631e1af0c3e7e981da5bf6479325e3b07eadef Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 7 Nov 2026 11:49:40 -0600
Subject: [PATCH 3/4] avformat/seek: leave the audio pre-roll only with
 AVSEEK_FLAG_PREROLL

Moving the target back by the audio pre-roll lands a seek on the video
stream one keyframe earlier, which a caller that copies the packets
keeps, as with a concat inpoint (#25672).

Move the target back and discard the pre-roll only with the flag, and
never pass it to the demuxer, so that a demuxer seeking through another
one, as concat does, does not move the target back twice.
---
 doc/APIchanges                                |  4 +++
 libavformat/avformat.h                        | 26 ++++++++------
 libavformat/demux.c                           |  3 +-
 libavformat/internal.h                        | 12 +++++--
 libavformat/seek.c                            | 13 +++++--
 libavformat/version.h                         |  2 +-
 .../fate/aac-seek-landing-frame-length-short  |  2 +-
 tests/ref/fate/aac-seek-landing-sbr           |  4 +--
 .../fate/mov-aac-2048-priming-seek-landing    |  2 +-
 tests/ref/fate/preroll-aac-concat-inpoint     |  8 ++---
 tests/ref/fate/preroll-aac-concat-seek        | 36 +++++++++----------
 tests/ref/seek/mkv-codec-delay                | 26 +++++++-------
 12 files changed, 80 insertions(+), 58 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 76398921963..06597c172e5 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,10 @@ The last version increases of all libraries were on 2026-06-23.
 
 API changes, most recent first:
 
+2026-11-xx - xxxxxxxxxx - lavf 63.19.101 - avformat.h
+  avformat_seek_file() leaves the audio pre-roll before the target, and
+  discards it after the seek, only with AVSEEK_FLAG_PREROLL.
+
 2026-11-xx - xxxxxxxxxx - lavf 63.19.100 - avformat.h
   Add AVSEEK_FLAG_PREROLL and AVFormatContext.seek2preroll, for
   avformat_seek_file() to leave the audio pre-roll before the target.
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 9f6d475f7db..a90081d3bda 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -2571,21 +2571,25 @@ int av_seek_frame(AVFormatContext *s, int stream_index, 
int64_t timestamp,
  * keyframes (this may not be supported by all demuxers).
  * If flags contain AVSEEK_FLAG_BACKWARD, it is ignored.
  *
- * A seek by timestamp moves ts back by the largest
+ * If flags contain AVSEEK_FLAG_PREROLL, or AVFormatContext.seek2preroll is
+ * set, a seek by timestamp moves ts back by the largest
  * AVCodecParameters.seek_preroll of the audio streams that are not discarded,
  * whatever stream_index is, so that the decoders get their declared pre-roll
- * before ts. The target stays within min_ts and after the start of the
- * stream, so less room may be left. max_ts moves back as much, unless it is
- * INT64_MAX. If that seek fails, the seek is done to ts. This also applies to
- * av_seek_frame() with a demuxer that only implements this function.
+ * before ts. This is meant for a caller that decodes: the seek may land one
+ * keyframe earlier, which a caller that copies the packets would keep. The
+ * target stays within min_ts and after the start of the stream, so less room
+ * may be left. max_ts moves back as much, unless it is INT64_MAX. If that
+ * seek fails, the seek is done to ts. This also applies to av_seek_frame()
+ * with a demuxer that only implements this function.
  *
  * The first packet read after a seek from an audio stream carries
- * AV_PKT_DATA_SKIP_SAMPLES side data to discard the larger of the rest of the
- * priming (AVCodecParameters.initial_padding) and the seek pre-roll. The
- * discard supplies the stream's declared pre-roll; recovery beyond that
- * allowance depends on the codec and bitstream. Only the priming is discarded
- * on the first packet of the stream, and no pre-roll after the priming of a
- * stream whose start the seek targets.
+ * AV_PKT_DATA_SKIP_SAMPLES side data to discard the rest of the priming
+ * (AVCodecParameters.initial_padding) if the seek landed inside it. With
+ * AVSEEK_FLAG_PREROLL, it discards at least the seek pre-roll. The discard
+ * supplies the stream's declared pre-roll; recovery beyond that allowance
+ * depends on the codec and bitstream. Only the priming is discarded on the
+ * first packet of the stream, and no pre-roll after the priming of a stream
+ * whose start the seek targets.
  *
  * @param s            media file handle
  * @param stream_index index of the stream which is used as time base reference
diff --git a/libavformat/demux.c b/libavformat/demux.c
index 1468726b096..581a5c24959 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -1583,7 +1583,8 @@ static int read_frame_internal(AVFormatContext *s, 
AVPacket *pkt)
                 if (first) {
                     if (par->initial_padding > 0)
                         skip = par->initial_padding;
-                } else if (!(sti->seek_to_start && pkt->pts != AV_NOPTS_VALUE 
&& !in_priming)) {
+                } else if (sti->seek_preroll &&
+                           !(sti->seek_to_start && pkt->pts != AV_NOPTS_VALUE 
&& !in_priming)) {
                     // Past the first packet, the decoder also misses what
                     // came before.
                     skip = FFMAX(skip, par->seek_preroll);
diff --git a/libavformat/internal.h b/libavformat/internal.h
index 4db9db85ce5..a175828718d 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -245,12 +245,18 @@ typedef struct FFStream {
 
     /**
      * Set by a seek, cleared on the next packet of the stream, which gets in
-     * skip_samples the rest of the priming if it is inside it, and unless it
-     * is the first packet of the stream, at least the seek pre-roll of the
-     * codec. After the priming, a seek to the start of the stream gets none.
+     * skip_samples the rest of the priming if it is inside it, and when
+     * seek_preroll is set and it is not the first packet of the stream, at
+     * least the seek pre-roll of the codec. After the priming, a seek to the
+     * start of the stream gets none.
      */
     int seek_discard;
 
+    /**
+     * Set with seek_discard when the seek had AVSEEK_FLAG_PREROLL.
+     */
+    int seek_preroll;
+
     /**
      * Set with seek_discard when the target of the seek is at or before the
      * start of this stream.
diff --git a/libavformat/seek.c b/libavformat/seek.c
index 93d63387e95..4c1c832e382 100644
--- a/libavformat/seek.c
+++ b/libavformat/seek.c
@@ -656,7 +656,8 @@ int av_seek_frame(AVFormatContext *s, int stream_index,
                                   flags & ~AVSEEK_FLAG_BACKWARD);
     }
 
-    ret = seek_frame_internal(s, stream_index, timestamp, flags);
+    ret = seek_frame_internal(s, stream_index, timestamp,
+                              flags & ~AVSEEK_FLAG_PREROLL);
 
     if (ret >= 0)
         ret = avformat_queue_attached_pictures(s);
@@ -712,6 +713,7 @@ static void request_seek_discard(AVFormatContext *s, int 
stream_index,
         FFStream *const sti = ffstream(st);
 
         sti->seek_discard  = 1;
+        sti->seek_preroll  = !!(flags & AVSEEK_FLAG_PREROLL);
         sti->seek_to_start = !(flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME)) 
&&
                              st->start_time != AV_NOPTS_VALUE &&
                              av_compare_ts(ts, time_base, st->start_time,
@@ -758,6 +760,7 @@ int avformat_seek_file(AVFormatContext *s, int 
stream_index, int64_t min_ts,
                        int64_t ts, int64_t max_ts, int flags)
 {
     int64_t target = ts, target_max = max_ts;
+    int preroll_flag;
     int ret;
 
     if (min_ts > ts || max_ts < ts)
@@ -770,8 +773,12 @@ int avformat_seek_file(AVFormatContext *s, int 
stream_index, int64_t min_ts,
     if (s->seek2preroll > 0)
         flags |= AVSEEK_FLAG_PREROLL;
     flags &= ~AVSEEK_FLAG_BACKWARD;
+    preroll_flag = flags & AVSEEK_FLAG_PREROLL;
+    // A demuxer that seeks through another one, as concat does, must not
+    // move the target back a second time.
+    flags &= ~AVSEEK_FLAG_PREROLL;
 
-    if (!(flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))) {
+    if (preroll_flag && !(flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))) {
         int64_t preroll = seek_preroll(s, stream_index);
         int64_t start = stream_index >= 0 ? 
s->streams[stream_index]->start_time
                                           : s->start_time;
@@ -795,7 +802,7 @@ int avformat_seek_file(AVFormatContext *s, int 
stream_index, int64_t min_ts,
         ret = seek_file(s, stream_index, min_ts, ts, max_ts, flags);
     }
     if (ret >= 0)
-        request_seek_discard(s, stream_index, target, flags);
+        request_seek_discard(s, stream_index, target, flags | preroll_flag);
     return ret;
 }
 
diff --git a/libavformat/version.h b/libavformat/version.h
index e063e12b983..bbb2fc7d871 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,7 +32,7 @@
 #include "version_major.h"
 
 #define LIBAVFORMAT_VERSION_MINOR  19
-#define LIBAVFORMAT_VERSION_MICRO 100
+#define LIBAVFORMAT_VERSION_MICRO 101
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \
diff --git a/tests/ref/fate/aac-seek-landing-frame-length-short 
b/tests/ref/fate/aac-seek-landing-frame-length-short
index ecb9fe031fb..fd28ed6ee4b 100644
--- a/tests/ref/fate/aac-seek-landing-frame-length-short
+++ b/tests/ref/fate/aac-seek-landing-frame-length-short
@@ -1,3 +1,3 @@
-packet|pts=157440|dts=157440|duration=960
 packet|pts=158400|dts=158400|duration=960
 packet|pts=159360|dts=159360|duration=960
+packet|pts=160320|dts=160320|duration=960
diff --git a/tests/ref/fate/aac-seek-landing-sbr 
b/tests/ref/fate/aac-seek-landing-sbr
index 1e8fc110a40..7f3e2ebc8d8 100644
--- a/tests/ref/fate/aac-seek-landing-sbr
+++ b/tests/ref/fate/aac-seek-landing-sbr
@@ -1,3 +1,3 @@
-packet|pts=244736|dts=244736|duration=1024
-packet|pts=245760|dts=245760|duration=1024
 packet|pts=246784|dts=246784|duration=1024
+packet|pts=247808|dts=247808|duration=1024
+packet|pts=248832|dts=248832|duration=1024
diff --git a/tests/ref/fate/mov-aac-2048-priming-seek-landing 
b/tests/ref/fate/mov-aac-2048-priming-seek-landing
index ff6091c3271..17292a119ae 100644
--- a/tests/ref/fate/mov-aac-2048-priming-seek-landing
+++ b/tests/ref/fate/mov-aac-2048-priming-seek-landing
@@ -1,3 +1,3 @@
-packet|pts=100352|dts=100352|duration=1024
 packet|pts=101376|dts=101376|duration=1024
 packet|pts=102400|dts=102400|duration=1024
+packet|pts=103424|dts=103424|duration=1024
diff --git a/tests/ref/fate/preroll-aac-concat-inpoint 
b/tests/ref/fate/preroll-aac-concat-inpoint
index e9f9d75572a..7aeb9236d30 100644
--- a/tests/ref/fate/preroll-aac-concat-inpoint
+++ b/tests/ref/fate/preroll-aac-concat-inpoint
@@ -1,4 +1,4 @@
-packet|stream_index=0|pts=-1000|flags=K__
-packet|stream_index=1|pts=-986|flags=K__|side_datum/skip_samples:skip_samples=1024
-packet|stream_index=1|pts=-965|flags=K__
-packet|stream_index=1|pts=-944|flags=K__
+packet|stream_index=0|pts=0|flags=K__
+packet|stream_index=1|pts=38|flags=K__
+packet|stream_index=1|pts=59|flags=K__
+packet|stream_index=1|pts=80|flags=K__
diff --git a/tests/ref/fate/preroll-aac-concat-seek 
b/tests/ref/fate/preroll-aac-concat-seek
index 895a3c46eab..0bdc1329cb4 100644
--- a/tests/ref/fate/preroll-aac-concat-seek
+++ b/tests/ref/fate/preroll-aac-concat-seek
@@ -1,6 +1,6 @@
-ret: 0         st: 0 flags:1 dts: 0.462948 pts: 0.462948 pos:   2363 size:    
26 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 0.486168 pts: 0.486168 pos:   2389 size:    
27
+ret: 0         st: 0 flags:1 dts: 0.486168 pts: 0.486168 pos:   2389 size:    
27 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 0.509388 pts: 0.509388 pos:   2416 size:    
27
+ret: 0         st: 0 flags:1 dts: 0.532608 pts: 0.532608 pos:   2443 size:    
28
 ret: 0         st:-1 flags:0  ts:-1.000000
 ret: 0         st: 0 flags:1 dts:-0.047891 pts:-0.047891 pos:   1366 size:     
6 skip:2112 discard:0
 ret: 0         st: 0 flags:1 dts:-0.024671 pts:-0.024671 pos:   1372 size:   
251
@@ -14,17 +14,17 @@ ret: 0         st: 0 flags:1 dts: 0.811247 pts: 0.811247 
pos:   2774 size:    27
 ret: 0         st: 0 flags:1 dts: 0.834467 pts: 0.834467 pos:   2801 size:    
28
 ret: 0         st: 0 flags:1 dts: 0.857687 pts: 0.857687 pos:   2829 size:    
28
 ret: 0         st: 0 flags:1  ts: 1.682494
-ret: 0         st: 0 flags:1 dts: 1.623946 pts: 1.623946 pos:   3903 size:    
39 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 1.647166 pts: 1.647166 pos:   3942 size:    
42
+ret: 0         st: 0 flags:1 dts: 1.647166 pts: 1.647166 pos:   3942 size:    
42 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.670385 pts: 1.670385 pos:   3984 size:    
41
+ret: 0         st: 0 flags:1 dts: 1.693605 pts: 1.693605 pos:   4025 size:    
41
 ret: 0         st:-1 flags:0  ts:-0.423332
 ret: 0         st: 0 flags:1 dts:-0.047891 pts:-0.047891 pos:   1366 size:     
6 skip:2112 discard:0
 ret: 0         st: 0 flags:1 dts:-0.024671 pts:-0.024671 pos:   1372 size:   
251
 ret: 0         st: 0 flags:1 dts:-0.001451 pts:-0.001451 pos:   1623 size:   
168
 ret: 0         st:-1 flags:1  ts: 0.470835
-ret: 0         st: 0 flags:1 dts: 0.416508 pts: 0.416508 pos:   2309 size:    
27 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 0.439728 pts: 0.439728 pos:   2336 size:    
27
+ret: 0         st: 0 flags:1 dts: 0.439728 pts: 0.439728 pos:   2336 size:    
27 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 0.462948 pts: 0.462948 pos:   2363 size:    
26
+ret: 0         st: 0 flags:1 dts: 0.486168 pts: 0.486168 pos:   2389 size:    
27
 ret: 0         st: 0 flags:0  ts: 1.365011
 ret: 0         st: 0 flags:1 dts: 1.368526 pts: 1.368526 pos:   3465 size:    
30 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.391746 pts: 1.391746 pos:   3495 size:    
39
@@ -38,9 +38,9 @@ ret: 0         st: 0 flags:1 dts: 0.161088 pts: 0.161088 pos: 
  2014 size:    27
 ret: 0         st: 0 flags:1 dts: 0.184308 pts: 0.184308 pos:   2041 size:    
27
 ret: 0         st: 0 flags:1 dts: 0.207528 pts: 0.207528 pos:   2068 size:    
27
 ret: 0         st:-1 flags:1  ts: 1.047503
-ret: 0         st: 0 flags:1 dts: 0.997007 pts: 0.997007 pos:   2996 size:    
28 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 1.020227 pts: 1.020227 pos:   3024 size:    
28
+ret: 0         st: 0 flags:1 dts: 1.020227 pts: 1.020227 pos:   3024 size:    
28 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.043447 pts: 1.043447 pos:   3052 size:    
27
+ret: 0         st: 0 flags:1 dts: 1.066667 pts: 1.066667 pos:   3079 size:    
28
 ret: 0         st: 0 flags:0  ts: 1.941678
 ret: 0         st: 0 flags:1 dts: 1.949025 pts: 1.949025 pos:   4474 size:    
41 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.972245 pts: 1.972245 pos:   4515 size:    
39
@@ -54,17 +54,17 @@ ret: 0         st: 0 flags:1 dts: 0.741587 pts: 0.741587 
pos:   2691 size:    28
 ret: 0         st: 0 flags:1 dts: 0.764807 pts: 0.764807 pos:   2719 size:    
28
 ret: 0         st: 0 flags:1 dts: 0.788027 pts: 0.788027 pos:   2747 size:    
27
 ret: 0         st:-1 flags:1  ts: 1.624171
-ret: 0         st: 0 flags:1 dts: 1.577506 pts: 1.577506 pos:   3821 size:    
41 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 1.600726 pts: 1.600726 pos:   3862 size:    
41
+ret: 0         st: 0 flags:1 dts: 1.600726 pts: 1.600726 pos:   3862 size:    
41 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.623946 pts: 1.623946 pos:   3903 size:    
39
+ret: 0         st: 0 flags:1 dts: 1.647166 pts: 1.647166 pos:   3942 size:    
42
 ret: 0         st: 0 flags:0  ts:-0.481655
 ret: 0         st: 0 flags:1 dts:-0.047891 pts:-0.047891 pos:   1366 size:     
6 skip:2112 discard:0
 ret: 0         st: 0 flags:1 dts:-0.024671 pts:-0.024671 pos:   1372 size:   
251
 ret: 0         st: 0 flags:1 dts:-0.001451 pts:-0.001451 pos:   1623 size:   
168
 ret: 0         st: 0 flags:1  ts: 0.412494
-ret: 0         st: 0 flags:1 dts: 0.346848 pts: 0.346848 pos:   2229 size:    
26 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 0.370068 pts: 0.370068 pos:   2255 size:    
27
+ret: 0         st: 0 flags:1 dts: 0.370068 pts: 0.370068 pos:   2255 size:    
27 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 0.393288 pts: 0.393288 pos:   2282 size:    
27
+ret: 0         st: 0 flags:1 dts: 0.416508 pts: 0.416508 pos:   2309 size:    
27
 ret: 0         st:-1 flags:0  ts: 1.306672
 ret: 0         st: 0 flags:1 dts: 1.322086 pts: 1.322086 pos:   3405 size:    
30 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.345306 pts: 1.345306 pos:   3435 size:    
30
@@ -78,9 +78,9 @@ ret: 0         st: 0 flags:1 dts: 0.114649 pts: 0.114649 pos: 
  1961 size:    26
 ret: 0         st: 0 flags:1 dts: 0.137868 pts: 0.137868 pos:   1987 size:    
27
 ret: 0         st: 0 flags:1 dts: 0.161088 pts: 0.161088 pos:   2014 size:    
27
 ret: 0         st: 0 flags:1  ts: 0.989184
-ret: 0         st: 0 flags:1 dts: 0.927347 pts: 0.927347 pos:   2913 size:    
27 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 0.950567 pts: 0.950567 pos:   2940 size:    
28
+ret: 0         st: 0 flags:1 dts: 0.950567 pts: 0.950567 pos:   2940 size:    
28 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 0.973787 pts: 0.973787 pos:   2968 size:    
28
+ret: 0         st: 0 flags:1 dts: 0.997007 pts: 0.997007 pos:   2996 size:    
28
 ret: 0         st:-1 flags:0  ts: 1.883340
 ret: 0         st: 0 flags:1 dts: 1.902585 pts: 1.902585 pos:   4392 size:    
41 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.925805 pts: 1.925805 pos:   4433 size:    
41
@@ -94,14 +94,14 @@ ret: 0         st: 0 flags:1 dts: 0.671927 pts: 0.671927 
pos:   2609 size:    27
 ret: 0         st: 0 flags:1 dts: 0.695147 pts: 0.695147 pos:   2636 size:    
27
 ret: 0         st: 0 flags:1 dts: 0.718367 pts: 0.718367 pos:   2663 size:    
28
 ret: 0         st: 0 flags:1  ts: 1.565850
-ret: 0         st: 0 flags:1 dts: 1.507846 pts: 1.507846 pos:   3699 size:    
39 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 1.531066 pts: 1.531066 pos:   3738 size:    
42
+ret: 0         st: 0 flags:1 dts: 1.531066 pts: 1.531066 pos:   3738 size:    
42 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 1.554286 pts: 1.554286 pos:   3780 size:    
41
+ret: 0         st: 0 flags:1 dts: 1.577506 pts: 1.577506 pos:   3821 size:    
41
 ret: 0         st:-1 flags:0  ts:-0.539992
 ret: 0         st: 0 flags:1 dts:-0.047891 pts:-0.047891 pos:   1366 size:     
6 skip:2112 discard:0
 ret: 0         st: 0 flags:1 dts:-0.024671 pts:-0.024671 pos:   1372 size:   
251
 ret: 0         st: 0 flags:1 dts:-0.001451 pts:-0.001451 pos:   1623 size:   
168
 ret: 0         st:-1 flags:1  ts: 0.354175
-ret: 0         st: 0 flags:1 dts: 0.300408 pts: 0.300408 pos:   2175 size:    
27 skip:1024 discard:0
-ret: 0         st: 0 flags:1 dts: 0.323628 pts: 0.323628 pos:   2202 size:    
27
+ret: 0         st: 0 flags:1 dts: 0.323628 pts: 0.323628 pos:   2202 size:    
27 skip:1024 discard:0
 ret: 0         st: 0 flags:1 dts: 0.346848 pts: 0.346848 pos:   2229 size:    
26
+ret: 0         st: 0 flags:1 dts: 0.370068 pts: 0.370068 pos:   2255 size:    
27
diff --git a/tests/ref/seek/mkv-codec-delay b/tests/ref/seek/mkv-codec-delay
index f2281552908..779298347fd 100644
--- a/tests/ref/seek/mkv-codec-delay
+++ b/tests/ref/seek/mkv-codec-delay
@@ -2,47 +2,47 @@ ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos: 
   748 size:   320
 ret: 0         st:-1 flags:0  ts:-1.000000
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
 ret: 0         st:-1 flags:1  ts: 1.894167
-ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:3840 discard:706
+ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:0 discard:706
 ret: 0         st: 0 flags:0  ts: 0.788000
-ret: 0         st: 0 flags:1 dts: 0.794000 pts: 0.794000 pos:   7358 size:   
154 skip:3840 discard:0
+ret: 0         st: 0 flags:1 dts: 0.794000 pts: 0.794000 pos:   7358 size:   
154
 ret: 0         st: 0 flags:1  ts:-0.317000
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
 ret:-1         st:-1 flags:0  ts: 2.576668
 ret: 0         st:-1 flags:1  ts: 1.470835
-ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:3840 discard:706
+ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:0 discard:706
 ret: 0         st: 0 flags:0  ts: 0.365000
-ret: 0         st: 0 flags:1 dts: 0.374000 pts: 0.374000 pos:   3963 size:   
150 skip:3840 discard:0
+ret: 0         st: 0 flags:1 dts: 0.374000 pts: 0.374000 pos:   3963 size:   
150
 ret: 0         st: 0 flags:1  ts:-0.741000
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
 ret:-1         st:-1 flags:0  ts: 2.153336
 ret: 0         st:-1 flags:1  ts: 1.047503
-ret: 0         st: 0 flags:1 dts: 0.954000 pts: 0.954000 pos:   8643 size:   
154 skip:3840 discard:0
+ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:0 discard:706
 ret: 0         st: 0 flags:0  ts:-0.058000
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
 ret: 0         st: 0 flags:1  ts: 2.836000
-ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:3840 discard:706
+ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:0 discard:706
 ret:-1         st:-1 flags:0  ts: 1.730004
 ret: 0         st:-1 flags:1  ts: 0.624171
-ret: 0         st: 0 flags:1 dts: 0.534000 pts: 0.534000 pos:   5251 size:   
150 skip:3840 discard:0
+ret: 0         st: 0 flags:1 dts: 0.614000 pts: 0.614000 pos:   5903 size:   
159
 ret: 0         st: 0 flags:0  ts:-0.482000
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
 ret: 0         st: 0 flags:1  ts: 2.413000
-ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:3840 discard:706
+ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:0 discard:706
 ret:-1         st:-1 flags:0  ts: 1.306672
 ret: 0         st:-1 flags:1  ts: 0.200839
-ret: 0         st: 0 flags:1 dts: 0.114000 pts: 0.114000 pos:   1855 size:   
158 skip:3840 discard:0
+ret: 0         st: 0 flags:1 dts: 0.194000 pts: 0.194000 pos:   2512 size:   
159
 ret: 0         st: 0 flags:0  ts:-0.905000
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
 ret: 0         st: 0 flags:1  ts: 1.989000
-ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:3840 discard:706
+ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:0 discard:706
 ret: 0         st:-1 flags:0  ts: 0.883340
-ret: 0         st: 0 flags:1 dts: 0.894000 pts: 0.894000 pos:   8154 size:   
155 skip:3840 discard:0
+ret: 0         st: 0 flags:1 dts: 0.894000 pts: 0.894000 pos:   8154 size:   
155
 ret: 0         st:-1 flags:1  ts:-0.222493
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
 ret:-1         st: 0 flags:0  ts: 2.672000
 ret: 0         st: 0 flags:1  ts: 1.566000
-ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:3840 discard:706
+ret: 0         st: 0 flags:1 dts: 1.014000 pts: 1.014000 pos:   9306 size:   
268 skip:0 discard:706
 ret: 0         st:-1 flags:0  ts: 0.460008
-ret: 0         st: 0 flags:1 dts: 0.474000 pts: 0.474000 pos:   4768 size:   
153 skip:3840 discard:0
+ret: 0         st: 0 flags:1 dts: 0.474000 pts: 0.474000 pos:   4768 size:   
153
 ret: 0         st:-1 flags:1  ts:-0.645825
 ret: 0         st: 0 flags:1 dts:-0.007000 pts:-0.007000 pos:    748 size:   
320 skip:312 discard:0
-- 
2.54.0


>From deefd93a1a5178c7b088ae220ebb1658a5082c22 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 7 Nov 2026 11:49:40 -0600
Subject: [PATCH 4/4] fftools/ffplay: set seek2preroll

ffplay decodes everything it reads, so its seeks need the audio
pre-roll before the target.
---
 fftools/ffplay.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index a149ec555c4..3968a1061b9 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -3249,6 +3249,7 @@ static int read_thread(void *arg)
     }
     ic->interrupt_callback.callback = decode_interrupt_cb;
     ic->interrupt_callback.opaque = is;
+    ic->seek2preroll = 1;
     if ((ret = av_dict_copy(&format_options, format_opts, 0)) < 0)
         goto fail;
     if (!av_dict_get(format_options, "scan_all_pmts", NULL, 
AV_DICT_MATCH_CASE)) {
-- 
2.54.0

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

Reply via email to