mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-01-26 22:18:30 +03:00
Compare commits
3 Commits
a2396db2aa
...
4319118e94
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4319118e94 | ||
|
|
4c689dde8e | ||
|
|
f6f555387e |
@ -240,6 +240,8 @@ birdseye:
|
||||
scaling_factor: 2.0
|
||||
# Optional: Maximum number of cameras to show at one time, showing the most recent (default: show all cameras)
|
||||
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
|
||||
# More information about presets at https://docs.frigate.video/configuration/ffmpeg_presets
|
||||
|
||||
@ -24,6 +24,11 @@ birdseye:
|
||||
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
|
||||
|
||||
The go2rtc restream can be secured with RTSP based username / password authentication. Ex:
|
||||
@ -164,4 +169,4 @@ NOTE: The output will need to be passed with two curly braces `{{output}}`
|
||||
go2rtc:
|
||||
streams:
|
||||
stream1: exec:ffmpeg -hide_banner -re -stream_loop -1 -i /media/BigBuckBunny.mp4 -c copy -rtsp_transport tcp -f rtsp {{output}}
|
||||
```
|
||||
```
|
||||
@ -55,6 +55,12 @@ class BirdseyeConfig(FrigateBaseModel):
|
||||
layout: BirdseyeLayoutConfig = Field(
|
||||
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
|
||||
|
||||
@ -9,6 +9,7 @@ import os
|
||||
import queue
|
||||
import subprocess as sp
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
from typing import Any, Optional
|
||||
|
||||
@ -791,6 +792,10 @@ class Birdseye:
|
||||
self.frame_manager = SharedMemoryFrameManager()
|
||||
self.stop_event = stop_event
|
||||
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:
|
||||
self.birdseye_buffer = self.frame_manager.create(
|
||||
@ -848,6 +853,15 @@ class Birdseye:
|
||||
if frame_layout_changed:
|
||||
coordinates = self.birdseye_manager.get_camera_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:
|
||||
self.converter.join()
|
||||
|
||||
@ -142,11 +142,14 @@ class TimelineProcessor(threading.Thread):
|
||||
timeline_entry[Timeline.data]["attribute"] = list(
|
||||
event_data["attributes"].keys()
|
||||
)[0]
|
||||
timeline_entry[Timeline.data]["attribute_box"] = to_relative_box(
|
||||
camera_config.detect.width,
|
||||
camera_config.detect.height,
|
||||
event_data["current_attributes"][0]["box"],
|
||||
)
|
||||
|
||||
if len(event_data["current_attributes"]) > 0:
|
||||
timeline_entry[Timeline.data]["attribute_box"] = to_relative_box(
|
||||
camera_config.detect.width,
|
||||
camera_config.detect.height,
|
||||
event_data["current_attributes"][0]["box"],
|
||||
)
|
||||
|
||||
save = True
|
||||
elif event_type == EventStateEnum.end:
|
||||
timeline_entry[Timeline.class_type] = "gone"
|
||||
|
||||
@ -385,7 +385,8 @@
|
||||
"mustNotBeSameWithCamera": "Zone name must not be the same as camera name.",
|
||||
"alreadyExists": "A zone with this name already exists for this camera.",
|
||||
"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": {
|
||||
@ -443,7 +444,7 @@
|
||||
"name": {
|
||||
"title": "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": {
|
||||
"title": "Inertia",
|
||||
|
||||
@ -149,6 +149,11 @@ export default function ZoneEditPane({
|
||||
)
|
||||
.refine((value: string) => /^[a-zA-Z0-9_-]+$/.test(value), {
|
||||
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
|
||||
.number()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user