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
When wireless AirPlay engages or ends, the engine reloads itself to move the
loopback playlist onto the device's LAN IP and back
(handleExternalPlaybackChange → reloadAtCurrentPosition(), the #86 path).
That reload is a full load(), and load()'s stopInternal wipes three
pieces of session state the host set up — none of which the engine restores:
Mid-session external subtitle registrations (addExternalSubtitleTrack, Select external subtitles to make them appear in engine.$subtitleTracks. #88). load() clears externalSubtitleRegistry / subtitleTracks / nextExternalSubtitleOrdinal and re-registers only LoadOptions.externalSubtitles. Every track the host added after load is
permanently gone — the host's menu still lists them, but selecting one
targets a synthetic id that no longer exists.
The active audio/subtitle selection. The reload re-runs
preferred-language auto-selection, which can silently override the user's
explicit picks (including subtitles they turned off).
nativeSubtitleReapplyOrdinal. This is the sharpest edge: the host
reacts to AirPlay starting by calling setNativeSubtitleRendering(true)
(per the documented contract), which sets the reapply ordinal — and the
engine's own AirPlay reload, triggered by the same KVO flip, then nils it
in stopInternal. Net effect: the receiver renders no subtitles at
all, even for plain embedded text tracks, unless the host knows to
re-assert after an engine-initiated reload it never asked for.
Notably the #65/#93 stall-recovery path already handles this correctly for
in-place item swaps — after host.load(url:startPosition:inPlaceSwap: true)
it replays nativeSubtitleReapplyOrdinal onto the fresh item. The AirPlay
reload deserves the same courtesy, extended to all three items above.
Surfaced by a Prism beta report: streaming iOS → tvOS via AirPlay, "the
subtitle selection dropdown is tripping out" (the wipe/republish churn
re-rendering an open menu) "and no data seems to be sent to the AirPlay
server side" (items 2 and 3, plus overlay-only mid-session tracks).
Repro
Host app: load() a stream with prepareNativeSubtitles = true, an
embedded text subtitle track active, and one track added mid-session via addExternalSubtitleTrack.
Start playback, then AirPlay the video to an Apple TV (wireless — the
wired-HDMI branch doesn't reload and is unaffected).
Call setNativeSubtitleRendering(true) on the external-playback flip (as
documented for PiP/AirPlay).
Observe: [AirPlay] external playback active (wireless) -> LAN media reload, then:
subtitleTracks republishes without the mid-session external track;
activeSubtitleTrackIndex is nil (or the preferred-language auto-pick,
not the user's track);
no legible rendition is selected on the receiver — the Sodalite#38
force-deselect loop runs because nativeSubtitleReapplyOrdinal is nil
again.
(Feature, separable) Native-rendition eligibility for addExternalSubtitleTrack: append a WebVTT rendition + cue store to the
live loopback master/media playlists so mid-session tracks can survive
PiP/AirPlay without the host performing a full re-declared reload. Today
the host's only route is a position-preserving load() with the track in LoadOptions.externalSubtitles, which costs a visible rebuffer — during
AirPlay, exactly when the user is most likely to be adding subtitles.
Host-side workaround (shipping in Prism meanwhile)
On every isExternalPlaybackActive flip: freeze the host's track/selection
mirrors against the reload's transient wipe publishes, wait for the reload to
settle, re-addExternalSubtitleTrack the dropped tracks, re-apply the
session's picks, promote an overlay-only active pick via a re-declared reload,
and re-call setNativeSubtitleRendering. It works, but it's ~150 lines of
host code compensating for state the engine could preserve in one place.
Summary
When wireless AirPlay engages or ends, the engine reloads itself to move the
loopback playlist onto the device's LAN IP and back
(
handleExternalPlaybackChange→reloadAtCurrentPosition(), the #86 path).That reload is a full
load(), andload()'sstopInternalwipes threepieces of session state the host set up — none of which the engine restores:
addExternalSubtitleTrack,Select external subtitles to make them appear in
engine.$subtitleTracks. #88).load()clearsexternalSubtitleRegistry/subtitleTracks/nextExternalSubtitleOrdinaland re-registers onlyLoadOptions.externalSubtitles. Every track the host added after load ispermanently gone — the host's menu still lists them, but selecting one
targets a synthetic id that no longer exists.
preferred-language auto-selection, which can silently override the user's
explicit picks (including subtitles they turned off).
nativeSubtitleReapplyOrdinal. This is the sharpest edge: the hostreacts to AirPlay starting by calling
setNativeSubtitleRendering(true)(per the documented contract), which sets the reapply ordinal — and the
engine's own AirPlay reload, triggered by the same KVO flip, then nils it
in
stopInternal. Net effect: the receiver renders no subtitles atall, even for plain embedded text tracks, unless the host knows to
re-assert after an engine-initiated reload it never asked for.
Notably the #65/#93 stall-recovery path already handles this correctly for
in-place item swaps — after
host.load(url:startPosition:inPlaceSwap: true)it replays
nativeSubtitleReapplyOrdinalonto the fresh item. The AirPlayreload deserves the same courtesy, extended to all three items above.
Surfaced by a Prism beta report: streaming iOS → tvOS via AirPlay, "the
subtitle selection dropdown is tripping out" (the wipe/republish churn
re-rendering an open menu) "and no data seems to be sent to the AirPlay
server side" (items 2 and 3, plus overlay-only mid-session tracks).
Repro
load()a stream withprepareNativeSubtitles = true, anembedded text subtitle track active, and one track added mid-session via
addExternalSubtitleTrack.wired-HDMI branch doesn't reload and is unaffected).
setNativeSubtitleRendering(true)on the external-playback flip (asdocumented for PiP/AirPlay).
[AirPlay] external playback active (wireless) -> LAN media reload, then:subtitleTracksrepublishes without the mid-session external track;activeSubtitleTrackIndexis nil (or the preferred-language auto-pick,not the user's track);
force-deselect loop runs because
nativeSubtitleReapplyOrdinalis nilagain.
Asks
today; anything else that calls
reloadAtCurrentPosition()internally):re-register mid-session external tracks (preserving their synthetic ids if
possible), re-select the previously active audio/subtitle tracks instead
of re-running auto-selection, and replay
nativeSubtitleReapplyOrdinalthe way the Presented frame drifts ~6 s ahead of currentTime/sourceTime after a rapid bidirectional seek burst (loopback-HLS native path) — frameAhead/producerShift/hostShift all report 0 #65 recovery already does.
addExternalSubtitleTrack: append a WebVTT rendition + cue store to thelive loopback master/media playlists so mid-session tracks can survive
PiP/AirPlay without the host performing a full re-declared reload. Today
the host's only route is a position-preserving
load()with the track inLoadOptions.externalSubtitles, which costs a visible rebuffer — duringAirPlay, exactly when the user is most likely to be adding subtitles.
Host-side workaround (shipping in Prism meanwhile)
On every
isExternalPlaybackActiveflip: freeze the host's track/selectionmirrors against the reload's transient wipe publishes, wait for the reload to
settle, re-
addExternalSubtitleTrackthe dropped tracks, re-apply thesession's picks, promote an overlay-only active pick via a re-declared reload,
and re-call
setNativeSubtitleRendering. It works, but it's ~150 lines ofhost code compensating for state the engine could preserve in one place.