@livekit/react-native 1.2.0 → 1.4.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.
Files changed (37) hide show
  1. package/README.md +53 -10
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/com/livekit/reactnative/LiveKitReactNative.kt +21 -2
  4. package/android/src/main/java/com/livekit/reactnative/LivekitReactNativeModule.kt +63 -14
  5. package/android/src/main/java/com/livekit/reactnative/audio/AudioManagerUtils.kt +72 -0
  6. package/android/src/main/java/com/livekit/reactnative/audio/AudioSwitchManager.java +108 -6
  7. package/android/src/main/java/com/livekit/reactnative/audio/AudioType.kt +46 -0
  8. package/android/src/main/java/com/livekit/reactnative/video/SimulcastVideoEncoderFactoryWrapper.kt +2 -1
  9. package/ios/AudioUtils.h +9 -0
  10. package/ios/AudioUtils.m +48 -0
  11. package/ios/LivekitReactNative.m +45 -0
  12. package/ios/LivekitReactNative.xcodeproj/project.xcworkspace/xcuserdata/davidliu.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  13. package/lib/commonjs/audio/AudioManager.js +108 -0
  14. package/lib/commonjs/audio/AudioManager.js.map +1 -0
  15. package/lib/commonjs/audio/AudioSession.js +59 -10
  16. package/lib/commonjs/audio/AudioSession.js.map +1 -1
  17. package/lib/commonjs/index.js +105 -3
  18. package/lib/commonjs/index.js.map +1 -1
  19. package/lib/commonjs/logger.js +32 -0
  20. package/lib/commonjs/logger.js.map +1 -0
  21. package/lib/module/audio/AudioManager.js +92 -0
  22. package/lib/module/audio/AudioManager.js.map +1 -0
  23. package/lib/module/audio/AudioSession.js +54 -9
  24. package/lib/module/audio/AudioSession.js.map +1 -1
  25. package/lib/module/index.js +19 -2
  26. package/lib/module/index.js.map +1 -1
  27. package/lib/module/logger.js +18 -0
  28. package/lib/module/logger.js.map +1 -0
  29. package/lib/typescript/audio/AudioManager.d.ts +11 -0
  30. package/lib/typescript/audio/AudioSession.d.ts +85 -14
  31. package/lib/typescript/index.d.ts +6 -2
  32. package/lib/typescript/logger.d.ts +13 -0
  33. package/package.json +9 -5
  34. package/src/audio/AudioManager.ts +119 -0
  35. package/src/audio/AudioSession.ts +206 -23
  36. package/src/index.tsx +41 -2
  37. package/src/logger.ts +23 -0
package/README.md CHANGED
@@ -39,13 +39,16 @@ In your [MainApplication.java](https://github.com/livekit/client-sdk-react-nativ
39
39
 
40
40
  ```
41
41
  import com.livekit.reactnative.LiveKitReactNative;
42
+ import com.livekit.reactnative.audio.AudioType;
42
43
 
43
44
  public class MainApplication extends Application implements ReactApplication {
44
45
 
45
46
  @Override
46
47
  public void onCreate() {
47
48
  // Place this above any other RN related initialization
48
- LiveKitReactNative.setup();
49
+ // When AudioType is omitted, it'll default to CommunicationAudioType
50
+ // use MediaAudioType if user is only consuming audio, and not publishing
51
+ LiveKitReactNative.setup(this, new AudioType.CommunicationAudioType());
49
52
 
50
53
  //...
51
54
  }
@@ -137,20 +140,26 @@ to manage the audio session on native platforms. This class wraps either [AudioM
137
140
 
138
141
  You can customize the configuration of the audio session with `configureAudio`.
139
142
 
143
+ ### Media playback
144
+
145
+ By default, the audio session is set up for bidirectional communication. In this mode, the audio framework exhibits the following behaviors:
146
+
147
+ - The volume cannot be reduced to 0.
148
+ - Echo cancellation is available and is enabled by default.
149
+ - A microphone indicator can be displayed, depending on the platform.
150
+
151
+ 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:
152
+
153
+ Note: iOS audio session customization is in development, and will be documented here when released.
154
+
140
155
  ```js
141
156
  useEffect(() => {
142
157
  let connect = async () => {
143
158
  // configure audio session prior to starting it.
144
159
  await AudioSession.configureAudio({
145
160
  android: {
146
- preferredOutputList: ['earpiece'],
147
- // See [AudioManager](https://developer.android.com/reference/android/media/AudioManager)
148
- // for details on audio and focus modes.
149
- audioMode: 'normal',
150
- audioFocusMode: 'gain',
151
- },
152
- ios: {
153
- defaultOutput: 'earpiece',
161
+ // currently supports .media and .communication presets
162
+ audioTypeOptions: AndroidAudioTypePresets.media,
154
163
  },
155
164
  });
156
165
  await AudioSession.startAudioSession();
@@ -164,6 +173,29 @@ useEffect(() => {
164
173
  }, [url, token, room]);
165
174
  ```
166
175
 
176
+ ### Customizing audio session
177
+
178
+ Instead of using our presets, you can further customize the audio session to suit your specific needs.
179
+
180
+ ```js
181
+ await AudioSession.configureAudio({
182
+ android: {
183
+ preferredOutputList: ['earpiece'],
184
+ // See [AudioManager](https://developer.android.com/reference/android/media/AudioManager)
185
+ // for details on audio and focus modes.
186
+ audioTypeOptions: {
187
+ manageAudioFocus: true,
188
+ audioMode: 'normal',
189
+ audioFocusMode: 'gain',
190
+ audioStreamType: 'music',
191
+ audioAttributesUsageType: 'media',
192
+ audioAttributesContentType: 'unknown',
193
+ },
194
+ },
195
+ });
196
+ await AudioSession.startAudioSession();
197
+ ```
198
+
167
199
  ## Screenshare
168
200
 
169
201
  Enabling screenshare requires extra installation steps:
@@ -223,7 +255,18 @@ return (
223
255
 
224
256
  ### Note
225
257
 
226
- Currently it does not run on iOS Simulator on M1 Macs.
258
+ You will not be able to publish camera or microphone tracks on iOS Simulator.
259
+
260
+ ## Troubleshooting
261
+
262
+ #### Cannot read properties of undefined (reading 'split')
263
+
264
+ This error could happen if you are using yarn and have incompatible versions of dependencies with livekit-client.
265
+
266
+ To fix this, you can either:
267
+
268
+ - use another package manager, like npm
269
+ - use [yarn-deduplicate](https://www.npmjs.com/package/yarn-deduplicate) to deduplicate dependencies
227
270
 
228
271
  ## Contributing
229
272
 
@@ -129,8 +129,8 @@ dependencies {
129
129
  // noinspection GradleDynamicVersion
130
130
  api 'com.facebook.react:react-native:+'
131
131
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
132
- api 'com.github.davidliu:audioswitch:fb33b237aa50b3d1d2dea0e0695ecb7b38a98971'
133
- api 'io.github.webrtc-sdk:android:104.5112.10'
132
+ api 'com.github.davidliu:audioswitch:89582c47c9a04c62f90aa5e57251af4800a62c9a'
133
+ api 'io.github.webrtc-sdk:android:114.5735.05'
134
134
  implementation project(':livekit_react-native-webrtc')
135
135
  implementation "androidx.annotation:annotation:1.4.0"
136
136
  }
@@ -1,16 +1,35 @@
1
1
  package com.livekit.reactnative
2
2
 
3
+ import android.app.Application
4
+ import android.content.Context
5
+ import android.os.Build
6
+ import com.livekit.reactnative.audio.AudioType
3
7
  import com.livekit.reactnative.video.SimulcastVideoEncoderFactoryWrapper
4
8
  import com.livekit.reactnative.video.WrappedVideoDecoderFactoryProxy
5
9
  import com.oney.WebRTCModule.WebRTCModuleOptions
6
-
10
+ import org.webrtc.audio.JavaAudioDeviceModule
7
11
 
8
12
  object LiveKitReactNative {
9
13
 
14
+ /**
15
+ * Initializes components required for LiveKit to work on Android.
16
+ *
17
+ * Must be called from your [Application.onCreate] method before any other react-native
18
+ * initialization.
19
+ */
10
20
  @JvmStatic
11
- fun setup() {
21
+ @JvmOverloads
22
+ fun setup(context: Context, audioType: AudioType = AudioType.CommunicationAudioType()) {
12
23
  val options = WebRTCModuleOptions.getInstance()
13
24
  options.videoEncoderFactory = SimulcastVideoEncoderFactoryWrapper(null, true, true)
14
25
  options.videoDecoderFactory = WrappedVideoDecoderFactoryProxy()
26
+
27
+ val useHardwareAudioProcessing = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
28
+
29
+ options.audioDeviceModule = JavaAudioDeviceModule.builder(context)
30
+ .setUseHardwareAcousticEchoCanceler(useHardwareAudioProcessing)
31
+ .setUseHardwareNoiseSuppressor(useHardwareAudioProcessing)
32
+ .setAudioAttributes(audioType.audioAttributes)
33
+ .createAudioDeviceModule()
15
34
  }
16
35
  }
@@ -1,5 +1,7 @@
1
1
  package com.livekit.reactnative
2
2
 
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
3
5
  import com.facebook.react.bridge.*
4
6
  import com.livekit.reactnative.audio.AudioDeviceKind
5
7
  import com.livekit.reactnative.audio.AudioManagerUtils
@@ -17,24 +19,71 @@ class LivekitReactNativeModule(reactContext: ReactApplicationContext) : ReactCon
17
19
  fun configureAudio(config: ReadableMap) {
18
20
  val androidConfig = config.getMap("android") ?: return
19
21
 
20
- androidConfig.getArray("preferredOutputList")?.let { preferredOutputList ->
21
- val preferredDeviceList = preferredOutputList.toArrayList().mapNotNull { output ->
22
- val outputStr = output as? String
23
- AudioDeviceKind.fromTypeName(outputStr)?.audioDeviceClass
22
+ if (androidConfig.hasKey("preferredOutputList")) {
23
+ androidConfig.getArray("preferredOutputList")?.let { preferredOutputList ->
24
+ val preferredDeviceList = preferredOutputList.toArrayList().mapNotNull { output ->
25
+ val outputStr = output as? String
26
+ AudioDeviceKind.fromTypeName(outputStr)?.audioDeviceClass
27
+ }
28
+ audioManager.preferredDeviceList = preferredDeviceList
24
29
  }
25
- audioManager.preferredDeviceList = preferredDeviceList
26
30
  }
27
31
 
28
- androidConfig.getString("audioMode")?.let { audioModeString ->
29
- val audioMode = AudioManagerUtils.audioModeFromString(audioModeString)
30
- if (audioMode != null) {
31
- audioManager.setAudioMode(audioMode)
32
+ if (androidConfig.hasKey("audioTypeOptions")) {
33
+ val audioTypeOptions = androidConfig.getMap("audioTypeOptions") ?: return
34
+
35
+ if (audioTypeOptions.hasKey("manageAudioFocus")) {
36
+ val manageFocus = audioTypeOptions.getBoolean("manageAudioFocus")
37
+ audioManager.setManageAudioFocus(manageFocus)
32
38
  }
33
- }
34
- androidConfig.getString("audioFocusMode")?.let { focusModeString ->
35
- val focusMode = AudioManagerUtils.focusModeFromString(focusModeString)
36
- if (focusMode != null) {
37
- audioManager.setFocusMode(focusMode)
39
+ if (audioTypeOptions.hasKey("audioMode")) {
40
+ audioTypeOptions.getString("audioMode")?.let { audioModeString ->
41
+ val audioMode = AudioManagerUtils.audioModeFromString(audioModeString)
42
+ if (audioMode != null) {
43
+ audioManager.setAudioMode(audioMode)
44
+ }
45
+ }
46
+ }
47
+
48
+ if (audioTypeOptions.hasKey("audioFocusMode")) {
49
+ audioTypeOptions.getString("audioFocusMode")?.let { focusModeString ->
50
+ val focusMode = AudioManagerUtils.focusModeFromString(focusModeString)
51
+ if (focusMode != null) {
52
+ audioManager.setFocusMode(focusMode)
53
+ }
54
+ }
55
+ }
56
+
57
+ if (audioTypeOptions.hasKey("audioStreamType")) {
58
+ audioTypeOptions.getString("audioStreamType")?.let { streamTypeString ->
59
+ val streamType = AudioManagerUtils.audioStreamTypeFromString(streamTypeString)
60
+ if (streamType != null) {
61
+ audioManager.setAudioStreamType(streamType)
62
+ }
63
+ }
64
+ }
65
+
66
+ if (audioTypeOptions.hasKey("audioAttributesUsageType")) {
67
+ audioTypeOptions.getString("audioAttributesUsageType")?.let { usageTypeString ->
68
+ val usageType = AudioManagerUtils.audioAttributesUsageTypeFromString(usageTypeString)
69
+ if (usageType != null) {
70
+ audioManager.setAudioAttributesUsageType(usageType)
71
+ }
72
+ }
73
+ }
74
+
75
+ if (audioTypeOptions.hasKey("audioAttributesContentType")) {
76
+ audioTypeOptions.getString("audioAttributesContentType")?.let { contentTypeString ->
77
+ val contentType = AudioManagerUtils.audioAttributesContentTypeFromString(contentTypeString)
78
+ if (contentType != null) {
79
+ audioManager.setAudioAttributesContentType(contentType)
80
+ }
81
+ }
82
+ }
83
+
84
+ if (audioTypeOptions.hasKey("forceHandleAudioRouting")) {
85
+ val force = audioTypeOptions.getBoolean("forceHandleAudioRouting")
86
+ audioManager.setForceHandleAudioRouting(force)
38
87
  }
39
88
  }
40
89
  }
@@ -1,5 +1,6 @@
1
1
  package com.livekit.reactnative.audio
2
2
 
3
+ import android.media.AudioAttributes
3
4
  import android.media.AudioManager
4
5
  import android.util.Log
5
6
 
@@ -41,4 +42,75 @@ object AudioManagerUtils {
41
42
 
42
43
  return focusMode
43
44
  }
45
+
46
+ fun audioAttributesUsageTypeFromString(usageTypeString: String?): Int? {
47
+ if (usageTypeString == null) {
48
+ return null
49
+ }
50
+
51
+ val usageType: Int? = when (usageTypeString) {
52
+ "alarm" -> AudioAttributes.USAGE_ALARM
53
+ "assistanceAccessibility" -> AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
54
+ "assistanceNavigationGuidance" -> AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
55
+ "assistanceSonification" -> AudioAttributes.USAGE_ASSISTANCE_SONIFICATION
56
+ "assistant" -> AudioAttributes.USAGE_ASSISTANT
57
+ "game" -> AudioAttributes.USAGE_GAME
58
+ "media" -> AudioAttributes.USAGE_MEDIA
59
+ "notification" -> AudioAttributes.USAGE_NOTIFICATION
60
+ "notificationEvent" -> AudioAttributes.USAGE_NOTIFICATION_EVENT
61
+ "notificationRingtone" -> AudioAttributes.USAGE_NOTIFICATION_RINGTONE
62
+ "unknown" -> AudioAttributes.USAGE_UNKNOWN
63
+ "voiceCommunication" -> AudioAttributes.USAGE_VOICE_COMMUNICATION
64
+ "voiceCommunicationSignalling" -> AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING
65
+ else -> {
66
+ Log.w(TAG, "Unknown audio attributes usage type: $usageTypeString")
67
+ null
68
+ }
69
+ }
70
+
71
+ return usageType
72
+ }
73
+
74
+ fun audioAttributesContentTypeFromString(contentTypeString: String?): Int? {
75
+ if (contentTypeString == null) {
76
+ return null
77
+ }
78
+
79
+ val contentType = when (contentTypeString) {
80
+ "movie" -> AudioAttributes.CONTENT_TYPE_MOVIE
81
+ "music" -> AudioAttributes.CONTENT_TYPE_MUSIC
82
+ "sonification" -> AudioAttributes.CONTENT_TYPE_SONIFICATION
83
+ "speech" -> AudioAttributes.CONTENT_TYPE_SPEECH
84
+ "unknown" -> AudioAttributes.CONTENT_TYPE_UNKNOWN
85
+ else -> {
86
+ Log.w(TAG, "Unknown audio attributes content type: $contentTypeString")
87
+ null
88
+ }
89
+ }
90
+
91
+ return contentType
92
+ }
93
+
94
+ fun audioStreamTypeFromString(streamTypeString: String?): Int? {
95
+ if (streamTypeString == null) {
96
+ return null
97
+ }
98
+
99
+ val streamType = when (streamTypeString) {
100
+ "accessibility" -> AudioManager.STREAM_ACCESSIBILITY
101
+ "alarm" -> AudioManager.STREAM_ALARM
102
+ "dtmf" -> AudioManager.STREAM_DTMF
103
+ "music" -> AudioManager.STREAM_MUSIC
104
+ "notification" -> AudioManager.STREAM_NOTIFICATION
105
+ "ring" -> AudioManager.STREAM_RING
106
+ "system" -> AudioManager.STREAM_SYSTEM
107
+ "voiceCall" -> AudioManager.STREAM_VOICE_CALL
108
+ else -> {
109
+ Log.w(TAG, "Unknown audio stream type: $streamTypeString")
110
+ null
111
+ }
112
+ }
113
+
114
+ return streamType
115
+ }
44
116
  }
@@ -1,6 +1,7 @@
1
1
  package com.livekit.reactnative.audio;
2
2
 
3
3
  import android.content.Context;
4
+ import android.media.AudioAttributes;
4
5
  import android.media.AudioManager;
5
6
  import android.os.Handler;
6
7
  import android.os.Looper;
@@ -14,6 +15,7 @@ import com.twilio.audioswitch.AudioSwitch;
14
15
  import java.util.ArrayList;
15
16
  import java.util.Collections;
16
17
  import java.util.List;
18
+ import java.util.Objects;
17
19
 
18
20
  import kotlin.Unit;
19
21
  import kotlin.jvm.functions.Function2;
@@ -32,7 +34,8 @@ public class AudioSwitchManager {
32
34
  Unit> audioDeviceChangeListener = (devices, currentDevice) -> null;
33
35
 
34
36
  @NonNull
35
- public AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = (i -> {});
37
+ public AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = (i -> {
38
+ });
36
39
 
37
40
  @NonNull
38
41
  public List<Class<? extends AudioDevice>> preferredDeviceList;
@@ -43,6 +46,13 @@ public class AudioSwitchManager {
43
46
  @Nullable
44
47
  private AudioSwitch audioSwitch;
45
48
 
49
+ /**
50
+ * When true, AudioSwitchHandler will request audio focus on start and abandon on stop.
51
+ *
52
+ * Defaults to true.
53
+ */
54
+ private boolean manageAudioFocus = true;
55
+
46
56
  /**
47
57
  * The audio focus mode to use while started.
48
58
  *
@@ -53,9 +63,55 @@ public class AudioSwitchManager {
53
63
  /**
54
64
  * The audio mode to use while started.
55
65
  *
56
- * Defaults to [AudioManager.MODE_NORMAL].
66
+ * Defaults to AudioManager.MODE_IN_COMMUNICATION.
67
+ */
68
+ private int audioMode = AudioManager.MODE_IN_COMMUNICATION;
69
+
70
+ /**
71
+ * The audio stream type to use when requesting audio focus on pre-O devices.
72
+ *
73
+ * Defaults to [AudioManager.STREAM_VOICE_CALL].
74
+ *
75
+ * Refer to this [compatibility table](https://source.android.com/docs/core/audio/attributes#compatibility)
76
+ * to ensure that your values match between android versions.
77
+ *
78
+ * Note: Manual audio routing may not work appropriately when using non-default values.
57
79
  */
58
- private int audioMode = AudioManager.MODE_NORMAL;
80
+ private int audioStreamType = AudioManager.STREAM_VOICE_CALL;
81
+
82
+ /**
83
+ * The audio attribute usage type to use when requesting audio focus on devices O and beyond.
84
+ *
85
+ * Defaults to [AudioAttributes.USAGE_VOICE_COMMUNICATION].
86
+ *
87
+ * Refer to this [compatibility table](https://source.android.com/docs/core/audio/attributes#compatibility)
88
+ * to ensure that your values match between android versions.
89
+ *
90
+ * Note: Manual audio routing may not work appropriately when using non-default values.
91
+ */
92
+ private int audioAttributeUsageType = AudioAttributes.USAGE_VOICE_COMMUNICATION;
93
+
94
+ /**
95
+ * The audio attribute content type to use when requesting audio focus on devices O and beyond.
96
+ *
97
+ * Defaults to [AudioAttributes.CONTENT_TYPE_SPEECH].
98
+ *
99
+ * Refer to this [compatibility table](https://source.android.com/docs/core/audio/attributes#compatibility)
100
+ * to ensure that your values match between android versions.
101
+ *
102
+ * Note: Manual audio routing may not work appropriately when using non-default values.
103
+ */
104
+ private int audioAttributeContentType = AudioAttributes.CONTENT_TYPE_SPEECH;
105
+
106
+ /**
107
+ * On certain Android devices, audio routing does not function properly and bluetooth microphones will not work
108
+ * unless audio mode is set to [AudioManager.MODE_IN_COMMUNICATION] or [AudioManager.MODE_IN_CALL].
109
+ *
110
+ * AudioSwitchManager by default will not handle audio routing in those cases to avoid audio issues.
111
+ *
112
+ * If this set to true, AudioSwitchManager will attempt to do audio routing, though behavior is undefined.
113
+ */
114
+ private boolean forceHandleAudioRouting = false;
59
115
 
60
116
  public AudioSwitchManager(@NonNull Context context) {
61
117
  this.context = context;
@@ -78,8 +134,13 @@ public class AudioSwitchManager {
78
134
  audioFocusChangeListener,
79
135
  preferredDeviceList
80
136
  );
137
+ audioSwitch.setManageAudioFocus(manageAudioFocus);
81
138
  audioSwitch.setFocusMode(focusMode);
82
139
  audioSwitch.setAudioMode(audioMode);
140
+ audioSwitch.setAudioStreamType(audioStreamType);
141
+ audioSwitch.setAudioAttributeContentType(audioAttributeContentType);
142
+ audioSwitch.setAudioAttributeUsageType(audioAttributeUsageType);
143
+ audioSwitch.setForceHandleAudioRouting(forceHandleAudioRouting);
83
144
  audioSwitch.start(audioDeviceChangeListener);
84
145
  audioSwitch.activate();
85
146
  });
@@ -96,7 +157,7 @@ public class AudioSwitchManager {
96
157
  });
97
158
  }
98
159
 
99
- public void setMicrophoneMute(boolean mute){
160
+ public void setMicrophoneMute(boolean mute) {
100
161
  audioManager.setMicrophoneMute(mute);
101
162
  }
102
163
 
@@ -141,24 +202,65 @@ public class AudioSwitchManager {
141
202
  }
142
203
 
143
204
  public void enableSpeakerphone(boolean enable) {
144
- if(enable) {
205
+ if (enable) {
145
206
  audioManager.setSpeakerphoneOn(true);
146
207
  } else {
147
208
  audioManager.setSpeakerphoneOn(false);
148
209
  }
149
210
  }
150
-
211
+
151
212
  public void selectAudioOutput(@Nullable AudioDeviceKind kind) {
152
213
  if (kind != null) {
153
214
  selectAudioOutput(kind.audioDeviceClass);
154
215
  }
155
216
  }
156
217
 
218
+ public void setManageAudioFocus(boolean manage) {
219
+ this.manageAudioFocus = manage;
220
+ if (audioSwitch != null) {
221
+ Objects.requireNonNull(audioSwitch).setManageAudioFocus(this.manageAudioFocus);
222
+ }
223
+ }
224
+
157
225
  public void setFocusMode(int focusMode) {
158
226
  this.focusMode = focusMode;
227
+ if (audioSwitch != null) {
228
+ Objects.requireNonNull(audioSwitch).setFocusMode(this.focusMode);
229
+ }
159
230
  }
160
231
 
161
232
  public void setAudioMode(int audioMode) {
162
233
  this.audioMode = audioMode;
234
+ if (audioSwitch != null) {
235
+ Objects.requireNonNull(audioSwitch).setAudioMode(this.audioMode);
236
+ }
237
+ }
238
+
239
+ public void setAudioStreamType(int streamType) {
240
+ this.audioStreamType = streamType;
241
+ if (audioSwitch != null) {
242
+ Objects.requireNonNull(audioSwitch).setAudioStreamType(this.audioStreamType);
243
+ }
244
+ }
245
+
246
+ public void setAudioAttributesUsageType(int usageType) {
247
+ this.audioAttributeUsageType = usageType;
248
+ if (audioSwitch != null) {
249
+ Objects.requireNonNull(audioSwitch).setAudioAttributeUsageType(this.audioAttributeUsageType);
250
+ }
251
+ }
252
+
253
+ public void setAudioAttributesContentType(int contentType) {
254
+ this.audioAttributeContentType = contentType;
255
+ if (audioSwitch != null) {
256
+ Objects.requireNonNull(audioSwitch).setAudioAttributeContentType(this.audioAttributeContentType);
257
+ }
258
+ }
259
+
260
+ public void setForceHandleAudioRouting(boolean force) {
261
+ this.forceHandleAudioRouting = force;
262
+ if (audioSwitch != null) {
263
+ Objects.requireNonNull(audioSwitch).setForceHandleAudioRouting(this.forceHandleAudioRouting);
264
+ }
163
265
  }
164
266
  }
@@ -0,0 +1,46 @@
1
+ package com.livekit.reactnative.audio
2
+
3
+ import android.media.AudioAttributes
4
+ import android.media.AudioManager
5
+
6
+ sealed class AudioType(
7
+ val audioMode: Int,
8
+ val audioAttributes: AudioAttributes,
9
+ val audioStreamType: Int
10
+ ) {
11
+ /**
12
+ * An audio type for general media playback usage (i.e. listener-only use cases).
13
+ *
14
+ * Audio routing is handled automatically by the system in normal media mode,
15
+ * and bluetooth microphones may not work on some devices.
16
+ */
17
+ class MediaAudioType : AudioType(
18
+ AudioManager.MODE_NORMAL,
19
+ AudioAttributes.Builder()
20
+ .setUsage(AudioAttributes.USAGE_MEDIA)
21
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
22
+ .build(),
23
+ AudioManager.STREAM_MUSIC
24
+ )
25
+
26
+ /**
27
+ * An audio type for communications (i.e. participating a call or otherwise
28
+ * publishing local microphone).
29
+ *
30
+ * Audio routing can be manually controlled.
31
+ */
32
+ class CommunicationAudioType : AudioType(
33
+ AudioManager.MODE_IN_COMMUNICATION,
34
+ AudioAttributes.Builder()
35
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
36
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
37
+ .build(),
38
+ AudioManager.STREAM_VOICE_CALL
39
+ )
40
+
41
+ /**
42
+ * An audio type that takes in a user-defined [AudioAttributes] and audio stream type.
43
+ */
44
+ class CustomAudioType(audioMode: Int, audioAttributes: AudioAttributes, audioStreamType: Int) :
45
+ AudioType(audioMode, audioAttributes, audioStreamType)
46
+ }
@@ -1,6 +1,7 @@
1
1
  package com.livekit.reactnative.video
2
2
 
3
3
  import android.util.Log
4
+ import com.oney.WebRTCModule.webrtcutils.SoftwareVideoEncoderFactoryProxy
4
5
  import org.webrtc.EglBase
5
6
  import org.webrtc.HardwareVideoEncoderFactory
6
7
  import org.webrtc.SimulcastVideoEncoderFactory
@@ -64,7 +65,7 @@ open class SimulcastVideoEncoderFactoryWrapper(
64
65
  private class FallbackFactory(private val hardwareVideoEncoderFactory: VideoEncoderFactory) :
65
66
  VideoEncoderFactory {
66
67
 
67
- private val softwareVideoEncoderFactory: VideoEncoderFactory = SoftwareVideoEncoderFactory()
68
+ private val softwareVideoEncoderFactory: VideoEncoderFactory = SoftwareVideoEncoderFactoryProxy()
68
69
 
69
70
  override fun createEncoder(info: VideoCodecInfo): VideoEncoder? {
70
71
  val softwareEncoder = softwareVideoEncoderFactory.createEncoder(info)
@@ -0,0 +1,9 @@
1
+ #if TARGET_OS_IPHONE
2
+ #import <AVFoundation/AVFoundation.h>
3
+
4
+ @interface AudioUtils : NSObject
5
+ + (AVAudioSessionMode)audioSessionModeFromString:(NSString*)mode;
6
+ + (AVAudioSessionCategory)audioSessionCategoryFromString:(NSString *)category;
7
+ @end
8
+
9
+ #endif
@@ -0,0 +1,48 @@
1
+ #if TARGET_OS_IPHONE
2
+ #import "AudioUtils.h"
3
+ #import <AVFoundation/AVFoundation.h>
4
+
5
+ @implementation AudioUtils
6
+
7
+ + (AVAudioSessionMode)audioSessionModeFromString:(NSString*)mode {
8
+ if([@"default_" isEqualToString:mode]) {
9
+ return AVAudioSessionModeDefault;
10
+ } else if([@"voicePrompt" isEqualToString:mode]) {
11
+ return AVAudioSessionModeVoicePrompt;
12
+ } else if([@"videoRecording" isEqualToString:mode]) {
13
+ return AVAudioSessionModeVideoRecording;
14
+ } else if([@"videoChat" isEqualToString:mode]) {
15
+ return AVAudioSessionModeVideoChat;
16
+ } else if([@"voiceChat" isEqualToString:mode]) {
17
+ return AVAudioSessionModeVoiceChat;
18
+ } else if([@"gameChat" isEqualToString:mode]) {
19
+ return AVAudioSessionModeGameChat;
20
+ } else if([@"measurement" isEqualToString:mode]) {
21
+ return AVAudioSessionModeMeasurement;
22
+ } else if([@"moviePlayback" isEqualToString:mode]) {
23
+ return AVAudioSessionModeMoviePlayback;
24
+ } else if([@"spokenAudio" isEqualToString:mode]) {
25
+ return AVAudioSessionModeSpokenAudio;
26
+ }
27
+ return AVAudioSessionModeDefault;
28
+ }
29
+
30
+ + (AVAudioSessionCategory)audioSessionCategoryFromString:(NSString *)category {
31
+ if([@"ambient" isEqualToString:category]) {
32
+ return AVAudioSessionCategoryAmbient;
33
+ } else if([@"soloAmbient" isEqualToString:category]) {
34
+ return AVAudioSessionCategorySoloAmbient;
35
+ } else if([@"playback" isEqualToString:category]) {
36
+ return AVAudioSessionCategoryPlayback;
37
+ } else if([@"record" isEqualToString:category]) {
38
+ return AVAudioSessionCategoryRecord;
39
+ } else if([@"playAndRecord" isEqualToString:category]) {
40
+ return AVAudioSessionCategoryPlayAndRecord;
41
+ } else if([@"multiRoute" isEqualToString:category]) {
42
+ return AVAudioSessionCategoryMultiRoute;
43
+ }
44
+ return AVAudioSessionCategoryAmbient;
45
+ }
46
+
47
+ @end
48
+ #endif