@whereby.com/core 0.3.0 → 0.5.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.d.ts +53 -10
- package/dist/index.js +1184 -1144
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { createAsyncThunk, createListenerMiddleware, addListener, createSlice, createAction, createSelector, isAnyOf, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
2
2
|
import { io } from 'socket.io-client';
|
|
3
|
-
import
|
|
3
|
+
import adapterRaw from 'webrtc-adapter';
|
|
4
4
|
import EventEmitter, { EventEmitter as EventEmitter$1 } from 'events';
|
|
5
5
|
import { d as debounce } from './debounce-B-cWYxqK.js';
|
|
6
|
+
import { v4 as v4$1 } from 'uuid';
|
|
6
7
|
import SDPUtils from 'sdp';
|
|
7
8
|
import * as sdpTransform from 'sdp-transform';
|
|
8
|
-
import { v4 as v4$1 } from 'uuid';
|
|
9
9
|
import { Address6 } from 'ip-address';
|
|
10
10
|
import checkIp from 'check-ip';
|
|
11
11
|
import validate from 'uuid-validate';
|
|
12
12
|
import { detectDevice, Device } from 'mediasoup-client';
|
|
13
|
+
import { Chrome111 } from 'mediasoup-client/lib/handlers/Chrome111.js';
|
|
13
14
|
import nodeBtoa from 'btoa';
|
|
14
15
|
import axios from 'axios';
|
|
15
16
|
|
|
@@ -42,6 +43,7 @@ const createReactor = (selectors, callback) => {
|
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
const initialState$c = {
|
|
46
|
+
isNodeSdk: false,
|
|
45
47
|
wantsToJoin: false,
|
|
46
48
|
roomName: null,
|
|
47
49
|
roomKey: null,
|
|
@@ -75,6 +77,7 @@ const selectAppRoomKey = (state) => state.app.roomKey;
|
|
|
75
77
|
const selectAppDisplayName = (state) => state.app.displayName;
|
|
76
78
|
const selectAppSdkVersion = (state) => state.app.sdkVersion;
|
|
77
79
|
const selectAppExternalId = (state) => state.app.externalId;
|
|
80
|
+
const selectAppIsNodeSdk = (state) => state.app.isNodeSdk;
|
|
78
81
|
|
|
79
82
|
function createSignalEventAction(name) {
|
|
80
83
|
return createAction(`signalConnection/event/${name}`);
|
|
@@ -386,7 +389,7 @@ const PROTOCOL_EVENTS = {
|
|
|
386
389
|
MEDIA_QUALITY_CHANGED: "media_quality_changed",
|
|
387
390
|
};
|
|
388
391
|
|
|
389
|
-
const logger$
|
|
392
|
+
const logger$8 = new Logger();
|
|
390
393
|
|
|
391
394
|
class ReconnectManager extends EventEmitter {
|
|
392
395
|
constructor(socket) {
|
|
@@ -501,7 +504,7 @@ class ReconnectManager extends EventEmitter {
|
|
|
501
504
|
|
|
502
505
|
client.mergeWithOldClientState = true;
|
|
503
506
|
} catch (error) {
|
|
504
|
-
logger$
|
|
507
|
+
logger$8.error("Failed to evaluate if we should merge client state %o", error);
|
|
505
508
|
this.metrics.evaluationFailed++;
|
|
506
509
|
}
|
|
507
510
|
});
|
|
@@ -538,7 +541,7 @@ class ReconnectManager extends EventEmitter {
|
|
|
538
541
|
const client = this._clients[clientId];
|
|
539
542
|
|
|
540
543
|
if (!client) {
|
|
541
|
-
logger$
|
|
544
|
+
logger$8.warn(`client ${clientId} not found`);
|
|
542
545
|
return;
|
|
543
546
|
}
|
|
544
547
|
|
|
@@ -708,6 +711,8 @@ class ReconnectManager extends EventEmitter {
|
|
|
708
711
|
}
|
|
709
712
|
}
|
|
710
713
|
|
|
714
|
+
const adapter$6 = adapterRaw.default ?? adapterRaw;
|
|
715
|
+
|
|
711
716
|
const DEFAULT_SOCKET_PATH = "/protocol/socket.io/v4";
|
|
712
717
|
|
|
713
718
|
const NOOP_KEEPALIVE_INTERVAL = 2000;
|
|
@@ -737,7 +742,7 @@ class ServerSocket {
|
|
|
737
742
|
// safari doesn't support cross doamin cookies making load-balancer stickiness not work
|
|
738
743
|
// and if socket.io reconnects to another signal instance with polling it will fail
|
|
739
744
|
// remove if we move signal to a whereby.com subdomain
|
|
740
|
-
if (adapter.browserDetails.browser !== "safari") delete this._wasConnectedUsingWebsocket;
|
|
745
|
+
if (adapter$6.browserDetails.browser !== "safari") delete this._wasConnectedUsingWebsocket;
|
|
741
746
|
} else {
|
|
742
747
|
this._socket.io.opts.transports = ["websocket", "polling"];
|
|
743
748
|
}
|
|
@@ -1108,7 +1113,9 @@ const selectCloudRecordingStartedAt = (state) => state.cloudRecording.startedAt;
|
|
|
1108
1113
|
const selectCloudRecordingError = (state) => state.cloudRecording.error;
|
|
1109
1114
|
const selectIsCloudRecording = (state) => state.cloudRecording.isRecording;
|
|
1110
1115
|
|
|
1111
|
-
const
|
|
1116
|
+
const adapter$5 = adapterRaw.default ?? adapterRaw;
|
|
1117
|
+
|
|
1118
|
+
const isSafari = adapter$5.browserDetails.browser === "safari";
|
|
1112
1119
|
|
|
1113
1120
|
// Expects format 640x360@25, returns [width, height, fps]
|
|
1114
1121
|
const parseResolution = (res) => res.split(/[^\d]/g).map((n) => parseInt(n, 10));
|
|
@@ -1119,7 +1126,6 @@ const parseResolution = (res) => res.split(/[^\d]/g).map((n) => parseInt(n, 10))
|
|
|
1119
1126
|
function getMediaConstraints({
|
|
1120
1127
|
disableAEC,
|
|
1121
1128
|
disableAGC,
|
|
1122
|
-
fps24,
|
|
1123
1129
|
hd,
|
|
1124
1130
|
lax,
|
|
1125
1131
|
lowDataMode,
|
|
@@ -1130,7 +1136,6 @@ function getMediaConstraints({
|
|
|
1130
1136
|
}) {
|
|
1131
1137
|
let HIGH_HEIGHT = 480;
|
|
1132
1138
|
let LOW_HEIGHT = 240;
|
|
1133
|
-
let NON_STANDARD_FPS = 0;
|
|
1134
1139
|
|
|
1135
1140
|
if (hd) {
|
|
1136
1141
|
// respect user choice, but default to HD for pro, and SD for free
|
|
@@ -1145,18 +1150,14 @@ function getMediaConstraints({
|
|
|
1145
1150
|
}
|
|
1146
1151
|
}
|
|
1147
1152
|
|
|
1148
|
-
// Set framerate to 24 to increase quality/bandwidth
|
|
1149
|
-
if (fps24) NON_STANDARD_FPS = 24;
|
|
1150
|
-
|
|
1151
|
-
// Set framerate for low data, but only for non-simulcast
|
|
1152
|
-
if (lowDataMode && !simulcast) NON_STANDARD_FPS = 15;
|
|
1153
|
-
|
|
1154
1153
|
const constraints = {
|
|
1155
1154
|
audio: { ...(preferredDeviceIds.audioId && { deviceId: preferredDeviceIds.audioId }) },
|
|
1156
1155
|
video: {
|
|
1157
1156
|
...(preferredDeviceIds.videoId ? { deviceId: preferredDeviceIds.videoId } : { facingMode: "user" }),
|
|
1158
1157
|
height: lowDataMode ? LOW_HEIGHT : HIGH_HEIGHT,
|
|
1159
|
-
|
|
1158
|
+
// Set a lower frame rate (15fps) for low data, but only for non-simulcast.
|
|
1159
|
+
// Otherwise use 24fps to increase quality/bandwidth.
|
|
1160
|
+
frameRate: lowDataMode && !simulcast ? 15 : 24,
|
|
1160
1161
|
},
|
|
1161
1162
|
};
|
|
1162
1163
|
if (lax) {
|
|
@@ -1289,7 +1290,7 @@ var assert_1 = assert;
|
|
|
1289
1290
|
|
|
1290
1291
|
var assert$1 = /*@__PURE__*/getDefaultExportFromCjs(assert_1);
|
|
1291
1292
|
|
|
1292
|
-
const logger$
|
|
1293
|
+
const logger$7 = new Logger();
|
|
1293
1294
|
|
|
1294
1295
|
const isMobile = /mobi/i.test(navigator.userAgent);
|
|
1295
1296
|
|
|
@@ -1310,7 +1311,7 @@ function getUserMedia(constraints) {
|
|
|
1310
1311
|
|
|
1311
1312
|
return navigator.mediaDevices.getUserMedia(constraints).catch((error) => {
|
|
1312
1313
|
const message = `${error}, ${JSON.stringify(constraints, null, 2)}`;
|
|
1313
|
-
logger$
|
|
1314
|
+
logger$7.error(`getUserMedia ${message}`);
|
|
1314
1315
|
throw error;
|
|
1315
1316
|
});
|
|
1316
1317
|
}
|
|
@@ -1496,7 +1497,7 @@ async function getStream(constraintOpt, { replaceStream, fallback = true } = {})
|
|
|
1496
1497
|
getConstraints({ ...constraintOpt, options: { ...constraintOpt.options, lax: true } })
|
|
1497
1498
|
);
|
|
1498
1499
|
} catch (e2) {
|
|
1499
|
-
logger$
|
|
1500
|
+
logger$7.warn(`Tried getting stream again with laxer constraints, but failed: ${"" + e2}`);
|
|
1500
1501
|
}
|
|
1501
1502
|
// Message often hints at which was the problem, let's use that
|
|
1502
1503
|
const errMsg = ("" + e).toLowerCase();
|
|
@@ -1505,7 +1506,7 @@ async function getStream(constraintOpt, { replaceStream, fallback = true } = {})
|
|
|
1505
1506
|
try {
|
|
1506
1507
|
stream = await getUserMedia(getConstraints({ ...constraintOpt, [problemWith]: null }));
|
|
1507
1508
|
} catch (e2) {
|
|
1508
|
-
logger$
|
|
1509
|
+
logger$7.warn(`Re-tried ${problemWith} with no constraints, but failed: ${"" + e2}`);
|
|
1509
1510
|
}
|
|
1510
1511
|
}
|
|
1511
1512
|
if (!stream) {
|
|
@@ -1516,7 +1517,7 @@ async function getStream(constraintOpt, { replaceStream, fallback = true } = {})
|
|
|
1516
1517
|
try {
|
|
1517
1518
|
stream = await getUserMedia(getConstraints({ ...constraintOpt, [kind]: false }));
|
|
1518
1519
|
} catch (e2) {
|
|
1519
|
-
logger$
|
|
1520
|
+
logger$7.warn(`Re-tried without ${kind}, but failed: ${"" + e2}`);
|
|
1520
1521
|
}
|
|
1521
1522
|
if (stream) break;
|
|
1522
1523
|
}
|
|
@@ -1972,8 +1973,8 @@ const selectIsLocalMediaStarting = createSelector(selectLocalMediaStatus, (statu
|
|
|
1972
1973
|
const selectCameraDevices = createSelector(selectLocalMediaDevices, selectBusyDeviceIds, (devices, busyDeviceIds) => devices.filter((d) => d.kind === "videoinput").filter((d) => !busyDeviceIds.includes(d.deviceId)));
|
|
1973
1974
|
const selectMicrophoneDevices = createSelector(selectLocalMediaDevices, selectBusyDeviceIds, (devices, busyDeviceIds) => devices.filter((d) => d.kind === "audioinput").filter((d) => !busyDeviceIds.includes(d.deviceId)));
|
|
1974
1975
|
const selectSpeakerDevices = createSelector(selectLocalMediaDevices, (devices) => devices.filter((d) => d.kind === "audiooutput"));
|
|
1975
|
-
const selectLocalMediaShouldStartWithOptions = createSelector(selectAppWantsToJoin, selectLocalMediaStatus, selectLocalMediaOptions, (appWantsToJoin, localMediaStatus, localMediaOptions) => {
|
|
1976
|
-
if (appWantsToJoin && localMediaStatus === "" && localMediaOptions) {
|
|
1976
|
+
const selectLocalMediaShouldStartWithOptions = createSelector(selectAppWantsToJoin, selectLocalMediaStatus, selectLocalMediaOptions, selectAppIsNodeSdk, (appWantsToJoin, localMediaStatus, localMediaOptions, isNodeSdk) => {
|
|
1977
|
+
if (appWantsToJoin && localMediaStatus === "" && !isNodeSdk && localMediaOptions) {
|
|
1977
1978
|
return localMediaOptions;
|
|
1978
1979
|
}
|
|
1979
1980
|
});
|
|
@@ -2494,6 +2495,9 @@ const roomConnectionSlice = createSlice({
|
|
|
2494
2495
|
if (error === "room_locked" && isLocked) {
|
|
2495
2496
|
return Object.assign(Object.assign({}, state), { status: "room_locked" });
|
|
2496
2497
|
}
|
|
2498
|
+
if (error) {
|
|
2499
|
+
return Object.assign(Object.assign({}, state), { status: "disconnected", error });
|
|
2500
|
+
}
|
|
2497
2501
|
return Object.assign(Object.assign({}, state), { status: "connected", session: (_b = (_a = action.payload.room) === null || _a === void 0 ? void 0 : _a.session) !== null && _b !== void 0 ? _b : null });
|
|
2498
2502
|
});
|
|
2499
2503
|
builder.addCase(signalEvents.newClient, (state, action) => {
|
|
@@ -2581,8 +2585,15 @@ const selectRoomConnectionRaw = (state) => state.roomConnection;
|
|
|
2581
2585
|
const selectRoomConnectionSession = (state) => state.roomConnection.session;
|
|
2582
2586
|
const selectRoomConnectionSessionId = (state) => { var _a; return (_a = state.roomConnection.session) === null || _a === void 0 ? void 0 : _a.id; };
|
|
2583
2587
|
const selectRoomConnectionStatus = (state) => state.roomConnection.status;
|
|
2584
|
-
const
|
|
2585
|
-
|
|
2588
|
+
const selectRoomConnectionError = (state) => state.roomConnection.error;
|
|
2589
|
+
const selectShouldConnectRoom = createSelector([
|
|
2590
|
+
selectOrganizationId,
|
|
2591
|
+
selectRoomConnectionStatus,
|
|
2592
|
+
selectSignalConnectionDeviceIdentified,
|
|
2593
|
+
selectLocalMediaStatus,
|
|
2594
|
+
selectAppIsNodeSdk,
|
|
2595
|
+
], (hasOrganizationIdFetched, roomConnectionStatus, signalConnectionDeviceIdentified, localMediaStatus, isNodeSdk) => {
|
|
2596
|
+
if ((localMediaStatus === "started" || isNodeSdk) &&
|
|
2586
2597
|
signalConnectionDeviceIdentified &&
|
|
2587
2598
|
!!hasOrganizationIdFetched &&
|
|
2588
2599
|
["initializing", "reconnect"].includes(roomConnectionStatus)) {
|
|
@@ -2614,161 +2625,594 @@ startAppListening({
|
|
|
2614
2625
|
},
|
|
2615
2626
|
});
|
|
2616
2627
|
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
};
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
CONNECTING: "connecting",
|
|
2630
|
-
CONNECTION_FAILED: "connection_failed",
|
|
2631
|
-
CONNECTION_SUCCESSFUL: "connection_successful",
|
|
2632
|
-
CONNECTION_DISCONNECTED: "connection_disconnected",
|
|
2633
|
-
};
|
|
2634
|
-
|
|
2635
|
-
const CAMERA_STREAM_ID$2 = "0";
|
|
2636
|
-
|
|
2637
|
-
const STREAM_TYPES = {
|
|
2638
|
-
CAMERA: "camera",
|
|
2639
|
-
SCREEN_SHARE: "screen_share",
|
|
2640
|
-
};
|
|
2641
|
-
|
|
2642
|
-
class RtcStream {
|
|
2643
|
-
constructor(id, type) {
|
|
2644
|
-
assert$1.notEqual(id, undefined, "id is required");
|
|
2645
|
-
assert$1.notEqual(type, undefined, "type is required");
|
|
2646
|
-
|
|
2647
|
-
this.id = "" + id;
|
|
2648
|
-
this.type = type;
|
|
2649
|
-
|
|
2650
|
-
this.isEnabled = true;
|
|
2651
|
-
this.hasSupportForAutoSuperSize = false;
|
|
2652
|
-
this.isAudioEnabled = true;
|
|
2653
|
-
this.isVideoEnabled = true;
|
|
2654
|
-
this.status = TYPES.CONNECTING;
|
|
2655
|
-
}
|
|
2628
|
+
// transforms a maplike to an object. Mostly for getStats +
|
|
2629
|
+
// JSON.parse(JSON.stringify())
|
|
2630
|
+
function map2obj(m) {
|
|
2631
|
+
if (!m.entries) {
|
|
2632
|
+
return m;
|
|
2633
|
+
}
|
|
2634
|
+
var o = {};
|
|
2635
|
+
m.forEach(function(v, k) {
|
|
2636
|
+
o[k] = v;
|
|
2637
|
+
});
|
|
2638
|
+
return o;
|
|
2639
|
+
}
|
|
2656
2640
|
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2641
|
+
// apply a delta compression to the stats report. Reduces size by ~90%.
|
|
2642
|
+
// To reduce further, report keys could be compressed.
|
|
2643
|
+
function deltaCompression(oldStats, newStats) {
|
|
2644
|
+
newStats = JSON.parse(JSON.stringify(newStats));
|
|
2645
|
+
Object.keys(newStats).forEach(function(id) {
|
|
2646
|
+
var report = newStats[id];
|
|
2647
|
+
delete report.id;
|
|
2648
|
+
if (!oldStats[id]) {
|
|
2649
|
+
return;
|
|
2663
2650
|
}
|
|
2651
|
+
Object.keys(report).forEach(function(name) {
|
|
2652
|
+
if (report[name] === oldStats[id][name]) {
|
|
2653
|
+
delete newStats[id][name];
|
|
2654
|
+
}
|
|
2655
|
+
if (Object.keys(report).length === 0) {
|
|
2656
|
+
delete newStats[id];
|
|
2657
|
+
} else if (Object.keys(report).length === 1 && report.timestamp) {
|
|
2658
|
+
delete newStats[id];
|
|
2659
|
+
}
|
|
2660
|
+
});
|
|
2661
|
+
});
|
|
2664
2662
|
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2663
|
+
var timestamp = -Infinity;
|
|
2664
|
+
Object.keys(newStats).forEach(function(id) {
|
|
2665
|
+
var report = newStats[id];
|
|
2666
|
+
if (report.timestamp > timestamp) {
|
|
2667
|
+
timestamp = report.timestamp;
|
|
2668
2668
|
}
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
}
|
|
2675
|
-
this.stream.getVideoTracks().forEach((track) => {
|
|
2676
|
-
track.enabled = isEnabled;
|
|
2677
|
-
});
|
|
2669
|
+
});
|
|
2670
|
+
Object.keys(newStats).forEach(function(id) {
|
|
2671
|
+
var report = newStats[id];
|
|
2672
|
+
if (report.timestamp === timestamp) {
|
|
2673
|
+
report.timestamp = 0;
|
|
2678
2674
|
}
|
|
2675
|
+
});
|
|
2676
|
+
newStats.timestamp = timestamp;
|
|
2677
|
+
return newStats;
|
|
2678
|
+
}
|
|
2679
2679
|
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2680
|
+
function dumpStream(stream) {
|
|
2681
|
+
return {
|
|
2682
|
+
id: stream.id,
|
|
2683
|
+
tracks: stream.getTracks().map(function(track) {
|
|
2684
|
+
return {
|
|
2685
|
+
id: track.id, // unique identifier (GUID) for the track
|
|
2686
|
+
kind: track.kind, // `audio` or `video`
|
|
2687
|
+
label: track.label, // identified the track source
|
|
2688
|
+
enabled: track.enabled, // application can control it
|
|
2689
|
+
muted: track.muted, // application cannot control it (read-only)
|
|
2690
|
+
readyState: track.readyState, // `live` or `ended`
|
|
2691
|
+
};
|
|
2692
|
+
}),
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2689
2695
|
|
|
2690
|
-
|
|
2691
|
-
|
|
2696
|
+
/*
|
|
2697
|
+
function filterBoringStats(results) {
|
|
2698
|
+
Object.keys(results).forEach(function(id) {
|
|
2699
|
+
switch (results[id].type) {
|
|
2700
|
+
case 'certificate':
|
|
2701
|
+
case 'codec':
|
|
2702
|
+
delete results[id];
|
|
2703
|
+
break;
|
|
2704
|
+
default:
|
|
2705
|
+
// noop
|
|
2692
2706
|
}
|
|
2707
|
+
});
|
|
2708
|
+
return results;
|
|
2709
|
+
}
|
|
2693
2710
|
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2711
|
+
function removeTimestamps(results) {
|
|
2712
|
+
// FIXME: does not work in FF since the timestamp can't be deleted.
|
|
2713
|
+
Object.keys(results).forEach(function(id) {
|
|
2714
|
+
delete results[id].timestamp;
|
|
2715
|
+
});
|
|
2716
|
+
return results;
|
|
2700
2717
|
}
|
|
2718
|
+
*/
|
|
2701
2719
|
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
if (
|
|
2712
|
-
adapter.browserDetails.browser !== "chrome" ||
|
|
2713
|
-
adapter.browserDetails.browser < 58 || // legacy getStats is no longer supported.
|
|
2714
|
-
pc.signalingState === "closed"
|
|
2715
|
-
) {
|
|
2716
|
-
return Promise.resolve(false);
|
|
2720
|
+
var rtcstats = function(trace, getStatsInterval, prefixesToWrap) {
|
|
2721
|
+
var peerconnectioncounter = 0;
|
|
2722
|
+
var isFirefox = !!window.mozRTCPeerConnection;
|
|
2723
|
+
var isEdge = !!window.RTCIceGatherer;
|
|
2724
|
+
var prevById = {};
|
|
2725
|
+
|
|
2726
|
+
prefixesToWrap.forEach(function(prefix) {
|
|
2727
|
+
if (!window[prefix + 'RTCPeerConnection']) {
|
|
2728
|
+
return;
|
|
2717
2729
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
) {
|
|
2728
|
-
if (report.bytesSent === 0) {
|
|
2729
|
-
microphoneFailed = "outbound";
|
|
2730
|
-
}
|
|
2731
|
-
} else if (
|
|
2732
|
-
report.type === "inbound-rtp" &&
|
|
2733
|
-
(report.kind === "audio" || report.mediaType === "audio") &&
|
|
2734
|
-
receivingAudio
|
|
2735
|
-
) {
|
|
2736
|
-
if (report.bytesReceived === 0) {
|
|
2737
|
-
microphoneFailed = "inbound";
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
|
-
});
|
|
2741
|
-
return microphoneFailed;
|
|
2742
|
-
});
|
|
2743
|
-
}
|
|
2730
|
+
if (prefix === 'webkit' && isEdge) {
|
|
2731
|
+
// dont wrap webkitRTCPeerconnection in Edge.
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
var origPeerConnection = window[prefix + 'RTCPeerConnection'];
|
|
2735
|
+
var peerconnection = function(config, constraints) {
|
|
2736
|
+
var pc = new origPeerConnection(config, constraints);
|
|
2737
|
+
var id = 'PC_' + peerconnectioncounter++;
|
|
2738
|
+
pc.__rtcStatsId = id;
|
|
2744
2739
|
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
ICE_IPV6_SEEN: "ice_ipv6_seen",
|
|
2749
|
-
ICE_MDNS_SEEN: "ice_mdns_seen",
|
|
2750
|
-
ICE_NO_PUBLIC_IP_GATHERED: "ice_no_public_ip_gathered",
|
|
2751
|
-
ICE_NO_PUBLIC_IP_GATHERED_3SEC: "ice_no_public_ip_gathered_3sec",
|
|
2752
|
-
ICE_RESTART: "ice_restart",
|
|
2753
|
-
MICROPHONE_NOT_WORKING: "microphone_not_working",
|
|
2754
|
-
MICROPHONE_STOPPED_WORKING: "microphone_stopped_working",
|
|
2755
|
-
NEW_PC: "new_pc",
|
|
2756
|
-
SFU_CONNECTION_OPEN: "sfu_connection_open",
|
|
2757
|
-
SFU_CONNECTION_CLOSED: "sfu_connection_closed",
|
|
2758
|
-
COLOCATION_SPEAKER: "colocation_speaker",
|
|
2759
|
-
DOMINANT_SPEAKER: "dominant_speaker",
|
|
2760
|
-
};
|
|
2740
|
+
if (!config) {
|
|
2741
|
+
config = { nullConfig: true };
|
|
2742
|
+
}
|
|
2761
2743
|
|
|
2762
|
-
|
|
2744
|
+
config = JSON.parse(JSON.stringify(config)); // deepcopy
|
|
2745
|
+
// don't log credentials
|
|
2746
|
+
((config && config.iceServers) || []).forEach(function(server) {
|
|
2747
|
+
delete server.credential;
|
|
2748
|
+
});
|
|
2763
2749
|
|
|
2764
|
-
|
|
2765
|
-
|
|
2750
|
+
if (isFirefox) {
|
|
2751
|
+
config.browserType = 'moz';
|
|
2752
|
+
} else if (isEdge) {
|
|
2753
|
+
config.browserType = 'edge';
|
|
2754
|
+
} else {
|
|
2755
|
+
config.browserType = 'webkit';
|
|
2756
|
+
}
|
|
2766
2757
|
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2758
|
+
trace('create', id, config);
|
|
2759
|
+
// TODO: do we want to log constraints here? They are chrome-proprietary.
|
|
2760
|
+
// http://stackoverflow.com/questions/31003928/what-do-each-of-these-experimental-goog-rtcpeerconnectionconstraints-do
|
|
2761
|
+
if (constraints) {
|
|
2762
|
+
trace('constraints', id, constraints);
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
pc.addEventListener('icecandidate', function(e) {
|
|
2766
|
+
trace('onicecandidate', id, e.candidate);
|
|
2767
|
+
});
|
|
2768
|
+
pc.addEventListener('addstream', function(e) {
|
|
2769
|
+
trace('onaddstream', id, e.stream.id + ' ' + e.stream.getTracks().map(function(t) { return t.kind + ':' + t.id; }));
|
|
2770
|
+
});
|
|
2771
|
+
pc.addEventListener('track', function(e) {
|
|
2772
|
+
trace('ontrack', id, e.track.kind + ':' + e.track.id + ' ' + e.streams.map(function(stream) { return 'stream:' + stream.id; }));
|
|
2773
|
+
});
|
|
2774
|
+
pc.addEventListener('removestream', function(e) {
|
|
2775
|
+
trace('onremovestream', id, e.stream.id + ' ' + e.stream.getTracks().map(function(t) { return t.kind + ':' + t.id; }));
|
|
2776
|
+
});
|
|
2777
|
+
pc.addEventListener('signalingstatechange', function() {
|
|
2778
|
+
trace('onsignalingstatechange', id, pc.signalingState);
|
|
2779
|
+
});
|
|
2780
|
+
pc.addEventListener('iceconnectionstatechange', function() {
|
|
2781
|
+
trace('oniceconnectionstatechange', id, pc.iceConnectionState);
|
|
2782
|
+
});
|
|
2783
|
+
pc.addEventListener('icegatheringstatechange', function() {
|
|
2784
|
+
trace('onicegatheringstatechange', id, pc.iceGatheringState);
|
|
2785
|
+
});
|
|
2786
|
+
pc.addEventListener('connectionstatechange', function() {
|
|
2787
|
+
trace('onconnectionstatechange', id, pc.connectionState);
|
|
2788
|
+
});
|
|
2789
|
+
pc.addEventListener('negotiationneeded', function() {
|
|
2790
|
+
trace('onnegotiationneeded', id, undefined);
|
|
2791
|
+
});
|
|
2792
|
+
pc.addEventListener('datachannel', function(event) {
|
|
2793
|
+
trace('ondatachannel', id, [event.channel.id, event.channel.label]);
|
|
2794
|
+
});
|
|
2795
|
+
|
|
2796
|
+
var getStats = function() {
|
|
2797
|
+
pc.getStats(null).then(function(res) {
|
|
2798
|
+
var now = map2obj(res);
|
|
2799
|
+
var base = JSON.parse(JSON.stringify(now)); // our new prev
|
|
2800
|
+
trace('getstats', id, deltaCompression(prevById[id] || {}, now));
|
|
2801
|
+
prevById[id] = base;
|
|
2802
|
+
});
|
|
2803
|
+
};
|
|
2804
|
+
// TODO: do we want one big interval and all peerconnections
|
|
2805
|
+
// queried in that or one setInterval per PC?
|
|
2806
|
+
// we have to collect results anyway so...
|
|
2807
|
+
if (!isEdge && getStatsInterval) {
|
|
2808
|
+
var interval = window.setInterval(function() {
|
|
2809
|
+
if (pc.signalingState === 'closed') {
|
|
2810
|
+
window.clearInterval(interval);
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
getStats();
|
|
2814
|
+
}, getStatsInterval);
|
|
2815
|
+
}
|
|
2816
|
+
if (!isEdge) {
|
|
2817
|
+
pc.addEventListener('connectionstatechange', function() {
|
|
2818
|
+
if (pc.connectionState === 'connected' || pc.connectionState === 'failed') {
|
|
2819
|
+
getStats();
|
|
2820
|
+
}
|
|
2821
|
+
});
|
|
2822
|
+
}
|
|
2823
|
+
return pc;
|
|
2824
|
+
};
|
|
2825
|
+
|
|
2826
|
+
['createDataChannel', 'close'].forEach(function(method) {
|
|
2827
|
+
var nativeMethod = origPeerConnection.prototype[method];
|
|
2828
|
+
if (nativeMethod) {
|
|
2829
|
+
origPeerConnection.prototype[method] = function() {
|
|
2830
|
+
trace(method, this.__rtcStatsId, arguments);
|
|
2831
|
+
return nativeMethod.apply(this, arguments);
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
});
|
|
2835
|
+
|
|
2836
|
+
['addStream', 'removeStream'].forEach(function(method) {
|
|
2837
|
+
var nativeMethod = origPeerConnection.prototype[method];
|
|
2838
|
+
if (nativeMethod) {
|
|
2839
|
+
origPeerConnection.prototype[method] = function() {
|
|
2840
|
+
var stream = arguments[0];
|
|
2841
|
+
var streamInfo = stream.getTracks().map(function(t) {
|
|
2842
|
+
return t.kind + ':' + t.id;
|
|
2843
|
+
}).join(',');
|
|
2844
|
+
|
|
2845
|
+
trace(method, this.__rtcStatsId, stream.id + ' ' + streamInfo);
|
|
2846
|
+
return nativeMethod.apply(this, arguments);
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
});
|
|
2850
|
+
|
|
2851
|
+
['addTrack'].forEach(function(method) {
|
|
2852
|
+
var nativeMethod = origPeerConnection.prototype[method];
|
|
2853
|
+
if (nativeMethod) {
|
|
2854
|
+
origPeerConnection.prototype[method] = function() {
|
|
2855
|
+
var track = arguments[0];
|
|
2856
|
+
var streams = [].slice.call(arguments, 1);
|
|
2857
|
+
trace(method, this.__rtcStatsId, track.kind + ':' + track.id + ' ' + (streams.map(function(s) { return 'stream:' + s.id; }).join(';') || '-'));
|
|
2858
|
+
return nativeMethod.apply(this, arguments);
|
|
2859
|
+
};
|
|
2860
|
+
}
|
|
2861
|
+
});
|
|
2862
|
+
|
|
2863
|
+
['removeTrack'].forEach(function(method) {
|
|
2864
|
+
var nativeMethod = origPeerConnection.prototype[method];
|
|
2865
|
+
if (nativeMethod) {
|
|
2866
|
+
origPeerConnection.prototype[method] = function() {
|
|
2867
|
+
var track = arguments[0].track;
|
|
2868
|
+
trace(method, this.__rtcStatsId, track ? track.kind + ':' + track.id : 'null');
|
|
2869
|
+
return nativeMethod.apply(this, arguments);
|
|
2870
|
+
};
|
|
2871
|
+
}
|
|
2872
|
+
});
|
|
2873
|
+
|
|
2874
|
+
['createOffer', 'createAnswer'].forEach(function(method) {
|
|
2875
|
+
var nativeMethod = origPeerConnection.prototype[method];
|
|
2876
|
+
if (nativeMethod) {
|
|
2877
|
+
origPeerConnection.prototype[method] = function() {
|
|
2878
|
+
var rtcStatsId = this.__rtcStatsId;
|
|
2879
|
+
var args = arguments;
|
|
2880
|
+
var opts;
|
|
2881
|
+
if (arguments.length === 1 && typeof arguments[0] === 'object') {
|
|
2882
|
+
opts = arguments[0];
|
|
2883
|
+
} else if (arguments.length === 3 && typeof arguments[2] === 'object') {
|
|
2884
|
+
opts = arguments[2];
|
|
2885
|
+
}
|
|
2886
|
+
trace(method, this.__rtcStatsId, opts);
|
|
2887
|
+
return nativeMethod.apply(this, opts ? [opts] : undefined)
|
|
2888
|
+
.then(function(description) {
|
|
2889
|
+
trace(method + 'OnSuccess', rtcStatsId, description);
|
|
2890
|
+
if (args.length > 0 && typeof args[0] === 'function') {
|
|
2891
|
+
args[0].apply(null, [description]);
|
|
2892
|
+
return undefined;
|
|
2893
|
+
}
|
|
2894
|
+
return description;
|
|
2895
|
+
}, function(err) {
|
|
2896
|
+
trace(method + 'OnFailure', rtcStatsId, err.toString());
|
|
2897
|
+
if (args.length > 1 && typeof args[1] === 'function') {
|
|
2898
|
+
args[1].apply(null, [err]);
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
throw err;
|
|
2902
|
+
});
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2907
|
+
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function(method) {
|
|
2908
|
+
var nativeMethod = origPeerConnection.prototype[method];
|
|
2909
|
+
if (nativeMethod) {
|
|
2910
|
+
origPeerConnection.prototype[method] = function() {
|
|
2911
|
+
var rtcStatsId = this.__rtcStatsId;
|
|
2912
|
+
var args = arguments;
|
|
2913
|
+
trace(method, this.__rtcStatsId, args[0]);
|
|
2914
|
+
return nativeMethod.apply(this, [args[0]])
|
|
2915
|
+
.then(function() {
|
|
2916
|
+
trace(method + 'OnSuccess', rtcStatsId, undefined);
|
|
2917
|
+
if (args.length >= 2 && typeof args[1] === 'function') {
|
|
2918
|
+
args[1].apply(null, []);
|
|
2919
|
+
return undefined;
|
|
2920
|
+
}
|
|
2921
|
+
return undefined;
|
|
2922
|
+
}, function(err) {
|
|
2923
|
+
trace(method + 'OnFailure', rtcStatsId, err.toString());
|
|
2924
|
+
if (args.length >= 3 && typeof args[2] === 'function') {
|
|
2925
|
+
args[2].apply(null, [err]);
|
|
2926
|
+
return undefined;
|
|
2927
|
+
}
|
|
2928
|
+
throw err;
|
|
2929
|
+
});
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
});
|
|
2933
|
+
|
|
2934
|
+
// wrap static methods. Currently just generateCertificate.
|
|
2935
|
+
if (origPeerConnection.generateCertificate) {
|
|
2936
|
+
Object.defineProperty(peerconnection, 'generateCertificate', {
|
|
2937
|
+
get: function() {
|
|
2938
|
+
return arguments.length ?
|
|
2939
|
+
origPeerConnection.generateCertificate.apply(null, arguments)
|
|
2940
|
+
: origPeerConnection.generateCertificate;
|
|
2941
|
+
},
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2944
|
+
window[prefix + 'RTCPeerConnection'] = peerconnection;
|
|
2945
|
+
window[prefix + 'RTCPeerConnection'].prototype = origPeerConnection.prototype;
|
|
2946
|
+
});
|
|
2947
|
+
|
|
2948
|
+
// getUserMedia wrappers
|
|
2949
|
+
prefixesToWrap.forEach(function(prefix) {
|
|
2950
|
+
var name = prefix + (prefix.length ? 'GetUserMedia' : 'getUserMedia');
|
|
2951
|
+
if (!navigator[name]) {
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
var origGetUserMedia = navigator[name].bind(navigator);
|
|
2955
|
+
var gum = function() {
|
|
2956
|
+
trace('getUserMedia', null, arguments[0]);
|
|
2957
|
+
var cb = arguments[1];
|
|
2958
|
+
var eb = arguments[2];
|
|
2959
|
+
origGetUserMedia(arguments[0],
|
|
2960
|
+
function(stream) {
|
|
2961
|
+
// we log the stream id, track ids and tracks readystate since that is ended GUM fails
|
|
2962
|
+
// to acquire the cam (in chrome)
|
|
2963
|
+
trace('getUserMediaOnSuccess', null, dumpStream(stream));
|
|
2964
|
+
if (cb) {
|
|
2965
|
+
cb(stream);
|
|
2966
|
+
}
|
|
2967
|
+
},
|
|
2968
|
+
function(err) {
|
|
2969
|
+
trace('getUserMediaOnFailure', null, err.name);
|
|
2970
|
+
if (eb) {
|
|
2971
|
+
eb(err);
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
);
|
|
2975
|
+
};
|
|
2976
|
+
navigator[name] = gum.bind(navigator);
|
|
2977
|
+
});
|
|
2978
|
+
|
|
2979
|
+
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
2980
|
+
var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
|
|
2981
|
+
var gum = function() {
|
|
2982
|
+
trace('navigator.mediaDevices.getUserMedia', null, arguments[0]);
|
|
2983
|
+
return origGetUserMedia.apply(navigator.mediaDevices, arguments)
|
|
2984
|
+
.then(function(stream) {
|
|
2985
|
+
trace('navigator.mediaDevices.getUserMediaOnSuccess', null, dumpStream(stream));
|
|
2986
|
+
return stream;
|
|
2987
|
+
}, function(err) {
|
|
2988
|
+
trace('navigator.mediaDevices.getUserMediaOnFailure', null, err.name);
|
|
2989
|
+
return Promise.reject(err);
|
|
2990
|
+
});
|
|
2991
|
+
};
|
|
2992
|
+
navigator.mediaDevices.getUserMedia = gum.bind(navigator.mediaDevices);
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
// getDisplayMedia
|
|
2996
|
+
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
|
|
2997
|
+
var origGetDisplayMedia = navigator.mediaDevices.getDisplayMedia.bind(navigator.mediaDevices);
|
|
2998
|
+
var gdm = function() {
|
|
2999
|
+
trace('navigator.mediaDevices.getDisplayMedia', null, arguments[0]);
|
|
3000
|
+
return origGetDisplayMedia.apply(navigator.mediaDevices, arguments)
|
|
3001
|
+
.then(function(stream) {
|
|
3002
|
+
trace('navigator.mediaDevices.getDisplayMediaOnSuccess', null, dumpStream(stream));
|
|
3003
|
+
return stream;
|
|
3004
|
+
}, function(err) {
|
|
3005
|
+
trace('navigator.mediaDevices.getDisplayMediaOnFailure', null, err.name);
|
|
3006
|
+
return Promise.reject(err);
|
|
3007
|
+
});
|
|
3008
|
+
};
|
|
3009
|
+
navigator.mediaDevices.getDisplayMedia = gdm.bind(navigator.mediaDevices);
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
// TODO: are there events defined on MST that would allow us to listen when enabled was set?
|
|
3013
|
+
// no :-(
|
|
3014
|
+
/*
|
|
3015
|
+
Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {
|
|
3016
|
+
set: function(value) {
|
|
3017
|
+
trace('MediaStreamTrackEnable', this, value);
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
*/
|
|
3021
|
+
|
|
3022
|
+
return {
|
|
3023
|
+
resetDelta() {
|
|
3024
|
+
prevById = {};
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
};
|
|
3029
|
+
|
|
3030
|
+
var rtcstats$1 = /*@__PURE__*/getDefaultExportFromCjs(rtcstats);
|
|
3031
|
+
|
|
3032
|
+
// ensure adapter is loaded first.
|
|
3033
|
+
|
|
3034
|
+
adapterRaw.default ?? adapterRaw; // eslint-disable-line no-unused-vars
|
|
3035
|
+
|
|
3036
|
+
const RTCSTATS_PROTOCOL_VERSION = "1.0";
|
|
3037
|
+
|
|
3038
|
+
// when not connected we need to buffer at least a few getstats reports
|
|
3039
|
+
// as they are delta compressed and we need the initial properties
|
|
3040
|
+
const GETSTATS_BUFFER_SIZE = 20;
|
|
3041
|
+
|
|
3042
|
+
const clientInfo = {
|
|
3043
|
+
id: v4$1(), // shared id across rtcstats reconnects
|
|
3044
|
+
connectionNumber: 0,
|
|
3045
|
+
};
|
|
3046
|
+
|
|
3047
|
+
const noop = () => {};
|
|
3048
|
+
let resetDelta = noop;
|
|
3049
|
+
|
|
3050
|
+
// Inlined version of rtcstats/trace-ws with improved disconnect handling.
|
|
3051
|
+
function rtcStatsConnection(wsURL, logger = console) {
|
|
3052
|
+
const buffer = [];
|
|
3053
|
+
let ws;
|
|
3054
|
+
let organizationId;
|
|
3055
|
+
let clientId;
|
|
3056
|
+
let displayName;
|
|
3057
|
+
let userRole;
|
|
3058
|
+
let roomSessionId;
|
|
3059
|
+
let connectionShouldBeOpen;
|
|
3060
|
+
let connectionAttempt = 0;
|
|
3061
|
+
let hasPassedOnRoomSessionId = false;
|
|
3062
|
+
let getStatsBufferUsed = 0;
|
|
3063
|
+
|
|
3064
|
+
const connection = {
|
|
3065
|
+
connected: false,
|
|
3066
|
+
trace: (...args) => {
|
|
3067
|
+
args.push(Date.now());
|
|
3068
|
+
|
|
3069
|
+
if (args[0] === "customEvent" && args[2].type === "roomSessionId") {
|
|
3070
|
+
const oldRoomSessionIdValue = roomSessionId && roomSessionId[2].value.roomSessionId;
|
|
3071
|
+
const newRoomSessionIdValue = args[2].value.roomSessionId;
|
|
3072
|
+
roomSessionId = args;
|
|
3073
|
+
|
|
3074
|
+
if (
|
|
3075
|
+
hasPassedOnRoomSessionId &&
|
|
3076
|
+
newRoomSessionIdValue &&
|
|
3077
|
+
newRoomSessionIdValue !== oldRoomSessionIdValue
|
|
3078
|
+
) {
|
|
3079
|
+
// roomSessionId was already sent. It may have been reset to null, but anything after should be part of the same
|
|
3080
|
+
// session. Now it is something else, and we force a reconnect to start a new session
|
|
3081
|
+
if (ws) {
|
|
3082
|
+
ws.close();
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
if (newRoomSessionIdValue) hasPassedOnRoomSessionId = true;
|
|
3087
|
+
} else if (args[0] === "customEvent" && args[2].type === "clientId") {
|
|
3088
|
+
clientId = args;
|
|
3089
|
+
} else if (args[0] === "customEvent" && args[2].type === "organizationId") {
|
|
3090
|
+
organizationId = args;
|
|
3091
|
+
} else if (args[0] === "customEvent" && args[2].type === "displayName") {
|
|
3092
|
+
displayName = args;
|
|
3093
|
+
} else if (args[0] === "customEvent" && args[2].type === "userRole") {
|
|
3094
|
+
userRole = args;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
3098
|
+
connectionAttempt = 0;
|
|
3099
|
+
ws.send(JSON.stringify(args));
|
|
3100
|
+
} else if (args[0] === "getstats") {
|
|
3101
|
+
// only buffer getStats for a while
|
|
3102
|
+
// we don't want this to pile up, but we need at least the initial reports
|
|
3103
|
+
if (getStatsBufferUsed < GETSTATS_BUFFER_SIZE) {
|
|
3104
|
+
getStatsBufferUsed++;
|
|
3105
|
+
buffer.push(args);
|
|
3106
|
+
}
|
|
3107
|
+
} else if (args[0] === "customEvent" && args[2].type === "insightsStats") ; else {
|
|
3108
|
+
// buffer everything else
|
|
3109
|
+
buffer.push(args);
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
// reconnect when closed by anything else than client
|
|
3113
|
+
if (ws.readyState === WebSocket.CLOSED && connectionShouldBeOpen) {
|
|
3114
|
+
setTimeout(() => {
|
|
3115
|
+
if (ws.readyState === WebSocket.CLOSED && connectionShouldBeOpen) {
|
|
3116
|
+
connection.connect();
|
|
3117
|
+
}
|
|
3118
|
+
}, 1000 * connectionAttempt);
|
|
3119
|
+
}
|
|
3120
|
+
},
|
|
3121
|
+
close: () => {
|
|
3122
|
+
connectionShouldBeOpen = false;
|
|
3123
|
+
if (ws) {
|
|
3124
|
+
ws.close();
|
|
3125
|
+
}
|
|
3126
|
+
},
|
|
3127
|
+
connect: () => {
|
|
3128
|
+
connectionShouldBeOpen = true;
|
|
3129
|
+
connectionAttempt += 1;
|
|
3130
|
+
if (ws) {
|
|
3131
|
+
ws.close();
|
|
3132
|
+
}
|
|
3133
|
+
connection.connected = true;
|
|
3134
|
+
ws = new WebSocket(wsURL + window.location.pathname, RTCSTATS_PROTOCOL_VERSION);
|
|
3135
|
+
|
|
3136
|
+
ws.onerror = (e) => {
|
|
3137
|
+
connection.connected = false;
|
|
3138
|
+
logger.warn(`[RTCSTATS] WebSocket error`, e);
|
|
3139
|
+
};
|
|
3140
|
+
ws.onclose = (e) => {
|
|
3141
|
+
connection.connected = false;
|
|
3142
|
+
logger.info(`[RTCSTATS] Closed ${e.code}`);
|
|
3143
|
+
resetDelta();
|
|
3144
|
+
};
|
|
3145
|
+
ws.onopen = () => {
|
|
3146
|
+
// send client info after each connection, so analysis tools can handle reconnections
|
|
3147
|
+
clientInfo.connectionNumber++;
|
|
3148
|
+
ws.send(JSON.stringify(["clientInfo", null, clientInfo]));
|
|
3149
|
+
|
|
3150
|
+
if (organizationId) {
|
|
3151
|
+
ws.send(JSON.stringify(organizationId));
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
if (clientId) {
|
|
3155
|
+
ws.send(JSON.stringify(clientId));
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
if (roomSessionId) {
|
|
3159
|
+
ws.send(JSON.stringify(roomSessionId));
|
|
3160
|
+
}
|
|
3161
|
+
if (displayName) {
|
|
3162
|
+
ws.send(JSON.stringify(displayName));
|
|
3163
|
+
}
|
|
3164
|
+
if (userRole) {
|
|
3165
|
+
ws.send(JSON.stringify(userRole));
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
// send buffered events
|
|
3169
|
+
while (buffer.length) {
|
|
3170
|
+
ws.send(JSON.stringify(buffer.shift()));
|
|
3171
|
+
}
|
|
3172
|
+
getStatsBufferUsed = 0;
|
|
3173
|
+
};
|
|
3174
|
+
},
|
|
3175
|
+
};
|
|
3176
|
+
connection.connect();
|
|
3177
|
+
return connection;
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
const server = rtcStatsConnection("wss://rtcstats.srv.whereby.com" );
|
|
3181
|
+
const stats = rtcstats$1(
|
|
3182
|
+
server.trace,
|
|
3183
|
+
10000, // query once every 10 seconds.
|
|
3184
|
+
[""] // only shim unprefixed RTCPeerConnecion.
|
|
3185
|
+
);
|
|
3186
|
+
// on node clients this function can be undefined
|
|
3187
|
+
resetDelta = stats?.resetDelta || noop;
|
|
3188
|
+
|
|
3189
|
+
const rtcStats = {
|
|
3190
|
+
sendEvent: (type, value) => {
|
|
3191
|
+
server.trace("customEvent", null, {
|
|
3192
|
+
type,
|
|
3193
|
+
value,
|
|
3194
|
+
});
|
|
3195
|
+
},
|
|
3196
|
+
sendAudioMuted: (muted) => {
|
|
3197
|
+
rtcStats.sendEvent("audio_muted", { muted });
|
|
3198
|
+
},
|
|
3199
|
+
sendVideoMuted: (muted) => {
|
|
3200
|
+
rtcStats.sendEvent("video_muted", { muted });
|
|
3201
|
+
},
|
|
3202
|
+
server,
|
|
3203
|
+
};
|
|
3204
|
+
|
|
3205
|
+
const adapter$4 = adapterRaw.default ?? adapterRaw;
|
|
3206
|
+
const logger$6 = new Logger();
|
|
3207
|
+
|
|
3208
|
+
const browserName$2 = adapter$4.browserDetails.browser;
|
|
3209
|
+
const browserVersion$1 = adapter$4.browserDetails.version;
|
|
3210
|
+
|
|
3211
|
+
function setCodecPreferenceSDP(sdp, vp9On, redOn) {
|
|
3212
|
+
try {
|
|
3213
|
+
const sdpObject = sdpTransform.parse(sdp);
|
|
3214
|
+
if (Array.isArray(sdpObject?.media)) {
|
|
3215
|
+
//audio
|
|
2772
3216
|
const mediaAudio = sdpObject.media.find((m) => m.type === "audio");
|
|
2773
3217
|
if (Array.isArray(mediaAudio?.rtp)) {
|
|
2774
3218
|
const rtp = mediaAudio.rtp;
|
|
@@ -2802,7 +3246,7 @@ function setCodecPreferenceSDP(sdp, vp9On, redOn) {
|
|
|
2802
3246
|
const newSdp = sdpTransform.write(sdpObject);
|
|
2803
3247
|
return newSdp;
|
|
2804
3248
|
} catch (error) {
|
|
2805
|
-
logger$
|
|
3249
|
+
logger$6.error("setCodecPreferenceSDP error:", error);
|
|
2806
3250
|
}
|
|
2807
3251
|
}
|
|
2808
3252
|
|
|
@@ -2855,7 +3299,7 @@ function replaceSSRCs(currentDescription, newDescription) {
|
|
|
2855
3299
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1478685
|
|
2856
3300
|
// filter out the mid rtp header extension
|
|
2857
3301
|
function filterMidExtension(sdp) {
|
|
2858
|
-
if (browserName$
|
|
3302
|
+
if (browserName$2 !== "safari" && (browserName$2 !== "firefox" || browserVersion$1 >= 63 || browserVersion$1 === 60)) {
|
|
2859
3303
|
return sdp;
|
|
2860
3304
|
}
|
|
2861
3305
|
return (
|
|
@@ -2876,7 +3320,7 @@ function filterMidExtension(sdp) {
|
|
|
2876
3320
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1534673
|
|
2877
3321
|
// Filter out a:msid-semantic header
|
|
2878
3322
|
function filterMsidSemantic(sdp) {
|
|
2879
|
-
if (browserName$
|
|
3323
|
+
if (browserName$2 !== "firefox") {
|
|
2880
3324
|
return sdp;
|
|
2881
3325
|
}
|
|
2882
3326
|
return (
|
|
@@ -2886,6 +3330,52 @@ function filterMsidSemantic(sdp) {
|
|
|
2886
3330
|
);
|
|
2887
3331
|
}
|
|
2888
3332
|
|
|
3333
|
+
// add SDP RTP header extension mapping
|
|
3334
|
+
function addExtMap(sdp, extmapUri, modifyAudio = false, modifyVideo = false) {
|
|
3335
|
+
try {
|
|
3336
|
+
const sdpObj = sdpTransform.parse(sdp);
|
|
3337
|
+
|
|
3338
|
+
// in case session level extmaps we skip modification
|
|
3339
|
+
// TODO: handle it more properly
|
|
3340
|
+
if (sdpObj?.ext?.length > 0) {
|
|
3341
|
+
return sdp;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
// if sdp string is faulty, and lib can't parse any m= lines we return it unmodified.
|
|
3345
|
+
if (sdpObj?.media.length < 1) return sdp;
|
|
3346
|
+
|
|
3347
|
+
const allHeaderExtensions = sdpObj?.media.flatMap((section) => section.ext || []);
|
|
3348
|
+
const extmapId =
|
|
3349
|
+
allHeaderExtensions.find((ext) => ext.uri === extmapUri)?.value ||
|
|
3350
|
+
[...new Set([0, 15, ...allHeaderExtensions.map((ext) => ext.value)])]
|
|
3351
|
+
.sort((a, b) => a - b)
|
|
3352
|
+
.find((n, i, arr) => n + 1 !== arr[i + 1]) + 1;
|
|
3353
|
+
|
|
3354
|
+
sdpObj.media.forEach((mediaSection) => {
|
|
3355
|
+
if ((modifyAudio && mediaSection.type === "audio") || (modifyVideo && mediaSection.type === "video")) {
|
|
3356
|
+
if (!mediaSection.ext?.find((e) => e.uri === extmapUri)) {
|
|
3357
|
+
if (Array.isArray(mediaSection.ext)) {
|
|
3358
|
+
mediaSection["ext"].push({ value: extmapId, uri: extmapUri });
|
|
3359
|
+
} else {
|
|
3360
|
+
mediaSection["ext"] = [{ value: extmapId, uri: extmapUri }];
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
});
|
|
3365
|
+
return sdpTransform.write(sdpObj);
|
|
3366
|
+
} catch (error) {
|
|
3367
|
+
console.error("Error during addAbsCaptureTimeExtMap: ", error);
|
|
3368
|
+
}
|
|
3369
|
+
return sdp;
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
// Add SDP RTP header extension mapping to abs-capture-time
|
|
3373
|
+
// a=extmap:9 http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time
|
|
3374
|
+
function addAbsCaptureTimeExtMap(sdp) {
|
|
3375
|
+
const absCaptureTimeUri = "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time";
|
|
3376
|
+
return addExtMap(sdp, absCaptureTimeUri, true, true);
|
|
3377
|
+
}
|
|
3378
|
+
|
|
2889
3379
|
function isRelayed(pc) {
|
|
2890
3380
|
return pc.getStats(null).then((result) => {
|
|
2891
3381
|
let localCandidateType;
|
|
@@ -2949,7 +3439,8 @@ const MAXIMUM_TURN_BANDWIDTH_UNLIMITED = -1; // kbps;
|
|
|
2949
3439
|
|
|
2950
3440
|
const MEDIA_JITTER_BUFFER_TARGET = 400; // milliseconds;
|
|
2951
3441
|
|
|
2952
|
-
const
|
|
3442
|
+
const adapter$3 = adapterRaw.default ?? adapterRaw;
|
|
3443
|
+
const logger$5 = new Logger();
|
|
2953
3444
|
|
|
2954
3445
|
class Session {
|
|
2955
3446
|
constructor({ peerConnectionId, bandwidth, maximumTurnBandwidth, deprioritizeH264Encoding }) {
|
|
@@ -3124,7 +3615,7 @@ class Session {
|
|
|
3124
3615
|
return setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
3125
3616
|
},
|
|
3126
3617
|
(e) => {
|
|
3127
|
-
logger$
|
|
3618
|
+
logger$5.warn("Could not set remote description from remote answer: ", e);
|
|
3128
3619
|
}
|
|
3129
3620
|
);
|
|
3130
3621
|
}
|
|
@@ -3140,12 +3631,12 @@ class Session {
|
|
|
3140
3631
|
if (this.pc.signalingState === "closed") {
|
|
3141
3632
|
return;
|
|
3142
3633
|
}
|
|
3143
|
-
if (adapter.browserDetails.browser === "safari" && candidate && candidate.candidate === "") {
|
|
3634
|
+
if (adapter$3.browserDetails.browser === "safari" && candidate && candidate.candidate === "") {
|
|
3144
3635
|
// filter due to https://github.com/webrtcHacks/adapter/issues/863
|
|
3145
3636
|
return;
|
|
3146
3637
|
}
|
|
3147
3638
|
this.pc.addIceCandidate(candidate).catch((e) => {
|
|
3148
|
-
logger$
|
|
3639
|
+
logger$5.warn("Failed to add ICE candidate ('%s'): %s", candidate ? candidate.candidate : null, e);
|
|
3149
3640
|
});
|
|
3150
3641
|
});
|
|
3151
3642
|
}
|
|
@@ -3167,7 +3658,7 @@ class Session {
|
|
|
3167
3658
|
// do not handle state change events when we close the connection explicitly
|
|
3168
3659
|
pc.close();
|
|
3169
3660
|
} catch (e) {
|
|
3170
|
-
logger$
|
|
3661
|
+
logger$5.warn("failures during close of session", e);
|
|
3171
3662
|
// we're not interested in errors from RTCPeerConnection.close()
|
|
3172
3663
|
}
|
|
3173
3664
|
}
|
|
@@ -3183,7 +3674,7 @@ class Session {
|
|
|
3183
3674
|
const senders = pc.getSenders();
|
|
3184
3675
|
function dbg(msg) {
|
|
3185
3676
|
const tr = (t) => t && `id:${t.id},kind:${t.kind},state:${t.readyState}`;
|
|
3186
|
-
logger$
|
|
3677
|
+
logger$5.warn(
|
|
3187
3678
|
`${msg}. newTrack:${tr(newTrack)}, oldTrack:${tr(oldTrack)}, sender tracks: ${JSON.stringify(
|
|
3188
3679
|
senders.map((s) => `s ${tr(s.track)}`)
|
|
3189
3680
|
)}, sender first codecs: ${JSON.stringify(senders.map((s) => (s.getParameters().codecs || [])[0]))}`
|
|
@@ -3317,687 +3808,568 @@ class Session {
|
|
|
3317
3808
|
});
|
|
3318
3809
|
}
|
|
3319
3810
|
|
|
3320
|
-
// no-signaling negotiation of bandwidth. Peer is NOT informed.
|
|
3321
|
-
// Prefers using RTCRtpSender.setParameters if possible.
|
|
3322
|
-
changeBandwidth(bandwidth) {
|
|
3323
|
-
// don't renegotiate if bandwidth is already set.
|
|
3324
|
-
if (bandwidth === this.bandwidth) {
|
|
3325
|
-
return;
|
|
3326
|
-
}
|
|
3811
|
+
// no-signaling negotiation of bandwidth. Peer is NOT informed.
|
|
3812
|
+
// Prefers using RTCRtpSender.setParameters if possible.
|
|
3813
|
+
changeBandwidth(bandwidth) {
|
|
3814
|
+
// don't renegotiate if bandwidth is already set.
|
|
3815
|
+
if (bandwidth === this.bandwidth) {
|
|
3816
|
+
return;
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
if (!this.canModifyPeerConnection()) {
|
|
3820
|
+
this.pending.push(() => this.changeBandwidth(bandwidth));
|
|
3821
|
+
return;
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
this.bandwidth = bandwidth;
|
|
3825
|
+
if (!this.pc.localDescription || this.pc.localDescription.type === "") {
|
|
3826
|
+
return;
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3829
|
+
setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3832
|
+
setAudioOnly(enable, excludedTrackIds = []) {
|
|
3833
|
+
this.pc
|
|
3834
|
+
.getTransceivers()
|
|
3835
|
+
.filter(
|
|
3836
|
+
(videoTransceiver) =>
|
|
3837
|
+
videoTransceiver?.direction !== "recvonly" &&
|
|
3838
|
+
videoTransceiver?.receiver?.track?.kind === "video" &&
|
|
3839
|
+
!excludedTrackIds.includes(videoTransceiver?.receiver?.track?.id) &&
|
|
3840
|
+
!excludedTrackIds.includes(videoTransceiver?.sender?.track?.id)
|
|
3841
|
+
)
|
|
3842
|
+
.forEach((videoTransceiver) => {
|
|
3843
|
+
videoTransceiver.direction = enable ? "sendonly" : "sendrecv";
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3848
|
+
const adapter$2 = adapterRaw.default ?? adapterRaw;
|
|
3849
|
+
|
|
3850
|
+
/**
|
|
3851
|
+
* Detect mic issue which seems to happen on OSX when the computer is woken up and sleeping
|
|
3852
|
+
* frequently. A browser restart fixes this.
|
|
3853
|
+
*
|
|
3854
|
+
* Should be called after the connection has been up for a while.
|
|
3855
|
+
*
|
|
3856
|
+
* @see Bug report {@link https://bugs.chromium.org/p/webrtc/issues/detail?id=4799}
|
|
3857
|
+
*/
|
|
3858
|
+
function detectMicrophoneNotWorking(pc) {
|
|
3859
|
+
if (
|
|
3860
|
+
adapter$2.browserDetails.browser !== "chrome" ||
|
|
3861
|
+
adapter$2.browserDetails.browser < 58 || // legacy getStats is no longer supported.
|
|
3862
|
+
pc.signalingState === "closed"
|
|
3863
|
+
) {
|
|
3864
|
+
return Promise.resolve(false);
|
|
3865
|
+
}
|
|
3866
|
+
const sendingAudio = pc.getSenders().some((sender) => sender.track && sender.track.kind === "audio");
|
|
3867
|
+
const receivingAudio = pc.getReceivers().some((receiver) => receiver.track && receiver.track.kind === "audio");
|
|
3868
|
+
return pc.getStats(null).then((result) => {
|
|
3869
|
+
let microphoneFailed = false;
|
|
3870
|
+
result.forEach((report) => {
|
|
3871
|
+
if (
|
|
3872
|
+
report.type === "outbound-rtp" &&
|
|
3873
|
+
(report.kind === "audio" || report.mediaType === "audio") &&
|
|
3874
|
+
sendingAudio
|
|
3875
|
+
) {
|
|
3876
|
+
if (report.bytesSent === 0) {
|
|
3877
|
+
microphoneFailed = "outbound";
|
|
3878
|
+
}
|
|
3879
|
+
} else if (
|
|
3880
|
+
report.type === "inbound-rtp" &&
|
|
3881
|
+
(report.kind === "audio" || report.mediaType === "audio") &&
|
|
3882
|
+
receivingAudio
|
|
3883
|
+
) {
|
|
3884
|
+
if (report.bytesReceived === 0) {
|
|
3885
|
+
microphoneFailed = "inbound";
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
});
|
|
3889
|
+
return microphoneFailed;
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
const EVENTS = {
|
|
3894
|
+
CLIENT_CONNECTION_STATUS_CHANGED: "client_connection_status_changed",
|
|
3895
|
+
STREAM_ADDED: "stream_added",
|
|
3896
|
+
RTC_MANAGER_CREATED: "rtc_manager_created",
|
|
3897
|
+
RTC_MANAGER_DESTROYED: "rtc_manager_destroyed",
|
|
3898
|
+
LOCAL_STREAM_TRACK_ADDED: "local_stream_track_added",
|
|
3899
|
+
LOCAL_STREAM_TRACK_REMOVED: "local_stream_track_removed",
|
|
3900
|
+
REMOTE_STREAM_TRACK_ADDED: "remote_stream_track_added",
|
|
3901
|
+
REMOTE_STREAM_TRACK_REMOVED: "remote_stream_track_removed",
|
|
3902
|
+
};
|
|
3903
|
+
|
|
3904
|
+
const TYPES = {
|
|
3905
|
+
CONNECTING: "connecting",
|
|
3906
|
+
CONNECTION_FAILED: "connection_failed",
|
|
3907
|
+
CONNECTION_SUCCESSFUL: "connection_successful",
|
|
3908
|
+
CONNECTION_DISCONNECTED: "connection_disconnected",
|
|
3909
|
+
};
|
|
3910
|
+
|
|
3911
|
+
const CAMERA_STREAM_ID$1 = "0";
|
|
3912
|
+
|
|
3913
|
+
const STREAM_TYPES = {
|
|
3914
|
+
CAMERA: "camera",
|
|
3915
|
+
SCREEN_SHARE: "screen_share",
|
|
3916
|
+
};
|
|
3917
|
+
|
|
3918
|
+
class RtcStream {
|
|
3919
|
+
constructor(id, type) {
|
|
3920
|
+
assert$1.notEqual(id, undefined, "id is required");
|
|
3921
|
+
assert$1.notEqual(type, undefined, "type is required");
|
|
3922
|
+
|
|
3923
|
+
this.id = "" + id;
|
|
3924
|
+
this.type = type;
|
|
3925
|
+
|
|
3926
|
+
this.isEnabled = true;
|
|
3927
|
+
this.hasSupportForAutoSuperSize = false;
|
|
3928
|
+
this.isAudioEnabled = true;
|
|
3929
|
+
this.isVideoEnabled = true;
|
|
3930
|
+
this.status = TYPES.CONNECTING;
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
setup(stream) {
|
|
3934
|
+
this.stream = stream;
|
|
3935
|
+
this.streamId = stream.id;
|
|
3936
|
+
this.setVideoEnabled(this.isVideoEnabled && stream.getVideoTracks().length > 0);
|
|
3937
|
+
this.setAudioEnabled(this.isAudioEnabled && stream.getAudioTracks().length > 0);
|
|
3938
|
+
return this;
|
|
3939
|
+
}
|
|
3940
|
+
|
|
3941
|
+
setStatus(status) {
|
|
3942
|
+
this.status = status;
|
|
3943
|
+
return this;
|
|
3944
|
+
}
|
|
3327
3945
|
|
|
3328
|
-
|
|
3329
|
-
|
|
3946
|
+
setVideoEnabled(isEnabled) {
|
|
3947
|
+
this.isVideoEnabled = isEnabled;
|
|
3948
|
+
if (!this.stream) {
|
|
3330
3949
|
return;
|
|
3331
3950
|
}
|
|
3951
|
+
this.stream.getVideoTracks().forEach((track) => {
|
|
3952
|
+
track.enabled = isEnabled;
|
|
3953
|
+
});
|
|
3954
|
+
}
|
|
3332
3955
|
|
|
3333
|
-
|
|
3334
|
-
|
|
3956
|
+
setAudioEnabled(isEnabled) {
|
|
3957
|
+
this.isAudioEnabled = isEnabled;
|
|
3958
|
+
if (!this.stream) {
|
|
3335
3959
|
return;
|
|
3336
3960
|
}
|
|
3961
|
+
this.stream.getAudioTracks().forEach((track) => {
|
|
3962
|
+
track.enabled = isEnabled;
|
|
3963
|
+
});
|
|
3964
|
+
}
|
|
3337
3965
|
|
|
3338
|
-
|
|
3966
|
+
static getCameraId() {
|
|
3967
|
+
return CAMERA_STREAM_ID$1;
|
|
3339
3968
|
}
|
|
3340
3969
|
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
videoTransceiver?.direction !== "recvonly" &&
|
|
3347
|
-
videoTransceiver?.receiver?.track?.kind === "video" &&
|
|
3348
|
-
!excludedTrackIds.includes(videoTransceiver?.receiver?.track?.id) &&
|
|
3349
|
-
!excludedTrackIds.includes(videoTransceiver?.sender?.track?.id)
|
|
3350
|
-
)
|
|
3351
|
-
.forEach((videoTransceiver) => {
|
|
3352
|
-
videoTransceiver.direction = enable ? "sendonly" : "sendrecv";
|
|
3353
|
-
});
|
|
3970
|
+
static getTypeFromId(id) {
|
|
3971
|
+
assert$1.notEqual(id, undefined, "id is required");
|
|
3972
|
+
|
|
3973
|
+
const streamId = "" + id;
|
|
3974
|
+
return streamId === CAMERA_STREAM_ID$1 ? STREAM_TYPES.CAMERA : STREAM_TYPES.SCREEN_SHARE;
|
|
3354
3975
|
}
|
|
3355
3976
|
}
|
|
3356
3977
|
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
}
|
|
3363
|
-
var o = {};
|
|
3364
|
-
m.forEach(function(v, k) {
|
|
3365
|
-
o[k] = v;
|
|
3366
|
-
});
|
|
3367
|
-
return o;
|
|
3368
|
-
}
|
|
3978
|
+
const lowPixelCount = 320 * 180;
|
|
3979
|
+
const lowBitratePerPixel = 150000 / lowPixelCount;
|
|
3980
|
+
const highPixelCount = 1280 * 720;
|
|
3981
|
+
const highBitratePerPixel = 1000000 / highPixelCount;
|
|
3982
|
+
const bitrateChangePerPixel = (highBitratePerPixel - lowBitratePerPixel) / (highPixelCount - lowPixelCount);
|
|
3369
3983
|
|
|
3370
|
-
//
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
var report = newStats[id];
|
|
3376
|
-
delete report.id;
|
|
3377
|
-
if (!oldStats[id]) {
|
|
3378
|
-
return;
|
|
3379
|
-
}
|
|
3380
|
-
Object.keys(report).forEach(function(name) {
|
|
3381
|
-
if (report[name] === oldStats[id][name]) {
|
|
3382
|
-
delete newStats[id][name];
|
|
3383
|
-
}
|
|
3384
|
-
if (Object.keys(report).length === 0) {
|
|
3385
|
-
delete newStats[id];
|
|
3386
|
-
} else if (Object.keys(report).length === 1 && report.timestamp) {
|
|
3387
|
-
delete newStats[id];
|
|
3388
|
-
}
|
|
3389
|
-
});
|
|
3390
|
-
});
|
|
3984
|
+
// calculates a bitrate for a given resolution+frameRate
|
|
3985
|
+
function getOptimalBitrate(width, height, frameRate) {
|
|
3986
|
+
let targetPixelCount = width * height;
|
|
3987
|
+
if (targetPixelCount < lowPixelCount) targetPixelCount = lowPixelCount;
|
|
3988
|
+
if (targetPixelCount > highPixelCount) targetPixelCount = highPixelCount;
|
|
3391
3989
|
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
timestamp = report.timestamp;
|
|
3397
|
-
}
|
|
3398
|
-
});
|
|
3399
|
-
Object.keys(newStats).forEach(function(id) {
|
|
3400
|
-
var report = newStats[id];
|
|
3401
|
-
if (report.timestamp === timestamp) {
|
|
3402
|
-
report.timestamp = 0;
|
|
3990
|
+
let targetBitratePerPixel = lowBitratePerPixel;
|
|
3991
|
+
if (targetPixelCount > highPixelCount) targetBitratePerPixel = highBitratePerPixel;
|
|
3992
|
+
else if (targetPixelCount > lowPixelCount) {
|
|
3993
|
+
targetBitratePerPixel += (targetPixelCount - lowPixelCount) * bitrateChangePerPixel;
|
|
3403
3994
|
}
|
|
3404
|
-
});
|
|
3405
|
-
newStats.timestamp = timestamp;
|
|
3406
|
-
return newStats;
|
|
3407
|
-
}
|
|
3408
3995
|
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
id: stream.id,
|
|
3412
|
-
tracks: stream.getTracks().map(function(track) {
|
|
3413
|
-
return {
|
|
3414
|
-
id: track.id, // unique identifier (GUID) for the track
|
|
3415
|
-
kind: track.kind, // `audio` or `video`
|
|
3416
|
-
label: track.label, // identified the track source
|
|
3417
|
-
enabled: track.enabled, // application can control it
|
|
3418
|
-
muted: track.muted, // application cannot control it (read-only)
|
|
3419
|
-
readyState: track.readyState, // `live` or `ended`
|
|
3420
|
-
};
|
|
3421
|
-
}),
|
|
3422
|
-
};
|
|
3423
|
-
}
|
|
3996
|
+
// we use the actual resolution for the target bitrate
|
|
3997
|
+
let targetBitrate = width * height * targetBitratePerPixel;
|
|
3424
3998
|
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
switch (results[id].type) {
|
|
3429
|
-
case 'certificate':
|
|
3430
|
-
case 'codec':
|
|
3431
|
-
delete results[id];
|
|
3432
|
-
break;
|
|
3433
|
-
default:
|
|
3434
|
-
// noop
|
|
3435
|
-
}
|
|
3436
|
-
});
|
|
3437
|
-
return results;
|
|
3438
|
-
}
|
|
3999
|
+
// adjust bitrate down a bit if reduced framerate
|
|
4000
|
+
if (frameRate <= 15) targetBitrate = targetBitrate * 0.7;
|
|
4001
|
+
else if (frameRate <= 24) targetBitrate = targetBitrate * 0.9;
|
|
3439
4002
|
|
|
3440
|
-
|
|
3441
|
-
// FIXME: does not work in FF since the timestamp can't be deleted.
|
|
3442
|
-
Object.keys(results).forEach(function(id) {
|
|
3443
|
-
delete results[id].timestamp;
|
|
3444
|
-
});
|
|
3445
|
-
return results;
|
|
4003
|
+
return targetBitrate;
|
|
3446
4004
|
}
|
|
3447
|
-
*/
|
|
3448
4005
|
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
var isEdge = !!window.RTCIceGatherer;
|
|
3453
|
-
var prevById = {};
|
|
3454
|
-
|
|
3455
|
-
prefixesToWrap.forEach(function(prefix) {
|
|
3456
|
-
if (!window[prefix + 'RTCPeerConnection']) {
|
|
3457
|
-
return;
|
|
3458
|
-
}
|
|
3459
|
-
if (prefix === 'webkit' && isEdge) {
|
|
3460
|
-
// dont wrap webkitRTCPeerconnection in Edge.
|
|
3461
|
-
return;
|
|
3462
|
-
}
|
|
3463
|
-
var origPeerConnection = window[prefix + 'RTCPeerConnection'];
|
|
3464
|
-
var peerconnection = function(config, constraints) {
|
|
3465
|
-
var pc = new origPeerConnection(config, constraints);
|
|
3466
|
-
var id = 'PC_' + peerconnectioncounter++;
|
|
3467
|
-
pc.__rtcStatsId = id;
|
|
4006
|
+
// taken from https://github.com/sindresorhus/ip-regex ^5.0.0
|
|
4007
|
+
// inlined because it's import caused errors in browser-sdk when running tests
|
|
4008
|
+
const word = "[a-fA-F\\d:]";
|
|
3468
4009
|
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
}
|
|
4010
|
+
const boundry = (options) =>
|
|
4011
|
+
options && options.includeBoundaries ? `(?:(?<=\\s|^)(?=${word})|(?<=${word})(?=\\s|$))` : "";
|
|
3472
4012
|
|
|
3473
|
-
|
|
3474
|
-
// don't log credentials
|
|
3475
|
-
((config && config.iceServers) || []).forEach(function(server) {
|
|
3476
|
-
delete server.credential;
|
|
3477
|
-
});
|
|
4013
|
+
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}";
|
|
3478
4014
|
|
|
3479
|
-
|
|
3480
|
-
config.browserType = 'moz';
|
|
3481
|
-
} else if (isEdge) {
|
|
3482
|
-
config.browserType = 'edge';
|
|
3483
|
-
} else {
|
|
3484
|
-
config.browserType = 'webkit';
|
|
3485
|
-
}
|
|
4015
|
+
const v6segment = "[a-fA-F\\d]{1,4}";
|
|
3486
4016
|
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
4017
|
+
const v6 = `
|
|
4018
|
+
(?:
|
|
4019
|
+
(?:${v6segment}:){7}(?:${v6segment}|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
|
|
4020
|
+
(?:${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
|
|
4021
|
+
(?:${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
|
|
4022
|
+
(?:${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
|
|
4023
|
+
(?:${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
|
|
4024
|
+
(?:${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
|
|
4025
|
+
(?:${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
|
|
4026
|
+
(?::(?:(?::${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
|
|
4027
|
+
)(?:%[0-9a-zA-Z]{1,})? // %eth0 %1
|
|
4028
|
+
`
|
|
4029
|
+
.replace(/\s*\/\/.*$/gm, "")
|
|
4030
|
+
.replace(/\n/g, "")
|
|
4031
|
+
.trim();
|
|
3493
4032
|
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
pc.addEventListener('signalingstatechange', function() {
|
|
3507
|
-
trace('onsignalingstatechange', id, pc.signalingState);
|
|
3508
|
-
});
|
|
3509
|
-
pc.addEventListener('iceconnectionstatechange', function() {
|
|
3510
|
-
trace('oniceconnectionstatechange', id, pc.iceConnectionState);
|
|
3511
|
-
});
|
|
3512
|
-
pc.addEventListener('icegatheringstatechange', function() {
|
|
3513
|
-
trace('onicegatheringstatechange', id, pc.iceGatheringState);
|
|
3514
|
-
});
|
|
3515
|
-
pc.addEventListener('connectionstatechange', function() {
|
|
3516
|
-
trace('onconnectionstatechange', id, pc.connectionState);
|
|
3517
|
-
});
|
|
3518
|
-
pc.addEventListener('negotiationneeded', function() {
|
|
3519
|
-
trace('onnegotiationneeded', id, undefined);
|
|
3520
|
-
});
|
|
3521
|
-
pc.addEventListener('datachannel', function(event) {
|
|
3522
|
-
trace('ondatachannel', id, [event.channel.id, event.channel.label]);
|
|
3523
|
-
});
|
|
4033
|
+
// Pre-compile only the exact regexes because adding a global flag make regexes stateful
|
|
4034
|
+
const v46Exact = new RegExp(`(?:^${v4}$)|(?:^${v6}$)`);
|
|
4035
|
+
const v4exact = new RegExp(`^${v4}$`);
|
|
4036
|
+
const v6exact = new RegExp(`^${v6}$`);
|
|
4037
|
+
|
|
4038
|
+
const ipRegex = (options) =>
|
|
4039
|
+
options && options.exact
|
|
4040
|
+
? v46Exact
|
|
4041
|
+
: new RegExp(
|
|
4042
|
+
`(?:${boundry(options)}${v4}${boundry(options)})|(?:${boundry(options)}${v6}${boundry(options)})`,
|
|
4043
|
+
"g"
|
|
4044
|
+
);
|
|
3524
4045
|
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
trace('getstats', id, deltaCompression(prevById[id] || {}, now));
|
|
3530
|
-
prevById[id] = base;
|
|
3531
|
-
});
|
|
3532
|
-
};
|
|
3533
|
-
// TODO: do we want one big interval and all peerconnections
|
|
3534
|
-
// queried in that or one setInterval per PC?
|
|
3535
|
-
// we have to collect results anyway so...
|
|
3536
|
-
if (!isEdge && getStatsInterval) {
|
|
3537
|
-
var interval = window.setInterval(function() {
|
|
3538
|
-
if (pc.signalingState === 'closed') {
|
|
3539
|
-
window.clearInterval(interval);
|
|
3540
|
-
return;
|
|
3541
|
-
}
|
|
3542
|
-
getStats();
|
|
3543
|
-
}, getStatsInterval);
|
|
3544
|
-
}
|
|
3545
|
-
if (!isEdge) {
|
|
3546
|
-
pc.addEventListener('connectionstatechange', function() {
|
|
3547
|
-
if (pc.connectionState === 'connected' || pc.connectionState === 'failed') {
|
|
3548
|
-
getStats();
|
|
3549
|
-
}
|
|
3550
|
-
});
|
|
3551
|
-
}
|
|
3552
|
-
return pc;
|
|
3553
|
-
};
|
|
4046
|
+
ipRegex.v4 = (options) =>
|
|
4047
|
+
options && options.exact ? v4exact : new RegExp(`${boundry(options)}${v4}${boundry(options)}`, "g");
|
|
4048
|
+
ipRegex.v6 = (options) =>
|
|
4049
|
+
options && options.exact ? v6exact : new RegExp(`${boundry(options)}${v6}${boundry(options)}`, "g");
|
|
3554
4050
|
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
4051
|
+
var rtcManagerEvents = {
|
|
4052
|
+
CAMERA_NOT_WORKING: "camera_not_working",
|
|
4053
|
+
CONNECTION_BLOCKED_BY_NETWORK: "connection_blocked_by_network",
|
|
4054
|
+
ICE_IPV6_SEEN: "ice_ipv6_seen",
|
|
4055
|
+
ICE_MDNS_SEEN: "ice_mdns_seen",
|
|
4056
|
+
ICE_NO_PUBLIC_IP_GATHERED: "ice_no_public_ip_gathered",
|
|
4057
|
+
ICE_NO_PUBLIC_IP_GATHERED_3SEC: "ice_no_public_ip_gathered_3sec",
|
|
4058
|
+
ICE_RESTART: "ice_restart",
|
|
4059
|
+
MICROPHONE_NOT_WORKING: "microphone_not_working",
|
|
4060
|
+
MICROPHONE_STOPPED_WORKING: "microphone_stopped_working",
|
|
4061
|
+
NEW_PC: "new_pc",
|
|
4062
|
+
SFU_CONNECTION_OPEN: "sfu_connection_open",
|
|
4063
|
+
SFU_CONNECTION_CLOSED: "sfu_connection_closed",
|
|
4064
|
+
COLOCATION_SPEAKER: "colocation_speaker",
|
|
4065
|
+
DOMINANT_SPEAKER: "dominant_speaker",
|
|
4066
|
+
};
|
|
3564
4067
|
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
if (nativeMethod) {
|
|
3568
|
-
origPeerConnection.prototype[method] = function() {
|
|
3569
|
-
var stream = arguments[0];
|
|
3570
|
-
var streamInfo = stream.getTracks().map(function(t) {
|
|
3571
|
-
return t.kind + ':' + t.id;
|
|
3572
|
-
}).join(',');
|
|
4068
|
+
const adapter$1 = adapterRaw.default ?? adapterRaw;
|
|
4069
|
+
const logger$4 = new Logger();
|
|
3573
4070
|
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
});
|
|
4071
|
+
const ICE_PUBLIC_IP_GATHERING_TIMEOUT = 3 * 1000;
|
|
4072
|
+
const CAMERA_STREAM_ID = RtcStream.getCameraId();
|
|
4073
|
+
const browserName$1 = adapter$1.browserDetails.browser;
|
|
4074
|
+
const browserVersion = adapter$1.browserDetails.version;
|
|
3579
4075
|
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
origPeerConnection.prototype[method] = function() {
|
|
3584
|
-
var track = arguments[0];
|
|
3585
|
-
var streams = [].slice.call(arguments, 1);
|
|
3586
|
-
trace(method, this.__rtcStatsId, track.kind + ':' + track.id + ' ' + (streams.map(function(s) { return 'stream:' + s.id; }).join(';') || '-'));
|
|
3587
|
-
return nativeMethod.apply(this, arguments);
|
|
3588
|
-
};
|
|
3589
|
-
}
|
|
3590
|
-
});
|
|
4076
|
+
if (browserName$1 === "firefox") {
|
|
4077
|
+
adapter$1.browserShim.shimGetDisplayMedia(window, "screen");
|
|
4078
|
+
}
|
|
3591
4079
|
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
var track = arguments[0].track;
|
|
3597
|
-
trace(method, this.__rtcStatsId, track ? track.kind + ':' + track.id : 'null');
|
|
3598
|
-
return nativeMethod.apply(this, arguments);
|
|
3599
|
-
};
|
|
3600
|
-
}
|
|
4080
|
+
let unloading$1 = false;
|
|
4081
|
+
if (browserName$1 === "chrome") {
|
|
4082
|
+
window.document.addEventListener("beforeunload", () => {
|
|
4083
|
+
unloading$1 = true;
|
|
3601
4084
|
});
|
|
4085
|
+
}
|
|
3602
4086
|
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
if (arguments.length === 1 && typeof arguments[0] === 'object') {
|
|
3611
|
-
opts = arguments[0];
|
|
3612
|
-
} else if (arguments.length === 3 && typeof arguments[2] === 'object') {
|
|
3613
|
-
opts = arguments[2];
|
|
3614
|
-
}
|
|
3615
|
-
trace(method, this.__rtcStatsId, opts);
|
|
3616
|
-
return nativeMethod.apply(this, opts ? [opts] : undefined)
|
|
3617
|
-
.then(function(description) {
|
|
3618
|
-
trace(method + 'OnSuccess', rtcStatsId, description);
|
|
3619
|
-
if (args.length > 0 && typeof args[0] === 'function') {
|
|
3620
|
-
args[0].apply(null, [description]);
|
|
3621
|
-
return undefined;
|
|
3622
|
-
}
|
|
3623
|
-
return description;
|
|
3624
|
-
}, function(err) {
|
|
3625
|
-
trace(method + 'OnFailure', rtcStatsId, err.toString());
|
|
3626
|
-
if (args.length > 1 && typeof args[1] === 'function') {
|
|
3627
|
-
args[1].apply(null, [err]);
|
|
3628
|
-
return;
|
|
3629
|
-
}
|
|
3630
|
-
throw err;
|
|
3631
|
-
});
|
|
3632
|
-
};
|
|
3633
|
-
}
|
|
3634
|
-
});
|
|
4087
|
+
class P2pRtcManager {
|
|
4088
|
+
constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features }) {
|
|
4089
|
+
assert$1.ok(selfId, "selfId is required");
|
|
4090
|
+
assert$1.ok(room, "room is required");
|
|
4091
|
+
assert$1.ok(emitter && emitter.emit, "emitter is required");
|
|
4092
|
+
assert$1.ok(serverSocket instanceof ServerSocket, "serverSocket is required");
|
|
4093
|
+
assert$1.ok(webrtcProvider, "webrtcProvider is required");
|
|
3635
4094
|
|
|
3636
|
-
|
|
3637
|
-
var nativeMethod = origPeerConnection.prototype[method];
|
|
3638
|
-
if (nativeMethod) {
|
|
3639
|
-
origPeerConnection.prototype[method] = function() {
|
|
3640
|
-
var rtcStatsId = this.__rtcStatsId;
|
|
3641
|
-
var args = arguments;
|
|
3642
|
-
trace(method, this.__rtcStatsId, args[0]);
|
|
3643
|
-
return nativeMethod.apply(this, [args[0]])
|
|
3644
|
-
.then(function() {
|
|
3645
|
-
trace(method + 'OnSuccess', rtcStatsId, undefined);
|
|
3646
|
-
if (args.length >= 2 && typeof args[1] === 'function') {
|
|
3647
|
-
args[1].apply(null, []);
|
|
3648
|
-
return undefined;
|
|
3649
|
-
}
|
|
3650
|
-
return undefined;
|
|
3651
|
-
}, function(err) {
|
|
3652
|
-
trace(method + 'OnFailure', rtcStatsId, err.toString());
|
|
3653
|
-
if (args.length >= 3 && typeof args[2] === 'function') {
|
|
3654
|
-
args[2].apply(null, [err]);
|
|
3655
|
-
return undefined;
|
|
3656
|
-
}
|
|
3657
|
-
throw err;
|
|
3658
|
-
});
|
|
3659
|
-
};
|
|
3660
|
-
}
|
|
3661
|
-
});
|
|
4095
|
+
const { name, session, iceServers, sfuServer, mediaserverConfigTtlSeconds } = room;
|
|
3662
4096
|
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
4097
|
+
this._selfId = selfId;
|
|
4098
|
+
this._roomName = name;
|
|
4099
|
+
this._roomSessionId = session && session.id;
|
|
4100
|
+
this.peerConnections = {};
|
|
4101
|
+
this.localStreams = {};
|
|
4102
|
+
this.enabledLocalStreamIds = [];
|
|
4103
|
+
this._screenshareVideoTrackIds = [];
|
|
4104
|
+
this._socketListenerDeregisterFunctions = [];
|
|
4105
|
+
this._localStreamDeregisterFunction = null;
|
|
4106
|
+
this._emitter = emitter;
|
|
4107
|
+
this._serverSocket = serverSocket;
|
|
4108
|
+
this._webrtcProvider = webrtcProvider;
|
|
4109
|
+
this._features = features || {};
|
|
4110
|
+
this._isAudioOnlyMode = false;
|
|
3676
4111
|
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
var name = prefix + (prefix.length ? 'GetUserMedia' : 'getUserMedia');
|
|
3680
|
-
if (!navigator[name]) {
|
|
3681
|
-
return;
|
|
3682
|
-
}
|
|
3683
|
-
var origGetUserMedia = navigator[name].bind(navigator);
|
|
3684
|
-
var gum = function() {
|
|
3685
|
-
trace('getUserMedia', null, arguments[0]);
|
|
3686
|
-
var cb = arguments[1];
|
|
3687
|
-
var eb = arguments[2];
|
|
3688
|
-
origGetUserMedia(arguments[0],
|
|
3689
|
-
function(stream) {
|
|
3690
|
-
// we log the stream id, track ids and tracks readystate since that is ended GUM fails
|
|
3691
|
-
// to acquire the cam (in chrome)
|
|
3692
|
-
trace('getUserMediaOnSuccess', null, dumpStream(stream));
|
|
3693
|
-
if (cb) {
|
|
3694
|
-
cb(stream);
|
|
3695
|
-
}
|
|
3696
|
-
},
|
|
3697
|
-
function(err) {
|
|
3698
|
-
trace('getUserMediaOnFailure', null, err.name);
|
|
3699
|
-
if (eb) {
|
|
3700
|
-
eb(err);
|
|
3701
|
-
}
|
|
3702
|
-
}
|
|
3703
|
-
);
|
|
3704
|
-
};
|
|
3705
|
-
navigator[name] = gum.bind(navigator);
|
|
3706
|
-
});
|
|
4112
|
+
this.offerOptions = { offerToReceiveAudio: true, offerToReceiveVideo: true };
|
|
4113
|
+
this._pendingActionsForConnectedPeerConnections = [];
|
|
3707
4114
|
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
}
|
|
4115
|
+
this._audioTrackOnEnded = () => {
|
|
4116
|
+
// There are a couple of reasons the microphone could stop working.
|
|
4117
|
+
// One of them is getting unplugged. The other is the Chrome audio
|
|
4118
|
+
// process crashing. The third is the tab being closed.
|
|
4119
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=1050008
|
|
4120
|
+
rtcStats.sendEvent("audio_ended", { unloading: unloading$1 });
|
|
4121
|
+
this._emit(rtcManagerEvents.MICROPHONE_STOPPED_WORKING, {});
|
|
4122
|
+
};
|
|
4123
|
+
|
|
4124
|
+
this._updateAndScheduleMediaServersRefresh({
|
|
4125
|
+
sfuServer,
|
|
4126
|
+
iceServers: iceServers.iceServers || [],
|
|
4127
|
+
mediaserverConfigTtlSeconds,
|
|
4128
|
+
});
|
|
3723
4129
|
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
var origGetDisplayMedia = navigator.mediaDevices.getDisplayMedia.bind(navigator.mediaDevices);
|
|
3727
|
-
var gdm = function() {
|
|
3728
|
-
trace('navigator.mediaDevices.getDisplayMedia', null, arguments[0]);
|
|
3729
|
-
return origGetDisplayMedia.apply(navigator.mediaDevices, arguments)
|
|
3730
|
-
.then(function(stream) {
|
|
3731
|
-
trace('navigator.mediaDevices.getDisplayMediaOnSuccess', null, dumpStream(stream));
|
|
3732
|
-
return stream;
|
|
3733
|
-
}, function(err) {
|
|
3734
|
-
trace('navigator.mediaDevices.getDisplayMediaOnFailure', null, err.name);
|
|
3735
|
-
return Promise.reject(err);
|
|
3736
|
-
});
|
|
3737
|
-
};
|
|
3738
|
-
navigator.mediaDevices.getDisplayMedia = gdm.bind(navigator.mediaDevices);
|
|
3739
|
-
}
|
|
4130
|
+
this.totalSessionsCreated = 0;
|
|
4131
|
+
}
|
|
3740
4132
|
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
/*
|
|
3744
|
-
Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {
|
|
3745
|
-
set: function(value) {
|
|
3746
|
-
trace('MediaStreamTrackEnable', this, value);
|
|
4133
|
+
numberOfPeerconnections() {
|
|
4134
|
+
return Object.keys(this.peerConnections).length;
|
|
3747
4135
|
}
|
|
3748
|
-
});
|
|
3749
|
-
*/
|
|
3750
4136
|
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
prevById = {};
|
|
4137
|
+
isInitializedWith({ selfId, roomName, isSfu }) {
|
|
4138
|
+
return this._selfId === selfId && this._roomName === roomName && isSfu === !!this._sfuServer;
|
|
3754
4139
|
}
|
|
3755
|
-
}
|
|
3756
4140
|
|
|
3757
|
-
|
|
4141
|
+
supportsScreenShareAudio() {
|
|
4142
|
+
return true;
|
|
4143
|
+
}
|
|
3758
4144
|
|
|
3759
|
-
|
|
4145
|
+
maybeRestrictRelayBandwidth(session) {
|
|
4146
|
+
session.maybeRestrictRelayBandwidth();
|
|
4147
|
+
}
|
|
3760
4148
|
|
|
3761
|
-
|
|
4149
|
+
addNewStream(streamId, stream) {
|
|
4150
|
+
if (stream === this.localStreams[streamId]) {
|
|
4151
|
+
// this can happen after reconnect. We do not want to add the stream to the
|
|
4152
|
+
// peerconnection again.
|
|
4153
|
+
return;
|
|
4154
|
+
}
|
|
3762
4155
|
|
|
3763
|
-
|
|
4156
|
+
this._addLocalStream(streamId, stream);
|
|
3764
4157
|
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
const
|
|
4158
|
+
if (streamId === CAMERA_STREAM_ID) {
|
|
4159
|
+
this._addStreamToPeerConnections(stream);
|
|
4160
|
+
const [audioTrack] = stream.getAudioTracks();
|
|
4161
|
+
if (audioTrack) {
|
|
4162
|
+
this._startMonitoringAudioTrack(audioTrack);
|
|
4163
|
+
}
|
|
3768
4164
|
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
4165
|
+
// This should not be needed, but checking nonetheless
|
|
4166
|
+
if (this._localStreamDeregisterFunction) {
|
|
4167
|
+
this._localStreamDeregisterFunction();
|
|
4168
|
+
this._localStreamDeregisterFunction = null;
|
|
4169
|
+
}
|
|
3773
4170
|
|
|
3774
|
-
const
|
|
3775
|
-
|
|
4171
|
+
const localStreamHandler = (e) => {
|
|
4172
|
+
const { enable, track } = e.detail;
|
|
4173
|
+
this._handleStopOrResumeVideo({ enable, track });
|
|
4174
|
+
};
|
|
3776
4175
|
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
let organizationId;
|
|
3782
|
-
let clientId;
|
|
3783
|
-
let displayName;
|
|
3784
|
-
let userRole;
|
|
3785
|
-
let roomSessionId;
|
|
3786
|
-
let connectionShouldBeOpen;
|
|
3787
|
-
let connectionAttempt = 0;
|
|
3788
|
-
let hasPassedOnRoomSessionId = false;
|
|
3789
|
-
let getStatsBufferUsed = 0;
|
|
4176
|
+
stream.addEventListener("stopresumevideo", localStreamHandler);
|
|
4177
|
+
this._localStreamDeregisterFunction = () => {
|
|
4178
|
+
stream.removeEventListener("stopresumevideo", localStreamHandler);
|
|
4179
|
+
};
|
|
3790
4180
|
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
trace: (...args) => {
|
|
3794
|
-
args.push(Date.now());
|
|
4181
|
+
return;
|
|
4182
|
+
}
|
|
3795
4183
|
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
4184
|
+
// at this point it is clearly a screensharing stream.
|
|
4185
|
+
this._screenshareVideoTrackIds.push(stream.getVideoTracks()[0].id);
|
|
4186
|
+
this._shareScreen(streamId, stream);
|
|
4187
|
+
return;
|
|
4188
|
+
}
|
|
3800
4189
|
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
return;
|
|
3811
|
-
}
|
|
3812
|
-
}
|
|
3813
|
-
if (newRoomSessionIdValue) hasPassedOnRoomSessionId = true;
|
|
3814
|
-
} else if (args[0] === "customEvent" && args[2].type === "clientId") {
|
|
3815
|
-
clientId = args;
|
|
3816
|
-
} else if (args[0] === "customEvent" && args[2].type === "organizationId") {
|
|
3817
|
-
organizationId = args;
|
|
3818
|
-
} else if (args[0] === "customEvent" && args[2].type === "displayName") {
|
|
3819
|
-
displayName = args;
|
|
3820
|
-
} else if (args[0] === "customEvent" && args[2].type === "userRole") {
|
|
3821
|
-
userRole = args;
|
|
3822
|
-
}
|
|
4190
|
+
replaceTrack(oldTrack, newTrack) {
|
|
4191
|
+
if (oldTrack && oldTrack.kind === "audio") {
|
|
4192
|
+
this._stopMonitoringAudioTrack(oldTrack);
|
|
4193
|
+
}
|
|
4194
|
+
if (newTrack && newTrack.kind === "audio") {
|
|
4195
|
+
this._startMonitoringAudioTrack(newTrack);
|
|
4196
|
+
}
|
|
4197
|
+
return this._replaceTrackToPeerConnections(oldTrack, newTrack);
|
|
4198
|
+
}
|
|
3823
4199
|
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
} else if (args[0] === "getstats") {
|
|
3828
|
-
// only buffer getStats for a while
|
|
3829
|
-
// we don't want this to pile up, but we need at least the initial reports
|
|
3830
|
-
if (getStatsBufferUsed < GETSTATS_BUFFER_SIZE) {
|
|
3831
|
-
getStatsBufferUsed++;
|
|
3832
|
-
buffer.push(args);
|
|
3833
|
-
}
|
|
3834
|
-
} else if (args[0] === "customEvent" && args[2].type === "insightsStats") ; else {
|
|
3835
|
-
// buffer everything else
|
|
3836
|
-
buffer.push(args);
|
|
3837
|
-
}
|
|
4200
|
+
accept({ clientId, shouldAddLocalVideo }) {
|
|
4201
|
+
return this.acceptNewStream({ streamId: clientId, clientId, shouldAddLocalVideo });
|
|
4202
|
+
}
|
|
3838
4203
|
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
close: () => {
|
|
3849
|
-
connectionShouldBeOpen = false;
|
|
3850
|
-
if (ws) {
|
|
3851
|
-
ws.close();
|
|
3852
|
-
}
|
|
3853
|
-
},
|
|
3854
|
-
connect: () => {
|
|
3855
|
-
connectionShouldBeOpen = true;
|
|
3856
|
-
connectionAttempt += 1;
|
|
3857
|
-
if (ws) {
|
|
3858
|
-
ws.close();
|
|
3859
|
-
}
|
|
3860
|
-
connection.connected = true;
|
|
3861
|
-
ws = new WebSocket(wsURL + window.location.pathname, RTCSTATS_PROTOCOL_VERSION);
|
|
4204
|
+
disconnectAll() {
|
|
4205
|
+
Object.keys(this.peerConnections).forEach((peerConnectionId) => {
|
|
4206
|
+
this.disconnect(peerConnectionId);
|
|
4207
|
+
});
|
|
4208
|
+
this.peerConnections = {};
|
|
4209
|
+
this._socketListenerDeregisterFunctions.forEach((func) => {
|
|
4210
|
+
func();
|
|
4211
|
+
});
|
|
4212
|
+
this._socketListenerDeregisterFunctions = [];
|
|
3862
4213
|
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
connection.connected = false;
|
|
3869
|
-
logger.info(`[RTCSTATS] Closed ${e.code}`);
|
|
3870
|
-
resetDelta();
|
|
3871
|
-
};
|
|
3872
|
-
ws.onopen = () => {
|
|
3873
|
-
// send client info after each connection, so analysis tools can handle reconnections
|
|
3874
|
-
clientInfo.connectionNumber++;
|
|
3875
|
-
ws.send(JSON.stringify(["clientInfo", null, clientInfo]));
|
|
4214
|
+
if (this._localStreamDeregisterFunction) {
|
|
4215
|
+
this._localStreamDeregisterFunction();
|
|
4216
|
+
this._localStreamDeregisterFunction = null;
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
3876
4219
|
|
|
3877
|
-
|
|
3878
|
-
|
|
4220
|
+
/* the Chrome audio process crashed (probably?)
|
|
4221
|
+
* try to fix it. Constraints are the audio device constraints
|
|
4222
|
+
* used in getUserMedia.
|
|
4223
|
+
*/
|
|
4224
|
+
fixChromeAudio(constraints) {
|
|
4225
|
+
if (browserName$1 !== "chrome") {
|
|
4226
|
+
return;
|
|
4227
|
+
}
|
|
4228
|
+
const localStream = this._getLocalCameraStream();
|
|
4229
|
+
const audioTrack = localStream.getAudioTracks()[0];
|
|
4230
|
+
if (!audioTrack || audioTrack.readyState !== "ended") {
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
4233
|
+
return navigator.mediaDevices.getUserMedia({ audio: constraints }).then((stream) => {
|
|
4234
|
+
const track = stream.getAudioTracks()[0];
|
|
4235
|
+
track.enabled = audioTrack.enabled; // retain mute state and don't accidentally unmute.
|
|
4236
|
+
localStream.removeTrack(audioTrack); // remove the old track.
|
|
4237
|
+
localStream.addTrack(track); // add the new track.
|
|
4238
|
+
return this.replaceTrack(audioTrack, track);
|
|
4239
|
+
});
|
|
4240
|
+
}
|
|
4241
|
+
|
|
4242
|
+
setupSocketListeners() {
|
|
4243
|
+
this._socketListenerDeregisterFunctions = [
|
|
4244
|
+
() => this._clearMediaServersRefresh(),
|
|
4245
|
+
|
|
4246
|
+
this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
|
|
4247
|
+
if (data.error) {
|
|
4248
|
+
logger$4.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
|
|
4249
|
+
return;
|
|
3879
4250
|
}
|
|
4251
|
+
this._updateAndScheduleMediaServersRefresh(data);
|
|
4252
|
+
}),
|
|
3880
4253
|
|
|
3881
|
-
|
|
3882
|
-
|
|
4254
|
+
this._serverSocket.on(RELAY_MESSAGES.READY_TO_RECEIVE_OFFER, (data) => {
|
|
4255
|
+
this._connect(data.clientId);
|
|
4256
|
+
}),
|
|
4257
|
+
|
|
4258
|
+
this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, (data) => {
|
|
4259
|
+
const session = this._getSession(data.clientId);
|
|
4260
|
+
if (!session) {
|
|
4261
|
+
logger$4.warn("No RTCPeerConnection on ICE_CANDIDATE", data);
|
|
4262
|
+
return;
|
|
4263
|
+
}
|
|
4264
|
+
session.addIceCandidate(data.message);
|
|
4265
|
+
}),
|
|
4266
|
+
|
|
4267
|
+
this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, (data) => {
|
|
4268
|
+
const session = this._getSession(data.clientId);
|
|
4269
|
+
if (!session) {
|
|
4270
|
+
logger$4.warn("No RTCPeerConnection on ICE_END_OF_CANDIDATES", data);
|
|
4271
|
+
return;
|
|
3883
4272
|
}
|
|
4273
|
+
session.addIceCandidate(null);
|
|
4274
|
+
}),
|
|
3884
4275
|
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
if (
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
if (userRole) {
|
|
3892
|
-
ws.send(JSON.stringify(userRole));
|
|
4276
|
+
// when a new SDP offer is received from another client
|
|
4277
|
+
this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, (data) => {
|
|
4278
|
+
const session = this._getSession(data.clientId);
|
|
4279
|
+
if (!session) {
|
|
4280
|
+
logger$4.warn("No RTCPeerConnection on SDP_OFFER", data);
|
|
4281
|
+
return;
|
|
3893
4282
|
}
|
|
4283
|
+
const offer = this._transformIncomingSdp(data.message, session.pc);
|
|
4284
|
+
session.handleOffer(offer).then((answer) => {
|
|
4285
|
+
this._emitServerEvent(RELAY_MESSAGES.SDP_ANSWER, {
|
|
4286
|
+
receiverId: data.clientId,
|
|
4287
|
+
message: this._transformOutgoingSdp(answer),
|
|
4288
|
+
});
|
|
4289
|
+
});
|
|
4290
|
+
}),
|
|
3894
4291
|
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
4292
|
+
// when a new SDP answer is received from another client
|
|
4293
|
+
this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, (data) => {
|
|
4294
|
+
const session = this._getSession(data.clientId);
|
|
4295
|
+
if (!session) {
|
|
4296
|
+
logger$4.warn("No RTCPeerConnection on SDP_ANSWER", data);
|
|
4297
|
+
return;
|
|
3898
4298
|
}
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
};
|
|
3903
|
-
connection.connect();
|
|
3904
|
-
return connection;
|
|
3905
|
-
}
|
|
4299
|
+
const answer = this._transformIncomingSdp(data.message, session.pc);
|
|
4300
|
+
session.handleAnswer(answer);
|
|
4301
|
+
}),
|
|
3906
4302
|
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
10000, // query once every 10 seconds.
|
|
3911
|
-
[""] // only shim unprefixed RTCPeerConnecion.
|
|
3912
|
-
);
|
|
3913
|
-
// on node clients this function can be undefined
|
|
3914
|
-
resetDelta = stats?.resetDelta || noop;
|
|
4303
|
+
// if this is a reconnect to signal-server during screen-share we must let signal-server know
|
|
4304
|
+
this._serverSocket.on(PROTOCOL_RESPONSES.ROOM_JOINED, ({ room: { sfuServer: isSfu } }) => {
|
|
4305
|
+
if (isSfu || !this._wasScreenSharing) return;
|
|
3915
4306
|
|
|
3916
|
-
const
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
value,
|
|
3921
|
-
});
|
|
3922
|
-
},
|
|
3923
|
-
sendAudioMuted: (muted) => {
|
|
3924
|
-
rtcStats.sendEvent("audio_muted", { muted });
|
|
3925
|
-
},
|
|
3926
|
-
sendVideoMuted: (muted) => {
|
|
3927
|
-
rtcStats.sendEvent("video_muted", { muted });
|
|
3928
|
-
},
|
|
3929
|
-
server,
|
|
3930
|
-
};
|
|
4307
|
+
const screenShareStreamId = Object.keys(this.localStreams).find((id) => id !== CAMERA_STREAM_ID);
|
|
4308
|
+
if (!screenShareStreamId) {
|
|
4309
|
+
return;
|
|
4310
|
+
}
|
|
3931
4311
|
|
|
3932
|
-
const
|
|
4312
|
+
const screenshareStream = this.localStreams[screenShareStreamId];
|
|
4313
|
+
if (!screenshareStream) {
|
|
4314
|
+
logger$4.warn("screenshare stream %s not found", screenShareStreamId);
|
|
4315
|
+
return;
|
|
4316
|
+
}
|
|
3933
4317
|
|
|
3934
|
-
const
|
|
3935
|
-
const browserName$2 = adapter.browserDetails.browser;
|
|
3936
|
-
const browserVersion = adapter.browserDetails.version;
|
|
4318
|
+
const hasAudioTrack = screenshareStream.getAudioTracks().length > 0;
|
|
3937
4319
|
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4320
|
+
this._emitServerEvent(PROTOCOL_REQUESTS.START_SCREENSHARE, {
|
|
4321
|
+
streamId: screenShareStreamId,
|
|
4322
|
+
hasAudioTrack,
|
|
4323
|
+
});
|
|
4324
|
+
}),
|
|
4325
|
+
];
|
|
4326
|
+
}
|
|
3941
4327
|
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
unloading$1 = true;
|
|
3946
|
-
});
|
|
3947
|
-
}
|
|
4328
|
+
sendAudioMutedStats(muted) {
|
|
4329
|
+
rtcStats.sendEvent("audio_muted", { muted });
|
|
4330
|
+
}
|
|
3948
4331
|
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
assert$1.ok(room, "room is required");
|
|
3953
|
-
assert$1.ok(emitter && emitter.emit, "emitter is required");
|
|
3954
|
-
assert$1.ok(serverSocket instanceof ServerSocket, "serverSocket is required");
|
|
3955
|
-
assert$1.ok(webrtcProvider, "webrtcProvider is required");
|
|
4332
|
+
sendVideoMutedStats(muted) {
|
|
4333
|
+
rtcStats.sendEvent("video_muted", { muted });
|
|
4334
|
+
}
|
|
3956
4335
|
|
|
3957
|
-
|
|
4336
|
+
sendStatsCustomEvent(eventName, data) {
|
|
4337
|
+
rtcStats.sendEvent(eventName, data);
|
|
4338
|
+
}
|
|
3958
4339
|
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
this.peerConnections = {};
|
|
3963
|
-
this.localStreams = {};
|
|
3964
|
-
this.enabledLocalStreamIds = [];
|
|
3965
|
-
this._screenshareVideoTrackIds = [];
|
|
3966
|
-
this._socketListenerDeregisterFunctions = [];
|
|
3967
|
-
this._localStreamDeregisterFunction = null;
|
|
3968
|
-
this._emitter = emitter;
|
|
3969
|
-
this._serverSocket = serverSocket;
|
|
3970
|
-
this._webrtcProvider = webrtcProvider;
|
|
3971
|
-
this._features = features || {};
|
|
3972
|
-
this._isAudioOnlyMode = false;
|
|
4340
|
+
rtcStatsDisconnect() {
|
|
4341
|
+
rtcStats.server.close();
|
|
4342
|
+
}
|
|
3973
4343
|
|
|
3974
|
-
|
|
3975
|
-
|
|
4344
|
+
rtcStatsReconnect() {
|
|
4345
|
+
if (!rtcStats.server.connected) {
|
|
4346
|
+
rtcStats.server.connect();
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
3976
4349
|
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
// One of them is getting unplugged. The other is the Chrome audio
|
|
3980
|
-
// process crashing. The third is the tab being closed.
|
|
3981
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=1050008
|
|
3982
|
-
rtcStats.sendEvent("audio_ended", { unloading: unloading$1 });
|
|
3983
|
-
this._emit(rtcManagerEvents.MICROPHONE_STOPPED_WORKING, {});
|
|
3984
|
-
};
|
|
4350
|
+
setAudioOnly(audioOnly) {
|
|
4351
|
+
this._isAudioOnlyMode = audioOnly;
|
|
3985
4352
|
|
|
3986
|
-
this.
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
4353
|
+
this._forEachPeerConnection((session) => {
|
|
4354
|
+
if (session.hasConnectedPeerConnection()) {
|
|
4355
|
+
this._withForcedRenegotiation(session, () =>
|
|
4356
|
+
session.setAudioOnly(this._isAudioOnlyMode, this._screenshareVideoTrackIds)
|
|
4357
|
+
);
|
|
4358
|
+
}
|
|
3990
4359
|
});
|
|
3991
|
-
|
|
3992
|
-
this.totalSessionsCreated = 0;
|
|
3993
4360
|
}
|
|
3994
4361
|
|
|
3995
|
-
|
|
3996
|
-
|
|
4362
|
+
setRemoteScreenshareVideoTrackIds(remoteScreenshareVideoTrackIds = []) {
|
|
4363
|
+
const localScreenshareStream = this._getFirstLocalNonCameraStream();
|
|
4364
|
+
|
|
4365
|
+
this._screenshareVideoTrackIds = [
|
|
4366
|
+
...(localScreenshareStream?.track ? [localScreenshareStream.track.id] : []),
|
|
4367
|
+
...remoteScreenshareVideoTrackIds,
|
|
4368
|
+
];
|
|
3997
4369
|
}
|
|
3998
4370
|
|
|
3999
|
-
|
|
4000
|
-
|
|
4371
|
+
setRoomSessionId(roomSessionId) {
|
|
4372
|
+
this._roomSessionId = roomSessionId;
|
|
4001
4373
|
}
|
|
4002
4374
|
|
|
4003
4375
|
_setConnectionStatus(session, newStatus, clientId) {
|
|
@@ -4040,7 +4412,7 @@ class BaseRtcManager {
|
|
|
4040
4412
|
receiver.playoutDelayHint = MEDIA_JITTER_BUFFER_TARGET / 1000; // seconds
|
|
4041
4413
|
});
|
|
4042
4414
|
} catch (error) {
|
|
4043
|
-
logger$
|
|
4415
|
+
logger$4.error("Error during setting jitter buffer target:", error);
|
|
4044
4416
|
}
|
|
4045
4417
|
}
|
|
4046
4418
|
|
|
@@ -4052,14 +4424,6 @@ class BaseRtcManager {
|
|
|
4052
4424
|
this._emitter.emit(eventName, data);
|
|
4053
4425
|
}
|
|
4054
4426
|
|
|
4055
|
-
isInitializedWith({ selfId, roomName, isSfu }) {
|
|
4056
|
-
return this._selfId === selfId && this._roomName === roomName && isSfu === !!this._sfuServer;
|
|
4057
|
-
}
|
|
4058
|
-
|
|
4059
|
-
supportsScreenShareAudio() {
|
|
4060
|
-
return true;
|
|
4061
|
-
}
|
|
4062
|
-
|
|
4063
4427
|
_addEnabledLocalStreamId(streamId) {
|
|
4064
4428
|
this.enabledLocalStreamIds.push(streamId);
|
|
4065
4429
|
}
|
|
@@ -4085,7 +4449,7 @@ class BaseRtcManager {
|
|
|
4085
4449
|
// Some macs + ios devices have troubles using h264 encoder since safari 14
|
|
4086
4450
|
// this will make them encode VP8 instead if available
|
|
4087
4451
|
const deprioritizeH264Encoding =
|
|
4088
|
-
browserName$
|
|
4452
|
+
browserName$1 === "safari" && browserVersion >= 14 && this._features.deprioritizeH264OnSafari;
|
|
4089
4453
|
|
|
4090
4454
|
this.peerConnections[peerConnectionId] = session = new Session({
|
|
4091
4455
|
peerConnectionId,
|
|
@@ -4102,11 +4466,11 @@ class BaseRtcManager {
|
|
|
4102
4466
|
}
|
|
4103
4467
|
|
|
4104
4468
|
_getLocalCameraStream() {
|
|
4105
|
-
return this.localStreams[CAMERA_STREAM_ID
|
|
4469
|
+
return this.localStreams[CAMERA_STREAM_ID];
|
|
4106
4470
|
}
|
|
4107
4471
|
|
|
4108
4472
|
_getNonLocalCameraStreamIds() {
|
|
4109
|
-
return Object.keys(this.localStreams).filter((streamId) => streamId !== CAMERA_STREAM_ID
|
|
4473
|
+
return Object.keys(this.localStreams).filter((streamId) => streamId !== CAMERA_STREAM_ID);
|
|
4110
4474
|
}
|
|
4111
4475
|
|
|
4112
4476
|
_isScreensharingLocally() {
|
|
@@ -4135,7 +4499,7 @@ class BaseRtcManager {
|
|
|
4135
4499
|
}
|
|
4136
4500
|
const session = this._getOrCreateSession(peerConnectionId, initialBandwidth);
|
|
4137
4501
|
const constraints = { optional: [] };
|
|
4138
|
-
if (browserName$
|
|
4502
|
+
if (browserName$1 === "chrome") {
|
|
4139
4503
|
constraints.optional.push({
|
|
4140
4504
|
googCpuOveruseDetection: true,
|
|
4141
4505
|
});
|
|
@@ -4179,7 +4543,7 @@ class BaseRtcManager {
|
|
|
4179
4543
|
}
|
|
4180
4544
|
}
|
|
4181
4545
|
|
|
4182
|
-
if (browserName$
|
|
4546
|
+
if (browserName$1 === "chrome") {
|
|
4183
4547
|
peerConnectionConfig.sdpSemantics = "unified-plan";
|
|
4184
4548
|
}
|
|
4185
4549
|
|
|
@@ -4246,7 +4610,7 @@ class BaseRtcManager {
|
|
|
4246
4610
|
if (
|
|
4247
4611
|
!session.wasEverConnected &&
|
|
4248
4612
|
(pc.iceConnectionState.match(/connected|completed/) ||
|
|
4249
|
-
(browserName$
|
|
4613
|
+
(browserName$1 === "chrome" && pc.localDescription && pc.localDescription.type === "answer"))
|
|
4250
4614
|
) {
|
|
4251
4615
|
session.wasEverConnected = true;
|
|
4252
4616
|
if (this._features.bandwidth !== "false") {
|
|
@@ -4324,7 +4688,7 @@ class BaseRtcManager {
|
|
|
4324
4688
|
}
|
|
4325
4689
|
};
|
|
4326
4690
|
|
|
4327
|
-
const localCameraStream = this.localStreams[CAMERA_STREAM_ID
|
|
4691
|
+
const localCameraStream = this.localStreams[CAMERA_STREAM_ID];
|
|
4328
4692
|
if (shouldAddLocalVideo && localCameraStream) {
|
|
4329
4693
|
session.addStream(localCameraStream);
|
|
4330
4694
|
}
|
|
@@ -4333,7 +4697,7 @@ class BaseRtcManager {
|
|
|
4333
4697
|
// added in a separate session/peerConnection by SfuV2RtcManager
|
|
4334
4698
|
if (shouldAddLocalVideo) {
|
|
4335
4699
|
Object.keys(this.localStreams).forEach((id) => {
|
|
4336
|
-
if (id === CAMERA_STREAM_ID
|
|
4700
|
+
if (id === CAMERA_STREAM_ID) {
|
|
4337
4701
|
return;
|
|
4338
4702
|
}
|
|
4339
4703
|
const screenshareStream = this.localStreams[id];
|
|
@@ -4361,7 +4725,7 @@ class BaseRtcManager {
|
|
|
4361
4725
|
_cleanup(peerConnectionId) {
|
|
4362
4726
|
const session = this._getSession(peerConnectionId);
|
|
4363
4727
|
if (!session) {
|
|
4364
|
-
logger$
|
|
4728
|
+
logger$4.warn("No RTCPeerConnection in RTCManager.disconnect()", peerConnectionId);
|
|
4365
4729
|
return;
|
|
4366
4730
|
}
|
|
4367
4731
|
session.close();
|
|
@@ -4369,8 +4733,7 @@ class BaseRtcManager {
|
|
|
4369
4733
|
}
|
|
4370
4734
|
|
|
4371
4735
|
_forEachPeerConnection(func) {
|
|
4372
|
-
Object.
|
|
4373
|
-
const peerConnection = this.peerConnections[peerConnectionId];
|
|
4736
|
+
Object.values(this.peerConnections).forEach((peerConnection) => {
|
|
4374
4737
|
func(peerConnection);
|
|
4375
4738
|
});
|
|
4376
4739
|
}
|
|
@@ -4391,10 +4754,10 @@ class BaseRtcManager {
|
|
|
4391
4754
|
const promises = [];
|
|
4392
4755
|
this._forEachPeerConnection((session) => {
|
|
4393
4756
|
if (!session.hasConnectedPeerConnection()) {
|
|
4394
|
-
logger$
|
|
4757
|
+
logger$4.info("Session doesn't have a connected PeerConnection, adding pending action!");
|
|
4395
4758
|
const pendingActions = this._pendingActionsForConnectedPeerConnections;
|
|
4396
4759
|
if (!pendingActions) {
|
|
4397
|
-
logger$
|
|
4760
|
+
logger$4.warn(
|
|
4398
4761
|
`No pending action is created to repalce track, because the pending actions array is null`
|
|
4399
4762
|
);
|
|
4400
4763
|
return;
|
|
@@ -4403,7 +4766,7 @@ class BaseRtcManager {
|
|
|
4403
4766
|
const action = () => {
|
|
4404
4767
|
const replacedTrackPromise = session.replaceTrack(oldTrack, newTrack);
|
|
4405
4768
|
if (!replacedTrackPromise) {
|
|
4406
|
-
logger$
|
|
4769
|
+
logger$4.error("replaceTrack returned false!");
|
|
4407
4770
|
reject(`ReplaceTrack returned false`);
|
|
4408
4771
|
return;
|
|
4409
4772
|
}
|
|
@@ -4416,7 +4779,7 @@ class BaseRtcManager {
|
|
|
4416
4779
|
}
|
|
4417
4780
|
const replacedTrackResult = session.replaceTrack(oldTrack, newTrack);
|
|
4418
4781
|
if (!replacedTrackResult) {
|
|
4419
|
-
logger$
|
|
4782
|
+
logger$4.error("replaceTrack returned false!");
|
|
4420
4783
|
return;
|
|
4421
4784
|
}
|
|
4422
4785
|
promises.push(replacedTrackResult);
|
|
@@ -4424,138 +4787,26 @@ class BaseRtcManager {
|
|
|
4424
4787
|
return Promise.all(promises);
|
|
4425
4788
|
}
|
|
4426
4789
|
|
|
4427
|
-
_removeStreamFromPeerConnections(stream) {
|
|
4428
|
-
this._forEachPeerConnection((session) => {
|
|
4429
|
-
this._withForcedRenegotiation(session, () => session.removeStream(stream));
|
|
4430
|
-
});
|
|
4431
|
-
}
|
|
4432
|
-
|
|
4433
|
-
_removeTrackFromPeerConnections(track) {
|
|
4434
|
-
this._forEachPeerConnection((session) => {
|
|
4435
|
-
this._withForcedRenegotiation(session, () => session.removeTrack(track));
|
|
4436
|
-
});
|
|
4437
|
-
}
|
|
4438
|
-
|
|
4439
|
-
_addLocalStream(streamId, stream) {
|
|
4440
|
-
this._addEnabledLocalStreamId(streamId);
|
|
4441
|
-
this.localStreams[streamId] = stream;
|
|
4442
|
-
}
|
|
4443
|
-
|
|
4444
|
-
_removeLocalStream(streamId) {
|
|
4445
|
-
delete this.localStreams[streamId];
|
|
4446
|
-
this._deleteEnabledLocalStreamId(streamId);
|
|
4447
|
-
}
|
|
4448
|
-
|
|
4449
|
-
maybeRestrictRelayBandwidth(session) {
|
|
4450
|
-
session.maybeRestrictRelayBandwidth();
|
|
4451
|
-
}
|
|
4452
|
-
|
|
4453
|
-
addNewStream(streamId, stream) {
|
|
4454
|
-
if (stream === this.localStreams[streamId]) {
|
|
4455
|
-
// this can happen after reconnect. We do not want to add the stream to the
|
|
4456
|
-
// peerconnection again.
|
|
4457
|
-
return;
|
|
4458
|
-
}
|
|
4459
|
-
|
|
4460
|
-
this._addLocalStream(streamId, stream);
|
|
4461
|
-
|
|
4462
|
-
if (streamId === CAMERA_STREAM_ID$1) {
|
|
4463
|
-
this._addStreamToPeerConnections(stream);
|
|
4464
|
-
const [audioTrack] = stream.getAudioTracks();
|
|
4465
|
-
if (audioTrack) {
|
|
4466
|
-
this._startMonitoringAudioTrack(audioTrack);
|
|
4467
|
-
}
|
|
4468
|
-
|
|
4469
|
-
// This should not be needed, but checking nonetheless
|
|
4470
|
-
if (this._localStreamDeregisterFunction) {
|
|
4471
|
-
this._localStreamDeregisterFunction();
|
|
4472
|
-
this._localStreamDeregisterFunction = null;
|
|
4473
|
-
}
|
|
4474
|
-
|
|
4475
|
-
const localStreamHandler = (e) => {
|
|
4476
|
-
const { enable, track } = e.detail;
|
|
4477
|
-
this._handleStopOrResumeVideo({ enable, track });
|
|
4478
|
-
};
|
|
4479
|
-
|
|
4480
|
-
stream.addEventListener("stopresumevideo", localStreamHandler);
|
|
4481
|
-
this._localStreamDeregisterFunction = () => {
|
|
4482
|
-
stream.removeEventListener("stopresumevideo", localStreamHandler);
|
|
4483
|
-
};
|
|
4484
|
-
|
|
4485
|
-
return;
|
|
4486
|
-
}
|
|
4487
|
-
|
|
4488
|
-
// at this point it is clearly a screensharing stream.
|
|
4489
|
-
this._screenshareVideoTrackIds.push(stream.getVideoTracks()[0].id);
|
|
4490
|
-
this._shareScreen(streamId, stream);
|
|
4491
|
-
return;
|
|
4492
|
-
}
|
|
4493
|
-
|
|
4494
|
-
removeStream(streamId, stream) {
|
|
4495
|
-
// essentially we only ever remove screensharing streams.
|
|
4496
|
-
// SFU and P2P provide their own ways of doing this.
|
|
4497
|
-
this._removeLocalStream(streamId, stream);
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4500
|
-
replaceTrack(oldTrack, newTrack) {
|
|
4501
|
-
if (oldTrack && oldTrack.kind === "audio") {
|
|
4502
|
-
this._stopMonitoringAudioTrack(oldTrack);
|
|
4503
|
-
}
|
|
4504
|
-
if (newTrack && newTrack.kind === "audio") {
|
|
4505
|
-
this._startMonitoringAudioTrack(newTrack);
|
|
4506
|
-
}
|
|
4507
|
-
return this._replaceTrackToPeerConnections(oldTrack, newTrack);
|
|
4508
|
-
}
|
|
4509
|
-
|
|
4510
|
-
accept({ clientId, shouldAddLocalVideo }) {
|
|
4511
|
-
return this.acceptNewStream({ streamId: clientId, clientId, shouldAddLocalVideo });
|
|
4512
|
-
}
|
|
4513
|
-
|
|
4514
|
-
disconnectAll() {
|
|
4515
|
-
Object.keys(this.peerConnections).forEach((peerConnectionId) => {
|
|
4516
|
-
this.disconnect(peerConnectionId);
|
|
4517
|
-
});
|
|
4518
|
-
this.peerConnections = {};
|
|
4519
|
-
this._socketListenerDeregisterFunctions.forEach((func) => {
|
|
4520
|
-
func();
|
|
4790
|
+
_removeStreamFromPeerConnections(stream) {
|
|
4791
|
+
this._forEachPeerConnection((session) => {
|
|
4792
|
+
this._withForcedRenegotiation(session, () => session.removeStream(stream));
|
|
4521
4793
|
});
|
|
4522
|
-
this._socketListenerDeregisterFunctions = [];
|
|
4523
|
-
|
|
4524
|
-
if (this._localStreamDeregisterFunction) {
|
|
4525
|
-
this._localStreamDeregisterFunction();
|
|
4526
|
-
this._localStreamDeregisterFunction = null;
|
|
4527
|
-
}
|
|
4528
4794
|
}
|
|
4529
4795
|
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4796
|
+
_removeTrackFromPeerConnections(track) {
|
|
4797
|
+
this._forEachPeerConnection((session) => {
|
|
4798
|
+
this._withForcedRenegotiation(session, () => session.removeTrack(track));
|
|
4799
|
+
});
|
|
4800
|
+
}
|
|
4535
4801
|
|
|
4536
|
-
|
|
4537
|
-
|
|
4802
|
+
_addLocalStream(streamId, stream) {
|
|
4803
|
+
this._addEnabledLocalStreamId(streamId);
|
|
4804
|
+
this.localStreams[streamId] = stream;
|
|
4805
|
+
}
|
|
4538
4806
|
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
*/
|
|
4543
|
-
fixChromeAudio(constraints) {
|
|
4544
|
-
if (browserName$2 !== "chrome") {
|
|
4545
|
-
return;
|
|
4546
|
-
}
|
|
4547
|
-
const localStream = this._getLocalCameraStream();
|
|
4548
|
-
const audioTrack = localStream.getAudioTracks()[0];
|
|
4549
|
-
if (!audioTrack || audioTrack.readyState !== "ended") {
|
|
4550
|
-
return;
|
|
4551
|
-
}
|
|
4552
|
-
return navigator.mediaDevices.getUserMedia({ audio: constraints }).then((stream) => {
|
|
4553
|
-
const track = stream.getAudioTracks()[0];
|
|
4554
|
-
track.enabled = audioTrack.enabled; // retain mute state and don't accidentally unmute.
|
|
4555
|
-
localStream.removeTrack(audioTrack); // remove the old track.
|
|
4556
|
-
localStream.addTrack(track); // add the new track.
|
|
4557
|
-
return this.replaceTrack(audioTrack, track);
|
|
4558
|
-
});
|
|
4807
|
+
_removeLocalStream(streamId) {
|
|
4808
|
+
delete this.localStreams[streamId];
|
|
4809
|
+
this._deleteEnabledLocalStreamId(streamId);
|
|
4559
4810
|
}
|
|
4560
4811
|
|
|
4561
4812
|
_updateAndScheduleMediaServersRefresh({ iceServers, sfuServer, mediaserverConfigTtlSeconds }) {
|
|
@@ -4579,114 +4830,6 @@ class BaseRtcManager {
|
|
|
4579
4830
|
this._fetchMediaServersTimer = null;
|
|
4580
4831
|
}
|
|
4581
4832
|
|
|
4582
|
-
setupSocketListeners() {
|
|
4583
|
-
this._socketListenerDeregisterFunctions = [
|
|
4584
|
-
() => this._clearMediaServersRefresh(),
|
|
4585
|
-
|
|
4586
|
-
this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
|
|
4587
|
-
if (data.error) {
|
|
4588
|
-
logger$5.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
|
|
4589
|
-
return;
|
|
4590
|
-
}
|
|
4591
|
-
this._updateAndScheduleMediaServersRefresh(data);
|
|
4592
|
-
}),
|
|
4593
|
-
|
|
4594
|
-
this._serverSocket.on(RELAY_MESSAGES.READY_TO_RECEIVE_OFFER, (data) => {
|
|
4595
|
-
this._connect(data.clientId);
|
|
4596
|
-
}),
|
|
4597
|
-
|
|
4598
|
-
this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, (data) => {
|
|
4599
|
-
const session = this._getSession(data.clientId);
|
|
4600
|
-
if (!session) {
|
|
4601
|
-
logger$5.warn("No RTCPeerConnection on ICE_CANDIDATE", data);
|
|
4602
|
-
return;
|
|
4603
|
-
}
|
|
4604
|
-
session.addIceCandidate(data.message);
|
|
4605
|
-
}),
|
|
4606
|
-
|
|
4607
|
-
this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, (data) => {
|
|
4608
|
-
const session = this._getSession(data.clientId);
|
|
4609
|
-
if (!session) {
|
|
4610
|
-
logger$5.warn("No RTCPeerConnection on ICE_END_OF_CANDIDATES", data);
|
|
4611
|
-
return;
|
|
4612
|
-
}
|
|
4613
|
-
session.addIceCandidate(null);
|
|
4614
|
-
}),
|
|
4615
|
-
|
|
4616
|
-
// when a new SDP offer is received from another client
|
|
4617
|
-
this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, (data) => {
|
|
4618
|
-
const session = this._getSession(data.clientId);
|
|
4619
|
-
if (!session) {
|
|
4620
|
-
logger$5.warn("No RTCPeerConnection on SDP_OFFER", data);
|
|
4621
|
-
return;
|
|
4622
|
-
}
|
|
4623
|
-
const offer = this._transformIncomingSdp(data.message, session.pc);
|
|
4624
|
-
session.handleOffer(offer).then((answer) => {
|
|
4625
|
-
this._emitServerEvent(RELAY_MESSAGES.SDP_ANSWER, {
|
|
4626
|
-
receiverId: data.clientId,
|
|
4627
|
-
message: this._transformOutgoingSdp(answer),
|
|
4628
|
-
});
|
|
4629
|
-
});
|
|
4630
|
-
}),
|
|
4631
|
-
|
|
4632
|
-
// when a new SDP answer is received from another client
|
|
4633
|
-
this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, (data) => {
|
|
4634
|
-
const session = this._getSession(data.clientId);
|
|
4635
|
-
if (!session) {
|
|
4636
|
-
logger$5.warn("No RTCPeerConnection on SDP_ANSWER", data);
|
|
4637
|
-
return;
|
|
4638
|
-
}
|
|
4639
|
-
const answer = this._transformIncomingSdp(data.message, session.pc);
|
|
4640
|
-
session.handleAnswer(answer);
|
|
4641
|
-
}),
|
|
4642
|
-
|
|
4643
|
-
// if this is a reconnect to signal-server during screen-share we must let signal-server know
|
|
4644
|
-
this._serverSocket.on(PROTOCOL_RESPONSES.ROOM_JOINED, ({ room: { sfuServer: isSfu } }) => {
|
|
4645
|
-
if (isSfu || !this._wasScreenSharing) return;
|
|
4646
|
-
|
|
4647
|
-
const screenShareStreamId = Object.keys(this.localStreams).find((id) => id !== CAMERA_STREAM_ID$1);
|
|
4648
|
-
if (!screenShareStreamId) {
|
|
4649
|
-
return;
|
|
4650
|
-
}
|
|
4651
|
-
|
|
4652
|
-
const screenshareStream = this.localStreams[screenShareStreamId];
|
|
4653
|
-
if (!screenshareStream) {
|
|
4654
|
-
logger$5.warn("screenshare stream %s not found", screenShareStreamId);
|
|
4655
|
-
return;
|
|
4656
|
-
}
|
|
4657
|
-
|
|
4658
|
-
const hasAudioTrack = screenshareStream.getAudioTracks().length > 0;
|
|
4659
|
-
|
|
4660
|
-
this._emitServerEvent(PROTOCOL_REQUESTS.START_SCREENSHARE, {
|
|
4661
|
-
streamId: screenShareStreamId,
|
|
4662
|
-
hasAudioTrack,
|
|
4663
|
-
});
|
|
4664
|
-
}),
|
|
4665
|
-
];
|
|
4666
|
-
}
|
|
4667
|
-
|
|
4668
|
-
sendAudioMutedStats(muted) {
|
|
4669
|
-
rtcStats.sendEvent("audio_muted", { muted });
|
|
4670
|
-
}
|
|
4671
|
-
|
|
4672
|
-
sendVideoMutedStats(muted) {
|
|
4673
|
-
rtcStats.sendEvent("video_muted", { muted });
|
|
4674
|
-
}
|
|
4675
|
-
|
|
4676
|
-
sendStatsCustomEvent(eventName, data) {
|
|
4677
|
-
rtcStats.sendEvent(eventName, data);
|
|
4678
|
-
}
|
|
4679
|
-
|
|
4680
|
-
rtcStatsDisconnect() {
|
|
4681
|
-
rtcStats.server.close();
|
|
4682
|
-
}
|
|
4683
|
-
|
|
4684
|
-
rtcStatsReconnect() {
|
|
4685
|
-
if (!rtcStats.server.connected) {
|
|
4686
|
-
rtcStats.server.connect();
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
|
|
4690
4833
|
_startMonitoringAudioTrack(track) {
|
|
4691
4834
|
track.addEventListener("ended", this._audioTrackOnEnded);
|
|
4692
4835
|
}
|
|
@@ -4694,113 +4837,6 @@ class BaseRtcManager {
|
|
|
4694
4837
|
_stopMonitoringAudioTrack(track) {
|
|
4695
4838
|
track.removeEventListener("ended", this._audioTrackOnEnded);
|
|
4696
4839
|
}
|
|
4697
|
-
|
|
4698
|
-
setAudioOnly(audioOnly) {
|
|
4699
|
-
this._isAudioOnlyMode = audioOnly;
|
|
4700
|
-
|
|
4701
|
-
this._forEachPeerConnection((session) => {
|
|
4702
|
-
if (session.hasConnectedPeerConnection()) {
|
|
4703
|
-
this._withForcedRenegotiation(session, () =>
|
|
4704
|
-
session.setAudioOnly(this._isAudioOnlyMode, this._screenshareVideoTrackIds)
|
|
4705
|
-
);
|
|
4706
|
-
}
|
|
4707
|
-
});
|
|
4708
|
-
}
|
|
4709
|
-
|
|
4710
|
-
setRemoteScreenshareVideoTrackIds(remoteScreenshareVideoTrackIds = []) {
|
|
4711
|
-
const localScreenshareStream = this._getFirstLocalNonCameraStream();
|
|
4712
|
-
|
|
4713
|
-
this._screenshareVideoTrackIds = [
|
|
4714
|
-
...(localScreenshareStream?.track ? [localScreenshareStream.track.id] : []),
|
|
4715
|
-
...remoteScreenshareVideoTrackIds,
|
|
4716
|
-
];
|
|
4717
|
-
}
|
|
4718
|
-
|
|
4719
|
-
setRoomSessionId(roomSessionId) {
|
|
4720
|
-
this._roomSessionId = roomSessionId;
|
|
4721
|
-
}
|
|
4722
|
-
}
|
|
4723
|
-
|
|
4724
|
-
const lowPixelCount = 320 * 180;
|
|
4725
|
-
const lowBitratePerPixel = 150000 / lowPixelCount;
|
|
4726
|
-
const highPixelCount = 1280 * 720;
|
|
4727
|
-
const highBitratePerPixel = 1000000 / highPixelCount;
|
|
4728
|
-
const bitrateChangePerPixel = (highBitratePerPixel - lowBitratePerPixel) / (highPixelCount - lowPixelCount);
|
|
4729
|
-
|
|
4730
|
-
// calculates a bitrate for a given resolution+frameRate
|
|
4731
|
-
function getOptimalBitrate(width, height, frameRate) {
|
|
4732
|
-
let targetPixelCount = width * height;
|
|
4733
|
-
if (targetPixelCount < lowPixelCount) targetPixelCount = lowPixelCount;
|
|
4734
|
-
if (targetPixelCount > highPixelCount) targetPixelCount = highPixelCount;
|
|
4735
|
-
|
|
4736
|
-
let targetBitratePerPixel = lowBitratePerPixel;
|
|
4737
|
-
if (targetPixelCount > highPixelCount) targetBitratePerPixel = highBitratePerPixel;
|
|
4738
|
-
else if (targetPixelCount > lowPixelCount) {
|
|
4739
|
-
targetBitratePerPixel += (targetPixelCount - lowPixelCount) * bitrateChangePerPixel;
|
|
4740
|
-
}
|
|
4741
|
-
|
|
4742
|
-
// we use the actual resolution for the target bitrate
|
|
4743
|
-
let targetBitrate = width * height * targetBitratePerPixel;
|
|
4744
|
-
|
|
4745
|
-
// adjust bitrate down a bit if reduced framerate
|
|
4746
|
-
if (frameRate <= 15) targetBitrate = targetBitrate * 0.7;
|
|
4747
|
-
else if (frameRate <= 24) targetBitrate = targetBitrate * 0.9;
|
|
4748
|
-
|
|
4749
|
-
return targetBitrate;
|
|
4750
|
-
}
|
|
4751
|
-
|
|
4752
|
-
// taken from https://github.com/sindresorhus/ip-regex ^5.0.0
|
|
4753
|
-
// inlined because it's import caused errors in browser-sdk when running tests
|
|
4754
|
-
const word = "[a-fA-F\\d:]";
|
|
4755
|
-
|
|
4756
|
-
const boundry = (options) =>
|
|
4757
|
-
options && options.includeBoundaries ? `(?:(?<=\\s|^)(?=${word})|(?<=${word})(?=\\s|$))` : "";
|
|
4758
|
-
|
|
4759
|
-
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}";
|
|
4760
|
-
|
|
4761
|
-
const v6segment = "[a-fA-F\\d]{1,4}";
|
|
4762
|
-
|
|
4763
|
-
const v6 = `
|
|
4764
|
-
(?:
|
|
4765
|
-
(?:${v6segment}:){7}(?:${v6segment}|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
|
|
4766
|
-
(?:${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
|
|
4767
|
-
(?:${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
|
|
4768
|
-
(?:${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
|
|
4769
|
-
(?:${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
|
|
4770
|
-
(?:${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
|
|
4771
|
-
(?:${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
|
|
4772
|
-
(?::(?:(?::${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
|
|
4773
|
-
)(?:%[0-9a-zA-Z]{1,})? // %eth0 %1
|
|
4774
|
-
`
|
|
4775
|
-
.replace(/\s*\/\/.*$/gm, "")
|
|
4776
|
-
.replace(/\n/g, "")
|
|
4777
|
-
.trim();
|
|
4778
|
-
|
|
4779
|
-
// Pre-compile only the exact regexes because adding a global flag make regexes stateful
|
|
4780
|
-
const v46Exact = new RegExp(`(?:^${v4}$)|(?:^${v6}$)`);
|
|
4781
|
-
const v4exact = new RegExp(`^${v4}$`);
|
|
4782
|
-
const v6exact = new RegExp(`^${v6}$`);
|
|
4783
|
-
|
|
4784
|
-
const ipRegex = (options) =>
|
|
4785
|
-
options && options.exact
|
|
4786
|
-
? v46Exact
|
|
4787
|
-
: new RegExp(
|
|
4788
|
-
`(?:${boundry(options)}${v4}${boundry(options)})|(?:${boundry(options)}${v6}${boundry(options)})`,
|
|
4789
|
-
"g"
|
|
4790
|
-
);
|
|
4791
|
-
|
|
4792
|
-
ipRegex.v4 = (options) =>
|
|
4793
|
-
options && options.exact ? v4exact : new RegExp(`${boundry(options)}${v4}${boundry(options)}`, "g");
|
|
4794
|
-
ipRegex.v6 = (options) =>
|
|
4795
|
-
options && options.exact ? v6exact : new RegExp(`${boundry(options)}${v6}${boundry(options)}`, "g");
|
|
4796
|
-
|
|
4797
|
-
const logger$4 = new Logger();
|
|
4798
|
-
|
|
4799
|
-
const ICE_PUBLIC_IP_GATHERING_TIMEOUT = 3 * 1000;
|
|
4800
|
-
const CAMERA_STREAM_ID = RtcStream.getCameraId();
|
|
4801
|
-
const browserName$1 = adapter.browserDetails.browser;
|
|
4802
|
-
|
|
4803
|
-
class P2pRtcManager extends BaseRtcManager {
|
|
4804
4840
|
_connect(clientId) {
|
|
4805
4841
|
this.rtcStatsReconnect();
|
|
4806
4842
|
const shouldAddLocalVideo = true;
|
|
@@ -4908,15 +4944,16 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
4908
4944
|
}
|
|
4909
4945
|
session.isOperationPending = true;
|
|
4910
4946
|
|
|
4911
|
-
const { vp9On, av1On, redOn } = this._features;
|
|
4947
|
+
const { vp9On, av1On, redOn, rtpAbsCaptureTimeOn } = this._features;
|
|
4912
4948
|
|
|
4913
4949
|
// Set codec preferences to video transceivers
|
|
4914
4950
|
if (vp9On || av1On || redOn) {
|
|
4915
4951
|
this._setCodecPreferences(pc, vp9On, av1On, redOn);
|
|
4916
4952
|
}
|
|
4917
|
-
|
|
4918
4953
|
pc.createOffer(constraints || this.offerOptions)
|
|
4919
4954
|
.then((offer) => {
|
|
4955
|
+
// Add https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/abs-capture-time
|
|
4956
|
+
if (rtpAbsCaptureTimeOn) offer.sdp = addAbsCaptureTimeExtMap(offer.sdp);
|
|
4920
4957
|
// SDP munging workaround for Firefox, because it doesn't support setCodecPreferences()
|
|
4921
4958
|
// Only vp9 because FF does not support AV1 yet
|
|
4922
4959
|
if ((vp9On || redOn) && browserName$1 === "firefox") {
|
|
@@ -5275,7 +5312,7 @@ class P2pRtcManager extends BaseRtcManager {
|
|
|
5275
5312
|
}
|
|
5276
5313
|
|
|
5277
5314
|
removeStream(streamId, stream, requestedByClientId) {
|
|
5278
|
-
|
|
5315
|
+
this._removeLocalStream(streamId, stream);
|
|
5279
5316
|
this._removeStreamFromPeerConnections(stream);
|
|
5280
5317
|
this._wasScreenSharing = false;
|
|
5281
5318
|
this._emitServerEvent(PROTOCOL_REQUESTS.STOP_SCREENSHARE, { streamId, requestedByClientId });
|
|
@@ -6173,6 +6210,7 @@ class VegaMediaQualityMonitor extends EventEmitter {
|
|
|
6173
6210
|
}
|
|
6174
6211
|
}
|
|
6175
6212
|
|
|
6213
|
+
const adapter = adapterRaw.default ?? adapterRaw;
|
|
6176
6214
|
const logger = new Logger();
|
|
6177
6215
|
|
|
6178
6216
|
const browserName = adapter.browserDetails.browser;
|
|
@@ -7983,6 +8021,7 @@ const doConnectRtc = createAppThunk(() => (dispatch, getState) => {
|
|
|
7983
8021
|
const dispatcher = selectRtcConnectionRaw(state).rtcManagerDispatcher;
|
|
7984
8022
|
const isCameraEnabled = selectIsCameraEnabled(state);
|
|
7985
8023
|
const isMicrophoneEnabled = selectIsMicrophoneEnabled(state);
|
|
8024
|
+
const isNodeSdk = selectAppIsNodeSdk(state);
|
|
7986
8025
|
if (dispatcher) {
|
|
7987
8026
|
return;
|
|
7988
8027
|
}
|
|
@@ -8008,6 +8047,7 @@ const doConnectRtc = createAppThunk(() => (dispatch, getState) => {
|
|
|
8008
8047
|
vp9On: false,
|
|
8009
8048
|
h264On: false,
|
|
8010
8049
|
simulcastScreenshareOn: false,
|
|
8050
|
+
deviceHandlerFactory: isNodeSdk ? Chrome111.createFactory() : undefined,
|
|
8011
8051
|
},
|
|
8012
8052
|
});
|
|
8013
8053
|
dispatch(rtcDispatcherCreated(rtcManagerDispatcher));
|
|
@@ -9464,7 +9504,7 @@ class LocalParticipant extends RoomParticipant {
|
|
|
9464
9504
|
}
|
|
9465
9505
|
}
|
|
9466
9506
|
|
|
9467
|
-
const sdkVersion = "0.
|
|
9507
|
+
const sdkVersion = "0.5.0";
|
|
9468
9508
|
|
|
9469
9509
|
const defaultSubdomainPattern = /^(?:([^.]+)[.])?((:?[^.]+[.]){1,}[^.]+)$/;
|
|
9470
9510
|
const localstackPattern = /^(?:([^.]+)-)?(ip-[^.]*[.](?:hereby[.]dev|rfc1918[.]disappear[.]at)(?::\d+|))$/;
|
|
@@ -9536,4 +9576,4 @@ function createServices() {
|
|
|
9536
9576
|
};
|
|
9537
9577
|
}
|
|
9538
9578
|
|
|
9539
|
-
export { ApiClient, Credentials, CredentialsService, LocalParticipant, OrganizationApiClient, OrganizationService, OrganizationServiceCache, RoomService, addAppListener, appLeft, appSlice, chatSlice, cloudRecordingSlice, createAppAsyncThunk, createAppThunk, createReactor, createServices, createStore, createWebRtcEmitter, deviceBusy, deviceCredentialsSlice, deviceIdentified, deviceIdentifying, doAcceptWaitingParticipant, doAppJoin, doConnectRoom, doConnectRtc, doDisconnectRtc, doEnableAudio, doEnableVideo, doGetDeviceCredentials, doHandleAcceptStreams, doHandleStreamingStarted, doHandleStreamingStopped, doKnockRoom, doOrganizationFetch, doRejectWaitingParticipant, doRtcAnalyticsCustomEventsInitialize, doRtcManagerCreated, doRtcManagerInitialize, doRtcReportStreamResolution, doSendChatMessage, doSetDevice, doSetDisplayName, doSetLocalParticipant, doSignalDisconnect, doSignalIdentifyDevice, doSignalReconnect, doSignalSocketConnect, doStartCloudRecording, doStartLocalMedia, doStartScreenshare, doStopCloudRecording, doStopLocalMedia, doStopScreenshare, doSwitchLocalStream, doToggleCamera, doUpdateDeviceList, initialCloudRecordingState, initialLocalMediaState, isAcceptingStreams, listenerMiddleware, localMediaSlice, localMediaStopped, localParticipantSlice, localScreenshareSlice, localStreamMetadataUpdated, observeStore, organizationSlice, participantStreamAdded, participantStreamIdAdded, recordingRequestStarted, remoteParticipantsSlice, resolutionReported, roomConnectionSlice, rootReducer, rtcAnalyticsCustomEvents, rtcAnalyticsSlice, rtcConnectionSlice, rtcDisconnected, rtcDispatcherCreated, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, sdkVersion, selectAppDisplayName, selectAppExternalId, selectAppRaw, selectAppRoomKey, selectAppRoomName, selectAppRoomUrl, selectAppSdkVersion, selectAppWantsToJoin, selectBusyDeviceIds, selectCameraDeviceError, selectCameraDevices, selectChatMessages, selectChatRaw, selectCloudRecordingError, selectCloudRecordingRaw, selectCloudRecordingStartedAt, selectCloudRecordingStatus, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectDeviceCredentialsRaw, selectDeviceId, selectHasFetchedDeviceCredentials, selectIsAcceptingStreams, selectIsCameraEnabled, selectIsCloudRecording, selectIsLocalMediaStarting, selectIsMicrophoneEnabled, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsToggleCamera, selectLocalMediaConstraintsOptions, selectLocalMediaDevices, selectLocalMediaIsSwitchingStream, selectLocalMediaOptions, selectLocalMediaOwnsStream, selectLocalMediaRaw, selectLocalMediaShouldStartWithOptions, selectLocalMediaShouldStop, selectLocalMediaStartError, selectLocalMediaStatus, selectLocalMediaStream, selectLocalParticipantIsScreenSharing, selectLocalParticipantRaw, selectLocalParticipantRole, selectLocalScreenshareRaw, selectLocalScreenshareStatus, selectLocalScreenshareStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectOrganizationId, selectOrganizationRaw, selectRemoteParticipants, selectRemoteParticipantsRaw, selectRoomConnectionRaw, selectRoomConnectionSession, selectRoomConnectionSessionId, selectRoomConnectionStatus, selectRtcConnectionRaw, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectRtcManager, selectRtcManagerInitialized, selectRtcStatus, selectScreenshares, selectSelfId, selectShouldConnectRoom, selectShouldConnectRtc, selectShouldConnectSignal, selectShouldDisconnectRtc, selectShouldFetchDeviceCredentials, selectShouldFetchOrganization, selectShouldIdentifyDevice, selectShouldInitializeRtc, selectSignalConnectionDeviceIdentified, selectSignalConnectionRaw, selectSignalConnectionSocket, selectSignalIsIdentifyingDevice, selectSignalStatus, selectSpeakerDevices, selectStreamingRaw, selectStreamsToAccept, selectWaitingParticipants, selectWaitingParticipantsRaw, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setLocalMediaOptions, setLocalMediaStream, setRoomKey, signalConnectionSlice, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, startAppListening, stopScreenshare, streamStatusUpdated, streamingSlice, toggleCameraEnabled, toggleMicrophoneEnabled, updateReportedValues, waitingParticipantsSlice };
|
|
9579
|
+
export { ApiClient, Credentials, CredentialsService, LocalParticipant, OrganizationApiClient, OrganizationService, OrganizationServiceCache, RoomService, addAppListener, appLeft, appSlice, chatSlice, cloudRecordingSlice, createAppAsyncThunk, createAppThunk, createReactor, createServices, createStore, createWebRtcEmitter, deviceBusy, deviceCredentialsSlice, deviceIdentified, deviceIdentifying, doAcceptWaitingParticipant, doAppJoin, doConnectRoom, doConnectRtc, doDisconnectRtc, doEnableAudio, doEnableVideo, doGetDeviceCredentials, doHandleAcceptStreams, doHandleStreamingStarted, doHandleStreamingStopped, doKnockRoom, doOrganizationFetch, doRejectWaitingParticipant, doRtcAnalyticsCustomEventsInitialize, doRtcManagerCreated, doRtcManagerInitialize, doRtcReportStreamResolution, doSendChatMessage, doSetDevice, doSetDisplayName, doSetLocalParticipant, doSignalDisconnect, doSignalIdentifyDevice, doSignalReconnect, doSignalSocketConnect, doStartCloudRecording, doStartLocalMedia, doStartScreenshare, doStopCloudRecording, doStopLocalMedia, doStopScreenshare, doSwitchLocalStream, doToggleCamera, doUpdateDeviceList, initialCloudRecordingState, initialLocalMediaState, isAcceptingStreams, listenerMiddleware, localMediaSlice, localMediaStopped, localParticipantSlice, localScreenshareSlice, localStreamMetadataUpdated, observeStore, organizationSlice, participantStreamAdded, participantStreamIdAdded, recordingRequestStarted, remoteParticipantsSlice, resolutionReported, roomConnectionSlice, rootReducer, rtcAnalyticsCustomEvents, rtcAnalyticsSlice, rtcConnectionSlice, rtcDisconnected, rtcDispatcherCreated, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, sdkVersion, selectAppDisplayName, selectAppExternalId, selectAppIsNodeSdk, selectAppRaw, selectAppRoomKey, selectAppRoomName, selectAppRoomUrl, selectAppSdkVersion, selectAppWantsToJoin, selectBusyDeviceIds, selectCameraDeviceError, selectCameraDevices, selectChatMessages, selectChatRaw, selectCloudRecordingError, selectCloudRecordingRaw, selectCloudRecordingStartedAt, selectCloudRecordingStatus, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectDeviceCredentialsRaw, selectDeviceId, selectHasFetchedDeviceCredentials, selectIsAcceptingStreams, selectIsCameraEnabled, selectIsCloudRecording, selectIsLocalMediaStarting, selectIsMicrophoneEnabled, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsToggleCamera, selectLocalMediaConstraintsOptions, selectLocalMediaDevices, selectLocalMediaIsSwitchingStream, selectLocalMediaOptions, selectLocalMediaOwnsStream, selectLocalMediaRaw, selectLocalMediaShouldStartWithOptions, selectLocalMediaShouldStop, selectLocalMediaStartError, selectLocalMediaStatus, selectLocalMediaStream, selectLocalParticipantIsScreenSharing, selectLocalParticipantRaw, selectLocalParticipantRole, selectLocalScreenshareRaw, selectLocalScreenshareStatus, selectLocalScreenshareStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectOrganizationId, selectOrganizationRaw, selectRemoteParticipants, selectRemoteParticipantsRaw, selectRoomConnectionError, selectRoomConnectionRaw, selectRoomConnectionSession, selectRoomConnectionSessionId, selectRoomConnectionStatus, selectRtcConnectionRaw, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectRtcManager, selectRtcManagerInitialized, selectRtcStatus, selectScreenshares, selectSelfId, selectShouldConnectRoom, selectShouldConnectRtc, selectShouldConnectSignal, selectShouldDisconnectRtc, selectShouldFetchDeviceCredentials, selectShouldFetchOrganization, selectShouldIdentifyDevice, selectShouldInitializeRtc, selectSignalConnectionDeviceIdentified, selectSignalConnectionRaw, selectSignalConnectionSocket, selectSignalIsIdentifyingDevice, selectSignalStatus, selectSpeakerDevices, selectStreamingRaw, selectStreamsToAccept, selectWaitingParticipants, selectWaitingParticipantsRaw, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setLocalMediaOptions, setLocalMediaStream, setRoomKey, signalConnectionSlice, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, startAppListening, stopScreenshare, streamStatusUpdated, streamingSlice, toggleCameraEnabled, toggleMicrophoneEnabled, updateReportedValues, waitingParticipantsSlice };
|