@camstack/addon-pipeline 1.2.268 → 1.2.269
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 +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-CKNqjjRy.js → default-detection-model-BfMsW_XK.js} +1 -1
- package/dist/{default-detection-model--x95ODoz.mjs → default-detection-model-pXamampo.mjs} +1 -1
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-C7CvyImc.js → dist-CkNVb7Zh.js} +187 -11
- package/dist/{dist-Dm1V0nJX.mjs → dist-Cqf-U6EN.mjs} +187 -11
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-CjN6Vze9.js → node-CqC8R84-.js} +116 -2
- package/dist/{node-B5haKIVa.mjs → node-jLmcbaar.mjs} +111 -3
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-BMCVwJcT.mjs → process-memory-B1YHg1_g.mjs} +1 -1
- package/dist/{process-memory-CaFOThD9.js → process-memory-D7LlWXty.js} +1 -1
- package/dist/recorder/index.js +2 -2
- package/dist/recorder/index.mjs +2 -2
- package/dist/{segment-demux-js-BaWzAeJu.mjs → segment-demux-js-B2TLcR_g.mjs} +1 -1
- package/dist/{segment-demux-js-bfaGjVqq.js → segment-demux-js-Bu-_Q7Um.js} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-6gUUOaeo.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CbUvu4Jn.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DJVznXRG.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-CeR_IxxU.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-DBu-k8Ps.mjs → hostInit-9ZN1ghaZ.mjs} +2 -2
- package/dist/stream-broker/index.js +397 -60
- package/dist/stream-broker/index.mjs +397 -60
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/python/inference_pool.py +84 -2
- package/python/test_inference_pool_ov_threads.py +112 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B5hze8qG.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-DTzfOQZq.mjs +0 -26
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { r as __toESM } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { A as RingBuffer, B as declareLogChannel, Bt as buildFfmpegArgs$1, E as RATE_CONTROL_TIGHT, Et as resolveEgressDecodeHwAccel, Gt as CAM_PROFILE_ORDER, H as deriveBatteryPresence, Ht as isSoftwareDecode, I as batteryCapability, It as webrtcSessionCapability, K as egressTranscodeSharingKey, Kt as DeviceFeature, L as cameraStreamsCapability, Lt as errMsg, M as addonWidgetsSourceCapability, Nt as streamBrokerCapability, Qt as expandAudioChunkToF32le, R as createHwAccelCache, Rt as AUDIO_PRESETS, T as RATE_CONTROL_RELAXED, Ut as BOOT_RECOVERY_BACKOFF_MS, Vt as invocationFromEncodeProfile, Wt as BaseAddon, Yt as createEvent, _n as record, an as parseProfileBrokerId, at as logChannelsCapability, bn as EventCategory, cn as _enum, dn as discriminatedUnion, en as isEvent, h as EncodeProfileSchema, hn as object, i as BatteryStatusSchema, it as loadContributionCapability, ln as array, mn as number, nn as makeSourceBrokerId, pn as literal, q as egressTransportFromRequest, qt as DeviceType, rn as nodePin, st as maskUrlCredentials, tn as makeProfileBrokerId, un as boolean, v as G711_SCALE_CORRECTION_DB, vn as string, xt as rankStreams, yn as union, z as createLogChannelsProvider } from "../dist-
|
|
3
|
-
import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-
|
|
2
|
+
import { A as RingBuffer, B as declareLogChannel, Bt as buildFfmpegArgs$1, E as RATE_CONTROL_TIGHT, Et as resolveEgressDecodeHwAccel, Gt as CAM_PROFILE_ORDER, H as deriveBatteryPresence, Ht as isSoftwareDecode, I as batteryCapability, It as webrtcSessionCapability, K as egressTranscodeSharingKey, Kt as DeviceFeature, L as cameraStreamsCapability, Lt as errMsg, M as addonWidgetsSourceCapability, Nt as streamBrokerCapability, Qt as expandAudioChunkToF32le, R as createHwAccelCache, Rt as AUDIO_PRESETS, T as RATE_CONTROL_RELAXED, Ut as BOOT_RECOVERY_BACKOFF_MS, Vt as invocationFromEncodeProfile, Wt as BaseAddon, Yt as createEvent, _n as record, an as parseProfileBrokerId, at as logChannelsCapability, bn as EventCategory, cn as _enum, dn as discriminatedUnion, en as isEvent, h as EncodeProfileSchema, hn as object, i as BatteryStatusSchema, it as loadContributionCapability, ln as array, mn as number, nn as makeSourceBrokerId, pn as literal, q as egressTransportFromRequest, qt as DeviceType, rn as nodePin, st as maskUrlCredentials, tn as makeProfileBrokerId, un as boolean, v as G711_SCALE_CORRECTION_DB, vn as string, xt as rankStreams, yn as union, z as createLogChannelsProvider } from "../dist-Cqf-U6EN.mjs";
|
|
3
|
+
import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-jLmcbaar.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 { i as require_mp4box_all, n as demuxSegmentPreferJs, r as runFfmpegOnce, t as demuxSegmentFullJs } from "../segment-demux-js-
|
|
7
|
+
import { i as require_mp4box_all, n as demuxSegmentPreferJs, r as runFfmpegOnce, t as demuxSegmentFullJs } from "../segment-demux-js-B2TLcR_g.mjs";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import * as os from "node:os";
|
|
10
10
|
import { networkInterfaces, tmpdir } from "node:os";
|
|
@@ -491,6 +491,8 @@ function logBannerArgs(level) {
|
|
|
491
491
|
}
|
|
492
492
|
//#endregion
|
|
493
493
|
//#region src/shared/ffmpeg-decode-args.ts
|
|
494
|
+
/** Default DRM render node used for the VA-API / QSV hardware decode path. */
|
|
495
|
+
var DEFAULT_VAAPI_RENDER_DEVICE = "/dev/dri/renderD128";
|
|
494
496
|
/**
|
|
495
497
|
* Canonical decode-hwaccel preference order for the ffmpeg-subprocess decoder.
|
|
496
498
|
*
|
|
@@ -5277,6 +5279,26 @@ function decideAudioPlaneAction(observation, state, nowMs) {
|
|
|
5277
5279
|
* "Device creation failed: -12 … Cannot allocate memory". Every egress child
|
|
5278
5280
|
* therefore spends ~3 s dying before its own software retry rescues it.
|
|
5279
5281
|
*
|
|
5282
|
+
* ## CORRECTION (2026-09-18): that `-12` was not a resource condition
|
|
5283
|
+
*
|
|
5284
|
+
* The two runners were not competing for a device — they were running
|
|
5285
|
+
* DIFFERENT BINARIES. The decoder addon reached a capable ffmpeg; the broker
|
|
5286
|
+
* reached the downloaded static build, which has no VAAPI compiled in at all
|
|
5287
|
+
* (`hwaccels: vdpau`). `-12` is what fftools prints when
|
|
5288
|
+
* `av_hwdevice_ctx_alloc` returns NULL because the type is not in the build:
|
|
5289
|
+
* the NAME parses, so the request looks valid and the device simply cannot
|
|
5290
|
+
* exist. It READS as a shortage and is a property of the binary (D536).
|
|
5291
|
+
*
|
|
5292
|
+
* That is why it "recurred forever on a hub where it has never once
|
|
5293
|
+
* succeeded": there was nothing to recover. The fix is upstream of this file —
|
|
5294
|
+
* `TranscodePipelineManager` now asks the binary (`ffmpeg -hwaccels`) and
|
|
5295
|
+
* refuses a backend it does not carry, so this quarantine never sees that
|
|
5296
|
+
* class of failure again.
|
|
5297
|
+
*
|
|
5298
|
+
* Everything below still stands for what remains: a backend the binary HAS and
|
|
5299
|
+
* the device still refuses. That one is a genuine runtime condition and can
|
|
5300
|
+
* clear, which is exactly why this is a quarantine and not a tombstone.
|
|
5301
|
+
*
|
|
5280
5302
|
* That was already reported — `EgressTranscodeManager.onDecodeHwAccelFailed`
|
|
5281
5303
|
* poisoned the broker's 60 s `egressHwAccelCache`. The memo is the wrong shape
|
|
5282
5304
|
* for the verdict: it exists to avoid a per-session cross-process
|
|
@@ -5288,10 +5310,11 @@ function decideAudioPlaneAction(observation, state, nowMs) {
|
|
|
5288
5310
|
*
|
|
5289
5311
|
* ## Why this is a quarantine and not a tombstone
|
|
5290
5312
|
*
|
|
5291
|
-
* A permanent verdict is wrong for the failure
|
|
5292
|
-
*
|
|
5293
|
-
* can clear (the operator gives the container more of
|
|
5294
|
-
* process releases it).
|
|
5313
|
+
* A permanent verdict is wrong for the failure this file is now left with:
|
|
5314
|
+
* a device that exists in the build and still refuses is a RESOURCE condition,
|
|
5315
|
+
* and a resource condition can clear (the operator gives the container more of
|
|
5316
|
+
* the device, another process releases it). The build-property case — the one
|
|
5317
|
+
* that produced the measurements above — no longer reaches here at all. A process that decided "never again" at 03:00 would
|
|
5295
5318
|
* still be decoding in software at noon with a working accelerator, and nothing
|
|
5296
5319
|
* would ever say so.
|
|
5297
5320
|
*
|
|
@@ -5429,36 +5452,54 @@ function classifyDecodeHwAccel(setting) {
|
|
|
5429
5452
|
function toAudioSidecarCodec(audio) {
|
|
5430
5453
|
return audio === "aac" || audio === "opus" || audio === "pcmu" ? audio : null;
|
|
5431
5454
|
}
|
|
5432
|
-
var KNOWN_ENCODERS = new Set([
|
|
5433
|
-
"libx264",
|
|
5434
|
-
"libx265",
|
|
5435
|
-
"h264_videotoolbox",
|
|
5436
|
-
"hevc_videotoolbox",
|
|
5437
|
-
"h264_vaapi",
|
|
5438
|
-
"hevc_vaapi",
|
|
5439
|
-
"h264_qsv",
|
|
5440
|
-
"hevc_qsv",
|
|
5441
|
-
"h264_nvenc",
|
|
5442
|
-
"hevc_nvenc",
|
|
5443
|
-
"h264_amf",
|
|
5444
|
-
"hevc_amf"
|
|
5445
|
-
]);
|
|
5446
|
-
/** Type guard — an encoder id the shared builder knows how to emit. */
|
|
5447
|
-
function isKnownEncoder(value) {
|
|
5448
|
-
return KNOWN_ENCODERS.has(value);
|
|
5449
|
-
}
|
|
5450
5455
|
/**
|
|
5451
|
-
*
|
|
5452
|
-
*
|
|
5453
|
-
*
|
|
5454
|
-
*
|
|
5455
|
-
*
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5456
|
+
* Choose the video encoder — separately from the DECODE preference, which is
|
|
5457
|
+
* the whole point.
|
|
5458
|
+
*
|
|
5459
|
+
* {@link pickVideoEncoder} folded the two together: `hwAccel: 'copy'` meant
|
|
5460
|
+
* "software everything". But `copy` is this fleet's decode DEFAULT for a real
|
|
5461
|
+
* reason — hardware decode hard-fails on a fragile HEVC source — and it was
|
|
5462
|
+
* quietly deciding the encoder as well. On a hub with a working Intel iGPU
|
|
5463
|
+
* that put every egress and derived transcode on `libx264`, spending cores
|
|
5464
|
+
* this box does not have, while the GPU idled. A software decode can feed a
|
|
5465
|
+
* hardware encoder (`hwupload` is exactly that), and the encode is where the
|
|
5466
|
+
* CPU goes.
|
|
5467
|
+
*
|
|
5468
|
+
* Three things can send it back to the CPU, and each is NAMED so the caller
|
|
5469
|
+
* can say which — a silent software transcode is the thing this exists to end:
|
|
5470
|
+
*
|
|
5471
|
+
* - `operator-forced-software` — someone asked for `none`. Honoured.
|
|
5472
|
+
* - `no-hardware-on-node` — the hwaccel resolver offers nothing.
|
|
5473
|
+
* - `binary-lacks-encoder` — the node has the hardware and THIS ffmpeg cannot
|
|
5474
|
+
* use it. That is a build property, not a resource condition (D536).
|
|
5475
|
+
*
|
|
5476
|
+
* An UNKNOWN encoder list (`null`, a probe that failed) is not a "no": we
|
|
5477
|
+
* could not ask, so the hardware encoder is still attempted and ffmpeg's own
|
|
5478
|
+
* failure is the answer. Reading "unknown" as "unsupported" would keep a
|
|
5479
|
+
* capable node in software forever on one bad probe.
|
|
5480
|
+
*/
|
|
5481
|
+
function chooseVideoEncoder(input) {
|
|
5482
|
+
const software = input.target === "H264" ? "libx264" : "libx265";
|
|
5483
|
+
const fallback = (reason) => ({
|
|
5484
|
+
encoder: software,
|
|
5485
|
+
hwEncodeDevice: null,
|
|
5486
|
+
softwareReason: reason
|
|
5487
|
+
});
|
|
5488
|
+
if (input.hwAccelSetting === "none") return fallback("operator-forced-software");
|
|
5489
|
+
const backend = input.backend === null ? null : input.backend.toLowerCase();
|
|
5490
|
+
if (backend === null) return fallback("no-hardware-on-node");
|
|
5491
|
+
const ids = ENCODER_IDS_BY_BACKEND[backend];
|
|
5492
|
+
if (!ids) return fallback("no-hardware-on-node");
|
|
5493
|
+
const encoder = input.target === "H264" ? ids.h264 : ids.h265;
|
|
5494
|
+
if (input.binaryEncoders !== null && !input.binaryEncoders.has(encoder)) return fallback("binary-lacks-encoder");
|
|
5495
|
+
return {
|
|
5496
|
+
encoder,
|
|
5497
|
+
hwEncodeDevice: backend === "vaapi" ? {
|
|
5498
|
+
kind: "vaapi",
|
|
5499
|
+
renderNode: input.renderNode
|
|
5500
|
+
} : null,
|
|
5501
|
+
softwareReason: null
|
|
5502
|
+
};
|
|
5462
5503
|
}
|
|
5463
5504
|
/**
|
|
5464
5505
|
* The h264/hevc hardware ENCODER ids for a decode-hwaccel backend name — a
|
|
@@ -5491,24 +5532,6 @@ var ENCODER_IDS_BY_BACKEND = {
|
|
|
5491
5532
|
}
|
|
5492
5533
|
};
|
|
5493
5534
|
/**
|
|
5494
|
-
* Build the {@link HardwareEncoders} default pair for THIS node's decode
|
|
5495
|
-
* backend, resolved LOCALLY from `ctx.kernel.hwaccel` — WITHOUT probing
|
|
5496
|
-
* `ffmpeg -encoders` through the `platform-probe` singleton, whose per-node
|
|
5497
|
-
* `nodePin` does not survive a forked agent child's link chain (it answers with
|
|
5498
|
-
* the HUB's encoders). Returns `null` for software / an unmapped backend.
|
|
5499
|
-
*/
|
|
5500
|
-
function localHardwareEncoders(backend) {
|
|
5501
|
-
if (backend === null) return null;
|
|
5502
|
-
const ids = ENCODER_IDS_BY_BACKEND[backend.toLowerCase()];
|
|
5503
|
-
if (!ids) return null;
|
|
5504
|
-
return {
|
|
5505
|
-
encoders: [],
|
|
5506
|
-
defaultH264: ids.h264,
|
|
5507
|
-
defaultH265: ids.h265,
|
|
5508
|
-
probedAt: Date.now()
|
|
5509
|
-
};
|
|
5510
|
-
}
|
|
5511
|
-
/**
|
|
5512
5535
|
* Translate the broker's transcode request into the shared
|
|
5513
5536
|
* {@link FfmpegInvocation}. Emits no arguments itself — `buildFfmpegArgs` from
|
|
5514
5537
|
* `@camstack/types` does that, and it is the only thing that may.
|
|
@@ -5522,6 +5545,7 @@ function brokerTranscodeInvocation(inv) {
|
|
|
5522
5545
|
return {
|
|
5523
5546
|
logLevel: "warning",
|
|
5524
5547
|
decodeHwAccel: inv.decodeHwAccel,
|
|
5548
|
+
...inv.hwEncodeDevice ? { hwEncodeDevice: inv.hwEncodeDevice } : {},
|
|
5525
5549
|
input: {
|
|
5526
5550
|
url: inv.sourceUrl,
|
|
5527
5551
|
rtspTransport: "tcp"
|
|
@@ -16276,6 +16300,9 @@ var TranscodePipelineManager = class {
|
|
|
16276
16300
|
/** In-flight transcode starts, keyed by pipeline key — single-flight the
|
|
16277
16301
|
* async egress start so concurrent acquires share one egress. */
|
|
16278
16302
|
pending = /* @__PURE__ */ new Map();
|
|
16303
|
+
/** What the ffmpeg at a path can do, asked once. `null` = could not ask. */
|
|
16304
|
+
binaryCapabilities = /* @__PURE__ */ new Map();
|
|
16305
|
+
probeFfmpeg;
|
|
16279
16306
|
logger;
|
|
16280
16307
|
hwaccel;
|
|
16281
16308
|
cameraStreamLookup;
|
|
@@ -16295,6 +16322,7 @@ var TranscodePipelineManager = class {
|
|
|
16295
16322
|
this.resolveProfileSource = opts.resolveProfileSource ?? null;
|
|
16296
16323
|
this.releaseGraceMs = opts.releaseGraceMs ?? 5e3;
|
|
16297
16324
|
this.ffmpegConfig = opts.ffmpegConfig ?? (() => DEFAULT_FFMPEG_CONFIG);
|
|
16325
|
+
this.probeFfmpeg = opts.probeFfmpeg ?? probeFfmpegBinary;
|
|
16298
16326
|
this.resolveStreamHwAccel = opts.resolveStreamHwAccel ?? null;
|
|
16299
16327
|
this.spawnFn = opts.spawnFn ?? spawn;
|
|
16300
16328
|
this.acquireChannel = opts.acquireChannel ?? null;
|
|
@@ -16367,8 +16395,33 @@ var TranscodePipelineManager = class {
|
|
|
16367
16395
|
if (!this.acquireChannel) throw new Error("getStreamWithCodec: transcode egress not wired (acquireChannel missing)");
|
|
16368
16396
|
const cfg = this.ffmpegConfig();
|
|
16369
16397
|
const effectiveHwAccel = this.effectiveHwAccelSetting(opts.deviceId, opts.camStreamId, cfg.hwAccel);
|
|
16370
|
-
const
|
|
16371
|
-
|
|
16398
|
+
const choice = chooseVideoEncoder({
|
|
16399
|
+
target: opts.targetCodec,
|
|
16400
|
+
backend: await this.resolveHardwareBackend(),
|
|
16401
|
+
binaryEncoders: await this.resolveBinaryEncoders(cfg.binaryPath),
|
|
16402
|
+
hwAccelSetting: effectiveHwAccel,
|
|
16403
|
+
renderNode: DEFAULT_VAAPI_RENDER_DEVICE
|
|
16404
|
+
});
|
|
16405
|
+
const encoder = choice.encoder;
|
|
16406
|
+
if (choice.softwareReason !== null) this.logger.error("transcode is running on the CPU — a software encode is work the hardware was supposed to do", {
|
|
16407
|
+
tags: { deviceId: opts.deviceId },
|
|
16408
|
+
meta: {
|
|
16409
|
+
reason: choice.softwareReason,
|
|
16410
|
+
encoder,
|
|
16411
|
+
camStreamId: opts.camStreamId,
|
|
16412
|
+
targetCodec: opts.targetCodec,
|
|
16413
|
+
hwAccelSetting: effectiveHwAccel,
|
|
16414
|
+
ffmpeg: cfg.binaryPath
|
|
16415
|
+
}
|
|
16416
|
+
});
|
|
16417
|
+
else this.logger.info("transcode encodes on the GPU", {
|
|
16418
|
+
tags: { deviceId: opts.deviceId },
|
|
16419
|
+
meta: {
|
|
16420
|
+
encoder,
|
|
16421
|
+
camStreamId: opts.camStreamId,
|
|
16422
|
+
device: choice.hwEncodeDevice?.renderNode
|
|
16423
|
+
}
|
|
16424
|
+
});
|
|
16372
16425
|
const decodeHwAccel = await this.resolveDecodeHwAccel(effectiveHwAccel);
|
|
16373
16426
|
const sourceUrl = this.resolveSourceUrl(opts.deviceId, opts.camStreamId);
|
|
16374
16427
|
const brokerId = `transcode:${opts.deviceId}:${randomUUID().slice(0, 8)}`;
|
|
@@ -16385,6 +16438,7 @@ var TranscodePipelineManager = class {
|
|
|
16385
16438
|
sourceUrl,
|
|
16386
16439
|
targetCodec: opts.targetCodec,
|
|
16387
16440
|
encoder,
|
|
16441
|
+
hwEncodeDevice: choice.hwEncodeDevice,
|
|
16388
16442
|
decodeHwAccel,
|
|
16389
16443
|
scale: opts.scale,
|
|
16390
16444
|
audio: opts.audio,
|
|
@@ -16515,16 +16569,59 @@ var TranscodePipelineManager = class {
|
|
|
16515
16569
|
* ranked above qsv (qsv is broken on the Intel stack) so an Intel node
|
|
16516
16570
|
* transcodes with `h264_vaapi`.
|
|
16517
16571
|
*/
|
|
16518
|
-
|
|
16572
|
+
/**
|
|
16573
|
+
* The node's hardware backend name (`vaapi`, `qsv`, `videotoolbox`, …), or
|
|
16574
|
+
* `null`. Resolved LOCALLY from `ctx.kernel.hwaccel` for the reason the
|
|
16575
|
+
* sibling method's docblock gives: the `platform-probe` singleton answers
|
|
16576
|
+
* with the HUB's hardware on a forked agent child.
|
|
16577
|
+
*/
|
|
16578
|
+
async resolveHardwareBackend() {
|
|
16519
16579
|
if (!this.hwaccel) return null;
|
|
16520
16580
|
try {
|
|
16521
|
-
return
|
|
16581
|
+
return rankDecodeHwAccels((await this.hwaccel.resolve(null)).preferred, [])[0] ?? null;
|
|
16522
16582
|
} catch (err) {
|
|
16523
|
-
this.logger.warn("local hardware
|
|
16583
|
+
this.logger.warn("local hardware resolve failed — the encoder falls back to software", { meta: { error: errMsg(err) } });
|
|
16524
16584
|
return null;
|
|
16525
16585
|
}
|
|
16526
16586
|
}
|
|
16527
16587
|
/**
|
|
16588
|
+
* What THIS ffmpeg can encode with, asked of the binary once and latched.
|
|
16589
|
+
*
|
|
16590
|
+
* `null` means we could not ask, which is not the same as "it cannot" — see
|
|
16591
|
+
* {@link chooseVideoEncoder}. Keyed by binary PATH, so an operator who
|
|
16592
|
+
* repoints `binaryPath` gets a fresh answer without a restart.
|
|
16593
|
+
*/
|
|
16594
|
+
async resolveBinaryEncoders(binaryPath) {
|
|
16595
|
+
const caps = await this.resolveBinaryCapabilities(binaryPath);
|
|
16596
|
+
return caps === null || caps.encoders.size === 0 ? null : caps.encoders;
|
|
16597
|
+
}
|
|
16598
|
+
/**
|
|
16599
|
+
* What the ffmpeg at this path reports about itself, asked once and latched
|
|
16600
|
+
* per PATH — so an operator who repoints `binaryPath` gets a fresh answer
|
|
16601
|
+
* without a restart.
|
|
16602
|
+
*
|
|
16603
|
+
* A probe that answers nothing is `null`: "we could not ask" must never be
|
|
16604
|
+
* read as "it cannot", or one bad probe pins a capable node in software.
|
|
16605
|
+
*/
|
|
16606
|
+
async resolveBinaryCapabilities(binaryPath) {
|
|
16607
|
+
const cached = this.binaryCapabilities.get(binaryPath);
|
|
16608
|
+
if (cached !== void 0) return await cached;
|
|
16609
|
+
const probing = this.probeFfmpeg(binaryPath).then((caps) => {
|
|
16610
|
+
if (caps.encoders.size === 0 && caps.hwaccels.size === 0) {
|
|
16611
|
+
this.logger.warn("ffmpeg answered nothing about itself — hardware is attempted anyway, and its own failure is the answer", { meta: { ffmpeg: binaryPath } });
|
|
16612
|
+
return null;
|
|
16613
|
+
}
|
|
16614
|
+
this.logger.info("ffmpeg capabilities probed for transcoding", { meta: {
|
|
16615
|
+
ffmpeg: binaryPath,
|
|
16616
|
+
version: caps.version,
|
|
16617
|
+
hwaccels: [...caps.hwaccels].join(",") || "none"
|
|
16618
|
+
} });
|
|
16619
|
+
return caps;
|
|
16620
|
+
});
|
|
16621
|
+
this.binaryCapabilities.set(binaryPath, probing);
|
|
16622
|
+
return await probing;
|
|
16623
|
+
}
|
|
16624
|
+
/**
|
|
16528
16625
|
* Resolve the effective decode `hwaccel` setting for a (deviceId, camStreamId)
|
|
16529
16626
|
* source. Precedence: per-source-stream override > global `ffmpeg.hwAccel`.
|
|
16530
16627
|
* The global default itself is `'copy'` (software) out of the box.
|
|
@@ -16558,7 +16655,18 @@ var TranscodePipelineManager = class {
|
|
|
16558
16655
|
async resolveSpecificDecodeHwAccel(prefer) {
|
|
16559
16656
|
if (!this.hwaccel) return null;
|
|
16560
16657
|
try {
|
|
16561
|
-
|
|
16658
|
+
const backend = rankDecodeHwAccels((await this.hwaccel.resolve(prefer)).preferred, [])[0] ?? null;
|
|
16659
|
+
if (backend === null) return null;
|
|
16660
|
+
const caps = await this.resolveBinaryCapabilities(this.ffmpegConfig().binaryPath);
|
|
16661
|
+
if (caps !== null && !caps.hwaccels.has(backend)) {
|
|
16662
|
+
this.logger.warn("this ffmpeg has no such decode backend compiled in — decoding in software instead of dying on a device that cannot exist", { meta: {
|
|
16663
|
+
backend,
|
|
16664
|
+
ffmpeg: this.ffmpegConfig().binaryPath,
|
|
16665
|
+
hwaccels: [...caps.hwaccels].join(",") || "none"
|
|
16666
|
+
} });
|
|
16667
|
+
return null;
|
|
16668
|
+
}
|
|
16669
|
+
return backend;
|
|
16562
16670
|
} catch (err) {
|
|
16563
16671
|
this.logger.warn("local decode hw-accel resolve failed — software decode fallback", { meta: { error: errMsg(err) } });
|
|
16564
16672
|
return null;
|
|
@@ -18124,6 +18232,20 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
18124
18232
|
if (!parsed) return null;
|
|
18125
18233
|
return findProfileForStream(this.assignments.get(parsed.deviceId)?.map ?? {}, parsed.camStreamId);
|
|
18126
18234
|
}, this.remoteBrokerTiers));
|
|
18235
|
+
server.setStreamOriginResolver((brokerId) => {
|
|
18236
|
+
if (this.remoteBrokerAdapters.has(brokerId)) return {
|
|
18237
|
+
ingestNodeId: this.cachedIngestOwnerNodeId,
|
|
18238
|
+
ingestIsRemote: true
|
|
18239
|
+
};
|
|
18240
|
+
if (this.localNodeId === null) return {
|
|
18241
|
+
ingestNodeId: null,
|
|
18242
|
+
ingestIsRemote: null
|
|
18243
|
+
};
|
|
18244
|
+
return {
|
|
18245
|
+
ingestNodeId: this.localNodeId,
|
|
18246
|
+
ingestIsRemote: false
|
|
18247
|
+
};
|
|
18248
|
+
});
|
|
18127
18249
|
server.setEgressTranscodeProvider({
|
|
18128
18250
|
acquire: (request) => this.acquireEgressTranscode(request),
|
|
18129
18251
|
release: (input) => this.releaseEgressTranscode(input)
|
|
@@ -23438,6 +23560,18 @@ var AdaptiveSession = class AdaptiveSession {
|
|
|
23438
23560
|
transcodeToBaseline: this.transcodeToBaseline
|
|
23439
23561
|
});
|
|
23440
23562
|
}
|
|
23563
|
+
/**
|
|
23564
|
+
* The codec off the SOURCE. Read-only, for the stream-debug overlay: an
|
|
23565
|
+
* operator staring at a frozen picture needs to know whether this hub is
|
|
23566
|
+
* re-encoding for them, and `needsTranscode` alone does not say WHAT from.
|
|
23567
|
+
*/
|
|
23568
|
+
get debugSourceCodec() {
|
|
23569
|
+
return this.sourceCodec;
|
|
23570
|
+
}
|
|
23571
|
+
/** The codec this client's SDP answer settled on. See `debugSourceCodec`. */
|
|
23572
|
+
get debugNegotiatedCodec() {
|
|
23573
|
+
return this.negotiatedCodec;
|
|
23574
|
+
}
|
|
23441
23575
|
_firstKeyFrame;
|
|
23442
23576
|
/**
|
|
23443
23577
|
* Last seen SPS and PPS NALs. Many cameras send SPS/PPS only once
|
|
@@ -26242,6 +26376,85 @@ var AdaptiveController = class {
|
|
|
26242
26376
|
this.healthySince = now;
|
|
26243
26377
|
}
|
|
26244
26378
|
};
|
|
26379
|
+
//#endregion
|
|
26380
|
+
//#region src/stream-broker/webrtc/session-debug.ts
|
|
26381
|
+
/** `<deviceId>/<camStreamId>` → the camStreamId, or null when it is not one. */
|
|
26382
|
+
function camStreamIdOf(brokerId) {
|
|
26383
|
+
const slash = brokerId.indexOf("/");
|
|
26384
|
+
if (slash <= 0) return null;
|
|
26385
|
+
const tail = brokerId.slice(slash + 1);
|
|
26386
|
+
return tail.length > 0 ? tail : null;
|
|
26387
|
+
}
|
|
26388
|
+
/**
|
|
26389
|
+
* Native (the camera's own encode) vs derived (a rung this hub BUILT).
|
|
26390
|
+
* `unknown` when the brokerId does not parse — a guess here would tell an
|
|
26391
|
+
* operator the hub is transcoding when it is not, or the reverse.
|
|
26392
|
+
*/
|
|
26393
|
+
function sourceKindOf(camStreamId) {
|
|
26394
|
+
if (camStreamId === null) return "unknown";
|
|
26395
|
+
return camStreamId.startsWith("derived:") ? "derived" : "native";
|
|
26396
|
+
}
|
|
26397
|
+
var DEAD = {
|
|
26398
|
+
alive: false,
|
|
26399
|
+
brokerId: null,
|
|
26400
|
+
camStreamId: null,
|
|
26401
|
+
sourceKind: "unknown",
|
|
26402
|
+
adaptive: false,
|
|
26403
|
+
servedTier: null,
|
|
26404
|
+
ingestNodeId: null,
|
|
26405
|
+
ingestIsRemote: null,
|
|
26406
|
+
sourceCodec: null,
|
|
26407
|
+
negotiatedCodec: null,
|
|
26408
|
+
egressLeg: "unknown",
|
|
26409
|
+
brokerStatus: null,
|
|
26410
|
+
brokerCodec: null,
|
|
26411
|
+
brokerBitrateKbps: null,
|
|
26412
|
+
brokerInputFps: null,
|
|
26413
|
+
brokerUptimeMs: null,
|
|
26414
|
+
rtpPacketsSent: null,
|
|
26415
|
+
trackMode: null,
|
|
26416
|
+
lastDecision: null,
|
|
26417
|
+
tierFailures: [],
|
|
26418
|
+
upgradeCount: null
|
|
26419
|
+
};
|
|
26420
|
+
function projectSessionDebug(input) {
|
|
26421
|
+
const { session } = input;
|
|
26422
|
+
if (session === null) return {
|
|
26423
|
+
...DEAD,
|
|
26424
|
+
sessionId: input.sessionId,
|
|
26425
|
+
observedAt: input.observedAt
|
|
26426
|
+
};
|
|
26427
|
+
const camStreamId = camStreamIdOf(session.brokerId);
|
|
26428
|
+
const stats = input.brokerStats;
|
|
26429
|
+
return {
|
|
26430
|
+
sessionId: input.sessionId,
|
|
26431
|
+
alive: true,
|
|
26432
|
+
observedAt: input.observedAt,
|
|
26433
|
+
brokerId: session.brokerId,
|
|
26434
|
+
camStreamId,
|
|
26435
|
+
sourceKind: sourceKindOf(camStreamId),
|
|
26436
|
+
adaptive: session.adaptive,
|
|
26437
|
+
servedTier: input.servedTier,
|
|
26438
|
+
ingestNodeId: input.origin?.ingestNodeId ?? null,
|
|
26439
|
+
ingestIsRemote: input.origin?.ingestIsRemote ?? null,
|
|
26440
|
+
sourceCodec: session.sourceCodec,
|
|
26441
|
+
negotiatedCodec: session.negotiatedCodec,
|
|
26442
|
+
egressLeg: session.needsTranscode ? "transcode" : "copy",
|
|
26443
|
+
brokerStatus: stats?.status ?? null,
|
|
26444
|
+
brokerCodec: stats?.codec !== void 0 && stats.codec.length > 0 ? stats.codec : null,
|
|
26445
|
+
brokerBitrateKbps: stats?.bitrateKbps ?? null,
|
|
26446
|
+
brokerInputFps: stats?.inputFps ?? null,
|
|
26447
|
+
brokerUptimeMs: stats?.uptimeMs ?? null,
|
|
26448
|
+
rtpPacketsSent: session.rtpPacketsSent,
|
|
26449
|
+
trackMode: session.trackMode,
|
|
26450
|
+
lastDecision: input.ladder?.lastDecision ?? null,
|
|
26451
|
+
tierFailures: (input.ladder?.failures ?? []).map(([tier, failures]) => ({
|
|
26452
|
+
tier,
|
|
26453
|
+
failures
|
|
26454
|
+
})),
|
|
26455
|
+
upgradeCount: input.ladder?.upgradeCount ?? null
|
|
26456
|
+
};
|
|
26457
|
+
}
|
|
26245
26458
|
var PLAYBACK_PROFILE_FALLBACK_ORDER = [
|
|
26246
26459
|
"high",
|
|
26247
26460
|
"mid",
|
|
@@ -30601,6 +30814,29 @@ var BrokerWebrtcServer = class {
|
|
|
30601
30814
|
* close.
|
|
30602
30815
|
*/
|
|
30603
30816
|
adaptiveIntent = /* @__PURE__ */ new Map();
|
|
30817
|
+
/**
|
|
30818
|
+
* sessionId → the ladder's LAST move, and the numbers it rested on.
|
|
30819
|
+
*
|
|
30820
|
+
* Kept on the SERVER and not on the `SessionEntry` for the same reason
|
|
30821
|
+
* `adaptiveIntent` is: a tier switch is a close-and-recreate re-offer, so
|
|
30822
|
+
* the entry that made the decision is gone by the time anybody asks — which
|
|
30823
|
+
* is exactly the moment an operator opens the debug overlay. Cleared on
|
|
30824
|
+
* close, in `cleanupSession`.
|
|
30825
|
+
*
|
|
30826
|
+
* A snapshot, deliberately, not a re-read of the controller: the decision
|
|
30827
|
+
* was made on a window that has since moved on, and a recomputed "current"
|
|
30828
|
+
* loss ratio answers a different question than "what made it move".
|
|
30829
|
+
* Absent until the ladder actually moves — a ladder that has never moved
|
|
30830
|
+
* reports `null`, which is NOT the same fact as a `healthy` move with a
|
|
30831
|
+
* 0 loss ratio (D393).
|
|
30832
|
+
*/
|
|
30833
|
+
lastLadderDecision = /* @__PURE__ */ new Map();
|
|
30834
|
+
/**
|
|
30835
|
+
* brokerId → where its media is ingested. Injected by the manager, which
|
|
30836
|
+
* owns the ownership map; unset (or a null answer) means UNRESOLVED, and
|
|
30837
|
+
* the overlay says so rather than naming the local node.
|
|
30838
|
+
*/
|
|
30839
|
+
streamOriginResolver = null;
|
|
30604
30840
|
stopped = false;
|
|
30605
30841
|
constructor(options) {
|
|
30606
30842
|
this.logger = options.logger;
|
|
@@ -31551,6 +31787,7 @@ var BrokerWebrtcServer = class {
|
|
|
31551
31787
|
closes.push(entry.session.close().catch(() => {}));
|
|
31552
31788
|
}
|
|
31553
31789
|
await Promise.all(closes);
|
|
31790
|
+
this.lastLadderDecision.clear();
|
|
31554
31791
|
this.brokers.clear();
|
|
31555
31792
|
this.logger.info("WebRTC server stopped");
|
|
31556
31793
|
}
|
|
@@ -31566,6 +31803,92 @@ var BrokerWebrtcServer = class {
|
|
|
31566
31803
|
alive: entry !== void 0
|
|
31567
31804
|
};
|
|
31568
31805
|
}
|
|
31806
|
+
/** Injected by the manager: brokerId → where its media is ingested. */
|
|
31807
|
+
setStreamOriginResolver(resolver) {
|
|
31808
|
+
this.streamOriginResolver = resolver;
|
|
31809
|
+
}
|
|
31810
|
+
/**
|
|
31811
|
+
* The SERVER half of the stream-debug overlay for one session — the rung
|
|
31812
|
+
* `auto` actually resolved to, why the ladder last moved, where the media
|
|
31813
|
+
* is ingested, and whether the egress leg copies or re-encodes.
|
|
31814
|
+
*
|
|
31815
|
+
* Read-only and allocation-light: polled at ~1 Hz by ONE overlay while it
|
|
31816
|
+
* is open. Shaping lives in the pure `projectSessionDebug`; this method
|
|
31817
|
+
* only gathers.
|
|
31818
|
+
*/
|
|
31819
|
+
getSessionDebug(sessionId, now) {
|
|
31820
|
+
const entry = this.sessions.get(sessionId);
|
|
31821
|
+
if (entry === void 0) return projectSessionDebug({
|
|
31822
|
+
sessionId,
|
|
31823
|
+
observedAt: now,
|
|
31824
|
+
session: null,
|
|
31825
|
+
servedTier: null,
|
|
31826
|
+
origin: null,
|
|
31827
|
+
brokerStats: null,
|
|
31828
|
+
ladder: null
|
|
31829
|
+
});
|
|
31830
|
+
const controller = entry.adaptiveController;
|
|
31831
|
+
const decision = this.lastLadderDecision.get(sessionId) ?? null;
|
|
31832
|
+
const ladder = controller !== void 0 ? {
|
|
31833
|
+
lastDecision: decision,
|
|
31834
|
+
failures: [...controller.failureCounts],
|
|
31835
|
+
upgradeCount: controller.successfulUpgradeCount
|
|
31836
|
+
} : decision !== null ? {
|
|
31837
|
+
lastDecision: decision,
|
|
31838
|
+
failures: [],
|
|
31839
|
+
upgradeCount: 0
|
|
31840
|
+
} : null;
|
|
31841
|
+
let brokerStats = null;
|
|
31842
|
+
try {
|
|
31843
|
+
brokerStats = this.brokers.get(entry.brokerId)?.getStats() ?? null;
|
|
31844
|
+
} catch {
|
|
31845
|
+
brokerStats = null;
|
|
31846
|
+
}
|
|
31847
|
+
return projectSessionDebug({
|
|
31848
|
+
sessionId,
|
|
31849
|
+
observedAt: now,
|
|
31850
|
+
session: {
|
|
31851
|
+
brokerId: entry.brokerId,
|
|
31852
|
+
adaptive: controller !== void 0,
|
|
31853
|
+
sourceCodec: entry.session.debugSourceCodec,
|
|
31854
|
+
negotiatedCodec: entry.session.debugNegotiatedCodec,
|
|
31855
|
+
needsTranscode: entry.session.needsTranscode,
|
|
31856
|
+
rtpPacketsSent: entry.session.rtpPacketsSentTotal,
|
|
31857
|
+
trackMode: entry.timeline?.currentMode ?? "live"
|
|
31858
|
+
},
|
|
31859
|
+
servedTier: this.profileTierResolver?.(entry.brokerId) ?? null,
|
|
31860
|
+
origin: this.streamOriginResolver?.(entry.brokerId) ?? null,
|
|
31861
|
+
brokerStats,
|
|
31862
|
+
ladder
|
|
31863
|
+
});
|
|
31864
|
+
}
|
|
31865
|
+
/**
|
|
31866
|
+
* Bound the ladder-decision memory. Entries survive a session teardown on
|
|
31867
|
+
* purpose (the re-offer keeps the id), so nothing else would ever evict the
|
|
31868
|
+
* one left behind by a client that disconnected mid-switch. Insertion order
|
|
31869
|
+
* is eviction order.
|
|
31870
|
+
*/
|
|
31871
|
+
pruneLadderDecisions() {
|
|
31872
|
+
const MAX_REMEMBERED_DECISIONS = 64;
|
|
31873
|
+
while (this.lastLadderDecision.size > MAX_REMEMBERED_DECISIONS) {
|
|
31874
|
+
const oldest = this.lastLadderDecision.keys().next();
|
|
31875
|
+
if (oldest.done === true) break;
|
|
31876
|
+
this.lastLadderDecision.delete(oldest.value);
|
|
31877
|
+
}
|
|
31878
|
+
}
|
|
31879
|
+
/** Snapshot the ladder's move so the overlay can name it later. */
|
|
31880
|
+
recordLadderDecision(sessionId, decision, tier, forcedTranscodeDown) {
|
|
31881
|
+
this.lastLadderDecision.set(sessionId, {
|
|
31882
|
+
cause: decision.cause,
|
|
31883
|
+
tier,
|
|
31884
|
+
forcedTranscodeDown,
|
|
31885
|
+
lossRatio: decision.lossRatio,
|
|
31886
|
+
clientKbps: decision.stall.clientKbps,
|
|
31887
|
+
stalledForMs: decision.stall.stalledForMs,
|
|
31888
|
+
serverSending: decision.stall.serverSending,
|
|
31889
|
+
at: Date.now()
|
|
31890
|
+
});
|
|
31891
|
+
}
|
|
31569
31892
|
/**
|
|
31570
31893
|
* Wire an {@link AdaptiveController} to a freshly-created adaptive session.
|
|
31571
31894
|
* The controller is fed loss from two sources — RTCP Receiver Reports
|
|
@@ -31590,9 +31913,11 @@ var BrokerWebrtcServer = class {
|
|
|
31590
31913
|
priorUpgradeCount,
|
|
31591
31914
|
config: ADAPTIVE_CONFIG,
|
|
31592
31915
|
onSwitchTier: (tier, decision) => {
|
|
31916
|
+
this.recordLadderDecision(sessionId, decision, tier, false);
|
|
31593
31917
|
this.switchSessionTier(entry, deviceId, tier, sessionId, false, decision);
|
|
31594
31918
|
},
|
|
31595
31919
|
onReduceMtu: (decision) => {
|
|
31920
|
+
this.recordLadderDecision(sessionId, decision, null, false);
|
|
31596
31921
|
entry.session.setRepacketizerMtu(AdaptiveSession.ADAPTIVE_LOW_MTU);
|
|
31597
31922
|
this.logger.info("adaptive: reducing repacketizer MTU", {
|
|
31598
31923
|
tags: { deviceId },
|
|
@@ -31609,6 +31934,7 @@ var BrokerWebrtcServer = class {
|
|
|
31609
31934
|
});
|
|
31610
31935
|
},
|
|
31611
31936
|
onTranscodeDown: (decision) => {
|
|
31937
|
+
this.recordLadderDecision(sessionId, decision, "low", true);
|
|
31612
31938
|
this.switchSessionTier(entry, deviceId, "low", sessionId, true, decision);
|
|
31613
31939
|
}
|
|
31614
31940
|
});
|
|
@@ -31714,6 +32040,7 @@ var BrokerWebrtcServer = class {
|
|
|
31714
32040
|
}
|
|
31715
32041
|
if (!brokerStillServed && !this.stopped) this.brokerLinger.hold(entry.brokerId);
|
|
31716
32042
|
this.adaptiveIntent.delete(sessionId);
|
|
32043
|
+
this.pruneLadderDecisions();
|
|
31717
32044
|
if (entry.adaptiveTimer) clearInterval(entry.adaptiveTimer);
|
|
31718
32045
|
if (entry.attributionTimer) clearTimeout(entry.attributionTimer);
|
|
31719
32046
|
entry.attributionTagged = true;
|
|
@@ -32361,6 +32688,16 @@ var WebrtcSessionProvider = class WebrtcSessionProvider {
|
|
|
32361
32688
|
async getSessionState(input) {
|
|
32362
32689
|
return this.webrtcServer.getSessionRenegotiationState(input.sessionId);
|
|
32363
32690
|
}
|
|
32691
|
+
/**
|
|
32692
|
+
* The SERVER half of the stream-debug overlay. Thin facade — the gathering
|
|
32693
|
+
* lives in the WebRTC server and the shaping in the pure
|
|
32694
|
+
* `projectSessionDebug`. `deviceId` is not used to look the session up
|
|
32695
|
+
* (session ids are already unique); it is part of the device-scoped cap's
|
|
32696
|
+
* signature and identifies the camera for the route.
|
|
32697
|
+
*/
|
|
32698
|
+
async getSessionDebug(input) {
|
|
32699
|
+
return this.webrtcServer.getSessionDebug(input.sessionId, Date.now());
|
|
32700
|
+
}
|
|
32364
32701
|
};
|
|
32365
32702
|
//#endregion
|
|
32366
32703
|
//#region src/stream-broker/event-loop-stall-monitor.ts
|
|
@@ -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-CbUvu4Jn.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.269",
|
|
4
4
|
"description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|