Skip to content

Seek starvation by superseded readers + cold far-seek latency #240

Description

@cmcpherson274

What happened?

Seek on a large remote MKV: superseded seek/prefetch AVIO readers keep fetching and starve the active seek — landing budget expires and re-anchors stack to 13-25 s; separately, a cold far seek anchors one segment before the target (~5 s floor at 65 Mbps source over a ~90 Mbps link)

Two related behaviours on far seeks in a large high-bitrate MKV served over HTTP, measured on a
real device with millisecond-timestamped logs (10 repetitions: 5x isolated cold seek 0->640 s in
fresh sessions, 5x rapid sequence 600->30->302->640 at ~4 s intervals).

THE BUG (rapid-seek case): when a new seek supersedes one still in flight, the superseded
pipeline's AVIOReader range connections and the forward subtitle prefetcher KEEP FETCHING. We
counted 5-8 concurrent range connections at 32 MiB-stride offsets competing on a link barely
above the source bitrate. The active seek's segment serve then starves: the same seg159 that
served in 2.2 s on a cold seek took 7.5 s (wait=7456.1 ms) under contention, the 8 s landing
budget expired — your own log: "seek did not land within budget (1 extension; starved,
island=1.37s at target...)" — the producer re-anchored, contention repeated, and observed
landings stacked to 13.7-25.2 s. The engine starves itself: its own budget is defeated by its
own uncancelled readers.

PERFORMANCE CONTEXT 1 (not claimed as a bug — cold-seek floor): on a fresh session, seek to
640.0 lands in 4.4-6.1 s (5/5, positionally exact every time). The timeline decomposes cleanly:
the producer restart anchors at the segment BEFORE the target (seg158 = 632.38 s for a 640.0
target at the seg159/seg160 boundary), so ~7.6 s of pre-target media — 56.4 MB at this bitrate —
must flow before the playhead reaches the target. We measured that transfer at 90 Mbps, our
link's line rate; the engine's own repositioning took 0.31 s (restart 33 ms, first ranged data
177 ms, A/V gates open at +310 ms). So the floor is transfer-bound geometry, not engine CPU.

PERFORMANCE CONTEXT 2: first new PGS cue after a cold first seek arrives in 3.7-6.4 s (10/10
sessions); after later seeks in the same session, usually 0.2-0.9 s. The warm number shows the
fast path exists; the cold path pays the full pipeline serially. (49 PGS tracks on this file.)

Our product gate has a 2 s command-to-correct-state seek deadline — that is OUR bar, not any
contract of yours, and this report is framed accordingly: the starvation is the part we believe
is yours; the cold floor on our Wi-Fi bench is substantially our link (on gigabit the same
geometry would cost ~0.5 s).

Steps to reproduce

  1. Serve a large high-bitrate MKV over HTTP (ours: 48.29 GB 4K HEVC DV P8.1 remux, ~65 Mbps
    overall; any comparable file should do) and play it via the local-HLS path into AVPlayer
    (4.000 s segment duration).
  2. BUG: while playback runs, issue several far seeks ~4 s apart (e.g. 600 -> 30 -> 302 -> 640).
    Watch the AVIOReader connection log: superseded generations keep opening/fetching at 32 MiB
    strides alongside the new seek's reads. On a link within ~1.5x of the source bitrate, the
    active seek's segment serve time balloons (2.2 s -> 7.5 s for the same segment), the landing
    budget expires ("starved"), and the re-anchor cycle stacks landings to 13-25 s.
  3. COLD FLOOR: from a fresh session, single seek to a far target near a segment-boundary end
    (e.g. 640.0 with 4 s segments). The producer restarts one segment early (632.38 s), forcing
    ~7.6 s of pre-target media through the pipe before landing.

AetherEngine version or commit SHA

5.25.2 (cf164d8); FFmpegBuild 2.2.0 (e832f22c), LibDovi 1.0.2

Host app

Custom / my own integration

Platform

tvOS

OS version

tvOS 26.5

Device / chip

Apple TV 4K (3rd generation), AppleTV14,1 — real hardware, not simulator. Network: Wi-Fi, ~90 Mbps effective measured (relevant to the numbers above and stated so the transfer-bound share is attributable to our bench, not your code).

Playback path

Native AVPlayer

Source media (for playback bugs)

48.29 GB Matroska remux: 4K HEVC, Dolby Vision Profile 8.1, DTS-X, 2 audio + 49 PGS tracks,
~65 Mbps overall bitrate, served by Jellyfin 10.11.11 as a direct file over HTTP range reads.
Cannot be attached (size + commercial content); full timestamped logs of every repetition are
available on request, and the behaviour should reproduce on any comparable large high-bitrate
MKV over a link within ~1.5x of its bitrate.

Error codes / log lines

No error is raised; the seek eventually lands. The two annotated timelines:

COLD SEEK (isolated-1, 5.199 s total; times relative to the seek command):
  t+0.000  seek begin target=640.0
  t+0.005  AVPlayer GETs /seg158.mp4; out-of-range -> producer restart
  t+0.039  producer restarted at idx=158, seek=632.38s absolute source PTS
           (restart took 33ms; stopWait=32 seek=0 build=0)
  t+0.216  new persistent range connection first data (offset ~4.52 GB — correct
           region immediately, no index hunting)
  t+0.310  video gate open: actual=632382 target=632382 shift=0 / audio gapMs=2.0
  t+2.725  seg158: served 21986807 B (wait=2719.9ms)
  t+5.024  seg159: served 34399188 B (wait=2241.6ms)
  t+5.199  landed at exactly 640.0

STARVED SEEK (sequence-2 gen=4, 25.1 s total, after superseding two in-flight seeks):
  t+0.011  seg159: out-of-range fetch -> producer restarted at idx=159 (101ms)
  t+0.9    firstNewCue=927ms (subtitle path is FINE here — video starves, cues don't)
  t+1.4..t+12  persistent conn starts gen=9..16 at 32 MiB strides — the superseded
           pipelines and forward prefetcher still fetching alongside the live seek
  t+7.5    seg159: served 34399356 B (wait=7456.1ms — same segment, 3.3x the cold time)
  t+8.1    "seek slow but producer serving target (island=1.37s at target...); extending
           budget 1/4"
  t+12.1   "seek did not land within budget (1 extension; starved, island=1.37s at
           target, rendered=603.01s buffered=603.01s); holding clock at target 640.00s
           and re-anchored producer at 640.00s, re-seeking"
  ...cycle repeats; landed at 640.0 at t+25.151

Anything else

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions