added bandwidth controls to ParticipantProvider
This commit is contained in:
parent
dc2dc63a98
commit
c322312343
|
|
@ -59,6 +59,7 @@ export const SpeakerTile = ({ participant, screenRef }) => {
|
||||||
participant={participant}
|
participant={participant}
|
||||||
style={style}
|
style={style}
|
||||||
videoFit={videoFit}
|
videoFit={videoFit}
|
||||||
|
showActiveSpeaker={false}
|
||||||
onVideoResize={handleNativeAspectRatio}
|
onVideoResize={handleNativeAspectRatio}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import React, { useState, useMemo, useEffect, useRef } from 'react';
|
||||||
import Tile from '@dailyjs/shared/components/Tile';
|
import Tile from '@dailyjs/shared/components/Tile';
|
||||||
import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants';
|
import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants';
|
||||||
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
||||||
import usePreferredLayer from '@dailyjs/shared/hooks/usePreferredLayer';
|
import usePreferredLayerByCount from '@dailyjs/shared/hooks/usePreferredLayerByCount';
|
||||||
import { useDeepCompareMemo } from 'use-deep-compare';
|
import { useDeepCompareMemo } from 'use-deep-compare';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -106,7 +106,7 @@ export const VideoGrid = React.memo(
|
||||||
|
|
||||||
// Optimise performance by reducing video quality
|
// Optimise performance by reducing video quality
|
||||||
// when more participants join (if in SFU mode)
|
// when more participants join (if in SFU mode)
|
||||||
usePreferredLayer(allParticipants);
|
usePreferredLayerByCount(allParticipants);
|
||||||
|
|
||||||
if (!participants.length) {
|
if (!participants.length) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants';
|
||||||
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
||||||
import { useActiveSpeaker } from '@dailyjs/shared/hooks/useActiveSpeaker';
|
import { useActiveSpeaker } from '@dailyjs/shared/hooks/useActiveSpeaker';
|
||||||
import { useCamSubscriptions } from '@dailyjs/shared/hooks/useCamSubscriptions';
|
import { useCamSubscriptions } from '@dailyjs/shared/hooks/useCamSubscriptions';
|
||||||
import usePreferredLayer from '@dailyjs/shared/hooks/usePreferredLayer';
|
import usePreferredLayerByCount from '@dailyjs/shared/hooks/usePreferredLayerByCount';
|
||||||
import { ReactComponent as IconArrow } from '@dailyjs/shared/icons/raquo-md.svg';
|
import { ReactComponent as IconArrow } from '@dailyjs/shared/icons/raquo-md.svg';
|
||||||
import sortByKey from '@dailyjs/shared/lib/sortByKey';
|
import sortByKey from '@dailyjs/shared/lib/sortByKey';
|
||||||
import { useDeepCompareMemo } from 'use-deep-compare';
|
import { useDeepCompareMemo } from 'use-deep-compare';
|
||||||
|
|
@ -181,7 +181,7 @@ export const PaginatedVideoGrid = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set bandwidth layer based on amount of visible participants
|
// Set bandwidth layer based on amount of visible participants
|
||||||
usePreferredLayer(visibleParticipants);
|
usePreferredLayerByCount(visibleParticipants);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle position updates based on active speaker events
|
* Handle position updates based on active speaker events
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export const Tile = React.memo(
|
||||||
mirrored = true,
|
mirrored = true,
|
||||||
showName = true,
|
showName = true,
|
||||||
showAvatar = true,
|
showAvatar = true,
|
||||||
|
showActiveSpeaker = true,
|
||||||
aspectRatio = DEFAULT_ASPECT_RATIO,
|
aspectRatio = DEFAULT_ASPECT_RATIO,
|
||||||
onVideoResize,
|
onVideoResize,
|
||||||
videoFit = 'contain',
|
videoFit = 'contain',
|
||||||
|
|
@ -22,6 +23,8 @@ export const Tile = React.memo(
|
||||||
const videoEl = useRef(null);
|
const videoEl = useRef(null);
|
||||||
const [tileAspectRatio, setTileAspectRatio] = useState(aspectRatio);
|
const [tileAspectRatio, setTileAspectRatio] = useState(aspectRatio);
|
||||||
|
|
||||||
|
const [layer, setLayer] = useState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add optional event listener for resize event so the parent component
|
* Add optional event listener for resize event so the parent component
|
||||||
* can know the video's native aspect ratio.
|
* can know the video's native aspect ratio.
|
||||||
|
|
@ -52,10 +55,26 @@ export const Tile = React.memo(
|
||||||
setTileAspectRatio(aspectRatio);
|
setTileAspectRatio(aspectRatio);
|
||||||
}, [aspectRatio, tileAspectRatio]);
|
}, [aspectRatio, tileAspectRatio]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
typeof rtcpeers === 'undefined' ||
|
||||||
|
rtcpeers?.getCurrentType() !== 'sfu'
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const i = setInterval(() => {
|
||||||
|
setLayer(
|
||||||
|
rtcpeers.sfu.consumers[`${participant.id}/cam-video`]?._preferredLayer
|
||||||
|
);
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
return () => clearInterval(i);
|
||||||
|
}, [participant]);
|
||||||
|
|
||||||
const cx = classNames('tile', videoFit, {
|
const cx = classNames('tile', videoFit, {
|
||||||
mirrored,
|
mirrored,
|
||||||
avatar: showAvatar && !videoTrack,
|
avatar: showAvatar && !videoTrack,
|
||||||
active: participant.isActiveSpeaker,
|
active: showActiveSpeaker && participant.isActiveSpeaker,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -64,7 +83,7 @@ export const Tile = React.memo(
|
||||||
{showName && (
|
{showName && (
|
||||||
<div className="name">
|
<div className="name">
|
||||||
{participant.isMicMuted && <IconMicMute />}
|
{participant.isMicMuted && <IconMicMute />}
|
||||||
{participant.name}
|
{participant.name} - {layer}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{videoTrack ? (
|
{videoTrack ? (
|
||||||
|
|
@ -92,8 +111,17 @@ export const Tile = React.memo(
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile.active {
|
.tile.active:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
left: 0px;
|
||||||
|
bottom: 0px;
|
||||||
border: 2px solid var(--primary-default);
|
border: 2px solid var(--primary-default);
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile .name {
|
.tile .name {
|
||||||
|
|
@ -104,10 +132,11 @@ export const Tile = React.memo(
|
||||||
left: 0px;
|
left: 0px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
font-size: 0.875rem;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: var(--weight-medium);
|
font-weight: var(--weight-medium);
|
||||||
padding: var(--spacing-xxs);
|
padding: var(--spacing-xxs);
|
||||||
text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.35);
|
text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.45);
|
||||||
gap: var(--spacing-xxs);
|
gap: var(--spacing-xxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,6 +188,7 @@ Tile.propTypes = {
|
||||||
aspectRatio: PropTypes.number,
|
aspectRatio: PropTypes.number,
|
||||||
onVideoResize: PropTypes.func,
|
onVideoResize: PropTypes.func,
|
||||||
videoFit: PropTypes.string,
|
videoFit: PropTypes.string,
|
||||||
|
showActiveSpeaker: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Tile;
|
export default Tile;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* global rtcpeers */
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
createContext,
|
createContext,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
|
@ -7,11 +9,16 @@ import React, {
|
||||||
useState,
|
useState,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import {
|
||||||
|
useUIState,
|
||||||
|
VIEW_MODE_SPEAKER,
|
||||||
|
} from '@dailyjs/shared/contexts/UIStateProvider';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { sortByKey } from '../lib/sortByKey';
|
import { sortByKey } from '../lib/sortByKey';
|
||||||
|
|
||||||
import { useCallState } from './CallProvider';
|
import { useCallState } from './CallProvider';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
initialParticipantsState,
|
initialParticipantsState,
|
||||||
isLocalId,
|
isLocalId,
|
||||||
|
|
@ -31,6 +38,7 @@ export const ParticipantsProvider = ({ children }) => {
|
||||||
participantsReducer,
|
participantsReducer,
|
||||||
initialParticipantsState
|
initialParticipantsState
|
||||||
);
|
);
|
||||||
|
const { viewMode } = useUIState();
|
||||||
const [participantMarkedForRemoval, setParticipantMarkedForRemoval] =
|
const [participantMarkedForRemoval, setParticipantMarkedForRemoval] =
|
||||||
useState(null);
|
useState(null);
|
||||||
|
|
||||||
|
|
@ -47,6 +55,14 @@ export const ParticipantsProvider = ({ children }) => {
|
||||||
*/
|
*/
|
||||||
const participants = useMemo(() => state.participants, [state.participants]);
|
const participants = useMemo(() => state.participants, [state.participants]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of participant IDs
|
||||||
|
*/
|
||||||
|
const participantIds = useMemo(
|
||||||
|
() => participants.map((p) => p.id).join(','),
|
||||||
|
[participants]
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of participants, who are not a shared screen
|
* The number of participants, who are not a shared screen
|
||||||
* (technically a shared screen counts as a participant, but we shouldn't tell humans)
|
* (technically a shared screen counts as a participant, but we shouldn't tell humans)
|
||||||
|
|
@ -218,6 +234,40 @@ export const ParticipantsProvider = ({ children }) => {
|
||||||
);
|
);
|
||||||
}, [callObject, handleNewParticipantsState]);
|
}, [callObject, handleNewParticipantsState]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust video quality from the 3 simulcast layers based
|
||||||
|
* on active speaker status. Note: this currently uses
|
||||||
|
* undocumented internal methods (we'll be adding support
|
||||||
|
* for this into our API soon!)
|
||||||
|
*/
|
||||||
|
const setBandWidthControls = useCallback(() => {
|
||||||
|
if (typeof rtcpeers === 'undefined') return;
|
||||||
|
const sfu = rtcpeers?.soup;
|
||||||
|
const isSFU = rtcpeers?.currentlyPreferred?.typeName?.() === 'sfu';
|
||||||
|
if (!isSFU) return;
|
||||||
|
|
||||||
|
const ids = participantIds.split(',');
|
||||||
|
|
||||||
|
ids.forEach((id) => {
|
||||||
|
if (isLocalId(id)) return;
|
||||||
|
|
||||||
|
// Speaker view settings based on speaker status or pinned user
|
||||||
|
if (viewMode === VIEW_MODE_SPEAKER) {
|
||||||
|
if (currentSpeaker?.id === id) {
|
||||||
|
sfu.setPreferredLayerForTrack(id, 'cam-video', 2);
|
||||||
|
} else {
|
||||||
|
sfu.setPreferredLayerForTrack(id, 'cam-video', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: grid view settings are handled by the grid view component
|
||||||
|
});
|
||||||
|
}, [currentSpeaker?.id, participantIds, viewMode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setBandWidthControls();
|
||||||
|
}, [setBandWidthControls]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!callObject) return false;
|
if (!callObject) return false;
|
||||||
const handleActiveSpeakerChange = ({ activeSpeaker }) => {
|
const handleActiveSpeakerChange = ({ activeSpeaker }) => {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { useEffect } from 'react';
|
||||||
*
|
*
|
||||||
* Note: this will have no effect when not in SFU mode
|
* Note: this will have no effect when not in SFU mode
|
||||||
*/
|
*/
|
||||||
export const usePreferredLayer = (participants) => {
|
export const usePreferredLayerByCount = (participants) => {
|
||||||
/**
|
/**
|
||||||
* Set bandwidth layer based on amount of visible participants
|
* Set bandwidth layer based on amount of visible participants
|
||||||
*/
|
*/
|
||||||
|
|
@ -40,4 +40,4 @@ export const usePreferredLayer = (participants) => {
|
||||||
}, [participants]);
|
}, [participants]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default usePreferredLayer;
|
export default usePreferredLayerByCount;
|
||||||
Loading…
Reference in New Issue