diff --git a/src/app/core/services/webrtc/screen-share.manager.ts b/src/app/core/services/webrtc/screen-share.manager.ts index c18dd66..a84abc9 100644 --- a/src/app/core/services/webrtc/screen-share.manager.ts +++ b/src/app/core/services/webrtc/screen-share.manager.ts @@ -190,10 +190,12 @@ export class ScreenShareManager { /** * Begin screen sharing. * - * On Linux Electron builds, prefers a dedicated PulseAudio/PipeWire routing - * path so remote voice playback is kept out of captured system audio. - * On other Electron builds, uses desktop capture. In browser contexts, uses - * `getDisplayMedia`. + * On Linux Electron builds, prefers a dedicated PulseAudio/PipeWire routing + * path so remote voice playback is kept out of captured system audio. + * On Windows Electron builds, prefers `getDisplayMedia` with system audio + * so the separate mic `getUserMedia` stream is not disrupted; falls back to + * Electron desktop capture only when `getDisplayMedia` fails entirely. + * In browser contexts, uses `getDisplayMedia`. * * @param options - Screen-share capture options. * @returns The captured screen {@link MediaStream}. @@ -230,16 +232,27 @@ export class ScreenShareManager { } } - if (!this.activeScreenStream && shareOptions.includeSystemAudio && !electronDesktopCaptureAvailable) { + if (!this.activeScreenStream && shareOptions.includeSystemAudio) { try { this.activeScreenStream = await this.startWithDisplayMedia(shareOptions, preset); captureMethod = 'display-media'; if (this.activeScreenStream.getAudioTracks().length === 0) { - this.logger.warn('getDisplayMedia did not provide system audio; trying Electron desktop capture'); - this.activeScreenStream.getTracks().forEach((track) => track.stop()); - this.activeScreenStream = null; - captureMethod = null; + if (electronDesktopCaptureAvailable) { + // On Windows Electron, keep the getDisplayMedia stream for video + // rather than falling through to getUserMedia desktop audio which + // can replace or kill the active mic stream. + this.logger.warn( + 'getDisplayMedia did not provide system audio; ' + + 'continuing without system audio to preserve mic stream' + ); + shareOptions.includeSystemAudio = false; + } else { + this.logger.warn('getDisplayMedia did not provide system audio; trying next capture method'); + this.activeScreenStream.getTracks().forEach((track) => track.stop()); + this.activeScreenStream = null; + captureMethod = null; + } } } catch (error) { this.rethrowIfScreenShareAborted(error); @@ -426,9 +439,12 @@ export class ScreenShareManager { private shouldSuppressRemotePlaybackDuringShare( includeSystemAudio: boolean, - captureMethod: ScreenShareCaptureMethod | null + _captureMethod: ScreenShareCaptureMethod | null ): boolean { - return includeSystemAudio && captureMethod === 'electron-desktop' && this.isWindowsElectron(); + // On Windows Electron, system audio capture (via getDisplayMedia or + // desktop capturer) includes all output audio. Remote voice playback + // must be suppressed to avoid a feedback loop regardless of capture method. + return includeSystemAudio && this.isWindowsElectron(); } private getRequiredLinuxElectronApi(): Required