livedesk 0.1.141 → 0.1.143

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.
@@ -124,7 +124,7 @@ const DEFAULT_PUBLIC_IP_TIMEOUT_MS = 1800;
124
124
  const DUPLICATE_DEVICE_ACTIVE_REJECT_MS = 15000;
125
125
  const DUPLICATE_DEVICE_LOG_THROTTLE_MS = 60000;
126
126
  const DUPLICATE_DEVICE_RETRY_AFTER_MS = 30000;
127
- const AGENT_BINARY_INGRESS_QUEUE_PACKETS = 18;
127
+ const AGENT_BINARY_INGRESS_QUEUE_PACKETS = 64;
128
128
  const AGENT_BINARY_INGRESS_DRAIN_BUDGET_PACKETS = 64;
129
129
  const SYNTHETIC_FRAME_DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mP8z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC';
130
130
  const SYNTHETIC_FRAME_PAYLOAD = Buffer.from(SYNTHETIC_FRAME_DATA_URL.split(',')[1], 'base64');
@@ -1348,9 +1348,7 @@ export function createRemoteHub(options = {}) {
1348
1348
  const audioSockets = new Map();
1349
1349
  const fileSockets = new Map();
1350
1350
  const allSockets = new Set();
1351
- const agentBinaryIngressLanes = [];
1352
1351
  const duplicateDeviceLogAt = new Map();
1353
- let agentBinaryIngressDraining = false;
1354
1352
  let server = null;
1355
1353
  let started = false;
1356
1354
  let boundPort = requestedPort;
@@ -3239,6 +3237,7 @@ export function createRemoteHub(options = {}) {
3239
3237
  device.latestLiveFrame = {
3240
3238
  streamId,
3241
3239
  frameSeq,
3240
+ streamFrameSeq: Number.isFinite(Number(message.streamFrameSeq)) ? Number(message.streamFrameSeq) : frameSeq,
3242
3241
  commandId,
3243
3242
  width: Number.isFinite(Number(message.width)) ? Number(message.width) : 0,
3244
3243
  height: Number.isFinite(Number(message.height)) ? Number(message.height) : 0,
@@ -3266,6 +3265,7 @@ export function createRemoteHub(options = {}) {
3266
3265
  agentSendDurationFrameSeq: Number.isFinite(Number(message.agentSendDurationFrameSeq)) ? Number(message.agentSendDurationFrameSeq) : 0,
3267
3266
  agentPaceMs: Number.isFinite(Number(message.agentPaceMs)) ? Number(message.agentPaceMs) : 0,
3268
3267
  droppedByAgent: Number.isFinite(Number(message.droppedByAgent)) ? Number(message.droppedByAgent) : 0,
3268
+ hubIngressDropped: Number.isFinite(Number(message.hubIngressDropped)) ? Number(message.hubIngressDropped) : 0,
3269
3269
  hardwareEncoder: safeString(message.hardwareEncoder, 80),
3270
3270
  platformProfile: safeString(message.platformProfile, 80),
3271
3271
  monitorIndex: Number.isFinite(Number(message.monitorIndex)) ? normalizeMonitorIndex(message.monitorIndex) : Number(device.activeLiveStream.monitorIndex || 0),
@@ -3429,7 +3429,10 @@ export function createRemoteHub(options = {}) {
3429
3429
  }
3430
3430
 
3431
3431
  if (frameKind === 'stream' || frameKind === 'live' || header.type === 'stream.binary') {
3432
- applyLiveFrame(device, header, framePayload, 'binary');
3432
+ applyLiveFrame(device, {
3433
+ ...header,
3434
+ hubIngressDropped: Number(state.binaryQueueDrops || 0)
3435
+ }, framePayload, 'binary');
3433
3436
  return;
3434
3437
  }
3435
3438
 
@@ -3467,52 +3470,36 @@ export function createRemoteHub(options = {}) {
3467
3470
  }
3468
3471
 
3469
3472
  state.binaryQueueSocket = socket;
3470
- if (state.binaryQueueScheduled !== true) {
3471
- state.binaryQueueScheduled = true;
3472
- agentBinaryIngressLanes.push(state);
3473
+ if (state.binaryQueueScheduled === true) {
3474
+ return;
3473
3475
  }
3474
- drainAgentBinaryIngressRoundRobin();
3476
+ state.binaryQueueScheduled = true;
3477
+ setImmediate(() => drainAgentBinaryIngressLane(state));
3475
3478
  }
3476
3479
 
3477
- function drainAgentBinaryIngressRoundRobin() {
3478
- if (agentBinaryIngressDraining) {
3480
+ function drainAgentBinaryIngressLane(state) {
3481
+ state.binaryQueueScheduled = false;
3482
+ const socket = state.binaryQueueSocket;
3483
+ if (state.closed || !socket || socket.destroyed) {
3484
+ if (Array.isArray(state.binaryQueue)) {
3485
+ state.binaryQueue.length = 0;
3486
+ }
3479
3487
  return;
3480
3488
  }
3481
- agentBinaryIngressDraining = true;
3482
- setImmediate(() => {
3483
- agentBinaryIngressDraining = false;
3484
- let processed = 0;
3485
- while (agentBinaryIngressLanes.length > 0 && processed < AGENT_BINARY_INGRESS_DRAIN_BUDGET_PACKETS) {
3486
- const state = agentBinaryIngressLanes.shift();
3487
- if (!state) {
3488
- continue;
3489
- }
3490
-
3491
- state.binaryQueueScheduled = false;
3492
- const socket = state.binaryQueueSocket;
3493
- if (state.closed || !socket || socket.destroyed) {
3494
- if (Array.isArray(state.binaryQueue)) {
3495
- state.binaryQueue.length = 0;
3496
- }
3497
- continue;
3498
- }
3499
3489
 
3500
- const item = state.binaryQueue.shift();
3501
- if (item) {
3502
- handleAgentBinaryFrame(socket, state, item.header, item.payload);
3503
- processed += 1;
3504
- }
3505
-
3506
- if (state.binaryQueue.length > 0 && !state.closed && !socket.destroyed) {
3507
- state.binaryQueueScheduled = true;
3508
- agentBinaryIngressLanes.push(state);
3509
- }
3490
+ let processed = 0;
3491
+ while (state.binaryQueue.length > 0 && processed < AGENT_BINARY_INGRESS_DRAIN_BUDGET_PACKETS) {
3492
+ const item = state.binaryQueue.shift();
3493
+ if (!item) {
3494
+ continue;
3510
3495
  }
3496
+ handleAgentBinaryFrame(socket, state, item.header, item.payload);
3497
+ processed += 1;
3498
+ }
3511
3499
 
3512
- if (agentBinaryIngressLanes.length > 0) {
3513
- drainAgentBinaryIngressRoundRobin();
3514
- }
3515
- });
3500
+ if (state.binaryQueue.length > 0 && !state.closed && !socket.destroyed) {
3501
+ scheduleAgentBinaryIngress(socket, state);
3502
+ }
3516
3503
  }
3517
3504
 
3518
3505
  function enqueueAgentBinaryFrame(socket, state, header, payload) {
package/hub/src/server.js CHANGED
@@ -21,7 +21,7 @@ const packageInfo = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.js
21
21
  const httpHost = process.env.LIVEDESK_HUB_HTTP_HOST || '127.0.0.1';
22
22
  const httpPort = Number(process.env.LIVEDESK_HUB_HTTP_PORT || process.env.PORT || 5179);
23
23
  const frameBackpressureBytes = readPositiveIntegerEnv('LIVEDESK_FRAME_WS_BACKPRESSURE_BYTES', 16 * 1024 * 1024);
24
- const frameClientQueuePackets = readPositiveIntegerEnv('LIVEDESK_FRAME_WS_QUEUE_PACKETS', 12);
24
+ const frameClientQueuePackets = readPositiveIntegerEnv('LIVEDESK_FRAME_WS_QUEUE_PACKETS', 64);
25
25
  const frameClientDrainBudgetPackets = readPositiveIntegerEnv('LIVEDESK_FRAME_WS_DRAIN_BUDGET_PACKETS', 8);
26
26
  const frameClients = new Set();
27
27
  const frameClientsByDeviceId = new Map();
@@ -572,6 +572,10 @@ function enqueueFramePacketForClient(ws, packet, meta) {
572
572
  }
573
573
  if (ws.bufferedAmount > frameBackpressureBytes) {
574
574
  ws.liveDeskFrameBackpressured = true;
575
+ lane.dropped += 1;
576
+ if (meta.isH264 && meta.deviceId) {
577
+ lane.awaitingKeyFrames.add(meta.deviceId);
578
+ }
575
579
  ws.liveDeskFrameBackpressureDrops = Number(ws.liveDeskFrameBackpressureDrops || 0) + 1;
576
580
  return false;
577
581
  }
@@ -709,7 +713,7 @@ function updateAudioSubscription(ws, payload = {}) {
709
713
  });
710
714
  }
711
715
 
712
- function buildRemoteFrameBinaryPacket(frameEvent) {
716
+ function buildRemoteFrameBinaryPacket(frameEvent, diagnostics = {}) {
713
717
  const payload = frameEvent?.payload;
714
718
  if (!Buffer.isBuffer(payload) || payload.length === 0) {
715
719
  return null;
@@ -722,6 +726,7 @@ function buildRemoteFrameBinaryPacket(frameEvent) {
722
726
  kind: frameEvent.kind || 'live',
723
727
  deviceId: frameEvent.deviceId || frame.deviceId || '',
724
728
  frameSeq: Number(frame.frameSeq || 0) || 0,
729
+ streamFrameSeq: Number(frame.streamFrameSeq || 0) || 0,
725
730
  streamId: frame.streamId || '',
726
731
  commandId: frame.commandId || '',
727
732
  width: Number(frame.width || 0) || 0,
@@ -749,6 +754,8 @@ function buildRemoteFrameBinaryPacket(frameEvent) {
749
754
  agentSendDurationFrameSeq: Number(frame.agentSendDurationFrameSeq || 0) || 0,
750
755
  agentPaceMs: Number(frame.agentPaceMs || 0) || 0,
751
756
  droppedByAgent: Number(frame.droppedByAgent || 0) || 0,
757
+ hubIngressDropped: Number(frame.hubIngressDropped || 0) || 0,
758
+ hubClientDropped: Number(diagnostics.hubClientDropped || 0) || 0,
752
759
  hardwareEncoder: frame.hardwareEncoder || '',
753
760
  platformProfile: frame.platformProfile || '',
754
761
  fps: Number(frame.fps || 0) || 0,
@@ -810,19 +817,24 @@ function broadcastRemoteBinaryFrame(frameEvent) {
810
817
  const isH264 = String(frame.codec || frame.mimeType || frame.format || '').toLowerCase().includes('h264')
811
818
  || String(frame.frameMode || frame.mode || '').toLowerCase() === 'mode3-h264-hw';
812
819
  const isKeyFrame = frame.isKeyFrame === true || String(frame.chunkType || '').toLowerCase() === 'key';
813
- const packet = buildRemoteFrameBinaryPacket(frameEvent);
814
- if (!packet) {
815
- return;
816
- }
817
820
  const frameSeq = Number(frame.frameSeq || 0) || 0;
818
821
  for (const client of targetClients) {
819
822
  if (client.readyState !== client.OPEN) {
820
823
  continue;
821
824
  }
825
+ const lane = ensureFrameClientSendLane(client);
822
826
  if (client.liveDeskFrameBackpressured && isH264 && !isKeyFrame) {
827
+ lane.dropped += 1;
828
+ lane.awaitingKeyFrames.add(deviceId);
823
829
  client.liveDeskFrameBackpressureDrops = Number(client.liveDeskFrameBackpressureDrops || 0) + 1;
824
830
  continue;
825
831
  }
832
+ const packet = buildRemoteFrameBinaryPacket(frameEvent, {
833
+ hubClientDropped: lane.dropped
834
+ });
835
+ if (!packet) {
836
+ continue;
837
+ }
826
838
  enqueueFramePacketForClient(client, packet, { deviceId, frameSeq, isH264, isKeyFrame });
827
839
  }
828
840
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.141",
3
+ "version": "0.1.143",
4
4
  "description": "LiveDesk Hub and client launcher",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,7 @@
30
30
  "node": ">=20"
31
31
  },
32
32
  "dependencies": {
33
- "@livedesk/client": "0.1.96",
33
+ "@livedesk/client": "0.1.97",
34
34
  "cors": "^2.8.5",
35
35
  "express": "^4.21.2",
36
36
  "path-to-regexp": "0.1.13",