updated readme

This commit is contained in:
Jon 2021-07-06 15:56:09 +01:00
parent 94c66e9efb
commit 114f7009c8
10 changed files with 117 additions and 17 deletions

View File

@ -3,17 +3,18 @@ import Tile from '@dailyjs/shared/components/Tile';
import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants';
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
import usePreferredLayer from '@dailyjs/shared/hooks/usePreferredLayer';
import sortByKey from '@dailyjs/shared/lib/sortByKey';
import { useDeepCompareMemo } from 'use-deep-compare';
/**
* Basic unpaginated video tile grid
* Basic unpaginated video tile grid, scaled by aspect ratio
*
* Note: this component is designed to work with automated track subscriptions
* and is only suitable for small call sizes as it will show all participants
* and not paginate.
*
* Note: this grid does not show screenshares (just participant cams)
*
* Note: this grid does not sort participants
*/
export const VideoGrid = React.memo(
() => {
@ -24,6 +25,7 @@ export const VideoGrid = React.memo(
height: 1,
});
// Keep a reference to the width and height of the page, so we can repack
useEffect(() => {
let frame;
const handleResize = () => {
@ -44,11 +46,7 @@ export const VideoGrid = React.memo(
};
}, []);
const sortedParticipants = useMemo(
() => participants.sort((a, b) => sortByKey(a, b, 'position')),
[participants]
);
// Basic brute-force packing algo
const layout = useMemo(() => {
const aspectRatio = DEFAULT_ASPECT_RATIO;
const tileCount = participants.length || 0;
@ -92,9 +90,10 @@ export const VideoGrid = React.memo(
return bestLayout;
}, [dimensions, participants]);
// Memoize our tile list to avoid unnecassary re-renders
const tiles = useDeepCompareMemo(
() =>
sortedParticipants.map((p) => (
participants.map((p) => (
<Tile
participant={p}
key={p.id}
@ -102,9 +101,11 @@ export const VideoGrid = React.memo(
style={{ maxWidth: layout.width, maxHeight: layout.height }}
/>
)),
[layout, sortedParticipants]
[layout, participants]
);
// Optimise performance by reducing video quality
// when more participants join (if in SFU mode)
usePreferredLayer();
if (!participants.length) {

View File

@ -0,0 +1,45 @@
# Pagination, Sorting & Track Management
![Pagination](./image.png)
### Live example
**[See it in action here ➡️](https://dailyjs-pagination.vercel.app)**
---
## What does this demo do?
- Switches to [manual track subscriptions](https://docs.daily.co/reference#%EF%B8%8F-setsubscribetotracksautomatically) to pause / resume video tracks as they are paged in and out of view
- Introduces a new video grid component that manages pagination and sorts participant tiles based on their active speaker status
Please note: this demo is not currently mobile optimised
### Getting started
```
# set both DAILY_API_KEY and DAILY_DOMAIN
mv env.example .env.local
yarn
yarn workspace @dailyjs/live-streaming dev
```
## How does this example work?
When call sizes exceed a certain volume (~12 or more particpants) it's important to start optimising for both bandwidth and CPU. Using manual track subscriptions allows each client to specify which participants they want to receive video and/or audio from, reducing how much data needs to be downloaded as well as the number of connections our servers maintain (subsequently supporting increased participant counts.)
This demo introduces a new paginated grid component that subscribes to any tiles that are in view. Our subscription API allows for the subscribing, pausing, resuming and unsubscribing of tracks. The grid component will:
1. Subscribe to all participants on the call.
2. Pause participants video if they are not in view (i.e. on the current page.) Pausing is optimal over unsubscribing in this particular use case since unsubscribing a track results in a full teardown of the data stream. Re-subscribing to a track is perceivably slower than pausing and resuming.
3. Play / resume participant's video when they are on the current page.
4. Unsubscribe from a participant's video if they are not on an adjacent page (explained below.)
When you pause a track, you are keeping the connection for that track open and connected to the SFU but stopping any bytes from flowing across that connection. Therefore, this simple approach of pausing a track when it is offscreen rather than completely unsubscribing (and tearing down that connection) speeds up the process of showing/hiding participant videos while also cutting out the processing and bandwidth required for those tracks.
It is important to note that a subscription and the underlying connections it entails does still result in some overhead and this approach breaks down once you get to even larger calls (e.g. ~50 or more depending on device, bandwidth, geolocation etc). In those scenarios it is best to take advantage of both pausing and unsubscribing to maximize both quickly showing videos and minimizing connections/processing/cpu. This example showcases how to do this by subscribing to the current page's videos (all videos resumed) as well as the adjacent pages' videos (all videos paused) and unsubscribing to any other pages' videos. This has the affect of minimizing the overall number of subscriptions while still having any video which may be displayed shortly, subscribed with their connections ready to be resumed as soon as the user pages over.
## Deploy your own on Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/daily-co/clone-flow?repository-url=https%3A%2F%2Fgithub.com%2Fdaily-demos%2Fexamples.git&env=DAILY_DOMAIN%2CDAILY_API_KEY&envDescription=Your%20Daily%20domain%20and%20API%20key%20can%20be%20found%20on%20your%20account%20dashboard&envLink=https%3A%2F%2Fdashboard.daily.co&project-name=daily-examples&repo-name=daily-examples)

View File

@ -0,0 +1,13 @@
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')([
'@dailyjs/shared',
'@dailyjs/basic-call',
]);
const packageJson = require('./package.json');
module.exports = withPlugins([withTM], {
env: {
PROJECT_TITLE: packageJson.description,
},
});

View File

@ -0,0 +1,25 @@
{
"name": "@dailyjs/pagination",
"description": "Basic Call + Pagination",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@dailyjs/shared": "*",
"@dailyjs/basic-call": "*",
"next": "^11.0.0",
"pluralize": "^8.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"babel-plugin-module-resolver": "^4.1.0",
"next-compose-plugins": "^2.2.1",
"next-transpile-modules": "^8.0.0"
}
}

View File

@ -0,0 +1,3 @@
import App from '@dailyjs/basic-call/pages/_app';
export default App;

View File

@ -0,0 +1 @@
../../basic-call/pages/api

View File

@ -0,0 +1,16 @@
import Index from '@dailyjs/basic-call/pages';
import getDemoProps from '@dailyjs/shared/lib/demoProps';
export async function getStaticProps() {
const defaultProps = getDemoProps();
return {
props: {
...defaultProps,
forceFetchToken: true,
forceOwner: true,
},
};
}
export default Index;

1
dailyjs/pagination/public Symbolic link
View File

@ -0,0 +1 @@
../basic-call/public

View File

@ -13,8 +13,9 @@ import { sortByKey } from '../lib/sortByKey';
import { useCallState } from './CallProvider';
import {
ACTIVE_SPEAKER,
initialParticipantsState,
isLocalId,
ACTIVE_SPEAKER,
PARTICIPANT_JOINED,
PARTICIPANT_LEFT,
PARTICIPANT_UPDATED,
@ -131,6 +132,7 @@ export const ParticipantsProvider = ({ children }) => {
};
const swapParticipantPosition = (id1, id2) => {
if (id1 === id2 || !id1 || !id2 || isLocalId(id1) || isLocalId(id2)) return;
dispatch({
type: SWAP_POSITION,
id1,

View File

@ -58,13 +58,6 @@ function isScreenId(id) {
// ---Helpers ---
function getMaxPosition(participants) {
return Math.max(
1,
Math.max(...Object.values(participants).map(({ position }) => position))
);
}
function getNewParticipant(participant) {
const id = getId(participant);