@camstack/addon-pipeline 1.2.232 → 1.2.234
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/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-DcB4--Xd.js → default-detection-model-CDhXxOJQ.js} +1 -1
- package/dist/{default-detection-model-DNsmwQx0.mjs → default-detection-model-I8kuDTEi.mjs} +1 -1
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-BwQxOtXy.mjs → dist-But4XMDJ.mjs} +61 -17
- package/dist/{dist-CoRJjU1W.js → dist-NX0e_oWp.js} +61 -17
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-BjScdHzE.mjs → node-BHxRrsoX.mjs} +1 -1
- package/dist/{node-DvcGhYd4.js → node-BgM4n8D4.js} +1 -1
- package/dist/pipeline-runner/index.js +205 -17
- package/dist/pipeline-runner/index.mjs +205 -17
- package/dist/{process-memory-62FAABqk.mjs → process-memory-C3-EOjjf.mjs} +1 -1
- package/dist/{process-memory-CIOO13cP.js → process-memory-C5_Mo9TI.js} +1 -1
- package/dist/recorder/index.js +3 -2
- package/dist/recorder/index.mjs +3 -2
- package/dist/{segment-demux-js-xLE3PxMz.js → segment-demux-js-BAD7i9RI.js} +1 -1
- package/dist/{segment-demux-js-CCnlSDIN.mjs → segment-demux-js-CVZACSOh.mjs} +1 -1
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BElWMGVT.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BWvbnTku.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D3XkHB88.mjs +26 -0
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-CU5lZ8MZ.mjs → hostInit-CoOZAV_n.mjs} +2 -2
- package/dist/stream-broker/index.js +3 -3
- package/dist/stream-broker/index.mjs +3 -3
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cdsf1_-A.mjs +0 -26
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_process_memory = require("../process-memory-
|
|
6
|
+
const require_dist = require("../dist-NX0e_oWp.js");
|
|
7
|
+
const require_process_memory = require("../process-memory-C5_Mo9TI.js");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
10
10
|
let node_path = require("node:path");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __require } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { $t as hydrateSchema, At as resolvePoolMemoryPolicy, Kt as BaseAddon, N as audioAnalysisCapability, P as audioAnalyzerCapability, Qt as expandAudioChunkToF32le, Xt as audioChunkBytesPerSample, bn as EventCategory, ct as mapAudioLabelToMacro, n as AUDIO_BACKEND_CHOICES, rn as nodePin, s as DEFAULT_AUDIO_ANALYZER_CONFIG, w as PoolMemoryWatchdog, y as HF_BASE_URL, zt as errMsg } from "../dist-
|
|
3
|
-
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-
|
|
2
|
+
import { $t as hydrateSchema, At as resolvePoolMemoryPolicy, Kt as BaseAddon, N as audioAnalysisCapability, P as audioAnalyzerCapability, Qt as expandAudioChunkToF32le, Xt as audioChunkBytesPerSample, bn as EventCategory, ct as mapAudioLabelToMacro, n as AUDIO_BACKEND_CHOICES, rn as nodePin, s as DEFAULT_AUDIO_ANALYZER_CONFIG, w as PoolMemoryWatchdog, y as HF_BASE_URL, zt as errMsg } from "../dist-But4XMDJ.mjs";
|
|
3
|
+
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-C3-EOjjf.mjs";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path$1 from "node:path";
|
|
6
6
|
import { downloadFile } from "@camstack/system/addon-utils";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as audioKindId, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS, rt as hfModelUrl } from "./dist-
|
|
1
|
+
import { F as audioKindId, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS, rt as hfModelUrl } from "./dist-But4XMDJ.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
//#region src/detection-pipeline/pipeline/landmark-precision-gate.ts
|
|
4
4
|
/**
|
|
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
8
|
-
const require_default_detection_model = require("../default-detection-model-
|
|
6
|
+
const require_dist = require("../dist-NX0e_oWp.js");
|
|
7
|
+
const require_node = require("../node-BgM4n8D4.js");
|
|
8
|
+
const require_default_detection_model = require("../default-detection-model-CDhXxOJQ.js");
|
|
9
9
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
10
|
-
const require_process_memory = require("../process-memory-
|
|
10
|
+
const require_process_memory = require("../process-memory-C5_Mo9TI.js");
|
|
11
11
|
let node_os = require("node:os");
|
|
12
12
|
node_os = require_chunk.__toESM(node_os);
|
|
13
13
|
let node_fs = require("node:fs");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as __require } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { $t as hydrateSchema, At as resolvePoolMemoryPolicy, Dt as resolveClusterStepModelId, H as defaultDeviceFor$1, Kt as BaseAddon, Lt as supportedRuntimes$1, Mt as runtimeDevices$1, Q as evaluateZoneRules, X as enumerateInferenceDevices, Zt as createEvent, _t as pickClusterStepModels, bn as EventCategory, c as DEFAULT_CLUSTER_STEP_MODELS, hn as object, ht as overlayClusterStepSettings, in as parseJsonUnknown, it as inferModelProvider, j as YAMNET_TO_MACRO, l as DEFAULT_CLUSTER_STEP_SETTINGS, ln as array, m as DEVICE_BACKEND_TO_FORMAT, ot as loadContributionCapability, q as detectionPipelineCapability, rn as nodePin, sn as sleep, t as APPLE_SA_TO_MACRO, vn as string, vt as pickClusterStepSettings, w as PoolMemoryWatchdog, xt as pipelineExecutorCapability, yn as union, zt as errMsg } from "../dist-
|
|
3
|
-
import { o as readProcessCost } from "../node-
|
|
4
|
-
import { a as ALL_PIPELINE_STEPS, c as getStep, d as landmarkPrecisionVerdict, l as getStepDefinition, n as resolveDefaultDetectionModel, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as resolveModelForFormat } from "../default-detection-model-
|
|
2
|
+
import { $t as hydrateSchema, At as resolvePoolMemoryPolicy, Dt as resolveClusterStepModelId, H as defaultDeviceFor$1, Kt as BaseAddon, Lt as supportedRuntimes$1, Mt as runtimeDevices$1, Q as evaluateZoneRules, X as enumerateInferenceDevices, Zt as createEvent, _t as pickClusterStepModels, bn as EventCategory, c as DEFAULT_CLUSTER_STEP_MODELS, hn as object, ht as overlayClusterStepSettings, in as parseJsonUnknown, it as inferModelProvider, j as YAMNET_TO_MACRO, l as DEFAULT_CLUSTER_STEP_SETTINGS, ln as array, m as DEVICE_BACKEND_TO_FORMAT, ot as loadContributionCapability, q as detectionPipelineCapability, rn as nodePin, sn as sleep, t as APPLE_SA_TO_MACRO, vn as string, vt as pickClusterStepSettings, w as PoolMemoryWatchdog, xt as pipelineExecutorCapability, yn as union, zt as errMsg } from "../dist-But4XMDJ.mjs";
|
|
3
|
+
import { o as readProcessCost } from "../node-BHxRrsoX.mjs";
|
|
4
|
+
import { a as ALL_PIPELINE_STEPS, c as getStep, d as landmarkPrecisionVerdict, l as getStepDefinition, n as resolveDefaultDetectionModel, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as resolveModelForFormat } from "../default-detection-model-I8kuDTEi.mjs";
|
|
5
5
|
import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
6
|
-
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-
|
|
6
|
+
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-C3-EOjjf.mjs";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
9
|
import * as path$1 from "node:path";
|
|
@@ -23916,6 +23916,14 @@ var MotionSourceEnum = _enum([
|
|
|
23916
23916
|
*/
|
|
23917
23917
|
var MotionSourcesSchema = array(MotionSourceEnum);
|
|
23918
23918
|
/**
|
|
23919
|
+
* Which root detectors a camera runs. Deliberately the SAME vocabulary
|
|
23920
|
+
* post-analysis already tags every detection with (`DetectionSource`) rather
|
|
23921
|
+
* than a second spelling of the same two ideas — the value an operator picks
|
|
23922
|
+
* here is the value that comes back on the track, the overlay and the debug
|
|
23923
|
+
* row.
|
|
23924
|
+
*/
|
|
23925
|
+
var DetectionSourcesSchema = array(DetectionSourceSchema);
|
|
23926
|
+
/**
|
|
23919
23927
|
* Input shape for `pipeline-runner.reportMotion` cap method. Exported
|
|
23920
23928
|
* so cap-side consumers (the orchestrator forward, the runner addon's
|
|
23921
23929
|
* cap implementation, tests) can reuse the type instead of redeclaring
|
|
@@ -24066,6 +24074,35 @@ var RunnerCameraConfigSchema = object({
|
|
|
24066
24074
|
* events; the orchestrator forwards to `reportMotion`.
|
|
24067
24075
|
*/
|
|
24068
24076
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
24077
|
+
/**
|
|
24078
|
+
* WHICH root detector runs for this camera. The detection counterpart of
|
|
24079
|
+
* {@link motionSources}, same shape and same discipline: a per-camera list
|
|
24080
|
+
* of sources the system understands, vendor-agnostic, owned here.
|
|
24081
|
+
*
|
|
24082
|
+
* - `pipeline` — this runner's own root step on decoded pixels (`steps`).
|
|
24083
|
+
* - `onboard` — the CAMERA's own detector, admitted only when it is named
|
|
24084
|
+
* here. Its boxes reach post-analysis on the same
|
|
24085
|
+
* `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
|
|
24086
|
+
* track gets the same tracker, the same overlay and the same detail
|
|
24087
|
+
* subtree (face / plate / embedding).
|
|
24088
|
+
*
|
|
24089
|
+
* `['onboard']` alone is the REPLACEMENT: this runner's root step does not
|
|
24090
|
+
* run, and that is where a camera with a usable onboard detector buys back
|
|
24091
|
+
* its share of the accelerator. It saves the root inference, not the decode
|
|
24092
|
+
* — the detail subtree still needs pixels to cut crops from.
|
|
24093
|
+
*
|
|
24094
|
+
* A camera whose onboard detections carry no geometry cannot serve as a root
|
|
24095
|
+
* step at all (there is nothing to track), and the cap that knows this says
|
|
24096
|
+
* so on `native-object-detection.getOptions().geometry`. On Reolink that is
|
|
24097
|
+
* every battery camera: the boxes ride a sub-stream a battery camera never
|
|
24098
|
+
* attaches, so such a camera stays on `['pipeline']` and its onboard AI is
|
|
24099
|
+
* worth exactly what it already is — a wake signal with a class.
|
|
24100
|
+
*
|
|
24101
|
+
* Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
|
|
24102
|
+
* vendor never turns a detector on for the operator, and neither does a
|
|
24103
|
+
* default.
|
|
24104
|
+
*/
|
|
24105
|
+
detectionSources: DetectionSourcesSchema.default(["pipeline"]),
|
|
24069
24106
|
pipelineEnabled: boolean().default(true),
|
|
24070
24107
|
/** Ordered tree of video steps. Absent → runner skips video detection. */
|
|
24071
24108
|
steps: array(PipelineStepInputSchema).readonly().optional(),
|
|
@@ -30124,31 +30161,38 @@ var NativeObjectDetectionStatusSchema = object({
|
|
|
30124
30161
|
/**
|
|
30125
30162
|
* Whether forwarding of onboard AI detections is enabled for this device.
|
|
30126
30163
|
*
|
|
30127
|
-
*
|
|
30128
|
-
*
|
|
30129
|
-
*
|
|
30130
|
-
*
|
|
30131
|
-
*
|
|
30132
|
-
* costs. {@link NativeObjectDetectionOptionsSchema.geometry} is how the UI
|
|
30133
|
-
* says which of the two a camera is.
|
|
30164
|
+
* Cold-start OFF on every camera. A vendor does not decide which detector a
|
|
30165
|
+
* camera runs — that is the operator's choice, and it is made where every
|
|
30166
|
+
* other detection choice is made. `geometry` on
|
|
30167
|
+
* {@link NativeObjectDetectionOptionsSchema} is how the form says what the
|
|
30168
|
+
* choice would buy this particular camera.
|
|
30134
30169
|
*/
|
|
30135
30170
|
enabled: boolean()
|
|
30136
30171
|
});
|
|
30137
30172
|
/**
|
|
30138
|
-
*
|
|
30173
|
+
* WHEN a camera's onboard detections carry geometry.
|
|
30139
30174
|
*
|
|
30140
|
-
* `boxed` —
|
|
30141
|
-
* a subject the
|
|
30142
|
-
* `
|
|
30143
|
-
*
|
|
30144
|
-
*
|
|
30145
|
-
*
|
|
30175
|
+
* - `boxed` — always. The provider holds a standing channel for the boxes, so
|
|
30176
|
+
* a detection is a trackable subject whenever the camera sees one.
|
|
30177
|
+
* - `while-streaming` — only while some stream of this camera is already open.
|
|
30178
|
+
* The boxes ride the video's own side-channel rather than a feed of their
|
|
30179
|
+
* own, so they cost nothing and they exist only when something is pulling.
|
|
30180
|
+
* On Reolink this is every BATTERY camera: a persistent second feed is real
|
|
30181
|
+
* radio drain, but a camera that is awake is awake precisely because
|
|
30182
|
+
* something is in front of it, and its stream is already being pulled.
|
|
30183
|
+
* - `flag-only` — never. The firmware ships the class and nothing else; the
|
|
30184
|
+
* pipeline needs geometry to make a subject, so such a push reaches the
|
|
30185
|
+
* tracker as motion and no further.
|
|
30146
30186
|
*
|
|
30147
30187
|
* Reported and not inferred, because the operator's question in front of the
|
|
30148
|
-
*
|
|
30149
|
-
*
|
|
30188
|
+
* picker is "what do I get", and these are three different answers (D14: the
|
|
30189
|
+
* derived form is only as honest as `getOptions`).
|
|
30150
30190
|
*/
|
|
30151
|
-
var NativeObjectGeometryEnum = _enum([
|
|
30191
|
+
var NativeObjectGeometryEnum = _enum([
|
|
30192
|
+
"boxed",
|
|
30193
|
+
"while-streaming",
|
|
30194
|
+
"flag-only"
|
|
30195
|
+
]);
|
|
30152
30196
|
var NativeObjectDetectionOptionsSchema = object({
|
|
30153
30197
|
/** Classes this firmware can detect — the same list the status reports. */
|
|
30154
30198
|
supportedClasses: array(NativeObjectClassEnum).readonly(),
|
|
@@ -23916,6 +23916,14 @@ var MotionSourceEnum = _enum([
|
|
|
23916
23916
|
*/
|
|
23917
23917
|
var MotionSourcesSchema = array(MotionSourceEnum);
|
|
23918
23918
|
/**
|
|
23919
|
+
* Which root detectors a camera runs. Deliberately the SAME vocabulary
|
|
23920
|
+
* post-analysis already tags every detection with (`DetectionSource`) rather
|
|
23921
|
+
* than a second spelling of the same two ideas — the value an operator picks
|
|
23922
|
+
* here is the value that comes back on the track, the overlay and the debug
|
|
23923
|
+
* row.
|
|
23924
|
+
*/
|
|
23925
|
+
var DetectionSourcesSchema = array(DetectionSourceSchema);
|
|
23926
|
+
/**
|
|
23919
23927
|
* Input shape for `pipeline-runner.reportMotion` cap method. Exported
|
|
23920
23928
|
* so cap-side consumers (the orchestrator forward, the runner addon's
|
|
23921
23929
|
* cap implementation, tests) can reuse the type instead of redeclaring
|
|
@@ -24066,6 +24074,35 @@ var RunnerCameraConfigSchema = object({
|
|
|
24066
24074
|
* events; the orchestrator forwards to `reportMotion`.
|
|
24067
24075
|
*/
|
|
24068
24076
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
24077
|
+
/**
|
|
24078
|
+
* WHICH root detector runs for this camera. The detection counterpart of
|
|
24079
|
+
* {@link motionSources}, same shape and same discipline: a per-camera list
|
|
24080
|
+
* of sources the system understands, vendor-agnostic, owned here.
|
|
24081
|
+
*
|
|
24082
|
+
* - `pipeline` — this runner's own root step on decoded pixels (`steps`).
|
|
24083
|
+
* - `onboard` — the CAMERA's own detector, admitted only when it is named
|
|
24084
|
+
* here. Its boxes reach post-analysis on the same
|
|
24085
|
+
* `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
|
|
24086
|
+
* track gets the same tracker, the same overlay and the same detail
|
|
24087
|
+
* subtree (face / plate / embedding).
|
|
24088
|
+
*
|
|
24089
|
+
* `['onboard']` alone is the REPLACEMENT: this runner's root step does not
|
|
24090
|
+
* run, and that is where a camera with a usable onboard detector buys back
|
|
24091
|
+
* its share of the accelerator. It saves the root inference, not the decode
|
|
24092
|
+
* — the detail subtree still needs pixels to cut crops from.
|
|
24093
|
+
*
|
|
24094
|
+
* A camera whose onboard detections carry no geometry cannot serve as a root
|
|
24095
|
+
* step at all (there is nothing to track), and the cap that knows this says
|
|
24096
|
+
* so on `native-object-detection.getOptions().geometry`. On Reolink that is
|
|
24097
|
+
* every battery camera: the boxes ride a sub-stream a battery camera never
|
|
24098
|
+
* attaches, so such a camera stays on `['pipeline']` and its onboard AI is
|
|
24099
|
+
* worth exactly what it already is — a wake signal with a class.
|
|
24100
|
+
*
|
|
24101
|
+
* Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
|
|
24102
|
+
* vendor never turns a detector on for the operator, and neither does a
|
|
24103
|
+
* default.
|
|
24104
|
+
*/
|
|
24105
|
+
detectionSources: DetectionSourcesSchema.default(["pipeline"]),
|
|
24069
24106
|
pipelineEnabled: boolean().default(true),
|
|
24070
24107
|
/** Ordered tree of video steps. Absent → runner skips video detection. */
|
|
24071
24108
|
steps: array(PipelineStepInputSchema).readonly().optional(),
|
|
@@ -30124,31 +30161,38 @@ var NativeObjectDetectionStatusSchema = object({
|
|
|
30124
30161
|
/**
|
|
30125
30162
|
* Whether forwarding of onboard AI detections is enabled for this device.
|
|
30126
30163
|
*
|
|
30127
|
-
*
|
|
30128
|
-
*
|
|
30129
|
-
*
|
|
30130
|
-
*
|
|
30131
|
-
*
|
|
30132
|
-
* costs. {@link NativeObjectDetectionOptionsSchema.geometry} is how the UI
|
|
30133
|
-
* says which of the two a camera is.
|
|
30164
|
+
* Cold-start OFF on every camera. A vendor does not decide which detector a
|
|
30165
|
+
* camera runs — that is the operator's choice, and it is made where every
|
|
30166
|
+
* other detection choice is made. `geometry` on
|
|
30167
|
+
* {@link NativeObjectDetectionOptionsSchema} is how the form says what the
|
|
30168
|
+
* choice would buy this particular camera.
|
|
30134
30169
|
*/
|
|
30135
30170
|
enabled: boolean()
|
|
30136
30171
|
});
|
|
30137
30172
|
/**
|
|
30138
|
-
*
|
|
30173
|
+
* WHEN a camera's onboard detections carry geometry.
|
|
30139
30174
|
*
|
|
30140
|
-
* `boxed` —
|
|
30141
|
-
* a subject the
|
|
30142
|
-
* `
|
|
30143
|
-
*
|
|
30144
|
-
*
|
|
30145
|
-
*
|
|
30175
|
+
* - `boxed` — always. The provider holds a standing channel for the boxes, so
|
|
30176
|
+
* a detection is a trackable subject whenever the camera sees one.
|
|
30177
|
+
* - `while-streaming` — only while some stream of this camera is already open.
|
|
30178
|
+
* The boxes ride the video's own side-channel rather than a feed of their
|
|
30179
|
+
* own, so they cost nothing and they exist only when something is pulling.
|
|
30180
|
+
* On Reolink this is every BATTERY camera: a persistent second feed is real
|
|
30181
|
+
* radio drain, but a camera that is awake is awake precisely because
|
|
30182
|
+
* something is in front of it, and its stream is already being pulled.
|
|
30183
|
+
* - `flag-only` — never. The firmware ships the class and nothing else; the
|
|
30184
|
+
* pipeline needs geometry to make a subject, so such a push reaches the
|
|
30185
|
+
* tracker as motion and no further.
|
|
30146
30186
|
*
|
|
30147
30187
|
* Reported and not inferred, because the operator's question in front of the
|
|
30148
|
-
*
|
|
30149
|
-
*
|
|
30188
|
+
* picker is "what do I get", and these are three different answers (D14: the
|
|
30189
|
+
* derived form is only as honest as `getOptions`).
|
|
30150
30190
|
*/
|
|
30151
|
-
var NativeObjectGeometryEnum = _enum([
|
|
30191
|
+
var NativeObjectGeometryEnum = _enum([
|
|
30192
|
+
"boxed",
|
|
30193
|
+
"while-streaming",
|
|
30194
|
+
"flag-only"
|
|
30195
|
+
]);
|
|
30152
30196
|
var NativeObjectDetectionOptionsSchema = object({
|
|
30153
30197
|
/** Classes this firmware can detect — the same list the status reports. */
|
|
30154
30198
|
supportedClasses: array(NativeObjectClassEnum).readonly(),
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-NX0e_oWp.js");
|
|
6
6
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $t as hydrateSchema, Kt as BaseAddon, Q as evaluateZoneRules, Yt as DeviceType, mt as motionDetectionCapability } from "../dist-
|
|
1
|
+
import { $t as hydrateSchema, Kt as BaseAddon, Q as evaluateZoneRules, Yt as DeviceType, mt as motionDetectionCapability } from "../dist-But4XMDJ.mjs";
|
|
2
2
|
import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ht as buildFfmpegArgs, Vt as Fmp4BoxSplitter, Wt as isSoftwareDecode, zt as errMsg } from "./dist-
|
|
1
|
+
import { Ht as buildFfmpegArgs, Vt as Fmp4BoxSplitter, Wt as isSoftwareDecode, zt as errMsg } from "./dist-But4XMDJ.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-emK7D4bc.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-NX0e_oWp.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
node_fs = require_chunk.__toESM(node_fs, 1);
|
|
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
8
|
-
const require_default_detection_model = require("../default-detection-model-
|
|
6
|
+
const require_dist = require("../dist-NX0e_oWp.js");
|
|
7
|
+
const require_node = require("../node-BgM4n8D4.js");
|
|
8
|
+
const require_default_detection_model = require("../default-detection-model-CDhXxOJQ.js");
|
|
9
9
|
const require_worker_protocol = require("../worker-protocol-B2MfQLlu.js");
|
|
10
10
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
11
11
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
@@ -2096,6 +2096,73 @@ function startSessionDecodePump(deps) {
|
|
|
2096
2096
|
iterator.return().catch(() => {});
|
|
2097
2097
|
};
|
|
2098
2098
|
}
|
|
2099
|
+
/**
|
|
2100
|
+
* Read the camera's `detectionSources`.
|
|
2101
|
+
*
|
|
2102
|
+
* ABSENT is the pipeline — a runner or an attach older than the field behaves
|
|
2103
|
+
* exactly as it always did. EMPTY is not the same thing and must never be
|
|
2104
|
+
* widened into "everything": it is a camera an operator left with no root
|
|
2105
|
+
* detector, and inventing one for them is how a default acts without anybody
|
|
2106
|
+
* choosing it.
|
|
2107
|
+
*/
|
|
2108
|
+
function planDetectionSources(sources) {
|
|
2109
|
+
if (sources === void 0) return {
|
|
2110
|
+
runsPipeline: true,
|
|
2111
|
+
runsOnboard: false
|
|
2112
|
+
};
|
|
2113
|
+
return {
|
|
2114
|
+
runsPipeline: sources.includes("pipeline"),
|
|
2115
|
+
runsOnboard: sources.includes("onboard")
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
/** Clamp a denormalised box into the frame; a firmware box may exceed it. */
|
|
2119
|
+
function clamp(value, max) {
|
|
2120
|
+
return Math.max(0, Math.min(value, max));
|
|
2121
|
+
}
|
|
2122
|
+
/**
|
|
2123
|
+
* Pair the camera's last detections with a decoded frame, in the FRAME's pixel
|
|
2124
|
+
* space.
|
|
2125
|
+
*
|
|
2126
|
+
* The old converter denormalised against a nominal 1000×1000 when the push
|
|
2127
|
+
* carried no dimensions, and documented that consumers should treat a native
|
|
2128
|
+
* bbox as relative. That was survivable while nothing cut pixels from it. Here
|
|
2129
|
+
* the boxes travel with a real frame and a real handle, so they are scaled to
|
|
2130
|
+
* that frame — the same space every other detection on this plane lives in.
|
|
2131
|
+
*/
|
|
2132
|
+
function onboardRootDetections(slot, frame) {
|
|
2133
|
+
if (slot === null || slot.detections.length === 0) return { kind: "none" };
|
|
2134
|
+
const ageMs = Math.abs(frame.timestamp - slot.at);
|
|
2135
|
+
if (ageMs > 1e3) return {
|
|
2136
|
+
kind: "expired",
|
|
2137
|
+
ageMs
|
|
2138
|
+
};
|
|
2139
|
+
const boxed = slot.detections.filter((d) => d.bbox != null);
|
|
2140
|
+
if (boxed.length === 0) return {
|
|
2141
|
+
kind: "no-geometry",
|
|
2142
|
+
classes: slot.detections.map((d) => d.class)
|
|
2143
|
+
};
|
|
2144
|
+
return {
|
|
2145
|
+
kind: "paired",
|
|
2146
|
+
detections: boxed.map((d, i) => {
|
|
2147
|
+
const [nx, ny, nw, nh] = d.bbox;
|
|
2148
|
+
const x = clamp(nx * frame.width, frame.width);
|
|
2149
|
+
const y = clamp(ny * frame.height, frame.height);
|
|
2150
|
+
return {
|
|
2151
|
+
id: `onboard-${String(slot.at)}-${String(i)}`,
|
|
2152
|
+
kind: "first-level",
|
|
2153
|
+
macroClass: d.class,
|
|
2154
|
+
score: d.confidence ?? 1,
|
|
2155
|
+
labels: [],
|
|
2156
|
+
bbox: {
|
|
2157
|
+
x,
|
|
2158
|
+
y,
|
|
2159
|
+
width: clamp(nw * frame.width, frame.width - x),
|
|
2160
|
+
height: clamp(nh * frame.height, frame.height - y)
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
})
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2099
2166
|
//#endregion
|
|
2100
2167
|
//#region src/pipeline-runner/bench-step-schema.ts
|
|
2101
2168
|
/**
|
|
@@ -5644,6 +5711,8 @@ var PipelineRunner = class {
|
|
|
5644
5711
|
cameras = /* @__PURE__ */ new Map();
|
|
5645
5712
|
semaphore;
|
|
5646
5713
|
resultCallbacks = [];
|
|
5714
|
+
/** Last report per `deviceId:reason` for {@link noteOnboardRootIdle}. */
|
|
5715
|
+
onboardIdleReportedAt = /* @__PURE__ */ new Map();
|
|
5647
5716
|
defaultRoundRobinKeys = [];
|
|
5648
5717
|
defaultRoundRobinIndex = 0;
|
|
5649
5718
|
intervalHandle = null;
|
|
@@ -5724,6 +5793,7 @@ var PipelineRunner = class {
|
|
|
5724
5793
|
const initialPhase = registration.detectionMode === "disabled" ? "idle" : registration.detectionMode === "always-on" ? "active" : "watching";
|
|
5725
5794
|
const state = {
|
|
5726
5795
|
registration,
|
|
5796
|
+
onboardSlot: null,
|
|
5727
5797
|
motionQueue,
|
|
5728
5798
|
detectionQueue,
|
|
5729
5799
|
preRollQueue: new PreRollLane({
|
|
@@ -6200,6 +6270,15 @@ var PipelineRunner = class {
|
|
|
6200
6270
|
const { frame, handle } = entry;
|
|
6201
6271
|
state.inflight += 1;
|
|
6202
6272
|
const enqueuedAt = frame._enqueuedAt ?? pickedAt;
|
|
6273
|
+
const plan = planDetectionSources(state.registration.detectionSources);
|
|
6274
|
+
if (!plan.runsPipeline) {
|
|
6275
|
+
try {
|
|
6276
|
+
await this.processOnboardRoot(deviceId, entry, state, streamType, plan);
|
|
6277
|
+
} finally {
|
|
6278
|
+
state.inflight -= 1;
|
|
6279
|
+
}
|
|
6280
|
+
return;
|
|
6281
|
+
}
|
|
6203
6282
|
const rawRelease = await this.semaphore.acquire();
|
|
6204
6283
|
let released = false;
|
|
6205
6284
|
const release = () => {
|
|
@@ -6223,7 +6302,7 @@ var PipelineRunner = class {
|
|
|
6223
6302
|
state.processedCount++;
|
|
6224
6303
|
if (result) {
|
|
6225
6304
|
if (result.shed === true) state.shedFrames++;
|
|
6226
|
-
await this.notifyCallbacks(deviceId, frame, result, streamType, handle);
|
|
6305
|
+
await this.notifyCallbacks(deviceId, frame, result, streamType, handle, "pipeline");
|
|
6227
6306
|
frameReleaseReason = "success";
|
|
6228
6307
|
const emittedAt = Date.now();
|
|
6229
6308
|
const capturedAt = frame.capturedAt;
|
|
@@ -6250,6 +6329,96 @@ var PipelineRunner = class {
|
|
|
6250
6329
|
}
|
|
6251
6330
|
}
|
|
6252
6331
|
/**
|
|
6332
|
+
* The CAMERA is this camera's root step.
|
|
6333
|
+
*
|
|
6334
|
+
* Same shape as the pipeline path and deliberately so: one decoded frame, one
|
|
6335
|
+
* `FrameResult`, the same `notifyCallbacks`, the same handle, the same
|
|
6336
|
+
* retention release. Everything downstream — subject tiles, birth evidence,
|
|
6337
|
+
* the `detailSteps` announce, the overlay, the tracker — cannot tell which
|
|
6338
|
+
* detector produced the boxes, which is the entire point. What it does NOT do
|
|
6339
|
+
* is take an inference permit: the accelerator is never asked.
|
|
6340
|
+
*
|
|
6341
|
+
* A frame with no fresh onboard detection produces NO result rather than an
|
|
6342
|
+
* empty one. An empty `FrameResult` is a positive statement ("I looked and
|
|
6343
|
+
* there was nothing"), and this path did not look — it decoded a frame while
|
|
6344
|
+
* the camera happened to be silent. Emitting emptiness would tell the
|
|
6345
|
+
* tracker every silent frame is evidence of absence and coast every subject
|
|
6346
|
+
* to death between two pushes.
|
|
6347
|
+
*/
|
|
6348
|
+
async processOnboardRoot(deviceId, entry, state, streamType, plan) {
|
|
6349
|
+
const { frame, handle } = entry;
|
|
6350
|
+
let frameReleaseReason = "drop";
|
|
6351
|
+
try {
|
|
6352
|
+
if (!plan.runsOnboard) {
|
|
6353
|
+
this.noteOnboardRootIdle(deviceId, "no-source");
|
|
6354
|
+
return;
|
|
6355
|
+
}
|
|
6356
|
+
const pairing = onboardRootDetections(state.onboardSlot, {
|
|
6357
|
+
timestamp: Date.now(),
|
|
6358
|
+
width: frame.width,
|
|
6359
|
+
height: frame.height
|
|
6360
|
+
});
|
|
6361
|
+
if (pairing.kind !== "paired") {
|
|
6362
|
+
this.noteOnboardRootIdle(deviceId, pairing.kind);
|
|
6363
|
+
return;
|
|
6364
|
+
}
|
|
6365
|
+
state.onboardSlot = null;
|
|
6366
|
+
state.processedCount++;
|
|
6367
|
+
const result = {
|
|
6368
|
+
kind: "frame",
|
|
6369
|
+
frameId: `onboard-${String(deviceId)}-${String(frame.timestamp)}`,
|
|
6370
|
+
deviceId,
|
|
6371
|
+
timestamp: Date.now(),
|
|
6372
|
+
width: frame.width,
|
|
6373
|
+
height: frame.height,
|
|
6374
|
+
detections: pairing.detections
|
|
6375
|
+
};
|
|
6376
|
+
state.lastResultDetections = pairing.detections.length;
|
|
6377
|
+
await this.notifyCallbacks(deviceId, frame, result, streamType, handle, "onboard");
|
|
6378
|
+
frameReleaseReason = "success";
|
|
6379
|
+
} catch {
|
|
6380
|
+
frameReleaseReason = "error";
|
|
6381
|
+
} finally {
|
|
6382
|
+
try {
|
|
6383
|
+
if (frameReleaseReason === "success" && handle && isContinuousRetainEnabled()) await this.config.hydrateRetainedFrame?.(handle);
|
|
6384
|
+
} catch {}
|
|
6385
|
+
if (handle) this.config.releaseRetention?.(handle, frameReleaseReason);
|
|
6386
|
+
if (frame.frameRef) this.config.releaseFrameRef?.(frame.frameRef, frameReleaseReason);
|
|
6387
|
+
}
|
|
6388
|
+
}
|
|
6389
|
+
/**
|
|
6390
|
+
* Report — once per camera per interval — that the onboard root step decoded
|
|
6391
|
+
* a frame and produced nothing, with WHICH of the reasons it was. Sampled
|
|
6392
|
+
* because it fires at detection fps on a silent camera, which is most of the
|
|
6393
|
+
* time and is not itself a fault.
|
|
6394
|
+
*/
|
|
6395
|
+
noteOnboardRootIdle(deviceId, reason) {
|
|
6396
|
+
const now = Date.now();
|
|
6397
|
+
const key = `${String(deviceId)}:${reason}`;
|
|
6398
|
+
if (now - (this.onboardIdleReportedAt.get(key) ?? 0) < 6e4) return;
|
|
6399
|
+
this.onboardIdleReportedAt.set(key, now);
|
|
6400
|
+
this.logger?.info("onboard root step produced nothing for a decoded frame", {
|
|
6401
|
+
tags: { deviceId },
|
|
6402
|
+
meta: { reason }
|
|
6403
|
+
});
|
|
6404
|
+
}
|
|
6405
|
+
/**
|
|
6406
|
+
* The camera spoke. Held until a decoded frame can be paired with it.
|
|
6407
|
+
*
|
|
6408
|
+
* Gated by the CALLER on `detectionSources` — this method is the mechanism,
|
|
6409
|
+
* not the policy, and a camera that does not name `onboard` never reaches it.
|
|
6410
|
+
*/
|
|
6411
|
+
reportNativeDetection(deviceId, detections, frameWidth, frameHeight) {
|
|
6412
|
+
const state = this.cameras.get(deviceId);
|
|
6413
|
+
if (!state) return;
|
|
6414
|
+
state.onboardSlot = {
|
|
6415
|
+
at: Date.now(),
|
|
6416
|
+
detections,
|
|
6417
|
+
...frameWidth !== void 0 ? { frameWidth } : {},
|
|
6418
|
+
...frameHeight !== void 0 ? { frameHeight } : {}
|
|
6419
|
+
};
|
|
6420
|
+
}
|
|
6421
|
+
/**
|
|
6253
6422
|
* Await `processFrame` with a hard bound. On expiry the permit is released
|
|
6254
6423
|
* immediately (via the caller's release-once closure) and {@link TIMED_OUT}
|
|
6255
6424
|
* is returned; the underlying call is left to settle into the void, since
|
|
@@ -6334,9 +6503,9 @@ var PipelineRunner = class {
|
|
|
6334
6503
|
});
|
|
6335
6504
|
}
|
|
6336
6505
|
}
|
|
6337
|
-
async notifyCallbacks(deviceId, frame, result, streamType, handle) {
|
|
6506
|
+
async notifyCallbacks(deviceId, frame, result, streamType, handle, source) {
|
|
6338
6507
|
for (const callback of this.resultCallbacks) try {
|
|
6339
|
-
await callback(deviceId, frame, result, streamType, handle);
|
|
6508
|
+
await callback(deviceId, frame, result, streamType, handle, source);
|
|
6340
6509
|
} catch {}
|
|
6341
6510
|
}
|
|
6342
6511
|
pickNextDetectionFrame() {
|
|
@@ -7160,6 +7329,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7160
7329
|
/** Last runtime-label failure, so only a CHANGE is logged (D463). */
|
|
7161
7330
|
lastRuntimeInfoFailure = null;
|
|
7162
7331
|
unsubMotionEvents = null;
|
|
7332
|
+
unsubNativeDetections = null;
|
|
7163
7333
|
/** Devices we have positively observed reporting `battery.sleeping: true`.
|
|
7164
7334
|
* Absent = unknown or awake; both mean "do not gate" (D49). Fed only by
|
|
7165
7335
|
* `BatteryOnStatusChanged`, so consulting it costs nothing and can never
|
|
@@ -7416,11 +7586,11 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7416
7586
|
this.runner.onDetectionStreamChange((deviceId, action) => {
|
|
7417
7587
|
this.handleDetectionStreamChange(deviceId, action);
|
|
7418
7588
|
});
|
|
7419
|
-
this.runner.onResult(async (deviceId, frame, result, _streamType, handle) => {
|
|
7589
|
+
this.runner.onResult(async (deviceId, frame, result, _streamType, handle, source) => {
|
|
7420
7590
|
this.retention?.noteInferenceResult(deviceId, handle, result);
|
|
7421
7591
|
this.cutSubjectTiles(deviceId, result, handle);
|
|
7422
7592
|
const carried = await this.attachBirthEvidence(deviceId, frame, result);
|
|
7423
|
-
this.emitInferenceResult(deviceId, frame, carried, handle);
|
|
7593
|
+
this.emitInferenceResult(deviceId, frame, carried, handle, source);
|
|
7424
7594
|
const detectionCount = result.detections?.length ?? 0;
|
|
7425
7595
|
this.runner?.noteDetectionResult(deviceId, detectionCount);
|
|
7426
7596
|
if (detectionCount > 0 && this.occupancyBurstInFlight.has(deviceId)) this.occupancyBurstFound.add(deviceId);
|
|
@@ -7444,13 +7614,26 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7444
7614
|
if (source === "analyzer" && cameraOwnsMotionViaSensor(attachment.config.motionSources)) return;
|
|
7445
7615
|
this.runner?.reportMotion(deviceId, data.detected, source, data.regions ? [...data.regions] : void 0);
|
|
7446
7616
|
});
|
|
7447
|
-
if (this.ctx.eventBus)
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7617
|
+
if (this.ctx.eventBus) {
|
|
7618
|
+
this.unsubNativeDetections = this.ctx.eventBus.subscribe({ category: require_dist.EventCategory.DetectionCameraNative }, (event) => {
|
|
7619
|
+
const data = event.data;
|
|
7620
|
+
const deviceId = data.cameraId;
|
|
7621
|
+
if (typeof deviceId !== "number") return;
|
|
7622
|
+
const attachment = this.attached.get(deviceId);
|
|
7623
|
+
if (!attachment) return;
|
|
7624
|
+
if (!planDetectionSources(attachment.config.detectionSources).runsOnboard) return;
|
|
7625
|
+
const detections = data.detections;
|
|
7626
|
+
if (!detections || detections.length === 0) return;
|
|
7627
|
+
this.runner?.reportNativeDetection(deviceId, detections, data.frameWidth, data.frameHeight);
|
|
7628
|
+
});
|
|
7629
|
+
this.unsubBatteryEvents = this.ctx.eventBus.subscribe({ category: require_dist.EventCategory.BatteryOnStatusChanged }, (event) => {
|
|
7630
|
+
const data = event.data;
|
|
7631
|
+
const deviceId = data.deviceId;
|
|
7632
|
+
if (typeof deviceId !== "number") return;
|
|
7633
|
+
if (data.status?.sleeping === true) this.sleepingBatteryDevices.add(deviceId);
|
|
7634
|
+
else this.sleepingBatteryDevices.delete(deviceId);
|
|
7635
|
+
});
|
|
7636
|
+
}
|
|
7454
7637
|
this.metricsSnapshotTimer = setInterval(() => this.emitMetricsSnapshot(), METRICS_SNAPSHOT_INTERVAL_MS);
|
|
7455
7638
|
return {
|
|
7456
7639
|
providers: [
|
|
@@ -7499,6 +7682,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7499
7682
|
this.unsubMotionEvents();
|
|
7500
7683
|
this.unsubMotionEvents = null;
|
|
7501
7684
|
}
|
|
7685
|
+
if (this.unsubNativeDetections) {
|
|
7686
|
+
this.unsubNativeDetections();
|
|
7687
|
+
this.unsubNativeDetections = null;
|
|
7688
|
+
}
|
|
7502
7689
|
if (this.unsubBatteryEvents) {
|
|
7503
7690
|
this.unsubBatteryEvents();
|
|
7504
7691
|
this.unsubBatteryEvents = null;
|
|
@@ -10611,7 +10798,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10611
10798
|
teardown();
|
|
10612
10799
|
}
|
|
10613
10800
|
}
|
|
10614
|
-
emitInferenceResult(deviceId, frame, result, handle) {
|
|
10801
|
+
emitInferenceResult(deviceId, frame, result, handle, source) {
|
|
10615
10802
|
if (!this.ctx?.eventBus) return;
|
|
10616
10803
|
const capturedAt = frame.capturedAt;
|
|
10617
10804
|
const detailSteps = this.attached.get(deviceId)?.detailSteps;
|
|
@@ -10622,7 +10809,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10622
10809
|
frameHandle: handle,
|
|
10623
10810
|
emittedAt: Date.now(),
|
|
10624
10811
|
...typeof capturedAt === "number" && capturedAt > 0 ? { capturedAt } : {},
|
|
10625
|
-
...detailSteps && detailSteps.length > 0 ? { detailSteps } : {}
|
|
10812
|
+
...detailSteps && detailSteps.length > 0 ? { detailSteps } : {},
|
|
10813
|
+
source
|
|
10626
10814
|
};
|
|
10627
10815
|
this.ctx.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.PipelineInferenceResult, {
|
|
10628
10816
|
type: "device",
|