refactor(voice): make screen-share source swaps atomic (#2385)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-02 17:33:18 +02:00
1 parent 62577b25bb
commit 8d34c6bcaa
43 files changed
+1168 -240

No files matched your search

@@ -268,6 +268,7 @@ function getConfiguredScreenShareOptions(
sourceId?: string | null,
preferredDisplaySurface?: 'window' | 'monitor',
videoCodec?: VideoCodec,
includeAudioOverride?: boolean,
) {
const currentResolution = VoiceSettings.getScreenshareResolution();
const currentStreamingMode = VoiceSettings.getStreamingMode();
@@ -288,12 +289,9 @@ function getConfiguredScreenShareOptions(
VoiceSettings.getVideoFrameRate(),
higherQuality,
);
const includeAudio = shouldIncludeAudioForShare(
shareContext,
displayShareEnvironment,
sourceId,
preferredDisplaySurface,
);
const includeAudio =
includeAudioOverride ??
shouldIncludeAudioForShare(shareContext, displayShareEnvironment, sourceId, preferredDisplaySurface);
if (!includeAudio && shareContext !== 'device' && supportsDesktopScreenShareAudioCapture()) {
logger.info('Screen share audio not requested for this surface', {
sourceId,
@@ -328,6 +326,7 @@ export interface ConfiguredDisplayScreenShareOptions {
};
preferredDisplaySurface?: 'window' | 'monitor';
isOwnWindow?: boolean;
includeAudio?: boolean;
}
async function runConfiguredDisplayScreenShare(
@@ -348,6 +347,8 @@ async function runConfiguredDisplayScreenShare(
options?.sourceDimensions,
sourceId,
options?.preferredDisplaySurface,
undefined,
options?.includeAudio,
);
if (electronApi) {
const restartWaylandPortalForSwitch = useWaylandPortal && mode === 'switch';
@@ -373,14 +374,7 @@ async function runConfiguredDisplayScreenShare(
sourceId,
platform: electronApi.platform,
});
degradeAudioToVideoOnly(
captureOptions,
buildAudioCaptureFailureDebug({
sourceId,
platform: electronApi.platform,
reason: 'self-window-audio-route-unavailable',
}),
);
removeAudioFromCaptureOptions(captureOptions);
}
const requestedAppAudioOnLinux =
requestedAudio && !isOwnWindowShare && electronApi.platform === 'linux' && sourceId?.startsWith('window:');
@@ -513,11 +507,14 @@ async function runConfiguredDisplayScreenShare(
}
if (!useWaylandPortal && sourceId) {
setDesktopSourceIntent({sourceId, includeAudio: false});
ActiveScreenShareSource.setSourceId(sourceId, {isOwnWindow: isOwnWindowShare});
}
let operationSucceeded = false;
if (mode === 'switch' && !restartWaylandPortalForSwitch) {
operationSucceeded = await MediaEngine.replaceActiveDisplayScreenShare(captureOptions, publishOptions);
operationSucceeded = await MediaEngine.replaceActiveDisplayScreenShare(captureOptions, publishOptions, {
sourceId: sourceId ?? null,
displayShareEnvironment,
requireAudio: requestedAudio && nativeAudioArmed,
});
} else {
await MediaEngine.setScreenShareEnabled(
true,
@@ -530,7 +527,15 @@ async function runConfiguredDisplayScreenShare(
if (nativeAudioArmed && !captured) {
cleanupNativeAudioAfterCaptureDidNotStart(mode);
}
if (!captured) {
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') {
@@ -570,7 +575,7 @@ async function runConfiguredDisplayScreenShare(
if (nativeAudioArmed && !capturedAfterError) {
cleanupNativeAudioAfterCaptureDidNotStart(mode);
}
if (!capturedAfterError) {
if (!capturedAfterError && mode === 'start' && !didScreenShareStart()) {
ActiveScreenShareSource.clear();
}
if (useWaylandPortal && !capturedAfterError && mode !== 'switch') {
@@ -589,80 +594,153 @@ async function runConfiguredDisplayScreenShare(
}
let operationSucceeded = false;
if (mode === 'switch') {
operationSucceeded = await MediaEngine.replaceActiveDisplayScreenShare(captureOptions, publishOptions);
operationSucceeded = await MediaEngine.replaceActiveDisplayScreenShare(captureOptions, publishOptions, {
sourceId: null,
displayShareEnvironment,
requireAudio: false,
});
} else {
await MediaEngine.setScreenShareEnabled(true, captureOptions, publishOptions);
operationSucceeded = didScreenShareStart();
}
return mode === 'switch' ? operationSucceeded : didScreenShareStart();
const captured = mode === 'switch' ? operationSucceeded : didScreenShareStart();
if (captured) {
ActiveScreenShareSource.setPublishedSource('web', null);
}
return captured;
}
interface ConfiguredScreenShareMutationRequest {
execute: () => Promise<boolean>;
promise: Promise<boolean>;
resolve: (result: boolean) => void;
reject: (error: unknown) => void;
}
let configuredScreenShareMutationActive = false;
let pendingConfiguredScreenShareMutation: ConfiguredScreenShareMutationRequest | null = null;
function createConfiguredScreenShareMutationRequest(
execute: () => Promise<boolean>,
): ConfiguredScreenShareMutationRequest {
let resolveRequest: ((result: boolean) => void) | undefined;
let rejectRequest: ((error: unknown) => void) | undefined;
const promise = new Promise<boolean>((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<void> {
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<boolean>): Promise<boolean> {
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<boolean> {
let didStart = false;
await executeScreenShareOperation(async () => {
didStart = await runConfiguredDisplayScreenShare(sourceId, options, 'start');
return scheduleConfiguredScreenShareMutation(async () => {
let didStart = false;
await executeScreenShareOperation(async () => {
didStart = await runConfiguredDisplayScreenShare(sourceId, options, 'start');
});
return didStart;
});
return didStart;
}
export async function switchConfiguredDisplayScreenShare(
sourceId?: string | null,
options?: ConfiguredDisplayScreenShareOptions,
): Promise<boolean> {
let didSwitch = false;
await executeScreenShareOperation(async () => {
didSwitch = await runConfiguredDisplayScreenShare(sourceId, options, 'switch');
return scheduleConfiguredScreenShareMutation(async () => {
let didSwitch = false;
await executeScreenShareOperation(async () => {
didSwitch = await runConfiguredDisplayScreenShare(sourceId, options, 'switch');
});
return didSwitch;
});
return didSwitch;
}
export async function startConfiguredDeviceScreenShare(videoDeviceId: string): Promise<boolean> {
normaliseDeviceScreenShareSettings();
const {captureOptions, publishOptions, includeAudio, audioDeviceId} = getConfiguredScreenShareOptions(
'device',
'desktop-custom',
);
try {
await MediaEngine.startDeviceScreenShare(
{
videoDeviceId,
audioDeviceId: includeAudio ? audioDeviceId : undefined,
resolution: captureOptions.resolution,
},
publishOptions,
return scheduleConfiguredScreenShareMutation(async () => {
normaliseDeviceScreenShareSettings();
const {captureOptions, publishOptions, includeAudio, audioDeviceId} = getConfiguredScreenShareOptions(
'device',
'desktop-custom',
);
} catch (error) {
logger.error('Failed to start device screen share', {
error,
videoDeviceId,
});
}
return didScreenShareStart();
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<boolean> {
normaliseDeviceScreenShareSettings();
const {captureOptions, publishOptions, includeAudio, audioDeviceId} = getConfiguredScreenShareOptions(
'device',
'desktop-custom',
);
try {
return await MediaEngine.replaceActiveDeviceScreenShare(
{
videoDeviceId,
audioDeviceId: includeAudio ? audioDeviceId : undefined,
resolution: captureOptions.resolution,
},
publishOptions,
return scheduleConfiguredScreenShareMutation(async () => {
normaliseDeviceScreenShareSettings();
const {captureOptions, publishOptions, includeAudio, audioDeviceId} = getConfiguredScreenShareOptions(
'device',
'desktop-custom',
);
} catch (error) {
logger.error('Failed to switch device screen share source', {
error,
videoDeviceId,
});
return false;
}
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;
}
});
}