mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-01 08:45:21 +03:00
Compare commits
3 Commits
a2396db2aa
...
4319118e94
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4319118e94 | ||
|
|
4c689dde8e | ||
|
|
f6f555387e |
@ -240,6 +240,8 @@ birdseye:
|
|||||||
scaling_factor: 2.0
|
scaling_factor: 2.0
|
||||||
# Optional: Maximum number of cameras to show at one time, showing the most recent (default: show all cameras)
|
# Optional: Maximum number of cameras to show at one time, showing the most recent (default: show all cameras)
|
||||||
max_cameras: 1
|
max_cameras: 1
|
||||||
|
# Optional: Frames-per-second to re-send the last composed Birdseye frame when idle (no motion or active updates). (default: shown below)
|
||||||
|
idle_heartbeat_fps: 0.0
|
||||||
|
|
||||||
# Optional: ffmpeg configuration
|
# Optional: ffmpeg configuration
|
||||||
# More information about presets at https://docs.frigate.video/configuration/ffmpeg_presets
|
# More information about presets at https://docs.frigate.video/configuration/ffmpeg_presets
|
||||||
|
|||||||
@ -24,6 +24,11 @@ birdseye:
|
|||||||
restream: True
|
restream: True
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
To improve connection speed when using Birdseye via restream you can enable a small idle heartbeat by setting `birdseye.idle_heartbeat_fps` to a low value (e.g. `1–2`). This makes Frigate periodically push the last frame even when no motion is detected, reducing initial connection latency.
|
||||||
|
|
||||||
|
:::
|
||||||
### Securing Restream With Authentication
|
### Securing Restream With Authentication
|
||||||
|
|
||||||
The go2rtc restream can be secured with RTSP based username / password authentication. Ex:
|
The go2rtc restream can be secured with RTSP based username / password authentication. Ex:
|
||||||
|
|||||||
@ -55,6 +55,12 @@ class BirdseyeConfig(FrigateBaseModel):
|
|||||||
layout: BirdseyeLayoutConfig = Field(
|
layout: BirdseyeLayoutConfig = Field(
|
||||||
default_factory=BirdseyeLayoutConfig, title="Birdseye Layout Config"
|
default_factory=BirdseyeLayoutConfig, title="Birdseye Layout Config"
|
||||||
)
|
)
|
||||||
|
idle_heartbeat_fps: float = Field(
|
||||||
|
default=0.0,
|
||||||
|
ge=0.0,
|
||||||
|
le=10.0,
|
||||||
|
title="Idle heartbeat FPS (0 disables, max 10)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# uses BaseModel because some global attributes are not available at the camera level
|
# uses BaseModel because some global attributes are not available at the camera level
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import os
|
|||||||
import queue
|
import queue
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
@ -791,6 +792,10 @@ class Birdseye:
|
|||||||
self.frame_manager = SharedMemoryFrameManager()
|
self.frame_manager = SharedMemoryFrameManager()
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
self.requestor = InterProcessRequestor()
|
self.requestor = InterProcessRequestor()
|
||||||
|
self.idle_fps: float = self.config.birdseye.idle_heartbeat_fps
|
||||||
|
self._idle_interval: Optional[float] = (
|
||||||
|
(1.0 / self.idle_fps) if self.idle_fps > 0 else None
|
||||||
|
)
|
||||||
|
|
||||||
if config.birdseye.restream:
|
if config.birdseye.restream:
|
||||||
self.birdseye_buffer = self.frame_manager.create(
|
self.birdseye_buffer = self.frame_manager.create(
|
||||||
@ -848,6 +853,15 @@ class Birdseye:
|
|||||||
if frame_layout_changed:
|
if frame_layout_changed:
|
||||||
coordinates = self.birdseye_manager.get_camera_coordinates()
|
coordinates = self.birdseye_manager.get_camera_coordinates()
|
||||||
self.requestor.send_data(UPDATE_BIRDSEYE_LAYOUT, coordinates)
|
self.requestor.send_data(UPDATE_BIRDSEYE_LAYOUT, coordinates)
|
||||||
|
if self._idle_interval:
|
||||||
|
now = time.monotonic()
|
||||||
|
is_idle = len(self.birdseye_manager.camera_layout) == 0
|
||||||
|
if (
|
||||||
|
is_idle
|
||||||
|
and (now - self.birdseye_manager.last_output_time)
|
||||||
|
>= self._idle_interval
|
||||||
|
):
|
||||||
|
self.__send_new_frame()
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
self.converter.join()
|
self.converter.join()
|
||||||
|
|||||||
@ -142,11 +142,14 @@ class TimelineProcessor(threading.Thread):
|
|||||||
timeline_entry[Timeline.data]["attribute"] = list(
|
timeline_entry[Timeline.data]["attribute"] = list(
|
||||||
event_data["attributes"].keys()
|
event_data["attributes"].keys()
|
||||||
)[0]
|
)[0]
|
||||||
timeline_entry[Timeline.data]["attribute_box"] = to_relative_box(
|
|
||||||
camera_config.detect.width,
|
if len(event_data["current_attributes"]) > 0:
|
||||||
camera_config.detect.height,
|
timeline_entry[Timeline.data]["attribute_box"] = to_relative_box(
|
||||||
event_data["current_attributes"][0]["box"],
|
camera_config.detect.width,
|
||||||
)
|
camera_config.detect.height,
|
||||||
|
event_data["current_attributes"][0]["box"],
|
||||||
|
)
|
||||||
|
|
||||||
save = True
|
save = True
|
||||||
elif event_type == EventStateEnum.end:
|
elif event_type == EventStateEnum.end:
|
||||||
timeline_entry[Timeline.class_type] = "gone"
|
timeline_entry[Timeline.class_type] = "gone"
|
||||||
|
|||||||
@ -385,7 +385,8 @@
|
|||||||
"mustNotBeSameWithCamera": "Zone name must not be the same as camera name.",
|
"mustNotBeSameWithCamera": "Zone name must not be the same as camera name.",
|
||||||
"alreadyExists": "A zone with this name already exists for this camera.",
|
"alreadyExists": "A zone with this name already exists for this camera.",
|
||||||
"mustNotContainPeriod": "Zone name must not contain periods.",
|
"mustNotContainPeriod": "Zone name must not contain periods.",
|
||||||
"hasIllegalCharacter": "Zone name contains illegal characters."
|
"hasIllegalCharacter": "Zone name contains illegal characters.",
|
||||||
|
"mustHaveAtLeastOneLetter": "Zone name must have at least one letter."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"distance": {
|
"distance": {
|
||||||
@ -443,7 +444,7 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"title": "Name",
|
"title": "Name",
|
||||||
"inputPlaceHolder": "Enter a name…",
|
"inputPlaceHolder": "Enter a name…",
|
||||||
"tips": "Name must be at least 2 characters and must not be the name of a camera or another zone."
|
"tips": "Name must be at least 2 characters, must have at least one letter, and must not be the name of a camera or another zone."
|
||||||
},
|
},
|
||||||
"inertia": {
|
"inertia": {
|
||||||
"title": "Inertia",
|
"title": "Inertia",
|
||||||
|
|||||||
@ -149,6 +149,11 @@ export default function ZoneEditPane({
|
|||||||
)
|
)
|
||||||
.refine((value: string) => /^[a-zA-Z0-9_-]+$/.test(value), {
|
.refine((value: string) => /^[a-zA-Z0-9_-]+$/.test(value), {
|
||||||
message: t("masksAndZones.form.zoneName.error.hasIllegalCharacter"),
|
message: t("masksAndZones.form.zoneName.error.hasIllegalCharacter"),
|
||||||
|
})
|
||||||
|
.refine((value: string) => /[a-zA-Z]/.test(value), {
|
||||||
|
message: t(
|
||||||
|
"masksAndZones.form.zoneName.error.mustHaveAtLeastOneLetter",
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
inertia: z.coerce
|
inertia: z.coerce
|
||||||
.number()
|
.number()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user