livedesk 0.1.456 → 0.1.457
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/package.json +1 -1
- package/hub/src/live-stream-monitor-contract.js +38 -0
- package/hub/src/remote-hub.js +105 -11
- package/hub/src/server.js +3 -7
- package/package.json +1 -1
package/hub/package.json
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const LIVE_STREAM_MONITOR_INDEX_MIN = 0;
|
|
2
|
+
export const LIVE_STREAM_MONITOR_INDEX_MAX = 63;
|
|
3
|
+
|
|
4
|
+
export function parseExactLiveStreamMonitorIndex(value) {
|
|
5
|
+
return typeof value === 'number'
|
|
6
|
+
&& Number.isInteger(value)
|
|
7
|
+
&& value >= LIVE_STREAM_MONITOR_INDEX_MIN
|
|
8
|
+
&& value <= LIVE_STREAM_MONITOR_INDEX_MAX
|
|
9
|
+
? value
|
|
10
|
+
: null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isReusedLiveStreamFrameReady(result, activeStream, expectedBinding) {
|
|
14
|
+
const expectedMonitorIndex = parseExactLiveStreamMonitorIndex(expectedBinding?.monitorIndex);
|
|
15
|
+
const activeMonitorIndex = parseExactLiveStreamMonitorIndex(activeStream?.monitorIndex);
|
|
16
|
+
const latestFrame = activeStream?.latestFrame;
|
|
17
|
+
const frameMonitorIndex = parseExactLiveStreamMonitorIndex(latestFrame?.monitorIndex);
|
|
18
|
+
const expectedStreamId = String(expectedBinding?.streamId || '');
|
|
19
|
+
const expectedCommandId = String(expectedBinding?.commandId || '');
|
|
20
|
+
const expectedCaptureGeneration = Number(expectedBinding?.captureGeneration || 0);
|
|
21
|
+
|
|
22
|
+
return result?.reused === true
|
|
23
|
+
&& result?.ready === true
|
|
24
|
+
&& Number(activeStream?.framesReceived || 0) > 0
|
|
25
|
+
&& !!expectedStreamId
|
|
26
|
+
&& String(activeStream?.streamId || '') === expectedStreamId
|
|
27
|
+
&& !!expectedCommandId
|
|
28
|
+
&& String(activeStream?.commandId || '') === expectedCommandId
|
|
29
|
+
&& Number.isInteger(expectedCaptureGeneration)
|
|
30
|
+
&& expectedCaptureGeneration > 0
|
|
31
|
+
&& Number(activeStream?.captureGeneration || 0) === expectedCaptureGeneration
|
|
32
|
+
&& latestFrame?.currentGenerationVerified === true
|
|
33
|
+
&& String(latestFrame?.commandId || '') === expectedCommandId
|
|
34
|
+
&& Number(latestFrame?.captureGeneration || 0) === expectedCaptureGeneration
|
|
35
|
+
&& expectedMonitorIndex !== null
|
|
36
|
+
&& activeMonitorIndex === expectedMonitorIndex
|
|
37
|
+
&& frameMonitorIndex === expectedMonitorIndex;
|
|
38
|
+
}
|
package/hub/src/remote-hub.js
CHANGED
|
@@ -2,6 +2,7 @@ import net from 'net';
|
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import crypto from 'crypto';
|
|
4
4
|
import { createHubRelayControl } from './transport/relay-hub-control.js';
|
|
5
|
+
import { parseExactLiveStreamMonitorIndex } from './live-stream-monitor-contract.js';
|
|
5
6
|
|
|
6
7
|
const DEFAULT_REMOTE_HUB_PORT = 5197;
|
|
7
8
|
const DEFAULT_REMOTE_HUB_HOST = '0.0.0.0';
|
|
@@ -3983,6 +3984,34 @@ export function createRemoteHub(options = {}) {
|
|
|
3983
3984
|
});
|
|
3984
3985
|
return null;
|
|
3985
3986
|
}
|
|
3987
|
+
const pendingMonitorIndex = parseExactLiveStreamMonitorIndex(pending.monitorIndex);
|
|
3988
|
+
const messageMonitorIndex = parseExactLiveStreamMonitorIndex(message.monitorIndex);
|
|
3989
|
+
if (pendingMonitorIndex === null || messageMonitorIndex === null) {
|
|
3990
|
+
emitRemoteEvent('RemoteLiveStreamOpenIgnored', device, {
|
|
3991
|
+
reason: 'invalid-pending-monitor-metadata',
|
|
3992
|
+
streamId: streamState.streamId,
|
|
3993
|
+
commandId,
|
|
3994
|
+
captureGeneration: messageCaptureGeneration,
|
|
3995
|
+
pendingCaptureGeneration,
|
|
3996
|
+
monitorIndex: messageMonitorIndex,
|
|
3997
|
+
pendingMonitorIndex,
|
|
3998
|
+
transport
|
|
3999
|
+
});
|
|
4000
|
+
return null;
|
|
4001
|
+
}
|
|
4002
|
+
if (messageMonitorIndex !== pendingMonitorIndex) {
|
|
4003
|
+
emitRemoteEvent('RemoteLiveStreamOpenIgnored', device, {
|
|
4004
|
+
reason: 'stale-pending-monitor',
|
|
4005
|
+
streamId: streamState.streamId,
|
|
4006
|
+
commandId,
|
|
4007
|
+
captureGeneration: messageCaptureGeneration,
|
|
4008
|
+
pendingCaptureGeneration,
|
|
4009
|
+
monitorIndex: messageMonitorIndex,
|
|
4010
|
+
pendingMonitorIndex,
|
|
4011
|
+
transport
|
|
4012
|
+
});
|
|
4013
|
+
return null;
|
|
4014
|
+
}
|
|
3986
4015
|
|
|
3987
4016
|
const previousCommandId = safeString(streamState.commandId, 128);
|
|
3988
4017
|
const transfer = buildRemoteFrameTransferDescriptorFromMessage(
|
|
@@ -4011,9 +4040,7 @@ export function createRemoteHub(options = {}) {
|
|
|
4011
4040
|
height: Number.isFinite(Number(message.height)) ? Number(message.height) : Number(pending.height || 0),
|
|
4012
4041
|
sourceWidth: Number.isFinite(Number(message.sourceWidth)) ? Number(message.sourceWidth) : Number(pending.sourceWidth || 0),
|
|
4013
4042
|
sourceHeight: Number.isFinite(Number(message.sourceHeight)) ? Number(message.sourceHeight) : Number(pending.sourceHeight || 0),
|
|
4014
|
-
monitorIndex:
|
|
4015
|
-
? normalizeMonitorIndex(message.monitorIndex)
|
|
4016
|
-
: Number(pending.monitorIndex || 0),
|
|
4043
|
+
monitorIndex: pendingMonitorIndex,
|
|
4017
4044
|
monitorCount: Number.isFinite(Number(message.monitorCount))
|
|
4018
4045
|
? clampNumber(message.monitorCount, 1, 64, 1)
|
|
4019
4046
|
: Number(pending.monitorCount || 1),
|
|
@@ -4106,6 +4133,37 @@ export function createRemoteHub(options = {}) {
|
|
|
4106
4133
|
return true;
|
|
4107
4134
|
}
|
|
4108
4135
|
|
|
4136
|
+
const activeMonitorIndex = parseExactLiveStreamMonitorIndex(streamState.monitorIndex);
|
|
4137
|
+
const messageMonitorIndex = parseExactLiveStreamMonitorIndex(message.monitorIndex);
|
|
4138
|
+
if (activeMonitorIndex === null || messageMonitorIndex === null) {
|
|
4139
|
+
emitRemoteEvent('RemoteLiveStreamOpenIgnored', device, {
|
|
4140
|
+
reason: 'invalid-live-stream-monitor-metadata',
|
|
4141
|
+
streamId,
|
|
4142
|
+
commandId,
|
|
4143
|
+
activeCommandId,
|
|
4144
|
+
captureGeneration: Number(message.captureGeneration || 0),
|
|
4145
|
+
activeCaptureGeneration: Number(streamState.captureGeneration || 0),
|
|
4146
|
+
monitorIndex: messageMonitorIndex,
|
|
4147
|
+
activeMonitorIndex,
|
|
4148
|
+
transport
|
|
4149
|
+
});
|
|
4150
|
+
return false;
|
|
4151
|
+
}
|
|
4152
|
+
if (messageMonitorIndex !== activeMonitorIndex) {
|
|
4153
|
+
emitRemoteEvent('RemoteLiveStreamOpenIgnored', device, {
|
|
4154
|
+
reason: 'stale-live-stream-monitor',
|
|
4155
|
+
streamId,
|
|
4156
|
+
commandId,
|
|
4157
|
+
activeCommandId,
|
|
4158
|
+
captureGeneration: Number(message.captureGeneration || 0),
|
|
4159
|
+
activeCaptureGeneration: Number(streamState.captureGeneration || 0),
|
|
4160
|
+
monitorIndex: messageMonitorIndex,
|
|
4161
|
+
activeMonitorIndex,
|
|
4162
|
+
transport
|
|
4163
|
+
});
|
|
4164
|
+
return false;
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4109
4167
|
const transfer = buildRemoteFrameTransferDescriptorFromMessage(message, streamState.mode || DEFAULT_REMOTE_FRAME_MODE);
|
|
4110
4168
|
const openedAt = safeString(message.openedAt, 80) || device.lastSeenAt || new Date().toISOString();
|
|
4111
4169
|
const nextStreamState = {
|
|
@@ -4126,7 +4184,7 @@ export function createRemoteHub(options = {}) {
|
|
|
4126
4184
|
height: Number.isFinite(Number(message.height)) ? Number(message.height) : Number(streamState.height || 0),
|
|
4127
4185
|
sourceWidth: Number.isFinite(Number(message.sourceWidth)) ? Number(message.sourceWidth) : Number(streamState.sourceWidth || 0),
|
|
4128
4186
|
sourceHeight: Number.isFinite(Number(message.sourceHeight)) ? Number(message.sourceHeight) : Number(streamState.sourceHeight || 0),
|
|
4129
|
-
monitorIndex:
|
|
4187
|
+
monitorIndex: activeMonitorIndex,
|
|
4130
4188
|
monitorCount: Number.isFinite(Number(message.monitorCount)) ? clampNumber(message.monitorCount, 1, 64, 1) : Number(streamState.monitorCount || 1),
|
|
4131
4189
|
streamPurpose: safeString(message.streamPurpose || streamState.streamPurpose, 24) || 'wall',
|
|
4132
4190
|
captureGeneration: Number.isSafeInteger(Number(message.captureGeneration)) && Number(message.captureGeneration) > 0
|
|
@@ -4204,8 +4262,40 @@ export function createRemoteHub(options = {}) {
|
|
|
4204
4262
|
});
|
|
4205
4263
|
return false;
|
|
4206
4264
|
}
|
|
4207
|
-
|
|
4208
|
-
const
|
|
4265
|
+
const activeMonitorIndex = parseExactLiveStreamMonitorIndex(streamState.monitorIndex);
|
|
4266
|
+
const frameMonitorIndex = parseExactLiveStreamMonitorIndex(message.monitorIndex);
|
|
4267
|
+
if (activeMonitorIndex === null || frameMonitorIndex === null) {
|
|
4268
|
+
device.counters.liveFramesDropped += 1;
|
|
4269
|
+
emitRemoteEvent('RemoteFrameDropped', device, {
|
|
4270
|
+
reason: 'invalid-live-stream-monitor-frame-metadata',
|
|
4271
|
+
streamId,
|
|
4272
|
+
commandId,
|
|
4273
|
+
captureGeneration: frameCaptureGeneration,
|
|
4274
|
+
activeCaptureGeneration,
|
|
4275
|
+
monitorIndex: frameMonitorIndex,
|
|
4276
|
+
activeMonitorIndex,
|
|
4277
|
+
frameSeq: Number.isFinite(frameSeq) ? frameSeq : null,
|
|
4278
|
+
transport
|
|
4279
|
+
});
|
|
4280
|
+
return false;
|
|
4281
|
+
}
|
|
4282
|
+
if (frameMonitorIndex !== activeMonitorIndex) {
|
|
4283
|
+
device.counters.liveFramesDropped += 1;
|
|
4284
|
+
emitRemoteEvent('RemoteFrameDropped', device, {
|
|
4285
|
+
reason: 'stale-live-stream-monitor-frame',
|
|
4286
|
+
streamId,
|
|
4287
|
+
commandId,
|
|
4288
|
+
captureGeneration: frameCaptureGeneration,
|
|
4289
|
+
activeCaptureGeneration,
|
|
4290
|
+
monitorIndex: frameMonitorIndex,
|
|
4291
|
+
activeMonitorIndex,
|
|
4292
|
+
frameSeq: Number.isFinite(frameSeq) ? frameSeq : null,
|
|
4293
|
+
transport
|
|
4294
|
+
});
|
|
4295
|
+
return false;
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4298
|
+
const byteLength = normalizeFrameByteLength(framePayload, frameData);
|
|
4209
4299
|
if ((!Buffer.isBuffer(framePayload) && !frameData)
|
|
4210
4300
|
|| byteLength > MAX_STREAM_BINARY_BYTES
|
|
4211
4301
|
|| !Number.isFinite(frameSeq)) {
|
|
@@ -4242,7 +4332,8 @@ export function createRemoteHub(options = {}) {
|
|
|
4242
4332
|
const currentGenerationVerified = (!activeCommandId || commandId === activeCommandId)
|
|
4243
4333
|
&& (activeCaptureGeneration <= 0
|
|
4244
4334
|
|| (frameCaptureGeneration > 0
|
|
4245
|
-
&& frameCaptureGeneration === activeCaptureGeneration))
|
|
4335
|
+
&& frameCaptureGeneration === activeCaptureGeneration))
|
|
4336
|
+
&& frameMonitorIndex === activeMonitorIndex;
|
|
4246
4337
|
const readyFrameReceivedBeforeFrame = streamState.readyFrameReceived === true;
|
|
4247
4338
|
const captureTimingAvailable = hasFrameCaptureTiming(message);
|
|
4248
4339
|
const videoTransport = transport === 'udp-p2p'
|
|
@@ -4323,7 +4414,7 @@ export function createRemoteHub(options = {}) {
|
|
|
4323
4414
|
udpIncompleteFrames: Number.isFinite(Number(message.udpIncompleteFrames)) ? Number(message.udpIncompleteFrames) : 0,
|
|
4324
4415
|
hardwareEncoder: safeString(message.hardwareEncoder, 80),
|
|
4325
4416
|
platformProfile: safeString(message.platformProfile, 80),
|
|
4326
|
-
monitorIndex:
|
|
4417
|
+
monitorIndex: activeMonitorIndex,
|
|
4327
4418
|
monitorCount: Number.isFinite(Number(message.monitorCount)) ? clampNumber(message.monitorCount, 1, 64, 1) : Number(streamState.monitorCount || 1),
|
|
4328
4419
|
transferProtocol: transfer.transferProtocol,
|
|
4329
4420
|
transferProtocolVersion: transfer.transferProtocolVersion,
|
|
@@ -4402,7 +4493,6 @@ export function createRemoteHub(options = {}) {
|
|
|
4402
4493
|
streamState.height = device.latestLiveFrame.height;
|
|
4403
4494
|
streamState.sourceWidth = device.latestLiveFrame.sourceWidth;
|
|
4404
4495
|
streamState.sourceHeight = device.latestLiveFrame.sourceHeight;
|
|
4405
|
-
streamState.monitorIndex = device.latestLiveFrame.monitorIndex;
|
|
4406
4496
|
streamState.monitorCount = device.latestLiveFrame.monitorCount;
|
|
4407
4497
|
ensureDeviceLiveStreams(device).set(streamId, streamState);
|
|
4408
4498
|
device.counters.liveFramesReceived += 1;
|
|
@@ -6457,8 +6547,12 @@ export function createRemoteHub(options = {}) {
|
|
|
6457
6547
|
}
|
|
6458
6548
|
const activeCaptureGeneration = Number(activeLiveStream.captureGeneration || 0);
|
|
6459
6549
|
const frameCaptureGeneration = Number(latestFrame.captureGeneration || 0);
|
|
6460
|
-
|
|
6461
|
-
|
|
6550
|
+
const activeMonitorIndex = parseExactLiveStreamMonitorIndex(activeLiveStream.monitorIndex);
|
|
6551
|
+
const frameMonitorIndex = parseExactLiveStreamMonitorIndex(latestFrame.monitorIndex);
|
|
6552
|
+
return activeMonitorIndex !== null
|
|
6553
|
+
&& frameMonitorIndex === activeMonitorIndex
|
|
6554
|
+
&& (activeCaptureGeneration <= 0
|
|
6555
|
+
|| (frameCaptureGeneration > 0 && frameCaptureGeneration === activeCaptureGeneration));
|
|
6462
6556
|
}
|
|
6463
6557
|
|
|
6464
6558
|
function getLiveStreamFreshWindowMs(activeLiveStream) {
|
package/hub/src/server.js
CHANGED
|
@@ -7,8 +7,9 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, wri
|
|
|
7
7
|
import { dirname, resolve } from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import os from 'node:os';
|
|
10
|
-
import { WebSocketServer } from 'ws';
|
|
10
|
+
import { WebSocketServer } from 'ws';
|
|
11
11
|
import { createRemoteHub } from './remote-hub.js';
|
|
12
|
+
import { isReusedLiveStreamFrameReady } from './live-stream-monitor-contract.js';
|
|
12
13
|
import { buildMode4AtlasSessionKey, Mode4AtlasPool, planMode4AtlasInputTransitions } from './mode4-atlas-pool.js';
|
|
13
14
|
import { resolveMode4AtlasTileSize } from './mode4-atlas-sizing.js';
|
|
14
15
|
import { createHubFilesystem } from './filesystem/hub-filesystem.js';
|
|
@@ -1976,12 +1977,7 @@ function startFrameSubscriptionLive(ws, reason = 'subscribe', onlyDeviceId = '',
|
|
|
1976
1977
|
readySent: false
|
|
1977
1978
|
};
|
|
1978
1979
|
const activeStream = device?.activeLiveStream;
|
|
1979
|
-
const reusedFrameReady = result
|
|
1980
|
-
&& result.ready === true
|
|
1981
|
-
&& Number(result.framesReceived ?? activeStream?.framesReceived ?? 0) > 0
|
|
1982
|
-
&& String(activeStream?.streamId || expectedBinding.streamId) === expectedBinding.streamId
|
|
1983
|
-
&& String(activeStream?.commandId || expectedBinding.commandId) === expectedBinding.commandId
|
|
1984
|
-
&& Number(activeStream?.captureGeneration || expectedBinding.captureGeneration) === expectedBinding.captureGeneration;
|
|
1980
|
+
const reusedFrameReady = isReusedLiveStreamFrameReady(result, activeStream, expectedBinding);
|
|
1985
1981
|
expectedBinding.readySent = reusedFrameReady;
|
|
1986
1982
|
ws.liveDeskStreamIdsByDeviceId.set(deviceId, result.streamId);
|
|
1987
1983
|
ws.liveDeskExpectedStreamBindingsByDeviceId.set(deviceId, expectedBinding);
|