in app/components/RemoteVideoGroup.tsx [26:156]
export default function RemoteVideoGroup(props: Props) {
const { viewMode, isContentShareEnabled } = props;
const chime: ChimeSdkWrapper | null = useContext(getChimeContext());
const [visibleIndices, setVisibleIndices] = useState<{
[index: string]: { boundAttendeeId: string };
}>({});
const raisedHandAttendees = useRaisedHandAttendees();
const roster = useRoster();
const videoElements: HTMLVideoElement[] = [];
const tiles: { [index: number]: number } = {};
const acquireVideoIndex = (tileId: number): number => {
for (let index = 0; index < MAX_REMOTE_VIDEOS; index += 1) {
if (tiles[index] === tileId) {
return index;
}
}
for (let index = 0; index < MAX_REMOTE_VIDEOS; index += 1) {
if (!(index in tiles)) {
tiles[index] = tileId;
return index;
}
}
throw new Error('no tiles are available');
};
const releaseVideoIndex = (tileId: number): number => {
for (let index = 0; index < MAX_REMOTE_VIDEOS; index += 1) {
if (tiles[index] === tileId) {
delete tiles[index];
return index;
}
}
return -1;
};
const numberOfVisibleIndices = Object.keys(visibleIndices).reduce<number>(
(result: number, key: string) => result + (visibleIndices[key] ? 1 : 0),
0
);
useEffect(() => {
chime?.audioVideo?.addObserver({
videoTileDidUpdate: (tileState: VideoTileState): void => {
if (
!tileState.boundAttendeeId ||
tileState.localTile ||
tileState.isContent ||
!tileState.tileId
) {
return;
}
const index = acquireVideoIndex(tileState.tileId);
chime?.audioVideo?.bindVideoElement(
tileState.tileId,
videoElements[index]
);
setVisibleIndices(previousVisibleIndices => ({
...previousVisibleIndices,
[index]: {
boundAttendeeId: tileState.boundAttendeeId
}
}));
},
videoTileWasRemoved: (tileId: number): void => {
const index = releaseVideoIndex(tileId);
setVisibleIndices(previousVisibleIndices => ({
...previousVisibleIndices,
[index]: null
}));
}
});
}, []);
const getSize = (): Size => {
if (numberOfVisibleIndices >= 10) {
return Size.Small;
}
if (numberOfVisibleIndices >= 5) {
return Size.Medium;
}
return Size.Large;
};
return (
<div
className={cx(
'remoteVideoGroup',
`remoteVideoGroup-${numberOfVisibleIndices}`,
{
roomMode: viewMode === ViewMode.Room,
screenShareMode: viewMode === ViewMode.ScreenShare,
isContentShareEnabled
}
)}
>
{numberOfVisibleIndices === 0 && (
<div className={cx('instruction')}>
<FormattedMessage id="RemoteVideoGroup.noVideo" />
</div>
)}
{Array.from(Array(MAX_REMOTE_VIDEOS).keys()).map((key, index) => {
const visibleIndex = visibleIndices[index];
const attendeeId = visibleIndex ? visibleIndex.boundAttendeeId : null;
const raisedHand = raisedHandAttendees
? raisedHandAttendees.has(attendeeId)
: false;
const activeSpeaker = visibleIndex
? roster[visibleIndex.boundAttendeeId]?.active
: false;
return (
<RemoteVideo
key={key}
viewMode={viewMode}
enabled={!!visibleIndex}
videoElementRef={useCallback((element: HTMLVideoElement | null) => {
if (element) {
videoElements[index] = element;
}
}, [])}
size={getSize()}
attendeeId={attendeeId}
raisedHand={raisedHand}
activeSpeaker={activeSpeaker}
isContentShareEnabled={isContentShareEnabled}
/>
);
})}
</div>
);
}