@camstack/addon-pipeline 1.1.22 → 1.1.24
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/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CLVitFM4.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BVFAEkIB.mjs} +1 -1
- package/dist/stream-broker/{hostInit-B15D9Mjv.mjs → hostInit-DHej3Ep9.mjs} +1 -1
- package/dist/stream-broker/index.js +79 -10
- package/dist/stream-broker/index.mjs +79 -10
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -8904,6 +8904,24 @@ var DEFAULT_HINT_FPS = 5;
|
|
|
8904
8904
|
* broker's 2s `inputFps` window so the two stats are comparable. */
|
|
8905
8905
|
var DECODE_FPS_WINDOW_MS = 2e3;
|
|
8906
8906
|
/**
|
|
8907
|
+
* Keep-warm grace before a decoder session with no subscribers is torn down.
|
|
8908
|
+
*
|
|
8909
|
+
* Subscribers flap on ~30s cadences (occupancy recheck bursts, detection
|
|
8910
|
+
* motion-gating watch↔active, onboard gray gating). Destroying the
|
|
8911
|
+
* ffmpeg/VAAPI decode session the instant the last subscriber leaves spawns +
|
|
8912
|
+
* destroys a full session every cycle — wasted setup/teardown. Chosen >30s so
|
|
8913
|
+
* consecutive bursts and watch↔active flips reuse the warm session, yet a
|
|
8914
|
+
* genuinely idle session still frees its ffmpeg within ~a minute.
|
|
8915
|
+
*/
|
|
8916
|
+
var SESSION_LINGER_MS = 45e3;
|
|
8917
|
+
/**
|
|
8918
|
+
* Default retry cooldown after a decoder session lands on a NON-LOCAL node
|
|
8919
|
+
* (see `FrameHandlePlaneOptions.sessionRetryCooldownMs`). Long enough to
|
|
8920
|
+
* turn a per-packet storm into a slow probe; short enough that the plane
|
|
8921
|
+
* recovers within seconds once the local decoder respawns.
|
|
8922
|
+
*/
|
|
8923
|
+
var SESSION_RETRY_COOLDOWN_MS = 1e4;
|
|
8924
|
+
/**
|
|
8907
8925
|
* Owns the broker's shared-memory frame-handle subscriptions and the
|
|
8908
8926
|
* per-format `frameSink: 'shm'` decoder sessions that feed them.
|
|
8909
8927
|
*/
|
|
@@ -8912,8 +8930,12 @@ var FrameHandlePlane = class {
|
|
|
8912
8930
|
logger;
|
|
8913
8931
|
resolveStreamInfo;
|
|
8914
8932
|
localNodeId;
|
|
8933
|
+
sessionRetryCooldownMs;
|
|
8915
8934
|
subscriptions = /* @__PURE__ */ new Map();
|
|
8916
8935
|
sessions = /* @__PURE__ */ new Map();
|
|
8936
|
+
/** Per-format retry gate set when a session lands on a non-local node:
|
|
8937
|
+
* `ensureSession` skips creation until the epoch-ms deadline passes. */
|
|
8938
|
+
sessionRetryCooldownUntil = /* @__PURE__ */ new Map();
|
|
8917
8939
|
/** In-flight `ensureSession` creation per format. Coalesces concurrent
|
|
8918
8940
|
* same-format callers onto ONE decoder session — without it two callers
|
|
8919
8941
|
* both pass the `sessions.get` check, both `createSession` (two ffmpeg),
|
|
@@ -8933,6 +8955,7 @@ var FrameHandlePlane = class {
|
|
|
8933
8955
|
this.logger = options.logger;
|
|
8934
8956
|
this.resolveStreamInfo = options.resolveStreamInfo;
|
|
8935
8957
|
this.localNodeId = options.localNodeId;
|
|
8958
|
+
this.sessionRetryCooldownMs = options.sessionRetryCooldownMs ?? SESSION_RETRY_COOLDOWN_MS;
|
|
8936
8959
|
}
|
|
8937
8960
|
/** Number of active handle subscriptions — surfaced for diagnostics. */
|
|
8938
8961
|
get subscriberCount() {
|
|
@@ -8966,8 +8989,13 @@ var FrameHandlePlane = class {
|
|
|
8966
8989
|
nodeId: session.nodeId
|
|
8967
8990
|
} });
|
|
8968
8991
|
await this.destroySession(session);
|
|
8969
|
-
|
|
8970
|
-
|
|
8992
|
+
let restarted = false;
|
|
8993
|
+
try {
|
|
8994
|
+
restarted = await this.startSessionDecoder(session);
|
|
8995
|
+
} finally {
|
|
8996
|
+
if (!restarted) this.sessions.delete(session.format);
|
|
8997
|
+
}
|
|
8998
|
+
if (restarted) rotated += 1;
|
|
8971
8999
|
}
|
|
8972
9000
|
return rotated;
|
|
8973
9001
|
}
|
|
@@ -9022,17 +9050,18 @@ var FrameHandlePlane = class {
|
|
|
9022
9050
|
* underlying decoder session is destroyed (and its shm segment unlinked
|
|
9023
9051
|
* by the decoder). Returns `true` when a known subscription was released.
|
|
9024
9052
|
*/
|
|
9025
|
-
async unsubscribe(subscriptionId) {
|
|
9053
|
+
async unsubscribe(subscriptionId, immediate = false) {
|
|
9026
9054
|
const subscription = this.subscriptions.get(subscriptionId);
|
|
9027
9055
|
if (!subscription) return false;
|
|
9028
9056
|
this.subscriptions.delete(subscriptionId);
|
|
9029
9057
|
const session = this.sessions.get(subscription.format);
|
|
9030
9058
|
if (session) {
|
|
9031
9059
|
session.subscriberIds.delete(subscriptionId);
|
|
9032
|
-
if (session.subscriberIds.size === 0) {
|
|
9060
|
+
if (session.subscriberIds.size === 0) if (immediate) {
|
|
9061
|
+
this.clearLinger(session);
|
|
9033
9062
|
this.sessions.delete(subscription.format);
|
|
9034
9063
|
await this.destroySession(session);
|
|
9035
|
-
}
|
|
9064
|
+
} else this.scheduleLinger(session);
|
|
9036
9065
|
}
|
|
9037
9066
|
this.logger?.info("frame-handle subscription removed", { meta: {
|
|
9038
9067
|
subscriptionId,
|
|
@@ -9041,6 +9070,32 @@ var FrameHandlePlane = class {
|
|
|
9041
9070
|
return true;
|
|
9042
9071
|
}
|
|
9043
9072
|
/**
|
|
9073
|
+
* Arm the keep-warm teardown timer for a session whose last subscriber just
|
|
9074
|
+
* left. Clears any existing timer first (idempotent). On fire it re-checks
|
|
9075
|
+
* `subscriberIds.size === 0` — a subscriber may have returned in the interim,
|
|
9076
|
+
* in which case the timer is a no-op — and only then removes the session and
|
|
9077
|
+
* destroys the decoder. The timer is `unref`'d so a lingering warm session
|
|
9078
|
+
* never keeps the process alive.
|
|
9079
|
+
*/
|
|
9080
|
+
scheduleLinger(session) {
|
|
9081
|
+
this.clearLinger(session);
|
|
9082
|
+
const timer = setTimeout(() => {
|
|
9083
|
+
session.lingerTimer = null;
|
|
9084
|
+
if (session.subscriberIds.size > 0) return;
|
|
9085
|
+
this.sessions.delete(session.format);
|
|
9086
|
+
this.destroySession(session).catch(() => {});
|
|
9087
|
+
}, SESSION_LINGER_MS);
|
|
9088
|
+
timer.unref?.();
|
|
9089
|
+
session.lingerTimer = timer;
|
|
9090
|
+
}
|
|
9091
|
+
/** Cancel a session's pending keep-warm teardown timer, if any. */
|
|
9092
|
+
clearLinger(session) {
|
|
9093
|
+
if (session.lingerTimer) {
|
|
9094
|
+
clearTimeout(session.lingerTimer);
|
|
9095
|
+
session.lingerTimer = null;
|
|
9096
|
+
}
|
|
9097
|
+
}
|
|
9098
|
+
/**
|
|
9044
9099
|
* Forward a video `EncodedPacket` to every active shm decoder session.
|
|
9045
9100
|
* Push-mode decoders consume this; a pull-mode decoder ignores it (it
|
|
9046
9101
|
* reads its own pipe via `openStream`). Audio packets are not forwarded.
|
|
@@ -9162,7 +9217,7 @@ var FrameHandlePlane = class {
|
|
|
9162
9217
|
*/
|
|
9163
9218
|
async killByTag(tag) {
|
|
9164
9219
|
const victims = [...this.subscriptions.values()].filter((s) => s.tag === tag).map((s) => s.id);
|
|
9165
|
-
for (const id of victims) await this.unsubscribe(id);
|
|
9220
|
+
for (const id of victims) await this.unsubscribe(id, true);
|
|
9166
9221
|
return victims.length;
|
|
9167
9222
|
}
|
|
9168
9223
|
/** Tear down every subscription + decoder session. Idempotent. */
|
|
@@ -9190,6 +9245,7 @@ var FrameHandlePlane = class {
|
|
|
9190
9245
|
async ensureSession(format, subscriptionId) {
|
|
9191
9246
|
const existing = this.sessions.get(format);
|
|
9192
9247
|
if (existing) {
|
|
9248
|
+
this.clearLinger(existing);
|
|
9193
9249
|
existing.subscriberIds.add(subscriptionId);
|
|
9194
9250
|
return;
|
|
9195
9251
|
}
|
|
@@ -9199,6 +9255,7 @@ var FrameHandlePlane = class {
|
|
|
9199
9255
|
this.sessions.get(format)?.subscriberIds.add(subscriptionId);
|
|
9200
9256
|
return;
|
|
9201
9257
|
}
|
|
9258
|
+
if (Date.now() < (this.sessionRetryCooldownUntil.get(format) ?? 0)) return;
|
|
9202
9259
|
const creation = (async () => {
|
|
9203
9260
|
const subscriberIds = new Set([subscriptionId]);
|
|
9204
9261
|
for (const subscription of this.subscriptions.values()) if (subscription.format === format) subscriberIds.add(subscription.id);
|
|
@@ -9206,9 +9263,17 @@ var FrameHandlePlane = class {
|
|
|
9206
9263
|
format,
|
|
9207
9264
|
proxy: null,
|
|
9208
9265
|
nodeId: null,
|
|
9209
|
-
subscriberIds
|
|
9266
|
+
subscriberIds,
|
|
9267
|
+
lingerTimer: null
|
|
9210
9268
|
};
|
|
9211
|
-
|
|
9269
|
+
let started = false;
|
|
9270
|
+
try {
|
|
9271
|
+
started = await this.startSessionDecoder(session);
|
|
9272
|
+
} catch (err) {
|
|
9273
|
+
this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);
|
|
9274
|
+
throw err;
|
|
9275
|
+
}
|
|
9276
|
+
if (started) this.sessions.set(format, session);
|
|
9212
9277
|
})();
|
|
9213
9278
|
this.sessionCreating.set(format, creation);
|
|
9214
9279
|
try {
|
|
@@ -9248,11 +9313,13 @@ var FrameHandlePlane = class {
|
|
|
9248
9313
|
tag: `${info.tag}:shm:${format}`
|
|
9249
9314
|
});
|
|
9250
9315
|
if (!isDecoderNodeColocated(this.localNodeId, nodeId)) {
|
|
9316
|
+
this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);
|
|
9251
9317
|
this.logger?.warn("frame-handle plane: decoder session landed on a non-local node — destroying (shm ring is node-local)", { meta: {
|
|
9252
9318
|
format,
|
|
9253
9319
|
requestedNode: this.localNodeId,
|
|
9254
9320
|
gotNode: nodeId,
|
|
9255
|
-
sessionId
|
|
9321
|
+
sessionId,
|
|
9322
|
+
retryInMs: this.sessionRetryCooldownMs
|
|
9256
9323
|
} });
|
|
9257
9324
|
await this.decoderApi.destroySession({ sessionId }).catch((err) => {
|
|
9258
9325
|
this.logger?.warn("frame-handle plane: failed to destroy mis-placed decoder session", { meta: {
|
|
@@ -9263,6 +9330,7 @@ var FrameHandlePlane = class {
|
|
|
9263
9330
|
});
|
|
9264
9331
|
return false;
|
|
9265
9332
|
}
|
|
9333
|
+
this.sessionRetryCooldownUntil.delete(format);
|
|
9266
9334
|
const proxy = new DecoderSessionProxy(this.decoderApi, sessionId);
|
|
9267
9335
|
session.proxy = proxy;
|
|
9268
9336
|
session.nodeId = nodeId;
|
|
@@ -9299,6 +9367,7 @@ var FrameHandlePlane = class {
|
|
|
9299
9367
|
}
|
|
9300
9368
|
/** Destroy a decoder session, swallowing teardown errors. */
|
|
9301
9369
|
async destroySession(session) {
|
|
9370
|
+
this.clearLinger(session);
|
|
9302
9371
|
const proxy = session.proxy;
|
|
9303
9372
|
session.proxy = null;
|
|
9304
9373
|
session.nodeId = null;
|
|
@@ -10579,12 +10648,12 @@ var StreamBroker = class StreamBroker {
|
|
|
10579
10648
|
async subscribeFrameHandles(input) {
|
|
10580
10649
|
const plane = this.ensureFrameHandlePlane();
|
|
10581
10650
|
if (!plane) throw new Error("stream-broker: no decoder available for frame-handle subscription");
|
|
10582
|
-
this.checkDemand();
|
|
10583
10651
|
const { subscriptionId, maxFps } = await plane.subscribe({
|
|
10584
10652
|
format: input.format,
|
|
10585
10653
|
maxFps: input.maxFps,
|
|
10586
10654
|
tag: input.tag
|
|
10587
10655
|
});
|
|
10656
|
+
this.checkDemand();
|
|
10588
10657
|
return {
|
|
10589
10658
|
subscriptionId,
|
|
10590
10659
|
maxFps
|
|
@@ -8900,6 +8900,24 @@ var DEFAULT_HINT_FPS = 5;
|
|
|
8900
8900
|
* broker's 2s `inputFps` window so the two stats are comparable. */
|
|
8901
8901
|
var DECODE_FPS_WINDOW_MS = 2e3;
|
|
8902
8902
|
/**
|
|
8903
|
+
* Keep-warm grace before a decoder session with no subscribers is torn down.
|
|
8904
|
+
*
|
|
8905
|
+
* Subscribers flap on ~30s cadences (occupancy recheck bursts, detection
|
|
8906
|
+
* motion-gating watch↔active, onboard gray gating). Destroying the
|
|
8907
|
+
* ffmpeg/VAAPI decode session the instant the last subscriber leaves spawns +
|
|
8908
|
+
* destroys a full session every cycle — wasted setup/teardown. Chosen >30s so
|
|
8909
|
+
* consecutive bursts and watch↔active flips reuse the warm session, yet a
|
|
8910
|
+
* genuinely idle session still frees its ffmpeg within ~a minute.
|
|
8911
|
+
*/
|
|
8912
|
+
var SESSION_LINGER_MS = 45e3;
|
|
8913
|
+
/**
|
|
8914
|
+
* Default retry cooldown after a decoder session lands on a NON-LOCAL node
|
|
8915
|
+
* (see `FrameHandlePlaneOptions.sessionRetryCooldownMs`). Long enough to
|
|
8916
|
+
* turn a per-packet storm into a slow probe; short enough that the plane
|
|
8917
|
+
* recovers within seconds once the local decoder respawns.
|
|
8918
|
+
*/
|
|
8919
|
+
var SESSION_RETRY_COOLDOWN_MS = 1e4;
|
|
8920
|
+
/**
|
|
8903
8921
|
* Owns the broker's shared-memory frame-handle subscriptions and the
|
|
8904
8922
|
* per-format `frameSink: 'shm'` decoder sessions that feed them.
|
|
8905
8923
|
*/
|
|
@@ -8908,8 +8926,12 @@ var FrameHandlePlane = class {
|
|
|
8908
8926
|
logger;
|
|
8909
8927
|
resolveStreamInfo;
|
|
8910
8928
|
localNodeId;
|
|
8929
|
+
sessionRetryCooldownMs;
|
|
8911
8930
|
subscriptions = /* @__PURE__ */ new Map();
|
|
8912
8931
|
sessions = /* @__PURE__ */ new Map();
|
|
8932
|
+
/** Per-format retry gate set when a session lands on a non-local node:
|
|
8933
|
+
* `ensureSession` skips creation until the epoch-ms deadline passes. */
|
|
8934
|
+
sessionRetryCooldownUntil = /* @__PURE__ */ new Map();
|
|
8913
8935
|
/** In-flight `ensureSession` creation per format. Coalesces concurrent
|
|
8914
8936
|
* same-format callers onto ONE decoder session — without it two callers
|
|
8915
8937
|
* both pass the `sessions.get` check, both `createSession` (two ffmpeg),
|
|
@@ -8929,6 +8951,7 @@ var FrameHandlePlane = class {
|
|
|
8929
8951
|
this.logger = options.logger;
|
|
8930
8952
|
this.resolveStreamInfo = options.resolveStreamInfo;
|
|
8931
8953
|
this.localNodeId = options.localNodeId;
|
|
8954
|
+
this.sessionRetryCooldownMs = options.sessionRetryCooldownMs ?? SESSION_RETRY_COOLDOWN_MS;
|
|
8932
8955
|
}
|
|
8933
8956
|
/** Number of active handle subscriptions — surfaced for diagnostics. */
|
|
8934
8957
|
get subscriberCount() {
|
|
@@ -8962,8 +8985,13 @@ var FrameHandlePlane = class {
|
|
|
8962
8985
|
nodeId: session.nodeId
|
|
8963
8986
|
} });
|
|
8964
8987
|
await this.destroySession(session);
|
|
8965
|
-
|
|
8966
|
-
|
|
8988
|
+
let restarted = false;
|
|
8989
|
+
try {
|
|
8990
|
+
restarted = await this.startSessionDecoder(session);
|
|
8991
|
+
} finally {
|
|
8992
|
+
if (!restarted) this.sessions.delete(session.format);
|
|
8993
|
+
}
|
|
8994
|
+
if (restarted) rotated += 1;
|
|
8967
8995
|
}
|
|
8968
8996
|
return rotated;
|
|
8969
8997
|
}
|
|
@@ -9018,17 +9046,18 @@ var FrameHandlePlane = class {
|
|
|
9018
9046
|
* underlying decoder session is destroyed (and its shm segment unlinked
|
|
9019
9047
|
* by the decoder). Returns `true` when a known subscription was released.
|
|
9020
9048
|
*/
|
|
9021
|
-
async unsubscribe(subscriptionId) {
|
|
9049
|
+
async unsubscribe(subscriptionId, immediate = false) {
|
|
9022
9050
|
const subscription = this.subscriptions.get(subscriptionId);
|
|
9023
9051
|
if (!subscription) return false;
|
|
9024
9052
|
this.subscriptions.delete(subscriptionId);
|
|
9025
9053
|
const session = this.sessions.get(subscription.format);
|
|
9026
9054
|
if (session) {
|
|
9027
9055
|
session.subscriberIds.delete(subscriptionId);
|
|
9028
|
-
if (session.subscriberIds.size === 0) {
|
|
9056
|
+
if (session.subscriberIds.size === 0) if (immediate) {
|
|
9057
|
+
this.clearLinger(session);
|
|
9029
9058
|
this.sessions.delete(subscription.format);
|
|
9030
9059
|
await this.destroySession(session);
|
|
9031
|
-
}
|
|
9060
|
+
} else this.scheduleLinger(session);
|
|
9032
9061
|
}
|
|
9033
9062
|
this.logger?.info("frame-handle subscription removed", { meta: {
|
|
9034
9063
|
subscriptionId,
|
|
@@ -9037,6 +9066,32 @@ var FrameHandlePlane = class {
|
|
|
9037
9066
|
return true;
|
|
9038
9067
|
}
|
|
9039
9068
|
/**
|
|
9069
|
+
* Arm the keep-warm teardown timer for a session whose last subscriber just
|
|
9070
|
+
* left. Clears any existing timer first (idempotent). On fire it re-checks
|
|
9071
|
+
* `subscriberIds.size === 0` — a subscriber may have returned in the interim,
|
|
9072
|
+
* in which case the timer is a no-op — and only then removes the session and
|
|
9073
|
+
* destroys the decoder. The timer is `unref`'d so a lingering warm session
|
|
9074
|
+
* never keeps the process alive.
|
|
9075
|
+
*/
|
|
9076
|
+
scheduleLinger(session) {
|
|
9077
|
+
this.clearLinger(session);
|
|
9078
|
+
const timer = setTimeout(() => {
|
|
9079
|
+
session.lingerTimer = null;
|
|
9080
|
+
if (session.subscriberIds.size > 0) return;
|
|
9081
|
+
this.sessions.delete(session.format);
|
|
9082
|
+
this.destroySession(session).catch(() => {});
|
|
9083
|
+
}, SESSION_LINGER_MS);
|
|
9084
|
+
timer.unref?.();
|
|
9085
|
+
session.lingerTimer = timer;
|
|
9086
|
+
}
|
|
9087
|
+
/** Cancel a session's pending keep-warm teardown timer, if any. */
|
|
9088
|
+
clearLinger(session) {
|
|
9089
|
+
if (session.lingerTimer) {
|
|
9090
|
+
clearTimeout(session.lingerTimer);
|
|
9091
|
+
session.lingerTimer = null;
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
/**
|
|
9040
9095
|
* Forward a video `EncodedPacket` to every active shm decoder session.
|
|
9041
9096
|
* Push-mode decoders consume this; a pull-mode decoder ignores it (it
|
|
9042
9097
|
* reads its own pipe via `openStream`). Audio packets are not forwarded.
|
|
@@ -9158,7 +9213,7 @@ var FrameHandlePlane = class {
|
|
|
9158
9213
|
*/
|
|
9159
9214
|
async killByTag(tag) {
|
|
9160
9215
|
const victims = [...this.subscriptions.values()].filter((s) => s.tag === tag).map((s) => s.id);
|
|
9161
|
-
for (const id of victims) await this.unsubscribe(id);
|
|
9216
|
+
for (const id of victims) await this.unsubscribe(id, true);
|
|
9162
9217
|
return victims.length;
|
|
9163
9218
|
}
|
|
9164
9219
|
/** Tear down every subscription + decoder session. Idempotent. */
|
|
@@ -9186,6 +9241,7 @@ var FrameHandlePlane = class {
|
|
|
9186
9241
|
async ensureSession(format, subscriptionId) {
|
|
9187
9242
|
const existing = this.sessions.get(format);
|
|
9188
9243
|
if (existing) {
|
|
9244
|
+
this.clearLinger(existing);
|
|
9189
9245
|
existing.subscriberIds.add(subscriptionId);
|
|
9190
9246
|
return;
|
|
9191
9247
|
}
|
|
@@ -9195,6 +9251,7 @@ var FrameHandlePlane = class {
|
|
|
9195
9251
|
this.sessions.get(format)?.subscriberIds.add(subscriptionId);
|
|
9196
9252
|
return;
|
|
9197
9253
|
}
|
|
9254
|
+
if (Date.now() < (this.sessionRetryCooldownUntil.get(format) ?? 0)) return;
|
|
9198
9255
|
const creation = (async () => {
|
|
9199
9256
|
const subscriberIds = new Set([subscriptionId]);
|
|
9200
9257
|
for (const subscription of this.subscriptions.values()) if (subscription.format === format) subscriberIds.add(subscription.id);
|
|
@@ -9202,9 +9259,17 @@ var FrameHandlePlane = class {
|
|
|
9202
9259
|
format,
|
|
9203
9260
|
proxy: null,
|
|
9204
9261
|
nodeId: null,
|
|
9205
|
-
subscriberIds
|
|
9262
|
+
subscriberIds,
|
|
9263
|
+
lingerTimer: null
|
|
9206
9264
|
};
|
|
9207
|
-
|
|
9265
|
+
let started = false;
|
|
9266
|
+
try {
|
|
9267
|
+
started = await this.startSessionDecoder(session);
|
|
9268
|
+
} catch (err) {
|
|
9269
|
+
this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);
|
|
9270
|
+
throw err;
|
|
9271
|
+
}
|
|
9272
|
+
if (started) this.sessions.set(format, session);
|
|
9208
9273
|
})();
|
|
9209
9274
|
this.sessionCreating.set(format, creation);
|
|
9210
9275
|
try {
|
|
@@ -9244,11 +9309,13 @@ var FrameHandlePlane = class {
|
|
|
9244
9309
|
tag: `${info.tag}:shm:${format}`
|
|
9245
9310
|
});
|
|
9246
9311
|
if (!isDecoderNodeColocated(this.localNodeId, nodeId)) {
|
|
9312
|
+
this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);
|
|
9247
9313
|
this.logger?.warn("frame-handle plane: decoder session landed on a non-local node — destroying (shm ring is node-local)", { meta: {
|
|
9248
9314
|
format,
|
|
9249
9315
|
requestedNode: this.localNodeId,
|
|
9250
9316
|
gotNode: nodeId,
|
|
9251
|
-
sessionId
|
|
9317
|
+
sessionId,
|
|
9318
|
+
retryInMs: this.sessionRetryCooldownMs
|
|
9252
9319
|
} });
|
|
9253
9320
|
await this.decoderApi.destroySession({ sessionId }).catch((err) => {
|
|
9254
9321
|
this.logger?.warn("frame-handle plane: failed to destroy mis-placed decoder session", { meta: {
|
|
@@ -9259,6 +9326,7 @@ var FrameHandlePlane = class {
|
|
|
9259
9326
|
});
|
|
9260
9327
|
return false;
|
|
9261
9328
|
}
|
|
9329
|
+
this.sessionRetryCooldownUntil.delete(format);
|
|
9262
9330
|
const proxy = new DecoderSessionProxy(this.decoderApi, sessionId);
|
|
9263
9331
|
session.proxy = proxy;
|
|
9264
9332
|
session.nodeId = nodeId;
|
|
@@ -9295,6 +9363,7 @@ var FrameHandlePlane = class {
|
|
|
9295
9363
|
}
|
|
9296
9364
|
/** Destroy a decoder session, swallowing teardown errors. */
|
|
9297
9365
|
async destroySession(session) {
|
|
9366
|
+
this.clearLinger(session);
|
|
9298
9367
|
const proxy = session.proxy;
|
|
9299
9368
|
session.proxy = null;
|
|
9300
9369
|
session.nodeId = null;
|
|
@@ -10575,12 +10644,12 @@ var StreamBroker = class StreamBroker {
|
|
|
10575
10644
|
async subscribeFrameHandles(input) {
|
|
10576
10645
|
const plane = this.ensureFrameHandlePlane();
|
|
10577
10646
|
if (!plane) throw new Error("stream-broker: no decoder available for frame-handle subscription");
|
|
10578
|
-
this.checkDemand();
|
|
10579
10647
|
const { subscriptionId, maxFps } = await plane.subscribe({
|
|
10580
10648
|
format: input.format,
|
|
10581
10649
|
maxFps: input.maxFps,
|
|
10582
10650
|
tag: input.tag
|
|
10583
10651
|
});
|
|
10652
|
+
this.checkDemand();
|
|
10584
10653
|
return {
|
|
10585
10654
|
subscriptionId,
|
|
10586
10655
|
maxFps
|
|
@@ -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-BVFAEkIB.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.1.
|
|
3
|
+
"version": "1.1.24",
|
|
4
4
|
"description": "CamStack Pipeline bundle — runner, detection, motion, decoders, audio + stream broker. Multi-entry npm package shipping 7 addons under a single bundle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|