diff --git a/docs/docs/configuration/reference.md b/docs/docs/configuration/reference.md index c12984d18..3d963a5bd 100644 --- a/docs/docs/configuration/reference.md +++ b/docs/docs/configuration/reference.md @@ -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 diff --git a/docs/docs/configuration/restream.md b/docs/docs/configuration/restream.md index 43c421dd3..0ab7a170c 100644 --- a/docs/docs/configuration/restream.md +++ b/docs/docs/configuration/restream.md @@ -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}} -``` +``` \ No newline at end of file diff --git a/frigate/config/camera/birdseye.py b/frigate/config/camera/birdseye.py index b7e8a7117..1e6f0f335 100644 --- a/frigate/config/camera/birdseye.py +++ b/frigate/config/camera/birdseye.py @@ -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 diff --git a/frigate/output/birdseye.py b/frigate/output/birdseye.py index 0939b5ce4..eb23c2573 100644 --- a/frigate/output/birdseye.py +++ b/frigate/output/birdseye.py @@ -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()