Group calling: tell RingRTC about our rendered resolutions for perf

This commit is contained in:
Evan Hahn 2020-12-01 19:52:01 -06:00 committed by GitHub
parent b30b83ed57
commit d1866a0e5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 211 additions and 7 deletions

View file

@ -73,6 +73,7 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
title: text('Caller Title', 'Morty Smith'),
},
renderDeviceSelection: () => <div />,
setGroupCallVideoRequest: action('set-group-call-video-request'),
setLocalAudio: action('set-local-audio'),
setLocalPreview: action('set-local-preview'),
setLocalVideo: action('set-local-video'),

View file

@ -13,6 +13,7 @@ import {
CallMode,
CallState,
GroupCallJoinState,
GroupCallVideoRequest,
VideoFrameSource,
} from '../types/Calling';
import { ConversationType } from '../state/ducks/conversations';
@ -23,6 +24,7 @@ import {
DeclineCallType,
DirectCallStateType,
HangUpType,
SetGroupCallVideoRequestType,
SetLocalAudioType,
SetLocalPreviewType,
SetLocalVideoType,
@ -60,6 +62,7 @@ export interface PropsType {
profileName?: string;
title: string;
};
setGroupCallVideoRequest: (_: SetGroupCallVideoRequestType) => void;
setLocalAudio: (_: SetLocalAudioType) => void;
setLocalVideo: (_: SetLocalVideoType) => void;
setLocalPreview: (_: SetLocalPreviewType) => void;
@ -83,6 +86,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
getGroupCallVideoFrameSource,
me,
renderDeviceSelection,
setGroupCallVideoRequest,
setLocalAudio,
setLocalPreview,
setLocalVideo,
@ -129,6 +133,16 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
[getGroupCallVideoFrameSource, conversation.id]
);
const setGroupCallVideoRequestForConversation = useCallback(
(resolutions: Array<GroupCallVideoRequest>) => {
setGroupCallVideoRequest({
conversationId: conversation.id,
resolutions,
});
},
[setGroupCallVideoRequest, conversation.id]
);
let showCallLobby: boolean;
switch (call.callMode) {
@ -205,6 +219,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
hangUp={hangUp}
hasLocalVideo={hasLocalVideo}
i18n={i18n}
setGroupCallVideoRequest={setGroupCallVideoRequestForConversation}
setLocalPreview={setLocalPreview}
setRendererCanvas={setRendererCanvas}
togglePip={togglePip}
@ -223,6 +238,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
i18n={i18n}
joinedAt={joinedAt}
me={me}
setGroupCallVideoRequest={setGroupCallVideoRequestForConversation}
setLocalPreview={setLocalPreview}
setRendererCanvas={setRendererCanvas}
setLocalAudio={setLocalAudio}

View file

@ -114,6 +114,7 @@ const createProps = (
profileName: 'Morty Smith',
title: 'Morty Smith',
},
setGroupCallVideoRequest: action('set-group-call-video-request'),
setLocalAudio: action('set-local-audio'),
setLocalPreview: action('set-local-preview'),
setLocalVideo: action('set-local-video'),

View file

@ -20,6 +20,7 @@ import {
CallMode,
CallState,
GroupCallConnectionState,
GroupCallVideoRequest,
VideoFrameSource,
} from '../types/Calling';
import { ColorType } from '../types/Colors';
@ -45,6 +46,7 @@ export type PropsType = {
profileName?: string;
title: string;
};
setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void;
setLocalAudio: (_: SetLocalAudioType) => void;
setLocalVideo: (_: SetLocalVideoType) => void;
setLocalPreview: (_: SetLocalPreviewType) => void;
@ -64,6 +66,7 @@ export const CallScreen: React.FC<PropsType> = ({
i18n,
joinedAt,
me,
setGroupCallVideoRequest,
setLocalAudio,
setLocalVideo,
setLocalPreview,
@ -187,6 +190,7 @@ export const CallScreen: React.FC<PropsType> = ({
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
i18n={i18n}
remoteParticipants={groupCallParticipants}
setGroupCallVideoRequest={setGroupCallVideoRequest}
/>
);
break;

View file

@ -68,6 +68,7 @@ const createProps = (
hangUp: action('hang-up'),
hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false),
i18n,
setGroupCallVideoRequest: action('set-group-call-video-request'),
setLocalPreview: action('set-local-preview'),
setRendererCanvas: action('set-renderer-canvas'),
togglePip: action('toggle-pip'),

View file

@ -5,7 +5,7 @@ import React from 'react';
import { minBy, debounce, noop } from 'lodash';
import { CallingPipRemoteVideo } from './CallingPipRemoteVideo';
import { LocalizerType } from '../types/Util';
import { VideoFrameSource } from '../types/Calling';
import { GroupCallVideoRequest, VideoFrameSource } from '../types/Calling';
import {
ActiveCallType,
HangUpType,
@ -54,6 +54,7 @@ export type PropsType = {
hangUp: (_: HangUpType) => void;
hasLocalVideo: boolean;
i18n: LocalizerType;
setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void;
setLocalPreview: (_: SetLocalPreviewType) => void;
setRendererCanvas: (_: SetRendererCanvasType) => void;
togglePip: () => void;
@ -70,6 +71,7 @@ export const CallingPip = ({
hangUp,
hasLocalVideo,
i18n,
setGroupCallVideoRequest,
setLocalPreview,
setRendererCanvas,
togglePip,
@ -269,6 +271,7 @@ export const CallingPip = ({
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
i18n={i18n}
setRendererCanvas={setRendererCanvas}
setGroupCallVideoRequest={setGroupCallVideoRequest}
/>
{hasLocalVideo ? (
<video

View file

@ -1,7 +1,7 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useMemo } from 'react';
import React, { useMemo, useEffect } from 'react';
import { maxBy } from 'lodash';
import { Avatar } from './Avatar';
import { CallBackgroundBlur } from './CallBackgroundBlur';
@ -11,9 +11,15 @@ import { LocalizerType } from '../types/Util';
import {
CallMode,
GroupCallRemoteParticipantType,
GroupCallVideoRequest,
VideoFrameSource,
} from '../types/Calling';
import { ActiveCallType, SetRendererCanvasType } from '../state/ducks/calling';
import { usePageVisibility } from '../util/hooks';
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,
@ -57,6 +63,7 @@ export interface PropsType {
activeCall: ActiveCallType;
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
i18n: LocalizerType;
setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void;
setRendererCanvas: (_: SetRendererCanvasType) => void;
}
@ -64,10 +71,13 @@ export const CallingPipRemoteVideo = ({
activeCall,
getGroupCallVideoFrameSource,
i18n,
setGroupCallVideoRequest,
setRendererCanvas,
}: PropsType): JSX.Element => {
const { call, conversation, groupCallParticipants } = activeCall;
const isPageVisible = usePageVisibility();
const activeGroupCallSpeaker:
| undefined
| GroupCallRemoteParticipantType = useMemo(() => {
@ -81,6 +91,42 @@ export const CallingPipRemoteVideo = ({
);
}, [call.callMode, groupCallParticipants]);
useEffect(() => {
if (call.callMode !== CallMode.Group) {
return;
}
if (isPageVisible) {
setGroupCallVideoRequest(
groupCallParticipants.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(
groupCallParticipants.map(nonRenderedRemoteParticipant)
);
}
}, [
call.callMode,
groupCallParticipants,
activeGroupCallSpeaker,
isPageVisible,
setGroupCallVideoRequest,
]);
if (call.callMode === CallMode.Direct) {
if (!call.hasRemoteVideo) {
return <NoVideo activeCall={activeCall} i18n={i18n} />;

View file

@ -1,19 +1,25 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState, useMemo } from 'react';
import React, { useState, useMemo, useEffect } from 'react';
import Measure from 'react-measure';
import { takeWhile, chunk, maxBy, flatten } from 'lodash';
import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
import {
GroupCallRemoteParticipantType,
GroupCallVideoRequest,
VideoFrameSource,
} from '../types/Calling';
import { LocalizerType } from '../types/Util';
import { usePageVisibility } from '../util/hooks';
import { nonRenderedRemoteParticipant } from '../util/ringrtc/nonRenderedRemoteParticipant';
const MIN_RENDERED_HEIGHT = 10;
const PARTICIPANT_MARGIN = 10;
// We scale our video requests down for performance. This number is somewhat arbitrary.
const VIDEO_REQUEST_SCALAR = 0.75;
interface Dimensions {
width: number;
height: number;
@ -28,6 +34,7 @@ interface PropsType {
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
i18n: LocalizerType;
remoteParticipants: ReadonlyArray<GroupCallRemoteParticipantType>;
setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void;
}
// This component lays out group call remote participants. It uses a custom layout
@ -58,11 +65,13 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
getGroupCallVideoFrameSource,
i18n,
remoteParticipants,
setGroupCallVideoRequest,
}) => {
const [containerDimensions, setContainerDimensions] = useState<Dimensions>({
width: 0,
height: 0,
});
const isPageVisible = usePageVisibility();
// 1. Figure out the maximum number of possible rows that could fit on the screen.
//
@ -101,6 +110,9 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
return totalWidth < maxTotalWidth;
});
}, [maxRowCount, containerDimensions.width, remoteParticipants]);
const overflowedParticipants: Array<GroupCallRemoteParticipantType> = remoteParticipants.slice(
visibleParticipants.length
);
// 3. For each possible number of rows (starting at 0 and ending at `maxRowCount`),
// distribute participants across the rows at the minimum height. Then find the
@ -216,7 +228,39 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
});
}
);
const remoteParticipantElements = flatten(rowElements);
useEffect(() => {
if (isPageVisible) {
setGroupCallVideoRequest([
...visibleParticipants.map(participant => {
if (participant.hasRemoteVideo) {
return {
demuxId: participant.demuxId,
width: Math.floor(
gridParticipantHeight *
participant.videoAspectRatio *
VIDEO_REQUEST_SCALAR
),
height: Math.floor(gridParticipantHeight * VIDEO_REQUEST_SCALAR),
};
}
return nonRenderedRemoteParticipant(participant);
}),
...overflowedParticipants.map(nonRenderedRemoteParticipant),
]);
} else {
setGroupCallVideoRequest(
remoteParticipants.map(nonRenderedRemoteParticipant)
);
}
}, [
gridParticipantHeight,
isPageVisible,
overflowedParticipants,
remoteParticipants,
setGroupCallVideoRequest,
visibleParticipants,
]);
return (
<Measure
@ -231,7 +275,7 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
>
{({ measureRef }) => (
<div className="module-ongoing-call__grid" ref={measureRef}>
{remoteParticipantElements}
{flatten(rowElements)}
</div>
)}
</Measure>