Fix #184: pin verified FFmpeg build and validate extracted frames - #192
Conversation
Repair FFmpeg previously downloaded BtbN's rolling latest build. FFmpeg's release/8.1 branch picked up a CUDA rework on 2026-06-29 whose builds can write corrupt EXR frames (invalid zlib chunks) during hardware-decode extraction, and can abort with allocation failures in the EXR encoder. Users who ran Repair FFmpeg after June 30 got that build, and every AI method then failed with 'failed to read frame' on affected clips. ☼ Pin Repair FFmpeg to the dated immutable autobuild 2026-06-27 (tag-exact n8.1.2), verified against the issue #184 reproduction clip. No rolling tags, no release-API dependency. ☼ Flag installed post-tag n8.1.2 rolling builds at startup and show a dedicated diagnostic that walks the user to Repair FFmpeg. ☼ Surface DWAB recompression read failures instead of swallowing them; the pass now doubles as a frame integrity check and only writes the .dwab_done marker when every frame reads back. ☼ Retry corrupt extractions once in safe mode (software decode plus single-threaded EXR encode), re-validate with a pool-free sequential recompress, and raise an actionable error if frames are still unreadable. The frame-corruption race exists at low rate in older builds too, so validation and retry protect every install. ☼ Keep recompression alive when a worker process dies (BrokenProcessPool is now a per-frame failure, not a silent abort), and log the tail of child stderr where the actual exception lives. ☼ Word-wrap the startup diagnostic detail label so long messages are never clipped. ☼ Tests: pinned-asset resolver, known-bad detection, safe-mode retry, sequential fallback wiring (28 ffmpeg-tools tests, full suite green).
In-app QA of the frame-corruption fix surfaced three adjacent defects. ☼ Adaptive EXR encoder threading. The encoder is frame-threaded and commits ~52 bytes per pixel per thread (measured: 435 MB per thread at 4K, 15.6 GB total at auto threads on a 32-core machine). Machines without that much commit headroom failed 4K imports with 'Cannot allocate memory'. Extraction now probes free commit via GetPerformanceInfo and caps encoder threads to what the machine can afford (half of free commit, floor 2, ceiling cores). Machines with headroom keep full-speed auto threading; loaded or small machines import slower instead of failing. ☼ Out-of-memory failures now raise a human message naming the cause and the remedy instead of a raw codec error. ☼ Extraction failures are visible: the viewer re-renders instead of keeping the stale 'Extracting frames...' placeholder forever, known failure signatures open the diagnostic dialog with fix steps, and unknown ones show a warning box. Previously the only signal was an error sound and a log line. ☼ RUN EXTRACTION now retries clips in the ERROR state (wiping partial frames first via the existing retry path). The button was silently dead after any failure. ☼ Tests: adaptive cap decision table, cap plumbing into the FFmpeg command, OOM message, hermetic commit-probe isolation for existing tests (37 ffmpeg-tools tests, full suite 739 green).
|
In-app QA drove three more fixes onto this branch (68863b3): ☼ Adaptive EXR encoder threading. Bench on the reporter-class pipeline: the encoder is frame-threaded at ~52 bytes/pixel/thread. At auto threads a 32-core machine commits 15.6 GB for one 4K import and fails with 'Cannot allocate memory' when commit headroom is smaller. Extraction now probes free commit and caps encoder threads to what the machine can afford; fast machines keep auto speed.
☼ Out-of-memory extraction failures raise a human message instead of a raw codec error. QA evidence: poisoned-build startup modal, blocked import, Repair flow installing the pinned build, and status turning green were all exercised on the live app. |
The issue #184 install held a master nightly delivered by the 2.1.0-era Repair. The stable-build fix in 2.1.2 only changed future downloads and never audited what was already installed, so the corrupting build survived two updates undetected. Any dev/nightly build now fails validation with the repair prompt, exact reported version string covered by tests.
|
Reporter's version string came back: a master nightly (N-125505, built 2026-07-08), delivered by the 2.1.0-era Repair on his install day and never replaced or flagged by later updates. Postdates the CUDA rework, same corruption class as reproduced here. That exposed a detection gap: the known-bad pattern only matched post-tag stable builds. 67cea6a flags every development nightly as needing repair, with his exact version string in the tests. Full suite 741 green. |
Fixes #184.
Problem
Repair FFmpeg downloaded BtbN's rolling latest build. FFmpeg's release/8.1 branch picked up a CUDA rework on 2026-06-29 (after the n8.1.2 tag) whose builds can write corrupt EXR frames during hardware-decode extraction: invalid zlib chunks inside ZIP16 files. Reproduced locally on the reporter's exact clip with the exact extraction arguments the app generates. The same builds can also abort with allocation failures in the EXR encoder. Every user who ran Repair FFmpeg after June 30 received a defective build, and AI methods then failed with 'failed to read frame'.
Testing also showed the corruption race fires at a low rate on older builds, so validation has to protect every install, not just poisoned ones.
Fix
☼ Repair FFmpeg now downloads a pinned, dated, immutable autobuild (2026-06-27, tag-exact n8.1.2), verified against the reproduction clip. No rolling tags, no release-API dependency.
☼ Startup validation flags installed post-tag n8.1.2 rolling builds and shows a dedicated diagnostic that walks the user to Repair FFmpeg.
☼ The DWAB recompression pass reads every frame back and now reports unreadable frames instead of swallowing them. The .dwab_done marker is only written when every frame is clean.
☼ Corrupt extractions retry once in safe mode (software decode plus single-threaded EXR encode), re-validate with a pool-free sequential recompress, and raise an actionable error if frames are still unreadable.
☼ A worker process dying (BrokenProcessPool) or a crashed recompress subprocess now counts as per-frame failure and feeds the retry, never a silent 'clean'.
☼ Hardware-decode extraction failures on a resumed import now restart from frame 0 after the wipe, instead of seeking past the deleted prefix.
☼ Diagnostic dialogs: scroll viewports keep the app background (no white panel), long detail lines word-wrap.
Verification
☼ Pinned asset re-downloaded fresh and hash-matched against the build validated on the dev machine; EXR encoder confirmed present.
☼ Reproduction matrix on the reporter's clip: corruption reproduced on rolling builds with CUDA decode, encoder allocation failures reproduced with single-threaded encode on rolling builds, pinned build clean across runs.
☼ 30 ffmpeg-tools tests covering the pin, known-bad detection, retry flow, resume interaction, and subprocess failure semantics. Full suite: 732 passed.
☼ Startup modal exercised end to end against a simulated poisoned install (real diagnostics routine, real dialog, app theme).