@livedesk/hub 0.1.26 → 0.1.28

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.
@@ -0,0 +1,427 @@
1
+ export function createRemoteFramePacketMetrics() {
2
+ return {
3
+ packetBuilds: 0,
4
+ packetAllocations: 0,
5
+ metadataSerializations: 0,
6
+ metadataUtf8Writes: 0,
7
+ payloadCopyOperations: 0,
8
+ payloadCopyBytes: 0,
9
+ packetBytes: 0,
10
+ fanoutFrames: 0,
11
+ fanoutEligibleSubscribers: 0,
12
+ fanoutEnqueuedSubscribers: 0,
13
+ fanoutMaxEligibleSubscribers: 0,
14
+ sparseTelemetryFrames: 0,
15
+ sparseTelemetrySuppressedFrames: 0,
16
+ sparseTelemetryFields: 0
17
+ };
18
+ }
19
+
20
+ export const REMOTE_FRAME_SPARSE_NATIVE_TELEMETRY_FIELDS = Object.freeze([
21
+ 'nativeCaptureTelemetryAvailable',
22
+ 'nativeCaptureTelemetryVersion',
23
+ 'nativeEncoderHardwareAccelerationKnown',
24
+ 'nativeEncoderHardwareAccelerated',
25
+ 'nativeEncoderHardwareQueryStatus',
26
+ 'nativeCaptureDisplayId',
27
+ 'nativeCaptureFps',
28
+ 'nativeCompressionFps',
29
+ 'nativeEncodedFps',
30
+ 'nativeCaptureSampleCount',
31
+ 'nativeEncodableFrameCount',
32
+ 'nativeSkippedFrameCount',
33
+ 'nativeCompressionOutputCount',
34
+ 'nativeEncodedFrameCount',
35
+ 'nativeEncodeFramesInFlight',
36
+ 'nativeEncodePendingFrameCount',
37
+ 'nativeEncodeTimestampOwnerCount',
38
+ 'nativeEncodeTimestampOwnerHighWatermark',
39
+ 'nativeBackpressureCoalescedFrameCount',
40
+ 'nativeBackpressureSkippedFrameCount',
41
+ 'nativeEncodePendingReplacedFrameCount',
42
+ 'nativeIdleSkippedFrameCount',
43
+ 'nativeVideoToolboxActiveSessionCount',
44
+ 'nativeVideoToolboxSessionHighWatermark',
45
+ 'nativeOutputQueueDepth',
46
+ 'nativeOutputQueueBytes',
47
+ 'nativeOutputQueueOldestAgeMs',
48
+ 'nativeOutputQueueHighWatermark',
49
+ 'nativeOutputQueueDroppedCount',
50
+ 'nativeOutputQueueAwaitingKeyFrame',
51
+ 'nativeCopyTelemetryAvailable',
52
+ 'nativeAnnexBAccessUnitAllocationCount',
53
+ 'nativeAnnexBPayloadCopyCount',
54
+ 'nativeAnnexBPayloadCopiedBytes',
55
+ 'nativeAndPipeResidenceAgeMs',
56
+ 'pipePayloadAllocationCount',
57
+ 'pipePayloadCopyCount',
58
+ 'pipePayloadCopiedBytes',
59
+ 'handoffDroppedFrameCount',
60
+ 'handoffRecoveryKeyFrameRequestCount',
61
+ 'handoffOldestFrameAgeMs',
62
+ 'nativeCaptureResourceTelemetryAvailable',
63
+ 'nativeCaptureTelemetryAt'
64
+ ]);
65
+
66
+ const EMPTY_SPARSE_TELEMETRY = Object.freeze({});
67
+ const REMOTE_FRAME_NATIVE_RESOURCE_TELEMETRY_FIELDS = new Set([
68
+ 'nativeEncodeFramesInFlight',
69
+ 'nativeEncodePendingFrameCount',
70
+ 'nativeEncodeTimestampOwnerCount',
71
+ 'nativeEncodeTimestampOwnerHighWatermark',
72
+ 'nativeBackpressureCoalescedFrameCount',
73
+ 'nativeBackpressureSkippedFrameCount',
74
+ 'nativeEncodePendingReplacedFrameCount',
75
+ 'nativeIdleSkippedFrameCount',
76
+ 'nativeVideoToolboxActiveSessionCount',
77
+ 'nativeVideoToolboxSessionHighWatermark',
78
+ 'nativeOutputQueueDepth',
79
+ 'nativeOutputQueueBytes',
80
+ 'nativeOutputQueueOldestAgeMs',
81
+ 'nativeOutputQueueHighWatermark',
82
+ 'nativeOutputQueueDroppedCount',
83
+ 'nativeOutputQueueAwaitingKeyFrame',
84
+ 'nativeCopyTelemetryAvailable',
85
+ 'nativeAnnexBAccessUnitAllocationCount',
86
+ 'nativeAnnexBPayloadCopyCount',
87
+ 'nativeAnnexBPayloadCopiedBytes',
88
+ 'nativeAndPipeResidenceAgeMs',
89
+ 'pipePayloadAllocationCount',
90
+ 'pipePayloadCopyCount',
91
+ 'pipePayloadCopiedBytes',
92
+ 'handoffDroppedFrameCount',
93
+ 'handoffRecoveryKeyFrameRequestCount',
94
+ 'handoffOldestFrameAgeMs'
95
+ ]);
96
+
97
+ function optionalFiniteNumber(value) {
98
+ if (value === null || value === undefined || value === '') {
99
+ return undefined;
100
+ }
101
+ const parsed = Number(value);
102
+ return Number.isFinite(parsed) ? parsed : undefined;
103
+ }
104
+
105
+ function createSparseTelemetryLane() {
106
+ return {
107
+ sessionId: '',
108
+ streamId: '',
109
+ commandId: '',
110
+ captureGeneration: 0,
111
+ monitorIndex: -1,
112
+ telemetryAt: '',
113
+ telemetryVersion: undefined,
114
+ captureSampleCount: undefined,
115
+ encodedFrameCount: undefined,
116
+ telemetryAvailable: undefined,
117
+ resourceTelemetryAvailable: undefined,
118
+ hardwareKnown: undefined,
119
+ lastEmittedAtMs: 0
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Contract:
125
+ * - At most two fixed lane records are retained per device (Wall + Control).
126
+ * - The device map has an explicit upper bound and only scans on cold-path
127
+ * admission after that bound has been reached.
128
+ * - A capture-generation handoff cannot inherit telemetry from its predecessor.
129
+ */
130
+ export function createRemoteFrameSparseTelemetryTracker(options = {}) {
131
+ return {
132
+ cadenceMs: Math.max(250, Number(options.cadenceMs) || 1_000),
133
+ maxDevices: Math.max(1, Number(options.maxDevices) || 1_024),
134
+ devices: new Map()
135
+ };
136
+ }
137
+
138
+ export function retainRemoteFrameSparseTelemetryDevices(tracker, activeDeviceIds) {
139
+ if (!tracker?.devices) {
140
+ return 0;
141
+ }
142
+ const active = activeDeviceIds instanceof Set
143
+ ? activeDeviceIds
144
+ : new Set(activeDeviceIds || []);
145
+ let removed = 0;
146
+ for (const deviceId of tracker.devices.keys()) {
147
+ if (!active.has(deviceId)) {
148
+ tracker.devices.delete(deviceId);
149
+ removed += 1;
150
+ }
151
+ }
152
+ return removed;
153
+ }
154
+
155
+ function sparseTelemetryDeviceState(tracker, deviceId, nowMs) {
156
+ let state = tracker.devices.get(deviceId);
157
+ if (state) {
158
+ state.lastSeenAtMs = nowMs;
159
+ return state;
160
+ }
161
+ if (tracker.devices.size >= tracker.maxDevices) {
162
+ let oldestDeviceId = '';
163
+ let oldestSeenAtMs = Number.POSITIVE_INFINITY;
164
+ for (const [candidateDeviceId, candidate] of tracker.devices) {
165
+ if (candidate.lastSeenAtMs < oldestSeenAtMs) {
166
+ oldestDeviceId = candidateDeviceId;
167
+ oldestSeenAtMs = candidate.lastSeenAtMs;
168
+ }
169
+ }
170
+ if (oldestDeviceId) {
171
+ tracker.devices.delete(oldestDeviceId);
172
+ }
173
+ }
174
+ state = {
175
+ lastSeenAtMs: nowMs,
176
+ wall: createSparseTelemetryLane(),
177
+ control: createSparseTelemetryLane()
178
+ };
179
+ tracker.devices.set(deviceId, state);
180
+ return state;
181
+ }
182
+
183
+ function sparseTelemetryOwnerChanged(lane, frame) {
184
+ return lane.sessionId !== String(frame.sessionId || '')
185
+ || lane.streamId !== String(frame.streamId || '')
186
+ || lane.commandId !== String(frame.commandId || '')
187
+ || lane.captureGeneration !== (Number(frame.captureGeneration || 0) || 0)
188
+ || lane.monitorIndex !== (Number.isFinite(Number(frame.monitorIndex)) ? Number(frame.monitorIndex) : 0);
189
+ }
190
+
191
+ function updateSparseTelemetryOwner(lane, frame) {
192
+ lane.sessionId = String(frame.sessionId || '');
193
+ lane.streamId = String(frame.streamId || '');
194
+ lane.commandId = String(frame.commandId || '');
195
+ lane.captureGeneration = Number(frame.captureGeneration || 0) || 0;
196
+ lane.monitorIndex = Number.isFinite(Number(frame.monitorIndex)) ? Number(frame.monitorIndex) : 0;
197
+ }
198
+
199
+ function appendSparseTelemetryValue(target, frame, field) {
200
+ if (!Object.prototype.hasOwnProperty.call(frame, field)) {
201
+ return;
202
+ }
203
+ const value = frame[field];
204
+ if (value === undefined || value === null || value === '') {
205
+ return;
206
+ }
207
+ if (field === 'nativeEncoderHardwareAccelerated'
208
+ || field === 'nativeOutputQueueAwaitingKeyFrame'
209
+ || field === 'nativeCopyTelemetryAvailable'
210
+ || field === 'nativeCaptureTelemetryAvailable'
211
+ || field === 'nativeEncoderHardwareAccelerationKnown'
212
+ || field === 'nativeCaptureResourceTelemetryAvailable') {
213
+ if (typeof value === 'boolean') {
214
+ target[field] = value;
215
+ }
216
+ return;
217
+ }
218
+ if (field === 'nativeEncoderHardwareQueryStatus' || field === 'nativeCaptureTelemetryAt') {
219
+ target[field] = String(value);
220
+ return;
221
+ }
222
+ const parsed = optionalFiniteNumber(value);
223
+ if (parsed !== undefined) {
224
+ target[field] = parsed;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Returns only native capture diagnostics that need to cross the browser
230
+ * packet boundary now. Per-frame timing/currentness remains in the base packet.
231
+ * Repeated one-Hz native samples are not expanded into 24/30 identical JSON
232
+ * objects per second.
233
+ */
234
+ export function selectSparseRemoteFrameTelemetry(
235
+ frame,
236
+ tracker,
237
+ nowMs = Date.now(),
238
+ metrics = null,
239
+ deviceIdOverride = ''
240
+ ) {
241
+ if (!frame || !tracker?.devices) {
242
+ return EMPTY_SPARSE_TELEMETRY;
243
+ }
244
+ const deviceId = String(deviceIdOverride || frame.deviceId || '').trim();
245
+ if (!deviceId) {
246
+ return EMPTY_SPARSE_TELEMETRY;
247
+ }
248
+ const purpose = String(frame.streamPurpose || '').toLowerCase() === 'control'
249
+ ? 'control'
250
+ : 'wall';
251
+ const state = sparseTelemetryDeviceState(tracker, deviceId, nowMs);
252
+ const lane = state[purpose];
253
+ const ownerChanged = sparseTelemetryOwnerChanged(lane, frame);
254
+ if (ownerChanged) {
255
+ updateSparseTelemetryOwner(lane, frame);
256
+ }
257
+
258
+ const telemetryAt = String(frame.nativeCaptureTelemetryAt || '');
259
+ const telemetryVersion = optionalFiniteNumber(frame.nativeCaptureTelemetryVersion);
260
+ const captureSampleCount = optionalFiniteNumber(frame.nativeCaptureSampleCount);
261
+ const encodedFrameCount = optionalFiniteNumber(frame.nativeEncodedFrameCount);
262
+ const telemetryAvailable = typeof frame.nativeCaptureTelemetryAvailable === 'boolean'
263
+ ? frame.nativeCaptureTelemetryAvailable
264
+ : undefined;
265
+ const resourceTelemetryAvailable = typeof frame.nativeCaptureResourceTelemetryAvailable === 'boolean'
266
+ ? frame.nativeCaptureResourceTelemetryAvailable
267
+ : undefined;
268
+ const hardwareKnown = typeof frame.nativeEncoderHardwareAccelerationKnown === 'boolean'
269
+ ? frame.nativeEncoderHardwareAccelerationKnown
270
+ : undefined;
271
+ const sampleChanged = lane.telemetryAt !== telemetryAt
272
+ || lane.telemetryVersion !== telemetryVersion
273
+ || lane.captureSampleCount !== captureSampleCount
274
+ || lane.encodedFrameCount !== encodedFrameCount
275
+ || lane.telemetryAvailable !== telemetryAvailable
276
+ || lane.resourceTelemetryAvailable !== resourceTelemetryAvailable
277
+ || lane.hardwareKnown !== hardwareKnown;
278
+ const cadenceElapsed = nowMs - lane.lastEmittedAtMs >= tracker.cadenceMs;
279
+
280
+ lane.telemetryAt = telemetryAt;
281
+ lane.telemetryVersion = telemetryVersion;
282
+ lane.captureSampleCount = captureSampleCount;
283
+ lane.encodedFrameCount = encodedFrameCount;
284
+ lane.telemetryAvailable = telemetryAvailable;
285
+ lane.resourceTelemetryAvailable = resourceTelemetryAvailable;
286
+ lane.hardwareKnown = hardwareKnown;
287
+
288
+ if (!ownerChanged && !sampleChanged && !cadenceElapsed) {
289
+ incrementMetric(metrics, 'sparseTelemetrySuppressedFrames');
290
+ return EMPTY_SPARSE_TELEMETRY;
291
+ }
292
+
293
+ lane.lastEmittedAtMs = nowMs;
294
+ const selected = {};
295
+ for (const field of REMOTE_FRAME_SPARSE_NATIVE_TELEMETRY_FIELDS) {
296
+ if (field !== 'nativeCaptureTelemetryAvailable' && telemetryAvailable !== true) {
297
+ continue;
298
+ }
299
+ if (REMOTE_FRAME_NATIVE_RESOURCE_TELEMETRY_FIELDS.has(field)
300
+ && resourceTelemetryAvailable !== true) {
301
+ continue;
302
+ }
303
+ appendSparseTelemetryValue(selected, frame, field);
304
+ }
305
+ incrementMetric(metrics, 'sparseTelemetryFrames');
306
+ incrementMetric(metrics, 'sparseTelemetryFields', Object.keys(selected).length);
307
+ return selected;
308
+ }
309
+
310
+ function incrementMetric(metrics, property, amount = 1) {
311
+ if (!metrics || !Object.prototype.hasOwnProperty.call(metrics, property)) {
312
+ return;
313
+ }
314
+ metrics[property] = Math.max(0, Number(metrics[property] || 0) + amount);
315
+ }
316
+
317
+ /**
318
+ * Contract:
319
+ * - One call owns exactly one browser wire packet.
320
+ * - Metadata is serialized and UTF-8 encoded once directly into that packet.
321
+ * - The compressed payload is copied once into the exact packet owner.
322
+ * - Callers must treat the returned frozen descriptor and its Buffer as
323
+ * immutable and may share the same descriptor across every subscriber lane.
324
+ */
325
+ export function buildImmutableRemoteFramePacket(metadata, payload, metrics = null) {
326
+ if (!Buffer.isBuffer(payload) || payload.length === 0) {
327
+ return null;
328
+ }
329
+
330
+ const metadataJson = JSON.stringify(metadata);
331
+ const metadataByteLength = Buffer.byteLength(metadataJson, 'utf8');
332
+ const packetByteLength = 4 + metadataByteLength + payload.length;
333
+ const packet = Buffer.allocUnsafeSlow(packetByteLength);
334
+ packet.writeUInt32BE(metadataByteLength, 0);
335
+ const metadataBytesWritten = packet.write(
336
+ metadataJson,
337
+ 4,
338
+ metadataByteLength,
339
+ 'utf8'
340
+ );
341
+ if (metadataBytesWritten !== metadataByteLength) {
342
+ throw new Error('remote-frame-metadata-write-incomplete');
343
+ }
344
+ const payloadBytesWritten = payload.copy(packet, 4 + metadataByteLength);
345
+ if (payloadBytesWritten !== payload.length) {
346
+ throw new Error('remote-frame-payload-copy-incomplete');
347
+ }
348
+
349
+ incrementMetric(metrics, 'packetBuilds');
350
+ incrementMetric(metrics, 'packetAllocations');
351
+ incrementMetric(metrics, 'metadataSerializations');
352
+ incrementMetric(metrics, 'metadataUtf8Writes');
353
+ incrementMetric(metrics, 'payloadCopyOperations');
354
+ incrementMetric(metrics, 'payloadCopyBytes', payload.length);
355
+ incrementMetric(metrics, 'packetBytes', packetByteLength);
356
+
357
+ return Object.freeze({
358
+ packet,
359
+ byteLength: packetByteLength,
360
+ metadataByteLength,
361
+ payloadByteLength: payload.length
362
+ });
363
+ }
364
+
365
+ /**
366
+ * Fan out one immutable packet owner without rebuilding subscriber-specific
367
+ * metadata or payload storage. Per-subscriber queue admission remains the
368
+ * enqueue callback's responsibility.
369
+ */
370
+ export function fanoutImmutableRemoteFramePacket(
371
+ packetOwner,
372
+ eligibleSubscribers,
373
+ enqueue,
374
+ metrics = null
375
+ ) {
376
+ if (!packetOwner?.packet || typeof enqueue !== 'function') {
377
+ return {
378
+ eligibleSubscribers: 0,
379
+ enqueuedSubscribers: 0
380
+ };
381
+ }
382
+
383
+ const subscribers = Array.isArray(eligibleSubscribers)
384
+ ? eligibleSubscribers
385
+ : [...(eligibleSubscribers || [])];
386
+ let enqueuedSubscribers = 0;
387
+ for (const subscriber of subscribers) {
388
+ if (enqueue(subscriber, packetOwner) === true) {
389
+ enqueuedSubscribers += 1;
390
+ }
391
+ }
392
+
393
+ incrementMetric(metrics, 'fanoutFrames');
394
+ incrementMetric(metrics, 'fanoutEligibleSubscribers', subscribers.length);
395
+ incrementMetric(metrics, 'fanoutEnqueuedSubscribers', enqueuedSubscribers);
396
+ if (metrics) {
397
+ metrics.fanoutMaxEligibleSubscribers = Math.max(
398
+ Math.max(0, Number(metrics.fanoutMaxEligibleSubscribers || 0)),
399
+ subscribers.length
400
+ );
401
+ }
402
+
403
+ return {
404
+ eligibleSubscribers: subscribers.length,
405
+ enqueuedSubscribers
406
+ };
407
+ }
408
+
409
+ export function snapshotRemoteFramePacketMetrics(metrics) {
410
+ const source = metrics || {};
411
+ return {
412
+ packetBuilds: Math.max(0, Number(source.packetBuilds || 0)),
413
+ packetAllocations: Math.max(0, Number(source.packetAllocations || 0)),
414
+ metadataSerializations: Math.max(0, Number(source.metadataSerializations || 0)),
415
+ metadataUtf8Writes: Math.max(0, Number(source.metadataUtf8Writes || 0)),
416
+ payloadCopyOperations: Math.max(0, Number(source.payloadCopyOperations || 0)),
417
+ payloadCopyBytes: Math.max(0, Number(source.payloadCopyBytes || 0)),
418
+ packetBytes: Math.max(0, Number(source.packetBytes || 0)),
419
+ fanoutFrames: Math.max(0, Number(source.fanoutFrames || 0)),
420
+ fanoutEligibleSubscribers: Math.max(0, Number(source.fanoutEligibleSubscribers || 0)),
421
+ fanoutEnqueuedSubscribers: Math.max(0, Number(source.fanoutEnqueuedSubscribers || 0)),
422
+ fanoutMaxEligibleSubscribers: Math.max(0, Number(source.fanoutMaxEligibleSubscribers || 0)),
423
+ sparseTelemetryFrames: Math.max(0, Number(source.sparseTelemetryFrames || 0)),
424
+ sparseTelemetrySuppressedFrames: Math.max(0, Number(source.sparseTelemetrySuppressedFrames || 0)),
425
+ sparseTelemetryFields: Math.max(0, Number(source.sparseTelemetryFields || 0))
426
+ };
427
+ }