@livekit/react-native 1.4.3 → 2.0.2

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.
Files changed (38) hide show
  1. package/README.md +35 -23
  2. package/android/src/main/java/com/livekit/reactnative/LiveKitReactNative.kt +4 -4
  3. package/android/src/main/java/com/livekit/reactnative/video/CustomVideoDecoderFactory.kt +67 -0
  4. package/android/src/main/java/com/livekit/reactnative/video/CustomVideoEncoderFactory.kt +74 -0
  5. package/lib/commonjs/audio/AudioManager.js +15 -6
  6. package/lib/commonjs/audio/AudioManager.js.map +1 -1
  7. package/lib/commonjs/audio/AudioSession.js +2 -0
  8. package/lib/commonjs/audio/AudioSession.js.map +1 -1
  9. package/lib/commonjs/components/VideoView.js +12 -11
  10. package/lib/commonjs/components/VideoView.js.map +1 -1
  11. package/lib/commonjs/components/ViewPortDetector.js +134 -39
  12. package/lib/commonjs/components/ViewPortDetector.js.map +1 -1
  13. package/lib/commonjs/useParticipant.js +9 -9
  14. package/lib/commonjs/useParticipant.js.map +1 -1
  15. package/lib/commonjs/useRoom.js +5 -5
  16. package/lib/commonjs/useRoom.js.map +1 -1
  17. package/lib/module/audio/AudioManager.js +15 -6
  18. package/lib/module/audio/AudioManager.js.map +1 -1
  19. package/lib/module/audio/AudioSession.js +2 -0
  20. package/lib/module/audio/AudioSession.js.map +1 -1
  21. package/lib/module/components/VideoView.js +13 -12
  22. package/lib/module/components/VideoView.js.map +1 -1
  23. package/lib/module/components/ViewPortDetector.js +134 -40
  24. package/lib/module/components/ViewPortDetector.js.map +1 -1
  25. package/lib/module/useParticipant.js +9 -9
  26. package/lib/module/useParticipant.js.map +1 -1
  27. package/lib/module/useRoom.js +5 -5
  28. package/lib/module/useRoom.js.map +1 -1
  29. package/lib/typescript/audio/AudioManager.d.ts +1 -1
  30. package/lib/typescript/audio/AudioSession.d.ts +2 -0
  31. package/lib/typescript/components/ViewPortDetector.d.ts +11 -4
  32. package/package.json +2 -2
  33. package/src/audio/AudioManager.ts +18 -8
  34. package/src/audio/AudioSession.ts +2 -0
  35. package/src/components/VideoView.tsx +20 -13
  36. package/src/components/ViewPortDetector.tsx +112 -21
  37. package/src/useParticipant.ts +15 -9
  38. package/src/useRoom.ts +5 -5
package/README.md CHANGED
@@ -1,14 +1,22 @@
1
1
  <!--BEGIN_BANNER_IMAGE-->
2
- <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="/.github/banner_dark.png">
4
- <source media="(prefers-color-scheme: light)" srcset="/.github/banner_light.png">
5
- <img style="width:100%;" alt="The LiveKit icon, the name of the repository and some sample code in the background." src="/.github/banner_light.png">
6
- </picture>
7
- <!--END_BANNER_IMAGE-->
2
+
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="/.github/banner_dark.png">
5
+ <source media="(prefers-color-scheme: light)" srcset="/.github/banner_light.png">
6
+ <img style="width:100%;" alt="The LiveKit icon, the name of the repository and some sample code in the background." src="https://raw.githubusercontent.com/livekit/client-sdk-react-native/main/.github/banner_light.png">
7
+ </picture>
8
+
9
+ <!--END_BANNER_IMAGE-->
8
10
 
9
11
  # livekit-react-native
10
12
 
11
- <!--BEGIN_DESCRIPTION-->Use this SDK to add real-time video, audio and data features to your React Native app. By connecting to a self- or cloud-hosted <a href="https://livekit.io/">LiveKit</a> server, you can quickly build applications like interactive live streaming or video calls with just a few lines of code.<!--END_DESCRIPTION-->
13
+ <!--BEGIN_DESCRIPTION-->
14
+ Use this SDK to add real-time video, audio and data features to your React Native app. By connecting to a self- or cloud-hosted <a href="https://livekit.io/">LiveKit</a> server, you can quickly build applications like interactive live streaming or video calls with just a few lines of code.
15
+ <!--END_DESCRIPTION-->
16
+
17
+ > [!NOTE]
18
+ > This is v2 of the React-Native SDK. When migrating from v1.x to v2.x you might encounter a small set of breaking changes.
19
+ > Read the [migration guide](https://docs.livekit.io/guides/migrate-from-v1/) for a detailed overview of what has changed.
12
20
 
13
21
  ## Installation
14
22
 
@@ -47,8 +55,8 @@ public class MainApplication extends Application implements ReactApplication {
47
55
  @Override
48
56
  public void onCreate() {
49
57
  // Place this above any other RN related initialization
50
- // When AudioType is omitted, it'll default to CommunicationAudioType
51
- // use MediaAudioType if user is only consuming audio, and not publishing
58
+ // When AudioType is omitted, it'll default to CommunicationAudioType.
59
+ // Use MediaAudioType if user is only consuming audio, and not publishing.
52
60
  LiveKitReactNative.setup(this, new AudioType.CommunicationAudioType());
53
61
 
54
62
  //...
@@ -62,11 +70,11 @@ Or in your **MainApplication.kt** if you are using RN 0.73+
62
70
  import com.livekit.reactnative.LiveKitReactNative
63
71
  import com.livekit.reactnative.audio.AudioType
64
72
 
65
- class MainActivity : ReactActivity() {
66
- override fun onCreate(savedInstanceState: Bundle?) {
73
+ class MainApplication : Application, ReactApplication() {
74
+ override fun onCreate() {
67
75
  // Place this above any other RN related initialization
68
- // When AudioType is omitted, it'll default to CommunicationAudioType
69
- // use MediaAudioType if user is only consuming audio, and not publishing
76
+ // When AudioType is omitted, it'll default to CommunicationAudioType.
77
+ // Use MediaAudioType if user is only consuming audio, and not publishing.
70
78
  LiveKitReactNative.setup(this, AudioType.CommunicationAudioType())
71
79
 
72
80
  //...
@@ -155,12 +163,13 @@ Additional documentation for the LiveKit SDK can be found at https://docs.liveki
155
163
 
156
164
  ## Audio sessions
157
165
 
158
- As seen in the above example, we've introduced a new class `AudioSession` that helps
166
+ As seen in the above example, we've introduced a class `AudioSession` that helps
159
167
  to manage the audio session on native platforms. This class wraps either [AudioManager](https://developer.android.com/reference/android/media/AudioManager) on Android, or [AVAudioSession](https://developer.apple.com/documentation/avfaudio/avaudiosession) on iOS.
160
168
 
161
169
  You can customize the configuration of the audio session with `configureAudio`.
162
170
 
163
- ### Media playback
171
+ ### Android
172
+ #### Media playback
164
173
 
165
174
  By default, the audio session is set up for bidirectional communication. In this mode, the audio framework exhibits the following behaviors:
166
175
 
@@ -170,8 +179,6 @@ By default, the audio session is set up for bidirectional communication. In this
170
179
 
171
180
  If you're leveraging LiveKit primarily for media playback, you have the option to reconfigure the audio session to better suit media playback. Here's how:
172
181
 
173
- Note: iOS audio session customization is in development, and will be documented here when released.
174
-
175
182
  ```js
176
183
  useEffect(() => {
177
184
  let connect = async () => {
@@ -193,7 +200,7 @@ useEffect(() => {
193
200
  }, [url, token, room]);
194
201
  ```
195
202
 
196
- ### Customizing audio session
203
+ #### Customizing audio session
197
204
 
198
205
  Instead of using our presets, you can further customize the audio session to suit your specific needs.
199
206
 
@@ -216,6 +223,12 @@ await AudioSession.configureAudio({
216
223
  await AudioSession.startAudioSession();
217
224
  ```
218
225
 
226
+ ### iOS
227
+
228
+ For iOS, the most appropriate audio configuration may change over time when local/remote
229
+ audio tracks publish and unpublish from the room. To adapt to this, the [`useIOSAudioManagement`](https://htmlpreview.github.io/?https://raw.githubusercontent.com/livekit/client-sdk-react-native/main/docs/functions/useIOSAudioManagement.html)
230
+ hook is advised over just configuring the audio session once for the entire audio session.
231
+
219
232
  ## Screenshare
220
233
 
221
234
  Enabling screenshare requires extra installation steps:
@@ -296,14 +309,13 @@ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the
296
309
  Apache License 2.0
297
310
 
298
311
  <!--BEGIN_REPO_NAV-->
299
-
300
312
  <br/><table>
301
-
302
313
  <thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
303
314
  <tbody>
304
- <tr><td>Client SDKs</td><td><a href="https://github.com/livekit/components-js">Components</a> · <a href="https://github.com/livekit/client-sdk-js">JavaScript</a> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <b>React Native</b> · <a href="https://github.com/livekit/client-sdk-rust">Rust</a> · <a href="https://github.com/livekit/client-sdk-python">Python</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (web)</a> · <a href="https://github.com/livekit/client-sdk-unity">Unity (beta)</a></td></tr><tr></tr>
305
- <tr><td>Server SDKs</td><td><a href="https://github.com/livekit/server-sdk-js">Node.js</a> · <a href="https://github.com/livekit/server-sdk-go">Golang</a> · <a href="https://github.com/livekit/server-sdk-ruby">Ruby</a> · <a href="https://github.com/livekit/server-sdk-kotlin">Java/Kotlin</a> · <a href="https://github.com/agence104/livekit-server-sdk-php">PHP (community)</a> · <a href="https://github.com/tradablebits/livekit-server-sdk-python">Python (community)</a></td></tr><tr></tr>
306
- <tr><td>Services</td><td><a href="https://github.com/livekit/livekit">Livekit server</a> · <a href="https://github.com/livekit/egress">Egress</a> · <a href="https://github.com/livekit/ingress">Ingress</a></td></tr><tr></tr>
315
+ <tr><td>Real-time SDKs</td><td><a href="https://github.com/livekit/components-js">React Components</a> · <a href="https://github.com/livekit/client-sdk-js">JavaScript</a> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <b>React Native</b> · <a href="https://github.com/livekit/client-sdk-rust">Rust</a> · <a href="https://github.com/livekit/client-sdk-python">Python</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (web)</a> · <a href="https://github.com/livekit/client-sdk-unity">Unity (beta)</a></td></tr><tr></tr>
316
+ <tr><td>Server APIs</td><td><a href="https://github.com/livekit/server-sdk-js">Node.js</a> · <a href="https://github.com/livekit/server-sdk-go">Golang</a> · <a href="https://github.com/livekit/server-sdk-ruby">Ruby</a> · <a href="https://github.com/livekit/server-sdk-kotlin">Java/Kotlin</a> · <a href="https://github.com/livekit/client-sdk-python">Python</a> · <a href="https://github.com/livekit/client-sdk-rust">Rust</a> · <a href="https://github.com/agence104/livekit-server-sdk-php">PHP (community)</a></td></tr><tr></tr>
317
+ <tr><td>Agents Frameworks</td><td><a href="https://github.com/livekit/agents">Python</a> · <a href="https://github.com/livekit/agent-playground">Playground</a></td></tr><tr></tr>
318
+ <tr><td>Services</td><td><a href="https://github.com/livekit/livekit">Livekit server</a> · <a href="https://github.com/livekit/egress">Egress</a> · <a href="https://github.com/livekit/ingress">Ingress</a> · <a href="https://github.com/livekit/sip">SIP</a></td></tr><tr></tr>
307
319
  <tr><td>Resources</td><td><a href="https://docs.livekit.io">Docs</a> · <a href="https://github.com/livekit-examples">Example apps</a> · <a href="https://livekit.io/cloud">Cloud</a> · <a href="https://docs.livekit.io/oss/deployment">Self-hosting</a> · <a href="https://github.com/livekit/livekit-cli">CLI</a></td></tr>
308
320
  </tbody>
309
321
  </table>
@@ -4,8 +4,8 @@ import android.app.Application
4
4
  import android.content.Context
5
5
  import android.os.Build
6
6
  import com.livekit.reactnative.audio.AudioType
7
- import com.livekit.reactnative.video.SimulcastVideoEncoderFactoryWrapper
8
- import com.livekit.reactnative.video.WrappedVideoDecoderFactoryProxy
7
+ import com.livekit.reactnative.video.CustomVideoEncoderFactory
8
+ import com.livekit.reactnative.video.CustomVideoDecoderFactory
9
9
  import com.oney.WebRTCModule.WebRTCModuleOptions
10
10
  import org.webrtc.audio.JavaAudioDeviceModule
11
11
 
@@ -21,8 +21,8 @@ object LiveKitReactNative {
21
21
  @JvmOverloads
22
22
  fun setup(context: Context, audioType: AudioType = AudioType.CommunicationAudioType()) {
23
23
  val options = WebRTCModuleOptions.getInstance()
24
- options.videoEncoderFactory = SimulcastVideoEncoderFactoryWrapper(null, true, true)
25
- options.videoDecoderFactory = WrappedVideoDecoderFactoryProxy()
24
+ options.videoEncoderFactory = CustomVideoEncoderFactory(null, true, true)
25
+ options.videoDecoderFactory = CustomVideoDecoderFactory()
26
26
 
27
27
  val useHardwareAudioProcessing = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
28
28
 
@@ -0,0 +1,67 @@
1
+ /*
2
+ * Copyright 2023-2024 LiveKit, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.livekit.reactnative.video
18
+
19
+ import com.oney.WebRTCModule.webrtcutils.SoftwareVideoDecoderFactoryProxy
20
+ import org.webrtc.EglBase
21
+ import org.webrtc.SoftwareVideoDecoderFactory
22
+ import org.webrtc.VideoCodecInfo
23
+ import org.webrtc.VideoDecoder
24
+ import org.webrtc.VideoDecoderFactory
25
+ import org.webrtc.WrappedVideoDecoderFactory
26
+
27
+ open class CustomVideoDecoderFactory(
28
+ private var forceSWCodec: Boolean = false,
29
+ private var forceSWCodecs: List<String> = listOf("VP9"),
30
+ ) : VideoDecoderFactory {
31
+ private val softwareVideoDecoderFactory = SoftwareVideoDecoderFactoryProxy()
32
+ private val wrappedVideoDecoderFactory = WrappedVideoDecoderFactoryProxy()
33
+
34
+ /**
35
+ * Set to true to force software codecs.
36
+ */
37
+ fun setForceSWCodec(forceSWCodec: Boolean) {
38
+ this.forceSWCodec = forceSWCodec
39
+ }
40
+
41
+ /**
42
+ * Set a list of codecs for which to use software codecs.
43
+ */
44
+ fun setForceSWCodecList(forceSWCodecs: List<String>) {
45
+ this.forceSWCodecs = forceSWCodecs
46
+ }
47
+
48
+ override fun createDecoder(videoCodecInfo: VideoCodecInfo): VideoDecoder? {
49
+ if (forceSWCodec) {
50
+ return softwareVideoDecoderFactory.createDecoder(videoCodecInfo)
51
+ }
52
+ if (forceSWCodecs.isNotEmpty()) {
53
+ if (forceSWCodecs.contains(videoCodecInfo.name)) {
54
+ return softwareVideoDecoderFactory.createDecoder(videoCodecInfo)
55
+ }
56
+ }
57
+ return wrappedVideoDecoderFactory.createDecoder(videoCodecInfo)
58
+ }
59
+
60
+ override fun getSupportedCodecs(): Array<VideoCodecInfo> {
61
+ return if (forceSWCodec && forceSWCodecs.isEmpty()) {
62
+ softwareVideoDecoderFactory.supportedCodecs
63
+ } else {
64
+ wrappedVideoDecoderFactory.supportedCodecs
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,74 @@
1
+ /*
2
+ * Copyright 2023-2024 LiveKit, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.livekit.reactnative.video
18
+
19
+ import com.oney.WebRTCModule.webrtcutils.SoftwareVideoEncoderFactoryProxy
20
+ import org.webrtc.EglBase
21
+ import org.webrtc.SoftwareVideoEncoderFactory
22
+ import org.webrtc.VideoCodecInfo
23
+ import org.webrtc.VideoEncoder
24
+ import org.webrtc.VideoEncoderFactory
25
+
26
+ open class CustomVideoEncoderFactory(
27
+ sharedContext: EglBase.Context?,
28
+ enableIntelVp8Encoder: Boolean,
29
+ enableH264HighProfile: Boolean,
30
+ private var forceSWCodec: Boolean = false,
31
+ private var forceSWCodecs: List<String> = listOf("VP9"),
32
+ ) : VideoEncoderFactory {
33
+ private val softwareVideoEncoderFactory = SoftwareVideoEncoderFactoryProxy()
34
+ private val simulcastVideoEncoderFactoryWrapper: SimulcastVideoEncoderFactoryWrapper
35
+
36
+ init {
37
+ simulcastVideoEncoderFactoryWrapper =
38
+ SimulcastVideoEncoderFactoryWrapper(sharedContext, enableIntelVp8Encoder, enableH264HighProfile)
39
+ }
40
+
41
+ /**
42
+ * Set to true to force software codecs.
43
+ */
44
+ fun setForceSWCodec(forceSWCodec: Boolean) {
45
+ this.forceSWCodec = forceSWCodec
46
+ }
47
+
48
+ /**
49
+ * Set a list of codecs for which to use software codecs.
50
+ */
51
+ fun setForceSWCodecList(forceSWCodecs: List<String>) {
52
+ this.forceSWCodecs = forceSWCodecs
53
+ }
54
+
55
+ override fun createEncoder(videoCodecInfo: VideoCodecInfo): VideoEncoder? {
56
+ if (forceSWCodec) {
57
+ return softwareVideoEncoderFactory.createEncoder(videoCodecInfo)
58
+ }
59
+ if (forceSWCodecs.isNotEmpty()) {
60
+ if (forceSWCodecs.contains(videoCodecInfo.name)) {
61
+ return softwareVideoEncoderFactory.createEncoder(videoCodecInfo)
62
+ }
63
+ }
64
+ return simulcastVideoEncoderFactoryWrapper.createEncoder(videoCodecInfo)
65
+ }
66
+
67
+ override fun getSupportedCodecs(): Array<VideoCodecInfo> {
68
+ return if (forceSWCodec && forceSWCodecs.isEmpty()) {
69
+ softwareVideoEncoderFactory.supportedCodecs
70
+ } else {
71
+ simulcastVideoEncoderFactoryWrapper.supportedCodecs
72
+ }
73
+ }
74
+ }
@@ -33,14 +33,23 @@ function useIOSAudioManagement(room) {
33
33
  const [localTrackCount, setLocalTrackCount] = (0, _react.useState)(0);
34
34
  const [remoteTrackCount, setRemoteTrackCount] = (0, _react.useState)(0);
35
35
  const trackState = (0, _react.useMemo)(() => computeAudioTrackState(localTrackCount, remoteTrackCount), [localTrackCount, remoteTrackCount]);
36
+ (0, _react.useEffect)(() => {
37
+ let recalculateTrackCounts = () => {
38
+ setLocalTrackCount(getLocalAudioTrackCount(room));
39
+ setRemoteTrackCount(getRemoteAudioTrackCount(room));
40
+ };
41
+
42
+ recalculateTrackCounts();
43
+ room.on(_livekitClient.RoomEvent.Connected, recalculateTrackCounts);
44
+ return () => {
45
+ room.off(_livekitClient.RoomEvent.Connected, recalculateTrackCounts);
46
+ };
47
+ }, [room]);
36
48
  (0, _react.useEffect)(() => {
37
49
  if (_reactNative.Platform.OS !== 'ios') {
38
50
  return () => {};
39
51
  }
40
52
 
41
- setLocalTrackCount(getLocalAudioTrackCount(room));
42
- setRemoteTrackCount(getRemoteAudioTrackCount(room));
43
-
44
53
  let onLocalPublished = () => {
45
54
  setLocalTrackCount(localTrackCount + 1);
46
55
  };
@@ -95,13 +104,13 @@ function computeAudioTrackState(localTracks, remoteTracks) {
95
104
  }
96
105
 
97
106
  function getLocalAudioTrackCount(room) {
98
- return room.localParticipant.audioTracks.entries.length;
107
+ return room.localParticipant.audioTrackPublications.size;
99
108
  }
100
109
 
101
110
  function getRemoteAudioTrackCount(room) {
102
111
  var audioTracks = 0;
103
- room.participants.forEach(participant => {
104
- audioTracks += participant.audioTracks.entries.length;
112
+ room.remoteParticipants.forEach(participant => {
113
+ audioTracks += participant.audioTrackPublications.size;
105
114
  });
106
115
  return audioTracks;
107
116
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["AudioManager.ts"],"names":["useIOSAudioManagement","room","preferSpeakerOutput","onConfigureNativeAudio","localTrackCount","setLocalTrackCount","remoteTrackCount","setRemoteTrackCount","trackState","computeAudioTrackState","Platform","OS","getLocalAudioTrackCount","getRemoteAudioTrackCount","onLocalPublished","onLocalUnpublished","log","warn","Math","max","onRemotePublished","onRemoteUnpublished","on","RoomEvent","LocalTrackPublished","LocalTrackUnpublished","TrackPublished","TrackUnpublished","off","configFunc","getDefaultAppleAudioConfigurationForMode","audioConfig","AudioSession","setAppleAudioConfiguration","localTracks","remoteTracks","localParticipant","audioTracks","entries","length","participants","forEach","participant"],"mappings":"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","sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport { Platform } from 'react-native';\nimport { RoomEvent, type Room } from 'livekit-client';\nimport AudioSession, {\n getDefaultAppleAudioConfigurationForMode,\n type AppleAudioConfiguration,\n type AudioTrackState,\n} from './AudioSession';\nimport { log } from '..';\n\n/**\n * Handles setting the appropriate AVAudioSession options automatically\n * depending on the audio track states of the Room.\n *\n * @param room\n * @param preferSpeakerOutput\n * @param onConfigureNativeAudio A custom method for determining options used.\n */\nexport function useIOSAudioManagement(\n room: Room,\n preferSpeakerOutput: boolean = true,\n onConfigureNativeAudio?: (\n trackState: AudioTrackState,\n preferSpeakerOutput: boolean\n ) => AppleAudioConfiguration\n) {\n const [localTrackCount, setLocalTrackCount] = useState(0);\n const [remoteTrackCount, setRemoteTrackCount] = useState(0);\n const trackState = useMemo(\n () => computeAudioTrackState(localTrackCount, remoteTrackCount),\n [localTrackCount, remoteTrackCount]\n );\n\n useEffect(() => {\n if (Platform.OS !== 'ios') {\n return () => {};\n }\n\n setLocalTrackCount(getLocalAudioTrackCount(room));\n setRemoteTrackCount(getRemoteAudioTrackCount(room));\n\n let onLocalPublished = () => {\n setLocalTrackCount(localTrackCount + 1);\n };\n let onLocalUnpublished = () => {\n if (localTrackCount - 1 < 0) {\n log.warn(\n 'mismatched local audio track count! attempted to reduce track count below zero.'\n );\n }\n setLocalTrackCount(Math.max(localTrackCount - 1, 0));\n };\n let onRemotePublished = () => {\n setRemoteTrackCount(remoteTrackCount + 1);\n };\n let onRemoteUnpublished = () => {\n if (remoteTrackCount - 1 < 0) {\n log.warn(\n 'mismatched remote audio track count! attempted to reduce track count below zero.'\n );\n }\n setRemoteTrackCount(Math.max(remoteTrackCount - 1, 0));\n };\n\n room\n .on(RoomEvent.LocalTrackPublished, onLocalPublished)\n .on(RoomEvent.LocalTrackUnpublished, onLocalUnpublished)\n .on(RoomEvent.TrackPublished, onRemotePublished)\n .on(RoomEvent.TrackUnpublished, onRemoteUnpublished);\n\n return () => {\n room\n .off(RoomEvent.LocalTrackPublished, onLocalPublished)\n .off(RoomEvent.LocalTrackUnpublished, onLocalUnpublished)\n .off(RoomEvent.TrackPublished, onRemotePublished)\n .off(RoomEvent.TrackUnpublished, onRemoteUnpublished);\n };\n }, [room, localTrackCount, remoteTrackCount]);\n\n useEffect(() => {\n if (Platform.OS !== 'ios') {\n return;\n }\n\n let configFunc =\n onConfigureNativeAudio ?? getDefaultAppleAudioConfigurationForMode;\n let audioConfig = configFunc(trackState, preferSpeakerOutput);\n\n AudioSession.setAppleAudioConfiguration(audioConfig);\n }, [trackState, onConfigureNativeAudio, preferSpeakerOutput]);\n}\n\nfunction computeAudioTrackState(\n localTracks: number,\n remoteTracks: number\n): AudioTrackState {\n if (localTracks > 0 && remoteTracks > 0) {\n return 'localAndRemote';\n } else if (localTracks > 0 && remoteTracks === 0) {\n return 'localOnly';\n } else if (localTracks === 0 && remoteTracks > 0) {\n return 'remoteOnly';\n } else {\n return 'none';\n }\n}\n\nfunction getLocalAudioTrackCount(room: Room): number {\n return room.localParticipant.audioTracks.entries.length;\n}\n\nfunction getRemoteAudioTrackCount(room: Room): number {\n var audioTracks = 0;\n room.participants.forEach((participant) => {\n audioTracks += participant.audioTracks.entries.length;\n });\n\n return audioTracks;\n}\n"]}
1
+ {"version":3,"sources":["AudioManager.ts"],"names":["useIOSAudioManagement","room","preferSpeakerOutput","onConfigureNativeAudio","localTrackCount","setLocalTrackCount","remoteTrackCount","setRemoteTrackCount","trackState","computeAudioTrackState","recalculateTrackCounts","getLocalAudioTrackCount","getRemoteAudioTrackCount","on","RoomEvent","Connected","off","Platform","OS","onLocalPublished","onLocalUnpublished","log","warn","Math","max","onRemotePublished","onRemoteUnpublished","LocalTrackPublished","LocalTrackUnpublished","TrackPublished","TrackUnpublished","configFunc","getDefaultAppleAudioConfigurationForMode","audioConfig","AudioSession","setAppleAudioConfiguration","localTracks","remoteTracks","localParticipant","audioTrackPublications","size","audioTracks","remoteParticipants","forEach","participant"],"mappings":"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","sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport { Platform } from 'react-native';\nimport { RoomEvent, Room } from 'livekit-client';\nimport AudioSession, {\n getDefaultAppleAudioConfigurationForMode,\n type AppleAudioConfiguration,\n type AudioTrackState,\n} from './AudioSession';\nimport { log } from '..';\n\n/**\n * Handles setting the appropriate AVAudioSession options automatically\n * depending on the audio track states of the Room.\n *\n * @param room\n * @param preferSpeakerOutput\n * @param onConfigureNativeAudio A custom method for determining options used.\n */\nexport function useIOSAudioManagement(\n room: Room,\n preferSpeakerOutput: boolean = true,\n onConfigureNativeAudio?: (\n trackState: AudioTrackState,\n preferSpeakerOutput: boolean\n ) => AppleAudioConfiguration\n) {\n const [localTrackCount, setLocalTrackCount] = useState(0);\n const [remoteTrackCount, setRemoteTrackCount] = useState(0);\n const trackState = useMemo(\n () => computeAudioTrackState(localTrackCount, remoteTrackCount),\n [localTrackCount, remoteTrackCount]\n );\n\n useEffect(() => {\n let recalculateTrackCounts = () => {\n setLocalTrackCount(getLocalAudioTrackCount(room));\n setRemoteTrackCount(getRemoteAudioTrackCount(room));\n };\n\n recalculateTrackCounts();\n\n room.on(RoomEvent.Connected, recalculateTrackCounts);\n\n return () => {\n room.off(RoomEvent.Connected, recalculateTrackCounts);\n };\n }, [room]);\n useEffect(() => {\n if (Platform.OS !== 'ios') {\n return () => {};\n }\n\n let onLocalPublished = () => {\n setLocalTrackCount(localTrackCount + 1);\n };\n let onLocalUnpublished = () => {\n if (localTrackCount - 1 < 0) {\n log.warn(\n 'mismatched local audio track count! attempted to reduce track count below zero.'\n );\n }\n setLocalTrackCount(Math.max(localTrackCount - 1, 0));\n };\n let onRemotePublished = () => {\n setRemoteTrackCount(remoteTrackCount + 1);\n };\n let onRemoteUnpublished = () => {\n if (remoteTrackCount - 1 < 0) {\n log.warn(\n 'mismatched remote audio track count! attempted to reduce track count below zero.'\n );\n }\n setRemoteTrackCount(Math.max(remoteTrackCount - 1, 0));\n };\n\n room\n .on(RoomEvent.LocalTrackPublished, onLocalPublished)\n .on(RoomEvent.LocalTrackUnpublished, onLocalUnpublished)\n .on(RoomEvent.TrackPublished, onRemotePublished)\n .on(RoomEvent.TrackUnpublished, onRemoteUnpublished);\n\n return () => {\n room\n .off(RoomEvent.LocalTrackPublished, onLocalPublished)\n .off(RoomEvent.LocalTrackUnpublished, onLocalUnpublished)\n .off(RoomEvent.TrackPublished, onRemotePublished)\n .off(RoomEvent.TrackUnpublished, onRemoteUnpublished);\n };\n }, [room, localTrackCount, remoteTrackCount]);\n\n useEffect(() => {\n if (Platform.OS !== 'ios') {\n return;\n }\n\n let configFunc =\n onConfigureNativeAudio ?? getDefaultAppleAudioConfigurationForMode;\n let audioConfig = configFunc(trackState, preferSpeakerOutput);\n AudioSession.setAppleAudioConfiguration(audioConfig);\n }, [trackState, onConfigureNativeAudio, preferSpeakerOutput]);\n}\n\nfunction computeAudioTrackState(\n localTracks: number,\n remoteTracks: number\n): AudioTrackState {\n if (localTracks > 0 && remoteTracks > 0) {\n return 'localAndRemote';\n } else if (localTracks > 0 && remoteTracks === 0) {\n return 'localOnly';\n } else if (localTracks === 0 && remoteTracks > 0) {\n return 'remoteOnly';\n } else {\n return 'none';\n }\n}\n\nfunction getLocalAudioTrackCount(room: Room): number {\n return room.localParticipant.audioTrackPublications.size;\n}\n\nfunction getRemoteAudioTrackCount(room: Room): number {\n var audioTracks = 0;\n room.remoteParticipants.forEach((participant) => {\n audioTracks += participant.audioTrackPublications.size;\n });\n\n return audioTracks;\n}\n"]}
@@ -40,6 +40,8 @@ const LivekitReactNative = _reactNative.NativeModules.LivekitReactNative ? _reac
40
40
  *
41
41
  * See {@link AndroidAudioTypePresets} for pre-configured values.
42
42
  *
43
+ * NOTE: If `audioTypeOptions` is set, this must also be reflected in your android MainApplication setup.
44
+ *
43
45
  * ----
44
46
  * iOS
45
47
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["AudioSession.ts"],"names":["LINKING_ERROR","Platform","select","ios","default","LivekitReactNative","NativeModules","Proxy","get","Error","AndroidAudioTypePresets","communication","manageAudioFocus","audioMode","audioFocusMode","audioStreamType","audioAttributesUsageType","audioAttributesContentType","media","getDefaultAppleAudioConfigurationForMode","mode","preferSpeakerOutput","audioCategory","audioCategoryOptions","AudioSession","config","configureAudio","startAudioSession","stopAudioSession","OS","getAudioOutputs","deviceId","selectAudioOutput","showAudioRoutePicker","setAppleAudioConfiguration"],"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;AACA;AACA;AACA;AACA;AACA;;AA6GO,MAAMU,uBASZ,GAAG;AACFC,EAAAA,aAAa,EAAE;AACbC,IAAAA,gBAAgB,EAAE,IADL;AAEbC,IAAAA,SAAS,EAAE,iBAFE;AAGbC,IAAAA,cAAc,EAAE,MAHH;AAIbC,IAAAA,eAAe,EAAE,WAJJ;AAKbC,IAAAA,wBAAwB,EAAE,oBALb;AAMbC,IAAAA,0BAA0B,EAAE;AANf,GADb;AASFC,EAAAA,KAAK,EAAE;AACLN,IAAAA,gBAAgB,EAAE,IADb;AAELC,IAAAA,SAAS,EAAE,QAFN;AAGLC,IAAAA,cAAc,EAAE,MAHX;AAILC,IAAAA,eAAe,EAAE,OAJZ;AAKLC,IAAAA,wBAAwB,EAAE,OALrB;AAMLC,IAAAA,0BAA0B,EAAE;AANvB;AATL,CATG;;;AAmEA,SAASE,wCAAT,CACLC,IADK,EAGoB;AAAA,MADzBC,mBACyB,uEADM,IACN;;AACzB,MAAID,IAAI,KAAK,YAAb,EAA2B;AACzB,WAAO;AACLE,MAAAA,aAAa,EAAE,UADV;AAELC,MAAAA,oBAAoB,EAAE,CAAC,eAAD,CAFjB;AAGLV,MAAAA,SAAS,EAAE;AAHN,KAAP;AAKD,GAND,MAMO,IAAIO,IAAI,KAAK,gBAAT,IAA6BA,IAAI,KAAK,WAA1C,EAAuD;AAC5D,WAAO;AACLE,MAAAA,aAAa,EAAE,eADV;AAELC,MAAAA,oBAAoB,EAAE,CAAC,gBAAD,EAAmB,eAAnB,CAFjB;AAGLV,MAAAA,SAAS,EAAEQ,mBAAmB,GAAG,WAAH,GAAiB;AAH1C,KAAP;AAKD;;AAED,SAAO;AACLC,IAAAA,aAAa,EAAE,aADV;AAELC,IAAAA,oBAAoB,EAAE,EAFjB;AAGLV,IAAAA,SAAS,EAAE;AAHN,GAAP;AAKD;;AAEc,MAAMW,YAAN,CAAmB;;;;gBAAbA,Y,oBAQK,MAAOC,MAAP,IAAsC;AAC5D,QAAMpB,kBAAkB,CAACqB,cAAnB,CAAkCD,MAAlC,CAAN;AACD,C;;gBAVkBD,Y,uBAeQ,YAAY;AACrC,QAAMnB,kBAAkB,CAACsB,iBAAnB,EAAN;AACD,C;;gBAjBkBH,Y,sBAsBO,YAAY;AACpC,QAAMnB,kBAAkB,CAACuB,gBAAnB,EAAN;AACD,C;;gBAxBkBJ,Y,qBAkDM,YAA+B;AACtD,MAAIvB,sBAAS4B,EAAT,KAAgB,KAApB,EAA2B;AACzB,WAAO,CAAC,SAAD,EAAY,eAAZ,CAAP;AACD,GAFD,MAEO,IAAI5B,sBAAS4B,EAAT,KAAgB,SAApB,EAA+B;AACpC,WAAQ,MAAMxB,kBAAkB,CAACyB,eAAnB,EAAd;AACD,GAFM,MAEA;AACL,WAAO,EAAP;AACD;AACF,C;;gBA1DkBN,Y,uBAmEQ,MAAOO,QAAP,IAA4B;AACrD,QAAM1B,kBAAkB,CAAC2B,iBAAnB,CAAqCD,QAArC,CAAN;AACD,C;;gBArEkBP,Y,0BA4EW,YAAY;AACxC,MAAIvB,sBAAS4B,EAAT,KAAgB,KAApB,EAA2B;AACzB,UAAMxB,kBAAkB,CAAC4B,oBAAnB,EAAN;AACD;AACF,C;;gBAhFkBT,Y,gCAkFiB,MAClCC,MADkC,IAE/B;AACH,MAAIxB,sBAAS4B,EAAT,KAAgB,KAApB,EAA2B;AACzB,UAAMxB,kBAAkB,CAAC6B,0BAAnB,CAA8CT,MAA9C,CAAN;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. `\"bluetooth\"\n * 2. `\"headset\"``\n * 3. `\"speaker\"`\n * 4. `\"earpiece\"`\n *\n * * audioTypeOptions - An {@link AndroidAudioTypeOptions} object which provides the\n * audio options to use on Android.\n *\n * See {@link AndroidAudioTypePresets} for pre-configured values.\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 audioTypeOptions: AndroidAudioTypeOptions;\n };\n ios?: {\n defaultOutput?: 'speaker' | 'earpiece';\n };\n};\n\nexport type AndroidAudioTypeOptions = {\n /**\n * Whether LiveKit should handle managing the audio focus or not.\n *\n * Defaults to true.\n */\n manageAudioFocus?: boolean;\n\n /**\n * Corresponds to {@link https://developer.android.com/reference/android/media/AudioManager#setMode(int)}\n *\n * Defaults to 'inCommunication'.\n */\n audioMode?:\n | 'normal'\n | 'callScreening'\n | 'inCall'\n | 'inCommunication'\n | 'ringtone';\n\n /**\n * Corresponds to the duration hint when requesting audio focus.\n *\n * Defaults to 'gain'.\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioManager#AUDIOFOCUS_GAIN}\n */\n audioFocusMode?:\n | 'gain'\n | 'gainTransient'\n | 'gainTransientExclusive'\n | 'gainTransientMayDuck';\n\n /**\n * Corresponds to Android's AudioAttributes usage type.\n *\n * Defaults to 'voiceCommunication'.\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioAttributes}\n */\n audioAttributesUsageType?:\n | 'alarm'\n | 'assistanceAccessibility'\n | 'assistanceNavigationGuidance'\n | 'assistanceSonification'\n | 'assistant'\n | 'game'\n | 'media'\n | 'notification'\n | 'notificationEvent'\n | 'notificationRingtone'\n | 'unknown'\n | 'voiceCommunication'\n | 'voiceCommunicationSignalling';\n\n /**\n * Corresponds to Android's AndroidAttributes content type.\n *\n * Defaults to 'speech'.\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioAttributes}\n */\n audioAttributesContentType?:\n | 'movie'\n | 'music'\n | 'sonification'\n | 'speech'\n | 'unknown';\n\n /**\n * Corresponds to the stream type when requesting audio focus. Used on pre-O devices.\n *\n * Defaults to 'voiceCall'\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioManager#STREAM_VOICE_CALL}\n */\n audioStreamType?:\n | 'accessibility'\n | 'alarm'\n | 'dtmf'\n | 'music'\n | 'notification'\n | 'ring'\n | 'system'\n | 'voiceCall';\n\n /**\n * On certain Android devices, audio routing does not function properly and\n * bluetooth microphones will not work unless audio mode is set to\n * `inCommunication` or `inCall`. Audio routing is turned off those cases.\n *\n * If this set to true, will attempt to do audio routing regardless of audio mode.\n *\n * Defaults to false.\n */\n forceHandleAudioRouting?: boolean;\n};\n\nexport const AndroidAudioTypePresets: {\n /**\n * A pre-configured AndroidAudioConfiguration for voice communication.\n */\n communication: AndroidAudioTypeOptions;\n /**\n * A pre-configured AndroidAudioConfiguration for media playback.\n */\n media: AndroidAudioTypeOptions;\n} = {\n communication: {\n manageAudioFocus: true,\n audioMode: 'inCommunication',\n audioFocusMode: 'gain',\n audioStreamType: 'voiceCall',\n audioAttributesUsageType: 'voiceCommunication',\n audioAttributesContentType: 'speech',\n },\n media: {\n manageAudioFocus: true,\n audioMode: 'normal',\n audioFocusMode: 'gain',\n audioStreamType: 'music',\n audioAttributesUsageType: 'media',\n audioAttributesContentType: 'unknown',\n },\n} as const;\n\nexport type AppleAudioMode =\n | 'default'\n | 'gameChat'\n | 'measurement'\n | 'moviePlayback'\n | 'spokenAudio'\n | 'videoChat'\n | 'videoRecording'\n | 'voiceChat'\n | 'voicePrompt';\n\nexport type AppleAudioCategory =\n | 'soloAmbient'\n | 'playback'\n | 'record'\n | 'playAndRecord'\n | 'multiRoute';\n\nexport type AppleAudioCategoryOption =\n | 'mixWithOthers'\n | 'duckOthers'\n | 'interruptSpokenAudioAndMixWithOthers'\n | 'allowBluetooth'\n | 'allowBluetoothA2DP'\n | 'allowAirPlay'\n | 'defaultToSpeaker';\n\nexport type AppleAudioConfiguration = {\n audioCategory?: AppleAudioCategory;\n audioCategoryOptions?: AppleAudioCategoryOption[];\n audioMode?: AppleAudioMode;\n};\n\nexport type AudioTrackState =\n | 'none'\n | 'remoteOnly'\n | 'localOnly'\n | 'localAndRemote';\n\nexport function getDefaultAppleAudioConfigurationForMode(\n mode: AudioTrackState,\n preferSpeakerOutput: boolean = true\n): AppleAudioConfiguration {\n if (mode === 'remoteOnly') {\n return {\n audioCategory: 'playback',\n audioCategoryOptions: ['mixWithOthers'],\n audioMode: 'spokenAudio',\n };\n } else if (mode === 'localAndRemote' || mode === 'localOnly') {\n return {\n audioCategory: 'playAndRecord',\n audioCategoryOptions: ['allowBluetooth', 'mixWithOthers'],\n audioMode: preferSpeakerOutput ? 'videoChat' : 'voiceChat',\n };\n }\n\n return {\n audioCategory: 'soloAmbient',\n audioCategoryOptions: [],\n audioMode: 'default',\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 * See also useIOSAudioManagement for automatic configuration of iOS audio options.\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 * ----\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 static setAppleAudioConfiguration = async (\n config: AppleAudioConfiguration\n ) => {\n if (Platform.OS === 'ios') {\n await LivekitReactNative.setAppleAudioConfiguration(config);\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["AudioSession.ts"],"names":["LINKING_ERROR","Platform","select","ios","default","LivekitReactNative","NativeModules","Proxy","get","Error","AndroidAudioTypePresets","communication","manageAudioFocus","audioMode","audioFocusMode","audioStreamType","audioAttributesUsageType","audioAttributesContentType","media","getDefaultAppleAudioConfigurationForMode","mode","preferSpeakerOutput","audioCategory","audioCategoryOptions","AudioSession","config","configureAudio","startAudioSession","stopAudioSession","OS","getAudioOutputs","deviceId","selectAudioOutput","showAudioRoutePicker","setAppleAudioConfiguration"],"mappings":"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","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. `\"bluetooth\"\n * 2. `\"headset\"``\n * 3. `\"speaker\"`\n * 4. `\"earpiece\"`\n *\n * * audioTypeOptions - An {@link AndroidAudioTypeOptions} object which provides the\n * audio options to use on Android.\n *\n * See {@link AndroidAudioTypePresets} for pre-configured values.\n *\n * NOTE: If `audioTypeOptions` is set, this must also be reflected in your android MainApplication setup.\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 audioTypeOptions: AndroidAudioTypeOptions;\n };\n ios?: {\n defaultOutput?: 'speaker' | 'earpiece';\n };\n};\n\nexport type AndroidAudioTypeOptions = {\n /**\n * Whether LiveKit should handle managing the audio focus or not.\n *\n * Defaults to true.\n */\n manageAudioFocus?: boolean;\n\n /**\n * Corresponds to {@link https://developer.android.com/reference/android/media/AudioManager#setMode(int)}\n *\n * Defaults to 'inCommunication'.\n */\n audioMode?:\n | 'normal'\n | 'callScreening'\n | 'inCall'\n | 'inCommunication'\n | 'ringtone';\n\n /**\n * Corresponds to the duration hint when requesting audio focus.\n *\n * Defaults to 'gain'.\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioManager#AUDIOFOCUS_GAIN}\n */\n audioFocusMode?:\n | 'gain'\n | 'gainTransient'\n | 'gainTransientExclusive'\n | 'gainTransientMayDuck';\n\n /**\n * Corresponds to Android's AudioAttributes usage type.\n *\n * Defaults to 'voiceCommunication'.\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioAttributes}\n */\n audioAttributesUsageType?:\n | 'alarm'\n | 'assistanceAccessibility'\n | 'assistanceNavigationGuidance'\n | 'assistanceSonification'\n | 'assistant'\n | 'game'\n | 'media'\n | 'notification'\n | 'notificationEvent'\n | 'notificationRingtone'\n | 'unknown'\n | 'voiceCommunication'\n | 'voiceCommunicationSignalling';\n\n /**\n * Corresponds to Android's AndroidAttributes content type.\n *\n * Defaults to 'speech'.\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioAttributes}\n */\n audioAttributesContentType?:\n | 'movie'\n | 'music'\n | 'sonification'\n | 'speech'\n | 'unknown';\n\n /**\n * Corresponds to the stream type when requesting audio focus. Used on pre-O devices.\n *\n * Defaults to 'voiceCall'\n *\n * See also {@link https://developer.android.com/reference/android/media/AudioManager#STREAM_VOICE_CALL}\n */\n audioStreamType?:\n | 'accessibility'\n | 'alarm'\n | 'dtmf'\n | 'music'\n | 'notification'\n | 'ring'\n | 'system'\n | 'voiceCall';\n\n /**\n * On certain Android devices, audio routing does not function properly and\n * bluetooth microphones will not work unless audio mode is set to\n * `inCommunication` or `inCall`. Audio routing is turned off those cases.\n *\n * If this set to true, will attempt to do audio routing regardless of audio mode.\n *\n * Defaults to false.\n */\n forceHandleAudioRouting?: boolean;\n};\n\nexport const AndroidAudioTypePresets: {\n /**\n * A pre-configured AndroidAudioConfiguration for voice communication.\n */\n communication: AndroidAudioTypeOptions;\n /**\n * A pre-configured AndroidAudioConfiguration for media playback.\n */\n media: AndroidAudioTypeOptions;\n} = {\n communication: {\n manageAudioFocus: true,\n audioMode: 'inCommunication',\n audioFocusMode: 'gain',\n audioStreamType: 'voiceCall',\n audioAttributesUsageType: 'voiceCommunication',\n audioAttributesContentType: 'speech',\n },\n media: {\n manageAudioFocus: true,\n audioMode: 'normal',\n audioFocusMode: 'gain',\n audioStreamType: 'music',\n audioAttributesUsageType: 'media',\n audioAttributesContentType: 'unknown',\n },\n} as const;\n\nexport type AppleAudioMode =\n | 'default'\n | 'gameChat'\n | 'measurement'\n | 'moviePlayback'\n | 'spokenAudio'\n | 'videoChat'\n | 'videoRecording'\n | 'voiceChat'\n | 'voicePrompt';\n\nexport type AppleAudioCategory =\n | 'soloAmbient'\n | 'playback'\n | 'record'\n | 'playAndRecord'\n | 'multiRoute';\n\nexport type AppleAudioCategoryOption =\n | 'mixWithOthers'\n | 'duckOthers'\n | 'interruptSpokenAudioAndMixWithOthers'\n | 'allowBluetooth'\n | 'allowBluetoothA2DP'\n | 'allowAirPlay'\n | 'defaultToSpeaker';\n\nexport type AppleAudioConfiguration = {\n audioCategory?: AppleAudioCategory;\n audioCategoryOptions?: AppleAudioCategoryOption[];\n audioMode?: AppleAudioMode;\n};\n\nexport type AudioTrackState =\n | 'none'\n | 'remoteOnly'\n | 'localOnly'\n | 'localAndRemote';\n\nexport function getDefaultAppleAudioConfigurationForMode(\n mode: AudioTrackState,\n preferSpeakerOutput: boolean = true\n): AppleAudioConfiguration {\n if (mode === 'remoteOnly') {\n return {\n audioCategory: 'playback',\n audioCategoryOptions: ['mixWithOthers'],\n audioMode: 'spokenAudio',\n };\n } else if (mode === 'localAndRemote' || mode === 'localOnly') {\n return {\n audioCategory: 'playAndRecord',\n audioCategoryOptions: ['allowBluetooth', 'mixWithOthers'],\n audioMode: preferSpeakerOutput ? 'videoChat' : 'voiceChat',\n };\n }\n\n return {\n audioCategory: 'soloAmbient',\n audioCategoryOptions: [],\n audioMode: 'default',\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 * See also useIOSAudioManagement for automatic configuration of iOS audio options.\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 * ----\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 static setAppleAudioConfiguration = async (\n config: AppleAudioConfiguration\n ) => {\n if (Platform.OS === 'ios') {\n await LivekitReactNative.setAppleAudioConfiguration(config);\n }\n };\n}\n"]}
@@ -39,6 +39,11 @@ const VideoView = _ref => {
39
39
  info.something = videoTrack;
40
40
  return info;
41
41
  });
42
+ const layoutOnChange = (0, React.useCallback)(event => elementInfo.onLayout(event), [elementInfo]);
43
+ const visibilityOnChange = (0, React.useCallback)(isVisible => elementInfo.onVisibility(isVisible), [elementInfo]);
44
+ const shouldObserveVisibility = (0, React.useMemo)(() => {
45
+ return videoTrack instanceof _livekitClient.RemoteVideoTrack && videoTrack.isAdaptiveStream;
46
+ }, [videoTrack]);
42
47
  const [mediaStream, setMediaStream] = (0, React.useState)(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
43
48
  (0, React.useEffect)(() => {
44
49
  setMediaStream(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
@@ -70,18 +75,14 @@ const VideoView = _ref => {
70
75
  style: { ...style,
71
76
  ...styles.container
72
77
  },
73
- onLayout: event => {
74
- elementInfo.onLayout(event);
75
- }
78
+ onLayout: layoutOnChange
76
79
  }, /*#__PURE__*/React.createElement(_ViewPortDetector.default, {
77
- onChange: isVisible => elementInfo.onVisibility(isVisible),
78
- style: styles.videoView
79
- }, /*#__PURE__*/React.createElement(_reactNativeWebrtc.RTCView // eslint-disable-next-line react-native/no-inline-styles
80
- , {
81
- style: {
82
- flex: 1,
83
- width: '100%'
84
- },
80
+ onChange: visibilityOnChange,
81
+ style: styles.videoView,
82
+ disabled: !shouldObserveVisibility,
83
+ propKey: videoTrack
84
+ }, /*#__PURE__*/React.createElement(_reactNativeWebrtc.RTCView, {
85
+ style: styles.videoView,
85
86
  streamURL: (_mediaStream$toURL = mediaStream === null || mediaStream === void 0 ? void 0 : mediaStream.toURL()) !== null && _mediaStream$toURL !== void 0 ? _mediaStream$toURL : '',
86
87
  objectFit: objectFit,
87
88
  zOrder: zOrder,
@@ -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","flex","width","toURL","StyleSheet","create","_width","_height","observe","_observing","stopObserving","height","nativeEvent","layout","handleResize","visible","visibilityChangedAt","Date","now","handleVisibilityChanged"],"mappings":";;;;;;;AAAA;;AAEA;;AACA;;AAOA;;AAGA;;;;;;;;;;AAUO,MAAMA,SAAS,GAAG,QAMZ;AAAA;;AAAA,MANa;AACxBC,IAAAA,KAAK,GAAG,EADgB;AAExBC,IAAAA,UAFwB;AAGxBC,IAAAA,SAAS,GAAG,OAHY;AAIxBC,IAAAA,MAJwB;AAKxBC,IAAAA;AALwB,GAMb;AACX,QAAM,CAACC,WAAD,IAAgB,oBAAS,MAAM;AACnC,QAAIC,IAAI,GAAG,IAAIC,oBAAJ,EAAX;AACAD,IAAAA,IAAI,CAACE,EAAL,GAAUP,UAAV,aAAUA,UAAV,uBAAUA,UAAU,CAAEQ,GAAtB;AACAH,IAAAA,IAAI,CAACI,SAAL,GAAiBT,UAAjB;AACA,WAAOK,IAAP;AACD,GALqB,CAAtB;AAOA,QAAM,CAACK,WAAD,EAAcC,cAAd,IAAgC,oBAASX,UAAT,aAASA,UAAT,uBAASA,UAAU,CAAEU,WAArB,CAAtC;AACA,uBAAU,MAAM;AACdC,IAAAA,cAAc,CAACX,UAAD,aAACA,UAAD,uBAACA,UAAU,CAAEU,WAAb,CAAd;;AACA,QAAIV,UAAU,YAAYY,8BAA1B,EAA2C;AACzC,YAAMC,WAAW,GAAIC,KAAD,IAAyB;AAC3CH,QAAAA,cAAc,CAACG,KAAD,aAACA,KAAD,uBAACA,KAAK,CAAEJ,WAAR,CAAd;AACD,OAFD;;AAGAV,MAAAA,UAAU,CAACe,EAAX,CAAcC,0BAAWC,SAAzB,EAAoCJ,WAApC;AAEA,aAAO,MAAM;AACXb,QAAAA,UAAU,CAACkB,GAAX,CAAeF,0BAAWC,SAA1B,EAAqCJ,WAArC;AACD,OAFD;AAGD,KATD,MASO;AACL,aAAO,MAAM,CAAE,CAAf;AACD;AACF,GAdD,EAcG,CAACb,UAAD,CAdH;AAgBA,uBAAU,MAAM;AACd,QAAIA,UAAU,YAAYmB,+BAAtB,IAA0CnB,UAAU,CAACoB,gBAAzD,EAA2E;AACzEpB,MAAAA,UAAU,SAAV,IAAAA,UAAU,WAAV,YAAAA,UAAU,CAAEqB,kBAAZ,CAA+BjB,WAA/B;AACA,aAAO,MAAM;AACXJ,QAAAA,UAAU,SAAV,IAAAA,UAAU,WAAV,YAAAA,UAAU,CAAEsB,wBAAZ,CAAqClB,WAArC;AACD,OAFD;AAGD,KALD,MAKO;AACL,aAAO,MAAM,CAAE,CAAf;AACD;AACF,GATD,EASG,CAACJ,UAAD,EAAaI,WAAb,CATH;AAWA,sBACE,oBAAC,iBAAD;AACE,IAAA,KAAK,EAAE,EAAE,GAAGL,KAAL;AAAY,SAAGwB,MAAM,CAACC;AAAtB,KADT;AAEE,IAAA,QAAQ,EAAGC,KAAD,IAAW;AACnBrB,MAAAA,WAAW,CAACsB,QAAZ,CAAqBD,KAArB;AACD;AAJH,kBAME,oBAAC,yBAAD;AACE,IAAA,QAAQ,EAAGE,SAAD,IAAwBvB,WAAW,CAACwB,YAAZ,CAAyBD,SAAzB,CADpC;AAEE,IAAA,KAAK,EAAEJ,MAAM,CAACM;AAFhB,kBAIE,oBAAC,0BAAD,CACE;AADF;AAEE,IAAA,KAAK,EAAE;AACLC,MAAAA,IAAI,EAAE,CADD;AAELC,MAAAA,KAAK,EAAE;AAFF,KAFT;AAME,IAAA,SAAS,wBAAErB,WAAF,aAAEA,WAAF,uBAAEA,WAAW,CAAEsB,KAAb,EAAF,mEAA0B,EANrC;AAOE,IAAA,SAAS,EAAE/B,SAPb;AAQE,IAAA,MAAM,EAAEC,MARV;AASE,IAAA,MAAM,EAAEC;AATV,IAJF,CANF,CADF;AAyBD,CAnEM;;;;AAqEP,MAAMoB,MAAM,GAAGU,wBAAWC,MAAX,CAAkB;AAC/BV,EAAAA,SAAS,EAAE,EADoB;AAE/BK,EAAAA,SAAS,EAAE;AACTC,IAAAA,IAAI,EAAE,CADG;AAETC,IAAAA,KAAK,EAAE;AAFE;AAFoB,CAAlB,CAAf;;AAQA,MAAMzB,oBAAN,CAAkD;AAAA;AAAA,qCAC9B,EAD8B;;AAAA;;AAAA;;AAAA,oCAIvC,CAJuC;;AAAA,qCAKtC,CALsC;;AAAA,wCAMnC,KANmC;;AAAA,qCAO7B,IAP6B;;AAAA;;AAAA,8CAS7B,KAT6B;;AAAA;;AAAA;;AAAA,mCAYxC,MAAM,KAAK6B,MAZ6B;;AAAA,oCAavC,MAAM,KAAKC,OAb4B;AAAA;;AAehDC,EAAAA,OAAO,GAAS;AACd,SAAKC,UAAL,GAAkB,IAAlB;AACD;;AACDC,EAAAA,aAAa,GAAS;AACpB,SAAKD,UAAL,GAAkB,KAAlB;AACD;;AAEDZ,EAAAA,QAAQ,CAACD,KAAD,EAA2B;AACjC,QAAI;AAAEM,MAAAA,KAAF;AAASS,MAAAA;AAAT,QAAoBf,KAAK,CAACgB,WAAN,CAAkBC,MAA1C;AACA,SAAKP,MAAL,GAAcJ,KAAd;AACA,SAAKK,OAAL,GAAeI,MAAf;;AAEA,QAAI,KAAKF,UAAT,EAAqB;AAAA;;AACnB,iCAAKK,YAAL;AACD;AACF;;AACDf,EAAAA,YAAY,CAACD,SAAD,EAAqB;AAC/B,QAAI,KAAKiB,OAAL,KAAiBjB,SAArB,EAAgC;AAC9B,WAAKiB,OAAL,GAAejB,SAAf;AACA,WAAKkB,mBAAL,GAA2BC,IAAI,CAACC,GAAL,EAA3B;;AACA,UAAI,KAAKT,UAAT,EAAqB;AAAA;;AACnB,sCAAKU,uBAAL;AACD;AACF;AACF;;AAvC+C","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 '@livekit/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 // eslint-disable-next-line react-native/no-inline-styles\n style={{\n flex: 1,\n width: '100%',\n }}\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"]}
1
+ {"version":3,"sources":["VideoView.tsx"],"names":["VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","layoutOnChange","event","onLayout","visibilityOnChange","isVisible","onVisibility","shouldObserveVisibility","RemoteVideoTrack","isAdaptiveStream","mediaStream","setMediaStream","LocalVideoTrack","onRestarted","track","on","TrackEvent","Restarted","off","observeElementInfo","stopObservingElementInfo","styles","container","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 '@livekit/react-native-webrtc';\nimport { useCallback, useEffect, useMemo, 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 layoutOnChange = useCallback(\n (event: LayoutChangeEvent) => elementInfo.onLayout(event),\n [elementInfo]\n );\n const visibilityOnChange = useCallback(\n (isVisible: boolean) => elementInfo.onVisibility(isVisible),\n [elementInfo]\n );\n const shouldObserveVisibility = useMemo(() => {\n return (\n videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream\n );\n }, [videoTrack]);\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 style={{ ...style, ...styles.container }} onLayout={layoutOnChange}>\n <ViewPortDetector\n onChange={visibilityOnChange}\n style={styles.videoView}\n disabled={!shouldObserveVisibility}\n propKey={videoTrack}\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"]}