@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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { r as __toESM } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { $n as discriminatedUnion, An as BaseAddon, D as G711_SCALE_CORRECTION_DB, Dn as DeviceType, Dt as gifDeriveSpeed, En as DeviceFeature, Gn as maskUrlCredentials, Hn as isEvent, I as RATE_CONTROL_RELAXED, In as createEvent, J as addonWidgetsSourceCapability, Kn as nodePin, L as RATE_CONTROL_TIGHT, Mn as CamProfileSchema, Nt as loadContributionCapability, On as errMsg, Ot as gifFromMp4Args, Pt as logChannelsCapability, Qn as boolean, Rt as memoizePinnedFfmpeg, Sn as buildFfmpegArgs, Tn as isSoftwareDecode, Tt as ffmpegRate, Un as makeProfileBrokerId, W as RingBuffer, Wn as makeSourceBrokerId, Xn as _enum, Xt as rankStreams, Zn as array, _t as egressTranscodeSharingKey, ar as record, at as clipExportFailureOf, c as CLIP_STILL_REASON_CODE_HEADER, cr as EventCategory, ct as createHwAccelCache, et as batteryCapability, ft as deriveBatteryPresence, jn as CAM_PROFILE_ORDER, kn as BOOT_RECOVERY_BACKOFF_MS, lt as createLogChannelsProvider, mn as streamBrokerCapability, nr as number, o as BatteryStatusSchema, or as string, qn as parseProfileBrokerId, rn as resolveEgressDecodeHwAccel, rr as object, rt as cameraStreamsCapability, s as CLIP_BROKER_PACED_RATES, sr as union, tr as literal, ut as declareLogChannel, vn as webrtcSessionCapability, vt as egressTransportFromRequest, w as EncodeProfileSchema, wn as invocationFromEncodeProfile, yn as AUDIO_PRESETS, zn as expandAudioChunkToF32le } from "../dist-
|
|
3
|
-
import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-
|
|
2
|
+
import { $n as discriminatedUnion, An as BaseAddon, D as G711_SCALE_CORRECTION_DB, Dn as DeviceType, Dt as gifDeriveSpeed, En as DeviceFeature, Gn as maskUrlCredentials, Hn as isEvent, I as RATE_CONTROL_RELAXED, In as createEvent, J as addonWidgetsSourceCapability, Kn as nodePin, L as RATE_CONTROL_TIGHT, Mn as CamProfileSchema, Nt as loadContributionCapability, On as errMsg, Ot as gifFromMp4Args, Pt as logChannelsCapability, Qn as boolean, Rt as memoizePinnedFfmpeg, Sn as buildFfmpegArgs, Tn as isSoftwareDecode, Tt as ffmpegRate, Un as makeProfileBrokerId, W as RingBuffer, Wn as makeSourceBrokerId, Xn as _enum, Xt as rankStreams, Zn as array, _t as egressTranscodeSharingKey, ar as record, at as clipExportFailureOf, c as CLIP_STILL_REASON_CODE_HEADER, cr as EventCategory, ct as createHwAccelCache, et as batteryCapability, ft as deriveBatteryPresence, jn as CAM_PROFILE_ORDER, kn as BOOT_RECOVERY_BACKOFF_MS, lt as createLogChannelsProvider, mn as streamBrokerCapability, nr as number, o as BatteryStatusSchema, or as string, qn as parseProfileBrokerId, rn as resolveEgressDecodeHwAccel, rr as object, rt as cameraStreamsCapability, s as CLIP_BROKER_PACED_RATES, sr as union, tr as literal, ut as declareLogChannel, vn as webrtcSessionCapability, vt as egressTransportFromRequest, w as EncodeProfileSchema, wn as invocationFromEncodeProfile, yn as AUDIO_PRESETS, zn as expandAudioChunkToF32le } from "../dist-ClKIJKn0.mjs";
|
|
3
|
+
import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-DlpWXqvO.mjs";
|
|
4
4
|
import { a as parseRestreamPath, d as PREBUFFER_MAX_MS, f as capPreRollRequestMs, i as isLiveEdgeOnlyDial, m as resolvePrebuffer, n as NO_DECLARATION, p as prebufferRetentionMs, s as sdpVariantFor, u as withSentryIntent } from "../restream-intent-DyGmLAU9.mjs";
|
|
5
5
|
import { n as profileForStreamId } from "../remote-restream-BeHi78PZ.mjs";
|
|
6
6
|
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
|
|
7
|
-
import { a as require_mp4box_all, c as parseNalLengthSize, i as runFfmpegOnce, n as demuxSegmentPreferJs, o as avccSampleToAnnexB, r as FfmpegFallbackRefusedError, s as buildKeyframeAnnexB, t as demuxSegmentFullJs } from "../segment-demux-js-
|
|
7
|
+
import { a as require_mp4box_all, c as parseNalLengthSize, i as runFfmpegOnce, n as demuxSegmentPreferJs, o as avccSampleToAnnexB, r as FfmpegFallbackRefusedError, s as buildKeyframeAnnexB, t as demuxSegmentFullJs } from "../segment-demux-js-Du_eAog1.mjs";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import * as os$1 from "node:os";
|
|
10
10
|
import { networkInterfaces, tmpdir } from "node:os";
|
|
@@ -18,6 +18,7 @@ import { spawn } from "node:child_process";
|
|
|
18
18
|
import { readFile } from "node:fs/promises";
|
|
19
19
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
20
20
|
import { renderCourtesyJpeg, renderCourtesyOverlayJpeg } from "@camstack/system/addon-utils";
|
|
21
|
+
import { getHeapSpaceStatistics, getHeapStatistics } from "node:v8";
|
|
21
22
|
import * as net$1 from "node:net";
|
|
22
23
|
import net from "node:net";
|
|
23
24
|
import { createSocket } from "node:dgram";
|
|
@@ -3081,6 +3082,258 @@ var RtpPacketizer = class {
|
|
|
3081
3082
|
}
|
|
3082
3083
|
};
|
|
3083
3084
|
//#endregion
|
|
3085
|
+
//#region src/stream-broker/stream-broker/broker-mem-probe.ts
|
|
3086
|
+
/**
|
|
3087
|
+
* broker-mem-probe — what owns the broker runner's NATIVE memory.
|
|
3088
|
+
*
|
|
3089
|
+
* The runner's `[mem]` heartbeat (`kernel/heap-watch.ts`) says how big the
|
|
3090
|
+
* process is; it cannot say who holds it. On the hub, `stream-broker` grew
|
|
3091
|
+
* 250 -> 1015 MB in steps while `heapUsed` stayed at ~90 MB and `arrayBuffers`
|
|
3092
|
+
* at ~150 MB, so ~600 MB sat outside everything the heartbeat reports. This
|
|
3093
|
+
* module is the attribution half: one `[broker-mem]` line every five minutes
|
|
3094
|
+
* carrying the kernel's view of the process (RssAnon / RssFile, the `[heap]`
|
|
3095
|
+
* brk mapping, the count of anonymous mappings) NEXT TO the broker's own
|
|
3096
|
+
* structures (rings, write queues, consumers by kind, egress children), so a
|
|
3097
|
+
* step in RSS can be laid against what changed.
|
|
3098
|
+
*
|
|
3099
|
+
* It is a probe, not a fix: no caps, no allocator tuning, no snapshot. Cost is
|
|
3100
|
+
* O(brokers + sessions) per line and O(1) per packet (two integer adds in
|
|
3101
|
+
* `copyOwned`). Everything here is a pure function over plain data so the
|
|
3102
|
+
* aggregation and the `/proc` parsers are testable on fixture text.
|
|
3103
|
+
*/
|
|
3104
|
+
var MB = 1024 * 1024;
|
|
3105
|
+
/** Cadence of the `[broker-mem]` line — aligned with the runner's `[mem]`. */
|
|
3106
|
+
var BROKER_MEM_INTERVAL_MS = 5 * 6e4;
|
|
3107
|
+
var copiedPackets = 0;
|
|
3108
|
+
var copiedBytes = 0;
|
|
3109
|
+
/** O(1) per packet: called by `copyOwned`. No allocation. */
|
|
3110
|
+
function recordCopyOwned(bytes) {
|
|
3111
|
+
copiedPackets += 1;
|
|
3112
|
+
copiedBytes += bytes;
|
|
3113
|
+
}
|
|
3114
|
+
function readCopyOwnedTotals() {
|
|
3115
|
+
return {
|
|
3116
|
+
packets: copiedPackets,
|
|
3117
|
+
bytes: copiedBytes
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
/** `RssAnon:` / `RssFile:` out of `/proc/self/status` text; null when absent. */
|
|
3121
|
+
function parseProcStatus(text) {
|
|
3122
|
+
const anon = /^RssAnon:\s+(\d+)\s*kB/m.exec(text);
|
|
3123
|
+
const file = /^RssFile:\s+(\d+)\s*kB/m.exec(text);
|
|
3124
|
+
if (anon === null || file === null) return null;
|
|
3125
|
+
return {
|
|
3126
|
+
rssAnonKb: Number(anon[1]),
|
|
3127
|
+
rssFileKb: Number(file[1])
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
/**
|
|
3131
|
+
* Summarise `/proc/self/maps`. Cheaper than `smaps_rollup`'s kernel page walk:
|
|
3132
|
+
* the file is one line per mapping and needs no per-page accounting. The
|
|
3133
|
+
* `[heap]` figure is the brk extent (virtual), which is the number glibc's
|
|
3134
|
+
* main arena grows when fragmented.
|
|
3135
|
+
*
|
|
3136
|
+
* Line shape: `start-end perms offset dev inode [path]` — an anonymous mapping
|
|
3137
|
+
* has no sixth field.
|
|
3138
|
+
*/
|
|
3139
|
+
function parseProcMaps(text) {
|
|
3140
|
+
let heapBytes = 0;
|
|
3141
|
+
let anonMappings = 0;
|
|
3142
|
+
let anonBytes = 0;
|
|
3143
|
+
let v8PageMaps = 0;
|
|
3144
|
+
let from = 0;
|
|
3145
|
+
while (from < text.length) {
|
|
3146
|
+
let to = text.indexOf("\n", from);
|
|
3147
|
+
if (to === -1) to = text.length;
|
|
3148
|
+
const line = text.slice(from, to);
|
|
3149
|
+
from = to + 1;
|
|
3150
|
+
const dash = line.indexOf("-");
|
|
3151
|
+
const space = line.indexOf(" ");
|
|
3152
|
+
if (dash <= 0 || space <= dash) continue;
|
|
3153
|
+
const size = Number.parseInt(line.slice(dash + 1, space), 16) - Number.parseInt(line.slice(0, dash), 16);
|
|
3154
|
+
if (!Number.isFinite(size)) continue;
|
|
3155
|
+
const path = line.split(/\s+/, 6)[5];
|
|
3156
|
+
if (path === void 0 || path === "") {
|
|
3157
|
+
anonMappings += 1;
|
|
3158
|
+
anonBytes += size;
|
|
3159
|
+
if (size === 262144) v8PageMaps += 1;
|
|
3160
|
+
} else if (path === "[heap]") heapBytes += size;
|
|
3161
|
+
}
|
|
3162
|
+
return {
|
|
3163
|
+
heapBytes,
|
|
3164
|
+
anonMappings,
|
|
3165
|
+
anonBytes,
|
|
3166
|
+
v8PageMaps
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
/** Read the main isolate. Probe tick only: `getHeapSpaceStatistics` walks the spaces. */
|
|
3170
|
+
function readV8Heap() {
|
|
3171
|
+
const h = getHeapStatistics();
|
|
3172
|
+
return {
|
|
3173
|
+
totalHeapBytes: h.total_heap_size,
|
|
3174
|
+
physicalBytes: h.total_physical_size,
|
|
3175
|
+
mallocedBytes: h.malloced_memory,
|
|
3176
|
+
peakMallocedBytes: h.peak_malloced_memory,
|
|
3177
|
+
externalBytes: h.external_memory,
|
|
3178
|
+
spaces: getHeapSpaceStatistics().map((x) => ({
|
|
3179
|
+
name: x.space_name,
|
|
3180
|
+
committedBytes: x.space_size
|
|
3181
|
+
}))
|
|
3182
|
+
};
|
|
3183
|
+
}
|
|
3184
|
+
var SPACE_ABBREVIATIONS = {
|
|
3185
|
+
read_only_space: "ro",
|
|
3186
|
+
old_space: "old",
|
|
3187
|
+
new_space: "new",
|
|
3188
|
+
large_object_space: "lo",
|
|
3189
|
+
new_large_object_space: "nlo",
|
|
3190
|
+
code_space: "code",
|
|
3191
|
+
code_large_object_space: "clo",
|
|
3192
|
+
map_space: "map",
|
|
3193
|
+
shared_space: "shared",
|
|
3194
|
+
shared_large_object_space: "slo",
|
|
3195
|
+
trusted_space: "trusted",
|
|
3196
|
+
trusted_large_object_space: "tlo"
|
|
3197
|
+
};
|
|
3198
|
+
/** `old:120,new:24,lo:8` — committed MB per space, empty spaces left out. */
|
|
3199
|
+
function formatHeapSpaces(spaces) {
|
|
3200
|
+
const parts = spaces.filter((x) => x.committedBytes > 0).map((x) => `${SPACE_ABBREVIATIONS[x.name] ?? x.name}:${Math.round(x.committedBytes / MB)}`);
|
|
3201
|
+
return parts.length === 0 ? "none" : parts.join(",");
|
|
3202
|
+
}
|
|
3203
|
+
/** Opened/closed consumers since the last reading; `drain()` resets. */
|
|
3204
|
+
var ConsumerChurn = class {
|
|
3205
|
+
opened = 0;
|
|
3206
|
+
closed = 0;
|
|
3207
|
+
openedByKind = /* @__PURE__ */ new Map();
|
|
3208
|
+
closedByKind = /* @__PURE__ */ new Map();
|
|
3209
|
+
recordOpen(kind) {
|
|
3210
|
+
this.opened += 1;
|
|
3211
|
+
this.openedByKind.set(kind, (this.openedByKind.get(kind) ?? 0) + 1);
|
|
3212
|
+
}
|
|
3213
|
+
recordClose(kind) {
|
|
3214
|
+
this.closed += 1;
|
|
3215
|
+
this.closedByKind.set(kind, (this.closedByKind.get(kind) ?? 0) + 1);
|
|
3216
|
+
}
|
|
3217
|
+
drain() {
|
|
3218
|
+
const reading = {
|
|
3219
|
+
opened: this.opened,
|
|
3220
|
+
closed: this.closed,
|
|
3221
|
+
openedByKind: Object.fromEntries(this.openedByKind),
|
|
3222
|
+
closedByKind: Object.fromEntries(this.closedByKind)
|
|
3223
|
+
};
|
|
3224
|
+
this.opened = 0;
|
|
3225
|
+
this.closed = 0;
|
|
3226
|
+
this.openedByKind = /* @__PURE__ */ new Map();
|
|
3227
|
+
this.closedByKind = /* @__PURE__ */ new Map();
|
|
3228
|
+
return reading;
|
|
3229
|
+
}
|
|
3230
|
+
};
|
|
3231
|
+
/** The one churn counter of this process (the broker runner). */
|
|
3232
|
+
var consumerChurn = new ConsumerChurn();
|
|
3233
|
+
/**
|
|
3234
|
+
* Sliding-window limiter for the attach/detach lines. `admit(now)` answers
|
|
3235
|
+
* whether this line may be written; the number of lines swallowed since the
|
|
3236
|
+
* last admitted one is returned with it so the next line can say so (a branch
|
|
3237
|
+
* that drops work names it).
|
|
3238
|
+
*/
|
|
3239
|
+
function createLineLimiter(maxPerWindow, windowMs) {
|
|
3240
|
+
let windowStart = 0;
|
|
3241
|
+
let inWindow = 0;
|
|
3242
|
+
let suppressed = 0;
|
|
3243
|
+
return (now) => {
|
|
3244
|
+
if (now - windowStart >= windowMs) {
|
|
3245
|
+
windowStart = now;
|
|
3246
|
+
inWindow = 0;
|
|
3247
|
+
}
|
|
3248
|
+
if (inWindow >= maxPerWindow) {
|
|
3249
|
+
suppressed += 1;
|
|
3250
|
+
return {
|
|
3251
|
+
admit: false,
|
|
3252
|
+
suppressedBefore: 0
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
inWindow += 1;
|
|
3256
|
+
const suppressedBefore = suppressed;
|
|
3257
|
+
suppressed = 0;
|
|
3258
|
+
return {
|
|
3259
|
+
admit: true,
|
|
3260
|
+
suppressedBefore
|
|
3261
|
+
};
|
|
3262
|
+
};
|
|
3263
|
+
}
|
|
3264
|
+
var consumerLineLimiter = createLineLimiter(40, 6e4);
|
|
3265
|
+
/**
|
|
3266
|
+
* One consumer attached to / detached from a broker: counted always (the
|
|
3267
|
+
* `[broker-mem]` line reports opened/closed since the last line), written at
|
|
3268
|
+
* info under a rate limit so a step in RSS can be matched to an event. The
|
|
3269
|
+
* line after a suppressed burst says how many were swallowed.
|
|
3270
|
+
*/
|
|
3271
|
+
function reportConsumerEvent(logger, event, kind, deviceId, total, now = Date.now()) {
|
|
3272
|
+
if (event === "attach") consumerChurn.recordOpen(kind);
|
|
3273
|
+
else consumerChurn.recordClose(kind);
|
|
3274
|
+
if (logger === void 0 || logger === null) return;
|
|
3275
|
+
const verdict = consumerLineLimiter(now);
|
|
3276
|
+
if (!verdict.admit) return;
|
|
3277
|
+
const suppressed = verdict.suppressedBefore > 0 ? ` suppressedBefore=${verdict.suppressedBefore}` : "";
|
|
3278
|
+
logger.info(`consumer ${event} kind=${kind} total=${total}${suppressed}`, { ...deviceId !== void 0 ? { tags: { deviceId } } : {} });
|
|
3279
|
+
}
|
|
3280
|
+
function ringBytesOf(s) {
|
|
3281
|
+
return s.preBufferBytes + s.clipBytes + s.rtpRingBytes + s.keyframeCacheBytes;
|
|
3282
|
+
}
|
|
3283
|
+
function mb(bytes) {
|
|
3284
|
+
return (bytes / MB).toFixed(1);
|
|
3285
|
+
}
|
|
3286
|
+
function kindList(counts) {
|
|
3287
|
+
const keys = Object.keys(counts).sort();
|
|
3288
|
+
return keys.length === 0 ? "none" : keys.map((k) => `${k}:${counts[k]}`).join(",");
|
|
3289
|
+
}
|
|
3290
|
+
/** One greppable `key=value` line; no per-device breakdown beyond the top N. */
|
|
3291
|
+
function formatBrokerMem(input) {
|
|
3292
|
+
const { samples, proc, churn, copied } = input;
|
|
3293
|
+
const consumers = {};
|
|
3294
|
+
let rtsp = 0;
|
|
3295
|
+
let audio = 0;
|
|
3296
|
+
let pipe = 0;
|
|
3297
|
+
let ingesting = 0;
|
|
3298
|
+
let planes = 0;
|
|
3299
|
+
let pre = 0;
|
|
3300
|
+
let clip = 0;
|
|
3301
|
+
let rtp = 0;
|
|
3302
|
+
let kf = 0;
|
|
3303
|
+
let qSum = 0;
|
|
3304
|
+
let qMax = 0;
|
|
3305
|
+
for (const s of samples) {
|
|
3306
|
+
if (s.ingesting) ingesting += 1;
|
|
3307
|
+
if (s.annexBPlaneActive) planes += 1;
|
|
3308
|
+
rtsp += s.rtspSessions;
|
|
3309
|
+
audio += s.audioSubscribers;
|
|
3310
|
+
pipe += s.pipeClients;
|
|
3311
|
+
pre += s.preBufferBytes;
|
|
3312
|
+
clip += s.clipBytes;
|
|
3313
|
+
rtp += s.rtpRingBytes;
|
|
3314
|
+
kf += s.keyframeCacheBytes;
|
|
3315
|
+
qSum += s.writeQueueSumBytes;
|
|
3316
|
+
if (s.writeQueueMaxBytes > qMax) qMax = s.writeQueueMaxBytes;
|
|
3317
|
+
for (const [kind, n] of Object.entries(s.encodedByKind)) consumers[kind] = (consumers[kind] ?? 0) + n;
|
|
3318
|
+
}
|
|
3319
|
+
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(",");
|
|
3320
|
+
const parts = ["[broker-mem]"];
|
|
3321
|
+
if (proc.status !== null) parts.push(`rssAnonMb=${(proc.status.rssAnonKb / 1024).toFixed(0)}`, `rssFileMb=${(proc.status.rssFileKb / 1024).toFixed(0)}`);
|
|
3322
|
+
if (proc.maps !== null) parts.push(`heapMapMb=${mb(proc.maps.heapBytes)}`, `anonMaps=${proc.maps.anonMappings}`, `anonMapsMb=${mb(proc.maps.anonBytes)}`, `v8PageMaps=${proc.maps.v8PageMaps}`);
|
|
3323
|
+
const v8 = input.v8;
|
|
3324
|
+
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)}`);
|
|
3325
|
+
const w = input.worker;
|
|
3326
|
+
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"}`);
|
|
3327
|
+
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}`);
|
|
3328
|
+
return parts.join(" ");
|
|
3329
|
+
}
|
|
3330
|
+
/** Count entries named `name` in `process.getActiveResourcesInfo()`. */
|
|
3331
|
+
function countActiveResources(resources, name) {
|
|
3332
|
+
let n = 0;
|
|
3333
|
+
for (const r of resources) if (r === name) n += 1;
|
|
3334
|
+
return n;
|
|
3335
|
+
}
|
|
3336
|
+
//#endregion
|
|
3084
3337
|
//#region src/stream-broker/stream-broker/owned-buffer.ts
|
|
3085
3338
|
/**
|
|
3086
3339
|
* Buffers that enter a LONG-LIVED ring own their backing store.
|
|
@@ -3140,6 +3393,7 @@ function detachFromPool(buf) {
|
|
|
3140
3393
|
function copyOwned(src, start, end) {
|
|
3141
3394
|
const owned = Buffer.allocUnsafeSlow(end - start);
|
|
3142
3395
|
src.copy(owned, 0, start, end);
|
|
3396
|
+
recordCopyOwned(end - start);
|
|
3143
3397
|
return owned;
|
|
3144
3398
|
}
|
|
3145
3399
|
//#endregion
|
|
@@ -3752,6 +4006,26 @@ var RtspRestreamer = class {
|
|
|
3752
4006
|
for (const session of this.sessions.values()) if (session.isPrestreamIntent()) n += 1;
|
|
3753
4007
|
return n;
|
|
3754
4008
|
}
|
|
4009
|
+
/**
|
|
4010
|
+
* What this restreamer holds, for the `[broker-mem]` probe: live sessions,
|
|
4011
|
+
* their socket write queues (sum and max) and the keyframe caches. One pass
|
|
4012
|
+
* over the sessions, no allocation beyond the result.
|
|
4013
|
+
*/
|
|
4014
|
+
getMemProbe() {
|
|
4015
|
+
let sum = 0;
|
|
4016
|
+
let max = 0;
|
|
4017
|
+
for (const s of this.sessions.values()) {
|
|
4018
|
+
const q = s.getWriteQueueBytes();
|
|
4019
|
+
sum += q;
|
|
4020
|
+
if (q > max) max = q;
|
|
4021
|
+
}
|
|
4022
|
+
return {
|
|
4023
|
+
sessions: this.sessions.size,
|
|
4024
|
+
writeQueueSumBytes: sum,
|
|
4025
|
+
writeQueueMaxBytes: max,
|
|
4026
|
+
keyframeCacheBytes: this.lastKeyframeNalBytes + this.lastKeyframeRtpBytes
|
|
4027
|
+
};
|
|
4028
|
+
}
|
|
3755
4029
|
/** Per-session diagnostic snapshots consumed by `StreamBroker.listClients`. */
|
|
3756
4030
|
listSessionInfos() {
|
|
3757
4031
|
return [...this.sessions.values()].map((s) => s.getInfo());
|
|
@@ -3763,6 +4037,7 @@ var RtspRestreamer = class {
|
|
|
3763
4037
|
streamName: this.streamName,
|
|
3764
4038
|
total: this.sessions.size
|
|
3765
4039
|
} });
|
|
4040
|
+
reportConsumerEvent(this.logger, "attach", "rtsp", this.deviceId, this.sessions.size);
|
|
3766
4041
|
this._onSessionCountChanged?.();
|
|
3767
4042
|
this.reconcileSentryAudio();
|
|
3768
4043
|
if (this._rtpPassthroughMode) setTimeout(() => this.servePendingSessionsRtp(), 0);
|
|
@@ -4227,6 +4502,7 @@ var RtspRestreamer = class {
|
|
|
4227
4502
|
streamName: this.streamName,
|
|
4228
4503
|
total: this.sessions.size
|
|
4229
4504
|
} });
|
|
4505
|
+
reportConsumerEvent(this.logger, "detach", "rtsp", this.deviceId, this.sessions.size);
|
|
4230
4506
|
this._onSessionCountChanged?.();
|
|
4231
4507
|
this.reconcileSentryAudio();
|
|
4232
4508
|
}
|
|
@@ -4496,6 +4772,10 @@ var RtspSession = class {
|
|
|
4496
4772
|
this.onClose?.();
|
|
4497
4773
|
});
|
|
4498
4774
|
}
|
|
4775
|
+
/** Bytes the socket has accepted but not yet flushed (corked bytes included). */
|
|
4776
|
+
getWriteQueueBytes() {
|
|
4777
|
+
return this.socket.writableLength;
|
|
4778
|
+
}
|
|
4499
4779
|
getSessionId() {
|
|
4500
4780
|
return this.sessionId;
|
|
4501
4781
|
}
|
|
@@ -4863,6 +5143,17 @@ var RtspSession = class {
|
|
|
4863
5143
|
//#endregion
|
|
4864
5144
|
//#region src/stream-broker/rtsp/rtsp-listen-server.ts
|
|
4865
5145
|
/**
|
|
5146
|
+
* Attribution of the synthetic consumer a {@link BrokerWarmUp} subscribes:
|
|
5147
|
+
* the RTSP listen server holding a cold broker open for a DESCRIBE. Named
|
|
5148
|
+
* after that real consumer — it used to subscribe with no attribution and
|
|
5149
|
+
* surfaced as `unknown` (45 attach/detach pairs per 5 min on the hub).
|
|
5150
|
+
*/
|
|
5151
|
+
var RTSP_DESCRIBE_WARMUP_ATTRIBUTION = {
|
|
5152
|
+
kind: "rtsp-listen",
|
|
5153
|
+
label: "describe-warmup",
|
|
5154
|
+
media: "both"
|
|
5155
|
+
};
|
|
5156
|
+
/**
|
|
4866
5157
|
* Single TCP server for all RTSP restream connections.
|
|
4867
5158
|
* Routes clients to the correct RtspRestreamer by a random token path
|
|
4868
5159
|
* (not the predictable brokerId) for security.
|
|
@@ -6449,6 +6740,10 @@ var EgressTranscodeManager = class {
|
|
|
6449
6740
|
/** In-flight starts, keyed by sharing key — concurrent acquires share one. */
|
|
6450
6741
|
pending = /* @__PURE__ */ new Map();
|
|
6451
6742
|
releaseGraceMs;
|
|
6743
|
+
/** Live egress transcodes (ffmpeg children), for the `[broker-mem]` probe. */
|
|
6744
|
+
activeCount() {
|
|
6745
|
+
return this.entries.size;
|
|
6746
|
+
}
|
|
6452
6747
|
constructor(opts) {
|
|
6453
6748
|
this.opts = opts;
|
|
6454
6749
|
this.releaseGraceMs = opts.releaseGraceMs ?? DEFAULT_RELEASE_GRACE_MS;
|
|
@@ -12812,6 +13107,8 @@ var StreamBroker = class StreamBroker {
|
|
|
12812
13107
|
* timeout was chosen against.
|
|
12813
13108
|
*/
|
|
12814
13109
|
static RTP_ACTIVITY_SWEEP_MS = 1e3;
|
|
13110
|
+
/** A source counts as ingesting when its last video packet is younger than this. */
|
|
13111
|
+
static INGESTING_WITHIN_MS = 1e4;
|
|
12815
13112
|
manualStop = false;
|
|
12816
13113
|
stopping = false;
|
|
12817
13114
|
pipeServer;
|
|
@@ -13086,6 +13383,33 @@ var StreamBroker = class StreamBroker {
|
|
|
13086
13383
|
return this.lastPacketAt;
|
|
13087
13384
|
}
|
|
13088
13385
|
/**
|
|
13386
|
+
* What this broker holds, for the `[broker-mem]` probe (`broker-mem-probe.ts`).
|
|
13387
|
+
* Ring sizes are the rings' own counters (backing-store bytes), so this is
|
|
13388
|
+
* O(sessions + subscribers) and allocates only the result.
|
|
13389
|
+
*/
|
|
13390
|
+
getMemSample(now) {
|
|
13391
|
+
const encodedByKind = {};
|
|
13392
|
+
for (const entry of this.encodedCallbacks.values()) encodedByKind[entry.attribution.kind] = (encodedByKind[entry.attribution.kind] ?? 0) + 1;
|
|
13393
|
+
for (const entry of this.rtpVideoCallbacks.values()) encodedByKind[entry.attribution.kind] = (encodedByKind[entry.attribution.kind] ?? 0) + 1;
|
|
13394
|
+
const rtsp = this.rtspRestreamer.getMemProbe();
|
|
13395
|
+
return {
|
|
13396
|
+
deviceId: this.numericDeviceId,
|
|
13397
|
+
brokerId: this.deviceId,
|
|
13398
|
+
ingesting: this.lastPacketAt > 0 && now - this.lastPacketAt < StreamBroker.INGESTING_WITHIN_MS,
|
|
13399
|
+
preBufferBytes: this.preBuffer.getBufferedBytes(),
|
|
13400
|
+
clipBytes: this.clipBuffer?.getBufferedBytes() ?? 0,
|
|
13401
|
+
rtpRingBytes: this.rtpRingBytes,
|
|
13402
|
+
keyframeCacheBytes: rtsp.keyframeCacheBytes,
|
|
13403
|
+
rtspSessions: rtsp.sessions,
|
|
13404
|
+
writeQueueSumBytes: rtsp.writeQueueSumBytes,
|
|
13405
|
+
writeQueueMaxBytes: rtsp.writeQueueMaxBytes,
|
|
13406
|
+
encodedByKind,
|
|
13407
|
+
audioSubscribers: this.audioChunkPlane.subscriberCount,
|
|
13408
|
+
pipeClients: this.pipeServer.getClientCount(),
|
|
13409
|
+
annexBPlaneActive: this.annexBPlaneActive
|
|
13410
|
+
};
|
|
13411
|
+
}
|
|
13412
|
+
/**
|
|
13089
13413
|
* Whether a LAZY publisher is being built right now.
|
|
13090
13414
|
*
|
|
13091
13415
|
* A consumer deciding whether it is worth waiting for this source needs to
|
|
@@ -14048,6 +14372,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14048
14372
|
packetsDelivered: 0
|
|
14049
14373
|
};
|
|
14050
14374
|
this.encodedCallbacks.set(callback, entry);
|
|
14375
|
+
reportConsumerEvent(this.logger, "attach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14051
14376
|
if (this.streamingDebug) this.logger?.info("encoded subscriber added", { meta: {
|
|
14052
14377
|
total: this.encodedCallbacks.size,
|
|
14053
14378
|
subscriberId: entry.id,
|
|
@@ -14058,6 +14383,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14058
14383
|
this.checkDemand();
|
|
14059
14384
|
return () => {
|
|
14060
14385
|
this.encodedCallbacks.delete(callback);
|
|
14386
|
+
reportConsumerEvent(this.logger, "detach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14061
14387
|
if (this.streamingDebug) this.logger?.info("encoded subscriber removed", { meta: {
|
|
14062
14388
|
total: this.encodedCallbacks.size,
|
|
14063
14389
|
subscriberId: entry.id
|
|
@@ -14152,6 +14478,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14152
14478
|
packetsDelivered: 0
|
|
14153
14479
|
};
|
|
14154
14480
|
this.rtpVideoCallbacks.set(callback, entry);
|
|
14481
|
+
reportConsumerEvent(this.logger, "attach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14155
14482
|
if (this.streamingDebug || CH_RTP.on) this.logger?.info("rtp video subscriber added", { meta: {
|
|
14156
14483
|
total: this.rtpVideoCallbacks.size,
|
|
14157
14484
|
subscriberId: entry.id,
|
|
@@ -14161,6 +14488,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14161
14488
|
this.checkDemand();
|
|
14162
14489
|
return () => {
|
|
14163
14490
|
this.rtpVideoCallbacks.delete(callback);
|
|
14491
|
+
reportConsumerEvent(this.logger, "detach", attribution.kind, this.numericDeviceId, this.encodedCallbacks.size + this.rtpVideoCallbacks.size);
|
|
14164
14492
|
if (this.streamingDebug || CH_RTP.on) this.logger?.info("rtp video subscriber removed", { meta: {
|
|
14165
14493
|
total: this.rtpVideoCallbacks.size,
|
|
14166
14494
|
subscriberId: entry.id
|
|
@@ -14561,6 +14889,7 @@ var StreamBroker = class StreamBroker {
|
|
|
14561
14889
|
tag,
|
|
14562
14890
|
accept: options?.accept
|
|
14563
14891
|
});
|
|
14892
|
+
reportConsumerEvent(this.logger, "attach", "audio-chunks", this.numericDeviceId, this.audioChunkPlane.subscriberCount);
|
|
14564
14893
|
this.checkDemand();
|
|
14565
14894
|
return result;
|
|
14566
14895
|
}
|
|
@@ -14571,7 +14900,10 @@ var StreamBroker = class StreamBroker {
|
|
|
14571
14900
|
/** Release an audio-chunk subscription. Returns `true` when it existed. */
|
|
14572
14901
|
unsubscribeAudioChunks(subscriptionId) {
|
|
14573
14902
|
const released = this.audioChunkPlane.unsubscribe(subscriptionId);
|
|
14574
|
-
if (released)
|
|
14903
|
+
if (released) {
|
|
14904
|
+
reportConsumerEvent(this.logger, "detach", "audio-chunks", this.numericDeviceId, this.audioChunkPlane.subscriberCount);
|
|
14905
|
+
this.checkDemand();
|
|
14906
|
+
}
|
|
14575
14907
|
return released;
|
|
14576
14908
|
}
|
|
14577
14909
|
getStats() {
|
|
@@ -19398,6 +19730,10 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19398
19730
|
return { killed: broker.killClient(input.channel, input.handle) };
|
|
19399
19731
|
}
|
|
19400
19732
|
/** Raw broker instances — internal use only. */
|
|
19733
|
+
/** Live egress transcodes (ffmpeg children) — the `[broker-mem]` probe. */
|
|
19734
|
+
getEgressTranscodeCount() {
|
|
19735
|
+
return this.egressTranscodeManager?.activeCount() ?? 0;
|
|
19736
|
+
}
|
|
19401
19737
|
getBrokerInstances() {
|
|
19402
19738
|
return [...this.brokers.values()];
|
|
19403
19739
|
}
|
|
@@ -19664,7 +20000,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19664
20000
|
const sourceBroker = this.brokers.get(sourceBrokerId);
|
|
19665
20001
|
if (!sourceBroker) continue;
|
|
19666
20002
|
const existingToken = this.persistedTokens.get(profileBrokerId);
|
|
19667
|
-
const warmUp = () => sourceBroker.onEncodedData(() => {});
|
|
20003
|
+
const warmUp = () => sourceBroker.onEncodedData(() => {}, RTSP_DESCRIBE_WARMUP_ATTRIBUTION);
|
|
19668
20004
|
const openPrestreamWindow = () => sourceBroker.openPrestreamWindow();
|
|
19669
20005
|
this.rtspServer.registerRestreamer(profileBrokerId, sourceBroker.getRtspRestreamer(), warmUp, existingToken, openPrestreamWindow);
|
|
19670
20006
|
this.profileRestreamerBindings.set(profileBrokerId, sourceBrokerId);
|
|
@@ -21323,7 +21659,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
21323
21659
|
this.brokers.set(brokerId, broker);
|
|
21324
21660
|
this.applyClipRetention(deviceId);
|
|
21325
21661
|
const existingToken = this.persistedTokens.get(brokerId);
|
|
21326
|
-
const warmUp = () => broker.onEncodedData(() => {});
|
|
21662
|
+
const warmUp = () => broker.onEncodedData(() => {}, RTSP_DESCRIBE_WARMUP_ATTRIBUTION);
|
|
21327
21663
|
const openPrestreamWindow = () => broker.openPrestreamWindow();
|
|
21328
21664
|
this.rtspServer.registerRestreamer(brokerId, broker.getRtspRestreamer(), warmUp, existingToken, openPrestreamWindow);
|
|
21329
21665
|
if (!existingToken) this.rtspProvider.persistTokens();
|
|
@@ -33652,14 +33988,11 @@ function parseWorkerMessage(message) {
|
|
|
33652
33988
|
function defaultWorkerFilename() {
|
|
33653
33989
|
return fileURLToPath(new URL("./demux-worker-child.js", import.meta.url));
|
|
33654
33990
|
}
|
|
33655
|
-
/**
|
|
33656
|
-
|
|
33657
|
-
* callers are matched by request id; the worker's own event loop serializes
|
|
33658
|
-
* mp4box. Live RTP on the parent thread is not waiting on box parsing.
|
|
33659
|
-
*/
|
|
33660
|
-
function createOffThreadJsParse(deps) {
|
|
33991
|
+
/** {@link createOffThreadJsParse} plus the heap accessor the memory probe reads. */
|
|
33992
|
+
function createOffThreadDemuxClient(deps) {
|
|
33661
33993
|
const Ctor = deps.WorkerCtor;
|
|
33662
33994
|
let worker = null;
|
|
33995
|
+
let spawnedAt = 0;
|
|
33663
33996
|
let nextId = 1;
|
|
33664
33997
|
const pending = /* @__PURE__ */ new Map();
|
|
33665
33998
|
const failAll = () => {
|
|
@@ -33692,12 +34025,13 @@ function createOffThreadJsParse(deps) {
|
|
|
33692
34025
|
const spawned = new Ctor(deps.workerFilename);
|
|
33693
34026
|
attach(spawned);
|
|
33694
34027
|
worker = spawned;
|
|
34028
|
+
spawnedAt = Date.now();
|
|
33695
34029
|
return spawned;
|
|
33696
34030
|
} catch {
|
|
33697
34031
|
return null;
|
|
33698
34032
|
}
|
|
33699
34033
|
};
|
|
33700
|
-
|
|
34034
|
+
const parse = async (bytes) => {
|
|
33701
34035
|
if (!deps.existsSync(deps.workerFilename)) return deps.parseInProcess(bytes);
|
|
33702
34036
|
const w = ensureWorker();
|
|
33703
34037
|
if (!w) return deps.parseInProcess(bytes);
|
|
@@ -33713,9 +34047,36 @@ function createOffThreadJsParse(deps) {
|
|
|
33713
34047
|
w.postMessage(request, [copy.buffer]);
|
|
33714
34048
|
});
|
|
33715
34049
|
};
|
|
34050
|
+
const readHeaps = async (now = Date.now()) => {
|
|
34051
|
+
const live = worker;
|
|
34052
|
+
const empty = {
|
|
34053
|
+
workers: 0,
|
|
34054
|
+
heapBytes: 0,
|
|
34055
|
+
physicalBytes: 0,
|
|
34056
|
+
usedBytes: 0,
|
|
34057
|
+
maxAgeMs: 0,
|
|
34058
|
+
idleTerminates: false
|
|
34059
|
+
};
|
|
34060
|
+
if (live === null) return empty;
|
|
34061
|
+
const stats = await live.getHeapStatistics?.().catch(() => null);
|
|
34062
|
+
return {
|
|
34063
|
+
...empty,
|
|
34064
|
+
workers: 1,
|
|
34065
|
+
maxAgeMs: Math.max(0, now - spawnedAt),
|
|
34066
|
+
...stats ? {
|
|
34067
|
+
heapBytes: stats.total_heap_size,
|
|
34068
|
+
physicalBytes: stats.total_physical_size,
|
|
34069
|
+
usedBytes: stats.used_heap_size
|
|
34070
|
+
} : {}
|
|
34071
|
+
};
|
|
34072
|
+
};
|
|
34073
|
+
return {
|
|
34074
|
+
parse,
|
|
34075
|
+
readHeaps
|
|
34076
|
+
};
|
|
33716
34077
|
}
|
|
33717
|
-
function
|
|
33718
|
-
return
|
|
34078
|
+
function defaultOffThreadClient() {
|
|
34079
|
+
return createOffThreadDemuxClient({
|
|
33719
34080
|
workerFilename: defaultWorkerFilename(),
|
|
33720
34081
|
existsSync,
|
|
33721
34082
|
parseInProcess: demuxSegmentFullJs,
|
|
@@ -33725,9 +34086,20 @@ function defaultOffThreadJsParse() {
|
|
|
33725
34086
|
var shared;
|
|
33726
34087
|
/** Process-wide JS parse: one Worker, shared by every WebRTC session. */
|
|
33727
34088
|
var offThreadJsParse = (bytes) => {
|
|
33728
|
-
shared ??=
|
|
33729
|
-
return shared(bytes);
|
|
34089
|
+
shared ??= defaultOffThreadClient();
|
|
34090
|
+
return shared.parse(bytes);
|
|
33730
34091
|
};
|
|
34092
|
+
/** Heap of the shared demux worker, for the `[broker-mem]` probe (tick only). */
|
|
34093
|
+
async function readDemuxWorkerHeaps(now) {
|
|
34094
|
+
return shared ? shared.readHeaps(now) : {
|
|
34095
|
+
workers: 0,
|
|
34096
|
+
heapBytes: 0,
|
|
34097
|
+
physicalBytes: 0,
|
|
34098
|
+
usedBytes: 0,
|
|
34099
|
+
maxAgeMs: 0,
|
|
34100
|
+
idleTerminates: false
|
|
34101
|
+
};
|
|
34102
|
+
}
|
|
33731
34103
|
/**
|
|
33732
34104
|
* Drop-in for the session `demuxVideo` hook: JS parse off the live loop,
|
|
33733
34105
|
* ffmpeg subprocess fallback unchanged.
|
|
@@ -35270,6 +35642,7 @@ var BrokerWebrtcServer = class {
|
|
|
35270
35642
|
}
|
|
35271
35643
|
};
|
|
35272
35644
|
this.sessions.set(sessionId, entry);
|
|
35645
|
+
reportConsumerEvent(this.logger, "attach", `webrtc:${entry.consumerKind}`, entry.rebind?.deviceId, this.sessions.size);
|
|
35273
35646
|
session.onClosed = () => this.cleanupSession(sessionId);
|
|
35274
35647
|
const locateRecorded = this.locateRecordedSegment;
|
|
35275
35648
|
const readRecorded = this.readRecordedSegmentBytes;
|
|
@@ -36487,6 +36860,7 @@ var BrokerWebrtcServer = class {
|
|
|
36487
36860
|
const entry = this.sessions.get(sessionId);
|
|
36488
36861
|
if (!entry) return;
|
|
36489
36862
|
this.sessions.delete(sessionId);
|
|
36863
|
+
reportConsumerEvent(this.logger, "detach", `webrtc:${entry.consumerKind}`, entry.rebind?.deviceId, this.sessions.size);
|
|
36490
36864
|
entry.pendingSwitch?.cancel();
|
|
36491
36865
|
entry.pendingSwitch = void 0;
|
|
36492
36866
|
let brokerStillServed = false;
|
|
@@ -37208,6 +37582,47 @@ function dialAnswerFromError(err) {
|
|
|
37208
37582
|
};
|
|
37209
37583
|
}
|
|
37210
37584
|
//#endregion
|
|
37585
|
+
//#region src/stream-broker/broker-mem-report.ts
|
|
37586
|
+
/** Linux only: elsewhere (or on a read error) the native view is simply absent. */
|
|
37587
|
+
async function readProc() {
|
|
37588
|
+
if (process.platform !== "linux") return {
|
|
37589
|
+
status: null,
|
|
37590
|
+
maps: null
|
|
37591
|
+
};
|
|
37592
|
+
const [status, maps] = await Promise.all([readFile("/proc/self/status", "utf8").then(parseProcStatus, () => null), readFile("/proc/self/maps", "utf8").then(parseProcMaps, () => null)]);
|
|
37593
|
+
return {
|
|
37594
|
+
status,
|
|
37595
|
+
maps
|
|
37596
|
+
};
|
|
37597
|
+
}
|
|
37598
|
+
async function emitOnce(deps) {
|
|
37599
|
+
const now = Date.now();
|
|
37600
|
+
const [proc, worker] = await Promise.all([readProc(), readDemuxWorkerHeaps(now)]);
|
|
37601
|
+
const resources = process.getActiveResourcesInfo();
|
|
37602
|
+
const line = formatBrokerMem({
|
|
37603
|
+
samples: deps.manager.getBrokerInstances().map((b) => b.getMemSample(now)),
|
|
37604
|
+
webrtcSessions: deps.webrtcSessions(),
|
|
37605
|
+
egressTranscodes: deps.manager.getEgressTranscodeCount(),
|
|
37606
|
+
childProcesses: countActiveResources(resources, "ProcessWrap"),
|
|
37607
|
+
proc,
|
|
37608
|
+
churn: consumerChurn.drain(),
|
|
37609
|
+
copied: readCopyOwnedTotals(),
|
|
37610
|
+
v8: readV8Heap(),
|
|
37611
|
+
worker
|
|
37612
|
+
});
|
|
37613
|
+
deps.logger.info(line);
|
|
37614
|
+
}
|
|
37615
|
+
/** Start the five-minute `[broker-mem]` heartbeat; returns its stop function. */
|
|
37616
|
+
function startBrokerMemReport(deps) {
|
|
37617
|
+
const timer = setInterval(() => {
|
|
37618
|
+
emitOnce(deps).catch((err) => {
|
|
37619
|
+
deps.logger.warn("broker-mem probe failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
37620
|
+
});
|
|
37621
|
+
}, BROKER_MEM_INTERVAL_MS);
|
|
37622
|
+
timer.unref();
|
|
37623
|
+
return () => clearInterval(timer);
|
|
37624
|
+
}
|
|
37625
|
+
//#endregion
|
|
37211
37626
|
//#region src/stream-broker/event-loop-stall-monitor.ts
|
|
37212
37627
|
var DEFAULT_INTERVAL_MS = 100;
|
|
37213
37628
|
var DEFAULT_THRESHOLD_MS = 250;
|
|
@@ -37382,6 +37797,8 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
37382
37797
|
* leaks those sockets and wedges the next runner's werift ICE agent
|
|
37383
37798
|
* (DTLS never connects until a full hub restart). */
|
|
37384
37799
|
webrtcServer = null;
|
|
37800
|
+
/** Stops the five-minute `[broker-mem]` attribution heartbeat. */
|
|
37801
|
+
stopBrokerMemReport = null;
|
|
37385
37802
|
/** Names this process's event-loop stalls — see onInitialize. */
|
|
37386
37803
|
loopStallMonitor = null;
|
|
37387
37804
|
/** The `log-channels` provider for this process. Stopped on shutdown so a
|
|
@@ -37739,6 +38156,11 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
37739
38156
|
this.webrtcServer = webrtcServer;
|
|
37740
38157
|
const webrtcSessionProvider = new WebrtcSessionProvider(webrtcServer, this.brokerManager);
|
|
37741
38158
|
this.metricsSnapshotTimer = setInterval(() => this.emitBrokerMetricsSnapshot(), BROKER_METRICS_SNAPSHOT_INTERVAL_MS);
|
|
38159
|
+
this.stopBrokerMemReport = startBrokerMemReport({
|
|
38160
|
+
logger: this.ctx.logger,
|
|
38161
|
+
manager: this.brokerManager,
|
|
38162
|
+
webrtcSessions: () => webrtcServer.getSessionCount()
|
|
38163
|
+
});
|
|
37742
38164
|
const widgetsProvider = { listWidgets: async () => [{
|
|
37743
38165
|
tab: "device-tab",
|
|
37744
38166
|
label: "Stream Brokers",
|
|
@@ -37843,6 +38265,8 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
37843
38265
|
async onShutdown() {
|
|
37844
38266
|
this.logChannels?.stop();
|
|
37845
38267
|
this.logChannels = null;
|
|
38268
|
+
this.stopBrokerMemReport?.();
|
|
38269
|
+
this.stopBrokerMemReport = null;
|
|
37846
38270
|
this.loopStallMonitor?.stop();
|
|
37847
38271
|
this.loopStallMonitor = null;
|
|
37848
38272
|
this.flushProbeSnapshots(Date.now(), true);
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CRxJwkgE.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|