@camstack/addon-pipeline 1.2.365 → 1.2.367
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/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +3 -3
- package/dist/{custom-model-catalog-CtpK3FKL.mjs → custom-model-catalog-BqKEZYFI.mjs} +2 -2
- package/dist/{custom-model-catalog-C34VqcPo.js → custom-model-catalog-CESZ9DM5.js} +2 -2
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-i4kS_3O7.js → dist-BmQm6_nm.js} +19889 -1345
- package/dist/{dist-BshC0NZR.mjs → dist-ClKIJKn0.mjs} +19889 -1345
- package/dist/{model-catalogs-Cy7mZRWh.js → model-catalogs-B3lwxS5R.js} +1 -1
- package/dist/{model-catalogs-DNx5ATgm.mjs → model-catalogs-DKHNVZC0.mjs} +1 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-TzgGsCvW.js → node-BqTmCMlP.js} +1 -1
- package/dist/{node-Dqmrb990.mjs → node-DlpWXqvO.mjs} +1 -1
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-DyfrbFY4.mjs → process-memory-BjIiiXGZ.mjs} +1 -1
- package/dist/{process-memory-DLCPFTGC.js → process-memory-DHF3ynR3.js} +1 -1
- package/dist/recorder/index.js +2 -2
- package/dist/recorder/index.mjs +2 -2
- package/dist/{segment-demux-js-C9AYdKkD.js → segment-demux-js-D1Up0mTP.js} +1 -1
- package/dist/{segment-demux-js-COoPqx93.mjs → segment-demux-js-Du_eAog1.mjs} +1 -1
- package/dist/stream-broker/_stub.js +83 -83
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DVgnss_e.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CRxJwkgE.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CIBBQAJc.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DNGQ-Tic.mjs +26 -0
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-ZvA7Cl_1.mjs → hostInit-DEnYuDjZ.mjs} +2 -2
- package/dist/stream-broker/index.js +441 -17
- package/dist/stream-broker/index.mjs +441 -17
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DWI1BCfa.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BUBmyDde.mjs +0 -26
|
@@ -3,12 +3,12 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
6
|
+
const require_dist = require("../dist-BmQm6_nm.js");
|
|
7
|
+
const require_node = require("../node-BqTmCMlP.js");
|
|
8
8
|
const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
|
|
9
9
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
10
10
|
const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
|
|
11
|
-
const require_segment_demux_js = require("../segment-demux-js-
|
|
11
|
+
const require_segment_demux_js = require("../segment-demux-js-D1Up0mTP.js");
|
|
12
12
|
let node_os = require("node:os");
|
|
13
13
|
node_os = require_chunk.__toESM(node_os);
|
|
14
14
|
let node_crypto = require("node:crypto");
|
|
@@ -22,6 +22,7 @@ let node_fs_promises = require("node:fs/promises");
|
|
|
22
22
|
let node_url = require("node:url");
|
|
23
23
|
let node_module = require("node:module");
|
|
24
24
|
let _camstack_system_addon_utils = require("@camstack/system/addon-utils");
|
|
25
|
+
let node_v8 = require("node:v8");
|
|
25
26
|
let node_net = require("node:net");
|
|
26
27
|
node_net = require_chunk.__toESM(node_net);
|
|
27
28
|
let node_dgram = require("node:dgram");
|
|
@@ -3085,6 +3086,258 @@ var RtpPacketizer = class {
|
|
|
3085
3086
|
}
|
|
3086
3087
|
};
|
|
3087
3088
|
//#endregion
|
|
3089
|
+
//#region src/stream-broker/stream-broker/broker-mem-probe.ts
|
|
3090
|
+
/**
|
|
3091
|
+
* broker-mem-probe — what owns the broker runner's NATIVE memory.
|
|
3092
|
+
*
|
|
3093
|
+
* The runner's `[mem]` heartbeat (`kernel/heap-watch.ts`) says how big the
|
|
3094
|
+
* process is; it cannot say who holds it. On the hub, `stream-broker` grew
|
|
3095
|
+
* 250 -> 1015 MB in steps while `heapUsed` stayed at ~90 MB and `arrayBuffers`
|
|
3096
|
+
* at ~150 MB, so ~600 MB sat outside everything the heartbeat reports. This
|
|
3097
|
+
* module is the attribution half: one `[broker-mem]` line every five minutes
|
|
3098
|
+
* carrying the kernel's view of the process (RssAnon / RssFile, the `[heap]`
|
|
3099
|
+
* brk mapping, the count of anonymous mappings) NEXT TO the broker's own
|
|
3100
|
+
* structures (rings, write queues, consumers by kind, egress children), so a
|
|
3101
|
+
* step in RSS can be laid against what changed.
|
|
3102
|
+
*
|
|
3103
|
+
* It is a probe, not a fix: no caps, no allocator tuning, no snapshot. Cost is
|
|
3104
|
+
* O(brokers + sessions) per line and O(1) per packet (two integer adds in
|
|
3105
|
+
* `copyOwned`). Everything here is a pure function over plain data so the
|
|
3106
|
+
* aggregation and the `/proc` parsers are testable on fixture text.
|
|
3107
|
+
*/
|
|
3108
|
+
var MB = 1024 * 1024;
|
|
3109
|
+
/** Cadence of the `[broker-mem]` line — aligned with the runner's `[mem]`. */
|
|
3110
|
+
var BROKER_MEM_INTERVAL_MS = 5 * 6e4;
|
|
3111
|
+
var copiedPackets = 0;
|
|
3112
|
+
var copiedBytes = 0;
|
|
3113
|
+
/** O(1) per packet: called by `copyOwned`. No allocation. */
|
|
3114
|
+
function recordCopyOwned(bytes) {
|
|
3115
|
+
copiedPackets += 1;
|
|
3116
|
+
copiedBytes += bytes;
|
|
3117
|
+
}
|
|
3118
|
+
function readCopyOwnedTotals() {
|
|
3119
|
+
return {
|
|
3120
|
+
packets: copiedPackets,
|
|
3121
|
+
bytes: copiedBytes
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
/** `RssAnon:` / `RssFile:` out of `/proc/self/status` text; null when absent. */
|
|
3125
|
+
function parseProcStatus(text) {
|
|
3126
|
+
const anon = /^RssAnon:\s+(\d+)\s*kB/m.exec(text);
|
|
3127
|
+
const file = /^RssFile:\s+(\d+)\s*kB/m.exec(text);
|
|
3128
|
+
if (anon === null || file === null) return null;
|
|
3129
|
+
return {
|
|
3130
|
+
rssAnonKb: Number(anon[1]),
|
|
3131
|
+
rssFileKb: Number(file[1])
|
|
3132
|
+
};
|
|
3133
|
+
}
|
|
3134
|
+
/**
|
|
3135
|
+
* Summarise `/proc/self/maps`. Cheaper than `smaps_rollup`'s kernel page walk:
|
|
3136
|
+
* the file is one line per mapping and needs no per-page accounting. The
|
|
3137
|
+
* `[heap]` figure is the brk extent (virtual), which is the number glibc's
|
|
3138
|
+
* main arena grows when fragmented.
|
|
3139
|
+
*
|
|
3140
|
+
* Line shape: `start-end perms offset dev inode [path]` — an anonymous mapping
|
|
3141
|
+
* has no sixth field.
|
|
3142
|
+
*/
|
|
3143
|
+
function parseProcMaps(text) {
|
|
3144
|
+
let heapBytes = 0;
|
|
3145
|
+
let anonMappings = 0;
|
|
3146
|
+
let anonBytes = 0;
|
|
3147
|
+
let v8PageMaps = 0;
|
|
3148
|
+
let from = 0;
|
|
3149
|
+
while (from < text.length) {
|
|
3150
|
+
let to = text.indexOf("\n", from);
|
|
3151
|
+
if (to === -1) to = text.length;
|
|
3152
|
+
const line = text.slice(from, to);
|
|
3153
|
+
from = to + 1;
|
|
3154
|
+
const dash = line.indexOf("-");
|
|
3155
|
+
const space = line.indexOf(" ");
|
|
3156
|
+
if (dash <= 0 || space <= dash) continue;
|
|
3157
|
+
const size = Number.parseInt(line.slice(dash + 1, space), 16) - Number.parseInt(line.slice(0, dash), 16);
|
|
3158
|
+
if (!Number.isFinite(size)) continue;
|
|
3159
|
+
const path = line.split(/\s+/, 6)[5];
|
|
3160
|
+
if (path === void 0 || path === "") {
|
|
3161
|
+
anonMappings += 1;
|
|
3162
|
+
anonBytes += size;
|
|
3163
|
+
if (size === 262144) v8PageMaps += 1;
|
|
3164
|
+
} else if (path === "[heap]") heapBytes += size;
|
|
3165
|
+
}
|
|
3166
|
+
return {
|
|
3167
|
+
heapBytes,
|
|
3168
|
+
anonMappings,
|
|
3169
|
+
anonBytes,
|
|
3170
|
+
v8PageMaps
|
|
3171
|
+
};
|
|
3172
|
+
}
|
|
3173
|
+
/** Read the main isolate. Probe tick only: `getHeapSpaceStatistics` walks the spaces. */
|
|
3174
|
+
function readV8Heap() {
|
|
3175
|
+
const h = (0, node_v8.getHeapStatistics)();
|
|
3176
|
+
return {
|
|
3177
|
+
totalHeapBytes: h.total_heap_size,
|
|
3178
|
+
physicalBytes: h.total_physical_size,
|
|
3179
|
+
mallocedBytes: h.malloced_memory,
|
|
3180
|
+
peakMallocedBytes: h.peak_malloced_memory,
|
|
3181
|
+
externalBytes: h.external_memory,
|
|
3182
|
+
spaces: (0, node_v8.getHeapSpaceStatistics)().map((x) => ({
|
|
3183
|
+
name: x.space_name,
|
|
3184
|
+
committedBytes: x.space_size
|
|
3185
|
+
}))
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
3188
|
+
var SPACE_ABBREVIATIONS = {
|
|
3189
|
+
read_only_space: "ro",
|
|
3190
|
+
old_space: "old",
|
|
3191
|
+
new_space: "new",
|
|
3192
|
+
large_object_space: "lo",
|
|
3193
|
+
new_large_object_space: "nlo",
|
|
3194
|
+
code_space: "code",
|
|
3195
|
+
code_large_object_space: "clo",
|
|
3196
|
+
map_space: "map",
|
|
3197
|
+
shared_space: "shared",
|
|
3198
|
+
shared_large_object_space: "slo",
|
|
3199
|
+
trusted_space: "trusted",
|
|
3200
|
+
trusted_large_object_space: "tlo"
|
|
3201
|
+
};
|
|
3202
|
+
/** `old:120,new:24,lo:8` — committed MB per space, empty spaces left out. */
|
|
3203
|
+
function formatHeapSpaces(spaces) {
|
|
3204
|
+
const parts = spaces.filter((x) => x.committedBytes > 0).map((x) => `${SPACE_ABBREVIATIONS[x.name] ?? x.name}:${Math.round(x.committedBytes / MB)}`);
|
|
3205
|
+
return parts.length === 0 ? "none" : parts.join(",");
|
|
3206
|
+
}
|
|
3207
|
+
/** Opened/closed consumers since the last reading; `drain()` resets. */
|
|
3208
|
+
var ConsumerChurn = class {
|
|
3209
|
+
opened = 0;
|
|
3210
|
+
closed = 0;
|
|
3211
|
+
openedByKind = /* @__PURE__ */ new Map();
|
|
3212
|
+
closedByKind = /* @__PURE__ */ new Map();
|
|
3213
|
+
recordOpen(kind) {
|
|
3214
|
+
this.opened += 1;
|
|
3215
|
+
this.openedByKind.set(kind, (this.openedByKind.get(kind) ?? 0) + 1);
|
|
3216
|
+
}
|
|
3217
|
+
recordClose(kind) {
|
|
3218
|
+
this.closed += 1;
|
|
3219
|
+
this.closedByKind.set(kind, (this.closedByKind.get(kind) ?? 0) + 1);
|
|
3220
|
+
}
|
|
3221
|
+
drain() {
|
|
3222
|
+
const reading = {
|
|
3223
|
+
opened: this.opened,
|
|
3224
|
+
closed: this.closed,
|
|
3225
|
+
openedByKind: Object.fromEntries(this.openedByKind),
|
|
3226
|
+
closedByKind: Object.fromEntries(this.closedByKind)
|
|
3227
|
+
};
|
|
3228
|
+
this.opened = 0;
|
|
3229
|
+
this.closed = 0;
|
|
3230
|
+
this.openedByKind = /* @__PURE__ */ new Map();
|
|
3231
|
+
this.closedByKind = /* @__PURE__ */ new Map();
|
|
3232
|
+
return reading;
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
/** The one churn counter of this process (the broker runner). */
|
|
3236
|
+
var consumerChurn = new ConsumerChurn();
|
|
3237
|
+
/**
|
|
3238
|
+
* Sliding-window limiter for the attach/detach lines. `admit(now)` answers
|
|
3239
|
+
* whether this line may be written; the number of lines swallowed since the
|
|
3240
|
+
* last admitted one is returned with it so the next line can say so (a branch
|
|
3241
|
+
* that drops work names it).
|
|
3242
|
+
*/
|
|
3243
|
+
function createLineLimiter(maxPerWindow, windowMs) {
|
|
3244
|
+
let windowStart = 0;
|
|
3245
|
+
let inWindow = 0;
|
|
3246
|
+
let suppressed = 0;
|
|
3247
|
+
return (now) => {
|
|
3248
|
+
if (now - windowStart >= windowMs) {
|
|
3249
|
+
windowStart = now;
|
|
3250
|
+
inWindow = 0;
|
|
3251
|
+
}
|
|
3252
|
+
if (inWindow >= maxPerWindow) {
|
|
3253
|
+
suppressed += 1;
|
|
3254
|
+
return {
|
|
3255
|
+
admit: false,
|
|
3256
|
+
suppressedBefore: 0
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
inWindow += 1;
|
|
3260
|
+
const suppressedBefore = suppressed;
|
|
3261
|
+
suppressed = 0;
|
|
3262
|
+
return {
|
|
3263
|
+
admit: true,
|
|
3264
|
+
suppressedBefore
|
|
3265
|
+
};
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
var consumerLineLimiter = createLineLimiter(40, 6e4);
|
|
3269
|
+
/**
|
|
3270
|
+
* One consumer attached to / detached from a broker: counted always (the
|
|
3271
|
+
* `[broker-mem]` line reports opened/closed since the last line), written at
|
|
3272
|
+
* info under a rate limit so a step in RSS can be matched to an event. The
|
|
3273
|
+
* line after a suppressed burst says how many were swallowed.
|
|
3274
|
+
*/
|
|
3275
|
+
function reportConsumerEvent(logger, event, kind, deviceId, total, now = Date.now()) {
|
|
3276
|
+
if (event === "attach") consumerChurn.recordOpen(kind);
|
|
3277
|
+
else consumerChurn.recordClose(kind);
|
|
3278
|
+
if (logger === void 0 || logger === null) return;
|
|
3279
|
+
const verdict = consumerLineLimiter(now);
|
|
3280
|
+
if (!verdict.admit) return;
|
|
3281
|
+
const suppressed = verdict.suppressedBefore > 0 ? ` suppressedBefore=${verdict.suppressedBefore}` : "";
|
|
3282
|
+
logger.info(`consumer ${event} kind=${kind} total=${total}${suppressed}`, { ...deviceId !== void 0 ? { tags: { deviceId } } : {} });
|
|
3283
|
+
}
|
|
3284
|
+
function ringBytesOf(s) {
|
|
3285
|
+
return s.preBufferBytes + s.clipBytes + s.rtpRingBytes + s.keyframeCacheBytes;
|
|
3286
|
+
}
|
|
3287
|
+
function mb(bytes) {
|
|
3288
|
+
return (bytes / MB).toFixed(1);
|
|
3289
|
+
}
|
|
3290
|
+
function kindList(counts) {
|
|
3291
|
+
const keys = Object.keys(counts).sort();
|
|
3292
|
+
return keys.length === 0 ? "none" : keys.map((k) => `${k}:${counts[k]}`).join(",");
|
|
3293
|
+
}
|
|
3294
|
+
/** One greppable `key=value` line; no per-device breakdown beyond the top N. */
|
|
3295
|
+
function formatBrokerMem(input) {
|
|
3296
|
+
const { samples, proc, churn, copied } = input;
|
|
3297
|
+
const consumers = {};
|
|
3298
|
+
let rtsp = 0;
|
|
3299
|
+
let audio = 0;
|
|
3300
|
+
let pipe = 0;
|
|
3301
|
+
let ingesting = 0;
|
|
3302
|
+
let planes = 0;
|
|
3303
|
+
let pre = 0;
|
|
3304
|
+
let clip = 0;
|
|
3305
|
+
let rtp = 0;
|
|
3306
|
+
let kf = 0;
|
|
3307
|
+
let qSum = 0;
|
|
3308
|
+
let qMax = 0;
|
|
3309
|
+
for (const s of samples) {
|
|
3310
|
+
if (s.ingesting) ingesting += 1;
|
|
3311
|
+
if (s.annexBPlaneActive) planes += 1;
|
|
3312
|
+
rtsp += s.rtspSessions;
|
|
3313
|
+
audio += s.audioSubscribers;
|
|
3314
|
+
pipe += s.pipeClients;
|
|
3315
|
+
pre += s.preBufferBytes;
|
|
3316
|
+
clip += s.clipBytes;
|
|
3317
|
+
rtp += s.rtpRingBytes;
|
|
3318
|
+
kf += s.keyframeCacheBytes;
|
|
3319
|
+
qSum += s.writeQueueSumBytes;
|
|
3320
|
+
if (s.writeQueueMaxBytes > qMax) qMax = s.writeQueueMaxBytes;
|
|
3321
|
+
for (const [kind, n] of Object.entries(s.encodedByKind)) consumers[kind] = (consumers[kind] ?? 0) + n;
|
|
3322
|
+
}
|
|
3323
|
+
const top = [...samples].filter((s) => s.deviceId !== void 0).sort((a, b) => ringBytesOf(b) - ringBytesOf(a)).slice(0, 5).map((s) => `${s.deviceId}:${mb(ringBytesOf(s))}`).join(",");
|
|
3324
|
+
const parts = ["[broker-mem]"];
|
|
3325
|
+
if (proc.status !== null) parts.push(`rssAnonMb=${(proc.status.rssAnonKb / 1024).toFixed(0)}`, `rssFileMb=${(proc.status.rssFileKb / 1024).toFixed(0)}`);
|
|
3326
|
+
if (proc.maps !== null) parts.push(`heapMapMb=${mb(proc.maps.heapBytes)}`, `anonMaps=${proc.maps.anonMappings}`, `anonMapsMb=${mb(proc.maps.anonBytes)}`, `v8PageMaps=${proc.maps.v8PageMaps}`);
|
|
3327
|
+
const v8 = input.v8;
|
|
3328
|
+
if (v8 !== void 0 && v8 !== null) parts.push(`v8TotalHeapMb=${mb(v8.totalHeapBytes)}`, `v8PhysicalMb=${mb(v8.physicalBytes)}`, `v8MallocedMb=${mb(v8.mallocedBytes)}`, `v8PeakMallocedMb=${mb(v8.peakMallocedBytes)}`, `v8ExternalMb=${mb(v8.externalBytes)}`, `spaces=${formatHeapSpaces(v8.spaces)}`);
|
|
3329
|
+
const w = input.worker;
|
|
3330
|
+
if (w !== void 0 && w !== null) parts.push(`workers=${w.workers}`, `workerHeapMb=${mb(w.heapBytes)}`, `workerPhysicalMb=${mb(w.physicalBytes)}`, `workerUsedMb=${mb(w.usedBytes)}`, `workerAgeS=${Math.round(w.maxAgeMs / 1e3)}`, `workerIdleExit=${w.idleTerminates ? "yes" : "never"}`);
|
|
3331
|
+
parts.push(`brokers=${samples.length}`, `ingesting=${ingesting}`, `annexBPlanes=${planes}`, `rtsp=${rtsp}`, `webrtc=${input.webrtcSessions}`, `encoded=${kindList(consumers)}`, `audioSubs=${audio}`, `pipeClients=${pipe}`, `egress=${input.egressTranscodes}`, `children=${input.childProcesses}`, `opened=${churn.opened}`, `closed=${churn.closed}`, `openedKinds=${kindList(churn.openedByKind)}`, `closedKinds=${kindList(churn.closedByKind)}`, `preBufferMb=${mb(pre)}`, `clipMb=${mb(clip)}`, `rtpRingMb=${mb(rtp)}`, `keyframeMb=${mb(kf)}`, `writeQueueSumMb=${mb(qSum)}`, `writeQueueMaxMb=${mb(qMax)}`, `copiedPackets=${copied.packets}`, `copiedMb=${mb(copied.bytes)}`, `topRingMb=${top === "" ? "none" : top}`);
|
|
3332
|
+
return parts.join(" ");
|
|
3333
|
+
}
|
|
3334
|
+
/** Count entries named `name` in `process.getActiveResourcesInfo()`. */
|
|
3335
|
+
function countActiveResources(resources, name) {
|
|
3336
|
+
let n = 0;
|
|
3337
|
+
for (const r of resources) if (r === name) n += 1;
|
|
3338
|
+
return n;
|
|
3339
|
+
}
|
|
3340
|
+
//#endregion
|
|
3088
3341
|
//#region src/stream-broker/stream-broker/owned-buffer.ts
|
|
3089
3342
|
/**
|
|
3090
3343
|
* Buffers that enter a LONG-LIVED ring own their backing store.
|
|
@@ -3144,6 +3397,7 @@ function detachFromPool(buf) {
|
|
|
3144
3397
|
function copyOwned(src, start, end) {
|
|
3145
3398
|
const owned = Buffer.allocUnsafeSlow(end - start);
|
|
3146
3399
|
src.copy(owned, 0, start, end);
|
|
3400
|
+
recordCopyOwned(end - start);
|
|
3147
3401
|
return owned;
|
|
3148
3402
|
}
|
|
3149
3403
|
//#endregion
|
|
@@ -3756,6 +4010,26 @@ var RtspRestreamer = class {
|
|
|
3756
4010
|
for (const session of this.sessions.values()) if (session.isPrestreamIntent()) n += 1;
|
|
3757
4011
|
return n;
|
|
3758
4012
|
}
|
|
4013
|
+
/**
|
|
4014
|
+
* What this restreamer holds, for the `[broker-mem]` probe: live sessions,
|
|
4015
|
+
* their socket write queues (sum and max) and the keyframe caches. One pass
|
|
4016
|
+
* over the sessions, no allocation beyond the result.
|
|
4017
|
+
*/
|
|
4018
|
+
getMemProbe() {
|
|
4019
|
+
let sum = 0;
|
|
4020
|
+
let max = 0;
|
|
4021
|
+
for (const s of this.sessions.values()) {
|
|
4022
|
+
const q = s.getWriteQueueBytes();
|
|
4023
|
+
sum += q;
|
|
4024
|
+
if (q > max) max = q;
|
|
4025
|
+
}
|
|
4026
|
+
return {
|
|
4027
|
+
sessions: this.sessions.size,
|
|
4028
|
+
writeQueueSumBytes: sum,
|
|
4029
|
+
writeQueueMaxBytes: max,
|
|
4030
|
+
keyframeCacheBytes: this.lastKeyframeNalBytes + this.lastKeyframeRtpBytes
|
|
4031
|
+
};
|
|
4032
|
+
}
|
|
3759
4033
|
/** Per-session diagnostic snapshots consumed by `StreamBroker.listClients`. */
|
|
3760
4034
|
listSessionInfos() {
|
|
3761
4035
|
return [...this.sessions.values()].map((s) => s.getInfo());
|
|
@@ -3767,6 +4041,7 @@ var RtspRestreamer = class {
|
|
|
3767
4041
|
streamName: this.streamName,
|
|
3768
4042
|
total: this.sessions.size
|
|
3769
4043
|
} });
|
|
4044
|
+
reportConsumerEvent(this.logger, "attach", "rtsp", this.deviceId, this.sessions.size);
|
|
3770
4045
|
this._onSessionCountChanged?.();
|
|
3771
4046
|
this.reconcileSentryAudio();
|
|
3772
4047
|
if (this._rtpPassthroughMode) setTimeout(() => this.servePendingSessionsRtp(), 0);
|
|
@@ -4231,6 +4506,7 @@ var RtspRestreamer = class {
|
|
|
4231
4506
|
streamName: this.streamName,
|
|
4232
4507
|
total: this.sessions.size
|
|
4233
4508
|
} });
|
|
4509
|
+
reportConsumerEvent(this.logger, "detach", "rtsp", this.deviceId, this.sessions.size);
|
|
4234
4510
|
this._onSessionCountChanged?.();
|
|
4235
4511
|
this.reconcileSentryAudio();
|
|
4236
4512
|
}
|
|
@@ -4500,6 +4776,10 @@ var RtspSession = class {
|
|
|
4500
4776
|
this.onClose?.();
|
|
4501
4777
|
});
|
|
4502
4778
|
}
|
|
4779
|
+
/** Bytes the socket has accepted but not yet flushed (corked bytes included). */
|
|
4780
|
+
getWriteQueueBytes() {
|
|
4781
|
+
return this.socket.writableLength;
|
|
4782
|
+
}
|
|
4503
4783
|
getSessionId() {
|
|
4504
4784
|
return this.sessionId;
|
|
4505
4785
|
}
|
|
@@ -4867,6 +5147,17 @@ var RtspSession = class {
|
|
|
4867
5147
|
//#endregion
|
|
4868
5148
|
//#region src/stream-broker/rtsp/rtsp-listen-server.ts
|
|
4869
5149
|
/**
|
|
5150
|
+
* Attribution of the synthetic consumer a {@link BrokerWarmUp} subscribes:
|
|
5151
|
+
* the RTSP listen server holding a cold broker open for a DESCRIBE. Named
|
|
5152
|
+
* after that real consumer — it used to subscribe with no attribution and
|
|
5153
|
+
* surfaced as `unknown` (45 attach/detach pairs per 5 min on the hub).
|
|
5154
|
+
*/
|
|
5155
|
+
var RTSP_DESCRIBE_WARMUP_ATTRIBUTION = {
|
|
5156
|
+
kind: "rtsp-listen",
|
|
5157
|
+
label: "describe-warmup",
|
|
5158
|
+
media: "both"
|
|
5159
|
+
};
|
|
5160
|
+
/**
|
|
4870
5161
|
* Single TCP server for all RTSP restream connections.
|
|
4871
5162
|
* Routes clients to the correct RtspRestreamer by a random token path
|
|
4872
5163
|
* (not the predictable brokerId) for security.
|
|
@@ -6453,6 +6744,10 @@ var EgressTranscodeManager = class {
|
|
|
6453
6744
|
/** In-flight starts, keyed by sharing key — concurrent acquires share one. */
|
|
6454
6745
|
pending = /* @__PURE__ */ new Map();
|
|
6455
6746
|
releaseGraceMs;
|
|
6747
|
+
/** Live egress transcodes (ffmpeg children), for the `[broker-mem]` probe. */
|
|
6748
|
+
activeCount() {
|
|
6749
|
+
return this.entries.size;
|
|
6750
|
+
}
|
|
6456
6751
|
constructor(opts) {
|
|
6457
6752
|
this.opts = opts;
|
|
6458
6753
|
this.releaseGraceMs = opts.releaseGraceMs ?? DEFAULT_RELEASE_GRACE_MS;
|
|
@@ -12816,6 +13111,8 @@ var StreamBroker = class StreamBroker {
|
|
|
12816
13111
|
* timeout was chosen against.
|
|
12817
13112
|
*/
|
|
12818
13113
|
static RTP_ACTIVITY_SWEEP_MS = 1e3;
|
|
13114
|
+
/** A source counts as ingesting when its last video packet is younger than this. */
|
|
13115
|
+
static INGESTING_WITHIN_MS = 1e4;
|
|
12819
13116
|
manualStop = false;
|
|
12820
13117
|
stopping = false;
|
|
12821
13118
|
pipeServer;
|
|
@@ -13090,6 +13387,33 @@ var StreamBroker = class StreamBroker {
|
|
|
13090
13387
|
return this.lastPacketAt;
|
|
13091
13388
|
}
|
|
13092
13389
|
/**
|
|
13390
|
+
* What this broker holds, for the `[broker-mem]` probe (`broker-mem-probe.ts`).
|
|
13391
|
+
* Ring sizes are the rings' own counters (backing-store bytes), so this is
|
|
13392
|
+
* O(sessions + subscribers) and allocates only the result.
|
|
13393
|
+
*/
|
|
13394
|
+
getMemSample(now) {
|
|
13395
|
+
const encodedByKind = {};
|
|
13396
|
+
for (const entry of this.encodedCallbacks.values()) encodedByKind[entry.attribution.kind] = (encodedByKind[entry.attribution.kind] ?? 0) + 1;
|
|
13397
|
+
for (const entry of this.rtpVideoCallbacks.values()) encodedByKind[entry.attribution.kind] = (encodedByKind[entry.attribution.kind] ?? 0) + 1;
|
|
13398
|
+
const rtsp = this.rtspRestreamer.getMemProbe();
|
|
13399
|
+
return {
|
|
13400
|
+
deviceId: this.numericDeviceId,
|
|
13401
|
+
brokerId: this.deviceId,
|
|
13402
|
+
ingesting: this.lastPacketAt > 0 && now - this.lastPacketAt < StreamBroker.INGESTING_WITHIN_MS,
|
|
13403
|
+
preBufferBytes: this.preBuffer.getBufferedBytes(),
|
|
13404
|
+
clipBytes: this.clipBuffer?.getBufferedBytes() ?? 0,
|
|
13405
|
+
rtpRingBytes: this.rtpRingBytes,
|
|
13406
|
+
keyframeCacheBytes: rtsp.keyframeCacheBytes,
|
|
13407
|
+
rtspSessions: rtsp.sessions,
|
|
13408
|
+
writeQueueSumBytes: rtsp.writeQueueSumBytes,
|
|
13409
|
+
writeQueueMaxBytes: rtsp.writeQueueMaxBytes,
|
|
13410
|
+
encodedByKind,
|
|
13411
|
+
audioSubscribers: this.audioChunkPlane.subscriberCount,
|
|
13412
|
+
pipeClients: this.pipeServer.getClientCount(),
|
|
13413
|
+
annexBPlaneActive: this.annexBPlaneActive
|
|
13414
|
+
};
|
|
13415
|
+
}
|
|
13416
|
+
/**
|
|
13093
13417
|
* Whether a LAZY publisher is being built right now.
|
|
13094
13418
|
*
|
|
13095
13419
|
* A consumer deciding whether it is worth waiting for this source needs to
|
|
@@ -14052,6 +14376,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14052
14376
|
packetsDelivered: 0
|
|
14053
14377
|
};
|
|
14054
14378
|
this.encodedCallbacks.set(callback, entry);
|
|
14379
|
+
reportConsumerEvent(this.logger, "attach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14055
14380
|
if (this.streamingDebug) this.logger?.info("encoded subscriber added", { meta: {
|
|
14056
14381
|
total: this.encodedCallbacks.size,
|
|
14057
14382
|
subscriberId: entry.id,
|
|
@@ -14062,6 +14387,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14062
14387
|
this.checkDemand();
|
|
14063
14388
|
return () => {
|
|
14064
14389
|
this.encodedCallbacks.delete(callback);
|
|
14390
|
+
reportConsumerEvent(this.logger, "detach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14065
14391
|
if (this.streamingDebug) this.logger?.info("encoded subscriber removed", { meta: {
|
|
14066
14392
|
total: this.encodedCallbacks.size,
|
|
14067
14393
|
subscriberId: entry.id
|
|
@@ -14156,6 +14482,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14156
14482
|
packetsDelivered: 0
|
|
14157
14483
|
};
|
|
14158
14484
|
this.rtpVideoCallbacks.set(callback, entry);
|
|
14485
|
+
reportConsumerEvent(this.logger, "attach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14159
14486
|
if (this.streamingDebug || CH_RTP.on) this.logger?.info("rtp video subscriber added", { meta: {
|
|
14160
14487
|
total: this.rtpVideoCallbacks.size,
|
|
14161
14488
|
subscriberId: entry.id,
|
|
@@ -14165,6 +14492,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14165
14492
|
this.checkDemand();
|
|
14166
14493
|
return () => {
|
|
14167
14494
|
this.rtpVideoCallbacks.delete(callback);
|
|
14495
|
+
reportConsumerEvent(this.logger, "detach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14168
14496
|
if (this.streamingDebug || CH_RTP.on) this.logger?.info("rtp video subscriber removed", { meta: {
|
|
14169
14497
|
total: this.rtpVideoCallbacks.size,
|
|
14170
14498
|
subscriberId: entry.id
|
|
@@ -14565,6 +14893,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14565
14893
|
tag,
|
|
14566
14894
|
accept: options?.accept
|
|
14567
14895
|
});
|
|
14896
|
+
reportConsumerEvent(this.logger, "attach", "audio-chunks", this.numericDeviceId, this.audioChunkPlane.subscriberCount);
|
|
14568
14897
|
this.checkDemand();
|
|
14569
14898
|
return result;
|
|
14570
14899
|
}
|
|
@@ -14575,7 +14904,10 @@ var StreamBroker = class StreamBroker {
|
|
|
14575
14904
|
/** Release an audio-chunk subscription. Returns `true` when it existed. */
|
|
14576
14905
|
unsubscribeAudioChunks(subscriptionId) {
|
|
14577
14906
|
const released = this.audioChunkPlane.unsubscribe(subscriptionId);
|
|
14578
|
-
if (released)
|
|
14907
|
+
if (released) {
|
|
14908
|
+
reportConsumerEvent(this.logger, "detach", "audio-chunks", this.numericDeviceId, this.audioChunkPlane.subscriberCount);
|
|
14909
|
+
this.checkDemand();
|
|
14910
|
+
}
|
|
14579
14911
|
return released;
|
|
14580
14912
|
}
|
|
14581
14913
|
getStats() {
|
|
@@ -19402,6 +19734,10 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19402
19734
|
return { killed: broker.killClient(input.channel, input.handle) };
|
|
19403
19735
|
}
|
|
19404
19736
|
/** Raw broker instances — internal use only. */
|
|
19737
|
+
/** Live egress transcodes (ffmpeg children) — the `[broker-mem]` probe. */
|
|
19738
|
+
getEgressTranscodeCount() {
|
|
19739
|
+
return this.egressTranscodeManager?.activeCount() ?? 0;
|
|
19740
|
+
}
|
|
19405
19741
|
getBrokerInstances() {
|
|
19406
19742
|
return [...this.brokers.values()];
|
|
19407
19743
|
}
|
|
@@ -19668,7 +20004,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19668
20004
|
const sourceBroker = this.brokers.get(sourceBrokerId);
|
|
19669
20005
|
if (!sourceBroker) continue;
|
|
19670
20006
|
const existingToken = this.persistedTokens.get(profileBrokerId);
|
|
19671
|
-
const warmUp = () => sourceBroker.onEncodedData(() => {});
|
|
20007
|
+
const warmUp = () => sourceBroker.onEncodedData(() => {}, RTSP_DESCRIBE_WARMUP_ATTRIBUTION);
|
|
19672
20008
|
const openPrestreamWindow = () => sourceBroker.openPrestreamWindow();
|
|
19673
20009
|
this.rtspServer.registerRestreamer(profileBrokerId, sourceBroker.getRtspRestreamer(), warmUp, existingToken, openPrestreamWindow);
|
|
19674
20010
|
this.profileRestreamerBindings.set(profileBrokerId, sourceBrokerId);
|
|
@@ -21327,7 +21663,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
21327
21663
|
this.brokers.set(brokerId, broker);
|
|
21328
21664
|
this.applyClipRetention(deviceId);
|
|
21329
21665
|
const existingToken = this.persistedTokens.get(brokerId);
|
|
21330
|
-
const warmUp = () => broker.onEncodedData(() => {});
|
|
21666
|
+
const warmUp = () => broker.onEncodedData(() => {}, RTSP_DESCRIBE_WARMUP_ATTRIBUTION);
|
|
21331
21667
|
const openPrestreamWindow = () => broker.openPrestreamWindow();
|
|
21332
21668
|
this.rtspServer.registerRestreamer(brokerId, broker.getRtspRestreamer(), warmUp, existingToken, openPrestreamWindow);
|
|
21333
21669
|
if (!existingToken) this.rtspProvider.persistTokens();
|
|
@@ -33656,14 +33992,11 @@ function parseWorkerMessage(message) {
|
|
|
33656
33992
|
function defaultWorkerFilename() {
|
|
33657
33993
|
return (0, node_url.fileURLToPath)(new URL("./demux-worker-child.js", require("url").pathToFileURL(__filename).href));
|
|
33658
33994
|
}
|
|
33659
|
-
/**
|
|
33660
|
-
|
|
33661
|
-
* callers are matched by request id; the worker's own event loop serializes
|
|
33662
|
-
* mp4box. Live RTP on the parent thread is not waiting on box parsing.
|
|
33663
|
-
*/
|
|
33664
|
-
function createOffThreadJsParse(deps) {
|
|
33995
|
+
/** {@link createOffThreadJsParse} plus the heap accessor the memory probe reads. */
|
|
33996
|
+
function createOffThreadDemuxClient(deps) {
|
|
33665
33997
|
const Ctor = deps.WorkerCtor;
|
|
33666
33998
|
let worker = null;
|
|
33999
|
+
let spawnedAt = 0;
|
|
33667
34000
|
let nextId = 1;
|
|
33668
34001
|
const pending = /* @__PURE__ */ new Map();
|
|
33669
34002
|
const failAll = () => {
|
|
@@ -33696,12 +34029,13 @@ function createOffThreadJsParse(deps) {
|
|
|
33696
34029
|
const spawned = new Ctor(deps.workerFilename);
|
|
33697
34030
|
attach(spawned);
|
|
33698
34031
|
worker = spawned;
|
|
34032
|
+
spawnedAt = Date.now();
|
|
33699
34033
|
return spawned;
|
|
33700
34034
|
} catch {
|
|
33701
34035
|
return null;
|
|
33702
34036
|
}
|
|
33703
34037
|
};
|
|
33704
|
-
|
|
34038
|
+
const parse = async (bytes) => {
|
|
33705
34039
|
if (!deps.existsSync(deps.workerFilename)) return deps.parseInProcess(bytes);
|
|
33706
34040
|
const w = ensureWorker();
|
|
33707
34041
|
if (!w) return deps.parseInProcess(bytes);
|
|
@@ -33717,9 +34051,36 @@ function createOffThreadJsParse(deps) {
|
|
|
33717
34051
|
w.postMessage(request, [copy.buffer]);
|
|
33718
34052
|
});
|
|
33719
34053
|
};
|
|
34054
|
+
const readHeaps = async (now = Date.now()) => {
|
|
34055
|
+
const live = worker;
|
|
34056
|
+
const empty = {
|
|
34057
|
+
workers: 0,
|
|
34058
|
+
heapBytes: 0,
|
|
34059
|
+
physicalBytes: 0,
|
|
34060
|
+
usedBytes: 0,
|
|
34061
|
+
maxAgeMs: 0,
|
|
34062
|
+
idleTerminates: false
|
|
34063
|
+
};
|
|
34064
|
+
if (live === null) return empty;
|
|
34065
|
+
const stats = await live.getHeapStatistics?.().catch(() => null);
|
|
34066
|
+
return {
|
|
34067
|
+
...empty,
|
|
34068
|
+
workers: 1,
|
|
34069
|
+
maxAgeMs: Math.max(0, now - spawnedAt),
|
|
34070
|
+
...stats ? {
|
|
34071
|
+
heapBytes: stats.total_heap_size,
|
|
34072
|
+
physicalBytes: stats.total_physical_size,
|
|
34073
|
+
usedBytes: stats.used_heap_size
|
|
34074
|
+
} : {}
|
|
34075
|
+
};
|
|
34076
|
+
};
|
|
34077
|
+
return {
|
|
34078
|
+
parse,
|
|
34079
|
+
readHeaps
|
|
34080
|
+
};
|
|
33720
34081
|
}
|
|
33721
|
-
function
|
|
33722
|
-
return
|
|
34082
|
+
function defaultOffThreadClient() {
|
|
34083
|
+
return createOffThreadDemuxClient({
|
|
33723
34084
|
workerFilename: defaultWorkerFilename(),
|
|
33724
34085
|
existsSync: node_fs.existsSync,
|
|
33725
34086
|
parseInProcess: require_segment_demux_js.demuxSegmentFullJs,
|
|
@@ -33729,9 +34090,20 @@ function defaultOffThreadJsParse() {
|
|
|
33729
34090
|
var shared;
|
|
33730
34091
|
/** Process-wide JS parse: one Worker, shared by every WebRTC session. */
|
|
33731
34092
|
var offThreadJsParse = (bytes) => {
|
|
33732
|
-
shared ??=
|
|
33733
|
-
return shared(bytes);
|
|
34093
|
+
shared ??= defaultOffThreadClient();
|
|
34094
|
+
return shared.parse(bytes);
|
|
33734
34095
|
};
|
|
34096
|
+
/** Heap of the shared demux worker, for the `[broker-mem]` probe (tick only). */
|
|
34097
|
+
async function readDemuxWorkerHeaps(now) {
|
|
34098
|
+
return shared ? shared.readHeaps(now) : {
|
|
34099
|
+
workers: 0,
|
|
34100
|
+
heapBytes: 0,
|
|
34101
|
+
physicalBytes: 0,
|
|
34102
|
+
usedBytes: 0,
|
|
34103
|
+
maxAgeMs: 0,
|
|
34104
|
+
idleTerminates: false
|
|
34105
|
+
};
|
|
34106
|
+
}
|
|
33735
34107
|
/**
|
|
33736
34108
|
* Drop-in for the session `demuxVideo` hook: JS parse off the live loop,
|
|
33737
34109
|
* ffmpeg subprocess fallback unchanged.
|
|
@@ -35274,6 +35646,7 @@ var BrokerWebrtcServer = class {
|
|
|
35274
35646
|
}
|
|
35275
35647
|
};
|
|
35276
35648
|
this.sessions.set(sessionId, entry);
|
|
35649
|
+
reportConsumerEvent(this.logger, "attach", `webrtc:${entry.consumerKind}`, entry.rebind?.deviceId, this.sessions.size);
|
|
35277
35650
|
session.onClosed = () => this.cleanupSession(sessionId);
|
|
35278
35651
|
const locateRecorded = this.locateRecordedSegment;
|
|
35279
35652
|
const readRecorded = this.readRecordedSegmentBytes;
|
|
@@ -36491,6 +36864,7 @@ var BrokerWebrtcServer = class {
|
|
|
36491
36864
|
const entry = this.sessions.get(sessionId);
|
|
36492
36865
|
if (!entry) return;
|
|
36493
36866
|
this.sessions.delete(sessionId);
|
|
36867
|
+
reportConsumerEvent(this.logger, "detach", `webrtc:${entry.consumerKind}`, entry.rebind?.deviceId, this.sessions.size);
|
|
36494
36868
|
entry.pendingSwitch?.cancel();
|
|
36495
36869
|
entry.pendingSwitch = void 0;
|
|
36496
36870
|
let brokerStillServed = false;
|
|
@@ -37212,6 +37586,47 @@ function dialAnswerFromError(err) {
|
|
|
37212
37586
|
};
|
|
37213
37587
|
}
|
|
37214
37588
|
//#endregion
|
|
37589
|
+
//#region src/stream-broker/broker-mem-report.ts
|
|
37590
|
+
/** Linux only: elsewhere (or on a read error) the native view is simply absent. */
|
|
37591
|
+
async function readProc() {
|
|
37592
|
+
if (process.platform !== "linux") return {
|
|
37593
|
+
status: null,
|
|
37594
|
+
maps: null
|
|
37595
|
+
};
|
|
37596
|
+
const [status, maps] = await Promise.all([(0, node_fs_promises.readFile)("/proc/self/status", "utf8").then(parseProcStatus, () => null), (0, node_fs_promises.readFile)("/proc/self/maps", "utf8").then(parseProcMaps, () => null)]);
|
|
37597
|
+
return {
|
|
37598
|
+
status,
|
|
37599
|
+
maps
|
|
37600
|
+
};
|
|
37601
|
+
}
|
|
37602
|
+
async function emitOnce(deps) {
|
|
37603
|
+
const now = Date.now();
|
|
37604
|
+
const [proc, worker] = await Promise.all([readProc(), readDemuxWorkerHeaps(now)]);
|
|
37605
|
+
const resources = process.getActiveResourcesInfo();
|
|
37606
|
+
const line = formatBrokerMem({
|
|
37607
|
+
samples: deps.manager.getBrokerInstances().map((b) => b.getMemSample(now)),
|
|
37608
|
+
webrtcSessions: deps.webrtcSessions(),
|
|
37609
|
+
egressTranscodes: deps.manager.getEgressTranscodeCount(),
|
|
37610
|
+
childProcesses: countActiveResources(resources, "ProcessWrap"),
|
|
37611
|
+
proc,
|
|
37612
|
+
churn: consumerChurn.drain(),
|
|
37613
|
+
copied: readCopyOwnedTotals(),
|
|
37614
|
+
v8: readV8Heap(),
|
|
37615
|
+
worker
|
|
37616
|
+
});
|
|
37617
|
+
deps.logger.info(line);
|
|
37618
|
+
}
|
|
37619
|
+
/** Start the five-minute `[broker-mem]` heartbeat; returns its stop function. */
|
|
37620
|
+
function startBrokerMemReport(deps) {
|
|
37621
|
+
const timer = setInterval(() => {
|
|
37622
|
+
emitOnce(deps).catch((err) => {
|
|
37623
|
+
deps.logger.warn("broker-mem probe failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
37624
|
+
});
|
|
37625
|
+
}, BROKER_MEM_INTERVAL_MS);
|
|
37626
|
+
timer.unref();
|
|
37627
|
+
return () => clearInterval(timer);
|
|
37628
|
+
}
|
|
37629
|
+
//#endregion
|
|
37215
37630
|
//#region src/stream-broker/event-loop-stall-monitor.ts
|
|
37216
37631
|
var DEFAULT_INTERVAL_MS = 100;
|
|
37217
37632
|
var DEFAULT_THRESHOLD_MS = 250;
|
|
@@ -37386,6 +37801,8 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
37386
37801
|
* leaks those sockets and wedges the next runner's werift ICE agent
|
|
37387
37802
|
* (DTLS never connects until a full hub restart). */
|
|
37388
37803
|
webrtcServer = null;
|
|
37804
|
+
/** Stops the five-minute `[broker-mem]` attribution heartbeat. */
|
|
37805
|
+
stopBrokerMemReport = null;
|
|
37389
37806
|
/** Names this process's event-loop stalls — see onInitialize. */
|
|
37390
37807
|
loopStallMonitor = null;
|
|
37391
37808
|
/** The `log-channels` provider for this process. Stopped on shutdown so a
|
|
@@ -37743,6 +38160,11 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
37743
38160
|
this.webrtcServer = webrtcServer;
|
|
37744
38161
|
const webrtcSessionProvider = new WebrtcSessionProvider(webrtcServer, this.brokerManager);
|
|
37745
38162
|
this.metricsSnapshotTimer = setInterval(() => this.emitBrokerMetricsSnapshot(), BROKER_METRICS_SNAPSHOT_INTERVAL_MS);
|
|
38163
|
+
this.stopBrokerMemReport = startBrokerMemReport({
|
|
38164
|
+
logger: this.ctx.logger,
|
|
38165
|
+
manager: this.brokerManager,
|
|
38166
|
+
webrtcSessions: () => webrtcServer.getSessionCount()
|
|
38167
|
+
});
|
|
37746
38168
|
const widgetsProvider = { listWidgets: async () => [{
|
|
37747
38169
|
tab: "device-tab",
|
|
37748
38170
|
label: "Stream Brokers",
|
|
@@ -37847,6 +38269,8 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
37847
38269
|
async onShutdown() {
|
|
37848
38270
|
this.logChannels?.stop();
|
|
37849
38271
|
this.logChannels = null;
|
|
38272
|
+
this.stopBrokerMemReport?.();
|
|
38273
|
+
this.stopBrokerMemReport = null;
|
|
37850
38274
|
this.loopStallMonitor?.stop();
|
|
37851
38275
|
this.loopStallMonitor = null;
|
|
37852
38276
|
this.flushProbeSnapshots(Date.now(), true);
|