@fishjam-cloud/react-native-client 0.1.0 → 0.2.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 (110) hide show
  1. package/README.md +403 -0
  2. package/android/build.gradle +6 -6
  3. package/app.plugin.js +1 -1
  4. package/build/RNFishjamClientModule.d.ts +7 -5
  5. package/build/RNFishjamClientModule.d.ts.map +1 -1
  6. package/build/RNFishjamClientModule.js.map +1 -1
  7. package/build/common/client.d.ts +8 -1
  8. package/build/common/client.d.ts.map +1 -1
  9. package/build/common/client.js +9 -2
  10. package/build/common/client.js.map +1 -1
  11. package/build/components/VideoPreviewView.d.ts +3 -3
  12. package/build/components/VideoPreviewView.d.ts.map +1 -1
  13. package/build/components/VideoPreviewView.js +1 -1
  14. package/build/components/VideoPreviewView.js.map +1 -1
  15. package/build/hooks/useAudioSettings.d.ts.map +1 -1
  16. package/build/hooks/useAudioSettings.js +4 -5
  17. package/build/hooks/useAudioSettings.js.map +1 -1
  18. package/build/hooks/useBandwidthEstimation.d.ts +0 -3
  19. package/build/hooks/useBandwidthEstimation.d.ts.map +1 -1
  20. package/build/hooks/useBandwidthEstimation.js +3 -6
  21. package/build/hooks/useBandwidthEstimation.js.map +1 -1
  22. package/build/hooks/useCamera.d.ts +12 -21
  23. package/build/hooks/useCamera.d.ts.map +1 -1
  24. package/build/hooks/useCamera.js +52 -52
  25. package/build/hooks/useCamera.js.map +1 -1
  26. package/build/{common/eventEmitter.d.ts → hooks/useFishjamEvent.d.ts} +2 -3
  27. package/build/hooks/useFishjamEvent.d.ts.map +1 -0
  28. package/build/{common/eventEmitter.js → hooks/useFishjamEvent.js} +11 -2
  29. package/build/hooks/useFishjamEvent.js.map +1 -0
  30. package/build/hooks/useMicrophone.d.ts.map +1 -1
  31. package/build/hooks/useMicrophone.js +3 -7
  32. package/build/hooks/useMicrophone.js.map +1 -1
  33. package/build/hooks/useParticipants.d.ts +0 -3
  34. package/build/hooks/useParticipants.d.ts.map +1 -1
  35. package/build/hooks/useParticipants.js +6 -6
  36. package/build/hooks/useParticipants.js.map +1 -1
  37. package/build/hooks/useReconnection.d.ts.map +1 -1
  38. package/build/hooks/useReconnection.js +8 -18
  39. package/build/hooks/useReconnection.js.map +1 -1
  40. package/build/hooks/useScreencast.d.ts.map +1 -1
  41. package/build/hooks/useScreencast.js +3 -7
  42. package/build/hooks/useScreencast.js.map +1 -1
  43. package/build/index.d.ts +4 -3
  44. package/build/index.d.ts.map +1 -1
  45. package/build/index.js +3 -1
  46. package/build/index.js.map +1 -1
  47. package/build/types.d.ts +25 -0
  48. package/build/types.d.ts.map +1 -1
  49. package/build/types.js +10 -1
  50. package/build/types.js.map +1 -1
  51. package/build/utils/foregroundService.d.ts +30 -0
  52. package/build/utils/foregroundService.d.ts.map +1 -0
  53. package/build/utils/foregroundService.js +41 -0
  54. package/build/utils/foregroundService.js.map +1 -0
  55. package/ios/RNFishjamClient.swift +37 -30
  56. package/ios/RNFishjamClientModule.swift +11 -7
  57. package/package.json +19 -19
  58. package/.eslintignore +0 -7
  59. package/.eslintrc +0 -6
  60. package/.prettierrc +0 -9
  61. package/.watchmanconfig +0 -1
  62. package/android/.editorconfig +0 -9
  63. package/android/src/main/AndroidManifest.xml +0 -2
  64. package/android/src/main/java/org/membraneframework/reactnative/AudioDeviceKind.kt +0 -34
  65. package/android/src/main/java/org/membraneframework/reactnative/AudioSwitchManager.kt +0 -61
  66. package/android/src/main/java/org/membraneframework/reactnative/EmitableEvents.kt +0 -14
  67. package/android/src/main/java/org/membraneframework/reactnative/Errors.kt +0 -31
  68. package/android/src/main/java/org/membraneframework/reactnative/RNFishjamClient.kt +0 -866
  69. package/android/src/main/java/org/membraneframework/reactnative/RNFishjamClientModule.kt +0 -294
  70. package/android/src/main/java/org/membraneframework/reactnative/Utils.kt +0 -11
  71. package/android/src/main/java/org/membraneframework/reactnative/VideoPreviewView.kt +0 -40
  72. package/android/src/main/java/org/membraneframework/reactnative/VideoPreviewViewModule.kt +0 -17
  73. package/android/src/main/java/org/membraneframework/reactnative/VideoRendererView.kt +0 -58
  74. package/android/src/main/java/org/membraneframework/reactnative/VideoRendererViewModule.kt +0 -25
  75. package/android/src/main/java/org/membraneframework/reactnative/VideoView.kt +0 -77
  76. package/babel.config.js +0 -2
  77. package/build/common/eventEmitter.d.ts.map +0 -1
  78. package/build/common/eventEmitter.js.map +0 -1
  79. package/expo-module.config.json +0 -17
  80. package/ios/RNFishjamClient.podspec +0 -30
  81. package/ios/swift-format-config.json +0 -56
  82. package/plugin/broadcastExtensionFiles/Info.plist +0 -27
  83. package/plugin/broadcastExtensionFiles/MembraneBroadcastSampleHandler.swift +0 -50
  84. package/plugin/broadcastExtensionFiles/MembraneScreenBroadcastExtension.entitlements +0 -10
  85. package/plugin/src/withMembrane.ts +0 -310
  86. package/plugin/tsconfig.json +0 -9
  87. package/scripts/lint.sh +0 -18
  88. package/scripts/lint_check.sh +0 -14
  89. package/src/RNFishjamClientModule.ts +0 -73
  90. package/src/__tests__/index.test.tsx +0 -199
  91. package/src/common/client.ts +0 -33
  92. package/src/common/eventEmitter.ts +0 -21
  93. package/src/common/metadata.ts +0 -12
  94. package/src/common/webRTC.ts +0 -31
  95. package/src/components/VideoPreviewView.tsx +0 -33
  96. package/src/components/VideoRendererView.tsx +0 -31
  97. package/src/hooks/useAudioSettings.ts +0 -115
  98. package/src/hooks/useBandwidthEstimation.ts +0 -24
  99. package/src/hooks/useCamera.ts +0 -270
  100. package/src/hooks/useMicrophone.ts +0 -38
  101. package/src/hooks/useParticipants.ts +0 -118
  102. package/src/hooks/useReconnection.ts +0 -41
  103. package/src/hooks/useScreencast.ts +0 -146
  104. package/src/index.tsx +0 -63
  105. package/src/protos/fishjam/peer_notifications.ts +0 -299
  106. package/src/stats/types.ts +0 -56
  107. package/src/stats/useRTCStatistics.ts +0 -97
  108. package/src/types.ts +0 -49
  109. package/tsconfig.json +0 -9
  110. package/typedoc.json +0 -5
@@ -1,866 +0,0 @@
1
- package org.membraneframework.reactnative
2
-
3
- import android.app.Activity
4
- import android.content.ActivityNotFoundException
5
- import android.content.Intent
6
- import android.media.projection.MediaProjectionManager
7
- import androidx.appcompat.app.AppCompatActivity
8
- import com.fishjamcloud.client.FishjamClient
9
- import com.fishjamcloud.client.FishjamClientListener
10
- import com.fishjamcloud.client.media.LocalAudioTrack
11
- import com.fishjamcloud.client.media.LocalScreencastTrack
12
- import com.fishjamcloud.client.media.LocalVideoTrack
13
- import com.fishjamcloud.client.media.RemoteAudioTrack
14
- import com.fishjamcloud.client.media.RemoteVideoTrack
15
- import com.fishjamcloud.client.media.Track
16
- import com.fishjamcloud.client.models.AuthError
17
- import com.fishjamcloud.client.models.Endpoint
18
- import com.fishjamcloud.client.models.Metadata
19
- import com.fishjamcloud.client.models.Peer
20
- import com.fishjamcloud.client.models.RTCInboundStats
21
- import com.fishjamcloud.client.models.RTCOutboundStats
22
- import com.fishjamcloud.client.models.ReconnectConfig
23
- import com.fishjamcloud.client.models.SimulcastConfig
24
- import com.fishjamcloud.client.models.TrackBandwidthLimit
25
- import com.fishjamcloud.client.models.VideoParameters
26
- import com.twilio.audioswitch.AudioDevice
27
- import expo.modules.kotlin.AppContext
28
- import expo.modules.kotlin.Promise
29
- import expo.modules.kotlin.exception.CodedException
30
- import kotlinx.coroutines.CoroutineScope
31
- import kotlinx.coroutines.Dispatchers
32
- import kotlinx.coroutines.SupervisorJob
33
- import kotlinx.coroutines.launch
34
- import org.webrtc.Logging
35
-
36
- class RNFishjamClient(
37
- val sendEvent: (name: String, data: Map<String, Any?>) -> Unit
38
- ) : FishjamClientListener {
39
- private val SCREENCAST_REQUEST = 1
40
-
41
- var isMicrophoneOn = false
42
- var isCameraOn = false
43
- var isScreencastOn = false
44
- var isConnected = false
45
-
46
- private var connectPromise: Promise? = null
47
- private var screencastPermissionPromise: Promise? = null
48
-
49
- var videoSimulcastConfig: SimulcastConfig = SimulcastConfig()
50
- private var localUserMetadata: Metadata = mutableMapOf()
51
-
52
- var screencastQuality: String? = null
53
- var screencastSimulcastConfig: SimulcastConfig = SimulcastConfig()
54
- var screencastMaxBandwidth: TrackBandwidthLimit = TrackBandwidthLimit.BandwidthLimit(0)
55
-
56
- var screencastMetadata: Map<String, Any> = mutableMapOf()
57
-
58
- private var mediaProjectionIntent: Intent? = null
59
-
60
- var audioSwitchManager: AudioSwitchManager? = null
61
-
62
- var appContext: AppContext? = null
63
-
64
- interface OnTrackUpdateListener {
65
- fun onTracksUpdate()
66
- }
67
-
68
- interface OnLocalTrackSwitchListener {
69
- suspend fun onLocalTrackWillSwitch()
70
-
71
- suspend fun onLocalTrackSwitched()
72
- }
73
-
74
- companion object {
75
- var onTracksUpdateListeners: MutableList<OnTrackUpdateListener> = mutableListOf()
76
- val onLocalTrackSwitchListener: MutableList<OnLocalTrackSwitchListener> = mutableListOf()
77
- lateinit var fishjamClient: FishjamClient
78
-
79
- fun getAllPeers(): List<Peer> {
80
- val listOfPeers = fishjamClient.getRemotePeers().toMutableList()
81
- val localEndpoint = fishjamClient.getLocalEndpoint()
82
- listOfPeers.add(localEndpoint)
83
- return listOfPeers
84
- }
85
- }
86
-
87
- fun onModuleCreate(appContext: AppContext) {
88
- this.appContext = appContext
89
- this.audioSwitchManager = AudioSwitchManager(appContext.reactContext!!)
90
- create()
91
- }
92
-
93
- fun onModuleDestroy() {
94
- audioSwitchManager?.stop()
95
- }
96
-
97
- private val coroutineScope: CoroutineScope =
98
- CoroutineScope(SupervisorJob() + Dispatchers.Default)
99
-
100
- fun onActivityResult(
101
- requestCode: Int,
102
- resultCode: Int,
103
- data: Intent?
104
- ) {
105
- coroutineScope.launch(Dispatchers.Main) {
106
- if (requestCode != SCREENCAST_REQUEST) return@launch
107
- if (resultCode != Activity.RESULT_OK) {
108
- screencastPermissionPromise?.resolve("denied")
109
- screencastPermissionPromise = null
110
- return@launch
111
- }
112
-
113
- mediaProjectionIntent = data
114
- screencastPermissionPromise?.resolve("granted")
115
- screencastPermissionPromise = null
116
- }
117
- }
118
-
119
- private fun getSimulcastConfigFromOptions(simulcastConfigMap: org.membraneframework.reactnative.SimulcastConfig): SimulcastConfig {
120
- val simulcastEnabled = simulcastConfigMap.enabled
121
- val activeEncodings = simulcastConfigMap.activeEncodings.map { e -> e.toTrackEncoding() }
122
- return SimulcastConfig(
123
- enabled = simulcastEnabled,
124
- activeEncodings = activeEncodings
125
- )
126
- }
127
-
128
- private fun getMaxBandwidthFromOptions(
129
- maxBandwidthMap: Map<String, Int>?,
130
- maxBandwidthInt: Int
131
- ): TrackBandwidthLimit {
132
- if (maxBandwidthMap != null) {
133
- val maxBandwidthSimulcast = mutableMapOf<String, TrackBandwidthLimit.BandwidthLimit>()
134
- maxBandwidthMap.forEach {
135
- maxBandwidthSimulcast[it.key] = TrackBandwidthLimit.BandwidthLimit(it.value)
136
- }
137
- return TrackBandwidthLimit.SimulcastBandwidthLimit(maxBandwidthSimulcast)
138
- }
139
- return TrackBandwidthLimit.BandwidthLimit(maxBandwidthInt)
140
- }
141
-
142
- private fun create() {
143
- audioSwitchManager = AudioSwitchManager(appContext?.reactContext!!)
144
- fishjamClient =
145
- FishjamClient(
146
- appContext = appContext?.reactContext!!,
147
- listener = this
148
- )
149
- }
150
-
151
- private fun getVideoParametersFromOptions(createOptions: CameraConfig): VideoParameters {
152
- val videoMaxBandwidth =
153
- getMaxBandwidthFromOptions(createOptions.maxBandwidthMap, createOptions.maxBandwidthInt)
154
- var videoParameters =
155
- when (createOptions.quality) {
156
- "QVGA169" -> VideoParameters.presetQVGA169
157
- "VGA169" -> VideoParameters.presetVGA169
158
- "QHD169" -> VideoParameters.presetQHD169
159
- "HD169" -> VideoParameters.presetHD169
160
- "FHD169" -> VideoParameters.presetFHD169
161
- "QVGA43" -> VideoParameters.presetQVGA43
162
- "VGA43" -> VideoParameters.presetVGA43
163
- "QHD43" -> VideoParameters.presetQHD43
164
- "HD43" -> VideoParameters.presetHD43
165
- "FHD43" -> VideoParameters.presetFHD43
166
- else -> VideoParameters.presetVGA169
167
- }
168
- videoParameters =
169
- videoParameters.copy(
170
- dimensions = if (createOptions.flipVideo) videoParameters.dimensions.flip() else videoParameters.dimensions,
171
- simulcastConfig = getSimulcastConfigFromOptions(createOptions.simulcastConfig),
172
- maxBitrate = videoMaxBandwidth
173
- )
174
- return videoParameters
175
- }
176
-
177
- private fun getLocalVideoTrack(): LocalVideoTrack? =
178
- fishjamClient
179
- .getLocalEndpoint()
180
- .tracks
181
- .values
182
- .filterIsInstance<LocalVideoTrack>()
183
- .firstOrNull()
184
-
185
- private fun getLocalAudioTrack(): LocalAudioTrack? =
186
- fishjamClient
187
- .getLocalEndpoint()
188
- .tracks
189
- .values
190
- .filterIsInstance<LocalAudioTrack>()
191
- .firstOrNull()
192
-
193
- private fun getLocalScreencastTrack(): LocalScreencastTrack? =
194
- fishjamClient
195
- .getLocalEndpoint()
196
- .tracks
197
- .values
198
- .filterIsInstance<LocalScreencastTrack>()
199
- .firstOrNull()
200
-
201
- private fun ensureConnected() {
202
- if (!isConnected) {
203
- throw ClientNotConnectedError()
204
- }
205
- }
206
-
207
- private fun ensureVideoTrack() {
208
- if (getLocalVideoTrack() == null) {
209
- throw NoLocalVideoTrackError()
210
- }
211
- }
212
-
213
- private fun ensureAudioTrack() {
214
- if (getLocalAudioTrack() == null) {
215
- throw NoLocalAudioTrackError()
216
- }
217
- }
218
-
219
- private fun ensureScreencastTrack() {
220
- if (getLocalScreencastTrack() == null) {
221
- throw NoScreencastTrackError()
222
- }
223
- }
224
-
225
- override fun onAuthError(reason: AuthError) {
226
- CoroutineScope(Dispatchers.Main).launch {
227
- connectPromise?.reject(ConnectionError(reason))
228
- connectPromise = null
229
- }
230
- }
231
-
232
- fun connect(
233
- url: String,
234
- peerToken: String,
235
- peerMetadata: Map<String, Any>,
236
- config: ConnectConfig,
237
- promise: Promise
238
- ) {
239
- connectPromise = promise
240
- localUserMetadata = peerMetadata
241
- fishjamClient.connect(
242
- com.fishjamcloud.client.ConnectConfig(
243
- url,
244
- peerToken,
245
- peerMetadata,
246
- ReconnectConfig(
247
- config.reconnectConfig.maxAttempts,
248
- config.reconnectConfig.initialDelayMs,
249
- config.reconnectConfig.delayMs
250
- )
251
- )
252
- )
253
- }
254
-
255
- fun leaveRoom() {
256
- if (isScreencastOn) {
257
- stopScreencast()
258
- }
259
- isMicrophoneOn = false
260
- isCameraOn = false
261
- isScreencastOn = false
262
- isConnected = false
263
- fishjamClient.leave()
264
- }
265
-
266
- suspend fun startCamera(config: CameraConfig) {
267
- val cameraTrack = createCameraTrack(config)
268
- setCameraTrackState(cameraTrack, config.cameraEnabled)
269
- emitEndpoints()
270
- }
271
-
272
- private suspend fun createCameraTrack(config: CameraConfig): LocalVideoTrack {
273
- val videoParameters = getVideoParametersFromOptions(config)
274
- videoSimulcastConfig = getSimulcastConfigFromOptions(config.simulcastConfig)
275
- return fishjamClient.createVideoTrack(
276
- videoParameters,
277
- config.videoTrackMetadata,
278
- config.captureDeviceId
279
- )
280
- }
281
-
282
- private fun setCameraTrackState(
283
- cameraTrack: LocalVideoTrack,
284
- isEnabled: Boolean
285
- ) {
286
- cameraTrack.setEnabled(isEnabled)
287
- isCameraOn = isEnabled
288
- val eventName = EmitableEvents.IsCameraOn
289
- val isCameraOnMap = mapOf(eventName to isEnabled)
290
- emitEvent(eventName, isCameraOnMap)
291
- }
292
-
293
- fun toggleCamera(): Boolean {
294
- ensureVideoTrack()
295
- getLocalVideoTrack()?.let { setCameraTrackState(it, !isCameraOn) }
296
- return isCameraOn
297
- }
298
-
299
- suspend fun flipCamera() {
300
- ensureVideoTrack()
301
- onLocalTrackSwitchListener.forEach { it.onLocalTrackWillSwitch() }
302
- getLocalVideoTrack()?.flipCamera()
303
- onLocalTrackSwitchListener.forEach { it.onLocalTrackSwitched() }
304
- }
305
-
306
- suspend fun switchCamera(captureDeviceId: String) {
307
- ensureVideoTrack()
308
- onLocalTrackSwitchListener.forEach { it.onLocalTrackWillSwitch() }
309
- getLocalVideoTrack()?.switchCamera(captureDeviceId)
310
- onLocalTrackSwitchListener.forEach {
311
- it.onLocalTrackSwitched()
312
- }
313
- }
314
-
315
- private suspend fun startMicrophone() {
316
- val microphoneTrack = fishjamClient.createAudioTrack(emptyMap())
317
- setMicrophoneTrackState(microphoneTrack, true)
318
- emitEndpoints()
319
- }
320
-
321
- private fun setMicrophoneTrackState(
322
- microphoneTrack: LocalAudioTrack,
323
- isEnabled: Boolean
324
- ) {
325
- microphoneTrack.setEnabled(isEnabled)
326
- isMicrophoneOn = isEnabled
327
- val eventName = EmitableEvents.IsMicrophoneOn
328
- val isMicrophoneOnMap = mapOf(eventName to isEnabled)
329
- emitEvent(eventName, isMicrophoneOnMap)
330
- }
331
-
332
- suspend fun toggleMicrophone(): Boolean {
333
- if (getLocalAudioTrack() == null) {
334
- startMicrophone()
335
- } else {
336
- getLocalAudioTrack()?.let { setMicrophoneTrackState(it, !isMicrophoneOn) }
337
- }
338
- return isMicrophoneOn
339
- }
340
-
341
- fun handleScreencastPermission(promise: Promise) {
342
- screencastPermissionPromise = promise
343
- if (!isScreencastOn) {
344
- ensureConnected()
345
- val currentActivity = appContext?.currentActivity ?: throw ActivityNotFoundException()
346
-
347
- val mediaProjectionManager =
348
- appContext?.reactContext!!.getSystemService(
349
- AppCompatActivity.MEDIA_PROJECTION_SERVICE
350
- ) as MediaProjectionManager
351
- val intent = mediaProjectionManager.createScreenCaptureIntent()
352
- currentActivity.startActivityForResult(intent, SCREENCAST_REQUEST)
353
- }
354
- }
355
-
356
- suspend fun toggleScreencast(screencastOptions: ScreencastOptions) {
357
- this.screencastMetadata = screencastOptions.screencastMetadata
358
- this.screencastQuality = screencastOptions.quality
359
- this.screencastSimulcastConfig =
360
- getSimulcastConfigFromOptions(screencastOptions.simulcastConfig)
361
- this.screencastMaxBandwidth =
362
- getMaxBandwidthFromOptions(
363
- screencastOptions.maxBandwidthMap,
364
- screencastOptions.maxBandwidthInt
365
- )
366
- if (!isScreencastOn) {
367
- ensureConnected()
368
- startScreencast()
369
- } else {
370
- stopScreencast()
371
- }
372
- }
373
-
374
- fun getPeers(): List<Map<String, Any?>> =
375
- getAllPeers().map { endpoint ->
376
- mapOf(
377
- "id" to endpoint.id,
378
- "isLocal" to (endpoint.id == fishjamClient.getLocalEndpoint().id),
379
- "type" to endpoint.type,
380
- "metadata" to endpoint.metadata,
381
- "tracks" to
382
- endpoint.tracks.values.mapNotNull { track ->
383
- when (track) {
384
- is RemoteVideoTrack ->
385
- mapOf(
386
- "id" to track.id(),
387
- "type" to "Video",
388
- "metadata" to track.metadata,
389
- "encoding" to track.encoding?.rid,
390
- "encodingReason" to track.encodingReason?.value
391
- )
392
-
393
- is RemoteAudioTrack ->
394
- mapOf(
395
- "id" to track.id(),
396
- "type" to "Audio",
397
- "metadata" to track.metadata,
398
- "vadStatus" to track.vadStatus.value
399
- )
400
-
401
- is LocalVideoTrack ->
402
- mapOf(
403
- "id" to track.id(),
404
- "type" to "Video",
405
- "metadata" to track.metadata
406
- )
407
-
408
- is LocalScreencastTrack ->
409
- mapOf(
410
- "id" to track.id(),
411
- "type" to "Video",
412
- "metadata" to track.metadata
413
- )
414
-
415
- is LocalAudioTrack ->
416
- mapOf(
417
- "id" to track.id(),
418
- "type" to "Audio",
419
- "metadata" to track.metadata
420
- )
421
-
422
- else -> {
423
- null
424
- }
425
- }
426
- }
427
- )
428
- }
429
-
430
- fun getCaptureDevices(): List<Map<String, Any>> {
431
- val devices = LocalVideoTrack.getCaptureDevices(appContext?.reactContext!!)
432
- return devices.map { device ->
433
- mapOf<String, Any>(
434
- "id" to device.deviceName,
435
- "name" to device.deviceName,
436
- "isFrontFacing" to device.isFrontFacing,
437
- "isBackFacing" to device.isBackFacing
438
- )
439
- }
440
- }
441
-
442
- fun updatePeerMetadata(metadata: Metadata) {
443
- ensureConnected()
444
- fishjamClient.updatePeerMetadata(metadata)
445
- }
446
-
447
- private fun updateTrackMetadata(
448
- trackId: String,
449
- metadata: Metadata
450
- ) {
451
- fishjamClient.updateTrackMetadata(trackId, metadata)
452
- emitEndpoints()
453
- }
454
-
455
- fun updateLocalVideoTrackMetadata(metadata: Metadata) {
456
- ensureVideoTrack()
457
- getLocalVideoTrack()?.let {
458
- updateTrackMetadata(it.id(), metadata)
459
- }
460
- }
461
-
462
- fun updateLocalAudioTrackMetadata(metadata: Metadata) {
463
- ensureAudioTrack()
464
- getLocalAudioTrack()?.let {
465
- updateTrackMetadata(it.id(), metadata)
466
- }
467
- }
468
-
469
- fun updateLocalScreencastTrackMetadata(metadata: Metadata) {
470
- ensureScreencastTrack()
471
- getLocalScreencastTrack()?.let {
472
- updateTrackMetadata(it.id(), metadata)
473
- }
474
- }
475
-
476
- fun setOutputAudioDevice(audioDevice: String) {
477
- audioSwitchManager?.selectAudioOutput(AudioDeviceKind.fromTypeName(audioDevice))
478
- }
479
-
480
- fun startAudioSwitcher() {
481
- audioSwitchManager?.let {
482
- it.start(this::emitAudioDeviceEvent)
483
- emitAudioDeviceEvent(
484
- it.availableAudioDevices(),
485
- it.selectedAudioDevice()
486
- )
487
- }
488
- }
489
-
490
- fun stopAudioSwitcher() {
491
- audioSwitchManager?.stop()
492
- }
493
-
494
- private fun toggleTrackEncoding(
495
- encoding: String,
496
- trackId: String,
497
- simulcastConfig: SimulcastConfig
498
- ): SimulcastConfig {
499
- val trackEncoding = encoding.toTrackEncoding()
500
-
501
- val isTrackEncodingActive = simulcastConfig.activeEncodings.contains(trackEncoding)
502
-
503
- if (isTrackEncodingActive) {
504
- fishjamClient.disableTrackEncoding(trackId, trackEncoding)
505
- } else {
506
- fishjamClient.enableTrackEncoding(trackId, trackEncoding)
507
- }
508
-
509
- val updatedActiveEncodings =
510
- if (isTrackEncodingActive) {
511
- simulcastConfig.activeEncodings.filter { it != trackEncoding }
512
- } else {
513
- simulcastConfig.activeEncodings + trackEncoding
514
- }
515
-
516
- return SimulcastConfig(
517
- enabled = true,
518
- activeEncodings = updatedActiveEncodings
519
- )
520
- }
521
-
522
- fun toggleScreencastTrackEncoding(encoding: String): Map<String, Any> {
523
- ensureScreencastTrack()
524
- getLocalScreencastTrack()?.let {
525
- screencastSimulcastConfig = toggleTrackEncoding(encoding, it.id(), screencastSimulcastConfig)
526
- }
527
- return getSimulcastConfigAsRNMap(screencastSimulcastConfig)
528
- }
529
-
530
- fun setScreencastTrackBandwidth(bandwidth: Int) {
531
- ensureScreencastTrack()
532
- getLocalScreencastTrack()?.let {
533
- fishjamClient.setTrackBandwidth(it.id(), TrackBandwidthLimit.BandwidthLimit(bandwidth))
534
- }
535
- }
536
-
537
- fun setScreencastTrackEncodingBandwidth(
538
- encoding: String,
539
- bandwidth: Int
540
- ) {
541
- ensureScreencastTrack()
542
- getLocalScreencastTrack()?.let {
543
- fishjamClient.setEncodingBandwidth(
544
- it.id(),
545
- encoding,
546
- TrackBandwidthLimit.BandwidthLimit(bandwidth)
547
- )
548
- }
549
- }
550
-
551
- fun setTargetTrackEncoding(
552
- trackId: String,
553
- encoding: String
554
- ) {
555
- ensureConnected()
556
- fishjamClient.setTargetTrackEncoding(trackId, encoding.toTrackEncoding())
557
- }
558
-
559
- fun toggleVideoTrackEncoding(encoding: String): Map<String, Any> {
560
- ensureVideoTrack()
561
- val trackId = getLocalVideoTrack()?.id() ?: return emptyMap()
562
- videoSimulcastConfig = toggleTrackEncoding(encoding, trackId, videoSimulcastConfig)
563
- val eventName = EmitableEvents.SimulcastConfigUpdate
564
- emitEvent(eventName, getSimulcastConfigAsRNMap(videoSimulcastConfig))
565
- return getSimulcastConfigAsRNMap(videoSimulcastConfig)
566
- }
567
-
568
- fun setVideoTrackEncodingBandwidth(
569
- encoding: String,
570
- bandwidth: Int
571
- ) {
572
- ensureVideoTrack()
573
- getLocalVideoTrack()?.let {
574
- fishjamClient.setEncodingBandwidth(
575
- it.id(),
576
- encoding,
577
- TrackBandwidthLimit.BandwidthLimit(bandwidth)
578
- )
579
- }
580
- }
581
-
582
- fun setVideoTrackBandwidth(bandwidth: Int) {
583
- ensureVideoTrack()
584
- getLocalVideoTrack()?.let {
585
- fishjamClient.setTrackBandwidth(it.id(), TrackBandwidthLimit.BandwidthLimit(bandwidth))
586
- }
587
- }
588
-
589
- fun changeWebRTCLoggingSeverity(severity: String) {
590
- when (severity) {
591
- "verbose" -> fishjamClient.changeWebRTCLoggingSeverity(Logging.Severity.LS_VERBOSE)
592
-
593
- "info" -> fishjamClient.changeWebRTCLoggingSeverity(Logging.Severity.LS_INFO)
594
-
595
- "error" -> fishjamClient.changeWebRTCLoggingSeverity(Logging.Severity.LS_ERROR)
596
-
597
- "warning" -> fishjamClient.changeWebRTCLoggingSeverity(Logging.Severity.LS_WARNING)
598
-
599
- "none" -> fishjamClient.changeWebRTCLoggingSeverity(Logging.Severity.LS_NONE)
600
-
601
- else -> {
602
- throw CodedException("Severity with name=$severity not found")
603
- }
604
- }
605
- }
606
-
607
- private fun rtcOutboundStatsToRNMap(stats: RTCOutboundStats): Map<String, Any?> {
608
- val innerMap = mutableMapOf<String, Double>()
609
- innerMap["bandwidth"] = stats.qualityLimitationDurations?.bandwidth ?: 0.0
610
- innerMap["cpu"] = stats.qualityLimitationDurations?.cpu ?: 0.0
611
- innerMap["none"] = stats.qualityLimitationDurations?.none ?: 0.0
612
- innerMap["other"] = stats.qualityLimitationDurations?.other ?: 0.0
613
-
614
- val res = mutableMapOf<String, Any?>()
615
- res["kind"] = stats.kind
616
- res["rid"] = stats.rid
617
- res["bytesSent"] = stats.bytesSent?.toInt() ?: 0
618
- res["targetBitrate"] = stats.targetBitrate ?: 0.0
619
- res["packetsSent"] = stats.packetsSent?.toInt() ?: 0
620
- res["framesEncoded"] = stats.framesEncoded?.toInt() ?: 0
621
- res["framesPerSecond"] = stats.framesPerSecond ?: 0.0
622
- res["frameWidth"] = stats.frameWidth?.toInt() ?: 0
623
- res["frameHeight"] = stats.frameHeight?.toInt() ?: 0
624
- res["qualityLimitationDurations"] = innerMap
625
-
626
- return res
627
- }
628
-
629
- private fun rtcInboundStatsToRNMap(stats: RTCInboundStats): Map<String, Any?> {
630
- val res = mutableMapOf<String, Any?>()
631
- res["kind"] = stats.kind
632
- res["jitter"] = stats.jitter ?: 0.0
633
- res["packetsLost"] = stats.packetsLost ?: 0
634
- res["packetsReceived"] = stats.packetsReceived?.toInt() ?: 0
635
- res["bytesReceived"] = stats.bytesReceived?.toInt() ?: 0
636
- res["framesReceived"] = stats.framesReceived ?: 0
637
- res["frameWidth"] = stats.frameWidth?.toInt() ?: 0
638
- res["frameHeight"] = stats.frameHeight?.toInt() ?: 0
639
- res["framesPerSecond"] = stats.framesPerSecond ?: 0.0
640
- res["framesDropped"] = stats.framesDropped?.toInt() ?: 0
641
-
642
- return res
643
- }
644
-
645
- fun getStatistics(): MutableMap<String, Map<String, Any?>> {
646
- val newMap = mutableMapOf<String, Map<String, Any?>>()
647
- fishjamClient.getStats().forEach { entry ->
648
- newMap[entry.key] =
649
- if (entry.value is RTCInboundStats) {
650
- rtcInboundStatsToRNMap(
651
- entry.value as RTCInboundStats
652
- )
653
- } else {
654
- rtcOutboundStatsToRNMap(entry.value as RTCOutboundStats)
655
- }
656
- }
657
- return newMap
658
- }
659
-
660
- private suspend fun startScreencast() {
661
- val videoParameters = getScreencastVideoParameters()
662
- if (mediaProjectionIntent == null) {
663
- throw MissingScreencastPermission()
664
- }
665
- fishjamClient.createScreencastTrack(
666
- mediaProjectionIntent!!,
667
- videoParameters,
668
- screencastMetadata
669
- )
670
- mediaProjectionIntent = null
671
-
672
- setScreencastTrackState(true)
673
- emitEndpoints()
674
- }
675
-
676
- private fun getScreencastVideoParameters(): VideoParameters {
677
- val videoParameters =
678
- when (screencastQuality) {
679
- "VGA" -> VideoParameters.presetScreenShareVGA
680
- "HD5" -> VideoParameters.presetScreenShareHD5
681
- "HD15" -> VideoParameters.presetScreenShareHD15
682
- "FHD15" -> VideoParameters.presetScreenShareFHD15
683
- "FHD30" -> VideoParameters.presetScreenShareFHD30
684
- else -> VideoParameters.presetScreenShareHD15
685
- }
686
- val dimensions = videoParameters.dimensions.flip()
687
- return videoParameters.copy(
688
- dimensions = dimensions,
689
- simulcastConfig = screencastSimulcastConfig,
690
- maxBitrate = screencastMaxBandwidth
691
- )
692
- }
693
-
694
- private fun setScreencastTrackState(isEnabled: Boolean) {
695
- isScreencastOn = isEnabled
696
- val eventName = EmitableEvents.IsScreencastOn
697
- emitEvent(eventName, mapOf(eventName to isEnabled))
698
- }
699
-
700
- private fun stopScreencast() {
701
- ensureScreencastTrack()
702
- coroutineScope.launch {
703
- val screencastTrack =
704
- fishjamClient.getLocalEndpoint().tracks.values.first { track ->
705
- track is LocalScreencastTrack
706
- } as? LocalScreencastTrack
707
- if (screencastTrack != null) {
708
- fishjamClient.removeTrack(screencastTrack.id())
709
- }
710
- setScreencastTrackState(false)
711
- emitEndpoints()
712
- }
713
- }
714
-
715
- private fun emitEvent(
716
- eventName: String,
717
- data: Map<String, Any?>
718
- ) {
719
- sendEvent(eventName, data)
720
- }
721
-
722
- private fun emitEndpoints() {
723
- val eventName = EmitableEvents.PeersUpdate
724
- val map = mapOf(eventName to getPeers())
725
- emitEvent(eventName, map)
726
- }
727
-
728
- private fun audioDeviceAsRNMap(audioDevice: AudioDevice): Map<String, String?> =
729
- mapOf(
730
- "name" to audioDevice.name,
731
- "type" to AudioDeviceKind.fromAudioDevice(audioDevice)?.typeName
732
- )
733
-
734
- private fun emitAudioDeviceEvent(
735
- audioDevices: List<AudioDevice>,
736
- selectedDevice: AudioDevice?
737
- ) {
738
- val eventName = EmitableEvents.AudioDeviceUpdate
739
- val map =
740
- mapOf(
741
- eventName to
742
- mapOf(
743
- "selectedDevice" to (
744
- if (selectedDevice != null) {
745
- audioDeviceAsRNMap(
746
- selectedDevice
747
- )
748
- } else {
749
- null
750
- }
751
- ),
752
- "availableDevices" to
753
- audioDevices.map { audioDevice ->
754
- audioDeviceAsRNMap(
755
- audioDevice
756
- )
757
- }
758
- )
759
- )
760
-
761
- emitEvent(eventName, map)
762
- }
763
-
764
- private fun getSimulcastConfigAsRNMap(simulcastConfig: SimulcastConfig): Map<String, Any> =
765
- mapOf(
766
- "enabled" to simulcastConfig.enabled,
767
- "activeEncodings" to
768
- simulcastConfig.activeEncodings.map {
769
- it.rid
770
- }
771
- )
772
-
773
- override fun onJoined(
774
- peerID: String,
775
- peersInRoom: MutableMap<String, Endpoint>
776
- ) {
777
- CoroutineScope(Dispatchers.Main).launch {
778
- isConnected = true
779
- connectPromise?.resolve(null)
780
- connectPromise = null
781
- emitEndpoints()
782
- }
783
- }
784
-
785
- override fun onJoinError(metadata: Any) {
786
- CoroutineScope(Dispatchers.Main).launch {
787
- connectPromise?.reject(JoinError(metadata))
788
- connectPromise = null
789
- }
790
- }
791
-
792
- private fun addOrUpdateTrack(ctx: Track) {
793
- emitEndpoints()
794
- onTracksUpdateListeners.forEach { it.onTracksUpdate() }
795
- }
796
-
797
- override fun onTrackReady(ctx: Track) {
798
- CoroutineScope(Dispatchers.Main).launch {
799
- addOrUpdateTrack(ctx)
800
- }
801
- }
802
-
803
- override fun onTrackAdded(ctx: Track) {}
804
-
805
- override fun onTrackRemoved(ctx: Track) {
806
- CoroutineScope(Dispatchers.Main).launch {
807
- emitEndpoints()
808
- }
809
- }
810
-
811
- override fun onTrackUpdated(ctx: Track) {
812
- CoroutineScope(Dispatchers.Main).launch {
813
- emitEndpoints()
814
- }
815
- }
816
-
817
- override fun onPeerJoined(peer: Peer) {
818
- CoroutineScope(Dispatchers.Main).launch {
819
- emitEndpoints()
820
- }
821
- }
822
-
823
- override fun onPeerLeft(peer: Peer) {
824
- CoroutineScope(Dispatchers.Main).launch {
825
- emitEndpoints()
826
- }
827
- }
828
-
829
- override fun onPeerUpdated(peer: Peer) {}
830
-
831
- override fun onBandwidthEstimationChanged(estimation: Long) {
832
- val eventName = EmitableEvents.BandwidthEstimation
833
- emitEvent(eventName, mapOf(eventName to estimation.toFloat()))
834
- }
835
-
836
- override fun onDisconnected() {}
837
-
838
- override fun onSocketClose(
839
- code: Int,
840
- reason: String
841
- ) {
842
- CoroutineScope(Dispatchers.Main).launch {
843
- connectPromise?.reject(SocketClosedError(code, reason))
844
- connectPromise = null
845
- }
846
- }
847
-
848
- override fun onSocketError(t: Throwable) {
849
- CoroutineScope(Dispatchers.Main).launch {
850
- connectPromise?.reject(SocketError(t.message ?: t.toString()))
851
- connectPromise = null
852
- }
853
- }
854
-
855
- override fun onReconnected() {
856
- emitEvent(EmitableEvents.Reconnected, emptyMap())
857
- }
858
-
859
- override fun onReconnectionStarted() {
860
- emitEvent(EmitableEvents.ReconnectionStarted, emptyMap())
861
- }
862
-
863
- override fun onReconnectionRetriesLimitReached() {
864
- emitEvent(EmitableEvents.ReconnectionRetriesLimitReached, emptyMap())
865
- }
866
- }