@camera.ui/server 2.1.7 → 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-D0lfw52Y.js → 404-BEb5gvkW.js} +1 -1
- package/dist/interface/assets/{About-DaaVZ9Cn.js → About-BKkjuIVJ.js} +1 -1
- package/dist/interface/assets/Automation-DA89AEEI.js +3 -0
- package/dist/interface/assets/AutomationImport-hYr3Lp8F.js +2 -0
- package/dist/interface/assets/{AutomationStore-CeRF0iz6.js → AutomationStore-DKhnExZn.js} +2 -2
- package/dist/interface/assets/{AutomationStoreDetail-DcCFUBJf.js → AutomationStoreDetail-DmqqEOir.js} +2 -2
- package/dist/interface/assets/{Automations-GMWeSNQv.js → Automations-B6WFJyxS.js} +2 -2
- package/dist/interface/assets/Camera-DmsSKvpY.js +2 -0
- package/dist/interface/assets/{CameraConsole-B0crXI59.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-DxxONfeK.css → Camview-Lp3QU0de.css} +1 -1
- package/dist/interface/assets/Config-DeUec_TN.js +2 -0
- package/dist/interface/assets/ConfigActionCameraControl-DWHg-YEU.js +1 -0
- package/dist/interface/assets/{ConfigActionDelay-DjFp5qpw.js → ConfigActionDelay-C8nNA7TC.js} +1 -1
- package/dist/interface/assets/{ConfigActionHttp-Cmjz4iAV.js → ConfigActionHttp-Dc97gItH.js} +1 -1
- package/dist/interface/assets/{ConfigActionImageInput-B0Hll2M3.js → ConfigActionImageInput-DpOGqlzA.js} +1 -1
- package/dist/interface/assets/{ConfigActionMqtt-D2boY4u0.js → ConfigActionMqtt-D5DJ21oe.js} +1 -1
- package/dist/interface/assets/{ConfigActionNotification-7iRd0L5l.js → ConfigActionNotification-4wKfanGB.js} +1 -1
- package/dist/interface/assets/ConfigActionNotificationControl-jSgxCZmV.js +1 -0
- package/dist/interface/assets/{ConfigActionOutput-BTGUzNQW.js → ConfigActionOutput-C8Esieni.js} +1 -1
- package/dist/interface/assets/{ConfigActionPlugin-CzspRdJx.js → ConfigActionPlugin-C_cv60d3.js} +1 -1
- package/dist/interface/assets/{ConfigActionSensor-CPK-s1E2.js → ConfigActionSensor-CFIKoJ3-.js} +1 -1
- package/dist/interface/assets/{ConfigActionSnapshot-CcSw9Yw-.js → ConfigActionSnapshot-BqaTwtN3.js} +1 -1
- package/dist/interface/assets/{ConfigActionVariable-BCDtM13I.js → ConfigActionVariable-De9RCGU4.js} +1 -1
- package/dist/interface/assets/{ConfigConditionIfElse-DZJkBeHI.js → ConfigConditionIfElse-CeNDIucw.js} +1 -1
- package/dist/interface/assets/{ConfigConditionSensorState-CxWutq1T.js → ConfigConditionSensorState-C_Q9gU23.js} +1 -1
- package/dist/interface/assets/{ConfigConditionSwitch-DBRR9cW4.js → ConfigConditionSwitch-DR63IGcD.js} +1 -1
- package/dist/interface/assets/{ConfigConditionTime-B-5s6QW2.js → ConfigConditionTime-C9iuSxxe.js} +1 -1
- package/dist/interface/assets/{ConfigSensorValueInput-B9MmHF-E.js → ConfigSensorValueInput-DnMlFzil.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerDetection-DlY8ne3V.js → ConfigTriggerDetection-iThbCKOO.js} +1 -1
- package/dist/interface/assets/ConfigTriggerGeofence-BeKwVWvS.js +1 -0
- package/dist/interface/assets/{ConfigTriggerManual-BSxA9fGt.js → ConfigTriggerManual-DPkRlUqD.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerMqtt-C7sFSgYi.js → ConfigTriggerMqtt-kd65tGJE.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerSchedule-fqVY5k3V.js → ConfigTriggerSchedule-D6MoVDPc.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerSensor-BMZ74d8e.js → ConfigTriggerSensor-AMQM6f2r.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerSystem-B8zwJpaY.js → ConfigTriggerSystem-CNBgtDF1.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerWebhook-SklqcrZq.js → ConfigTriggerWebhook-B70DobSi.js} +1 -1
- package/dist/interface/assets/Console-N_2Vornl.js +1 -0
- package/dist/interface/assets/{CuiAutomationRunsDialog-N6NFABq2.js → CuiAutomationRunsDialog-Cp6Apki9.js} +1 -1
- package/dist/interface/assets/CuiBottomSheet-CIR_fmSk.js +1 -0
- package/dist/interface/assets/{CuiBottomSheet-BuK_lFkK.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-DMHwZEf1.js +1 -0
- package/dist/interface/assets/{CuiCameraTable-DOPSHZ28.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-DBNU5MJ_.js → CuiDataTable-wA_LbHH8.js} +1 -1
- package/dist/interface/assets/{CuiLightControl-CCwkaMEn.js → CuiLightControl-CB_Tp4tI.js} +1 -1
- package/dist/interface/assets/{CuiList-BOMdGNXj.js → CuiList-DrYuFhK4.js} +1 -1
- package/dist/interface/assets/CuiPluginCard-DEuNt5ni.js +2 -0
- package/dist/interface/assets/CuiPluginCategoryChip-BbkbkYj6.js +1 -0
- package/dist/interface/assets/{CuiPluginOAuthButton-6HQLdlfg.js → CuiPluginOAuthButton-vMuRa9Qd.js} +2 -2
- package/dist/interface/assets/{CuiPluginTrustBadge-BS9iEcZr.js → CuiPluginTrustBadge-2wsPzYZA.js} +1 -1
- package/dist/interface/assets/CuiQRCode-Dh9EcaVc.js +1 -0
- package/dist/interface/assets/CuiRecordingsGrid-HjZEiSyp.js +1 -0
- package/dist/interface/assets/{CuiRecordingsGrid-Sv3uyj9a.css → CuiRecordingsGrid-Q4C3aeLo.css} +1 -1
- package/dist/interface/assets/CuiSchema-CSFoI6cg.js +1 -0
- package/dist/interface/assets/{CuiTopNavbarItem-MCdYsYHh.js → CuiTopNavbarItem-CSdvkKHR.js} +1 -1
- package/dist/interface/assets/{Faces-DDTbgPtz.js → Faces-CPZ5RA68.js} +1 -1
- package/dist/interface/assets/FirstSteps-CaF8STWH.js +1 -0
- package/dist/interface/assets/Instances-Cw7A4I7E.js +1 -0
- package/dist/interface/assets/{Logs-DEQlc2WM.js → Logs-4Mgj7YWu.js} +2 -2
- package/dist/interface/assets/{Menu-0EVo1-xs.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--YqcKa9u.js → OAuthDeviceFlow-DGikOkgK.js} +1 -1
- package/dist/interface/assets/Plugin-YxqbMbUc.js +1 -0
- package/dist/interface/assets/{PluginConsole-BB_dbd25.js → PluginConsole-ZZvJlgIh.js} +1 -1
- package/dist/interface/assets/PluginDetail-B9NgbzkJ.js +2 -0
- package/dist/interface/assets/{PluginSchema-C4dQGJOV.js → PluginSchema-Cx_LInpM.js} +1 -1
- package/dist/interface/assets/PluginSearch-5AgSWlmi.js +1 -0
- package/dist/interface/assets/{PluginSearch-gY-PHMyu.js → PluginSearch-P274oMvy.js} +2 -2
- package/dist/interface/assets/{PluginUpdateProgress-BjeH5abL.js → PluginUpdateProgress-0nV-8LvI.js} +1 -1
- package/dist/interface/assets/{Plugins-Kz1A-VUA.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-CZyv_BYp.js → SensorEdit-gEw6HHZs.js} +1 -1
- package/dist/interface/assets/{SensorHistory-Mr03IqiL.js → SensorHistory-IgMpuLdC.js} +1 -1
- package/dist/interface/assets/Sensors-BUpob7LU.css +1 -0
- package/dist/interface/assets/Sensors-DukA8Xqx.js +2 -0
- package/dist/interface/assets/{Settings-CIv_iZ_m.js → Settings-DWpeWPJh.js} +1 -1
- package/dist/interface/assets/{Settings-B1MnGPuf.css → Settings-jQIhZFRY.css} +1 -1
- package/dist/interface/assets/SettingsAccount-C1OpCtob.js +1 -0
- package/dist/interface/assets/SettingsAccount-CrLcqzSn.js +1 -0
- package/dist/interface/assets/SettingsAppearance-AhUjml5E.js +1 -0
- package/dist/interface/assets/{SettingsAppearance-DEQyCR5b.js → SettingsAppearance-C8EEBVZN.js} +1 -1
- package/dist/interface/assets/{SettingsBackup-4y9MuseJ.js → SettingsBackup-BJupyZCJ.js} +1 -1
- package/dist/interface/assets/{SettingsList-DqyvUuS3.js → SettingsList-MXYSQMZ3.js} +1 -1
- package/dist/interface/assets/SettingsMqtt-F3pV1Bvo.js +1 -0
- package/dist/interface/assets/SettingsNotifications-BmaAccX9.js +1 -0
- package/dist/interface/assets/SettingsPermissions-Dd_yfNlk.js +1 -0
- package/dist/interface/assets/{SettingsRecordings-r9AwjQZX.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-D36vZ8Zf.js +1 -0
- package/dist/interface/assets/{Terminal-Xo2uy0RX.js → Terminal-ltPiB9CB.js} +3 -3
- package/dist/interface/assets/{VariableInput-C3Jbe-vC.js → VariableInput-7c7Px-DM.js} +1 -1
- package/dist/interface/assets/{VariableSuggestions-pOrYkqys.js → VariableSuggestions-CrEa2zN-.js} +1 -1
- package/dist/interface/assets/{VersionsHandler-BhBynPF8.js → VersionsHandler-BFuLGk6Y.js} +1 -1
- package/dist/interface/assets/{VirtualSensorCreate-B3bwlAJz.js → VirtualSensorCreate-uumfOHgB.js} +1 -1
- package/dist/interface/assets/Workers-Bowrc1AF.js +2 -0
- package/dist/interface/assets/{api-B7yg8bdk.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-y-X5RKO6.js → auth-uQlIia9h.js} +1 -1
- package/dist/interface/assets/{automations-h1Wvnh36.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-BZTZOfxI.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-Dxxlrn77.js → flowSchema-CNnBxcwy.js} +1 -1
- package/dist/interface/assets/intercomService-DJHBix4X.js +1 -0
- package/dist/interface/assets/main-CLvcwWW7.js +3 -0
- package/dist/interface/assets/main-mJXdi_gx.css +1 -0
- package/dist/interface/assets/mqtt-Cyhv8shC.js +1 -0
- package/dist/interface/assets/{nodeDefinitions-DQQlFmPN.js → nodeDefinitions-c5rXs9EW.js} +1 -1
- package/dist/interface/assets/{notifications-C8eyTPRM.js → notifications-DniREnpL.js} +1 -1
- package/dist/interface/assets/{plugins-D0Txz0BA.js → plugins-5gXj0utE.js} +1 -1
- package/dist/interface/assets/round-alt-arrow-left-bold-CfccdiWp.js +1 -0
- 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-r5wmAxeD.js → sensorIcons-BXauWntX.js} +1 -1
- package/dist/interface/assets/{sensors-MIbrTUSa.js → sensors-Bc02Z1G9.js} +1 -1
- package/dist/interface/assets/{server-alwZXRo-.js → server-D2KGja0u.js} +1 -1
- package/dist/interface/assets/{theme-qmnHJ2WT.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-DBWHsh0W.js → types-fCQkAGfW.js} +1 -1
- package/dist/interface/assets/useAppVersion-9zH_FspP.js +1 -0
- package/dist/interface/assets/useBackupRestore-Dbf24OqP.js +1 -0
- package/dist/interface/assets/{useCameraOptions-DYZKQx7X.js → useCameraOptions-BIaDXHeq.js} +1 -1
- package/dist/interface/assets/{useLogsSocket-DvwkpzT2.js → useLogsSocket-BLV7dWSG.js} +1 -1
- package/dist/interface/assets/{useNavLayout-CTX4KBkz.js → useNavLayout-wq-6JyWK.js} +1 -1
- package/dist/interface/assets/usePermissions-BiBlyeQl.js +1 -0
- package/dist/interface/assets/{usePluginUpdates-CiqRTrjF.js → usePluginUpdates-BZJSqnSF.js} +1 -1
- package/dist/interface/assets/usePushRegistration-CGFiyCuv.js +2 -0
- package/dist/interface/assets/{useServerRestart-Bqpn8N4T.js → useServerRestart-CjPAgzoX.js} +1 -1
- package/dist/interface/assets/{useStreamStatus-BWCnwuaj.js → useStreamStatus-BFCF1B3T.js} +1 -1
- package/dist/interface/assets/{useUpdateChannel-Cxm4QR-6.js → useUpdateChannel-F4WpbMJR.js} +1 -1
- package/dist/interface/assets/{useUserOptions-B2LNsU9C.js → useUserOptions-GbSC7_qt.js} +1 -1
- package/dist/interface/assets/{vendor-cameraui-C6beJsya.js → vendor-cameraui-0EJfDg8o.js} +56 -56
- package/dist/interface/assets/vendor-core-RnPzfwaA.js +1 -0
- package/dist/interface/assets/{vendor-primevue-DfyAPSov.js → vendor-primevue-b9CB9Uh0.js} +1 -1
- package/dist/interface/index.html +27 -27
- package/dist/interface/sw.js +1 -1
- package/dist/server/package.json +7 -7
- 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 +12 -4
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/__init__.py +16 -4
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/config.py +7 -10
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/detection.py +96 -15
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/device.py +7 -13
- 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/events.py +2 -0
- 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/cameras.controller.d.ts +2 -7
- package/dist/server/src/api/controllers/cameras.controller.js +2 -94
- package/dist/server/src/api/controllers/cameras.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.d.ts +3 -0
- package/dist/server/src/api/database/migrations/v2-1-6.js +62 -0
- package/dist/server/src/api/database/migrations/v2-1-6.js.map +1 -0
- package/dist/server/src/api/database/migrations/v2-1-7.d.ts +3 -0
- package/dist/server/src/api/database/migrations/v2-1-7.js +44 -0
- package/dist/server/src/api/database/migrations/v2-1-7.js.map +1 -0
- 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 +4 -11
- package/dist/server/src/api/routes/cameras.routes.js +5 -62
- package/dist/server/src/api/routes/cameras.routes.js.map +1 -1
- 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 +466 -154
- package/dist/server/src/api/schemas/cameras.schema.js +85 -43
- 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/services/cameras.service.d.ts +2 -4
- package/dist/server/src/api/services/cameras.service.js +2 -32
- package/dist/server/src/api/services/cameras.service.js.map +1 -1
- package/dist/server/src/api/types/index.d.ts +4 -9
- 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/automations/actions/cameraControl.js +15 -1
- package/dist/server/src/automations/actions/cameraControl.js.map +1 -1
- package/dist/server/src/automations/actions/notificationControl.js +10 -1
- package/dist/server/src/automations/actions/notificationControl.js.map +1 -1
- package/dist/server/src/camera/controller.d.ts +1 -0
- package/dist/server/src/camera/controller.js +22 -8
- 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 +6 -5
- package/dist/server/src/camera/decoder/child.js +31 -22
- package/dist/server/src/camera/decoder/child.js.map +1 -1
- package/dist/server/src/camera/decoder/detection-coordinator.d.ts +12 -10
- package/dist/server/src/camera/decoder/detection-coordinator.js +274 -163
- package/dist/server/src/camera/decoder/detection-coordinator.js.map +1 -1
- package/dist/server/src/camera/decoder/detection-pipeline.d.ts +12 -4
- package/dist/server/src/camera/decoder/detection-pipeline.js +71 -14
- package/dist/server/src/camera/decoder/detection-pipeline.js.map +1 -1
- package/dist/server/src/camera/decoder/event-manager.d.ts +2 -0
- package/dist/server/src/camera/decoder/event-manager.js +14 -0
- package/dist/server/src/camera/decoder/event-manager.js.map +1 -1
- package/dist/server/src/camera/decoder/event-thumbnailer.d.ts +4 -2
- package/dist/server/src/camera/decoder/event-thumbnailer.js +3 -3
- package/dist/server/src/camera/decoder/event-thumbnailer.js.map +1 -1
- package/dist/server/src/camera/decoder/frame-scaler.d.ts +10 -3
- package/dist/server/src/camera/decoder/frame-scaler.js +76 -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/ptz/autotracker.d.ts +4 -0
- package/dist/server/src/camera/decoder/ptz/autotracker.js +97 -18
- package/dist/server/src/camera/decoder/ptz/autotracker.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 +28 -9
- package/dist/server/src/camera/decoder/sources/buffered-source.js +114 -66
- 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 +74 -18
- package/dist/server/src/camera/decoder/types.js +5 -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 +39 -29
- package/dist/server/src/camera/decoder/worker.js.map +1 -1
- package/dist/server/src/camera/index.d.ts +3 -4
- package/dist/server/src/camera/index.js +6 -8
- package/dist/server/src/camera/index.js.map +1 -1
- package/dist/server/src/camera/privacy/mask.d.ts +27 -0
- package/dist/server/src/camera/privacy/mask.js +204 -0
- package/dist/server/src/camera/privacy/mask.js.map +1 -0
- package/dist/server/src/camera/privacy/snapshot.d.ts +13 -0
- package/dist/server/src/camera/privacy/snapshot.js +126 -0
- package/dist/server/src/camera/privacy/snapshot.js.map +1 -0
- 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/camera/utils/filter.d.ts +3 -3
- package/dist/server/src/camera/utils/filter.js.map +1 -1
- package/dist/server/src/camera/zones.d.ts +2 -0
- package/dist/server/src/camera/zones.js +11 -0
- package/dist/server/src/camera/zones.js.map +1 -0
- package/dist/server/src/manager/notificationManager.d.ts +2 -0
- package/dist/server/src/manager/notificationManager.js +21 -2
- package/dist/server/src/manager/notificationManager.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 +22 -13
- 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/plugins/schema.js +1 -1
- package/dist/server/src/plugins/schema.js.map +1 -1
- 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 +6 -5
- 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 +7 -7
- package/dist/interface/assets/Automation-DQyPNAzK.js +0 -3
- package/dist/interface/assets/AutomationImport-BDD_LuC1.js +0 -2
- package/dist/interface/assets/Camera-CYxRoESX.js +0 -2
- package/dist/interface/assets/Cameras-V_twilcC.js +0 -652
- package/dist/interface/assets/Camview-CSb2iVBf.js +0 -1
- package/dist/interface/assets/Config-BKuXgZxF.js +0 -2
- package/dist/interface/assets/ConfigActionCameraControl-TFe1_K6j.js +0 -1
- package/dist/interface/assets/ConfigActionNotificationControl-DI_t2wHh.js +0 -1
- package/dist/interface/assets/ConfigTriggerGeofence-DJt-3FpV.js +0 -1
- package/dist/interface/assets/Console-BZB3MZKj.js +0 -1
- package/dist/interface/assets/CuiBottomSheet-BnKYWBBA.js +0 -1
- package/dist/interface/assets/CuiCameraPipCard-DaJlFY1g.js +0 -1
- package/dist/interface/assets/CuiCameraRecordings-z4WblFJ9.js +0 -1
- package/dist/interface/assets/CuiCameraShares-CvKMe_kX.js +0 -1
- package/dist/interface/assets/CuiCameraTable-_7KwbPyM.js +0 -1
- package/dist/interface/assets/CuiConsole-BOfQ5I3b.js +0 -5
- package/dist/interface/assets/CuiPluginCard-CvNfBRhG.js +0 -2
- package/dist/interface/assets/CuiPluginCategoryChip-JG_8D7Wt.js +0 -1
- package/dist/interface/assets/CuiQRCode-Vd-1ssJK.js +0 -1
- package/dist/interface/assets/CuiRecordingsGrid-skKnK074.js +0 -1
- package/dist/interface/assets/CuiSchema-DvY3Bdnz.js +0 -1
- package/dist/interface/assets/FirstSteps-T4ra4jxE.js +0 -1
- package/dist/interface/assets/Instances-D_Td0oJ-.js +0 -1
- package/dist/interface/assets/Metrics-BstlZavQ.css +0 -1
- package/dist/interface/assets/Metrics-FFvFP2kR.js +0 -1
- package/dist/interface/assets/Plugin-CKcptqV9.js +0 -1
- package/dist/interface/assets/PluginDetail-Y_jg8hAX.js +0 -2
- package/dist/interface/assets/PluginSearch-D2efYsof.js +0 -1
- package/dist/interface/assets/Recordings-80Xd1V3L.css +0 -1
- package/dist/interface/assets/Recordings-BlBmyZlM.js +0 -1
- package/dist/interface/assets/Sensors-B-Qhfr5i.css +0 -1
- package/dist/interface/assets/Sensors-CHbtecpf.js +0 -2
- package/dist/interface/assets/SettingsAccount-DA67jXmp.js +0 -1
- package/dist/interface/assets/SettingsAccount-DPPwXOlM.js +0 -1
- package/dist/interface/assets/SettingsAppearance-C92Nlobb.js +0 -1
- package/dist/interface/assets/SettingsMqtt-CuTZBTrA.js +0 -1
- package/dist/interface/assets/SettingsNotifications-D7Vs6NoU.js +0 -1
- package/dist/interface/assets/SettingsPermissions-C_8g_nff.js +0 -1
- package/dist/interface/assets/SettingsRemote-DYld6-1F.css +0 -1
- package/dist/interface/assets/SettingsRemote-oDVzvhmy.js +0 -1
- package/dist/interface/assets/SettingsSystem-QfBIPq4i.js +0 -2
- package/dist/interface/assets/SettingsUsers-CqRJ8-7H.js +0 -1
- package/dist/interface/assets/Workers-tgzMqP3o.js +0 -2
- package/dist/interface/assets/api-1K2Gzz8B.js +0 -2
- package/dist/interface/assets/api-Bj4QRftG.css +0 -2
- package/dist/interface/assets/cameras.schema-CKnsFYBP.js +0 -1
- package/dist/interface/assets/cctv-BA5DvNlE.js +0 -1
- package/dist/interface/assets/cloudHandoff-qezHiAZI.js +0 -1
- package/dist/interface/assets/main-Ei2ORV9y.js +0 -3
- package/dist/interface/assets/main-kdyR9bI7.css +0 -1
- package/dist/interface/assets/mqtt-CCq5JGGk.js +0 -1
- package/dist/interface/assets/round-alt-arrow-left-bold-BxX4ufd0.js +0 -1
- package/dist/interface/assets/round-restart-alt-Bw-T1H6w.js +0 -1
- package/dist/interface/assets/round-restart-alt-CmrjVFat.css +0 -1
- package/dist/interface/assets/types-BTVMzSuj.js +0 -1
- package/dist/interface/assets/types-vaT6wuY0.js +0 -1
- package/dist/interface/assets/useAppVersion-BRc6OWZP.js +0 -1
- package/dist/interface/assets/useBackupRestore-DCO_Pw9q.js +0 -1
- package/dist/interface/assets/useElectron-CtslU_0f.js +0 -1
- package/dist/interface/assets/usePermissions-DpsfFydh.js +0 -1
- package/dist/interface/assets/usePushRegistration-Coz8RZob.js +0 -2
- package/dist/interface/assets/vendor-core-_ft7qIgH.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
|
@@ -12,7 +12,9 @@ import { isNoRespondersError, RPCClass, RPCMethod } from '@camera.ui/rpc';
|
|
|
12
12
|
import { SensorType } from '@camera.ui/sdk';
|
|
13
13
|
import { NamespaceManager } from '../../rpc/namespaces.js';
|
|
14
14
|
import { DETECTION_SENSOR_TYPES } from '../../sensors/types.js';
|
|
15
|
-
import {
|
|
15
|
+
import { PrivacyMask } from '../privacy/mask.js';
|
|
16
|
+
import { normalizePolygon } from '../utils/filter.js';
|
|
17
|
+
import { normalizeZones } from '../zones.js';
|
|
16
18
|
import { AudioDetectionLoop } from './audio-loop.js';
|
|
17
19
|
import { CascadeManager } from './cascade-manager.js';
|
|
18
20
|
import { detectionRecord } from './debug/detection-record.js';
|
|
@@ -24,12 +26,12 @@ import { FrameScaler } from './frame-scaler.js';
|
|
|
24
26
|
import { directionBetween, directionOf, MOMENT_FORMATS, MOMENT_QUALITY, momentWindow, unionBox } from './moment-crop.js';
|
|
25
27
|
import { PerfTracker } from './perf-tracker.js';
|
|
26
28
|
import { normalizePlateText } from './plate-vote.js';
|
|
27
|
-
import { isVideoInputSpec, PluginRegistry } from './plugin-registry.js';
|
|
29
|
+
import { isVideoInputSpec, modelIdentity, PluginRegistry } from './plugin-registry.js';
|
|
28
30
|
import { PtzAutotracker } from './ptz/autotracker.js';
|
|
29
|
-
import { ReconnectBackoff } from './reconnect-backoff.js';
|
|
30
31
|
import { SecondaryStage } from './secondary-stage.js';
|
|
32
|
+
import { BufferedSource } from './sources/buffered-source.js';
|
|
31
33
|
import { FrameSource } from './sources/frame-source.js';
|
|
32
|
-
import { DETECT_TIMEOUT_MS, ensureDetectionBoxes, MOTION_WIDTH_MAP } from './types.js';
|
|
34
|
+
import { DETECT_TIMEOUT_MS, DETECTOR_METRIC_TYPES, ensureDetectionBoxes, isFullFrameBox, MOTION_WIDTH_MAP } from './types.js';
|
|
33
35
|
const MOMENT_EVENTS = new Set(['objectEntered', 'objectWoke', 'objectRecovered', 'bestShotUpdated']);
|
|
34
36
|
const MOMENT_MOVING_SPEED = 0.05;
|
|
35
37
|
const MOMENT_ATTRIBUTE_MIN_AREA = 600;
|
|
@@ -44,6 +46,7 @@ const ACTIVE_TICK_MS = 100;
|
|
|
44
46
|
const MOTION_INTERVAL_MS = 200;
|
|
45
47
|
const TICK_SLACK_MS = 20;
|
|
46
48
|
const MAIN_STREAM_HOLD_MS = 5000;
|
|
49
|
+
const EXTERNAL_FRAME_MAX_AGE_MS = 1000;
|
|
47
50
|
let DetectionCoordinator = class DetectionCoordinator {
|
|
48
51
|
config;
|
|
49
52
|
proxy;
|
|
@@ -66,9 +69,10 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
66
69
|
classifierLabels = new Map();
|
|
67
70
|
feedingSensors = new Map();
|
|
68
71
|
dwell = new DwellManager();
|
|
69
|
-
|
|
72
|
+
privacy;
|
|
70
73
|
// debugging
|
|
71
74
|
perf = new PerfTracker();
|
|
75
|
+
benchmarkRunning = false;
|
|
72
76
|
hqUpgrade;
|
|
73
77
|
mainStreamActive = false;
|
|
74
78
|
idleSince = 0;
|
|
@@ -89,27 +93,32 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
89
93
|
this.config = config;
|
|
90
94
|
this.proxy = proxy;
|
|
91
95
|
this.logger = logger;
|
|
92
|
-
const
|
|
93
|
-
streamUrl: config.streamUrl,
|
|
94
|
-
snapshotUrl: config.snapshotUrl,
|
|
95
|
-
decoder: this.config.frameWorkerSettings.decoder,
|
|
96
|
-
};
|
|
96
|
+
const snapshot = { snapshotUrl: config.snapshotUrl };
|
|
97
97
|
if (config.controllerSnapshotSourceId) {
|
|
98
98
|
const sourceId = config.controllerSnapshotSourceId;
|
|
99
99
|
const controllerProxy = this.proxy.createProxy(NamespaceManager.cameraNamespaces(config.cameraId).cameraControllerRpc);
|
|
100
|
-
|
|
100
|
+
snapshot.snapshotProvider = async () => {
|
|
101
101
|
const jpeg = await controllerProxy.snapshot(sourceId, true, true);
|
|
102
102
|
if (!jpeg || jpeg.byteLength === 0)
|
|
103
103
|
return null;
|
|
104
104
|
return Buffer.from(jpeg);
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
-
this.
|
|
108
|
-
this.
|
|
109
|
-
this.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
this.config.zones = normalizeZones(config.zones);
|
|
108
|
+
this.privacy = new PrivacyMask(logger);
|
|
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();
|
|
117
|
+
this.frameScaler = new FrameScaler(null, logger, this.privacy);
|
|
118
|
+
this.pipeline = new DetectionPipeline(this.pipelineZones(), config.detectionSettings);
|
|
119
|
+
this.secondaries = new SecondaryStage(this, this.plugins, this.pipeline, this.frameScaler, this.proxy, this.perf, logger);
|
|
120
|
+
if (config.zones.lines.length > 0) {
|
|
121
|
+
this.pipeline.updateLines(config.zones.lines, this.videoAspectRatio);
|
|
113
122
|
}
|
|
114
123
|
this.eventManager = new DetectionEventManager(config.cameraId, this.proxy, this.logger);
|
|
115
124
|
this.eventManager.updateNvrRpc(config.nvrRpc);
|
|
@@ -122,6 +131,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
122
131
|
this.thumbnailer = new EventThumbnailer({
|
|
123
132
|
frameSource: this.frameSource,
|
|
124
133
|
frameScaler: this.frameScaler,
|
|
134
|
+
privacy: this.privacy,
|
|
125
135
|
eventManager: this.eventManager,
|
|
126
136
|
logger,
|
|
127
137
|
decoder: this.config.frameWorkerSettings.decoder,
|
|
@@ -194,18 +204,93 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
194
204
|
return this.config.detectionSettings;
|
|
195
205
|
}
|
|
196
206
|
getPerfSnapshot() {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
+
};
|
|
202
229
|
}
|
|
203
|
-
|
|
204
|
-
this.
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
this.
|
|
288
|
+
updateZoneConfig(zones) {
|
|
289
|
+
zones = normalizeZones(zones);
|
|
290
|
+
this.config.zones = zones;
|
|
291
|
+
this.pipeline.updateZones(this.pipelineZones());
|
|
292
|
+
this.privacy.update(zones);
|
|
293
|
+
this.pipeline.updateLines(zones.lines, this.videoAspectRatio);
|
|
209
294
|
}
|
|
210
295
|
updateDetectionSettings(settings) {
|
|
211
296
|
this.config.detectionSettings = settings;
|
|
@@ -225,12 +310,13 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
225
310
|
}
|
|
226
311
|
updateInterfaceSettings(settings) {
|
|
227
312
|
this.config.interfaceSettings = settings;
|
|
228
|
-
if (this.config.lines.length > 0) {
|
|
229
|
-
this.pipeline.updateLines(this.config.lines, this.videoAspectRatio);
|
|
313
|
+
if (this.config.zones.lines.length > 0) {
|
|
314
|
+
this.pipeline.updateLines(this.config.zones.lines, this.videoAspectRatio);
|
|
230
315
|
}
|
|
231
316
|
}
|
|
232
317
|
async dispose() {
|
|
233
318
|
await this.stopVideoLoop();
|
|
319
|
+
await this.frameSource.stop();
|
|
234
320
|
await this.audioLoop.stop();
|
|
235
321
|
await this.thumbnailer.stop();
|
|
236
322
|
this.ptzAutotracker.dispose();
|
|
@@ -290,6 +376,12 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
290
376
|
// the registry only announces the plugin assigned for this type, everyone else reports into the void
|
|
291
377
|
if (!this.feedingSensors.has(sensorId))
|
|
292
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
|
+
}
|
|
293
385
|
if (sensorType === SensorType.Object) {
|
|
294
386
|
const filtered = this.applyExternalDetectionFilters(sensorType, properties);
|
|
295
387
|
// presence feed keeps the autotracker's return-home timer alive even while
|
|
@@ -319,39 +411,19 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
319
411
|
this.processingExternalSecondary = true;
|
|
320
412
|
try {
|
|
321
413
|
const rawExternal = filtered.detections ?? [];
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
try {
|
|
325
|
-
const results = { timestamp: Date.now() };
|
|
326
|
-
const objects = await this.runObjectAssist(peeked.frame, rawExternal);
|
|
327
|
-
this.ingestAssistedObjects(sensorId, objects);
|
|
328
|
-
// processExternal only adapts the shape, external boxes get no real tracking
|
|
329
|
-
const objectDetections = this.pipeline.processExternal(objects.detections);
|
|
330
|
-
await this.runSecondariesAndThumbnails({ frame: peeked.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
|
|
331
|
-
this.ingestResultsForAllSecondaries(results);
|
|
332
|
-
const snapshot = this.buildSnapshot();
|
|
333
|
-
if (results.thumbnails && results.thumbnails.length > 0) {
|
|
334
|
-
snapshot.thumbnails = results.thumbnails;
|
|
335
|
-
}
|
|
336
|
-
this.eventManager.processResults(snapshot);
|
|
337
|
-
}
|
|
338
|
-
finally {
|
|
339
|
-
await peeked[Symbol.asyncDispose]();
|
|
340
|
-
}
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
const fetched = await this.frameSource.fetchSnapshotFrame();
|
|
344
|
-
if (!fetched)
|
|
414
|
+
const handle = await this.frameSource.getFrame(EXTERNAL_FRAME_MAX_AGE_MS);
|
|
415
|
+
if (!handle)
|
|
345
416
|
return;
|
|
346
417
|
try {
|
|
347
418
|
const results = { timestamp: Date.now() };
|
|
348
|
-
const objects = await this.runObjectAssist(
|
|
419
|
+
const objects = await this.runObjectAssist(handle.frame, rawExternal);
|
|
349
420
|
this.ingestAssistedObjects(sensorId, objects);
|
|
350
421
|
if (objects.assisted) {
|
|
351
422
|
// real boxes: crop the object like the frame pipeline, so face and
|
|
352
423
|
// plate secondaries run on the subject instead of the whole scene
|
|
424
|
+
// (processExternal only adapts the shape, external boxes get no real tracking)
|
|
353
425
|
const objectDetections = this.pipeline.processExternal(objects.detections);
|
|
354
|
-
await this.runSecondariesAndThumbnails({ frame:
|
|
426
|
+
await this.runSecondariesAndThumbnails({ frame: handle.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
|
|
355
427
|
this.ingestResultsForAllSecondaries(results);
|
|
356
428
|
const snapshot = this.buildSnapshot();
|
|
357
429
|
if (results.thumbnails && results.thumbnails.length > 0) {
|
|
@@ -360,9 +432,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
360
432
|
this.eventManager.processResults(snapshot);
|
|
361
433
|
}
|
|
362
434
|
else {
|
|
363
|
-
const sceneJpeg = await this.secondaries.detectFullFrame(
|
|
435
|
+
const sceneJpeg = await this.secondaries.detectFullFrame(handle.frame, results);
|
|
364
436
|
try {
|
|
365
|
-
results.thumbnails = await this.secondaries.generateThumbnails(
|
|
437
|
+
results.thumbnails = await this.secondaries.generateThumbnails(handle.frame, this.frameScaler, results);
|
|
366
438
|
}
|
|
367
439
|
catch (error) {
|
|
368
440
|
this.logger.error('Thumbnail generation error:', error);
|
|
@@ -382,7 +454,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
382
454
|
}
|
|
383
455
|
}
|
|
384
456
|
finally {
|
|
385
|
-
await
|
|
457
|
+
await handle[Symbol.asyncDispose]();
|
|
386
458
|
}
|
|
387
459
|
}
|
|
388
460
|
finally {
|
|
@@ -543,7 +615,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
543
615
|
faceEmbeddingModel: cs.faceEmbeddingModel,
|
|
544
616
|
plates: cs.licensePlate?.detections ?? [],
|
|
545
617
|
plateVoting: this.plugins.get(SensorType.LicensePlate)?.requiresFrames === true,
|
|
546
|
-
plateMinConfidence: this.config.detectionSettings.licensePlate?.
|
|
618
|
+
plateMinConfidence: this.config.detectionSettings.licensePlate?.ocrConfidence,
|
|
547
619
|
plateMinLength: this.config.detectionSettings.licensePlate?.minLength,
|
|
548
620
|
faceMinConfidence: this.config.detectionSettings.face?.confidence,
|
|
549
621
|
classifiers: cs.classifiers ? Object.values(cs.classifiers).flatMap((c) => c.detections) : [],
|
|
@@ -557,57 +629,69 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
557
629
|
detectionZones: this.getNormalizedDetectionZones(),
|
|
558
630
|
};
|
|
559
631
|
}
|
|
632
|
+
pipelineZones() {
|
|
633
|
+
return { motion: this.config.zones.motion, object: this.config.zones.object, privacy: this.config.zones.privacy };
|
|
634
|
+
}
|
|
560
635
|
getNormalizedDetectionZones() {
|
|
561
636
|
const result = [];
|
|
562
|
-
for (const zone of this.config.zones
|
|
563
|
-
if (zone.isPrivacyMask)
|
|
564
|
-
continue;
|
|
637
|
+
for (const zone of this.config.zones.object) {
|
|
565
638
|
if (!zone.name)
|
|
566
639
|
continue;
|
|
567
|
-
result.push({ name: zone.name, points:
|
|
640
|
+
result.push({ name: zone.name, points: normalizePolygon(zone.points) });
|
|
568
641
|
}
|
|
569
|
-
for (const zone of this.config.
|
|
642
|
+
for (const zone of this.config.zones.alert) {
|
|
570
643
|
if (!zone.name)
|
|
571
644
|
continue;
|
|
572
|
-
result.push({
|
|
645
|
+
result.push({
|
|
646
|
+
name: zone.name,
|
|
647
|
+
points: normalizePolygon(zone.points),
|
|
648
|
+
match: zone.match ?? 'contain',
|
|
649
|
+
alertLabels: zone.labels.map((label) => label.toLowerCase()),
|
|
650
|
+
});
|
|
573
651
|
}
|
|
574
652
|
return result;
|
|
575
653
|
}
|
|
576
|
-
filterAllowedObjectLabels(detections) {
|
|
577
|
-
const allowed = this.config.detectionSettings.object.labels;
|
|
578
|
-
if (!allowed || allowed.length === 0 || detections.length === 0)
|
|
579
|
-
return detections;
|
|
580
|
-
const set = new Set(allowed.map((label) => label.toLowerCase()));
|
|
581
|
-
return detections.filter((detection) => set.has(detection.label.toLowerCase()));
|
|
582
|
-
}
|
|
583
654
|
applyExternalDetectionFilters(sensorType, properties) {
|
|
584
655
|
const raw = properties.detections;
|
|
585
656
|
if (!Array.isArray(raw) || raw.length === 0)
|
|
586
657
|
return properties;
|
|
587
658
|
// the zone filter and the rust merge assume a box on every detection
|
|
588
659
|
const detections = ensureDetectionBoxes(raw);
|
|
660
|
+
// a camera that reports a label without coordinates gives no position, a
|
|
661
|
+
// zone cannot judge it and the object assist has not run yet
|
|
662
|
+
const positioned = detections.filter((detection) => !isFullFrameBox(detection.box));
|
|
663
|
+
const boxless = detections.filter((detection) => isFullFrameBox(detection.box));
|
|
589
664
|
let filtered;
|
|
665
|
+
let boxlessLabel;
|
|
590
666
|
switch (sensorType) {
|
|
591
667
|
case SensorType.Face:
|
|
592
|
-
|
|
668
|
+
boxlessLabel = 'face';
|
|
669
|
+
filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'face');
|
|
593
670
|
break;
|
|
594
671
|
case SensorType.LicensePlate:
|
|
595
|
-
|
|
672
|
+
boxlessLabel = 'license_plate';
|
|
673
|
+
filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'license_plate');
|
|
596
674
|
break;
|
|
597
675
|
case SensorType.Object:
|
|
598
|
-
filtered = this.pipeline.runZoneFilter(
|
|
676
|
+
filtered = this.pipeline.runZoneFilter(positioned);
|
|
599
677
|
break;
|
|
600
678
|
case SensorType.Classifier:
|
|
601
679
|
case SensorType.Motion:
|
|
602
|
-
filtered = this.pipeline.runZoneFilter(
|
|
680
|
+
filtered = this.pipeline.runZoneFilter(positioned);
|
|
603
681
|
break;
|
|
604
682
|
default:
|
|
605
683
|
return { ...properties, detections }; // audio etc, no zone filter
|
|
606
684
|
}
|
|
685
|
+
const survivors = new Set(filtered);
|
|
686
|
+
for (const detection of boxless) {
|
|
687
|
+
if (this.pipeline.objectLabelAllowed(boxlessLabel ?? detection.label))
|
|
688
|
+
survivors.add(detection);
|
|
689
|
+
}
|
|
690
|
+
const kept = detections.filter((detection) => survivors.has(detection));
|
|
607
691
|
return {
|
|
608
692
|
...properties,
|
|
609
|
-
detections:
|
|
610
|
-
detected:
|
|
693
|
+
detections: kept,
|
|
694
|
+
detected: kept.length > 0,
|
|
611
695
|
};
|
|
612
696
|
}
|
|
613
697
|
applyAudioConfidence(properties) {
|
|
@@ -740,6 +824,18 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
740
824
|
break;
|
|
741
825
|
}
|
|
742
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
|
+
}
|
|
743
839
|
async registerDetectionPluginInternal(sensor) {
|
|
744
840
|
if (this.videoStopPromise)
|
|
745
841
|
await this.videoStopPromise;
|
|
@@ -747,7 +843,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
747
843
|
const wasVideoNeeded = this.plugins.shouldVideoBeActive();
|
|
748
844
|
const wasAudioNeeded = this.plugins.shouldAudioBeActive();
|
|
749
845
|
const namespaces = NamespaceManager.sensorProviderNamespaces(sensor.pluginId, sensor.sensorId);
|
|
750
|
-
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
|
+
});
|
|
751
849
|
const registered = this.plugins.register({
|
|
752
850
|
pluginId: sensor.pluginId,
|
|
753
851
|
sensorId: sensor.sensorId,
|
|
@@ -758,7 +856,8 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
758
856
|
});
|
|
759
857
|
if (!registered) {
|
|
760
858
|
// re-push from the registry: the sensor is already live, only the spec may have changed
|
|
761
|
-
|
|
859
|
+
if (sensor.modelSpec)
|
|
860
|
+
this.applyModelSpec(sensor.sensorId, sensor.sensorType, sensor.modelSpec);
|
|
762
861
|
return;
|
|
763
862
|
}
|
|
764
863
|
this.logger.trace(`Plugin registered: ${sensor.pluginId} for ${sensor.sensorType}`);
|
|
@@ -839,7 +938,6 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
839
938
|
return;
|
|
840
939
|
this.logger.debug('Starting video detection loop');
|
|
841
940
|
this.loopRunning = true;
|
|
842
|
-
this.videoBackoff.reset();
|
|
843
941
|
this.loopPromise = this.runDetectionLoop();
|
|
844
942
|
}
|
|
845
943
|
async stopVideoLoop() {
|
|
@@ -854,7 +952,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
854
952
|
this.mainStreamActive = false;
|
|
855
953
|
this.idleSince = 0;
|
|
856
954
|
const doStop = async () => {
|
|
857
|
-
await this.frameSource.
|
|
955
|
+
await this.frameSource.detach();
|
|
858
956
|
await this.loopPromise;
|
|
859
957
|
this.loopPromise = undefined;
|
|
860
958
|
};
|
|
@@ -881,81 +979,87 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
881
979
|
}
|
|
882
980
|
}
|
|
883
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;
|
|
884
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
|
+
}
|
|
885
1005
|
try {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
// would reset the background model on every transition
|
|
903
|
-
const analysis = await this.acquireAnalysisFrame(snap.frame);
|
|
904
|
-
// debugging
|
|
905
|
-
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 {
|
|
906
1022
|
try {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
try {
|
|
911
|
-
if (this.hqUpgrade) {
|
|
912
|
-
this.hqUpgrade.frame[Symbol.dispose]?.();
|
|
913
|
-
this.hqUpgrade = undefined;
|
|
914
|
-
}
|
|
915
|
-
if (analysis.frame !== snap.frame)
|
|
916
|
-
analysis.frame[Symbol.dispose]?.();
|
|
917
|
-
snap.frame[Symbol.dispose]?.();
|
|
918
|
-
}
|
|
919
|
-
catch {
|
|
920
|
-
// best-effort
|
|
1023
|
+
if (this.hqUpgrade) {
|
|
1024
|
+
this.hqUpgrade.frame[Symbol.dispose]?.();
|
|
1025
|
+
this.hqUpgrade = undefined;
|
|
921
1026
|
}
|
|
1027
|
+
if (analysis.frame !== snap.frame)
|
|
1028
|
+
analysis.frame[Symbol.dispose]?.();
|
|
1029
|
+
}
|
|
1030
|
+
catch {
|
|
1031
|
+
// ignore
|
|
922
1032
|
}
|
|
923
|
-
this.updateStreamState();
|
|
924
|
-
// activity, not stream choice: cameras without main-stream analysis still count as active
|
|
925
|
-
if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment())
|
|
926
|
-
this.perf.activeTicks++;
|
|
927
|
-
else
|
|
928
|
-
this.perf.idleTicks++;
|
|
929
|
-
this.perf.report(this.logger);
|
|
930
|
-
const remaining = (this.mainStreamActive ? ACTIVE_TICK_MS : IDLE_TICK_MS) - (Date.now() - tickStart);
|
|
931
|
-
if (remaining > 0)
|
|
932
|
-
await sleep(remaining);
|
|
933
|
-
this.perf.loopMs += Date.now() - tickStart;
|
|
934
|
-
}
|
|
935
|
-
const streamError = this.frameSource.lastError;
|
|
936
|
-
await this.frameSource.stop();
|
|
937
|
-
this.frameScaler.clearCache();
|
|
938
|
-
if (this.loopRunning && streamError) {
|
|
939
|
-
const delay = this.videoBackoff.nextDelayMs();
|
|
940
|
-
this.logger.warn(`Stream ended with read error, reconnecting in ${delay / 1000}s: ${streamError.message}`);
|
|
941
|
-
await sleep(delay);
|
|
942
|
-
}
|
|
943
|
-
else if (this.loopRunning && frameCount === 0) {
|
|
944
|
-
this.logger.debug('Stream ended without frames, waiting before reconnect...');
|
|
945
|
-
await sleep(this.videoBackoff.idleDelayMs);
|
|
946
1033
|
}
|
|
1034
|
+
this.updateStreamState();
|
|
947
1035
|
}
|
|
948
1036
|
catch (error) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
await this.frameSource.stop();
|
|
952
|
-
this.frameScaler.clearCache();
|
|
953
|
-
const delay = this.videoBackoff.nextDelayMs();
|
|
954
|
-
this.logger.warn(`Stream error, reconnecting in ${delay / 1000}s: ${error.message}`);
|
|
955
|
-
await sleep(delay);
|
|
1037
|
+
// the stream keeps running, a broken tick must not end the loop
|
|
1038
|
+
this.logger.error('Detection tick failed:', error);
|
|
956
1039
|
}
|
|
957
|
-
|
|
958
|
-
|
|
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();
|
|
959
1063
|
this.frameScaler.clearCache();
|
|
960
1064
|
this.logger.debug('Detection loop ended');
|
|
961
1065
|
}
|
|
@@ -974,7 +1078,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
974
1078
|
const main = await this.thumbnailer.acquireHqFrame(0);
|
|
975
1079
|
if (main) {
|
|
976
1080
|
this.perf.mainFrames++;
|
|
977
|
-
this.perf.
|
|
1081
|
+
this.perf.mainDecodeMs += Date.now() - t0;
|
|
978
1082
|
return { frame: main.frame, scaler: main.scaler, isMainStream: true };
|
|
979
1083
|
}
|
|
980
1084
|
}
|
|
@@ -992,7 +1096,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
992
1096
|
const main = await this.thumbnailer.acquireHqFrame(0);
|
|
993
1097
|
if (main) {
|
|
994
1098
|
this.perf.mainFrames++;
|
|
995
|
-
this.perf.
|
|
1099
|
+
this.perf.mainDecodeMs += Date.now() - t0;
|
|
996
1100
|
return { frame: main.frame, scaler: main.scaler, isMainStream: true };
|
|
997
1101
|
}
|
|
998
1102
|
}
|
|
@@ -1059,8 +1163,8 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1059
1163
|
try {
|
|
1060
1164
|
const motionInferStart = Date.now();
|
|
1061
1165
|
const result = await motionPlugin.proxy.detectMotion(motionFrame);
|
|
1062
|
-
this.perf.
|
|
1063
|
-
this.perf.
|
|
1166
|
+
this.perf.motionMs += Date.now() - motionInferStart;
|
|
1167
|
+
this.perf.motionCount++;
|
|
1064
1168
|
if (!this.loopRunning)
|
|
1065
1169
|
return;
|
|
1066
1170
|
if (ptzSuppressed) {
|
|
@@ -1111,16 +1215,18 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1111
1215
|
try {
|
|
1112
1216
|
const inferStart = Date.now();
|
|
1113
1217
|
const result = await PromiseTimeout(objectPlugin.proxy.detectObjects(objectFrame.model), DETECT_TIMEOUT_MS, undefined, `Object detection timed out after ${DETECT_TIMEOUT_MS}ms`);
|
|
1114
|
-
this.perf.
|
|
1115
|
-
this.perf.
|
|
1218
|
+
this.perf.objectMs += Date.now() - inferStart;
|
|
1219
|
+
this.perf.objectCount++;
|
|
1116
1220
|
if (!this.loopRunning)
|
|
1117
1221
|
return;
|
|
1118
1222
|
this.trackDetectionCadence();
|
|
1119
1223
|
// run the pipeline even on empty frames so Norfair advances its
|
|
1120
1224
|
// Kalman state; the pose delta keeps predictions stable across pans
|
|
1121
1225
|
const poseDelta = this.ptzAutotracker.consumePoseDelta();
|
|
1122
|
-
const
|
|
1226
|
+
const postStart = Date.now();
|
|
1227
|
+
const detected = FrameScaler.undoLetterbox(ensureDetectionBoxes(result.detections), objectFrame.geometry);
|
|
1123
1228
|
const pipelineResult = this.pipeline.process(detected, poseDelta);
|
|
1229
|
+
this.perf.postMs += Date.now() - postStart;
|
|
1124
1230
|
// the tick that opens a span analysed the low stream (the switch lands
|
|
1125
1231
|
// next tick), its picture work is worth a one-off HQ decode
|
|
1126
1232
|
if (this.updateWorldSpans(pipelineResult) && !analysis.isMainStream) {
|
|
@@ -1244,6 +1350,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1244
1350
|
if (objectDetections.length === 0)
|
|
1245
1351
|
return;
|
|
1246
1352
|
this.perf.objects += objectDetections.length;
|
|
1353
|
+
this.perf.framesWithObjects++;
|
|
1247
1354
|
const secondaryStart = Date.now();
|
|
1248
1355
|
await this.secondaries.detect(analysis.frame, analysis.scaler, objectDetections, results);
|
|
1249
1356
|
this.perf.secondaryMs += Date.now() - secondaryStart;
|
|
@@ -1265,15 +1372,18 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1265
1372
|
}
|
|
1266
1373
|
const facePlugin = this.plugins.get(SensorType.Face);
|
|
1267
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');
|
|
1268
1378
|
// no mid-segment clear: a turned-away head yields a no-face frame and
|
|
1269
1379
|
// would flap the list, departure is reflected when the segment ends
|
|
1270
|
-
for (const detection of
|
|
1380
|
+
for (const detection of detections) {
|
|
1271
1381
|
if (detection.identity)
|
|
1272
1382
|
this.faceIdentities.add(detection.identity);
|
|
1273
1383
|
}
|
|
1274
1384
|
this.ingestDetectionResult(SensorType.Face, facePlugin.sensorId, {
|
|
1275
|
-
detected: results.face.detected,
|
|
1276
|
-
detections
|
|
1385
|
+
detected: detections.length > 0 && results.face.detected,
|
|
1386
|
+
detections,
|
|
1277
1387
|
identities: [...this.faceIdentities].sort(),
|
|
1278
1388
|
});
|
|
1279
1389
|
}
|
|
@@ -1281,14 +1391,15 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1281
1391
|
if (results.licensePlate) {
|
|
1282
1392
|
const lpdPlugin = this.plugins.get(SensorType.LicensePlate);
|
|
1283
1393
|
if (lpdPlugin) {
|
|
1284
|
-
|
|
1394
|
+
const detections = this.pipeline.runZoneFilterWithLabel(results.licensePlate.detections, 'license_plate');
|
|
1395
|
+
for (const detection of detections) {
|
|
1285
1396
|
const plate = detection.plateText ? normalizePlateText(detection.plateText) || detection.plateText : '';
|
|
1286
1397
|
if (plate)
|
|
1287
1398
|
this.platesSeen.add(plate);
|
|
1288
1399
|
}
|
|
1289
1400
|
this.ingestDetectionResult(SensorType.LicensePlate, lpdPlugin.sensorId, {
|
|
1290
|
-
detected: results.licensePlate.detected,
|
|
1291
|
-
detections
|
|
1401
|
+
detected: detections.length > 0 && results.licensePlate.detected,
|
|
1402
|
+
detections,
|
|
1292
1403
|
plates: [...this.platesSeen].sort(),
|
|
1293
1404
|
});
|
|
1294
1405
|
}
|
|
@@ -1578,10 +1689,10 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1578
1689
|
try {
|
|
1579
1690
|
const inferStart = Date.now();
|
|
1580
1691
|
const result = await PromiseTimeout(assist.proxy.detectObjects(scaled.model), DETECT_TIMEOUT_MS, undefined, `Object assist timed out after ${DETECT_TIMEOUT_MS}ms`);
|
|
1581
|
-
this.perf.
|
|
1582
|
-
this.perf.
|
|
1692
|
+
this.perf.assistMs += Date.now() - inferStart;
|
|
1693
|
+
this.perf.assistCount++;
|
|
1583
1694
|
const reportedLabels = new Set(reported.map((d) => d.label.toLowerCase()));
|
|
1584
|
-
const boxed =
|
|
1695
|
+
const boxed = FrameScaler.undoLetterbox(ensureDetectionBoxes(result?.detections ?? []), scaled.geometry);
|
|
1585
1696
|
const found = boxed.filter((d) => reportedLabels.size === 0 || reportedLabels.has(d.label.toLowerCase()));
|
|
1586
1697
|
if (found.length === 0)
|
|
1587
1698
|
return { detections: reported, assisted: false };
|