What happened?
On a live 4K50 HEVC / EAC3+JOC (Atmos) channel, the Atmos stream-copy path itself is set up correctly (stream-copy as ec-3 [JOC=Atmos], no decode). The bug is the recovery path when the origin starves the reader: the producer seals the window with ENDLIST, AVPlayer fires didPlayToEndTime, the nudge fails to revive, and the engine reloads the item via replaceCurrentItemWithPlayerItem:. Each item swap re-initialises the AVPlayer audio output, and for an EAC3+JOC passthrough track that forces the AVR/soundbar to renegotiate and lose the Atmos lock for ~1-2s every time.
In one ~90s session this happened across 3 successive AVPlayer items (2 mid-session reloads). The user hears it as "Atmos keeps dropping on Live TV". Nothing on the host side restarts playback here — the host's own live-recovery layer never fired; every reload originates inside AetherEngine.
Steps to reproduce
Load a live EAC3+JOC channel (native path) whose origin delivers slower than real time — here a single h2 connection that trickles bytes, producing chronic multi-second AVIOReader slow read events with reconnects=0 connect=0ms (healthy connection, just slow).
Let it run. The producer keeps hitting "source stopped delivering … under the 6.0s floor" and serves ENDLIST.
AVPlayer reaches didPlayToEndTime; the nudge fails; the engine logs reloading item … (same URL, same host) and swaps in a fresh AVPlayerItem.
On an HDMI AVR/soundbar doing EAC3+JOC passthrough, each swap is an audible drop-and-relock of Atmos. Repeats for as long as the origin stays slow (3 items in ~90s here).
AetherEngine version or commit SHA
6.68.0
Host app
Custom / my own integration
Platform
tvOS
OS version
tvOS 26.6
Device / chip
Apple TV 4K
Playback path
Native AVPlayer
Source media (for playback bugs)
Live HLS (loopback) from an MPEG-TS origin. Video HEVC Main 10, hvc1.2.4.L153, 3840x2160 @ 50fps, HDR10 (PQ), DV=none. Audio eac3 with JOC, stream-copied as ec-3. Origin: single h2 (multiplexed) connection.
Error codes / log lines
21:13:18.949 [HLSVideoEngine] audio: codec=eac3 → stream-copy as `ec-3` [JOC=Atmos]
21:13:18.950 [HLSVideoEngine] EAC3+JOC Atmos: stream-copy engaged; DD+/JOC bitstream preserved
21:13:20.460 [NativeAVPlayerHost] #1 timeControlStatus=playing t+1.50s
# origin starves the reader for essentially the whole session (19 events, mean ~4.6s, max 7.96s)
21:13:22.042 [AVIOReader] slow read: 2910ms ... stallWaits=5 reconnects=0 backoff=0ms connect=0ms origin=1inflight
21:13:34.099 [AVIOReader] slow read: 7960ms ...
21:14:46.657 [AVIOReader] slow read: 5961ms ...
21:14:48.790 [AVIOReader] origin transport: h2 at https://021.galaxypulse.link (multiplexed)
# producer runs out of runway and seals with ENDLIST
21:14:12.247 [VideoSegmentProvider] #446 the source stopped delivering, consumer at 35 of 39
(5.0s runway left, under the 6.0s floor) ... serving the rest of the window as a finished asset (ENDLIST)
# AVPlayer reaches end -> nudge fails -> item reload (the audible event)
21:14:23.039 [NativeAVPlayerHost] #1 didPlayToEndTime
21:14:23.040 [AetherEngine] #65 nudge did not revive the consumer; reloading item at 64517.69s (same URL, same host)
21:14:23.041 [NativeAVPlayerHost] #2 load url=http://127.0.0.1:49325/.../media.m3u8
21:14:23.043 AVPlayer replaceCurrentItemWithPlayerItem: I/BAT.01 -> I/VGC.01
21:14:23.051 AVPlayerItem dealloc I/BAT.01
# and again ~19s later
21:14:30.562 [VideoSegmentProvider] #446 ENDLIST, consumer at 45 of 49
21:14:42.020 [NativeAVPlayerHost] #2 didPlayToEndTime
21:14:42.020 [AetherEngine] #65 nudge did not revive the consumer; reloading item at 64535.89s
21:14:42.020 [NativeAVPlayerHost] #3 load ... (I/VGC.01 -> I/UZR.01)
# session ended by the user zapping away
21:14:48.795 [HLSSegmentProducer] pump finished: reason=stopRequested elapsed=89845ms
Anything else
The core question: when the origin merely slows down (not a genuine EOS), is there a recovery path that keeps the same AVPlayerItem alive and rebuffers / bridges the gap, instead of ENDLIST → didPlayToEndTime → replaceCurrentItemWithPlayerItem:? The item swap is what tears down and re-establishes the audio output, and for bitstream passthrough (EAC3+JOC, and likely AC-3/DD+ 5.1) every teardown is an audible drop-and-relock on the receiver — unlike a decoded/PCM path where a silent rebuffer would be invisible.
Two directions that would help, either or both:
Prefer holding the current item and rebuffering over sealing+reloading when the stall is "out of content but the connection is healthy". This looks related to the #446 ENDLIST/holdback tuning — here the 6.0s runway floor vs 9.0s silence window trips the seal readily on a chronically slow origin.
If a reload is unavoidable, is there any way to preserve the audio output / passthrough across the swap so the AVR does not renegotiate?
Secondary observation (probably not the cause): the route settled at output=2 preferred=2 max=32 ports=[Apple TV[HDMIOutput, ch=2]] and the engine's own ec-3 6-channel track playing into a 2-channel route warning fired. Since the complaint is that Atmos drops (i.e. it works between reloads), I read the 2-channel route as the normal MAT carrier rather than the fault, but flagging it in case it is relevant.
Full unified log available on request.
What happened?
On a live 4K50 HEVC / EAC3+JOC (Atmos) channel, the Atmos stream-copy path itself is set up correctly (stream-copy as ec-3 [JOC=Atmos], no decode). The bug is the recovery path when the origin starves the reader: the producer seals the window with ENDLIST, AVPlayer fires didPlayToEndTime, the nudge fails to revive, and the engine reloads the item via replaceCurrentItemWithPlayerItem:. Each item swap re-initialises the AVPlayer audio output, and for an EAC3+JOC passthrough track that forces the AVR/soundbar to renegotiate and lose the Atmos lock for ~1-2s every time.
In one ~90s session this happened across 3 successive AVPlayer items (2 mid-session reloads). The user hears it as "Atmos keeps dropping on Live TV". Nothing on the host side restarts playback here — the host's own live-recovery layer never fired; every reload originates inside AetherEngine.
Steps to reproduce
Load a live EAC3+JOC channel (native path) whose origin delivers slower than real time — here a single h2 connection that trickles bytes, producing chronic multi-second AVIOReader slow read events with reconnects=0 connect=0ms (healthy connection, just slow).
Let it run. The producer keeps hitting "source stopped delivering … under the 6.0s floor" and serves ENDLIST.
AVPlayer reaches didPlayToEndTime; the nudge fails; the engine logs reloading item … (same URL, same host) and swaps in a fresh AVPlayerItem.
On an HDMI AVR/soundbar doing EAC3+JOC passthrough, each swap is an audible drop-and-relock of Atmos. Repeats for as long as the origin stays slow (3 items in ~90s here).
AetherEngine version or commit SHA
6.68.0
Host app
Custom / my own integration
Platform
tvOS
OS version
tvOS 26.6
Device / chip
Apple TV 4K
Playback path
Native AVPlayer
Source media (for playback bugs)
Live HLS (loopback) from an MPEG-TS origin. Video HEVC Main 10, hvc1.2.4.L153, 3840x2160 @ 50fps, HDR10 (PQ), DV=none. Audio eac3 with JOC, stream-copied as ec-3. Origin: single h2 (multiplexed) connection.
Error codes / log lines
Anything else
The core question: when the origin merely slows down (not a genuine EOS), is there a recovery path that keeps the same AVPlayerItem alive and rebuffers / bridges the gap, instead of ENDLIST → didPlayToEndTime → replaceCurrentItemWithPlayerItem:? The item swap is what tears down and re-establishes the audio output, and for bitstream passthrough (EAC3+JOC, and likely AC-3/DD+ 5.1) every teardown is an audible drop-and-relock on the receiver — unlike a decoded/PCM path where a silent rebuffer would be invisible.
Two directions that would help, either or both:
Prefer holding the current item and rebuffering over sealing+reloading when the stall is "out of content but the connection is healthy". This looks related to the #446 ENDLIST/holdback tuning — here the 6.0s runway floor vs 9.0s silence window trips the seal readily on a chronically slow origin.
If a reload is unavoidable, is there any way to preserve the audio output / passthrough across the swap so the AVR does not renegotiate?
Secondary observation (probably not the cause): the route settled at output=2 preferred=2 max=32 ports=[Apple TV[HDMIOutput, ch=2]] and the engine's own ec-3 6-channel track playing into a 2-channel route warning fired. Since the complaint is that Atmos drops (i.e. it works between reloads), I read the 2-channel route as the normal MAT carrier rather than the fault, but flagging it in case it is relevant.
Full unified log available on request.