@camera.ui/server 2.1.8 → 2.1.9
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/interface/assets/{404-vRjJVgl4.js → 404-BEb5gvkW.js} +1 -1
- package/dist/interface/assets/{About-CO9cx5Gp.js → About-BKkjuIVJ.js} +1 -1
- package/dist/interface/assets/Automation-DA89AEEI.js +3 -0
- package/dist/interface/assets/{AutomationImport-DAeFd-SZ.js → AutomationImport-hYr3Lp8F.js} +2 -2
- package/dist/interface/assets/{AutomationStore-NyXuw3tI.js → AutomationStore-DKhnExZn.js} +2 -2
- package/dist/interface/assets/{AutomationStoreDetail-Dz9Y8j8g.js → AutomationStoreDetail-DmqqEOir.js} +2 -2
- package/dist/interface/assets/{Automations-BeGe7iQE.js → Automations-B6WFJyxS.js} +2 -2
- package/dist/interface/assets/{Camera-C0sNg85L.js → Camera-DmsSKvpY.js} +2 -2
- package/dist/interface/assets/{CameraConsole-S9Il0g0K.js → CameraConsole-DVpyGhx8.js} +1 -1
- package/dist/interface/assets/Cameras-KOOphtlx.js +652 -0
- package/dist/interface/assets/Camview-BPQIinyP.js +1 -0
- package/dist/interface/assets/{Camview-CS7PIBMz.css → Camview-Lp3QU0de.css} +1 -1
- package/dist/interface/assets/{Config-B0cy1RIU.js → Config-DeUec_TN.js} +1 -1
- package/dist/interface/assets/ConfigActionCameraControl-DWHg-YEU.js +1 -0
- package/dist/interface/assets/{ConfigActionDelay-DrzMP_7q.js → ConfigActionDelay-C8nNA7TC.js} +1 -1
- package/dist/interface/assets/{ConfigActionHttp-BUANm5zh.js → ConfigActionHttp-Dc97gItH.js} +1 -1
- package/dist/interface/assets/{ConfigActionImageInput-CKvnzs_4.js → ConfigActionImageInput-DpOGqlzA.js} +1 -1
- package/dist/interface/assets/{ConfigActionMqtt-Cz-fMzXH.js → ConfigActionMqtt-D5DJ21oe.js} +1 -1
- package/dist/interface/assets/{ConfigActionNotification-exxG4m-k.js → ConfigActionNotification-4wKfanGB.js} +1 -1
- package/dist/interface/assets/{ConfigActionNotificationControl-BMFbm9lJ.js → ConfigActionNotificationControl-jSgxCZmV.js} +1 -1
- package/dist/interface/assets/{ConfigActionOutput-js-UUBci.js → ConfigActionOutput-C8Esieni.js} +1 -1
- package/dist/interface/assets/{ConfigActionPlugin-B5hrC_B6.js → ConfigActionPlugin-C_cv60d3.js} +1 -1
- package/dist/interface/assets/{ConfigActionSensor-BESCtedA.js → ConfigActionSensor-CFIKoJ3-.js} +1 -1
- package/dist/interface/assets/{ConfigActionSnapshot-Uxz9dCqh.js → ConfigActionSnapshot-BqaTwtN3.js} +1 -1
- package/dist/interface/assets/{ConfigActionVariable-CsiC6bgF.js → ConfigActionVariable-De9RCGU4.js} +1 -1
- package/dist/interface/assets/{ConfigConditionIfElse-CiScJB42.js → ConfigConditionIfElse-CeNDIucw.js} +1 -1
- package/dist/interface/assets/{ConfigConditionSensorState-CbDfsuOl.js → ConfigConditionSensorState-C_Q9gU23.js} +1 -1
- package/dist/interface/assets/{ConfigConditionSwitch-B7E5DAoC.js → ConfigConditionSwitch-DR63IGcD.js} +1 -1
- package/dist/interface/assets/{ConfigConditionTime-BmeGYmwH.js → ConfigConditionTime-C9iuSxxe.js} +1 -1
- package/dist/interface/assets/{ConfigSensorValueInput-CNTToTaw.js → ConfigSensorValueInput-DnMlFzil.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerDetection-ijTkd_SS.js → ConfigTriggerDetection-iThbCKOO.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerGeofence-Q7LQpVoM.js → ConfigTriggerGeofence-BeKwVWvS.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerManual-Z1_1wUUM.js → ConfigTriggerManual-DPkRlUqD.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerMqtt-BBvD0ow9.js → ConfigTriggerMqtt-kd65tGJE.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerSchedule-B0rMjsZL.js → ConfigTriggerSchedule-D6MoVDPc.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerSensor-B1p15KGD.js → ConfigTriggerSensor-AMQM6f2r.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerSystem-Du-AkCKy.js → ConfigTriggerSystem-CNBgtDF1.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerWebhook-Debqt-nw.js → ConfigTriggerWebhook-B70DobSi.js} +1 -1
- package/dist/interface/assets/{Console-Gu1eu82G.js → Console-N_2Vornl.js} +1 -1
- package/dist/interface/assets/{CuiAutomationRunsDialog-CxcWpqrI.js → CuiAutomationRunsDialog-Cp6Apki9.js} +1 -1
- package/dist/interface/assets/CuiBottomSheet-CIR_fmSk.js +1 -0
- package/dist/interface/assets/{CuiBottomSheet-DECWGdrp.js → CuiBottomSheet-D5N8X1K-.js} +1 -1
- package/dist/interface/assets/CuiCameraPipCard-DP6X45oq.js +1 -0
- package/dist/interface/assets/CuiCameraRecordings-CRjXsdO4.js +1 -0
- package/dist/interface/assets/{CuiCameraShares-D32cM63m.js → CuiCameraShares-DMHwZEf1.js} +1 -1
- package/dist/interface/assets/{CuiCameraTable-Bk9N-Oc6.js → CuiCameraTable-AnEPu3XS.js} +1 -1
- package/dist/interface/assets/CuiCameraTable-D5ZqTLVt.js +1 -0
- package/dist/interface/assets/CuiConsole-CqEvNGe_.js +5 -0
- package/dist/interface/assets/{CuiDataTable-P8bWN0ZK.js → CuiDataTable-wA_LbHH8.js} +1 -1
- package/dist/interface/assets/{CuiLightControl-D5Z1AqWH.js → CuiLightControl-CB_Tp4tI.js} +1 -1
- package/dist/interface/assets/{CuiList-w69dERIq.js → CuiList-DrYuFhK4.js} +1 -1
- package/dist/interface/assets/CuiPluginCard-DEuNt5ni.js +2 -0
- package/dist/interface/assets/{CuiPluginCategoryChip-CQ2FJafE.js → CuiPluginCategoryChip-BbkbkYj6.js} +1 -1
- package/dist/interface/assets/{CuiPluginOAuthButton-CNxj7cuf.js → CuiPluginOAuthButton-vMuRa9Qd.js} +2 -2
- package/dist/interface/assets/{CuiPluginTrustBadge-DYsEz6WI.js → CuiPluginTrustBadge-2wsPzYZA.js} +1 -1
- package/dist/interface/assets/{CuiQRCode-CZnspFFw.js → CuiQRCode-Dh9EcaVc.js} +1 -1
- package/dist/interface/assets/{CuiRecordingsGrid-wEaU6-3c.js → CuiRecordingsGrid-HjZEiSyp.js} +1 -1
- package/dist/interface/assets/{CuiSchema-DLV1lIiB.js → CuiSchema-CSFoI6cg.js} +1 -1
- package/dist/interface/assets/{CuiTopNavbarItem-7yu9c68R.js → CuiTopNavbarItem-CSdvkKHR.js} +1 -1
- package/dist/interface/assets/{Faces-BlPQek1-.js → Faces-CPZ5RA68.js} +1 -1
- package/dist/interface/assets/{FirstSteps-C-DKv_P2.js → FirstSteps-CaF8STWH.js} +1 -1
- package/dist/interface/assets/{Instances-Bmb-yWM-.js → Instances-Cw7A4I7E.js} +1 -1
- package/dist/interface/assets/{Logs-DxHqkMpx.js → Logs-4Mgj7YWu.js} +2 -2
- package/dist/interface/assets/{Menu-DceReBlq.js → Menu-T-54ZSSD.js} +1 -1
- package/dist/interface/assets/Metrics-CQGKmHev.css +1 -0
- package/dist/interface/assets/Metrics-Dgm5qpzh.js +6 -0
- package/dist/interface/assets/{OAuthDeviceFlow-CLi6l8jS.js → OAuthDeviceFlow-DGikOkgK.js} +1 -1
- package/dist/interface/assets/{Plugin-BBVxbQeT.js → Plugin-YxqbMbUc.js} +1 -1
- package/dist/interface/assets/{PluginConsole-xOY_kwz_.js → PluginConsole-ZZvJlgIh.js} +1 -1
- package/dist/interface/assets/PluginDetail-B9NgbzkJ.js +2 -0
- package/dist/interface/assets/{PluginSchema-DG3ou73v.js → PluginSchema-Cx_LInpM.js} +1 -1
- package/dist/interface/assets/PluginSearch-5AgSWlmi.js +1 -0
- package/dist/interface/assets/PluginSearch-P274oMvy.js +2 -0
- package/dist/interface/assets/{PluginUpdateProgress-Bx0MXwAk.js → PluginUpdateProgress-0nV-8LvI.js} +1 -1
- package/dist/interface/assets/{Plugins-COHwVV0A.js → Plugins-Bvru3q2P.js} +1 -1
- package/dist/interface/assets/Recordings-BWGWC6Kq.js +1 -0
- package/dist/interface/assets/Recordings-cR4_6aGN.css +1 -0
- package/dist/interface/assets/{SensorEdit-C-H_3ap8.js → SensorEdit-gEw6HHZs.js} +1 -1
- package/dist/interface/assets/{SensorHistory-CTIUwIWN.js → SensorHistory-IgMpuLdC.js} +1 -1
- package/dist/interface/assets/{Sensors-lPcOdHJN.js → Sensors-DukA8Xqx.js} +2 -2
- package/dist/interface/assets/{Settings-BUoXoFiH.js → Settings-DWpeWPJh.js} +1 -1
- package/dist/interface/assets/{SettingsAccount-DUy_W02_.js → SettingsAccount-C1OpCtob.js} +1 -1
- package/dist/interface/assets/SettingsAccount-CrLcqzSn.js +1 -0
- package/dist/interface/assets/SettingsAppearance-AhUjml5E.js +1 -0
- package/dist/interface/assets/{SettingsAppearance-CoTdsIwM.js → SettingsAppearance-C8EEBVZN.js} +1 -1
- package/dist/interface/assets/{SettingsBackup-ibb5I5Jv.js → SettingsBackup-BJupyZCJ.js} +1 -1
- package/dist/interface/assets/{SettingsList-W5FvbkXm.js → SettingsList-MXYSQMZ3.js} +1 -1
- package/dist/interface/assets/{SettingsMqtt-Ct42icAW.js → SettingsMqtt-F3pV1Bvo.js} +1 -1
- package/dist/interface/assets/{SettingsNotifications-B4lyMlLR.js → SettingsNotifications-BmaAccX9.js} +1 -1
- package/dist/interface/assets/SettingsPermissions-Dd_yfNlk.js +1 -0
- package/dist/interface/assets/{SettingsRecordings-COUi9BQg.js → SettingsRecordings-85B2bQIf.js} +1 -1
- package/dist/interface/assets/SettingsRemote-C_ikoXW7.js +2 -0
- package/dist/interface/assets/SettingsRemote-DzrfpHjw.css +1 -0
- package/dist/interface/assets/SettingsSystem-Ct3qms5O.js +2 -0
- package/dist/interface/assets/{SettingsUsers-Cjk6mevW.js → SettingsUsers-D36vZ8Zf.js} +1 -1
- package/dist/interface/assets/{Terminal-BIdBrL4g.js → Terminal-ltPiB9CB.js} +3 -3
- package/dist/interface/assets/{VariableInput-DhAhhfsm.js → VariableInput-7c7Px-DM.js} +1 -1
- package/dist/interface/assets/{VariableSuggestions-q0Jd1DVI.js → VariableSuggestions-CrEa2zN-.js} +1 -1
- package/dist/interface/assets/{VersionsHandler-XEn3uFy7.js → VersionsHandler-BFuLGk6Y.js} +1 -1
- package/dist/interface/assets/{VirtualSensorCreate-_E5E0U7E.js → VirtualSensorCreate-uumfOHgB.js} +1 -1
- package/dist/interface/assets/{Workers-DVABUc39.js → Workers-Bowrc1AF.js} +1 -1
- package/dist/interface/assets/{api-C7Yv5Scr.js → api-DLVsQBAO.js} +1 -1
- package/dist/interface/assets/api-daCggwpn.css +2 -0
- package/dist/interface/assets/api-py5HP4SF.js +2 -0
- package/dist/interface/assets/{auth-BixzEnBM.js → auth-uQlIia9h.js} +1 -1
- package/dist/interface/assets/{automations-K8XPcP3U.js → automations-ChG1qcO7.js} +1 -1
- package/dist/interface/assets/cameras.schema-Cv7cjMz3.js +1 -0
- package/dist/interface/assets/cloudHandoff-CL4sircA.js +1 -0
- package/dist/interface/assets/{config-CURuT8_k.js → config-B1aWtF-N.js} +1 -1
- package/dist/interface/assets/content-copy-CLkJTA4w.js +1 -0
- package/dist/interface/assets/eventLabels-BsPoPWS9.js +1 -0
- package/dist/interface/assets/{flowSchema-szT9B0sA.js → flowSchema-CNnBxcwy.js} +1 -1
- package/dist/interface/assets/{intercomService-BjB4QQiX.js → intercomService-DJHBix4X.js} +1 -1
- package/dist/interface/assets/main-CLvcwWW7.js +3 -0
- package/dist/interface/assets/main-mJXdi_gx.css +1 -0
- package/dist/interface/assets/{mqtt-uItF3Vd7.js → mqtt-Cyhv8shC.js} +1 -1
- package/dist/interface/assets/{notifications-CuZPzI4-.js → notifications-DniREnpL.js} +1 -1
- package/dist/interface/assets/{plugins-QEqfAk0G.js → plugins-5gXj0utE.js} +1 -1
- package/dist/interface/assets/round-restart-alt-D3jT5i_T.js +1 -0
- package/dist/interface/assets/round-restart-alt-DdIfy7mU.css +1 -0
- package/dist/interface/assets/{sensorIcons-BujySw0j.js → sensorIcons-BXauWntX.js} +1 -1
- package/dist/interface/assets/{sensors-BxvL01cB.js → sensors-Bc02Z1G9.js} +1 -1
- package/dist/interface/assets/{server-CDm6FwHH.js → server-D2KGja0u.js} +1 -1
- package/dist/interface/assets/{theme-CRw47tnu.js → theme-vkX_uBgJ.js} +1 -1
- package/dist/interface/assets/types-CrvnT4-i.js +1 -0
- package/dist/interface/assets/types-D6IQ6ssc.js +1 -0
- package/dist/interface/assets/{types-DtdpTPfZ.js → types-fCQkAGfW.js} +1 -1
- package/dist/interface/assets/{useAppVersion-BxTTVLKg.js → useAppVersion-9zH_FspP.js} +1 -1
- package/dist/interface/assets/{useBackupRestore-DLieDBFC.js → useBackupRestore-Dbf24OqP.js} +1 -1
- package/dist/interface/assets/{useCameraOptions-DMC2KjMS.js → useCameraOptions-BIaDXHeq.js} +1 -1
- package/dist/interface/assets/{useLogsSocket-CmyalQi8.js → useLogsSocket-BLV7dWSG.js} +1 -1
- package/dist/interface/assets/{useNavLayout-BacPxpZv.js → useNavLayout-wq-6JyWK.js} +1 -1
- package/dist/interface/assets/{usePermissions-C2wQBWmH.js → usePermissions-BiBlyeQl.js} +1 -1
- package/dist/interface/assets/{usePluginUpdates-CyjxFTTi.js → usePluginUpdates-BZJSqnSF.js} +1 -1
- package/dist/interface/assets/{usePushRegistration-BPSfvI_w.js → usePushRegistration-CGFiyCuv.js} +1 -1
- package/dist/interface/assets/{useServerRestart-x8p2rFqn.js → useServerRestart-CjPAgzoX.js} +1 -1
- package/dist/interface/assets/{useStreamStatus-PMpl519O.js → useStreamStatus-BFCF1B3T.js} +1 -1
- package/dist/interface/assets/{useUpdateChannel-ChscS5KW.js → useUpdateChannel-F4WpbMJR.js} +1 -1
- package/dist/interface/assets/{useUserOptions-YiEj2ox6.js → useUserOptions-GbSC7_qt.js} +1 -1
- package/dist/interface/assets/{vendor-cameraui-Cf0XVNQ7.js → vendor-cameraui-0EJfDg8o.js} +56 -56
- package/dist/interface/assets/vendor-core-RnPzfwaA.js +1 -0
- package/dist/interface/assets/{vendor-primevue-BDu2BpXv.js → vendor-primevue-b9CB9Uh0.js} +1 -1
- package/dist/interface/index.html +26 -25
- package/dist/interface/sw.js +1 -1
- package/dist/server/package.json +6 -6
- package/dist/server/requirements.txt +2 -2
- package/dist/server/src/_camera_ui_tools/camera_ui_rpc/client.py +8 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_rpc/types.py +20 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/__init__.py +4 -2
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/__init__.py +6 -2
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/config.py +3 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/detection.py +22 -8
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/enums.py +0 -7
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/notification.py +28 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/__init__.py +4 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/base.py +18 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/spec.py +27 -3
- package/dist/server/src/api/controllers/auth.controller.js +5 -0
- package/dist/server/src/api/controllers/auth.controller.js.map +1 -1
- package/dist/server/src/api/controllers/frameWorkers.controller.d.ts +14 -0
- package/dist/server/src/api/controllers/frameWorkers.controller.js +120 -1
- package/dist/server/src/api/controllers/frameWorkers.controller.js.map +1 -1
- package/dist/server/src/api/controllers/remote.controller.js +3 -0
- package/dist/server/src/api/controllers/remote.controller.js.map +1 -1
- package/dist/server/src/api/controllers/server.controller.js +1 -0
- package/dist/server/src/api/controllers/server.controller.js.map +1 -1
- package/dist/server/src/api/database/constants.d.ts +0 -1
- package/dist/server/src/api/database/constants.js +0 -1
- package/dist/server/src/api/database/constants.js.map +1 -1
- package/dist/server/src/api/database/index.d.ts +2 -3
- package/dist/server/src/api/database/index.js +3 -6
- package/dist/server/src/api/database/index.js.map +1 -1
- package/dist/server/src/api/database/migrations/v2-1-10.js +33 -0
- package/dist/server/src/api/database/migrations/v2-1-10.js.map +1 -0
- package/dist/server/src/api/database/migrations/v2-1-11.js +31 -0
- package/dist/server/src/api/database/migrations/v2-1-11.js.map +1 -0
- package/dist/server/src/api/database/migrations/v2-1-12.js +25 -0
- package/dist/server/src/api/database/migrations/v2-1-12.js.map +1 -0
- package/dist/server/src/api/database/migrations/v2-1-6.js.map +1 -1
- package/dist/server/src/api/database/migrations/v2-1-9.d.ts +3 -0
- package/dist/server/src/api/database/migrations/v2-1-9.js +38 -0
- package/dist/server/src/api/database/migrations/v2-1-9.js.map +1 -0
- package/dist/server/src/api/database/record-schemas.d.ts +18 -13
- package/dist/server/src/api/database/record-schemas.js +3 -3
- package/dist/server/src/api/database/record-schemas.js.map +1 -1
- package/dist/server/src/api/database/types.d.ts +3 -11
- package/dist/server/src/api/routes/frameWorkers.routes.js +22 -1
- package/dist/server/src/api/routes/frameWorkers.routes.js.map +1 -1
- package/dist/server/src/api/schemas/cameras.schema.d.ts +127 -28
- package/dist/server/src/api/schemas/cameras.schema.js +40 -7
- package/dist/server/src/api/schemas/cameras.schema.js.map +1 -1
- package/dist/server/src/api/schemas/frameWorkers.schema.d.ts +6 -0
- package/dist/server/src/api/schemas/frameWorkers.schema.js +7 -0
- package/dist/server/src/api/schemas/frameWorkers.schema.js.map +1 -1
- package/dist/server/src/api/schemas/server.schema.d.ts +1 -0
- package/dist/server/src/api/schemas/server.schema.js +4 -0
- package/dist/server/src/api/schemas/server.schema.js.map +1 -1
- package/dist/server/src/api/schemas/users.schema.d.ts +119 -84
- package/dist/server/src/api/schemas/users.schema.js +3 -0
- package/dist/server/src/api/schemas/users.schema.js.map +1 -1
- package/dist/server/src/api/types/index.d.ts +1 -0
- package/dist/server/src/api/types/index.js.map +1 -1
- package/dist/server/src/api/websocket/nsp/metrics.d.ts +1 -1
- package/dist/server/src/api/websocket/nsp/metrics.js +54 -33
- package/dist/server/src/api/websocket/nsp/metrics.js.map +1 -1
- package/dist/server/src/api/websocket/nsp/server.js +2 -2
- package/dist/server/src/api/websocket/nsp/server.js.map +1 -1
- package/dist/server/src/api/websocket/types.d.ts +20 -17
- package/dist/server/src/camera/controller.js +1 -0
- package/dist/server/src/camera/controller.js.map +1 -1
- package/dist/server/src/camera/decoder/audio-loop.d.ts +0 -1
- package/dist/server/src/camera/decoder/audio-loop.js +38 -68
- package/dist/server/src/camera/decoder/audio-loop.js.map +1 -1
- package/dist/server/src/camera/decoder/child.d.ts +4 -1
- package/dist/server/src/camera/decoder/child.js +27 -0
- package/dist/server/src/camera/decoder/child.js.map +1 -1
- package/dist/server/src/camera/decoder/detection-coordinator.d.ts +7 -2
- package/dist/server/src/camera/decoder/detection-coordinator.js +226 -130
- package/dist/server/src/camera/decoder/detection-coordinator.js.map +1 -1
- package/dist/server/src/camera/decoder/detection-pipeline.d.ts +2 -2
- package/dist/server/src/camera/decoder/detection-pipeline.js +5 -6
- package/dist/server/src/camera/decoder/detection-pipeline.js.map +1 -1
- package/dist/server/src/camera/decoder/event-thumbnailer.d.ts +3 -3
- package/dist/server/src/camera/decoder/event-thumbnailer.js +2 -2
- package/dist/server/src/camera/decoder/event-thumbnailer.js.map +1 -1
- package/dist/server/src/camera/decoder/frame-scaler.d.ts +2 -1
- package/dist/server/src/camera/decoder/frame-scaler.js +25 -13
- package/dist/server/src/camera/decoder/frame-scaler.js.map +1 -1
- package/dist/server/src/camera/decoder/perf-tracker.d.ts +27 -8
- package/dist/server/src/camera/decoder/perf-tracker.js +72 -50
- package/dist/server/src/camera/decoder/perf-tracker.js.map +1 -1
- package/dist/server/src/camera/decoder/plugin-registry.d.ts +1 -0
- package/dist/server/src/camera/decoder/plugin-registry.js +7 -0
- package/dist/server/src/camera/decoder/plugin-registry.js.map +1 -1
- package/dist/server/src/camera/decoder/secondary-stage.d.ts +4 -1
- package/dist/server/src/camera/decoder/secondary-stage.js +26 -3
- package/dist/server/src/camera/decoder/secondary-stage.js.map +1 -1
- package/dist/server/src/camera/decoder/sources/analysis-source.d.ts +14 -1
- package/dist/server/src/camera/decoder/sources/audio-source.d.ts +7 -3
- package/dist/server/src/camera/decoder/sources/audio-source.js +89 -78
- package/dist/server/src/camera/decoder/sources/audio-source.js.map +1 -1
- package/dist/server/src/camera/decoder/sources/buffered-source.d.ts +26 -9
- package/dist/server/src/camera/decoder/sources/buffered-source.js +113 -65
- package/dist/server/src/camera/decoder/sources/buffered-source.js.map +1 -1
- package/dist/server/src/camera/decoder/sources/frame-handle.d.ts +13 -0
- package/dist/server/src/camera/decoder/sources/frame-handle.js +106 -0
- package/dist/server/src/camera/decoder/sources/frame-handle.js.map +1 -0
- package/dist/server/src/camera/decoder/sources/frame-source.d.ts +25 -26
- package/dist/server/src/camera/decoder/sources/frame-source.js +213 -268
- package/dist/server/src/camera/decoder/sources/frame-source.js.map +1 -1
- package/dist/server/src/camera/decoder/sources/reconnect-loop.d.ts +17 -0
- package/dist/server/src/camera/decoder/sources/reconnect-loop.js +59 -0
- package/dist/server/src/camera/decoder/sources/reconnect-loop.js.map +1 -0
- package/dist/server/src/camera/decoder/sources/snapshot-fetcher.d.ts +15 -0
- package/dist/server/src/camera/decoder/sources/snapshot-fetcher.js +58 -0
- package/dist/server/src/camera/decoder/sources/snapshot-fetcher.js.map +1 -0
- package/dist/server/src/camera/decoder/sources/video-input.d.ts +18 -0
- package/dist/server/src/camera/decoder/sources/video-input.js +55 -0
- package/dist/server/src/camera/decoder/sources/video-input.js.map +1 -0
- package/dist/server/src/camera/decoder/types.d.ts +73 -18
- package/dist/server/src/camera/decoder/types.js +2 -0
- package/dist/server/src/camera/decoder/types.js.map +1 -1
- package/dist/server/src/camera/decoder/worker.d.ts +5 -1
- package/dist/server/src/camera/decoder/worker.js +35 -5
- package/dist/server/src/camera/decoder/worker.js.map +1 -1
- package/dist/server/src/camera/index.d.ts +2 -1
- package/dist/server/src/camera/index.js +3 -0
- package/dist/server/src/camera/index.js.map +1 -1
- package/dist/server/src/camera/streaming/fmp4-session.js +1 -1
- package/dist/server/src/camera/streaming/fmp4-session.js.map +1 -1
- package/dist/server/src/camera/streaming/rtp-session.js +1 -1
- package/dist/server/src/camera/streaming/rtp-session.js.map +1 -1
- package/dist/server/src/mqtt/bridge.d.ts +0 -1
- package/dist/server/src/mqtt/bridge.js +0 -33
- package/dist/server/src/mqtt/bridge.js.map +1 -1
- package/dist/server/src/plugins/runtime/node/proxy/cameraDevice.js +9 -0
- package/dist/server/src/plugins/runtime/node/proxy/cameraDevice.js.map +1 -1
- package/dist/server/src/plugins/runtime/node/proxy/sensorManager.js +8 -0
- package/dist/server/src/plugins/runtime/node/proxy/sensorManager.js.map +1 -1
- package/dist/server/src/plugins/runtime/python/proxy/camera_device.py +14 -0
- package/dist/server/src/plugins/runtime/python/proxy/sensor.py +1 -0
- package/dist/server/src/plugins/runtime/python/proxy/sensor_manager.py +13 -0
- package/dist/server/src/remote/services/cloudflare-managed.d.ts +2 -0
- package/dist/server/src/remote/services/cloudflare-managed.js +9 -2
- package/dist/server/src/remote/services/cloudflare-managed.js.map +1 -1
- package/dist/server/src/remote/services/cloudflare.d.ts +3 -0
- package/dist/server/src/remote/services/cloudflare.js +11 -0
- package/dist/server/src/remote/services/cloudflare.js.map +1 -1
- package/dist/server/src/remote/services/index.d.ts +2 -0
- package/dist/server/src/remote/services/index.js +60 -8
- package/dist/server/src/remote/services/index.js.map +1 -1
- package/dist/server/src/remote/services/tunnelConnections.d.ts +9 -0
- package/dist/server/src/remote/services/tunnelConnections.js +45 -0
- package/dist/server/src/remote/services/tunnelConnections.js.map +1 -0
- package/dist/server/src/remote/services/urlReadiness.d.ts +2 -0
- package/dist/server/src/remote/services/urlReadiness.js +7 -4
- package/dist/server/src/remote/services/urlReadiness.js.map +1 -1
- package/dist/server/src/rpc/interfaces/frameworker.d.ts +4 -1
- package/dist/server/src/rpc/interfaces/sensor.d.ts +1 -0
- package/dist/server/src/sensors/registry.d.ts +2 -0
- package/dist/server/src/sensors/registry.js +16 -0
- package/dist/server/src/sensors/registry.js.map +1 -1
- package/dist/server/src/utils/plugin-registry/index.js +1 -1
- package/dist/server/src/utils/plugin-registry/index.js.map +1 -1
- package/dist/server/src/workers/agent.js +1 -1
- package/dist/server/src/workers/agent.js.map +1 -1
- package/package.json +6 -6
- package/dist/interface/assets/Automation-DDNhcjDg.js +0 -3
- package/dist/interface/assets/Cameras-lDMBkoUW.js +0 -652
- package/dist/interface/assets/Camview-DzMmXQvW.js +0 -1
- package/dist/interface/assets/ConfigActionCameraControl-BMlRbdRM.js +0 -1
- package/dist/interface/assets/CuiBottomSheet-BkmkKf1u.js +0 -1
- package/dist/interface/assets/CuiCameraPipCard-CuZZZ7Cv.js +0 -1
- package/dist/interface/assets/CuiCameraRecordings-BxEA_Vhd.js +0 -1
- package/dist/interface/assets/CuiCameraTable-67Itb7my.js +0 -1
- package/dist/interface/assets/CuiConsole-De9JpSLz.js +0 -5
- package/dist/interface/assets/CuiPluginCard-D3x9FPb6.js +0 -2
- package/dist/interface/assets/Metrics-BstlZavQ.css +0 -1
- package/dist/interface/assets/Metrics-Cw_Z_e0C.js +0 -1
- package/dist/interface/assets/PluginDetail-D2RncxpL.js +0 -2
- package/dist/interface/assets/PluginSearch-CBhloRm6.js +0 -1
- package/dist/interface/assets/PluginSearch-RGhSeM1T.js +0 -2
- package/dist/interface/assets/Recordings-H5qOm_rD.js +0 -1
- package/dist/interface/assets/Recordings-zdTzwv7C.css +0 -1
- package/dist/interface/assets/SettingsAccount-CudWgknL.js +0 -1
- package/dist/interface/assets/SettingsAppearance-eZsdWWp3.js +0 -1
- package/dist/interface/assets/SettingsPermissions-Bsv293ri.js +0 -1
- package/dist/interface/assets/SettingsRemote-DYld6-1F.css +0 -1
- package/dist/interface/assets/SettingsRemote-Dq_BD0fk.js +0 -1
- package/dist/interface/assets/SettingsSystem-C5xROdA0.js +0 -2
- package/dist/interface/assets/api-AVHJeggR.css +0 -2
- package/dist/interface/assets/api-CmdQlVDD.js +0 -2
- package/dist/interface/assets/cameras.schema-A_6JklLs.js +0 -1
- package/dist/interface/assets/cloudHandoff-D0ytFfHK.js +0 -1
- package/dist/interface/assets/main-B1ryIyTr.css +0 -1
- package/dist/interface/assets/main-BdIdmDhs.js +0 -3
- package/dist/interface/assets/round-restart-alt-B_7HR24a.js +0 -1
- package/dist/interface/assets/round-restart-alt-CmrjVFat.css +0 -1
- package/dist/interface/assets/types-R10eIequ.js +0 -1
- package/dist/interface/assets/types-p_uC0rfp.js +0 -1
- package/dist/interface/assets/vendor-core-u4xNQ-aF.js +0 -1
- package/dist/server/src/api/database/migrations/v2-0-50.js +0 -45
- package/dist/server/src/api/database/migrations/v2-0-50.js.map +0 -1
- package/dist/server/src/api/database/migrations/v2-0-51.js +0 -43
- package/dist/server/src/api/database/migrations/v2-0-51.js.map +0 -1
- package/dist/server/src/api/database/migrations/v2-1-0.js +0 -72
- package/dist/server/src/api/database/migrations/v2-1-0.js.map +0 -1
- /package/dist/server/src/api/database/migrations/{v2-0-50.d.ts → v2-1-10.d.ts} +0 -0
- /package/dist/server/src/api/database/migrations/{v2-0-51.d.ts → v2-1-11.d.ts} +0 -0
- /package/dist/server/src/api/database/migrations/{v2-1-0.d.ts → v2-1-12.d.ts} +0 -0
|
@@ -26,12 +26,12 @@ import { FrameScaler } from './frame-scaler.js';
|
|
|
26
26
|
import { directionBetween, directionOf, MOMENT_FORMATS, MOMENT_QUALITY, momentWindow, unionBox } from './moment-crop.js';
|
|
27
27
|
import { PerfTracker } from './perf-tracker.js';
|
|
28
28
|
import { normalizePlateText } from './plate-vote.js';
|
|
29
|
-
import { isVideoInputSpec, PluginRegistry } from './plugin-registry.js';
|
|
29
|
+
import { isVideoInputSpec, modelIdentity, PluginRegistry } from './plugin-registry.js';
|
|
30
30
|
import { PtzAutotracker } from './ptz/autotracker.js';
|
|
31
|
-
import { ReconnectBackoff } from './reconnect-backoff.js';
|
|
32
31
|
import { SecondaryStage } from './secondary-stage.js';
|
|
32
|
+
import { BufferedSource } from './sources/buffered-source.js';
|
|
33
33
|
import { FrameSource } from './sources/frame-source.js';
|
|
34
|
-
import { DETECT_TIMEOUT_MS, ensureDetectionBoxes, isFullFrameBox, MOTION_WIDTH_MAP } from './types.js';
|
|
34
|
+
import { DETECT_TIMEOUT_MS, DETECTOR_METRIC_TYPES, ensureDetectionBoxes, isFullFrameBox, MOTION_WIDTH_MAP } from './types.js';
|
|
35
35
|
const MOMENT_EVENTS = new Set(['objectEntered', 'objectWoke', 'objectRecovered', 'bestShotUpdated']);
|
|
36
36
|
const MOMENT_MOVING_SPEED = 0.05;
|
|
37
37
|
const MOMENT_ATTRIBUTE_MIN_AREA = 600;
|
|
@@ -46,6 +46,7 @@ const ACTIVE_TICK_MS = 100;
|
|
|
46
46
|
const MOTION_INTERVAL_MS = 200;
|
|
47
47
|
const TICK_SLACK_MS = 20;
|
|
48
48
|
const MAIN_STREAM_HOLD_MS = 5000;
|
|
49
|
+
const EXTERNAL_FRAME_MAX_AGE_MS = 1000;
|
|
49
50
|
let DetectionCoordinator = class DetectionCoordinator {
|
|
50
51
|
config;
|
|
51
52
|
proxy;
|
|
@@ -69,9 +70,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
69
70
|
feedingSensors = new Map();
|
|
70
71
|
dwell = new DwellManager();
|
|
71
72
|
privacy;
|
|
72
|
-
videoBackoff = new ReconnectBackoff();
|
|
73
73
|
// debugging
|
|
74
74
|
perf = new PerfTracker();
|
|
75
|
+
benchmarkRunning = false;
|
|
75
76
|
hqUpgrade;
|
|
76
77
|
mainStreamActive = false;
|
|
77
78
|
idleSince = 0;
|
|
@@ -92,28 +93,30 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
92
93
|
this.config = config;
|
|
93
94
|
this.proxy = proxy;
|
|
94
95
|
this.logger = logger;
|
|
95
|
-
const
|
|
96
|
-
streamUrl: config.streamUrl,
|
|
97
|
-
snapshotUrl: config.snapshotUrl,
|
|
98
|
-
decoder: this.config.frameWorkerSettings.decoder,
|
|
99
|
-
};
|
|
96
|
+
const snapshot = { snapshotUrl: config.snapshotUrl };
|
|
100
97
|
if (config.controllerSnapshotSourceId) {
|
|
101
98
|
const sourceId = config.controllerSnapshotSourceId;
|
|
102
99
|
const controllerProxy = this.proxy.createProxy(NamespaceManager.cameraNamespaces(config.cameraId).cameraControllerRpc);
|
|
103
|
-
|
|
100
|
+
snapshot.snapshotProvider = async () => {
|
|
104
101
|
const jpeg = await controllerProxy.snapshot(sourceId, true, true);
|
|
105
102
|
if (!jpeg || jpeg.byteLength === 0)
|
|
106
103
|
return null;
|
|
107
104
|
return Buffer.from(jpeg);
|
|
108
105
|
};
|
|
109
106
|
}
|
|
110
|
-
this.frameSource = new FrameSource(frameSourceConfig, logger);
|
|
111
107
|
this.config.zones = normalizeZones(config.zones);
|
|
112
108
|
this.privacy = new PrivacyMask(logger);
|
|
113
109
|
this.privacy.update(this.config.zones);
|
|
110
|
+
const decoder = this.config.frameWorkerSettings.decoder;
|
|
111
|
+
this.frameSource = config.streamHot
|
|
112
|
+
? new BufferedSource({ url: config.streamUrl, decoder, privacy: this.privacy, snapshot }, logger)
|
|
113
|
+
: new FrameSource({ streamUrl: config.streamUrl, decoder, ...snapshot }, logger);
|
|
114
|
+
// a hot source buffers from the start, the loop only decides what is analysed
|
|
115
|
+
if (config.streamHot)
|
|
116
|
+
this.frameSource.start();
|
|
114
117
|
this.frameScaler = new FrameScaler(null, logger, this.privacy);
|
|
115
118
|
this.pipeline = new DetectionPipeline(this.pipelineZones(), config.detectionSettings);
|
|
116
|
-
this.secondaries = new SecondaryStage(this, this.plugins, this.pipeline, this.frameScaler, this.proxy, logger);
|
|
119
|
+
this.secondaries = new SecondaryStage(this, this.plugins, this.pipeline, this.frameScaler, this.proxy, this.perf, logger);
|
|
117
120
|
if (config.zones.lines.length > 0) {
|
|
118
121
|
this.pipeline.updateLines(config.zones.lines, this.videoAspectRatio);
|
|
119
122
|
}
|
|
@@ -201,7 +204,86 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
201
204
|
return this.config.detectionSettings;
|
|
202
205
|
}
|
|
203
206
|
getPerfSnapshot() {
|
|
204
|
-
|
|
207
|
+
const { timings, ...counters } = this.perf.snapshot();
|
|
208
|
+
const detectors = {};
|
|
209
|
+
for (const type of DETECTOR_METRIC_TYPES) {
|
|
210
|
+
const plugin = this.plugins.get(type);
|
|
211
|
+
// motion without frames is the camera's own signal, there is no detector to report
|
|
212
|
+
if (!plugin || (type === SensorType.Motion && !plugin.requiresFrames))
|
|
213
|
+
continue;
|
|
214
|
+
const input = plugin.modelSpec?.input;
|
|
215
|
+
detectors[type] = {
|
|
216
|
+
plugin: plugin.pluginId,
|
|
217
|
+
input: isVideoInputSpec(input) ? `${input.width}x${input.height}` : undefined,
|
|
218
|
+
runtime: plugin.modelSpec?.runtime,
|
|
219
|
+
models: plugin.modelSpec?.models,
|
|
220
|
+
...timings[type],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
...counters,
|
|
225
|
+
detectors,
|
|
226
|
+
mainStreamEnabled: this.mainStreamAvailable,
|
|
227
|
+
frameAnalysis: this.plugins.shouldVideoBeActive(),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
resetPerf() {
|
|
231
|
+
this.perf.reset();
|
|
232
|
+
}
|
|
233
|
+
pauseForBenchmark(paused) {
|
|
234
|
+
this.benchmarkRunning = paused;
|
|
235
|
+
}
|
|
236
|
+
async runObjectBenchmark(iterations, concurrency) {
|
|
237
|
+
const plugin = this.plugins.get(SensorType.Object);
|
|
238
|
+
const input = plugin?.modelSpec?.input;
|
|
239
|
+
if (!plugin || !isVideoInputSpec(input))
|
|
240
|
+
return null;
|
|
241
|
+
const channels = input.format === 'gray' ? 1 : input.format === 'nv12' ? 1.5 : 3;
|
|
242
|
+
const frame = {
|
|
243
|
+
id: 'benchmark',
|
|
244
|
+
data: Buffer.alloc(Math.round(input.width * input.height * channels)),
|
|
245
|
+
width: input.width,
|
|
246
|
+
height: input.height,
|
|
247
|
+
format: input.format,
|
|
248
|
+
};
|
|
249
|
+
let handlerMs = 0;
|
|
250
|
+
let completed = 0;
|
|
251
|
+
let failed = 0;
|
|
252
|
+
const timed = this.proxy.createProxy(NamespaceManager.sensorProviderNamespaces(plugin.pluginId, plugin.sensorId).sensorRpc, {
|
|
253
|
+
onTiming: (_method, timing) => {
|
|
254
|
+
handlerMs += timing.handlerMs;
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
const startedAt = Date.now();
|
|
258
|
+
const workers = Array.from({ length: Math.max(1, concurrency) }, async () => {
|
|
259
|
+
while (completed + failed < iterations) {
|
|
260
|
+
completed++;
|
|
261
|
+
try {
|
|
262
|
+
await PromiseTimeout(timed.detectObjects(frame), DETECT_TIMEOUT_MS, undefined, `Benchmark call timed out after ${DETECT_TIMEOUT_MS}ms`);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
completed--;
|
|
266
|
+
failed++;
|
|
267
|
+
this.logger.debug('Benchmark call failed:', error);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
await Promise.all(workers);
|
|
272
|
+
const totalMs = Date.now() - startedAt;
|
|
273
|
+
return {
|
|
274
|
+
camera: '',
|
|
275
|
+
plugin: plugin.pluginId,
|
|
276
|
+
sensorId: plugin.sensorId,
|
|
277
|
+
runtime: plugin.modelSpec?.runtime,
|
|
278
|
+
models: plugin.modelSpec?.models,
|
|
279
|
+
input: `${input.width}x${input.height}`,
|
|
280
|
+
iterations: completed,
|
|
281
|
+
failed,
|
|
282
|
+
concurrency,
|
|
283
|
+
totalMs,
|
|
284
|
+
perSecond: totalMs > 0 ? Math.round((completed / (totalMs / 1000)) * 10) / 10 : 0,
|
|
285
|
+
handlerMs: completed > 0 ? Math.round((handlerMs / completed) * 10) / 10 : 0,
|
|
286
|
+
};
|
|
205
287
|
}
|
|
206
288
|
updateZoneConfig(zones) {
|
|
207
289
|
zones = normalizeZones(zones);
|
|
@@ -234,6 +316,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
234
316
|
}
|
|
235
317
|
async dispose() {
|
|
236
318
|
await this.stopVideoLoop();
|
|
319
|
+
await this.frameSource.stop();
|
|
237
320
|
await this.audioLoop.stop();
|
|
238
321
|
await this.thumbnailer.stop();
|
|
239
322
|
this.ptzAutotracker.dispose();
|
|
@@ -293,6 +376,12 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
293
376
|
// the registry only announces the plugin assigned for this type, everyone else reports into the void
|
|
294
377
|
if (!this.feedingSensors.has(sensorId))
|
|
295
378
|
return;
|
|
379
|
+
// a detector reports its spec again once its models finished loading
|
|
380
|
+
if (properties.modelSpec) {
|
|
381
|
+
this.applyModelSpec(sensorId, sensorType, properties.modelSpec);
|
|
382
|
+
if (Object.keys(properties).length === 1)
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
296
385
|
if (sensorType === SensorType.Object) {
|
|
297
386
|
const filtered = this.applyExternalDetectionFilters(sensorType, properties);
|
|
298
387
|
// presence feed keeps the autotracker's return-home timer alive even while
|
|
@@ -322,39 +411,19 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
322
411
|
this.processingExternalSecondary = true;
|
|
323
412
|
try {
|
|
324
413
|
const rawExternal = filtered.detections ?? [];
|
|
325
|
-
const
|
|
326
|
-
if (
|
|
327
|
-
try {
|
|
328
|
-
const results = { timestamp: Date.now() };
|
|
329
|
-
const objects = await this.runObjectAssist(peeked.frame, rawExternal);
|
|
330
|
-
this.ingestAssistedObjects(sensorId, objects);
|
|
331
|
-
// processExternal only adapts the shape, external boxes get no real tracking
|
|
332
|
-
const objectDetections = this.pipeline.processExternal(objects.detections);
|
|
333
|
-
await this.runSecondariesAndThumbnails({ frame: peeked.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
|
|
334
|
-
this.ingestResultsForAllSecondaries(results);
|
|
335
|
-
const snapshot = this.buildSnapshot();
|
|
336
|
-
if (results.thumbnails && results.thumbnails.length > 0) {
|
|
337
|
-
snapshot.thumbnails = results.thumbnails;
|
|
338
|
-
}
|
|
339
|
-
this.eventManager.processResults(snapshot);
|
|
340
|
-
}
|
|
341
|
-
finally {
|
|
342
|
-
await peeked[Symbol.asyncDispose]();
|
|
343
|
-
}
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
const fetched = await this.frameSource.fetchSnapshotFrame();
|
|
347
|
-
if (!fetched)
|
|
414
|
+
const handle = await this.frameSource.getFrame(EXTERNAL_FRAME_MAX_AGE_MS);
|
|
415
|
+
if (!handle)
|
|
348
416
|
return;
|
|
349
417
|
try {
|
|
350
418
|
const results = { timestamp: Date.now() };
|
|
351
|
-
const objects = await this.runObjectAssist(
|
|
419
|
+
const objects = await this.runObjectAssist(handle.frame, rawExternal);
|
|
352
420
|
this.ingestAssistedObjects(sensorId, objects);
|
|
353
421
|
if (objects.assisted) {
|
|
354
422
|
// real boxes: crop the object like the frame pipeline, so face and
|
|
355
423
|
// plate secondaries run on the subject instead of the whole scene
|
|
424
|
+
// (processExternal only adapts the shape, external boxes get no real tracking)
|
|
356
425
|
const objectDetections = this.pipeline.processExternal(objects.detections);
|
|
357
|
-
await this.runSecondariesAndThumbnails({ frame:
|
|
426
|
+
await this.runSecondariesAndThumbnails({ frame: handle.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
|
|
358
427
|
this.ingestResultsForAllSecondaries(results);
|
|
359
428
|
const snapshot = this.buildSnapshot();
|
|
360
429
|
if (results.thumbnails && results.thumbnails.length > 0) {
|
|
@@ -363,9 +432,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
363
432
|
this.eventManager.processResults(snapshot);
|
|
364
433
|
}
|
|
365
434
|
else {
|
|
366
|
-
const sceneJpeg = await this.secondaries.detectFullFrame(
|
|
435
|
+
const sceneJpeg = await this.secondaries.detectFullFrame(handle.frame, results);
|
|
367
436
|
try {
|
|
368
|
-
results.thumbnails = await this.secondaries.generateThumbnails(
|
|
437
|
+
results.thumbnails = await this.secondaries.generateThumbnails(handle.frame, this.frameScaler, results);
|
|
369
438
|
}
|
|
370
439
|
catch (error) {
|
|
371
440
|
this.logger.error('Thumbnail generation error:', error);
|
|
@@ -385,7 +454,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
385
454
|
}
|
|
386
455
|
}
|
|
387
456
|
finally {
|
|
388
|
-
await
|
|
457
|
+
await handle[Symbol.asyncDispose]();
|
|
389
458
|
}
|
|
390
459
|
}
|
|
391
460
|
finally {
|
|
@@ -546,7 +615,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
546
615
|
faceEmbeddingModel: cs.faceEmbeddingModel,
|
|
547
616
|
plates: cs.licensePlate?.detections ?? [],
|
|
548
617
|
plateVoting: this.plugins.get(SensorType.LicensePlate)?.requiresFrames === true,
|
|
549
|
-
plateMinConfidence: this.config.detectionSettings.licensePlate?.
|
|
618
|
+
plateMinConfidence: this.config.detectionSettings.licensePlate?.ocrConfidence,
|
|
550
619
|
plateMinLength: this.config.detectionSettings.licensePlate?.minLength,
|
|
551
620
|
faceMinConfidence: this.config.detectionSettings.face?.confidence,
|
|
552
621
|
classifiers: cs.classifiers ? Object.values(cs.classifiers).flatMap((c) => c.detections) : [],
|
|
@@ -596,12 +665,12 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
596
665
|
let boxlessLabel;
|
|
597
666
|
switch (sensorType) {
|
|
598
667
|
case SensorType.Face:
|
|
599
|
-
boxlessLabel = '
|
|
600
|
-
filtered = this.pipeline.runZoneFilterWithLabel(positioned, '
|
|
668
|
+
boxlessLabel = 'face';
|
|
669
|
+
filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'face');
|
|
601
670
|
break;
|
|
602
671
|
case SensorType.LicensePlate:
|
|
603
|
-
boxlessLabel = '
|
|
604
|
-
filtered = this.pipeline.runZoneFilterWithLabel(positioned, '
|
|
672
|
+
boxlessLabel = 'license_plate';
|
|
673
|
+
filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'license_plate');
|
|
605
674
|
break;
|
|
606
675
|
case SensorType.Object:
|
|
607
676
|
filtered = this.pipeline.runZoneFilter(positioned);
|
|
@@ -755,6 +824,18 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
755
824
|
break;
|
|
756
825
|
}
|
|
757
826
|
}
|
|
827
|
+
applyModelSpec(sensorId, sensorType, modelSpec) {
|
|
828
|
+
const current = this.plugins.get(sensorType);
|
|
829
|
+
if (current?.sensorId !== sensorId)
|
|
830
|
+
return;
|
|
831
|
+
// another model means other numbers, keeping the old average would mislead;
|
|
832
|
+
// load time is not part of the identity, a reload of the same model is not a change
|
|
833
|
+
if (sensorType === SensorType.Object || sensorType === SensorType.Motion) {
|
|
834
|
+
if (modelIdentity(current.modelSpec) !== modelIdentity(modelSpec))
|
|
835
|
+
this.perf.reset();
|
|
836
|
+
}
|
|
837
|
+
this.plugins.updateModelSpec(sensorId, modelSpec);
|
|
838
|
+
}
|
|
758
839
|
async registerDetectionPluginInternal(sensor) {
|
|
759
840
|
if (this.videoStopPromise)
|
|
760
841
|
await this.videoStopPromise;
|
|
@@ -762,7 +843,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
762
843
|
const wasVideoNeeded = this.plugins.shouldVideoBeActive();
|
|
763
844
|
const wasAudioNeeded = this.plugins.shouldAudioBeActive();
|
|
764
845
|
const namespaces = NamespaceManager.sensorProviderNamespaces(sensor.pluginId, sensor.sensorId);
|
|
765
|
-
const sensorProxy = this.proxy.createProxy(namespaces.sensorRpc
|
|
846
|
+
const sensorProxy = this.proxy.createProxy(namespaces.sensorRpc, {
|
|
847
|
+
onTiming: (_method, timing) => this.perf.trackTiming(sensor.sensorType, timing.handlerMs, timing.transportMs),
|
|
848
|
+
});
|
|
766
849
|
const registered = this.plugins.register({
|
|
767
850
|
pluginId: sensor.pluginId,
|
|
768
851
|
sensorId: sensor.sensorId,
|
|
@@ -773,7 +856,8 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
773
856
|
});
|
|
774
857
|
if (!registered) {
|
|
775
858
|
// re-push from the registry: the sensor is already live, only the spec may have changed
|
|
776
|
-
|
|
859
|
+
if (sensor.modelSpec)
|
|
860
|
+
this.applyModelSpec(sensor.sensorId, sensor.sensorType, sensor.modelSpec);
|
|
777
861
|
return;
|
|
778
862
|
}
|
|
779
863
|
this.logger.trace(`Plugin registered: ${sensor.pluginId} for ${sensor.sensorType}`);
|
|
@@ -854,7 +938,6 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
854
938
|
return;
|
|
855
939
|
this.logger.debug('Starting video detection loop');
|
|
856
940
|
this.loopRunning = true;
|
|
857
|
-
this.videoBackoff.reset();
|
|
858
941
|
this.loopPromise = this.runDetectionLoop();
|
|
859
942
|
}
|
|
860
943
|
async stopVideoLoop() {
|
|
@@ -869,7 +952,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
869
952
|
this.mainStreamActive = false;
|
|
870
953
|
this.idleSince = 0;
|
|
871
954
|
const doStop = async () => {
|
|
872
|
-
await this.frameSource.
|
|
955
|
+
await this.frameSource.detach();
|
|
873
956
|
await this.loopPromise;
|
|
874
957
|
this.loopPromise = undefined;
|
|
875
958
|
};
|
|
@@ -896,81 +979,87 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
896
979
|
}
|
|
897
980
|
}
|
|
898
981
|
async runDetectionLoop() {
|
|
982
|
+
// the source reconnects on its own, nextFrame simply waits it out
|
|
983
|
+
const startedAt = Date.now();
|
|
984
|
+
await this.frameSource.start();
|
|
985
|
+
let lastFrameId = -1;
|
|
986
|
+
let seenGeneration = -1;
|
|
987
|
+
let firstFrame = true;
|
|
899
988
|
while (this.loopRunning) {
|
|
989
|
+
const tickStart = Date.now();
|
|
990
|
+
const snap = await this.frameSource.nextFrame(lastFrameId);
|
|
991
|
+
if (!snap)
|
|
992
|
+
break; // source stopped
|
|
993
|
+
if (firstFrame) {
|
|
994
|
+
firstFrame = false;
|
|
995
|
+
this.logger.debug(`Analysing after ${Date.now() - startedAt}ms`);
|
|
996
|
+
}
|
|
997
|
+
lastFrameId = snap.id;
|
|
998
|
+
if (this.benchmarkRunning) {
|
|
999
|
+
// the detector is saturated on purpose, feeding it real frames would
|
|
1000
|
+
// both distort the measurement and queue up behind it
|
|
1001
|
+
snap.frame[Symbol.dispose]?.();
|
|
1002
|
+
await sleep(IDLE_TICK_MS);
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
900
1005
|
try {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
// would reset the background model on every transition
|
|
918
|
-
const analysis = await this.acquireAnalysisFrame(snap.frame);
|
|
919
|
-
// debugging
|
|
920
|
-
detectionRecord.setFrame(analysis.isMainStream ? 'main' : 'low', analysis.frame.width, analysis.frame.height);
|
|
1006
|
+
if (this.frameSource.generation !== seenGeneration) {
|
|
1007
|
+
seenGeneration = this.frameSource.generation;
|
|
1008
|
+
// a fresh connection brings its own hardware context and resolution
|
|
1009
|
+
this.frameScaler.updateHardwareContext(this.frameSource.hardwareContext);
|
|
1010
|
+
this.frameScaler.clearCache();
|
|
1011
|
+
this.thumbnailer.sync(this.mainStreamAnalysisWanted);
|
|
1012
|
+
}
|
|
1013
|
+
// motion always reads the low stream: switching its input resolution
|
|
1014
|
+
// would reset the background model on every transition
|
|
1015
|
+
const analysis = await this.acquireAnalysisFrame(snap.frame);
|
|
1016
|
+
// debugging
|
|
1017
|
+
detectionRecord.setFrame(analysis.isMainStream ? 'main' : 'low', analysis.frame.width, analysis.frame.height);
|
|
1018
|
+
try {
|
|
1019
|
+
await this.processRawFrame(analysis, snap.frame);
|
|
1020
|
+
}
|
|
1021
|
+
finally {
|
|
921
1022
|
try {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
try {
|
|
926
|
-
if (this.hqUpgrade) {
|
|
927
|
-
this.hqUpgrade.frame[Symbol.dispose]?.();
|
|
928
|
-
this.hqUpgrade = undefined;
|
|
929
|
-
}
|
|
930
|
-
if (analysis.frame !== snap.frame)
|
|
931
|
-
analysis.frame[Symbol.dispose]?.();
|
|
932
|
-
snap.frame[Symbol.dispose]?.();
|
|
933
|
-
}
|
|
934
|
-
catch {
|
|
935
|
-
// best-effort
|
|
1023
|
+
if (this.hqUpgrade) {
|
|
1024
|
+
this.hqUpgrade.frame[Symbol.dispose]?.();
|
|
1025
|
+
this.hqUpgrade = undefined;
|
|
936
1026
|
}
|
|
1027
|
+
if (analysis.frame !== snap.frame)
|
|
1028
|
+
analysis.frame[Symbol.dispose]?.();
|
|
1029
|
+
}
|
|
1030
|
+
catch {
|
|
1031
|
+
// ignore
|
|
937
1032
|
}
|
|
938
|
-
this.updateStreamState();
|
|
939
|
-
// activity, not stream choice: cameras without main-stream analysis still count as active
|
|
940
|
-
if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment())
|
|
941
|
-
this.perf.activeTicks++;
|
|
942
|
-
else
|
|
943
|
-
this.perf.idleTicks++;
|
|
944
|
-
this.perf.report(this.logger);
|
|
945
|
-
const remaining = (this.mainStreamActive ? ACTIVE_TICK_MS : IDLE_TICK_MS) - (Date.now() - tickStart);
|
|
946
|
-
if (remaining > 0)
|
|
947
|
-
await sleep(remaining);
|
|
948
|
-
this.perf.loopMs += Date.now() - tickStart;
|
|
949
|
-
}
|
|
950
|
-
const streamError = this.frameSource.lastError;
|
|
951
|
-
await this.frameSource.stop();
|
|
952
|
-
this.frameScaler.clearCache();
|
|
953
|
-
if (this.loopRunning && streamError) {
|
|
954
|
-
const delay = this.videoBackoff.nextDelayMs();
|
|
955
|
-
this.logger.warn(`Stream ended with read error, reconnecting in ${delay / 1000}s: ${streamError.message}`);
|
|
956
|
-
await sleep(delay);
|
|
957
|
-
}
|
|
958
|
-
else if (this.loopRunning && frameCount === 0) {
|
|
959
|
-
this.logger.debug('Stream ended without frames, waiting before reconnect...');
|
|
960
|
-
await sleep(this.videoBackoff.idleDelayMs);
|
|
961
1033
|
}
|
|
1034
|
+
this.updateStreamState();
|
|
962
1035
|
}
|
|
963
1036
|
catch (error) {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
await this.frameSource.stop();
|
|
967
|
-
this.frameScaler.clearCache();
|
|
968
|
-
const delay = this.videoBackoff.nextDelayMs();
|
|
969
|
-
this.logger.warn(`Stream error, reconnecting in ${delay / 1000}s: ${error.message}`);
|
|
970
|
-
await sleep(delay);
|
|
1037
|
+
// the stream keeps running, a broken tick must not end the loop
|
|
1038
|
+
this.logger.error('Detection tick failed:', error);
|
|
971
1039
|
}
|
|
972
|
-
|
|
973
|
-
|
|
1040
|
+
finally {
|
|
1041
|
+
try {
|
|
1042
|
+
snap.frame[Symbol.dispose]?.();
|
|
1043
|
+
}
|
|
1044
|
+
catch {
|
|
1045
|
+
// ignore
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
const decode = this.frameSource.takeDecodeStats();
|
|
1049
|
+
this.perf.decodeMs += decode.ms;
|
|
1050
|
+
this.perf.decodedFrames += decode.frames;
|
|
1051
|
+
// activity, not stream choice: cameras without main-stream analysis still count as active
|
|
1052
|
+
if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment())
|
|
1053
|
+
this.perf.activeTicks++;
|
|
1054
|
+
else
|
|
1055
|
+
this.perf.idleTicks++;
|
|
1056
|
+
this.perf.report(this.logger);
|
|
1057
|
+
const remaining = (this.mainStreamActive ? ACTIVE_TICK_MS : IDLE_TICK_MS) - (Date.now() - tickStart);
|
|
1058
|
+
if (remaining > 0)
|
|
1059
|
+
await sleep(remaining);
|
|
1060
|
+
this.perf.loopMs += Date.now() - tickStart;
|
|
1061
|
+
}
|
|
1062
|
+
await this.frameSource.detach();
|
|
974
1063
|
this.frameScaler.clearCache();
|
|
975
1064
|
this.logger.debug('Detection loop ended');
|
|
976
1065
|
}
|
|
@@ -989,7 +1078,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
989
1078
|
const main = await this.thumbnailer.acquireHqFrame(0);
|
|
990
1079
|
if (main) {
|
|
991
1080
|
this.perf.mainFrames++;
|
|
992
|
-
this.perf.
|
|
1081
|
+
this.perf.mainDecodeMs += Date.now() - t0;
|
|
993
1082
|
return { frame: main.frame, scaler: main.scaler, isMainStream: true };
|
|
994
1083
|
}
|
|
995
1084
|
}
|
|
@@ -1007,7 +1096,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1007
1096
|
const main = await this.thumbnailer.acquireHqFrame(0);
|
|
1008
1097
|
if (main) {
|
|
1009
1098
|
this.perf.mainFrames++;
|
|
1010
|
-
this.perf.
|
|
1099
|
+
this.perf.mainDecodeMs += Date.now() - t0;
|
|
1011
1100
|
return { frame: main.frame, scaler: main.scaler, isMainStream: true };
|
|
1012
1101
|
}
|
|
1013
1102
|
}
|
|
@@ -1074,8 +1163,8 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1074
1163
|
try {
|
|
1075
1164
|
const motionInferStart = Date.now();
|
|
1076
1165
|
const result = await motionPlugin.proxy.detectMotion(motionFrame);
|
|
1077
|
-
this.perf.
|
|
1078
|
-
this.perf.
|
|
1166
|
+
this.perf.motionMs += Date.now() - motionInferStart;
|
|
1167
|
+
this.perf.motionCount++;
|
|
1079
1168
|
if (!this.loopRunning)
|
|
1080
1169
|
return;
|
|
1081
1170
|
if (ptzSuppressed) {
|
|
@@ -1126,16 +1215,18 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1126
1215
|
try {
|
|
1127
1216
|
const inferStart = Date.now();
|
|
1128
1217
|
const result = await PromiseTimeout(objectPlugin.proxy.detectObjects(objectFrame.model), DETECT_TIMEOUT_MS, undefined, `Object detection timed out after ${DETECT_TIMEOUT_MS}ms`);
|
|
1129
|
-
this.perf.
|
|
1130
|
-
this.perf.
|
|
1218
|
+
this.perf.objectMs += Date.now() - inferStart;
|
|
1219
|
+
this.perf.objectCount++;
|
|
1131
1220
|
if (!this.loopRunning)
|
|
1132
1221
|
return;
|
|
1133
1222
|
this.trackDetectionCadence();
|
|
1134
1223
|
// run the pipeline even on empty frames so Norfair advances its
|
|
1135
1224
|
// Kalman state; the pose delta keeps predictions stable across pans
|
|
1136
1225
|
const poseDelta = this.ptzAutotracker.consumePoseDelta();
|
|
1226
|
+
const postStart = Date.now();
|
|
1137
1227
|
const detected = FrameScaler.undoLetterbox(ensureDetectionBoxes(result.detections), objectFrame.geometry);
|
|
1138
1228
|
const pipelineResult = this.pipeline.process(detected, poseDelta);
|
|
1229
|
+
this.perf.postMs += Date.now() - postStart;
|
|
1139
1230
|
// the tick that opens a span analysed the low stream (the switch lands
|
|
1140
1231
|
// next tick), its picture work is worth a one-off HQ decode
|
|
1141
1232
|
if (this.updateWorldSpans(pipelineResult) && !analysis.isMainStream) {
|
|
@@ -1259,6 +1350,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1259
1350
|
if (objectDetections.length === 0)
|
|
1260
1351
|
return;
|
|
1261
1352
|
this.perf.objects += objectDetections.length;
|
|
1353
|
+
this.perf.framesWithObjects++;
|
|
1262
1354
|
const secondaryStart = Date.now();
|
|
1263
1355
|
await this.secondaries.detect(analysis.frame, analysis.scaler, objectDetections, results);
|
|
1264
1356
|
this.perf.secondaryMs += Date.now() - secondaryStart;
|
|
@@ -1280,15 +1372,18 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1280
1372
|
}
|
|
1281
1373
|
const facePlugin = this.plugins.get(SensorType.Face);
|
|
1282
1374
|
if (facePlugin) {
|
|
1375
|
+
// the crop path reaches ingestDetectionResult directly, so the zone
|
|
1376
|
+
// filter that guards the plugin path has to be applied here too
|
|
1377
|
+
const detections = this.pipeline.runZoneFilterWithLabel(results.face.detections, 'face');
|
|
1283
1378
|
// no mid-segment clear: a turned-away head yields a no-face frame and
|
|
1284
1379
|
// would flap the list, departure is reflected when the segment ends
|
|
1285
|
-
for (const detection of
|
|
1380
|
+
for (const detection of detections) {
|
|
1286
1381
|
if (detection.identity)
|
|
1287
1382
|
this.faceIdentities.add(detection.identity);
|
|
1288
1383
|
}
|
|
1289
1384
|
this.ingestDetectionResult(SensorType.Face, facePlugin.sensorId, {
|
|
1290
|
-
detected: results.face.detected,
|
|
1291
|
-
detections
|
|
1385
|
+
detected: detections.length > 0 && results.face.detected,
|
|
1386
|
+
detections,
|
|
1292
1387
|
identities: [...this.faceIdentities].sort(),
|
|
1293
1388
|
});
|
|
1294
1389
|
}
|
|
@@ -1296,14 +1391,15 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1296
1391
|
if (results.licensePlate) {
|
|
1297
1392
|
const lpdPlugin = this.plugins.get(SensorType.LicensePlate);
|
|
1298
1393
|
if (lpdPlugin) {
|
|
1299
|
-
|
|
1394
|
+
const detections = this.pipeline.runZoneFilterWithLabel(results.licensePlate.detections, 'license_plate');
|
|
1395
|
+
for (const detection of detections) {
|
|
1300
1396
|
const plate = detection.plateText ? normalizePlateText(detection.plateText) || detection.plateText : '';
|
|
1301
1397
|
if (plate)
|
|
1302
1398
|
this.platesSeen.add(plate);
|
|
1303
1399
|
}
|
|
1304
1400
|
this.ingestDetectionResult(SensorType.LicensePlate, lpdPlugin.sensorId, {
|
|
1305
|
-
detected: results.licensePlate.detected,
|
|
1306
|
-
detections
|
|
1401
|
+
detected: detections.length > 0 && results.licensePlate.detected,
|
|
1402
|
+
detections,
|
|
1307
1403
|
plates: [...this.platesSeen].sort(),
|
|
1308
1404
|
});
|
|
1309
1405
|
}
|
|
@@ -1593,8 +1689,8 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1593
1689
|
try {
|
|
1594
1690
|
const inferStart = Date.now();
|
|
1595
1691
|
const result = await PromiseTimeout(assist.proxy.detectObjects(scaled.model), DETECT_TIMEOUT_MS, undefined, `Object assist timed out after ${DETECT_TIMEOUT_MS}ms`);
|
|
1596
|
-
this.perf.
|
|
1597
|
-
this.perf.
|
|
1692
|
+
this.perf.assistMs += Date.now() - inferStart;
|
|
1693
|
+
this.perf.assistCount++;
|
|
1598
1694
|
const reportedLabels = new Set(reported.map((d) => d.label.toLowerCase()));
|
|
1599
1695
|
const boxed = FrameScaler.undoLetterbox(ensureDetectionBoxes(result?.detections ?? []), scaled.geometry);
|
|
1600
1696
|
const found = boxed.filter((d) => reportedLabels.size === 0 || reportedLabels.has(d.label.toLowerCase()));
|