mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-11-01 03:10:33 +03:00
Compare commits
4
Commits
d92a98a36d
...
b2fbf9e14b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2fbf9e14b | ||
|
|
1c745e0847 | ||
|
|
729ee86043 | ||
|
|
243172fbf4 |
@@ -106,6 +106,41 @@ jobs:
|
||||
tensorrt.tags=${{ steps.setup.outputs.image-name }}-tensorrt-jp6
|
||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp6
|
||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp6,mode=max
|
||||
jetson_jp7_build:
|
||||
runs-on: ubuntu-22.04-arm
|
||||
name: Jetson Jetpack 7
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up QEMU and Buildx
|
||||
id: setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push TensorRT (Jetson, Jetpack 7)
|
||||
env:
|
||||
ARCH: arm64
|
||||
BASE_IMAGE: nvcr.io/nvidia/tensorrt:26.02-py3-igpu
|
||||
SLIM_BASE: nvcr.io/nvidia/tensorrt:26.02-py3-igpu
|
||||
TRT_BASE: nvcr.io/nvidia/tensorrt:26.02-py3-igpu
|
||||
BUILD_ONNXRUNTIME_FROM_SOURCE: "1"
|
||||
ONNXRUNTIME_VERSION: "1.25.1"
|
||||
ONNXRUNTIME_BRANCH: rel-1.25.1
|
||||
TENSORRT_PYTHON_BRANCH: auto
|
||||
L4T_APT_RELEASE: r39.2
|
||||
JETSON_SOC_REPO: som
|
||||
uses: docker/bake-action@v7
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: tensorrt
|
||||
files: docker/tensorrt/trt.hcl
|
||||
set: |
|
||||
tensorrt.tags=${{ steps.setup.outputs.image-name }}-tensorrt-jp7
|
||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp7
|
||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp7,mode=max
|
||||
amd64_extra_builds:
|
||||
runs-on: ubuntu-22.04
|
||||
name: AMD64 Extra Build
|
||||
|
||||
@@ -39,14 +39,16 @@ jobs:
|
||||
STABLE_TAG=${BASE}:stable
|
||||
PULL_TAG=${BASE}:${BUILD_TAG}
|
||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG} docker://${VERSION_TAG}
|
||||
for variant in standard-arm64 tensorrt tensorrt-jp6 rk rocm synaptics; do
|
||||
for variant in standard-arm64 tensorrt tensorrt-jp6 tensorrt-jp7 rk rocm synaptics; do
|
||||
docker manifest inspect "${PULL_TAG}-${variant}" >/dev/null
|
||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG}-${variant} docker://${VERSION_TAG}-${variant}
|
||||
done
|
||||
|
||||
# stable tag
|
||||
if [[ "${BUILD_TYPE}" == "stable" ]]; then
|
||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG} docker://${STABLE_TAG}
|
||||
for variant in standard-arm64 tensorrt tensorrt-jp6 rk rocm synaptics; do
|
||||
for variant in standard-arm64 tensorrt tensorrt-jp6 tensorrt-jp7 rk rocm synaptics; do
|
||||
docker manifest inspect "${PULL_TAG}-${variant}" >/dev/null
|
||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG}-${variant} docker://${STABLE_TAG}-${variant}
|
||||
done
|
||||
fi
|
||||
|
||||
+14
-4
@@ -87,7 +87,9 @@ RUN apt-get -qq update \
|
||||
&& apt-get -qq install -y wget python3 python3-dev python3-distutils gcc pkg-config libhdf5-dev \
|
||||
&& wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \
|
||||
&& sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \
|
||||
&& python3 get-pip.py "pip" \
|
||||
# --ignore-installed: the Ubuntu 24.04 (noble) JP7 base ships a distro pip with no RECORD
|
||||
# file, so get-pip.py's default reinstall dies with "uninstall-no-record-file". Harmless on JP6.
|
||||
&& python3 get-pip.py --ignore-installed "pip" \
|
||||
&& pip3 install -r /requirements-ov.txt
|
||||
|
||||
# Get OpenVino Model
|
||||
@@ -181,9 +183,11 @@ RUN apt-get -qq update \
|
||||
|
||||
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
|
||||
|
||||
# --ignore-installed: the Ubuntu 24.04 (noble) JP7 base ships a distro pip with no RECORD file,
|
||||
# so get-pip.py's default reinstall dies with "uninstall-no-record-file". Harmless on JP6.
|
||||
RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \
|
||||
&& sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \
|
||||
&& python3 get-pip.py "pip"
|
||||
&& python3 get-pip.py --ignore-installed "pip"
|
||||
|
||||
COPY docker/main/requirements.txt /requirements.txt
|
||||
COPY docker/main/requirements-dev.txt /requirements-dev.txt
|
||||
@@ -268,12 +272,18 @@ RUN --mount=type=bind,source=docker/main/install_deps.sh,target=/deps/install_de
|
||||
ENV DEFAULT_FFMPEG_VERSION="8.0"
|
||||
ENV INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:7.0:5.0"
|
||||
|
||||
# --ignore-installed: the Ubuntu 24.04 (noble) JP7 base ships a distro pip with no RECORD file,
|
||||
# so get-pip.py's default reinstall dies with "uninstall-no-record-file". Harmless on JP6.
|
||||
RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \
|
||||
&& sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \
|
||||
&& python3 get-pip.py "pip"
|
||||
&& python3 get-pip.py --ignore-installed "pip"
|
||||
|
||||
# --ignore-installed: on the Ubuntu 24.04 (noble) JP7 base, `pip3 install -U` tries to upgrade
|
||||
# distro-managed packages (e.g. wheel 0.42.0) that ship without a RECORD file, which fails with
|
||||
# "uninstall-no-record-file". Installing over them into /usr/local without uninstalling avoids
|
||||
# that; harmless on JP6 where these come from pip and have RECORD files.
|
||||
RUN --mount=type=bind,from=wheels,source=/wheels,target=/deps/wheels \
|
||||
pip3 install -U /deps/wheels/*.whl
|
||||
pip3 install -U --ignore-installed /deps/wheels/*.whl
|
||||
|
||||
# Install Axera Engine
|
||||
RUN pip3 install https://github.com/AXERA-TECH/pyaxengine/releases/download/0.1.3-frigate/axengine-0.1.3-py3-none-any.whl
|
||||
|
||||
@@ -10,9 +10,15 @@ NGX_DEVEL_KIT_VERSION="v0.3.3"
|
||||
|
||||
source /etc/os-release
|
||||
|
||||
if [[ "$VERSION_ID" == "12" ]]; then
|
||||
# Enable deb-src so `apt-get build-dep nginx` can find the source package. Detect the apt
|
||||
# source FORMAT rather than guessing by distro: Debian 12 + Ubuntu 24.04 (noble — the JP7
|
||||
# TensorRT igpu base) use the deb822 *.sources format with a `Types:` line; older Ubuntu
|
||||
# (e.g. the JP6 22.04 jammy base) uses the legacy one-line /etc/apt/sources.list.
|
||||
if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
|
||||
sed -i '/^Types:/s/deb/& deb-src/' /etc/apt/sources.list.d/debian.sources
|
||||
else
|
||||
elif [[ -f /etc/apt/sources.list.d/ubuntu.sources ]]; then
|
||||
sed -i '/^Types:/s/deb/& deb-src/' /etc/apt/sources.list.d/ubuntu.sources
|
||||
elif [[ -f /etc/apt/sources.list ]]; then
|
||||
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
|
||||
fi
|
||||
|
||||
@@ -6,9 +6,16 @@ SQLITE_VEC_VERSION="0.1.3"
|
||||
|
||||
source /etc/os-release
|
||||
|
||||
if [[ "$VERSION_ID" == "12" ]]; then
|
||||
# Enable deb-src so `apt-get build-dep sqlite3` can find the source package. Detect the apt
|
||||
# source FORMAT rather than guessing by distro: Debian 12 + Ubuntu 24.04 (noble — the JP7
|
||||
# TensorRT igpu base) use the deb822 *.sources format with a `Types:` line; older Ubuntu
|
||||
# (e.g. the JP6 22.04 jammy base) uses the legacy one-line /etc/apt/sources.list. On noble the
|
||||
# legacy /etc/apt/sources.list is effectively empty, so the old copy+sed path enabled nothing.
|
||||
if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
|
||||
sed -i '/^Types:/s/deb/& deb-src/' /etc/apt/sources.list.d/debian.sources
|
||||
else
|
||||
elif [[ -f /etc/apt/sources.list.d/ubuntu.sources ]]; then
|
||||
sed -i '/^Types:/s/deb/& deb-src/' /etc/apt/sources.list.d/ubuntu.sources
|
||||
elif [[ -f /etc/apt/sources.list ]]; then
|
||||
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
|
||||
fi
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG BASE_IMAGE
|
||||
ARG TRT_BASE=nvcr.io/nvidia/tensorrt:23.12-py3
|
||||
ARG BUILD_ONNXRUNTIME_FROM_SOURCE=0
|
||||
ARG ONNXRUNTIME_VERSION=1.25.1
|
||||
ARG ONNXRUNTIME_BRANCH=rel-1.25.1
|
||||
ARG TENSORRT_PYTHON_BRANCH=release/8.6
|
||||
ARG L4T_APT_RELEASE=
|
||||
ARG JETSON_SOC_REPO=
|
||||
|
||||
# Build TensorRT-specific library
|
||||
FROM ${TRT_BASE} AS trt-deps
|
||||
@@ -12,7 +18,9 @@ ARG TARGETARCH
|
||||
ARG COMPUTE_LEVEL
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git build-essential cuda-nvcc-* cuda-nvtx-* libnvinfer-dev libnvinfer-plugin-dev libnvparsers-dev libnvonnxparsers-dev \
|
||||
&& TRT_DEV_PACKAGES="git build-essential cuda-nvcc-* cuda-nvtx-* libnvinfer-dev libnvinfer-plugin-dev libnvonnxparsers-dev" \
|
||||
&& if apt-cache show libnvparsers-dev > /dev/null 2>&1; then TRT_DEV_PACKAGES="${TRT_DEV_PACKAGES} libnvparsers-dev"; fi \
|
||||
&& apt-get install -y ${TRT_DEV_PACKAGES} \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN --mount=type=bind,source=docker/tensorrt/detector/tensorrt_libyolo.sh,target=/tensorrt_libyolo.sh \
|
||||
/tensorrt_libyolo.sh
|
||||
@@ -58,6 +66,12 @@ HEALTHCHECK --start-period=600s --start-interval=5s --interval=15s --timeout=5s
|
||||
|
||||
FROM ${BASE_IMAGE} AS build-wheels
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG BUILD_ONNXRUNTIME_FROM_SOURCE
|
||||
ARG ONNXRUNTIME_VERSION
|
||||
ARG ONNXRUNTIME_BRANCH
|
||||
ARG TENSORRT_PYTHON_BRANCH
|
||||
ARG L4T_APT_RELEASE
|
||||
ARG JETSON_SOC_REPO
|
||||
|
||||
# Add deadsnakes PPA for python3.11
|
||||
RUN apt-get -qq update && \
|
||||
@@ -69,41 +83,66 @@ RUN apt-get -qq update && \
|
||||
RUN apt-get -qq update \
|
||||
&& apt-get -qq install -y --no-install-recommends \
|
||||
python3.11 python3.11-dev \
|
||||
wget build-essential cmake git \
|
||||
wget curl build-essential cmake git ninja-build \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Ensure python3 defaults to python3.11
|
||||
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
|
||||
|
||||
# --ignore-installed: on the JP7 Ubuntu 24.04 (noble) base the deadsnakes python3.11 ships a
|
||||
# distro-packaged pip 24.0 with no RECORD file, so get-pip.py's default reinstall fails with
|
||||
# "uninstall-no-record-file". Installing over it without uninstalling avoids that; harmless on JP6.
|
||||
RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \
|
||||
&& sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \
|
||||
&& python3 get-pip.py "pip"
|
||||
&& python3 get-pip.py --ignore-installed "pip"
|
||||
|
||||
FROM build-wheels AS trt-wheels
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG TARGETARCH
|
||||
ARG BUILD_ONNXRUNTIME_FROM_SOURCE
|
||||
ARG ONNXRUNTIME_VERSION
|
||||
ARG ONNXRUNTIME_BRANCH
|
||||
ARG TENSORRT_PYTHON_BRANCH
|
||||
|
||||
# python-tensorrt build deps are 3.4 GB!
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ccache cuda-cudart-dev-* cuda-nvcc-* libnvonnxparsers-dev libnvparsers-dev libnvinfer-plugin-dev \
|
||||
&& TRT_DEV_PACKAGES="ccache cuda-cudart-dev-* cuda-nvcc-* libnvonnxparsers-dev libnvinfer-plugin-dev" \
|
||||
&& if apt-cache show libnvparsers-dev > /dev/null 2>&1; then TRT_DEV_PACKAGES="${TRT_DEV_PACKAGES} libnvparsers-dev"; fi \
|
||||
&& apt-get install -y ${TRT_DEV_PACKAGES} \
|
||||
&& ([ -e /usr/local/cuda ] || ln -s /usr/local/cuda-* /usr/local/cuda) \
|
||||
&& rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
# The JP7 base image ships a stale cmake 3.24 at /usr/local/bin/cmake that shadows apt's 3.28.
|
||||
# TensorRT 10.11's python CMakeLists (and the ONNX Runtime source build) require cmake >= 3.27,
|
||||
# so install a current cmake from pip into /usr/local/bin ahead of the stale one. No-op risk on
|
||||
# JP6 where the bundled cmake already satisfies the minimum.
|
||||
RUN pip3 install --no-cache-dir --upgrade "cmake>=3.27"
|
||||
|
||||
# Determine version of tensorrt already installed in base image, e.g. "Version: 8.4.1-1+cuda11.4"
|
||||
RUN NVINFER_VER=$(dpkg -s libnvinfer8 | grep -Po "Version: \K.*") \
|
||||
&& echo $NVINFER_VER | grep -Po "^\d+\.\d+\.\d+" > /etc/TENSORRT_VER
|
||||
RUN NVINFER_VER="$(dpkg-query -W -f='${Version}\n' 'libnvinfer*' 2>/dev/null | grep -E '^[0-9]+[.][0-9]+[.][0-9]+' | sort -V | tail -n1)" \
|
||||
&& test -n "$NVINFER_VER" \
|
||||
&& TENSORRT_VER="$(echo "$NVINFER_VER" | grep -Eo '^[0-9]+[.][0-9]+[.][0-9]+')" \
|
||||
&& TENSORRT_MAJOR="$(echo "$TENSORRT_VER" | cut -d. -f1)" \
|
||||
&& echo "$TENSORRT_VER" > /etc/TENSORRT_VER \
|
||||
&& echo "$TENSORRT_MAJOR" > /etc/TENSORRT_MAJOR
|
||||
|
||||
RUN --mount=type=bind,source=docker/tensorrt/detector/build_python_tensorrt.sh,target=/deps/build_python_tensorrt.sh \
|
||||
--mount=type=cache,target=/root/.ccache \
|
||||
export PATH="/usr/lib/ccache:$PATH" CCACHE_DIR=/root/.ccache CCACHE_MAXSIZE=2G \
|
||||
&& TENSORRT_VER=$(cat /etc/TENSORRT_VER) /deps/build_python_tensorrt.sh
|
||||
&& TENSORRT_VER=$(cat /etc/TENSORRT_VER) TENSORRT_PYTHON_BRANCH="${TENSORRT_PYTHON_BRANCH}" /deps/build_python_tensorrt.sh
|
||||
|
||||
COPY docker/tensorrt/requirements-arm64.txt /requirements-tensorrt.txt
|
||||
|
||||
RUN pip3 wheel --wheel-dir=/trt-wheels -r /requirements-tensorrt.txt
|
||||
|
||||
# See https://elinux.org/Jetson_Zoo#ONNX_Runtime
|
||||
ADD https://nvidia.box.com/shared/static/9yvw05k6u343qfnkhdv2x6xhygze0aq1.whl /trt-wheels/onnxruntime_gpu-1.19.0-cp311-cp311-linux_aarch64.whl
|
||||
RUN --mount=type=bind,source=docker/tensorrt/detector/build_onnxruntime_arm64.sh,target=/deps/build_onnxruntime_arm64.sh \
|
||||
mkdir -p /trt-wheels \
|
||||
&& if [ "${BUILD_ONNXRUNTIME_FROM_SOURCE}" = "1" ]; then \
|
||||
ONNXRUNTIME_VERSION="${ONNXRUNTIME_VERSION}" ONNXRUNTIME_BRANCH="${ONNXRUNTIME_BRANCH}" /deps/build_onnxruntime_arm64.sh; \
|
||||
else \
|
||||
wget -q https://nvidia.box.com/shared/static/9yvw05k6u343qfnkhdv2x6xhygze0aq1.whl -O /trt-wheels/onnxruntime_gpu-1.19.0-cp311-cp311-linux_aarch64.whl; \
|
||||
fi
|
||||
|
||||
FROM build-wheels AS trt-model-wheels
|
||||
ARG DEBIAN_FRONTEND
|
||||
@@ -116,16 +155,20 @@ RUN --mount=type=bind,source=docker/tensorrt/requirements-models-arm64.txt,targe
|
||||
|
||||
FROM wget AS jetson-ffmpeg
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG L4T_APT_RELEASE
|
||||
ARG JETSON_SOC_REPO
|
||||
ENV CCACHE_DIR /root/.ccache
|
||||
ENV CCACHE_MAXSIZE 2G
|
||||
RUN --mount=type=bind,source=docker/tensorrt/build_jetson_ffmpeg.sh,target=/deps/build_jetson_ffmpeg.sh \
|
||||
--mount=type=cache,target=/root/.ccache \
|
||||
/deps/build_jetson_ffmpeg.sh
|
||||
L4T_APT_RELEASE="${L4T_APT_RELEASE}" JETSON_SOC_REPO="${JETSON_SOC_REPO}" /deps/build_jetson_ffmpeg.sh
|
||||
|
||||
# Frigate w/ TensorRT for NVIDIA Jetson platforms
|
||||
FROM tensorrt-base AS frigate-tensorrt
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y python-is-python3 libprotobuf23 \
|
||||
&& pick_package() { for package in "$@"; do if apt-cache show "$package" > /dev/null 2>&1; then echo "$package"; return 0; fi; done; return 1; } \
|
||||
&& PROTOBUF_RUNTIME="$(pick_package libprotobuf23 libprotobuf32t64 libprotobuf32)" \
|
||||
&& apt-get install -y python-is-python3 "${PROTOBUF_RUNTIME}" \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=jetson-ffmpeg /rootfs /
|
||||
@@ -134,23 +177,30 @@ ENV INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:${INCLUDED_FFMPEG_VERSIO
|
||||
|
||||
# ffmpeg runtime dependencies
|
||||
RUN apt-get -qq update \
|
||||
&& pick_package() { for package in "$@"; do if apt-cache show "$package" > /dev/null 2>&1; then echo "$package"; return 0; fi; done; return 1; } \
|
||||
&& X264_RUNTIME="$(pick_package libx264-163 libx264-164)" \
|
||||
&& X265_RUNTIME="$(pick_package libx265-199 libx265-209)" \
|
||||
&& apt-get -qq install -y --no-install-recommends \
|
||||
libx264-163 libx265-199 libegl1 \
|
||||
"${X264_RUNTIME}" "${X265_RUNTIME}" libegl1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Fixes "Error loading shared libs"
|
||||
RUN mkdir -p /etc/ld.so.conf.d && echo /usr/lib/ffmpeg/jetson/lib/ > /etc/ld.so.conf.d/ffmpeg.conf
|
||||
|
||||
COPY --from=trt-wheels /etc/TENSORRT_VER /etc/TENSORRT_VER
|
||||
COPY --from=trt-wheels /etc/TENSORRT_MAJOR /etc/TENSORRT_MAJOR
|
||||
RUN --mount=type=bind,from=trt-wheels,source=/trt-wheels,target=/deps/trt-wheels \
|
||||
--mount=type=bind,from=trt-model-wheels,source=/trt-model-wheels,target=/deps/trt-model-wheels \
|
||||
pip3 uninstall -y onnxruntime \
|
||||
&& pip3 install -U /deps/trt-wheels/*.whl \
|
||||
&& pip3 install -U /deps/trt-model-wheels/*.whl \
|
||||
# --ignore-installed: on the noble JP7 base, `-U` would try to upgrade distro-managed deps that
|
||||
# lack a RECORD file (e.g. wheel/setuptools), failing with "uninstall-no-record-file". Install
|
||||
# the TRT/ORT GPU wheels over them into /usr/local instead. Harmless on JP6.
|
||||
&& pip3 install -U --ignore-installed /deps/trt-wheels/*.whl \
|
||||
&& pip3 install -U --ignore-installed /deps/trt-model-wheels/*.whl \
|
||||
&& ldconfig
|
||||
|
||||
WORKDIR /opt/frigate/
|
||||
COPY --from=rootfs / /
|
||||
|
||||
# Fixes "Error importing detector runtime: /usr/lib/aarch64-linux-gnu/libstdc++.so.6: cannot allocate memory in static TLS block"
|
||||
ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libstdc++.so.6
|
||||
ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libstdc++.so.6
|
||||
|
||||
@@ -8,18 +8,81 @@ set -euxo pipefail
|
||||
INSTALL_PREFIX=/rootfs/usr/lib/ffmpeg/jetson
|
||||
|
||||
apt-get -qq update
|
||||
apt-get -qq install -y --no-install-recommends build-essential ccache clang cmake pkg-config
|
||||
apt-get -qq install -y --no-install-recommends build-essential ccache clang cmake pkg-config unzip
|
||||
apt-get -qq install -y --no-install-recommends libx264-dev libx265-dev
|
||||
|
||||
pushd /tmp
|
||||
|
||||
CUDA_MAJOR=""
|
||||
if [ -f /usr/local/cuda/version.json ]; then
|
||||
CUDA_MAJOR="$(grep -m1 -oE '"version"[[:space:]]*:[[:space:]]*"[^"]+"' /usr/local/cuda/version.json | sed -E 's/.*"([0-9]+)\..*/\1/' || true)"
|
||||
fi
|
||||
if [ -z "${CUDA_MAJOR}" ] && compgen -G "/usr/local/cuda-13*" > /dev/null; then
|
||||
CUDA_MAJOR=13
|
||||
fi
|
||||
if [ -z "${CUDA_MAJOR}" ] && command -v nvcc > /dev/null 2>&1; then
|
||||
CUDA_MAJOR="$(nvcc --version | sed -nE 's/.*release ([0-9]+)\..*/\1/p' | head -n1 || true)"
|
||||
fi
|
||||
|
||||
# Tracks whether this is the JP7/R39 path, so the ffmpeg build below can link the real R39
|
||||
# tegra multimedia libs instead of the stale jetson-ffmpeg stubs (see TEGRA_LIB_DIR usage).
|
||||
JETSON_R39=0
|
||||
TEGRA_LIB_DIR=/usr/lib/aarch64-linux-gnu/nvidia
|
||||
|
||||
# Install libnvmpi to enable nvmpi decoders (h264_nvmpi, hevc_nvmpi)
|
||||
if [ -e /usr/local/cuda-12 ]; then
|
||||
if [[ "${CUDA_MAJOR}" = "13" || -n "${L4T_APT_RELEASE:-}" || -n "${JETSON_SOC_REPO:-}" ]]; then
|
||||
JETSON_R39=1
|
||||
L4T_APT_RELEASE=${L4T_APT_RELEASE:-r39.2}
|
||||
JETSON_SOC_REPO=${JETSON_SOC_REPO:-som}
|
||||
|
||||
apt-key adv --fetch-key https://repo.download.nvidia.com/jetson/jetson-ota-public.asc
|
||||
{
|
||||
echo "deb https://repo.download.nvidia.com/jetson/common ${L4T_APT_RELEASE} main"
|
||||
echo "deb https://repo.download.nvidia.com/jetson/${JETSON_SOC_REPO} ${L4T_APT_RELEASE} main"
|
||||
echo "deb https://repo.download.nvidia.com/jetson/ffmpeg ${L4T_APT_RELEASE} main"
|
||||
} >> /etc/apt/sources.list.d/nvidia-l4t-apt-source.list
|
||||
|
||||
mkdir -p /opt/nvidia/l4t-packages/
|
||||
touch /opt/nvidia/l4t-packages/.nv-l4t-disable-boot-fw-update-in-preinstall
|
||||
|
||||
apt-get update
|
||||
apt-get -qq install -y --no-install-recommends -o Dpkg::Options::="--force-confold" nvidia-l4t-jetson-multimedia-api
|
||||
|
||||
require_jetson_file() {
|
||||
local description="$1"
|
||||
local pattern="$2"
|
||||
shift 2
|
||||
local directory
|
||||
local found
|
||||
|
||||
# Match case-insensitively: R39.2 (JP7) renamed the multimedia headers to all-lowercase
|
||||
# (e.g. nvbufsurftransform.h) while older L4T shipped CamelCase (NvBufSurfTransform.h). The
|
||||
# jetson-ffmpeg build's nvUtils2NvBuf.h includes the lowercase names, so the lowercase
|
||||
# headers (present on R39.2) satisfy the build; only this validation gate was casing-strict.
|
||||
for directory in "$@"; do
|
||||
found="$(find "${directory}" -type f -iname "${pattern}" -print -quit 2> /dev/null || true)"
|
||||
if [ -n "${found}" ]; then
|
||||
echo "${description}: ${found}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Missing ${description}" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
require_jetson_file "Jetson multimedia header NvBufSurface.h" "NvBufSurface.h" /usr/src/jetson_multimedia_api /usr/include
|
||||
require_jetson_file "Jetson multimedia header NvBufSurfTransform.h" "NvBufSurfTransform.h" /usr/src/jetson_multimedia_api /usr/include
|
||||
require_jetson_file "Jetson multimedia library libnvbufsurface" "libnvbufsurface.so*" /usr/lib /usr/local/lib
|
||||
require_jetson_file "Jetson multimedia library libnvbufsurftransform" "libnvbufsurftransform.so*" /usr/lib /usr/local/lib
|
||||
elif [ -e /usr/local/cuda-12 ]; then
|
||||
# assume Jetpack 6.2
|
||||
apt-key adv --fetch-key https://repo.download.nvidia.com/jetson/jetson-ota-public.asc
|
||||
echo "deb https://repo.download.nvidia.com/jetson/common r36.4 main" >> /etc/apt/sources.list.d/nvidia-l4t-apt-source.list
|
||||
echo "deb https://repo.download.nvidia.com/jetson/t234 r36.4 main" >> /etc/apt/sources.list.d/nvidia-l4t-apt-source.list
|
||||
echo "deb https://repo.download.nvidia.com/jetson/ffmpeg r36.4 main" >> /etc/apt/sources.list.d/nvidia-l4t-apt-source.list
|
||||
{
|
||||
echo "deb https://repo.download.nvidia.com/jetson/common r36.4 main"
|
||||
echo "deb https://repo.download.nvidia.com/jetson/t234 r36.4 main"
|
||||
echo "deb https://repo.download.nvidia.com/jetson/ffmpeg r36.4 main"
|
||||
} >> /etc/apt/sources.list.d/nvidia-l4t-apt-source.list
|
||||
|
||||
mkdir -p /opt/nvidia/l4t-packages/
|
||||
touch /opt/nvidia/l4t-packages/.nv-l4t-disable-boot-fw-update-in-preinstall
|
||||
@@ -38,11 +101,39 @@ fi
|
||||
|
||||
wget -q https://github.com/AndBobsYourUncle/jetson-ffmpeg/archive/9c17b09.zip -O jetson-ffmpeg.zip
|
||||
unzip jetson-ffmpeg.zip && rm jetson-ffmpeg.zip && mv jetson-ffmpeg-* jetson-ffmpeg && cd jetson-ffmpeg
|
||||
LD_LIBRARY_PATH=$(pwd)/stubs:$LD_LIBRARY_PATH # tegra multimedia libs aren't available in image, so use stubs for ffmpeg build
|
||||
# On R39/JP7 the real tegra multimedia libs ARE installed (by nvidia-l4t-jetson-multimedia-api),
|
||||
# living in /usr/lib/aarch64-linux-gnu/nvidia. The 2023-era jetson-ffmpeg stubs predate R39 and
|
||||
# lack newer symbols (e.g. NvBufSurfaceGetDeviceInfo), so libnvmpi.so links with an unresolved
|
||||
# reference and ffmpeg's later `-lnvmpi` configure test fails. Put the real lib dir ahead of the
|
||||
# stubs so libnvmpi resolves against R39's libnvbufsurface. Those real libs in turn need the CUDA
|
||||
# driver (libcuda.so.1) and the CUDA runtime libs, which are only present at container RUNTIME via
|
||||
# the nvidia runtime — so for the build-time link we point -rpath-link at the CUDA *stubs* dir
|
||||
# (libcuda.so stub) and the CUDA lib dir. Pre-R39 keeps the stubs-only path.
|
||||
if [ "${JETSON_R39}" = "1" ]; then
|
||||
CUDA_STUBS_DIR="$(dirname "$(find /usr/local/cuda*/targets/*/lib/stubs -name libcuda.so 2>/dev/null | head -n1)")"
|
||||
CUDA_LIB_DIR="$(dirname "$(find /usr/local/cuda*/targets/*/lib -maxdepth 1 -name 'libcudart.so*' 2>/dev/null | head -n1)")"
|
||||
# The CUDA stub ships only unversioned libcuda.so, but R39's libnvbufsurface.so.1.0.0 records a
|
||||
# NEEDED entry for the versioned soname libcuda.so.1. -rpath-link looks up that exact soname, so
|
||||
# without a libcuda.so.1 the transitive cu* driver symbols stay unresolved at link time. Provide
|
||||
# a build-only versioned symlink to the stub (real libcuda.so.1 is injected at container runtime).
|
||||
CUDA_SONAME_DIR=/tmp/cuda-soname-compat
|
||||
mkdir -p "${CUDA_SONAME_DIR}"
|
||||
if [ -n "${CUDA_STUBS_DIR}" ]; then
|
||||
ln -sf "${CUDA_STUBS_DIR}/libcuda.so" "${CUDA_SONAME_DIR}/libcuda.so.1"
|
||||
ln -sf "${CUDA_STUBS_DIR}/libcuda.so" "${CUDA_SONAME_DIR}/libcuda.so"
|
||||
fi
|
||||
export LD_LIBRARY_PATH=${TEGRA_LIB_DIR}:${CUDA_SONAME_DIR}:${CUDA_STUBS_DIR}:${CUDA_LIB_DIR}:$(pwd)/stubs:${LD_LIBRARY_PATH:-}
|
||||
EXTRA_NVMPI_LDFLAGS="-L${TEGRA_LIB_DIR} -Wl,-rpath-link,${TEGRA_LIB_DIR}"
|
||||
[ -n "${CUDA_STUBS_DIR}" ] && EXTRA_NVMPI_LDFLAGS="${EXTRA_NVMPI_LDFLAGS} -L${CUDA_STUBS_DIR} -Wl,-rpath-link,${CUDA_STUBS_DIR} -L${CUDA_SONAME_DIR} -Wl,-rpath-link,${CUDA_SONAME_DIR}"
|
||||
[ -n "${CUDA_LIB_DIR}" ] && EXTRA_NVMPI_LDFLAGS="${EXTRA_NVMPI_LDFLAGS} -Wl,-rpath-link,${CUDA_LIB_DIR}"
|
||||
else
|
||||
export LD_LIBRARY_PATH=$(pwd)/stubs:${LD_LIBRARY_PATH:-} # tegra multimedia libs aren't available in image, so use stubs for ffmpeg build
|
||||
EXTRA_NVMPI_LDFLAGS=""
|
||||
fi
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX
|
||||
make -j$(nproc)
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX ${EXTRA_NVMPI_LDFLAGS:+-DCMAKE_SHARED_LINKER_FLAGS="${EXTRA_NVMPI_LDFLAGS}"}
|
||||
make -j"$(nproc)"
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
@@ -57,14 +148,24 @@ wget -q https://ffmpeg.org/releases/ffmpeg-6.0.tar.xz
|
||||
tar xaf ffmpeg-*.tar.xz && rm ffmpeg-*.tar.xz && cd ffmpeg-*
|
||||
patch -p1 < ../jetson-ffmpeg/ffmpeg_patches/ffmpeg6.0_nvmpi.patch
|
||||
export PKG_CONFIG_PATH=$INSTALL_PREFIX/lib/pkgconfig
|
||||
# On R39/JP7, ffmpeg's `-lnvmpi` configure probe (and final link) must see the real tegra libs in
|
||||
# /usr/lib/aarch64-linux-gnu/nvidia AND the CUDA driver/runtime stubs to resolve everything libnvmpi
|
||||
# transitively pulls in (NvBufSurfaceGetDeviceInfo from libnvbufsurface, and that lib's own
|
||||
# libcuda.so.1 / cu* driver-API deps). Reuse the same link flags assembled for the libnvmpi build
|
||||
# (TEGRA + CUDA stubs + CUDA lib dirs). Pre-R39 leaves configure flags unchanged.
|
||||
FFMPEG_EXTRA_LDFLAGS=""
|
||||
if [ "${JETSON_R39}" = "1" ]; then
|
||||
FFMPEG_EXTRA_LDFLAGS="${EXTRA_NVMPI_LDFLAGS}"
|
||||
fi
|
||||
# enable Jetson codecs but disable dGPU codecs
|
||||
./configure --cc='ccache gcc' --cxx='ccache g++' \
|
||||
--enable-shared --disable-static --prefix=$INSTALL_PREFIX \
|
||||
--enable-gpl --enable-libx264 --enable-libx265 \
|
||||
--enable-nvmpi --enable-ffnvcodec --enable-cuda-llvm \
|
||||
--disable-cuvid --disable-nvenc --disable-nvdec \
|
||||
${FFMPEG_EXTRA_LDFLAGS:+--extra-ldflags="${FFMPEG_EXTRA_LDFLAGS}"} \
|
||||
|| { cat ffbuild/config.log && false; }
|
||||
make -j$(nproc)
|
||||
make -j"$(nproc)"
|
||||
make install
|
||||
cd ../
|
||||
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
ONNXRUNTIME_VERSION=${ONNXRUNTIME_VERSION:-1.25.1}
|
||||
ONNXRUNTIME_BRANCH=${ONNXRUNTIME_BRANCH:-rel-1.25.1}
|
||||
ORT_PARALLEL=${ORT_PARALLEL:-4}
|
||||
ORT_NVCC_THREADS=${ORT_NVCC_THREADS:-1}
|
||||
|
||||
mkdir -p /trt-wheels /workspace
|
||||
|
||||
pip3 install --upgrade pip setuptools wheel packaging numpy
|
||||
|
||||
command -v nvcc
|
||||
test -f /usr/local/cuda/include/cuda.h
|
||||
|
||||
require_header() {
|
||||
local pattern="$1"
|
||||
local header
|
||||
|
||||
for header in /usr/include/${pattern} /usr/include/aarch64-linux-gnu/${pattern}; do
|
||||
if [[ -e "${header}" ]]; then
|
||||
echo "${header}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Missing required header matching ${pattern}" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
require_ldconfig_entry() {
|
||||
local lib_name="$1"
|
||||
|
||||
if ! ldconfig -p | grep -q "${lib_name}\\.so"; then
|
||||
echo "Missing ldconfig entry for ${lib_name}" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_header "cudnn.h"
|
||||
require_header "cudnn_version*.h"
|
||||
require_header "NvInfer.h"
|
||||
require_header "NvOnnxParser.h"
|
||||
|
||||
require_ldconfig_entry "libnvinfer"
|
||||
require_ldconfig_entry "libnvinfer_plugin"
|
||||
require_ldconfig_entry "libnvonnxparser"
|
||||
|
||||
cd /workspace
|
||||
rm -rf onnxruntime
|
||||
git clone --recursive --branch "${ONNXRUNTIME_BRANCH}" https://github.com/microsoft/onnxruntime.git onnxruntime
|
||||
|
||||
cd /workspace/onnxruntime
|
||||
./build.sh \
|
||||
--config Release \
|
||||
--update \
|
||||
--build \
|
||||
--build_wheel \
|
||||
--parallel "${ORT_PARALLEL}" \
|
||||
--nvcc_threads "${ORT_NVCC_THREADS}" \
|
||||
--allow_running_as_root \
|
||||
--compile_no_warning_as_error \
|
||||
--skip_tests \
|
||||
--use_cuda \
|
||||
--cuda_home /usr/local/cuda \
|
||||
--use_tensorrt \
|
||||
--cudnn_home /usr/lib/aarch64-linux-gnu \
|
||||
--tensorrt_home /usr/lib/aarch64-linux-gnu \
|
||||
--cmake_extra_defines onnxruntime_BUILD_UNIT_TESTS=OFF
|
||||
|
||||
cp build/Linux/Release/dist/onnxruntime_gpu-"${ONNXRUNTIME_VERSION}"-*.whl /trt-wheels/
|
||||
@@ -2,6 +2,19 @@
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
TENSORRT_PYTHON_BRANCH=${TENSORRT_PYTHON_BRANCH:-release/8.6}
|
||||
|
||||
if [[ -n "${TENSORRT_VER:-}" ]]; then
|
||||
TENSORRT_MAJOR_MINOR="${TENSORRT_VER%.*}"
|
||||
EXPECTED_TENSORRT_PYTHON_BRANCH="release/${TENSORRT_MAJOR_MINOR}"
|
||||
|
||||
if [[ "${TENSORRT_PYTHON_BRANCH}" == "auto" ]]; then
|
||||
TENSORRT_PYTHON_BRANCH="${EXPECTED_TENSORRT_PYTHON_BRANCH}"
|
||||
elif [[ "${TENSORRT_PYTHON_BRANCH}" != "${EXPECTED_TENSORRT_PYTHON_BRANCH}" ]]; then
|
||||
echo "WARNING: TENSORRT_PYTHON_BRANCH=${TENSORRT_PYTHON_BRANCH} does not match TENSORRT_VER=${TENSORRT_VER}; auto would select ${EXPECTED_TENSORRT_PYTHON_BRANCH}" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p /trt-wheels
|
||||
|
||||
if [[ "${TARGETARCH}" == "arm64" ]]; then
|
||||
@@ -12,13 +25,44 @@ if [[ "${TARGETARCH}" == "arm64" ]]; then
|
||||
# Get python-tensorrt source
|
||||
mkdir -p /workspace
|
||||
cd /workspace
|
||||
git clone -b release/8.6 https://github.com/NVIDIA/TensorRT.git --depth=1
|
||||
git clone -b "${TENSORRT_PYTHON_BRANCH}" https://github.com/NVIDIA/TensorRT.git --depth=1
|
||||
|
||||
# The TensorRT python build runs the legacy `setup.py bdist_wheel`, which on the JP7 base
|
||||
# otherwise resolves the deadsnakes/Debian system setuptools in /usr/lib/python3/dist-packages.
|
||||
# That copy's wheel.bdist_wheel routes through distutils `install` -> `install_lib`, whose
|
||||
# finalize_options reads a Debian-only `install_layout` option that the install command lacks
|
||||
# under this setuptools, crashing with "AttributeError: install_layout". Install a clean,
|
||||
# non-Debian setuptools+wheel into /usr/local (which python3.11 imports ahead of /usr/lib)
|
||||
# so bdist_wheel uses a consistent toolchain. JP6 already had a compatible setuptools.
|
||||
pip3 install --upgrade --ignore-installed 'setuptools>=70.1,<81' 'wheel>=0.43'
|
||||
|
||||
# Collect dependencies
|
||||
EXT_PATH=/workspace/external && mkdir -p $EXT_PATH
|
||||
pip3 install pybind11 && ln -s /usr/local/lib/python3.11/dist-packages/pybind11 $EXT_PATH/pybind11
|
||||
ln -s /usr/include/python3.11 $EXT_PATH/python3.11
|
||||
ln -s /usr/include/aarch64-linux-gnu/NvOnnxParser.h /workspace/TensorRT/parsers/onnx/
|
||||
|
||||
# TensorRT 10's python onnx bindings (pyOnnx.cpp) #include onnx-tensorrt headers such as
|
||||
# errorHelpers.hpp that live ONLY in the parsers/onnx submodule (onnx-tensorrt), not in the
|
||||
# base image's system include or the shallow TensorRT clone. TRT 8.6 bindings did not need
|
||||
# these, so a lone NvOnnxParser.h symlink sufficed there. Populate the submodule at its pinned
|
||||
# ref so CMake's ONNX_INC_DIR=${TENSORRT_ROOT}/parsers/onnx resolves every header it needs.
|
||||
cd /workspace/TensorRT
|
||||
if git submodule update --init --depth=1 parsers/onnx 2>/dev/null && [[ -e parsers/onnx/errorHelpers.hpp ]]; then
|
||||
:
|
||||
else
|
||||
# Fallback: clone onnx-tensorrt at the branch recorded in .gitmodules for this TRT release.
|
||||
ONNX_TRT_BRANCH="$(git config -f .gitmodules submodule.parsers/onnx.branch || echo '')"
|
||||
rm -rf parsers/onnx
|
||||
git clone --depth=1 ${ONNX_TRT_BRANCH:+-b "${ONNX_TRT_BRANCH}"} https://github.com/onnx/onnx-tensorrt.git parsers/onnx
|
||||
fi
|
||||
# Prefer the system NvOnnxParser.h (matches the installed libnvonnxparser) when present.
|
||||
for header in /usr/include/aarch64-linux-gnu/NvOnnxParser.h /usr/include/NvOnnxParser.h; do
|
||||
if [[ -e "$header" ]]; then
|
||||
ln -sf "$header" /workspace/TensorRT/parsers/onnx/NvOnnxParser.h
|
||||
break
|
||||
fi
|
||||
done
|
||||
cd /workspace
|
||||
|
||||
# Build wheel
|
||||
cd /workspace/TensorRT/python
|
||||
|
||||
@@ -10,11 +10,12 @@ set -o errexit -o nounset -o pipefail
|
||||
|
||||
MODEL_CACHE_DIR=${MODEL_CACHE_DIR:-"/config/model_cache/tensorrt"}
|
||||
TRT_VER=${TRT_VER:-$(cat /etc/TENSORRT_VER)}
|
||||
TRT_MAJOR=${TRT_VER%%.*}
|
||||
OUTPUT_FOLDER="${MODEL_CACHE_DIR}/${TRT_VER}"
|
||||
YOLO_MODELS=${YOLO_MODELS:-""}
|
||||
|
||||
# Create output folder
|
||||
mkdir -p ${OUTPUT_FOLDER}
|
||||
mkdir -p "${OUTPUT_FOLDER}"
|
||||
|
||||
FIRST_MODEL=true
|
||||
MODEL_DOWNLOAD=""
|
||||
@@ -28,9 +29,9 @@ fi
|
||||
for model in ${YOLO_MODELS//,/ }
|
||||
do
|
||||
# Remove old link in case path/version changed
|
||||
rm -f ${MODEL_CACHE_DIR}/${model}.trt
|
||||
rm -f "${MODEL_CACHE_DIR}/${model}.trt"
|
||||
|
||||
if [[ ! -f ${OUTPUT_FOLDER}/${model}.trt ]]; then
|
||||
if [[ ! -f "${OUTPUT_FOLDER}/${model}.trt" ]]; then
|
||||
if [[ ${FIRST_MODEL} = true ]]; then
|
||||
MODEL_DOWNLOAD="${model%-dla}";
|
||||
MODEL_CONVERT="${model}"
|
||||
@@ -40,7 +41,7 @@ do
|
||||
MODEL_CONVERT+=",${model}";
|
||||
fi
|
||||
else
|
||||
ln -s ${OUTPUT_FOLDER}/${model}.trt ${MODEL_CACHE_DIR}/${model}.trt
|
||||
ln -s "${OUTPUT_FOLDER}/${model}.trt" "${MODEL_CACHE_DIR}/${model}.trt"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -50,8 +51,8 @@ if [[ -z ${MODEL_CONVERT} ]]; then
|
||||
fi
|
||||
|
||||
# Setup ENV to select GPU for conversion
|
||||
if [ ! -z ${TRT_MODEL_PREP_DEVICE+x} ]; then
|
||||
if [ ! -z ${CUDA_VISIBLE_DEVICES+x} ]; then
|
||||
if [ -n "${TRT_MODEL_PREP_DEVICE+x}" ]; then
|
||||
if [ -n "${CUDA_VISIBLE_DEVICES+x}" ]; then
|
||||
PREVIOUS_CVD="$CUDA_VISIBLE_DEVICES"
|
||||
unset CUDA_VISIBLE_DEVICES
|
||||
fi
|
||||
@@ -67,12 +68,30 @@ if [[ "$(arch)" == "aarch64" ]]; then
|
||||
if [[ ! -e /usr/lib/aarch64-linux-gnu/tegra && ! -e /usr/lib/aarch64-linux-gnu/tegra-egl ]]; then
|
||||
echo "ERROR: Container must be launched with nvidia runtime"
|
||||
exit 1
|
||||
elif [[ ! -e /usr/lib/aarch64-linux-gnu/libnvinfer.so.8 ||
|
||||
! -e /usr/lib/aarch64-linux-gnu/libnvinfer_plugin.so.8 ||
|
||||
! -e /usr/lib/aarch64-linux-gnu/libnvparsers.so.8 ||
|
||||
! -e /usr/lib/aarch64-linux-gnu/libnvonnxparser.so.8 ]]; then
|
||||
echo "ERROR: Please run the following on the HOST:"
|
||||
echo " sudo apt install libnvinfer8 libnvinfer-plugin8 libnvparsers8 libnvonnxparsers8 nvidia-container"
|
||||
fi
|
||||
|
||||
TRT_LIB_DIR=/usr/lib/aarch64-linux-gnu
|
||||
REQUIRED_TRT_LIBS=(
|
||||
"${TRT_LIB_DIR}/libnvinfer.so.${TRT_MAJOR}"
|
||||
"${TRT_LIB_DIR}/libnvinfer_plugin.so.${TRT_MAJOR}"
|
||||
"${TRT_LIB_DIR}/libnvonnxparser.so.${TRT_MAJOR}"
|
||||
)
|
||||
|
||||
if (( TRT_MAJOR < 10 )) || [[ -e "${TRT_LIB_DIR}/libnvparsers.so.${TRT_MAJOR}" ]]; then
|
||||
REQUIRED_TRT_LIBS+=("${TRT_LIB_DIR}/libnvparsers.so.${TRT_MAJOR}")
|
||||
fi
|
||||
|
||||
MISSING_TRT_LIBS=()
|
||||
for TRT_LIB in "${REQUIRED_TRT_LIBS[@]}"; do
|
||||
if [[ ! -e "${TRT_LIB}" ]]; then
|
||||
MISSING_TRT_LIBS+=("${TRT_LIB}")
|
||||
fi
|
||||
done
|
||||
|
||||
if (( ${#MISSING_TRT_LIBS[@]} > 0 )); then
|
||||
echo "ERROR: Missing TensorRT runtime libraries:"
|
||||
printf ' %s\n' "${MISSING_TRT_LIBS[@]}"
|
||||
echo "ERROR: Install the matching TensorRT ${TRT_MAJOR} runtime packages and nvidia-container on the HOST."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
@@ -83,33 +102,33 @@ echo "Generating the following TRT Models: ${MODEL_CONVERT}"
|
||||
cd /usr/local/src/tensorrt_demos/yolo
|
||||
|
||||
echo "Downloading yolo weights"
|
||||
./download_yolo.sh $MODEL_DOWNLOAD 2> /dev/null
|
||||
./download_yolo.sh "${MODEL_DOWNLOAD}" 2> /dev/null
|
||||
|
||||
for model in ${MODEL_CONVERT//,/ }
|
||||
do
|
||||
python3 yolo_to_onnx.py -m ${model%-dla} > /dev/null
|
||||
python3 yolo_to_onnx.py -m "${model%-dla}" > /dev/null
|
||||
|
||||
echo -e "\nGenerating ${model}.trt. This may take a few minutes.\n"; start=$(date +%s)
|
||||
if [[ $model == *-dla ]]; then
|
||||
cmd="python3 onnx_to_tensorrt.py -m ${model%-dla} --dla_core 0"
|
||||
cmd=(python3 onnx_to_tensorrt.py -m "${model%-dla}" --dla_core 0)
|
||||
else
|
||||
cmd="python3 onnx_to_tensorrt.py -m ${model}"
|
||||
cmd=(python3 onnx_to_tensorrt.py -m "${model}")
|
||||
fi
|
||||
$cmd > /tmp/onnx_to_tensorrt.log || { cat /tmp/onnx_to_tensorrt.log && continue; }
|
||||
"${cmd[@]}" > /tmp/onnx_to_tensorrt.log || { cat /tmp/onnx_to_tensorrt.log && continue; }
|
||||
|
||||
mv ${model%-dla}.trt ${OUTPUT_FOLDER}/${model}.trt;
|
||||
ln -s ${OUTPUT_FOLDER}/${model}.trt ${MODEL_CACHE_DIR}/${model}.trt
|
||||
mv "${model%-dla}.trt" "${OUTPUT_FOLDER}/${model}.trt";
|
||||
ln -s "${OUTPUT_FOLDER}/${model}.trt" "${MODEL_CACHE_DIR}/${model}.trt"
|
||||
echo "Generated ${model}.trt in $(($(date +%s)-start)) seconds"
|
||||
done
|
||||
|
||||
# Restore ENV after conversion
|
||||
if [ ! -z ${TRT_MODEL_PREP_DEVICE+x} ]; then
|
||||
if [ -n "${TRT_MODEL_PREP_DEVICE+x}" ]; then
|
||||
unset CUDA_VISIBLE_DEVICES
|
||||
if [ ! -z ${PREVIOUS_CVD+x} ]; then
|
||||
if [ -n "${PREVIOUS_CVD+x}" ]; then
|
||||
export CUDA_VISIBLE_DEVICES="$PREVIOUS_CVD"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Print which models exist in output folder
|
||||
echo "Available tensorrt models:"
|
||||
cd ${OUTPUT_FOLDER} && ls *.trt;
|
||||
cd "${OUTPUT_FOLDER}" && ls -- *.trt;
|
||||
|
||||
@@ -7,6 +7,21 @@ 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
|
||||
|
||||
# CUDA 13 removed libnvToolsExt.so (NVTX is now header-only nvtx3). The plugin Makefile links
|
||||
# -lnvToolsExt only for optional NVTX profiling annotations, so strip it when the lib is absent —
|
||||
# the link then succeeds on JP7 / CUDA 13. No-op on CUDA 12 (JP6), where the lib still exists.
|
||||
if ! ldconfig -p | grep -q 'libnvToolsExt\.so'; then
|
||||
sed -i 's/-lnvToolsExt//g' ./tensorrt_demos/plugins/Makefile
|
||||
fi
|
||||
|
||||
# TensorRT 10 (JP7 base) dropped libnvparsers.so (the legacy UFF/Caffe parsers). The plugin
|
||||
# Makefile links -lnvparsers from an over-broad LIBS list, but the YOLO custom layer never uses
|
||||
# it, so strip it when the lib is absent — the link then succeeds on TRT 10. No-op on TRT 8
|
||||
# (JP5/JP6), where libnvparsers still ships.
|
||||
if ! ldconfig -p | grep -q 'libnvparsers\.so'; then
|
||||
sed -i 's/-lnvparsers//g' ./tensorrt_demos/plugins/Makefile
|
||||
fi
|
||||
|
||||
# Build libyolo
|
||||
if [ ! -e /usr/local/cuda ]; then
|
||||
ln -s /usr/local/cuda-* /usr/local/cuda
|
||||
|
||||
@@ -13,6 +13,24 @@ variable "TRT_BASE" {
|
||||
variable "COMPUTE_LEVEL" {
|
||||
default = ""
|
||||
}
|
||||
variable "BUILD_ONNXRUNTIME_FROM_SOURCE" {
|
||||
default = "0"
|
||||
}
|
||||
variable "ONNXRUNTIME_VERSION" {
|
||||
default = "1.25.1"
|
||||
}
|
||||
variable "ONNXRUNTIME_BRANCH" {
|
||||
default = "rel-1.25.1"
|
||||
}
|
||||
variable "TENSORRT_PYTHON_BRANCH" {
|
||||
default = "release/8.6"
|
||||
}
|
||||
variable "L4T_APT_RELEASE" {
|
||||
default = ""
|
||||
}
|
||||
variable "JETSON_SOC_REPO" {
|
||||
default = ""
|
||||
}
|
||||
variable "BASE_HOOK" {
|
||||
# Ensure an up-to-date python 3.11 is available in jetson images
|
||||
default = <<EOT
|
||||
@@ -35,6 +53,12 @@ target "_build_args" {
|
||||
SLIM_BASE = SLIM_BASE,
|
||||
TRT_BASE = TRT_BASE,
|
||||
COMPUTE_LEVEL = COMPUTE_LEVEL,
|
||||
BUILD_ONNXRUNTIME_FROM_SOURCE = BUILD_ONNXRUNTIME_FROM_SOURCE,
|
||||
ONNXRUNTIME_VERSION = ONNXRUNTIME_VERSION,
|
||||
ONNXRUNTIME_BRANCH = ONNXRUNTIME_BRANCH,
|
||||
TENSORRT_PYTHON_BRANCH = TENSORRT_PYTHON_BRANCH,
|
||||
L4T_APT_RELEASE = L4T_APT_RELEASE,
|
||||
JETSON_SOC_REPO = JETSON_SOC_REPO,
|
||||
BASE_HOOK = BASE_HOOK
|
||||
}
|
||||
platforms = ["linux/${ARCH}"]
|
||||
|
||||
@@ -2,9 +2,11 @@ BOARDS += trt
|
||||
|
||||
JETPACK5_BASE ?= nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime # L4T 35.3.1 JetPack 5.1.1
|
||||
JETPACK6_BASE ?= nvcr.io/nvidia/tensorrt:23.12-py3-igpu
|
||||
JETPACK7_BASE ?= nvcr.io/nvidia/tensorrt:26.02-py3-igpu
|
||||
X86_DGPU_ARGS := ARCH=amd64 COMPUTE_LEVEL="50 60 70 80 90"
|
||||
JETPACK5_ARGS := ARCH=arm64 BASE_IMAGE=$(JETPACK5_BASE) SLIM_BASE=$(JETPACK5_BASE) TRT_BASE=$(JETPACK5_BASE)
|
||||
JETPACK6_ARGS := ARCH=arm64 BASE_IMAGE=$(JETPACK6_BASE) SLIM_BASE=$(JETPACK6_BASE) TRT_BASE=$(JETPACK6_BASE)
|
||||
JETPACK7_ARGS := ARCH=arm64 BASE_IMAGE=$(JETPACK7_BASE) SLIM_BASE=$(JETPACK7_BASE) TRT_BASE=$(JETPACK7_BASE) BUILD_ONNXRUNTIME_FROM_SOURCE=1 ONNXRUNTIME_VERSION=1.25.1 ONNXRUNTIME_BRANCH=rel-1.25.1 TENSORRT_PYTHON_BRANCH=auto L4T_APT_RELEASE=r39.2 JETSON_SOC_REPO=som
|
||||
|
||||
local-trt: version
|
||||
$(X86_DGPU_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
@@ -21,6 +23,11 @@ local-trt-jp6: version
|
||||
--set tensorrt.tags=frigate:latest-tensorrt-jp6 \
|
||||
--load
|
||||
|
||||
local-trt-jp7: version
|
||||
$(JETPACK7_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
--set tensorrt.tags=frigate:latest-tensorrt-jp7 \
|
||||
--load
|
||||
|
||||
build-trt:
|
||||
$(X86_DGPU_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
--set tensorrt.tags=$(IMAGE_REPO):${GITHUB_REF_NAME}-$(COMMIT_HASH)-tensorrt
|
||||
@@ -28,6 +35,8 @@ build-trt:
|
||||
--set tensorrt.tags=$(IMAGE_REPO):${GITHUB_REF_NAME}-$(COMMIT_HASH)-tensorrt-jp5
|
||||
$(JETPACK6_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
--set tensorrt.tags=$(IMAGE_REPO):${GITHUB_REF_NAME}-$(COMMIT_HASH)-tensorrt-jp6
|
||||
$(JETPACK7_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
--set tensorrt.tags=$(IMAGE_REPO):${GITHUB_REF_NAME}-$(COMMIT_HASH)-tensorrt-jp7
|
||||
|
||||
push-trt: build-trt
|
||||
$(X86_DGPU_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
@@ -39,3 +48,6 @@ push-trt: build-trt
|
||||
$(JETPACK6_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
--set tensorrt.tags=$(IMAGE_REPO):${GITHUB_REF_NAME}-$(COMMIT_HASH)-tensorrt-jp6 \
|
||||
--push
|
||||
$(JETPACK7_ARGS) docker buildx bake --file=docker/tensorrt/trt.hcl tensorrt \
|
||||
--set tensorrt.tags=$(IMAGE_REPO):${GITHUB_REF_NAME}-$(COMMIT_HASH)-tensorrt-jp7 \
|
||||
--push
|
||||
|
||||
@@ -126,12 +126,12 @@ birdseye:
|
||||
|
||||
### Sorting cameras in the Birdseye view
|
||||
|
||||
It is possible to override the order of cameras that are being shown in the Birdseye view. The order is set at the camera level.
|
||||
It is possible to override the order of cameras that are being shown in the Birdseye view. The order is set at the camera level (when using YAML).
|
||||
|
||||
<ConfigTabs>
|
||||
<TabItem value="ui">
|
||||
|
||||
Navigate to <NavPath path="Settings > Camera configuration > Birdseye" /> for each camera and set the **Position** field to control the display order.
|
||||
Navigate to <NavPath path="Settings > System > Birdseye" /> and in the **Camera order** field, use the drag handle next to each camera name to control the display order.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yaml">
|
||||
|
||||
@@ -194,7 +194,7 @@ Camera groups let you organize cameras together with a shared name and icon, mak
|
||||
<ConfigTabs>
|
||||
<TabItem value="ui">
|
||||
|
||||
On the Live dashboard, press the **+** icon in the main navigation to add a new camera group. Configure the group name, select which cameras to include, choose an icon, and set the display order.
|
||||
On the Live dashboard, press the **pencil icon** in the main navigation to add a new camera group. Configure the group name, select which cameras to include, choose an icon, and set the display order.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yaml">
|
||||
|
||||
@@ -7,27 +7,27 @@ import ConfigTabs from "@site/src/components/ConfigTabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
import NavPath from "@site/src/components/NavPath";
|
||||
|
||||
Some presets of FFmpeg args are provided by default to make the configuration easier. All presets can be seen in [this file](https://github.com/blakeblackshear/frigate/blob/master/frigate/ffmpeg_presets.py).
|
||||
Frigate ships with a set of FFmpeg presets to keep your configuration short and readable. Each preset expands to a longer list of FFmpeg arguments at runtime. You can see exactly what every preset expands to in [this file](https://github.com/blakeblackshear/frigate/blob/master/frigate/ffmpeg_presets.py).
|
||||
|
||||
### Hwaccel Presets
|
||||
In the config file you reference a preset by its name (for example, `preset-vaapi`). In the UI, the same preset is shown with a friendly label (for example, **VAAPI (Intel/AMD GPU)**). Both refer to the same thing — the tables below list the config name alongside the label you'll see in the UI.
|
||||
|
||||
It is highly recommended to use hwaccel presets in the config. These presets not only replace the longer args, but they also give Frigate hints of what hardware is available and allows Frigate to make other optimizations using the GPU such as when encoding the birdseye restream or when scaling a stream that has a size different than the native stream size.
|
||||
### Hwaccel (Hardware Acceleration) Presets
|
||||
|
||||
See [the hwaccel docs](/configuration/hardware_acceleration_video.md) for more info on how to setup hwaccel for your GPU / iGPU.
|
||||
Hardware acceleration arguments tell FFmpeg to decode your camera's video stream on a GPU or integrated graphics chip instead of the CPU, which dramatically lowers CPU usage. Using a preset is highly recommended. Beyond replacing a long list of arguments, each preset also tells Frigate what hardware is available so it can offload additional work to the GPU — for example, encoding the Birdseye restream or scaling a stream whose resolution differs from the camera's native size.
|
||||
|
||||
| Preset | Usage | Other Notes |
|
||||
| --------------------- | ------------------------------ | ----------------------------------------------------- |
|
||||
| preset-rpi-64-h264 | 64 bit Rpi with h264 stream | |
|
||||
| preset-rpi-64-h265 | 64 bit Rpi with h265 stream | |
|
||||
| preset-vaapi | Intel & AMD VAAPI | Check hwaccel docs to ensure correct driver is chosen |
|
||||
| preset-intel-qsv-h264 | Intel QSV with h264 stream | If issues occur recommend using vaapi preset instead |
|
||||
| preset-intel-qsv-h265 | Intel QSV with h265 stream | If issues occur recommend using vaapi preset instead |
|
||||
| preset-nvidia | Nvidia GPU | |
|
||||
| preset-jetson-h264 | Nvidia Jetson with h264 stream | |
|
||||
| preset-jetson-h265 | Nvidia Jetson with h265 stream | |
|
||||
| preset-rkmpp | Rockchip MPP | Use image with \*-rk suffix and privileged mode |
|
||||
See [the hardware acceleration docs](/configuration/hardware_acceleration_video.md) for details on setting up hardware acceleration for your GPU / iGPU, then select the preset that matches your hardware.
|
||||
|
||||
Select the appropriate hwaccel preset for your hardware.
|
||||
| Preset (YAML config) | UI Label | Usage | Notes |
|
||||
| --------------------- | ----------------------- | --------------------------------- | --------------------------------------------------------------- |
|
||||
| preset-rpi-64-h264 | Raspberry Pi (H.264) | 64-bit Raspberry Pi, H.264 stream | |
|
||||
| preset-rpi-64-h265 | Raspberry Pi (H.265) | 64-bit Raspberry Pi, H.265 stream | |
|
||||
| preset-vaapi | VAAPI (Intel/AMD GPU) | Intel or AMD GPU via VAAPI | Check the hwaccel docs to ensure the correct driver is selected |
|
||||
| preset-intel-qsv-h264 | Intel QuickSync (H.264) | Intel QuickSync, H.264 stream | If you have issues, use the VAAPI preset instead |
|
||||
| preset-intel-qsv-h265 | Intel QuickSync (H.265) | Intel QuickSync, H.265 stream | If you have issues, use the VAAPI preset instead |
|
||||
| preset-nvidia | NVIDIA GPU | NVIDIA GPU | |
|
||||
| preset-jetson-h264 | NVIDIA Jetson (H.264) | NVIDIA Jetson, H.264 stream | |
|
||||
| preset-jetson-h265 | NVIDIA Jetson (H.265) | NVIDIA Jetson, H.265 stream | |
|
||||
| preset-rkmpp | Rockchip RKMPP | Rockchip MPP | Use an image with the `-rk` suffix and run in privileged mode |
|
||||
|
||||
<ConfigTabs>
|
||||
<TabItem value="ui">
|
||||
@@ -53,25 +53,25 @@ cameras:
|
||||
|
||||
### Input Args Presets
|
||||
|
||||
Input args presets help make the config more readable and handle use cases for different types of streams to ensure maximum compatibility.
|
||||
Input arguments are passed to FFmpeg before your camera source and control how Frigate connects to and reads the stream — the transport protocol, timeouts, reconnection behavior, and how the stream is probed. The right input args ensure a reliable connection and maximum compatibility for each type of stream.
|
||||
|
||||
See [the camera specific docs](/configuration/camera_specific.md) for more info on non-standard cameras and recommendations for using them in Frigate.
|
||||
See [the camera-specific docs](/configuration/camera_specific.md) for more on non-standard cameras and recommendations for using them in Frigate.
|
||||
|
||||
| Preset | Usage | Other Notes |
|
||||
| -------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| preset-http-jpeg-generic | HTTP Live Jpeg | Recommend restreaming live jpeg instead |
|
||||
| preset-http-mjpeg-generic | HTTP Mjpeg Stream | Recommend restreaming mjpeg stream instead |
|
||||
| preset-http-reolink | Reolink HTTP-FLV Stream | Only for reolink http, not when restreaming as rtsp |
|
||||
| preset-rtmp-generic | RTMP Stream | |
|
||||
| preset-rtsp-generic | RTSP Stream | This is the default when nothing is specified |
|
||||
| preset-rtsp-restream | RTSP Stream from restream | Use for rtsp restream as source for frigate |
|
||||
| preset-rtsp-restream-low-latency | RTSP Stream from restream | Use for rtsp restream as source for frigate to lower latency, may cause issues with some cameras |
|
||||
| preset-rtsp-udp | RTSP Stream via UDP | Use when camera is UDP only |
|
||||
| preset-rtsp-blue-iris | Blue Iris RTSP Stream | Use when consuming a stream from Blue Iris |
|
||||
| Preset (config) | UI Label | Usage | Notes |
|
||||
| -------------------------------- | ----------------------------------------- | --------------------------- | ------------------------------------------------------------------------------- |
|
||||
| preset-http-jpeg-generic | HTTP JPEG (Generic) | HTTP live JPEG | Restreaming the live JPEG is recommended instead |
|
||||
| preset-http-mjpeg-generic | HTTP MJPEG (Generic) | HTTP MJPEG stream | Restreaming the MJPEG stream is recommended instead |
|
||||
| preset-http-reolink | HTTP - Reolink Cameras | Reolink HTTP-FLV stream | Only for Reolink HTTP, not when restreaming as RTSP |
|
||||
| preset-rtmp-generic | RTMP (Generic) | RTMP stream | |
|
||||
| preset-rtsp-generic | RTSP (Generic) | RTSP stream | The default when no input args are specified |
|
||||
| preset-rtsp-restream | RTSP - Restream from go2rtc | RTSP stream from a restream | Use when a go2rtc restream is the source for Frigate |
|
||||
| preset-rtsp-restream-low-latency | RTSP - Restream from go2rtc (Low Latency) | RTSP stream from a restream | Lowers latency for a go2rtc restream source; may cause issues with some cameras |
|
||||
| preset-rtsp-udp | RTSP - UDP | RTSP stream over UDP | Use when the camera only supports UDP |
|
||||
| preset-rtsp-blue-iris | RTSP - Blue Iris | Blue Iris RTSP stream | Use when consuming a stream from Blue Iris |
|
||||
|
||||
:::warning
|
||||
|
||||
It is important to be mindful of input args when using restream because you can have a mix of protocols. `http` and `rtmp` presets cannot be used with `rtsp` streams. For example, when using a reolink cam with the rtsp restream as a source for record the preset-http-reolink will cause a crash. In this case presets will need to be set at the stream level. See the example below.
|
||||
Be mindful of input arguments when restreaming, because you can end up with a mix of protocols. The `http` and `rtmp` presets cannot be used with `rtsp` streams. For example, using a Reolink camera with an RTSP restream as the recording source while `preset-http-reolink` is applied will cause a crash. In cases like this, set the preset at the stream level instead. See the example below.
|
||||
|
||||
:::
|
||||
|
||||
@@ -96,13 +96,13 @@ cameras:
|
||||
|
||||
### Output Args Presets
|
||||
|
||||
Output args presets help make the config more readable and handle use cases for different types of streams to ensure consistent recordings.
|
||||
Output arguments are passed to FFmpeg after your camera source and control how recordings are written — which codecs are used and whether audio and video are copied as-is or re-encoded. The right output args ensure consistent, playable recordings for each type of stream.
|
||||
|
||||
| Preset | Usage | Other Notes |
|
||||
| -------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| preset-record-generic | Record WITHOUT audio | If your camera doesn't have audio, or if you don't want to record audio, use this option |
|
||||
| preset-record-generic-audio-copy | Record WITH original audio | Use this to enable audio in recordings |
|
||||
| preset-record-generic-audio-aac | Record WITH transcoded aac audio | This is the default when no option is specified. Use it to transcode audio to AAC. If the source is already in AAC format, use preset-record-generic-audio-copy instead to avoid unnecessary re-encoding |
|
||||
| preset-record-mjpeg | Record an mjpeg stream | Recommend restreaming mjpeg stream instead |
|
||||
| preset-record-jpeg | Record live jpeg | Recommend restreaming live jpeg instead |
|
||||
| preset-record-ubiquiti | Record ubiquiti stream with audio | Recordings with ubiquiti non-standard audio |
|
||||
| Preset (config) | UI Label | Usage | Notes |
|
||||
| -------------------------------- | ------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| preset-record-generic | Record (Generic, no audio) | Record without audio | Use this if your camera has no audio, or if you don't want to record audio |
|
||||
| preset-record-generic-audio-copy | Record (Generic + Copy Audio) | Record with the original audio | Use this to keep the camera's audio in recordings without re-encoding |
|
||||
| preset-record-generic-audio-aac | Record (Generic + Audio to AAC) | Record with audio transcoded to AAC | The default when no output args are specified. Transcodes audio to AAC. If the source is already AAC, use `preset-record-generic-audio-copy` to avoid re-encoding |
|
||||
| preset-record-mjpeg | Record - MJPEG Cameras | Record an MJPEG stream | Restreaming the MJPEG stream is recommended instead |
|
||||
| preset-record-jpeg | Record - JPEG Cameras | Record a live JPEG | Restreaming the live JPEG is recommended instead |
|
||||
| preset-record-ubiquiti | Record - Ubiquiti Cameras | Record a Ubiquiti stream with audio | Handles Ubiquiti's non-standard audio format |
|
||||
|
||||
@@ -27,13 +27,12 @@ Running Generative AI models on CPU is not recommended, as high inference times
|
||||
|
||||
You must use a vision-capable model with Frigate. The following models are recommended for local deployment:
|
||||
|
||||
| Model | Notes |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `qwen3-vl` | Strong visual and situational understanding, enhanced ability to identify smaller objects and interactions with object. |
|
||||
| `qwen3.5` | Strong situational understanding, but missing DeepStack from qwen3-vl leading to worse performance for identifying objects in people's hand and other small details. |
|
||||
| `gemma4` | Strong situational understanding, sometimes resorts to more vague terms like 'interacts' instead of assigning a specific action. |
|
||||
| `Intern3.5VL` | Relatively fast with good vision comprehension |
|
||||
| `gemma3` | Slower model with good vision and temporal understanding |
|
||||
| Model | Notes |
|
||||
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `qwen3-vl` | Strong visual and situational understanding, enhanced ability to identify smaller objects and interactions with object. |
|
||||
| `qwen3.5` | Strong situational understanding, but missing DeepStack from qwen3-vl leading to worse performance for identifying objects in people's hand and other small details. |
|
||||
| `qwen3.6` | Strong situational understanding, similar to qwen3-vl |
|
||||
| `gemma4` | Strong situational understanding, sometimes resorts to more vague terms like 'interacts' instead of assigning a specific action. |
|
||||
|
||||
:::info
|
||||
|
||||
@@ -294,7 +293,7 @@ Other HTTP options are available, see the [python-genai documentation](https://g
|
||||
|
||||
### OpenAI
|
||||
|
||||
OpenAI does not have a free tier for their API. With the release of gpt-4o, pricing has been reduced and each generation should cost fractions of a cent if you choose to go this route.
|
||||
OpenAI does not have a free tier for their API.
|
||||
|
||||
#### Supported Models
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Object detection and enrichments (like Semantic Search, Face Recognition, and Li
|
||||
|
||||
- **Nvidia**
|
||||
- Nvidia GPUs will automatically be detected and used for enrichments in the `-tensorrt` Frigate image.
|
||||
- Jetson devices will automatically be detected and used for enrichments in the `-tensorrt-jp6` Frigate image.
|
||||
- Jetson devices will automatically be detected and used for enrichments in the `-tensorrt-jp6` Frigate image on JetPack 6 / L4T R36. JetPack 7 enrichment acceleration is documented after separate hardware validation.
|
||||
|
||||
- **RockChip**
|
||||
- RockChip NPU will automatically be detected and used for semantic search v1 and face recognition in the `-rk` Frigate image.
|
||||
|
||||
@@ -359,7 +359,7 @@ Or map in all the `/dev/video*` devices.
|
||||
|
||||
## NVIDIA Jetson
|
||||
|
||||
A separate set of docker images is available for Jetson devices. They come with an `ffmpeg` build with codecs that use the Jetson's dedicated media engine. If your Jetson host is running Jetpack 6.0+ use the `stable-tensorrt-jp6` tagged image. Note that the Orin Nano has no video encoder, so frigate will use software encoding on this platform, but the image will still allow hardware decoding and tensorrt object detection.
|
||||
A separate set of docker images is available for Jetson devices. They come with an `ffmpeg` build with codecs that use the Jetson's dedicated media engine. If your Jetson host is running JetPack 6 / L4T R36, use the `stable-tensorrt-jp6` tagged image. If your Jetson host is running JetPack 7.2 / L4T R39.2, use the `stable-tensorrt-jp7` tagged image. Note that the Orin Nano has no video encoder, so Frigate will use software encoding on this platform, but the Jetson TensorRT images will still allow hardware decoding and ONNX detector GPU acceleration.
|
||||
|
||||
You will need to use the image with the nvidia container runtime:
|
||||
|
||||
@@ -369,7 +369,7 @@ You will need to use the image with the nvidia container runtime:
|
||||
docker run -d \
|
||||
...
|
||||
--runtime nvidia
|
||||
ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp6
|
||||
ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp7
|
||||
```
|
||||
|
||||
### Docker Compose - Jetson
|
||||
@@ -378,10 +378,12 @@ docker run -d \
|
||||
services:
|
||||
frigate:
|
||||
...
|
||||
image: ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp6
|
||||
image: ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp7
|
||||
runtime: nvidia # Add this
|
||||
```
|
||||
|
||||
Use `stable-tensorrt-jp6` instead when the Jetson host is running JetPack 6 / L4T R36. JetPack 7.0/7.1 on L4T R38.x are not covered by the initial `stable-tensorrt-jp7` guidance unless separately validated.
|
||||
|
||||
:::note
|
||||
|
||||
The `runtime:` tag is not supported on older versions of docker-compose. If you run into this, you can instead use the nvidia runtime system-wide by adding `"default-runtime": "nvidia"` to `/etc/docker/daemon.json`:
|
||||
|
||||
@@ -46,8 +46,8 @@ Frigate supports multiple different detectors that work on different types of ha
|
||||
|
||||
**Nvidia Jetson** <CommunityBadge />
|
||||
|
||||
- [TensortRT](#nvidia-tensorrt-detector): TensorRT can run on Jetson devices, using one of many default models.
|
||||
- [ONNX](#onnx): TensorRT will automatically be detected and used as a detector in the `-tensorrt-jp6` Frigate image when a supported ONNX model is configured.
|
||||
- [TensortRT](#nvidia-tensorrt-detector): TensorRT can run on JetPack 6 / L4T R36 Jetson devices, using one of many default models.
|
||||
- [ONNX](#onnx): Jetson GPU acceleration will automatically be detected and used by the ONNX detector in the `-tensorrt-jp6` image on JetPack 6 / L4T R36 or the `-tensorrt-jp7` image on JetPack 7.2 / L4T R39.2 when a supported ONNX model is configured.
|
||||
|
||||
**Rockchip** <CommunityBadge />
|
||||
|
||||
@@ -437,7 +437,7 @@ If the correct build is used for your GPU then the GPU will be detected and used
|
||||
|
||||
- **Nvidia**
|
||||
- Nvidia GPUs will automatically be detected and used with the ONNX detector in the `-tensorrt` Frigate image.
|
||||
- Jetson devices will automatically be detected and used with the ONNX detector in the `-tensorrt-jp6` Frigate image.
|
||||
- Jetson devices will automatically be detected and used with the ONNX detector in the `-tensorrt-jp6` Frigate image on JetPack 6 / L4T R36 or the `-tensorrt-jp7` Frigate image on JetPack 7.2 / L4T R39.2.
|
||||
|
||||
:::
|
||||
|
||||
@@ -567,7 +567,9 @@ For detailed instructions on compiling models, refer to the [MemryX Compiler](ht
|
||||
|
||||
## NVidia TensorRT Detector
|
||||
|
||||
Nvidia Jetson devices may be used for object detection using the TensorRT libraries. Due to the size of the additional libraries, this detector is only provided in images with the `-tensorrt-jp6` tag suffix, e.g. `ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp6`. This detector is designed to work with Yolo models for object detection.
|
||||
Nvidia Jetson devices may be used for object detection using the TensorRT libraries on JetPack 6 / L4T R36. Due to the size of the additional libraries, this detector is only provided in images with the `-tensorrt-jp6` tag suffix, e.g. `ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp6`. This detector is designed to work with Yolo models for object detection.
|
||||
|
||||
On JetPack 7.2 / L4T R39.2, the `-tensorrt-jp7` image initially supports ONNX detector GPU acceleration. Native `type: tensorrt` engine generation on JP7 is pending hardware validation.
|
||||
|
||||
### Generate Models
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ Frigate supports multiple different detectors that work on different types of ha
|
||||
- [Supports majority of model architectures via ONNX](../../configuration/object_detectors#onnx)
|
||||
- Runs well with any size models including large
|
||||
|
||||
- <CommunityBadge /> [Jetson](#nvidia-jetson): Jetson devices are supported via the TensorRT or ONNX detectors when running Jetpack 6.
|
||||
- <CommunityBadge /> [Jetson](#nvidia-jetson): Jetson devices are supported via the TensorRT or ONNX detectors when running JetPack 6 / L4T R36. On JetPack 7.2 / L4T R39.2, the initial supported path is ONNX detector GPU acceleration.
|
||||
|
||||
**Rockchip** <CommunityBadge />
|
||||
|
||||
@@ -258,9 +258,9 @@ Inference speeds may vary depending on the host platform. The above data was mea
|
||||
|
||||
### Nvidia Jetson
|
||||
|
||||
Jetson devices are supported via the TensorRT or ONNX detectors when running Jetpack 6. It will [make use of the Jetson's hardware media engine](/configuration/hardware_acceleration_video#nvidia-jetson) when configured with the [appropriate presets](/configuration/ffmpeg_presets#hwaccel-presets), and will make use of the Jetson's GPU and DLA for object detection when configured with the [TensorRT detector](/configuration/object_detectors#nvidia-tensorrt-detector).
|
||||
Jetson devices running JetPack 6 / L4T R36 are supported via the TensorRT or ONNX detectors. Jetson devices running JetPack 7.2 / L4T R39.2 use the `stable-tensorrt-jp7` image and initially support ONNX detector GPU acceleration; native `type: tensorrt` engine generation on JP7 is pending hardware validation. The Jetson images will [make use of the Jetson's hardware media engine](/configuration/hardware_acceleration_video#nvidia-jetson) when configured with the [appropriate presets](/configuration/ffmpeg_presets#hwaccel-presets). The JetPack 6 image will also make use of the Jetson's GPU and DLA for object detection when configured with the [TensorRT detector](/configuration/object_detectors#nvidia-tensorrt-detector).
|
||||
|
||||
Inference speed will vary depending on the YOLO model, jetson platform and jetson nvpmodel (GPU/DLA/EMC clock speed). It is typically 20-40 ms for most models. The DLA is more efficient than the GPU, but not faster, so using the DLA will reduce power consumption but will slightly increase inference time.
|
||||
For JetPack 6 TensorRT detector usage, inference speed will vary depending on the YOLO model, jetson platform and jetson nvpmodel (GPU/DLA/EMC clock speed). It is typically 20-40 ms for most models. The DLA is more efficient than the GPU, but not faster, so using the DLA will reduce power consumption but will slightly increase inference time.
|
||||
|
||||
### Rockchip platform
|
||||
|
||||
|
||||
@@ -556,7 +556,8 @@ The official docker image tags for the current stable version are:
|
||||
|
||||
The community supported docker image tags for the current stable version are:
|
||||
|
||||
- `stable-tensorrt-jp6` - Frigate build optimized for Nvidia Jetson devices running Jetpack 6
|
||||
- `stable-tensorrt-jp6` - Frigate build optimized for Nvidia Jetson devices running JetPack 6 / L4T R36
|
||||
- `stable-tensorrt-jp7` - Frigate build optimized for Nvidia Jetson devices running JetPack 7.2 / L4T R39.2
|
||||
- `stable-rk` - Frigate build for SBCs with Rockchip SoC
|
||||
|
||||
## Home Assistant App
|
||||
|
||||
@@ -49,9 +49,9 @@ This almost always means that the width/height defined for your camera are not c
|
||||
|
||||
These messages in the logs are expected in certain situations. Frigate checks the integrity of the recordings before storing. Occasionally these cached files will be invalid and cleaned up automatically.
|
||||
|
||||
### "On connect called"
|
||||
### "MQTT connected" repeats in the logs
|
||||
|
||||
If you see repeated "On connect called" messages in your logs, check for another instance of Frigate. This happens when multiple Frigate containers are trying to connect to MQTT with the same `client_id`.
|
||||
If you see repeated "MQTT connected" messages in your logs, check for another instance of Frigate. This happens when multiple Frigate containers are trying to connect to MQTT with the same `client_id`.
|
||||
|
||||
### Error: Database Is Locked
|
||||
|
||||
|
||||
@@ -10,4 +10,47 @@ title: GPU Errors
|
||||
Some users have reported issues using some Intel iGPUs with OpenVINO, where the GPU would not be detected. This error can be caused by various problems, so it is important to ensure the configuration is setup correctly. Some solutions users have noted:
|
||||
|
||||
- In some cases users have noted that an HDMI dummy plug was necessary to be plugged into the motherboard's HDMI port.
|
||||
- When mixing an Intel iGPU with Nvidia GPU, the devices can be mixed up between `/dev/dri/renderD128` and `/dev/dri/renderD129` so it is important to confirm the correct device, or map the entire `/dev/dri` directory into the Frigate container.
|
||||
- When mixing an Intel iGPU with Nvidia GPU, the devices can be mixed up between `/dev/dri/renderD128` and `/dev/dri/renderD129` so it is important to confirm the correct device, or map the entire `/dev/dri` directory into the Frigate container.
|
||||
|
||||
## Intel/AMD GPU
|
||||
|
||||
### Hardware acceleration is not being used
|
||||
|
||||
For VAAPI or QSV to work, the GPU's render device must be passed through to the Frigate container. Intel and AMD GPUs expose this as a render node under `/dev/dri`, usually `/dev/dri/renderD128`. If it is not passed through, hardware acceleration is unavailable — ffmpeg fails to initialize it (for example `Failed to open the drm device` or `No VA display found for device`) and GPU usage stays at zero while CPU usage remains high.
|
||||
|
||||
Pass the render device through when starting the container. With `docker compose`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
frigate:
|
||||
devices:
|
||||
- /dev/dri/renderD128:/dev/dri/renderD128 # Intel / AMD GPU, update for your hardware
|
||||
```
|
||||
|
||||
Or with `docker run`, add `--device /dev/dri/renderD128`. See the [installation docs](/frigate/installation) for a complete example.
|
||||
|
||||
If it still isn't working after passing the device through:
|
||||
|
||||
- **Confirm the render node exists and is the correct one.** Run `ls /dev/dri` on the host — you should see one or more `renderD12X` entries. Systems with more than one GPU (an Intel iGPU plus a discrete GPU) can expose both `/dev/dri/renderD128` and `/dev/dri/renderD129`, and the numbering is not guaranteed. Pass through the correct node, or map the entire directory (`/dev/dri:/dev/dri`, or `--device /dev/dri`) so all render nodes are available.
|
||||
- **Check device permissions.** The Frigate process must be able to access the render node. This is usually automatic when the container runs as root (the default), but nested setups such as an unprivileged Proxmox/LXC container often require making the device accessible on the host (for example, a world-readable render node) or running the container privileged. Note that running Frigate inside an LXC is not officially supported — see the [installation docs](/frigate/installation#proxmox) for details.
|
||||
|
||||
### Failed to download frame: -5
|
||||
|
||||
When using VAAPI or QSV hardware acceleration, ffmpeg may crash and restart periodically with a signature like this in the `ffmpeg.<camera>.detect` log:
|
||||
|
||||
```
|
||||
[AVHWFramesContext @ 0x...] Failed to sync surface ... (operation failed).
|
||||
[hwdownload @ 0x...] Failed to download frame: -5.
|
||||
[vf#0:0 @ 0x...] Error while filtering: Input/output error
|
||||
[vf#0:0 @ 0x...] Task finished with error code: -5 (Input/output error)
|
||||
[frigate.video] <camera>: Unable to read frames from ffmpeg process.
|
||||
```
|
||||
|
||||
This is a hardware frame synchronization failure between ffmpeg and the GPU driver, not a Frigate bug. It comes from how a specific camera stream interacts with the GPU's decode and scaling path, so it is highly dependent on your hardware, driver, and stream. Frigate's automatic hardware acceleration detection is a best-guess effort, so the fix is usually to tune the configuration for your specific hardware and camera. The solutions below are ordered from most to least likely to help:
|
||||
|
||||
- **Switch between the VAAPI and QSV presets.** On Intel Gen 12 and newer iGPUs, `preset-intel-qsv-h264` / `preset-intel-qsv-h265` is often more stable than the auto-detected `preset-vaapi`. See the [hardware acceleration docs](/configuration/hardware_acceleration_video.md#intel-based-cpus) for the recommended preset for your Intel generation.
|
||||
- **Try a different VAAPI driver.** The default driver is `iHD`. On older Intel CPUs, `LIBVA_DRIVER_NAME=i965` can be more stable; on AMD GPUs use `LIBVA_DRIVER_NAME=radeonsi`. See [the hardware acceleration docs](/configuration/hardware_acceleration_video.md#intel-based-cpus) for how to set the driver.
|
||||
- **Use a codec that decodes more reliably.** H.265/HEVC streams may trigger this error far more often than H.264 depending on your CPU generation. If your camera exposes a separate sub-stream, assign an H.264 stream to the `detect` role. Cameras that output full-range YUV (for example some Hikvision models) are especially prone to it.
|
||||
- **Match the detect resolution to the stream resolution.** When the `detect` resolution differs from the stream, Frigate inserts a GPU scaling filter (`scale_vaapi`), which is where these surface-sync failures can often originate. Set the `detect` `width` and `height` to match the exact resolution of the stream assigned the `detect` role.
|
||||
- **Match the detect `fps` to the camera stream.** Aggressively dropping frames (for example `detect` `fps: 1` on a stream that runs at 15 fps) can cause timing mismatches in the GPU's frame buffer. Lower the sub-stream's frame rate on the camera itself instead of dropping most frames in Frigate.
|
||||
- **Fall back to software decoding.** If none of the above resolve it, remove the preset for that camera (`hwaccel_args: []`). Hardware decoding is only an optimization — on a capable CPU, software-decoding a low-resolution sub-stream is inexpensive and gives a stable detect pipeline.
|
||||
|
||||
@@ -35,9 +35,10 @@ devices:
|
||||
helpType: "warning"
|
||||
needsNvidiaConfig: true
|
||||
|
||||
- id: "stable-tensorrt-jp6"
|
||||
name: "NVIDIA Jetson"
|
||||
description: "Jetson development board"
|
||||
- &jetsonDevice
|
||||
id: "stable-tensorrt-jp6"
|
||||
name: "NVIDIA Jetson JP6"
|
||||
description: "JetPack 6 / L4T R36"
|
||||
icon: '<svg viewBox="0 0 32 33" xmlns="http://www.w3.org/2000/svg"><path d="M11.6925 17.7697V15.8762C11.8738 15.8633 12.0563 15.8528 12.2422 15.847C17.3372 15.6837 20.68 20.2978 20.68 20.2978C20.68 20.2978 17.0699 25.3962 13.1992 25.3962C12.6415 25.3962 12.1423 25.3052 11.6925 25.1511V19.4077C13.6766 19.6515 14.0748 20.5417 15.2671 22.5623L17.919 20.2885C17.919 20.2885 15.9831 17.7067 12.7195 17.7067C12.3649 17.7067 12.0253 17.7323 11.6913 17.7685L11.6925 17.7697ZM11.6913 11.5128V14.342C11.8738 14.3268 12.0574 14.3152 12.241 14.3082C19.3259 14.0655 23.9425 20.2162 23.9425 20.2162C23.9425 20.2162 18.6408 26.7705 13.1166 26.7705C12.6105 26.7705 12.1366 26.7227 11.6913 26.6433V28.3922C12.0723 28.4412 12.4671 28.4703 12.8779 28.4703C18.0177 28.4703 21.7358 25.8021 25.3356 22.6428C25.9323 23.1281 28.3754 24.31 28.8781 24.828C25.4549 27.7412 17.4795 30.0885 12.9571 30.0885C12.521 30.0885 12.1022 30.0617 11.6913 30.022V32.479H31.2282V11.514H11.6925L11.6913 11.5128ZM11.6913 25.15V26.6433C6.93708 25.7812 5.6174 20.7575 5.6174 20.7575C5.6174 20.7575 7.89986 18.1862 11.6913 17.7697V19.4077C11.6913 19.4077 11.6867 19.4077 11.6845 19.4077C9.69462 19.165 8.14085 21.055 8.14085 21.055C8.14085 21.055 9.01183 24.2365 11.6925 25.1523L11.6913 25.15ZM3.24888 20.5417C3.24888 20.5417 6.06609 16.3148 11.6925 15.8773V14.3443C5.46134 14.853 0.0644531 20.2185 0.0644531 20.2185C0.0644531 20.2185 3.12036 29.2018 11.6925 30.0243V28.3945C5.40167 27.5895 3.24888 20.5417 3.24888 20.5417Z" fill="#76B900"></path></svg>'
|
||||
svgStyle:
|
||||
width: 50px
|
||||
@@ -46,10 +47,17 @@ devices:
|
||||
padding-bottom: 15px
|
||||
imageTag: "stable-tensorrt-jp6"
|
||||
autoHardware: []
|
||||
helpText: "NVIDIA Jetson devices automatically configure runtime: nvidia."
|
||||
helpText: "JetPack 6 / L4T R36 Jetson devices automatically configure runtime: nvidia."
|
||||
helpType: "info"
|
||||
runtime: "nvidia"
|
||||
|
||||
- <<: *jetsonDevice
|
||||
id: "stable-tensorrt-jp7"
|
||||
name: "NVIDIA Jetson JP7"
|
||||
description: "JetPack 7.2 / L4T R39.2"
|
||||
imageTag: "stable-tensorrt-jp7"
|
||||
helpText: "JetPack 7.2 / L4T R39.2 Jetson devices automatically configure runtime: nvidia."
|
||||
|
||||
- id: "stable-rocm"
|
||||
name: "AMD GPU"
|
||||
description: "ROCm acceleration"
|
||||
@@ -176,6 +184,7 @@ hardware:
|
||||
description: "Pass through /dev/dri for GPU hardware acceleration (Intel/AMD)."
|
||||
disabledWhen:
|
||||
- "stable-tensorrt-jp6"
|
||||
- "stable-tensorrt-jp7"
|
||||
- "apple-silicon"
|
||||
devices:
|
||||
- host: "/dev/dri"
|
||||
@@ -187,6 +196,7 @@ hardware:
|
||||
description: "Pass through /dev/accel for Intel NPU acceleration."
|
||||
disabledWhen:
|
||||
- "stable-tensorrt-jp6"
|
||||
- "stable-tensorrt-jp7"
|
||||
- "apple-silicon"
|
||||
- "stable-rocm"
|
||||
- "stable-rk"
|
||||
@@ -247,6 +257,7 @@ hardware:
|
||||
disabledWhen:
|
||||
- "stable-tensorrt"
|
||||
- "stable-tensorrt-jp6"
|
||||
- "stable-tensorrt-jp7"
|
||||
- "stable-rocm"
|
||||
- "stable-rk"
|
||||
- "stable-synaptics"
|
||||
|
||||
@@ -86,13 +86,15 @@ class LicensePlateProcessingMixin:
|
||||
self.similarity_threshold = 0.8
|
||||
self.cluster_threshold = 0.85
|
||||
|
||||
def _detect(self, image: np.ndarray) -> List[np.ndarray]:
|
||||
def _detect(self, image: np.ndarray, debug_frame_id: int) -> List[np.ndarray]:
|
||||
"""
|
||||
Detect possible areas of text in the input image by first resizing and normalizing it,
|
||||
running a detection model, and filtering out low-probability regions.
|
||||
|
||||
Args:
|
||||
image (np.ndarray): The input image in which license plates will be detected.
|
||||
debug_frame_id (int): Shared id used to name debug images so all artifacts
|
||||
from a single LPR pass share the same filename suffix.
|
||||
|
||||
Returns:
|
||||
List[np.ndarray]: A list of bounding box coordinates representing detected license plates.
|
||||
@@ -106,9 +108,8 @@ class LicensePlateProcessingMixin:
|
||||
normalized_image = self._normalize_image(resized_image)
|
||||
|
||||
if WRITE_DEBUG_IMAGES:
|
||||
current_time = int(datetime.datetime.now().timestamp())
|
||||
cv2.imwrite(
|
||||
f"debug/frames/license_plate_resized_{current_time}.jpg",
|
||||
f"debug/frames/license_plate_resized_{debug_frame_id}.jpg",
|
||||
resized_image,
|
||||
)
|
||||
|
||||
@@ -203,7 +204,7 @@ class LicensePlateProcessingMixin:
|
||||
return self.ctc_decoder(outputs)
|
||||
|
||||
def _process_license_plate(
|
||||
self, camera: str, id: str, image: np.ndarray
|
||||
self, camera: str, id: str, image: np.ndarray, debug_frame_id: int
|
||||
) -> Tuple[List[str], List[List[float]], List[int]]:
|
||||
"""
|
||||
Complete pipeline for detecting, classifying, and recognizing license plates in the input image.
|
||||
@@ -214,6 +215,8 @@ class LicensePlateProcessingMixin:
|
||||
camera (str): Camera identifier.
|
||||
id (str): Event identifier.
|
||||
image (np.ndarray): The input image in which to detect, classify, and recognize license plates.
|
||||
debug_frame_id (int): Shared id used to name debug images so all artifacts
|
||||
from a single LPR pass share the same filename suffix.
|
||||
|
||||
Returns:
|
||||
Tuple[List[str], List[List[float]], List[int]]: Detected license plate texts, character-level confidence scores for each plate (flattened into a single list per plate), and areas of the plates.
|
||||
@@ -227,7 +230,7 @@ class LicensePlateProcessingMixin:
|
||||
logger.debug("Model runners not loaded")
|
||||
return [], [], []
|
||||
|
||||
boxes = self._detect(image)
|
||||
boxes = self._detect(image, debug_frame_id)
|
||||
if len(boxes) == 0:
|
||||
logger.debug(f"{camera}: No boxes found by OCR detector model")
|
||||
return [], [], []
|
||||
@@ -243,7 +246,6 @@ class LicensePlateProcessingMixin:
|
||||
boxes, plate_width=plate_width, gap_fraction=0.1
|
||||
)
|
||||
|
||||
current_time = int(datetime.datetime.now().timestamp())
|
||||
if WRITE_DEBUG_IMAGES:
|
||||
debug_image = image.copy()
|
||||
for box in boxes:
|
||||
@@ -259,7 +261,7 @@ class LicensePlateProcessingMixin:
|
||||
)
|
||||
|
||||
cv2.imwrite(
|
||||
f"debug/frames/license_plate_boxes_{current_time}.jpg", debug_image
|
||||
f"debug/frames/license_plate_boxes_{debug_frame_id}.jpg", debug_image
|
||||
)
|
||||
|
||||
boxes = self._sort_boxes(list(boxes))
|
||||
@@ -322,7 +324,7 @@ class LicensePlateProcessingMixin:
|
||||
if WRITE_DEBUG_IMAGES:
|
||||
for i, img in enumerate(group_plate_images):
|
||||
cv2.imwrite(
|
||||
f"debug/frames/license_plate_cropped_{current_time}_{group_indices[i] + 1}.jpg",
|
||||
f"debug/frames/license_plate_cropped_{debug_frame_id}_{group_indices[i] + 1}.jpg",
|
||||
img,
|
||||
)
|
||||
|
||||
@@ -335,7 +337,7 @@ class LicensePlateProcessingMixin:
|
||||
cv2.imwrite(
|
||||
os.path.join(
|
||||
CLIPS_DIR,
|
||||
f"lpr/{camera}/{id}/{current_time}_{group_indices[i] + 1}.jpg",
|
||||
f"lpr/{camera}/{id}/{debug_frame_id}_{group_indices[i] + 1}.jpg",
|
||||
),
|
||||
img,
|
||||
)
|
||||
@@ -1199,6 +1201,7 @@ class LicensePlateProcessingMixin:
|
||||
self.metrics.yolov9_lpr_pps.value = self.plates_det_second.eps()
|
||||
camera = obj_data if dedicated_lpr else obj_data["camera"]
|
||||
current_time = int(datetime.datetime.now().timestamp())
|
||||
debug_frame_id = int(datetime.datetime.now().timestamp() * 1000)
|
||||
|
||||
if not self.config.cameras[camera].lpr.enabled:
|
||||
return
|
||||
@@ -1214,7 +1217,7 @@ class LicensePlateProcessingMixin:
|
||||
|
||||
if WRITE_DEBUG_IMAGES:
|
||||
cv2.imwrite(
|
||||
f"debug/frames/dedicated_lpr_masked_{current_time}.jpg",
|
||||
f"debug/frames/dedicated_lpr_masked_{debug_frame_id}.jpg",
|
||||
rgb,
|
||||
)
|
||||
|
||||
@@ -1326,7 +1329,7 @@ class LicensePlateProcessingMixin:
|
||||
|
||||
if WRITE_DEBUG_IMAGES:
|
||||
cv2.imwrite(
|
||||
f"debug/frames/car_frame_{current_time}.jpg",
|
||||
f"debug/frames/car_frame_{debug_frame_id}.jpg",
|
||||
car,
|
||||
)
|
||||
|
||||
@@ -1454,7 +1457,7 @@ class LicensePlateProcessingMixin:
|
||||
|
||||
if WRITE_DEBUG_IMAGES:
|
||||
cv2.imwrite(
|
||||
f"debug/frames/license_plate_frame_{current_time}.jpg",
|
||||
f"debug/frames/license_plate_frame_{debug_frame_id}.jpg",
|
||||
license_plate_frame,
|
||||
)
|
||||
|
||||
@@ -1464,7 +1467,7 @@ class LicensePlateProcessingMixin:
|
||||
# run detection, returns results sorted by confidence, best first
|
||||
start = datetime.datetime.now().timestamp()
|
||||
license_plates, confidences, areas = self._process_license_plate(
|
||||
camera, id, license_plate_frame
|
||||
camera, id, license_plate_frame, debug_frame_id
|
||||
)
|
||||
self.plates_rec_second.update()
|
||||
self.plate_rec_speed.update(datetime.datetime.now().timestamp() - start)
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
"""Test in-place yaml config updates."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from frigate.util.builtin import update_yaml_file_bulk
|
||||
|
||||
|
||||
class TestUpdateYaml(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.yaml = YAML()
|
||||
fd, self.config_path = tempfile.mkstemp(suffix=".yml")
|
||||
os.close(fd)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
os.unlink(self.config_path)
|
||||
|
||||
def _write(self, text: str) -> None:
|
||||
with open(self.config_path, "w") as f:
|
||||
f.write(text)
|
||||
|
||||
def _read(self) -> str:
|
||||
with open(self.config_path) as f:
|
||||
return f.read()
|
||||
|
||||
def _load(self):
|
||||
with open(self.config_path) as f:
|
||||
return self.yaml.load(f)
|
||||
|
||||
def test_delete_key(self):
|
||||
"""Deleting a key removes it and leaves valid yaml."""
|
||||
self._write(
|
||||
"cameras:\n"
|
||||
" cam1:\n"
|
||||
" objects:\n"
|
||||
" filters:\n"
|
||||
" car:\n"
|
||||
" mask: 0,0.45,0.245,0.45\n"
|
||||
)
|
||||
update_yaml_file_bulk(
|
||||
self.config_path, {"cameras.cam1.objects.filters.car.mask": ""}
|
||||
)
|
||||
data = self._load()
|
||||
assert "mask" not in data["cameras"]["cam1"]["objects"]["filters"]["car"]
|
||||
|
||||
def test_delete_commented_key_emptying_map(self):
|
||||
"""Deleting the only key of a map whose key carries comments must not
|
||||
emit unparseable yaml (orphaned comment tokens above a flow-style {})."""
|
||||
self._write(
|
||||
"cameras:\n"
|
||||
" cam1:\n"
|
||||
" objects:\n"
|
||||
" filters:\n"
|
||||
" car:\n"
|
||||
" # cars parked across the street\n"
|
||||
" # second comment line\n"
|
||||
" mask: 0,0.45,0.245,0.45\n"
|
||||
" motion:\n"
|
||||
" mask: 0,0.449,0.686,0.395\n"
|
||||
)
|
||||
update_yaml_file_bulk(
|
||||
self.config_path, {"cameras.cam1.objects.filters.car.mask": ""}
|
||||
)
|
||||
# must re-parse cleanly
|
||||
data = self._load()
|
||||
assert "mask" not in data["cameras"]["cam1"]["objects"]["filters"]["car"]
|
||||
assert data["cameras"]["cam1"]["motion"]["mask"] == "0,0.449,0.686,0.395"
|
||||
# the orphaned comments must be gone from the file, not just parseable
|
||||
content = self._read()
|
||||
assert "cars parked across the street" not in content
|
||||
assert "second comment line" not in content
|
||||
|
||||
def test_delete_last_named_mask_emptying_map(self):
|
||||
"""The path the current UI actually sends: a named object mask deleted
|
||||
down to an empty `mask` map, with a comment inside that map."""
|
||||
self._write(
|
||||
"cameras:\n"
|
||||
" cam1:\n"
|
||||
" objects:\n"
|
||||
" filters:\n"
|
||||
" car:\n"
|
||||
" mask:\n"
|
||||
" # ignore the neighbor's driveway\n"
|
||||
" driveway:\n"
|
||||
" coordinates: 0,0.1,0.2,0.3\n"
|
||||
)
|
||||
update_yaml_file_bulk(
|
||||
self.config_path,
|
||||
{"cameras.cam1.objects.filters.car.mask.driveway": ""},
|
||||
)
|
||||
data = self._load()
|
||||
assert data["cameras"]["cam1"]["objects"]["filters"]["car"]["mask"] == {}
|
||||
assert "ignore the neighbor's driveway" not in self._read()
|
||||
|
||||
def test_delete_last_commented_list_item(self):
|
||||
"""Deleting the last element of a commented sequence must not emit
|
||||
an orphaned comment above a flow-style [] at column 0."""
|
||||
self._write(
|
||||
"cameras:\n"
|
||||
" cam1:\n"
|
||||
" motion:\n"
|
||||
" mask:\n"
|
||||
" # driveway motion mask\n"
|
||||
" - 0,0.4,0.6,0.4\n"
|
||||
)
|
||||
update_yaml_file_bulk(self.config_path, {"cameras.cam1.motion.mask.0": ""})
|
||||
data = self._load()
|
||||
assert data["cameras"]["cam1"]["motion"]["mask"] == []
|
||||
assert "driveway motion mask" not in self._read()
|
||||
|
||||
def test_delete_list_item_preserves_remaining(self):
|
||||
"""Deleting one element of a sequence keeps the others and stays valid."""
|
||||
self._write(
|
||||
"cameras:\n"
|
||||
" cam1:\n"
|
||||
" motion:\n"
|
||||
" mask:\n"
|
||||
" - 0,0.4,0.6,0.4\n"
|
||||
" - 0,0.1,0.2,0.3\n"
|
||||
)
|
||||
update_yaml_file_bulk(self.config_path, {"cameras.cam1.motion.mask.0": ""})
|
||||
data = self._load()
|
||||
assert data["cameras"]["cam1"]["motion"]["mask"] == ["0,0.1,0.2,0.3"]
|
||||
|
||||
def test_delete_key_preserves_siblings(self):
|
||||
"""Deleting one key among several keeps the sibling entries and any
|
||||
comments on keys preceding the deleted one."""
|
||||
self._write(
|
||||
"cameras:\n"
|
||||
" cam1:\n"
|
||||
" objects:\n"
|
||||
" filters:\n"
|
||||
" car:\n"
|
||||
" # mask drawn around the parked suv\n"
|
||||
" mask: 0,0.45,0.245,0.45\n"
|
||||
" threshold: 0.8\n"
|
||||
)
|
||||
update_yaml_file_bulk(
|
||||
self.config_path, {"cameras.cam1.objects.filters.car.threshold": ""}
|
||||
)
|
||||
data = self._load()
|
||||
car = data["cameras"]["cam1"]["objects"]["filters"]["car"]
|
||||
assert "threshold" not in car
|
||||
assert car["mask"] == "0,0.45,0.245,0.45"
|
||||
assert "# mask drawn around the parked suv" in self._read()
|
||||
|
||||
def test_delete_first_commented_key_keeps_map_valid(self):
|
||||
"""Deleting a commented key from a map that still has other keys
|
||||
leaves the remaining entries intact and the file parseable."""
|
||||
self._write(
|
||||
"cameras:\n"
|
||||
" cam1:\n"
|
||||
" objects:\n"
|
||||
" filters:\n"
|
||||
" car:\n"
|
||||
" # comment on the deleted key\n"
|
||||
" mask: 0,0.45,0.245,0.45\n"
|
||||
" threshold: 0.8\n"
|
||||
)
|
||||
update_yaml_file_bulk(
|
||||
self.config_path, {"cameras.cam1.objects.filters.car.mask": ""}
|
||||
)
|
||||
data = self._load()
|
||||
car = data["cameras"]["cam1"]["objects"]["filters"]["car"]
|
||||
assert "mask" not in car
|
||||
assert car["threshold"] == 0.8
|
||||
|
||||
def test_update_value_preserves_comments(self):
|
||||
"""Updating a value keeps surrounding comments intact."""
|
||||
self._write(
|
||||
"cameras:\n cam1:\n detect:\n # tuned for the pi\n fps: 4\n"
|
||||
)
|
||||
update_yaml_file_bulk(self.config_path, {"cameras.cam1.detect.fps": 5})
|
||||
data = self._load()
|
||||
assert data["cameras"]["cam1"]["detect"]["fps"] == 5
|
||||
assert "# tuned for the pi" in self._read()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
@@ -293,26 +293,51 @@ def update_yaml_file_bulk(file_path: str, updates: Dict[str, Any]):
|
||||
logger.error(f"Unable to write to Frigate config file {file_path}: {e}")
|
||||
|
||||
|
||||
def clear_orphaned_comments(collection, parent, parent_key) -> None:
|
||||
"""Drop stale ruamel comment tokens after a deletion empties a collection.
|
||||
|
||||
When the last entry of a mapping or sequence is removed, any comments that
|
||||
lived inside that collection's block are orphaned. ruamel then emits them
|
||||
above a flow-style `{}`/`[]` dedented to column 0, which is unparseable and
|
||||
corrupts the config. Clearing the emptied collection's own comment metadata
|
||||
(and the parent's entry pointing at it) keeps the dump valid. Non-empty
|
||||
collections are left untouched so comments on remaining siblings survive.
|
||||
"""
|
||||
if not hasattr(collection, "ca") or len(collection) != 0:
|
||||
return
|
||||
|
||||
collection.ca.items.clear()
|
||||
collection.ca.comment = None
|
||||
if parent is not None and hasattr(parent, "ca"):
|
||||
parent.ca.items.pop(parent_key, None)
|
||||
|
||||
|
||||
def update_yaml(data, key_path, new_value):
|
||||
temp = data
|
||||
parent = None
|
||||
parent_key = None
|
||||
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)
|
||||
parent, parent_key = temp[key[0]], key[1]
|
||||
temp = temp[key[0]][key[1]]
|
||||
else:
|
||||
if key not in temp or temp[key] is None:
|
||||
temp[key] = {}
|
||||
parent, parent_key = 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]]
|
||||
clear_orphaned_comments(temp[last_key[0]], temp, last_key[0])
|
||||
else:
|
||||
del temp[last_key]
|
||||
clear_orphaned_comments(temp, parent, parent_key)
|
||||
else:
|
||||
if isinstance(last_key, tuple):
|
||||
if last_key[0] not in temp:
|
||||
|
||||
@@ -2,7 +2,11 @@ import { baseUrl } from "./baseUrl";
|
||||
import { ReactNode, useCallback, useEffect, useRef } from "react";
|
||||
import { WsSendContext } from "./wsContext";
|
||||
import type { Update } from "./wsContext";
|
||||
import { processWsMessage, resetWsStore } from "./ws";
|
||||
import {
|
||||
invalidateCameraActivityCache,
|
||||
processWsMessage,
|
||||
resetWsStore,
|
||||
} from "./ws";
|
||||
|
||||
export function WsProvider({ children }: { children: ReactNode }) {
|
||||
const wsUrl = `${baseUrl.replace(/^http/, "ws")}ws`;
|
||||
@@ -34,6 +38,9 @@ export function WsProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
ws.onopen = () => {
|
||||
reconnectAttempt.current = 0;
|
||||
// events may have been missed while disconnected — the snapshot
|
||||
// requested below must fully apply even if byte-identical
|
||||
invalidateCameraActivityCache();
|
||||
ws.send(
|
||||
JSON.stringify({ topic: "onConnect", message: "", retain: false }),
|
||||
);
|
||||
|
||||
+28
-5
@@ -82,12 +82,15 @@ export function processWsMessage(raw: string) {
|
||||
|
||||
function applyTopicUpdate(topic: string, newVal: unknown) {
|
||||
const oldVal = wsState[topic];
|
||||
// camera_activity snapshots always re-notify: consumers reconcile local
|
||||
// state that may have diverged from an unchanged snapshot
|
||||
const isActivitySnapshot = topic.startsWith("camera_activity/");
|
||||
// Fast path: === for primitives ("ON"/"OFF", numbers).
|
||||
// Fall back to isEqual for objects/arrays.
|
||||
const unchanged =
|
||||
oldVal === newVal ||
|
||||
(typeof newVal === "object" && newVal !== null && isEqual(oldVal, newVal));
|
||||
if (unchanged) return;
|
||||
if (unchanged && !isActivitySnapshot) return;
|
||||
|
||||
wsState[topic] = newVal;
|
||||
// Snapshot the Set — a listener may trigger unmount that modifies it.
|
||||
@@ -131,6 +134,25 @@ let wsMessageIdCounter = 0;
|
||||
// traversals) on every flush — critical with many cameras.
|
||||
let lastCameraActivityPayload: string | null = null;
|
||||
|
||||
// Make the next camera_activity snapshot fully apply even when byte-identical
|
||||
// to the previous one — local state may have diverged while no messages flowed
|
||||
export function invalidateCameraActivityCache() {
|
||||
lastCameraActivityPayload = null;
|
||||
}
|
||||
|
||||
// Collapse same-task resync requests (one hook per camera card) into a
|
||||
// single onConnect round-trip
|
||||
let resyncScheduled = false;
|
||||
function requestCameraActivityResync(sendOnConnect: () => void) {
|
||||
if (resyncScheduled) return;
|
||||
resyncScheduled = true;
|
||||
queueMicrotask(() => {
|
||||
resyncScheduled = false;
|
||||
invalidateCameraActivityCache();
|
||||
sendOnConnect();
|
||||
});
|
||||
}
|
||||
|
||||
function applyCameraActivity(payload: string) {
|
||||
// Fast path: if the raw JSON string is identical, nothing changed.
|
||||
if (payload === lastCameraActivityPayload) return;
|
||||
@@ -509,15 +531,16 @@ export function useInitialCameraState(
|
||||
// camera_activity sub-topic payload is already parsed by expandCameraActivity
|
||||
const data = payload as FrigateCameraState | undefined;
|
||||
|
||||
// onConnect is sent once in WsProvider.onopen — no need to re-request on
|
||||
// every component mount. Components read cached wsState immediately via
|
||||
// useSyncExternalStore. Only re-request when the user tabs back in.
|
||||
// the cached snapshot is only written on onConnect and can be stale by the
|
||||
// time this hook mounts — re-request on mount and when the user tabs back in
|
||||
useEffect(() => {
|
||||
if (!revalidateOnFocus) return;
|
||||
|
||||
requestCameraActivityResync(() => sendCommand("onConnect"));
|
||||
|
||||
const listener = () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
sendCommand("onConnect");
|
||||
requestCameraActivityResync(() => sendCommand("onConnect"));
|
||||
}
|
||||
};
|
||||
addEventListener("visibilitychange", listener);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "@/api/ws";
|
||||
import { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
|
||||
import { MotionData, ReviewSegment } from "@/types/review";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { AudioDetection, ObjectType } from "@/types/ws";
|
||||
import { useTimelineUtils } from "./use-timeline-utils";
|
||||
import useDeepMemo from "./use-deep-memo";
|
||||
@@ -57,7 +57,13 @@ export function useCameraActivity(
|
||||
);
|
||||
useEffect(() => {
|
||||
if (updatedCameraState) {
|
||||
setObjects(updatedCameraState.objects);
|
||||
// functional updater keeps `objects` out of the deps: this effect must
|
||||
// only run on snapshot arrival, or it would re-assert a stale snapshot
|
||||
// over newer event-driven state
|
||||
const newObjects = updatedCameraState.objects ?? [];
|
||||
setObjects((currentObjects) =>
|
||||
isEqual(currentObjects, newObjects) ? currentObjects : newObjects,
|
||||
);
|
||||
}
|
||||
}, [updatedCameraState, camera]);
|
||||
|
||||
@@ -82,15 +88,6 @@ export function useCameraActivity(
|
||||
const { payload: event } = useFrigateEvents();
|
||||
const updatedEvent = useDeepMemo(event);
|
||||
|
||||
const handleSetObjects = useCallback(
|
||||
(newObjects: ObjectType[]) => {
|
||||
if (!isEqual(objects, newObjects)) {
|
||||
setObjects(newObjects);
|
||||
}
|
||||
},
|
||||
[objects],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!updatedEvent) {
|
||||
return;
|
||||
@@ -100,53 +97,71 @@ export function useCameraActivity(
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedEventIndex =
|
||||
objects?.findIndex((obj) => obj.id === updatedEvent.after.id) ?? -1;
|
||||
// functional updater keeps `objects` out of the deps: each event applies
|
||||
// exactly once against the true current list, so events arriving close
|
||||
// together cannot clobber each other's changes with a stale write-back
|
||||
setObjects((currentObjects) => {
|
||||
const existingObjects = currentObjects ?? [];
|
||||
const updatedEventIndex = existingObjects.findIndex(
|
||||
(obj) => obj.id === updatedEvent.after.id,
|
||||
);
|
||||
|
||||
let newObjects: ObjectType[] = [...(objects ?? [])];
|
||||
|
||||
if (updatedEvent.type === "end") {
|
||||
if (updatedEventIndex !== -1) {
|
||||
if (updatedEvent.type === "end") {
|
||||
if (updatedEventIndex === -1) {
|
||||
return currentObjects;
|
||||
}
|
||||
const newObjects = [...existingObjects];
|
||||
newObjects.splice(updatedEventIndex, 1);
|
||||
return newObjects;
|
||||
}
|
||||
} else {
|
||||
|
||||
let label = updatedEvent.after.label;
|
||||
|
||||
if (updatedEvent.after.sub_label) {
|
||||
const sub_label = updatedEvent.after.sub_label[0];
|
||||
|
||||
if (attributeLabels.includes(sub_label)) {
|
||||
label = sub_label;
|
||||
} else {
|
||||
label = `${label}-verified`;
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedEventIndex === -1) {
|
||||
// add unknown updatedEvent to list if not stationary
|
||||
if (!updatedEvent.after.stationary) {
|
||||
const newActiveObject: ObjectType = {
|
||||
id: updatedEvent.after.id,
|
||||
label: updatedEvent.after.label,
|
||||
stationary: updatedEvent.after.stationary,
|
||||
area: updatedEvent.after.area,
|
||||
ratio: updatedEvent.after.ratio,
|
||||
score: updatedEvent.after.score,
|
||||
sub_label: updatedEvent.after.sub_label?.[0] ?? "",
|
||||
};
|
||||
newObjects = [...(objects ?? []), newActiveObject];
|
||||
if (updatedEvent.after.stationary) {
|
||||
return currentObjects;
|
||||
}
|
||||
} else {
|
||||
let label = updatedEvent.after.label;
|
||||
|
||||
if (updatedEvent.after.sub_label) {
|
||||
const sub_label = updatedEvent.after.sub_label[0];
|
||||
|
||||
if (attributeLabels.includes(sub_label)) {
|
||||
label = sub_label;
|
||||
} else {
|
||||
label = `${label}-verified`;
|
||||
}
|
||||
}
|
||||
|
||||
newObjects[updatedEventIndex] = {
|
||||
...newObjects[updatedEventIndex],
|
||||
const newActiveObject: ObjectType = {
|
||||
id: updatedEvent.after.id,
|
||||
label,
|
||||
stationary: updatedEvent.after.stationary,
|
||||
area: updatedEvent.after.area,
|
||||
ratio: updatedEvent.after.ratio,
|
||||
score: updatedEvent.after.score,
|
||||
sub_label: updatedEvent.after.sub_label?.[0] ?? "",
|
||||
};
|
||||
return [...existingObjects, newActiveObject];
|
||||
}
|
||||
}
|
||||
|
||||
handleSetObjects(newObjects);
|
||||
}, [attributeLabels, camera, updatedEvent, objects, handleSetObjects]);
|
||||
const existing = existingObjects[updatedEventIndex];
|
||||
|
||||
if (
|
||||
existing.label === label &&
|
||||
existing.stationary === updatedEvent.after.stationary
|
||||
) {
|
||||
return currentObjects;
|
||||
}
|
||||
|
||||
const newObjects = [...existingObjects];
|
||||
newObjects[updatedEventIndex] = {
|
||||
...existing,
|
||||
label,
|
||||
stationary: updatedEvent.after.stationary,
|
||||
};
|
||||
return newObjects;
|
||||
});
|
||||
}, [attributeLabels, camera, updatedEvent]);
|
||||
|
||||
// determine if camera is offline
|
||||
|
||||
|
||||
Reference in New Issue
Block a user