@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.
@@ -3,12 +3,12 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var heresy = require('heresy');
6
- var React = require('react');
7
6
  var tslib = require('tslib');
7
+ var React = require('react');
8
+ var assert = require('assert');
8
9
  var adapter = require('webrtc-adapter');
9
10
  var io = require('socket.io-client');
10
11
  var SDPUtils = require('sdp');
11
- var assert = require('assert');
12
12
  var mediasoupClient = require('mediasoup-client');
13
13
  var EventEmitter = require('events');
14
14
  var uuid = require('uuid');
@@ -18,10 +18,10 @@ var axios = require('axios');
18
18
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
19
19
 
20
20
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
21
+ var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
21
22
  var adapter__default = /*#__PURE__*/_interopDefaultLegacy(adapter);
22
23
  var io__default = /*#__PURE__*/_interopDefaultLegacy(io);
23
24
  var SDPUtils__default = /*#__PURE__*/_interopDefaultLegacy(SDPUtils);
24
- var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
25
25
  var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
26
26
  var nodeBtoa__default = /*#__PURE__*/_interopDefaultLegacy(nodeBtoa);
27
27
  var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
@@ -94,8 +94,7 @@ heresy.define("WherebyEmbed", {
94
94
  // Commands
95
95
  _postCommand(command, args = []) {
96
96
  if (this.iframe.current) {
97
- const url = new URL(this.room, `https://${this.subdomain}.whereby.com`);
98
- this.iframe.current.contentWindow.postMessage({ command, args }, url.origin);
97
+ this.iframe.current.contentWindow.postMessage({ command, args }, this.url.origin);
99
98
  }
100
99
  },
101
100
  startRecording() {
@@ -114,8 +113,7 @@ heresy.define("WherebyEmbed", {
114
113
  this._postCommand("toggle_screenshare", [enabled]);
115
114
  },
116
115
  onmessage({ origin, data }) {
117
- const url = new URL(this.room, `https://${this.subdomain}.whereby.com`);
118
- if (origin !== url.origin)
116
+ if (origin !== this.url.origin)
119
117
  return;
120
118
  const { type, payload: detail } = data;
121
119
  this.dispatchEvent(new CustomEvent(type, { detail }));
@@ -125,36 +123,354 @@ heresy.define("WherebyEmbed", {
125
123
  if (!room)
126
124
  return this.html `Whereby: Missing room attribute.`;
127
125
  // Get subdomain from room URL, or use it specified
128
- const m = /https:\/\/([^.]+)\.whereby.com\/.+/.exec(room);
126
+ const m = /https:\/\/([^.]+)(\.whereby.com|-ip-\d+-\d+-\d+-\d+.hereby.dev:4443)\/.+/.exec(room);
129
127
  const subdomain = (m && m[1]) || this.subdomain;
130
128
  if (!subdomain)
131
129
  return this.html `Whereby: Missing subdomain attr.`;
132
- const url = new URL(room, `https://${subdomain}.whereby.com`);
133
- 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(
130
+ if (!m) {
131
+ return this.html `could not parse URL.`;
132
+ }
133
+ const baseURL = m[2] || `.whereby.com`;
134
+ this.url = new URL(room, `https://${subdomain}${baseURL}`);
135
+ const roomUrl = new URL(room);
136
+ if (roomUrl.searchParams.get("roomKey")) {
137
+ this.url.searchParams.append("roomKey", roomUrl.searchParams.get("roomKey"));
138
+ }
139
+ 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(
134
140
  // add to URL if set in any way
135
141
  (o, v) => (this[v.toLowerCase()] != null ? Object.assign(Object.assign({}, o), { [v]: this[v.toLowerCase()] }) : o), {}))).forEach(([k, v]) => {
136
- if (!url.searchParams.has(k) && typeof v === "string") {
137
- url.searchParams.set(k, v);
142
+ if (!this.url.searchParams.has(k) && typeof v === "string") {
143
+ this.url.searchParams.set(k, v);
138
144
  }
139
145
  });
140
146
  return this.html `
141
147
  <iframe
142
148
  ref=${this.iframe}
143
- src=${url}
149
+ src=${this.url}
144
150
  allow="autoplay; camera; microphone; fullscreen; speaker; display-capture" />
145
151
  `;
146
152
  },
147
153
  });
148
154
 
149
- var VideoElement = ({ stream, style }) => {
150
- const videoEl = React.useCallback((node) => {
151
- if (node !== null && node.srcObject !== stream) {
152
- node.srcObject = stream;
155
+ /**
156
+ * Debounce function.
157
+ *
158
+ * @param {Function} fn - Function to debounce.
159
+ * @param {Object} [options] - Options.
160
+ * @param {number} [options.delay=500] - Delay in milliseconds.
161
+ * @param {boolean} [options.edges=false] - Whether to call the function on the
162
+ * leading and trailing edges of the wait timeout.
163
+ * @returns {Function} Debounced function.
164
+ */
165
+ function debounce(fn, { delay = 500, edges } = {}) {
166
+ assert__default["default"].ok(typeof fn === "function", "fn<function> is required");
167
+ let timeout;
168
+ let nCalls = 0;
169
+ return (...args) => {
170
+ nCalls += 1;
171
+ if (edges && nCalls === 1) {
172
+ fn(...args);
153
173
  }
154
- }, []);
155
- return React__default["default"].createElement("video", { ref: videoEl, autoPlay: true, playsInline: true, style: style });
174
+ clearTimeout(timeout);
175
+ timeout = setTimeout(() => {
176
+ if (!edges || nCalls > 1) {
177
+ fn(...args);
178
+ }
179
+ timeout = undefined;
180
+ nCalls = 0;
181
+ }, delay);
182
+ };
183
+ }
184
+
185
+ var VideoView = (_a) => {
186
+ var { muted, stream, onResize } = _a, rest = tslib.__rest(_a, ["muted", "stream", "onResize"]);
187
+ const videoEl = React.useRef(null);
188
+ React.useEffect(() => {
189
+ if (!videoEl.current || !onResize) {
190
+ return;
191
+ }
192
+ const resizeObserver = new ResizeObserver(debounce(() => {
193
+ if (videoEl.current && (stream === null || stream === void 0 ? void 0 : stream.id)) {
194
+ onResize({
195
+ width: videoEl.current.clientWidth,
196
+ height: videoEl.current.clientHeight,
197
+ stream,
198
+ });
199
+ }
200
+ }, { delay: 1000, edges: true }));
201
+ resizeObserver.observe(videoEl.current);
202
+ return () => {
203
+ resizeObserver.disconnect();
204
+ };
205
+ }, [stream]);
206
+ React.useEffect(() => {
207
+ if (!videoEl.current) {
208
+ return;
209
+ }
210
+ if (videoEl.current.srcObject !== stream) {
211
+ videoEl.current.srcObject = stream;
212
+ }
213
+ // Handle muting programatically, not as video attribute
214
+ // https://stackoverflow.com/questions/14111917/html5-video-muted-but-still-playing
215
+ if (videoEl.current.muted !== muted) {
216
+ videoEl.current.muted = Boolean(muted);
217
+ }
218
+ }, [muted, stream, videoEl]);
219
+ return React__default["default"].createElement("video", Object.assign({ ref: videoEl, autoPlay: true, playsInline: true }, rest));
156
220
  };
157
221
 
222
+ const TypedLocalMediaEventTarget = EventTarget;
223
+ class LocalMedia extends TypedLocalMediaEventTarget {
224
+ constructor(constraints) {
225
+ super();
226
+ this._constraints = constraints;
227
+ this.stream = new MediaStream();
228
+ this._rtcManagers = [];
229
+ this.screenshareStream = undefined;
230
+ navigator.mediaDevices.addEventListener("devicechange", this._updateDeviceList.bind(this));
231
+ }
232
+ addRtcManager(rtcManager) {
233
+ this._rtcManagers.push(rtcManager);
234
+ }
235
+ removeRtcManager(rtcManager) {
236
+ this._rtcManagers = this._rtcManagers.filter((r) => r !== rtcManager);
237
+ }
238
+ getCameraDeviceId() {
239
+ var _a;
240
+ return (_a = this.stream.getVideoTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings().deviceId;
241
+ }
242
+ getMicrophoneDeviceId() {
243
+ var _a;
244
+ return (_a = this.stream.getAudioTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings().deviceId;
245
+ }
246
+ isCameraEnabled() {
247
+ var _a;
248
+ return !!((_a = this.stream.getVideoTracks()[0]) === null || _a === void 0 ? void 0 : _a.enabled);
249
+ }
250
+ isMicrophoneEnabled() {
251
+ var _a;
252
+ return !!((_a = this.stream.getAudioTracks()[0]) === null || _a === void 0 ? void 0 : _a.enabled);
253
+ }
254
+ toggleCameraEnabled(enabled) {
255
+ const videoTrack = this.stream.getVideoTracks()[0];
256
+ if (!videoTrack) {
257
+ return;
258
+ }
259
+ const newValue = enabled !== null && enabled !== void 0 ? enabled : !videoTrack.enabled;
260
+ videoTrack.enabled = newValue;
261
+ this.dispatchEvent(new CustomEvent("camera_enabled", { detail: { enabled: newValue } }));
262
+ }
263
+ toggleMichrophoneEnabled(enabled) {
264
+ const audioTrack = this.stream.getAudioTracks()[0];
265
+ if (!audioTrack) {
266
+ return;
267
+ }
268
+ const newValue = enabled !== null && enabled !== void 0 ? enabled : !audioTrack.enabled;
269
+ audioTrack.enabled = newValue;
270
+ this.dispatchEvent(new CustomEvent("microphone_enabled", { detail: { enabled: newValue } }));
271
+ }
272
+ startScreenshare() {
273
+ return tslib.__awaiter(this, void 0, void 0, function* () {
274
+ if (this.screenshareStream) {
275
+ return this.screenshareStream;
276
+ }
277
+ const screenshareStream = yield navigator.mediaDevices.getDisplayMedia();
278
+ this.screenshareStream = screenshareStream;
279
+ return this.screenshareStream;
280
+ });
281
+ }
282
+ stopScreenshare() {
283
+ var _a;
284
+ (_a = this.screenshareStream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((track) => track.stop());
285
+ this.screenshareStream = undefined;
286
+ }
287
+ setCameraDevice(deviceId) {
288
+ return tslib.__awaiter(this, void 0, void 0, function* () {
289
+ const newStream = yield navigator.mediaDevices.getUserMedia({ video: { deviceId } });
290
+ const newVideoTrack = newStream.getVideoTracks()[0];
291
+ if (newVideoTrack) {
292
+ const oldVideoTrack = this.stream.getVideoTracks()[0];
293
+ newVideoTrack.enabled = oldVideoTrack.enabled;
294
+ oldVideoTrack === null || oldVideoTrack === void 0 ? void 0 : oldVideoTrack.stop();
295
+ this._rtcManagers.forEach((rtcManager) => {
296
+ rtcManager.replaceTrack(oldVideoTrack, newVideoTrack);
297
+ });
298
+ this.stream.removeTrack(oldVideoTrack);
299
+ this.stream.addTrack(newVideoTrack);
300
+ }
301
+ this.dispatchEvent(new CustomEvent("stream_updated", {
302
+ detail: { stream: this.stream },
303
+ }));
304
+ });
305
+ }
306
+ setMicrophoneDevice(deviceId) {
307
+ return tslib.__awaiter(this, void 0, void 0, function* () {
308
+ const newStream = yield navigator.mediaDevices.getUserMedia({ audio: { deviceId } });
309
+ const newAudioTrack = newStream.getAudioTracks()[0];
310
+ const oldAudioTrack = this.stream.getAudioTracks()[0];
311
+ if (oldAudioTrack) {
312
+ newAudioTrack.enabled = oldAudioTrack.enabled;
313
+ oldAudioTrack.stop();
314
+ this.stream.removeTrack(oldAudioTrack);
315
+ }
316
+ this._rtcManagers.forEach((rtcManager) => {
317
+ rtcManager.replaceTrack(oldAudioTrack, newAudioTrack);
318
+ });
319
+ this.stream.addTrack(newAudioTrack);
320
+ this.dispatchEvent(new CustomEvent("stream_updated", {
321
+ detail: { stream: this.stream },
322
+ }));
323
+ });
324
+ }
325
+ _updateDeviceList() {
326
+ return tslib.__awaiter(this, void 0, void 0, function* () {
327
+ try {
328
+ const devices = yield navigator.mediaDevices.enumerateDevices();
329
+ this.dispatchEvent(new CustomEvent("device_list_updated", {
330
+ detail: {
331
+ cameraDevices: devices.filter((d) => d.kind === "videoinput"),
332
+ microphoneDevices: devices.filter((d) => d.kind === "audioinput"),
333
+ speakerDevices: devices.filter((d) => d.kind === "audiooutput"),
334
+ },
335
+ }));
336
+ }
337
+ catch (error) {
338
+ this.dispatchEvent(new CustomEvent("device_list_update_error", {
339
+ detail: {
340
+ error,
341
+ },
342
+ }));
343
+ throw error;
344
+ }
345
+ });
346
+ }
347
+ start() {
348
+ return tslib.__awaiter(this, void 0, void 0, function* () {
349
+ const newStream = yield navigator.mediaDevices.getUserMedia(this._constraints);
350
+ newStream.getTracks().forEach((t) => this.stream.addTrack(t));
351
+ this._updateDeviceList();
352
+ this.dispatchEvent(new CustomEvent("stream_updated", {
353
+ detail: { stream: this.stream },
354
+ }));
355
+ return this.stream;
356
+ });
357
+ }
358
+ stop() {
359
+ var _a;
360
+ (_a = this.stream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((t) => {
361
+ t.stop();
362
+ });
363
+ }
364
+ }
365
+
366
+ const initialState$1 = {
367
+ cameraDeviceError: null,
368
+ cameraDevices: [],
369
+ isSettingCameraDevice: false,
370
+ isSettingMicrophoneDevice: false,
371
+ isStarting: false,
372
+ microphoneDeviceError: null,
373
+ microphoneDevices: [],
374
+ speakerDevices: [],
375
+ startError: null,
376
+ };
377
+ function reducer$1(state, action) {
378
+ switch (action.type) {
379
+ case "DEVICE_LIST_UPDATED":
380
+ return Object.assign(Object.assign({}, state), action.payload);
381
+ case "LOCAL_STREAM_UPDATED":
382
+ return Object.assign(Object.assign({}, state), { currentCameraDeviceId: action.payload.currentCameraDeviceId, currentMicrophoneDeviceId: action.payload.currentMicrophoneDeviceId, localStream: action.payload.stream });
383
+ case "SET_CAMERA_DEVICE":
384
+ return Object.assign(Object.assign({}, state), { cameraDeviceError: null, isSettingCameraDevice: true });
385
+ case "SET_CAMERA_DEVICE_COMPLETE":
386
+ return Object.assign(Object.assign({}, state), { isSettingCameraDevice: false });
387
+ case "SET_CAMERA_DEVICE_ERROR":
388
+ return Object.assign(Object.assign({}, state), { cameraDeviceError: action.payload, isSettingCameraDevice: false });
389
+ case "SET_MICROPHONE_DEVICE":
390
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: true, microphoneDeviceError: null });
391
+ case "SET_MICROPHONE_DEVICE_COMPLETE":
392
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: false });
393
+ case "SET_MICROPHONE_DEVICE_ERROR":
394
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: false, microphoneDeviceError: action.payload });
395
+ case "START":
396
+ return Object.assign(Object.assign({}, state), { isStarting: true, startError: null });
397
+ case "START_COMPLETE":
398
+ return Object.assign(Object.assign({}, state), { isStarting: false });
399
+ case "START_ERROR":
400
+ return Object.assign(Object.assign({}, state), { isStarting: false, startError: action.payload });
401
+ default:
402
+ return state;
403
+ }
404
+ }
405
+ function useLocalMedia(constraints = { audio: true, video: true }) {
406
+ const [localMedia] = React.useState(() => new LocalMedia(constraints));
407
+ const [state, dispatch] = React.useReducer(reducer$1, initialState$1);
408
+ React.useEffect(() => {
409
+ localMedia.addEventListener("device_list_updated", (e) => {
410
+ const { cameraDevices, microphoneDevices, speakerDevices } = e.detail;
411
+ dispatch({ type: "DEVICE_LIST_UPDATED", payload: { cameraDevices, microphoneDevices, speakerDevices } });
412
+ });
413
+ localMedia.addEventListener("stream_updated", (e) => {
414
+ const { stream } = e.detail;
415
+ dispatch({
416
+ type: "LOCAL_STREAM_UPDATED",
417
+ payload: {
418
+ stream,
419
+ currentCameraDeviceId: localMedia.getCameraDeviceId(),
420
+ currentMicrophoneDeviceId: localMedia.getMicrophoneDeviceId(),
421
+ },
422
+ });
423
+ });
424
+ const start = () => tslib.__awaiter(this, void 0, void 0, function* () {
425
+ dispatch({ type: "START" });
426
+ try {
427
+ yield localMedia.start();
428
+ dispatch({ type: "START_COMPLETE" });
429
+ }
430
+ catch (error) {
431
+ dispatch({ type: "START_ERROR", payload: error });
432
+ }
433
+ });
434
+ start();
435
+ // Perform cleanup on unmount
436
+ return () => {
437
+ localMedia.stop();
438
+ };
439
+ }, []);
440
+ return {
441
+ state,
442
+ actions: {
443
+ setCameraDevice: (...args) => tslib.__awaiter(this, void 0, void 0, function* () {
444
+ dispatch({ type: "SET_CAMERA_DEVICE" });
445
+ try {
446
+ yield localMedia.setCameraDevice(...args);
447
+ dispatch({ type: "SET_CAMERA_DEVICE_COMPLETE" });
448
+ }
449
+ catch (error) {
450
+ dispatch({ type: "SET_CAMERA_DEVICE_ERROR", payload: error });
451
+ }
452
+ }),
453
+ setMicrophoneDevice: (...args) => tslib.__awaiter(this, void 0, void 0, function* () {
454
+ dispatch({ type: "SET_MICROPHONE_DEVICE" });
455
+ try {
456
+ yield localMedia.setMicrophoneDevice(...args);
457
+ dispatch({ type: "SET_MICROPHONE_DEVICE_COMPLETE" });
458
+ }
459
+ catch (error) {
460
+ dispatch({ type: "SET_MICROPHONE_DEVICE_ERROR", payload: error });
461
+ }
462
+ }),
463
+ toggleCameraEnabled: (...args) => {
464
+ return localMedia.toggleCameraEnabled(...args);
465
+ },
466
+ toggleMicrophoneEnabled: (...args) => {
467
+ return localMedia.toggleMichrophoneEnabled(...args);
468
+ },
469
+ },
470
+ _ref: localMedia,
471
+ };
472
+ }
473
+
158
474
  const EVENTS = {
159
475
  CLIENT_CONNECTION_STATUS_CHANGED: "client_connection_status_changed",
160
476
  STREAM_ADDED: "stream_added",
@@ -285,10 +601,10 @@ class ServerSocket {
285
601
  }
286
602
 
287
603
  this._socket = io__default["default"](hostName, options);
288
- this._socket.on("reconnect", () => {
604
+ this._socket.io.on("reconnect", () => {
289
605
  this._socket.sendBuffer = [];
290
606
  });
291
- this._socket.on("reconnect_attempt", () => {
607
+ this._socket.io.on("reconnect_attempt", () => {
292
608
  if (this._wasConnectedUsingWebsocket) {
293
609
  this._socket.io.opts.transports = ["websocket"];
294
610
  // only fallback to polling if not safari
@@ -346,6 +662,10 @@ class ServerSocket {
346
662
  );
347
663
  }
348
664
 
665
+ getManager() {
666
+ return this._socket.io;
667
+ }
668
+
349
669
  isConnecting() {
350
670
  return this._socket && this._socket.connecting;
351
671
  }
@@ -400,21 +720,21 @@ var rtcManagerEvents = {
400
720
  DOMINANT_SPEAKER: "dominant_speaker",
401
721
  };
402
722
 
403
- const browserName$3 = adapter__default["default"].browserDetails.browser;
723
+ const browserName$2 = adapter__default["default"].browserDetails.browser;
404
724
  const browserVersion$1 = adapter__default["default"].browserDetails.version;
405
725
 
406
726
  // SDP mangling for deprioritizing H264
407
727
  function deprioritizeH264(sdp) {
408
728
  return SDPUtils__default["default"].splitSections(sdp)
409
- .map((section) => {
729
+ .map(section => {
410
730
  // only modify video sections
411
731
  if (SDPUtils__default["default"].getKind(section) !== "video") return section;
412
732
 
413
733
  // list of payloadTypes used in this sdp/section
414
734
  const h264payloadTypes = SDPUtils__default["default"].matchPrefix(section, "a=rtpmap:")
415
- .map((line) => SDPUtils__default["default"].parseRtpMap(line))
416
- .filter((codec) => /h264/i.test(codec.name))
417
- .map((codec) => "" + codec.payloadType);
735
+ .map(line => SDPUtils__default["default"].parseRtpMap(line))
736
+ .filter(codec => /h264/i.test(codec.name))
737
+ .map(codec => "" + codec.payloadType);
418
738
 
419
739
  // return as is if no h264 found
420
740
  if (!h264payloadTypes.length) return section;
@@ -424,7 +744,7 @@ function deprioritizeH264(sdp) {
424
744
  const mlinePayloadsSection = /(\s\d+)+$/i.exec(mline)[0];
425
745
  const mlinePayloadsNonH264 = mlinePayloadsSection
426
746
  .split(" ")
427
- .filter((payloadType) => payloadType && !h264payloadTypes.includes(payloadType));
747
+ .filter(payloadType => payloadType && !h264payloadTypes.includes(payloadType));
428
748
  const reorderedPayloads = [...mlinePayloadsNonH264, ...h264payloadTypes].join(" ");
429
749
  const newmline = mline.replace(mlinePayloadsSection, " " + reorderedPayloads);
430
750
  return section.replace(mline, newmline);
@@ -452,12 +772,12 @@ function replaceSSRCs(currentDescription, newDescription) {
452
772
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1478685
453
773
  // filter out the mid rtp header extension
454
774
  function filterMidExtension(sdp) {
455
- if (browserName$3 !== "safari" && (browserName$3 !== "firefox" || browserVersion$1 >= 63 || browserVersion$1 === 60)) {
775
+ if (browserName$2 !== "safari" && (browserName$2 !== "firefox" || browserVersion$1 >= 63 || browserVersion$1 === 60)) {
456
776
  return sdp;
457
777
  }
458
778
  return (
459
779
  SDPUtils__default["default"].splitLines(sdp.trim())
460
- .filter((line) => {
780
+ .filter(line => {
461
781
  if (!line.startsWith("a=extmap:")) {
462
782
  return true;
463
783
  }
@@ -473,21 +793,21 @@ function filterMidExtension(sdp) {
473
793
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1534673
474
794
  // Filter out a:msid-semantic header
475
795
  function filterMsidSemantic(sdp) {
476
- if (browserName$3 !== "firefox") {
796
+ if (browserName$2 !== "firefox") {
477
797
  return sdp;
478
798
  }
479
799
  return (
480
800
  SDPUtils__default["default"].splitLines(sdp.trim())
481
- .map((line) => (line.startsWith("a=msid-semantic:") ? "a=msid-semantic: WMS *" : line))
801
+ .map(line => (line.startsWith("a=msid-semantic:") ? "a=msid-semantic: WMS *" : line))
482
802
  .join("\r\n") + "\r\n"
483
803
  );
484
804
  }
485
805
 
486
806
  function isRelayed(pc) {
487
- return pc.getStats(null).then((result) => {
807
+ return pc.getStats(null).then(result => {
488
808
  let localCandidateType;
489
809
  let remoteCandidateType;
490
- result.forEach((report) => {
810
+ result.forEach(report => {
491
811
  // Chrome 58+ / spec
492
812
  if (report.type === "transport" && report.selectedCandidatePairId) {
493
813
  const transport = result.get(report.selectedCandidatePairId);
@@ -517,7 +837,7 @@ const logger$4 = console;
517
837
 
518
838
  // use https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-setparameters to change the video bandwidth.
519
839
  function setVideoBandwidthUsingSetParameters(pc, bandwidth) {
520
- const sender = pc.getSenders().find((s) => s.track && s.track.kind === "video");
840
+ const sender = pc.getSenders().find(s => s.track && s.track.kind === "video");
521
841
  if (!sender) {
522
842
  return Promise.resolve();
523
843
  }
@@ -538,7 +858,7 @@ function setVideoBandwidthUsingSetParameters(pc, bandwidth) {
538
858
  parameters.encodings[0].maxBitrate = bandwidth * 1000; // convert to bps
539
859
  }
540
860
 
541
- return sender.setParameters(parameters).catch((err) => {
861
+ return sender.setParameters(parameters).catch(err => {
542
862
  logger$4.error("setParameters err: ", err);
543
863
  });
544
864
  }
@@ -563,7 +883,7 @@ class Session {
563
883
  this.streamIds = [];
564
884
  this.streams = [];
565
885
  this.earlyIceCandidates = [];
566
- this.afterConnected = new Promise((resolve) => {
886
+ this.afterConnected = new Promise(resolve => {
567
887
  this.registerConnected = resolve;
568
888
  });
569
889
  this.offerOptions = { offerToReceiveAudio: true, offerToReceiveVideo: true };
@@ -600,10 +920,10 @@ class Session {
600
920
  this.streamIds.push(stream.id);
601
921
  this.streams.push(stream);
602
922
  if (RTCPeerConnection.prototype.addTrack) {
603
- stream.getAudioTracks().forEach((track) => {
923
+ stream.getAudioTracks().forEach(track => {
604
924
  this.pc.addTrack(track, stream);
605
925
  });
606
- stream.getVideoTracks().forEach((track) => {
926
+ stream.getVideoTracks().forEach(track => {
607
927
  this.pc.addTrack(track, stream);
608
928
  });
609
929
  } else {
@@ -623,7 +943,7 @@ class Session {
623
943
  removeTrack(track) {
624
944
  const stream = this.streams[0];
625
945
  stream.removeTrack(track);
626
- const sender = this.pc.getSenders().find((sender) => sender.track === track);
946
+ const sender = this.pc.getSenders().find(sender => sender.track === track);
627
947
  if (sender) {
628
948
  this.pc.removeTrack(sender);
629
949
  }
@@ -639,8 +959,8 @@ class Session {
639
959
 
640
960
  if (this.pc) {
641
961
  if (this.pc.removeTrack) {
642
- stream.getTracks().forEach((track) => {
643
- const sender = this.pc.getSenders().find((sender) => sender.track === track);
962
+ stream.getTracks().forEach(track => {
963
+ const sender = this.pc.getSenders().find(sender => sender.track === track);
644
964
  if (sender) {
645
965
  this.pc.removeTrack(sender);
646
966
  }
@@ -658,14 +978,14 @@ class Session {
658
978
  // wrapper around SRD which stores a promise
659
979
  this.srdComplete = this.pc.setRemoteDescription(desc);
660
980
  return this.srdComplete.then(() => {
661
- this.earlyIceCandidates.forEach((candidate) => this.pc.addIceCandidate(candidate));
981
+ this.earlyIceCandidates.forEach(candidate => this.pc.addIceCandidate(candidate));
662
982
  this.earlyIceCandidates = [];
663
983
  });
664
984
  }
665
985
 
666
986
  handleOffer(message) {
667
987
  if (!this.canModifyPeerConnection()) {
668
- return new Promise((resolve) => {
988
+ return new Promise(resolve => {
669
989
  this.pending.push(() => this.handleOffer(message).then(resolve));
670
990
  });
671
991
  }
@@ -683,7 +1003,7 @@ class Session {
683
1003
  .then(() => {
684
1004
  return this.pc.createAnswer();
685
1005
  })
686
- .then((answer) => {
1006
+ .then(answer => {
687
1007
  answerToSignal = answer;
688
1008
  return this.pc.setLocalDescription(answer);
689
1009
  })
@@ -711,7 +1031,7 @@ class Session {
711
1031
  () => {
712
1032
  return setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
713
1033
  },
714
- (e) => {
1034
+ e => {
715
1035
  logger$3.warn("Could not set remote description from remote answer: ", e);
716
1036
  }
717
1037
  );
@@ -732,7 +1052,7 @@ class Session {
732
1052
  // filter due to https://github.com/webrtcHacks/adapter/issues/863
733
1053
  return;
734
1054
  }
735
- this.pc.addIceCandidate(candidate).catch((e) => {
1055
+ this.pc.addIceCandidate(candidate).catch(e => {
736
1056
  logger$3.warn("Failed to add ICE candidate ('%s'): %s", candidate ? candidate.candidate : null, e);
737
1057
  });
738
1058
  });
@@ -770,11 +1090,11 @@ class Session {
770
1090
  if (!pc) return false;
771
1091
  const senders = pc.getSenders();
772
1092
  function dbg(msg) {
773
- const tr = (t) => t && `id:${t.id},kind:${t.kind},state:${t.readyState}`;
1093
+ const tr = t => t && `id:${t.id},kind:${t.kind},state:${t.readyState}`;
774
1094
  logger$3.warn(
775
1095
  `${msg}. newTrack:${tr(newTrack)}, oldTrack:${tr(oldTrack)}, sender tracks: ${JSON.stringify(
776
- senders.map((s) => `s ${tr(s.track)}`)
777
- )}, sender first codecs: ${JSON.stringify(senders.map((s) => (s.getParameters().codecs || [])[0]))}`
1096
+ senders.map(s => `s ${tr(s.track)}`)
1097
+ )}, sender first codecs: ${JSON.stringify(senders.map(s => (s.getParameters().codecs || [])[0]))}`
778
1098
  );
779
1099
  }
780
1100
  if (!senders.length) {
@@ -782,7 +1102,7 @@ class Session {
782
1102
  }
783
1103
  // If we didn't specify oldTrack, replace with first of its kind
784
1104
  if (!oldTrack) {
785
- oldTrack = (senders.find((s) => s.track && s.track.kind === newTrack.kind) || {}).track;
1105
+ oldTrack = (senders.find(s => s.track && s.track.kind === newTrack.kind) || {}).track;
786
1106
  if (!oldTrack) {
787
1107
  // odin: Temporary debug data, remove if you see after 2020-12-01
788
1108
  dbg("No sender with same kind! Add new track then.");
@@ -839,7 +1159,7 @@ class Session {
839
1159
  // we already know that the track has been added at least to the mediastream
840
1160
  return result;
841
1161
  }
842
- const stream = this.streams.find((s) => s.getTracks().find((t) => t.id === newTrack.id)) || this.streams[0];
1162
+ const stream = this.streams.find(s => s.getTracks().find(t => t.id === newTrack.id)) || this.streams[0];
843
1163
  if (!stream) {
844
1164
  dbg("No stream?");
845
1165
  return Promise.reject(new Error("replaceTrack: No stream?"));
@@ -868,7 +1188,7 @@ class Session {
868
1188
  if (pc.localDescription.type === "offer") {
869
1189
  return pc
870
1190
  .createOffer()
871
- .then((offer) => {
1191
+ .then(offer => {
872
1192
  offer.sdp = replaceSSRCs(pc.localDescription.sdp, offer.sdp);
873
1193
  return pc.setLocalDescription(offer);
874
1194
  })
@@ -880,7 +1200,7 @@ class Session {
880
1200
  .then(() => {
881
1201
  return pc.createAnswer();
882
1202
  })
883
- .then((answer) => {
1203
+ .then(answer => {
884
1204
  answer.sdp = replaceSSRCs(pc.localDescription.sdp, answer.sdp);
885
1205
  return pc.setLocalDescription(answer);
886
1206
  });
@@ -895,7 +1215,7 @@ class Session {
895
1215
  if (!this.pc.getStats) {
896
1216
  return;
897
1217
  }
898
- isRelayed(this.pc).then((isRelayed) => {
1218
+ isRelayed(this.pc).then(isRelayed => {
899
1219
  if (isRelayed && this.bandwidth === 0) {
900
1220
  this.changeBandwidth(this.maximumTurnBandwidth);
901
1221
  }
@@ -928,15 +1248,11 @@ const MAXIMUM_TURN_BANDWIDTH = 512; // kbps;
928
1248
  const MAXIMUM_TURN_BANDWIDTH_PREMIUM = 768; // kbps;
929
1249
 
930
1250
  /* globals process */
931
-
932
- process.env.AWF_BASE_URL;
933
- process.env.AWF_API_BASE_URL;
934
- process.env.AP_ROOM_BASE_URL;
935
1251
  const CAMERA_STREAM_ID = "0";
936
1252
 
937
1253
  const logger$2 = console;
938
1254
 
939
- const browserName$2 = adapter__default["default"].browserDetails.browser;
1255
+ const browserName$1 = adapter__default["default"].browserDetails.browser;
940
1256
  const browserVersion = adapter__default["default"].browserDetails.version;
941
1257
 
942
1258
  class BaseRtcManager {
@@ -976,7 +1292,7 @@ class BaseRtcManager {
976
1292
  }
977
1293
 
978
1294
  numberOfRemotePeers() {
979
- return Object.values(this.peerConnections).filter((session) => session.clientId !== this._selfId).length;
1295
+ return Object.values(this.peerConnections).filter(session => session.clientId !== this._selfId).length;
980
1296
  }
981
1297
 
982
1298
  _setConnectionStatus(session, newStatus, clientId) {
@@ -1050,7 +1366,7 @@ class BaseRtcManager {
1050
1366
  // Some macs + ios devices have troubles using h264 encoder since safari 14
1051
1367
  // this will make them encode VP8 instead if available
1052
1368
  const deprioritizeH264Encoding =
1053
- browserName$2 === "safari" && browserVersion >= 14 && this._features.deprioritizeH264OnSafari;
1369
+ browserName$1 === "safari" && browserVersion >= 14 && this._features.deprioritizeH264OnSafari;
1054
1370
 
1055
1371
  this.peerConnections[peerConnectionId] = session = new Session({
1056
1372
  peerConnectionId,
@@ -1069,7 +1385,7 @@ class BaseRtcManager {
1069
1385
  }
1070
1386
 
1071
1387
  _getNonLocalCameraStreamIds() {
1072
- return Object.keys(this.localStreams).filter((streamId) => streamId !== CAMERA_STREAM_ID);
1388
+ return Object.keys(this.localStreams).filter(streamId => streamId !== CAMERA_STREAM_ID);
1073
1389
  }
1074
1390
 
1075
1391
  _isScreensharingLocally() {
@@ -1098,7 +1414,7 @@ class BaseRtcManager {
1098
1414
  }
1099
1415
  const session = this._getOrCreateSession(peerConnectionId, initialBandwidth);
1100
1416
  const constraints = { optional: [] };
1101
- if (browserName$2 === "chrome") {
1417
+ if (browserName$1 === "chrome") {
1102
1418
  constraints.optional.push({
1103
1419
  googCpuOveruseDetection: true,
1104
1420
  });
@@ -1111,13 +1427,13 @@ class BaseRtcManager {
1111
1427
  const host = this._features.turnServerOverrideHost;
1112
1428
  const port = host.indexOf(":") > 0 ? "" : ":443";
1113
1429
  const override = ":" + host + port;
1114
- peerConnectionConfig.iceServers = peerConnectionConfig.iceServers.map((original) => {
1430
+ peerConnectionConfig.iceServers = peerConnectionConfig.iceServers.map(original => {
1115
1431
  const entry = Object.assign({}, original);
1116
1432
  if (entry.url) {
1117
1433
  entry.url = entry.url.replace(/:[^?]*/, override);
1118
1434
  }
1119
1435
  if (entry.urls) {
1120
- entry.urls = entry.urls.map((url) => url.replace(/:[^?]*/, override));
1436
+ entry.urls = entry.urls.map(url => url.replace(/:[^?]*/, override));
1121
1437
  }
1122
1438
  return entry;
1123
1439
  });
@@ -1131,12 +1447,12 @@ class BaseRtcManager {
1131
1447
  }[this._features.useOnlyTURN];
1132
1448
  if (filter) {
1133
1449
  peerConnectionConfig.iceServers = peerConnectionConfig.iceServers.filter(
1134
- (entry) => entry.url && entry.url.match(filter)
1450
+ entry => entry.url && entry.url.match(filter)
1135
1451
  );
1136
1452
  }
1137
1453
  }
1138
1454
 
1139
- if (browserName$2 === "chrome") {
1455
+ if (browserName$1 === "chrome") {
1140
1456
  peerConnectionConfig.sdpSemantics = "unified-plan";
1141
1457
  }
1142
1458
 
@@ -1147,7 +1463,7 @@ class BaseRtcManager {
1147
1463
  clientId,
1148
1464
  });
1149
1465
 
1150
- pc.ontrack = (event) => {
1466
+ pc.ontrack = event => {
1151
1467
  const stream = event.streams[0];
1152
1468
  if (stream.id === "default" && stream.getAudioTracks().length === 0) {
1153
1469
  // due to our PlanB / UnifiedPlan conversion we can run into this:
@@ -1189,7 +1505,7 @@ class BaseRtcManager {
1189
1505
  case "completed":
1190
1506
  newStatus = TYPES.CONNECTION_SUCCESSFUL;
1191
1507
  if (!session.wasEverConnected) {
1192
- this._pendingActionsForConnectedPeerConnections.forEach((action) => {
1508
+ this._pendingActionsForConnectedPeerConnections.forEach(action => {
1193
1509
  if (typeof action === "function") {
1194
1510
  action();
1195
1511
  }
@@ -1201,7 +1517,7 @@ class BaseRtcManager {
1201
1517
  if (
1202
1518
  !session.wasEverConnected &&
1203
1519
  (pc.iceConnectionState.match(/connected|completed/) ||
1204
- (browserName$2 === "chrome" && pc.localDescription && pc.localDescription.type === "answer"))
1520
+ (browserName$1 === "chrome" && pc.localDescription && pc.localDescription.type === "answer"))
1205
1521
  ) {
1206
1522
  session.wasEverConnected = true;
1207
1523
  if (this._features.bandwidth !== "false") {
@@ -1272,7 +1588,7 @@ class BaseRtcManager {
1272
1588
  // Don't add existing screenshare-streams when using SFU as those will be
1273
1589
  // added in a separate session/peerConnection
1274
1590
  if (shouldAddLocalVideo) {
1275
- Object.keys(this.localStreams).forEach((id) => {
1591
+ Object.keys(this.localStreams).forEach(id => {
1276
1592
  if (id === CAMERA_STREAM_ID) {
1277
1593
  return;
1278
1594
  }
@@ -1309,27 +1625,27 @@ class BaseRtcManager {
1309
1625
  }
1310
1626
 
1311
1627
  _forEachPeerConnection(func) {
1312
- Object.keys(this.peerConnections).forEach((peerConnectionId) => {
1628
+ Object.keys(this.peerConnections).forEach(peerConnectionId => {
1313
1629
  const peerConnection = this.peerConnections[peerConnectionId];
1314
1630
  func(peerConnection);
1315
1631
  });
1316
1632
  }
1317
1633
 
1318
1634
  _addStreamToPeerConnections(stream) {
1319
- this._forEachPeerConnection((session) => {
1635
+ this._forEachPeerConnection(session => {
1320
1636
  this._withForcedRenegotiation(session, () => session.addStream(stream));
1321
1637
  });
1322
1638
  }
1323
1639
 
1324
1640
  _addTrackToPeerConnections(track, stream) {
1325
- this._forEachPeerConnection((session) => {
1641
+ this._forEachPeerConnection(session => {
1326
1642
  this._withForcedRenegotiation(session, () => session.addTrack(track, stream));
1327
1643
  });
1328
1644
  }
1329
1645
 
1330
1646
  _replaceTrackToPeerConnections(oldTrack, newTrack) {
1331
1647
  const promises = [];
1332
- this._forEachPeerConnection((session) => {
1648
+ this._forEachPeerConnection(session => {
1333
1649
  if (!session.hasConnectedPeerConnection()) {
1334
1650
  logger$2.log("Session doesn't have a connected PeerConnection, adding pending action!");
1335
1651
  const pendingActions = this._pendingActionsForConnectedPeerConnections;
@@ -1347,7 +1663,7 @@ class BaseRtcManager {
1347
1663
  reject(`ReplaceTrack returned false`);
1348
1664
  return;
1349
1665
  }
1350
- replacedTrackPromise.then((track) => resolve(track)).catch((error) => reject(error));
1666
+ replacedTrackPromise.then(track => resolve(track)).catch(error => reject(error));
1351
1667
  };
1352
1668
  pendingActions.push(action);
1353
1669
  });
@@ -1365,13 +1681,13 @@ class BaseRtcManager {
1365
1681
  }
1366
1682
 
1367
1683
  _removeStreamFromPeerConnections(stream) {
1368
- this._forEachPeerConnection((session) => {
1684
+ this._forEachPeerConnection(session => {
1369
1685
  this._withForcedRenegotiation(session, () => session.removeStream(stream));
1370
1686
  });
1371
1687
  }
1372
1688
 
1373
1689
  _removeTrackFromPeerConnections(track) {
1374
- this._forEachPeerConnection((session) => {
1690
+ this._forEachPeerConnection(session => {
1375
1691
  this._withForcedRenegotiation(session, () => session.removeTrack(track));
1376
1692
  });
1377
1693
  }
@@ -1424,11 +1740,11 @@ class BaseRtcManager {
1424
1740
  }
1425
1741
 
1426
1742
  disconnectAll() {
1427
- Object.keys(this.peerConnections).forEach((peerConnectionId) => {
1743
+ Object.keys(this.peerConnections).forEach(peerConnectionId => {
1428
1744
  this.disconnect(peerConnectionId);
1429
1745
  });
1430
1746
  this.peerConnections = {};
1431
- this._socketListenerDeregisterFunctions.forEach((func) => {
1747
+ this._socketListenerDeregisterFunctions.forEach(func => {
1432
1748
  func();
1433
1749
  });
1434
1750
  this._socketListenerDeregisterFunctions = [];
@@ -1445,7 +1761,7 @@ class BaseRtcManager {
1445
1761
  * used in getUserMedia.
1446
1762
  */
1447
1763
  fixChromeAudio(constraints) {
1448
- if (browserName$2 !== "chrome") {
1764
+ if (browserName$1 !== "chrome") {
1449
1765
  return;
1450
1766
  }
1451
1767
  const localStream = this._getLocalCameraStream();
@@ -1453,7 +1769,7 @@ class BaseRtcManager {
1453
1769
  if (!audioTrack || audioTrack.readyState !== "ended") {
1454
1770
  return;
1455
1771
  }
1456
- return navigator.mediaDevices.getUserMedia({ audio: constraints }).then((stream) => {
1772
+ return navigator.mediaDevices.getUserMedia({ audio: constraints }).then(stream => {
1457
1773
  const track = stream.getAudioTracks()[0];
1458
1774
  track.enabled = audioTrack.enabled; // retain mute state and don't accidentally unmute.
1459
1775
  localStream.removeTrack(audioTrack); // remove the old track.
@@ -1487,7 +1803,7 @@ class BaseRtcManager {
1487
1803
  this._socketListenerDeregisterFunctions = [
1488
1804
  () => this._clearMediaServersRefresh(),
1489
1805
 
1490
- this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
1806
+ this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, data => {
1491
1807
  if (data.error) {
1492
1808
  logger$2.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
1493
1809
  return;
@@ -1495,11 +1811,11 @@ class BaseRtcManager {
1495
1811
  this._updateAndScheduleMediaServersRefresh(data);
1496
1812
  }),
1497
1813
 
1498
- this._serverSocket.on(RELAY_MESSAGES.READY_TO_RECEIVE_OFFER, (data) => {
1814
+ this._serverSocket.on(RELAY_MESSAGES.READY_TO_RECEIVE_OFFER, data => {
1499
1815
  this._connect(data.clientId);
1500
1816
  }),
1501
1817
 
1502
- this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, (data) => {
1818
+ this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, data => {
1503
1819
  const session = this._getSession(data.clientId);
1504
1820
  if (!session) {
1505
1821
  logger$2.warn("No RTCPeerConnection on ICE_CANDIDATE", data);
@@ -1508,7 +1824,7 @@ class BaseRtcManager {
1508
1824
  session.addIceCandidate(data.message);
1509
1825
  }),
1510
1826
 
1511
- this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, (data) => {
1827
+ this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, data => {
1512
1828
  const session = this._getSession(data.clientId);
1513
1829
  if (!session) {
1514
1830
  logger$2.warn("No RTCPeerConnection on ICE_END_OF_CANDIDATES", data);
@@ -1518,14 +1834,14 @@ class BaseRtcManager {
1518
1834
  }),
1519
1835
 
1520
1836
  // when a new SDP offer is received from another client
1521
- this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, (data) => {
1837
+ this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, data => {
1522
1838
  const session = this._getSession(data.clientId);
1523
1839
  if (!session) {
1524
1840
  logger$2.warn("No RTCPeerConnection on SDP_OFFER", data);
1525
1841
  return;
1526
1842
  }
1527
1843
  const offer = this._transformIncomingSdp(data.message, session.pc);
1528
- session.handleOffer(offer).then((answer) => {
1844
+ session.handleOffer(offer).then(answer => {
1529
1845
  this._emitServerEvent(RELAY_MESSAGES.SDP_ANSWER, {
1530
1846
  receiverId: data.clientId,
1531
1847
  message: this._transformOutgoingSdp(answer),
@@ -1534,7 +1850,7 @@ class BaseRtcManager {
1534
1850
  }),
1535
1851
 
1536
1852
  // when a new SDP answer is received from another client
1537
- this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, (data) => {
1853
+ this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, data => {
1538
1854
  const session = this._getSession(data.clientId);
1539
1855
  if (!session) {
1540
1856
  logger$2.warn("No RTCPeerConnection on SDP_ANSWER", data);
@@ -1552,7 +1868,7 @@ class BaseRtcManager {
1552
1868
  }
1553
1869
 
1554
1870
  const logger$1 = console;
1555
- const browserName$1 = adapter__default["default"].browserDetails.browser;
1871
+ const browserName = adapter__default["default"].browserDetails.browser;
1556
1872
  class P2pRtcManager extends BaseRtcManager {
1557
1873
  _connect(clientId) {
1558
1874
  const shouldAddLocalVideo = true;
@@ -1604,7 +1920,7 @@ class P2pRtcManager extends BaseRtcManager {
1604
1920
  session.isOperationPending = true;
1605
1921
 
1606
1922
  pc.createOffer(constraints || this.offerOptions)
1607
- .then((offer) => {
1923
+ .then(offer => {
1608
1924
  this._emitServerEvent(RELAY_MESSAGES.SDP_OFFER, {
1609
1925
  receiverId: clientId,
1610
1926
  message: this._transformOutgoingSdp(offer),
@@ -1613,17 +1929,17 @@ class P2pRtcManager extends BaseRtcManager {
1613
1929
  // workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1394602
1614
1930
  // make Chrome send media later when there are two (more more?) video tracks.
1615
1931
  if (
1616
- browserName$1 === "chrome" &&
1617
- pc.getSenders().filter((sender) => sender.track && sender.track.kind === "video").length >= 2
1932
+ browserName === "chrome" &&
1933
+ pc.getSenders().filter(sender => sender.track && sender.track.kind === "video").length >= 2
1618
1934
  ) {
1619
1935
  session.pendingOffer = offer;
1620
1936
  return;
1621
1937
  }
1622
- pc.setLocalDescription(offer).catch((e) => {
1938
+ pc.setLocalDescription(offer).catch(e => {
1623
1939
  logger$1.warn("RTCPeerConnection.setLocalDescription() failed with local offer", e);
1624
1940
  });
1625
1941
  })
1626
- .catch((e) => {
1942
+ .catch(e => {
1627
1943
  logger$1.warn("RTCPeerConnection.createOffer() failed to create local offer", e);
1628
1944
  });
1629
1945
  }
@@ -1673,7 +1989,7 @@ class P2pRtcManager extends BaseRtcManager {
1673
1989
  return 0;
1674
1990
  }
1675
1991
 
1676
- this._forEachPeerConnection((session) => {
1992
+ this._forEachPeerConnection(session => {
1677
1993
  session.changeBandwidth(bandwidth);
1678
1994
  });
1679
1995
 
@@ -1704,7 +2020,7 @@ class P2pRtcManager extends BaseRtcManager {
1704
2020
  pc.addTrack(this._stoppedVideoTrack, localCameraStream);
1705
2021
  }
1706
2022
 
1707
- pc.onicecandidate = (event) => {
2023
+ pc.onicecandidate = event => {
1708
2024
  if (event.candidate) {
1709
2025
  session.relayCandidateSeen = session.relayCandidateSeen || event.candidate.type === "relay";
1710
2026
  this._emitServerEvent(RELAY_MESSAGES.ICE_CANDIDATE, {
@@ -1761,7 +2077,7 @@ class P2pRtcManager extends BaseRtcManager {
1761
2077
  const numPeers = this.numberOfPeerconnections();
1762
2078
  if (numPeers === 0) {
1763
2079
  setTimeout(() => {
1764
- this.numberOfPeerconnections();
2080
+ //const numPeers = this.numberOfPeerconnections();
1765
2081
  }, 60 * 1000);
1766
2082
  }
1767
2083
  }
@@ -1775,13 +2091,13 @@ class P2pRtcManager extends BaseRtcManager {
1775
2091
 
1776
2092
  stopOrResumeVideo(localStream, enable) {
1777
2093
  // actually turn off the camera. Chrome-only (Firefox has different plans)
1778
- if (browserName$1 !== "chrome") {
2094
+ if (browserName !== "chrome") {
1779
2095
  return;
1780
2096
  }
1781
2097
  if (enable === false) {
1782
2098
  // try to stop the local camera so the camera light goes off.
1783
2099
  setTimeout(() => {
1784
- localStream.getVideoTracks().forEach((track) => {
2100
+ localStream.getVideoTracks().forEach(track => {
1785
2101
  if (track.enabled === false) {
1786
2102
  track.stop();
1787
2103
  localStream.removeTrack(track);
@@ -1820,7 +2136,7 @@ class P2pRtcManager extends BaseRtcManager {
1820
2136
  // device has been plugged out or similar
1821
2137
  return;
1822
2138
  }
1823
- navigator.mediaDevices.getUserMedia({ video: constraints }).then((stream) => {
2139
+ navigator.mediaDevices.getUserMedia({ video: constraints }).then(stream => {
1824
2140
  const track = stream.getVideoTracks()[0];
1825
2141
  localStream.addTrack(track);
1826
2142
  this._emit(EVENTS.LOCAL_STREAM_TRACK_ADDED, {
@@ -1979,10 +2295,11 @@ class VegaParser {
1979
2295
  }
1980
2296
 
1981
2297
  class VegaConnection extends EventEmitter.EventEmitter {
1982
- constructor(wsUrl, logger) {
2298
+ constructor(wsUrl, logger, protocol = "whereby-sfu#v4") {
1983
2299
  super();
1984
2300
 
1985
2301
  this.wsUrl = wsUrl;
2302
+ this.protocol = protocol;
1986
2303
  this.logger = logger;
1987
2304
 
1988
2305
  // This is the map of sent requests that are waiting for a response
@@ -1991,7 +2308,7 @@ class VegaConnection extends EventEmitter.EventEmitter {
1991
2308
  }
1992
2309
 
1993
2310
  _setupSocket() {
1994
- this.socket = new WebSocket(this.wsUrl, "whereby-sfu#v4");
2311
+ this.socket = new WebSocket(this.wsUrl, this.protocol);
1995
2312
  this.socket.onopen = this._onOpen.bind(this);
1996
2313
  this.socket.onmessage = this._onMessage.bind(this);
1997
2314
  this.socket.onclose = this._onClose.bind(this);
@@ -2005,13 +2322,15 @@ class VegaConnection extends EventEmitter.EventEmitter {
2005
2322
  this.socket.onerror = null;
2006
2323
  this.socket = null;
2007
2324
 
2008
- this.sents.forEach((sent) => sent.close());
2325
+ this.sents.forEach(sent => sent.close());
2009
2326
 
2010
2327
  this.emit("close");
2011
2328
  }
2012
2329
 
2013
2330
  close() {
2014
- this.socket?.close();
2331
+ if (!this.socket) return;
2332
+
2333
+ this.socket.close();
2015
2334
  }
2016
2335
 
2017
2336
  _onOpen() {
@@ -2023,11 +2342,15 @@ class VegaConnection extends EventEmitter.EventEmitter {
2023
2342
  _onMessage(event) {
2024
2343
  const socketMessage = VegaParser.parse(event.data);
2025
2344
 
2345
+ if (!socketMessage) {
2346
+ return this.logger.log("VegaConnectionManager: Received invalid message", event.data);
2347
+ }
2348
+
2026
2349
  this.logger.log("VegaConnectionManager: Received message", socketMessage);
2027
2350
 
2028
- if (socketMessage?.response) {
2351
+ if (socketMessage.response) {
2029
2352
  this._handleResponse(socketMessage);
2030
- } else if (socketMessage?.message) {
2353
+ } else if (socketMessage.message) {
2031
2354
  this.emit("message", socketMessage);
2032
2355
  }
2033
2356
  }
@@ -2073,13 +2396,13 @@ class VegaConnection extends EventEmitter.EventEmitter {
2073
2396
  const sent = {
2074
2397
  id: request.id,
2075
2398
  method: request.method,
2076
- resolve: (data2) => {
2399
+ resolve: data2 => {
2077
2400
  if (!this.sents.delete(request.id)) return;
2078
2401
 
2079
2402
  clearTimeout(sent.timer);
2080
2403
  pResolve(data2);
2081
2404
  },
2082
- reject: (error) => {
2405
+ reject: error => {
2083
2406
  if (!this.sents.delete(request.id)) return;
2084
2407
 
2085
2408
  clearTimeout(sent.timer);
@@ -2134,7 +2457,7 @@ const VIDEO_SETTINGS_VP9 = {
2134
2457
  codecOptions: {
2135
2458
  videoGoogleStartBitrate: 500,
2136
2459
  },
2137
- encodings: [{ scalabilityMode: "S3T3_KEY", networkPriority: "high" }],
2460
+ encodings: [{ scalabilityMode: "L3T2_KEY", networkPriority: "high" }],
2138
2461
  };
2139
2462
 
2140
2463
  const SCREEN_SHARE_SETTINGS = {
@@ -2143,13 +2466,13 @@ const SCREEN_SHARE_SETTINGS = {
2143
2466
 
2144
2467
  const SCREEN_SHARE_SIMULCAST_SETTINGS = {
2145
2468
  encodings: [
2146
- { dtx: true, maxBitrate: 500000 },
2147
- { dtx: true, maxBitrate: 1500000 },
2469
+ { scaleResolutionDownBy: 2, dtx: true, maxBitrate: 500000 },
2470
+ { scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
2148
2471
  ],
2149
2472
  };
2150
2473
 
2151
2474
  const SCREEN_SHARE_SETTINGS_VP9 = {
2152
- encodings: [{ scalabilityMode: "S3T3", dtx: true, networkPriority: "high" }],
2475
+ encodings: [{ scalabilityMode: "L1T1", dtx: true, networkPriority: "high" }],
2153
2476
  };
2154
2477
 
2155
2478
  const getMediaSettings = (kind, isScreenShare, features) => {
@@ -2165,8 +2488,8 @@ const getMediaSettings = (kind, isScreenShare, features) => {
2165
2488
 
2166
2489
  return SCREEN_SHARE_SETTINGS;
2167
2490
  } else {
2168
- if (lowDataModeEnabled) return VIDEO_SETTINGS_SD;
2169
2491
  if (vp9On) return VIDEO_SETTINGS_VP9;
2492
+ if (lowDataModeEnabled) return VIDEO_SETTINGS_SD;
2170
2493
 
2171
2494
  return VIDEO_SETTINGS_HD;
2172
2495
  }
@@ -2177,30 +2500,30 @@ const modifyMediaCapabilities = (routerRtpCapabilities, features) => {
2177
2500
 
2178
2501
  if (vp9On) {
2179
2502
  const { preferredPayloadType } = routerRtpCapabilities.codecs.find(
2180
- (codec) => codec.mimeType.toLowerCase() === "video/vp9"
2503
+ codec => codec.mimeType.toLowerCase() === "video/vp9"
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
2192
2515
  );
2193
2516
  } else if (h264On) {
2194
2517
  const { preferredPayloadType } = routerRtpCapabilities.codecs.find(
2195
- (codec) => codec.mimeType.toLowerCase() === "video/h264"
2518
+ codec => codec.mimeType.toLowerCase() === "video/h264"
2196
2519
  );
2197
2520
 
2198
2521
  const { preferredPayloadType: aptPreferredPayloadType } = routerRtpCapabilities.codecs.find(
2199
- (codec) => codec.mimeType.toLowerCase() === "video/rtx" && codec.parameters.apt === preferredPayloadType
2522
+ codec => codec.mimeType.toLowerCase() === "video/rtx" && codec.parameters.apt === preferredPayloadType
2200
2523
  );
2201
2524
 
2202
2525
  routerRtpCapabilities.codecs = routerRtpCapabilities.codecs.filter(
2203
- (codec) =>
2526
+ codec =>
2204
2527
  codec.kind === "audio" ||
2205
2528
  codec.preferredPayloadType === preferredPayloadType ||
2206
2529
  codec.preferredPayloadType === aptPreferredPayloadType
@@ -2234,7 +2557,7 @@ const maybeTurnOnly = (transportConfig, features) => {
2234
2557
  }[features.useOnlyTURN];
2235
2558
 
2236
2559
  if (filter) {
2237
- transportConfig.iceServers = transportConfig.iceServers.filter((entry) => entry.url && entry.url.match(filter));
2560
+ transportConfig.iceServers = transportConfig.iceServers.filter(entry => entry.url && entry.url.match(filter));
2238
2561
  }
2239
2562
  };
2240
2563
 
@@ -2338,7 +2661,7 @@ class VegaRtcManager {
2338
2661
  this._socketListenerDeregisterFunctions.push(
2339
2662
  () => this._clearMediaServersRefresh(),
2340
2663
 
2341
- this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
2664
+ this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, data => {
2342
2665
  if (data.error) {
2343
2666
  logger.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
2344
2667
  return;
@@ -2375,7 +2698,7 @@ class VegaRtcManager {
2375
2698
  this._vegaConnection = new VegaConnection(wsUrl, logger);
2376
2699
  this._vegaConnection.on("open", () => this._join());
2377
2700
  this._vegaConnection.on("close", () => this._onClose());
2378
- this._vegaConnection.on("message", (message) => this._onMessage(message));
2701
+ this._vegaConnection.on("message", message => this._onMessage(message));
2379
2702
  }
2380
2703
 
2381
2704
  _onClose() {
@@ -2456,7 +2779,7 @@ class VegaRtcManager {
2456
2779
  maybeTurnOnly(transportOptions, this._features);
2457
2780
 
2458
2781
  const transport = this._mediasoupDevice[creator](transportOptions);
2459
- const onConnectionStateListener = async (connectionState) => {
2782
+ const onConnectionStateListener = async connectionState => {
2460
2783
  logger.debug(`Transport ConnectionStateChanged ${connectionState}`);
2461
2784
  if (connectionState !== "disconnected" && connectionState !== "failed") {
2462
2785
  return;
@@ -2568,7 +2891,7 @@ class VegaRtcManager {
2568
2891
  const error = await transport
2569
2892
  .restartIce({ iceParameters })
2570
2893
  .then(() => null)
2571
- .catch((err) => err);
2894
+ .catch(err => err);
2572
2895
 
2573
2896
  if (error) {
2574
2897
  logger.error(`_restartIce: ICE restart failed: ${error}`);
@@ -2579,7 +2902,7 @@ class VegaRtcManager {
2579
2902
  break;
2580
2903
  default:
2581
2904
  // exponential backoff
2582
- await new Promise((resolve) => {
2905
+ await new Promise(resolve => {
2583
2906
  setTimeout(() => {
2584
2907
  resolve();
2585
2908
  }, Math.min(RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS * 2 ** retried, 60000));
@@ -2589,7 +2912,7 @@ class VegaRtcManager {
2589
2912
  }
2590
2913
  return;
2591
2914
  }
2592
- await new Promise((resolve) => {
2915
+ await new Promise(resolve => {
2593
2916
  setTimeout(() => {
2594
2917
  resolve();
2595
2918
  }, 60000 * Math.min(8, retried + 1));
@@ -3163,54 +3486,6 @@ class VegaRtcManager {
3163
3486
  this._webcamPaused = !enable;
3164
3487
 
3165
3488
  this._pauseResumeWebcam();
3166
-
3167
- if (browserName === "chrome") {
3168
- // actually turn off the camera. Chrome-only (Firefox etc. has different plans)
3169
-
3170
- if (!enable) {
3171
- clearTimeout(this._stopCameraTimeout);
3172
-
3173
- // try to stop the local camera so the camera light goes off.
3174
- this._stopCameraTimeout = setTimeout(() => {
3175
- localStream.getVideoTracks().forEach((track) => {
3176
- if (track.enabled === false) {
3177
- track.stop();
3178
- localStream.removeTrack(track);
3179
-
3180
- this._emitToPWA(EVENTS.LOCAL_STREAM_TRACK_REMOVED, {
3181
- stream: localStream,
3182
- track,
3183
- });
3184
-
3185
- if (
3186
- this._webcamProducer &&
3187
- !this._webcamProducer.closed &&
3188
- this._webcamProducer.track === track
3189
- ) {
3190
- this._stopProducer(this._webcamProducer);
3191
- this._webcamProducer = null;
3192
- this._webcamTrack = null;
3193
- }
3194
- }
3195
- });
3196
- }, 5000);
3197
- } else if (localStream.getVideoTracks().length === 0) {
3198
- // re-enable the stream
3199
- const constraints = this._webrtcProvider.getMediaConstraints().video;
3200
- navigator.mediaDevices.getUserMedia({ video: constraints }).then((stream) => {
3201
- const track = stream.getVideoTracks()[0];
3202
- localStream.addTrack(track);
3203
-
3204
- this._emitToPWA(EVENTS.LOCAL_STREAM_TRACK_ADDED, {
3205
- streamId: localStream.id,
3206
- tracks: [track],
3207
- screenShare: false,
3208
- });
3209
-
3210
- this._sendWebcam(track);
3211
- });
3212
- }
3213
- }
3214
3489
  }
3215
3490
 
3216
3491
  supportsScreenShareAudio() {
@@ -3277,7 +3552,7 @@ class VegaRtcManager {
3277
3552
  }
3278
3553
 
3279
3554
  disconnectAll() {
3280
- this._socketListenerDeregisterFunctions.forEach((func) => {
3555
+ this._socketListenerDeregisterFunctions.forEach(func => {
3281
3556
  func();
3282
3557
  });
3283
3558
 
@@ -3320,7 +3595,7 @@ class VegaRtcManager {
3320
3595
  return;
3321
3596
  }
3322
3597
  })
3323
- .catch((error) => {
3598
+ .catch(error => {
3324
3599
  console.error('"message" failed [error:%o]', error);
3325
3600
  });
3326
3601
  }
@@ -3455,7 +3730,7 @@ class VegaRtcManager {
3455
3730
  const toPauseConsumers = [];
3456
3731
  const toResumeConsumers = [];
3457
3732
 
3458
- this._consumers.forEach((consumer) => {
3733
+ this._consumers.forEach(consumer => {
3459
3734
  if (consumer.appData.sourceClientId !== clientId) return;
3460
3735
 
3461
3736
  const hasAccepted = consumer.appData.screenShare ? hasAcceptedScreenStream : hasAcceptedWebcamStream;
@@ -3493,6 +3768,7 @@ class VegaRtcManager {
3493
3768
  clientId,
3494
3769
  stream: webcamStream,
3495
3770
  streamId: camStreamId,
3771
+ streamType: "webcam",
3496
3772
  });
3497
3773
 
3498
3774
  clientState.hasEmittedWebcamStream = true;
@@ -3504,6 +3780,7 @@ class VegaRtcManager {
3504
3780
  clientId,
3505
3781
  stream: screenStream,
3506
3782
  streamId: screenShareStreamId,
3783
+ streamType: "screenshare",
3507
3784
  });
3508
3785
 
3509
3786
  clientState.hasEmittedScreenStream = true;
@@ -5006,6 +5283,15 @@ class RemoteParticipant extends RoomParticipant {
5006
5283
  this.newJoiner = newJoiner;
5007
5284
  this.streams = streams.map((streamId) => ({ id: streamId, state: newJoiner ? "new_accept" : "to_accept" }));
5008
5285
  }
5286
+ addStream(streamId, state) {
5287
+ this.streams.push({ id: streamId, state });
5288
+ }
5289
+ removeStream(streamId) {
5290
+ const index = this.streams.findIndex((s) => s.id === streamId);
5291
+ if (index !== -1) {
5292
+ this.streams.splice(index, 1);
5293
+ }
5294
+ }
5009
5295
  updateStreamState(streamId, state) {
5010
5296
  const stream = this.streams.find((s) => s.id === streamId);
5011
5297
  if (stream) {
@@ -5020,19 +5306,23 @@ class LocalParticipant extends RoomParticipant {
5020
5306
  }
5021
5307
  }
5022
5308
 
5023
- const API_BASE_URL = "https://api.appearin.net";
5024
- const SIGNAL_BASE_URL = "wss://signal.appearin.net";
5309
+ const API_BASE_URL = process.env["REACT_APP_API_BASE_URL"] || "https://api.whereby.dev";
5310
+ const SIGNAL_BASE_URL = process.env["REACT_APP_SIGNAL_BASE_URL"] || "wss://signal.appearin.net";
5025
5311
  const NON_PERSON_ROLES = ["recorder", "streamer"];
5312
+ // cache last reported stream resolutions
5313
+ const reportedStreamResolutions = new Map();
5026
5314
  function createSocket() {
5027
5315
  const parsedUrl = new URL(SIGNAL_BASE_URL);
5028
- const path = `${parsedUrl.pathname.replace(/^\/$/, "")}/protocol/socket.io/v1`;
5316
+ const path = `${parsedUrl.pathname.replace(/^\/$/, "")}/protocol/socket.io/v4`;
5029
5317
  const SOCKET_HOST = parsedUrl.origin;
5030
5318
  const socketConf = {
5319
+ autoConnect: false,
5031
5320
  host: SOCKET_HOST,
5032
5321
  path,
5033
5322
  reconnectionDelay: 5000,
5034
5323
  reconnectionDelayMax: 30000,
5035
5324
  timeout: 10000,
5325
+ withCredentials: true,
5036
5326
  };
5037
5327
  return new ServerSocket(SOCKET_HOST, socketConf);
5038
5328
  }
@@ -5041,12 +5331,20 @@ const noop = () => {
5041
5331
  };
5042
5332
  const TypedEventTarget = EventTarget;
5043
5333
  class RoomConnection extends TypedEventTarget {
5044
- constructor(roomUrl, { displayName, localMediaConstraints, localStream, logger }) {
5334
+ constructor(roomUrl, { displayName, localMedia, localMediaConstraints, logger, roomKey }) {
5045
5335
  super();
5046
5336
  this.localParticipant = null;
5047
5337
  this.remoteParticipants = [];
5048
- this.roomConnectionState = "";
5338
+ this.screenshares = [];
5339
+ this._deviceCredentials = null;
5340
+ this._ownsLocalMedia = false;
5341
+ this.organizationId = "";
5342
+ this.roomConnectionStatus = "";
5343
+ this.selfId = null;
5049
5344
  this.roomUrl = new URL(roomUrl); // Throw if invalid Whereby room url
5345
+ const searchParams = new URLSearchParams(this.roomUrl.search);
5346
+ this._roomKey = roomKey || searchParams.get("roomKey");
5347
+ this.roomName = this.roomUrl.pathname;
5050
5348
  this.logger = logger || {
5051
5349
  debug: noop,
5052
5350
  error: noop,
@@ -5054,10 +5352,20 @@ class RoomConnection extends TypedEventTarget {
5054
5352
  warn: noop,
5055
5353
  };
5056
5354
  this.displayName = displayName;
5057
- this.localStream = localStream;
5058
5355
  this.localMediaConstraints = localMediaConstraints;
5059
5356
  const urls = fromLocation({ host: this.roomUrl.host });
5060
- // Initialize services
5357
+ // Set up local media
5358
+ if (localMedia) {
5359
+ this.localMedia = localMedia;
5360
+ }
5361
+ else if (localMediaConstraints) {
5362
+ this.localMedia = new LocalMedia(localMediaConstraints);
5363
+ this._ownsLocalMedia = true;
5364
+ }
5365
+ else {
5366
+ throw new Error("Missing constraints");
5367
+ }
5368
+ // Set up services
5061
5369
  this.credentialsService = CredentialsService.create({ baseUrl: API_BASE_URL });
5062
5370
  this.apiClient = new ApiClient({
5063
5371
  fetchDeviceCredentials: this.credentialsService.getCredentials.bind(this.credentialsService),
@@ -5079,12 +5387,93 @@ class RoomConnection extends TypedEventTarget {
5079
5387
  // Create signal socket and set up event listeners
5080
5388
  this.signalSocket = createSocket();
5081
5389
  this.signalSocket.on("new_client", this._handleNewClient.bind(this));
5390
+ this.signalSocket.on("chat_message", this._handleNewChatMessage.bind(this));
5082
5391
  this.signalSocket.on("client_left", this._handleClientLeft.bind(this));
5083
5392
  this.signalSocket.on("audio_enabled", this._handleClientAudioEnabled.bind(this));
5084
5393
  this.signalSocket.on("video_enabled", this._handleClientVideoEnabled.bind(this));
5085
5394
  this.signalSocket.on("client_metadata_received", this._handleClientMetadataReceived.bind(this));
5395
+ this.signalSocket.on("knock_handled", this._handleKnockHandled.bind(this));
5396
+ this.signalSocket.on("knocker_left", this._handleKnockerLeft.bind(this));
5397
+ this.signalSocket.on("room_joined", this._handleRoomJoined.bind(this));
5398
+ this.signalSocket.on("room_knocked", this._handleRoomKnocked.bind(this));
5399
+ this.signalSocket.on("cloud_recording_stopped", this._handleCloudRecordingStopped.bind(this));
5400
+ this.signalSocket.on("screenshare_started", this._handleScreenshareStarted.bind(this));
5401
+ this.signalSocket.on("screenshare_stopped", this._handleScreenshareStopped.bind(this));
5402
+ this.signalSocket.on("streaming_stopped", this._handleStreamingStopped.bind(this));
5403
+ this.signalSocket.on("disconnect", this._handleDisconnect.bind(this));
5404
+ this.signalSocket.on("connect_error", this._handleDisconnect.bind(this));
5405
+ this.signalSocketManager = this.signalSocket.getManager();
5406
+ this.signalSocketManager.on("reconnect", this._handleReconnect.bind(this));
5407
+ // Set up local media listeners
5408
+ this.localMedia.addEventListener("camera_enabled", (e) => {
5409
+ const { enabled } = e.detail;
5410
+ this.signalSocket.emit("enable_video", { enabled });
5411
+ });
5412
+ this.localMedia.addEventListener("microphone_enabled", (e) => {
5413
+ const { enabled } = e.detail;
5414
+ this.signalSocket.emit("enable_audio", { enabled });
5415
+ });
5416
+ const webrtcProvider = {
5417
+ getMediaConstraints: () => ({
5418
+ audio: this.localMedia.isMicrophoneEnabled(),
5419
+ video: this.localMedia.isCameraEnabled(),
5420
+ }),
5421
+ deferrable(clientId) {
5422
+ return !clientId;
5423
+ },
5424
+ };
5425
+ this.rtcManagerDispatcher = new RtcManagerDispatcher({
5426
+ emitter: {
5427
+ emit: this._handleRtcEvent.bind(this),
5428
+ },
5429
+ serverSocket: this.signalSocket,
5430
+ webrtcProvider,
5431
+ features: {
5432
+ lowDataModeEnabled: false,
5433
+ sfuServerOverrideHost: undefined,
5434
+ turnServerOverrideHost: undefined,
5435
+ useOnlyTURN: undefined,
5436
+ vp9On: false,
5437
+ h264On: false,
5438
+ simulcastScreenshareOn: false,
5439
+ },
5440
+ });
5441
+ }
5442
+ get roomKey() {
5443
+ return this._roomKey;
5444
+ }
5445
+ _handleNewChatMessage(message) {
5446
+ this.dispatchEvent(new CustomEvent("chat_message", { detail: message }));
5447
+ }
5448
+ _handleCloudRecordingStarted({ client }) {
5449
+ this.dispatchEvent(new CustomEvent("cloud_recording_started", {
5450
+ detail: {
5451
+ status: "recording",
5452
+ startedAt: client.startedCloudRecordingAt
5453
+ ? new Date(client.startedCloudRecordingAt).getTime()
5454
+ : new Date().getTime(),
5455
+ },
5456
+ }));
5457
+ }
5458
+ _handleStreamingStarted() {
5459
+ this.dispatchEvent(new CustomEvent("streaming_started", {
5460
+ detail: {
5461
+ status: "streaming",
5462
+ // We don't have the streaming start time stored on the
5463
+ // server, so we use the current time instead. This gives
5464
+ // an invalid timestamp for "Client B" if "Client A" has
5465
+ // been streaming for a while before "Client B" joins.
5466
+ startedAt: new Date().getTime(),
5467
+ },
5468
+ }));
5086
5469
  }
5087
5470
  _handleNewClient({ client }) {
5471
+ if (client.role.roleName === "recorder") {
5472
+ this._handleCloudRecordingStarted({ client });
5473
+ }
5474
+ if (client.role.roleName === "streamer") {
5475
+ this._handleStreamingStarted();
5476
+ }
5088
5477
  if (NON_PERSON_ROLES.includes(client.role.roleName)) {
5089
5478
  return;
5090
5479
  }
@@ -5128,6 +5517,135 @@ class RoomConnection extends TypedEventTarget {
5128
5517
  detail: { participantId: remoteParticipant.id, displayName },
5129
5518
  }));
5130
5519
  }
5520
+ _handleKnockHandled(payload) {
5521
+ const { clientId, resolution } = payload;
5522
+ // If the knocker is not the local participant, ignore the event
5523
+ if (clientId !== this.selfId) {
5524
+ return;
5525
+ }
5526
+ if (resolution === "accepted") {
5527
+ this.roomConnectionStatus = "accepted";
5528
+ this._roomKey = payload.metadata.roomKey;
5529
+ this._joinRoom();
5530
+ }
5531
+ else if (resolution === "rejected") {
5532
+ this.roomConnectionStatus = "rejected";
5533
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5534
+ detail: {
5535
+ roomConnectionStatus: this.roomConnectionStatus,
5536
+ },
5537
+ }));
5538
+ }
5539
+ }
5540
+ _handleKnockerLeft(payload) {
5541
+ const { clientId } = payload;
5542
+ this.dispatchEvent(new CustomEvent("waiting_participant_left", {
5543
+ detail: { participantId: clientId },
5544
+ }));
5545
+ }
5546
+ _handleRoomJoined(event) {
5547
+ const { error, isLocked, room, selfId } = event;
5548
+ this.selfId = selfId;
5549
+ if (error === "room_locked" && isLocked) {
5550
+ this.roomConnectionStatus = "room_locked";
5551
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5552
+ detail: {
5553
+ roomConnectionStatus: this.roomConnectionStatus,
5554
+ },
5555
+ }));
5556
+ return;
5557
+ }
5558
+ // Check if we have an error
5559
+ // Check if it is a room joined error
5560
+ // Set state to connect_failed_locked
5561
+ // Set state to connect_failed_no_host
5562
+ if (room) {
5563
+ const { clients, knockers } = room;
5564
+ const localClient = clients.find((c) => c.id === selfId);
5565
+ if (!localClient)
5566
+ throw new Error("Missing local client");
5567
+ this.localParticipant = new LocalParticipant(Object.assign(Object.assign({}, localClient), { stream: this.localMedia.stream || undefined }));
5568
+ const recorderClient = clients.find((c) => c.role.roleName === "recorder");
5569
+ if (recorderClient) {
5570
+ this._handleCloudRecordingStarted({ client: recorderClient });
5571
+ }
5572
+ const streamerClient = clients.find((c) => c.role.roleName === "streamer");
5573
+ if (streamerClient) {
5574
+ this._handleStreamingStarted();
5575
+ }
5576
+ this.remoteParticipants = clients
5577
+ .filter((c) => c.id !== selfId)
5578
+ .filter((c) => !NON_PERSON_ROLES.includes(c.role.roleName))
5579
+ .map((c) => new RemoteParticipant(Object.assign(Object.assign({}, c), { newJoiner: false })));
5580
+ this.roomConnectionStatus = "connected";
5581
+ this.dispatchEvent(new CustomEvent("room_joined", {
5582
+ detail: {
5583
+ localParticipant: this.localParticipant,
5584
+ remoteParticipants: this.remoteParticipants,
5585
+ waitingParticipants: knockers.map((knocker) => {
5586
+ return { id: knocker.clientId, displayName: knocker.displayName };
5587
+ }),
5588
+ },
5589
+ }));
5590
+ }
5591
+ }
5592
+ _handleRoomKnocked(event) {
5593
+ const { clientId, displayName } = event;
5594
+ this.dispatchEvent(new CustomEvent("waiting_participant_joined", {
5595
+ detail: { participantId: clientId, displayName },
5596
+ }));
5597
+ }
5598
+ _handleReconnect() {
5599
+ this.logger.log("Reconnected to signal socket");
5600
+ this.signalSocket.emit("identify_device", { deviceCredentials: this._deviceCredentials });
5601
+ this.signalSocket.once("device_identified", () => {
5602
+ this._joinRoom();
5603
+ });
5604
+ }
5605
+ _handleDisconnect() {
5606
+ this.roomConnectionStatus = "disconnected";
5607
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5608
+ detail: {
5609
+ roomConnectionStatus: this.roomConnectionStatus,
5610
+ },
5611
+ }));
5612
+ }
5613
+ _handleCloudRecordingStopped() {
5614
+ this.dispatchEvent(new CustomEvent("cloud_recording_stopped"));
5615
+ }
5616
+ _handleStreamingStopped() {
5617
+ this.dispatchEvent(new CustomEvent("streaming_stopped"));
5618
+ }
5619
+ _handleScreenshareStarted(screenshare) {
5620
+ const { clientId: participantId, streamId: id, hasAudioTrack } = screenshare;
5621
+ const remoteParticipant = this.remoteParticipants.find((p) => p.id === participantId);
5622
+ if (!remoteParticipant) {
5623
+ this.logger.log("WARN: Could not find participant for screenshare");
5624
+ return;
5625
+ }
5626
+ const foundScreenshare = this.screenshares.find((s) => s.id === id);
5627
+ if (foundScreenshare) {
5628
+ this.logger.log("WARN: Screenshare already exists");
5629
+ return;
5630
+ }
5631
+ remoteParticipant.addStream(id, "to_accept");
5632
+ this._handleAcceptStreams([remoteParticipant]);
5633
+ this.screenshares = [
5634
+ ...this.screenshares,
5635
+ { participantId, id, hasAudioTrack, stream: undefined, isLocal: false },
5636
+ ];
5637
+ }
5638
+ _handleScreenshareStopped(screenshare) {
5639
+ const { clientId: participantId, streamId: id } = screenshare;
5640
+ const remoteParticipant = this.remoteParticipants.find((p) => p.id === participantId);
5641
+ if (!remoteParticipant) {
5642
+ this.logger.log("WARN: Could not find participant for screenshare");
5643
+ return;
5644
+ }
5645
+ remoteParticipant.removeStream(id);
5646
+ this.screenshares = this.screenshares.filter((s) => !(s.participantId === participantId && s.id === id));
5647
+ this.dispatchEvent(new CustomEvent("screenshare_stopped", { detail: { participantId, id } }));
5648
+ }
5131
5649
  _handleRtcEvent(eventName, data) {
5132
5650
  if (eventName === "rtc_manager_created") {
5133
5651
  return this._handleRtcManagerCreated(data);
@@ -5135,16 +5653,26 @@ class RoomConnection extends TypedEventTarget {
5135
5653
  else if (eventName === "stream_added") {
5136
5654
  return this._handleStreamAdded(data);
5137
5655
  }
5656
+ else if (eventName === "rtc_manager_destroyed") {
5657
+ return this._handleRtcManagerDestroyed();
5658
+ }
5138
5659
  else {
5139
5660
  this.logger.log(`Unhandled RTC event ${eventName}`);
5140
5661
  }
5141
5662
  }
5142
5663
  _handleRtcManagerCreated({ rtcManager }) {
5143
- var _a, _b, _c;
5664
+ var _a;
5144
5665
  this.rtcManager = rtcManager;
5145
- if (this.localStream) {
5146
- (_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)));
5666
+ this.localMedia.addRtcManager(rtcManager);
5667
+ if (this.localMedia.stream) {
5668
+ (_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.addNewStream("0", this.localMedia.stream, !this.localMedia.isMicrophoneEnabled(), !this.localMedia.isCameraEnabled());
5147
5669
  }
5670
+ if (this.remoteParticipants.length) {
5671
+ this._handleAcceptStreams(this.remoteParticipants);
5672
+ }
5673
+ }
5674
+ _handleRtcManagerDestroyed() {
5675
+ this.rtcManager = undefined;
5148
5676
  }
5149
5677
  _handleAcceptStreams(remoteParticipants) {
5150
5678
  var _a, _b;
@@ -5168,8 +5696,6 @@ class RoomConnection extends TypedEventTarget {
5168
5696
  if (!newState) {
5169
5697
  return;
5170
5698
  }
5171
- // #endregion
5172
- // #region doAcceptStreams
5173
5699
  if (newState === "to_accept" ||
5174
5700
  (newState === "new_accept" && shouldAcceptNewClients) ||
5175
5701
  (newState === "old_accept" && !shouldAcceptNewClients)) {
@@ -5193,155 +5719,118 @@ class RoomConnection extends TypedEventTarget {
5193
5719
  else ;
5194
5720
  // Update stream state
5195
5721
  participant.updateStreamState(streamId, streamState.replace(/to_|new_|old_/, "done_"));
5196
- // #endregion
5197
5722
  });
5198
5723
  });
5199
5724
  }
5200
- _handleStreamAdded({ clientId, stream, streamId }) {
5725
+ _handleStreamAdded({ clientId, stream, streamId, streamType }) {
5201
5726
  const remoteParticipant = this.remoteParticipants.find((p) => p.id === clientId);
5202
5727
  if (!remoteParticipant) {
5203
5728
  this.logger.log("WARN: Could not find participant for incoming stream");
5204
5729
  return;
5205
5730
  }
5206
- this.dispatchEvent(new CustomEvent("participant_stream_added", { detail: { participantId: clientId, stream, streamId } }));
5731
+ const remoteParticipantStream = remoteParticipant.streams.find((s) => s.id === streamId);
5732
+ if ((remoteParticipant.stream && remoteParticipant.stream.id === streamId) ||
5733
+ (!remoteParticipant.stream && streamType === "webcam") ||
5734
+ (!remoteParticipant.stream &&
5735
+ !streamType &&
5736
+ remoteParticipantStream &&
5737
+ remoteParticipant.streams.indexOf(remoteParticipantStream) < 1)) {
5738
+ this.dispatchEvent(new CustomEvent("participant_stream_added", { detail: { participantId: clientId, stream, streamId } }));
5739
+ return;
5740
+ }
5741
+ // screenshare
5742
+ this.dispatchEvent(new CustomEvent("screenshare_started", {
5743
+ detail: { participantId: clientId, stream, id: streamId, isLocal: false },
5744
+ }));
5745
+ }
5746
+ _joinRoom() {
5747
+ this.signalSocket.emit("join_room", {
5748
+ avatarUrl: null,
5749
+ config: {
5750
+ isAudioEnabled: this.localMedia.isMicrophoneEnabled(),
5751
+ isVideoEnabled: this.localMedia.isCameraEnabled(),
5752
+ },
5753
+ deviceCapabilities: { canScreenshare: true },
5754
+ displayName: this.displayName,
5755
+ isCoLocated: false,
5756
+ isDevicePermissionDenied: false,
5757
+ kickFromOtherRooms: false,
5758
+ organizationId: this.organizationId,
5759
+ roomKey: this.roomKey,
5760
+ roomName: this.roomName,
5761
+ selfId: "",
5762
+ userAgent: `browser-sdk:${sdkVersion }`,
5763
+ });
5207
5764
  }
5208
- /**
5209
- * Public API
5210
- */
5211
5765
  join() {
5212
5766
  return tslib.__awaiter(this, void 0, void 0, function* () {
5213
- if (["connected", "connecting"].includes(this.roomConnectionState)) {
5214
- console.warn(`Trying to join room state is ${this.roomConnectionState}`);
5767
+ if (["connected", "connecting"].includes(this.roomConnectionStatus)) {
5768
+ console.warn(`Trying to join when room state is already ${this.roomConnectionStatus}`);
5215
5769
  return;
5216
5770
  }
5217
5771
  this.logger.log("Joining room");
5218
- this.roomConnectionState = "connecting";
5219
- if (!this.localStream && this.localMediaConstraints) {
5220
- const localStream = yield navigator.mediaDevices.getUserMedia(this.localMediaConstraints);
5221
- this.localStream = localStream;
5222
- }
5772
+ this.signalSocket.connect();
5773
+ this.roomConnectionStatus = "connecting";
5774
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5775
+ detail: {
5776
+ roomConnectionStatus: this.roomConnectionStatus,
5777
+ },
5778
+ }));
5223
5779
  const organization = yield this.organizationServiceCache.fetchOrganization();
5224
5780
  if (!organization) {
5225
5781
  throw new Error("Invalid room url");
5226
5782
  }
5227
- // TODO: Get room permissions
5228
- // TODO: Get room features
5229
- const webrtcProvider = {
5230
- getMediaConstraints: () => {
5231
- var _a, _b;
5232
- return ({
5233
- audio: !!((_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().find((t) => t.enabled)),
5234
- video: !!((_b = this.localStream) === null || _b === void 0 ? void 0 : _b.getVideoTracks().find((t) => t.enabled)),
5235
- });
5236
- },
5237
- deferrable(clientId) {
5238
- return !clientId;
5239
- },
5240
- };
5241
- this.rtcManagerDispatcher = new RtcManagerDispatcher({
5242
- emitter: {
5243
- emit: this._handleRtcEvent.bind(this),
5244
- },
5245
- serverSocket: this.signalSocket,
5246
- webrtcProvider,
5247
- features: {
5248
- lowDataModeEnabled: false,
5249
- sfuServerOverrideHost: undefined,
5250
- turnServerOverrideHost: undefined,
5251
- useOnlyTURN: undefined,
5252
- vp9On: false,
5253
- h264On: false,
5254
- simulcastScreenshareOn: false,
5255
- },
5256
- });
5783
+ this.organizationId = organization.organizationId;
5784
+ if (this._ownsLocalMedia) {
5785
+ yield this.localMedia.start();
5786
+ }
5257
5787
  // Identify device on signal connection
5258
- const deviceCredentials = yield this.credentialsService.getCredentials();
5259
- // TODO: Handle connection and failed connection properly
5260
- setTimeout(() => {
5261
- this.logger.log("Connected to signal socket");
5262
- this.signalSocket.emit("identify_device", { deviceCredentials });
5263
- }, 2000);
5788
+ this._deviceCredentials = yield this.credentialsService.getCredentials();
5789
+ this.logger.log("Connected to signal socket");
5790
+ this.signalSocket.emit("identify_device", { deviceCredentials: this._deviceCredentials });
5264
5791
  this.signalSocket.once("device_identified", () => {
5265
- var _a, _b;
5266
- this.signalSocket.emit("join_room", {
5267
- avatarUrl: null,
5268
- config: {
5269
- isAudioEnabled: !!((_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().find((t) => t.enabled)),
5270
- isVideoEnabled: !!((_b = this.localStream) === null || _b === void 0 ? void 0 : _b.getVideoTracks().find((t) => t.enabled)),
5271
- },
5272
- deviceCapabilities: { canScreenshare: true },
5273
- displayName: this.displayName,
5274
- isCoLocated: false,
5275
- isDevicePermissionDenied: false,
5276
- kickFromOtherRooms: false,
5277
- organizationId: organization.organizationId,
5278
- roomKey: null,
5279
- roomName: this.roomUrl.pathname,
5280
- selfId: "",
5281
- });
5282
- });
5283
- this.signalSocket.once("room_joined", (res) => {
5284
- const { selfId, room: { clients }, } = res;
5285
- const localClient = clients.find((c) => c.id === selfId);
5286
- if (!localClient)
5287
- throw new Error("Missing local client");
5288
- this.localParticipant = new LocalParticipant(Object.assign(Object.assign({}, localClient), { stream: this.localStream }));
5289
- this.remoteParticipants = clients
5290
- .filter((c) => c.id !== selfId)
5291
- .map((c) => new RemoteParticipant(Object.assign(Object.assign({}, c), { newJoiner: false })));
5292
- // Accept remote streams if RTC manager has been initialized
5293
- if (this.rtcManager) {
5294
- this._handleAcceptStreams(this.remoteParticipants);
5295
- }
5296
- this.roomConnectionState = "connected";
5297
- this.dispatchEvent(new CustomEvent("room_joined", {
5298
- detail: {
5299
- localParticipant: this.localParticipant,
5300
- remoteParticipants: this.remoteParticipants,
5301
- },
5302
- }));
5792
+ this._joinRoom();
5303
5793
  });
5304
5794
  });
5305
5795
  }
5306
- leave() {
5307
- return new Promise((resolve) => {
5308
- if (!this.signalSocket) {
5309
- return resolve();
5310
- }
5311
- this.signalSocket.emit("leave_room");
5312
- const leaveTimeout = setTimeout(() => {
5313
- resolve();
5314
- }, 200);
5315
- this.signalSocket.once("room_left", () => {
5316
- clearTimeout(leaveTimeout);
5317
- this.signalSocket.disconnect();
5318
- resolve();
5319
- });
5796
+ knock() {
5797
+ this.roomConnectionStatus = "knocking";
5798
+ this.dispatchEvent(new CustomEvent("room_connection_status_changed", {
5799
+ detail: {
5800
+ roomConnectionStatus: this.roomConnectionStatus,
5801
+ },
5802
+ }));
5803
+ this.signalSocket.emit("knock_room", {
5804
+ displayName: this.displayName,
5805
+ imageUrl: null,
5806
+ kickFromOtherRooms: true,
5807
+ liveVideo: false,
5808
+ organizationId: this.organizationId,
5809
+ roomKey: this._roomKey,
5810
+ roomName: this.roomName,
5320
5811
  });
5321
5812
  }
5322
- toggleCamera(enabled) {
5323
- var _a;
5324
- const localVideoTrack = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks()[0];
5325
- if (!localVideoTrack) {
5326
- this.logger.log("Tried toggling non-existing video track");
5327
- return;
5813
+ leave() {
5814
+ this.roomConnectionStatus = "disconnecting";
5815
+ if (this._ownsLocalMedia) {
5816
+ this.localMedia.stop();
5328
5817
  }
5329
- // TODO: Do stopOrResumeVideo
5330
- const newValue = enabled !== null && enabled !== void 0 ? enabled : !localVideoTrack.enabled;
5331
- localVideoTrack.enabled = newValue;
5332
- this.signalSocket.emit("enable_video", { enabled: newValue });
5333
- }
5334
- toggleMicrophone(enabled) {
5335
- var _a;
5336
- const localAudioTrack = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];
5337
- if (!localAudioTrack) {
5338
- this.logger.log("Tried toggling non-existing audio track");
5818
+ if (this.rtcManager) {
5819
+ this.localMedia.removeRtcManager(this.rtcManager);
5820
+ this.rtcManager.disconnectAll();
5821
+ this.rtcManager = undefined;
5822
+ }
5823
+ if (!this.signalSocket) {
5339
5824
  return;
5340
5825
  }
5341
- // TODO: Do stopOrResumeAudio
5342
- const newValue = enabled !== null && enabled !== void 0 ? enabled : !localAudioTrack.enabled;
5343
- localAudioTrack.enabled = newValue;
5344
- this.signalSocket.emit("enable_audio", { enabled: newValue });
5826
+ this.signalSocket.emit("leave_room");
5827
+ this.signalSocket.disconnect();
5828
+ this.roomConnectionStatus = "disconnected";
5829
+ }
5830
+ sendChatMessage(text) {
5831
+ this.signalSocket.emit("chat_message", {
5832
+ text,
5833
+ });
5345
5834
  }
5346
5835
  setDisplayName(displayName) {
5347
5836
  this.signalSocket.emit("send_client_metadata", {
@@ -5351,8 +5840,93 @@ class RoomConnection extends TypedEventTarget {
5351
5840
  },
5352
5841
  });
5353
5842
  }
5843
+ acceptWaitingParticipant(participantId) {
5844
+ this.signalSocket.emit("handle_knock", {
5845
+ action: "accept",
5846
+ clientId: participantId,
5847
+ response: {},
5848
+ });
5849
+ }
5850
+ rejectWaitingParticipant(participantId) {
5851
+ this.signalSocket.emit("handle_knock", {
5852
+ action: "reject",
5853
+ clientId: participantId,
5854
+ response: {},
5855
+ });
5856
+ }
5857
+ updateStreamResolution({ streamId, width, height }) {
5858
+ var _a, _b;
5859
+ if (!streamId || !this.rtcManager) {
5860
+ return;
5861
+ }
5862
+ // no need to report resolution for local participant
5863
+ if (((_b = (_a = this.localParticipant) === null || _a === void 0 ? void 0 : _a.stream) === null || _b === void 0 ? void 0 : _b.id) === streamId) {
5864
+ return;
5865
+ }
5866
+ const old = reportedStreamResolutions.get(streamId);
5867
+ if (!old || old.width !== width || old.height !== height) {
5868
+ this.rtcManager.updateStreamResolution(streamId, null, { width: width || 1, height: height || 1 });
5869
+ }
5870
+ reportedStreamResolutions.set(streamId, { width, height });
5871
+ }
5872
+ startScreenshare() {
5873
+ var _a;
5874
+ return tslib.__awaiter(this, void 0, void 0, function* () {
5875
+ const screenshareStream = this.localMedia.screenshareStream || (yield this.localMedia.startScreenshare());
5876
+ (_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.addNewStream(screenshareStream.id, screenshareStream, false, true);
5877
+ this.screenshares = [
5878
+ ...this.screenshares,
5879
+ {
5880
+ participantId: this.selfId || "",
5881
+ id: screenshareStream.id,
5882
+ hasAudioTrack: false,
5883
+ stream: screenshareStream,
5884
+ isLocal: true,
5885
+ },
5886
+ ];
5887
+ this.dispatchEvent(new CustomEvent("screenshare_started", {
5888
+ detail: {
5889
+ participantId: this.selfId || "",
5890
+ id: screenshareStream.id,
5891
+ hasAudioTrack: false,
5892
+ stream: screenshareStream,
5893
+ isLocal: true,
5894
+ },
5895
+ }));
5896
+ });
5897
+ }
5898
+ stopScreenshare() {
5899
+ var _a;
5900
+ if (this.localMedia.screenshareStream) {
5901
+ const { id } = this.localMedia.screenshareStream;
5902
+ (_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.removeStream(id, this.localMedia.screenshareStream, null);
5903
+ this.screenshares = this.screenshares.filter((s) => s.id !== id);
5904
+ this.dispatchEvent(new CustomEvent("screenshare_stopped", { detail: { participantId: this.selfId, id } }));
5905
+ this.localMedia.stopScreenshare();
5906
+ }
5907
+ }
5354
5908
  }
5355
5909
 
5910
+ const initialState = {
5911
+ chatMessages: [],
5912
+ cloudRecording: {
5913
+ status: "",
5914
+ startedAt: null,
5915
+ },
5916
+ isJoining: false,
5917
+ isStartingScreenshare: false,
5918
+ joinError: null,
5919
+ startScreenshareError: null,
5920
+ mostRecentChatMessage: null,
5921
+ remoteParticipants: [],
5922
+ roomConnectionStatus: "",
5923
+ screenshares: [],
5924
+ streaming: {
5925
+ status: "",
5926
+ startedAt: null,
5927
+ },
5928
+ waitingParticipants: [],
5929
+ };
5356
5930
  function updateParticipant(remoteParticipants, participantId, updates) {
5357
5931
  const existingParticipant = remoteParticipants.find((p) => p.id === participantId);
5358
5932
  if (!existingParticipant) {
@@ -5365,10 +5939,31 @@ function updateParticipant(remoteParticipants, participantId, updates) {
5365
5939
  ...remoteParticipants.slice(index + 1),
5366
5940
  ];
5367
5941
  }
5942
+ function addScreenshare(screenshares, screenshare) {
5943
+ const existingScreenshare = screenshares.find((ss) => ss.id === screenshare.id);
5944
+ if (existingScreenshare) {
5945
+ return screenshares;
5946
+ }
5947
+ return [...screenshares, screenshare];
5948
+ }
5368
5949
  function reducer(state, action) {
5369
5950
  switch (action.type) {
5951
+ case "CHAT_MESSAGE":
5952
+ return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, action.payload], mostRecentChatMessage: action.payload });
5953
+ case "CLOUD_RECORDING_STARTED":
5954
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
5955
+ status: action.payload.status,
5956
+ startedAt: action.payload.startedAt,
5957
+ } });
5958
+ case "CLOUD_RECORDING_STOPPED":
5959
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
5960
+ status: "",
5961
+ startedAt: null,
5962
+ } });
5370
5963
  case "ROOM_JOINED":
5371
- return Object.assign(Object.assign({}, state), { localParticipant: action.payload.localParticipant, remoteParticipants: action.payload.remoteParticipants, roomConnectionStatus: "connected" });
5964
+ return Object.assign(Object.assign({}, state), { localParticipant: action.payload.localParticipant, remoteParticipants: action.payload.remoteParticipants, waitingParticipants: action.payload.waitingParticipants, roomConnectionStatus: "connected" });
5965
+ case "ROOM_CONNECTION_STATUS_CHANGED":
5966
+ return Object.assign(Object.assign({}, state), { roomConnectionStatus: action.payload.roomConnectionStatus });
5372
5967
  case "PARTICIPANT_AUDIO_ENABLED":
5373
5968
  return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
5374
5969
  isAudioEnabled: action.payload.isAudioEnabled,
@@ -5393,75 +5988,202 @@ function reducer(state, action) {
5393
5988
  if (!state.localParticipant)
5394
5989
  return state;
5395
5990
  return Object.assign(Object.assign({}, state), { localParticipant: Object.assign(Object.assign({}, state.localParticipant), { displayName: action.payload.displayName }) });
5991
+ case "SCREENSHARE_STARTED":
5992
+ return Object.assign(Object.assign({}, state), { screenshares: addScreenshare(state.screenshares, {
5993
+ participantId: action.payload.participantId,
5994
+ id: action.payload.id,
5995
+ hasAudioTrack: action.payload.hasAudioTrack,
5996
+ stream: action.payload.stream,
5997
+ isLocal: action.payload.isLocal,
5998
+ }) });
5999
+ case "SCREENSHARE_STOPPED":
6000
+ return Object.assign(Object.assign({}, state), { screenshares: state.screenshares.filter((ss) => ss.id !== action.payload.id) });
6001
+ case "LOCAL_SCREENSHARE_START_ERROR":
6002
+ return Object.assign(Object.assign({}, state), { isStartingScreenshare: false, startScreenshareError: action.payload });
6003
+ case "LOCAL_SCREENSHARE_STARTING":
6004
+ return Object.assign(Object.assign({}, state), { isStartingScreenshare: true });
6005
+ case "LOCAL_SCREENSHARE_STOPPED":
6006
+ return Object.assign(Object.assign({}, state), { screenshares: state.screenshares.filter((ss) => !ss.isLocal) });
6007
+ case "STREAMING_STARTED":
6008
+ return Object.assign(Object.assign({}, state), { streaming: {
6009
+ status: action.payload.status,
6010
+ startedAt: action.payload.startedAt,
6011
+ } });
6012
+ case "STREAMING_STOPPED":
6013
+ return Object.assign(Object.assign({}, state), { streaming: {
6014
+ status: "",
6015
+ startedAt: null,
6016
+ } });
6017
+ case "WAITING_PARTICIPANT_JOINED":
6018
+ return Object.assign(Object.assign({}, state), { waitingParticipants: [
6019
+ ...state.waitingParticipants,
6020
+ { id: action.payload.participantId, displayName: action.payload.displayName },
6021
+ ] });
6022
+ case "WAITING_PARTICIPANT_LEFT":
6023
+ return Object.assign(Object.assign({}, state), { waitingParticipants: state.waitingParticipants.filter((wp) => wp.id !== action.payload.participantId) });
5396
6024
  default:
5397
6025
  throw state;
5398
6026
  }
5399
- }
5400
-
6027
+ }
5401
6028
  function useRoomConnection(roomUrl, roomConnectionOptions) {
5402
- const [roomConnection, setRoomConnection] = React.useState(null);
5403
- const [state, dispatch] = React.useReducer(reducer, { remoteParticipants: [] });
5404
- React.useEffect(() => {
5405
- setRoomConnection(new RoomConnection(roomUrl, roomConnectionOptions));
5406
- }, [roomUrl]);
5407
- React.useEffect(() => {
5408
- if (!roomConnection) {
5409
- return;
5410
- }
5411
- roomConnection.addEventListener("participant_audio_enabled", (e) => {
6029
+ const [roomConnection] = React.useState(() => {
6030
+ var _a;
6031
+ 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 }));
6032
+ });
6033
+ const [state, dispatch] = React.useReducer(reducer, initialState);
6034
+ function createEventListener(eventName, listener, options) {
6035
+ return {
6036
+ eventName,
6037
+ listener,
6038
+ options,
6039
+ };
6040
+ }
6041
+ const eventListeners = React__default["default"].useMemo(() => [
6042
+ createEventListener("chat_message", (e) => {
6043
+ dispatch({ type: "CHAT_MESSAGE", payload: e.detail });
6044
+ }),
6045
+ createEventListener("cloud_recording_started", (e) => {
6046
+ const { status, startedAt } = e.detail;
6047
+ dispatch({ type: "CLOUD_RECORDING_STARTED", payload: { status, startedAt } });
6048
+ }),
6049
+ createEventListener("cloud_recording_stopped", () => {
6050
+ dispatch({ type: "CLOUD_RECORDING_STOPPED" });
6051
+ }),
6052
+ createEventListener("participant_audio_enabled", (e) => {
5412
6053
  const { participantId, isAudioEnabled } = e.detail;
5413
6054
  dispatch({ type: "PARTICIPANT_AUDIO_ENABLED", payload: { participantId, isAudioEnabled } });
5414
- });
5415
- roomConnection.addEventListener("participant_joined", (e) => {
6055
+ }),
6056
+ createEventListener("participant_joined", (e) => {
5416
6057
  const { remoteParticipant } = e.detail;
5417
6058
  dispatch({ type: "PARTICIPANT_JOINED", payload: { paritipant: remoteParticipant } });
5418
- });
5419
- roomConnection.addEventListener("participant_left", (e) => {
6059
+ }),
6060
+ createEventListener("participant_left", (e) => {
5420
6061
  const { participantId } = e.detail;
5421
6062
  dispatch({ type: "PARTICIPANT_LEFT", payload: { participantId } });
5422
- });
5423
- roomConnection.addEventListener("participant_stream_added", (e) => {
6063
+ }),
6064
+ createEventListener("participant_metadata_changed", (e) => {
6065
+ const { participantId, displayName } = e.detail;
6066
+ dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
6067
+ }),
6068
+ createEventListener("participant_stream_added", (e) => {
5424
6069
  const { participantId, stream } = e.detail;
5425
6070
  dispatch({ type: "PARTICIPANT_STREAM_ADDED", payload: { participantId, stream } });
5426
- });
5427
- roomConnection.addEventListener("room_joined", (e) => {
5428
- const { localParticipant, remoteParticipants } = e.detail;
5429
- dispatch({ type: "ROOM_JOINED", payload: { localParticipant, remoteParticipants } });
5430
- });
5431
- roomConnection.addEventListener("participant_video_enabled", (e) => {
6071
+ }),
6072
+ createEventListener("participant_video_enabled", (e) => {
5432
6073
  const { participantId, isVideoEnabled } = e.detail;
5433
6074
  dispatch({ type: "PARTICIPANT_VIDEO_ENABLED", payload: { participantId, isVideoEnabled } });
5434
- });
5435
- roomConnection.addEventListener("participant_metadata_changed", (e) => {
6075
+ }),
6076
+ createEventListener("room_connection_status_changed", (e) => {
6077
+ const { roomConnectionStatus } = e.detail;
6078
+ dispatch({
6079
+ type: "ROOM_CONNECTION_STATUS_CHANGED",
6080
+ payload: { roomConnectionStatus },
6081
+ });
6082
+ }),
6083
+ createEventListener("room_joined", (e) => {
6084
+ const { localParticipant, remoteParticipants, waitingParticipants } = e.detail;
6085
+ dispatch({
6086
+ type: "ROOM_JOINED",
6087
+ payload: { localParticipant, remoteParticipants, waitingParticipants },
6088
+ });
6089
+ }),
6090
+ createEventListener("screenshare_started", (e) => {
6091
+ const { participantId, stream, id, hasAudioTrack, isLocal } = e.detail;
6092
+ dispatch({
6093
+ type: "SCREENSHARE_STARTED",
6094
+ payload: { participantId, stream, id, hasAudioTrack, isLocal },
6095
+ });
6096
+ }),
6097
+ createEventListener("screenshare_stopped", (e) => {
6098
+ const { participantId, id } = e.detail;
6099
+ dispatch({
6100
+ type: "SCREENSHARE_STOPPED",
6101
+ payload: { participantId, id },
6102
+ });
6103
+ }),
6104
+ createEventListener("streaming_started", (e) => {
6105
+ const { status, startedAt } = e.detail;
6106
+ dispatch({ type: "STREAMING_STARTED", payload: { status, startedAt } });
6107
+ }),
6108
+ createEventListener("streaming_stopped", () => {
6109
+ dispatch({ type: "STREAMING_STOPPED" });
6110
+ }),
6111
+ createEventListener("waiting_participant_joined", (e) => {
5436
6112
  const { participantId, displayName } = e.detail;
5437
- dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
6113
+ dispatch({
6114
+ type: "WAITING_PARTICIPANT_JOINED",
6115
+ payload: { participantId, displayName },
6116
+ });
6117
+ }),
6118
+ ], []);
6119
+ React.useEffect(() => {
6120
+ eventListeners.forEach(({ eventName, listener }) => {
6121
+ roomConnection.addEventListener(eventName, listener);
5438
6122
  });
5439
6123
  roomConnection.join();
5440
6124
  return () => {
6125
+ eventListeners.forEach(({ eventName, listener }) => {
6126
+ roomConnection.removeEventListener(eventName, listener);
6127
+ });
5441
6128
  roomConnection.leave();
5442
6129
  };
5443
- }, [roomConnection]);
5444
- return [
6130
+ }, []);
6131
+ return {
5445
6132
  state,
5446
- {
5447
- toggleCamera: (enabled) => {
5448
- roomConnection === null || roomConnection === void 0 ? void 0 : roomConnection.toggleCamera(enabled);
6133
+ actions: {
6134
+ knock: () => {
6135
+ roomConnection.knock();
5449
6136
  },
5450
- toggleMicrophone: (enabled) => {
5451
- roomConnection === null || roomConnection === void 0 ? void 0 : roomConnection.toggleMicrophone(enabled);
6137
+ sendChatMessage: (text) => {
6138
+ roomConnection.sendChatMessage(text);
5452
6139
  },
5453
6140
  setDisplayName: (displayName) => {
5454
- roomConnection === null || roomConnection === void 0 ? void 0 : roomConnection.setDisplayName(displayName);
6141
+ roomConnection.setDisplayName(displayName);
5455
6142
  dispatch({ type: "LOCAL_CLIENT_DISPLAY_NAME_CHANGED", payload: { displayName } });
5456
6143
  },
6144
+ toggleCamera: (enabled) => {
6145
+ roomConnection.localMedia.toggleCameraEnabled(enabled);
6146
+ },
6147
+ toggleMicrophone: (enabled) => {
6148
+ roomConnection.localMedia.toggleMichrophoneEnabled(enabled);
6149
+ },
6150
+ acceptWaitingParticipant: (participantId) => {
6151
+ roomConnection.acceptWaitingParticipant(participantId);
6152
+ },
6153
+ rejectWaitingParticipant: (participantId) => {
6154
+ roomConnection.rejectWaitingParticipant(participantId);
6155
+ },
6156
+ startScreenshare: () => {
6157
+ dispatch({ type: "LOCAL_SCREENSHARE_STARTING" });
6158
+ try {
6159
+ roomConnection.startScreenshare();
6160
+ }
6161
+ catch (error) {
6162
+ dispatch({ type: "LOCAL_SCREENSHARE_START_ERROR", payload: error });
6163
+ }
6164
+ },
6165
+ stopScreenshare: () => {
6166
+ roomConnection.stopScreenshare();
6167
+ },
5457
6168
  },
5458
- {
5459
- VideoView: VideoElement,
6169
+ components: {
6170
+ VideoView: (props) => React__default["default"].createElement(VideoView, Object.assign({}, props, {
6171
+ onResize: ({ stream, width, height, }) => {
6172
+ roomConnection.updateStreamResolution({
6173
+ streamId: stream.id,
6174
+ width,
6175
+ height,
6176
+ });
6177
+ },
6178
+ })),
5460
6179
  },
5461
- ];
6180
+ _ref: roomConnection,
6181
+ };
5462
6182
  }
5463
6183
 
5464
- const sdkVersion = "2.0.0-alpha2";
6184
+ const sdkVersion = "2.0.0-alpha21";
5465
6185
 
6186
+ exports.VideoView = VideoView;
5466
6187
  exports.sdkVersion = sdkVersion;
6188
+ exports.useLocalMedia = useLocalMedia;
5467
6189
  exports.useRoomConnection = useRoomConnection;