mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-01-26 14:08:30 +03:00
Refactors Birdseye idle frame broadcasting
Simplifies the idle frame broadcasting logic by removing the dedicated thread. The idle frame is now resent directly within the main loop, improving efficiency and reducing complexity. Also, limits the idle heartbeat FPS to a maximum of 10 since the framebuffer is limited to 10 anyway
This commit is contained in:
parent
ce9dd7d7f7
commit
3e102b1bdf
@ -58,7 +58,8 @@ class BirdseyeConfig(FrigateBaseModel):
|
|||||||
idle_heartbeat_fps: float = Field(
|
idle_heartbeat_fps: float = Field(
|
||||||
default=0.0,
|
default=0.0,
|
||||||
ge=0.0,
|
ge=0.0,
|
||||||
title="Idle heartbeat FPS (0 disables)",
|
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
|
||||||
|
|||||||
@ -792,14 +792,7 @@ 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._heartbeat_thread = None
|
self.idle_fps: float = self.config.birdseye.idle_heartbeat_fps
|
||||||
|
|
||||||
# --- Optional idle heartbeat (disabled by default) ---
|
|
||||||
# If FRIGATE_BIRDSEYE_IDLE_FPS > 0, periodically re-send the last frame
|
|
||||||
# when no frames have been output recently. This improves client attach times
|
|
||||||
# without altering default behavior.
|
|
||||||
self.idle_fps = float(self.config.birdseye.idle_heartbeat_fps or 0.0)
|
|
||||||
self.idle_fps = max(0.0, self.idle_fps)
|
|
||||||
self._idle_interval: Optional[float] = (1.0 / self.idle_fps) if self.idle_fps > 0 else None
|
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:
|
||||||
@ -811,16 +804,6 @@ class Birdseye:
|
|||||||
self.converter.start()
|
self.converter.start()
|
||||||
self.broadcaster.start()
|
self.broadcaster.start()
|
||||||
|
|
||||||
|
|
||||||
# Start heartbeat loop only if enabled
|
|
||||||
if self._idle_interval:
|
|
||||||
self._heartbeat_thread = threading.Thread(
|
|
||||||
target=self._idle_heartbeat_loop,
|
|
||||||
name="birdseye_idle_heartbeat",
|
|
||||||
daemon=True,
|
|
||||||
)
|
|
||||||
self._heartbeat_thread.start()
|
|
||||||
|
|
||||||
def __send_new_frame(self) -> None:
|
def __send_new_frame(self) -> None:
|
||||||
frame_bytes = self.birdseye_manager.frame.tobytes()
|
frame_bytes = self.birdseye_manager.frame.tobytes()
|
||||||
|
|
||||||
@ -868,28 +851,12 @@ 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:
|
||||||
def _idle_heartbeat_loop(self) -> None:
|
now = time.monotonic()
|
||||||
"""
|
is_idle = (len(self.birdseye_manager.camera_layout) == 0)
|
||||||
Periodically re-send the last composed frame when idle.
|
if is_idle and (now - self.birdseye_manager.last_output_time) >= self._idle_interval:
|
||||||
Active only if FRIGATE_BIRDSEYE_IDLE_FPS > 0.
|
self.__send_new_frame()
|
||||||
"""
|
|
||||||
# Small sleep granularity to check often without busy-spinning.
|
|
||||||
min_sleep = 0.2
|
|
||||||
while not self.stop_event.is_set():
|
|
||||||
try:
|
|
||||||
if self._idle_interval:
|
|
||||||
now = datetime.datetime.now().timestamp()
|
|
||||||
if (now - self.birdseye_manager.last_output_time) >= self._idle_interval:
|
|
||||||
self.__send_new_frame()
|
|
||||||
finally:
|
|
||||||
# Sleep at the smaller of idle interval or a safe minimum
|
|
||||||
sleep_for = self._idle_interval if self._idle_interval and self._idle_interval < min_sleep else min_sleep
|
|
||||||
time.sleep(sleep_for)
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
self.converter.join()
|
self.converter.join()
|
||||||
self.broadcaster.join()
|
self.broadcaster.join()
|
||||||
if self._heartbeat_thread and self._heartbeat_thread.is_alive():
|
|
||||||
# the thread is daemon=True; join a moment just for cleanliness
|
|
||||||
self._heartbeat_thread.join(timeout=0.2)
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user