Skip to content

5.9.7

Choose a tag to compare

@superuser404notfound superuser404notfound released this 19 Jul 16:57

Fixed

Direct-play embedded subtitles: forward cue exposure now reaches the drainer's full 60 s lead window, so host-applied ADVANCE sync offsets work, text and bitmap alike (#151).

The #112 pump tap harvests subtitle packets only as far as the segment producer's forward park (#102), which on direct play sits just a few seconds past AVPlayer's fetch position. The drainer's 60 s lead (subtitleDrainLeadSeconds) was therefore an empty promise: a host rendering $subtitleCues in its own overlay with a delay offset was fine (300 s trailing retention), but an advance offset beyond the park found nothing, or cues flashed in late and were cut short as the window caught up. SRT and PGS alike, since both flow through the same packet-store drain.

The fix: a VOD-only subtitle forward prefetcher, the forward-production approach preferred in the issue, covering text and bitmap:

  • A subtitle-only side reader fills the session SubtitlePacketStore up to playhead + 60 s independent of the producer. The drainer is untouched; it already decodes to +60 s once packets exist.
  • Every embedded subtitle stream is routed; all other streams are discarded on the side demuxer (#104 rule), so between cues the demuxer fast-walks the index where the container allows.
  • Parks on the subtitle PTS axis (500 ms poll) once the read passes the lead edge; the playhead snapshot refreshes only while parked (no MainActor hop per packet in a backfill burst).
  • Seeks re-anchor it through the drain tick's jump detection; a fresh selection starts it with the #73 resume anchor.
  • The open defers (bounded 30 s) while a producer restart is in flight, the #93 rule: no second WAN demuxer competing with a restart on a starved link.
  • Positioning follows the shared side-reader rules: MKV cue-index prewarm, bounded seek within the 8 s budget, verified byte-estimate fallback (#112 round 10).
  • Split-PES PGS display-set assembly (MPEG-TS) is now keyed per writer (.pump / .prefetch) in the store, so the pump and the prefetcher can assemble the same stream concurrently without corrupting each other's in-flight set; overlapping completed packets dedupe by PTS.

Safety model: live sessions skip it entirely (content past the edge does not exist; the pump already rides the edge). It is best-effort by construction: if the side demuxer cannot open, wedges, or the source has no independent reader clone, behavior is exactly the pre-5.9.7 tap-fed path. Enabling a subtitle stays instant (synchronous backfill from the store); the prefetcher only extends forward coverage in the background.

Expected log signature when active: #151 forward prefetch started: streams=[...] startAt=...s lead=60.0s.

Covered by Issue151SubtitleForwardPrefetchTests (6 tests: writer-keyed assembly isolation, park/resume pacing against a real in-memory Matroska demux, abort semantics, gating and re-anchor rules). Full suite: 768 tests / 130 suites green; strict-concurrency and tvOS/iOS Simulator builds green.

Reported by rrgomes (#151). Thanks for the precise diagnosis, down to the exact bound (#102's forward park) and the preferred resolution.