mirror of
https://github.com/cloudflare/cloudflare-docs.git
synced 2026-01-11 20:06:58 +00:00
docs(realtimekit): add guide for displaying active speakers (#27465)
This commit is contained in:
parent
aa6f6daf48
commit
c060eaa47c
12 changed files with 604 additions and 258 deletions
|
|
@ -3,7 +3,7 @@ pcx_content_type: how-to
|
|||
title: AI (Transcription and Summary)
|
||||
slug: realtime/realtimekit/core/ai
|
||||
sidebar:
|
||||
order: 10
|
||||
order: 11
|
||||
---
|
||||
|
||||
RealtimeKit provides AI-powered features to enhance your meetings, including real-time transcription and automatic meeting summaries. These features help you capture important discussions and generate concise overviews of your meetings.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: content
|
|||
title: Breakout Rooms
|
||||
slug: realtime/realtimekit/core/breakout-rooms
|
||||
sidebar:
|
||||
order: 15
|
||||
order: 16
|
||||
---
|
||||
|
||||
import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro";
|
||||
|
|
@ -17,15 +17,24 @@ import { Render } from "~/components";
|
|||
Before creating breakout rooms, validate the permissions of the current participant to ensure that the participant has the required permissions to create breakout rooms. Incorrect permissions can lead to errors being thrown.
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
<Render file="realtimekit/web/breakout-room-permission-check" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-permission-check"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
<Render file="realtimekit/web/breakout-room-permission-check" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-permission-check"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
<Render file="realtimekit/web/breakout-room-permission-check" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-permission-check"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
### Create breakout rooms
|
||||
|
|
@ -33,15 +42,24 @@ Before creating breakout rooms, validate the permissions of the current particip
|
|||
<RTKSDKSelector />
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
<Render file="realtimekit/web/breakout-room-creation-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-creation-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
<Render file="realtimekit/web/breakout-room-creation-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-creation-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
<Render file="realtimekit/web/breakout-room-creation-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-creation-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
### Retrieve list of breakout rooms and their participants
|
||||
|
|
@ -49,15 +67,24 @@ Before creating breakout rooms, validate the permissions of the current particip
|
|||
If there are more than one host in the room creating breakouts, you can retrieve consolidated list of breakout rooms using the following API.
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
<Render file="realtimekit/web/breakout-room-fetch-meetings-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-fetch-meetings-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
<Render file="realtimekit/web/breakout-room-fetch-meetings-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-fetch-meetings-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
<Render file="realtimekit/web/breakout-room-creation-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-creation-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
### Move participants to breakout rooms
|
||||
|
|
@ -65,15 +92,24 @@ If there are more than one host in the room creating breakouts, you can retrieve
|
|||
Once you have created breakout rooms, assign participants to the rooms.
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
<Render file="realtimekit/web/breakout-room-move-participant-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-move-participant-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
<Render file="realtimekit/web/breakout-room-move-participant-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-move-participant-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
<Render file="realtimekit/web/breakout-room-move-participant-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-move-participant-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
### Move participants, with a specific preset, to breakout rooms
|
||||
|
|
@ -81,15 +117,24 @@ Once you have created breakout rooms, assign participants to the rooms.
|
|||
Once you have created breakout rooms, assign participants to the rooms.
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
<Render file="realtimekit/web/breakout-room-move-preset-participant-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-move-preset-participant-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
<Render file="realtimekit/web/breakout-room-move-preset-participant-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-move-preset-participant-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
<Render file="realtimekit/web/breakout-room-move-preset-participant-sdk-api" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-move-preset-participant-sdk-api"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
### Move local participant to breakout room
|
||||
|
|
@ -101,15 +146,24 @@ To move the local participant to a different breakout room or back to the parent
|
|||
If a participant has been moved to a breakout room, the `changingMeeting` event is triggered, followed by the `meetingChanged` event. These events are also triggered when a participant switches between the main meeting and breakout rooms. Participants will autojoin the breakout room if they are assigned to it. You won't have to join meeting explicitly.
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
<Render file="realtimekit/web/breakout-room-handle-breakout-events" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-handle-breakout-events"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
<Render file="realtimekit/web/breakout-room-handle-breakout-events" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-handle-breakout-events"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
<Render file="realtimekit/web/breakout-room-handle-breakout-events" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-handle-breakout-events"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
### Close breakout rooms
|
||||
|
|
@ -117,15 +171,24 @@ If a participant has been moved to a breakout room, the `changingMeeting` event
|
|||
You can close/delete the breakout rooms. This will force participants in those meetings to come to the main room.
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
<Render file="realtimekit/web/breakout-room-close-breakout" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-close-breakout"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
<Render file="realtimekit/web/breakout-room-close-breakout" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-close-breakout"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
<Render file="realtimekit/web/breakout-room-close-breakout" product="realtime" />
|
||||
<Render
|
||||
file="realtimekit/web/breakout-room-close-breakout"
|
||||
product="realtime"
|
||||
/>
|
||||
</RTKCodeSnippet>
|
||||
|
||||
## Next steps
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: navigation
|
|||
title: Chat
|
||||
slug: realtime/realtimekit/core/chat
|
||||
sidebar:
|
||||
order: 11
|
||||
order: 12
|
||||
---
|
||||
|
||||
import { Tabs, TabItem } from "~/components";
|
||||
|
|
@ -599,7 +599,7 @@ There is a method in `meeting.chat` to send a message of each type.
|
|||
To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room.
|
||||
|
||||
```jsx
|
||||
const message = 'Is this the real life?';
|
||||
const message = "Is this the real life?";
|
||||
await meeting.chat.sendTextMessage(message);
|
||||
```
|
||||
|
||||
|
|
@ -608,20 +608,20 @@ await meeting.chat.sendTextMessage(message);
|
|||
You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type File, and sends it to the participants in the meeting.
|
||||
|
||||
```jsx
|
||||
import DocumentPicker from '@react-native-documents/picker';
|
||||
import DocumentPicker from "@react-native-documents/picker";
|
||||
|
||||
async function onSendImage() {
|
||||
// Get the image uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.images],
|
||||
});
|
||||
const image = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.sendImageMessage(image);
|
||||
// Get the image uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.images],
|
||||
});
|
||||
const image = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.sendImageMessage(image);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -630,20 +630,20 @@ async function onSendImage() {
|
|||
Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`.
|
||||
|
||||
```jsx
|
||||
import DocumentPicker from '@react-native-documents/picker';
|
||||
import DocumentPicker from "@react-native-documents/picker";
|
||||
|
||||
async function onSendFile() {
|
||||
// Get the file uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.allFiles],
|
||||
});
|
||||
const file = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.sendFileMessage(file);
|
||||
// Get the file uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.allFiles],
|
||||
});
|
||||
const file = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.sendFileMessage(file);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -653,20 +653,20 @@ There is also a common method called `meeting.chat.sendMessage()` that can be us
|
|||
|
||||
```typescript
|
||||
async function sendMessage(
|
||||
message:
|
||||
| { type: "text"; message: string }
|
||||
| { type: "image"; image: File }
|
||||
| { type: "file"; file: File },
|
||||
message:
|
||||
| { type: "text"; message: string }
|
||||
| { type: "image"; image: File }
|
||||
| { type: "file"; file: File },
|
||||
) {
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Here is how you would use the `sendMessage()` method to send a text message:
|
||||
|
||||
```jsx
|
||||
const message = 'Is this just fantasy?';
|
||||
await meeting.chat.sendMessage({ type: 'text', message });
|
||||
const message = "Is this just fantasy?";
|
||||
await meeting.chat.sendMessage({ type: "text", message });
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
@ -836,9 +836,9 @@ Whenever a chat message is received, the `meeting.chat.messages` list is automat
|
|||
The `meeting.chat` object emits events when new chat messages are received. You can listen for the `chatUpdate` event to log when a new chat message is received.
|
||||
|
||||
```jsx
|
||||
meeting.chat.on('chatUpdate', ({ message, messages }) => {
|
||||
console.log(`Received message ${message}`);
|
||||
console.log(`All messages in chat: ${messages.join(', ')}`);
|
||||
meeting.chat.on("chatUpdate", ({ message, messages }) => {
|
||||
console.log(`Received message ${message}`);
|
||||
console.log(`All messages in chat: ${messages.join(", ")}`);
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -849,8 +849,8 @@ When a chat message is received, the `meeting.chat.messages` list is also update
|
|||
```jsx
|
||||
console.log(JSON.stringify(meeting.chat.messages));
|
||||
|
||||
meeting.chat.on('chatUpdate', () => {
|
||||
console.log(JSON.stringify(meeting.chat.messages));
|
||||
meeting.chat.on("chatUpdate", () => {
|
||||
console.log(JSON.stringify(meeting.chat.messages));
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -1051,7 +1051,7 @@ To edit a text message, use the `meeting.chat.editTextMessage()` method. This ac
|
|||
```jsx
|
||||
const message = meeting.chat.messages[0];
|
||||
const messageId = message?.id;
|
||||
const newMessage = 'Is this the real life?';
|
||||
const newMessage = "Is this the real life?";
|
||||
await meeting.chat.editTextMessage(messageId, newMessage);
|
||||
```
|
||||
|
||||
|
|
@ -1060,21 +1060,21 @@ await meeting.chat.editTextMessage(messageId, newMessage);
|
|||
You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type File.
|
||||
|
||||
```jsx
|
||||
import DocumentPicker from '@react-native-documents/picker';
|
||||
import DocumentPicker from "@react-native-documents/picker";
|
||||
|
||||
async function onEditImage() {
|
||||
const messageId = '...';
|
||||
// Get the image uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.images],
|
||||
});
|
||||
const image = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.editImageMessage(messageId, image);
|
||||
const messageId = "...";
|
||||
// Get the image uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.images],
|
||||
});
|
||||
const image = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.editImageMessage(messageId, image);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1083,21 +1083,21 @@ async function onEditImage() {
|
|||
Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`.
|
||||
|
||||
```jsx
|
||||
import DocumentPicker from '@react-native-documents/picker';
|
||||
import DocumentPicker from "@react-native-documents/picker";
|
||||
|
||||
async function onEditFile() {
|
||||
const messageId = '...';
|
||||
// Get the file uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.allFiles],
|
||||
});
|
||||
const file = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.editFileMessage(messageId, file);
|
||||
const messageId = "...";
|
||||
// Get the file uri and create an object with the following fields
|
||||
const res = await DocumentPicker.pickSingle({
|
||||
type: [DocumentPicker.types.allFiles],
|
||||
});
|
||||
const file = {
|
||||
uri: res.uri,
|
||||
name: res.name,
|
||||
size: res.size,
|
||||
type: res.type,
|
||||
};
|
||||
await meeting.chat.editFileMessage(messageId, file);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1107,22 +1107,22 @@ There is also a common method called `meeting.chat.editMessage()` that can be us
|
|||
|
||||
```typescript
|
||||
async function editMessage(
|
||||
messageId: string,
|
||||
message:
|
||||
| { type: "text"; message: string }
|
||||
| { type: "image"; image: File }
|
||||
| { type: "file"; file: File },
|
||||
messageId: string,
|
||||
message:
|
||||
| { type: "text"; message: string }
|
||||
| { type: "image"; image: File }
|
||||
| { type: "file"; file: File },
|
||||
) {
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Here is how you would use the `editMessage()` method to edit a text message:
|
||||
|
||||
```jsx
|
||||
const messageId = '...';
|
||||
const message = 'Is this just fantasy?';
|
||||
await meeting.chat.editMessage(messageId, { type: 'text', message });
|
||||
const messageId = "...";
|
||||
const message = "Is this just fantasy?";
|
||||
await meeting.chat.editMessage(messageId, { type: "text", message });
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,265 @@
|
|||
---
|
||||
pcx_content_type: how-to
|
||||
title: Display active speakers
|
||||
slug: realtime/realtimekit/core/display-active-speakers
|
||||
sidebar:
|
||||
order: 6
|
||||
---
|
||||
|
||||
import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro";
|
||||
import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro";
|
||||
|
||||
RealtimeKit automatically detects and tracks participants who are actively speaking in a meeting. You can display either a single active speaker or multiple active speakers in your application UI, depending on your design requirements.
|
||||
|
||||
An active speaker in RealtimeKit is a remote participant with prominent audio activity at any given moment. The SDK maintains two types of data to help you build your UI:
|
||||
|
||||
- **Active speaker** — A single remote participant who is currently speaking most prominently, tracked via `meeting.participants.lastActiveSpeaker`.
|
||||
- **Active participants** — A set of remote participants with the most prominent audio activity, tracked via `meeting.participants.active`.
|
||||
|
||||
The SDK automatically updates these properties and subscribes to participant media as speaking activity changes. It prioritizes prominent audio activity, so a participant not currently visible in your UI can replace a visible participant if their audio becomes more active.
|
||||
|
||||
:::note
|
||||
The SDK tracks active speakers only when the local participant is viewing or rendering participants in ACTIVE mode (page 0). Refer to the [Remote participants](/realtime/realtimekit/core/remote-participant/#participant-view-modes) page to learn about participant view modes.
|
||||
|
||||
Active speaker properties contain only remote participants. Use `meeting.self` to display the local participant.
|
||||
:::
|
||||
|
||||
The maximum number of participants in the `active` map is one less than the grid size configured in the local participant's [Preset](/realtime/realtimekit/concepts/preset/).
|
||||
This reserves space for the local participant in your UI. For example, if the grid size is 6, the `active` map contains a maximum of 5 remote participants.
|
||||
|
||||
<RTKSDKSelector />
|
||||
|
||||
## Display a single active speaker
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
|
||||
Use `lastActiveSpeaker` to show the most recently active participant in your UI. Access the current active speaker with the `useRealtimeKitSelector` hook:
|
||||
|
||||
```ts
|
||||
const activeSpeaker = useRealtimeKitSelector((meeting) => {
|
||||
const activeSpeakerId = meeting.participants.lastActiveSpeaker;
|
||||
return meeting.participants.joined.get(activeSpeakerId);
|
||||
});
|
||||
|
||||
if (activeSpeaker) {
|
||||
// Render the active speaker video
|
||||
}
|
||||
```
|
||||
|
||||
The `useRealtimeKitSelector` hook automatically updates your component when the active speaker changes.
|
||||
|
||||
Refer to [Display participant videos](/realtime/realtimekit/core/remote-participant/#display-participant-videos) to learn how to render the participant video in your UI.
|
||||
|
||||
The SDK also emits an `activeSpeaker` event on `meeting.participants` when a different participant becomes the active speaker. For imperative updates or side effects, listen to this event:
|
||||
|
||||
```ts
|
||||
meeting.participants.on("activeSpeaker", ({ peerId, volume }) => {
|
||||
const activeSpeaker = meeting.participants.joined.get(peerId);
|
||||
// Update your UI or trigger side effects
|
||||
});
|
||||
```
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
|
||||
Use `lastActiveSpeaker` to show the most recently active participant in your UI.
|
||||
|
||||
Access the `lastActiveSpeaker` property to get the participant ID, then retrieve the participant object from the joined participants map:
|
||||
|
||||
```ts
|
||||
const activeSpeakerId = meeting.participants.lastActiveSpeaker;
|
||||
const activeSpeaker = meeting.participants.joined.get(activeSpeakerId);
|
||||
|
||||
if (activeSpeaker) {
|
||||
// Render the active speaker video
|
||||
}
|
||||
```
|
||||
|
||||
Refer to [Display participant videos](/realtime/realtimekit/core/remote-participant/#display-participant-videos) to learn how to render the participant video in your UI.
|
||||
|
||||
The SDK emits an `activeSpeaker` event on `meeting.participants` when a different participant becomes the active speaker:
|
||||
|
||||
```ts
|
||||
meeting.participants.on("activeSpeaker", ({ peerId, volume }) => {
|
||||
const activeSpeaker = meeting.participants.joined.get(peerId);
|
||||
// Update your UI to display the new active speaker
|
||||
});
|
||||
```
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
|
||||
Use `lastActiveSpeaker` to show the most recently active participant in your UI.
|
||||
|
||||
Access the `lastActiveSpeaker` property to get the participant ID, then retrieve the participant object from the joined participants map:
|
||||
|
||||
```ts
|
||||
const activeSpeakerId = meeting.participants.lastActiveSpeaker;
|
||||
const activeSpeaker = meeting.participants.joined.get(activeSpeakerId);
|
||||
|
||||
if (activeSpeaker) {
|
||||
// Render the active speaker video
|
||||
}
|
||||
```
|
||||
|
||||
Refer to [Display participant videos](/realtime/realtimekit/core/remote-participant/#display-participant-videos) to learn how to render the participant video in your UI.
|
||||
|
||||
The SDK emits an `activeSpeaker` event on `meeting.participants` when a different participant becomes the active speaker:
|
||||
|
||||
```ts
|
||||
meeting.participants.on("activeSpeaker", ({ peerId, volume }) => {
|
||||
const activeSpeaker = meeting.participants.joined.get(peerId);
|
||||
// Update your UI to display the new active speaker
|
||||
});
|
||||
```
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
## Display multiple active speakers
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
|
||||
Use the `active` map to show multiple participants with prominent audio activity, typically in a grid layout. Access the current active participants with the `useRealtimeKitSelector` hook:
|
||||
|
||||
```ts
|
||||
const activeMap = useRealtimeKitSelector(
|
||||
(meeting) => meeting.participants.active,
|
||||
);
|
||||
|
||||
const activeParticipants = activeMap.toArray();
|
||||
|
||||
// Render active participants in your grid
|
||||
activeParticipants.forEach((participant) => {
|
||||
// Render participant video tile
|
||||
});
|
||||
```
|
||||
|
||||
The `useRealtimeKitSelector` hook automatically updates your component when the set of active speakers changes.
|
||||
|
||||
Refer to [Display participant videos](/realtime/realtimekit/core/remote-participant/#display-participant-videos) to learn how to render the participant video in your UI.
|
||||
|
||||
The SDK also emits a `participantsUpdate` event on the `active` map when the set of active speakers changes. For imperative updates or side effects when the `active` map changes, listen to this event:
|
||||
|
||||
```ts
|
||||
meeting.participants.active.on("participantsUpdate", () => {
|
||||
const activeParticipants = meeting.participants.active.toArray();
|
||||
// Perform side effects
|
||||
});
|
||||
```
|
||||
|
||||
(Optional) If your application needs to respond when a specific participant is added to or removed from the active map, listen for `participantJoined` and `participantLeft` on `meeting.participants.active` map.
|
||||
|
||||
```ts
|
||||
meeting.participants.active.on("participantJoined", (participant) => {
|
||||
console.log("Participant added to active map:", participant.id);
|
||||
});
|
||||
|
||||
meeting.participants.active.on("participantLeft", (participant) => {
|
||||
console.log("Participant removed from active map:", participant.id);
|
||||
});
|
||||
```
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
|
||||
Use the `active` map to show multiple participants with prominent audio activity, typically in a grid layout.
|
||||
|
||||
```ts
|
||||
const activeParticipants = meeting.participants.active.toArray();
|
||||
|
||||
// Render active participants in your grid
|
||||
activeParticipants.forEach((participant) => {
|
||||
// Render participant video tile
|
||||
});
|
||||
```
|
||||
|
||||
Refer to [Display participant videos](/realtime/realtimekit/core/remote-participant/#display-participant-videos) to learn how to render the participant video in your UI.
|
||||
|
||||
The SDK emits a `participantsUpdate` event on the `active` map when the set of active speakers changes. Listen to this event, retrieve the updated array, and re-render your grid:
|
||||
|
||||
```ts
|
||||
meeting.participants.active.on("participantsUpdate", () => {
|
||||
const activeParticipants = meeting.participants.active.toArray();
|
||||
// Update your grid UI with the new active participants
|
||||
});
|
||||
```
|
||||
|
||||
(Optional) If your application needs to respond when a specific participant is added to or removed from the active map, listen for `participantJoined` and `participantLeft` on `meeting.participants.active` map.
|
||||
|
||||
```ts
|
||||
meeting.participants.active.on("participantJoined", (participant) => {
|
||||
console.log("Participant added to active map:", participant.id);
|
||||
});
|
||||
|
||||
meeting.participants.active.on("participantLeft", (participant) => {
|
||||
console.log("Participant removed from active map:", participant.id);
|
||||
});
|
||||
```
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
|
||||
Use the `active` map to show multiple participants with prominent audio activity, typically in a grid layout.
|
||||
|
||||
```ts
|
||||
const activeParticipants = meeting.participants.active.toArray();
|
||||
|
||||
// Render active participants in your grid
|
||||
activeParticipants.forEach((participant) => {
|
||||
// Render participant video tile
|
||||
});
|
||||
```
|
||||
|
||||
Refer to [Display participant videos](/realtime/realtimekit/core/remote-participant/#display-participant-videos) to learn how to render the participant video in your UI.
|
||||
|
||||
The SDK emits a `participantsUpdate` event on the `active` map when the set of active speakers changes. Listen to this event, retrieve the updated array, and re-render your grid:
|
||||
|
||||
```ts
|
||||
meeting.participants.active.on("participantsUpdate", () => {
|
||||
const activeParticipants = meeting.participants.active.toArray();
|
||||
// Update your grid UI with the new active participants
|
||||
});
|
||||
```
|
||||
|
||||
(Optional) If your application needs to respond when a specific participant is added to or removed from the active map, listen for `participantJoined` and `participantLeft` on `meeting.participants.active` map.
|
||||
|
||||
```ts
|
||||
meeting.participants.active.on("participantJoined", (participant) => {
|
||||
console.log("Participant added to active map:", participant.id);
|
||||
});
|
||||
|
||||
meeting.participants.active.on("participantLeft", (participant) => {
|
||||
console.log("Participant removed from active map:", participant.id);
|
||||
});
|
||||
```
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
## Visualize audio activity
|
||||
|
||||
<RTKCodeSnippet id="web-react">
|
||||
|
||||
You can create custom audio visualizations using audio data from a participant's audio track. Extract volume information from the audio track to calculate amplitude series. Use this data to render waveforms, speech indicators, or audio level meters in your UI.
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-web-components">
|
||||
|
||||
You can create custom audio visualizations using audio data from a participant's audio track. Extract volume information from the audio track to calculate amplitude series. Use this data to render waveforms, speech indicators, or audio level meters in your UI.
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
<RTKCodeSnippet id="web-angular">
|
||||
|
||||
You can create custom audio visualizations using audio data from a participant's audio track. Extract volume information from the audio track to calculate amplitude series. Use this data to render waveforms, speech indicators, or audio level meters in your UI.
|
||||
|
||||
</RTKCodeSnippet>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Meeting object explained](/realtime/realtimekit/core/meeting-object-explained/) - Understand the meeting object structure and available properties.
|
||||
- [Remote participant](/realtime/realtimekit/core/remote-participant/) - Learn more about remote participants in a session and how to display their video.
|
||||
|
|
@ -3,7 +3,7 @@ pcx_content_type: how-to
|
|||
title: End a session
|
||||
slug: realtime/realtimekit/core/end-a-session
|
||||
sidebar:
|
||||
order: 7
|
||||
order: 8
|
||||
---
|
||||
|
||||
import { Render } from "~/components";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: content
|
|||
title: Error Codes
|
||||
slug: realtime/realtimekit/core/error-codes
|
||||
sidebar:
|
||||
order: 14
|
||||
order: 15
|
||||
---
|
||||
|
||||
import { Tabs, TabItem } from "~/components";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ title: Manage other participants in a session
|
|||
pcx_content_type: how-to
|
||||
slug: realtime/realtimekit/core/manage-other-participants-in-a-session
|
||||
sidebar:
|
||||
order: 6
|
||||
order: 7
|
||||
---
|
||||
|
||||
import { Render } from "~/components";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: how-to
|
|||
title: Media Acquisition Approaches
|
||||
slug: realtime/realtimekit/core/media-acquisition-approaches
|
||||
sidebar:
|
||||
order: 16
|
||||
order: 17
|
||||
---
|
||||
|
||||
import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: navigation
|
|||
title: Polls
|
||||
slug: realtime/realtimekit/core/polls
|
||||
sidebar:
|
||||
order: 12
|
||||
order: 13
|
||||
---
|
||||
|
||||
import { Tabs, TabItem } from "~/components";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: navigation
|
|||
title: Stage Management
|
||||
slug: realtime/realtimekit/core/stage-management
|
||||
sidebar:
|
||||
order: 9
|
||||
order: 10
|
||||
---
|
||||
|
||||
import { Tabs, TabItem } from "~/components";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: content
|
|||
title: Video Background
|
||||
slug: realtime/realtimekit/core/video-background
|
||||
sidebar:
|
||||
order: 13
|
||||
order: 14
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, PackageManagers } from "~/components";
|
||||
|
|
@ -28,8 +28,8 @@ If you are using the `rtk-meeting` component with UI Kit and prefer a higher-lev
|
|||
Disable the default per frame rendering of video middleware to improve speed and quality by letting this middleware control it on its own:
|
||||
|
||||
```javascript
|
||||
await meeting.self.setVideoMiddlewareGlobalConfig({
|
||||
disablePerFrameCanvasRendering: true
|
||||
await meeting.self.setVideoMiddlewareGlobalConfig({
|
||||
disablePerFrameCanvasRendering: true,
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -38,11 +38,12 @@ await meeting.self.setVideoMiddlewareGlobalConfig({
|
|||
Create a video background transformer object:
|
||||
|
||||
```javascript
|
||||
import RealtimeKitVideoBackgroundTransformer from '@cloudflare/realtimekit-virtual-background';
|
||||
import RealtimeKitVideoBackgroundTransformer from "@cloudflare/realtimekit-virtual-background";
|
||||
|
||||
const videoBackgroundTransformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting,
|
||||
});
|
||||
const videoBackgroundTransformer =
|
||||
await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting,
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Apply background effects
|
||||
|
|
@ -54,10 +55,12 @@ The `videoBackgroundTransformer` exposes two types of middlewares:
|
|||
Use `createStaticBackgroundVideoMiddleware` to set an image as the background:
|
||||
|
||||
```javascript
|
||||
const imageUrl = 'https://images.unsplash.com/photo-1487088678257-3a541e6e3922';
|
||||
const imageUrl = "https://images.unsplash.com/photo-1487088678257-3a541e6e3922";
|
||||
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(imageUrl)
|
||||
await videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(
|
||||
imageUrl,
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -67,7 +70,7 @@ Use `createBackgroundBlurVideoMiddleware` to blur the background. Pass `blurStre
|
|||
|
||||
```javascript
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createBackgroundBlurVideoMiddleware(50)
|
||||
await videoBackgroundTransformer.createBackgroundBlurVideoMiddleware(50),
|
||||
);
|
||||
```
|
||||
|
||||
|
|
@ -77,13 +80,16 @@ Check browser support before initializing:
|
|||
|
||||
```javascript
|
||||
if (RealtimeKitVideoBackgroundTransformer.isSupported()) {
|
||||
const videoBackgroundTransformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting: meeting,
|
||||
});
|
||||
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(imageUrl)
|
||||
);
|
||||
const videoBackgroundTransformer =
|
||||
await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting: meeting,
|
||||
});
|
||||
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(
|
||||
imageUrl,
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -96,17 +102,18 @@ Image URLs must allow CORS to avoid tainting the canvas. You can find CORS-enabl
|
|||
For better, sharper results, pass a custom segmentation configuration:
|
||||
|
||||
```javascript
|
||||
const videoBackgroundTransformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting,
|
||||
segmentationConfig: {
|
||||
model: 'mlkit', // 'meet' | 'mlkit'
|
||||
backend: 'wasmSimd',
|
||||
inputResolution: '256x256', // '256x144' for meet
|
||||
pipeline: 'webgl2', // 'webgl2' | 'canvas2dCpu'
|
||||
// canvas2dCpu gives sharper blur, webgl2 is faster
|
||||
targetFps: 35,
|
||||
}
|
||||
});
|
||||
const videoBackgroundTransformer =
|
||||
await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting,
|
||||
segmentationConfig: {
|
||||
model: "mlkit", // 'meet' | 'mlkit'
|
||||
backend: "wasmSimd",
|
||||
inputResolution: "256x256", // '256x144' for meet
|
||||
pipeline: "webgl2", // 'webgl2' | 'canvas2dCpu'
|
||||
// canvas2dCpu gives sharper blur, webgl2 is faster
|
||||
targetFps: 35,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
@ -119,74 +126,81 @@ const videoBackgroundTransformer = await RealtimeKitVideoBackgroundTransformer.i
|
|||
## Usage
|
||||
|
||||
```jsx
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRealtimeKitClient } from '@cloudflare/realtimekit-react';
|
||||
import RealtimeKitVideoBackgroundTransformer from '@cloudflare/realtimekit-virtual-background';
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
|
||||
import RealtimeKitVideoBackgroundTransformer from "@cloudflare/realtimekit-virtual-background";
|
||||
|
||||
function App() {
|
||||
const [meeting] = useRealtimeKitClient();
|
||||
const [videoBackgroundTransformer, setVideoBackgroundTransformer] = useState(null);
|
||||
const [meeting] = useRealtimeKitClient();
|
||||
const [videoBackgroundTransformer, setVideoBackgroundTransformer] =
|
||||
useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const initializeTransformer = async () => {
|
||||
if (!meeting) return;
|
||||
useEffect(() => {
|
||||
const initializeTransformer = async () => {
|
||||
if (!meeting) return;
|
||||
|
||||
// Check browser support
|
||||
if (!RealtimeKitVideoBackgroundTransformer.isSupported()) {
|
||||
console.warn('Video background not supported in this browser');
|
||||
return;
|
||||
}
|
||||
// Check browser support
|
||||
if (!RealtimeKitVideoBackgroundTransformer.isSupported()) {
|
||||
console.warn("Video background not supported in this browser");
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable default per frame rendering
|
||||
await meeting.self.setVideoMiddlewareGlobalConfig({
|
||||
disablePerFrameCanvasRendering: true
|
||||
});
|
||||
// Disable default per frame rendering
|
||||
await meeting.self.setVideoMiddlewareGlobalConfig({
|
||||
disablePerFrameCanvasRendering: true,
|
||||
});
|
||||
|
||||
// Initialize transformer
|
||||
const transformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting,
|
||||
});
|
||||
// Initialize transformer
|
||||
const transformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting,
|
||||
});
|
||||
|
||||
setVideoBackgroundTransformer(transformer);
|
||||
};
|
||||
setVideoBackgroundTransformer(transformer);
|
||||
};
|
||||
|
||||
initializeTransformer();
|
||||
}, [meeting]);
|
||||
initializeTransformer();
|
||||
}, [meeting]);
|
||||
|
||||
const applyStaticBackground = async (imageUrl) => {
|
||||
if (!videoBackgroundTransformer) return;
|
||||
const applyStaticBackground = async (imageUrl) => {
|
||||
if (!videoBackgroundTransformer) return;
|
||||
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(imageUrl)
|
||||
);
|
||||
};
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(
|
||||
imageUrl,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const applyBlur = async (blurStrength = 50) => {
|
||||
if (!videoBackgroundTransformer) return;
|
||||
const applyBlur = async (blurStrength = 50) => {
|
||||
if (!videoBackgroundTransformer) return;
|
||||
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createBackgroundBlurVideoMiddleware(blurStrength)
|
||||
);
|
||||
};
|
||||
meeting.self.addVideoMiddleware(
|
||||
await videoBackgroundTransformer.createBackgroundBlurVideoMiddleware(
|
||||
blurStrength,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const removeBackground = () => {
|
||||
// Remove all video middlewares
|
||||
meeting.self.removeVideoMiddleware();
|
||||
};
|
||||
const removeBackground = () => {
|
||||
// Remove all video middlewares
|
||||
meeting.self.removeVideoMiddleware();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={() => applyStaticBackground('https://images.unsplash.com/photo-1487088678257-3a541e6e3922')}>
|
||||
Apply Background
|
||||
</button>
|
||||
<button onClick={() => applyBlur(50)}>
|
||||
Apply Blur
|
||||
</button>
|
||||
<button onClick={removeBackground}>
|
||||
Remove Background
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={() =>
|
||||
applyStaticBackground(
|
||||
"https://images.unsplash.com/photo-1487088678257-3a541e6e3922",
|
||||
)
|
||||
}
|
||||
>
|
||||
Apply Background
|
||||
</button>
|
||||
<button onClick={() => applyBlur(50)}>Apply Blur</button>
|
||||
<button onClick={removeBackground}>Remove Background</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -200,15 +214,15 @@ For better, sharper results, pass a custom segmentation configuration:
|
|||
|
||||
```jsx
|
||||
const transformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting,
|
||||
segmentationConfig: {
|
||||
model: 'mlkit', // 'meet' | 'mlkit'
|
||||
backend: 'wasmSimd',
|
||||
inputResolution: '256x256', // '256x144' for meet
|
||||
pipeline: 'webgl2', // 'webgl2' | 'canvas2dCpu'
|
||||
// canvas2dCpu gives sharper blur, webgl2 is faster
|
||||
targetFps: 35,
|
||||
}
|
||||
meeting,
|
||||
segmentationConfig: {
|
||||
model: "mlkit", // 'meet' | 'mlkit'
|
||||
backend: "wasmSimd",
|
||||
inputResolution: "256x256", // '256x144' for meet
|
||||
pipeline: "webgl2", // 'webgl2' | 'canvas2dCpu'
|
||||
// canvas2dCpu gives sharper blur, webgl2 is faster
|
||||
targetFps: 35,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -224,78 +238,81 @@ const transformer = await RealtimeKitVideoBackgroundTransformer.init({
|
|||
In your component TypeScript file:
|
||||
|
||||
```typescript
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import RealtimeKitClient from '@cloudflare/realtimekit';
|
||||
import RealtimeKitVideoBackgroundTransformer from '@cloudflare/realtimekit-virtual-background';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import RealtimeKitClient from "@cloudflare/realtimekit";
|
||||
import RealtimeKitVideoBackgroundTransformer from "@cloudflare/realtimekit-virtual-background";
|
||||
|
||||
@Component({
|
||||
selector: 'app-meeting',
|
||||
templateUrl: './meeting.component.html',
|
||||
selector: "app-meeting",
|
||||
templateUrl: "./meeting.component.html",
|
||||
})
|
||||
export class MeetingComponent implements OnInit {
|
||||
meeting: any;
|
||||
videoBackgroundTransformer: any;
|
||||
meeting: any;
|
||||
videoBackgroundTransformer: any;
|
||||
|
||||
async ngOnInit() {
|
||||
// Initialize meeting
|
||||
this.meeting = await RealtimeKitClient.init({
|
||||
authToken: '<participant_auth_token>',
|
||||
});
|
||||
async ngOnInit() {
|
||||
// Initialize meeting
|
||||
this.meeting = await RealtimeKitClient.init({
|
||||
authToken: "<participant_auth_token>",
|
||||
});
|
||||
|
||||
await this.meeting.join();
|
||||
await this.meeting.join();
|
||||
|
||||
// Check browser support
|
||||
if (!RealtimeKitVideoBackgroundTransformer.isSupported()) {
|
||||
console.warn('Video background not supported in this browser');
|
||||
return;
|
||||
}
|
||||
// Check browser support
|
||||
if (!RealtimeKitVideoBackgroundTransformer.isSupported()) {
|
||||
console.warn("Video background not supported in this browser");
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable default per frame rendering
|
||||
await this.meeting.self.setVideoMiddlewareGlobalConfig({
|
||||
disablePerFrameCanvasRendering: true
|
||||
});
|
||||
// Disable default per frame rendering
|
||||
await this.meeting.self.setVideoMiddlewareGlobalConfig({
|
||||
disablePerFrameCanvasRendering: true,
|
||||
});
|
||||
|
||||
// Initialize transformer
|
||||
this.videoBackgroundTransformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting: this.meeting,
|
||||
});
|
||||
}
|
||||
// Initialize transformer
|
||||
this.videoBackgroundTransformer =
|
||||
await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting: this.meeting,
|
||||
});
|
||||
}
|
||||
|
||||
async applyStaticBackground(imageUrl: string) {
|
||||
if (!this.videoBackgroundTransformer) return;
|
||||
async applyStaticBackground(imageUrl: string) {
|
||||
if (!this.videoBackgroundTransformer) return;
|
||||
|
||||
this.meeting.self.addVideoMiddleware(
|
||||
await this.videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(imageUrl)
|
||||
);
|
||||
}
|
||||
this.meeting.self.addVideoMiddleware(
|
||||
await this.videoBackgroundTransformer.createStaticBackgroundVideoMiddleware(
|
||||
imageUrl,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async applyBlur(blurStrength: number = 50) {
|
||||
if (!this.videoBackgroundTransformer) return;
|
||||
async applyBlur(blurStrength: number = 50) {
|
||||
if (!this.videoBackgroundTransformer) return;
|
||||
|
||||
this.meeting.self.addVideoMiddleware(
|
||||
await this.videoBackgroundTransformer.createBackgroundBlurVideoMiddleware(blurStrength)
|
||||
);
|
||||
}
|
||||
this.meeting.self.addVideoMiddleware(
|
||||
await this.videoBackgroundTransformer.createBackgroundBlurVideoMiddleware(
|
||||
blurStrength,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
removeBackground() {
|
||||
// Remove all video middlewares
|
||||
this.meeting.self.removeVideoMiddleware();
|
||||
}
|
||||
removeBackground() {
|
||||
// Remove all video middlewares
|
||||
this.meeting.self.removeVideoMiddleware();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In your component template:
|
||||
|
||||
```html
|
||||
<button (click)="applyStaticBackground('https://images.unsplash.com/photo-1487088678257-3a541e6e3922')">
|
||||
Apply Background
|
||||
</button>
|
||||
<button (click)="applyBlur(50)">
|
||||
Apply Blur
|
||||
</button>
|
||||
<button (click)="removeBackground()">
|
||||
Remove Background
|
||||
<button
|
||||
(click)="applyStaticBackground('https://images.unsplash.com/photo-1487088678257-3a541e6e3922')"
|
||||
>
|
||||
Apply Background
|
||||
</button>
|
||||
<button (click)="applyBlur(50)">Apply Blur</button>
|
||||
<button (click)="removeBackground()">Remove Background</button>
|
||||
```
|
||||
|
||||
:::note[Image CORS requirements]
|
||||
|
|
@ -307,17 +324,18 @@ Image URLs must allow CORS to avoid tainting the canvas. You can find CORS-enabl
|
|||
For better, sharper results, pass a custom segmentation configuration:
|
||||
|
||||
```typescript
|
||||
this.videoBackgroundTransformer = await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting: this.meeting,
|
||||
segmentationConfig: {
|
||||
model: 'mlkit', // 'meet' | 'mlkit'
|
||||
backend: 'wasmSimd',
|
||||
inputResolution: '256x256', // '256x144' for meet
|
||||
pipeline: 'webgl2', // 'webgl2' | 'canvas2dCpu'
|
||||
// canvas2dCpu gives sharper blur, webgl2 is faster
|
||||
targetFps: 35,
|
||||
}
|
||||
});
|
||||
this.videoBackgroundTransformer =
|
||||
await RealtimeKitVideoBackgroundTransformer.init({
|
||||
meeting: this.meeting,
|
||||
segmentationConfig: {
|
||||
model: "mlkit", // 'meet' | 'mlkit'
|
||||
backend: "wasmSimd",
|
||||
inputResolution: "256x256", // '256x144' for meet
|
||||
pipeline: "webgl2", // 'webgl2' | 'canvas2dCpu'
|
||||
// canvas2dCpu gives sharper blur, webgl2 is faster
|
||||
targetFps: 35,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
@ -405,4 +423,4 @@ val meeting = RealtimeKitMeetingBuilder
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pcx_content_type: navigation
|
|||
title: Waiting Room
|
||||
slug: realtime/realtimekit/core/waiting-room
|
||||
sidebar:
|
||||
order: 8
|
||||
order: 9
|
||||
---
|
||||
|
||||
import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue