Skip to content

fix(producer): audioPadTrim FFmpeg-8.x-compatible apad invocation#2525

Merged
vanceingalls merged 2 commits into
mainfrom
via/audiopad-ffmpeg8-compat
Jul 16, 2026
Merged

fix(producer): audioPadTrim FFmpeg-8.x-compatible apad invocation#2525
vanceingalls merged 2 commits into
mainfrom
via/audiopad-ffmpeg8-compat

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

What

Change audioPadTrim's pad-concat step to use bare file paths in the concat script instead of file:// URLs. One-line change to concatFileLine in packages/producer/src/services/render/audioPadTrim.ts; drops the pathToFileURL import; 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. 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 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 = -2 unsigned = FFmpeg's AVERROR(ENOENT). The "Impossible to open …" string is the concat demuxer's message for a file it couldn't open.

How

Ground-truth audit findings:

  1. 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").href produces file:///C:/…/audio.aac. FFmpeg's file: protocol handler (libavformat/file.c) strips the file: prefix, leaving ///C:/…/audio.aac. FFmpeg 8.x on Windows rejects this triple-slash form.

  2. 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 test packages/producer/tests/distributed/_smoke/webm-concat-copy.test.ts all use bare paths with only single-quote escaping (file '${path.replace(/'/g, "'\\\\''")}') — those work on Windows today. audioPadTrim.ts was the outlier.

  3. Regression provenance. The original implementation (commit 62317f7f3, 2026-05-13) used apad=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 (commit 0473254bd, 2026-06-20, "fix(engine): preserve AAC start time during MP4 mux") refactored the pad path to the concat-copy approach and introduced the pathToFileURL conversion. That refactor is the direct source of the Windows-8.x incompatibility.

  4. Manual command vs. module command diff:

    • Reporter's working command: ffmpeg -i track.mp3 -af apad=whole_dur=16 -t 16 -c:a aac out.aac — filter-based padding, no concat demuxer.
    • Module's failing command (step 2, pad-concat): ffmpeg -f concat -safe 0 -protocol_whitelist file,pipe,crypto,data -i pipe:0 -c:a copy -y outputPath with stdin file 'file:///C:/…/audio.aac'\nfile 'file:///C:/…/silence.aac'\n.
    • The concat approach is intentional (avoids re-encoding the pre-mixed AAC — that was the point of PR fix(engine): preserve AAC start time during MP4 mux #1615's refactor). Fix is to keep the concat approach but switch to bare paths so it survives FFmpeg 8.x on Windows.

The fix (packages/producer/src/services/render/audioPadTrim.ts, concatFileLine):

```diff
function concatFileLine(path: string): string {

  • const normalized = pathToFileURL(path).href;
  • return `file '${normalized.replace(/'/g, "'\\''")}'`;
  • // Bare paths in concat directives — NOT `file://` URLs. FFmpeg 8.x on
  • // Windows fails to open URL-form paths from the concat demuxer with
  • // "Impossible to open file:///C:/…" (its file protocol strips the
  • // `file:` prefix leaving `///C:/…`, which Windows path parsing then
  • // rejects). …
  • return `file '${path.replace(/'/g, "'\\''")}'`;
    }
    ```

Also drops the now-unused pathToFileURL import.

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 contains file:// 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 --check on the two changed files — clean.
  • End-to-end verification on Windows + FFmpeg 8.x — I don't have a Windows box with FFmpeg 8.x to reproduce the pre-fix failure and verify the post-fix success. Reviewer with access, please:
    1. Reproduce with main: render a composition on Windows that triggers the pad branch (audio shorter than video by >1ms) with ffmpeg 8.1.1 on PATH → expect the "Impossible to open file:///…/audio.aac" error.
    2. Repeat with this branch → expect the render to complete with the concat-copy pad step succeeding.

Enterprise release / feature flag holdout

  • N/A — no user-facing behavior change beyond the bug fix
  • Feature-flagged and held out from enterprise until validated

UX/Screenshot recording

  • No UI impact — this is a producer-side FFmpeg arg shape fix, backend-only.

Co-Authored-By: Claude noreply@anthropic.com

🤖 Generated with Claude Code

— Via

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 miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@vanceingalls
vanceingalls merged commit d0d281d into main Jul 16, 2026
52 checks passed
@vanceingalls
vanceingalls deleted the via/audiopad-ffmpeg8-compat branch July 16, 2026 07:35
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