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/WebRtcProvider.tsx b/client/src/contexts/WebRtcProvider.tsx
index 59999b9..1ce3ff6 100644
--- a/client/src/contexts/WebRtcProvider.tsx
+++ b/client/src/contexts/WebRtcProvider.tsx
@@ -33,9 +33,11 @@
iceConnectionState: RTCIceConnectionState | undefined;
localStream: MediaStream | undefined;
+ screenShareLocalStream: MediaStream | undefined;
remoteStreams: readonly MediaStream[] | undefined;
getMediaDevices: () => Promise<MediaDevicesInfo>;
updateLocalStream: (mediaDeviceIds?: MediaInputIds) => Promise<void>;
+ updateScreenShare: (active: boolean) => Promise<MediaStream | undefined>;
sendWebRtcOffer: () => Promise<void>;
closeConnection: () => void;
@@ -44,9 +46,11 @@
const defaultWebRtcContext: IWebRtcContext = {
iceConnectionState: undefined,
localStream: undefined,
+ screenShareLocalStream: undefined,
remoteStreams: undefined,
getMediaDevices: async () => Promise.reject(),
updateLocalStream: async () => Promise.reject(),
+ updateScreenShare: async () => Promise.reject(),
sendWebRtcOffer: async () => Promise.reject(),
closeConnection: () => {},
};
@@ -101,6 +105,7 @@
}) => {
const { conversation, conversationId } = useConversationContext();
const [localStream, setLocalStream] = useState<MediaStream>();
+ const [screenShareLocalStream, setScreenShareLocalStream] = useState<MediaStream>();
const [remoteStreams, setRemoteStreams] = useState<readonly MediaStream[]>();
const [iceConnectionState, setIceConnectionState] = useState<RTCIceConnectionState | undefined>();
@@ -183,14 +188,37 @@
[getMediaDevices]
);
+ const updateScreenShare = useCallback(
+ async (isOn: boolean) => {
+ if (isOn) {
+ const stream = await navigator.mediaDevices.getDisplayMedia({
+ video: true,
+ audio: false,
+ });
+
+ setScreenShareLocalStream(stream);
+ return stream;
+ } else {
+ if (screenShareLocalStream) {
+ for (const track of screenShareLocalStream.getTracks()) {
+ track.stop();
+ }
+ }
+
+ setScreenShareLocalStream(undefined);
+ }
+ },
+ [screenShareLocalStream]
+ );
+
useEffect(() => {
- if (!localStream || !webRtcConnection) {
+ if ((!localStream && !screenShareLocalStream) || !webRtcConnection) {
return;
}
- const updateTracks = async (kind: 'audio' | 'video') => {
+ const updateTracks = async (stream: MediaStream, kind: 'audio' | 'video') => {
const senders = kind === 'audio' ? audioRtcRtpSenders : videoRtcRtpSenders;
- const tracks = kind === 'audio' ? localStream.getAudioTracks() : localStream.getVideoTracks();
+ const tracks = kind === 'audio' ? stream.getAudioTracks() : stream.getVideoTracks();
if (senders) {
const promises: Promise<void>[] = [];
for (let i = 0; i < senders.length; i++) {
@@ -210,7 +238,7 @@
// TODO: Currently, we do not support adding new devices. To enable this feature, we would need to implement
// the "Perfect negotiation" pattern to renegotiate after `addTrack`.
// https://blog.mozilla.org/webrtc/perfect-negotiation-in-webrtc/
- const newSenders = tracks.map((track) => webRtcConnection.addTrack(track, localStream));
+ const newSenders = tracks.map((track) => webRtcConnection.addTrack(track, stream));
if (kind === 'audio') {
setAudioRtcRtpSenders(newSenders);
} else {
@@ -218,9 +246,15 @@
}
};
- updateTracks('audio');
- updateTracks('video');
- }, [localStream, webRtcConnection, audioRtcRtpSenders, videoRtcRtpSenders]);
+ if (localStream) {
+ updateTracks(localStream, 'audio');
+ updateTracks(localStream, 'video');
+ }
+
+ if (screenShareLocalStream) {
+ updateTracks(screenShareLocalStream, 'video');
+ }
+ }, [localStream, screenShareLocalStream, webRtcConnection, audioRtcRtpSenders, videoRtcRtpSenders]);
const sendWebRtcOffer = useCallback(async () => {
const sdp = await webRtcConnection.createOffer({
@@ -375,24 +409,35 @@
}, [webRtcConnection]);
const closeConnection = useCallback(() => {
- const localTracks = localStream?.getTracks();
- if (localTracks) {
- for (const track of localTracks) {
- track.stop();
+ const stopStream = (stream: MediaStream) => {
+ const localTracks = stream.getTracks();
+ if (localTracks) {
+ for (const track of localTracks) {
+ track.stop();
+ }
}
+ };
+
+ if (localStream) {
+ stopStream(localStream);
+ }
+ if (screenShareLocalStream) {
+ stopStream(screenShareLocalStream);
}
webRtcConnection.close();
- }, [webRtcConnection, localStream]);
+ }, [webRtcConnection, localStream, screenShareLocalStream]);
return (
<WebRtcContext.Provider
value={{
iceConnectionState,
localStream,
+ screenShareLocalStream,
remoteStreams,
getMediaDevices,
updateLocalStream,
+ updateScreenShare,
sendWebRtcOffer,
closeConnection,
}}