5.25.1 - Subtitle prefetch seek anchor
Patch release. A discard flag that was never meant to steer the positioning seek had been steering it, and 5.23.11 changed that flag.
Fixed
The subtitle forward prefetcher anchors its positioning seek on the subtitle axis. (#234)
A seek landing whose only later display set sits beyond the 60 s lead window went dark in 5.23.11, the release that added the #230 pacing stream. The shape is the one fixed in 5.15.2 for #143 and completed in 5.20.6 for #204: land inside a long line whose clear is far away, and the line has to paint at the destination. It held from 5.15.2 through 5.23.10.
The park is not the problem. The positioning is.
The prefetcher positions with avformat_seek_file(ctx, -1, ...), and a -1 stream index leaves the choice of reference stream to av_find_default_stream_index. That score awards +200 to any stream with discard != AVDISCARD_ALL:
if (st->discard != AVDISCARD_ALL)
score += 200;Until 5.23.10 every non-subtitle stream sat at AVDISCARD_ALL, so the subtitle stream was the only one collecting that +200 and won the vote 200 to 75. The target was measured on the subtitle axis and the seek landed on the last display set at or before it, which is the landing set. That was never requested, it was inherited from a discard flag.
#230 moved the pacing stream to AVDISCARD_NONKEY so the read loop would have a control point between sparse cues. AVDISCARD_NONKEY is not AVDISCARD_ALL, so video collected the same +200 and won at 275. The seek started landing on the last video keyframe instead.
On Matroska that decides the outcome. matroska_read_seek jumps to the cluster byte position named by the reference stream's index entry, and anything in earlier clusters is never read. A landing set further back than that cluster is behind the read head before the first packet is delivered: never harvested, never decoded, so neither the #143 candidate seed nor the #204 finalize can act on it. The reporter measured harvested dropping from 3 to 1 with no apply event at the destination, which is exactly that.
The anchor is now passed explicitly through Demuxer.seekBounded(to:anchorStreamIndex:timeout:), which also converts the target into that stream's own time base. The prefetcher passes its lowest subtitle stream index, which is what the score used to elect. Positioning no longer depends on which streams happen to be deliverable. The #230 park is unchanged.
The class does not reproduce on MP4, which is why the #230 tests stayed green: mov_read_seek re-seeks every stream individually and backwards, so a sparse subtitle track keeps its landing sample whatever the reference is, while matroska_read_seek moves all streams to one cluster position. The new coverage therefore runs on a purpose-built Matroska fixture carrying the reported shape at fixture scale, and the container contrast is a test of its own.
Diagnostics: the #151 forward prefetch started line now carries anchor=<index> alongside pacing=, so the axis a session positioned on is readable from the log.
Reported by cmcpherson274, bisected across four releases on identical fixture bytes.
Notes
No API changes. seekBounded gained a defaulted anchorStreamIndex parameter; existing call sites keep libavformat's default reference.