// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useMemo, useEffect } from 'react'; import { maxBy } from 'lodash'; import { Avatar } from './Avatar'; import { CallBackgroundBlur } from './CallBackgroundBlur'; import { DirectCallRemoteParticipant } from './DirectCallRemoteParticipant'; import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant'; import { LocalizerType } from '../types/Util'; import { ActiveCallType, CallMode, GroupCallRemoteParticipantType, GroupCallVideoRequest, VideoFrameSource, } from '../types/Calling'; import { SetRendererCanvasType } from '../state/ducks/calling'; import { useGetCallingFrameBuffer } from '../calling/useGetCallingFrameBuffer'; import { usePageVisibility } from '../util/hooks'; import { missingCaseError } from '../util/missingCaseError'; import { nonRenderedRemoteParticipant } from '../util/ringrtc/nonRenderedRemoteParticipant'; // This value should be kept in sync with the hard-coded CSS height. const PIP_VIDEO_HEIGHT_PX = 120; const NoVideo = ({ activeCall, i18n, }: { activeCall: ActiveCallType; i18n: LocalizerType; }): JSX.Element => { const { acceptedMessageRequest, avatarPath, color, isMe, name, phoneNumber, profileName, sharedGroupNames, title, } = activeCall.conversation; return (
); }; export type PropsType = { activeCall: ActiveCallType; getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource; i18n: LocalizerType; setGroupCallVideoRequest: (_: Array) => void; setRendererCanvas: (_: SetRendererCanvasType) => void; }; export const CallingPipRemoteVideo = ({ activeCall, getGroupCallVideoFrameSource, i18n, setGroupCallVideoRequest, setRendererCanvas, }: PropsType): JSX.Element => { const { conversation } = activeCall; const getGroupCallFrameBuffer = useGetCallingFrameBuffer(); const isPageVisible = usePageVisibility(); const activeGroupCallSpeaker: | undefined | GroupCallRemoteParticipantType = useMemo(() => { if (activeCall.callMode !== CallMode.Group) { return undefined; } return maxBy(activeCall.remoteParticipants, participant => participant.presenting ? Infinity : participant.speakerTime || -Infinity ); }, [activeCall.callMode, activeCall.remoteParticipants]); useEffect(() => { if (activeCall.callMode !== CallMode.Group) { return; } if (isPageVisible) { setGroupCallVideoRequest( activeCall.remoteParticipants.map(participant => { const isVisible = participant === activeGroupCallSpeaker && participant.hasRemoteVideo; if (isVisible) { return { demuxId: participant.demuxId, width: Math.floor( PIP_VIDEO_HEIGHT_PX * participant.videoAspectRatio ), height: PIP_VIDEO_HEIGHT_PX, }; } return nonRenderedRemoteParticipant(participant); }) ); } else { setGroupCallVideoRequest( activeCall.remoteParticipants.map(nonRenderedRemoteParticipant) ); } }, [ activeCall.callMode, activeCall.remoteParticipants, activeGroupCallSpeaker, isPageVisible, setGroupCallVideoRequest, ]); switch (activeCall.callMode) { case CallMode.Direct: { const { hasRemoteVideo } = activeCall.remoteParticipants[0]; if (!hasRemoteVideo) { return ; } return (
); } case CallMode.Group: if (!activeGroupCallSpeaker) { return ; } return (
); default: throw missingCaseError(activeCall); } };