Skip to content

Live: item reload on a starving origin tears down the audio output and drops EAC3+JOC Atmos passthrough on the receiver #520

Description

@Simpendaal

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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