Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions electron/ipc/export/native-video.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,10 @@ describe("getExperimentalNvidiaCudaExportSkipReason", () => {
process.env[exportEnvName] = "1";
process.env[forceEnvName] = "1";
delete process.env[allowAudioEnvName];
fsMocks.access.mockResolvedValue(undefined);
electronAppMock.getGPUInfo.mockResolvedValue({
gpuDevice: [{ vendorId: 0x10de, deviceString: "NVIDIA GeForce GTX 1650" }],
});

try {
const reason = await getExperimentalNvidiaCudaExportSkipReason(
Expand All @@ -446,6 +450,31 @@ describe("getExperimentalNvidiaCudaExportSkipReason", () => {
} else {
process.env[allowAudioEnvName] = originalAllowAudioEnv;
}
electronAppMock.getGPUInfo.mockReset();
electronAppMock.getGPUInfo.mockResolvedValue({ gpuDevice: [] });
resetFsAccessMock();
}
});

it("reports explicit lab CUDA as unavailable when the wrapper cannot be resolved", async () => {
const exportEnvName = "RECORDLY_EXPERIMENTAL_NVIDIA_CUDA_EXPORT";
const originalExportEnv = process.env[exportEnvName];
process.env[exportEnvName] = "1";

try {
const reason = await getExperimentalNvidiaCudaExportSkipReason(
createNvidiaCudaSkipOptions(),
);

expect(reason).toBe(
process.platform === "win32" ? "cuda-wrapper-unavailable" : "not-windows",
);
} finally {
if (originalExportEnv === undefined) {
delete process.env[exportEnvName];
} else {
process.env[exportEnvName] = originalExportEnv;
}
}
});

Expand Down Expand Up @@ -675,6 +704,58 @@ describe("buildExperimentalNvidiaCudaStaticLayoutArgs", () => {
});

describe("buildExperimentalWindowsGpuStaticLayoutArgs", () => {
it("prefers the high-performance adapter by default for D3D11 fallback diagnostics", () => {
const envName = "RECORDLY_WINDOWS_GPU_EXPORT_ADAPTER_INDEX";
const preferEnvName = "RECORDLY_WINDOWS_GPU_EXPORT_PREFER_HIGH_PERFORMANCE_ADAPTER";
const originalValue = process.env[envName];
const originalPreferValue = process.env[preferEnvName];
delete process.env[envName];
process.env[preferEnvName] = "1";

try {
const args = buildExperimentalWindowsGpuStaticLayoutArgs(
createNvidiaCudaSkipOptions(),
"output.mp4",
);

expect(args).toContain("--prefer-high-performance-adapter");
expect(args).not.toContain("--adapter-index");
} finally {
if (originalValue === undefined) {
delete process.env[envName];
} else {
process.env[envName] = originalValue;
}
if (originalPreferValue === undefined) {
delete process.env[preferEnvName];
} else {
process.env[preferEnvName] = originalPreferValue;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});

it("passes an explicit D3D11 adapter index when configured", () => {
const envName = "RECORDLY_WINDOWS_GPU_EXPORT_ADAPTER_INDEX";
const originalValue = process.env[envName];

try {
process.env[envName] = "2";
const args = buildExperimentalWindowsGpuStaticLayoutArgs(
createNvidiaCudaSkipOptions(),
"output.mp4",
);

expect(args).toEqual(expect.arrayContaining(["--adapter-index", "2"]));
expect(args).not.toContain("--prefer-high-performance-adapter");
} finally {
if (originalValue === undefined) {
delete process.env[envName];
} else {
process.env[envName] = originalValue;
}
}
});

it("passes background blur to the D3D11 compositor", () => {
const args = buildExperimentalWindowsGpuStaticLayoutArgs(
createNvidiaCudaSkipOptions({
Expand Down Expand Up @@ -813,6 +894,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",
Expand Down Expand Up @@ -863,6 +954,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", () => {
Expand Down Expand Up @@ -1078,6 +1174,47 @@ describe("validateNvidiaCudaExportSummary", () => {
expect(issues).toEqual([]);
});

it("rejects inline-audio CUDA output when the helper does not produce audio", () => {
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" },
},
{ durationSec: 10, targetFrames: 300, requiresTimelineSync: true },
);

expect(issues).toEqual(["missing output audio stream"]);
});

it("rejects inline-audio CUDA output when the probed audio stream is empty", () => {
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: { duration: "0.000000" },
},
{ durationSec: 10, targetFrames: 300, requiresTimelineSync: true },
);

expect(issues).toEqual(["output audio duration is not positive"]);
});

it("accepts audio CUDA output when the helper reports PTS-aligned selection", () => {
const issues = validateNvidiaCudaExportSummary(
{
Expand Down
Loading