Send presentation
In the previous lesson we learnt how to receive a presentation from other participants and display it in our interface. Now we will do the reverse. We will add a new button to the interface that, when clicked, will ask the user what they want to share (tab, window or whole screen) and after that, remote users will see that content.
Screen sharing from a mobile browser to other devices is not permitted by mobile browsers. Mobile browsers don't support this feature due to security restrictions. If you want to use this feature in a mobile, you will need to create a native application using our Mobile SDKs.
However, a mobile web application is still able to receive screen shares from other devices. The limitation only prevents mobile browsers from sending screen shares.
You can download the starter code from Step.07-Exercise-Send-presentation.
App component
We will start by making some modifications in the src/App.tsx
file.
First of all, we will add a new state variable to indicate if the user is sharing their screen or not:
const [screenShared, setScreenShared] = useState(false);
We will also add a new reference to the presentation stream. It will be used by a callback function to get the current stream that is being shared:
const presentationStreamRef = useRef<MediaStream>();
Then we will define the function that will be called once the user click on the share screen button to activate or deactivate this feature:
const handleScreenShare = async (): Promise<void> => {
if (screenShared) {
presentationStream?.getTracks().forEach(track => {
track.stop();
});
setPresentationStream(undefined);
infinityClient.stopPresenting();
} else {
const stream = await navigator.mediaDevices.getDisplayMedia();
stream.getVideoTracks()[0].onended = () => {
setScreenShared(false);
setPresentationStream(undefined);
infinityClient.stopPresenting();
};
infinityClient.present(stream);
setPresentationStream(stream);
}
setScreenShared(!screenShared);
};
In this function the first thing that we will do is to check the value of
screenShared
, and depending of its value we will activate the feature or not.
However, independently of the value of screenShared
, we will change the state
this value to the opposite calling setScreenShared(!screenShared)
:
screenShared === false
: In this case, the first thing that we will do is to stop all the tracks in the same way that we do with video mute. Then we set the state variable toundefined
to remove the video from the interface, and finally, we callinfinityClient.stopPresenting()
to stop sending the stream to Pexip Infinity.screenShared === true
: If this condition is met, means that we will enable this feature. The first step is to get aMediaStream
with the presentation. We will use the browser APIgetDisplayMedia()
that will show a native browser message asking the user to choose what they want to share:Once we have this stream, we attach a callback function to
onended
. This function will be called when the user clicks on the native browser message "Stop sharing":The callback function will change the value of the state variable
screenShared
tofalse
and do the same as in the first case. We stop all the tracks, set the state variablepresentationStream
toundefined
, and callinfinityClient.stopPresenting()
.Then we call
infinityClient.present(stream)
and, at this point, the stream will be sent to Pexip Infinity and to other participants. The last call tosetPresentationStream(stream)
will make our stream visible in our interface.
Other task that we will do is to modify the handleDisconnect
function and stop
the presentation stream in case the user clicks on the hang up button:
const handleDisconnect = () => {
presentationStream?.getTracks().forEach((track) => {
track.stop()
})
setPresentationStream(undefined)
...
};
Now we will use the useEffect
hook to update the value of the
presentationStreamRef
reference each time the presentationStream
changes:
useEffect(() => {
presentationStreamRef.current = presentationStream;
}, [presentationStream]);
Next, we will need to make a small modification to the
onRemotePresentationStream
callback function. We will check if the user is
sharing their screen and, in that case, we will stop the presentation stream:
callSignals.onRemotePresentationStream.add(stream => {
presentationStreamRef.current?.getTracks().forEach(track => {
track.stop();
});
setScreenShared(false);
setPresentationStream(stream);
});
We will subscribe to a new signal, onPresentationConnectionChange
, and in case
we don't have any participant sharing their screen, we will set the value of the
state variable presentationStream
to undefined
:
callSignals.onPresentationConnectionChange.add(event => {
if (event.recv === 'disconnected' && event.send !== 'connected') {
setPresentationStream(undefined);
}
});
Now it's the turn to pass screenShared
and handleScreenShare
to the
Conference component:
<Conference
localVideoStream={processedStream}
remoteStream={remoteStream}
presentationStream={presentationStream}
screenShared={screenShared}
devices={devices}
settings={{
audioInput,
audioOutput,
videoInput,
effect
}}
onAudioMute={handleAudioMute}
onVideoMute={handleVideoMute}
onScreenShare={handleScreenShare}
onSettingsChange={handleSettingsChange}
onDisconnect={handleDisconnect}
/>
Conference component
We have to include some modifications in the conference component. So the first
step is to open src/components/Conference/Conference.tsx
.
We will start by including screenShared
and onScreenShare
in the list of
props:
interface ConferenceProps {
localVideoStream: MediaStream | undefined;
remoteStream: MediaStream | undefined;
presentationStream: MediaStream | undefined;
screenShared: boolean;
devices: MediaDeviceInfoLike[];
settings: Settings;
onAudioMute: (mute: boolean) => Promise<void>;
onVideoMute: (mute: boolean) => Promise<void>;
onScreenShare: (share: boolean, onEnded: () => void) => void;
onSettingsChange: (settings: Settings) => Promise<void>;
onDisconnect: () => Promise<void>;
}
Now we add the same screenShared
and onScreenShare
when we define the
Toolbar
component:
<Toolbar
className="toolbar"
settingsOpened={settingsOpened}
screenShared={props.screenShared}
onAudioMute={props.onAudioMute}
onVideoMute={props.onVideoMute}
onScreenShare={async () => {
await props.onScreenShare()
setPresentationInMain(false)
}}
onOpenSettings={() => {
setSettingsOpened(true)
}}
onDisconnect={props.onDisconnect}
/>
In this case, instead of calling directly to onScreenShare
, we set the value
of presentationInMain
to false
. This way, we will show the video with the
remote participants in the main region and the PiP with the presentation on the
top-left corner. After that, we call props.onScreenShare()
.
Toolbar component
Next we will modify the file src/components/Conference/Toolbar/Toolbar.tsx
and
create the button that the user will use to start or stop sharing their screen.
The first step is to add screenShared
and onScreenShare
to the list of
props:
interface ToolbarProps {
className: string;
settingsOpened: boolean;
screenShared: boolean;
onAudioMute: (mute: boolean) => Promise<void>;
onVideoMute: (mute: boolean) => Promise<void>;
onScreenShare: () => Promise<void>;
onOpenSettings: () => void;
onDisconnect: () => Promise<void>;
}
After that, we will define the function that will be triggered each time the user clicks on the screen share button. This function will call the function defined in props and change the button state:
const handleScreenShare = async (): Promise<void> => {
await props.onScreenShare();
};
The final step is to return the button itself with the rest of the components from the toolbar:
<Tooltip text={`${props.screenShared ? 'Stop' : 'Start'} screen sharing`}>
<Button
onClick={() => {
handleScreenShare().catch(console.error)
}}
variant="translucent"
modifier="square"
isActive={!props.screenShared}
colorScheme="light"
>
<Icon
source={
props.screenShared
? IconTypes.IconPresentationOff
: IconTypes.IconPresentationOn
}
/>
</Button>
</Tooltip>
Run the app
Now we have everything in place to test our new functionality. The user only has to press the screen share button and the rest of the users will start seeing the shared content.
You can compare your code with the solution in Step.07-Solution-Send-presentation. You can also check the differences with the previous lesson in the git diff.