@cloudflare/realtimekit-react-native 0.1.4-staging.1 → 0.1.4-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/lib/commonjs/AudioSampleHandler.js +61 -0
  2. package/lib/commonjs/AudioSampleHandler.js.map +1 -0
  3. package/lib/commonjs/BackgroundHandler.js +109 -0
  4. package/lib/commonjs/BackgroundHandler.js.map +1 -0
  5. package/lib/commonjs/DeviceInfo.js +39 -0
  6. package/lib/commonjs/DeviceInfo.js.map +1 -0
  7. package/lib/commonjs/LocalMediaError.js +18 -0
  8. package/lib/commonjs/LocalMediaError.js.map +1 -0
  9. package/lib/commonjs/LocalMediaHandler.js +785 -0
  10. package/lib/commonjs/LocalMediaHandler.js.map +1 -0
  11. package/lib/commonjs/LocalMediaInterfaces.js +2 -0
  12. package/lib/commonjs/LocalMediaInterfaces.js.map +1 -0
  13. package/lib/commonjs/LocalMediaUtils.js +298 -0
  14. package/lib/commonjs/LocalMediaUtils.js.map +1 -0
  15. package/lib/commonjs/NativeAudioManager.js +72 -0
  16. package/lib/commonjs/NativeAudioManager.js.map +1 -0
  17. package/lib/commonjs/PermissionHandler.js +157 -0
  18. package/lib/commonjs/PermissionHandler.js.map +1 -0
  19. package/lib/commonjs/ReactContext.js +116 -0
  20. package/lib/commonjs/ReactContext.js.map +1 -0
  21. package/lib/commonjs/ReactHooks.js +99 -0
  22. package/lib/commonjs/ReactHooks.js.map +1 -0
  23. package/lib/commonjs/index.js +95 -0
  24. package/lib/commonjs/index.js.map +1 -0
  25. package/lib/commonjs/utils/crypto.js +76 -0
  26. package/lib/commonjs/utils/crypto.js.map +1 -0
  27. package/lib/module/AudioSampleHandler.js +54 -0
  28. package/lib/module/AudioSampleHandler.js.map +1 -0
  29. package/lib/module/BackgroundHandler.js +103 -0
  30. package/lib/module/BackgroundHandler.js.map +1 -0
  31. package/lib/module/DeviceInfo.js +32 -0
  32. package/lib/module/DeviceInfo.js.map +1 -0
  33. package/lib/module/LocalMediaError.js +11 -0
  34. package/lib/module/LocalMediaError.js.map +1 -0
  35. package/lib/module/LocalMediaHandler.js +778 -0
  36. package/lib/module/LocalMediaHandler.js.map +1 -0
  37. package/lib/module/LocalMediaInterfaces.js +2 -0
  38. package/lib/module/LocalMediaInterfaces.js.map +1 -0
  39. package/lib/module/LocalMediaUtils.js +290 -0
  40. package/lib/module/LocalMediaUtils.js.map +1 -0
  41. package/lib/module/NativeAudioManager.js +65 -0
  42. package/lib/module/NativeAudioManager.js.map +1 -0
  43. package/lib/module/PermissionHandler.js +149 -0
  44. package/lib/module/PermissionHandler.js.map +1 -0
  45. package/lib/module/ReactContext.js +107 -0
  46. package/lib/module/ReactContext.js.map +1 -0
  47. package/lib/module/ReactHooks.js +75 -0
  48. package/lib/module/ReactHooks.js.map +1 -0
  49. package/lib/{src → module}/index.js +32 -32
  50. package/lib/module/index.js.map +1 -0
  51. package/lib/module/utils/crypto.js +74 -0
  52. package/lib/module/utils/crypto.js.map +1 -0
  53. package/lib/{src → typescript/src}/LocalMediaHandler.d.ts +0 -4
  54. package/package.json +5 -4
  55. package/lib/package.json +0 -168
  56. package/lib/src/AudioSampleHandler.js +0 -57
  57. package/lib/src/BackgroundHandler.js +0 -100
  58. package/lib/src/DeviceInfo.js +0 -27
  59. package/lib/src/LocalMediaError.js +0 -6
  60. package/lib/src/LocalMediaHandler.js +0 -860
  61. package/lib/src/LocalMediaInterfaces.js +0 -1
  62. package/lib/src/LocalMediaUtils.js +0 -288
  63. package/lib/src/NativeAudioManager.js +0 -93
  64. package/lib/src/PermissionHandler.js +0 -179
  65. package/lib/src/ReactContext.js +0 -90
  66. package/lib/src/ReactHooks.js +0 -80
  67. package/lib/src/utils/crypto.js +0 -82
  68. /package/lib/{src → typescript/src}/AudioSampleHandler.d.ts +0 -0
  69. /package/lib/{src → typescript/src}/BackgroundHandler.d.ts +0 -0
  70. /package/lib/{src → typescript/src}/DeviceInfo.d.ts +0 -0
  71. /package/lib/{src → typescript/src}/LocalMediaError.d.ts +0 -0
  72. /package/lib/{src → typescript/src}/LocalMediaInterfaces.d.ts +0 -0
  73. /package/lib/{src → typescript/src}/LocalMediaUtils.d.ts +0 -0
  74. /package/lib/{src → typescript/src}/NativeAudioManager.d.ts +0 -0
  75. /package/lib/{src → typescript/src}/PermissionHandler.d.ts +0 -0
  76. /package/lib/{src → typescript/src}/ReactContext.d.ts +0 -0
  77. /package/lib/{src → typescript/src}/ReactHooks.d.ts +0 -0
  78. /package/lib/{src → typescript/src}/index.d.ts +0 -0
  79. /package/lib/{src → typescript/src}/utils/crypto.d.ts +0 -0
@@ -0,0 +1,778 @@
1
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
2
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
3
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
4
+ function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
5
+ function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
6
+ function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
7
+ function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
8
+ function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
9
+ /* eslint-disable @typescript-eslint/no-unused-vars */
10
+ import { EventEmitter } from 'events';
11
+ import LocalMediaUtils from './LocalMediaUtils';
12
+ import { AppState, NativeEventEmitter, NativeModules, PermissionsAndroid, Platform } from 'react-native';
13
+ import BackgroundTimer from './BackgroundHandler';
14
+ import InCallManger from './NativeAudioManager';
15
+ import { setupPermissions } from './PermissionHandler';
16
+ const {
17
+ RTKHelper,
18
+ BroadcastEventEmitter
19
+ } = NativeModules;
20
+ const broadcastEmitter = new NativeEventEmitter(BroadcastEventEmitter);
21
+
22
+ // eslint-disable-next-line no-shadow
23
+ export let MediaEvents = /*#__PURE__*/function (MediaEvents) {
24
+ MediaEvents[MediaEvents["AUDIO_TRACK_CHANGE"] = 0] = "AUDIO_TRACK_CHANGE";
25
+ MediaEvents[MediaEvents["VIDEO_TRACK_CHANGE"] = 1] = "VIDEO_TRACK_CHANGE";
26
+ MediaEvents[MediaEvents["SCREENSHARE_TRACK_CHANGE"] = 2] = "SCREENSHARE_TRACK_CHANGE";
27
+ MediaEvents[MediaEvents["SCREENSHARE_ENDED"] = 3] = "SCREENSHARE_ENDED";
28
+ MediaEvents[MediaEvents["DEVICE_CHANGE"] = 4] = "DEVICE_CHANGE";
29
+ MediaEvents[MediaEvents["SCREENSHARE_STARTED"] = 5] = "SCREENSHARE_STARTED";
30
+ MediaEvents[MediaEvents["MEDIA_PERMISSION_UPDATE"] = 6] = "MEDIA_PERMISSION_UPDATE";
31
+ MediaEvents[MediaEvents["DEVICE_LIST_UPDATED"] = 7] = "DEVICE_LIST_UPDATED";
32
+ return MediaEvents;
33
+ }({});
34
+ function checkIfAudioTrackIsSilent(track) {
35
+ //TODO
36
+ return !!track;
37
+ }
38
+ var _localMediaUtils = /*#__PURE__*/new WeakMap();
39
+ var _appState = /*#__PURE__*/new WeakMap();
40
+ var _appStateSubscription = /*#__PURE__*/new WeakMap();
41
+ var _interval = /*#__PURE__*/new WeakMap();
42
+ export default class LocalMediaHandler extends EventEmitter {
43
+ async configureForeground() {
44
+ if (Platform.OS !== 'android') return;
45
+ const val = await RTKHelper.isForegroundServiceRunning();
46
+ if (!val) {
47
+ if (Platform.Version >= 33) {
48
+ const res = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
49
+ if (res !== PermissionsAndroid.RESULTS.GRANTED) {
50
+ console.warn('Permission required for screenshare notification');
51
+ }
52
+ }
53
+ if (Platform.Version >= 26) {
54
+ await RTKHelper.createNotificationChannel();
55
+ }
56
+ try {
57
+ await RTKHelper.startService();
58
+ } catch (e) {
59
+ console.warn('Failed to start foreground service', e);
60
+ }
61
+ }
62
+ }
63
+ constructor(localMediaUtils) {
64
+ super();
65
+ _classPrivateFieldInitSpec(this, _localMediaUtils, void 0);
66
+ _classPrivateFieldInitSpec(this, _appState, void 0);
67
+ _classPrivateFieldInitSpec(this, _appStateSubscription, void 0);
68
+ _classPrivateFieldInitSpec(this, _interval, void 0);
69
+ _defineProperty(this, "audioTrack", void 0);
70
+ _defineProperty(this, "rawAudioTrack", void 0);
71
+ _defineProperty(this, "videoTrack", void 0);
72
+ _defineProperty(this, "rawVideoTrack", void 0);
73
+ _defineProperty(this, "screenShareTracks", void 0);
74
+ _defineProperty(this, "audioEnabled", void 0);
75
+ _defineProperty(this, "videoEnabled", void 0);
76
+ _defineProperty(this, "screenShareEnabled", void 0);
77
+ _defineProperty(this, "_pendingScreenShare", false);
78
+ _defineProperty(this, "currentDevices", void 0);
79
+ _defineProperty(this, "permissions", void 0);
80
+ _defineProperty(this, "_handleAppStateChange", nextAppState => {
81
+ if (_classPrivateFieldGet(_appState, this).match(/inactive|background/) && nextAppState === 'active') {
82
+ BackgroundTimer.clearInterval(_classPrivateFieldGet(_interval, this));
83
+ _classPrivateFieldSet(_interval, this, null);
84
+ if (this.videoTrack) {
85
+ this.videoTrack.enabled = false;
86
+ setTimeout(() => {
87
+ if (this.videoTrack) {
88
+ this.videoTrack.enabled = true;
89
+ }
90
+ }, 100);
91
+ }
92
+ } else {
93
+ if (_classPrivateFieldGet(_interval, this) === null) {
94
+ if (this.screenShareEnabled || this._pendingScreenShare) {
95
+ this.configureForeground();
96
+ }
97
+ }
98
+ _classPrivateFieldSet(_interval, this, BackgroundTimer.setInterval(() => {}, 1000));
99
+ _classPrivateFieldSet(_appState, this, nextAppState);
100
+ }
101
+ });
102
+ _classPrivateFieldSet(_localMediaUtils, this, localMediaUtils);
103
+ _classPrivateFieldSet(_appState, this, '');
104
+ this.audioEnabled = true;
105
+ this.videoEnabled = true;
106
+ this.screenShareEnabled = false;
107
+ this.permissions = {
108
+ audio: 'NOT_REQUESTED',
109
+ video: 'NOT_REQUESTED'
110
+ };
111
+ this.screenShareTracks = {
112
+ audio: undefined,
113
+ video: undefined
114
+ };
115
+ this.currentDevices = {
116
+ audio: undefined,
117
+ video: undefined,
118
+ speaker: undefined
119
+ };
120
+ // LocalMediaUtils onDeviceChange with callback from native onAudioDeviceChanged events
121
+ _classPrivateFieldGet(_localMediaUtils, this).onDeviceChange(async changedDevices => {
122
+ await this.onDeviceChange(changedDevices, false);
123
+ });
124
+ if (Platform.OS === 'ios') {
125
+ broadcastEmitter.addListener('iOS_BroadcastStarted', () => {
126
+ this.oniOSBroadcastStart();
127
+ });
128
+ broadcastEmitter.addListener('iOS_BroadcastStopped', () => {
129
+ this.oniOSBroadcastStop();
130
+ });
131
+ }
132
+ InCallManger.start({});
133
+ }
134
+ async conditionallyRestartAudio() {
135
+ if (!this.currentDevices.audio || !this.audioEnabled) {
136
+ return;
137
+ }
138
+ // log.info('maybe_restart_audio: ');
139
+ await Promise.race([new Promise(resolve => {
140
+ var _this$audioTrack;
141
+ return (_this$audioTrack = this.audioTrack) === null || _this$audioTrack === void 0 ? void 0 : _this$audioTrack.addEventListener('unmute', e => {
142
+ resolve(e);
143
+ });
144
+ }), new Promise(resolve => setTimeout(resolve, 50))]);
145
+ const reacquireTrack = await this.shouldReacquireTrack('audio');
146
+ if (reacquireTrack) {
147
+ var _this$currentDevices$;
148
+ this.removeMediaStreamTrackListeners(this.audioTrack);
149
+ await this.setupAudioStream((_this$currentDevices$ = this.currentDevices.audio) === null || _this$currentDevices$ === void 0 ? void 0 : _this$currentDevices$.deviceId);
150
+ this.addMediaStreamTrackListeners(this.audioTrack);
151
+ this.emit('AUDIO_TRACK_CHANGE');
152
+ }
153
+ }
154
+ async conditionallyRestartVideo() {
155
+ if (!this.currentDevices.video || !this.videoEnabled) return;
156
+ // log.info('maybe_restart_video');
157
+
158
+ await Promise.race([new Promise(resolve => {
159
+ var _this$videoTrack;
160
+ return (_this$videoTrack = this.videoTrack) === null || _this$videoTrack === void 0 ? void 0 : _this$videoTrack.addEventListener('unmute', resolve);
161
+ }), new Promise(resolve => setTimeout(resolve, 50))]);
162
+ const reacquireTrack = await this.shouldReacquireTrack('video');
163
+ if (reacquireTrack) {
164
+ this.removeMediaStreamTrackListeners(this.videoTrack);
165
+ await this.setupVideoStream(this.currentDevices.video.deviceId);
166
+ this.addMediaStreamTrackListeners(this.videoTrack);
167
+ this.emit('VIDEO_TRACK_CHANGE');
168
+ }
169
+ }
170
+ async shouldReacquireTrack(kind) {
171
+ const workaroundWebKitBug1208516 = false;
172
+ let mediaStreamTrack;
173
+ let wasEnabled = false;
174
+ if (kind === 'audio') {
175
+ mediaStreamTrack = this.audioTrack;
176
+ wasEnabled = this.audioEnabled;
177
+ } else if (kind === 'video') {
178
+ mediaStreamTrack = this.videoTrack;
179
+ wasEnabled = this.videoEnabled;
180
+ }
181
+ if (!mediaStreamTrack) return false;
182
+ if (!this.rawAudioTrack || !this.audioTrack) return true;
183
+ if (!this.rawVideoTrack || !this.videoTrack) return true;
184
+ const isStopped = mediaStreamTrack.readyState === 'ended';
185
+ const isInadvertentlyStopped = isStopped && wasEnabled && workaroundWebKitBug1208516;
186
+ let isSilentAudio = false;
187
+ if (kind === 'audio') {
188
+ isSilentAudio = await checkIfAudioTrackIsSilent(this.audioTrack);
189
+ }
190
+ return mediaStreamTrack.muted || isInadvertentlyStopped || isSilentAudio;
191
+ }
192
+ async removeDocumentEventListeners() {}
193
+ onUnmute(ev) {
194
+ // log.info('Track unmuted', ev);
195
+ }
196
+ removeMediaStreamTrackListeners(mediaStreamTrack) {
197
+ if (!mediaStreamTrack) {
198
+ return undefined;
199
+ }
200
+ const conditionallyRestart = mediaStreamTrack.kind === 'audio' ? this.conditionallyRestartAudio : this.conditionallyRestartVideo;
201
+ mediaStreamTrack.removeEventListener('ended', conditionallyRestart);
202
+ mediaStreamTrack.removeEventListener('mute', conditionallyRestart);
203
+ mediaStreamTrack.removeEventListener('unmute', this.onUnmute);
204
+ return mediaStreamTrack;
205
+ }
206
+ addMediaStreamTrackListeners(mediaStreamTrack) {
207
+ if (!mediaStreamTrack) {
208
+ return null;
209
+ }
210
+ const conditionallyRestart = mediaStreamTrack.kind === 'audio' ? this.conditionallyRestartAudio : this.conditionallyRestartVideo;
211
+ mediaStreamTrack.addEventListener('ended', conditionallyRestart);
212
+ mediaStreamTrack.addEventListener('mute', conditionallyRestart);
213
+ mediaStreamTrack.addEventListener('unmute', this.onUnmute);
214
+ return mediaStreamTrack;
215
+ }
216
+ async setupAudioStream(deviceId, forceStopTrack = false) {
217
+ var _this$currentDevices$2;
218
+ const audioDevices = _classPrivateFieldGet(_localMediaUtils, this).getAudioDevices();
219
+ if (!audioDevices.length) {
220
+ return;
221
+ }
222
+ let audioDeviceId = deviceId;
223
+ if (!audioDeviceId) {
224
+ audioDeviceId = (await this.getOperationalMicDeviceId()).deviceId;
225
+ }
226
+ if (((_this$currentDevices$2 = this.currentDevices.audio) === null || _this$currentDevices$2 === void 0 ? void 0 : _this$currentDevices$2.deviceId) !== audioDeviceId) {
227
+ this.currentDevices.audio = _classPrivateFieldGet(_localMediaUtils, this).getDeviceById(audioDeviceId, 'audioinput');
228
+ }
229
+ if (forceStopTrack) {
230
+ var _this$audioTrack2, _this$rawAudioTrack;
231
+ (_this$audioTrack2 = this.audioTrack) === null || _this$audioTrack2 === void 0 || _this$audioTrack2.stop();
232
+ (_this$rawAudioTrack = this.rawAudioTrack) === null || _this$rawAudioTrack === void 0 || _this$rawAudioTrack.stop();
233
+ }
234
+ try {
235
+ this.rawAudioTrack = await LocalMediaUtils.getAudioTrack();
236
+ } catch (err) {
237
+ console.warn('Failed to acquire audio track: ', err);
238
+ this.rawAudioTrack = undefined;
239
+ }
240
+ this.setAudioTrack(this.rawAudioTrack);
241
+ this.addMediaStreamTrackListeners(this.audioTrack);
242
+ this.emit('AUDIO_TRACK_CHANGE');
243
+ this.setDevice(this.currentDevices.audio);
244
+
245
+ /**
246
+ * NOTE(roerohan): Firefox does not show device labels until permissions are granted.
247
+ * So, we need to repopulate the devices once permission is granted.
248
+ */
249
+ if (this.audioTrack && this.permissions.audio === 'NOT_REQUESTED') {
250
+ await _classPrivateFieldGet(_localMediaUtils, this).repopulateAvailableDevices();
251
+ }
252
+ this.emit('DEVICE_CHANGE', {
253
+ device: this.currentDevices.audio
254
+ });
255
+ }
256
+ onVisibilityChange() {
257
+ // pass
258
+ }
259
+ async setupVideoStream(deviceId, forceStopTrack = false) {
260
+ var _this$currentDevices$3;
261
+ const videoDevices = _classPrivateFieldGet(_localMediaUtils, this).getVideoDevices();
262
+ if (!videoDevices.length) {
263
+ return;
264
+ }
265
+ let videoDeviceId = deviceId;
266
+ if (!videoDeviceId) {
267
+ videoDeviceId = (await this.getOperationalWebcamDeviceId()).deviceId;
268
+ }
269
+ if (((_this$currentDevices$3 = this.currentDevices.video) === null || _this$currentDevices$3 === void 0 ? void 0 : _this$currentDevices$3.deviceId) !== videoDeviceId) {
270
+ this.currentDevices.video = _classPrivateFieldGet(_localMediaUtils, this).getDeviceById(videoDeviceId, 'videoinput');
271
+ }
272
+
273
+ /**
274
+ * NOTE(roerohan): this.videoEnabled is checked, so that upon device change,
275
+ * the video track is not set when the user has turned off his camera.
276
+ */
277
+ if (this.videoEnabled) {
278
+ const videoConstraints = LocalMediaUtils.getVideoConstraints({
279
+ videoDeviceId
280
+ });
281
+ if (!this.rawVideoTrack) {
282
+ try {
283
+ this.rawVideoTrack = await LocalMediaUtils.getVideoTrack(videoConstraints);
284
+ } catch (err) {
285
+ console.warn('Failed to acquire video track: ', err);
286
+ this.rawVideoTrack = undefined;
287
+ }
288
+ }
289
+ this.setVideoTrack(this.rawVideoTrack);
290
+ this.addMediaStreamTrackListeners(this.rawVideoTrack);
291
+ this.emit('VIDEO_TRACK_CHANGE');
292
+ }
293
+ if (forceStopTrack) {
294
+ var _this$videoTrack2, _this$rawVideoTrack;
295
+ (_this$videoTrack2 = this.videoTrack) === null || _this$videoTrack2 === void 0 || _this$videoTrack2.stop();
296
+ (_this$rawVideoTrack = this.rawVideoTrack) === null || _this$rawVideoTrack === void 0 || _this$rawVideoTrack.stop();
297
+ }
298
+ /**
299
+ * NOTE(roerohan): Firefox does not show device labels until permissions are granted.
300
+ * So, we need to repopulate the devices once permission is granted.
301
+ */
302
+ if (this.videoTrack && this.permissions.video === 'NOT_REQUESTED') {
303
+ await _classPrivateFieldGet(_localMediaUtils, this).repopulateAvailableDevices();
304
+ }
305
+ this.emit('DEVICE_CHANGE', {
306
+ device: this.currentDevices.video
307
+ });
308
+ }
309
+ async setupSpeaker(deviceId) {
310
+ var _this$currentDevices$4;
311
+ const speakerDevices = _classPrivateFieldGet(_localMediaUtils, this).getSpeakerDevices();
312
+ if (!speakerDevices.length) {
313
+ return;
314
+ }
315
+ const lastDeviceIndex = speakerDevices.length - 1;
316
+ const speakerDeviceId = deviceId ?? speakerDevices[lastDeviceIndex].deviceId;
317
+ if (((_this$currentDevices$4 = this.currentDevices.speaker) === null || _this$currentDevices$4 === void 0 ? void 0 : _this$currentDevices$4.deviceId) !== speakerDeviceId) {
318
+ // Audio Input devices are same as output devices in mobile
319
+ this.currentDevices.audio = _classPrivateFieldGet(_localMediaUtils, this).getDeviceById(speakerDeviceId, 'audioinput');
320
+ this.currentDevices.speaker = this.currentDevices.audio;
321
+ }
322
+ await this.setDevice(this.currentDevices.audio);
323
+ this.emit('DEVICE_CHANGE', {
324
+ device: this.currentDevices.speaker
325
+ });
326
+ }
327
+ async getOperationalWebcamDeviceId() {
328
+ let videoDevices = _classPrivateFieldGet(_localMediaUtils, this).getVideoDevices();
329
+ if (!videoDevices.length) {
330
+ return {
331
+ isOperational: false,
332
+ deviceId: null
333
+ };
334
+ }
335
+
336
+ /*
337
+ NOTE(ravindra-dyte): Usually external devices come at the bottom,
338
+ By reversing the deviceList and breaking on first operational device
339
+ We are giving preference to external device
340
+ */
341
+
342
+ videoDevices = [...videoDevices].reverse();
343
+ let operationalDeviceId = null;
344
+ for (let deviceIndex = 0; deviceIndex < videoDevices.length; deviceIndex += 1) {
345
+ try {
346
+ operationalDeviceId = videoDevices[deviceIndex].deviceId;
347
+ break;
348
+ } catch (err) {}
349
+ }
350
+ return operationalDeviceId ? {
351
+ isOperational: true,
352
+ deviceId: operationalDeviceId
353
+ } : {
354
+ isOperational: false,
355
+ deviceId: videoDevices[0].deviceId
356
+ };
357
+ }
358
+ async getOperationalMicDeviceId() {
359
+ let audioDevices = _classPrivateFieldGet(_localMediaUtils, this).getAudioDevices();
360
+ if (!audioDevices.length) {
361
+ return {
362
+ isOperational: false,
363
+ deviceId: null
364
+ };
365
+ }
366
+ /*
367
+ NOTE(ravindra-dyte): Usually external devices come at the bottom,
368
+ By reversing the deviceList and breaking on first operational device
369
+ We are giving preference to external device
370
+ */
371
+
372
+ audioDevices = [...audioDevices].reverse();
373
+ let operationalDeviceId = null;
374
+ for (let deviceIndex = 0; deviceIndex < audioDevices.length; deviceIndex += 1) {
375
+ try {
376
+ const audioTrack = await LocalMediaUtils.getAudioTrack();
377
+ const isSilent = await checkIfAudioTrackIsSilent(audioTrack);
378
+ audioTrack.stop();
379
+ audioTrack.release();
380
+ if (!isSilent) {
381
+ operationalDeviceId = audioDevices[deviceIndex].deviceId;
382
+ break;
383
+ }
384
+ } catch (err) {
385
+ console.warn('Failed to acquire audio track: ', err);
386
+ }
387
+ }
388
+ return operationalDeviceId ? {
389
+ isOperational: true,
390
+ deviceId: operationalDeviceId
391
+ } : {
392
+ isOperational: false,
393
+ deviceId: audioDevices[0].deviceId
394
+ };
395
+ }
396
+ async setupStreams({
397
+ audio,
398
+ video
399
+ }) {
400
+ try {
401
+ const permResult = await setupPermissions(audio, video);
402
+ this.permissions.video = permResult.camera;
403
+ this.permissions.audio = permResult.microphone;
404
+ } catch (val) {
405
+ this.permissions.video = val.camera ?? false;
406
+ this.permissions.audio = val.microphone ?? false;
407
+ }
408
+ this.emit('MEDIA_PERMISSION_UPDATE', {
409
+ kind: 'audio',
410
+ message: this.permissions.audio
411
+ });
412
+ this.emit('MEDIA_PERMISSION_UPDATE', {
413
+ kind: 'video',
414
+ message: this.permissions.video
415
+ });
416
+ const constraints = {};
417
+ if (!this.currentDevices) {
418
+ this.currentDevices = {
419
+ audio: undefined,
420
+ video: undefined,
421
+ speaker: undefined
422
+ };
423
+ }
424
+ if (audio) {
425
+ const audioDevices = _classPrivateFieldGet(_localMediaUtils, this).getAudioDevices();
426
+ if (audioDevices.length !== 0) {
427
+ const audioDeviceId = (await this.getOperationalMicDeviceId()).deviceId;
428
+ this.currentDevices.audio = _classPrivateFieldGet(_localMediaUtils, this).getDeviceById(audioDeviceId, 'audioinput');
429
+ constraints.audio = true;
430
+ }
431
+ }
432
+ if (video) {
433
+ const videoDevices = _classPrivateFieldGet(_localMediaUtils, this).getVideoDevices();
434
+ if (videoDevices.length !== 0) {
435
+ const videoDeviceId = (await this.getOperationalWebcamDeviceId()).deviceId;
436
+ this.currentDevices.video = _classPrivateFieldGet(_localMediaUtils, this).getDeviceById(videoDeviceId, 'videoinput');
437
+ const videoConstraints = LocalMediaUtils.getVideoConstraints({
438
+ videoDeviceId
439
+ });
440
+ constraints.video = videoConstraints.video;
441
+ }
442
+ }
443
+ if (audio) await this.setupAudioStream();
444
+ if (video) await this.setupVideoStream();
445
+ if (this.audioTrack) this.audioEnabled = true;else this.audioEnabled = false;
446
+ if (this.videoTrack) this.videoEnabled = true;else this.videoEnabled = false;
447
+ this.screenShareEnabled = false;
448
+
449
+ // Setting default audio output to speaker
450
+ await this.setupSpeaker();
451
+
452
+ // Setup Foreground Service after handling permissions
453
+ if (Platform.OS === 'android') {
454
+ _classPrivateFieldSet(_appStateSubscription, this, AppState.addEventListener('change', this._handleAppStateChange));
455
+ }
456
+ }
457
+ stopScreenShareTracks(tracks = {}) {
458
+ const {
459
+ audio = true,
460
+ video = true
461
+ } = tracks;
462
+ if (audio) {
463
+ var _this$screenShareTrac;
464
+ (_this$screenShareTrac = this.screenShareTracks) === null || _this$screenShareTrac === void 0 || (_this$screenShareTrac = _this$screenShareTrac.audio) === null || _this$screenShareTrac === void 0 || _this$screenShareTrac.stop();
465
+ }
466
+ if (video) {
467
+ var _this$screenShareTrac2;
468
+ (_this$screenShareTrac2 = this.screenShareTracks) === null || _this$screenShareTrac2 === void 0 || (_this$screenShareTrac2 = _this$screenShareTrac2.video) === null || _this$screenShareTrac2 === void 0 || _this$screenShareTrac2.stop();
469
+ }
470
+ if (audio && video) {
471
+ this.screenShareEnabled = false;
472
+ }
473
+ }
474
+ removeScreenShareTracks(tracks) {
475
+ var _this$screenShareTrac3;
476
+ (_this$screenShareTrac3 = this.screenShareTracks.video) === null || _this$screenShareTrac3 === void 0 || _this$screenShareTrac3.removeEventListener('ended', this.onScreenShareEnded);
477
+ this.stopScreenShareTracks(tracks);
478
+ this.screenShareTracks = {
479
+ audio: undefined,
480
+ video: undefined
481
+ };
482
+ }
483
+ setAudioTrack(audioTrack) {
484
+ if (audioTrack === this.audioTrack) {
485
+ return;
486
+ }
487
+ if (audioTrack === undefined) {
488
+ this.stopAudioTrack();
489
+ }
490
+ this.audioTrack = audioTrack;
491
+ }
492
+ setVideoTrack(videoTrack) {
493
+ if (videoTrack === this.videoTrack) {
494
+ return;
495
+ }
496
+ if (videoTrack === undefined) {
497
+ this.stopVideoTrack();
498
+ }
499
+ this.videoTrack = videoTrack;
500
+ }
501
+ setScreenShareTracks(tracks) {
502
+ var _this$screenShareTrac4;
503
+ const {
504
+ audio,
505
+ video
506
+ } = tracks;
507
+ // console.log('SetScreenshare called');
508
+ if (!audio || !video) {
509
+ this.stopScreenShareTracks({
510
+ audio: !!audio,
511
+ video: !!video
512
+ });
513
+ }
514
+ this.screenShareTracks = tracks;
515
+ (_this$screenShareTrac4 = this.screenShareTracks.video) === null || _this$screenShareTrac4 === void 0 || _this$screenShareTrac4.addEventListener('ended', this.onScreenShareEnded.bind(this));
516
+ }
517
+ onScreenShareEnded() {
518
+ this.emit('SCREENSHARE_ENDED');
519
+ }
520
+ async disableAudio() {
521
+ await this.toggleAudio();
522
+ }
523
+ async enableAudio() {
524
+ await this.toggleAudio();
525
+ }
526
+ async toggleAudio() {
527
+ if (this.permissions.audio !== 'ACCEPTED') {
528
+ try {
529
+ const permResult = await setupPermissions(true, false);
530
+ this.permissions.audio = permResult.microphone;
531
+ } catch (val) {
532
+ this.permissions.audio = val.microphone ?? false;
533
+ }
534
+ }
535
+ this.emit('MEDIA_PERMISSION_UPDATE', {
536
+ kind: 'audio',
537
+ message: this.permissions.audio
538
+ });
539
+ if (this.permissions.audio === 'DENIED' || this.permissions.audio === 'NOT_REQUESTED') {
540
+ this.audioEnabled = false;
541
+ return;
542
+ }
543
+ if (this.audioEnabled) {
544
+ this.audioEnabled = false;
545
+ if (this.audioTrack) {
546
+ this.audioTrack.enabled = this.audioEnabled;
547
+ }
548
+ return;
549
+ }
550
+ try {
551
+ /**
552
+ * NOTE (@madhugb): We need to setup new stream,
553
+ * if the audio track is not preset or it has ended
554
+ */
555
+ if (!this.audioTrack || this.audioTrack.readyState === 'ended') {
556
+ var _this$currentDevices$5;
557
+ await this.setupAudioStream((_this$currentDevices$5 = this.currentDevices.audio) === null || _this$currentDevices$5 === void 0 ? void 0 : _this$currentDevices$5.deviceId);
558
+ }
559
+ // if we still can't get a track after setting up audio stream
560
+ // do not toggle the audio state
561
+ if (!this.audioTrack) {
562
+ this.audioEnabled = false;
563
+ return;
564
+ }
565
+ this.audioEnabled = true;
566
+ } catch (ex) {
567
+ this.audioEnabled = false;
568
+ this.audioTrack = undefined;
569
+ this.rawAudioTrack = undefined;
570
+ }
571
+ if (this.audioTrack) {
572
+ this.audioTrack.enabled = true;
573
+ }
574
+ }
575
+ async enableVideo() {
576
+ await this.toggleVideo();
577
+ }
578
+ async disableVideo() {
579
+ await this.toggleVideo();
580
+ }
581
+ async toggleVideo() {
582
+ var _this$currentDevices$6;
583
+ if (this.permissions.video !== 'ACCEPTED') {
584
+ try {
585
+ const permResult = await setupPermissions(false, true);
586
+ this.permissions.video = permResult.camera;
587
+ } catch (val) {
588
+ this.permissions.video = val.camera ?? false;
589
+ }
590
+ }
591
+ this.emit('MEDIA_PERMISSION_UPDATE', {
592
+ kind: 'video',
593
+ message: this.permissions.video
594
+ });
595
+ if (this.permissions.video === 'DENIED' || this.permissions.video === 'NOT_REQUESTED') {
596
+ this.videoEnabled = false;
597
+ return;
598
+ }
599
+ if (this.videoEnabled) {
600
+ this.stopVideoTrack();
601
+ this.videoEnabled = false;
602
+ return;
603
+ }
604
+ /**
605
+ * NOTE(roerohan): The videoEnabled flag is used inside the setupVideoStream method
606
+ * hence it must be set to true first.
607
+ */
608
+ this.videoEnabled = true;
609
+ await this.setupVideoStream((_this$currentDevices$6 = this.currentDevices.video) === null || _this$currentDevices$6 === void 0 ? void 0 : _this$currentDevices$6.deviceId);
610
+ }
611
+ async setAudioDevice(device) {
612
+ await this.setupAudioStream(device.deviceId);
613
+ }
614
+ async setVideoDevice(device) {
615
+ if (this.videoEnabled) {
616
+ this.stopVideoTrack();
617
+ }
618
+ await this.setupVideoStream(device.deviceId);
619
+ }
620
+ async setSpeakerDevice(device) {
621
+ await this.setupSpeaker(device.deviceId);
622
+ }
623
+ async setDevice(route) {
624
+ await _classPrivateFieldGet(_localMediaUtils, this).chooseAudioRoute(route.deviceId);
625
+ }
626
+ async oniOSBroadcastStart() {
627
+ this.setScreenShareTracks(await LocalMediaUtils.getScreenShareTracks());
628
+ this.screenShareEnabled = true;
629
+ this.emit('SCREENSHARE_TRACK_CHANGE');
630
+ }
631
+ async oniOSBroadcastStop() {
632
+ if (this.screenShareEnabled) {
633
+ this.removeScreenShareTracks();
634
+ this.emit('SCREENSHARE_TRACK_CHANGE');
635
+ return;
636
+ }
637
+ }
638
+ async toggleScreenShare() {
639
+ if (this.screenShareEnabled && Platform.OS !== 'ios') {
640
+ this.removeScreenShareTracks();
641
+ try {
642
+ await RTKHelper.stopService();
643
+ } catch (e) {
644
+ console.warn('Failed to stop foreground service', e);
645
+ }
646
+ return;
647
+ }
648
+ if (Platform.OS === 'ios' && this.screenShareEnabled) {
649
+ NativeModules.RTKScreensharePickerView.showScreenSharePickerView();
650
+ return;
651
+ }
652
+ if (Platform.OS === 'android') {
653
+ this._pendingScreenShare = true;
654
+ await this.configureForeground();
655
+ try {
656
+ this.setScreenShareTracks(await LocalMediaUtils.getScreenShareTracks());
657
+ this.screenShareEnabled = true;
658
+ } catch (e) {
659
+ console.warn('Failed to start screenshare', e);
660
+ this.screenShareEnabled = false;
661
+ await RTKHelper.stopService();
662
+ this._pendingScreenShare = false;
663
+ } finally {
664
+ this._pendingScreenShare = false;
665
+ }
666
+ }
667
+ if (Platform.OS === 'ios') {
668
+ NativeModules.RTKScreensharePickerView.showScreenSharePickerView();
669
+ }
670
+ }
671
+ async enableScreenShare() {
672
+ await this.toggleScreenShare();
673
+ }
674
+ async disableScreenShare() {
675
+ await this.toggleScreenShare();
676
+ }
677
+ getAllDevices() {
678
+ return _classPrivateFieldGet(_localMediaUtils, this).availableDevices;
679
+ }
680
+ getCurrentDevices() {
681
+ return this.currentDevices;
682
+ }
683
+ getDeviceById(deviceId, kind) {
684
+ return _classPrivateFieldGet(_localMediaUtils, this).getDeviceById(deviceId, kind);
685
+ }
686
+ getAudioDevices() {
687
+ return _classPrivateFieldGet(_localMediaUtils, this).getAudioDevices();
688
+ }
689
+ getVideoDevices() {
690
+ return _classPrivateFieldGet(_localMediaUtils, this).getVideoDevices();
691
+ }
692
+ getSpeakerDevices() {
693
+ return _classPrivateFieldGet(_localMediaUtils, this).getSpeakerDevices();
694
+ }
695
+ async onDeviceChange(changedDevices, forceDeviceChange) {
696
+ let addedDevice;
697
+ let removedDevice;
698
+ if (changedDevices.added.length) [addedDevice] = changedDevices.added;
699
+ if (changedDevices.removed.length) [removedDevice] = changedDevices.removed;
700
+
701
+ /**
702
+ * NOTE(roerohan): forceStopTrack is set required to be set to true for firefox.
703
+ * https://stackoverflow.com/questions/59068146/navigator-mediadevices-getusermedia-api-rejecting-with-error-notreadableerror
704
+ * forceStopTrack is the second argument of setupAudioStream and setupVideoStream.
705
+ */
706
+ if (addedDevice) {
707
+ if (addedDevice.kind === 'audioinput') {
708
+ if (this.permissions.audio === 'DENIED' || this.permissions.audio === 'NOT_REQUESTED') {
709
+ await this.setupSpeaker(addedDevice.deviceId);
710
+ } else {
711
+ await this.setupAudioStream(addedDevice.deviceId);
712
+ }
713
+ } else if (addedDevice.kind === 'videoinput') {
714
+ await this.setupVideoStream(addedDevice.deviceId);
715
+ } else if (addedDevice.kind === 'audiooutput') {
716
+ await this.setupSpeaker(addedDevice.deviceId);
717
+ }
718
+ this.emit('AUDIO_TRACK_CHANGE');
719
+ }
720
+ if (removedDevice) {
721
+ if (removedDevice.kind === 'audioinput') {
722
+ if (this.permissions.audio === 'DENIED' || this.permissions.audio === 'NOT_REQUESTED') {
723
+ await this.setupSpeaker();
724
+ } else {
725
+ await this.setupAudioStream();
726
+ }
727
+ } else if (removedDevice.kind === 'videoinput') {
728
+ await this.setupVideoStream();
729
+ } else if (removedDevice.kind === 'audiooutput') {
730
+ await this.setupSpeaker();
731
+ }
732
+ this.emit('AUDIO_TRACK_CHANGE');
733
+ }
734
+ if (changedDevices.added.length || changedDevices.removed.length) {
735
+ this.emit('DEVICE_LIST_UPDATED', changedDevices);
736
+ }
737
+ }
738
+ static async init(_) {
739
+ const localMediaUtils = await LocalMediaUtils.init();
740
+ return new LocalMediaHandler(localMediaUtils);
741
+ }
742
+ emit(event, ...args) {
743
+ return super.emit(event, ...args);
744
+ }
745
+ on(event, listener) {
746
+ return super.on(event, listener);
747
+ }
748
+ destruct() {
749
+ // TODO: Do not remove native broadcast listeners when going OFF_STAGE in webinar
750
+ // this.#localMediaUtils.destruct();
751
+ if (Platform.OS === 'android') _classPrivateFieldGet(_appStateSubscription, this).remove();
752
+ BackgroundTimer.stop();
753
+ try {
754
+ if (Platform.OS === 'android') RTKHelper.stopService();
755
+ } catch (_err) {}
756
+ // InCallManger.stop(undefined);
757
+ }
758
+ stopAudioTrack() {
759
+ var _this$rawAudioTrack2, _this$audioTrack3, _this$rawAudioTrack3, _this$audioTrack4;
760
+ (_this$rawAudioTrack2 = this.rawAudioTrack) === null || _this$rawAudioTrack2 === void 0 || _this$rawAudioTrack2.stop();
761
+ (_this$audioTrack3 = this.audioTrack) === null || _this$audioTrack3 === void 0 || _this$audioTrack3.stop();
762
+ (_this$rawAudioTrack3 = this.rawAudioTrack) === null || _this$rawAudioTrack3 === void 0 || _this$rawAudioTrack3.release();
763
+ (_this$audioTrack4 = this.audioTrack) === null || _this$audioTrack4 === void 0 || _this$audioTrack4.release();
764
+ this.audioEnabled = false;
765
+ this.audioTrack = undefined;
766
+ this.rawAudioTrack = undefined;
767
+ }
768
+ stopVideoTrack() {
769
+ var _this$rawVideoTrack2, _this$videoTrack3, _this$rawVideoTrack3, _this$videoTrack4;
770
+ (_this$rawVideoTrack2 = this.rawVideoTrack) === null || _this$rawVideoTrack2 === void 0 || _this$rawVideoTrack2.stop();
771
+ (_this$videoTrack3 = this.videoTrack) === null || _this$videoTrack3 === void 0 || _this$videoTrack3.stop();
772
+ (_this$rawVideoTrack3 = this.rawVideoTrack) === null || _this$rawVideoTrack3 === void 0 || _this$rawVideoTrack3.release();
773
+ (_this$videoTrack4 = this.videoTrack) === null || _this$videoTrack4 === void 0 || _this$videoTrack4.release();
774
+ this.videoTrack = undefined;
775
+ this.rawVideoTrack = undefined;
776
+ }
777
+ }
778
+ //# sourceMappingURL=LocalMediaHandler.js.map