Start processes dynamically when needed

This commit is contained in:
Nicolas Mowen 2025-06-10 16:15:27 -06:00
parent f5b3f9ae87
commit c7b4d7791b
2 changed files with 68 additions and 56 deletions

View File

@ -9,6 +9,7 @@ from multiprocessing.synchronize import Event as MpEvent
from frigate.camera import CameraMetrics, PTZMetrics from frigate.camera import CameraMetrics, PTZMetrics
from frigate.config import FrigateConfig from frigate.config import FrigateConfig
from frigate.config.camera import CameraConfig
from frigate.config.updater import GlobalConfigUpdateEnum, GlobalConfigUpdateSubscriber from frigate.config.updater import GlobalConfigUpdateEnum, GlobalConfigUpdateSubscriber
from frigate.const import SHM_FRAMES_VAR from frigate.const import SHM_FRAMES_VAR
from frigate.models import Regions from frigate.models import Regions
@ -49,6 +50,7 @@ class CameraMaintainer(threading.Thread):
GlobalConfigUpdateEnum.remove_camera, GlobalConfigUpdateEnum.remove_camera,
] ]
) )
self.shm_count = self.__calculate_shm_frame_count()
def __init_historical_regions(self) -> None: def __init_historical_regions(self) -> None:
# delete region grids for removed or renamed cameras # delete region grids for removed or renamed cameras
@ -77,13 +79,23 @@ class CameraMaintainer(threading.Thread):
cam_total_frame_size = 0.0 cam_total_frame_size = 0.0
for camera in self.config.cameras.values(): for camera in self.config.cameras.values():
if camera.enabled and camera.detect.width and camera.detect.height: if (
camera.enabled_in_config
and camera.detect.width
and camera.detect.height
):
cam_total_frame_size += round( cam_total_frame_size += round(
(camera.detect.width * camera.detect.height * 1.5 + 270480) (camera.detect.width * camera.detect.height * 1.5 + 270480)
/ 1048576, / 1048576,
1, 1,
) )
# leave room for 2 cameras that are added dynamically, if a user wants to add more cameras they may need to increase the SHM size and restart after adding them.
cam_total_frame_size += 2 * round(
(camera.detect.width * camera.detect.height * 1.5 + 270480) / 1048576,
1,
)
if cam_total_frame_size == 0.0: if cam_total_frame_size == 0.0:
return 0 return 0
@ -103,17 +115,17 @@ class CameraMaintainer(threading.Thread):
return shm_frame_count return shm_frame_count
def __start_camera_processors(self) -> None: def __start_camera_processor(self, name: str, config: CameraConfig) -> None:
for name, config in self.config.cameras.items(): config = self.config.cameras[name]
if not self.config.cameras[name].enabled_in_config: if not config.enabled_in_config:
logger.info(f"Camera processor not started for disabled camera {name}") logger.info(f"Camera processor not started for disabled camera {name}")
continue return
camera_process = FrigateProcess( camera_process = FrigateProcess(
target=track_camera, target=track_camera,
name=f"camera_processor:{name}", name=f"camera_processor:{name}",
args=( args=(
name, config.name,
config, config,
self.config.model, self.config.model,
self.config.model.merged_labelmap, self.config.model.merged_labelmap,
@ -126,27 +138,24 @@ class CameraMaintainer(threading.Thread):
), ),
daemon=True, daemon=True,
) )
self.camera_metrics[name].process = camera_process self.camera_metrics[config.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 {config.name}: {camera_process.pid}")
def __start_camera_capture(self) -> None: def __start_camera_capture(self, name: str, config: CameraConfig) -> None:
shm_frame_count = self.__calculate_shm_frame_count()
for name, config in self.config.cameras.items():
if not self.config.cameras[name].enabled_in_config: if not self.config.cameras[name].enabled_in_config:
logger.info(f"Capture process not started for disabled camera {name}") logger.info(f"Capture process not started for disabled camera {name}")
continue return
# pre-create shms # pre-create shms
for i in range(shm_frame_count): for i in range(self.shm_count):
frame_size = config.frame_shape_yuv[0] * config.frame_shape_yuv[1] frame_size = config.frame_shape_yuv[0] * config.frame_shape_yuv[1]
self.frame_manager.create(f"{config.name}_frame{i}", frame_size) self.frame_manager.create(f"{config.name}_frame{i}", frame_size)
capture_process = FrigateProcess( capture_process = FrigateProcess(
target=capture_camera, target=capture_camera,
name=f"camera_capture:{name}", name=f"camera_capture:{name}",
args=(config, shm_frame_count, self.camera_metrics[name]), args=(config, self.shm_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
@ -174,19 +183,21 @@ class CameraMaintainer(threading.Thread):
self.__init_historical_regions() self.__init_historical_regions()
# start camera processes # start camera processes
self.__start_camera_processors() for camera, config in self.config.cameras.items():
self.__start_camera_capture() self.__start_camera_processor(camera, config)
self.__start_camera_capture(camera, config)
while not self.stop_event.wait(1): while not self.stop_event.wait(1):
updates = self.update_subscriber.check_for_updates() updates = self.update_subscriber.check_for_updates()
for update_type, update_payload in updates: for update_type, update_config in updates:
if update_type == GlobalConfigUpdateEnum.add_camera: if update_type == GlobalConfigUpdateEnum.add_camera:
pass self.__start_camera_processor(update_config.name, update_config)
self.__start_camera_capture(update_config.name, update_config)
elif update_type == GlobalConfigUpdateEnum.debug_camera: elif update_type == GlobalConfigUpdateEnum.debug_camera:
pass pass
elif update_type == GlobalConfigUpdateEnum.remove_camera: elif update_type == GlobalConfigUpdateEnum.remove_camera:
camera = update_payload.get("camera") camera = update_config.name
if camera: if camera:
self.__stop_camera_capture_process(camera) self.__stop_camera_capture_process(camera)

View File

@ -5,6 +5,7 @@ from enum import Enum
from typing import Any from typing import Any
from frigate.comms.config_updater import ConfigPublisher, ConfigSubscriber from frigate.comms.config_updater import ConfigPublisher, ConfigSubscriber
from frigate.config.camera import CameraConfig
class GlobalConfigUpdateEnum(str, Enum): class GlobalConfigUpdateEnum(str, Enum):
@ -46,21 +47,21 @@ class GlobalConfigUpdateSubscriber:
exact=False, exact=False,
) )
def check_for_updates(self) -> list[tuple[GlobalConfigUpdateEnum, Any]]: def check_for_updates(self) -> list[tuple[GlobalConfigUpdateEnum, CameraConfig]]:
updated_topics: list[tuple[GlobalConfigUpdateEnum, Any]] = [] updated_topics: list[tuple[GlobalConfigUpdateEnum, CameraConfig]] = []
# get all updates available # get all updates available
while True: while True:
update_topic, payload = self.subscriber.check_for_update() update_topic, update_config = self.subscriber.check_for_update()
if update_topic is None or payload is None: if update_topic is None or update_config is None:
break break
_, raw_type = update_topic.split("/") _, raw_type = update_topic.split("/")
update_type = GlobalConfigUpdateEnum[raw_type] update_type = GlobalConfigUpdateEnum[raw_type]
if update_type in self.topics: if update_type in self.topics:
updated_topics.append((update_type, payload)) updated_topics.append((update_type, update_config))
return updated_topics return updated_topics