@whereby.com/media 11.1.1 → 11.1.3
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 +566 -123
- package/dist/index.d.cts +122 -34
- package/dist/index.d.mts +122 -34
- package/dist/index.d.ts +122 -34
- package/dist/index.mjs +556 -124
- package/dist/legacy-esm.js +556 -124
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -304,7 +304,7 @@ class Logger {
|
|
|
304
304
|
|
|
305
305
|
var _a$5, _b$2;
|
|
306
306
|
const adapter$5 = (_a$5 = adapterRaw.default) !== null && _a$5 !== void 0 ? _a$5 : adapterRaw;
|
|
307
|
-
const logger$
|
|
307
|
+
const logger$c = new Logger();
|
|
308
308
|
const browserName$2 = (_b$2 = adapter$5.browserDetails) === null || _b$2 === void 0 ? void 0 : _b$2.browser;
|
|
309
309
|
const browserVersion$1 = adapter$5.browserDetails.version;
|
|
310
310
|
function setCodecPreferenceSDP({ sdp, redOn }) {
|
|
@@ -345,7 +345,7 @@ function setCodecPreferenceSDP({ sdp, redOn }) {
|
|
|
345
345
|
return newSdp;
|
|
346
346
|
}
|
|
347
347
|
catch (error) {
|
|
348
|
-
logger$
|
|
348
|
+
logger$c.error("setCodecPreferenceSDP error:", error);
|
|
349
349
|
return sdp;
|
|
350
350
|
}
|
|
351
351
|
}
|
|
@@ -496,7 +496,7 @@ function trackAnnotations(o) {
|
|
|
496
496
|
|
|
497
497
|
var _a$4;
|
|
498
498
|
const adapter$4 = (_a$4 = adapterRaw.default) !== null && _a$4 !== void 0 ? _a$4 : adapterRaw;
|
|
499
|
-
const logger$
|
|
499
|
+
const logger$b = new Logger();
|
|
500
500
|
class Session {
|
|
501
501
|
constructor({ clientId, bandwidth, peerConnectionConfig, deprioritizeH264Encoding, incrementAnalyticMetric, mediaPrefs, }) {
|
|
502
502
|
this.relayCandidateSeen = false;
|
|
@@ -628,7 +628,7 @@ class Session {
|
|
|
628
628
|
}
|
|
629
629
|
handleAnswer(message) {
|
|
630
630
|
if (this.pc.signalingState !== "have-local-offer") {
|
|
631
|
-
logger$
|
|
631
|
+
logger$b.warn("Ignoring stale SDP answer for client %s (signalingState: %s)", this.clientId, this.pc.signalingState);
|
|
632
632
|
this._incrementAnalyticMetric("P2PStaleAnswerIgnored");
|
|
633
633
|
rtcStats.sendEvent("P2PStaleAnswerIgnored", {
|
|
634
634
|
clientId: this.clientId,
|
|
@@ -656,7 +656,7 @@ class Session {
|
|
|
656
656
|
return;
|
|
657
657
|
}
|
|
658
658
|
this.pc.addIceCandidate(candidate).catch((e) => {
|
|
659
|
-
logger$
|
|
659
|
+
logger$b.warn("Failed to add ICE candidate ('%s'): %s", candidate ? candidate.candidate : null, e);
|
|
660
660
|
this._incrementAnalyticMetric("P2PAddIceCandidateFailure");
|
|
661
661
|
rtcStats.sendEvent("P2PAddIceCandidateFailure", {
|
|
662
662
|
clientId: this.clientId,
|
|
@@ -684,7 +684,7 @@ class Session {
|
|
|
684
684
|
pc.close();
|
|
685
685
|
}
|
|
686
686
|
catch (e) {
|
|
687
|
-
logger$
|
|
687
|
+
logger$b.warn("failures during close of session", e);
|
|
688
688
|
}
|
|
689
689
|
}
|
|
690
690
|
hasConnectedPeerConnection() {
|
|
@@ -693,7 +693,7 @@ class Session {
|
|
|
693
693
|
replaceTrack(oldTrack, newTrack) {
|
|
694
694
|
return __awaiter(this, void 0, void 0, function* () {
|
|
695
695
|
var _a;
|
|
696
|
-
logger$
|
|
696
|
+
logger$b.info("replacetrack() [oldTrackId: %s, newTrackId: %s]", oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.id, newTrack.id);
|
|
697
697
|
if (newTrack.readyState === "ended") {
|
|
698
698
|
throw new Error(`refusing to use ended track with id: ${newTrack.id}, kind: ${newTrack.kind}`);
|
|
699
699
|
}
|
|
@@ -764,6 +764,16 @@ class Session {
|
|
|
764
764
|
}
|
|
765
765
|
|
|
766
766
|
const MEDIA_JITTER_BUFFER_TARGET = 400;
|
|
767
|
+
const INITIAL_CONSUMER_SPATIAL_LAYER = 1;
|
|
768
|
+
const INITIAL_HIGHEST_PREFERRED_LAYER = 1;
|
|
769
|
+
const LOWEST_SVC_LAYER_MAX_BITRATE = 100000;
|
|
770
|
+
const MIDDLE_SVC_LAYER_MAX_BITRATE = 500000;
|
|
771
|
+
const PREFERRED_LAYER_SWITCH_POLL_INTERVAL_MS = 100;
|
|
772
|
+
const PREFERRED_LAYER_SWITCH_WATCH_TIMEOUT_MS = 10000;
|
|
773
|
+
const MAX_CONCURRENT_PREFERRED_LAYER_SWITCH_WATCHES = 5;
|
|
774
|
+
const CONGESTED_RTT_INFLATION_MS = 100;
|
|
775
|
+
const CONGESTED_FRACTION_LOST = 0.02;
|
|
776
|
+
const CAM_SAMPLES_SKIPPED_AFTER_CANDIDATE_PAIR_CHANGE = 2;
|
|
767
777
|
|
|
768
778
|
const PROTOCOL_REQUESTS = {
|
|
769
779
|
BLOCK_CLIENT: "block_client",
|
|
@@ -1063,7 +1073,7 @@ const getMediasoupDeviceAsync = (features) => __awaiter(void 0, void 0, void 0,
|
|
|
1063
1073
|
return new mediasoupClient.Device({ handlerName });
|
|
1064
1074
|
});
|
|
1065
1075
|
|
|
1066
|
-
const logger$
|
|
1076
|
+
const logger$a = new Logger();
|
|
1067
1077
|
const AUDIO_SETTINGS = {
|
|
1068
1078
|
codecOptions: {
|
|
1069
1079
|
opusDtx: true,
|
|
@@ -1243,7 +1253,7 @@ function sortCodecsByPowerEfficiency(codecs) {
|
|
|
1243
1253
|
codecPowerEfficiencyEntries = yield Promise.all(codecs.map(({ mimeType }) => getIsCodecDecodingPowerEfficient(mimeType).then((val) => [mimeType, val])));
|
|
1244
1254
|
}
|
|
1245
1255
|
catch (error) {
|
|
1246
|
-
logger$
|
|
1256
|
+
logger$a.error(error);
|
|
1247
1257
|
return codecs;
|
|
1248
1258
|
}
|
|
1249
1259
|
const codecPowerEfficiencies = Object.fromEntries(codecPowerEfficiencyEntries);
|
|
@@ -1312,7 +1322,24 @@ function captureSsrcInfo(ssrcMetrics, currentSsrcStats, prevSsrcStats, timeDiff,
|
|
|
1312
1322
|
ssrcMetrics.mid = currentSsrcStats.mid;
|
|
1313
1323
|
ssrcMetrics.rid = currentSsrcStats.rid;
|
|
1314
1324
|
}
|
|
1325
|
+
const selectedCandidatePairIdByReport = new WeakMap();
|
|
1326
|
+
function getSelectedCandidatePairId(report, transportId) {
|
|
1327
|
+
var _a;
|
|
1328
|
+
const selectedCandidatePairId = transportId ? (_a = report.get(transportId)) === null || _a === void 0 ? void 0 : _a.selectedCandidatePairId : undefined;
|
|
1329
|
+
if (selectedCandidatePairId)
|
|
1330
|
+
return selectedCandidatePairId;
|
|
1331
|
+
if (!selectedCandidatePairIdByReport.has(report)) {
|
|
1332
|
+
let selectedId;
|
|
1333
|
+
report.forEach((stats) => {
|
|
1334
|
+
if (stats.type === "candidate-pair" && stats.selected)
|
|
1335
|
+
selectedId = stats.id;
|
|
1336
|
+
});
|
|
1337
|
+
selectedCandidatePairIdByReport.set(report, selectedId);
|
|
1338
|
+
}
|
|
1339
|
+
return selectedCandidatePairIdByReport.get(report);
|
|
1340
|
+
}
|
|
1315
1341
|
function captureCommonSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, timeDiff, report) {
|
|
1342
|
+
ssrcMetrics.selectedCandidatePairId = getSelectedCandidatePairId(report, currentSsrcStats.transportId);
|
|
1316
1343
|
const nackCountDiff = (currentSsrcStats.nackCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.nackCount) || 0);
|
|
1317
1344
|
ssrcMetrics.nackCount = (ssrcMetrics.nackCount || 0) + nackCountDiff;
|
|
1318
1345
|
ssrcMetrics.nackRate = (1000 * nackCountDiff) / timeDiff;
|
|
@@ -1366,6 +1393,7 @@ function captureCommonSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats,
|
|
|
1366
1393
|
ssrcMetrics.roundTripTime = remoteReport.roundTripTime || 0;
|
|
1367
1394
|
ssrcMetrics.jitter = remoteReport.jitter || 0;
|
|
1368
1395
|
ssrcMetrics.fractionLost = remoteReport.fractionLost || 0;
|
|
1396
|
+
ssrcMetrics.remoteReportTimestamp = remoteReport.timestamp;
|
|
1369
1397
|
}
|
|
1370
1398
|
}
|
|
1371
1399
|
}
|
|
@@ -1597,7 +1625,6 @@ const DEFAULT_CLIENT = {
|
|
|
1597
1625
|
};
|
|
1598
1626
|
function collectStats(state_1, _a, immediate_1) {
|
|
1599
1627
|
return __awaiter(this, arguments, void 0, function* (state, { logger, interval }, immediate) {
|
|
1600
|
-
const collectStatsBound = collectStats.bind(null, state, { interval, logger });
|
|
1601
1628
|
try {
|
|
1602
1629
|
const clients = state.getClients();
|
|
1603
1630
|
const defaultClient = clients.find((c) => c.isLocalClient && !c.isPresentation) || DEFAULT_CLIENT;
|
|
@@ -1612,7 +1639,6 @@ function collectStats(state_1, _a, immediate_1) {
|
|
|
1612
1639
|
if (immediate)
|
|
1613
1640
|
return state.statsByView;
|
|
1614
1641
|
state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients, state.renderedDimensionsByTrack); });
|
|
1615
|
-
state.nextTimeout = setTimeout(collectStatsBound, interval);
|
|
1616
1642
|
return;
|
|
1617
1643
|
}
|
|
1618
1644
|
state.lastUpdateTime = Date.now();
|
|
@@ -1735,11 +1761,10 @@ function collectStats(state_1, _a, immediate_1) {
|
|
|
1735
1761
|
message: e === null || e === void 0 ? void 0 : e.message,
|
|
1736
1762
|
});
|
|
1737
1763
|
}
|
|
1738
|
-
state.nextTimeout = setTimeout(collectStatsBound, interval);
|
|
1739
1764
|
});
|
|
1740
1765
|
}
|
|
1741
1766
|
|
|
1742
|
-
const logger$
|
|
1767
|
+
const logger$9 = new Logger();
|
|
1743
1768
|
const CPU_OBSERVER_OPTIONS = {
|
|
1744
1769
|
sampleRate: 1,
|
|
1745
1770
|
};
|
|
@@ -1749,7 +1774,7 @@ function startCpuObserver(cb, { sampleRate } = CPU_OBSERVER_OPTIONS, window = gl
|
|
|
1749
1774
|
if ("PressureObserver" in window &&
|
|
1750
1775
|
(window.PressureObserver.knownSources || []).includes("cpu")) {
|
|
1751
1776
|
pressureObserver = new window.PressureObserver(cb, { sampleRate });
|
|
1752
|
-
(_a = pressureObserver.observe("cpu", { sampleInterval: sampleRate * 1000 })) === null || _a === void 0 ? void 0 : _a.catch((error) => logger$
|
|
1777
|
+
(_a = pressureObserver.observe("cpu", { sampleInterval: sampleRate * 1000 })) === null || _a === void 0 ? void 0 : _a.catch((error) => logger$9.error(error));
|
|
1753
1778
|
return {
|
|
1754
1779
|
stop: () => {
|
|
1755
1780
|
pressureObserver.unobserve("cpu");
|
|
@@ -1785,7 +1810,20 @@ const updateRenderedDimensions = (trackId, data) => {
|
|
|
1785
1810
|
STATE.renderedDimensionsByTrack[trackId] = data;
|
|
1786
1811
|
};
|
|
1787
1812
|
function startStatsMonitor(state, { interval, logger }) {
|
|
1788
|
-
|
|
1813
|
+
let stopped = false;
|
|
1814
|
+
let nextTimeout;
|
|
1815
|
+
const scheduleNextRun = () => {
|
|
1816
|
+
nextTimeout = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
1817
|
+
try {
|
|
1818
|
+
yield collectStats(state, { interval, logger }, false);
|
|
1819
|
+
}
|
|
1820
|
+
catch (error) {
|
|
1821
|
+
logger.error("Stats collection failed unexpectedly", error);
|
|
1822
|
+
}
|
|
1823
|
+
if (!stopped)
|
|
1824
|
+
scheduleNextRun();
|
|
1825
|
+
}), interval);
|
|
1826
|
+
};
|
|
1789
1827
|
let cpuObserver;
|
|
1790
1828
|
try {
|
|
1791
1829
|
cpuObserver = startCpuObserver((records) => (state.lastPressureObserverRecord = records.pop()));
|
|
@@ -1793,13 +1831,14 @@ function startStatsMonitor(state, { interval, logger }) {
|
|
|
1793
1831
|
catch (ex) {
|
|
1794
1832
|
logger.warn("Failed to observe CPU pressure", ex);
|
|
1795
1833
|
}
|
|
1796
|
-
|
|
1834
|
+
scheduleNextRun();
|
|
1797
1835
|
return {
|
|
1798
1836
|
getUpdatedStats: () => {
|
|
1799
|
-
return
|
|
1837
|
+
return collectStats(state, { interval, logger }, true);
|
|
1800
1838
|
},
|
|
1801
1839
|
stop: () => {
|
|
1802
|
-
|
|
1840
|
+
stopped = true;
|
|
1841
|
+
clearTimeout(nextTimeout);
|
|
1803
1842
|
cpuObserver === null || cpuObserver === void 0 ? void 0 : cpuObserver.stop();
|
|
1804
1843
|
},
|
|
1805
1844
|
};
|
|
@@ -1820,7 +1859,7 @@ function subscribeStats(subscription, options = OPTIONS, state = STATE) {
|
|
|
1820
1859
|
};
|
|
1821
1860
|
}
|
|
1822
1861
|
|
|
1823
|
-
const logger$
|
|
1862
|
+
const logger$8 = new Logger();
|
|
1824
1863
|
class ReconnectManager extends EventEmitter {
|
|
1825
1864
|
constructor(socket) {
|
|
1826
1865
|
super();
|
|
@@ -1908,7 +1947,7 @@ class ReconnectManager extends EventEmitter {
|
|
|
1908
1947
|
client.mergeWithOldClientState = true;
|
|
1909
1948
|
}
|
|
1910
1949
|
catch (error) {
|
|
1911
|
-
logger$
|
|
1950
|
+
logger$8.error("Failed to evaluate if we should merge client state %o", error);
|
|
1912
1951
|
this.metrics.evaluationFailed++;
|
|
1913
1952
|
}
|
|
1914
1953
|
});
|
|
@@ -1936,7 +1975,7 @@ class ReconnectManager extends EventEmitter {
|
|
|
1936
1975
|
const { clientId } = payload;
|
|
1937
1976
|
const client = this._clients[clientId];
|
|
1938
1977
|
if (!client) {
|
|
1939
|
-
logger$
|
|
1978
|
+
logger$8.warn(`client ${clientId} not found`);
|
|
1940
1979
|
return;
|
|
1941
1980
|
}
|
|
1942
1981
|
if (client.isPendingToLeave) {
|
|
@@ -2409,7 +2448,7 @@ fromLocation(typeof window !== "undefined" ? window.location : undefined);
|
|
|
2409
2448
|
|
|
2410
2449
|
var _a$2, _b$1;
|
|
2411
2450
|
const adapter$2 = (_a$2 = adapterRaw.default) !== null && _a$2 !== void 0 ? _a$2 : adapterRaw;
|
|
2412
|
-
const logger$
|
|
2451
|
+
const logger$7 = new Logger();
|
|
2413
2452
|
const ICE_PUBLIC_IP_GATHERING_TIMEOUT = 3 * 1000;
|
|
2414
2453
|
const ICE_RESTART_DELAY = 2 * 1000;
|
|
2415
2454
|
const browserName$1 = (_b$1 = adapter$2.browserDetails) === null || _b$1 === void 0 ? void 0 : _b$1.browser;
|
|
@@ -2457,30 +2496,30 @@ class P2pRtcManager {
|
|
|
2457
2496
|
mediaserverConfigTtlSeconds,
|
|
2458
2497
|
});
|
|
2459
2498
|
this.analytics = {
|
|
2460
|
-
|
|
2461
|
-
|
|
2499
|
+
camTrackEndedCount: 0,
|
|
2500
|
+
micTrackEndedCount: 0,
|
|
2462
2501
|
numIceConnected: 0,
|
|
2463
2502
|
numIceDisconnected: 0,
|
|
2464
2503
|
numIceFailed: 0,
|
|
2465
|
-
numIceRestart: 0,
|
|
2466
|
-
numIceNoPublicIpGathered: 0,
|
|
2467
|
-
numIceNoPublicIpGatheredIn3sec: 0,
|
|
2468
2504
|
numIceIpv6Seen: 0,
|
|
2469
|
-
numIceIpv6TeredoSeen: 0,
|
|
2470
2505
|
numIceIpv6SixToFour: 0,
|
|
2506
|
+
numIceIpv6TeredoSeen: 0,
|
|
2471
2507
|
numIceMdnsSeen: 0,
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2508
|
+
numIceNoPublicIpGathered: 0,
|
|
2509
|
+
numIceNoPublicIpGatheredIn3sec: 0,
|
|
2510
|
+
numIceRestart: 0,
|
|
2511
|
+
numNewPc: 0,
|
|
2475
2512
|
numPcOnAnswerFailure: 0,
|
|
2476
2513
|
numPcOnOfferFailure: 0,
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
P2PReplaceTrackNewTrackNotInStream: 0,
|
|
2480
|
-
P2POnTrackNoStream: 0,
|
|
2514
|
+
numPcSldFailure: 0,
|
|
2515
|
+
P2PAddIceCandidateFailure: 0,
|
|
2481
2516
|
P2PLocalNetworkFailed: 0,
|
|
2517
|
+
P2POffendingInitialOffer: 0,
|
|
2518
|
+
P2POnTrackNoStream: 0,
|
|
2482
2519
|
P2PRelayedIceCandidate: 0,
|
|
2483
|
-
|
|
2520
|
+
P2PReplaceTrackNewTrackNotInStream: 0,
|
|
2521
|
+
P2PReplaceTrackNoStream: 0,
|
|
2522
|
+
P2PStaleAnswerIgnored: 0,
|
|
2484
2523
|
};
|
|
2485
2524
|
}
|
|
2486
2525
|
numberOfPeerconnections() {
|
|
@@ -2490,7 +2529,7 @@ class P2pRtcManager {
|
|
|
2490
2529
|
return this._selfId === selfId && this._roomName === roomName && !isSfu;
|
|
2491
2530
|
}
|
|
2492
2531
|
addCameraStream(stream, { beforeEffectTracks = [] } = { beforeEffectTracks: [] }) {
|
|
2493
|
-
logger$
|
|
2532
|
+
logger$7.info("addCameraStream: [stream.id: %s]", stream.id);
|
|
2494
2533
|
if (stream === this._localCameraStream) {
|
|
2495
2534
|
return;
|
|
2496
2535
|
}
|
|
@@ -2533,7 +2572,7 @@ class P2pRtcManager {
|
|
|
2533
2572
|
};
|
|
2534
2573
|
}
|
|
2535
2574
|
addScreenshareStream(stream) {
|
|
2536
|
-
logger$
|
|
2575
|
+
logger$7.info("addScreenshareStream() [stream.id: %s]", stream.id);
|
|
2537
2576
|
if (stream === this._localScreenshareStream) {
|
|
2538
2577
|
return;
|
|
2539
2578
|
}
|
|
@@ -2546,7 +2585,7 @@ class P2pRtcManager {
|
|
|
2546
2585
|
this._addStreamToPeerConnections(stream);
|
|
2547
2586
|
}
|
|
2548
2587
|
replaceTrack(oldTrack, newTrack) {
|
|
2549
|
-
logger$
|
|
2588
|
+
logger$7.info("replaceTrack() [kind: %s, oldTrackId: %s, newTrackId: %s]", newTrack.kind, oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.id, newTrack.id);
|
|
2550
2589
|
if (newTrack.kind === "audio" && !trackAnnotations(newTrack).isEffectTrack) {
|
|
2551
2590
|
this._monitorAudioTrack(newTrack);
|
|
2552
2591
|
}
|
|
@@ -2560,7 +2599,7 @@ class P2pRtcManager {
|
|
|
2560
2599
|
this.disconnectAll();
|
|
2561
2600
|
}
|
|
2562
2601
|
disconnectAll() {
|
|
2563
|
-
logger$
|
|
2602
|
+
logger$7.info("disconnectAll()");
|
|
2564
2603
|
Object.keys(this.peerConnections).forEach((clientId) => {
|
|
2565
2604
|
this.disconnect(clientId);
|
|
2566
2605
|
});
|
|
@@ -2580,39 +2619,39 @@ class P2pRtcManager {
|
|
|
2580
2619
|
() => this._clearMediaServersRefresh(),
|
|
2581
2620
|
this._serverSocket.on(PROTOCOL_RESPONSES.MEDIASERVER_CONFIG, (data) => {
|
|
2582
2621
|
if (data.error) {
|
|
2583
|
-
logger$
|
|
2622
|
+
logger$7.warn("FETCH_MEDIASERVER_CONFIG failed:", data.error);
|
|
2584
2623
|
return;
|
|
2585
2624
|
}
|
|
2586
2625
|
this._updateAndScheduleMediaServersRefresh(data);
|
|
2587
2626
|
}),
|
|
2588
2627
|
this._serverSocket.on(RELAY_MESSAGES.READY_TO_RECEIVE_OFFER, (data) => {
|
|
2589
|
-
logger$
|
|
2628
|
+
logger$7.info(`Got ready_to_receive_offer from client ${data.clientId}`);
|
|
2590
2629
|
this._connect(data.clientId);
|
|
2591
2630
|
}),
|
|
2592
2631
|
this._serverSocket.on(RELAY_MESSAGES.ICE_CANDIDATE, (data) => {
|
|
2593
|
-
logger$
|
|
2632
|
+
logger$7.info(`Got ice_candidate from client ${data.clientId}`);
|
|
2594
2633
|
const session = this._getSession(data.clientId);
|
|
2595
2634
|
if (!session) {
|
|
2596
|
-
logger$
|
|
2635
|
+
logger$7.warn("No RTCPeerConnection on ICE_CANDIDATE", data);
|
|
2597
2636
|
return;
|
|
2598
2637
|
}
|
|
2599
2638
|
session.addIceCandidate(data.message);
|
|
2600
2639
|
}),
|
|
2601
2640
|
this._serverSocket.on(RELAY_MESSAGES.ICE_END_OF_CANDIDATES, (data) => {
|
|
2602
|
-
logger$
|
|
2641
|
+
logger$7.info(`Got end_of_ice_candidates from client ${data.clientId}`);
|
|
2603
2642
|
const session = this._getSession(data.clientId);
|
|
2604
2643
|
if (!session) {
|
|
2605
|
-
logger$
|
|
2644
|
+
logger$7.warn("No RTCPeerConnection on ICE_END_OF_CANDIDATES", data);
|
|
2606
2645
|
return;
|
|
2607
2646
|
}
|
|
2608
2647
|
session.addIceCandidate(null);
|
|
2609
2648
|
}),
|
|
2610
2649
|
this._serverSocket.on(RELAY_MESSAGES.SDP_OFFER, (data) => {
|
|
2611
2650
|
var _a, _b;
|
|
2612
|
-
logger$
|
|
2651
|
+
logger$7.info(`Got offer from client ${data.clientId}, isInitialOffer: ${Boolean(data.message.isInitialOffer)}`);
|
|
2613
2652
|
const session = this._getSession(data.clientId);
|
|
2614
2653
|
if (!session) {
|
|
2615
|
-
logger$
|
|
2654
|
+
logger$7.warn("No RTCPeerConnection on SDP_OFFER", data);
|
|
2616
2655
|
return;
|
|
2617
2656
|
}
|
|
2618
2657
|
const sdp = {
|
|
@@ -2624,7 +2663,7 @@ class P2pRtcManager {
|
|
|
2624
2663
|
session.pc.connectionState === "new" &&
|
|
2625
2664
|
session.pc.iceConnectionState === "new" &&
|
|
2626
2665
|
!session.connectionStatus) {
|
|
2627
|
-
logger$
|
|
2666
|
+
logger$7.info("We have asked for an initial offer, ignoring all others");
|
|
2628
2667
|
this.analytics.P2POffendingInitialOffer++;
|
|
2629
2668
|
rtcStats.sendEvent("P2POffendingInitialOffer", { clientId: session.clientId });
|
|
2630
2669
|
return;
|
|
@@ -2632,22 +2671,22 @@ class P2pRtcManager {
|
|
|
2632
2671
|
(_b = (_a = session
|
|
2633
2672
|
.handleOffer(sdp)
|
|
2634
2673
|
.then((answer) => {
|
|
2635
|
-
logger$
|
|
2674
|
+
logger$7.info(`Sending answer to client ${data.clientId}`);
|
|
2636
2675
|
this._emitServerEvent(RELAY_MESSAGES.SDP_ANSWER, {
|
|
2637
2676
|
receiverId: data.clientId,
|
|
2638
2677
|
message: answer,
|
|
2639
2678
|
});
|
|
2640
2679
|
})).catch) === null || _b === void 0 ? void 0 : _b.call(_a, (e) => {
|
|
2641
|
-
logger$
|
|
2680
|
+
logger$7.error(e);
|
|
2642
2681
|
this.analytics.numPcOnOfferFailure++;
|
|
2643
2682
|
});
|
|
2644
2683
|
}),
|
|
2645
2684
|
this._serverSocket.on(RELAY_MESSAGES.SDP_ANSWER, (data) => {
|
|
2646
2685
|
var _a, _b;
|
|
2647
|
-
logger$
|
|
2686
|
+
logger$7.info(`Got answer from client ${data.clientId}`);
|
|
2648
2687
|
const session = this._getSession(data.clientId);
|
|
2649
2688
|
if (!session) {
|
|
2650
|
-
logger$
|
|
2689
|
+
logger$7.warn("No RTCPeerConnection on SDP_ANSWER", data);
|
|
2651
2690
|
return;
|
|
2652
2691
|
}
|
|
2653
2692
|
const sdp = {
|
|
@@ -2655,7 +2694,7 @@ class P2pRtcManager {
|
|
|
2655
2694
|
type: data.message.type,
|
|
2656
2695
|
};
|
|
2657
2696
|
(_b = (_a = session.handleAnswer(sdp)) === null || _a === void 0 ? void 0 : _a.catch) === null || _b === void 0 ? void 0 : _b.call(_a, (e) => {
|
|
2658
|
-
logger$
|
|
2697
|
+
logger$7.warn("Could not set remote description from remote answer: ", e);
|
|
2659
2698
|
this.analytics.numPcOnAnswerFailure++;
|
|
2660
2699
|
});
|
|
2661
2700
|
}),
|
|
@@ -2743,12 +2782,12 @@ class P2pRtcManager {
|
|
|
2743
2782
|
});
|
|
2744
2783
|
}
|
|
2745
2784
|
catch (error) {
|
|
2746
|
-
logger$
|
|
2785
|
+
logger$7.error("Error during setting jitter buffer target:", error);
|
|
2747
2786
|
}
|
|
2748
2787
|
}
|
|
2749
2788
|
_emitServerEvent(eventName, data) {
|
|
2750
2789
|
if (this._closed) {
|
|
2751
|
-
logger$
|
|
2790
|
+
logger$7.warn("RtcManager closed. Will not send event", eventName, data);
|
|
2752
2791
|
return;
|
|
2753
2792
|
}
|
|
2754
2793
|
if (this._features.awaitJoinRoomFinished && !this._serverSocket.joinRoomFinished) {
|
|
@@ -2853,7 +2892,7 @@ class P2pRtcManager {
|
|
|
2853
2892
|
}
|
|
2854
2893
|
}
|
|
2855
2894
|
catch (error) {
|
|
2856
|
-
logger$
|
|
2895
|
+
logger$7.info("Error during parsing candidates! Error: ", { error });
|
|
2857
2896
|
}
|
|
2858
2897
|
break;
|
|
2859
2898
|
case "srflx":
|
|
@@ -2895,7 +2934,7 @@ class P2pRtcManager {
|
|
|
2895
2934
|
if (pc.iceConnectionState === "new" || !session.connectionStatus) {
|
|
2896
2935
|
return;
|
|
2897
2936
|
}
|
|
2898
|
-
logger$
|
|
2937
|
+
logger$7.info(`onnegotiationneeded client ${clientId}`);
|
|
2899
2938
|
this._negotiatePeerConnection({ clientId, session });
|
|
2900
2939
|
};
|
|
2901
2940
|
pc.ontrack = (event) => {
|
|
@@ -2920,7 +2959,7 @@ class P2pRtcManager {
|
|
|
2920
2959
|
};
|
|
2921
2960
|
pc.oniceconnectionstatechange = () => {
|
|
2922
2961
|
var _a;
|
|
2923
|
-
logger$
|
|
2962
|
+
logger$7.info(`iceConnectionState changed to ${pc.iceConnectionState} for session ${session.clientId}`);
|
|
2924
2963
|
let newStatus;
|
|
2925
2964
|
const currentStatus = session.connectionStatus;
|
|
2926
2965
|
switch (pc.iceConnectionState) {
|
|
@@ -2975,7 +3014,7 @@ class P2pRtcManager {
|
|
|
2975
3014
|
};
|
|
2976
3015
|
pc.onconnectionstatechange = () => {
|
|
2977
3016
|
var _a;
|
|
2978
|
-
logger$
|
|
3017
|
+
logger$7.info(`connectionState changed to ${pc.connectionState} for session ${session.clientId}`);
|
|
2979
3018
|
switch (pc.connectionState) {
|
|
2980
3019
|
case "connected":
|
|
2981
3020
|
(_a = session.registerConnected) === null || _a === void 0 ? void 0 : _a.call(session, {});
|
|
@@ -3032,7 +3071,7 @@ class P2pRtcManager {
|
|
|
3032
3071
|
_cleanup(clientId) {
|
|
3033
3072
|
const session = this._getSession(clientId);
|
|
3034
3073
|
if (!session) {
|
|
3035
|
-
logger$
|
|
3074
|
+
logger$7.warn("No RTCPeerConnection in RTCManager.disconnect()", clientId);
|
|
3036
3075
|
return;
|
|
3037
3076
|
}
|
|
3038
3077
|
this.removeRemoteClientMediaPrefs(session.clientId);
|
|
@@ -3060,7 +3099,7 @@ class P2pRtcManager {
|
|
|
3060
3099
|
if (!session.hasConnectedPeerConnection()) {
|
|
3061
3100
|
if (session.pc.connectionState === "closed")
|
|
3062
3101
|
return;
|
|
3063
|
-
logger$
|
|
3102
|
+
logger$7.info("Session doesn't have a connected PeerConnection, adding pending action!");
|
|
3064
3103
|
const promise = new Promise((resolve, reject) => {
|
|
3065
3104
|
const action = () => __awaiter(this, void 0, void 0, function* () {
|
|
3066
3105
|
try {
|
|
@@ -3079,7 +3118,7 @@ class P2pRtcManager {
|
|
|
3079
3118
|
promises.push(session.replaceTrack(oldTrack, newTrack));
|
|
3080
3119
|
});
|
|
3081
3120
|
return Promise.all(promises).catch((error) => {
|
|
3082
|
-
logger$
|
|
3121
|
+
logger$7.error(String(error));
|
|
3083
3122
|
});
|
|
3084
3123
|
}
|
|
3085
3124
|
_removeStreamFromPeerConnections(stream) {
|
|
@@ -3127,7 +3166,7 @@ class P2pRtcManager {
|
|
|
3127
3166
|
let session = this._getSession(clientId);
|
|
3128
3167
|
let initialBandwidth = (session && session.bandwidth) || 0;
|
|
3129
3168
|
if (session) {
|
|
3130
|
-
logger$
|
|
3169
|
+
logger$7.warn("Replacing peer session", clientId);
|
|
3131
3170
|
this._cleanup(clientId);
|
|
3132
3171
|
}
|
|
3133
3172
|
else {
|
|
@@ -3198,13 +3237,13 @@ class P2pRtcManager {
|
|
|
3198
3237
|
})));
|
|
3199
3238
|
}
|
|
3200
3239
|
catch (error) {
|
|
3201
|
-
logger$
|
|
3240
|
+
logger$7.error("Error during setting setCodecPreferences:", error);
|
|
3202
3241
|
}
|
|
3203
3242
|
});
|
|
3204
3243
|
}
|
|
3205
3244
|
_negotiatePeerConnection({ clientId, session, constraints, isInitialOffer = false, }) {
|
|
3206
3245
|
if (!session) {
|
|
3207
|
-
logger$
|
|
3246
|
+
logger$7.warn("No RTCPeerConnection in negotiatePeerConnection()", clientId);
|
|
3208
3247
|
return;
|
|
3209
3248
|
}
|
|
3210
3249
|
const pc = session.pc;
|
|
@@ -3234,7 +3273,7 @@ class P2pRtcManager {
|
|
|
3234
3273
|
offer.sdp = cleanSdp(offer.sdp);
|
|
3235
3274
|
pc.setLocalDescription(offer)
|
|
3236
3275
|
.catch((e) => {
|
|
3237
|
-
logger$
|
|
3276
|
+
logger$7.warn("RTCPeerConnection.setLocalDescription() failed with local offer", e);
|
|
3238
3277
|
this.analytics.numPcSldFailure++;
|
|
3239
3278
|
throw e;
|
|
3240
3279
|
})
|
|
@@ -3246,7 +3285,7 @@ class P2pRtcManager {
|
|
|
3246
3285
|
type: offer.type,
|
|
3247
3286
|
isInitialOffer,
|
|
3248
3287
|
};
|
|
3249
|
-
logger$
|
|
3288
|
+
logger$7.info(`sending offer to client ${clientId}`);
|
|
3250
3289
|
this._emitServerEvent(RELAY_MESSAGES.SDP_OFFER, {
|
|
3251
3290
|
receiverId: clientId,
|
|
3252
3291
|
message,
|
|
@@ -3254,7 +3293,7 @@ class P2pRtcManager {
|
|
|
3254
3293
|
});
|
|
3255
3294
|
})
|
|
3256
3295
|
.catch((e) => {
|
|
3257
|
-
logger$
|
|
3296
|
+
logger$7.warn("RTCPeerConnection.createOffer() failed to create local offer", e);
|
|
3258
3297
|
}));
|
|
3259
3298
|
}
|
|
3260
3299
|
_withForcedRenegotiation(session, action) {
|
|
@@ -3296,7 +3335,7 @@ class P2pRtcManager {
|
|
|
3296
3335
|
return bandwidth;
|
|
3297
3336
|
}
|
|
3298
3337
|
acceptNewStream({ streamId, clientId }) {
|
|
3299
|
-
logger$
|
|
3338
|
+
logger$7.info("acceptNewStream() [streamId: %s}, clientId: %s]", streamId, clientId);
|
|
3300
3339
|
let session = this._getSession(clientId);
|
|
3301
3340
|
if (session && streamId !== clientId) {
|
|
3302
3341
|
return session;
|
|
@@ -3319,7 +3358,7 @@ class P2pRtcManager {
|
|
|
3319
3358
|
return session;
|
|
3320
3359
|
}
|
|
3321
3360
|
disconnect(clientId) {
|
|
3322
|
-
logger$
|
|
3361
|
+
logger$7.info("disconnect() [clientId: %s]", clientId);
|
|
3323
3362
|
this._cleanup(clientId);
|
|
3324
3363
|
this._changeBandwidthForAllClients(false);
|
|
3325
3364
|
const numPeers = this.numberOfPeerconnections();
|
|
@@ -3356,7 +3395,7 @@ class P2pRtcManager {
|
|
|
3356
3395
|
stopOrResumeVideo() {
|
|
3357
3396
|
}
|
|
3358
3397
|
removeScreenshareStream(stream, { requestedByClientId } = {}) {
|
|
3359
|
-
logger$
|
|
3398
|
+
logger$7.info("removeScreenshareStream() [stream.id: %s, requestedByClientId: %s]", stream.id, requestedByClientId);
|
|
3360
3399
|
this._removeStreamFromPeerConnections(stream);
|
|
3361
3400
|
this._emitServerEvent(PROTOCOL_REQUESTS.STOP_SCREENSHARE, { streamId: stream.id, requestedByClientId });
|
|
3362
3401
|
delete this._localScreenshareStream;
|
|
@@ -3477,7 +3516,7 @@ const defaultParams = {
|
|
|
3477
3516
|
scoreFormula: "300 * bands[1].avg / Math.max(bands[1].avg + bands[0].avg, 0.01)",
|
|
3478
3517
|
outFormula: "score",
|
|
3479
3518
|
};
|
|
3480
|
-
const logger$
|
|
3519
|
+
const logger$6 = new Logger();
|
|
3481
3520
|
function createMicAnalyser({ micTrack, params, onScoreUpdated, }) {
|
|
3482
3521
|
const audioCtx = new AudioContext();
|
|
3483
3522
|
let analyser = null;
|
|
@@ -3499,7 +3538,7 @@ function createMicAnalyser({ micTrack, params, onScoreUpdated, }) {
|
|
|
3499
3538
|
lastTrackWasOurs = true;
|
|
3500
3539
|
}
|
|
3501
3540
|
catch (ex) {
|
|
3502
|
-
logger$
|
|
3541
|
+
logger$6.warn("unable to fetch new track for colocation speaker analysis, using current", ex);
|
|
3503
3542
|
}
|
|
3504
3543
|
}
|
|
3505
3544
|
lastTrack = track;
|
|
@@ -3586,7 +3625,7 @@ function createMicAnalyser({ micTrack, params, onScoreUpdated, }) {
|
|
|
3586
3625
|
};
|
|
3587
3626
|
}
|
|
3588
3627
|
|
|
3589
|
-
const logger$
|
|
3628
|
+
const logger$5 = new Logger();
|
|
3590
3629
|
const MEDIA_QUALITY = Object.freeze({
|
|
3591
3630
|
ok: "ok",
|
|
3592
3631
|
warning: "warning",
|
|
@@ -3656,7 +3695,7 @@ class VegaMediaQualityMonitor extends EventEmitter {
|
|
|
3656
3695
|
}
|
|
3657
3696
|
addProducer(clientId, producerId) {
|
|
3658
3697
|
if (!clientId || !producerId || !(typeof clientId === "string" && typeof producerId === "string")) {
|
|
3659
|
-
logger$
|
|
3698
|
+
logger$5.warn("Missing clientId or producerId");
|
|
3660
3699
|
return;
|
|
3661
3700
|
}
|
|
3662
3701
|
if (!this._producers[clientId]) {
|
|
@@ -3672,7 +3711,7 @@ class VegaMediaQualityMonitor extends EventEmitter {
|
|
|
3672
3711
|
}
|
|
3673
3712
|
addConsumer(clientId, consumerId) {
|
|
3674
3713
|
if (!clientId || !consumerId) {
|
|
3675
|
-
logger$
|
|
3714
|
+
logger$5.warn("Missing clientId or consumerId");
|
|
3676
3715
|
return;
|
|
3677
3716
|
}
|
|
3678
3717
|
if (!this._producers[clientId]) {
|
|
@@ -3690,14 +3729,14 @@ class VegaMediaQualityMonitor extends EventEmitter {
|
|
|
3690
3729
|
if (!Array.isArray(score) ||
|
|
3691
3730
|
score.length === 0 ||
|
|
3692
3731
|
score.some((s) => !s || !s.hasOwnProperty("score") || typeof s.score !== "number" || isNaN(s.score))) {
|
|
3693
|
-
logger$
|
|
3732
|
+
logger$5.warn("Unexpected producer score format");
|
|
3694
3733
|
return;
|
|
3695
3734
|
}
|
|
3696
3735
|
this._producers[clientId][producerId] = { kind, score: this._calcAvgProducerScore(score.map((s) => s.score)) };
|
|
3697
3736
|
}
|
|
3698
3737
|
addConsumerScore(clientId, consumerId, kind, score) {
|
|
3699
3738
|
if (!score || !score.hasOwnProperty("producerScores") || !Array.isArray(score.producerScores)) {
|
|
3700
|
-
logger$
|
|
3739
|
+
logger$5.warn("Unexpected consumer score format");
|
|
3701
3740
|
return;
|
|
3702
3741
|
}
|
|
3703
3742
|
this._producers[clientId][consumerId] = { kind, score: this._calcAvgProducerScore(score.producerScores) };
|
|
@@ -3734,12 +3773,13 @@ class VegaMediaQualityMonitor extends EventEmitter {
|
|
|
3734
3773
|
}
|
|
3735
3774
|
}
|
|
3736
3775
|
catch (error) {
|
|
3737
|
-
logger$
|
|
3776
|
+
logger$5.error(error);
|
|
3738
3777
|
return 0;
|
|
3739
3778
|
}
|
|
3740
3779
|
}
|
|
3741
3780
|
}
|
|
3742
3781
|
|
|
3782
|
+
const logger$4 = new Logger();
|
|
3743
3783
|
function getLayers({ width, height, }, { numberOfActiveVideos, numberOfTemporalLayers, uncappedSingleRemoteVideoOn, }) {
|
|
3744
3784
|
const maxSide = Math.max(width, height);
|
|
3745
3785
|
let spatialLayer = maxSide >= 480 ? (maxSide >= 960 ? 2 : 1) : 0;
|
|
@@ -3759,6 +3799,27 @@ function getLayers({ width, height, }, { numberOfActiveVideos, numberOfTemporalL
|
|
|
3759
3799
|
}
|
|
3760
3800
|
return { spatialLayer, temporalLayer };
|
|
3761
3801
|
}
|
|
3802
|
+
function createMetricStats() {
|
|
3803
|
+
return { count: 0, sum: 0 };
|
|
3804
|
+
}
|
|
3805
|
+
function recordMetricSample(stats, value) {
|
|
3806
|
+
stats.count++;
|
|
3807
|
+
stats.sum += value;
|
|
3808
|
+
}
|
|
3809
|
+
function aggregateMetricStats(stats) {
|
|
3810
|
+
return {
|
|
3811
|
+
count: stats.count,
|
|
3812
|
+
avg: stats.count ? Math.round(stats.sum / stats.count) : undefined,
|
|
3813
|
+
};
|
|
3814
|
+
}
|
|
3815
|
+
function getPacketWeightedFractionLost(reports) {
|
|
3816
|
+
const weights = reports.map(({ packetRate }) => (packetRate && packetRate > 0 ? packetRate : 0));
|
|
3817
|
+
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
3818
|
+
if (!totalWeight) {
|
|
3819
|
+
return reports.reduce((sum, { fractionLost }) => sum + (fractionLost || 0), 0) / (reports.length || 1);
|
|
3820
|
+
}
|
|
3821
|
+
return (reports.reduce((sum, { fractionLost }, index) => sum + (fractionLost || 0) * weights[index], 0) / totalWeight);
|
|
3822
|
+
}
|
|
3762
3823
|
function getNumberOfActiveVideos(consumers) {
|
|
3763
3824
|
let numberOfActiveVideos = 0;
|
|
3764
3825
|
consumers.forEach((c) => {
|
|
@@ -3774,6 +3835,51 @@ function getNumberOfTemporalLayers(consumer) {
|
|
|
3774
3835
|
var _a, _b, _c;
|
|
3775
3836
|
return /T3/.test(((_c = (_b = (_a = consumer._rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.scalabilityMode) || "") ? 3 : 2;
|
|
3776
3837
|
}
|
|
3838
|
+
const SCALABILITY_MODE_REGEX = /^([LS])([1-9]\d?)T([1-9]\d?)(_KEY)?$/;
|
|
3839
|
+
function getTopSpatialLayer(encodings) {
|
|
3840
|
+
if (encodings.length > 1)
|
|
3841
|
+
return encodings.length - 1;
|
|
3842
|
+
const { scalabilityMode } = encodings[0];
|
|
3843
|
+
const match = SCALABILITY_MODE_REGEX.exec(scalabilityMode || "");
|
|
3844
|
+
if (!match) {
|
|
3845
|
+
if (scalabilityMode) {
|
|
3846
|
+
logger$4.warn("getTopSpatialLayer() unrecognized scalabilityMode %s, using a single spatial layer", scalabilityMode);
|
|
3847
|
+
}
|
|
3848
|
+
return 0;
|
|
3849
|
+
}
|
|
3850
|
+
const spatialLayers = Number(match[2]) || 1;
|
|
3851
|
+
return spatialLayers - 1;
|
|
3852
|
+
}
|
|
3853
|
+
function getInitialHighestPreferredLayer(encodings) {
|
|
3854
|
+
return Math.min(getTopSpatialLayer(encodings), INITIAL_HIGHEST_PREFERRED_LAYER);
|
|
3855
|
+
}
|
|
3856
|
+
function getReducedSvcEncodingParams(originalScalabilityMode, spatialLayer, originalMaxBitrate) {
|
|
3857
|
+
const match = SCALABILITY_MODE_REGEX.exec(originalScalabilityMode || "");
|
|
3858
|
+
if (!match)
|
|
3859
|
+
return undefined;
|
|
3860
|
+
const [, mode, originalSpatialLayers, temporalLayers, key = ""] = match;
|
|
3861
|
+
const topLayerIndex = Number(originalSpatialLayers) - 1;
|
|
3862
|
+
if (topLayerIndex === 0)
|
|
3863
|
+
return undefined;
|
|
3864
|
+
const preferredLayerIndex = Math.min(Math.max(spatialLayer, 0), topLayerIndex);
|
|
3865
|
+
const onlyLowestLayerPreferred = preferredLayerIndex === 0;
|
|
3866
|
+
const middleLayerPreferred = preferredLayerIndex > 0 && preferredLayerIndex < topLayerIndex;
|
|
3867
|
+
let maxBitrate = originalMaxBitrate;
|
|
3868
|
+
if (onlyLowestLayerPreferred) {
|
|
3869
|
+
maxBitrate = Math.min(LOWEST_SVC_LAYER_MAX_BITRATE, originalMaxBitrate !== null && originalMaxBitrate !== void 0 ? originalMaxBitrate : Infinity);
|
|
3870
|
+
}
|
|
3871
|
+
else if (middleLayerPreferred) {
|
|
3872
|
+
maxBitrate = Math.min(MIDDLE_SVC_LAYER_MAX_BITRATE, originalMaxBitrate !== null && originalMaxBitrate !== void 0 ? originalMaxBitrate : Infinity);
|
|
3873
|
+
}
|
|
3874
|
+
const scalabilityMode = onlyLowestLayerPreferred
|
|
3875
|
+
? `L1T${temporalLayers}`
|
|
3876
|
+
: `${mode}${preferredLayerIndex + 1}T${temporalLayers}${key}`;
|
|
3877
|
+
return {
|
|
3878
|
+
scalabilityMode,
|
|
3879
|
+
scaleResolutionDownBy: Math.pow(2, (topLayerIndex - preferredLayerIndex)),
|
|
3880
|
+
maxBitrate,
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3777
3883
|
function addProducerCpuOveruseWatch({ producer, onOveruse }) {
|
|
3778
3884
|
var _a;
|
|
3779
3885
|
const encodings = (_a = producer.rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings;
|
|
@@ -4230,10 +4336,12 @@ const RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS = 3500;
|
|
|
4230
4336
|
const RESTARTICE_ERROR_MAX_RETRY_COUNT = 5;
|
|
4231
4337
|
const OUTBOUND_CAM_OUTBOUND_STREAM_ID = uuid.v4();
|
|
4232
4338
|
const OUTBOUND_SCREEN_OUTBOUND_STREAM_ID = uuid.v4();
|
|
4339
|
+
const getFrameArea = (frameSize) => { var _a, _b; return ((_a = frameSize === null || frameSize === void 0 ? void 0 : frameSize.width) !== null && _a !== void 0 ? _a : 0) * ((_b = frameSize === null || frameSize === void 0 ? void 0 : frameSize.height) !== null && _b !== void 0 ? _b : 0); };
|
|
4233
4340
|
if (browserName === "chrome")
|
|
4234
4341
|
window.document.addEventListener("beforeunload", () => (unloading = true));
|
|
4235
4342
|
class VegaRtcManager {
|
|
4236
4343
|
constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim }) {
|
|
4344
|
+
var _a;
|
|
4237
4345
|
const { session, iceServers, turnServers, sfuServer, mediaserverConfigTtlSeconds } = room;
|
|
4238
4346
|
this._selfId = selfId;
|
|
4239
4347
|
this._room = room;
|
|
@@ -4266,6 +4374,11 @@ class VegaRtcManager {
|
|
|
4266
4374
|
this._micScoreProducer = null;
|
|
4267
4375
|
this._micScoreProducerPromise = null;
|
|
4268
4376
|
this._webcamProducer = null;
|
|
4377
|
+
this._webcamProducerOriginalSvcScalabilityMode = undefined;
|
|
4378
|
+
this._webcamProducerOriginalSvcMaxBitrate = undefined;
|
|
4379
|
+
this._webcamProducerHighestPreferredLayer = undefined;
|
|
4380
|
+
this._highestPreferredLayerUpdate = null;
|
|
4381
|
+
this._highestPreferredLayerUpdatePending = false;
|
|
4269
4382
|
this._webcamProducerPromise = null;
|
|
4270
4383
|
this._webcamPaused = false;
|
|
4271
4384
|
this._screenVideoProducer = null;
|
|
@@ -4305,35 +4418,49 @@ class VegaRtcManager {
|
|
|
4305
4418
|
offlineDetectedAt: null,
|
|
4306
4419
|
onBrowserOffline: () => this._sfuZombieOnOffline(),
|
|
4307
4420
|
};
|
|
4308
|
-
|
|
4309
|
-
window.addEventListener("offline", this._sfuZombie.onBrowserOffline);
|
|
4310
|
-
}
|
|
4421
|
+
(_a = window === null || window === void 0 ? void 0 : window.addEventListener) === null || _a === void 0 ? void 0 : _a.call(window, "offline", this._sfuZombie.onBrowserOffline);
|
|
4311
4422
|
this.analytics = {
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
vegaJoinWithoutVegaConnection: 0,
|
|
4316
|
-
vegaCreateTransportWithoutVegaConnection: 0,
|
|
4317
|
-
vegaIceRestarts: 0,
|
|
4318
|
-
vegaIceRestartMissingTransport: 0,
|
|
4319
|
-
vegaIceRestartWrongTransportId: 0,
|
|
4320
|
-
vegaReplaceTrackNoProducerNoEnabledTrack: 0,
|
|
4321
|
-
vegaMicProducerFailed: 0,
|
|
4322
|
-
vegaWebcamProducerFailed: 0,
|
|
4323
|
-
vegaConsumerCreationFailed: 0,
|
|
4324
|
-
vegaScreenVideoProducerFailed: 0,
|
|
4325
|
-
vegaScreenAudioProducerFailed: 0,
|
|
4326
|
-
vegaMicProducerClosed: 0,
|
|
4327
|
-
micTrackEndedCount: 0,
|
|
4423
|
+
avgPreferredLayerSwitchLatencyMs: undefined,
|
|
4424
|
+
camOutboundCongestedFraction: undefined,
|
|
4425
|
+
camOutboundRttInflationMs: undefined,
|
|
4328
4426
|
camTrackEndedCount: 0,
|
|
4329
|
-
|
|
4427
|
+
highestPreferredLayerChangeCounts: {},
|
|
4428
|
+
micTrackEndedCount: 0,
|
|
4429
|
+
numHighestPreferredLayerChanges: 0,
|
|
4330
4430
|
numIceConnected: 0,
|
|
4331
4431
|
numIceDisconnected: 0,
|
|
4332
4432
|
numIceFailed: 0,
|
|
4433
|
+
numNewPc: 0,
|
|
4434
|
+
numPreferredSpatialLayerChanges: 0,
|
|
4435
|
+
preferredSpatialLayerChangeCounts: {},
|
|
4333
4436
|
sfuMsFromOfflineToClose: 0,
|
|
4334
|
-
sfuOfflineWhileConnectedCount: 0,
|
|
4335
4437
|
sfuOfflineToCloseCount: 0,
|
|
4438
|
+
sfuOfflineWhileConnectedCount: 0,
|
|
4439
|
+
vegaConsumerCreationFailed: 0,
|
|
4440
|
+
vegaCreateTransportWithoutVegaConnection: 0,
|
|
4441
|
+
vegaIceRestartMissingTransport: 0,
|
|
4442
|
+
vegaIceRestarts: 0,
|
|
4443
|
+
vegaIceRestartWrongTransportId: 0,
|
|
4444
|
+
vegaJoinFailed: 0,
|
|
4445
|
+
vegaJoinWithoutVegaConnection: 0,
|
|
4446
|
+
vegaMicProducerClosed: 0,
|
|
4447
|
+
vegaMicProducerFailed: 0,
|
|
4448
|
+
vegaReplaceTrackNoProducerNoEnabledTrack: 0,
|
|
4449
|
+
vegaRequestTimeout: 0,
|
|
4450
|
+
vegaScreenAudioProducerFailed: 0,
|
|
4451
|
+
vegaScreenVideoProducerFailed: 0,
|
|
4452
|
+
vegaUnknownResponse: 0,
|
|
4453
|
+
vegaWebcamProducerFailed: 0,
|
|
4336
4454
|
};
|
|
4455
|
+
this._preferredLayerSwitchLatencyStats = createMetricStats();
|
|
4456
|
+
this._preferredLayerSwitchWatches = new Map();
|
|
4457
|
+
this._lastSeenRemoteReportTimestamps = new Map();
|
|
4458
|
+
this._camOutboundCandidatePairId = undefined;
|
|
4459
|
+
this._camOutboundSamplesToSkip = 0;
|
|
4460
|
+
this._camOutboundPathRtt = { sumMs: 0, count: 0, minMs: undefined };
|
|
4461
|
+
this._camOutboundRttInflation = { sumMs: 0, count: 0 };
|
|
4462
|
+
this._camOutboundSamples = { total: 0, congested: 0 };
|
|
4463
|
+
this._statsSubscription = null;
|
|
4337
4464
|
}
|
|
4338
4465
|
_updateAndScheduleMediaServersRefresh({ iceServers, turnServers, sfuServer, mediaserverConfigTtlSeconds, }) {
|
|
4339
4466
|
var _a, _b, _c, _d;
|
|
@@ -4503,7 +4630,6 @@ class VegaRtcManager {
|
|
|
4503
4630
|
this._sendTransport = null;
|
|
4504
4631
|
this._receiveTransport = null;
|
|
4505
4632
|
this._streamIdToVideoConsumerId.clear();
|
|
4506
|
-
this._streamIdToVideoResolution.clear();
|
|
4507
4633
|
if (this._reconnect) {
|
|
4508
4634
|
this._reconnectTimeOut = setTimeout(() => this._connect(), 1000);
|
|
4509
4635
|
}
|
|
@@ -4926,6 +5052,14 @@ class VegaRtcManager {
|
|
|
4926
5052
|
const simulcastLayer3ShouldBeActive = !this._cpuOveruseDetected;
|
|
4927
5053
|
const params = this._webcamProducer.rtpParameters;
|
|
4928
5054
|
if (((_a = params === null || params === void 0 ? void 0 : params.encodings) === null || _a === void 0 ? void 0 : _a.length) === 3) {
|
|
5055
|
+
if (this._features.sfuHighestPreferredLayerTrackingOn) {
|
|
5056
|
+
this._syncWebcamEncoderToHighestPreferredLayer();
|
|
5057
|
+
rtcStats.sendEvent("simulcast_layer_activation_changed", {
|
|
5058
|
+
layerIndex: 2,
|
|
5059
|
+
active: simulcastLayer3ShouldBeActive,
|
|
5060
|
+
});
|
|
5061
|
+
return;
|
|
5062
|
+
}
|
|
4929
5063
|
const targetMaxSpatialLayer = simulcastLayer3ShouldBeActive ? 2 : 1;
|
|
4930
5064
|
if (this._webcamProducer.maxSpatialLayer !== targetMaxSpatialLayer) {
|
|
4931
5065
|
this._webcamProducer.setMaxSpatialLayer(targetMaxSpatialLayer);
|
|
@@ -4948,7 +5082,7 @@ class VegaRtcManager {
|
|
|
4948
5082
|
return;
|
|
4949
5083
|
}
|
|
4950
5084
|
this._webcamProducerPromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
4951
|
-
var _a;
|
|
5085
|
+
var _a, _b, _c;
|
|
4952
5086
|
try {
|
|
4953
5087
|
const currentPaused = this._webcamPaused;
|
|
4954
5088
|
if (!this._sendTransport) {
|
|
@@ -4974,6 +5108,16 @@ class VegaRtcManager {
|
|
|
4974
5108
|
})
|
|
4975
5109
|
: () => { };
|
|
4976
5110
|
this._webcamProducer = producer;
|
|
5111
|
+
if (this._features.sfuHighestPreferredLayerTrackingOn) {
|
|
5112
|
+
const originalWebcamEncodings = (_b = (_a = producer.rtpSender) === null || _a === void 0 ? void 0 : _a.getParameters()) === null || _b === void 0 ? void 0 : _b.encodings;
|
|
5113
|
+
const svcEncoding = (originalWebcamEncodings === null || originalWebcamEncodings === void 0 ? void 0 : originalWebcamEncodings.length) === 1 ? originalWebcamEncodings[0] : undefined;
|
|
5114
|
+
this._webcamProducerOriginalSvcScalabilityMode = svcEncoding === null || svcEncoding === void 0 ? void 0 : svcEncoding.scalabilityMode;
|
|
5115
|
+
this._webcamProducerOriginalSvcMaxBitrate = svcEncoding === null || svcEncoding === void 0 ? void 0 : svcEncoding.maxBitrate;
|
|
5116
|
+
this._webcamProducerHighestPreferredLayer = (originalWebcamEncodings === null || originalWebcamEncodings === void 0 ? void 0 : originalWebcamEncodings.length)
|
|
5117
|
+
? getInitialHighestPreferredLayer(originalWebcamEncodings)
|
|
5118
|
+
: undefined;
|
|
5119
|
+
this._syncWebcamEncoderToHighestPreferredLayer();
|
|
5120
|
+
}
|
|
4977
5121
|
this._qualityMonitor.addProducer(this._selfId, producer.id);
|
|
4978
5122
|
producer.observer.once("close", () => {
|
|
4979
5123
|
var _a;
|
|
@@ -4981,11 +5125,16 @@ class VegaRtcManager {
|
|
|
4981
5125
|
if (producer.appData.localClosed)
|
|
4982
5126
|
(_a = this._vegaConnection) === null || _a === void 0 ? void 0 : _a.message("closeProducers", { producerIds: [producer.id] });
|
|
4983
5127
|
cleanUpCpuWatch();
|
|
5128
|
+
this._stopWebcamStatsSubscription();
|
|
4984
5129
|
this._webcamProducer = null;
|
|
5130
|
+
this._webcamProducerOriginalSvcScalabilityMode = undefined;
|
|
5131
|
+
this._webcamProducerOriginalSvcMaxBitrate = undefined;
|
|
5132
|
+
this._webcamProducerHighestPreferredLayer = undefined;
|
|
4985
5133
|
this._webcamProducerPromise = null;
|
|
4986
5134
|
this._qualityMonitor.removeProducer(this._selfId, producer.id);
|
|
4987
5135
|
});
|
|
4988
|
-
|
|
5136
|
+
this._startWebcamStatsSubscription();
|
|
5137
|
+
if (this._webcamTrack && this._webcamTrack !== ((_c = this._webcamProducer) === null || _c === void 0 ? void 0 : _c.track)) {
|
|
4989
5138
|
this._webcamProducerPromise = null;
|
|
4990
5139
|
this._replaceWebcamTrack();
|
|
4991
5140
|
}
|
|
@@ -5002,6 +5151,9 @@ class VegaRtcManager {
|
|
|
5002
5151
|
if (!this._webcamTrack) {
|
|
5003
5152
|
this._stopProducer(this._webcamProducer);
|
|
5004
5153
|
this._webcamProducer = null;
|
|
5154
|
+
this._webcamProducerOriginalSvcScalabilityMode = undefined;
|
|
5155
|
+
this._webcamProducerOriginalSvcMaxBitrate = undefined;
|
|
5156
|
+
this._webcamProducerHighestPreferredLayer = undefined;
|
|
5005
5157
|
}
|
|
5006
5158
|
}
|
|
5007
5159
|
}))();
|
|
@@ -5253,6 +5405,7 @@ class VegaRtcManager {
|
|
|
5253
5405
|
if (clientState) {
|
|
5254
5406
|
clientState.hasAcceptedWebcamStream = false;
|
|
5255
5407
|
clientState.hasAcceptedScreenStream = false;
|
|
5408
|
+
[clientState.webcamStream, clientState.screenStream].forEach((stream) => stream && this._streamIdToVideoResolution.delete(stream.id));
|
|
5256
5409
|
this._syncIncomingStreamsWithPWA(clientId);
|
|
5257
5410
|
}
|
|
5258
5411
|
if (eventClaim) {
|
|
@@ -5376,6 +5529,9 @@ class VegaRtcManager {
|
|
|
5376
5529
|
if (this._webcamProducer && !this._webcamProducer.closed && this._webcamProducer.track === track) {
|
|
5377
5530
|
this._stopProducer(this._webcamProducer);
|
|
5378
5531
|
this._webcamProducer = null;
|
|
5532
|
+
this._webcamProducerOriginalSvcScalabilityMode = undefined;
|
|
5533
|
+
this._webcamProducerOriginalSvcMaxBitrate = undefined;
|
|
5534
|
+
this._webcamProducerHighestPreferredLayer = undefined;
|
|
5379
5535
|
this._webcamTrack = null;
|
|
5380
5536
|
}
|
|
5381
5537
|
}
|
|
@@ -5403,14 +5559,18 @@ class VegaRtcManager {
|
|
|
5403
5559
|
this._syncIncomingStreamsWithPWA(clientId);
|
|
5404
5560
|
}
|
|
5405
5561
|
updateStreamResolution(streamId, _ignored, { width, height, }) {
|
|
5406
|
-
var _a, _b, _c;
|
|
5407
5562
|
logger$2.info("updateStreamResolution()", { streamId, width, height });
|
|
5563
|
+
this._streamIdToVideoResolution.set(streamId, { width, height });
|
|
5564
|
+
this._applyStreamResolution(streamId);
|
|
5565
|
+
}
|
|
5566
|
+
_applyStreamResolution(streamId) {
|
|
5567
|
+
var _a, _b, _c;
|
|
5568
|
+
const resolution = this._streamIdToVideoResolution.get(streamId);
|
|
5408
5569
|
const consumerId = this._streamIdToVideoConsumerId.get(streamId);
|
|
5409
5570
|
const consumer = this._consumers.get(consumerId);
|
|
5410
|
-
if (!consumer)
|
|
5411
|
-
this._streamIdToVideoResolution.set(streamId, { width, height });
|
|
5571
|
+
if (!resolution || !consumer)
|
|
5412
5572
|
return;
|
|
5413
|
-
}
|
|
5573
|
+
const { width, height } = resolution;
|
|
5414
5574
|
updateRenderedDimensions((_a = consumer.track) === null || _a === void 0 ? void 0 : _a.id, { width, height, time: Date.now() });
|
|
5415
5575
|
const numberOfActiveVideos = getNumberOfActiveVideos(this._consumers);
|
|
5416
5576
|
const numberOfTemporalLayers = getNumberOfTemporalLayers(consumer);
|
|
@@ -5420,6 +5580,12 @@ class VegaRtcManager {
|
|
|
5420
5580
|
uncappedSingleRemoteVideoOn: (_b = this._features) === null || _b === void 0 ? void 0 : _b.uncappedSingleRemoteVideoOn,
|
|
5421
5581
|
});
|
|
5422
5582
|
if (consumer.appData.spatialLayer !== spatialLayer || consumer.appData.temporalLayer !== temporalLayer) {
|
|
5583
|
+
const previousSpatialLayer = consumer.appData.spatialLayer;
|
|
5584
|
+
const spatialLayerChanged = previousSpatialLayer !== spatialLayer;
|
|
5585
|
+
if (spatialLayerChanged) {
|
|
5586
|
+
this.analytics.numPreferredSpatialLayerChanges++;
|
|
5587
|
+
this._incrementHistogramCount(this.analytics.preferredSpatialLayerChangeCounts, `${previousSpatialLayer}->${spatialLayer}`);
|
|
5588
|
+
}
|
|
5423
5589
|
consumer.appData.spatialLayer = spatialLayer;
|
|
5424
5590
|
consumer.appData.temporalLayer = temporalLayer;
|
|
5425
5591
|
(_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.message("setConsumersPreferredLayers", {
|
|
@@ -5427,22 +5593,191 @@ class VegaRtcManager {
|
|
|
5427
5593
|
spatialLayer,
|
|
5428
5594
|
temporalLayer,
|
|
5429
5595
|
});
|
|
5596
|
+
if (spatialLayerChanged) {
|
|
5597
|
+
this._watchForPreferredLayerSwitch(consumerId, consumer, {
|
|
5598
|
+
toHigherLayer: spatialLayer > previousSpatialLayer,
|
|
5599
|
+
});
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
_watchForPreferredLayerSwitch(consumerId, consumer, { toHigherLayer }) {
|
|
5604
|
+
const previousWatch = this._preferredLayerSwitchWatches.get(consumerId);
|
|
5605
|
+
if (!previousWatch && this._preferredLayerSwitchWatches.size >= MAX_CONCURRENT_PREFERRED_LAYER_SWITCH_WATCHES) {
|
|
5606
|
+
return;
|
|
5607
|
+
}
|
|
5608
|
+
previousWatch === null || previousWatch === void 0 ? void 0 : previousWatch.stop();
|
|
5609
|
+
let stopped = false;
|
|
5610
|
+
let intervalId;
|
|
5611
|
+
const stop = () => {
|
|
5612
|
+
if (stopped)
|
|
5613
|
+
return;
|
|
5614
|
+
stopped = true;
|
|
5615
|
+
clearInterval(intervalId);
|
|
5616
|
+
clearTimeout(timeoutId);
|
|
5617
|
+
this._preferredLayerSwitchWatches.delete(consumerId);
|
|
5618
|
+
};
|
|
5619
|
+
this._preferredLayerSwitchWatches.set(consumerId, { stop });
|
|
5620
|
+
const timeoutId = setTimeout(stop, PREFERRED_LAYER_SWITCH_WATCH_TIMEOUT_MS);
|
|
5621
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
5622
|
+
const sentAt = Date.now();
|
|
5623
|
+
const baseline = yield this._getConsumerFrameSize(consumer);
|
|
5624
|
+
if (stopped || !(baseline === null || baseline === void 0 ? void 0 : baseline.width) || !(baseline === null || baseline === void 0 ? void 0 : baseline.height)) {
|
|
5625
|
+
stop();
|
|
5626
|
+
return;
|
|
5627
|
+
}
|
|
5628
|
+
let polling = false;
|
|
5629
|
+
intervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
5630
|
+
if (stopped || consumer.closed) {
|
|
5631
|
+
stop();
|
|
5632
|
+
return;
|
|
5633
|
+
}
|
|
5634
|
+
if (polling)
|
|
5635
|
+
return;
|
|
5636
|
+
polling = true;
|
|
5637
|
+
const current = yield this._getConsumerFrameSize(consumer);
|
|
5638
|
+
polling = false;
|
|
5639
|
+
if (stopped)
|
|
5640
|
+
return;
|
|
5641
|
+
const switched = toHigherLayer
|
|
5642
|
+
? getFrameArea(current) > getFrameArea(baseline)
|
|
5643
|
+
: getFrameArea(current) < getFrameArea(baseline);
|
|
5644
|
+
if (current && switched) {
|
|
5645
|
+
this._recordPreferredLayerSwitchLatency(Date.now() - sentAt);
|
|
5646
|
+
stop();
|
|
5647
|
+
}
|
|
5648
|
+
}), PREFERRED_LAYER_SWITCH_POLL_INTERVAL_MS);
|
|
5649
|
+
}))();
|
|
5650
|
+
}
|
|
5651
|
+
_getConsumerFrameSize(consumer) {
|
|
5652
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
5653
|
+
try {
|
|
5654
|
+
const stats = yield consumer.getStats();
|
|
5655
|
+
let frameSize;
|
|
5656
|
+
stats.forEach((report) => {
|
|
5657
|
+
if (report.type === "inbound-rtp") {
|
|
5658
|
+
frameSize = { width: report.frameWidth, height: report.frameHeight };
|
|
5659
|
+
}
|
|
5660
|
+
});
|
|
5661
|
+
return frameSize;
|
|
5662
|
+
}
|
|
5663
|
+
catch (error) {
|
|
5664
|
+
logger$2.warn("_getConsumerFrameSize() failed to read stats", { error });
|
|
5665
|
+
return undefined;
|
|
5666
|
+
}
|
|
5667
|
+
});
|
|
5668
|
+
}
|
|
5669
|
+
_recordPreferredLayerSwitchLatency(latencyMs) {
|
|
5670
|
+
recordMetricSample(this._preferredLayerSwitchLatencyStats, latencyMs);
|
|
5671
|
+
const { avg } = aggregateMetricStats(this._preferredLayerSwitchLatencyStats);
|
|
5672
|
+
this.analytics.avgPreferredLayerSwitchLatencyMs = avg;
|
|
5673
|
+
}
|
|
5674
|
+
_onUpdatedStats(statsByView) {
|
|
5675
|
+
var _a, _b, _c, _d;
|
|
5676
|
+
const webcamProducerMid = (_b = (_a = this._webcamProducer) === null || _a === void 0 ? void 0 : _a.rtpParameters) === null || _b === void 0 ? void 0 : _b.mid;
|
|
5677
|
+
if (webcamProducerMid === undefined)
|
|
5678
|
+
return;
|
|
5679
|
+
const sendTransportPc = (_d = (_c = this._sendTransport) === null || _c === void 0 ? void 0 : _c.handler) === null || _d === void 0 ? void 0 : _d._pc;
|
|
5680
|
+
const sendTransportPcIndex = sendTransportPc ? getPeerConnectionIndex(sendTransportPc) : undefined;
|
|
5681
|
+
const newRemoteReports = [];
|
|
5682
|
+
Object.values(statsByView).forEach((viewStats) => {
|
|
5683
|
+
Object.values(viewStats.tracks || {}).forEach((trackStats) => {
|
|
5684
|
+
Object.entries(trackStats.ssrcs || {}).forEach(([ssrc, ssrcMetrics]) => {
|
|
5685
|
+
if (ssrcMetrics.direction !== "out" || String(ssrcMetrics.mid) !== webcamProducerMid)
|
|
5686
|
+
return;
|
|
5687
|
+
if (sendTransportPcIndex !== undefined && ssrcMetrics.pcIndex !== sendTransportPcIndex)
|
|
5688
|
+
return;
|
|
5689
|
+
this._followCamOutboundCandidatePair(ssrcMetrics);
|
|
5690
|
+
const { remoteReportTimestamp } = ssrcMetrics;
|
|
5691
|
+
if (remoteReportTimestamp === undefined)
|
|
5692
|
+
return;
|
|
5693
|
+
if (remoteReportTimestamp === this._lastSeenRemoteReportTimestamps.get(ssrc))
|
|
5694
|
+
return;
|
|
5695
|
+
this._lastSeenRemoteReportTimestamps.set(ssrc, remoteReportTimestamp);
|
|
5696
|
+
newRemoteReports.push(ssrcMetrics);
|
|
5697
|
+
});
|
|
5698
|
+
});
|
|
5699
|
+
});
|
|
5700
|
+
if (!newRemoteReports.length)
|
|
5701
|
+
return;
|
|
5702
|
+
if (this._camOutboundSamplesToSkip > 0) {
|
|
5703
|
+
this._camOutboundSamplesToSkip--;
|
|
5704
|
+
return;
|
|
5430
5705
|
}
|
|
5706
|
+
this._recordCamOutboundSample(newRemoteReports);
|
|
5707
|
+
this._updateCamOutboundAnalytics();
|
|
5708
|
+
}
|
|
5709
|
+
_recordCamOutboundSample(newRemoteReports) {
|
|
5710
|
+
var _a, _b;
|
|
5711
|
+
const rttsMs = newRemoteReports.map(({ roundTripTime }) => (roundTripTime || 0) * 1000).filter((ms) => ms > 0);
|
|
5712
|
+
const rttMs = rttsMs.length ? Math.max(...rttsMs) : undefined;
|
|
5713
|
+
const fractionLost = getPacketWeightedFractionLost(newRemoteReports);
|
|
5714
|
+
const path = this._camOutboundPathRtt;
|
|
5715
|
+
if (rttMs !== undefined) {
|
|
5716
|
+
path.sumMs += rttMs;
|
|
5717
|
+
path.count++;
|
|
5718
|
+
path.minMs = Math.min((_a = path.minMs) !== null && _a !== void 0 ? _a : Infinity, rttMs);
|
|
5719
|
+
}
|
|
5720
|
+
const congested = (rttMs !== undefined && rttMs - ((_b = path.minMs) !== null && _b !== void 0 ? _b : rttMs) > CONGESTED_RTT_INFLATION_MS) ||
|
|
5721
|
+
fractionLost > CONGESTED_FRACTION_LOST;
|
|
5722
|
+
this._camOutboundSamples.total++;
|
|
5723
|
+
if (congested)
|
|
5724
|
+
this._camOutboundSamples.congested++;
|
|
5725
|
+
}
|
|
5726
|
+
_followCamOutboundCandidatePair({ selectedCandidatePairId }) {
|
|
5727
|
+
if (!selectedCandidatePairId || selectedCandidatePairId === this._camOutboundCandidatePairId)
|
|
5728
|
+
return;
|
|
5729
|
+
if (this._camOutboundCandidatePairId !== undefined) {
|
|
5730
|
+
this._finishCamOutboundRttPath();
|
|
5731
|
+
this._camOutboundSamplesToSkip = CAM_SAMPLES_SKIPPED_AFTER_CANDIDATE_PAIR_CHANGE;
|
|
5732
|
+
}
|
|
5733
|
+
this._camOutboundCandidatePairId = selectedCandidatePairId;
|
|
5734
|
+
}
|
|
5735
|
+
_finishCamOutboundRttPath() {
|
|
5736
|
+
const path = this._camOutboundPathRtt;
|
|
5737
|
+
if (path.count && path.minMs !== undefined) {
|
|
5738
|
+
this._camOutboundRttInflation.sumMs += path.sumMs - path.count * path.minMs;
|
|
5739
|
+
this._camOutboundRttInflation.count += path.count;
|
|
5740
|
+
}
|
|
5741
|
+
this._camOutboundPathRtt = { sumMs: 0, count: 0, minMs: undefined };
|
|
5742
|
+
}
|
|
5743
|
+
_updateCamOutboundAnalytics() {
|
|
5744
|
+
const path = this._camOutboundPathRtt;
|
|
5745
|
+
const inflationSumMs = this._camOutboundRttInflation.sumMs + (path.minMs !== undefined ? path.sumMs - path.count * path.minMs : 0);
|
|
5746
|
+
const inflationCount = this._camOutboundRttInflation.count + path.count;
|
|
5747
|
+
this.analytics.camOutboundRttInflationMs = inflationCount
|
|
5748
|
+
? Math.round(inflationSumMs / inflationCount)
|
|
5749
|
+
: undefined;
|
|
5750
|
+
const { congested, total } = this._camOutboundSamples;
|
|
5751
|
+
this.analytics.camOutboundCongestedFraction = total ? congested / total : undefined;
|
|
5752
|
+
}
|
|
5753
|
+
_startWebcamStatsSubscription() {
|
|
5754
|
+
if (this._statsSubscription)
|
|
5755
|
+
return;
|
|
5756
|
+
this._statsSubscription = subscribeStats({
|
|
5757
|
+
onUpdatedStats: (statsByView) => this._onUpdatedStats(statsByView),
|
|
5758
|
+
});
|
|
5759
|
+
}
|
|
5760
|
+
_stopWebcamStatsSubscription() {
|
|
5761
|
+
var _a;
|
|
5762
|
+
(_a = this._statsSubscription) === null || _a === void 0 ? void 0 : _a.stop();
|
|
5763
|
+
this._statsSubscription = null;
|
|
5764
|
+
this._lastSeenRemoteReportTimestamps.clear();
|
|
5765
|
+
this._finishCamOutboundRttPath();
|
|
5766
|
+
this._camOutboundCandidatePairId = undefined;
|
|
5767
|
+
this._camOutboundSamplesToSkip = 0;
|
|
5431
5768
|
}
|
|
5432
5769
|
close() {
|
|
5433
5770
|
this.disconnectAll();
|
|
5434
5771
|
}
|
|
5435
5772
|
disconnectAll() {
|
|
5436
|
-
var _a, _b, _c, _d;
|
|
5773
|
+
var _a, _b, _c, _d, _e;
|
|
5437
5774
|
this._reconnect = false;
|
|
5438
5775
|
if (this._reconnectTimeOut) {
|
|
5439
5776
|
clearTimeout(this._reconnectTimeOut);
|
|
5440
5777
|
this._reconnectTimeOut = null;
|
|
5441
5778
|
}
|
|
5442
5779
|
this._sfuZombieReset();
|
|
5443
|
-
|
|
5444
|
-
window.removeEventListener("offline", this._sfuZombie.onBrowserOffline);
|
|
5445
|
-
}
|
|
5780
|
+
(_a = window === null || window === void 0 ? void 0 : window.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(window, "offline", this._sfuZombie.onBrowserOffline);
|
|
5446
5781
|
this._socketListenerDeregisterFunctions.forEach((func) => {
|
|
5447
5782
|
func();
|
|
5448
5783
|
});
|
|
@@ -5451,17 +5786,20 @@ class VegaRtcManager {
|
|
|
5451
5786
|
this._localStreamDeregisterFunction = null;
|
|
5452
5787
|
}
|
|
5453
5788
|
this._socketListenerDeregisterFunctions = [];
|
|
5454
|
-
(
|
|
5455
|
-
(
|
|
5456
|
-
(
|
|
5457
|
-
(
|
|
5789
|
+
(_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
|
|
5790
|
+
(_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.close();
|
|
5791
|
+
(_d = this._sendTransport) === null || _d === void 0 ? void 0 : _d.close();
|
|
5792
|
+
(_e = this._receiveTransport) === null || _e === void 0 ? void 0 : _e.close();
|
|
5458
5793
|
this._micTrack = null;
|
|
5459
5794
|
this._webcamTrack = null;
|
|
5460
5795
|
this._screenVideoTrack = null;
|
|
5461
5796
|
this._screenAudioTrack = null;
|
|
5462
5797
|
this._streamIdToVideoConsumerId.clear();
|
|
5798
|
+
this._streamIdToVideoResolution.clear();
|
|
5463
5799
|
this._mediasoupDeviceInitializedAsync = Promise.resolve(null);
|
|
5464
5800
|
this._qualityMonitor.close();
|
|
5801
|
+
this._stopWebcamStatsSubscription();
|
|
5802
|
+
this._preferredLayerSwitchWatches.forEach(({ stop }) => stop());
|
|
5465
5803
|
}
|
|
5466
5804
|
sendStatsCustomEvent(eventName, data) {
|
|
5467
5805
|
rtcStats.sendEvent(eventName, data);
|
|
@@ -5519,6 +5857,8 @@ class VegaRtcManager {
|
|
|
5519
5857
|
return this._onConsumerScore(data);
|
|
5520
5858
|
case "producerScore":
|
|
5521
5859
|
return this._onProducerScore(data);
|
|
5860
|
+
case "changedHighestPreferredLayer":
|
|
5861
|
+
return this._onChangedHighestPreferredLayer(data);
|
|
5522
5862
|
default:
|
|
5523
5863
|
logger$2.info(`unknown message method "${method}"`);
|
|
5524
5864
|
return;
|
|
@@ -5546,13 +5886,16 @@ class VegaRtcManager {
|
|
|
5546
5886
|
}
|
|
5547
5887
|
consumer.pause();
|
|
5548
5888
|
consumer.appData.localPaused = true;
|
|
5549
|
-
consumer.appData.spatialLayer =
|
|
5889
|
+
consumer.appData.spatialLayer = INITIAL_CONSUMER_SPATIAL_LAYER;
|
|
5550
5890
|
this._consumers.set(consumer.id, consumer);
|
|
5551
5891
|
this._qualityMonitor.addConsumer(consumer.appData.sourceClientId, consumer.id);
|
|
5552
5892
|
consumer.observer.once("close", () => {
|
|
5553
5893
|
this._consumers.delete(consumer.id);
|
|
5554
5894
|
this._qualityMonitor.removeConsumer(consumer.appData.sourceClientId, consumer.id);
|
|
5555
5895
|
this._consumerClosedCleanup(consumer);
|
|
5896
|
+
if (consumer.kind === "video" && this._receiveTransport && !this._receiveTransport.closed) {
|
|
5897
|
+
this._reapplyStreamResolutions();
|
|
5898
|
+
}
|
|
5556
5899
|
});
|
|
5557
5900
|
if (this._features.increaseIncomingMediaBufferOn && consumer.rtpReceiver) {
|
|
5558
5901
|
try {
|
|
@@ -5584,13 +5927,12 @@ class VegaRtcManager {
|
|
|
5584
5927
|
}
|
|
5585
5928
|
stream.addTrack(consumer.track);
|
|
5586
5929
|
this._syncIncomingStreamsWithPWA(clientId);
|
|
5587
|
-
|
|
5588
|
-
if (resolution) {
|
|
5589
|
-
this.updateStreamResolution(stream.id, null, resolution);
|
|
5590
|
-
this._streamIdToVideoResolution.delete(stream.id);
|
|
5591
|
-
}
|
|
5930
|
+
this._reapplyStreamResolutions();
|
|
5592
5931
|
});
|
|
5593
5932
|
}
|
|
5933
|
+
_reapplyStreamResolutions() {
|
|
5934
|
+
this._streamIdToVideoResolution.forEach((_resolution, streamId) => this._applyStreamResolution(streamId));
|
|
5935
|
+
}
|
|
5594
5936
|
_onConsumerClosed(_a) {
|
|
5595
5937
|
return __awaiter(this, arguments, void 0, function* ({ consumerId, reason }) {
|
|
5596
5938
|
var _b;
|
|
@@ -5605,6 +5947,8 @@ class VegaRtcManager {
|
|
|
5605
5947
|
return;
|
|
5606
5948
|
consumer.appData.remotePaused = true;
|
|
5607
5949
|
consumer.pause();
|
|
5950
|
+
if (consumer.kind === "video")
|
|
5951
|
+
this._reapplyStreamResolutions();
|
|
5608
5952
|
}
|
|
5609
5953
|
_onConsumerResumed({ consumerId }) {
|
|
5610
5954
|
logger$2.info("_onConsumerResumed()", { consumerId });
|
|
@@ -5615,6 +5959,8 @@ class VegaRtcManager {
|
|
|
5615
5959
|
if (!consumer.appData.localPaused) {
|
|
5616
5960
|
consumer.resume();
|
|
5617
5961
|
}
|
|
5962
|
+
if (consumer.kind === "video")
|
|
5963
|
+
this._reapplyStreamResolutions();
|
|
5618
5964
|
}
|
|
5619
5965
|
_onConsumerScore({ consumerId, kind, score }) {
|
|
5620
5966
|
logger$2.info("_onConsumerScore()", { consumerId, kind, score });
|
|
@@ -5631,6 +5977,90 @@ class VegaRtcManager {
|
|
|
5631
5977
|
}
|
|
5632
5978
|
});
|
|
5633
5979
|
}
|
|
5980
|
+
_getSvcEncodingUpdate(encoding, spatialLayer) {
|
|
5981
|
+
var _a;
|
|
5982
|
+
const reduced = getReducedSvcEncodingParams(this._webcamProducerOriginalSvcScalabilityMode, spatialLayer, this._webcamProducerOriginalSvcMaxBitrate);
|
|
5983
|
+
if (!reduced)
|
|
5984
|
+
return undefined;
|
|
5985
|
+
const changed = reduced.scalabilityMode !== encoding.scalabilityMode ||
|
|
5986
|
+
reduced.scaleResolutionDownBy !== ((_a = encoding.scaleResolutionDownBy) !== null && _a !== void 0 ? _a : 1) ||
|
|
5987
|
+
reduced.maxBitrate !== encoding.maxBitrate;
|
|
5988
|
+
return changed ? reduced : undefined;
|
|
5989
|
+
}
|
|
5990
|
+
_onChangedHighestPreferredLayer(_a) {
|
|
5991
|
+
return __awaiter(this, arguments, void 0, function* ({ producerId, spatialLayer }) {
|
|
5992
|
+
var _b;
|
|
5993
|
+
if (!this._features.sfuHighestPreferredLayerTrackingOn)
|
|
5994
|
+
return;
|
|
5995
|
+
if (((_b = this._webcamProducer) === null || _b === void 0 ? void 0 : _b.id) !== producerId)
|
|
5996
|
+
return;
|
|
5997
|
+
if (!Number.isFinite(spatialLayer)) {
|
|
5998
|
+
logger$2.warn("_onChangedHighestPreferredLayer() ignoring invalid spatialLayer", {
|
|
5999
|
+
producerId,
|
|
6000
|
+
spatialLayer,
|
|
6001
|
+
});
|
|
6002
|
+
return;
|
|
6003
|
+
}
|
|
6004
|
+
if (this._webcamProducerHighestPreferredLayer !== undefined &&
|
|
6005
|
+
this._webcamProducerHighestPreferredLayer !== spatialLayer) {
|
|
6006
|
+
this.analytics.numHighestPreferredLayerChanges++;
|
|
6007
|
+
this._incrementHistogramCount(this.analytics.highestPreferredLayerChangeCounts, `${this._webcamProducerHighestPreferredLayer}->${spatialLayer}`);
|
|
6008
|
+
}
|
|
6009
|
+
this._webcamProducerHighestPreferredLayer = spatialLayer;
|
|
6010
|
+
logger$2.info("_onChangedHighestPreferredLayer()", { producerId, spatialLayer });
|
|
6011
|
+
return this._syncWebcamEncoderToHighestPreferredLayer();
|
|
6012
|
+
});
|
|
6013
|
+
}
|
|
6014
|
+
_syncWebcamEncoderToHighestPreferredLayer() {
|
|
6015
|
+
this._highestPreferredLayerUpdatePending = true;
|
|
6016
|
+
if (!this._highestPreferredLayerUpdate) {
|
|
6017
|
+
this._highestPreferredLayerUpdate = this._applyPendingHighestPreferredLayerUpdates();
|
|
6018
|
+
}
|
|
6019
|
+
return this._highestPreferredLayerUpdate;
|
|
6020
|
+
}
|
|
6021
|
+
_applyPendingHighestPreferredLayerUpdates() {
|
|
6022
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6023
|
+
while (this._highestPreferredLayerUpdatePending) {
|
|
6024
|
+
this._highestPreferredLayerUpdatePending = false;
|
|
6025
|
+
try {
|
|
6026
|
+
yield this._applyHighestPreferredLayerToWebcamEncoder();
|
|
6027
|
+
}
|
|
6028
|
+
catch (error) {
|
|
6029
|
+
logger$2.error("Failed to apply highest preferred layer: %o", error);
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
this._highestPreferredLayerUpdate = null;
|
|
6033
|
+
});
|
|
6034
|
+
}
|
|
6035
|
+
_applyHighestPreferredLayerToWebcamEncoder() {
|
|
6036
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6037
|
+
const producer = this._webcamProducer;
|
|
6038
|
+
const highestPreferredLayer = this._webcamProducerHighestPreferredLayer;
|
|
6039
|
+
if (!producer || highestPreferredLayer === undefined)
|
|
6040
|
+
return;
|
|
6041
|
+
const encodings = producer.rtpSender.getParameters().encodings;
|
|
6042
|
+
if (encodings.length > 1) {
|
|
6043
|
+
const cpuOveruseLimit = this._cpuOveruseDetected && encodings.length === 3 ? 1 : Infinity;
|
|
6044
|
+
const spatialLayer = Math.max(0, Math.min(highestPreferredLayer, cpuOveruseLimit, encodings.length - 1));
|
|
6045
|
+
if (producer.maxSpatialLayer === spatialLayer)
|
|
6046
|
+
return;
|
|
6047
|
+
yield producer.setMaxSpatialLayer(spatialLayer);
|
|
6048
|
+
const applied = producer.rtpSender
|
|
6049
|
+
.getParameters()
|
|
6050
|
+
.encodings.every((encoding, index) => encoding.active === index <= spatialLayer);
|
|
6051
|
+
if (!applied)
|
|
6052
|
+
logger$2.error("setMaxSpatialLayer(%d) was not applied to the encodings", spatialLayer);
|
|
6053
|
+
}
|
|
6054
|
+
else if (encodings.length === 1 && this._webcamProducerOriginalSvcScalabilityMode) {
|
|
6055
|
+
const update = this._getSvcEncodingUpdate(encodings[0], highestPreferredLayer);
|
|
6056
|
+
if (update)
|
|
6057
|
+
yield producer.setRtpEncodingParameters(update);
|
|
6058
|
+
}
|
|
6059
|
+
});
|
|
6060
|
+
}
|
|
6061
|
+
_incrementHistogramCount(histogram, key) {
|
|
6062
|
+
histogram[key] = (histogram[key] || 0) + 1;
|
|
6063
|
+
}
|
|
5634
6064
|
_onDataConsumerReady(options) {
|
|
5635
6065
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5636
6066
|
logger$2.info("_onDataConsumerReady()", { id: options.id, dataProducerId: options.dataProducerId });
|
|
@@ -5680,8 +6110,8 @@ class VegaRtcManager {
|
|
|
5680
6110
|
stream.removeTrack(consumer.track);
|
|
5681
6111
|
if (stream.getTracks().length === 0) {
|
|
5682
6112
|
this._streamIdToVideoConsumerId.delete(stream.id);
|
|
5683
|
-
this._streamIdToVideoResolution.delete(stream.id);
|
|
5684
6113
|
if (screenShare) {
|
|
6114
|
+
this._streamIdToVideoResolution.delete(stream.id);
|
|
5685
6115
|
clientState.screenStream = undefined;
|
|
5686
6116
|
clientState.hasEmittedScreenStream = false;
|
|
5687
6117
|
clientState.screenShareStreamId = undefined;
|
|
@@ -5727,6 +6157,8 @@ class VegaRtcManager {
|
|
|
5727
6157
|
consumerIds: toResumeConsumers,
|
|
5728
6158
|
});
|
|
5729
6159
|
}
|
|
6160
|
+
if (toPauseConsumers.length > 0 || toResumeConsumers.length > 0)
|
|
6161
|
+
this._reapplyStreamResolutions();
|
|
5730
6162
|
if (webcamStream && !hasEmittedWebcamStream && hasAcceptedWebcamStream) {
|
|
5731
6163
|
this._emitToPWA(EVENTS.STREAM_ADDED, {
|
|
5732
6164
|
clientId,
|
|
@@ -7499,14 +7931,24 @@ exports.ADDITIONAL_SCREEN_SHARE_SETTINGS = ADDITIONAL_SCREEN_SHARE_SETTINGS;
|
|
|
7499
7931
|
exports.AUDIO_SETTINGS = AUDIO_SETTINGS;
|
|
7500
7932
|
exports.BandwidthTester = BandwidthTester;
|
|
7501
7933
|
exports.CAMERA_STREAM_ID = CAMERA_STREAM_ID;
|
|
7934
|
+
exports.CAM_SAMPLES_SKIPPED_AFTER_CANDIDATE_PAIR_CHANGE = CAM_SAMPLES_SKIPPED_AFTER_CANDIDATE_PAIR_CHANGE;
|
|
7935
|
+
exports.CONGESTED_FRACTION_LOST = CONGESTED_FRACTION_LOST;
|
|
7936
|
+
exports.CONGESTED_RTT_INFLATION_MS = CONGESTED_RTT_INFLATION_MS;
|
|
7502
7937
|
exports.EVENTS = EVENTS;
|
|
7503
7938
|
exports.FILE_SHARE_ERROR_CODES = FILE_SHARE_ERROR_CODES;
|
|
7939
|
+
exports.INITIAL_CONSUMER_SPATIAL_LAYER = INITIAL_CONSUMER_SPATIAL_LAYER;
|
|
7940
|
+
exports.INITIAL_HIGHEST_PREFERRED_LAYER = INITIAL_HIGHEST_PREFERRED_LAYER;
|
|
7504
7941
|
exports.KNOCK_MESSAGES = KNOCK_MESSAGES;
|
|
7505
7942
|
exports.KalmanFilter = KalmanFilter;
|
|
7943
|
+
exports.LOWEST_SVC_LAYER_MAX_BITRATE = LOWEST_SVC_LAYER_MAX_BITRATE;
|
|
7506
7944
|
exports.Logger = Logger;
|
|
7945
|
+
exports.MAX_CONCURRENT_PREFERRED_LAYER_SWITCH_WATCHES = MAX_CONCURRENT_PREFERRED_LAYER_SWITCH_WATCHES;
|
|
7507
7946
|
exports.MEDIA_JITTER_BUFFER_TARGET = MEDIA_JITTER_BUFFER_TARGET;
|
|
7947
|
+
exports.MIDDLE_SVC_LAYER_MAX_BITRATE = MIDDLE_SVC_LAYER_MAX_BITRATE;
|
|
7508
7948
|
exports.NoDevicesError = NoDevicesError;
|
|
7509
7949
|
exports.P2pRtcManager = P2pRtcManager;
|
|
7950
|
+
exports.PREFERRED_LAYER_SWITCH_POLL_INTERVAL_MS = PREFERRED_LAYER_SWITCH_POLL_INTERVAL_MS;
|
|
7951
|
+
exports.PREFERRED_LAYER_SWITCH_WATCH_TIMEOUT_MS = PREFERRED_LAYER_SWITCH_WATCH_TIMEOUT_MS;
|
|
7510
7952
|
exports.PROTOCOL_ERRORS = PROTOCOL_ERRORS;
|
|
7511
7953
|
exports.PROTOCOL_EVENTS = PROTOCOL_EVENTS;
|
|
7512
7954
|
exports.PROTOCOL_REQUESTS = PROTOCOL_REQUESTS;
|
|
@@ -7563,6 +8005,7 @@ exports.getNumFailedStatsReports = getNumFailedStatsReports;
|
|
|
7563
8005
|
exports.getNumFailedTrackSsrcLookups = getNumFailedTrackSsrcLookups;
|
|
7564
8006
|
exports.getNumMissingTrackSsrcLookups = getNumMissingTrackSsrcLookups;
|
|
7565
8007
|
exports.getPeerConnectionIndex = getPeerConnectionIndex;
|
|
8008
|
+
exports.getSelectedCandidatePairId = getSelectedCandidatePairId;
|
|
7566
8009
|
exports.getStats = getStats;
|
|
7567
8010
|
exports.getStream = getStream;
|
|
7568
8011
|
exports.getUpdatedDevices = getUpdatedDevices;
|