Expose dBFS when doing audio analysis

This commit is contained in:
JP Verdejo 2023-06-26 19:33:11 +00:00
parent ba3c7f59e5
commit 39a33f6143
3 changed files with 50 additions and 1 deletions

View File

@ -259,6 +259,7 @@ class FrigateApp:
self.onvif_controller, self.onvif_controller,
self.external_event_processor, self.external_event_processor,
self.plus_api, self.plus_api,
self.dispatcher,
) )
def init_onvif(self) -> None: def init_onvif(self) -> None:

View File

@ -167,9 +167,22 @@ class AudioEventMaintainer(threading.Thread):
if not self.feature_metrics[self.config.name]["audio_enabled"].value: if not self.feature_metrics[self.config.name]["audio_enabled"].value:
return return
waveform = (audio / AUDIO_MAX_BIT_RANGE).astype(np.float32) audio_as_float = audio.astype(np.float32)
waveform = audio_as_float / AUDIO_MAX_BIT_RANGE
model_detections = self.detector.detect(waveform) model_detections = self.detector.detect(waveform)
# Calculate RMS (Root-Mean-Square) which represents the average signal amplitude
# Note: np.float32 isn't serializable, we must use np.float64 to publish the message
rms = np.sqrt(np.mean(np.absolute(audio_as_float**2))).astype(np.float64)
# Transform RMS to dBFS (decibels relative to full scale)
dBFS = 20 * np.log10(np.abs(rms) / AUDIO_MAX_BIT_RANGE)
requests.post(
f"http://127.0.0.1:5000/api/{self.config.name}/metadata",
json={"dBFS": dBFS, "rms": rms},
)
for label, score, _ in model_detections: for label, score, _ in model_detections:
if label not in self.config.audio.listen: if label not in self.config.audio.listen:
continue continue

View File

@ -28,6 +28,7 @@ from peewee import DoesNotExist, SqliteDatabase, fn, operator
from playhouse.shortcuts import model_to_dict from playhouse.shortcuts import model_to_dict
from tzlocal import get_localzone_name from tzlocal import get_localzone_name
from frigate.comms.dispatcher import Dispatcher
from frigate.config import FrigateConfig from frigate.config import FrigateConfig
from frigate.const import CLIPS_DIR, MAX_SEGMENT_DURATION, RECORD_DIR from frigate.const import CLIPS_DIR, MAX_SEGMENT_DURATION, RECORD_DIR
from frigate.events.external import ExternalEventProcessor from frigate.events.external import ExternalEventProcessor
@ -61,6 +62,7 @@ def create_app(
onvif: OnvifController, onvif: OnvifController,
external_processor: ExternalEventProcessor, external_processor: ExternalEventProcessor,
plus_api: PlusApi, plus_api: PlusApi,
dispatcher: Dispatcher,
): ):
app = Flask(__name__) app = Flask(__name__)
@ -81,6 +83,7 @@ def create_app(
app.onvif = onvif app.onvif = onvif
app.external_processor = external_processor app.external_processor = external_processor
app.plus_api = plus_api app.plus_api = plus_api
app.dispatcher = dispatcher
app.camera_error_image = None app.camera_error_image = None
app.hwaccel_errors = [] app.hwaccel_errors = []
@ -1396,6 +1399,38 @@ def recording_clip(camera_name, start_ts, end_ts):
return response return response
@bp.route("/<camera_name>/metadata", methods=["POST"])
def create_metadata_message(camera_name):
if not camera_name or not current_app.frigate_config.cameras.get(camera_name):
return jsonify(
{"success": False, "message": f"{camera_name} is not a valid camera."}, 404
)
request_json = request.get_json(silent=True)
if request_json == {}:
return jsonify(
{"success": False, "message": "Metadata json cannot be empty."}, 404
)
try:
current_app.dispatcher.publish(
"metadata", json.dumps(request_json), retain=False
)
except Exception as e:
logger.error(f"The error is {e}")
return jsonify(
{"success": False, "message": f"An unknown error occurred: {e}"}, 404
)
return jsonify(
{
"success": True,
"message": "Successfully published metadata message.",
},
200,
)
@bp.route("/vod/<camera_name>/start/<int:start_ts>/end/<int:end_ts>") @bp.route("/vod/<camera_name>/start/<int:start_ts>/end/<int:end_ts>")
@bp.route("/vod/<camera_name>/start/<float:start_ts>/end/<float:end_ts>") @bp.route("/vod/<camera_name>/start/<float:start_ts>/end/<float:end_ts>")
def vod_ts(camera_name, start_ts, end_ts): def vod_ts(camera_name, start_ts, end_ts):