@camstack/addon-pipeline 1.1.22 → 1.1.23
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 +47 -6
- package/dist/stream-broker/index.mjs +47 -6
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -8904,6 +8904,17 @@ 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
|
+
/**
|
|
8907
8918
|
* Owns the broker's shared-memory frame-handle subscriptions and the
|
|
8908
8919
|
* per-format `frameSink: 'shm'` decoder sessions that feed them.
|
|
8909
8920
|
*/
|
|
@@ -9022,17 +9033,18 @@ var FrameHandlePlane = class {
|
|
|
9022
9033
|
* underlying decoder session is destroyed (and its shm segment unlinked
|
|
9023
9034
|
* by the decoder). Returns `true` when a known subscription was released.
|
|
9024
9035
|
*/
|
|
9025
|
-
async unsubscribe(subscriptionId) {
|
|
9036
|
+
async unsubscribe(subscriptionId, immediate = false) {
|
|
9026
9037
|
const subscription = this.subscriptions.get(subscriptionId);
|
|
9027
9038
|
if (!subscription) return false;
|
|
9028
9039
|
this.subscriptions.delete(subscriptionId);
|
|
9029
9040
|
const session = this.sessions.get(subscription.format);
|
|
9030
9041
|
if (session) {
|
|
9031
9042
|
session.subscriberIds.delete(subscriptionId);
|
|
9032
|
-
if (session.subscriberIds.size === 0) {
|
|
9043
|
+
if (session.subscriberIds.size === 0) if (immediate) {
|
|
9044
|
+
this.clearLinger(session);
|
|
9033
9045
|
this.sessions.delete(subscription.format);
|
|
9034
9046
|
await this.destroySession(session);
|
|
9035
|
-
}
|
|
9047
|
+
} else this.scheduleLinger(session);
|
|
9036
9048
|
}
|
|
9037
9049
|
this.logger?.info("frame-handle subscription removed", { meta: {
|
|
9038
9050
|
subscriptionId,
|
|
@@ -9041,6 +9053,32 @@ var FrameHandlePlane = class {
|
|
|
9041
9053
|
return true;
|
|
9042
9054
|
}
|
|
9043
9055
|
/**
|
|
9056
|
+
* Arm the keep-warm teardown timer for a session whose last subscriber just
|
|
9057
|
+
* left. Clears any existing timer first (idempotent). On fire it re-checks
|
|
9058
|
+
* `subscriberIds.size === 0` — a subscriber may have returned in the interim,
|
|
9059
|
+
* in which case the timer is a no-op — and only then removes the session and
|
|
9060
|
+
* destroys the decoder. The timer is `unref`'d so a lingering warm session
|
|
9061
|
+
* never keeps the process alive.
|
|
9062
|
+
*/
|
|
9063
|
+
scheduleLinger(session) {
|
|
9064
|
+
this.clearLinger(session);
|
|
9065
|
+
const timer = setTimeout(() => {
|
|
9066
|
+
session.lingerTimer = null;
|
|
9067
|
+
if (session.subscriberIds.size > 0) return;
|
|
9068
|
+
this.sessions.delete(session.format);
|
|
9069
|
+
this.destroySession(session).catch(() => {});
|
|
9070
|
+
}, SESSION_LINGER_MS);
|
|
9071
|
+
timer.unref?.();
|
|
9072
|
+
session.lingerTimer = timer;
|
|
9073
|
+
}
|
|
9074
|
+
/** Cancel a session's pending keep-warm teardown timer, if any. */
|
|
9075
|
+
clearLinger(session) {
|
|
9076
|
+
if (session.lingerTimer) {
|
|
9077
|
+
clearTimeout(session.lingerTimer);
|
|
9078
|
+
session.lingerTimer = null;
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
9081
|
+
/**
|
|
9044
9082
|
* Forward a video `EncodedPacket` to every active shm decoder session.
|
|
9045
9083
|
* Push-mode decoders consume this; a pull-mode decoder ignores it (it
|
|
9046
9084
|
* reads its own pipe via `openStream`). Audio packets are not forwarded.
|
|
@@ -9162,7 +9200,7 @@ var FrameHandlePlane = class {
|
|
|
9162
9200
|
*/
|
|
9163
9201
|
async killByTag(tag) {
|
|
9164
9202
|
const victims = [...this.subscriptions.values()].filter((s) => s.tag === tag).map((s) => s.id);
|
|
9165
|
-
for (const id of victims) await this.unsubscribe(id);
|
|
9203
|
+
for (const id of victims) await this.unsubscribe(id, true);
|
|
9166
9204
|
return victims.length;
|
|
9167
9205
|
}
|
|
9168
9206
|
/** Tear down every subscription + decoder session. Idempotent. */
|
|
@@ -9190,6 +9228,7 @@ var FrameHandlePlane = class {
|
|
|
9190
9228
|
async ensureSession(format, subscriptionId) {
|
|
9191
9229
|
const existing = this.sessions.get(format);
|
|
9192
9230
|
if (existing) {
|
|
9231
|
+
this.clearLinger(existing);
|
|
9193
9232
|
existing.subscriberIds.add(subscriptionId);
|
|
9194
9233
|
return;
|
|
9195
9234
|
}
|
|
@@ -9206,7 +9245,8 @@ var FrameHandlePlane = class {
|
|
|
9206
9245
|
format,
|
|
9207
9246
|
proxy: null,
|
|
9208
9247
|
nodeId: null,
|
|
9209
|
-
subscriberIds
|
|
9248
|
+
subscriberIds,
|
|
9249
|
+
lingerTimer: null
|
|
9210
9250
|
};
|
|
9211
9251
|
if (await this.startSessionDecoder(session)) this.sessions.set(format, session);
|
|
9212
9252
|
})();
|
|
@@ -9299,6 +9339,7 @@ var FrameHandlePlane = class {
|
|
|
9299
9339
|
}
|
|
9300
9340
|
/** Destroy a decoder session, swallowing teardown errors. */
|
|
9301
9341
|
async destroySession(session) {
|
|
9342
|
+
this.clearLinger(session);
|
|
9302
9343
|
const proxy = session.proxy;
|
|
9303
9344
|
session.proxy = null;
|
|
9304
9345
|
session.nodeId = null;
|
|
@@ -10579,12 +10620,12 @@ var StreamBroker = class StreamBroker {
|
|
|
10579
10620
|
async subscribeFrameHandles(input) {
|
|
10580
10621
|
const plane = this.ensureFrameHandlePlane();
|
|
10581
10622
|
if (!plane) throw new Error("stream-broker: no decoder available for frame-handle subscription");
|
|
10582
|
-
this.checkDemand();
|
|
10583
10623
|
const { subscriptionId, maxFps } = await plane.subscribe({
|
|
10584
10624
|
format: input.format,
|
|
10585
10625
|
maxFps: input.maxFps,
|
|
10586
10626
|
tag: input.tag
|
|
10587
10627
|
});
|
|
10628
|
+
this.checkDemand();
|
|
10588
10629
|
return {
|
|
10589
10630
|
subscriptionId,
|
|
10590
10631
|
maxFps
|
|
@@ -8900,6 +8900,17 @@ 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
|
+
/**
|
|
8903
8914
|
* Owns the broker's shared-memory frame-handle subscriptions and the
|
|
8904
8915
|
* per-format `frameSink: 'shm'` decoder sessions that feed them.
|
|
8905
8916
|
*/
|
|
@@ -9018,17 +9029,18 @@ var FrameHandlePlane = class {
|
|
|
9018
9029
|
* underlying decoder session is destroyed (and its shm segment unlinked
|
|
9019
9030
|
* by the decoder). Returns `true` when a known subscription was released.
|
|
9020
9031
|
*/
|
|
9021
|
-
async unsubscribe(subscriptionId) {
|
|
9032
|
+
async unsubscribe(subscriptionId, immediate = false) {
|
|
9022
9033
|
const subscription = this.subscriptions.get(subscriptionId);
|
|
9023
9034
|
if (!subscription) return false;
|
|
9024
9035
|
this.subscriptions.delete(subscriptionId);
|
|
9025
9036
|
const session = this.sessions.get(subscription.format);
|
|
9026
9037
|
if (session) {
|
|
9027
9038
|
session.subscriberIds.delete(subscriptionId);
|
|
9028
|
-
if (session.subscriberIds.size === 0) {
|
|
9039
|
+
if (session.subscriberIds.size === 0) if (immediate) {
|
|
9040
|
+
this.clearLinger(session);
|
|
9029
9041
|
this.sessions.delete(subscription.format);
|
|
9030
9042
|
await this.destroySession(session);
|
|
9031
|
-
}
|
|
9043
|
+
} else this.scheduleLinger(session);
|
|
9032
9044
|
}
|
|
9033
9045
|
this.logger?.info("frame-handle subscription removed", { meta: {
|
|
9034
9046
|
subscriptionId,
|
|
@@ -9037,6 +9049,32 @@ var FrameHandlePlane = class {
|
|
|
9037
9049
|
return true;
|
|
9038
9050
|
}
|
|
9039
9051
|
/**
|
|
9052
|
+
* Arm the keep-warm teardown timer for a session whose last subscriber just
|
|
9053
|
+
* left. Clears any existing timer first (idempotent). On fire it re-checks
|
|
9054
|
+
* `subscriberIds.size === 0` — a subscriber may have returned in the interim,
|
|
9055
|
+
* in which case the timer is a no-op — and only then removes the session and
|
|
9056
|
+
* destroys the decoder. The timer is `unref`'d so a lingering warm session
|
|
9057
|
+
* never keeps the process alive.
|
|
9058
|
+
*/
|
|
9059
|
+
scheduleLinger(session) {
|
|
9060
|
+
this.clearLinger(session);
|
|
9061
|
+
const timer = setTimeout(() => {
|
|
9062
|
+
session.lingerTimer = null;
|
|
9063
|
+
if (session.subscriberIds.size > 0) return;
|
|
9064
|
+
this.sessions.delete(session.format);
|
|
9065
|
+
this.destroySession(session).catch(() => {});
|
|
9066
|
+
}, SESSION_LINGER_MS);
|
|
9067
|
+
timer.unref?.();
|
|
9068
|
+
session.lingerTimer = timer;
|
|
9069
|
+
}
|
|
9070
|
+
/** Cancel a session's pending keep-warm teardown timer, if any. */
|
|
9071
|
+
clearLinger(session) {
|
|
9072
|
+
if (session.lingerTimer) {
|
|
9073
|
+
clearTimeout(session.lingerTimer);
|
|
9074
|
+
session.lingerTimer = null;
|
|
9075
|
+
}
|
|
9076
|
+
}
|
|
9077
|
+
/**
|
|
9040
9078
|
* Forward a video `EncodedPacket` to every active shm decoder session.
|
|
9041
9079
|
* Push-mode decoders consume this; a pull-mode decoder ignores it (it
|
|
9042
9080
|
* reads its own pipe via `openStream`). Audio packets are not forwarded.
|
|
@@ -9158,7 +9196,7 @@ var FrameHandlePlane = class {
|
|
|
9158
9196
|
*/
|
|
9159
9197
|
async killByTag(tag) {
|
|
9160
9198
|
const victims = [...this.subscriptions.values()].filter((s) => s.tag === tag).map((s) => s.id);
|
|
9161
|
-
for (const id of victims) await this.unsubscribe(id);
|
|
9199
|
+
for (const id of victims) await this.unsubscribe(id, true);
|
|
9162
9200
|
return victims.length;
|
|
9163
9201
|
}
|
|
9164
9202
|
/** Tear down every subscription + decoder session. Idempotent. */
|
|
@@ -9186,6 +9224,7 @@ var FrameHandlePlane = class {
|
|
|
9186
9224
|
async ensureSession(format, subscriptionId) {
|
|
9187
9225
|
const existing = this.sessions.get(format);
|
|
9188
9226
|
if (existing) {
|
|
9227
|
+
this.clearLinger(existing);
|
|
9189
9228
|
existing.subscriberIds.add(subscriptionId);
|
|
9190
9229
|
return;
|
|
9191
9230
|
}
|
|
@@ -9202,7 +9241,8 @@ var FrameHandlePlane = class {
|
|
|
9202
9241
|
format,
|
|
9203
9242
|
proxy: null,
|
|
9204
9243
|
nodeId: null,
|
|
9205
|
-
subscriberIds
|
|
9244
|
+
subscriberIds,
|
|
9245
|
+
lingerTimer: null
|
|
9206
9246
|
};
|
|
9207
9247
|
if (await this.startSessionDecoder(session)) this.sessions.set(format, session);
|
|
9208
9248
|
})();
|
|
@@ -9295,6 +9335,7 @@ var FrameHandlePlane = class {
|
|
|
9295
9335
|
}
|
|
9296
9336
|
/** Destroy a decoder session, swallowing teardown errors. */
|
|
9297
9337
|
async destroySession(session) {
|
|
9338
|
+
this.clearLinger(session);
|
|
9298
9339
|
const proxy = session.proxy;
|
|
9299
9340
|
session.proxy = null;
|
|
9300
9341
|
session.nodeId = null;
|
|
@@ -10575,12 +10616,12 @@ var StreamBroker = class StreamBroker {
|
|
|
10575
10616
|
async subscribeFrameHandles(input) {
|
|
10576
10617
|
const plane = this.ensureFrameHandlePlane();
|
|
10577
10618
|
if (!plane) throw new Error("stream-broker: no decoder available for frame-handle subscription");
|
|
10578
|
-
this.checkDemand();
|
|
10579
10619
|
const { subscriptionId, maxFps } = await plane.subscribe({
|
|
10580
10620
|
format: input.format,
|
|
10581
10621
|
maxFps: input.maxFps,
|
|
10582
10622
|
tag: input.tag
|
|
10583
10623
|
});
|
|
10624
|
+
this.checkDemand();
|
|
10584
10625
|
return {
|
|
10585
10626
|
subscriptionId,
|
|
10586
10627
|
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.23",
|
|
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",
|