mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(render): scope M4A priming preservation to trims
This commit is contained in:
@@ -418,7 +418,7 @@ describe("muxVideoWithAudio audio codec handling", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves M4A priming edit lists instead of shifting copied video", async () => {
|
||||
it("keeps negative-timestamp repair for an M4A without a known priming edit list", async () => {
|
||||
const { spawn, calls } = createSpawnSpy();
|
||||
vi.resetModules();
|
||||
vi.doMock("child_process", () => ({ spawn }));
|
||||
@@ -433,6 +433,30 @@ describe("muxVideoWithAudio audio codec handling", () => {
|
||||
{ num: 30, den: 1 },
|
||||
);
|
||||
|
||||
await flushMuxCodecResolution();
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0]!.args).toContain("copy");
|
||||
expect(calls[0]!.args).toContain("-avoid_negative_ts");
|
||||
|
||||
emitClose(calls[0]!.proc, 0);
|
||||
await expect(muxPromise).resolves.toMatchObject({ success: true });
|
||||
});
|
||||
|
||||
it("preserves a known M4A priming edit list 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", preserveAudioPrimingEditList: true },
|
||||
{ num: 30, den: 1 },
|
||||
);
|
||||
|
||||
await flushMuxCodecResolution();
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0]!.args).toContain("copy");
|
||||
|
||||
@@ -77,6 +77,8 @@ export interface MuxVideoWithAudioOptions extends Partial<
|
||||
* depend on the file extension alone.
|
||||
*/
|
||||
audioCodec?: "aac";
|
||||
/** Preserve a priming edit list known to have been created by AAC re-encoding. */
|
||||
preserveAudioPrimingEditList?: boolean;
|
||||
}
|
||||
|
||||
async function shouldCopyAacSidecar(
|
||||
@@ -691,7 +693,7 @@ export async function muxVideoWithAudio(
|
||||
}
|
||||
}
|
||||
const copiesContainerizedAac =
|
||||
!isWebm && shouldCopyAudio && extname(audioPath).toLowerCase() === ".m4a";
|
||||
!isWebm && shouldCopyAudio && config?.preserveAudioPrimingEditList === true;
|
||||
// PTS bases can diverge during mux and reintroduce negative DTS. See
|
||||
// buildEncoderArgs for the full reasoning on why that breaks playback.
|
||||
// A freshly encoded M4A is the exception: its edit list already hides the
|
||||
|
||||
@@ -290,6 +290,7 @@ export async function assemble(
|
||||
|
||||
// ── 3. Audio: pad-or-trim then mux ────────────────────────────────────
|
||||
let audioForMux: string | null = null;
|
||||
let preserveAudioPrimingEditList = false;
|
||||
if (audioPath !== null && existsSync(audioPath)) {
|
||||
const paddedAudioPath = join(workDir, "audio-padded.m4a");
|
||||
const padTrimResult = await padOrTrimAudioToVideoFrameCount({
|
||||
@@ -302,6 +303,7 @@ export async function assemble(
|
||||
throw new Error(`[assemble] audio pad/trim failed: ${padTrimResult.error}`);
|
||||
}
|
||||
audioForMux = paddedAudioPath;
|
||||
preserveAudioPrimingEditList = padTrimResult.operation === "trim";
|
||||
log.info("[assemble] audio normalized for mux", {
|
||||
operation: padTrimResult.operation,
|
||||
targetDurationSeconds: padTrimResult.targetDurationSeconds,
|
||||
@@ -321,7 +323,7 @@ export async function assemble(
|
||||
audioForMux,
|
||||
muxOutputPath,
|
||||
abortSignal,
|
||||
{ audioCodec: "aac" },
|
||||
{ audioCodec: "aac", preserveAudioPrimingEditList },
|
||||
{ num: plan.dimensions.fpsNum, den: plan.dimensions.fpsDen },
|
||||
);
|
||||
if (!muxResult.success) {
|
||||
|
||||
@@ -69,7 +69,7 @@ describe("runAssembleStage audio duration parity", () => {
|
||||
"/tmp/audio.duration-normalized.m4a",
|
||||
"/tmp/output.mp4",
|
||||
undefined,
|
||||
{ audioCodec: "aac" },
|
||||
{ audioCodec: "aac", preserveAudioPrimingEditList: true },
|
||||
{ num: 30, den: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
@@ -76,7 +76,10 @@ export async function runAssembleStage(input: AssembleStageInput): Promise<Assem
|
||||
normalizeResult.outputPath,
|
||||
outputPath,
|
||||
abortSignal,
|
||||
{ audioCodec: "aac" },
|
||||
{
|
||||
audioCodec: "aac",
|
||||
preserveAudioPrimingEditList: normalizeResult.operation === "trim",
|
||||
},
|
||||
job.config.fps,
|
||||
);
|
||||
assertNotAborted();
|
||||
|
||||
Reference in New Issue
Block a user