@stream-io/video-react-sdk 1.2.11 → 1.2.13
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.
- package/CHANGELOG.md +20 -0
- package/dist/index.cjs.js +82 -92
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +83 -93
- package/dist/index.es.js.map +1 -1
- package/dist/src/components/BackgroundFilters/BackgroundFilters.d.ts +7 -0
- package/package.json +9 -6
- package/src/components/BackgroundFilters/BackgroundFilters.tsx +136 -129
- package/src/components/CallStats/CallStats.tsx +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
### [1.2.13](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.2.12...@stream-io/video-react-sdk-1.2.13) (2024-07-02)
|
|
6
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
* `@stream-io/video-client` updated to version `1.4.4`
|
|
10
|
+
* `@stream-io/video-filters-web` updated to version `0.1.3`
|
|
11
|
+
* `@stream-io/video-react-bindings` updated to version `0.4.48`
|
|
12
|
+
* `@stream-io/audio-filters-web` updated to version `0.2.1`
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* refactor background filters ([#1415](https://github.com/GetStream/stream-video-js/issues/1415)) ([deb6da2](https://github.com/GetStream/stream-video-js/commit/deb6da238f541c733451e84b198434671da8dceb))
|
|
17
|
+
|
|
18
|
+
### [1.2.12](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.2.11...@stream-io/video-react-sdk-1.2.12) (2024-07-02)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* **CallStats:** stat card labels translations ([#1429](https://github.com/GetStream/stream-video-js/issues/1429)) ([51132cd](https://github.com/GetStream/stream-video-js/commit/51132cd5f939ef6cebe1f7ad26a1576a6f92f71f))
|
|
24
|
+
|
|
5
25
|
### [1.2.11](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.2.10...@stream-io/video-react-sdk-1.2.11) (2024-06-28)
|
|
6
26
|
|
|
7
27
|
### Dependency Updates
|
package/dist/index.cjs.js
CHANGED
|
@@ -5,6 +5,7 @@ var videoReactBindings = require('@stream-io/video-react-bindings');
|
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
var react = require('react');
|
|
7
7
|
var clsx = require('clsx');
|
|
8
|
+
var reactDom = require('react-dom');
|
|
8
9
|
var videoFiltersWeb = require('@stream-io/video-filters-web');
|
|
9
10
|
var react$1 = require('@floating-ui/react');
|
|
10
11
|
var chart_js = require('chart.js');
|
|
@@ -74,7 +75,7 @@ const useBackgroundFilters = () => {
|
|
|
74
75
|
* in your project before using this component.
|
|
75
76
|
*/
|
|
76
77
|
const BackgroundFiltersProvider = (props) => {
|
|
77
|
-
const { children, backgroundImages = [], backgroundFilter: bgFilterFromProps = undefined, backgroundImage: bgImageFromProps = undefined, backgroundBlurLevel: bgBlurLevelFromProps = 'high', tfFilePath, modelFilePath, basePath, } = props;
|
|
78
|
+
const { children, backgroundImages = [], backgroundFilter: bgFilterFromProps = undefined, backgroundImage: bgImageFromProps = undefined, backgroundBlurLevel: bgBlurLevelFromProps = 'high', tfFilePath, modelFilePath, basePath, onError, } = props;
|
|
78
79
|
const [backgroundFilter, setBackgroundFilter] = react.useState(bgFilterFromProps);
|
|
79
80
|
const [backgroundImage, setBackgroundImage] = react.useState(bgImageFromProps);
|
|
80
81
|
const [backgroundBlurLevel, setBackgroundBlurLevel] = react.useState(bgBlurLevelFromProps);
|
|
@@ -104,6 +105,11 @@ const BackgroundFiltersProvider = (props) => {
|
|
|
104
105
|
.then(setTfLite)
|
|
105
106
|
.catch((err) => console.error('Failed to load TFLite', err));
|
|
106
107
|
}, [basePath, isSupported, modelFilePath, tfFilePath]);
|
|
108
|
+
const handleError = react.useCallback((error) => {
|
|
109
|
+
videoClient.getLogger(['filters'])('warn', 'Filter encountered an error and will be disabled');
|
|
110
|
+
disableBackgroundFilter();
|
|
111
|
+
onError?.(error);
|
|
112
|
+
}, [disableBackgroundFilter, onError]);
|
|
107
113
|
return (jsxRuntime.jsxs(BackgroundFiltersContext.Provider, { value: {
|
|
108
114
|
isSupported,
|
|
109
115
|
isReady: !!tfLite,
|
|
@@ -117,104 +123,88 @@ const BackgroundFiltersProvider = (props) => {
|
|
|
117
123
|
tfFilePath,
|
|
118
124
|
modelFilePath,
|
|
119
125
|
basePath,
|
|
120
|
-
|
|
126
|
+
onError: handleError,
|
|
127
|
+
}, children: [children, tfLite && jsxRuntime.jsx(BackgroundFilters, { tfLite: tfLite })] }));
|
|
121
128
|
};
|
|
122
129
|
const BackgroundFilters = (props) => {
|
|
123
|
-
const { tfLite } = props;
|
|
124
130
|
const call = videoReactBindings.useCall();
|
|
125
|
-
const {
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
const [width, setWidth] = react.useState(1920);
|
|
130
|
-
const [height, setHeight] = react.useState(1080);
|
|
131
|
-
// Holds a ref to the `resolve` function of the returned Promise as part
|
|
132
|
-
// of the `camera.registerFilter()` API. Once the filter is initialized,
|
|
133
|
-
// it should be called with the filtered MediaStream as an argument.
|
|
134
|
-
const signalFilterReadyRef = react.useRef();
|
|
135
|
-
const [mediaStream, setMediaStream] = react.useState();
|
|
136
|
-
const unregister = react.useRef();
|
|
131
|
+
const { children, start } = useRenderer(props.tfLite);
|
|
132
|
+
const { backgroundFilter, onError } = useBackgroundFilters();
|
|
133
|
+
const handleErrorRef = react.useRef(undefined);
|
|
134
|
+
handleErrorRef.current = onError;
|
|
137
135
|
react.useEffect(() => {
|
|
138
136
|
if (!call || !backgroundFilter)
|
|
139
137
|
return;
|
|
140
|
-
const
|
|
141
|
-
return new Promise((resolve) => {
|
|
142
|
-
signalFilterReadyRef.current = resolve;
|
|
143
|
-
setMediaStream(ms);
|
|
144
|
-
});
|
|
145
|
-
}));
|
|
138
|
+
const { unregister } = call.camera.registerFilter((ms) => start(ms, (error) => handleErrorRef.current?.(error)));
|
|
146
139
|
return () => {
|
|
147
|
-
unregister
|
|
148
|
-
.then((unregisterFilter) => unregisterFilter())
|
|
149
|
-
.then(() => (signalFilterReadyRef.current = undefined))
|
|
150
|
-
.then(() => setMediaStream(undefined))
|
|
151
|
-
.catch((err) => console.error('Failed to unregister filter', err));
|
|
140
|
+
unregister();
|
|
152
141
|
};
|
|
153
|
-
}, [backgroundFilter, call]);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
142
|
+
}, [backgroundFilter, call, start]);
|
|
143
|
+
return children;
|
|
144
|
+
};
|
|
145
|
+
const useRenderer = (tfLite) => {
|
|
146
|
+
const { backgroundFilter, backgroundBlurLevel, backgroundImage } = useBackgroundFilters();
|
|
147
|
+
const videoRef = react.useRef(null);
|
|
148
|
+
const canvasRef = react.useRef(null);
|
|
149
|
+
const bgImageRef = react.useRef(null);
|
|
150
|
+
const [videoSize, setVideoSize] = react.useState({
|
|
151
|
+
width: 1920,
|
|
152
|
+
height: 1080,
|
|
153
|
+
});
|
|
154
|
+
const start = react.useCallback((ms, onError) => {
|
|
155
|
+
let outputStream;
|
|
156
|
+
let renderer;
|
|
157
|
+
const output = new Promise((resolve, reject) => {
|
|
158
|
+
if (!backgroundFilter) {
|
|
159
|
+
reject(new Error('No filter specified'));
|
|
161
160
|
return;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
};
|
|
193
|
-
const RenderPipeline = (props) => {
|
|
194
|
-
const { tfLite, videoRef, canvasRef, backgroundImageRef } = props;
|
|
195
|
-
const { backgroundFilter, backgroundBlurLevel } = useBackgroundFilters();
|
|
196
|
-
react.useEffect(() => {
|
|
197
|
-
if (!videoRef || !canvasRef || !backgroundFilter)
|
|
198
|
-
return;
|
|
199
|
-
if (backgroundFilter === 'image' && !backgroundImageRef)
|
|
200
|
-
return;
|
|
201
|
-
const renderer = videoFiltersWeb.createRenderer(tfLite, videoRef, canvasRef, {
|
|
202
|
-
backgroundFilter,
|
|
203
|
-
backgroundImage: backgroundImageRef ?? undefined,
|
|
204
|
-
backgroundBlurLevel,
|
|
161
|
+
}
|
|
162
|
+
const videoEl = videoRef.current;
|
|
163
|
+
const canvasEl = canvasRef.current;
|
|
164
|
+
const bgImageEl = bgImageRef.current;
|
|
165
|
+
if (!videoEl || !canvasEl || (backgroundImage && !bgImageEl)) {
|
|
166
|
+
// You should start renderer in effect or event handlers
|
|
167
|
+
reject(new Error('Renderer started before elements are ready'));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
videoEl.srcObject = ms;
|
|
171
|
+
videoEl.play().then(() => {
|
|
172
|
+
const [track] = ms.getVideoTracks();
|
|
173
|
+
if (!track) {
|
|
174
|
+
reject(new Error('No video tracks in input media stream'));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const trackSettings = track.getSettings();
|
|
178
|
+
reactDom.flushSync(() => setVideoSize({
|
|
179
|
+
width: trackSettings.width ?? 0,
|
|
180
|
+
height: trackSettings.height ?? 0,
|
|
181
|
+
}));
|
|
182
|
+
renderer = videoFiltersWeb.createRenderer(tfLite, videoEl, canvasEl, {
|
|
183
|
+
backgroundFilter,
|
|
184
|
+
backgroundBlurLevel,
|
|
185
|
+
backgroundImage: bgImageEl ?? undefined,
|
|
186
|
+
}, onError);
|
|
187
|
+
outputStream = canvasEl.captureStream();
|
|
188
|
+
resolve(outputStream);
|
|
189
|
+
}, () => {
|
|
190
|
+
reject(new Error('Could not play the source video stream'));
|
|
191
|
+
});
|
|
205
192
|
});
|
|
206
|
-
return
|
|
207
|
-
|
|
193
|
+
return {
|
|
194
|
+
output,
|
|
195
|
+
stop: () => {
|
|
196
|
+
renderer?.dispose();
|
|
197
|
+
videoRef.current && (videoRef.current.srcObject = null);
|
|
198
|
+
outputStream && videoClient.disposeOfMediaStream(outputStream);
|
|
199
|
+
},
|
|
208
200
|
};
|
|
209
|
-
}, [
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
]);
|
|
217
|
-
return null;
|
|
201
|
+
}, [backgroundBlurLevel, backgroundFilter, backgroundImage, tfLite]);
|
|
202
|
+
const children = (jsxRuntime.jsxs("div", { className: "str-video__background-filters", children: [jsxRuntime.jsx("video", { className: clsx('str-video__background-filters__video', videoSize.height > videoSize.width &&
|
|
203
|
+
'str-video__background-filters__video--tall'), ref: videoRef, playsInline: true, muted: true, controls: false, ...videoSize }), backgroundImage && (jsxRuntime.jsx("img", { className: "str-video__background-filters__background-image", alt: "Background", ref: bgImageRef, src: backgroundImage, ...videoSize })), jsxRuntime.jsx("canvas", { className: "str-video__background-filters__target-canvas", ...videoSize, ref: canvasRef })] }));
|
|
204
|
+
return {
|
|
205
|
+
start,
|
|
206
|
+
children,
|
|
207
|
+
};
|
|
218
208
|
};
|
|
219
209
|
|
|
220
210
|
const useFloatingUIPreset = ({ middleware = [], placement, strategy, offset: offsetInPx = 10, }) => {
|
|
@@ -1346,13 +1336,13 @@ const CallStats = (props) => {
|
|
|
1346
1336
|
highBound: latencyHighBound,
|
|
1347
1337
|
value: callStatsReport?.publisherStats.averageRoundTripTimeInMs || 0,
|
|
1348
1338
|
};
|
|
1349
|
-
return (jsxRuntime.jsx("div", { className: "str-video__call-stats", children: callStatsReport && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "str-video__call-stats__header", children: [jsxRuntime.jsxs("h3", { className: "str-video__call-stats__heading", children: [jsxRuntime.jsx(Icon, { className: "str-video__call-stats__icon", icon: "call-latency" }), t('Call Latency')] }), jsxRuntime.jsx("p", { className: "str-video__call-stats__description", children: t('Very high latency values may reduce call quality, cause lag, and make the call less enjoyable.') })] }), jsxRuntime.jsx("div", { className: "str-video__call-stats__latencychart", children: jsxRuntime.jsx(CallStatsLatencyChart, { values: latencyBuffer }) }), jsxRuntime.jsxs("div", { className: "str-video__call-stats__header", children: [jsxRuntime.jsxs("h3", { className: "str-video__call-stats__heading", children: [jsxRuntime.jsx(Icon, { className: "str-video__call-stats__icon", icon: "network-quality" }), t('Call performance')] }), jsxRuntime.jsx("p", { className: "str-video__call-stats__description", children: t('Review the key data points below to assess call performance') })] }), jsxRuntime.jsxs("div", { className: "str-video__call-stats__card-container", children: [jsxRuntime.jsx(StatCard, { label:
|
|
1339
|
+
return (jsxRuntime.jsx("div", { className: "str-video__call-stats", children: callStatsReport && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "str-video__call-stats__header", children: [jsxRuntime.jsxs("h3", { className: "str-video__call-stats__heading", children: [jsxRuntime.jsx(Icon, { className: "str-video__call-stats__icon", icon: "call-latency" }), t('Call Latency')] }), jsxRuntime.jsx("p", { className: "str-video__call-stats__description", children: t('Very high latency values may reduce call quality, cause lag, and make the call less enjoyable.') })] }), jsxRuntime.jsx("div", { className: "str-video__call-stats__latencychart", children: jsxRuntime.jsx(CallStatsLatencyChart, { values: latencyBuffer }) }), jsxRuntime.jsxs("div", { className: "str-video__call-stats__header", children: [jsxRuntime.jsxs("h3", { className: "str-video__call-stats__heading", children: [jsxRuntime.jsx(Icon, { className: "str-video__call-stats__icon", icon: "network-quality" }), t('Call performance')] }), jsxRuntime.jsx("p", { className: "str-video__call-stats__description", children: t('Review the key data points below to assess call performance') })] }), jsxRuntime.jsxs("div", { className: "str-video__call-stats__card-container", children: [jsxRuntime.jsx(StatCard, { label: t('Region'), value: callStatsReport.datacenter }), jsxRuntime.jsx(StatCard, { label: t('Latency'), value: `${callStatsReport.publisherStats.averageRoundTripTimeInMs} ms.`, comparison: latencyComparison }), jsxRuntime.jsx(StatCard, { label: t('Receive jitter'), value: `${callStatsReport.subscriberStats.averageJitterInMs} ms.`, comparison: {
|
|
1350
1340
|
...latencyComparison,
|
|
1351
1341
|
value: callStatsReport.subscriberStats.averageJitterInMs,
|
|
1352
|
-
} }), jsxRuntime.jsx(StatCard, { label:
|
|
1342
|
+
} }), jsxRuntime.jsx(StatCard, { label: t('Publish jitter'), value: `${callStatsReport.publisherStats.averageJitterInMs} ms.`, comparison: {
|
|
1353
1343
|
...latencyComparison,
|
|
1354
1344
|
value: callStatsReport.publisherStats.averageJitterInMs,
|
|
1355
|
-
} }), jsxRuntime.jsx(StatCard, { label:
|
|
1345
|
+
} }), jsxRuntime.jsx(StatCard, { label: t('Publish resolution'), value: toFrameSize(callStatsReport.publisherStats) }), jsxRuntime.jsx(StatCard, { label: t('Publish quality drop reason'), value: callStatsReport.publisherStats.qualityLimitationReasons }), jsxRuntime.jsx(StatCard, { label: t('Receiving resolution'), value: toFrameSize(callStatsReport.subscriberStats) }), jsxRuntime.jsx(StatCard, { label: t('Receive quality drop reason'), value: callStatsReport.subscriberStats.qualityLimitationReasons }), jsxRuntime.jsx(StatCard, { label: t('Publish bitrate'), value: publishBitrate }), jsxRuntime.jsx(StatCard, { label: t('Receiving bitrate'), value: subscribeBitrate })] })] })) }));
|
|
1356
1346
|
};
|
|
1357
1347
|
const StatCardExplanation = (props) => {
|
|
1358
1348
|
const { description } = props;
|
|
@@ -2622,7 +2612,7 @@ const LivestreamPlayer = (props) => {
|
|
|
2622
2612
|
return (jsxRuntime.jsx(StreamCall, { call: call, children: jsxRuntime.jsx(LivestreamLayout, { ...layoutProps }) }));
|
|
2623
2613
|
};
|
|
2624
2614
|
|
|
2625
|
-
const [major, minor, patch] = ("1.2.
|
|
2615
|
+
const [major, minor, patch] = ("1.2.13" ).split('.');
|
|
2626
2616
|
videoClient.setSdkInfo({
|
|
2627
2617
|
type: videoClient.SfuModels.SdkType.REACT,
|
|
2628
2618
|
major,
|