fix(producer): audioPadTrim FFmpeg-8.x-compatible apad invocation#2525
Conversation
The `audioPadTrim` module's pad-concat step generates a concat script
whose file directives use `file://` URLs (built via Node's
`pathToFileURL`). FFmpeg 8.x on Windows rejects these with
"Impossible to open file:///C:/…" — its `file:` protocol handler strips
the scheme leaving `///C:/…`, which Windows path parsing then rejects.
Field-signal (4 reports over ~24h, all win32/x64, CLI 0.7.59):
- ts=1784169914 (Baoyu, 60s render, native audio assembly failed)
- ts=1784177061 (andre 22cores, 345.87s composition, 9 WAV audio elements)
- ts=1784177375 (KEY DIAGNOSTIC: 13 mono 44.1kHz mp3 tracks, ffmpeg
8.1.1-full_build gyan.dev, "same project rendered fine in July with
an older ffmpeg"; manual `ffmpeg -i track.mp3 -af apad=whole_dur=16
-t 16 -c:a aac out.aac` works with the same binary, so the tool's
audioPadTrim invocation is the incompatible part)
- ts=1784177375 (duplicate reporter follow-up)
The concat approach itself is fine — the sibling concat scripts in
`assemble.ts` and `chunkEncoder.ts` pass raw paths (no `pathToFileURL`)
and work on Windows. `audioPadTrim.ts` was the outlier introduced in
PR #1615 (2026-06-20). Aligns with the codebase convention.
Regression pin: unit test asserts the pad-concat stdin never contains
the `file://` scheme, including for a Windows-shaped input path.
End-to-end verification requires a Windows + FFmpeg 8.x reviewer; the
unit test snapshots the arg shape.
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
— Via
miga-heygen
left a comment
There was a problem hiding this comment.
Review — #2525: audioPadTrim FFmpeg 8.x-compatible apad invocation
Invariant: concatFileLine must produce bare-path concat directives (no file:// URLs) with the concat demuxer's own single-quote escaping.
Bug: pathToFileURL(path).href produced file:///C:/... URLs. FFmpeg 8.x's concat demuxer on Windows strips the file: prefix, leaving ///C:/..., which Windows path parsing rejects.
Fix: Drop pathToFileURL, use bare path directly. Single-quote escaping unchanged.
What I verified
- Sibling convention (assemble.ts:184): Verified — uses identical bare-path pattern, no pathToFileURL
- pathToFileURL import removed: node:url import dropped cleanly
- Escaping correctness: Concat demuxer's documented escape rule for single quotes
- Existing test updated: Assertions switched to bare paths, plus negative regression pin
- New Windows regression test: Bare Windows paths verified, no file:// or file:\\ URLs
- Special characters: Bare paths are MORE correct than percent-encoded URLs for the concat demuxer
Disconfirming probe: is there any code path in this file that still produces file:// URLs? Searched: pathToFileURL removed, no other file:// construction. The only concat-script builder is concatFileLine, now bare-path. Clean.
Verdict
Code: clean. Mechanical fix, well-documented with field-signal references, matches established sibling convention. CI: all checks in progress.
Review by Miga
The prior fix (dc410ca) dropped `pathToFileURL` from the pad-concat step to make FFmpeg 8.x on Windows stop rejecting `file:///C:/…` URLs — but kept feeding the concat script via `pipe:0` stdin. That combination broke Linux CI: FFmpeg's concat demuxer resolves bare paths in the script against the base URL of the script's own source, and when the script is fed via `pipe:0` the base URL is `pipe:`. Absolute POSIX paths (`/tmp/foo.aac`) then join to `pipe:/tmp/foo.aac`, which the demuxer tries to open as a pipe and fails with: [concat @ 0x…] Impossible to open 'pipe:/tmp/…/audio.aac' pipe:0: End of file Manually reproduced with `ffmpeg-static@7.0.2` on this repo's binary. Fix: write the concat script to a real temp file (`<outputPath>.concat- list.txt`) and pass `-i concatListPath` — matching the sibling concat in `distributed/assemble.ts:180-186` exactly. A real file's directory becomes the base URL, so absolute paths in the script resolve as-is on both Linux and Windows. The `file://` scheme prefix stays out of the script (Windows FFmpeg 8.x fix preserved) and no `pipe:` prefix gets prepended (Linux regression fixed). Cleanup path list now covers both the silence tail and the concat list script. Also drops the now-unused `runFfmpegWithStdin` helper — no consumer needs stdin plumbing anymore. Regression pins in `audioPadTrim.test.ts`: - `does not emit file:// URLs …` — Windows arg-shape pin (unchanged intent, moved from `stdin` to `concatListContent` field). - `materializes the pad-concat script to a real file …` — new pin that asserts `-i` is not `pipe:0` and points at the concat list path, so the Linux failure mode can't regress. CI failures fixed: - CI / Producer: integration tests (assemble.test.ts pad case) - regression / regression-shards shard-1 (style-3-prod field-signal end-to-end render exercising the assemble pad path) Co-Authored-By: Claude <noreply@anthropic.com> — Via
What
Change
audioPadTrim's pad-concat step to use bare file paths in the concat script instead offile://URLs. One-line change toconcatFileLineinpackages/producer/src/services/render/audioPadTrim.ts; drops thepathToFileURLimport; updates the arg-shape unit tests + adds a Windows-path regression pin.Why
Four field-signal reports over ~24h — all on Windows, CLI 0.7.59:
ts=1784169914(:warning: 7/10, win32/x64, Baoyu) — 60s render, native audio assembly failed; workaround = video-only render + external ffmpeg mux.ts=1784177061(:red_circle: 5/10, win32/x64, andre 22cores) — 345.87s composition, 9 WAV audio elements, same error shape.ts=1784177375(:red_circle: 3/10, win32/x64, ffmpeg 8.1.1-full_build gyan.dev) — KEY DIAGNOSTIC. 13 mono 44.1kHz mp3 tracks. Reporter isolated the failure: "Same project rendered fine in July with an older ffmpeg. Manualffmpeg -i track.mp3 -af apad=whole_dur=16 -t 16 -c:a aac out.aacworks with the same binary, so the tool's audioPadTrim invocation appears incompatible with ffmpeg 8.x."ts=1784177375(follow-up from same reporter).Error signature in all four:
```
[audioPadTrim] FFmpeg exited with code 4294967294
Impossible to open file:///…/Temp/hf-render-*/audio.aac
```
4294967294=-2unsigned = FFmpeg'sAVERROR(ENOENT). The"Impossible to open …"string is the concat demuxer's message for a file it couldn't open.How
Ground-truth audit findings:
packages/producer/src/services/render/audioPadTrim.ts(line 233-236, pre-fix) built each concat directive as:```ts
function concatFileLine(path: string): string {
const normalized = pathToFileURL(path).href;
return `file '${normalized.replace(/'/g, "'\\''")}'`;
}
```
On Windows,
pathToFileURL("C:\\…\\audio.aac").hrefproducesfile:///C:/…/audio.aac. FFmpeg'sfile:protocol handler (libavformat/file.c) strips thefile:prefix, leaving///C:/…/audio.aac. FFmpeg 8.x on Windows rejects this triple-slash form.The sibling concat scripts in this repo do NOT use URLs.
packages/producer/src/services/distributed/assemble.ts(line 184),packages/engine/src/services/chunkEncoder.ts(line 680), and the smoke testpackages/producer/tests/distributed/_smoke/webm-concat-copy.test.tsall use bare paths with only single-quote escaping (file '${path.replace(/'/g, "'\\\\''")}') — those work on Windows today.audioPadTrim.tswas the outlier.Regression provenance. The original implementation (commit
62317f7f3, 2026-05-13) usedapad=pad_dur=Δ— the same filter family as the reporter's proven-working manual command. PR fix(engine): preserve AAC start time during MP4 mux #1615 (commit0473254bd, 2026-06-20, "fix(engine): preserve AAC start time during MP4 mux") refactored the pad path to the concat-copy approach and introduced thepathToFileURLconversion. That refactor is the direct source of the Windows-8.x incompatibility.Manual command vs. module command diff:
ffmpeg -i track.mp3 -af apad=whole_dur=16 -t 16 -c:a aac out.aac— filter-based padding, no concat demuxer.ffmpeg -f concat -safe 0 -protocol_whitelist file,pipe,crypto,data -i pipe:0 -c:a copy -y outputPathwith stdinfile 'file:///C:/…/audio.aac'\nfile 'file:///C:/…/silence.aac'\n.The fix (
packages/producer/src/services/render/audioPadTrim.ts,concatFileLine):```diff
function concatFileLine(path: string): string {
}
```
Also drops the now-unused
pathToFileURLimport.Test plan
bun test packages/producer/src/services/render/audioPadTrim.test.ts— 17/17 pass (16 pre-existing + 1 new regression pin asserting the pad-concat stdin never containsfile://for a Windows-shaped input path).node scripts/run-test-lane.mjs unit bun(producer) — 365 pass / 0 fail.bun x vitest run src/services/render/stages/assembleStage.test.ts— 3/3 pass (audioPadTrim consumer, mocks the module).bun run --filter @hyperframes/producer typecheck— clean.oxlint+oxfmt --checkon the two changed files — clean.main: render a composition on Windows that triggers the pad branch (audio shorter than video by >1ms) withffmpeg 8.1.1on PATH → expect the "Impossible to open file:///…/audio.aac" error.Enterprise release / feature flag holdout
UX/Screenshot recording
Co-Authored-By: Claude noreply@anthropic.com
🤖 Generated with Claude Code
— Via