@whereby.com/media 8.0.4 → 8.0.6
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.cjs +2171 -2313
- package/dist/index.d.cts +1 -7
- package/dist/index.d.mts +1 -7
- package/dist/index.d.ts +1 -7
- package/dist/index.mjs +2171 -2313
- package/dist/legacy-esm.js +2171 -2313
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var mediasoupClient = require('mediasoup-client');
|
|
4
3
|
var adapterRaw = require('webrtc-adapter');
|
|
5
|
-
var EventEmitter = require('events');
|
|
6
4
|
var rtcstats = require('rtcstats');
|
|
7
5
|
var uuid = require('uuid');
|
|
8
|
-
var socket_ioClient = require('socket.io-client');
|
|
9
6
|
var SDPUtils = require('sdp');
|
|
10
7
|
var sdpTransform = require('sdp-transform');
|
|
11
8
|
var ipAddress = require('ip-address');
|
|
12
9
|
var checkIp = require('check-ip');
|
|
13
10
|
var validate = require('uuid-validate');
|
|
11
|
+
var mediasoupClient = require('mediasoup-client');
|
|
12
|
+
var EventEmitter = require('events');
|
|
13
|
+
var socket_ioClient = require('socket.io-client');
|
|
14
14
|
|
|
15
15
|
function _interopNamespaceDefault(e) {
|
|
16
16
|
var n = Object.create(null);
|
|
@@ -31,91 +31,6 @@ function _interopNamespaceDefault(e) {
|
|
|
31
31
|
|
|
32
32
|
var sdpTransform__namespace = /*#__PURE__*/_interopNamespaceDefault(sdpTransform);
|
|
33
33
|
|
|
34
|
-
class AssertionError extends Error {
|
|
35
|
-
constructor(options) {
|
|
36
|
-
super(options.message);
|
|
37
|
-
this.name = "AssertionError";
|
|
38
|
-
this.code = "ERR_ASSERTION";
|
|
39
|
-
this.actual = options.actual;
|
|
40
|
-
this.expected = options.expected;
|
|
41
|
-
this.operator = options.operator;
|
|
42
|
-
if (Error.captureStackTrace) {
|
|
43
|
-
Error.captureStackTrace(this, options.stackStartFn);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function innerOk(fn, argLen, value, message) {
|
|
48
|
-
if (!value) {
|
|
49
|
-
let generatedMessage = false;
|
|
50
|
-
if (argLen === 0) {
|
|
51
|
-
generatedMessage = true;
|
|
52
|
-
message = "No value argument passed to `assert.ok()`";
|
|
53
|
-
}
|
|
54
|
-
else if (message instanceof Error) {
|
|
55
|
-
throw message;
|
|
56
|
-
}
|
|
57
|
-
const err = new AssertionError({
|
|
58
|
-
actual: value,
|
|
59
|
-
expected: true,
|
|
60
|
-
message,
|
|
61
|
-
operator: "==",
|
|
62
|
-
stackStartFn: fn,
|
|
63
|
-
});
|
|
64
|
-
err.generatedMessage = generatedMessage;
|
|
65
|
-
throw err;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function innerFail(obj) {
|
|
69
|
-
if (obj.message instanceof Error)
|
|
70
|
-
throw obj.message;
|
|
71
|
-
throw new AssertionError(obj);
|
|
72
|
-
}
|
|
73
|
-
function ok(...args) {
|
|
74
|
-
innerOk(ok, args.length, ...args);
|
|
75
|
-
}
|
|
76
|
-
const assert = {
|
|
77
|
-
fail: (message) => {
|
|
78
|
-
innerFail({
|
|
79
|
-
actual: "fail()",
|
|
80
|
-
expected: "fail() should not be called",
|
|
81
|
-
message,
|
|
82
|
-
operator: "fail",
|
|
83
|
-
stackStartFn: assert.fail,
|
|
84
|
-
});
|
|
85
|
-
},
|
|
86
|
-
ok,
|
|
87
|
-
};
|
|
88
|
-
assert.notEqual = function notEqual(actual, expected, message) {
|
|
89
|
-
if (arguments.length < 2) {
|
|
90
|
-
throw new Error("'actual' and 'expected' arguments are required");
|
|
91
|
-
}
|
|
92
|
-
if (actual == expected) {
|
|
93
|
-
innerFail({
|
|
94
|
-
actual,
|
|
95
|
-
expected,
|
|
96
|
-
message,
|
|
97
|
-
operator: "!=",
|
|
98
|
-
stackStartFn: notEqual,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
assert.ok = ok;
|
|
103
|
-
|
|
104
|
-
const createWorker = (fn) => {
|
|
105
|
-
return new Worker(URL.createObjectURL(new Blob(["self.onmessage = ", fn.toString()], { type: "text/javascript" })));
|
|
106
|
-
};
|
|
107
|
-
const generateByteString = (count) => {
|
|
108
|
-
if (count === 0) {
|
|
109
|
-
return "";
|
|
110
|
-
}
|
|
111
|
-
const count2 = count / 2;
|
|
112
|
-
let result = "F";
|
|
113
|
-
while (result.length <= count2) {
|
|
114
|
-
result += result;
|
|
115
|
-
}
|
|
116
|
-
return result + result.substring(0, count - result.length);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
34
|
/******************************************************************************
|
|
120
35
|
Copyright (c) Microsoft Corporation.
|
|
121
36
|
|
|
@@ -148,2389 +63,2357 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
148
63
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
149
64
|
};
|
|
150
65
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
class Logger {
|
|
195
|
-
constructor() {
|
|
196
|
-
this._isEnabled = false;
|
|
197
|
-
this._isEnabled = debugOn;
|
|
198
|
-
}
|
|
199
|
-
isEnabled() {
|
|
200
|
-
return this._isEnabled;
|
|
201
|
-
}
|
|
202
|
-
enable() {
|
|
203
|
-
this._isEnabled = true;
|
|
204
|
-
}
|
|
205
|
-
disable() {
|
|
206
|
-
this._isEnabled = false;
|
|
207
|
-
}
|
|
208
|
-
info(...params) {
|
|
209
|
-
if (!this._isEnabled) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
return console.info(...params);
|
|
213
|
-
}
|
|
214
|
-
warn(...params) {
|
|
215
|
-
if (!this._isEnabled) {
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
return console.warn(...params);
|
|
219
|
-
}
|
|
220
|
-
error(...params) {
|
|
221
|
-
if (!this._isEnabled) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
return console.error(...params);
|
|
225
|
-
}
|
|
226
|
-
withDebugLogger(myDebugger) {
|
|
227
|
-
this._debugger = myDebugger;
|
|
228
|
-
return this;
|
|
229
|
-
}
|
|
230
|
-
debug(...params) {
|
|
231
|
-
if (!this._isEnabled || !this._debugger) {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const suppliedParams = [];
|
|
235
|
-
params.forEach((param) => {
|
|
236
|
-
if (typeof param === "function") {
|
|
237
|
-
const suppliedParam = param();
|
|
238
|
-
suppliedParams.push(suppliedParam);
|
|
66
|
+
var _a$7;
|
|
67
|
+
(_a$7 = adapterRaw.default) !== null && _a$7 !== void 0 ? _a$7 : adapterRaw;
|
|
68
|
+
const RTCSTATS_PROTOCOL_VERSION = "1.0";
|
|
69
|
+
const GETSTATS_BUFFER_SIZE = 20;
|
|
70
|
+
const clientInfo = {
|
|
71
|
+
id: uuid.v4(),
|
|
72
|
+
connectionNumber: 0,
|
|
73
|
+
};
|
|
74
|
+
const noop = () => { };
|
|
75
|
+
let resetDelta = noop;
|
|
76
|
+
function rtcStatsConnection(wsURL, logger = console) {
|
|
77
|
+
const buffer = [];
|
|
78
|
+
let ws;
|
|
79
|
+
let organizationId;
|
|
80
|
+
let clientId;
|
|
81
|
+
let displayName;
|
|
82
|
+
let userRole;
|
|
83
|
+
let roomSessionId;
|
|
84
|
+
let connectionShouldBeOpen;
|
|
85
|
+
let connectionAttempt = 0;
|
|
86
|
+
let hasPassedOnRoomSessionId = false;
|
|
87
|
+
let getStatsBufferUsed = 0;
|
|
88
|
+
let deviceId;
|
|
89
|
+
let roomProduct;
|
|
90
|
+
let roomMode;
|
|
91
|
+
let sfuServer;
|
|
92
|
+
let featureFlags;
|
|
93
|
+
const connection = {
|
|
94
|
+
connected: false,
|
|
95
|
+
attemptedConnectedAtLeastOnce: false,
|
|
96
|
+
trace: (...args) => {
|
|
97
|
+
args.push(Date.now());
|
|
98
|
+
if (args[0] === "customEvent" && args[2].type === "roomSessionId") {
|
|
99
|
+
const oldRoomSessionIdValue = roomSessionId && roomSessionId[2].value.roomSessionId;
|
|
100
|
+
const newRoomSessionIdValue = args[2].value.roomSessionId;
|
|
101
|
+
roomSessionId = args;
|
|
102
|
+
if (hasPassedOnRoomSessionId &&
|
|
103
|
+
newRoomSessionIdValue &&
|
|
104
|
+
newRoomSessionIdValue !== oldRoomSessionIdValue) {
|
|
105
|
+
ws === null || ws === void 0 ? void 0 : ws.close();
|
|
106
|
+
}
|
|
107
|
+
if (newRoomSessionIdValue)
|
|
108
|
+
hasPassedOnRoomSessionId = true;
|
|
239
109
|
}
|
|
240
|
-
else {
|
|
241
|
-
|
|
110
|
+
else if (args[0] === "customEvent" && args[2].type === "clientId") {
|
|
111
|
+
clientId = args;
|
|
242
112
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
113
|
+
else if (args[0] === "customEvent" && args[2].type === "organizationId") {
|
|
114
|
+
organizationId = args;
|
|
115
|
+
}
|
|
116
|
+
else if (args[0] === "customEvent" && args[2].type === "displayName") {
|
|
117
|
+
displayName = args;
|
|
118
|
+
}
|
|
119
|
+
else if (args[0] === "customEvent" && args[2].type === "userRole") {
|
|
120
|
+
userRole = args;
|
|
121
|
+
}
|
|
122
|
+
else if (args[0] === "customEvent" && args[2].type === "deviceId") {
|
|
123
|
+
deviceId = args;
|
|
124
|
+
}
|
|
125
|
+
else if (args[0] === "customEvent" && args[2].type === "roomProduct") {
|
|
126
|
+
roomProduct = args;
|
|
127
|
+
}
|
|
128
|
+
else if (args[0] === "customEvent" && args[2].type === "roomMode") {
|
|
129
|
+
roomMode = args;
|
|
130
|
+
}
|
|
131
|
+
else if (args[0] === "customEvent" && args[2].type === "sfuServer") {
|
|
132
|
+
sfuServer = args;
|
|
133
|
+
}
|
|
134
|
+
else if (args[0] === "customEvent" && args[2].type === "featureFlags") {
|
|
135
|
+
featureFlags = args;
|
|
136
|
+
}
|
|
137
|
+
if ((ws === null || ws === void 0 ? void 0 : ws.readyState) === WebSocket.OPEN) {
|
|
138
|
+
connectionAttempt = 0;
|
|
139
|
+
ws.send(JSON.stringify(args));
|
|
140
|
+
}
|
|
141
|
+
else if (args[0] === "getstats") {
|
|
142
|
+
if (getStatsBufferUsed < GETSTATS_BUFFER_SIZE) {
|
|
143
|
+
getStatsBufferUsed++;
|
|
144
|
+
buffer.push(args);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (args[0] === "customEvent" && args[2].type === "insightsStats") ;
|
|
148
|
+
else {
|
|
149
|
+
buffer.push(args);
|
|
150
|
+
}
|
|
151
|
+
if ((ws === null || ws === void 0 ? void 0 : ws.readyState) === WebSocket.CLOSED && connectionShouldBeOpen) {
|
|
152
|
+
setTimeout(() => {
|
|
153
|
+
if (ws.readyState === WebSocket.CLOSED && connectionShouldBeOpen) {
|
|
154
|
+
connection.connect();
|
|
155
|
+
}
|
|
156
|
+
}, 1000 * connectionAttempt);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
close: () => {
|
|
160
|
+
connectionShouldBeOpen = false;
|
|
161
|
+
ws === null || ws === void 0 ? void 0 : ws.close();
|
|
162
|
+
},
|
|
163
|
+
connect: () => {
|
|
164
|
+
connectionShouldBeOpen = true;
|
|
165
|
+
connectionAttempt += 1;
|
|
166
|
+
ws === null || ws === void 0 ? void 0 : ws.close();
|
|
167
|
+
connection.connected = true;
|
|
168
|
+
connection.attemptedConnectedAtLeastOnce = true;
|
|
169
|
+
ws = new WebSocket(wsURL + window.location.pathname, RTCSTATS_PROTOCOL_VERSION);
|
|
170
|
+
ws.onerror = (e) => {
|
|
171
|
+
connection.connected = false;
|
|
172
|
+
logger.warn(`[RTCSTATS] WebSocket error`, e);
|
|
173
|
+
};
|
|
174
|
+
ws.onclose = (e) => {
|
|
175
|
+
connection.connected = false;
|
|
176
|
+
logger.info(`[RTCSTATS] Closed ${e.code}`);
|
|
177
|
+
resetDelta();
|
|
178
|
+
};
|
|
179
|
+
ws.onopen = () => {
|
|
180
|
+
clientInfo.connectionNumber++;
|
|
181
|
+
ws.send(JSON.stringify(["clientInfo", null, clientInfo]));
|
|
182
|
+
if (organizationId) {
|
|
183
|
+
ws.send(JSON.stringify(organizationId));
|
|
184
|
+
}
|
|
185
|
+
if (clientId) {
|
|
186
|
+
ws.send(JSON.stringify(clientId));
|
|
187
|
+
}
|
|
188
|
+
if (roomSessionId) {
|
|
189
|
+
ws.send(JSON.stringify(roomSessionId));
|
|
190
|
+
}
|
|
191
|
+
if (displayName) {
|
|
192
|
+
ws.send(JSON.stringify(displayName));
|
|
193
|
+
}
|
|
194
|
+
if (userRole) {
|
|
195
|
+
ws.send(JSON.stringify(userRole));
|
|
196
|
+
}
|
|
197
|
+
if (deviceId) {
|
|
198
|
+
ws.send(JSON.stringify(deviceId));
|
|
199
|
+
}
|
|
200
|
+
if (roomMode) {
|
|
201
|
+
ws.send(JSON.stringify(roomMode));
|
|
202
|
+
}
|
|
203
|
+
if (roomProduct) {
|
|
204
|
+
ws.send(JSON.stringify(roomProduct));
|
|
205
|
+
}
|
|
206
|
+
if (sfuServer) {
|
|
207
|
+
ws.send(JSON.stringify(sfuServer));
|
|
208
|
+
}
|
|
209
|
+
if (featureFlags) {
|
|
210
|
+
ws.send(JSON.stringify(featureFlags));
|
|
211
|
+
}
|
|
212
|
+
while (buffer.length) {
|
|
213
|
+
ws.send(JSON.stringify(buffer.shift()));
|
|
214
|
+
}
|
|
215
|
+
getStatsBufferUsed = 0;
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
return connection;
|
|
220
|
+
}
|
|
221
|
+
const RTCSTATS_URL = "wss://rtcstats.srv.whereby.com";
|
|
222
|
+
const server = rtcStatsConnection(RTCSTATS_URL);
|
|
223
|
+
const stats = rtcstats(server.trace, 10000, [""]);
|
|
224
|
+
resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
|
|
225
|
+
const rtcStats = {
|
|
226
|
+
sendEvent: (type, value) => {
|
|
227
|
+
server.trace("customEvent", null, {
|
|
228
|
+
type,
|
|
229
|
+
value,
|
|
230
|
+
});
|
|
278
231
|
},
|
|
279
|
-
|
|
280
|
-
};
|
|
281
|
-
const VIDEO_SETTINGS_VP9_LOW_BANDWIDTH = {
|
|
282
|
-
codecOptions: {
|
|
283
|
-
videoGoogleStartBitrate: 500,
|
|
232
|
+
sendAudioMuted: (muted) => {
|
|
233
|
+
rtcStats.sendEvent("audio_muted", { muted });
|
|
284
234
|
},
|
|
285
|
-
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
};
|
|
290
|
-
const SCREEN_SHARE_SIMULCAST_SETTINGS = {
|
|
291
|
-
encodings: [
|
|
292
|
-
{ scaleResolutionDownBy: 2, dtx: true, maxBitrate: 500000 },
|
|
293
|
-
{ scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
|
|
294
|
-
],
|
|
295
|
-
};
|
|
296
|
-
const ADDITIONAL_SCREEN_SHARE_SETTINGS = {
|
|
297
|
-
encodings: [
|
|
298
|
-
{ scaleResolutionDownBy: 4, dtx: true, maxBitrate: 150000 },
|
|
299
|
-
{ scaleResolutionDownBy: 2, dtx: true, maxBitrate: 500000 },
|
|
300
|
-
{ scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
|
|
301
|
-
],
|
|
235
|
+
sendVideoMuted: (muted) => {
|
|
236
|
+
rtcStats.sendEvent("video_muted", { muted });
|
|
237
|
+
},
|
|
238
|
+
server,
|
|
302
239
|
};
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const isChrome = ((_a = adapterRaw.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) === "chrome";
|
|
310
|
-
const isVp9Available = isChrome && vp9On;
|
|
311
|
-
if (isScreenShare) {
|
|
312
|
-
return getScreenShareMediaSettings({
|
|
313
|
-
areTooManyAlreadyPresenting,
|
|
314
|
-
simulcastScreenshareOn,
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
return getCameraMediaSettings({
|
|
319
|
-
lowBandwidth: lowDataModeEnabled,
|
|
320
|
-
isVp9Available,
|
|
321
|
-
});
|
|
240
|
+
|
|
241
|
+
const debugOn = new URLSearchParams(window.location.search).has("debug");
|
|
242
|
+
class Logger {
|
|
243
|
+
constructor() {
|
|
244
|
+
this._isEnabled = false;
|
|
245
|
+
this._isEnabled = debugOn;
|
|
322
246
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (lowBandwidth) {
|
|
326
|
-
if (isVp9Available) {
|
|
327
|
-
return VIDEO_SETTINGS_VP9_LOW_BANDWIDTH;
|
|
328
|
-
}
|
|
329
|
-
return VIDEO_SETTINGS_SD;
|
|
247
|
+
isEnabled() {
|
|
248
|
+
return this._isEnabled;
|
|
330
249
|
}
|
|
331
|
-
|
|
332
|
-
|
|
250
|
+
enable() {
|
|
251
|
+
this._isEnabled = true;
|
|
333
252
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const getScreenShareMediaSettings = ({ areTooManyAlreadyPresenting, simulcastScreenshareOn, }) => {
|
|
337
|
-
if (areTooManyAlreadyPresenting) {
|
|
338
|
-
return ADDITIONAL_SCREEN_SHARE_SETTINGS;
|
|
253
|
+
disable() {
|
|
254
|
+
this._isEnabled = false;
|
|
339
255
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
(function (PrioritizableCodec) {
|
|
346
|
-
PrioritizableCodec["H264"] = "video/h264";
|
|
347
|
-
PrioritizableCodec["VP9"] = "video/vp9";
|
|
348
|
-
})(PrioritizableCodec || (PrioritizableCodec = {}));
|
|
349
|
-
const modifyMediaCapabilities = (routerRtpCapabilities, features) => {
|
|
350
|
-
var _a;
|
|
351
|
-
const { vp9On, h264On } = features;
|
|
352
|
-
const isChrome = ((_a = adapterRaw.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) === "chrome";
|
|
353
|
-
if (!(routerRtpCapabilities === null || routerRtpCapabilities === void 0 ? void 0 : routerRtpCapabilities.codecs)) {
|
|
354
|
-
return routerRtpCapabilities;
|
|
256
|
+
info(...params) {
|
|
257
|
+
if (!this._isEnabled) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
return console.info(...params);
|
|
355
261
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
262
|
+
warn(...params) {
|
|
263
|
+
if (!this._isEnabled) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
return console.warn(...params);
|
|
359
267
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
268
|
+
error(...params) {
|
|
269
|
+
if (!this._isEnabled) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
return console.error(...params);
|
|
363
273
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const preferredCodecEntry = codecs.find(({ mimeType }) => mimeType.toLowerCase() === preferredCodec);
|
|
368
|
-
if (!preferredCodecEntry) {
|
|
369
|
-
return codecs;
|
|
274
|
+
withDebugLogger(myDebugger) {
|
|
275
|
+
this._debugger = myDebugger;
|
|
276
|
+
return this;
|
|
370
277
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return -1;
|
|
278
|
+
debug(...params) {
|
|
279
|
+
if (!this._isEnabled || !this._debugger) {
|
|
280
|
+
return;
|
|
375
281
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (
|
|
379
|
-
|
|
282
|
+
const suppliedParams = [];
|
|
283
|
+
params.forEach((param) => {
|
|
284
|
+
if (typeof param === "function") {
|
|
285
|
+
const suppliedParam = param();
|
|
286
|
+
suppliedParams.push(suppliedParam);
|
|
380
287
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
function getPreferredOrder(availableCodecs, { av1On }) {
|
|
387
|
-
availableCodecs.unshift("video/vp9");
|
|
388
|
-
if (av1On) {
|
|
389
|
-
availableCodecs.unshift("video/av1");
|
|
288
|
+
else {
|
|
289
|
+
suppliedParams.push(param);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
this._debugger.print(...suppliedParams);
|
|
390
293
|
}
|
|
391
|
-
return availableCodecs;
|
|
392
294
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
295
|
+
|
|
296
|
+
var _a$6, _b$2;
|
|
297
|
+
const adapter$6 = (_a$6 = adapterRaw.default) !== null && _a$6 !== void 0 ? _a$6 : adapterRaw;
|
|
298
|
+
const logger$a = new Logger();
|
|
299
|
+
const browserName$2 = (_b$2 = adapter$6.browserDetails) === null || _b$2 === void 0 ? void 0 : _b$2.browser;
|
|
300
|
+
const browserVersion$1 = adapter$6.browserDetails.version;
|
|
301
|
+
function setCodecPreferenceSDP({ sdp, redOn }) {
|
|
302
|
+
var _a, _b;
|
|
303
|
+
try {
|
|
304
|
+
const sdpObject = sdpTransform__namespace.parse(sdp);
|
|
305
|
+
if (Array.isArray(sdpObject === null || sdpObject === void 0 ? void 0 : sdpObject.media)) {
|
|
306
|
+
const mediaAudio = sdpObject.media.find((m) => m.type === "audio");
|
|
307
|
+
if (Array.isArray(mediaAudio === null || mediaAudio === void 0 ? void 0 : mediaAudio.rtp)) {
|
|
308
|
+
const rtp = mediaAudio.rtp;
|
|
309
|
+
for (let i = 0; i < rtp.length; i++) {
|
|
310
|
+
if (redOn && rtp[i].codec === "red") {
|
|
311
|
+
const payloads = (_a = mediaAudio.payloads) === null || _a === void 0 ? void 0 : _a.split(" ");
|
|
312
|
+
const pt = payloads === null || payloads === void 0 ? void 0 : payloads.indexOf("" + rtp[i].payload);
|
|
313
|
+
if (pt && pt !== -1 && pt >= 0) {
|
|
314
|
+
payloads === null || payloads === void 0 ? void 0 : payloads.unshift(payloads.splice(pt, 1)[0]);
|
|
315
|
+
mediaAudio.payloads = payloads === null || payloads === void 0 ? void 0 : payloads.join(" ");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const mediaVideo = sdpObject.media.find((m) => m.type === "video");
|
|
321
|
+
if (Array.isArray(mediaVideo === null || mediaVideo === void 0 ? void 0 : mediaVideo.rtp)) {
|
|
322
|
+
const rtp = mediaVideo.rtp;
|
|
323
|
+
for (let i = 0; i < rtp.length; i++) {
|
|
324
|
+
if (rtp[i].codec === "VP9") {
|
|
325
|
+
const payloads = (_b = mediaVideo.payloads) === null || _b === void 0 ? void 0 : _b.split(" ");
|
|
326
|
+
const pt = payloads === null || payloads === void 0 ? void 0 : payloads.indexOf("" + rtp[i].payload);
|
|
327
|
+
if (pt && pt !== -1 && pt >= 0) {
|
|
328
|
+
payloads === null || payloads === void 0 ? void 0 : payloads.unshift(payloads.splice(pt, 1)[0]);
|
|
329
|
+
mediaVideo.payloads = payloads === null || payloads === void 0 ? void 0 : payloads.join(" ");
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const newSdp = sdpTransform__namespace.write(sdpObject);
|
|
336
|
+
return newSdp;
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
logger$a.error("setCodecPreferenceSDP error:", error);
|
|
340
|
+
return sdp;
|
|
341
|
+
}
|
|
405
342
|
}
|
|
406
|
-
function
|
|
407
|
-
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
343
|
+
function cleanSdp(sdp) {
|
|
344
|
+
try {
|
|
345
|
+
const sdpObject = sdpTransform__namespace.parse(sdp);
|
|
346
|
+
sdpObject.media.forEach((mediaObject) => {
|
|
347
|
+
const usedPayloads = {};
|
|
348
|
+
if (mediaObject.payloads)
|
|
349
|
+
mediaObject.payloads = ("" + mediaObject.payloads)
|
|
350
|
+
.split(" ")
|
|
351
|
+
.filter((p) => !usedPayloads[p] && (usedPayloads[p] = true))
|
|
352
|
+
.join(" ");
|
|
353
|
+
const usedRtps = {};
|
|
354
|
+
mediaObject.rtp = mediaObject.rtp.filter((p) => !p.payload || (usedPayloads[p.payload] && !usedRtps[p.payload] && (usedRtps[p.payload] = true)));
|
|
355
|
+
const usedFmtps = {};
|
|
356
|
+
if (mediaObject.fmtp)
|
|
357
|
+
mediaObject.fmtp = mediaObject.fmtp.filter((p) => !p.payload ||
|
|
358
|
+
(usedPayloads[p.payload] && !usedFmtps[p.payload] && (usedFmtps[p.payload] = true)));
|
|
359
|
+
const usedRtcpFb = {};
|
|
360
|
+
if (mediaObject.rtcpFb)
|
|
361
|
+
mediaObject.rtcpFb = mediaObject.rtcpFb.filter((p) => !p.payload ||
|
|
362
|
+
(usedPayloads[p.payload] &&
|
|
363
|
+
!usedRtcpFb[p.payload + p.type + p.subtype] &&
|
|
364
|
+
(usedRtcpFb[p.payload + p.type + p.subtype] = true)));
|
|
417
365
|
});
|
|
418
|
-
return
|
|
419
|
-
}
|
|
366
|
+
return sdpTransform__namespace.write(sdpObject);
|
|
367
|
+
}
|
|
368
|
+
catch (_) { }
|
|
369
|
+
return sdp;
|
|
420
370
|
}
|
|
421
|
-
function
|
|
422
|
-
return
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
371
|
+
function deprioritizeH264(sdp) {
|
|
372
|
+
return SDPUtils.splitSections(sdp)
|
|
373
|
+
.map((section) => {
|
|
374
|
+
if (SDPUtils.getKind(section) !== "video")
|
|
375
|
+
return section;
|
|
376
|
+
const h264payloadTypes = SDPUtils.matchPrefix(section, "a=rtpmap:")
|
|
377
|
+
.map((line) => SDPUtils.parseRtpMap(line))
|
|
378
|
+
.filter((codec) => /h264/i.test(codec.name))
|
|
379
|
+
.map((codec) => "" + codec.payloadType);
|
|
380
|
+
if (!h264payloadTypes.length)
|
|
381
|
+
return section;
|
|
382
|
+
const mline = SDPUtils.matchPrefix(section, "m=video")[0];
|
|
383
|
+
const mlinePayloadsSectionExec = /(\s\d+)+$/i.exec(mline);
|
|
384
|
+
const mlinePayloadsSection = mlinePayloadsSectionExec ? mlinePayloadsSectionExec[0] : "";
|
|
385
|
+
const mlinePayloadsNonH264 = mlinePayloadsSection
|
|
386
|
+
.split(" ")
|
|
387
|
+
.filter((payloadType) => payloadType && !h264payloadTypes.includes(payloadType));
|
|
388
|
+
const reorderedPayloads = [...mlinePayloadsNonH264, ...h264payloadTypes].join(" ");
|
|
389
|
+
const newmline = mline.replace(mlinePayloadsSection, " " + reorderedPayloads);
|
|
390
|
+
return section.replace(mline, newmline);
|
|
391
|
+
})
|
|
392
|
+
.join("");
|
|
393
|
+
}
|
|
394
|
+
function filterMidExtension(sdp) {
|
|
395
|
+
if (browserName$2 !== "firefox" || (browserVersion$1 && browserVersion$1 >= 63) || browserVersion$1 === 60) {
|
|
396
|
+
return sdp;
|
|
397
|
+
}
|
|
398
|
+
return (SDPUtils.splitLines(sdp.trim())
|
|
399
|
+
.filter((line) => {
|
|
400
|
+
if (!line.startsWith("a=extmap:")) {
|
|
401
|
+
return true;
|
|
426
402
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
403
|
+
const extmap = SDPUtils.parseExtmap(line);
|
|
404
|
+
return extmap.uri !== "urn:ietf:params:rtp-hdrext:sdes:mid";
|
|
405
|
+
})
|
|
406
|
+
.join("\r\n") + "\r\n");
|
|
407
|
+
}
|
|
408
|
+
function filterMsidSemantic(sdp) {
|
|
409
|
+
if (browserName$2 !== "firefox" && browserVersion$1 < 68) {
|
|
410
|
+
return sdp;
|
|
411
|
+
}
|
|
412
|
+
return (SDPUtils.splitLines(sdp.trim())
|
|
413
|
+
.map((line) => (line.startsWith("a=msid-semantic:") ? "a=msid-semantic: WMS *" : line))
|
|
414
|
+
.join("\r\n") + "\r\n");
|
|
415
|
+
}
|
|
416
|
+
function addExtMap(sdp, extmapUri, modifyAudio = false, modifyVideo = false) {
|
|
417
|
+
var _a, _b;
|
|
418
|
+
try {
|
|
419
|
+
const sdpObj = sdpTransform__namespace.parse(sdp);
|
|
420
|
+
if (((_a = sdpObj === null || sdpObj === void 0 ? void 0 : sdpObj.ext) === null || _a === void 0 ? void 0 : _a.length) && sdpObj.ext.length > 0) {
|
|
421
|
+
return sdp;
|
|
430
422
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
423
|
+
if ((sdpObj === null || sdpObj === void 0 ? void 0 : sdpObj.media.length) < 1)
|
|
424
|
+
return sdp;
|
|
425
|
+
const allHeaderExtensions = sdpObj === null || sdpObj === void 0 ? void 0 : sdpObj.media.flatMap((section) => section.ext || []);
|
|
426
|
+
const extmapId = ((_b = allHeaderExtensions.find((ext) => ext.uri === extmapUri)) === null || _b === void 0 ? void 0 : _b.value) ||
|
|
427
|
+
[...new Set([0, 15, ...allHeaderExtensions.map((ext) => ext.value)])]
|
|
428
|
+
.sort((a, b) => a - b)
|
|
429
|
+
.find((n, i, arr) => n + 1 !== arr[i + 1]) + 1;
|
|
430
|
+
sdpObj.media.forEach((mediaSection) => {
|
|
431
|
+
var _a;
|
|
432
|
+
if ((modifyAudio && mediaSection.type === "audio") || (modifyVideo && mediaSection.type === "video")) {
|
|
433
|
+
if (!((_a = mediaSection.ext) === null || _a === void 0 ? void 0 : _a.find((e) => e.uri === extmapUri))) {
|
|
434
|
+
if (Array.isArray(mediaSection.ext)) {
|
|
435
|
+
mediaSection["ext"].push({ value: extmapId, uri: extmapUri });
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
mediaSection["ext"] = [{ value: extmapId, uri: extmapUri }];
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
436
442
|
});
|
|
437
|
-
return
|
|
438
|
-
}
|
|
443
|
+
return sdpTransform__namespace.write(sdpObj);
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
console.error("Error during addAbsCaptureTimeExtMap: ", error);
|
|
447
|
+
}
|
|
448
|
+
return sdp;
|
|
439
449
|
}
|
|
440
|
-
function
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
sortedCodecs = yield sortCodecsByPowerEfficiency(codecs);
|
|
444
|
-
return sortedCodecs;
|
|
445
|
-
});
|
|
450
|
+
function addAbsCaptureTimeExtMap(sdp) {
|
|
451
|
+
const absCaptureTimeUri = "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time";
|
|
452
|
+
return addExtMap(sdp, absCaptureTimeUri, true, true);
|
|
446
453
|
}
|
|
447
454
|
|
|
448
|
-
function
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
cpMetrics.bitrateOut = (8000 * bytesSentDiff) / timeDiff;
|
|
453
|
-
cpMetrics.state = currentCptats.state;
|
|
454
|
-
cpMetrics.roundTripTime = currentCptats.currentRoundTripTime;
|
|
455
|
-
cpMetrics.requestsSent = currentCptats.requestsSent;
|
|
456
|
-
cpMetrics.responsesReceived = currentCptats.responsesReceived;
|
|
457
|
-
cpMetrics.requestsReceived = currentCptats.requestsReceived;
|
|
458
|
-
cpMetrics.responsesSent = currentCptats.responsesSent;
|
|
459
|
-
cpMetrics.availableOutgoingBitrate = currentCptats.availableOutgoingBitrate;
|
|
460
|
-
cpMetrics.availableIncomingBitrate = currentCptats.availableIncomingBitrate;
|
|
461
|
-
const remote = report.get(currentCptats.remoteCandidateId);
|
|
462
|
-
const local = report.get(currentCptats.localCandidateId);
|
|
463
|
-
cpMetrics.usingTurn = false;
|
|
464
|
-
if (local) {
|
|
465
|
-
if (/relay/i.test(local.candidateType || "")) {
|
|
466
|
-
cpMetrics.usingTurn = true;
|
|
467
|
-
cpMetrics.turnProtocol = local.relayProtocol;
|
|
468
|
-
}
|
|
455
|
+
function setVideoBandwidthUsingSetParameters(pc, bandwidth, logger = console) {
|
|
456
|
+
const sender = pc.getSenders().find((s) => s.track && s.track.kind === "video");
|
|
457
|
+
if (!sender) {
|
|
458
|
+
return Promise.resolve();
|
|
469
459
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
460
|
+
const parameters = sender.getParameters();
|
|
461
|
+
if (parameters.encodings && parameters.encodings.length === 0) {
|
|
462
|
+
return Promise.resolve();
|
|
463
|
+
}
|
|
464
|
+
if (!parameters.encodings) {
|
|
465
|
+
parameters.encodings = [{}];
|
|
473
466
|
}
|
|
467
|
+
if (bandwidth === 0) {
|
|
468
|
+
delete parameters.encodings[0].maxBitrate;
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
parameters.encodings[0].maxBitrate = bandwidth * 1000;
|
|
472
|
+
}
|
|
473
|
+
return sender.setParameters(parameters).catch((err) => {
|
|
474
|
+
logger.error("setParameters err: ", err);
|
|
475
|
+
});
|
|
474
476
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
[
|
|
485
|
-
codecStats.mimeType,
|
|
486
|
-
codecStats.channels && `${codecStats.channels}ch`,
|
|
487
|
-
codecStats.clockRate,
|
|
488
|
-
codecStats.payloadType,
|
|
489
|
-
currentSsrcStats.encoderImplementation || currentSsrcStats.decoderImplementation,
|
|
490
|
-
codecStats.sdpFmtpLine,
|
|
491
|
-
]
|
|
492
|
-
.filter(Boolean)
|
|
493
|
-
.join(" ");
|
|
494
|
-
ssrcMetrics.mid = currentSsrcStats.mid;
|
|
495
|
-
ssrcMetrics.rid = currentSsrcStats.rid;
|
|
477
|
+
|
|
478
|
+
const _trackAnnotations = new WeakMap();
|
|
479
|
+
function trackAnnotations(o) {
|
|
480
|
+
let props = _trackAnnotations.get(o);
|
|
481
|
+
if (!props) {
|
|
482
|
+
props = {};
|
|
483
|
+
_trackAnnotations.set(o, props);
|
|
484
|
+
}
|
|
485
|
+
return props;
|
|
496
486
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
487
|
+
|
|
488
|
+
var _a$5;
|
|
489
|
+
const adapter$5 = (_a$5 = adapterRaw.default) !== null && _a$5 !== void 0 ? _a$5 : adapterRaw;
|
|
490
|
+
const logger$9 = new Logger();
|
|
491
|
+
class Session {
|
|
492
|
+
constructor({ clientId, bandwidth, peerConnectionConfig, deprioritizeH264Encoding, incrementAnalyticMetric, }) {
|
|
493
|
+
this.relayCandidateSeen = false;
|
|
494
|
+
this.serverReflexiveCandidateSeen = false;
|
|
495
|
+
this.publicHostCandidateSeen = false;
|
|
496
|
+
this.ipv6HostCandidateSeen = false;
|
|
497
|
+
this.ipv6HostCandidateTeredoSeen = false;
|
|
498
|
+
this.ipv6HostCandidate6to4Seen = false;
|
|
499
|
+
this.mdnsHostCandidateSeen = false;
|
|
500
|
+
this.pendingReplaceTrackActions = [];
|
|
501
|
+
this.peerConnectionConfig = peerConnectionConfig;
|
|
502
|
+
this.clientId = clientId;
|
|
503
|
+
this.pc = new RTCPeerConnection(this.peerConnectionConfig);
|
|
504
|
+
this.signalingState = this.pc.signalingState;
|
|
505
|
+
this.pc.addEventListener("signalingstatechange", () => {
|
|
506
|
+
if (this.signalingState === this.pc.signalingState) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
this.signalingState = this.pc.signalingState;
|
|
510
|
+
if (this.pc.signalingState === "stable") {
|
|
511
|
+
this.isOperationPending = false;
|
|
512
|
+
const action = this.pending.shift();
|
|
513
|
+
if (action) {
|
|
514
|
+
action.apply();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
this.wasEverConnected = false;
|
|
519
|
+
this.connectionStatus = null;
|
|
520
|
+
this.bandwidth = bandwidth || 0;
|
|
521
|
+
this.pending = [];
|
|
522
|
+
this.isOperationPending = false;
|
|
523
|
+
this.streamIds = [];
|
|
524
|
+
this.streams = [];
|
|
525
|
+
this.earlyIceCandidates = [];
|
|
526
|
+
this.afterConnected = new Promise((resolve) => {
|
|
527
|
+
this.registerConnected = resolve;
|
|
528
|
+
});
|
|
529
|
+
this._deprioritizeH264Encoding = deprioritizeH264Encoding;
|
|
530
|
+
this._incrementAnalyticMetric = incrementAnalyticMetric;
|
|
531
|
+
}
|
|
532
|
+
addStream(stream) {
|
|
533
|
+
this.streamIds.push(stream.id);
|
|
534
|
+
this.streams.push(stream);
|
|
535
|
+
stream.getAudioTracks().forEach((track) => {
|
|
536
|
+
this.pc.addTrack(track, stream);
|
|
537
|
+
});
|
|
538
|
+
stream.getVideoTracks().forEach((track) => {
|
|
539
|
+
this.pc.addTrack(track, stream);
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
addTrack(track, stream) {
|
|
543
|
+
if (!stream) {
|
|
544
|
+
stream = this.streams[0];
|
|
515
545
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
546
|
+
stream === null || stream === void 0 ? void 0 : stream.addTrack(track);
|
|
547
|
+
this.pc.addTrack(track, stream);
|
|
548
|
+
}
|
|
549
|
+
removeTrack(track) {
|
|
550
|
+
const stream = this.streams[0];
|
|
551
|
+
stream.removeTrack(track);
|
|
552
|
+
const sender = this.pc.getSenders().find((sender) => sender.track === track);
|
|
553
|
+
if (sender) {
|
|
554
|
+
this.pc.removeTrack(sender);
|
|
522
555
|
}
|
|
523
|
-
const jitterBufferDelayDiff = (currentSsrcStats.jitterBufferDelay || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.jitterBufferDelay) || 0);
|
|
524
|
-
const jitterBufferEmittedDiff = (currentSsrcStats.jitterBufferEmittedCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.jitterBufferEmittedCount) || 0);
|
|
525
|
-
ssrcMetrics.jitter = jitterBufferEmittedDiff ? jitterBufferDelayDiff / jitterBufferEmittedDiff : 0;
|
|
526
556
|
}
|
|
527
|
-
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const byteCountDiff = currentSsrcStats.bytesSent - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.bytesSent) || 0);
|
|
532
|
-
ssrcMetrics.byteCount = (ssrcMetrics.byteCount || 0) + byteCountDiff;
|
|
533
|
-
let headerByteCountDiff = 0;
|
|
534
|
-
if (currentSsrcStats.headerBytesSent) {
|
|
535
|
-
headerByteCountDiff = currentSsrcStats.headerBytesSent - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.headerBytesSent) || 0);
|
|
536
|
-
ssrcMetrics.headerByteCount = (ssrcMetrics.headerByteCount || 0) + headerByteCountDiff;
|
|
557
|
+
removeStream(stream) {
|
|
558
|
+
const streamIdIndex = this.streamIds.indexOf(stream.id);
|
|
559
|
+
if (streamIdIndex !== -1) {
|
|
560
|
+
this.streamIds.splice(streamIdIndex, 1);
|
|
537
561
|
}
|
|
538
|
-
const
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const sendDelayDiff = currentSsrcStats.totalPacketSendDelay - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalPacketSendDelay) || 0);
|
|
542
|
-
ssrcMetrics.sendDelay = sendDelayDiff / packetCountDiff;
|
|
543
|
-
const retransDiff = currentSsrcStats.retransmittedPacketsSent - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.retransmittedPacketsSent) || 0);
|
|
544
|
-
ssrcMetrics.retransRatio = (1000 * (retransDiff / packetCountDiff)) / timeDiff;
|
|
545
|
-
if (currentSsrcStats.remoteId) {
|
|
546
|
-
const remoteReport = report.get(currentSsrcStats.remoteId);
|
|
547
|
-
if (remoteReport) {
|
|
548
|
-
ssrcMetrics.roundTripTime = remoteReport.roundTripTime || 0;
|
|
549
|
-
ssrcMetrics.jitter = remoteReport.jitter || 0;
|
|
550
|
-
ssrcMetrics.fractionLost = remoteReport.fractionLost || 0;
|
|
551
|
-
}
|
|
562
|
+
const streamIndex = this.streams.indexOf(stream);
|
|
563
|
+
if (streamIndex !== -1) {
|
|
564
|
+
this.streams.splice(streamIndex, 1);
|
|
552
565
|
}
|
|
566
|
+
stream.getTracks().forEach((track) => {
|
|
567
|
+
const sender = this.pc.getSenders().find((sender) => sender.track === track);
|
|
568
|
+
if (sender) {
|
|
569
|
+
this.pc.removeTrack(sender);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
553
572
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const samples = currentSsrcStats.totalSamplesReceived || 0;
|
|
563
|
-
const concealmentEvents = currentSsrcStats.concealmentEvents || 0;
|
|
564
|
-
const samplesDiff = samples - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalSamplesReceived) || 0);
|
|
565
|
-
const concealedSamples = currentSsrcStats.concealedSamples || 0;
|
|
566
|
-
const concealedDiff = concealedSamples - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.concealedSamples) || 0);
|
|
567
|
-
const silentConcealedDiff = (currentSsrcStats.silentConcealedSamples || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.silentConcealedSamples) || 0);
|
|
568
|
-
const insDiff = (currentSsrcStats.insertedSamplesForDeceleration || 0) -
|
|
569
|
-
((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.insertedSamplesForDeceleration) || 0);
|
|
570
|
-
const remDiff = (currentSsrcStats.removedSamplesForAcceleration || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.removedSamplesForAcceleration) || 0);
|
|
571
|
-
ssrcMetrics.audioSamples = samples;
|
|
572
|
-
ssrcMetrics.audioConcealmentEvents = concealmentEvents;
|
|
573
|
-
ssrcMetrics.audioConcealedSamples = concealedSamples;
|
|
574
|
-
ssrcMetrics.audioConcealment = concealedDiff ? concealedDiff / samplesDiff : 0;
|
|
575
|
-
ssrcMetrics.audioSilentConcealment = silentConcealedDiff ? silentConcealedDiff / samplesDiff : 0;
|
|
576
|
-
ssrcMetrics.audioAcceleration = remDiff ? remDiff / samplesDiff : 0;
|
|
577
|
-
ssrcMetrics.audioDeceleration = insDiff ? insDiff / samplesDiff : 0;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, timeDiff, report) {
|
|
581
|
-
ssrcMetrics.width = currentSsrcStats.frameWidth;
|
|
582
|
-
ssrcMetrics.height = currentSsrcStats.frameHeight;
|
|
583
|
-
ssrcMetrics.qualityLimitationReason = currentSsrcStats.qualityLimitationReason || "";
|
|
584
|
-
const pliCountDiff = currentSsrcStats.pliCount - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.pliCount) || 0);
|
|
585
|
-
ssrcMetrics.pliCount = (ssrcMetrics.pliCount || 0) + pliCountDiff;
|
|
586
|
-
ssrcMetrics.pliRate = (1000 * pliCountDiff) / timeDiff;
|
|
587
|
-
const firCountDiff = currentSsrcStats.firCount - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.firCount) || 0);
|
|
588
|
-
ssrcMetrics.firCount = (ssrcMetrics.firCount || 0) + firCountDiff;
|
|
589
|
-
ssrcMetrics.firRate = (1000 * firCountDiff) / timeDiff;
|
|
590
|
-
if (ssrcMetrics.direction === "in") {
|
|
591
|
-
const kfCountDiff = currentSsrcStats.keyFramesDecoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesDecoded) || 0);
|
|
592
|
-
ssrcMetrics.kfCount = (ssrcMetrics.kfCount || 0) + kfCountDiff;
|
|
593
|
-
ssrcMetrics.kfRate = (1000 * kfCountDiff) / timeDiff;
|
|
594
|
-
const frameCountDiff = currentSsrcStats.framesDecoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.framesDecoded) || 0);
|
|
595
|
-
ssrcMetrics.frameCount = (ssrcMetrics.frameCount || 0) + frameCountDiff;
|
|
596
|
-
const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
|
|
597
|
-
ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
|
|
598
|
-
ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
|
|
573
|
+
_setRemoteDescription(desc) {
|
|
574
|
+
if (this._deprioritizeH264Encoding)
|
|
575
|
+
desc.sdp = deprioritizeH264(desc.sdp);
|
|
576
|
+
this.srdComplete = this.pc.setRemoteDescription(desc);
|
|
577
|
+
return this.srdComplete.then(() => {
|
|
578
|
+
this.earlyIceCandidates.forEach((candidate) => this.pc.addIceCandidate(candidate));
|
|
579
|
+
this.earlyIceCandidates = [];
|
|
580
|
+
});
|
|
599
581
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
ssrcMetrics.frameCount = (ssrcMetrics.frameCount || 0) + frameCountDiff;
|
|
606
|
-
const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
|
|
607
|
-
ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
|
|
608
|
-
ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
|
|
609
|
-
const encodeTimeDiff = currentSsrcStats.totalEncodeTime - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalEncodeTime) || 0);
|
|
610
|
-
ssrcMetrics.encodeTime = encodeTimeDiff / frameCountDiff;
|
|
611
|
-
if (currentSsrcStats.mediaSourceId) {
|
|
612
|
-
const mediaSourceReport = report.get(currentSsrcStats.mediaSourceId);
|
|
613
|
-
if (mediaSourceReport) {
|
|
614
|
-
ssrcMetrics.sourceWidth = mediaSourceReport.width || 0;
|
|
615
|
-
ssrcMetrics.sourceHeight = mediaSourceReport.height || 0;
|
|
616
|
-
ssrcMetrics.sourceFps = mediaSourceReport.framesPerSecond || 0;
|
|
617
|
-
}
|
|
582
|
+
handleOffer(offer) {
|
|
583
|
+
if (!this.canModifyPeerConnection()) {
|
|
584
|
+
return new Promise((resolve) => {
|
|
585
|
+
this.pending.push(() => this.handleOffer(offer).then(resolve));
|
|
586
|
+
});
|
|
618
587
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
function rtcStatsConnection(wsURL, logger = console) {
|
|
633
|
-
const buffer = [];
|
|
634
|
-
let ws;
|
|
635
|
-
let organizationId;
|
|
636
|
-
let clientId;
|
|
637
|
-
let displayName;
|
|
638
|
-
let userRole;
|
|
639
|
-
let roomSessionId;
|
|
640
|
-
let connectionShouldBeOpen;
|
|
641
|
-
let connectionAttempt = 0;
|
|
642
|
-
let hasPassedOnRoomSessionId = false;
|
|
643
|
-
let getStatsBufferUsed = 0;
|
|
644
|
-
let deviceId;
|
|
645
|
-
let roomProduct;
|
|
646
|
-
let roomMode;
|
|
647
|
-
let sfuServer;
|
|
648
|
-
let featureFlags;
|
|
649
|
-
const connection = {
|
|
650
|
-
connected: false,
|
|
651
|
-
attemptedConnectedAtLeastOnce: false,
|
|
652
|
-
trace: (...args) => {
|
|
653
|
-
args.push(Date.now());
|
|
654
|
-
if (args[0] === "customEvent" && args[2].type === "roomSessionId") {
|
|
655
|
-
const oldRoomSessionIdValue = roomSessionId && roomSessionId[2].value.roomSessionId;
|
|
656
|
-
const newRoomSessionIdValue = args[2].value.roomSessionId;
|
|
657
|
-
roomSessionId = args;
|
|
658
|
-
if (hasPassedOnRoomSessionId &&
|
|
659
|
-
newRoomSessionIdValue &&
|
|
660
|
-
newRoomSessionIdValue !== oldRoomSessionIdValue) {
|
|
661
|
-
ws === null || ws === void 0 ? void 0 : ws.close();
|
|
662
|
-
}
|
|
663
|
-
if (newRoomSessionIdValue)
|
|
664
|
-
hasPassedOnRoomSessionId = true;
|
|
665
|
-
}
|
|
666
|
-
else if (args[0] === "customEvent" && args[2].type === "clientId") {
|
|
667
|
-
clientId = args;
|
|
668
|
-
}
|
|
669
|
-
else if (args[0] === "customEvent" && args[2].type === "organizationId") {
|
|
670
|
-
organizationId = args;
|
|
588
|
+
this.isOperationPending = true;
|
|
589
|
+
let sdp = offer.sdp;
|
|
590
|
+
sdp = filterMidExtension(sdp);
|
|
591
|
+
sdp = filterMsidSemantic(sdp);
|
|
592
|
+
const desc = { type: offer.type, sdp };
|
|
593
|
+
let answerToSignal;
|
|
594
|
+
return this._setRemoteDescription(desc)
|
|
595
|
+
.then(() => {
|
|
596
|
+
return this.pc.createAnswer();
|
|
597
|
+
})
|
|
598
|
+
.then((answer) => {
|
|
599
|
+
if (!answer.sdp) {
|
|
600
|
+
throw new Error("SDP undefined while creating answer");
|
|
671
601
|
}
|
|
672
|
-
else
|
|
673
|
-
|
|
602
|
+
else {
|
|
603
|
+
answerToSignal = {
|
|
604
|
+
sdp: answer.sdp,
|
|
605
|
+
sdpU: answer.sdp,
|
|
606
|
+
type: answer.type,
|
|
607
|
+
};
|
|
608
|
+
return this.pc.setLocalDescription(answer);
|
|
674
609
|
}
|
|
675
|
-
|
|
676
|
-
|
|
610
|
+
})
|
|
611
|
+
.then(() => {
|
|
612
|
+
return setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
613
|
+
})
|
|
614
|
+
.then(() => {
|
|
615
|
+
return answerToSignal;
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
handleAnswer(message) {
|
|
619
|
+
const sdp = filterMsidSemantic(message.sdp);
|
|
620
|
+
const desc = { type: message.type, sdp };
|
|
621
|
+
return this._setRemoteDescription(desc).then(() => {
|
|
622
|
+
return setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
addIceCandidate(candidate) {
|
|
626
|
+
if (!this.srdComplete) {
|
|
627
|
+
this.earlyIceCandidates.push(candidate);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
this.srdComplete.then(() => {
|
|
631
|
+
var _a;
|
|
632
|
+
if (this.pc.signalingState === "closed") {
|
|
633
|
+
return;
|
|
677
634
|
}
|
|
678
|
-
|
|
679
|
-
|
|
635
|
+
if (((_a = adapter$5.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) === "safari" && candidate && candidate.candidate === "") {
|
|
636
|
+
return;
|
|
680
637
|
}
|
|
681
|
-
|
|
682
|
-
|
|
638
|
+
this.pc.addIceCandidate(candidate).catch((e) => {
|
|
639
|
+
logger$9.warn("Failed to add ICE candidate ('%s'): %s", candidate ? candidate.candidate : null, e);
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
canModifyPeerConnection() {
|
|
644
|
+
return this.pc.signalingState === "stable" && !this.isOperationPending;
|
|
645
|
+
}
|
|
646
|
+
close() {
|
|
647
|
+
const pc = this.pc;
|
|
648
|
+
if (!pc) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
pc.oniceconnectionstatechange = null;
|
|
652
|
+
pc.onicecandidate = null;
|
|
653
|
+
pc.ontrack = null;
|
|
654
|
+
try {
|
|
655
|
+
pc.close();
|
|
656
|
+
}
|
|
657
|
+
catch (e) {
|
|
658
|
+
logger$9.warn("failures during close of session", e);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
hasConnectedPeerConnection() {
|
|
662
|
+
return this.pc.connectionState === "connected";
|
|
663
|
+
}
|
|
664
|
+
replaceTrack(oldTrack, newTrack) {
|
|
665
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
666
|
+
logger$9.info("replacetrack() [oldTrackId: %s, newTrackId: %s]", oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.id, newTrack.id);
|
|
667
|
+
if (newTrack.readyState === "ended") {
|
|
668
|
+
throw new Error(`refusing to use ended track with id: ${newTrack.id}, kind: ${newTrack.kind}`);
|
|
683
669
|
}
|
|
684
|
-
|
|
685
|
-
|
|
670
|
+
const pc = this.pc;
|
|
671
|
+
if (oldTrack) {
|
|
672
|
+
const sender = pc.getSenders().find((s) => { var _a; return ((_a = s.track) === null || _a === void 0 ? void 0 : _a.id) === oldTrack.id; });
|
|
673
|
+
if (sender) {
|
|
674
|
+
return yield sender.replaceTrack(newTrack);
|
|
675
|
+
}
|
|
686
676
|
}
|
|
687
|
-
|
|
688
|
-
|
|
677
|
+
const sender = pc.getSenders().find((s) => {
|
|
678
|
+
const track = s.track;
|
|
679
|
+
return (track === null || track === void 0 ? void 0 : track.kind) === newTrack.kind && !trackAnnotations(track).fromGetDisplayMedia;
|
|
680
|
+
});
|
|
681
|
+
if (sender) {
|
|
682
|
+
return yield sender.replaceTrack(newTrack);
|
|
689
683
|
}
|
|
690
|
-
|
|
691
|
-
|
|
684
|
+
let stream = this.streams.find((s) => s.getTracks().find((t) => t.id === newTrack.id));
|
|
685
|
+
if (!stream) {
|
|
686
|
+
rtcStats.sendEvent("P2PReplaceTrackNewTrackNotInStream", {
|
|
687
|
+
oldTrackId: oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.id,
|
|
688
|
+
oldTrackKind: oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.kind,
|
|
689
|
+
oldTrackIsEffect: oldTrack && trackAnnotations(oldTrack).isEffectTrack,
|
|
690
|
+
newTrackId: newTrack.id,
|
|
691
|
+
newTrackKind: newTrack.kind,
|
|
692
|
+
newTrackIsEffect: trackAnnotations(newTrack).isEffectTrack,
|
|
693
|
+
});
|
|
694
|
+
this._incrementAnalyticMetric("P2PReplaceTrackNewTrackNotInStream");
|
|
692
695
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
+
stream = this.streams[0];
|
|
697
|
+
if (!stream) {
|
|
698
|
+
rtcStats.sendEvent("P2PReplaceTrackNoStream", {});
|
|
699
|
+
this._incrementAnalyticMetric("P2PReplaceTrackNoStream");
|
|
700
|
+
throw new Error("replaceTrack: No stream?");
|
|
696
701
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
702
|
+
pc.addTrack(newTrack, stream);
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
changeBandwidth(bandwidth) {
|
|
706
|
+
if (bandwidth === this.bandwidth) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (!this.canModifyPeerConnection()) {
|
|
710
|
+
this.pending.push(() => this.changeBandwidth(bandwidth));
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
this.bandwidth = bandwidth;
|
|
714
|
+
if (!this.pc.localDescription) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
718
|
+
}
|
|
719
|
+
setAudioOnly(enable, excludedTrackIds = []) {
|
|
720
|
+
var _a;
|
|
721
|
+
(_a = this.pc) === null || _a === void 0 ? void 0 : _a.getTransceivers().filter((videoTransceiver) => {
|
|
722
|
+
var _a, _b, _c, _d, _e, _f;
|
|
723
|
+
return (videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.direction) !== "recvonly" &&
|
|
724
|
+
((_b = (_a = videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.receiver) === null || _a === void 0 ? void 0 : _a.track) === null || _b === void 0 ? void 0 : _b.kind) === "video" &&
|
|
725
|
+
!excludedTrackIds.includes((_d = (_c = videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.receiver) === null || _c === void 0 ? void 0 : _c.track) === null || _d === void 0 ? void 0 : _d.id) &&
|
|
726
|
+
!excludedTrackIds.includes((_f = (_e = videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.sender) === null || _e === void 0 ? void 0 : _e.track) === null || _f === void 0 ? void 0 : _f.id);
|
|
727
|
+
}).forEach((videoTransceiver) => {
|
|
728
|
+
videoTransceiver.direction = enable ? "sendonly" : "sendrecv";
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const MEDIA_JITTER_BUFFER_TARGET = 400;
|
|
734
|
+
|
|
735
|
+
var _a$4;
|
|
736
|
+
const adapter$4 = (_a$4 = adapterRaw.default) !== null && _a$4 !== void 0 ? _a$4 : adapterRaw;
|
|
737
|
+
function detectMicrophoneNotWorking(pc) {
|
|
738
|
+
var _a, _b;
|
|
739
|
+
if (((_a = adapter$4.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) !== "chrome" ||
|
|
740
|
+
((_b = adapter$4.browserDetails) === null || _b === void 0 ? void 0 : _b.browser) < 58 ||
|
|
741
|
+
pc.signalingState === "closed") {
|
|
742
|
+
return Promise.resolve("");
|
|
743
|
+
}
|
|
744
|
+
const sendingAudio = pc.getSenders().some((sender) => { var _a; return ((_a = sender.track) === null || _a === void 0 ? void 0 : _a.kind) === "audio"; });
|
|
745
|
+
const receivingAudio = pc.getReceivers().some((receiver) => { var _a; return ((_a = receiver.track) === null || _a === void 0 ? void 0 : _a.kind) === "audio"; });
|
|
746
|
+
return pc.getStats(null).then((result) => {
|
|
747
|
+
let microphoneFailed = "";
|
|
748
|
+
result.forEach((report) => {
|
|
749
|
+
if (report.type === "outbound-rtp" &&
|
|
750
|
+
(report.kind === "audio" || report.mediaType === "audio") &&
|
|
751
|
+
sendingAudio) {
|
|
752
|
+
if (report.bytesSent === 0) {
|
|
753
|
+
microphoneFailed = "outbound";
|
|
701
754
|
}
|
|
702
755
|
}
|
|
703
|
-
else if (
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
setTimeout(() => {
|
|
709
|
-
if (ws.readyState === WebSocket.CLOSED && connectionShouldBeOpen) {
|
|
710
|
-
connection.connect();
|
|
711
|
-
}
|
|
712
|
-
}, 1000 * connectionAttempt);
|
|
713
|
-
}
|
|
714
|
-
},
|
|
715
|
-
close: () => {
|
|
716
|
-
connectionShouldBeOpen = false;
|
|
717
|
-
ws === null || ws === void 0 ? void 0 : ws.close();
|
|
718
|
-
},
|
|
719
|
-
connect: () => {
|
|
720
|
-
connectionShouldBeOpen = true;
|
|
721
|
-
connectionAttempt += 1;
|
|
722
|
-
ws === null || ws === void 0 ? void 0 : ws.close();
|
|
723
|
-
connection.connected = true;
|
|
724
|
-
connection.attemptedConnectedAtLeastOnce = true;
|
|
725
|
-
ws = new WebSocket(wsURL + window.location.pathname, RTCSTATS_PROTOCOL_VERSION);
|
|
726
|
-
ws.onerror = (e) => {
|
|
727
|
-
connection.connected = false;
|
|
728
|
-
logger.warn(`[RTCSTATS] WebSocket error`, e);
|
|
729
|
-
};
|
|
730
|
-
ws.onclose = (e) => {
|
|
731
|
-
connection.connected = false;
|
|
732
|
-
logger.info(`[RTCSTATS] Closed ${e.code}`);
|
|
733
|
-
resetDelta();
|
|
734
|
-
};
|
|
735
|
-
ws.onopen = () => {
|
|
736
|
-
clientInfo.connectionNumber++;
|
|
737
|
-
ws.send(JSON.stringify(["clientInfo", null, clientInfo]));
|
|
738
|
-
if (organizationId) {
|
|
739
|
-
ws.send(JSON.stringify(organizationId));
|
|
740
|
-
}
|
|
741
|
-
if (clientId) {
|
|
742
|
-
ws.send(JSON.stringify(clientId));
|
|
743
|
-
}
|
|
744
|
-
if (roomSessionId) {
|
|
745
|
-
ws.send(JSON.stringify(roomSessionId));
|
|
746
|
-
}
|
|
747
|
-
if (displayName) {
|
|
748
|
-
ws.send(JSON.stringify(displayName));
|
|
749
|
-
}
|
|
750
|
-
if (userRole) {
|
|
751
|
-
ws.send(JSON.stringify(userRole));
|
|
752
|
-
}
|
|
753
|
-
if (deviceId) {
|
|
754
|
-
ws.send(JSON.stringify(deviceId));
|
|
755
|
-
}
|
|
756
|
-
if (roomMode) {
|
|
757
|
-
ws.send(JSON.stringify(roomMode));
|
|
756
|
+
else if (report.type === "inbound-rtp" &&
|
|
757
|
+
(report.kind === "audio" || report.mediaType === "audio") &&
|
|
758
|
+
receivingAudio) {
|
|
759
|
+
if (report.bytesReceived === 0) {
|
|
760
|
+
microphoneFailed = "inbound";
|
|
758
761
|
}
|
|
759
|
-
|
|
760
|
-
ws.send(JSON.stringify(roomProduct));
|
|
761
|
-
}
|
|
762
|
-
if (sfuServer) {
|
|
763
|
-
ws.send(JSON.stringify(sfuServer));
|
|
764
|
-
}
|
|
765
|
-
if (featureFlags) {
|
|
766
|
-
ws.send(JSON.stringify(featureFlags));
|
|
767
|
-
}
|
|
768
|
-
while (buffer.length) {
|
|
769
|
-
ws.send(JSON.stringify(buffer.shift()));
|
|
770
|
-
}
|
|
771
|
-
getStatsBufferUsed = 0;
|
|
772
|
-
};
|
|
773
|
-
},
|
|
774
|
-
};
|
|
775
|
-
return connection;
|
|
776
|
-
}
|
|
777
|
-
const RTCSTATS_URL = "wss://rtcstats.srv.whereby.com";
|
|
778
|
-
const server = rtcStatsConnection(RTCSTATS_URL);
|
|
779
|
-
const stats = rtcstats(server.trace, 10000, [""]);
|
|
780
|
-
resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
|
|
781
|
-
const rtcStats = {
|
|
782
|
-
sendEvent: (type, value) => {
|
|
783
|
-
server.trace("customEvent", null, {
|
|
784
|
-
type,
|
|
785
|
-
value,
|
|
762
|
+
}
|
|
786
763
|
});
|
|
764
|
+
return microphoneFailed;
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const PROTOCOL_REQUESTS = {
|
|
769
|
+
BLOCK_CLIENT: "block_client",
|
|
770
|
+
CANCEL_ROOM_KNOCK: "cancel_room_knock",
|
|
771
|
+
CLAIM_ROOM: "claim_room",
|
|
772
|
+
CLEAR_CHAT_HISTORY: "clear_chat_history",
|
|
773
|
+
ENABLE_AUDIO: "enable_audio",
|
|
774
|
+
ENABLE_VIDEO: "enable_video",
|
|
775
|
+
END_STREAM: "end_stream",
|
|
776
|
+
FETCH_MEDIASERVER_CONFIG: "fetch_mediaserver_config",
|
|
777
|
+
HANDLE_KNOCK: "handle_knock",
|
|
778
|
+
IDENTIFY_DEVICE: "identify_device",
|
|
779
|
+
INVITE_CLIENT_AS_MEMBER: "invite_client_as_member",
|
|
780
|
+
JOIN_ROOM: "join_room",
|
|
781
|
+
KICK_CLIENT: "kick_client",
|
|
782
|
+
KNOCK_ROOM: "knock_room",
|
|
783
|
+
LEAVE_ROOM: "leave_room",
|
|
784
|
+
SEND_CLIENT_METADATA: "send_client_metadata",
|
|
785
|
+
SET_LOCK: "set_lock",
|
|
786
|
+
SHARE_MEDIA: "share_media",
|
|
787
|
+
START_NEW_STREAM: "start_new_stream",
|
|
788
|
+
START_SCREENSHARE: "start_screenshare",
|
|
789
|
+
STOP_SCREENSHARE: "stop_screenshare",
|
|
790
|
+
START_URL_EMBED: "start_url_embed",
|
|
791
|
+
STOP_URL_EMBED: "stop_url_embed",
|
|
792
|
+
START_RECORDING: "start_recording",
|
|
793
|
+
STOP_RECORDING: "stop_recording",
|
|
794
|
+
};
|
|
795
|
+
const PROTOCOL_RESPONSES = {
|
|
796
|
+
AUDIO_ENABLED: "audio_enabled",
|
|
797
|
+
BACKGROUND_IMAGE_CHANGED: "background_image_changed",
|
|
798
|
+
BLOCK_ADDED: "block_added",
|
|
799
|
+
BLOCK_REMOVED: "block_removed",
|
|
800
|
+
CHAT_HISTORY_CLEARED: "chat_history_cleared",
|
|
801
|
+
CLIENT_BLOCKED: "client_blocked",
|
|
802
|
+
CLIENT_INVITED_AS_MEMBER: "client_invited_as_member",
|
|
803
|
+
CLIENT_KICKED: "client_kicked",
|
|
804
|
+
CLIENT_LEFT: "client_left",
|
|
805
|
+
CLIENT_METADATA_RECEIVED: "client_metadata_received",
|
|
806
|
+
CLIENT_READY: "client_ready",
|
|
807
|
+
CLIENT_ROLE_CHANGED: "client_role_changed",
|
|
808
|
+
CLIENT_USER_ID_CHANGED: "client_user_id_changed",
|
|
809
|
+
CONTACTS_UPDATED: "contacts_updated",
|
|
810
|
+
DEVICE_IDENTIFIED: "device_identified",
|
|
811
|
+
ROOM_ROLES_UPDATED: "room_roles_updated",
|
|
812
|
+
KNOCK_HANDLED: "knock_handled",
|
|
813
|
+
KNOCK_PAGE_BACKGROUND_CHANGED: "knock_page_background_changed",
|
|
814
|
+
KNOCKER_LEFT: "knocker_left",
|
|
815
|
+
MEDIASERVER_CONFIG: "mediaserver_config",
|
|
816
|
+
MEDIA_SHARED: "media_shared",
|
|
817
|
+
MEMBER_INVITE: "member_invite",
|
|
818
|
+
NEW_CLIENT: "new_client",
|
|
819
|
+
NEW_STREAM_STARTED: "new_stream_started",
|
|
820
|
+
SCREENSHARE_STARTED: "screenshare_started",
|
|
821
|
+
SCREENSHARE_STOPPED: "screenshare_stopped",
|
|
822
|
+
OWNER_NOTIFIED: "owner_notified",
|
|
823
|
+
OWNERS_CHANGED: "owners_changed",
|
|
824
|
+
PLAY_CLIENT_STICKER: "play_client_sticker",
|
|
825
|
+
ROOM_INTEGRATION_ENABLED: "room_integration_enabled",
|
|
826
|
+
ROOM_INTEGRATION_DISABLED: "room_integration_disabled",
|
|
827
|
+
ROOM_JOINED: "room_joined",
|
|
828
|
+
ROOM_KNOCKED: "room_knocked",
|
|
829
|
+
ROOM_LEFT: "room_left",
|
|
830
|
+
ROOM_LOCKED: "room_locked",
|
|
831
|
+
ROOM_PERMISSIONS_CHANGED: "room_permissions_changed",
|
|
832
|
+
ROOM_LOGO_CHANGED: "room_logo_changed",
|
|
833
|
+
ROOM_TYPE_CHANGED: "room_type_changed",
|
|
834
|
+
ROOM_MODE_CHANGED: "room_mode_changed",
|
|
835
|
+
SOCKET_USER_ID_CHANGED: "socket_user_id_changed",
|
|
836
|
+
STICKERS_UNLOCKED: "stickers_unlocked",
|
|
837
|
+
STREAM_ENDED: "stream_ended",
|
|
838
|
+
URL_EMBED_STARTED: "url_embed_started",
|
|
839
|
+
URL_EMBED_STOPPED: "url_embed_stopped",
|
|
840
|
+
RECORDING_STARTED: "recording_started",
|
|
841
|
+
RECORDING_STOPPED: "recording_stopped",
|
|
842
|
+
USER_NOTIFIED: "user_notified",
|
|
843
|
+
VIDEO_ENABLED: "video_enabled",
|
|
844
|
+
CLIENT_UNABLE_TO_JOIN: "client_unable_to_join",
|
|
845
|
+
LIVE_TRANSCRIPTION_STARTED: "live_transcription_started",
|
|
846
|
+
LIVE_TRANSCRIPTION_STOPPED: "live_transcription_stopped",
|
|
847
|
+
LIVE_CAPTIONS_STARTED: "live_captions_started",
|
|
848
|
+
LIVE_CAPTIONS_STOPPED: "live_captions_stopped",
|
|
849
|
+
};
|
|
850
|
+
const PROTOCOL_ERRORS = {
|
|
851
|
+
CANNOT_INVITE_YOURSELF: "cannot_invite_yourself",
|
|
852
|
+
CLIENT_BLOCKED: "client_blocked",
|
|
853
|
+
CLIENT_MISSING_DEVICE_ID: "client_missing_device_id",
|
|
854
|
+
FORBIDDEN: "forbidden",
|
|
855
|
+
FREE_TIER_EXHAUSTED: "free_tier_exhausted",
|
|
856
|
+
INTERNAL_SERVER_ERROR: "internal_server_error",
|
|
857
|
+
INVALID_AVATAR: "invalid_avatar",
|
|
858
|
+
INVALID_PARAMETERS: "invalid_parameters",
|
|
859
|
+
INVALID_ROOM_NAME: "invalid_room_name",
|
|
860
|
+
MAX_VIEWER_LIMIT_REACHED: "max_viewer_limit_reached",
|
|
861
|
+
MISSING_PARAMETERS: "missing_parameters",
|
|
862
|
+
MISSING_ROOM_NAME: "missing_room_name",
|
|
863
|
+
NOT_AN_OWNER: "not_an_owner",
|
|
864
|
+
NOT_IN_A_ROOM: "not_in_a_room",
|
|
865
|
+
ROOM_ALREADY_CLAIMED: "room_already_claimed",
|
|
866
|
+
ROOM_CONCURRENCY_CONTROL_ERROR: "room_concurrency_control_error",
|
|
867
|
+
ROOM_EMAIL_MISSING: "room_email_missing",
|
|
868
|
+
ROOM_EMPTY: "room_empty",
|
|
869
|
+
ROOM_FULL: "room_full",
|
|
870
|
+
ROOM_JOIN_PERMISSION_DENIED: "room_join_permission_denied",
|
|
871
|
+
ROOM_LOCKED: "room_locked",
|
|
872
|
+
ROOM_MEETING_TIME_EXHAUSTED: "room_meeting_time_exhausted",
|
|
873
|
+
ROOM_UNCLAIMED: "room_unclaimed",
|
|
874
|
+
TOO_LONG_TEXT: "too_long_text",
|
|
875
|
+
UNIQUE_ROLE_ALREADY_IN_ROOM: "unique_role_already_in_room",
|
|
876
|
+
UNSUPPORTED_VIDEO_ENCODING: "unsupported_video_encoding",
|
|
877
|
+
VIDEO_STICKER_DOES_NOT_EXIST: "video_sticker_does_not_exist",
|
|
878
|
+
VIDEO_STICKER_FORMAT_ERROR: "video_sticker_format_error",
|
|
879
|
+
};
|
|
880
|
+
const RELAY_MESSAGES = {
|
|
881
|
+
CHAT_MESSAGE: "chat_message",
|
|
882
|
+
CHAT_READ_STATE: "chat_read_state",
|
|
883
|
+
CHAT_STATE: "chat_state",
|
|
884
|
+
ICE_CANDIDATE: "ice_candidate",
|
|
885
|
+
ICE_END_OF_CANDIDATES: "ice_endofcandidates",
|
|
886
|
+
READY_TO_RECEIVE_OFFER: "ready_to_receive_offer",
|
|
887
|
+
REMOTE_CLIENT_MEDIA_REQUEST: "remote_client_media_request",
|
|
888
|
+
SDP_ANSWER: "sdp_answer",
|
|
889
|
+
SDP_OFFER: "sdp_offer",
|
|
890
|
+
VIDEO_STICKER: "video_sticker",
|
|
891
|
+
};
|
|
892
|
+
const KNOCK_MESSAGES = {
|
|
893
|
+
actions: {
|
|
894
|
+
ACCEPT: "accept",
|
|
895
|
+
HOLD: "hold",
|
|
896
|
+
REJECT: "reject",
|
|
787
897
|
},
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
rtcStats.sendEvent("video_muted", { muted });
|
|
898
|
+
resolutions: {
|
|
899
|
+
ACCEPTED: "accepted",
|
|
900
|
+
ON_HOLD: "on_hold",
|
|
901
|
+
REJECTED: "rejected",
|
|
793
902
|
},
|
|
794
|
-
|
|
903
|
+
};
|
|
904
|
+
const PROTOCOL_EVENTS = {
|
|
905
|
+
PENDING_CLIENT_LEFT: "pending_client_left",
|
|
906
|
+
MEDIA_QUALITY_CHANGED: "media_quality_changed",
|
|
795
907
|
};
|
|
796
908
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
909
|
+
const EVENTS = {
|
|
910
|
+
CLIENT_CONNECTION_STATUS_CHANGED: "client_connection_status_changed",
|
|
911
|
+
STREAM_ADDED: "stream_added",
|
|
912
|
+
RTC_MANAGER_CREATED: "rtc_manager_created",
|
|
913
|
+
RTC_MANAGER_DESTROYED: "rtc_manager_destroyed",
|
|
914
|
+
LOCAL_STREAM_TRACK_ADDED: "local_stream_track_added",
|
|
915
|
+
LOCAL_STREAM_TRACK_REMOVED: "local_stream_track_removed",
|
|
916
|
+
REMOTE_STREAM_TRACK_ADDED: "remote_stream_track_added",
|
|
917
|
+
REMOTE_STREAM_TRACK_REMOVED: "remote_stream_track_removed",
|
|
918
|
+
};
|
|
919
|
+
const TYPES = {
|
|
920
|
+
CONNECTING: "connecting",
|
|
921
|
+
CONNECTION_FAILED: "connection_failed",
|
|
922
|
+
CONNECTION_SUCCESSFUL: "connection_successful",
|
|
923
|
+
CONNECTION_DISCONNECTED: "connection_disconnected",
|
|
802
924
|
};
|
|
803
|
-
if (window.RTCPeerConnection) {
|
|
804
|
-
const OriginalRTCPeerConnection = window.RTCPeerConnection;
|
|
805
|
-
function PatchedRTCPeerConnection(rtcConfig) {
|
|
806
|
-
const pc = new OriginalRTCPeerConnection(rtcConfig);
|
|
807
|
-
peerConnections.push(pc);
|
|
808
|
-
peerConnectionData.set(pc, { index: peerConnectionCounter++ });
|
|
809
|
-
const onConnectionStateChange = () => {
|
|
810
|
-
if (pc.connectionState === "closed") {
|
|
811
|
-
removePeerConnection(pc);
|
|
812
|
-
pc.removeEventListener("connectionstatechange", onConnectionStateChange);
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
pc.addEventListener("connectionstatechange", onConnectionStateChange);
|
|
816
|
-
return pc;
|
|
817
|
-
}
|
|
818
|
-
PatchedRTCPeerConnection.prototype = OriginalRTCPeerConnection.prototype;
|
|
819
|
-
window.RTCPeerConnection = PatchedRTCPeerConnection;
|
|
820
|
-
}
|
|
821
|
-
const getCurrentPeerConnections = () => peerConnections;
|
|
822
|
-
const getPeerConnectionIndex = (pc) => { var _a; return (_a = peerConnectionData.get(pc)) === null || _a === void 0 ? void 0 : _a.index; };
|
|
823
|
-
const setPeerConnectionsForTests = (pcs) => (peerConnections = pcs);
|
|
824
925
|
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
const sendersAndReceivers = [...pc.getSenders(), ...pc.getReceivers()];
|
|
866
|
-
const reports = yield Promise.all(sendersAndReceivers.map((o) => o.getStats()));
|
|
867
|
-
reports.forEach((tReport, index) => {
|
|
868
|
-
tReport.forEach((stats) => {
|
|
869
|
-
if (stats.type === "inbound-rtp" || stats.type === "outbound-rtp") {
|
|
870
|
-
pcData.ssrcToTrackId[stats.ssrc] = sendersAndReceivers[index].track.id;
|
|
871
|
-
}
|
|
872
|
-
});
|
|
873
|
-
});
|
|
874
|
-
missingSsrcs.forEach((ssrc) => {
|
|
875
|
-
numMissingTrackSsrcLookups++;
|
|
876
|
-
if (!pcData.ssrcToTrackId[ssrc]) {
|
|
877
|
-
pcData.ssrcToTrackId[ssrc] = "?" + ssrc;
|
|
878
|
-
}
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
return [pc, report, pcData];
|
|
882
|
-
}
|
|
883
|
-
catch (e) {
|
|
884
|
-
rtcStats.sendEvent("trackSsrcLookupFailed", {
|
|
885
|
-
name: e === null || e === void 0 ? void 0 : e.name,
|
|
886
|
-
cause: e === null || e === void 0 ? void 0 : e.cause,
|
|
887
|
-
message: e === null || e === void 0 ? void 0 : e.message,
|
|
888
|
-
});
|
|
889
|
-
numFailedTrackSsrcLookups++;
|
|
890
|
-
return [pc, [], pcData];
|
|
891
|
-
}
|
|
892
|
-
})));
|
|
893
|
-
|
|
894
|
-
const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, trackId, ssrc) => {
|
|
895
|
-
let viewStats = statsByView[clientId];
|
|
896
|
-
if (!viewStats) {
|
|
897
|
-
viewStats = { candidatePairs: {}, tracks: {}, startTime: time, updated: time };
|
|
898
|
-
statsByView[clientId] = viewStats;
|
|
899
|
-
}
|
|
900
|
-
viewStats.updated = time;
|
|
901
|
-
let trackStats = viewStats.tracks[trackId];
|
|
902
|
-
if (!trackStats) {
|
|
903
|
-
trackStats = { ssrcs: {}, startTime: time, updated: time };
|
|
904
|
-
viewStats.tracks[trackId] = trackStats;
|
|
905
|
-
}
|
|
906
|
-
trackStats.updated = time;
|
|
907
|
-
let ssrcStats = trackStats.ssrcs[ssrc];
|
|
908
|
-
if (!ssrcStats) {
|
|
909
|
-
ssrcStats = {
|
|
910
|
-
startTime: time,
|
|
911
|
-
updated: time,
|
|
912
|
-
pcIndex,
|
|
913
|
-
};
|
|
914
|
-
trackStats.ssrcs[ssrc] = ssrcStats;
|
|
915
|
-
}
|
|
916
|
-
ssrcStats.updated = time;
|
|
917
|
-
return ssrcStats;
|
|
918
|
-
};
|
|
919
|
-
const removeNonUpdatedStats = (statsByView, time) => {
|
|
920
|
-
Object.entries(statsByView).forEach(([viewId, viewStats]) => {
|
|
921
|
-
if (viewStats.updated !== undefined && viewStats.updated < time) {
|
|
922
|
-
delete statsByView[viewId];
|
|
923
|
-
}
|
|
924
|
-
else {
|
|
925
|
-
Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
|
|
926
|
-
if (trackStats.updated < time) {
|
|
927
|
-
delete viewStats.tracks[trackId];
|
|
928
|
-
}
|
|
929
|
-
else {
|
|
930
|
-
Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
|
|
931
|
-
if (ssrcStats.updated < time) {
|
|
932
|
-
delete trackStats.ssrcs[ssrc];
|
|
933
|
-
}
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
});
|
|
939
|
-
};
|
|
940
|
-
const DEFAULT_CLIENT = {
|
|
941
|
-
id: "unknown",
|
|
942
|
-
clientId: "unknown",
|
|
943
|
-
audio: { enabled: false, track: undefined },
|
|
944
|
-
video: { enabled: false, track: undefined },
|
|
945
|
-
isAudioOnlyModeEnabled: false,
|
|
946
|
-
isLocalClient: true,
|
|
947
|
-
isPresentation: false,
|
|
948
|
-
};
|
|
949
|
-
function collectStats(state_1, _a, immediate_1) {
|
|
950
|
-
return __awaiter(this, arguments, void 0, function* (state, { logger, interval }, immediate) {
|
|
951
|
-
const collectStatsBound = collectStats.bind(null, state, { interval, logger });
|
|
952
|
-
try {
|
|
953
|
-
const clients = state.getClients();
|
|
954
|
-
const defaultClient = clients.find((c) => c.isLocalClient && !c.isPresentation) || DEFAULT_CLIENT;
|
|
955
|
-
let defaultViewStats = state.statsByView[defaultClient.id];
|
|
956
|
-
if (!defaultViewStats) {
|
|
957
|
-
defaultViewStats = { tracks: {}, candidatePairs: {}, pressure: null };
|
|
958
|
-
state.statsByView[defaultClient.id] = defaultViewStats;
|
|
959
|
-
}
|
|
960
|
-
defaultViewStats.pressure = state.lastPressureObserverRecord;
|
|
961
|
-
const timeSinceLastUpdate = Date.now() - state.lastUpdateTime;
|
|
962
|
-
if (timeSinceLastUpdate < 400) {
|
|
963
|
-
if (immediate)
|
|
964
|
-
return state.statsByView;
|
|
965
|
-
state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
|
|
966
|
-
state.nextTimeout = setTimeout(collectStatsBound, interval);
|
|
967
|
-
return;
|
|
968
|
-
}
|
|
969
|
-
state.lastUpdateTime = Date.now();
|
|
970
|
-
(yield getPeerConnectionsWithStatsReports()).forEach(([pc, report, pcData]) => {
|
|
971
|
-
const pcIndex = getPeerConnectionIndex(pc);
|
|
972
|
-
if (pcIndex === undefined) {
|
|
973
|
-
logger.warn("Could not find index for PeerConnection");
|
|
974
|
-
}
|
|
975
|
-
if (pc.connectionState === "closed") {
|
|
976
|
-
report = new Map();
|
|
977
|
-
removePeerConnection(pc);
|
|
978
|
-
}
|
|
979
|
-
pcData.previousSSRCs = pcData.currentSSRCs || {};
|
|
980
|
-
pcData.currentSSRCs = {};
|
|
981
|
-
report.forEach((currentRtcStats) => {
|
|
982
|
-
var _a, _b;
|
|
983
|
-
if (currentRtcStats.type === "candidate-pair" && /inprogress|succeeded/.test(currentRtcStats.state)) {
|
|
984
|
-
const prevRtcStats = (_a = pcData._oldReport) === null || _a === void 0 ? void 0 : _a.get(currentRtcStats.id);
|
|
985
|
-
const timeDiff = prevRtcStats ? currentRtcStats.timestamp - prevRtcStats.timestamp : interval;
|
|
986
|
-
const cpId = pcIndex + ":" + currentRtcStats.id;
|
|
987
|
-
let cpMetrics = defaultViewStats.candidatePairs[cpId];
|
|
988
|
-
if (!cpMetrics) {
|
|
989
|
-
cpMetrics = { startTime: state.lastUpdateTime, id: cpId };
|
|
990
|
-
defaultViewStats.candidatePairs[cpId] = cpMetrics;
|
|
991
|
-
}
|
|
992
|
-
captureCandidatePairInfoMetrics(cpMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
993
|
-
cpMetrics.lastRtcStatsTime = state.lastUpdateTime;
|
|
994
|
-
}
|
|
995
|
-
if (currentRtcStats.type === "inbound-rtp" || currentRtcStats.type === "outbound-rtp") {
|
|
996
|
-
const kind = (currentRtcStats.mediaType || currentRtcStats.kind);
|
|
997
|
-
const ssrc = currentRtcStats.ssrc;
|
|
998
|
-
let trackId = currentRtcStats.trackIdentifier || pcData.ssrcToTrackId[ssrc];
|
|
999
|
-
let prevRtcStats = (_b = pcData._oldReport) === null || _b === void 0 ? void 0 : _b.get(currentRtcStats.id);
|
|
1000
|
-
if (prevRtcStats && prevRtcStats.mediaSourceId !== currentRtcStats.mediaSourceId) {
|
|
1001
|
-
const mediaSourceStats = report.get(currentRtcStats.mediaSourceId);
|
|
1002
|
-
if (mediaSourceStats && mediaSourceStats.trackIdentifier) {
|
|
1003
|
-
trackId = mediaSourceStats.trackIdentifier;
|
|
1004
|
-
pcData.ssrcToTrackId[ssrc] = trackId;
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
const client = clients.find((c) => { var _a; return ((_a = c[kind].track) === null || _a === void 0 ? void 0 : _a.id) === trackId; }) || defaultClient;
|
|
1008
|
-
const clientTrack = client[kind].track;
|
|
1009
|
-
if (!currentRtcStats.trackIdentifier &&
|
|
1010
|
-
pcData.ssrcToTrackId[ssrc] &&
|
|
1011
|
-
(clientTrack === null || clientTrack === void 0 ? void 0 : clientTrack.id) &&
|
|
1012
|
-
clientTrack.id !== pcData.ssrcToTrackId[ssrc]) {
|
|
1013
|
-
trackId = clientTrack.id;
|
|
1014
|
-
pcData.ssrcToTrackId[ssrc] = clientTrack.id;
|
|
1015
|
-
}
|
|
1016
|
-
pcData.currentSSRCs[ssrc] = client.id;
|
|
1017
|
-
if (prevRtcStats) {
|
|
1018
|
-
const newTransport = report.get(currentRtcStats.transportId);
|
|
1019
|
-
const oldTransport = pcData._oldReport.get(prevRtcStats.transportId);
|
|
1020
|
-
if (oldTransport &&
|
|
1021
|
-
(newTransport === null || newTransport === void 0 ? void 0 : newTransport.selectedCandidatePairId) !== oldTransport.selectedCandidatePairId) {
|
|
1022
|
-
prevRtcStats = null;
|
|
1023
|
-
}
|
|
1024
|
-
else if (currentRtcStats.bytesReceived < prevRtcStats.bytesReceived ||
|
|
1025
|
-
currentRtcStats.bytesSent < prevRtcStats.bytesSent) {
|
|
1026
|
-
prevRtcStats = null;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
const timeDiff = prevRtcStats ? currentRtcStats.timestamp - prevRtcStats.timestamp : interval;
|
|
1030
|
-
const ssrcMetrics = getOrCreateSsrcMetricsContainer(state.statsByView, state.lastUpdateTime, pcIndex, client.id, trackId, ssrc);
|
|
1031
|
-
captureSsrcInfo(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1032
|
-
captureCommonSsrcMetrics(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1033
|
-
if (kind === "video") {
|
|
1034
|
-
captureVideoSsrcMetrics(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1035
|
-
}
|
|
1036
|
-
if (kind === "audio") {
|
|
1037
|
-
captureAudioSsrcMetrics(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
});
|
|
1041
|
-
pcData._oldReport = report;
|
|
1042
|
-
Object.keys(pcData.previousSSRCs)
|
|
1043
|
-
.filter((ssrc) => !pcData.currentSSRCs[ssrc])
|
|
1044
|
-
.forEach((ssrc) => {
|
|
1045
|
-
const clientId = pcData.previousSSRCs[ssrc];
|
|
1046
|
-
if (clientId) {
|
|
1047
|
-
const clientView = state.statsByView[clientId];
|
|
1048
|
-
if (clientView) {
|
|
1049
|
-
Object.values(clientView.tracks).forEach((trackStats) => {
|
|
1050
|
-
if (trackStats.ssrcs[ssrc]) {
|
|
1051
|
-
delete trackStats.ssrcs[ssrc];
|
|
1052
|
-
}
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
});
|
|
1057
|
-
});
|
|
1058
|
-
removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
|
|
1059
|
-
Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
|
|
1060
|
-
const active = cp.lastRtcStatsTime === state.lastUpdateTime;
|
|
1061
|
-
cp.active = active;
|
|
1062
|
-
if (!active) {
|
|
1063
|
-
cp.state = "old/inactive";
|
|
1064
|
-
if (!cp.inactiveFromTime)
|
|
1065
|
-
cp.inactiveFromTime = state.lastUpdateTime;
|
|
1066
|
-
else {
|
|
1067
|
-
if (state.lastUpdateTime - cp.inactiveFromTime > 4000) {
|
|
1068
|
-
delete defaultViewStats.candidatePairs[cpKey];
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
});
|
|
1073
|
-
if (immediate) {
|
|
1074
|
-
return state.statsByView;
|
|
1075
|
-
}
|
|
1076
|
-
else {
|
|
1077
|
-
state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
catch (e) {
|
|
1081
|
-
logger.warn(e);
|
|
1082
|
-
state.numFailedStatsReports++;
|
|
1083
|
-
rtcStats.sendEvent("collectStatsFailed", {
|
|
1084
|
-
name: e === null || e === void 0 ? void 0 : e.name,
|
|
1085
|
-
cause: e === null || e === void 0 ? void 0 : e.cause,
|
|
1086
|
-
message: e === null || e === void 0 ? void 0 : e.message,
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1089
|
-
state.nextTimeout = setTimeout(collectStatsBound, interval);
|
|
1090
|
-
});
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
const REGISTERED_TRIALS = {};
|
|
1094
|
-
function registerOriginTrials(trials, registeredTrials = REGISTERED_TRIALS, document = window.document) {
|
|
1095
|
-
trials.forEach(({ hostnamePattern, token }) => {
|
|
1096
|
-
const key = `${hostnamePattern}-${token}`;
|
|
1097
|
-
if (registeredTrials[key]) {
|
|
1098
|
-
return;
|
|
1099
|
-
}
|
|
1100
|
-
if (hostnamePattern.test(document.location.hostname)) {
|
|
1101
|
-
const otMeta = document.createElement("meta");
|
|
1102
|
-
otMeta.httpEquiv = "origin-trial";
|
|
1103
|
-
otMeta.content = token;
|
|
1104
|
-
document.head.append(otMeta);
|
|
1105
|
-
registeredTrials[key] = true;
|
|
1106
|
-
}
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
const CPU_OBSERVER_OPTIONS = {
|
|
1111
|
-
sampleRate: 1,
|
|
1112
|
-
originTrials: [
|
|
1113
|
-
{
|
|
1114
|
-
hostnamePattern: /hereby\.dev/,
|
|
1115
|
-
token: "AkSNPHJw6EK08X0QU7kORnK9NABzRLAC7dqfXOwk5JwFAQG4Ey7WxLXxsnhieCgC1QHUdevE2EFICy7uBGDANwUAAABqeyJvcmlnaW4iOiJodHRwczovL2hlcmVieS5kZXY6NDQ0MyIsImZlYXR1cmUiOiJDb21wdXRlUHJlc3N1cmVfdjIiLCJleHBpcnkiOjE3MTY5NDA3OTksImlzU3ViZG9tYWluIjp0cnVlfQ==",
|
|
1116
|
-
},
|
|
1117
|
-
{
|
|
1118
|
-
hostnamePattern: /whereby\.com/,
|
|
1119
|
-
token: "Asc2wu8KpSx648i932NICteQDFcB05yl2QUUSHD7AQo8JGP2Fp6FF91TvYVJBsKGzLMH349rysPw5q9tqPC/PAUAAABqeyJvcmlnaW4iOiJodHRwczovL3doZXJlYnkuY29tOjQ0MyIsImZlYXR1cmUiOiJDb21wdXRlUHJlc3N1cmVfdjIiLCJleHBpcnkiOjE3MTY5NDA3OTksImlzU3ViZG9tYWluIjp0cnVlfQ==",
|
|
1120
|
-
},
|
|
1121
|
-
],
|
|
1122
|
-
};
|
|
1123
|
-
function startCpuObserver(cb, { sampleRate, originTrials } = CPU_OBSERVER_OPTIONS, window = globalThis.window) {
|
|
1124
|
-
registerOriginTrials(originTrials);
|
|
1125
|
-
let pressureObserver;
|
|
1126
|
-
if ("PressureObserver" in window) {
|
|
1127
|
-
pressureObserver = new window.PressureObserver(cb, { sampleRate });
|
|
1128
|
-
pressureObserver.observe("cpu", { sampleInterval: sampleRate * 1000 });
|
|
1129
|
-
return {
|
|
1130
|
-
stop: () => {
|
|
1131
|
-
pressureObserver.unobserve("cpu");
|
|
1132
|
-
},
|
|
1133
|
-
};
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
const STATE = {
|
|
1138
|
-
currentMonitor: null,
|
|
1139
|
-
getClients: () => [],
|
|
1140
|
-
lastUpdateTime: 0,
|
|
1141
|
-
statsByView: {},
|
|
1142
|
-
subscriptions: [],
|
|
1143
|
-
numFailedStatsReports: 0,
|
|
1144
|
-
};
|
|
1145
|
-
const OPTIONS = {
|
|
1146
|
-
interval: 2000,
|
|
1147
|
-
logger: new Logger(),
|
|
1148
|
-
};
|
|
1149
|
-
const getStats = () => {
|
|
1150
|
-
return Object.assign({}, STATE.statsByView);
|
|
1151
|
-
};
|
|
1152
|
-
const getNumFailedStatsReports = () => {
|
|
1153
|
-
return STATE.numFailedStatsReports;
|
|
1154
|
-
};
|
|
1155
|
-
const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
|
|
1156
|
-
const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
|
|
1157
|
-
const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
|
|
1158
|
-
const setClientProvider = (provider) => (STATE.getClients = provider);
|
|
1159
|
-
function startStatsMonitor(state, { interval, logger }) {
|
|
1160
|
-
const collectStatsBound = collectStats.bind(null, state, { interval, logger });
|
|
1161
|
-
let cpuObserver;
|
|
1162
|
-
try {
|
|
1163
|
-
cpuObserver = startCpuObserver((records) => (state.lastPressureObserverRecord = records.pop()));
|
|
1164
|
-
}
|
|
1165
|
-
catch (ex) {
|
|
1166
|
-
logger.warn("Failed to observe CPU pressure", ex);
|
|
1167
|
-
}
|
|
1168
|
-
setTimeout(collectStatsBound, interval);
|
|
1169
|
-
return {
|
|
1170
|
-
getUpdatedStats: () => {
|
|
1171
|
-
return collectStatsBound(true);
|
|
1172
|
-
},
|
|
1173
|
-
stop: () => {
|
|
1174
|
-
clearTimeout(state.nextTimeout);
|
|
1175
|
-
cpuObserver === null || cpuObserver === void 0 ? void 0 : cpuObserver.stop();
|
|
1176
|
-
},
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
function subscribeStats(subscription, options = OPTIONS, state = STATE) {
|
|
1180
|
-
state.subscriptions.push(subscription);
|
|
1181
|
-
if (!state.currentMonitor)
|
|
1182
|
-
state.currentMonitor = startStatsMonitor(state, options);
|
|
1183
|
-
return {
|
|
1184
|
-
stop() {
|
|
1185
|
-
var _a;
|
|
1186
|
-
state.subscriptions = state.subscriptions.filter((s) => s !== subscription);
|
|
1187
|
-
if (!state.subscriptions.length) {
|
|
1188
|
-
(_a = state.currentMonitor) === null || _a === void 0 ? void 0 : _a.stop();
|
|
1189
|
-
state.currentMonitor = null;
|
|
1190
|
-
}
|
|
1191
|
-
},
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
const PROTOCOL_REQUESTS = {
|
|
1196
|
-
BLOCK_CLIENT: "block_client",
|
|
1197
|
-
CANCEL_ROOM_KNOCK: "cancel_room_knock",
|
|
1198
|
-
CLAIM_ROOM: "claim_room",
|
|
1199
|
-
CLEAR_CHAT_HISTORY: "clear_chat_history",
|
|
1200
|
-
ENABLE_AUDIO: "enable_audio",
|
|
1201
|
-
ENABLE_VIDEO: "enable_video",
|
|
1202
|
-
END_STREAM: "end_stream",
|
|
1203
|
-
FETCH_MEDIASERVER_CONFIG: "fetch_mediaserver_config",
|
|
1204
|
-
HANDLE_KNOCK: "handle_knock",
|
|
1205
|
-
IDENTIFY_DEVICE: "identify_device",
|
|
1206
|
-
INVITE_CLIENT_AS_MEMBER: "invite_client_as_member",
|
|
1207
|
-
JOIN_ROOM: "join_room",
|
|
1208
|
-
KICK_CLIENT: "kick_client",
|
|
1209
|
-
KNOCK_ROOM: "knock_room",
|
|
1210
|
-
LEAVE_ROOM: "leave_room",
|
|
1211
|
-
SEND_CLIENT_METADATA: "send_client_metadata",
|
|
1212
|
-
SET_LOCK: "set_lock",
|
|
1213
|
-
SHARE_MEDIA: "share_media",
|
|
1214
|
-
START_NEW_STREAM: "start_new_stream",
|
|
1215
|
-
START_SCREENSHARE: "start_screenshare",
|
|
1216
|
-
STOP_SCREENSHARE: "stop_screenshare",
|
|
1217
|
-
START_URL_EMBED: "start_url_embed",
|
|
1218
|
-
STOP_URL_EMBED: "stop_url_embed",
|
|
1219
|
-
START_RECORDING: "start_recording",
|
|
1220
|
-
STOP_RECORDING: "stop_recording",
|
|
1221
|
-
};
|
|
1222
|
-
const PROTOCOL_RESPONSES = {
|
|
1223
|
-
AUDIO_ENABLED: "audio_enabled",
|
|
1224
|
-
BACKGROUND_IMAGE_CHANGED: "background_image_changed",
|
|
1225
|
-
BLOCK_ADDED: "block_added",
|
|
1226
|
-
BLOCK_REMOVED: "block_removed",
|
|
1227
|
-
CHAT_HISTORY_CLEARED: "chat_history_cleared",
|
|
1228
|
-
CLIENT_BLOCKED: "client_blocked",
|
|
1229
|
-
CLIENT_INVITED_AS_MEMBER: "client_invited_as_member",
|
|
1230
|
-
CLIENT_KICKED: "client_kicked",
|
|
1231
|
-
CLIENT_LEFT: "client_left",
|
|
1232
|
-
CLIENT_METADATA_RECEIVED: "client_metadata_received",
|
|
1233
|
-
CLIENT_READY: "client_ready",
|
|
1234
|
-
CLIENT_ROLE_CHANGED: "client_role_changed",
|
|
1235
|
-
CLIENT_USER_ID_CHANGED: "client_user_id_changed",
|
|
1236
|
-
CONTACTS_UPDATED: "contacts_updated",
|
|
1237
|
-
DEVICE_IDENTIFIED: "device_identified",
|
|
1238
|
-
ROOM_ROLES_UPDATED: "room_roles_updated",
|
|
1239
|
-
KNOCK_HANDLED: "knock_handled",
|
|
1240
|
-
KNOCK_PAGE_BACKGROUND_CHANGED: "knock_page_background_changed",
|
|
1241
|
-
KNOCKER_LEFT: "knocker_left",
|
|
1242
|
-
MEDIASERVER_CONFIG: "mediaserver_config",
|
|
1243
|
-
MEDIA_SHARED: "media_shared",
|
|
1244
|
-
MEMBER_INVITE: "member_invite",
|
|
1245
|
-
NEW_CLIENT: "new_client",
|
|
1246
|
-
NEW_STREAM_STARTED: "new_stream_started",
|
|
1247
|
-
SCREENSHARE_STARTED: "screenshare_started",
|
|
1248
|
-
SCREENSHARE_STOPPED: "screenshare_stopped",
|
|
1249
|
-
OWNER_NOTIFIED: "owner_notified",
|
|
1250
|
-
OWNERS_CHANGED: "owners_changed",
|
|
1251
|
-
PLAY_CLIENT_STICKER: "play_client_sticker",
|
|
1252
|
-
ROOM_INTEGRATION_ENABLED: "room_integration_enabled",
|
|
1253
|
-
ROOM_INTEGRATION_DISABLED: "room_integration_disabled",
|
|
1254
|
-
ROOM_JOINED: "room_joined",
|
|
1255
|
-
ROOM_KNOCKED: "room_knocked",
|
|
1256
|
-
ROOM_LEFT: "room_left",
|
|
1257
|
-
ROOM_LOCKED: "room_locked",
|
|
1258
|
-
ROOM_PERMISSIONS_CHANGED: "room_permissions_changed",
|
|
1259
|
-
ROOM_LOGO_CHANGED: "room_logo_changed",
|
|
1260
|
-
ROOM_TYPE_CHANGED: "room_type_changed",
|
|
1261
|
-
ROOM_MODE_CHANGED: "room_mode_changed",
|
|
1262
|
-
SOCKET_USER_ID_CHANGED: "socket_user_id_changed",
|
|
1263
|
-
STICKERS_UNLOCKED: "stickers_unlocked",
|
|
1264
|
-
STREAM_ENDED: "stream_ended",
|
|
1265
|
-
URL_EMBED_STARTED: "url_embed_started",
|
|
1266
|
-
URL_EMBED_STOPPED: "url_embed_stopped",
|
|
1267
|
-
RECORDING_STARTED: "recording_started",
|
|
1268
|
-
RECORDING_STOPPED: "recording_stopped",
|
|
1269
|
-
USER_NOTIFIED: "user_notified",
|
|
1270
|
-
VIDEO_ENABLED: "video_enabled",
|
|
1271
|
-
CLIENT_UNABLE_TO_JOIN: "client_unable_to_join",
|
|
1272
|
-
LIVE_TRANSCRIPTION_STARTED: "live_transcription_started",
|
|
1273
|
-
LIVE_TRANSCRIPTION_STOPPED: "live_transcription_stopped",
|
|
1274
|
-
LIVE_CAPTIONS_STARTED: "live_captions_started",
|
|
1275
|
-
LIVE_CAPTIONS_STOPPED: "live_captions_stopped",
|
|
1276
|
-
};
|
|
1277
|
-
const PROTOCOL_ERRORS = {
|
|
1278
|
-
CANNOT_INVITE_YOURSELF: "cannot_invite_yourself",
|
|
1279
|
-
CLIENT_BLOCKED: "client_blocked",
|
|
1280
|
-
CLIENT_MISSING_DEVICE_ID: "client_missing_device_id",
|
|
1281
|
-
FORBIDDEN: "forbidden",
|
|
1282
|
-
FREE_TIER_EXHAUSTED: "free_tier_exhausted",
|
|
1283
|
-
INTERNAL_SERVER_ERROR: "internal_server_error",
|
|
1284
|
-
INVALID_AVATAR: "invalid_avatar",
|
|
1285
|
-
INVALID_PARAMETERS: "invalid_parameters",
|
|
1286
|
-
INVALID_ROOM_NAME: "invalid_room_name",
|
|
1287
|
-
MAX_VIEWER_LIMIT_REACHED: "max_viewer_limit_reached",
|
|
1288
|
-
MISSING_PARAMETERS: "missing_parameters",
|
|
1289
|
-
MISSING_ROOM_NAME: "missing_room_name",
|
|
1290
|
-
NOT_AN_OWNER: "not_an_owner",
|
|
1291
|
-
NOT_IN_A_ROOM: "not_in_a_room",
|
|
1292
|
-
ROOM_ALREADY_CLAIMED: "room_already_claimed",
|
|
1293
|
-
ROOM_CONCURRENCY_CONTROL_ERROR: "room_concurrency_control_error",
|
|
1294
|
-
ROOM_EMAIL_MISSING: "room_email_missing",
|
|
1295
|
-
ROOM_EMPTY: "room_empty",
|
|
1296
|
-
ROOM_FULL: "room_full",
|
|
1297
|
-
ROOM_JOIN_PERMISSION_DENIED: "room_join_permission_denied",
|
|
1298
|
-
ROOM_LOCKED: "room_locked",
|
|
1299
|
-
ROOM_MEETING_TIME_EXHAUSTED: "room_meeting_time_exhausted",
|
|
1300
|
-
ROOM_UNCLAIMED: "room_unclaimed",
|
|
1301
|
-
TOO_LONG_TEXT: "too_long_text",
|
|
1302
|
-
UNIQUE_ROLE_ALREADY_IN_ROOM: "unique_role_already_in_room",
|
|
1303
|
-
UNSUPPORTED_VIDEO_ENCODING: "unsupported_video_encoding",
|
|
1304
|
-
VIDEO_STICKER_DOES_NOT_EXIST: "video_sticker_does_not_exist",
|
|
1305
|
-
VIDEO_STICKER_FORMAT_ERROR: "video_sticker_format_error",
|
|
1306
|
-
};
|
|
1307
|
-
const RELAY_MESSAGES = {
|
|
1308
|
-
CHAT_MESSAGE: "chat_message",
|
|
1309
|
-
CHAT_READ_STATE: "chat_read_state",
|
|
1310
|
-
CHAT_STATE: "chat_state",
|
|
1311
|
-
ICE_CANDIDATE: "ice_candidate",
|
|
1312
|
-
ICE_END_OF_CANDIDATES: "ice_endofcandidates",
|
|
1313
|
-
READY_TO_RECEIVE_OFFER: "ready_to_receive_offer",
|
|
1314
|
-
REMOTE_CLIENT_MEDIA_REQUEST: "remote_client_media_request",
|
|
1315
|
-
SDP_ANSWER: "sdp_answer",
|
|
1316
|
-
SDP_OFFER: "sdp_offer",
|
|
1317
|
-
VIDEO_STICKER: "video_sticker",
|
|
1318
|
-
};
|
|
1319
|
-
const KNOCK_MESSAGES = {
|
|
1320
|
-
actions: {
|
|
1321
|
-
ACCEPT: "accept",
|
|
1322
|
-
HOLD: "hold",
|
|
1323
|
-
REJECT: "reject",
|
|
1324
|
-
},
|
|
1325
|
-
resolutions: {
|
|
1326
|
-
ACCEPTED: "accepted",
|
|
1327
|
-
ON_HOLD: "on_hold",
|
|
1328
|
-
REJECTED: "rejected",
|
|
1329
|
-
},
|
|
1330
|
-
};
|
|
1331
|
-
const PROTOCOL_EVENTS = {
|
|
1332
|
-
PENDING_CLIENT_LEFT: "pending_client_left",
|
|
1333
|
-
MEDIA_QUALITY_CHANGED: "media_quality_changed",
|
|
926
|
+
const word = "[a-fA-F\\d:]";
|
|
927
|
+
const boundry = (options) => options && options.includeBoundaries ? `(?:(?<=\\s|^)(?=${word})|(?<=${word})(?=\\s|$))` : "";
|
|
928
|
+
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}";
|
|
929
|
+
const v6segment = "[a-fA-F\\d]{1,4}";
|
|
930
|
+
const v6 = `
|
|
931
|
+
(?:
|
|
932
|
+
(?:${v6segment}:){7}(?:${v6segment}|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
|
|
933
|
+
(?:${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
|
|
934
|
+
(?:${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
|
|
935
|
+
(?:${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
|
|
936
|
+
(?:${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
|
|
937
|
+
(?:${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
|
|
938
|
+
(?:${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
|
|
939
|
+
(?::(?:(?::${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
|
|
940
|
+
)(?:%[0-9a-zA-Z]{1,})? // %eth0 %1
|
|
941
|
+
`
|
|
942
|
+
.replace(/\s*\/\/.*$/gm, "")
|
|
943
|
+
.replace(/\n/g, "")
|
|
944
|
+
.trim();
|
|
945
|
+
const v46Exact = new RegExp(`(?:^${v4}$)|(?:^${v6}$)`);
|
|
946
|
+
const v4exact = new RegExp(`^${v4}$`);
|
|
947
|
+
const v6exact = new RegExp(`^${v6}$`);
|
|
948
|
+
const ipRegex = (options) => options && options.exact
|
|
949
|
+
? v46Exact
|
|
950
|
+
: new RegExp(`(?:${boundry(options)}${v4}${boundry(options)})|(?:${boundry(options)}${v6}${boundry(options)})`, "g");
|
|
951
|
+
ipRegex.v4 = (options) => options && options.exact ? v4exact : new RegExp(`${boundry(options)}${v4}${boundry(options)}`, "g");
|
|
952
|
+
ipRegex.v6 = (options) => options && options.exact ? v6exact : new RegExp(`${boundry(options)}${v6}${boundry(options)}`, "g");
|
|
953
|
+
|
|
954
|
+
var rtcManagerEvents = {
|
|
955
|
+
CAMERA_NOT_WORKING: "camera_not_working",
|
|
956
|
+
CONNECTION_BLOCKED_BY_NETWORK: "connection_blocked_by_network",
|
|
957
|
+
MICROPHONE_NOT_WORKING: "microphone_not_working",
|
|
958
|
+
MICROPHONE_STOPPED_WORKING: "microphone_stopped_working",
|
|
959
|
+
CAMERA_STOPPED_WORKING: "camera_stopped_working",
|
|
960
|
+
NEW_PC: "new_pc",
|
|
961
|
+
SFU_CONNECTION_OPEN: "sfu_connection_open",
|
|
962
|
+
SFU_CONNECTION_CLOSED: "sfu_connection_closed",
|
|
963
|
+
SFU_CONNECTION_INFO: "sfu_connection_info",
|
|
964
|
+
COLOCATION_SPEAKER: "colocation_speaker",
|
|
965
|
+
DOMINANT_SPEAKER: "dominant_speaker",
|
|
1334
966
|
};
|
|
1335
967
|
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
this.
|
|
1341
|
-
this.
|
|
1342
|
-
this.
|
|
1343
|
-
this.
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
roomJoinedLate: 0,
|
|
1347
|
-
pendingClientCanceled: 0,
|
|
1348
|
-
evaluationFailed: 0,
|
|
1349
|
-
roomJoined: 0,
|
|
1350
|
-
};
|
|
1351
|
-
socket.on("disconnect", () => {
|
|
1352
|
-
this._signalDisconnectTime = Date.now();
|
|
1353
|
-
});
|
|
1354
|
-
socket.on(PROTOCOL_RESPONSES.ROOM_JOINED, (payload) => this._onRoomJoined(payload));
|
|
1355
|
-
socket.on(PROTOCOL_RESPONSES.NEW_CLIENT, (payload) => this._onNewClient(payload));
|
|
1356
|
-
socket.on(PROTOCOL_RESPONSES.CLIENT_LEFT, (payload) => this._onClientLeft(payload));
|
|
1357
|
-
socket.on(PROTOCOL_EVENTS.PENDING_CLIENT_LEFT, (payload) => this._onPendingClientLeft(payload));
|
|
1358
|
-
socket.on(PROTOCOL_RESPONSES.AUDIO_ENABLED, (payload) => this._onAudioEnabled(payload));
|
|
1359
|
-
socket.on(PROTOCOL_RESPONSES.VIDEO_ENABLED, (payload) => this._onVideoEnabled(payload));
|
|
1360
|
-
socket.on(PROTOCOL_RESPONSES.SCREENSHARE_STARTED, (payload) => this._onScreenshareChanged(payload, true));
|
|
1361
|
-
socket.on(PROTOCOL_RESPONSES.SCREENSHARE_STOPPED, (payload) => this._onScreenshareChanged(payload, false));
|
|
1362
|
-
}
|
|
1363
|
-
_onRoomJoined(payload) {
|
|
1364
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1365
|
-
var _a;
|
|
1366
|
-
this.reconnectThresholdInMs = (payload.disconnectTimeout || 0) * 0.8;
|
|
1367
|
-
if (payload === null || payload === void 0 ? void 0 : payload.error) {
|
|
1368
|
-
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1369
|
-
return;
|
|
1370
|
-
}
|
|
1371
|
-
if (!payload.selfId) {
|
|
1372
|
-
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1373
|
-
return;
|
|
1374
|
-
}
|
|
1375
|
-
const myDeviceId = (_a = payload.room.clients.find((c) => payload.selfId === c.id)) === null || _a === void 0 ? void 0 : _a.deviceId;
|
|
1376
|
-
if (!myDeviceId) {
|
|
1377
|
-
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
if (!this._signalDisconnectTime) {
|
|
1381
|
-
this._resetClientState(payload);
|
|
1382
|
-
payload.room.clients = payload.room.clients.filter((c) => !(c.deviceId === myDeviceId && c.isPendingToLeave));
|
|
1383
|
-
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1384
|
-
return;
|
|
1385
|
-
}
|
|
1386
|
-
const RECONNECT_THRESHOLD = payload.disconnectTimeout * 0.8;
|
|
1387
|
-
const timeSinceDisconnect = Date.now() - this._signalDisconnectTime;
|
|
1388
|
-
if (timeSinceDisconnect > RECONNECT_THRESHOLD) {
|
|
1389
|
-
this._resetClientState(payload);
|
|
1390
|
-
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1391
|
-
this.metrics.roomJoinedLate++;
|
|
1392
|
-
return;
|
|
1393
|
-
}
|
|
1394
|
-
payload.room.clients = payload.room.clients.filter((c) => !(c.deviceId === myDeviceId && c.isPendingToLeave));
|
|
1395
|
-
const allStats = yield getUpdatedStats();
|
|
1396
|
-
payload.room.clients.forEach((client) => {
|
|
1397
|
-
var _a;
|
|
1398
|
-
try {
|
|
1399
|
-
if (client.id === payload.selfId)
|
|
1400
|
-
return;
|
|
1401
|
-
if (!this._clients[client.id]) {
|
|
1402
|
-
this._addClientToState(client);
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1405
|
-
if (!((_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.hasClient(client.id))) {
|
|
1406
|
-
return;
|
|
1407
|
-
}
|
|
1408
|
-
if (this._hasClientStateChanged({
|
|
1409
|
-
clientId: client.id,
|
|
1410
|
-
webcam: client.isVideoEnabled,
|
|
1411
|
-
mic: client.isAudioEnabled,
|
|
1412
|
-
screenShare: client.streams.length > 1,
|
|
1413
|
-
})) {
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
if (this._wasClientSendingMedia(client.id)) {
|
|
1417
|
-
if (!this._isClientMediaActive(allStats, client.id)) {
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
client.mergeWithOldClientState = true;
|
|
1422
|
-
}
|
|
1423
|
-
catch (error) {
|
|
1424
|
-
logger$a.error("Failed to evaluate if we should merge client state %o", error);
|
|
1425
|
-
this.metrics.evaluationFailed++;
|
|
1426
|
-
}
|
|
1427
|
-
});
|
|
1428
|
-
payload.room.clients.forEach((c) => {
|
|
1429
|
-
if (c.isPendingToLeave) {
|
|
1430
|
-
this._onPendingClientLeft({ clientId: c.id });
|
|
1431
|
-
}
|
|
1432
|
-
});
|
|
1433
|
-
this.metrics.roomJoined++;
|
|
1434
|
-
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
_onClientLeft(payload) {
|
|
1438
|
-
var _a;
|
|
1439
|
-
const { clientId } = payload;
|
|
1440
|
-
const client = this._clients[clientId];
|
|
1441
|
-
if (client) {
|
|
1442
|
-
clearTimeout(client.timeout);
|
|
1443
|
-
delete this._clients[clientId];
|
|
1444
|
-
}
|
|
1445
|
-
(_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.disconnect(clientId, payload.eventClaim);
|
|
1446
|
-
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, payload);
|
|
1447
|
-
}
|
|
1448
|
-
_onPendingClientLeft(payload) {
|
|
1449
|
-
const { clientId } = payload;
|
|
1450
|
-
const client = this._clients[clientId];
|
|
1451
|
-
if (!client) {
|
|
1452
|
-
logger$a.warn(`client ${clientId} not found`);
|
|
1453
|
-
return;
|
|
1454
|
-
}
|
|
1455
|
-
if (client.isPendingToLeave) {
|
|
1456
|
-
return;
|
|
1457
|
-
}
|
|
1458
|
-
client.isPendingToLeave = true;
|
|
1459
|
-
if (this._wasClientSendingMedia(clientId)) {
|
|
1460
|
-
client.checkActiveMediaAttempts = 0;
|
|
1461
|
-
this._abortIfNotActive(payload);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
_onNewClient(payload) {
|
|
1465
|
-
const { client: { id: clientId, deviceId }, } = payload;
|
|
1466
|
-
const client = this._clients[clientId];
|
|
1467
|
-
if (client && client.isPendingToLeave) {
|
|
1468
|
-
clearTimeout(client.timeoutHandler);
|
|
1469
|
-
client.isPendingToLeave = false;
|
|
1470
|
-
this.metrics.pendingClientCanceled++;
|
|
1471
|
-
return;
|
|
1472
|
-
}
|
|
1473
|
-
this._getPendingClientsByDeviceId(deviceId).forEach((client) => {
|
|
1474
|
-
clearTimeout(client.timeoutHandler);
|
|
1475
|
-
client.isPendingToLeave = undefined;
|
|
1476
|
-
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, { clientId: client.clientId });
|
|
1477
|
-
});
|
|
1478
|
-
this._addClientToState(payload.client);
|
|
1479
|
-
this.emit(PROTOCOL_RESPONSES.NEW_CLIENT, payload);
|
|
1480
|
-
}
|
|
1481
|
-
_abortIfNotActive(payload) {
|
|
1482
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1483
|
-
const { clientId } = payload;
|
|
1484
|
-
let client = this._clients[clientId];
|
|
1485
|
-
if (!(client === null || client === void 0 ? void 0 : client.isPendingToLeave))
|
|
1486
|
-
return;
|
|
1487
|
-
client.checkActiveMediaAttempts++;
|
|
1488
|
-
if (client.checkActiveMediaAttempts > 3) {
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
const stillActive = yield this._checkIsActive(clientId);
|
|
1492
|
-
if (stillActive) {
|
|
1493
|
-
client.timeoutHandler = setTimeout(() => this._abortIfNotActive(payload), 500);
|
|
1494
|
-
return;
|
|
1495
|
-
}
|
|
1496
|
-
client = this._clients[clientId];
|
|
1497
|
-
if (client === null || client === void 0 ? void 0 : client.isPendingToLeave) {
|
|
1498
|
-
clearTimeout(client.timeoutHandler);
|
|
1499
|
-
delete this._clients[clientId];
|
|
1500
|
-
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, payload);
|
|
1501
|
-
}
|
|
1502
|
-
});
|
|
1503
|
-
}
|
|
1504
|
-
_checkIsActive(clientId) {
|
|
1505
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1506
|
-
const allStats = yield getUpdatedStats();
|
|
1507
|
-
return this._isClientMediaActive(allStats, clientId);
|
|
1508
|
-
});
|
|
1509
|
-
}
|
|
1510
|
-
_isClientMediaActive(stats, clientId) {
|
|
1511
|
-
const clientStats = stats === null || stats === void 0 ? void 0 : stats[clientId];
|
|
1512
|
-
let isActive = false;
|
|
1513
|
-
if (clientStats) {
|
|
1514
|
-
Object.entries(clientStats.tracks).forEach(([trackId, trackStats]) => {
|
|
1515
|
-
if (trackId !== "probator")
|
|
1516
|
-
Object.values(trackStats.ssrcs).forEach((ssrcStats) => {
|
|
1517
|
-
if ((ssrcStats.bitrate || 0) > 0)
|
|
1518
|
-
isActive = true;
|
|
1519
|
-
});
|
|
1520
|
-
});
|
|
968
|
+
class AssertionError extends Error {
|
|
969
|
+
constructor(options) {
|
|
970
|
+
super(options.message);
|
|
971
|
+
this.name = "AssertionError";
|
|
972
|
+
this.code = "ERR_ASSERTION";
|
|
973
|
+
this.actual = options.actual;
|
|
974
|
+
this.expected = options.expected;
|
|
975
|
+
this.operator = options.operator;
|
|
976
|
+
if (Error.captureStackTrace) {
|
|
977
|
+
Error.captureStackTrace(this, options.stackStartFn);
|
|
1521
978
|
}
|
|
1522
|
-
return isActive;
|
|
1523
|
-
}
|
|
1524
|
-
_onAudioEnabled(payload) {
|
|
1525
|
-
const { clientId, isAudioEnabled } = payload;
|
|
1526
|
-
this._clients[clientId] = Object.assign(Object.assign({}, (this._clients[clientId] || {})), { isAudioEnabled });
|
|
1527
|
-
}
|
|
1528
|
-
_onVideoEnabled(payload) {
|
|
1529
|
-
const { clientId, isVideoEnabled } = payload;
|
|
1530
|
-
this._clients[clientId] = Object.assign(Object.assign({}, (this._clients[clientId] || {})), { isVideoEnabled });
|
|
1531
|
-
}
|
|
1532
|
-
_onScreenshareChanged(payload, action) {
|
|
1533
|
-
const { clientId } = payload;
|
|
1534
|
-
this._clients[clientId] = Object.assign(Object.assign({}, (this._clients[clientId] || {})), { isScreenshareEnabled: action });
|
|
1535
979
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
}
|
|
1544
|
-
if (mic !== state.isAudioEnabled) {
|
|
1545
|
-
return true;
|
|
1546
|
-
}
|
|
1547
|
-
if (screenShare !== state.isScreenshareEnabled) {
|
|
1548
|
-
return true;
|
|
980
|
+
}
|
|
981
|
+
function innerOk(fn, argLen, value, message) {
|
|
982
|
+
if (!value) {
|
|
983
|
+
let generatedMessage = false;
|
|
984
|
+
if (argLen === 0) {
|
|
985
|
+
generatedMessage = true;
|
|
986
|
+
message = "No value argument passed to `assert.ok()`";
|
|
1549
987
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
_addClientToState(newClient) {
|
|
1553
|
-
this._clients[newClient.id] = Object.assign(Object.assign({}, (this._clients[newClient.id] || {})), { isAudioEnabled: newClient.isAudioEnabled, isVideoEnabled: newClient.isVideoEnabled, isScreenshareEnabled: newClient.streams.length > 1, deviceId: newClient.deviceId, isPendingToLeave: newClient.isPendingToLeave, clientId: newClient.id });
|
|
1554
|
-
}
|
|
1555
|
-
_wasClientSendingMedia(clientId) {
|
|
1556
|
-
const client = this._clients[clientId];
|
|
1557
|
-
if (!client) {
|
|
1558
|
-
throw new Error(`Client ${clientId} not found in ReconnectManager state`);
|
|
988
|
+
else if (message instanceof Error) {
|
|
989
|
+
throw message;
|
|
1559
990
|
}
|
|
1560
|
-
|
|
991
|
+
const err = new AssertionError({
|
|
992
|
+
actual: value,
|
|
993
|
+
expected: true,
|
|
994
|
+
message,
|
|
995
|
+
operator: "==",
|
|
996
|
+
stackStartFn: fn,
|
|
997
|
+
});
|
|
998
|
+
err.generatedMessage = generatedMessage;
|
|
999
|
+
throw err;
|
|
1561
1000
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1001
|
+
}
|
|
1002
|
+
function innerFail(obj) {
|
|
1003
|
+
if (obj.message instanceof Error)
|
|
1004
|
+
throw obj.message;
|
|
1005
|
+
throw new AssertionError(obj);
|
|
1006
|
+
}
|
|
1007
|
+
function ok(...args) {
|
|
1008
|
+
innerOk(ok, args.length, ...args);
|
|
1009
|
+
}
|
|
1010
|
+
const assert = {
|
|
1011
|
+
fail: (message) => {
|
|
1012
|
+
innerFail({
|
|
1013
|
+
actual: "fail()",
|
|
1014
|
+
expected: "fail() should not be called",
|
|
1015
|
+
message,
|
|
1016
|
+
operator: "fail",
|
|
1017
|
+
stackStartFn: assert.fail,
|
|
1565
1018
|
});
|
|
1019
|
+
},
|
|
1020
|
+
ok,
|
|
1021
|
+
};
|
|
1022
|
+
assert.notEqual = function notEqual(actual, expected, message) {
|
|
1023
|
+
if (arguments.length < 2) {
|
|
1024
|
+
throw new Error("'actual' and 'expected' arguments are required");
|
|
1566
1025
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
this._addClientToState(client);
|
|
1575
|
-
}
|
|
1026
|
+
if (actual == expected) {
|
|
1027
|
+
innerFail({
|
|
1028
|
+
actual,
|
|
1029
|
+
expected,
|
|
1030
|
+
message,
|
|
1031
|
+
operator: "!=",
|
|
1032
|
+
stackStartFn: notEqual,
|
|
1576
1033
|
});
|
|
1577
1034
|
}
|
|
1578
|
-
}
|
|
1035
|
+
};
|
|
1036
|
+
assert.ok = ok;
|
|
1579
1037
|
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
this._disconnectDurationLimitEnabled = false;
|
|
1587
|
-
this.disconnectDurationLimitExceeded = false;
|
|
1588
|
-
this.serverSocket = serverSocket;
|
|
1589
|
-
this.serverSocket.on("connect", () => this.onConnect());
|
|
1590
|
-
this.serverSocket.onEngineEvent("ping", () => this.onPing());
|
|
1591
|
-
this.serverSocket.on("disconnect", () => this.onDisconnect());
|
|
1592
|
-
this.serverSocket.onEngineEvent("reconnect_attempt", () => this.onReconnectAttempt());
|
|
1593
|
-
}
|
|
1594
|
-
enableDisconnectDurationLimit() {
|
|
1595
|
-
this._disconnectDurationLimitEnabled = true;
|
|
1596
|
-
}
|
|
1597
|
-
pingHeartbeat() {
|
|
1598
|
-
clearTimeout(this.pingTimer);
|
|
1599
|
-
this.pingTimer = setTimeout(() => {
|
|
1600
|
-
this.serverSocket._socket.io.engine.close();
|
|
1601
|
-
}, SIGNAL_PING_INTERVAL + SIGNAL_PING_MAX_LATENCY);
|
|
1602
|
-
this.lastPingTimestamp = Date.now();
|
|
1603
|
-
}
|
|
1604
|
-
onConnect() {
|
|
1605
|
-
this.pingHeartbeat();
|
|
1606
|
-
}
|
|
1607
|
-
onPing() {
|
|
1608
|
-
this.pingHeartbeat();
|
|
1609
|
-
}
|
|
1610
|
-
onDisconnect() {
|
|
1611
|
-
clearTimeout(this.pingTimer);
|
|
1038
|
+
const createWorker = (fn) => {
|
|
1039
|
+
return new Worker(URL.createObjectURL(new Blob(["self.onmessage = ", fn.toString()], { type: "text/javascript" })));
|
|
1040
|
+
};
|
|
1041
|
+
const generateByteString = (count) => {
|
|
1042
|
+
if (count === 0) {
|
|
1043
|
+
return "";
|
|
1612
1044
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
this.serverSocket.disconnect();
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1045
|
+
const count2 = count / 2;
|
|
1046
|
+
let result = "F";
|
|
1047
|
+
while (result.length <= count2) {
|
|
1048
|
+
result += result;
|
|
1620
1049
|
}
|
|
1621
|
-
|
|
1050
|
+
return result + result.substring(0, count - result.length);
|
|
1051
|
+
};
|
|
1622
1052
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
const NOOP_KEEPALIVE_INTERVAL = 2000;
|
|
1627
|
-
const DISCONNECT_DURATION_LIMIT_MS = 60000;
|
|
1628
|
-
class ServerSocket {
|
|
1629
|
-
constructor(hostName, optionsOverrides, glitchFree = false, disconnectDurationLimitOn = false, serverSideDisconnectDurationLimitOn = false) {
|
|
1630
|
-
this._wasConnectedUsingWebsocket = false;
|
|
1631
|
-
this._disconnectDurationLimitOn = disconnectDurationLimitOn && !serverSideDisconnectDurationLimitOn;
|
|
1632
|
-
this._serverSideDisconnectDurationLimitOn = serverSideDisconnectDurationLimitOn;
|
|
1633
|
-
this._disconnectDurationLimitExceeded = false;
|
|
1634
|
-
this._reconnectManager = null;
|
|
1635
|
-
this._socket = socket_ioClient.io(hostName, Object.assign({ path: DEFAULT_SOCKET_PATH, randomizationFactor: 0.5, reconnectionDelay: 250, reconnectionDelayMax: 5000, timeout: 5000, transports: ["websocket"], withCredentials: true }, optionsOverrides));
|
|
1636
|
-
this._disconnectDurationLimitEnabled = false;
|
|
1637
|
-
this.joinRoomFinished = false;
|
|
1638
|
-
this._socket.io.on("reconnect", () => {
|
|
1639
|
-
if (this._disconnectDurationLimitOn &&
|
|
1640
|
-
this._didExceedDisconnectDurationLimit(this._disconnectDurationLimitLatestTimestamp)) {
|
|
1641
|
-
this._socket.close();
|
|
1642
|
-
this._disconnectDurationLimitExceeded = true;
|
|
1643
|
-
}
|
|
1644
|
-
this._socket.sendBuffer = [];
|
|
1645
|
-
});
|
|
1646
|
-
this._socket.io.on("reconnect_attempt", () => {
|
|
1647
|
-
var _a;
|
|
1648
|
-
if (this._disconnectDurationLimitOn &&
|
|
1649
|
-
this._didExceedDisconnectDurationLimit(this._disconnectDurationLimitLatestTimestamp)) {
|
|
1650
|
-
this._socket.close();
|
|
1651
|
-
this._disconnectDurationLimitExceeded = true;
|
|
1652
|
-
}
|
|
1653
|
-
if (this._wasConnectedUsingWebsocket) {
|
|
1654
|
-
this._socket.io.opts.transports = ["websocket"];
|
|
1655
|
-
if (((_a = adapter$6.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) !== "safari") {
|
|
1656
|
-
delete this._wasConnectedUsingWebsocket;
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
else {
|
|
1660
|
-
this._socket.io.opts.transports = ["websocket", "polling"];
|
|
1661
|
-
}
|
|
1662
|
-
});
|
|
1663
|
-
if (glitchFree)
|
|
1664
|
-
this._reconnectManager = new ReconnectManager(this._socket);
|
|
1665
|
-
if (this._serverSideDisconnectDurationLimitOn)
|
|
1666
|
-
this._keepAliveManager = new KeepAliveManager(this);
|
|
1667
|
-
this._socket.on("room_joined", (payload) => {
|
|
1668
|
-
if (!("error" in payload)) {
|
|
1669
|
-
this.joinRoomFinished = true;
|
|
1670
|
-
}
|
|
1671
|
-
});
|
|
1672
|
-
this._socket.on("connect", () => {
|
|
1673
|
-
const transport = this.getTransport();
|
|
1674
|
-
if (transport === "websocket") {
|
|
1675
|
-
this._wasConnectedUsingWebsocket = true;
|
|
1676
|
-
if (this._serverSideDisconnectDurationLimitOn)
|
|
1677
|
-
return;
|
|
1678
|
-
if (!this.noopKeepaliveInterval) {
|
|
1679
|
-
let disconnectDurationLimitTimestampCandidate = Date.now();
|
|
1680
|
-
this.noopKeepaliveInterval = setInterval(() => {
|
|
1681
|
-
try {
|
|
1682
|
-
if (this._socket.connected) {
|
|
1683
|
-
if (this._disconnectDurationLimitOn &&
|
|
1684
|
-
!this._didExceedDisconnectDurationLimit(disconnectDurationLimitTimestampCandidate)) {
|
|
1685
|
-
this._disconnectDurationLimitLatestTimestamp =
|
|
1686
|
-
disconnectDurationLimitTimestampCandidate;
|
|
1687
|
-
disconnectDurationLimitTimestampCandidate = Date.now();
|
|
1688
|
-
}
|
|
1689
|
-
this._socket.io.engine.sendPacket("noop");
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
catch (ex) { }
|
|
1693
|
-
}, NOOP_KEEPALIVE_INTERVAL);
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
});
|
|
1697
|
-
this._socket.on("disconnect", () => {
|
|
1698
|
-
this.joinRoomFinished = false;
|
|
1699
|
-
this.disconnectTimestamp = Date.now();
|
|
1700
|
-
if (this._serverSideDisconnectDurationLimitOn)
|
|
1701
|
-
return;
|
|
1702
|
-
if (this._disconnectDurationLimitOn &&
|
|
1703
|
-
this._didExceedDisconnectDurationLimit(this._disconnectDurationLimitLatestTimestamp)) {
|
|
1704
|
-
this._socket.close();
|
|
1705
|
-
this._disconnectDurationLimitExceeded = true;
|
|
1706
|
-
}
|
|
1707
|
-
if (this.noopKeepaliveInterval) {
|
|
1708
|
-
clearInterval(this.noopKeepaliveInterval);
|
|
1709
|
-
this.noopKeepaliveInterval = null;
|
|
1710
|
-
}
|
|
1711
|
-
});
|
|
1053
|
+
const getMediasoupDeviceAsync = (features) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1054
|
+
if (features.isNodeSdk) {
|
|
1055
|
+
return new mediasoupClient.Device({ handlerName: "Safari12" });
|
|
1712
1056
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
if (disconnectedDuration > DISCONNECT_DURATION_LIMIT_MS) {
|
|
1718
|
-
return true;
|
|
1719
|
-
}
|
|
1720
|
-
return false;
|
|
1057
|
+
let handlerName = (yield mediasoupClient.detectDeviceAsync()) ||
|
|
1058
|
+
(/applecoremedia|applewebkit|safari/i.test(navigator.userAgent) ? "Safari12" : undefined);
|
|
1059
|
+
if (/iphone|ipad/i.test(navigator.userAgent)) {
|
|
1060
|
+
handlerName = "Safari12";
|
|
1721
1061
|
}
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1062
|
+
if (!handlerName)
|
|
1063
|
+
handlerName = "Chrome111";
|
|
1064
|
+
return new mediasoupClient.Device({ handlerName });
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
const logger$8 = new Logger();
|
|
1068
|
+
const AUDIO_SETTINGS = {
|
|
1069
|
+
codecOptions: {
|
|
1070
|
+
opusDtx: true,
|
|
1071
|
+
opusFec: true,
|
|
1072
|
+
},
|
|
1073
|
+
encodings: [{ dtx: true }],
|
|
1074
|
+
};
|
|
1075
|
+
const VIDEO_SETTINGS_HD = {
|
|
1076
|
+
codecOptions: {
|
|
1077
|
+
videoGoogleStartBitrate: 500,
|
|
1078
|
+
},
|
|
1079
|
+
encodings: [
|
|
1080
|
+
{ scaleResolutionDownBy: 4, maxBitrate: 150000 },
|
|
1081
|
+
{ scaleResolutionDownBy: 2, maxBitrate: 500000 },
|
|
1082
|
+
{ scaleResolutionDownBy: 1, maxBitrate: 1000000 },
|
|
1083
|
+
],
|
|
1084
|
+
};
|
|
1085
|
+
const VIDEO_SETTINGS_SD = {
|
|
1086
|
+
codecOptions: {
|
|
1087
|
+
videoGoogleStartBitrate: 500,
|
|
1088
|
+
},
|
|
1089
|
+
encodings: [
|
|
1090
|
+
{ scaleResolutionDownBy: 2, maxBitrate: 150000 },
|
|
1091
|
+
{ scaleResolutionDownBy: 1, maxBitrate: 500000 },
|
|
1092
|
+
],
|
|
1093
|
+
};
|
|
1094
|
+
const VIDEO_SETTINGS_VP9 = {
|
|
1095
|
+
codecOptions: {
|
|
1096
|
+
videoGoogleStartBitrate: 500,
|
|
1097
|
+
},
|
|
1098
|
+
encodings: [{ scalabilityMode: "L3T2", maxBitrate: 1000000 }],
|
|
1099
|
+
};
|
|
1100
|
+
const VIDEO_SETTINGS_VP9_LOW_BANDWIDTH = {
|
|
1101
|
+
codecOptions: {
|
|
1102
|
+
videoGoogleStartBitrate: 500,
|
|
1103
|
+
},
|
|
1104
|
+
encodings: [{ scalabilityMode: "L2T2", maxBitrate: 500000 }],
|
|
1105
|
+
};
|
|
1106
|
+
const SCREEN_SHARE_SETTINGS = {
|
|
1107
|
+
encodings: [{}],
|
|
1108
|
+
};
|
|
1109
|
+
const SCREEN_SHARE_SIMULCAST_SETTINGS = {
|
|
1110
|
+
encodings: [
|
|
1111
|
+
{ scaleResolutionDownBy: 2, dtx: true, maxBitrate: 500000 },
|
|
1112
|
+
{ scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
|
|
1113
|
+
],
|
|
1114
|
+
};
|
|
1115
|
+
const ADDITIONAL_SCREEN_SHARE_SETTINGS = {
|
|
1116
|
+
encodings: [
|
|
1117
|
+
{ scaleResolutionDownBy: 4, dtx: true, maxBitrate: 150000 },
|
|
1118
|
+
{ scaleResolutionDownBy: 2, dtx: true, maxBitrate: 500000 },
|
|
1119
|
+
{ scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
|
|
1120
|
+
],
|
|
1121
|
+
};
|
|
1122
|
+
const getMediaSettings = (kind, isScreenShare, features, areTooManyAlreadyPresenting = false) => {
|
|
1123
|
+
var _a;
|
|
1124
|
+
const { lowDataModeEnabled, simulcastScreenshareOn, vp9On } = features;
|
|
1125
|
+
if (kind === "audio") {
|
|
1126
|
+
return AUDIO_SETTINGS;
|
|
1731
1127
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
}
|
|
1128
|
+
const isChrome = ((_a = adapterRaw.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) === "chrome";
|
|
1129
|
+
const isVp9Available = isChrome && vp9On;
|
|
1130
|
+
if (isScreenShare) {
|
|
1131
|
+
return getScreenShareMediaSettings({
|
|
1132
|
+
areTooManyAlreadyPresenting,
|
|
1133
|
+
simulcastScreenshareOn,
|
|
1134
|
+
});
|
|
1740
1135
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1136
|
+
else {
|
|
1137
|
+
return getCameraMediaSettings({
|
|
1138
|
+
lowBandwidth: lowDataModeEnabled,
|
|
1139
|
+
isVp9Available,
|
|
1140
|
+
});
|
|
1745
1141
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1142
|
+
};
|
|
1143
|
+
const getCameraMediaSettings = ({ lowBandwidth, isVp9Available, }) => {
|
|
1144
|
+
if (lowBandwidth) {
|
|
1145
|
+
if (isVp9Available) {
|
|
1146
|
+
return VIDEO_SETTINGS_VP9_LOW_BANDWIDTH;
|
|
1749
1147
|
}
|
|
1750
|
-
|
|
1751
|
-
}
|
|
1752
|
-
disconnect() {
|
|
1753
|
-
this._socket.disconnect();
|
|
1148
|
+
return VIDEO_SETTINGS_SD;
|
|
1754
1149
|
}
|
|
1755
|
-
|
|
1756
|
-
|
|
1150
|
+
if (isVp9Available) {
|
|
1151
|
+
return VIDEO_SETTINGS_VP9;
|
|
1757
1152
|
}
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1153
|
+
return VIDEO_SETTINGS_HD;
|
|
1154
|
+
};
|
|
1155
|
+
const getScreenShareMediaSettings = ({ areTooManyAlreadyPresenting, simulcastScreenshareOn, }) => {
|
|
1156
|
+
if (areTooManyAlreadyPresenting) {
|
|
1157
|
+
return ADDITIONAL_SCREEN_SHARE_SETTINGS;
|
|
1761
1158
|
}
|
|
1762
|
-
|
|
1763
|
-
return
|
|
1159
|
+
if (simulcastScreenshareOn)
|
|
1160
|
+
return SCREEN_SHARE_SIMULCAST_SETTINGS;
|
|
1161
|
+
return SCREEN_SHARE_SETTINGS;
|
|
1162
|
+
};
|
|
1163
|
+
var PrioritizableCodec;
|
|
1164
|
+
(function (PrioritizableCodec) {
|
|
1165
|
+
PrioritizableCodec["H264"] = "video/h264";
|
|
1166
|
+
PrioritizableCodec["VP9"] = "video/vp9";
|
|
1167
|
+
})(PrioritizableCodec || (PrioritizableCodec = {}));
|
|
1168
|
+
const modifyMediaCapabilities = (routerRtpCapabilities, features) => {
|
|
1169
|
+
var _a;
|
|
1170
|
+
const { vp9On, h264On } = features;
|
|
1171
|
+
const isChrome = ((_a = adapterRaw.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) === "chrome";
|
|
1172
|
+
if (!(routerRtpCapabilities === null || routerRtpCapabilities === void 0 ? void 0 : routerRtpCapabilities.codecs)) {
|
|
1173
|
+
return routerRtpCapabilities;
|
|
1764
1174
|
}
|
|
1765
|
-
|
|
1766
|
-
|
|
1175
|
+
if (vp9On && isChrome) {
|
|
1176
|
+
const sorted = prioritizeRouterRtpCapabilitiesCodecs(routerRtpCapabilities.codecs, PrioritizableCodec.VP9);
|
|
1177
|
+
return Object.assign(Object.assign({}, routerRtpCapabilities), { codecs: sorted });
|
|
1767
1178
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1179
|
+
else if (h264On) {
|
|
1180
|
+
const sorted = prioritizeRouterRtpCapabilitiesCodecs(routerRtpCapabilities.codecs, PrioritizableCodec.H264);
|
|
1181
|
+
return Object.assign(Object.assign({}, routerRtpCapabilities), { codecs: sorted });
|
|
1770
1182
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
if (this._reconnectManager && relayableEvents.includes(eventName)) {
|
|
1778
|
-
return this._interceptEvent(eventName, handler);
|
|
1779
|
-
}
|
|
1780
|
-
this._socket.on(eventName, handler);
|
|
1781
|
-
return () => {
|
|
1782
|
-
this._socket.off(eventName, handler);
|
|
1783
|
-
};
|
|
1183
|
+
return routerRtpCapabilities;
|
|
1184
|
+
};
|
|
1185
|
+
function prioritizeRouterRtpCapabilitiesCodecs(codecs, preferredCodec) {
|
|
1186
|
+
const preferredCodecEntry = codecs.find(({ mimeType }) => mimeType.toLowerCase() === preferredCodec);
|
|
1187
|
+
if (!preferredCodecEntry) {
|
|
1188
|
+
return codecs;
|
|
1784
1189
|
}
|
|
1785
|
-
|
|
1190
|
+
return [...codecs].sort((left, right) => {
|
|
1786
1191
|
var _a;
|
|
1787
|
-
(
|
|
1788
|
-
|
|
1789
|
-
var _a;
|
|
1790
|
-
(_a = this._socket.io) === null || _a === void 0 ? void 0 : _a.off(eventName, handler);
|
|
1791
|
-
};
|
|
1792
|
-
}
|
|
1793
|
-
once(eventName, handler) {
|
|
1794
|
-
this._socket.once(eventName, handler);
|
|
1795
|
-
}
|
|
1796
|
-
off(eventName, handler) {
|
|
1797
|
-
this._socket.off(eventName, handler);
|
|
1798
|
-
}
|
|
1799
|
-
_interceptEvent(eventName, handler) {
|
|
1800
|
-
if (this._reconnectManager) {
|
|
1801
|
-
this._reconnectManager.on(eventName, handler);
|
|
1192
|
+
if (left.mimeType.toLowerCase() === preferredCodec) {
|
|
1193
|
+
return -1;
|
|
1802
1194
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1195
|
+
if (left.mimeType.toLowerCase() === "video/rtx" &&
|
|
1196
|
+
((_a = left.parameters) === null || _a === void 0 ? void 0 : _a.apt) === preferredCodecEntry.preferredPayloadType) {
|
|
1197
|
+
if (right.mimeType.toLowerCase() === preferredCodec) {
|
|
1198
|
+
return 1;
|
|
1199
|
+
}
|
|
1200
|
+
return -1;
|
|
1201
|
+
}
|
|
1202
|
+
return 0;
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
function getPreferredOrder(availableCodecs, { av1On }) {
|
|
1206
|
+
availableCodecs.unshift("video/vp9");
|
|
1207
|
+
if (av1On) {
|
|
1208
|
+
availableCodecs.unshift("video/av1");
|
|
1807
1209
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1210
|
+
return availableCodecs;
|
|
1211
|
+
}
|
|
1212
|
+
function sortCodecsByMimeType(codecs, features) {
|
|
1213
|
+
const availableCodecs = codecs
|
|
1214
|
+
.map(({ mimeType }) => mimeType)
|
|
1215
|
+
.filter((value, index, array) => array.indexOf(value) === index);
|
|
1216
|
+
const preferredOrder = getPreferredOrder(availableCodecs, features);
|
|
1217
|
+
return codecs.sort((a, b) => {
|
|
1218
|
+
const indexA = preferredOrder.indexOf(a.mimeType.toLowerCase());
|
|
1219
|
+
const indexB = preferredOrder.indexOf(b.mimeType.toLowerCase());
|
|
1220
|
+
const orderA = indexA >= 0 ? indexA : Number.MAX_VALUE;
|
|
1221
|
+
const orderB = indexB >= 0 ? indexB : Number.MAX_VALUE;
|
|
1222
|
+
return orderA - orderB;
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
function getIsCodecDecodingPowerEfficient(codec) {
|
|
1226
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1227
|
+
const { powerEfficient } = yield navigator.mediaCapabilities.decodingInfo({
|
|
1228
|
+
type: "webrtc",
|
|
1229
|
+
video: {
|
|
1230
|
+
width: 1280,
|
|
1231
|
+
height: 720,
|
|
1232
|
+
bitrate: 2580,
|
|
1233
|
+
framerate: 24,
|
|
1234
|
+
contentType: codec,
|
|
1235
|
+
},
|
|
1236
|
+
});
|
|
1237
|
+
return powerEfficient;
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
function sortCodecsByPowerEfficiency(codecs) {
|
|
1241
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1242
|
+
let codecPowerEfficiencyEntries;
|
|
1243
|
+
try {
|
|
1244
|
+
codecPowerEfficiencyEntries = yield Promise.all(codecs.map(({ mimeType }) => getIsCodecDecodingPowerEfficient(mimeType).then((val) => [mimeType, val])));
|
|
1245
|
+
}
|
|
1246
|
+
catch (error) {
|
|
1247
|
+
logger$8.error(error);
|
|
1248
|
+
return codecs;
|
|
1249
|
+
}
|
|
1250
|
+
const codecPowerEfficiencies = Object.fromEntries(codecPowerEfficiencyEntries);
|
|
1251
|
+
const sorted = codecs.sort((a, b) => {
|
|
1252
|
+
const aPowerEfficient = codecPowerEfficiencies[a.mimeType];
|
|
1253
|
+
const bPowerEfficient = codecPowerEfficiencies[b.mimeType];
|
|
1254
|
+
return aPowerEfficient === bPowerEfficient ? 0 : aPowerEfficient ? -1 : 1;
|
|
1255
|
+
});
|
|
1256
|
+
return sorted;
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
function sortCodecs(codecs, features) {
|
|
1260
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1261
|
+
let sortedCodecs = sortCodecsByMimeType(codecs, features);
|
|
1262
|
+
sortedCodecs = yield sortCodecsByPowerEfficiency(codecs);
|
|
1263
|
+
return sortedCodecs;
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function captureCandidatePairInfoMetrics(cpMetrics, currentCptats, prevCptats, timeDiff, report) {
|
|
1268
|
+
const bytesReceivedDiff = currentCptats.bytesReceived - ((prevCptats === null || prevCptats === void 0 ? void 0 : prevCptats.bytesReceived) || 0);
|
|
1269
|
+
const bytesSentDiff = currentCptats.bytesSent - ((prevCptats === null || prevCptats === void 0 ? void 0 : prevCptats.bytesSent) || 0);
|
|
1270
|
+
cpMetrics.bitrateIn = (8000 * bytesReceivedDiff) / timeDiff;
|
|
1271
|
+
cpMetrics.bitrateOut = (8000 * bytesSentDiff) / timeDiff;
|
|
1272
|
+
cpMetrics.state = currentCptats.state;
|
|
1273
|
+
cpMetrics.roundTripTime = currentCptats.currentRoundTripTime;
|
|
1274
|
+
cpMetrics.requestsSent = currentCptats.requestsSent;
|
|
1275
|
+
cpMetrics.responsesReceived = currentCptats.responsesReceived;
|
|
1276
|
+
cpMetrics.requestsReceived = currentCptats.requestsReceived;
|
|
1277
|
+
cpMetrics.responsesSent = currentCptats.responsesSent;
|
|
1278
|
+
cpMetrics.availableOutgoingBitrate = currentCptats.availableOutgoingBitrate;
|
|
1279
|
+
cpMetrics.availableIncomingBitrate = currentCptats.availableIncomingBitrate;
|
|
1280
|
+
const remote = report.get(currentCptats.remoteCandidateId);
|
|
1281
|
+
const local = report.get(currentCptats.localCandidateId);
|
|
1282
|
+
cpMetrics.usingTurn = false;
|
|
1283
|
+
if (local) {
|
|
1284
|
+
if (/relay/i.test(local.candidateType || "")) {
|
|
1285
|
+
cpMetrics.usingTurn = true;
|
|
1286
|
+
cpMetrics.turnProtocol = local.relayProtocol;
|
|
1287
|
+
}
|
|
1811
1288
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1289
|
+
if (remote && local) {
|
|
1290
|
+
cpMetrics.localEp = `${local.protocol}:${local.address || local.ip}:${local.port} ${local.candidateType} (${local.networkType})`;
|
|
1291
|
+
cpMetrics.remoteEp = `${remote.protocol}:${remote.address || remote.ip}:${remote.port} ${remote.candidateType}`;
|
|
1815
1292
|
}
|
|
1816
1293
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1294
|
+
function captureSsrcInfo(ssrcMetrics, currentSsrcStats, prevSsrcStats, timeDiff, report) {
|
|
1295
|
+
if (ssrcMetrics.codec)
|
|
1296
|
+
return;
|
|
1297
|
+
ssrcMetrics.kind = currentSsrcStats.kind || currentSsrcStats.mediaType;
|
|
1298
|
+
ssrcMetrics.ssrc = currentSsrcStats.ssrc;
|
|
1299
|
+
ssrcMetrics.direction = currentSsrcStats.type === "inbound-rtp" ? "in" : "out";
|
|
1300
|
+
const codecStats = currentSsrcStats.codecId && report.get(currentSsrcStats.codecId);
|
|
1301
|
+
ssrcMetrics.codec =
|
|
1302
|
+
codecStats &&
|
|
1303
|
+
[
|
|
1304
|
+
codecStats.mimeType,
|
|
1305
|
+
codecStats.channels && `${codecStats.channels}ch`,
|
|
1306
|
+
codecStats.clockRate,
|
|
1307
|
+
codecStats.payloadType,
|
|
1308
|
+
currentSsrcStats.encoderImplementation || currentSsrcStats.decoderImplementation,
|
|
1309
|
+
codecStats.sdpFmtpLine,
|
|
1310
|
+
]
|
|
1311
|
+
.filter(Boolean)
|
|
1312
|
+
.join(" ");
|
|
1313
|
+
ssrcMetrics.mid = currentSsrcStats.mid;
|
|
1314
|
+
ssrcMetrics.rid = currentSsrcStats.rid;
|
|
1315
|
+
}
|
|
1316
|
+
function captureCommonSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, timeDiff, report) {
|
|
1317
|
+
const nackCountDiff = (currentSsrcStats.nackCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.nackCount) || 0);
|
|
1318
|
+
ssrcMetrics.nackCount = (ssrcMetrics.nackCount || 0) + nackCountDiff;
|
|
1319
|
+
ssrcMetrics.nackRate = (1000 * nackCountDiff) / timeDiff;
|
|
1320
|
+
if (ssrcMetrics.direction === "in") {
|
|
1321
|
+
const packetCountDiff = currentSsrcStats.packetsReceived - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.packetsReceived) || 0);
|
|
1322
|
+
ssrcMetrics.packetCount = (ssrcMetrics.packetCount || 0) + packetCountDiff;
|
|
1323
|
+
ssrcMetrics.packetRate = (1000 * packetCountDiff) / timeDiff;
|
|
1324
|
+
const packetLossCountDiff = currentSsrcStats.packetsLost - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.packetsLost) || 0);
|
|
1325
|
+
ssrcMetrics.packetLossCount = (ssrcMetrics.packetLossCount || 0) + packetLossCountDiff;
|
|
1326
|
+
ssrcMetrics.packetLossRate = (1000 * packetLossCountDiff) / timeDiff;
|
|
1327
|
+
ssrcMetrics.lossRatio = (1000 * (packetLossCountDiff / (packetLossCountDiff + packetCountDiff))) / timeDiff;
|
|
1328
|
+
const byteCountDiff = currentSsrcStats.bytesReceived - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.bytesReceived) || 0);
|
|
1329
|
+
ssrcMetrics.byteCount = (ssrcMetrics.byteCount || 0) + byteCountDiff;
|
|
1330
|
+
let headerByteCountDiff = 0;
|
|
1331
|
+
if (currentSsrcStats.headerBytesReceived) {
|
|
1332
|
+
headerByteCountDiff = currentSsrcStats.headerBytesReceived - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.headerBytesReceived) || 0);
|
|
1333
|
+
ssrcMetrics.headerByteCount = (ssrcMetrics.headerByteCount || 0) + headerByteCountDiff;
|
|
1334
|
+
}
|
|
1335
|
+
const totalBytesDiff = byteCountDiff + headerByteCountDiff;
|
|
1336
|
+
ssrcMetrics.bitrate = (8000 * totalBytesDiff) / timeDiff;
|
|
1337
|
+
ssrcMetrics.mediaRatio = byteCountDiff / totalBytesDiff;
|
|
1338
|
+
if (currentSsrcStats.kind === "audio") {
|
|
1339
|
+
const fecBytesDiff = (currentSsrcStats.fecBytesReceived || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.fecBytesReceived) || 0);
|
|
1340
|
+
ssrcMetrics.fecRatio = fecBytesDiff / byteCountDiff;
|
|
1341
|
+
}
|
|
1342
|
+
const jitterBufferDelayDiff = (currentSsrcStats.jitterBufferDelay || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.jitterBufferDelay) || 0);
|
|
1343
|
+
const jitterBufferEmittedDiff = (currentSsrcStats.jitterBufferEmittedCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.jitterBufferEmittedCount) || 0);
|
|
1344
|
+
ssrcMetrics.jitter = jitterBufferEmittedDiff ? jitterBufferDelayDiff / jitterBufferEmittedDiff : 0;
|
|
1821
1345
|
}
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1346
|
+
else {
|
|
1347
|
+
const packetCountDiff = currentSsrcStats.packetsSent - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.packetsSent) || 0);
|
|
1348
|
+
ssrcMetrics.packetCount = (ssrcMetrics.packetCount || 0) + packetCountDiff;
|
|
1349
|
+
ssrcMetrics.packetRate = (1000 * packetCountDiff) / timeDiff;
|
|
1350
|
+
const byteCountDiff = currentSsrcStats.bytesSent - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.bytesSent) || 0);
|
|
1351
|
+
ssrcMetrics.byteCount = (ssrcMetrics.byteCount || 0) + byteCountDiff;
|
|
1352
|
+
let headerByteCountDiff = 0;
|
|
1353
|
+
if (currentSsrcStats.headerBytesSent) {
|
|
1354
|
+
headerByteCountDiff = currentSsrcStats.headerBytesSent - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.headerBytesSent) || 0);
|
|
1355
|
+
ssrcMetrics.headerByteCount = (ssrcMetrics.headerByteCount || 0) + headerByteCountDiff;
|
|
1356
|
+
}
|
|
1357
|
+
const totalBytesDiff = byteCountDiff + headerByteCountDiff;
|
|
1358
|
+
ssrcMetrics.bitrate = (8000 * totalBytesDiff) / timeDiff;
|
|
1359
|
+
ssrcMetrics.mediaRatio = byteCountDiff / totalBytesDiff;
|
|
1360
|
+
const sendDelayDiff = currentSsrcStats.totalPacketSendDelay - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalPacketSendDelay) || 0);
|
|
1361
|
+
ssrcMetrics.sendDelay = sendDelayDiff / packetCountDiff;
|
|
1362
|
+
const retransDiff = currentSsrcStats.retransmittedPacketsSent - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.retransmittedPacketsSent) || 0);
|
|
1363
|
+
ssrcMetrics.retransRatio = (1000 * (retransDiff / packetCountDiff)) / timeDiff;
|
|
1364
|
+
if (currentSsrcStats.remoteId) {
|
|
1365
|
+
const remoteReport = report.get(currentSsrcStats.remoteId);
|
|
1366
|
+
if (remoteReport) {
|
|
1367
|
+
ssrcMetrics.roundTripTime = remoteReport.roundTripTime || 0;
|
|
1368
|
+
ssrcMetrics.jitter = remoteReport.jitter || 0;
|
|
1369
|
+
ssrcMetrics.fractionLost = remoteReport.fractionLost || 0;
|
|
1836
1370
|
}
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
};
|
|
1840
|
-
const external_stun_servers = (iceConfig, features) => {
|
|
1841
|
-
if (features.addGoogleStunServers) {
|
|
1842
|
-
iceConfig.iceServers = [
|
|
1843
|
-
{ urls: "stun:stun.l.google.com:19302" },
|
|
1844
|
-
{ urls: "stun:stun2.l.google.com:19302" },
|
|
1845
|
-
...iceConfig.iceServers,
|
|
1846
|
-
];
|
|
1371
|
+
}
|
|
1847
1372
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1373
|
+
}
|
|
1374
|
+
function captureAudioSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, timeDiff, report) {
|
|
1375
|
+
const packetsDiscardedDiff = currentSsrcStats.packetsDiscarded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.packetsDiscarded) || 0);
|
|
1376
|
+
ssrcMetrics.packetsDiscarded = (ssrcMetrics.packetsDiscarded || 0) + packetsDiscardedDiff;
|
|
1377
|
+
if (ssrcMetrics.direction === "in") {
|
|
1378
|
+
const totalAudioEnergyDiff = currentSsrcStats.totalAudioEnergy - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalAudioEnergy) || 0);
|
|
1379
|
+
const totalSamplesDurationDiff = currentSsrcStats.totalSamplesDuration - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalSamplesDuration) || 0);
|
|
1380
|
+
ssrcMetrics.audioLevel = Math.sqrt(totalAudioEnergyDiff / totalSamplesDurationDiff);
|
|
1381
|
+
const samples = currentSsrcStats.totalSamplesReceived || 0;
|
|
1382
|
+
const concealmentEvents = currentSsrcStats.concealmentEvents || 0;
|
|
1383
|
+
const samplesDiff = samples - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalSamplesReceived) || 0);
|
|
1384
|
+
const concealedSamples = currentSsrcStats.concealedSamples || 0;
|
|
1385
|
+
const concealedDiff = concealedSamples - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.concealedSamples) || 0);
|
|
1386
|
+
const silentConcealedDiff = (currentSsrcStats.silentConcealedSamples || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.silentConcealedSamples) || 0);
|
|
1387
|
+
const insDiff = (currentSsrcStats.insertedSamplesForDeceleration || 0) -
|
|
1388
|
+
((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.insertedSamplesForDeceleration) || 0);
|
|
1389
|
+
const remDiff = (currentSsrcStats.removedSamplesForAcceleration || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.removedSamplesForAcceleration) || 0);
|
|
1390
|
+
ssrcMetrics.audioSamples = samples;
|
|
1391
|
+
ssrcMetrics.audioConcealmentEvents = concealmentEvents;
|
|
1392
|
+
ssrcMetrics.audioConcealedSamples = concealedSamples;
|
|
1393
|
+
ssrcMetrics.audioConcealment = concealedDiff ? concealedDiff / samplesDiff : 0;
|
|
1394
|
+
ssrcMetrics.audioSilentConcealment = silentConcealedDiff ? silentConcealedDiff / samplesDiff : 0;
|
|
1395
|
+
ssrcMetrics.audioAcceleration = remDiff ? remDiff / samplesDiff : 0;
|
|
1396
|
+
ssrcMetrics.audioDeceleration = insDiff ? insDiff / samplesDiff : 0;
|
|
1854
1397
|
}
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1398
|
+
}
|
|
1399
|
+
function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, timeDiff, report) {
|
|
1400
|
+
ssrcMetrics.width = currentSsrcStats.frameWidth;
|
|
1401
|
+
ssrcMetrics.height = currentSsrcStats.frameHeight;
|
|
1402
|
+
ssrcMetrics.qualityLimitationReason = currentSsrcStats.qualityLimitationReason || "";
|
|
1403
|
+
const pliCountDiff = currentSsrcStats.pliCount - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.pliCount) || 0);
|
|
1404
|
+
ssrcMetrics.pliCount = (ssrcMetrics.pliCount || 0) + pliCountDiff;
|
|
1405
|
+
ssrcMetrics.pliRate = (1000 * pliCountDiff) / timeDiff;
|
|
1406
|
+
const firCountDiff = currentSsrcStats.firCount - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.firCount) || 0);
|
|
1407
|
+
ssrcMetrics.firCount = (ssrcMetrics.firCount || 0) + firCountDiff;
|
|
1408
|
+
ssrcMetrics.firRate = (1000 * firCountDiff) / timeDiff;
|
|
1409
|
+
if (ssrcMetrics.direction === "in") {
|
|
1410
|
+
const kfCountDiff = currentSsrcStats.keyFramesDecoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesDecoded) || 0);
|
|
1411
|
+
ssrcMetrics.kfCount = (ssrcMetrics.kfCount || 0) + kfCountDiff;
|
|
1412
|
+
ssrcMetrics.kfRate = (1000 * kfCountDiff) / timeDiff;
|
|
1413
|
+
const frameCountDiff = currentSsrcStats.framesDecoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.framesDecoded) || 0);
|
|
1414
|
+
ssrcMetrics.frameCount = (ssrcMetrics.frameCount || 0) + frameCountDiff;
|
|
1415
|
+
const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
|
|
1416
|
+
ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
|
|
1417
|
+
ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
|
|
1871
1418
|
}
|
|
1872
1419
|
else {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
const
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
let subdomain = "";
|
|
1891
|
-
let domain = host;
|
|
1892
|
-
let subdomainSeparator = ".";
|
|
1893
|
-
for (const { separator, pattern } of subdomainPatterns) {
|
|
1894
|
-
const match = pattern.exec(host);
|
|
1895
|
-
if (match) {
|
|
1896
|
-
subdomain = match[1] || "";
|
|
1897
|
-
domain = match[2];
|
|
1898
|
-
subdomainSeparator = separator;
|
|
1899
|
-
break;
|
|
1420
|
+
const kfCountDiff = currentSsrcStats.keyFramesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesEncoded) || 0);
|
|
1421
|
+
ssrcMetrics.kfCount = (ssrcMetrics.kfCount || 0) + kfCountDiff;
|
|
1422
|
+
ssrcMetrics.kfRate = (1000 * kfCountDiff) / timeDiff;
|
|
1423
|
+
const frameCountDiff = currentSsrcStats.framesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.framesEncoded) || 0);
|
|
1424
|
+
ssrcMetrics.frameCount = (ssrcMetrics.frameCount || 0) + frameCountDiff;
|
|
1425
|
+
const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
|
|
1426
|
+
ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
|
|
1427
|
+
ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
|
|
1428
|
+
const encodeTimeDiff = currentSsrcStats.totalEncodeTime - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.totalEncodeTime) || 0);
|
|
1429
|
+
ssrcMetrics.encodeTime = encodeTimeDiff / frameCountDiff;
|
|
1430
|
+
if (currentSsrcStats.mediaSourceId) {
|
|
1431
|
+
const mediaSourceReport = report.get(currentSsrcStats.mediaSourceId);
|
|
1432
|
+
if (mediaSourceReport) {
|
|
1433
|
+
ssrcMetrics.sourceWidth = mediaSourceReport.width || 0;
|
|
1434
|
+
ssrcMetrics.sourceHeight = mediaSourceReport.height || 0;
|
|
1435
|
+
ssrcMetrics.sourceFps = mediaSourceReport.framesPerSecond || 0;
|
|
1436
|
+
}
|
|
1900
1437
|
}
|
|
1901
1438
|
}
|
|
1902
|
-
const organizationDomain = !subdomain ? domain : `${subdomain}${subdomainSeparator}${domain}`;
|
|
1903
|
-
return {
|
|
1904
|
-
domain,
|
|
1905
|
-
domainWithSeparator: `${subdomainSeparator}${domain}`,
|
|
1906
|
-
organizationDomain,
|
|
1907
|
-
organization: `${protocol}//${organizationDomain}`,
|
|
1908
|
-
service: `${protocol}//${domain}`,
|
|
1909
|
-
subdomain,
|
|
1910
|
-
};
|
|
1911
1439
|
}
|
|
1912
|
-
fromLocation(window && window.location);
|
|
1913
1440
|
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1441
|
+
let peerConnections = [];
|
|
1442
|
+
let peerConnectionCounter = 0;
|
|
1443
|
+
const peerConnectionData = new WeakMap();
|
|
1444
|
+
const removePeerConnection = (pc) => {
|
|
1445
|
+
peerConnections = peerConnections.filter((old) => old !== pc);
|
|
1446
|
+
};
|
|
1447
|
+
if (window.RTCPeerConnection) {
|
|
1448
|
+
const OriginalRTCPeerConnection = window.RTCPeerConnection;
|
|
1449
|
+
function PatchedRTCPeerConnection(rtcConfig) {
|
|
1450
|
+
const pc = new OriginalRTCPeerConnection(rtcConfig);
|
|
1451
|
+
peerConnections.push(pc);
|
|
1452
|
+
peerConnectionData.set(pc, { index: peerConnectionCounter++ });
|
|
1453
|
+
const onConnectionStateChange = () => {
|
|
1454
|
+
if (pc.connectionState === "closed") {
|
|
1455
|
+
removePeerConnection(pc);
|
|
1456
|
+
pc.removeEventListener("connectionstatechange", onConnectionStateChange);
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
pc.addEventListener("connectionstatechange", onConnectionStateChange);
|
|
1460
|
+
return pc;
|
|
1920
1461
|
}
|
|
1921
|
-
|
|
1462
|
+
PatchedRTCPeerConnection.prototype = OriginalRTCPeerConnection.prototype;
|
|
1463
|
+
window.RTCPeerConnection = PatchedRTCPeerConnection;
|
|
1922
1464
|
}
|
|
1465
|
+
const getCurrentPeerConnections = () => peerConnections;
|
|
1466
|
+
const getPeerConnectionIndex = (pc) => { var _a; return (_a = peerConnectionData.get(pc)) === null || _a === void 0 ? void 0 : _a.index; };
|
|
1467
|
+
const setPeerConnectionsForTests = (pcs) => (peerConnections = pcs);
|
|
1923
1468
|
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1469
|
+
const PC_DATA_BY_PC = new WeakMap();
|
|
1470
|
+
let numMissingTrackSsrcLookups = 0;
|
|
1471
|
+
let numFailedTrackSsrcLookups = 0;
|
|
1472
|
+
const getPeerConnectionsWithStatsReports = (pcDataByPc = PC_DATA_BY_PC) => Promise.all(getCurrentPeerConnections().map((pc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1473
|
+
let pcData = pcDataByPc.get(pc);
|
|
1474
|
+
if (!pcData) {
|
|
1475
|
+
pcData = { ssrcToTrackId: {} };
|
|
1476
|
+
pcDataByPc.set(pc, pcData);
|
|
1477
|
+
}
|
|
1478
|
+
try {
|
|
1479
|
+
const report = yield pc.getStats();
|
|
1480
|
+
let missingSsrcs = null;
|
|
1481
|
+
report.forEach((stats) => {
|
|
1482
|
+
if (stats.type === "inbound-rtp" || stats.type === "outbound-rtp") {
|
|
1483
|
+
if (!stats.trackIdentifier && !pcData.ssrcToTrackId[stats.ssrc]) {
|
|
1484
|
+
if (stats.mediaSourceId) {
|
|
1485
|
+
const mediaSourceStats = report.get(stats.mediaSourceId);
|
|
1486
|
+
if (mediaSourceStats) {
|
|
1487
|
+
if (mediaSourceStats.trackIdentifier) {
|
|
1488
|
+
pcData.ssrcToTrackId[stats.ssrc] = mediaSourceStats.trackIdentifier;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if (!pcData.ssrcToTrackId[stats.ssrc] && stats.trackId) {
|
|
1493
|
+
const deprecatedTrackStats = report.get(stats.trackId);
|
|
1494
|
+
if (deprecatedTrackStats) {
|
|
1495
|
+
if (deprecatedTrackStats.trackIdentifier) {
|
|
1496
|
+
pcData.ssrcToTrackId[stats.ssrc] = deprecatedTrackStats.trackIdentifier;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
if (!pcData.ssrcToTrackId[stats.ssrc]) {
|
|
1501
|
+
if (!missingSsrcs)
|
|
1502
|
+
missingSsrcs = [];
|
|
1503
|
+
missingSsrcs.push(stats.ssrc);
|
|
1504
|
+
}
|
|
1958
1505
|
}
|
|
1959
1506
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1507
|
+
});
|
|
1508
|
+
if (missingSsrcs) {
|
|
1509
|
+
const sendersAndReceivers = [...pc.getSenders(), ...pc.getReceivers()];
|
|
1510
|
+
const reports = yield Promise.all(sendersAndReceivers.map((o) => o.getStats()));
|
|
1511
|
+
reports.forEach((tReport, index) => {
|
|
1512
|
+
tReport.forEach((stats) => {
|
|
1513
|
+
if (stats.type === "inbound-rtp" || stats.type === "outbound-rtp") {
|
|
1514
|
+
pcData.ssrcToTrackId[stats.ssrc] = sendersAndReceivers[index].track.id;
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1517
|
+
});
|
|
1518
|
+
missingSsrcs.forEach((ssrc) => {
|
|
1519
|
+
numMissingTrackSsrcLookups++;
|
|
1520
|
+
if (!pcData.ssrcToTrackId[ssrc]) {
|
|
1521
|
+
pcData.ssrcToTrackId[ssrc] = "?" + ssrc;
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1967
1524
|
}
|
|
1525
|
+
return [pc, report, pcData];
|
|
1968
1526
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1527
|
+
catch (e) {
|
|
1528
|
+
rtcStats.sendEvent("trackSsrcLookupFailed", {
|
|
1529
|
+
name: e === null || e === void 0 ? void 0 : e.name,
|
|
1530
|
+
cause: e === null || e === void 0 ? void 0 : e.cause,
|
|
1531
|
+
message: e === null || e === void 0 ? void 0 : e.message,
|
|
1532
|
+
});
|
|
1533
|
+
numFailedTrackSsrcLookups++;
|
|
1534
|
+
return [pc, [], pcData];
|
|
1976
1535
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
return false;
|
|
1536
|
+
})));
|
|
1537
|
+
|
|
1538
|
+
const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, trackId, ssrc) => {
|
|
1539
|
+
let viewStats = statsByView[clientId];
|
|
1540
|
+
if (!viewStats) {
|
|
1541
|
+
viewStats = { candidatePairs: {}, tracks: {}, startTime: time, updated: time };
|
|
1542
|
+
statsByView[clientId] = viewStats;
|
|
1985
1543
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1544
|
+
viewStats.updated = time;
|
|
1545
|
+
let trackStats = viewStats.tracks[trackId];
|
|
1546
|
+
if (!trackStats) {
|
|
1547
|
+
trackStats = { ssrcs: {}, startTime: time, updated: time };
|
|
1548
|
+
viewStats.tracks[trackId] = trackStats;
|
|
1549
|
+
}
|
|
1550
|
+
trackStats.updated = time;
|
|
1551
|
+
let ssrcStats = trackStats.ssrcs[ssrc];
|
|
1552
|
+
if (!ssrcStats) {
|
|
1553
|
+
ssrcStats = {
|
|
1554
|
+
startTime: time,
|
|
1555
|
+
updated: time,
|
|
1556
|
+
pcIndex,
|
|
1557
|
+
};
|
|
1558
|
+
trackStats.ssrcs[ssrc] = ssrcStats;
|
|
1995
1559
|
}
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
1560
|
+
ssrcStats.updated = time;
|
|
1561
|
+
return ssrcStats;
|
|
1562
|
+
};
|
|
1563
|
+
const removeNonUpdatedStats = (statsByView, time) => {
|
|
1564
|
+
Object.entries(statsByView).forEach(([viewId, viewStats]) => {
|
|
1565
|
+
if (viewStats.updated !== undefined && viewStats.updated < time) {
|
|
1566
|
+
delete statsByView[viewId];
|
|
2002
1567
|
}
|
|
2003
1568
|
else {
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
if (!history.currPeriod || !history.prevPeriod)
|
|
2013
|
-
throw new Error("missing period timestamps");
|
|
2014
|
-
const prevPeriodCenter = this.calculatePeriodCenterTimestamp(history.prevPeriod);
|
|
2015
|
-
const currPeriodCenter = this.calculatePeriodCenterTimestamp(history.currPeriod);
|
|
2016
|
-
const currIntervalInMs = currPeriodCenter - prevPeriodCenter;
|
|
2017
|
-
if (history.prevIntervalInMs) {
|
|
2018
|
-
const intervalDiffInMs = Math.abs(history.prevIntervalInMs - currIntervalInMs);
|
|
2019
|
-
history.hasPeriodicPacketLoss = intervalDiffInMs < this.INTERVAL_DIFF_THRESHOLD_MS;
|
|
2020
|
-
logger$9.debug("addNewPacketLossInterval() [hasPeriodicPacketLoss: %s, intervalDiffInMs: %s]", intervalDiffInMs < this.INTERVAL_DIFF_THRESHOLD_MS, intervalDiffInMs);
|
|
2021
|
-
}
|
|
2022
|
-
history.prevIntervalInMs = currIntervalInMs;
|
|
2023
|
-
history.prevPeriod = history.currPeriod;
|
|
2024
|
-
}
|
|
2025
|
-
calculatePeriodCenterTimestamp(period) {
|
|
2026
|
-
if (!period.end)
|
|
2027
|
-
throw new Error("Missing period end timestamp");
|
|
2028
|
-
return period.begin + (period.end - period.begin) / 2;
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
var _a$5, _b$2;
|
|
2033
|
-
const adapter$5 = (_a$5 = adapterRaw.default) !== null && _a$5 !== void 0 ? _a$5 : adapterRaw;
|
|
2034
|
-
const logger$8 = new Logger();
|
|
2035
|
-
const browserName$2 = (_b$2 = adapter$5.browserDetails) === null || _b$2 === void 0 ? void 0 : _b$2.browser;
|
|
2036
|
-
const browserVersion$1 = adapter$5.browserDetails.version;
|
|
2037
|
-
function setCodecPreferenceSDP({ sdp, redOn, incrementAnalyticMetric }) {
|
|
2038
|
-
var _a, _b;
|
|
2039
|
-
try {
|
|
2040
|
-
const sdpObject = sdpTransform__namespace.parse(sdp);
|
|
2041
|
-
if (Array.isArray(sdpObject === null || sdpObject === void 0 ? void 0 : sdpObject.media)) {
|
|
2042
|
-
const mediaAudio = sdpObject.media.find((m) => m.type === "audio");
|
|
2043
|
-
if (Array.isArray(mediaAudio === null || mediaAudio === void 0 ? void 0 : mediaAudio.rtp)) {
|
|
2044
|
-
const rtp = mediaAudio.rtp;
|
|
2045
|
-
for (let i = 0; i < rtp.length; i++) {
|
|
2046
|
-
if (redOn && rtp[i].codec === "red") {
|
|
2047
|
-
const payloads = (_a = mediaAudio.payloads) === null || _a === void 0 ? void 0 : _a.split(" ");
|
|
2048
|
-
const pt = payloads === null || payloads === void 0 ? void 0 : payloads.indexOf("" + rtp[i].payload);
|
|
2049
|
-
if (pt && pt !== -1 && pt >= 0) {
|
|
2050
|
-
payloads === null || payloads === void 0 ? void 0 : payloads.unshift(payloads.splice(pt, 1)[0]);
|
|
2051
|
-
mediaAudio.payloads = payloads === null || payloads === void 0 ? void 0 : payloads.join(" ");
|
|
1569
|
+
Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
|
|
1570
|
+
if (trackStats.updated < time) {
|
|
1571
|
+
delete viewStats.tracks[trackId];
|
|
1572
|
+
}
|
|
1573
|
+
else {
|
|
1574
|
+
Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
|
|
1575
|
+
if (ssrcStats.updated < time) {
|
|
1576
|
+
delete trackStats.ssrcs[ssrc];
|
|
2052
1577
|
}
|
|
2053
|
-
}
|
|
1578
|
+
});
|
|
2054
1579
|
}
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1583
|
+
};
|
|
1584
|
+
const DEFAULT_CLIENT = {
|
|
1585
|
+
id: "unknown",
|
|
1586
|
+
clientId: "unknown",
|
|
1587
|
+
audio: { enabled: false, track: undefined },
|
|
1588
|
+
video: { enabled: false, track: undefined },
|
|
1589
|
+
isAudioOnlyModeEnabled: false,
|
|
1590
|
+
isLocalClient: true,
|
|
1591
|
+
isPresentation: false,
|
|
1592
|
+
};
|
|
1593
|
+
function collectStats(state_1, _a, immediate_1) {
|
|
1594
|
+
return __awaiter(this, arguments, void 0, function* (state, { logger, interval }, immediate) {
|
|
1595
|
+
const collectStatsBound = collectStats.bind(null, state, { interval, logger });
|
|
1596
|
+
try {
|
|
1597
|
+
const clients = state.getClients();
|
|
1598
|
+
const defaultClient = clients.find((c) => c.isLocalClient && !c.isPresentation) || DEFAULT_CLIENT;
|
|
1599
|
+
let defaultViewStats = state.statsByView[defaultClient.id];
|
|
1600
|
+
if (!defaultViewStats) {
|
|
1601
|
+
defaultViewStats = { tracks: {}, candidatePairs: {}, pressure: null };
|
|
1602
|
+
state.statsByView[defaultClient.id] = defaultViewStats;
|
|
2055
1603
|
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
1604
|
+
defaultViewStats.pressure = state.lastPressureObserverRecord;
|
|
1605
|
+
const timeSinceLastUpdate = Date.now() - state.lastUpdateTime;
|
|
1606
|
+
if (timeSinceLastUpdate < 400) {
|
|
1607
|
+
if (immediate)
|
|
1608
|
+
return state.statsByView;
|
|
1609
|
+
state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
|
|
1610
|
+
state.nextTimeout = setTimeout(collectStatsBound, interval);
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
state.lastUpdateTime = Date.now();
|
|
1614
|
+
(yield getPeerConnectionsWithStatsReports()).forEach(([pc, report, pcData]) => {
|
|
1615
|
+
const pcIndex = getPeerConnectionIndex(pc);
|
|
1616
|
+
if (pcIndex === undefined) {
|
|
1617
|
+
logger.warn("Could not find index for PeerConnection");
|
|
1618
|
+
}
|
|
1619
|
+
if (pc.connectionState === "closed") {
|
|
1620
|
+
report = new Map();
|
|
1621
|
+
removePeerConnection(pc);
|
|
1622
|
+
}
|
|
1623
|
+
pcData.previousSSRCs = pcData.currentSSRCs || {};
|
|
1624
|
+
pcData.currentSSRCs = {};
|
|
1625
|
+
report.forEach((currentRtcStats) => {
|
|
1626
|
+
var _a, _b;
|
|
1627
|
+
if (currentRtcStats.type === "candidate-pair" && /inprogress|succeeded/.test(currentRtcStats.state)) {
|
|
1628
|
+
const prevRtcStats = (_a = pcData._oldReport) === null || _a === void 0 ? void 0 : _a.get(currentRtcStats.id);
|
|
1629
|
+
const timeDiff = prevRtcStats ? currentRtcStats.timestamp - prevRtcStats.timestamp : interval;
|
|
1630
|
+
const cpId = pcIndex + ":" + currentRtcStats.id;
|
|
1631
|
+
let cpMetrics = defaultViewStats.candidatePairs[cpId];
|
|
1632
|
+
if (!cpMetrics) {
|
|
1633
|
+
cpMetrics = { startTime: state.lastUpdateTime, id: cpId };
|
|
1634
|
+
defaultViewStats.candidatePairs[cpId] = cpMetrics;
|
|
1635
|
+
}
|
|
1636
|
+
captureCandidatePairInfoMetrics(cpMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1637
|
+
cpMetrics.lastRtcStatsTime = state.lastUpdateTime;
|
|
1638
|
+
}
|
|
1639
|
+
if (currentRtcStats.type === "inbound-rtp" || currentRtcStats.type === "outbound-rtp") {
|
|
1640
|
+
const kind = (currentRtcStats.mediaType || currentRtcStats.kind);
|
|
1641
|
+
const ssrc = currentRtcStats.ssrc;
|
|
1642
|
+
let trackId = currentRtcStats.trackIdentifier || pcData.ssrcToTrackId[ssrc];
|
|
1643
|
+
let prevRtcStats = (_b = pcData._oldReport) === null || _b === void 0 ? void 0 : _b.get(currentRtcStats.id);
|
|
1644
|
+
if (prevRtcStats && prevRtcStats.mediaSourceId !== currentRtcStats.mediaSourceId) {
|
|
1645
|
+
const mediaSourceStats = report.get(currentRtcStats.mediaSourceId);
|
|
1646
|
+
if (mediaSourceStats && mediaSourceStats.trackIdentifier) {
|
|
1647
|
+
trackId = mediaSourceStats.trackIdentifier;
|
|
1648
|
+
pcData.ssrcToTrackId[ssrc] = trackId;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
const client = clients.find((c) => { var _a; return ((_a = c[kind].track) === null || _a === void 0 ? void 0 : _a.id) === trackId; }) || defaultClient;
|
|
1652
|
+
const clientTrack = client[kind].track;
|
|
1653
|
+
if (!currentRtcStats.trackIdentifier &&
|
|
1654
|
+
pcData.ssrcToTrackId[ssrc] &&
|
|
1655
|
+
(clientTrack === null || clientTrack === void 0 ? void 0 : clientTrack.id) &&
|
|
1656
|
+
clientTrack.id !== pcData.ssrcToTrackId[ssrc]) {
|
|
1657
|
+
trackId = clientTrack.id;
|
|
1658
|
+
pcData.ssrcToTrackId[ssrc] = clientTrack.id;
|
|
1659
|
+
}
|
|
1660
|
+
pcData.currentSSRCs[ssrc] = client.id;
|
|
1661
|
+
if (prevRtcStats) {
|
|
1662
|
+
const newTransport = report.get(currentRtcStats.transportId);
|
|
1663
|
+
const oldTransport = pcData._oldReport.get(prevRtcStats.transportId);
|
|
1664
|
+
if (oldTransport &&
|
|
1665
|
+
(newTransport === null || newTransport === void 0 ? void 0 : newTransport.selectedCandidatePairId) !== oldTransport.selectedCandidatePairId) {
|
|
1666
|
+
prevRtcStats = null;
|
|
1667
|
+
}
|
|
1668
|
+
else if (currentRtcStats.bytesReceived < prevRtcStats.bytesReceived ||
|
|
1669
|
+
currentRtcStats.bytesSent < prevRtcStats.bytesSent) {
|
|
1670
|
+
prevRtcStats = null;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
const timeDiff = prevRtcStats ? currentRtcStats.timestamp - prevRtcStats.timestamp : interval;
|
|
1674
|
+
const ssrcMetrics = getOrCreateSsrcMetricsContainer(state.statsByView, state.lastUpdateTime, pcIndex, client.id, trackId, ssrc);
|
|
1675
|
+
captureSsrcInfo(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1676
|
+
captureCommonSsrcMetrics(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1677
|
+
if (kind === "video") {
|
|
1678
|
+
captureVideoSsrcMetrics(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1679
|
+
}
|
|
1680
|
+
if (kind === "audio") {
|
|
1681
|
+
captureAudioSsrcMetrics(ssrcMetrics, currentRtcStats, prevRtcStats, timeDiff, report);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
});
|
|
1685
|
+
pcData._oldReport = report;
|
|
1686
|
+
Object.keys(pcData.previousSSRCs)
|
|
1687
|
+
.filter((ssrc) => !pcData.currentSSRCs[ssrc])
|
|
1688
|
+
.forEach((ssrc) => {
|
|
1689
|
+
const clientId = pcData.previousSSRCs[ssrc];
|
|
1690
|
+
if (clientId) {
|
|
1691
|
+
const clientView = state.statsByView[clientId];
|
|
1692
|
+
if (clientView) {
|
|
1693
|
+
Object.values(clientView.tracks).forEach((trackStats) => {
|
|
1694
|
+
if (trackStats.ssrcs[ssrc]) {
|
|
1695
|
+
delete trackStats.ssrcs[ssrc];
|
|
1696
|
+
}
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
});
|
|
1702
|
+
removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
|
|
1703
|
+
Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
|
|
1704
|
+
const active = cp.lastRtcStatsTime === state.lastUpdateTime;
|
|
1705
|
+
cp.active = active;
|
|
1706
|
+
if (!active) {
|
|
1707
|
+
cp.state = "old/inactive";
|
|
1708
|
+
if (!cp.inactiveFromTime)
|
|
1709
|
+
cp.inactiveFromTime = state.lastUpdateTime;
|
|
1710
|
+
else {
|
|
1711
|
+
if (state.lastUpdateTime - cp.inactiveFromTime > 4000) {
|
|
1712
|
+
delete defaultViewStats.candidatePairs[cpKey];
|
|
2066
1713
|
}
|
|
2067
1714
|
}
|
|
2068
1715
|
}
|
|
1716
|
+
});
|
|
1717
|
+
if (immediate) {
|
|
1718
|
+
return state.statsByView;
|
|
1719
|
+
}
|
|
1720
|
+
else {
|
|
1721
|
+
state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
|
|
2069
1722
|
}
|
|
2070
1723
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
1724
|
+
catch (e) {
|
|
1725
|
+
logger.warn(e);
|
|
1726
|
+
state.numFailedStatsReports++;
|
|
1727
|
+
rtcStats.sendEvent("collectStatsFailed", {
|
|
1728
|
+
name: e === null || e === void 0 ? void 0 : e.name,
|
|
1729
|
+
cause: e === null || e === void 0 ? void 0 : e.cause,
|
|
1730
|
+
message: e === null || e === void 0 ? void 0 : e.message,
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
state.nextTimeout = setTimeout(collectStatsBound, interval);
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
const REGISTERED_TRIALS = {};
|
|
1738
|
+
function registerOriginTrials(trials, registeredTrials = REGISTERED_TRIALS, document = window.document) {
|
|
1739
|
+
trials.forEach(({ hostnamePattern, token }) => {
|
|
1740
|
+
const key = `${hostnamePattern}-${token}`;
|
|
1741
|
+
if (registeredTrials[key]) {
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
if (hostnamePattern.test(document.location.hostname)) {
|
|
1745
|
+
const otMeta = document.createElement("meta");
|
|
1746
|
+
otMeta.httpEquiv = "origin-trial";
|
|
1747
|
+
otMeta.content = token;
|
|
1748
|
+
document.head.append(otMeta);
|
|
1749
|
+
registeredTrials[key] = true;
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
const CPU_OBSERVER_OPTIONS = {
|
|
1755
|
+
sampleRate: 1,
|
|
1756
|
+
originTrials: [
|
|
1757
|
+
{
|
|
1758
|
+
hostnamePattern: /hereby\.dev/,
|
|
1759
|
+
token: "AkSNPHJw6EK08X0QU7kORnK9NABzRLAC7dqfXOwk5JwFAQG4Ey7WxLXxsnhieCgC1QHUdevE2EFICy7uBGDANwUAAABqeyJvcmlnaW4iOiJodHRwczovL2hlcmVieS5kZXY6NDQ0MyIsImZlYXR1cmUiOiJDb21wdXRlUHJlc3N1cmVfdjIiLCJleHBpcnkiOjE3MTY5NDA3OTksImlzU3ViZG9tYWluIjp0cnVlfQ==",
|
|
1760
|
+
},
|
|
1761
|
+
{
|
|
1762
|
+
hostnamePattern: /whereby\.com/,
|
|
1763
|
+
token: "Asc2wu8KpSx648i932NICteQDFcB05yl2QUUSHD7AQo8JGP2Fp6FF91TvYVJBsKGzLMH349rysPw5q9tqPC/PAUAAABqeyJvcmlnaW4iOiJodHRwczovL3doZXJlYnkuY29tOjQ0MyIsImZlYXR1cmUiOiJDb21wdXRlUHJlc3N1cmVfdjIiLCJleHBpcnkiOjE3MTY5NDA3OTksImlzU3ViZG9tYWluIjp0cnVlfQ==",
|
|
1764
|
+
},
|
|
1765
|
+
],
|
|
1766
|
+
};
|
|
1767
|
+
function startCpuObserver(cb, { sampleRate, originTrials } = CPU_OBSERVER_OPTIONS, window = globalThis.window) {
|
|
1768
|
+
registerOriginTrials(originTrials);
|
|
1769
|
+
let pressureObserver;
|
|
1770
|
+
if ("PressureObserver" in window) {
|
|
1771
|
+
pressureObserver = new window.PressureObserver(cb, { sampleRate });
|
|
1772
|
+
pressureObserver.observe("cpu", { sampleInterval: sampleRate * 1000 });
|
|
1773
|
+
return {
|
|
1774
|
+
stop: () => {
|
|
1775
|
+
pressureObserver.unobserve("cpu");
|
|
1776
|
+
},
|
|
1777
|
+
};
|
|
2079
1778
|
}
|
|
2080
1779
|
}
|
|
2081
|
-
|
|
1780
|
+
|
|
1781
|
+
const STATE = {
|
|
1782
|
+
currentMonitor: null,
|
|
1783
|
+
getClients: () => [],
|
|
1784
|
+
lastUpdateTime: 0,
|
|
1785
|
+
statsByView: {},
|
|
1786
|
+
subscriptions: [],
|
|
1787
|
+
numFailedStatsReports: 0,
|
|
1788
|
+
};
|
|
1789
|
+
const OPTIONS = {
|
|
1790
|
+
interval: 2000,
|
|
1791
|
+
logger: new Logger(),
|
|
1792
|
+
};
|
|
1793
|
+
const getStats = () => {
|
|
1794
|
+
return Object.assign({}, STATE.statsByView);
|
|
1795
|
+
};
|
|
1796
|
+
const getNumFailedStatsReports = () => {
|
|
1797
|
+
return STATE.numFailedStatsReports;
|
|
1798
|
+
};
|
|
1799
|
+
const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
|
|
1800
|
+
const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
|
|
1801
|
+
const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
|
|
1802
|
+
const setClientProvider = (provider) => (STATE.getClients = provider);
|
|
1803
|
+
function startStatsMonitor(state, { interval, logger }) {
|
|
1804
|
+
const collectStatsBound = collectStats.bind(null, state, { interval, logger });
|
|
1805
|
+
let cpuObserver;
|
|
2082
1806
|
try {
|
|
2083
|
-
|
|
2084
|
-
sdpObject.media.forEach((mediaObject) => {
|
|
2085
|
-
const usedPayloads = {};
|
|
2086
|
-
if (mediaObject.payloads)
|
|
2087
|
-
mediaObject.payloads = ("" + mediaObject.payloads)
|
|
2088
|
-
.split(" ")
|
|
2089
|
-
.filter((p) => !usedPayloads[p] && (usedPayloads[p] = true))
|
|
2090
|
-
.join(" ");
|
|
2091
|
-
const usedRtps = {};
|
|
2092
|
-
mediaObject.rtp = mediaObject.rtp.filter((p) => !p.payload || (usedPayloads[p.payload] && !usedRtps[p.payload] && (usedRtps[p.payload] = true)));
|
|
2093
|
-
const usedFmtps = {};
|
|
2094
|
-
if (mediaObject.fmtp)
|
|
2095
|
-
mediaObject.fmtp = mediaObject.fmtp.filter((p) => !p.payload ||
|
|
2096
|
-
(usedPayloads[p.payload] && !usedFmtps[p.payload] && (usedFmtps[p.payload] = true)));
|
|
2097
|
-
const usedRtcpFb = {};
|
|
2098
|
-
if (mediaObject.rtcpFb)
|
|
2099
|
-
mediaObject.rtcpFb = mediaObject.rtcpFb.filter((p) => !p.payload ||
|
|
2100
|
-
(usedPayloads[p.payload] &&
|
|
2101
|
-
!usedRtcpFb[p.payload + p.type + p.subtype] &&
|
|
2102
|
-
(usedRtcpFb[p.payload + p.type + p.subtype] = true)));
|
|
2103
|
-
});
|
|
2104
|
-
return sdpTransform__namespace.write(sdpObject);
|
|
1807
|
+
cpuObserver = startCpuObserver((records) => (state.lastPressureObserverRecord = records.pop()));
|
|
2105
1808
|
}
|
|
2106
|
-
catch (
|
|
2107
|
-
|
|
2108
|
-
}
|
|
2109
|
-
function deprioritizeH264(sdp) {
|
|
2110
|
-
return SDPUtils.splitSections(sdp)
|
|
2111
|
-
.map((section) => {
|
|
2112
|
-
if (SDPUtils.getKind(section) !== "video")
|
|
2113
|
-
return section;
|
|
2114
|
-
const h264payloadTypes = SDPUtils.matchPrefix(section, "a=rtpmap:")
|
|
2115
|
-
.map((line) => SDPUtils.parseRtpMap(line))
|
|
2116
|
-
.filter((codec) => /h264/i.test(codec.name))
|
|
2117
|
-
.map((codec) => "" + codec.payloadType);
|
|
2118
|
-
if (!h264payloadTypes.length)
|
|
2119
|
-
return section;
|
|
2120
|
-
const mline = SDPUtils.matchPrefix(section, "m=video")[0];
|
|
2121
|
-
const mlinePayloadsSectionExec = /(\s\d+)+$/i.exec(mline);
|
|
2122
|
-
const mlinePayloadsSection = mlinePayloadsSectionExec ? mlinePayloadsSectionExec[0] : "";
|
|
2123
|
-
const mlinePayloadsNonH264 = mlinePayloadsSection
|
|
2124
|
-
.split(" ")
|
|
2125
|
-
.filter((payloadType) => payloadType && !h264payloadTypes.includes(payloadType));
|
|
2126
|
-
const reorderedPayloads = [...mlinePayloadsNonH264, ...h264payloadTypes].join(" ");
|
|
2127
|
-
const newmline = mline.replace(mlinePayloadsSection, " " + reorderedPayloads);
|
|
2128
|
-
return section.replace(mline, newmline);
|
|
2129
|
-
})
|
|
2130
|
-
.join("");
|
|
2131
|
-
}
|
|
2132
|
-
function filterMidExtension(sdp) {
|
|
2133
|
-
if (browserName$2 !== "firefox" || (browserVersion$1 && browserVersion$1 >= 63) || browserVersion$1 === 60) {
|
|
2134
|
-
return sdp;
|
|
1809
|
+
catch (ex) {
|
|
1810
|
+
logger.warn("Failed to observe CPU pressure", ex);
|
|
2135
1811
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
return true;
|
|
2140
|
-
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
1812
|
+
setTimeout(collectStatsBound, interval);
|
|
1813
|
+
return {
|
|
1814
|
+
getUpdatedStats: () => {
|
|
1815
|
+
return collectStatsBound(true);
|
|
1816
|
+
},
|
|
1817
|
+
stop: () => {
|
|
1818
|
+
clearTimeout(state.nextTimeout);
|
|
1819
|
+
cpuObserver === null || cpuObserver === void 0 ? void 0 : cpuObserver.stop();
|
|
1820
|
+
},
|
|
1821
|
+
};
|
|
2145
1822
|
}
|
|
2146
|
-
function
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
return
|
|
2151
|
-
|
|
2152
|
-
|
|
1823
|
+
function subscribeStats(subscription, options = OPTIONS, state = STATE) {
|
|
1824
|
+
state.subscriptions.push(subscription);
|
|
1825
|
+
if (!state.currentMonitor)
|
|
1826
|
+
state.currentMonitor = startStatsMonitor(state, options);
|
|
1827
|
+
return {
|
|
1828
|
+
stop() {
|
|
1829
|
+
var _a;
|
|
1830
|
+
state.subscriptions = state.subscriptions.filter((s) => s !== subscription);
|
|
1831
|
+
if (!state.subscriptions.length) {
|
|
1832
|
+
(_a = state.currentMonitor) === null || _a === void 0 ? void 0 : _a.stop();
|
|
1833
|
+
state.currentMonitor = null;
|
|
1834
|
+
}
|
|
1835
|
+
},
|
|
1836
|
+
};
|
|
2153
1837
|
}
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
1838
|
+
|
|
1839
|
+
const logger$7 = new Logger();
|
|
1840
|
+
class ReconnectManager extends EventEmitter {
|
|
1841
|
+
constructor(socket) {
|
|
1842
|
+
super();
|
|
1843
|
+
this.reconnectThresholdInMs = 0;
|
|
1844
|
+
this._socket = socket;
|
|
1845
|
+
this._clients = {};
|
|
1846
|
+
this._signalDisconnectTime = undefined;
|
|
1847
|
+
this.rtcManager = undefined;
|
|
1848
|
+
this.metrics = {
|
|
1849
|
+
roomJoinedLate: 0,
|
|
1850
|
+
pendingClientCanceled: 0,
|
|
1851
|
+
evaluationFailed: 0,
|
|
1852
|
+
roomJoined: 0,
|
|
1853
|
+
};
|
|
1854
|
+
socket.on("disconnect", () => {
|
|
1855
|
+
this._signalDisconnectTime = Date.now();
|
|
1856
|
+
});
|
|
1857
|
+
socket.on(PROTOCOL_RESPONSES.ROOM_JOINED, (payload) => this._onRoomJoined(payload));
|
|
1858
|
+
socket.on(PROTOCOL_RESPONSES.NEW_CLIENT, (payload) => this._onNewClient(payload));
|
|
1859
|
+
socket.on(PROTOCOL_RESPONSES.CLIENT_LEFT, (payload) => this._onClientLeft(payload));
|
|
1860
|
+
socket.on(PROTOCOL_EVENTS.PENDING_CLIENT_LEFT, (payload) => this._onPendingClientLeft(payload));
|
|
1861
|
+
socket.on(PROTOCOL_RESPONSES.AUDIO_ENABLED, (payload) => this._onAudioEnabled(payload));
|
|
1862
|
+
socket.on(PROTOCOL_RESPONSES.VIDEO_ENABLED, (payload) => this._onVideoEnabled(payload));
|
|
1863
|
+
socket.on(PROTOCOL_RESPONSES.SCREENSHARE_STARTED, (payload) => this._onScreenshareChanged(payload, true));
|
|
1864
|
+
socket.on(PROTOCOL_RESPONSES.SCREENSHARE_STOPPED, (payload) => this._onScreenshareChanged(payload, false));
|
|
1865
|
+
}
|
|
1866
|
+
_onRoomJoined(payload) {
|
|
1867
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2169
1868
|
var _a;
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
1869
|
+
this.reconnectThresholdInMs = (payload.disconnectTimeout || 0) * 0.8;
|
|
1870
|
+
if (payload === null || payload === void 0 ? void 0 : payload.error) {
|
|
1871
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
if (!payload.selfId) {
|
|
1875
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
const myDeviceId = (_a = payload.room.clients.find((c) => payload.selfId === c.id)) === null || _a === void 0 ? void 0 : _a.deviceId;
|
|
1879
|
+
if (!myDeviceId) {
|
|
1880
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
if (!this._signalDisconnectTime) {
|
|
1884
|
+
this._resetClientState(payload);
|
|
1885
|
+
payload.room.clients = payload.room.clients.filter((c) => !(c.deviceId === myDeviceId && c.isPendingToLeave));
|
|
1886
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
const RECONNECT_THRESHOLD = payload.disconnectTimeout * 0.8;
|
|
1890
|
+
const timeSinceDisconnect = Date.now() - this._signalDisconnectTime;
|
|
1891
|
+
if (timeSinceDisconnect > RECONNECT_THRESHOLD) {
|
|
1892
|
+
this._resetClientState(payload);
|
|
1893
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
1894
|
+
this.metrics.roomJoinedLate++;
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
payload.room.clients = payload.room.clients.filter((c) => !(c.deviceId === myDeviceId && c.isPendingToLeave));
|
|
1898
|
+
const allStats = yield getUpdatedStats();
|
|
1899
|
+
payload.room.clients.forEach((client) => {
|
|
1900
|
+
var _a;
|
|
1901
|
+
try {
|
|
1902
|
+
if (client.id === payload.selfId)
|
|
1903
|
+
return;
|
|
1904
|
+
if (!this._clients[client.id]) {
|
|
1905
|
+
this._addClientToState(client);
|
|
1906
|
+
return;
|
|
2174
1907
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
1908
|
+
if (!((_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.hasClient(client.id))) {
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
if (this._hasClientStateChanged({
|
|
1912
|
+
clientId: client.id,
|
|
1913
|
+
webcam: client.isVideoEnabled,
|
|
1914
|
+
mic: client.isAudioEnabled,
|
|
1915
|
+
screenShare: client.streams.length > 1,
|
|
1916
|
+
})) {
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
if (this._wasClientSendingMedia(client.id)) {
|
|
1920
|
+
if (!this._isClientMediaActive(allStats, client.id)) {
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
2177
1923
|
}
|
|
1924
|
+
client.mergeWithOldClientState = true;
|
|
2178
1925
|
}
|
|
2179
|
-
|
|
1926
|
+
catch (error) {
|
|
1927
|
+
logger$7.error("Failed to evaluate if we should merge client state %o", error);
|
|
1928
|
+
this.metrics.evaluationFailed++;
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
payload.room.clients.forEach((c) => {
|
|
1932
|
+
if (c.isPendingToLeave) {
|
|
1933
|
+
this._onPendingClientLeft({ clientId: c.id });
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
this.metrics.roomJoined++;
|
|
1937
|
+
this.emit(PROTOCOL_RESPONSES.ROOM_JOINED, payload);
|
|
2180
1938
|
});
|
|
2181
|
-
return sdpTransform__namespace.write(sdpObj);
|
|
2182
|
-
}
|
|
2183
|
-
catch (error) {
|
|
2184
|
-
console.error("Error during addAbsCaptureTimeExtMap: ", error);
|
|
2185
|
-
}
|
|
2186
|
-
return sdp;
|
|
2187
|
-
}
|
|
2188
|
-
function addAbsCaptureTimeExtMap(sdp) {
|
|
2189
|
-
const absCaptureTimeUri = "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time";
|
|
2190
|
-
return addExtMap(sdp, absCaptureTimeUri, true, true);
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
function setVideoBandwidthUsingSetParameters(pc, bandwidth, logger = console) {
|
|
2194
|
-
const sender = pc.getSenders().find((s) => s.track && s.track.kind === "video");
|
|
2195
|
-
if (!sender) {
|
|
2196
|
-
return Promise.resolve();
|
|
2197
|
-
}
|
|
2198
|
-
const parameters = sender.getParameters();
|
|
2199
|
-
if (parameters.encodings && parameters.encodings.length === 0) {
|
|
2200
|
-
return Promise.resolve();
|
|
2201
1939
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
1940
|
+
_onClientLeft(payload) {
|
|
1941
|
+
var _a;
|
|
1942
|
+
const { clientId } = payload;
|
|
1943
|
+
const client = this._clients[clientId];
|
|
1944
|
+
if (client) {
|
|
1945
|
+
clearTimeout(client.timeout);
|
|
1946
|
+
delete this._clients[clientId];
|
|
1947
|
+
}
|
|
1948
|
+
(_a = this.rtcManager) === null || _a === void 0 ? void 0 : _a.disconnect(clientId, payload.eventClaim);
|
|
1949
|
+
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, payload);
|
|
2204
1950
|
}
|
|
2205
|
-
|
|
2206
|
-
|
|
1951
|
+
_onPendingClientLeft(payload) {
|
|
1952
|
+
const { clientId } = payload;
|
|
1953
|
+
const client = this._clients[clientId];
|
|
1954
|
+
if (!client) {
|
|
1955
|
+
logger$7.warn(`client ${clientId} not found`);
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
if (client.isPendingToLeave) {
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
client.isPendingToLeave = true;
|
|
1962
|
+
if (this._wasClientSendingMedia(clientId)) {
|
|
1963
|
+
client.checkActiveMediaAttempts = 0;
|
|
1964
|
+
this._abortIfNotActive(payload);
|
|
1965
|
+
}
|
|
2207
1966
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
1967
|
+
_onNewClient(payload) {
|
|
1968
|
+
const { client: { id: clientId, deviceId }, } = payload;
|
|
1969
|
+
const client = this._clients[clientId];
|
|
1970
|
+
if (client && client.isPendingToLeave) {
|
|
1971
|
+
clearTimeout(client.timeoutHandler);
|
|
1972
|
+
client.isPendingToLeave = false;
|
|
1973
|
+
this.metrics.pendingClientCanceled++;
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
this._getPendingClientsByDeviceId(deviceId).forEach((client) => {
|
|
1977
|
+
clearTimeout(client.timeoutHandler);
|
|
1978
|
+
client.isPendingToLeave = undefined;
|
|
1979
|
+
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, { clientId: client.clientId });
|
|
1980
|
+
});
|
|
1981
|
+
this._addClientToState(payload.client);
|
|
1982
|
+
this.emit(PROTOCOL_RESPONSES.NEW_CLIENT, payload);
|
|
2210
1983
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
class Session {
|
|
2220
|
-
constructor({ clientId, bandwidth, peerConnectionConfig, deprioritizeH264Encoding, incrementAnalyticMetric, }) {
|
|
2221
|
-
this.relayCandidateSeen = false;
|
|
2222
|
-
this.serverReflexiveCandidateSeen = false;
|
|
2223
|
-
this.publicHostCandidateSeen = false;
|
|
2224
|
-
this.ipv6HostCandidateSeen = false;
|
|
2225
|
-
this.ipv6HostCandidateTeredoSeen = false;
|
|
2226
|
-
this.ipv6HostCandidate6to4Seen = false;
|
|
2227
|
-
this.mdnsHostCandidateSeen = false;
|
|
2228
|
-
this.pendingReplaceTrackActions = [];
|
|
2229
|
-
this.peerConnectionConfig = peerConnectionConfig;
|
|
2230
|
-
this.clientId = clientId;
|
|
2231
|
-
this.pc = new RTCPeerConnection(this.peerConnectionConfig);
|
|
2232
|
-
this.signalingState = this.pc.signalingState;
|
|
2233
|
-
this.pc.addEventListener("signalingstatechange", () => {
|
|
2234
|
-
if (this.signalingState === this.pc.signalingState) {
|
|
1984
|
+
_abortIfNotActive(payload) {
|
|
1985
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1986
|
+
const { clientId } = payload;
|
|
1987
|
+
let client = this._clients[clientId];
|
|
1988
|
+
if (!(client === null || client === void 0 ? void 0 : client.isPendingToLeave))
|
|
1989
|
+
return;
|
|
1990
|
+
client.checkActiveMediaAttempts++;
|
|
1991
|
+
if (client.checkActiveMediaAttempts > 3) {
|
|
2235
1992
|
return;
|
|
2236
1993
|
}
|
|
2237
|
-
|
|
2238
|
-
if (
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
1994
|
+
const stillActive = yield this._checkIsActive(clientId);
|
|
1995
|
+
if (stillActive) {
|
|
1996
|
+
client.timeoutHandler = setTimeout(() => this._abortIfNotActive(payload), 500);
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
client = this._clients[clientId];
|
|
2000
|
+
if (client === null || client === void 0 ? void 0 : client.isPendingToLeave) {
|
|
2001
|
+
clearTimeout(client.timeoutHandler);
|
|
2002
|
+
delete this._clients[clientId];
|
|
2003
|
+
this.emit(PROTOCOL_RESPONSES.CLIENT_LEFT, payload);
|
|
2244
2004
|
}
|
|
2245
2005
|
});
|
|
2246
|
-
this.wasEverConnected = false;
|
|
2247
|
-
this.connectionStatus = null;
|
|
2248
|
-
this.bandwidth = bandwidth || 0;
|
|
2249
|
-
this.pending = [];
|
|
2250
|
-
this.isOperationPending = false;
|
|
2251
|
-
this.streamIds = [];
|
|
2252
|
-
this.streams = [];
|
|
2253
|
-
this.earlyIceCandidates = [];
|
|
2254
|
-
this.afterConnected = new Promise((resolve) => {
|
|
2255
|
-
this.registerConnected = resolve;
|
|
2256
|
-
});
|
|
2257
|
-
this._deprioritizeH264Encoding = deprioritizeH264Encoding;
|
|
2258
|
-
this._incrementAnalyticMetric = incrementAnalyticMetric;
|
|
2259
2006
|
}
|
|
2260
|
-
|
|
2261
|
-
this
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
this.pc.addTrack(track, stream);
|
|
2265
|
-
});
|
|
2266
|
-
stream.getVideoTracks().forEach((track) => {
|
|
2267
|
-
this.pc.addTrack(track, stream);
|
|
2007
|
+
_checkIsActive(clientId) {
|
|
2008
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2009
|
+
const allStats = yield getUpdatedStats();
|
|
2010
|
+
return this._isClientMediaActive(allStats, clientId);
|
|
2268
2011
|
});
|
|
2269
2012
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2013
|
+
_isClientMediaActive(stats, clientId) {
|
|
2014
|
+
const clientStats = stats === null || stats === void 0 ? void 0 : stats[clientId];
|
|
2015
|
+
let isActive = false;
|
|
2016
|
+
if (clientStats) {
|
|
2017
|
+
Object.entries(clientStats.tracks).forEach(([trackId, trackStats]) => {
|
|
2018
|
+
if (trackId !== "probator")
|
|
2019
|
+
Object.values(trackStats.ssrcs).forEach((ssrcStats) => {
|
|
2020
|
+
if ((ssrcStats.bitrate || 0) > 0)
|
|
2021
|
+
isActive = true;
|
|
2022
|
+
});
|
|
2023
|
+
});
|
|
2273
2024
|
}
|
|
2274
|
-
|
|
2275
|
-
this.pc.addTrack(track, stream);
|
|
2025
|
+
return isActive;
|
|
2276
2026
|
}
|
|
2277
|
-
|
|
2278
|
-
const
|
|
2279
|
-
|
|
2280
|
-
const sender = this.pc.getSenders().find((sender) => sender.track === track);
|
|
2281
|
-
if (sender) {
|
|
2282
|
-
this.pc.removeTrack(sender);
|
|
2283
|
-
}
|
|
2027
|
+
_onAudioEnabled(payload) {
|
|
2028
|
+
const { clientId, isAudioEnabled } = payload;
|
|
2029
|
+
this._clients[clientId] = Object.assign(Object.assign({}, (this._clients[clientId] || {})), { isAudioEnabled });
|
|
2284
2030
|
}
|
|
2285
|
-
|
|
2286
|
-
const
|
|
2287
|
-
|
|
2288
|
-
|
|
2031
|
+
_onVideoEnabled(payload) {
|
|
2032
|
+
const { clientId, isVideoEnabled } = payload;
|
|
2033
|
+
this._clients[clientId] = Object.assign(Object.assign({}, (this._clients[clientId] || {})), { isVideoEnabled });
|
|
2034
|
+
}
|
|
2035
|
+
_onScreenshareChanged(payload, action) {
|
|
2036
|
+
const { clientId } = payload;
|
|
2037
|
+
this._clients[clientId] = Object.assign(Object.assign({}, (this._clients[clientId] || {})), { isScreenshareEnabled: action });
|
|
2038
|
+
}
|
|
2039
|
+
_hasClientStateChanged({ clientId, webcam, mic, screenShare, }) {
|
|
2040
|
+
const state = this._clients[clientId];
|
|
2041
|
+
if (!state) {
|
|
2042
|
+
throw new Error(`Client ${clientId} not found in ReconnectManager state`);
|
|
2289
2043
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2044
|
+
if (webcam !== state.isVideoEnabled) {
|
|
2045
|
+
return true;
|
|
2046
|
+
}
|
|
2047
|
+
if (mic !== state.isAudioEnabled) {
|
|
2048
|
+
return true;
|
|
2049
|
+
}
|
|
2050
|
+
if (screenShare !== state.isScreenshareEnabled) {
|
|
2051
|
+
return true;
|
|
2052
|
+
}
|
|
2053
|
+
return false;
|
|
2054
|
+
}
|
|
2055
|
+
_addClientToState(newClient) {
|
|
2056
|
+
this._clients[newClient.id] = Object.assign(Object.assign({}, (this._clients[newClient.id] || {})), { isAudioEnabled: newClient.isAudioEnabled, isVideoEnabled: newClient.isVideoEnabled, isScreenshareEnabled: newClient.streams.length > 1, deviceId: newClient.deviceId, isPendingToLeave: newClient.isPendingToLeave, clientId: newClient.id });
|
|
2057
|
+
}
|
|
2058
|
+
_wasClientSendingMedia(clientId) {
|
|
2059
|
+
const client = this._clients[clientId];
|
|
2060
|
+
if (!client) {
|
|
2061
|
+
throw new Error(`Client ${clientId} not found in ReconnectManager state`);
|
|
2293
2062
|
}
|
|
2294
|
-
|
|
2295
|
-
const sender = this.pc.getSenders().find((sender) => sender.track === track);
|
|
2296
|
-
if (sender) {
|
|
2297
|
-
this.pc.removeTrack(sender);
|
|
2298
|
-
}
|
|
2299
|
-
});
|
|
2063
|
+
return client.isAudioEnabled || client.isVideoEnabled || client.isScreenshareEnabled;
|
|
2300
2064
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
this.srdComplete = this.pc.setRemoteDescription(desc);
|
|
2305
|
-
return this.srdComplete.then(() => {
|
|
2306
|
-
this.earlyIceCandidates.forEach((candidate) => this.pc.addIceCandidate(candidate));
|
|
2307
|
-
this.earlyIceCandidates = [];
|
|
2065
|
+
_getPendingClientsByDeviceId(deviceId) {
|
|
2066
|
+
return Object.values(this._clients).filter((clientState) => {
|
|
2067
|
+
return clientState.deviceId === deviceId && clientState.isPendingToLeave;
|
|
2308
2068
|
});
|
|
2309
2069
|
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
}
|
|
2316
|
-
this.isOperationPending = true;
|
|
2317
|
-
let sdp = offer.sdp;
|
|
2318
|
-
sdp = filterMidExtension(sdp);
|
|
2319
|
-
sdp = filterMsidSemantic(sdp);
|
|
2320
|
-
const desc = { type: offer.type, sdp };
|
|
2321
|
-
let answerToSignal;
|
|
2322
|
-
return this._setRemoteDescription(desc)
|
|
2323
|
-
.then(() => {
|
|
2324
|
-
return this.pc.createAnswer();
|
|
2325
|
-
})
|
|
2326
|
-
.then((answer) => {
|
|
2327
|
-
if (!answer.sdp) {
|
|
2328
|
-
this._incrementAnalyticMetric("P2PCreateAnswerNoSDP");
|
|
2329
|
-
rtcStats.sendEvent("P2PCreateAnswerNoSDP", {});
|
|
2330
|
-
throw new Error("SDP undefined while creating answer");
|
|
2070
|
+
_resetClientState(payload) {
|
|
2071
|
+
this._clients = {};
|
|
2072
|
+
payload.room.clients.forEach((client) => {
|
|
2073
|
+
if (client.id === payload.selfId) {
|
|
2074
|
+
return;
|
|
2331
2075
|
}
|
|
2332
2076
|
else {
|
|
2333
|
-
|
|
2334
|
-
sdp: answer.sdp,
|
|
2335
|
-
sdpU: answer.sdp,
|
|
2336
|
-
type: answer.type,
|
|
2337
|
-
};
|
|
2338
|
-
return this.pc.setLocalDescription(answer);
|
|
2077
|
+
this._addClientToState(client);
|
|
2339
2078
|
}
|
|
2340
|
-
})
|
|
2341
|
-
.then(() => {
|
|
2342
|
-
return setVideoBandwidthUsingSetParameters(this.pc, this.bandwidth);
|
|
2343
|
-
})
|
|
2344
|
-
.then(() => {
|
|
2345
|
-
return answerToSignal;
|
|
2346
2079
|
});
|
|
2347
2080
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
const DISCONNECT_DURATION_LIMIT_MS$1 = 60000;
|
|
2084
|
+
const SIGNAL_PING_INTERVAL = 2000;
|
|
2085
|
+
const SIGNAL_PING_MAX_LATENCY = 1000;
|
|
2086
|
+
class KeepAliveManager {
|
|
2087
|
+
constructor(serverSocket) {
|
|
2088
|
+
this.lastPingTimestamp = Date.now();
|
|
2089
|
+
this._disconnectDurationLimitEnabled = false;
|
|
2090
|
+
this.disconnectDurationLimitExceeded = false;
|
|
2091
|
+
this.serverSocket = serverSocket;
|
|
2092
|
+
this.serverSocket.on("connect", () => this.onConnect());
|
|
2093
|
+
this.serverSocket.onEngineEvent("ping", () => this.onPing());
|
|
2094
|
+
this.serverSocket.on("disconnect", () => this.onDisconnect());
|
|
2095
|
+
this.serverSocket.onEngineEvent("reconnect_attempt", () => this.onReconnectAttempt());
|
|
2354
2096
|
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
this.earlyIceCandidates.push(candidate);
|
|
2358
|
-
return;
|
|
2359
|
-
}
|
|
2360
|
-
this.srdComplete.then(() => {
|
|
2361
|
-
var _a;
|
|
2362
|
-
if (this.pc.signalingState === "closed") {
|
|
2363
|
-
return;
|
|
2364
|
-
}
|
|
2365
|
-
if (((_a = adapter$4.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) === "safari" && candidate && candidate.candidate === "") {
|
|
2366
|
-
return;
|
|
2367
|
-
}
|
|
2368
|
-
this.pc.addIceCandidate(candidate).catch((e) => {
|
|
2369
|
-
logger$7.warn("Failed to add ICE candidate ('%s'): %s", candidate ? candidate.candidate : null, e);
|
|
2370
|
-
});
|
|
2371
|
-
});
|
|
2097
|
+
enableDisconnectDurationLimit() {
|
|
2098
|
+
this._disconnectDurationLimitEnabled = true;
|
|
2372
2099
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2100
|
+
pingHeartbeat() {
|
|
2101
|
+
clearTimeout(this.pingTimer);
|
|
2102
|
+
this.pingTimer = setTimeout(() => {
|
|
2103
|
+
this.serverSocket._socket.io.engine.close();
|
|
2104
|
+
}, SIGNAL_PING_INTERVAL + SIGNAL_PING_MAX_LATENCY);
|
|
2105
|
+
this.lastPingTimestamp = Date.now();
|
|
2375
2106
|
}
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
if (!pc) {
|
|
2379
|
-
return;
|
|
2380
|
-
}
|
|
2381
|
-
pc.oniceconnectionstatechange = null;
|
|
2382
|
-
pc.onicecandidate = null;
|
|
2383
|
-
pc.ontrack = null;
|
|
2384
|
-
try {
|
|
2385
|
-
pc.close();
|
|
2386
|
-
}
|
|
2387
|
-
catch (e) {
|
|
2388
|
-
logger$7.warn("failures during close of session", e);
|
|
2389
|
-
}
|
|
2107
|
+
onConnect() {
|
|
2108
|
+
this.pingHeartbeat();
|
|
2390
2109
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2110
|
+
onPing() {
|
|
2111
|
+
this.pingHeartbeat();
|
|
2393
2112
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2113
|
+
onDisconnect() {
|
|
2114
|
+
clearTimeout(this.pingTimer);
|
|
2115
|
+
}
|
|
2116
|
+
onReconnectAttempt() {
|
|
2117
|
+
if (this._disconnectDurationLimitEnabled) {
|
|
2118
|
+
this.disconnectDurationLimitExceeded = Boolean(Date.now() - this.lastPingTimestamp > DISCONNECT_DURATION_LIMIT_MS$1);
|
|
2119
|
+
if (this.disconnectDurationLimitExceeded) {
|
|
2120
|
+
this.serverSocket.disconnect();
|
|
2399
2121
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
var _a$3;
|
|
2127
|
+
const adapter$3 = (_a$3 = adapterRaw.default) !== null && _a$3 !== void 0 ? _a$3 : adapterRaw;
|
|
2128
|
+
const DEFAULT_SOCKET_PATH = "/protocol/socket.io/v4";
|
|
2129
|
+
const NOOP_KEEPALIVE_INTERVAL = 2000;
|
|
2130
|
+
const DISCONNECT_DURATION_LIMIT_MS = 60000;
|
|
2131
|
+
class ServerSocket {
|
|
2132
|
+
constructor(hostName, optionsOverrides, glitchFree = false, disconnectDurationLimitOn = false, serverSideDisconnectDurationLimitOn = false) {
|
|
2133
|
+
this._wasConnectedUsingWebsocket = false;
|
|
2134
|
+
this._disconnectDurationLimitOn = disconnectDurationLimitOn && !serverSideDisconnectDurationLimitOn;
|
|
2135
|
+
this._serverSideDisconnectDurationLimitOn = serverSideDisconnectDurationLimitOn;
|
|
2136
|
+
this._disconnectDurationLimitExceeded = false;
|
|
2137
|
+
this._reconnectManager = null;
|
|
2138
|
+
this._socket = socket_ioClient.io(hostName, Object.assign({ path: DEFAULT_SOCKET_PATH, randomizationFactor: 0.5, reconnectionDelay: 250, reconnectionDelayMax: 5000, timeout: 5000, transports: ["websocket"], withCredentials: true }, optionsOverrides));
|
|
2139
|
+
this._disconnectDurationLimitEnabled = false;
|
|
2140
|
+
this.joinRoomFinished = false;
|
|
2141
|
+
this._socket.io.on("reconnect", () => {
|
|
2142
|
+
if (this._disconnectDurationLimitOn &&
|
|
2143
|
+
this._didExceedDisconnectDurationLimit(this._disconnectDurationLimitLatestTimestamp)) {
|
|
2144
|
+
this._socket.close();
|
|
2145
|
+
this._disconnectDurationLimitExceeded = true;
|
|
2146
|
+
}
|
|
2147
|
+
this._socket.sendBuffer = [];
|
|
2148
|
+
});
|
|
2149
|
+
this._socket.io.on("reconnect_attempt", () => {
|
|
2150
|
+
var _a;
|
|
2151
|
+
if (this._disconnectDurationLimitOn &&
|
|
2152
|
+
this._didExceedDisconnectDurationLimit(this._disconnectDurationLimitLatestTimestamp)) {
|
|
2153
|
+
this._socket.close();
|
|
2154
|
+
this._disconnectDurationLimitExceeded = true;
|
|
2155
|
+
}
|
|
2156
|
+
if (this._wasConnectedUsingWebsocket) {
|
|
2157
|
+
this._socket.io.opts.transports = ["websocket"];
|
|
2158
|
+
if (((_a = adapter$3.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) !== "safari") {
|
|
2159
|
+
delete this._wasConnectedUsingWebsocket;
|
|
2405
2160
|
}
|
|
2406
2161
|
}
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
return (track === null || track === void 0 ? void 0 : track.kind) === newTrack.kind && !trackAnnotations(track).fromGetDisplayMedia;
|
|
2410
|
-
});
|
|
2411
|
-
if (sender) {
|
|
2412
|
-
return yield sender.replaceTrack(newTrack);
|
|
2162
|
+
else {
|
|
2163
|
+
this._socket.io.opts.transports = ["websocket", "polling"];
|
|
2413
2164
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
newTrackIsEffect: trackAnnotations(newTrack).isEffectTrack,
|
|
2423
|
-
});
|
|
2424
|
-
this._incrementAnalyticMetric("P2PReplaceTrackNewTrackNotInStream");
|
|
2165
|
+
});
|
|
2166
|
+
if (glitchFree)
|
|
2167
|
+
this._reconnectManager = new ReconnectManager(this._socket);
|
|
2168
|
+
if (this._serverSideDisconnectDurationLimitOn)
|
|
2169
|
+
this._keepAliveManager = new KeepAliveManager(this);
|
|
2170
|
+
this._socket.on("room_joined", (payload) => {
|
|
2171
|
+
if (!("error" in payload)) {
|
|
2172
|
+
this.joinRoomFinished = true;
|
|
2425
2173
|
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2174
|
+
});
|
|
2175
|
+
this._socket.on("connect", () => {
|
|
2176
|
+
const transport = this.getTransport();
|
|
2177
|
+
if (transport === "websocket") {
|
|
2178
|
+
this._wasConnectedUsingWebsocket = true;
|
|
2179
|
+
if (this._serverSideDisconnectDurationLimitOn)
|
|
2180
|
+
return;
|
|
2181
|
+
if (!this.noopKeepaliveInterval) {
|
|
2182
|
+
let disconnectDurationLimitTimestampCandidate = Date.now();
|
|
2183
|
+
this.noopKeepaliveInterval = setInterval(() => {
|
|
2184
|
+
try {
|
|
2185
|
+
if (this._socket.connected) {
|
|
2186
|
+
if (this._disconnectDurationLimitOn &&
|
|
2187
|
+
!this._didExceedDisconnectDurationLimit(disconnectDurationLimitTimestampCandidate)) {
|
|
2188
|
+
this._disconnectDurationLimitLatestTimestamp =
|
|
2189
|
+
disconnectDurationLimitTimestampCandidate;
|
|
2190
|
+
disconnectDurationLimitTimestampCandidate = Date.now();
|
|
2191
|
+
}
|
|
2192
|
+
this._socket.io.engine.sendPacket("noop");
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
catch (ex) { }
|
|
2196
|
+
}, NOOP_KEEPALIVE_INTERVAL);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
this._socket.on("disconnect", () => {
|
|
2201
|
+
this.joinRoomFinished = false;
|
|
2202
|
+
this.disconnectTimestamp = Date.now();
|
|
2203
|
+
if (this._serverSideDisconnectDurationLimitOn)
|
|
2204
|
+
return;
|
|
2205
|
+
if (this._disconnectDurationLimitOn &&
|
|
2206
|
+
this._didExceedDisconnectDurationLimit(this._disconnectDurationLimitLatestTimestamp)) {
|
|
2207
|
+
this._socket.close();
|
|
2208
|
+
this._disconnectDurationLimitExceeded = true;
|
|
2209
|
+
}
|
|
2210
|
+
if (this.noopKeepaliveInterval) {
|
|
2211
|
+
clearInterval(this.noopKeepaliveInterval);
|
|
2212
|
+
this.noopKeepaliveInterval = null;
|
|
2431
2213
|
}
|
|
2432
|
-
pc.addTrack(newTrack, stream);
|
|
2433
2214
|
});
|
|
2434
2215
|
}
|
|
2435
|
-
|
|
2216
|
+
_didExceedDisconnectDurationLimit(timestamp) {
|
|
2217
|
+
if (!timestamp || !this._disconnectDurationLimitOn || !this._disconnectDurationLimitEnabled)
|
|
2218
|
+
return false;
|
|
2219
|
+
const disconnectedDuration = Date.now() - timestamp;
|
|
2220
|
+
if (disconnectedDuration > DISCONNECT_DURATION_LIMIT_MS) {
|
|
2221
|
+
return true;
|
|
2222
|
+
}
|
|
2223
|
+
return false;
|
|
2224
|
+
}
|
|
2225
|
+
get disconnectDurationLimitExceeded() {
|
|
2226
|
+
var _a, _b;
|
|
2227
|
+
if (this._serverSideDisconnectDurationLimitOn) {
|
|
2228
|
+
return (_b = (_a = this._keepAliveManager) === null || _a === void 0 ? void 0 : _a.disconnectDurationLimitExceeded) !== null && _b !== void 0 ? _b : false;
|
|
2229
|
+
}
|
|
2230
|
+
else if (this._disconnectDurationLimitOn) {
|
|
2231
|
+
return this._disconnectDurationLimitExceeded;
|
|
2232
|
+
}
|
|
2233
|
+
return false;
|
|
2234
|
+
}
|
|
2235
|
+
enableDisconnectDurationLimit() {
|
|
2436
2236
|
var _a;
|
|
2437
|
-
if (
|
|
2438
|
-
|
|
2237
|
+
if (this._serverSideDisconnectDurationLimitOn) {
|
|
2238
|
+
(_a = this._keepAliveManager) === null || _a === void 0 ? void 0 : _a.enableDisconnectDurationLimit();
|
|
2439
2239
|
}
|
|
2440
|
-
if (
|
|
2441
|
-
this.
|
|
2442
|
-
return;
|
|
2240
|
+
else if (this._disconnectDurationLimitOn) {
|
|
2241
|
+
this._disconnectDurationLimitEnabled = true;
|
|
2443
2242
|
}
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2243
|
+
}
|
|
2244
|
+
setRtcManager(rtcManager) {
|
|
2245
|
+
if (this._reconnectManager) {
|
|
2246
|
+
this._reconnectManager.rtcManager = rtcManager;
|
|
2448
2247
|
}
|
|
2449
|
-
|
|
2248
|
+
}
|
|
2249
|
+
connect() {
|
|
2250
|
+
if (this.isConnected() || this.isConnecting()) {
|
|
2450
2251
|
return;
|
|
2451
2252
|
}
|
|
2452
|
-
|
|
2253
|
+
this._socket.open();
|
|
2453
2254
|
}
|
|
2454
|
-
|
|
2255
|
+
disconnect() {
|
|
2256
|
+
this._socket.disconnect();
|
|
2257
|
+
}
|
|
2258
|
+
emit(eventName, ...args) {
|
|
2259
|
+
this._socket.emit.apply(this._socket, arguments);
|
|
2260
|
+
}
|
|
2261
|
+
getTransport() {
|
|
2262
|
+
var _a, _b, _c, _d;
|
|
2263
|
+
return (_d = (_c = (_b = (_a = this._socket) === null || _a === void 0 ? void 0 : _a.io) === null || _b === void 0 ? void 0 : _b.engine) === null || _c === void 0 ? void 0 : _c.transport) === null || _d === void 0 ? void 0 : _d.name;
|
|
2264
|
+
}
|
|
2265
|
+
getManager() {
|
|
2266
|
+
return this._socket.io;
|
|
2267
|
+
}
|
|
2268
|
+
isConnecting() {
|
|
2269
|
+
return this._socket && this._socket.connecting;
|
|
2270
|
+
}
|
|
2271
|
+
isConnected() {
|
|
2272
|
+
return this._socket && this._socket.connected;
|
|
2273
|
+
}
|
|
2274
|
+
on(eventName, handler) {
|
|
2275
|
+
const relayableEvents = [
|
|
2276
|
+
PROTOCOL_RESPONSES.ROOM_JOINED,
|
|
2277
|
+
PROTOCOL_RESPONSES.CLIENT_LEFT,
|
|
2278
|
+
PROTOCOL_RESPONSES.NEW_CLIENT,
|
|
2279
|
+
];
|
|
2280
|
+
if (this._reconnectManager && relayableEvents.includes(eventName)) {
|
|
2281
|
+
return this._interceptEvent(eventName, handler);
|
|
2282
|
+
}
|
|
2283
|
+
this._socket.on(eventName, handler);
|
|
2284
|
+
return () => {
|
|
2285
|
+
this._socket.off(eventName, handler);
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
onEngineEvent(eventName, handler) {
|
|
2455
2289
|
var _a;
|
|
2456
|
-
(_a = this.
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2290
|
+
(_a = this._socket.io) === null || _a === void 0 ? void 0 : _a.on(eventName, handler);
|
|
2291
|
+
return () => {
|
|
2292
|
+
var _a;
|
|
2293
|
+
(_a = this._socket.io) === null || _a === void 0 ? void 0 : _a.off(eventName, handler);
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
once(eventName, handler) {
|
|
2297
|
+
this._socket.once(eventName, handler);
|
|
2298
|
+
}
|
|
2299
|
+
off(eventName, handler) {
|
|
2300
|
+
this._socket.off(eventName, handler);
|
|
2301
|
+
}
|
|
2302
|
+
_interceptEvent(eventName, handler) {
|
|
2303
|
+
if (this._reconnectManager) {
|
|
2304
|
+
this._reconnectManager.on(eventName, handler);
|
|
2305
|
+
}
|
|
2306
|
+
return () => {
|
|
2307
|
+
if (this._reconnectManager)
|
|
2308
|
+
this._reconnectManager.removeListener(eventName, handler);
|
|
2309
|
+
};
|
|
2310
|
+
}
|
|
2311
|
+
getGlitchFreeMetrics() {
|
|
2312
|
+
var _a;
|
|
2313
|
+
return (_a = this._reconnectManager) === null || _a === void 0 ? void 0 : _a.metrics;
|
|
2314
|
+
}
|
|
2315
|
+
getReconnectThreshold() {
|
|
2316
|
+
var _a;
|
|
2317
|
+
return (_a = this._reconnectManager) === null || _a === void 0 ? void 0 : _a.reconnectThresholdInMs;
|
|
2465
2318
|
}
|
|
2466
2319
|
}
|
|
2467
2320
|
|
|
2468
|
-
const
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
const adapter$3 = (_a$3 = adapterRaw.default) !== null && _a$3 !== void 0 ? _a$3 : adapterRaw;
|
|
2472
|
-
function detectMicrophoneNotWorking(pc) {
|
|
2473
|
-
var _a, _b;
|
|
2474
|
-
if (((_a = adapter$3.browserDetails) === null || _a === void 0 ? void 0 : _a.browser) !== "chrome" ||
|
|
2475
|
-
((_b = adapter$3.browserDetails) === null || _b === void 0 ? void 0 : _b.browser) < 58 ||
|
|
2476
|
-
pc.signalingState === "closed") {
|
|
2477
|
-
return Promise.resolve("");
|
|
2321
|
+
const maybeTurnOnly = (iceConfig, features) => {
|
|
2322
|
+
if (!features.useOnlyTURN) {
|
|
2323
|
+
return;
|
|
2478
2324
|
}
|
|
2479
|
-
|
|
2480
|
-
const
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
receivingAudio) {
|
|
2494
|
-
if (report.bytesReceived === 0) {
|
|
2495
|
-
microphoneFailed = "inbound";
|
|
2496
|
-
}
|
|
2325
|
+
iceConfig.iceTransportPolicy = "relay";
|
|
2326
|
+
const filter = {
|
|
2327
|
+
onlyudp: /^turn:.*transport=udp$/,
|
|
2328
|
+
onlytcp: /^turn:.*transport=tcp$/,
|
|
2329
|
+
onlytls: /^turns:.*transport=tcp$/,
|
|
2330
|
+
}[features.useOnlyTURN];
|
|
2331
|
+
if (filter) {
|
|
2332
|
+
iceConfig.iceServers = iceConfig.iceServers.filter((entry) => {
|
|
2333
|
+
if (entry.url && entry.url.match(filter))
|
|
2334
|
+
return entry;
|
|
2335
|
+
if (entry.urls) {
|
|
2336
|
+
entry.urls = (entry.urls.some ? entry.urls : [entry.urls]).filter((url) => url.match(filter));
|
|
2337
|
+
if (entry.urls.length > 0)
|
|
2338
|
+
return entry;
|
|
2497
2339
|
}
|
|
2498
2340
|
});
|
|
2499
|
-
|
|
2500
|
-
});
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
const EVENTS = {
|
|
2504
|
-
CLIENT_CONNECTION_STATUS_CHANGED: "client_connection_status_changed",
|
|
2505
|
-
STREAM_ADDED: "stream_added",
|
|
2506
|
-
RTC_MANAGER_CREATED: "rtc_manager_created",
|
|
2507
|
-
RTC_MANAGER_DESTROYED: "rtc_manager_destroyed",
|
|
2508
|
-
LOCAL_STREAM_TRACK_ADDED: "local_stream_track_added",
|
|
2509
|
-
LOCAL_STREAM_TRACK_REMOVED: "local_stream_track_removed",
|
|
2510
|
-
REMOTE_STREAM_TRACK_ADDED: "remote_stream_track_added",
|
|
2511
|
-
REMOTE_STREAM_TRACK_REMOVED: "remote_stream_track_removed",
|
|
2341
|
+
}
|
|
2512
2342
|
};
|
|
2513
|
-
const
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2343
|
+
const external_stun_servers = (iceConfig, features) => {
|
|
2344
|
+
if (features.addGoogleStunServers) {
|
|
2345
|
+
iceConfig.iceServers = [
|
|
2346
|
+
{ urls: "stun:stun.l.google.com:19302" },
|
|
2347
|
+
{ urls: "stun:stun2.l.google.com:19302" },
|
|
2348
|
+
...iceConfig.iceServers,
|
|
2349
|
+
];
|
|
2350
|
+
}
|
|
2351
|
+
if (features.addCloudflareStunServers) {
|
|
2352
|
+
iceConfig.iceServers = [
|
|
2353
|
+
{ urls: "stun:stun.cloudflare.com:3478" },
|
|
2354
|
+
{ urls: "stun:stun.cloudflare.com:53" },
|
|
2355
|
+
...iceConfig.iceServers,
|
|
2356
|
+
];
|
|
2357
|
+
}
|
|
2518
2358
|
};
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2359
|
+
const turnServerOverride = (iceServers, overrideHost) => {
|
|
2360
|
+
if (overrideHost && iceServers) {
|
|
2361
|
+
const host = overrideHost;
|
|
2362
|
+
const port = host.indexOf(":") > 0 ? "" : ":443";
|
|
2363
|
+
const override = ":" + host + port;
|
|
2364
|
+
return iceServers.map((original) => {
|
|
2365
|
+
const entry = Object.assign({}, original);
|
|
2366
|
+
if (entry.url) {
|
|
2367
|
+
entry.url = entry.url.replace(/:[^?]*/, override);
|
|
2368
|
+
}
|
|
2369
|
+
if (entry.urls) {
|
|
2370
|
+
entry.urls = entry.urls.map((url) => url.replace(/:[^?]*/, override));
|
|
2371
|
+
}
|
|
2372
|
+
return entry;
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2375
|
+
else {
|
|
2376
|
+
return iceServers;
|
|
2377
|
+
}
|
|
2532
2378
|
};
|
|
2533
2379
|
|
|
2380
|
+
const defaultSubdomainPattern = /^(?:([^.]+)[.])?((:?[^.]+[.]){1,}[^.]+)$/;
|
|
2381
|
+
const localstackPattern = /^(?:([^.]+)-)?(ip-[^.]*[.](?:hereby[.]dev|rfc1918[.]disappear[.]at)(?::\d+|))$/;
|
|
2382
|
+
const localhostPattern = /^(?:([^.]+)[.])?(localhost:?\d*)/;
|
|
2383
|
+
const serverPattern = /^(?:([^.]+)[.])?(server:?\d*)/;
|
|
2384
|
+
const ipv4Pattern = /^(?:([^.]+)[.])?((\d+[.]){3}:?\d*)$/;
|
|
2385
|
+
const subdomainPatterns = [
|
|
2386
|
+
{ pattern: serverPattern, separator: "." },
|
|
2387
|
+
{ pattern: localhostPattern, separator: "." },
|
|
2388
|
+
{ pattern: ipv4Pattern, separator: "." },
|
|
2389
|
+
{ pattern: localstackPattern, separator: "-" },
|
|
2390
|
+
{ pattern: defaultSubdomainPattern, separator: "." },
|
|
2391
|
+
];
|
|
2392
|
+
function fromLocation({ host = "whereby.com", protocol = "https:" } = {}) {
|
|
2393
|
+
let subdomain = "";
|
|
2394
|
+
let domain = host;
|
|
2395
|
+
let subdomainSeparator = ".";
|
|
2396
|
+
for (const { separator, pattern } of subdomainPatterns) {
|
|
2397
|
+
const match = pattern.exec(host);
|
|
2398
|
+
if (match) {
|
|
2399
|
+
subdomain = match[1] || "";
|
|
2400
|
+
domain = match[2];
|
|
2401
|
+
subdomainSeparator = separator;
|
|
2402
|
+
break;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
const organizationDomain = !subdomain ? domain : `${subdomain}${subdomainSeparator}${domain}`;
|
|
2406
|
+
return {
|
|
2407
|
+
domain,
|
|
2408
|
+
domainWithSeparator: `${subdomainSeparator}${domain}`,
|
|
2409
|
+
organizationDomain,
|
|
2410
|
+
organization: `${protocol}//${organizationDomain}`,
|
|
2411
|
+
service: `${protocol}//${domain}`,
|
|
2412
|
+
subdomain,
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
fromLocation(window && window.location);
|
|
2416
|
+
|
|
2534
2417
|
var _a$2, _b$1;
|
|
2535
2418
|
const adapter$2 = (_a$2 = adapterRaw.default) !== null && _a$2 !== void 0 ? _a$2 : adapterRaw;
|
|
2536
2419
|
const isSafari = ((_b$1 = adapter$2.browserDetails) === null || _b$1 === void 0 ? void 0 : _b$1.browser) === "safari";
|
|
@@ -2663,17 +2546,12 @@ class P2pRtcManager {
|
|
|
2663
2546
|
numPcSldFailure: 0,
|
|
2664
2547
|
numPcOnAnswerFailure: 0,
|
|
2665
2548
|
numPcOnOfferFailure: 0,
|
|
2666
|
-
P2PChangeBandwidthEmptySDPType: 0,
|
|
2667
2549
|
P2PReplaceTrackNoStream: 0,
|
|
2668
2550
|
P2PReplaceTrackNewTrackNotInStream: 0,
|
|
2669
2551
|
P2POnTrackNoStream: 0,
|
|
2670
|
-
P2PSetCodecPreferenceError: 0,
|
|
2671
|
-
P2PCreateOfferNoSDP: 0,
|
|
2672
|
-
P2PCreateAnswerNoSDP: 0,
|
|
2673
2552
|
P2PMicNotWorking: 0,
|
|
2674
2553
|
P2PLocalNetworkFailed: 0,
|
|
2675
2554
|
P2PRelayedIceCandidate: 0,
|
|
2676
|
-
P2PStartScreenshareNoStream: 0,
|
|
2677
2555
|
};
|
|
2678
2556
|
}
|
|
2679
2557
|
numberOfPeerconnections() {
|
|
@@ -3214,10 +3092,6 @@ class P2pRtcManager {
|
|
|
3214
3092
|
if (this._localScreenshareStream) {
|
|
3215
3093
|
session.addStream(this._localScreenshareStream);
|
|
3216
3094
|
}
|
|
3217
|
-
else {
|
|
3218
|
-
this.analytics.P2PStartScreenshareNoStream++;
|
|
3219
|
-
rtcStats.sendEvent("P2PStartScreenshareNoStream", {});
|
|
3220
|
-
}
|
|
3221
3095
|
});
|
|
3222
3096
|
});
|
|
3223
3097
|
}
|
|
@@ -3425,8 +3299,6 @@ class P2pRtcManager {
|
|
|
3425
3299
|
.createOffer(constraints || this.offerOptions)
|
|
3426
3300
|
.then((offer) => {
|
|
3427
3301
|
if (!offer.sdp) {
|
|
3428
|
-
this.analytics.P2PCreateOfferNoSDP++;
|
|
3429
|
-
rtcStats.sendEvent("P2PCreateOfferNoSDP", {});
|
|
3430
3302
|
throw new Error("SDP undefined while creating offer");
|
|
3431
3303
|
}
|
|
3432
3304
|
if (rtpAbsCaptureTimeOn)
|
|
@@ -3435,7 +3307,6 @@ class P2pRtcManager {
|
|
|
3435
3307
|
offer.sdp = setCodecPreferenceSDP({
|
|
3436
3308
|
sdp: offer.sdp,
|
|
3437
3309
|
redOn,
|
|
3438
|
-
incrementAnalyticMetric: (metric) => this.analytics[metric]++,
|
|
3439
3310
|
});
|
|
3440
3311
|
}
|
|
3441
3312
|
if (cleanSdpOn)
|
|
@@ -5408,6 +5279,11 @@ class VegaRtcManager {
|
|
|
5408
5279
|
}
|
|
5409
5280
|
}
|
|
5410
5281
|
replaceTrack(_, track) {
|
|
5282
|
+
logger$2.info("replaceTrack() [kind: %s, id: %s, readyState: %s]", track.kind, track.id, track.readyState);
|
|
5283
|
+
if (track.readyState === "ended") {
|
|
5284
|
+
logger$2.error(`refusing to use ended track with id: ${track.id}, kind: ${track.kind}`);
|
|
5285
|
+
return;
|
|
5286
|
+
}
|
|
5411
5287
|
if (track.kind === "audio") {
|
|
5412
5288
|
if (!trackAnnotations(track).isEffectTrack) {
|
|
5413
5289
|
this._monitorAudioTrack(track);
|
|
@@ -6011,23 +5887,6 @@ const getRoomMode = () => {
|
|
|
6011
5887
|
return roomMode;
|
|
6012
5888
|
};
|
|
6013
5889
|
|
|
6014
|
-
const packetLossAnalyser = new PacketLossAnalyser();
|
|
6015
|
-
const periodicPacketLossDetector = {
|
|
6016
|
-
id: "periodic-packet-loss",
|
|
6017
|
-
enabled: ({ client, hasLiveTrack, ssrc0 }) => {
|
|
6018
|
-
return (!!client.isLocalClient &&
|
|
6019
|
-
hasLiveTrack &&
|
|
6020
|
-
!!(ssrc0 === null || ssrc0 === void 0 ? void 0 : ssrc0.ssrc) &&
|
|
6021
|
-
(ssrc0 === null || ssrc0 === void 0 ? void 0 : ssrc0.direction) === "out" &&
|
|
6022
|
-
((ssrc0 === null || ssrc0 === void 0 ? void 0 : ssrc0.bitrate) || 0) > 0);
|
|
6023
|
-
},
|
|
6024
|
-
check: ({ ssrc0 }) => {
|
|
6025
|
-
if (!ssrc0 || !ssrc0.ssrc)
|
|
6026
|
-
return false;
|
|
6027
|
-
packetLossAnalyser.addPacketLossMeasurement(ssrc0.ssrc, ssrc0.fractionLost || 0, Date.now());
|
|
6028
|
-
return packetLossAnalyser.hasPeriodicPacketLoss(ssrc0.ssrc, Date.now());
|
|
6029
|
-
},
|
|
6030
|
-
};
|
|
6031
5890
|
const badNetworkIssueDetector = {
|
|
6032
5891
|
id: "bad-network",
|
|
6033
5892
|
enabled: ({ hasLiveTrack, ssrcs }) => hasLiveTrack && ssrcs.length > 0,
|
|
@@ -6215,7 +6074,6 @@ const issueDetectors = [
|
|
|
6215
6074
|
check: ({ ssrc0 }) => ((ssrc0 === null || ssrc0 === void 0 ? void 0 : ssrc0.fps) || 0) < 10,
|
|
6216
6075
|
},
|
|
6217
6076
|
badNetworkIssueDetector,
|
|
6218
|
-
periodicPacketLossDetector,
|
|
6219
6077
|
{
|
|
6220
6078
|
id: "cpu-pressure-serious",
|
|
6221
6079
|
global: true,
|