fix: bound synthetic silence beds with atrim — unbounded anullsrc deadlocked concat
Card and audio-less-clip anullsrc sources are infinite; concat never terminates an infinite segment, so the encode hung forever at 30%. atrim pins each bed to its item's span (card duration / clip trim).
This commit is contained in:
parent
6373715ffe
commit
8401aa3d37
2 changed files with 4 additions and 4 deletions
|
|
@ -135,7 +135,7 @@ test("per-item chains feed one concat v=1:a=1 in timeline order", () => {
|
|||
assert.match(chains[0]!, /^\[0:v\]scale=1280:720[^;]*\[v0\]$/);
|
||||
assert.match(chains[1]!, /^\[0:a\]aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo\[a0\]$/);
|
||||
assert.match(chains[2]!, /^\[1:v\]scale=1280:720[^;]*\[v1\]$/);
|
||||
assert.equal(chains[3]!, `anullsrc=r=${ASSEMBLE_PRESET.sampleRate}:cl=stereo,aformat=sample_fmts=fltp:sample_rates=${ASSEMBLE_PRESET.sampleRate}:channel_layouts=stereo[a1]`);
|
||||
assert.equal(chains[3]!, `anullsrc=r=${ASSEMBLE_PRESET.sampleRate}:cl=stereo,atrim=0:2.000,asetpts=PTS-STARTPTS,aformat=sample_fmts=fltp:sample_rates=${ASSEMBLE_PRESET.sampleRate}:channel_layouts=stereo[a1]`);
|
||||
// The rotated clip transposes before its fit chain and applies its volume.
|
||||
assert.ok(chains[4]!.startsWith("[2:v]transpose=1,scale="));
|
||||
assert.ok(chains[5]!.includes("volume=-6dB"));
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ export function clipVideoChain(rotate: EdlRotate, fit: "cover" | "contain", aspe
|
|||
return `${rotated}scale=${W}:${H}:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=${W}:${H}:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,fps=${ASSEMBLE_PRESET.fps},format=yuv420p`;
|
||||
}
|
||||
|
||||
function clipAudioChain(volumeDb: number, hasAudio: boolean, inputIndex: number): string {
|
||||
function clipAudioChain(volumeDb: number, hasAudio: boolean, inputIndex: number, trimSpanSec: string): string {
|
||||
const aformat = `aformat=sample_fmts=fltp:sample_rates=${ASSEMBLE_PRESET.sampleRate}:channel_layouts=stereo`;
|
||||
if (!hasAudio) return `anullsrc=r=${ASSEMBLE_PRESET.sampleRate}:cl=stereo,aformat=sample_fmts=fltp:sample_rates=${ASSEMBLE_PRESET.sampleRate}:channel_layouts=stereo`;
|
||||
return `[${inputIndex}:a]${aformat}${volumeDb === 0 ? "" : `,volume=${volumeDb}dB`}`;
|
||||
|
|
@ -201,7 +201,7 @@ export function buildAssembleFilter(plan: AssemblePlan, index: AssembleInputInde
|
|||
const at = index.timeline[i];
|
||||
if (at === undefined) fail(`clip ${i} has no input index`);
|
||||
chains.push(`[${at}:v]${clipVideoChain(entry.rotate, project.fit, project.aspect)}[${v}]`);
|
||||
chains.push(`${clipAudioChain(entry.volumeDb, source.hasAudio, at)}[${a}]`);
|
||||
chains.push(`${clipAudioChain(entry.volumeDb, source.hasAudio, at, ((entry.trimEndMs - entry.trimStartMs) / 1000).toFixed(3))}[${a}]`);
|
||||
clipCursor += 1;
|
||||
} else {
|
||||
const source = plan.cards[cardCursor];
|
||||
|
|
@ -210,7 +210,7 @@ export function buildAssembleFilter(plan: AssemblePlan, index: AssembleInputInde
|
|||
if (at === undefined) fail(`card ${i} has no input index`);
|
||||
// -loop 1 -t {dur} supplies the frames; the chain only normalizes to the canvas.
|
||||
chains.push(`[${at}:v]scale=${EDL_ASPECTS[project.aspect].w}:${EDL_ASPECTS[project.aspect].h},setsar=1,fps=${ASSEMBLE_PRESET.fps},format=yuv420p[${v}]`);
|
||||
chains.push(`anullsrc=r=${ASSEMBLE_PRESET.sampleRate}:cl=stereo,aformat=sample_fmts=fltp:sample_rates=${ASSEMBLE_PRESET.sampleRate}:channel_layouts=stereo[${a}]`);
|
||||
chains.push(`anullsrc=r=${ASSEMBLE_PRESET.sampleRate}:cl=stereo,atrim=0:${(source.durationMs / 1000).toFixed(3)},asetpts=PTS-STARTPTS,aformat=sample_fmts=fltp:sample_rates=${ASSEMBLE_PRESET.sampleRate}:channel_layouts=stereo[${a}]`);
|
||||
cardCursor += 1;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue