@camstack/addon-pipeline 1.2.311 → 1.2.312
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 +137 -13
- package/dist/stream-broker/index.mjs +137 -13
- package/package.json +1 -1
|
@@ -8498,6 +8498,41 @@ function resolveSourceParser(setting, sourceType) {
|
|
|
8498
8498
|
}
|
|
8499
8499
|
}
|
|
8500
8500
|
//#endregion
|
|
8501
|
+
//#region src/stream-broker/catalog-absence.ts
|
|
8502
|
+
function judgeCatalogAbsence(input) {
|
|
8503
|
+
const { marks, listed, candidates, nowMs, confirmMinMs } = input;
|
|
8504
|
+
const nextMarks = /* @__PURE__ */ new Map();
|
|
8505
|
+
const marked = [];
|
|
8506
|
+
const cleared = [];
|
|
8507
|
+
const retract = [];
|
|
8508
|
+
for (const camStreamId of candidates) {
|
|
8509
|
+
const markedAt = marks.get(camStreamId);
|
|
8510
|
+
if (listed.has(camStreamId)) {
|
|
8511
|
+
if (markedAt !== void 0) cleared.push(camStreamId);
|
|
8512
|
+
continue;
|
|
8513
|
+
}
|
|
8514
|
+
if (markedAt === void 0) {
|
|
8515
|
+
nextMarks.set(camStreamId, nowMs);
|
|
8516
|
+
marked.push(camStreamId);
|
|
8517
|
+
continue;
|
|
8518
|
+
}
|
|
8519
|
+
if (nowMs - markedAt < confirmMinMs) {
|
|
8520
|
+
nextMarks.set(camStreamId, markedAt);
|
|
8521
|
+
continue;
|
|
8522
|
+
}
|
|
8523
|
+
retract.push({
|
|
8524
|
+
camStreamId,
|
|
8525
|
+
absentForMs: nowMs - markedAt
|
|
8526
|
+
});
|
|
8527
|
+
}
|
|
8528
|
+
return {
|
|
8529
|
+
nextMarks,
|
|
8530
|
+
marked,
|
|
8531
|
+
cleared,
|
|
8532
|
+
retract
|
|
8533
|
+
};
|
|
8534
|
+
}
|
|
8535
|
+
//#endregion
|
|
8501
8536
|
//#region src/stream-broker/rtmp/flv-parser.ts
|
|
8502
8537
|
var AudioSoundFormat = /* @__PURE__ */ function(AudioSoundFormat) {
|
|
8503
8538
|
AudioSoundFormat[AudioSoundFormat["PCM_BE"] = 0] = "PCM_BE";
|
|
@@ -17098,6 +17133,13 @@ var RtspEnabledSchema = require_dist.record(require_dist.string(), require_dist.
|
|
|
17098
17133
|
* one observation.
|
|
17099
17134
|
*/
|
|
17100
17135
|
var DEPARTURE_CONFIRM_MIN_MS = 3e4;
|
|
17136
|
+
/**
|
|
17137
|
+
* The catalog reconcile's second omission of a stream must be at least this
|
|
17138
|
+
* long after the first (D689) — the same one-poll-interval floor as the
|
|
17139
|
+
* departed-device sweep, for the same reason: an event-driven pull next to a
|
|
17140
|
+
* poll, or the startup pass's quick re-runs, are one observation.
|
|
17141
|
+
*/
|
|
17142
|
+
var CATALOG_ABSENCE_CONFIRM_MIN_MS = DEPARTURE_CONFIRM_MIN_MS;
|
|
17101
17143
|
var PUSH_KINDS = new Set(["push-annexb"]);
|
|
17102
17144
|
/** Walk a profile map and return the slot that points at `camStreamId`, or null. */
|
|
17103
17145
|
function findProfileForStream(map, camStreamId) {
|
|
@@ -17304,6 +17346,14 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
17304
17346
|
* agrees, and a reappearance clears it (D49).
|
|
17305
17347
|
*/
|
|
17306
17348
|
departureMarks = /* @__PURE__ */ new Map();
|
|
17349
|
+
/**
|
|
17350
|
+
* Per device: provider streams the broker holds that a successful catalog
|
|
17351
|
+
* pull OMITTED, and when (D689). A mark, not a verdict — see
|
|
17352
|
+
* `catalog-absence.ts`. Dropped when the stream is retracted by any path and
|
|
17353
|
+
* when the device is disabled (no pulls run then, so a mark would outlive
|
|
17354
|
+
* the observation it records).
|
|
17355
|
+
*/
|
|
17356
|
+
catalogAbsenceMarks = /* @__PURE__ */ new Map();
|
|
17307
17357
|
eventBus = null;
|
|
17308
17358
|
/**
|
|
17309
17359
|
* Per-broker stream health flag set by the watchdog. `undefined`
|
|
@@ -19034,6 +19084,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19034
19084
|
await this.releaseBroker(deviceId, camStreamId);
|
|
19035
19085
|
streamMap.delete(camStreamId);
|
|
19036
19086
|
if (streamMap.size === 0) this.cameraStreams.delete(deviceId);
|
|
19087
|
+
this.dropCatalogAbsenceMark(deviceId, camStreamId);
|
|
19037
19088
|
this.emitCamStreamsChanged(deviceId);
|
|
19038
19089
|
return { success: true };
|
|
19039
19090
|
}
|
|
@@ -19095,13 +19146,21 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19095
19146
|
}
|
|
19096
19147
|
/**
|
|
19097
19148
|
* Pull one camera's `stream-catalog` and reconcile its cam-streams: upsert
|
|
19098
|
-
* every descriptor, retract
|
|
19149
|
+
* every descriptor, retract a cam-stream the catalog no longer lists. A pull
|
|
19099
19150
|
* that throws (provider mid-restart, no `stream-catalog` cap, a camera whose
|
|
19100
19151
|
* session died) omits THIS DEVICE ONLY — it is logged at `warn` with the
|
|
19101
|
-
* device's id and re-asked on the `catalog-pull-backoff.ts` ladder.
|
|
19102
|
-
*
|
|
19103
|
-
*
|
|
19104
|
-
*
|
|
19152
|
+
* device's id and re-asked on the `catalog-pull-backoff.ts` ladder.
|
|
19153
|
+
*
|
|
19154
|
+
* The retract DESTROYS work (broker, restreamer, every writer and viewer on
|
|
19155
|
+
* it), so it is never decided by one read (D689, D49): a pull that omits a
|
|
19156
|
+
* stream only marks it, and the retract needs a later successful pull, at
|
|
19157
|
+
* least {@link CATALOG_ABSENCE_CONFIRM_MIN_MS} after the mark, to agree. A
|
|
19158
|
+
* failed pull, or one that answered nothing at all (`null`), decides
|
|
19159
|
+
* nothing. Neither does an answered EMPTY catalog: providers answer `[]`
|
|
19160
|
+
* both for "not mine" and for "don't know yet" (a sleeping Reolink battery
|
|
19161
|
+
* camera, a rebooting Hikvision), so it is not a read that may destroy work
|
|
19162
|
+
* — it neither marks nor clears. Genuine removal of a whole camera is the
|
|
19163
|
+
* `DeviceUnregistered` path and the departed-device sweep (D685).
|
|
19105
19164
|
*
|
|
19106
19165
|
* `trigger` decides whether the backoff applies — see
|
|
19107
19166
|
* {@link CatalogReconcileTrigger}. It defaults to `'event'` (always attempt)
|
|
@@ -19123,9 +19182,9 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19123
19182
|
});
|
|
19124
19183
|
return;
|
|
19125
19184
|
}
|
|
19126
|
-
let
|
|
19185
|
+
let answer;
|
|
19127
19186
|
try {
|
|
19128
|
-
|
|
19187
|
+
answer = await this.withCatalogTimeout(this.api.streamCatalog.getCatalog.query({ deviceId }), deviceId);
|
|
19129
19188
|
} catch (err) {
|
|
19130
19189
|
const next = nextCatalogPullFailureState(this.catalogPullFailures.get(deviceId), Date.now());
|
|
19131
19190
|
this.catalogPullFailures.set(deviceId, next);
|
|
@@ -19142,21 +19201,85 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19142
19201
|
}
|
|
19143
19202
|
if (this.catalogPullFailures.delete(deviceId)) this.logger.info("stream-catalog pull recovered — device is back in the catalog", {
|
|
19144
19203
|
tags: { deviceId },
|
|
19145
|
-
meta: { descriptors:
|
|
19204
|
+
meta: { descriptors: answer?.length ?? null }
|
|
19146
19205
|
});
|
|
19147
|
-
if (
|
|
19148
|
-
|
|
19206
|
+
if (answer === null || answer === void 0) {
|
|
19207
|
+
this.logger.debug("stream-catalog pull answered nothing — the registry is left as it is", {
|
|
19208
|
+
tags: { deviceId },
|
|
19209
|
+
meta: { trigger }
|
|
19210
|
+
});
|
|
19211
|
+
return;
|
|
19212
|
+
}
|
|
19213
|
+
const descriptors = answer;
|
|
19214
|
+
if (descriptors.length === 0) {
|
|
19215
|
+
this.logger.debug("stream-catalog pull answered empty — ignored: neither marks nor clears a stream (D689)", {
|
|
19216
|
+
tags: { deviceId },
|
|
19217
|
+
meta: {
|
|
19218
|
+
trigger,
|
|
19219
|
+
pendingMarks: this.catalogAbsenceMarks.get(deviceId)?.size ?? 0
|
|
19220
|
+
}
|
|
19221
|
+
});
|
|
19222
|
+
return;
|
|
19223
|
+
}
|
|
19149
19224
|
await this.publishCatalog(deviceId, descriptors);
|
|
19225
|
+
await this.retractStreamsTwoPullsOmitted(deviceId, descriptors, trigger);
|
|
19226
|
+
}
|
|
19227
|
+
/**
|
|
19228
|
+
* The retract half of a SUCCESSFUL catalog pull (D689): mark what this pull
|
|
19229
|
+
* omitted, unmark what it lists again, retract what two pulls a reconcile
|
|
19230
|
+
* interval apart both omitted. See `catalog-absence.ts`.
|
|
19231
|
+
*/
|
|
19232
|
+
async retractStreamsTwoPullsOmitted(deviceId, descriptors, trigger) {
|
|
19150
19233
|
const existing = this.cameraStreams.get(deviceId);
|
|
19151
|
-
|
|
19152
|
-
|
|
19153
|
-
|
|
19234
|
+
const candidates = existing ? Array.from(existing.values()).filter((cam) => !cam.brokerOwned).map((cam) => cam.camStreamId) : [];
|
|
19235
|
+
const verdict = judgeCatalogAbsence({
|
|
19236
|
+
marks: this.catalogAbsenceMarks.get(deviceId) ?? /* @__PURE__ */ new Map(),
|
|
19237
|
+
listed: new Set(descriptors.map((d) => d.camStreamId)),
|
|
19238
|
+
candidates,
|
|
19239
|
+
nowMs: Date.now(),
|
|
19240
|
+
confirmMinMs: CATALOG_ABSENCE_CONFIRM_MIN_MS
|
|
19241
|
+
});
|
|
19242
|
+
if (verdict.nextMarks.size > 0) this.catalogAbsenceMarks.set(deviceId, verdict.nextMarks);
|
|
19243
|
+
else this.catalogAbsenceMarks.delete(deviceId);
|
|
19244
|
+
for (const camStreamId of verdict.marked) this.logger.info("stream absent from the catalog — retracted only if a pull a reconcile interval later agrees", {
|
|
19245
|
+
tags: { deviceId },
|
|
19246
|
+
meta: {
|
|
19247
|
+
camStreamId,
|
|
19248
|
+
trigger,
|
|
19249
|
+
confirmAfterMs: CATALOG_ABSENCE_CONFIRM_MIN_MS
|
|
19250
|
+
}
|
|
19251
|
+
});
|
|
19252
|
+
for (const camStreamId of verdict.cleared) this.logger.info("stream is back in the catalog — pending retract cancelled", {
|
|
19253
|
+
tags: { deviceId },
|
|
19254
|
+
meta: {
|
|
19255
|
+
camStreamId,
|
|
19256
|
+
trigger
|
|
19257
|
+
}
|
|
19258
|
+
});
|
|
19259
|
+
for (const { camStreamId, absentForMs } of verdict.retract) {
|
|
19260
|
+
this.logger.warn("stream absent from two catalog pulls — retracted", {
|
|
19261
|
+
tags: { deviceId },
|
|
19262
|
+
meta: {
|
|
19263
|
+
camStreamId,
|
|
19264
|
+
absentForMs,
|
|
19265
|
+
trigger
|
|
19266
|
+
}
|
|
19267
|
+
});
|
|
19268
|
+
await this.retractCameraStream({
|
|
19154
19269
|
deviceId,
|
|
19155
19270
|
camStreamId,
|
|
19156
19271
|
preserveAssignment: true
|
|
19157
19272
|
});
|
|
19158
19273
|
}
|
|
19159
19274
|
}
|
|
19275
|
+
/** Forget one stream's pending catalog-absence mark — it was retracted by some path. */
|
|
19276
|
+
dropCatalogAbsenceMark(deviceId, camStreamId) {
|
|
19277
|
+
const marks = this.catalogAbsenceMarks.get(deviceId);
|
|
19278
|
+
if (!marks?.has(camStreamId)) return;
|
|
19279
|
+
const next = new Map([...marks].filter(([id]) => id !== camStreamId));
|
|
19280
|
+
if (next.size > 0) this.catalogAbsenceMarks.set(deviceId, next);
|
|
19281
|
+
else this.catalogAbsenceMarks.delete(deviceId);
|
|
19282
|
+
}
|
|
19160
19283
|
/**
|
|
19161
19284
|
* Enumerate every camera and reconcile its catalog. The reconcile backstop
|
|
19162
19285
|
* (poll cadence) and the start-up fetch. Per-device failures are isolated so
|
|
@@ -19176,6 +19299,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19176
19299
|
for (const d of cameras) if (d.disabled) {
|
|
19177
19300
|
const firstSeen = !this.disabledDevices.has(d.id);
|
|
19178
19301
|
this.disabledDevices.add(d.id);
|
|
19302
|
+
this.catalogAbsenceMarks.delete(d.id);
|
|
19179
19303
|
await this.releaseDisabledDeviceBrokers(d.id, firstSeen);
|
|
19180
19304
|
} else this.disabledDevices.delete(d.id);
|
|
19181
19305
|
const live = cameras.filter((d) => !d.disabled);
|
|
@@ -8494,6 +8494,41 @@ function resolveSourceParser(setting, sourceType) {
|
|
|
8494
8494
|
}
|
|
8495
8495
|
}
|
|
8496
8496
|
//#endregion
|
|
8497
|
+
//#region src/stream-broker/catalog-absence.ts
|
|
8498
|
+
function judgeCatalogAbsence(input) {
|
|
8499
|
+
const { marks, listed, candidates, nowMs, confirmMinMs } = input;
|
|
8500
|
+
const nextMarks = /* @__PURE__ */ new Map();
|
|
8501
|
+
const marked = [];
|
|
8502
|
+
const cleared = [];
|
|
8503
|
+
const retract = [];
|
|
8504
|
+
for (const camStreamId of candidates) {
|
|
8505
|
+
const markedAt = marks.get(camStreamId);
|
|
8506
|
+
if (listed.has(camStreamId)) {
|
|
8507
|
+
if (markedAt !== void 0) cleared.push(camStreamId);
|
|
8508
|
+
continue;
|
|
8509
|
+
}
|
|
8510
|
+
if (markedAt === void 0) {
|
|
8511
|
+
nextMarks.set(camStreamId, nowMs);
|
|
8512
|
+
marked.push(camStreamId);
|
|
8513
|
+
continue;
|
|
8514
|
+
}
|
|
8515
|
+
if (nowMs - markedAt < confirmMinMs) {
|
|
8516
|
+
nextMarks.set(camStreamId, markedAt);
|
|
8517
|
+
continue;
|
|
8518
|
+
}
|
|
8519
|
+
retract.push({
|
|
8520
|
+
camStreamId,
|
|
8521
|
+
absentForMs: nowMs - markedAt
|
|
8522
|
+
});
|
|
8523
|
+
}
|
|
8524
|
+
return {
|
|
8525
|
+
nextMarks,
|
|
8526
|
+
marked,
|
|
8527
|
+
cleared,
|
|
8528
|
+
retract
|
|
8529
|
+
};
|
|
8530
|
+
}
|
|
8531
|
+
//#endregion
|
|
8497
8532
|
//#region src/stream-broker/rtmp/flv-parser.ts
|
|
8498
8533
|
var AudioSoundFormat = /* @__PURE__ */ function(AudioSoundFormat) {
|
|
8499
8534
|
AudioSoundFormat[AudioSoundFormat["PCM_BE"] = 0] = "PCM_BE";
|
|
@@ -17094,6 +17129,13 @@ var RtspEnabledSchema = record(string(), boolean());
|
|
|
17094
17129
|
* one observation.
|
|
17095
17130
|
*/
|
|
17096
17131
|
var DEPARTURE_CONFIRM_MIN_MS = 3e4;
|
|
17132
|
+
/**
|
|
17133
|
+
* The catalog reconcile's second omission of a stream must be at least this
|
|
17134
|
+
* long after the first (D689) — the same one-poll-interval floor as the
|
|
17135
|
+
* departed-device sweep, for the same reason: an event-driven pull next to a
|
|
17136
|
+
* poll, or the startup pass's quick re-runs, are one observation.
|
|
17137
|
+
*/
|
|
17138
|
+
var CATALOG_ABSENCE_CONFIRM_MIN_MS = DEPARTURE_CONFIRM_MIN_MS;
|
|
17097
17139
|
var PUSH_KINDS = new Set(["push-annexb"]);
|
|
17098
17140
|
/** Walk a profile map and return the slot that points at `camStreamId`, or null. */
|
|
17099
17141
|
function findProfileForStream(map, camStreamId) {
|
|
@@ -17300,6 +17342,14 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
17300
17342
|
* agrees, and a reappearance clears it (D49).
|
|
17301
17343
|
*/
|
|
17302
17344
|
departureMarks = /* @__PURE__ */ new Map();
|
|
17345
|
+
/**
|
|
17346
|
+
* Per device: provider streams the broker holds that a successful catalog
|
|
17347
|
+
* pull OMITTED, and when (D689). A mark, not a verdict — see
|
|
17348
|
+
* `catalog-absence.ts`. Dropped when the stream is retracted by any path and
|
|
17349
|
+
* when the device is disabled (no pulls run then, so a mark would outlive
|
|
17350
|
+
* the observation it records).
|
|
17351
|
+
*/
|
|
17352
|
+
catalogAbsenceMarks = /* @__PURE__ */ new Map();
|
|
17303
17353
|
eventBus = null;
|
|
17304
17354
|
/**
|
|
17305
17355
|
* Per-broker stream health flag set by the watchdog. `undefined`
|
|
@@ -19030,6 +19080,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19030
19080
|
await this.releaseBroker(deviceId, camStreamId);
|
|
19031
19081
|
streamMap.delete(camStreamId);
|
|
19032
19082
|
if (streamMap.size === 0) this.cameraStreams.delete(deviceId);
|
|
19083
|
+
this.dropCatalogAbsenceMark(deviceId, camStreamId);
|
|
19033
19084
|
this.emitCamStreamsChanged(deviceId);
|
|
19034
19085
|
return { success: true };
|
|
19035
19086
|
}
|
|
@@ -19091,13 +19142,21 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19091
19142
|
}
|
|
19092
19143
|
/**
|
|
19093
19144
|
* Pull one camera's `stream-catalog` and reconcile its cam-streams: upsert
|
|
19094
|
-
* every descriptor, retract
|
|
19145
|
+
* every descriptor, retract a cam-stream the catalog no longer lists. A pull
|
|
19095
19146
|
* that throws (provider mid-restart, no `stream-catalog` cap, a camera whose
|
|
19096
19147
|
* session died) omits THIS DEVICE ONLY — it is logged at `warn` with the
|
|
19097
|
-
* device's id and re-asked on the `catalog-pull-backoff.ts` ladder.
|
|
19098
|
-
*
|
|
19099
|
-
*
|
|
19100
|
-
*
|
|
19148
|
+
* device's id and re-asked on the `catalog-pull-backoff.ts` ladder.
|
|
19149
|
+
*
|
|
19150
|
+
* The retract DESTROYS work (broker, restreamer, every writer and viewer on
|
|
19151
|
+
* it), so it is never decided by one read (D689, D49): a pull that omits a
|
|
19152
|
+
* stream only marks it, and the retract needs a later successful pull, at
|
|
19153
|
+
* least {@link CATALOG_ABSENCE_CONFIRM_MIN_MS} after the mark, to agree. A
|
|
19154
|
+
* failed pull, or one that answered nothing at all (`null`), decides
|
|
19155
|
+
* nothing. Neither does an answered EMPTY catalog: providers answer `[]`
|
|
19156
|
+
* both for "not mine" and for "don't know yet" (a sleeping Reolink battery
|
|
19157
|
+
* camera, a rebooting Hikvision), so it is not a read that may destroy work
|
|
19158
|
+
* — it neither marks nor clears. Genuine removal of a whole camera is the
|
|
19159
|
+
* `DeviceUnregistered` path and the departed-device sweep (D685).
|
|
19101
19160
|
*
|
|
19102
19161
|
* `trigger` decides whether the backoff applies — see
|
|
19103
19162
|
* {@link CatalogReconcileTrigger}. It defaults to `'event'` (always attempt)
|
|
@@ -19119,9 +19178,9 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19119
19178
|
});
|
|
19120
19179
|
return;
|
|
19121
19180
|
}
|
|
19122
|
-
let
|
|
19181
|
+
let answer;
|
|
19123
19182
|
try {
|
|
19124
|
-
|
|
19183
|
+
answer = await this.withCatalogTimeout(this.api.streamCatalog.getCatalog.query({ deviceId }), deviceId);
|
|
19125
19184
|
} catch (err) {
|
|
19126
19185
|
const next = nextCatalogPullFailureState(this.catalogPullFailures.get(deviceId), Date.now());
|
|
19127
19186
|
this.catalogPullFailures.set(deviceId, next);
|
|
@@ -19138,21 +19197,85 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19138
19197
|
}
|
|
19139
19198
|
if (this.catalogPullFailures.delete(deviceId)) this.logger.info("stream-catalog pull recovered — device is back in the catalog", {
|
|
19140
19199
|
tags: { deviceId },
|
|
19141
|
-
meta: { descriptors:
|
|
19200
|
+
meta: { descriptors: answer?.length ?? null }
|
|
19142
19201
|
});
|
|
19143
|
-
if (
|
|
19144
|
-
|
|
19202
|
+
if (answer === null || answer === void 0) {
|
|
19203
|
+
this.logger.debug("stream-catalog pull answered nothing — the registry is left as it is", {
|
|
19204
|
+
tags: { deviceId },
|
|
19205
|
+
meta: { trigger }
|
|
19206
|
+
});
|
|
19207
|
+
return;
|
|
19208
|
+
}
|
|
19209
|
+
const descriptors = answer;
|
|
19210
|
+
if (descriptors.length === 0) {
|
|
19211
|
+
this.logger.debug("stream-catalog pull answered empty — ignored: neither marks nor clears a stream (D689)", {
|
|
19212
|
+
tags: { deviceId },
|
|
19213
|
+
meta: {
|
|
19214
|
+
trigger,
|
|
19215
|
+
pendingMarks: this.catalogAbsenceMarks.get(deviceId)?.size ?? 0
|
|
19216
|
+
}
|
|
19217
|
+
});
|
|
19218
|
+
return;
|
|
19219
|
+
}
|
|
19145
19220
|
await this.publishCatalog(deviceId, descriptors);
|
|
19221
|
+
await this.retractStreamsTwoPullsOmitted(deviceId, descriptors, trigger);
|
|
19222
|
+
}
|
|
19223
|
+
/**
|
|
19224
|
+
* The retract half of a SUCCESSFUL catalog pull (D689): mark what this pull
|
|
19225
|
+
* omitted, unmark what it lists again, retract what two pulls a reconcile
|
|
19226
|
+
* interval apart both omitted. See `catalog-absence.ts`.
|
|
19227
|
+
*/
|
|
19228
|
+
async retractStreamsTwoPullsOmitted(deviceId, descriptors, trigger) {
|
|
19146
19229
|
const existing = this.cameraStreams.get(deviceId);
|
|
19147
|
-
|
|
19148
|
-
|
|
19149
|
-
|
|
19230
|
+
const candidates = existing ? Array.from(existing.values()).filter((cam) => !cam.brokerOwned).map((cam) => cam.camStreamId) : [];
|
|
19231
|
+
const verdict = judgeCatalogAbsence({
|
|
19232
|
+
marks: this.catalogAbsenceMarks.get(deviceId) ?? /* @__PURE__ */ new Map(),
|
|
19233
|
+
listed: new Set(descriptors.map((d) => d.camStreamId)),
|
|
19234
|
+
candidates,
|
|
19235
|
+
nowMs: Date.now(),
|
|
19236
|
+
confirmMinMs: CATALOG_ABSENCE_CONFIRM_MIN_MS
|
|
19237
|
+
});
|
|
19238
|
+
if (verdict.nextMarks.size > 0) this.catalogAbsenceMarks.set(deviceId, verdict.nextMarks);
|
|
19239
|
+
else this.catalogAbsenceMarks.delete(deviceId);
|
|
19240
|
+
for (const camStreamId of verdict.marked) this.logger.info("stream absent from the catalog — retracted only if a pull a reconcile interval later agrees", {
|
|
19241
|
+
tags: { deviceId },
|
|
19242
|
+
meta: {
|
|
19243
|
+
camStreamId,
|
|
19244
|
+
trigger,
|
|
19245
|
+
confirmAfterMs: CATALOG_ABSENCE_CONFIRM_MIN_MS
|
|
19246
|
+
}
|
|
19247
|
+
});
|
|
19248
|
+
for (const camStreamId of verdict.cleared) this.logger.info("stream is back in the catalog — pending retract cancelled", {
|
|
19249
|
+
tags: { deviceId },
|
|
19250
|
+
meta: {
|
|
19251
|
+
camStreamId,
|
|
19252
|
+
trigger
|
|
19253
|
+
}
|
|
19254
|
+
});
|
|
19255
|
+
for (const { camStreamId, absentForMs } of verdict.retract) {
|
|
19256
|
+
this.logger.warn("stream absent from two catalog pulls — retracted", {
|
|
19257
|
+
tags: { deviceId },
|
|
19258
|
+
meta: {
|
|
19259
|
+
camStreamId,
|
|
19260
|
+
absentForMs,
|
|
19261
|
+
trigger
|
|
19262
|
+
}
|
|
19263
|
+
});
|
|
19264
|
+
await this.retractCameraStream({
|
|
19150
19265
|
deviceId,
|
|
19151
19266
|
camStreamId,
|
|
19152
19267
|
preserveAssignment: true
|
|
19153
19268
|
});
|
|
19154
19269
|
}
|
|
19155
19270
|
}
|
|
19271
|
+
/** Forget one stream's pending catalog-absence mark — it was retracted by some path. */
|
|
19272
|
+
dropCatalogAbsenceMark(deviceId, camStreamId) {
|
|
19273
|
+
const marks = this.catalogAbsenceMarks.get(deviceId);
|
|
19274
|
+
if (!marks?.has(camStreamId)) return;
|
|
19275
|
+
const next = new Map([...marks].filter(([id]) => id !== camStreamId));
|
|
19276
|
+
if (next.size > 0) this.catalogAbsenceMarks.set(deviceId, next);
|
|
19277
|
+
else this.catalogAbsenceMarks.delete(deviceId);
|
|
19278
|
+
}
|
|
19156
19279
|
/**
|
|
19157
19280
|
* Enumerate every camera and reconcile its catalog. The reconcile backstop
|
|
19158
19281
|
* (poll cadence) and the start-up fetch. Per-device failures are isolated so
|
|
@@ -19172,6 +19295,7 @@ var StreamBrokerManager = class StreamBrokerManager {
|
|
|
19172
19295
|
for (const d of cameras) if (d.disabled) {
|
|
19173
19296
|
const firstSeen = !this.disabledDevices.has(d.id);
|
|
19174
19297
|
this.disabledDevices.add(d.id);
|
|
19298
|
+
this.catalogAbsenceMarks.delete(d.id);
|
|
19175
19299
|
await this.releaseDisabledDeviceBrokers(d.id, firstSeen);
|
|
19176
19300
|
} else this.disabledDevices.delete(d.id);
|
|
19177
19301
|
const live = cameras.filter((d) => !d.disabled);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.312",
|
|
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",
|