diff --git a/dailyjs/basic-call/components/VideoGrid/VideoGrid.js b/dailyjs/basic-call/components/VideoGrid/VideoGrid.js
index 97a2b3c..7f66798 100644
--- a/dailyjs/basic-call/components/VideoGrid/VideoGrid.js
+++ b/dailyjs/basic-call/components/VideoGrid/VideoGrid.js
@@ -2,7 +2,6 @@ import React, { useState, useMemo, useEffect, useRef } from 'react';
import Tile from '@dailyjs/shared/components/Tile';
import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants';
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
-import usePreferredLayerByCount from '@dailyjs/shared/hooks/usePreferredLayerByCount';
import { useDeepCompareMemo } from 'use-deep-compare';
/**
@@ -19,7 +18,7 @@ import { useDeepCompareMemo } from 'use-deep-compare';
export const VideoGrid = React.memo(
() => {
const containerRef = useRef();
- const { participants, allParticipants } = useParticipants();
+ const { participants } = useParticipants();
const [dimensions, setDimensions] = useState({
width: 1,
height: 1,
@@ -104,10 +103,6 @@ export const VideoGrid = React.memo(
[layout, participants]
);
- // Optimise performance by reducing video quality
- // when more participants join (if in SFU mode)
- usePreferredLayerByCount(allParticipants);
-
if (!participants.length) {
return null;
}
diff --git a/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js b/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js
index 80648e0..72ea368 100644
--- a/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js
+++ b/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js
@@ -8,10 +8,11 @@ import React, {
import { Button } from '@dailyjs/shared/components/Button';
import Tile from '@dailyjs/shared/components/Tile';
import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants';
+import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
+import { isLocalId } from '@dailyjs/shared/contexts/participantsState';
import { useActiveSpeaker } from '@dailyjs/shared/hooks/useActiveSpeaker';
import { useCamSubscriptions } from '@dailyjs/shared/hooks/useCamSubscriptions';
-import usePreferredLayerByCount from '@dailyjs/shared/hooks/usePreferredLayerByCount';
import { ReactComponent as IconArrow } from '@dailyjs/shared/icons/raquo-md.svg';
import sortByKey from '@dailyjs/shared/lib/sortByKey';
import { useDeepCompareMemo } from 'use-deep-compare';
@@ -21,6 +22,7 @@ const MIN_TILE_WIDTH = 280;
const MAX_TILES_PER_PAGE = 12;
export const PaginatedVideoGrid = () => {
+ const { callObject } = useCallState();
const {
activeParticipant,
participantCount,
@@ -46,6 +48,8 @@ export const PaginatedVideoGrid = () => {
const gridRef = useRef(null);
+ // -- Layout / UI
+
// Update width and height of grid when window is resized
useEffect(() => {
let frame;
@@ -131,26 +135,27 @@ export const PaginatedVideoGrid = () => {
[page, pageSize, participants]
);
+ // -- Track subscriptions
+
/**
* Play / pause tracks based on pagination
* Note: we pause adjacent page tracks and unsubscribe from everything else
- * Please refer to project README for more information
*/
const camSubscriptions = useMemo(() => {
const maxSubs = 3 * pageSize;
- // Determine participant ids to subscribe to, based on page.
- let subscribedIds = [];
+ // Determine participant ids to subscribe to or stage, based on page
+ let renderedOrBufferedIds = [];
switch (page) {
// First page
case 1:
- subscribedIds = participants
+ renderedOrBufferedIds = participants
.slice(0, Math.min(maxSubs, 2 * pageSize))
.map((p) => p.id);
break;
// Last page
case Math.ceil(participants.length / pageSize):
- subscribedIds = participants
+ renderedOrBufferedIds = participants
.slice(-Math.min(maxSubs, 2 * pageSize))
.map((p) => p.id);
break;
@@ -160,18 +165,29 @@ export const PaginatedVideoGrid = () => {
const buffer = (maxSubs - pageSize) / 2;
const min = (page - 1) * pageSize - buffer;
const max = page * pageSize + buffer;
- subscribedIds = participants.slice(min, max).map((p) => p.id);
+ renderedOrBufferedIds = participants.slice(min, max).map((p) => p.id);
}
break;
}
- // Determine subscribed, but invisible (= paused) video tracks
- const invisibleSubscribedIds = subscribedIds.filter(
- (id) => id !== 'local' && !visibleParticipants.some((vp) => vp.id === id)
- );
+ const subscribedIds = [];
+ const stagedIds = [];
+
+ // Decide whether to subscribe to or stage participants'
+ // track based on isibility
+ renderedOrBufferedIds.forEach((id) => {
+ if (id !== isLocalId()) {
+ if (visibleParticipants.some((vp) => vp.id === id)) {
+ subscribedIds.push(id);
+ } else {
+ stagedIds.push(id);
+ }
+ }
+ });
+
return {
- subscribedIds: subscribedIds.filter((id) => id !== 'local'),
- pausedIds: invisibleSubscribedIds,
+ subscribedIds,
+ stagedIds,
};
}, [page, pageSize, participants, visibleParticipants]);
@@ -180,8 +196,36 @@ export const PaginatedVideoGrid = () => {
camSubscriptions?.pausedIds
);
- // Set bandwidth layer based on amount of visible participants
- usePreferredLayerByCount(visibleParticipants);
+ /**
+ * Set bandwidth layer based on amount of visible participants
+ */
+ useEffect(() => {
+ if (!(callObject && callObject.meetingState() === 'joined-meeting')) return;
+ const count = visibleParticipants.length;
+
+ let layer;
+ if (count < 5) {
+ // highest quality layer
+ layer = 2;
+ } else if (count < 10) {
+ // mid quality layer
+ layer = 1;
+ } else {
+ // low qualtiy layer
+ layer = 0;
+ }
+
+ const receiveSettings = visibleParticipants.reduce(
+ (settings, participant) => {
+ if (isLocalId(participant.id)) return settings;
+ return { ...settings, [participant.id]: { video: { layer } } };
+ },
+ {}
+ );
+ callObject.updateReceiveSettings(receiveSettings);
+ }, [visibleParticipants, callObject]);
+
+ // -- Active speaker
/**
* Handle position updates based on active speaker events
@@ -255,7 +299,9 @@ export const PaginatedVideoGrid = () => {
>