// SPDX-License-Identifier: AGPL-3.0-or-later import {LimitResolver} from '@app/features/app/utils/LimitResolverAdapter'; import {isLimitToggleEnabled} from '@app/features/app/utils/LimitUtils'; import {Logger} from '@app/features/platform/utils/AppLogger'; import {getElectronAPI, supportsDesktopScreenShareAudioCapture} from '@app/features/ui/utils/NativeUtils'; import * as VoiceSettingsCommands from '@app/features/voice/commands/VoiceSettingsCommands'; import MediaEngine from '@app/features/voice/engine/MediaEngineFacade'; import ScreenShareCodecNegotiation from '@app/features/voice/engine/ScreenShareCodecNegotiation'; import ActiveScreenShareSource from '@app/features/voice/state/ActiveScreenShareSource'; import {clearDesktopSourceIntent, setDesktopSourceIntent} from '@app/features/voice/state/DesktopSourceIntent'; import LocalVoiceState from '@app/features/voice/state/LocalVoiceState'; import VoiceSettings from '@app/features/voice/state/VoiceSettings'; import { resolveScreenShareContentHintForContext, type ScreenShareContentSource, } from '@app/features/voice/utils/CodecCapabilityDetector'; import { LINUX_AUDIO_TARGET_OBJECTS_PATTERN_KEY, toNativeLinuxAudioPatterns, } from '@app/features/voice/utils/LinuxAudioSourceRules'; import {disarmVirtmic} from '@app/features/voice/utils/LinuxScreenShareAudio'; import { armNativeAudioForLinuxRouting, armNativeAudioForNextCapture, armNativeSystemAudioForNextCapture, disarmNativeAudio, disarmPendingNativeAudio, getLastNativeAudioArmFailure, } from '@app/features/voice/utils/NativeAudioCaptureBridge'; import { type ScreenShareAudioCaptureDebugInfo, ScreenShareAudioCaptureError, } from '@app/features/voice/utils/ScreenShareAudioCaptureError'; import { type DisplayShareEnvironment, getDisplayShareEnvironment, usesNativeDisplayShareAudioSelection, } from '@app/features/voice/utils/ScreenShareEnvironment'; import { buildScreenShareOptions, normaliseResolutionForContext, normaliseStreamingModeForContext, resolveStreamingModeSettings, type ScreenShareContext, } from '@app/features/voice/utils/ScreenShareOptions'; import { isScreenSharePortalUnavailableError, ScreenSharePortalUnavailableError, } from '@app/features/voice/utils/ScreenSharePortalUnavailableError'; import {executeScreenShareOperation} from '@app/features/voice/utils/ScreenShareUtils'; import type {NativeAudioStartOptions, VirtmicNode} from '@app/types/electron.d'; import type {ScreenShareCaptureOptions, VideoCodec} from 'livekit-client'; const logger = new Logger('ScreenShareStartFlow'); type LinuxNativeAudioRule = NonNullable; interface LinuxAudioLinkOptions { workaround: boolean; ignoreInputMedia: boolean; ignoreVirtual: boolean; ignoreDevices: boolean; } function getLinkOptions(): LinuxAudioLinkOptions { return { workaround: VoiceSettings.getLinuxAudioCaptureWorkaround(), ignoreInputMedia: VoiceSettings.getLinuxAudioCaptureIgnoreInputMedia(), ignoreVirtual: VoiceSettings.getLinuxAudioCaptureIgnoreVirtual(), ignoreDevices: VoiceSettings.getLinuxAudioCaptureIgnoreDevices(), }; } function getSystemOptions() { return { ...getLinkOptions(), onlySpeakers: VoiceSettings.getLinuxAudioCaptureOnlySpeakers(), onlyDefaultSpeakers: VoiceSettings.getLinuxAudioCaptureOnlyDefaultSpeakers(), }; } function withNativeAudioExcludes(exclude: Array, options: LinuxAudioLinkOptions): Array { const next = toNativeLinuxAudioPatterns(exclude); if (options.ignoreVirtual) { next.push({'node.virtual': 'true'}); } return next; } function buildLinuxNativeAudioRule( sourceMode: 'system' | 'specific', userIncludeSources: Array, userExcludeSources: Array, ): LinuxNativeAudioRule { const linkOptions = getLinkOptions(); const nativeIncludeSources = toNativeLinuxAudioPatterns(userIncludeSources); if (sourceMode === 'specific' && nativeIncludeSources.length > 0) { const includesDeviceTarget = nativeIncludeSources.some( (source) => LINUX_AUDIO_TARGET_OBJECTS_PATTERN_KEY in source, ); return { include: nativeIncludeSources, exclude: withNativeAudioExcludes([], linkOptions), ignoreDevices: includesDeviceTarget ? false : linkOptions.ignoreDevices, }; } const systemOptions = getSystemOptions(); return { include: [], exclude: withNativeAudioExcludes(userExcludeSources, linkOptions), ignoreDevices: systemOptions.ignoreDevices, onlySpeakers: systemOptions.onlySpeakers, onlyDefaultSpeakers: systemOptions.onlyDefaultSpeakers, }; } export async function reconfigureActiveLinuxScreenShareAudioLink(): Promise { const electronApi = getElectronAPI(); const virtmicApi = electronApi?.virtmic; if (!electronApi || electronApi.platform !== 'linux') { return false; } const sourceMode = VoiceSettings.getScreenShareAudioSourceMode(); const userIncludeSources = VoiceSettings.getScreenShareAudioIncludeSources().map((entry) => ({...entry})); const userExcludeSources = VoiceSettings.getScreenShareAudioExcludeSources().map((entry) => ({...entry})); if (sourceMode === 'none') { disarmVirtmic(); disarmNativeAudio(); await virtmicApi?.stop(); return true; } const nativeRule = buildLinuxNativeAudioRule(sourceMode, userIncludeSources, userExcludeSources); if (await MediaEngine.ensureLinuxScreenShareAudioPublication(nativeRule).catch(() => false)) { disarmVirtmic(); await virtmicApi?.stop(); return true; } disarmNativeAudio(); return false; } export async function stopActiveLinuxScreenShareAudioLink(): Promise { const electronApi = getElectronAPI(); const virtmicApi = electronApi?.virtmic; if (!electronApi || electronApi.platform !== 'linux') { return false; } disarmVirtmic(); disarmNativeAudio(); await virtmicApi?.stop(); return true; } function hasHigherVideoQuality(): boolean { return isLimitToggleEnabled( { feature_higher_video_quality: LimitResolver.resolve({ key: 'feature_higher_video_quality', fallback: 0, }), }, 'feature_higher_video_quality', ); } function didScreenShareStart(): boolean { return Boolean(MediaEngine.room?.localParticipant?.isScreenShareEnabled || LocalVoiceState.getSelfStream()); } function getScreenShareContentSource( shareContext: ScreenShareContext, preferredDisplaySurface?: 'window' | 'monitor', ): ScreenShareContentSource { if (shareContext === 'device') return 'device'; if (preferredDisplaySurface === 'window') return 'app'; return 'display'; } export function normaliseDeviceScreenShareSettings(): void { const nextStreamingMode = normaliseStreamingModeForContext(VoiceSettings.getStreamingMode(), 'device'); const nextResolution = normaliseResolutionForContext( VoiceSettings.getScreenshareResolution(), 'device', hasHigherVideoQuality(), ); if ( nextStreamingMode === VoiceSettings.getStreamingMode() && nextResolution === VoiceSettings.getScreenshareResolution() ) { return; } VoiceSettingsCommands.update({ streamingMode: nextStreamingMode, screenshareResolution: nextResolution, }); } function shouldIncludeAudioForShare( shareContext: ScreenShareContext, displayShareEnvironment: DisplayShareEnvironment, sourceId?: string | null, preferredDisplaySurface?: 'window' | 'monitor', ): boolean { if (shareContext === 'device') { return VoiceSettings.getShareDeviceAudio(); } if (displayShareEnvironment === 'web') { return supportsDesktopScreenShareAudioCapture(); } if (sourceId?.startsWith('window:')) { return supportsDesktopScreenShareAudioCapture() && VoiceSettings.getShareAppAudio(); } if (sourceId?.startsWith('screen:')) { return supportsDesktopScreenShareAudioCapture() && VoiceSettings.getShareDesktopAudio(); } if (preferredDisplaySurface === 'window') { return supportsDesktopScreenShareAudioCapture() && VoiceSettings.getShareAppAudio(); } if (shareContext === 'display' && usesNativeDisplayShareAudioSelection(displayShareEnvironment)) { return supportsDesktopScreenShareAudioCapture() && VoiceSettings.getShareDesktopAudio(); } return supportsDesktopScreenShareAudioCapture() && VoiceSettings.getShareDesktopAudio(); } function removeAudioFromCaptureOptions(captureOptions: ScreenShareCaptureOptions): void { captureOptions.audio = false; captureOptions.systemAudio = 'exclude'; captureOptions.windowAudio = 'exclude'; } function buildAudioCaptureFailureDebug( overrides: { platform?: string | null; sourceId?: string | null; sourceMode?: string | null; reason?: string | null; detail?: string | null; } = {}, ): ScreenShareAudioCaptureDebugInfo { return { platform: overrides.platform ?? getElectronAPI()?.platform ?? null, ...getLastNativeAudioArmFailure(), ...overrides, }; } function degradeAudioToVideoOnly( captureOptions: ScreenShareCaptureOptions, debugInfo: ScreenShareAudioCaptureDebugInfo, ): void { logger.warn('Screen share audio capture unavailable; proceeding with video only', debugInfo); removeAudioFromCaptureOptions(captureOptions); } function failRequestedAudioCapture(debugInfo: ScreenShareAudioCaptureDebugInfo): never { logger.warn('Screen share audio capture was requested but could not start', debugInfo); throw new ScreenShareAudioCaptureError(debugInfo); } function cleanupNativeAudioAfterCaptureDidNotStart(mode: 'start' | 'switch'): void { if (mode === 'switch') { disarmPendingNativeAudio(); return; } disarmNativeAudio(); } function getConfiguredScreenShareOptions( shareContext: ScreenShareContext, displayShareEnvironment: DisplayShareEnvironment, sourceDimensions?: { width: number; height: number; }, sourceId?: string | null, preferredDisplaySurface?: 'window' | 'monitor', videoCodec?: VideoCodec, includeAudioOverride?: boolean, ) { const currentResolution = VoiceSettings.getScreenshareResolution(); const currentStreamingMode = VoiceSettings.getStreamingMode(); const higherQuality = hasHigherVideoQuality(); const normalisedStreamingMode = normaliseStreamingModeForContext(currentStreamingMode, shareContext); const normalisedResolution = normaliseResolutionForContext(currentResolution, shareContext, higherQuality); const codecPreference = VoiceSettings.getPreferredScreenShareCodec(); const preferredVideoCodec = videoCodec ?? ScreenShareCodecNegotiation.selectScreenShareCodec(codecPreference); const contentHint = resolveScreenShareContentHintForContext( VoiceSettings.getScreenShareContentHintOverride(), preferredVideoCodec, getScreenShareContentSource(shareContext, preferredDisplaySurface), normalisedStreamingMode, ); const {resolution, frameRate} = resolveStreamingModeSettings( normalisedStreamingMode, normalisedResolution, VoiceSettings.getVideoFrameRate(), higherQuality, ); const includeAudio = includeAudioOverride ?? shouldIncludeAudioForShare(shareContext, displayShareEnvironment, sourceId, preferredDisplaySurface); if (!includeAudio && shareContext !== 'device' && supportsDesktopScreenShareAudioCapture()) { logger.info('Screen share audio not requested for this surface', { sourceId, preferredDisplaySurface, shareAppAudio: VoiceSettings.getShareAppAudio(), shareDesktopAudio: VoiceSettings.getShareDesktopAudio(), }); } const {captureOptions, publishOptions} = buildScreenShareOptions({ resolution, frameRate, includeAudio, streamingMode: normalisedStreamingMode, contentHint, sourceDimensions, preferredDisplaySurface, useBrowserAudioPicker: displayShareEnvironment === 'web', }); publishOptions.videoCodec = preferredVideoCodec; return { captureOptions, publishOptions, includeAudio, audioDeviceId: includeAudio ? VoiceSettings.getEffectiveScreenShareAudioDeviceId() || undefined : undefined, }; } export interface ConfiguredDisplayScreenShareOptions { sourceDimensions?: { width: number; height: number; }; preferredDisplaySurface?: 'window' | 'monitor'; isOwnWindow?: boolean; includeAudio?: boolean; } async function runConfiguredDisplayScreenShare( sourceId?: string | null, options?: ConfiguredDisplayScreenShareOptions, mode: 'start' | 'switch' = 'start', ): Promise { const electronApi = getElectronAPI(); const displayShareEnvironment = await getDisplayShareEnvironment(); const useWaylandPortal = displayShareEnvironment === 'desktop-wayland'; const { captureOptions, publishOptions, includeAudio: requestedAudio, } = getConfiguredScreenShareOptions( 'display', displayShareEnvironment, options?.sourceDimensions, sourceId, options?.preferredDisplaySurface, undefined, options?.includeAudio, ); if (electronApi) { const restartWaylandPortalForSwitch = useWaylandPortal && mode === 'switch'; if (!useWaylandPortal && !sourceId) { logger.warn('No desktop source selected for display share'); return false; } if (restartWaylandPortalForSwitch) { if (!didScreenShareStart()) { logger.warn('No active screen share to restart for Wayland portal source switch'); return false; } await MediaEngine.setScreenShareEnabled(false, { sendUpdate: false, playSound: false, preserveStreamAudioPreferences: true, }); } let nativeAudioArmed = false; const isOwnWindowShare = options?.isOwnWindow === true && sourceId?.startsWith('window:'); if (isOwnWindowShare && requestedAudio) { logger.warn('Fluxer-owned window audio is excluded from screen share capture; continuing video-only', { sourceId, platform: electronApi.platform, }); removeAudioFromCaptureOptions(captureOptions); } const requestedAppAudioOnLinux = requestedAudio && !isOwnWindowShare && electronApi.platform === 'linux' && sourceId?.startsWith('window:'); const requestedDesktopAudio = requestedAudio && (sourceId?.startsWith('screen:') || (useWaylandPortal && VoiceSettings.getShareDesktopAudio())); const requestedNativeDesktopAudio = requestedDesktopAudio && (electronApi.platform === 'darwin' || electronApi.platform === 'win32') && sourceId?.startsWith('screen:'); const requestedNativePickerAudioOnLinux = requestedAudio && electronApi.platform === 'linux' && useWaylandPortal; const linuxDesktopAudioSourceMode = electronApi.platform === 'linux' && (requestedDesktopAudio || requestedNativePickerAudioOnLinux) ? VoiceSettings.getScreenShareAudioSourceMode() : null; if (requestedAppAudioOnLinux) { try { nativeAudioArmed = await armNativeAudioForNextCapture(sourceId ?? ''); } catch (error) { logger.warn('Failed to arm Linux native per-window audio capture', { sourceId, error, }); } if (!nativeAudioArmed) { failRequestedAudioCapture( buildAudioCaptureFailureDebug({ sourceId, reason: getLastNativeAudioArmFailure()?.reason ?? 'linux-window-audio-route-unavailable', }), ); } } else if (requestedNativeDesktopAudio) { try { nativeAudioArmed = await armNativeSystemAudioForNextCapture(); } catch (error) { logger.warn('Failed to arm native desktop audio capture', { sourceId, platform: electronApi.platform, error, }); } if (!nativeAudioArmed) { const debugInfo = buildAudioCaptureFailureDebug({ sourceId, sourceMode: 'system', platform: electronApi.platform, reason: getLastNativeAudioArmFailure()?.reason ?? 'system-audio-route-unavailable', }); logger.warn('Desktop audio unavailable; aborting screen share because audio was requested', debugInfo); failRequestedAudioCapture(debugInfo); } } else if (linuxDesktopAudioSourceMode === 'none') { removeAudioFromCaptureOptions(captureOptions); } else if ((requestedDesktopAudio || requestedNativePickerAudioOnLinux) && electronApi.platform === 'linux') { const sourceMode = linuxDesktopAudioSourceMode ?? 'system'; const userIncludeSources = VoiceSettings.getScreenShareAudioIncludeSources().map((entry) => ({...entry})); const userExcludeSources = VoiceSettings.getScreenShareAudioExcludeSources().map((entry) => ({...entry})); try { nativeAudioArmed = await armNativeAudioForLinuxRouting( buildLinuxNativeAudioRule(sourceMode, userIncludeSources, userExcludeSources), ); } catch (error) { logger.warn('Failed to arm Linux native audio-capture link', { sourceMode, error, }); } if (!nativeAudioArmed) { failRequestedAudioCapture( buildAudioCaptureFailureDebug({ sourceMode, reason: getLastNativeAudioArmFailure()?.reason ?? 'linux-system-audio-route-unavailable', }), ); } } if ( requestedAudio && !isOwnWindowShare && sourceId?.startsWith('window:') && (electronApi.platform === 'darwin' || electronApi.platform === 'win32') ) { try { nativeAudioArmed = await armNativeAudioForNextCapture(sourceId); } catch (error) { logger.warn('Failed to arm native per-window audio capture', { sourceId, error, }); } if (!nativeAudioArmed) { const debugInfo = buildAudioCaptureFailureDebug({ sourceId, reason: getLastNativeAudioArmFailure()?.reason ?? 'native-window-audio-route-unavailable', }); logger.warn('Per-window audio unavailable; aborting screen share because audio was requested', { sourceId, platform: electronApi.platform, reason: debugInfo.reason, }); failRequestedAudioCapture(debugInfo); } } if ( requestedAudio && sourceId?.startsWith('window:') && !isOwnWindowShare && !nativeAudioArmed && electronApi.platform !== 'darwin' && electronApi.platform !== 'win32' && electronApi.platform !== 'linux' ) { failRequestedAudioCapture( buildAudioCaptureFailureDebug({ sourceId, platform: electronApi.platform, reason: getLastNativeAudioArmFailure()?.reason ?? 'window-audio-route-unavailable', }), ); } if (requestedAudio && (nativeAudioArmed || electronApi.platform === 'linux')) { removeAudioFromCaptureOptions(captureOptions); } try { if (useWaylandPortal && options?.preferredDisplaySurface) { await electronApi.setDisplayMediaPortalPreference?.(options.preferredDisplaySurface); } if (!useWaylandPortal && sourceId) { setDesktopSourceIntent({sourceId, includeAudio: false}); } let operationSucceeded = false; if (mode === 'switch' && !restartWaylandPortalForSwitch) { operationSucceeded = await MediaEngine.replaceActiveDisplayScreenShare(captureOptions, publishOptions, { sourceId: sourceId ?? null, displayShareEnvironment, requireAudio: requestedAudio && nativeAudioArmed, }); } else { await MediaEngine.setScreenShareEnabled( true, restartWaylandPortalForSwitch ? {...captureOptions, playSound: false} : captureOptions, publishOptions, ); operationSucceeded = didScreenShareStart(); } const captured = mode === 'switch' ? operationSucceeded : didScreenShareStart(); if (nativeAudioArmed && !captured) { cleanupNativeAudioAfterCaptureDidNotStart(mode); } if (captured && !useWaylandPortal && sourceId) { ActiveScreenShareSource.setPublishedSource(sourceId.startsWith('window:') ? 'app' : 'display', sourceId, { isOwnWindow: isOwnWindowShare, }); } if (captured && useWaylandPortal) { ActiveScreenShareSource.setPublishedSource('wayland', null); } if (!captured && mode === 'start' && !didScreenShareStart()) { ActiveScreenShareSource.clear(); } if (!captured && useWaylandPortal && mode !== 'switch') { logger.warn('Wayland screen share portal did not yield a capturable source', {sourceId}); throw new ScreenSharePortalUnavailableError('empty'); } if ( captured && requestedAudio && electronApi.platform === 'linux' && linuxDesktopAudioSourceMode !== null && linuxDesktopAudioSourceMode !== 'none' ) { const audioRelinked = await reconfigureActiveLinuxScreenShareAudioLink().catch((error) => { logger.warn('Failed to link Linux screen-share audio after capture start', {mode, error}); return false; }); if (!audioRelinked) { const debugInfo = buildAudioCaptureFailureDebug({ sourceMode: linuxDesktopAudioSourceMode, platform: electronApi.platform, reason: getLastNativeAudioArmFailure()?.reason ?? 'linux-system-audio-route-unavailable', }); logger.warn('Linux screen-share capture succeeded, but audio link did not complete', { ...debugInfo, mode, }); degradeAudioToVideoOnly(captureOptions, debugInfo); } } return captured; } catch (error) { if (isScreenSharePortalUnavailableError(error)) { throw error; } const capturedAfterError = didScreenShareStart(); if (nativeAudioArmed && !capturedAfterError) { cleanupNativeAudioAfterCaptureDidNotStart(mode); } if (!capturedAfterError && mode === 'start' && !didScreenShareStart()) { ActiveScreenShareSource.clear(); } if (useWaylandPortal && !capturedAfterError && mode !== 'switch') { logger.warn('Wayland screen share portal capture failed to start', { sourceId, error, }); throw new ScreenSharePortalUnavailableError('error', error instanceof Error ? error.message : undefined); } throw error; } finally { if (!useWaylandPortal) { clearDesktopSourceIntent(); } } } let operationSucceeded = false; if (mode === 'switch') { operationSucceeded = await MediaEngine.replaceActiveDisplayScreenShare(captureOptions, publishOptions, { sourceId: null, displayShareEnvironment, requireAudio: false, }); } else { await MediaEngine.setScreenShareEnabled(true, captureOptions, publishOptions); operationSucceeded = didScreenShareStart(); } const captured = mode === 'switch' ? operationSucceeded : didScreenShareStart(); if (captured) { ActiveScreenShareSource.setPublishedSource('web', null); } return captured; } interface ConfiguredScreenShareMutationRequest { execute: () => Promise; promise: Promise; resolve: (result: boolean) => void; reject: (error: unknown) => void; } let configuredScreenShareMutationActive = false; let pendingConfiguredScreenShareMutation: ConfiguredScreenShareMutationRequest | null = null; function createConfiguredScreenShareMutationRequest( execute: () => Promise, ): ConfiguredScreenShareMutationRequest { let resolveRequest: ((result: boolean) => void) | undefined; let rejectRequest: ((error: unknown) => void) | undefined; const promise = new Promise((resolve, reject) => { resolveRequest = resolve; rejectRequest = reject; }); if (!resolveRequest || !rejectRequest) { throw new Error('Configured screen share mutation deferred was not initialized'); } return {execute, promise, resolve: resolveRequest, reject: rejectRequest}; } async function drainConfiguredScreenShareMutations( initialRequest: ConfiguredScreenShareMutationRequest, ): Promise { let request: ConfiguredScreenShareMutationRequest | null = initialRequest; while (request) { try { request.resolve(await request.execute()); } catch (error) { request.reject(error); } request = pendingConfiguredScreenShareMutation; pendingConfiguredScreenShareMutation = null; } configuredScreenShareMutationActive = false; } function scheduleConfiguredScreenShareMutation(execute: () => Promise): Promise { const request = createConfiguredScreenShareMutationRequest(execute); if (!configuredScreenShareMutationActive) { configuredScreenShareMutationActive = true; void drainConfiguredScreenShareMutations(request); return request.promise; } pendingConfiguredScreenShareMutation?.resolve(false); pendingConfiguredScreenShareMutation = request; return request.promise; } export async function startConfiguredDisplayScreenShare( sourceId?: string | null, options?: ConfiguredDisplayScreenShareOptions, ): Promise { return scheduleConfiguredScreenShareMutation(async () => { let didStart = false; await executeScreenShareOperation(async () => { didStart = await runConfiguredDisplayScreenShare(sourceId, options, 'start'); }); return didStart; }); } export async function switchConfiguredDisplayScreenShare( sourceId?: string | null, options?: ConfiguredDisplayScreenShareOptions, ): Promise { return scheduleConfiguredScreenShareMutation(async () => { let didSwitch = false; await executeScreenShareOperation(async () => { didSwitch = await runConfiguredDisplayScreenShare(sourceId, options, 'switch'); }); return didSwitch; }); } export async function startConfiguredDeviceScreenShare(videoDeviceId: string): Promise { return scheduleConfiguredScreenShareMutation(async () => { normaliseDeviceScreenShareSettings(); const {captureOptions, publishOptions, includeAudio, audioDeviceId} = getConfiguredScreenShareOptions( 'device', 'desktop-custom', ); try { await MediaEngine.startDeviceScreenShare( { videoDeviceId, audioDeviceId: includeAudio ? audioDeviceId : undefined, resolution: captureOptions.resolution, }, publishOptions, ); } catch (error) { logger.error('Failed to start device screen share', { error, videoDeviceId, }); } const didStart = didScreenShareStart(); if (didStart) ActiveScreenShareSource.setPublishedSource('device', null); return didStart; }); } export async function switchConfiguredDeviceScreenShare(videoDeviceId: string): Promise { return scheduleConfiguredScreenShareMutation(async () => { normaliseDeviceScreenShareSettings(); const {captureOptions, publishOptions, includeAudio, audioDeviceId} = getConfiguredScreenShareOptions( 'device', 'desktop-custom', ); try { const didSwitch = await MediaEngine.replaceActiveDeviceScreenShare( { videoDeviceId, audioDeviceId: includeAudio ? audioDeviceId : undefined, resolution: captureOptions.resolution, }, publishOptions, ); if (didSwitch) ActiveScreenShareSource.setPublishedSource('device', null); return didSwitch; } catch (error) { logger.error('Failed to switch device screen share source', { error, videoDeviceId, }); return false; } }); }