mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
fix(render): preserve normalized M4A edit timing
This commit is contained in:
@@ -418,6 +418,30 @@ describe("muxVideoWithAudio audio codec handling", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves M4A priming edit lists instead of shifting copied video", async () => {
|
||||
const { spawn, calls } = createSpawnSpy();
|
||||
vi.resetModules();
|
||||
vi.doMock("child_process", () => ({ spawn }));
|
||||
|
||||
const { muxVideoWithAudio } = await import("./chunkEncoder.js");
|
||||
const muxPromise = muxVideoWithAudio(
|
||||
"/tmp/video-only.mp4",
|
||||
"/tmp/audio.duration-normalized.m4a",
|
||||
"/tmp/output.mp4",
|
||||
undefined,
|
||||
{ audioCodec: "aac" },
|
||||
{ num: 30, den: 1 },
|
||||
);
|
||||
|
||||
await flushMuxCodecResolution();
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0]!.args).toContain("copy");
|
||||
expect(calls[0]!.args).not.toContain("-avoid_negative_ts");
|
||||
|
||||
emitClose(calls[0]!.proc, 0);
|
||||
await expect(muxPromise).resolves.toMatchObject({ success: true });
|
||||
});
|
||||
|
||||
it("uses the caller-provided AAC codec contract instead of the sidecar extension", async () => {
|
||||
const { spawn, calls } = createSpawnSpy();
|
||||
vi.resetModules();
|
||||
|
||||
@@ -690,9 +690,14 @@ export async function muxVideoWithAudio(
|
||||
args.push("-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart");
|
||||
}
|
||||
}
|
||||
const copiesContainerizedAac =
|
||||
!isWebm && shouldCopyAudio && extname(audioPath).toLowerCase() === ".m4a";
|
||||
// PTS bases can diverge during mux and reintroduce negative DTS. See
|
||||
// buildEncoderArgs for the full reasoning on why that breaks playback.
|
||||
args.push("-avoid_negative_ts", "make_zero");
|
||||
// A freshly encoded M4A is the exception: its edit list already hides the
|
||||
// AAC priming packet. `make_zero` discards that edit and shifts copied video
|
||||
// forward by one AAC frame (~21ms), creating a visible first-frame offset.
|
||||
if (!copiesContainerizedAac) args.push("-avoid_negative_ts", "make_zero");
|
||||
if (fps !== undefined) {
|
||||
// Set the exact output framerate so the muxer doesn't PTS-average a
|
||||
// fractional rational like `360000/12001` instead of `30/1` into the
|
||||
|
||||
Reference in New Issue
Block a user