mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
fix(engine): stop destroying the AAC priming edit list when muxing (#3505)
`muxVideoWithAudio` passed `-avoid_negative_ts make_zero` unless the caller
set `preserveAudioPrimingEditList`. In practice the dominant path is an AAC
sidecar copied into mp4, where that flag is actively harmful: ffmpeg's
default is `auto`, which the mp4/mov muxers (AVFMT_TS_NEGATIVE) already
resolve to `disabled`. Forcing `make_zero` overrides the correct default,
discards the priming edit list the sidecar encode created, shifts the video
start_time forward by one AAC frame and writes an empty video edit at t=0 —
which edit-list-honoring players (QuickTime/Safari) render as a black first
frame.
Verified with ffprobe on a copy mux of a 30fps h264 mp4 and an AAC sidecar:
with `make_zero` video start_time 0.066000, elst: [media time -1,
dur 5940] + [media time 6000, dur 180000]
audio start_time 0.042993, elst: [media time -1, ...]
without (this fix) video start_time 0.000000, elst: [media time 6000,
dur 180000]
audio start_time 0.000000, elst: [media time 1024, ...]
The empty leading edit and the offset both disappear, and the audio keeps
its 1024-sample priming edit.
The flag is now never passed for a mux, in any mode. `preserveAudioPrimingEditList`
is part of the exported engine API, so it stays on `MuxVideoWithAudioOptions`
as `@deprecated` and no-op rather than being removed; the two internal callers
that set it (`assembleStage`, distributed `assemble`) drop it.
`buildEncoderArgs` and `streamingEncoder` still pass the flag for video-only
output and are deliberately left alone — those chunks are consumed as
intermediates, not as a delivered mp4/mov.
Fixes #3487
Co-authored-by: Alexandru Mincu <alex@mountsoftware.ro>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Alexandru Mincu
Claude Fable 5
parent
18409c9f27
commit
ee64c3b116
@@ -302,10 +302,7 @@ export async function assemble(
|
||||
}
|
||||
|
||||
// ── 3. Audio: pad-or-trim then mux ────────────────────────────────────
|
||||
let normalizedAudio: {
|
||||
path: string;
|
||||
preserveAudioPrimingEditList: boolean;
|
||||
} | null = null;
|
||||
let normalizedAudioPath: string | null = null;
|
||||
if (audioPath !== null && existsSync(audioPath)) {
|
||||
const paddedAudioPath = join(workDir, "audio-padded.m4a");
|
||||
const padTrimResult = await padOrTrimAudioToVideoFrameCount({
|
||||
@@ -317,10 +314,7 @@ export async function assemble(
|
||||
if (!padTrimResult.success) {
|
||||
throw new Error(`[assemble] audio pad/trim failed: ${padTrimResult.error}`);
|
||||
}
|
||||
normalizedAudio = {
|
||||
path: paddedAudioPath,
|
||||
preserveAudioPrimingEditList: padTrimResult.operation !== "copy",
|
||||
};
|
||||
normalizedAudioPath = paddedAudioPath;
|
||||
log.info("[assemble] audio normalized for mux", {
|
||||
operation: padTrimResult.operation,
|
||||
targetDurationSeconds: padTrimResult.targetDurationSeconds,
|
||||
@@ -333,16 +327,17 @@ export async function assemble(
|
||||
// because it operates on a `RenderJob` and emits `updateJobStatus`
|
||||
// payloads — the distributed activity has no job to thread through.
|
||||
const muxOutputPath =
|
||||
normalizedAudio !== null ? join(workDir, `mux.${plan.dimensions.format}`) : postConcatPath;
|
||||
if (normalizedAudio !== null) {
|
||||
normalizedAudioPath !== null
|
||||
? join(workDir, `mux.${plan.dimensions.format}`)
|
||||
: postConcatPath;
|
||||
if (normalizedAudioPath !== null) {
|
||||
const muxResult = await muxVideoWithAudio(
|
||||
postConcatPath,
|
||||
normalizedAudio.path,
|
||||
normalizedAudioPath,
|
||||
muxOutputPath,
|
||||
abortSignal,
|
||||
{
|
||||
audioCodec: "aac",
|
||||
preserveAudioPrimingEditList: normalizedAudio.preserveAudioPrimingEditList,
|
||||
},
|
||||
{ num: plan.dimensions.fpsNum, den: plan.dimensions.fpsDen },
|
||||
);
|
||||
|
||||
@@ -69,7 +69,7 @@ describe("runAssembleStage audio duration parity", () => {
|
||||
"/tmp/audio.duration-normalized.m4a",
|
||||
"/tmp/output.mp4",
|
||||
undefined,
|
||||
{ audioCodec: "aac", preserveAudioPrimingEditList: true },
|
||||
{ audioCodec: "aac" },
|
||||
{ num: 30, den: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
@@ -78,7 +78,6 @@ export async function runAssembleStage(input: AssembleStageInput): Promise<Assem
|
||||
abortSignal,
|
||||
{
|
||||
audioCodec: "aac",
|
||||
preserveAudioPrimingEditList: normalizeResult.operation !== "copy",
|
||||
},
|
||||
job.config.fps,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user