Compare commits

...
Author SHA1 Message Date
dependabot[bot]andGitHub 0592a8c2c0 Bump protobuf from 3.20.3 to 7.35.1 in /docker/tensorrt
Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 3.20.3 to 7.35.1.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Commits](https://github.com/protocolbuffers/protobuf/commits)

---
updated-dependencies:
- dependency-name: protobuf
  dependency-version: 7.35.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-12 11:32:58 +00:00
Josh HawkinsandGitHub e6601d50a6 Add recording keyframe analysis to camera probe dialog (#23453)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
* backend: endpoint and util funcs

* tests

* frontend and i18n

* update openapi spec

* add tip to docs
2026-06-11 14:16:41 -06:00
Josh HawkinsandGitHub efe585a920 Miscellaneous fixes (#23445)
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / AMD64 Build (push) Waiting to run
* keep global camera config subscribers broad when only one camera exists at startup

* update glossary
2026-06-11 05:36:30 -06:00
Josh HawkinsandGitHub f3a352ef3f Miscellaneous fixes (#23413)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
* update e2e mock data to remove deprecated fields

* remove scream audio label

scream was never mapped to anything in frigate's custom labelmap, yell is used everywhere

* document common audio labels

* deprecate ffmpeg 5

* language tweak

* add field message to recommend presets instead of manual hwaccel args

* add guidance to docs on choosing a detect fps
2026-06-08 09:14:16 -06:00
ad968efd3e Added translation using Weblate (Zuni)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Added translation using Weblate (Zuni)

Co-authored-by: Firas <firas.amm@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2026-06-06 22:25:39 -05:00
3fe91e20d0 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (1171 of 1171 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (53 of 53 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: OverTheHillsAndFarAway <prosjektx@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/nb_NO/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
bd52a1cc48 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1272 of 1272 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1268 of 1268 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 94.6% (1196 of 1263 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1195 of 1195 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1186 of 1186 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1183 of 1183 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1181 of 1181 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1176 of 1176 strings)

Co-authored-by: GuoQing Liu <842607283@qq.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/zh_Hans/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
cb40343be7 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 99.2% (803 of 809 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 99.5% (237 of 238 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (26 of 26 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KelvinKueh <kelvin.kueh@gmail.com>
Co-authored-by: Yu Chun Huang <yujun@bo2.tw>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/zh_Hant/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-player
2026-06-06 22:25:39 -05:00
b912a62e0b Translated using Weblate (Uzbek)
Currently translated at 0.3% (2 of 501 strings)

Co-authored-by: Hamza Foziljonov <hamza.uztranslator@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/uz/
Translation: Frigate NVR/audio
2026-06-06 22:25:39 -05:00
fcfab8ef14 Translated using Weblate (Khmer (Central))
Currently translated at 0.9% (5 of 501 strings)

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Added translation using Weblate (Khmer (Central))

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: reanyouda <mr.reanyouda@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/km/
Translation: Frigate NVR/audio
2026-06-06 22:25:39 -05:00
e0d0b2a345 Translated using Weblate (Persian)
Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Persian)

Currently translated at 45.4% (368 of 809 strings)

Translated using Weblate (Persian)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Persian)

Currently translated at 50.0% (639 of 1276 strings)

Translated using Weblate (Persian)

Currently translated at 100.0% (49 of 49 strings)

Translated using Weblate (Persian)

Currently translated at 98.7% (236 of 239 strings)

Translated using Weblate (Persian)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Persian)

Currently translated at 17.4% (15 of 86 strings)

Co-authored-by: Amir reza Irani ali poor <amir1376irani@yahoo.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/fa/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/fa/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/fa/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/fa/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/fa/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/fa/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/fa/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/fa/
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
b72f5a986e Translated using Weblate (Swedish)
Currently translated at 92.0% (46 of 50 strings)

Translated using Weblate (Swedish)

Currently translated at 94.0% (94 of 100 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (74 of 74 strings)

Translated using Weblate (Swedish)

Currently translated at 50.7% (647 of 1276 strings)

Translated using Weblate (Swedish)

Currently translated at 54.4% (55 of 101 strings)

Translated using Weblate (Swedish)

Currently translated at 77.7% (136 of 175 strings)

Translated using Weblate (Swedish)

Currently translated at 54.4% (55 of 101 strings)

Translated using Weblate (Swedish)

Currently translated at 50.7% (647 of 1276 strings)

Translated using Weblate (Swedish)

Currently translated at 90.0% (54 of 60 strings)

Translated using Weblate (Swedish)

Currently translated at 93.0% (120 of 129 strings)

Translated using Weblate (Swedish)

Currently translated at 92.8% (222 of 239 strings)

Translated using Weblate (Swedish)

Currently translated at 94.4% (137 of 145 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (74 of 74 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (49 of 49 strings)

Translated using Weblate (Swedish)

Currently translated at 94.0% (94 of 100 strings)

Translated using Weblate (Swedish)

Currently translated at 90.0% (54 of 60 strings)

Translated using Weblate (Swedish)

Currently translated at 77.7% (136 of 175 strings)

Translated using Weblate (Swedish)

Currently translated at 50.7% (647 of 1276 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (74 of 74 strings)

Translated using Weblate (Swedish)

Currently translated at 94.4% (137 of 145 strings)

Translated using Weblate (Swedish)

Currently translated at 93.0% (120 of 129 strings)

Translated using Weblate (Swedish)

Currently translated at 92.8% (222 of 239 strings)

Translated using Weblate (Swedish)

Currently translated at 91.2% (218 of 239 strings)

Co-authored-by: Douglas Stier <douglas.stier@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mona Lisa <monalisa@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/sv/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-filter
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-06 22:25:39 -05:00
63b1506dd6 Translated using Weblate (French)
Currently translated at 5.4% (44 of 809 strings)

Translated using Weblate (French)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (French)

Currently translated at 67.0% (850 of 1268 strings)

Translated using Weblate (French)

Currently translated at 85.1% (86 of 101 strings)

Translated using Weblate (French)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (French)

Currently translated at 82.1% (83 of 101 strings)

Co-authored-by: Gloup <emeric.denis@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: LeBuzzy <bwinster2@outlook.com>
Co-authored-by: Lorent Felix <comloren@gmail.com>
Co-authored-by: Thomas <arpelboxes@yahoo.fr>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/fr/
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
90a18852ef Translated using Weblate (Spanish)
Currently translated at 100.0% (1276 of 1276 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1272 of 1272 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (101 of 101 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (10 of 10 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1268 of 1268 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Spanish)

Currently translated at 99.2% (1253 of 1263 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1186 of 1186 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1183 of 1183 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1181 of 1181 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1176 of 1176 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Saninn Salas Diaz <saninnsalas@gmail.com>
Co-authored-by: ThatStella7922 <stella@thatstel.la>
Co-authored-by: jjavin <javiernovoa@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-configeditor/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/es/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-configeditor
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
b1c133bfd1 Translated using Weblate (Dutch)
Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Dutch)

Currently translated at 77.2% (78 of 101 strings)

Translated using Weblate (Dutch)

Currently translated at 97.0% (232 of 239 strings)

Translated using Weblate (Dutch)

Currently translated at 83.9% (397 of 473 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Dutch)

Currently translated at 97.9% (794 of 811 strings)

Translated using Weblate (Dutch)

Currently translated at 93.7% (224 of 239 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Dutch)

Currently translated at 92.8% (221 of 238 strings)

Translated using Weblate (Dutch)

Currently translated at 98.0% (1148 of 1171 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hosted Weblate user 151476 <marijndekker3@gmail.com>
Co-authored-by: Hosted Weblate user 151476 <micel@users.noreply.hosted.weblate.org>
Co-authored-by: bb61523 <brambini@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/nl/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
68e74c32e3 Translated using Weblate (Indonesian)
Currently translated at 100.0% (1276 of 1276 strings)

Translated using Weblate (Indonesian)

Currently translated at 5.0% (24 of 473 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Indonesian)

Currently translated at 1.8% (15 of 811 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (64 of 64 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (59 of 59 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (45 of 45 strings)

Translated using Weblate (Indonesian)

Currently translated at 86.6% (52 of 60 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (175 of 175 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (49 of 49 strings)

Translated using Weblate (Indonesian)

Currently translated at 59.3% (38 of 64 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (1176 of 1176 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Indonesian)

Currently translated at 30.7% (39 of 127 strings)

Co-authored-by: Arif Budiman <arifpedia@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Joseph K <o.joseph.k@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-replay/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/id/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-replay
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-06 22:25:39 -05:00
9ced2c25ee Translated using Weblate (Arabic)
Currently translated at 28.3% (142 of 501 strings)

Translated using Weblate (Arabic)

Currently translated at 18.8% (24 of 127 strings)

Co-authored-by: Firas <firas.amm@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/ar/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/ar/
Translation: Frigate NVR/audio
Translation: Frigate NVR/objects
2026-06-06 22:25:39 -05:00
b76457e0af Translated using Weblate (Italian)
Currently translated at 55.3% (448 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 55.2% (447 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 75.3% (358 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 74.7% (355 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 54.8% (444 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 74.7% (355 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 54.8% (444 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 74.7% (355 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 54.8% (444 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 74.7% (355 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 54.8% (444 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 54.8% (444 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 74.7% (355 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 54.8% (444 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 74.7% (355 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 74.7% (355 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 54.8% (444 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 50.7% (241 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 41.7% (338 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (1276 of 1276 strings)

Translated using Weblate (Italian)

Currently translated at 34.1% (276 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 37.0% (176 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (64 of 64 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (1272 of 1272 strings)

Translated using Weblate (Italian)

Currently translated at 26.5% (126 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 28.4% (230 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 94.6% (1204 of 1272 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Italian)

Currently translated at 26.4% (125 of 473 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (1195 of 1195 strings)

Translated using Weblate (Italian)

Currently translated at 28.3% (230 of 811 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Italian)

Currently translated at 26.2% (124 of 473 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Italian)

Currently translated at 28.2% (229 of 811 strings)

Translated using Weblate (Italian)

Currently translated at 28.1% (228 of 811 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (1183 of 1183 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Italian)

Currently translated at 26.0% (123 of 473 strings)

Co-authored-by: Filippo-riccardo Franzin (filippo franzin) <filric01@gmail.com>
Co-authored-by: Frank_ai <cyberpez.ai@gmail.com>
Co-authored-by: Gringo <ita.translations@tiscali.it>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/it/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
4626d91fbb Translated using Weblate (Polish)
Currently translated at 24.0% (114 of 475 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Polish)

Currently translated at 97.0% (232 of 239 strings)

Translated using Weblate (Polish)

Currently translated at 63.3% (64 of 101 strings)

Translated using Weblate (Polish)

Currently translated at 4.8% (39 of 809 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Polish)

Currently translated at 96.6% (231 of 239 strings)

Translated using Weblate (Polish)

Currently translated at 62.3% (63 of 101 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Polish)

Currently translated at 94.1% (224 of 238 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (501 of 501 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Piotr Łoboda <loboda4450@gmail.com>
Co-authored-by: Tomasz Słuszniak <tomasz.sluszniak@gmail.com>
Co-authored-by: magnumek <m4gnumek@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/pl/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
2026-06-06 22:25:39 -05:00
77474ccfea Translated using Weblate (Czech)
Currently translated at 84.8% (123 of 145 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milan K. <kostler.milan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/cs/
Translation: Frigate NVR/views-explore
2026-06-06 22:25:39 -05:00
8be5b9d8d0 Translated using Weblate (Catalan)
Currently translated at 100.0% (1276 of 1276 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1272 of 1272 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1268 of 1268 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (45 of 45 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1195 of 1195 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1186 of 1186 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1183 of 1183 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (175 of 175 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1181 of 1181 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1176 of 1176 strings)

Co-authored-by: Eduardo Pastor Fernández <123eduardoneko123@gmail.com>
Co-authored-by: Gerard Ricart Castells <gerard.ricart@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-replay/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/ca/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-replay
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-06 22:25:39 -05:00
ffed173d5a Translated using Weblate (Japanese)
Currently translated at 100.0% (1268 of 1268 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Japanese)

Currently translated at 93.9% (1186 of 1263 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (10 of 10 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (175 of 175 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (59 of 59 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (101 of 101 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (1186 of 1186 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (64 of 64 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (25 of 25 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (74 of 74 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (45 of 45 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: alpha <etc@alpha-line.org>
Co-authored-by: yhi264 <yhiraki@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-auth/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-groups/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-replay/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ja/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/ja/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Groups
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-auth
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-filter
Translation: Frigate NVR/components-player
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-replay
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-06 22:25:39 -05:00
f70c142892 Translated using Weblate (Ukrainian)
Currently translated at 93.0% (120 of 129 strings)

Translated using Weblate (Ukrainian)

Currently translated at 77.7% (136 of 175 strings)

Translated using Weblate (Ukrainian)

Currently translated at 54.9% (649 of 1181 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (101 of 101 strings)

Translated using Weblate (Ukrainian)

Currently translated at 92.2% (119 of 129 strings)

Translated using Weblate (Ukrainian)

Currently translated at 96.1% (25 of 26 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Ukrainian)

Currently translated at 54.7% (644 of 1176 strings)

Translated using Weblate (Ukrainian)

Currently translated at 90.0% (91 of 101 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: ivabil <ivanbilych@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/uk/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-06 22:25:39 -05:00
7c3b7f3c12 Translated using Weblate (Bulgarian)
Currently translated at 71.2% (357 of 501 strings)

Translated using Weblate (Bulgarian)

Currently translated at 20.4% (26 of 127 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: dirty <dirty@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/bg/
Translation: Frigate NVR/audio
Translation: Frigate NVR/objects
2026-06-06 22:25:39 -05:00
0756889d0e Translated using Weblate (Romanian)
Currently translated at 100.0% (1276 of 1276 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1272 of 1272 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (175 of 175 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (101 of 101 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1268 of 1268 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1263 of 1263 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1186 of 1186 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1183 of 1183 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1176 of 1176 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (238 of 238 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: lukasig <lukasig@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/ro/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-06 22:25:39 -05:00
bb1f8757e6 Translated using Weblate (Estonian)
Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Estonian)

Currently translated at 35.0% (21 of 60 strings)

Translated using Weblate (Estonian)

Currently translated at 2.5% (12 of 473 strings)

Translated using Weblate (Estonian)

Currently translated at 13.9% (18 of 129 strings)

Translated using Weblate (Estonian)

Currently translated at 41.3% (60 of 145 strings)

Translated using Weblate (Estonian)

Currently translated at 18.5% (234 of 1263 strings)

Translated using Weblate (Estonian)

Currently translated at 1.8% (15 of 811 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (49 of 49 strings)

Translated using Weblate (Estonian)

Currently translated at 14.8% (8 of 54 strings)

Translated using Weblate (Estonian)

Currently translated at 11.4% (20 of 175 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Estonian)

Currently translated at 26.6% (12 of 45 strings)

Translated using Weblate (Estonian)

Currently translated at 3.3% (2 of 59 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (Estonian)

Currently translated at 1.6% (8 of 473 strings)

Translated using Weblate (Estonian)

Currently translated at 0.3% (3 of 811 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-replay/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/et/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-replay
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-06 22:25:39 -05:00
38bc0397a6 Translated using Weblate (German)
Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (German)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (German)

Currently translated at 100.0% (1268 of 1268 strings)

Translated using Weblate (German)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (German)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (German)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (German)

Currently translated at 95.6% (1208 of 1263 strings)

Translated using Weblate (German)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (German)

Currently translated at 100.0% (238 of 238 strings)

Translated using Weblate (German)

Currently translated at 100.0% (811 of 811 strings)

Translated using Weblate (German)

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (German)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (German)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (German)

Currently translated at 99.5% (1178 of 1183 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Sebastian Sie <sebastian.neuplanitz@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/de/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
1674058b85 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.5% (238 of 239 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 80.1% (81 of 101 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (499 of 501 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.3% (234 of 238 strings)

Co-authored-by: AmilcarNetto <amilcar.netto@gmail.com>
Co-authored-by: Geraldo Fensterseifer Júnior <gerafenster@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/pt_BR/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
2026-06-06 22:25:39 -05:00
9b9bde9491 Added translation using Weblate (Telugu)
Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Added translation using Weblate (Telugu)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Pavann Kumar Kade <pavannkumar.kade@gmail.com>
2026-06-06 22:25:39 -05:00
fb3c72359f Translated using Weblate (Latvian)
Currently translated at 94.5% (226 of 239 strings)

Translated using Weblate (Latvian)

Currently translated at 24.7% (124 of 501 strings)

Translated using Weblate (Latvian)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Latvian)

Currently translated at 22.0% (28 of 127 strings)

Translated using Weblate (Latvian)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Latvian)

Currently translated at 6.6% (85 of 1276 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jānis Sanders <sanders.janis@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/lv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/lv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/lv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/lv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/lv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/lv/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-player
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-settings
2026-06-06 22:25:39 -05:00
ec7d0c8f7b Translated using Weblate (Turkish)
Currently translated at 88.1% (89 of 101 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Turhan Munis <turhan.munis@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/tr/
Translation: Frigate NVR/components-dialog
2026-06-06 22:25:39 -05:00
b7cdc1c614 Docs updates (#23407)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
* refactor go2rtc docs

* clarify go2rtc language in live

* add export docs

* Move around config items to reflect reference config is now for advanced users

* Remove outdated ipv6 section

* Fix broken links

* live usage docs

* review usage docs

* history usage

* explore usage

* add usage sidebar and move related text to usage sections

* update links

* update live

* move exports to usage

* fix anchors

* Make starts of usage pages consistent

* refactor network config

* Adjustments for review

* Add AI details to history page

* describe alerts vs detections in review usage

* simplify

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2026-06-04 17:07:12 -06:00
Josh HawkinsandGitHub d594e9d9a6 Add script to investigate accuracy of classification training set (#23324)
* add classification testing script

* add caveat
2026-06-04 15:19:45 -06:00
Josh HawkinsandGitHub 8343a96746 Update reference config (#23404)
* update reference config to include missing fields

* tweak
2026-06-04 14:24:52 -06:00
Josh HawkinsandGitHub a4f077b128 Miscellaneous fixes (#23394)
* serialize OpenVINO inference per process to prevent concurrent-inference segfault

* clean up

* add max scaling meta to login page

* add more detect section field messages

* fix icon layout in settings field messages

* tweak edit icon color
2026-06-04 12:48:58 -06:00
Josh HawkinsandGitHub b751025339 Mobile UI/UX improvements (#23402)
CI / Synaptics Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / AMD64 Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / Assemble and push default build (push) Blocked by required conditions
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Build (push) Waiting to run
* increase camera group icon size on mobile

add an animated slider when there is not enough space for all defined camera groups

* change desktop and mobile edit camera groups icon to pencil and add desktop tooltip

* apply safe area insets to mobile layout in PWA mode using viewport-fit=cover

* adaptively size bottom bar nav targets to 48px when they fit, else compact

icon size now targets the standardized 48×48px mobile touch target (Material Design 3 / Android 48dp bottom-nav minimum)
2026-06-04 09:56:11 -06:00
Josh HawkinsandGitHub 7e83d5de90 add snapshot download to History player (#23395)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
2026-06-03 16:17:04 -06:00
Nicolas MowenandGitHub a08e2d7529 Upgrade ffmpeg to 8 by default (#23393)
* Upgrade to ffmpeg 8

* Remove workaround

* Cleanup ffmpeg version resolution

* Include older 7.0 for testing purposes

* include
2026-06-03 12:28:28 -05:00
Nicolas MowenandGitHub 3f0ebb3577 Add ability to hide cameras from review UI (#23387)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* Add field to control if cameras show in review

* i18n

* Add config to UI
2026-06-02 16:11:42 -05:00
c25a522fcc docs: fix spelling mistakes in documentation (#23380)
* docs: fix spelling mistakes in documentation

* docs: fix typos and revert incorrect dfine to define rename

* docs: fix typo in installation.md

---------

Co-authored-by: TheInfamousToTo <TheInfamousToTo@users.noreply.github.com>
2026-06-02 05:49:42 -06:00
Josh HawkinsandGitHub db9e64c598 replace motion activity resample apply/agg lambdas with vectorized max() and first() (#23383)
CI / AMD64 Build (push) Waiting to run
CI / Assemble and push default build (push) Blocked by required conditions
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
2026-06-01 15:51:43 -06:00
Josh HawkinsandGitHub 570e21340a Miscellaneous fixes (#23373)
* republish MQTT switch states when a profile is activated or deactivated

* fix object mask default name when created from Explore tracking details

* tweak annotation offset max in UI

* optimize recordings/unavailable gap detection and drop empty motion activity buckets

* add tests
2026-06-01 13:55:52 -06:00
Josh HawkinsandGitHub 8073174c20 Refactor motion search (#23378)
* refactor motion search

* cleanup dead code and tests

* tweaks

* fix multi-day seeking

* start playback a few seconds before the change so the motion is in view
2026-06-01 12:08:46 -05:00
Josh HawkinsandGitHub 47a06c8b30 Tweaks (#23367)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* add ptz presets and default role widgets

* language tweaks

* fix width in triggers view

* tweak iOS PWA message in notifications settings

* deprecate ui.date_style and ui.time_style

these have been unused since date/time formatting has been pushed to i18n

* add config migrator to remove date_style and time_style

* remove date_style and time_style from reference config

* fix camera list scrolling in state classification wizard on mobile
2026-05-31 15:09:10 -06:00
Josh HawkinsandGitHub ae60197cb0 Support onvif PasswordText cameras in the add camera wizard (#23365)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / Jetson Jetpack 6 (push) Waiting to run
* try both onvif WS-Security password encodings when probing in the add camera wizard

* update onvif docs

* add tests
2026-05-31 08:20:09 -06:00
Josh HawkinsandGitHub 407817a3b1 Motion search fixes (#23359)
* improve error parsing and increase skip default

* improve motion search  layout to match tracking details

* implement draw and move mode on mobile

* update motion search docs

* language tweaks

* improve tips

* note actions menu
2026-05-31 07:51:32 -06:00
423 changed files with 17435 additions and 2124 deletions
+9
View File
@@ -162,6 +162,7 @@ mpegts
mqtt
mse
msenc
muxing
namedtuples
nbytes
nchw
@@ -197,6 +198,8 @@ OWASP
paddleocr
paho
passwordless
PCMA
PCMU
popleft
posthog
postprocess
@@ -222,7 +225,9 @@ radeontop
rawvideo
rcond
RDONLY
realmonitor
rebranded
recvonly
referer
reindex
Reolink
@@ -239,8 +244,11 @@ rocminfo
rootfs
rtmp
RTSP
rtsps
rtspx
ruamel
scroller
sendonly
setproctitle
setpts
shms
@@ -251,6 +259,7 @@ SNDMORE
socs
sqliteq
sqlitevecq
Srtp
ssdlite
statm
stimeout
+2 -2
View File
@@ -265,8 +265,8 @@ ENV PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PA
RUN --mount=type=bind,source=docker/main/install_deps.sh,target=/deps/install_deps.sh \
/deps/install_deps.sh
ENV DEFAULT_FFMPEG_VERSION="7.0"
ENV INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:5.0"
ENV DEFAULT_FFMPEG_VERSION="8.0"
ENV INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:7.0:5.0"
RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \
&& sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \
+10 -2
View File
@@ -52,9 +52,13 @@ if [[ "${TARGETARCH}" == "amd64" ]]; then
tar -xf ffmpeg.tar.xz -C /usr/lib/ffmpeg/5.0 --strip-components 1 amd64/bin/ffmpeg amd64/bin/ffprobe
rm -rf ffmpeg.tar.xz
mkdir -p /usr/lib/ffmpeg/7.0
wget -qO ffmpeg.tar.xz "https://github.com/NickM-27/FFmpeg-Builds/releases/download/autobuild-2026-03-19-13-03/ffmpeg-n7.1.3-43-g5a1f107b4c-linux64-gpl-7.1.tar.xz"
wget -qO ffmpeg.tar.xz "https://github.com/NickM-27/FFmpeg-Builds/releases/download/autobuild-2024-09-19-12-51/ffmpeg-n7.0.2-18-g3e6cec1286-linux64-gpl-7.0.tar.xz"
tar -xf ffmpeg.tar.xz -C /usr/lib/ffmpeg/7.0 --strip-components 1 amd64/bin/ffmpeg amd64/bin/ffprobe
rm -rf ffmpeg.tar.xz
mkdir -p /usr/lib/ffmpeg/8.0
wget -qO ffmpeg.tar.xz "https://github.com/NickM-27/FFmpeg-Builds/releases/download/autobuild-2026-06-02-14-20/ffmpeg-n8.1.1-9-g58d4114d36-linux64-gpl-8.1.tar.xz"
tar -xf ffmpeg.tar.xz -C /usr/lib/ffmpeg/8.0 --strip-components 1 amd64/bin/ffmpeg amd64/bin/ffprobe
rm -rf ffmpeg.tar.xz
fi
# ffmpeg -> arm64
@@ -64,9 +68,13 @@ if [[ "${TARGETARCH}" == "arm64" ]]; then
tar -xf ffmpeg.tar.xz -C /usr/lib/ffmpeg/5.0 --strip-components 1 arm64/bin/ffmpeg arm64/bin/ffprobe
rm -f ffmpeg.tar.xz
mkdir -p /usr/lib/ffmpeg/7.0
wget -qO ffmpeg.tar.xz "https://github.com/NickM-27/FFmpeg-Builds/releases/download/autobuild-2026-03-19-13-03/ffmpeg-n7.1.3-43-g5a1f107b4c-linuxarm64-gpl-7.1.tar.xz"
wget -qO ffmpeg.tar.xz "https://github.com/NickM-27/FFmpeg-Builds/releases/download/autobuild-2024-09-19-12-51/ffmpeg-n7.0.2-18-g3e6cec1286-linuxarm64-gpl-7.0.tar.xz"
tar -xf ffmpeg.tar.xz -C /usr/lib/ffmpeg/7.0 --strip-components 1 arm64/bin/ffmpeg arm64/bin/ffprobe
rm -f ffmpeg.tar.xz
mkdir -p /usr/lib/ffmpeg/8.0
wget -qO ffmpeg.tar.xz "https://github.com/NickM-27/FFmpeg-Builds/releases/download/autobuild-2026-06-02-14-20/ffmpeg-n8.1.1-9-g58d4114d36-linuxarm64-gpl-8.1.tar.xz"
tar -xf ffmpeg.tar.xz -C /usr/lib/ffmpeg/8.0 --strip-components 1 arm64/bin/ffmpeg arm64/bin/ffprobe
rm -f ffmpeg.tar.xz
fi
# arch specific packages
@@ -5,11 +5,7 @@ from typing import Any
from ruamel.yaml import YAML
sys.path.insert(0, "/opt/frigate")
from frigate.const import (
DEFAULT_FFMPEG_VERSION,
INCLUDED_FFMPEG_VERSIONS,
)
from frigate.util.config import find_config_file
from frigate.util.config import find_config_file, resolve_ffmpeg_path
sys.path.remove("/opt/frigate")
@@ -29,9 +25,4 @@ except FileNotFoundError:
config: dict[str, Any] = {}
path = config.get("ffmpeg", {}).get("path", "default")
if path == "default":
print(f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg")
elif path in INCLUDED_FFMPEG_VERSIONS:
print(f"/usr/lib/ffmpeg/{path}/bin/ffmpeg")
else:
print(f"{path}/bin/ffmpeg")
print(resolve_ffmpeg_path(path, "ffmpeg"))
@@ -11,12 +11,10 @@ sys.path.insert(0, "/opt/frigate")
from frigate.config.env import substitute_frigate_vars
from frigate.const import (
BIRDSEYE_PIPE,
DEFAULT_FFMPEG_VERSION,
INCLUDED_FFMPEG_VERSIONS,
LIBAVFORMAT_VERSION_MAJOR,
)
from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_encode
from frigate.util.config import find_config_file
from frigate.util.config import find_config_file, resolve_ffmpeg_path
from frigate.util.services import is_restricted_go2rtc_source
sys.path.remove("/opt/frigate")
@@ -81,12 +79,7 @@ if go2rtc_config.get("rtsp", {}).get("password") is not None:
# ensure ffmpeg path is set correctly
path = config.get("ffmpeg", {}).get("path", "default")
if path == "default":
ffmpeg_path = f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg"
elif path in INCLUDED_FFMPEG_VERSIONS:
ffmpeg_path = f"/usr/lib/ffmpeg/{path}/bin/ffmpeg"
else:
ffmpeg_path = f"{path}/bin/ffmpeg"
ffmpeg_path = resolve_ffmpeg_path(path, "ffmpeg")
if go2rtc_config.get("ffmpeg") is None:
go2rtc_config["ffmpeg"] = {"bin": ffmpeg_path}
+1 -1
View File
@@ -15,4 +15,4 @@ nvidia-nccl-cu12==2.26.2.post1; platform_machine == 'x86_64'
nvidia-nvjitlink-cu12==12.8.93; platform_machine == 'x86_64'
onnx==1.16.*; platform_machine == 'x86_64'
onnxruntime-gpu==1.24.*; platform_machine == 'x86_64'
protobuf==3.20.3; platform_machine == 'x86_64'
protobuf==7.35.1; platform_machine == 'x86_64'
@@ -1,2 +1,2 @@
onnx == 1.14.0; platform_machine == 'aarch64'
protobuf == 3.20.3; platform_machine == 'aarch64'
protobuf == 7.35.1; platform_machine == 'aarch64'
@@ -147,6 +147,13 @@ auth:
# NOTE: changing this value will not automatically update password hashes, you
# will need to change each user password for it to apply
hash_iterations: 600000
# Optional: Map roles to the list of cameras each role can access (default: none)
# NOTE: An empty list grants the role access to all cameras. Roles defined here can be
# referenced by proxy header role mapping or assigned to native users.
roles:
my_custom_role:
- front_door
- back_yard
# Optional: model modifications
# NOTE: The default values are for the EdgeTPU detector.
@@ -166,6 +173,9 @@ model:
# Required: Object detection model input tensor format
# Valid values are nhwc or nchw (default: shown below)
input_tensor: nhwc
# Optional: Data type of the model input tensor
# Valid values are float, float_denorm, or int (default: shown below)
input_dtype: int
# Required: Object detection model type, currently only used with the OpenVINO detector
# Valid values are ssd, yolox, yolonas (default: shown below)
model_type: ssd
@@ -196,11 +206,12 @@ audio:
# - 500 - medium sensitivity
# - 1000 - low sensitivity
min_volume: 500
# Optional: Number of threads to use for audio detection (default: shown below)
num_threads: 2
# Optional: Types of audio to listen for (default: shown below)
listen:
- bark
- fire_alarm
- scream
- speech
- yell
# Optional: Filters to configure detection.
@@ -257,7 +268,7 @@ birdseye:
# More information about presets at https://docs.frigate.video/configuration/ffmpeg_presets
ffmpeg:
# Optional: ffmpeg binary path (default: shown below)
# can also be set to `7.0` or `5.0` to specify one of the included versions
# can also be set to `8.0` or `5.0` to specify one of the included versions
# or can be set to any path that holds `bin/ffmpeg` & `bin/ffprobe`
path: "default"
# Optional: global ffmpeg args (default: shown below)
@@ -469,6 +480,8 @@ review:
- Animals in the garden
# Optional: Preferred response language (default: English)
preferred_language: English
# Optional: Save thumbnails sent to the GenAI provider for review/debugging purposes (default: shown below)
debug_save_thumbnails: False
# Optional: Motion configuration
# NOTE: Can be overridden at the camera level
@@ -500,6 +513,8 @@ motion:
# - 30 - medium sensitivity
# - 50 - low sensitivity
contour_area: 10
# Optional: Alpha blending factor used in frame differencing for motion calculation (default: shown below)
delta_alpha: 0.2
# Optional: Alpha value passed to cv2.accumulateWeighted when averaging frames to determine the background (default: shown below)
# Higher values mean the current frame impacts the average a lot, and a new object will be averaged into the background faster.
# Low values will cause things like moving shadows to be detected as motion for longer.
@@ -572,6 +587,8 @@ record:
timelapse_args: "-vf setpts=0.04*PTS -r 30"
# Optional: Global hardware acceleration settings for timelapse exports. (default: inherit)
hwaccel_args: auto
# Optional: Maximum number of export jobs to process at the same time (default: shown below)
max_concurrent: 3
# Optional: Recording Preview Settings
preview:
# Optional: Quality of recording preview (default: shown below).
@@ -638,6 +655,11 @@ snapshots:
retain:
# Required: Default retention days (default: shown below)
default: 10
# Optional: Mode for retention. (default: shown below)
# all - save all snapshots regardless of activity
# motion - save snapshots for any detected motion
# active_objects - save snapshots for active/moving objects
mode: motion
# Optional: Per object retention days
objects:
person: 15
@@ -714,28 +736,42 @@ lpr:
enhancement: 0
# Optional: Save plate images to /media/frigate/clips/lpr for debugging purposes (default: shown below)
debug_save_plates: False
# Optional: List of regex replacement rules to normalize detected plates (default: shown below)
replace_rules: {}
# Optional: List of regex replacement rules to normalize detected plates before matching (default: none)
replace_rules:
# Required: regex pattern to match in the detected plate
- pattern: "O"
# Required: string to replace the matched pattern with
replacement: "0"
# Optional: Configuration for AI / LLM provider
# Optional: Configuration for AI / LLM providers
# WARNING: Depending on the provider, this will send thumbnails over the internet
# to Google or OpenAI's LLMs to generate descriptions. GenAI features can be configured at
# the camera level to enhance privacy for indoor cameras.
# NOTE: genai is a map of named providers. Each key is a name you choose for the provider,
# and each role (chat, descriptions, embeddings) may be assigned to exactly one provider.
genai:
# Required: Provider must be one of ollama, gemini, or openai
provider: ollama
# Required if provider is ollama. May also be used for an OpenAI API compatible backend with the openai provider.
base_url: http://localhost::11434
# Required if gemini or openai
api_key: "{FRIGATE_GENAI_API_KEY}"
# Required: The model to use with the provider.
model: gemini-1.5-flash
# Optional additional args to pass to the GenAI Provider (default: None)
provider_options:
keep_alive: -1
# Optional: Options to pass during inference calls (default: {})
runtime_options:
temperature: 0.7
# Required: name of the provider (chosen by you, used to reference it elsewhere)
my_provider:
# Required: Provider must be one of ollama, openai, azure_openai, gemini, or llamacpp
provider: ollama
# Required if provider is ollama. May also be used for an OpenAI API compatible backend with the openai provider.
base_url: http://localhost::11434
# Required if gemini or openai
api_key: "{FRIGATE_GENAI_API_KEY}"
# Required: The model to use with the provider.
model: gemini-1.5-flash
# Optional: Roles this provider handles (default: shown below)
# Each role (chat, descriptions, embeddings) must be assigned to exactly one provider.
roles:
- chat
- descriptions
- embeddings
# Optional additional args to pass to the GenAI Provider (default: None)
provider_options:
keep_alive: -1
# Optional: Options to pass during inference calls (default: {})
runtime_options:
temperature: 0.7
# Optional: Configuration for audio transcription
# NOTE: only the enabled option can be overridden at the camera level
@@ -908,6 +944,9 @@ cameras:
inertia: 3
# Optional: Number of seconds that an object must loiter to be considered in the zone (default: shown below)
loitering_time: 0
# Optional: Minimum speed required for an object to be considered present in the zone (default: none)
# In real-world units if distances are set. Used for speed-based zone triggers.
speed_threshold: 2.5
# Optional: List of objects that can trigger this zone (default: all tracked objects)
objects:
- person
@@ -945,6 +984,9 @@ cameras:
order: 0
# Optional: Whether or not to show the camera in the Frigate UI (default: shown below)
dashboard: True
# Optional: Whether this camera is visible in review (the review page and its camera
# filter, motion review, and the history view) (default: shown below)
review: True
# Optional: connect to ONVIF camera
# to enable PTZ controls.
@@ -1083,22 +1125,6 @@ ui:
# Optional: Set the time format used.
# Options are browser, 12hour, or 24hour (default: shown below)
time_format: browser
# Optional: Set the date style for a specified length.
# Options are: full, long, medium, short
# Examples:
# short: 2/11/23
# medium: Feb 11, 2023
# full: Saturday, February 11, 2023
# (default: shown below).
date_style: short
# Optional: Set the time style for a specified length.
# Options are: full, long, medium, short
# Examples:
# short: 8:14 PM
# medium: 8:15:22 PM
# full: 8:15:22 PM Mountain Standard Time
# (default: shown below).
time_style: medium
# Optional: Set the unit system to either "imperial" or "metric" (default: metric)
# Used in the UI and in MQTT topics
unit_system: metric
@@ -1,7 +1,6 @@
---
id: advanced
title: Advanced Options
sidebar_label: Advanced Options
id: system
title: System
---
import ConfigTabs from "@site/src/components/ConfigTabs";
@@ -202,7 +201,7 @@ model:
:::warning
If the labelmap is customized then the labels used for alerts will need to be adjusted as well. See [alert labels](../configuration/review.md#restricting-alerts-to-specific-labels) for more info.
If the labelmap is customized then the labels used for alerts will need to be adjusted as well. See [alert labels](../review.md#restricting-alerts-to-specific-labels) for more info.
:::
@@ -234,26 +233,16 @@ Some labels have special handling and modifications can disable functionality.
## Network Configuration
Changes to Frigate's internal network configuration can be made by bind mounting nginx.conf into the container. For example:
```yaml
services:
frigate:
container_name: frigate
...
volumes:
...
- /path/to/your/nginx.conf:/usr/local/nginx/conf/nginx.conf
```
Frigate exposes a few networking options. IPv6 and the listen ports are set in the `networking` configuration (or from the Settings UI); more advanced changes require [customizing the bundled Nginx configuration](#customizing-the-nginx-configuration).
### Enabling IPv6
IPv6 is disabled by default. Enable it in the Frigate configuration.
By default Frigate listens on IPv4 only. To also listen on IPv6 — on port `5000`, and on `8971` when TLS is configured — enable it in the `networking` configuration.
<ConfigTabs>
<TabItem value="ui">
Navigate to <NavPath path="Settings > System > Networking" /> and expand **IPv6 configuration**, then enable **Enable IPv6**.
Navigate to <NavPath path="Settings > System > Networking" /> and enable **IPv6**.
</TabItem>
<TabItem value="yaml">
@@ -261,7 +250,7 @@ Navigate to <NavPath path="Settings > System > Networking" /> and expand **IPv6
```yaml
networking:
ipv6:
enabled: True
enabled: true
```
</TabItem>
@@ -300,6 +289,20 @@ This setting is for advanced users. For the majority of use cases it's recommend
:::
### Customizing the Nginx configuration
More advanced changes to Frigate's internal network configuration can be made by bind mounting your own `nginx.conf` into the container. For example:
```yaml
services:
frigate:
container_name: frigate
...
volumes:
...
- /path/to/your/nginx.conf:/usr/local/nginx/conf/nginx.conf
```
## Base path
By default, Frigate runs at the root path (`/`). However some setups require to run Frigate under a custom path prefix (e.g. `/frigate`), especially when Frigate is located behind a reverse proxy that requires path-based routing.
+65 -2
View File
@@ -88,7 +88,7 @@ Volume is considered motion for recordings, this means when the `record -> retai
### Configuring Audio Events
The included audio model has over [500 different types](https://github.com/blakeblackshear/frigate/blob/dev/audio-labelmap.txt) of audio that can be detected, many of which are not practical. By default `bark`, `fire_alarm`, `scream`, `speech`, and `yell` are enabled but these can be customized.
The included audio model has over [500 different types](https://github.com/blakeblackshear/frigate/blob/dev/audio-labelmap.txt) of audio that can be detected, many of which are not practical. By default `bark`, `fire_alarm`, `speech`, and `yell` are enabled but these can be customized.
<ConfigTabs>
<TabItem value="ui">
@@ -107,7 +107,6 @@ audio:
listen:
- bark
- fire_alarm
- scream
- speech
- yell
```
@@ -115,6 +114,70 @@ audio:
</TabItem>
</ConfigTabs>
### Common Audio Labels
The labelmap includes hundreds of sound types. The labels below are the ones most users may find practical, grouped by what they're typically used for. Use the exact label string from the left column in your `listen` config, or search for the label in the Frigate UI directly.
Some labels cover several related sounds: `yell` is triggered by shouting, yelling, children shouting, and screaming; `crying` covers baby cries, sobbing, and whimpering; and `speech` covers ordinary talking and conversation.
**Safety and security**
| Label | Detects |
| ---------------- | ---------------------------------- |
| `yell` | Shouting, yelling, screaming |
| `fire_alarm` | Fire and smoke alarm sirens |
| `smoke_detector` | Smoke detector beeps |
| `alarm` | General alarm sounds |
| `car_alarm` | Car alarms |
| `siren` | Emergency vehicle and civil sirens |
| `glass` | Glass clinking |
| `shatter` | Breaking glass |
| `breaking` | Something breaking |
| `gunshot` | Gunshots |
| `explosion` | Explosions |
**People and activity**
| Label | Detects |
| ----------- | ------------------------ |
| `speech` | Talking and conversation |
| `laughter` | Laughing |
| `crying` | Baby crying and sobbing |
| `cough` | Coughing |
| `footsteps` | Footsteps and walking |
| `knock` | Knocking on a door |
| `doorbell` | Doorbell |
| `ding-dong` | Doorbell chime |
**Pets and animals**
| Label | Detects |
| ---------- | ---------------- |
| `bark` | Dog barking |
| `dog` | Other dog sounds |
| `howl` | Howling |
| `growling` | Growling |
| `meow` | Cat meowing |
| `cat` | Other cat sounds |
| `hiss` | Hissing |
**Vehicles and driveway**
| Label | Detects |
| ----------------- | -------------------- |
| `car` | Passing cars |
| `honk` | Car horns |
| `truck` | Trucks |
| `reversing_beeps` | Vehicle backup beeps |
| `motorcycle` | Motorcycles |
| `engine_starting` | Engines starting |
:::tip
Frequently-heard labels like `speech` can generate a lot of events, and each event could save a snapshot and recording based on your configuration, so start with a focused set — the defaults (`bark`, `fire_alarm`, `speech`, `yell`) plus a few of the safety labels above cover most needs — and expand from there. See the [full audio labelmap](https://github.com/blakeblackshear/frigate/blob/dev/audio-labelmap.txt) or the Frigate UI for every available type.
:::
### Audio Transcription
Frigate supports fully local audio transcription using either `sherpa-onnx` or OpenAI's open-source Whisper models via `faster-whisper`. The goal of this feature is to support Semantic Search for `speech` audio events. Frigate is not intended to act as a continuous, fully-automatic speech transcription service — automatically transcribing all speech (or queuing many audio events for transcription) requires substantial CPU (or GPU) resources and is impractical on most systems. For this reason, transcriptions for events are initiated manually from the UI or the API rather than being run continuously in the background.
+1 -1
View File
@@ -167,7 +167,7 @@ A fast [detector](object_detectors.md) is recommended. CPU detectors will not pe
A full-frame zone in `required_zones` is not recommended, especially if you've calibrated your camera and there are `movement_weights` defined in the configuration file. Frigate will continue to autotrack an object that has entered one of the `required_zones`, even if it moves outside of that zone.
Some users have found it helpful to adjust the zone `inertia` value. See the [configuration reference](index.md).
Some users have found it helpful to adjust the zone `inertia` value. See the [configuration reference](advanced/reference.md).
## Zooming
+6 -1
View File
@@ -143,6 +143,11 @@ If your ONVIF camera does not require authentication credentials, you may still
:::
If a camera connects but fails to authenticate, two optional fields can help:
- `tls_insecure`: Skips TLS certificate verification and sends the ONVIF password as plaintext (`PasswordText`) instead of a hashed digest (`PasswordDigest`). Some cameras reject the digest token and only accept plaintext. This weakens connection security, so only enable it on a trusted local network.
- `ignore_time_mismatch`: ONVIF authentication tokens include a timestamp, and a camera will reject the token if its clock differs too much from Frigate's. Enabling this makes Frigate compensate for the time offset so authentication can still succeed. Running NTP on both the camera and the Frigate host is the recommended fix; only use this in a "safe" environment, as it slightly weakens token validation.
If your camera has multiple ONVIF profiles, you can specify which one to use for PTZ control with the `profile` option, matched by token or name. When not set, Frigate selects the first profile with a valid PTZ configuration. Check the Frigate debug logs (`frigate.ptz.onvif: debug`) to see available profile names and tokens for your camera.
An ONVIF-capable camera that supports relative movement within the field of view (FOV) can also be configured to automatically track moving objects and keep them in the center of the frame. For autotracking setup, see the [autotracking](autotracking.md) docs.
@@ -174,7 +179,7 @@ The FeatureList on the [ONVIF Conformant Products Database](https://www.onvif.or
| Hikvision DS-2DE3A404IWG-E/W | ✅ | ✅ | |
| Reolink | ✅ | ❌ | |
| Speco O8P32X | ✅ | ❌ | |
| Sunba 405-D20X | ✅ | ❌ | Incomplete ONVIF support reported on original, and 4k models. All models are suspected incompatable. |
| Sunba 405-D20X | ✅ | ❌ | Incomplete ONVIF support reported on original, and 4k models. All models are suspected incompatible. |
| Tapo | ✅ | ❌ | Many models supported, ONVIF Service Port: 2020 |
| Uniview IPC672LR-AX4DUPK | ✅ | ❌ | Firmware says FOV relative movement is supported, but camera doesn't actually move when sending ONVIF commands |
| Uniview IPC6612SR-X33-VG | ✅ | ✅ | Leave `calibrate_on_startup` as `False`. A user has reported that zooming with `absolute` is working. |
@@ -1,5 +1,5 @@
---
id: index
id: config
title: Frigate Configuration
---
@@ -57,7 +57,7 @@ VS Code supports JSON schemas for automatically validating configuration files.
## Environment Variable Substitution
Frigate supports the use of environment variables starting with `FRIGATE_` **only** where specifically indicated in the [reference config](./reference.md). For example, the following values can be replaced at runtime by using environment variables:
Frigate supports the use of environment variables starting with `FRIGATE_` **only** where specifically indicated in the [reference config](./advanced/reference.md). For example, the following values can be replaced at runtime by using environment variables:
```yaml
mqtt:
@@ -92,7 +92,7 @@ genai:
## Common configuration examples
Here are some common starter configuration examples. These can be configured through the Settings UI or via YAML. Refer to the [reference config](./reference.md) for detailed information about all config values.
Here are some common starter configuration examples. These can be configured through the Settings UI or via YAML. Refer to the [reference config](./advanced/reference.md) for detailed information about all config values.
### Raspberry Pi Home Assistant App with USB Coral
+70
View File
@@ -0,0 +1,70 @@
---
id: go2rtc
title: go2rtc
---
import ConfigTabs from "@site/src/components/ConfigTabs";
import TabItem from "@theme/TabItem";
import NavPath from "@site/src/components/NavPath";
Frigate uses the bundled go2rtc to power a number of key features:
- WebRTC or MSE for live viewing with audio, higher resolutions and frame rates than the jsmpeg stream which is limited to the detect stream and does not support audio
- Live stream support for cameras in Home Assistant Integration
- RTSP relay for use with other consumers to reduce the number of connections to your camera streams
:::tip[Most users no longer need to configure go2rtc by hand]
The **camera setup wizard** is the recommended way to add cameras. Click **Add Camera** in <NavPath path="Settings > Global configuration > Camera management" />, and the wizard probes your camera and writes its configuration for you — including the go2rtc restream and the live stream mapping — so go2rtc is set up automatically.
This guide is mainly useful if you are **upgrading from an older version and have existing cameras that don't yet use go2rtc**, or if you want to fine-tune a stream by hand (for example, to transcode a codec your browser can't play). The [go2rtc troubleshooting guide](/troubleshooting/go2rtc) applies regardless of how your cameras were added.
:::
## Adding a go2rtc stream manually
If you added your cameras with the wizard, go2rtc is already configured — you can skip straight to [troubleshooting](/troubleshooting/go2rtc). The steps below are for upgrading users with existing cameras that aren't using go2rtc yet, or for anyone who prefers to configure a stream by hand.
Configure go2rtc to connect to your camera by adding the stream you want to use for live view. Avoid changing any other parts of your config at this step. Note that go2rtc supports [many different stream types](https://github.com/AlexxIT/go2rtc/tree/v1.9.13#module-streams), not just rtsp.
:::tip
For the best experience, set the stream name under `go2rtc` to match the name of your camera so that Frigate will automatically map it and be able to use better live view options for the camera.
See [the live view docs](/configuration/live#setting-streams-for-live-ui) for more information.
:::
<ConfigTabs>
<TabItem value="ui">
Navigate to <NavPath path="Settings > System > go2rtc Streams" /> and click **Add stream**. Give the stream a name (use the camera's name so Frigate can auto-map it - for example, if your camera's name is `back`, use `back` as the go2rtc stream name), then paste the camera's stream URL into the **Source** field. Save the section.
</TabItem>
<TabItem value="yaml">
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
```
</TabItem>
</ConfigTabs>
After adding this to the config, restart Frigate and try to watch the live stream for a single camera by clicking on it from the dashboard. It should look much clearer and more fluent than the original jsmpeg stream.
### Next steps
1. If the stream you added to go2rtc is also used by Frigate for the `record` or `detect` role, you can migrate your config to pull from the RTSP restream to reduce the number of connections to your camera as shown [here](/configuration/restream#reduce-connections-to-camera).
2. You can [set up WebRTC](/configuration/live#webrtc-extra-configuration) if your camera supports two-way talk. Note that WebRTC only supports specific audio formats and may require opening ports on your router.
3. If your camera supports two-way talk, you must configure your stream with `#backchannel=0` to prevent go2rtc from blocking other applications from accessing the camera's audio output. See [preventing go2rtc from blocking two-way audio](/configuration/restream#two-way-talk-restream) in the restream documentation.
## Troubleshooting
If your stream won't play, has no audio, uses excessive CPU, or otherwise misbehaves, see the dedicated [go2rtc troubleshooting guide](/troubleshooting/go2rtc). It walks through how to isolate where the problem is and covers the most common issues — unsupported codecs, H.265/HEVC, audio, WebRTC and two-way talk, hardware-accelerated transcoding with FFmpeg 8, and camera-specific quirks.
## Homekit Configuration
To add camera streams to Homekit Frigate must be configured in docker to use `host` networking mode. Once that is done, you can use the go2rtc WebUI (accessed via port 1984, which is disabled by default) to share export a camera to Homekit. Any changes made will automatically be saved to `/config/go2rtc_homekit.yml`.
@@ -72,7 +72,7 @@ Frigate can utilize most Intel integrated GPUs and Arc GPUs to accelerate video
:::note
The default driver is `iHD`. You may need to change the driver to `i965` by adding the following environment variable `LIBVA_DRIVER_NAME=i965` to your docker-compose file or [in the `config.yml` for HA App users](advanced.md#environment_vars).
The default driver is `iHD`. You may need to change the driver to `i965` by adding the following environment variable `LIBVA_DRIVER_NAME=i965` to your docker-compose file or [in the `config.yml` for HA App users](advanced/system.md#environment_vars).
See [The Intel Docs](https://www.intel.com/content/www/us/en/support/articles/000005505/processors.html) to figure out what generation your CPU is.
@@ -169,7 +169,7 @@ Frigate can utilize modern AMD integrated GPUs and AMD GPUs to accelerate video
### Configuring Radeon Driver
You need to change the driver to `radeonsi` by adding the following environment variable `LIBVA_DRIVER_NAME=radeonsi` to your docker-compose file or [in the `config.yml` for HA App users](advanced.md#environment_vars).
You need to change the driver to `radeonsi` by adding the following environment variable `LIBVA_DRIVER_NAME=radeonsi` to your docker-compose file or [in the `config.yml` for HA App users](advanced/system.md#environment_vars).
### Via VAAPI
@@ -193,7 +193,7 @@ ffmpeg:
## NVIDIA GPUs
While older GPUs may work, it is recommended to use modern, supported GPUs. NVIDIA provides a [matrix of supported GPUs and features](https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new). If your card is on the list and supports CUVID/NVDEC, it will most likely work with Frigate for decoding. However, you must also use [a driver version that will work with FFmpeg](https://github.com/FFmpeg/nv-codec-headers/blob/master/README). Older driver versions may be missing symbols and fail to work, and older cards are not supported by newer driver versions. The only way around this is to [provide your own FFmpeg](/configuration/advanced#custom-ffmpeg-build) that will work with your driver version, but this is unsupported and may not work well if at all.
While older GPUs may work, it is recommended to use modern, supported GPUs. NVIDIA provides a [matrix of supported GPUs and features](https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new). If your card is on the list and supports CUVID/NVDEC, it will most likely work with Frigate for decoding. However, you must also use [a driver version that will work with FFmpeg](https://github.com/FFmpeg/nv-codec-headers/blob/master/README). Older driver versions may be missing symbols and fail to work, and older cards are not supported by newer driver versions. The only way around this is to [provide your own FFmpeg](/configuration/advanced/system#custom-ffmpeg-build) that will work with your driver version, but this is unsupported and may not work well if at all.
A more complete list of cards and their compatible drivers is available in the [driver release readme](https://download.nvidia.com/XFree86/Linux-x86_64/525.85.05/README/supportedchips.html).
+1 -1
View File
@@ -11,7 +11,7 @@ Frigate intelligently displays your camera streams on the Live view dashboard. B
### Live View technologies
Frigate intelligently uses three different streaming technologies to display your camera streams on the dashboard and the single camera view, switching between available modes based on network bandwidth, player errors, or required features like two-way talk. The highest quality and fluency of the Live view requires the bundled `go2rtc` to be configured as shown in the [step by step guide](/guides/configuring_go2rtc).
Frigate intelligently uses three different streaming technologies to display your camera streams on the dashboard and the single camera view, switching between available modes based on network bandwidth, player errors, or required features like two-way talk. The highest quality and fluency of the Live view requires the bundled `go2rtc` to be [configured](/configuration/go2rtc).
The jsmpeg live view will use more browser and client GPU resources. Using go2rtc is highly recommended and will provide a superior experience.
+1 -1
View File
@@ -200,4 +200,4 @@ When the skip threshold is exceeded, **no motion is reported** for that frame, m
## Reviewing Detected Motion
To review what the detector picked up — or to search past recordings for motion in a specific region — see [Reviewing Motion](review.md#reviewing-motion) on the Review page.
To review what the detector picked up — or to search past recordings for motion in a specific region — see [Reviewing Motion](/usage/review#reviewing-motion) on the Review page.
+1 -1
View File
@@ -660,7 +660,7 @@ Note that the labelmap uses a subset of the complete COCO label set that has onl
#### RF-DETR
[RF-DETR](https://github.com/roboflow/rf-detr) is a DETR based model. The ONNX exported models are supported, but not included by default. See [the models section](#downloading-rf-detr-model) for more informatoin on downloading the RF-DETR model for use in Frigate.
[RF-DETR](https://github.com/roboflow/rf-detr) is a DETR based model. The ONNX exported models are supported, but not included by default. See [the models section](#downloading-rf-detr-model) for more information on downloading the RF-DETR model for use in Frigate.
:::warning
+1 -1
View File
@@ -158,4 +158,4 @@ Models for both CPU and EdgeTPU (Coral) are bundled in the image. You can use yo
- EdgeTPU Model: `/edgetpu_model.tflite`
- Labels: `/labelmap.txt`
You also need to update the [model config](advanced.md#model) if they differ from the defaults.
You also need to update the [model config](advanced/system.md#model) if they differ from the defaults.
+6
View File
@@ -11,6 +11,12 @@ Recordings can be enabled and are stored at `/media/frigate/recordings`. The fol
New recording segments are written from the camera stream to cache, they are only moved to disk if they match the setup recording retention policy.
:::tip
To keep a specific clip beyond your retention window, [export](/usage/exports) it rather than increasing retention for the whole camera. Exports are saved separately and are never removed by retention.
:::
H265 recordings can be viewed in Chrome 108+, Edge and Safari only. All other browsers require recordings to be encoded with H264.
## Common recording configurations
+1 -37
View File
@@ -133,40 +133,4 @@ Because zones don't apply to audio, audio labels will always be marked as a dete
## Reviewing Motion
The Review page also can show periods of motion that didn't produce a tracked object, and provides a way to search past recordings for motion in a specific region. These tools complement the alerts and detections workflow above — see [Tuning Motion Detection](motion_detection.md) for how the underlying motion detector is configured.
### Motion Previews
The Motion Previews pane shows preview clips for periods of significant motion that did not produce a tracked object. It is useful for spotting things that motion detection picked up but object detection did not, which can help validate tuning or catch missed objects.
On the <NavPath path="Review > Motion" /> page, click the kebab menu on a camera and choose **Motion Previews**. Each card represents a continuous range of motion-only activity and plays back the recorded preview for that range. A heatmap overlay dims areas of the frame with no motion so the moving regions stand out.
The pane provides a few controls:
- **Speed** — speeds up or slows down all of the preview clips at once.
- **Dim** — controls how strongly non-motion areas are darkened by the heatmap overlay. Higher values increase motion area visibility.
- **Filter** — opens a 16×16 grid overlaid on a snapshot of the camera. Select one or more cells to only show clips with motion in those regions. This is helpful for filtering out motion in areas like a busy street while keeping motion in your driveway.
Clicking a preview clip seeks the recording player to that timestamp so you can review the full footage.
### Motion Search
Motion Search lets you scan recorded footage for changes inside a region of interest you draw on the camera. Unlike Motion Previews, which surfaces what Frigate's motion detector flagged in real time, Motion Search re-analyzes the saved recordings, so it can find changes that were missed (for example, an object that appeared while motion detection was paused by `lightning_threshold`, or in a region that is normally motion-masked).
To start a search, click the kebab menu on a camera in the <NavPath path="Review > Motion" /> page and choose **Motion Search**. In the dialog:
1. Pick the camera and time range to scan.
2. Draw a polygon on the camera frame to define the region of interest.
3. Adjust the search parameters if needed:
| Field | Description |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Sensitivity Threshold** | Per-pixel luminance change required to count as motion inside the ROI. Behaves like Frigate's motion detection `threshold` setting. |
| **Minimum Change Area** | Minimum percentage of the region of interest that must change for a frame to be considered significant. Raise it to ignore small movements (leaves, distant motion); lower it when the object you care about only covers a small slice of the ROI. |
| **Frame Skip** | Number of frames to skip between samples — at a camera recording 20 fps, a skip value of 20 takes motion samples roughly once per second. Higher values scan much faster and are usually the right choice; lower it only when you need to catch the exact appearance or disappearance of a fast-moving object. |
| **Maximum Results** | Maximum number of matching timestamps to return. |
| **Parallel mode** | Process multiple recording segments in parallel. Speeds up large time ranges at the cost of higher CPU usage. |
Once running, Frigate scans the recording segments that overlap the time range and reports timestamps where changes were detected inside the polygon, along with the percentage of the ROI that changed. Clicking a result seeks the player to that moment so you can review what happened.
The status panel shows live progress and metrics such as how many segments were scanned, how many were skipped because no motion was recorded for that segment (using the stored motion heatmap), how many frames were decoded, and the total wall-clock time. Segments with no recorded motion in the selected ROI are skipped automatically, which is what makes searching long time ranges practical.
The Review page can also surface periods of motion that didn't produce a tracked object, and lets you search past recordings for motion in a region you draw. See [Reviewing Motion](/usage/review#reviewing-motion) in the Usage docs for how to use **Motion Previews** and **Motion Search**, and [Tuning Motion Detection](motion_detection.md) for configuring the underlying motion detector.
+1 -6
View File
@@ -222,12 +222,7 @@ See the [Hardware Accelerated Enrichments](/configuration/hardware_acceleration_
## Usage and Best Practices
1. Semantic Search is used in conjunction with the other filters available on the Explore page. Use a combination of traditional filtering and Semantic Search for the best results.
2. Use the thumbnail search type when searching for particular objects in the scene. Use the description search type when attempting to discern the intent of your object.
3. Because of how the AI models Frigate uses have been trained, the comparison between text and image embedding distances generally means that with multi-modal (`thumbnail` and `description`) searches, results matching `description` will appear first, even if a `thumbnail` embedding may be a better match. Play with the "Search Type" setting to help find what you are looking for. Note that if you are generating descriptions for specific objects or zones only, this may cause search results to prioritize the objects with descriptions even if the the ones without them are more relevant.
4. Make your search language and tone closely match exactly what you're looking for. If you are using thumbnail search, **phrase your query as an image caption**. Searching for "red car" may not work as well as "red sedan driving down a residential street on a sunny day".
5. Semantic search on thumbnails tends to return better results when matching large subjects that take up most of the frame. Small things like "cat" tend to not work well.
6. Experiment! Find a tracked object you want to test and start typing keywords and phrases to see what works for you.
For tips on getting the best results from Semantic Search — choosing between thumbnail and description search, phrasing queries effectively, and combining search with the other Explore filters — see [Usage and best practices](/usage/explore#usage-and-best-practices) in the Usage docs.
## Triggers
+39 -1
View File
@@ -5,7 +5,7 @@ title: Camera setup
Cameras configured to output H.264 video and AAC audio will offer the most compatibility with all features of Frigate and Home Assistant. H.265 has better compression, but less compatibility. Firefox 134+/136+/137+ (Windows/Mac/Linux & Android), Chrome 108+, Safari and Edge are the only browsers able to play H.265 and only support a limited number of H.265 profiles. Ideally, cameras should be configured directly for the desired resolutions and frame rates you want to use in Frigate. Reducing frame rates within Frigate will waste CPU resources decoding extra frames that are discarded. There are three different goals that you want to tune your stream configurations around.
- **Detection**: This is the only stream that Frigate will decode for processing. Also, this is the stream where snapshots will be generated from. The resolution for detection should be tuned for the size of the objects you want to detect. See [Choosing a detect resolution](#choosing-a-detect-resolution) for more details. The recommended frame rate is 5fps, but may need to be higher (10fps is the recommended maximum for most users) for very fast moving objects. Higher resolutions and frame rates will drive higher CPU usage on your server.
- **Detection**: This is the only stream that Frigate will decode for processing. Also, this is the stream where snapshots will be generated from. The resolution for detection should be tuned for the size of the objects you want to detect. See [Choosing a detect resolution](#choosing-a-detect-resolution) for more details. The default frame rate of 5fps is correct for almost all cameras and rarely needs to be changed; see [Choosing a detect frame rate](#choosing-a-detect-frame-rate). Higher resolutions and frame rates will drive higher CPU usage on your server.
- **Recording**: This stream should be the resolution you wish to store for reference. Typically, this will be the highest resolution your camera supports. I recommend setting this feed in your camera's firmware to 15 fps.
@@ -25,6 +25,44 @@ Larger resolutions **do** improve performance if the objects are very small in t
![Resolutions](/img/resolutions-min.jpg)
### Choosing a detect frame rate
`detect.fps` controls how many times per second Frigate runs object detection — it does **not** need to match your camera's frame rate. The default of **5** is correct for the vast majority of cameras.
:::warning
Most users who raise `detect.fps` above the default don't need to. Increasing it consumes more CPU/GPU (detection load scales directly with the frame rate) while providing **no benefit to tracking** once objects are already being followed smoothly. Leave it at **5** unless you have a specific scene that fails the test below, and confirm any change actually helps in the debug view.
:::
#### Why 5 is enough for almost everyone
Frigate follows an object by matching its bounding box from one detection frame to the next, which requires the object to be detected often enough while it is on screen. At 5 fps this is satisfied in normal scenes: an object crossing a yard, porch, driveway, or walkway is in view for several seconds and produces ~15 or more detections, which is more than enough for a reliable track and a good snapshot. This includes fast subjects such as a running person or a bolting pet, which on a wide-angle view remain on screen for several seconds.
A higher rate helps only when an object crosses the **entire frame in less than two seconds**, which is determined by camera framing rather than object speed - for example, a camera aimed down a street at fast cross-traffic. In those scenes 5 fps may produce too few detections to hold a track. Cameras covering normal approaches and open areas are unaffected.
#### Checking whether a higher rate is needed
Estimate how long an object is visible as it crosses the area of interest, aiming for roughly 810 detections during the pass:
> **`detect.fps` ≈ 10 ÷ (seconds the object is in view)**
Most objects — people walking or running, pets, and vehicles in a yard, driveway, or walkway — stay in view for two seconds or more, so the default of 5 fps is correct. Slowly try raising it to 10 (the recommended maximum) in increments only when objects routinely cross the entire frame in about a second, such as a camera aimed at a street or sidewalk with fast cross-traffic. Objects that transit in under a second cannot be tracked reliably at any practical rate, so reposition the camera instead.
:::tip
If the formula calls for more than 10, the fix is **camera placement, not frame rate**. Angle the camera so objects move toward it rather than across the view, or aim it where traffic slows. A higher `detect.fps` increases CPU load proportionally without producing more detections of a too-brief object.
:::
#### Verify in the debug view
Confirm any change in the Debug view or Debug Replay. Watch a typical object cross the scene: if its bounding box follows it smoothly while visible, the rate is sufficient. A box that jumps erratically, drops out, or splits one object into multiple events indicates the rate should be increased one step.
#### Dedicated LPR cameras
A dedicated license plate recognition camera is the most common reason to use something higher than 5 fps: the camera is highly zoomed, the plate is small, and it moves at full vehicle speed, so it transits the frame quickly. However, the same ceiling applies: above 10 fps is unnecessary, and **placement matters most**: aim LPR cameras where vehicles slow down, such as gates, driveways, and parking entrances. A tight view of a fast through-road will not likely read plates reliably at any frame rate. See [License Plate Recognition](/configuration/license_plate_recognition) for details.
### Example Camera Configuration
For the Dahua/Loryta 5442 camera, I use the following settings:
+46 -14
View File
@@ -5,20 +5,40 @@ title: Glossary
The glossary explains terms commonly used in Frigate's documentation.
## Alert
The higher-priority of the two [review item](#review-item) severities, the other being a [detection](#detection). By default a review item is an alert when it involves a `person` or `car`; the qualifying [labels](#label) and [zones](#zone) can be configured. [See the review docs for more info](/configuration/review)
## Attribute
A property detected on an [object](#object) that exists alongside its [label](#label). Unlike a [sub label](#sub-label), an object can carry several attributes at once. Some attributes come directly from the object detection [model](#model) — for example `face`, `license_plate`, or delivery carrier logos such as `amazon`, `ups`, and `fedex` — while others come from a [custom object classification model](/configuration/custom_classification/object_classification) configured with the `attribute` type. Attributes are visible in the Tracked Object Details pane in Explore, in `frigate/events` MQTT messages, and through the HTTP API.
## Bounding Box
A box returned from the object detection model that outlines an object in the frame. These have multiple colors depending on object type in the debug live view.
A box returned by the object detection [model](#model) that outlines a detected [object](#object) in the frame. In the Debug view, bounding boxes are colored by object [label](#label).
### Bounding Box Colors
- At startup different colors will be assigned to each object label
- A dark blue thin line indicates that object is not detected at this current point in time
- A gray thin line indicates that object is detected as being stationary
- A thick line indicates that object is the subject of autotracking (when enabled).
- A thick line indicates that object is the subject of autotracking (when enabled)
## Class
The categories a classification [model](#model) is trained to distinguish between. Each class is a distinct visual category the model predicts, plus a `none` class for inputs that don't fit any category. For example, a custom object classification model for `person` objects might use the classes `delivery_person`, `resident`, and `none`. The predicted class is applied to the [object](#object) as either a [sub label](#sub-label) or an [attribute](#attribute), depending on the model's configuration. [See the object classification docs for more info](/configuration/custom_classification/object_classification)
## Detection
The lower-priority of the two [review item](#review-item) severities, the other being an [alert](#alert). By default, any review item that does not qualify as an alert is a detection; the qualifying [labels](#label) and [zones](#zone) can be configured. Despite the name, a detection is a category of review item — not the same as the object detection performed by the [model](#model). [See the review docs for more info](/configuration/review)
## False Positive
An incorrect detection of an object type. For example a dog being detected as a person, a chair being detected as a dog, etc. A person being detected in an area you want to ignore is not a false positive.
An incorrect result from the object detection [model](#model), where it assigns the wrong [label](#label) to something in the frame — for example a dog identified as a person, or a chair identified as a dog. A person correctly identified in an area you want to ignore is not a false positive.
## Label
The type assigned to a detected [object](#object) by the object detection [model](#model), drawn from the model's labelmap — for example `person`, `car`, or `dog`. Frigate tracks `person` by default; additional labels are tracked by adding them to the objects configuration. [See the available objects docs for the full list](/configuration/objects)
## Mask
@@ -26,44 +46,56 @@ There are two types of masks in Frigate. [See the mask docs for more info](/conf
### Motion Mask
Motion masks prevent detection of [motion](#motion) in masked areas from triggering Frigate to run object detection, but do not prevent objects from being detected if object detection runs due to motion in nearby areas. For example: camera timestamps, skies, the tops of trees, etc.
A motion mask stops [motion](#motion) in the masked area from triggering object detection. It does not stop an object from being detected when object detection runs because of motion in a nearby area. Use motion masks for parts of the frame that change constantly but never contain objects you care about — camera timestamps, the sky, the tops of trees, and so on.
### Object Mask
Object filter masks drop any bounding boxes where the bottom center (overlap doesn't matter) is in the masked area. It forces them to be considered a [false positive](#false-positive) so that they are ignored.
An object filter mask drops any [bounding box](#bounding-box) whose bottom center falls inside the masked area (overlap elsewhere doesn't matter). The object is forced to be treated as a [false positive](#false-positive) and ignored.
## Min Score
The lowest score that an object can be detected with during tracking, any detection with a lower score will be assumed to be a false positive
The lowest score a detected object can have to be kept during tracking. Anything scoring below the minimum is assumed to be a [false positive](#false-positive) and discarded.
## Model
A machine learning model that Frigate uses to detect or classify objects. The object detection model locates [objects](#object) in each frame and returns their [labels](#label) and [bounding boxes](#bounding-box). Additional enrichment models run on tracked objects to add detail: face recognition, license plate recognition, bird classification, custom object and state classification, and the embedding models used for semantic search. [See the object detectors docs for more info](/configuration/object_detectors)
## Motion
When pixels in the current camera frame are different than previous frames. When many nearby pixels are different in the current frame they grouped together and indicated with a red motion box in the live debug view. [See the motion detection docs for more info](/configuration/motion_detection)
A change in pixels between the current camera frame and previous frames. When many nearby pixels change together, they are grouped and shown as a red motion box in the debug live view. [See the motion detection docs for more info](/configuration/motion_detection)
## Object
Something Frigate can detect and follow in a camera frame, identified by its [label](#label) (for example a person or a car). The object types Frigate watches for are set in the `objects` configuration. Once an object is detected and followed across frames it becomes a [tracked object](#tracked-object-event-in-previous-versions), which may also carry a [sub label](#sub-label) and [attributes](#attribute). [See the available objects docs for more info](/configuration/objects)
## Region
A portion of the camera frame that is sent to object detection, regions can be sent due to motion, active objects, or occasionally for stationary objects. These are represented by green boxes in the debug live view.
A portion of the camera frame sent to the object detection [model](#model). Regions are selected because of [motion](#motion), active objects, or occasionally to recheck stationary objects, and are shown as green boxes in the debug live view.
## Review Item
A review item is a time period where any number of events/tracked objects were active. [See the review docs for more info](/configuration/review)
A period of time during which one or more [tracked objects](#tracked-object-event-in-previous-versions) were active, grouped together for review. Each review item is categorized as either an [alert](#alert) or a [detection](#detection). [See the review docs for more info](/configuration/review)
## Snapshot Score
The score shown in a snapshot is the score of that object at that specific moment in time.
The object's score at the specific moment the snapshot was captured.
## Sub Label
A more specific identity assigned to a [tracked object](#tracked-object-event-in-previous-versions) in addition to its [label](#label). A `person` may get the name of a recognized face, a `car` may get the name of a known license plate, and a `bird` may get its species. An object can have only one sub label at a time. Sub labels are produced by face recognition, license plate recognition, bird classification, custom object classification configured with the `sub label` type, and semantic search triggers.
## Threshold
The threshold is the median score that an object must reach in order to be considered a true positive.
The median score an object must reach to be considered a true positive.
## Top Score
The top score for an object is the highest median score for an object.
The highest median score an object reached over its lifetime.
## Tracked Object ("event" in previous versions)
The time period starting when a tracked object entered the frame and ending when it left the frame, including any time that the object remained still. Tracked objects are saved when it is considered a [true positive](#threshold) and meets the requirements for a snapshot or recording to be saved.
An [object](#object) followed from the moment it enters the frame until it leaves, including any time it stays still. A tracked object is saved once it is considered a [true positive](#threshold) and meets the requirements for a snapshot or recording.
## Zone
Zones are areas of interest, zones can be used for notifications and for limiting the areas where Frigate will create a [review item](#review-item). [See the zone docs for more info](/configuration/zones)
A user-defined area of interest within the camera frame. Zones can be used for notifications and to limit where Frigate creates a [review item](#review-item). [See the zone docs for more info](/configuration/zones)
+3 -3
View File
@@ -600,7 +600,7 @@ There are several variants of the App available:
If you are using hardware acceleration for ffmpeg, you **may** need to use the _Full Access_ variant of the App. This is because the Frigate App runs in a container with limited access to the host system. The _Full Access_ variant allows you to disable _Protection mode_ and give Frigate full access to the host system.
You can also edit the Frigate configuration file through the [VS Code App](https://github.com/hassio-addons/addon-vscode) or similar. In that case, the configuration file will be at `/addon_configs/<addon_directory>/config.yml`, where `<addon_directory>` is specific to the variant of the Frigate App you are running. See the list of directories [here](../configuration/index.md#accessing-app-config-dir).
You can also edit the Frigate configuration file through the [VS Code App](https://github.com/hassio-addons/addon-vscode) or similar. In that case, the configuration file will be at `/addon_configs/<addon_directory>/config.yml`, where `<addon_directory>` is specific to the variant of the Frigate App you are running. See the list of directories [here](../configuration/config.md#accessing-app-config-dir).
## Kubernetes
@@ -749,7 +749,7 @@ Failure to remap port 5000 on the host will result in the WebUI and all API endp
:::
Docker containers on macOS can be orchestrated by either [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/) or [OrbStack](https://orbstack.dev) (native swift app). The difference in inference speeds is negligable, however CPU, power consumption and container start times will be lower on OrbStack because it is a native Swift application.
Docker containers on macOS can be orchestrated by either [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/) or [OrbStack](https://orbstack.dev) (native Swift app). The difference in inference speeds is negligible, however CPU, power consumption and container start times will be lower on OrbStack because it is a native Swift application.
To allow Frigate to use the Apple Silicon Neural Engine / Processing Unit (NPU) the host must be running [Apple Silicon Detector](../configuration/object_detectors.md#apple-silicon-detector) on the host (outside Docker)
@@ -768,7 +768,7 @@ services:
- /path/to/your/recordings:/recordings
ports:
- "8971:8971"
# If exposing on macOS map to a diffent host port like 5001 or any orher port with no conflicts
# If exposing on macOS map to a different host port like 5001 or any other port with no conflicts
# - "5001:5000" # Internal unauthenticated access. Expose carefully.
- "8554:8554" # RTSP feeds
extra_hosts:
+1 -1
View File
@@ -32,7 +32,7 @@ The following models are downloaded automatically the first time their associate
| [License plate recognition](/configuration/license_plate_recognition) | PaddleOCR (detection, classification, recognition) + YOLOv9 plate detector | GitHub |
| [Bird classification](/configuration/bird_classification) | MobileNetV2 bird model + label map | GitHub |
| [Custom classification](/configuration/custom_classification/state_classification) (training) | MobileNetV2 ImageNet base weights (via Keras) | Google storage |
| [Audio transcription](/configuration/advanced) | Whisper or Sherpa-ONNX streaming model | HuggingFace / OpenAI |
| [Audio transcription](/configuration/advanced/system) | Whisper or Sherpa-ONNX streaming model | HuggingFace / OpenAI |
### Hardware-Specific Detector Models
-116
View File
@@ -1,116 +0,0 @@
---
id: configuring_go2rtc
title: Configuring go2rtc
---
Use of the bundled go2rtc is optional. You can still configure FFmpeg to connect directly to your cameras. However, adding go2rtc to your configuration is required for the following features:
- WebRTC or MSE for live viewing with audio, higher resolutions and frame rates than the jsmpeg stream which is limited to the detect stream and does not support audio
- Live stream support for cameras in Home Assistant Integration
- RTSP relay for use with other consumers to reduce the number of connections to your camera streams
## Setup a go2rtc stream
First, you will want to configure go2rtc to connect to your camera stream by adding the stream you want to use for live view in your Frigate config file. Avoid changing any other parts of your config at this step. Note that go2rtc supports [many different stream types](https://github.com/AlexxIT/go2rtc/tree/v1.9.13#module-streams), not just rtsp.
:::tip
For the best experience, you should set the stream name under `go2rtc` to match the name of your camera so that Frigate will automatically map it and be able to use better live view options for the camera.
See [the live view docs](../configuration/live.md#setting-streams-for-live-ui) for more information.
:::
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
```
After adding this to the config, restart Frigate and try to watch the live stream for a single camera by clicking on it from the dashboard. It should look much clearer and more fluent than the original jsmpeg stream.
### What if my video doesn't play?
- Check Logs:
- Access the go2rtc logs in the Frigate UI under Logs in the sidebar.
- If go2rtc is having difficulty connecting to your camera, you should see some error messages in the log.
- Check go2rtc Web Interface: if you don't see any errors in the logs, try viewing the camera through go2rtc's web interface.
- Navigate to port 1984 in your browser to access go2rtc's web interface.
- If using Frigate through Home Assistant, enable the web interface at port 1984.
- If using Docker, forward port 1984 before accessing the web interface.
- Click `stream` for the specific camera to see if the camera's stream is being received.
- Check Video Codec:
- If the camera stream works in go2rtc but not in your browser, the video codec might be unsupported.
- If using H265, switch to H264. Refer to [video codec compatibility](https://github.com/AlexxIT/go2rtc/tree/v1.9.13#codecs-madness) in go2rtc documentation.
- If unable to switch from H265 to H264, or if the stream format is different (e.g., MJPEG), re-encode the video using [FFmpeg parameters](https://github.com/AlexxIT/go2rtc/tree/v1.9.13#source-ffmpeg). It supports rotating and resizing video feeds and hardware acceleration. Keep in mind that transcoding video from one format to another is a resource intensive task and you may be better off using the built-in jsmpeg view.
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
- "ffmpeg:back#video=h264#hardware"
```
- Switch to FFmpeg if needed:
- Some camera streams may need to use the ffmpeg module in go2rtc. This has the downside of slower startup times, but has compatibility with more stream types.
```yaml
go2rtc:
streams:
back:
- ffmpeg:rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
```
- If you can see the video but do not have audio, this is most likely because your camera's audio stream codec is not AAC.
- If possible, update your camera's audio settings to AAC in your camera's firmware.
- If your cameras do not support AAC audio, you will need to tell go2rtc to re-encode the audio to AAC on demand if you want audio. This will use additional CPU and add some latency. To add AAC audio on demand, you can update your go2rtc config as follows:
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
- "ffmpeg:back#audio=aac"
```
If you need to convert **both** the audio and video streams, you can use the following:
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
- "ffmpeg:back#video=h264#audio=aac#hardware"
```
When using the ffmpeg module, you would add AAC audio like this:
```yaml
go2rtc:
streams:
back:
- "ffmpeg:rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2#video=copy#audio=copy#audio=aac#hardware"
```
:::warning
To access the go2rtc stream externally when utilizing the Frigate App (for
instance through VLC), you must first enable the RTSP Restream port.
You can do this by visiting the Frigate App configuration page within Home
Assistant and revealing the hidden options under the "Show disabled ports"
section.
:::
### Next steps
1. If the stream you added to go2rtc is also used by Frigate for the `record` or `detect` role, you can migrate your config to pull from the RTSP restream to reduce the number of connections to your camera as shown [here](/configuration/restream#reduce-connections-to-camera).
2. You can [set up WebRTC](/configuration/live#webrtc-extra-configuration) if your camera supports two-way talk. Note that WebRTC only supports specific audio formats and may require opening ports on your router.
3. If your camera supports two-way talk, you must configure your stream with `#backchannel=0` to prevent go2rtc from blocking other applications from accessing the camera's audio output. See [preventing go2rtc from blocking two-way audio](/configuration/restream#two-way-talk-restream) in the restream documentation.
## Homekit Configuration
To add camera streams to Homekit Frigate must be configured in docker to use `host` networking mode. Once that is done, you can use the go2rtc WebUI (accessed via port 1984, which is disabled by default) to share export a camera to Homekit. Any changes made will automatically be saved to `/config/go2rtc_homekit.yml`.
+8 -9
View File
@@ -301,7 +301,7 @@ cameras:
More details on available detectors can be found [here](../configuration/object_detectors.md).
Restart Frigate and you should start seeing detections for `person`. If you want to track other objects, they can be configured in <NavPath path="Settings > Global configuration > Objects" /> or via the [configuration file reference](../configuration/reference.md).
Restart Frigate and you should start seeing detections for `person`. If you want to track other objects, they can be configured in <NavPath path="Settings > Global configuration > Objects" /> or via the [configuration file reference](../configuration/advanced/reference.md).
### Step 5: Setup motion masks
@@ -388,21 +388,20 @@ If you only plan to use Frigate for recording, it is still recommended to define
:::
By default, Frigate will retain video of all tracked objects for 10 days. The full set of options for recording can be found [here](../configuration/reference.md).
By default, Frigate will retain video of all tracked objects for 10 days. The full set of options for recording can be found [here](../configuration/advanced/reference.md).
### Step 7: Complete config
At this point you have a complete config with basic functionality.
- View [common configuration examples](../configuration/index.md#common-configuration-examples) for a list of common configuration examples.
- View [full config reference](../configuration/reference.md) for a complete list of configuration options.
- View [common configuration examples](../configuration/config.md#common-configuration-examples) for a list of common configuration examples.
- View [full config reference](../configuration/advanced/reference.md) for a complete list of configuration options.
### Follow up
Now that you have a working install, you can use the following documentation for additional features:
1. [Configuring go2rtc](configuring_go2rtc.md) - Additional live view options and RTSP relay
2. [Zones](../configuration/zones.md)
3. [Review](../configuration/review.md)
4. [Masks](../configuration/masks.md)
5. [Home Assistant Integration](../integrations/home-assistant.md) - Integrate with Home Assistant
1. [Zones](../configuration/zones.md)
2. [Review](../configuration/review.md)
3. [Masks](../configuration/masks.md)
4. [Home Assistant Integration](../integrations/home-assistant.md) - Integrate with Home Assistant
+1 -1
View File
@@ -12,7 +12,7 @@ Before setting up a reverse proxy, check if any of the built-in functionality in
|-|-|
|TLS|Please see the `tls` [configuration option](../configuration/tls.md)|
|Authentication|Please see the [authentication](../configuration/authentication.md) documentation|
|IPv6|[Enabling IPv6](../configuration/advanced.md#enabling-ipv6)
|IPv6|[Enabling IPv6](../configuration/advanced/system.md#enabling-ipv6)
**Note about TLS**
When using a reverse proxy, the TLS session is usually terminated at the proxy, sending the internal request over plain HTTP. If this is the desired behavior, TLS must first be disabled in Frigate, or you will encounter an HTTP 400 error: "The plain HTTP request was sent to HTTPS port."
+1 -1
View File
@@ -24,7 +24,7 @@ Video decoding is one of the most CPU-intensive tasks in Frigate. While an AI ac
### Configuration
Frigate provides preset configurations for common hardware acceleration scenarios. Set up `hwaccel_args` based on your hardware in your [configuration](../configuration/reference) as described in the [getting started guide](../guides/getting_started).
Frigate provides preset configurations for common hardware acceleration scenarios. Set up `hwaccel_args` based on your hardware in your [configuration](../configuration/advanced/reference) as described in the [getting started guide](../guides/getting_started).
### Troubleshooting Hardware Acceleration
+1 -1
View File
@@ -55,7 +55,7 @@ If you see repeated "On connect called" messages in your logs, check for another
### Error: Database Is Locked
SQLite does not work well on a network share, if the `/media` folder is mapped to a network share then [this guide](../configuration/advanced.md#database) should be used to move the database to a location on the internal drive.
SQLite does not work well on a network share, if the `/media` folder is mapped to a network share then [this guide](../configuration/advanced/system.md#database) should be used to move the database to a location on the internal drive.
### Unable to publish to MQTT: client is not connected
+235
View File
@@ -0,0 +1,235 @@
---
id: go2rtc
title: Troubleshooting go2rtc
---
import ConfigTabs from "@site/src/components/ConfigTabs";
import TabItem from "@theme/TabItem";
import NavPath from "@site/src/components/NavPath";
This page covers common problems with the bundled [go2rtc](/configuration/go2rtc) and how to resolve them, whether your cameras were added with the setup wizard or configured by hand.
When a stream won't play or behaves oddly, the most important first step is to figure out **where** in the pipeline it breaks. Frigate's live view is a chain — _camera → go2rtc → your browser_ — and each stage fails for different reasons. Work through the checks below in order, then jump to the matching problem category.
## Start by isolating the problem
### 1. Read the go2rtc logs
Access the go2rtc logs in the Frigate UI under <NavPath path="System Logs" /> in the sidebar (select the **go2rtc** tab). If go2rtc cannot connect to your camera you will usually see a clear error here — `401 Unauthorized` (bad or incorrectly encoded credentials), `Connection refused` / `timeout` (wrong IP, port, or the camera is at its connection limit), or `404 Not Found` (wrong RTSP path, or the referenced stream name does not exist).
### 2. Test the stream in the go2rtc web interface
If the logs look clean, open go2rtc's own web interface on port `1984`. This is the single most useful diagnostic, because it takes Frigate's UI out of the equation entirely.
- If using Frigate through Home Assistant, enable the web interface at port `1984` (it is disabled by default — see [Home Assistant ports](#home-assistant-and-port-access)).
- If using Docker, forward port `1984` before accessing the web interface.
Open the stream page for your camera (`http://<frigate_host>:1984/stream.html?src=back`) and try each player link:
- **If nothing plays here**, the problem is between the camera and go2rtc (codec, credentials, or transport), _not_ your browser. Fix it at the source before touching anything in Frigate.
- **If a player works here but Frigate's live view does not**, the problem is browser/codec related — compare the **MSE** and **WebRTC** links. Frigate prefers MSE and only attempts WebRTC when MSE fails (or for two-way talk). If `mode=mse` plays but `mode=webrtc` does not, you have a [WebRTC codec problem](#webrtc-and-two-way-talk); if neither plays, your browser cannot decode the codec (commonly H.265 — see [H.265 / HEVC cameras](#h265--hevc-cameras)).
### 3. Inspect the negotiated codecs
You can view detailed stream info — including the exact video and audio codecs go2rtc negotiated with the camera — at `http://frigate_ip:5000/api/go2rtc/streams` (or `http://frigate_ip:5000/api/go2rtc/streams/back` for a single camera). This is the authoritative answer to "what is my camera actually sending?" and is far more reliable than guessing from the camera's web UI. It also shows whether the audio track is `sendonly`/`recvonly`, which matters for [two-way talk](#webrtc-and-two-way-talk).
### 4. Fix the codec with the FFmpeg module
If the camera plays in go2rtc but not in your browser, the video or audio codec is unsupported. Browsers can reliably play **H.264** video and **AAC** audio; many cannot play H.265/HEVC, and some camera audio (G.711/PCM, MJPEG containers, etc.) is not playable at all. The fix is to have go2rtc re-encode the stream on demand using its FFmpeg module.
In the Frigate UI this is the **Use compatibility mode (ffmpeg)** toggle on a stream source; in YAML it is the `ffmpeg:` prefix on the source URL.
<ConfigTabs>
<TabItem value="ui">
1. Navigate to <NavPath path="Settings > System > go2rtc Streams" /> and expand your camera's stream.
2. On the source you want to convert, click the **Use compatibility mode (ffmpeg)** button (the sliders icon next to the URL). This routes the source through go2rtc's FFmpeg module and reveals the transcoding options.
3. Set **Video** to **Transcode to H.264** if your browser can't play the camera's video codec (e.g. H.265). Leave it on **Copy** to pass the video through untouched — this is much cheaper and should be your default whenever only the audio needs converting.
4. Set **Audio** to **Transcode to AAC** (for MSE) or **Transcode to Opus** (for WebRTC) if the camera's audio codec is unsupported. Leave it on **Copy** to keep the original, or **Exclude** to drop audio entirely.
5. When transcoding **video**, set **Hardware acceleration** to **Automatic (recommended)** so the encode runs on your GPU instead of the CPU. See [hardware-accelerated transcoding](#hardware-accelerated-transcoding-with-ffmpeg-8) for an important FFmpeg 8 caveat.
6. **Save** the section, then reload the live view.
</TabItem>
<TabItem value="yaml">
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
# transcode video to H.264 on the GPU; only needed if the browser can't play the source codec
- "ffmpeg:back#video=h264#hardware"
```
To convert audio only (leaving video untouched), or to convert both:
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
- "ffmpeg:back#audio=aac" # audio only — preferred when the video already plays
# or, to convert both video and audio:
# - "ffmpeg:back#video=h264#audio=aac#hardware"
```
</TabItem>
</ConfigTabs>
:::warning
The `#`-modifiers (`#video=`, `#audio=`, `#hardware`, `#backchannel=0`, …) **only take effect on a source that is prefixed with `ffmpeg:`**. Adding them to a bare `rtsp://…#audio=opus` source does nothing — go2rtc ignores them. Likewise, when a source references another stream by name (e.g. `ffmpeg:back#audio=aac`), the name must match the stream key **exactly** (it is case sensitive), or the transcode is silently never produced. This is the single most common configuration mistake. In the Frigate UI, the **Use compatibility mode (ffmpeg)** toggle adds the `ffmpeg:` prefix for you.
:::
Transcoding video is resource intensive. Always prefer `#video=copy` (the **Copy** option) and only convert the track that is actually unsupported. If you must transcode video and have no hardware encoder available, the built-in jsmpeg view may be the better option.
## Live view is black, buffering, or stuck in "low-bandwidth mode"
When the live view shows a black screen, spins forever, or repeatedly drops to the lower-quality jsmpeg player ("low-bandwidth mode"), the stream almost always contains something the browser cannot decode over MSE — usually H.265 video or a non-AAC audio track. Confirm this in the go2rtc web UI (port `1984`): if MSE won't play there, Frigate can't play it either, since it uses the same pipeline.
The fix is to produce an **H.264 + AAC** stream, either by changing your camera's firmware codecs or by transcoding in go2rtc (see [Fix the codec with the FFmpeg module](#4-fix-the-codec-with-the-ffmpeg-module)). A few other things worth checking:
- **Set the camera's I-frame (keyframe) interval to match its frame rate** (or "1x" on Reolink), and avoid "smart"/"+" codecs like _H.264+_ or _H.265+_. A long keyframe interval delays the first decodable frame past Frigate's startup timeout, which forces the fallback to jsmpeg. See [camera settings recommendations](/configuration/live#camera-settings-recommendations).
- **A spinner that never clears, even though video plays in VLC**, is often an unplayable _audio_ track stalling playback. Drop or transcode the audio (see below).
- **Remote/VPN viewing that buffers** while the LAN is fine is usually latency/jitter exceeding MSE's startup buffer — set up [WebRTC](/configuration/live#webrtc-extra-configuration), which drops late frames instead of buffering.
The general live-view behavior (smart streaming, the MSE → WebRTC → jsmpeg fallback chain, and how to read browser console errors) is documented in detail in the [Live view FAQ](/configuration/live#live-view-faq).
## H.265 / HEVC cameras
H.265/HEVC playback in the browser is unreliable and version-dependent. WebRTC does not support H.265 on some browsers, and MSE/HEVC support varies by browser, OS, and whether a hardware decoder is present. An H.265 stream that plays fine in VLC, the go2rtc web UI, and Frigate's recordings can still be blank in a live view.
For dependable live viewing, use **H.264** for the stream the live view consumes:
- Point the live view at the camera's H.264 **substream** and keep the H.265 main stream for recording only, or
- Transcode H.265 → H.264 in go2rtc with the FFmpeg module and `#hardware` (software HEVC transcoding is very CPU heavy).
Treat browser HEVC playback as best-effort. See also [H.265 cameras via Safari](/configuration/camera_specific#h265-cameras-via-safari).
## No audio in Live view
Live view audio has strict codec requirements that differ by player: **MSE requires AAC, PCMA, or PCMU**, and **WebRTC requires Opus, PCMA, or PCMU**. Many cameras default to a codec outside these sets (or to PCM/G.711), so the player loads video only and no audio control appears.
The most robust approach is to provide both an AAC track (for MSE) and an Opus track (for WebRTC) on the same stream by transcoding audio with the FFmpeg module while copying the video:
<ConfigTabs>
<TabItem value="ui">
1. Navigate to <NavPath path="Settings > System > go2rtc Streams" /> and expand the camera's stream.
2. Add a second **Source** that references the stream by name (e.g. the URL `ffmpeg:back`), enable **Use compatibility mode (ffmpeg)**, and set **Audio** to **Transcode to Opus** for WebRTC support.
3. Keep the original source as **Source 1** so MSE can use the camera's AAC (or transcode the first source's audio to AAC if the camera doesn't provide it).
4. **Save** the section.
</TabItem>
<TabItem value="yaml">
```yaml
go2rtc:
streams:
back:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2 # video + AAC for MSE
- "ffmpeg:back#audio=opus" # adds an Opus track for WebRTC
```
If the camera's native audio isn't AAC either, transcode both:
```yaml
go2rtc:
streams:
back:
- "ffmpeg:rtsp://user:password@10.0.10.10:554/live0#video=copy#audio=aac" # video copy + AAC for MSE
- "ffmpeg:back#audio=opus" # Opus for WebRTC
```
</TabItem>
</ConfigTabs>
Setting the camera firmware to AAC (and H.264) avoids transcoding entirely and is always preferable when the camera supports it. For more detail and examples, see [Audio Support](/configuration/live#audio-support).
## WebRTC and two-way talk
WebRTC is only attempted when MSE fails or when using a camera's two-way talk feature; the "All Cameras" dashboard never uses it. When it doesn't work, the cause is almost always one of:
- **Codec mismatch** — WebRTC cannot carry H.265 or AAC. The stream backing the WebRTC view must provide Opus (or PCMA/PCMU) audio and H.264 video. Add an `ffmpeg:back#audio=opus` source as shown above.
- **Port `8555` not reachable, or no candidates set** — WebRTC needs port `8555` (both TCP and UDP) open and a reachable candidate advertised. On Docker installs running on a custom/overlay network, go2rtc may advertise unreachable container IPs as ICE candidates; setting `webrtc.filters.candidates: []` and supplying only your host's LAN IP resolves this. See [WebRTC extra configuration](/configuration/live#webrtc-extra-configuration).
- **Two-way talk** additionally requires a secure context (HTTPS or the authenticated port `8971`, because browsers block microphone access on plain HTTP). The camera's RTSP backchannel must also be handled correctly — go2rtc seizes the backchannel by default, which blocks two-way audio for other consumers and can inject static. Disable it on the primary stream with `#backchannel=0` and use a separate dedicated stream for talk, as documented in [preventing go2rtc from blocking two-way audio](/configuration/restream#two-way-talk-restream).
## High CPU usage
If go2rtc is using a lot of CPU, it is almost always transcoding in software. An FFmpeg source with a codec modifier like `#video=h264` or `#audio=aac` but **no** `#hardware` re-encodes on the CPU. (Frigate's `ffmpeg.hwaccel_args` only applies to Frigate's own detect/record processes — it does _not_ accelerate go2rtc's transcodes.)
To keep CPU usage down:
- Only transcode the track that is genuinely unsupported, and use `#video=copy` to pass video through untouched whenever possible.
- When you must transcode video, always add `#hardware` (the **Automatic** hardware option in the UI) so the encode runs on the GPU. Note the [FFmpeg 8 device requirement](#hardware-accelerated-transcoding-with-ffmpeg-8) below.
- Don't restream a high-resolution main stream just to feed the live view — even with `#video=copy`, muxing a 4K/8MP+ stream is inherently expensive. Use the camera's lower-resolution substream for live and detect, and let Frigate pull the main stream directly for recording.
## Connection, authentication, and complex passwords
If go2rtc logs `401 Unauthorized` for a URL that works in VLC, the password almost certainly contains reserved URL characters. **Frigate URL-encodes passwords for its own `cameras.ffmpeg.inputs`, but it does not touch what you write under `go2rtc.streams`** — go2rtc parses that URL itself. You must URL-encode special characters yourself in the `go2rtc.streams` section (`@``%40`, `#``%23`, `?``%3F`, `%``%25`, etc.).
Note the asymmetry: under `cameras.ffmpeg.inputs` you should use the **raw** password (Frigate encodes it for you) — pre-encoding it there causes a double-encode and fails. See [Handling Complex Passwords](/configuration/restream#handling-complex-passwords).
Repeated `401`/`Connection refused` errors can also mean the camera hit its **concurrent connection limit** or triggered a login lockout. Routing all roles through a single [RTSP restream](/configuration/restream#reduce-connections-to-camera) means the camera only ever sees one connection from go2rtc.
## Stream names must match everywhere
A surprising number of "the better live options aren't available" or `404 Not Found` problems come down to a name mismatch. The same string must be used consistently:
- the **go2rtc stream key** (`go2rtc.streams.<name>`),
- any `ffmpeg:<name>#…` source that references it,
- the camera's restream input path (`rtsp://127.0.0.1:8554/<name>`), and
- the camera name itself (so Frigate auto-maps it for MSE/WebRTC) — or an explicit `live -> streams` mapping pointing at the go2rtc stream **name** (never a path).
If you rename or remove a go2rtc stream while experimenting and the live stream selector then shows a blank entry, clear your browser's site data for the Frigate URL — the selected stream is cached per-device in local storage.
## Camera-specific behavior
Several camera brands have well-known quirks with go2rtc. Rather than repeat them here, see the [camera-specific configuration](/configuration/camera_specific) page, which covers them in detail. The highlights:
- **Reolink** — RTSP is unreliable on many models; the **http-flv** stream through the FFmpeg module is recommended, and you must enable HTTP/RTMP in the camera and **reboot** it. 6MP+ models stream H.265 over http-flv-enhanced, which requires FFmpeg 8.0. See [Reolink Cameras](/configuration/camera_specific#reolink-cameras).
- **TP-Link Tapo** — use go2rtc's native `tapo://` source for stability and two-way audio; a stale RTSP credential can often be revived by clicking play once in the go2rtc web UI.
- **Ubiquiti/UniFi Protect** — use the `rtspx://` scheme (not `rtsps://…?enableSrtp`).
- **Amcrest/Dahua** — use the `/cam/realmonitor?channel=1&subtype=N` scheme, where `subtype=0` is the main stream. See [Amcrest & Dahua](/configuration/camera_specific#amcrest--dahua).
## Non-RTSP sources and the FFmpeg module
go2rtc's native zero-copy handling only supports well-formed RTSP H.264/H.265. Anything else — MJPEG, HTTP/HTTP-FLV, RTMP, or unusual codecs — must be handed to the FFmpeg module by prefixing the source with `ffmpeg:`. This is also necessary for some camera streams to be parsed at all, at the cost of slightly slower startup. MJPEG and other non-H.264 sources additionally need `#video=h264` (with `#hardware`) before they can be used for the `record`, `detect`, or restream roles. See [MJPEG Cameras](/configuration/camera_specific#mjpeg-cameras) for a complete example.
## Hardware-accelerated transcoding with FFmpeg 8
Frigate 0.18 ships **FFmpeg 8.0** as the default, and FFmpeg 8 is stricter about hardware-accelerated filtering than earlier versions. Whenever go2rtc transcodes video with hardware acceleration (any source using `#hardware`, `#hardware=vaapi`, or the **Automatic** hardware option in the UI), it builds a filter chain that uploads frames to the GPU with the `hwupload` filter. FFmpeg 8 now refuses to do this unless it is told **which device** to use — earlier versions selected one automatically. The result is that an otherwise-working transcode fails to start, the live view never loads, and go2rtc logs:
```
[hwupload] A hardware device reference is required to upload frames to.
[AVFilterGraph] Error initializing filters
Error opening output files: Invalid argument
```
The fix is to tell go2rtc's bundled FFmpeg which hardware device to use via the `go2rtc -> ffmpeg -> global` option. For **VAAPI**-based acceleration — which covers most Intel and AMD GPUs, and is what go2rtc selects automatically on that hardware — point it at your render device:
```yaml
go2rtc:
ffmpeg:
global: "-vaapi_device /dev/dri/renderD128"
streams:
back:
- "ffmpeg:rtsp://user:password@10.0.10.10:554/live0#video=h264#hardware"
```
`/dev/dri/renderD128` is the usual render node; on a system with more than one GPU you may need `renderD129` (or higher), and the device must be passed into the container (e.g. `devices: - /dev/dri:/dev/dri` in Docker Compose).
If you use a **different hardware acceleration backend**, you will likely need to specify its device in the same way, using the option that matches that backend instead of `-vaapi_device`. See the [go2rtc FFmpeg source documentation](https://github.com/AlexxIT/go2rtc/tree/v1.9.13#source-ffmpeg) and the upstream report ([go2rtc issue #1984](https://github.com/AlexxIT/go2rtc/issues/1984)) for background and other examples.
:::tip
If you don't transcode in go2rtc with hardware acceleration, this does not affect you. If you want to avoid the change entirely, you can pin Frigate (and the go2rtc it bundles) back to FFmpeg 7.0 by setting `ffmpeg -> path: "7.0"` in your config.
:::
## Home Assistant and port access
When running Frigate as a Home Assistant add-on, the go2rtc API (port `1984`), the RTSP restream (port `8554`), and WebRTC (port `8555`) are **disabled and hidden by default**. To use them — for example to reach the go2rtc web interface for troubleshooting, or to open a go2rtc stream externally in an app like VLC — go to <NavPath path="Settings > Add-ons > Frigate > Configuration > Network" />, click **Show disabled ports**, enable the port you need, and save. Use the host's IP address rather than an mDNS name like `homeassistant.local`.
If live view works in the Frigate UI but not in Home Assistant, the most common cause is the go2rtc stream name not matching the camera name — name the primary go2rtc stream exactly like the camera, or add a `live -> streams` mapping, so the integration can resolve the restream.
+6
View File
@@ -121,6 +121,12 @@ If segments are only ~1 second instead of ~10 seconds, the camera is sending cor
- **Changing codec, bitrate, or resolution mid-stream** — Any encoding changes during an active stream can cause unpredictable segment splitting.
- **Camera firmware bugs** — Check for firmware updates from your camera manufacturer.
:::tip
You don't have to run `ffprobe` by hand to catch this. Open a camera's **Camera Probe Info** dialog (the info icon on the System → Metrics → Cameras page) and check the **Keyframe analysis** section. It probes the record stream and flags sparse or variable keyframes, which is what smart/"+" codecs (H.264+/H.265+) and long keyframe intervals produce.
:::
### Step 4: Check for a stuck detector
If the detect stream is not processing frames, segments will accumulate. Common causes:
+95
View File
@@ -0,0 +1,95 @@
---
id: explore
title: Explore
---
import NavPath from "@site/src/components/NavPath";
**Explore** is where you browse and search every **tracked object** Frigate has saved. By default it groups recent objects by label; when [Semantic Search](/configuration/semantic_search) is enabled, you can also search by natural-language description or visual similarity. Selecting any object opens a detail pane with its snapshot, lifecycle, and metadata.
This page describes how to _use_ the Explore view. For how the underlying features are _configured_, see [Semantic Search](/configuration/semantic_search) and [Generative AI descriptions](/configuration/genai/genai_objects).
## Browsing tracked objects
The default view shows your most recent tracked objects grouped into rows by label — _Person_, _Car_, _Dog_, and so on — each row labeled with the object type and a count. The arrow at the end of a row opens the full, filterable grid for that label.
Clicking a thumbnail opens its [detail dialog](#tracked-object-details); right-clicking or long-pressing a thumbnail opens an [actions menu](#actions-and-bulk-selection). You can switch to a denser grid layout and adjust the number of columns from the view's settings.
## Searching
When [Semantic Search](/configuration/semantic_search) is enabled, a search bar appears that combines two things in one input:
- **Natural-language search** — type a free-text query and press Enter to run a semantic search over your tracked objects.
- **Filter tokens** — type a `key:` to get suggestions, then a value, to add a structured filter. Each filter becomes a removable chip, and you can chain several together.
You can save a search with the star icon and reload it later, and clear everything with the clear-search icon. A help popover explains the token syntax, for example:
```
cameras:front_door label:person before:01012024 time_range:3:00PM-4:00PM
```
### Filter reference
The most common filter tokens are:
| Filter | Description |
| ---------------------------- | ---------------------------------------------------------------------------------- |
| **Cameras** | Limit to one or more cameras. |
| **Labels** | Object labels (person, car, etc.). |
| **Sub Labels** | Recognized sub labels (e.g. a recognized face or name). |
| **Attributes** | Classification attributes applied to the object. |
| **Recognized License Plate** | Match a recognized plate. |
| **Zones** | Objects that entered specific zones. |
| **Before / After** | Restrict to a date range. |
| **Time Range** | Restrict to a time of day (`HH:MM-HH:MM`). |
| **Min / Max Score** | Restrict by the object's confidence score. |
| **Min / Max Speed** | Restrict by estimated speed (when speed estimation is configured). |
| **Has Snapshot / Has Clip** | Only objects that saved a snapshot or recording. |
| **Submitted to Frigate+** | Only objects already submitted (when Frigate+ is enabled). |
| **Search Type** | Whether semantic search matches the object's **Thumbnail** or its **Description**. |
### Sorting
When a filter or search is active, a **Sort** control lets you order results by **date**, **object score**, or **estimated speed** (ascending or descending). When a semantic query or similarity search is active, results can also be ordered by **relevance**.
### Thumbnail and description search
- The **Search Type** setting controls whether a text query is matched against each object's **thumbnail** or its **description**. Each result indicates which one it matched and the confidence.
Natural-language search, thumbnail search, and description search all require [Semantic Search](/configuration/semantic_search) to be enabled.
## Tracked Object Details
Selecting an object opens the **Tracked Object Details** dialog. Use the arrows (or the left/right keys) to step to the previous or next object. The dialog has two tabs:
- **Snapshot** or **Thumbnail** — the saved snapshot (or thumbnail).
- **Tracking Details** — the object's lifecycle, available when the object has a recording. It lists each significant moment (detected, entered a zone, became active or stationary, left, and so on); clicking a moment plays that part of the recording with the bounding box overlaid. A settings popover lets you show all zones and adjust the annotation offset.
The details pane shows the object's **label**, **scores**, **camera**, **timestamp**, estimated **speed**, any **recognized license plate** and **classification attributes**, and its **description**. Admins can edit the sub label, license plate, and attributes inline.
The **description** can be edited by hand, and — when [Generative AI descriptions](/configuration/genai/genai_objects) are enabled and the object's lifecycle has ended — regenerated from the snapshot or from thumbnails. For `speech` objects, a **Transcribe** action is available when audio transcription is enabled. When [Frigate+](/integrations/plus) is enabled, admins can submit a snapshot to improve their model directly from this pane.
## Actions and bulk selection
Right-clicking or long-pressing an object (in the grid or its thumbnail) opens an actions menu with options to **download** the video, snapshot, or a clean snapshot; **view tracking details**; **find similar**; **add a trigger**; **view in History**; and **delete the tracked object**.
:::note
Deleting a tracked object removes its snapshot, embeddings, and tracking-details entries, but the recorded footage of that object in [History](/usage/history) is **not** deleted.
:::
To act on many objects at once, Ctrl/Cmd-click or right-click to start a selection (selected tiles gain a blue ring), then use the toolbar to select all, clear the selection, or delete (admins).
## Semantic Search - Usage and best practices {#usage-and-best-practices}
1. Semantic Search is used in conjunction with the other filters available on the Explore page. Use a combination of traditional filtering and Semantic Search for the best results.
2. Use the thumbnail search type when searching for particular objects in the scene. Use the description search type when attempting to discern the intent of your object.
3. Because of how the AI models Frigate uses have been trained, the comparison between text and image embedding distances generally means that with multi-modal (`thumbnail` and `description`) searches, results matching `description` will appear first, even if a `thumbnail` embedding may be a better match. Play with the "Search Type" setting to help find what you are looking for. Note that if you are generating descriptions for specific objects or zones only, this may cause search results to prioritize the objects with descriptions even if the the ones without them are more relevant.
4. Make your search language and tone closely match exactly what you're looking for. If you are using thumbnail search, **phrase your query as an image caption**. Searching for "red car" may not work as well as "red sedan driving down a residential street on a sunny day".
5. Semantic search on thumbnails tends to return better results when matching large subjects that take up most of the frame. Small things like "cat" tend to not work well.
6. Experiment! Find a tracked object you want to test and start typing keywords and phrases to see what works for you.
## Triggers
From an object's actions menu, **Add trigger** sets up a per-camera trigger that uses Semantic Search to automate an action (a notification, sub label, or attribute) whenever a similar object appears. Triggers require Semantic Search and are managed under <NavPath path="Settings > Enrichments > Triggers" />. See [Triggers](/configuration/semantic_search#triggers) for full configuration and best practices.
+43
View File
@@ -0,0 +1,43 @@
---
id: exports
title: Exports
---
**Exports** are how you keep a specific piece of footage permanently.
Frigate's recordings are governed by your [retention settings](/configuration/record): once footage ages past its retention window — or, depending on your configuration, once it is only kept where motion, alerts, or detections occurred — it is deleted to free up disk space. An **export** saves a copy of a chosen time range to a separate location that is **never removed by retention**, so it stays available until you delete it yourself.
This is the answer to the common question _"how do I stop Frigate from deleting an important clip?"_ Instead of increasing retention for an entire camera (which uses far more storage to protect a single moment), export just the footage you want to keep.
:::tip
Exports are stored under `/media/frigate/exports`, separate from your recordings, and are not counted against or removed by recording retention. They remain on disk until you delete them, so be aware that they accumulate over time.
:::
## Creating an export
There are a few ways to create an export:
- **From Review** — select (right click or long-press) an individual review item directly, and choose Export from the header menu. You can also select multiple review items and export them all at once, optionally grouping them into a [case](#cases).
- **From History** — open the **Actions** menu and choose **Export**. You can export a preset duration (the last 1, 4, 8, 12, or 24 hours), enter a custom start and end time, or select a range directly on the timeline. A **multi-camera** option lets you export the same time range across several cameras at once.
In every case you can give the export a name. Frigate then saves the footage from your recordings as a single video file. Larger ranges take time to process; the export is marked _in progress_ until it finishes, and you can keep using Frigate while it runs.
## Managing exports
All of your exports live on the **Exports** page, reachable from the main navigation, where you can search for one by name. Each export offers the following actions:
- **Play** it in the browser,
- **Download** it to save the footage outside of Frigate,
- **Share** it — copies a direct link to the export (or uses your device's share sheet),
- **Rename** it, and
- **Delete** it — deleting is the only way an export is removed.
You can also select multiple exports at once to **delete** them in bulk, or to **add them to** (or **remove them from**) a [case](#cases).
## Cases
A **case** groups related exports together — for example, all the clips from a single incident across multiple cameras. On the **Exports** page you can create a case with a name and description, add existing exports to it (or create a new case while exporting), and **download the entire case as a single archive** to hand off as one package.
Exports that don't belong to a case appear under **Uncategorized Exports**. Deleting a case lets you either keep its exports (they move back to uncategorized) or delete them along with the case.
+69
View File
@@ -0,0 +1,69 @@
---
id: history
title: History
---
import NavPath from "@site/src/components/NavPath";
**History** is Frigate's full-resolution recording viewer. Unlike Live, Review, and Explore, there is no menu item for it — you reach it from within another view, then scrub the timeline, switch cameras, inspect a tracked object's lifecycle, and export or share any moment.
This page describes how to _use_ the History view. For how recordings are _configured_ (retention, pre/post capture), see [Recording](/configuration/record).
## Opening History
You can open History from several places:
- **From [Review](/usage/review):** clicking a review item opens its recording, scrubbed to just before the activity on that camera.
- **From [Live](/usage/live):** the **History** button in a camera's single-camera view opens that camera about 30 seconds in the past.
- **From a share link:** opening a shared timestamp link (see [Share Timestamp](#the-actions-menu) below) jumps straight to that camera and moment.
Use the **Back** button to return where you came from, or the **Live** button to jump to the current camera's live view.
## Timeline, Events, and Detail
A toggle (a drawer on mobile) switches the side panel between three modes:
- **Timeline** — a scrubbable vertical timeline of the selected camera, annotated with a motion line, review-item markers, and gaps where no recording exists.
- **Events** — a scrollable list of the camera's review items for the time range; clicking one seeks the player to it.
- **Detail** — the [tracking details inspector](#the-detail-view) for the objects in view.
While you are selecting a range to export, the panel temporarily switches to Timeline.
## Scrubbing and previews
Drag the timeline handlebar to move through time; the main player and any secondary camera previews scrub together so everything stays in sync. Press the zoom buttons on the timeline to change its zoom level (from coarse to fine segments). Sections of the timeline with no recordings are shown as gaps.
On desktop, when more than one camera is available, a **row of secondary previews** shows the other cameras at the same moment. Clicking one of them makes it the main camera at the current timestamp, so you can follow activity across cameras without losing your place. On mobile, use the camera drawer to switch cameras.
## Filtering and the calendar
You can filter History by **cameras** and **date**. The calendar behaves the same as it does in [Review](/usage/review#filtering-and-the-calendar): an **underline** under a day means recordings exist for that day, and a **colored dot** (red for unreviewed alerts, orange for unreviewed detections) marks days with unreviewed activity.
## The Detail view
The **Detail** mode turns the side panel into a tracking details inspector. It lists one card per review item, each showing the item's severity, start time, the object labels involved, a count of tracked objects, and the duration. The active card is highlighted as the video plays, and clicking a card seeks to it.
Expanding a card reveals the **lifecycle** of each tracked object — a row for each significant moment (detected, entered a zone, became active, became stationary, left, and so on), with a progress line that follows the current playback position. Hovering a row shows that moment's score, ratio, and area, and clicking a row seeks the video to that exact timestamp.
The **Detail View Settings** at the bottom let you toggle whether the active item's objects expand automatically, and adjust the **annotation offset** — a fine timing correction that aligns the bounding-box overlays with the recorded video when your camera's snapshot and recording timestamps drift. Admins can save the offset to the camera's configuration.
## The Actions menu
On desktop, the **Actions** menu (the film icon) collects the things you can do with the footage you are viewing:
- **Export** — save a clip of a chosen time range so it is never removed by retention. The dialog pre-selects the last hour; adjust the range or drag the timeline handles, then export. See [Exports](/usage/exports) for managing and downloading exports.
- **Share Timestamp** — generate a link to the current moment (or a custom timestamp) to share with another Frigate user. This is an internal link, not a public share URL.
- **Motion Search** — scan this camera's recordings for changes in a region you draw. This is the same tool documented under [Reviewing Motion](/usage/review#motion-search).
- **Debug Replay** (admins) — replay a recorded range back through Frigate's detection pipeline to see how it would be processed.
You can also capture an instant snapshot of the current frame, and submit a frame to [Frigate+](/integrations/plus) directly from the player (admins only).
## AI review summaries
When [Generative AI review](/configuration/genai/genai_review) is configured, Frigate can generate a title, description, and threat classification for review items and surface them as you scrub through History. A review item that has an AI summary exposes its details in a few places:
- **Over the video** — when the item is on screen, a popup appears over the player.
- **In the Events side panel** — items with a summary show the title below the thumbnail.
- **In the Detail side panel** — the item's card shows the title alongside its tracking details.
Clicking any of these opens the **AI Analysis** dialog with the generated detail and any flagged concerns for that item.
+118
View File
@@ -0,0 +1,118 @@
---
id: live
title: Live View
---
import NavPath from "@site/src/components/NavPath";
**Live view** is Frigate's real-time dashboard and the page you land on by default. It shows all of your cameras at a glance, streams your most recent alerts across the top, and lets you open any camera in a full-resolution single-camera view with audio, two-way talk, PTZ, and on-demand recording controls.
This page describes how to _use_ the Live view. For how to _configure_ live streaming — go2rtc, stream selection, smart streaming, WebRTC, and audio — see the [Live View configuration](/configuration/live) docs.
## The dashboard at a glance
The default **All Cameras** dashboard shows every camera, with a filmstrip of recent **alerts** scrolling across the top. Clicking an alert opens it in [Review](/usage/review); each card also has a check button to mark it reviewed without leaving the dashboard.
By default Frigate uses **smart streaming**: a camera's image updates roughly once per minute while nothing is happening, and switches to a full live stream the moment activity is detected. This conserves bandwidth and resources. You can change this per camera or per group (see [Streaming settings](#streaming-settings-and-the-right-click-menu) below), and the behavior is explained in detail under [Live view technologies](/configuration/live#live-view-technologies).
On mobile, a toggle in the header switches between a **grid** layout and a single-column **list** layout. On desktop a **fullscreen** button is available in the lower-right corner.
## Switching dashboards and camera groups
The icon rail (top-left on desktop, a horizontal strip on mobile) switches between dashboards:
- The **home** icon is the **All Cameras** dashboard, which shows every camera enabled for the dashboard.
- Each **camera group** you create appears as its own icon. Selecting a group shows only that group's cameras.
Camera groups are useful for organizing cameras by location (for example, _Front of House_ or _Backyard_) and for giving each group its own dashboard layout and streaming preferences.
You can also view [Birdseye](/configuration/birdseye) on the dashboard, or open it directly at `http://<frigate_host>:5000/#birdseye`. Clicking a camera inside the Birdseye view jumps to that camera's live feed.
## Creating and editing camera groups
Admins can manage groups from the pencil icon next to the group rail, which opens the **Camera Groups** dialog. From there you can add a group, or edit and delete existing ones. When creating a group you choose:
- a **Name** (spaces are converted to underscores),
- the **cameras** to include — each camera has a toggle and a gear that opens its [streaming settings](#streaming-settings-and-the-right-click-menu), and
- an **icon** used for the group's button in the rail.
Deleting a group also clears any custom layout you saved for it.
## Rearranging a camera group layout
On desktop and tablet, each camera group has its own freely-arrangeable grid. Enter **Edit Layout** mode from the layout button in the lower-right corner: camera tiles gain a drag handle and corner resize handles. Drag a tile to reposition it and drag a corner to resize it (the aspect ratio is preserved). Exit edit mode to save. The layout is stored in your browser per device, so each device can have its own arrangement.
The default **All Cameras** dashboard is not manually arrangeable — it automatically sizes tiles based on each camera's aspect ratio (wide cameras span two columns, tall cameras span two rows).
## Reading the tile indicators
Each camera tile surfaces its current state with a few overlays:
- A **pulsing red dot** in the corner means **motion is currently detected** on that camera.
- A **red outline** around the tile means an **active tracked object** is on that camera.
- A small **label chip** lists the object types currently detected (for example, _Person_, _Car_).
- A **camera-name label** appears when you have enabled always-on camera names, or when a camera is offline or disabled.
- A **Stream Offline** or **Camera is off** placeholder appears when no frames are being received or the camera has been turned off.
You can optionally overlay live streaming statistics (stream type, bandwidth, latency, and frame counts) on a tile to diagnose playback issues.
## Streaming settings and the right-click menu
Right-clicking (or long-pressing) a camera tile opens a context menu with quick controls: an **audio volume** control for streams that support audio, **Mute / Unmute all cameras**, **show or hide streaming statistics**, the **debug view**, **notification** options, and — for admins — turning the camera on or off.
A **Low-bandwidth mode** notice may also appear in the context menu with a **Reset** option appears when Frigate has fallen back to the lower-quality jsmpeg stream — see the [Live view FAQ](/configuration/live#live-view-faq) for why this happens.
For non-default groups, the context menu also exposes **Streaming Settings** for that camera, which let you choose:
- the **stream** to display (the dropdown lists the streams you configured under [`live -> streams`](/configuration/live#setting-streams-for-live-ui), and indicates whether audio is available),
- the **streaming method****No Streaming**, **Smart Streaming** (recommended), or **Continuous Streaming** (higher bandwidth), and
- **compatibility mode**, for devices that have trouble rendering the default player.
These settings are saved per group and per device in your browser, not in your config file.
## The single-camera view
Clicking a camera tile opens its full-resolution single-camera view. The top bar provides:
- **Back** (also the `Esc` key) to return to the dashboard,
- **History** to jump to the [recordings](/usage/history) for this camera, starting about 30 seconds in the past,
- **Fullscreen** and **Picture-in-Picture** (if supported by your browser),
- **Two-way talk** (the microphone button — requires a supported camera and WebRTC; keyboard shortcut `t`), and
- **Camera audio muting** (the speaker button; keyboard shortcut `m`).
You can pinch or scroll to zoom into the feed. A **settings** gear provides a **stream** selector (with audio and two-way-talk availability indicators), **Play in background**, **Show stats**, and a **Debug view** that overlays Frigate's detection regions and bounding boxes.
:::tip
Two-way talk and camera audio have specific codec and port requirements. See [Audio Support](/configuration/live#audio-support) and [WebRTC](/configuration/live#webrtc-extra-configuration) for setup details.
:::
## Camera controls
Admins get a row of toggles in the single-camera view (a settings drawer on mobile) to turn camera features on and off in real time:
- **Camera** on/off,
- **Object detection**,
- **Recording** (only available when recording is enabled in the camera's config),
- **Snapshots**,
- **Audio detection**,
- **Live audio transcription** (when audio detection is enabled), and
- **Autotracking** (for [autotracking-capable PTZ cameras](/configuration/autotracking)).
These toggles change runtime behavior immediately. Whether a change persists across a restart depends on the feature — see the relevant configuration page.
## On-demand recording and snapshots
The single-camera view can capture footage on demand:
- **Start on-demand recording** begins a manual recording based on the camera's recording retention settings (the button pulses while active). If recording is disabled for the camera, only a snapshot is saved. Use **End on-demand recording** to stop.
- **Download instant snapshot** saves a still image of the current frame.
See [Recording](/configuration/record) and [Snapshots](/configuration/snapshots) for how retention is configured, and [Exports](/usage/exports) for keeping a clip permanently.
## PTZ controls
For ONVIF cameras that support it, a control panel provides pan/tilt arrows, **zoom**, **focus**, and saved **presets**. You can also enable a **click-to-move / drag-to-zoom** overlay: click a point in the frame to center the camera there, or drag a box to pan and zoom to that area (dragging top-left to bottom-right zooms in, the reverse zooms out).
For continuous, automatic tracking of a moving object, see [Autotracking](/configuration/autotracking).
+140
View File
@@ -0,0 +1,140 @@
---
id: review
title: Review
---
import NavPath from "@site/src/components/NavPath";
**Review** is where you triage what happened on your cameras. It groups activity into **review items** — segments of time on a single camera that bundle together the objects and audio that were active at once — and sorts them into **Alerts**, **Detections**, and **Motion**. From here you can scrub through activity, mark items as reviewed, filter, export, and jump to the full recording in [History](/usage/history).
This page describes how to _use_ the Review view. For how alerts and detections are _configured_ (labels, zones, required zones, retention), see the [Review configuration](/configuration/review) docs.
:::info
Review items are only created for a camera when **recording is enabled** for that camera. See [Recording](/configuration/record).
:::
## Alerts, Detections, and Motion
Not every segment of video captured by Frigate is of the same level of interest. The people who enter your property may be a higher priority than those just walking by on the sidewalk. For this reason, Frigate sorts **review items** by importance into **alerts** and **detections**, with a separate **Motion** category for significant motion.
The toggle at the top of the page switches between these three severities. One is always selected.
| Tab | Indicator color | What it shows |
| -------------- | --------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Alerts** | dark red | The activity you most want to see. By default, all `person` and `car` tracked objects are alerts. |
| **Detections** | orange | Everything else Frigate tracked that wasn't promoted to an alert. |
| **Motion** | yellow | Periods of significant motion, with the ability to filter to periods which did **not** produce a tracked object. |
This same color coding is used for the ring around a selected item and the dots on the calendar. How an object is categorized as an alert vs. a detection — and how required zones refine that — is covered in [Alerts and Detections](/configuration/review#alerts-and-detections).
The **Alerts** and **Detections** tabs show a count next to their label. With **Show Reviewed** turned off (the default), this is the number of items still left to review; with it on, the count reflects every item in the selected time range.
## Marking items as reviewed
Review items are shown as a grid of thumbnail cards next to a vertical activity timeline. Hovering a card (desktop) or swiping to the right (mobile) plays a short preview inline.
- **Clicking** a card opens its recording in [History](/usage/history) and marks the item as reviewed.
- The object chip on each card is **gray** when the item is unreviewed and turns **green** once it has been reviewed.
- The **Mark these items as reviewed** button marks everything currently shown as reviewed at once.
Reviewed state is tracked per user, so marking an item reviewed does not hide it for other users.
## Selecting and acting on multiple items
To act on several items at once, start a selection by **Ctrl/Cmd-clicking** a card (desktop) or **long-pressing** one (mobile). Selected cards gain a colored ring matching their severity. Keyboard shortcuts speed this up: `Ctrl+A` selects all, `R` marks the selection reviewed, and `Esc` clears it.
With items selected, an action bar appears with options to:
- **Export** the selected items (a single item exports directly; multiple items open the batch [export](/usage/exports) dialog),
- **Mark as reviewed** or **Mark as unreviewed**, and
- **Delete** them (admins only).
## Filtering and the calendar
Use the filter controls in the header to narrow what's shown. The available filters depend on the tab: Alerts and Detections can be filtered by **cameras**, **date**, **labels**, **zones**, and whether items are already reviewed; the Motion tab can be filtered by **cameras**, **date**, and **motion only**.
The **calendar** filter lets you jump to a specific day (it shows **Last 24 Hours** until you pick one). On each day:
- An **underline** under the day number means **recordings exist** for that day. Days without recordings are dimmed.
- A **colored dot** under the day number means there is **unreviewed activity** that day — a **red dot** for unreviewed alerts, or an **orange dot** for unreviewed detections when there are no unreviewed alerts. Motion is not represented by a dot.
Future dates are disabled, and the week start and time zone follow your configuration.
## Reviewing Motion
The Review page also can show periods of motion that didn't produce a tracked object, and provides a way to search past recordings for motion in a specific region. These tools complement the alerts and detections workflow above — see [Tuning Motion Detection](/configuration/motion_detection) for how the underlying motion detector is configured.
The **Motion** tab itself shows a multi-camera grid scrubbed to a shared point in time, with a draggable timeline and a playback-speed selector. A camera tile gains a colored ring when a review item or significant motion overlaps the current time, and clicking a tile opens that camera's recording at that moment. Each camera's options menu (the kebab in the corner of its tile) is where you open **Motion Previews** and **Motion Search**, described below.
### Motion Previews
The Motion Previews pane shows preview clips for periods of significant motion that did not produce a tracked object. It is useful for spotting things that motion detection picked up but object detection did not, which can help validate tuning or catch missed objects.
On the <NavPath path="Review > Motion" /> page, click the kebab menu on a camera and choose **Motion Previews**. Each card represents a continuous range of motion-only activity and plays back the recorded preview for that range. A heatmap overlay dims areas of the frame with no motion so the moving regions stand out.
The pane provides a few controls:
- **Speed** — speeds up or slows down all of the preview clips at once.
- **Dim** — controls how strongly non-motion areas are darkened by the heatmap overlay. Higher values increase motion area visibility.
- **Filter** — opens a 16×16 grid overlaid on a snapshot of the camera. Select one or more cells to only show clips with motion in those regions. This is helpful for filtering out motion in areas like a busy street while keeping motion in your driveway.
Clicking a preview clip seeks the recording player to that timestamp so you can review the full footage.
### Motion Search
Motion Search lets you scan recorded footage for changes inside a region of interest you draw on the camera. Unlike Motion Previews, which surfaces what Frigate's motion detector flagged in real time, Motion Search re-analyzes the saved recordings, so it can find changes that were missed (for example, an object that appeared while motion detection was paused by `lightning_threshold`, or in a region that is normally motion-masked).
To start a search, open the Actions menu in [History](/usage/history) or click the kebab menu on a camera in the <NavPath path="Review > Motion" /> page and choose **Motion Search**. In the dialog:
1. Pick the camera and time range to scan. In the date pickers, days that have recordings available are underlined.
2. Draw a polygon on the camera frame to define the region of interest.
3. Adjust the search parameters if needed:
| Field | Description |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Sensitivity Threshold** | Per-pixel luminance change required to count as motion inside the ROI. Behaves like Frigate's motion detection `threshold` setting. |
| **Minimum Change Area** | Minimum size of a single moving region, as a percentage of the ROI, for a frame to count as significant. Raise it to ignore small movements (leaves, distant motion); lower it when your subject covers only a small slice of the ROI. Every result shows the percentage it scored, so you can use those values to tune this. |
| **Maximum Results** | Maximum number of matching timestamps to return. The search stops once it reaches this many results, so a lower value finishes sooner while a higher value scans further into the range. |
| **Parallel mode** | Decode multiple recording ranges at the same time. Speeds up large time ranges at the cost of higher decoding and CPU usage. |
Motion Search samples each recording's keyframes automatically, so there is no frame-rate or sampling setting to tune.
Once running, Frigate scans the recording segments that overlap the time range and reports timestamps where changes were detected inside the polygon, along with the percentage of the ROI that changed. Clicking a result seeks the player to that moment so you can review what happened.
The results panel shows the time range being scanned, a live progress bar with the timestamp currently being analyzed, and the running result count. A collapsible **Search Metrics** section reports how many segments were scanned and processed, how many were skipped because no motion was recorded in the ROI (using the stored motion heatmap), how many frames were decoded, and the total search time. Skipping segments with no recorded motion in the selected ROI is what makes searching long time ranges practical.
#### Common use cases
Frigate's main use case is to record and surface tracked objects, so Motion Search is most useful for the cases where object detection produced nothing — there is no object to find in Explore, but you suspect something happened.
- **Locating an unattributed change.** You know something appeared, disappeared, or moved in a window of footage — a package now gone, a gate left open — but no detection points to it. A search returns the candidate timestamps instead of scrubbing the timeline by hand.
- **An object that was never detected.** Something Frigate doesn't have a model label for, an object too small or distant to be detected, or movement in a region where detection isn't running. The activity left no tracked object but did change the pixels, so a search can still find it.
- **Activity while detection was effectively paused.** Changes that occurred while object detection was disabled, motion was suppressed by `skip_motion_threshold`, or inside an area covered by a motion mask, won't appear as review items or tracked objects but can be recovered by searching the recordings directly.
#### Examples
These show how to choose the ROI and **Minimum Change Area** for two common goals. Minimum Change Area is the size of a single moving region as a percentage of the ROI you draw, so the right value depends on how much of the ROI your subject — and its movement between samples — covers.
Because samples are a second or more apart, a moving subject usually appears in two places at once in the comparison, so even ordinary motion often scores tens of percent and a low threshold lets in almost everything. The most reliable approach is to **run a search, look at the percentage each result scored, and set Minimum Change Area just below the values for the events you care about.** The default is 20%; the suggestions below are starting points.
- **When did this item first appear (or disappear)?** A package was dropped off, a car parked, or a trash can was moved, and you want the exact moment. Draw a **tight ROI** around the spot the item occupies and **raise Minimum Change Area** (start around 4060%). Because the item fills most of a tight ROI, its arrival or removal is a large change, while smaller nearby motion (shadows, a passing pedestrian) stays below the threshold. The **earliest result** is when it appeared; if you only care about that moment, a low Maximum Results finishes faster. If you get no hits, the ROI is probably looser than the item — lower the threshold or tighten the ROI.
- **What's been getting into the garden?** Something has been trampling a flower bed overnight and no object was ever tracked. Draw a **looser ROI** covering the whole bed and use a **lower Minimum Change Area than the case above** — start near the 20% default and lower it (toward 510%) only if a small or distant subject is missed, since it covers just a slice of a large region. Expect more results to scan through — step through the timestamps and jump to each to see what triggered it. If wind-blown plants add noise, raise Minimum Change Area or the Sensitivity Threshold.
#### Expected performance
Motion Search analyzes the saved recordings on demand rather than reading a pre-built index, so a search over a long range takes longer than browsing Motion Previews. Cost scales mainly with how much footage has to be examined: segments with no recorded motion in your ROI are skipped using the stored motion heatmap (shown as "segments skipped" in the status panel), so a quiet range finishes quickly while a busy one takes longer.
To increase the speed of searches:
- Draw a tight ROI. Because **Minimum Change Area** is measured as a percentage of the region you draw, a tight ROI around where you expect the change makes the object fill a larger share of the area, so it clears the threshold more easily. A loose ROI makes the same object a small fraction of the region, so it can fall below the threshold and be missed — forcing you to lower Minimum Change Area, which lets in more noise.
- Narrow the time range to the window you care about, so there is less footage to examine.
- Lower **Maximum Results** when you only need the first few hits. Because the search stops once it reaches that many results, a smaller value lets a busy range finish early instead of scanning the whole window.
- Use Parallel mode to shorten wall-clock time on multi-core systems, at the cost of higher decoding and CPU usage while it runs.
## AI review summaries
When [Generative AI review](/configuration/genai/genai_review) is configured, Frigate can generate a title, description, and threat classification for review items and surface them automatically in Review and History. Clicking the summary chip opens an **AI Analysis** dialog with the generated detail and any flagged concerns.
In Review, an additional icon appears on unreviewed items that the AI classified as **suspicious** (Level 1) or **critical** (Level 2), so the activity that most warrants attention stands out before you open it. The icon goes away once the item has been reviewed.
+115 -79
View File
@@ -17,91 +17,126 @@ const sidebars: SidebarsConfig = {
],
Guides: [
"guides/getting_started",
"guides/configuring_go2rtc",
"guides/ha_notifications",
"guides/ha_network_storage",
"guides/reverse_proxy",
],
Configuration: {
"Configuration Files": [
"configuration/index",
"configuration/reference",
{
type: "link",
label: "Go2RTC Configuration Reference",
href: "https://github.com/AlexxIT/go2rtc/tree/v1.9.13#configuration",
} as PropSidebarItemLink,
],
Detectors: [
"configuration/object_detectors",
"configuration/audio_detectors",
],
Enrichments: [
"configuration/semantic_search",
"configuration/face_recognition",
"configuration/license_plate_recognition",
"configuration/bird_classification",
{
type: "category",
label: "Custom Classification",
link: {
type: "generated-index",
title: "Custom Classification",
description: "Configuration for custom classification models",
Usage: [
"usage/live",
"usage/review",
"usage/history",
"usage/explore",
"usage/exports",
],
Configuration: [
"configuration/config",
{
type: "category",
label: "Detectors",
items: [
"configuration/object_detectors",
"configuration/audio_detectors",
],
},
{
type: "category",
label: "Enrichments",
items: [
"configuration/semantic_search",
"configuration/face_recognition",
"configuration/license_plate_recognition",
"configuration/bird_classification",
{
type: "category",
label: "Custom Classification",
link: {
type: "generated-index",
title: "Custom Classification",
description: "Configuration for custom classification models",
},
items: [
"configuration/custom_classification/state_classification",
"configuration/custom_classification/object_classification",
],
},
items: [
"configuration/custom_classification/state_classification",
"configuration/custom_classification/object_classification",
],
},
{
type: "category",
label: "Generative AI",
link: {
type: "generated-index",
title: "Generative AI",
description: "Generative AI Features",
{
type: "category",
label: "Generative AI",
link: {
type: "generated-index",
title: "Generative AI",
description: "Generative AI Features",
},
items: [
"configuration/genai/genai_config",
"configuration/genai/genai_review",
"configuration/genai/genai_objects",
],
},
items: [
"configuration/genai/genai_config",
"configuration/genai/genai_review",
"configuration/genai/genai_objects",
],
},
],
Cameras: [
"configuration/cameras",
"configuration/review",
"configuration/record",
"configuration/snapshots",
"configuration/motion_detection",
"configuration/birdseye",
"configuration/live",
"configuration/restream",
"configuration/autotracking",
"configuration/camera_specific",
],
Objects: [
"configuration/object_filters",
"configuration/masks",
"configuration/zones",
"configuration/objects",
"configuration/stationary_objects",
],
"Hardware Acceleration": [
"configuration/hardware_acceleration_video",
"configuration/hardware_acceleration_enrichments",
],
"Extra Configuration": [
"configuration/authentication",
"configuration/notifications",
"configuration/profiles",
"configuration/ffmpeg_presets",
"configuration/pwa",
"configuration/tls",
"configuration/advanced",
],
},
],
},
{
type: "category",
label: "Cameras",
items: [
"configuration/cameras",
"configuration/review",
"configuration/record",
"configuration/snapshots",
"configuration/motion_detection",
"configuration/birdseye",
"configuration/live",
"configuration/restream",
"configuration/autotracking",
"configuration/camera_specific",
],
},
{
type: "category",
label: "Objects",
items: [
"configuration/object_filters",
"configuration/masks",
"configuration/zones",
"configuration/objects",
"configuration/stationary_objects",
],
},
{
type: "category",
label: "Hardware Acceleration",
items: [
"configuration/hardware_acceleration_video",
"configuration/hardware_acceleration_enrichments",
],
},
{
type: "category",
label: "Extra Configuration",
items: [
"configuration/authentication",
"configuration/notifications",
"configuration/profiles",
"configuration/go2rtc",
"configuration/ffmpeg_presets",
"configuration/pwa",
"configuration/tls",
],
},
{
type: "category",
label: "Advanced Configuration",
items: [
"configuration/advanced/system",
"configuration/advanced/reference",
{
type: "link",
label: "Go2RTC Configuration Reference",
href: "https://github.com/AlexxIT/go2rtc/tree/v1.9.13#configuration",
} as PropSidebarItemLink,
],
},
],
Integrations: [
"integrations/plus",
"integrations/home-assistant",
@@ -130,6 +165,7 @@ const sidebars: SidebarsConfig = {
],
Troubleshooting: [
"troubleshooting/faqs",
"troubleshooting/go2rtc",
"troubleshooting/recordings",
"troubleshooting/dummy-camera",
{
+39 -7
View File
@@ -400,6 +400,35 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/HTTPValidationError"
/keyframe_analysis:
get:
tags:
- Camera
summary: Keyframe Analysis
description: >-
Probe a camera's record stream and classify its keyframe spacing.
Detects smart/+ codecs and long/variable GOPs that degrade recording.
operationId: keyframe_analysis_keyframe_analysis_get
parameters:
- name: camera
in: query
required: false
schema:
type: string
default: ""
title: Camera
responses:
"200":
description: Successful Response
content:
application/json:
schema: {}
"422":
description: Validation Error
content:
application/json:
schema:
$ref: "#/components/schemas/HTTPValidationError"
/ffprobe/snapshot:
get:
tags:
@@ -7288,13 +7317,6 @@ components:
title: Min Area
description: Minimum change area as a percentage of the ROI
default: 5
frame_skip:
type: integer
maximum: 30
minimum: 1
title: Frame Skip
description: "Process every Nth frame (1=all frames, 5=every 5th frame)"
default: 5
parallel:
type: boolean
title: Parallel
@@ -7380,6 +7402,16 @@ components:
anyOf:
- $ref: "#/components/schemas/MotionSearchMetricsResponse"
- type: "null"
scanning_timestamp:
anyOf:
- type: number
- type: "null"
title: Scanning Timestamp
progress:
anyOf:
- type: number
- type: "null"
title: Progress
type: object
required:
- success
+124 -44
View File
@@ -34,11 +34,15 @@ from frigate.config.camera.updater import (
)
from frigate.config.env import substitute_frigate_vars
from frigate.models import User
from frigate.util.builtin import clean_camera_user_pass
from frigate.util.builtin import clean_camera_user_pass, get_record_segment_time
from frigate.util.camera_cleanup import cleanup_camera_db, cleanup_camera_files
from frigate.util.config import find_config_file
from frigate.util.image import run_ffmpeg_snapshot
from frigate.util.services import ffprobe_stream, is_restricted_go2rtc_source
from frigate.util.services import (
analyze_record_keyframes,
ffprobe_stream,
is_restricted_go2rtc_source,
)
logger = logging.getLogger(__name__)
@@ -362,6 +366,48 @@ def ffprobe(request: Request, paths: str = "", detailed: bool = False):
return JSONResponse(content=output)
@router.get("/keyframe_analysis", dependencies=[Depends(require_role(["admin"]))])
async def keyframe_analysis(request: Request, camera: str = ""):
"""Probe a camera's record stream and classify its keyframe spacing.
Detects smart/+ codecs and long/variable GOPs that degrade recording.
"""
config: FrigateConfig = request.app.frigate_config
if camera not in config.cameras:
return JSONResponse(
content={"success": False, "message": f"{camera} is not a valid camera."},
status_code=404,
)
camera_config = config.cameras[camera]
if not camera_config.enabled:
return JSONResponse(
content={"success": False, "message": f"{camera} is not enabled."},
status_code=404,
)
# keyframe spacing only matters when this camera is recording
if not camera_config.record.enabled:
return JSONResponse(content={"severity": "record_disabled"})
# recording guarantees an input carries the record role; its index matches
# the "Stream N" numbering the ffprobe endpoint surfaces (same input order)
record_index, record_input = next(
(idx, i)
for idx, i in enumerate(camera_config.ffmpeg.inputs)
if "record" in i.roles
)
segment_time = get_record_segment_time(camera_config)
result = await analyze_record_keyframes(
config.ffmpeg, record_input.path, segment_time
)
result["stream_index"] = record_index
return JSONResponse(content=result)
@router.get("/ffprobe/snapshot", dependencies=[Depends(require_role(["admin"]))])
def ffprobe_snapshot(request: Request, url: str = "", timeout: int = 10):
"""Get a snapshot from a stream URL using ffmpeg."""
@@ -529,6 +575,68 @@ def _extract_fps(r_frame_rate: str) -> float | None:
return None
def _build_digest_transport(username: str, password: str) -> AsyncTransport:
"""Build a zeep transport backed by an httpx client using HTTP digest auth."""
auth = httpx.DigestAuth(username, password)
client = httpx.AsyncClient(auth=auth, timeout=10.0)
return AsyncTransport(client=client)
async def _connect_onvif_camera(
host: str,
port: int,
username: str,
password: str,
wsdl_base: str | None,
auth_type: str,
) -> ONVIFCamera:
"""Connect to an ONVIF device, trying both WS-Security password encodings.
Cameras disagree on whether the WS-Security UsernameToken should carry a
hashed PasswordDigest or a plaintext PasswordText. The wizard can't know
which a given camera expects, so we try PasswordDigest first (the common
case) and fall back to PasswordText when the device rejects the token. This
is independent of auth_type, which controls HTTP transport-level auth.
"""
first_error: Fault | None = None
# encrypt=True -> PasswordDigest, encrypt=False -> PasswordText
for encrypt in (True, False):
onvif_camera = ONVIFCamera(
host,
port,
username or "",
password or "",
wsdl_dir=wsdl_base,
encrypt=encrypt,
)
try:
await onvif_camera.update_xaddrs()
except Fault as e:
# A SOAP fault here is how a camera signals the wrong password
# encoding, so retry with the other encoding before giving up.
logger.debug(
"ONVIF connect with %s rejected, trying alternate encoding",
"PasswordDigest" if encrypt else "PasswordText",
)
if first_error is None:
first_error = e
continue
if auth_type == "digest" and username and password:
transport = _build_digest_transport(username, password)
for service in ("devicemgmt", "media", "ptz"):
if hasattr(onvif_camera, service):
getattr(onvif_camera, service).zeep_client.transport = transport
logger.debug("Configured digest authentication")
return onvif_camera
# Both encodings failed authentication; surface the original fault.
raise first_error
@router.get(
"/onvif/probe",
dependencies=[Depends(require_role(["admin"]))],
@@ -605,34 +713,10 @@ async def onvif_probe(
except Exception:
wsdl_base = None
onvif_camera = ONVIFCamera(
host, port, username or "", password or "", wsdl_dir=wsdl_base
onvif_camera = await _connect_onvif_camera(
host, port, username, password, wsdl_base, auth_type
)
# Configure digest authentication if requested
if auth_type == "digest" and username and password:
# Create httpx client with digest auth
auth = httpx.DigestAuth(username, password)
client = httpx.AsyncClient(auth=auth, timeout=10.0)
# Replace the transport in the zeep client
transport = AsyncTransport(client=client)
# Update the xaddr before setting transport
await onvif_camera.update_xaddrs()
# Replace transport in all services
if hasattr(onvif_camera, "devicemgmt"):
onvif_camera.devicemgmt.zeep_client.transport = transport
if hasattr(onvif_camera, "media"):
onvif_camera.media.zeep_client.transport = transport
if hasattr(onvif_camera, "ptz"):
onvif_camera.ptz.zeep_client.transport = transport
logger.debug("Configured digest authentication")
else:
await onvif_camera.update_xaddrs()
# Get device information
device_info = {
"manufacturer": "Unknown",
@@ -644,10 +728,9 @@ async def onvif_probe(
# Update transport for device service if digest auth
if auth_type == "digest" and username and password:
auth = httpx.DigestAuth(username, password)
client = httpx.AsyncClient(auth=auth, timeout=10.0)
transport = AsyncTransport(client=client)
device_service.zeep_client.transport = transport
device_service.zeep_client.transport = _build_digest_transport(
username, password
)
device_info_resp = await device_service.GetDeviceInformation()
manufacturer = getattr(device_info_resp, "Manufacturer", None) or (
@@ -685,10 +768,9 @@ async def onvif_probe(
# Update transport for media service if digest auth
if auth_type == "digest" and username and password:
auth = httpx.DigestAuth(username, password)
client = httpx.AsyncClient(auth=auth, timeout=10.0)
transport = AsyncTransport(client=client)
media_service.zeep_client.transport = transport
media_service.zeep_client.transport = _build_digest_transport(
username, password
)
profiles = await media_service.GetProfiles()
profiles_count = len(profiles) if profiles else 0
@@ -720,10 +802,9 @@ async def onvif_probe(
# Update transport for PTZ service if digest auth
if auth_type == "digest" and username and password:
auth = httpx.DigestAuth(username, password)
client = httpx.AsyncClient(auth=auth, timeout=10.0)
transport = AsyncTransport(client=client)
ptz_service.zeep_client.transport = transport
ptz_service.zeep_client.transport = _build_digest_transport(
username, password
)
# Check if PTZ service is available
try:
@@ -876,10 +957,9 @@ async def onvif_probe(
# Update transport for media service if digest auth
if auth_type == "digest" and username and password:
auth = httpx.DigestAuth(username, password)
client = httpx.AsyncClient(auth=auth, timeout=10.0)
transport = AsyncTransport(client=client)
media_service.zeep_client.transport = transport
media_service.zeep_client.transport = _build_digest_transport(
username, password
)
if profiles_count and media_service:
for p in profiles or []:
+5 -7
View File
@@ -41,12 +41,6 @@ class MotionSearchRequest(BaseModel):
le=100.0,
description="Minimum change area as a percentage of the ROI",
)
frame_skip: int = Field(
default=5,
ge=1,
le=30,
description="Process every Nth frame (1=all frames, 5=every 5th frame)",
)
parallel: bool = Field(
default=False,
description="Enable parallel scanning across segments",
@@ -97,6 +91,8 @@ class MotionSearchStatusResponse(BaseModel):
total_frames_processed: Optional[int] = None
error_message: Optional[str] = None
metrics: Optional[MotionSearchMetricsResponse] = None
scanning_timestamp: Optional[float] = None
progress: Optional[float] = None
@router.post(
@@ -151,7 +147,6 @@ async def start_motion_search(
polygon_points=body.polygon_points,
threshold=body.threshold,
min_area=body.min_area,
frame_skip=body.frame_skip,
parallel=body.parallel,
max_results=body.max_results,
)
@@ -231,6 +226,9 @@ async def get_motion_search_status_endpoint(
if job.metrics:
response_content["metrics"] = job.metrics.to_dict()
response_content["scanning_timestamp"] = job.scanning_timestamp
response_content["progress"] = job.progress
return JSONResponse(content=response_content)
+20 -6
View File
@@ -299,22 +299,36 @@ async def no_recordings(
.iterator()
)
# Convert recordings to list of (start, end) tuples
# Convert recordings to list of (start, end) tuples, ordered by start_time
recordings = [(r["start_time"], r["end_time"]) for r in data]
# Merge overlapping/adjacent recordings into covered intervals. The query
# orders by start_time, so a single pass merges them
covered: list[tuple[float, float]] = []
for rec_start, rec_end in recordings:
if covered and rec_start <= covered[-1][1]:
covered[-1] = (covered[-1][0], max(covered[-1][1], rec_end))
else:
covered.append((rec_start, rec_end))
# Iterate through time segments and check if each has any recording
no_recording_segments = []
current = after
current_gap_start = None
idx = 0
covered_count = len(covered)
while current < before:
segment_end = min(current + scale, before)
# Check if this segment overlaps with any recording
has_recording = any(
rec_start < segment_end and rec_end > current
for rec_start, rec_end in recordings
)
# Advance past covered intervals that end before this segment begins;
# they cannot overlap this or any later segment.
while idx < covered_count and covered[idx][1] <= current:
idx += 1
# A covered interval overlaps the segment when it starts before the
# segment ends (its end is already known to be > current).
has_recording = idx < covered_count and covered[idx][0] < segment_end
if not has_recording:
# This segment has no recordings
+14 -10
View File
@@ -605,9 +605,10 @@ def motion_activity(
if not filtered:
return JSONResponse(content=[])
camera_list = list(filtered)
clauses.append((Recordings.camera << camera_list))
else:
clauses.append((Recordings.camera << allowed_cameras))
camera_list = list(allowed_cameras)
clauses.append((Recordings.camera << camera_list))
data: list[Recordings] = (
Recordings.select(
@@ -635,14 +636,12 @@ def motion_activity(
df.set_index(["start_time"], inplace=True)
# normalize data
motion = (
df["motion"]
.resample(f"{scale}s")
.apply(lambda x: max(x, key=abs, default=0.0))
.fillna(0.0)
.to_frame()
)
cameras = df["camera"].resample(f"{scale}s").agg(lambda x: ",".join(set(x)))
motion = df["motion"].resample(f"{scale}s").max().fillna(0.0).to_frame()
if len(camera_list) == 1:
cameras = df["camera"].resample(f"{scale}s").first().fillna("")
else:
cameras = df["camera"].resample(f"{scale}s").agg(lambda x: ",".join(set(x)))
df = motion.join(cameras)
length = df.shape[0]
@@ -658,6 +657,11 @@ def motion_activity(
else:
df.iloc[i : i + chunk, 0] = 0.0
# Drop resample gap-fill buckets. The resample above emits a row for every
# {scale}s bucket spanning the range, and buckets with no recording get a
# motion of 0 (from fillna) and an empty camera (from joining an empty set).
df = df[df["camera"] != ""]
# change types for output
df.index = df.index.astype(int) // (10**9)
normalized = df.reset_index().to_dict("records")
+2 -2
View File
@@ -9,7 +9,7 @@ from ..base import FrigateBaseModel
__all__ = ["AudioConfig", "AudioFilterConfig"]
DEFAULT_LISTEN_AUDIO = ["bark", "fire_alarm", "scream", "speech", "yell"]
DEFAULT_LISTEN_AUDIO = ["bark", "fire_alarm", "speech", "yell"]
class AudioFilterConfig(FrigateBaseModel):
@@ -41,7 +41,7 @@ class AudioConfig(FrigateBaseModel):
listen: list[str] = Field(
default=DEFAULT_LISTEN_AUDIO,
title="Listen types",
description="List of audio event types to detect (for example: bark, fire_alarm, scream, speech, yell).",
description="List of audio event types to detect (for example: bark, fire_alarm, speech, yell).",
)
filters: Optional[dict[str, AudioFilterConfig]] = Field(
None,
+1 -1
View File
@@ -146,7 +146,7 @@ class CameraConfig(FrigateBaseModel):
timestamp_style: TimestampStyleConfig = Field(
default_factory=TimestampStyleConfig,
title="Timestamp style",
description="Styling options for in-feed timestamps applied to recordings and snapshots.",
description="Styling options for timestamps applied to snapshots and Debug view.",
)
# Options without global fallback
+4 -14
View File
@@ -3,7 +3,7 @@ from typing import Union
from pydantic import Field, field_validator
from frigate.const import DEFAULT_FFMPEG_VERSION, INCLUDED_FFMPEG_VERSIONS
from frigate.util.config import resolve_ffmpeg_path
from ..base import FrigateBaseModel
from ..env import EnvString
@@ -49,7 +49,7 @@ class FfmpegConfig(FrigateBaseModel):
path: str = Field(
default="default",
title="FFmpeg path",
description='Path to the FFmpeg binary to use or a version alias ("5.0" or "7.0").',
description='Path to the FFmpeg binary to use or a version alias ("7.0" or "8.0").',
)
global_args: Union[str, list[str]] = Field(
default=FFMPEG_GLOBAL_ARGS_DEFAULT,
@@ -90,21 +90,11 @@ class FfmpegConfig(FrigateBaseModel):
@property
def ffmpeg_path(self) -> str:
if self.path == "default":
return f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg"
elif self.path in INCLUDED_FFMPEG_VERSIONS:
return f"/usr/lib/ffmpeg/{self.path}/bin/ffmpeg"
else:
return f"{self.path}/bin/ffmpeg"
return resolve_ffmpeg_path(self.path, "ffmpeg")
@property
def ffprobe_path(self) -> str:
if self.path == "default":
return f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffprobe"
elif self.path in INCLUDED_FFMPEG_VERSIONS:
return f"/usr/lib/ffmpeg/{self.path}/bin/ffprobe"
else:
return f"{self.path}/bin/ffprobe"
return resolve_ffmpeg_path(self.path, "ffprobe")
class CameraRoleEnum(str, Enum):
+5
View File
@@ -16,3 +16,8 @@ class CameraUiConfig(FrigateBaseModel):
title="Show in UI",
description="Toggle whether this camera is visible everywhere in the Frigate UI. Disabling this will require manually editing the config to view this camera in the UI again.",
)
review: bool = Field(
default=True,
title="Show in review",
description="Toggle whether this camera is visible in review (the review page and its camera filter, motion review, and the history view).",
)
+6 -1
View File
@@ -73,7 +73,12 @@ class CameraConfigUpdateSubscriber:
base_topic = "config/cameras"
if len(self.camera_configs) == 1:
# global subscribers must hear every camera; only narrow per-camera workers
is_global_subscriber = (
CameraConfigUpdateEnum.add in self.topics
or CameraConfigUpdateEnum.remove in self.topics
)
if not is_global_subscriber and len(self.camera_configs) == 1:
base_topic += f"/{list(self.camera_configs.keys())[0]}"
self.subscriber = ConfigSubscriber(
+49 -1
View File
@@ -5,7 +5,7 @@ import json
import logging
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
from typing import Any, Callable, Optional
from frigate.config.camera.updater import (
CameraConfigUpdateEnum,
@@ -34,6 +34,45 @@ PROFILE_SECTION_UPDATES: dict[str, CameraConfigUpdateEnum] = {
"zones": CameraConfigUpdateEnum.zones,
}
# Retained MQTT switch topics per profile section, with a payload getter.
# Republished on profile change so MQTT/HA don't show a stale toggle.
SECTION_STATE_TOPICS: dict[str, list[tuple[str, Callable[[Any], Any]]]] = {
"audio": [("audio", lambda c: "ON" if c.audio.enabled else "OFF")],
"birdseye": [
("birdseye", lambda c: "ON" if c.birdseye.enabled else "OFF"),
(
"birdseye_mode",
lambda c: c.birdseye.mode.value.upper() if c.birdseye.enabled else "OFF",
),
],
"detect": [("detect", lambda c: "ON" if c.detect.enabled else "OFF")],
"motion": [
("motion", lambda c: "ON" if c.motion.enabled else "OFF"),
("improve_contrast", lambda c: "ON" if c.motion.improve_contrast else "OFF"),
("motion_threshold", lambda c: c.motion.threshold),
("motion_contour_area", lambda c: c.motion.contour_area),
],
"notifications": [
("notifications", lambda c: "ON" if c.notifications.enabled else "OFF"),
],
"objects": [
("object_descriptions", lambda c: "ON" if c.objects.genai.enabled else "OFF"),
],
"record": [("recordings", lambda c: "ON" if c.record.enabled else "OFF")],
"review": [
("review_alerts", lambda c: "ON" if c.review.alerts.enabled else "OFF"),
(
"review_detections",
lambda c: "ON" if c.review.detections.enabled else "OFF",
),
(
"review_descriptions",
lambda c: "ON" if c.review.genai.enabled else "OFF",
),
],
"snapshots": [("snapshots", lambda c: "ON" if c.snapshots.enabled else "OFF")],
}
PERSISTENCE_FILE = Path(CONFIG_DIR) / ".profiles"
@@ -310,6 +349,15 @@ class ProfileManager:
settings,
)
# republish MQTT switch states
if self.dispatcher is not None:
for suffix, get_payload in SECTION_STATE_TOPICS.get(section, ()):
self.dispatcher.publish(
f"{cam_name}/{suffix}/state",
get_payload(cam_config),
retain=True,
)
def _persist_active_profile(self, profile_name: Optional[str]) -> None:
"""Persist the active profile state to disk as JSON."""
try:
+1 -1
View File
@@ -45,7 +45,7 @@ class ProxyConfig(FrigateBaseModel):
default_role: Optional[str] = Field(
default="viewer",
title="Default role",
description="Default role assigned to proxy-authenticated users when no role mapping applies (admin or viewer).",
description="Default role assigned to proxy-authenticated users when no role mapping applies.",
)
separator: Optional[str] = Field(
default=",",
+1 -18
View File
@@ -5,7 +5,7 @@ from pydantic import Field
from .base import FrigateBaseModel
__all__ = ["TimeFormatEnum", "DateTimeStyleEnum", "UnitSystemEnum", "UIConfig"]
__all__ = ["TimeFormatEnum", "UnitSystemEnum", "UIConfig"]
class TimeFormatEnum(str, Enum):
@@ -14,13 +14,6 @@ class TimeFormatEnum(str, Enum):
hours24 = "24hour"
class DateTimeStyleEnum(str, Enum):
full = "full"
long = "long"
medium = "medium"
short = "short"
class UnitSystemEnum(str, Enum):
imperial = "imperial"
metric = "metric"
@@ -37,16 +30,6 @@ class UIConfig(FrigateBaseModel):
title="Time format",
description="Time format to use in the UI (browser, 12hour, or 24hour).",
)
date_style: DateTimeStyleEnum = Field(
default=DateTimeStyleEnum.short,
title="Date style",
description="Date style to use in the UI (full, long, medium, short).",
)
time_style: DateTimeStyleEnum = Field(
default=DateTimeStyleEnum.medium,
title="Time style",
description="Time style to use in the UI (full, long, medium, short).",
)
unit_system: UnitSystemEnum = Field(
default=UnitSystemEnum.metric,
title="Unit system",
+16 -13
View File
@@ -15,6 +15,9 @@ from frigate.util.rknn_converter import auto_convert_model, is_rknn_compatible
logger = logging.getLogger(__name__)
# Process-wide lock serializing all OpenVINO compile/inference calls
_OPENVINO_LOCK = threading.Lock()
def is_arm64_platform() -> bool:
"""Check if we're running on an ARM platform."""
@@ -326,19 +329,17 @@ class OpenVINOModelRunner(BaseModelRunner):
except Exception as e:
logger.debug(f"NPU_TURBO not supported by driver: {e}")
# Compile model
self.compiled_model = self.ov_core.compile_model(
model=model_path, device_name=device
)
# Compile model under the shared lock
with _OPENVINO_LOCK:
self.compiled_model = self.ov_core.compile_model(
model=model_path, device_name=device
)
# Create reusable inference request
self.infer_request = self.compiled_model.create_infer_request()
# Create reusable inference request
self.infer_request = self.compiled_model.create_infer_request()
self.input_tensor: ov.Tensor | None = None
# Thread lock to prevent concurrent inference (needed for JinaV2 which shares
# one runner between text and vision embeddings called from different threads)
self._inference_lock = threading.Lock()
if not self.complex_model:
try:
input_shape = self.compiled_model.inputs[0].get_shape()
@@ -382,9 +383,11 @@ class OpenVINOModelRunner(BaseModelRunner):
Returns:
List of output tensors
"""
# Lock prevents concurrent access to infer_request
# Needed for JinaV2: genai thread (text) + embeddings thread (vision)
with self._inference_lock:
# Shared lock serializes inference across every OpenVINO runner in this
# process — both the shared-runner JinaV2 case (genai text thread +
# embeddings vision thread) and distinct runners running on separate
# threads (e.g. the ArcFace face-model build vs the LPR detector).
with _OPENVINO_LOCK:
from frigate.embeddings.types import EnrichmentModelTypeEnum
if self.model_type in [EnrichmentModelTypeEnum.arcface.value]:
+1 -13
View File
@@ -465,16 +465,6 @@ PRESETS_RECORD_OUTPUT = {
"-c:a",
"aac",
],
# NOTE: This preset originally used "-c:a copy" to pass through audio
# without re-encoding. FFmpeg 7.x introduced a threaded pipeline where
# demuxing, encoding, and muxing run in parallel via a Scheduler. This
# broke audio streamcopy from RTSP sources: packets are demuxed correctly
# but silently dropped before reaching the muxer (0 bytes written). The
# issue is specific to RTSP + streamcopy; file inputs and transcoding both
# work. Transcoding AAC audio is very lightweight (~30KiB per 10s segment)
# and adds negligible CPU overhead, so this is an acceptable workaround.
# The benefits of FFmpeg 7.x — particularly the removal of gamma correction
# hacks required by earlier versions — outweigh this trade-off.
"preset-record-generic-audio-copy": [
"-f",
"segment",
@@ -486,10 +476,8 @@ PRESETS_RECORD_OUTPUT = {
"1",
"-strftime",
"1",
"-c:v",
"-c",
"copy",
"-c:a",
"aac",
],
"preset-record-mjpeg": [
"-f",
+413 -329
View File
@@ -3,6 +3,8 @@
import logging
import os
import threading
import time
from collections.abc import Callable, Generator, Iterable
from concurrent.futures import Future, ThreadPoolExecutor, as_completed
from dataclasses import asdict, dataclass, field
from datetime import datetime
@@ -19,6 +21,18 @@ from frigate.jobs.manager import (
get_job_by_id,
set_current_job,
)
from frigate.jobs.motion_search_batch import (
build_segment_time_map,
coalesce_runs,
stream_time_to_absolute,
)
from frigate.jobs.motion_search_decode import (
iter_vod_frames,
keyframe_sampling_eligible,
probe_video_dimensions,
probe_vod_keyframe_pts,
resolve_motion_decode_args,
)
from frigate.models import Recordings
from frigate.types import JobStatusTypesEnum
@@ -26,6 +40,18 @@ logger = logging.getLogger(__name__)
# Constants
HEATMAP_GRID_SIZE = 16
# Max wall-clock span of one VOD run request (seconds). Bounds per-request size
# and gives streaming/cancel/early-exit granularity.
MAX_RUN_SECONDS = 600.0
# Treat segments within this many seconds end-to-start as time-contiguous.
RUN_GAP_EPSILON = 1.0
# Longest-side pixels for the ROI downscale before motion detection.
SCALE_TARGET = 400
# Minimum wall seconds between intra-run progress broadcasts.
PROGRESS_BROADCAST_INTERVAL = 1.0
# Output frame rate for the fixed-cadence fallback used on long-GOP cameras
# (where keyframe sampling is too sparse). Keyframe cameras ignore this.
FALLBACK_SAMPLE_FPS = 2.0
@dataclass
@@ -69,13 +95,16 @@ class MotionSearchJob(Job):
polygon_points: list[list[float]] = field(default_factory=list)
threshold: int = 30
min_area: float = 5.0
frame_skip: int = 5
parallel: bool = False
max_results: int = 25
# Track progress
total_frames_processed: int = 0
# Live progress (ride the existing to_dict() websocket broadcast)
scanning_timestamp: Optional[float] = None
progress: float = 0.0
# Metrics for observability
metrics: Optional[MotionSearchMetrics] = None
@@ -100,6 +129,113 @@ def create_polygon_mask(
return mask
def compute_roi_crop_and_scale(
polygon_points: list[list[float]],
frame_width: int,
frame_height: int,
scale_target: int,
) -> tuple[tuple[int, int, int, int], tuple[int, int]]:
"""Compute the ROI crop box and never-upscale scaled dimensions.
Returns ((crop_w, crop_h, crop_x, crop_y), (scaled_w, scaled_h)) in pixels.
The crop is the polygon's bounding box in frame pixels; the scaled size fits
the crop's longest side to ``scale_target`` without ever enlarging it.
"""
xs = [p[0] for p in polygon_points]
ys = [p[1] for p in polygon_points]
# nv12 (4:2:0) hwdownload requires even crop offsets and even crop/scale
# dimensions; otherwise ffmpeg rounds the chroma planes and the raw byte
# stream stops matching the expected frame size. Force even values, and the
# mask is built from these same values so the two stay aligned.
crop_x = int(min(xs) * frame_width)
crop_y = int(min(ys) * frame_height)
crop_x -= crop_x % 2
crop_y -= crop_y % 2
crop_w = max(2, int(max(xs) * frame_width) - crop_x)
crop_h = max(2, int(max(ys) * frame_height) - crop_y)
crop_w -= crop_w % 2
crop_h -= crop_h % 2
longest = max(crop_w, crop_h)
factor = min(1.0, scale_target / longest)
scaled_w = max(2, round(crop_w * factor))
scaled_h = max(2, round(crop_h * factor))
scaled_w -= scaled_w % 2
scaled_h -= scaled_h % 2
return (crop_w, crop_h, crop_x, crop_y), (scaled_w, scaled_h)
def build_scaled_roi_mask(
polygon_points: list[list[float]],
frame_width: int,
frame_height: int,
crop: tuple[int, int, int, int],
scaled: tuple[int, int],
) -> np.ndarray:
"""Rasterize the polygon mask at the scaled ROI size.
Builds the full-resolution mask, crops it to the ROI box, and nearest-
neighbor resizes it to the scaled dimensions so it lines up exactly with the
frames ffmpeg crops and scales.
"""
crop_w, crop_h, crop_x, crop_y = crop
scaled_w, scaled_h = scaled
full_mask = create_polygon_mask(polygon_points, frame_width, frame_height)
cropped = full_mask[crop_y : crop_y + crop_h, crop_x : crop_x + crop_w]
return cv2.resize(cropped, (scaled_w, scaled_h), interpolation=cv2.INTER_NEAREST)
def detect_motion_scaled(
frames: Iterable[tuple[int, np.ndarray]],
mask: np.ndarray,
threshold: int,
min_area: float,
timestamp_fn: Callable[[int], float],
) -> list[MotionSearchResult]:
"""Detect motion across pre-cropped, pre-scaled gray frames.
``frames`` yields (absolute_frame_index, gray_roi_frame); ``mask`` is the
scaled ROI mask. ``min_area`` is a percentage of the masked ROI. Mirrors the
full-res detection math (absdiff -> blur -> threshold -> dilate -> contours)
on the already-reduced frames.
"""
results: list[MotionSearchResult] = []
mask_area = np.count_nonzero(mask)
if mask_area == 0:
return results
min_area_pixels = int((min_area / 100.0) * mask_area)
prev: np.ndarray | None = None
for frame_idx, gray in frames:
masked = cv2.bitwise_and(gray, gray, mask=mask)
if prev is not None:
diff = cv2.absdiff(prev, masked)
diff_blurred = cv2.GaussianBlur(diff, (3, 3), 0)
_, thresh = cv2.threshold(diff_blurred, threshold, 255, cv2.THRESH_BINARY)
thresh_dilated = cv2.dilate(thresh, None, iterations=1) # type: ignore[call-overload]
thresh_masked = cv2.bitwise_and(thresh_dilated, thresh_dilated, mask=mask)
change_pixels = cv2.countNonZero(thresh_masked)
if change_pixels > min_area_pixels:
contours, _ = cv2.findContours(
thresh_masked, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
total_change_area = sum(
cv2.contourArea(c)
for c in contours
if cv2.contourArea(c) >= min_area_pixels
)
if total_change_area > 0:
change_percentage = (total_change_area / mask_area) * 100
results.append(
MotionSearchResult(
timestamp=timestamp_fn(frame_idx),
change_percentage=round(change_percentage, 2),
)
)
prev = masked
return results
def compute_roi_bbox_normalized(
polygon_points: list[list[float]],
) -> tuple[float, float, float, float]:
@@ -184,6 +320,22 @@ def segment_passes_heatmap_gate(
return heatmap_overlaps_roi(heatmap, roi_bbox)
def resolve_internal_port(config: FrigateConfig) -> int:
"""Return the unauthenticated internal nginx port for VOD requests."""
listen = config.networking.listen.internal
if isinstance(listen, str):
return int(listen.split(":")[-1])
return int(listen)
def build_vod_url(internal_port: int, camera: str, start: float, end: float) -> str:
"""Build the internal VOD HLS URL for a camera time range."""
return (
f"http://127.0.0.1:{internal_port}/vod/{camera}"
f"/start/{start}/end/{end}/index.m3u8"
)
class MotionSearchRunner(threading.Thread):
"""Thread-based runner for motion search jobs with parallel verification."""
@@ -206,6 +358,23 @@ class MotionSearchRunner(threading.Thread):
cpu_count = os.cpu_count() or 1
self.max_workers = min(4, cpu_count)
# Resolved once per job in _execute_search
self.ffmpeg_path: str = "ffmpeg"
self.ffprobe_path: str = "ffprobe"
self.decode_args: list[str] = []
# Keyframe sampling decision, decided once per job from the first run's
# GOP. The fallback cadence is a fixed rate (see FALLBACK_SAMPLE_FPS).
self.use_keyframe: bool = True
self.fps_rate: float = FALLBACK_SAMPLE_FPS
# ROI crop/scale + scaled mask, computed once from the VOD-stream
# dimensions (which can differ from the detect resolution).
self.crop: tuple[int, int, int, int] = (0, 0, 0, 0)
self.scaled: tuple[int, int] = (0, 0)
self.scaled_mask: np.ndarray = np.zeros((0, 0), dtype=np.uint8)
self.channels: int = 1
self.internal_port: int = 5000
self._last_progress_broadcast: float = 0.0
def run(self) -> None:
"""Execute the motion search job."""
try:
@@ -281,6 +450,9 @@ class MotionSearchRunner(threading.Thread):
if frame_width is None or frame_height is None:
raise ValueError(f"Camera {camera_name} detect dimensions not configured")
self.ffmpeg_path = camera_config.ffmpeg.ffmpeg_path
self.ffprobe_path = camera_config.ffmpeg.ffprobe_path
# Create polygon mask
polygon_mask = create_polygon_mask(
self.job.polygon_points, frame_width, frame_height
@@ -384,205 +556,274 @@ class MotionSearchRunner(threading.Thread):
self.metrics.heatmap_roi_skip_segments,
)
if self.job.parallel:
return self._search_motion_parallel(filtered_recordings, polygon_mask)
# Resolve decode backend (allowlisted hwaccel or software), coalesce the
# gate-passing segments into time-contiguous runs, and probe the first
# run's VOD stream once for dimensions + keyframe layout. VOD output is
# what we decode, so crop/scale/mask are computed against it.
self.internal_port = resolve_internal_port(self.config)
self.decode_args = resolve_motion_decode_args(camera_config)
ffprobe_path = self.ffprobe_path
return self._search_motion_sequential(filtered_recordings, polygon_mask)
runs = coalesce_runs(filtered_recordings, MAX_RUN_SECONDS, RUN_GAP_EPSILON)
if not runs:
return []
def _search_motion_parallel(
self,
recordings: list[Recordings],
polygon_mask: np.ndarray,
) -> list[MotionSearchResult]:
"""Search for motion in parallel across segments, streaming results."""
all_results: list[MotionSearchResult] = []
total_frames = 0
next_recording_idx_to_merge = 0
first_run = runs[0]
first_url = build_vod_url(
self.internal_port,
camera_name,
float(first_run[0].start_time),
float(first_run[-1].end_time),
)
dims = probe_video_dimensions(ffprobe_path, first_url)
if dims is None:
raise ValueError(f"Could not probe VOD dimensions for camera {camera_name}")
rec_width, rec_height, _rec_fps = dims
self.crop, self.scaled = compute_roi_crop_and_scale(
self.job.polygon_points, rec_width, rec_height, SCALE_TARGET
)
self.scaled_mask = build_scaled_roi_mask(
self.job.polygon_points, rec_width, rec_height, self.crop, self.scaled
)
self.channels = 1 # always gray output
# Decide keyframe vs fixed-cadence sampling once from the first run's GOP
# (keyframe structure is a per-camera constant).
first_pts = probe_vod_keyframe_pts(ffprobe_path, first_url)
self.use_keyframe = keyframe_sampling_eligible(first_pts)
logger.debug(
"Motion search job %s: starting motion search with %d workers "
"across %d segments",
"Motion search job %s: %d runs, sampling=%s, hwaccel=%s, vod=%dx%d",
self.job.id,
self.max_workers,
len(recordings),
len(runs),
"keyframe" if self.use_keyframe else "cadence",
bool(self.decode_args),
rec_width,
rec_height,
)
# Initialize partial results on the job so they stream to the frontend
return self._search_runs(runs)
def _emit_progress(self, abs_ts: float) -> None:
"""Throttled intra-run progress broadcast (scanning cursor)."""
now = time.monotonic()
if now - self._last_progress_broadcast < PROGRESS_BROADCAST_INTERVAL:
return
self._last_progress_broadcast = now
self.job.scanning_timestamp = abs_ts
self._broadcast_status()
def _detect_with_progress(
self,
indexed_frames: list[tuple[int, np.ndarray]],
timestamp_fn: Callable[[int], float],
) -> list[MotionSearchResult]:
"""Run detection while firing throttled progress as frames are scanned."""
def _gen() -> Generator[tuple[int, np.ndarray], None, None]:
for i, frame in indexed_frames:
if not self._should_stop():
self._emit_progress(timestamp_fn(i))
yield i, frame
return detect_motion_scaled(
_gen(),
self.scaled_mask,
self.job.threshold,
self.job.min_area,
timestamp_fn,
)
def _process_run(
self, run: list[Recordings]
) -> tuple[list[MotionSearchResult], int]:
"""Decode one run's VOD stream and detect motion.
Keyframe mode compares every decoded keyframe (free recall, since they
are all decoded anyway) paired with its probed PTS; if the decoded and
probed counts disagree (the decoder ignored ``-skip_frame nokey`` or the
stream is corrupt) this run re-runs in the fixed-cadence fallback.
Returns ``(results, frame_count)``.
"""
run_start: float = run[0].start_time # type: ignore[assignment]
run_end: float = run[-1].end_time # type: ignore[assignment]
vod_url = build_vod_url(self.internal_port, self.job.camera, run_start, run_end)
time_map = build_segment_time_map(run)
if self.use_keyframe:
kf_pts = probe_vod_keyframe_pts(self.ffprobe_path, vod_url)
frames = list(
iter_vod_frames(
self.ffmpeg_path,
vod_url,
self.scaled[0],
self.scaled[1],
self.channels,
self.decode_args,
self.crop,
self.scaled,
True,
self._should_stop,
skip_nonkey=True,
fps_rate=None,
)
)
if kf_pts and len(frames) == len(kf_pts):
abs_times = [stream_time_to_absolute(time_map, p) for p in kf_pts]
indexed = list(enumerate(frames))
def _ts_kf(i: int) -> float:
return abs_times[i]
results = self._detect_with_progress(indexed, _ts_kf)
return results, len(frames)
logger.debug(
"Keyframe count mismatch (%d decoded vs %d probed), using cadence",
len(frames),
len(kf_pts),
)
return self._process_run_cadence(vod_url, time_map)
def _process_run_cadence(
self, vod_url: str, time_map: list[tuple[float, float, float]]
) -> tuple[list[MotionSearchResult], int]:
"""Fixed-cadence fallback: fps-filtered VOD decode, evenly spaced times."""
frames = list(
iter_vod_frames(
self.ffmpeg_path,
vod_url,
self.scaled[0],
self.scaled[1],
self.channels,
self.decode_args,
self.crop,
self.scaled,
True,
self._should_stop,
skip_nonkey=False,
fps_rate=self.fps_rate,
)
)
indexed = list(enumerate(frames))
def _ts_fps(i: int) -> float:
return stream_time_to_absolute(time_map, i / self.fps_rate)
results = self._detect_with_progress(indexed, _ts_fps)
return results, len(frames)
def _merge_run(
self,
run: list[Recordings],
run_results: list[MotionSearchResult],
frames: int,
state: dict[str, Any],
) -> bool:
"""Fold one run's output into the running results; stream + dedup.
Returns True once ``max_results`` deduped hits have accumulated.
"""
state["completed_runs"] += 1
state["all_results"].extend(run_results)
state["total_frames"] += frames
self.job.total_frames_processed = state["total_frames"]
self.metrics.frames_decoded = state["total_frames"]
self.metrics.segments_processed += len(run)
self.job.progress = state["completed_runs"] / state["total_runs"]
state["all_results"].sort(key=lambda r: r.timestamp)
deduped = self._deduplicate_results(state["all_results"])[
: self.job.max_results
]
self.job.results = {
"results": [r.to_dict() for r in deduped],
"total_frames_processed": state["total_frames"],
}
self._broadcast_status()
return len(deduped) >= self.job.max_results
def _search_runs(self, runs: list[list[Recordings]]) -> list[MotionSearchResult]:
"""Decode runs (parallel pool when enabled), merge in order, stream."""
state: dict[str, Any] = {
"all_results": [],
"total_frames": 0,
"completed_runs": 0,
"total_runs": len(runs),
}
self.job.results = {"results": [], "total_frames_processed": 0}
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
futures: dict[Future, int] = {}
completed_segments: dict[int, tuple[list[MotionSearchResult], int]] = {}
logger.debug(
"Motion search job %s: searching %d runs (parallel=%s, workers=%d)",
self.job.id,
len(runs),
self.job.parallel,
self.max_workers,
)
for idx, recording in enumerate(recordings):
if self._should_stop():
break
if self.job.parallel and len(runs) > 1:
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
futures: dict[Future, int] = {}
for idx, run in enumerate(runs):
if self._should_stop():
break
futures[executor.submit(self._process_run, run)] = idx
rec_start: float = recording.start_time # type: ignore[assignment]
rec_end: float = recording.end_time # type: ignore[assignment]
future = executor.submit(
self._process_recording_for_motion,
str(recording.path),
rec_start,
rec_end,
self.job.start_time_range,
self.job.end_time_range,
polygon_mask,
self.job.threshold,
self.job.min_area,
self.job.frame_skip,
)
futures[future] = idx
completed: dict[int, tuple[list[MotionSearchResult], int]] = {}
next_idx = 0
for future in as_completed(futures):
if self._should_stop():
break
run_idx = futures[future]
try:
completed[run_idx] = future.result()
except Exception as e:
self.metrics.segments_with_errors += 1
logger.warning("Error processing run %d: %s", run_idx, e)
completed[run_idx] = ([], 0)
for future in as_completed(futures):
if self._should_stop():
# Cancel remaining futures
for f in futures:
f.cancel()
break
recording_idx = futures[future]
recording = recordings[recording_idx]
try:
results, frames = future.result()
self.metrics.segments_processed += 1
completed_segments[recording_idx] = (results, frames)
while next_recording_idx_to_merge in completed_segments:
segment_results, segment_frames = completed_segments.pop(
next_recording_idx_to_merge
)
all_results.extend(segment_results)
total_frames += segment_frames
self.job.total_frames_processed = total_frames
self.metrics.frames_decoded = total_frames
if segment_results:
deduped = self._deduplicate_results(all_results)
self.job.results = {
"results": [
r.to_dict() for r in deduped[: self.job.max_results]
],
"total_frames_processed": total_frames,
}
self._broadcast_status()
if segment_results and len(deduped) >= self.job.max_results:
while next_idx in completed:
run_results, frames = completed.pop(next_idx)
if self._merge_run(runs[next_idx], run_results, frames, state):
self.internal_stop_event.set()
for pending_future in futures:
pending_future.cancel()
for pending in futures:
pending.cancel()
break
next_recording_idx_to_merge += 1
next_idx += 1
if self.internal_stop_event.is_set():
break
else:
for run in runs:
if self._should_stop():
break
try:
run_results, frames = self._process_run(run)
except Exception as e:
self.metrics.segments_processed += 1
self.metrics.segments_with_errors += 1
self.metrics.segments_processed += len(run)
self._broadcast_status()
logger.warning(
"Error processing segment %s: %s",
recording.path,
e,
)
self.job.total_frames_processed = total_frames
self.metrics.frames_decoded = total_frames
logger.debug(
"Motion search job %s: motion search complete, "
"found %d raw results, decoded %d frames, %d segment errors",
self.job.id,
len(all_results),
total_frames,
self.metrics.segments_with_errors,
)
# Sort and deduplicate results
all_results.sort(key=lambda x: x.timestamp)
return self._deduplicate_results(all_results)[: self.job.max_results]
def _search_motion_sequential(
self,
recordings: list[Recordings],
polygon_mask: np.ndarray,
) -> list[MotionSearchResult]:
"""Search for motion sequentially across segments, streaming results."""
all_results: list[MotionSearchResult] = []
total_frames = 0
logger.debug(
"Motion search job %s: starting sequential motion search across %d segments",
self.job.id,
len(recordings),
)
self.job.results = {"results": [], "total_frames_processed": 0}
for recording in recordings:
if self.cancel_event.is_set():
break
try:
rec_start: float = recording.start_time # type: ignore[assignment]
rec_end: float = recording.end_time # type: ignore[assignment]
results, frames = self._process_recording_for_motion(
str(recording.path),
rec_start,
rec_end,
self.job.start_time_range,
self.job.end_time_range,
polygon_mask,
self.job.threshold,
self.job.min_area,
self.job.frame_skip,
)
all_results.extend(results)
total_frames += frames
self.job.total_frames_processed = total_frames
self.metrics.frames_decoded = total_frames
self.metrics.segments_processed += 1
if results:
all_results.sort(key=lambda x: x.timestamp)
deduped = self._deduplicate_results(all_results)[
: self.job.max_results
]
self.job.results = {
"results": [r.to_dict() for r in deduped],
"total_frames_processed": total_frames,
}
self._broadcast_status()
if results and len(deduped) >= self.job.max_results:
logger.warning("Error processing run: %s", e)
continue
if self._merge_run(run, run_results, frames, state):
break
except Exception as e:
self.metrics.segments_processed += 1
self.metrics.segments_with_errors += 1
self._broadcast_status()
logger.warning("Error processing segment %s: %s", recording.path, e)
self.job.total_frames_processed = total_frames
self.metrics.frames_decoded = total_frames
all_results: list[MotionSearchResult] = state["all_results"]
self.job.total_frames_processed = state["total_frames"]
self.metrics.frames_decoded = state["total_frames"]
self.job.progress = 1.0
logger.debug(
"Motion search job %s: sequential motion search complete, "
"found %d raw results, decoded %d frames, %d segment errors",
"Motion search job %s: complete, %d raw results, %d frames, %d errors",
self.job.id,
len(all_results),
total_frames,
state["total_frames"],
self.metrics.segments_with_errors,
)
all_results.sort(key=lambda x: x.timestamp)
all_results.sort(key=lambda r: r.timestamp)
return self._deduplicate_results(all_results)[: self.job.max_results]
def _deduplicate_results(
@@ -602,160 +843,6 @@ class MotionSearchRunner(threading.Thread):
return deduplicated
def _process_recording_for_motion(
self,
recording_path: str,
recording_start: float,
recording_end: float,
search_start: float,
search_end: float,
polygon_mask: np.ndarray,
threshold: int,
min_area: float,
frame_skip: int,
) -> tuple[list[MotionSearchResult], int]:
"""Process a single recording file for motion detection.
This method is designed to be called from a thread pool.
Args:
min_area: Minimum change area as a percentage of the ROI (0-100).
"""
results: list[MotionSearchResult] = []
frames_processed = 0
if not os.path.exists(recording_path):
logger.warning("Recording file not found: %s", recording_path)
return results, frames_processed
cap = cv2.VideoCapture(recording_path)
if not cap.isOpened():
logger.error("Could not open recording: %s", recording_path)
return results, frames_processed
try:
fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
recording_duration = recording_end - recording_start
# Calculate frame range
start_offset = max(0, search_start - recording_start)
end_offset = min(recording_duration, search_end - recording_start)
start_frame = int(start_offset * fps)
end_frame = int(end_offset * fps)
start_frame = max(0, min(start_frame, total_frames - 1))
end_frame = max(0, min(end_frame, total_frames))
if start_frame >= end_frame:
return results, frames_processed
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
# Get ROI bounding box
roi_bbox = cv2.boundingRect(polygon_mask)
roi_x, roi_y, roi_w, roi_h = roi_bbox
prev_frame_gray = None
frame_step = max(frame_skip, 1)
frame_idx = start_frame
while frame_idx < end_frame:
if self._should_stop():
break
ret, frame = cap.read()
if not ret:
frame_idx += 1
continue
if (frame_idx - start_frame) % frame_step != 0:
frame_idx += 1
continue
frames_processed += 1
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Handle frame dimension changes
if gray.shape != polygon_mask.shape:
resized_mask = cv2.resize(
polygon_mask,
(gray.shape[1], gray.shape[0]),
interpolation=cv2.INTER_NEAREST,
)
current_bbox = cv2.boundingRect(resized_mask)
else:
resized_mask = polygon_mask
current_bbox = roi_bbox
roi_x, roi_y, roi_w, roi_h = current_bbox
cropped_gray = gray[roi_y : roi_y + roi_h, roi_x : roi_x + roi_w]
cropped_mask = resized_mask[
roi_y : roi_y + roi_h, roi_x : roi_x + roi_w
]
cropped_mask_area = np.count_nonzero(cropped_mask)
if cropped_mask_area == 0:
frame_idx += 1
continue
# Convert percentage to pixel count for this ROI
min_area_pixels = int((min_area / 100.0) * cropped_mask_area)
masked_gray = cv2.bitwise_and(
cropped_gray, cropped_gray, mask=cropped_mask
)
if prev_frame_gray is not None:
diff = cv2.absdiff(prev_frame_gray, masked_gray) # type: ignore[unreachable]
diff_blurred = cv2.GaussianBlur(diff, (3, 3), 0)
_, thresh = cv2.threshold(
diff_blurred, threshold, 255, cv2.THRESH_BINARY
)
thresh_dilated = cv2.dilate(thresh, None, iterations=1)
thresh_masked = cv2.bitwise_and(
thresh_dilated, thresh_dilated, mask=cropped_mask
)
change_pixels = cv2.countNonZero(thresh_masked)
if change_pixels > min_area_pixels:
contours, _ = cv2.findContours(
thresh_masked, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
total_change_area = sum(
cv2.contourArea(c)
for c in contours
if cv2.contourArea(c) >= min_area_pixels
)
if total_change_area > 0:
frame_time_offset = (frame_idx - start_frame) / fps
timestamp = (
recording_start + start_offset + frame_time_offset
)
change_percentage = (
total_change_area / cropped_mask_area
) * 100
results.append(
MotionSearchResult(
timestamp=timestamp,
change_percentage=round(change_percentage, 2),
)
)
prev_frame_gray = masked_gray
frame_idx += 1
finally:
cap.release()
logger.debug(
"Motion search segment complete: %s, %d frames processed, %d results found",
recording_path,
frames_processed,
len(results),
)
return results, frames_processed
# Module-level state for managing per-camera jobs
_motion_search_jobs: dict[str, tuple[MotionSearchJob, threading.Event]] = {}
@@ -779,7 +866,6 @@ def start_motion_search_job(
polygon_points: list[list[float]],
threshold: int = 30,
min_area: float = 5.0,
frame_skip: int = 5,
parallel: bool = False,
max_results: int = 25,
) -> str:
@@ -794,7 +880,6 @@ def start_motion_search_job(
polygon_points=polygon_points,
threshold=threshold,
min_area=min_area,
frame_skip=frame_skip,
parallel=parallel,
max_results=max_results,
)
@@ -812,14 +897,13 @@ def start_motion_search_job(
logger.debug(
"Started motion search job %s for camera %s: "
"time_range=%.1f-%.1f, threshold=%d, min_area=%.1f%%, "
"frame_skip=%d, parallel=%s, max_results=%d, polygon_points=%d vertices",
"parallel=%s, max_results=%d, polygon_points=%d vertices",
job.id,
camera_name,
start_time,
end_time,
threshold,
min_area,
frame_skip,
parallel,
max_results,
len(polygon_points),
+75
View File
@@ -0,0 +1,75 @@
"""Pure helpers for VOD-batched motion search.
Coalescing gate-passing segments into time-contiguous runs, mapping a frame's
VOD stream time back to an absolute timestamp, and thinning sample times to a
target interval. No I/O or ffmpeg here so the tricky math stays unit-testable.
"""
from bisect import bisect_right
from typing import Any
def coalesce_runs(
segments: list[Any], max_seconds: float, epsilon: float
) -> list[list[Any]]:
"""Group gate-passing segments into time-contiguous runs.
A run extends while each segment's ``start_time`` is within ``epsilon`` of
the previous segment's ``end_time`` (no recording gap) and the run's total
span stays at or below ``max_seconds``. A gap or the cap starts a new run.
Each segment must expose ``start_time`` / ``end_time``.
"""
runs: list[list[Any]] = []
current: list[Any] = []
for seg in segments:
if not current:
current = [seg]
continue
prev_end = float(current[-1].end_time)
run_start = float(current[0].start_time)
contiguous = abs(float(seg.start_time) - prev_end) <= epsilon
within_cap = (float(seg.end_time) - run_start) <= max_seconds
if contiguous and within_cap:
current.append(seg)
else:
runs.append(current)
current = [seg]
if current:
runs.append(current)
return runs
def build_segment_time_map(
run: list[Any],
) -> list[tuple[float, float, float]]:
"""Build a (stream_offset, abs_start, duration) row per segment in a run.
``stream_offset`` is the segment's start in continuous VOD stream time (the
cumulative sum of preceding segment durations); ``abs_start`` is its absolute
``start_time``. Built from each segment's own duration; for a gap-free run
this makes stream time equal ``run_start + offset``.
"""
rows: list[tuple[float, float, float]] = []
offset = 0.0
for seg in run:
duration = float(seg.end_time) - float(seg.start_time)
rows.append((offset, float(seg.start_time), duration))
offset += duration
return rows
def stream_time_to_absolute(
time_map: list[tuple[float, float, float]], stream_time: float
) -> float:
"""Map a VOD stream time to an absolute timestamp via the run's table.
Binary-searches the segment whose stream range contains ``stream_time`` and
returns ``abs_start + (stream_time - stream_offset)``. Times past the last
segment map into the last segment (clamped at the run edge).
"""
offsets = [row[0] for row in time_map]
idx = bisect_right(offsets, stream_time) - 1
if idx < 0:
idx = 0
stream_offset, abs_start, _duration = time_map[idx]
return abs_start + (stream_time - stream_offset)
+382
View File
@@ -0,0 +1,382 @@
"""Hardware-accelerated ffmpeg decode for motion search.
Decodes a recording run's VOD/HLS stream with an ffmpeg subprocess, optionally
selecting only keyframes, and streams raw frames over a pipe for the motion
math. Output is the requested ``pix_fmt`` (gray or ``bgr24``) with optional
crop/scale applied in the filter graph so downstream pixels are unchanged.
"""
import json
import logging
import subprocess as sp
import tempfile
from collections.abc import Callable, Generator
from typing import IO
import numpy as np
from frigate.config import CameraConfig
from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_decode
from frigate.util.services import auto_detect_hwaccel
logger = logging.getLogger(__name__)
# Output-format surfaces that download cleanly to nv12 via the fixed
# ``hwdownload,format=nv12`` step the decode path appends. Other surfaces
# (drm_prime from rkmpp, vulkan, amf) need a different download step, so motion
# search decodes them in software to keep results byte-identical rather than risk
# a wrong-but-valid-sized frame the zero-frame fallback gate would not catch.
_NV12_OUTPUT_FORMATS = frozenset({"vaapi", "cuda", "qsv"})
def _hwaccel_output_format(decode_args: list[str]) -> str | None:
"""Return the ``-hwaccel_output_format`` value in ffmpeg args, or None."""
try:
idx = decode_args.index("-hwaccel_output_format")
except ValueError:
return None
return decode_args[idx + 1] if idx + 1 < len(decode_args) else None
def resolve_motion_decode_args(camera_config: CameraConfig) -> list[str]:
"""Resolve the ffmpeg hwaccel decode args for a camera's recordings.
``auto`` is resolved via ``auto_detect_hwaccel`` and the preset is expanded
by ``parse_preset_hardware_acceleration_decode`` (the same table the live
pipeline uses). Acceleration is kept only when the decoded surface downloads
cleanly to nv12 -- decided by reading ``-hwaccel_output_format`` back from the
resolved args rather than a separate preset allowlist that could drift from
``PRESETS_HW_ACCEL_DECODE``. Anything else (custom args, a software-only
preset, or an nv12-incompatible surface) returns an empty list, meaning
software decode, so results stay byte-identical.
"""
raw = camera_config.ffmpeg.hwaccel_args
preset = auto_detect_hwaccel() if raw == "auto" else raw
# Custom args (a list) decode in software so results stay byte-identical.
if not isinstance(preset, str):
return []
decode_args = parse_preset_hardware_acceleration_decode(
preset,
camera_config.detect.fps,
camera_config.detect.width or 0,
camera_config.detect.height or 0,
camera_config.ffmpeg.gpu,
)
if not decode_args:
return []
if _hwaccel_output_format(decode_args) not in _NV12_OUTPUT_FORMATS:
return []
return decode_args
def _read_exact(stream: IO[bytes], size: int) -> bytes | None:
"""Read exactly ``size`` bytes from a pipe, or None at clean EOF.
Pipe reads can return fewer bytes than requested, so loop until the frame
is complete. A short read at the start of a frame means end-of-stream.
"""
buf = bytearray()
while len(buf) < size:
chunk = stream.read(size - len(buf))
if not chunk:
return None
buf.extend(chunk)
return bytes(buf)
def _terminate(proc: sp.Popen[bytes]) -> None:
"""Stop an ffmpeg decode process promptly."""
# Close the read end first so a blocked ffmpeg write unblocks (ffmpeg then
# sees a broken pipe), then signal it. The resulting ffmpeg write error is
# harmless and goes to the captured stderr.
if proc.stdout is not None:
try:
proc.stdout.close()
except OSError:
pass
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=5)
except sp.TimeoutExpired:
proc.kill()
proc.wait()
KEYFRAME_MAX_GAP_SECONDS = 2.0
def keyframe_sampling_eligible(
keyframe_pts: list[float], max_gap: float = KEYFRAME_MAX_GAP_SECONDS
) -> bool:
"""True if keyframes are dense and regular enough for keyframe-only sampling.
Requires at least two keyframes and no gap longer than ``max_gap`` seconds, so
a multi-second motion event necessarily spans a sampled keyframe.
"""
if len(keyframe_pts) < 2:
return False
gaps = [b - a for a, b in zip(keyframe_pts, keyframe_pts[1:])]
return max(gaps) <= max_gap
VOD_PROTOCOL_ARGS = ["-protocol_whitelist", "pipe,file,http,tcp"]
def build_vod_decode_command(
ffmpeg_path: str,
vod_url: str,
decode_args: list[str],
crop: tuple[int, int, int, int] | None,
scale: tuple[int, int] | None,
gray: bool,
*,
skip_nonkey: bool,
fps_rate: float | None,
) -> list[str]:
"""Build the ffmpeg argv to decode a VOD HLS URL.
``skip_nonkey`` adds ``-skip_frame nokey`` (keyframe-only). ``fps_rate`` adds
an ``fps`` filter for the fixed-cadence fallback. They are mutually
exclusive: keyframe mode passes ``skip_nonkey=True``/``fps_rate=None``; the
fallback passes ``skip_nonkey=False`` with a rate.
"""
filters: list[str] = []
# With hwaccel the decoded frames are GPU surfaces; pull them back to system
# memory before the CPU fps/crop/scale filters and the rawvideo encoder.
if decode_args:
filters.append("hwdownload")
filters.append("format=nv12")
if fps_rate is not None:
filters.append(f"fps={fps_rate}")
if crop is not None:
cw, ch, cx, cy = crop
filters.append(f"crop={cw}:{ch}:{cx}:{cy}")
if scale is not None:
sw, sh = scale
filters.append(f"scale={sw}:{sh}")
pix_fmt = "gray" if gray else "bgr24"
cmd = [ffmpeg_path, "-hide_banner", "-loglevel", "error"]
if skip_nonkey:
cmd += ["-skip_frame", "nokey"]
cmd += [*decode_args, *VOD_PROTOCOL_ARGS, "-i", vod_url, "-an"]
if filters:
cmd += ["-vf", ",".join(filters)]
cmd += ["-vsync", "0", "-f", "rawvideo", "-pix_fmt", pix_fmt, "pipe:"]
return cmd
def _run_vod_decode(
ffmpeg_path: str,
vod_url: str,
out_width: int,
out_height: int,
channels: int,
decode_args: list[str],
crop: tuple[int, int, int, int] | None,
scale: tuple[int, int] | None,
gray: bool,
should_stop: Callable[[], bool],
*,
skip_nonkey: bool,
fps_rate: float | None,
software_retry: bool,
) -> Generator[np.ndarray, None, None]:
"""Run one VOD decode, yielding raw frames; retry in software if empty."""
cmd = build_vod_decode_command(
ffmpeg_path,
vod_url,
decode_args,
crop,
scale,
gray,
skip_nonkey=skip_nonkey,
fps_rate=fps_rate,
)
frame_size = out_width * out_height * channels
stderr_file = tempfile.SpooledTemporaryFile(max_size=65536)
proc = sp.Popen(cmd, stdout=sp.PIPE, stderr=stderr_file)
assert proc.stdout is not None
count = 0
try:
while True:
if should_stop():
break
buf = _read_exact(proc.stdout, frame_size)
if buf is None:
break
if channels == 1:
frame = np.frombuffer(buf, dtype=np.uint8).reshape(
(out_height, out_width)
)
else:
frame = np.frombuffer(buf, dtype=np.uint8).reshape(
(out_height, out_width, channels)
)
count += 1
yield frame
finally:
_terminate(proc)
stderr_file.close()
if count == 0 and software_retry and not should_stop():
logger.warning("Hardware VOD decode produced no frames, retrying in software")
yield from _run_vod_decode(
ffmpeg_path,
vod_url,
out_width,
out_height,
channels,
[],
crop,
scale,
gray,
should_stop,
skip_nonkey=skip_nonkey,
fps_rate=fps_rate,
software_retry=False,
)
def iter_vod_frames(
ffmpeg_path: str,
vod_url: str,
out_width: int,
out_height: int,
channels: int,
decode_args: list[str],
crop: tuple[int, int, int, int] | None,
scale: tuple[int, int] | None,
gray: bool,
should_stop: Callable[[], bool],
*,
skip_nonkey: bool,
fps_rate: float | None,
) -> Generator[np.ndarray, None, None]:
"""Decode a VOD HLS URL and yield raw frames in order.
Pair keyframe-mode output with probed keyframe PTS; pair fallback output with
a fixed cadence. Falls back once to software decode if a hwaccel decode yields
no frames.
"""
yield from _run_vod_decode(
ffmpeg_path,
vod_url,
out_width,
out_height,
channels,
decode_args,
crop,
scale,
gray,
should_stop,
skip_nonkey=skip_nonkey,
fps_rate=fps_rate,
software_retry=bool(decode_args),
)
def probe_vod_keyframe_pts(ffprobe_path: str, vod_url: str) -> list[float]:
"""Return keyframe presentation timestamps (VOD stream time) in order.
Reads packet flags via ffprobe over the VOD URL (no decode). Returns [] on
any failure so the caller can fall back.
"""
cmd = [
ffprobe_path,
"-v",
"error",
*VOD_PROTOCOL_ARGS,
"-i",
vod_url,
"-select_streams",
"v:0",
"-show_packets",
"-show_entries",
"packet=pts_time,flags",
"-of",
"json",
]
try:
completed = sp.run(cmd, capture_output=True, text=True, timeout=120)
except (OSError, sp.SubprocessError):
logger.warning("ffprobe failed for VOD keyframe probe")
return []
if completed.returncode != 0 or not completed.stdout:
return []
try:
packets = json.loads(completed.stdout).get("packets", [])
except json.JSONDecodeError:
return []
pts: list[float] = []
for pkt in packets:
flags = pkt.get("flags", "")
pts_time = pkt.get("pts_time")
if flags.startswith("K") and pts_time is not None:
try:
pts.append(float(pts_time))
except ValueError:
continue
return sorted(pts)
def probe_video_dimensions(
ffprobe_path: str, recording_path: str
) -> tuple[int, int, float] | None:
"""Return (width, height, fps) for a recording's video stream, or None.
Reads stream metadata via ffprobe (no decode). The record stream resolution
can differ from the camera's detect resolution, so this is probed once per
job against a real segment.
"""
cmd = [
ffprobe_path,
"-v",
"error",
"-select_streams",
"v:0",
"-show_entries",
"stream=width,height,avg_frame_rate",
"-of",
"json",
recording_path,
]
try:
completed = sp.run(cmd, capture_output=True, text=True, timeout=30)
except (OSError, sp.SubprocessError):
return None
if completed.returncode != 0 or not completed.stdout:
return None
try:
streams = json.loads(completed.stdout).get("streams", [])
except json.JSONDecodeError:
return None
if not streams:
return None
stream = streams[0]
width = int(stream.get("width", 0) or 0)
height = int(stream.get("height", 0) or 0)
rate = stream.get("avg_frame_rate", "0/0") or "0/0"
try:
num, _, den = rate.partition("/")
fps = float(num) / float(den) if float(den) != 0 else 0.0
except (ValueError, ZeroDivisionError):
fps = 0.0
if width <= 0 or height <= 0:
return None
return width, height, fps
+1 -1
View File
@@ -456,7 +456,7 @@ class RecordingExporter(threading.Thread):
diff = max(0.0, float(self.start_time) - float(preview.start_time))
ffmpeg_cmd = [
"/usr/lib/ffmpeg/7.0/bin/ffmpeg", # hardcode path for exports thumbnail due to missing libwebp support
"/usr/lib/ffmpeg/8.0/bin/ffmpeg", # hardcode path for exports thumbnail due to missing libwebp support
"-hide_banner",
"-loglevel",
"warning",
@@ -0,0 +1,58 @@
from unittest.mock import AsyncMock, patch
from frigate.models import Event, Recordings, ReviewSegment
from frigate.test.http_api.base_http_test import AuthTestClient, BaseTestHttp
class TestHttpKeyframeAnalysis(BaseTestHttp):
def setUp(self):
super().setUp([Event, Recordings, ReviewSegment])
def test_invalid_camera_returns_404(self):
app = super().create_app()
with AuthTestClient(app) as client:
response = client.get("/keyframe_analysis?camera=does_not_exist")
assert response.status_code == 404
def test_record_disabled_returns_neutral(self):
# default minimal_config has recording disabled
app = super().create_app()
with AuthTestClient(app) as client:
response = client.get("/keyframe_analysis?camera=front_door")
assert response.status_code == 200
assert response.json()["severity"] == "record_disabled"
def test_probes_record_input_and_returns_severity(self):
self.minimal_config["cameras"]["front_door"]["ffmpeg"]["inputs"] = [
{
"path": "rtsp://10.0.0.1:554/record",
"roles": ["detect", "record"],
}
]
self.minimal_config["cameras"]["front_door"]["record"] = {"enabled": True}
app = super().create_app()
canned = {
"severity": "ok",
"keyframe_count": 5,
"max_gap": 1.0,
"mean_gap": 1.0,
"min_gap": 1.0,
"segment_time": 10,
"duration_observed": 4.0,
"thresholds": {"warning": 4.0, "error": 10},
}
with patch(
"frigate.api.camera.analyze_record_keyframes",
AsyncMock(return_value=canned),
) as mock_probe:
with AuthTestClient(app) as client:
response = client.get("/keyframe_analysis?camera=front_door")
assert response.status_code == 200
assert response.json()["severity"] == "ok"
# index matches the input carrying the record role ("Stream 1")
assert response.json()["stream_index"] == 0
# the record-role input path was probed
assert mock_probe.await_args.args[1] == "rtsp://10.0.0.1:554/record"
+72
View File
@@ -403,3 +403,75 @@ class TestHttpMedia(BaseTestHttp):
assert len(summary) == 1
assert "2024-03-10" in summary
assert summary["2024-03-10"] is True
def test_recordings_unavailable_reports_gap_between_recordings(self):
"""A gap between two recordings is reported as an unavailable segment."""
with AuthTestClient(self.app) as client:
# Two recordings with a 20s gap (1010-1030) between them.
Recordings.insert(
id="rec_a",
path="/media/recordings/a.mp4",
camera="front_door",
start_time=1000,
end_time=1010,
duration=10,
motion=0,
).execute()
Recordings.insert(
id="rec_b",
path="/media/recordings/b.mp4",
camera="front_door",
start_time=1030,
end_time=1040,
duration=10,
motion=0,
).execute()
response = client.get(
"/recordings/unavailable",
params={
"after": 1000,
"before": 1040,
"scale": 5,
"cameras": "front_door",
},
)
assert response.status_code == 200
assert response.json() == [{"start_time": 1010, "end_time": 1030}]
def test_recordings_unavailable_merges_overlapping_recordings(self):
"""Overlapping recordings are merged so no false gap is reported."""
with AuthTestClient(self.app) as client:
# Overlapping recordings spanning the whole requested range.
Recordings.insert(
id="rec_a",
path="/media/recordings/a.mp4",
camera="front_door",
start_time=1000,
end_time=1020,
duration=20,
motion=0,
).execute()
Recordings.insert(
id="rec_b",
path="/media/recordings/b.mp4",
camera="front_door",
start_time=1010,
end_time=1030,
duration=20,
motion=0,
).execute()
response = client.get(
"/recordings/unavailable",
params={
"after": 1000,
"before": 1030,
"scale": 5,
"cameras": "front_door",
},
)
assert response.status_code == 200
assert response.json() == []
+4 -7
View File
@@ -610,19 +610,16 @@ class TestHttpReview(BaseTestHttp):
response = client.get("/review/activity/motion", params=params)
assert response.status_code == 200
response_json = response.json()
assert len(response_json) == 61
# Only buckets with an actual recording are returned. Empty
# gap-fill buckets between the two recordings are dropped.
assert len(response_json) == 2
self.assertDictEqual(
{"motion": 50.5, "camera": "front_door", "start_time": now + 1},
response_json[0],
)
for item in response_json[1:-1]:
self.assertDictEqual(
{"motion": 0.0, "camera": "", "start_time": item["start_time"]},
item,
)
self.assertDictEqual(
{"motion": 100.0, "camera": "front_door", "start_time": one_m + 1},
response_json[len(response_json) - 1],
response_json[1],
)
####################################################################################################################
+111
View File
@@ -0,0 +1,111 @@
"""Tests for keyframe-spacing analysis used to detect smart/+ codecs."""
import asyncio
import unittest
from unittest.mock import AsyncMock, MagicMock, patch
from frigate.util.services import (
analyze_record_keyframes,
classify_keyframe_gaps,
parse_keyframe_packets,
)
class TestClassifyKeyframeGaps(unittest.TestCase):
def test_ok_when_gaps_small(self):
# keyframes every ~1s
pts = [0.0, 1.0, 2.0, 3.0, 4.0]
result = classify_keyframe_gaps(pts, segment_time=10)
self.assertEqual(result["severity"], "ok")
self.assertEqual(result["max_gap"], 1.0)
self.assertEqual(result["keyframe_count"], 5)
self.assertEqual(result["thresholds"], {"warning": 4.0, "error": 10})
def test_warning_when_gap_exceeds_four_seconds(self):
pts = [0.0, 1.0, 6.5] # 5.5s gap
result = classify_keyframe_gaps(pts, segment_time=10)
self.assertEqual(result["severity"], "warning")
self.assertEqual(result["max_gap"], 5.5)
def test_error_when_gap_exceeds_segment_time(self):
pts = [0.0, 12.0] # 12s gap > 10s segment
result = classify_keyframe_gaps(pts, segment_time=10)
self.assertEqual(result["severity"], "error")
def test_error_threshold_tracks_segment_time(self):
pts = [0.0, 6.0] # 6s gap, segment_time=5 -> error
result = classify_keyframe_gaps(pts, segment_time=5)
self.assertEqual(result["severity"], "error")
def test_unknown_with_single_keyframe(self):
result = classify_keyframe_gaps([1.0], segment_time=10)
self.assertEqual(result["severity"], "unknown")
self.assertIsNone(result["max_gap"])
self.assertEqual(result["keyframe_count"], 1)
def test_unknown_with_no_keyframes(self):
result = classify_keyframe_gaps([], segment_time=10)
self.assertEqual(result["severity"], "unknown")
self.assertEqual(result["keyframe_count"], 0)
class TestParseKeyframePackets(unittest.TestCase):
def test_extracts_keyframe_pts_and_max(self):
output = "0.000000,K__\n0.033333,___\n1.000000,K__\n1.500000,___\n"
keyframe_pts, max_pts = parse_keyframe_packets(output)
self.assertEqual(keyframe_pts, [0.0, 1.0])
self.assertEqual(max_pts, 1.5)
def test_skips_unparseable_and_empty_lines(self):
output = "N/A,K__\n\n2.0,K__\nbad line\n"
keyframe_pts, max_pts = parse_keyframe_packets(output)
self.assertEqual(keyframe_pts, [2.0])
self.assertEqual(max_pts, 2.0)
def test_empty_output(self):
keyframe_pts, max_pts = parse_keyframe_packets("")
self.assertEqual(keyframe_pts, [])
self.assertIsNone(max_pts)
class TestAnalyzeRecordKeyframes(unittest.IsolatedAsyncioTestCase):
async def test_merges_duration_and_classification(self):
csv = b"0.0,K__\n1.0,___\n6.0,K__\n7.0,___\n"
proc = MagicMock()
proc.communicate = AsyncMock(return_value=(csv, b""))
ffmpeg = MagicMock()
ffmpeg.ffprobe_path = "/usr/bin/ffprobe"
with patch(
"frigate.util.services.asyncio.create_subprocess_exec",
AsyncMock(return_value=proc),
):
result = await analyze_record_keyframes(
ffmpeg, "rtsp://cam/stream", segment_time=10
)
self.assertEqual(result["severity"], "warning") # 6s gap > 4s
self.assertEqual(result["max_gap"], 6.0)
self.assertEqual(result["duration_observed"], 7.0)
async def test_timeout_returns_unknown(self):
proc = MagicMock()
proc.communicate = AsyncMock(side_effect=asyncio.TimeoutError())
proc.kill = MagicMock()
ffmpeg = MagicMock()
ffmpeg.ffprobe_path = "/usr/bin/ffprobe"
with patch(
"frigate.util.services.asyncio.create_subprocess_exec",
AsyncMock(return_value=proc),
):
result = await analyze_record_keyframes(
ffmpeg, "rtsp://cam/stream", segment_time=10
)
self.assertEqual(result["severity"], "unknown")
proc.kill.assert_called_once()
if __name__ == "__main__":
unittest.main()
+58
View File
@@ -0,0 +1,58 @@
"""Tests for motion search batch helpers (runs + timestamp mapping)."""
import unittest
from dataclasses import dataclass
from frigate.jobs.motion_search_batch import (
build_segment_time_map,
coalesce_runs,
stream_time_to_absolute,
)
@dataclass
class _Seg:
path: str
start_time: float
end_time: float
def _run_seconds(run):
return float(run[-1].end_time) - float(run[0].start_time)
class TestCoalesceRuns(unittest.TestCase):
def test_contiguous_segments_form_one_run(self):
segs = [_Seg("a", 0.0, 10.0), _Seg("b", 10.0, 20.0), _Seg("c", 20.0, 30.0)]
runs = coalesce_runs(segs, max_seconds=600.0, epsilon=0.5)
self.assertEqual(len(runs), 1)
self.assertEqual(len(runs[0]), 3)
def test_time_gap_splits_runs(self):
# b ends 20, c starts 25 -> 5s gap > epsilon -> two runs.
segs = [_Seg("a", 0.0, 10.0), _Seg("b", 10.0, 20.0), _Seg("c", 25.0, 35.0)]
runs = coalesce_runs(segs, max_seconds=600.0, epsilon=0.5)
self.assertEqual([len(r) for r in runs], [2, 1])
def test_max_duration_caps_a_run(self):
# Five contiguous 10s segments, cap 25s.
segs = [_Seg(str(i), i * 10.0, i * 10.0 + 10.0) for i in range(5)]
runs = coalesce_runs(segs, max_seconds=25.0, epsilon=0.5)
self.assertTrue(all(_run_seconds(r) <= 30.0 for r in runs))
self.assertEqual(sum(len(r) for r in runs), 5)
def test_empty(self):
self.assertEqual(coalesce_runs([], max_seconds=600.0, epsilon=0.5), [])
class TestTimestampMapping(unittest.TestCase):
def test_gapfree_run_maps_to_start_plus_pts(self):
run = [_Seg("a", 1000.0, 1010.0), _Seg("b", 1010.0, 1020.0)]
time_map = build_segment_time_map(run)
self.assertAlmostEqual(stream_time_to_absolute(time_map, 3.0), 1003.0)
self.assertAlmostEqual(stream_time_to_absolute(time_map, 12.0), 1012.0)
def test_past_end_clamps(self):
run = [_Seg("a", 1000.0, 1010.0)]
time_map = build_segment_time_map(run)
self.assertAlmostEqual(stream_time_to_absolute(time_map, 9.9), 1009.9)
+190
View File
@@ -0,0 +1,190 @@
"""Tests for the motion search hardware-accelerated decode helpers."""
import unittest
from types import SimpleNamespace
from unittest import mock
from frigate.jobs.motion_search_decode import (
KEYFRAME_MAX_GAP_SECONDS,
build_vod_decode_command,
keyframe_sampling_eligible,
probe_video_dimensions,
probe_vod_keyframe_pts,
resolve_motion_decode_args,
)
def _fake_camera_config(
hwaccel_args, gpu=0, fps=5, width=1280, height=720, ffmpeg_path="ffmpeg"
):
return SimpleNamespace(
ffmpeg=SimpleNamespace(
hwaccel_args=hwaccel_args, gpu=gpu, ffmpeg_path=ffmpeg_path
),
detect=SimpleNamespace(fps=fps, width=width, height=height),
)
class TestResolveMotionDecodeArgs(unittest.TestCase):
def test_vaapi_preset_is_accelerated(self):
args = resolve_motion_decode_args(_fake_camera_config("preset-vaapi"))
self.assertIn("-hwaccel", args)
self.assertIn("vaapi", args)
def test_non_nv12_preset_falls_back_to_software(self):
# rkmpp produces drm_prime surfaces that do not download to nv12, so it
# must resolve to software decode (empty args) rather than risk corrupt
# frames.
self.assertEqual(
resolve_motion_decode_args(_fake_camera_config("preset-rkmpp")), []
)
def test_custom_args_fall_back_to_software(self):
# Arbitrary custom hwaccel args (a list, not a preset) decode in software
# to preserve byte-identical results.
self.assertEqual(
resolve_motion_decode_args(_fake_camera_config(["-hwaccel", "vulkan"])),
[],
)
def test_nvidia_codec_preset_is_accelerated(self):
# Codec-specific nvidia presets resolve to the same cuda decode args as
# the bare preset, so eligibility is derived from -hwaccel_output_format
# rather than a hardcoded list that omitted these aliases.
args = resolve_motion_decode_args(_fake_camera_config("preset-nvidia-h264"))
self.assertIn("-hwaccel_output_format", args)
self.assertIn("cuda", args)
def test_software_only_preset_falls_back_to_software(self):
# A preset with no -hwaccel_output_format (decoder-based, no GPU surface)
# cannot use the nv12 download step, so it decodes in software.
self.assertEqual(
resolve_motion_decode_args(_fake_camera_config("preset-rpi-64-h264")), []
)
class TestKeyframeEligibility(unittest.TestCase):
def test_regular_short_gop_is_eligible(self):
pts = [0.0, 0.5, 1.0, 1.5, 2.0] # 0.5s gaps
self.assertTrue(keyframe_sampling_eligible(pts))
def test_long_gop_is_ineligible(self):
pts = [0.0, 5.0, 10.0] # 5s gaps
self.assertFalse(keyframe_sampling_eligible(pts))
def test_irregular_gop_ineligible_when_a_gap_is_long(self):
pts = [0.0, 0.5, 1.0, 8.0] # one 7s gap
self.assertFalse(keyframe_sampling_eligible(pts))
def test_too_few_keyframes_ineligible(self):
self.assertFalse(keyframe_sampling_eligible([1.0]))
self.assertFalse(keyframe_sampling_eligible([]))
def test_default_max_gap_constant(self):
self.assertEqual(KEYFRAME_MAX_GAP_SECONDS, 2.0)
class TestVodDecodeCommand(unittest.TestCase):
URL = "http://127.0.0.1:5000/vod/cam/start/1/end/2/index.m3u8"
def test_keyframe_command_shape(self):
cmd = build_vod_decode_command(
"ffmpeg",
self.URL,
decode_args=[],
crop=(100, 80, 10, 20),
scale=(50, 40),
gray=True,
skip_nonkey=True,
fps_rate=None,
)
joined = " ".join(cmd)
self.assertIn("-skip_frame nokey", joined)
self.assertIn("-protocol_whitelist pipe,file,http,tcp", joined)
self.assertIn(f"-i {self.URL}", joined)
self.assertIn("crop=100:80:10:20", joined)
self.assertIn("scale=50:40", joined)
self.assertIn("-pix_fmt gray", joined)
self.assertNotIn("fps=", joined)
def test_fps_command_uses_fps_filter_not_skip_frame(self):
cmd = build_vod_decode_command(
"ffmpeg",
self.URL,
decode_args=[],
crop=None,
scale=None,
gray=False,
skip_nonkey=False,
fps_rate=2.0,
)
joined = " ".join(cmd)
self.assertNotIn("skip_frame", joined)
self.assertIn("fps=2.0", joined)
self.assertIn("-pix_fmt bgr24", joined)
def test_hwaccel_inserts_hwdownload(self):
cmd = build_vod_decode_command(
"ffmpeg",
self.URL,
decode_args=["-hwaccel", "vaapi"],
crop=None,
scale=None,
gray=True,
skip_nonkey=True,
fps_rate=None,
)
joined = " ".join(cmd)
self.assertIn("hwdownload", joined)
self.assertIn("format=nv12", joined)
class TestProbeVodKeyframePts(unittest.TestCase):
def test_parses_keyframe_packets(self):
sample = (
'{"packets":['
'{"pts_time":"0.000000","flags":"K__"},'
'{"pts_time":"1.000000","flags":"___"},'
'{"pts_time":"2.000000","flags":"K__"}]}'
)
completed = mock.Mock(stdout=sample, returncode=0)
with mock.patch(
"frigate.jobs.motion_search_decode.sp.run", return_value=completed
):
pts = probe_vod_keyframe_pts("ffprobe", "http://x/index.m3u8")
self.assertEqual(pts, [0.0, 2.0])
def test_returns_empty_on_failure(self):
with mock.patch(
"frigate.jobs.motion_search_decode.sp.run",
side_effect=OSError("boom"),
):
self.assertEqual(probe_vod_keyframe_pts("ffprobe", "http://x"), [])
class TestProbeVideoDimensions(unittest.TestCase):
def test_parses_dimensions_and_fps(self):
sample = (
'{"streams":[{"width":1920,"height":1080,"avg_frame_rate":"30000/1001"}]}'
)
completed = mock.Mock(stdout=sample, returncode=0)
with mock.patch(
"frigate.jobs.motion_search_decode.sp.run", return_value=completed
):
dims = probe_video_dimensions("ffprobe", "/tmp/a.mp4")
assert dims is not None
width, height, fps = dims
self.assertEqual((width, height), (1920, 1080))
self.assertAlmostEqual(fps, 29.97, places=2)
def test_returns_none_on_zero_dimensions(self):
sample = '{"streams":[{"width":0,"height":0,"avg_frame_rate":"0/0"}]}'
completed = mock.Mock(stdout=sample, returncode=0)
with mock.patch(
"frigate.jobs.motion_search_decode.sp.run", return_value=completed
):
self.assertIsNone(probe_video_dimensions("ffprobe", "/tmp/a.mp4"))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,87 @@
"""Tests for motion search spatial (crop/scale/mask) helpers."""
import unittest
import numpy as np
from frigate.jobs.motion_search import (
build_scaled_roi_mask,
compute_roi_crop_and_scale,
detect_motion_scaled,
)
class TestComputeRoiCropAndScale(unittest.TestCase):
def test_crop_box_in_record_pixels(self):
# ROI covering x [0.25, 0.75], y [0.5, 1.0] of a 1000x600 frame.
polygon = [[0.25, 0.5], [0.75, 0.5], [0.75, 1.0], [0.25, 1.0]]
crop, scaled = compute_roi_crop_and_scale(polygon, 1000, 600, scale_target=125)
cw, ch, cx, cy = crop
self.assertEqual((cx, cy), (250, 300))
self.assertEqual((cw, ch), (500, 300))
# longest side 500 -> factor 0.25 -> (125, 75), rounded down to even.
self.assertEqual(scaled, (124, 74))
def test_never_upscales(self):
polygon = [[0.0, 0.0], [0.1, 0.0], [0.1, 0.1], [0.0, 0.1]]
crop, scaled = compute_roi_crop_and_scale(polygon, 200, 200, scale_target=400)
cw, ch, _, _ = crop
# crop is 20x20; target 400 would upscale, so scaled == crop size.
self.assertEqual(scaled, (cw, ch))
def test_scaled_dims_are_at_least_one(self):
polygon = [[0.0, 0.0], [0.02, 0.0], [0.02, 0.02], [0.0, 0.02]]
crop, scaled = compute_roi_crop_and_scale(polygon, 50, 50, scale_target=1)
self.assertGreaterEqual(scaled[0], 1)
self.assertGreaterEqual(scaled[1], 1)
def test_all_dims_are_even_for_nv12(self):
# Odd-aligned ROI on an odd-ish frame must still yield even crop/scale so
# the nv12 hwdownload byte stream matches the expected frame size.
polygon = [[0.123, 0.321], [0.777, 0.321], [0.777, 0.901], [0.123, 0.901]]
crop, scaled = compute_roi_crop_and_scale(polygon, 1377, 911, scale_target=257)
for value in (*crop, *scaled):
self.assertEqual(value % 2, 0, f"{value} is not even")
class TestBuildScaledRoiMask(unittest.TestCase):
def test_mask_matches_scaled_dims_and_has_coverage(self):
polygon = [[0.25, 0.5], [0.75, 0.5], [0.75, 1.0], [0.25, 1.0]]
crop, scaled = compute_roi_crop_and_scale(polygon, 1000, 600, scale_target=125)
mask = build_scaled_roi_mask(polygon, 1000, 600, crop, scaled)
self.assertEqual(mask.shape, (scaled[1], scaled[0]))
self.assertEqual(mask.dtype, np.uint8)
# A full rectangle ROI fills its whole crop -> mask is all 255.
self.assertGreater(np.count_nonzero(mask), 0)
self.assertEqual(np.count_nonzero(mask), mask.size)
class TestDetectMotionScaled(unittest.TestCase):
def _ts(self, idx):
return float(idx)
def test_finds_change_between_frames(self):
mask = np.full((60, 80), 255, dtype=np.uint8)
f0 = np.zeros((60, 80), dtype=np.uint8)
f1 = np.zeros((60, 80), dtype=np.uint8)
f1[10:50, 20:60] = 255 # big bright block appears
frames = [(0, f0), (30, f1)]
results = detect_motion_scaled(
frames, mask, threshold=30, min_area=1.0, timestamp_fn=self._ts
)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].timestamp, 30.0)
self.assertGreater(results[0].change_percentage, 0.0)
def test_no_change_yields_nothing(self):
mask = np.full((60, 80), 255, dtype=np.uint8)
f0 = np.zeros((60, 80), dtype=np.uint8)
f1 = np.zeros((60, 80), dtype=np.uint8)
results = detect_motion_scaled(
[(0, f0), (30, f1)], mask, threshold=30, min_area=1.0, timestamp_fn=self._ts
)
self.assertEqual(results, [])
if __name__ == "__main__":
unittest.main()
+124
View File
@@ -0,0 +1,124 @@
import unittest
from unittest.mock import AsyncMock, MagicMock, patch
from zeep.exceptions import Fault, TransportError
from zeep.transports import AsyncTransport
from frigate.api.camera import _build_digest_transport, _connect_onvif_camera
def _make_camera(update_side_effect=None):
"""Build a mock ONVIFCamera whose update_xaddrs can raise or succeed."""
camera = MagicMock()
camera.update_xaddrs = AsyncMock(side_effect=update_side_effect)
return camera
class TestConnectOnvifCamera(unittest.IsolatedAsyncioTestCase):
async def test_password_digest_succeeds_first(self):
# Cameras that accept PasswordDigest authenticate on the first attempt
# and should never trigger the PasswordText fallback.
camera = _make_camera()
with patch("frigate.api.camera.ONVIFCamera", return_value=camera) as mock_cls:
result = await _connect_onvif_camera(
"cam.local", 80, "user", "pass", None, "basic"
)
self.assertIs(result, camera)
mock_cls.assert_called_once()
self.assertTrue(mock_cls.call_args.kwargs["encrypt"])
async def test_falls_back_to_password_text(self):
# A PasswordDigest rejection should retry once with PasswordText.
camera_digest = _make_camera(update_side_effect=Fault("token rejected"))
camera_text = _make_camera()
with patch(
"frigate.api.camera.ONVIFCamera",
side_effect=[camera_digest, camera_text],
) as mock_cls:
result = await _connect_onvif_camera(
"cam.local", 80, "user", "pass", None, "basic"
)
self.assertIs(result, camera_text)
self.assertEqual(mock_cls.call_count, 2)
self.assertTrue(mock_cls.call_args_list[0].kwargs["encrypt"])
self.assertFalse(mock_cls.call_args_list[1].kwargs["encrypt"])
async def test_both_encodings_fail_raises_first_fault(self):
# When both encodings fault, the original (PasswordDigest) fault is
# surfaced so the caller's existing Fault handler reports it.
first_fault = Fault("digest rejected")
camera_digest = _make_camera(update_side_effect=first_fault)
camera_text = _make_camera(update_side_effect=Fault("text rejected"))
with patch(
"frigate.api.camera.ONVIFCamera",
side_effect=[camera_digest, camera_text],
) as mock_cls:
with self.assertRaises(Fault) as ctx:
await _connect_onvif_camera(
"cam.local", 80, "user", "pass", None, "basic"
)
self.assertIs(ctx.exception, first_fault)
self.assertEqual(mock_cls.call_count, 2)
async def test_transport_error_is_not_retried(self):
# Connection-level errors (timeout, refused, unreachable) should
# propagate immediately without doubling latency on a second encoding.
camera = _make_camera(update_side_effect=TransportError("unreachable"))
with patch("frigate.api.camera.ONVIFCamera", side_effect=[camera]) as mock_cls:
with self.assertRaises(TransportError):
await _connect_onvif_camera(
"cam.local", 80, "user", "pass", None, "basic"
)
mock_cls.assert_called_once()
async def test_digest_auth_replaces_service_transports(self):
# auth_type "digest" wires an HTTP digest transport onto each service,
# independently of the WS-Security encoding.
camera = _make_camera()
with (
patch("frigate.api.camera.ONVIFCamera", return_value=camera),
patch(
"frigate.api.camera._build_digest_transport",
return_value="TRANSPORT",
) as mock_transport,
):
result = await _connect_onvif_camera(
"cam.local", 80, "user", "pass", None, "digest"
)
self.assertIs(result, camera)
mock_transport.assert_called_once_with("user", "pass")
self.assertEqual(camera.devicemgmt.zeep_client.transport, "TRANSPORT")
self.assertEqual(camera.media.zeep_client.transport, "TRANSPORT")
self.assertEqual(camera.ptz.zeep_client.transport, "TRANSPORT")
async def test_basic_auth_does_not_replace_transports(self):
# Without digest auth, no transport override is built.
camera = _make_camera()
with (
patch("frigate.api.camera.ONVIFCamera", return_value=camera),
patch("frigate.api.camera._build_digest_transport") as mock_transport,
):
await _connect_onvif_camera("cam.local", 80, "user", "pass", None, "basic")
mock_transport.assert_not_called()
class TestBuildDigestTransport(unittest.TestCase):
def test_returns_async_transport(self):
transport = _build_digest_transport("user", "pass")
self.assertIsInstance(transport, AsyncTransport)
if __name__ == "__main__":
unittest.main()
+31
View File
@@ -1,5 +1,6 @@
"""Tests for the profiles system."""
import copy
import json
import os
import unittest
@@ -746,6 +747,36 @@ class TestProfileManager(unittest.TestCase):
manager.activate_profile(None)
dispatcher.clear_runtime_state.assert_called_once_with()
@patch.object(ProfileManager, "_persist_active_profile")
def test_profile_change_republishes_switch_states(self, mock_persist):
"""Profile changes republish MQTT switch states so HA stays in sync.
Regression: activating/deactivating a profile updated the in-memory
config (and Frigate's behavior) but left the retained MQTT state
topics stale, so external integrations like Home Assistant kept
showing the pre-profile toggle position.
"""
config_data = copy.deepcopy(self.config_data)
config_data["cameras"]["front"]["profiles"]["disarmed"]["review"] = {
"alerts": {"enabled": False},
}
config = FrigateConfig(**config_data)
dispatcher = MagicMock()
manager = ProfileManager(config, self.mock_updater, dispatcher)
# Activating disarmed turns alerts off -> MQTT state must follow
manager.activate_profile("disarmed")
dispatcher.publish.assert_any_call(
"front/review_alerts/state", "OFF", retain=True
)
# Deactivating restores the base (alerts on) -> MQTT state must follow
dispatcher.publish.reset_mock()
manager.activate_profile(None)
dispatcher.publish.assert_any_call(
"front/review_alerts/state", "ON", retain=True
)
@patch.object(ProfileManager, "_persist_active_profile")
def test_startup_replay_does_not_clear_runtime_state(self, mock_persist):
"""Startup callers pass clear_runtime_overrides=False to preserve state."""
+22 -1
View File
@@ -14,13 +14,16 @@ import urllib.parse
from collections.abc import Mapping
from multiprocessing.managers import ValueProxy
from pathlib import Path
from typing import Any, Dict, Optional, Tuple, Union
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
import numpy as np
from ruamel.yaml import YAML
from frigate.const import REGEX_HTTP_CAMERA_USER_PASS, REGEX_RTSP_CAMERA_USER_PASS
if TYPE_CHECKING:
from frigate.config import CameraConfig
logger = logging.getLogger(__name__)
@@ -132,6 +135,24 @@ def get_ffmpeg_arg_list(arg: Any) -> list:
return arg if isinstance(arg, list) else shlex.split(arg)
# all built-in record presets use this segment_time
DEFAULT_RECORD_SEGMENT_TIME = 10
def get_record_segment_time(config: "CameraConfig") -> int:
"""Extract -segment_time from the camera's record output args."""
record_args = get_ffmpeg_arg_list(config.ffmpeg.output_args.record)
if record_args and record_args[0].startswith("preset"):
return DEFAULT_RECORD_SEGMENT_TIME
try:
idx = record_args.index("-segment_time")
return int(record_args[idx + 1])
except (ValueError, IndexError):
return DEFAULT_RECORD_SEGMENT_TIME
def load_labels(
path: Optional[str], encoding="utf-8", prefill=91, indexed: bool | None = None
):
+2 -2
View File
@@ -394,7 +394,7 @@ def collect_state_classification_examples(
# Step 3: Extract keyframes from recordings with crops applied
keyframes = _extract_keyframes(
"/usr/lib/ffmpeg/7.0/bin/ffmpeg", timestamps, temp_dir, cameras
"/usr/lib/ffmpeg/8.0/bin/ffmpeg", timestamps, temp_dir, cameras
)
# Step 4: Select 24 most visually distinct images (they're already cropped)
@@ -566,7 +566,7 @@ def _extract_keyframes(
relative_time = timestamp - recording.start_time
try:
config = FfmpegConfig(path="/usr/lib/ffmpeg/7.0")
config = FfmpegConfig(path="/usr/lib/ffmpeg/8.0")
image_data = get_image_from_recording(
config,
recording.path,
+37 -1
View File
@@ -8,7 +8,13 @@ from typing import Any, Optional, Union
from ruamel.yaml import YAML
from frigate.const import CONFIG_DIR, EXPORT_DIR, REDACTED_CREDENTIAL_SENTINEL
from frigate.const import (
CONFIG_DIR,
DEFAULT_FFMPEG_VERSION,
EXPORT_DIR,
INCLUDED_FFMPEG_VERSIONS,
REDACTED_CREDENTIAL_SENTINEL,
)
from frigate.util.builtin import deep_merge
from frigate.util.services import get_video_properties
@@ -18,6 +24,26 @@ CURRENT_CONFIG_VERSION = "0.18-0"
DEFAULT_CONFIG_FILE = os.path.join(CONFIG_DIR, "config.yml")
def resolve_ffmpeg_path(path: str, binary: str = "ffmpeg") -> str:
"""Resolve an ffmpeg version alias or custom path to a binary path.
A bare version alias that is no longer bundled (for example one that was
dropped when the default version changed) falls back to the default
bundled version so existing configs keep working across an upgrade or a
revert. Custom install paths (anything absolute) are used as-is.
"""
if path == "default" or (
not path.startswith("/") and path not in INCLUDED_FFMPEG_VERSIONS
):
version = DEFAULT_FFMPEG_VERSION
elif path in INCLUDED_FFMPEG_VERSIONS:
version = path
else:
return f"{path}/bin/{binary}"
return f"/usr/lib/ffmpeg/{version}/bin/{binary}"
def redact_credential(obj: dict[str, Any], key: str) -> None:
"""Replace obj[key] with the redaction sentinel if a value is saved, else drop.
@@ -618,6 +644,16 @@ def migrate_018_0(config: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]
new_config["cameras"][name] = camera_config
# Remove deprecated date_style and time_style from global ui config
global_ui = new_config.get("ui", {})
if global_ui.get("date_style") is not None:
del new_config["ui"]["date_style"]
if global_ui.get("time_style") is not None:
del new_config["ui"]["time_style"]
# Remove ui section if empty
if "ui" in new_config and not new_config["ui"]:
del new_config["ui"]
new_config["version"] = "0.18-0"
return new_config
+125
View File
@@ -879,6 +879,131 @@ def ffprobe_stream(ffmpeg, path: str, detailed: bool = False) -> sp.CompletedPro
return result
KEYFRAME_PROBE_WINDOW_SECONDS = 20
KEYFRAME_GAP_WARNING_SECONDS = 4.0
def parse_keyframe_packets(output: str) -> Tuple[List[float], Optional[float]]:
"""Parse ffprobe CSV `pts_time,flags` output.
Returns the presentation timestamps of keyframes (flags containing "K")
and the maximum timestamp observed across all packets.
"""
keyframe_pts: List[float] = []
max_pts: Optional[float] = None
for line in output.splitlines():
parts = line.split(",")
if len(parts) < 2:
continue
try:
pts = float(parts[0])
except ValueError:
continue
if max_pts is None or pts > max_pts:
max_pts = pts
if "K" in parts[1]:
keyframe_pts.append(pts)
return keyframe_pts, max_pts
def classify_keyframe_gaps(
keyframe_pts: List[float], segment_time: int
) -> dict[str, Any]:
"""Classify keyframe spacing for recording suitability.
A camera using a smart/+ codec or a long/variable GOP produces large or
irregular gaps between keyframes, which breaks time-based recording
segmentation. Severity:
- "unknown" when fewer than two keyframes were observed
- "error" when the longest gap exceeds the record segment length
- "warning" when the longest gap exceeds the warning threshold
- "ok" otherwise
"""
thresholds = {
"warning": KEYFRAME_GAP_WARNING_SECONDS,
"error": segment_time,
}
if len(keyframe_pts) < 2:
return {
"keyframe_count": len(keyframe_pts),
"max_gap": None,
"mean_gap": None,
"min_gap": None,
"segment_time": segment_time,
"severity": "unknown",
"thresholds": thresholds,
}
gaps = [b - a for a, b in zip(keyframe_pts, keyframe_pts[1:])]
max_gap = max(gaps)
if max_gap > segment_time:
severity = "error"
elif max_gap > KEYFRAME_GAP_WARNING_SECONDS:
severity = "warning"
else:
severity = "ok"
return {
"keyframe_count": len(keyframe_pts),
"max_gap": round(max_gap, 2),
"mean_gap": round(sum(gaps) / len(gaps), 2),
"min_gap": round(min(gaps), 2),
"segment_time": segment_time,
"severity": severity,
"thresholds": thresholds,
}
async def analyze_record_keyframes(
ffmpeg, url: str, segment_time: int, window: int = KEYFRAME_PROBE_WINDOW_SECONDS
) -> dict[str, Any]:
"""Probe a stream for ~`window` seconds and classify its keyframe spacing.
Reads video packet flags via ffprobe to find keyframes, then measures the
gaps between them. On timeout or failure returns an "unknown" result rather
than a false all-clear.
"""
clean_url = escape_special_characters(url)
cmd = [
ffmpeg.ffprobe_path,
"-v",
"error",
"-select_streams",
"v:0",
"-read_intervals",
f"%+{window}",
"-show_entries",
"packet=pts_time,flags",
"-of",
"csv=p=0",
clean_url,
]
try:
proc = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=window + 15)
except asyncio.TimeoutError:
logger.warning("Keyframe probe timed out for record stream")
proc.kill()
return classify_keyframe_gaps([], segment_time)
except OSError as err:
logger.error("Keyframe probe failed: %s", err)
return classify_keyframe_gaps([], segment_time)
keyframe_pts, max_pts = parse_keyframe_packets(stdout.decode("utf-8", "replace"))
result = classify_keyframe_gaps(keyframe_pts, segment_time)
result["duration_observed"] = round(max_pts, 2) if max_pts is not None else None
return result
def vainfo_hwaccel(device_name: Optional[str] = None) -> sp.CompletedProcess:
"""Run vainfo."""
if not device_name:
+2 -19
View File
@@ -24,7 +24,7 @@ from frigate.config.camera.updater import (
)
from frigate.const import PROCESS_PRIORITY_HIGH
from frigate.log import LogPipe
from frigate.util.builtin import EventsPerSecond, get_ffmpeg_arg_list
from frigate.util.builtin import EventsPerSecond, get_record_segment_time
from frigate.util.ffmpeg import start_or_restart_ffmpeg, stop_ffmpeg
from frigate.util.image import (
FrameManager,
@@ -34,23 +34,6 @@ from frigate.util.process import FrigateProcess
logger = logging.getLogger(__name__)
# all built-in record presets use this segment_time
DEFAULT_RECORD_SEGMENT_TIME = 10
def _get_record_segment_time(config: CameraConfig) -> int:
"""Extract -segment_time from the camera's record output args."""
record_args = get_ffmpeg_arg_list(config.ffmpeg.output_args.record)
if record_args and record_args[0].startswith("preset"):
return DEFAULT_RECORD_SEGMENT_TIME
try:
idx = record_args.index("-segment_time")
return int(record_args[idx + 1])
except (ValueError, IndexError):
return DEFAULT_RECORD_SEGMENT_TIME
def capture_frames(
ffmpeg_process: sp.Popen[Any],
@@ -185,7 +168,7 @@ class CameraWatchdog(threading.Thread):
# `valid` segments are published with the segment's start time, so the
# gap between consecutive publishes can reach 2 * segment_time. Pad the
# staleness threshold so it's never tighter than that worst case.
segment_time = _get_record_segment_time(self.config)
segment_time = get_record_segment_time(self.config)
self.record_stale_threshold = max(120, 2 * segment_time + 30)
# Stall tracking (based on last processed frame)
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
[{"id": "case-001", "name": "Package Theft Investigation", "description": "Review of suspicious activity near the front porch", "created_at": 1775407931.3863528, "updated_at": 1775483531.3863528}]
[{"id": "case-001", "name": "Package Theft Investigation", "description": "Review of suspicious activity near the front porch", "created_at": 1780597809.365581, "updated_at": 1780673409.365581}]
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
[{"id": "event-person-001", "label": "person", "sub_label": null, "camera": "front_door", "start_time": 1775487131.3863528, "end_time": 1775487161.3863528, "false_positive": false, "zones": ["front_yard"], "thumbnail": null, "has_clip": true, "has_snapshot": true, "retain_indefinitely": false, "plus_id": null, "model_hash": "abc123", "detector_type": "cpu", "model_type": "ssd", "data": {"top_score": 0.92, "score": 0.92, "region": [0.1, 0.1, 0.5, 0.8], "box": [0.2, 0.15, 0.45, 0.75], "area": 0.18, "ratio": 0.6, "type": "object", "description": "A person walking toward the front door", "average_estimated_speed": 1.2, "velocity_angle": 45.0, "path_data": [[[0.2, 0.5], 0.0], [[0.3, 0.5], 1.0]]}}, {"id": "event-car-001", "label": "car", "sub_label": null, "camera": "backyard", "start_time": 1775483531.3863528, "end_time": 1775483576.3863528, "false_positive": false, "zones": ["driveway"], "thumbnail": null, "has_clip": true, "has_snapshot": true, "retain_indefinitely": false, "plus_id": null, "model_hash": "def456", "detector_type": "cpu", "model_type": "ssd", "data": {"top_score": 0.87, "score": 0.87, "region": [0.3, 0.2, 0.9, 0.7], "box": [0.35, 0.25, 0.85, 0.65], "area": 0.2, "ratio": 1.25, "type": "object", "description": "A car parked in the driveway", "average_estimated_speed": 0.0, "velocity_angle": 0.0, "path_data": []}}, {"id": "event-person-002", "label": "person", "sub_label": null, "camera": "garage", "start_time": 1775479931.3863528, "end_time": 1775479951.3863528, "false_positive": false, "zones": [], "thumbnail": null, "has_clip": false, "has_snapshot": true, "retain_indefinitely": false, "plus_id": null, "model_hash": "ghi789", "detector_type": "cpu", "model_type": "ssd", "data": {"top_score": 0.78, "score": 0.78, "region": [0.0, 0.0, 0.6, 0.9], "box": [0.1, 0.05, 0.5, 0.85], "area": 0.32, "ratio": 0.5, "type": "object", "description": null, "average_estimated_speed": 0.5, "velocity_angle": 90.0, "path_data": [[[0.1, 0.4], 0.0]]}}]
[{"id": "event-person-001", "label": "person", "sub_label": null, "camera": "front_door", "start_time": 1780677009.365581, "end_time": 1780677039.365581, "false_positive": false, "zones": ["front_yard"], "thumbnail": null, "has_clip": true, "has_snapshot": true, "retain_indefinitely": false, "plus_id": null, "model_hash": "abc123", "detector_type": "cpu", "model_type": "ssd", "data": {"top_score": 0.92, "score": 0.92, "region": [0.1, 0.1, 0.5, 0.8], "box": [0.2, 0.15, 0.45, 0.75], "area": 0.18, "ratio": 0.6, "type": "object", "description": "A person walking toward the front door", "average_estimated_speed": 1.2, "velocity_angle": 45.0, "path_data": [[[0.2, 0.5], 0.0], [[0.3, 0.5], 1.0]]}}, {"id": "event-car-001", "label": "car", "sub_label": null, "camera": "backyard", "start_time": 1780673409.365581, "end_time": 1780673454.365581, "false_positive": false, "zones": ["driveway"], "thumbnail": null, "has_clip": true, "has_snapshot": true, "retain_indefinitely": false, "plus_id": null, "model_hash": "def456", "detector_type": "cpu", "model_type": "ssd", "data": {"top_score": 0.87, "score": 0.87, "region": [0.3, 0.2, 0.9, 0.7], "box": [0.35, 0.25, 0.85, 0.65], "area": 0.2, "ratio": 1.25, "type": "object", "description": "A car parked in the driveway", "average_estimated_speed": 0.0, "velocity_angle": 0.0, "path_data": []}}, {"id": "event-person-002", "label": "person", "sub_label": null, "camera": "garage", "start_time": 1780669809.365581, "end_time": 1780669829.365581, "false_positive": false, "zones": [], "thumbnail": null, "has_clip": false, "has_snapshot": true, "retain_indefinitely": false, "plus_id": null, "model_hash": "ghi789", "detector_type": "cpu", "model_type": "ssd", "data": {"top_score": 0.78, "score": 0.78, "region": [0.0, 0.0, 0.6, 0.9], "box": [0.1, 0.05, 0.5, 0.85], "area": 0.32, "ratio": 0.5, "type": "object", "description": null, "average_estimated_speed": 0.5, "velocity_angle": 90.0, "path_data": [[[0.1, 0.4], 0.0]]}}]
+1 -1
View File
@@ -1 +1 @@
[{"id": "export-001", "camera": "front_door", "name": "Front Door - Person Alert", "date": 1775490731.3863528, "video_path": "/exports/export-001.mp4", "thumb_path": "/exports/export-001-thumb.jpg", "in_progress": false, "export_case_id": null}, {"id": "export-002", "camera": "backyard", "name": "Backyard - Car Detection", "date": 1775483531.3863528, "video_path": "/exports/export-002.mp4", "thumb_path": "/exports/export-002-thumb.jpg", "in_progress": false, "export_case_id": "case-001"}, {"id": "export-003", "camera": "garage", "name": "Garage - In Progress", "date": 1775492531.3863528, "video_path": "/exports/export-003.mp4", "thumb_path": "/exports/export-003-thumb.jpg", "in_progress": true, "export_case_id": null}]
[{"id": "export-001", "camera": "front_door", "name": "Front Door - Person Alert", "date": 1780680609.365581, "video_path": "/exports/export-001.mp4", "thumb_path": "/exports/export-001-thumb.jpg", "in_progress": false, "export_case_id": null}, {"id": "export-002", "camera": "backyard", "name": "Backyard - Car Detection", "date": 1780673409.365581, "video_path": "/exports/export-002.mp4", "thumb_path": "/exports/export-002-thumb.jpg", "in_progress": false, "export_case_id": "case-001"}, {"id": "export-003", "camera": "garage", "name": "Garage - In Progress", "date": 1780682409.365581, "video_path": "/exports/export-003.mp4", "thumb_path": "/exports/export-003-thumb.jpg", "in_progress": true, "export_case_id": null}]
@@ -1 +1 @@
{"2026-04-06": {"day": "2026-04-06", "reviewed_alert": 1, "reviewed_detection": 0, "total_alert": 2, "total_detection": 2}, "2026-04-05": {"day": "2026-04-05", "reviewed_alert": 3, "reviewed_detection": 2, "total_alert": 3, "total_detection": 4}}
{"2026-06-05": {"day": "2026-06-05", "reviewed_alert": 1, "reviewed_detection": 0, "total_alert": 2, "total_detection": 2}, "2026-06-04": {"day": "2026-06-04", "reviewed_alert": 3, "reviewed_detection": 2, "total_alert": 3, "total_detection": 4}}
+1 -1
View File
@@ -1 +1 @@
[{"id": "review-alert-001", "camera": "front_door", "start_time": "2026-04-06T09:52:11.386353", "end_time": "2026-04-06T09:52:41.386353", "has_been_reviewed": false, "severity": "alert", "thumb_path": "/clips/front_door/review-alert-001-thumb.jpg", "data": {"audio": [], "detections": ["person-abc123"], "objects": ["person"], "sub_labels": [], "significant_motion_areas": [], "zones": ["front_yard"]}}, {"id": "review-alert-002", "camera": "backyard", "start_time": "2026-04-06T08:52:11.386353", "end_time": "2026-04-06T08:52:56.386353", "has_been_reviewed": true, "severity": "alert", "thumb_path": "/clips/backyard/review-alert-002-thumb.jpg", "data": {"audio": [], "detections": ["car-def456"], "objects": ["car"], "sub_labels": [], "significant_motion_areas": [], "zones": ["driveway"]}}, {"id": "review-detect-001", "camera": "garage", "start_time": "2026-04-06T07:52:11.386353", "end_time": "2026-04-06T07:52:31.386353", "has_been_reviewed": false, "severity": "detection", "thumb_path": "/clips/garage/review-detect-001-thumb.jpg", "data": {"audio": [], "detections": ["person-ghi789"], "objects": ["person"], "sub_labels": [], "significant_motion_areas": [], "zones": []}}, {"id": "review-detect-002", "camera": "front_door", "start_time": "2026-04-06T06:52:11.386353", "end_time": "2026-04-06T06:52:26.386353", "has_been_reviewed": false, "severity": "detection", "thumb_path": "/clips/front_door/review-detect-002-thumb.jpg", "data": {"audio": [], "detections": ["car-jkl012"], "objects": ["car"], "sub_labels": [], "significant_motion_areas": [], "zones": ["front_yard"]}}]
[{"id": "review-alert-001", "camera": "front_door", "start_time": "2026-06-05T11:30:09.365581", "end_time": "2026-06-05T11:30:39.365581", "has_been_reviewed": false, "severity": "alert", "thumb_path": "/clips/front_door/review-alert-001-thumb.jpg", "data": {"audio": [], "detections": ["person-abc123"], "objects": ["person"], "sub_labels": [], "significant_motion_areas": [], "zones": ["front_yard"]}}, {"id": "review-alert-002", "camera": "backyard", "start_time": "2026-06-05T10:30:09.365581", "end_time": "2026-06-05T10:30:54.365581", "has_been_reviewed": true, "severity": "alert", "thumb_path": "/clips/backyard/review-alert-002-thumb.jpg", "data": {"audio": [], "detections": ["car-def456"], "objects": ["car"], "sub_labels": [], "significant_motion_areas": [], "zones": ["driveway"]}}, {"id": "review-detect-001", "camera": "garage", "start_time": "2026-06-05T09:30:09.365581", "end_time": "2026-06-05T09:30:29.365581", "has_been_reviewed": false, "severity": "detection", "thumb_path": "/clips/garage/review-detect-001-thumb.jpg", "data": {"audio": [], "detections": ["person-ghi789"], "objects": ["person"], "sub_labels": [], "significant_motion_areas": [], "zones": []}}, {"id": "review-detect-002", "camera": "front_door", "start_time": "2026-06-05T08:30:09.365581", "end_time": "2026-06-05T08:30:24.365581", "has_been_reviewed": false, "severity": "detection", "thumb_path": "/clips/front_door/review-detect-002-thumb.jpg", "data": {"audio": [], "detections": ["car-jkl012"], "objects": ["car"], "sub_labels": [], "significant_motion_areas": [], "zones": ["front_yard"]}}]
+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/images/branding/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>Frigate</title>
<link
rel="apple-touch-icon"
+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/images/branding/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Frigate</title>
<link
rel="apple-touch-icon"
+78 -3
View File
@@ -23,7 +23,7 @@
"singing": "غناء",
"choir": "فرقة غناء",
"chant": "تَرْنِيم",
"mantra": َرْنِيمَة",
"mantra": عويذة",
"child_singing": "غِنَاء طِفْل",
"synthetic_singing": "غِنَاء اِصْطِنَاعِيّ",
"rapping": "رَاب",
@@ -50,7 +50,7 @@
"hands": "أَيْدِي",
"finger_snapping": "طَقْطَقَة الأَصَابِع",
"clapping": "تَصْفِيق",
"heart_murmur": "لَغَط القَلْب",
"heart_murmur": "نفخة القَلْب",
"cheering": "صِيَاح",
"applause": "تَصْفِيق",
"chatter": "حَدِيث",
@@ -74,5 +74,80 @@
"bus": "حافلة",
"train": "قطار",
"boat": "زورق",
"bird": "طائر"
"bird": "طائر",
"sine_wave": "موجة الإشارة",
"harmonic": "أوزة",
"caw": "نُعَاقُ الغراب",
"owl": "بومة",
"hoot": "صاح",
"flapping_wings": "أجنحة ترفرف",
"dogs": "كلاب",
"rats": "فئران",
"mouse": "فأر",
"patter": "طقطق",
"insect": "حشرة",
"cricket": "كريكيت",
"mosquito": "بعوضة",
"fly": "سافر",
"buzz": "طنين",
"frog": "ضفدع",
"croak": "نق الضفدع",
"snake": "ثعبان",
"rattle": "جلجلية",
"whale_vocalization": "أصوات الحيتان",
"music": "موسيقى",
"musical_instrument": "آلة موسيقية",
"plucked_string_instrument": "آلة وترية",
"guitar": "غيتار",
"electric_guitar": "غيتار كهربائي",
"bass_guitar": "غيتار البيس",
"acoustic_guitar": "غيتار صوتي",
"steel_guitar": "غيتار فولاذي",
"tapping": "نقر",
"strum": "داعب الأ وتار",
"banjo": "البانجو",
"sitar": "سيتار",
"mandolin": "الماندولين",
"zither": "زيثارة",
"ukulele": "أوكوليلي",
"keyboard": "لوحة المفاتيح",
"piano": "بيانو",
"electric_piano": "بيانو كهربائي",
"organ": "أرغن",
"electronic_organ": "الأورغن الإلكتروني",
"hammond_organ": "أورغن هاموند",
"synthesizer": "مُركِّب صوتي",
"sampler": "عينة",
"harpsichord": "بيان القيثاري",
"percussion": "آلات الإيقاع",
"drum_kit": "طقم طبول",
"drum_machine": "آلة الطبول",
"drum": "طبل",
"snare_drum": "طبلة جانبية",
"rimshot": "طقطة",
"drum_roll": "قرع الطبول",
"bass_drum": "طبلة الباس",
"timpani": "الطبول",
"tabla": "طبلة",
"cymbal": "الصنج",
"hi_hat": "هاي-هات",
"wood_block": "كتلة خشبية",
"tambourine": "دف",
"maraca": "ماراكا",
"gong": "غونغ",
"tubular_bells": "أجراس أنبوبية",
"cattle": "ماشية",
"moo": "خوار",
"cowbell": "جرس البقر",
"pig": "خنزير",
"oink": "أوينك",
"goat": "معزة",
"bleat": "ثغاء",
"sheep": "غنم",
"fowl": "الدواجن",
"chicken": "دجاجة",
"cluck": "قرقرة",
"cock_a_doodle_doo": "كوكو-كو-كوووووو",
"turkey": "ديك رومى",
"gobble": "كركرة"
}
+5 -1
View File
@@ -18,5 +18,9 @@
"train": "قطار",
"boat": "زورق",
"bench": "مقعدة",
"bird": "طائر"
"bird": "طائر",
"mouse": "فأر",
"keyboard": "لوحة المفاتيح",
"goat": "معزة",
"sheep": "غنم"
}
+94 -1
View File
@@ -265,5 +265,98 @@
"stomach_rumble": "Къркорене на стомах",
"heartbeat": "Сърцебиене",
"scream": "Вик",
"snicker": "Хихикане"
"snicker": "Хихикане",
"chant": "Скандиране",
"synthetic_singing": "Синтетично Пеене",
"grunt": "Грухтене",
"wheeze": "Хриптене",
"gasp": "Издихание",
"snort": "Смъркане",
"heart_murmur": "Сърдечен Шум",
"cheering": "Радостни Викове",
"yip": "Джавкане",
"howl": "Вой",
"bow_wow": "Кучешки Вой",
"growling": "Ръмжене",
"whimper_dog": "Кучешко Скимтене",
"caterwaul": "Мяукане",
"clip_clop": "Копита",
"cattle": "Добитък",
"bleat": "Блеене",
"fowl": "Домашни Птици",
"honk": "Бибиткане",
"chirp": "Пиукане",
"squawk": "Кряскане/Грачене",
"patter": "Ромолене/Потупване",
"rattle": "Тракане",
"tapping": "Потупване",
"strum": "Звук от струни",
"zither": "Цитра",
"harpsichord": "Клавесин",
"snare_drum": "Малко барабанче",
"rimshot": "Римшот",
"bass_drum": "Голям барабан",
"hi_hat": "Фус",
"wood_block": "Парче дърво",
"electronic_dance_music": "Електронна денс музика",
"music_of_bollywood": "Музика от Боливут",
"traditional_music": "Традиционна Музика",
"soundtrack_music": "Саундтрак музика",
"lullaby": "Приспивна песен",
"video_game_music": "Музика от компютърна игра",
"christmas_music": "Коледна музика",
"dance_music": "Денс музика",
"wedding_music": "Сватбена музика",
"happy_music": "Радостна музика",
"sad_music": "Тъжна музика",
"tender_music": "Нежна музика",
"exciting_music": "Вълнуваща музика",
"angry_music": "Яростна музика",
"scary_music": "Страшна музика",
"wind": "Вятър",
"rustling_leaves": "Шумолящи листа",
"wind_noise": "Шум от вятър",
"rain_on_surface": "Дъжд на повърхност",
"crackle": "Пукане",
"emergency_vehicle": "Кола на спешна помощ",
"engine_knocking": "Чукане от двигател",
"cupboard_open_or_close": "Отваряне или затваряне на шкаф",
"sink": "Мивка",
"bathtub": "Вана",
"hair_dryer": "Сешоар",
"toilet_flush": "Пускане на вода в тоалетна",
"toothbrush": "Четка за зъби",
"electric_toothbrush": "Електрическа четка за зъби",
"vacuum_cleaner": "Прахосмукачка",
"zipper": "Цип",
"keys_jangling": "Дрънкане на ключове",
"coin": "Монета",
"scissors": "Ножица",
"electric_shaver": "Електрическа самобръсначка",
"shuffling_cards": "Разбъркване на карти",
"typing": "Пишене",
"typewriter": "Пишеща машина",
"computer_keyboard": "Клавиатура",
"writing": "Писане на ръка",
"alarm": "Аларма",
"telephone": "Телефон",
"telephone_bell_ringing": "Камбанен звук от телефон",
"ringtone": "Мелодия за звънене",
"telephone_dialing": "Набиране на телефон",
"dial_tone": "Набиране на цифра на телефон",
"busy_signal": "Сигнал заето",
"alarm_clock": "Алармен часовник",
"siren": "Сирена",
"civil_defense_siren": "Сирена на гражданска защита",
"buzzer": "Бъзър",
"smoke_detector": "Детектор за пушек",
"fire_alarm": "Пожарна аларма",
"whistle": "Свиркане",
"steam_whistle": "Парна свирка",
"mechanisms": "Механизми",
"clock": "Часовник",
"tick": "",
"tick-tock": "Тиктакане",
"gears": "Зъбни колела",
"sewing_machine": "Шиеща машина"
}
+6 -1
View File
@@ -19,5 +19,10 @@
"skateboard": "Скейтборд",
"door": "Врата",
"blender": "Блендер",
"person": "Човек"
"person": "Човек",
"sink": "Мивка",
"hair_dryer": "Сешоар",
"toothbrush": "Четка за зъби",
"scissors": "Ножица",
"clock": "Часовник"
}
+6 -2
View File
@@ -50,7 +50,8 @@
"id": "Bahasa Indonesia (Indonesi)",
"ur": "اردو (Urdú)",
"hr": "Hrvatski (croat)",
"bs": "Bosanski (Bosni)"
"bs": "Bosanski (Bosni)",
"zhHant": "繁體中文 (Xinès Tradicional)"
},
"system": "Sistema",
"systemMetrics": "Mètriques del sistema",
@@ -323,5 +324,8 @@
"internalID": "L'ID intern que Frigate s'utilitza a la configuració i a la base de dades"
},
"no_items": "Sense elements",
"validation_errors": "Errors de validació"
"validation_errors": "Errors de validació",
"credentialField": {
"savedPlaceholder": "Desat — deixa en blanc per mantenir l'actual"
}
}
+4 -1
View File
@@ -68,7 +68,10 @@
},
"success": "El grup de càmeres ({{name}}) ha estat guardat.",
"icon": "Icona",
"label": "Grups de Càmeres"
"label": "Grups de Càmeres",
"showAll": "Mostra tots els grups de càmeres",
"showLess": "Mostra menys",
"editGroups": "Edita els grups de la càmera"
},
"debug": {
"options": {
+2 -1
View File
@@ -48,5 +48,6 @@
"error": {
"submitFrigatePlusFailed": "Error al enviar fotograma a Frigate+"
}
}
},
"cameraOff": "La càmera està apagada"
}
+5 -1
View File
@@ -686,7 +686,7 @@
},
"timestamp_style": {
"label": "Estil de la marca horària",
"description": "Opcions d'estilització per a marques de temps d'alimentació aplicades a enregistraments i instantànies.",
"description": "Opcions d'estilització per a marques de temps aplicades instantànies i la vista de depuració.",
"position": {
"label": "Posició de la marca horària",
"description": "Posició de la marca horària a la imatge (tl/tr/bl/br)."
@@ -866,6 +866,10 @@
"dashboard": {
"label": "Mostra a l'interfície d'usuari",
"description": "Estableix si aquesta càmera és visible a tot arreu a la interfície d'usuari de la Frigate. Desactivar això requerirà editar manualment la configuració per tornar a veure aquesta càmera a la interfície d'usuari."
},
"review": {
"label": "Mostra en la revisió",
"description": "Alterna si aquesta càmera és visible a la revisió (la pàgina de revisió i el seu filtre de càmera, la revisió de moviment i la vista de l'historial)."
}
},
"webui_url": {
+8 -4
View File
@@ -524,11 +524,11 @@
},
"reindex": {
"label": "Reindexa en iniciar",
"description": "Activa un reíndex complet d'objectes rastrejats històrics a la base de dades d'incrustacions."
"description": "Activa un reindexat complet d'objectes rastrejats històrics a la base de dades d'incrustacions."
},
"model": {
"label": "Model de cerca semàntica o nom del proveïdor GenAI",
"description": "El model d'incrustació a utilitzar per a la cerca semàntica (per exemple 'jinav1'), o el nom d'un proveïdor de GenAI amb el rol d'incrustació."
"description": "El model de vectors a utilitzar per a la cerca semàntica (per exemple 'jinav1'), o el nom d'un proveïdor de GenAI amb el rol de vectors."
},
"model_size": {
"label": "Mida del model",
@@ -808,7 +808,7 @@
},
"model_size": {
"label": "Mida del model",
"description": "Mida del model a utilitzar per a incrustacions facials (petit/gran); més gran pot requerir GPU."
"description": "Mida del model a utilitzar per als vectors facials (petit/gran); més gran pot requerir GPU."
},
"unknown_score": {
"label": "Llindar de puntuació desconegut",
@@ -984,7 +984,7 @@
},
"default_role": {
"label": "Rol predeterminat",
"description": "Rol predeterminat assignat als usuaris intermediaris autenticats quan no s'aplica cap mapatge de rols (administrador o visor)."
"description": "Rol predeterminat assignat als usuaris intermediaris autenticats quan no s'aplica cap mapatge de rols."
},
"separator": {
"label": "Caràcter separador",
@@ -2337,6 +2337,10 @@
"dashboard": {
"label": "Mostra a la interfície",
"description": "Estableix si aquesta càmera és visible a tot arreu a la interfície d'usuari de Frigate. Desactivar això requerirà editar manualment la configuració per tornar a veure aquesta càmera a la interfície d'usuari."
},
"review": {
"label": "Mostra en la revisió",
"description": "Alterna si aquesta càmera és visible a la revisió (la pàgina de revisió i el seu filtre de càmera, la revisió de moviment i la vista de l'historial)."
}
},
"profiles": {
@@ -28,5 +28,8 @@
"detectRequired": "Almenys un flux d'entrada ha de tenir assignat el rol «detecta».",
"hwaccelDetectOnly": "Només el flux d'entrada amb el rol detect pot definir arguments d'acceleració del maquinari."
}
},
"detect": {
"dimensionMustBeEven": "Ha de ser un nombre parell."
}
}
+3
View File
@@ -65,5 +65,8 @@
"active": "Raonant…",
"show": "Mostra el raonament",
"hide": "Amaga el raonament"
},
"thinking": {
"toggle": "Commuta el pensament"
}
}
+4 -4
View File
@@ -2,10 +2,10 @@
"exploreIsUnavailable": {
"downloadingModels": {
"tips": {
"context": "Potser voldreu reindexar les incrustacions dels objectes seguits un cop s'hagin descarregat els models.",
"context": "Potser voldreu reindexar els vectors dels objectes seguits un cop s'hagin descarregat els models.",
"documentation": "Llegir la documentació"
},
"context": "Frigate està descarregant els models d'embeddings necessaris per a donar suport a la funció de cerca semàntica. Això pot trigar diversos minuts, depenent de la velocitat de la teva connexió de xarxa.",
"context": "El Frigate està baixant els models de vectors necessaris per a admetre la funció de Cerca Semàntica. Això pot trigar uns quants minuts depenent de la velocitat de la vostra connexió de xarxa.",
"setup": {
"visionModel": "Model de visió",
"visionModelFeatureExtractor": "Extractor de característiques del model de visió",
@@ -248,7 +248,7 @@
"dialog": {
"confirmDelete": {
"title": "Confirmar la supressió",
"desc": "Suprimir aquest objecte rastrejat elimina la instantània, qualsevol incrustació desada, i qualsevol entrada de detalls de seguiment associada. Les imatges gravades d'aquest objecte seguit en l'historial <em>NO</em> seràn eliminades.<br /><br />Estas segur que vols continuar?"
"desc": "En eliminar aquest objecte detectat, s'esborrarà la instantània, els vectors desats i qualsevol entrada associada als detalls de seguiment d'aquest objecte. El metratge enregistrat d'aquest objecte detectat a la vista de l'Historial <em>NO</em> s'esborrarà.<br /><br />Segur que voleu continuar?"
},
"toast": {
"error": "S'ha produït un error en suprimir aquest objecte rastrejat: {{errorMessage}}"
@@ -282,7 +282,7 @@
"faceOrLicense_plate": "{{attribute}} detectat per {{label}}",
"other": "{{label}} reconegut com a {{attribute}}"
},
"gone": "{{label}} esquerra",
"gone": "{{label}} ha sortit",
"heard": "{{label}} sentit",
"external": "{{label}} detectat",
"header": {
+5 -2
View File
@@ -58,7 +58,9 @@
},
"camera": {
"enable": "Habilitar la càmera",
"disable": "Deshabilita la càmera"
"disable": "Deshabilita la càmera",
"turnOn": "Activa la càmera",
"turnOff": "Apaga la càmera"
},
"muteCameras": {
"enable": "Silencia totes les càmeres",
@@ -151,7 +153,8 @@
"autotracking": "Seguiment automàtic",
"objectDetection": "Detecció d'objectes",
"audioDetection": "Detecció d'àudio",
"transcription": "Transcripció d'audio"
"transcription": "Transcripció d'audio",
"camera": "Càmera"
},
"history": {
"label": "Mostrar gravacions històriques"
+10 -5
View File
@@ -26,7 +26,9 @@
"points_many": "{{count}} punts",
"points_other": "{{count}} punts",
"undo": "Desfés l'últim punt",
"reset": "Restableix el polígon"
"reset": "Restableix el polígon",
"drawMode": "Dibuxa",
"moveMode": "Moure"
},
"motionHeatmapLabel": "Mapa de calor del moviment",
"dialog": {
@@ -42,11 +44,11 @@
"settings": {
"title": "Configuració de la cerca",
"parallelMode": "Mode paral·lel",
"parallelModeDesc": "Escaneja múltiples segments d'enregistrament al mateix temps (més ràpid, però significativament més intensiu en CPU)",
"parallelModeDesc": "Escaneja múltiples intervals d'enregistrament al mateix temps (més ràpid; utilitza més recursos de descodificació)",
"threshold": "Llindar de la sensibilitat",
"thresholdDesc": "Els valors més baixos detecten canvis més petits (1-255)",
"minArea": "Àrea de canvi mínim",
"minAreaDesc": "Percentatge mínim de la regió d'interès que s'ha de canviar per considerar-se significatiu",
"minAreaDesc": "Mida mínima d'una sola regió en moviment, com a percentatge de la regió d'interès",
"frameSkip": "Omet el fotograma",
"frameSkipDesc": "Processa cada N fotograma. Establiu això a la velocitat de fotogrames de la càmera per processar un fotograma per segon (p. ex. 5 per a una càmera de 5 FPS, 30 per a una càmera de 30 FPS). Els valors més alts seran més ràpids, però poden perdre els esdeveniments de curt moviment.",
"maxResults": "Resultats màxims",
@@ -72,6 +74,9 @@
"framesDecoded": "Fotogrames descodificats",
"wallTime": "Temps de cerca",
"segmentErrors": "Errors del segment",
"seconds": "{{seconds}}s"
}
"seconds": "{{seconds}}s",
"scanSummary": "{{segments}} segments · {{time}}",
"minutesSeconds": "{{minutes}}m {{seconds}}s"
},
"scanning": "S'està analitzant {{time}}"
}
+1 -1
View File
@@ -55,5 +55,5 @@
"goToReplay": "Ves a la repetició"
}
},
"description": "Reprodueix els enregistraments de la càmera per a la depuració. La llista d'objectes mostra un resum retardat en el temps dels objectes detectats i la pestanya Missatges mostra un flux de missatges interns de la fragata a partir del metratge de reproducció."
"description": "Reprodueix els enregistraments de la càmera per a la depuració. La llista d'objectes mostra un resum retardat en el temps dels objectes detectats i la pestanya Missatges mostra un flux de missatges interns de frigate a partir del metratge de reproducció."
}
+182 -27
View File
@@ -29,7 +29,7 @@
"frigateplus": "Frigate+",
"enrichments": "Enriquiments",
"triggers": "Disparadors",
"cameraManagement": "Gestió",
"cameraManagement": "Gestió de la càmera",
"cameraReview": "Revisió",
"roles": "Rols",
"general": "General",
@@ -136,7 +136,7 @@
"clearAll": "Esborra tots els paràmetres de transmissió"
},
"recordingsViewer": {
"title": "Visor d'enregistraments",
"title": "Visualitzador d'enregistraments",
"defaultPlaybackRate": {
"label": "Velocitat de reproducció predeterminada",
"desc": "Velocitat de reproducció predeterminada per a la reproducció de gravacions."
@@ -426,7 +426,8 @@
"notificationUnavailable": {
"title": "Notificacions no disponibles",
"documentation": "Llegir la documentació",
"desc": "Les notificacions push web requereixen un context segur (<code>https://…</code>). Aquesta és una limitació del navegador. Accedeix a Frigate de manera segura per utilitzar les notificacions."
"desc": "Les notificacions push web requereixen un context segur (<code>https://…</code>). Aquesta és una limitació del navegador. Accedeix a Frigate de manera segura per utilitzar les notificacions.",
"descPwa": "A iOS, les notificacions push web només estàn disponibles quan Frigate està instalat a la pantalla principal. Obre el menú <strong>Compartir</strong> , selecciona <strong>Afegir a la pantalla</strong>, i obre Frigate des del nou icona per registrar les notificacions en aquest dispositiu."
},
"unsavedChanges": "Canvis de notificació no desats",
"globalSettings": {
@@ -773,22 +774,22 @@
"modelSize": {
"small": {
"title": "petit",
"desc": "Lopció <em>small</em> fa servir una versió quantitzada del model que consumeix menys RAM i sexecuta més ràpidament a la CPU, amb una diferència gairebé inapreciable en la qualitat de les incrustacions (embeddings)."
"desc": "Si s'utilitza <em>small</em>, s'empra una versió quantitzada del model que consumeix menys memòria RAM i s'executa més ràpidament a la CPU, amb una diferència inapreciable en la qualitat dels vectors."
},
"label": "Mida del model",
"large": {
"title": "gran",
"desc": "Lopció <em>large</em> fa servir el model complet de Jina i sexecutarà automàticament a la GPU si està disponible."
},
"desc": "La mida del model utilitzat per incrustacions de cerca semàntica."
"desc": "La mida del model utilitzat per als vectors de la cerca semàntica."
},
"reindexNow": {
"confirmButton": "Reindexar",
"success": "La reindexació ha començat amb èxit.",
"label": "Reindexar ara",
"confirmTitle": "Confirmar la reindexació",
"desc": "La reindexació regenerarà les incrustacions per a tots els objectes rastrejats. Aquest procés s'executa en segon pla i pot treure el màxim de la CPU i prendre una quantitat de temps raonable depenent del nombre d'objectes rastrejats que tingueu.",
"confirmDesc": "Estàs segur que vols reindexar totes les incrustacions (embeddings) dels objectes seguits? Aquest procés sexecuta en segon pla, però pot arribar a saturar la CPU i trigar bastant temps. Pots seguir-ne el progrés a la pàgina dExplora.",
"desc": "La reindexació tornarà a generar els vectors de tots els objectes detectats. Aquest procés s'executa en segon pla, pot posar la CPU al màxim i trigar una bona estona segons el nombre d'objectes detectats que tingueu.",
"confirmDesc": "Segur que voleu tornar a indexar els vectors de tots els objectes detectats? Aquest procés s'executa en segon pla, però pot posar la CPU al màxim i trigar una bona estona. En podeu veure el progrés a la pàgina Explora.",
"alreadyInProgress": "La reindexació ja està en curs.",
"error": "Error en iniciar la reindexació: {{errorMessage}}"
},
@@ -1059,7 +1060,7 @@
"brands": {
"reolink-rtsp": "No es recomana Reolink RST. Es recomana habilitar HTTP a la configuració de la càmera i reiniciar l'assistent de la càmera."
},
"customUrlRtspRequired": "Els URL personalitzats han de començar amb \"rtsp://\". Es requereix configuració manual per a fluxos de càmera no RTSP."
"customUrlRtspRequired": "Els URL personalitzats han de començar amb \"rtsp://\" o \"rtsps://\". Es requereix configuració manual per a fluxos de càmera no RTSP."
},
"selectBrand": "Seleccioneu la marca de la càmera per a la plantilla d'URL",
"customUrl": "URL de flux personalitzat",
@@ -1303,13 +1304,13 @@
"selectCamera": "Selecciona una càmera",
"backToSettings": "Torna a la configuració de la càmera",
"streams": {
"title": "Habilita / Inhabilita les càmeres",
"title": "Estat i detalls de la càmera",
"desc": "Inhabilita temporalment una càmera fins que es reiniciï la fragata. La inhabilitació d'una càmera atura completament el processament de Frigate dels fluxos d'aquesta càmera. La detecció, l'enregistrament i la depuració no estaran disponibles.<br /> <em>Nota: això no desactiva les retransmissions de go2rtc.</em>",
"enableLabel": "Càmeres habilitades",
"enableDesc": "Inhabilita temporalment una càmera habilitada fins que es reiniciï Frigate. La inhabilitació d'una càmera atura completament el processament de Frigate dels fluxos d'aquesta càmera. La detecció, l'enregistrament i la depuració no estaran disponibles.<br /> <em>Nota: això no inhabilita els restreams go2rtc.</em><br /><br />Drag el handle per reordenar les càmeres tal com apareixen a la interfície d'usuari. L'ordre de les càmeres habilitades es reflectirà en tota la interfície d'usuari, incloent el tauler en viu i els desplegables de selecció de càmeres.",
"disableLabel": "Càmeres inhabilitades",
"disableDesc": "Habilita una càmera que actualment no és visible a la interfície d'usuari i està desactivada a la configuració. Es requereix un reinici de Frigate després d'activar-la.",
"enableSuccess": "{{cameraName}} activat a la configuració. Reinicia Frigate per aplicar els canvis.",
"enableSuccess": "{{cameraName}} activat. Reinicia Frigate a aplicar.",
"friendlyName": {
"edit": "Edita el nom de la pantalla de la càmera",
"title": "Edita el nom de la pantalla",
@@ -1318,7 +1319,30 @@
},
"reorderHandle": "Arrossega per reordenar",
"saving": "S'està desant…",
"saved": "Desat"
"saved": "Desat",
"details": {
"edit": "Edita els detalls de la càmera",
"title": "Edita els detalls de la càmera",
"description": "Actualitza el nom de visualització, l'URL extern i la visibilitat utilitzada per a aquesta càmera a tota la interfície d'usuari de la Fragata.",
"friendlyNameLabel": "Nom a mostrar",
"friendlyNameHelp": "Nom amistós que es mostra per a aquesta càmera a tota la interfície d'usuari de Frigate. Deixeu-ho en blanc per utilitzar l'ID de la càmera.",
"webuiUrlLabel": "URL de la interfície web de la càmera",
"webuiUrlHelp": "URL per a visitar la interfície d'usuari web de la càmera directament des de la vista de depuració. Deixeu-ho en blanc per desactivar l'enllaç.",
"webuiUrlInvalid": "Ha de ser un URL vàlid (p. ex., https://example.com).",
"dashboardLabel": "Mostra al tauler en directe",
"dashboardHelp": "Mostra aquesta càmera al Tauler en viu.",
"reviewLabel": "Mostra a la ressenya",
"reviewHelp": "Mostra aquesta càmera a Revisió, incloent el filtre de càmera, la revisió de moviment i la vista de l'historial."
},
"label": "Estat de la càmera",
"description": "Estableix l'estat operatiu de cada càmera.<br /><br /><strong>A</strong>: els fluxos es processen normalment.<br /><strong>Off</strong>: pausa temporalment el processament. No persisteix a través de reinicis de Frigate.<br /><strong>Inhabilitat</strong>: deixa de processar i desa el canvi a la configuració. Es requereix un reinici per a tornar a habilitar una càmera inhabilitada.<br /><br /><em>Nota: La inhabilitació no afecta els restreams de go2rtc.</em><br /><br />Arrossegueu l'ansa per a reordenar les càmeres actives a mesura que apareguin a tota la interfície d'usuari, inclosos els desplegables de selecció de quadres en viu i de càmera.",
"disabledSubheading": "Desactivat en la configuració",
"status": {
"on": "Engegat",
"off": "Apagat",
"disabled": "Desactivat"
},
"disableSuccess": "{{cameraName}} desactivat i desat a la configuració."
},
"cameraConfig": {
"add": "Afegeix una càmera",
@@ -1364,20 +1388,110 @@
"profiles": {
"title": "Sobreescriu la càmera de perfil",
"selectLabel": "Seleccioneu el perfil",
"description": "Configura quines càmeres estan habilitades o desactivades quan s'activa un perfil. Les càmeres establertes a «Inherit» mantenen el seu estat base habilitat.",
"description": "Configura quines càmeres estan activades o desactivades quan s'activa un perfil. Les càmeres establertes a «herit» mantenen el seu estat per defecte.",
"inherit": "Hereta",
"enabled": "Habilitat",
"disabled": "Desactivat"
"disabled": "Desactivat",
"on": "Engegat",
"off": "Apagat"
},
"cameraType": {
"title": "Tipus de càmera",
"label": "Tipus de càmera",
"description": "Estableix el tipus per a cada càmera. Les càmeres LPR dedicades són càmeres d'un sol ús amb un potent zoom òptic per capturar matrícules en vehicles distants. La majoria de les càmeres haurien d'utilitzar el tipus de càmera normal llevat que la càmera sigui específicament per a LPR i tingui una vista molt centrada en les matrícules.",
"dedicatedLpr": "LPR dedicat",
"saveSuccess": "Tipus de càmera actualitzat per {{cameraName}}. Reinicia la fragata per aplicar els canvis.",
"saveSuccess": "Tipus de càmera actualitzat per {{cameraName}}. Reinicia Frigate per aplicar els canvis.",
"normal": "Normal"
},
"description": "Afegiu, editeu i suprimiu les càmeres, controleu quines càmeres estan habilitades, i configureu les superposicions per perfil i tipus de càmera. Per a configurar fluxos, detecció, moviment i altres paràmetres específics de la càmera, trieu la secció específica a Configuració de la càmera."
"description": "Afegiu, editeu i suprimiu les càmeres, controleu l'estat de cada càmera, i configureu les superposicions per perfil i tipus de càmera. Per a configurar fluxos, detecció, moviment i altres paràmetres específics de la càmera, trieu la secció específica a Configuració de la càmera.",
"clone": {
"sectionTitle": "Clona la configuració",
"sectionDescription": "Copia la configuració d'una càmera a una altra càmera o una de nova.",
"button": "Clona la configuració",
"title": "Clona la configuració de la càmera",
"description": "Copia la configuració d'una càmera a una o més càmeres o a una càmera nova. La identitat (nom, nom amigable, URL de la interfície d'usuari web, ordre de visualització) no es copia mai.",
"source": {
"label": "Càmera d'origen",
"placeholder": "Seleccioneu una càmera d'origen",
"required": "Seleccioneu una càmera d'origen"
},
"target": {
"legend": "Objectiu",
"newRadio": "Càmara nova",
"newNameLabel": "Nom de la càmera",
"newNamePlaceholder": "p. ex., porta enrere orporta o porta posterior",
"newNameInvalid": "Es requereix el nom de la càmera",
"newNameCollision": "Ja existeix una càmera amb aquest nom",
"newStreamsForced": "Els fluxos sempre es copien per a una càmera nova.",
"existingCamerasRadio": "Càmeres existents",
"allCameras": "Totes les càmeres",
"existingPlaceholder": "Selecciona almenys una càmera",
"existingDisabled": "No hi ha cap altra càmera a la qual copiar",
"newNameRequired": "Es requereix el nom de la càmera"
},
"categories": {
"legend": "Configuració per clonar",
"description": "Trieu quina configuració voleu copiar de la càmera d'origen.",
"selectAll": "Selecciona-ho tot",
"selectNone": "No en seleccioneu cap",
"resetDefaults": "Restableix als valors predeterminats",
"general": "General",
"spatial": "Paràmetres espacials",
"streams": "Fluxos",
"spatialWarningTitle": "La resolució no coincideix",
"spatialWarning": "La càmera d'origen {{srcCamera}} detecta la resolució ({{srcWidth}}.{{srcHeight}}) difereix de: {{cameras}}. És possible que els polígons no s'alineïn en aquestes càmeres. Aquests valors predeterminats estan desactivats; habiliteu-ho per a copiar tal qual.",
"restartHint": "Reinicia requerit",
"items": {
"record": "Enregistrament",
"snapshots": "Instantànies",
"review": "Revisió",
"motion": "Detecció de moviment",
"objects": "Objectes",
"audio": "Detecció d'àudio",
"audio_transcription": "Transcripció d'àudio",
"notifications": "Notificacions",
"birdseye": "Birdseye",
"timestamp_style": "Estil de la marca horària",
"lpr": "Reconeixement de la matrícula",
"face_recognition": "Reconeixement de cares",
"semantic_search": "Cerca semàntica",
"genai": "IA generativa",
"type": "Tipus de càmera (LPR normal / dedicat)",
"profiles": "Perfils",
"detect": "Detecta les dimensions",
"zones": "Zones",
"motion_mask": "Màscares de moviment",
"object_masks": "Màscares d'objecte",
"ffmpeg_live": "URL i rols de flux",
"mqtt": "MQTT",
"onvif": "ONVIF"
}
},
"footer": {
"changeCount_one": "{{count}} s'aplicarà el canvi",
"changeCount_many": "{{count}} canvis s'aplicaran",
"changeCount_other": "{{count}} canvis s'aplicaran",
"restartNeeded": "Es requerirà reiniciar per a alguns canvis.",
"liveOnly": "Tots els canvis s'aplicaran en viu sense reiniciar.",
"submit": "Clona",
"submitting": "S'està clonant…"
},
"toast": {
"success": "Configuració copiada a {{cameraName}}",
"successWithRestart": "Configuració copiada a {{cameraName}}. Reinicia Frigate per aplicar tots els canvis.",
"successMulti_one": "Configuració copiada a la càmera {{count}}",
"successMulti_many": "Configuració copiada a {{count}} càmeres",
"successMulti_other": "Configuració copiada a {{count}} càmeres",
"successMultiWithRestart_one": "Configuració copiada a la càmera {{count}}. Reinicia Frigate per aplicar tots els canvis.",
"successMultiWithRestart_many": "Configuració copiada a {{count}} càmeres. Reinicia Frigate per aplicar tots els canvis.",
"successMultiWithRestart_other": "Configuració copiada a {{count}} càmeres. Reinicia la fragata per aplicar tots els canvis.",
"partialFailure": "{{successCount}} seccions aplicades; «{{failedSection}}» ha fallat: {{errorMessage}}",
"partialFailureMulti": "S'ha copiat a {{successCount}} càmera(es); ha fallat {{failed}}: {{errorMessage}}",
"newCameraPartialFailure": "S'ha creat la càmera {{cameraName}} però no s'han pogut copiar alguns paràmetres: {{errorMessage}}",
"sourceMissing": "La càmera d'origen ja no existeix",
"submitError": "No s'ha pogut clonar la càmera: {{errorMessage}}"
}
}
},
"cameraReview": {
"object_descriptions": {
@@ -1499,7 +1613,7 @@
"desc": "La quadrícula de regions és una optimització que aprèn on solen aparèixer objectes de diferents mides en el camp de visió de cada càmera. Frigate utilitza aquestes dades per detectar regions de mida eficient. La quadrícula es construeix automàticament amb el temps a partir de dades d'objectes rastrejats.",
"clear": "Neteja la quadrícula de la regió",
"clearConfirmTitle": "Neteja la quadrícula de la regió",
"clearConfirmDesc": "No es recomana netejar la quadrícula de la regió tret que hagi canviat recentment la mida del model del detector o hagi canviat la posició física de la càmera i tingui problemes de seguiment d'objectes. La quadrícula es reconstruirà automàticament amb el temps a mesura que els objectes siguin rastrejats. Es requereix un reinici de la fragata perquè els canvis tinguin efecte.",
"clearConfirmDesc": "No es recomana netejar la quadrícula de la regió tret que hagi canviat recentment la mida del model del detector o hagi canviat la posició física de la càmera i tingui problemes de seguiment d'objectes. La quadrícula es reconstruirà automàticament amb el temps a mesura que els objectes siguin rastrejats. Es requereix un reinici de Frigate perquè els canvis tinguin efecte.",
"clearSuccess": "La quadrícula de la regió s'ha netejat correctament",
"clearError": "Ha fallat en netejar la graella de la regió",
"restartRequired": "Cal reiniciar per a que els canvis de la quadrícula de la regió tinguin efecte"
@@ -1674,7 +1788,7 @@
"searchPlaceholder": "Cerca...",
"genaiRoles": {
"options": {
"embeddings": "Incrustació",
"embeddings": "Vectors",
"vision": "Visió",
"tools": "Eines",
"descriptions": "Descripcions",
@@ -1693,13 +1807,43 @@
},
"addCustomLabel": "Afegeix una etiqueta personalitzada...",
"genaiModel": {
"placeholder": "Selecciona el model…",
"search": "Cerca models…",
"noModels": "No hi ha models disponibles"
"placeholder": "Seleccioneu o introduïu un model…",
"search": "Cerca o introdueix un model…",
"noModels": "No hi ha models disponibles",
"available": "Models disponibles",
"useCustom": "Utilitza \"{{value}}\"",
"refresh": "Actualitza els models",
"probeFailed": "No s'han pogut investigar els models",
"fetchedModels": "S'ha obtingut correctament la llista de models"
},
"knownPlates": {
"namePlaceholder": "per exemple. Cotxe de la parella",
"platePlaceholder": "Matricula o regex"
},
"semanticSearchModelSize": {
"notApplicable": "No aplicable als proveïdors de GenAI"
},
"liveStreams": {
"streamNameLabel": "Nom del flux",
"streamNamePlaceholder": "p. ex., corrent HD principal",
"go2rtcStreamLabel": "flux go2rtc",
"go2rtcStreamPlaceholder": "Selecciona un flux go2rtc",
"go2rtcStreamSearch": "Cerca o introdueix un nom de flux…",
"noGo2rtcStreams": "No s'ha configurat cap flux go2rtc",
"availableStreams": "Fluxos disponibles",
"useCustom": "Utilitza \"{{value}}\"",
"addStream": "Afegeix un flux"
},
"ptzPresets": {
"placeholder": "Selecciona o entra una configuració...",
"search": "Busca o entra una configuració...",
"noPresets": "No hi ha configuracions disponibles",
"available": "Parámetres de Cámera",
"useCustom": "Usa \"{{value}}\""
},
"defaultRole": {
"admin": "Administrar",
"viewer": "Visor"
}
},
"globalConfig": {
@@ -1736,9 +1880,9 @@
"saveAllPartial_other": "{{successCount}} de {{totalCount}} seccions desades. {{failCount}} ha fallat.",
"saveAllFailure": "Ha fallat en desar totes les seccions.",
"applied": "La configuració s'ha aplicat correctament",
"saveAllSuccessRestartRequired_one": "S'ha desat la secció {{count}} correctament. Reinicia la fragata per aplicar els canvis.",
"saveAllSuccessRestartRequired_many": "Totes les {{count}} seccions s'han desat correctament. Reinicia la fragata per aplicar els canvis.",
"saveAllSuccessRestartRequired_other": "Totes les {{count}} seccions s'han desat correctament. Reinicia la fragata per aplicar els canvis."
"saveAllSuccessRestartRequired_one": "S'ha desat la secció {{count}} correctament. Reinicia Frigate per aplicar els canvis.",
"saveAllSuccessRestartRequired_many": "Totes les {{count}} seccions s'han desat correctament. Reinicia Frigate per aplicar els canvis.",
"saveAllSuccessRestartRequired_other": "Totes les {{count}} seccions s'han desat correctament. Reinicia Frigate per aplicar els canvis."
},
"unsavedChanges": "Teniu canvis sense desar",
"confirmReset": "Confirma el restabliment",
@@ -1865,7 +2009,8 @@
"hardwareDxva2": "DXVA2",
"hardwareVideotoolbox": "VideoToolbox"
},
"streamNumber": "Flux {{index}}"
"streamNumber": "Flux {{index}}",
"sourceNumber": "Font {{index}}"
},
"timestampPosition": {
"tl": "A dalt a l'esquerra",
@@ -1889,7 +2034,7 @@
"recordDisabled": "L'enregistrament està desactivat, els elements de revisió no es generaran.",
"detectDisabled": "La detecció d'objectes està desactivada. Els elements de revisió requereixen objectes detectats per categoritzar alertes i deteccions.",
"allNonAlertDetections": "Totes les activitats no alertes s'inclouran com a deteccions.",
"genaiImageSourceRecordingsRecordDisabled": "La font d'imatges està configurada com a 'enregistraments', però l'enregistrament està desactivat. La fragata tornarà a la vista prèvia de les imatges."
"genaiImageSourceRecordingsRecordDisabled": "La font d'imatges està configurada com a 'enregistraments', però l'enregistrament està desactivat. Frigate tornarà a la vista prèvia de les imatges."
},
"audio": {
"noAudioRole": "Cap flux té definit el rol d'àudio. Heu d'habilitar el rol d'àudio per a la detecció d'àudio perquè funcioni."
@@ -1899,7 +2044,13 @@
},
"detect": {
"fpsGreaterThanFive": "No es recomana establir el detect FPS superior a 5. Els valors més alts poden causar problemes de rendiment i no proporcionaran cap benefici.",
"disabled": "La detecció d'objectes està desactivada. Les instantànies, articles de revisió i enriquiments com el reconeixement de rostres, el reconeixement de matrícules i la IA Generativa no funcionaran."
"disabled": "La detecció d'objectes està desactivada. Les instantànies, articles de revisió i enriquiments com el reconeixement de rostres, el reconeixement de matrícules i la IA Generativa no funcionaran.",
"resolutionShouldBeMultipleOfFour": "Per obtenir els millors resultats, detectar l'amplada i l'alçada han de ser múltiples de 4. Altres valors parells poden produir artefactes visuals o una lleugera distorsió en el flux de detecció.",
"aspectRatioMismatch": "L'amplada i alçada que heu introduït no coincideixen amb la relació d'aspecte de la resolució de detecció actual. Això pot produir una imatge estirada o distorsionada.",
"maxFramesSet": "La configuració dels fotogrames màxims anul·la el comportament predeterminat i desactiva el seguiment d'objectes estacionaris. Hi ha molt poques situacions en què això sigui necessari, utilitzeu-lo amb precaució.",
"squareResolution": "Una resolució de detecció quadrada és inusual. L'amplada i l'alçada de la detecció han de coincidir amb la relació d'aspecte de la càmera (per exemple, 16:9), no amb les dimensions del model de detecció d'objectes. Una relació d'aspecte no coincident pot estirar la imatge i reduir la precisió de detecció.",
"resolutionHigh": "Aquesta resolució de detecció és més alta del recomanat i pot causar un ús més elevat dels recursos sense millorar la precisió de detecció. Es recomana una resolució de detecció a o per sota de 1080p per a la majoria de les càmeres.",
"globalResolutionMultipleCameras": "S'estableix una resolució de detecció global mentre es configuren diverses càmeres. Tret que totes les càmeres comparteixin la mateixa resolució i relació d'aspecte, l'amplada i l'alçada de la detecció s'haurien de definir per càmera perquè coincideixi amb la relació d'aspecte nativa de cada càmera."
},
"faceRecognition": {
"globalDisabled": "L'enriquiment del reconeixement facial s'ha d'habilitar perquè les funcions de reconeixement facial funcionin en aquesta càmera.",
@@ -1928,7 +2079,11 @@
"genaiNoDescriptionsProvider": "Heu de configurar un proveïdor de GenAI amb el rol 'descripcions' per a les descripcions que es generaran."
},
"semanticSearch": {
"jinav2SmallModelSize": "La mida 'petita' amb el model Jina V2 té un alt cost de RAM i d'inferència. Es recomana el model 'gran' amb una GPU discreta."
"jinav2SmallModelSize": "La mida 'petita' amb el model Jina V2 té un alt cost de RAM i d'inferència. Es recomana el model 'gran' amb una GPU discreta.",
"modelSizeIgnoredForProvider": "La mida del model només s'aplica als models de Jina incorporats. Aquest valor s'ignorarà quan s'utilitzi un proveïdor d'incrustació GenAI."
},
"onvif": {
"autotrackingNoZones": "Autotraquejar requereix al menys una zona. Defineix una zona per aquesta cámera a Mascares/Zones, després usa'l com a requerit a la part inferior."
}
},
"modelSize": {
+10 -10
View File
@@ -66,10 +66,10 @@
},
"general": {
"detector": {
"memoryUsage": "Ús de memòria del detector",
"memoryUsage": "Ús de la memòria del detector",
"title": "Detectors",
"inferenceSpeed": "Velocitat d'inferència del detector",
"cpuUsage": "Ús de CPU del detector",
"cpuUsage": "Ús de la CPU del detector",
"temperature": "Temperatura del detector",
"cpuUsageInformation": "CPU usada en la preparació d'entrades i sortides desde/cap als models de detecció. Aquest valor no mesura l'utilització d'inferència, encara que usis una GPU o accelerador."
},
@@ -118,11 +118,11 @@
"otherProcesses": {
"title": "Altres processos",
"processMemoryUsage": "Ús de memòria de procés",
"processCpuUsage": "Ús de la CPU del procés",
"processCpuUsage": "Ús de la CPU per procés",
"series": {
"recording": "gravant",
"review_segment": "segment de revisió",
"embeddings": "incrustacions",
"embeddings": "Vectors",
"audio_detector": "detector d'àudio",
"go2rtc": "go2rtc"
}
@@ -220,7 +220,7 @@
},
"lastRefreshed": "Darrera actualització: ",
"stats": {
"reindexingEmbeddings": "Reindexant incrustacions ({{processed}}% completat)",
"reindexingEmbeddings": "Reindexant vectors ({{processed}}% completat)",
"healthy": "El sistema és saludable",
"cameraIsOffline": "{{camera}} està fora de línia",
"ffmpegHighCpuUsage": "{{camera}} te un ús elevat de CPU per FFmpeg ({{ffmpegAvg}}%)",
@@ -234,14 +234,14 @@
"title": "Enriquiments",
"embeddings": {
"face_recognition_speed": "Velocitat de reconeixement facial",
"image_embedding": "Incrustació d'imatges",
"text_embedding": "Incrustació de text",
"image_embedding": "Vectors d'imatges",
"text_embedding": "Vectors de text",
"face_recognition": "Reconeixement de rostres",
"plate_recognition": "Reconeixemnt de matrícules",
"image_embedding_speed": "Velocitat d'ncrustació d'imatges",
"face_embedding_speed": "Velocitat d'incrustació de rostres",
"image_embedding_speed": "Velocitat de generació de vectors",
"face_embedding_speed": "Velocitat de generació de vectors facials",
"plate_recognition_speed": "Velocitat de reconeixement de matrícules",
"text_embedding_speed": "Velocitat d'incrustació de text",
"text_embedding_speed": "Velocitat de generació de vectors de text",
"yolov9_plate_detection": "Detecció de matrícules YOLOv9",
"yolov9_plate_detection_speed": "Velocitat de detecció de matrícules YOLOv9",
"review_description": "Descripció de la revisió",
+9 -1
View File
@@ -116,7 +116,9 @@
"error": "Sledovaný objekt se nepodařilo smazat: {{errorMessage}}",
"success": "Sledovaný objekt úspěšně smazán."
}
}
},
"previousTrackedObject": "Předchozí sledovaný objekt",
"nextTrackedObject": "Následující sledovaný objekt"
},
"objectLifecycle": {
"count": "{{first}} z {{second}}",
@@ -202,6 +204,12 @@
"audioTranscription": {
"label": "Přepsat",
"aria": "Požádat o přepis zvukového záznamu"
},
"showObjectDetails": {
"label": "Zobrazit trasu objektu"
},
"hideObjectDetails": {
"label": "Skrýt trasu objektu"
}
},
"dialog": {
+6 -2
View File
@@ -193,7 +193,8 @@
"gl": "Galego (Galicisch)",
"id": "Bahasa Indonesia (Indonesisch)",
"hr": "Hrvatski (Kroatisch)",
"bs": "Bosnisch"
"bs": "Bosnisch",
"zhHant": "Traditional Chinese"
},
"appearance": "Erscheinung",
"theme": {
@@ -326,5 +327,8 @@
"separatorWithSpace": ", "
},
"no_items": "Keine Artikel",
"validation_errors": "Validierungsfehler"
"validation_errors": "Validierungsfehler",
"credentialField": {
"savedPlaceholder": "Gespeichert leer lassen, um den aktuellen Stand beizubehalten"
}
}

Some files were not shown because too many files have changed in this diff Show More