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
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:
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]
}
for camera_name in self.config.cameras.keys():
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():

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 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:

View File

@ -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"] = {}

View File

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

View File

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