@camstack/addon-pipeline 1.1.23 → 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.
|
@@ -8915,6 +8915,13 @@ var DECODE_FPS_WINDOW_MS = 2e3;
|
|
|
8915
8915
|
*/
|
|
8916
8916
|
var SESSION_LINGER_MS = 45e3;
|
|
8917
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
|
+
/**
|
|
8918
8925
|
* Owns the broker's shared-memory frame-handle subscriptions and the
|
|
8919
8926
|
* per-format `frameSink: 'shm'` decoder sessions that feed them.
|
|
8920
8927
|
*/
|
|
@@ -8923,8 +8930,12 @@ var FrameHandlePlane = class {
|
|
|
8923
8930
|
logger;
|
|
8924
8931
|
resolveStreamInfo;
|
|
8925
8932
|
localNodeId;
|
|
8933
|
+
sessionRetryCooldownMs;
|
|
8926
8934
|
subscriptions = /* @__PURE__ */ new Map();
|
|
8927
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();
|
|
8928
8939
|
/** In-flight `ensureSession` creation per format. Coalesces concurrent
|
|
8929
8940
|
* same-format callers onto ONE decoder session — without it two callers
|
|
8930
8941
|
* both pass the `sessions.get` check, both `createSession` (two ffmpeg),
|
|
@@ -8944,6 +8955,7 @@ var FrameHandlePlane = class {
|
|
|
8944
8955
|
this.logger = options.logger;
|
|
8945
8956
|
this.resolveStreamInfo = options.resolveStreamInfo;
|
|
8946
8957
|
this.localNodeId = options.localNodeId;
|
|
8958
|
+
this.sessionRetryCooldownMs = options.sessionRetryCooldownMs ?? SESSION_RETRY_COOLDOWN_MS;
|
|
8947
8959
|
}
|
|
8948
8960
|
/** Number of active handle subscriptions — surfaced for diagnostics. */
|
|
8949
8961
|
get subscriberCount() {
|
|
@@ -8977,8 +8989,13 @@ var FrameHandlePlane = class {
|
|
|
8977
8989
|
nodeId: session.nodeId
|
|
8978
8990
|
} });
|
|
8979
8991
|
await this.destroySession(session);
|
|
8980
|
-
|
|
8981
|
-
|
|
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;
|
|
8982
8999
|
}
|
|
8983
9000
|
return rotated;
|
|
8984
9001
|
}
|
|
@@ -9238,6 +9255,7 @@ var FrameHandlePlane = class {
|
|
|
9238
9255
|
this.sessions.get(format)?.subscriberIds.add(subscriptionId);
|
|
9239
9256
|
return;
|
|
9240
9257
|
}
|
|
9258
|
+
if (Date.now() < (this.sessionRetryCooldownUntil.get(format) ?? 0)) return;
|
|
9241
9259
|
const creation = (async () => {
|
|
9242
9260
|
const subscriberIds = new Set([subscriptionId]);
|
|
9243
9261
|
for (const subscription of this.subscriptions.values()) if (subscription.format === format) subscriberIds.add(subscription.id);
|
|
@@ -9248,7 +9266,14 @@ var FrameHandlePlane = class {
|
|
|
9248
9266
|
subscriberIds,
|
|
9249
9267
|
lingerTimer: null
|
|
9250
9268
|
};
|
|
9251
|
-
|
|
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);
|
|
9252
9277
|
})();
|
|
9253
9278
|
this.sessionCreating.set(format, creation);
|
|
9254
9279
|
try {
|
|
@@ -9288,11 +9313,13 @@ var FrameHandlePlane = class {
|
|
|
9288
9313
|
tag: `${info.tag}:shm:${format}`
|
|
9289
9314
|
});
|
|
9290
9315
|
if (!isDecoderNodeColocated(this.localNodeId, nodeId)) {
|
|
9316
|
+
this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);
|
|
9291
9317
|
this.logger?.warn("frame-handle plane: decoder session landed on a non-local node — destroying (shm ring is node-local)", { meta: {
|
|
9292
9318
|
format,
|
|
9293
9319
|
requestedNode: this.localNodeId,
|
|
9294
9320
|
gotNode: nodeId,
|
|
9295
|
-
sessionId
|
|
9321
|
+
sessionId,
|
|
9322
|
+
retryInMs: this.sessionRetryCooldownMs
|
|
9296
9323
|
} });
|
|
9297
9324
|
await this.decoderApi.destroySession({ sessionId }).catch((err) => {
|
|
9298
9325
|
this.logger?.warn("frame-handle plane: failed to destroy mis-placed decoder session", { meta: {
|
|
@@ -9303,6 +9330,7 @@ var FrameHandlePlane = class {
|
|
|
9303
9330
|
});
|
|
9304
9331
|
return false;
|
|
9305
9332
|
}
|
|
9333
|
+
this.sessionRetryCooldownUntil.delete(format);
|
|
9306
9334
|
const proxy = new DecoderSessionProxy(this.decoderApi, sessionId);
|
|
9307
9335
|
session.proxy = proxy;
|
|
9308
9336
|
session.nodeId = nodeId;
|
|
@@ -8911,6 +8911,13 @@ var DECODE_FPS_WINDOW_MS = 2e3;
|
|
|
8911
8911
|
*/
|
|
8912
8912
|
var SESSION_LINGER_MS = 45e3;
|
|
8913
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
|
+
/**
|
|
8914
8921
|
* Owns the broker's shared-memory frame-handle subscriptions and the
|
|
8915
8922
|
* per-format `frameSink: 'shm'` decoder sessions that feed them.
|
|
8916
8923
|
*/
|
|
@@ -8919,8 +8926,12 @@ var FrameHandlePlane = class {
|
|
|
8919
8926
|
logger;
|
|
8920
8927
|
resolveStreamInfo;
|
|
8921
8928
|
localNodeId;
|
|
8929
|
+
sessionRetryCooldownMs;
|
|
8922
8930
|
subscriptions = /* @__PURE__ */ new Map();
|
|
8923
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();
|
|
8924
8935
|
/** In-flight `ensureSession` creation per format. Coalesces concurrent
|
|
8925
8936
|
* same-format callers onto ONE decoder session — without it two callers
|
|
8926
8937
|
* both pass the `sessions.get` check, both `createSession` (two ffmpeg),
|
|
@@ -8940,6 +8951,7 @@ var FrameHandlePlane = class {
|
|
|
8940
8951
|
this.logger = options.logger;
|
|
8941
8952
|
this.resolveStreamInfo = options.resolveStreamInfo;
|
|
8942
8953
|
this.localNodeId = options.localNodeId;
|
|
8954
|
+
this.sessionRetryCooldownMs = options.sessionRetryCooldownMs ?? SESSION_RETRY_COOLDOWN_MS;
|
|
8943
8955
|
}
|
|
8944
8956
|
/** Number of active handle subscriptions — surfaced for diagnostics. */
|
|
8945
8957
|
get subscriberCount() {
|
|
@@ -8973,8 +8985,13 @@ var FrameHandlePlane = class {
|
|
|
8973
8985
|
nodeId: session.nodeId
|
|
8974
8986
|
} });
|
|
8975
8987
|
await this.destroySession(session);
|
|
8976
|
-
|
|
8977
|
-
|
|
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;
|
|
8978
8995
|
}
|
|
8979
8996
|
return rotated;
|
|
8980
8997
|
}
|
|
@@ -9234,6 +9251,7 @@ var FrameHandlePlane = class {
|
|
|
9234
9251
|
this.sessions.get(format)?.subscriberIds.add(subscriptionId);
|
|
9235
9252
|
return;
|
|
9236
9253
|
}
|
|
9254
|
+
if (Date.now() < (this.sessionRetryCooldownUntil.get(format) ?? 0)) return;
|
|
9237
9255
|
const creation = (async () => {
|
|
9238
9256
|
const subscriberIds = new Set([subscriptionId]);
|
|
9239
9257
|
for (const subscription of this.subscriptions.values()) if (subscription.format === format) subscriberIds.add(subscription.id);
|
|
@@ -9244,7 +9262,14 @@ var FrameHandlePlane = class {
|
|
|
9244
9262
|
subscriberIds,
|
|
9245
9263
|
lingerTimer: null
|
|
9246
9264
|
};
|
|
9247
|
-
|
|
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);
|
|
9248
9273
|
})();
|
|
9249
9274
|
this.sessionCreating.set(format, creation);
|
|
9250
9275
|
try {
|
|
@@ -9284,11 +9309,13 @@ var FrameHandlePlane = class {
|
|
|
9284
9309
|
tag: `${info.tag}:shm:${format}`
|
|
9285
9310
|
});
|
|
9286
9311
|
if (!isDecoderNodeColocated(this.localNodeId, nodeId)) {
|
|
9312
|
+
this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);
|
|
9287
9313
|
this.logger?.warn("frame-handle plane: decoder session landed on a non-local node — destroying (shm ring is node-local)", { meta: {
|
|
9288
9314
|
format,
|
|
9289
9315
|
requestedNode: this.localNodeId,
|
|
9290
9316
|
gotNode: nodeId,
|
|
9291
|
-
sessionId
|
|
9317
|
+
sessionId,
|
|
9318
|
+
retryInMs: this.sessionRetryCooldownMs
|
|
9292
9319
|
} });
|
|
9293
9320
|
await this.decoderApi.destroySession({ sessionId }).catch((err) => {
|
|
9294
9321
|
this.logger?.warn("frame-handle plane: failed to destroy mis-placed decoder session", { meta: {
|
|
@@ -9299,6 +9326,7 @@ var FrameHandlePlane = class {
|
|
|
9299
9326
|
});
|
|
9300
9327
|
return false;
|
|
9301
9328
|
}
|
|
9329
|
+
this.sessionRetryCooldownUntil.delete(format);
|
|
9302
9330
|
const proxy = new DecoderSessionProxy(this.decoderApi, sessionId);
|
|
9303
9331
|
session.proxy = proxy;
|
|
9304
9332
|
session.nodeId = nodeId;
|
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",
|