Only store PID instead of entire process reference

This commit is contained in:
Nicolas Mowen 2025-06-12 08:56:56 -06:00
parent 4dd5e9c66e
commit 85dcfdd670
5 changed files with 38 additions and 37 deletions

View File

@ -85,8 +85,8 @@ class FrigateApp:
self.detectors: dict[str, ObjectDetectProcess] = {}
self.detection_shms: list[mp.shared_memory.SharedMemory] = []
self.log_queue: Queue = mp.Queue()
self.camera_metrics: dict[str, CameraMetrics] = {}
self.metrics_manager = mp.Manager()
self.camera_metrics: dict[str, CameraMetrics] = self.metrics_manager.dict()
self.embeddings_metrics: DataProcessorMetrics | None = (
DataProcessorMetrics(
self.metrics_manager, list(config.classification.custom.keys())
@ -128,7 +128,7 @@ class FrigateApp:
def init_camera_metrics(self) -> None:
# create camera_metrics
for camera_name in self.config.cameras.keys():
self.camera_metrics[camera_name] = CameraMetrics()
self.camera_metrics[camera_name] = CameraMetrics(self.metrics_manager)
self.ptz_metrics[camera_name] = PTZMetrics(
autotracker_enabled=self.config.cameras[
camera_name

View File

@ -1,7 +1,7 @@
import multiprocessing as mp
from multiprocessing.managers import SyncManager
from multiprocessing.sharedctypes import Synchronized
from multiprocessing.synchronize import Event
from typing import Optional
class CameraMetrics:
@ -16,25 +16,25 @@ class CameraMetrics:
frame_queue: mp.Queue
process: Optional[mp.Process]
capture_process: Optional[mp.Process]
process_pid: Synchronized
capture_process_pid: Synchronized
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)
def __init__(self, manager: SyncManager):
self.camera_fps = manager.Value("d", 0)
self.detection_fps = manager.Value("d", 0)
self.detection_frame = manager.Value("d", 0)
self.process_fps = manager.Value("d", 0)
self.skipped_fps = manager.Value("d", 0)
self.read_start = manager.Value("d", 0)
self.audio_rms = manager.Value("d", 0)
self.audio_dBFS = manager.Value("d", 0)
self.frame_queue = mp.Queue(maxsize=2)
self.frame_queue = manager.Queue(maxsize=2)
self.process = None
self.capture_process = None
self.ffmpeg_pid = mp.Value("i", 0)
self.process_pid = manager.Value("i", 0)
self.capture_process_pid = manager.Value("i", 0)
self.ffmpeg_pid = manager.Value("i", 0)
class PTZMetrics:

View File

@ -54,6 +54,8 @@ class CameraMaintainer(threading.Thread):
],
)
self.shm_count = self.__calculate_shm_frame_count()
self.camera_processes: dict[str, mp.Process] = {}
self.capture_processes: dict[str, mp.Process] = {}
def __init_historical_regions(self) -> None:
# delete region grids for removed or renamed cameras
@ -120,7 +122,7 @@ class CameraMaintainer(threading.Thread):
def __start_camera_processor(
self, name: str, config: CameraConfig, runtime: bool = False
) -> mp.Process:
) -> None:
if not config.enabled_in_config:
logger.info(f"Camera processor not started for disabled camera {name}")
return
@ -168,13 +170,14 @@ class CameraMaintainer(threading.Thread):
),
daemon=True,
)
self.camera_processes[config.name] = camera_process
camera_process.start()
self.camera_metrics[config.name].process_pid.value = camera_process.pid
logger.info(f"Camera processor started for {config.name}: {camera_process.pid}")
return camera_process
def __start_camera_capture(
self, name: str, config: CameraConfig, runtime: bool = False
) -> mp.Process:
) -> None:
if not config.enabled_in_config:
logger.info(f"Capture process not started for disabled camera {name}")
return
@ -191,26 +194,26 @@ class CameraMaintainer(threading.Thread):
args=(config, count, self.camera_metrics[name]),
)
capture_process.daemon = True
self.capture_processes[name] = capture_process
capture_process.start()
self.camera_metrics[name].capture_process_pid.value = capture_process.pid
logger.info(f"Capture process started for {name}: {capture_process.pid}")
return capture_process
def __stop_camera_capture_process(self, camera: str) -> None:
capture_process = self.camera_metrics[camera].capture_process
capture_process = self.capture_processes[camera]
if capture_process is not None:
logger.info(f"Waiting for capture process for {camera} to stop")
capture_process.terminate()
capture_process.join()
def __stop_camera_process(self, camera: str) -> None:
metrics = self.camera_metrics[camera]
camera_process = metrics.process
camera_process = self.camera_processes[camera]
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}")
empty_and_close_queue(metrics.frame_queue)
empty_and_close_queue(self.camera_metrics[camera].frame_queue)
def run(self):
self.__init_historical_regions()
@ -226,30 +229,26 @@ class CameraMaintainer(threading.Thread):
for update_type, updated_cameras in updates.items():
if update_type == CameraConfigUpdateEnum.add.name:
for camera in updated_cameras:
camera_process = self.__start_camera_processor(
self.__start_camera_processor(
camera,
self.update_subscriber.camera_configs[camera],
runtime=True,
)
capture_process = self.__start_camera_capture(
self.__start_camera_capture(
camera,
self.update_subscriber.camera_configs[camera],
runtime=True,
)
self.camera_metrics[config.name].process = camera_process
self.camera_metrics[
config.name
].capture_process = capture_process
elif update_type == CameraConfigUpdateEnum.remove.name:
self.__stop_camera_capture_process(camera)
self.__stop_camera_process(camera)
# ensure the capture processes are done
for camera in self.camera_metrics.keys():
for camera in self.camera_processes.keys():
self.__stop_camera_capture_process(camera)
# ensure the camera processors are done
for camera in self.camera_metrics.keys():
for camera in self.capture_processes.keys():
self.__stop_camera_process(camera)
self.update_subscriber.stop()

View File

@ -271,10 +271,12 @@ 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
pid = camera_stats.process_pid.value if camera_stats.process_pid.value 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.value
if camera_stats.capture_process_pid.value
else None
)
stats["cameras"][name] = {
"camera_fps": round(camera_stats.camera_fps.value, 2),

View File

@ -173,7 +173,7 @@ export default function CameraMetrics({
});
series[key]["detect"].data.push({
x: statsIdx,
y: stats.cpu_usages[camStats.pid.toString()].cpu,
y: stats.cpu_usages[camStats.pid?.toString()]?.cpu,
});
});
});