mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-05 18:55:23 +03:00
Use api instead of event queue to save audio events
This commit is contained in:
parent
f0a59da933
commit
cc0cb3a78d
@ -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()
|
||||||
|
|||||||
@ -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}"
|
|
||||||
self.detections[label] = {
|
|
||||||
"id": event_id,
|
|
||||||
"label": label,
|
|
||||||
"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])
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if resp.status_code == 200:
|
||||||
|
event_id = resp.json["event_id"]
|
||||||
|
self.detections[label] = {
|
||||||
|
"id": event_id,
|
||||||
|
"last_detection": datetime.datetime.now().timestamp(),
|
||||||
|
}
|
||||||
|
|
||||||
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:
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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,20 +230,14 @@ 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"],
|
||||||
Event.end_time: event_data["end_time"],
|
Event.end_time: event_data["end_time"],
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(
|
Event.update(event).execute()
|
||||||
Event.insert(event)
|
except Exception:
|
||||||
.on_conflict(
|
logger.warning(f"Failed to update manual event: {event_data['id']}")
|
||||||
conflict_target=[Event.id],
|
|
||||||
update=event,
|
|
||||||
)
|
|
||||||
.execute()
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
logger.warning(f"Failed to update manual event: {event_data['id']}")
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user