Add support for screen-sharing

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2022-08-07 19:09:45 +02:00
commit 305c2cb806
No known key found for this signature in database
GPG key ID: 4F68B9EC0536B5CC
5 changed files with 127 additions and 54 deletions

View file

@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { useCallback, useMemo } from "react";
import React, { useCallback, useMemo, useRef } from "react";
import { usePreventScroll } from "@react-aria/overlays";
import { GroupCall, MatrixClient } from "matrix-js-sdk";
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
import classNames from "classnames";
import styles from "./InCallView.module.css";
import {
@ -46,6 +47,7 @@ import { useMediaHandler } from "../settings/useMediaHandler";
import { useShowInspector } from "../settings/useSetting";
import { useModalTriggerState } from "../Modal";
import { useAudioContext } from "../video-grid/useMediaStream";
import { useFullscreen } from "../video-grid/useFullscreen";
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
@ -72,7 +74,7 @@ interface Props {
roomId: string;
unencryptedEventsFromUsers: Set<string>;
}
interface Participant {
export interface Participant {
id: string;
callFeed: CallFeed;
focused: boolean;
@ -100,7 +102,9 @@ export function InCallView({
unencryptedEventsFromUsers,
}: Props) {
usePreventScroll();
const elementRef = useRef<HTMLDivElement>();
const [layout, setLayout] = useVideoGridLayout(screenshareFeeds.length > 0);
const { toggleFullscreen, fullscreenParticipant } = useFullscreen(elementRef);
const [audioContext, audioDestination, audioRef] = useAudioContext();
const { audioOutput } = useMediaHandler();
@ -161,65 +165,107 @@ export function InCallView({
);
}, []);
const renderContent = useCallback(() => {
if (items.length === 0) {
return (
<div className={styles.centerMessage}>
<p>Waiting for other participants...</p>
</div>
);
}
if (fullscreenParticipant) {
return (
<VideoTileContainer
key={fullscreenParticipant.id}
item={fullscreenParticipant}
getAvatar={renderAvatar}
audioOutputDevice={audioOutput}
audioContext={audioContext}
audioDestination={audioDestination}
disableSpeakingIndicator={true}
isFullscreen={fullscreenParticipant}
onFullscreen={toggleFullscreen}
/>
);
}
return (
<VideoGrid items={items} layout={layout} disableAnimations={isSafari}>
{({ item, ...rest }: { item: Participant; [x: string]: unknown }) => (
<VideoTileContainer
key={item.id}
item={item}
getAvatar={renderAvatar}
showName={items.length > 2 || item.focused}
audioOutputDevice={audioOutput}
audioContext={audioContext}
audioDestination={audioDestination}
disableSpeakingIndicator={items.length < 3}
isFullscreen={fullscreenParticipant}
onFullscreen={toggleFullscreen}
{...rest}
/>
)}
</VideoGrid>
);
}, [
fullscreenParticipant,
items,
audioContext,
audioDestination,
audioOutput,
layout,
renderAvatar,
toggleFullscreen,
]);
const {
modalState: rageshakeRequestModalState,
modalProps: rageshakeRequestModalProps,
} = useRageshakeRequestModal(groupCall.room.roomId);
const footerClassNames = classNames(styles.footer, {
[styles.footerFullscreen]: fullscreenParticipant,
});
return (
<div className={styles.inRoom}>
<div className={styles.inRoom} ref={elementRef}>
<audio ref={audioRef} />
<Header>
<LeftNav>
<RoomHeaderInfo roomName={roomName} avatarUrl={avatarUrl} />
<VersionMismatchWarning
users={unencryptedEventsFromUsers}
room={groupCall.room}
/>
</LeftNav>
<RightNav>
<GridLayoutMenu layout={layout} setLayout={setLayout} />
<UserMenuContainer preventNavigation />
</RightNav>
</Header>
{items.length === 0 ? (
<div className={styles.centerMessage}>
<p>Waiting for other participants...</p>
</div>
) : (
<VideoGrid items={items} layout={layout} disableAnimations={isSafari}>
{({ item, ...rest }: { item: Participant; [x: string]: unknown }) => (
<VideoTileContainer
key={item.id}
item={item}
getAvatar={renderAvatar}
showName={items.length > 2 || item.focused}
audioOutputDevice={audioOutput}
audioContext={audioContext}
audioDestination={audioDestination}
disableSpeakingIndicator={items.length < 3}
{...rest}
{!fullscreenParticipant && (
<Header>
<LeftNav>
<RoomHeaderInfo roomName={roomName} avatarUrl={avatarUrl} />
<VersionMismatchWarning
users={unencryptedEventsFromUsers}
room={groupCall.room}
/>
)}
</VideoGrid>
</LeftNav>
<RightNav>
<GridLayoutMenu layout={layout} setLayout={setLayout} />
<UserMenuContainer preventNavigation />
</RightNav>
</Header>
)}
<div className={styles.footer}>
{renderContent()}
<div className={footerClassNames}>
<MicButton muted={microphoneMuted} onPress={toggleMicrophoneMuted} />
<VideoButton muted={localVideoMuted} onPress={toggleLocalVideoMuted} />
{canScreenshare && !isSafari && (
{canScreenshare && !isSafari && !fullscreenParticipant && (
<ScreenshareButton
enabled={isScreensharing}
onPress={toggleScreensharing}
/>
)}
<OverflowMenu
inCall
roomId={roomId}
groupCall={groupCall}
showInvite={true}
feedbackModalState={feedbackModalState}
feedbackModalProps={feedbackModalProps}
/>
{!fullscreenParticipant && (
<OverflowMenu
inCall
roomId={roomId}
groupCall={groupCall}
showInvite={true}
feedbackModalState={feedbackModalState}
feedbackModalProps={feedbackModalProps}
/>
)}
<HangupButton onPress={onLeave} />
</div>
<GroupCallInspector