Fix screen sharing
* Make the embedded mode screen sharing a request-each-way rather than request-and-reply, since replies time out and so can't wait for the user. * Try normal screen sharing first, then fall back to using the widget API if it fails (for lack of a good way of detecting when we should be using the widget API). Fixes https://github.com/vector-im/element-call/issues/649
This commit is contained in:
parent
39c30ebf56
commit
71dcc94166
2 changed files with 93 additions and 26 deletions
|
@ -27,10 +27,11 @@ import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||||
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
|
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { IWidgetApiRequest } from "matrix-widget-api";
|
||||||
|
|
||||||
import { usePageUnload } from "./usePageUnload";
|
import { usePageUnload } from "./usePageUnload";
|
||||||
import { TranslatedError, translatedError } from "../TranslatedError";
|
import { TranslatedError, translatedError } from "../TranslatedError";
|
||||||
import { ElementWidgetActions, widget } from "../widget";
|
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
|
||||||
|
|
||||||
export interface UseGroupCallReturnType {
|
export interface UseGroupCallReturnType {
|
||||||
state: GroupCallState;
|
state: GroupCallState;
|
||||||
|
@ -302,36 +303,82 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
||||||
groupCall.setMicrophoneMuted(!groupCall.isMicrophoneMuted());
|
groupCall.setMicrophoneMuted(!groupCall.isMicrophoneMuted());
|
||||||
}, [groupCall]);
|
}, [groupCall]);
|
||||||
|
|
||||||
const toggleScreensharing = useCallback(() => {
|
const toggleScreensharing = useCallback(async () => {
|
||||||
updateState({ requestingScreenshare: true });
|
if (!groupCall.isScreensharing()) {
|
||||||
|
// toggling on
|
||||||
|
updateState({ requestingScreenshare: true });
|
||||||
|
|
||||||
if (groupCall.isScreensharing()) {
|
try {
|
||||||
groupCall.setScreensharingEnabled(false).then(() => {
|
await groupCall.setScreensharingEnabled(true, {
|
||||||
|
audio: true,
|
||||||
|
throwOnFail: true,
|
||||||
|
});
|
||||||
updateState({ requestingScreenshare: false });
|
updateState({ requestingScreenshare: false });
|
||||||
});
|
} catch (e) {
|
||||||
} else {
|
// this will fail in Electron because getDisplayMedia just throws a permission
|
||||||
widget.api.transport
|
// error, so if we have a widget API, try requesting via that.
|
||||||
.send(ElementWidgetActions.Screenshare, {})
|
if (widget) {
|
||||||
.then(
|
const reply = await widget.api.transport.send(
|
||||||
(reply: { desktopCapturerSourceId: string; failed?: boolean }) => {
|
ElementWidgetActions.ScreenshareRequest,
|
||||||
if (reply.failed) {
|
{}
|
||||||
updateState({ requestingScreenshare: false });
|
);
|
||||||
return;
|
if (!reply.pending) {
|
||||||
}
|
updateState({ requestingScreenshare: false });
|
||||||
|
|
||||||
groupCall
|
|
||||||
.setScreensharingEnabled(true, {
|
|
||||||
audio: !reply.desktopCapturerSourceId,
|
|
||||||
desktopCapturerSourceId: reply.desktopCapturerSourceId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
updateState({ requestingScreenshare: false });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// toggling off
|
||||||
|
groupCall.setScreensharingEnabled(false);
|
||||||
}
|
}
|
||||||
}, [groupCall]);
|
}, [groupCall]);
|
||||||
|
|
||||||
|
const onScreenshareStart = useCallback(
|
||||||
|
async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||||
|
updateState({ requestingScreenshare: false });
|
||||||
|
await groupCall.setScreensharingEnabled(true, {
|
||||||
|
desktopCapturerSourceId: ev.detail.data
|
||||||
|
.desktopCapturerSourceId as string,
|
||||||
|
audio: !ev.detail.data.desktopCapturerSourceId,
|
||||||
|
});
|
||||||
|
await widget.api.transport.reply(ev.detail, {});
|
||||||
|
},
|
||||||
|
[groupCall]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onScreenshareStop = useCallback(
|
||||||
|
async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||||
|
updateState({ requestingScreenshare: false });
|
||||||
|
await groupCall.setScreensharingEnabled(false);
|
||||||
|
await widget.api.transport.reply(ev.detail, {});
|
||||||
|
},
|
||||||
|
[groupCall]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (widget) {
|
||||||
|
widget.lazyActions.on(
|
||||||
|
ElementWidgetActions.ScreenshareStart,
|
||||||
|
onScreenshareStart
|
||||||
|
);
|
||||||
|
widget.lazyActions.on(
|
||||||
|
ElementWidgetActions.ScreenshareStop,
|
||||||
|
onScreenshareStop
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
widget.lazyActions.off(
|
||||||
|
ElementWidgetActions.ScreenshareStart,
|
||||||
|
onScreenshareStart
|
||||||
|
);
|
||||||
|
widget.lazyActions.off(
|
||||||
|
ElementWidgetActions.ScreenshareStop,
|
||||||
|
onScreenshareStop
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [onScreenshareStart, onScreenshareStop]);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -30,7 +30,21 @@ export enum ElementWidgetActions {
|
||||||
HangupCall = "im.vector.hangup",
|
HangupCall = "im.vector.hangup",
|
||||||
TileLayout = "io.element.tile_layout",
|
TileLayout = "io.element.tile_layout",
|
||||||
SpotlightLayout = "io.element.spotlight_layout",
|
SpotlightLayout = "io.element.spotlight_layout",
|
||||||
Screenshare = "io.element.screenshare",
|
|
||||||
|
// Element Call -> host requesting to start a screenshare
|
||||||
|
// (ie. expects a ScreenshareStart once the user has picked a source)
|
||||||
|
// Element Call -> host requesting to start a screenshare
|
||||||
|
// (ie. expects a ScreenshareStart once the user has picked a source)
|
||||||
|
// replies with { pending } where pending is true if the host has asked
|
||||||
|
// the user to choose a window and false if not (ie. if the host isn't
|
||||||
|
// running within Electron)
|
||||||
|
ScreenshareRequest = "io.element.screenshare_request",
|
||||||
|
// host -> Element Call telling EC to start screen sharing with
|
||||||
|
// the given source
|
||||||
|
ScreenshareStart = "io.element.screenshare_start",
|
||||||
|
// host -> Element Call telling EC to stop screen sharing, or that
|
||||||
|
// the user cancelled when selecting a source after a ScreenshareRequest
|
||||||
|
ScreenshareStop = "io.element.screenshare_stop",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JoinCallData {
|
export interface JoinCallData {
|
||||||
|
@ -38,6 +52,10 @@ export interface JoinCallData {
|
||||||
videoInput: string | null;
|
videoInput: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScreenshareStartData {
|
||||||
|
desktopCapturerSourceId: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface WidgetHelpers {
|
interface WidgetHelpers {
|
||||||
api: WidgetApi;
|
api: WidgetApi;
|
||||||
lazyActions: LazyEventEmitter;
|
lazyActions: LazyEventEmitter;
|
||||||
|
@ -69,6 +87,8 @@ export const widget: WidgetHelpers | null = (() => {
|
||||||
ElementWidgetActions.HangupCall,
|
ElementWidgetActions.HangupCall,
|
||||||
ElementWidgetActions.TileLayout,
|
ElementWidgetActions.TileLayout,
|
||||||
ElementWidgetActions.SpotlightLayout,
|
ElementWidgetActions.SpotlightLayout,
|
||||||
|
ElementWidgetActions.ScreenshareStart,
|
||||||
|
ElementWidgetActions.ScreenshareStop,
|
||||||
].forEach((action) => {
|
].forEach((action) => {
|
||||||
api.on(`action:${action}`, (ev: CustomEvent<IWidgetApiRequest>) => {
|
api.on(`action:${action}`, (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
Loading…
Add table
Reference in a new issue