From cbb77e579464c78f42494465611e1544dfabb9e2 Mon Sep 17 00:00:00 2001 From: Sergey Krashevich Date: Thu, 6 Jul 2023 21:50:22 +0300 Subject: [PATCH 01/20] Bugfix: SqliteQueueDatabase instead of SqliteDatabase and retry_interval for fetching latest frame in http.py (#7059) * Refactor http.py to use SqliteQueueDatabase instead of SqliteDatabase and add retry interval for fetching latest frame * isort && black --- frigate/http.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frigate/http.py b/frigate/http.py index fe6dc54ef..db3ccb8df 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -24,8 +24,9 @@ from flask import ( make_response, request, ) -from peewee import DoesNotExist, SqliteDatabase, fn, operator +from peewee import DoesNotExist, fn, operator from playhouse.shortcuts import model_to_dict +from playhouse.sqliteq import SqliteQueueDatabase from tzlocal import get_localzone_name from frigate.config import FrigateConfig @@ -49,7 +50,7 @@ bp = Blueprint("frigate", __name__) def create_app( frigate_config, - database: SqliteDatabase, + database: SqliteQueueDatabase, stats_tracking, detected_frames_processor, storage_maintainer: StorageMaintainer, @@ -1099,10 +1100,14 @@ def latest_frame(camera_name): frame = current_app.detected_frames_processor.get_current_frame( camera_name, draw_options ) + retry_interval = float( + current_app.frigate_config.cameras.get(camera_name).ffmpeg.retry_interval + or 10 + ) if frame is None or datetime.now().timestamp() > ( current_app.detected_frames_processor.get_current_frame_time(camera_name) - + 10 + + retry_interval ): if current_app.camera_error_image is None: error_image = glob.glob("/opt/frigate/frigate/images/camera-error.jpg") From 22cc2712a6c6a8beb860b7601a042bce19ceb24d Mon Sep 17 00:00:00 2001 From: Sergey Krashevich Date: Thu, 6 Jul 2023 21:51:28 +0300 Subject: [PATCH 02/20] Bump NGINX version to 1.25.1, VOD module version to 1.31, secure token module version to 1.5, and RTMP module version to 1.2.2 (#7058) --- docker/build_nginx.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/build_nginx.sh b/docker/build_nginx.sh index 1e7bfad21..56c9a146d 100755 --- a/docker/build_nginx.sh +++ b/docker/build_nginx.sh @@ -2,10 +2,10 @@ set -euxo pipefail -NGINX_VERSION="1.22.1" -VOD_MODULE_VERSION="1.30" -SECURE_TOKEN_MODULE_VERSION="1.4" -RTMP_MODULE_VERSION="1.2.1" +NGINX_VERSION="1.25.1" +VOD_MODULE_VERSION="1.31" +SECURE_TOKEN_MODULE_VERSION="1.5" +RTMP_MODULE_VERSION="1.2.2" cp /etc/apt/sources.list /etc/apt/sources.list.d/sources-src.list sed -i 's|deb http|deb-src http|g' /etc/apt/sources.list.d/sources-src.list From f48dd8c1abb8e7c180fdc03cfc5acea16d96315d Mon Sep 17 00:00:00 2001 From: Sergey Krashevich Date: Thu, 6 Jul 2023 21:54:26 +0300 Subject: [PATCH 03/20] Feature: camera debug/config enhancements (#6920) * Add functionality to update YAML config file with PUT request in HTTP endpoint * Refactor copying of text to clipboard with Clipboard API and fallback to document.execCommand('copy') in CameraMap.jsx file * Update YAML file from URL query parameters in frigate/http.py and add functionality to save motion masks, zones, and object masks in CameraMap.jsx * formatting * fix merging fuckup * Refactor camera zone coordinate saving to use single query parameter per zone in CameraMap.jsx * remove unnecessary print statements in util.py * Refactor update_yaml_file function to separate the logic for updating YAML data into a new function update_yaml(). * fix merge errors * Refactor code to improve error handling and add dependencies to useEffect hooks --- frigate/http.py | 50 ++++++++++++++- frigate/util/builtin.py | 75 ++++++++++++++++++++++ requirements-wheels.txt | 1 + web/src/routes/CameraMap.jsx | 121 ++++++++++++++++++++++++++++++++--- 4 files changed, 236 insertions(+), 11 deletions(-) diff --git a/frigate/http.py b/frigate/http.py index db3ccb8df..ba62af047 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -30,7 +30,7 @@ from playhouse.sqliteq import SqliteQueueDatabase from tzlocal import get_localzone_name from frigate.config import FrigateConfig -from frigate.const import CLIPS_DIR, MAX_SEGMENT_DURATION, RECORD_DIR +from frigate.const import CLIPS_DIR, CONFIG_DIR, MAX_SEGMENT_DURATION, RECORD_DIR from frigate.events.external import ExternalEventProcessor from frigate.models import Event, Recordings, Timeline from frigate.object_processing import TrackedObject @@ -39,7 +39,11 @@ from frigate.ptz import OnvifController from frigate.record.export import PlaybackFactorEnum, RecordingExporter from frigate.stats import stats_snapshot from frigate.storage import StorageMaintainer -from frigate.util.builtin import clean_camera_user_pass, get_tz_modifiers +from frigate.util.builtin import ( + clean_camera_user_pass, + get_tz_modifiers, + update_yaml_from_url, +) from frigate.util.services import ffprobe_stream, restart_frigate, vainfo_hwaccel from frigate.version import VERSION @@ -1026,6 +1030,48 @@ def config_save(): return "Config successfully saved.", 200 +@bp.route("/config/set", methods=["PUT"]) +def config_set(): + config_file = os.environ.get("CONFIG_FILE", f"{CONFIG_DIR}/config.yml") + + # Check if we can use .yaml instead of .yml + config_file_yaml = config_file.replace(".yml", ".yaml") + + if os.path.isfile(config_file_yaml): + config_file = config_file_yaml + + with open(config_file, "r") as f: + old_raw_config = f.read() + f.close() + + try: + update_yaml_from_url(config_file, request.url) + with open(config_file, "r") as f: + new_raw_config = f.read() + f.close() + # Validate the config schema + try: + FrigateConfig.parse_raw(new_raw_config) + except Exception: + with open(config_file, "w") as f: + f.write(old_raw_config) + f.close() + return make_response( + jsonify( + { + "success": False, + "message": f"\nConfig Error:\n\n{str(traceback.format_exc())}", + } + ), + 400, + ) + except Exception as e: + logging.error(f"Error updating config: {e}") + return "Error updating config", 500 + + return "Config successfully updated", 200 + + @bp.route("/config/schema.json") def config_schema(): return current_app.response_class( diff --git a/frigate/util/builtin.py b/frigate/util/builtin.py index 900764a23..f55ea5e37 100644 --- a/frigate/util/builtin.py +++ b/frigate/util/builtin.py @@ -14,10 +14,12 @@ from collections.abc import Mapping from queue import Empty, Full from typing import Any, Tuple +import numpy as np import pytz import yaml from faster_fifo import DEFAULT_CIRCULAR_BUFFER_SIZE, DEFAULT_TIMEOUT from faster_fifo import Queue as FFQueue +from ruamel.yaml import YAML from frigate.const import REGEX_HTTP_CAMERA_USER_PASS, REGEX_RTSP_CAMERA_USER_PASS @@ -224,3 +226,76 @@ def to_relative_box( (box[2] - box[0]) / width, # w (box[3] - box[1]) / height, # h ) + + +def create_mask(frame_shape, mask): + mask_img = np.zeros(frame_shape, np.uint8) + mask_img[:] = 255 + + +def update_yaml_from_url(file_path, url): + parsed_url = urllib.parse.urlparse(url) + query_string = urllib.parse.parse_qs(parsed_url.query, keep_blank_values=True) + + for key_path_str, new_value_list in query_string.items(): + key_path = key_path_str.split(".") + for i in range(len(key_path)): + try: + index = int(key_path[i]) + key_path[i] = (key_path[i - 1], index) + key_path.pop(i - 1) + except ValueError: + pass + new_value = new_value_list[0] + update_yaml_file(file_path, key_path, new_value) + + +def update_yaml_file(file_path, key_path, new_value): + yaml = YAML() + with open(file_path, "r") as f: + data = yaml.load(f) + + data = update_yaml(data, key_path, new_value) + + with open(file_path, "w") as f: + yaml.dump(data, f) + + +def update_yaml(data, key_path, new_value): + temp = data + for key in key_path[:-1]: + if isinstance(key, tuple): + if key[0] not in temp: + temp[key[0]] = [{}] * max(1, key[1] + 1) + elif len(temp[key[0]]) <= key[1]: + temp[key[0]] += [{}] * (key[1] - len(temp[key[0]]) + 1) + temp = temp[key[0]][key[1]] + else: + if key not in temp: + temp[key] = {} + temp = temp[key] + + last_key = key_path[-1] + if new_value == "": + if isinstance(last_key, tuple): + del temp[last_key[0]][last_key[1]] + else: + del temp[last_key] + else: + if isinstance(last_key, tuple): + if last_key[0] not in temp: + temp[last_key[0]] = [{}] * max(1, last_key[1] + 1) + elif len(temp[last_key[0]]) <= last_key[1]: + temp[last_key[0]] += [{}] * (last_key[1] - len(temp[last_key[0]]) + 1) + temp[last_key[0]][last_key[1]] = new_value + else: + if ( + last_key in temp + and isinstance(temp[last_key], dict) + and isinstance(new_value, dict) + ): + temp[last_key].update(new_value) + else: + temp[last_key] = new_value + + return data diff --git a/requirements-wheels.txt b/requirements-wheels.txt index 778737a92..3232e8f31 100644 --- a/requirements-wheels.txt +++ b/requirements-wheels.txt @@ -15,6 +15,7 @@ pydantic == 1.10.* git+https://github.com/fbcotter/py3nvml#egg=py3nvml PyYAML == 6.0 pytz == 2023.3 +ruamel.yaml == 0.17.* tzlocal == 5.0.* types-PyYAML == 6.0.* requests == 2.31.* diff --git a/web/src/routes/CameraMap.jsx b/web/src/routes/CameraMap.jsx index ca77ec56e..2dce5597d 100644 --- a/web/src/routes/CameraMap.jsx +++ b/web/src/routes/CameraMap.jsx @@ -7,7 +7,7 @@ import { useResizeObserver } from '../hooks'; import { useCallback, useMemo, useRef, useState } from 'preact/hooks'; import { useApiHost } from '../api'; import useSWR from 'swr'; - +import axios from 'axios'; export default function CameraMasks({ camera }) { const { data: config } = useSWR('config'); const apiHost = useApiHost(); @@ -95,12 +95,53 @@ export default function CameraMasks({ camera }) { [motionMaskPoints, setMotionMaskPoints] ); - const handleCopyMotionMasks = useCallback(async () => { - await window.navigator.clipboard.writeText(` motion: - mask: -${motionMaskPoints.map((mask) => ` - ${polylinePointsToPolyline(mask)}`).join('\n')}`); + const handleCopyMotionMasks = useCallback(() => { + const textToCopy = ` motion: + mask: + ${motionMaskPoints.map((mask) => ` - ${polylinePointsToPolyline(mask)}`).join('\n')}`; + + if (window.navigator.clipboard && window.navigator.clipboard.writeText) { + // Use Clipboard API if available + window.navigator.clipboard.writeText(textToCopy).catch((err) => { + throw new Error('Failed to copy text: ', err); + }); + } else { + // Fallback to document.execCommand('copy') + const textarea = document.createElement('textarea'); + textarea.value = textToCopy; + document.body.appendChild(textarea); + textarea.select(); + + try { + const successful = document.execCommand('copy'); + if (!successful) { + throw new Error('Failed to copy text'); + } + } catch (err) { + throw new Error('Failed to copy text: ', err); + } + + document.body.removeChild(textarea); + } }, [motionMaskPoints]); + const handleSaveMotionMasks = useCallback(async () => { + try { + const queryParameters = motionMaskPoints + .map((mask, index) => `cameras.${camera}.motion.mask.${index}=${polylinePointsToPolyline(mask)}`) + .join('&'); + const endpoint = `config/set?${queryParameters}`; + const response = await axios.put(endpoint); + if (response.status === 200) { + // handle successful response + } + } catch (error) { + // handle error + //console.error(error); + } + }, [camera, motionMaskPoints]); + + // Zone methods const handleEditZone = useCallback( (key) => { @@ -127,15 +168,53 @@ ${motionMaskPoints.map((mask) => ` - ${polylinePointsToPolyline(mask)}`).jo ); const handleCopyZones = useCallback(async () => { - await window.navigator.clipboard.writeText(` zones: + const textToCopy = ` zones: ${Object.keys(zonePoints) .map( (zoneName) => ` ${zoneName}: - coordinates: ${polylinePointsToPolyline(zonePoints[zoneName])}` - ) - .join('\n')}`); + coordinates: ${polylinePointsToPolyline(zonePoints[zoneName])}`).join('\n')}`; + + if (window.navigator.clipboard && window.navigator.clipboard.writeText) { + // Use Clipboard API if available + window.navigator.clipboard.writeText(textToCopy).catch((err) => { + throw new Error('Failed to copy text: ', err); + }); + } else { + // Fallback to document.execCommand('copy') + const textarea = document.createElement('textarea'); + textarea.value = textToCopy; + document.body.appendChild(textarea); + textarea.select(); + + try { + const successful = document.execCommand('copy'); + if (!successful) { + throw new Error('Failed to copy text'); + } + } catch (err) { + throw new Error('Failed to copy text: ', err); + } + + document.body.removeChild(textarea); + } }, [zonePoints]); + const handleSaveZones = useCallback(async () => { + try { + const queryParameters = Object.keys(zonePoints) + .map((zoneName) => `cameras.${camera}.zones.${zoneName}.coordinates=${polylinePointsToPolyline(zonePoints[zoneName])}`) + .join('&'); + const endpoint = `config/set?${queryParameters}`; + const response = await axios.put(endpoint); + if (response.status === 200) { + // handle successful response + } + } catch (error) { + // handle error + //console.error(error); + } + }, [camera, zonePoints]); + // Object methods const handleEditObjectMask = useCallback( (key, subkey) => { @@ -175,6 +254,23 @@ ${Object.keys(objectMaskPoints) .join('\n')}`); }, [objectMaskPoints]); + const handleSaveObjectMasks = useCallback(async () => { + try { + const queryParameters = Object.keys(objectMaskPoints) + .filter((objectName) => objectMaskPoints[objectName].length > 0) + .map((objectName, index) => `cameras.${camera}.objects.filters.${objectName}.mask.${index}=${polylinePointsToPolyline(objectMaskPoints[objectName])}`) + .join('&'); + const endpoint = `config/set?${queryParameters}`; + const response = await axios.put(endpoint); + if (response.status === 200) { + // handle successful response + } + } catch (error) { + // handle error + //console.error(error); + } + }, [camera, objectMaskPoints]); + const handleAddToObjectMask = useCallback( (key) => { const newObjectMaskPoints = { ...objectMaskPoints, [key]: [...objectMaskPoints[key], []] }; @@ -246,6 +342,7 @@ ${Object.keys(objectMaskPoints) editing={editing} title="Motion masks" onCopy={handleCopyMotionMasks} + onSave={handleSaveMotionMasks} onCreate={handleAddMask} onEdit={handleEditMask} onRemove={handleRemoveMask} @@ -258,6 +355,7 @@ ${Object.keys(objectMaskPoints) editing={editing} title="Zones" onCopy={handleCopyZones} + onSave={handleSaveZones} onCreate={handleAddZone} onEdit={handleEditZone} onRemove={handleRemoveZone} @@ -272,6 +370,7 @@ ${Object.keys(objectMaskPoints) title="Object masks" onAdd={handleAddToObjectMask} onCopy={handleCopyObjectMasks} + onSave={handleSaveObjectMasks} onCreate={handleAddObjectMask} onEdit={handleEditObjectMask} onRemove={handleRemoveObjectMask} @@ -407,6 +506,7 @@ function MaskValues({ title, onAdd, onCopy, + onSave, onCreate, onEdit, onRemove, @@ -455,6 +555,8 @@ function MaskValues({ [onAdd] ); + + return (
@@ -463,6 +565,7 @@ function MaskValues({ +
         {yamlPrefix}

From 30dfdf47d4bc440ea1f558e4c3965798f64bb904 Mon Sep 17 00:00:00 2001
From: Sergey Krashevich 
Date: Thu, 6 Jul 2023 21:54:55 +0300
Subject: [PATCH 04/20] Add thread-safety to LimitedQueue by implementing a
 lock for put and get methods (#7053)

---
 frigate/util/builtin.py | 36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/frigate/util/builtin.py b/frigate/util/builtin.py
index f55ea5e37..2f623567c 100644
--- a/frigate/util/builtin.py
+++ b/frigate/util/builtin.py
@@ -78,29 +78,33 @@ class LimitedQueue(FFQueue):
         self.size = multiprocessing.RawValue(
             ctypes.c_int, 0
         )  # Add a counter for the number of items in the queue
+        self.lock = multiprocessing.Lock()  # Add a lock for thread-safety
 
     def put(self, x, block=True, timeout=DEFAULT_TIMEOUT):
-        if self.maxsize > 0 and self.size.value >= self.maxsize:
-            if block:
-                start_time = time.time()
-                while self.size.value >= self.maxsize:
-                    remaining = timeout - (time.time() - start_time)
-                    if remaining <= 0.0:
-                        raise Full
-                    time.sleep(min(remaining, 0.1))
-            else:
-                raise Full
-        self.size.value += 1
+        with self.lock:  # Ensure thread-safety
+            if self.maxsize > 0 and self.size.value >= self.maxsize:
+                if block:
+                    start_time = time.time()
+                    while self.size.value >= self.maxsize:
+                        remaining = timeout - (time.time() - start_time)
+                        if remaining <= 0.0:
+                            raise Full
+                        time.sleep(min(remaining, 0.1))
+                else:
+                    raise Full
+            self.size.value += 1
         return super().put(x, block=block, timeout=timeout)
 
     def get(self, block=True, timeout=DEFAULT_TIMEOUT):
-        if self.size.value <= 0 and not block:
-            raise Empty
-        self.size.value -= 1
-        return super().get(block=block, timeout=timeout)
+        item = super().get(block=block, timeout=timeout)
+        with self.lock:  # Ensure thread-safety
+            if self.size.value <= 0 and not block:
+                raise Empty
+            self.size.value -= 1
+        return item
 
     def qsize(self):
-        return self.size
+        return self.size.value
 
     def empty(self):
         return self.qsize() == 0

From dd02958f7c4cdb6a756e032aa8108300f052da2c Mon Sep 17 00:00:00 2001
From: Nate Meyer 
Date: Thu, 6 Jul 2023 15:20:33 -0400
Subject: [PATCH 05/20] Upgrade TensorRT to 8.5.3 (#7006)

* Update to latest tensorrt (8.6.1) release

* Build trt libyolo_layer.so in container

* Update tensorrt_models script to convert models from the frigate container

* Fix typo in model script

* Fix paths to yolo lib and models folder

* Add S6 scripts to test and convert specified TensortRT models at startup.

Rearrange tensorrt files into a docker support folder.

* Update TensorRT documentation to reflect the new model conversion process and minimum HW support.

* Fix model_cache path to live in config directory

* Move tensorrt s6 files to the correct directory

* Fix issues in model generation script

* Disable global timeout for s6 services

* Add version folder to tensorrt model_cache path

* Include TensorRT version 8.5.3

* Add numpy requirement prior to removal of np.bool

* This TRT version uses a mixture of cuda dependencies

* Redirect stdout from noisy model conversion
---
 Dockerfile                                    | 22 +++++++-
 docker/install_deps.sh                        |  2 +-
 .../etc/ld.so.conf.d/cuda_tensorrt.conf       |  1 +
 .../frigate/dependencies.d/trt-model-prepare  |  0
 .../trt-model-prepare/dependencies.d/base     |  0
 .../s6-overlay/s6-rc.d/trt-model-prepare/run  | 53 +++++++++++++++++++
 .../s6-overlay/s6-rc.d/trt-model-prepare/type |  1 +
 .../s6-overlay/s6-rc.d/trt-model-prepare/up   |  1 +
 .../tensorrt_detector/tensorrt_libyolo.sh     | 18 +++++++
 docker/tensorrt_models.sh                     | 34 ------------
 docs/docs/configuration/object_detectors.md   | 34 ++++++------
 docs/docs/frigate/hardware.md                 |  2 +-
 frigate/detectors/plugins/tensorrt.py         |  2 +-
 requirements-tensorrt.txt                     | 17 +++---
 14 files changed, 125 insertions(+), 62 deletions(-)
 rename docker/{ => support/tensorrt_detector}/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf (94%)
 create mode 100644 docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/frigate/dependencies.d/trt-model-prepare
 create mode 100644 docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/dependencies.d/base
 create mode 100755 docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/run
 create mode 100644 docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/type
 create mode 100644 docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/up
 create mode 100755 docker/support/tensorrt_detector/tensorrt_libyolo.sh
 delete mode 100755 docker/tensorrt_models.sh

diff --git a/Dockerfile b/Dockerfile
index d1dbd0755..660cb5b25 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -262,15 +262,35 @@ FROM deps AS frigate
 WORKDIR /opt/frigate/
 COPY --from=rootfs / /
 
+# Build TensorRT-specific library
+FROM nvcr.io/nvidia/tensorrt:23.03-py3 AS trt-deps
+
+RUN --mount=type=bind,source=docker/support/tensorrt_detector/tensorrt_libyolo.sh,target=/tensorrt_libyolo.sh \
+    /tensorrt_libyolo.sh
+
 # Frigate w/ TensorRT Support as separate image
 FROM frigate AS frigate-tensorrt
+
+#Disable S6 Global timeout
+ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
+
+ENV TRT_VER=8.5.3
+ENV YOLO_MODELS="yolov7-tiny-416"
+
+COPY --from=trt-deps /usr/local/lib/libyolo_layer.so /usr/local/lib/libyolo_layer.so
+COPY --from=trt-deps /usr/local/src/tensorrt_demos /usr/local/src/tensorrt_demos
+COPY docker/support/tensorrt_detector/rootfs/ /
+
 RUN --mount=type=bind,from=trt-wheels,source=/trt-wheels,target=/deps/trt-wheels \
     pip3 install -U /deps/trt-wheels/*.whl && \
-    ln -s libnvrtc.so.11.2 /usr/local/lib/python3.9/dist-packages/nvidia/cuda_nvrtc/lib/libnvrtc.so && \
     ldconfig
 
 # Dev Container w/ TRT
 FROM devcontainer AS devcontainer-trt
 
+COPY --from=trt-deps /usr/local/lib/libyolo_layer.so /usr/local/lib/libyolo_layer.so
+COPY --from=trt-deps /usr/local/src/tensorrt_demos /usr/local/src/tensorrt_demos
+COPY docker/support/tensorrt_detector/rootfs/ /
+COPY --from=trt-deps /usr/local/lib/libyolo_layer.so /usr/local/lib/libyolo_layer.so
 RUN --mount=type=bind,from=trt-wheels,source=/trt-wheels,target=/deps/trt-wheels \
     pip3 install -U /deps/trt-wheels/*.whl
diff --git a/docker/install_deps.sh b/docker/install_deps.sh
index 25b6951b5..7d5242d83 100755
--- a/docker/install_deps.sh
+++ b/docker/install_deps.sh
@@ -68,7 +68,7 @@ if [[ "${TARGETARCH}" == "arm64" ]]; then
         libva-drm2 mesa-va-drivers
 fi
 
-apt-get purge gnupg apt-transport-https wget xz-utils -y
+apt-get purge gnupg apt-transport-https xz-utils -y
 apt-get clean autoclean -y
 apt-get autoremove --purge -y
 rm -rf /var/lib/apt/lists/*
diff --git a/docker/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf b/docker/support/tensorrt_detector/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf
similarity index 94%
rename from docker/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf
rename to docker/support/tensorrt_detector/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf
index d4248d047..fe16ed9c5 100644
--- a/docker/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf
+++ b/docker/support/tensorrt_detector/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf
@@ -1,3 +1,4 @@
+/usr/local/lib
 /usr/local/lib/python3.9/dist-packages/nvidia/cudnn/lib
 /usr/local/lib/python3.9/dist-packages/nvidia/cuda_runtime/lib
 /usr/local/lib/python3.9/dist-packages/nvidia/cublas/lib
diff --git a/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/frigate/dependencies.d/trt-model-prepare b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/frigate/dependencies.d/trt-model-prepare
new file mode 100644
index 000000000..e69de29bb
diff --git a/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/dependencies.d/base b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/dependencies.d/base
new file mode 100644
index 000000000..e69de29bb
diff --git a/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/run b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/run
new file mode 100755
index 000000000..5f0e43553
--- /dev/null
+++ b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/run
@@ -0,0 +1,53 @@
+#!/command/with-contenv bash
+# shellcheck shell=bash
+# Generate models for the TensorRT detector
+
+set -o errexit -o nounset -o pipefail
+
+MODEL_CACHE_DIR=${MODEL_CACHE_DIR:-"/config/model_cache/tensorrt"}
+OUTPUT_FOLDER="${MODEL_CACHE_DIR}/${TRT_VER}"
+
+# Create output folder
+mkdir -p ${OUTPUT_FOLDER}
+
+FIRST_MODEL=true
+MODEL_CONVERT=""
+
+for model in ${YOLO_MODELS//,/ }
+do
+    # Remove old link in case path/version changed
+    rm -f ${MODEL_CACHE_DIR}/${model}.trt
+    
+    if [[ ! -f ${OUTPUT_FOLDER}/${model}.trt ]]; then
+        if [[ ${FIRST_MODEL} = true ]]; then
+            MODEL_CONVERT="${model}"
+            FIRST_MODEL=false;
+        else
+            MODEL_CONVERT+=",${model}";
+        fi
+    else
+        ln -s ${OUTPUT_FOLDER}/${model}.trt ${MODEL_CACHE_DIR}/${model}.trt
+    fi
+done
+
+if [[ -z ${MODEL_CONVERT} ]]; then
+    echo "No models to convert."
+    exit 0
+fi
+
+echo "Generating the following TRT Models: ${MODEL_CONVERT}"
+
+# Build trt engine
+cd /usr/local/src/tensorrt_demos/yolo
+
+# Download yolo weights
+./download_yolo.sh $MODEL_CONVERT > /dev/null
+
+for model in ${MODEL_CONVERT//,/ }
+do
+    echo "Converting ${model} model"
+    python3 yolo_to_onnx.py -m ${model} > /dev/null
+    python3 onnx_to_tensorrt.py -m ${model} > /dev/null
+    cp ${model}.trt ${OUTPUT_FOLDER}/${model}.trt
+    ln -s ${OUTPUT_FOLDER}/${model}.trt ${MODEL_CACHE_DIR}/${model}.trt
+done
diff --git a/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/type b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/type
new file mode 100644
index 000000000..bdd22a185
--- /dev/null
+++ b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/type
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/up b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/up
new file mode 100644
index 000000000..b9de40ad0
--- /dev/null
+++ b/docker/support/tensorrt_detector/rootfs/etc/s6-overlay/s6-rc.d/trt-model-prepare/up
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/trt-model-prepare/run
diff --git a/docker/support/tensorrt_detector/tensorrt_libyolo.sh b/docker/support/tensorrt_detector/tensorrt_libyolo.sh
new file mode 100755
index 000000000..e6fc415e5
--- /dev/null
+++ b/docker/support/tensorrt_detector/tensorrt_libyolo.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+SCRIPT_DIR="/usr/local/src/tensorrt_demos"
+
+# Clone tensorrt_demos repo
+git clone --depth 1 https://github.com/NateMeyer/tensorrt_demos.git -b conditional_download
+
+# Build libyolo
+cd ./tensorrt_demos/plugins && make all
+cp libyolo_layer.so /usr/local/lib/libyolo_layer.so
+
+# Store yolo scripts for later conversion
+cd ../
+mkdir -p ${SCRIPT_DIR}/plugins
+cp plugins/libyolo_layer.so ${SCRIPT_DIR}/plugins/libyolo_layer.so
+cp -a yolo ${SCRIPT_DIR}/
diff --git a/docker/tensorrt_models.sh b/docker/tensorrt_models.sh
deleted file mode 100755
index 957e817d6..000000000
--- a/docker/tensorrt_models.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-
-set -euxo pipefail
-
-CUDA_HOME=/usr/local/cuda
-LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64
-OUTPUT_FOLDER=/tensorrt_models
-echo "Generating the following TRT Models: ${YOLO_MODELS:="yolov4-tiny-288,yolov4-tiny-416,yolov7-tiny-416"}"
-
-# Create output folder
-mkdir -p ${OUTPUT_FOLDER}
-
-# Install packages
-pip install --upgrade pip && pip install onnx==1.9.0 protobuf==3.20.3
-
-# Clone tensorrt_demos repo
-git clone --depth 1 https://github.com/yeahme49/tensorrt_demos.git /tensorrt_demos
-
-# Build libyolo
-cd /tensorrt_demos/plugins && make all
-cp libyolo_layer.so ${OUTPUT_FOLDER}/libyolo_layer.so
-
-# Download yolo weights
-cd /tensorrt_demos/yolo && ./download_yolo.sh
-
-# Build trt engine
-cd /tensorrt_demos/yolo
-
-for model in ${YOLO_MODELS//,/ }
-do
-    python3 yolo_to_onnx.py -m ${model}
-    python3 onnx_to_tensorrt.py -m ${model}
-    cp /tensorrt_demos/yolo/${model}.trt ${OUTPUT_FOLDER}/${model}.trt;
-done
diff --git a/docs/docs/configuration/object_detectors.md b/docs/docs/configuration/object_detectors.md
index 3f48423bc..d684a2917 100644
--- a/docs/docs/configuration/object_detectors.md
+++ b/docs/docs/configuration/object_detectors.md
@@ -174,9 +174,7 @@ NVidia GPUs may be used for object detection using the TensorRT libraries. Due t
 
 ### Minimum Hardware Support
 
-The TensorRT detector uses the 11.x series of CUDA libraries which have minor version compatibility. The minimum driver version on the host system must be `>=450.80.02`. Also the GPU must support a Compute Capability of `5.0` or greater. This generally correlates to a Maxwell-era GPU or newer, check the NVIDIA GPU Compute Capability table linked below.
-
-> **TODO:** NVidia claims support on compute 3.5 and 3.7, but marks it as deprecated. This would have some, but not all, Kepler GPUs as possibly working. This needs testing before making any claims of support.
+The TensorRT detector uses the 12.x series of CUDA libraries which have minor version compatibility. The minimum driver version on the host system must be `>=525.60.13`. Also the GPU must support a Compute Capability of `5.0` or greater. This generally correlates to a Maxwell-era GPU or newer, check the NVIDIA GPU Compute Capability table linked below.
 
 To use the TensorRT detector, make sure your host system has the [nvidia-container-runtime](https://docs.docker.com/config/containers/resource_constraints/#access-an-nvidia-gpu) installed to pass through the GPU to the container and the host system has a compatible driver installed for your GPU.
 
@@ -192,22 +190,15 @@ There are improved capabilities in newer GPU architectures that TensorRT can ben
 
 ### Generate Models
 
-The model used for TensorRT must be preprocessed on the same hardware platform that they will run on. This means that each user must run additional setup to generate a model file for the TensorRT library. A script is provided that will build several common models.
+The model used for TensorRT must be preprocessed on the same hardware platform that they will run on. This means that each user must run additional setup to generate a model file for the TensorRT library. A script is included that will build several common models.
 
-To generate model files, create a new folder to save the models, download the script, and launch a docker container that will run the script.
+The Frigate image will generate model files during startup if the specified model is not found.  Processed models are stored in the `/config/model_cache` folder.  Typically the `/config` path is mapped to a directory on the host already and the `model_cache` does not need to be mapped separately unless the user wants to store it in a different location on the host.
 
-```bash
-mkdir trt-models
-wget https://github.com/blakeblackshear/frigate/raw/master/docker/tensorrt_models.sh
-chmod +x tensorrt_models.sh
-docker run --gpus=all --rm -it -v `pwd`/trt-models:/tensorrt_models -v `pwd`/tensorrt_models.sh:/tensorrt_models.sh nvcr.io/nvidia/tensorrt:22.07-py3 /tensorrt_models.sh
-```
+To by default, the `yolov7-tiny-416` model will be generated, but this can be overridden by specifying the `YOLO_MODELS` environment variable in Docker.  One or more models may be listed in a comma-separated format, and each one will be generated.  To select no model generation, set the variable to an empty string, `YOLO_MODELS=""`.  Models will only be generated if the corresponding `{model}.trt` file is not present in the `model_cache` folder, so you can force a model to be regenerated by deleting it from your Frigate data folder.
 
-The `trt-models` folder can then be mapped into your Frigate container as `trt-models` and the models referenced from the config.
+If your GPU does not support FP16 operations, you can pass the environment variable `USE_FP16=False` to disable it.
 
-If your GPU does not support FP16 operations, you can pass the environment variable `-e USE_FP16=False` to the `docker run` command to disable it.
-
-Specific models can be selected by passing an environment variable to the `docker run` command. Use the form `-e YOLO_MODELS=yolov4-416,yolov4-tiny-416` to select one or more model names. The models available are shown below.
+Specific models can be selected by passing an environment variable to the `docker run` command or in your `docker-compose.yml` file. Use the form `-e YOLO_MODELS=yolov4-416,yolov4-tiny-416` to select one or more model names. The models available are shown below.
 
 ```
 yolov3-288
@@ -237,11 +228,20 @@ yolov7x-640
 yolov7x-320
 ```
 
+An example `docker-compose.yml` fragment that converts the `yolov4-608` and `yolov7x-640` models for a Pascal card would look something like this:
+
+```yml
+frigate:
+  environment:
+    - YOLO_MODELS="yolov4-608,yolov7x-640"
+    - USE_FP16=false
+```
+
 ### Configuration Parameters
 
 The TensorRT detector can be selected by specifying `tensorrt` as the model type. The GPU will need to be passed through to the docker container using the same methods described in the [Hardware Acceleration](hardware_acceleration.md#nvidia-gpu) section. If you pass through multiple GPUs, you can select which GPU is used for a detector with the `device` configuration parameter. The `device` parameter is an integer value of the GPU index, as shown by `nvidia-smi` within the container.
 
-The TensorRT detector uses `.trt` model files that are located in `/trt-models/` by default. These model file path and dimensions used will depend on which model you have generated.
+The TensorRT detector uses `.trt` model files that are located in `/config/model_cache/tensorrt` by default. These model path and dimensions used will depend on which model you have generated.
 
 ```yaml
 detectors:
@@ -250,7 +250,7 @@ detectors:
     device: 0 #This is the default, select the first GPU
 
 model:
-  path: /trt-models/yolov7-tiny-416.trt
+  path: /config/model_cache/tensorrt/yolov7-tiny-416.trt
   input_tensor: nchw
   input_pixel_format: rgb
   width: 416
diff --git a/docs/docs/frigate/hardware.md b/docs/docs/frigate/hardware.md
index 36233ea68..5daf8fe3b 100644
--- a/docs/docs/frigate/hardware.md
+++ b/docs/docs/frigate/hardware.md
@@ -72,7 +72,7 @@ Inference speeds vary greatly depending on the CPU, GPU, or VPU used, some known
 
 ### TensorRT
 
-The TensortRT detector is able to run on x86 hosts that have an Nvidia GPU which supports the 11.x series of CUDA libraries. The minimum driver version on the host system must be `>=450.80.02`. Also the GPU must support a Compute Capability of `5.0` or greater. This generally correlates to a Maxwell-era GPU or newer, check the [TensorRT docs for more info](/configuration/object_detectors#nvidia-tensorrt-detector).
+The TensortRT detector is able to run on x86 hosts that have an Nvidia GPU which supports the 12.x series of CUDA libraries. The minimum driver version on the host system must be `>=525.60.13`. Also the GPU must support a Compute Capability of `5.0` or greater. This generally correlates to a Maxwell-era GPU or newer, check the [TensorRT docs for more info](/configuration/object_detectors#nvidia-tensorrt-detector).
 
 Inference speeds will vary greatly depending on the GPU and the model used.
 `tiny` variants are faster than the equivalent non-tiny model, some known examples are below:
diff --git a/frigate/detectors/plugins/tensorrt.py b/frigate/detectors/plugins/tensorrt.py
index 7251b8751..dea3fe078 100644
--- a/frigate/detectors/plugins/tensorrt.py
+++ b/frigate/detectors/plugins/tensorrt.py
@@ -78,7 +78,7 @@ class TensorRtDetector(DetectionApi):
         try:
             trt.init_libnvinfer_plugins(self.trt_logger, "")
 
-            ctypes.cdll.LoadLibrary("/trt-models/libyolo_layer.so")
+            ctypes.cdll.LoadLibrary("/usr/local/lib/libyolo_layer.so")
         except OSError as e:
             logger.error(
                 "ERROR: failed to load libraries. %s",
diff --git a/requirements-tensorrt.txt b/requirements-tensorrt.txt
index 90517babd..214202e43 100644
--- a/requirements-tensorrt.txt
+++ b/requirements-tensorrt.txt
@@ -1,9 +1,12 @@
 # NVidia TensorRT Support (amd64 only)
-nvidia-pyindex; platform_machine == 'x86_64'
-nvidia-tensorrt == 8.4.1.5; platform_machine == 'x86_64'
-cuda-python == 11.7; platform_machine == 'x86_64'
+--extra-index-url 'https://pypi.nvidia.com'
+numpy < 1.24; platform_machine == 'x86_64'
+tensorrt == 8.5.3.*; platform_machine == 'x86_64'
+cuda-python == 11.8; platform_machine == 'x86_64'
 cython == 0.29.*; platform_machine == 'x86_64'
-nvidia-cuda-runtime-cu11 == 11.7.*; platform_machine == 'x86_64'
-nvidia-cublas-cu11 == 11.11.*; platform_machine == 'x86_64'
-nvidia-cudnn-cu11 == 8.7.*; platform_machine == 'x86_64'
-nvidia-cuda-nvrtc-cu11 == 11.7.*; platform_machine == 'x86_64'
\ No newline at end of file
+nvidia-cuda-runtime-cu12 == 12.1.*; platform_machine == 'x86_64'
+nvidia-cuda-runtime-cu11 == 11.8.*; platform_machine == 'x86_64'
+nvidia-cublas-cu11 == 11.11.3.6; platform_machine == 'x86_64'
+nvidia-cudnn-cu11 == 8.6.0.*; platform_machine == 'x86_64'
+onnx==1.14.0; platform_machine == 'x86_64'
+protobuf==3.20.3; platform_machine == 'x86_64'
\ No newline at end of file

From 69630e73851c57428b64242a5ef0d592efa3c167 Mon Sep 17 00:00:00 2001
From: Nicolas Mowen 
Date: Fri, 7 Jul 2023 06:06:42 -0600
Subject: [PATCH 06/20] Fix incorrect respnses (#7066)

---
 frigate/http.py | 66 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 41 insertions(+), 25 deletions(-)

diff --git a/frigate/http.py b/frigate/http.py
index ba62af047..fe5da869a 100644
--- a/frigate/http.py
+++ b/frigate/http.py
@@ -420,8 +420,8 @@ def get_labels():
         else:
             events = Event.select(Event.label).distinct()
     except Exception as e:
-        return jsonify(
-            {"success": False, "message": f"Failed to get labels: {e}"}, "404"
+        return make_response(
+            jsonify({"success": False, "message": f"Failed to get labels: {e}"}), 404
         )
 
     labels = sorted([e.label for e in events])
@@ -435,8 +435,9 @@ def get_sub_labels():
     try:
         events = Event.select(Event.sub_label).distinct()
     except Exception as e:
-        return jsonify(
-            {"success": False, "message": f"Failed to get sub_labels: {e}"}, "404"
+        return make_response(
+            jsonify({"success": False, "message": f"Failed to get sub_labels: {e}"}),
+            404,
         )
 
     sub_labels = [e.sub_label for e in events]
@@ -869,12 +870,17 @@ def events():
 @bp.route("/events//