mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-01 19:17:41 +03:00
Move camera management fully to separate class
This commit is contained in:
parent
7085790dcd
commit
fa6f531e88
@ -36,7 +36,6 @@ from frigate.const import (
|
|||||||
FACE_DIR,
|
FACE_DIR,
|
||||||
MODEL_CACHE_DIR,
|
MODEL_CACHE_DIR,
|
||||||
RECORD_DIR,
|
RECORD_DIR,
|
||||||
SHM_FRAMES_VAR,
|
|
||||||
THUMB_DIR,
|
THUMB_DIR,
|
||||||
)
|
)
|
||||||
from frigate.data_processing.types import DataProcessorMetrics
|
from frigate.data_processing.types import DataProcessorMetrics
|
||||||
@ -70,8 +69,7 @@ from frigate.storage import StorageMaintainer
|
|||||||
from frigate.timeline import TimelineProcessor
|
from frigate.timeline import TimelineProcessor
|
||||||
from frigate.track.object_processing import TrackedObjectProcessor
|
from frigate.track.object_processing import TrackedObjectProcessor
|
||||||
from frigate.util.builtin import empty_and_close_queue
|
from frigate.util.builtin import empty_and_close_queue
|
||||||
from frigate.util.image import SharedMemoryFrameManager, UntrackedSharedMemory
|
from frigate.util.image import UntrackedSharedMemory
|
||||||
from frigate.util.object import get_camera_regions_grid
|
|
||||||
from frigate.version import VERSION
|
from frigate.version import VERSION
|
||||||
from frigate.watchdog import FrigateWatchdog
|
from frigate.watchdog import FrigateWatchdog
|
||||||
|
|
||||||
@ -101,8 +99,6 @@ class FrigateApp:
|
|||||||
self.ptz_metrics: dict[str, PTZMetrics] = {}
|
self.ptz_metrics: dict[str, PTZMetrics] = {}
|
||||||
self.processes: dict[str, int] = {}
|
self.processes: dict[str, int] = {}
|
||||||
self.embeddings: Optional[EmbeddingsContext] = None
|
self.embeddings: Optional[EmbeddingsContext] = None
|
||||||
self.region_grids: dict[str, list[list[dict[str, int]]]] = {}
|
|
||||||
self.frame_manager = SharedMemoryFrameManager()
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def ensure_dirs(self) -> None:
|
def ensure_dirs(self) -> None:
|
||||||
@ -423,20 +419,6 @@ class FrigateApp:
|
|||||||
output_processor.start()
|
output_processor.start()
|
||||||
logger.info(f"Output process started: {output_processor.pid}")
|
logger.info(f"Output process started: {output_processor.pid}")
|
||||||
|
|
||||||
def init_historical_regions(self) -> None:
|
|
||||||
# delete region grids for removed or renamed cameras
|
|
||||||
cameras = list(self.config.cameras.keys())
|
|
||||||
Regions.delete().where(~(Regions.camera << cameras)).execute()
|
|
||||||
|
|
||||||
# create or update region grids for each camera
|
|
||||||
for camera in self.config.cameras.values():
|
|
||||||
assert camera.name is not None
|
|
||||||
self.region_grids[camera.name] = get_camera_regions_grid(
|
|
||||||
camera.name,
|
|
||||||
camera.detect,
|
|
||||||
max(self.config.model.width, self.config.model.height),
|
|
||||||
)
|
|
||||||
|
|
||||||
def start_camera_processor(self) -> None:
|
def start_camera_processor(self) -> None:
|
||||||
self.camera_maintainer = CameraMaintainer(self.config, self.stop_event)
|
self.camera_maintainer = CameraMaintainer(self.config, self.stop_event)
|
||||||
self.camera_maintainer.start()
|
self.camera_maintainer.start()
|
||||||
@ -499,45 +481,6 @@ class FrigateApp:
|
|||||||
self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
|
self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
|
||||||
self.frigate_watchdog.start()
|
self.frigate_watchdog.start()
|
||||||
|
|
||||||
def shm_frame_count(self) -> int:
|
|
||||||
total_shm = round(shutil.disk_usage("/dev/shm").total / pow(2, 20), 1)
|
|
||||||
|
|
||||||
# required for log files + nginx cache
|
|
||||||
min_req_shm = 40 + 10
|
|
||||||
|
|
||||||
if self.config.birdseye.restream:
|
|
||||||
min_req_shm += 8
|
|
||||||
|
|
||||||
available_shm = total_shm - min_req_shm
|
|
||||||
cam_total_frame_size = 0.0
|
|
||||||
|
|
||||||
for camera in self.config.cameras.values():
|
|
||||||
if camera.enabled and camera.detect.width and camera.detect.height:
|
|
||||||
cam_total_frame_size += round(
|
|
||||||
(camera.detect.width * camera.detect.height * 1.5 + 270480)
|
|
||||||
/ 1048576,
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
if cam_total_frame_size == 0.0:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
shm_frame_count = min(
|
|
||||||
int(os.environ.get(SHM_FRAMES_VAR, "50")),
|
|
||||||
int(available_shm / (cam_total_frame_size)),
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
f"Calculated total camera size {available_shm} / {cam_total_frame_size} :: {shm_frame_count} frames for each camera in SHM"
|
|
||||||
)
|
|
||||||
|
|
||||||
if shm_frame_count < 20:
|
|
||||||
logger.warning(
|
|
||||||
f"The current SHM size of {total_shm}MB is too small, recommend increasing it to at least {round(min_req_shm + cam_total_frame_size * 20)}MB."
|
|
||||||
)
|
|
||||||
|
|
||||||
return shm_frame_count
|
|
||||||
|
|
||||||
def init_auth(self) -> None:
|
def init_auth(self) -> None:
|
||||||
if self.config.auth.enabled:
|
if self.config.auth.enabled:
|
||||||
if User.select().count() == 0:
|
if User.select().count() == 0:
|
||||||
@ -604,10 +547,8 @@ class FrigateApp:
|
|||||||
self.init_embeddings_client()
|
self.init_embeddings_client()
|
||||||
self.start_video_output_processor()
|
self.start_video_output_processor()
|
||||||
self.start_ptz_autotracker()
|
self.start_ptz_autotracker()
|
||||||
self.init_historical_regions()
|
|
||||||
self.start_detected_frames_processor()
|
self.start_detected_frames_processor()
|
||||||
self.start_camera_processor()
|
self.start_camera_processor()
|
||||||
self.start_camera_capture_processes()
|
|
||||||
self.start_audio_processor()
|
self.start_audio_processor()
|
||||||
self.start_storage_maintainer()
|
self.start_storage_maintainer()
|
||||||
self.start_stats_emitter()
|
self.start_stats_emitter()
|
||||||
@ -706,7 +647,6 @@ class FrigateApp:
|
|||||||
self.event_metadata_updater.stop()
|
self.event_metadata_updater.stop()
|
||||||
self.inter_zmq_proxy.stop()
|
self.inter_zmq_proxy.stop()
|
||||||
|
|
||||||
self.frame_manager.cleanup()
|
|
||||||
while len(self.detection_shms) > 0:
|
while len(self.detection_shms) > 0:
|
||||||
shm = self.detection_shms.pop()
|
shm = self.detection_shms.pop()
|
||||||
shm.close()
|
shm.close()
|
||||||
|
|||||||
@ -1,23 +1,107 @@
|
|||||||
"""Create and maintain camera processes / management."""
|
"""Create and maintain camera processes / management."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
import threading
|
import threading
|
||||||
|
from multiprocessing import Queue
|
||||||
from multiprocessing.synchronize import Event as MpEvent
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
|
|
||||||
|
from frigate.camera import CameraMetrics, PTZMetrics
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
|
from frigate.config.updater import GlobalConfigUpdateEnum, GlobalConfigUpdateSubscriber
|
||||||
|
from frigate.const import SHM_FRAMES_VAR
|
||||||
|
from frigate.models import Regions
|
||||||
from frigate.util import Process as FrigateProcess
|
from frigate.util import Process as FrigateProcess
|
||||||
from frigate.util.builtin import empty_and_close_queue
|
from frigate.util.builtin import empty_and_close_queue
|
||||||
|
from frigate.util.image import SharedMemoryFrameManager
|
||||||
|
from frigate.util.object import get_camera_regions_grid
|
||||||
from frigate.video import capture_camera, track_camera
|
from frigate.video import capture_camera, track_camera
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CameraMaintainer(threading.Thread):
|
class CameraMaintainer(threading.Thread):
|
||||||
def __init__(self, config: FrigateConfig, stop_event: MpEvent):
|
def __init__(
|
||||||
|
self,
|
||||||
|
config: FrigateConfig,
|
||||||
|
detection_queue: Queue,
|
||||||
|
detection_out_events: dict[str, MpEvent],
|
||||||
|
detected_frames_queue: Queue,
|
||||||
|
camera_metrics: dict[str, CameraMetrics],
|
||||||
|
ptz_metrics: dict[str, PTZMetrics],
|
||||||
|
stop_event: MpEvent,
|
||||||
|
):
|
||||||
super().__init__(name="camera_processor")
|
super().__init__(name="camera_processor")
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.detection_queue = detection_queue
|
||||||
|
self.detection_out_events = detection_out_events
|
||||||
|
self.detected_frames_queue = detected_frames_queue
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
|
self.camera_metrics = camera_metrics
|
||||||
|
self.ptz_metrics = ptz_metrics
|
||||||
|
self.frame_manager = SharedMemoryFrameManager()
|
||||||
|
self.region_grids: dict[str, list[list[dict[str, int]]]] = {}
|
||||||
|
self.update_subscriber = GlobalConfigUpdateSubscriber(
|
||||||
|
[
|
||||||
|
GlobalConfigUpdateEnum.add_camera,
|
||||||
|
GlobalConfigUpdateEnum.debug_camera,
|
||||||
|
GlobalConfigUpdateEnum.remove_camera,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init_historical_regions(self) -> None:
|
||||||
|
# delete region grids for removed or renamed cameras
|
||||||
|
cameras = list(self.config.cameras.keys())
|
||||||
|
Regions.delete().where(~(Regions.camera << cameras)).execute()
|
||||||
|
|
||||||
|
# create or update region grids for each camera
|
||||||
|
for camera in self.config.cameras.values():
|
||||||
|
assert camera.name is not None
|
||||||
|
self.region_grids[camera.name] = get_camera_regions_grid(
|
||||||
|
camera.name,
|
||||||
|
camera.detect,
|
||||||
|
max(self.config.model.width, self.config.model.height),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __calculate_shm_frame_count(self) -> int:
|
||||||
|
total_shm = round(shutil.disk_usage("/dev/shm").total / pow(2, 20), 1)
|
||||||
|
|
||||||
|
# required for log files + nginx cache
|
||||||
|
min_req_shm = 40 + 10
|
||||||
|
|
||||||
|
if self.config.birdseye.restream:
|
||||||
|
min_req_shm += 8
|
||||||
|
|
||||||
|
available_shm = total_shm - min_req_shm
|
||||||
|
cam_total_frame_size = 0.0
|
||||||
|
|
||||||
|
for camera in self.config.cameras.values():
|
||||||
|
if camera.enabled and camera.detect.width and camera.detect.height:
|
||||||
|
cam_total_frame_size += round(
|
||||||
|
(camera.detect.width * camera.detect.height * 1.5 + 270480)
|
||||||
|
/ 1048576,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if cam_total_frame_size == 0.0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
shm_frame_count = min(
|
||||||
|
int(os.environ.get(SHM_FRAMES_VAR, "50")),
|
||||||
|
int(available_shm / (cam_total_frame_size)),
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f"Calculated total camera size {available_shm} / {cam_total_frame_size} :: {shm_frame_count} frames for each camera in SHM"
|
||||||
|
)
|
||||||
|
|
||||||
|
if shm_frame_count < 20:
|
||||||
|
logger.warning(
|
||||||
|
f"The current SHM size of {total_shm}MB is too small, recommend increasing it to at least {round(min_req_shm + cam_total_frame_size * 20)}MB."
|
||||||
|
)
|
||||||
|
|
||||||
|
return shm_frame_count
|
||||||
|
|
||||||
def __start_camera_processors(self) -> None:
|
def __start_camera_processors(self) -> None:
|
||||||
for name, config in self.config.cameras.items():
|
for name, config in self.config.cameras.items():
|
||||||
@ -47,7 +131,7 @@ class CameraMaintainer(threading.Thread):
|
|||||||
logger.info(f"Camera processor started for {name}: {camera_process.pid}")
|
logger.info(f"Camera processor started for {name}: {camera_process.pid}")
|
||||||
|
|
||||||
def __start_camera_capture(self) -> None:
|
def __start_camera_capture(self) -> None:
|
||||||
shm_frame_count = self.shm_frame_count()
|
shm_frame_count = self.__calculate_shm_frame_count()
|
||||||
|
|
||||||
for name, config in self.config.cameras.items():
|
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:
|
||||||
@ -70,10 +154,21 @@ class CameraMaintainer(threading.Thread):
|
|||||||
logger.info(f"Capture process started for {name}: {capture_process.pid}")
|
logger.info(f"Capture process started for {name}: {capture_process.pid}")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
self.__init_historical_regions()
|
||||||
|
|
||||||
# start camera processes
|
# start camera processes
|
||||||
self.__start_camera_processors()
|
self.__start_camera_processors()
|
||||||
|
self.__start_camera_capture()
|
||||||
|
|
||||||
while not self.stop_event.is_set():
|
while not self.stop_event.wait(1):
|
||||||
|
updates = self.update_subscriber.check_for_updates()
|
||||||
|
|
||||||
|
for update_type, update_payload in updates:
|
||||||
|
if update_type == GlobalConfigUpdateEnum.add_camera:
|
||||||
|
pass
|
||||||
|
elif update_type == GlobalConfigUpdateEnum.debug_camera:
|
||||||
|
pass
|
||||||
|
elif update_type == GlobalConfigUpdateEnum.remove_camera:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# ensure the capture processes are done
|
# ensure the capture processes are done
|
||||||
@ -93,3 +188,5 @@ class CameraMaintainer(threading.Thread):
|
|||||||
camera_process.join()
|
camera_process.join()
|
||||||
logger.info(f"Closing frame queue for {camera}")
|
logger.info(f"Closing frame queue for {camera}")
|
||||||
empty_and_close_queue(metrics.frame_queue)
|
empty_and_close_queue(metrics.frame_queue)
|
||||||
|
|
||||||
|
self.frame_manager.cleanup()
|
||||||
|
|||||||
@ -11,6 +11,7 @@ class GlobalConfigUpdateEnum(str, Enum):
|
|||||||
"""Supported global config update types."""
|
"""Supported global config update types."""
|
||||||
|
|
||||||
add_camera = "add_camera"
|
add_camera = "add_camera"
|
||||||
|
debug_camera = "debug_camera"
|
||||||
remove_camera = "remove_camera"
|
remove_camera = "remove_camera"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user