diff --git a/electron/ipc/recording/audioFilters.ts b/electron/ipc/recording/audioFilters.ts index 9f3ea4b1..b465f5f2 100644 --- a/electron/ipc/recording/audioFilters.ts +++ b/electron/ipc/recording/audioFilters.ts @@ -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; diff --git a/electron/ipc/recording/diagnostics.ts b/electron/ipc/recording/diagnostics.ts index 03299f0a..77b34c45 100644 --- a/electron/ipc/recording/diagnostics.ts +++ b/electron/ipc/recording/diagnostics.ts @@ -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", diff --git a/electron/ipc/recording/mac.ts b/electron/ipc/recording/mac.ts index ef1b8b13..0017188e 100644 --- a/electron/ipc/recording/mac.ts +++ b/electron/ipc/recording/mac.ts @@ -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((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 = 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 { diff --git a/electron/ipc/recording/windows.ts b/electron/ipc/recording/windows.ts index 2dc016bf..70c73cbf 100644 --- a/electron/ipc/recording/windows.ts +++ b/electron/ipc/recording/windows.ts @@ -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 { @@ -190,97 +169,6 @@ export function attachWindowsCaptureLifecycle(proc: ChildProcessWithoutNullStrea }); } -export async function extendNativeWindowsVideoToDuration( - videoPath: string, - targetDurationMs: number | null | undefined, -): Promise { - 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 { 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 = 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`, - ); - } } diff --git a/electron/ipc/register/recording.ts b/electron/ipc/register/recording.ts index 15e2e95d..3e8b4c93 100644 --- a/electron/ipc/register/recording.ts +++ b/electron/ipc/register/recording.ts @@ -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) {