From 591fde8e37f107940b0d3df796c18127cbb5c218 Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Fri, 22 May 2026 20:00:24 +0700 Subject: [PATCH] fix(export): tighten native audio safety guards --- electron/ipc/export/native-video.test.ts | 36 ++++++++++++++++++++++++ electron/ipc/export/native-video.ts | 6 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/electron/ipc/export/native-video.test.ts b/electron/ipc/export/native-video.test.ts index 6508e6d2..ddd99168 100644 --- a/electron/ipc/export/native-video.test.ts +++ b/electron/ipc/export/native-video.test.ts @@ -856,6 +856,16 @@ describe("buildNativeVideoAudioMuxArgs", () => { expect(args.join(";")).not.toContain("-c:a;copy"); }); + it("transcodes unknown source audio instead of copying unsafe codecs into MP4", () => { + const args = buildNativeVideoAudioMuxArgs("video.mp4", "source.wav", "out.mp4", { + audioMode: "copy-source", + outputDurationSec: 60, + }); + + expect(args).toEqual(expect.arrayContaining(["-c:a", "aac", "-b:a", "192k"])); + expect(args.join(";")).not.toContain("-c:a;copy"); + }); + it("keeps filtered audio on the AAC encode path", () => { const args = buildNativeVideoAudioMuxArgs("video.mp4", "source.mp4", "out.mp4", { audioMode: "trim-source", @@ -906,6 +916,11 @@ describe("canCopyAudioCodecIntoMp4", () => { it("blocks Opus so native exports transcode it to AAC for MP4", () => { expect(canCopyAudioCodecIntoMp4("opus")).toBe(false); }); + + it("blocks unknown codecs so sidecar WAV/PCM audio is encoded for MP4", () => { + expect(canCopyAudioCodecIntoMp4(undefined)).toBe(false); + expect(canCopyAudioCodecIntoMp4("")).toBe(false); + }); }); describe("validateNativeVideoStreamStats", () => { @@ -1162,6 +1177,27 @@ describe("validateNvidiaCudaExportSummary", () => { expect(issues).toEqual(["output audio duration is not positive"]); }); + it("rejects inline-audio CUDA output when the audio duration is missing", () => { + const issues = validateNvidiaCudaExportSummary( + { + success: true, + targetFrames: 300, + durationSec: 10, + nativeSummary: { + success: true, + frames: 300, + sourceTimestampMode: "pts", + selectionStage: "timestamp-mapped-callback", + }, + outputVideo: { duration: "9.999900", nb_frames: "300" }, + outputAudio: {}, + }, + { durationSec: 10, targetFrames: 300, requiresTimelineSync: true }, + ); + + expect(issues).toEqual(["missing output audio duration"]); + }); + it("accepts audio CUDA output when the helper reports PTS-aligned selection", () => { const issues = validateNvidiaCudaExportSummary( { diff --git a/electron/ipc/export/native-video.ts b/electron/ipc/export/native-video.ts index 0f9e36f6..27ee4c52 100644 --- a/electron/ipc/export/native-video.ts +++ b/electron/ipc/export/native-video.ts @@ -436,7 +436,9 @@ export function validateNvidiaCudaExportSummary( if (expected.requiresTimelineSync) { if (!summary.outputAudio) { issues.push("missing output audio stream"); - } else if (outputAudioDurationSec !== null && outputAudioDurationSec <= 0) { + } else if (outputAudioDurationSec === null) { + issues.push("missing output audio duration"); + } else if (outputAudioDurationSec <= 0) { issues.push("output audio duration is not positive"); } } @@ -3903,7 +3905,7 @@ export async function resolveNativeVideoEncoder( export function canCopyAudioCodecIntoMp4(codec?: string | null) { const normalized = (codec ?? "").trim().toLowerCase(); if (!normalized) { - return true; + return false; } return (