mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Use zmq for inter process communication (#9309)
* Use zmq for inter process communication * Use localhost for reply and request * Use pyobj instead of json and Need to use separate requestors for each audio listener * Cleanup port defining
This commit is contained in:
parent
198dbbdff1
commit
dd3dc7949a
@ -15,6 +15,7 @@ pydantic == 1.10.*
|
|||||||
git+https://github.com/fbcotter/py3nvml#egg=py3nvml
|
git+https://github.com/fbcotter/py3nvml#egg=py3nvml
|
||||||
PyYAML == 6.0.*
|
PyYAML == 6.0.*
|
||||||
pytz == 2023.3.post1
|
pytz == 2023.3.post1
|
||||||
|
pyzmq == 25.1.*
|
||||||
ruamel.yaml == 0.18.*
|
ruamel.yaml == 0.18.*
|
||||||
tzlocal == 5.2
|
tzlocal == 5.2
|
||||||
types-PyYAML == 6.0.*
|
types-PyYAML == 6.0.*
|
||||||
|
@ -171,7 +171,6 @@ class FrigateApp:
|
|||||||
# issue https://github.com/python/typeshed/issues/8799
|
# issue https://github.com/python/typeshed/issues/8799
|
||||||
# from mypy 0.981 onwards
|
# from mypy 0.981 onwards
|
||||||
"frame_queue": mp.Queue(maxsize=2),
|
"frame_queue": mp.Queue(maxsize=2),
|
||||||
"region_grid_queue": mp.Queue(maxsize=1),
|
|
||||||
"capture_process": None,
|
"capture_process": None,
|
||||||
"process": None,
|
"process": None,
|
||||||
"audio_rms": mp.Value("d", 0.0), # type: ignore[typeddict-item]
|
"audio_rms": mp.Value("d", 0.0), # type: ignore[typeddict-item]
|
||||||
@ -273,9 +272,6 @@ class FrigateApp:
|
|||||||
# Queue for timeline events
|
# Queue for timeline events
|
||||||
self.timeline_queue: Queue = mp.Queue()
|
self.timeline_queue: Queue = mp.Queue()
|
||||||
|
|
||||||
# Queue for inter process communication
|
|
||||||
self.inter_process_queue: Queue = mp.Queue()
|
|
||||||
|
|
||||||
def init_database(self) -> None:
|
def init_database(self) -> None:
|
||||||
def vacuum_db(db: SqliteExtDatabase) -> None:
|
def vacuum_db(db: SqliteExtDatabase) -> None:
|
||||||
logger.info("Running database vacuum")
|
logger.info("Running database vacuum")
|
||||||
@ -350,7 +346,6 @@ class FrigateApp:
|
|||||||
name="recording_manager",
|
name="recording_manager",
|
||||||
args=(
|
args=(
|
||||||
self.config,
|
self.config,
|
||||||
self.inter_process_queue,
|
|
||||||
self.object_recordings_info_queue,
|
self.object_recordings_info_queue,
|
||||||
self.audio_recordings_info_queue,
|
self.audio_recordings_info_queue,
|
||||||
self.feature_metrics,
|
self.feature_metrics,
|
||||||
@ -390,9 +385,7 @@ class FrigateApp:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def init_inter_process_communicator(self) -> None:
|
def init_inter_process_communicator(self) -> None:
|
||||||
self.inter_process_communicator = InterProcessCommunicator(
|
self.inter_process_communicator = InterProcessCommunicator()
|
||||||
self.inter_process_queue
|
|
||||||
)
|
|
||||||
|
|
||||||
def init_web_server(self) -> None:
|
def init_web_server(self) -> None:
|
||||||
self.flask_app = create_app(
|
self.flask_app = create_app(
|
||||||
@ -495,7 +488,6 @@ class FrigateApp:
|
|||||||
args=(
|
args=(
|
||||||
self.config,
|
self.config,
|
||||||
self.video_output_queue,
|
self.video_output_queue,
|
||||||
self.inter_process_queue,
|
|
||||||
self.camera_metrics,
|
self.camera_metrics,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -534,7 +526,6 @@ class FrigateApp:
|
|||||||
self.detection_queue,
|
self.detection_queue,
|
||||||
self.detection_out_events[name],
|
self.detection_out_events[name],
|
||||||
self.detected_frames_queue,
|
self.detected_frames_queue,
|
||||||
self.inter_process_queue,
|
|
||||||
self.camera_metrics[name],
|
self.camera_metrics[name],
|
||||||
self.ptz_metrics[name],
|
self.ptz_metrics[name],
|
||||||
self.region_grids[name],
|
self.region_grids[name],
|
||||||
@ -571,7 +562,6 @@ class FrigateApp:
|
|||||||
self.audio_recordings_info_queue,
|
self.audio_recordings_info_queue,
|
||||||
self.camera_metrics,
|
self.camera_metrics,
|
||||||
self.feature_metrics,
|
self.feature_metrics,
|
||||||
self.inter_process_communicator,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
audio_process.daemon = True
|
audio_process.daemon = True
|
||||||
@ -777,7 +767,6 @@ class FrigateApp:
|
|||||||
self.object_recordings_info_queue,
|
self.object_recordings_info_queue,
|
||||||
self.audio_recordings_info_queue,
|
self.audio_recordings_info_queue,
|
||||||
self.log_queue,
|
self.log_queue,
|
||||||
self.inter_process_queue,
|
|
||||||
]:
|
]:
|
||||||
if queue is not None:
|
if queue is not None:
|
||||||
while not queue.empty():
|
while not queue.empty():
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
||||||
from frigate.const import INSERT_MANY_RECORDINGS, INSERT_PREVIEW, REQUEST_REGION_GRID
|
from frigate.const import INSERT_MANY_RECORDINGS, INSERT_PREVIEW, REQUEST_REGION_GRID
|
||||||
@ -70,7 +70,7 @@ class Dispatcher:
|
|||||||
for comm in self.comms:
|
for comm in self.comms:
|
||||||
comm.subscribe(self._receive)
|
comm.subscribe(self._receive)
|
||||||
|
|
||||||
def _receive(self, topic: str, payload: str) -> None:
|
def _receive(self, topic: str, payload: str) -> Optional[Any]:
|
||||||
"""Handle receiving of payload from communicators."""
|
"""Handle receiving of payload from communicators."""
|
||||||
if topic.endswith("set"):
|
if topic.endswith("set"):
|
||||||
try:
|
try:
|
||||||
@ -95,13 +95,12 @@ class Dispatcher:
|
|||||||
Recordings.insert_many(payload).execute()
|
Recordings.insert_many(payload).execute()
|
||||||
elif topic == REQUEST_REGION_GRID:
|
elif topic == REQUEST_REGION_GRID:
|
||||||
camera = payload
|
camera = payload
|
||||||
self.camera_metrics[camera]["region_grid_queue"].put(
|
grid = get_camera_regions_grid(
|
||||||
get_camera_regions_grid(
|
camera,
|
||||||
camera,
|
self.config.cameras[camera].detect,
|
||||||
self.config.cameras[camera].detect,
|
max(self.config.model.width, self.config.model.height),
|
||||||
max(self.config.model.width, self.config.model.height),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
return grid
|
||||||
elif topic == INSERT_PREVIEW:
|
elif topic == INSERT_PREVIEW:
|
||||||
Previews.insert(payload).execute()
|
Previews.insert(payload).execute()
|
||||||
else:
|
else:
|
||||||
|
@ -1,16 +1,25 @@
|
|||||||
|
"""Facilitates communication between processes."""
|
||||||
|
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
import queue
|
import os
|
||||||
import threading
|
import threading
|
||||||
from multiprocessing import Queue
|
|
||||||
from multiprocessing.synchronize import Event as MpEvent
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
import zmq
|
||||||
|
|
||||||
from frigate.comms.dispatcher import Communicator
|
from frigate.comms.dispatcher import Communicator
|
||||||
|
from frigate.const import PORT_INTER_PROCESS_COMM
|
||||||
|
|
||||||
|
|
||||||
class InterProcessCommunicator(Communicator):
|
class InterProcessCommunicator(Communicator):
|
||||||
def __init__(self, queue: Queue) -> None:
|
def __init__(self) -> None:
|
||||||
self.queue = queue
|
INTER_PROCESS_COMM_PORT = (
|
||||||
|
os.environ.get("INTER_PROCESS_COMM_PORT") or PORT_INTER_PROCESS_COMM
|
||||||
|
)
|
||||||
|
self.context = zmq.Context()
|
||||||
|
self.socket = self.context.socket(zmq.REP)
|
||||||
|
self.socket.bind(f"tcp://127.0.0.1:{INTER_PROCESS_COMM_PORT}")
|
||||||
self.stop_event: MpEvent = mp.Event()
|
self.stop_event: MpEvent = mp.Event()
|
||||||
|
|
||||||
def publish(self, topic: str, payload: str, retain: bool) -> None:
|
def publish(self, topic: str, payload: str, retain: bool) -> None:
|
||||||
@ -23,17 +32,41 @@ class InterProcessCommunicator(Communicator):
|
|||||||
self.reader_thread.start()
|
self.reader_thread.start()
|
||||||
|
|
||||||
def read(self) -> None:
|
def read(self) -> None:
|
||||||
while not self.stop_event.is_set():
|
while not self.stop_event.wait(0.5):
|
||||||
try:
|
while True: # load all messages that are queued
|
||||||
(
|
try:
|
||||||
topic,
|
(topic, value) = self.socket.recv_pyobj(flags=zmq.NOBLOCK)
|
||||||
value,
|
|
||||||
) = self.queue.get(True, 1)
|
|
||||||
except queue.Empty:
|
|
||||||
continue
|
|
||||||
|
|
||||||
self._dispatcher(topic, value)
|
response = self._dispatcher(topic, value)
|
||||||
|
|
||||||
|
if response is not None:
|
||||||
|
self.socket.send_pyobj(response)
|
||||||
|
else:
|
||||||
|
self.socket.send_pyobj([])
|
||||||
|
except zmq.ZMQError:
|
||||||
|
break
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
self.stop_event.set()
|
self.stop_event.set()
|
||||||
self.reader_thread.join()
|
self.reader_thread.join()
|
||||||
|
self.socket.close()
|
||||||
|
self.context.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class InterProcessRequestor:
|
||||||
|
"""Simplifies sending data to InterProcessCommunicator and getting a reply."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
port = os.environ.get("INTER_PROCESS_COMM_PORT") or PORT_INTER_PROCESS_COMM
|
||||||
|
self.context = zmq.Context()
|
||||||
|
self.socket = self.context.socket(zmq.REQ)
|
||||||
|
self.socket.connect(f"tcp://127.0.0.1:{port}")
|
||||||
|
|
||||||
|
def send_data(self, topic: str, data: any) -> any:
|
||||||
|
"""Sends data and then waits for reply."""
|
||||||
|
self.socket.send_pyobj((topic, data))
|
||||||
|
return self.socket.recv_pyobj()
|
||||||
|
|
||||||
|
def stop(self) -> None:
|
||||||
|
self.socket.close()
|
||||||
|
self.context.destroy()
|
||||||
|
@ -887,6 +887,10 @@ class CameraConfig(FrigateBaseModel):
|
|||||||
global_args = get_ffmpeg_arg_list(
|
global_args = get_ffmpeg_arg_list(
|
||||||
ffmpeg_input.global_args or self.ffmpeg.global_args
|
ffmpeg_input.global_args or self.ffmpeg.global_args
|
||||||
)
|
)
|
||||||
|
|
||||||
|
camera_arg = (
|
||||||
|
self.ffmpeg.hwaccel_args if self.ffmpeg.hwaccel_args != "auto" else None
|
||||||
|
)
|
||||||
hwaccel_args = get_ffmpeg_arg_list(
|
hwaccel_args = get_ffmpeg_arg_list(
|
||||||
parse_preset_hardware_acceleration_decode(
|
parse_preset_hardware_acceleration_decode(
|
||||||
ffmpeg_input.hwaccel_args,
|
ffmpeg_input.hwaccel_args,
|
||||||
@ -896,12 +900,13 @@ class CameraConfig(FrigateBaseModel):
|
|||||||
)
|
)
|
||||||
or ffmpeg_input.hwaccel_args
|
or ffmpeg_input.hwaccel_args
|
||||||
or parse_preset_hardware_acceleration_decode(
|
or parse_preset_hardware_acceleration_decode(
|
||||||
self.ffmpeg.hwaccel_args,
|
camera_arg,
|
||||||
self.detect.fps,
|
self.detect.fps,
|
||||||
self.detect.width,
|
self.detect.width,
|
||||||
self.detect.height,
|
self.detect.height,
|
||||||
)
|
)
|
||||||
or self.ffmpeg.hwaccel_args
|
or camera_arg
|
||||||
|
or []
|
||||||
)
|
)
|
||||||
input_args = get_ffmpeg_arg_list(
|
input_args = get_ffmpeg_arg_list(
|
||||||
parse_preset_input(ffmpeg_input.input_args, self.detect.fps)
|
parse_preset_input(ffmpeg_input.input_args, self.detect.fps)
|
||||||
|
@ -57,6 +57,10 @@ DRIVER_AMD = "radeonsi"
|
|||||||
DRIVER_INTEL_i965 = "i965"
|
DRIVER_INTEL_i965 = "i965"
|
||||||
DRIVER_INTEL_iHD = "iHD"
|
DRIVER_INTEL_iHD = "iHD"
|
||||||
|
|
||||||
|
# Ports
|
||||||
|
|
||||||
|
PORT_INTER_PROCESS_COMM = 4892
|
||||||
|
|
||||||
# Record Values
|
# Record Values
|
||||||
|
|
||||||
CACHE_SEGMENT_FORMAT = "%Y%m%d%H%M%S%z"
|
CACHE_SEGMENT_FORMAT = "%Y%m%d%H%M%S%z"
|
||||||
|
@ -13,7 +13,7 @@ import numpy as np
|
|||||||
import requests
|
import requests
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
from frigate.comms.inter_process import InterProcessCommunicator
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import CameraConfig, CameraInput, FfmpegConfig, FrigateConfig
|
from frigate.config import CameraConfig, CameraInput, FfmpegConfig, FrigateConfig
|
||||||
from frigate.const import (
|
from frigate.const import (
|
||||||
AUDIO_DURATION,
|
AUDIO_DURATION,
|
||||||
@ -70,7 +70,6 @@ def listen_to_audio(
|
|||||||
recordings_info_queue: mp.Queue,
|
recordings_info_queue: mp.Queue,
|
||||||
camera_metrics: dict[str, CameraMetricsTypes],
|
camera_metrics: dict[str, CameraMetricsTypes],
|
||||||
process_info: dict[str, FeatureMetricsTypes],
|
process_info: dict[str, FeatureMetricsTypes],
|
||||||
inter_process_communicator: InterProcessCommunicator,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
stop_event = mp.Event()
|
stop_event = mp.Event()
|
||||||
audio_threads: list[threading.Thread] = []
|
audio_threads: list[threading.Thread] = []
|
||||||
@ -100,7 +99,6 @@ def listen_to_audio(
|
|||||||
camera_metrics,
|
camera_metrics,
|
||||||
process_info,
|
process_info,
|
||||||
stop_event,
|
stop_event,
|
||||||
inter_process_communicator,
|
|
||||||
)
|
)
|
||||||
audio_threads.append(audio)
|
audio_threads.append(audio)
|
||||||
audio.start()
|
audio.start()
|
||||||
@ -174,7 +172,6 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
camera_metrics: dict[str, CameraMetricsTypes],
|
camera_metrics: dict[str, CameraMetricsTypes],
|
||||||
feature_metrics: dict[str, FeatureMetricsTypes],
|
feature_metrics: dict[str, FeatureMetricsTypes],
|
||||||
stop_event: mp.Event,
|
stop_event: mp.Event,
|
||||||
inter_process_communicator: InterProcessCommunicator,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.name = f"{camera.name}_audio_event_processor"
|
self.name = f"{camera.name}_audio_event_processor"
|
||||||
@ -182,7 +179,6 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
self.recordings_info_queue = recordings_info_queue
|
self.recordings_info_queue = recordings_info_queue
|
||||||
self.camera_metrics = camera_metrics
|
self.camera_metrics = camera_metrics
|
||||||
self.feature_metrics = feature_metrics
|
self.feature_metrics = feature_metrics
|
||||||
self.inter_process_communicator = inter_process_communicator
|
|
||||||
self.detections: dict[dict[str, any]] = {}
|
self.detections: dict[dict[str, any]] = {}
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
self.detector = AudioTfl(stop_event, self.config.audio.num_threads)
|
self.detector = AudioTfl(stop_event, self.config.audio.num_threads)
|
||||||
@ -193,6 +189,9 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
self.logpipe = LogPipe(f"ffmpeg.{self.config.name}.audio")
|
self.logpipe = LogPipe(f"ffmpeg.{self.config.name}.audio")
|
||||||
self.audio_listener = None
|
self.audio_listener = None
|
||||||
|
|
||||||
|
# create communication for audio detections
|
||||||
|
self.requestor = InterProcessRequestor()
|
||||||
|
|
||||||
def detect_audio(self, audio) -> None:
|
def detect_audio(self, audio) -> None:
|
||||||
if not self.feature_metrics[self.config.name]["audio_enabled"].value:
|
if not self.feature_metrics[self.config.name]["audio_enabled"].value:
|
||||||
return
|
return
|
||||||
@ -245,12 +244,8 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
else:
|
else:
|
||||||
dBFS = 0
|
dBFS = 0
|
||||||
|
|
||||||
self.inter_process_communicator.queue.put(
|
self.requestor.send_data(f"{self.config.name}/audio/dBFS", float(dBFS))
|
||||||
(f"{self.config.name}/audio/dBFS", float(dBFS))
|
self.requestor.send_data(f"{self.config.name}/audio/rms", float(rms))
|
||||||
)
|
|
||||||
self.inter_process_communicator.queue.put(
|
|
||||||
(f"{self.config.name}/audio/rms", float(rms))
|
|
||||||
)
|
|
||||||
|
|
||||||
return float(rms), float(dBFS)
|
return float(rms), float(dBFS)
|
||||||
|
|
||||||
@ -260,9 +255,7 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
"last_detection"
|
"last_detection"
|
||||||
] = datetime.datetime.now().timestamp()
|
] = datetime.datetime.now().timestamp()
|
||||||
else:
|
else:
|
||||||
self.inter_process_communicator.queue.put(
|
self.requestor.send_data(f"{self.config.name}/audio/{label}", "ON")
|
||||||
(f"{self.config.name}/audio/{label}", "ON")
|
|
||||||
)
|
|
||||||
|
|
||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
f"{FRIGATE_LOCALHOST}/api/events/{self.config.name}/{label}/create",
|
f"{FRIGATE_LOCALHOST}/api/events/{self.config.name}/{label}/create",
|
||||||
@ -288,8 +281,8 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
now - detection.get("last_detection", now)
|
now - detection.get("last_detection", now)
|
||||||
> self.config.audio.max_not_heard
|
> self.config.audio.max_not_heard
|
||||||
):
|
):
|
||||||
self.inter_process_communicator.queue.put(
|
self.requestor.send_data(
|
||||||
(f"{self.config.name}/audio/{detection['label']}", "OFF")
|
f"{self.config.name}/audio/{detection['label']}", "OFF"
|
||||||
)
|
)
|
||||||
|
|
||||||
resp = requests.put(
|
resp = requests.put(
|
||||||
@ -350,3 +343,4 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
|
|
||||||
stop_ffmpeg(self.audio_listener, self.logger)
|
stop_ffmpeg(self.audio_listener, self.logger)
|
||||||
self.logpipe.close()
|
self.logpipe.close()
|
||||||
|
self.requestor.stop()
|
||||||
|
@ -175,7 +175,7 @@ def parse_preset_hardware_acceleration_scale(
|
|||||||
if not isinstance(arg, str) or " " in arg:
|
if not isinstance(arg, str) or " " in arg:
|
||||||
scale = PRESETS_HW_ACCEL_SCALE["default"]
|
scale = PRESETS_HW_ACCEL_SCALE["default"]
|
||||||
else:
|
else:
|
||||||
scale = PRESETS_HW_ACCEL_SCALE.get(arg, "")
|
scale = PRESETS_HW_ACCEL_SCALE.get(arg, PRESETS_HW_ACCEL_SCALE["default"])
|
||||||
|
|
||||||
scale = scale.format(fps, width, height).split(" ")
|
scale = scale.format(fps, width, height).split(" ")
|
||||||
scale.extend(detect_args)
|
scale.extend(detect_args)
|
||||||
|
@ -30,7 +30,6 @@ logger = logging.getLogger(__name__)
|
|||||||
def output_frames(
|
def output_frames(
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
video_output_queue: mp.Queue,
|
video_output_queue: mp.Queue,
|
||||||
inter_process_queue: mp.Queue,
|
|
||||||
camera_metrics: dict[str, CameraMetricsTypes],
|
camera_metrics: dict[str, CameraMetricsTypes],
|
||||||
):
|
):
|
||||||
threading.current_thread().name = "output"
|
threading.current_thread().name = "output"
|
||||||
@ -68,7 +67,7 @@ def output_frames(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
jsmpeg_cameras[camera] = JsmpegCamera(cam_config, stop_event, websocket_server)
|
jsmpeg_cameras[camera] = JsmpegCamera(cam_config, stop_event, websocket_server)
|
||||||
preview_recorders[camera] = PreviewRecorder(cam_config, inter_process_queue)
|
preview_recorders[camera] = PreviewRecorder(cam_config)
|
||||||
|
|
||||||
if config.birdseye.enabled:
|
if config.birdseye.enabled:
|
||||||
birdseye = Birdseye(
|
birdseye = Birdseye(
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import multiprocessing as mp
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
@ -12,6 +11,7 @@ from pathlib import Path
|
|||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import CameraConfig, RecordQualityEnum
|
from frigate.config import CameraConfig, RecordQualityEnum
|
||||||
from frigate.const import CACHE_DIR, CLIPS_DIR, INSERT_PREVIEW
|
from frigate.const import CACHE_DIR, CLIPS_DIR, INSERT_PREVIEW
|
||||||
from frigate.ffmpeg_presets import (
|
from frigate.ffmpeg_presets import (
|
||||||
@ -53,13 +53,13 @@ class FFMpegConverter(threading.Thread):
|
|||||||
self,
|
self,
|
||||||
config: CameraConfig,
|
config: CameraConfig,
|
||||||
frame_times: list[float],
|
frame_times: list[float],
|
||||||
inter_process_queue: mp.Queue,
|
requestor: InterProcessRequestor,
|
||||||
):
|
):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.name = f"{config.name}_preview_converter"
|
self.name = f"{config.name}_preview_converter"
|
||||||
self.config = config
|
self.config = config
|
||||||
self.frame_times = frame_times
|
self.frame_times = frame_times
|
||||||
self.inter_process_queue = inter_process_queue
|
self.requestor = requestor
|
||||||
self.path = os.path.join(
|
self.path = os.path.join(
|
||||||
CLIPS_DIR,
|
CLIPS_DIR,
|
||||||
f"previews/{self.config.name}/{self.frame_times[0]}-{self.frame_times[-1]}.mp4",
|
f"previews/{self.config.name}/{self.frame_times[0]}-{self.frame_times[-1]}.mp4",
|
||||||
@ -105,18 +105,16 @@ class FFMpegConverter(threading.Thread):
|
|||||||
|
|
||||||
if p.returncode == 0:
|
if p.returncode == 0:
|
||||||
logger.debug("successfully saved preview")
|
logger.debug("successfully saved preview")
|
||||||
self.inter_process_queue.put_nowait(
|
self.requestor.send_data(
|
||||||
(
|
INSERT_PREVIEW,
|
||||||
INSERT_PREVIEW,
|
{
|
||||||
{
|
Previews.id: f"{self.config.name}_{end}",
|
||||||
Previews.id: f"{self.config.name}_{end}",
|
Previews.camera: self.config.name,
|
||||||
Previews.camera: self.config.name,
|
Previews.path: self.path,
|
||||||
Previews.path: self.path,
|
Previews.start_time: start,
|
||||||
Previews.start_time: start,
|
Previews.end_time: end,
|
||||||
Previews.end_time: end,
|
Previews.duration: end - start,
|
||||||
Previews.duration: end - start,
|
},
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Error saving preview for {self.config.name} :: {p.stderr}")
|
logger.error(f"Error saving preview for {self.config.name} :: {p.stderr}")
|
||||||
@ -128,9 +126,8 @@ class FFMpegConverter(threading.Thread):
|
|||||||
|
|
||||||
|
|
||||||
class PreviewRecorder:
|
class PreviewRecorder:
|
||||||
def __init__(self, config: CameraConfig, inter_process_queue: mp.Queue) -> None:
|
def __init__(self, config: CameraConfig) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
self.inter_process_queue = inter_process_queue
|
|
||||||
self.start_time = 0
|
self.start_time = 0
|
||||||
self.last_output_time = 0
|
self.last_output_time = 0
|
||||||
self.output_frames = []
|
self.output_frames = []
|
||||||
@ -139,6 +136,9 @@ class PreviewRecorder:
|
|||||||
int((config.detect.width / config.detect.height) * self.out_height) // 4 * 4
|
int((config.detect.width / config.detect.height) * self.out_height) // 4 * 4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# create communication for finished previews
|
||||||
|
self.requestor = InterProcessRequestor()
|
||||||
|
|
||||||
y, u1, u2, v1, v2 = get_yuv_crop(
|
y, u1, u2, v1, v2 = get_yuv_crop(
|
||||||
self.config.frame_shape_yuv,
|
self.config.frame_shape_yuv,
|
||||||
(
|
(
|
||||||
@ -237,7 +237,7 @@ class PreviewRecorder:
|
|||||||
FFMpegConverter(
|
FFMpegConverter(
|
||||||
self.config,
|
self.config,
|
||||||
self.output_frames,
|
self.output_frames,
|
||||||
self.inter_process_queue,
|
self.requestor,
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
# reset frame cache
|
# reset frame cache
|
||||||
@ -262,3 +262,5 @@ class PreviewRecorder:
|
|||||||
shutil.rmtree(os.path.join(CACHE_DIR, FOLDER_PREVIEW_FRAMES))
|
shutil.rmtree(os.path.join(CACHE_DIR, FOLDER_PREVIEW_FRAMES))
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
self.requestor.stop()
|
||||||
|
@ -17,6 +17,7 @@ from typing import Any, Optional, Tuple
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import FrigateConfig, RetainModeEnum
|
from frigate.config import FrigateConfig, RetainModeEnum
|
||||||
from frigate.const import (
|
from frigate.const import (
|
||||||
CACHE_DIR,
|
CACHE_DIR,
|
||||||
@ -59,7 +60,6 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
inter_process_queue: mp.Queue,
|
|
||||||
object_recordings_info_queue: mp.Queue,
|
object_recordings_info_queue: mp.Queue,
|
||||||
audio_recordings_info_queue: Optional[mp.Queue],
|
audio_recordings_info_queue: Optional[mp.Queue],
|
||||||
process_info: dict[str, FeatureMetricsTypes],
|
process_info: dict[str, FeatureMetricsTypes],
|
||||||
@ -68,7 +68,10 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.name = "recording_maintainer"
|
self.name = "recording_maintainer"
|
||||||
self.config = config
|
self.config = config
|
||||||
self.inter_process_queue = inter_process_queue
|
|
||||||
|
# create communication for retained recordings
|
||||||
|
self.requestor = InterProcessRequestor()
|
||||||
|
|
||||||
self.object_recordings_info_queue = object_recordings_info_queue
|
self.object_recordings_info_queue = object_recordings_info_queue
|
||||||
self.audio_recordings_info_queue = audio_recordings_info_queue
|
self.audio_recordings_info_queue = audio_recordings_info_queue
|
||||||
self.process_info = process_info
|
self.process_info = process_info
|
||||||
@ -183,8 +186,9 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
recordings_to_insert: list[Optional[Recordings]] = await asyncio.gather(*tasks)
|
recordings_to_insert: list[Optional[Recordings]] = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
# fire and forget recordings entries
|
# fire and forget recordings entries
|
||||||
self.inter_process_queue.put(
|
self.requestor.send_data(
|
||||||
(INSERT_MANY_RECORDINGS, [r for r in recordings_to_insert if r is not None])
|
INSERT_MANY_RECORDINGS,
|
||||||
|
[r for r in recordings_to_insert if r is not None],
|
||||||
)
|
)
|
||||||
|
|
||||||
async def validate_and_move_segment(
|
async def validate_and_move_segment(
|
||||||
@ -525,4 +529,5 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
duration = datetime.datetime.now().timestamp() - run_start
|
duration = datetime.datetime.now().timestamp() - run_start
|
||||||
wait_time = max(0, 5 - duration)
|
wait_time = max(0, 5 - duration)
|
||||||
|
|
||||||
|
self.requestor.stop()
|
||||||
logger.info("Exiting recording maintenance...")
|
logger.info("Exiting recording maintenance...")
|
||||||
|
@ -21,7 +21,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def manage_recordings(
|
def manage_recordings(
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
inter_process_queue: mp.Queue,
|
|
||||||
object_recordings_info_queue: mp.Queue,
|
object_recordings_info_queue: mp.Queue,
|
||||||
audio_recordings_info_queue: mp.Queue,
|
audio_recordings_info_queue: mp.Queue,
|
||||||
process_info: dict[str, FeatureMetricsTypes],
|
process_info: dict[str, FeatureMetricsTypes],
|
||||||
@ -52,7 +51,6 @@ def manage_recordings(
|
|||||||
|
|
||||||
maintainer = RecordingMaintainer(
|
maintainer = RecordingMaintainer(
|
||||||
config,
|
config,
|
||||||
inter_process_queue,
|
|
||||||
object_recordings_info_queue,
|
object_recordings_info_queue,
|
||||||
audio_recordings_info_queue,
|
audio_recordings_info_queue,
|
||||||
process_info,
|
process_info,
|
||||||
|
@ -11,6 +11,7 @@ import time
|
|||||||
import cv2
|
import cv2
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import CameraConfig, DetectConfig, ModelConfig
|
from frigate.config import CameraConfig, DetectConfig, ModelConfig
|
||||||
from frigate.const import (
|
from frigate.const import (
|
||||||
ALL_ATTRIBUTE_LABELS,
|
ALL_ATTRIBUTE_LABELS,
|
||||||
@ -388,7 +389,6 @@ def track_camera(
|
|||||||
detection_queue,
|
detection_queue,
|
||||||
result_connection,
|
result_connection,
|
||||||
detected_objects_queue,
|
detected_objects_queue,
|
||||||
inter_process_queue,
|
|
||||||
process_info,
|
process_info,
|
||||||
ptz_metrics,
|
ptz_metrics,
|
||||||
region_grid,
|
region_grid,
|
||||||
@ -406,7 +406,6 @@ def track_camera(
|
|||||||
listen()
|
listen()
|
||||||
|
|
||||||
frame_queue = process_info["frame_queue"]
|
frame_queue = process_info["frame_queue"]
|
||||||
region_grid_queue = process_info["region_grid_queue"]
|
|
||||||
detection_enabled = process_info["detection_enabled"]
|
detection_enabled = process_info["detection_enabled"]
|
||||||
motion_enabled = process_info["motion_enabled"]
|
motion_enabled = process_info["motion_enabled"]
|
||||||
improve_contrast_enabled = process_info["improve_contrast_enabled"]
|
improve_contrast_enabled = process_info["improve_contrast_enabled"]
|
||||||
@ -433,11 +432,13 @@ def track_camera(
|
|||||||
|
|
||||||
frame_manager = SharedMemoryFrameManager()
|
frame_manager = SharedMemoryFrameManager()
|
||||||
|
|
||||||
|
# create communication for region grid updates
|
||||||
|
requestor = InterProcessRequestor()
|
||||||
|
|
||||||
process_frames(
|
process_frames(
|
||||||
name,
|
name,
|
||||||
inter_process_queue,
|
requestor,
|
||||||
frame_queue,
|
frame_queue,
|
||||||
region_grid_queue,
|
|
||||||
frame_shape,
|
frame_shape,
|
||||||
model_config,
|
model_config,
|
||||||
config.detect,
|
config.detect,
|
||||||
@ -505,9 +506,8 @@ def detect(
|
|||||||
|
|
||||||
def process_frames(
|
def process_frames(
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
inter_process_queue: mp.Queue,
|
requestor: InterProcessRequestor,
|
||||||
frame_queue: mp.Queue,
|
frame_queue: mp.Queue,
|
||||||
region_grid_queue: mp.Queue,
|
|
||||||
frame_shape,
|
frame_shape,
|
||||||
model_config: ModelConfig,
|
model_config: ModelConfig,
|
||||||
detect_config: DetectConfig,
|
detect_config: DetectConfig,
|
||||||
@ -544,13 +544,7 @@ def process_frames(
|
|||||||
datetime.datetime.now().astimezone(datetime.timezone.utc)
|
datetime.datetime.now().astimezone(datetime.timezone.utc)
|
||||||
> next_region_update
|
> next_region_update
|
||||||
):
|
):
|
||||||
inter_process_queue.put((REQUEST_REGION_GRID, camera_name))
|
region_grid = requestor.send_data(REQUEST_REGION_GRID, camera_name)
|
||||||
|
|
||||||
try:
|
|
||||||
region_grid = region_grid_queue.get(True, 10)
|
|
||||||
except queue.Empty:
|
|
||||||
logger.error(f"Unable to get updated region grid for {camera_name}")
|
|
||||||
|
|
||||||
next_region_update = get_tomorrow_at_time(2)
|
next_region_update = get_tomorrow_at_time(2)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -826,3 +820,5 @@ def process_frames(
|
|||||||
)
|
)
|
||||||
detection_fps.value = object_detector.fps.eps()
|
detection_fps.value = object_detector.fps.eps()
|
||||||
frame_manager.close(f"{camera_name}{frame_time}")
|
frame_manager.close(f"{camera_name}{frame_time}")
|
||||||
|
|
||||||
|
requestor.stop()
|
||||||
|
Loading…
Reference in New Issue
Block a user