From 7c6da0230c5a6866c15be35ef96dcf605dfa0e03 Mon Sep 17 00:00:00 2001 From: Mikolaj Date: Mon, 27 Nov 2023 18:31:42 +0100 Subject: [PATCH] Add events object and improve event emiting --- .../com/reactnativemembrane/EmitableEvents.kt | 12 + .../com/reactnativemembrane/MembraneWebRTC.kt | 188 +- example/ios/Podfile.lock | 414 ++-- .../project.pbxproj | 17 + ios/Events.swift | 10 + ios/MembraneWebRTC.swift | 1766 +++++++++-------- src/index.tsx | 47 +- 7 files changed, 1301 insertions(+), 1153 deletions(-) create mode 100644 android/src/main/java/com/reactnativemembrane/EmitableEvents.kt create mode 100644 ios/Events.swift diff --git a/android/src/main/java/com/reactnativemembrane/EmitableEvents.kt b/android/src/main/java/com/reactnativemembrane/EmitableEvents.kt new file mode 100644 index 00000000..29207a8b --- /dev/null +++ b/android/src/main/java/com/reactnativemembrane/EmitableEvents.kt @@ -0,0 +1,12 @@ +package com.reactnativemembrane + +object EmitableEvents { + const val IsCameraOn = "IsCameraOn" + const val IsMicrophoneOn = "IsMicrophoneOn" + const val IsScreencastOn = "IsScreencastOn" + const val SimulcastConfigUpdate = "SimulcastConfigUpdate" + const val EndpointsUpdate = "EndpointsUpdate" + const val AudioDeviceUpdate = "AudioDeviceUpdate" + const val SendMediaEvent = "SendMediaEvent" + const val BandwidthEstimation = "BandwidthEstimation" +} diff --git a/android/src/main/java/com/reactnativemembrane/MembraneWebRTC.kt b/android/src/main/java/com/reactnativemembrane/MembraneWebRTC.kt index 332bf532..b2a1698d 100644 --- a/android/src/main/java/com/reactnativemembrane/MembraneWebRTC.kt +++ b/android/src/main/java/com/reactnativemembrane/MembraneWebRTC.kt @@ -34,7 +34,7 @@ import org.webrtc.Logging import java.util.UUID class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> Unit) : - MembraneRTCListener { + MembraneRTCListener { private val SCREENCAST_REQUEST = 1 private var membraneRTC: MembraneRTC? = null @@ -83,7 +83,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U } fun onActivityResult( - requestCode: Int, resultCode: Int, data: Intent? + requestCode: Int, resultCode: Int, data: Intent? ) { if (requestCode != SCREENCAST_REQUEST) return if (resultCode != Activity.RESULT_OK) { @@ -101,12 +101,12 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U val simulcastEnabled = simulcastConfigMap.enabled val activeEncodings = simulcastConfigMap.activeEncodings.map { e -> e.toTrackEncoding() } return SimulcastConfig( - enabled = simulcastEnabled, activeEncodings = activeEncodings + enabled = simulcastEnabled, activeEncodings = activeEncodings ) } private fun getMaxBandwidthFromOptions( - maxBandwidthMap: Map?, maxBandwidthInt: Int + maxBandwidthMap: Map?, maxBandwidthInt: Int ): TrackBandwidthLimit { if (maxBandwidthMap != null) { val maxBandwidthSimulcast = mutableMapOf() @@ -126,9 +126,9 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U val uuid = UUID.randomUUID().toString() localEndpointId = uuid val endpoint = RNEndpoint( - id = uuid, - metadata = localUserMetadata, - type = "webrtc", + id = uuid, + metadata = localUserMetadata, + type = "webrtc", ) endpoints[uuid] = endpoint emitEndpoints() @@ -137,7 +137,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U fun create() { audioSwitchManager = AudioSwitchManager(appContext?.reactContext!!) membraneRTC = MembraneRTC.create( - appContext = appContext?.reactContext!!, listener = this + appContext = appContext?.reactContext!!, listener = this ) ensureCreated() initLocalEndpoint() @@ -145,7 +145,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U private fun getVideoParametersFromOptions(createOptions: CameraConfig): VideoParameters { val videoMaxBandwidth = - getMaxBandwidthFromOptions(createOptions.maxBandwidthMap, createOptions.maxBandwidthInt) + getMaxBandwidthFromOptions(createOptions.maxBandwidthMap, createOptions.maxBandwidthInt) var videoParameters = when (createOptions.quality) { "QVGA169" -> VideoParameters.presetQVGA169 "VGA169" -> VideoParameters.presetVGA169 @@ -160,9 +160,9 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U else -> VideoParameters.presetVGA169 } videoParameters = videoParameters.copy( - dimensions = if (createOptions.flipVideo) videoParameters.dimensions.flip() else videoParameters.dimensions, - simulcastConfig = videoSimulcastConfig, - maxBitrate = videoMaxBandwidth + dimensions = if (createOptions.flipVideo) videoParameters.dimensions.flip() else videoParameters.dimensions, + simulcastConfig = videoSimulcastConfig, + maxBitrate = videoMaxBandwidth ) return videoParameters } @@ -172,6 +172,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U throw CodedException("Client not created yet. Make sure to call create() first!") } } + private fun ensureConnected() { if (membraneRTC == null) { throw CodedException("Client not connected to server yet. Make sure to call connect() first!") @@ -196,8 +197,8 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U } } - private fun ensureEndpoints(){ - if (localEndpointId == null || endpoints.size == 0){ + private fun ensureEndpoints() { + if (localEndpointId == null || endpoints.size == 0) { throw CodedException("No endpoints available. Ensure the connection is established or endpoints are present.") } } @@ -214,7 +215,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U connectPromise = promise localUserMetadata = endpointMetadata val id = localEndpointId ?: return - val endpoint = endpoints[id]?: return + val endpoint = endpoints[id] ?: return endpoints[id] = endpoint.copy(metadata = localUserMetadata) membraneRTC?.connect(localUserMetadata) } @@ -242,7 +243,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U val videoParameters = getVideoParametersFromOptions(config) videoSimulcastConfig = getSimulcastConfigFromOptions(config.simulcastConfig) return membraneRTC?.createVideoTrack( - videoParameters, config.videoTrackMetadata, config.captureDeviceId + videoParameters, config.videoTrackMetadata, config.captureDeviceId ) } @@ -250,14 +251,15 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U ensureConnected() cameraTrack.setEnabled(isEnabled) isCameraOn = isEnabled - val isCameraOnMap = mapOf("IsCameraOn" to isEnabled) - emitEvent("IsCameraOn", isCameraOnMap) + val eventName = EmitableEvents.IsCameraOn + val isCameraOnMap = mapOf(eventName to isEnabled) + emitEvent(eventName, isCameraOnMap) } private fun addTrackToLocalEndpoint(track: VideoTrack, metadata: Metadata) { ensureEndpoints() val localEndpoint = endpoints[localEndpointId] - localEndpoint?.let{ + localEndpoint?.let { it.addOrUpdateTrack(track, metadata) emitEndpoints() } @@ -266,7 +268,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U fun toggleCamera(): Boolean { ensureVideoTrack() localVideoTrack?.let { setCameraTrackState(it, !isCameraOn) } - return isCameraOn + return isCameraOn } fun flipCamera() { @@ -297,6 +299,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U emitEndpoints() } } + private fun removeTrackFromLocalEndpoint(track: AudioTrack) { ensureEndpoints() val localEndpoint = endpoints[localEndpointId] @@ -309,7 +312,8 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U fun startMicrophone(config: MicrophoneConfig) { ensureConnected() - val microphoneTrack = membraneRTC?.createAudioTrack(config.audioTrackMetadata) ?: throw CodedException("Failed to Create Track") + val microphoneTrack = membraneRTC?.createAudioTrack(config.audioTrackMetadata) + ?: throw CodedException("Failed to Create Track") localAudioTrack = microphoneTrack addTrackToLocalEndpoint(microphoneTrack, config.audioTrackMetadata) setMicrophoneTrackState(microphoneTrack, config.microphoneEnabled) @@ -319,23 +323,24 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U ensureConnected() microphoneTrack.setEnabled(isEnabled) isMicrophoneOn = isEnabled - val isMicrophoneOnMap = mapOf("IsMicrophoneOn" to isEnabled) - emitEvent("IsMicrophoneOn", isMicrophoneOnMap) + val eventName = EmitableEvents.IsMicrophoneOn + val isMicrophoneOnMap = mapOf(eventName to isEnabled) + emitEvent(eventName, isMicrophoneOnMap) } fun toggleMicrophone(): Boolean { ensureAudioTrack() localAudioTrack?.let { setMicrophoneTrackState(it, !isMicrophoneOn) } - return isMicrophoneOn + return isMicrophoneOn } fun toggleScreencast(screencastOptions: ScreencastOptions, promise: Promise) { this.screencastMetadata = screencastOptions.screencastMetadata this.screencastQuality = screencastOptions.quality this.screencastSimulcastConfig = - getSimulcastConfigFromOptions(screencastOptions.simulcastConfig) + getSimulcastConfigFromOptions(screencastOptions.simulcastConfig) this.screencastMaxBandwidth = getMaxBandwidthFromOptions( - screencastOptions.maxBandwidthMap, screencastOptions.maxBandwidthInt + screencastOptions.maxBandwidthMap, screencastOptions.maxBandwidthInt ) screencastPromise = promise if (!isScreencastOn) { @@ -343,7 +348,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U val currentActivity = appContext?.currentActivity ?: throw ActivityNotFoundException() val mediaProjectionManager = - appContext?.reactContext!!.getSystemService(AppCompatActivity.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager + appContext?.reactContext!!.getSystemService(AppCompatActivity.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager val intent = mediaProjectionManager.createScreenCaptureIntent() currentActivity.startActivityForResult(intent, SCREENCAST_REQUEST) } else { @@ -352,38 +357,38 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U } fun getEndpoints(): List> { - return endpoints.values.map { endpoint -> - mapOf("id" to endpoint.id, - "isLocal" to (endpoint.id == localEndpointId), - "type" to endpoint.type, - "metadata" to endpoint.metadata, - "tracks" to endpoint.videoTracks.values.map { video -> - mapOf( - "id" to video.id(), - "type" to "Video", - "metadata" to (endpoint.tracksMetadata[video.id()] ?: emptyMap()), - "encoding" to trackContexts[video.id()]?.encoding?.rid, - "encodingReason" to trackContexts[video.id()]?.encodingReason?.value - ) - } + endpoint.audioTracks.values.map { audio -> - mapOf( - "id" to audio.id(), - "type" to "Audio", - "metadata" to (endpoint.tracksMetadata[audio.id()] ?: emptyMap()), - "vadStatus" to trackContexts[audio.id()]?.vadStatus?.value - ) - }) - } + return endpoints.values.map { endpoint -> + mapOf("id" to endpoint.id, + "isLocal" to (endpoint.id == localEndpointId), + "type" to endpoint.type, + "metadata" to endpoint.metadata, + "tracks" to endpoint.videoTracks.values.map { video -> + mapOf( + "id" to video.id(), + "type" to "Video", + "metadata" to (endpoint.tracksMetadata[video.id()] ?: emptyMap()), + "encoding" to trackContexts[video.id()]?.encoding?.rid, + "encodingReason" to trackContexts[video.id()]?.encodingReason?.value + ) + } + endpoint.audioTracks.values.map { audio -> + mapOf( + "id" to audio.id(), + "type" to "Audio", + "metadata" to (endpoint.tracksMetadata[audio.id()] ?: emptyMap()), + "vadStatus" to trackContexts[audio.id()]?.vadStatus?.value + ) + }) + } } fun getCaptureDevices(): List> { val devices = LocalVideoTrack.getCaptureDevices(appContext?.reactContext!!) return devices.map { device -> mapOf( - "id" to device.deviceName, - "name" to device.deviceName, - "isFrontFacing" to device.isFrontFacing, - "isBackFacing" to device.isBackFacing + "id" to device.deviceName, + "name" to device.deviceName, + "isFrontFacing" to device.isFrontFacing, + "isBackFacing" to device.isBackFacing ) } } @@ -434,7 +439,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U audioSwitchManager?.let { it.start(this::emitAudioDeviceEvent) emitAudioDeviceEvent( - it.availableAudioDevices(), it.selectedAudioDevice() + it.availableAudioDevices(), it.selectedAudioDevice() ) } } @@ -444,7 +449,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U } private fun toggleTrackEncoding( - encoding: String, trackId: String, simulcastConfig: SimulcastConfig + encoding: String, trackId: String, simulcastConfig: SimulcastConfig ): SimulcastConfig { val trackEncoding = encoding.toTrackEncoding() @@ -463,7 +468,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U } return SimulcastConfig( - enabled = true, activeEncodings = updatedActiveEncodings + enabled = true, activeEncodings = updatedActiveEncodings ) } @@ -489,7 +494,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U localScreencastTrack?.let { val trackId = it.id() membraneRTC?.setEncodingBandwidth( - trackId, encoding, TrackBandwidthLimit.BandwidthLimit(bandwidth) + trackId, encoding, TrackBandwidthLimit.BandwidthLimit(bandwidth) ) } } @@ -497,8 +502,8 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U fun setTargetTrackEncoding(trackId: String, encoding: String) { ensureConnected() val globalTrackId = - getGlobalTrackId(trackId) - ?: throw CodedException("Remote track with id=$trackId not found") + getGlobalTrackId(trackId) + ?: throw CodedException("Remote track with id=$trackId not found") membraneRTC?.setTargetTrackEncoding(globalTrackId, encoding.toTrackEncoding()) } @@ -506,7 +511,8 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U ensureVideoTrack() val trackId = localVideoTrack?.id() ?: return emptyMap() videoSimulcastConfig = toggleTrackEncoding(encoding, trackId, videoSimulcastConfig) - emitEvent("SimulcastConfigUpdate", getSimulcastConfigAsRNMap(videoSimulcastConfig)) + val eventName = EmitableEvents.SimulcastConfigUpdate + emitEvent(eventName, getSimulcastConfigAsRNMap(videoSimulcastConfig)) return getSimulcastConfigAsRNMap(videoSimulcastConfig) } @@ -515,14 +521,14 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U localVideoTrack?.let { val trackId = it.id() membraneRTC?.setEncodingBandwidth( - trackId, encoding, TrackBandwidthLimit.BandwidthLimit(bandwidth) + trackId, encoding, TrackBandwidthLimit.BandwidthLimit(bandwidth) ) } } fun setVideoTrackBandwidth(bandwidth: Int) { ensureVideoTrack() - localVideoTrack?.let{ + localVideoTrack?.let { val trackId = it.id() membraneRTC?.setTrackBandwidth(trackId, TrackBandwidthLimit.BandwidthLimit(bandwidth)) } @@ -584,7 +590,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U val newMap = mutableMapOf>() membraneRTC?.getStats()?.forEach { entry -> newMap[entry.key] = if (entry.value is RTCInboundStats) rtcInboundStatsToRNMap( - entry.value as RTCInboundStats + entry.value as RTCInboundStats ) else rtcOutboundStatsToRNMap(entry.value as RTCOutboundStats) } return newMap @@ -594,7 +600,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U localScreencastId = UUID.randomUUID().toString() val videoParameters = getScreencastVideoParameters() val screencastTrack = membraneRTC?.createScreencastTrack( - mediaProjectionPermission, videoParameters, screencastMetadata + mediaProjectionPermission, videoParameters, screencastMetadata ) ?: throw CodedException("Failed to Create ScreenCast Track") localScreencastTrack = screencastTrack addTrackToLocalEndpoint(screencastTrack, screencastMetadata) @@ -614,15 +620,16 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U } val dimensions = videoParameters.dimensions.flip() return videoParameters.copy( - dimensions = dimensions, - simulcastConfig = screencastSimulcastConfig, - maxBitrate = screencastMaxBandwidth, + dimensions = dimensions, + simulcastConfig = screencastSimulcastConfig, + maxBitrate = screencastMaxBandwidth, ) } private fun setScreencastTrackState(isEnabled: Boolean) { isScreencastOn = isEnabled - emitEvent("IsScreencastOn", mapOf("IsScreencastOn" to isEnabled)) + val eventName = EmitableEvents.IsScreencastOn + emitEvent(eventName, mapOf(eventName to isEnabled)) emitEndpoints() } @@ -643,35 +650,38 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U } private fun emitEndpoints() { - val map = mapOf("EndpointsUpdate" to getEndpoints()) - emitEvent("EndpointsUpdate", map) + val eventName = EmitableEvents.EndpointsUpdate + val map = mapOf(eventName to getEndpoints()) + emitEvent(eventName, map) } private fun audioDeviceAsRNMap(audioDevice: AudioDevice): Map { return mapOf( - "name" to audioDevice.name, - "type" to AudioDeviceKind.fromAudioDevice(audioDevice)?.typeName + "name" to audioDevice.name, + "type" to AudioDeviceKind.fromAudioDevice(audioDevice)?.typeName ) } private fun emitAudioDeviceEvent( - audioDevices: List, selectedDevice: AudioDevice? + audioDevices: List, selectedDevice: AudioDevice? ) { + val eventName = EmitableEvents.AudioDeviceUpdate val map = - mapOf("selectedDevice" to (if (selectedDevice != null) audioDeviceAsRNMap(selectedDevice) else null), - "availableDevices" to audioDevices.map { audioDevice -> - audioDeviceAsRNMap( - audioDevice - ) - }) - emitEvent("AudioDeviceUpdate", map) + mapOf(eventName to mapOf("selectedDevice" to (if (selectedDevice != null) audioDeviceAsRNMap(selectedDevice) else null), + "availableDevices" to audioDevices.map { audioDevice -> + audioDeviceAsRNMap( + audioDevice + ) + })) + + emitEvent(eventName, map) } private fun getSimulcastConfigAsRNMap(simulcastConfig: SimulcastConfig): Map { return mapOf("enabled" to simulcastConfig.enabled, - "activeEncodings" to simulcastConfig.activeEncodings.map { - it.rid - }) + "activeEncodings" to simulcastConfig.activeEncodings.map { + it.rid + }) } override fun onConnected(endpointID: String, otherEndpoints: List) { @@ -695,7 +705,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U private fun addOrUpdateTrack(ctx: TrackContext) { val endpoint = endpoints[ctx.endpoint.id] - ?: throw IllegalArgumentException("endpoint with id ${ctx.endpoint.id} not found") + ?: throw IllegalArgumentException("endpoint with id ${ctx.endpoint.id} not found") when (ctx.track) { is RemoteVideoTrack -> { @@ -740,7 +750,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U override fun onTrackRemoved(ctx: TrackContext) { CoroutineScope(Dispatchers.Main).launch { val endpoint = endpoints[ctx.endpoint.id] - ?: throw IllegalArgumentException("endpoint with id ${ctx.endpoint.id} not found") + ?: throw IllegalArgumentException("endpoint with id ${ctx.endpoint.id} not found") when (ctx.track) { is RemoteVideoTrack -> endpoint.removeTrack(ctx.track as RemoteVideoTrack) @@ -766,7 +776,7 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U override fun onEndpointAdded(endpoint: Endpoint) { CoroutineScope(Dispatchers.Main).launch { endpoints[endpoint.id] = - RNEndpoint(id = endpoint.id, metadata = endpoint.metadata, type = endpoint.type) + RNEndpoint(id = endpoint.id, metadata = endpoint.metadata, type = endpoint.type) emitEndpoints() } } @@ -781,11 +791,13 @@ class MembraneWebRTC(val sendEvent: (name: String, data: Map) -> U override fun onEndpointUpdated(endpoint: Endpoint) {} override fun onSendMediaEvent(event: SerializedMediaEvent) { - emitEvent("SendMediaEvent", mapOf("event" to event)) + val eventName = EmitableEvents.SendMediaEvent + emitEvent(eventName, mapOf("event" to event)) } override fun onBandwidthEstimationChanged(estimation: Long) { - emitEvent("BandwidthEstimation", mapOf("BandwidthEstimation" to estimation.toFloat())) + val eventName = EmitableEvents.BandwidthEstimation + emitEvent(eventName, mapOf(eventName to estimation.toFloat())) } override fun onDisconnected() {} diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 031ff08d..99c175b7 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -5,41 +5,41 @@ PODS: - Charts/Core (4.1.0): - SwiftAlgorithms (~> 1.0) - DoubleConversion (1.1.6) - - EXApplication (5.3.0): + - EXApplication (5.3.1): - ExpoModulesCore - EXConstants (14.4.2): - ExpoModulesCore - - EXFileSystem (15.4.2): + - EXFileSystem (15.4.5): - ExpoModulesCore - EXFont (11.4.0): - ExpoModulesCore - - Expo (49.0.2): + - Expo (49.0.21): - ExpoModulesCore - ExpoKeepAwake (12.3.0): - ExpoModulesCore - - ExpoModulesCore (1.5.6): + - ExpoModulesCore (1.5.12): - RCT-Folly (= 2021.07.22.00) - React-Core - React-NativeModulesApple - React-RCTAppDelegate - ReactCommon/turbomodule/core - - EXSplashScreen (0.20.4): + - EXSplashScreen (0.20.5): - ExpoModulesCore - RCT-Folly (= 2021.07.22.00) - React-Core - - FBLazyVector (0.72.1) - - FBReactNativeSpec (0.72.1): - - RCT-Folly (= 2021.07.22.00) - - RCTRequired (= 0.72.1) - - RCTTypeSafety (= 0.72.1) - - React-Core (= 0.72.1) - - React-jsi (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) + - FBLazyVector (0.72.6) + - FBReactNativeSpec (0.72.6): + - RCT-Folly (= 2021.07.22.00) + - RCTRequired (= 0.72.6) + - RCTTypeSafety (= 0.72.6) + - React-Core (= 0.72.6) + - React-jsi (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) - fmt (6.2.1) - glog (0.3.5) - - hermes-engine (0.72.1): - - hermes-engine/Pre-built (= 0.72.1) - - hermes-engine/Pre-built (0.72.1) + - hermes-engine (0.72.6): + - hermes-engine/Pre-built (= 0.72.6) + - hermes-engine/Pre-built (0.72.6) - libevent (2.1.12) - MembraneRTC (5.1.1): - MembraneRTC/Broadcast (= 5.1.1) @@ -77,26 +77,26 @@ PODS: - fmt (~> 6.2.1) - glog - libevent - - RCTRequired (0.72.1) - - RCTTypeSafety (0.72.1): - - FBLazyVector (= 0.72.1) - - RCTRequired (= 0.72.1) - - React-Core (= 0.72.1) - - React (0.72.1): - - React-Core (= 0.72.1) - - React-Core/DevSupport (= 0.72.1) - - React-Core/RCTWebSocket (= 0.72.1) - - React-RCTActionSheet (= 0.72.1) - - React-RCTAnimation (= 0.72.1) - - React-RCTBlob (= 0.72.1) - - React-RCTImage (= 0.72.1) - - React-RCTLinking (= 0.72.1) - - React-RCTNetwork (= 0.72.1) - - React-RCTSettings (= 0.72.1) - - React-RCTText (= 0.72.1) - - React-RCTVibration (= 0.72.1) - - React-callinvoker (0.72.1) - - React-Codegen (0.72.1): + - RCTRequired (0.72.6) + - RCTTypeSafety (0.72.6): + - FBLazyVector (= 0.72.6) + - RCTRequired (= 0.72.6) + - React-Core (= 0.72.6) + - React (0.72.6): + - React-Core (= 0.72.6) + - React-Core/DevSupport (= 0.72.6) + - React-Core/RCTWebSocket (= 0.72.6) + - React-RCTActionSheet (= 0.72.6) + - React-RCTAnimation (= 0.72.6) + - React-RCTBlob (= 0.72.6) + - React-RCTImage (= 0.72.6) + - React-RCTLinking (= 0.72.6) + - React-RCTNetwork (= 0.72.6) + - React-RCTSettings (= 0.72.6) + - React-RCTText (= 0.72.6) + - React-RCTVibration (= 0.72.6) + - React-callinvoker (0.72.6) + - React-Codegen (0.72.6): - DoubleConversion - FBReactNativeSpec - glog @@ -111,11 +111,11 @@ PODS: - React-rncore - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-Core (0.72.1): + - React-Core (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 0.72.1) + - React-Core/Default (= 0.72.6) - React-cxxreact - React-hermes - React-jsi @@ -125,7 +125,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/CoreModulesHeaders (0.72.1): + - React-Core/CoreModulesHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -139,7 +139,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/Default (0.72.1): + - React-Core/Default (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -152,23 +152,23 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/DevSupport (0.72.1): + - React-Core/DevSupport (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 0.72.1) - - React-Core/RCTWebSocket (= 0.72.1) + - React-Core/Default (= 0.72.6) + - React-Core/RCTWebSocket (= 0.72.6) - React-cxxreact - React-hermes - React-jsi - React-jsiexecutor - - React-jsinspector (= 0.72.1) + - React-jsinspector (= 0.72.6) - React-perflogger - React-runtimeexecutor - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTActionSheetHeaders (0.72.1): + - React-Core/RCTActionSheetHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -182,7 +182,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTAnimationHeaders (0.72.1): + - React-Core/RCTAnimationHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -196,7 +196,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTBlobHeaders (0.72.1): + - React-Core/RCTBlobHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -210,7 +210,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTImageHeaders (0.72.1): + - React-Core/RCTImageHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -224,7 +224,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTLinkingHeaders (0.72.1): + - React-Core/RCTLinkingHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -238,7 +238,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTNetworkHeaders (0.72.1): + - React-Core/RCTNetworkHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -252,7 +252,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTSettingsHeaders (0.72.1): + - React-Core/RCTSettingsHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -266,7 +266,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTTextHeaders (0.72.1): + - React-Core/RCTTextHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -280,7 +280,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTVibrationHeaders (0.72.1): + - React-Core/RCTVibrationHeaders (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -294,11 +294,11 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTWebSocket (0.72.1): + - React-Core/RCTWebSocket (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 0.72.1) + - React-Core/Default (= 0.72.6) - React-cxxreact - React-hermes - React-jsi @@ -308,56 +308,57 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-CoreModules (0.72.1): + - React-CoreModules (0.72.6): - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.72.1) - - React-Codegen (= 0.72.1) - - React-Core/CoreModulesHeaders (= 0.72.1) - - React-jsi (= 0.72.1) + - RCTTypeSafety (= 0.72.6) + - React-Codegen (= 0.72.6) + - React-Core/CoreModulesHeaders (= 0.72.6) + - React-jsi (= 0.72.6) - React-RCTBlob - - React-RCTImage (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) + - React-RCTImage (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) - SocketRocket (= 0.6.1) - - React-cxxreact (0.72.1): + - React-cxxreact (0.72.6): - boost (= 1.76.0) - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-callinvoker (= 0.72.1) - - React-jsi (= 0.72.1) - - React-jsinspector (= 0.72.1) - - React-logger (= 0.72.1) - - React-perflogger (= 0.72.1) - - React-runtimeexecutor (= 0.72.1) - - React-debug (0.72.1) - - React-hermes (0.72.1): + - React-callinvoker (= 0.72.6) + - React-debug (= 0.72.6) + - React-jsi (= 0.72.6) + - React-jsinspector (= 0.72.6) + - React-logger (= 0.72.6) + - React-perflogger (= 0.72.6) + - React-runtimeexecutor (= 0.72.6) + - React-debug (0.72.6) + - React-hermes (0.72.6): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - RCT-Folly/Futures (= 2021.07.22.00) - - React-cxxreact (= 0.72.1) + - React-cxxreact (= 0.72.6) - React-jsi - - React-jsiexecutor (= 0.72.1) - - React-jsinspector (= 0.72.1) - - React-perflogger (= 0.72.1) - - React-jsi (0.72.1): + - React-jsiexecutor (= 0.72.6) + - React-jsinspector (= 0.72.6) + - React-perflogger (= 0.72.6) + - React-jsi (0.72.6): - boost (= 1.76.0) - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-jsiexecutor (0.72.1): + - React-jsiexecutor (0.72.6): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-cxxreact (= 0.72.1) - - React-jsi (= 0.72.1) - - React-perflogger (= 0.72.1) - - React-jsinspector (0.72.1) - - React-logger (0.72.1): + - React-cxxreact (= 0.72.6) + - React-jsi (= 0.72.6) + - React-perflogger (= 0.72.6) + - React-jsinspector (0.72.6) + - React-logger (0.72.6): - glog - react-native-charts-wrapper (0.5.11): - Charts (= 4.1.0) @@ -369,7 +370,7 @@ PODS: - RCTTypeSafety - React-Core - ReactCommon/turbomodule/core - - React-NativeModulesApple (0.72.1): + - React-NativeModulesApple (0.72.6): - hermes-engine - React-callinvoker - React-Core @@ -378,17 +379,17 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-perflogger (0.72.1) - - React-RCTActionSheet (0.72.1): - - React-Core/RCTActionSheetHeaders (= 0.72.1) - - React-RCTAnimation (0.72.1): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.72.1) - - React-Codegen (= 0.72.1) - - React-Core/RCTAnimationHeaders (= 0.72.1) - - React-jsi (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) - - React-RCTAppDelegate (0.72.1): + - React-perflogger (0.72.6) + - React-RCTActionSheet (0.72.6): + - React-Core/RCTActionSheetHeaders (= 0.72.6) + - React-RCTAnimation (0.72.6): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.72.6) + - React-Codegen (= 0.72.6) + - React-Core/RCTAnimationHeaders (= 0.72.6) + - React-jsi (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) + - React-RCTAppDelegate (0.72.6): - RCT-Folly - RCTRequired - RCTTypeSafety @@ -400,54 +401,54 @@ PODS: - React-RCTNetwork - React-runtimescheduler - ReactCommon/turbomodule/core - - React-RCTBlob (0.72.1): + - React-RCTBlob (0.72.6): - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Codegen (= 0.72.1) - - React-Core/RCTBlobHeaders (= 0.72.1) - - React-Core/RCTWebSocket (= 0.72.1) - - React-jsi (= 0.72.1) - - React-RCTNetwork (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) - - React-RCTImage (0.72.1): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.72.1) - - React-Codegen (= 0.72.1) - - React-Core/RCTImageHeaders (= 0.72.1) - - React-jsi (= 0.72.1) - - React-RCTNetwork (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) - - React-RCTLinking (0.72.1): - - React-Codegen (= 0.72.1) - - React-Core/RCTLinkingHeaders (= 0.72.1) - - React-jsi (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) - - React-RCTNetwork (0.72.1): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.72.1) - - React-Codegen (= 0.72.1) - - React-Core/RCTNetworkHeaders (= 0.72.1) - - React-jsi (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) - - React-RCTSettings (0.72.1): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.72.1) - - React-Codegen (= 0.72.1) - - React-Core/RCTSettingsHeaders (= 0.72.1) - - React-jsi (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) - - React-RCTText (0.72.1): - - React-Core/RCTTextHeaders (= 0.72.1) - - React-RCTVibration (0.72.1): - - RCT-Folly (= 2021.07.22.00) - - React-Codegen (= 0.72.1) - - React-Core/RCTVibrationHeaders (= 0.72.1) - - React-jsi (= 0.72.1) - - ReactCommon/turbomodule/core (= 0.72.1) - - React-rncore (0.72.1) - - React-runtimeexecutor (0.72.1): - - React-jsi (= 0.72.1) - - React-runtimescheduler (0.72.1): + - React-Codegen (= 0.72.6) + - React-Core/RCTBlobHeaders (= 0.72.6) + - React-Core/RCTWebSocket (= 0.72.6) + - React-jsi (= 0.72.6) + - React-RCTNetwork (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) + - React-RCTImage (0.72.6): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.72.6) + - React-Codegen (= 0.72.6) + - React-Core/RCTImageHeaders (= 0.72.6) + - React-jsi (= 0.72.6) + - React-RCTNetwork (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) + - React-RCTLinking (0.72.6): + - React-Codegen (= 0.72.6) + - React-Core/RCTLinkingHeaders (= 0.72.6) + - React-jsi (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) + - React-RCTNetwork (0.72.6): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.72.6) + - React-Codegen (= 0.72.6) + - React-Core/RCTNetworkHeaders (= 0.72.6) + - React-jsi (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) + - React-RCTSettings (0.72.6): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.72.6) + - React-Codegen (= 0.72.6) + - React-Core/RCTSettingsHeaders (= 0.72.6) + - React-jsi (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) + - React-RCTText (0.72.6): + - React-Core/RCTTextHeaders (= 0.72.6) + - React-RCTVibration (0.72.6): + - RCT-Folly (= 2021.07.22.00) + - React-Codegen (= 0.72.6) + - React-Core/RCTVibrationHeaders (= 0.72.6) + - React-jsi (= 0.72.6) + - ReactCommon/turbomodule/core (= 0.72.6) + - React-rncore (0.72.6) + - React-runtimeexecutor (0.72.6): + - React-jsi (= 0.72.6) + - React-runtimescheduler (0.72.6): - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) @@ -455,33 +456,34 @@ PODS: - React-debug - React-jsi - React-runtimeexecutor - - React-utils (0.72.1): + - React-utils (0.72.6): - glog - RCT-Folly (= 2021.07.22.00) - React-debug - - ReactCommon/turbomodule/bridging (0.72.1): + - ReactCommon/turbomodule/bridging (0.72.6): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-callinvoker (= 0.72.1) - - React-cxxreact (= 0.72.1) - - React-jsi (= 0.72.1) - - React-logger (= 0.72.1) - - React-perflogger (= 0.72.1) - - ReactCommon/turbomodule/core (0.72.1): + - React-callinvoker (= 0.72.6) + - React-cxxreact (= 0.72.6) + - React-jsi (= 0.72.6) + - React-logger (= 0.72.6) + - React-perflogger (= 0.72.6) + - ReactCommon/turbomodule/core (0.72.6): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-callinvoker (= 0.72.1) - - React-cxxreact (= 0.72.1) - - React-jsi (= 0.72.1) - - React-logger (= 0.72.1) - - React-perflogger (= 0.72.1) + - React-callinvoker (= 0.72.6) + - React-cxxreact (= 0.72.6) + - React-jsi (= 0.72.6) + - React-logger (= 0.72.6) + - React-perflogger (= 0.72.6) - RNCAsyncStorage (1.18.2): - React-Core - - RNGestureHandler (2.12.0): + - RNGestureHandler (2.14.0): + - RCT-Folly (= 2021.07.22.00) - React-Core - RNReanimated (3.3.0): - DoubleConversion @@ -515,19 +517,19 @@ PODS: - RNScreens (3.22.1): - React-Core - React-RCTImage - - RNSentry (5.5.0): + - RNSentry (5.10.0): - React-Core - - Sentry/HybridSDK (= 8.7.1) - - Sentry/HybridSDK (8.7.1): - - SentryPrivate (= 8.7.1) - - SentryPrivate (8.7.1) + - Sentry/HybridSDK (= 8.11.0) + - Sentry/HybridSDK (8.11.0): + - SentryPrivate (= 8.11.0) + - SentryPrivate (8.11.0) - SocketRocket (0.6.1) - SwiftAlgorithms (1.0.0) - SwiftLogJellyfish (1.5.2) - SwiftPhoenixClient (4.0.0): - SwiftPhoenixClient/Core (= 4.0.0) - SwiftPhoenixClient/Core (4.0.0) - - SwiftProtobuf (1.24.0) + - SwiftProtobuf (1.25.1) - SwiftyJSON (5.0.0) - WebRTC-SDK (104.5112.15) - Yoga (1.14.0) @@ -639,7 +641,7 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2023-03-20-RNv0.72.0-49794cfc7c81fb8f69fd60c3bbf85a7480cc5a77 + :tag: hermes-2023-08-07-RNv0.72.4-813b2def12bc9df02654b3e3653ae4a68d0572e0 MembraneWebRTC: :path: "../../ios" RCT-Folly: @@ -727,73 +729,73 @@ SPEC CHECKSUMS: boost: 57d2868c099736d80fcd648bf211b4431e51a558 Charts: ce0768268078eee0336f122c3c4ca248e4e204c5 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 - EXApplication: 02655a251434d564bb0e73291f5a490c74b5b76f + EXApplication: 042aa2e3f05258a16962ea1a9914bf288db9c9a1 EXConstants: ce5bbea779da8031ac818c36bea41b10e14d04e1 - EXFileSystem: d7f59869885cfeab3ac771e2a8d0f5ed98cd3fdb + EXFileSystem: f8b838a880254de42a5a7da20ed5ce12e2697c1b EXFont: 738c44c390953ebcbab075a4848bfbef025fd9ee - Expo: 5dfc753088c8787bfe4d8b18797c86c85a017dac + Expo: 61a8e1aa94311557c137c0a4dfd4fe78281cfbb4 ExpoKeepAwake: be4cbd52d9b177cde0fd66daa1913afa3161fc1d - ExpoModulesCore: 26b909d549da585166bcfb16738428e79bbfc093 - EXSplashScreen: 89586b0961acd173b04713b7457949b063a659da - FBLazyVector: 55cd4593d570bd9e5e227488d637ce6a9581ce51 - FBReactNativeSpec: 799b0e1a1561699cd0e424e24fe5624da38402f0 + ExpoModulesCore: c480fd4e3c7c8e81f0a6ba3a7c56869f25fe016d + EXSplashScreen: c0e7f2d4a640f3b875808ed0b88575538daf6d82 + FBLazyVector: 748c0ef74f2bf4b36cfcccf37916806940a64c32 + FBReactNativeSpec: 966f29e4e697de53a3b366355e8f57375c856ad9 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b - hermes-engine: 9df83855a0fd15ef8eb61694652bae636b0c466e + hermes-engine: 8057e75cfc1437b178ac86c8654b24e7fead7f60 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 MembraneRTC: 88c29eec2b6e1729d6f9b3bc048be65c02ab1f1e MembraneWebRTC: b6cf0e0b7758f54417e3825b56068673141d0442 PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265 RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 - RCTRequired: c52ee8fb2b35c1b54031dd8e92d88ad4dba8f2ce - RCTTypeSafety: 75fa444becadf0ebfa0a456b8c64560c7c89c7df - React: 3e5b3962f27b7334eaf5517a35b3434503df35ad - React-callinvoker: c3a225610efe0caadac78da53b6fe78e53eb2b03 - React-Codegen: bba20685e5c1515f8ecb289bd9770835a1338125 - React-Core: 6f6564ea4c5fc118757c945b6359a36aaea86216 - React-CoreModules: ab635016811b610a93e873485f6f900ce0582192 - React-cxxreact: f82f0f1832606fabb9e8c9d61c4230704a3d2d2f - React-debug: 8aa2bd54b0f0011049300ce3339b0e51254ef3b5 - React-hermes: f076cb5f7351d6cc1600125bef3259ea880460fb - React-jsi: 9f381c8594161b2328b93cd3ba5d0bcfcd1e093a - React-jsiexecutor: 184eae1ecdedc7a083194bd9ff809c93f08fd34c - React-jsinspector: d0b5bfd1085599265f4212034321e829bdf83cc0 - React-logger: b8103c9b04e707b50cdd2b1aeb382483900cbb37 + RCTRequired: 28469809442eb4eb5528462705f7d852948c8a74 + RCTTypeSafety: e9c6c409fca2cc584e5b086862d562540cb38d29 + React: 769f469909b18edfe934f0539fffb319c4c61043 + React-callinvoker: e48ce12c83706401251921896576710d81e54763 + React-Codegen: a136b8094d39fd071994eaa935366e6be2239cb1 + React-Core: e548a186fb01c3a78a9aeeffa212d625ca9511bf + React-CoreModules: d226b22d06ea1bc4e49d3c073b2c6cbb42265405 + React-cxxreact: 44a3560510ead6633b6e02f9fbbdd1772fb40f92 + React-debug: 238501490155574ae9f3f8dd1c74330eba30133e + React-hermes: 46e66dc854124d7645c20bfec0a6be9542826ecd + React-jsi: fbdaf4166bae60524b591b18c851b530c8cdb90c + React-jsiexecutor: 3bf18ff7cb03cd8dfdce08fbbc0d15058c1d71ae + React-jsinspector: 194e32c6aab382d88713ad3dd0025c5f5c4ee072 + React-logger: cebf22b6cf43434e471dc561e5911b40ac01d289 react-native-charts-wrapper: 4461b72f48e737a490e968bf2468cc0fd2281681 react-native-safe-area-context: 36cc67648134e89465663b8172336a19eeda493d - React-NativeModulesApple: 4f31a812364443cee6ef768d256c594ad3b20f53 - React-perflogger: 3d501f34c8d4b10cb75f348e43591765788525ad - React-RCTActionSheet: f5335572c979198c0c3daff67b07bd1ad8370c1d - React-RCTAnimation: 5d0d31a4f9c49a70f93f32e4da098fb49b5ae0b3 - React-RCTAppDelegate: 01ddbdeb01b7cefa932cb66a17299d60620e820d - React-RCTBlob: 280d2605ba10b8f2282f1e8a849584368577251a - React-RCTImage: e15d22db53406401cdd1407ce51080a66a9c7ed4 - React-RCTLinking: 39815800ec79d6fb15e6329244d195ebeabf7541 - React-RCTNetwork: 2a6548e13d2577b112d4250ac5be74ae62e1e86b - React-RCTSettings: a76aee44d5398144646be146c334b15c90ad9582 - React-RCTText: afad390f3838f210c2bc9e1a19bb048003b2a771 - React-RCTVibration: 29bbaa5c57c02dc036d7e557584b492000b1d3e7 - React-rncore: 50966ce412d63bee9ffe5c98249857c23870a3c4 - React-runtimeexecutor: d129f2b53d61298093d7c7d8ebfafa664f911ce6 - React-runtimescheduler: 67707a955b9ecc628cc38bdc721fbc498910f0fd - React-utils: 0a70ea97d4e2749f336b450c082905be1d389435 - ReactCommon: e593d19c9e271a6da4d0bd7f13b28cfeae5d164b + React-NativeModulesApple: 02e35e9a51e10c6422f04f5e4076a7c02243fff2 + React-perflogger: e3596db7e753f51766bceadc061936ef1472edc3 + React-RCTActionSheet: 17ab132c748b4471012abbcdcf5befe860660485 + React-RCTAnimation: c8bbaab62be5817d2a31c36d5f2571e3f7dcf099 + React-RCTAppDelegate: af1c7dace233deba4b933cd1d6491fe4e3584ad1 + React-RCTBlob: 1bcf3a0341eb8d6950009b1ddb8aefaf46996b8c + React-RCTImage: 670a3486b532292649b1aef3ffddd0b495a5cee4 + React-RCTLinking: bd7ab853144aed463903237e615fd91d11b4f659 + React-RCTNetwork: be86a621f3e4724758f23ad1fdce32474ab3d829 + React-RCTSettings: 4f3a29a6d23ffa639db9701bc29af43f30781058 + React-RCTText: adde32164a243103aaba0b1dc7b0a2599733873e + React-RCTVibration: 6bd85328388ac2e82ae0ca11afe48ad5555b483a + React-rncore: fda7b1ae5918fa7baa259105298a5487875a57c8 + React-runtimeexecutor: 57d85d942862b08f6d15441a0badff2542fd233c + React-runtimescheduler: f23e337008403341177fc52ee4ca94e442c17ede + React-utils: fa59c9a3375fb6f4aeb66714fd3f7f76b43a9f16 + ReactCommon: dd03c17275c200496f346af93a7b94c53f3093a4 RNCAsyncStorage: ddc4ee162bfd41b0d2c68bf2d95acd81dd7f1f93 - RNGestureHandler: dec4645026e7401a0899f2846d864403478ff6a5 + RNGestureHandler: 32a01c29ecc9bb0b5bf7bc0a33547f61b4dc2741 RNReanimated: 9f7068e43b9358a46a688d94a5a3adb258139457 RNScreens: 50ffe2fa2342eabb2d0afbe19f7c1af286bc7fb3 - RNSentry: 4fb2cd7d2d6cb94423c24884488206ef881da136 - Sentry: 11776f6a25a128808d793d0d41bb7ad873b5ae4f - SentryPrivate: b3c448eacdabe9eab7679a2e0af609c608f91572 + RNSentry: 11917f7bf3e28806aca4c2791c6ba7522d8f09fe + Sentry: 39d57e691e311bdb73bc1ab5bbebbd6bc890050d + SentryPrivate: 48712023cdfd523735c2edb6b06bedf26c4730a3 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 SwiftAlgorithms: 38dda4731d19027fdeee1125f973111bf3386b53 SwiftLogJellyfish: 6207ec91ef3913d303ac797b73298248ee360eb0 SwiftPhoenixClient: 4e36f35d00e43d11881c255bd60c20efc0f39c91 - SwiftProtobuf: bcfd2bc231cf9ae552cdc7c4e877bd3b41fe57b1 + SwiftProtobuf: 69f02cd54fb03201c5e6bf8b76f687c5ef7541a3 SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 WebRTC-SDK: 18eef1ab50d4c0239adc1cbca33d4ffd9fab6639 - Yoga: 65286bb6a07edce5e4fe8c90774da977ae8fc009 + Yoga: b76f1acfda8212aa16b7e26bcce3983230c82603 PODFILE CHECKSUM: 572236a5087de7862dd62f90e6f7bcebcc061765 diff --git a/example/ios/reactnativemembranewebrtcexample.xcodeproj/project.pbxproj b/example/ios/reactnativemembranewebrtcexample.xcodeproj/project.pbxproj index 21913519..b8e7b75d 100644 --- a/example/ios/reactnativemembranewebrtcexample.xcodeproj/project.pbxproj +++ b/example/ios/reactnativemembranewebrtcexample.xcodeproj/project.pbxproj @@ -402,11 +402,13 @@ "${PODS_ROOT}/Target Support Files/Pods-reactnativemembranewebrtcexample/Pods-reactnativemembranewebrtcexample-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", + "${PODS_ROOT}/Sentry/Sources/Resources/PrivacyInfo.xcprivacy", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PrivacyInfo.xcprivacy", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -665,6 +667,7 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", + _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION, ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -680,6 +683,11 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_CPLUSPLUSFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; }; @@ -720,6 +728,10 @@ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION, + ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -732,6 +744,11 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_CPLUSPLUSFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/ios/Events.swift b/ios/Events.swift new file mode 100644 index 00000000..a8f0f62c --- /dev/null +++ b/ios/Events.swift @@ -0,0 +1,10 @@ +struct EmitableEvents { + static let IsCameraOn = "IsCameraOn" + static let IsMicrophoneOn = "IsMicrophoneOn" + static let IsScreencastOn = "IsScreencastOn" + static let SimulcastConfigUpdate = "SimulcastConfigUpdate" + static let EndpointsUpdate = "EndpointsUpdate" + static let AudioDeviceUpdate = "AudioDeviceUpdate" + static let SendMediaEvent = "SendMediaEvent" + static let BandwidthEstimation = "BandwidthEstimation" +} diff --git a/ios/MembraneWebRTC.swift b/ios/MembraneWebRTC.swift index 5e8766c4..2884829c 100644 --- a/ios/MembraneWebRTC.swift +++ b/ios/MembraneWebRTC.swift @@ -1,883 +1,967 @@ -import os.log +import AVKit +import ExpoModulesCore import MembraneRTC import React import ReplayKit -import AVKit import WebRTC -import ExpoModulesCore +import os.log let log = OSLog(subsystem: "com.swm.membranewebrtc", category: "ErrorHandling") #if os(iOS) -@available(iOS 12, *) -public extension RPSystemBroadcastPickerView { - static func show(for preferredExtension: String? = nil, showsMicrophoneButton: Bool = false) { - let view = RPSystemBroadcastPickerView() - view.preferredExtension = preferredExtension - view.showsMicrophoneButton = showsMicrophoneButton - let selector = NSSelectorFromString("buttonPressed:") - if view.responds(to: selector) { - view.perform(selector, with: nil) - } - } -} + @available(iOS 12, *) + extension RPSystemBroadcastPickerView { + public static func show( + for preferredExtension: String? = nil, showsMicrophoneButton: Bool = false + ) { + let view = RPSystemBroadcastPickerView() + view.preferredExtension = preferredExtension + view.showsMicrophoneButton = showsMicrophoneButton + let selector = NSSelectorFromString("buttonPressed:") + if view.responds(to: selector) { + view.perform(selector, with: nil) + } + } + } #endif -public extension [String: Any] { - func toMetadata() -> Metadata { - var res: Metadata = .init() - self.forEach { entry in - res[entry.key] = entry.value - } - return res - } +extension [String: Any] { + public func toMetadata() -> Metadata { + var res: Metadata = .init() + self.forEach { entry in + res[entry.key] = entry.value + } + return res + } } -public extension AnyJson { - func toDict() -> [String: Any] { - var res: [String: Any] = [:] - self.keys.forEach { key in - res[key] = self[key] - } - return res - } +extension AnyJson { + public func toDict() -> [String: Any] { + var res: [String: Any] = [:] + self.keys.forEach { key in + res[key] = self[key] + } + return res + } } extension String: Error {} -public extension String { - func toTrackEncoding() -> TrackEncoding? { - switch(self) { - case "l": - return TrackEncoding.l - case "m": - return TrackEncoding.m - case "h": - return TrackEncoding.h - default: - return nil - } - } +extension String { + public func toTrackEncoding() -> TrackEncoding? { + switch self { + case "l": + return TrackEncoding.l + case "m": + return TrackEncoding.m + case "h": + return TrackEncoding.h + default: + return nil + } + } } class MembraneWebRTC: MembraneRTCDelegate { - var membraneRTC: MembraneRTC? = nil; - - var localAudioTrack: LocalAudioTrack? - var localVideoTrack: LocalVideoTrack? - var localScreencastTrack: LocalScreenBroadcastTrack? - var localEndpointId: String? - - - - var isMicEnabled: Bool = true - var isCameraEnabled: Bool = true - var isScreensharingEnabled: Bool = false - - var globalToLocalTrackId: [String:String] = [:] - - var connectPromise: Promise? = nil - - var videoSimulcastConfig: SimulcastConfig = SimulcastConfig() - var localUserMetadata: Metadata = .init() - - var screencastSimulcastConfig: SimulcastConfig = SimulcastConfig() - var screencastMaxBandwidth: TrackBandwidthLimit = .BandwidthLimit(0) - - var tracksContexts: [String: TrackContext] = [:] - - var captureDeviceId: String? = nil - - var audioSessionMode: AVAudioSession.Mode = AVAudioSession.Mode.videoChat - var errorMessage: String? - - let sendEvent: (_ eventName: String, _ data: [String: Any]) -> Void - - init(sendEvent: @escaping (_ eventName: String, _ data: [String: Any]) -> Void) { - self.sendEvent = sendEvent - NotificationCenter.default.addObserver( - self, - selector: #selector(onRouteChangeNotification), - name: AVAudioSession.routeChangeNotification, - object: nil - ) - } - - - private func getSimulcastConfigFromOptions(simulcastConfig: RNSimulcastConfig) throws -> SimulcastConfig? { - var activeEncodings: [TrackEncoding] = [] - try simulcastConfig.activeEncodings.forEach({ e in - activeEncodings.append(try validateEncoding(encoding: e)) - }) - return SimulcastConfig( - enabled: simulcastConfig.enabled, - activeEncodings: activeEncodings - ) - } - - private func getMaxBandwidthFromOptions(maxBandwidth: RNTrackBandwidthLimit) -> TrackBandwidthLimit { - if let maxBandwidth: Int = maxBandwidth.get() { - return .BandwidthLimit(maxBandwidth) - } else if let maxBandwidth: [String: Int] = maxBandwidth.get() { - return .SimulcastBandwidthLimit(maxBandwidth) - } - return .BandwidthLimit(0) - } - private func getGlobalTrackId(localTrackId: String) -> String? { - return globalToLocalTrackId.filter { $0.value == localTrackId }.first?.key - } - - - - private func validateEncoding(encoding: String) throws -> TrackEncoding { - let trackEncoding = encoding.toTrackEncoding() - guard let trackEncoding = trackEncoding else { - throw Exception(name: "E_INVALID_ENCODING", description: "Invalid track encoding specified: \(encoding)") - } - return trackEncoding - } - private func initLocalEndpoint(){ - let uuid = UUID().uuidString - self.localEndpointId = uuid - let endpoint = RNEndpoint( - id: uuid, - metadata: localUserMetadata, - type: "webrtc" - ) - MembraneRoom.sharedInstance.endpoints[uuid] = endpoint - emitEndpoints() - } - - func create() throws -> Void { - self.membraneRTC = MembraneRTC.create(delegate: self) - try ensureCreated() - initLocalEndpoint() - } - - func getVideoParametersFromOptions(connectionOptions: CameraConfig) -> VideoParameters { - let videoQuality = connectionOptions.quality - let flipVideo = connectionOptions.flipVideo - let videoBandwidthLimit = getMaxBandwidthFromOptions(maxBandwidth: connectionOptions.maxBandwidth) - - let preset: VideoParameters = { - switch videoQuality { - case "QVGA169": - return VideoParameters.presetQVGA169 - case "VGA169": - return VideoParameters.presetVGA169 - case "VQHD169": - return VideoParameters.presetQHD169 - case "HD169": - return VideoParameters.presetHD169 - case "FHD169": - return VideoParameters.presetFHD169 - case "QVGA43": - return VideoParameters.presetQVGA43 - case "VGA43": - return VideoParameters.presetVGA43 - case "VQHD43": - return VideoParameters.presetQHD43 - case "HD43": - return VideoParameters.presetHD43 - case "FHD43": - return VideoParameters.presetFHD43 - default: - return VideoParameters.presetVGA169 - } - }() - let videoParameters = VideoParameters( - dimensions: flipVideo ? preset.dimensions.flip() : preset.dimensions, - maxBandwidth: videoBandwidthLimit, - simulcastConfig: self.videoSimulcastConfig - ) - return videoParameters - } - private func ensureCreated() throws{ - if(membraneRTC == nil) { - throw Exception(name: "E_NO_MEMBRANERTC", description: "Client not created yet. Make sure to call create() first!") - } - } - private func ensureConnected() throws { - if(membraneRTC == nil) { - throw Exception(name: "E_NOT_CONNECTED", description: "Client not connected to server yet. Make sure to call connect() first!") - } - } - - private func ensureVideoTrack() throws { - if(membraneRTC == nil) { - throw Exception(name: "E_NO_LOCAL_VIDEO_TRACK", description: "No local video track. Make sure to call connect() first!") - } - } - - private func ensureAudioTrack() throws { - if(membraneRTC == nil) { - throw Exception(name: "E_NO_LOCAL_AUDIO_TRACK", description: "No local audio track. Make sure to call connect() first!") - } - } - - private func ensureScreencastTrack() throws { - if(membraneRTC == nil) { - throw Exception(name: "E_NO_LOCAL_SCREENCAST_TRACK", description: "No local screencast track. Make sure to toggle screencast on first!") - } - } - private func ensureEndpoints() throws { - guard let _ = localEndpointId, !MembraneRoom.sharedInstance.endpoints.isEmpty else { - throw Exception(name:"E_NO_ENDPOINTS", description: "No endpoints available. Ensure the connection is established or endpoints are present.") - } - } - - - func receiveMediaEvent(data: String) throws -> Void { - try ensureConnected() - membraneRTC?.receiveMediaEvent(mediaEvent: data as SerializedMediaEvent) - } - - - func connect(metadata: [String: Any], promise: Promise) { - connectPromise = promise - localUserMetadata = metadata.toMetadata() - - guard let localEndpointId = localEndpointId, - var endpoint = MembraneRoom.sharedInstance.endpoints[localEndpointId] else { - return - } - - endpoint.metadata = metadata.toMetadata() - membraneRTC?.connect(metadata: metadata.toMetadata()) - } - - func disconnect() -> Void { - if isScreensharingEnabled { - let screencastExtensionBundleId = Bundle.main.infoDictionary?["ScreencastExtensionBundleId"] as? String - DispatchQueue.main.async { - RPSystemBroadcastPickerView.show(for: screencastExtensionBundleId) - } - } - membraneRTC?.remove(delegate: self) - membraneRTC?.disconnect() - membraneRTC = nil - MembraneRoom.sharedInstance.endpoints = [:] - } - - func startCamera(config: CameraConfig) throws -> Void { - try ensureConnected() - guard let cameraTrack = try createCameraTrack(config: config) else { return } - localVideoTrack = cameraTrack - try addTrackToLocalEndpoint(cameraTrack,config.videoTrackMetadata.toMetadata()) - try setCameraTrackState(cameraTrack: cameraTrack, isEnabled: config.cameraEnabled) - } - private func createCameraTrack(config: CameraConfig) throws -> LocalVideoTrack? { - try ensureConnected() - let videoParameters = getVideoParametersFromOptions(connectionOptions: config) - guard let simulcastConfig = try getSimulcastConfigFromOptions(simulcastConfig: config.simulcastConfig) else { return nil } - self.videoSimulcastConfig = simulcastConfig - return membraneRTC?.createVideoTrack( - videoParameters: videoParameters, metadata: config.videoTrackMetadata.toMetadata(), captureDeviceId: config.captureDeviceId - ) - } - private func setCameraTrackState(cameraTrack: LocalVideoTrack, isEnabled: Bool) throws { - try ensureConnected() - cameraTrack.setEnabled(isEnabled) - isCameraEnabled = isEnabled - let isCameraEnabledMap = ["IsCameraOn": isEnabled] - emitEvent(name: "IsCameraOn", data: isCameraEnabledMap) - } - private func addTrackToLocalEndpoint(_ track: LocalVideoTrack, _ metadata: Metadata) throws { - try ensureEndpoints() - if let localEndpointId = localEndpointId { - MembraneRoom.sharedInstance.endpoints[localEndpointId]?.videoTracks = [track.trackId(): track] - MembraneRoom.sharedInstance.endpoints[localEndpointId]?.tracksMetadata[track.trackId()] = metadata - emitEndpoints() - } + var membraneRTC: MembraneRTC? = nil + + var localAudioTrack: LocalAudioTrack? + var localVideoTrack: LocalVideoTrack? + var localScreencastTrack: LocalScreenBroadcastTrack? + var localEndpointId: String? + + var isMicEnabled: Bool = true + var isCameraEnabled: Bool = true + var isScreensharingEnabled: Bool = false + + var globalToLocalTrackId: [String: String] = [:] + + var connectPromise: Promise? = nil + + var videoSimulcastConfig: SimulcastConfig = SimulcastConfig() + var localUserMetadata: Metadata = .init() + + var screencastSimulcastConfig: SimulcastConfig = SimulcastConfig() + var screencastMaxBandwidth: TrackBandwidthLimit = .BandwidthLimit(0) + + var tracksContexts: [String: TrackContext] = [:] + + var captureDeviceId: String? = nil + + var audioSessionMode: AVAudioSession.Mode = AVAudioSession.Mode.videoChat + var errorMessage: String? + + let sendEvent: (_ eventName: String, _ data: [String: Any]) -> Void + + init(sendEvent: @escaping (_ eventName: String, _ data: [String: Any]) -> Void) { + self.sendEvent = sendEvent + NotificationCenter.default.addObserver( + self, + selector: #selector(onRouteChangeNotification), + name: AVAudioSession.routeChangeNotification, + object: nil + ) + } + + private func getSimulcastConfigFromOptions(simulcastConfig: RNSimulcastConfig) throws + -> SimulcastConfig? + { + var activeEncodings: [TrackEncoding] = [] + try simulcastConfig.activeEncodings.forEach({ e in + activeEncodings.append(try validateEncoding(encoding: e)) + }) + return SimulcastConfig( + enabled: simulcastConfig.enabled, + activeEncodings: activeEncodings + ) + } + + private func getMaxBandwidthFromOptions(maxBandwidth: RNTrackBandwidthLimit) + -> TrackBandwidthLimit + { + if let maxBandwidth: Int = maxBandwidth.get() { + return .BandwidthLimit(maxBandwidth) + } else if let maxBandwidth: [String: Int] = maxBandwidth.get() { + return .SimulcastBandwidthLimit(maxBandwidth) + } + return .BandwidthLimit(0) + } + private func getGlobalTrackId(localTrackId: String) -> String? { + return globalToLocalTrackId.filter { $0.value == localTrackId }.first?.key + } + + private func validateEncoding(encoding: String) throws -> TrackEncoding { + let trackEncoding = encoding.toTrackEncoding() + guard let trackEncoding = trackEncoding else { + throw Exception( + name: "E_INVALID_ENCODING", description: "Invalid track encoding specified: \(encoding)") + } + return trackEncoding + } + private func initLocalEndpoint() { + let uuid = UUID().uuidString + self.localEndpointId = uuid + let endpoint = RNEndpoint( + id: uuid, + metadata: localUserMetadata, + type: "webrtc" + ) + MembraneRoom.sharedInstance.endpoints[uuid] = endpoint + emitEndpoints() + } + + func create() throws { + self.membraneRTC = MembraneRTC.create(delegate: self) + try ensureCreated() + initLocalEndpoint() + } + + func getVideoParametersFromOptions(connectionOptions: CameraConfig) -> VideoParameters { + let videoQuality = connectionOptions.quality + let flipVideo = connectionOptions.flipVideo + let videoBandwidthLimit = getMaxBandwidthFromOptions( + maxBandwidth: connectionOptions.maxBandwidth) + + let preset: VideoParameters = { + switch videoQuality { + case "QVGA169": + return VideoParameters.presetQVGA169 + case "VGA169": + return VideoParameters.presetVGA169 + case "VQHD169": + return VideoParameters.presetQHD169 + case "HD169": + return VideoParameters.presetHD169 + case "FHD169": + return VideoParameters.presetFHD169 + case "QVGA43": + return VideoParameters.presetQVGA43 + case "VGA43": + return VideoParameters.presetVGA43 + case "VQHD43": + return VideoParameters.presetQHD43 + case "HD43": + return VideoParameters.presetHD43 + case "FHD43": + return VideoParameters.presetFHD43 + default: + return VideoParameters.presetVGA169 + } + }() + let videoParameters = VideoParameters( + dimensions: flipVideo ? preset.dimensions.flip() : preset.dimensions, + maxBandwidth: videoBandwidthLimit, + simulcastConfig: self.videoSimulcastConfig + ) + return videoParameters + } + private func ensureCreated() throws { + if membraneRTC == nil { + throw Exception( + name: "E_NO_MEMBRANERTC", + description: "Client not created yet. Make sure to call create() first!") + } + } + private func ensureConnected() throws { + if membraneRTC == nil { + throw Exception( + name: "E_NOT_CONNECTED", + description: "Client not connected to server yet. Make sure to call connect() first!") + } + } + + private func ensureVideoTrack() throws { + if membraneRTC == nil { + throw Exception( + name: "E_NO_LOCAL_VIDEO_TRACK", + description: "No local video track. Make sure to call connect() first!") } - - func toggleCamera() throws -> Bool{ - try ensureVideoTrack() - if let localVideoTrack = self.localVideoTrack { - try setCameraTrackState(cameraTrack: localVideoTrack, isEnabled: !isCameraEnabled) - } - return isCameraEnabled + } + + private func ensureAudioTrack() throws { + if membraneRTC == nil { + throw Exception( + name: "E_NO_LOCAL_AUDIO_TRACK", + description: "No local audio track. Make sure to call connect() first!") } - - func flipCamera() throws { - try ensureVideoTrack() - if let cameraTrack = localVideoTrack as? LocalCameraVideoTrack { - cameraTrack.switchCamera() - } + } + + private func ensureScreencastTrack() throws { + if membraneRTC == nil { + throw Exception( + name: "E_NO_LOCAL_SCREENCAST_TRACK", + description: "No local screencast track. Make sure to toggle screencast on first!") + } + } + private func ensureEndpoints() throws { + guard let _ = localEndpointId, !MembraneRoom.sharedInstance.endpoints.isEmpty else { + throw Exception( + name: "E_NO_ENDPOINTS", + description: + "No endpoints available. Ensure the connection is established or endpoints are present.") + } + } + + func receiveMediaEvent(data: String) throws { + try ensureConnected() + membraneRTC?.receiveMediaEvent(mediaEvent: data as SerializedMediaEvent) + } + + func connect(metadata: [String: Any], promise: Promise) { + connectPromise = promise + localUserMetadata = metadata.toMetadata() + + guard let localEndpointId = localEndpointId, + var endpoint = MembraneRoom.sharedInstance.endpoints[localEndpointId] + else { + return } - - func switchCamera(captureDeviceId: String) throws { - try ensureVideoTrack() - if let cameraTrack = localVideoTrack as? LocalCameraVideoTrack { - cameraTrack.switchCamera(deviceId: captureDeviceId as String) - } + + endpoint.metadata = metadata.toMetadata() + membraneRTC?.connect(metadata: metadata.toMetadata()) + } + + func disconnect() { + if isScreensharingEnabled { + let screencastExtensionBundleId = + Bundle.main.infoDictionary?["ScreencastExtensionBundleId"] as? String + DispatchQueue.main.async { + RPSystemBroadcastPickerView.show(for: screencastExtensionBundleId) + } + } + membraneRTC?.remove(delegate: self) + membraneRTC?.disconnect() + membraneRTC = nil + MembraneRoom.sharedInstance.endpoints = [:] + } + + func startCamera(config: CameraConfig) throws { + try ensureConnected() + guard let cameraTrack = try createCameraTrack(config: config) else { return } + localVideoTrack = cameraTrack + try addTrackToLocalEndpoint(cameraTrack, config.videoTrackMetadata.toMetadata()) + try setCameraTrackState(cameraTrack: cameraTrack, isEnabled: config.cameraEnabled) + } + private func createCameraTrack(config: CameraConfig) throws -> LocalVideoTrack? { + try ensureConnected() + let videoParameters = getVideoParametersFromOptions(connectionOptions: config) + guard + let simulcastConfig = try getSimulcastConfigFromOptions( + simulcastConfig: config.simulcastConfig) + else { return nil } + self.videoSimulcastConfig = simulcastConfig + return membraneRTC?.createVideoTrack( + videoParameters: videoParameters, metadata: config.videoTrackMetadata.toMetadata(), + captureDeviceId: config.captureDeviceId + ) + } + private func setCameraTrackState(cameraTrack: LocalVideoTrack, isEnabled: Bool) throws { + try ensureConnected() + cameraTrack.setEnabled(isEnabled) + isCameraEnabled = isEnabled + let eventName = EmitableEvents.IsCameraOn + let isCameraEnabledMap = [eventName: isEnabled] + emitEvent(name: eventName, data: isCameraEnabledMap) + } + private func addTrackToLocalEndpoint(_ track: LocalVideoTrack, _ metadata: Metadata) throws { + try ensureEndpoints() + if let localEndpointId = localEndpointId { + MembraneRoom.sharedInstance.endpoints[localEndpointId]?.videoTracks = [track.trackId(): track] + MembraneRoom.sharedInstance.endpoints[localEndpointId]?.tracksMetadata[track.trackId()] = + metadata + emitEndpoints() + } + } + + func toggleCamera() throws -> Bool { + try ensureVideoTrack() + if let localVideoTrack = self.localVideoTrack { + try setCameraTrackState(cameraTrack: localVideoTrack, isEnabled: !isCameraEnabled) } - private func addTrackToLocalEndpoint(_ track: LocalAudioTrack, _ metadata: Metadata) throws{ - try ensureEndpoints() - if let localEndpointId = localEndpointId { - MembraneRoom.sharedInstance.endpoints[localEndpointId]?.audioTracks = [track.trackId(): track] - MembraneRoom.sharedInstance.endpoints[localEndpointId]?.tracksMetadata[track.trackId()] = metadata - emitEndpoints() - } + return isCameraEnabled + } + + func flipCamera() throws { + try ensureVideoTrack() + if let cameraTrack = localVideoTrack as? LocalCameraVideoTrack { + cameraTrack.switchCamera() } - private func removeTrackFromLocalEndpoint(_ track: LocalVideoTrack) throws { - try ensureEndpoints() - if let localEndpointId = localEndpointId { - guard let localEndpoint = MembraneRoom.sharedInstance.endpoints[localEndpointId] else { return } - MembraneRoom.sharedInstance.endpoints[localEndpointId] = localEndpoint.removeTrack(trackId: track.trackId()) - emitEndpoints() - } + } + + func switchCamera(captureDeviceId: String) throws { + try ensureVideoTrack() + if let cameraTrack = localVideoTrack as? LocalCameraVideoTrack { + cameraTrack.switchCamera(deviceId: captureDeviceId as String) + } + } + private func addTrackToLocalEndpoint(_ track: LocalAudioTrack, _ metadata: Metadata) throws { + try ensureEndpoints() + if let localEndpointId = localEndpointId { + MembraneRoom.sharedInstance.endpoints[localEndpointId]?.audioTracks = [track.trackId(): track] + MembraneRoom.sharedInstance.endpoints[localEndpointId]?.tracksMetadata[track.trackId()] = + metadata + emitEndpoints() + } + } + private func removeTrackFromLocalEndpoint(_ track: LocalVideoTrack) throws { + try ensureEndpoints() + if let localEndpointId = localEndpointId { + guard let localEndpoint = MembraneRoom.sharedInstance.endpoints[localEndpointId] else { + return + } + MembraneRoom.sharedInstance.endpoints[localEndpointId] = localEndpoint.removeTrack( + trackId: track.trackId()) + emitEndpoints() + } + } + func startMicrophone(config: MicrophoneConfig) throws { + try ensureConnected() + guard + let microphoneTrack = membraneRTC?.createAudioTrack( + metadata: config.audioTrackMetadata.toMetadata()) + else { return } + localAudioTrack = microphoneTrack + setAudioSessionMode() + try addTrackToLocalEndpoint(microphoneTrack, config.audioTrackMetadata.toMetadata()) + try setMicrophoneTrackState(microphoneTrack, config.microphoneEnabled) + } + private func setMicrophoneTrackState(_ microphoneTrack: LocalAudioTrack, _ isEnabled: Bool) throws + { + try ensureConnected() + microphoneTrack.setEnabled(isEnabled) + isMicEnabled = isEnabled + let eventName = EmitableEvents.IsMicrophoneOn + let isMicEnabledMap = [eventName: isEnabled] + emitEvent(name: eventName, data: isMicEnabledMap) + } + func toggleMicrophone() throws -> Bool { + try ensureAudioTrack() + if let localAudioTrack = localAudioTrack { + try setMicrophoneTrackState(localAudioTrack, !isMicEnabled) + } + return isMicEnabled + } + private func getScreencastVideoParameters(screencastOptions: ScreencastOptions) -> VideoParameters + { + let preset: VideoParameters + switch screencastOptions.quality { + case "VGA": + preset = VideoParameters.presetScreenShareVGA + case "HD5": + preset = VideoParameters.presetScreenShareHD5 + case "HD15": + preset = VideoParameters.presetScreenShareHD15 + case "FHD15": + preset = VideoParameters.presetScreenShareFHD15 + case "FHD30": + preset = VideoParameters.presetScreenShareFHD30 + default: + preset = VideoParameters.presetScreenShareHD15 + } + return VideoParameters( + dimensions: preset.dimensions.flip(), + maxBandwidth: screencastMaxBandwidth, + maxFps: preset.maxFps, + simulcastConfig: screencastSimulcastConfig + ) + } + private func setScreencastTrackState(isEnabled: Bool) throws { + isScreensharingEnabled = isEnabled + let eventName = EmitableEvents.IsScreencastOn + let isScreencastEnabled = [eventName: isEnabled] + emitEvent(name: eventName, data: isScreencastEnabled) + } + func toggleScreencast(screencastOptions: ScreencastOptions) throws { + try ensureConnected() + guard + let screencastExtensionBundleId = Bundle.main.infoDictionary?["ScreencastExtensionBundleId"] + as? String + else { + throw Exception( + name: "E_NO_BUNDLE_ID_SET", + description: + "No screencast extension bundle id set. Please set ScreencastExtensionBundleId in Info.plist" + ) + } + guard let appGroupName = Bundle.main.infoDictionary?["AppGroupName"] as? String else { + throw Exception( + name: "E_NO_APP_GROUP_SET", + description: "No app group name set. Please set AppGroupName in Info.plist") } - private func removeTrackFromLocalEndpoint(_ track: LocalAudioTrack){} - func startMicrophone(config: MicrophoneConfig) throws -> Void { - try ensureConnected() - guard let microphoneTrack = membraneRTC?.createAudioTrack(metadata: config.audioTrackMetadata.toMetadata()) else { return } - localAudioTrack = microphoneTrack - setAudioSessionMode() - try addTrackToLocalEndpoint(microphoneTrack, config.audioTrackMetadata.toMetadata()) - try setMicrophoneTrackState(microphoneTrack,config.microphoneEnabled) - } - private func setMicrophoneTrackState(_ microphoneTrack: LocalAudioTrack, _ isEnabled: Bool) throws { - try ensureConnected() - microphoneTrack.setEnabled(isEnabled) - isMicEnabled = isEnabled - let isMicEnabledMap = ["IsMicrophoneOn" : isEnabled] - emitEvent(name: "IsMicrophoneOn", data: isMicEnabledMap) - } - func toggleMicrophone() throws -> Bool { - try ensureAudioTrack() - if let localAudioTrack = localAudioTrack { - try setMicrophoneTrackState(localAudioTrack, !isMicEnabled) - } - return isMicEnabled - } - private func getScreencastVideoParameters(screencastOptions: ScreencastOptions) -> VideoParameters { - let preset: VideoParameters - switch screencastOptions.quality { - case "VGA": - preset = VideoParameters.presetScreenShareVGA - case "HD5": - preset = VideoParameters.presetScreenShareHD5 - case "HD15": - preset = VideoParameters.presetScreenShareHD15 - case "FHD15": - preset = VideoParameters.presetScreenShareFHD15 - case "FHD30": - preset = VideoParameters.presetScreenShareFHD30 - default: - preset = VideoParameters.presetScreenShareHD15 - } - return VideoParameters( - dimensions: preset.dimensions.flip(), - maxBandwidth: screencastMaxBandwidth, - maxFps: preset.maxFps, - simulcastConfig: screencastSimulcastConfig - ) - } - private func setScreencastTrackState(isEnabled:Bool) throws { - isScreensharingEnabled = isEnabled - let isScreencastEnabled = ["IsScreencastOn": isEnabled] - emitEvent(name: "IsScreencastOn", data: isScreencastEnabled) - } - func toggleScreencast(screencastOptions: ScreencastOptions) throws -> Void { - try ensureConnected() - guard let screencastExtensionBundleId = Bundle.main.infoDictionary?["ScreencastExtensionBundleId"] as? String else { - throw Exception(name: "E_NO_BUNDLE_ID_SET", description: "No screencast extension bundle id set. Please set ScreencastExtensionBundleId in Info.plist") - } - guard let appGroupName = Bundle.main.infoDictionary?["AppGroupName"] as? String else { - throw Exception(name: "E_NO_APP_GROUP_SET", description: "No app group name set. Please set AppGroupName in Info.plist") - } - // if screensharing is enabled it must be closed by the Broadcast Extension, not by our application - // the only thing we can do is to display stop recording button, which we already do - guard isScreensharingEnabled == false else { - DispatchQueue.main.async { - RPSystemBroadcastPickerView.show(for: screencastExtensionBundleId) - } - return - } - guard let simulcastConfig = try getSimulcastConfigFromOptions(simulcastConfig: screencastOptions.simulcastConfig) else { - return - } - screencastSimulcastConfig = simulcastConfig - screencastMaxBandwidth = getMaxBandwidthFromOptions(maxBandwidth: screencastOptions.maxBandwidth) - let screencastMetadata = screencastOptions.screencastMetadata.toMetadata() - let videoParameters = getScreencastVideoParameters(screencastOptions: screencastOptions) - localScreencastTrack = membraneRTC?.createScreencastTrack( - appGroup: appGroupName, - videoParameters: videoParameters, - metadata: screencastMetadata, - onStart: { [weak self] screencastTrack in - guard let self = self else { - DispatchQueue.main.async { - RPSystemBroadcastPickerView.show(for: screencastExtensionBundleId) - } - return - } - do { - guard let screencastTrack = localScreencastTrack else {return } - try addTrackToLocalEndpoint(screencastTrack, screencastMetadata) - try setScreencastTrackState(isEnabled: true) - } - catch{ - os_log("Error starting screencast: %{public}s", log: log, type: .error, String(describing: error)) - } - - }, onStop: { [weak self] in - guard let self = self else { - return - } - do { - guard let screencastTrack = localScreencastTrack else {return } - try removeTrackFromLocalEndpoint(screencastTrack) - localScreencastTrack = nil - try setScreencastTrackState(isEnabled: false) - } - catch{ - os_log("Error stopping screencast: %{public}s", log: log, type: .error, String(describing: error)) - } - }) - DispatchQueue.main.async { + + guard isScreensharingEnabled == false else { + DispatchQueue.main.async { + RPSystemBroadcastPickerView.show(for: screencastExtensionBundleId) + } + return + } + guard + let simulcastConfig = try getSimulcastConfigFromOptions( + simulcastConfig: screencastOptions.simulcastConfig) + else { + return + } + screencastSimulcastConfig = simulcastConfig + screencastMaxBandwidth = getMaxBandwidthFromOptions( + maxBandwidth: screencastOptions.maxBandwidth) + let screencastMetadata = screencastOptions.screencastMetadata.toMetadata() + let videoParameters = getScreencastVideoParameters(screencastOptions: screencastOptions) + localScreencastTrack = membraneRTC?.createScreencastTrack( + appGroup: appGroupName, + videoParameters: videoParameters, + metadata: screencastMetadata, + onStart: { [weak self] screencastTrack in + guard let self = self else { + DispatchQueue.main.async { RPSystemBroadcastPickerView.show(for: screencastExtensionBundleId) + } + return + } + do { + guard let screencastTrack = localScreencastTrack else { return } + try addTrackToLocalEndpoint(screencastTrack, screencastMetadata) + try setScreencastTrackState(isEnabled: true) + } catch { + os_log( + "Error starting screencast: %{public}s", log: log, type: .error, + String(describing: error)) } - } - - func getEndpoints() -> Array> { - MembraneRoom.sharedInstance.endpoints.values.sorted(by: {$0.order < $1.order}).map { - (p) -> Dictionary in - let videoTracks = p.videoTracks.keys.map { trackId in [ - "id": trackId, - "type": "Video", - "metadata": p.tracksMetadata[trackId]?.toDict() ?? [:], - "encoding": tracksContexts[trackId]?.encoding?.description as Any, - "encodingReason": tracksContexts[trackId]?.encodingReason?.rawValue as Any, - ]} - - let audioTracks = p.audioTracks.keys.map { trackId in [ - "id": trackId, - "type": "Audio", - "metadata": p.tracksMetadata[trackId]?.toDict() ?? [:], - "vadStatus": tracksContexts[trackId]?.vadStatus.rawValue as Any, - ]} - - return [ - "id": p.id, - "metadata": p.metadata.toDict(), - "tracks": videoTracks + audioTracks, - "isLocal": p.id == localEndpointId, - "type": p.type, - ] - } - } - - func getCaptureDevices() -> [[String: Any]] { - let devices = LocalCameraVideoTrack.getCaptureDevices() - var rnArray: [[String : Any]] = [] - devices.forEach { device in - rnArray.append([ - "id": device.uniqueID, - "name": device.localizedName, - "isFrontFacing": device.position == .front, - "isBackFacing": device.position == .back, - ]) - } - return rnArray - } - - func getSimulcastConfigAsRNMap(simulcastConfig: SimulcastConfig) -> [String: Any] { - return [ - "enabled": simulcastConfig.enabled, - "activeEncodings": simulcastConfig.activeEncodings.map { e in e.description }, + + }, + onStop: { [weak self] in + guard let self = self else { + return + } + do { + guard let screencastTrack = localScreencastTrack else { return } + try removeTrackFromLocalEndpoint(screencastTrack) + localScreencastTrack = nil + try setScreencastTrackState(isEnabled: false) + } catch { + os_log( + "Error stopping screencast: %{public}s", log: log, type: .error, + String(describing: error)) + } + }) + DispatchQueue.main.async { + RPSystemBroadcastPickerView.show(for: screencastExtensionBundleId) + } + } + + func getEndpoints() -> [[String: Any]] { + MembraneRoom.sharedInstance.endpoints.values.sorted(by: { $0.order < $1.order }).map { + (p) -> Dictionary in + let videoTracks = p.videoTracks.keys.map { trackId in + [ + "id": trackId, + "type": "Video", + "metadata": p.tracksMetadata[trackId]?.toDict() ?? [:], + "encoding": tracksContexts[trackId]?.encoding?.description as Any, + "encodingReason": tracksContexts[trackId]?.encodingReason?.rawValue as Any, ] + } + + let audioTracks = p.audioTracks.keys.map { trackId in + [ + "id": trackId, + "type": "Audio", + "metadata": p.tracksMetadata[trackId]?.toDict() ?? [:], + "vadStatus": tracksContexts[trackId]?.vadStatus.rawValue as Any, + ] + } + + return [ + "id": p.id, + "metadata": p.metadata.toDict(), + "tracks": videoTracks + audioTracks, + "isLocal": p.id == localEndpointId, + "type": p.type, + ] + } + } + + func getCaptureDevices() -> [[String: Any]] { + let devices = LocalCameraVideoTrack.getCaptureDevices() + var rnArray: [[String: Any]] = [] + devices.forEach { device in + rnArray.append([ + "id": device.uniqueID, + "name": device.localizedName, + "isFrontFacing": device.position == .front, + "isBackFacing": device.position == .back, + ]) + } + return rnArray + } + + func getSimulcastConfigAsRNMap(simulcastConfig: SimulcastConfig) -> [String: Any] { + return [ + "enabled": simulcastConfig.enabled, + "activeEncodings": simulcastConfig.activeEncodings.map { e in e.description }, + ] + } + + func updateEndpointMetadata(metadata: [String: Any]) throws { + try ensureConnected() + membraneRTC?.updateEndpointMetadata(metadata: metadata.toMetadata()) + } + + func updateTrackMetadata(trackId: String, metadata: [String: Any]) { + guard let room = membraneRTC, let endpointId = localEndpointId else { + return } - - - - func updateEndpointMetadata(metadata: [String: Any]) throws { - try ensureConnected() - membraneRTC?.updateEndpointMetadata(metadata: metadata.toMetadata()) - } - - func updateTrackMetadata(trackId: String, metadata: [String: Any]) { - guard let room = membraneRTC, let endpointId = localEndpointId else { - return - } - - room.updateTrackMetadata(trackId: trackId, trackMetadata: metadata.toMetadata()) - MembraneRoom.sharedInstance.endpoints[endpointId]?.tracksMetadata[trackId] = metadata.toMetadata() - emitEndpoints() - } - - func updateVideoTrackMetadata(metadata: [String: Any]) throws -> Void { - try ensureVideoTrack() - guard let trackId = localVideoTrack?.trackId() else { - return - } - - updateTrackMetadata(trackId: trackId, metadata: metadata) - } - - func updateAudioTrackMetadata(metadata: [String: Any]) throws -> Void { - try ensureAudioTrack() - guard let trackId = localAudioTrack?.trackId() else { - return - } - - updateTrackMetadata(trackId: trackId, metadata: metadata) - } - - func updateScreencastTrackMetadata(metadata: [String: Any]) throws -> Void { - try ensureScreencastTrack() - guard let trackId = localScreencastTrack?.trackId() else { - return - } - - updateTrackMetadata(trackId: trackId, metadata: metadata) - } - - private func toggleTrackEncoding(encoding: TrackEncoding, trackId: String, simulcastConfig: SimulcastConfig) -> SimulcastConfig? { - guard let room = membraneRTC else { - return nil - } - if(simulcastConfig.activeEncodings.contains(encoding)) { - room.disableTrackEncoding(trackId: trackId, encoding: encoding) - return SimulcastConfig( - enabled: true, - activeEncodings: simulcastConfig.activeEncodings.filter { e in e != encoding} - ) - } else { - room.enableTrackEncoding(trackId: trackId, encoding: encoding) - return SimulcastConfig( - enabled: true, - activeEncodings: simulcastConfig.activeEncodings + [encoding] - ) - } - } - - func toggleScreencastTrackEncoding(encoding: String) throws -> [String: Any] { - try ensureScreencastTrack() - let trackEncoding = try validateEncoding(encoding: encoding as String) - guard - let trackId = localScreencastTrack?.trackId(), - let simulcastConfig = toggleTrackEncoding(encoding: trackEncoding, trackId: trackId, simulcastConfig: screencastSimulcastConfig) else { - throw Exception(name: "E_NOT_CONNECTED", description: "Client not connected to server yet. Make sure to call connect() first!") - } - self.screencastSimulcastConfig = simulcastConfig - return getSimulcastConfigAsRNMap(simulcastConfig: simulcastConfig) - } - func setScreencastTrackBandwidth(bandwidth: Int) throws -> Void { - try ensureScreencastTrack() - guard let room = membraneRTC, let trackId = localScreencastTrack?.trackId() else { - return - } - room.setTrackBandwidth(trackId: trackId, bandwidth: BandwidthLimit(bandwidth)) - } - - - func setScreencastTrackEncodingBandwidth(encoding: String, bandwidth: Int) throws -> Void { - try ensureScreencastTrack() - let trackEncoding = try validateEncoding(encoding: encoding as String) - guard let room = membraneRTC, let trackId = localScreencastTrack?.trackId() else { - return - } - room.setEncodingBandwidth(trackId: trackId, encoding: trackEncoding.description, bandwidth: BandwidthLimit(bandwidth)) - } - - func setTargetTrackEncoding(trackId: String, encoding: String) throws -> Void { - try ensureConnected() - guard - let room = membraneRTC, - let videoTrack = MembraneRoom.sharedInstance.getVideoTrackById(trackId: trackId as String), - let trackId = (videoTrack as? RemoteVideoTrack)?.track.trackId ?? (videoTrack as? LocalVideoTrack)?.trackId(), - let globalTrackId = getGlobalTrackId(localTrackId: trackId as String) - else { - throw Exception(name: "E_INVALID_TRACK_ID", description: "Remote track with id=\(trackId) not found") - } - let trackEncoding = try validateEncoding(encoding: encoding as String) - room.setTargetTrackEncoding(trackId: globalTrackId, encoding: trackEncoding) - } - - func toggleVideoTrackEncoding(encoding: String) throws -> [String: Any] { - try ensureVideoTrack() - let trackEncoding = try validateEncoding(encoding: encoding as String) - guard - let trackId = localVideoTrack?.trackId(), - let simulcastConfig = toggleTrackEncoding(encoding: trackEncoding, trackId: trackId, simulcastConfig: videoSimulcastConfig) else { - throw Exception(name: "E_NOT_CONNECTED", description: "Client not connected to server yet. Make sure to call connect() first!") - } - self.videoSimulcastConfig = simulcastConfig - emitEvent(name: "SimulcastConfigUpdate", data: getSimulcastConfigAsRNMap(simulcastConfig: simulcastConfig)) - return getSimulcastConfigAsRNMap(simulcastConfig: simulcastConfig) - } - - func setVideoTrackEncodingBandwidth(encoding: String, bandwidth: Int) throws -> Void { - try ensureVideoTrack() - guard let room = membraneRTC, let trackId = localVideoTrack?.trackId() else { - return - } - room.setEncodingBandwidth(trackId: trackId, encoding: encoding as String, bandwidth: BandwidthLimit(bandwidth)) - } - - func setVideoTrackBandwidth(bandwidth: Int) throws { - try ensureVideoTrack() - guard let room = membraneRTC, let trackId = localVideoTrack?.trackId() else { - return - } - room.setTrackBandwidth(trackId: trackId, bandwidth: BandwidthLimit(bandwidth)) - } - - func changeWebRTCLoggingSeverity(severity: String) throws { - switch severity { - case "verbose": - membraneRTC?.changeWebRTCLoggingSeverity(severity: .verbose) - case "info": - membraneRTC?.changeWebRTCLoggingSeverity(severity: .info) - case "warning": - membraneRTC?.changeWebRTCLoggingSeverity(severity: .warning) - case "error": - membraneRTC?.changeWebRTCLoggingSeverity(severity: .error) - case "none": - membraneRTC?.changeWebRTCLoggingSeverity(severity: .none) - default: - throw Exception(name: "E_INVALID_SEVERITY_LEVEL", description: "Severity with name=\(severity) not found") - } - } - - private func getMapFromStatsObject(obj: RTCInboundStats) -> [String: Any] { - var res: [String:Any] = [:] - res["kind"] = obj.kind - res["jitter"] = obj.jitter - res["packetsLost"] = obj.packetsLost - res["packetsReceived"] = obj.packetsReceived - res["bytesReceived"] = obj.bytesReceived - res["framesReceived"] = obj.framesReceived - res["frameWidth"] = obj.frameWidth - res["frameHeight"] = obj.frameHeight - res["framesPerSecond"] = obj.framesPerSecond - res["framesDropped"] = obj.framesDropped - - return res - } - - private func getMapFromStatsObject(obj: RTCOutboundStats) -> [String: Any] { - var innerMap: [String: Double] = [:] - - innerMap["bandwidth"] = obj.qualityLimitationDurations?.bandwidth ?? 0.0 - innerMap["cpu"] = obj.qualityLimitationDurations?.cpu ?? 0.0 - innerMap["none"] = obj.qualityLimitationDurations?.none ?? 0.0 - innerMap["other"] = obj.qualityLimitationDurations?.other ?? 0.0 - - var res: [String: Any] = [:] - res["kind"] = obj.kind - res["rid"] = obj.rid - res["bytesSent"] = obj.bytesSent - res["targetBitrate"] = obj.targetBitrate - res["packetsSent"] = obj.packetsSent - res["framesEncoded"] = obj.framesEncoded - res["framesPerSecond"] = obj.framesPerSecond - res["frameWidth"] = obj.frameWidth - res["frameHeight"] = obj.frameHeight - res["qualityLimitationDurations"] = innerMap - - return res - } - - private func statsToRNMap(stats: [String: RTCStats]?) -> [String: Any] { - var res: [String: Any] = [:] - stats?.forEach{ pair in - if let val = pair.value as? RTCOutboundStats { - res[pair.key] = getMapFromStatsObject(obj: val) - } else { - res[pair.key] = getMapFromStatsObject(obj: pair.value as! RTCInboundStats) - } - } - return res - } - - func getStatistics() -> [String: Any] { - return statsToRNMap(stats:membraneRTC?.getStats()) - } - - func setAudioSessionMode() { - guard let localAudioTrack = localAudioTrack else { - return - } - - switch self.audioSessionMode { - case AVAudioSession.Mode.videoChat: - localAudioTrack.setVideoChatMode() - break - case AVAudioSession.Mode.voiceChat: - localAudioTrack.setVoiceChatMode() - break - default: - localAudioTrack.setVideoChatMode() - break - } - } - - func selectAudioSessionMode(sessionMode: String) throws { - switch sessionMode { - case "videoChat": - self.audioSessionMode = AVAudioSession.Mode.videoChat - break - case "voiceChat": - self.audioSessionMode = AVAudioSession.Mode.voiceChat - break - default: - throw Exception(name: "E_MEMBRANE_AUDIO_SESSION", description: "Invalid audio session mode: \(sessionMode). Supported modes: videoChat, voiceChat") - } - setAudioSessionMode() - } - - func showAudioRoutePicker() { - DispatchQueue.main.sync { - let pickerView = AVRoutePickerView() - if let button = pickerView.subviews.first(where: { $0 is UIButton }) as? UIButton { - button.sendActions(for: .touchUpInside) - } - } - } - - func startAudioSwitcher() { - onRouteChangeNotification() - } - - func emitEvent(name: String, data: [String: Any]) -> Void { - sendEvent(name, data) - } - - func emitEndpoints() -> Void { - let EndpointsUpdateMap = ["EndpointsUpdate": getEndpoints()] - emitEvent(name: "EndpointsUpdate", data: EndpointsUpdateMap ) - } - - @objc func onRouteChangeNotification() { - let currentRoute = AVAudioSession.sharedInstance().currentRoute - let output = currentRoute.outputs[0] - let deviceType = output.portType - var deviceTypeString: String = "" - - switch deviceType { - case .bluetoothA2DP, .bluetoothLE, .bluetoothHFP: - deviceTypeString = "bluetooth" - break - case .builtInSpeaker: - deviceTypeString = "speaker" - break - case .builtInReceiver: - deviceTypeString = "earpiece" - break - case .headphones: - deviceTypeString = "headphones" - break - default: - deviceTypeString = deviceType.rawValue - } - emitEvent(name: "AudioDeviceUpdate", data: ["selectedDevice": ["name": output.portName, "type": deviceTypeString], "availableDevices": []] as [String : Any]) - } - - func onSendMediaEvent(event: SerializedMediaEvent) { - emitEvent(name: "SendMediaEvent", data: ["event": event]) + + room.updateTrackMetadata(trackId: trackId, trackMetadata: metadata.toMetadata()) + MembraneRoom.sharedInstance.endpoints[endpointId]?.tracksMetadata[trackId] = + metadata.toMetadata() + emitEndpoints() + } + + func updateVideoTrackMetadata(metadata: [String: Any]) throws { + try ensureVideoTrack() + guard let trackId = localVideoTrack?.trackId() else { + return } - - func onConnected(endpointId: String, otherEndpoints: [Endpoint]) { - otherEndpoints.forEach { endpoint in - MembraneRoom.sharedInstance.endpoints[endpoint.id] = RNEndpoint(id: endpoint.id, metadata: endpoint.metadata, type: endpoint.type) - } - - emitEndpoints() - if let connectPromise = connectPromise { - connectPromise.resolve(nil) - } - connectPromise = nil + + updateTrackMetadata(trackId: trackId, metadata: metadata) + } + + func updateAudioTrackMetadata(metadata: [String: Any]) throws { + try ensureAudioTrack() + guard let trackId = localAudioTrack?.trackId() else { + return } - - func onConnectionError(metadata: Any) { - if let connectPromise = connectPromise { - connectPromise.reject("E_MEMBRANE_CONNECT", "Failed to connect: \(metadata)") - } - connectPromise = nil + + updateTrackMetadata(trackId: trackId, metadata: metadata) + } + + func updateScreencastTrackMetadata(metadata: [String: Any]) throws { + try ensureScreencastTrack() + guard let trackId = localScreencastTrack?.trackId() else { + return } - - func updateOrAddTrack(ctx: TrackContext) { - guard var endpoint = MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] else { - return - } - if let audioTrack = ctx.track as? RemoteAudioTrack { - let localTrackId = (ctx.track as? RemoteAudioTrack)?.track.trackId - globalToLocalTrackId[ctx.trackId] = localTrackId - endpoint.audioTracks[audioTrack.track.trackId] = audioTrack - endpoint.tracksMetadata[audioTrack.track.trackId] = ctx.metadata - if let localTrackId = localTrackId, - tracksContexts[localTrackId] == nil { - tracksContexts[localTrackId] = ctx - ctx.setOnVoiceActivityChangedListener { ctx in - self.emitEndpoints() - } - } - } - - if let videoTrack = ctx.track as? RemoteVideoTrack { - let localTrackId = (ctx.track as? RemoteVideoTrack)?.track.trackId - globalToLocalTrackId[ctx.trackId] = localTrackId - endpoint.videoTracks[videoTrack.track.trackId] = videoTrack - endpoint.tracksMetadata[videoTrack.track.trackId] = ctx.metadata - if let localTrackId = localTrackId, - tracksContexts[localTrackId] == nil { - tracksContexts[localTrackId] = ctx - ctx.setOnEncodingChangedListener { ctx in - self.emitEndpoints() - } - } - } - MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] = endpoint - self.emitEndpoints() + + updateTrackMetadata(trackId: trackId, metadata: metadata) + } + + private func toggleTrackEncoding( + encoding: TrackEncoding, trackId: String, simulcastConfig: SimulcastConfig + ) -> SimulcastConfig? { + guard let room = membraneRTC else { + return nil + } + if simulcastConfig.activeEncodings.contains(encoding) { + room.disableTrackEncoding(trackId: trackId, encoding: encoding) + return SimulcastConfig( + enabled: true, + activeEncodings: simulcastConfig.activeEncodings.filter { e in e != encoding } + ) + } else { + room.enableTrackEncoding(trackId: trackId, encoding: encoding) + return SimulcastConfig( + enabled: true, + activeEncodings: simulcastConfig.activeEncodings + [encoding] + ) + } + } + + func toggleScreencastTrackEncoding(encoding: String) throws -> [String: Any] { + try ensureScreencastTrack() + let trackEncoding = try validateEncoding(encoding: encoding as String) + guard + let trackId = localScreencastTrack?.trackId(), + let simulcastConfig = toggleTrackEncoding( + encoding: trackEncoding, trackId: trackId, simulcastConfig: screencastSimulcastConfig) + else { + throw Exception( + name: "E_NOT_CONNECTED", + description: "Client not connected to server yet. Make sure to call connect() first!") + } + self.screencastSimulcastConfig = simulcastConfig + return getSimulcastConfigAsRNMap(simulcastConfig: simulcastConfig) + } + func setScreencastTrackBandwidth(bandwidth: Int) throws { + try ensureScreencastTrack() + guard let room = membraneRTC, let trackId = localScreencastTrack?.trackId() else { + return + } + room.setTrackBandwidth(trackId: trackId, bandwidth: BandwidthLimit(bandwidth)) + } + + func setScreencastTrackEncodingBandwidth(encoding: String, bandwidth: Int) throws { + try ensureScreencastTrack() + let trackEncoding = try validateEncoding(encoding: encoding as String) + guard let room = membraneRTC, let trackId = localScreencastTrack?.trackId() else { + return + } + room.setEncodingBandwidth( + trackId: trackId, encoding: trackEncoding.description, bandwidth: BandwidthLimit(bandwidth)) + } + + func setTargetTrackEncoding(trackId: String, encoding: String) throws { + try ensureConnected() + guard + let room = membraneRTC, + let videoTrack = MembraneRoom.sharedInstance.getVideoTrackById(trackId: trackId as String), + let trackId = (videoTrack as? RemoteVideoTrack)?.track.trackId + ?? (videoTrack as? LocalVideoTrack)?.trackId(), + let globalTrackId = getGlobalTrackId(localTrackId: trackId as String) + else { + throw Exception( + name: "E_INVALID_TRACK_ID", description: "Remote track with id=\(trackId) not found") + } + let trackEncoding = try validateEncoding(encoding: encoding as String) + room.setTargetTrackEncoding(trackId: globalTrackId, encoding: trackEncoding) + } + + func toggleVideoTrackEncoding(encoding: String) throws -> [String: Any] { + try ensureVideoTrack() + let trackEncoding = try validateEncoding(encoding: encoding as String) + guard + let trackId = localVideoTrack?.trackId(), + let simulcastConfig = toggleTrackEncoding( + encoding: trackEncoding, trackId: trackId, simulcastConfig: videoSimulcastConfig) + else { + throw Exception( + name: "E_NOT_CONNECTED", + description: "Client not connected to server yet. Make sure to call connect() first!") + } + self.videoSimulcastConfig = simulcastConfig + let eventName = EmitableEvents.SimulcastConfigUpdate + emitEvent( + name: eventName, + data: getSimulcastConfigAsRNMap(simulcastConfig: simulcastConfig)) + return getSimulcastConfigAsRNMap(simulcastConfig: simulcastConfig) + } + + func setVideoTrackEncodingBandwidth(encoding: String, bandwidth: Int) throws { + try ensureVideoTrack() + guard let room = membraneRTC, let trackId = localVideoTrack?.trackId() else { + return + } + room.setEncodingBandwidth( + trackId: trackId, encoding: encoding as String, bandwidth: BandwidthLimit(bandwidth)) + } + + func setVideoTrackBandwidth(bandwidth: Int) throws { + try ensureVideoTrack() + guard let room = membraneRTC, let trackId = localVideoTrack?.trackId() else { + return } - - func onTrackReady(ctx: TrackContext) { - updateOrAddTrack(ctx: ctx) + room.setTrackBandwidth(trackId: trackId, bandwidth: BandwidthLimit(bandwidth)) + } + + func changeWebRTCLoggingSeverity(severity: String) throws { + switch severity { + case "verbose": + membraneRTC?.changeWebRTCLoggingSeverity(severity: .verbose) + case "info": + membraneRTC?.changeWebRTCLoggingSeverity(severity: .info) + case "warning": + membraneRTC?.changeWebRTCLoggingSeverity(severity: .warning) + case "error": + membraneRTC?.changeWebRTCLoggingSeverity(severity: .error) + case "none": + membraneRTC?.changeWebRTCLoggingSeverity(severity: .none) + default: + throw Exception( + name: "E_INVALID_SEVERITY_LEVEL", description: "Severity with name=\(severity) not found") + } + } + + private func getMapFromStatsObject(obj: RTCInboundStats) -> [String: Any] { + var res: [String: Any] = [:] + res["kind"] = obj.kind + res["jitter"] = obj.jitter + res["packetsLost"] = obj.packetsLost + res["packetsReceived"] = obj.packetsReceived + res["bytesReceived"] = obj.bytesReceived + res["framesReceived"] = obj.framesReceived + res["frameWidth"] = obj.frameWidth + res["frameHeight"] = obj.frameHeight + res["framesPerSecond"] = obj.framesPerSecond + res["framesDropped"] = obj.framesDropped + + return res + } + + private func getMapFromStatsObject(obj: RTCOutboundStats) -> [String: Any] { + var innerMap: [String: Double] = [:] + + innerMap["bandwidth"] = obj.qualityLimitationDurations?.bandwidth ?? 0.0 + innerMap["cpu"] = obj.qualityLimitationDurations?.cpu ?? 0.0 + innerMap["none"] = obj.qualityLimitationDurations?.none ?? 0.0 + innerMap["other"] = obj.qualityLimitationDurations?.other ?? 0.0 + + var res: [String: Any] = [:] + res["kind"] = obj.kind + res["rid"] = obj.rid + res["bytesSent"] = obj.bytesSent + res["targetBitrate"] = obj.targetBitrate + res["packetsSent"] = obj.packetsSent + res["framesEncoded"] = obj.framesEncoded + res["framesPerSecond"] = obj.framesPerSecond + res["frameWidth"] = obj.frameWidth + res["frameHeight"] = obj.frameHeight + res["qualityLimitationDurations"] = innerMap + + return res + } + + private func statsToRNMap(stats: [String: RTCStats]?) -> [String: Any] { + var res: [String: Any] = [:] + stats?.forEach { pair in + if let val = pair.value as? RTCOutboundStats { + res[pair.key] = getMapFromStatsObject(obj: val) + } else { + res[pair.key] = getMapFromStatsObject(obj: pair.value as! RTCInboundStats) + } + } + return res + } + + func getStatistics() -> [String: Any] { + return statsToRNMap(stats: membraneRTC?.getStats()) + } + + func setAudioSessionMode() { + guard let localAudioTrack = localAudioTrack else { + return } - - func onTrackAdded(ctx: TrackContext) { - + + switch self.audioSessionMode { + case AVAudioSession.Mode.videoChat: + localAudioTrack.setVideoChatMode() + break + case AVAudioSession.Mode.voiceChat: + localAudioTrack.setVoiceChatMode() + break + default: + localAudioTrack.setVideoChatMode() + break + } + } + + func selectAudioSessionMode(sessionMode: String) throws { + switch sessionMode { + case "videoChat": + self.audioSessionMode = AVAudioSession.Mode.videoChat + break + case "voiceChat": + self.audioSessionMode = AVAudioSession.Mode.voiceChat + break + default: + throw Exception( + name: "E_MEMBRANE_AUDIO_SESSION", + description: + "Invalid audio session mode: \(sessionMode). Supported modes: videoChat, voiceChat") + } + setAudioSessionMode() + } + + func showAudioRoutePicker() { + DispatchQueue.main.sync { + let pickerView = AVRoutePickerView() + if let button = pickerView.subviews.first(where: { $0 is UIButton }) as? UIButton { + button.sendActions(for: .touchUpInside) + } + } + } + + func startAudioSwitcher() { + onRouteChangeNotification() + } + + func emitEvent(name: String, data: [String: Any]) { + sendEvent(name, data) + } + + func emitEndpoints() { + let eventName = EmitableEvents.EndpointsUpdate + let EndpointsUpdateMap = [eventName: getEndpoints()] + emitEvent(name: eventName, data: EndpointsUpdateMap) + } + + @objc func onRouteChangeNotification() { + let currentRoute = AVAudioSession.sharedInstance().currentRoute + let output = currentRoute.outputs[0] + let deviceType = output.portType + var deviceTypeString: String = "" + + switch deviceType { + case .bluetoothA2DP, .bluetoothLE, .bluetoothHFP: + deviceTypeString = "bluetooth" + break + case .builtInSpeaker: + deviceTypeString = "speaker" + break + case .builtInReceiver: + deviceTypeString = "earpiece" + break + case .headphones: + deviceTypeString = "headphones" + break + default: + deviceTypeString = deviceType.rawValue + } + let eventName = EmitableEvents.AudioDeviceUpdate + emitEvent( + name: eventName, + data: [ + eventName: [ + "selectedDevice": ["name": output.portName, "type": deviceTypeString], + "availableDevices": [], + ] + ] as [String: [String: Any]]) + } + + func onSendMediaEvent(event: SerializedMediaEvent) { + let eventName = EmitableEvents.SendMediaEvent + emitEvent(name: eventName, data: ["event": event]) + } + + func onConnected(endpointId: String, otherEndpoints: [Endpoint]) { + otherEndpoints.forEach { endpoint in + MembraneRoom.sharedInstance.endpoints[endpoint.id] = RNEndpoint( + id: endpoint.id, metadata: endpoint.metadata, type: endpoint.type) } - - func onTrackRemoved(ctx: TrackContext) { - guard var endpoint = MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] else { - return - } - if let audioTrack = ctx.track as? RemoteAudioTrack { - endpoint = endpoint.removeTrack(trackId: audioTrack.track.trackId) - } - if let videoTrack = ctx.track as? RemoteVideoTrack { - endpoint = endpoint.removeTrack(trackId: videoTrack.track.trackId) - } - globalToLocalTrackId.removeValue(forKey: ctx.trackId) - MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] = endpoint - emitEndpoints() + + emitEndpoints() + if let connectPromise = connectPromise { + connectPromise.resolve(nil) } - - func onTrackUpdated(ctx: TrackContext) { - updateOrAddTrack(ctx: ctx) + connectPromise = nil + } + + func onConnectionError(metadata: Any) { + if let connectPromise = connectPromise { + connectPromise.reject("E_MEMBRANE_CONNECT", "Failed to connect: \(metadata)") } - - func onEndpointAdded(endpoint: Endpoint) { - MembraneRoom.sharedInstance.endpoints[endpoint.id] = RNEndpoint(id: endpoint.id, metadata: endpoint.metadata, type: endpoint.type) - emitEndpoints() + connectPromise = nil + } + + func updateOrAddTrack(ctx: TrackContext) { + guard var endpoint = MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] else { + return + } + if let audioTrack = ctx.track as? RemoteAudioTrack { + let localTrackId = (ctx.track as? RemoteAudioTrack)?.track.trackId + globalToLocalTrackId[ctx.trackId] = localTrackId + endpoint.audioTracks[audioTrack.track.trackId] = audioTrack + endpoint.tracksMetadata[audioTrack.track.trackId] = ctx.metadata + if let localTrackId = localTrackId, + tracksContexts[localTrackId] == nil + { + tracksContexts[localTrackId] = ctx + ctx.setOnVoiceActivityChangedListener { ctx in + self.emitEndpoints() + } + } } - - func onEndpointRemoved(endpoint: Endpoint) { - MembraneRoom.sharedInstance.endpoints.removeValue(forKey: endpoint.id) - emitEndpoints() + + if let videoTrack = ctx.track as? RemoteVideoTrack { + let localTrackId = (ctx.track as? RemoteVideoTrack)?.track.trackId + globalToLocalTrackId[ctx.trackId] = localTrackId + endpoint.videoTracks[videoTrack.track.trackId] = videoTrack + endpoint.tracksMetadata[videoTrack.track.trackId] = ctx.metadata + if let localTrackId = localTrackId, + tracksContexts[localTrackId] == nil + { + tracksContexts[localTrackId] = ctx + ctx.setOnEncodingChangedListener { ctx in + self.emitEndpoints() + } + } + } + MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] = endpoint + self.emitEndpoints() + } + + func onTrackReady(ctx: TrackContext) { + updateOrAddTrack(ctx: ctx) + } + + func onTrackAdded(ctx: TrackContext) { + + } + + func onTrackRemoved(ctx: TrackContext) { + guard var endpoint = MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] else { + return } - - func onEndpointUpdated(endpoint: Endpoint) { - + if let audioTrack = ctx.track as? RemoteAudioTrack { + endpoint = endpoint.removeTrack(trackId: audioTrack.track.trackId) } - - func onBandwidthEstimationChanged(estimation: Int) { - emitEvent(name: "BandwidthEstimation", data: ["BandwidthEstimation": estimation]) + if let videoTrack = ctx.track as? RemoteVideoTrack { + endpoint = endpoint.removeTrack(trackId: videoTrack.track.trackId) } - -} + globalToLocalTrackId.removeValue(forKey: ctx.trackId) + MembraneRoom.sharedInstance.endpoints[ctx.endpoint.id] = endpoint + emitEndpoints() + } + + func onTrackUpdated(ctx: TrackContext) { + updateOrAddTrack(ctx: ctx) + } + + func onEndpointAdded(endpoint: Endpoint) { + MembraneRoom.sharedInstance.endpoints[endpoint.id] = RNEndpoint( + id: endpoint.id, metadata: endpoint.metadata, type: endpoint.type) + emitEndpoints() + } + func onEndpointRemoved(endpoint: Endpoint) { + MembraneRoom.sharedInstance.endpoints.removeValue(forKey: endpoint.id) + emitEndpoints() + } + + func onEndpointUpdated(endpoint: Endpoint) { + + } + + func onBandwidthEstimationChanged(estimation: Int) { + let eventName = EmitableEvents.BandwidthEstimation + emitEvent(name: eventName, data: [eventName: estimation]) + } + +} diff --git a/src/index.tsx b/src/index.tsx index e6180929..2d085f98 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -33,6 +33,17 @@ import MembraneWebRTCModule from './MembraneWebRTCModule'; import VideoPreviewView from './VideoPreviewView'; import VideoRendererView from './VideoRendererView'; +const ReceivableEvents = { + IsCameraOn: 'IsCameraOn', + IsMicrophoneOn: 'IsMicrophoneOn', + IsScreencastOn: 'IsScreencastOn', + SimulcastConfigUpdate: 'SimulcastConfigUpdate', + EndpointsUpdate: 'EndpointsUpdate', + AudioDeviceUpdate: 'AudioDeviceUpdate', + SendMediaEvent: 'SendMediaEvent', + BandwidthEstimation: 'BandwidthEstimation', +} as const; + const eventEmitter = new EventEmitter( MembraneWebRTCModule ?? NativeModulesProxy.MembraneWebRTC ); @@ -60,7 +71,7 @@ export function useWebRTC() { useEffect(() => { const eventListener = eventEmitter.addListener( - 'SendMediaEvent', + ReceivableEvents.SendMediaEvent, sendMediaEvent ); return () => eventListener.remove(); @@ -220,19 +231,17 @@ export function useEndpoints< VideoTrackMetadataType, AudioTrackMetadataType > - >('EndpointsUpdate', ({ endpoints }) => { - setEndpoints(endpoints); + >(ReceivableEvents.EndpointsUpdate, (event) => { + setEndpoints(event.EndpointsUpdate); }); MembraneWebRTCModule.getEndpoints().then( - ({ - endpoints, - }: { + ( endpoints: Endpoint< EndpointMetadataType, VideoTrackMetadataType, AudioTrackMetadataType - >[]; - }) => setEndpoints(endpoints) + >[] + ) => setEndpoints(endpoints) ); return () => eventListener.remove(); }, []); @@ -269,7 +278,7 @@ export function useCamera() { useEffect(() => { const eventListener = eventEmitter.addListener( - 'SimulcastConfigUpdate', + ReceivableEvents.SimulcastConfigUpdate, (event) => setSimulcastConfig(event) ); return () => eventListener.remove(); @@ -277,7 +286,7 @@ export function useCamera() { useEffect(() => { const eventListener = eventEmitter.addListener( - 'IsCameraOn', + ReceivableEvents.IsCameraOn, (event) => setIsCameraOn(event.IsCameraOn) ); setIsCameraOn(MembraneWebRTCModule.isCameraOn); @@ -404,7 +413,7 @@ export function useMicrophone() { useEffect(() => { const eventListener = eventEmitter.addListener( - 'IsMicrophoneOn', + ReceivableEvents.IsMicrophoneOn, (event) => setIsMicrophoneOn(event.IsMicrophoneOn) ); setIsMicrophoneOn(MembraneWebRTCModule.isMicrophoneOn); @@ -449,7 +458,7 @@ export function useScreencast() { ); useEffect(() => { const eventListener = eventEmitter.addListener( - 'IsScreencastOn', + ReceivableEvents.IsScreencastOn, (event) => setIsScreencastOn(event.IsScreencastOn) ); setIsScreencastOn(MembraneWebRTCModule.isScreencastOn); @@ -578,18 +587,20 @@ export function useAudioSettings() { ); type onAudioDeviceEvent = { - selectedDevice: AudioOutputDevice; - availableDevices: AudioOutputDevice[]; + AudioDeviceUpdate: { + selectedDevice: AudioOutputDevice; + availableDevices: AudioOutputDevice[]; + }; }; const onAudioDevice = useCallback((event: onAudioDeviceEvent) => { - setSelectedAudioOutputDevice(event.selectedDevice); - setAvailableDevices(event.availableDevices); + setSelectedAudioOutputDevice(event.AudioDeviceUpdate.selectedDevice); + setAvailableDevices(event.AudioDeviceUpdate.availableDevices); }, []); useEffect(() => { const eventListener = eventEmitter.addListener( - 'AudioDeviceUpdate', + ReceivableEvents.AudioDeviceUpdate, onAudioDevice ); MembraneWebRTCModule.startAudioSwitcher(); @@ -683,7 +694,7 @@ export function useBandwidthEstimation() { useEffect(() => { const eventListener = eventEmitter.addListener( - 'BandwidthEstimation', + ReceivableEvents.BandwidthEstimation, (event) => setEstimation(event.BandwidthEstimation) ); return () => eventListener.remove();