diff --git a/dailyjs/basic-call/.gitignore b/dailyjs/basic-call/.gitignore new file mode 100644 index 0000000..058f0ec --- /dev/null +++ b/dailyjs/basic-call/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +.pnp +.pnp.js + +# testing +/coverage + +# next.js +.next +out + +# production +build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel \ No newline at end of file diff --git a/dailyjs/basic-call/components/Room/Room.js b/dailyjs/basic-call/components/Room/Room.js index 4dd639a..13e8ce4 100644 --- a/dailyjs/basic-call/components/Room/Room.js +++ b/dailyjs/basic-call/components/Room/Room.js @@ -54,7 +54,6 @@ export const Room = ({ MainComponent = VideoGrid }) => { overflow: hidden; min-height: 0px; height: 100%; - padding: var(--spacing-xxxs); box-sizing: border-box; } `} diff --git a/dailyjs/basic-call/components/VideoGrid/VideoGrid.js b/dailyjs/basic-call/components/VideoGrid/VideoGrid.js index 1975d5f..744f185 100644 --- a/dailyjs/basic-call/components/VideoGrid/VideoGrid.js +++ b/dailyjs/basic-call/components/VideoGrid/VideoGrid.js @@ -19,7 +19,7 @@ import { useDeepCompareMemo } from 'use-deep-compare'; export const VideoGrid = React.memo( () => { const containerRef = useRef(); - const { participants } = useParticipants(); + const { participants, allParticipants } = useParticipants(); const [dimensions, setDimensions] = useState({ width: 1, height: 1, @@ -106,7 +106,7 @@ export const VideoGrid = React.memo( // Optimise performance by reducing video quality // when more participants join (if in SFU mode) - usePreferredLayer(); + usePreferredLayer(allParticipants); if (!participants.length) { return null; diff --git a/dailyjs/pagination/README.md b/dailyjs/pagination/README.md index 55a599d..733e3b7 100644 --- a/dailyjs/pagination/README.md +++ b/dailyjs/pagination/README.md @@ -25,7 +25,7 @@ yarn yarn workspace @dailyjs/live-streaming dev ``` -Note that this example uses a env `MANUAL_TRACK_SUBS=1` which will disable [automatic track management](https://docs.daily.co/reference#%EF%B8%8F-setsubscribetotracksautomatically). +Note: this example uses an additional env `MANUAL_TRACK_SUBS=1` that will disable [automatic track management](https://docs.daily.co/reference#%EF%B8%8F-setsubscribetotracksautomatically). ## How does this example work? diff --git a/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js b/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js index 9fcafcc..e7be39e 100644 --- a/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js +++ b/dailyjs/pagination/components/PaginatedVideoGrid/PaginatedVideoGrid.js @@ -1,5 +1,3 @@ -/* global rtcpeers */ - import React, { useCallback, useMemo, @@ -7,16 +5,19 @@ import React, { useRef, useState, } from 'react'; - +import { Button } from '@dailyjs/shared/components/Button'; import Tile from '@dailyjs/shared/components/Tile'; import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants'; import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider'; import { useTracks } from '@dailyjs/shared/contexts/TracksProvider'; import { useActiveSpeaker } from '@dailyjs/shared/hooks/useActiveSpeaker'; +import usePreferredLayer from '@dailyjs/shared/hooks/usePreferredLayer'; +import { ReactComponent as IconArrow } from '@dailyjs/shared/icons/raquo-md.svg'; import sortByKey from '@dailyjs/shared/lib/sortByKey'; - +import { debounce } from 'debounce'; import { useDeepCompareMemo } from 'use-deep-compare'; +// --- Constants const MIN_TILE_WIDTH = 280; const MAX_TILES_PER_PAGE = 12; @@ -31,11 +32,13 @@ export const PaginatedVideoGrid = () => { const { updateCamSubscriptions } = useTracks(); + // Memoized participant count (does not include screen shares) const displayableParticipantCount = useMemo( () => participantCount, [participantCount] ); + // Grid size (dictated by screen size) const [dimensions, setDimensions] = useState({ width: 1, height: 1, @@ -69,30 +72,33 @@ export const PaginatedVideoGrid = () => { }; }, []); + // Memoized reference to the max columns and rows possible given screen size const [maxColumns, maxRows] = useMemo(() => { const { width, height } = dimensions; - const columns = Math.max(1, Math.floor(width / MIN_TILE_WIDTH)); const widthPerTile = width / columns; const rows = Math.max(1, Math.floor(height / (widthPerTile * (9 / 16)))); - return [columns, rows]; }, [dimensions]); + // Memoized count of how many tiles can we show per page const pageSize = useMemo( () => Math.min(maxColumns * maxRows, maxTilesPerPage), [maxColumns, maxRows, maxTilesPerPage] ); + // Calc and set the total number of pages as participant count mutates useEffect(() => { setPages(Math.ceil(displayableParticipantCount / pageSize)); }, [pageSize, displayableParticipantCount]); + // Make sure we never see a blank page (if we're on the last page and people leave) useEffect(() => { if (page <= pages) return; setPage(pages); }, [page, pages]); + // Brutishly calculate the dimensions of each tile given the size of the grid const [tileWidth, tileHeight] = useMemo(() => { const { width, height } = dimensions; const n = Math.min(pageSize, displayableParticipantCount); @@ -119,6 +125,7 @@ export const PaginatedVideoGrid = () => { ); }, [dimensions, pageSize, displayableParticipantCount]); + // Memoized array of participants on the current page (those we can see) const visibleParticipants = useMemo( () => participants.length - page * pageSize > 0 @@ -129,6 +136,8 @@ export const PaginatedVideoGrid = () => { /** * 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; @@ -169,40 +178,26 @@ export const PaginatedVideoGrid = () => { }; }, [page, pageSize, participants, visibleParticipants]); + // Update subscriptions when array of subscribed or paused participants mutates + const debouncedUpdate = useCallback( + (subIds, pausedIds) => + debounce(() => updateCamSubscriptions(subIds, pausedIds), 90), + [updateCamSubscriptions] + ); + useEffect(() => { - updateCamSubscriptions( + debouncedUpdate( camSubscriptions?.subscribedIds, camSubscriptions?.pausedIds ); }, [ camSubscriptions?.subscribedIds, camSubscriptions?.pausedIds, - updateCamSubscriptions, + debouncedUpdate, ]); - /** - * Set bandwidth layer based on amount of visible participants - */ - useEffect(() => { - if (typeof rtcpeers === 'undefined' || rtcpeers?.getCurrentType() !== 'sfu') - return; - - const sfu = rtcpeers.soup; - const count = visibleParticipants.length; - - visibleParticipants.forEach(({ id }) => { - if (count < 5) { - // High quality video for calls with < 5 people per page - sfu.setPreferredLayerForTrack(id, 'cam-video', 2); - } else if (count < 10) { - // Medium quality video for calls with < 10 people per page - sfu.setPreferredLayerForTrack(id, 'cam-video', 1); - } else { - // Low quality video for calls with 10 or more people per page - sfu.setPreferredLayerForTrack(id, 'cam-video', 0); - } - }); - }, [visibleParticipants]); + // Set bandwidth layer based on amount of visible participants + usePreferredLayer(visibleParticipants); /** * Handle position updates based on active speaker events @@ -268,17 +263,23 @@ export const PaginatedVideoGrid = () => { return (