@stream-io/video-react-sdk 0.6.10 → 0.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import type { INoiseCancellation } from '@stream-io/audio-filters-web';
3
+ export type NoiseCancellationProviderProps = {
4
+ /**
5
+ * The noise cancellation instance to use.
6
+ */
7
+ noiseCancellation: INoiseCancellation;
8
+ };
9
+ /**
10
+ * The Noise Cancellation API.
11
+ */
12
+ export type NoiseCancellationAPI = {
13
+ /**
14
+ * A boolean providing information whether Noise Cancelling functionalities
15
+ * are supported on this platform and for the current user.
16
+ */
17
+ isSupported: boolean;
18
+ /**
19
+ * Provides information whether Noise Cancellation is active or not.
20
+ */
21
+ isEnabled: boolean;
22
+ /**
23
+ * Allows you to temporary enable or disable the Noise Cancellation filters.
24
+ *
25
+ * @param enabled a boolean or a setter.
26
+ */
27
+ setEnabled: (enabled: boolean | ((value: boolean) => boolean)) => void;
28
+ };
29
+ /**
30
+ * Exposes the NoiseCancellation API.
31
+ * Throws an error if used outside <NoiseCancellationProvider />.
32
+ */
33
+ export declare const useNoiseCancellation: () => NoiseCancellationAPI;
34
+ export declare const NoiseCancellationProvider: (props: PropsWithChildren<NoiseCancellationProviderProps>) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export * from './NoiseCancellationProvider';
@@ -12,6 +12,7 @@ export * from './DropdownSelect';
12
12
  export * from './Icon';
13
13
  export * from './LoadingIndicator';
14
14
  export * from './Menu';
15
+ export * from './NoiseCancellation';
15
16
  export * from './Notification';
16
17
  export * from './RingingCall';
17
18
  export * from './Permissions';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "0.6.10",
3
+ "version": "0.6.12",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.es.js",
@@ -29,9 +29,9 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "@floating-ui/react": "^0.26.5",
32
- "@stream-io/video-client": "0.7.7",
32
+ "@stream-io/video-client": "0.7.8",
33
33
  "@stream-io/video-filters-web": "0.1.0",
34
- "@stream-io/video-react-bindings": "0.4.18",
34
+ "@stream-io/video-react-bindings": "0.4.19",
35
35
  "chart.js": "^4.4.1",
36
36
  "clsx": "^2.0.0",
37
37
  "react-chartjs-2": "^5.2.0"
@@ -44,6 +44,7 @@
44
44
  "@rollup/plugin-json": "^6.1.0",
45
45
  "@rollup/plugin-replace": "^5.0.5",
46
46
  "@rollup/plugin-typescript": "^11.1.6",
47
+ "@stream-io/audio-filters-web": "^0.0.1",
47
48
  "@stream-io/video-styling": "^1.0.2",
48
49
  "react": "^18.2.0",
49
50
  "react-dom": "^18.2.0",
@@ -219,25 +219,28 @@ const BackgroundFilters = (props: { tfLite: TFLite }) => {
219
219
  const [width, setWidth] = useState(1920);
220
220
  const [height, setHeight] = useState(1080);
221
221
 
222
- const resolveFilterRef =
222
+ // Holds a ref to the `resolve` function of the returned Promise as part
223
+ // of the `camera.registerFilter()` API. Once the filter is initialized,
224
+ // it should be called with the filtered MediaStream as an argument.
225
+ const signalFilterReadyRef =
223
226
  useRef<(value: MediaStream | PromiseLike<MediaStream>) => void>();
224
227
 
225
228
  const [mediaStream, setMediaStream] = useState<MediaStream>();
226
- const registerFilterRef = useRef(Promise.resolve(async () => {}));
229
+ const unregister = useRef<Promise<void>>();
227
230
  useEffect(() => {
228
231
  if (!call || !backgroundFilter) return;
229
- registerFilterRef.current = registerFilterRef.current.then(() =>
232
+ const register = (unregister.current || Promise.resolve()).then(() =>
230
233
  call.camera.registerFilter(async (ms) => {
231
234
  return new Promise<MediaStream>((resolve) => {
232
235
  setMediaStream(ms);
233
- resolveFilterRef.current = resolve;
236
+ signalFilterReadyRef.current = resolve;
234
237
  });
235
238
  }),
236
239
  );
237
240
 
238
241
  return () => {
239
- registerFilterRef.current
240
- .then((unregister) => unregister())
242
+ unregister.current = register
243
+ .then((unregisterFilter) => unregisterFilter())
241
244
  .then(() => setMediaStream(undefined))
242
245
  .catch((err) => console.error('Failed to unregister filter', err));
243
246
  };
@@ -254,7 +257,7 @@ const BackgroundFilters = (props: { tfLite: TFLite }) => {
254
257
  setHeight(h);
255
258
  }
256
259
 
257
- const resolveFilter = resolveFilterRef.current;
260
+ const resolveFilter = signalFilterReadyRef.current;
258
261
  if (!resolveFilter) return;
259
262
  const filter = canvasRef.captureStream();
260
263
  resolveFilter(filter);
@@ -0,0 +1,124 @@
1
+ import {
2
+ createContext,
3
+ PropsWithChildren,
4
+ useContext,
5
+ useEffect,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import {
10
+ NoiseCancellationSettingsModeEnum,
11
+ OwnCapability,
12
+ } from '@stream-io/video-client';
13
+ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
14
+ import type { INoiseCancellation } from '@stream-io/audio-filters-web';
15
+
16
+ export type NoiseCancellationProviderProps = {
17
+ /**
18
+ * The noise cancellation instance to use.
19
+ */
20
+ noiseCancellation: INoiseCancellation;
21
+ };
22
+
23
+ /**
24
+ * The Noise Cancellation API.
25
+ */
26
+ export type NoiseCancellationAPI = {
27
+ /**
28
+ * A boolean providing information whether Noise Cancelling functionalities
29
+ * are supported on this platform and for the current user.
30
+ */
31
+ isSupported: boolean;
32
+ /**
33
+ * Provides information whether Noise Cancellation is active or not.
34
+ */
35
+ isEnabled: boolean;
36
+ /**
37
+ * Allows you to temporary enable or disable the Noise Cancellation filters.
38
+ *
39
+ * @param enabled a boolean or a setter.
40
+ */
41
+ setEnabled: (enabled: boolean | ((value: boolean) => boolean)) => void;
42
+ };
43
+
44
+ const NoiseCancellationContext = createContext<NoiseCancellationAPI | null>(
45
+ null,
46
+ );
47
+
48
+ /**
49
+ * Exposes the NoiseCancellation API.
50
+ * Throws an error if used outside <NoiseCancellationProvider />.
51
+ */
52
+ export const useNoiseCancellation = (): NoiseCancellationAPI => {
53
+ const context = useContext(NoiseCancellationContext);
54
+ if (!context) {
55
+ throw new Error(
56
+ 'useNoiseCancellation must be used within a NoiseCancellationProvider',
57
+ );
58
+ }
59
+ return context;
60
+ };
61
+
62
+ export const NoiseCancellationProvider = (
63
+ props: PropsWithChildren<NoiseCancellationProviderProps>,
64
+ ) => {
65
+ const { children, noiseCancellation } = props;
66
+ const call = useCall();
67
+ const { useCallSettings, useHasPermissions } = useCallStateHooks();
68
+ const settings = useCallSettings();
69
+ const noiseCancellationAllowed = !!(
70
+ settings &&
71
+ settings.audio.noise_cancellation &&
72
+ settings.audio.noise_cancellation.mode !==
73
+ NoiseCancellationSettingsModeEnum.DISABLED
74
+ );
75
+
76
+ const hasCapability = useHasPermissions(
77
+ OwnCapability.ENABLE_NOISE_CANCELLATION,
78
+ );
79
+ const isSupported =
80
+ hasCapability &&
81
+ noiseCancellationAllowed &&
82
+ noiseCancellation.isSupported();
83
+
84
+ const [isEnabled, setIsEnabled] = useState(false);
85
+ const deinit = useRef<Promise<void>>();
86
+ useEffect(() => {
87
+ if (!call || !isSupported) return;
88
+ const unsubscribe = noiseCancellation.on('change', (v) => setIsEnabled(v));
89
+ const init = (deinit.current || Promise.resolve())
90
+ .then(() => noiseCancellation.init())
91
+ .then(() => call.microphone.enableNoiseCancellation(noiseCancellation))
92
+ .catch((err) => console.error(`Can't initialize noise suppression`, err));
93
+
94
+ return () => {
95
+ deinit.current = init
96
+ .then(() => call.microphone.disableNoiseCancellation())
97
+ .then(() => noiseCancellation.dispose())
98
+ .then(() => unsubscribe());
99
+ };
100
+ }, [call, isSupported, noiseCancellation]);
101
+
102
+ return (
103
+ <NoiseCancellationContext.Provider
104
+ value={{
105
+ isSupported,
106
+ isEnabled,
107
+ setEnabled: (enabledOrSetter) => {
108
+ if (!noiseCancellation) return;
109
+ const enable =
110
+ typeof enabledOrSetter === 'function'
111
+ ? enabledOrSetter(isEnabled)
112
+ : enabledOrSetter;
113
+ if (enable) {
114
+ noiseCancellation.enable();
115
+ } else {
116
+ noiseCancellation.disable();
117
+ }
118
+ },
119
+ }}
120
+ >
121
+ {children}
122
+ </NoiseCancellationContext.Provider>
123
+ );
124
+ };
@@ -0,0 +1 @@
1
+ export * from './NoiseCancellationProvider';
@@ -12,6 +12,7 @@ export * from './DropdownSelect';
12
12
  export * from './Icon';
13
13
  export * from './LoadingIndicator';
14
14
  export * from './Menu';
15
+ export * from './NoiseCancellation';
15
16
  export * from './Notification';
16
17
  export * from './RingingCall';
17
18
  export * from './Permissions';