Make camera_metrics into a class

This commit is contained in:
George Tsiamasiotis 2024-09-25 09:47:18 +03:00
parent 30a3c6dc86
commit 618b9ef37b
6 changed files with 86 additions and 93 deletions

View File

@ -17,6 +17,7 @@ from playhouse.sqliteq import SqliteQueueDatabase
import frigate.util as util import frigate.util as util
from frigate.api.auth import hash_password from frigate.api.auth import hash_password
from frigate.api.fastapi_app import create_fastapi_app from frigate.api.fastapi_app import create_fastapi_app
from frigate.camera import CameraMetrics
from frigate.comms.config_updater import ConfigPublisher from frigate.comms.config_updater import ConfigPublisher
from frigate.comms.dispatcher import Communicator, Dispatcher from frigate.comms.dispatcher import Communicator, Dispatcher
from frigate.comms.event_metadata_updater import ( 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.stats.util import stats_init
from frigate.storage import StorageMaintainer from frigate.storage import StorageMaintainer
from frigate.timeline import TimelineProcessor 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.builtin import empty_and_close_queue
from frigate.util.object import get_camera_regions_grid from frigate.util.object import get_camera_regions_grid
from frigate.version import VERSION from frigate.version import VERSION
@ -85,7 +86,7 @@ class FrigateApp:
self.detection_out_events: dict[str, MpEvent] = {} self.detection_out_events: dict[str, MpEvent] = {}
self.detection_shms: list[mp.shared_memory.SharedMemory] = [] self.detection_shms: list[mp.shared_memory.SharedMemory] = []
self.log_queue: Queue = mp.Queue() 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.ptz_metrics: dict[str, PTZMetricsTypes] = {}
self.processes: dict[str, int] = {} self.processes: dict[str, int] = {}
self.region_grids: dict[str, list[list[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}") logger.debug(f"Skipping directory: {d}")
def init_camera_metrics(self) -> None: def init_camera_metrics(self) -> None:
for camera_name in self.config.cameras.keys():
# create camera_metrics # create camera_metrics
self.camera_metrics[camera_name] = { for camera_name in self.config.cameras.keys():
"camera_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item] self.camera_metrics[camera_name] = CameraMetrics()
# 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.ptz_metrics[camera_name] = { self.ptz_metrics[camera_name] = {
"ptz_autotracker_enabled": mp.Value( # type: ignore[typeddict-item] "ptz_autotracker_enabled": mp.Value( # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799 # issue https://github.com/python/typeshed/issues/8799
@ -469,7 +445,7 @@ class FrigateApp:
), ),
daemon=True, daemon=True,
) )
self.camera_metrics[name]["process"] = camera_process self.camera_metrics[name].process = camera_process
camera_process.start() camera_process.start()
logger.info(f"Camera processor started for {name}: {camera_process.pid}") 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]), args=(name, config, self.shm_frame_count(), self.camera_metrics[name]),
) )
capture_process.daemon = True capture_process.daemon = True
self.camera_metrics[name]["capture_process"] = capture_process self.camera_metrics[name].capture_process = capture_process
capture_process.start() capture_process.start()
logger.info(f"Capture process started for {name}: {capture_process.pid}") logger.info(f"Capture process started for {name}: {capture_process.pid}")
@ -680,23 +656,22 @@ class FrigateApp:
self.audio_process.join() self.audio_process.join()
# ensure the capture processes are done # ensure the capture processes are done
for camera in self.camera_metrics.keys(): for camera, metrics in self.camera_metrics.items():
capture_process = self.camera_metrics[camera]["capture_process"] capture_process = metrics.capture_process
if capture_process is not None: if capture_process is not None:
logger.info(f"Waiting for capture process for {camera} to stop") logger.info(f"Waiting for capture process for {camera} to stop")
capture_process.terminate() capture_process.terminate()
capture_process.join() capture_process.join()
# ensure the camera processors are done # ensure the camera processors are done
for camera in self.camera_metrics.keys(): for camera, metrics in self.camera_metrics.items():
camera_process = self.camera_metrics[camera]["process"] camera_process = metrics.process
if camera_process is not None: if camera_process is not None:
logger.info(f"Waiting for process for {camera} to stop") logger.info(f"Waiting for process for {camera} to stop")
camera_process.terminate() camera_process.terminate()
camera_process.join() camera_process.join()
logger.info(f"Closing frame queue for {camera}") logger.info(f"Closing frame queue for {camera}")
frame_queue = self.camera_metrics[camera]["frame_queue"] empty_and_close_queue(metrics.frame_queue)
empty_and_close_queue(frame_queue)
# ensure the detectors are done # ensure the detectors are done
for detector in self.detectors.values(): for detector in self.detectors.values():

View File

@ -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)

View File

@ -12,6 +12,7 @@ import numpy as np
import requests import requests
import frigate.util as util import frigate.util as util
from frigate.camera import CameraMetrics
from frigate.comms.config_updater import ConfigSubscriber from frigate.comms.config_updater import ConfigSubscriber
from frigate.comms.detections_updater import DetectionPublisher, DetectionTypeEnum from frigate.comms.detections_updater import DetectionPublisher, DetectionTypeEnum
from frigate.comms.inter_process import InterProcessRequestor 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.ffmpeg_presets import parse_preset_input
from frigate.log import LogPipe from frigate.log import LogPipe
from frigate.object_detection import load_labels from frigate.object_detection import load_labels
from frigate.types import CameraMetricsTypes
from frigate.util.builtin import get_ffmpeg_arg_list from frigate.util.builtin import get_ffmpeg_arg_list
from frigate.video import start_or_restart_ffmpeg, stop_ffmpeg from frigate.video import start_or_restart_ffmpeg, stop_ffmpeg
@ -69,7 +69,7 @@ class AudioProcessor(util.Process):
def __init__( def __init__(
self, self,
config: FrigateConfig, config: FrigateConfig,
camera_metrics: dict[str, CameraMetricsTypes], camera_metrics: dict[str, CameraMetrics],
): ):
super().__init__(name="frigate.audio_manager", daemon=True) super().__init__(name="frigate.audio_manager", daemon=True)
@ -123,7 +123,7 @@ class AudioEventMaintainer(threading.Thread):
def __init__( def __init__(
self, self,
camera: CameraConfig, camera: CameraConfig,
camera_metrics: dict[str, CameraMetricsTypes], camera_metrics: dict[str, CameraMetrics],
stop_event: threading.Event, stop_event: threading.Event,
) -> None: ) -> None:
super().__init__(name=f"{camera.name}_audio_event_processor") super().__init__(name=f"{camera.name}_audio_event_processor")
@ -152,8 +152,8 @@ class AudioEventMaintainer(threading.Thread):
audio_as_float = audio.astype(np.float32) audio_as_float = audio.astype(np.float32)
rms, dBFS = self.calculate_audio_levels(audio_as_float) 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_rms.value = rms
self.camera_metrics[self.config.name]["audio_dBFS"].value = dBFS self.camera_metrics[self.config.name].audio_dBFS.value = dBFS
# only run audio detection when volume is above min_volume # only run audio detection when volume is above min_volume
if rms >= self.config.audio.min_volume: if rms >= self.config.audio.min_volume:

View File

@ -11,10 +11,11 @@ import psutil
import requests import requests
from requests.exceptions import RequestException from requests.exceptions import RequestException
from frigate.camera import CameraMetrics
from frigate.config import FrigateConfig from frigate.config import FrigateConfig
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
from frigate.object_detection import ObjectDetectProcess from frigate.object_detection import ObjectDetectProcess
from frigate.types import CameraMetricsTypes, StatsTrackingTypes from frigate.types import StatsTrackingTypes
from frigate.util.services import ( from frigate.util.services import (
get_amd_gpu_stats, get_amd_gpu_stats,
get_bandwidth_stats, get_bandwidth_stats,
@ -49,7 +50,7 @@ def get_latest_version(config: FrigateConfig) -> str:
def stats_init( def stats_init(
config: FrigateConfig, config: FrigateConfig,
camera_metrics: dict[str, CameraMetricsTypes], camera_metrics: dict[str, CameraMetrics],
detectors: dict[str, ObjectDetectProcess], detectors: dict[str, ObjectDetectProcess],
processes: dict[str, int], processes: dict[str, int],
) -> StatsTrackingTypes: ) -> StatsTrackingTypes:
@ -245,27 +246,23 @@ def stats_snapshot(
stats["cameras"] = {} stats["cameras"] = {}
for name, camera_stats in camera_metrics.items(): for name, camera_stats in camera_metrics.items():
total_detection_fps += camera_stats["detection_fps"].value total_detection_fps += camera_stats.detection_fps.value
pid = camera_stats["process"].pid if camera_stats["process"] else None pid = camera_stats.process.pid if camera_stats.process else None
ffmpeg_pid = ( ffmpeg_pid = camera_stats.ffmpeg_pid.value if camera_stats.ffmpeg_pid else None
camera_stats["ffmpeg_pid"].value if camera_stats["ffmpeg_pid"] else None
)
capture_pid = ( capture_pid = (
camera_stats["capture_process"].pid camera_stats.capture_process.pid if camera_stats.capture_process else None
if camera_stats["capture_process"]
else None
) )
stats["cameras"][name] = { stats["cameras"][name] = {
"camera_fps": round(camera_stats["camera_fps"].value, 2), "camera_fps": round(camera_stats.camera_fps.value, 2),
"process_fps": round(camera_stats["process_fps"].value, 2), "process_fps": round(camera_stats.process_fps.value, 2),
"skipped_fps": round(camera_stats["skipped_fps"].value, 2), "skipped_fps": round(camera_stats.skipped_fps.value, 2),
"detection_fps": round(camera_stats["detection_fps"].value, 2), "detection_fps": round(camera_stats.detection_fps.value, 2),
"detection_enabled": config.cameras[name].detect.enabled, "detection_enabled": config.cameras[name].detect.enabled,
"pid": pid, "pid": pid,
"capture_pid": capture_pid, "capture_pid": capture_pid,
"ffmpeg_pid": ffmpeg_pid, "ffmpeg_pid": ffmpeg_pid,
"audio_rms": round(camera_stats["audio_rms"].value, 4), "audio_rms": round(camera_stats.audio_rms.value, 4),
"audio_dBFS": round(camera_stats["audio_dBFS"].value, 4), "audio_dBFS": round(camera_stats.audio_dBFS.value, 4),
} }
stats["detectors"] = {} stats["detectors"] = {}

View File

@ -1,27 +1,11 @@
from multiprocessing import Queue
from multiprocessing.context import Process
from multiprocessing.sharedctypes import Synchronized from multiprocessing.sharedctypes import Synchronized
from multiprocessing.synchronize import Event 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 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): class PTZMetricsTypes(TypedDict):
ptz_autotracker_enabled: Synchronized ptz_autotracker_enabled: Synchronized
ptz_tracking_active: Event ptz_tracking_active: Event
@ -36,7 +20,7 @@ class PTZMetricsTypes(TypedDict):
class StatsTrackingTypes(TypedDict): class StatsTrackingTypes(TypedDict):
camera_metrics: dict[str, CameraMetricsTypes] camera_metrics: dict[str, CameraMetrics]
detectors: dict[str, ObjectDetectProcess] detectors: dict[str, ObjectDetectProcess]
started: int started: int
latest_frigate_version: str latest_frigate_version: str

View File

@ -11,6 +11,7 @@ import time
import cv2 import cv2
from setproctitle import setproctitle from setproctitle import setproctitle
from frigate.camera import CameraMetrics
from frigate.comms.config_updater import ConfigSubscriber from frigate.comms.config_updater import ConfigSubscriber
from frigate.comms.inter_process import InterProcessRequestor from frigate.comms.inter_process import InterProcessRequestor
from frigate.config import CameraConfig, DetectConfig, ModelConfig 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() stop_event = mp.Event()
def receiveSignal(signalNumber, frame): 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}" threading.current_thread().name = f"capture:{name}"
setproctitle(f"frigate.capture:{name}") setproctitle(f"frigate.capture:{name}")
frame_queue = process_info["frame_queue"]
camera_watchdog = CameraWatchdog( camera_watchdog = CameraWatchdog(
name, name,
config, config,
shm_frame_count, shm_frame_count,
frame_queue, camera_metrics.frame_queue,
process_info["camera_fps"], camera_metrics.camera_fps,
process_info["skipped_fps"], camera_metrics.skipped_fps,
process_info["ffmpeg_pid"], camera_metrics.ffmpeg_pid,
stop_event, stop_event,
) )
camera_watchdog.start() camera_watchdog.start()
@ -421,7 +423,7 @@ def track_camera(
detection_queue, detection_queue,
result_connection, result_connection,
detected_objects_queue, detected_objects_queue,
process_info, camera_metrics: CameraMetrics,
ptz_metrics, ptz_metrics,
region_grid, region_grid,
): ):
@ -437,7 +439,7 @@ def track_camera(
setproctitle(f"frigate.process:{name}") setproctitle(f"frigate.process:{name}")
listen() listen()
frame_queue = process_info["frame_queue"] frame_queue = camera_metrics.frame_queue
frame_shape = config.frame_shape frame_shape = config.frame_shape
objects_to_track = config.objects.track objects_to_track = config.objects.track
@ -469,7 +471,7 @@ def track_camera(
object_detector, object_detector,
object_tracker, object_tracker,
detected_objects_queue, detected_objects_queue,
process_info, camera_metrics,
objects_to_track, objects_to_track,
object_filters, object_filters,
stop_event, stop_event,
@ -542,7 +544,7 @@ def process_frames(
object_detector: RemoteObjectDetector, object_detector: RemoteObjectDetector,
object_tracker: ObjectTracker, object_tracker: ObjectTracker,
detected_objects_queue: mp.Queue, detected_objects_queue: mp.Queue,
process_info: dict, camera_metrics: CameraMetrics,
objects_to_track: list[str], objects_to_track: list[str],
object_filters, object_filters,
stop_event, stop_event,
@ -550,9 +552,6 @@ def process_frames(
region_grid, region_grid,
exit_on_empty: bool = False, 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) next_region_update = get_tomorrow_at_time(2)
config_subscriber = ConfigSubscriber(f"config/detect/{camera_name}") config_subscriber = ConfigSubscriber(f"config/detect/{camera_name}")
@ -589,7 +588,7 @@ def process_frames(
break break
continue continue
current_frame_time.value = frame_time camera_metrics.detection_frame.value = frame_time
ptz_metrics["ptz_frame_time"].value = frame_time ptz_metrics["ptz_frame_time"].value = frame_time
frame = frame_manager.get( frame = frame_manager.get(
@ -839,7 +838,7 @@ def process_frames(
continue continue
else: else:
fps_tracker.update() fps_tracker.update()
fps.value = fps_tracker.eps() camera_metrics.process_fps.value = fps_tracker.eps()
detected_objects_queue.put( detected_objects_queue.put(
( (
camera_name, camera_name,
@ -849,7 +848,7 @@ def process_frames(
regions, 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}") frame_manager.close(f"{camera_name}{frame_time}")
motion_detector.stop() motion_detector.stop()