forked from Github/frigate
Convert config updating and video/audio queues to use zmq (#9893)
* Add config pub / sub pattern * remove recording from feature metrics * remove audio and feature metrics * Check for updates from all cameras * remove birdseye from camera metrics * remove motion and detection camera metrics * Ensure that all processes are stopped * Stop communicators * Detections * Cleanup video output queue * Use select for time sensitive polls * Use ipc instead of tcp
This commit is contained in:
@@ -14,9 +14,9 @@ import traceback
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from frigate.comms.config_updater import ConfigSubscriber
|
||||
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
||||
from frigate.const import BASE_DIR, BIRDSEYE_PIPE
|
||||
from frigate.types import CameraMetricsTypes
|
||||
from frigate.util.image import (
|
||||
SharedMemoryFrameManager,
|
||||
copy_yuv_to_position,
|
||||
@@ -267,7 +267,6 @@ class BirdsEyeFrameManager:
|
||||
config: FrigateConfig,
|
||||
frame_manager: SharedMemoryFrameManager,
|
||||
stop_event: mp.Event,
|
||||
camera_metrics: dict[str, CameraMetricsTypes],
|
||||
):
|
||||
self.config = config
|
||||
self.mode = config.birdseye.mode
|
||||
@@ -278,7 +277,6 @@ class BirdsEyeFrameManager:
|
||||
self.frame = np.ndarray(self.yuv_shape, dtype=np.uint8)
|
||||
self.canvas = Canvas(width, height, config.birdseye.layout.scaling_factor)
|
||||
self.stop_event = stop_event
|
||||
self.camera_metrics = camera_metrics
|
||||
self.inactivity_threshold = config.birdseye.inactivity_threshold
|
||||
|
||||
if config.birdseye.layout.max_cameras:
|
||||
@@ -435,10 +433,7 @@ class BirdsEyeFrameManager:
|
||||
|
||||
# check if we need to reset the layout because there is a different number of cameras
|
||||
if len(self.active_cameras) - len(active_cameras) == 0:
|
||||
if (
|
||||
len(self.active_cameras) == 1
|
||||
and self.active_cameras[0] == active_cameras[0]
|
||||
):
|
||||
if len(self.active_cameras) == 1 and self.active_cameras != active_cameras:
|
||||
reset_layout = True
|
||||
elif max_camera_refresh:
|
||||
reset_layout = True
|
||||
@@ -675,15 +670,12 @@ class BirdsEyeFrameManager:
|
||||
def update(self, camera, object_count, motion_count, frame_time, frame) -> bool:
|
||||
# don't process if birdseye is disabled for this camera
|
||||
camera_config = self.config.cameras[camera].birdseye
|
||||
|
||||
if not camera_config.enabled:
|
||||
return False
|
||||
|
||||
# get our metrics (sync'd across processes)
|
||||
# which allows us to control it via mqtt (or any other dispatcher)
|
||||
camera_metrics = self.camera_metrics[camera]
|
||||
|
||||
# disabling birdseye is a little tricky
|
||||
if not camera_metrics["birdseye_enabled"].value:
|
||||
if not camera_config.enabled:
|
||||
# if we've rendered a frame (we have a value for last_active_frame)
|
||||
# then we need to set it to zero
|
||||
if self.cameras[camera]["last_active_frame"] > 0:
|
||||
@@ -691,12 +683,9 @@ class BirdsEyeFrameManager:
|
||||
|
||||
return False
|
||||
|
||||
# get the birdseye mode state from camera metrics
|
||||
birdseye_mode = BirdseyeModeEnum.get(camera_metrics["birdseye_mode"].value)
|
||||
|
||||
# update the last active frame for the camera
|
||||
self.cameras[camera]["current_frame"] = frame_time
|
||||
if self.camera_active(birdseye_mode, object_count, motion_count):
|
||||
if self.camera_active(camera_config.mode, object_count, motion_count):
|
||||
self.cameras[camera]["last_active_frame"] = frame_time
|
||||
|
||||
now = datetime.datetime.now().timestamp()
|
||||
@@ -725,7 +714,6 @@ class Birdseye:
|
||||
self,
|
||||
config: FrigateConfig,
|
||||
frame_manager: SharedMemoryFrameManager,
|
||||
camera_metrics: dict[str, CameraMetricsTypes],
|
||||
stop_event: mp.Event,
|
||||
websocket_server,
|
||||
) -> None:
|
||||
@@ -745,9 +733,8 @@ class Birdseye:
|
||||
self.broadcaster = BroadcastThread(
|
||||
"birdseye", self.converter, websocket_server, stop_event
|
||||
)
|
||||
self.birdseye_manager = BirdsEyeFrameManager(
|
||||
config, frame_manager, stop_event, camera_metrics
|
||||
)
|
||||
self.birdseye_manager = BirdsEyeFrameManager(config, frame_manager, stop_event)
|
||||
self.config_subscriber = ConfigSubscriber("config/birdseye/")
|
||||
|
||||
if config.birdseye.restream:
|
||||
self.birdseye_buffer = frame_manager.create(
|
||||
@@ -766,6 +753,19 @@ class Birdseye:
|
||||
frame_time: float,
|
||||
frame,
|
||||
) -> None:
|
||||
# check if there is an updated config
|
||||
while True:
|
||||
(
|
||||
updated_topic,
|
||||
updated_birdseye_config,
|
||||
) = self.config_subscriber.check_for_update()
|
||||
|
||||
if not updated_topic:
|
||||
break
|
||||
|
||||
camera_name = updated_topic.rpartition("/")[-1]
|
||||
self.config.cameras[camera_name].birdseye = updated_birdseye_config
|
||||
|
||||
if self.birdseye_manager.update(
|
||||
camera,
|
||||
len([o for o in current_tracked_objects if not o["stationary"]]),
|
||||
@@ -785,5 +785,6 @@ class Birdseye:
|
||||
pass
|
||||
|
||||
def stop(self) -> None:
|
||||
self.config_subscriber.stop()
|
||||
self.converter.join()
|
||||
self.broadcaster.join()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import logging
|
||||
import multiprocessing as mp
|
||||
import queue
|
||||
import signal
|
||||
import threading
|
||||
from typing import Optional
|
||||
@@ -16,12 +15,12 @@ from ws4py.server.wsgirefserver import (
|
||||
)
|
||||
from ws4py.server.wsgiutils import WebSocketWSGIApplication
|
||||
|
||||
from frigate.comms.detections_updater import DetectionSubscriber, DetectionTypeEnum
|
||||
from frigate.comms.ws import WebSocket
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.output.birdseye import Birdseye
|
||||
from frigate.output.camera import JsmpegCamera
|
||||
from frigate.output.preview import PreviewRecorder
|
||||
from frigate.types import CameraMetricsTypes
|
||||
from frigate.util.image import SharedMemoryFrameManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -29,8 +28,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
def output_frames(
|
||||
config: FrigateConfig,
|
||||
video_output_queue: mp.Queue,
|
||||
camera_metrics: dict[str, CameraMetricsTypes],
|
||||
):
|
||||
threading.current_thread().name = "output"
|
||||
setproctitle("frigate.output")
|
||||
@@ -58,6 +55,8 @@ def output_frames(
|
||||
websocket_server.initialize_websockets_manager()
|
||||
websocket_thread = threading.Thread(target=websocket_server.serve_forever)
|
||||
|
||||
detection_subscriber = DetectionSubscriber(DetectionTypeEnum.video)
|
||||
|
||||
jsmpeg_cameras: dict[str, JsmpegCamera] = {}
|
||||
birdseye: Optional[Birdseye] = None
|
||||
preview_recorders: dict[str, PreviewRecorder] = {}
|
||||
@@ -70,24 +69,24 @@ def output_frames(
|
||||
preview_recorders[camera] = PreviewRecorder(cam_config)
|
||||
|
||||
if config.birdseye.enabled:
|
||||
birdseye = Birdseye(
|
||||
config, frame_manager, camera_metrics, stop_event, websocket_server
|
||||
)
|
||||
birdseye = Birdseye(config, frame_manager, stop_event, websocket_server)
|
||||
|
||||
websocket_thread.start()
|
||||
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
(
|
||||
camera,
|
||||
frame_time,
|
||||
current_tracked_objects,
|
||||
motion_boxes,
|
||||
regions,
|
||||
) = video_output_queue.get(True, 1)
|
||||
except queue.Empty:
|
||||
(topic, data) = detection_subscriber.get_data(timeout=10)
|
||||
|
||||
if not topic:
|
||||
continue
|
||||
|
||||
(
|
||||
camera,
|
||||
frame_time,
|
||||
current_tracked_objects,
|
||||
motion_boxes,
|
||||
regions,
|
||||
) = data
|
||||
|
||||
frame_id = f"{camera}{frame_time}"
|
||||
|
||||
frame = frame_manager.get(frame_id, config.cameras[camera].frame_shape_yuv)
|
||||
@@ -126,19 +125,26 @@ def output_frames(
|
||||
|
||||
previous_frames[camera] = frame_time
|
||||
|
||||
while not video_output_queue.empty():
|
||||
while True:
|
||||
(topic, data) = detection_subscriber.get_data(timeout=0)
|
||||
|
||||
if not topic:
|
||||
break
|
||||
|
||||
(
|
||||
camera,
|
||||
frame_time,
|
||||
current_tracked_objects,
|
||||
motion_boxes,
|
||||
regions,
|
||||
) = video_output_queue.get(True, 10)
|
||||
) = data
|
||||
|
||||
frame_id = f"{camera}{frame_time}"
|
||||
frame = frame_manager.get(frame_id, config.cameras[camera].frame_shape_yuv)
|
||||
frame_manager.delete(frame_id)
|
||||
|
||||
detection_subscriber.stop()
|
||||
|
||||
for jsmpeg in jsmpeg_cameras.values():
|
||||
jsmpeg.stop()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user