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