@camstack/addon-pipeline 1.2.366 → 1.2.368
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-C34VqcPo.js → custom-model-catalog-BY-aEI2s.js} +2 -2
- package/dist/{custom-model-catalog-CtpK3FKL.mjs → custom-model-catalog-Bnel86hH.mjs} +2 -2
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-BshC0NZR.mjs → dist-5AhGOWWu.mjs} +20270 -1345
- package/dist/{dist-i4kS_3O7.js → dist-DDXsZaRH.js} +20270 -1345
- package/dist/{model-catalogs-Cy7mZRWh.js → model-catalogs-DJN58c_U.js} +1 -1
- package/dist/{model-catalogs-DNx5ATgm.mjs → model-catalogs-DWR9EsGI.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-CiciZWts.js} +1 -1
- package/dist/{node-Dqmrb990.mjs → node-DqZJFOzR.mjs} +1 -1
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-DLCPFTGC.js → process-memory-BRu19Dgr.js} +1 -1
- package/dist/{process-memory-DyfrbFY4.mjs → process-memory-Batknjjt.mjs} +1 -1
- package/dist/recorder/index.js +3 -2
- package/dist/recorder/index.mjs +3 -2
- package/dist/{segment-demux-js-C9AYdKkD.js → segment-demux-js-0CRf7GAb.js} +2 -2
- package/dist/{segment-demux-js-COoPqx93.mjs → segment-demux-js-dlhO3Q8W.mjs} +2 -2
- 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-CSajlHKC.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BfVREmhg.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-5KL24-a_.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-KJ1eka58.mjs} +2 -2
- package/dist/stream-broker/index.js +240 -65
- package/dist/stream-broker/index.mjs +240 -65
- 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-DDXsZaRH.js");
|
|
7
|
+
const require_node = require("../node-CiciZWts.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-0CRf7GAb.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");
|
|
@@ -3086,6 +3087,25 @@ var RtpPacketizer = class {
|
|
|
3086
3087
|
};
|
|
3087
3088
|
//#endregion
|
|
3088
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;
|
|
3089
3109
|
/** Cadence of the `[broker-mem]` line — aligned with the runner's `[mem]`. */
|
|
3090
3110
|
var BROKER_MEM_INTERVAL_MS = 5 * 6e4;
|
|
3091
3111
|
var copiedPackets = 0;
|
|
@@ -3124,6 +3144,7 @@ function parseProcMaps(text) {
|
|
|
3124
3144
|
let heapBytes = 0;
|
|
3125
3145
|
let anonMappings = 0;
|
|
3126
3146
|
let anonBytes = 0;
|
|
3147
|
+
let v8PageMaps = 0;
|
|
3127
3148
|
let from = 0;
|
|
3128
3149
|
while (from < text.length) {
|
|
3129
3150
|
let to = text.indexOf("\n", from);
|
|
@@ -3139,14 +3160,50 @@ function parseProcMaps(text) {
|
|
|
3139
3160
|
if (path === void 0 || path === "") {
|
|
3140
3161
|
anonMappings += 1;
|
|
3141
3162
|
anonBytes += size;
|
|
3163
|
+
if (size === 262144) v8PageMaps += 1;
|
|
3142
3164
|
} else if (path === "[heap]") heapBytes += size;
|
|
3143
3165
|
}
|
|
3144
3166
|
return {
|
|
3145
3167
|
heapBytes,
|
|
3146
3168
|
anonMappings,
|
|
3147
|
-
anonBytes
|
|
3169
|
+
anonBytes,
|
|
3170
|
+
v8PageMaps
|
|
3148
3171
|
};
|
|
3149
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
|
+
}
|
|
3150
3207
|
/** Opened/closed consumers since the last reading; `drain()` resets. */
|
|
3151
3208
|
var ConsumerChurn = class {
|
|
3152
3209
|
opened = 0;
|
|
@@ -3224,7 +3281,6 @@ function reportConsumerEvent(logger, event, kind, deviceId, total, now = Date.no
|
|
|
3224
3281
|
const suppressed = verdict.suppressedBefore > 0 ? ` suppressedBefore=${verdict.suppressedBefore}` : "";
|
|
3225
3282
|
logger.info(`consumer ${event} kind=${kind} total=${total}${suppressed}`, { ...deviceId !== void 0 ? { tags: { deviceId } } : {} });
|
|
3226
3283
|
}
|
|
3227
|
-
var MB = 1024 * 1024;
|
|
3228
3284
|
function ringBytesOf(s) {
|
|
3229
3285
|
return s.preBufferBytes + s.clipBytes + s.rtpRingBytes + s.keyframeCacheBytes;
|
|
3230
3286
|
}
|
|
@@ -3267,7 +3323,11 @@ function formatBrokerMem(input) {
|
|
|
3267
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(",");
|
|
3268
3324
|
const parts = ["[broker-mem]"];
|
|
3269
3325
|
if (proc.status !== null) parts.push(`rssAnonMb=${(proc.status.rssAnonKb / 1024).toFixed(0)}`, `rssFileMb=${(proc.status.rssFileKb / 1024).toFixed(0)}`);
|
|
3270
|
-
if (proc.maps !== null) parts.push(`heapMapMb=${mb(proc.maps.heapBytes)}`, `anonMaps=${proc.maps.anonMappings}`, `anonMapsMb=${mb(proc.maps.anonBytes)}`);
|
|
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"}`);
|
|
3271
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}`);
|
|
3272
3332
|
return parts.join(" ");
|
|
3273
3333
|
}
|
|
@@ -5087,6 +5147,17 @@ var RtspSession = class {
|
|
|
5087
5147
|
//#endregion
|
|
5088
5148
|
//#region src/stream-broker/rtsp/rtsp-listen-server.ts
|
|
5089
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
|
+
/**
|
|
5090
5161
|
* Single TCP server for all RTSP restream connections.
|
|
5091
5162
|
* Routes clients to the correct RtspRestreamer by a random token path
|
|
5092
5163
|
* (not the predictable brokerId) for security.
|
|
@@ -19933,7 +20004,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19933
20004
|
const sourceBroker = this.brokers.get(sourceBrokerId);
|
|
19934
20005
|
if (!sourceBroker) continue;
|
|
19935
20006
|
const existingToken = this.persistedTokens.get(profileBrokerId);
|
|
19936
|
-
const warmUp = () => sourceBroker.onEncodedData(() => {});
|
|
20007
|
+
const warmUp = () => sourceBroker.onEncodedData(() => {}, RTSP_DESCRIBE_WARMUP_ATTRIBUTION);
|
|
19937
20008
|
const openPrestreamWindow = () => sourceBroker.openPrestreamWindow();
|
|
19938
20009
|
this.rtspServer.registerRestreamer(profileBrokerId, sourceBroker.getRtspRestreamer(), warmUp, existingToken, openPrestreamWindow);
|
|
19939
20010
|
this.profileRestreamerBindings.set(profileBrokerId, sourceBrokerId);
|
|
@@ -21592,7 +21663,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
21592
21663
|
this.brokers.set(brokerId, broker);
|
|
21593
21664
|
this.applyClipRetention(deviceId);
|
|
21594
21665
|
const existingToken = this.persistedTokens.get(brokerId);
|
|
21595
|
-
const warmUp = () => broker.onEncodedData(() => {});
|
|
21666
|
+
const warmUp = () => broker.onEncodedData(() => {}, RTSP_DESCRIBE_WARMUP_ATTRIBUTION);
|
|
21596
21667
|
const openPrestreamWindow = () => broker.openPrestreamWindow();
|
|
21597
21668
|
this.rtspServer.registerRestreamer(brokerId, broker.getRtspRestreamer(), warmUp, existingToken, openPrestreamWindow);
|
|
21598
21669
|
if (!existingToken) this.rtspProvider.persistTokens();
|
|
@@ -33870,23 +33941,37 @@ function createHttpClipStreamOpener(options = {}) {
|
|
|
33870
33941
|
req.end();
|
|
33871
33942
|
});
|
|
33872
33943
|
}
|
|
33873
|
-
//#endregion
|
|
33874
|
-
//#region src/stream-broker/recorded/demux-off-thread.ts
|
|
33875
33944
|
/**
|
|
33876
|
-
*
|
|
33877
|
-
*
|
|
33878
|
-
*
|
|
33879
|
-
*
|
|
33880
|
-
*
|
|
33881
|
-
*
|
|
33882
|
-
*
|
|
33883
|
-
*
|
|
33884
|
-
*
|
|
33885
|
-
*
|
|
33886
|
-
*
|
|
33887
|
-
*
|
|
33888
|
-
*
|
|
33889
|
-
|
|
33945
|
+
* Heap shaping for the demux isolate. The runner's process-wide V8 flags
|
|
33946
|
+
* (`--initial-old-space-size=512`, `--max-semi-space-size=8`, from the
|
|
33947
|
+
* stream-broker manifest) are sized for the MAIN isolate and leak into every
|
|
33948
|
+
* Worker: with a 512 MB initial old space the worker grew to 144 MB of
|
|
33949
|
+
* garbage without a single major GC. Explicit `resourceLimits` replace the
|
|
33950
|
+
* inherited sizing (measured under those flags with the same allocation
|
|
33951
|
+
* churn: worker heap 474 MB without limits, 88 MB with them; see
|
|
33952
|
+
* docs/history/sessions.md).
|
|
33953
|
+
*
|
|
33954
|
+
* 128 MB old generation: parsing a recorded fragment retains about 1 MB
|
|
33955
|
+
* after GC for an 86 KB input (fixture bench), so even a 10 MB fragment (a
|
|
33956
|
+
* long 4K segment) holds well under 30 MB live; 128 MB is more than 4x that.
|
|
33957
|
+
* A parse that still exceeds it fails the worker cleanly
|
|
33958
|
+
* (`ERR_WORKER_OUT_OF_MEMORY`): in-flight parses resolve null (ffmpeg
|
|
33959
|
+
* fallback) and the next parse spawns a fresh worker.
|
|
33960
|
+
*/
|
|
33961
|
+
var DEMUX_WORKER_RESOURCE_LIMITS = {
|
|
33962
|
+
maxOldGenerationSizeMb: 128,
|
|
33963
|
+
maxYoungGenerationSizeMb: 16
|
|
33964
|
+
};
|
|
33965
|
+
var defaultTimers = {
|
|
33966
|
+
set: (fn, ms) => {
|
|
33967
|
+
const t = setTimeout(fn, ms);
|
|
33968
|
+
t.unref();
|
|
33969
|
+
return t;
|
|
33970
|
+
},
|
|
33971
|
+
clear: (handle) => {
|
|
33972
|
+
if (handle !== void 0) clearTimeout(handle);
|
|
33973
|
+
}
|
|
33974
|
+
};
|
|
33890
33975
|
function isCodec(value) {
|
|
33891
33976
|
return value === "H264" || value === "H265";
|
|
33892
33977
|
}
|
|
@@ -33921,55 +34006,93 @@ function parseWorkerMessage(message) {
|
|
|
33921
34006
|
function defaultWorkerFilename() {
|
|
33922
34007
|
return (0, node_url.fileURLToPath)(new URL("./demux-worker-child.js", require("url").pathToFileURL(__filename).href));
|
|
33923
34008
|
}
|
|
33924
|
-
/**
|
|
33925
|
-
|
|
33926
|
-
* callers are matched by request id; the worker's own event loop serializes
|
|
33927
|
-
* mp4box. Live RTP on the parent thread is not waiting on box parsing.
|
|
33928
|
-
*/
|
|
33929
|
-
function createOffThreadJsParse(deps) {
|
|
34009
|
+
/** {@link createOffThreadJsParse} plus the heap accessor the memory probe reads. */
|
|
34010
|
+
function createOffThreadDemuxClient(deps) {
|
|
33930
34011
|
const Ctor = deps.WorkerCtor;
|
|
33931
|
-
|
|
34012
|
+
const idleMs = deps.idleMs ?? 6e4;
|
|
34013
|
+
const timers = deps.timers ?? defaultTimers;
|
|
34014
|
+
const now = deps.now ?? Date.now;
|
|
34015
|
+
let current = null;
|
|
33932
34016
|
let nextId = 1;
|
|
33933
|
-
const
|
|
33934
|
-
|
|
33935
|
-
|
|
33936
|
-
|
|
33937
|
-
|
|
34017
|
+
const failAll = (slot, why) => {
|
|
34018
|
+
const waiting = [...slot.pending.values()];
|
|
34019
|
+
slot.pending.clear();
|
|
34020
|
+
if (waiting.length > 0 && deps.logger) for (const deviceId of new Set(waiting.map((p) => p.deviceId))) deps.logger.warn(`demux worker lost with parses in flight (${why}) — ffmpeg fallback`, {
|
|
34021
|
+
...deviceId !== void 0 ? { tags: { deviceId } } : {},
|
|
34022
|
+
meta: { inFlight: waiting.length }
|
|
34023
|
+
});
|
|
34024
|
+
for (const p of waiting) p.resolve(null);
|
|
34025
|
+
};
|
|
34026
|
+
const retire = (slot) => {
|
|
34027
|
+
timers.clear(slot.idleTimer);
|
|
34028
|
+
slot.idleTimer = void 0;
|
|
34029
|
+
if (current === slot) current = null;
|
|
34030
|
+
};
|
|
34031
|
+
const onIdle = async (slot) => {
|
|
34032
|
+
slot.idleTimer = void 0;
|
|
34033
|
+
if (current !== slot || slot.pending.size > 0) return;
|
|
34034
|
+
current = null;
|
|
34035
|
+
const heapBytes = (await slot.worker.getHeapStatistics?.().catch(() => null))?.total_heap_size ?? slot.lastHeapBytes;
|
|
34036
|
+
deps.logger?.info("demux worker idle — terminating", { meta: {
|
|
34037
|
+
ageMs: Math.max(0, now() - slot.spawnedAt),
|
|
34038
|
+
idleMs,
|
|
34039
|
+
lastHeapMb: Math.round(heapBytes / 1048576)
|
|
34040
|
+
} });
|
|
34041
|
+
slot.worker.terminate();
|
|
34042
|
+
};
|
|
34043
|
+
const armIdle = (slot) => {
|
|
34044
|
+
timers.clear(slot.idleTimer);
|
|
34045
|
+
slot.idleTimer = timers.set(() => void onIdle(slot), idleMs);
|
|
33938
34046
|
};
|
|
33939
|
-
const attach = (
|
|
34047
|
+
const attach = (slot) => {
|
|
34048
|
+
const w = slot.worker;
|
|
33940
34049
|
w.on("message", (message) => {
|
|
33941
34050
|
const parsed = parseWorkerMessage(message);
|
|
33942
34051
|
if (!parsed) return;
|
|
33943
|
-
const
|
|
33944
|
-
if (!
|
|
33945
|
-
pending.delete(parsed.id);
|
|
33946
|
-
resolve(parsed.aus);
|
|
34052
|
+
const pending = slot.pending.get(parsed.id);
|
|
34053
|
+
if (!pending) return;
|
|
34054
|
+
slot.pending.delete(parsed.id);
|
|
34055
|
+
pending.resolve(parsed.aus);
|
|
34056
|
+
if (slot.pending.size === 0 && current === slot) armIdle(slot);
|
|
33947
34057
|
});
|
|
33948
|
-
w.on("error", () => {
|
|
33949
|
-
|
|
33950
|
-
failAll();
|
|
34058
|
+
w.on("error", (err) => {
|
|
34059
|
+
retire(slot);
|
|
34060
|
+
failAll(slot, String(err));
|
|
33951
34061
|
});
|
|
33952
34062
|
w.on("exit", () => {
|
|
33953
|
-
|
|
33954
|
-
failAll();
|
|
34063
|
+
retire(slot);
|
|
34064
|
+
failAll(slot, "exit");
|
|
33955
34065
|
});
|
|
33956
34066
|
};
|
|
33957
34067
|
const ensureWorker = () => {
|
|
33958
|
-
if (
|
|
34068
|
+
if (current) return current;
|
|
33959
34069
|
if (!Ctor) return null;
|
|
33960
34070
|
try {
|
|
33961
|
-
const
|
|
33962
|
-
|
|
33963
|
-
|
|
33964
|
-
|
|
33965
|
-
|
|
34071
|
+
const slot = {
|
|
34072
|
+
worker: new Ctor(deps.workerFilename, { resourceLimits: DEMUX_WORKER_RESOURCE_LIMITS }),
|
|
34073
|
+
pending: /* @__PURE__ */ new Map(),
|
|
34074
|
+
spawnedAt: now(),
|
|
34075
|
+
idleTimer: void 0,
|
|
34076
|
+
lastHeapBytes: 0
|
|
34077
|
+
};
|
|
34078
|
+
attach(slot);
|
|
34079
|
+
current = slot;
|
|
34080
|
+
deps.logger?.info("demux worker spawned", { meta: {
|
|
34081
|
+
resourceLimits: DEMUX_WORKER_RESOURCE_LIMITS,
|
|
34082
|
+
idleMs
|
|
34083
|
+
} });
|
|
34084
|
+
return slot;
|
|
34085
|
+
} catch (err) {
|
|
34086
|
+
deps.logger?.warn(`demux worker spawn failed: ${String(err)} — parsing in-process`);
|
|
33966
34087
|
return null;
|
|
33967
34088
|
}
|
|
33968
34089
|
};
|
|
33969
|
-
|
|
34090
|
+
const parse = async (bytes, ctx) => {
|
|
33970
34091
|
if (!deps.existsSync(deps.workerFilename)) return deps.parseInProcess(bytes);
|
|
33971
|
-
const
|
|
33972
|
-
if (!
|
|
34092
|
+
const slot = ensureWorker();
|
|
34093
|
+
if (!slot) return deps.parseInProcess(bytes);
|
|
34094
|
+
timers.clear(slot.idleTimer);
|
|
34095
|
+
slot.idleTimer = void 0;
|
|
33973
34096
|
const id = nextId;
|
|
33974
34097
|
nextId += 1;
|
|
33975
34098
|
const copy = new Uint8Array(bytes);
|
|
@@ -33978,25 +34101,73 @@ function createOffThreadJsParse(deps) {
|
|
|
33978
34101
|
bytes: copy
|
|
33979
34102
|
};
|
|
33980
34103
|
return new Promise((resolve) => {
|
|
33981
|
-
pending.set(id,
|
|
33982
|
-
|
|
34104
|
+
slot.pending.set(id, {
|
|
34105
|
+
resolve,
|
|
34106
|
+
deviceId: ctx?.deviceId
|
|
34107
|
+
});
|
|
34108
|
+
slot.worker.postMessage(request, [copy.buffer]);
|
|
33983
34109
|
});
|
|
33984
34110
|
};
|
|
34111
|
+
const readHeaps = async (at = Date.now()) => {
|
|
34112
|
+
const live = current;
|
|
34113
|
+
const empty = {
|
|
34114
|
+
workers: 0,
|
|
34115
|
+
heapBytes: 0,
|
|
34116
|
+
physicalBytes: 0,
|
|
34117
|
+
usedBytes: 0,
|
|
34118
|
+
maxAgeMs: 0,
|
|
34119
|
+
idleTerminates: true
|
|
34120
|
+
};
|
|
34121
|
+
if (live === null) return empty;
|
|
34122
|
+
const stats = await live.worker.getHeapStatistics?.().catch(() => null);
|
|
34123
|
+
if (stats) live.lastHeapBytes = stats.total_heap_size;
|
|
34124
|
+
return {
|
|
34125
|
+
...empty,
|
|
34126
|
+
workers: 1,
|
|
34127
|
+
maxAgeMs: Math.max(0, at - live.spawnedAt),
|
|
34128
|
+
...stats ? {
|
|
34129
|
+
heapBytes: stats.total_heap_size,
|
|
34130
|
+
physicalBytes: stats.total_physical_size,
|
|
34131
|
+
usedBytes: stats.used_heap_size
|
|
34132
|
+
} : {}
|
|
34133
|
+
};
|
|
34134
|
+
};
|
|
34135
|
+
return {
|
|
34136
|
+
parse,
|
|
34137
|
+
readHeaps
|
|
34138
|
+
};
|
|
33985
34139
|
}
|
|
33986
|
-
function
|
|
33987
|
-
return
|
|
34140
|
+
function defaultOffThreadClient() {
|
|
34141
|
+
return createOffThreadDemuxClient({
|
|
33988
34142
|
workerFilename: defaultWorkerFilename(),
|
|
33989
34143
|
existsSync: node_fs.existsSync,
|
|
33990
34144
|
parseInProcess: require_segment_demux_js.demuxSegmentFullJs,
|
|
33991
|
-
WorkerCtor: node_worker_threads.Worker
|
|
34145
|
+
WorkerCtor: node_worker_threads.Worker,
|
|
34146
|
+
...sharedLogger ? { logger: sharedLogger } : {}
|
|
33992
34147
|
});
|
|
33993
34148
|
}
|
|
33994
34149
|
var shared;
|
|
33995
34150
|
/** Process-wide JS parse: one Worker, shared by every WebRTC session. */
|
|
33996
|
-
var offThreadJsParse = (bytes) => {
|
|
33997
|
-
shared ??=
|
|
33998
|
-
return shared(bytes);
|
|
34151
|
+
var offThreadJsParse = (bytes, ctx) => {
|
|
34152
|
+
shared ??= defaultOffThreadClient();
|
|
34153
|
+
return shared.parse(bytes, ctx);
|
|
33999
34154
|
};
|
|
34155
|
+
var sharedLogger;
|
|
34156
|
+
/** Give the process-wide client a logger; call before the first parse. */
|
|
34157
|
+
function setDemuxWorkerLogger(logger) {
|
|
34158
|
+
sharedLogger = logger;
|
|
34159
|
+
}
|
|
34160
|
+
/** Heap of the shared demux worker, for the `[broker-mem]` probe (tick only). */
|
|
34161
|
+
async function readDemuxWorkerHeaps(now) {
|
|
34162
|
+
return shared ? shared.readHeaps(now) : {
|
|
34163
|
+
workers: 0,
|
|
34164
|
+
heapBytes: 0,
|
|
34165
|
+
physicalBytes: 0,
|
|
34166
|
+
usedBytes: 0,
|
|
34167
|
+
maxAgeMs: 0,
|
|
34168
|
+
idleTerminates: true
|
|
34169
|
+
};
|
|
34170
|
+
}
|
|
34000
34171
|
/**
|
|
34001
34172
|
* Drop-in for the session `demuxVideo` hook: JS parse off the live loop,
|
|
34002
34173
|
* ffmpeg subprocess fallback unchanged.
|
|
@@ -34897,6 +35068,7 @@ var BrokerWebrtcServer = class {
|
|
|
34897
35068
|
stopped = false;
|
|
34898
35069
|
constructor(options) {
|
|
34899
35070
|
this.logger = options.logger;
|
|
35071
|
+
setDemuxWorkerLogger(options.logger.child("demux-worker"));
|
|
34900
35072
|
this.getIceServersFn = options.getIceServers;
|
|
34901
35073
|
this.staticIceServers = options.iceServers;
|
|
34902
35074
|
this.icePortRange = options.icePortRange;
|
|
@@ -35562,6 +35734,7 @@ var BrokerWebrtcServer = class {
|
|
|
35562
35734
|
if (locateRecorded && readRecorded) {
|
|
35563
35735
|
const demuxVideo = (b) => demuxSegmentPreferJsOffThread(b, {
|
|
35564
35736
|
ffmpegPath: resolveFfmpeg(),
|
|
35737
|
+
deviceId,
|
|
35565
35738
|
onDemuxPath: (outcome) => {
|
|
35566
35739
|
const report = demuxTally.record(outcome, Date.now());
|
|
35567
35740
|
if (!report) return;
|
|
@@ -37494,7 +37667,7 @@ async function readProc() {
|
|
|
37494
37667
|
}
|
|
37495
37668
|
async function emitOnce(deps) {
|
|
37496
37669
|
const now = Date.now();
|
|
37497
|
-
const proc = await readProc();
|
|
37670
|
+
const [proc, worker] = await Promise.all([readProc(), readDemuxWorkerHeaps(now)]);
|
|
37498
37671
|
const resources = process.getActiveResourcesInfo();
|
|
37499
37672
|
const line = formatBrokerMem({
|
|
37500
37673
|
samples: deps.manager.getBrokerInstances().map((b) => b.getMemSample(now)),
|
|
@@ -37503,7 +37676,9 @@ async function emitOnce(deps) {
|
|
|
37503
37676
|
childProcesses: countActiveResources(resources, "ProcessWrap"),
|
|
37504
37677
|
proc,
|
|
37505
37678
|
churn: consumerChurn.drain(),
|
|
37506
|
-
copied: readCopyOwnedTotals()
|
|
37679
|
+
copied: readCopyOwnedTotals(),
|
|
37680
|
+
v8: readV8Heap(),
|
|
37681
|
+
worker
|
|
37507
37682
|
});
|
|
37508
37683
|
deps.logger.info(line);
|
|
37509
37684
|
}
|