@camstack/addon-pipeline 1.2.249 → 1.2.250
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/index.js +130 -36
- package/dist/stream-broker/index.mjs +130 -36
- package/package.json +1 -1
|
@@ -12484,6 +12484,8 @@ var StreamBroker = class StreamBroker {
|
|
|
12484
12484
|
* behaviour, unchanged.
|
|
12485
12485
|
*/
|
|
12486
12486
|
prestreamPump;
|
|
12487
|
+
/** The manager's hook for "real media started" — see `PrestreamProvider`. */
|
|
12488
|
+
onRealMediaStarted;
|
|
12487
12489
|
/**
|
|
12488
12490
|
* Latched once real media has been seen on this session.
|
|
12489
12491
|
*
|
|
@@ -12760,6 +12762,31 @@ var StreamBroker = class StreamBroker {
|
|
|
12760
12762
|
/** True while the stream is suspended (ffmpeg killed, no RTSP connection). */
|
|
12761
12763
|
_suspended = false;
|
|
12762
12764
|
/**
|
|
12765
|
+
* The LAZY url this broker was started from, remembered for as long as it
|
|
12766
|
+
* lives.
|
|
12767
|
+
*
|
|
12768
|
+
* It exists because the published url and the broker's state disagree for
|
|
12769
|
+
* about sixteen seconds after a suspend. The catalog still carries the real
|
|
12770
|
+
* `tcp://…` socket — the publisher only republishes the sentinel when its
|
|
12771
|
+
* sleep poll notices the camera has gone — while the broker has already torn
|
|
12772
|
+
* that socket down. In that window `resolveCurrentSource()` truthfully
|
|
12773
|
+
* answers with a socket nobody can dial.
|
|
12774
|
+
*
|
|
12775
|
+
* That cost device 640 its prestream on 2026-09-16: the window re-opened
|
|
12776
|
+
* (which `suspend()` does deliberately, for a consumer that never left) with
|
|
12777
|
+
* the real url, `planPrestream` refused it `not-lazy`, and the refusal is
|
|
12778
|
+
* terminal — the operator's client sat dark for ten seconds and gave up six
|
|
12779
|
+
* before the sentinel came back.
|
|
12780
|
+
*
|
|
12781
|
+
* NOT cleared when `start()` is later called with a materialized url: that
|
|
12782
|
+
* happens on every wake, and clearing it there is the bug again. A broker
|
|
12783
|
+
* genuinely reconfigured onto a permanent mains source keeps a stale value,
|
|
12784
|
+
* which costs nothing — `prestreamEligible` (battery + declared signals) is
|
|
12785
|
+
* the authority on whether a camera gets a prestream at all, and it is asked
|
|
12786
|
+
* first, in the manager.
|
|
12787
|
+
*/
|
|
12788
|
+
lazySourceUrl;
|
|
12789
|
+
/**
|
|
12763
12790
|
* Optional demand-transition observer. Fires `true` the instant demand first
|
|
12764
12791
|
* appears (a consumer attaches) and `false` when the broker actually suspends
|
|
12765
12792
|
* after the idle grace — aligned with the broker's own reader spawn/teardown
|
|
@@ -13031,6 +13058,7 @@ var StreamBroker = class StreamBroker {
|
|
|
13031
13058
|
* sees black, which is the outcome every refusal falls back to.
|
|
13032
13059
|
*/
|
|
13033
13060
|
setPrestreamProvider(provider) {
|
|
13061
|
+
this.onRealMediaStarted = provider.onRealMediaStarted;
|
|
13034
13062
|
this.prestreamPump = new PrestreamPump({
|
|
13035
13063
|
prepare: provider.prepare,
|
|
13036
13064
|
emit: (frame) => {
|
|
@@ -13140,13 +13168,26 @@ var StreamBroker = class StreamBroker {
|
|
|
13140
13168
|
const fresh = this.sourceProvider();
|
|
13141
13169
|
if (fresh) {
|
|
13142
13170
|
this.source = fresh;
|
|
13171
|
+
this.noteLazyUrl(fresh.url);
|
|
13143
13172
|
return fresh;
|
|
13144
13173
|
}
|
|
13145
13174
|
}
|
|
13146
13175
|
return this.source ?? null;
|
|
13147
13176
|
}
|
|
13177
|
+
/**
|
|
13178
|
+
* Remember a lazy url ANY time one is seen — see {@link lazySourceUrl}.
|
|
13179
|
+
*
|
|
13180
|
+
* Here and not only in `start()` because a broker that has never dialled is a
|
|
13181
|
+
* real and common state: a battery camera nobody has asked for yet, with a
|
|
13182
|
+
* prestream consumer attached, has a recipe and no socket. Learning it from
|
|
13183
|
+
* the dial alone would leave exactly that broker unable to describe itself.
|
|
13184
|
+
*/
|
|
13185
|
+
noteLazyUrl(url) {
|
|
13186
|
+
if (typeof url === "string" && url.startsWith("lazy:rfc4571:")) this.lazySourceUrl = url;
|
|
13187
|
+
}
|
|
13148
13188
|
async start(source) {
|
|
13149
13189
|
this.source = source;
|
|
13190
|
+
this.noteLazyUrl(source.url);
|
|
13150
13191
|
this.manualStop = false;
|
|
13151
13192
|
this.stopping = false;
|
|
13152
13193
|
if (source.videoCodec) this.detectedCodec = source.videoCodec;
|
|
@@ -13660,7 +13701,7 @@ var StreamBroker = class StreamBroker {
|
|
|
13660
13701
|
reconcilePrestreamWindow() {
|
|
13661
13702
|
if (!this.prestreamPump) return;
|
|
13662
13703
|
if (this.prestreamWanted && !this.prestreamWindowClosed) {
|
|
13663
|
-
const url = this.resolveCurrentSource()?.url ?? this.source?.url;
|
|
13704
|
+
const url = this.lazySourceUrl ?? this.resolveCurrentSource()?.url ?? this.source?.url;
|
|
13664
13705
|
if (url !== void 0) this.prestreamPump.start(url);
|
|
13665
13706
|
return;
|
|
13666
13707
|
}
|
|
@@ -13787,6 +13828,14 @@ var StreamBroker = class StreamBroker {
|
|
|
13787
13828
|
if (packet.isPlaceholder !== true && !this.prestreamWindowClosed) {
|
|
13788
13829
|
this.prestreamWindowClosed = true;
|
|
13789
13830
|
this.prestreamPump?.stop("first real packet");
|
|
13831
|
+
try {
|
|
13832
|
+
this.onRealMediaStarted?.();
|
|
13833
|
+
} catch (err) {
|
|
13834
|
+
this.logger?.debug("prestream: real-media hook threw", {
|
|
13835
|
+
tags: { deviceId: this.numericDeviceId ?? 0 },
|
|
13836
|
+
meta: { error: String(err) }
|
|
13837
|
+
});
|
|
13838
|
+
}
|
|
13790
13839
|
}
|
|
13791
13840
|
if (this.source?.type === "push") {
|
|
13792
13841
|
this.stopErrorPlaceholder();
|
|
@@ -17382,44 +17431,89 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
17382
17431
|
* synthetic-frame path is shaped around.
|
|
17383
17432
|
*/
|
|
17384
17433
|
buildPrestreamProvider(deviceId, camStreamId, brokerId, logger) {
|
|
17385
|
-
return {
|
|
17386
|
-
|
|
17387
|
-
|
|
17388
|
-
|
|
17389
|
-
|
|
17390
|
-
if (!gate.eligible) {
|
|
17391
|
-
logger.info("prestream: not offered for this camera", {
|
|
17392
|
-
tags: { deviceId },
|
|
17393
|
-
meta: {
|
|
17394
|
-
brokerId,
|
|
17395
|
-
reason: gate.reason
|
|
17396
|
-
}
|
|
17434
|
+
return {
|
|
17435
|
+
prepare: async ({ sourceUrl }) => {
|
|
17436
|
+
const gate = prestreamEligible({
|
|
17437
|
+
batteryOperated: await this.isBatteryOperated(deviceId),
|
|
17438
|
+
declaresSignals: (await this.declaredSignalCodes(deviceId)).length > 0
|
|
17397
17439
|
});
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
17404
|
-
|
|
17405
|
-
|
|
17406
|
-
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
|
|
17411
|
-
|
|
17412
|
-
|
|
17413
|
-
|
|
17414
|
-
|
|
17415
|
-
|
|
17416
|
-
|
|
17417
|
-
|
|
17440
|
+
if (!gate.eligible) {
|
|
17441
|
+
logger.info("prestream: not offered for this camera", {
|
|
17442
|
+
tags: { deviceId },
|
|
17443
|
+
meta: {
|
|
17444
|
+
brokerId,
|
|
17445
|
+
reason: gate.reason
|
|
17446
|
+
}
|
|
17447
|
+
});
|
|
17448
|
+
return null;
|
|
17449
|
+
}
|
|
17450
|
+
const stream = this.cameraStreams.get(deviceId)?.get(camStreamId);
|
|
17451
|
+
const codec = this.lastProbedByBroker.get(brokerId)?.codec ?? stream?.codec;
|
|
17452
|
+
const planned = planPrestream({
|
|
17453
|
+
sourceUrl,
|
|
17454
|
+
width: stream?.resolution?.width,
|
|
17455
|
+
height: stream?.resolution?.height,
|
|
17456
|
+
codec
|
|
17457
|
+
});
|
|
17458
|
+
if ("refusal" in planned) {
|
|
17459
|
+
logger.info("prestream: no fill for this camera", {
|
|
17460
|
+
tags: { deviceId },
|
|
17461
|
+
meta: {
|
|
17462
|
+
refusal: planned.refusal,
|
|
17463
|
+
camStreamId,
|
|
17464
|
+
width: stream?.resolution?.width ?? null,
|
|
17465
|
+
height: stream?.resolution?.height ?? null,
|
|
17466
|
+
codec: codec ?? null
|
|
17467
|
+
}
|
|
17468
|
+
});
|
|
17469
|
+
return null;
|
|
17470
|
+
}
|
|
17471
|
+
return await this.preparePrestreamFrame(deviceId, brokerId, planned.plan, logger);
|
|
17472
|
+
},
|
|
17473
|
+
/**
|
|
17474
|
+
* REFRESH THE PICTURE WHILE THE CAMERA IS UP.
|
|
17475
|
+
*
|
|
17476
|
+
* The window shows the camera's last snapshot, and nothing was
|
|
17477
|
+
* refreshing it: a battery camera refuses a capture while it believes
|
|
17478
|
+
* itself asleep, and that belief lags the wake by seconds — so the only
|
|
17479
|
+
* attempts ever made were the prestream's own, while composing, which is
|
|
17480
|
+
* BEFORE any media exists. Measured on device 640 (2026-09-16): four
|
|
17481
|
+
* wakes in forty minutes, the encoded picture `bytes=23912` every time,
|
|
17482
|
+
* and between first packet and suspend not one capture was even
|
|
17483
|
+
* attempted. The operator read the result as a placeholder rather than
|
|
17484
|
+
* as their own room, which is exactly what it had become.
|
|
17485
|
+
*
|
|
17486
|
+
* Here the camera is provably awake and its media is flowing, so a
|
|
17487
|
+
* capture costs it nothing it is not already paying — and what lands is
|
|
17488
|
+
* a scene from the event that just happened.
|
|
17489
|
+
*
|
|
17490
|
+
* NEVER `force`: it is an operator signal, and the rule holds even where
|
|
17491
|
+
* it would be harmless. If the native path still refuses, the addon's
|
|
17492
|
+
* own stream-broker fallback pulls a frame from the restream that is, by
|
|
17493
|
+
* construction, running right now.
|
|
17494
|
+
*
|
|
17495
|
+
* Fire and forget, and a failure is DEBUG: the window already has a
|
|
17496
|
+
* picture to show, and this only decides how recent the NEXT one is.
|
|
17497
|
+
*/
|
|
17498
|
+
onRealMediaStarted: () => {
|
|
17499
|
+
const api = this.api;
|
|
17500
|
+
if (!api) return;
|
|
17501
|
+
api.snapshot.getSnapshot.query({ deviceId }).then(() => {
|
|
17502
|
+
logger.debug("prestream: picture refreshed from the live window", {
|
|
17503
|
+
tags: { deviceId },
|
|
17504
|
+
meta: { brokerId }
|
|
17505
|
+
});
|
|
17506
|
+
}).catch((err) => {
|
|
17507
|
+
logger.debug("prestream: live-window refresh failed — keeping the older picture", {
|
|
17508
|
+
tags: { deviceId },
|
|
17509
|
+
meta: {
|
|
17510
|
+
brokerId,
|
|
17511
|
+
error: require_dist.errMsg(err)
|
|
17512
|
+
}
|
|
17513
|
+
});
|
|
17418
17514
|
});
|
|
17419
|
-
return null;
|
|
17420
17515
|
}
|
|
17421
|
-
|
|
17422
|
-
} };
|
|
17516
|
+
};
|
|
17423
17517
|
}
|
|
17424
17518
|
/**
|
|
17425
17519
|
* Fetch the picture, compose the treatment, encode ONCE and cache the bytes.
|
|
@@ -12480,6 +12480,8 @@ var StreamBroker = class StreamBroker {
|
|
|
12480
12480
|
* behaviour, unchanged.
|
|
12481
12481
|
*/
|
|
12482
12482
|
prestreamPump;
|
|
12483
|
+
/** The manager's hook for "real media started" — see `PrestreamProvider`. */
|
|
12484
|
+
onRealMediaStarted;
|
|
12483
12485
|
/**
|
|
12484
12486
|
* Latched once real media has been seen on this session.
|
|
12485
12487
|
*
|
|
@@ -12756,6 +12758,31 @@ var StreamBroker = class StreamBroker {
|
|
|
12756
12758
|
/** True while the stream is suspended (ffmpeg killed, no RTSP connection). */
|
|
12757
12759
|
_suspended = false;
|
|
12758
12760
|
/**
|
|
12761
|
+
* The LAZY url this broker was started from, remembered for as long as it
|
|
12762
|
+
* lives.
|
|
12763
|
+
*
|
|
12764
|
+
* It exists because the published url and the broker's state disagree for
|
|
12765
|
+
* about sixteen seconds after a suspend. The catalog still carries the real
|
|
12766
|
+
* `tcp://…` socket — the publisher only republishes the sentinel when its
|
|
12767
|
+
* sleep poll notices the camera has gone — while the broker has already torn
|
|
12768
|
+
* that socket down. In that window `resolveCurrentSource()` truthfully
|
|
12769
|
+
* answers with a socket nobody can dial.
|
|
12770
|
+
*
|
|
12771
|
+
* That cost device 640 its prestream on 2026-09-16: the window re-opened
|
|
12772
|
+
* (which `suspend()` does deliberately, for a consumer that never left) with
|
|
12773
|
+
* the real url, `planPrestream` refused it `not-lazy`, and the refusal is
|
|
12774
|
+
* terminal — the operator's client sat dark for ten seconds and gave up six
|
|
12775
|
+
* before the sentinel came back.
|
|
12776
|
+
*
|
|
12777
|
+
* NOT cleared when `start()` is later called with a materialized url: that
|
|
12778
|
+
* happens on every wake, and clearing it there is the bug again. A broker
|
|
12779
|
+
* genuinely reconfigured onto a permanent mains source keeps a stale value,
|
|
12780
|
+
* which costs nothing — `prestreamEligible` (battery + declared signals) is
|
|
12781
|
+
* the authority on whether a camera gets a prestream at all, and it is asked
|
|
12782
|
+
* first, in the manager.
|
|
12783
|
+
*/
|
|
12784
|
+
lazySourceUrl;
|
|
12785
|
+
/**
|
|
12759
12786
|
* Optional demand-transition observer. Fires `true` the instant demand first
|
|
12760
12787
|
* appears (a consumer attaches) and `false` when the broker actually suspends
|
|
12761
12788
|
* after the idle grace — aligned with the broker's own reader spawn/teardown
|
|
@@ -13027,6 +13054,7 @@ var StreamBroker = class StreamBroker {
|
|
|
13027
13054
|
* sees black, which is the outcome every refusal falls back to.
|
|
13028
13055
|
*/
|
|
13029
13056
|
setPrestreamProvider(provider) {
|
|
13057
|
+
this.onRealMediaStarted = provider.onRealMediaStarted;
|
|
13030
13058
|
this.prestreamPump = new PrestreamPump({
|
|
13031
13059
|
prepare: provider.prepare,
|
|
13032
13060
|
emit: (frame) => {
|
|
@@ -13136,13 +13164,26 @@ var StreamBroker = class StreamBroker {
|
|
|
13136
13164
|
const fresh = this.sourceProvider();
|
|
13137
13165
|
if (fresh) {
|
|
13138
13166
|
this.source = fresh;
|
|
13167
|
+
this.noteLazyUrl(fresh.url);
|
|
13139
13168
|
return fresh;
|
|
13140
13169
|
}
|
|
13141
13170
|
}
|
|
13142
13171
|
return this.source ?? null;
|
|
13143
13172
|
}
|
|
13173
|
+
/**
|
|
13174
|
+
* Remember a lazy url ANY time one is seen — see {@link lazySourceUrl}.
|
|
13175
|
+
*
|
|
13176
|
+
* Here and not only in `start()` because a broker that has never dialled is a
|
|
13177
|
+
* real and common state: a battery camera nobody has asked for yet, with a
|
|
13178
|
+
* prestream consumer attached, has a recipe and no socket. Learning it from
|
|
13179
|
+
* the dial alone would leave exactly that broker unable to describe itself.
|
|
13180
|
+
*/
|
|
13181
|
+
noteLazyUrl(url) {
|
|
13182
|
+
if (typeof url === "string" && url.startsWith("lazy:rfc4571:")) this.lazySourceUrl = url;
|
|
13183
|
+
}
|
|
13144
13184
|
async start(source) {
|
|
13145
13185
|
this.source = source;
|
|
13186
|
+
this.noteLazyUrl(source.url);
|
|
13146
13187
|
this.manualStop = false;
|
|
13147
13188
|
this.stopping = false;
|
|
13148
13189
|
if (source.videoCodec) this.detectedCodec = source.videoCodec;
|
|
@@ -13656,7 +13697,7 @@ var StreamBroker = class StreamBroker {
|
|
|
13656
13697
|
reconcilePrestreamWindow() {
|
|
13657
13698
|
if (!this.prestreamPump) return;
|
|
13658
13699
|
if (this.prestreamWanted && !this.prestreamWindowClosed) {
|
|
13659
|
-
const url = this.resolveCurrentSource()?.url ?? this.source?.url;
|
|
13700
|
+
const url = this.lazySourceUrl ?? this.resolveCurrentSource()?.url ?? this.source?.url;
|
|
13660
13701
|
if (url !== void 0) this.prestreamPump.start(url);
|
|
13661
13702
|
return;
|
|
13662
13703
|
}
|
|
@@ -13783,6 +13824,14 @@ var StreamBroker = class StreamBroker {
|
|
|
13783
13824
|
if (packet.isPlaceholder !== true && !this.prestreamWindowClosed) {
|
|
13784
13825
|
this.prestreamWindowClosed = true;
|
|
13785
13826
|
this.prestreamPump?.stop("first real packet");
|
|
13827
|
+
try {
|
|
13828
|
+
this.onRealMediaStarted?.();
|
|
13829
|
+
} catch (err) {
|
|
13830
|
+
this.logger?.debug("prestream: real-media hook threw", {
|
|
13831
|
+
tags: { deviceId: this.numericDeviceId ?? 0 },
|
|
13832
|
+
meta: { error: String(err) }
|
|
13833
|
+
});
|
|
13834
|
+
}
|
|
13786
13835
|
}
|
|
13787
13836
|
if (this.source?.type === "push") {
|
|
13788
13837
|
this.stopErrorPlaceholder();
|
|
@@ -17378,44 +17427,89 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
17378
17427
|
* synthetic-frame path is shaped around.
|
|
17379
17428
|
*/
|
|
17380
17429
|
buildPrestreamProvider(deviceId, camStreamId, brokerId, logger) {
|
|
17381
|
-
return {
|
|
17382
|
-
|
|
17383
|
-
|
|
17384
|
-
|
|
17385
|
-
|
|
17386
|
-
if (!gate.eligible) {
|
|
17387
|
-
logger.info("prestream: not offered for this camera", {
|
|
17388
|
-
tags: { deviceId },
|
|
17389
|
-
meta: {
|
|
17390
|
-
brokerId,
|
|
17391
|
-
reason: gate.reason
|
|
17392
|
-
}
|
|
17430
|
+
return {
|
|
17431
|
+
prepare: async ({ sourceUrl }) => {
|
|
17432
|
+
const gate = prestreamEligible({
|
|
17433
|
+
batteryOperated: await this.isBatteryOperated(deviceId),
|
|
17434
|
+
declaresSignals: (await this.declaredSignalCodes(deviceId)).length > 0
|
|
17393
17435
|
});
|
|
17394
|
-
|
|
17395
|
-
|
|
17396
|
-
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
17404
|
-
|
|
17405
|
-
|
|
17406
|
-
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
|
|
17411
|
-
|
|
17412
|
-
|
|
17413
|
-
|
|
17436
|
+
if (!gate.eligible) {
|
|
17437
|
+
logger.info("prestream: not offered for this camera", {
|
|
17438
|
+
tags: { deviceId },
|
|
17439
|
+
meta: {
|
|
17440
|
+
brokerId,
|
|
17441
|
+
reason: gate.reason
|
|
17442
|
+
}
|
|
17443
|
+
});
|
|
17444
|
+
return null;
|
|
17445
|
+
}
|
|
17446
|
+
const stream = this.cameraStreams.get(deviceId)?.get(camStreamId);
|
|
17447
|
+
const codec = this.lastProbedByBroker.get(brokerId)?.codec ?? stream?.codec;
|
|
17448
|
+
const planned = planPrestream({
|
|
17449
|
+
sourceUrl,
|
|
17450
|
+
width: stream?.resolution?.width,
|
|
17451
|
+
height: stream?.resolution?.height,
|
|
17452
|
+
codec
|
|
17453
|
+
});
|
|
17454
|
+
if ("refusal" in planned) {
|
|
17455
|
+
logger.info("prestream: no fill for this camera", {
|
|
17456
|
+
tags: { deviceId },
|
|
17457
|
+
meta: {
|
|
17458
|
+
refusal: planned.refusal,
|
|
17459
|
+
camStreamId,
|
|
17460
|
+
width: stream?.resolution?.width ?? null,
|
|
17461
|
+
height: stream?.resolution?.height ?? null,
|
|
17462
|
+
codec: codec ?? null
|
|
17463
|
+
}
|
|
17464
|
+
});
|
|
17465
|
+
return null;
|
|
17466
|
+
}
|
|
17467
|
+
return await this.preparePrestreamFrame(deviceId, brokerId, planned.plan, logger);
|
|
17468
|
+
},
|
|
17469
|
+
/**
|
|
17470
|
+
* REFRESH THE PICTURE WHILE THE CAMERA IS UP.
|
|
17471
|
+
*
|
|
17472
|
+
* The window shows the camera's last snapshot, and nothing was
|
|
17473
|
+
* refreshing it: a battery camera refuses a capture while it believes
|
|
17474
|
+
* itself asleep, and that belief lags the wake by seconds — so the only
|
|
17475
|
+
* attempts ever made were the prestream's own, while composing, which is
|
|
17476
|
+
* BEFORE any media exists. Measured on device 640 (2026-09-16): four
|
|
17477
|
+
* wakes in forty minutes, the encoded picture `bytes=23912` every time,
|
|
17478
|
+
* and between first packet and suspend not one capture was even
|
|
17479
|
+
* attempted. The operator read the result as a placeholder rather than
|
|
17480
|
+
* as their own room, which is exactly what it had become.
|
|
17481
|
+
*
|
|
17482
|
+
* Here the camera is provably awake and its media is flowing, so a
|
|
17483
|
+
* capture costs it nothing it is not already paying — and what lands is
|
|
17484
|
+
* a scene from the event that just happened.
|
|
17485
|
+
*
|
|
17486
|
+
* NEVER `force`: it is an operator signal, and the rule holds even where
|
|
17487
|
+
* it would be harmless. If the native path still refuses, the addon's
|
|
17488
|
+
* own stream-broker fallback pulls a frame from the restream that is, by
|
|
17489
|
+
* construction, running right now.
|
|
17490
|
+
*
|
|
17491
|
+
* Fire and forget, and a failure is DEBUG: the window already has a
|
|
17492
|
+
* picture to show, and this only decides how recent the NEXT one is.
|
|
17493
|
+
*/
|
|
17494
|
+
onRealMediaStarted: () => {
|
|
17495
|
+
const api = this.api;
|
|
17496
|
+
if (!api) return;
|
|
17497
|
+
api.snapshot.getSnapshot.query({ deviceId }).then(() => {
|
|
17498
|
+
logger.debug("prestream: picture refreshed from the live window", {
|
|
17499
|
+
tags: { deviceId },
|
|
17500
|
+
meta: { brokerId }
|
|
17501
|
+
});
|
|
17502
|
+
}).catch((err) => {
|
|
17503
|
+
logger.debug("prestream: live-window refresh failed — keeping the older picture", {
|
|
17504
|
+
tags: { deviceId },
|
|
17505
|
+
meta: {
|
|
17506
|
+
brokerId,
|
|
17507
|
+
error: errMsg(err)
|
|
17508
|
+
}
|
|
17509
|
+
});
|
|
17414
17510
|
});
|
|
17415
|
-
return null;
|
|
17416
17511
|
}
|
|
17417
|
-
|
|
17418
|
-
} };
|
|
17512
|
+
};
|
|
17419
17513
|
}
|
|
17420
17514
|
/**
|
|
17421
17515
|
* Fetch the picture, compose the treatment, encode ONCE and cache the bytes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.250",
|
|
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",
|