mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(producer): audioPadTrim FFmpeg-8.x-compatible apad invocation
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
This commit is contained in:
@@ -47,8 +47,13 @@ describe("buildPadTrimAudioArgs", () => {
|
||||
expect(concatArgs[concatArgs.indexOf("-i") + 1]).toBe("pipe:0");
|
||||
expect(concatArgs[concatArgs.indexOf("-c:a") + 1]).toBe("copy");
|
||||
expect(concatArgs[concatArgs.length - 1]).toBe("/tmp/out.aac");
|
||||
expect(plan.steps[1]!.stdin).toContain("file 'file:///tmp/in.aac'");
|
||||
expect(plan.steps[1]!.stdin).toContain("file 'file:///tmp/out.aac.pad-silence.aac'");
|
||||
// Concat script MUST use bare paths, NOT `file://` URLs. FFmpeg 8.x
|
||||
// on Windows can't open `file:///C:/…` URLs from the concat demuxer
|
||||
// (field-signal ts=1784169914 / 1784177061 / 1784177375). Regression
|
||||
// pin: the `file://` scheme prefix must never appear in the stdin.
|
||||
expect(plan.steps[1]!.stdin).toContain("file '/tmp/in.aac'");
|
||||
expect(plan.steps[1]!.stdin).toContain("file '/tmp/out.aac.pad-silence.aac'");
|
||||
expect(plan.steps[1]!.stdin).not.toContain("file://");
|
||||
expect(plan.cleanupPaths).toEqual(["/tmp/out.aac.pad-silence.aac"]);
|
||||
|
||||
const reencodedSourceStep = plan.steps.find(
|
||||
@@ -106,6 +111,31 @@ describe("buildPadTrimAudioArgs", () => {
|
||||
const trimNeeded = buildPadTrimAudioArgs("/tmp/a.aac", "/tmp/o.aac", 5.002, 5.0);
|
||||
expect(trimNeeded.operation).toBe("trim");
|
||||
});
|
||||
|
||||
it("does not emit `file://` URLs in the pad-concat stdin (FFmpeg 8.x Windows compat)", () => {
|
||||
// Regression pin for field-signal reports ts=1784169914 / 1784177061 /
|
||||
// ts=1784177375 (win32/x64, CLI 0.7.59, ffmpeg 8.1.1-full_build). The
|
||||
// concat demuxer's file open on Windows in FFmpeg 8.x rejects
|
||||
// `file:///C:/…` URLs with "Impossible to open …". The concat script
|
||||
// MUST use bare paths. Match sibling `assemble.ts` /
|
||||
// `chunkEncoder.ts` conventions.
|
||||
const winPlan = buildPadTrimAudioPlan(
|
||||
"C:\\Users\\alice\\AppData\\Local\\Temp\\hf-render-abc\\audio.aac",
|
||||
"C:\\Users\\alice\\AppData\\Local\\Temp\\hf-render-abc\\audio-padded.aac",
|
||||
4.0,
|
||||
5.0,
|
||||
);
|
||||
expect(winPlan.operation).toBe("pad");
|
||||
const concatStep = winPlan.steps.find((s) => s.kind === "pad-concat");
|
||||
expect(concatStep).toBeDefined();
|
||||
expect(concatStep!.stdin).toBeDefined();
|
||||
expect(concatStep!.stdin).not.toContain("file://");
|
||||
expect(concatStep!.stdin).not.toContain("file:\\\\");
|
||||
// Bare Windows paths appear as-is in the concat directives.
|
||||
expect(concatStep!.stdin).toContain(
|
||||
"file 'C:\\Users\\alice\\AppData\\Local\\Temp\\hf-render-abc\\audio.aac'",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("padOrTrimAudioToVideoFrameCount", () => {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
import { spawn } from "node:child_process";
|
||||
import { rmSync } from "node:fs";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import {
|
||||
extractAudioMetadata,
|
||||
formatFfmpegError,
|
||||
@@ -231,8 +230,17 @@ function channelLayoutForChannels(channels: number | undefined): string {
|
||||
}
|
||||
|
||||
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). Field-signal reports (ts=1784169914 / 1784177061 /
|
||||
// 1784177375, all win32/x64 CLI 0.7.59; the last isolated the module's
|
||||
// arg shape vs a working manual `apad=whole_dur` command). Bare paths
|
||||
// also match the convention already used by the sibling concat
|
||||
// scripts in `assemble.ts` and `chunkEncoder.ts`. The single-quote
|
||||
// escaping (`'\''`) is the concat demuxer's own escape rule.
|
||||
return `file '${path.replace(/'/g, "'\\''")}'`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user