Compare commits

..

No commits in common. "4319118e947b6ca49c9bb408d2a0b129abe78b9b" and "a2396db2aa21eeacc9106691d57afd75ec40ba65" have entirely different histories.

7 changed files with 8 additions and 44 deletions

View File

@ -240,8 +240,6 @@ 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

View File

@ -24,11 +24,6 @@ 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. `12`). 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:

View File

@ -55,12 +55,6 @@ 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

View File

@ -9,7 +9,6 @@ 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
@ -792,10 +791,6 @@ 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(
@ -853,15 +848,6 @@ 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()

View File

@ -142,14 +142,11 @@ 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(
if len(event_data["current_attributes"]) > 0: camera_config.detect.width,
timeline_entry[Timeline.data]["attribute_box"] = to_relative_box( camera_config.detect.height,
camera_config.detect.width, event_data["current_attributes"][0]["box"],
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"

View File

@ -385,8 +385,7 @@
"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": {
@ -444,7 +443,7 @@
"name": { "name": {
"title": "Name", "title": "Name",
"inputPlaceHolder": "Enter a name…", "inputPlaceHolder": "Enter a name…",
"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." "tips": "Name must be at least 2 characters and must not be the name of a camera or another zone."
}, },
"inertia": { "inertia": {
"title": "Inertia", "title": "Inertia",

View File

@ -149,11 +149,6 @@ 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()