From 618b9ef37b31792b7a4e7947756a8a3fc822bf14 Mon Sep 17 00:00:00 2001 From: George Tsiamasiotis Date: Wed, 25 Sep 2024 09:47:18 +0300 Subject: [PATCH] Make camera_metrics into a class --- frigate/app.py | 49 ++++++++++---------------------------- frigate/camera/__init__.py | 38 +++++++++++++++++++++++++++++ frigate/events/audio.py | 10 ++++---- frigate/stats/util.py | 29 ++++++++++------------ frigate/types.py | 22 +++-------------- frigate/video.py | 31 ++++++++++++------------ 6 files changed, 86 insertions(+), 93 deletions(-) create mode 100644 frigate/camera/__init__.py diff --git a/frigate/app.py b/frigate/app.py index 02e3784dc..6c75ebff7 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -17,6 +17,7 @@ from playhouse.sqliteq import SqliteQueueDatabase import frigate.util as util from frigate.api.auth import hash_password from frigate.api.fastapi_app import create_fastapi_app +from frigate.camera import CameraMetrics from frigate.comms.config_updater import ConfigPublisher from frigate.comms.dispatcher import Communicator, Dispatcher from frigate.comms.event_metadata_updater import ( @@ -66,7 +67,7 @@ from frigate.stats.emitter import StatsEmitter from frigate.stats.util import stats_init from frigate.storage import StorageMaintainer from frigate.timeline import TimelineProcessor -from frigate.types import CameraMetricsTypes, PTZMetricsTypes +from frigate.types import PTZMetricsTypes from frigate.util.builtin import empty_and_close_queue from frigate.util.object import get_camera_regions_grid from frigate.version import VERSION @@ -85,7 +86,7 @@ class FrigateApp: self.detection_out_events: dict[str, MpEvent] = {} self.detection_shms: list[mp.shared_memory.SharedMemory] = [] self.log_queue: Queue = mp.Queue() - self.camera_metrics: dict[str, CameraMetricsTypes] = {} + self.camera_metrics: dict[str, CameraMetrics] = {} self.ptz_metrics: dict[str, PTZMetricsTypes] = {} self.processes: dict[str, int] = {} self.region_grids: dict[str, list[list[dict[str, int]]]] = {} @@ -107,34 +108,9 @@ class FrigateApp: logger.debug(f"Skipping directory: {d}") def init_camera_metrics(self) -> None: + # create camera_metrics for camera_name in self.config.cameras.keys(): - # create camera_metrics - self.camera_metrics[camera_name] = { - "camera_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item] - # issue https://github.com/python/typeshed/issues/8799 - # from mypy 0.981 onwards - "skipped_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item] - # issue https://github.com/python/typeshed/issues/8799 - # from mypy 0.981 onwards - "process_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item] - "detection_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item] - # issue https://github.com/python/typeshed/issues/8799 - # from mypy 0.981 onwards - "detection_frame": mp.Value("d", 0.0), # type: ignore[typeddict-item] - # issue https://github.com/python/typeshed/issues/8799 - # from mypy 0.981 onwards - "read_start": mp.Value("d", 0.0), # type: ignore[typeddict-item] - # issue https://github.com/python/typeshed/issues/8799 - # from mypy 0.981 onwards - "ffmpeg_pid": mp.Value("i", 0), # type: ignore[typeddict-item] - # issue https://github.com/python/typeshed/issues/8799 - # from mypy 0.981 onwards - "frame_queue": mp.Queue(maxsize=2), - "capture_process": None, - "process": None, - "audio_rms": mp.Value("d", 0.0), # type: ignore[typeddict-item] - "audio_dBFS": mp.Value("d", 0.0), # type: ignore[typeddict-item] - } + self.camera_metrics[camera_name] = CameraMetrics() self.ptz_metrics[camera_name] = { "ptz_autotracker_enabled": mp.Value( # type: ignore[typeddict-item] # issue https://github.com/python/typeshed/issues/8799 @@ -469,7 +445,7 @@ class FrigateApp: ), daemon=True, ) - self.camera_metrics[name]["process"] = camera_process + self.camera_metrics[name].process = camera_process camera_process.start() logger.info(f"Camera processor started for {name}: {camera_process.pid}") @@ -485,7 +461,7 @@ class FrigateApp: args=(name, config, self.shm_frame_count(), self.camera_metrics[name]), ) capture_process.daemon = True - self.camera_metrics[name]["capture_process"] = capture_process + self.camera_metrics[name].capture_process = capture_process capture_process.start() logger.info(f"Capture process started for {name}: {capture_process.pid}") @@ -680,23 +656,22 @@ class FrigateApp: self.audio_process.join() # ensure the capture processes are done - for camera in self.camera_metrics.keys(): - capture_process = self.camera_metrics[camera]["capture_process"] + for camera, metrics in self.camera_metrics.items(): + capture_process = metrics.capture_process if capture_process is not None: logger.info(f"Waiting for capture process for {camera} to stop") capture_process.terminate() capture_process.join() # ensure the camera processors are done - for camera in self.camera_metrics.keys(): - camera_process = self.camera_metrics[camera]["process"] + for camera, metrics in self.camera_metrics.items(): + camera_process = metrics.process if camera_process is not None: logger.info(f"Waiting for process for {camera} to stop") camera_process.terminate() camera_process.join() logger.info(f"Closing frame queue for {camera}") - frame_queue = self.camera_metrics[camera]["frame_queue"] - empty_and_close_queue(frame_queue) + empty_and_close_queue(metrics.frame_queue) # ensure the detectors are done for detector in self.detectors.values(): diff --git a/frigate/camera/__init__.py b/frigate/camera/__init__.py new file mode 100644 index 000000000..9a7ce447d --- /dev/null +++ b/frigate/camera/__init__.py @@ -0,0 +1,38 @@ +import multiprocessing as mp +from multiprocessing.sharedctypes import Synchronized +from multiprocessing.synchronize import Event +from typing import Optional + + +class CameraMetrics: + camera_fps: Synchronized + detection_fps: Synchronized + detection_frame: Synchronized + process_fps: Synchronized + skipped_fps: Synchronized + read_start: Synchronized + audio_rms: Synchronized + audio_dBFS: Synchronized + + frame_queue: mp.Queue + + process: Optional[mp.Process] + capture_process: Optional[mp.Process] + ffmpeg_pid: Synchronized + + def __init__(self): + self.camera_fps = mp.Value("d", 0) + self.detection_fps = mp.Value("d", 0) + self.detection_frame = mp.Value("d", 0) + self.process_fps = mp.Value("d", 0) + self.skipped_fps = mp.Value("d", 0) + self.read_start = mp.Value("d", 0) + self.audio_rms = mp.Value("d", 0) + self.audio_dBFS = mp.Value("d", 0) + + self.frame_queue = mp.Queue(maxsize=2) + + self.process = None + self.capture_process = None + self.ffmpeg_pid = mp.Value("i", 0) + diff --git a/frigate/events/audio.py b/frigate/events/audio.py index 207f73767..298fafc78 100644 --- a/frigate/events/audio.py +++ b/frigate/events/audio.py @@ -12,6 +12,7 @@ import numpy as np import requests import frigate.util as util +from frigate.camera import CameraMetrics from frigate.comms.config_updater import ConfigSubscriber from frigate.comms.detections_updater import DetectionPublisher, DetectionTypeEnum from frigate.comms.inter_process import InterProcessRequestor @@ -27,7 +28,6 @@ from frigate.const import ( from frigate.ffmpeg_presets import parse_preset_input from frigate.log import LogPipe from frigate.object_detection import load_labels -from frigate.types import CameraMetricsTypes from frigate.util.builtin import get_ffmpeg_arg_list from frigate.video import start_or_restart_ffmpeg, stop_ffmpeg @@ -69,7 +69,7 @@ class AudioProcessor(util.Process): def __init__( self, config: FrigateConfig, - camera_metrics: dict[str, CameraMetricsTypes], + camera_metrics: dict[str, CameraMetrics], ): super().__init__(name="frigate.audio_manager", daemon=True) @@ -123,7 +123,7 @@ class AudioEventMaintainer(threading.Thread): def __init__( self, camera: CameraConfig, - camera_metrics: dict[str, CameraMetricsTypes], + camera_metrics: dict[str, CameraMetrics], stop_event: threading.Event, ) -> None: super().__init__(name=f"{camera.name}_audio_event_processor") @@ -152,8 +152,8 @@ class AudioEventMaintainer(threading.Thread): audio_as_float = audio.astype(np.float32) rms, dBFS = self.calculate_audio_levels(audio_as_float) - self.camera_metrics[self.config.name]["audio_rms"].value = rms - self.camera_metrics[self.config.name]["audio_dBFS"].value = dBFS + self.camera_metrics[self.config.name].audio_rms.value = rms + self.camera_metrics[self.config.name].audio_dBFS.value = dBFS # only run audio detection when volume is above min_volume if rms >= self.config.audio.min_volume: diff --git a/frigate/stats/util.py b/frigate/stats/util.py index e9a78742f..2a0f251fc 100644 --- a/frigate/stats/util.py +++ b/frigate/stats/util.py @@ -11,10 +11,11 @@ import psutil import requests from requests.exceptions import RequestException +from frigate.camera import CameraMetrics from frigate.config import FrigateConfig from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR from frigate.object_detection import ObjectDetectProcess -from frigate.types import CameraMetricsTypes, StatsTrackingTypes +from frigate.types import StatsTrackingTypes from frigate.util.services import ( get_amd_gpu_stats, get_bandwidth_stats, @@ -49,7 +50,7 @@ def get_latest_version(config: FrigateConfig) -> str: def stats_init( config: FrigateConfig, - camera_metrics: dict[str, CameraMetricsTypes], + camera_metrics: dict[str, CameraMetrics], detectors: dict[str, ObjectDetectProcess], processes: dict[str, int], ) -> StatsTrackingTypes: @@ -245,27 +246,23 @@ def stats_snapshot( stats["cameras"] = {} for name, camera_stats in camera_metrics.items(): - total_detection_fps += camera_stats["detection_fps"].value - pid = camera_stats["process"].pid if camera_stats["process"] else None - ffmpeg_pid = ( - camera_stats["ffmpeg_pid"].value if camera_stats["ffmpeg_pid"] else None - ) + total_detection_fps += camera_stats.detection_fps.value + pid = camera_stats.process.pid if camera_stats.process else None + ffmpeg_pid = camera_stats.ffmpeg_pid.value if camera_stats.ffmpeg_pid else None capture_pid = ( - camera_stats["capture_process"].pid - if camera_stats["capture_process"] - else None + camera_stats.capture_process.pid if camera_stats.capture_process else None ) stats["cameras"][name] = { - "camera_fps": round(camera_stats["camera_fps"].value, 2), - "process_fps": round(camera_stats["process_fps"].value, 2), - "skipped_fps": round(camera_stats["skipped_fps"].value, 2), - "detection_fps": round(camera_stats["detection_fps"].value, 2), + "camera_fps": round(camera_stats.camera_fps.value, 2), + "process_fps": round(camera_stats.process_fps.value, 2), + "skipped_fps": round(camera_stats.skipped_fps.value, 2), + "detection_fps": round(camera_stats.detection_fps.value, 2), "detection_enabled": config.cameras[name].detect.enabled, "pid": pid, "capture_pid": capture_pid, "ffmpeg_pid": ffmpeg_pid, - "audio_rms": round(camera_stats["audio_rms"].value, 4), - "audio_dBFS": round(camera_stats["audio_dBFS"].value, 4), + "audio_rms": round(camera_stats.audio_rms.value, 4), + "audio_dBFS": round(camera_stats.audio_dBFS.value, 4), } stats["detectors"] = {} diff --git a/frigate/types.py b/frigate/types.py index e93391fb0..9bc53dbf4 100644 --- a/frigate/types.py +++ b/frigate/types.py @@ -1,27 +1,11 @@ -from multiprocessing import Queue -from multiprocessing.context import Process from multiprocessing.sharedctypes import Synchronized from multiprocessing.synchronize import Event -from typing import Optional, TypedDict +from typing import TypedDict +from frigate.camera import CameraMetrics from frigate.object_detection import ObjectDetectProcess -class CameraMetricsTypes(TypedDict): - camera_fps: Synchronized - capture_process: Optional[Process] - detection_fps: Synchronized - detection_frame: Synchronized - ffmpeg_pid: Synchronized - frame_queue: Queue - process: Optional[Process] - process_fps: Synchronized - read_start: Synchronized - skipped_fps: Synchronized - audio_rms: Synchronized - audio_dBFS: Synchronized - - class PTZMetricsTypes(TypedDict): ptz_autotracker_enabled: Synchronized ptz_tracking_active: Event @@ -36,7 +20,7 @@ class PTZMetricsTypes(TypedDict): class StatsTrackingTypes(TypedDict): - camera_metrics: dict[str, CameraMetricsTypes] + camera_metrics: dict[str, CameraMetrics] detectors: dict[str, ObjectDetectProcess] started: int latest_frigate_version: str diff --git a/frigate/video.py b/frigate/video.py index 5c3c8a054..02d95864b 100755 --- a/frigate/video.py +++ b/frigate/video.py @@ -11,6 +11,7 @@ import time import cv2 from setproctitle import setproctitle +from frigate.camera import CameraMetrics from frigate.comms.config_updater import ConfigSubscriber from frigate.comms.inter_process import InterProcessRequestor from frigate.config import CameraConfig, DetectConfig, ModelConfig @@ -386,7 +387,9 @@ class CameraCapture(threading.Thread): ) -def capture_camera(name, config: CameraConfig, shm_frame_count: int, process_info): +def capture_camera( + name, config: CameraConfig, shm_frame_count: int, camera_metrics: CameraMetrics +): stop_event = mp.Event() def receiveSignal(signalNumber, frame): @@ -398,15 +401,14 @@ def capture_camera(name, config: CameraConfig, shm_frame_count: int, process_inf threading.current_thread().name = f"capture:{name}" setproctitle(f"frigate.capture:{name}") - frame_queue = process_info["frame_queue"] camera_watchdog = CameraWatchdog( name, config, shm_frame_count, - frame_queue, - process_info["camera_fps"], - process_info["skipped_fps"], - process_info["ffmpeg_pid"], + camera_metrics.frame_queue, + camera_metrics.camera_fps, + camera_metrics.skipped_fps, + camera_metrics.ffmpeg_pid, stop_event, ) camera_watchdog.start() @@ -421,7 +423,7 @@ def track_camera( detection_queue, result_connection, detected_objects_queue, - process_info, + camera_metrics: CameraMetrics, ptz_metrics, region_grid, ): @@ -437,7 +439,7 @@ def track_camera( setproctitle(f"frigate.process:{name}") listen() - frame_queue = process_info["frame_queue"] + frame_queue = camera_metrics.frame_queue frame_shape = config.frame_shape objects_to_track = config.objects.track @@ -469,7 +471,7 @@ def track_camera( object_detector, object_tracker, detected_objects_queue, - process_info, + camera_metrics, objects_to_track, object_filters, stop_event, @@ -542,7 +544,7 @@ def process_frames( object_detector: RemoteObjectDetector, object_tracker: ObjectTracker, detected_objects_queue: mp.Queue, - process_info: dict, + camera_metrics: CameraMetrics, objects_to_track: list[str], object_filters, stop_event, @@ -550,9 +552,6 @@ def process_frames( region_grid, exit_on_empty: bool = False, ): - fps = process_info["process_fps"] - detection_fps = process_info["detection_fps"] - current_frame_time = process_info["detection_frame"] next_region_update = get_tomorrow_at_time(2) config_subscriber = ConfigSubscriber(f"config/detect/{camera_name}") @@ -589,7 +588,7 @@ def process_frames( break continue - current_frame_time.value = frame_time + camera_metrics.detection_frame.value = frame_time ptz_metrics["ptz_frame_time"].value = frame_time frame = frame_manager.get( @@ -839,7 +838,7 @@ def process_frames( continue else: fps_tracker.update() - fps.value = fps_tracker.eps() + camera_metrics.process_fps.value = fps_tracker.eps() detected_objects_queue.put( ( camera_name, @@ -849,7 +848,7 @@ def process_frames( regions, ) ) - detection_fps.value = object_detector.fps.eps() + camera_metrics.detection_fps.value = object_detector.fps.eps() frame_manager.close(f"{camera_name}{frame_time}") motion_detector.stop()