Skip to content

6.4.1 - Subtitle cues publish once per drain tick

Choose a tag to compare

@superuser404notfound superuser404notfound released this 01 Aug 15:45

A dense subtitle track no longer makes every $subtitleCues subscriber walk the whole cumulative cue array once per decoded packet.

Changed

  • $subtitleCues publishes once per drain tick instead of once per decoded subtitle packet. Every publication carries the whole cumulative cue array, and a snapshot cannot tell a consumer which of its elements are new, so each one cost every subscriber a full walk: O(n) per packet, O(n²) per drain window. On a typeset ASS track that came to 104 publications and 608,608 cue visits per second in a single consumer, none of which found new work.

    The tick now binds the channel's array once, applies the whole batch of decoded events to it, and publishes only when the batch actually changed something. The retained-store insert looks up same-start cues by binary search rather than scanning the whole array.

    Reported and measured by @edde746 (#271).

Fixed

  • One drain tick no longer decodes an unbounded number of subtitle packets. The drain window is bounded in seconds of content (backscan plus lead), never in packets, so its size was set by the file's subtitle density while the decode loop ran synchronously on the main actor with no suspension point.

    It is now capped per tick, with the boundary extended to the end of the run sharing the last packet's PTS. That extension is load-bearing: the drain cursor is a bare PTS advanced past what it decoded, so a cut inside a same-PTS run would skip the remainder for good rather than resume it on the next tick, and dense ASS deliberately keeps hundreds of distinct payloads on one timestamp. The subtitle OCR worker's existing cap gets the same PTS-boundary correction (#271).

  • A slow drain tick no longer reads its own duration as a seek. The plan compared the live playhead against the playhead captured at the previous tick's start, so a tick lasting longer than the 2.5 s jump threshold made the next one reset onto a fresh, disjoint window: a positive feedback loop, since the reset window is the expensive one. Forward drift is now forgiven up to the wall time the previous tick consumed. Backward drift is not, because playback never moves the playhead backwards (#271).

Upgrading

.package(url: "https://github.com/superuser404notfound/AetherEngine", from: "6.4.1")

Source-compatible, no public API change. Cue content and timing are unchanged; only the number of publications carrying them drops. A host that already deduplicates what it has seen keeps working as is, and one that does per-cue work on every publication gets the reduction for free.