@stream-io/video-react-sdk 0.5.8 → 0.5.10

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/dist/index.es.js CHANGED
@@ -3,8 +3,9 @@ export * from '@stream-io/video-client';
3
3
  import { useCall, useCallStateHooks, useI18n, Restricted, useConnectedUser, StreamCallProvider, StreamVideoProvider } from '@stream-io/video-react-bindings';
4
4
  export * from '@stream-io/video-react-bindings';
5
5
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
- import { useState, useEffect, Fragment as Fragment$1, createContext, useContext, useCallback, useMemo, useRef, forwardRef, isValidElement, useLayoutEffect } from 'react';
6
+ import { useState, useEffect, Fragment as Fragment$1, createContext, useContext, useCallback, useRef, useMemo, forwardRef, isValidElement, useLayoutEffect } from 'react';
7
7
  import clsx from 'clsx';
8
+ import { isPlatformSupported, loadTFLite, createRenderer } from '@stream-io/video-filters-web';
8
9
  import { useFloating, offset, shift, flip, size, autoUpdate, FloatingOverlay, FloatingPortal, useListItem, useListNavigation, useTypeahead, useClick, useDismiss, useRole, useInteractions, FloatingFocusManager, FloatingList, useHover } from '@floating-ui/react';
9
10
  import { Chart, CategoryScale, LinearScale, LineElement, PointElement } from 'chart.js';
10
11
  import { Line } from 'react-chartjs-2';
@@ -50,6 +51,157 @@ const AvatarFallback = ({ className, names, style, }) => {
50
51
  return (jsx("div", { className: clsx('str-video__avatar--initials-fallback', className), style: style, children: jsxs("div", { children: [names[0][0], names[1]?.[0]] }) }));
51
52
  };
52
53
 
54
+ /**
55
+ * The context for the background filters.
56
+ */
57
+ const BackgroundFiltersContext = createContext(undefined);
58
+ /**
59
+ * A hook to access the background filters context API.
60
+ */
61
+ const useBackgroundFilters = () => {
62
+ const context = useContext(BackgroundFiltersContext);
63
+ if (!context) {
64
+ throw new Error('useBackgroundFilters must be used within a BackgroundFiltersProvider');
65
+ }
66
+ return context;
67
+ };
68
+ /**
69
+ * A provider component that enables the use of background filters in your app.
70
+ *
71
+ * Please make sure you have the `@stream-io/video-filters-web` package installed
72
+ * in your project before using this component.
73
+ */
74
+ const BackgroundFiltersProvider = (props) => {
75
+ const { children, isBlurringEnabled = true, backgroundImages = [], backgroundFilter: bgFilterFromProps = undefined, backgroundImage: bgImageFromProps = undefined, backgroundBlurLevel: bgBlurLevelFromProps = 'high', tfFilePath, modelFilePath, basePath, } = props;
76
+ const [backgroundFilter, setBackgroundFilter] = useState(bgFilterFromProps);
77
+ const [backgroundImage, setBackgroundImage] = useState(bgImageFromProps);
78
+ const [backgroundBlurLevel, setBackgroundBlurLevel] = useState(bgBlurLevelFromProps);
79
+ const applyBackgroundImageFilter = useCallback((imageUrl) => {
80
+ setBackgroundFilter('image');
81
+ setBackgroundImage(imageUrl);
82
+ }, []);
83
+ const applyBackgroundBlurFilter = useCallback((blurLevel = 'high') => {
84
+ setBackgroundFilter('blur');
85
+ setBackgroundBlurLevel(blurLevel);
86
+ }, []);
87
+ const disableBackgroundFilter = useCallback(() => {
88
+ setBackgroundFilter(undefined);
89
+ setBackgroundImage(undefined);
90
+ setBackgroundBlurLevel('high');
91
+ }, []);
92
+ const [isSupported, setIsSupported] = useState(false);
93
+ useEffect(() => {
94
+ isPlatformSupported().then(setIsSupported);
95
+ }, []);
96
+ const [tfLite, setTfLite] = useState();
97
+ useEffect(() => {
98
+ // don't try to load TFLite if the platform is not supported
99
+ if (!isSupported)
100
+ return;
101
+ loadTFLite({ basePath, modelFilePath, tfFilePath })
102
+ .then(setTfLite)
103
+ .catch((err) => console.error('Failed to load TFLite', err));
104
+ }, [basePath, isSupported, modelFilePath, tfFilePath]);
105
+ return (jsxs(BackgroundFiltersContext.Provider, { value: {
106
+ isSupported,
107
+ isReady: !!tfLite,
108
+ backgroundImage,
109
+ backgroundBlurLevel,
110
+ backgroundFilter,
111
+ disableBackgroundFilter,
112
+ applyBackgroundBlurFilter,
113
+ applyBackgroundImageFilter,
114
+ backgroundImages,
115
+ isBlurringEnabled,
116
+ tfFilePath,
117
+ modelFilePath,
118
+ basePath,
119
+ }, children: [children, tfLite && backgroundFilter && jsx(BackgroundFilters, { tfLite: tfLite })] }));
120
+ };
121
+ const BackgroundFilters = (props) => {
122
+ const { tfLite } = props;
123
+ const call = useCall();
124
+ const { backgroundImage, backgroundFilter } = useBackgroundFilters();
125
+ const [videoRef, setVideoRef] = useState(null);
126
+ const [bgImageRef, setBgImageRef] = useState(null);
127
+ const [canvasRef, setCanvasRef] = useState(null);
128
+ const [width, setWidth] = useState(1920);
129
+ const [height, setHeight] = useState(1080);
130
+ const resolveFilterRef = useRef();
131
+ const [mediaStream, setMediaStream] = useState();
132
+ const registerFilterRef = useRef(Promise.resolve(async () => { }));
133
+ useEffect(() => {
134
+ if (!call || !backgroundFilter)
135
+ return;
136
+ registerFilterRef.current = registerFilterRef.current.then(() => call.camera.registerFilter(async (ms) => {
137
+ return new Promise((resolve) => {
138
+ setMediaStream(ms);
139
+ resolveFilterRef.current = resolve;
140
+ });
141
+ }));
142
+ return () => {
143
+ registerFilterRef.current
144
+ .then((unregister) => unregister())
145
+ .then(() => setMediaStream(undefined))
146
+ .catch((err) => console.error('Failed to unregister filter', err));
147
+ };
148
+ }, [backgroundFilter, call]);
149
+ useEffect(() => {
150
+ if (!mediaStream || !videoRef || !canvasRef)
151
+ return;
152
+ const handleOnPlay = () => {
153
+ const [track] = mediaStream.getVideoTracks();
154
+ if (track) {
155
+ const { width: w = 0, height: h = 0 } = track.getSettings();
156
+ setWidth(w);
157
+ setHeight(h);
158
+ }
159
+ const resolveFilter = resolveFilterRef.current;
160
+ if (!resolveFilter)
161
+ return;
162
+ const filter = canvasRef.captureStream();
163
+ resolveFilter(filter);
164
+ };
165
+ videoRef.addEventListener('play', handleOnPlay);
166
+ videoRef.srcObject = mediaStream;
167
+ videoRef.play().catch((err) => console.error('Failed to play video', err));
168
+ return () => {
169
+ videoRef.removeEventListener('play', handleOnPlay);
170
+ videoRef.srcObject = null;
171
+ };
172
+ }, [canvasRef, mediaStream, videoRef]);
173
+ return (jsxs("div", { className: "str-video__background-filters", style: {
174
+ width: `${width}px`,
175
+ height: `${height}px`,
176
+ }, children: [mediaStream && (jsx(RenderPipeline, { tfLite: tfLite, videoRef: videoRef, canvasRef: canvasRef, backgroundImageRef: bgImageRef })), jsx("video", { className: clsx('str-video__background-filters__video', height > width && 'str-video__background-filters__video--tall'), ref: setVideoRef, autoPlay: true, playsInline: true, controls: false, width: width, height: height, muted: true, loop: true }), backgroundImage && (jsx("img", { className: "str-video__background-filters__background-image", alt: "Background", ref: setBgImageRef, src: backgroundImage, width: width, height: height }, backgroundImage)), jsx("canvas", { className: "str-video__background-filters__target-canvas", width: width, height: height, ref: setCanvasRef }, `key-${width}${height}`)] }));
177
+ };
178
+ const RenderPipeline = (props) => {
179
+ const { tfLite, videoRef, canvasRef, backgroundImageRef } = props;
180
+ const { backgroundFilter, backgroundBlurLevel } = useBackgroundFilters();
181
+ useEffect(() => {
182
+ if (!videoRef || !canvasRef || !backgroundFilter)
183
+ return;
184
+ if (backgroundFilter === 'image' && !backgroundImageRef)
185
+ return;
186
+ const renderer = createRenderer(tfLite, videoRef, canvasRef, {
187
+ backgroundFilter,
188
+ backgroundImage: backgroundImageRef ?? undefined,
189
+ backgroundBlurLevel,
190
+ });
191
+ return () => {
192
+ renderer.dispose();
193
+ };
194
+ }, [
195
+ backgroundBlurLevel,
196
+ backgroundFilter,
197
+ backgroundImageRef,
198
+ canvasRef,
199
+ tfLite,
200
+ videoRef,
201
+ ]);
202
+ return null;
203
+ };
204
+
53
205
  const useFloatingUIPreset = ({ placement, strategy, offset: offsetInPx = 10, }) => {
54
206
  const { refs, x, y, update, elements: { domReference, floating }, } = useFloating({
55
207
  placement,
@@ -2281,7 +2433,7 @@ const VerticalScrollButtons = ({ scrollWrapper, }) => {
2281
2433
  };
2282
2434
  const hasScreenShare = (p) => !!p?.publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE);
2283
2435
 
2284
- const [major, minor, patch] = ("0.5.8" ).split('.');
2436
+ const [major, minor, patch] = ("0.5.10" ).split('.');
2285
2437
  setSdkInfo({
2286
2438
  type: SfuModels.SdkType.REACT,
2287
2439
  major,
@@ -2289,5 +2441,5 @@ setSdkInfo({
2289
2441
  patch,
2290
2442
  });
2291
2443
 
2292
- export { AcceptCallButton, Audio, Avatar, AvatarFallback, BaseVideo, CallControls, CallParticipantListing, CallParticipantListingItem, CallParticipantsList, CallPreview, CallRecordingList, CallRecordingListHeader, CallRecordingListItem, CallStats, CallStatsButton, CallStatsLatencyChart, CancelCallButton, CancelCallConfirmButton, CompositeButton, DefaultParticipantViewUI, DefaultReactionsMenu, DefaultScreenShareOverlay, DefaultVideoPlaceholder, DeviceSelector, DeviceSelectorAudioInput, DeviceSelectorAudioOutput, DeviceSelectorVideo, DeviceSettings, DropDownSelect, DropDownSelectOption, EmptyCallRecordingListing, GenericMenu, GenericMenuButtonItem, Icon, IconButton, LivestreamLayout, LoadingCallRecordingListing, LoadingIndicator, MenuToggle, MenuVisualType, Notification, PaginatedGridLayout, ParticipantActionsContextMenu, ParticipantDetails, ParticipantView, ParticipantViewContext, ParticipantsAudio, PermissionNotification, PermissionRequestList, PermissionRequests, ReactionsButton, RecordCallButton, RecordCallConfirmationButton, RecordingInProgressNotification, RingingCall, RingingCallControls, ScreenShareButton, SearchInput, SearchResults, SpeakerLayout, SpeakingWhileMutedNotification, SpeechIndicator, StatCard, StatCardExplanation, StatsTag, Statuses, StreamCall, StreamTheme, StreamVideo, TextButton, ToggleAudioOutputButton, ToggleAudioPreviewButton, ToggleAudioPublishingButton, ToggleVideoPreviewButton, ToggleVideoPublishingButton, Tooltip, Video$1 as Video, VideoPreview, WithTooltip, defaultReactions, translations, useHorizontalScrollPosition, useMenuContext, useParticipantViewContext, usePersistedDevicePreferences, useRequestPermission, useTrackElementVisibility, useVerticalScrollPosition };
2444
+ export { AcceptCallButton, Audio, Avatar, AvatarFallback, BackgroundFiltersProvider, BaseVideo, CallControls, CallParticipantListing, CallParticipantListingItem, CallParticipantsList, CallPreview, CallRecordingList, CallRecordingListHeader, CallRecordingListItem, CallStats, CallStatsButton, CallStatsLatencyChart, CancelCallButton, CancelCallConfirmButton, CompositeButton, DefaultParticipantViewUI, DefaultReactionsMenu, DefaultScreenShareOverlay, DefaultVideoPlaceholder, DeviceSelector, DeviceSelectorAudioInput, DeviceSelectorAudioOutput, DeviceSelectorVideo, DeviceSettings, DropDownSelect, DropDownSelectOption, EmptyCallRecordingListing, GenericMenu, GenericMenuButtonItem, Icon, IconButton, LivestreamLayout, LoadingCallRecordingListing, LoadingIndicator, MenuToggle, MenuVisualType, Notification, PaginatedGridLayout, ParticipantActionsContextMenu, ParticipantDetails, ParticipantView, ParticipantViewContext, ParticipantsAudio, PermissionNotification, PermissionRequestList, PermissionRequests, ReactionsButton, RecordCallButton, RecordCallConfirmationButton, RecordingInProgressNotification, RingingCall, RingingCallControls, ScreenShareButton, SearchInput, SearchResults, SpeakerLayout, SpeakingWhileMutedNotification, SpeechIndicator, StatCard, StatCardExplanation, StatsTag, Statuses, StreamCall, StreamTheme, StreamVideo, TextButton, ToggleAudioOutputButton, ToggleAudioPreviewButton, ToggleAudioPublishingButton, ToggleVideoPreviewButton, ToggleVideoPublishingButton, Tooltip, Video$1 as Video, VideoPreview, WithTooltip, defaultReactions, translations, useBackgroundFilters, useHorizontalScrollPosition, useMenuContext, useParticipantViewContext, usePersistedDevicePreferences, useRequestPermission, useTrackElementVisibility, useVerticalScrollPosition };
2293
2445
  //# sourceMappingURL=index.es.js.map