fix(routing): let a decode-path correction reach a custom source (#461 follow-up) - #473
Merged
Merged
Conversation
…follow-up) `LoadOptions.preferredDecodePath` is read at three sites, all inside `load`. The rebuild that keeps a retained `IOReader` picks its host from the backend the session was already on, so a host correcting a playing custom source onto the software path through #460 was told the correction had been applied and stayed native: accepted, named in the log, ignored. That is the one outcome #460's own rules forbid, and it left the escape unreachable for exactly the population it was built for, a spool-backed source with no second engine to hand a struggling stream to. The reopen now asks the same routing policy `load` asks, seeded with the backend it is on. The one-way type is what makes that safe: `DecodePath` has no `.native`, so the only flip reachable is native to software, and the software path is the general one; `loadNative` on a software-routed source stays unreachable by construction rather than by a comment. `keepNativeHost` and the two post-load fixups follow the target route, not the previous one. Measured with a new `--reload-decode-path` arm on `customio`, on a seekable VOD custom source and on the live spool reader: RELOAD applying preferredDecodePath=software: playhead=0.00s backend=native #461: reload re-routing this session native -> software RELOAD done: backend native -> software, decoder=libavcodec H264 (SW) The same run before this reported `backend native -> native, decoder= VideoToolbox H264 (HW)` under an applied-correction log line. On live the rebuild keeps #460's reach-back of 0.0 MB, and the footprint step across the switch (25 to 66 MB, then flat at 67 for 140 s, ratio 0.18 and falling) is the software pipeline's baseline, not retention. What the software path cannot REPRESENT is refused before any teardown, with two new `SessionReloadRefusal` cases: `.softwarePathCannotRepresentSource` (IPT-PQ-c2, HEVC P5 / AV1 P10.0) and `.demuxedAudioLiveIsNativeOnly`. Both guards live after the routing decision inside `load`, so on a correction they would have failed a session already torn down; a refusal has to cost nothing. Deciding them early needs the base-layer compatibility id to outlive the probe, so it is retained next to `sourceDVProfile`. Verified on the Dolby browser test kit, which carries its own control: the Profile 5 cut is refused and left playing on VideoToolbox, the Profile 8.1 cut of the same material and grading is honoured and rebuilds in software. Reported by @cmcpherson274. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4QpZDsHPJfbz5gkxxPXF7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #461, reported by @cmcpherson274 after taking the escape onto his own stack.
The defect
LoadOptions.preferredDecodePathis read at three sites, all insidepublic func load(. The rebuild that keeps a retainedIOReaderpicks its host fromwasOnSoftwarePath, the backend the session was already on, and never reads the option. So a host correcting a playing custom source onto the software path through #460 got the correction written intoloadedOptions, logged as applied, and then ignored.Accepted, named in the log, ignored is the one outcome #460's own rules forbid, and it left the escape unreachable for exactly the population it was built for: a spool-backed source with no second engine to hand a struggling stream to.
The docs were worse than silent on it. "Where a correction lands" said the routing fields all apply on the custom path, which is the opposite of what happened.
The fix
The reopen asks the same routing policy
loadasks, seeded with the backend it is on.keepNativeHostand the two post-load fixups (the native-only live watchdog, the software-only track republish) follow the target route rather than the previous one.The one-way type is what makes re-routing this path safe.
DecodePathhas no.native, so the only flip reachable is native to software, and the software path is the general one;loadNativeon a software-routed AV1 source, which the code comment there warns about, stays unreachable by construction rather than by asking the next reader not to write it.The refusal
What the software path cannot REPRESENT is refused before any teardown, with two new
SessionReloadRefusalcases:.softwarePathCannotRepresentSource(IPT-PQ-c2: HEVC P5, AV1 P10.0) and.demuxedAudioLiveIsNativeOnly. Insideloadboth guards run AFTER the routing decision, so reaching them on a correction means failing a session that is already down, and a refusal has to cost nothing. Deciding them early needs the base-layer compatibility id to outlive the probe, so it is retained next tosourceDVProfile.This is narrower than the refusal the report offered to consume: only a flip is refusable, and only for a source that genuinely cannot be served, so the sources that CAN use the escape keep it.
Measured
New
--reload-decode-patharm oncustomio, because the existing--reload-atcorrection (anhttpHeadersprobe) is inert on a custom source by design and can never measure this field.Seekable custom VOD source and the live spool reader:
The same run with the routing change reverted:
backend native -> native, decoder=VideoToolbox H264 (HW), under an applied-correction log line.On live the rebuild keeps #460's reach-back of 0.0 MB. The footprint step across the switch (
physFP25 to 66 MB, then flat at 67 for 140 s, ratio 0.18 and still falling) is the software pipeline's own baseline, not retention; a 100 s run reads 1.31 purely because the slope anchor at 60 s lands on the switch.Refusal arm, on the Dolby browser test kit because it carries its own control:
Plain reload arms unchanged, no
#461line,backend native -> native, reach-back 0.0 MB.10 new unit tests; full suite green on both runners (swift-testing 2595 / 355 suites, XCTest 606 with 0 failures).
🤖 Generated with Claude Code
https://claude.ai/code/session_01Q4QpZDsHPJfbz5gkxxPXF7