From c2cde2fb624ba5a311615574dff822a41f61ea96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Wed, 28 Oct 2026 17:48:36 +0000 Subject: [PATCH] perf(producer): the audio true-peak check seeks to each segment instead of decoding up to it (#6672) * perf(producer): the audio true-peak check seeks to each segment instead of decoding up to it Each parallel segment scan decoded the AAC from the start and trimmed after, so the last one decoded the whole file. It now seeks to a whole second one second before its segment and trims relative to it: the same peak, measured in about half the time. * test(producer): the true-peak check's ffmpeg tests run in the integration lane --- .../producer/scripts/integration-tests.json | 1 + .../render/audioTruePeak.fakeFfmpeg.test.ts | 9 +++++-- .../src/services/render/audioTruePeak.test.ts | 16 ++++++++++++ .../src/services/render/audioTruePeak.ts | 26 ++++++++++++++++--- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/packages/producer/scripts/integration-tests.json b/packages/producer/scripts/integration-tests.json index 3a91aeccc2..1c5a9be944 100644 --- a/packages/producer/scripts/integration-tests.json +++ b/packages/producer/scripts/integration-tests.json @@ -28,6 +28,7 @@ "src/services/htmlCompiler.probeCount.test.ts", "src/services/htmlCompiler.variableMedia.test.ts", "src/services/render/audioPadTrim.integration.test.ts", + "src/services/render/audioTruePeak.test.ts", "src/services/render/deliveryRender.integration.test.ts", "src/services/render/firstFrameStartingState.integration.test.ts", "src/services/render/hlsRender.integration.test.ts", diff --git a/packages/producer/src/services/render/audioTruePeak.fakeFfmpeg.test.ts b/packages/producer/src/services/render/audioTruePeak.fakeFfmpeg.test.ts index 4d2ee4618c..790f711509 100644 --- a/packages/producer/src/services/render/audioTruePeak.fakeFfmpeg.test.ts +++ b/packages/producer/src/services/render/audioTruePeak.fakeFfmpeg.test.ts @@ -104,9 +104,14 @@ describe("a local render's audio pad/trim", () => { expect(result.success).toBe(true); expect(scanFilters()).toEqual([ "atrim=start_sample=0:end_sample=2880000,ebur128=peak=true", - "atrim=start_sample=2875200:end_sample=5760000,ebur128=peak=true", - "atrim=start_sample=5755200,ebur128=peak=true", + "atrim=start_sample=91200:end_sample=2976000,ebur128=peak=true", + "atrim=start_sample=91200,ebur128=peak=true", ]); + const seeks = vi + .mocked(runFfmpeg) + .mock.calls.filter(([args]) => args.some((arg) => arg.includes("ebur128"))) + .map(([args]) => (args[0] === "-ss" ? args[1] : null)); + expect(seeks).toEqual([null, "58", "118"]); }); }); diff --git a/packages/producer/src/services/render/audioTruePeak.test.ts b/packages/producer/src/services/render/audioTruePeak.test.ts index 0d5d4ccd31..8e3f45e8f9 100644 --- a/packages/producer/src/services/render/audioTruePeak.test.ts +++ b/packages/producer/src/services/render/audioTruePeak.test.ts @@ -74,6 +74,22 @@ describe.skipIf(!hasFfmpeg)("measureTruePeakDbfs in segments", () => { 60_000, ); + it.each([ + ["ends on a segment cut", CUT - BURST.length], + ["starts where the last segment's scan starts", 2 * CUT - BLOCK], + ])( + "reads an AAC peak that %s exactly as one whole-file pass", + async (_, at) => { + const wav = monoWav(`aac-burst-${at}`, at); + const aac = wav.replace(/\.wav$/, ".m4a"); + execFileSync(getFfmpegBinary(), ["-nostdin", "-v", "error", "-i", wav, "-c:a", "aac", aac]); + const [whole, segmented] = await bothWays(aac); + expect(whole).toBeGreaterThan(-10); + expect(segmented).toBe(whole); + }, + 60_000, + ); + it("reads silence as -inf", async () => { expect(await bothWays(monoWav("silence"))).toEqual([ Number.NEGATIVE_INFINITY, diff --git a/packages/producer/src/services/render/audioTruePeak.ts b/packages/producer/src/services/render/audioTruePeak.ts index 9eec1f9014..2a10923355 100644 --- a/packages/producer/src/services/render/audioTruePeak.ts +++ b/packages/producer/src/services/render/audioTruePeak.ts @@ -28,6 +28,7 @@ async function scanTruePeak( trim: string, timeoutMs: number, signal?: AbortSignal, + seekSeconds = 0, ): Promise { let firstBlock: number | undefined; let laterBlocks = Number.NEGATIVE_INFINITY; @@ -45,7 +46,21 @@ async function scanTruePeak( } }; const result = await runFfmpeg( - ["-i", audioPath, "-map", "0:a:0", "-vn", "-af", `${trim}ebur128=peak=true`, "-f", "null", "-"], + [ + ...(seekSeconds > 0 ? ["-ss", String(seekSeconds)] : []), + ...[ + "-i", + audioPath, + "-map", + "0:a:0", + "-vn", + "-af", + `${trim}ebur128=peak=true`, + "-f", + "null", + "-", + ], + ], { signal, timeout: timeoutMs, onStderr: readLines }, ); if (!result.success) { @@ -87,12 +102,17 @@ export async function measureTruePeakDbfs( // A later segment starts one block early and drops that block: its oversampler starts // from mirrored history there, and the previous segment stops 16 samples short. const start = k === 0 ? 0 : cut(k) - block; - const end = k === count - 1 ? "" : `:end_sample=${cut(k + 1)}`; + // Decoding from the top costs a segment everything before it: seek to a whole second (an exact + // sample) one second early, so the AAC decoder has settled before the segment's first sample. + const seekSeconds = Math.max(0, Math.floor(start / sampleRate) - 1); + const skipped = seekSeconds * sampleRate; + const end = k === count - 1 ? "" : `:end_sample=${cut(k + 1) - skipped}`; return scanTruePeak( audioPath, - `atrim=start_sample=${start}${end},`, + `atrim=start_sample=${start - skipped}${end},`, timeoutMs, segmentSignal, + seekSeconds, ); }), ).catch((error: unknown) => {