3.4.0 — Live direct play: demuxed + packed audio, rejoin-at-edge reloads
Live TV release: live HLS upstreams with separate or packed audio renditions now direct-play, and live sessions survive reloads and flaky playlist origins. Built against real broadcast streams (ARD Das Erste 1080p50, device-verified on tvOS 26).
Added
Demuxed-audio HLS direct play
Many broadcasters publish video-only variants with the audio in a separate EXT-X-MEDIA rendition playlist. 3.3.0 detected this shape and failed fast; 3.4.0 plays it:
HLSLiveIngestReaderresolves the variant's audio group and spawns a companion rendition reader for the audio playlist.- A side demuxer opens the audio stream, and
HLSSegmentProducerpull-merges both sources by DTS onto one output timeline (audio gated on video so the first cut segment is A/V aligned). - The fail-fast from 3.3.0 remains for shapes the ingest cannot handle (encrypted, fMP4), so hosts keep their typed fallback.
Packed-audio renditions
Audio playlists that carry raw ADTS segments (no TS wrapper) framed by ID3 PRIV timestamps (com.apple.streaming.transportStreamTimestamp, 90 kHz) are classified per segment and wrapped on the fly (PackedAudioSegments), with a synthesized clock aligning them to the video timeline.
Live playlist-refresh retry
Transient playlist-refresh failures (CDN hiccup, origin restart) retry inside a bounded ~12 s budget before the ingest goes terminal. A single dropped poll no longer ends the session.
Fixed
Live reloads rejoin at the live edge
An audio-track switch (or any engine reload) on a live session used to re-apply a stale resume position against an origin that re-serves its full transcode backlog on reconnect (Jellyfin does), which could park AVPlayer in waitingToPlay indefinitely. Device-verified failure case on tvOS 26.
- Reload positioning is now policy-driven (
LiveReloadPolicy, pure and unit-tested): live rejoins take the playlist's own live-edge join, never a stale clock, and skip the pre-readiness zero seek. - A readiness watchdog (10 s budget from first serving evidence, 60 s lifetime) fails a wedged rejoin cleanly into the host's retune surface instead of hanging the session.
Swallowed play intent on the reused AVPlayer host
A play() issued while replaceCurrentItem was mid-swap could be silently dropped: the item reached readyToPlay but stayed paused forever. The host now latches the play intent and re-asserts it at readyToPlay (cleared on pause/unload), preserving the existing reuse gate.
Published audio index after a live reload
The engine reconciles the published audio-track selection with what the rebuilt pipeline actually plays, so hosts no longer observe a phantom track switch after a reload.
Tooling
aetherctl live --reload-testexercises the live rejoin end to end against the built-in fixture, including the full-backlog replay shape that reproduced the device failure.
Compatibility
No breaking API changes. New public surface: demuxed/packed-audio ingest is automatic inside HLSLiveIngestReader; LoadOptions gains the live-rejoin discriminator used by reloads. Pin from: "3.4.0".