@whereby.com/browser-sdk 2.0.0-alpha2 → 2.0.0-alpha21

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.
package/dist/lib.esm.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { define, ref } from 'heresy';
2
- import React, { useCallback, useState, useReducer, useEffect } from 'react';
3
- import { __awaiter } from 'tslib';
2
+ import { __rest, __awaiter } from 'tslib';
3
+ import React, { useRef, useEffect, useState, useReducer } from 'react';
4
+ import assert from 'assert';
4
5
  import adapter from 'webrtc-adapter';
5
6
  import io from 'socket.io-client';
6
7
  import SDPUtils from 'sdp';
7
- import assert from 'assert';
8
8
  import { detectDevice, Device } from 'mediasoup-client';
9
9
  import EventEmitter$1, { EventEmitter } from 'events';
10
10
  import { v4 } from 'uuid';
@@ -79,8 +79,7 @@ define("WherebyEmbed", {
79
79
  // Commands
80
80
  _postCommand(command, args = []) {
81
81
  if (this.iframe.current) {
82
- const url = new URL(this.room, `https://${this.subdomain}.whereby.com`);
83
- this.iframe.current.contentWindow.postMessage({ command, args }, url.origin);
82
+ this.iframe.current.contentWindow.postMessage({ command, args }, this.url.origin);
84
83
  }
85
84
  },
86
85
  startRecording() {
@@ -99,8 +98,7 @@ define("WherebyEmbed", {
99
98
  this._postCommand("toggle_screenshare", [enabled]);
100
99
  },
101
100
  onmessage({ origin, data }) {
102
- const url = new URL(this.room, `https://${this.subdomain}.whereby.com`);
103
- if (origin !== url.origin)
101
+ if (origin !== this.url.origin)
104
102
  return;
105
103
  const { type, payload: detail } = data;
106
104
  this.dispatchEvent(new CustomEvent(type, { detail }));
@@ -110,36 +108,354 @@ define("WherebyEmbed", {
110
108
  if (!room)
111
109
  return this.html `Whereby: Missing room attribute.`;
112
110
  // Get subdomain from room URL, or use it specified
113
- const m = /https:\/\/([^.]+)\.whereby.com\/.+/.exec(room);
111
+ const m = /https:\/\/([^.]+)(\.whereby.com|-ip-\d+-\d+-\d+-\d+.hereby.dev:4443)\/.+/.exec(room);
114
112
  const subdomain = (m && m[1]) || this.subdomain;
115
113
  if (!subdomain)
116
114
  return this.html `Whereby: Missing subdomain attr.`;
117
- const url = new URL(room, `https://${subdomain}.whereby.com`);
118
- Object.entries(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ jsApi: true, we: "2.0.0-alpha2", iframeSource: subdomain }, (displayName && { displayName })), (lang && { lang })), (metadata && { metadata })), (groups && { groups })), (virtualBackgroundUrl && { virtualBackgroundUrl })), (avatarUrl && { avatarUrl })), (minimal != null && { embed: minimal })), boolAttrs.reduce(
115
+ if (!m) {
116
+ return this.html `could not parse URL.`;
117
+ }
118
+ const baseURL = m[2] || `.whereby.com`;
119
+ this.url = new URL(room, `https://${subdomain}${baseURL}`);
120
+ const roomUrl = new URL(room);
121
+ if (roomUrl.searchParams.get("roomKey")) {
122
+ this.url.searchParams.append("roomKey", roomUrl.searchParams.get("roomKey"));
123
+ }
124
+ Object.entries(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ jsApi: true, we: "2.0.0-alpha21", iframeSource: subdomain }, (displayName && { displayName })), (lang && { lang })), (metadata && { metadata })), (groups && { groups })), (virtualBackgroundUrl && { virtualBackgroundUrl })), (avatarUrl && { avatarUrl })), (minimal != null && { embed: minimal })), boolAttrs.reduce(
119
125
  // add to URL if set in any way
120
126
  (o, v) => (this[v.toLowerCase()] != null ? Object.assign(Object.assign({}, o), { [v]: this[v.toLowerCase()] }) : o), {}))).forEach(([k, v]) => {
121
- if (!url.searchParams.has(k) && typeof v === "string") {
122
- url.searchParams.set(k, v);
127
+ if (!this.url.searchParams.has(k) && typeof v === "string") {
128
+ this.url.searchParams.set(k, v);
123
129
  }
124
130
  });
125
131
  return this.html `
126
132
  <iframe
127
133
  ref=${this.iframe}
128
- src=${url}
134
+ src=${this.url}
129
135
  allow="autoplay; camera; microphone; fullscreen; speaker; display-capture" />
130
136
  `;
131
137
  },
132
138
  });
133
139
 
134
- var VideoElement = ({ stream, style }) => {
135
- const videoEl = useCallback((node) => {
136
- if (node !== null && node.srcObject !== stream) {
137
- node.srcObject = stream;
140
+ /**
141
+ * Debounce function.
142
+ *
143
+ * @param {Function} fn - Function to debounce.
144
+ * @param {Object} [options] - Options.
145
+ * @param {number} [options.delay=500] - Delay in milliseconds.
146
+ * @param {boolean} [options.edges=false] - Whether to call the function on the
147
+ * leading and trailing edges of the wait timeout.
148
+ * @returns {Function} Debounced function.
149
+ */
150
+ function debounce(fn, { delay = 500, edges } = {}) {
151
+ assert.ok(typeof fn === "function", "fn<function> is required");
152
+ let timeout;
153
+ let nCalls = 0;
154
+ return (...args) => {
155
+ nCalls += 1;
156
+ if (edges && nCalls === 1) {
157
+ fn(...args);
138
158
  }
139
- }, []);
140
- return React.createElement("video", { ref: videoEl, autoPlay: true, playsInline: true, style: style });
159
+ clearTimeout(timeout);
160
+ timeout = setTimeout(() => {
161
+ if (!edges || nCalls > 1) {
162
+ fn(...args);
163
+ }
164
+ timeout = undefined;
165
+ nCalls = 0;
166
+ }, delay);
167
+ };
168
+ }
169
+
170
+ var VideoView = (_a) => {
171
+ var { muted, stream, onResize } = _a, rest = __rest(_a, ["muted", "stream", "onResize"]);
172
+ const videoEl = useRef(null);
173
+ useEffect(() => {
174
+ if (!videoEl.current || !onResize) {
175
+ return;
176
+ }
177
+ const resizeObserver = new ResizeObserver(debounce(() => {
178
+ if (videoEl.current && (stream === null || stream === void 0 ? void 0 : stream.id)) {
179
+ onResize({
180
+ width: videoEl.current.clientWidth,
181
+ height: videoEl.current.clientHeight,
182
+ stream,
183
+ });
184
+ }
185
+ }, { delay: 1000, edges: true }));
186
+ resizeObserver.observe(videoEl.current);
187
+ return () => {
188
+ resizeObserver.disconnect();
189
+ };
190
+ }, [stream]);
191
+ useEffect(() => {
192
+ if (!videoEl.current) {
193
+ return;
194
+ }
195
+ if (videoEl.current.srcObject !== stream) {
196
+ videoEl.current.srcObject = stream;
197
+ }
198
+ // Handle muting programatically, not as video attribute
199
+ // https://stackoverflow.com/questions/14111917/html5-video-muted-but-still-playing
200
+ if (videoEl.current.muted !== muted) {
201
+ videoEl.current.muted = Boolean(muted);
202
+ }
203
+ }, [muted, stream, videoEl]);
204
+ return React.createElement("video", Object.assign({ ref: videoEl, autoPlay: true, playsInline: true }, rest));
141
205
  };
142
206
 
207
+ const TypedLocalMediaEventTarget = EventTarget;
208
+ class LocalMedia extends TypedLocalMediaEventTarget {
209
+ constructor(constraints) {
210
+ super();
211
+ this._constraints = constraints;
212
+ this.stream = new MediaStream();
213
+ this._rtcManagers = [];
214
+ this.screenshareStream = undefined;
215
+ navigator.mediaDevices.addEventListener("devicechange", this._updateDeviceList.bind(this));
216
+ }
217
+ addRtcManager(rtcManager) {
218
+ this._rtcManagers.push(rtcManager);
219
+ }
220
+ removeRtcManager(rtcManager) {
221
+ this._rtcManagers = this._rtcManagers.filter((r) => r !== rtcManager);
222
+ }
223
+ getCameraDeviceId() {
224
+ var _a;
225
+ return (_a = this.stream.getVideoTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings().deviceId;
226
+ }
227
+ getMicrophoneDeviceId() {
228
+ var _a;
229
+ return (_a = this.stream.getAudioTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings().deviceId;
230
+ }
231
+ isCameraEnabled() {
232
+ var _a;
233
+ return !!((_a = this.stream.getVideoTracks()[0]) === null || _a === void 0 ? void 0 : _a.enabled);
234
+ }
235
+ isMicrophoneEnabled() {
236
+ var _a;
237
+ return !!((_a = this.stream.getAudioTracks()[0]) === null || _a === void 0 ? void 0 : _a.enabled);
238
+ }
239
+ toggleCameraEnabled(enabled) {
240
+ const videoTrack = this.stream.getVideoTracks()[0];
241
+ if (!videoTrack) {
242
+ return;
243
+ }
244
+ const newValue = enabled !== null && enabled !== void 0 ? enabled : !videoTrack.enabled;
245
+ videoTrack.enabled = newValue;
246
+ this.dispatchEvent(new CustomEvent("camera_enabled", { detail: { enabled: newValue } }));
247
+ }
248
+ toggleMichrophoneEnabled(enabled) {
249
+ const audioTrack = this.stream.getAudioTracks()[0];
250
+ if (!audioTrack) {
251
+ return;
252
+ }
253
+ const newValue = enabled !== null && enabled !== void 0 ? enabled : !audioTrack.enabled;
254
+ audioTrack.enabled = newValue;
255
+ this.dispatchEvent(new CustomEvent("microphone_enabled", { detail: { enabled: newValue } }));
256
+ }
257
+ startScreenshare() {
258
+ return __awaiter(this, void 0, void 0, function* () {
259
+ if (this.screenshareStream) {
260
+ return this.screenshareStream;
261
+ }
262
+ const screenshareStream = yield navigator.mediaDevices.getDisplayMedia();
263
+ this.screenshareStream = screenshareStream;
264
+ return this.screenshareStream;
265
+ });
266
+ }
267
+ stopScreenshare() {
268
+ var _a;
269
+ (_a = this.screenshareStream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((track) => track.stop());
270
+ this.screenshareStream = undefined;
271
+ }
272
+ setCameraDevice(deviceId) {
273
+ return __awaiter(this, void 0, void 0, function* () {
274
+ const newStream = yield navigator.mediaDevices.getUserMedia({ video: { deviceId } });
275
+ const newVideoTrack = newStream.getVideoTracks()[0];
276
+ if (newVideoTrack) {
277
+ const oldVideoTrack = this.stream.getVideoTracks()[0];
278
+ newVideoTrack.enabled = oldVideoTrack.enabled;
279
+ oldVideoTrack === null || oldVideoTrack === void 0 ? void 0 : oldVideoTrack.stop();
280
+ this._rtcManagers.forEach((rtcManager) => {
281
+ rtcManager.replaceTrack(oldVideoTrack, newVideoTrack);
282
+ });
283
+ this.stream.removeTrack(oldVideoTrack);
284
+ this.stream.addTrack(newVideoTrack);
285
+ }
286
+ this.dispatchEvent(new CustomEvent("stream_updated", {
287
+ detail: { stream: this.stream },
288
+ }));
289
+ });
290
+ }
291
+ setMicrophoneDevice(deviceId) {
292
+ return __awaiter(this, void 0, void 0, function* () {
293
+ const newStream = yield navigator.mediaDevices.getUserMedia({ audio: { deviceId } });
294
+ const newAudioTrack = newStream.getAudioTracks()[0];
295
+ const oldAudioTrack = this.stream.getAudioTracks()[0];
296
+ if (oldAudioTrack) {
297
+ newAudioTrack.enabled = oldAudioTrack.enabled;
298
+ oldAudioTrack.stop();
299
+ this.stream.removeTrack(oldAudioTrack);
300
+ }
301
+ this._rtcManagers.forEach((rtcManager) => {
302
+ rtcManager.replaceTrack(oldAudioTrack, newAudioTrack);
303
+ });
304
+ this.stream.addTrack(newAudioTrack);
305
+ this.dispatchEvent(new CustomEvent("stream_updated", {
306
+ detail: { stream: this.stream },
307
+ }));
308
+ });
309
+ }
310
+ _updateDeviceList() {
311
+ return __awaiter(this, void 0, void 0, function* () {
312
+ try {
313
+ const devices = yield navigator.mediaDevices.enumerateDevices();
314
+ this.dispatchEvent(new CustomEvent("device_list_updated", {
315
+ detail: {
316
+ cameraDevices: devices.filter((d) => d.kind === "videoinput"),
317
+ microphoneDevices: devices.filter((d) => d.kind === "audioinput"),
318
+ speakerDevices: devices.filter((d) => d.kind === "audiooutput"),
319
+ },
320
+ }));
321
+ }
322
+ catch (error) {
323
+ this.dispatchEvent(new CustomEvent("device_list_update_error", {
324
+ detail: {
325
+ error,
326
+ },
327
+ }));
328
+ throw error;
329
+ }
330
+ });
331
+ }
332
+ start() {
333
+ return __awaiter(this, void 0, void 0, function* () {
334
+ const newStream = yield navigator.mediaDevices.getUserMedia(this._constraints);
335
+ newStream.getTracks().forEach((t) => this.stream.addTrack(t));
336
+ this._updateDeviceList();
337
+ this.dispatchEvent(new CustomEvent("stream_updated", {
338
+ detail: { stream: this.stream },
339
+ }));
340
+ return this.stream;
341
+ });
342
+ }
343
+ stop() {
344
+ var _a;
345
+ (_a = this.stream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((t) => {
346
+ t.stop();
347
+ });
348
+ }
349
+ }
350
+
351
+ const initialState$1 = {
352
+ cameraDeviceError: null,
353
+ cameraDevices: [],
354
+ isSettingCameraDevice: false,
355
+ isSettingMicrophoneDevice: false,
356
+ isStarting: false,
357
+ microphoneDeviceError: null,
358
+ microphoneDevices: [],
359
+ speakerDevices: [],
360
+ startError: null,
361
+ };
362
+ function reducer$1(state, action) {
363
+ switch (action.type) {
364
+ case "DEVICE_LIST_UPDATED":
365
+ return Object.assign(Object.assign({}, state), action.payload);
366
+ case "LOCAL_STREAM_UPDATED":
367
+ return Object.assign(Object.assign({}, state), { currentCameraDeviceId: action.payload.currentCameraDeviceId, currentMicrophoneDeviceId: action.payload.currentMicrophoneDeviceId, localStream: action.payload.stream });
368
+ case "SET_CAMERA_DEVICE":
369
+ return Object.assign(Object.assign({}, state), { cameraDeviceError: null, isSettingCameraDevice: true });
370
+ case "SET_CAMERA_DEVICE_COMPLETE":
371
+ return Object.assign(Object.assign({}, state), { isSettingCameraDevice: false });
372
+ case "SET_CAMERA_DEVICE_ERROR":
373
+ return Object.assign(Object.assign({}, state), { cameraDeviceError: action.payload, isSettingCameraDevice: false });
374
+ case "SET_MICROPHONE_DEVICE":
375
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: true, microphoneDeviceError: null });
376
+ case "SET_MICROPHONE_DEVICE_COMPLETE":
377
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: false });
378
+ case "SET_MICROPHONE_DEVICE_ERROR":
379
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: false, microphoneDeviceError: action.payload });
380
+ case "START":
381
+ return Object.assign(Object.assign({}, state), { isStarting: true, startError: null });
382
+ case "START_COMPLETE":
383
+ return Object.assign(Object.assign({}, state), { isStarting: false });
384
+ case "START_ERROR":
385
+ return Object.assign(Object.assign({}, state), { isStarting: false, startError: action.payload });
386
+ default:
387
+ return state;
388
+ }
389
+ }
390
+ function useLocalMedia(constraints = { audio: true, video: true }) {
391
+ const [localMedia] = useState(() => new LocalMedia(constraints));
392
+ const [state, dispatch] = useReducer(reducer$1, initialState$1);
393
+ useEffect(() => {
394
+ localMedia.addEventListener("device_list_updated", (e) => {
395
+ const { cameraDevices, microphoneDevices, speakerDevices } = e.detail;
396
+ dispatch({ type: "DEVICE_LIST_UPDATED", payload: { cameraDevices, microphoneDevices, speakerDevices } });
397
+ });
398
+ localMedia.addEventListener("stream_updated", (e) => {
399
+ const { stream } = e.detail;
400
+ dispatch({
401
+ type: "LOCAL_STREAM_UPDATED",
402
+ payload: {
403
+ stream,
404
+ currentCameraDeviceId: localMedia.getCameraDeviceId(),
405
+ currentMicrophoneDeviceId: localMedia.getMicrophoneDeviceId(),
406
+ },
407
+ });
408
+ });
409
+ const start = () => __awaiter(this, void 0, void 0, function* () {
410
+ dispatch({ type: "START" });
411
+ try {
412
+ yield localMedia.start();
413
+ dispatch({ type: "START_COMPLETE" });
414
+ }
415
+ catch (error) {
416
+ dispatch({ type: "START_ERROR", payload: error });
417
+ }
418
+ });
419
+ start();
420
+ // Perform cleanup on unmount
421
+ return () => {
422
+ localMedia.stop();
423
+ };
424
+ }, []);
425
+ return {
426
+ state,
427
+ actions: {
428
+ setCameraDevice: (...args) => __awaiter(this, void 0, void 0, function* () {
429
+ dispatch({ type: "SET_CAMERA_DEVICE" });
430
+ try {
431
+ yield localMedia.setCameraDevice(...args);
432
+ dispatch({ type: "SET_CAMERA_DEVICE_COMPLETE" });
433
+ }
434
+ catch (error) {
435
+ dispatch({ type: "SET_CAMERA_DEVICE_ERROR", payload: error });
436
+ }
437
+ }),
438
+ setMicrophoneDevice: (...args) => __awaiter(this, void 0, void 0, function* () {
439
+ dispatch({ type: "SET_MICROPHONE_DEVICE" });
440
+ try {
441
+ yield localMedia.setMicrophoneDevice(...args);
442
+ dispatch({ type: "SET_MICROPHONE_DEVICE_COMPLETE" });
443
+ }
444
+ catch (error) {
445
+ dispatch({ type: "SET_MICROPHONE_DEVICE_ERROR", payload: error });
446
+ }
447
+ }),
448
+ toggleCameraEnabled: (...args) => {
449
+ return localMedia.toggleCameraEnabled(...args);
450
+ },
451
+ toggleMicrophoneEnabled: (...args) => {
452
+ return localMedia.toggleMichrophoneEnabled(...args);
453
+ },
454
+ },
455
+ _ref: localMedia,
456
+ };
457
+ }
458
+
143
459
  const EVENTS = {
144
460
  CLIENT_CONNECTION_STATUS_CHANGED: "client_connection_status_changed",
145
461
  STREAM_ADDED: "stream_added",
@@ -270,10 +586,10 @@ class ServerSocket {
270
586
  }
271
587
 
272
588
  this._socket = io(hostName, options);
273
- this._socket.on("reconnect", () => {
589
+ this._socket.io.on("reconnect", () => {
274
590
  this._socket.sendBuffer = [];
275
591
  });
276
- this._socket.on("reconnect_attempt", () => {
592
+ this._socket.io.on("reconnect_attempt", () => {
277
593
  if (this._wasConnectedUsingWebsocket) {
278
594
  this._socket.io.opts.transports = ["websocket"];
279
595
  // only fallback to polling if not safari
@@ -331,6 +647,10 @@ class ServerSocket {
331
647
  );
332
648
  }
333
649
 
650
+ getManager() {
651
+ return this._socket.io;
652
+ }
653
+
334
654
  isConnecting() {
335
655
  return this._socket && this._socket.connecting;
336
656
  }
@@ -385,21 +705,21 @@ var rtcManagerEvents = {
385
705
  DOMINANT_SPEAKER: "dominant_speaker",
386
706
  };
387
707
 
388
- const browserName$3 = adapter.browserDetails.browser;
708
+ const browserName$2 = adapter.browserDetails.browser;
389
709
  const browserVersion$1 = adapter.browserDetails.version;
390
710
 
391
711
  // SDP mangling for deprioritizing H264
392
712
  function deprioritizeH264(sdp) {
393
713
  return SDPUtils.splitSections(sdp)
394
- .map((section) => {
714
+ .map(section => {
395
715
  // only modify video sections
396
716
  if (SDPUtils.getKind(section) !== "video") return section;
397
717
 
398
718
  // list of payloadTypes used in this sdp/section
399
719
  const h264payloadTypes = SDPUtils.matchPrefix(section, "a=rtpmap:")
400
- .map((line) => SDPUtils.parseRtpMap(line))
401
- .filter((codec) => /h264/i.test(codec.name))
402
- .map((codec) => "" + codec.payloadType);
720
+ .map(line => SDPUtils.parseRtpMap(line))
721
+ .filter(codec => /h264/i.test(codec.name))
722
+ .map(codec => "" + codec.payloadType);
403
723
 
404
724
  // return as is if no h264 found
405
725
  if (!h264payloadTypes.length) return section;
@@ -409,7 +729,7 @@ function deprioritizeH264(sdp) {
409
729
  const mlinePayloadsSection = /(\s\d+)+$/i.exec(mline)[0];
410
730
  const mlinePayloadsNonH264 = mlinePayloadsSection
411
731
  .split(" ")
412
- .filter((payloadType) => payloadType && !h264payloadTypes.includes(payloadType));
732
+ .filter(payloadType => payloadType && !h264payloadTypes.includes(payloadType));
413
733
  const reorderedPayloads = [...mlinePayloadsNonH264, ...h264payloadTypes].join(" ");
414
734
  const newmline = mline.replace(mlinePayloadsSection, " " + reorderedPayloads);
415
735
  return section.replace(mline, newmline);
@@ -437,12 +757,12 @@ function replaceSSRCs(currentDescription, newDescription) {
437
757
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1478685
438
758
  // filter out the mid rtp header extension
439
759
  function filterMidExtension(sdp) {
440
- if (browserName$3 !== "safari" && (browserName$3 !== "firefox" || browserVersion$1 >= 63 || browserVersion$1 === 60)) {
760
+ if (browserName$2 !== "safari" && (browserName$2 !== "firefox" || browserVersion$1 >= 63 || browserVersion$1 === 60)) {
441
761
  return sdp;
442
762
  }
443
763
  return (
444
764
  SDPUtils.splitLines(sdp.trim())
445
- .filter((line) => {
765
+ .filter(line => {
446
766
  if (!line.startsWith("a=extmap:")) {
447
767
  return true;
448
768
  }
@@ -458,21 +778,21 @@ function filterMidExtension(sdp) {
458
778
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1534673
459
779
  // Filter out a:msid-semantic header
460
780
  function filterMsidSemantic(sdp) {
461
- if (browserName$3 !== "firefox") {
781
+ if (browserName$2 !== "firefox") {
462
782
  return sdp;
463
783
  }
464
784
  return (
465
785
  SDPUtils.splitLines(sdp.trim())
466
- .map((line) => (line.startsWith("a=msid-semantic:") ? "a=msid-semantic: WMS *" : line))
786
+ .map(line => (line.startsWith("a=msid-semantic:") ? "a=msid-semantic: WMS *" : line))
467
787
  .join("\r\n") + "\r\n"
468
788
  );
469
789
  }
470
790
 
471
791
  function isRelayed(pc) {
472
- return pc.getStats(null).then((result) => {
792
+ return pc.getStats(null).then(result => {
473
793
  let localCandidateType;
474
794
  let remoteCandidateType;
475
- result.forEach((report) => {
795
+ result.forEach(report => {
476
796
  // Chrome 58+ / spec
477
797
  if (report.type === "transport" && report.selectedCandidatePairId) {
478
798
  const transport = result.get(report.selectedCandidatePairId);
@@ -502,7 +822,7 @@ const logger$4 = console;
502
822
 
503
823
  // use https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-setparameters to change the video bandwidth.
504
824
  function setVideoBandwidthUsingSetParameters(pc, bandwidth) {
505
- const sender = pc.getSenders().find((s) => s.track && s.track.kind === "video");
825
+ const sender = pc.getSenders().find(s => s.track && s.track.kind === "video");
506
826
  if (!sender) {
507
827
  return Promise.resolve();
508
828
  }
@@ -523,7 +843,7 @@ function setVideoBandwidthUsingSetParameters(pc, bandwidth) {
523
843
  parameters.encodings[0].maxBitrate = bandwidth * 1000; // convert to bps
524
844
  }
525
845
 
526
- return sender.setParameters(parameters).catch((err) => {
846
+ return sender.setParameters(parameters).catch(err => {
527
847
  logger$4.error("setParameters err: ", err);
528
848
  });
529
849
  }
@@ -548,7 +868,7 @@ class Session {
548
868
  this.streamIds = [];
549
869
  this.streams = [];
550
870
  this.earlyIceCandidates = [];
551
- this.afterConnected = new Promise((resolve) => {
871
+ this.afterConnected = new Promise(resolve => {
552
872
  this.registerConnected = resolve;
553
873
  });
554
874
  this.offerOptions = { offerToReceiveAudio: true, offerToReceiveVideo: true };
@@ -585,10 +905,10 @@ class Session {
585
905
  this.streamIds.push(stream.id);
586
906
  this.streams.push(stream);
587
907
  if (RTCPeerConnection.prototype.addTrack) {
588
- stream.getAudioTracks().forEach((track) => {
908
+ stream.getAudioTracks().forEach(track => {
589
909
  this.pc.addTrack(track, stream);
590
910
  });
591
- stream.getVideoTracks().forEach((track) => {
911
+ stream.getVideoTracks().forEach(track => {
592
912
  this.pc.addTrack(track, stream);
593
913
  });
594
914
  } else {
@@ -608,7 +928,7 @@ class Session {
608
928
  removeTrack(track) {
609
929
  const stream = this.streams[0];
610
930
  stream.removeTrack(track);
611
- const sender = this.pc.getSenders().find((sender) => sender.track === track);
931
+ const sender = this.pc.getSenders().find(sender => sender.track === track);
612
932
  if (sender) {
613
933
  this.pc.removeTrack(sender);
614
934
  }
@@ -624,8 +944,8 @@ class Session {
624
944
 
625
945
  if (this.pc) {
626
946
  if (this.pc.removeTrack) {
627
- stream.getTracks().forEach((track) => {
628
- const sender = this.pc.getSenders().find((sender) => sender.track === track);
947
+ stream.getTracks().forEach(track => {
948
+ const sender = this.pc.getSenders().find(sender => sender.track === track);
629
949
  if (sender) {
630
950
  this.pc.removeTrack(sender);
631
951
  }
@@ -643,14 +963,14 @@ class Session {
643
963
  // wrapper around SRD which stores a promise
644
964
  this.srdComplete = this.pc.setRemoteDescription(desc);
645
965
  return this.srdComplete.then(() => {
646
- this.earlyIceCandidates.forEach((candidate) => this.pc.addIceCandidate(candidate));
966
+ this.earlyIceCandidates.forEach(candidate => this.pc.addIceCandidate(candidate));
647
967
  this.earlyIceCandidates = [];
648
968
  });
649
969
  }
650
970
 
651
971
  handleOffer(message) {
652
972
  if (!this.canModifyPeerConnection()) {
653
- return new Promise((resolve) => {
973
+ return new Promise(resolve => {
654
974
  this.pending.push(() => this.handleOffer(message).then(resolve));
655
975
  });
656
976
  }
@@ -668,7 +988,7 @@ class Session {
668
988
  .then(() => {
669
989
  return this.pc.createAnswer();
670
990
  })
671
- .then((answer) => {
991
+ .then(answer => {
672
992
  answerToSignal = answer;
673
993
  return this.pc.setLocalDescription(answer);
674
994
  })
@@ -696,7 +1016,7 @@ class Session {
696
1016
  () => {
697
1017
  return setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
698
1018
  },
699
- (e) => {
1019
+ e => {
700
1020
  logger$3.warn("Could not set remote description from remote answer: ", e);
701
1021
  }
702
1022
  );
@@ -717,7 +1037,7 @@ class Session {
717
1037
  // filter due to https://github.com/webrtcHacks/adapter/issues/863
718
1038
  return;
719
1039
  }
720
- this.pc.addIceCandidate(candidate).catch((e) => {
1040
+ this.pc.addIceCandidate(candidate).catch(e => {
721
1041
  logger$3.warn("Failed to add ICE candidate ('%s'): %s", candidate ? candidate.candidate : null, e);
722
1042
  });
723
1043
  });
@@ -755,11 +1075,11 @@ class Session {
755
1075
  if (!pc) return false;
756
1076
  const senders = pc.getSenders();
757
1077
  function dbg(msg) {
758
- const tr = (t) => t && `id:${t.id},kind:${t.kind},state:${t.readyState}`;
1078
+ const tr = t => t && `id:${t.id},kind:${t.kind},state:${t.readyState}`;
759
1079
  logger$3.warn(
760
1080
  `${msg}. newTrack:${tr(newTrack)}, oldTrack:${tr(oldTrack)}, sender tracks: ${JSON.stringify(
761
- senders.map((s) => `s ${tr(s.track)}`)
762
- )}, sender first codecs: ${JSON.stringify(senders.map((s) => (s.getParameters().codecs || [])[0]))}`
1081
+ senders.map(s => `s ${tr(s.track)}`)
1082
+ )}, sender first codecs: ${JSON.stringify(senders.map(s => (s.getParameters().codecs || [])[0]))}`
763
1083
  );
764
1084
  }
765
1085
  if (!senders.length) {
@@ -767,7 +1087,7 @@ class Session {
767
1087
  }
768
1088
  // If we didn't specify oldTrack, replace with first of its kind
769
1089
  if (!oldTrack) {
770
- oldTrack = (senders.find((s) => s.track && s.track.kind === newTrack.kind) || {}).track;
1090
+ oldTrack = (senders.find(s => s.track && s.track.kind === newTrack.kind) || {}).track;
771
1091
  if (!oldTrack) {
772
1092
  // odin: Temporary debug data, remove if you see after 2020-12-01
773
1093
  dbg("No sender with same kind! Add new track then.");
@@ -824,7 +1144,7 @@ class Session {
824
1144
  // we already know that the track has been added at least to the mediastream
825
1145
  return result;
826
1146
  }
827
- const stream = this.streams.find((s) => s.getTracks().find((t) => t.id === newTrack.id)) || this.streams[0];
1147
+ const stream = this.streams.find(s => s.getTracks().find(t => t.id === newTrack.id)) || this.streams[0];
828
1148
  if (!stream) {
829
1149
  dbg("No stream?");
830
1150
  return Promise.reject(new Error("replaceTrack: No stream?"));
@@ -853,7 +1173,7 @@ class Session {
853
1173
  if (pc.localDescription.type === "offer") {
854
1174
  return pc
855
1175
  .createOffer()
856
- .then((offer) => {
1176
+ .then(offer => {
857
1177
  offer.sdp = replaceSSRCs(pc.localDescription.sdp, offer.sdp);
858
1178
  return pc.setLocalDescription(offer);
859
1179
  })
@@ -865,7 +1185,7 @@ class Session {
865
1185
  .then(() => {
866
1186
  return pc.createAnswer();
867
1187
  })
868
- .then((answer) => {
1188
+ .then(answer => {
869
1189
  answer.sdp = replaceSSRCs(pc.localDescription.sdp, answer.sdp);
870
1190
  return pc.setLocalDescription(answer);
871
1191
  });
@@ -880,7 +1200,7 @@ class Session {
880
1200
  if (!this.pc.getStats) {
881
1201
  return;
882
1202
  }
883
- isRelayed(this.pc).then((isRelayed) => {
1203
+ isRelayed(this.pc).then(isRelayed => {
884
1204
  if (isRelayed && this.bandwidth === 0) {
885
1205
  this.changeBandwidth(this.maximumTurnBandwidth);
886
1206
  }
@@ -913,15 +1233,11 @@ const MAXIMUM_TURN_BANDWIDTH = 512; // kbps;
913
1233
  const MAXIMUM_TURN_BANDWIDTH_PREMIUM = 768; // kbps;
914
1234
 
915
1235
  /* globals process */
916
-
917
- process.env.AWF_BASE_URL;
918
- process.env.AWF_API_BASE_URL;
919
- process.env.AP_ROOM_BASE_URL;
920
1236
  const CAMERA_STREAM_ID = "0";
921
1237
 
922
1238
  const logger$2 = console;
923
1239
 
924
- const browserName$2 = adapter.browserDetails.browser;
1240
+ const browserName$1 = adapter.browserDetails.browser;
925
1241
  const browserVersion = adapter.browserDetails.version;
926
1242
 
927
1243
  class BaseRtcManager {
@@ -961,7 +1277,7 @@ class BaseRtcManager {
961
1277
  }
962
1278
 
963
1279
  numberOfRemotePeers() {
964
- return Object.values(this.peerConnections).filter((session) => session.clientId !== this._selfId).length;
1280
+ return Object.values(this.peerConnections).filter(session => session.clientId !== this._selfId).length;
965
1281
  }
966
1282
 
967
1283
  _setConnectionStatus(session, newStatus, clientId) {
@@ -1035,7 +1351,7 @@ class BaseRtcManager {
1035
1351
  // Some macs + ios devices have troubles using h264 encoder since safari 14
1036
1352
  // this will make them encode VP8 instead if available
1037
1353
  const deprioritizeH264Encoding =
1038
- browserName$2 === "safari" && browserVersion >= 14 && this._features.deprioritizeH264OnSafari;
1354
+ browserName$1 === "safari" && browserVersion >= 14 && this._features.deprioritizeH264OnSafari;
1039
1355
 
1040
1356
  this.peerConnections[peerConnectionId] = session = new Session({
1041
1357
  peerConnectionId,
@@ -1054,7 +1370,7 @@ class BaseRtcManager {
1054
1370
  }
1055
1371
 
1056
1372
  _getNonLocalCameraStreamIds() {
1057
- return Object.keys(this.localStreams).filter((streamId) => streamId !== CAMERA_STREAM_ID);
1373
+ return Object.keys(this.localStreams).filter(streamId => streamId !== CAMERA_STREAM_ID);
1058
1374
  }
1059
1375
 
1060
1376
  _isScreensharingLocally() {
@@ -1083,7 +1399,7 @@ class BaseRtcManager {
1083
1399
  }
1084
1400
  const session = this._getOrCreateSession(peerConnectionId, initialBandwidth);
1085
1401
  const constraints = { optional: [] };
1086
- if (browserName$2 === "chrome") {
1402
+ if (browserName$1 === "chrome") {
1087
1403
  constraints.optional.push({
1088
1404
  googCpuOveruseDetection: true,
1089
1405
  });
@@ -1096,13 +1412,13 @@ class BaseRtcManager {
1096
1412
  const host = this._features.turnServerOverrideHost;
1097
1413
  const port = host.indexOf(":") > 0 ? "" : ":443";
1098
1414
  const override = ":" + host + port;
1099
- peerConnectionConfig.iceServers = peerConnectionConfig.iceServers.map((original) => {
1415
+ peerConnectionConfig.iceServers = peerConnectionConfig.iceServers.map(original => {
1100
1416
  const entry = Object.assign({}, original);
1101
1417
  if (entry.url) {
1102
1418
  entry.url = entry.url.replace(/:[^?]*/, override);
1103
1419
  }
1104
1420
  if (entry.urls) {
1105
- entry.urls = entry.urls.map((url) => url.replace(/:[^?]*/, override));
1421
+ entry.urls = entry.urls.map(url => url.replace(/:[^?]*/, override));
1106
1422
  }
1107
1423
  return entry;
1108
1424
  });
@@ -1116,12 +1432,12 @@ class BaseRtcManager {
1116
1432
  }[this._features.useOnlyTURN];
1117
1433
  if (filter) {
1118
1434
  peerConnectionConfig.iceServers = peerConnectionConfig.iceServers.filter(
1119
- (entry) => entry.url && entry.url.match(filter)
1435
+ entry => entry.url && entry.url.match(filter)
1120
1436
  );
1121
1437
  }
1122
1438
  }
1123
1439
 
1124
- if (browserName$2 === "chrome") {
1440
+ if (browserName$1 === "chrome") {
1125
1441
  peerConnectionConfig.sdpSemantics = "unified-plan";
1126
1442
  }
1127
1443
 
@@ -1132,7 +1448,7 @@ class BaseRtcManager {
1132
1448
  clientId,
1133
1449
  });
1134
1450
 
1135
- pc.ontrack = (event) => {
1451
+ pc.ontrack = event => {
1136
1452
  const stream = event.streams[0];
1137
1453
  if (stream.id === "default" && stream.getAudioTracks().length === 0) {
1138
1454
  // due to our PlanB / UnifiedPlan conversion we can run into this:
@@ -1174,7 +1490,7 @@ class BaseRtcManager {
1174
1490
  case "completed":
1175
1491
  newStatus = TYPES.CONNECTION_SUCCESSFUL;
1176
1492
  if (!session.wasEverConnected) {
1177
- this._pendingActionsForConnectedPeerConnections.forEach((action) => {
1493
+ this._pendingActionsForConnectedPeerConnections.forEach(action => {
1178
1494
  if (typeof action === "function") {
1179
1495
  action();
1180
1496
  }
@@ -1186,7 +1502,7 @@ class BaseRtcManager {
1186
1502
  if (
1187
1503
  !session.wasEverConnected &&
1188
1504
  (pc.iceConnectionState.match(/connected|completed/) ||
1189
- (browserName$2 === "chrome" && pc.localDescription && pc.localDescription.type === "answer"))
1505
+ (browserName$1 === "chrome" && pc.localDescription && pc.localDescription.type === "answer"))
1190
1506
  ) {
1191
1507
  session.wasEverConnected = true;
1192
1508
  if (this._features.bandwidth !== "false") {
@@ -1257,7 +1573,7 @@ class BaseRtcManager {
1257
1573
  // Don't add existing screenshare-streams when using SFU as those will be
1258
1574
  // added in a separate session/peerConnection
1259
1575
  if (shouldAddLocalVideo) {
1260
- Object.keys(this.localStreams).forEach((id) => {
1576
+ Object.keys(this.localStreams).forEach(id => {
1261
1577
  if (id === CAMERA_STREAM_ID) {
1262
1578
  return;
1263
1579
  }
@@ -1294,27 +1610,27 @@ class BaseRtcManager {
1294
1610
  }
1295
1611
 
1296
1612
  _forEachPeerConnection(func) {
1297
- Object.keys(this.peerConnections).forEach((peerConnectionId) => {
1613
+ Object.keys(this.peerConnections).forEach(peerConnectionId => {
1298
1614
  const peerConnection = this.peerConnections[peerConnectionId];
1299
1615
  func(peerConnection);
1300
1616
  });
1301
1617
  }
1302
1618
 
1303
1619
  _addStreamToPeerConnections(stream) {
1304
- this._forEachPeerConnection((session) => {
1620
+ this._forEachPeerConnection(session => {
1305
1621
  this._withForcedRenegotiation(session, () => session.addStream(stream));
1306
1622
  });
1307
1623
  }
1308
1624
 
1309
1625
  _addTrackToPeerConnections(track, stream) {
1310
- this._forEachPeerConnection((session) => {
1626
+ this._forEachPeerConnection(session => {
1311
1627
  this._withForcedRenegotiation(session, () => session.addTrack(track, stream));
1312
1628
  });
1313
1629
  }
1314
1630
 
1315
1631
  _replaceTrackToPeerConnections(oldTrack, newTrack) {
1316
1632
  const promises = [];
1317
- this._forEachPeerConnection((session) => {
1633
+ this._forEachPeerConnection(session => {
1318
1634
  if (!session.hasConnectedPeerConnection()) {
1319
1635
  logger$2.log("Session doesn't have a connected PeerConnection, adding pending action!");
1320
1636
  const pendingActions = this._pendingActionsForConnectedPeerConnections;
@@ -1332,7 +1648,7 @@ class BaseRtcManager {
1332
1648
  reject(`ReplaceTrack returned false`);
1333
1649
  return;
1334
1650
  }
1335
- replacedTrackPromise.then((track) => resolve(track)).catch((error) => reject(error));
1651
+ replacedTrackPromise.then(track => resolve(track)).catch(error => reject(error));
1336
1652
  };
1337
1653
  pendingActions.push(action);
1338
1654
  });
@@ -1350,13 +1666,13 @@ class BaseRtcManager {
1350
1666
  }
1351
1667
 
1352
1668
  _removeStreamFromPeerConnections(stream) {
1353
- this._forEachPeerConnection((session) => {
1669
+ this._forEachPeerConnection(session => {
1354
1670
  this._withForcedRenegotiation(session, () => session.removeStream(stream));
1355
1671
  });
1356
1672
  }
1357
1673
 
1358
1674
  _removeTrackFromPeerConnections(track) {
1359
- this._forEachPeerConnection((session) => {
1675
+ this._forEachPeerConnection(session => {
1360
1676
  this._withForcedRenegotiation(session, () => session.removeTrack(track));
1361
1677
  });
1362
1678
  }
@@ -1409,11 +1725,11 @@ class BaseRtcManager {
1409
1725
  }
1410
1726
 
1411
1727
  disconnectAll() {
1412
- Object.keys(this.peerConnections).forEach((peerConnectionId) => {
1728
+ Object.keys(this.peerConnections).forEach(peerConnectionId => {
1413
1729
  this.disconnect(peerConnectionId);
1414
1730
  });
1415
1731
  this.peerConnections = {};
1416
- this._socketListenerDeregisterFunctions.forEach((func) => {
1732
+ this._socketListenerDeregisterFunctions.forEach(func => {
1417
1733
  func();
1418
1734
  });
1419
1735
  this._socketListenerDeregisterFunctions = [];
@@ -1430,7 +1746,7 @@ class BaseRtcManager {
1430
1746
  * used in getUserMedia.
1431
1747
  */
1432
1748
  fixChromeAudio(constraints) {
1433
- if (browserName$2 !== "chrome") {
1749
+ if (browserName$1 !== "chrome") {
1434
1750
  return;
1435
1751
  }
1436
1752
  const localStream = this._getLocalCameraStream();
@@ -1438,7 +1754,7 @@ class BaseRtcManager {
1438
1754
  if (!audioTrack || audioTrack.readyState !== "ended") {
1439
1755
  return;
1440
1756
  }
1441
- return navigator.mediaDevices.getUserMedia({ audio: constraints }).then((stream) => {
1757
+ return navigator.mediaDevices.getUserMedia({ audio: constraints }).then(stream => {
1442
1758
  const track = stream.getAudioTracks()[0];
1443
1759
  track.enabled = audioTrack.enabled; // retain mute state and don't accidentally unmute.
1444
1760
  localStream.removeTrack(audioTrack); // remove the old track.
@@ -1472,7 +1788,7 @@ class BaseRtcManager {
1472
1788
  this._socketListenerDeregisterFunctions = [
1473
1789
  () => this._clearMediaServersRefresh(),
1474
1790
 
1475
- this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
1791
+ this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, data => {
1476
1792
  if (data.error) {
1477
1793
  logger$2.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
1478
1794
  return;
@@ -1480,11 +1796,11 @@ class BaseRtcManager {
1480
1796
  this._updateAndScheduleMediaServersRefresh(data);
1481
1797
  }),
1482
1798
 
1483
- this._serverSocket.on(RELAY_MESSAGES.READY_TO_RECEIVE_OFFER, (data) => {
1799
+ this._serverSocket.on(RELAY_MESSAGES.READY_TO_RECEIVE_OFFER, data => {
1484
1800
  this._connect(data.clientId);
1485
1801
  }),
1486
1802
 
1487
- this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, (data) => {
1803
+ this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, data => {
1488
1804
  const session = this._getSession(data.clientId);
1489
1805
  if (!session) {
1490
1806
  logger$2.warn("No RTCPeerConnection on ICE_CANDIDATE", data);
@@ -1493,7 +1809,7 @@ class BaseRtcManager {
1493
1809
  session.addIceCandidate(data.message);
1494
1810
  }),
1495
1811
 
1496
- this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, (data) => {
1812
+ this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, data => {
1497
1813
  const session = this._getSession(data.clientId);
1498
1814
  if (!session) {
1499
1815
  logger$2.warn("No RTCPeerConnection on ICE_END_OF_CANDIDATES", data);
@@ -1503,14 +1819,14 @@ class BaseRtcManager {
1503
1819
  }),
1504
1820
 
1505
1821
  // when a new SDP offer is received from another client
1506
- this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, (data) => {
1822
+ this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, data => {
1507
1823
  const session = this._getSession(data.clientId);
1508
1824
  if (!session) {
1509
1825
  logger$2.warn("No RTCPeerConnection on SDP_OFFER", data);
1510
1826
  return;
1511
1827
  }
1512
1828
  const offer = this._transformIncomingSdp(data.message, session.pc);
1513
- session.handleOffer(offer).then((answer) => {
1829
+ session.handleOffer(offer).then(answer => {
1514
1830
  this._emitServerEvent(RELAY_MESSAGES.SDP_ANSWER, {
1515
1831
  receiverId: data.clientId,
1516
1832
  message: this._transformOutgoingSdp(answer),
@@ -1519,7 +1835,7 @@ class BaseRtcManager {
1519
1835
  }),
1520
1836
 
1521
1837
  // when a new SDP answer is received from another client
1522
- this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, (data) => {
1838
+ this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, data => {
1523
1839
  const session = this._getSession(data.clientId);
1524
1840
  if (!session) {
1525
1841
  logger$2.warn("No RTCPeerConnection on SDP_ANSWER", data);
@@ -1537,7 +1853,7 @@ class BaseRtcManager {
1537
1853
  }
1538
1854
 
1539
1855
  const logger$1 = console;
1540
- const browserName$1 = adapter.browserDetails.browser;
1856
+ const browserName = adapter.browserDetails.browser;
1541
1857
  class P2pRtcManager extends BaseRtcManager {
1542
1858
  _connect(clientId) {
1543
1859
  const shouldAddLocalVideo = true;
@@ -1589,7 +1905,7 @@ class P2pRtcManager extends BaseRtcManager {
1589
1905
  session.isOperationPending = true;
1590
1906
 
1591
1907
  pc.createOffer(constraints || this.offerOptions)
1592
- .then((offer) => {
1908
+ .then(offer => {
1593
1909
  this._emitServerEvent(RELAY_MESSAGES.SDP_OFFER, {
1594
1910
  receiverId: clientId,
1595
1911
  message: this._transformOutgoingSdp(offer),
@@ -1598,17 +1914,17 @@ class P2pRtcManager extends BaseRtcManager {
1598
1914
  // workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1394602
1599
1915
  // make Chrome send media later when there are two (more more?) video tracks.
1600
1916
  if (
1601
- browserName$1 === "chrome" &&
1602
- pc.getSenders().filter((sender) => sender.track && sender.track.kind === "video").length >= 2
1917
+ browserName === "chrome" &&
1918
+ pc.getSenders().filter(sender => sender.track && sender.track.kind === "video").length >= 2
1603
1919
  ) {
1604
1920
  session.pendingOffer = offer;
1605
1921
  return;
1606
1922
  }
1607
- pc.setLocalDescription(offer).catch((e) => {
1923
+ pc.setLocalDescription(offer).catch(e => {
1608
1924
  logger$1.warn("RTCPeerConnection.setLocalDescription() failed with local offer", e);
1609
1925
  });
1610
1926
  })
1611
- .catch((e) => {
1927
+ .catch(e => {
1612
1928
  logger$1.warn("RTCPeerConnection.createOffer() failed to create local offer", e);
1613
1929
  });
1614
1930
  }
@@ -1658,7 +1974,7 @@ class P2pRtcManager extends BaseRtcManager {
1658
1974
  return 0;
1659
1975
  }
1660
1976
 
1661
- this._forEachPeerConnection((session) => {
1977
+ this._forEachPeerConnection(session => {
1662
1978
  session.changeBandwidth(bandwidth);
1663
1979
  });
1664
1980
 
@@ -1689,7 +2005,7 @@ class P2pRtcManager extends BaseRtcManager {
1689
2005
  pc.addTrack(this._stoppedVideoTrack, localCameraStream);
1690
2006
  }
1691
2007
 
1692
- pc.onicecandidate = (event) => {
2008
+ pc.onicecandidate = event => {
1693
2009
  if (event.candidate) {
1694
2010
  session.relayCandidateSeen = session.relayCandidateSeen || event.candidate.type === "relay";
1695
2011
  this._emitServerEvent(RELAY_MESSAGES.ICE_CANDIDATE, {
@@ -1746,7 +2062,7 @@ class P2pRtcManager extends BaseRtcManager {
1746
2062
  const numPeers = this.numberOfPeerconnections();
1747
2063
  if (numPeers === 0) {
1748
2064
  setTimeout(() => {
1749
- this.numberOfPeerconnections();
2065
+ //const numPeers = this.numberOfPeerconnections();
1750
2066
  }, 60 * 1000);
1751
2067
  }
1752
2068
  }
@@ -1760,13 +2076,13 @@ class P2pRtcManager extends BaseRtcManager {
1760
2076
 
1761
2077
  stopOrResumeVideo(localStream, enable) {
1762
2078
  // actually turn off the camera. Chrome-only (Firefox has different plans)
1763
- if (browserName$1 !== "chrome") {
2079
+ if (browserName !== "chrome") {
1764
2080
  return;
1765
2081
  }
1766
2082
  if (enable === false) {
1767
2083
  // try to stop the local camera so the camera light goes off.
1768
2084
  setTimeout(() => {
1769
- localStream.getVideoTracks().forEach((track) => {
2085
+ localStream.getVideoTracks().forEach(track => {
1770
2086
  if (track.enabled === false) {
1771
2087
  track.stop();
1772
2088
  localStream.removeTrack(track);
@@ -1805,7 +2121,7 @@ class P2pRtcManager extends BaseRtcManager {
1805
2121
  // device has been plugged out or similar
1806
2122
  return;
1807
2123
  }
1808
- navigator.mediaDevices.getUserMedia({ video: constraints }).then((stream) => {
2124
+ navigator.mediaDevices.getUserMedia({ video: constraints }).then(stream => {
1809
2125
  const track = stream.getVideoTracks()[0];
1810
2126
  localStream.addTrack(track);
1811
2127
  this._emit(EVENTS.LOCAL_STREAM_TRACK_ADDED, {
@@ -1964,10 +2280,11 @@ class VegaParser {
1964
2280
  }
1965
2281
 
1966
2282
  class VegaConnection extends EventEmitter {
1967
- constructor(wsUrl, logger) {
2283
+ constructor(wsUrl, logger, protocol = "whereby-sfu#v4") {
1968
2284
  super();
1969
2285
 
1970
2286
  this.wsUrl = wsUrl;
2287
+ this.protocol = protocol;
1971
2288
  this.logger = logger;
1972
2289
 
1973
2290
  // This is the map of sent requests that are waiting for a response
@@ -1976,7 +2293,7 @@ class VegaConnection extends EventEmitter {
1976
2293
  }
1977
2294
 
1978
2295
  _setupSocket() {
1979
- this.socket = new WebSocket(this.wsUrl, "whereby-sfu#v4");
2296
+ this.socket = new WebSocket(this.wsUrl, this.protocol);
1980
2297
  this.socket.onopen = this._onOpen.bind(this);
1981
2298
  this.socket.onmessage = this._onMessage.bind(this);
1982
2299
  this.socket.onclose = this._onClose.bind(this);
@@ -1990,13 +2307,15 @@ class VegaConnection extends EventEmitter {
1990
2307
  this.socket.onerror = null;
1991
2308
  this.socket = null;
1992
2309
 
1993
- this.sents.forEach((sent) => sent.close());
2310
+ this.sents.forEach(sent => sent.close());
1994
2311
 
1995
2312
  this.emit("close");
1996
2313
  }
1997
2314
 
1998
2315
  close() {
1999
- this.socket?.close();
2316
+ if (!this.socket) return;
2317
+
2318
+ this.socket.close();
2000
2319
  }
2001
2320
 
2002
2321
  _onOpen() {
@@ -2008,11 +2327,15 @@ class VegaConnection extends EventEmitter {
2008
2327
  _onMessage(event) {
2009
2328
  const socketMessage = VegaParser.parse(event.data);
2010
2329
 
2330
+ if (!socketMessage) {
2331
+ return this.logger.log("VegaConnectionManager: Received invalid message", event.data);
2332
+ }
2333
+
2011
2334
  this.logger.log("VegaConnectionManager: Received message", socketMessage);
2012
2335
 
2013
- if (socketMessage?.response) {
2336
+ if (socketMessage.response) {
2014
2337
  this._handleResponse(socketMessage);
2015
- } else if (socketMessage?.message) {
2338
+ } else if (socketMessage.message) {
2016
2339
  this.emit("message", socketMessage);
2017
2340
  }
2018
2341
  }
@@ -2058,13 +2381,13 @@ class VegaConnection extends EventEmitter {
2058
2381
  const sent = {
2059
2382
  id: request.id,
2060
2383
  method: request.method,
2061
- resolve: (data2) => {
2384
+ resolve: data2 => {
2062
2385
  if (!this.sents.delete(request.id)) return;
2063
2386
 
2064
2387
  clearTimeout(sent.timer);
2065
2388
  pResolve(data2);
2066
2389
  },
2067
- reject: (error) => {
2390
+ reject: error => {
2068
2391
  if (!this.sents.delete(request.id)) return;
2069
2392
 
2070
2393
  clearTimeout(sent.timer);
@@ -2119,7 +2442,7 @@ const VIDEO_SETTINGS_VP9 = {
2119
2442
  codecOptions: {
2120
2443
  videoGoogleStartBitrate: 500,
2121
2444
  },
2122
- encodings: [{ scalabilityMode: "S3T3_KEY", networkPriority: "high" }],
2445
+ encodings: [{ scalabilityMode: "L3T2_KEY", networkPriority: "high" }],
2123
2446
  };
2124
2447
 
2125
2448
  const SCREEN_SHARE_SETTINGS = {
@@ -2128,13 +2451,13 @@ const SCREEN_SHARE_SETTINGS = {
2128
2451
 
2129
2452
  const SCREEN_SHARE_SIMULCAST_SETTINGS = {
2130
2453
  encodings: [
2131
- { dtx: true, maxBitrate: 500000 },
2132
- { dtx: true, maxBitrate: 1500000 },
2454
+ { scaleResolutionDownBy: 2, dtx: true, maxBitrate: 500000 },
2455
+ { scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
2133
2456
  ],
2134
2457
  };
2135
2458
 
2136
2459
  const SCREEN_SHARE_SETTINGS_VP9 = {
2137
- encodings: [{ scalabilityMode: "S3T3", dtx: true, networkPriority: "high" }],
2460
+ encodings: [{ scalabilityMode: "L1T1", dtx: true, networkPriority: "high" }],
2138
2461
  };
2139
2462
 
2140
2463
  const getMediaSettings = (kind, isScreenShare, features) => {
@@ -2150,8 +2473,8 @@ const getMediaSettings = (kind, isScreenShare, features) => {
2150
2473
 
2151
2474
  return SCREEN_SHARE_SETTINGS;
2152
2475
  } else {
2153
- if (lowDataModeEnabled) return VIDEO_SETTINGS_SD;
2154
2476
  if (vp9On) return VIDEO_SETTINGS_VP9;
2477
+ if (lowDataModeEnabled) return VIDEO_SETTINGS_SD;
2155
2478
 
2156
2479
  return VIDEO_SETTINGS_HD;
2157
2480
  }
@@ -2162,30 +2485,30 @@ const modifyMediaCapabilities = (routerRtpCapabilities, features) => {
2162
2485
 
2163
2486
  if (vp9On) {
2164
2487
  const { preferredPayloadType } = routerRtpCapabilities.codecs.find(
2165
- (codec) => codec.mimeType.toLowerCase() === "video/vp9"
2488
+ codec => codec.mimeType.toLowerCase() === "video/vp9"
2166
2489
  );
2167
2490
 
2168
2491
  const { preferredPayloadType: aptPreferredPayloadType } = routerRtpCapabilities.codecs.find(
2169
- (codec) => codec.mimeType.toLowerCase() === "video/rtx" && codec.parameters.apt === preferredPayloadType
2492
+ codec => codec.mimeType.toLowerCase() === "video/rtx" && codec.parameters.apt === preferredPayloadType
2170
2493
  );
2171
2494
 
2172
2495
  routerRtpCapabilities.codecs = routerRtpCapabilities.codecs.filter(
2173
- (codec) =>
2496
+ codec =>
2174
2497
  codec.kind === "audio" ||
2175
2498
  codec.preferredPayloadType === preferredPayloadType ||
2176
2499
  codec.preferredPayloadType === aptPreferredPayloadType
2177
2500
  );
2178
2501
  } else if (h264On) {
2179
2502
  const { preferredPayloadType } = routerRtpCapabilities.codecs.find(
2180
- (codec) => codec.mimeType.toLowerCase() === "video/h264"
2503
+ codec => codec.mimeType.toLowerCase() === "video/h264"
2181
2504
  );
2182
2505
 
2183
2506
  const { preferredPayloadType: aptPreferredPayloadType } = routerRtpCapabilities.codecs.find(
2184
- (codec) => codec.mimeType.toLowerCase() === "video/rtx" && codec.parameters.apt === preferredPayloadType
2507
+ codec => codec.mimeType.toLowerCase() === "video/rtx" && codec.parameters.apt === preferredPayloadType
2185
2508
  );
2186
2509
 
2187
2510
  routerRtpCapabilities.codecs = routerRtpCapabilities.codecs.filter(
2188
- (codec) =>
2511
+ codec =>
2189
2512
  codec.kind === "audio" ||
2190
2513
  codec.preferredPayloadType === preferredPayloadType ||
2191
2514
  codec.preferredPayloadType === aptPreferredPayloadType
@@ -2219,7 +2542,7 @@ const maybeTurnOnly = (transportConfig, features) => {
2219
2542
  }[features.useOnlyTURN];
2220
2543
 
2221
2544
  if (filter) {
2222
- transportConfig.iceServers = transportConfig.iceServers.filter((entry) => entry.url && entry.url.match(filter));
2545
+ transportConfig.iceServers = transportConfig.iceServers.filter(entry => entry.url && entry.url.match(filter));
2223
2546
  }
2224
2547
  };
2225
2548
 
@@ -2323,7 +2646,7 @@ class VegaRtcManager {
2323
2646
  this._socketListenerDeregisterFunctions.push(
2324
2647
  () => this._clearMediaServersRefresh(),
2325
2648
 
2326
- this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
2649
+ this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, data => {
2327
2650
  if (data.error) {
2328
2651
  logger.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
2329
2652
  return;
@@ -2360,7 +2683,7 @@ class VegaRtcManager {
2360
2683
  this._vegaConnection = new VegaConnection(wsUrl, logger);
2361
2684
  this._vegaConnection.on("open", () => this._join());
2362
2685
  this._vegaConnection.on("close", () => this._onClose());
2363
- this._vegaConnection.on("message", (message) => this._onMessage(message));
2686
+ this._vegaConnection.on("message", message => this._onMessage(message));
2364
2687
  }
2365
2688
 
2366
2689
  _onClose() {
@@ -2441,7 +2764,7 @@ class VegaRtcManager {
2441
2764
  maybeTurnOnly(transportOptions, this._features);
2442
2765
 
2443
2766
  const transport = this._mediasoupDevice[creator](transportOptions);
2444
- const onConnectionStateListener = async (connectionState) => {
2767
+ const onConnectionStateListener = async connectionState => {
2445
2768
  logger.debug(`Transport ConnectionStateChanged ${connectionState}`);
2446
2769
  if (connectionState !== "disconnected" && connectionState !== "failed") {
2447
2770
  return;
@@ -2553,7 +2876,7 @@ class VegaRtcManager {
2553
2876
  const error = await transport
2554
2877
  .restartIce({ iceParameters })
2555
2878
  .then(() => null)
2556
- .catch((err) => err);
2879
+ .catch(err => err);
2557
2880
 
2558
2881
  if (error) {
2559
2882
  logger.error(`_restartIce: ICE restart failed: ${error}`);
@@ -2564,7 +2887,7 @@ class VegaRtcManager {
2564
2887
  break;
2565
2888
  default:
2566
2889
  // exponential backoff
2567
- await new Promise((resolve) => {
2890
+ await new Promise(resolve => {
2568
2891
  setTimeout(() => {
2569
2892
  resolve();
2570
2893
  }, Math.min(RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS * 2 ** retried, 60000));
@@ -2574,7 +2897,7 @@ class VegaRtcManager {
2574
2897
  }
2575
2898
  return;
2576
2899
  }
2577
- await new Promise((resolve) => {
2900
+ await new Promise(resolve => {
2578
2901
  setTimeout(() => {
2579
2902
  resolve();
2580
2903
  }, 60000 * Math.min(8, retried + 1));
@@ -3148,54 +3471,6 @@ class VegaRtcManager {
3148
3471
  this._webcamPaused = !enable;
3149
3472
 
3150
3473
  this._pauseResumeWebcam();
3151
-
3152
- if (browserName === "chrome") {
3153
- // actually turn off the camera. Chrome-only (Firefox etc. has different plans)
3154
-
3155
- if (!enable) {
3156
- clearTimeout(this._stopCameraTimeout);
3157
-
3158
- // try to stop the local camera so the camera light goes off.
3159
- this._stopCameraTimeout = setTimeout(() => {
3160
- localStream.getVideoTracks().forEach((track) => {
3161
- if (track.enabled === false) {
3162
- track.stop();
3163
- localStream.removeTrack(track);
3164
-
3165
- this._emitToPWA(EVENTS.LOCAL_STREAM_TRACK_REMOVED, {
3166
- stream: localStream,
3167
- track,
3168
- });
3169
-
3170
- if (
3171
- this._webcamProducer &&
3172
- !this._webcamProducer.closed &&
3173
- this._webcamProducer.track === track
3174
- ) {
3175
- this._stopProducer(this._webcamProducer);
3176
- this._webcamProducer = null;
3177
- this._webcamTrack = null;
3178
- }
3179
- }
3180
- });
3181
- }, 5000);
3182
- } else if (localStream.getVideoTracks().length === 0) {
3183
- // re-enable the stream
3184
- const constraints = this._webrtcProvider.getMediaConstraints().video;
3185
- navigator.mediaDevices.getUserMedia({ video: constraints }).then((stream) => {
3186
- const track = stream.getVideoTracks()[0];
3187
- localStream.addTrack(track);
3188
-
3189
- this._emitToPWA(EVENTS.LOCAL_STREAM_TRACK_ADDED, {
3190
- streamId: localStream.id,
3191
- tracks: [track],
3192
- screenShare: false,
3193
- });
3194
-
3195
- this._sendWebcam(track);
3196
- });
3197
- }
3198
- }
3199
3474
  }
3200
3475
 
3201
3476
  supportsScreenShareAudio() {
@@ -3262,7 +3537,7 @@ class VegaRtcManager {
3262
3537
  }
3263
3538
 
3264
3539
  disconnectAll() {
3265
- this._socketListenerDeregisterFunctions.forEach((func) => {
3540
+ this._socketListenerDeregisterFunctions.forEach(func => {
3266
3541
  func();
3267
3542
  });
3268
3543
 
@@ -3305,7 +3580,7 @@ class VegaRtcManager {
3305
3580
  return;
3306
3581
  }
3307
3582
  })
3308
- .catch((error) => {
3583
+ .catch(error => {
3309
3584
  console.error('"message" failed [error:%o]', error);
3310
3585
  });
3311
3586
  }
@@ -3440,7 +3715,7 @@ class VegaRtcManager {
3440
3715
  const toPauseConsumers = [];
3441
3716
  const toResumeConsumers = [];
3442
3717
 
3443
- this._consumers.forEach((consumer) => {
3718
+ this._consumers.forEach(consumer => {
3444
3719
  if (consumer.appData.sourceClientId !== clientId) return;
3445
3720
 
3446
3721
  const hasAccepted = consumer.appData.screenShare ? hasAcceptedScreenStream : hasAcceptedWebcamStream;
@@ -3478,6 +3753,7 @@ class VegaRtcManager {
3478
3753
  clientId,
3479
3754
  stream: webcamStream,
3480
3755
  streamId: camStreamId,
3756
+ streamType: "webcam",
3481
3757
  });
3482
3758
 
3483
3759
  clientState.hasEmittedWebcamStream = true;
@@ -3489,6 +3765,7 @@ class VegaRtcManager {
3489
3765
  clientId,
3490
3766
  stream: screenStream,
3491
3767
  streamId: screenShareStreamId,
3768
+ streamType: "screenshare",
3492
3769
  });
3493
3770
 
3494
3771
  clientState.hasEmittedScreenStream = true;
@@ -4991,6 +5268,15 @@ class RemoteParticipant extends RoomParticipant {
4991
5268
  this.newJoiner = newJoiner;
4992
5269
  this.streams = streams.map((streamId) => ({ id: streamId, state: newJoiner ? "new_accept" : "to_accept" }));
4993
5270
  }
5271
+ addStream(streamId, state) {
5272
+ this.streams.push({ id: streamId, state });
5273
+ }
5274
+ removeStream(streamId) {
5275
+ const index = this.streams.findIndex((s) => s.id === streamId);
5276
+ if (index !== -1) {
5277
+ this.streams.splice(index, 1);
5278
+ }
5279
+ }
4994
5280
  updateStreamState(streamId, state) {
4995
5281
  const stream = this.streams.find((s) => s.id === streamId);
4996
5282
  if (stream) {
@@ -5005,19 +5291,23 @@ class LocalParticipant extends RoomParticipant {
5005
5291
  }
5006
5292
  }
5007
5293
 
5008
- const API_BASE_URL = "https://api.appearin.net";
5009
- const SIGNAL_BASE_URL = "wss://signal.appearin.net";
5294
+ const API_BASE_URL = process.env["REACT_APP_API_BASE_URL"] || "https://api.whereby.dev";
5295
+ const SIGNAL_BASE_URL = process.env["REACT_APP_SIGNAL_BASE_URL"] || "wss://signal.appearin.net";
5010
5296
  const NON_PERSON_ROLES = ["recorder", "streamer"];
5297
+ // cache last reported stream resolutions
5298
+ const reportedStreamResolutions = new Map();
5011
5299
  function createSocket() {
5012
5300
  const parsedUrl = new URL(SIGNAL_BASE_URL);
5013
- const path = `${parsedUrl.pathname.replace(/^\/$/, "")}/protocol/socket.io/v1`;
5301
+ const path = `${parsedUrl.pathname.replace(/^\/$/, "")}/protocol/socket.io/v4`;
5014
5302
  const SOCKET_HOST = parsedUrl.origin;
5015
5303
  const socketConf = {
5304
+ autoConnect: false,
5016
5305
  host: SOCKET_HOST,
5017
5306
  path,
5018
5307
  reconnectionDelay: 5000,
5019
5308
  reconnectionDelayMax: 30000,
5020
5309
  timeout: 10000,
5310
+ withCredentials: true,
5021
5311
  };
5022
5312
  return new ServerSocket(SOCKET_HOST, socketConf);
5023
5313
  }
@@ -5026,12 +5316,20 @@ const noop = () => {
5026
5316
  };
5027
5317
  const TypedEventTarget = EventTarget;
5028
5318
  class RoomConnection extends TypedEventTarget {
5029
- constructor(roomUrl, { displayName, localMediaConstraints, localStream, logger }) {
5319
+ constructor(roomUrl, { displayName, localMedia, localMediaConstraints, logger, roomKey }) {
5030
5320
  super();
5031
5321
  this.localParticipant = null;
5032
5322
  this.remoteParticipants = [];
5033
- this.roomConnectionState = "";
5323
+ this.screenshares = [];
5324
+ this._deviceCredentials = null;
5325
+ this._ownsLocalMedia = false;
5326
+ this.organizationId = "";
5327
+ this.roomConnectionStatus = "";
5328
+ this.selfId = null;
5034
5329
  this.roomUrl = new URL(roomUrl); // Throw if invalid Whereby room url
5330
+ const searchParams = new URLSearchParams(this.roomUrl.search);
5331
+ this._roomKey = roomKey || searchParams.get("roomKey");
5332
+ this.roomName = this.roomUrl.pathname;
5035
5333
  this.logger = logger || {
5036
5334
  debug: noop,
5037
5335
  error: noop,
@@ -5039,10 +5337,20 @@ class RoomConnection extends TypedEventTarget {
5039
5337
  warn: noop,
5040
5338
  };
5041
5339
  this.displayName = displayName;
5042
- this.localStream = localStream;
5043
5340
  this.localMediaConstraints = localMediaConstraints;
5044
5341
  const urls = fromLocation({ host: this.roomUrl.host });
5045
- // Initialize services
5342
+ // Set up local media
5343
+ if (localMedia) {
5344
+ this.localMedia = localMedia;
5345
+ }
5346
+ else if (localMediaConstraints) {
5347
+ this.localMedia = new LocalMedia(localMediaConstraints);
5348
+ this._ownsLocalMedia = true;
5349
+ }
5350
+ else {
5351
+ throw new Error("Missing constraints");
5352
+ }
5353
+ // Set up services
5046
5354
  this.credentialsService = CredentialsService.create({ baseUrl: API_BASE_URL });
5047
5355
  this.apiClient = new ApiClient({
5048
5356
  fetchDeviceCredentials: this.credentialsService.getCredentials.bind(this.credentialsService),
@@ -5064,12 +5372,93 @@ class RoomConnection extends TypedEventTarget {
5064
5372
  // Create signal socket and set up event listeners
5065
5373
  this.signalSocket = createSocket();
5066
5374
  this.signalSocket.on("new_client", this._handleNewClient.bind(this));
5375
+ this.signalSocket.on("chat_message", this._handleNewChatMessage.bind(this));
5067
5376
  this.signalSocket.on("client_left", this._handleClientLeft.bind(this));
5068
5377
  this.signalSocket.on("audio_enabled", this._handleClientAudioEnabled.bind(this));
5069
5378
  this.signalSocket.on("video_enabled", this._handleClientVideoEnabled.bind(this));
5070
5379
  this.signalSocket.on("client_metadata_received", this._handleClientMetadataReceived.bind(this));
5380
+ this.signalSocket.on("knock_handled", this._handleKnockHandled.bind(this));
5381
+ this.signalSocket.on("knocker_left", this._handleKnockerLeft.bind(this));
5382
+ this.signalSocket.on("room_joined", this._handleRoomJoined.bind(this));
5383
+ this.signalSocket.on("room_knocked", this._handleRoomKnocked.bind(this));
5384
+ this.signalSocket.on("cloud_recording_stopped", this._handleCloudRecordingStopped.bind(this));
5385
+ this.signalSocket.on("screenshare_started", this._handleScreenshareStarted.bind(this));
5386
+ this.signalSocket.on("screenshare_stopped", this._handleScreenshareStopped.bind(this));
5387
+ this.signalSocket.on("streaming_stopped", this._handleStreamingStopped.bind(this));
5388
+ this.signalSocket.on("disconnect", this._handleDisconnect.bind(this));
5389
+ this.signalSocket.on("connect_error", this._handleDisconnect.bind(this));
5390
+ this.signalSocketManager = this.signalSocket.getManager();
5391
+ this.signalSocketManager.on("reconnect", this._handleReconnect.bind(this));
5392
+ // Set up local media listeners
5393
+ this.localMedia.addEventListener("camera_enabled", (e) => {
5394
+ const { enabled } = e.detail;
5395
+ this.signalSocket.emit("enable_video", { enabled });
5396
+ });
5397
+ this.localMedia.addEventListener("microphone_enabled", (e) => {
5398
+ const { enabled } = e.detail;
5399
+ this.signalSocket.emit("enable_audio", { enabled });
5400
+ });
5401
+ const webrtcProvider = {
5402
+ getMediaConstraints: () => ({
5403
+ audio: this.localMedia.isMicrophoneEnabled(),
5404
+ video: this.localMedia.isCameraEnabled(),
5405
+ }),
5406
+ deferrable(clientId) {
5407
+ return !clientId;
5408
+ },
5409
+ };
5410
+ this.rtcManagerDispatcher = new RtcManagerDispatcher({
5411
+ emitter: {
5412
+ emit: this._handleRtcEvent.bind(this),
5413
+ },
5414
+ serverSocket: this.signalSocket,
5415
+ webrtcProvider,
5416
+ features: {
5417
+ lowDataModeEnabled: false,
5418
+ sfuServerOverrideHost: undefined,
5419
+ turnServerOverrideHost: undefined,
5420
+ useOnlyTURN: undefined,
5421
+ vp9On: false,
5422
+ h264On: false,
5423
+ simulcastScreenshareOn: false,
5424
+ },
5425
+ });
5426
+ }
5427
+ get roomKey() {
5428
+ return this._roomKey;
5429
+ }
5430
+ _handleNewChatMessage(message) {
5431
+ this.dispatchEvent(new CustomEvent("chat_message", { detail: message }));
5432
+ }
5433
+ _handleCloudRecordingStarted({ client }) {
5434
+ this.dispatchEvent(new CustomEvent("cloud_recording_started", {
5435
+ detail: {
5436
+ status: "recording",
5437
+ startedAt: client.startedCloudRecordingAt
5438
+ ? new Date(client.startedCloudRecordingAt).getTime()
5439
+ : new Date().getTime(),
5440
+ },
5441
+ }));
5442
+ }
5443
+ _handleStreamingStarted() {
5444
+ this.dispatchEvent(new CustomEvent("streaming_started", {
5445
+ detail: {
5446
+ status: "streaming",
5447
+ // We don't have the streaming start time stored on the
5448
+ // server, so we use the current time instead. This gives
5449
+ // an invalid timestamp for "Client B" if "Client A" has
5450
+ // been streaming for a while before "Client B" joins.
5451
+ startedAt: new Date().getTime(),
5452
+ },
5453
+ }));
5071
5454
  }
5072
5455
  _handleNewClient({ client }) {
5456
+ if (client.role.roleName === "recorder") {
5457
+ this._handleCloudRecordingStarted({ client });
5458
+ }
5459
+ if (client.role.roleName === "streamer") {
5460
+ this._handleStreamingStarted();
5461
+ }
5073
5462
  if (NON_PERSON_ROLES.includes(client.role.roleName)) {
5074
5463
  return;
5075
5464
  }
@@ -5113,6 +5502,135 @@ class RoomConnection extends TypedEventTarget {
5113
5502
  detail: { participantId: remoteParticipant.id, displayName },
5114
5503
  }));
5115
5504
  }
5505
+ _handleKnockHandled(payload) {
5506
+ const { clientId, resolution } = payload;
5507
+ // If the knocker is not the local participant, ignore the event
5508
+ if (clientId !== this.selfId) {
5509
+ return;
5510
+ }
5511
+ if (resolution === "accepted") {
5512
+ this.roomConnectionStatus = "accepted";
5513
+ this._roomKey = payload.metadata.roomKey;
5514
+ this._joinRoom();
5515
+ }
5516
+ else if (resolution === "rejected") {
5517
+ this.roomConnectionStatus = "rejected";
5518
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5519
+ detail: {
5520
+ roomConnectionStatus: this.roomConnectionStatus,
5521
+ },
5522
+ }));
5523
+ }
5524
+ }
5525
+ _handleKnockerLeft(payload) {
5526
+ const { clientId } = payload;
5527
+ this.dispatchEvent(new CustomEvent("waiting_participant_left", {
5528
+ detail: { participantId: clientId },
5529
+ }));
5530
+ }
5531
+ _handleRoomJoined(event) {
5532
+ const { error, isLocked, room, selfId } = event;
5533
+ this.selfId = selfId;
5534
+ if (error === "room_locked" && isLocked) {
5535
+ this.roomConnectionStatus = "room_locked";
5536
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5537
+ detail: {
5538
+ roomConnectionStatus: this.roomConnectionStatus,
5539
+ },
5540
+ }));
5541
+ return;
5542
+ }
5543
+ // Check if we have an error
5544
+ // Check if it is a room joined error
5545
+ // Set state to connect_failed_locked
5546
+ // Set state to connect_failed_no_host
5547
+ if (room) {
5548
+ const { clients, knockers } = room;
5549
+ const localClient = clients.find((c) => c.id === selfId);
5550
+ if (!localClient)
5551
+ throw new Error("Missing local client");
5552
+ this.localParticipant = new LocalParticipant(Object.assign(Object.assign({}, localClient), { stream: this.localMedia.stream || undefined }));
5553
+ const recorderClient = clients.find((c) => c.role.roleName === "recorder");
5554
+ if (recorderClient) {
5555
+ this._handleCloudRecordingStarted({ client: recorderClient });
5556
+ }
5557
+ const streamerClient = clients.find((c) => c.role.roleName === "streamer");
5558
+ if (streamerClient) {
5559
+ this._handleStreamingStarted();
5560
+ }
5561
+ this.remoteParticipants = clients
5562
+ .filter((c) => c.id !== selfId)
5563
+ .filter((c) => !NON_PERSON_ROLES.includes(c.role.roleName))
5564
+ .map((c) => new RemoteParticipant(Object.assign(Object.assign({}, c), { newJoiner: false })));
5565
+ this.roomConnectionStatus = "connected";
5566
+ this.dispatchEvent(new CustomEvent("room_joined", {
5567
+ detail: {
5568
+ localParticipant: this.localParticipant,
5569
+ remoteParticipants: this.remoteParticipants,
5570
+ waitingParticipants: knockers.map((knocker) => {
5571
+ return { id: knocker.clientId, displayName: knocker.displayName };
5572
+ }),
5573
+ },
5574
+ }));
5575
+ }
5576
+ }
5577
+ _handleRoomKnocked(event) {
5578
+ const { clientId, displayName } = event;
5579
+ this.dispatchEvent(new CustomEvent("waiting_participant_joined", {
5580
+ detail: { participantId: clientId, displayName },
5581
+ }));
5582
+ }
5583
+ _handleReconnect() {
5584
+ this.logger.log("Reconnected to signal socket");
5585
+ this.signalSocket.emit("identify_device", { deviceCredentials: this._deviceCredentials });
5586
+ this.signalSocket.once("device_identified", () => {
5587
+ this._joinRoom();
5588
+ });
5589
+ }
5590
+ _handleDisconnect() {
5591
+ this.roomConnectionStatus = "disconnected";
5592
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5593
+ detail: {
5594
+ roomConnectionStatus: this.roomConnectionStatus,
5595
+ },
5596
+ }));
5597
+ }
5598
+ _handleCloudRecordingStopped() {
5599
+ this.dispatchEvent(new CustomEvent("cloud_recording_stopped"));
5600
+ }
5601
+ _handleStreamingStopped() {
5602
+ this.dispatchEvent(new CustomEvent("streaming_stopped"));
5603
+ }
5604
+ _handleScreenshareStarted(screenshare) {
5605
+ const { clientId: participantId, streamId: id, hasAudioTrack } = screenshare;
5606
+ const remoteParticipant = this.remoteParticipants.find((p) => p.id === participantId);
5607
+ if (!remoteParticipant) {
5608
+ this.logger.log("WARN: Could not find participant for screenshare");
5609
+ return;
5610
+ }
5611
+ const foundScreenshare = this.screenshares.find((s) => s.id === id);
5612
+ if (foundScreenshare) {
5613
+ this.logger.log("WARN: Screenshare already exists");
5614
+ return;
5615
+ }
5616
+ remoteParticipant.addStream(id, "to_accept");
5617
+ this._handleAcceptStreams([remoteParticipant]);
5618
+ this.screenshares = [
5619
+ ...this.screenshares,
5620
+ { participantId, id, hasAudioTrack, stream: undefined, isLocal: false },
5621
+ ];
5622
+ }
5623
+ _handleScreenshareStopped(screenshare) {
5624
+ const { clientId: participantId, streamId: id } = screenshare;
5625
+ const remoteParticipant = this.remoteParticipants.find((p) => p.id === participantId);
5626
+ if (!remoteParticipant) {
5627
+ this.logger.log("WARN: Could not find participant for screenshare");
5628
+ return;
5629
+ }
5630
+ remoteParticipant.removeStream(id);
5631
+ this.screenshares = this.screenshares.filter((s) => !(s.participantId === participantId && s.id === id));
5632
+ this.dispatchEvent(new CustomEvent("screenshare_stopped", { detail: { participantId, id } }));
5633
+ }
5116
5634
  _handleRtcEvent(eventName, data) {
5117
5635
  if (eventName === "rtc_manager_created") {
5118
5636
  return this._handleRtcManagerCreated(data);
@@ -5120,17 +5638,27 @@ class RoomConnection extends TypedEventTarget {
5120
5638
  else if (eventName === "stream_added") {
5121
5639
  return this._handleStreamAdded(data);
5122
5640
  }
5641
+ else if (eventName === "rtc_manager_destroyed") {
5642
+ return this._handleRtcManagerDestroyed();
5643
+ }
5123
5644
  else {
5124
5645
  this.logger.log(`Unhandled RTC event ${eventName}`);
5125
5646
  }
5126
5647
  }
5127
5648
  _handleRtcManagerCreated({ rtcManager }) {
5128
- var _a, _b, _c;
5649
+ var _a;
5129
5650
  this.rtcManager = rtcManager;
5130
- if (this.localStream) {
5131
- (_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.addNewStream("0", this.localStream, !((_b = this.localStream) === null || _b === void 0 ? void 0 : _b.getAudioTracks().find((t) => t.enabled)), !((_c = this.localStream) === null || _c === void 0 ? void 0 : _c.getVideoTracks().find((t) => t.enabled)));
5651
+ this.localMedia.addRtcManager(rtcManager);
5652
+ if (this.localMedia.stream) {
5653
+ (_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.addNewStream("0", this.localMedia.stream, !this.localMedia.isMicrophoneEnabled(), !this.localMedia.isCameraEnabled());
5654
+ }
5655
+ if (this.remoteParticipants.length) {
5656
+ this._handleAcceptStreams(this.remoteParticipants);
5132
5657
  }
5133
5658
  }
5659
+ _handleRtcManagerDestroyed() {
5660
+ this.rtcManager = undefined;
5661
+ }
5134
5662
  _handleAcceptStreams(remoteParticipants) {
5135
5663
  var _a, _b;
5136
5664
  if (!this.rtcManager) {
@@ -5153,8 +5681,6 @@ class RoomConnection extends TypedEventTarget {
5153
5681
  if (!newState) {
5154
5682
  return;
5155
5683
  }
5156
- // #endregion
5157
- // #region doAcceptStreams
5158
5684
  if (newState === "to_accept" ||
5159
5685
  (newState === "new_accept" && shouldAcceptNewClients) ||
5160
5686
  (newState === "old_accept" && !shouldAcceptNewClients)) {
@@ -5178,155 +5704,118 @@ class RoomConnection extends TypedEventTarget {
5178
5704
  else ;
5179
5705
  // Update stream state
5180
5706
  participant.updateStreamState(streamId, streamState.replace(/to_|new_|old_/, "done_"));
5181
- // #endregion
5182
5707
  });
5183
5708
  });
5184
5709
  }
5185
- _handleStreamAdded({ clientId, stream, streamId }) {
5710
+ _handleStreamAdded({ clientId, stream, streamId, streamType }) {
5186
5711
  const remoteParticipant = this.remoteParticipants.find((p) => p.id === clientId);
5187
5712
  if (!remoteParticipant) {
5188
5713
  this.logger.log("WARN: Could not find participant for incoming stream");
5189
5714
  return;
5190
5715
  }
5191
- this.dispatchEvent(new CustomEvent("participant_stream_added", { detail: { participantId: clientId, stream, streamId } }));
5716
+ const remoteParticipantStream = remoteParticipant.streams.find((s) => s.id === streamId);
5717
+ if ((remoteParticipant.stream && remoteParticipant.stream.id === streamId) ||
5718
+ (!remoteParticipant.stream && streamType === "webcam") ||
5719
+ (!remoteParticipant.stream &&
5720
+ !streamType &&
5721
+ remoteParticipantStream &&
5722
+ remoteParticipant.streams.indexOf(remoteParticipantStream) < 1)) {
5723
+ this.dispatchEvent(new CustomEvent("participant_stream_added", { detail: { participantId: clientId, stream, streamId } }));
5724
+ return;
5725
+ }
5726
+ // screenshare
5727
+ this.dispatchEvent(new CustomEvent("screenshare_started", {
5728
+ detail: { participantId: clientId, stream, id: streamId, isLocal: false },
5729
+ }));
5730
+ }
5731
+ _joinRoom() {
5732
+ this.signalSocket.emit("join_room", {
5733
+ avatarUrl: null,
5734
+ config: {
5735
+ isAudioEnabled: this.localMedia.isMicrophoneEnabled(),
5736
+ isVideoEnabled: this.localMedia.isCameraEnabled(),
5737
+ },
5738
+ deviceCapabilities: { canScreenshare: true },
5739
+ displayName: this.displayName,
5740
+ isCoLocated: false,
5741
+ isDevicePermissionDenied: false,
5742
+ kickFromOtherRooms: false,
5743
+ organizationId: this.organizationId,
5744
+ roomKey: this.roomKey,
5745
+ roomName: this.roomName,
5746
+ selfId: "",
5747
+ userAgent: `browser-sdk:${sdkVersion }`,
5748
+ });
5192
5749
  }
5193
- /**
5194
- * Public API
5195
- */
5196
5750
  join() {
5197
5751
  return __awaiter(this, void 0, void 0, function* () {
5198
- if (["connected", "connecting"].includes(this.roomConnectionState)) {
5199
- console.warn(`Trying to join room state is ${this.roomConnectionState}`);
5752
+ if (["connected", "connecting"].includes(this.roomConnectionStatus)) {
5753
+ console.warn(`Trying to join when room state is already ${this.roomConnectionStatus}`);
5200
5754
  return;
5201
5755
  }
5202
5756
  this.logger.log("Joining room");
5203
- this.roomConnectionState = "connecting";
5204
- if (!this.localStream && this.localMediaConstraints) {
5205
- const localStream = yield navigator.mediaDevices.getUserMedia(this.localMediaConstraints);
5206
- this.localStream = localStream;
5207
- }
5757
+ this.signalSocket.connect();
5758
+ this.roomConnectionStatus = "connecting";
5759
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5760
+ detail: {
5761
+ roomConnectionStatus: this.roomConnectionStatus,
5762
+ },
5763
+ }));
5208
5764
  const organization = yield this.organizationServiceCache.fetchOrganization();
5209
5765
  if (!organization) {
5210
5766
  throw new Error("Invalid room url");
5211
5767
  }
5212
- // TODO: Get room permissions
5213
- // TODO: Get room features
5214
- const webrtcProvider = {
5215
- getMediaConstraints: () => {
5216
- var _a, _b;
5217
- return ({
5218
- audio: !!((_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().find((t) => t.enabled)),
5219
- video: !!((_b = this.localStream) === null || _b === void 0 ? void 0 : _b.getVideoTracks().find((t) => t.enabled)),
5220
- });
5221
- },
5222
- deferrable(clientId) {
5223
- return !clientId;
5224
- },
5225
- };
5226
- this.rtcManagerDispatcher = new RtcManagerDispatcher({
5227
- emitter: {
5228
- emit: this._handleRtcEvent.bind(this),
5229
- },
5230
- serverSocket: this.signalSocket,
5231
- webrtcProvider,
5232
- features: {
5233
- lowDataModeEnabled: false,
5234
- sfuServerOverrideHost: undefined,
5235
- turnServerOverrideHost: undefined,
5236
- useOnlyTURN: undefined,
5237
- vp9On: false,
5238
- h264On: false,
5239
- simulcastScreenshareOn: false,
5240
- },
5241
- });
5768
+ this.organizationId = organization.organizationId;
5769
+ if (this._ownsLocalMedia) {
5770
+ yield this.localMedia.start();
5771
+ }
5242
5772
  // Identify device on signal connection
5243
- const deviceCredentials = yield this.credentialsService.getCredentials();
5244
- // TODO: Handle connection and failed connection properly
5245
- setTimeout(() => {
5246
- this.logger.log("Connected to signal socket");
5247
- this.signalSocket.emit("identify_device", { deviceCredentials });
5248
- }, 2000);
5773
+ this._deviceCredentials = yield this.credentialsService.getCredentials();
5774
+ this.logger.log("Connected to signal socket");
5775
+ this.signalSocket.emit("identify_device", { deviceCredentials: this._deviceCredentials });
5249
5776
  this.signalSocket.once("device_identified", () => {
5250
- var _a, _b;
5251
- this.signalSocket.emit("join_room", {
5252
- avatarUrl: null,
5253
- config: {
5254
- isAudioEnabled: !!((_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().find((t) => t.enabled)),
5255
- isVideoEnabled: !!((_b = this.localStream) === null || _b === void 0 ? void 0 : _b.getVideoTracks().find((t) => t.enabled)),
5256
- },
5257
- deviceCapabilities: { canScreenshare: true },
5258
- displayName: this.displayName,
5259
- isCoLocated: false,
5260
- isDevicePermissionDenied: false,
5261
- kickFromOtherRooms: false,
5262
- organizationId: organization.organizationId,
5263
- roomKey: null,
5264
- roomName: this.roomUrl.pathname,
5265
- selfId: "",
5266
- });
5267
- });
5268
- this.signalSocket.once("room_joined", (res) => {
5269
- const { selfId, room: { clients }, } = res;
5270
- const localClient = clients.find((c) => c.id === selfId);
5271
- if (!localClient)
5272
- throw new Error("Missing local client");
5273
- this.localParticipant = new LocalParticipant(Object.assign(Object.assign({}, localClient), { stream: this.localStream }));
5274
- this.remoteParticipants = clients
5275
- .filter((c) => c.id !== selfId)
5276
- .map((c) => new RemoteParticipant(Object.assign(Object.assign({}, c), { newJoiner: false })));
5277
- // Accept remote streams if RTC manager has been initialized
5278
- if (this.rtcManager) {
5279
- this._handleAcceptStreams(this.remoteParticipants);
5280
- }
5281
- this.roomConnectionState = "connected";
5282
- this.dispatchEvent(new CustomEvent("room_joined", {
5283
- detail: {
5284
- localParticipant: this.localParticipant,
5285
- remoteParticipants: this.remoteParticipants,
5286
- },
5287
- }));
5777
+ this._joinRoom();
5288
5778
  });
5289
5779
  });
5290
5780
  }
5291
- leave() {
5292
- return new Promise((resolve) => {
5293
- if (!this.signalSocket) {
5294
- return resolve();
5295
- }
5296
- this.signalSocket.emit("leave_room");
5297
- const leaveTimeout = setTimeout(() => {
5298
- resolve();
5299
- }, 200);
5300
- this.signalSocket.once("room_left", () => {
5301
- clearTimeout(leaveTimeout);
5302
- this.signalSocket.disconnect();
5303
- resolve();
5304
- });
5781
+ knock() {
5782
+ this.roomConnectionStatus = "knocking";
5783
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5784
+ detail: {
5785
+ roomConnectionStatus: this.roomConnectionStatus,
5786
+ },
5787
+ }));
5788
+ this.signalSocket.emit("knock_room", {
5789
+ displayName: this.displayName,
5790
+ imageUrl: null,
5791
+ kickFromOtherRooms: true,
5792
+ liveVideo: false,
5793
+ organizationId: this.organizationId,
5794
+ roomKey: this._roomKey,
5795
+ roomName: this.roomName,
5305
5796
  });
5306
5797
  }
5307
- toggleCamera(enabled) {
5308
- var _a;
5309
- const localVideoTrack = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks()[0];
5310
- if (!localVideoTrack) {
5311
- this.logger.log("Tried toggling non-existing video track");
5312
- return;
5798
+ leave() {
5799
+ this.roomConnectionStatus = "disconnecting";
5800
+ if (this._ownsLocalMedia) {
5801
+ this.localMedia.stop();
5313
5802
  }
5314
- // TODO: Do stopOrResumeVideo
5315
- const newValue = enabled !== null && enabled !== void 0 ? enabled : !localVideoTrack.enabled;
5316
- localVideoTrack.enabled = newValue;
5317
- this.signalSocket.emit("enable_video", { enabled: newValue });
5318
- }
5319
- toggleMicrophone(enabled) {
5320
- var _a;
5321
- const localAudioTrack = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];
5322
- if (!localAudioTrack) {
5323
- this.logger.log("Tried toggling non-existing audio track");
5803
+ if (this.rtcManager) {
5804
+ this.localMedia.removeRtcManager(this.rtcManager);
5805
+ this.rtcManager.disconnectAll();
5806
+ this.rtcManager = undefined;
5807
+ }
5808
+ if (!this.signalSocket) {
5324
5809
  return;
5325
5810
  }
5326
- // TODO: Do stopOrResumeAudio
5327
- const newValue = enabled !== null && enabled !== void 0 ? enabled : !localAudioTrack.enabled;
5328
- localAudioTrack.enabled = newValue;
5329
- this.signalSocket.emit("enable_audio", { enabled: newValue });
5811
+ this.signalSocket.emit("leave_room");
5812
+ this.signalSocket.disconnect();
5813
+ this.roomConnectionStatus = "disconnected";
5814
+ }
5815
+ sendChatMessage(text) {
5816
+ this.signalSocket.emit("chat_message", {
5817
+ text,
5818
+ });
5330
5819
  }
5331
5820
  setDisplayName(displayName) {
5332
5821
  this.signalSocket.emit("send_client_metadata", {
@@ -5336,8 +5825,93 @@ class RoomConnection extends TypedEventTarget {
5336
5825
  },
5337
5826
  });
5338
5827
  }
5828
+ acceptWaitingParticipant(participantId) {
5829
+ this.signalSocket.emit("handle_knock", {
5830
+ action: "accept",
5831
+ clientId: participantId,
5832
+ response: {},
5833
+ });
5834
+ }
5835
+ rejectWaitingParticipant(participantId) {
5836
+ this.signalSocket.emit("handle_knock", {
5837
+ action: "reject",
5838
+ clientId: participantId,
5839
+ response: {},
5840
+ });
5841
+ }
5842
+ updateStreamResolution({ streamId, width, height }) {
5843
+ var _a, _b;
5844
+ if (!streamId || !this.rtcManager) {
5845
+ return;
5846
+ }
5847
+ // no need to report resolution for local participant
5848
+ if (((_b = (_a = this.localParticipant) === null || _a === void 0 ? void 0 : _a.stream) === null || _b === void 0 ? void 0 : _b.id) === streamId) {
5849
+ return;
5850
+ }
5851
+ const old = reportedStreamResolutions.get(streamId);
5852
+ if (!old || old.width !== width || old.height !== height) {
5853
+ this.rtcManager.updateStreamResolution(streamId, null, { width: width || 1, height: height || 1 });
5854
+ }
5855
+ reportedStreamResolutions.set(streamId, { width, height });
5856
+ }
5857
+ startScreenshare() {
5858
+ var _a;
5859
+ return __awaiter(this, void 0, void 0, function* () {
5860
+ const screenshareStream = this.localMedia.screenshareStream || (yield this.localMedia.startScreenshare());
5861
+ (_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.addNewStream(screenshareStream.id, screenshareStream, false, true);
5862
+ this.screenshares = [
5863
+ ...this.screenshares,
5864
+ {
5865
+ participantId: this.selfId || "",
5866
+ id: screenshareStream.id,
5867
+ hasAudioTrack: false,
5868
+ stream: screenshareStream,
5869
+ isLocal: true,
5870
+ },
5871
+ ];
5872
+ this.dispatchEvent(new CustomEvent("screenshare_started", {
5873
+ detail: {
5874
+ participantId: this.selfId || "",
5875
+ id: screenshareStream.id,
5876
+ hasAudioTrack: false,
5877
+ stream: screenshareStream,
5878
+ isLocal: true,
5879
+ },
5880
+ }));
5881
+ });
5882
+ }
5883
+ stopScreenshare() {
5884
+ var _a;
5885
+ if (this.localMedia.screenshareStream) {
5886
+ const { id } = this.localMedia.screenshareStream;
5887
+ (_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.removeStream(id, this.localMedia.screenshareStream, null);
5888
+ this.screenshares = this.screenshares.filter((s) => s.id !== id);
5889
+ this.dispatchEvent(new CustomEvent("screenshare_stopped", { detail: { participantId: this.selfId, id } }));
5890
+ this.localMedia.stopScreenshare();
5891
+ }
5892
+ }
5339
5893
  }
5340
5894
 
5895
+ const initialState = {
5896
+ chatMessages: [],
5897
+ cloudRecording: {
5898
+ status: "",
5899
+ startedAt: null,
5900
+ },
5901
+ isJoining: false,
5902
+ isStartingScreenshare: false,
5903
+ joinError: null,
5904
+ startScreenshareError: null,
5905
+ mostRecentChatMessage: null,
5906
+ remoteParticipants: [],
5907
+ roomConnectionStatus: "",
5908
+ screenshares: [],
5909
+ streaming: {
5910
+ status: "",
5911
+ startedAt: null,
5912
+ },
5913
+ waitingParticipants: [],
5914
+ };
5341
5915
  function updateParticipant(remoteParticipants, participantId, updates) {
5342
5916
  const existingParticipant = remoteParticipants.find((p) => p.id === participantId);
5343
5917
  if (!existingParticipant) {
@@ -5350,10 +5924,31 @@ function updateParticipant(remoteParticipants, participantId, updates) {
5350
5924
  ...remoteParticipants.slice(index + 1),
5351
5925
  ];
5352
5926
  }
5927
+ function addScreenshare(screenshares, screenshare) {
5928
+ const existingScreenshare = screenshares.find((ss) => ss.id === screenshare.id);
5929
+ if (existingScreenshare) {
5930
+ return screenshares;
5931
+ }
5932
+ return [...screenshares, screenshare];
5933
+ }
5353
5934
  function reducer(state, action) {
5354
5935
  switch (action.type) {
5936
+ case "CHAT_MESSAGE":
5937
+ return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, action.payload], mostRecentChatMessage: action.payload });
5938
+ case "CLOUD_RECORDING_STARTED":
5939
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
5940
+ status: action.payload.status,
5941
+ startedAt: action.payload.startedAt,
5942
+ } });
5943
+ case "CLOUD_RECORDING_STOPPED":
5944
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
5945
+ status: "",
5946
+ startedAt: null,
5947
+ } });
5355
5948
  case "ROOM_JOINED":
5356
- return Object.assign(Object.assign({}, state), { localParticipant: action.payload.localParticipant, remoteParticipants: action.payload.remoteParticipants, roomConnectionStatus: "connected" });
5949
+ return Object.assign(Object.assign({}, state), { localParticipant: action.payload.localParticipant, remoteParticipants: action.payload.remoteParticipants, waitingParticipants: action.payload.waitingParticipants, roomConnectionStatus: "connected" });
5950
+ case "ROOM_CONNECTION_STATUS_CHANGED":
5951
+ return Object.assign(Object.assign({}, state), { roomConnectionStatus: action.payload.roomConnectionStatus });
5357
5952
  case "PARTICIPANT_AUDIO_ENABLED":
5358
5953
  return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
5359
5954
  isAudioEnabled: action.payload.isAudioEnabled,
@@ -5378,74 +5973,199 @@ function reducer(state, action) {
5378
5973
  if (!state.localParticipant)
5379
5974
  return state;
5380
5975
  return Object.assign(Object.assign({}, state), { localParticipant: Object.assign(Object.assign({}, state.localParticipant), { displayName: action.payload.displayName }) });
5976
+ case "SCREENSHARE_STARTED":
5977
+ return Object.assign(Object.assign({}, state), { screenshares: addScreenshare(state.screenshares, {
5978
+ participantId: action.payload.participantId,
5979
+ id: action.payload.id,
5980
+ hasAudioTrack: action.payload.hasAudioTrack,
5981
+ stream: action.payload.stream,
5982
+ isLocal: action.payload.isLocal,
5983
+ }) });
5984
+ case "SCREENSHARE_STOPPED":
5985
+ return Object.assign(Object.assign({}, state), { screenshares: state.screenshares.filter((ss) => ss.id !== action.payload.id) });
5986
+ case "LOCAL_SCREENSHARE_START_ERROR":
5987
+ return Object.assign(Object.assign({}, state), { isStartingScreenshare: false, startScreenshareError: action.payload });
5988
+ case "LOCAL_SCREENSHARE_STARTING":
5989
+ return Object.assign(Object.assign({}, state), { isStartingScreenshare: true });
5990
+ case "LOCAL_SCREENSHARE_STOPPED":
5991
+ return Object.assign(Object.assign({}, state), { screenshares: state.screenshares.filter((ss) => !ss.isLocal) });
5992
+ case "STREAMING_STARTED":
5993
+ return Object.assign(Object.assign({}, state), { streaming: {
5994
+ status: action.payload.status,
5995
+ startedAt: action.payload.startedAt,
5996
+ } });
5997
+ case "STREAMING_STOPPED":
5998
+ return Object.assign(Object.assign({}, state), { streaming: {
5999
+ status: "",
6000
+ startedAt: null,
6001
+ } });
6002
+ case "WAITING_PARTICIPANT_JOINED":
6003
+ return Object.assign(Object.assign({}, state), { waitingParticipants: [
6004
+ ...state.waitingParticipants,
6005
+ { id: action.payload.participantId, displayName: action.payload.displayName },
6006
+ ] });
6007
+ case "WAITING_PARTICIPANT_LEFT":
6008
+ return Object.assign(Object.assign({}, state), { waitingParticipants: state.waitingParticipants.filter((wp) => wp.id !== action.payload.participantId) });
5381
6009
  default:
5382
6010
  throw state;
5383
6011
  }
5384
- }
5385
-
6012
+ }
5386
6013
  function useRoomConnection(roomUrl, roomConnectionOptions) {
5387
- const [roomConnection, setRoomConnection] = useState(null);
5388
- const [state, dispatch] = useReducer(reducer, { remoteParticipants: [] });
5389
- useEffect(() => {
5390
- setRoomConnection(new RoomConnection(roomUrl, roomConnectionOptions));
5391
- }, [roomUrl]);
5392
- useEffect(() => {
5393
- if (!roomConnection) {
5394
- return;
5395
- }
5396
- roomConnection.addEventListener("participant_audio_enabled", (e) => {
6014
+ const [roomConnection] = useState(() => {
6015
+ var _a;
6016
+ return new RoomConnection(roomUrl, Object.assign(Object.assign({}, roomConnectionOptions), { localMedia: ((_a = roomConnectionOptions === null || roomConnectionOptions === void 0 ? void 0 : roomConnectionOptions.localMedia) === null || _a === void 0 ? void 0 : _a._ref) || undefined }));
6017
+ });
6018
+ const [state, dispatch] = useReducer(reducer, initialState);
6019
+ function createEventListener(eventName, listener, options) {
6020
+ return {
6021
+ eventName,
6022
+ listener,
6023
+ options,
6024
+ };
6025
+ }
6026
+ const eventListeners = React.useMemo(() => [
6027
+ createEventListener("chat_message", (e) => {
6028
+ dispatch({ type: "CHAT_MESSAGE", payload: e.detail });
6029
+ }),
6030
+ createEventListener("cloud_recording_started", (e) => {
6031
+ const { status, startedAt } = e.detail;
6032
+ dispatch({ type: "CLOUD_RECORDING_STARTED", payload: { status, startedAt } });
6033
+ }),
6034
+ createEventListener("cloud_recording_stopped", () => {
6035
+ dispatch({ type: "CLOUD_RECORDING_STOPPED" });
6036
+ }),
6037
+ createEventListener("participant_audio_enabled", (e) => {
5397
6038
  const { participantId, isAudioEnabled } = e.detail;
5398
6039
  dispatch({ type: "PARTICIPANT_AUDIO_ENABLED", payload: { participantId, isAudioEnabled } });
5399
- });
5400
- roomConnection.addEventListener("participant_joined", (e) => {
6040
+ }),
6041
+ createEventListener("participant_joined", (e) => {
5401
6042
  const { remoteParticipant } = e.detail;
5402
6043
  dispatch({ type: "PARTICIPANT_JOINED", payload: { paritipant: remoteParticipant } });
5403
- });
5404
- roomConnection.addEventListener("participant_left", (e) => {
6044
+ }),
6045
+ createEventListener("participant_left", (e) => {
5405
6046
  const { participantId } = e.detail;
5406
6047
  dispatch({ type: "PARTICIPANT_LEFT", payload: { participantId } });
5407
- });
5408
- roomConnection.addEventListener("participant_stream_added", (e) => {
6048
+ }),
6049
+ createEventListener("participant_metadata_changed", (e) => {
6050
+ const { participantId, displayName } = e.detail;
6051
+ dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
6052
+ }),
6053
+ createEventListener("participant_stream_added", (e) => {
5409
6054
  const { participantId, stream } = e.detail;
5410
6055
  dispatch({ type: "PARTICIPANT_STREAM_ADDED", payload: { participantId, stream } });
5411
- });
5412
- roomConnection.addEventListener("room_joined", (e) => {
5413
- const { localParticipant, remoteParticipants } = e.detail;
5414
- dispatch({ type: "ROOM_JOINED", payload: { localParticipant, remoteParticipants } });
5415
- });
5416
- roomConnection.addEventListener("participant_video_enabled", (e) => {
6056
+ }),
6057
+ createEventListener("participant_video_enabled", (e) => {
5417
6058
  const { participantId, isVideoEnabled } = e.detail;
5418
6059
  dispatch({ type: "PARTICIPANT_VIDEO_ENABLED", payload: { participantId, isVideoEnabled } });
5419
- });
5420
- roomConnection.addEventListener("participant_metadata_changed", (e) => {
6060
+ }),
6061
+ createEventListener("room_connection_status_changed", (e) => {
6062
+ const { roomConnectionStatus } = e.detail;
6063
+ dispatch({
6064
+ type: "ROOM_CONNECTION_STATUS_CHANGED",
6065
+ payload: { roomConnectionStatus },
6066
+ });
6067
+ }),
6068
+ createEventListener("room_joined", (e) => {
6069
+ const { localParticipant, remoteParticipants, waitingParticipants } = e.detail;
6070
+ dispatch({
6071
+ type: "ROOM_JOINED",
6072
+ payload: { localParticipant, remoteParticipants, waitingParticipants },
6073
+ });
6074
+ }),
6075
+ createEventListener("screenshare_started", (e) => {
6076
+ const { participantId, stream, id, hasAudioTrack, isLocal } = e.detail;
6077
+ dispatch({
6078
+ type: "SCREENSHARE_STARTED",
6079
+ payload: { participantId, stream, id, hasAudioTrack, isLocal },
6080
+ });
6081
+ }),
6082
+ createEventListener("screenshare_stopped", (e) => {
6083
+ const { participantId, id } = e.detail;
6084
+ dispatch({
6085
+ type: "SCREENSHARE_STOPPED",
6086
+ payload: { participantId, id },
6087
+ });
6088
+ }),
6089
+ createEventListener("streaming_started", (e) => {
6090
+ const { status, startedAt } = e.detail;
6091
+ dispatch({ type: "STREAMING_STARTED", payload: { status, startedAt } });
6092
+ }),
6093
+ createEventListener("streaming_stopped", () => {
6094
+ dispatch({ type: "STREAMING_STOPPED" });
6095
+ }),
6096
+ createEventListener("waiting_participant_joined", (e) => {
5421
6097
  const { participantId, displayName } = e.detail;
5422
- dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
6098
+ dispatch({
6099
+ type: "WAITING_PARTICIPANT_JOINED",
6100
+ payload: { participantId, displayName },
6101
+ });
6102
+ }),
6103
+ ], []);
6104
+ useEffect(() => {
6105
+ eventListeners.forEach(({ eventName, listener }) => {
6106
+ roomConnection.addEventListener(eventName, listener);
5423
6107
  });
5424
6108
  roomConnection.join();
5425
6109
  return () => {
6110
+ eventListeners.forEach(({ eventName, listener }) => {
6111
+ roomConnection.removeEventListener(eventName, listener);
6112
+ });
5426
6113
  roomConnection.leave();
5427
6114
  };
5428
- }, [roomConnection]);
5429
- return [
6115
+ }, []);
6116
+ return {
5430
6117
  state,
5431
- {
5432
- toggleCamera: (enabled) => {
5433
- roomConnection === null || roomConnection === void 0 ? void 0 : roomConnection.toggleCamera(enabled);
6118
+ actions: {
6119
+ knock: () => {
6120
+ roomConnection.knock();
5434
6121
  },
5435
- toggleMicrophone: (enabled) => {
5436
- roomConnection === null || roomConnection === void 0 ? void 0 : roomConnection.toggleMicrophone(enabled);
6122
+ sendChatMessage: (text) => {
6123
+ roomConnection.sendChatMessage(text);
5437
6124
  },
5438
6125
  setDisplayName: (displayName) => {
5439
- roomConnection === null || roomConnection === void 0 ? void 0 : roomConnection.setDisplayName(displayName);
6126
+ roomConnection.setDisplayName(displayName);
5440
6127
  dispatch({ type: "LOCAL_CLIENT_DISPLAY_NAME_CHANGED", payload: { displayName } });
5441
6128
  },
6129
+ toggleCamera: (enabled) => {
6130
+ roomConnection.localMedia.toggleCameraEnabled(enabled);
6131
+ },
6132
+ toggleMicrophone: (enabled) => {
6133
+ roomConnection.localMedia.toggleMichrophoneEnabled(enabled);
6134
+ },
6135
+ acceptWaitingParticipant: (participantId) => {
6136
+ roomConnection.acceptWaitingParticipant(participantId);
6137
+ },
6138
+ rejectWaitingParticipant: (participantId) => {
6139
+ roomConnection.rejectWaitingParticipant(participantId);
6140
+ },
6141
+ startScreenshare: () => {
6142
+ dispatch({ type: "LOCAL_SCREENSHARE_STARTING" });
6143
+ try {
6144
+ roomConnection.startScreenshare();
6145
+ }
6146
+ catch (error) {
6147
+ dispatch({ type: "LOCAL_SCREENSHARE_START_ERROR", payload: error });
6148
+ }
6149
+ },
6150
+ stopScreenshare: () => {
6151
+ roomConnection.stopScreenshare();
6152
+ },
5442
6153
  },
5443
- {
5444
- VideoView: VideoElement,
6154
+ components: {
6155
+ VideoView: (props) => React.createElement(VideoView, Object.assign({}, props, {
6156
+ onResize: ({ stream, width, height, }) => {
6157
+ roomConnection.updateStreamResolution({
6158
+ streamId: stream.id,
6159
+ width,
6160
+ height,
6161
+ });
6162
+ },
6163
+ })),
5445
6164
  },
5446
- ];
6165
+ _ref: roomConnection,
6166
+ };
5447
6167
  }
5448
6168
 
5449
- const sdkVersion = "2.0.0-alpha2";
6169
+ const sdkVersion = "2.0.0-alpha21";
5450
6170
 
5451
- export { sdkVersion, useRoomConnection };
6171
+ export { VideoView, sdkVersion, useLocalMedia, useRoomConnection };