@whereby.com/core 0.2.0-beta.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1302 -275
- package/dist/utils.js +1 -1
- package/package.json +5 -5
- /package/dist/{debounce-IlgXge5U.js → debounce-B-cWYxqK.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { createAsyncThunk, createListenerMiddleware, addListener, createSlice, createAction, createSelector, isAnyOf, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
2
2
|
import { io } from 'socket.io-client';
|
|
3
3
|
import adapter from 'webrtc-adapter';
|
|
4
|
-
import {
|
|
4
|
+
import EventEmitter, { EventEmitter as EventEmitter$1 } from 'events';
|
|
5
|
+
import { d as debounce } from './debounce-B-cWYxqK.js';
|
|
5
6
|
import SDPUtils from 'sdp';
|
|
6
7
|
import * as sdpTransform from 'sdp-transform';
|
|
7
|
-
import { v4 } from 'uuid';
|
|
8
|
+
import { v4 as v4$1 } from 'uuid';
|
|
9
|
+
import { Address6 } from 'ip-address';
|
|
10
|
+
import checkIp from 'check-ip';
|
|
11
|
+
import validate from 'uuid-validate';
|
|
8
12
|
import { detectDevice, Device } from 'mediasoup-client';
|
|
9
|
-
import EventEmitter$1, { EventEmitter } from 'events';
|
|
10
13
|
import nodeBtoa from 'btoa';
|
|
11
14
|
import axios from 'axios';
|
|
12
15
|
|
|
@@ -184,13 +187,535 @@ createReactor([selectShouldFetchDeviceCredentials], ({ dispatch }, shouldFetchDe
|
|
|
184
187
|
}
|
|
185
188
|
});
|
|
186
189
|
|
|
190
|
+
let peerConnections = [];
|
|
191
|
+
let peerConnectionCounter = 0;
|
|
192
|
+
const peerConnectionData = new WeakMap();
|
|
193
|
+
|
|
194
|
+
const removePeerConnection = (pc) => {
|
|
195
|
+
peerConnections = peerConnections.filter((old) => old !== pc);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
if (window.RTCPeerConnection) {
|
|
199
|
+
const OriginalRTCPeerConnection = window.RTCPeerConnection;
|
|
200
|
+
function PatchedRTCPeerConnection(rtcConfig) {
|
|
201
|
+
const pc = new OriginalRTCPeerConnection(rtcConfig);
|
|
202
|
+
peerConnections.push(pc);
|
|
203
|
+
peerConnectionData.set(pc, { index: peerConnectionCounter++ });
|
|
204
|
+
const onConnectionStateChange = () => {
|
|
205
|
+
if (pc.connectionState === "closed") {
|
|
206
|
+
removePeerConnection(pc);
|
|
207
|
+
pc.removeEventListener("connectionstatechange", onConnectionStateChange);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
pc.addEventListener("connectionstatechange", onConnectionStateChange);
|
|
211
|
+
return pc;
|
|
212
|
+
}
|
|
213
|
+
PatchedRTCPeerConnection.prototype = OriginalRTCPeerConnection.prototype;
|
|
214
|
+
window.RTCPeerConnection = PatchedRTCPeerConnection;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const debugOn = new URLSearchParams(window.location.search).has("debug");
|
|
218
|
+
|
|
219
|
+
class Logger {
|
|
220
|
+
constructor() {
|
|
221
|
+
this._isEnabled = debugOn;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
isEnabled() {
|
|
225
|
+
return this._isEnabled;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
enable() {
|
|
229
|
+
this._isEnabled = true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
disable() {
|
|
233
|
+
this._isEnabled = false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
info(...params) {
|
|
237
|
+
if (!this._isEnabled) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
// eslint-disable-next-line no-console
|
|
241
|
+
return console.info(...params);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
warn(...params) {
|
|
245
|
+
if (!this._isEnabled) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
return console.warn(...params);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
error(...params) {
|
|
252
|
+
if (!this._isEnabled) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
return console.error(...params);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
withDebugLogger(myDebugger = null) {
|
|
259
|
+
this._debugger = myDebugger;
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
debug(...params) {
|
|
264
|
+
if (!this._isEnabled || !this._debugger) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const suppliedParams = [];
|
|
268
|
+
params.forEach((param) => {
|
|
269
|
+
if (typeof param === "function") {
|
|
270
|
+
const suppliedParam = param();
|
|
271
|
+
suppliedParams.push(suppliedParam);
|
|
272
|
+
} else {
|
|
273
|
+
suppliedParams.push(param);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
this._debugger.print(...suppliedParams);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let currentMonitor = null;
|
|
281
|
+
|
|
282
|
+
const getUpdatedStats = () => currentMonitor?.getUpdatedStats();
|
|
283
|
+
|
|
284
|
+
// Protocol enum used for the CLIENT (Make sure to keep it in sync with its server counterpart)
|
|
285
|
+
|
|
286
|
+
// Requests: messages from the client to the server
|
|
287
|
+
const PROTOCOL_REQUESTS = {
|
|
288
|
+
BLOCK_CLIENT: "block_client",
|
|
289
|
+
CLAIM_ROOM: "claim_room",
|
|
290
|
+
CLEAR_CHAT_HISTORY: "clear_chat_history",
|
|
291
|
+
ENABLE_AUDIO: "enable_audio",
|
|
292
|
+
ENABLE_VIDEO: "enable_video",
|
|
293
|
+
END_STREAM: "end_stream",
|
|
294
|
+
FETCH_MEDIASERVER_CONFIG: "fetch_mediaserver_config",
|
|
295
|
+
HANDLE_KNOCK: "handle_knock",
|
|
296
|
+
IDENTIFY_DEVICE: "identify_device",
|
|
297
|
+
INVITE_CLIENT_AS_MEMBER: "invite_client_as_member",
|
|
298
|
+
JOIN_ROOM: "join_room",
|
|
299
|
+
KICK_CLIENT: "kick_client",
|
|
300
|
+
KNOCK_ROOM: "knock_room",
|
|
301
|
+
LEAVE_ROOM: "leave_room",
|
|
302
|
+
SEND_CLIENT_METADATA: "send_client_metadata",
|
|
303
|
+
SET_LOCK: "set_lock",
|
|
304
|
+
SHARE_MEDIA: "share_media",
|
|
305
|
+
START_NEW_STREAM: "start_new_stream",
|
|
306
|
+
START_SCREENSHARE: "start_screenshare",
|
|
307
|
+
STOP_SCREENSHARE: "stop_screenshare",
|
|
308
|
+
START_URL_EMBED: "start_url_embed",
|
|
309
|
+
STOP_URL_EMBED: "stop_url_embed",
|
|
310
|
+
START_RECORDING: "start_recording",
|
|
311
|
+
STOP_RECORDING: "stop_recording",
|
|
312
|
+
SFU_TOKEN: "sfu_token",
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// Responses: messages from the server to the client, in response to requests
|
|
316
|
+
const PROTOCOL_RESPONSES = {
|
|
317
|
+
AUDIO_ENABLED: "audio_enabled",
|
|
318
|
+
BACKGROUND_IMAGE_CHANGED: "background_image_changed",
|
|
319
|
+
BLOCK_ADDED: "block_added",
|
|
320
|
+
BLOCK_REMOVED: "block_removed",
|
|
321
|
+
CHAT_HISTORY_CLEARED: "chat_history_cleared",
|
|
322
|
+
CLIENT_BLOCKED: "client_blocked",
|
|
323
|
+
CLIENT_INVITED_AS_MEMBER: "client_invited_as_member",
|
|
324
|
+
CLIENT_KICKED: "client_kicked",
|
|
325
|
+
CLIENT_LEFT: "client_left",
|
|
326
|
+
CLIENT_METADATA_RECEIVED: "client_metadata_received",
|
|
327
|
+
CLIENT_READY: "client_ready",
|
|
328
|
+
CLIENT_ROLE_CHANGED: "client_role_changed",
|
|
329
|
+
CLIENT_USER_ID_CHANGED: "client_user_id_changed",
|
|
330
|
+
CONTACTS_UPDATED: "contacts_updated",
|
|
331
|
+
DEVICE_IDENTIFIED: "device_identified",
|
|
332
|
+
ROOM_ROLES_UPDATED: "room_roles_updated",
|
|
333
|
+
KNOCK_HANDLED: "knock_handled",
|
|
334
|
+
KNOCK_PAGE_BACKGROUND_CHANGED: "knock_page_background_changed",
|
|
335
|
+
KNOCKER_LEFT: "knocker_left",
|
|
336
|
+
MEDIASERVER_CONFIG: "mediaserver_config",
|
|
337
|
+
MEDIA_SHARED: "media_shared",
|
|
338
|
+
MEMBER_INVITE: "member_invite",
|
|
339
|
+
NEW_CLIENT: "new_client",
|
|
340
|
+
NEW_STREAM_STARTED: "new_stream_started",
|
|
341
|
+
SCREENSHARE_STARTED: "screenshare_started",
|
|
342
|
+
SCREENSHARE_STOPPED: "screenshare_stopped",
|
|
343
|
+
OWNER_NOTIFIED: "owner_notified",
|
|
344
|
+
OWNERS_CHANGED: "owners_changed",
|
|
345
|
+
PLAY_CLIENT_STICKER: "play_client_sticker",
|
|
346
|
+
ROOM_INTEGRATION_ENABLED: "room_integration_enabled",
|
|
347
|
+
ROOM_INTEGRATION_DISABLED: "room_integration_disabled",
|
|
348
|
+
ROOM_JOINED: "room_joined",
|
|
349
|
+
ROOM_KNOCKED: "room_knocked",
|
|
350
|
+
ROOM_LEFT: "room_left",
|
|
351
|
+
ROOM_LOCKED: "room_locked",
|
|
352
|
+
ROOM_PERMISSIONS_CHANGED: "room_permissions_changed",
|
|
353
|
+
ROOM_LOGO_CHANGED: "room_logo_changed",
|
|
354
|
+
ROOM_TYPE_CHANGED: "room_type_changed",
|
|
355
|
+
ROOM_MODE_CHANGED: "room_mode_changed",
|
|
356
|
+
SOCKET_USER_ID_CHANGED: "socket_user_id_changed",
|
|
357
|
+
STICKERS_UNLOCKED: "stickers_unlocked",
|
|
358
|
+
STREAM_ENDED: "stream_ended",
|
|
359
|
+
URL_EMBED_STARTED: "url_embed_started",
|
|
360
|
+
URL_EMBED_STOPPED: "url_embed_stopped",
|
|
361
|
+
RECORDING_STARTED: "recording_started",
|
|
362
|
+
RECORDING_STOPPED: "recording_stopped",
|
|
363
|
+
USER_NOTIFIED: "user_notified",
|
|
364
|
+
VIDEO_ENABLED: "video_enabled",
|
|
365
|
+
CLIENT_UNABLE_TO_JOIN: "client_unable_to_join",
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// Relays: messages between clients, relayed through the server
|
|
369
|
+
const RELAY_MESSAGES = {
|
|
370
|
+
CHAT_MESSAGE: "chat_message",
|
|
371
|
+
CHAT_READ_STATE: "chat_read_state",
|
|
372
|
+
CHAT_STATE: "chat_state",
|
|
373
|
+
ICE_CANDIDATE: "ice_candidate",
|
|
374
|
+
ICE_END_OF_CANDIDATES: "ice_endofcandidates",
|
|
375
|
+
READY_TO_RECEIVE_OFFER: "ready_to_receive_offer",
|
|
376
|
+
REMOTE_CLIENT_MEDIA_REQUEST: "remote_client_media_request",
|
|
377
|
+
SDP_ANSWER: "sdp_answer",
|
|
378
|
+
SDP_OFFER: "sdp_offer",
|
|
379
|
+
VIDEO_STICKER: "video_sticker",
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// Events: something happened that we want to let the client know about
|
|
383
|
+
const PROTOCOL_EVENTS = {
|
|
384
|
+
PENDING_CLIENT_LEFT: "pending_client_left",
|
|
385
|
+
MEDIA_QUALITY_CHANGED: "media_quality_changed",
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const logger$9 = new Logger();
|
|
389
|
+
|
|
390
|
+
class ReconnectManager extends EventEmitter {
|
|
391
|
+
constructor(socket) {
|
|
392
|
+
super();
|
|
393
|
+
this._socket = socket;
|
|
394
|
+
this._clients = {};
|
|
395
|
+
this._signalDisconnectTime = undefined;
|
|
396
|
+
this.rtcManager = undefined;
|
|
397
|
+
this.metrics = {
|
|
398
|
+
roomJoinedLate: 0,
|
|
399
|
+
pendingClientCanceled: 0,
|
|
400
|
+
evaluationFailed: 0,
|
|
401
|
+
roomJoined: 0,
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
socket.on("disconnect", () => {
|
|
405
|
+
this._signalDisconnectTime = Date.now();
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// We intercept these events and take responsiblity for forwarding them
|
|
409
|
+
socket.on(PROTOCOL_RESPONSES.ROOM_JOINED, (payload) => this._onRoomJoined(payload));
|
|
410
|
+
socket.on(PROTOCOL_RESPONSES.NEW_CLIENT, (payload) => this._onNewClient(payload));
|
|
411
|
+
socket.on(PROTOCOL_RESPONSES.CLIENT_LEFT, (payload) => this._onClientLeft(payload));
|
|
412
|
+
|
|
413
|
+
// We intercept these events and handle them without forwarding them
|
|
414
|
+
socket.on(PROTOCOL_EVENTS.PENDING_CLIENT_LEFT, (payload) => this._onPendingClientLeft(payload));
|
|
415
|
+
|
|
416
|
+
// We gather information from these events but they will also be forwarded
|
|
417
|
+
socket.on(PROTOCOL_RESPONSES.AUDIO_ENABLED, (payload) => this._onAudioEnabled(payload));
|
|
418
|
+
socket.on(PROTOCOL_RESPONSES.VIDEO_ENABLED, (payload) => this._onVideoEnabled(payload));
|
|
419
|
+
socket.on(PROTOCOL_RESPONSES.SCREENSHARE_STARTED, (payload) => this._onScreenshareChanged(payload, true));
|
|
420
|
+
socket.on(PROTOCOL_RESPONSES.SCREENSHARE_STOPPED, (payload) => this._onScreenshareChanged(payload, false));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async _onRoomJoined(payload) {
|
|
424
|
+
// We might have gotten an error
|
|
425
|
+
if (!payload.room?.clients) {
|
|
426
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (!payload.selfId) {
|
|
431
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const myDeviceId = payload.room.clients.find((c) => payload.selfId === c.id)?.deviceId;
|
|
436
|
+
if (!myDeviceId) {
|
|
437
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Try to remove our own pending client if this is a page reload
|
|
442
|
+
// Could also be a first normal room_joined which can never be glitch-free
|
|
443
|
+
if (!this._signalDisconnectTime) {
|
|
444
|
+
this._resetClientState(payload);
|
|
445
|
+
payload.room.clients = payload.room.clients.filter(
|
|
446
|
+
(c) => !(c.deviceId === myDeviceId && c.isPendingToLeave)
|
|
447
|
+
);
|
|
448
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// The threshold for trying glitch-free reconnect should be less than server-side configuration
|
|
453
|
+
const RECONNECT_THRESHOLD = payload.disconnectTimeout * 0.8;
|
|
454
|
+
const timeSinceDisconnect = Date.now() - this._signalDisconnectTime;
|
|
455
|
+
if (timeSinceDisconnect > RECONNECT_THRESHOLD) {
|
|
456
|
+
this._resetClientState(payload);
|
|
457
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
458
|
+
this.metrics.roomJoinedLate++;
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// At this point we want to try to attempt glitch-free reconnection experience
|
|
463
|
+
|
|
464
|
+
// Filter out our own pending client after page reload
|
|
465
|
+
payload.room.clients = payload.room.clients.filter((c) => !(c.deviceId === myDeviceId && c.isPendingToLeave));
|
|
466
|
+
|
|
467
|
+
const allStats = await getUpdatedStats();
|
|
468
|
+
payload.room.clients.forEach((client) => {
|
|
469
|
+
try {
|
|
470
|
+
if (client.id === payload.selfId) return;
|
|
471
|
+
|
|
472
|
+
// Maybe add client to state
|
|
473
|
+
if (!this._clients[client.id]) {
|
|
474
|
+
this._addClientToState(client);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// Verify that rtcManager knows about the client
|
|
478
|
+
if (!this.rtcManager?.hasClient(client.id)) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Verify that the client state hasn't changed
|
|
483
|
+
if (
|
|
484
|
+
this._hasClientStateChanged({
|
|
485
|
+
clientId: client.id,
|
|
486
|
+
webcam: client.isVideoEnabled,
|
|
487
|
+
mic: client.isAudioEnabled,
|
|
488
|
+
screenShare: client.streams.length > 1,
|
|
489
|
+
})
|
|
490
|
+
) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (this._wasClientSendingMedia(client.id)) {
|
|
495
|
+
// Verify the client media is still flowing (not stopped from other end)
|
|
496
|
+
if (!this._isClientMediaActive(allStats, client.id)) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
client.mergeWithOldClientState = true;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
logger$9.error("Failed to evaluate if we should merge client state %o", error);
|
|
504
|
+
this.metrics.evaluationFailed++;
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// We will try to remove any remote client pending to leave
|
|
509
|
+
payload.room.clients.forEach((c) => {
|
|
510
|
+
if (c.isPendingToLeave) {
|
|
511
|
+
this._onPendingClientLeft({ clientId: c.id });
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
this.metrics.roomJoined++;
|
|
516
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
_onClientLeft(payload) {
|
|
520
|
+
const { clientId } = payload;
|
|
521
|
+
const client = this._clients[clientId];
|
|
522
|
+
|
|
523
|
+
// Remove client from state and clear timeout if client was pending to leave
|
|
524
|
+
if (client) {
|
|
525
|
+
clearTimeout(client.timeout);
|
|
526
|
+
delete this._clients[clientId];
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Old RTCManager only takes one argument, so rest is ignored.
|
|
530
|
+
this.rtcManager?.disconnect(clientId, /* activeBreakout */ null, payload.eventClaim);
|
|
531
|
+
|
|
532
|
+
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, payload);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
_onPendingClientLeft(payload) {
|
|
536
|
+
const { clientId } = payload;
|
|
537
|
+
const client = this._clients[clientId];
|
|
538
|
+
|
|
539
|
+
if (!client) {
|
|
540
|
+
logger$9.warn(`client ${clientId} not found`);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// We've already started the check below, don't do it again
|
|
545
|
+
if (client.isPendingToLeave) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
client.isPendingToLeave = true;
|
|
550
|
+
if (this._wasClientSendingMedia(clientId)) {
|
|
551
|
+
client.checkActiveMediaAttempts = 0;
|
|
552
|
+
this._abortIfNotActive(payload);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
_onNewClient(payload) {
|
|
557
|
+
const {
|
|
558
|
+
client: { id: clientId, deviceId },
|
|
559
|
+
} = payload;
|
|
560
|
+
|
|
561
|
+
const client = this._clients[clientId];
|
|
562
|
+
if (client && client.isPendingToLeave) {
|
|
563
|
+
clearTimeout(client.timeoutHandler);
|
|
564
|
+
client.isPendingToLeave = false;
|
|
565
|
+
this.metrics.pendingClientCanceled++;
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
this._getPendingClientsByDeviceId(deviceId).forEach((client) => {
|
|
570
|
+
clearTimeout(client.timeoutHandler);
|
|
571
|
+
client.isPendingToLeave = undefined;
|
|
572
|
+
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, { clientId: client.clientId });
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
this._addClientToState(payload.client);
|
|
576
|
+
this.emit(PROTOCOL_RESPONSES.NEW_CLIENT, payload);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Evaluate if we should send send client_left before getting it from signal-server
|
|
580
|
+
async _abortIfNotActive(payload) {
|
|
581
|
+
const { clientId } = payload;
|
|
582
|
+
|
|
583
|
+
let client = this._clients[clientId];
|
|
584
|
+
if (!client?.isPendingToLeave) return;
|
|
585
|
+
|
|
586
|
+
client.checkActiveMediaAttempts++;
|
|
587
|
+
if (client.checkActiveMediaAttempts > 3) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const stillActive = await this._checkIsActive(clientId);
|
|
592
|
+
if (stillActive) {
|
|
593
|
+
client.timeoutHandler = setTimeout(() => this._abortIfNotActive(payload), 500);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
client = this._clients[clientId];
|
|
598
|
+
if (client?.isPendingToLeave) {
|
|
599
|
+
clearTimeout(client.timeoutHandler);
|
|
600
|
+
delete this._clients[clientId];
|
|
601
|
+
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, payload);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Check if client is active
|
|
606
|
+
async _checkIsActive(clientId) {
|
|
607
|
+
const allStats = await getUpdatedStats();
|
|
608
|
+
return this._isClientMediaActive(allStats, clientId);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Check if client has bitrates for all tracks
|
|
612
|
+
_isClientMediaActive(stats, clientId) {
|
|
613
|
+
const clientStats = stats?.[clientId];
|
|
614
|
+
let isActive = false;
|
|
615
|
+
if (clientStats) {
|
|
616
|
+
Object.entries(clientStats.tracks).forEach(([trackId, trackStats]) => {
|
|
617
|
+
if (trackId !== "probator")
|
|
618
|
+
Object.values(trackStats.ssrcs).forEach((ssrcStats) => {
|
|
619
|
+
if ((ssrcStats.bitrate || 0) > 0) isActive = true;
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return isActive;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
_onAudioEnabled(payload) {
|
|
627
|
+
const { clientId, isAudioEnabled } = payload;
|
|
628
|
+
this._clients[clientId] = {
|
|
629
|
+
...(this._clients[clientId] || {}),
|
|
630
|
+
isAudioEnabled,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
_onVideoEnabled(payload) {
|
|
635
|
+
const { clientId, isVideoEnabled } = payload;
|
|
636
|
+
this._clients[clientId] = {
|
|
637
|
+
...(this._clients[clientId] || {}),
|
|
638
|
+
isVideoEnabled,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
_onScreenshareChanged(payload, action) {
|
|
643
|
+
const { clientId } = payload;
|
|
644
|
+
this._clients[clientId] = {
|
|
645
|
+
...(this._clients[clientId] || {}),
|
|
646
|
+
isScreenshareEnabled: action,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
_hasClientStateChanged({ clientId, webcam, mic, screenShare }) {
|
|
651
|
+
const state = this._clients[clientId];
|
|
652
|
+
|
|
653
|
+
if (!state) {
|
|
654
|
+
throw new Error(`Client ${clientId} not found in ReconnectManager state`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (webcam !== state.isVideoEnabled) {
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
if (mic !== state.isAudioEnabled) {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
if (screenShare !== state.isScreenshareEnabled) {
|
|
664
|
+
return true;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
_addClientToState(newClient) {
|
|
671
|
+
this._clients[newClient.id] = {
|
|
672
|
+
...(this._clients[newClient.id] || {}),
|
|
673
|
+
isAudioEnabled: newClient.isAudioEnabled,
|
|
674
|
+
isVideoEnabled: newClient.isVideoEnabled,
|
|
675
|
+
isScreenshareEnabled: newClient.streams.length > 1,
|
|
676
|
+
deviceId: newClient.deviceId,
|
|
677
|
+
isPendingToLeave: newClient.isPendingToLeave,
|
|
678
|
+
clientId: newClient.id,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
_wasClientSendingMedia(clientId) {
|
|
683
|
+
const client = this._clients[clientId];
|
|
684
|
+
|
|
685
|
+
if (!client) {
|
|
686
|
+
throw new Error(`Client ${clientId} not found in ReconnectManager state`);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return client.isAudioEnabled || client.isVideoEnabled || client.isScreenshareEnabled;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
_getPendingClientsByDeviceId(deviceId) {
|
|
693
|
+
return Object.values(this._clients).filter((clientState) => {
|
|
694
|
+
return clientState.deviceId === deviceId && clientState.isPendingToLeave;
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
_resetClientState(payload) {
|
|
699
|
+
this._clients = {};
|
|
700
|
+
payload.room.clients.forEach((client) => {
|
|
701
|
+
if (client.id === payload.selfId) {
|
|
702
|
+
return;
|
|
703
|
+
} else {
|
|
704
|
+
this._addClientToState(client);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
187
710
|
const DEFAULT_SOCKET_PATH = "/protocol/socket.io/v4";
|
|
188
711
|
|
|
712
|
+
const NOOP_KEEPALIVE_INTERVAL = 2000;
|
|
713
|
+
|
|
189
714
|
/**
|
|
190
715
|
* Wrapper class that extends the Socket.IO client library.
|
|
191
716
|
*/
|
|
192
717
|
class ServerSocket {
|
|
193
|
-
constructor(hostName, optionsOverrides) {
|
|
718
|
+
constructor(hostName, optionsOverrides, glitchFree) {
|
|
194
719
|
this._socket = io(hostName, {
|
|
195
720
|
path: DEFAULT_SOCKET_PATH,
|
|
196
721
|
randomizationFactor: 0.5,
|
|
@@ -216,14 +741,41 @@ class ServerSocket {
|
|
|
216
741
|
this._socket.io.opts.transports = ["websocket", "polling"];
|
|
217
742
|
}
|
|
218
743
|
});
|
|
744
|
+
|
|
745
|
+
if (glitchFree) this._reconnectManager = new ReconnectManager(this._socket);
|
|
746
|
+
|
|
219
747
|
this._socket.on("connect", () => {
|
|
220
748
|
const transport = this.getTransport();
|
|
221
749
|
if (transport === "websocket") {
|
|
222
750
|
this._wasConnectedUsingWebsocket = true;
|
|
751
|
+
|
|
752
|
+
// start noop keepalive loop to detect client side disconnects fast
|
|
753
|
+
if (!this.noopKeepaliveInterval)
|
|
754
|
+
this.noopKeepaliveInterval = setInterval(() => {
|
|
755
|
+
try {
|
|
756
|
+
// send a noop message if it thinks it is connected (might not be)
|
|
757
|
+
if (this._socket.connected) {
|
|
758
|
+
this._socket.io.engine.sendPacket("noop");
|
|
759
|
+
}
|
|
760
|
+
} catch (ex) {}
|
|
761
|
+
}, NOOP_KEEPALIVE_INTERVAL);
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
this._socket.on("disconnect", () => {
|
|
766
|
+
if (this.noopKeepaliveInterval) {
|
|
767
|
+
clearInterval(this.noopKeepaliveInterval);
|
|
768
|
+
this.noopKeepaliveInterval = null;
|
|
223
769
|
}
|
|
224
770
|
});
|
|
225
771
|
}
|
|
226
772
|
|
|
773
|
+
setRtcManager(rtcManager) {
|
|
774
|
+
if (this._reconnectManager) {
|
|
775
|
+
this._reconnectManager.rtcManager = rtcManager;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
227
779
|
connect() {
|
|
228
780
|
if (this.isConnected() || this.isConnecting()) {
|
|
229
781
|
return;
|
|
@@ -282,6 +834,17 @@ class ServerSocket {
|
|
|
282
834
|
* @returns {function} Function to deregister the listener.
|
|
283
835
|
*/
|
|
284
836
|
on(eventName, handler) {
|
|
837
|
+
const relayableEvents = [
|
|
838
|
+
PROTOCOL_RESPONSES.ROOM_JOINED,
|
|
839
|
+
PROTOCOL_RESPONSES.CLIENT_LEFT,
|
|
840
|
+
PROTOCOL_RESPONSES.NEW_CLIENT,
|
|
841
|
+
];
|
|
842
|
+
|
|
843
|
+
// Intercept certain events if glitch-free is enabled.
|
|
844
|
+
if (this._reconnectManager && relayableEvents.includes(eventName)) {
|
|
845
|
+
return this._interceptEvent(eventName, handler);
|
|
846
|
+
}
|
|
847
|
+
|
|
285
848
|
this._socket.on(eventName, handler);
|
|
286
849
|
|
|
287
850
|
return () => {
|
|
@@ -308,6 +871,23 @@ class ServerSocket {
|
|
|
308
871
|
off(eventName, handler) {
|
|
309
872
|
this._socket.off(eventName, handler);
|
|
310
873
|
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Intercept event and let ReconnectManager handle them.
|
|
877
|
+
*/
|
|
878
|
+
_interceptEvent(eventName, handler) {
|
|
879
|
+
if (this._reconnectManager) {
|
|
880
|
+
this._reconnectManager.on(eventName, handler);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return () => {
|
|
884
|
+
if (this._reconnectManager) this._reconnectManager.removeListener(eventName, handler);
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
getGlitchFreeMetrics() {
|
|
889
|
+
return this._reconnectManager?.metrics;
|
|
890
|
+
}
|
|
311
891
|
}
|
|
312
892
|
|
|
313
893
|
function forwardSocketEvents(socket, dispatch) {
|
|
@@ -537,6 +1117,7 @@ const parseResolution = (res) => res.split(/[^\d]/g).map((n) => parseInt(n, 10))
|
|
|
537
1117
|
function getMediaConstraints({
|
|
538
1118
|
disableAEC,
|
|
539
1119
|
disableAGC,
|
|
1120
|
+
fps24,
|
|
540
1121
|
hd,
|
|
541
1122
|
lax,
|
|
542
1123
|
lowDataMode,
|
|
@@ -547,7 +1128,7 @@ function getMediaConstraints({
|
|
|
547
1128
|
}) {
|
|
548
1129
|
let HIGH_HEIGHT = 480;
|
|
549
1130
|
let LOW_HEIGHT = 240;
|
|
550
|
-
let
|
|
1131
|
+
let NON_STANDARD_FPS = 0;
|
|
551
1132
|
|
|
552
1133
|
if (hd) {
|
|
553
1134
|
// respect user choice, but default to HD for pro, and SD for free
|
|
@@ -560,15 +1141,20 @@ function getMediaConstraints({
|
|
|
560
1141
|
} else {
|
|
561
1142
|
LOW_HEIGHT = 360;
|
|
562
1143
|
}
|
|
563
|
-
LOW_FPS = 30; // we still use 30fps because of assumptions about temporal layers
|
|
564
1144
|
}
|
|
565
1145
|
|
|
1146
|
+
// Set framerate to 24 to increase quality/bandwidth
|
|
1147
|
+
if (fps24) NON_STANDARD_FPS = 24;
|
|
1148
|
+
|
|
1149
|
+
// Set framerate for low data, but only for non-simulcast
|
|
1150
|
+
if (lowDataMode && !simulcast) NON_STANDARD_FPS = 15;
|
|
1151
|
+
|
|
566
1152
|
const constraints = {
|
|
567
1153
|
audio: { ...(preferredDeviceIds.audioId && { deviceId: preferredDeviceIds.audioId }) },
|
|
568
1154
|
video: {
|
|
569
1155
|
...(preferredDeviceIds.videoId ? { deviceId: preferredDeviceIds.videoId } : { facingMode: "user" }),
|
|
570
1156
|
height: lowDataMode ? LOW_HEIGHT : HIGH_HEIGHT,
|
|
571
|
-
...(
|
|
1157
|
+
...(NON_STANDARD_FPS && { frameRate: NON_STANDARD_FPS }),
|
|
572
1158
|
},
|
|
573
1159
|
};
|
|
574
1160
|
if (lax) {
|
|
@@ -701,6 +1287,8 @@ var assert_1 = assert;
|
|
|
701
1287
|
|
|
702
1288
|
var assert$1 = /*@__PURE__*/getDefaultExportFromCjs(assert_1);
|
|
703
1289
|
|
|
1290
|
+
const logger$8 = new Logger();
|
|
1291
|
+
|
|
704
1292
|
const isMobile = /mobi/i.test(navigator.userAgent);
|
|
705
1293
|
|
|
706
1294
|
class NoDevicesError extends Error {
|
|
@@ -720,7 +1308,7 @@ function getUserMedia(constraints) {
|
|
|
720
1308
|
|
|
721
1309
|
return navigator.mediaDevices.getUserMedia(constraints).catch((error) => {
|
|
722
1310
|
const message = `${error}, ${JSON.stringify(constraints, null, 2)}`;
|
|
723
|
-
|
|
1311
|
+
logger$8.error(`getUserMedia ${message}`);
|
|
724
1312
|
throw error;
|
|
725
1313
|
});
|
|
726
1314
|
}
|
|
@@ -906,7 +1494,7 @@ async function getStream(constraintOpt, { replaceStream, fallback = true } = {})
|
|
|
906
1494
|
getConstraints({ ...constraintOpt, options: { ...constraintOpt.options, lax: true } })
|
|
907
1495
|
);
|
|
908
1496
|
} catch (e2) {
|
|
909
|
-
|
|
1497
|
+
logger$8.warn(`Tried getting stream again with laxer constraints, but failed: ${"" + e2}`);
|
|
910
1498
|
}
|
|
911
1499
|
// Message often hints at which was the problem, let's use that
|
|
912
1500
|
const errMsg = ("" + e).toLowerCase();
|
|
@@ -915,7 +1503,7 @@ async function getStream(constraintOpt, { replaceStream, fallback = true } = {})
|
|
|
915
1503
|
try {
|
|
916
1504
|
stream = await getUserMedia(getConstraints({ ...constraintOpt, [problemWith]: null }));
|
|
917
1505
|
} catch (e2) {
|
|
918
|
-
|
|
1506
|
+
logger$8.warn(`Re-tried ${problemWith} with no constraints, but failed: ${"" + e2}`);
|
|
919
1507
|
}
|
|
920
1508
|
}
|
|
921
1509
|
if (!stream) {
|
|
@@ -926,7 +1514,7 @@ async function getStream(constraintOpt, { replaceStream, fallback = true } = {})
|
|
|
926
1514
|
try {
|
|
927
1515
|
stream = await getUserMedia(getConstraints({ ...constraintOpt, [kind]: false }));
|
|
928
1516
|
} catch (e2) {
|
|
929
|
-
|
|
1517
|
+
logger$8.warn(`Re-tried without ${kind}, but failed: ${"" + e2}`);
|
|
930
1518
|
}
|
|
931
1519
|
if (stream) break;
|
|
932
1520
|
}
|
|
@@ -1978,163 +2566,65 @@ const doConnectRoom = createAppThunk(() => (dispatch, getState) => {
|
|
|
1978
2566
|
organizationId,
|
|
1979
2567
|
roomKey,
|
|
1980
2568
|
roomName,
|
|
1981
|
-
selfId,
|
|
1982
|
-
userAgent: `browser-sdk:${sdkVersion || "unknown"}`,
|
|
1983
|
-
externalId,
|
|
1984
|
-
});
|
|
1985
|
-
dispatch(connectionStatusChanged("connecting"));
|
|
1986
|
-
});
|
|
1987
|
-
const selectRoomConnectionRaw = (state) => state.roomConnection;
|
|
1988
|
-
const selectRoomConnectionSession = (state) => state.roomConnection.session;
|
|
1989
|
-
const selectRoomConnectionSessionId = (state) => { var _a; return (_a = state.roomConnection.session) === null || _a === void 0 ? void 0 : _a.id; };
|
|
1990
|
-
const selectRoomConnectionStatus = (state) => state.roomConnection.status;
|
|
1991
|
-
const selectShouldConnectRoom = createSelector([selectOrganizationId, selectRoomConnectionStatus, selectSignalConnectionDeviceIdentified, selectLocalMediaStatus], (hasOrganizationIdFetched, roomConnectionStatus, signalConnectionDeviceIdentified, localMediaStatus) => {
|
|
1992
|
-
if (localMediaStatus === "started" &&
|
|
1993
|
-
signalConnectionDeviceIdentified &&
|
|
1994
|
-
!!hasOrganizationIdFetched &&
|
|
1995
|
-
["initializing", "reconnect"].includes(roomConnectionStatus)) {
|
|
1996
|
-
return true;
|
|
1997
|
-
}
|
|
1998
|
-
return false;
|
|
1999
|
-
});
|
|
2000
|
-
createReactor([selectShouldConnectRoom], ({ dispatch }, shouldConnectRoom) => {
|
|
2001
|
-
if (shouldConnectRoom) {
|
|
2002
|
-
dispatch(doConnectRoom());
|
|
2003
|
-
}
|
|
2004
|
-
});
|
|
2005
|
-
startAppListening({
|
|
2006
|
-
actionCreator: signalEvents.knockHandled,
|
|
2007
|
-
effect: ({ payload }, { dispatch, getState }) => {
|
|
2008
|
-
const { clientId, resolution } = payload;
|
|
2009
|
-
const state = getState();
|
|
2010
|
-
const selfId = selectSelfId(state);
|
|
2011
|
-
if (clientId !== selfId) {
|
|
2012
|
-
return;
|
|
2013
|
-
}
|
|
2014
|
-
if (resolution === "accepted") {
|
|
2015
|
-
dispatch(setRoomKey(payload.metadata.roomKey));
|
|
2016
|
-
dispatch(doConnectRoom());
|
|
2017
|
-
}
|
|
2018
|
-
else if (resolution === "rejected") {
|
|
2019
|
-
dispatch(connectionStatusChanged("knock_rejected"));
|
|
2020
|
-
}
|
|
2021
|
-
},
|
|
2022
|
-
});
|
|
2023
|
-
|
|
2024
|
-
const EVENTS = {
|
|
2025
|
-
CLIENT_CONNECTION_STATUS_CHANGED: "client_connection_status_changed",
|
|
2026
|
-
STREAM_ADDED: "stream_added",
|
|
2027
|
-
RTC_MANAGER_CREATED: "rtc_manager_created",
|
|
2028
|
-
RTC_MANAGER_DESTROYED: "rtc_manager_destroyed",
|
|
2029
|
-
LOCAL_STREAM_TRACK_ADDED: "local_stream_track_added",
|
|
2030
|
-
LOCAL_STREAM_TRACK_REMOVED: "local_stream_track_removed",
|
|
2031
|
-
REMOTE_STREAM_TRACK_ADDED: "remote_stream_track_added",
|
|
2032
|
-
REMOTE_STREAM_TRACK_REMOVED: "remote_stream_track_removed",
|
|
2033
|
-
};
|
|
2034
|
-
|
|
2035
|
-
const TYPES = {
|
|
2036
|
-
CONNECTING: "connecting",
|
|
2037
|
-
CONNECTION_FAILED: "connection_failed",
|
|
2038
|
-
CONNECTION_SUCCESSFUL: "connection_successful",
|
|
2039
|
-
CONNECTION_DISCONNECTED: "connection_disconnected",
|
|
2040
|
-
};
|
|
2041
|
-
|
|
2042
|
-
// Protocol enum used for the CLIENT (Make sure to keep it in sync with its server counterpart)
|
|
2043
|
-
|
|
2044
|
-
// Requests: messages from the client to the server
|
|
2045
|
-
const PROTOCOL_REQUESTS = {
|
|
2046
|
-
BLOCK_CLIENT: "block_client",
|
|
2047
|
-
CLAIM_ROOM: "claim_room",
|
|
2048
|
-
CLEAR_CHAT_HISTORY: "clear_chat_history",
|
|
2049
|
-
ENABLE_AUDIO: "enable_audio",
|
|
2050
|
-
ENABLE_VIDEO: "enable_video",
|
|
2051
|
-
END_STREAM: "end_stream",
|
|
2052
|
-
FETCH_MEDIASERVER_CONFIG: "fetch_mediaserver_config",
|
|
2053
|
-
HANDLE_KNOCK: "handle_knock",
|
|
2054
|
-
IDENTIFY_DEVICE: "identify_device",
|
|
2055
|
-
INVITE_CLIENT_AS_MEMBER: "invite_client_as_member",
|
|
2056
|
-
JOIN_ROOM: "join_room",
|
|
2057
|
-
KICK_CLIENT: "kick_client",
|
|
2058
|
-
KNOCK_ROOM: "knock_room",
|
|
2059
|
-
LEAVE_ROOM: "leave_room",
|
|
2060
|
-
SEND_CLIENT_METADATA: "send_client_metadata",
|
|
2061
|
-
SET_LOCK: "set_lock",
|
|
2062
|
-
SHARE_MEDIA: "share_media",
|
|
2063
|
-
START_NEW_STREAM: "start_new_stream",
|
|
2064
|
-
START_SCREENSHARE: "start_screenshare",
|
|
2065
|
-
STOP_SCREENSHARE: "stop_screenshare",
|
|
2066
|
-
START_URL_EMBED: "start_url_embed",
|
|
2067
|
-
STOP_URL_EMBED: "stop_url_embed",
|
|
2068
|
-
START_RECORDING: "start_recording",
|
|
2069
|
-
STOP_RECORDING: "stop_recording",
|
|
2070
|
-
SFU_TOKEN: "sfu_token",
|
|
2071
|
-
};
|
|
2072
|
-
|
|
2073
|
-
// Responses: messages from the server to the client, in response to requests
|
|
2074
|
-
const PROTOCOL_RESPONSES = {
|
|
2075
|
-
AUDIO_ENABLED: "audio_enabled",
|
|
2076
|
-
BACKGROUND_IMAGE_CHANGED: "background_image_changed",
|
|
2077
|
-
BLOCK_ADDED: "block_added",
|
|
2078
|
-
BLOCK_REMOVED: "block_removed",
|
|
2079
|
-
CHAT_HISTORY_CLEARED: "chat_history_cleared",
|
|
2080
|
-
CLIENT_BLOCKED: "client_blocked",
|
|
2081
|
-
CLIENT_INVITED_AS_MEMBER: "client_invited_as_member",
|
|
2082
|
-
CLIENT_KICKED: "client_kicked",
|
|
2083
|
-
CLIENT_LEFT: "client_left",
|
|
2084
|
-
CLIENT_METADATA_RECEIVED: "client_metadata_received",
|
|
2085
|
-
CLIENT_READY: "client_ready",
|
|
2086
|
-
CLIENT_ROLE_CHANGED: "client_role_changed",
|
|
2087
|
-
CLIENT_USER_ID_CHANGED: "client_user_id_changed",
|
|
2088
|
-
CONTACTS_UPDATED: "contacts_updated",
|
|
2089
|
-
DEVICE_IDENTIFIED: "device_identified",
|
|
2090
|
-
ROOM_ROLES_UPDATED: "room_roles_updated",
|
|
2091
|
-
KNOCK_HANDLED: "knock_handled",
|
|
2092
|
-
KNOCK_PAGE_BACKGROUND_CHANGED: "knock_page_background_changed",
|
|
2093
|
-
KNOCKER_LEFT: "knocker_left",
|
|
2094
|
-
MEDIASERVER_CONFIG: "mediaserver_config",
|
|
2095
|
-
MEDIA_SHARED: "media_shared",
|
|
2096
|
-
MEMBER_INVITE: "member_invite",
|
|
2097
|
-
NEW_CLIENT: "new_client",
|
|
2098
|
-
NEW_STREAM_STARTED: "new_stream_started",
|
|
2099
|
-
SCREENSHARE_STARTED: "screenshare_started",
|
|
2100
|
-
SCREENSHARE_STOPPED: "screenshare_stopped",
|
|
2101
|
-
OWNER_NOTIFIED: "owner_notified",
|
|
2102
|
-
OWNERS_CHANGED: "owners_changed",
|
|
2103
|
-
PLAY_CLIENT_STICKER: "play_client_sticker",
|
|
2104
|
-
ROOM_INTEGRATION_ENABLED: "room_integration_enabled",
|
|
2105
|
-
ROOM_INTEGRATION_DISABLED: "room_integration_disabled",
|
|
2106
|
-
ROOM_JOINED: "room_joined",
|
|
2107
|
-
ROOM_KNOCKED: "room_knocked",
|
|
2108
|
-
ROOM_LEFT: "room_left",
|
|
2109
|
-
ROOM_LOCKED: "room_locked",
|
|
2110
|
-
ROOM_PERMISSIONS_CHANGED: "room_permissions_changed",
|
|
2111
|
-
ROOM_LOGO_CHANGED: "room_logo_changed",
|
|
2112
|
-
ROOM_TYPE_CHANGED: "room_type_changed",
|
|
2113
|
-
ROOM_MODE_CHANGED: "room_mode_changed",
|
|
2114
|
-
SOCKET_USER_ID_CHANGED: "socket_user_id_changed",
|
|
2115
|
-
STICKERS_UNLOCKED: "stickers_unlocked",
|
|
2116
|
-
STREAM_ENDED: "stream_ended",
|
|
2117
|
-
URL_EMBED_STARTED: "url_embed_started",
|
|
2118
|
-
URL_EMBED_STOPPED: "url_embed_stopped",
|
|
2119
|
-
RECORDING_STARTED: "recording_started",
|
|
2120
|
-
RECORDING_STOPPED: "recording_stopped",
|
|
2121
|
-
USER_NOTIFIED: "user_notified",
|
|
2122
|
-
VIDEO_ENABLED: "video_enabled",
|
|
2123
|
-
CLIENT_UNABLE_TO_JOIN: "client_unable_to_join",
|
|
2569
|
+
selfId,
|
|
2570
|
+
userAgent: `browser-sdk:${sdkVersion || "unknown"}`,
|
|
2571
|
+
externalId,
|
|
2572
|
+
});
|
|
2573
|
+
dispatch(connectionStatusChanged("connecting"));
|
|
2574
|
+
});
|
|
2575
|
+
const selectRoomConnectionRaw = (state) => state.roomConnection;
|
|
2576
|
+
const selectRoomConnectionSession = (state) => state.roomConnection.session;
|
|
2577
|
+
const selectRoomConnectionSessionId = (state) => { var _a; return (_a = state.roomConnection.session) === null || _a === void 0 ? void 0 : _a.id; };
|
|
2578
|
+
const selectRoomConnectionStatus = (state) => state.roomConnection.status;
|
|
2579
|
+
const selectShouldConnectRoom = createSelector([selectOrganizationId, selectRoomConnectionStatus, selectSignalConnectionDeviceIdentified, selectLocalMediaStatus], (hasOrganizationIdFetched, roomConnectionStatus, signalConnectionDeviceIdentified, localMediaStatus) => {
|
|
2580
|
+
if (localMediaStatus === "started" &&
|
|
2581
|
+
signalConnectionDeviceIdentified &&
|
|
2582
|
+
!!hasOrganizationIdFetched &&
|
|
2583
|
+
["initializing", "reconnect"].includes(roomConnectionStatus)) {
|
|
2584
|
+
return true;
|
|
2585
|
+
}
|
|
2586
|
+
return false;
|
|
2587
|
+
});
|
|
2588
|
+
createReactor([selectShouldConnectRoom], ({ dispatch }, shouldConnectRoom) => {
|
|
2589
|
+
if (shouldConnectRoom) {
|
|
2590
|
+
dispatch(doConnectRoom());
|
|
2591
|
+
}
|
|
2592
|
+
});
|
|
2593
|
+
startAppListening({
|
|
2594
|
+
actionCreator: signalEvents.knockHandled,
|
|
2595
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2596
|
+
const { clientId, resolution } = payload;
|
|
2597
|
+
const state = getState();
|
|
2598
|
+
const selfId = selectSelfId(state);
|
|
2599
|
+
if (clientId !== selfId) {
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
if (resolution === "accepted") {
|
|
2603
|
+
dispatch(setRoomKey(payload.metadata.roomKey));
|
|
2604
|
+
dispatch(doConnectRoom());
|
|
2605
|
+
}
|
|
2606
|
+
else if (resolution === "rejected") {
|
|
2607
|
+
dispatch(connectionStatusChanged("knock_rejected"));
|
|
2608
|
+
}
|
|
2609
|
+
},
|
|
2610
|
+
});
|
|
2611
|
+
|
|
2612
|
+
const EVENTS = {
|
|
2613
|
+
CLIENT_CONNECTION_STATUS_CHANGED: "client_connection_status_changed",
|
|
2614
|
+
STREAM_ADDED: "stream_added",
|
|
2615
|
+
RTC_MANAGER_CREATED: "rtc_manager_created",
|
|
2616
|
+
RTC_MANAGER_DESTROYED: "rtc_manager_destroyed",
|
|
2617
|
+
LOCAL_STREAM_TRACK_ADDED: "local_stream_track_added",
|
|
2618
|
+
LOCAL_STREAM_TRACK_REMOVED: "local_stream_track_removed",
|
|
2619
|
+
REMOTE_STREAM_TRACK_ADDED: "remote_stream_track_added",
|
|
2620
|
+
REMOTE_STREAM_TRACK_REMOVED: "remote_stream_track_removed",
|
|
2124
2621
|
};
|
|
2125
2622
|
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
ICE_CANDIDATE: "ice_candidate",
|
|
2132
|
-
ICE_END_OF_CANDIDATES: "ice_endofcandidates",
|
|
2133
|
-
READY_TO_RECEIVE_OFFER: "ready_to_receive_offer",
|
|
2134
|
-
REMOTE_CLIENT_MEDIA_REQUEST: "remote_client_media_request",
|
|
2135
|
-
SDP_ANSWER: "sdp_answer",
|
|
2136
|
-
SDP_OFFER: "sdp_offer",
|
|
2137
|
-
VIDEO_STICKER: "video_sticker",
|
|
2623
|
+
const TYPES = {
|
|
2624
|
+
CONNECTING: "connecting",
|
|
2625
|
+
CONNECTION_FAILED: "connection_failed",
|
|
2626
|
+
CONNECTION_SUCCESSFUL: "connection_successful",
|
|
2627
|
+
CONNECTION_DISCONNECTED: "connection_disconnected",
|
|
2138
2628
|
};
|
|
2139
2629
|
|
|
2140
2630
|
const CAMERA_STREAM_ID$2 = "0";
|
|
@@ -2250,13 +2740,22 @@ function detectMicrophoneNotWorking(pc) {
|
|
|
2250
2740
|
var rtcManagerEvents = {
|
|
2251
2741
|
CAMERA_NOT_WORKING: "camera_not_working",
|
|
2252
2742
|
CONNECTION_BLOCKED_BY_NETWORK: "connection_blocked_by_network",
|
|
2743
|
+
ICE_IPV6_SEEN: "ice_ipv6_seen",
|
|
2744
|
+
ICE_MDNS_SEEN: "ice_mdns_seen",
|
|
2745
|
+
ICE_NO_PUBLIC_IP_GATHERED: "ice_no_public_ip_gathered",
|
|
2746
|
+
ICE_NO_PUBLIC_IP_GATHERED_3SEC: "ice_no_public_ip_gathered_3sec",
|
|
2747
|
+
ICE_RESTART: "ice_restart",
|
|
2253
2748
|
MICROPHONE_NOT_WORKING: "microphone_not_working",
|
|
2254
2749
|
MICROPHONE_STOPPED_WORKING: "microphone_stopped_working",
|
|
2750
|
+
NEW_PC: "new_pc",
|
|
2751
|
+
SFU_CONNECTION_OPEN: "sfu_connection_open",
|
|
2255
2752
|
SFU_CONNECTION_CLOSED: "sfu_connection_closed",
|
|
2256
2753
|
COLOCATION_SPEAKER: "colocation_speaker",
|
|
2257
2754
|
DOMINANT_SPEAKER: "dominant_speaker",
|
|
2258
2755
|
};
|
|
2259
2756
|
|
|
2757
|
+
const logger$7 = new Logger();
|
|
2758
|
+
|
|
2260
2759
|
const browserName$3 = adapter.browserDetails.browser;
|
|
2261
2760
|
const browserVersion$1 = adapter.browserDetails.version;
|
|
2262
2761
|
|
|
@@ -2298,8 +2797,7 @@ function setCodecPreferenceSDP(sdp, vp9On, redOn) {
|
|
|
2298
2797
|
const newSdp = sdpTransform.write(sdpObject);
|
|
2299
2798
|
return newSdp;
|
|
2300
2799
|
} catch (error) {
|
|
2301
|
-
|
|
2302
|
-
console.log("setCodecPreferenceSDP error:", error);
|
|
2800
|
+
logger$7.error("setCodecPreferenceSDP error:", error);
|
|
2303
2801
|
}
|
|
2304
2802
|
}
|
|
2305
2803
|
|
|
@@ -2446,10 +2944,19 @@ const MAXIMUM_TURN_BANDWIDTH_UNLIMITED = -1; // kbps;
|
|
|
2446
2944
|
|
|
2447
2945
|
const MEDIA_JITTER_BUFFER_TARGET = 400; // milliseconds;
|
|
2448
2946
|
|
|
2947
|
+
const logger$6 = new Logger();
|
|
2948
|
+
|
|
2449
2949
|
class Session {
|
|
2450
|
-
constructor({ peerConnectionId, bandwidth, maximumTurnBandwidth, deprioritizeH264Encoding
|
|
2950
|
+
constructor({ peerConnectionId, bandwidth, maximumTurnBandwidth, deprioritizeH264Encoding }) {
|
|
2451
2951
|
this.peerConnectionId = peerConnectionId;
|
|
2452
2952
|
this.relayCandidateSeen = false;
|
|
2953
|
+
this.serverReflexiveCandidateSeen = false;
|
|
2954
|
+
this.publicHostCandidateSeen = false;
|
|
2955
|
+
this.ipv6HostCandidateSeen = false;
|
|
2956
|
+
this.ipv6HostCandidateTeredoSeen = false;
|
|
2957
|
+
this.ipv6HostCandidate6to4Seen = false;
|
|
2958
|
+
this.mdnsHostCandidateSeen = false;
|
|
2959
|
+
|
|
2453
2960
|
this.pc = null;
|
|
2454
2961
|
this.wasEverConnected = false;
|
|
2455
2962
|
this.connectionStatus = null;
|
|
@@ -2469,7 +2976,6 @@ class Session {
|
|
|
2469
2976
|
});
|
|
2470
2977
|
this.offerOptions = { offerToReceiveAudio: true, offerToReceiveVideo: true };
|
|
2471
2978
|
this._deprioritizeH264Encoding = deprioritizeH264Encoding;
|
|
2472
|
-
this._logger = logger;
|
|
2473
2979
|
}
|
|
2474
2980
|
|
|
2475
2981
|
setAndGetPeerConnection({ clientId, constraints, peerConnectionConfig, shouldAddLocalVideo }) {
|
|
@@ -2613,7 +3119,7 @@ class Session {
|
|
|
2613
3119
|
return setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
2614
3120
|
},
|
|
2615
3121
|
(e) => {
|
|
2616
|
-
|
|
3122
|
+
logger$6.warn("Could not set remote description from remote answer: ", e);
|
|
2617
3123
|
}
|
|
2618
3124
|
);
|
|
2619
3125
|
}
|
|
@@ -2634,7 +3140,7 @@ class Session {
|
|
|
2634
3140
|
return;
|
|
2635
3141
|
}
|
|
2636
3142
|
this.pc.addIceCandidate(candidate).catch((e) => {
|
|
2637
|
-
|
|
3143
|
+
logger$6.warn("Failed to add ICE candidate ('%s'): %s", candidate ? candidate.candidate : null, e);
|
|
2638
3144
|
});
|
|
2639
3145
|
});
|
|
2640
3146
|
}
|
|
@@ -2656,7 +3162,7 @@ class Session {
|
|
|
2656
3162
|
// do not handle state change events when we close the connection explicitly
|
|
2657
3163
|
pc.close();
|
|
2658
3164
|
} catch (e) {
|
|
2659
|
-
|
|
3165
|
+
logger$6.warn("failures during close of session", e);
|
|
2660
3166
|
// we're not interested in errors from RTCPeerConnection.close()
|
|
2661
3167
|
}
|
|
2662
3168
|
}
|
|
@@ -2672,7 +3178,7 @@ class Session {
|
|
|
2672
3178
|
const senders = pc.getSenders();
|
|
2673
3179
|
function dbg(msg) {
|
|
2674
3180
|
const tr = (t) => t && `id:${t.id},kind:${t.kind},state:${t.readyState}`;
|
|
2675
|
-
|
|
3181
|
+
logger$6.warn(
|
|
2676
3182
|
`${msg}. newTrack:${tr(newTrack)}, oldTrack:${tr(oldTrack)}, sender tracks: ${JSON.stringify(
|
|
2677
3183
|
senders.map((s) => `s ${tr(s.track)}`)
|
|
2678
3184
|
)}, sender first codecs: ${JSON.stringify(senders.map((s) => (s.getParameters().codecs || [])[0]))}`
|
|
@@ -2826,6 +3332,21 @@ class Session {
|
|
|
2826
3332
|
|
|
2827
3333
|
setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
2828
3334
|
}
|
|
3335
|
+
|
|
3336
|
+
setAudioOnly(enable, excludedTrackIds = []) {
|
|
3337
|
+
this.pc
|
|
3338
|
+
.getTransceivers()
|
|
3339
|
+
.filter(
|
|
3340
|
+
(videoTransceiver) =>
|
|
3341
|
+
videoTransceiver?.direction !== "recvonly" &&
|
|
3342
|
+
videoTransceiver?.receiver?.track?.kind === "video" &&
|
|
3343
|
+
!excludedTrackIds.includes(videoTransceiver?.receiver?.track?.id) &&
|
|
3344
|
+
!excludedTrackIds.includes(videoTransceiver?.sender?.track?.id)
|
|
3345
|
+
)
|
|
3346
|
+
.forEach((videoTransceiver) => {
|
|
3347
|
+
videoTransceiver.direction = enable ? "sendonly" : "sendrecv";
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
2829
3350
|
}
|
|
2830
3351
|
|
|
2831
3352
|
// transforms a maplike to an object. Mostly for getStats +
|
|
@@ -2924,6 +3445,8 @@ var rtcstats = function(trace, getStatsInterval, prefixesToWrap) {
|
|
|
2924
3445
|
var peerconnectioncounter = 0;
|
|
2925
3446
|
var isFirefox = !!window.mozRTCPeerConnection;
|
|
2926
3447
|
var isEdge = !!window.RTCIceGatherer;
|
|
3448
|
+
var prevById = {};
|
|
3449
|
+
|
|
2927
3450
|
prefixesToWrap.forEach(function(prefix) {
|
|
2928
3451
|
if (!window[prefix + 'RTCPeerConnection']) {
|
|
2929
3452
|
return;
|
|
@@ -2994,13 +3517,12 @@ var rtcstats = function(trace, getStatsInterval, prefixesToWrap) {
|
|
|
2994
3517
|
trace('ondatachannel', id, [event.channel.id, event.channel.label]);
|
|
2995
3518
|
});
|
|
2996
3519
|
|
|
2997
|
-
var prev = {};
|
|
2998
3520
|
var getStats = function() {
|
|
2999
3521
|
pc.getStats(null).then(function(res) {
|
|
3000
3522
|
var now = map2obj(res);
|
|
3001
3523
|
var base = JSON.parse(JSON.stringify(now)); // our new prev
|
|
3002
|
-
trace('getstats', id, deltaCompression(
|
|
3003
|
-
|
|
3524
|
+
trace('getstats', id, deltaCompression(prevById[id] || {}, now));
|
|
3525
|
+
prevById[id] = base;
|
|
3004
3526
|
});
|
|
3005
3527
|
};
|
|
3006
3528
|
// TODO: do we want one big interval and all peerconnections
|
|
@@ -3220,6 +3742,13 @@ var rtcstats = function(trace, getStatsInterval, prefixesToWrap) {
|
|
|
3220
3742
|
}
|
|
3221
3743
|
});
|
|
3222
3744
|
*/
|
|
3745
|
+
|
|
3746
|
+
return {
|
|
3747
|
+
resetDelta() {
|
|
3748
|
+
prevById = {};
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
|
|
3223
3752
|
};
|
|
3224
3753
|
|
|
3225
3754
|
var rtcstats$1 = /*@__PURE__*/getDefaultExportFromCjs(rtcstats);
|
|
@@ -3228,11 +3757,18 @@ var rtcstats$1 = /*@__PURE__*/getDefaultExportFromCjs(rtcstats);
|
|
|
3228
3757
|
|
|
3229
3758
|
const RTCSTATS_PROTOCOL_VERSION = "1.0";
|
|
3230
3759
|
|
|
3760
|
+
// when not connected we need to buffer at least a few getstats reports
|
|
3761
|
+
// as they are delta compressed and we need the initial properties
|
|
3762
|
+
const GETSTATS_BUFFER_SIZE = 20;
|
|
3763
|
+
|
|
3231
3764
|
const clientInfo = {
|
|
3232
|
-
id: v4(), // shared id across rtcstats reconnects
|
|
3765
|
+
id: v4$1(), // shared id across rtcstats reconnects
|
|
3233
3766
|
connectionNumber: 0,
|
|
3234
3767
|
};
|
|
3235
3768
|
|
|
3769
|
+
const noop = () => {};
|
|
3770
|
+
let resetDelta = noop;
|
|
3771
|
+
|
|
3236
3772
|
// Inlined version of rtcstats/trace-ws with improved disconnect handling.
|
|
3237
3773
|
function rtcStatsConnection(wsURL, logger = console) {
|
|
3238
3774
|
const buffer = [];
|
|
@@ -3245,6 +3781,7 @@ function rtcStatsConnection(wsURL, logger = console) {
|
|
|
3245
3781
|
let connectionShouldBeOpen;
|
|
3246
3782
|
let connectionAttempt = 0;
|
|
3247
3783
|
let hasPassedOnRoomSessionId = false;
|
|
3784
|
+
let getStatsBufferUsed = 0;
|
|
3248
3785
|
|
|
3249
3786
|
const connection = {
|
|
3250
3787
|
connected: false,
|
|
@@ -3282,8 +3819,15 @@ function rtcStatsConnection(wsURL, logger = console) {
|
|
|
3282
3819
|
if (ws.readyState === WebSocket.OPEN) {
|
|
3283
3820
|
connectionAttempt = 0;
|
|
3284
3821
|
ws.send(JSON.stringify(args));
|
|
3285
|
-
} else if (args[0]
|
|
3286
|
-
// buffer
|
|
3822
|
+
} else if (args[0] === "getstats") {
|
|
3823
|
+
// only buffer getStats for a while
|
|
3824
|
+
// we don't want this to pile up, but we need at least the initial reports
|
|
3825
|
+
if (getStatsBufferUsed < GETSTATS_BUFFER_SIZE) {
|
|
3826
|
+
getStatsBufferUsed++;
|
|
3827
|
+
buffer.push(args);
|
|
3828
|
+
}
|
|
3829
|
+
} else if (args[0] === "customEvent" && args[2].type === "insightsStats") ; else {
|
|
3830
|
+
// buffer everything else
|
|
3287
3831
|
buffer.push(args);
|
|
3288
3832
|
}
|
|
3289
3833
|
|
|
@@ -3318,6 +3862,7 @@ function rtcStatsConnection(wsURL, logger = console) {
|
|
|
3318
3862
|
ws.onclose = (e) => {
|
|
3319
3863
|
connection.connected = false;
|
|
3320
3864
|
logger.info(`[RTCSTATS] Closed ${e.code}`);
|
|
3865
|
+
resetDelta();
|
|
3321
3866
|
};
|
|
3322
3867
|
ws.onopen = () => {
|
|
3323
3868
|
// send client info after each connection, so analysis tools can handle reconnections
|
|
@@ -3342,10 +3887,11 @@ function rtcStatsConnection(wsURL, logger = console) {
|
|
|
3342
3887
|
ws.send(JSON.stringify(userRole));
|
|
3343
3888
|
}
|
|
3344
3889
|
|
|
3345
|
-
// send buffered events
|
|
3890
|
+
// send buffered events
|
|
3346
3891
|
while (buffer.length) {
|
|
3347
3892
|
ws.send(JSON.stringify(buffer.shift()));
|
|
3348
3893
|
}
|
|
3894
|
+
getStatsBufferUsed = 0;
|
|
3349
3895
|
};
|
|
3350
3896
|
},
|
|
3351
3897
|
};
|
|
@@ -3354,11 +3900,13 @@ function rtcStatsConnection(wsURL, logger = console) {
|
|
|
3354
3900
|
}
|
|
3355
3901
|
|
|
3356
3902
|
const server = rtcStatsConnection("wss://rtcstats.srv.whereby.com" );
|
|
3357
|
-
rtcstats$1(
|
|
3903
|
+
const stats = rtcstats$1(
|
|
3358
3904
|
server.trace,
|
|
3359
3905
|
10000, // query once every 10 seconds.
|
|
3360
3906
|
[""] // only shim unprefixed RTCPeerConnecion.
|
|
3361
3907
|
);
|
|
3908
|
+
// on node clients this function can be undefined
|
|
3909
|
+
resetDelta = stats?.resetDelta || noop;
|
|
3362
3910
|
|
|
3363
3911
|
const rtcStats = {
|
|
3364
3912
|
sendEvent: (type, value) => {
|
|
@@ -3376,6 +3924,8 @@ const rtcStats = {
|
|
|
3376
3924
|
server,
|
|
3377
3925
|
};
|
|
3378
3926
|
|
|
3927
|
+
const logger$5 = new Logger();
|
|
3928
|
+
|
|
3379
3929
|
const CAMERA_STREAM_ID$1 = RtcStream.getCameraId();
|
|
3380
3930
|
const browserName$2 = adapter.browserDetails.browser;
|
|
3381
3931
|
const browserVersion = adapter.browserDetails.version;
|
|
@@ -3392,7 +3942,7 @@ if (browserName$2 === "chrome") {
|
|
|
3392
3942
|
}
|
|
3393
3943
|
|
|
3394
3944
|
class BaseRtcManager {
|
|
3395
|
-
constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features
|
|
3945
|
+
constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features }) {
|
|
3396
3946
|
assert$1.ok(selfId, "selfId is required");
|
|
3397
3947
|
assert$1.ok(room, "room is required");
|
|
3398
3948
|
assert$1.ok(emitter && emitter.emit, "emitter is required");
|
|
@@ -3407,13 +3957,14 @@ class BaseRtcManager {
|
|
|
3407
3957
|
this.peerConnections = {};
|
|
3408
3958
|
this.localStreams = {};
|
|
3409
3959
|
this.enabledLocalStreamIds = [];
|
|
3960
|
+
this._screenshareVideoTrackIds = [];
|
|
3410
3961
|
this._socketListenerDeregisterFunctions = [];
|
|
3411
3962
|
this._localStreamDeregisterFunction = null;
|
|
3412
3963
|
this._emitter = emitter;
|
|
3413
3964
|
this._serverSocket = serverSocket;
|
|
3414
3965
|
this._webrtcProvider = webrtcProvider;
|
|
3415
3966
|
this._features = features || {};
|
|
3416
|
-
this.
|
|
3967
|
+
this._isAudioOnlyMode = false;
|
|
3417
3968
|
|
|
3418
3969
|
this.offerOptions = { offerToReceiveAudio: true, offerToReceiveVideo: true };
|
|
3419
3970
|
this._pendingActionsForConnectedPeerConnections = [];
|
|
@@ -3432,6 +3983,8 @@ class BaseRtcManager {
|
|
|
3432
3983
|
iceServers: iceServers.iceServers || [],
|
|
3433
3984
|
mediaserverConfigTtlSeconds,
|
|
3434
3985
|
});
|
|
3986
|
+
|
|
3987
|
+
this.totalSessionsCreated = 0;
|
|
3435
3988
|
}
|
|
3436
3989
|
|
|
3437
3990
|
numberOfPeerconnections() {
|
|
@@ -3482,7 +4035,7 @@ class BaseRtcManager {
|
|
|
3482
4035
|
receiver.playoutDelayHint = MEDIA_JITTER_BUFFER_TARGET / 1000; // seconds
|
|
3483
4036
|
});
|
|
3484
4037
|
} catch (error) {
|
|
3485
|
-
|
|
4038
|
+
logger$5.error("Error during setting jitter buffer target:", error);
|
|
3486
4039
|
}
|
|
3487
4040
|
}
|
|
3488
4041
|
|
|
@@ -3537,6 +4090,8 @@ class BaseRtcManager {
|
|
|
3537
4090
|
: MAXIMUM_TURN_BANDWIDTH,
|
|
3538
4091
|
deprioritizeH264Encoding,
|
|
3539
4092
|
});
|
|
4093
|
+
|
|
4094
|
+
this.totalSessionsCreated++;
|
|
3540
4095
|
}
|
|
3541
4096
|
return session;
|
|
3542
4097
|
}
|
|
@@ -3630,6 +4185,8 @@ class BaseRtcManager {
|
|
|
3630
4185
|
clientId,
|
|
3631
4186
|
});
|
|
3632
4187
|
|
|
4188
|
+
setTimeout(() => this._emit(rtcManagerEvents.NEW_PC), 0);
|
|
4189
|
+
|
|
3633
4190
|
pc.ontrack = (event) => {
|
|
3634
4191
|
const stream = event.streams[0];
|
|
3635
4192
|
if (stream.id === "default" && stream.getAudioTracks().length === 0) {
|
|
@@ -3691,6 +4248,11 @@ class BaseRtcManager {
|
|
|
3691
4248
|
this.maybeRestrictRelayBandwidth(session);
|
|
3692
4249
|
}
|
|
3693
4250
|
}
|
|
4251
|
+
|
|
4252
|
+
if (this._isAudioOnlyMode) {
|
|
4253
|
+
session.setAudioOnly(true, this._screenshareVideoTrackIds);
|
|
4254
|
+
}
|
|
4255
|
+
|
|
3694
4256
|
session.registerConnected();
|
|
3695
4257
|
break;
|
|
3696
4258
|
case "disconnected":
|
|
@@ -3794,7 +4356,7 @@ class BaseRtcManager {
|
|
|
3794
4356
|
_cleanup(peerConnectionId) {
|
|
3795
4357
|
const session = this._getSession(peerConnectionId);
|
|
3796
4358
|
if (!session) {
|
|
3797
|
-
|
|
4359
|
+
logger$5.warn("No RTCPeerConnection in RTCManager.disconnect()", peerConnectionId);
|
|
3798
4360
|
return;
|
|
3799
4361
|
}
|
|
3800
4362
|
session.close();
|
|
@@ -3824,10 +4386,10 @@ class BaseRtcManager {
|
|
|
3824
4386
|
const promises = [];
|
|
3825
4387
|
this._forEachPeerConnection((session) => {
|
|
3826
4388
|
if (!session.hasConnectedPeerConnection()) {
|
|
3827
|
-
|
|
4389
|
+
logger$5.info("Session doesn't have a connected PeerConnection, adding pending action!");
|
|
3828
4390
|
const pendingActions = this._pendingActionsForConnectedPeerConnections;
|
|
3829
4391
|
if (!pendingActions) {
|
|
3830
|
-
|
|
4392
|
+
logger$5.warn(
|
|
3831
4393
|
`No pending action is created to repalce track, because the pending actions array is null`
|
|
3832
4394
|
);
|
|
3833
4395
|
return;
|
|
@@ -3836,7 +4398,7 @@ class BaseRtcManager {
|
|
|
3836
4398
|
const action = () => {
|
|
3837
4399
|
const replacedTrackPromise = session.replaceTrack(oldTrack, newTrack);
|
|
3838
4400
|
if (!replacedTrackPromise) {
|
|
3839
|
-
|
|
4401
|
+
logger$5.error("replaceTrack returned false!");
|
|
3840
4402
|
reject(`ReplaceTrack returned false`);
|
|
3841
4403
|
return;
|
|
3842
4404
|
}
|
|
@@ -3849,7 +4411,7 @@ class BaseRtcManager {
|
|
|
3849
4411
|
}
|
|
3850
4412
|
const replacedTrackResult = session.replaceTrack(oldTrack, newTrack);
|
|
3851
4413
|
if (!replacedTrackResult) {
|
|
3852
|
-
|
|
4414
|
+
logger$5.error("replaceTrack returned false!");
|
|
3853
4415
|
return;
|
|
3854
4416
|
}
|
|
3855
4417
|
promises.push(replacedTrackResult);
|
|
@@ -3919,6 +4481,7 @@ class BaseRtcManager {
|
|
|
3919
4481
|
}
|
|
3920
4482
|
|
|
3921
4483
|
// at this point it is clearly a screensharing stream.
|
|
4484
|
+
this._screenshareVideoTrackIds.push(stream.getVideoTracks()[0].id);
|
|
3922
4485
|
this._shareScreen(streamId, stream);
|
|
3923
4486
|
return;
|
|
3924
4487
|
}
|
|
@@ -4017,7 +4580,7 @@ class BaseRtcManager {
|
|
|
4017
4580
|
|
|
4018
4581
|
this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
|
|
4019
4582
|
if (data.error) {
|
|
4020
|
-
|
|
4583
|
+
logger$5.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
|
|
4021
4584
|
return;
|
|
4022
4585
|
}
|
|
4023
4586
|
this._updateAndScheduleMediaServersRefresh(data);
|
|
@@ -4030,7 +4593,7 @@ class BaseRtcManager {
|
|
|
4030
4593
|
this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, (data) => {
|
|
4031
4594
|
const session = this._getSession(data.clientId);
|
|
4032
4595
|
if (!session) {
|
|
4033
|
-
|
|
4596
|
+
logger$5.warn("No RTCPeerConnection on ICE_CANDIDATE", data);
|
|
4034
4597
|
return;
|
|
4035
4598
|
}
|
|
4036
4599
|
session.addIceCandidate(data.message);
|
|
@@ -4039,7 +4602,7 @@ class BaseRtcManager {
|
|
|
4039
4602
|
this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, (data) => {
|
|
4040
4603
|
const session = this._getSession(data.clientId);
|
|
4041
4604
|
if (!session) {
|
|
4042
|
-
|
|
4605
|
+
logger$5.warn("No RTCPeerConnection on ICE_END_OF_CANDIDATES", data);
|
|
4043
4606
|
return;
|
|
4044
4607
|
}
|
|
4045
4608
|
session.addIceCandidate(null);
|
|
@@ -4049,7 +4612,7 @@ class BaseRtcManager {
|
|
|
4049
4612
|
this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, (data) => {
|
|
4050
4613
|
const session = this._getSession(data.clientId);
|
|
4051
4614
|
if (!session) {
|
|
4052
|
-
|
|
4615
|
+
logger$5.warn("No RTCPeerConnection on SDP_OFFER", data);
|
|
4053
4616
|
return;
|
|
4054
4617
|
}
|
|
4055
4618
|
const offer = this._transformIncomingSdp(data.message, session.pc);
|
|
@@ -4065,12 +4628,35 @@ class BaseRtcManager {
|
|
|
4065
4628
|
this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, (data) => {
|
|
4066
4629
|
const session = this._getSession(data.clientId);
|
|
4067
4630
|
if (!session) {
|
|
4068
|
-
|
|
4631
|
+
logger$5.warn("No RTCPeerConnection on SDP_ANSWER", data);
|
|
4069
4632
|
return;
|
|
4070
4633
|
}
|
|
4071
4634
|
const answer = this._transformIncomingSdp(data.message, session.pc);
|
|
4072
4635
|
session.handleAnswer(answer);
|
|
4073
4636
|
}),
|
|
4637
|
+
|
|
4638
|
+
// if this is a reconnect to signal-server during screen-share we must let signal-server know
|
|
4639
|
+
this._serverSocket.on(PROTOCOL_RESPONSES.ROOM_JOINED, ({ room: { sfuServer: isSfu } }) => {
|
|
4640
|
+
if (isSfu || !this._wasScreenSharing) return;
|
|
4641
|
+
|
|
4642
|
+
const screenShareStreamId = Object.keys(this.localStreams).find((id) => id !== CAMERA_STREAM_ID$1);
|
|
4643
|
+
if (!screenShareStreamId) {
|
|
4644
|
+
return;
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
const screenshareStream = this.localStreams[screenShareStreamId];
|
|
4648
|
+
if (!screenshareStream) {
|
|
4649
|
+
logger$5.warn("screenshare stream %s not found", screenShareStreamId);
|
|
4650
|
+
return;
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
const hasAudioTrack = screenshareStream.getAudioTracks().length > 0;
|
|
4654
|
+
|
|
4655
|
+
this._emitServerEvent(PROTOCOL_REQUESTS.START_SCREENSHARE, {
|
|
4656
|
+
streamId: screenShareStreamId,
|
|
4657
|
+
hasAudioTrack,
|
|
4658
|
+
});
|
|
4659
|
+
}),
|
|
4074
4660
|
];
|
|
4075
4661
|
}
|
|
4076
4662
|
|
|
@@ -4104,6 +4690,27 @@ class BaseRtcManager {
|
|
|
4104
4690
|
track.removeEventListener("ended", this._audioTrackOnEnded);
|
|
4105
4691
|
}
|
|
4106
4692
|
|
|
4693
|
+
setAudioOnly(audioOnly) {
|
|
4694
|
+
this._isAudioOnlyMode = audioOnly;
|
|
4695
|
+
|
|
4696
|
+
this._forEachPeerConnection((session) => {
|
|
4697
|
+
if (session.hasConnectedPeerConnection()) {
|
|
4698
|
+
this._withForcedRenegotiation(session, () =>
|
|
4699
|
+
session.setAudioOnly(this._isAudioOnlyMode, this._screenshareVideoTrackIds)
|
|
4700
|
+
);
|
|
4701
|
+
}
|
|
4702
|
+
});
|
|
4703
|
+
}
|
|
4704
|
+
|
|
4705
|
+
setRemoteScreenshareVideoTrackIds(remoteScreenshareVideoTrackIds = []) {
|
|
4706
|
+
const localScreenshareStream = this._getFirstLocalNonCameraStream();
|
|
4707
|
+
|
|
4708
|
+
this._screenshareVideoTrackIds = [
|
|
4709
|
+
...(localScreenshareStream?.track ? [localScreenshareStream.track.id] : []),
|
|
4710
|
+
...remoteScreenshareVideoTrackIds,
|
|
4711
|
+
];
|
|
4712
|
+
}
|
|
4713
|
+
|
|
4107
4714
|
setRoomSessionId(roomSessionId) {
|
|
4108
4715
|
this._roomSessionId = roomSessionId;
|
|
4109
4716
|
}
|
|
@@ -4137,6 +4744,54 @@ function getOptimalBitrate(width, height, frameRate) {
|
|
|
4137
4744
|
return targetBitrate;
|
|
4138
4745
|
}
|
|
4139
4746
|
|
|
4747
|
+
// taken from https://github.com/sindresorhus/ip-regex ^5.0.0
|
|
4748
|
+
// inlined because it's import caused errors in browser-sdk when running tests
|
|
4749
|
+
const word = "[a-fA-F\\d:]";
|
|
4750
|
+
|
|
4751
|
+
const boundry = (options) =>
|
|
4752
|
+
options && options.includeBoundaries ? `(?:(?<=\\s|^)(?=${word})|(?<=${word})(?=\\s|$))` : "";
|
|
4753
|
+
|
|
4754
|
+
const v4 = "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}";
|
|
4755
|
+
|
|
4756
|
+
const v6segment = "[a-fA-F\\d]{1,4}";
|
|
4757
|
+
|
|
4758
|
+
const v6 = `
|
|
4759
|
+
(?:
|
|
4760
|
+
(?:${v6segment}:){7}(?:${v6segment}|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
|
|
4761
|
+
(?:${v6segment}:){6}(?:${v4}|:${v6segment}|:)| // 1:2:3:4:5:6:: 1:2:3:4:5:6::8 1:2:3:4:5:6::8 1:2:3:4:5:6::1.2.3.4
|
|
4762
|
+
(?:${v6segment}:){5}(?::${v4}|(?::${v6segment}){1,2}|:)| // 1:2:3:4:5:: 1:2:3:4:5::7:8 1:2:3:4:5::8 1:2:3:4:5::7:1.2.3.4
|
|
4763
|
+
(?:${v6segment}:){4}(?:(?::${v6segment}){0,1}:${v4}|(?::${v6segment}){1,3}|:)| // 1:2:3:4:: 1:2:3:4::6:7:8 1:2:3:4::8 1:2:3:4::6:7:1.2.3.4
|
|
4764
|
+
(?:${v6segment}:){3}(?:(?::${v6segment}){0,2}:${v4}|(?::${v6segment}){1,4}|:)| // 1:2:3:: 1:2:3::5:6:7:8 1:2:3::8 1:2:3::5:6:7:1.2.3.4
|
|
4765
|
+
(?:${v6segment}:){2}(?:(?::${v6segment}){0,3}:${v4}|(?::${v6segment}){1,5}|:)| // 1:2:: 1:2::4:5:6:7:8 1:2::8 1:2::4:5:6:7:1.2.3.4
|
|
4766
|
+
(?:${v6segment}:){1}(?:(?::${v6segment}){0,4}:${v4}|(?::${v6segment}){1,6}|:)| // 1:: 1::3:4:5:6:7:8 1::8 1::3:4:5:6:7:1.2.3.4
|
|
4767
|
+
(?::(?:(?::${v6segment}){0,5}:${v4}|(?::${v6segment}){1,7}|:)) // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::1.2.3.4
|
|
4768
|
+
)(?:%[0-9a-zA-Z]{1,})? // %eth0 %1
|
|
4769
|
+
`
|
|
4770
|
+
.replace(/\s*\/\/.*$/gm, "")
|
|
4771
|
+
.replace(/\n/g, "")
|
|
4772
|
+
.trim();
|
|
4773
|
+
|
|
4774
|
+
// Pre-compile only the exact regexes because adding a global flag make regexes stateful
|
|
4775
|
+
const v46Exact = new RegExp(`(?:^${v4}$)|(?:^${v6}$)`);
|
|
4776
|
+
const v4exact = new RegExp(`^${v4}$`);
|
|
4777
|
+
const v6exact = new RegExp(`^${v6}$`);
|
|
4778
|
+
|
|
4779
|
+
const ipRegex = (options) =>
|
|
4780
|
+
options && options.exact
|
|
4781
|
+
? v46Exact
|
|
4782
|
+
: new RegExp(
|
|
4783
|
+
`(?:${boundry(options)}${v4}${boundry(options)})|(?:${boundry(options)}${v6}${boundry(options)})`,
|
|
4784
|
+
"g"
|
|
4785
|
+
);
|
|
4786
|
+
|
|
4787
|
+
ipRegex.v4 = (options) =>
|
|
4788
|
+
options && options.exact ? v4exact : new RegExp(`${boundry(options)}${v4}${boundry(options)}`, "g");
|
|
4789
|
+
ipRegex.v6 = (options) =>
|
|
4790
|
+
options && options.exact ? v6exact : new RegExp(`${boundry(options)}${v6}${boundry(options)}`, "g");
|
|
4791
|
+
|
|
4792
|
+
const logger$4 = new Logger();
|
|
4793
|
+
|
|
4794
|
+
const ICE_PUBLIC_IP_GATHERING_TIMEOUT = 3 * 1000;
|
|
4140
4795
|
const CAMERA_STREAM_ID = RtcStream.getCameraId();
|
|
4141
4796
|
const browserName$1 = adapter.browserDetails.browser;
|
|
4142
4797
|
|
|
@@ -4147,7 +4802,7 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4147
4802
|
let session = this._getSession(clientId);
|
|
4148
4803
|
let bandwidth = (session && session.bandwidth) || 0;
|
|
4149
4804
|
if (session) {
|
|
4150
|
-
|
|
4805
|
+
logger$4.warn("Replacing peer session", clientId);
|
|
4151
4806
|
this._cleanup(clientId);
|
|
4152
4807
|
} else {
|
|
4153
4808
|
bandwidth = this._changeBandwidthForAllClients(true);
|
|
@@ -4168,6 +4823,14 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4168
4823
|
// clean up some helpers.
|
|
4169
4824
|
session.wasEverConnected = false;
|
|
4170
4825
|
session.relayCandidateSeen = false;
|
|
4826
|
+
session.serverReflexiveCandidateSeen = false;
|
|
4827
|
+
session.publicHostCandidateSeen = false;
|
|
4828
|
+
session.ipv6HostCandidateSeen = false;
|
|
4829
|
+
this.ipv6HostCandidateTeredoSeen = false;
|
|
4830
|
+
this.ipv6HostCandidate6to4Seen = false;
|
|
4831
|
+
this.mdnsHostCandidateSeen = false;
|
|
4832
|
+
|
|
4833
|
+
this._emit(rtcManagerEvents.ICE_RESTART);
|
|
4171
4834
|
|
|
4172
4835
|
this._negotiatePeerConnection(
|
|
4173
4836
|
clientId,
|
|
@@ -4222,13 +4885,13 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4222
4885
|
videoTransceiver.setCodecPreferences(capabilities.codecs);
|
|
4223
4886
|
});
|
|
4224
4887
|
} catch (error) {
|
|
4225
|
-
|
|
4888
|
+
logger$4.error("Error during setting setCodecPreferences:", error);
|
|
4226
4889
|
}
|
|
4227
4890
|
}
|
|
4228
4891
|
|
|
4229
4892
|
_negotiatePeerConnection(clientId, session, constraints) {
|
|
4230
4893
|
if (!session) {
|
|
4231
|
-
|
|
4894
|
+
logger$4.warn("No RTCPeerConnection in negotiatePeerConnection()", clientId);
|
|
4232
4895
|
return;
|
|
4233
4896
|
}
|
|
4234
4897
|
const pc = session.pc;
|
|
@@ -4270,11 +4933,24 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4270
4933
|
return;
|
|
4271
4934
|
}
|
|
4272
4935
|
pc.setLocalDescription(offer).catch((e) => {
|
|
4273
|
-
|
|
4936
|
+
logger$4.warn("RTCPeerConnection.setLocalDescription() failed with local offer", e);
|
|
4937
|
+
|
|
4938
|
+
// we failed to create a valid offer so try having the other side create it, without looping
|
|
4939
|
+
if (this._features.reverseOfferOnFailure) {
|
|
4940
|
+
if (!this._lastReverseDirectionAttemptByClientId)
|
|
4941
|
+
this._lastReverseDirectionAttemptByClientId = {};
|
|
4942
|
+
if (
|
|
4943
|
+
!this._lastReverseDirectionAttemptByClientId[clientId] ||
|
|
4944
|
+
this._lastReverseDirectionAttemptByClientId[clientId] < Date.now() - 10000
|
|
4945
|
+
) {
|
|
4946
|
+
this.acceptNewStream({ clientId, streamId: clientId, shouldAddLocalVideo: true });
|
|
4947
|
+
this._lastReverseDirectionAttemptByClientId[clientId] = Date.now();
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4274
4950
|
});
|
|
4275
4951
|
})
|
|
4276
4952
|
.catch((e) => {
|
|
4277
|
-
|
|
4953
|
+
logger$4.warn("RTCPeerConnection.createOffer() failed to create local offer", e);
|
|
4278
4954
|
});
|
|
4279
4955
|
}
|
|
4280
4956
|
|
|
@@ -4310,7 +4986,7 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4310
4986
|
let bandwidth = this._features.bandwidth
|
|
4311
4987
|
? parseInt(this._features.bandwidth, 10)
|
|
4312
4988
|
: {
|
|
4313
|
-
1:
|
|
4989
|
+
1: 0,
|
|
4314
4990
|
2: this._features.highP2PBandwidth ? 768 : 384,
|
|
4315
4991
|
3: this._features.highP2PBandwidth ? 512 : 256,
|
|
4316
4992
|
4: 192,
|
|
@@ -4376,9 +5052,73 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4376
5052
|
pc.addTrack(this._stoppedVideoTrack, localCameraStream);
|
|
4377
5053
|
}
|
|
4378
5054
|
|
|
5055
|
+
pc.onicegatheringstatechange = (event) => {
|
|
5056
|
+
const connection = event.target;
|
|
5057
|
+
|
|
5058
|
+
switch (connection.iceGatheringState) {
|
|
5059
|
+
case "gathering":
|
|
5060
|
+
if (this.icePublicIPGatheringTimeoutID) clearTimeout(this.icePublicIPGatheringTimeoutID);
|
|
5061
|
+
this.icePublicIPGatheringTimeoutID = setTimeout(() => {
|
|
5062
|
+
if (
|
|
5063
|
+
!session.publicHostCandidateSeen &&
|
|
5064
|
+
!session.relayCandidateSeen &&
|
|
5065
|
+
!session.serverReflexiveCandidateSeen
|
|
5066
|
+
) {
|
|
5067
|
+
this._emit(rtcManagerEvents.ICE_NO_PUBLIC_IP_GATHERED_3SEC);
|
|
5068
|
+
}
|
|
5069
|
+
}, ICE_PUBLIC_IP_GATHERING_TIMEOUT);
|
|
5070
|
+
break;
|
|
5071
|
+
case "complete":
|
|
5072
|
+
if (this.icePublicIPGatheringTimeoutID) clearTimeout(this.icePublicIPGatheringTimeoutID);
|
|
5073
|
+
this.icePublicIPGatheringTimeoutID = undefined;
|
|
5074
|
+
break;
|
|
5075
|
+
}
|
|
5076
|
+
};
|
|
5077
|
+
|
|
4379
5078
|
pc.onicecandidate = (event) => {
|
|
4380
5079
|
if (event.candidate) {
|
|
4381
|
-
|
|
5080
|
+
switch (event.candidate?.type) {
|
|
5081
|
+
case "host":
|
|
5082
|
+
const address = event?.candidate?.address;
|
|
5083
|
+
try {
|
|
5084
|
+
if (ipRegex.v4({ exact: true }).test(address)) {
|
|
5085
|
+
const ipv4 = checkIp(address);
|
|
5086
|
+
if (ipv4.isPublicIp) session.publicHostCandidateSeen = true;
|
|
5087
|
+
} else if (ipRegex.v6({ exact: true }).test(address.replace(/^\[(.*)\]/, "$1"))) {
|
|
5088
|
+
const ipv6 = new Address6(address.replace(/^\[(.*)\]/, "$1"));
|
|
5089
|
+
session.ipv6HostCandidateSeen = true;
|
|
5090
|
+
|
|
5091
|
+
if (ipv6.getScope() === "Global") {
|
|
5092
|
+
session.publicHostCandidateSeen = true;
|
|
5093
|
+
}
|
|
5094
|
+
if (ipv6.isTeredo()) {
|
|
5095
|
+
session.ipv6HostCandidateTeredoSeen = true;
|
|
5096
|
+
}
|
|
5097
|
+
if (ipv6.is6to4()) {
|
|
5098
|
+
session.ipv6HostCandidate6to4Seen = true;
|
|
5099
|
+
}
|
|
5100
|
+
} else {
|
|
5101
|
+
const uuidv4 = address.replace(/.local/, "");
|
|
5102
|
+
if (uuidv4 && validate(uuidv4, 4)) {
|
|
5103
|
+
session.mdnsHostCandidateSeen = true;
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
} catch (error) {
|
|
5107
|
+
logger$4.info("Error during parsing candidates! Error: ", { error });
|
|
5108
|
+
}
|
|
5109
|
+
break;
|
|
5110
|
+
case "srflx":
|
|
5111
|
+
if (!session.serverReflexiveCandidateSeen) {
|
|
5112
|
+
session.serverReflexiveCandidateSeen = true;
|
|
5113
|
+
}
|
|
5114
|
+
break;
|
|
5115
|
+
case "relay":
|
|
5116
|
+
case "relayed":
|
|
5117
|
+
if (!session.relayCandidateSeen) {
|
|
5118
|
+
session.relayCandidateSeen = true;
|
|
5119
|
+
}
|
|
5120
|
+
break;
|
|
5121
|
+
}
|
|
4382
5122
|
this._emitServerEvent(RELAY_MESSAGES.ICE_CANDIDATE, {
|
|
4383
5123
|
receiverId: clientId,
|
|
4384
5124
|
message: event.candidate,
|
|
@@ -4387,6 +5127,20 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4387
5127
|
this._emitServerEvent(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, {
|
|
4388
5128
|
receiverId: clientId,
|
|
4389
5129
|
});
|
|
5130
|
+
if (
|
|
5131
|
+
!session.publicHostCandidateSeen &&
|
|
5132
|
+
!session.relayCandidateSeen &&
|
|
5133
|
+
!session.serverReflexiveCandidateSeen
|
|
5134
|
+
) {
|
|
5135
|
+
this._emit(rtcManagerEvents.ICE_NO_PUBLIC_IP_GATHERED);
|
|
5136
|
+
}
|
|
5137
|
+
if (session.ipv6HostCandidateSeen) {
|
|
5138
|
+
this._emit(rtcManagerEvents.ICE_IPV6_SEEN, {
|
|
5139
|
+
teredoSeen: session.ipv6HostCandidateTeredoSeen,
|
|
5140
|
+
sixtofourSeen: session.ipv6HostCandidate6to4Seen,
|
|
5141
|
+
});
|
|
5142
|
+
}
|
|
5143
|
+
if (session.mdnsHostCandidateSeen) this._emit(rtcManagerEvents.ICE_MDNS_SEEN);
|
|
4390
5144
|
}
|
|
4391
5145
|
};
|
|
4392
5146
|
|
|
@@ -4413,7 +5167,7 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4413
5167
|
if (session) {
|
|
4414
5168
|
// this will happen on a signal-server reconnect
|
|
4415
5169
|
// before we tried an ice-restart here, now we recreate the session/pc
|
|
4416
|
-
|
|
5170
|
+
logger$4.warn("Replacing peer session", clientId);
|
|
4417
5171
|
this._cleanup(clientId); // will cleanup and delete session/pc
|
|
4418
5172
|
} else {
|
|
4419
5173
|
// we adjust bandwidth based on number of sessions/pcs
|
|
@@ -4511,14 +5265,20 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4511
5265
|
streamId,
|
|
4512
5266
|
hasAudioTrack: !!stream.getAudioTracks().length,
|
|
4513
5267
|
});
|
|
5268
|
+
this._wasScreenSharing = true;
|
|
4514
5269
|
this._addStreamToPeerConnections(stream);
|
|
4515
5270
|
}
|
|
4516
5271
|
|
|
4517
5272
|
removeStream(streamId, stream, requestedByClientId) {
|
|
4518
5273
|
super.removeStream(streamId, stream);
|
|
4519
5274
|
this._removeStreamFromPeerConnections(stream);
|
|
5275
|
+
this._wasScreenSharing = false;
|
|
4520
5276
|
this._emitServerEvent(PROTOCOL_REQUESTS.STOP_SCREENSHARE, { streamId, requestedByClientId });
|
|
4521
5277
|
}
|
|
5278
|
+
|
|
5279
|
+
hasClient(clientId) {
|
|
5280
|
+
return Object.keys(this.peerConnections).includes(clientId);
|
|
5281
|
+
}
|
|
4522
5282
|
}
|
|
4523
5283
|
|
|
4524
5284
|
class SfuV2Parser {
|
|
@@ -4643,12 +5403,13 @@ class SfuV2Parser {
|
|
|
4643
5403
|
}
|
|
4644
5404
|
}
|
|
4645
5405
|
|
|
4646
|
-
|
|
4647
|
-
|
|
5406
|
+
const logger$3 = new Logger();
|
|
5407
|
+
|
|
5408
|
+
class VegaConnection extends EventEmitter$1 {
|
|
5409
|
+
constructor(wsUrl, protocol = "whereby-sfu#v4") {
|
|
4648
5410
|
super();
|
|
4649
5411
|
|
|
4650
5412
|
this.wsUrl = wsUrl;
|
|
4651
|
-
this.logger = logger;
|
|
4652
5413
|
this.protocol = protocol;
|
|
4653
5414
|
|
|
4654
5415
|
// This is the map of sent requests that are waiting for a response
|
|
@@ -4681,7 +5442,7 @@ class VegaConnection extends EventEmitter {
|
|
|
4681
5442
|
}
|
|
4682
5443
|
|
|
4683
5444
|
_onOpen() {
|
|
4684
|
-
|
|
5445
|
+
logger$3.info("Connected");
|
|
4685
5446
|
|
|
4686
5447
|
this.emit("open");
|
|
4687
5448
|
}
|
|
@@ -4689,7 +5450,7 @@ class VegaConnection extends EventEmitter {
|
|
|
4689
5450
|
_onMessage(event) {
|
|
4690
5451
|
const socketMessage = SfuV2Parser.parse(event.data);
|
|
4691
5452
|
|
|
4692
|
-
|
|
5453
|
+
logger$3.info("Received message", socketMessage);
|
|
4693
5454
|
|
|
4694
5455
|
if (socketMessage?.response) {
|
|
4695
5456
|
this._handleResponse(socketMessage);
|
|
@@ -4699,13 +5460,13 @@ class VegaConnection extends EventEmitter {
|
|
|
4699
5460
|
}
|
|
4700
5461
|
|
|
4701
5462
|
_onClose() {
|
|
4702
|
-
|
|
5463
|
+
logger$3.info("Disconnected");
|
|
4703
5464
|
|
|
4704
5465
|
this._tearDown();
|
|
4705
5466
|
}
|
|
4706
5467
|
|
|
4707
5468
|
_onError(error) {
|
|
4708
|
-
|
|
5469
|
+
logger$3.info("Error", error);
|
|
4709
5470
|
}
|
|
4710
5471
|
|
|
4711
5472
|
_handleResponse(socketMessage) {
|
|
@@ -5061,6 +5822,8 @@ const defaultParams = {
|
|
|
5061
5822
|
outFormula: "score", // the out/score sent to SFU
|
|
5062
5823
|
};
|
|
5063
5824
|
|
|
5825
|
+
const logger$2 = new Logger();
|
|
5826
|
+
|
|
5064
5827
|
function createMicAnalyser({ micTrack, params, onScoreUpdated }) {
|
|
5065
5828
|
// todo: might need to reuse existing in PWA
|
|
5066
5829
|
const audioCtx = new AudioContext();
|
|
@@ -5086,7 +5849,7 @@ function createMicAnalyser({ micTrack, params, onScoreUpdated }) {
|
|
|
5086
5849
|
track = stream.getAudioTracks()[0];
|
|
5087
5850
|
lastTrackWasOurs = true;
|
|
5088
5851
|
} catch (ex) {
|
|
5089
|
-
|
|
5852
|
+
logger$2.warn("unable to fetch new track for colocation speaker analysis, using current", ex);
|
|
5090
5853
|
}
|
|
5091
5854
|
}
|
|
5092
5855
|
|
|
@@ -5218,18 +5981,207 @@ const maybeTurnOnly = (transportConfig, features) => {
|
|
|
5218
5981
|
}
|
|
5219
5982
|
};
|
|
5220
5983
|
|
|
5984
|
+
const logger$1 = new Logger();
|
|
5985
|
+
|
|
5986
|
+
const MEDIA_QUALITY = Object.freeze({
|
|
5987
|
+
ok: "ok",
|
|
5988
|
+
warning: "warning",
|
|
5989
|
+
critical: "critical",
|
|
5990
|
+
});
|
|
5991
|
+
|
|
5992
|
+
const MONITOR_INTERVAL = 600; // ms
|
|
5993
|
+
const TREND_HORIZON = 3; // number of monitor intervals needed for quality to change
|
|
5994
|
+
const WARNING_SCORE = 9;
|
|
5995
|
+
const CRITICAL_SCORE = 7;
|
|
5996
|
+
|
|
5997
|
+
class VegaMediaQualityMonitor extends EventEmitter {
|
|
5998
|
+
constructor() {
|
|
5999
|
+
super();
|
|
6000
|
+
this._clients = {};
|
|
6001
|
+
this._producers = {};
|
|
6002
|
+
this._startMonitor();
|
|
6003
|
+
}
|
|
6004
|
+
|
|
6005
|
+
close() {
|
|
6006
|
+
clearInterval(this._intervalHandle);
|
|
6007
|
+
delete this._intervalHandle;
|
|
6008
|
+
this._producers = {};
|
|
6009
|
+
this._clients = {};
|
|
6010
|
+
}
|
|
6011
|
+
|
|
6012
|
+
_startMonitor() {
|
|
6013
|
+
this._intervalHandle = setInterval(() => {
|
|
6014
|
+
Object.entries(this._producers).forEach(([clientId, producers]) => {
|
|
6015
|
+
this._evaluateClient(clientId, producers);
|
|
6016
|
+
});
|
|
6017
|
+
}, MONITOR_INTERVAL);
|
|
6018
|
+
}
|
|
6019
|
+
|
|
6020
|
+
_evaluateClient(clientId, producers) {
|
|
6021
|
+
if (!this._clients[clientId]) {
|
|
6022
|
+
this._clients[clientId] = {
|
|
6023
|
+
audio: { currentQuality: MEDIA_QUALITY.ok, trend: [] },
|
|
6024
|
+
video: { currentQuality: MEDIA_QUALITY.ok, trend: [] },
|
|
6025
|
+
};
|
|
6026
|
+
}
|
|
6027
|
+
|
|
6028
|
+
this._evaluateProducer(
|
|
6029
|
+
clientId,
|
|
6030
|
+
Object.values(producers).filter((p) => p.kind === "audio"),
|
|
6031
|
+
"audio"
|
|
6032
|
+
);
|
|
6033
|
+
this._evaluateProducer(
|
|
6034
|
+
clientId,
|
|
6035
|
+
Object.values(producers).filter((p) => p.kind === "video"),
|
|
6036
|
+
"video"
|
|
6037
|
+
);
|
|
6038
|
+
}
|
|
6039
|
+
|
|
6040
|
+
_evaluateProducer(clientId, producers, kind) {
|
|
6041
|
+
if (producers.length === 0) {
|
|
6042
|
+
return;
|
|
6043
|
+
}
|
|
6044
|
+
|
|
6045
|
+
const avgScore = producers.reduce((prev, curr) => prev + curr.score, 0) / producers.length;
|
|
6046
|
+
const newQuality = this._evaluateScore(avgScore);
|
|
6047
|
+
const qualityChanged = this._updateTrend(newQuality, this._clients[clientId][kind]);
|
|
6048
|
+
if (qualityChanged) {
|
|
6049
|
+
this.emit(PROTOCOL_EVENTS.MEDIA_QUALITY_CHANGED, {
|
|
6050
|
+
clientId,
|
|
6051
|
+
kind,
|
|
6052
|
+
quality: newQuality,
|
|
6053
|
+
});
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
|
|
6057
|
+
_updateTrend(newQuality, state) {
|
|
6058
|
+
state.trend.push(newQuality);
|
|
6059
|
+
if (state.trend.length > TREND_HORIZON) {
|
|
6060
|
+
state.trend.shift();
|
|
6061
|
+
}
|
|
6062
|
+
|
|
6063
|
+
if (newQuality !== state.currentQuality && state.trend.every((t) => t !== state.currentQuality)) {
|
|
6064
|
+
state.currentQuality = newQuality;
|
|
6065
|
+
return true;
|
|
6066
|
+
} else {
|
|
6067
|
+
return false;
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
|
|
6071
|
+
addProducer(clientId, producerId) {
|
|
6072
|
+
if (!clientId || !producerId || !(typeof clientId === "string" && typeof producerId === "string")) {
|
|
6073
|
+
logger$1.warn("Missing clientId or producerId");
|
|
6074
|
+
return;
|
|
6075
|
+
}
|
|
6076
|
+
|
|
6077
|
+
if (!this._producers[clientId]) {
|
|
6078
|
+
this._producers[clientId] = {};
|
|
6079
|
+
}
|
|
6080
|
+
|
|
6081
|
+
this._producers[clientId][producerId] = {};
|
|
6082
|
+
}
|
|
6083
|
+
|
|
6084
|
+
removeProducer(clientId, producerId) {
|
|
6085
|
+
delete this._producers[clientId][producerId];
|
|
6086
|
+
|
|
6087
|
+
if (Object.keys(this._producers[clientId]).length === 0) {
|
|
6088
|
+
delete this._producers[clientId];
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6092
|
+
addConsumer(clientId, consumerId) {
|
|
6093
|
+
if (!clientId || !consumerId) {
|
|
6094
|
+
logger$1.warn("Missing clientId or consumerId");
|
|
6095
|
+
return;
|
|
6096
|
+
}
|
|
6097
|
+
|
|
6098
|
+
if (!this._producers[clientId]) {
|
|
6099
|
+
this._producers[clientId] = {};
|
|
6100
|
+
}
|
|
6101
|
+
|
|
6102
|
+
this._producers[clientId][consumerId] = {};
|
|
6103
|
+
}
|
|
6104
|
+
|
|
6105
|
+
removeConsumer(clientId, consumerId) {
|
|
6106
|
+
delete this._producers[clientId][consumerId];
|
|
6107
|
+
|
|
6108
|
+
if (Object.keys(this._producers[clientId]).length === 0) {
|
|
6109
|
+
delete this._producers[clientId];
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
|
|
6113
|
+
addProducerScore(clientId, producerId, kind, score) {
|
|
6114
|
+
if (
|
|
6115
|
+
!Array.isArray(score) ||
|
|
6116
|
+
score.length === 0 ||
|
|
6117
|
+
score.some((s) => !s || !s.hasOwnProperty("score") || typeof s.score !== "number" || isNaN(s.score))
|
|
6118
|
+
) {
|
|
6119
|
+
logger$1.warn("Unexpected producer score format");
|
|
6120
|
+
return;
|
|
6121
|
+
}
|
|
6122
|
+
this._producers[clientId][producerId] = { kind, score: this._calcAvgProducerScore(score.map((s) => s.score)) };
|
|
6123
|
+
}
|
|
6124
|
+
|
|
6125
|
+
addConsumerScore(clientId, consumerId, kind, score) {
|
|
6126
|
+
if (!score || !score.hasOwnProperty("producerScores") || !Array.isArray(score.producerScores)) {
|
|
6127
|
+
logger$1.warn("Unexpected consumer score format");
|
|
6128
|
+
return;
|
|
6129
|
+
}
|
|
6130
|
+
this._producers[clientId][consumerId] = { kind, score: this._calcAvgProducerScore(score.producerScores) };
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
_evaluateScore(score) {
|
|
6134
|
+
if (score <= WARNING_SCORE && score > CRITICAL_SCORE) {
|
|
6135
|
+
return MEDIA_QUALITY.warning;
|
|
6136
|
+
} else if (score <= CRITICAL_SCORE && score > 0) {
|
|
6137
|
+
return MEDIA_QUALITY.critical;
|
|
6138
|
+
} else {
|
|
6139
|
+
return MEDIA_QUALITY.ok;
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
6142
|
+
|
|
6143
|
+
_calcAvgProducerScore(scores) {
|
|
6144
|
+
try {
|
|
6145
|
+
if (!Array.isArray(scores) || scores.length === 0) {
|
|
6146
|
+
return 0;
|
|
6147
|
+
}
|
|
6148
|
+
|
|
6149
|
+
let totalScore = 0;
|
|
6150
|
+
let divisor = 0;
|
|
6151
|
+
|
|
6152
|
+
scores.forEach((score) => {
|
|
6153
|
+
if (score > 0) {
|
|
6154
|
+
totalScore += score;
|
|
6155
|
+
divisor++;
|
|
6156
|
+
}
|
|
6157
|
+
});
|
|
6158
|
+
|
|
6159
|
+
if (totalScore === 0 || divisor === 0) {
|
|
6160
|
+
return 0;
|
|
6161
|
+
} else {
|
|
6162
|
+
return totalScore / divisor;
|
|
6163
|
+
}
|
|
6164
|
+
} catch (error) {
|
|
6165
|
+
logger$1.error(error);
|
|
6166
|
+
return 0;
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6170
|
+
|
|
6171
|
+
const logger = new Logger();
|
|
6172
|
+
|
|
5221
6173
|
const browserName = adapter.browserDetails.browser;
|
|
5222
6174
|
let unloading = false;
|
|
5223
6175
|
|
|
5224
6176
|
const RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS = 3500;
|
|
5225
6177
|
const RESTARTICE_ERROR_MAX_RETRY_COUNT = 5;
|
|
5226
|
-
const OUTBOUND_CAM_OUTBOUND_STREAM_ID = v4();
|
|
5227
|
-
const OUTBOUND_SCREEN_OUTBOUND_STREAM_ID = v4();
|
|
6178
|
+
const OUTBOUND_CAM_OUTBOUND_STREAM_ID = v4$1();
|
|
6179
|
+
const OUTBOUND_SCREEN_OUTBOUND_STREAM_ID = v4$1();
|
|
5228
6180
|
|
|
5229
6181
|
if (browserName === "chrome") window.document.addEventListener("beforeunload", () => (unloading = true));
|
|
5230
6182
|
|
|
5231
6183
|
class VegaRtcManager {
|
|
5232
|
-
constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim,
|
|
6184
|
+
constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim, deviceHandlerFactory }) {
|
|
5233
6185
|
assert$1.ok(selfId, "selfId is required");
|
|
5234
6186
|
assert$1.ok(room, "room is required");
|
|
5235
6187
|
assert$1.ok(emitter && emitter.emit, "emitter is required");
|
|
@@ -5246,14 +6198,18 @@ class VegaRtcManager {
|
|
|
5246
6198
|
this._webrtcProvider = webrtcProvider;
|
|
5247
6199
|
this._features = features || {};
|
|
5248
6200
|
this._eventClaim = eventClaim;
|
|
5249
|
-
this._logger = logger;
|
|
5250
6201
|
|
|
5251
6202
|
this._vegaConnection = null;
|
|
5252
6203
|
|
|
5253
6204
|
this._micAnalyser = null;
|
|
5254
6205
|
this._micAnalyserDebugger = null;
|
|
5255
6206
|
|
|
5256
|
-
|
|
6207
|
+
if (deviceHandlerFactory) {
|
|
6208
|
+
this._mediasoupDevice = new Device({ handlerFactory: deviceHandlerFactory });
|
|
6209
|
+
} else {
|
|
6210
|
+
this._mediasoupDevice = new Device({ handlerName: getHandler() });
|
|
6211
|
+
}
|
|
6212
|
+
|
|
5257
6213
|
this._routerRtpCapabilities = null;
|
|
5258
6214
|
|
|
5259
6215
|
this._sendTransport = null;
|
|
@@ -5316,6 +6272,11 @@ class VegaRtcManager {
|
|
|
5316
6272
|
// Retry if connection closed until disconnectAll called;
|
|
5317
6273
|
this._reconnect = true;
|
|
5318
6274
|
this._reconnectTimeOut = null;
|
|
6275
|
+
|
|
6276
|
+
this._qualityMonitor = new VegaMediaQualityMonitor();
|
|
6277
|
+
this._qualityMonitor.on(PROTOCOL_EVENTS.MEDIA_QUALITY_CHANGED, (payload) => {
|
|
6278
|
+
this._emitToPWA(PROTOCOL_EVENTS.MEDIA_QUALITY_CHANGED, payload);
|
|
6279
|
+
});
|
|
5319
6280
|
}
|
|
5320
6281
|
|
|
5321
6282
|
_updateAndScheduleMediaServersRefresh({ iceServers, sfuServer, mediaserverConfigTtlSeconds }) {
|
|
@@ -5348,7 +6309,7 @@ class VegaRtcManager {
|
|
|
5348
6309
|
|
|
5349
6310
|
this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
|
|
5350
6311
|
if (data.error) {
|
|
5351
|
-
|
|
6312
|
+
logger.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
|
|
5352
6313
|
return;
|
|
5353
6314
|
}
|
|
5354
6315
|
this._updateAndScheduleMediaServersRefresh(data);
|
|
@@ -5380,14 +6341,14 @@ class VegaRtcManager {
|
|
|
5380
6341
|
const queryString = searchParams.toString();
|
|
5381
6342
|
const wsUrl = `wss://${host}?${queryString}`;
|
|
5382
6343
|
|
|
5383
|
-
this._vegaConnection = new VegaConnection(wsUrl
|
|
6344
|
+
this._vegaConnection = new VegaConnection(wsUrl);
|
|
5384
6345
|
this._vegaConnection.on("open", () => this._join());
|
|
5385
6346
|
this._vegaConnection.on("close", () => this._onClose());
|
|
5386
6347
|
this._vegaConnection.on("message", (message) => this._onMessage(message));
|
|
5387
6348
|
}
|
|
5388
6349
|
|
|
5389
6350
|
_onClose() {
|
|
5390
|
-
|
|
6351
|
+
logger.info("_onClose()");
|
|
5391
6352
|
|
|
5392
6353
|
// These will clean up any and all producers/consumers
|
|
5393
6354
|
this._sendTransport?.close();
|
|
@@ -5400,10 +6361,14 @@ class VegaRtcManager {
|
|
|
5400
6361
|
if (this._reconnect) {
|
|
5401
6362
|
this._reconnectTimeOut = setTimeout(() => this._connect(), 1000);
|
|
5402
6363
|
}
|
|
6364
|
+
|
|
6365
|
+
this._qualityMonitor.close();
|
|
6366
|
+
this._emitToPWA(rtcManagerEvents.SFU_CONNECTION_CLOSED);
|
|
5403
6367
|
}
|
|
5404
6368
|
|
|
5405
6369
|
async _join() {
|
|
5406
|
-
|
|
6370
|
+
logger.info("join()");
|
|
6371
|
+
this._emitToPWA(rtcManagerEvents.SFU_CONNECTION_OPEN);
|
|
5407
6372
|
|
|
5408
6373
|
try {
|
|
5409
6374
|
// We need to always do this, as this triggers the join logic on the SFU
|
|
@@ -5437,7 +6402,7 @@ class VegaRtcManager {
|
|
|
5437
6402
|
|
|
5438
6403
|
await Promise.all(mediaPromises);
|
|
5439
6404
|
} catch (error) {
|
|
5440
|
-
|
|
6405
|
+
logger.error("_join() [error:%o]", error);
|
|
5441
6406
|
// TODO: handle this error, rejoin?
|
|
5442
6407
|
}
|
|
5443
6408
|
}
|
|
@@ -5468,7 +6433,7 @@ class VegaRtcManager {
|
|
|
5468
6433
|
|
|
5469
6434
|
const transport = this._mediasoupDevice[creator](transportOptions);
|
|
5470
6435
|
const onConnectionStateListener = async (connectionState) => {
|
|
5471
|
-
|
|
6436
|
+
logger.info(`Transport ConnectionStateChanged ${connectionState}`);
|
|
5472
6437
|
if (connectionState !== "disconnected" && connectionState !== "failed") {
|
|
5473
6438
|
return;
|
|
5474
6439
|
}
|
|
@@ -5542,17 +6507,17 @@ class VegaRtcManager {
|
|
|
5542
6507
|
|
|
5543
6508
|
async _restartIce(transport, retried = 0) {
|
|
5544
6509
|
if (!transport || !("closed" in transport) || !("connectionState" in transport)) {
|
|
5545
|
-
|
|
6510
|
+
logger.info("_restartIce: No transport or property closed or connectionState!");
|
|
5546
6511
|
return;
|
|
5547
6512
|
}
|
|
5548
6513
|
|
|
5549
6514
|
if (transport.closed) {
|
|
5550
|
-
|
|
6515
|
+
logger.info("_restartIce: Transport is closed!");
|
|
5551
6516
|
return;
|
|
5552
6517
|
}
|
|
5553
6518
|
|
|
5554
6519
|
if (transport.connectionState !== "disconnected" && transport.connectionState !== "failed") {
|
|
5555
|
-
|
|
6520
|
+
logger.info("_restartIce: Connection is healthy ICE restart no loneger needed!");
|
|
5556
6521
|
return;
|
|
5557
6522
|
}
|
|
5558
6523
|
|
|
@@ -5565,24 +6530,24 @@ class VegaRtcManager {
|
|
|
5565
6530
|
transport.appData.iceRestartStarted = now;
|
|
5566
6531
|
|
|
5567
6532
|
if (RESTARTICE_ERROR_MAX_RETRY_COUNT <= retried) {
|
|
5568
|
-
|
|
6533
|
+
logger.info("_restartIce: Reached restart ICE maximum retry count!");
|
|
5569
6534
|
return;
|
|
5570
6535
|
}
|
|
5571
6536
|
|
|
5572
6537
|
if (!this._vegaConnection) {
|
|
5573
|
-
|
|
6538
|
+
logger.info(`_restartIce: Connection is undefined`);
|
|
5574
6539
|
return;
|
|
5575
6540
|
}
|
|
5576
6541
|
const { iceParameters } = await this._vegaConnection.request("restartIce", { transportId: transport.id });
|
|
5577
6542
|
|
|
5578
|
-
|
|
6543
|
+
logger.info("_restartIce: ICE restart iceParameters received from SFU: ", iceParameters);
|
|
5579
6544
|
const error = await transport
|
|
5580
6545
|
.restartIce({ iceParameters })
|
|
5581
6546
|
.then(() => null)
|
|
5582
6547
|
.catch((err) => err);
|
|
5583
6548
|
|
|
5584
6549
|
if (error) {
|
|
5585
|
-
|
|
6550
|
+
logger.error(`_restartIce: ICE restart failed: ${error}`);
|
|
5586
6551
|
switch (error.message) {
|
|
5587
6552
|
case "missing transportId":
|
|
5588
6553
|
case "no such transport":
|
|
@@ -5618,7 +6583,7 @@ class VegaRtcManager {
|
|
|
5618
6583
|
}
|
|
5619
6584
|
|
|
5620
6585
|
async _internalSendMic() {
|
|
5621
|
-
|
|
6586
|
+
logger.info("_internalSendMic()");
|
|
5622
6587
|
|
|
5623
6588
|
this._micProducerPromise = (async () => {
|
|
5624
6589
|
try {
|
|
@@ -5647,21 +6612,23 @@ class VegaRtcManager {
|
|
|
5647
6612
|
currentPaused ? producer.pause() : producer.resume();
|
|
5648
6613
|
|
|
5649
6614
|
this._micProducer = producer;
|
|
6615
|
+
this._qualityMonitor.addProducer(this._selfId, producer.id);
|
|
5650
6616
|
|
|
5651
6617
|
producer.observer.once("close", () => {
|
|
5652
|
-
|
|
6618
|
+
logger.info('micProducer "close" event');
|
|
5653
6619
|
|
|
5654
6620
|
if (producer.appData.localClosed)
|
|
5655
6621
|
this._vegaConnection?.message("closeProducers", { producerIds: [producer.id] });
|
|
5656
6622
|
|
|
5657
6623
|
this._micProducer = null;
|
|
5658
6624
|
this._micProducerPromise = null;
|
|
6625
|
+
this._qualityMonitor.removeProducer(this._selfId, producer.id);
|
|
5659
6626
|
});
|
|
5660
6627
|
|
|
5661
6628
|
if (this._micTrack !== this._micProducer.track) await this._replaceMicTrack();
|
|
5662
6629
|
if (this._micPaused !== this._micProducer.paused) this._pauseResumeMic();
|
|
5663
6630
|
} catch (error) {
|
|
5664
|
-
|
|
6631
|
+
logger.error("micProducer failed:%o", error);
|
|
5665
6632
|
} finally {
|
|
5666
6633
|
this._micProducerPromise = null;
|
|
5667
6634
|
|
|
@@ -5675,7 +6642,7 @@ class VegaRtcManager {
|
|
|
5675
6642
|
}
|
|
5676
6643
|
|
|
5677
6644
|
async _internalSetupMicScore() {
|
|
5678
|
-
|
|
6645
|
+
logger.info("_internalSetupMicScore()");
|
|
5679
6646
|
|
|
5680
6647
|
this._micScoreProducerPromise = (async () => {
|
|
5681
6648
|
try {
|
|
@@ -5698,7 +6665,7 @@ class VegaRtcManager {
|
|
|
5698
6665
|
this._micScoreProducer = producer;
|
|
5699
6666
|
|
|
5700
6667
|
producer.observer.once("close", () => {
|
|
5701
|
-
|
|
6668
|
+
logger.info('micScoreProducer "close" event');
|
|
5702
6669
|
if (producer.appData.localClosed) {
|
|
5703
6670
|
this._vegaConnection?.message("closeDataProducers", { dataProducerIds: [producer.id] });
|
|
5704
6671
|
}
|
|
@@ -5707,7 +6674,7 @@ class VegaRtcManager {
|
|
|
5707
6674
|
this._micScoreProducerPromise = null;
|
|
5708
6675
|
});
|
|
5709
6676
|
} catch (error) {
|
|
5710
|
-
|
|
6677
|
+
logger.error("micScoreProducer failed:%o", error);
|
|
5711
6678
|
} finally {
|
|
5712
6679
|
this._micScoreProducerPromise = null;
|
|
5713
6680
|
|
|
@@ -5725,7 +6692,7 @@ class VegaRtcManager {
|
|
|
5725
6692
|
}
|
|
5726
6693
|
|
|
5727
6694
|
async _replaceMicTrack() {
|
|
5728
|
-
|
|
6695
|
+
logger.info("_replaceMicTrack()");
|
|
5729
6696
|
|
|
5730
6697
|
if (!this._micTrack || !this._micProducer || this._micProducer.closed) return;
|
|
5731
6698
|
|
|
@@ -5741,7 +6708,7 @@ class VegaRtcManager {
|
|
|
5741
6708
|
}
|
|
5742
6709
|
|
|
5743
6710
|
_pauseResumeMic() {
|
|
5744
|
-
|
|
6711
|
+
logger.info("_pauseResumeMic()");
|
|
5745
6712
|
|
|
5746
6713
|
if (!this._micProducer || this._micProducer.closed) return;
|
|
5747
6714
|
|
|
@@ -5768,7 +6735,7 @@ class VegaRtcManager {
|
|
|
5768
6735
|
}
|
|
5769
6736
|
|
|
5770
6737
|
async _sendMic(track) {
|
|
5771
|
-
|
|
6738
|
+
logger.info("_sendMic() [track:%o]", track);
|
|
5772
6739
|
|
|
5773
6740
|
this._micTrack = track;
|
|
5774
6741
|
|
|
@@ -5800,7 +6767,7 @@ class VegaRtcManager {
|
|
|
5800
6767
|
try {
|
|
5801
6768
|
this._micScoreProducer.send(JSON.stringify({ score }));
|
|
5802
6769
|
} catch (ex) {
|
|
5803
|
-
|
|
6770
|
+
logger.error("_sendMicScore failed [error:%o]", ex);
|
|
5804
6771
|
}
|
|
5805
6772
|
return;
|
|
5806
6773
|
}
|
|
@@ -5812,7 +6779,7 @@ class VegaRtcManager {
|
|
|
5812
6779
|
}
|
|
5813
6780
|
|
|
5814
6781
|
async _internalSendWebcam() {
|
|
5815
|
-
|
|
6782
|
+
logger.info("_internalSendWebcam()");
|
|
5816
6783
|
|
|
5817
6784
|
this._webcamProducerPromise = (async () => {
|
|
5818
6785
|
try {
|
|
@@ -5841,21 +6808,23 @@ class VegaRtcManager {
|
|
|
5841
6808
|
currentPaused ? producer.pause() : producer.resume();
|
|
5842
6809
|
|
|
5843
6810
|
this._webcamProducer = producer;
|
|
6811
|
+
this._qualityMonitor.addProducer(this._selfId, producer.id);
|
|
5844
6812
|
producer.observer.once("close", () => {
|
|
5845
|
-
|
|
6813
|
+
logger.info('webcamProducer "close" event');
|
|
5846
6814
|
|
|
5847
6815
|
if (producer.appData.localClosed)
|
|
5848
6816
|
this._vegaConnection?.message("closeProducers", { producerIds: [producer.id] });
|
|
5849
6817
|
|
|
5850
6818
|
this._webcamProducer = null;
|
|
5851
6819
|
this._webcamProducerPromise = null;
|
|
6820
|
+
this._qualityMonitor.removeProducer(this._selfId, producer.id);
|
|
5852
6821
|
});
|
|
5853
6822
|
|
|
5854
6823
|
// Has someone replaced the track?
|
|
5855
6824
|
if (this._webcamTrack !== this._webcamProducer.track) await this._replaceWebcamTrack();
|
|
5856
6825
|
if (this._webcamPaused !== this._webcamProducer.paused) this._pauseResumeWebcam();
|
|
5857
6826
|
} catch (error) {
|
|
5858
|
-
|
|
6827
|
+
logger.error("webcamProducer failed:%o", error);
|
|
5859
6828
|
} finally {
|
|
5860
6829
|
this._webcamProducerPromise = null;
|
|
5861
6830
|
|
|
@@ -5869,7 +6838,7 @@ class VegaRtcManager {
|
|
|
5869
6838
|
}
|
|
5870
6839
|
|
|
5871
6840
|
async _replaceWebcamTrack() {
|
|
5872
|
-
|
|
6841
|
+
logger.info("_replaceWebcamTrack()");
|
|
5873
6842
|
|
|
5874
6843
|
if (!this._webcamTrack || !this._webcamProducer || this._webcamProducer.closed) return;
|
|
5875
6844
|
|
|
@@ -5880,7 +6849,7 @@ class VegaRtcManager {
|
|
|
5880
6849
|
}
|
|
5881
6850
|
|
|
5882
6851
|
_pauseResumeWebcam() {
|
|
5883
|
-
|
|
6852
|
+
logger.info("_pauseResumeWebcam()");
|
|
5884
6853
|
|
|
5885
6854
|
if (!this._webcamProducer || this._webcamProducer.closed) return;
|
|
5886
6855
|
|
|
@@ -5904,7 +6873,7 @@ class VegaRtcManager {
|
|
|
5904
6873
|
}
|
|
5905
6874
|
|
|
5906
6875
|
async _sendWebcam(track) {
|
|
5907
|
-
|
|
6876
|
+
logger.info("_sendWebcam() [track:%o]", track);
|
|
5908
6877
|
|
|
5909
6878
|
this._webcamTrack = track;
|
|
5910
6879
|
|
|
@@ -5918,7 +6887,7 @@ class VegaRtcManager {
|
|
|
5918
6887
|
}
|
|
5919
6888
|
|
|
5920
6889
|
async _internalSendScreenVideo() {
|
|
5921
|
-
|
|
6890
|
+
logger.info("_internalSendScreenVideo()");
|
|
5922
6891
|
|
|
5923
6892
|
this._screenVideoProducerPromise = (async () => {
|
|
5924
6893
|
try {
|
|
@@ -5943,20 +6912,22 @@ class VegaRtcManager {
|
|
|
5943
6912
|
});
|
|
5944
6913
|
|
|
5945
6914
|
this._screenVideoProducer = producer;
|
|
6915
|
+
this._qualityMonitor.addProducer(this._selfId, producer.id);
|
|
5946
6916
|
producer.observer.once("close", () => {
|
|
5947
|
-
|
|
6917
|
+
logger.info('screenVideoProducer "close" event');
|
|
5948
6918
|
|
|
5949
6919
|
if (producer.appData.localClosed)
|
|
5950
6920
|
this._vegaConnection?.message("closeProducers", { producerIds: [producer.id] });
|
|
5951
6921
|
|
|
5952
6922
|
this._screenVideoProducer = null;
|
|
5953
6923
|
this._screenVideoProducerPromise = null;
|
|
6924
|
+
this._qualityMonitor.removeProducer(this._selfId, producer.id);
|
|
5954
6925
|
});
|
|
5955
6926
|
|
|
5956
6927
|
// Has someone replaced the track?
|
|
5957
6928
|
if (this._screenVideoTrack !== this._screenVideoProducer.track) await this._replaceScreenVideoTrack();
|
|
5958
6929
|
} catch (error) {
|
|
5959
|
-
|
|
6930
|
+
logger.error("screenVideoProducer failed:%o", error);
|
|
5960
6931
|
} finally {
|
|
5961
6932
|
this._screenVideoProducerPromise = null;
|
|
5962
6933
|
|
|
@@ -5970,7 +6941,7 @@ class VegaRtcManager {
|
|
|
5970
6941
|
}
|
|
5971
6942
|
|
|
5972
6943
|
async _replaceScreenVideoTrack() {
|
|
5973
|
-
|
|
6944
|
+
logger.info("_replaceScreenVideoTrack()");
|
|
5974
6945
|
|
|
5975
6946
|
if (!this._screenVideoTrack || !this._screenVideoProducer || this._screenVideoProducer.closed) return;
|
|
5976
6947
|
|
|
@@ -5981,7 +6952,7 @@ class VegaRtcManager {
|
|
|
5981
6952
|
}
|
|
5982
6953
|
|
|
5983
6954
|
async _sendScreenVideo(track) {
|
|
5984
|
-
|
|
6955
|
+
logger.info("_sendScreenVideo() [track:%o]", track);
|
|
5985
6956
|
|
|
5986
6957
|
this._screenVideoTrack = track;
|
|
5987
6958
|
|
|
@@ -5995,7 +6966,7 @@ class VegaRtcManager {
|
|
|
5995
6966
|
}
|
|
5996
6967
|
|
|
5997
6968
|
async _internalSendScreenAudio() {
|
|
5998
|
-
|
|
6969
|
+
logger.info("_internalSendScreenAudio()");
|
|
5999
6970
|
|
|
6000
6971
|
this._screenAudioProducerPromise = (async () => {
|
|
6001
6972
|
try {
|
|
@@ -6020,20 +6991,22 @@ class VegaRtcManager {
|
|
|
6020
6991
|
});
|
|
6021
6992
|
|
|
6022
6993
|
this._screenAudioProducer = producer;
|
|
6994
|
+
this._qualityMonitor.addProducer(this._selfId, producer.id);
|
|
6023
6995
|
producer.observer.once("close", () => {
|
|
6024
|
-
|
|
6996
|
+
logger.info('screenAudioProducer "close" event');
|
|
6025
6997
|
|
|
6026
6998
|
if (producer.appData.localClosed)
|
|
6027
6999
|
this._vegaConnection?.message("closeProducers", { producerIds: [producer.id] });
|
|
6028
7000
|
|
|
6029
7001
|
this._screenAudioProducer = null;
|
|
6030
7002
|
this._screenAudioProducerPromise = null;
|
|
7003
|
+
this._qualityMonitor.removeProducer(this._selfId, producer.id);
|
|
6031
7004
|
});
|
|
6032
7005
|
|
|
6033
7006
|
// Has someone replaced the track?
|
|
6034
7007
|
if (this._screenAudioTrack !== this._screenAudioProducer.track) await this._replaceScreenAudioTrack();
|
|
6035
7008
|
} catch (error) {
|
|
6036
|
-
|
|
7009
|
+
logger.error("screenAudioProducer failed:%o", error);
|
|
6037
7010
|
} finally {
|
|
6038
7011
|
this._screenAudioProducerPromise = null;
|
|
6039
7012
|
|
|
@@ -6047,7 +7020,7 @@ class VegaRtcManager {
|
|
|
6047
7020
|
}
|
|
6048
7021
|
|
|
6049
7022
|
async _replaceScreenAudioTrack() {
|
|
6050
|
-
|
|
7023
|
+
logger.info("_replaceScreenAudioTrack()");
|
|
6051
7024
|
|
|
6052
7025
|
if (!this._screenAudioTrack || !this._screenAudioProducer || this._screenAudioProducer.closed) return;
|
|
6053
7026
|
|
|
@@ -6058,7 +7031,7 @@ class VegaRtcManager {
|
|
|
6058
7031
|
}
|
|
6059
7032
|
|
|
6060
7033
|
async _sendScreenAudio(track) {
|
|
6061
|
-
|
|
7034
|
+
logger.info("_sendScreenAudio() [track:%o]", track);
|
|
6062
7035
|
|
|
6063
7036
|
this._screenAudioTrack = track;
|
|
6064
7037
|
|
|
@@ -6072,7 +7045,7 @@ class VegaRtcManager {
|
|
|
6072
7045
|
}
|
|
6073
7046
|
|
|
6074
7047
|
_stopProducer(producer) {
|
|
6075
|
-
|
|
7048
|
+
logger.info("_stopProducer()");
|
|
6076
7049
|
|
|
6077
7050
|
if (!producer || producer.closed) return;
|
|
6078
7051
|
|
|
@@ -6127,6 +7100,18 @@ class VegaRtcManager {
|
|
|
6127
7100
|
rtcStats.sendEvent("colocation_changed", { colocation });
|
|
6128
7101
|
}
|
|
6129
7102
|
|
|
7103
|
+
/**
|
|
7104
|
+
* This sends a signal to the SFU to pause all incoming video streams to the client.
|
|
7105
|
+
*
|
|
7106
|
+
* @param {boolean} audioOnly
|
|
7107
|
+
*/
|
|
7108
|
+
setAudioOnly(audioOnly) {
|
|
7109
|
+
this._vegaConnection?.message(audioOnly ? "enableAudioOnly" : "disableAudioOnly");
|
|
7110
|
+
}
|
|
7111
|
+
|
|
7112
|
+
// the track ids send by signal server for remote-initiated screenshares
|
|
7113
|
+
setRemoteScreenshareVideoTrackIds(/*remoteScreenshareVideoTrackIds*/) {}
|
|
7114
|
+
|
|
6130
7115
|
/**
|
|
6131
7116
|
* The unique identifier for this room session.
|
|
6132
7117
|
*
|
|
@@ -6146,7 +7131,7 @@ class VegaRtcManager {
|
|
|
6146
7131
|
* @param {string} eventClaim
|
|
6147
7132
|
*/
|
|
6148
7133
|
disconnect(clientIdOrStreamId, _activeBreakout, eventClaim) {
|
|
6149
|
-
|
|
7134
|
+
logger.info("disconnect() [clientIdOrStreamId:%s, eventClaim:%s]", clientIdOrStreamId, eventClaim);
|
|
6150
7135
|
|
|
6151
7136
|
if (this._clientStates.has(clientIdOrStreamId)) {
|
|
6152
7137
|
// In this case this is a disconnect from an actual client, not just a screen share.
|
|
@@ -6204,7 +7189,7 @@ class VegaRtcManager {
|
|
|
6204
7189
|
* @param {string} requestedByClientId
|
|
6205
7190
|
*/
|
|
6206
7191
|
removeStream(streamId, _stream, requestedByClientId) {
|
|
6207
|
-
|
|
7192
|
+
logger.info("removeStream() [streamId:%s, requestedByClientId:%s]", streamId, requestedByClientId);
|
|
6208
7193
|
|
|
6209
7194
|
this._emitToSignal(PROTOCOL_REQUESTS.STOP_SCREENSHARE, {
|
|
6210
7195
|
streamId: OUTBOUND_SCREEN_OUTBOUND_STREAM_ID,
|
|
@@ -6308,7 +7293,7 @@ class VegaRtcManager {
|
|
|
6308
7293
|
* @param {boolean} enabled
|
|
6309
7294
|
*/
|
|
6310
7295
|
stopOrResumeAudio(stream, enabled) {
|
|
6311
|
-
|
|
7296
|
+
logger.info("stopOrResumeAudio() [enabled:%s]", enabled);
|
|
6312
7297
|
|
|
6313
7298
|
this._micPaused = !enabled;
|
|
6314
7299
|
|
|
@@ -6334,7 +7319,7 @@ class VegaRtcManager {
|
|
|
6334
7319
|
* @param {boolean} enabled
|
|
6335
7320
|
*/
|
|
6336
7321
|
stopOrResumeVideo(localStream, enable) {
|
|
6337
|
-
|
|
7322
|
+
logger.info("stopOrResumeVideo() [enable:%s]", enable);
|
|
6338
7323
|
|
|
6339
7324
|
this._webcamPaused = !enable;
|
|
6340
7325
|
|
|
@@ -6397,7 +7382,7 @@ class VegaRtcManager {
|
|
|
6397
7382
|
* }} streamOptions
|
|
6398
7383
|
*/
|
|
6399
7384
|
acceptNewStream({ streamId, clientId }) {
|
|
6400
|
-
|
|
7385
|
+
logger.info("acceptNewStream()", { streamId, clientId });
|
|
6401
7386
|
|
|
6402
7387
|
// make sure we have rtcStats connection
|
|
6403
7388
|
this.rtcStatsReconnect();
|
|
@@ -6428,7 +7413,7 @@ class VegaRtcManager {
|
|
|
6428
7413
|
* }} size
|
|
6429
7414
|
*/
|
|
6430
7415
|
updateStreamResolution(streamId, _ignored, { width, height }) {
|
|
6431
|
-
|
|
7416
|
+
logger.info("updateStreamResolution()", { streamId, width, height });
|
|
6432
7417
|
|
|
6433
7418
|
const consumerId = this._streamIdToVideoConsumerId.get(streamId);
|
|
6434
7419
|
const consumer = this._consumers.get(consumerId);
|
|
@@ -6561,18 +7546,22 @@ class VegaRtcManager {
|
|
|
6561
7546
|
return this._onDataConsumerClosed(data);
|
|
6562
7547
|
case "dominantSpeaker":
|
|
6563
7548
|
return this._onDominantSpeaker(data);
|
|
7549
|
+
case "consumerScore":
|
|
7550
|
+
return this._onConsumerScore(data);
|
|
7551
|
+
case "producerScore":
|
|
7552
|
+
return this._onProducerScore(data);
|
|
6564
7553
|
default:
|
|
6565
|
-
|
|
7554
|
+
logger.info(`unknown message method "${method}"`);
|
|
6566
7555
|
return;
|
|
6567
7556
|
}
|
|
6568
7557
|
})
|
|
6569
7558
|
.catch((error) => {
|
|
6570
|
-
|
|
7559
|
+
logger.error('"message" failed [error:%o]', error);
|
|
6571
7560
|
});
|
|
6572
7561
|
}
|
|
6573
7562
|
|
|
6574
7563
|
async _onConsumerReady(options) {
|
|
6575
|
-
|
|
7564
|
+
logger.info("_onConsumerReady()", { id: options.id, producerId: options.producerId });
|
|
6576
7565
|
|
|
6577
7566
|
const consumer = await this._receiveTransport.consume(options);
|
|
6578
7567
|
|
|
@@ -6581,8 +7570,10 @@ class VegaRtcManager {
|
|
|
6581
7570
|
consumer.appData.spatialLayer = 2;
|
|
6582
7571
|
|
|
6583
7572
|
this._consumers.set(consumer.id, consumer);
|
|
7573
|
+
this._qualityMonitor.addConsumer(consumer.appData.sourceClientId, consumer.id);
|
|
6584
7574
|
consumer.observer.once("close", () => {
|
|
6585
7575
|
this._consumers.delete(consumer.id);
|
|
7576
|
+
this._qualityMonitor.removeConsumer(consumer.appData.sourceClientId, consumer.id);
|
|
6586
7577
|
|
|
6587
7578
|
this._consumerClosedCleanup(consumer);
|
|
6588
7579
|
});
|
|
@@ -6593,7 +7584,7 @@ class VegaRtcManager {
|
|
|
6593
7584
|
// Legacy Chrome API
|
|
6594
7585
|
consumer.rtpReceiver.playoutDelayHint = MEDIA_JITTER_BUFFER_TARGET / 1000; // seconds
|
|
6595
7586
|
} catch (error) {
|
|
6596
|
-
|
|
7587
|
+
logger.error("Error during setting jitter buffer target:", error);
|
|
6597
7588
|
}
|
|
6598
7589
|
}
|
|
6599
7590
|
|
|
@@ -6626,13 +7617,13 @@ class VegaRtcManager {
|
|
|
6626
7617
|
}
|
|
6627
7618
|
|
|
6628
7619
|
async _onConsumerClosed({ consumerId, reason }) {
|
|
6629
|
-
|
|
7620
|
+
logger.info("_onConsumerClosed()", { consumerId, reason });
|
|
6630
7621
|
|
|
6631
7622
|
this._consumers.get(consumerId)?.close();
|
|
6632
7623
|
}
|
|
6633
7624
|
|
|
6634
7625
|
_onConsumerPaused({ consumerId }) {
|
|
6635
|
-
|
|
7626
|
+
logger.info("_onConsumerPaused()", { consumerId });
|
|
6636
7627
|
|
|
6637
7628
|
const consumer = this._consumers.get(consumerId);
|
|
6638
7629
|
|
|
@@ -6643,7 +7634,7 @@ class VegaRtcManager {
|
|
|
6643
7634
|
}
|
|
6644
7635
|
|
|
6645
7636
|
_onConsumerResumed({ consumerId }) {
|
|
6646
|
-
|
|
7637
|
+
logger.info("_onConsumerResumed()", { consumerId });
|
|
6647
7638
|
|
|
6648
7639
|
const consumer = this._consumers.get(consumerId);
|
|
6649
7640
|
|
|
@@ -6656,8 +7647,30 @@ class VegaRtcManager {
|
|
|
6656
7647
|
}
|
|
6657
7648
|
}
|
|
6658
7649
|
|
|
7650
|
+
_onConsumerScore({ consumerId, kind, score }) {
|
|
7651
|
+
logger.info("_onConsumerScore()", { consumerId, kind, score });
|
|
7652
|
+
const {
|
|
7653
|
+
appData: { sourceClientId },
|
|
7654
|
+
} = this._consumers.get(consumerId) || { appData: {} };
|
|
7655
|
+
|
|
7656
|
+
if (sourceClientId) {
|
|
7657
|
+
this._qualityMonitor.addConsumerScore(sourceClientId, consumerId, kind, score);
|
|
7658
|
+
}
|
|
7659
|
+
}
|
|
7660
|
+
|
|
7661
|
+
_onProducerScore({ producerId, kind, score }) {
|
|
7662
|
+
logger.info("_onProducerScore()", { producerId, kind, score });
|
|
7663
|
+
[this._micProducer, this._webcamProducer, this._screenVideoProducer, this._screenAudioProducer].forEach(
|
|
7664
|
+
(producer) => {
|
|
7665
|
+
if (producer?.id === producerId) {
|
|
7666
|
+
this._qualityMonitor.addProducerScore(this._selfId, producerId, kind, score);
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
);
|
|
7670
|
+
}
|
|
7671
|
+
|
|
6659
7672
|
async _onDataConsumerReady(options) {
|
|
6660
|
-
|
|
7673
|
+
logger.info("_onDataConsumerReady()", { id: options.id, producerId: options.producerId });
|
|
6661
7674
|
const consumer = await this._receiveTransport.consumeData(options);
|
|
6662
7675
|
|
|
6663
7676
|
this._dataConsumers.set(consumer.id, consumer);
|
|
@@ -6683,7 +7696,7 @@ class VegaRtcManager {
|
|
|
6683
7696
|
}
|
|
6684
7697
|
|
|
6685
7698
|
async _onDataConsumerClosed({ dataConsumerId, reason }) {
|
|
6686
|
-
|
|
7699
|
+
logger.info("_onDataConsumerClosed()", { dataConsumerId, reason });
|
|
6687
7700
|
const consumer = this._dataConsumers.get(dataConsumerId);
|
|
6688
7701
|
consumer?.close();
|
|
6689
7702
|
}
|
|
@@ -6739,7 +7752,7 @@ class VegaRtcManager {
|
|
|
6739
7752
|
} = clientState;
|
|
6740
7753
|
|
|
6741
7754
|
// Need to pause/resume any consumers that are part of a stream that has been
|
|
6742
|
-
// accepted or
|
|
7755
|
+
// accepted or disconnected by the PWA
|
|
6743
7756
|
const toPauseConsumers = [];
|
|
6744
7757
|
const toResumeConsumers = [];
|
|
6745
7758
|
|
|
@@ -6841,6 +7854,10 @@ class VegaRtcManager {
|
|
|
6841
7854
|
setMicAnalyserParams(params) {
|
|
6842
7855
|
this._micAnalyser?.setParams(params);
|
|
6843
7856
|
}
|
|
7857
|
+
|
|
7858
|
+
hasClient(clientId) {
|
|
7859
|
+
return this._clientStates.has(clientId);
|
|
7860
|
+
}
|
|
6844
7861
|
}
|
|
6845
7862
|
|
|
6846
7863
|
class RtcManagerDispatcher {
|
|
@@ -6849,7 +7866,16 @@ class RtcManagerDispatcher {
|
|
|
6849
7866
|
this.currentManager = null;
|
|
6850
7867
|
serverSocket.on(PROTOCOL_RESPONSES.ROOM_JOINED, ({ room, selfId, error, eventClaim }) => {
|
|
6851
7868
|
if (error) return; // ignore error responses which lack room
|
|
6852
|
-
const config = {
|
|
7869
|
+
const config = {
|
|
7870
|
+
selfId,
|
|
7871
|
+
room,
|
|
7872
|
+
emitter,
|
|
7873
|
+
serverSocket,
|
|
7874
|
+
webrtcProvider,
|
|
7875
|
+
features,
|
|
7876
|
+
eventClaim,
|
|
7877
|
+
deviceHandlerFactory: features?.deviceHandlerFactory,
|
|
7878
|
+
};
|
|
6853
7879
|
const isSfu = !!room.sfuServer;
|
|
6854
7880
|
if (this.currentManager) {
|
|
6855
7881
|
if (this.currentManager.isInitializedWith({ selfId, roomName: room.name, isSfu })) {
|
|
@@ -6870,6 +7896,7 @@ class RtcManagerDispatcher {
|
|
|
6870
7896
|
rtcManager.setupSocketListeners();
|
|
6871
7897
|
emitter.emit(EVENTS.RTC_MANAGER_CREATED, { rtcManager });
|
|
6872
7898
|
this.currentManager = rtcManager;
|
|
7899
|
+
serverSocket.setRtcManager(rtcManager);
|
|
6873
7900
|
});
|
|
6874
7901
|
}
|
|
6875
7902
|
|
|
@@ -7794,7 +8821,7 @@ var localStorage$1 = localStorage;
|
|
|
7794
8821
|
const events = {
|
|
7795
8822
|
CREDENTIALS_SAVED: "credentials_saved",
|
|
7796
8823
|
};
|
|
7797
|
-
class CredentialsService extends EventEmitter
|
|
8824
|
+
class CredentialsService extends EventEmitter {
|
|
7798
8825
|
constructor({ deviceService, credentialsStore, }) {
|
|
7799
8826
|
super();
|
|
7800
8827
|
this._deviceService = deviceService;
|
|
@@ -8432,7 +9459,7 @@ class LocalParticipant extends RoomParticipant {
|
|
|
8432
9459
|
}
|
|
8433
9460
|
}
|
|
8434
9461
|
|
|
8435
|
-
const sdkVersion = "0.2.0
|
|
9462
|
+
const sdkVersion = "0.2.0";
|
|
8436
9463
|
|
|
8437
9464
|
const defaultSubdomainPattern = /^(?:([^.]+)[.])?((:?[^.]+[.]){1,}[^.]+)$/;
|
|
8438
9465
|
const localstackPattern = /^(?:([^.]+)-)?(ip-[^.]*[.](?:hereby[.]dev|rfc1918[.]disappear[.]at)(?::\d+|))$/;
|