Enable screen sharing
For now, only the video is transmitted.
The camera is turned off when screensharing, just like on the desktop
app.
In CallProvider, replace `isVideoOn` state with `videoStatus` to toggle
between off, on or screenshare.
Add icon for when screenshare is active.
Remove screenshare button menu options, as they are unavailable on the
web.
Change-Id: I95dd1c18efd91ab8b9063f0e7f817839e4f24aba
diff --git a/client/src/contexts/CallProvider.tsx b/client/src/contexts/CallProvider.tsx
index 39aad07..5464228 100644
--- a/client/src/contexts/CallProvider.tsx
+++ b/client/src/contexts/CallProvider.tsx
@@ -24,7 +24,7 @@
import CallPermissionDenied from '../pages/CallPermissionDenied';
import { CallRouteParams } from '../router';
import { callTimeoutMs } from '../utils/constants';
-import { SetState, WithChildren } from '../utils/utils';
+import { AsyncSetState, SetState, WithChildren } from '../utils/utils';
import { useConversationContext } from './ConversationProvider';
import { MediaDevicesInfo, MediaInputKind, WebRtcContext } from './WebRtcProvider';
import { IWebSocketContext, WebSocketContext } from './WebSocketProvider';
@@ -40,6 +40,12 @@
PermissionsDenied,
}
+export enum VideoStatus {
+ Off,
+ Camera,
+ ScreenShare,
+}
+
type MediaDeviceIdState = {
id: string | undefined;
setId: (id: string | undefined) => void | Promise<void>;
@@ -52,8 +58,8 @@
isAudioOn: boolean;
setIsAudioOn: SetState<boolean>;
- isVideoOn: boolean;
- setIsVideoOn: SetState<boolean>;
+ videoStatus: VideoStatus;
+ updateVideoStatus: AsyncSetState<VideoStatus>;
isChatShown: boolean;
setIsChatShown: SetState<boolean>;
isFullscreen: boolean;
@@ -89,8 +95,8 @@
isAudioOn: false,
setIsAudioOn: () => {},
- isVideoOn: false,
- setIsVideoOn: () => {},
+ videoStatus: VideoStatus.Off,
+ updateVideoStatus: () => Promise.reject(),
isChatShown: false,
setIsChatShown: () => {},
isFullscreen: false,
@@ -122,8 +128,15 @@
webSocket: IWebSocketContext;
}) => {
const { state: routeState } = useUrlParams<CallRouteParams>();
- const { localStream, sendWebRtcOffer, iceConnectionState, closeConnection, getMediaDevices, updateLocalStream } =
- useContext(WebRtcContext);
+ const {
+ localStream,
+ updateScreenShare,
+ sendWebRtcOffer,
+ iceConnectionState,
+ closeConnection,
+ getMediaDevices,
+ updateLocalStream,
+ } = useContext(WebRtcContext);
const { conversationId, conversation } = useConversationContext();
const navigate = useNavigate();
@@ -133,7 +146,7 @@
const [videoDeviceId, setVideoDeviceId] = useState<string>();
const [isAudioOn, setIsAudioOn] = useState(false);
- const [isVideoOn, setIsVideoOn] = useState(false);
+ const [videoStatus, setVideoStatus] = useState(VideoStatus.Off);
const [isChatShown, setIsChatShown] = useState(false);
const [isFullscreen, setIsFullscreen] = useState(false);
const [callStatus, setCallStatus] = useState(routeState?.callStatus);
@@ -187,14 +200,35 @@
useEffect(() => {
if (localStream) {
for (const track of localStream.getVideoTracks()) {
- track.enabled = isVideoOn;
+ track.enabled = videoStatus === VideoStatus.Camera;
const deviceId = track.getSettings().deviceId;
if (deviceId) {
setVideoDeviceId(deviceId);
}
}
}
- }, [isVideoOn, localStream]);
+ }, [videoStatus, localStream]);
+
+ const updateVideoStatus = useCallback(
+ async (newStatus: ((prevState: VideoStatus) => VideoStatus) | VideoStatus) => {
+ if (typeof newStatus === 'function') {
+ newStatus = newStatus(videoStatus);
+ }
+
+ const stream = await updateScreenShare(newStatus === VideoStatus.ScreenShare);
+ if (stream) {
+ for (const track of stream.getTracks()) {
+ track.addEventListener('ended', () => {
+ console.warn('Browser ended screen sharing');
+ updateVideoStatus(VideoStatus.Off);
+ });
+ }
+ }
+
+ setVideoStatus(newStatus);
+ },
+ [videoStatus, updateScreenShare]
+ );
useEffect(() => {
const onFullscreenChange = () => {
@@ -220,7 +254,7 @@
};
setCallStatus(CallStatus.Ringing);
- setIsVideoOn(withVideoOn);
+ setVideoStatus(withVideoOn ? VideoStatus.Camera : VideoStatus.Off);
console.info('Sending CallBegin', callBegin);
webSocket.send(WebSocketMessageType.CallBegin, callBegin);
})
@@ -241,7 +275,7 @@
conversationId,
};
- setIsVideoOn(withVideoOn);
+ setVideoStatus(withVideoOn ? VideoStatus.Camera : VideoStatus.Off);
setCallStatus(CallStatus.Connecting);
console.info('Sending CallAccept', callAccept);
webSocket.send(WebSocketMessageType.CallAccept, callAccept);
@@ -324,7 +358,7 @@
console.info('ICE connection disconnected or failed, ending call');
endCall();
}
- }, [iceConnectionState, callStatus, isVideoOn, endCall]);
+ }, [iceConnectionState, callStatus, videoStatus, endCall]);
useEffect(() => {
const checkStatusTimeout = () => {
@@ -386,8 +420,8 @@
currentMediaDeviceIds,
isAudioOn,
setIsAudioOn,
- isVideoOn,
- setIsVideoOn,
+ videoStatus,
+ updateVideoStatus,
isChatShown,
setIsChatShown,
isFullscreen,