Group calls: when window is invisible, stop requesting video after 20 seconds

This commit is contained in:
Evan Hahn 2021-11-04 16:55:30 -05:00 committed by GitHub
parent 3bd3207e18
commit 0c83b1d26b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,7 +3,7 @@
import React, { useState, useMemo, useEffect } from 'react'; import React, { useState, useMemo, useEffect } from 'react';
import Measure from 'react-measure'; import Measure from 'react-measure';
import { takeWhile, chunk, maxBy, flatten } from 'lodash'; import { takeWhile, chunk, maxBy, flatten, noop } from 'lodash';
import type { VideoFrameSource } from 'ringrtc'; import type { VideoFrameSource } from 'ringrtc';
import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant'; import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
import { import {
@ -18,10 +18,13 @@ import { useGetCallingFrameBuffer } from '../calling/useGetCallingFrameBuffer';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
import { usePageVisibility } from '../hooks/usePageVisibility'; import { usePageVisibility } from '../hooks/usePageVisibility';
import { nonRenderedRemoteParticipant } from '../util/ringrtc/nonRenderedRemoteParticipant'; import { nonRenderedRemoteParticipant } from '../util/ringrtc/nonRenderedRemoteParticipant';
import { missingCaseError } from '../util/missingCaseError';
import { SECOND } from '../util/durations';
import * as log from '../logging/log'; import * as log from '../logging/log';
const MIN_RENDERED_HEIGHT = 180; const MIN_RENDERED_HEIGHT = 180;
const PARTICIPANT_MARGIN = 10; const PARTICIPANT_MARGIN = 10;
const TIME_TO_STOP_REQUESTING_VIDEO_WHEN_PAGE_INVISIBLE = 20 * SECOND;
// We scale our video requests down for performance. This number is somewhat arbitrary. // We scale our video requests down for performance. This number is somewhat arbitrary.
const VIDEO_REQUEST_SCALAR = 0.75; const VIDEO_REQUEST_SCALAR = 0.75;
@ -44,6 +47,12 @@ type PropsType = {
setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void; setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void;
}; };
enum VideoRequestMode {
Normal,
LowResolution,
NoVideo,
}
// This component lays out group call remote participants. It uses a custom layout // This component lays out group call remote participants. It uses a custom layout
// algorithm (in other words, nothing that the browser provides, like flexbox) in // algorithm (in other words, nothing that the browser provides, like flexbox) in
// order to animate the boxes as they move around, and to figure out the right fits. // order to animate the boxes as they move around, and to figure out the right fits.
@ -83,7 +92,6 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
height: 0, height: 0,
}); });
const isPageVisible = usePageVisibility();
const getFrameBuffer = useGetCallingFrameBuffer(); const getFrameBuffer = useGetCallingFrameBuffer();
// 1. Figure out the maximum number of possible rows that could fit on the screen. // 1. Figure out the maximum number of possible rows that could fit on the screen.
@ -273,52 +281,74 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
} }
); );
const videoRequestMode = useVideoRequestMode();
useEffect(() => { useEffect(() => {
if (isPageVisible) { let videoRequest: Array<GroupCallVideoRequest>;
setGroupCallVideoRequest([
...gridParticipants.map(participant => { switch (videoRequestMode) {
let scalar: number; case VideoRequestMode.Normal:
if (participant.sharingScreen) { videoRequest = [
// We want best-resolution video if someone is sharing their screen. This code ...gridParticipants.map(participant => {
// is extra-defensive against strange devicePixelRatios. let scalar: number;
scalar = Math.max(window.devicePixelRatio || 1, 1); if (participant.sharingScreen) {
} else if (participant.hasRemoteVideo) { // We want best-resolution video if someone is sharing their screen. This
scalar = VIDEO_REQUEST_SCALAR; // code is extra-defensive against strange devicePixelRatios.
} else { scalar = Math.max(window.devicePixelRatio || 1, 1);
scalar = 0; } else if (participant.hasRemoteVideo) {
} scalar = VIDEO_REQUEST_SCALAR;
return { } else {
demuxId: participant.demuxId, scalar = 0;
width: Math.floor( }
gridParticipantHeight * participant.videoAspectRatio * scalar
),
height: Math.floor(gridParticipantHeight * scalar),
};
}),
...overflowedParticipants.map(participant => {
if (participant.hasRemoteVideo) {
return { return {
demuxId: participant.demuxId, demuxId: participant.demuxId,
width: Math.floor( width: Math.floor(
OVERFLOW_PARTICIPANT_WIDTH * VIDEO_REQUEST_SCALAR gridParticipantHeight * participant.videoAspectRatio * scalar
),
height: Math.floor(
(OVERFLOW_PARTICIPANT_WIDTH / participant.videoAspectRatio) *
VIDEO_REQUEST_SCALAR
), ),
height: Math.floor(gridParticipantHeight * scalar),
}; };
} }),
return nonRenderedRemoteParticipant(participant); ...overflowedParticipants.map(participant => {
}), if (participant.hasRemoteVideo) {
]); return {
} else { demuxId: participant.demuxId,
setGroupCallVideoRequest( width: Math.floor(
remoteParticipants.map(nonRenderedRemoteParticipant) OVERFLOW_PARTICIPANT_WIDTH * VIDEO_REQUEST_SCALAR
); ),
height: Math.floor(
(OVERFLOW_PARTICIPANT_WIDTH / participant.videoAspectRatio) *
VIDEO_REQUEST_SCALAR
),
};
}
return nonRenderedRemoteParticipant(participant);
}),
];
break;
case VideoRequestMode.LowResolution:
videoRequest = remoteParticipants.map(participant =>
participant.hasRemoteVideo
? {
demuxId: participant.demuxId,
width: 1,
height: 1,
}
: nonRenderedRemoteParticipant(participant)
);
break;
case VideoRequestMode.NoVideo:
videoRequest = remoteParticipants.map(nonRenderedRemoteParticipant);
break;
default:
log.error(missingCaseError(videoRequestMode));
videoRequest = remoteParticipants.map(nonRenderedRemoteParticipant);
break;
} }
setGroupCallVideoRequest(videoRequest);
}, [ }, [
gridParticipantHeight, gridParticipantHeight,
isPageVisible, videoRequestMode,
overflowedParticipants, overflowedParticipants,
remoteParticipants, remoteParticipants,
setGroupCallVideoRequest, setGroupCallVideoRequest,
@ -373,6 +403,33 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
); );
}; };
function useVideoRequestMode(): VideoRequestMode {
const isPageVisible = usePageVisibility();
const [result, setResult] = useState<VideoRequestMode>(
isPageVisible ? VideoRequestMode.Normal : VideoRequestMode.LowResolution
);
useEffect(() => {
if (isPageVisible) {
setResult(VideoRequestMode.Normal);
return noop;
}
setResult(VideoRequestMode.LowResolution);
const timeout = setTimeout(() => {
setResult(VideoRequestMode.NoVideo);
}, TIME_TO_STOP_REQUESTING_VIDEO_WHEN_PAGE_INVISIBLE);
return () => {
clearTimeout(timeout);
};
}, [isPageVisible]);
return result;
}
function totalRemoteParticipantWidthAtMinHeight( function totalRemoteParticipantWidthAtMinHeight(
remoteParticipants: ReadonlyArray<GroupCallRemoteParticipantType> remoteParticipants: ReadonlyArray<GroupCallRemoteParticipantType>
): number { ): number {