Group calls: when window is invisible, stop requesting video after 20 seconds
This commit is contained in:
parent
3bd3207e18
commit
0c83b1d26b
1 changed files with 96 additions and 39 deletions
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue