diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index 5db60fd4..8cce8eb4 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -1113,25 +1113,63 @@ function waitForNativeCaptureStop(process: ChildProcessWithoutNullStreams) { }) } +async function fileHasAudioStream(filePath: string) { + const ffmpegPath = getFfmpegBinaryPath() + + try { + await execFileAsync( + ffmpegPath, + ['-i', filePath], + { timeout: 30000, maxBuffer: 10 * 1024 * 1024 }, + ) + } catch (error) { + const stderr = typeof error === 'object' && error !== null && 'stderr' in error + ? String((error as { stderr?: unknown }).stderr ?? '') + : '' + + if (stderr) { + return /Audio:/i.test(stderr) + } + } + + return false +} + async function mixNativeMacAudioTracks(videoPath: string, microphonePath: string) { const ffmpegPath = getFfmpegBinaryPath() const mixedOutputPath = `${videoPath}.mixed.mp4` + const videoHasAudio = await fileHasAudioStream(videoPath) + + const args = videoHasAudio + ? [ + '-y', + '-i', videoPath, + '-i', microphonePath, + '-filter_complex', '[0:a][1:a]amix=inputs=2:duration=longest:normalize=0[aout]', + '-map', '0:v:0', + '-map', '[aout]', + '-c:v', 'copy', + '-c:a', 'aac', + '-b:a', '192k', + '-shortest', + mixedOutputPath, + ] + : [ + '-y', + '-i', videoPath, + '-i', microphonePath, + '-map', '0:v:0', + '-map', '1:a:0', + '-c:v', 'copy', + '-c:a', 'aac', + '-b:a', '192k', + '-shortest', + mixedOutputPath, + ] await execFileAsync( ffmpegPath, - [ - '-y', - '-i', videoPath, - '-i', microphonePath, - '-filter_complex', '[0:a][1:a]amix=inputs=2:duration=longest:normalize=0[aout]', - '-map', '0:v:0', - '-map', '[aout]', - '-c:v', 'copy', - '-c:a', 'aac', - '-b:a', '192k', - '-shortest', - mixedOutputPath, - ], + args, { timeout: 120000, maxBuffer: 10 * 1024 * 1024 }, ) diff --git a/electron/native/ScreenCaptureKitRecorder.swift b/electron/native/ScreenCaptureKitRecorder.swift index 89cd4c93..0b2c521e 100644 --- a/electron/native/ScreenCaptureKitRecorder.swift +++ b/electron/native/ScreenCaptureKitRecorder.swift @@ -82,7 +82,7 @@ final class ScreenCaptureRecorder: NSObject, SCStreamOutput, SCStreamDelegate { streamConfig.queueDepth = 6 streamConfig.pixelFormat = kCVPixelFormatType_32BGRA streamConfig.showsCursor = false - streamConfig.capturesAudio = capturesSystemAudio + streamConfig.capturesAudio = capturesSystemAudio || capturesMicrophone streamConfig.sampleRate = 48000 streamConfig.channelCount = 2 streamConfig.excludesCurrentProcessAudio = true diff --git a/electron/native/bin/darwin-arm64/openscreen-screencapturekit-helper b/electron/native/bin/darwin-arm64/openscreen-screencapturekit-helper index 1c5df9e3..54c53d95 100755 Binary files a/electron/native/bin/darwin-arm64/openscreen-screencapturekit-helper and b/electron/native/bin/darwin-arm64/openscreen-screencapturekit-helper differ diff --git a/electron/windows.ts b/electron/windows.ts index 05b35dff..4c21241a 100644 --- a/electron/windows.ts +++ b/electron/windows.ts @@ -22,7 +22,7 @@ let hudOverlayCaptureProtectionLoaded = false; let countdownWindow: BrowserWindow | null = null; const HUD_OVERLAY_SETTINGS_FILE = path.join(app.getPath("userData"), "hud-overlay-settings.json"); -const HUD_BOTTOM_CLEARANCE_CM = 7; +const HUD_BOTTOM_CLEARANCE_CM = 3.5; const DIP_PER_INCH = 96; const CM_PER_INCH = 2.54; const HUD_EDGE_MARGIN_DIP = 16;