livedesk 0.1.379 → 0.1.381
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/hub/src/server.js
CHANGED
|
@@ -59,14 +59,14 @@ const runtimeManager = createHubRuntime({
|
|
|
59
59
|
runtimePid: process.pid
|
|
60
60
|
});
|
|
61
61
|
runtimeManager.emit('runtime.started', { role: runtimeRole, pid: process.pid });
|
|
62
|
-
const frameBackpressureBytes = readPositiveIntegerEnv('LIVEDESK_FRAME_WS_BACKPRESSURE_BYTES',
|
|
62
|
+
const frameBackpressureBytes = readPositiveIntegerEnv('LIVEDESK_FRAME_WS_BACKPRESSURE_BYTES', 8 * 1024 * 1024);
|
|
63
63
|
const frameClientQueuePackets = readPositiveIntegerEnv('LIVEDESK_FRAME_WS_QUEUE_PACKETS', 4);
|
|
64
64
|
const controlFrameClientQueuePackets = readPositiveIntegerEnv('LIVEDESK_CONTROL_FRAME_WS_QUEUE_PACKETS', 2);
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
65
|
+
const frameStreamStopGraceMs = readPositiveIntegerEnv('LIVEDESK_FRAME_STREAM_STOP_GRACE_MS', 1500);
|
|
66
|
+
const mode5FrameBackpressureBytes = readPositiveIntegerEnv('LIVEDESK_MODE5_WS_BACKPRESSURE_BYTES', 2 * 1024 * 1024);
|
|
67
|
+
const mode5FrameQueuePackets = readPositiveIntegerEnv('LIVEDESK_MODE5_WS_QUEUE_PACKETS', 2);
|
|
68
|
+
const audioBackpressureBytes = readPositiveIntegerEnv('LIVEDESK_AUDIO_WS_BACKPRESSURE_BYTES', 96 * 1024);
|
|
69
|
+
const webSocketHeartbeatMs = readPositiveIntegerEnv('LIVEDESK_BROWSER_WS_HEARTBEAT_MS', 15_000);
|
|
70
70
|
const frameClients = new Set();
|
|
71
71
|
const frameClientsByDeviceId = new Map();
|
|
72
72
|
const frameWildcardClients = new Set();
|
|
@@ -1064,10 +1064,38 @@ const hubSharedFolders = createHubSharedFolders({
|
|
|
1064
1064
|
|
|
1065
1065
|
const app = express();
|
|
1066
1066
|
const httpServer = createServer(app);
|
|
1067
|
-
const frameWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1068
|
-
const atlasWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1069
|
-
const inputWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1070
|
-
const audioWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1067
|
+
const frameWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1068
|
+
const atlasWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1069
|
+
const inputWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1070
|
+
const audioWss = new WebSocketServer({ noServer: true, perMessageDeflate: false });
|
|
1071
|
+
const browserWebSocketServers = [frameWss, atlasWss, inputWss, audioWss];
|
|
1072
|
+
|
|
1073
|
+
for (const wss of browserWebSocketServers) {
|
|
1074
|
+
wss.on('connection', ws => {
|
|
1075
|
+
ws.liveDeskHeartbeatAlive = true;
|
|
1076
|
+
ws.on('pong', () => {
|
|
1077
|
+
ws.liveDeskHeartbeatAlive = true;
|
|
1078
|
+
});
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
const browserWebSocketHeartbeatTimer = setInterval(() => {
|
|
1083
|
+
for (const wss of browserWebSocketServers) {
|
|
1084
|
+
for (const ws of wss.clients) {
|
|
1085
|
+
if (ws.liveDeskHeartbeatAlive === false) {
|
|
1086
|
+
ws.terminate();
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
ws.liveDeskHeartbeatAlive = false;
|
|
1090
|
+
try {
|
|
1091
|
+
ws.ping();
|
|
1092
|
+
} catch {
|
|
1093
|
+
ws.terminate();
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}, webSocketHeartbeatMs);
|
|
1098
|
+
browserWebSocketHeartbeatTimer.unref?.();
|
|
1071
1099
|
|
|
1072
1100
|
function normalizeHostname(value) {
|
|
1073
1101
|
return String(value || '').trim().toLowerCase().replace(/^\[|\]$/g, '');
|
|
@@ -1347,23 +1375,25 @@ function forgetWebSocketClient(ws) {
|
|
|
1347
1375
|
}
|
|
1348
1376
|
}
|
|
1349
1377
|
|
|
1350
|
-
function safeWebSocketSend(ws, payload, options = undefined) {
|
|
1351
|
-
if (!ws || ws.readyState !== 1) {
|
|
1352
|
-
return false;
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
try {
|
|
1356
|
-
ws.send(payload, options, err => {
|
|
1357
|
-
if (err) {
|
|
1358
|
-
forgetWebSocketClient(ws);
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1378
|
+
function safeWebSocketSend(ws, payload, options = undefined, onComplete = undefined) {
|
|
1379
|
+
if (!ws || ws.readyState !== 1) {
|
|
1380
|
+
return false;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
try {
|
|
1384
|
+
ws.send(payload, options, err => {
|
|
1385
|
+
if (err) {
|
|
1386
|
+
forgetWebSocketClient(ws);
|
|
1387
|
+
}
|
|
1388
|
+
onComplete?.(err || null);
|
|
1389
|
+
});
|
|
1390
|
+
return true;
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
forgetWebSocketClient(ws);
|
|
1393
|
+
onComplete?.(error);
|
|
1394
|
+
return false;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1367
1397
|
|
|
1368
1398
|
function unregisterFrameClient(ws) {
|
|
1369
1399
|
if (!ws) {
|
|
@@ -1383,7 +1413,12 @@ function unregisterFrameClient(ws) {
|
|
|
1383
1413
|
}
|
|
1384
1414
|
frameWildcardClients.delete(ws);
|
|
1385
1415
|
ws.liveDeskExpectedStreamBindingsByDeviceId?.clear?.();
|
|
1386
|
-
ws.liveDeskFrameSendLane
|
|
1416
|
+
const lane = ws.liveDeskFrameSendLane;
|
|
1417
|
+
if (lane) {
|
|
1418
|
+
clearTimeout(lane.retryTimer);
|
|
1419
|
+
lane.retryTimer = null;
|
|
1420
|
+
lane.queue.splice(0);
|
|
1421
|
+
}
|
|
1387
1422
|
}
|
|
1388
1423
|
|
|
1389
1424
|
function hasOtherFrameStreamOwner(ws, deviceId, streamId, streamPurpose = '') {
|
|
@@ -1484,11 +1519,12 @@ function registerFrameClient(ws) {
|
|
|
1484
1519
|
function ensureFrameClientSendLane(ws) {
|
|
1485
1520
|
if (!ws.liveDeskFrameSendLane) {
|
|
1486
1521
|
ws.liveDeskFrameSendLane = {
|
|
1487
|
-
queue: [],
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1522
|
+
queue: [],
|
|
1523
|
+
sending: false,
|
|
1524
|
+
retryTimer: null,
|
|
1525
|
+
dropped: 0,
|
|
1526
|
+
awaitingKeyFrames: new Set()
|
|
1527
|
+
};
|
|
1492
1528
|
}
|
|
1493
1529
|
return ws.liveDeskFrameSendLane;
|
|
1494
1530
|
}
|
|
@@ -1536,39 +1572,48 @@ function dropQueuedMode5FrameForLane(lane, deviceId) {
|
|
|
1536
1572
|
return true;
|
|
1537
1573
|
}
|
|
1538
1574
|
|
|
1539
|
-
function drainFrameClientSendLane(ws) {
|
|
1540
|
-
const lane = ws.liveDeskFrameSendLane;
|
|
1541
|
-
if (!lane || lane.
|
|
1542
|
-
return;
|
|
1543
|
-
}
|
|
1544
|
-
lane.
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1575
|
+
function drainFrameClientSendLane(ws) {
|
|
1576
|
+
const lane = ws.liveDeskFrameSendLane;
|
|
1577
|
+
if (!lane || lane.sending) {
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
clearTimeout(lane.retryTimer);
|
|
1581
|
+
lane.retryTimer = null;
|
|
1582
|
+
if (!ws || ws.readyState !== ws.OPEN) {
|
|
1583
|
+
lane.queue.length = 0;
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
const item = lane.queue[0];
|
|
1587
|
+
if (!item) {
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
const backpressureBytes = item.isMode5 ? mode5FrameBackpressureBytes : frameBackpressureBytes;
|
|
1591
|
+
if (ws.bufferedAmount > backpressureBytes) {
|
|
1592
|
+
ws.liveDeskFrameBackpressured = true;
|
|
1593
|
+
lane.retryTimer = setTimeout(() => {
|
|
1594
|
+
lane.retryTimer = null;
|
|
1595
|
+
drainFrameClientSendLane(ws);
|
|
1596
|
+
}, 10);
|
|
1597
|
+
lane.retryTimer.unref?.();
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
lane.queue.shift();
|
|
1602
|
+
lane.sending = true;
|
|
1603
|
+
ws.liveDeskFrameBackpressured = false;
|
|
1604
|
+
const accepted = safeWebSocketSend(ws, item.packet, { binary: true }, error => {
|
|
1605
|
+
lane.sending = false;
|
|
1606
|
+
if (error || ws.readyState !== ws.OPEN) {
|
|
1607
|
+
lane.queue.length = 0;
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
drainFrameClientSendLane(ws);
|
|
1611
|
+
});
|
|
1612
|
+
if (!accepted) {
|
|
1613
|
+
lane.sending = false;
|
|
1614
|
+
lane.queue.length = 0;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1572
1617
|
|
|
1573
1618
|
function enqueueFramePacketForClient(ws, packet, meta) {
|
|
1574
1619
|
if (!ws || ws.readyState !== ws.OPEN) {
|
|
@@ -2227,10 +2272,35 @@ function isCapabilityEnabled(device, key) {
|
|
|
2227
2272
|
return /^(1|true|yes|on)$/i.test(String(value || '').trim());
|
|
2228
2273
|
}
|
|
2229
2274
|
|
|
2230
|
-
app.get('/api/health', (_req, res) => {
|
|
2231
|
-
noStore(res);
|
|
2232
|
-
|
|
2233
|
-
|
|
2275
|
+
app.get('/api/health', (_req, res) => {
|
|
2276
|
+
noStore(res);
|
|
2277
|
+
const memory = process.memoryUsage();
|
|
2278
|
+
const webSockets = {
|
|
2279
|
+
frame: frameWss.clients.size,
|
|
2280
|
+
atlas: atlasWss.clients.size,
|
|
2281
|
+
input: inputWss.clients.size,
|
|
2282
|
+
audio: audioWss.clients.size
|
|
2283
|
+
};
|
|
2284
|
+
res.json({
|
|
2285
|
+
ok: true,
|
|
2286
|
+
product: 'LiveDesk',
|
|
2287
|
+
timestamp: new Date().toISOString(),
|
|
2288
|
+
memory: {
|
|
2289
|
+
rssBytes: memory.rss,
|
|
2290
|
+
heapUsedBytes: memory.heapUsed,
|
|
2291
|
+
heapTotalBytes: memory.heapTotal,
|
|
2292
|
+
externalBytes: memory.external,
|
|
2293
|
+
arrayBuffersBytes: memory.arrayBuffers
|
|
2294
|
+
},
|
|
2295
|
+
webSockets: {
|
|
2296
|
+
...webSockets,
|
|
2297
|
+
total: Object.values(webSockets).reduce((sum, count) => sum + count, 0),
|
|
2298
|
+
bufferedBytes: [...browserWebSocketServers]
|
|
2299
|
+
.flatMap(wss => [...wss.clients])
|
|
2300
|
+
.reduce((sum, ws) => sum + Math.max(0, Number(ws.bufferedAmount || 0)), 0)
|
|
2301
|
+
}
|
|
2302
|
+
});
|
|
2303
|
+
});
|
|
2234
2304
|
|
|
2235
2305
|
app.get('/api/settings', async (_req, res) => {
|
|
2236
2306
|
noStore(res);
|
|
@@ -3972,6 +4042,7 @@ for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
|
3972
4042
|
if (roleWatchTimer) {
|
|
3973
4043
|
clearInterval(roleWatchTimer);
|
|
3974
4044
|
}
|
|
4045
|
+
clearInterval(browserWebSocketHeartbeatTimer);
|
|
3975
4046
|
liveDeskUpdateManager?.close();
|
|
3976
4047
|
atlasPool.close();
|
|
3977
4048
|
atlasClients.clear();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livedesk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.381",
|
|
4
4
|
"buildFlavor": "production",
|
|
5
5
|
"description": "LiveDesk Hub and client launcher",
|
|
6
6
|
"type": "module",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"ws": "^8.18.3"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"@livedesk/fast-linux-x64": "0.1.373",
|
|
52
|
-
"@livedesk/fast-osx-arm64": "0.1.375",
|
|
53
|
-
"@livedesk/fast-osx-x64": "0.1.375",
|
|
54
|
-
"@livedesk/fast-win-x64": "0.1.373"
|
|
51
|
+
"@livedesk/fast-linux-x64": "0.1.373",
|
|
52
|
+
"@livedesk/fast-osx-arm64": "0.1.375",
|
|
53
|
+
"@livedesk/fast-osx-x64": "0.1.375",
|
|
54
|
+
"@livedesk/fast-win-x64": "0.1.373"
|
|
55
55
|
},
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|