Group calls: Make renderVideoFrame generate less garbage
This commit is contained in:
parent
fed84be0b6
commit
683823a114
6 changed files with 30 additions and 21 deletions
|
@ -12,12 +12,12 @@ import { FRAME_BUFFER_SIZE } from './constants';
|
||||||
* of allocating one per participant. Be careful when using this buffer elsewhere, as it
|
* of allocating one per participant. Be careful when using this buffer elsewhere, as it
|
||||||
* is not cleaned up and may hold stale data.
|
* is not cleaned up and may hold stale data.
|
||||||
*/
|
*/
|
||||||
export function useGetCallingFrameBuffer(): () => ArrayBuffer {
|
export function useGetCallingFrameBuffer(): () => Buffer {
|
||||||
const ref = useRef<ArrayBuffer | null>(null);
|
const ref = useRef<Buffer | null>(null);
|
||||||
|
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
if (!ref.current) {
|
if (!ref.current) {
|
||||||
ref.current = new ArrayBuffer(FRAME_BUFFER_SIZE);
|
ref.current = Buffer.alloc(FRAME_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
return ref.current;
|
return ref.current;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -35,7 +35,7 @@ const allRemoteParticipants = times(MAX_PARTICIPANTS).map(index => ({
|
||||||
const story = storiesOf('Components/GroupCallOverflowArea', module);
|
const story = storiesOf('Components/GroupCallOverflowArea', module);
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
getFrameBuffer: memoize(() => new ArrayBuffer(FRAME_BUFFER_SIZE)),
|
getFrameBuffer: memoize(() => Buffer.alloc(FRAME_BUFFER_SIZE)),
|
||||||
getGroupCallVideoFrameSource: fakeGetGroupCallVideoFrameSource,
|
getGroupCallVideoFrameSource: fakeGetGroupCallVideoFrameSource,
|
||||||
i18n,
|
i18n,
|
||||||
onParticipantVisibilityChanged: action('onParticipantVisibilityChanged'),
|
onParticipantVisibilityChanged: action('onParticipantVisibilityChanged'),
|
||||||
|
|
|
@ -16,7 +16,7 @@ const OVERFLOW_SCROLL_BUTTON_RATIO = 0.75;
|
||||||
export const OVERFLOW_PARTICIPANT_WIDTH = 140;
|
export const OVERFLOW_PARTICIPANT_WIDTH = 140;
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
getFrameBuffer: () => ArrayBuffer;
|
getFrameBuffer: () => Buffer;
|
||||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
onParticipantVisibilityChanged: (
|
onParticipantVisibilityChanged: (
|
||||||
|
|
|
@ -26,7 +26,7 @@ type OverridePropsType =
|
||||||
width: number;
|
width: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFrameBuffer = memoize(() => new ArrayBuffer(FRAME_BUFFER_SIZE));
|
const getFrameBuffer = memoize(() => Buffer.alloc(FRAME_BUFFER_SIZE));
|
||||||
|
|
||||||
const createProps = (
|
const createProps = (
|
||||||
overrideProps: OverridePropsType,
|
overrideProps: OverridePropsType,
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { MAX_FRAME_SIZE } from '../calling/constants';
|
||||||
const MAX_TIME_TO_SHOW_STALE_VIDEO_FRAMES = 5000;
|
const MAX_TIME_TO_SHOW_STALE_VIDEO_FRAMES = 5000;
|
||||||
|
|
||||||
type BasePropsType = {
|
type BasePropsType = {
|
||||||
getFrameBuffer: () => ArrayBuffer;
|
getFrameBuffer: () => Buffer;
|
||||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
onVisibilityChanged?: (demuxId: number, isVisible: boolean) => unknown;
|
onVisibilityChanged?: (demuxId: number, isVisible: boolean) => unknown;
|
||||||
|
@ -93,6 +93,7 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
||||||
const lastReceivedVideoAt = useRef(-Infinity);
|
const lastReceivedVideoAt = useRef(-Infinity);
|
||||||
const remoteVideoRef = useRef<HTMLCanvasElement | null>(null);
|
const remoteVideoRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
const canvasContextRef = useRef<CanvasRenderingContext2D | null>(null);
|
const canvasContextRef = useRef<CanvasRenderingContext2D | null>(null);
|
||||||
|
const imageDataRef = useRef<ImageData | null>(null);
|
||||||
|
|
||||||
const [intersectionRef, intersectionObserverEntry] =
|
const [intersectionRef, intersectionObserverEntry] =
|
||||||
useIntersectionObserver();
|
useIntersectionObserver();
|
||||||
|
@ -134,9 +135,7 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
||||||
// for other participants, or pixel data from a previous frame. That's why we
|
// for other participants, or pixel data from a previous frame. That's why we
|
||||||
// return early and use the `frameWidth` and `frameHeight`.
|
// return early and use the `frameWidth` and `frameHeight`.
|
||||||
const frameBuffer = getFrameBuffer();
|
const frameBuffer = getFrameBuffer();
|
||||||
const frameDimensions = videoFrameSource.receiveVideoFrame(
|
const frameDimensions = videoFrameSource.receiveVideoFrame(frameBuffer);
|
||||||
Buffer.from(frameBuffer)
|
|
||||||
);
|
|
||||||
if (!frameDimensions) {
|
if (!frameDimensions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -154,15 +153,16 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
||||||
canvasEl.width = frameWidth;
|
canvasEl.width = frameWidth;
|
||||||
canvasEl.height = frameHeight;
|
canvasEl.height = frameHeight;
|
||||||
|
|
||||||
canvasContext.putImageData(
|
let imageData = imageDataRef.current;
|
||||||
new ImageData(
|
if (
|
||||||
new Uint8ClampedArray(frameBuffer, 0, frameWidth * frameHeight * 4),
|
imageData?.width !== frameWidth ||
|
||||||
frameWidth,
|
imageData?.height !== frameHeight
|
||||||
frameHeight
|
) {
|
||||||
),
|
imageData = new ImageData(frameWidth, frameHeight);
|
||||||
0,
|
imageDataRef.current = imageData;
|
||||||
0
|
}
|
||||||
);
|
imageData.data.set(frameBuffer.subarray(0, frameWidth * frameHeight * 4));
|
||||||
|
canvasContext.putImageData(imageData, 0, 0);
|
||||||
|
|
||||||
lastReceivedVideoAt.current = Date.now();
|
lastReceivedVideoAt.current = Date.now();
|
||||||
|
|
||||||
|
|
|
@ -6965,9 +6965,10 @@
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/calling/useGetCallingFrameBuffer.ts",
|
"path": "ts/calling/useGetCallingFrameBuffer.ts",
|
||||||
"line": " const ref = useRef<ArrayBuffer | null>(null);",
|
"line": " const ref = useRef<Buffer | null>(null);",
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2021-07-30T16:57:33.618Z"
|
"updated": "2021-12-10T23:24:03.829Z",
|
||||||
|
"reasonDetail": "Doesn't touch the DOM."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"rule": "jQuery-load(",
|
"rule": "jQuery-load(",
|
||||||
|
@ -7331,6 +7332,14 @@
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2021-07-30T16:57:33.618Z"
|
"updated": "2021-07-30T16:57:33.618Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/GroupCallRemoteParticipant.tsx",
|
||||||
|
"line": " const imageDataRef = useRef<ImageData | null>(null);",
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2021-12-10T23:24:31.237Z",
|
||||||
|
"reasonDetail": "Doesn't touch the DOM."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/Inbox.tsx",
|
"path": "ts/components/Inbox.tsx",
|
||||||
|
|
Loading…
Add table
Reference in a new issue