Skip to content

Commit f790ee7

Browse files
authored
feat(react): Support for Background Filters and Background Blurring (#1283)
Adds API for registering video filters, default plugin based on TensorFlow Lite, and a sample integration in our Demo app. **New APIs:** - `camera.registerFilter(async (mediaStream) => mediaStream)` the core part. It allows you to chain multiple video and audio filters. - `@stream-io/video-filters-web` - a new lib that is responsible for the ML part and WebGL rendering - `BackgroundFiltersProvider` - a component in the React SDK that helps you to manage the applied filters. Our dogfooding app integrates these new APIs and provides the necessary input (background images) and styling. ### Notes Because of our deployment setup, this PR contains the changes done as part of #1271 and #1276 :)
1 parent 67c738e commit f790ee7

File tree

88 files changed

+4941
-341
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+4941
-341
lines changed

.github/workflows/deploy-react-sample-apps.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ jobs:
4747
project-id: prj_0WnHcvVkXpM4PRc2ymVmrAHFILoT
4848
- name: react-dogfood - https://pronto.getstream.io
4949
project-id: prj_4TTdjeVHEDhWWiFRfjIr1QFb5ell
50+
- name: react-dogfood-staging - https://pronto-staging.getstream.io
51+
project-id: prj_xmKoy4ExySCQ4fpWoWSVXlcuvtfS
5052
- name: react-dogfood - https://getstream.io/video/demos
5153
project-id: prj_tTLn3XMVal4D1Nnel9nPJ0hoI9wA
5254
base-path: /video/demos

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"start:styling": "yarn workspace @stream-io/video-styling run start",
2626
"build:client": "yarn workspace @stream-io/video-client run build",
2727
"start:client": "yarn workspace @stream-io/video-client run start",
28-
"build:react:deps": "yarn build:client && yarn build:styling && yarn build:react:bindings && yarn build:react:sdk",
28+
"build:video-filters-web": "yarn workspace @stream-io/video-filters-web run build",
29+
"build:react:deps": "yarn build:client && yarn build:styling && yarn build:react:bindings && yarn build:video-filters-web && yarn build:react:sdk",
2930
"build:react-native:deps": "yarn build:client && yarn build:react:bindings && yarn build:react-native:sdk",
3031
"build:vercel": "yarn build:react:deps && yarn build:react:dogfood",
3132
"start:egress": "yarn workspace @stream-io/egress-composite start",
@@ -51,7 +52,9 @@
5152
"release": "nx run-many --target version --parallel=1",
5253
"release:react-bindings": "yarn workspace @stream-io/video-react-bindings npm publish --access=public",
5354
"release:react-sdk": "yarn workspace @stream-io/video-react-sdk npm publish --access=public",
54-
"release:react-native-sdk": "yarn workspace @stream-io/video-react-native-sdk npm publish --access=public"
55+
"release:react-native-sdk": "yarn workspace @stream-io/video-react-native-sdk npm publish --access=public",
56+
"release:video-filters-web": "yarn workspace @stream-io/video-filters-web npm publish --access=public",
57+
"release:styling": "yarn workspace @stream-io/video-styling npm publish --access=public"
5558
},
5659
"devDependencies": {
5760
"@commitlint/cli": "^17.0.0",
@@ -70,8 +73,8 @@
7073
"ngx-deploy-npm": "^5.2.0",
7174
"nx": "16.0.1",
7275
"prettier": "^2.8.8",
73-
"typescript": "^5.2.2",
74-
"vercel": "^33.2.0",
76+
"typescript": "^5.4.3",
77+
"vercel": "^33.6.2",
7578
"vite": "^4.4.11"
7679
}
7780
}

packages/client/docusaurus/docs/javascript/02-guides/04-camera-and-microphone.mdx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,49 @@ call.microphone.state.speakingWhileMuted$.subscribe((isSpeaking) => {
248248

249249
The notification is automatically disabled if the user doesn't have the permission to send audio.
250250

251+
## Camera and Microphone Filters
252+
253+
Both the Camera and the Microphone allow you to apply filters to the media stream.
254+
This can be useful for various use-cases, such as:
255+
256+
- applying a video effects such as background blurring, or background replacement
257+
- applying a custom video filter (e.g. color correction, or face detection)
258+
- applying a custom audio filter (e.g. noise reduction)
259+
260+
```typescript
261+
import { Call } from '@stream-io/video-client';
262+
263+
let call: Call;
264+
265+
// apply a custom video filter
266+
const unregisterMyVideoFilter = await camera.registerFilter(
267+
async function myVideoFilter(inputMediaStream: MediaStream) {
268+
// initialize the video filter, do some magic and
269+
// return the modified media stream
270+
return mediaStreamWithFilterApplied;
271+
},
272+
);
273+
274+
// apply a custom audio filter
275+
const unregisterMyAudioFilter = await microphone.registerFilter(
276+
async function myAudioFilter(inputMediaStream: MediaStream) {
277+
// initialize the audio filter, do some magic and
278+
// return the modified media stream
279+
return mediaStreamWithFilterApplied;
280+
},
281+
);
282+
283+
// unregister the filters
284+
unregisterMyVideoFilter();
285+
unregisterMyAudioFilter();
286+
```
287+
288+
Filters can be registered and unregistered at any time, and the SDK will take care of the rest.
289+
Filters can be chained, and the order of registration matters.
290+
The first registered filter will be the first to modify the raw `MediaStream`.
291+
292+
Once a filter(s) is registered, the SDK will send the last returned `MediaStream` to the remote participants.
293+
251294
## Speaker management
252295

253296
### List and select devices

packages/client/generate-openapi.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ else
1010
fi
1111
if [ "$FROM_REPO" == 'chat' ]; then
1212
SCHEMA_FILE="$PROTOCOL_REPO_DIR/releases/video-openapi.yaml"
13-
else
13+
elif [ "$FROM_REPO" == 'protocol' ]; then
1414
SCHEMA_FILE="$PROTOCOL_REPO_DIR/openapi/video-openapi.yaml"
15+
else
16+
SCHEMA_FILE=$FROM_REPO
1517
fi
1618

1719
if [ "$FROM_REPO" == 'chat' ]; then

packages/client/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
},
4545
"devDependencies": {
4646
"@openapitools/openapi-generator-cli": "^2.7.0",
47-
"@rollup/plugin-replace": "^5.0.4",
48-
"@rollup/plugin-typescript": "^11.1.5",
47+
"@rollup/plugin-replace": "^5.0.5",
48+
"@rollup/plugin-typescript": "^11.1.6",
4949
"@types/jsonwebtoken": "^9.0.3",
5050
"@types/sdp-transform": "^2.4.7",
5151
"@types/ua-parser-js": "^0.7.37",
@@ -55,7 +55,7 @@
5555
"prettier": "^2.8.8",
5656
"rimraf": "^5.0.5",
5757
"rollup": "^3.29.4",
58-
"typescript": "^5.2.2",
58+
"typescript": "^5.4.3",
5959
"vite": "^4.4.11",
6060
"vitest": "^0.34.4",
6161
"vitest-mock-extended": "^1.2.1"

packages/client/src/Call.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
GoLiveRequest,
3131
GoLiveResponse,
3232
ListRecordingsResponse,
33+
ListTranscriptionsResponse,
3334
MuteUsersRequest,
3435
MuteUsersResponse,
3536
OwnCapability,
@@ -48,10 +49,13 @@ import {
4849
StartHLSBroadcastingResponse,
4950
StartRecordingRequest,
5051
StartRecordingResponse,
52+
StartTranscriptionRequest,
53+
StartTranscriptionResponse,
5154
StatsOptions,
5255
StopHLSBroadcastingResponse,
5356
StopLiveResponse,
5457
StopRecordingResponse,
58+
StopTranscriptionResponse,
5559
UnblockUserRequest,
5660
UnblockUserResponse,
5761
UnpinRequest,
@@ -509,10 +513,10 @@ export class Call {
509513

510514
this.clientStore.unregisterCall(this);
511515

512-
this.camera.removeSubscriptions();
513-
this.microphone.removeSubscriptions();
514-
this.screenShare.removeSubscriptions();
515-
this.speaker.removeSubscriptions();
516+
this.camera.dispose();
517+
this.microphone.dispose();
518+
this.screenShare.dispose();
519+
this.speaker.dispose();
516520

517521
const stopOnLeavePromises: Promise<void>[] = [];
518522
if (this.camera.stopOnLeave) {
@@ -1582,6 +1586,29 @@ export class Call {
15821586
);
15831587
};
15841588

1589+
/**
1590+
* Starts the transcription of the call.
1591+
*
1592+
* @param request the request data.
1593+
*/
1594+
startTranscription = async (
1595+
request?: StartTranscriptionRequest,
1596+
): Promise<StartTranscriptionResponse> => {
1597+
return this.streamClient.post<
1598+
StartTranscriptionResponse,
1599+
StartTranscriptionRequest
1600+
>(`${this.streamClientBasePath}/start_transcription`, request);
1601+
};
1602+
1603+
/**
1604+
* Stops the transcription of the call.
1605+
*/
1606+
stopTranscription = async (): Promise<StopTranscriptionResponse> => {
1607+
return this.streamClient.post<StopTranscriptionResponse>(
1608+
`${this.streamClientBasePath}/stop_transcription`,
1609+
);
1610+
};
1611+
15851612
/**
15861613
* Sends a `call.permission_request` event to all users connected to the call. The call settings object contains infomration about which permissions can be requested during a call (for example a user might be allowed to request permission to publish audio, but not video).
15871614
*/
@@ -1859,6 +1886,17 @@ export class Call {
18591886
);
18601887
};
18611888

1889+
/**
1890+
* Retrieves the list of transcriptions for the current call.
1891+
*
1892+
* @returns the list of transcriptions.
1893+
*/
1894+
queryTranscriptions = async (): Promise<ListTranscriptionsResponse> => {
1895+
return this.streamClient.get<ListTranscriptionsResponse>(
1896+
`${this.streamClientBasePath}/transcriptions`,
1897+
);
1898+
};
1899+
18621900
/**
18631901
* Retrieve call statistics for a particular call session (historical).
18641902
* Here `callSessionID` is mandatory.

0 commit comments

Comments
 (0)