fix(probe): evidence in hand outranks the budget that bounded fetching it - #586
Merged
Merged
Conversation
…g it Follow-up to PR #583, whose validator and whole-probe controls are right in the core. Four places where a bound meant to limit COST was also deciding what the probe may report. The HDR10+ validator discarded a message it had already parsed in full whenever anything later in the same packet failed the structural walk. Verified against the scan directly: a valid ST 2094-40 SEI followed by a NAL with its forbidden bit set, or by an SEI missing rbsp_trailing_bits, came back negative, where the byte scan this replaced said yes. That is ordinary shape on real media, and it reaches the playback path through HLSSegmentProducer, not just the probe. The walks now stop at the damage and report what they validated. Nothing can invent a positive there: only validT35 ever sets one. The HDR10+ and Atmos detail passes retracted a detection they had already made when their soft wall-clock budget expired mid-pass. The caller cannot tell a withheld confirmation apart from a source that carries none, so an overrun on a slow origin silently became "no Atmos", on the existing API with no opt-in. An overrun now retires the pass without taking its answer back. The whole-probe deadline from #583 is untouched: that one throws, so the caller knows. Passing `limits` switched the open to the still extractor's profile, so probe(url:limits:) read 2 MiB / 2 s where probe(url:) reads 50 MiB / 60 s and could report fewer streams for it. The caller's own limits are the only thing that should bind: the profile is built from .playback with probesize clamped to maxInputBytes, and the deadline plus the FFmpeg interrupt callback bound the analysis. The recordless Dolby Vision audit stays out of reach, and docs/api.md now says so in terms of what a host observes: that audit opens the source a second time by URL, outside this probe's budget and cancellation. A controlled HTTP probe took the origin slot with tryAcquire and failed with sourceBusy the instant any other request held it, which is the ordinary shape when a host probes several items off one server. It waits for the slot until its own deadline instead, since a slot wait is the one wait the watchdog cannot interrupt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015PM3xUJB6ZQyqnmGK1fp6F
4 tasks
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 #583, whose validator and whole-probe controls are right in the core. Four places where a bound meant to limit cost was also deciding what the probe may report.
1. A validated message was withdrawn by damage next to it
HDR10PlusMetadataScancollected a confirmation infoundand then threw it away whenever anything later in the same packet failed the structural walk. Verified against the scan directly, before this change:The byte scan this replaced answered yes to all three. A vendor SEI, a trailing byte or a second unreadable NAL next to the metadata is ordinary shape on real media, and this reaches the playback path through
HLSSegmentProducer, not only the probe. The NAL walk, the OBU walk and the SEI message walk now stop at the damage and report what they validated. Nothing can invent a positive there: onlyvalidT35ever sets one.2. A pass retracted a detection it had already made
The HDR10+ and Atmos detail passes turned a confirmation into
.timeCap(anddecodedProfile: nil) when their soft wall-clock budget expired mid-pass. A caller cannot tell a withheld confirmation apart from a source that carries none, so an overrun on a slow origin silently became "no Atmos" on the existing API, with no opt-in. An overrun now retires the pass without taking its answer back.The whole-probe deadline from #583 is untouched. That one throws, so the caller knows.
3.
limitsalso chose how deeply to analysePassing
limitsswitched the open to.stillExtraction, soprobe(url:limits:)read 2 MiB / 2 s whereprobe(url:)reads 50 MiB / 60 s, and could report fewer streams for it. The caller's own limits should be the only thing that binds: the profile is built from.playbackwithprobesizeclamped tomaxInputBytes, and the deadline plus the FFmpeg interrupt callback bound the analysis instead.The recordless Dolby Vision audit (#567) stays out of reach, and
docs/api.mdnow states it in terms of what a host observes rather than the mechanism: that audit opens the source a second time by URL, traffic this probe's budget and cancellation do not police, so an untagged 10-bit HEVC source comes back without a DV record from a controlled probe while an uncontrolled one synthesizes it.4.
sourceBusywaited zeroA controlled HTTP probe took the origin slot with
tryAcquireand failed the instant any other request held it, which is the ordinary shape when a host probes several items off one server. It waits until its own deadline instead, since a slot wait is the one wait the deadline watchdog cannot interrupt.Test plan
swift test, real exit code 0: 3298 swift-testing tests in 441 suites and 642 XCTest cases, checked in both halves rather than through a pipe.Scripts/check-doc-links.pyclean.malformedNALsandav1Malformedkeep every case where nothing was validated (those still fail closed), and the cases where a validated message precedes the damage moved into a new test that pins the new contract. The "found at the deadline is not published" integration test became "found as the deadline passes is still published".remainingTimeonProbeControl, the bound the slot wait now uses.One finding is worth keeping in view: the "missing
rbsp_trailing_bits" case is only observable under length-prefixed framing. In Annex B the zero bytes ahead of a start code are stripped, and the payload's own zero padding goes with them, so that case truncates the message instead of damaging what follows it and correctly stays negative.🤖 Generated with Claude Code
https://claude.ai/code/session_015PM3xUJB6ZQyqnmGK1fp6F