@fishjam-cloud/react-native-client 0.23.0 → 0.25.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/README.md +56 -15
  2. package/dist/index.d.ts +13 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +19 -0
  5. package/dist/overrides/RTCPeerConnection.d.ts +8 -0
  6. package/dist/overrides/RTCPeerConnection.d.ts.map +1 -0
  7. package/dist/overrides/RTCPeerConnection.js +16 -0
  8. package/dist/overrides/RTCView.d.ts +11 -0
  9. package/dist/overrides/RTCView.d.ts.map +1 -0
  10. package/dist/overrides/RTCView.js +21 -0
  11. package/dist/polyfills/local-storage.d.ts +10 -0
  12. package/dist/polyfills/local-storage.d.ts.map +1 -0
  13. package/dist/polyfills/local-storage.js +24 -0
  14. package/{build/hooks → dist}/useForegroundService.d.ts +10 -10
  15. package/dist/useForegroundService.d.ts.map +1 -0
  16. package/dist/useForegroundService.js +10 -0
  17. package/dist/webrtc-polyfill.d.ts +2 -0
  18. package/dist/webrtc-polyfill.d.ts.map +1 -0
  19. package/dist/webrtc-polyfill.js +14 -0
  20. package/package.json +44 -92
  21. package/plugin/broadcastExtensionFiles/Atomic.swift +37 -0
  22. package/plugin/broadcastExtensionFiles/DarwinNotificationCenter.swift +29 -0
  23. package/plugin/broadcastExtensionFiles/Info.plist +2 -2
  24. package/plugin/broadcastExtensionFiles/SampleHandler.swift +104 -0
  25. package/plugin/broadcastExtensionFiles/SampleUploader.swift +146 -0
  26. package/plugin/broadcastExtensionFiles/SocketConnection.swift +198 -0
  27. package/plugin/build/types.d.ts +5 -3
  28. package/plugin/build/withFishjam.d.ts +31 -2
  29. package/plugin/build/withFishjam.js +32 -6
  30. package/plugin/build/withFishjamAndroid.d.ts +2 -2
  31. package/plugin/build/withFishjamAndroid.js +60 -13
  32. package/plugin/build/withFishjamIos.d.ts +3 -8
  33. package/plugin/build/withFishjamIos.js +54 -69
  34. package/plugin/src/types.ts +19 -0
  35. package/plugin/src/withFishjam.ts +42 -0
  36. package/plugin/src/withFishjamAndroid.ts +112 -0
  37. package/plugin/src/withFishjamIos.ts +336 -0
  38. package/plugin/tsconfig.json +9 -0
  39. package/plugin/tsconfig.tsbuildinfo +1 -0
  40. package/LICENSE.txt +0 -201
  41. package/android/build.gradle +0 -95
  42. package/android/src/main/AndroidManifest.xml +0 -2
  43. package/android/src/main/java/io/fishjam/reactnative/AudioDeviceKind.kt +0 -34
  44. package/android/src/main/java/io/fishjam/reactnative/AudioSwitchManager.kt +0 -61
  45. package/android/src/main/java/io/fishjam/reactnative/EmitableEvents.kt +0 -126
  46. package/android/src/main/java/io/fishjam/reactnative/Errors.kt +0 -31
  47. package/android/src/main/java/io/fishjam/reactnative/PipContainerView.kt +0 -282
  48. package/android/src/main/java/io/fishjam/reactnative/PipContainerViewModule.kt +0 -41
  49. package/android/src/main/java/io/fishjam/reactnative/PipTrackSelector.kt +0 -98
  50. package/android/src/main/java/io/fishjam/reactnative/PipViewFactory.kt +0 -186
  51. package/android/src/main/java/io/fishjam/reactnative/RNFishjamClient.kt +0 -1001
  52. package/android/src/main/java/io/fishjam/reactnative/RNFishjamClientModule.kt +0 -358
  53. package/android/src/main/java/io/fishjam/reactnative/Utils.kt +0 -11
  54. package/android/src/main/java/io/fishjam/reactnative/VideoPreviewView.kt +0 -40
  55. package/android/src/main/java/io/fishjam/reactnative/VideoPreviewViewModule.kt +0 -22
  56. package/android/src/main/java/io/fishjam/reactnative/VideoRendererView.kt +0 -76
  57. package/android/src/main/java/io/fishjam/reactnative/VideoRendererViewModule.kt +0 -33
  58. package/android/src/main/java/io/fishjam/reactnative/VideoView.kt +0 -257
  59. package/android/src/main/java/io/fishjam/reactnative/extensions/CaptureDevice.kt +0 -17
  60. package/android/src/main/java/io/fishjam/reactnative/foregroundService/FishjamForegroundService.kt +0 -101
  61. package/android/src/main/java/io/fishjam/reactnative/foregroundService/ForegroundServiceManager.kt +0 -118
  62. package/android/src/main/java/io/fishjam/reactnative/foregroundService/ForegroundServiceState.kt +0 -46
  63. package/android/src/main/java/io/fishjam/reactnative/helpers/PictureInPictureHelperFragment.kt +0 -20
  64. package/android/src/main/java/io/fishjam/reactnative/managers/ListenerManager.kt +0 -24
  65. package/android/src/main/java/io/fishjam/reactnative/managers/LocalCameraTracksChangedListenersManager.kt +0 -8
  66. package/android/src/main/java/io/fishjam/reactnative/managers/LocalTracksSwitchListenersManager.kt +0 -17
  67. package/android/src/main/java/io/fishjam/reactnative/managers/TracksUpdateListenersManager.kt +0 -7
  68. package/android/src/main/java/io/fishjam/reactnative/utils/PermissionUtils.kt +0 -45
  69. package/build/RNFishjamClientModule.d.ts +0 -106
  70. package/build/RNFishjamClientModule.d.ts.map +0 -1
  71. package/build/RNFishjamClientModule.js +0 -21
  72. package/build/RNFishjamClientModule.js.map +0 -1
  73. package/build/common/metadata.d.ts +0 -9
  74. package/build/common/metadata.d.ts.map +0 -1
  75. package/build/common/metadata.js +0 -11
  76. package/build/common/metadata.js.map +0 -1
  77. package/build/components/FishjamRoom/GridTrackItem.d.ts +0 -12
  78. package/build/components/FishjamRoom/GridTrackItem.d.ts.map +0 -1
  79. package/build/components/FishjamRoom/GridTrackItem.js +0 -56
  80. package/build/components/FishjamRoom/GridTrackItem.js.map +0 -1
  81. package/build/components/FishjamRoom/VideosGrid.d.ts +0 -3
  82. package/build/components/FishjamRoom/VideosGrid.d.ts.map +0 -1
  83. package/build/components/FishjamRoom/VideosGrid.js +0 -20
  84. package/build/components/FishjamRoom/VideosGrid.js.map +0 -1
  85. package/build/components/FishjamRoom/index.d.ts +0 -33
  86. package/build/components/FishjamRoom/index.d.ts.map +0 -1
  87. package/build/components/FishjamRoom/index.js +0 -53
  88. package/build/components/FishjamRoom/index.js.map +0 -1
  89. package/build/components/FishjamRoom/parsePeersToTracks.d.ts +0 -4
  90. package/build/components/FishjamRoom/parsePeersToTracks.d.ts.map +0 -1
  91. package/build/components/FishjamRoom/parsePeersToTracks.js +0 -37
  92. package/build/components/FishjamRoom/parsePeersToTracks.js.map +0 -1
  93. package/build/components/PipContainerView.d.ts +0 -48
  94. package/build/components/PipContainerView.d.ts.map +0 -1
  95. package/build/components/PipContainerView.js +0 -34
  96. package/build/components/PipContainerView.js.map +0 -1
  97. package/build/components/VideoPreviewView.d.ts +0 -28
  98. package/build/components/VideoPreviewView.d.ts.map +0 -1
  99. package/build/components/VideoPreviewView.js +0 -17
  100. package/build/components/VideoPreviewView.js.map +0 -1
  101. package/build/components/VideoRendererView.d.ts +0 -39
  102. package/build/components/VideoRendererView.d.ts.map +0 -1
  103. package/build/components/VideoRendererView.js +0 -25
  104. package/build/components/VideoRendererView.js.map +0 -1
  105. package/build/consts/index.d.ts +0 -5
  106. package/build/consts/index.d.ts.map +0 -1
  107. package/build/consts/index.js +0 -8
  108. package/build/consts/index.js.map +0 -1
  109. package/build/debug/common/webRTC.d.ts +0 -9
  110. package/build/debug/common/webRTC.d.ts.map +0 -1
  111. package/build/debug/common/webRTC.js +0 -11
  112. package/build/debug/common/webRTC.js.map +0 -1
  113. package/build/debug/index.d.ts +0 -4
  114. package/build/debug/index.d.ts.map +0 -1
  115. package/build/debug/index.js +0 -3
  116. package/build/debug/index.js.map +0 -1
  117. package/build/debug/stats/types.d.ts +0 -55
  118. package/build/debug/stats/types.d.ts.map +0 -1
  119. package/build/debug/stats/types.js +0 -2
  120. package/build/debug/stats/types.js.map +0 -1
  121. package/build/debug/stats/useRTCStatistics.d.ts +0 -9
  122. package/build/debug/stats/useRTCStatistics.d.ts.map +0 -1
  123. package/build/debug/stats/useRTCStatistics.js +0 -79
  124. package/build/debug/stats/useRTCStatistics.js.map +0 -1
  125. package/build/hooks/internal/useFishjamEvent.d.ts +0 -4
  126. package/build/hooks/internal/useFishjamEvent.d.ts.map +0 -1
  127. package/build/hooks/internal/useFishjamEvent.js +0 -38
  128. package/build/hooks/internal/useFishjamEvent.js.map +0 -1
  129. package/build/hooks/internal/useFishjamEventState.d.ts +0 -3
  130. package/build/hooks/internal/useFishjamEventState.d.ts.map +0 -1
  131. package/build/hooks/internal/useFishjamEventState.js +0 -18
  132. package/build/hooks/internal/useFishjamEventState.js.map +0 -1
  133. package/build/hooks/useAppScreenShare.d.ts +0 -20
  134. package/build/hooks/useAppScreenShare.d.ts.map +0 -1
  135. package/build/hooks/useAppScreenShare.js +0 -54
  136. package/build/hooks/useAppScreenShare.js.map +0 -1
  137. package/build/hooks/useAudioSettings.d.ts +0 -49
  138. package/build/hooks/useAudioSettings.d.ts.map +0 -1
  139. package/build/hooks/useAudioSettings.js +0 -70
  140. package/build/hooks/useAudioSettings.js.map +0 -1
  141. package/build/hooks/useBandwidthEstimation.d.ts +0 -14
  142. package/build/hooks/useBandwidthEstimation.d.ts.map +0 -1
  143. package/build/hooks/useBandwidthEstimation.js +0 -19
  144. package/build/hooks/useBandwidthEstimation.js.map +0 -1
  145. package/build/hooks/useCallKit.d.ts +0 -121
  146. package/build/hooks/useCallKit.d.ts.map +0 -1
  147. package/build/hooks/useCallKit.js +0 -142
  148. package/build/hooks/useCallKit.js.map +0 -1
  149. package/build/hooks/useCamera.d.ts +0 -113
  150. package/build/hooks/useCamera.d.ts.map +0 -1
  151. package/build/hooks/useCamera.js +0 -95
  152. package/build/hooks/useCamera.js.map +0 -1
  153. package/build/hooks/useConnection.d.ts +0 -73
  154. package/build/hooks/useConnection.d.ts.map +0 -1
  155. package/build/hooks/useConnection.js +0 -46
  156. package/build/hooks/useConnection.js.map +0 -1
  157. package/build/hooks/useForegroundService.d.ts.map +0 -1
  158. package/build/hooks/useForegroundService.js +0 -66
  159. package/build/hooks/useForegroundService.js.map +0 -1
  160. package/build/hooks/useMicrophone.d.ts +0 -16
  161. package/build/hooks/useMicrophone.d.ts.map +0 -1
  162. package/build/hooks/useMicrophone.js +0 -31
  163. package/build/hooks/useMicrophone.js.map +0 -1
  164. package/build/hooks/usePeerStatus.d.ts +0 -10
  165. package/build/hooks/usePeerStatus.d.ts.map +0 -1
  166. package/build/hooks/usePeerStatus.js +0 -13
  167. package/build/hooks/usePeerStatus.js.map +0 -1
  168. package/build/hooks/usePeers.d.ts +0 -98
  169. package/build/hooks/usePeers.d.ts.map +0 -1
  170. package/build/hooks/usePeers.js +0 -81
  171. package/build/hooks/usePeers.js.map +0 -1
  172. package/build/hooks/usePermissions.d.ts +0 -24
  173. package/build/hooks/usePermissions.d.ts.map +0 -1
  174. package/build/hooks/usePermissions.js +0 -61
  175. package/build/hooks/usePermissions.js.map +0 -1
  176. package/build/hooks/useReconnection.d.ts +0 -11
  177. package/build/hooks/useReconnection.d.ts.map +0 -1
  178. package/build/hooks/useReconnection.js +0 -14
  179. package/build/hooks/useReconnection.js.map +0 -1
  180. package/build/hooks/useSandbox.d.ts +0 -20
  181. package/build/hooks/useSandbox.d.ts.map +0 -1
  182. package/build/hooks/useSandbox.js +0 -54
  183. package/build/hooks/useSandbox.js.map +0 -1
  184. package/build/hooks/useScreenShare.d.ts +0 -37
  185. package/build/hooks/useScreenShare.d.ts.map +0 -1
  186. package/build/hooks/useScreenShare.js +0 -52
  187. package/build/hooks/useScreenShare.js.map +0 -1
  188. package/build/hooks/useUpdatePeerMetadata.d.ts +0 -15
  189. package/build/hooks/useUpdatePeerMetadata.d.ts.map +0 -1
  190. package/build/hooks/useUpdatePeerMetadata.js +0 -21
  191. package/build/hooks/useUpdatePeerMetadata.js.map +0 -1
  192. package/build/index.d.ts +0 -36
  193. package/build/index.d.ts.map +0 -1
  194. package/build/index.js +0 -30
  195. package/build/index.js.map +0 -1
  196. package/build/livestream/components/LivestreamStreamer.d.ts +0 -24
  197. package/build/livestream/components/LivestreamStreamer.d.ts.map +0 -1
  198. package/build/livestream/components/LivestreamStreamer.js +0 -12
  199. package/build/livestream/components/LivestreamStreamer.js.map +0 -1
  200. package/build/livestream/components/LivestreamViewer.d.ts +0 -46
  201. package/build/livestream/components/LivestreamViewer.d.ts.map +0 -1
  202. package/build/livestream/components/LivestreamViewer.js +0 -10
  203. package/build/livestream/components/LivestreamViewer.js.map +0 -1
  204. package/build/livestream/hooks/useLivestreamStreamer.d.ts +0 -65
  205. package/build/livestream/hooks/useLivestreamStreamer.d.ts.map +0 -1
  206. package/build/livestream/hooks/useLivestreamStreamer.js +0 -72
  207. package/build/livestream/hooks/useLivestreamStreamer.js.map +0 -1
  208. package/build/livestream/hooks/useLivestreamViewer.d.ts +0 -36
  209. package/build/livestream/hooks/useLivestreamViewer.d.ts.map +0 -1
  210. package/build/livestream/hooks/useLivestreamViewer.js +0 -73
  211. package/build/livestream/hooks/useLivestreamViewer.js.map +0 -1
  212. package/build/livestream/index.d.ts +0 -11
  213. package/build/livestream/index.d.ts.map +0 -1
  214. package/build/livestream/index.js +0 -6
  215. package/build/livestream/index.js.map +0 -1
  216. package/build/protos/fishjam/peer_notifications.d.ts +0 -63
  217. package/build/protos/fishjam/peer_notifications.d.ts.map +0 -1
  218. package/build/protos/fishjam/peer_notifications.js +0 -235
  219. package/build/protos/fishjam/peer_notifications.js.map +0 -1
  220. package/build/types.d.ts +0 -34
  221. package/build/types.d.ts.map +0 -1
  222. package/build/types.js +0 -2
  223. package/build/types.js.map +0 -1
  224. package/build/utils/config.d.ts +0 -6
  225. package/build/utils/config.d.ts.map +0 -1
  226. package/build/utils/config.js +0 -10
  227. package/build/utils/config.js.map +0 -1
  228. package/build/utils/errorListener.d.ts +0 -2
  229. package/build/utils/errorListener.d.ts.map +0 -1
  230. package/build/utils/errorListener.js +0 -15
  231. package/build/utils/errorListener.js.map +0 -1
  232. package/build/utils/eventPayloadValidator.d.ts +0 -3
  233. package/build/utils/eventPayloadValidator.d.ts.map +0 -1
  234. package/build/utils/eventPayloadValidator.js +0 -86
  235. package/build/utils/eventPayloadValidator.js.map +0 -1
  236. package/build/utils/getFishjamUrl.d.ts +0 -2
  237. package/build/utils/getFishjamUrl.d.ts.map +0 -1
  238. package/build/utils/getFishjamUrl.js +0 -10
  239. package/build/utils/getFishjamUrl.js.map +0 -1
  240. package/debug/package.json +0 -4
  241. package/expo-module.config.json +0 -19
  242. package/ios/CameraPermissionsRequester.swift +0 -117
  243. package/ios/Events.swift +0 -152
  244. package/ios/PictureInPictureManager.swift +0 -215
  245. package/ios/PipContainerView.swift +0 -56
  246. package/ios/PipContainerViewModule.swift +0 -48
  247. package/ios/RNFishjamClient.podspec +0 -30
  248. package/ios/RNFishjamClient.swift +0 -995
  249. package/ios/RNFishjamClientModule.swift +0 -289
  250. package/ios/RNFishjamProxy.swift +0 -12
  251. package/ios/Utils/PermissionUtils.swift +0 -16
  252. package/ios/Utils.swift +0 -23
  253. package/ios/VideoPreviewView.swift +0 -54
  254. package/ios/VideoPreviewViewModule.swift +0 -21
  255. package/ios/VideoRendererView.swift +0 -98
  256. package/ios/VideoRendererViewModule.swift +0 -23
  257. package/ios/extensions/AVCaptureDevice.swift +0 -20
  258. package/ios/managers/LocalCameraTracksChangedListenersManager.swift +0 -23
  259. package/ios/managers/TracksUpdateListenersManager.swift +0 -21
  260. package/livestream/package.json +0 -4
  261. package/plugin/broadcastExtensionFiles/FishjamBroadcastSampleHandler.swift +0 -50
  262. /package/plugin/broadcastExtensionFiles/{FishjamScreenBroadcastExtension.entitlements → ScreenBroadcastExtension.entitlements} +0 -0
@@ -1,995 +0,0 @@
1
- import AVKit
2
- import ExpoModulesCore
3
- import FishjamCloudClient
4
- import React
5
- import ReplayKit
6
- import WebRTC
7
-
8
- class RNFishjamClient: FishjamClientListener {
9
- static var fishjamClient: FishjamClient? = nil
10
- private var callKitManager: CallKitManager?
11
-
12
- var isMicrophoneOn = false
13
- var isCameraOn = false
14
- var isScreenShareOn = false
15
- var isAppScreenShareOn = false
16
- var isConnected = false
17
-
18
- private(set) var isCameraInitialized = false {
19
- didSet {
20
- emit(
21
- event: .currentCameraChanged(
22
- localCamera: currentCamera, isCameraOn: isCameraOn, isCameraInitialized: isCameraInitialized))
23
- }
24
- }
25
-
26
- var connectPromise: Promise? = nil {
27
- didSet {
28
- guard connectPromise != nil else { return }
29
-
30
- let timeout = DispatchTime.now() + .seconds(15)
31
- DispatchQueue.main.asyncAfter(deadline: timeout) { [weak self] in
32
- // If promise is still assigned it means it was not resolved in that time,
33
- // so close the client and reject it with timeout error.
34
- guard let promise = self?.connectPromise else { return }
35
- RNFishjamClient.fishjamClient?.leave { [weak self] in
36
- promise.reject("E_MEMBRANE_CONNECT", "Failed to connect: Fishjam server is not responding")
37
- self?.connectPromise = nil
38
- self?.peerStatus = .error
39
- }
40
- }
41
- }
42
- }
43
-
44
- var videoSimulcastConfig: SimulcastConfig = SimulcastConfig()
45
-
46
- var screenShareSimulcastConfig: SimulcastConfig = SimulcastConfig()
47
-
48
- var cameraId: String? = nil
49
-
50
- var audioSessionMode: AVAudioSession.Mode = .videoChat
51
- var errorMessage: String?
52
-
53
- var currentCamera: LocalCamera? { getLocalCameraTrack()?.currentCaptureDevice?.toLocalCamera() }
54
-
55
- private(set) var peerStatus: PeerStatus = .idle {
56
- didSet {
57
- emit(event: .peerStatusChanged(peerStatus: peerStatus))
58
- }
59
- }
60
-
61
- private(set) var reconnectionStatus: ReconnectionStatus = .idle {
62
- didSet {
63
- emit(event: .reconnectionStatusChanged(reconnectionStatus: reconnectionStatus))
64
- }
65
- }
66
-
67
- static private(set) var sendEvent: ((_ event: EmitableEvent) -> Void)?
68
-
69
- static var tracksUpdateListenersManager = TracksUpdateListenersManager()
70
- static var localCameraTracksChangedListenersManager = LocalCameraTracksChangedListenersManager()
71
-
72
- init(_ eventEmitter: @escaping (_ eventName: String, _ data: [String: Any?]) -> Void) {
73
- RNFishjamClient.sendEvent = { event in
74
- eventEmitter(event.event.name, event.data)
75
- }
76
- NotificationCenter.default.addObserver(
77
- self,
78
- selector: #selector(onRouteChangeNotification),
79
- name: AVAudioSession.routeChangeNotification,
80
- object: nil
81
- )
82
- }
83
-
84
- deinit {
85
- RNFishjamClient.sendEvent = nil
86
- }
87
-
88
- private func getSimulcastConfigFromOptions(simulcastConfig: RNSimulcastConfig) throws -> SimulcastConfig {
89
- // iOS has a limit of 3 hardware encoders
90
- // 3 simulcast layers + 1 screen share layer = 4, which is too much
91
- // so we limit simulcast layers to 2
92
- let activeEncodings: [TrackEncoding] = [.l, .h]
93
-
94
- return SimulcastConfig(
95
- enabled: simulcastConfig.enabled,
96
- activeEncodings: activeEncodings
97
- )
98
- }
99
-
100
- func create() {
101
- RNFishjamClient.fishjamClient = FishjamClient(listener: self)
102
- }
103
-
104
- func getVideoParametersFromOptions(connectionOptions: CameraConfig) throws -> VideoParameters {
105
- let videoQuality = connectionOptions.quality
106
- let flipDimensions = connectionOptions.flipDimensions
107
- let simulcastConfig = try getSimulcastConfigFromOptions(simulcastConfig: connectionOptions.simulcastConfig)
108
-
109
- let preset: VideoParameters = {
110
- switch videoQuality {
111
- case "QVGA169":
112
- return VideoParameters.presetQVGA169
113
- case "VGA169":
114
- return VideoParameters.presetVGA169
115
- case "VQHD169":
116
- return VideoParameters.presetQHD169
117
- case "HD169":
118
- return VideoParameters.presetHD169
119
- case "FHD169":
120
- return VideoParameters.presetFHD169
121
- case "QVGA43":
122
- return VideoParameters.presetQVGA43
123
- case "VGA43":
124
- return VideoParameters.presetVGA43
125
- case "VQHD43":
126
- return VideoParameters.presetQHD43
127
- case "HD43":
128
- return VideoParameters.presetHD43
129
- case "FHD43":
130
- return VideoParameters.presetFHD43
131
- default:
132
- return VideoParameters.presetVGA169
133
- }
134
- }()
135
- let videoParameters = VideoParameters(
136
- dimensions: flipDimensions ? preset.dimensions.flipped : preset.dimensions,
137
- maxBandwidth: preset.maxBandwidth,
138
- simulcastConfig: simulcastConfig
139
- )
140
- return videoParameters
141
- }
142
-
143
- private var localEndpoint: Endpoint? {
144
- RNFishjamClient.fishjamClient?.getLocalEndpoint()
145
- }
146
-
147
- private func getLocalCameraTrack() -> LocalCameraTrack? {
148
- return localEndpoint?.tracks.compactMap { $0.value as? LocalCameraTrack }.first
149
-
150
- }
151
-
152
- private func getLocalAudioTrack() -> LocalAudioTrack? {
153
- return localEndpoint?.tracks.compactMap { $0.value as? LocalAudioTrack }.first
154
-
155
- }
156
-
157
- private func getLocalScreenBroadcastTrack() -> LocalBroadcastScreenShareTrack? {
158
- return localEndpoint?.tracks.compactMap { $0.value as? LocalBroadcastScreenShareTrack }.first
159
-
160
- }
161
-
162
- private func getLocalScreenAppTrack() -> LocalAppScreenShareTrack? {
163
- return localEndpoint?.tracks.compactMap { $0.value as? LocalAppScreenShareTrack }.first
164
- }
165
-
166
- private func ensureCreated() throws {
167
- if RNFishjamClient.fishjamClient == nil {
168
- throw Exception(
169
- name: "E_NOT_CREATED",
170
- description: "Client not created. Call create"
171
- )
172
- }
173
- }
174
-
175
- private func ensureConnected() throws {
176
- if !isConnected {
177
- throw Exception(
178
- name: "E_NOT_CONNECTED",
179
- description:
180
- "Client not connected to server yet. Make sure to call connect() first!")
181
- }
182
- }
183
-
184
- private func ensureCameraTrack() throws {
185
- if getLocalCameraTrack() == nil {
186
- throw Exception(
187
- name: "E_NO_LOCAL_VIDEO_TRACK",
188
- description: "No local video track. Make sure to call connect() first!")
189
- }
190
- }
191
-
192
- private func ensureAudioTrack() throws {
193
- if getLocalAudioTrack() == nil {
194
- throw Exception(
195
- name: "E_NO_LOCAL_AUDIO_TRACK",
196
- description: "No local audio track. Make sure to call connect() first!")
197
- }
198
- }
199
-
200
- private func ensureScreenBroadcastTrack() throws {
201
- if getLocalScreenBroadcastTrack() == nil {
202
- throw Exception(
203
- name: "E_NO_LOCAL_SCREENSHARE_TRACK",
204
- description: "No local screen broadcast track. Make sure to toggle screen broadcast on first!")
205
- }
206
- }
207
-
208
- private func ensureScreenAppTrack() throws {
209
- if getLocalScreenAppTrack() == nil {
210
- throw Exception(
211
- name: "E_NO_LOCAL_SCREENSHARE_TRACK",
212
- description: "No local screen app track. Make sure to toggle screen app on first!")
213
- }
214
- }
215
-
216
- func onAuthError(reason: FishjamCloudClient.AuthError) {
217
- if let connectPromise = connectPromise {
218
- connectPromise.reject("E_MEMBRANE_CONNECT", "Failed to connect: socket error")
219
- }
220
- connectPromise = nil
221
- peerStatus = .error
222
- }
223
-
224
- func joinRoom(
225
- url: String, peerToken: String, peerMetadata: [String: Any], config: ConnectConfig,
226
- promise: Promise
227
- ) {
228
- guard connectPromise == nil && peerStatus != .connected else {
229
- promise.reject("E_MEMBRANE_CONNECT", "Room already joined or it's connecting. You must call leaveRoom() before calling joinRoom() again.")
230
- return
231
- }
232
-
233
- peerStatus = .connecting
234
- connectPromise = promise
235
-
236
- let reconnectConfig = FishjamCloudClient.ReconnectConfig(
237
- maxAttempts: config.reconnectConfig.maxAttempts, initialDelayMs: config.reconnectConfig.initialDelayMs,
238
- delayMs: config.reconnectConfig.delayMs)
239
-
240
- RNFishjamClient.fishjamClient?.connect(
241
- config: FishjamCloudClient.ConnectConfig(
242
- websocketUrl: url, token: peerToken, peerMetadata: peerMetadata.toMetadata(),
243
- reconnectConfig: reconnectConfig
244
- ))
245
-
246
- }
247
-
248
- func leaveRoom() {
249
- connectPromise?.reject("E_MEMBRANE_CONNECT", "leaveRoom called before connected")
250
- connectPromise = nil
251
- if isScreenShareOn {
252
- let screenShareExtensionBundleId =
253
- Bundle.main.infoDictionary?["ScreenShareExtensionBundleId"] as? String
254
- DispatchQueue.main.async {
255
- RPSystemBroadcastPickerView.show(for: screenShareExtensionBundleId)
256
- }
257
- }
258
- isMicrophoneOn = false
259
- isCameraOn = false
260
- isScreenShareOn = false
261
- isAppScreenShareOn = false
262
- isConnected = false
263
- isCameraInitialized = false
264
- RNFishjamClient.fishjamClient?.leave { [weak self] in
265
- self?.emitEndpoints()
266
- }
267
- }
268
-
269
- private let startCameraLock = NSLock()
270
-
271
- func startCamera(config: CameraConfig) async throws -> Bool {
272
- #if targetEnvironment(simulator)
273
- emit(
274
- event: .warning(
275
- message: "Camera is not supported on simulator."))
276
- return false
277
- #endif
278
-
279
- try ensureCreated()
280
-
281
- guard await PermissionUtils.requestCameraPermission() else {
282
- emit(event: .warning(message: "Camera permission not granted."))
283
- return false
284
- }
285
-
286
- return try startCameraLock.withLock {
287
- guard !isCameraInitialized else {
288
- return true
289
- }
290
-
291
- let cameraTrack = try createCameraTrack(config: config)
292
- cameraTrack.captureDeviceChangedListener = self
293
- setCameraTrackState(cameraTrack, enabled: config.cameraEnabled)
294
- emitEndpoints()
295
- isCameraInitialized = true
296
- return true
297
- }
298
- }
299
-
300
- private func createCameraTrack(config: CameraConfig) throws -> LocalCameraTrack {
301
- try ensureCreated()
302
- let videoParameters = try getVideoParametersFromOptions(connectionOptions: config)
303
- videoSimulcastConfig = try getSimulcastConfigFromOptions(simulcastConfig: config.simulcastConfig)
304
- return RNFishjamClient.fishjamClient!.createVideoTrack(
305
- videoParameters: videoParameters,
306
- metadata: config.videoTrackMetadata.toMetadata(),
307
- captureDeviceName: config.cameraId
308
- )
309
- }
310
-
311
- private func setCameraTrackState(_ cameraTrack: LocalCameraTrack, enabled: Bool) {
312
- cameraTrack.enabled = enabled
313
- isCameraOn = enabled
314
- emit(
315
- event: .currentCameraChanged(
316
- localCamera: cameraTrack.currentCaptureDevice?.toLocalCamera(), isCameraOn: enabled,
317
- isCameraInitialized: isCameraInitialized))
318
- RNFishjamClient.localCameraTracksChangedListenersManager.notifyListeners()
319
- }
320
-
321
- func toggleCamera() throws -> Bool {
322
- try ensureCameraTrack()
323
- setCameraTrackState(getLocalCameraTrack()!, enabled: !isCameraOn)
324
- return isCameraOn
325
- }
326
-
327
- func flipCamera() throws {
328
- try ensureCameraTrack()
329
- getLocalCameraTrack()?.flipCamera()
330
- }
331
-
332
- func switchCamera(cameraId: String) throws {
333
- try ensureCameraTrack()
334
- getLocalCameraTrack()?.switchCamera(deviceId: cameraId)
335
-
336
- }
337
-
338
- func toggleMicrophone() async throws -> Bool {
339
- if isMicrophoneOn {
340
- try stopMicrophone()
341
- } else {
342
- try await startMicrophone()
343
- }
344
-
345
- return isMicrophoneOn
346
- }
347
-
348
- func startMicrophone() async throws {
349
- // If microphone track already exist, just enable it
350
- if let microphoneTrack = getLocalAudioTrack() {
351
- try setMicrophoneTrackState(microphoneTrack, enabled: true)
352
- } else {
353
- guard await PermissionUtils.requestMicrophonePermission() else {
354
- emit(event: .warning(message: "Microphone permission not granted."))
355
- return
356
- }
357
- let microphoneTrack = RNFishjamClient.fishjamClient!.createAudioTrack(
358
- metadata: getMicrophoneTrackMetadata(isEnabled: true).toMetadata())
359
- setAudioSessionMode()
360
- try setMicrophoneTrackState(microphoneTrack, enabled: true)
361
- emitEndpoints()
362
- }
363
- }
364
-
365
- func stopMicrophone() throws {
366
- guard let microphoneTrack = getLocalAudioTrack() else {
367
- return
368
- }
369
- try setMicrophoneTrackState(microphoneTrack, enabled: false)
370
- }
371
-
372
- private func getMicrophoneTrackMetadata(isEnabled: Bool) -> [String: Any] {
373
- return [
374
- "active": isEnabled,
375
- "paused": !isEnabled, //TODO: FCE-711
376
- "type": "microphone",
377
- ]
378
- }
379
-
380
- private func setMicrophoneTrackState(_ microphoneTrack: LocalAudioTrack, enabled: Bool) throws {
381
- microphoneTrack.enabled = enabled
382
- isMicrophoneOn = enabled
383
- try updateLocalAudioTrackMetadata(metadata: getMicrophoneTrackMetadata(isEnabled: enabled))
384
- emit(event: .isMicrophoneOn(enabled: enabled))
385
- }
386
-
387
- func setAudioSessionMode() {
388
- guard let localAudioTrack = getLocalAudioTrack() else {
389
- return
390
- }
391
-
392
- switch self.audioSessionMode {
393
- case AVAudioSession.Mode.videoChat:
394
- localAudioTrack.setVideoChatMode()
395
- break
396
- case AVAudioSession.Mode.voiceChat:
397
- localAudioTrack.setVoiceChatMode()
398
- break
399
- default:
400
- localAudioTrack.setVideoChatMode()
401
- break
402
- }
403
- }
404
-
405
- func toggleScreenShare(screenShareOptions: ScreenShareOptions) throws {
406
- try ensureCreated()
407
- try ensureConnected()
408
-
409
- guard isAppScreenShareOn == false else {
410
- emit(event: .warning(message: "Screensharing screen not available during screensharing app."))
411
- return
412
- }
413
- guard let screenShareExtensionBundleId = Bundle.main.infoDictionary?["ScreenShareExtensionBundleId"] as? String
414
- else {
415
- throw Exception(
416
- name: "E_NO_BUNDLE_ID_SET",
417
- description:
418
- "No screen share extension bundle id set. Please set ScreenShareExtensionBundleId in Info.plist"
419
- )
420
- }
421
- guard let appGroupName = Bundle.main.infoDictionary?["AppGroupName"] as? String
422
- else {
423
- throw Exception(
424
- name: "E_NO_APP_GROUP_SET",
425
- description: "No app group name set. Please set AppGroupName in Info.plist")
426
- }
427
-
428
- guard !isScreenShareOn else {
429
- DispatchQueue.main.async {
430
- RPSystemBroadcastPickerView.show(for: screenShareExtensionBundleId)
431
- }
432
- return
433
- }
434
-
435
- if RNFishjamClient.fishjamClient?.lastSdpAnswer?.detectedCodec != .h264 {
436
- emit(event: .warning(
437
- message: "Incompatible codec detected: \(RNFishjamClient.fishjamClient?.lastSdpAnswer?.detectedCodec?.rawValue ?? "Unknown"). Screen sharing requires H264, otherwise UploadBroadcastExtenstion will crash due to memory pressure (no hardware acceleration). " +
438
- "Please configure your Fishjam room to use H264 codec for better screen sharing performance."
439
- ))
440
- }
441
-
442
- let simulcastConfig = try getSimulcastConfigFromOptions(simulcastConfig: screenShareOptions.simulcastConfig)
443
-
444
- screenShareSimulcastConfig = simulcastConfig
445
- let screenShareMetadata = screenShareOptions.screenShareMetadata.toMetadata()
446
- let videoParameters = getScreenShareVideoParameters(options: screenShareOptions)
447
- RNFishjamClient.fishjamClient!.prepareForScreenBroadcast(
448
- appGroup: appGroupName,
449
- videoParameters: videoParameters,
450
- metadata: screenShareMetadata,
451
- canStart: {
452
- if self.isAppScreenShareOn {
453
- self.emit(event: .warning(message: "Screensharing screen not available during screensharing app."))
454
- }
455
- return !self.isAppScreenShareOn
456
- },
457
- onStart: { [weak self] in
458
- guard let self else { return }
459
- do {
460
- try setScreenShareTrackState(enabled: true)
461
- } catch {
462
- os_log(
463
- "Error starting screen share: %{public}s", log: log, type: .error,
464
- String(describing: error)
465
- )
466
- }
467
-
468
- },
469
- onStop: { [weak self] in
470
- guard let self else { return }
471
- do {
472
- try setScreenShareTrackState(enabled: false)
473
- } catch {
474
- os_log(
475
- "Error stopping screen share: %{public}s", log: log, type: .error,
476
- String(describing: error)
477
- )
478
- }
479
- }
480
- )
481
- DispatchQueue.main.async {
482
- RPSystemBroadcastPickerView.show(for: screenShareExtensionBundleId)
483
- }
484
- }
485
-
486
- private func setScreenShareTrackState(enabled: Bool) throws {
487
- isScreenShareOn = enabled
488
- emit(event: .isScreenShareOn(enabled: enabled))
489
- }
490
-
491
- func startScreenAppShare(screenShareOptions: ScreenShareOptions) throws {
492
- try ensureCreated()
493
- try ensureConnected()
494
-
495
- let simulcastConfig = try getSimulcastConfigFromOptions(simulcastConfig: screenShareOptions.simulcastConfig)
496
-
497
- screenShareSimulcastConfig = simulcastConfig
498
- let metadata = screenShareOptions.screenShareMetadata.toMetadata()
499
- let videoParameters = getScreenShareVideoParameters(options: screenShareOptions)
500
- let screenAppTrack = RNFishjamClient.fishjamClient!.createScreenAppTrack(
501
- videoParameters: videoParameters, metadata: metadata)
502
- setScreenAppTrackState(screenAppTrack, enabled: true)
503
- emitEndpoints()
504
- }
505
-
506
- func stopAppScreenShare() throws {
507
- try ensureScreenAppTrack()
508
- let track = getLocalScreenAppTrack()!
509
- setScreenAppTrackState(track, enabled: false)
510
- RNFishjamClient.fishjamClient?.removeTrack(trackId: track.id)
511
- emitEndpoints()
512
- }
513
-
514
- func toggleAppScreenShare(screenShareOptions: ScreenShareOptions) throws {
515
- guard isScreenShareOn == false else {
516
- emit(event: .warning(message: "App screensharing not available during screensharing."))
517
- return
518
- }
519
- if getLocalScreenAppTrack() != nil {
520
- try stopAppScreenShare()
521
- } else {
522
- try startScreenAppShare(screenShareOptions: screenShareOptions)
523
- }
524
- }
525
-
526
- private func setScreenAppTrackState(_ track: LocalAppScreenShareTrack, enabled: Bool) {
527
- track.enabled = enabled
528
- isAppScreenShareOn = enabled
529
- emit(event: .isAppScreenShareOn(enabled: enabled))
530
- }
531
-
532
- //returns local endpoint and remote endpoints
533
- static func getLocalAndRemoteEndpoints() -> [Endpoint] {
534
- let localEndpoint = RNFishjamClient.fishjamClient!.getLocalEndpoint()
535
- let remoteEndpoints = RNFishjamClient.fishjamClient!.getRemoteEndpoints()
536
- return [localEndpoint] + remoteEndpoints
537
- }
538
-
539
- func getPeers() -> [[String: Any?]] {
540
- let endpoints = RNFishjamClient.getLocalAndRemoteEndpoints()
541
- return endpoints.compactMap { endpoint in
542
- [
543
- "id": endpoint.id,
544
- "isLocal": endpoint.id == RNFishjamClient.fishjamClient!.getLocalEndpoint().id,
545
- "metadata": endpoint.metadata.toDict(),
546
- "tracks": endpoint.tracks.values.compactMap { track -> [String: Any?]? in
547
- switch track {
548
- case let track as RemoteVideoTrack:
549
- return [
550
- "id": track.id,
551
- "type": "Video",
552
- "metadata": track.metadata.toDict(),
553
- "encoding": track.encoding?.description,
554
- "encodingReason": track.encodingReason?.rawValue,
555
- "aspectRatio": track.dimensions?.aspectRatio,
556
- ]
557
-
558
- case let track as RemoteAudioTrack:
559
- return [
560
- "id": track.id,
561
- "type": "Audio",
562
- "metadata": track.metadata.toDict(),
563
- "vadStatus": track.vadStatus == .speech ? "speech" : "silence",
564
- ]
565
-
566
- case let track as LocalVideoTrack:
567
- return [
568
- "id": track.id,
569
- "type": "Video",
570
- "metadata": track.metadata.toDict(),
571
- "aspectRatio": track.dimensions?.aspectRatio,
572
- ]
573
- case let track as LocalAudioTrack:
574
- return [
575
- "id": track.id,
576
- "type": "Audio",
577
- "metadata": track.metadata.toDict(),
578
- ]
579
-
580
- default:
581
- return nil
582
- }
583
- },
584
- ]
585
- }
586
- }
587
-
588
- func getCaptureDevices() -> [[String: Any]] {
589
- let devices = LocalCameraTrack.getCaptureDevices()
590
- return devices.map { $0.toLocalCamera() }
591
- }
592
-
593
- func updatePeerMetadata(metadata: [String: Any]) throws {
594
- try ensureConnected()
595
- RNFishjamClient.fishjamClient?.updatePeerMetadata(metadata: metadata.toMetadata())
596
- }
597
-
598
- func updateTrackMetadata(trackId: String, metadata: [String: Any]) {
599
- RNFishjamClient.fishjamClient?.updateTrackMetadata(trackId: trackId, metadata: metadata.toMetadata())
600
- emitEndpoints()
601
- }
602
-
603
- func updateLocalVideoTrackMetadata(metadata: [String: Any]) throws {
604
- try ensureCameraTrack()
605
- if let track = getLocalCameraTrack() {
606
- updateTrackMetadata(trackId: track.id, metadata: metadata)
607
- }
608
- }
609
-
610
- private func updateLocalAudioTrackMetadata(metadata: [String: Any]) throws {
611
- try ensureAudioTrack()
612
- if let track = getLocalAudioTrack() {
613
- updateTrackMetadata(trackId: track.id, metadata: metadata)
614
- }
615
- }
616
-
617
- func updateLocalScreenShareTrackMetadata(metadata: [String: Any]) throws {
618
- try ensureScreenBroadcastTrack()
619
- if let track = getLocalScreenBroadcastTrack() {
620
- updateTrackMetadata(trackId: track.id, metadata: metadata)
621
- }
622
- }
623
-
624
- private func toggleTrackEncoding(encoding: String, trackId: String, simulcastConfig: SimulcastConfig) throws
625
- -> SimulcastConfig
626
- {
627
- try ensureCreated()
628
- let trackEncoding = try TrackEncoding(encoding)
629
- let trackEncodingActive = simulcastConfig.activeEncodings.contains(trackEncoding)
630
- var updatedEncodings = simulcastConfig.activeEncodings
631
-
632
- if trackEncodingActive {
633
- RNFishjamClient.fishjamClient?.disableTrackEncoding(trackId: trackId, encoding: trackEncoding)
634
- updatedEncodings.removeAll(where: { encoding in trackEncoding == encoding })
635
- } else {
636
- RNFishjamClient.fishjamClient?.enableTrackEncoding(trackId: trackId, encoding: trackEncoding)
637
- updatedEncodings += [trackEncoding]
638
- }
639
-
640
- return SimulcastConfig(enabled: true, activeEncodings: updatedEncodings)
641
- }
642
-
643
- func toggleScreenShareTrackEncoding(encoding: String) throws -> [String: Any?] {
644
- try ensureScreenBroadcastTrack()
645
- if let track = getLocalScreenBroadcastTrack() {
646
- screenShareSimulcastConfig = try toggleTrackEncoding(
647
- encoding: encoding, trackId: track.id, simulcastConfig: screenShareSimulcastConfig)
648
- }
649
-
650
- return EmitableEvent.simulcastConfigUpdate(simulcastConfig: screenShareSimulcastConfig).data
651
- }
652
-
653
- func setScreenShareTrackBandwidth(bandwidth: Int) throws {
654
- try ensureScreenBroadcastTrack()
655
- if let track = getLocalScreenBroadcastTrack() {
656
- RNFishjamClient.fishjamClient?.setTrackBandwidth(trackId: track.id, bandwidthLimit: bandwidth)
657
- }
658
- }
659
-
660
- func setScreenShareTrackEncodingBandwidth(encoding: String, bandwidth: Int) throws {
661
- try ensureScreenBroadcastTrack()
662
- if let track = getLocalScreenBroadcastTrack() {
663
- RNFishjamClient.fishjamClient?.setEncodingBandwidth(
664
- trackId: track.id, encoding: encoding, bandwidthLimit: bandwidth)
665
- }
666
- }
667
-
668
- func setTargetTrackEncoding(trackId: String, encoding: String) throws {
669
- try ensureConnected()
670
- let trackEncoding = try TrackEncoding(encoding)
671
- RNFishjamClient.fishjamClient?.setTargetTrackEncoding(trackId: trackId, encoding: trackEncoding)
672
- }
673
-
674
- func toggleVideoTrackEncoding(encoding: String) throws -> [String: Any?] {
675
- try ensureCameraTrack()
676
- try ensureConnected()
677
-
678
- let track = getLocalCameraTrack()!
679
- videoSimulcastConfig = try toggleTrackEncoding(
680
- encoding: encoding, trackId: track.id, simulcastConfig: videoSimulcastConfig)
681
-
682
- let event = EmitableEvent.simulcastConfigUpdate(simulcastConfig: videoSimulcastConfig)
683
- emit(event: event)
684
-
685
- return event.data
686
- }
687
-
688
- func setVideoTrackEncodingBandwidth(encoding: String, bandwidth: Int) throws {
689
- try ensureCameraTrack()
690
- let track = getLocalCameraTrack()!
691
- RNFishjamClient.fishjamClient?.setEncodingBandwidth(
692
- trackId: track.id, encoding: encoding, bandwidthLimit: bandwidth)
693
- }
694
-
695
- func setVideoTrackBandwidth(bandwidth: Int) throws {
696
- try ensureCameraTrack()
697
- let track = getLocalCameraTrack()!
698
- RNFishjamClient.fishjamClient?.setTrackBandwidth(trackId: track.id, bandwidthLimit: bandwidth)
699
- }
700
-
701
- func changeWebRTCLoggingSeverity(severity: String) throws {
702
- switch severity {
703
- case "verbose":
704
- RNFishjamClient.fishjamClient?.changeWebRTCLoggingSeverity(severity: .verbose)
705
- case "info":
706
- RNFishjamClient.fishjamClient?.changeWebRTCLoggingSeverity(severity: .info)
707
- case "warning":
708
- RNFishjamClient.fishjamClient?.changeWebRTCLoggingSeverity(severity: .warning)
709
- case "error":
710
- RNFishjamClient.fishjamClient?.changeWebRTCLoggingSeverity(severity: .error)
711
- case "none":
712
- RNFishjamClient.fishjamClient?.changeWebRTCLoggingSeverity(severity: .none)
713
- default:
714
- throw Exception(
715
- name: "E_INVALID_SEVERITY_LEVEL",
716
- description: "Severity with name=\(severity) not found")
717
- }
718
- }
719
-
720
- private func rtcOutboundStatsToRNMap(obj: RTCOutboundStats) -> [String: Any] {
721
- var innerMap: [String: Double] = [:]
722
-
723
- innerMap["bandwidth"] = obj.qualityLimitationDurations?.bandwidth ?? 0.0
724
- innerMap["cpu"] = obj.qualityLimitationDurations?.cpu ?? 0.0
725
- innerMap["none"] = obj.qualityLimitationDurations?.none ?? 0.0
726
- innerMap["other"] = obj.qualityLimitationDurations?.other ?? 0.0
727
-
728
- var res: [String: Any] = [:]
729
- res["kind"] = obj.kind
730
- res["rid"] = obj.rid
731
- res["bytesSent"] = obj.bytesSent
732
- res["targetBitrate"] = obj.targetBitrate
733
- res["packetsSent"] = obj.packetsSent
734
- res["framesEncoded"] = obj.framesEncoded
735
- res["framesPerSecond"] = obj.framesPerSecond
736
- res["frameWidth"] = obj.frameWidth
737
- res["frameHeight"] = obj.frameHeight
738
- res["qualityLimitationDurations"] = innerMap
739
-
740
- return res
741
- }
742
-
743
- private func rtcInboundStatsToRNMap(obj: RTCInboundStats) -> [String: Any] {
744
- var res: [String: Any] = [:]
745
- res["kind"] = obj.kind
746
- res["jitter"] = obj.jitter
747
- res["packetsLost"] = obj.packetsLost
748
- res["packetsReceived"] = obj.packetsReceived
749
- res["bytesReceived"] = obj.bytesReceived
750
- res["framesReceived"] = obj.framesReceived
751
- res["frameWidth"] = obj.frameWidth
752
- res["frameHeight"] = obj.frameHeight
753
- res["framesPerSecond"] = obj.framesPerSecond
754
- res["framesDropped"] = obj.framesDropped
755
-
756
- return res
757
- }
758
-
759
- func getStatistics() async throws -> [String: Any] {
760
- try ensureCreated()
761
-
762
- let stats = await RNFishjamClient.fishjamClient!.getStats()
763
-
764
- let pairs = stats.map { (key, value) in
765
- let rnValue =
766
- value is RTCOutboundStats
767
- ? rtcOutboundStatsToRNMap(obj: value as! RTCOutboundStats)
768
- : rtcInboundStatsToRNMap(obj: value as! RTCInboundStats)
769
- return (key, rnValue)
770
- }
771
-
772
- return Dictionary(uniqueKeysWithValues: pairs)
773
- }
774
-
775
- private func getScreenShareVideoParameters(options: ScreenShareOptions) -> VideoParameters {
776
- let preset: VideoParameters
777
- switch options.quality {
778
- case "VGA":
779
- preset = VideoParameters.presetScreenShareVGA
780
- case "HD5":
781
- preset = VideoParameters.presetScreenShareHD5
782
- case "HD15":
783
- preset = VideoParameters.presetScreenShareHD15
784
- case "FHD15":
785
- preset = VideoParameters.presetScreenShareFHD15
786
- case "FHD30":
787
- preset = VideoParameters.presetScreenShareFHD30
788
- default:
789
- preset = VideoParameters.presetScreenShareHD15
790
- }
791
- return VideoParameters(
792
- dimensions: preset.dimensions.flipped,
793
- maxFps: preset.maxFps,
794
- simulcastConfig: screenShareSimulcastConfig
795
- )
796
- }
797
-
798
- func emit(event: EmitableEvent) {
799
- DispatchQueue.main.async {
800
- RNFishjamClient.sendEvent?(event)
801
- }
802
- }
803
-
804
- func emitEndpoints() {
805
- emit(event: .peersUpdate())
806
- RNFishjamClient.tracksUpdateListenersManager.notifyListeners()
807
- }
808
-
809
- func onJoined(peerID: String, peersInRoom: [String: Endpoint]) {
810
- isConnected = true
811
- connectPromise?.resolve(nil)
812
- connectPromise = nil
813
- emitEndpoints()
814
- peerStatus = .connected
815
- }
816
-
817
- func onJoinError(metadata: Any) {
818
- let reason = (metadata as? [String: Any])?["reason"] as? String ?? "Unknown error"
819
- connectPromise?.reject("E_MEMBRANE_CONNECT", "Failed to join room, reason: \(reason)")
820
- connectPromise = nil
821
- }
822
-
823
- private func addOrUpdateTrack(_ track: Track) {
824
- if track is RemoteAudioTrack {
825
- (track as! RemoteAudioTrack).setVadChangedListener { track in
826
- self.emitEndpoints()
827
- }
828
- }
829
-
830
- if track is RemoteVideoTrack {
831
- (track as! RemoteVideoTrack).setOnEncodingChangedListener { track in
832
- self.emitEndpoints()
833
- }
834
- }
835
-
836
- RNFishjamClient.tracksUpdateListenersManager.notifyListeners()
837
-
838
- emitEndpoints()
839
- }
840
-
841
- func onTrackReady(track: Track) {
842
- addOrUpdateTrack(track)
843
- }
844
-
845
- func onTrackAdded(track: Track) {
846
- emitEndpoints()
847
- }
848
-
849
- func onTrackRemoved(track: Track) {
850
- emitEndpoints()
851
- }
852
-
853
- func onTrackUpdated(track: Track) {
854
- emitEndpoints()
855
- }
856
-
857
- func onPeerJoined(endpoint: Endpoint) {
858
- emitEndpoints()
859
- }
860
-
861
- func onPeerLeft(endpoint: Endpoint) {
862
- emitEndpoints()
863
- }
864
-
865
- func onPeerUpdated(endpoint: Endpoint) {
866
- emitEndpoints()
867
- }
868
-
869
- func onSocketClose(code: UInt16, reason: String) {
870
- if let connectPromise = connectPromise {
871
- connectPromise.reject(
872
- "E_MEMBRANE_CONNECT", "Failed to connect: socket close, code: \(code), reason: \(reason)")
873
- }
874
- connectPromise = nil
875
- peerStatus = .idle
876
- }
877
-
878
- func onSocketError(_ errorMessage: String? = nil) {
879
- connectPromise?.reject("E_MEMBRANE_CONNECT", "Failed to connect: socket error\(errorMessage.map { " - \($0)" } ?? "")")
880
- connectPromise = nil
881
- peerStatus = .error
882
- }
883
-
884
- func onSocketOpen() {}
885
-
886
- func onDisconnected() {
887
- peerStatus = .idle
888
- }
889
-
890
- func selectAudioSessionMode(sessionMode: String) throws {
891
- switch sessionMode {
892
- case "videoChat":
893
- self.audioSessionMode = AVAudioSession.Mode.videoChat
894
- break
895
- case "voiceChat":
896
- self.audioSessionMode = AVAudioSession.Mode.voiceChat
897
- break
898
- default:
899
- throw Exception(
900
- name: "E_MEMBRANE_AUDIO_SESSION",
901
- description:
902
- "Invalid audio session mode: \(sessionMode). Supported modes: videoChat, voiceChat"
903
- )
904
- }
905
- setAudioSessionMode()
906
- }
907
-
908
- func showAudioRoutePicker() {
909
- DispatchQueue.main.sync {
910
- let pickerView = AVRoutePickerView()
911
- if let button = pickerView.subviews.first(where: { $0 is UIButton }) as? UIButton {
912
- button.sendActions(for: .touchUpInside)
913
- }
914
- }
915
- }
916
-
917
- func startAudioSwitcher() {
918
- onRouteChangeNotification()
919
- }
920
-
921
- @objc func onRouteChangeNotification() {
922
- emit(event: .audioDeviceUpdate(currentRoute: AVAudioSession.sharedInstance().currentRoute))
923
- }
924
-
925
- func onBandwidthEstimationChanged(estimation: Int) {
926
- emit(event: .bandwidthEstimation(estimation: estimation))
927
- }
928
-
929
- func onReconnectionStarted() {
930
- reconnectionStatus = .reconnecting
931
- }
932
-
933
- func onReconnected() {
934
- reconnectionStatus = .idle
935
- }
936
-
937
- func onReconnectionRetriesLimitReached() {
938
- reconnectionStatus = .error
939
- }
940
-
941
- func onIncompatibleTracksDetected() {
942
- // TODO: FCE-1215 Add proper url after docs are updated
943
- emit(
944
- event: .warning(
945
- message:
946
- "Incompatible track detected. This usually means your device is missing codecs negotiated for the room. Visit https://docs.fishjam.io/category/react-native-integration for information."
947
- ))
948
- }
949
-
950
- static func add(customSource: CustomSource) async throws {
951
- try await fishjamClient?.create(customSource: customSource)
952
- }
953
-
954
- static func remove(customSource: CustomSource) {
955
- fishjamClient?.remove(customSource: customSource)
956
- }
957
-
958
- public func startCallKitSessionWith(displayName: String, isVideo: Bool) throws {
959
- if callKitManager == nil {
960
- callKitManager = CallKitManager()
961
- callKitManager?.onCallStarted = { [weak self] in
962
- self?.emit(event: .callKitActionPerformed(.started))
963
- }
964
- callKitManager?.onCallEnded = { [weak self] in
965
- self?.emit(event: .callKitActionPerformed(.ended))
966
- }
967
- callKitManager?.onCallFailed = { [weak self] reason in
968
- self?.emit(event: .callKitActionPerformed(.failed(reason)))
969
- }
970
- callKitManager?.onCallMuted = { [weak self] isMuted in
971
- self?.emit(event: .callKitActionPerformed(.muted(isMuted)))
972
- }
973
- callKitManager?.onCallHeld = { [weak self] isOnHold in
974
- self?.emit(event: .callKitActionPerformed(.held(isOnHold)))
975
- }
976
- }
977
- try callKitManager?.startCallWith(displayName: displayName, isVideo: isVideo)
978
- }
979
-
980
- public func endCallKitSession() {
981
- callKitManager?.endCall()
982
- }
983
-
984
- public var hasActiveCallKitSession: Bool {
985
- return callKitManager?.hasActiveCall ?? false
986
- }
987
- }
988
-
989
- extension RNFishjamClient: CameraCapturerDeviceChangedListener {
990
- func onCaptureDeviceChanged(_ device: AVCaptureDevice?) {
991
- emit(
992
- event: .currentCameraChanged(
993
- localCamera: device?.toLocalCamera(), isCameraOn: isCameraOn, isCameraInitialized: isCameraInitialized))
994
- }
995
- }