@camstack/addon-pipeline 1.2.137 → 1.2.139
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 +38 -86
- package/dist/audio-analyzer/index.mjs +36 -84
- package/dist/detection-pipeline/index.js +12 -12
- package/dist/detection-pipeline/index.mjs +5 -5
- package/dist/{dist-g0Or5_pR.mjs → dist-CTmOaCwF.mjs} +163 -3
- package/dist/{dist-4EMKvxCp.js → dist-WUZH-lAM.js} +168 -2
- package/dist/{event-loop-stall-monitor-CskL9Lgu.mjs → event-loop-stall-monitor-BMtR9qZR.mjs} +1 -1
- package/dist/{event-loop-stall-monitor-DDm07Ei6.js → event-loop-stall-monitor-CoM21UjL.js} +1 -1
- package/dist/{lazy-sharp-BlA9PxWk.js → lazy-sharp-1WF7ZnxZ.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-BOHn8gpk.js → node-BAo2ivVu.js} +1 -1
- package/dist/{node-Bo1g8hLO.mjs → node-CQcgxO8l.mjs} +1 -1
- package/dist/pipeline-runner/index.js +55 -12
- package/dist/pipeline-runner/index.mjs +54 -11
- package/dist/{process-memory-DmrHWybb.mjs → process-memory-BPEKERfe.mjs} +1 -1
- package/dist/{process-memory-C3084DOt.js → process-memory-BZvkQCLP.js} +1 -1
- package/dist/recorder/index.js +6 -6
- package/dist/recorder/index.mjs +3 -3
- package/dist/{segment-demux-js-BrQ_A0oi.js → segment-demux-js-B1L1pfoc.js} +1 -1
- package/dist/{segment-demux-js-C-a2pghM.mjs → segment-demux-js-CubhFNwb.mjs} +1 -1
- package/dist/session-decode/decode-worker-child.js +115 -5
- package/dist/session-decode/decode-worker-child.mjs +114 -4
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-AH6OJ3hJ.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DJ4k5ZBb.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-xdmO8wYp.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DPpl_mtg.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-XNyn-FTM.mjs → hostInit-oy-XDnw6.mjs} +3 -3
- package/dist/stream-broker/index.js +170 -62
- package/dist/stream-broker/index.mjs +170 -62
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-BLfaD5uC.js → worker-protocol-C-0LYdSt.js} +2 -1
- package/dist/{worker-protocol-vc8WWhjx.mjs → worker-protocol-DsuI4ifI.mjs} +2 -1
- package/package.json +5 -3
- package/dist/addon-utils-C6b5Tnia.js +0 -625
- package/dist/addon-utils-C8KTuPdE.mjs +0 -583
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CQyiEd0l.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BrTM_tsb.mjs +0 -26
|
@@ -5932,6 +5932,40 @@ var BaseAddon = class {
|
|
|
5932
5932
|
deviceSettingsSchema() {
|
|
5933
5933
|
return null;
|
|
5934
5934
|
}
|
|
5935
|
+
/**
|
|
5936
|
+
* INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
|
|
5937
|
+
* ARE the configuration of its integration.
|
|
5938
|
+
*
|
|
5939
|
+
* Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
|
|
5940
|
+
* operator should find on the addon's integration page (System →
|
|
5941
|
+
* Integrations → <name>) rather than only in the cluster-wide list of every
|
|
5942
|
+
* addon. Empty (the default) means the addon has no integration-level
|
|
5943
|
+
* settings and no such surface is offered — this is opt-in, because whether
|
|
5944
|
+
* an addon's configuration IS its integration's configuration depends on the
|
|
5945
|
+
* nature of the integration.
|
|
5946
|
+
*
|
|
5947
|
+
* WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
|
|
5948
|
+
* the ONE global schema, in the ONE addon store, written by the ONE
|
|
5949
|
+
* `updateGlobalSettings` path. There is deliberately no
|
|
5950
|
+
* `updateIntegrationSettings`: a second write path is how a surface acquires
|
|
5951
|
+
* a second store key, and this repo has shipped that twice (`btmPath@hub`,
|
|
5952
|
+
* D266). Selecting sections cannot introduce a key that selecting cannot.
|
|
5953
|
+
*
|
|
5954
|
+
* WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
|
|
5955
|
+
* `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
|
|
5956
|
+
* removed with the reason recorded at
|
|
5957
|
+
* `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
|
|
5958
|
+
* determined by WHICH schema it lives in, not by a field-level marker."* A
|
|
5959
|
+
* marker sprinkled across sections also has to borrow a field that already
|
|
5960
|
+
* means something else; borrowing `section.tab` put the literal word
|
|
5961
|
+
* "integration" into an operator-facing tab bar, because `tab` means "how to
|
|
5962
|
+
* GROUP this visually" and cannot also mean "where this lives" (D269
|
|
5963
|
+
* supersedes D268). One declaration, in one place, next to the schema whose
|
|
5964
|
+
* ids it names.
|
|
5965
|
+
*/
|
|
5966
|
+
integrationSettingSections() {
|
|
5967
|
+
return [];
|
|
5968
|
+
}
|
|
5935
5969
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
5936
5970
|
const schema = this.globalSettingsSchema(cap);
|
|
5937
5971
|
if (!schema) return { sections: [] };
|
|
@@ -5942,6 +5976,55 @@ var BaseAddon = class {
|
|
|
5942
5976
|
} : projected);
|
|
5943
5977
|
}
|
|
5944
5978
|
/**
|
|
5979
|
+
* The integration-level view of this addon's settings: exactly the sections
|
|
5980
|
+
* named by {@link integrationSettingSections}, hydrated from the SAME store
|
|
5981
|
+
* `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
|
|
5982
|
+
*
|
|
5983
|
+
* Returns `null` when the addon declared nothing — an addon that opts out has
|
|
5984
|
+
* no integration settings surface at all, rather than an empty one that reads
|
|
5985
|
+
* as a failed load.
|
|
5986
|
+
*
|
|
5987
|
+
* Three properties hold BY CONSTRUCTION, which is why they are here in core
|
|
5988
|
+
* and not in whichever UI happens to render this:
|
|
5989
|
+
*
|
|
5990
|
+
* 1. **One key.** The payload is a SUBSET of the global schema, so a field
|
|
5991
|
+
* shown here is the same field, with the same bare key, that the addon's
|
|
5992
|
+
* own page shows. There is no integration-specific writer — callers save
|
|
5993
|
+
* through `updateGlobalSettings` — so a second store key is unreachable,
|
|
5994
|
+
* not merely discouraged.
|
|
5995
|
+
* 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
|
|
5996
|
+
* is `<key>@<nodeId>` and an integration is not a node; whichever node
|
|
5997
|
+
* such a field silently picked would be a wrong answer for the operator
|
|
5998
|
+
* who opened the page (D266).
|
|
5999
|
+
* 3. **No silent typo.** A declared id that names no section throws. The
|
|
6000
|
+
* alternative — skip it — turns a rename into a surface that quietly
|
|
6001
|
+
* empties, which looks exactly like an addon with nothing to configure.
|
|
6002
|
+
*/
|
|
6003
|
+
async getIntegrationSettings(nodeId) {
|
|
6004
|
+
const declared = this.integrationSettingSections();
|
|
6005
|
+
if (declared.length === 0) return null;
|
|
6006
|
+
const schema = this.globalSettingsSchema();
|
|
6007
|
+
if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
|
|
6008
|
+
const byId = new Map(schema.sections.map((section) => [section.id, section]));
|
|
6009
|
+
const sections = [];
|
|
6010
|
+
for (const id of declared) {
|
|
6011
|
+
const section = byId.get(id);
|
|
6012
|
+
if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
|
|
6013
|
+
const fields = dropPerNodeFields(section.fields);
|
|
6014
|
+
if (fields.length === 0) continue;
|
|
6015
|
+
sections.push({
|
|
6016
|
+
...section,
|
|
6017
|
+
fields
|
|
6018
|
+
});
|
|
6019
|
+
}
|
|
6020
|
+
if (sections.length === 0) return null;
|
|
6021
|
+
const projected = await this.resolveGlobalStore(nodeId);
|
|
6022
|
+
return hydrateSchema({
|
|
6023
|
+
...schema,
|
|
6024
|
+
sections
|
|
6025
|
+
}, projected);
|
|
6026
|
+
}
|
|
6027
|
+
/**
|
|
5945
6028
|
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
5946
6029
|
* every `perNode: true` field carries THAT node's scoped value on its bare
|
|
5947
6030
|
* key (absent scoped key ⇒ key absent, so the schema `default` wins — no
|
|
@@ -6245,6 +6328,41 @@ var BaseAddon = class {
|
|
|
6245
6328
|
* `hydrateSchema` does. Valueless structural fields (separator/info/…)
|
|
6246
6329
|
* don't declare `perNode` and are excluded by the `in` narrowing.
|
|
6247
6330
|
*/
|
|
6331
|
+
/**
|
|
6332
|
+
* The same fields with every `perNode: true` one removed, recursing into layout
|
|
6333
|
+
* containers exactly as {@link collectPerNodeFieldKeys} does. A container left
|
|
6334
|
+
* with no child is dropped rather than rendered empty.
|
|
6335
|
+
*
|
|
6336
|
+
* Used by `getIntegrationSettings`: an integration is not a node, so a field
|
|
6337
|
+
* whose store key is `<key>@<nodeId>` has no node to belong to there.
|
|
6338
|
+
*/
|
|
6339
|
+
function dropPerNodeFields(fields) {
|
|
6340
|
+
const kept = [];
|
|
6341
|
+
for (const field of fields) {
|
|
6342
|
+
if (field.type === "group") {
|
|
6343
|
+
const inner = dropPerNodeFields(field.fields);
|
|
6344
|
+
if (inner.length > 0) kept.push({
|
|
6345
|
+
...field,
|
|
6346
|
+
fields: inner
|
|
6347
|
+
});
|
|
6348
|
+
continue;
|
|
6349
|
+
}
|
|
6350
|
+
if (field.type === "sub-tabs") {
|
|
6351
|
+
const tabs = field.tabs.map((tab) => ({
|
|
6352
|
+
...tab,
|
|
6353
|
+
fields: dropPerNodeFields(tab.fields)
|
|
6354
|
+
})).filter((tab) => tab.fields.length > 0);
|
|
6355
|
+
if (tabs.length > 0) kept.push({
|
|
6356
|
+
...field,
|
|
6357
|
+
tabs
|
|
6358
|
+
});
|
|
6359
|
+
continue;
|
|
6360
|
+
}
|
|
6361
|
+
if ("perNode" in field && field.perNode === true) continue;
|
|
6362
|
+
kept.push(field);
|
|
6363
|
+
}
|
|
6364
|
+
return kept;
|
|
6365
|
+
}
|
|
6248
6366
|
function collectPerNodeFieldKeys(fields) {
|
|
6249
6367
|
const collected = [];
|
|
6250
6368
|
for (const field of fields) {
|
|
@@ -7173,6 +7291,40 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
|
|
|
7173
7291
|
input: unknown()
|
|
7174
7292
|
}), unknown(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
7175
7293
|
/**
|
|
7294
|
+
* The ONE retry schedule for work that can fail simply because the node was
|
|
7295
|
+
* still booting.
|
|
7296
|
+
*
|
|
7297
|
+
* The measured failure (docs/design/boot-to-usable-camera.md, item E): a hub
|
|
7298
|
+
* boot that serialised its addon graph starved every driver dial on it. Fifteen
|
|
7299
|
+
* Hikvision devices logged `Initial probe did not complete — scheduling retry
|
|
7300
|
+
* chain {firstRetryMs=300000}` and then sat unprobed for five minutes, long
|
|
7301
|
+
* after the condition that caused the failure had gone. The Tailscale addon's
|
|
7302
|
+
* auto-rejoin carried a byte-identical schedule, armed from `initialize()`, for
|
|
7303
|
+
* the same reason.
|
|
7304
|
+
*
|
|
7305
|
+
* The shape that fixes it: **two cheap attempts first, then the long tail.**
|
|
7306
|
+
*
|
|
7307
|
+
* - 15 s / 60 s — the boot window. Anything that failed because the node was
|
|
7308
|
+
* busy is recovered here, before an operator can open the UI and see a
|
|
7309
|
+
* camera that is wrong about itself.
|
|
7310
|
+
* - 5 m → 90 m — the genuinely-offline case. A battery cam that is asleep, a
|
|
7311
|
+
* VLAN that is down, a peer that has not come back. Unchanged in intent from
|
|
7312
|
+
* the schedule this replaces (~3 h 30 m of coverage): here ~3 h 21 m, then
|
|
7313
|
+
* the chain ends and manual re-arm (reprobe / restart) is the only path.
|
|
7314
|
+
*
|
|
7315
|
+
* Bounded on purpose — see D6. A chain that never gives up is a busy loop with
|
|
7316
|
+
* extra steps.
|
|
7317
|
+
*/
|
|
7318
|
+
var BOOT_RECOVERY_BACKOFF_MS = [
|
|
7319
|
+
15e3,
|
|
7320
|
+
6e4,
|
|
7321
|
+
5 * 6e4,
|
|
7322
|
+
15 * 6e4,
|
|
7323
|
+
30 * 6e4,
|
|
7324
|
+
60 * 6e4,
|
|
7325
|
+
90 * 6e4
|
|
7326
|
+
];
|
|
7327
|
+
/**
|
|
7176
7328
|
* Type-safe JSON parsing helpers.
|
|
7177
7329
|
*
|
|
7178
7330
|
* `JSON.parse` is typed as `any` in lib.es5.d.ts, which triggers
|
|
@@ -10708,6 +10860,9 @@ method(object({
|
|
|
10708
10860
|
kind: "mutation",
|
|
10709
10861
|
auth: "admin"
|
|
10710
10862
|
}), method(object({
|
|
10863
|
+
addonId: string(),
|
|
10864
|
+
nodeId: string().optional()
|
|
10865
|
+
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
10711
10866
|
addonId: string(),
|
|
10712
10867
|
deviceId: number(),
|
|
10713
10868
|
nodeId: string().optional()
|
|
@@ -11095,8 +11250,7 @@ var AUDIO_BACKEND_CHOICES = [
|
|
|
11095
11250
|
];
|
|
11096
11251
|
var DEFAULT_AUDIO_ANALYZER_CONFIG = {
|
|
11097
11252
|
audioBackend: "auto",
|
|
11098
|
-
probedBestAudioBackend: ""
|
|
11099
|
-
selectedAudioModel: ""
|
|
11253
|
+
probedBestAudioBackend: ""
|
|
11100
11254
|
};
|
|
11101
11255
|
var audioAnalyzerCapability = {
|
|
11102
11256
|
name: "audio-analyzer",
|
|
@@ -30756,6 +30910,12 @@ Object.freeze({
|
|
|
30756
30910
|
addonId: null,
|
|
30757
30911
|
access: "view"
|
|
30758
30912
|
},
|
|
30913
|
+
"addonSettings.getIntegrationSettings": {
|
|
30914
|
+
capName: "addon-settings",
|
|
30915
|
+
capScope: "system",
|
|
30916
|
+
addonId: null,
|
|
30917
|
+
access: "view"
|
|
30918
|
+
},
|
|
30759
30919
|
"addonSettings.updateDeviceSettings": {
|
|
30760
30920
|
capName: "addon-settings",
|
|
30761
30921
|
capScope: "system",
|
|
@@ -39711,4 +39871,4 @@ function enumerateInferenceDevices(hw) {
|
|
|
39711
39871
|
return out;
|
|
39712
39872
|
}
|
|
39713
39873
|
//#endregion
|
|
39714
|
-
export { loadContributionCapability as $,
|
|
39874
|
+
export { loadContributionCapability as $, string as $t, audioAnalysisCapability as A, BaseAddon as At, defineCustomActions as B, parseJsonUnknown as Bt, RATE_CONTROL_RELAXED as C, errMsg as Ct, RingBuffer as D, invocationFromEncodeProfile as Dt, RecordingConfigSchema as E, buildFfmpegArgs as Et, createHwAccelCache as F, hydrateSchema as Ft, egressTranscodeSharingKey as G, array as Gt, deriveDetailCropRect as H, selectAssignedProfileSlots as Ht, createLogChannelsProvider as I, isEvent as It, evaluateSensorEdge as J, lazy as Jt, egressTransportFromRequest as K, boolean as Kt, customAction as L, makeProfileBrokerId as Lt, audioKindId as M, DeviceFeature as Mt, batteryCapability as N, DeviceType as Nt, YAMNET_TO_MACRO as O, isSoftwareDecode as Ot, cameraStreamsCapability as P, createEvent as Pt, isFirstLevelMacroClass as Q, record as Qt, declareLogChannel as R, makeSourceBrokerId as Rt, PoolMemoryWatchdog as S, webrtcSessionCapability as St, RECORDING_EXPORT_MAX_READ_BYTES as T, Fmp4BoxSplitter as Tt, deriveRecordingMode as U, sleep as Ut, deriveBatteryPresence as V, parseProfileBrokerId as Vt, detectionPipelineCapability as W, _enum as Wt, hfModelUrl as X, number as Xt, evaluateZoneRules as Y, literal as Yt, inferModelProvider as Z, object as Zt, ExportRecordSchema as _, resolveRecordingProfiles as _t, COCO_80_LABELS as a, parseProcStatus as at, NativeLeaseSettingsSchema as b, streamBrokerCapability as bt, DEFAULT_CLUSTER_STEP_MODELS as c, pickDetailCropConvention as ct, DEFAULT_EVENTS_BAND_BUFFER_SEC as d, pipelineRunnerCapability as dt, union as en, logChannelsCapability as et, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as f, recordingCapability as ft, EncodeProfileSchema as g, resolvePoolMemoryPolicy as gt, EVENT_PAD_MS as h, resolveEgressDecodeHwAccel as ht, BatteryStatusSchema as i, overlayClusterStepSettings as it, audioAnalyzerCapability as j, CAM_PROFILE_ORDER as jt, addonWidgetsSourceCapability as k, BOOT_RECOVERY_BACKOFF_MS as kt, DEFAULT_CLUSTER_STEP_SETTINGS as l, pickNativeLeaseOverride as lt, DEVICE_BACKEND_TO_FORMAT as m, resolveClusterStepModelId as mt, AUDIO_BACKEND_CHOICES as n, maskUrlCredentials as nt, COCO_TO_MACRO as o, pickClusterStepModels as ot, DEFAULT_NATIVE_LEASE_SETTINGS as p, recordingExportCapability as pt, enumerateInferenceDevices as q, discriminatedUnion as qt, AUDIO_MACRO_LABELS as r, motionDetectionCapability as rt, DEFAULT_AUDIO_ANALYZER_CONFIG as s, pickClusterStepSettings as st, APPLE_SA_TO_MACRO as t, EventCategory as tn, mapAudioLabelToMacro as tt, DEFAULT_DETAIL_CROP_CONVENTION as u, pipelineExecutorCapability as ut, HF_BASE_URL as v, runtimeDevices as vt, RATE_CONTROL_TIGHT as w, AUDIO_PRESETS as wt, OpsLogEntrySchema as x, supportedRuntimes as xt, NativeLeaseAdmissionSchema as y, storageEvictableCapability as yt, defaultDeviceFor as z, nodePin as zt };
|
|
@@ -5955,6 +5955,40 @@ var BaseAddon = class {
|
|
|
5955
5955
|
deviceSettingsSchema() {
|
|
5956
5956
|
return null;
|
|
5957
5957
|
}
|
|
5958
|
+
/**
|
|
5959
|
+
* INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
|
|
5960
|
+
* ARE the configuration of its integration.
|
|
5961
|
+
*
|
|
5962
|
+
* Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
|
|
5963
|
+
* operator should find on the addon's integration page (System →
|
|
5964
|
+
* Integrations → <name>) rather than only in the cluster-wide list of every
|
|
5965
|
+
* addon. Empty (the default) means the addon has no integration-level
|
|
5966
|
+
* settings and no such surface is offered — this is opt-in, because whether
|
|
5967
|
+
* an addon's configuration IS its integration's configuration depends on the
|
|
5968
|
+
* nature of the integration.
|
|
5969
|
+
*
|
|
5970
|
+
* WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
|
|
5971
|
+
* the ONE global schema, in the ONE addon store, written by the ONE
|
|
5972
|
+
* `updateGlobalSettings` path. There is deliberately no
|
|
5973
|
+
* `updateIntegrationSettings`: a second write path is how a surface acquires
|
|
5974
|
+
* a second store key, and this repo has shipped that twice (`btmPath@hub`,
|
|
5975
|
+
* D266). Selecting sections cannot introduce a key that selecting cannot.
|
|
5976
|
+
*
|
|
5977
|
+
* WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
|
|
5978
|
+
* `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
|
|
5979
|
+
* removed with the reason recorded at
|
|
5980
|
+
* `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
|
|
5981
|
+
* determined by WHICH schema it lives in, not by a field-level marker."* A
|
|
5982
|
+
* marker sprinkled across sections also has to borrow a field that already
|
|
5983
|
+
* means something else; borrowing `section.tab` put the literal word
|
|
5984
|
+
* "integration" into an operator-facing tab bar, because `tab` means "how to
|
|
5985
|
+
* GROUP this visually" and cannot also mean "where this lives" (D269
|
|
5986
|
+
* supersedes D268). One declaration, in one place, next to the schema whose
|
|
5987
|
+
* ids it names.
|
|
5988
|
+
*/
|
|
5989
|
+
integrationSettingSections() {
|
|
5990
|
+
return [];
|
|
5991
|
+
}
|
|
5958
5992
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
5959
5993
|
const schema = this.globalSettingsSchema(cap);
|
|
5960
5994
|
if (!schema) return { sections: [] };
|
|
@@ -5965,6 +5999,55 @@ var BaseAddon = class {
|
|
|
5965
5999
|
} : projected);
|
|
5966
6000
|
}
|
|
5967
6001
|
/**
|
|
6002
|
+
* The integration-level view of this addon's settings: exactly the sections
|
|
6003
|
+
* named by {@link integrationSettingSections}, hydrated from the SAME store
|
|
6004
|
+
* `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
|
|
6005
|
+
*
|
|
6006
|
+
* Returns `null` when the addon declared nothing — an addon that opts out has
|
|
6007
|
+
* no integration settings surface at all, rather than an empty one that reads
|
|
6008
|
+
* as a failed load.
|
|
6009
|
+
*
|
|
6010
|
+
* Three properties hold BY CONSTRUCTION, which is why they are here in core
|
|
6011
|
+
* and not in whichever UI happens to render this:
|
|
6012
|
+
*
|
|
6013
|
+
* 1. **One key.** The payload is a SUBSET of the global schema, so a field
|
|
6014
|
+
* shown here is the same field, with the same bare key, that the addon's
|
|
6015
|
+
* own page shows. There is no integration-specific writer — callers save
|
|
6016
|
+
* through `updateGlobalSettings` — so a second store key is unreachable,
|
|
6017
|
+
* not merely discouraged.
|
|
6018
|
+
* 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
|
|
6019
|
+
* is `<key>@<nodeId>` and an integration is not a node; whichever node
|
|
6020
|
+
* such a field silently picked would be a wrong answer for the operator
|
|
6021
|
+
* who opened the page (D266).
|
|
6022
|
+
* 3. **No silent typo.** A declared id that names no section throws. The
|
|
6023
|
+
* alternative — skip it — turns a rename into a surface that quietly
|
|
6024
|
+
* empties, which looks exactly like an addon with nothing to configure.
|
|
6025
|
+
*/
|
|
6026
|
+
async getIntegrationSettings(nodeId) {
|
|
6027
|
+
const declared = this.integrationSettingSections();
|
|
6028
|
+
if (declared.length === 0) return null;
|
|
6029
|
+
const schema = this.globalSettingsSchema();
|
|
6030
|
+
if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
|
|
6031
|
+
const byId = new Map(schema.sections.map((section) => [section.id, section]));
|
|
6032
|
+
const sections = [];
|
|
6033
|
+
for (const id of declared) {
|
|
6034
|
+
const section = byId.get(id);
|
|
6035
|
+
if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
|
|
6036
|
+
const fields = dropPerNodeFields(section.fields);
|
|
6037
|
+
if (fields.length === 0) continue;
|
|
6038
|
+
sections.push({
|
|
6039
|
+
...section,
|
|
6040
|
+
fields
|
|
6041
|
+
});
|
|
6042
|
+
}
|
|
6043
|
+
if (sections.length === 0) return null;
|
|
6044
|
+
const projected = await this.resolveGlobalStore(nodeId);
|
|
6045
|
+
return hydrateSchema({
|
|
6046
|
+
...schema,
|
|
6047
|
+
sections
|
|
6048
|
+
}, projected);
|
|
6049
|
+
}
|
|
6050
|
+
/**
|
|
5968
6051
|
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
5969
6052
|
* every `perNode: true` field carries THAT node's scoped value on its bare
|
|
5970
6053
|
* key (absent scoped key ⇒ key absent, so the schema `default` wins — no
|
|
@@ -6268,6 +6351,41 @@ var BaseAddon = class {
|
|
|
6268
6351
|
* `hydrateSchema` does. Valueless structural fields (separator/info/…)
|
|
6269
6352
|
* don't declare `perNode` and are excluded by the `in` narrowing.
|
|
6270
6353
|
*/
|
|
6354
|
+
/**
|
|
6355
|
+
* The same fields with every `perNode: true` one removed, recursing into layout
|
|
6356
|
+
* containers exactly as {@link collectPerNodeFieldKeys} does. A container left
|
|
6357
|
+
* with no child is dropped rather than rendered empty.
|
|
6358
|
+
*
|
|
6359
|
+
* Used by `getIntegrationSettings`: an integration is not a node, so a field
|
|
6360
|
+
* whose store key is `<key>@<nodeId>` has no node to belong to there.
|
|
6361
|
+
*/
|
|
6362
|
+
function dropPerNodeFields(fields) {
|
|
6363
|
+
const kept = [];
|
|
6364
|
+
for (const field of fields) {
|
|
6365
|
+
if (field.type === "group") {
|
|
6366
|
+
const inner = dropPerNodeFields(field.fields);
|
|
6367
|
+
if (inner.length > 0) kept.push({
|
|
6368
|
+
...field,
|
|
6369
|
+
fields: inner
|
|
6370
|
+
});
|
|
6371
|
+
continue;
|
|
6372
|
+
}
|
|
6373
|
+
if (field.type === "sub-tabs") {
|
|
6374
|
+
const tabs = field.tabs.map((tab) => ({
|
|
6375
|
+
...tab,
|
|
6376
|
+
fields: dropPerNodeFields(tab.fields)
|
|
6377
|
+
})).filter((tab) => tab.fields.length > 0);
|
|
6378
|
+
if (tabs.length > 0) kept.push({
|
|
6379
|
+
...field,
|
|
6380
|
+
tabs
|
|
6381
|
+
});
|
|
6382
|
+
continue;
|
|
6383
|
+
}
|
|
6384
|
+
if ("perNode" in field && field.perNode === true) continue;
|
|
6385
|
+
kept.push(field);
|
|
6386
|
+
}
|
|
6387
|
+
return kept;
|
|
6388
|
+
}
|
|
6271
6389
|
function collectPerNodeFieldKeys(fields) {
|
|
6272
6390
|
const collected = [];
|
|
6273
6391
|
for (const field of fields) {
|
|
@@ -7196,6 +7314,40 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
|
|
|
7196
7314
|
input: unknown()
|
|
7197
7315
|
}), unknown(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
7198
7316
|
/**
|
|
7317
|
+
* The ONE retry schedule for work that can fail simply because the node was
|
|
7318
|
+
* still booting.
|
|
7319
|
+
*
|
|
7320
|
+
* The measured failure (docs/design/boot-to-usable-camera.md, item E): a hub
|
|
7321
|
+
* boot that serialised its addon graph starved every driver dial on it. Fifteen
|
|
7322
|
+
* Hikvision devices logged `Initial probe did not complete — scheduling retry
|
|
7323
|
+
* chain {firstRetryMs=300000}` and then sat unprobed for five minutes, long
|
|
7324
|
+
* after the condition that caused the failure had gone. The Tailscale addon's
|
|
7325
|
+
* auto-rejoin carried a byte-identical schedule, armed from `initialize()`, for
|
|
7326
|
+
* the same reason.
|
|
7327
|
+
*
|
|
7328
|
+
* The shape that fixes it: **two cheap attempts first, then the long tail.**
|
|
7329
|
+
*
|
|
7330
|
+
* - 15 s / 60 s — the boot window. Anything that failed because the node was
|
|
7331
|
+
* busy is recovered here, before an operator can open the UI and see a
|
|
7332
|
+
* camera that is wrong about itself.
|
|
7333
|
+
* - 5 m → 90 m — the genuinely-offline case. A battery cam that is asleep, a
|
|
7334
|
+
* VLAN that is down, a peer that has not come back. Unchanged in intent from
|
|
7335
|
+
* the schedule this replaces (~3 h 30 m of coverage): here ~3 h 21 m, then
|
|
7336
|
+
* the chain ends and manual re-arm (reprobe / restart) is the only path.
|
|
7337
|
+
*
|
|
7338
|
+
* Bounded on purpose — see D6. A chain that never gives up is a busy loop with
|
|
7339
|
+
* extra steps.
|
|
7340
|
+
*/
|
|
7341
|
+
var BOOT_RECOVERY_BACKOFF_MS = [
|
|
7342
|
+
15e3,
|
|
7343
|
+
6e4,
|
|
7344
|
+
5 * 6e4,
|
|
7345
|
+
15 * 6e4,
|
|
7346
|
+
30 * 6e4,
|
|
7347
|
+
60 * 6e4,
|
|
7348
|
+
90 * 6e4
|
|
7349
|
+
];
|
|
7350
|
+
/**
|
|
7199
7351
|
* Type-safe JSON parsing helpers.
|
|
7200
7352
|
*
|
|
7201
7353
|
* `JSON.parse` is typed as `any` in lib.es5.d.ts, which triggers
|
|
@@ -10731,6 +10883,9 @@ method(object({
|
|
|
10731
10883
|
kind: "mutation",
|
|
10732
10884
|
auth: "admin"
|
|
10733
10885
|
}), method(object({
|
|
10886
|
+
addonId: string(),
|
|
10887
|
+
nodeId: string().optional()
|
|
10888
|
+
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
10734
10889
|
addonId: string(),
|
|
10735
10890
|
deviceId: number(),
|
|
10736
10891
|
nodeId: string().optional()
|
|
@@ -11118,8 +11273,7 @@ var AUDIO_BACKEND_CHOICES = [
|
|
|
11118
11273
|
];
|
|
11119
11274
|
var DEFAULT_AUDIO_ANALYZER_CONFIG = {
|
|
11120
11275
|
audioBackend: "auto",
|
|
11121
|
-
probedBestAudioBackend: ""
|
|
11122
|
-
selectedAudioModel: ""
|
|
11276
|
+
probedBestAudioBackend: ""
|
|
11123
11277
|
};
|
|
11124
11278
|
var audioAnalyzerCapability = {
|
|
11125
11279
|
name: "audio-analyzer",
|
|
@@ -30779,6 +30933,12 @@ Object.freeze({
|
|
|
30779
30933
|
addonId: null,
|
|
30780
30934
|
access: "view"
|
|
30781
30935
|
},
|
|
30936
|
+
"addonSettings.getIntegrationSettings": {
|
|
30937
|
+
capName: "addon-settings",
|
|
30938
|
+
capScope: "system",
|
|
30939
|
+
addonId: null,
|
|
30940
|
+
access: "view"
|
|
30941
|
+
},
|
|
30782
30942
|
"addonSettings.updateDeviceSettings": {
|
|
30783
30943
|
capName: "addon-settings",
|
|
30784
30944
|
capScope: "system",
|
|
@@ -39758,6 +39918,12 @@ Object.defineProperty(exports, "AUDIO_PRESETS", {
|
|
|
39758
39918
|
return AUDIO_PRESETS;
|
|
39759
39919
|
}
|
|
39760
39920
|
});
|
|
39921
|
+
Object.defineProperty(exports, "BOOT_RECOVERY_BACKOFF_MS", {
|
|
39922
|
+
enumerable: true,
|
|
39923
|
+
get: function() {
|
|
39924
|
+
return BOOT_RECOVERY_BACKOFF_MS;
|
|
39925
|
+
}
|
|
39926
|
+
});
|
|
39761
39927
|
Object.defineProperty(exports, "BaseAddon", {
|
|
39762
39928
|
enumerable: true,
|
|
39763
39929
|
get: function() {
|
package/dist/{event-loop-stall-monitor-CskL9Lgu.mjs → event-loop-stall-monitor-BMtR9qZR.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as audioKindId, X as hfModelUrl, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS } from "./dist-
|
|
1
|
+
import { M as audioKindId, X as hfModelUrl, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS } from "./dist-CTmOaCwF.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { PerformanceObserver } from "node:perf_hooks";
|
|
4
4
|
//#region src/detection-pipeline/registry/model-catalogs.ts
|
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
5
|
+
const require_dist = require("../dist-WUZH-lAM.js");
|
|
6
|
+
const require_lazy_sharp = require("../lazy-sharp-1WF7ZnxZ.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as BaseAddon, Ft as hydrateSchema, Nt as DeviceType, Y as evaluateZoneRules, rt as motionDetectionCapability } from "../dist-CTmOaCwF.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 { Ct as errMsg, Et as buildFfmpegArgs, Ot as isSoftwareDecode, Tt as Fmp4BoxSplitter } from "./dist-
|
|
1
|
+
import { Ct as errMsg, Et as buildFfmpegArgs, Ot as isSoftwareDecode, Tt as Fmp4BoxSplitter } from "./dist-CTmOaCwF.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import "node:fs";
|
|
4
4
|
import "node:path";
|
|
@@ -2,12 +2,12 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_node = require("../node-
|
|
7
|
-
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-
|
|
8
|
-
const require_worker_protocol = require("../worker-protocol-
|
|
5
|
+
const require_dist = require("../dist-WUZH-lAM.js");
|
|
6
|
+
const require_node = require("../node-BAo2ivVu.js");
|
|
7
|
+
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CoM21UjL.js");
|
|
8
|
+
const require_worker_protocol = require("../worker-protocol-C-0LYdSt.js");
|
|
9
9
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
10
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
10
|
+
const require_lazy_sharp = require("../lazy-sharp-1WF7ZnxZ.js");
|
|
11
11
|
const require_restream_intent = require("../restream-intent-Cv9x3jmu.js");
|
|
12
12
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
13
13
|
let node_child_process = require("node:child_process");
|
|
@@ -891,10 +891,29 @@ var DEFAULT_FIRST_FRAME_TIMEOUT_MS = 2e4;
|
|
|
891
891
|
*
|
|
892
892
|
* The first decodable frame is not a fixed cost: it is one IDR interval plus
|
|
893
893
|
* every GOP the decoder has to discard. Measured on device 618 (2560x1440
|
|
894
|
-
* h264,
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
894
|
+
* h264, `Invalid NAL unit` / `corrupt decoded frame` in its bitstream), dial →
|
|
895
|
+
* first decoded frame over the broker's own muted restream was 1.8 / 6.5 / 7.0
|
|
896
|
+
* / 7.2 / 9.2 / 10.0 / 10.8 / 12.9 / 14.9 / 20.0 / 30.3 / >40 s across 12
|
|
897
|
+
* trials — against a healthy 587's 2.0 s ± 0.02.
|
|
898
|
+
*
|
|
899
|
+
* **The "8.0 s IDR interval" this used to attribute 618's spread to was wrong,
|
|
900
|
+
* and is corrected here rather than deleted** (2026-08-28): a stated number
|
|
901
|
+
* nobody re-derived reads as verification, and this one was used to argue that
|
|
902
|
+
* 618 was an encoder outlier. The fleet runs 100-frame GOPs at 25 fps — a
|
|
903
|
+
* **4 000 ms** IDR interval — and two independent readings of 6 h of live logs
|
|
904
|
+
* agree with the operator rather than with this docblock. First, the join-burst
|
|
905
|
+
* span logged on `restream prime (lat-probe)` is the time since the last IDR,
|
|
906
|
+
* uniform in `[0, IDR]` and censored at `PRIME_MAX_SPAN_MS`: its median is
|
|
907
|
+
* ~1 000 ms on eleven cameras, which is what `uniform[0, 4000]` conditioned on
|
|
908
|
+
* `≤2000` predicts. Second, `detectionIntentServed` over detection dials puts
|
|
909
|
+
* the SERVED fraction at 44-61 % on the bulk of the fleet, i.e. the ~50 %
|
|
910
|
+
* `P(span > 2000)` a 4 000 ms interval implies. 618 measures 51 %, which is the
|
|
911
|
+
* fleet norm. The genuine long-GOP outlier is **637** at 30 % served (⇒ ~6.7 s).
|
|
912
|
+
*
|
|
913
|
+
* So the spread is not this camera's encoder. It is D57's fixed 2 000 ms bound
|
|
914
|
+
* withholding the join burst WHOLE on roughly half of every 4 s-GOP camera's
|
|
915
|
+
* dials — see `join-burst-bound.ts`, which states the arithmetic, and
|
|
916
|
+
* `docs/architecture/load-ledger.md` (2026-08-28) for the measurement.
|
|
898
917
|
*
|
|
899
918
|
* Abandoning at 20 s was therefore not a timeout, it was a coin flip, and the
|
|
900
919
|
* recovery made it worse: ending the session RE-DIALS, which throws the
|
|
@@ -930,6 +949,7 @@ var SessionDecodeSession = class {
|
|
|
930
949
|
firstFrameTimeoutMs;
|
|
931
950
|
frameTimeoutMs;
|
|
932
951
|
firstFrameMaxWaitMs;
|
|
952
|
+
onDialTiming;
|
|
933
953
|
pendingPull = null;
|
|
934
954
|
pendingToBuffers = /* @__PURE__ */ new Map();
|
|
935
955
|
/** Outstanding `nativeCrop` requests, keyed by the monotonic `requestId`. */
|
|
@@ -943,13 +963,14 @@ var SessionDecodeSession = class {
|
|
|
943
963
|
firstFrameReceived = false;
|
|
944
964
|
/** `Date.now()` of the FIRST `pull` — the origin of the cold-start ceiling. */
|
|
945
965
|
firstPullAt = 0;
|
|
946
|
-
constructor(child, logger, deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs) {
|
|
966
|
+
constructor(child, logger, deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs, onDialTiming) {
|
|
947
967
|
this.child = child;
|
|
948
968
|
this.logger = logger;
|
|
949
969
|
this.deviceId = deviceId;
|
|
950
970
|
this.firstFrameTimeoutMs = firstFrameTimeoutMs;
|
|
951
971
|
this.frameTimeoutMs = frameTimeoutMs;
|
|
952
972
|
this.firstFrameMaxWaitMs = firstFrameMaxWaitMs;
|
|
973
|
+
this.onDialTiming = onDialTiming;
|
|
953
974
|
this.child.on("message", (m) => this.handleMessage(m));
|
|
954
975
|
this.child.on("exit", (m) => this.handleExit(toWorkerExitInfo(m)));
|
|
955
976
|
}
|
|
@@ -1125,6 +1146,15 @@ var SessionDecodeSession = class {
|
|
|
1125
1146
|
case "log":
|
|
1126
1147
|
this.logger[raw.level](raw.message);
|
|
1127
1148
|
return;
|
|
1149
|
+
case "dialTiming":
|
|
1150
|
+
this.onDialTiming?.({
|
|
1151
|
+
hwContextMs: raw.hwContextMs,
|
|
1152
|
+
demuxerOpenMs: raw.demuxerOpenMs,
|
|
1153
|
+
decoderCreateMs: raw.decoderCreateMs,
|
|
1154
|
+
firstDecodeMs: raw.firstDecodeMs,
|
|
1155
|
+
streamInfoProbed: raw.streamInfoProbed
|
|
1156
|
+
});
|
|
1157
|
+
return;
|
|
1128
1158
|
}
|
|
1129
1159
|
}
|
|
1130
1160
|
handleExit(info) {
|
|
@@ -1349,7 +1379,7 @@ function createSessionDecodeCoordinator(deps) {
|
|
|
1349
1379
|
const frameTimeoutMs = deps.frameTimeoutMs ?? DEFAULT_FRAME_TIMEOUT_MS;
|
|
1350
1380
|
const firstFrameMaxWaitMs = Math.max(deps.firstFrameMaxWaitMs ?? DEFAULT_FIRST_FRAME_MAX_WAIT_MS, firstFrameTimeoutMs);
|
|
1351
1381
|
return { async *generateVideoFrames(source, opts) {
|
|
1352
|
-
const session = new SessionDecodeSession(deps.fork(source), deps.logger, source.deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs);
|
|
1382
|
+
const session = new SessionDecodeSession(deps.fork(source), deps.logger, source.deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs, deps.onDialTiming);
|
|
1353
1383
|
session.start(source, opts);
|
|
1354
1384
|
try {
|
|
1355
1385
|
while (true) {
|
|
@@ -1403,9 +1433,13 @@ async function toDecodedFrame(frame, format, viewSpec) {
|
|
|
1403
1433
|
*/
|
|
1404
1434
|
function startSessionDecodePump(deps) {
|
|
1405
1435
|
const format = deps.opts.format ?? "rgb";
|
|
1436
|
+
let dialTiming = null;
|
|
1406
1437
|
const iterator = createSessionDecodeCoordinator({
|
|
1407
1438
|
fork: deps.fork,
|
|
1408
1439
|
logger: deps.logger,
|
|
1440
|
+
onDialTiming: (timing) => {
|
|
1441
|
+
dialTiming = timing;
|
|
1442
|
+
},
|
|
1409
1443
|
...deps.firstFrameTimeoutMs !== void 0 ? { firstFrameTimeoutMs: deps.firstFrameTimeoutMs } : {},
|
|
1410
1444
|
...deps.frameTimeoutMs !== void 0 ? { frameTimeoutMs: deps.frameTimeoutMs } : {}
|
|
1411
1445
|
}).generateVideoFrames(deps.source, deps.opts);
|
|
@@ -1436,9 +1470,11 @@ function startSessionDecodePump(deps) {
|
|
|
1436
1470
|
if (!firstFrameSeen) {
|
|
1437
1471
|
firstFrameSeen = true;
|
|
1438
1472
|
releaseSpawnSlotOnce();
|
|
1473
|
+
const dial = dialTiming;
|
|
1439
1474
|
deps.onFirstFrame?.({
|
|
1440
1475
|
spawnWaitMs: spawnGrantedAt - pumpStartedAt,
|
|
1441
|
-
forkToFrameMs: Date.now() - spawnGrantedAt
|
|
1476
|
+
forkToFrameMs: Date.now() - spawnGrantedAt,
|
|
1477
|
+
...dial !== null ? { dial } : {}
|
|
1442
1478
|
});
|
|
1443
1479
|
}
|
|
1444
1480
|
if (stopped) {
|
|
@@ -7346,6 +7382,13 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7346
7382
|
dialMs,
|
|
7347
7383
|
spawnWaitMs: timing.spawnWaitMs,
|
|
7348
7384
|
forkToFrameMs: timing.forkToFrameMs,
|
|
7385
|
+
...timing.dial ? {
|
|
7386
|
+
hwContextMs: timing.dial.hwContextMs,
|
|
7387
|
+
demuxerOpenMs: timing.dial.demuxerOpenMs,
|
|
7388
|
+
decoderCreateMs: timing.dial.decoderCreateMs,
|
|
7389
|
+
firstDecodeMs: timing.dial.firstDecodeMs,
|
|
7390
|
+
streamInfoProbed: timing.dial.streamInfoProbed
|
|
7391
|
+
} : {},
|
|
7349
7392
|
firstFrameMs: Date.now() - requestedAt,
|
|
7350
7393
|
...phaseActiveAt !== void 0 ? { sincePhaseActiveMs: Date.now() - phaseActiveAt } : {}
|
|
7351
7394
|
}
|