Group calling: show avatar if we haven't received video yet/in awhile
This commit is contained in:
parent
01eabf9ec6
commit
b1c1bd5e41
2 changed files with 59 additions and 4 deletions
ts
|
@ -24,6 +24,8 @@ import { ContactName } from './conversation/ContactName';
|
|||
import { useIntersectionObserver } from '../util/hooks';
|
||||
import { MAX_FRAME_SIZE } from '../calling/constants';
|
||||
|
||||
const MAX_TIME_TO_SHOW_STALE_VIDEO_FRAMES = 5000;
|
||||
|
||||
type BasePropsType = {
|
||||
getFrameBuffer: () => ArrayBuffer;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
|
@ -68,12 +70,24 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
videoAspectRatio,
|
||||
} = props.remoteParticipant;
|
||||
|
||||
const [hasReceivedVideoRecently, setHasReceivedVideoRecently] = useState(
|
||||
false
|
||||
);
|
||||
const [isWide, setIsWide] = useState<boolean>(
|
||||
videoAspectRatio ? videoAspectRatio >= 1 : true
|
||||
);
|
||||
const [hasHover, setHover] = useState(false);
|
||||
const [showBlockInfo, setShowBlockInfo] = useState(false);
|
||||
|
||||
// We have some state (`hasReceivedVideoRecently`) and this ref. We can't have a
|
||||
// single state value like `lastReceivedVideoAt` because (1) it won't automatically
|
||||
// trigger a re-render after the video has become stale (2) it would cause a full
|
||||
// re-render of the component for every frame, which is way too often.
|
||||
//
|
||||
// Alternatively, we could create a timeout that's reset every time we get a video
|
||||
// frame (perhaps using a debounce function), but that becomes harder to clean up
|
||||
// when the component unmounts.
|
||||
const lastReceivedVideoAt = useRef(-Infinity);
|
||||
const remoteVideoRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const canvasContextRef = useRef<CanvasRenderingContext2D | null>(null);
|
||||
|
||||
|
@ -85,12 +99,22 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
? intersectionObserverEntry.isIntersecting
|
||||
: true;
|
||||
|
||||
const wantsToShowVideo = hasRemoteVideo && !isBlocked && isVisible;
|
||||
const hasVideoToShow = wantsToShowVideo && hasReceivedVideoRecently;
|
||||
|
||||
const videoFrameSource = useMemo(
|
||||
() => getGroupCallVideoFrameSource(demuxId),
|
||||
[getGroupCallVideoFrameSource, demuxId]
|
||||
);
|
||||
|
||||
const renderVideoFrame = useCallback(() => {
|
||||
if (
|
||||
Date.now() - lastReceivedVideoAt.current >
|
||||
MAX_TIME_TO_SHOW_STALE_VIDEO_FRAMES
|
||||
) {
|
||||
setHasReceivedVideoRecently(false);
|
||||
}
|
||||
|
||||
const canvasEl = remoteVideoRef.current;
|
||||
if (!canvasEl) {
|
||||
return;
|
||||
|
@ -133,9 +157,18 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
0
|
||||
);
|
||||
|
||||
lastReceivedVideoAt.current = Date.now();
|
||||
|
||||
setHasReceivedVideoRecently(true);
|
||||
setIsWide(frameWidth > frameHeight);
|
||||
}, [getFrameBuffer, videoFrameSource]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasRemoteVideo) {
|
||||
setHasReceivedVideoRecently(false);
|
||||
}
|
||||
}, [hasRemoteVideo]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasRemoteVideo || !isVisible) {
|
||||
return noop;
|
||||
|
@ -198,7 +231,6 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
}
|
||||
|
||||
const showHover = hasHover && !props.isInPip;
|
||||
const canShowVideo = hasRemoteVideo && !isBlocked && isVisible;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -254,10 +286,16 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
{canShowVideo ? (
|
||||
{wantsToShowVideo && (
|
||||
<canvas
|
||||
className="module-ongoing-call__group-call-remote-participant__remote-video"
|
||||
style={canvasStyles}
|
||||
style={{
|
||||
...canvasStyles,
|
||||
// If we want to show video but don't have any yet, we still render the
|
||||
// canvas invisibly. This lets us render frame data immediately without
|
||||
// having to juggle anything.
|
||||
...(hasVideoToShow ? {} : { display: 'none' }),
|
||||
}}
|
||||
ref={canvasEl => {
|
||||
remoteVideoRef.current = canvasEl;
|
||||
if (canvasEl) {
|
||||
|
@ -271,7 +309,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
)}
|
||||
{!hasVideoToShow && (
|
||||
<CallBackgroundBlur avatarPath={avatarPath} color={color}>
|
||||
{isBlocked ? (
|
||||
<>
|
||||
|
|
|
@ -13570,6 +13570,22 @@
|
|||
"updated": "2020-11-17T23:29:38.698Z",
|
||||
"reasonDetail": "Doesn't touch the DOM."
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/GroupCallRemoteParticipant.js",
|
||||
"line": " const lastReceivedVideoAt = react_1.useRef(-Infinity);",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-06-17T20:46:02.342Z",
|
||||
"reasonDetail": "Doesn't reference the DOM."
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/GroupCallRemoteParticipant.tsx",
|
||||
"line": " const lastReceivedVideoAt = useRef(-Infinity);",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-06-17T20:46:02.342Z",
|
||||
"reasonDetail": "Doesn't reference the DOM."
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/GroupDescriptionInput.js",
|
||||
|
|
Loading…
Add table
Reference in a new issue