You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
panelRefusedHDRMaster is latched for the life of the process, and nothing ever clears it. The latch is correct at the moment it is set and stale from the moment the user changes the Apple TV's output format, which they have to leave the app to do and which the platform never reports. An Apple TV can stay in the same app process for days, so the window for the stale state is effectively unbounded, the user has no indication anything is wrong, and the only cure is force-quitting.
The code already predicts this, in its own comment on AetherEngine.swift:
Not cleared when the user changes the output format mid-process, because nothing reports that either. A stale refusal costs the master route until the app restarts, which is the behaviour this replaces rather than a regression from it.
That trade was made against a cost of "the master route". Measurements on #459 show the cost is larger than that and all of it is user-visible.
The measurement
All of this is classicjazz's work, reported in #459 (comment). Sodalite 1.0.0 (37), AetherEngine 7.10.0, tvOS 27.0 (24J361), two Apple TV 4K (AppleTV14,1), two LG OLEDs, same HDR10 title throughout.
Same file, same box, 100 seconds apart. Box B, healthy process:
19:03:52.136 panel unproven but HDR-eligible: serving the master and letting
AVPlayer answer (refusal costs one in-place media fallback)
19:03:52.163 serving on .../master.m3u8 (panelIsHDR=true ... useMaster=true)
19:03:53.565 republishing videoFormat sdr -> hdr10: AVFoundation accepted the
HDR master (#459)
Box A, identical panel readout at load, no attempt line at all:
19:05:33.842 serving on .../media.m3u8 (panelIsHDR=false ... useMaster=false)
19:05:47.405 playback probe: no HDR reading in 12158ms ...
Of the four terms in sessionRoutesAsHDRPanel, three are identical across the two runs: panelPresentsHDR false on both, attemptsHDRMasterOnUnprovenPanel defaults true and the host never sets it, supportsHDR true on both per DisplayCapabilities observed. That leaves the latch. A force-quit and a fresh process on the same box, same file, same television confirms it:
19:11:36.580 serving on .../master.m3u8 (panelIsHDR=true ... useMaster=true)
19:11:37.911 republishing videoFormat sdr -> hdr10: AVFoundation accepted the
HDR master (#459)
The afternoon before those captures had been spent switching the box's output format and Match Dynamic Range back and forth, so at some point the process genuinely was in a mode that refused the master. The latch recorded a true fact about a configuration that no longer exists.
Worth stating plainly because it bounds the fix: the headroom leg is dead on both of these panels. currentEDR=1.00 potentialEDR=1.00 at load and at teardown, and the explicit probe verdict after 12 seconds of PQ on screen is no HDR reading in 12158ms (max headroom 1.00, 48 samples). On this hardware everything that works is master acceptance, so losing the master route loses the whole answer.
Dolby Vision signaling. On a Profile 7 source the engine builds the supplemental descriptor and then has nowhere to put it, because a media playlist has no variant to carry it:
Subtitles in PiP. No master means no EXT-X-MEDIA:TYPE=SUBTITLES rendition. Cues kept publishing to the on-frame overlay for the whole time PiP was up (seven outcome=published in thirty seconds, subActive=true) and nothing was visible in the window. Controlled against SDR on the same display with the same PGS path: SDR is routing-safe without panel proof, so it gets a master, the rendition is served, and AVKit fetches subs_0_1294.vtt and renders it in PiP.
That last one is a different surface from Sodalite#34, which was a wired external display on iOS and found master-served subtitles not reaching the screen. tvOS PiP behaves the other way here. It adds a row to that matrix rather than disputing one.
The fix
Clear the latch on foreground return.
A user who changes the output format has to leave the app to reach Settings, so UIApplication.didBecomeActiveNotification is the one event guaranteed to follow such a change. The engine already observes it (setupLifecycleObservers, under #if os(iOS) || os(tvOS)), so the hook exists and costs nothing to reach.
What the clear costs when it is wrong, which is the case of a genuinely SDR panel that will refuse again: one in-place media fallback, measured at 223 ms with no visible black frame, paid at the next HDR load after a foregrounding rather than once per process. The latch keeps doing its job inside a viewing session, which is where the repetition it was built to prevent would actually have happened.
What this deliberately does not do is guess. The alternative of keying the latch to a fingerprint of the display configuration would be more precise, but the latch can only be set while eligibleForHDRPlayback is true, so the capability table at refusal time may well be identical to the table afterwards, and a fingerprint built on that assumption would silently keep the stale latch. That would need measuring on the device before anything is built on it. Foreground return needs no such assumption.
panelRefusedHDRMasteris latched for the life of the process, and nothing ever clears it. The latch is correct at the moment it is set and stale from the moment the user changes the Apple TV's output format, which they have to leave the app to do and which the platform never reports. An Apple TV can stay in the same app process for days, so the window for the stale state is effectively unbounded, the user has no indication anything is wrong, and the only cure is force-quitting.The code already predicts this, in its own comment on
AetherEngine.swift:That trade was made against a cost of "the master route". Measurements on #459 show the cost is larger than that and all of it is user-visible.
The measurement
All of this is classicjazz's work, reported in #459 (comment). Sodalite 1.0.0 (37), AetherEngine 7.10.0, tvOS 27.0 (24J361), two Apple TV 4K (AppleTV14,1), two LG OLEDs, same HDR10 title throughout.
Same file, same box, 100 seconds apart. Box B, healthy process:
Box A, identical panel readout at load, no attempt line at all:
Of the four terms in
sessionRoutesAsHDRPanel, three are identical across the two runs:panelPresentsHDRfalse on both,attemptsHDRMasterOnUnprovenPaneldefaults true and the host never sets it,supportsHDRtrue on both perDisplayCapabilities observed. That leaves the latch. A force-quit and a fresh process on the same box, same file, same television confirms it:The afternoon before those captures had been spent switching the box's output format and Match Dynamic Range back and forth, so at some point the process genuinely was in a mode that refused the master. The latch recorded a true fact about a configuration that no longer exists.
Worth stating plainly because it bounds the fix: the headroom leg is dead on both of these panels.
currentEDR=1.00 potentialEDR=1.00at load and at teardown, and the explicit probe verdict after 12 seconds of PQ on screen isno HDR reading in 12158ms (max headroom 1.00, 48 samples). On this hardware everything that works is master acceptance, so losing the master route loses the whole answer.What a stale latch costs
EXT-X-MEDIA:TYPE=SUBTITLESrendition. Cues kept publishing to the on-frame overlay for the whole time PiP was up (sevenoutcome=publishedin thirty seconds,subActive=true) and nothing was visible in the window. Controlled against SDR on the same display with the same PGS path: SDR is routing-safe without panel proof, so it gets a master, the rendition is served, and AVKit fetchessubs_0_1294.vttand renders it in PiP.That last one is a different surface from Sodalite#34, which was a wired external display on iOS and found master-served subtitles not reaching the screen. tvOS PiP behaves the other way here. It adds a row to that matrix rather than disputing one.
The fix
Clear the latch on foreground return.
A user who changes the output format has to leave the app to reach Settings, so
UIApplication.didBecomeActiveNotificationis the one event guaranteed to follow such a change. The engine already observes it (setupLifecycleObservers, under#if os(iOS) || os(tvOS)), so the hook exists and costs nothing to reach.What the clear costs when it is wrong, which is the case of a genuinely SDR panel that will refuse again: one in-place media fallback, measured at 223 ms with no visible black frame, paid at the next HDR load after a foregrounding rather than once per process. The latch keeps doing its job inside a viewing session, which is where the repetition it was built to prevent would actually have happened.
What this deliberately does not do is guess. The alternative of keying the latch to a fingerprint of the display configuration would be more precise, but the latch can only be set while
eligibleForHDRPlaybackis true, so the capability table at refusal time may well be identical to the table afterwards, and a fingerprint built on that assumption would silently keep the stale latch. That would need measuring on the device before anything is built on it. Foreground return needs no such assumption.