@stream-io/video-react-native-sdk 1.45.1 → 1.46.0
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 +36 -2
- package/android/build.gradle +7 -1
- package/dist/commonjs/components/Call/CallContent/CallContent.js +1 -1
- package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js +78 -7
- package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js.map +1 -1
- package/dist/commonjs/components/Call/CallContent/RTCViewPipNative.js +3 -0
- package/dist/commonjs/components/Call/CallContent/RTCViewPipNative.js.map +1 -1
- package/dist/commonjs/components/Call/RingingCallContent/OutgoingCall.js +4 -12
- package/dist/commonjs/components/Call/RingingCallContent/OutgoingCall.js.map +1 -1
- package/dist/commonjs/components/Participant/ParticipantView/VideoRenderer/TrackSubscriber.js +27 -4
- package/dist/commonjs/components/Participant/ParticipantView/VideoRenderer/TrackSubscriber.js.map +1 -1
- package/dist/commonjs/index.js +14 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/modules/encryption/EncryptionManager.js +305 -0
- package/dist/commonjs/modules/encryption/EncryptionManager.js.map +1 -0
- package/dist/commonjs/modules/encryption/eventMapping.js +143 -0
- package/dist/commonjs/modules/encryption/eventMapping.js.map +1 -0
- package/dist/commonjs/modules/encryption/index.js +13 -0
- package/dist/commonjs/modules/encryption/index.js.map +1 -0
- package/dist/commonjs/modules/encryption/parity.js +6 -0
- package/dist/commonjs/modules/encryption/parity.js.map +1 -0
- package/dist/commonjs/utils/StreamVideoRN/index.js +23 -0
- package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/commonjs/utils/internal/callingx/callingx.js +29 -4
- package/dist/commonjs/utils/internal/callingx/callingx.js.map +1 -1
- package/dist/commonjs/utils/internal/iosPipTrack.js +41 -0
- package/dist/commonjs/utils/internal/iosPipTrack.js.map +1 -0
- package/dist/commonjs/utils/internal/registerSDKGlobals.js +6 -0
- package/dist/commonjs/utils/internal/registerSDKGlobals.js.map +1 -1
- package/dist/commonjs/utils/internal/ringingCallLifecycle.js +142 -0
- package/dist/commonjs/utils/internal/ringingCallLifecycle.js.map +1 -0
- package/dist/commonjs/utils/push/internal/utils.js +4 -0
- package/dist/commonjs/utils/push/internal/utils.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/module/components/Call/CallContent/CallContent.js +1 -1
- package/dist/module/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/module/components/Call/CallContent/RTCViewPipIOS.js +80 -9
- package/dist/module/components/Call/CallContent/RTCViewPipIOS.js.map +1 -1
- package/dist/module/components/Call/CallContent/RTCViewPipNative.js +3 -0
- package/dist/module/components/Call/CallContent/RTCViewPipNative.js.map +1 -1
- package/dist/module/components/Call/RingingCallContent/OutgoingCall.js +4 -12
- package/dist/module/components/Call/RingingCallContent/OutgoingCall.js.map +1 -1
- package/dist/module/components/Participant/ParticipantView/VideoRenderer/TrackSubscriber.js +28 -5
- package/dist/module/components/Participant/ParticipantView/VideoRenderer/TrackSubscriber.js.map +1 -1
- package/dist/module/index.js +5 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/modules/encryption/EncryptionManager.js +301 -0
- package/dist/module/modules/encryption/EncryptionManager.js.map +1 -0
- package/dist/module/modules/encryption/eventMapping.js +135 -0
- package/dist/module/modules/encryption/eventMapping.js.map +1 -0
- package/dist/module/modules/encryption/index.js +4 -0
- package/dist/module/modules/encryption/index.js.map +1 -0
- package/dist/module/modules/encryption/parity.js +4 -0
- package/dist/module/modules/encryption/parity.js.map +1 -0
- package/dist/module/utils/StreamVideoRN/index.js +23 -0
- package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/module/utils/internal/callingx/callingx.js +29 -4
- package/dist/module/utils/internal/callingx/callingx.js.map +1 -1
- package/dist/module/utils/internal/iosPipTrack.js +35 -0
- package/dist/module/utils/internal/iosPipTrack.js.map +1 -0
- package/dist/module/utils/internal/registerSDKGlobals.js +6 -0
- package/dist/module/utils/internal/registerSDKGlobals.js.map +1 -1
- package/dist/module/utils/internal/ringingCallLifecycle.js +134 -0
- package/dist/module/utils/internal/ringingCallLifecycle.js.map +1 -0
- package/dist/module/utils/push/internal/utils.js +4 -0
- package/dist/module/utils/push/internal/utils.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/components/Call/CallContent/RTCViewPipIOS.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts +12 -0
- package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts.map +1 -1
- package/dist/typescript/components/Call/RingingCallContent/OutgoingCall.d.ts.map +1 -1
- package/dist/typescript/components/Participant/ParticipantView/VideoRenderer/TrackSubscriber.d.ts +11 -0
- package/dist/typescript/components/Participant/ParticipantView/VideoRenderer/TrackSubscriber.d.ts.map +1 -1
- package/dist/typescript/index.d.ts +1 -0
- package/dist/typescript/index.d.ts.map +1 -1
- package/dist/typescript/modules/encryption/EncryptionManager.d.ts +190 -0
- package/dist/typescript/modules/encryption/EncryptionManager.d.ts.map +1 -0
- package/dist/typescript/modules/encryption/eventMapping.d.ts +38 -0
- package/dist/typescript/modules/encryption/eventMapping.d.ts.map +1 -0
- package/dist/typescript/modules/encryption/index.d.ts +2 -0
- package/dist/typescript/modules/encryption/index.d.ts.map +1 -0
- package/dist/typescript/modules/encryption/parity.d.ts +55 -0
- package/dist/typescript/modules/encryption/parity.d.ts.map +1 -0
- package/dist/typescript/utils/StreamVideoRN/index.d.ts +20 -1
- package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
- package/dist/typescript/utils/StreamVideoRN/types.d.ts +67 -0
- package/dist/typescript/utils/StreamVideoRN/types.d.ts.map +1 -1
- package/dist/typescript/utils/internal/callingx/callingx.d.ts +1 -1
- package/dist/typescript/utils/internal/callingx/callingx.d.ts.map +1 -1
- package/dist/typescript/utils/internal/iosPipTrack.d.ts +9 -0
- package/dist/typescript/utils/internal/iosPipTrack.d.ts.map +1 -0
- package/dist/typescript/utils/internal/registerSDKGlobals.d.ts.map +1 -1
- package/dist/typescript/utils/internal/ringingCallLifecycle.d.ts +27 -0
- package/dist/typescript/utils/internal/ringingCallLifecycle.d.ts.map +1 -0
- package/dist/typescript/utils/push/internal/utils.d.ts.map +1 -1
- package/dist/typescript/version.d.ts +1 -1
- package/ios/RTCViewPip.swift +33 -8
- package/ios/RTCViewPipManager.mm +1 -0
- package/package.json +8 -8
- package/src/components/Call/CallContent/CallContent.tsx +1 -1
- package/src/components/Call/CallContent/RTCViewPipIOS.tsx +92 -10
- package/src/components/Call/CallContent/RTCViewPipNative.tsx +13 -0
- package/src/components/Call/RingingCallContent/OutgoingCall.tsx +4 -12
- package/src/components/Participant/ParticipantView/VideoRenderer/TrackSubscriber.tsx +58 -5
- package/src/index.ts +5 -0
- package/src/modules/encryption/EncryptionManager.ts +360 -0
- package/src/modules/encryption/eventMapping.ts +151 -0
- package/src/modules/encryption/index.ts +1 -0
- package/src/modules/encryption/parity.ts +83 -0
- package/src/utils/StreamVideoRN/index.ts +24 -1
- package/src/utils/StreamVideoRN/types.ts +68 -0
- package/src/utils/internal/callingx/callingx.ts +37 -2
- package/src/utils/internal/iosPipTrack.ts +39 -0
- package/src/utils/internal/registerSDKGlobals.ts +2 -0
- package/src/utils/internal/ringingCallLifecycle.ts +148 -0
- package/src/utils/push/internal/utils.ts +4 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-native-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.46.0",
|
|
4
4
|
"description": "Stream Video SDK for React Native",
|
|
5
5
|
"author": "https://getstream.io",
|
|
6
6
|
"homepage": "https://getstream.io/video/docs/react-native/",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"!**/.*"
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@stream-io/video-client": "1.
|
|
56
|
-
"@stream-io/video-react-bindings": "1.20.
|
|
55
|
+
"@stream-io/video-client": "1.61.0",
|
|
56
|
+
"@stream-io/video-react-bindings": "1.20.4",
|
|
57
57
|
"intl-pluralrules": "2.0.1",
|
|
58
58
|
"react-native-url-polyfill": "^3.0.0",
|
|
59
59
|
"rxjs": "~7.8.2",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@react-native-firebase/messaging": ">=17.5.0",
|
|
66
66
|
"@stream-io/noise-cancellation-react-native": ">=0.1.0",
|
|
67
67
|
"@stream-io/react-native-callingx": ">=0.1.0",
|
|
68
|
-
"@stream-io/react-native-webrtc": "^145.
|
|
68
|
+
"@stream-io/react-native-webrtc": "^145.4.1",
|
|
69
69
|
"@stream-io/video-filters-react-native": ">=0.1.0",
|
|
70
70
|
"expo": ">=47.0.0",
|
|
71
71
|
"expo-notifications": "*",
|
|
@@ -117,11 +117,11 @@
|
|
|
117
117
|
"@react-native/babel-preset": "0.86.2",
|
|
118
118
|
"@react-native/jest-preset": "0.86.2",
|
|
119
119
|
"@react-native/metro-config": "0.86.2",
|
|
120
|
-
"@stream-io/noise-cancellation-react-native": "^0.11.
|
|
121
|
-
"@stream-io/react-native-callingx": "^0.11.
|
|
122
|
-
"@stream-io/react-native-webrtc": "145.
|
|
120
|
+
"@stream-io/noise-cancellation-react-native": "^0.11.1",
|
|
121
|
+
"@stream-io/react-native-callingx": "^0.11.5",
|
|
122
|
+
"@stream-io/react-native-webrtc": "145.4.1",
|
|
123
123
|
"@stream-io/typescript-config": "^0.0.0",
|
|
124
|
-
"@stream-io/video-filters-react-native": "^0.16.
|
|
124
|
+
"@stream-io/video-filters-react-native": "^0.16.2",
|
|
125
125
|
"@testing-library/jest-native": "^5.4.3",
|
|
126
126
|
"@testing-library/react-native": "13.3.3",
|
|
127
127
|
"@tsconfig/node18": "^18.2.6",
|
|
@@ -267,7 +267,7 @@ export const CallContent = ({
|
|
|
267
267
|
|
|
268
268
|
return (
|
|
269
269
|
<>
|
|
270
|
-
{!disablePictureInPicture && (
|
|
270
|
+
{Platform.OS === 'ios' && !disablePictureInPicture && (
|
|
271
271
|
<RTCViewPipIOS
|
|
272
272
|
includeLocalParticipantVideo={iOSPiPIncludeLocalParticipantVideo}
|
|
273
273
|
mirror={mirror}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type Call,
|
|
2
3
|
CallingState,
|
|
3
4
|
SfuModels,
|
|
4
5
|
hasAudio,
|
|
@@ -12,17 +13,35 @@ import {
|
|
|
12
13
|
} from '@stream-io/video-client';
|
|
13
14
|
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
14
15
|
import type { MediaStream } from '@stream-io/react-native-webrtc';
|
|
15
|
-
import React, { useEffect, useCallback, useState } from 'react';
|
|
16
|
+
import React, { useEffect, useCallback, useRef, useState } from 'react';
|
|
16
17
|
import { findNodeHandle } from 'react-native';
|
|
17
18
|
import {
|
|
18
19
|
onNativeCallClosed,
|
|
19
20
|
onNativeDimensionsUpdated,
|
|
21
|
+
type PiPBoundsChangeEvent,
|
|
22
|
+
type PiPChangeEvent,
|
|
20
23
|
RTCViewPipNative,
|
|
21
24
|
} from './RTCViewPipNative';
|
|
22
|
-
import { debounceTime } from 'rxjs';
|
|
25
|
+
import { BehaviorSubject, debounceTime } from 'rxjs';
|
|
23
26
|
import { shouldDisableIOSLocalVideoOnBackgroundRef } from '../../../utils/internal/shouldDisableIOSLocalVideoOnBackground';
|
|
24
27
|
import { useTrackDimensions } from '../../../hooks/useTrackDimensions';
|
|
25
28
|
import { isInPiPMode$ } from '../../../utils/internal/rxSubjects';
|
|
29
|
+
import TrackSubscriber from '../../Participant/ParticipantView/VideoRenderer/TrackSubscriber';
|
|
30
|
+
|
|
31
|
+
// One native view per Call instance. React Native drops the events of an
|
|
32
|
+
// unmounted view, so an event the previous call's controller queued before
|
|
33
|
+
// the replacement cannot reach the view of its successor.
|
|
34
|
+
const nativeViewKeys = new WeakMap<Call, number>();
|
|
35
|
+
let nextNativeViewKey = 0;
|
|
36
|
+
const nativeViewKeyOf = (call: Call | undefined) => {
|
|
37
|
+
if (!call) return 0;
|
|
38
|
+
let key = nativeViewKeys.get(call);
|
|
39
|
+
if (key === undefined) {
|
|
40
|
+
key = ++nextNativeViewKey;
|
|
41
|
+
nativeViewKeys.set(call, key);
|
|
42
|
+
}
|
|
43
|
+
return key;
|
|
44
|
+
};
|
|
26
45
|
|
|
27
46
|
type Props = {
|
|
28
47
|
includeLocalParticipantVideo?: boolean;
|
|
@@ -52,6 +71,39 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
|
52
71
|
StreamVideoParticipant[]
|
|
53
72
|
>(call?.state.participants ?? []);
|
|
54
73
|
|
|
74
|
+
// bounds of the native window, in logical points. While the window is on
|
|
75
|
+
// screen they are the demand of the track it renders.
|
|
76
|
+
const [pipDimensions$] = useState(
|
|
77
|
+
() => new BehaviorSubject<SfuModels.VideoDimension | undefined>(undefined),
|
|
78
|
+
);
|
|
79
|
+
const [isPipActive, setIsPipActive] = useState(false);
|
|
80
|
+
// synchronous mirror of isPipActive: native may emit the same lifecycle
|
|
81
|
+
// state twice (e.g. failedToStart after didStop) before React re-renders.
|
|
82
|
+
const isPipActiveRef = useRef(false);
|
|
83
|
+
// set once the call is closed: later native events describe a disposed window.
|
|
84
|
+
const isClosedRef = useRef(false);
|
|
85
|
+
// the handlers below are bound to the view they were rendered for; an event
|
|
86
|
+
// of a replaced view is rejected even if it were delivered.
|
|
87
|
+
const nativeViewKey = nativeViewKeyOf(call);
|
|
88
|
+
const activeNativeViewKeyRef = useRef(nativeViewKey);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
activeNativeViewKeyRef.current = nativeViewKey;
|
|
91
|
+
}, [nativeViewKey]);
|
|
92
|
+
const isStaleNativeEvent = () =>
|
|
93
|
+
isClosedRef.current || nativeViewKey !== activeNativeViewKeyRef.current;
|
|
94
|
+
const onPiPChangeRef = useRef(onPiPChange);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
onPiPChangeRef.current = onPiPChange;
|
|
97
|
+
}, [onPiPChange]);
|
|
98
|
+
|
|
99
|
+
const updatePipState = useCallback((active: boolean) => {
|
|
100
|
+
if (isPipActiveRef.current === active) return;
|
|
101
|
+
isPipActiveRef.current = active;
|
|
102
|
+
setIsPipActive(active);
|
|
103
|
+
isInPiPMode$.next(active);
|
|
104
|
+
onPiPChangeRef.current?.(active);
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
55
107
|
// we debounce the participants to avoid unnecessary rerenders
|
|
56
108
|
// that happen when participant tracks are all subscribed simultaneously
|
|
57
109
|
useEffect(() => {
|
|
@@ -86,17 +138,19 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
|
86
138
|
const nativeRef = React.useRef<any>(null);
|
|
87
139
|
|
|
88
140
|
React.useEffect(() => {
|
|
89
|
-
|
|
141
|
+
const node = findNodeHandle(nativeRef.current);
|
|
142
|
+
isClosedRef.current = false;
|
|
90
143
|
const onCallClosed = () => {
|
|
91
|
-
if (
|
|
144
|
+
if (isClosedRef.current) {
|
|
92
145
|
return;
|
|
93
146
|
}
|
|
94
|
-
|
|
95
|
-
const node = findNodeHandle(nativeRef.current);
|
|
147
|
+
isClosedRef.current = true;
|
|
96
148
|
if (node !== null) {
|
|
97
149
|
onNativeCallClosed(node);
|
|
98
150
|
}
|
|
99
151
|
shouldDisableIOSLocalVideoOnBackgroundRef.current = true;
|
|
152
|
+
pipDimensions$.next(undefined);
|
|
153
|
+
updatePipState(false);
|
|
100
154
|
};
|
|
101
155
|
const unsubFunc = call?.on('call.ended', () => {
|
|
102
156
|
videoLoggerSystem
|
|
@@ -117,7 +171,7 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
|
117
171
|
unsubFunc?.();
|
|
118
172
|
subscription?.unsubscribe();
|
|
119
173
|
};
|
|
120
|
-
}, [call]);
|
|
174
|
+
}, [call, pipDimensions$, updatePipState]);
|
|
121
175
|
|
|
122
176
|
const onDimensionsUpdated = useCallback((width: number, height: number) => {
|
|
123
177
|
const node = findNodeHandle(nativeRef.current);
|
|
@@ -150,9 +204,17 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
|
150
204
|
? mirrorOverride
|
|
151
205
|
: !!participantInSpotlight?.isLocalParticipant && direction === 'front';
|
|
152
206
|
|
|
153
|
-
const handlePiPChange = (event: { nativeEvent:
|
|
154
|
-
|
|
155
|
-
|
|
207
|
+
const handlePiPChange = (event: { nativeEvent: PiPChangeEvent }) => {
|
|
208
|
+
if (isStaleNativeEvent()) return;
|
|
209
|
+
updatePipState(event.nativeEvent.active);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const handlePiPBoundsChange = (event: {
|
|
213
|
+
nativeEvent: PiPBoundsChangeEvent;
|
|
214
|
+
}) => {
|
|
215
|
+
if (isStaleNativeEvent()) return;
|
|
216
|
+
const { width, height } = event.nativeEvent;
|
|
217
|
+
pipDimensions$.next({ width, height });
|
|
156
218
|
};
|
|
157
219
|
|
|
158
220
|
// Get participant info for avatar placeholder
|
|
@@ -193,13 +255,23 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
|
193
255
|
participantInSpotlight?.connectionQuality ??
|
|
194
256
|
SfuModels.ConnectionQuality.UNSPECIFIED;
|
|
195
257
|
|
|
258
|
+
// while the native window is on screen, its bounds - and not the hidden
|
|
259
|
+
// inline layout behind it - are the demand of the track it renders. The local
|
|
260
|
+
// preview is never subscribed to.
|
|
261
|
+
const pipTrackOwner =
|
|
262
|
+
isPipActive && participantInSpotlight?.isLocalParticipant !== true
|
|
263
|
+
? participantInSpotlight
|
|
264
|
+
: undefined;
|
|
265
|
+
|
|
196
266
|
return (
|
|
197
267
|
<>
|
|
198
268
|
<RTCViewPipNative
|
|
269
|
+
key={nativeViewKey}
|
|
199
270
|
streamURL={streamURL}
|
|
200
271
|
mirror={mirror}
|
|
201
272
|
ref={nativeRef}
|
|
202
273
|
onPiPChange={handlePiPChange}
|
|
274
|
+
onPiPBoundsChange={handlePiPBoundsChange}
|
|
203
275
|
participantName={participantName}
|
|
204
276
|
participantImageURL={participantImageURL}
|
|
205
277
|
isReconnecting={isReconnecting}
|
|
@@ -210,6 +282,16 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
|
210
282
|
isSpeaking={participantIsSpeaking}
|
|
211
283
|
connectionQuality={participantConnectionQuality}
|
|
212
284
|
/>
|
|
285
|
+
{pipTrackOwner && call && (
|
|
286
|
+
<TrackSubscriber
|
|
287
|
+
call={call}
|
|
288
|
+
participantSessionId={pipTrackOwner.sessionId}
|
|
289
|
+
trackType={trackType}
|
|
290
|
+
isVisible={true}
|
|
291
|
+
dimensions$={pipDimensions$}
|
|
292
|
+
isPipWriter
|
|
293
|
+
/>
|
|
294
|
+
)}
|
|
213
295
|
{participantInSpotlight && (
|
|
214
296
|
<DimensionsUpdatedRenderless
|
|
215
297
|
participant={participantInSpotlight}
|
|
@@ -14,10 +14,21 @@ export type PiPChangeEvent = {
|
|
|
14
14
|
active: boolean;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
export type PiPBoundsChangeEvent = {
|
|
18
|
+
/** the laid out width of the native window, in logical points */
|
|
19
|
+
width: number;
|
|
20
|
+
/** the laid out height of the native window, in logical points */
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
17
24
|
type RTCViewPipNativeProps = {
|
|
18
25
|
streamURL?: string;
|
|
19
26
|
mirror?: boolean;
|
|
20
27
|
onPiPChange?: (event: { nativeEvent: PiPChangeEvent }) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Reports the actual laid out bounds of the native Picture in Picture window.
|
|
30
|
+
*/
|
|
31
|
+
onPiPBoundsChange?: (event: { nativeEvent: PiPBoundsChangeEvent }) => void;
|
|
21
32
|
/** The participant's name for the avatar placeholder when video is disabled */
|
|
22
33
|
participantName?: string;
|
|
23
34
|
/** The URL string for the participant's profile image */
|
|
@@ -84,6 +95,8 @@ export const RTCViewPipNative = React.memo(
|
|
|
84
95
|
// eslint-disable-next-line react/prop-types
|
|
85
96
|
onPiPChange={props.onPiPChange}
|
|
86
97
|
// eslint-disable-next-line react/prop-types
|
|
98
|
+
onPiPBoundsChange={props.onPiPBoundsChange}
|
|
99
|
+
// eslint-disable-next-line react/prop-types
|
|
87
100
|
participantName={props.participantName}
|
|
88
101
|
// eslint-disable-next-line react/prop-types
|
|
89
102
|
participantImageURL={props.participantImageURL}
|
|
@@ -3,12 +3,12 @@ import { StyleSheet, Text, View, type ViewStyle } from 'react-native';
|
|
|
3
3
|
import { UserInfo } from './UserInfo';
|
|
4
4
|
import { Z_INDEX } from '../../../constants';
|
|
5
5
|
import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
|
|
6
|
-
import { MediaStream, RTCView } from '@stream-io/react-native-webrtc';
|
|
7
6
|
import { useTheme } from '../../../contexts/ThemeContext';
|
|
8
7
|
import {
|
|
9
8
|
OutgoingCallControls as DefaultOutgoingCallControls,
|
|
10
9
|
type OutgoingCallControlsProps,
|
|
11
10
|
} from '../CallControls';
|
|
11
|
+
import { LobbyCameraPreview } from '../Lobby';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Props for the OutgoingCall Component.
|
|
@@ -107,11 +107,9 @@ const Background = () => {
|
|
|
107
107
|
theme: { colors, outgoingCall },
|
|
108
108
|
} = useTheme();
|
|
109
109
|
const { useCameraState } = useCallStateHooks();
|
|
110
|
-
const {
|
|
111
|
-
const localVideoStream = camera.state.mediaStream as unknown as
|
|
112
|
-
MediaStream | undefined;
|
|
110
|
+
const { optimisticIsMute } = useCameraState();
|
|
113
111
|
|
|
114
|
-
if (
|
|
112
|
+
if (optimisticIsMute) {
|
|
115
113
|
return (
|
|
116
114
|
<View
|
|
117
115
|
style={[
|
|
@@ -130,13 +128,7 @@ const Background = () => {
|
|
|
130
128
|
outgoingCall.background,
|
|
131
129
|
]}
|
|
132
130
|
>
|
|
133
|
-
<
|
|
134
|
-
streamURL={localVideoStream.toURL()}
|
|
135
|
-
zOrder={Z_INDEX.IN_BACK}
|
|
136
|
-
style={StyleSheet.absoluteFill}
|
|
137
|
-
mirror
|
|
138
|
-
objectFit="cover"
|
|
139
|
-
/>
|
|
131
|
+
<LobbyCameraPreview />
|
|
140
132
|
</View>
|
|
141
133
|
);
|
|
142
134
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
2
3
|
import {
|
|
3
4
|
Call,
|
|
4
5
|
CallingState,
|
|
@@ -16,7 +17,12 @@ import {
|
|
|
16
17
|
distinctUntilKeyChanged,
|
|
17
18
|
filter,
|
|
18
19
|
map,
|
|
20
|
+
of,
|
|
19
21
|
} from 'rxjs';
|
|
22
|
+
import {
|
|
23
|
+
getIosPipTrack$,
|
|
24
|
+
setIosPipTrack,
|
|
25
|
+
} from '../../../../utils/internal/iosPipTrack';
|
|
20
26
|
|
|
21
27
|
type TrackSubscriberProps = {
|
|
22
28
|
participantSessionId: string;
|
|
@@ -28,6 +34,13 @@ type TrackSubscriberProps = {
|
|
|
28
34
|
* the last reported layout survives a remount of this component.
|
|
29
35
|
*/
|
|
30
36
|
dimensions$: BehaviorSubject<SfuModels.VideoDimension | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Set by the native iOS Picture in Picture window, whose bounds this
|
|
39
|
+
* subscriber requests. While it does, the inline views of the same track
|
|
40
|
+
* stop requesting their own, hidden layout. Only rendered on iOS; on other
|
|
41
|
+
* platforms the gate is always open.
|
|
42
|
+
*/
|
|
43
|
+
isPipWriter?: boolean;
|
|
31
44
|
};
|
|
32
45
|
|
|
33
46
|
/**
|
|
@@ -40,10 +53,20 @@ type TrackSubscriberProps = {
|
|
|
40
53
|
* This component is used to unsubscribe to video track and subscribe only to the audio track of the participant (by passing undefined dimensions) in the following cases:
|
|
41
54
|
* 1. When the participant stops publishing the video track
|
|
42
55
|
* 2. When the participant becomes invisible
|
|
56
|
+
*
|
|
57
|
+
* The same component serves the native iOS Picture in Picture window, which
|
|
58
|
+
* renders one of the tracks too. Its bounds take precedence over the inline
|
|
59
|
+
* views of that track for as long as it is on screen.
|
|
43
60
|
*/
|
|
44
61
|
const TrackSubscriber = (props: TrackSubscriberProps) => {
|
|
45
|
-
const {
|
|
46
|
-
|
|
62
|
+
const {
|
|
63
|
+
call,
|
|
64
|
+
participantSessionId,
|
|
65
|
+
trackType,
|
|
66
|
+
isVisible,
|
|
67
|
+
dimensions$,
|
|
68
|
+
isPipWriter,
|
|
69
|
+
} = props;
|
|
47
70
|
|
|
48
71
|
useEffect(() => {
|
|
49
72
|
const requestTrackWithDimensions = (
|
|
@@ -75,12 +98,28 @@ const TrackSubscriber = (props: TrackSubscriberProps) => {
|
|
|
75
98
|
map((callingState) => callingState === CallingState.JOINED),
|
|
76
99
|
);
|
|
77
100
|
|
|
101
|
+
const trackKey = { sessionId: participantSessionId, trackType };
|
|
102
|
+
const pipTrack$ = Platform.OS === 'ios' ? getIosPipTrack$(call) : undefined;
|
|
103
|
+
if (isPipWriter && pipTrack$) setIosPipTrack(call, trackKey);
|
|
104
|
+
const canWrite$ = pipTrack$
|
|
105
|
+
? pipTrack$.pipe(
|
|
106
|
+
map((track) => {
|
|
107
|
+
const sameTrack =
|
|
108
|
+
track?.sessionId === participantSessionId &&
|
|
109
|
+
track?.trackType === trackType;
|
|
110
|
+
return isPipWriter ? sameTrack : !sameTrack;
|
|
111
|
+
}),
|
|
112
|
+
distinctUntilChanged(),
|
|
113
|
+
)
|
|
114
|
+
: of(true);
|
|
115
|
+
|
|
78
116
|
const subscription = combineLatest([
|
|
79
117
|
dimensions$,
|
|
80
118
|
isPublishingTrack$,
|
|
81
119
|
isJoinedState$,
|
|
82
|
-
|
|
83
|
-
|
|
120
|
+
canWrite$,
|
|
121
|
+
]).subscribe(([dimension, isPublishing, isJoined, canWrite]) => {
|
|
122
|
+
if (isJoined && canWrite) {
|
|
84
123
|
if (!isVisible || !isPublishing) {
|
|
85
124
|
requestTrackWithDimensions(DebounceType.MEDIUM, undefined);
|
|
86
125
|
} else if (dimension) {
|
|
@@ -91,8 +130,22 @@ const TrackSubscriber = (props: TrackSubscriberProps) => {
|
|
|
91
130
|
|
|
92
131
|
return () => {
|
|
93
132
|
subscription.unsubscribe();
|
|
133
|
+
if (isPipWriter && pipTrack$) {
|
|
134
|
+
// Let sibling inline views finish unmounting before handing back demand.
|
|
135
|
+
queueMicrotask(() => {
|
|
136
|
+
if (pipTrack$.getValue() === trackKey)
|
|
137
|
+
setIosPipTrack(call, undefined);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
94
140
|
};
|
|
95
|
-
}, [
|
|
141
|
+
}, [
|
|
142
|
+
call,
|
|
143
|
+
participantSessionId,
|
|
144
|
+
trackType,
|
|
145
|
+
isVisible,
|
|
146
|
+
dimensions$,
|
|
147
|
+
isPipWriter,
|
|
148
|
+
]);
|
|
96
149
|
|
|
97
150
|
return null;
|
|
98
151
|
};
|
package/src/index.ts
CHANGED
|
@@ -36,6 +36,11 @@ export * from './modules/call-manager';
|
|
|
36
36
|
// Explicitly re-exporting to resolve ambiguity.
|
|
37
37
|
export { StreamVideo } from './providers/StreamVideo';
|
|
38
38
|
export { StreamCall } from './providers/StreamCall';
|
|
39
|
+
// Overriding 'EncryptionManager' from '@stream-io/video-client' with the React
|
|
40
|
+
// Native implementation, which is backed by the native WebRTC transform. React
|
|
41
|
+
// Native Web is not supported: `isSupported()` returns false there rather than
|
|
42
|
+
// falling back to the web manager.
|
|
43
|
+
export { EncryptionManager } from './modules/encryption';
|
|
39
44
|
export * from './providers/NoiseCancellation';
|
|
40
45
|
|
|
41
46
|
setClientDetails();
|