removed muxing completly from the code (mac/windows)

This commit is contained in:
Alan Trebugeais
2026-05-10 13:30:34 +02:00
parent 2537952a94
commit 902f42d07d
5 changed files with 74 additions and 566 deletions
-7
View File
@@ -38,10 +38,3 @@ export function shouldKeepRecordingAudioSidecars(env: NodeJS.ProcessEnv = proces
const value = env[RECORDING_AUDIO_SIDECAR_DEBUG_ENV]?.trim().toLowerCase();
return value === "1" || value === "true" || value === "yes" || value === "on";
}
/**
* Toggle for the recording finalization optimizations.
* - true: Fast probe (no -count_frames), separate audio tracks (no muxing).
* - false: slow probe (full decode), combined audio/video (heavy muxing).
*/
export const OPTIMIZE_RECORDING_FINALIZATION = true;
-2
View File
@@ -2,7 +2,6 @@ import { execFile } from "node:child_process";
import fs from "node:fs/promises";
import { promisify } from "node:util";
import { COMPANION_AUDIO_LAYOUTS } from "../constants";
import { OPTIMIZE_RECORDING_FINALIZATION } from "./audioFilters";
import { getFfmpegBinaryPath, getFfprobeBinaryPath } from "../ffmpeg/binary";
import { lastNativeCaptureDiagnostics, setLastNativeCaptureDiagnostics } from "../state";
import type { CompanionAudioCandidate, NativeCaptureDiagnostics } from "../types";
@@ -280,7 +279,6 @@ export async function probeVideoStreamDuration(
"error",
"-select_streams",
"v:0",
...(OPTIMIZE_RECORDING_FINALIZATION ? [] : ["-count_frames"]),
"-show_entries",
"stream=duration,nb_frames,nb_read_frames,avg_frame_rate,r_frame_rate",
"-of",
+22 -173
View File
@@ -1,14 +1,10 @@
import type { ChildProcessWithoutNullStreams } from "node:child_process";
import { execFile } from "node:child_process";
import fs from "node:fs/promises";
import { promisify } from "node:util";
import { BrowserWindow } from "electron";
import {
persistPendingCursorTelemetry,
snapshotCursorTelemetryForPersistence,
} from "../cursor/telemetry";
import { getFfmpegBinaryPath } from "../ffmpeg/binary";
import { appendSyncedAudioFilter, getAudioSyncAdjustment } from "../ffmpeg/filters";
import {
lastNativeCaptureDiagnostics,
nativeCaptureMicrophonePath,
@@ -27,22 +23,15 @@ import {
setNativeCaptureTargetPath,
setNativeScreenRecordingActive,
} from "../state";
import type { AudioSyncAdjustment } from "../types";
import { isAutoRecordingPath, moveFileWithOverwrite } from "../utils";
import { OPTIMIZE_RECORDING_FINALIZATION } from "./audioFilters";
import {
getFileSizeIfPresent,
getRecordingAudioMuxTimeoutMs,
getUsableCompanionAudioCandidates,
probeMediaDurationSeconds,
recordNativeCaptureDiagnostics,
validateRecordedVideo,
} from "./diagnostics";
import { emitRecordingInterrupted } from "./events";
import { pruneAutoRecordings } from "./prune";
import { muxNativeWindowsVideoWithAudio } from "./windows";
const execFileAsync = promisify(execFile);
export function waitForNativeCaptureStart(process: ChildProcessWithoutNullStreams) {
return new Promise<void>((resolve, reject) => {
@@ -129,155 +118,35 @@ export async function muxNativeMacRecordingWithAudio(
systemAudioPath?: string | null,
microphonePath?: string | null,
) {
const ffmpegPath = getFfmpegBinaryPath();
const mixedOutputPath = `${videoPath}.mixed.mp4`;
console.log("[mac-mux] Optimization active: keeping tracks separate.");
const videoPathWithoutExt = videoPath.replace(/\.[^.]+$/u, "");
const inputs = ["-i", videoPath];
const availableAudioInputs: string[] = [];
const audioFilePaths: string[] = [];
for (const [label, audioPath] of [
["system", systemAudioPath],
["microphone", microphonePath],
] as const) {
if (!audioPath) continue;
// Optimization: instead of heavy FFmpeg muxing, we ensure audio sidecars
// are available alongside the video for the editor.
if (systemAudioPath) {
const finalSystemPath = `${videoPathWithoutExt}.system.wav`;
try {
const stat = await fs.stat(audioPath);
if (stat.size <= 0) {
console.warn(`[mux] Skipping ${label} audio: file is empty (${audioPath})`);
await fs.rm(audioPath, { force: true }).catch(() => undefined);
continue;
const stat = await fs.stat(systemAudioPath);
if (stat.size > 0 && systemAudioPath !== finalSystemPath) {
await moveFileWithOverwrite(systemAudioPath, finalSystemPath);
}
inputs.push("-i", audioPath);
availableAudioInputs.push(label);
audioFilePaths.push(audioPath);
} catch {
console.warn(`[mux] Skipping ${label} audio: file not accessible (${audioPath})`);
} catch (err) {
console.error(`[mac-mux] Failed to handle system audio:`, err);
}
}
if (availableAudioInputs.length === 0) {
console.warn(
"[mux] No valid audio files to mux — video will have no audio. " +
`system=${systemAudioPath ?? "none"} mic=${microphonePath ?? "none"}`,
);
return;
}
const videoDuration = await probeMediaDurationSeconds(videoPath);
const muxTimeoutMs = getRecordingAudioMuxTimeoutMs(videoDuration);
const audioAdjustments: Map<string, AudioSyncAdjustment> = new Map();
if (videoDuration > 0) {
for (let i = 0; i < audioFilePaths.length; i++) {
const audioDuration = await probeMediaDurationSeconds(audioFilePaths[i]);
const adjustment = getAudioSyncAdjustment(videoDuration, audioDuration);
audioAdjustments.set(availableAudioInputs[i], adjustment);
if (adjustment.mode === "tempo") {
console.log(
`[mux] ${availableAudioInputs[i]} audio differs from video by ${adjustment.durationDeltaMs}ms — applying tempo ratio ${adjustment.tempoRatio.toFixed(6)}`,
);
} else if (adjustment.mode === "delay" && adjustment.delayMs > 0) {
console.log(
`[mux] ${availableAudioInputs[i]} audio appears to start late by ${adjustment.delayMs}ms — adding leading silence`,
);
if (microphonePath) {
const finalMicPath = `${videoPathWithoutExt}.mic.wav`;
try {
const stat = await fs.stat(microphonePath);
if (stat.size > 0 && microphonePath !== finalMicPath) {
await moveFileWithOverwrite(microphonePath, finalMicPath);
}
} catch (err) {
console.error(`[mac-mux] Failed to handle mic audio:`, err);
}
}
const systemAdjustment = audioAdjustments.get("system") ?? {
mode: "none",
delayMs: 0,
tempoRatio: 1,
durationDeltaMs: 0,
};
const micAdjustment = audioAdjustments.get("microphone") ?? {
mode: "none",
delayMs: 0,
tempoRatio: 1,
durationDeltaMs: 0,
};
// Always route through the filter graph so that aresample=async=1 is
// applied to every audio stream. This corrects progressive clock drift
// between the video and audio tracks that a simple duration comparison
// cannot detect (e.g. audio gradually falling behind under CPU load).
let args: string[];
if (availableAudioInputs.length === 2) {
const filterParts: string[] = [];
appendSyncedAudioFilter(filterParts, "[1:a]", "s", systemAdjustment);
appendSyncedAudioFilter(filterParts, "[2:a]", "m", micAdjustment);
filterParts.push("[s][m]amix=inputs=2:duration=longest:normalize=0[aout]");
args = [
"-y",
"-hide_banner",
"-nostdin",
"-nostats",
...inputs,
"-filter_complex",
filterParts.join(";"),
"-map",
"0:v:0",
"-map",
"[aout]",
"-c:v",
"copy",
"-c:a",
"aac",
"-b:a",
"192k",
"-shortest",
mixedOutputPath,
];
} else {
const singleAdjustment = audioAdjustments.get(availableAudioInputs[0]) ?? {
mode: "none",
delayMs: 0,
tempoRatio: 1,
durationDeltaMs: 0,
};
const filterParts: string[] = [];
appendSyncedAudioFilter(filterParts, "[1:a]", "aout", singleAdjustment);
args = [
"-y",
"-hide_banner",
"-nostdin",
"-nostats",
...inputs,
"-filter_complex",
filterParts.join(";"),
"-map",
"0:v:0",
"-map",
"[aout]",
"-c:v",
"copy",
"-c:a",
"aac",
"-b:a",
"192k",
"-shortest",
mixedOutputPath,
];
}
console.log("[mux] Running ffmpeg:", ffmpegPath, args.join(" "));
try {
await execFileAsync(ffmpegPath, args, {
timeout: muxTimeoutMs,
maxBuffer: 20 * 1024 * 1024,
});
await validateRecordedVideo(mixedOutputPath);
} catch (error) {
const execError = error as NodeJS.ErrnoException & { stderr?: string };
console.error("[mux] failed:", execError.stderr || execError.message || String(error));
await fs.rm(mixedOutputPath, { force: true }).catch(() => undefined);
throw error;
}
await moveFileWithOverwrite(mixedOutputPath, videoPath);
console.log("[mux] Successfully muxed audio into video:", videoPath);
}
export function attachNativeCaptureLifecycle(process: ChildProcessWithoutNullStreams) {
@@ -290,6 +159,7 @@ export function attachNativeCaptureLifecycle(process: ChildProcessWithoutNullStr
}
setNativeScreenRecordingActive(false);
console.log("[mac-finalize] Optimization active: skipping safety-net muxing.");
setNativeCaptureTargetPath(null);
setNativeCaptureStopRequested(false);
setNativeCaptureSystemAudioPath(null);
@@ -318,28 +188,7 @@ export function attachNativeCaptureLifecycle(process: ChildProcessWithoutNullStr
}
export async function finalizeStoredVideo(videoPath: string) {
// Safety net: if companion audio files still exist, the mux was skipped — attempt it now
if (!OPTIMIZE_RECORDING_FINALIZATION && videoPath.endsWith(".mp4")) {
const companionCandidates = await getUsableCompanionAudioCandidates(videoPath);
for (const { systemPath, micPath, platform } of companionCandidates) {
if (platform === "mac" || platform === "win") {
console.log(
`[finalize] Detected un-muxed ${platform} audio files alongside video — attempting safety-net mux`,
);
try {
if (platform === "win") {
await muxNativeWindowsVideoWithAudio(videoPath, systemPath, micPath);
} else {
await muxNativeMacRecordingWithAudio(videoPath, systemPath, micPath);
}
console.log("[finalize] Safety-net mux completed successfully");
} catch (error) {
console.warn("[finalize] Safety-net mux failed:", error);
}
break;
}
}
}
console.log("[finalize] Optimization active: skipping safety-net muxing.");
let validation: { fileSizeBytes: number; durationSeconds: number | null };
try {
+51 -353
View File
@@ -1,15 +1,7 @@
import type { ChildProcessWithoutNullStreams } from "node:child_process";
import { execFile } from "node:child_process";
import { constants as fsConstants } from "node:fs";
import fs from "node:fs/promises";
import { promisify } from "node:util";
import { BrowserWindow } from "electron";
import { getFfmpegBinaryPath } from "../ffmpeg/binary";
import {
appendSyncedAudioFilter,
applyRecordedAudioStartDelay,
getAudioSyncAdjustment,
} from "../ffmpeg/filters";
import { getWindowsCaptureExePath } from "../paths/binaries";
import {
selectedSource,
@@ -21,24 +13,11 @@ import {
windowsCaptureTargetPath,
windowsNativeCaptureActive,
} from "../state";
import type { AudioSyncAdjustment } from "../types";
import { moveFileWithOverwrite } from "../utils";
import {
OPTIMIZE_RECORDING_FINALIZATION,
shouldKeepRecordingAudioSidecars,
WINDOWS_NATIVE_MIC_PRE_FILTERS,
} from "./audioFilters";
import {
getCompanionAudioStartDelayMs,
getRecordingAudioMuxTimeoutMs,
probeMediaDurationSeconds,
probeVideoStreamDurationSeconds,
validateRecordedVideo,
} from "./diagnostics";
AudioSyncAdjustment,
} from "../types";
import { emitRecordingInterrupted } from "./events";
const execFileAsync = promisify(execFile);
const MIN_NATIVE_WINDOWS_VIDEO_PAD_MS = 500;
import { moveFileWithOverwrite } from "../utils";
export type NativeWindowsVideoPaddingResult = {
padded: boolean;
@@ -64,7 +43,7 @@ export type NativeWindowsAudioMuxResult = {
}
>;
outputPath?: string;
keptAudioSidecars: boolean;
keptAudioSidecars?: boolean;
};
export async function isNativeWindowsCaptureAvailable(): Promise<boolean> {
@@ -190,97 +169,6 @@ export function attachWindowsCaptureLifecycle(proc: ChildProcessWithoutNullStrea
});
}
export async function extendNativeWindowsVideoToDuration(
videoPath: string,
targetDurationMs: number | null | undefined,
): Promise<NativeWindowsVideoPaddingResult> {
const start = Date.now();
console.log("[PERF:MAIN] extendNativeWindowsVideoToDuration: STARTED");
try {
if (!Number.isFinite(targetDurationMs) || (targetDurationMs ?? 0) <= 0) {
return {
padded: false,
durationSeconds: 0,
containerDurationSeconds: await probeMediaDurationSeconds(videoPath),
targetDurationSeconds: 0,
padDurationSeconds: 0,
};
}
const containerDurationSeconds = await probeMediaDurationSeconds(videoPath);
const currentDurationSeconds = await probeVideoStreamDurationSeconds(videoPath);
if (currentDurationSeconds <= 0) {
return {
padded: false,
durationSeconds: currentDurationSeconds,
containerDurationSeconds,
targetDurationSeconds: (targetDurationMs ?? 0) / 1000,
padDurationSeconds: 0,
};
}
const targetDurationSeconds = (targetDurationMs ?? 0) / 1000;
const padDurationSeconds = targetDurationSeconds - currentDurationSeconds;
if (padDurationSeconds * 1000 < MIN_NATIVE_WINDOWS_VIDEO_PAD_MS) {
return {
padded: false,
durationSeconds: currentDurationSeconds,
containerDurationSeconds,
targetDurationSeconds,
padDurationSeconds: Math.max(0, padDurationSeconds),
};
}
const ffmpegPath = getFfmpegBinaryPath();
const paddedOutputPath = `${videoPath}.duration-padded.mp4`;
try {
await execFileAsync(
ffmpegPath,
[
"-y",
"-hide_banner",
"-nostdin",
"-nostats",
"-i",
videoPath,
"-vf",
`tpad=stop_mode=clone:stop_duration=${padDurationSeconds.toFixed(3)}`,
"-an",
"-c:v",
"libx264",
"-preset",
"veryfast",
"-crf",
"18",
"-pix_fmt",
"yuv420p",
"-movflags",
"+faststart",
paddedOutputPath,
],
{ timeout: 300000, maxBuffer: 10 * 1024 * 1024 },
);
await validateRecordedVideo(paddedOutputPath);
await moveFileWithOverwrite(paddedOutputPath, videoPath);
return {
padded: true,
durationSeconds: targetDurationSeconds,
containerDurationSeconds,
targetDurationSeconds,
padDurationSeconds,
};
} catch (error) {
await fs.rm(paddedOutputPath, { force: true }).catch(() => undefined);
throw error;
}
} finally {
console.log(
`[PERF:MAIN] extendNativeWindowsVideoToDuration: COMPLETED in ${Date.now() - start}ms`,
);
}
}
export async function muxNativeWindowsVideoWithAudio(
videoPath: string,
systemAudioPath: string | null,
@@ -288,253 +176,63 @@ export async function muxNativeWindowsVideoWithAudio(
): Promise<NativeWindowsAudioMuxResult> {
const start = Date.now();
console.log("[PERF:MAIN] muxNativeWindowsVideoWithAudio: STARTED");
try {
const ffmpegPath = getFfmpegBinaryPath();
const keepAudioSidecars = shouldKeepRecordingAudioSidecars();
const inputs: string[] = ["-i", videoPath];
const audioInputs: string[] = [];
const audioFilePaths: string[] = [];
const audio: NativeWindowsAudioMuxResult["audio"] = {};
const audioInputs: string[] = [];
for (const [label, audioPath] of [
["system", systemAudioPath],
["mic", micAudioPath],
] as const) {
if (!audioPath) continue;
const videoPathWithoutExt = videoPath.replace(/\.[^.]+$/u, "");
// Optimization: instead of heavy FFmpeg muxing, we just move the audio sidecars
// to their final companion paths so the editor can find them as separate tracks.
if (systemAudioPath) {
const finalSystemPath = `${videoPathWithoutExt}.system.wav`;
try {
const stat = await fs.stat(audioPath);
if (stat.size <= 0) {
console.warn(`[mux-win] Skipping ${label} audio: file is empty (${audioPath})`);
if (!keepAudioSidecars) {
await fs.rm(audioPath, { force: true }).catch(() => undefined);
}
continue;
const stat = await fs.stat(systemAudioPath);
if (stat.size > 0) {
await moveFileWithOverwrite(systemAudioPath, finalSystemPath);
audioInputs.push("system");
audio.system = {
path: finalSystemPath,
sizeBytes: stat.size,
durationSeconds: 0,
startDelayMs: null,
adjustment: { mode: "none", delayMs: 0, tempoRatio: 1, durationDeltaMs: 0 },
};
}
inputs.push("-i", audioPath);
audioInputs.push(label);
audioFilePaths.push(audioPath);
audio[label] = {
path: audioPath,
sizeBytes: stat.size,
durationSeconds: 0,
startDelayMs: null,
adjustment: {
mode: "none",
delayMs: 0,
tempoRatio: 1,
durationDeltaMs: 0,
},
};
} catch {
console.warn(`[mux-win] Skipping ${label} audio: file not accessible (${audioPath})`);
} catch (err) {
console.error(`[mux-win] Failed to handle system audio:`, err);
}
}
const videoDuration = await probeVideoStreamDurationSeconds(videoPath);
const muxTimeoutMs = getRecordingAudioMuxTimeoutMs(videoDuration);
const audioAdjustments: Map<string, AudioSyncAdjustment> = new Map();
if (audioInputs.length === 0) {
return {
muxed: false,
videoDurationSeconds: videoDuration,
muxTimeoutMs,
audioInputs,
audio,
keptAudioSidecars: keepAudioSidecars,
};
}
if (videoDuration > 0) {
for (let i = 0; i < audioFilePaths.length; i++) {
const audioDuration = await probeMediaDurationSeconds(audioFilePaths[i]);
const recordedStartDelayMs = await getCompanionAudioStartDelayMs(audioFilePaths[i]);
const adjustment = applyRecordedAudioStartDelay(
getAudioSyncAdjustment(videoDuration, audioDuration),
recordedStartDelayMs,
);
audioAdjustments.set(audioInputs[i], adjustment);
audio[audioInputs[i]] = {
...audio[audioInputs[i]],
durationSeconds: audioDuration,
startDelayMs: recordedStartDelayMs,
adjustment,
};
if (Number.isFinite(recordedStartDelayMs) && adjustment.mode === "delay") {
console.log(
`[mux-win] ${audioInputs[i]} audio recorded a start delay of ${adjustment.delayMs}ms`,
);
} else if (Number.isFinite(recordedStartDelayMs) && adjustment.mode === "pad") {
console.log(
`[mux-win] ${audioInputs[i]} audio started on time but ends ${adjustment.durationDeltaMs}ms early — padding trailing silence`,
);
} else if (adjustment.mode === "tempo") {
console.log(
`[mux-win] ${audioInputs[i]} audio differs from video by ${adjustment.durationDeltaMs}ms — applying tempo ratio ${adjustment.tempoRatio.toFixed(6)}`,
);
} else if (adjustment.mode === "delay" && adjustment.delayMs > 0) {
console.log(
`[mux-win] ${audioInputs[i]} audio appears to start late by ${adjustment.delayMs}ms — adding leading silence`,
);
} else if (adjustment.mode === "pad" && adjustment.durationDeltaMs > 0) {
console.log(
`[mux-win] ${audioInputs[i]} audio is much shorter than video by ${adjustment.durationDeltaMs}ms — padding trailing silence`,
);
if (micAudioPath) {
const finalMicPath = `${videoPathWithoutExt}.mic.wav`;
try {
const stat = await fs.stat(micAudioPath);
if (stat.size > 0) {
await moveFileWithOverwrite(micAudioPath, finalMicPath);
audioInputs.push("mic");
audio.mic = {
path: finalMicPath,
sizeBytes: stat.size,
durationSeconds: 0,
startDelayMs: null,
adjustment: { mode: "none", delayMs: 0, tempoRatio: 1, durationDeltaMs: 0 },
};
}
} catch (err) {
console.error(`[mux-win] Failed to handle mic audio:`, err);
}
}
const mixedOutputPath = `${videoPath}.muxed.mp4`;
const systemAdjustment = audioAdjustments.get("system") ?? {
mode: "none",
delayMs: 0,
tempoRatio: 1,
durationDeltaMs: 0,
console.log(
`[PERF:MAIN] muxNativeWindowsVideoWithAudio: COMPLETED in ${Date.now() - start}ms`,
);
return {
muxed: false,
videoDurationSeconds: 0, // No longer needed here
muxTimeoutMs: 0,
audioInputs,
audio,
keptAudioSidecars: true,
};
const micAdjustment = audioAdjustments.get("mic") ?? {
mode: "none",
delayMs: 0,
tempoRatio: 1,
durationDeltaMs: 0,
};
if (OPTIMIZE_RECORDING_FINALIZATION) {
console.log("[mux-win] Optimization enabled: skipping heavy muxing, keeping tracks separate.");
const videoPathWithoutExt = videoPath.replace(/\.[^.]+$/u, "");
// Move audio sidecars to final companion paths if they exist
if (systemAudioPath) {
const finalSystemPath = `${videoPathWithoutExt}.system.wav`;
if (systemAudioPath !== finalSystemPath) {
try {
await moveFileWithOverwrite(systemAudioPath, finalSystemPath);
if (audio.system) audio.system.path = finalSystemPath;
} catch (err) {
console.error(`[mux-win] Failed to move system audio to ${finalSystemPath}:`, err);
}
}
}
if (micAudioPath) {
const finalMicPath = `${videoPathWithoutExt}.mic.wav`;
if (micAudioPath !== finalMicPath) {
try {
await moveFileWithOverwrite(micAudioPath, finalMicPath);
if (audio.mic) audio.mic.path = finalMicPath;
} catch (err) {
console.error(`[mux-win] Failed to move mic audio to ${finalMicPath}:`, err);
}
}
}
return {
muxed: false,
videoDurationSeconds: videoDuration,
muxTimeoutMs,
audioInputs,
audio,
keptAudioSidecars: true,
};
}
try {
if (audioInputs.length === 2) {
const filterParts: string[] = [];
appendSyncedAudioFilter(filterParts, "[1:a]", "s", systemAdjustment);
appendSyncedAudioFilter(filterParts, "[2:a]", "m", micAdjustment, {
preFilters: WINDOWS_NATIVE_MIC_PRE_FILTERS,
});
filterParts.push("[s][m]amix=inputs=2:duration=longest:normalize=0[aout]");
await execFileAsync(
ffmpegPath,
[
"-y",
"-hide_banner",
"-nostdin",
"-nostats",
...inputs,
"-filter_complex",
filterParts.join(";"),
"-map",
"0:v:0",
"-map",
"[aout]",
"-c:v",
"copy",
"-c:a",
"aac",
"-b:a",
"192k",
"-shortest",
mixedOutputPath,
],
{ timeout: muxTimeoutMs, maxBuffer: 20 * 1024 * 1024 },
);
} else {
const singleAdjustment = audioAdjustments.get(audioInputs[0]) ?? {
mode: "none",
delayMs: 0,
tempoRatio: 1,
durationDeltaMs: 0,
};
const filterParts: string[] = [];
// Always route through the filter graph so that aresample=async=1 is
// applied. This corrects progressive clock drift between video and
// audio tracks that a simple duration comparison cannot detect.
appendSyncedAudioFilter(
filterParts,
"[1:a]",
"aout",
singleAdjustment,
audioInputs[0] === "mic" ? { preFilters: WINDOWS_NATIVE_MIC_PRE_FILTERS } : 1,
);
await execFileAsync(
ffmpegPath,
[
"-y",
"-hide_banner",
"-nostdin",
"-nostats",
...inputs,
"-filter_complex",
filterParts.join(";"),
"-map",
"0:v:0",
"-map",
"[aout]",
"-c:v",
"copy",
"-c:a",
"aac",
"-b:a",
"192k",
"-shortest",
mixedOutputPath,
],
{ timeout: muxTimeoutMs, maxBuffer: 20 * 1024 * 1024 },
);
}
await validateRecordedVideo(mixedOutputPath);
await moveFileWithOverwrite(mixedOutputPath, videoPath);
} catch (error) {
await fs.rm(mixedOutputPath, { force: true }).catch(() => undefined);
throw error;
}
return {
muxed: true,
videoDurationSeconds: videoDuration,
muxTimeoutMs,
audioInputs,
audio,
outputPath: videoPath,
keptAudioSidecars: true,
};
} finally {
console.log(
`[PERF:MAIN] muxNativeWindowsVideoWithAudio: COMPLETED in ${Date.now() - start}ms`,
);
}
}
+1 -31
View File
@@ -41,7 +41,6 @@ import {
import { rememberApprovedLocalReadPath } from "../project/manager";
import {
getBrowserMicSidecarFilters,
OPTIMIZE_RECORDING_FINALIZATION,
shouldKeepRecordingAudioSidecars,
} from "../recording/audioFilters";
import {
@@ -70,7 +69,6 @@ import {
} from "../recording/mac";
import {
attachWindowsCaptureLifecycle,
extendNativeWindowsVideoToDuration,
isNativeWindowsCaptureAvailable,
muxNativeWindowsVideoWithAudio,
waitForWindowsCaptureStart,
@@ -1300,35 +1298,7 @@ export function registerRecordingHandlers(
hasOrphanedMicrophone: Boolean(orphanedMicAudioPath),
},
});
if (!OPTIMIZE_RECORDING_FINALIZATION && expectedDurationMs) {
try {
const padding = await extendNativeWindowsVideoToDuration(
videoPath,
expectedDurationMs,
);
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "pad",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
details: { ...padding },
});
} catch (paddingError) {
console.warn(
"[mux-win] Failed to extend native Windows video duration:",
paddingError,
);
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "pad",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
error: String(paddingError),
});
}
}
console.log("[mux-win] Optimization active: skipping video padding.");
let muxDetails: unknown = null;
if (diagnosticsSystemAudioPath || diagnosticsMicAudioPath) {