signal-desktop/ts/components/CallingPipRemoteVideo.tsx

186 lines
5.7 KiB
TypeScript
Raw Normal View History

2023-01-03 19:55:46 +00:00
// Copyright 2020 Signal Messenger, LLC
2020-11-17 15:07:53 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useMemo, useEffect } from 'react';
import { clamp, maxBy } from 'lodash';
2023-01-09 18:38:57 +00:00
import type { VideoFrameSource } from '@signalapp/ringrtc';
2022-12-09 20:37:45 +00:00
import { Avatar, AvatarSize } from './Avatar';
2020-11-17 15:07:53 +00:00
import { CallBackgroundBlur } from './CallBackgroundBlur';
import { DirectCallRemoteParticipant } from './DirectCallRemoteParticipant';
import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
import type { LocalizerType } from '../types/Util';
import type {
2020-12-02 18:14:03 +00:00
ActiveCallType,
GroupCallRemoteParticipantType,
GroupCallVideoRequest,
} from '../types/Calling';
import { CallMode } from '../types/Calling';
2021-08-06 00:17:05 +00:00
import { AvatarColors } from '../types/Colors';
import type { SetRendererCanvasType } from '../state/ducks/calling';
import { useGetCallingFrameBuffer } from '../calling/useGetCallingFrameBuffer';
import { MAX_FRAME_WIDTH } from '../calling/constants';
2021-09-17 22:24:21 +00:00
import { usePageVisibility } from '../hooks/usePageVisibility';
import { missingCaseError } from '../util/missingCaseError';
import { nonRenderedRemoteParticipant } from '../util/ringrtc/nonRenderedRemoteParticipant';
import { isReconnecting } from '../util/callingIsReconnecting';
// This value should be kept in sync with the hard-coded CSS height. It should also be
// less than `MAX_FRAME_HEIGHT`.
const PIP_VIDEO_HEIGHT_PX = 120;
2020-11-17 15:07:53 +00:00
2022-11-18 00:45:19 +00:00
function NoVideo({
activeCall,
i18n,
}: {
activeCall: ActiveCallType;
i18n: LocalizerType;
2022-11-18 00:45:19 +00:00
}): JSX.Element {
const {
2021-05-07 22:21:10 +00:00
acceptedMessageRequest,
avatarPath,
color,
2021-05-07 22:21:10 +00:00
isMe,
phoneNumber,
profileName,
2021-05-07 22:21:10 +00:00
sharedGroupNames,
title,
} = activeCall.conversation;
return (
<div className="module-calling-pip__video--remote">
<CallBackgroundBlur avatarPath={avatarPath} color={color}>
<div className="module-calling-pip__video--avatar">
<Avatar
2021-05-07 22:21:10 +00:00
acceptedMessageRequest={acceptedMessageRequest}
avatarPath={avatarPath}
badge={undefined}
2021-08-06 00:17:05 +00:00
color={color || AvatarColors[0]}
noteToSelf={false}
conversationType="direct"
i18n={i18n}
2021-05-07 22:21:10 +00:00
isMe={isMe}
phoneNumber={phoneNumber}
profileName={profileName}
title={title}
2022-12-09 20:37:45 +00:00
size={AvatarSize.FORTY_EIGHT}
2021-05-07 22:21:10 +00:00
sharedGroupNames={sharedGroupNames}
/>
</div>
</CallBackgroundBlur>
</div>
);
2022-11-18 00:45:19 +00:00
}
export type PropsType = {
activeCall: ActiveCallType;
2020-11-17 15:07:53 +00:00
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
i18n: LocalizerType;
2022-09-07 15:52:55 +00:00
setGroupCallVideoRequest: (
_: Array<GroupCallVideoRequest>,
speakerHeight: number
) => void;
2020-11-17 15:07:53 +00:00
setRendererCanvas: (_: SetRendererCanvasType) => void;
};
2020-11-17 15:07:53 +00:00
2022-11-18 00:45:19 +00:00
export function CallingPipRemoteVideo({
activeCall,
2020-11-17 15:07:53 +00:00
getGroupCallVideoFrameSource,
i18n,
setGroupCallVideoRequest,
2020-11-17 15:07:53 +00:00
setRendererCanvas,
2022-11-18 00:45:19 +00:00
}: PropsType): JSX.Element {
2020-12-02 18:14:03 +00:00
const { conversation } = activeCall;
const getGroupCallFrameBuffer = useGetCallingFrameBuffer();
const isPageVisible = usePageVisibility();
2021-11-11 22:43:05 +00:00
const activeGroupCallSpeaker: undefined | GroupCallRemoteParticipantType =
useMemo(() => {
if (activeCall.callMode !== CallMode.Group) {
return undefined;
}
2021-11-11 22:43:05 +00:00
return maxBy(activeCall.remoteParticipants, participant =>
participant.presenting ? Infinity : participant.speakerTime || -Infinity
);
}, [activeCall.callMode, activeCall.remoteParticipants]);
useEffect(() => {
2020-12-02 18:14:03 +00:00
if (activeCall.callMode !== CallMode.Group) {
return;
}
if (isPageVisible) {
setGroupCallVideoRequest(
2020-12-02 18:14:03 +00:00
activeCall.remoteParticipants.map(participant => {
if (participant === activeGroupCallSpeaker) {
return {
demuxId: participant.demuxId,
width: clamp(
Math.floor(PIP_VIDEO_HEIGHT_PX * participant.videoAspectRatio),
1,
MAX_FRAME_WIDTH
),
height: PIP_VIDEO_HEIGHT_PX,
};
}
return nonRenderedRemoteParticipant(participant);
2022-09-07 15:52:55 +00:00
}),
PIP_VIDEO_HEIGHT_PX
);
} else {
setGroupCallVideoRequest(
2022-09-07 15:52:55 +00:00
activeCall.remoteParticipants.map(nonRenderedRemoteParticipant),
0
);
}
}, [
2020-12-02 18:14:03 +00:00
activeCall.callMode,
activeCall.remoteParticipants,
activeGroupCallSpeaker,
isPageVisible,
setGroupCallVideoRequest,
]);
switch (activeCall.callMode) {
case CallMode.Direct: {
const { hasRemoteVideo } = activeCall.remoteParticipants[0];
if (!hasRemoteVideo) {
return <NoVideo activeCall={activeCall} i18n={i18n} />;
}
return (
<div className="module-calling-pip__video--remote">
<DirectCallRemoteParticipant
conversation={conversation}
hasRemoteVideo={hasRemoteVideo}
i18n={i18n}
isReconnecting={isReconnecting(activeCall)}
setRendererCanvas={setRendererCanvas}
/>
</div>
);
}
case CallMode.Group:
if (!activeGroupCallSpeaker) {
return <NoVideo activeCall={activeCall} i18n={i18n} />;
}
return (
<div className="module-calling-pip__video--remote">
<GroupCallRemoteParticipant
getFrameBuffer={getGroupCallFrameBuffer}
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
i18n={i18n}
isInPip
remoteParticipant={activeGroupCallSpeaker}
remoteParticipantsCount={activeCall.remoteParticipants.length}
isActiveSpeakerInSpeakerView={false}
isCallReconnecting={isReconnecting(activeCall)}
/>
</div>
);
default:
throw missingCaseError(activeCall);
2020-11-17 15:07:53 +00:00
}
2022-11-18 00:45:19 +00:00
}