From b4ac0cdc92b5743250453278ecca971704a11bd5 Mon Sep 17 00:00:00 2001 From: Myx Date: Thu, 16 Apr 2026 19:07:44 +0200 Subject: [PATCH] fix: Windows audio mute fix --- electron/window/create-window.ts | 33 +++++++++++++++++++ .../desktop-electron-screen-share.capture.ts | 16 +++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/electron/window/create-window.ts b/electron/window/create-window.ts index a88f249..9cad765 100644 --- a/electron/window/create-window.ts +++ b/electron/window/create-window.ts @@ -210,6 +210,39 @@ export async function createWindow(): Promise { ); } + if (process.platform === 'win32') { + session.defaultSession.setDisplayMediaRequestHandler( + async (request, respond) => { + // On Windows the system picker (useSystemPicker: true) is preferred. + // This handler is only reached when the system picker is unavailable. + // Include loopback audio when the renderer requested it so that + // getDisplayMedia receives an audio track and the renderer-side + // restrictOwnAudio constraint can keep the app's own voice playback + // out of the captured stream. + try { + const sources = await desktopCapturer.getSources({ + types: ['window', 'screen'], + thumbnailSize: { width: 150, height: 150 } + }); + const firstSource = sources[0]; + + if (firstSource) { + respond({ + video: firstSource, + ...(request.audioRequested ? { audio: 'loopback' } : {}) + }); + return; + } + } catch { + // desktopCapturer also unavailable + } + + respond({}); + }, + { useSystemPicker: true } + ); + } + if (process.env['NODE_ENV'] === 'development') { const devUrl = process.env['SSL'] === 'true' ? 'https://localhost:4200' diff --git a/toju-app/src/app/infrastructure/realtime/media/screen-share-platforms/desktop-electron-screen-share.capture.ts b/toju-app/src/app/infrastructure/realtime/media/screen-share-platforms/desktop-electron-screen-share.capture.ts index 98705b2..61ab8b3 100644 --- a/toju-app/src/app/infrastructure/realtime/media/screen-share-platforms/desktop-electron-screen-share.capture.ts +++ b/toju-app/src/app/infrastructure/realtime/media/screen-share-platforms/desktop-electron-screen-share.capture.ts @@ -49,9 +49,19 @@ export class DesktopElectronScreenShareCapture { const sources = await electronApi.getSources(); const selection = await this.resolveSourceSelection(sources, options.includeSystemAudio); + + // On Windows, electron-desktop loopback audio captures all system output + // including the app's voice playback, creating echo for watchers or + // requiring total voice muting for the sharer. The getDisplayMedia path + // handles this correctly via restrictOwnAudio — if we fell back here, + // share video only so voice chat stays functional. + const effectiveIncludeSystemAudio = this.isWindowsElectron() + ? false + : selection.includeSystemAudio; + const captureOptions = { ...options, - includeSystemAudio: selection.includeSystemAudio + includeSystemAudio: effectiveIncludeSystemAudio }; if (!selection.source) { @@ -59,7 +69,7 @@ export class DesktopElectronScreenShareCapture { } this.logger.info('Selected Electron desktop source', { - includeSystemAudio: selection.includeSystemAudio, + includeSystemAudio: effectiveIncludeSystemAudio, sourceId: selection.source.id, sourceName: selection.source.name }); @@ -73,7 +83,7 @@ export class DesktopElectronScreenShareCapture { } return { - includeSystemAudio: selection.includeSystemAudio, + includeSystemAudio: effectiveIncludeSystemAudio, stream: await navigator.mediaDevices.getUserMedia(constraints) }; }