@livekit/react-native 0.2.2 → 1.0.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/README.md +38 -23
- package/android/local.properties +1 -1
- package/ios/LivekitReactNative.xcodeproj/project.xcworkspace/contents.xcworkspacedata +3 -0
- package/ios/LivekitReactNative.xcodeproj/project.xcworkspace/xcuserdata/davidliu.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/lib/commonjs/audio/AudioSession.js +1 -1
- package/lib/commonjs/audio/AudioSession.js.map +1 -1
- package/lib/commonjs/components/VideoView.js +4 -5
- package/lib/commonjs/components/VideoView.js.map +1 -1
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/audio/AudioSession.js +1 -1
- package/lib/module/audio/AudioSession.js.map +1 -1
- package/lib/module/components/VideoView.js +5 -4
- package/lib/module/components/VideoView.js.map +1 -1
- package/lib/module/index.js +11 -0
- package/lib/module/index.js.map +1 -1
- package/package.json +15 -3
- package/src/audio/AudioSession.ts +1 -1
- package/src/components/VideoView.tsx +4 -10
- package/src/index.tsx +12 -0
package/README.md
CHANGED
|
@@ -1,52 +1,64 @@
|
|
|
1
|
+
<!--BEGIN_BANNER_IMAGE--><!--END_BANNER_IMAGE-->
|
|
2
|
+
|
|
1
3
|
# livekit-react-native
|
|
2
4
|
|
|
5
|
+
<!--BEGIN_DESCRIPTION-->
|
|
3
6
|
LiveKit Client SDK for React Native. (beta)
|
|
7
|
+
<!--END_DESCRIPTION-->
|
|
8
|
+
|
|
9
|
+
> **Info**
|
|
10
|
+
> Beta status currently
|
|
4
11
|
|
|
5
12
|
## Installation
|
|
6
13
|
|
|
7
14
|
### NPM
|
|
15
|
+
|
|
8
16
|
```sh
|
|
9
|
-
npm install
|
|
17
|
+
npm install @livekit/react-native
|
|
10
18
|
```
|
|
11
19
|
|
|
12
20
|
### Yarn
|
|
21
|
+
|
|
13
22
|
```sh
|
|
14
|
-
yarn add
|
|
23
|
+
yarn add @livekit/react-native
|
|
15
24
|
```
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
This library depends on `react-native-webrtc`, which has additional installation instructions found here:
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
- [iOS Installation Guide](https://github.com/react-native-webrtc/react-native-webrtc/blob/master/Documentation/iOSInstallation.md)
|
|
29
|
+
- [Android Installation Guide](https://github.com/react-native-webrtc/react-native-webrtc/blob/master/Documentation/AndroidInstallation.md)
|
|
21
30
|
|
|
22
31
|
## Example app
|
|
23
32
|
|
|
24
33
|
We've included an [example app](example/) that you can try out.
|
|
25
34
|
|
|
35
|
+
## Beta limitations
|
|
36
|
+
|
|
37
|
+
This library does not currently support simulcast due to a limitation with `react-native-webrtc`
|
|
38
|
+
|
|
26
39
|
## Usage
|
|
27
40
|
|
|
28
|
-
In your `index.js` file, setup the LiveKit SDK by calling `registerGlobals()`.
|
|
41
|
+
In your `index.js` file, setup the LiveKit SDK by calling `registerGlobals()`.
|
|
29
42
|
This sets up the required WebRTC libraries for use in Javascript, and is needed for LiveKit to work.
|
|
30
43
|
|
|
31
44
|
```js
|
|
32
|
-
import { registerGlobals } from
|
|
45
|
+
import { registerGlobals } from '@livekit/react-native';
|
|
33
46
|
|
|
34
47
|
// ...
|
|
35
48
|
|
|
36
|
-
registerGlobals()
|
|
49
|
+
registerGlobals();
|
|
37
50
|
```
|
|
38
51
|
|
|
39
52
|
A Room object can then be created and connected to.
|
|
40
53
|
|
|
41
54
|
```js
|
|
42
|
-
|
|
43
55
|
import { Participant, Room, Track } from 'livekit-client';
|
|
44
|
-
import { useRoom, AudioSession, VideoView } from 'livekit
|
|
56
|
+
import { useRoom, AudioSession, VideoView } from '@livekit/react-native';
|
|
45
57
|
|
|
46
58
|
/*...*/
|
|
47
59
|
|
|
48
60
|
// Create a room state
|
|
49
|
-
const [room
|
|
61
|
+
const [room] = useState(() => new Room());
|
|
50
62
|
|
|
51
63
|
// Get the participants from the room
|
|
52
64
|
const { participants } = useRoom(room);
|
|
@@ -55,13 +67,16 @@ useEffect(() => {
|
|
|
55
67
|
AudioSession.startAudioSession();
|
|
56
68
|
room.connect(url, token, {});
|
|
57
69
|
return () => {
|
|
58
|
-
room.disconnect()
|
|
70
|
+
room.disconnect();
|
|
59
71
|
AudioSession.stopAudioSession();
|
|
60
|
-
}
|
|
72
|
+
};
|
|
61
73
|
}, [url, token, room]);
|
|
62
74
|
|
|
63
75
|
const videoView = participants.length > 0 && (
|
|
64
|
-
<VideoView
|
|
76
|
+
<VideoView
|
|
77
|
+
style={{ flex: 1, width: '100%' }}
|
|
78
|
+
videoTrack={participants[0].getTrack(Track.Source.Camera)?.videoTrack}
|
|
79
|
+
/>
|
|
65
80
|
);
|
|
66
81
|
```
|
|
67
82
|
|
|
@@ -81,7 +96,7 @@ The example app uses [@voximplant/react-native-foreground-service](https://githu
|
|
|
81
96
|
Ensure that the service is labelled a `mediaProjection` service like so:
|
|
82
97
|
|
|
83
98
|
```
|
|
84
|
-
<service android:name="com.voximplant.foregroundservice.VIForegroundService"
|
|
99
|
+
<service android:name="com.voximplant.foregroundservice.VIForegroundService"
|
|
85
100
|
android:foregroundServiceType="mediaProjection" />
|
|
86
101
|
```
|
|
87
102
|
|
|
@@ -93,10 +108,10 @@ iOS screenshare requires adding a Broadcast Extension to your iOS project. Follo
|
|
|
93
108
|
|
|
94
109
|
https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-ios-sdk/#screen-sharing-integration
|
|
95
110
|
|
|
96
|
-
It involves copying the files found in this [sample project](https://github.com/jitsi/jitsi-meet-sdk-samples/tree/18c35f7625b38233579ff34f761f4c126ba7e03a/ios/swift-screensharing/JitsiSDKScreenSharingTest/Broadcast%20Extension)
|
|
111
|
+
It involves copying the files found in this [sample project](https://github.com/jitsi/jitsi-meet-sdk-samples/tree/18c35f7625b38233579ff34f761f4c126ba7e03a/ios/swift-screensharing/JitsiSDKScreenSharingTest/Broadcast%20Extension)
|
|
97
112
|
to your iOS project, and registering a Broadcast Extension in Xcode.
|
|
98
113
|
|
|
99
|
-
It's also recommended to use [CallKeep](https://github.com/react-native-webrtc/react-native-callkeep),
|
|
114
|
+
It's also recommended to use [CallKeep](https://github.com/react-native-webrtc/react-native-callkeep),
|
|
100
115
|
to register a call with CallKit (as well as turning on the `voip` background mode).
|
|
101
116
|
Due to background app processing limitations, screen recording may be interrupted if the app is restricted
|
|
102
117
|
in the background. Registering with CallKit allows the app to continue processing for the duration of the call.
|
|
@@ -105,24 +120,22 @@ Once setup, iOS screenshare can be initiated like so:
|
|
|
105
120
|
|
|
106
121
|
```js
|
|
107
122
|
const screenCaptureRef = React.useRef(null);
|
|
108
|
-
const screenCapturePickerView = Platform.OS ===
|
|
123
|
+
const screenCapturePickerView = Platform.OS === 'ios' && (
|
|
109
124
|
<ScreenCapturePickerView ref={screenCaptureRef} />
|
|
110
125
|
);
|
|
111
126
|
const startBroadcast = async () => {
|
|
112
|
-
if(Platform.OS ===
|
|
127
|
+
if (Platform.OS === 'ios') {
|
|
113
128
|
const reactTag = findNodeHandle(screenCaptureRef.current);
|
|
114
129
|
await NativeModules.ScreenCapturePickerViewManager.show(reactTag);
|
|
115
130
|
room.localParticipant.setScreenShareEnabled(true);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
131
|
+
} else {
|
|
118
132
|
room.localParticipant.setScreenShareEnabled(true);
|
|
119
133
|
}
|
|
120
134
|
};
|
|
121
135
|
|
|
122
136
|
return (
|
|
123
137
|
<View style={styles.container}>
|
|
124
|
-
/*...*/
|
|
125
|
-
// Make sure the ScreenCapturePickerView exists in the view tree.
|
|
138
|
+
/*...*/ // Make sure the ScreenCapturePickerView exists in the view tree.
|
|
126
139
|
{screenCapturePickerView}
|
|
127
140
|
</View>
|
|
128
141
|
);
|
|
@@ -139,3 +152,5 @@ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the
|
|
|
139
152
|
## License
|
|
140
153
|
|
|
141
154
|
Apache License 2.0
|
|
155
|
+
|
|
156
|
+
<!--BEGIN_REPO_NAV--><!--END_REPO_NAV-->
|
package/android/local.properties
CHANGED
|
Binary file
|
|
@@ -9,7 +9,7 @@ var _reactNative = require("react-native");
|
|
|
9
9
|
|
|
10
10
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
11
11
|
|
|
12
|
-
const LINKING_ERROR = `The package 'livekit
|
|
12
|
+
const LINKING_ERROR = `The package '@livekit/react-native' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
|
|
13
13
|
ios: "- You have run 'pod install'\n",
|
|
14
14
|
default: ''
|
|
15
15
|
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo managed workflow\n';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["AudioSession.ts"],"names":["LINKING_ERROR","Platform","select","ios","default","LivekitReactNative","NativeModules","Proxy","get","Error","AudioSession","config","configureAudio","startAudioSession","stopAudioSession","OS","getAudioOutputs","deviceId","selectAudioOutput","showAudioRoutePicker"],"mappings":";;;;;;;AAAA;;;;AACA,MAAMA,aAAa,GAChB
|
|
1
|
+
{"version":3,"sources":["AudioSession.ts"],"names":["LINKING_ERROR","Platform","select","ios","default","LivekitReactNative","NativeModules","Proxy","get","Error","AudioSession","config","configureAudio","startAudioSession","stopAudioSession","OS","getAudioOutputs","deviceId","selectAudioOutput","showAudioRoutePicker"],"mappings":";;;;;;;AAAA;;;;AACA,MAAMA,aAAa,GAChB,gFAAD,GACAC,sBAASC,MAAT,CAAgB;AAAEC,EAAAA,GAAG,EAAE,gCAAP;AAAyCC,EAAAA,OAAO,EAAE;AAAlD,CAAhB,CADA,GAEA,sDAFA,GAGA,6CAJF;AAMA,MAAMC,kBAAkB,GAAGC,2BAAcD,kBAAd,GACvBC,2BAAcD,kBADS,GAEvB,IAAIE,KAAJ,CACE,EADF,EAEE;AACEC,EAAAA,GAAG,GAAG;AACJ,UAAM,IAAIC,KAAJ,CAAUT,aAAV,CAAN;AACD;;AAHH,CAFF,CAFJ;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAUe,MAAMU,YAAN,CAAmB;;;;gBAAbA,Y,oBAMK,MAAOC,MAAP,IAAsC;AAC5D,QAAMN,kBAAkB,CAACO,cAAnB,CAAkCD,MAAlC,CAAN;AACD,C;;gBARkBD,Y,uBAaQ,YAAY;AACrC,QAAML,kBAAkB,CAACQ,iBAAnB,EAAN;AACD,C;;gBAfkBH,Y,sBAoBO,YAAY;AACpC,QAAML,kBAAkB,CAACS,gBAAnB,EAAN;AACD,C;;gBAtBkBJ,Y,qBAmDM,YAA+B;AACtD,MAAIT,sBAASc,EAAT,KAAgB,KAApB,EAA2B;AACzB,WAAO,CAAC,SAAD,EAAY,eAAZ,CAAP;AACD,GAFD,MAEO,IAAId,sBAASc,EAAT,KAAgB,SAApB,EAA+B;AACpC,WAAQ,MAAMV,kBAAkB,CAACW,eAAnB,EAAd;AACD,GAFM,MAEA;AACL,WAAO,EAAP;AACD;AACF,C;;gBA3DkBN,Y,uBAoEQ,MAAOO,QAAP,IAA4B;AACrD,QAAMZ,kBAAkB,CAACa,iBAAnB,CAAqCD,QAArC,CAAN;AACD,C;;gBAtEkBP,Y,0BA6EW,YAAY;AACxC,MAAIT,sBAASc,EAAT,KAAgB,KAApB,EAA2B;AACzB,UAAMV,kBAAkB,CAACc,oBAAnB,EAAN;AACD;AACF,C","sourcesContent":["import { NativeModules, Platform } from 'react-native';\nconst LINKING_ERROR =\n `The package '@livekit/react-native' doesn't seem to be linked. Make sure: \\n\\n` +\n Platform.select({ ios: \"- You have run 'pod install'\\n\", default: '' }) +\n '- You rebuilt the app after installing the package\\n' +\n '- You are not using Expo managed workflow\\n';\n\nconst LivekitReactNative = NativeModules.LivekitReactNative\n ? NativeModules.LivekitReactNative\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\n/**\n * Configuration for the underlying AudioSession.\n *\n * ----\n * Android specific options:\n *\n * * preferredOutputList - The preferred order in which to automatically select an audio output.\n * This is ignored when an output is manually selected with {@link AudioSession.selectAudioOutput}.\n *\n * By default, the order is set to:\n * 1. `\"speaker\"`\n * 2. `\"earpiece\"`\n * 3. `\"headset\"`\n * 4. `\"bluetooth\"`\n *\n * ----\n * iOS\n *\n * * defaultOutput - The default preferred output to use when a wired headset or bluetooth output is unavailable.\n *\n * By default, this is set to `\"speaker\"`\n */\nexport type AudioConfiguration = {\n android: {\n preferredOutputList: ('speaker' | 'earpiece' | 'headset' | 'bluetooth')[];\n };\n ios: {\n defaultOutput: 'speaker' | 'earpiece';\n };\n};\n\nexport default class AudioSession {\n /**\n * Applies the provided audio configuration to the underlying AudioSession.\n *\n * Must be called prior to connecting to a Room for the configuration to apply correctly.\n */\n static configureAudio = async (config: AudioConfiguration) => {\n await LivekitReactNative.configureAudio(config);\n };\n\n /**\n * Starts an AudioSession.\n */\n static startAudioSession = async () => {\n await LivekitReactNative.startAudioSession();\n };\n\n /**\n * Stops the existing AudioSession.\n */\n static stopAudioSession = async () => {\n await LivekitReactNative.stopAudioSession();\n };\n\n /**\n * Gets the available audio outputs for use with {@link selectAudioOutput}.\n *\n * {@link startAudioSession} must be called prior to using this method.\n *\n * For Android, will return if available:\n * * \"speaker\"\n * * \"earpiece\"\n * * \"headset\"\n * * \"bluetooth\"\n *\n * Note: For applications targeting SDK versions over 30, the runtime BLUETOOTH_CONNECT\n * permission must be requested to send audio to bluetooth headsets.\n *\n * ----\n *\n * For iOS, due to OS limitations, the only available types are:\n * * \"default\" - Use default iOS audio routing\n * * \"force_speaker\" - Force audio output through speaker\n *\n * See also {@link showAudioRoutePicker} to display a route picker that\n * can choose between other audio devices (i.e. headset/bluetooth/airplay),\n * or use a library like `react-native-avroutepicker` for a native platform\n * control.\n *\n * @returns the available audio output types\n */\n static getAudioOutputs = async (): Promise<string[]> => {\n if (Platform.OS === 'ios') {\n return ['default', 'force_speaker'];\n } else if (Platform.OS === 'android') {\n return (await LivekitReactNative.getAudioOutputs()) as string[];\n } else {\n return [];\n }\n };\n\n /**\n * Select the provided audio output if available.\n *\n * {@link startAudioSession} must be called prior to using this method.\n *\n * @param deviceId A deviceId retrieved from {@link getAudioOutputs}\n */\n static selectAudioOutput = async (deviceId: string) => {\n await LivekitReactNative.selectAudioOutput(deviceId);\n };\n\n /**\n * iOS only, requires iOS 11+.\n *\n * Displays an AVRoutePickerView for the user to choose their audio output.\n */\n static showAudioRoutePicker = async () => {\n if (Platform.OS === 'ios') {\n await LivekitReactNative.showAudioRoutePicker();\n }\n };\n}\n"]}
|
|
@@ -113,6 +113,8 @@ class VideoViewElementInfo {
|
|
|
113
113
|
|
|
114
114
|
_defineProperty(this, "visibilityChangedAt", void 0);
|
|
115
115
|
|
|
116
|
+
_defineProperty(this, "pictureInPicture", false);
|
|
117
|
+
|
|
116
118
|
_defineProperty(this, "handleResize", void 0);
|
|
117
119
|
|
|
118
120
|
_defineProperty(this, "handleVisibilityChanged", void 0);
|
|
@@ -135,11 +137,8 @@ class VideoViewElementInfo {
|
|
|
135
137
|
width,
|
|
136
138
|
height
|
|
137
139
|
} = event.nativeEvent.layout;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
this._width = width * pixelRatio;
|
|
142
|
-
this._height = height * pixelRatio;
|
|
140
|
+
this._width = width;
|
|
141
|
+
this._height = height;
|
|
143
142
|
|
|
144
143
|
if (this._observing) {
|
|
145
144
|
var _this$handleResize;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["VideoView.tsx"],"names":["VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","mediaStream","setMediaStream","LocalVideoTrack","onRestarted","track","on","TrackEvent","Restarted","off","RemoteVideoTrack","isAdaptiveStream","observeElementInfo","stopObservingElementInfo","styles","container","event","onLayout","isVisible","onVisibility","videoView","toURL","StyleSheet","create","flex","width","_width","_height","observe","_observing","stopObserving","height","nativeEvent","layout","
|
|
1
|
+
{"version":3,"sources":["VideoView.tsx"],"names":["VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","mediaStream","setMediaStream","LocalVideoTrack","onRestarted","track","on","TrackEvent","Restarted","off","RemoteVideoTrack","isAdaptiveStream","observeElementInfo","stopObservingElementInfo","styles","container","event","onLayout","isVisible","onVisibility","videoView","toURL","StyleSheet","create","flex","width","_width","_height","observe","_observing","stopObserving","height","nativeEvent","layout","handleResize","visible","visibilityChangedAt","Date","now","handleVisibilityChanged"],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native';\nimport {\n ElementInfo,\n LocalVideoTrack,\n Track,\n TrackEvent,\n VideoTrack,\n} from 'livekit-client';\nimport { RTCView } from 'react-native-webrtc';\nimport { useEffect, useState } from 'react';\nimport { RemoteVideoTrack } from 'livekit-client';\nimport ViewPortDetector from './ViewPortDetector';\n\nexport type Props = {\n videoTrack?: VideoTrack | undefined;\n style?: ViewStyle;\n objectFit?: 'cover' | 'contain' | undefined;\n mirror?: boolean;\n zOrder?: number;\n};\n\nexport const VideoView = ({\n style = {},\n videoTrack,\n objectFit = 'cover',\n zOrder,\n mirror,\n}: Props) => {\n const [elementInfo] = useState(() => {\n let info = new VideoViewElementInfo();\n info.id = videoTrack?.sid;\n info.something = videoTrack;\n return info;\n });\n\n const [mediaStream, setMediaStream] = useState(videoTrack?.mediaStream);\n useEffect(() => {\n setMediaStream(videoTrack?.mediaStream);\n if (videoTrack instanceof LocalVideoTrack) {\n const onRestarted = (track: Track | null) => {\n setMediaStream(track?.mediaStream);\n };\n videoTrack.on(TrackEvent.Restarted, onRestarted);\n\n return () => {\n videoTrack.off(TrackEvent.Restarted, onRestarted);\n };\n } else {\n return () => {};\n }\n }, [videoTrack]);\n\n useEffect(() => {\n if (videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream) {\n videoTrack?.observeElementInfo(elementInfo);\n return () => {\n videoTrack?.stopObservingElementInfo(elementInfo);\n };\n } else {\n return () => {};\n }\n }, [videoTrack, elementInfo]);\n\n return (\n <View\n style={{ ...style, ...styles.container }}\n onLayout={(event) => {\n elementInfo.onLayout(event);\n }}\n >\n <ViewPortDetector\n onChange={(isVisible: boolean) => elementInfo.onVisibility(isVisible)}\n style={styles.videoView}\n >\n <RTCView\n style={styles.videoView}\n streamURL={mediaStream?.toURL() ?? ''}\n objectFit={objectFit}\n zOrder={zOrder}\n mirror={mirror}\n />\n </ViewPortDetector>\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {},\n videoView: {\n flex: 1,\n width: '100%',\n },\n});\n\nclass VideoViewElementInfo implements ElementInfo {\n element: object = {};\n something?: any;\n id?: string;\n _width = 0;\n _height = 0;\n _observing = false;\n visible: boolean = true;\n visibilityChangedAt: number | undefined;\n pictureInPicture = false;\n handleResize?: (() => void) | undefined;\n handleVisibilityChanged?: (() => void) | undefined;\n width = () => this._width;\n height = () => this._height;\n\n observe(): void {\n this._observing = true;\n }\n stopObserving(): void {\n this._observing = false;\n }\n\n onLayout(event: LayoutChangeEvent) {\n let { width, height } = event.nativeEvent.layout;\n this._width = width;\n this._height = height;\n\n if (this._observing) {\n this.handleResize?.();\n }\n }\n onVisibility(isVisible: boolean) {\n if (this.visible !== isVisible) {\n this.visible = isVisible;\n this.visibilityChangedAt = Date.now();\n if (this._observing) {\n this.handleVisibilityChanged?.();\n }\n }\n }\n}\n"]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -21,6 +21,8 @@ var _reactNativeUrlPolyfill = require("react-native-url-polyfill");
|
|
|
21
21
|
|
|
22
22
|
var _AudioSession = _interopRequireDefault(require("./audio/AudioSession"));
|
|
23
23
|
|
|
24
|
+
var _reactNative = require("react-native");
|
|
25
|
+
|
|
24
26
|
var _VideoView = require("./components/VideoView");
|
|
25
27
|
|
|
26
28
|
Object.keys(_VideoView).forEach(function (key) {
|
|
@@ -72,11 +74,21 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
72
74
|
*/
|
|
73
75
|
function registerGlobals() {
|
|
74
76
|
(0, _reactNativeWebrtc.registerGlobals)();
|
|
77
|
+
livekitRegisterGlobals();
|
|
75
78
|
(0, _reactNativeUrlPolyfill.setupURLPolyfill)();
|
|
76
79
|
fixWebrtcAdapter();
|
|
77
80
|
shimPromiseAllSettled();
|
|
78
81
|
}
|
|
79
82
|
|
|
83
|
+
function livekitRegisterGlobals() {
|
|
84
|
+
let lkGlobal = {
|
|
85
|
+
platform: _reactNative.Platform.OS,
|
|
86
|
+
devicePixelRatio: _reactNative.PixelRatio.get()
|
|
87
|
+
}; // @ts-ignore
|
|
88
|
+
|
|
89
|
+
global.LiveKitReactNativeGlobal = lkGlobal;
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
function fixWebrtcAdapter() {
|
|
81
93
|
var _window;
|
|
82
94
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["index.tsx"],"names":["registerGlobals","fixWebrtcAdapter","shimPromiseAllSettled","window","navigator","undefined","userAgent","product","allSettled","require","shim"],"mappings":";;;;;;;;;;;;;;;;;AAAA;;AACA;;AACA;;
|
|
1
|
+
{"version":3,"sources":["index.tsx"],"names":["registerGlobals","livekitRegisterGlobals","fixWebrtcAdapter","shimPromiseAllSettled","lkGlobal","platform","Platform","OS","devicePixelRatio","PixelRatio","get","global","LiveKitReactNativeGlobal","window","navigator","undefined","userAgent","product","allSettled","require","shim"],"mappings":";;;;;;;;;;;;;;;;;AAAA;;AACA;;AACA;;AAEA;;AAyCA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AACA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AACA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;AAxCA;AACA;AACA;AACA;AACA;AACO,SAASA,eAAT,GAA2B;AAChC;AACAC,EAAAA,sBAAsB;AACtB;AACAC,EAAAA,gBAAgB;AAChBC,EAAAA,qBAAqB;AACtB;;AACD,SAASF,sBAAT,GAAkC;AAChC,MAAIG,QAAgC,GAAG;AACrCC,IAAAA,QAAQ,EAAEC,sBAASC,EADkB;AAErCC,IAAAA,gBAAgB,EAAEC,wBAAWC,GAAX;AAFmB,GAAvC,CADgC,CAMhC;;AACAC,EAAAA,MAAM,CAACC,wBAAP,GAAkCR,QAAlC;AACD;;AAED,SAASF,gBAAT,GAA4B;AAAA;;AAC1B;AACA,MAAI,YAAAW,MAAM,UAAN,0CAAQC,SAAR,MAAsBC,SAA1B,EAAqC;AACnC;AACA,UAAM;AAAED,MAAAA;AAAF,QAAgBD,MAAtB;;AACA,QAAIC,SAAS,CAACE,SAAV,KAAwBD,SAA5B,EAAuC;AAAA;;AACrCD,MAAAA,SAAS,CAACE,SAAV,yBAAsBF,SAAS,CAACG,OAAhC,mEAA2C,SAA3C;AACD;AACF;AACF;;AAED,SAASd,qBAAT,GAAiC;AAC/B,MAAIe,UAAU,GAAGC,OAAO,CAAC,oBAAD,CAAxB;;AACAD,EAAAA,UAAU,CAACE,IAAX;AACD","sourcesContent":["import { registerGlobals as webrtcRegisterGlobals } from 'react-native-webrtc';\nimport { setupURLPolyfill } from 'react-native-url-polyfill';\nimport AudioSession from './audio/AudioSession';\nimport type { AudioConfiguration } from './audio/AudioSession';\nimport { PixelRatio, Platform } from 'react-native';\nimport type { LiveKitReactNativeInfo } from 'livekit-client';\n\n/**\n * Registers the required globals needed for LiveKit to work.\n *\n * Must be called before using LiveKit.\n */\nexport function registerGlobals() {\n webrtcRegisterGlobals();\n livekitRegisterGlobals();\n setupURLPolyfill();\n fixWebrtcAdapter();\n shimPromiseAllSettled();\n}\nfunction livekitRegisterGlobals() {\n let lkGlobal: LiveKitReactNativeInfo = {\n platform: Platform.OS,\n devicePixelRatio: PixelRatio.get(),\n };\n\n // @ts-ignore\n global.LiveKitReactNativeGlobal = lkGlobal;\n}\n\nfunction fixWebrtcAdapter() {\n // @ts-ignore\n if (window?.navigator !== undefined) {\n // @ts-ignore\n const { navigator } = window;\n if (navigator.userAgent === undefined) {\n navigator.userAgent = navigator.product ?? 'Unknown';\n }\n }\n}\n\nfunction shimPromiseAllSettled() {\n var allSettled = require('promise.allsettled');\n allSettled.shim();\n}\n\nexport * from './components/VideoView';\nexport * from './useParticipant';\nexport * from './useRoom';\nexport { AudioSession, AudioConfiguration };\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
2
2
|
|
|
3
3
|
import { NativeModules, Platform } from 'react-native';
|
|
4
|
-
const LINKING_ERROR = `The package 'livekit
|
|
4
|
+
const LINKING_ERROR = `The package '@livekit/react-native' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
|
|
5
5
|
ios: "- You have run 'pod install'\n",
|
|
6
6
|
default: ''
|
|
7
7
|
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo managed workflow\n';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["AudioSession.ts"],"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","LivekitReactNative","Proxy","get","Error","AudioSession","config","configureAudio","startAudioSession","stopAudioSession","OS","getAudioOutputs","deviceId","selectAudioOutput","showAudioRoutePicker"],"mappings":";;AAAA,SAASA,aAAT,EAAwBC,QAAxB,QAAwC,cAAxC;AACA,MAAMC,aAAa,GAChB
|
|
1
|
+
{"version":3,"sources":["AudioSession.ts"],"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","LivekitReactNative","Proxy","get","Error","AudioSession","config","configureAudio","startAudioSession","stopAudioSession","OS","getAudioOutputs","deviceId","selectAudioOutput","showAudioRoutePicker"],"mappings":";;AAAA,SAASA,aAAT,EAAwBC,QAAxB,QAAwC,cAAxC;AACA,MAAMC,aAAa,GAChB,gFAAD,GACAD,QAAQ,CAACE,MAAT,CAAgB;AAAEC,EAAAA,GAAG,EAAE,gCAAP;AAAyCC,EAAAA,OAAO,EAAE;AAAlD,CAAhB,CADA,GAEA,sDAFA,GAGA,6CAJF;AAMA,MAAMC,kBAAkB,GAAGN,aAAa,CAACM,kBAAd,GACvBN,aAAa,CAACM,kBADS,GAEvB,IAAIC,KAAJ,CACE,EADF,EAEE;AACEC,EAAAA,GAAG,GAAG;AACJ,UAAM,IAAIC,KAAJ,CAAUP,aAAV,CAAN;AACD;;AAHH,CAFF,CAFJ;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAUA,eAAe,MAAMQ,YAAN,CAAmB;;gBAAbA,Y,oBAMK,MAAOC,MAAP,IAAsC;AAC5D,QAAML,kBAAkB,CAACM,cAAnB,CAAkCD,MAAlC,CAAN;AACD,C;;gBARkBD,Y,uBAaQ,YAAY;AACrC,QAAMJ,kBAAkB,CAACO,iBAAnB,EAAN;AACD,C;;gBAfkBH,Y,sBAoBO,YAAY;AACpC,QAAMJ,kBAAkB,CAACQ,gBAAnB,EAAN;AACD,C;;gBAtBkBJ,Y,qBAmDM,YAA+B;AACtD,MAAIT,QAAQ,CAACc,EAAT,KAAgB,KAApB,EAA2B;AACzB,WAAO,CAAC,SAAD,EAAY,eAAZ,CAAP;AACD,GAFD,MAEO,IAAId,QAAQ,CAACc,EAAT,KAAgB,SAApB,EAA+B;AACpC,WAAQ,MAAMT,kBAAkB,CAACU,eAAnB,EAAd;AACD,GAFM,MAEA;AACL,WAAO,EAAP;AACD;AACF,C;;gBA3DkBN,Y,uBAoEQ,MAAOO,QAAP,IAA4B;AACrD,QAAMX,kBAAkB,CAACY,iBAAnB,CAAqCD,QAArC,CAAN;AACD,C;;gBAtEkBP,Y,0BA6EW,YAAY;AACxC,MAAIT,QAAQ,CAACc,EAAT,KAAgB,KAApB,EAA2B;AACzB,UAAMT,kBAAkB,CAACa,oBAAnB,EAAN;AACD;AACF,C","sourcesContent":["import { NativeModules, Platform } from 'react-native';\nconst LINKING_ERROR =\n `The package '@livekit/react-native' doesn't seem to be linked. Make sure: \\n\\n` +\n Platform.select({ ios: \"- You have run 'pod install'\\n\", default: '' }) +\n '- You rebuilt the app after installing the package\\n' +\n '- You are not using Expo managed workflow\\n';\n\nconst LivekitReactNative = NativeModules.LivekitReactNative\n ? NativeModules.LivekitReactNative\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\n/**\n * Configuration for the underlying AudioSession.\n *\n * ----\n * Android specific options:\n *\n * * preferredOutputList - The preferred order in which to automatically select an audio output.\n * This is ignored when an output is manually selected with {@link AudioSession.selectAudioOutput}.\n *\n * By default, the order is set to:\n * 1. `\"speaker\"`\n * 2. `\"earpiece\"`\n * 3. `\"headset\"`\n * 4. `\"bluetooth\"`\n *\n * ----\n * iOS\n *\n * * defaultOutput - The default preferred output to use when a wired headset or bluetooth output is unavailable.\n *\n * By default, this is set to `\"speaker\"`\n */\nexport type AudioConfiguration = {\n android: {\n preferredOutputList: ('speaker' | 'earpiece' | 'headset' | 'bluetooth')[];\n };\n ios: {\n defaultOutput: 'speaker' | 'earpiece';\n };\n};\n\nexport default class AudioSession {\n /**\n * Applies the provided audio configuration to the underlying AudioSession.\n *\n * Must be called prior to connecting to a Room for the configuration to apply correctly.\n */\n static configureAudio = async (config: AudioConfiguration) => {\n await LivekitReactNative.configureAudio(config);\n };\n\n /**\n * Starts an AudioSession.\n */\n static startAudioSession = async () => {\n await LivekitReactNative.startAudioSession();\n };\n\n /**\n * Stops the existing AudioSession.\n */\n static stopAudioSession = async () => {\n await LivekitReactNative.stopAudioSession();\n };\n\n /**\n * Gets the available audio outputs for use with {@link selectAudioOutput}.\n *\n * {@link startAudioSession} must be called prior to using this method.\n *\n * For Android, will return if available:\n * * \"speaker\"\n * * \"earpiece\"\n * * \"headset\"\n * * \"bluetooth\"\n *\n * Note: For applications targeting SDK versions over 30, the runtime BLUETOOTH_CONNECT\n * permission must be requested to send audio to bluetooth headsets.\n *\n * ----\n *\n * For iOS, due to OS limitations, the only available types are:\n * * \"default\" - Use default iOS audio routing\n * * \"force_speaker\" - Force audio output through speaker\n *\n * See also {@link showAudioRoutePicker} to display a route picker that\n * can choose between other audio devices (i.e. headset/bluetooth/airplay),\n * or use a library like `react-native-avroutepicker` for a native platform\n * control.\n *\n * @returns the available audio output types\n */\n static getAudioOutputs = async (): Promise<string[]> => {\n if (Platform.OS === 'ios') {\n return ['default', 'force_speaker'];\n } else if (Platform.OS === 'android') {\n return (await LivekitReactNative.getAudioOutputs()) as string[];\n } else {\n return [];\n }\n };\n\n /**\n * Select the provided audio output if available.\n *\n * {@link startAudioSession} must be called prior to using this method.\n *\n * @param deviceId A deviceId retrieved from {@link getAudioOutputs}\n */\n static selectAudioOutput = async (deviceId: string) => {\n await LivekitReactNative.selectAudioOutput(deviceId);\n };\n\n /**\n * iOS only, requires iOS 11+.\n *\n * Displays an AVRoutePickerView for the user to choose their audio output.\n */\n static showAudioRoutePicker = async () => {\n if (Platform.OS === 'ios') {\n await LivekitReactNative.showAudioRoutePicker();\n }\n };\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { StyleSheet, View } from 'react-native';
|
|
5
5
|
import { LocalVideoTrack, TrackEvent } from 'livekit-client';
|
|
6
6
|
import { RTCView } from 'react-native-webrtc';
|
|
7
7
|
import { useEffect, useState } from 'react';
|
|
@@ -94,6 +94,8 @@ class VideoViewElementInfo {
|
|
|
94
94
|
|
|
95
95
|
_defineProperty(this, "visibilityChangedAt", void 0);
|
|
96
96
|
|
|
97
|
+
_defineProperty(this, "pictureInPicture", false);
|
|
98
|
+
|
|
97
99
|
_defineProperty(this, "handleResize", void 0);
|
|
98
100
|
|
|
99
101
|
_defineProperty(this, "handleVisibilityChanged", void 0);
|
|
@@ -116,9 +118,8 @@ class VideoViewElementInfo {
|
|
|
116
118
|
width,
|
|
117
119
|
height
|
|
118
120
|
} = event.nativeEvent.layout;
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
this._height = height * pixelRatio;
|
|
121
|
+
this._width = width;
|
|
122
|
+
this._height = height;
|
|
122
123
|
|
|
123
124
|
if (this._observing) {
|
|
124
125
|
var _this$handleResize;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["VideoView.tsx"],"names":["React","
|
|
1
|
+
{"version":3,"sources":["VideoView.tsx"],"names":["React","StyleSheet","View","LocalVideoTrack","TrackEvent","RTCView","useEffect","useState","RemoteVideoTrack","ViewPortDetector","VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","mediaStream","setMediaStream","onRestarted","track","on","Restarted","off","isAdaptiveStream","observeElementInfo","stopObservingElementInfo","styles","container","event","onLayout","isVisible","onVisibility","videoView","toURL","create","flex","width","_width","_height","observe","_observing","stopObserving","height","nativeEvent","layout","handleResize","visible","visibilityChangedAt","Date","now","handleVisibilityChanged"],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native';\nimport {\n ElementInfo,\n LocalVideoTrack,\n Track,\n TrackEvent,\n VideoTrack,\n} from 'livekit-client';\nimport { RTCView } from 'react-native-webrtc';\nimport { useEffect, useState } from 'react';\nimport { RemoteVideoTrack } from 'livekit-client';\nimport ViewPortDetector from './ViewPortDetector';\n\nexport type Props = {\n videoTrack?: VideoTrack | undefined;\n style?: ViewStyle;\n objectFit?: 'cover' | 'contain' | undefined;\n mirror?: boolean;\n zOrder?: number;\n};\n\nexport const VideoView = ({\n style = {},\n videoTrack,\n objectFit = 'cover',\n zOrder,\n mirror,\n}: Props) => {\n const [elementInfo] = useState(() => {\n let info = new VideoViewElementInfo();\n info.id = videoTrack?.sid;\n info.something = videoTrack;\n return info;\n });\n\n const [mediaStream, setMediaStream] = useState(videoTrack?.mediaStream);\n useEffect(() => {\n setMediaStream(videoTrack?.mediaStream);\n if (videoTrack instanceof LocalVideoTrack) {\n const onRestarted = (track: Track | null) => {\n setMediaStream(track?.mediaStream);\n };\n videoTrack.on(TrackEvent.Restarted, onRestarted);\n\n return () => {\n videoTrack.off(TrackEvent.Restarted, onRestarted);\n };\n } else {\n return () => {};\n }\n }, [videoTrack]);\n\n useEffect(() => {\n if (videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream) {\n videoTrack?.observeElementInfo(elementInfo);\n return () => {\n videoTrack?.stopObservingElementInfo(elementInfo);\n };\n } else {\n return () => {};\n }\n }, [videoTrack, elementInfo]);\n\n return (\n <View\n style={{ ...style, ...styles.container }}\n onLayout={(event) => {\n elementInfo.onLayout(event);\n }}\n >\n <ViewPortDetector\n onChange={(isVisible: boolean) => elementInfo.onVisibility(isVisible)}\n style={styles.videoView}\n >\n <RTCView\n style={styles.videoView}\n streamURL={mediaStream?.toURL() ?? ''}\n objectFit={objectFit}\n zOrder={zOrder}\n mirror={mirror}\n />\n </ViewPortDetector>\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {},\n videoView: {\n flex: 1,\n width: '100%',\n },\n});\n\nclass VideoViewElementInfo implements ElementInfo {\n element: object = {};\n something?: any;\n id?: string;\n _width = 0;\n _height = 0;\n _observing = false;\n visible: boolean = true;\n visibilityChangedAt: number | undefined;\n pictureInPicture = false;\n handleResize?: (() => void) | undefined;\n handleVisibilityChanged?: (() => void) | undefined;\n width = () => this._width;\n height = () => this._height;\n\n observe(): void {\n this._observing = true;\n }\n stopObserving(): void {\n this._observing = false;\n }\n\n onLayout(event: LayoutChangeEvent) {\n let { width, height } = event.nativeEvent.layout;\n this._width = width;\n this._height = height;\n\n if (this._observing) {\n this.handleResize?.();\n }\n }\n onVisibility(isVisible: boolean) {\n if (this.visible !== isVisible) {\n this.visible = isVisible;\n this.visibilityChangedAt = Date.now();\n if (this._observing) {\n this.handleVisibilityChanged?.();\n }\n }\n }\n}\n"]}
|
package/lib/module/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { registerGlobals as webrtcRegisterGlobals } from 'react-native-webrtc';
|
|
2
2
|
import { setupURLPolyfill } from 'react-native-url-polyfill';
|
|
3
3
|
import AudioSession from './audio/AudioSession';
|
|
4
|
+
import { PixelRatio, Platform } from 'react-native';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Registers the required globals needed for LiveKit to work.
|
|
@@ -9,11 +10,21 @@ import AudioSession from './audio/AudioSession';
|
|
|
9
10
|
*/
|
|
10
11
|
export function registerGlobals() {
|
|
11
12
|
webrtcRegisterGlobals();
|
|
13
|
+
livekitRegisterGlobals();
|
|
12
14
|
setupURLPolyfill();
|
|
13
15
|
fixWebrtcAdapter();
|
|
14
16
|
shimPromiseAllSettled();
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
function livekitRegisterGlobals() {
|
|
20
|
+
let lkGlobal = {
|
|
21
|
+
platform: Platform.OS,
|
|
22
|
+
devicePixelRatio: PixelRatio.get()
|
|
23
|
+
}; // @ts-ignore
|
|
24
|
+
|
|
25
|
+
global.LiveKitReactNativeGlobal = lkGlobal;
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
function fixWebrtcAdapter() {
|
|
18
29
|
var _window;
|
|
19
30
|
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["index.tsx"],"names":["registerGlobals","webrtcRegisterGlobals","setupURLPolyfill","AudioSession","fixWebrtcAdapter","shimPromiseAllSettled","window","navigator","undefined","userAgent","product","allSettled","require","shim"],"mappings":"AAAA,SAASA,eAAe,IAAIC,qBAA5B,QAAyD,qBAAzD;AACA,SAASC,gBAAT,QAAiC,2BAAjC;AACA,OAAOC,YAAP,MAAyB,sBAAzB;;AAGA;AACA;AACA;AACA;AACA;AACA,OAAO,
|
|
1
|
+
{"version":3,"sources":["index.tsx"],"names":["registerGlobals","webrtcRegisterGlobals","setupURLPolyfill","AudioSession","PixelRatio","Platform","livekitRegisterGlobals","fixWebrtcAdapter","shimPromiseAllSettled","lkGlobal","platform","OS","devicePixelRatio","get","global","LiveKitReactNativeGlobal","window","navigator","undefined","userAgent","product","allSettled","require","shim"],"mappings":"AAAA,SAASA,eAAe,IAAIC,qBAA5B,QAAyD,qBAAzD;AACA,SAASC,gBAAT,QAAiC,2BAAjC;AACA,OAAOC,YAAP,MAAyB,sBAAzB;AAEA,SAASC,UAAT,EAAqBC,QAArB,QAAqC,cAArC;;AAGA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASL,eAAT,GAA2B;AAChCC,EAAAA,qBAAqB;AACrBK,EAAAA,sBAAsB;AACtBJ,EAAAA,gBAAgB;AAChBK,EAAAA,gBAAgB;AAChBC,EAAAA,qBAAqB;AACtB;;AACD,SAASF,sBAAT,GAAkC;AAChC,MAAIG,QAAgC,GAAG;AACrCC,IAAAA,QAAQ,EAAEL,QAAQ,CAACM,EADkB;AAErCC,IAAAA,gBAAgB,EAAER,UAAU,CAACS,GAAX;AAFmB,GAAvC,CADgC,CAMhC;;AACAC,EAAAA,MAAM,CAACC,wBAAP,GAAkCN,QAAlC;AACD;;AAED,SAASF,gBAAT,GAA4B;AAAA;;AAC1B;AACA,MAAI,YAAAS,MAAM,UAAN,0CAAQC,SAAR,MAAsBC,SAA1B,EAAqC;AACnC;AACA,UAAM;AAAED,MAAAA;AAAF,QAAgBD,MAAtB;;AACA,QAAIC,SAAS,CAACE,SAAV,KAAwBD,SAA5B,EAAuC;AAAA;;AACrCD,MAAAA,SAAS,CAACE,SAAV,yBAAsBF,SAAS,CAACG,OAAhC,mEAA2C,SAA3C;AACD;AACF;AACF;;AAED,SAASZ,qBAAT,GAAiC;AAC/B,MAAIa,UAAU,GAAGC,OAAO,CAAC,oBAAD,CAAxB;;AACAD,EAAAA,UAAU,CAACE,IAAX;AACD;;AAED,cAAc,wBAAd;AACA,cAAc,kBAAd;AACA,cAAc,WAAd;AACA,SAASpB,YAAT","sourcesContent":["import { registerGlobals as webrtcRegisterGlobals } from 'react-native-webrtc';\nimport { setupURLPolyfill } from 'react-native-url-polyfill';\nimport AudioSession from './audio/AudioSession';\nimport type { AudioConfiguration } from './audio/AudioSession';\nimport { PixelRatio, Platform } from 'react-native';\nimport type { LiveKitReactNativeInfo } from 'livekit-client';\n\n/**\n * Registers the required globals needed for LiveKit to work.\n *\n * Must be called before using LiveKit.\n */\nexport function registerGlobals() {\n webrtcRegisterGlobals();\n livekitRegisterGlobals();\n setupURLPolyfill();\n fixWebrtcAdapter();\n shimPromiseAllSettled();\n}\nfunction livekitRegisterGlobals() {\n let lkGlobal: LiveKitReactNativeInfo = {\n platform: Platform.OS,\n devicePixelRatio: PixelRatio.get(),\n };\n\n // @ts-ignore\n global.LiveKitReactNativeGlobal = lkGlobal;\n}\n\nfunction fixWebrtcAdapter() {\n // @ts-ignore\n if (window?.navigator !== undefined) {\n // @ts-ignore\n const { navigator } = window;\n if (navigator.userAgent === undefined) {\n navigator.userAgent = navigator.product ?? 'Unknown';\n }\n }\n}\n\nfunction shimPromiseAllSettled() {\n var allSettled = require('promise.allsettled');\n allSettled.shim();\n}\n\nexport * from './components/VideoView';\nexport * from './useParticipant';\nexport * from './useRoom';\nexport { AudioSession, AudioConfiguration };\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livekit/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "LiveKit for React Native",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"android"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"livekit-client": "^1.
|
|
42
|
+
"livekit-client": "^1.8.0",
|
|
43
43
|
"promise.allsettled": "^1.0.5",
|
|
44
44
|
"react-native-url-polyfill": "^1.3.0"
|
|
45
45
|
},
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"react": "*",
|
|
74
74
|
"react-native": "*",
|
|
75
|
-
"react-native-webrtc": "^
|
|
75
|
+
"react-native-webrtc": "^111.0.0"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
78
|
"test": "jest",
|
|
@@ -98,6 +98,18 @@
|
|
|
98
98
|
]
|
|
99
99
|
},
|
|
100
100
|
"release-it": {
|
|
101
|
+
"hooks": {
|
|
102
|
+
"before:init": [
|
|
103
|
+
"yarn lint",
|
|
104
|
+
"yarn test",
|
|
105
|
+
"yarn typescript"
|
|
106
|
+
],
|
|
107
|
+
"after:bump": [
|
|
108
|
+
"yarn pods",
|
|
109
|
+
"yarn build-docs"
|
|
110
|
+
],
|
|
111
|
+
"after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
|
|
112
|
+
},
|
|
101
113
|
"git": {
|
|
102
114
|
"commitMessage": "chore: release ${version}",
|
|
103
115
|
"tagName": "v${version}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NativeModules, Platform } from 'react-native';
|
|
2
2
|
const LINKING_ERROR =
|
|
3
|
-
`The package 'livekit
|
|
3
|
+
`The package '@livekit/react-native' doesn't seem to be linked. Make sure: \n\n` +
|
|
4
4
|
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
5
5
|
'- You rebuilt the app after installing the package\n' +
|
|
6
6
|
'- You are not using Expo managed workflow\n';
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
LayoutChangeEvent,
|
|
5
|
-
PixelRatio,
|
|
6
|
-
StyleSheet,
|
|
7
|
-
View,
|
|
8
|
-
ViewStyle,
|
|
9
|
-
} from 'react-native';
|
|
3
|
+
import { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native';
|
|
10
4
|
import {
|
|
11
5
|
ElementInfo,
|
|
12
6
|
LocalVideoTrack,
|
|
@@ -109,6 +103,7 @@ class VideoViewElementInfo implements ElementInfo {
|
|
|
109
103
|
_observing = false;
|
|
110
104
|
visible: boolean = true;
|
|
111
105
|
visibilityChangedAt: number | undefined;
|
|
106
|
+
pictureInPicture = false;
|
|
112
107
|
handleResize?: (() => void) | undefined;
|
|
113
108
|
handleVisibilityChanged?: (() => void) | undefined;
|
|
114
109
|
width = () => this._width;
|
|
@@ -123,9 +118,8 @@ class VideoViewElementInfo implements ElementInfo {
|
|
|
123
118
|
|
|
124
119
|
onLayout(event: LayoutChangeEvent) {
|
|
125
120
|
let { width, height } = event.nativeEvent.layout;
|
|
126
|
-
|
|
127
|
-
this.
|
|
128
|
-
this._height = height * pixelRatio;
|
|
121
|
+
this._width = width;
|
|
122
|
+
this._height = height;
|
|
129
123
|
|
|
130
124
|
if (this._observing) {
|
|
131
125
|
this.handleResize?.();
|
package/src/index.tsx
CHANGED
|
@@ -2,6 +2,8 @@ import { registerGlobals as webrtcRegisterGlobals } from 'react-native-webrtc';
|
|
|
2
2
|
import { setupURLPolyfill } from 'react-native-url-polyfill';
|
|
3
3
|
import AudioSession from './audio/AudioSession';
|
|
4
4
|
import type { AudioConfiguration } from './audio/AudioSession';
|
|
5
|
+
import { PixelRatio, Platform } from 'react-native';
|
|
6
|
+
import type { LiveKitReactNativeInfo } from 'livekit-client';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Registers the required globals needed for LiveKit to work.
|
|
@@ -10,10 +12,20 @@ import type { AudioConfiguration } from './audio/AudioSession';
|
|
|
10
12
|
*/
|
|
11
13
|
export function registerGlobals() {
|
|
12
14
|
webrtcRegisterGlobals();
|
|
15
|
+
livekitRegisterGlobals();
|
|
13
16
|
setupURLPolyfill();
|
|
14
17
|
fixWebrtcAdapter();
|
|
15
18
|
shimPromiseAllSettled();
|
|
16
19
|
}
|
|
20
|
+
function livekitRegisterGlobals() {
|
|
21
|
+
let lkGlobal: LiveKitReactNativeInfo = {
|
|
22
|
+
platform: Platform.OS,
|
|
23
|
+
devicePixelRatio: PixelRatio.get(),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
global.LiveKitReactNativeGlobal = lkGlobal;
|
|
28
|
+
}
|
|
17
29
|
|
|
18
30
|
function fixWebrtcAdapter() {
|
|
19
31
|
// @ts-ignore
|