Skip to content

fix(playback): restore VC-1 references across loads and source seeks - #979

Draft
orut34iop wants to merge 6 commits into
superuser404notfound:mainfrom
orut34iop:codex/fix-vc1-paused-resume-reference-chain
Draft

orut34iop wants to merge 6 commits into
superuser404notfound:mainfrom
orut34iop:codex/fix-vc1-paused-resume-reference-chain

Conversation

@orut34iop

@orut34iop orut34iop commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

A seek into an index-less VC-1 M2TS can start without the references needed for its target picture. A paused cold load then holds the concealed first frame; a running decoder quickly replaces it. This revision follows the maintainer's review by sharing bounded reference preroll between nonzero loads and transport source seeks, independent of paused/playing intent.

  • Seekable, non-live software VC-1 only. Cache hits retain their existing preroll and do not seek the source; cache misses use the shared entry after readers are parked.
  • Preparation looks back four seconds and admits a key at/before the target, at most eight seconds earlier. Inconclusive preparation (including EOF, missing/future key, errors and limits) falls back to the existing reposition at the original target. Preparation uses at most eight seconds within the existing total 30-second positioning budget; fallback receives the remainder. Lock waits/blocking filesystem reads are not hard wall-clock bounded.
  • Generation/cancellation checks suppress obsolete admission and fallback. Only cancellation of a whole load closes its demuxer; transport cancellation leaves it reusable. A cancelled caller without a successor settles the host window as a positioning failure instead of leaving it parked indefinitely.
  • Removed the load-only initiallyPaused wiring. Target PTS filters, clocks, playback intent and paused-seek held-picture behavior are preserved. No FFmpeg or pixel-heuristic change.

The local same-file comparison found a gray first decoder output after both cold open and warm flush at two nonzero-origin targets; shared preparation produces scene contrast in all four cases. Ordinary paused host seek parks decoding and retains the old layer, so this is not a claim of sustained gray reproduced on a TV during paused transport seek. No media, frame image, private path or credentials are distributed.

Validation and fixture contract: docs/vc1-paused-resume.md. Policy/host regressions cover preserved keys, missing/future keys, EOF and bounds, fallback success/failure and remaining budget, cancelled load interruption, cancelled/superseded transport reuse, and cancellation without a successor. Real cold-load and synchronous cold/warm first-output tests remain opt-in for the documented locally authorized fixture.

Still Draft: the redistributable VC-1 fixture/default-CI real-decoder gate remains open. Local real-media checks do not close that gate or replace physical-device acceptance. No downstream private-kernel revision or APP pin is changed.

Current head: 7cbdf6de680e529626408bef19b5cb534569b968. Refreshed by ordinary merge of official a6fbf0a1; only CHANGELOG had a textual conflict, and both entries were retained. Final refreshed tree: signed build and 54 affected tests in ordinary/strict-pool runs passed, including the opt-in real VC-1 cases, seek/cancellation/cache and WMV coverage. Docs/hygiene/diff checks passed. Before refresh, implementation f2c65cab also passed the complete default parallel suite (5704 Swift Testing cases in both ordinary and strict runs, plus XCTest); that is not substituted for refreshed-head CI. Latest full CI: default macOS passed (5727 Swift Testing cases in both ordinary and strict-pool runs, plus the independent cache/timestamp scripts); docs/hygiene passed. All-traits macOS and both simulator jobs were cancelled before acquiring a runner, with no steps executed. Each annotation reports: “The job was not acquired by Runner of type hosted even after multiple attempts”. These three lanes remain unverified on this head. A failed-jobs rerun request was rejected because repository admin rights are required. An upstream maintainer can retry with gh run rerun 41863087587 --failed; no code failure has been identified and no empty commit or workflow change is used to retrigger it.

@orut34iop

Copy link
Copy Markdown
Contributor Author

The follow-up in fe81aa9e now directly tests cancellation after reference preparation has entered a blocked positioning operation, using the same async entry point as the software host. The 36 affected tests pass locally in ordinary and strict cooperative-pool runs; the new CI run is still in progress. This is interruption coverage, not VC-1 picture-decoding evidence.

The remaining ready gate is a redistribution-cleared VC-1 fixture that reproduces the cold paused-resume reference failure. I have not included the downstream movie or a clip of it. The existing opt-in harness documents its exact source-origin/target contract, and the PR remains a draft.

Does the project have a VC-1 sample with clear redistribution permission that we could adapt for this regression? Alternatively, would you consider reviewing a source-supplied reproduction workflow and before/after decode evidence without distributing the movie, and what evidence would you require? I will keep the current public-fixture gate and draft status until that validation approach is explicitly agreed; policy-test/CI success alone is not being presented as proof of the decoded picture fix.

@orut34iop

Copy link
Copy Markdown
Contributor Author

Additional local real-decoder evidence is now available for the extracted PR itself, without distributing media:

Session target (source origin 600 s) Exact upstream production Sources 61887f0f PR fe81aa9e
789.92911257 s gray: luma 128...128; scene assertion fails luma 20...176; all assertions pass
796.46233333 s gray: luma 128...128; scene assertion fails luma 20...166; all assertions pass

macOS 26.7.1, Xcode 27.2 (27B52), signed test binaries. Ordinary and strict cooperative-pool runs agree. The baseline Sources tree was verified byte-for-byte against the upstream commit; the added opt-in harness only omits the new initiallyPaused argument for that baseline's signature. After restoring the submitted source and rebuilding, both cases pass again. The paused clock, zero playback rate, source origin and rendered-picture PTS assertions also pass on the PR. The contrast check is specific to these scenes, not a production pixel heuristic.

CI for the exact submitted head has also passed all build/test lanes. That CI still skips the source-supplied real-decoder harness. This strengthens the local before/after evidence but does not supply the missing redistributable fixture; the draft and the validation-approach question above remain open. No clip, frame image, private path or access URL has been uploaded.

@superuser404notfound

Copy link
Copy Markdown
Owner

Thanks for extracting this and for the before/after luma numbers, that is a clear reproduction. Agreed that it stays a draft until the redistributable fixture exists. Three points on the design before then:

  1. The failure mode should be the old path, not a failed load. prepare throws repositionFailed when no key packet is found within the window (an unreliable key flag in an index-less TS, a GOP longer than the window, a slow source hitting the 8 s budget). That error ends the whole load, where today the same session at worst shows a concealed picture for one GOP. Please fall back to the existing reposition(...) call when priming does not find a key, and keep the throw only for cancellation/stop.

  2. Scope. The gate is initiallyPaused && VC-1 && software decoder, but the cause it addresses (a seek into index-less M2TS lands without the references for the target) is not specific to a cold paused load. A paused transport seek on the same file should hit the same gray picture, and while playing it is only hidden by the next GOP. Before landing a special case I would like to know whether the paused seek reproduces too. If it does, the fix belongs where both paths meet (the post-seek keyframe guarantee in the demuxer/decoder skip), not in the load path.

  3. Small thing. markClosed() in onCancel is correct only because a cancelled load tears the demuxer down anyway. A one-line comment saying so would keep the helper from being reused somewhere the demuxer survives.

None of this blocks the fixture work. FFmpeg has no VC-1 encoder, so the fixture probably has to be a short permissively licensed VC-1 sample remuxed to M2TS, with source and hash recorded as docs/vc1-paused-resume.md already describes.

@orut34iop orut34iop changed the title fix(playback): prime paused VC-1 cold resumes from a reference keyframe fix(playback): restore VC-1 references across loads and source seeks Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants