6.15.0 - The route the session actually took
One addition: the engine now says which pipeline is actually serving the session. Drop-in from 6.14.0, no source change required on the consumer side.
The request was never the outcome
LoadOptions.nativeRemoteHLS records what the host asked for. The engine can change the route after that, on its own findings, and a host had no way to learn it had:
| Move | Trigger | When |
|---|---|---|
| bypass -> ingest loopback | #168 carriage watchdog: readyToPlay arrives, but AVFoundation builds no video track for a master that advertises one (HEVC in MPEG-TS) |
mid-session |
| bypass -> ingest loopback | #199: that master's verdict is remembered, so the doomed mount is skipped | at load |
| bypass -> ingest loopback | AE#268: playlist plus the first segment's PMT identify a finite HEVC-in-MPEG-TS VOD | at load |
| loopback -> bypass | AE#154 / AE#246: an HLS playlist reached the loopback path, which cannot demux it | at load |
loadedOptions is internal, and playbackBackend reads .native for both native pipelines, so the split between them was invisible from outside. The mid-session reroute existed only as a log line.
videoRoute
player.$videoRoute // .none / .remoteBypass / .loopback / .software / .audio.remoteBypassis AVPlayer on the origin URL: no demuxer, no local server. AVFoundation owns media selection, buffering and subtitle drawing..loopbackis the demuxer plus the local HLS-fMP4 server, the default video route. The engine owns the source connection and the subtitle pipeline..software,.audioand.nonemirror the corresponding backends.
The value hosts most need it for is subtitle ownership: on the bypass AVPlayer draws the origin's own legible renditions, on the loopback and the software path the host's renderer does. A host that guesses wrong draws nothing, or draws every cue twice.
It is derived, not a second copy of the session state: a pure fold over playbackBackend and the session's effective options, recomputed from each one's didSet and assigned only on a real change. That is the arrangement playbackPhase uses, and it is what keeps a reroute added later from being able to forget the route, since every reroute already writes one of those two properties.
Two properties of the value worth knowing before you write against it:
- A declared bypass is not a route. Until a backend exists the value stays
.none, so a load that reroutes before it ever mounts natively never publishes a bypass it did not take. - Teardown drops it back to
.none, like every other per-session published value.
playbackBackend stays what it was, a diagnostic. Branch on videoRoute.
Verification
1606 tests in 238 suites, -strict-concurrency=complete clean, tvOS and iOS Simulator builds green. On real media: a local MP4 settles on route=loopback, and Apple's bipbop_adv_example_hevc master takes the AE#154 reroute and publishes remoteBypass at the moment of the reroute, with no .loopback emission in front of it.
aetherctl play now prints route= next to backend=, so the value is capturable from the harness without a device.