Skip to content

fix: unbreak the regression suite on Windows (PSNR filtergraph escaping + console window popups) - #3839

Open
Bforbtay wants to merge 2 commits into
heygen-com:mainfrom
Bforbtay:fix/windows-ffmpeg-console-and-psnr-escaping
Open

fix: unbreak the regression suite on Windows (PSNR filtergraph escaping + console window popups)#3839
Bforbtay wants to merge 2 commits into
heygen-com:mainfrom
Bforbtay:fix/windows-ffmpeg-console-and-psnr-escaping

Conversation

@Bforbtay

Copy link
Copy Markdown

What

Two independent Windows fixes found while setting the repo up on Windows 11:

  1. PSNR stats_file escaping (packages/producer/src/regression-harness.ts) — the regression suite's visual comparison could not run at all on Windows.
  2. windowsHide: true on the 11 remaining child_process call sites that lacked it, across core, engine and producer.

Why

PSNR escaping. An ffmpeg filtergraph argument is unescaped twice: once when the graph is split into filters and their options, then again when the option value itself is read. psnrAtFrames escaped \ and : a single time, which only survives the first pass.

On POSIX this is invisible, because mkdtemp under tmpdir() produces neither character. On Windows the stats path is always C:\Users\..., so it reaches the option parser with a bare :, which starts a new option and fails the whole graph:

[AVFilterGraph] No option name near '\Users\btay\AppData\Local\Temp\hf-psnr-CeMZ8F\psnr.log'
[AVFilterGraph] Error parsing filterchain '[rv][gv]psnr=shortest=1:repeatlast=0:stats_file=C\:\\Users\\btay\\...\\psnr.log'
Error : Invalid argument

Every suite that got as far as quality validation died there. A full Windows run reported 73 total / 0 passed / 73 failed, but only 2 of those failed at compilation and 1 at visual comparison — the other ~70 never got to compare anything.

windowsHide. Node's windowsHide defaults to false, so every child process that is a console application pops a window on the user's desktop. Over a render — let alone a regression run — that is a steady stream of black windows stealing focus. The repo already sets the flag across cli, studio-server and most of engine; these were the remaining gaps, all on paths that run during a normal render.

Worth calling out one of them: probeNvidiaVramMb in engine/browserManager uses execSync, which goes through a shell, so it flashes a cmd.exe window even on machines with no NVIDIA GPU — where the command exists only to fail.

How

PSNR escaping — escape for both passes. Verified against ffmpeg 9.0.1 on Windows with an identical fixture:

stats_file argument result
C\:\\Users\\... (single escape, previous) fails — reproduces the error above
C\\:\\\\Users\\\\... (double escape, this PR) works
C\:/Users/... (forward slashes, single-escaped colon) fails
C\\:/Users/... (forward slashes, double-escaped colon) works
relative filename + cwd works

Double-escaping was chosen over the cwd approach because it is a one-line change that keeps runFfmpeg's signature and does not require the other path arguments to be absolute. It is a no-op on POSIX.

windowsHide — added to the call sites that lacked it:

  • core/mediaGradeAnalyzer — ffprobe + ffmpeg, once per analyzed media file
  • engine/browserManager — the nvidia-smi VRAM probe described above
  • producer — the regression harness ffmpeg runner, the PSNR/audio comparison helpers, fixture synthesis, and the ffprobe call in audioPadTrim

The two concerns are separate commits, so either can be dropped independently.

Test plan

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (if applicable)

Environment: Windows 11 Pro 26200, Node v24.19.0, bun 1.4.2, ffmpeg 9.0.1.

  • bun run --filter '*' typecheck — all 14 packages exit 0
  • bunx oxlint / bunx oxfmt --check on the changed files — 0 warnings, 0 errors
  • lefthook pre-commit (format, fallow, typecheck, commitlint) green on both commits
  • PSNR fix, before: every suite reaching quality validation failed with No option name near '\Users\...'
  • PSNR fix, after: bunx tsx src/regression-harness.ts sub-comp-t0 completes all 100 checkpoints and passes — Total: 1 | Passed: 1 | Failed: 0, visual PASSED (3 failed frames, threshold 5), audio PASSED (correlation 1.000, lag 0)

No POSIX behavior change in either commit, so this needs a look from someone on macOS/Linux only insofar as CI covers it.

Two notes for maintainers, both out of scope here and neither touched by this PR:

  • The regression fixtures fetch source media from gen-os-static.s3.us-east-2.amazonaws.com. That host is sinkholed to 0.0.0.0 by Cloudflare's malware-filtering resolver (1.1.1.2), which silently strips data-end / data-duration and shows up as a golden-snapshot mismatch rather than a network error. Took a while to identify; may be worth a clearer diagnostic.
  • mediaTypeTestFixtures and a couple of extractor tests still pass -vsync, which ffmpeg 9 removed in favour of -fps_mode. Test-only — the production paths already use -fps_mode.

🤖 Generated with Claude Code

Bforbtay and others added 2 commits September 10, 2026 14:57
Node's `windowsHide` defaults to false, so every child process that is a
console application pops a window on the user's desktop. During a render
or a regression run that is a stream of black windows stealing focus.

The repo already sets `windowsHide: true` across cli, studio-server and
most of engine; these were the remaining gaps, all on paths that run
during a normal render:

- core/mediaGradeAnalyzer: ffprobe + ffmpeg, once per analyzed media file
- engine/browserManager: `nvidia-smi` VRAM probe. This one goes through a
  shell (`execSync`), so it flashes a cmd.exe window even on machines with
  no NVIDIA GPU, where the command exists only to fail
- producer: the regression harness ffmpeg runner, PSNR/audio comparison
  helpers, fixture synthesis, and the ffprobe call in audioPadTrim

No behavior change on POSIX, where the flag is ignored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ph parse

An ffmpeg filtergraph argument is unescaped twice: once when the graph is
split into filters and their options, then again when the option value is
read. `psnrAtFrames` escaped `\` and `:` a single time, which only survives
the first pass.

On POSIX that is invisible, because mkdtemp under tmpdir() produces neither
character. On Windows the stats path is always `C:\Users\...`, so it reaches
the option parser with a bare `:`, which starts a new option and fails the
whole graph:

    [AVFilterGraph] No option name near '\Users\...\psnr.log'
    Error parsing filterchain '[rv][gv]psnr=...:stats_file=C\:\Users\...'

Every suite that got as far as quality validation died there, so a Windows
regression run reported 0 passes with only 2 genuine compilation failures.
Escaping for both passes fixes it and stays a no-op on POSIX.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant