Use api instead of event queue to save audio events

This commit is contained in:
Nick Mowen 2023-06-19 07:50:02 -06:00
parent f0a59da933
commit cc0cb3a78d
5 changed files with 33 additions and 72 deletions

View File

@ -395,7 +395,7 @@ class FrigateApp:
audio_process = mp.Process( audio_process = mp.Process(
target=listen_to_audio, target=listen_to_audio,
name=f"audio_capture", name=f"audio_capture",
args=(self.config, self.event_queue) args=(self.config)
) )
audio_process.daemon = True audio_process.daemon = True
audio_process.start() audio_process.start()

View File

@ -6,9 +6,9 @@ import multiprocessing as mp
import numpy as np import numpy as np
import os import os
import random import random
import requests
import signal import signal
import string import string
import subprocess as sp
import threading import threading
from types import FrameType from types import FrameType
from typing import Optional from typing import Optional
@ -17,7 +17,6 @@ from setproctitle import setproctitle
from frigate.config import CameraConfig, FrigateConfig from frigate.config import CameraConfig, FrigateConfig
from frigate.const import ( from frigate.const import (
AUDIO_DETECTOR,
AUDIO_DURATION, AUDIO_DURATION,
AUDIO_FORMAT, AUDIO_FORMAT,
AUDIO_SAMPLE_RATE, AUDIO_SAMPLE_RATE,
@ -42,7 +41,7 @@ FFMPEG_COMMAND = (
) )
def listen_to_audio(config: FrigateConfig, event_queue: mp.Queue) -> None: def listen_to_audio(config: FrigateConfig) -> None:
stop_event = mp.Event() stop_event = mp.Event()
def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None: def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None:
@ -57,7 +56,7 @@ def listen_to_audio(config: FrigateConfig, event_queue: mp.Queue) -> None:
for camera in config.cameras.values(): for camera in config.cameras.values():
if camera.enabled and camera.audio.enabled: if camera.enabled and camera.audio.enabled:
AudioEventMaintainer(camera, event_queue, stop_event).start() AudioEventMaintainer(camera, stop_event).start()
class AudioTfl: class AudioTfl:
@ -116,13 +115,10 @@ class AudioTfl:
class AudioEventMaintainer(threading.Thread): class AudioEventMaintainer(threading.Thread):
def __init__( def __init__(self, camera: CameraConfig, stop_event: mp.Event) -> None:
self, camera: CameraConfig, event_queue: mp.Queue, stop_event: mp.Event
) -> 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"
self.config = camera self.config = camera
self.queue = event_queue
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() self.detector = AudioTfl()
@ -161,35 +157,31 @@ class AudioEventMaintainer(threading.Thread):
"last_detection" "last_detection"
] = datetime.datetime.now().timestamp() ] = datetime.datetime.now().timestamp()
else: else:
now = datetime.datetime.now().timestamp() resp = requests.post(
rand_id = "".join( f"http://127.0.0.1:5000/api/events/{self.config.name}/{label}/create",
random.choices(string.ascii_lowercase + string.digits, k=6) json={"duration": None},
) )
event_id = f"{now}-{rand_id}"
if resp.status_code == 200:
event_id = resp.json["event_id"]
self.detections[label] = { self.detections[label] = {
"id": event_id, "id": event_id,
"label": label, "last_detection": datetime.datetime.now().timestamp(),
"camera": self.config.name,
"score": score,
"start_time": now - self.config.record.events.pre_capture,
"last_detection": now,
} }
self.queue.put(
(EventTypeEnum.audio, "start", self.config.name, self.detections[label])
)
def expire_detections(self) -> None: def expire_detections(self) -> None:
now = datetime.datetime.now().timestamp() now = datetime.datetime.now().timestamp()
for detection in self.detections.values(): for detection in self.detections.values():
if now - detection["last_detection"] > self.config.audio.max_not_heard: if now - detection["last_detection"] > self.config.audio.max_not_heard:
detection["end_time"] = (
detection["last_detection"] + self.config.record.events.post_capture
)
self.queue.put(
(EventTypeEnum.audio, "end", self.config.name, detection)
)
self.detections[detection["label"]] = None self.detections[detection["label"]] = None
requests.put(
f"http://127.0.0.1/api/events/{detection['event_id']}/end",
json={
"end_time": detection["last_detection"]
+ self.config.record.events.post_capture
},
)
def restart_audio_pipe(self) -> None: def restart_audio_pipe(self) -> None:
try: try:

View File

@ -67,11 +67,10 @@ class ExternalEventProcessor:
return event_id return event_id
def finish_manual_event(self, event_id: str) -> None: def finish_manual_event(self, event_id: str, end_time: float) -> None:
"""Finish external event with indeterminate duration.""" """Finish external event with indeterminate duration."""
now = datetime.datetime.now().timestamp()
self.queue.put( self.queue.put(
(EventTypeEnum.api, "end", None, {"id": event_id, "end_time": now}) (EventTypeEnum.api, "end", None, {"id": event_id, "end_time": end_time})
) )
def _write_images( def _write_images(

View File

@ -17,7 +17,6 @@ logger = logging.getLogger(__name__)
class EventTypeEnum(str, Enum): class EventTypeEnum(str, Enum):
api = "api" api = "api"
audio = "audio"
tracked_object = "tracked_object" tracked_object = "tracked_object"
@ -92,8 +91,6 @@ class EventProcessor(threading.Thread):
continue continue
self.handle_object_detection(event_type, camera, event_data) self.handle_object_detection(event_type, camera, event_data)
elif source_type == EventTypeEnum.audio:
self.handle_audio_detection(event_type, event_data)
elif source_type == EventTypeEnum.api: elif source_type == EventTypeEnum.api:
self.handle_external_detection(event_type, event_data) self.handle_external_detection(event_type, event_data)
@ -218,30 +215,6 @@ class EventProcessor(threading.Thread):
del self.events_in_process[event_data["id"]] del self.events_in_process[event_data["id"]]
self.event_processed_queue.put((event_data["id"], camera)) self.event_processed_queue.put((event_data["id"], camera))
def handle_audio_detection(self, type: str, event_data: Event) -> None:
event = {
Event.id: event_data["id"],
Event.label: event_data["label"],
Event.score: event_data["score"],
Event.camera: event_data["camera"],
Event.start_time: event_data["start_time"],
Event.end_time: event_data.get("end_time"),
Event.thumbnail: "",
Event.has_clip: True,
Event.has_snapshot: True,
Event.zones: [],
Event.data: {},
}
(
Event.insert(event)
.on_conflict(
conflict_target=[Event.id],
update=event,
)
.execute()
)
def handle_external_detection(self, type: str, event_data: Event) -> None: def handle_external_detection(self, type: str, event_data: Event) -> None:
if type == "new": if type == "new":
event = { event = {
@ -257,6 +230,7 @@ class EventProcessor(threading.Thread):
Event.zones: [], Event.zones: [],
Event.data: {}, Event.data: {},
} }
Event.insert(event).execute()
elif type == "end": elif type == "end":
event = { event = {
Event.id: event_data["id"], Event.id: event_data["id"],
@ -264,13 +238,6 @@ class EventProcessor(threading.Thread):
} }
try: try:
( Event.update(event).execute()
Event.insert(event)
.on_conflict(
conflict_target=[Event.id],
update=event,
)
.execute()
)
except Exception: except Exception:
logger.warning(f"Failed to update manual event: {event_data['id']}") logger.warning(f"Failed to update manual event: {event_data['id']}")

View File

@ -890,8 +890,11 @@ def create_event(camera_name, label):
@bp.route("/events/<event_id>/end", methods=["PUT"]) @bp.route("/events/<event_id>/end", methods=["PUT"])
def end_event(event_id): def end_event(event_id):
json: dict[str, any] = request.get_json(silent=True) or {}
try: try:
current_app.external_processor.finish_manual_event(event_id) end_time = json.get("end_time", datetime.now().timestamp())
current_app.external_processor.finish_manual_event(event_id, end_time)
except Exception: except Exception:
return jsonify( return jsonify(
{"success": False, "message": f"{event_id} must be set and valid."}, 404 {"success": False, "message": f"{event_id} must be set and valid."}, 404