forked from Github/frigate
Compare commits
72 Commits
v0.12.0-be
...
v0.12.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66881eb89f | ||
|
|
ad60f4894b | ||
|
|
8d21c950a3 | ||
|
|
ea8ec23cbe | ||
|
|
f06e8b47be | ||
|
|
7edeaa3407 | ||
|
|
7a7e30e1ff | ||
|
|
78dc75a4f1 | ||
|
|
a5078b8517 | ||
|
|
77876bd663 | ||
|
|
a0697b81a1 | ||
|
|
88b903ba39 | ||
|
|
116edce3dc | ||
|
|
7130d93616 | ||
|
|
11054daebe | ||
|
|
7926ddd008 | ||
|
|
93f2316711 | ||
|
|
fd3688a9c7 | ||
|
|
a63dd9bccd | ||
|
|
f54b223adf | ||
|
|
9ddcf7b1fe | ||
|
|
07c635d822 | ||
|
|
6ed8977548 | ||
|
|
e50cccc78a | ||
|
|
feeb7107d5 | ||
|
|
f52de322d3 | ||
|
|
2f3e046130 | ||
|
|
edbdbb7f07 | ||
|
|
4bb5785d89 | ||
|
|
36c6ee73fe | ||
|
|
924f946e46 | ||
|
|
d22e25064b | ||
|
|
12d51d3c73 | ||
|
|
5fa1fbafed | ||
|
|
d36ab05bf1 | ||
|
|
fb85c8ca54 | ||
|
|
b2a2a9d6c7 | ||
|
|
e2239d36c9 | ||
|
|
6620236bc3 | ||
|
|
02df2a8bbd | ||
|
|
89513e469a | ||
|
|
f5466426df | ||
|
|
2631a4c35b | ||
|
|
ef9338f5af | ||
|
|
19afb035ff | ||
|
|
a7751f210b | ||
|
|
3bec28ffef | ||
|
|
81b3fdb423 | ||
|
|
30f520f6f0 | ||
|
|
0de1da5943 | ||
|
|
e39fb51dec | ||
|
|
621aa0cf61 | ||
|
|
367ac28a94 | ||
|
|
99577a57e6 | ||
|
|
65bc644d03 | ||
|
|
01b9d4d848 | ||
|
|
daadd206dd | ||
|
|
60b2315028 | ||
|
|
19d17c8c81 | ||
|
|
24410849b7 | ||
|
|
e0b3b27b8a | ||
|
|
170899bd71 | ||
|
|
2031c3146f | ||
|
|
dafc7edb59 | ||
|
|
7fbfdb3cb8 | ||
|
|
e0a0d129df | ||
|
|
2d76363da5 | ||
|
|
54bfa4679c | ||
|
|
3c708d7672 | ||
|
|
b4fb6086b3 | ||
|
|
ab44a65fe3 | ||
|
|
95596b39fd |
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@@ -6,6 +6,11 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
# only run the latest commit to avoid cache overwrites
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: 3.9
|
||||
|
||||
@@ -14,6 +19,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Image Build
|
||||
steps:
|
||||
- id: lowercaseRepo
|
||||
uses: ASzc/change-string-case-action@v5
|
||||
with:
|
||||
string: ${{ github.repository }}
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
@@ -38,7 +47,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
target: frigate
|
||||
tags: |
|
||||
ghcr.io/blakeblackshear/frigate:${{ github.ref_name }}-${{ env.SHORT_SHA }}
|
||||
ghcr.io/${{ steps.lowercaseRepo.outputs.lowercase }}:${{ github.ref_name }}-${{ env.SHORT_SHA }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Build and push TensorRT
|
||||
@@ -49,5 +58,5 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
target: frigate-tensorrt
|
||||
tags: |
|
||||
ghcr.io/blakeblackshear/frigate:${{ github.ref_name }}-${{ env.SHORT_SHA }}-tensorrt
|
||||
ghcr.io/${{ steps.lowercaseRepo.outputs.lowercase }}:${{ github.ref_name }}-${{ env.SHORT_SHA }}-tensorrt
|
||||
cache-from: type=gha
|
||||
|
||||
22
.github/workflows/dependabot-auto-merge.yaml
vendored
Normal file
22
.github/workflows/dependabot-auto-merge.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: dependabot-auto-merge
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dependabot-auto-merge:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- name: Get Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Enable auto-merge for Dependabot PRs
|
||||
if: steps.metadata.outputs.dependency-type == 'direct:development' && (steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch')
|
||||
run: gh pr merge --auto --squash "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
2
.github/workflows/pull_request.yml
vendored
2
.github/workflows/pull_request.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v4.4.0
|
||||
uses: actions/setup-python@v4.5.0
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Install requirements
|
||||
|
||||
22
Dockerfile
22
Dockerfile
@@ -27,7 +27,7 @@ RUN --mount=type=tmpfs,target=/tmp --mount=type=tmpfs,target=/var/cache/apt \
|
||||
FROM wget AS go2rtc
|
||||
ARG TARGETARCH
|
||||
WORKDIR /rootfs/usr/local/go2rtc/bin
|
||||
RUN wget -qO go2rtc "https://github.com/AlexxIT/go2rtc/releases/download/v0.1-rc.7/go2rtc_linux_${TARGETARCH}" \
|
||||
RUN wget -qO go2rtc "https://github.com/AlexxIT/go2rtc/releases/download/v1.1.1/go2rtc_linux_${TARGETARCH}" \
|
||||
&& chmod +x go2rtc
|
||||
|
||||
|
||||
@@ -192,22 +192,10 @@ RUN ldconfig
|
||||
EXPOSE 5000
|
||||
EXPOSE 1935
|
||||
EXPOSE 8554
|
||||
EXPOSE 8555
|
||||
EXPOSE 8555/tcp 8555/udp
|
||||
|
||||
# Fails if cont-init.d fails
|
||||
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=2
|
||||
# Wait indefinitely for cont-init.d to finish before starting services
|
||||
ENV S6_CMD_WAIT_FOR_SERVICES=1
|
||||
ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
|
||||
# Give services (including Frigate) 30 seconds to stop before killing them
|
||||
# But this is not working currently because of:
|
||||
# https://github.com/just-containers/s6-overlay/issues/503
|
||||
ENV S6_SERVICES_GRACETIME=30000
|
||||
# Configure logging to prepend timestamps, log to stdout, keep 0 archives and rotate on 10MB
|
||||
ENV S6_LOGGING_SCRIPT="T 1 n0 s10000000 T"
|
||||
# TODO: remove after a new version of s6-overlay is released. See:
|
||||
# https://github.com/just-containers/s6-overlay/issues/460#issuecomment-1327127006
|
||||
ENV S6_SERVICES_READYTIME=50
|
||||
|
||||
ENTRYPOINT ["/init"]
|
||||
CMD []
|
||||
@@ -217,7 +205,7 @@ FROM deps AS devcontainer
|
||||
|
||||
# Do not start the actual Frigate service on devcontainer as it will be started by VSCode
|
||||
# But start a fake service for simulating the logs
|
||||
COPY docker/fake_frigate_run /etc/services.d/frigate/run
|
||||
COPY docker/fake_frigate_run /etc/s6-overlay/s6-rc.d/frigate/run
|
||||
|
||||
# Install Node 16
|
||||
RUN apt-get update \
|
||||
@@ -269,7 +257,9 @@ COPY --from=rootfs / /
|
||||
# Frigate w/ TensorRT Support as separate image
|
||||
FROM frigate AS frigate-tensorrt
|
||||
RUN --mount=type=bind,from=trt-wheels,source=/trt-wheels,target=/deps/trt-wheels \
|
||||
pip3 install -U /deps/trt-wheels/*.whl
|
||||
pip3 install -U /deps/trt-wheels/*.whl && \
|
||||
ln -s libnvrtc.so.11.2 /usr/local/lib/python3.9/dist-packages/nvidia/cuda_nvrtc/lib/libnvrtc.so && \
|
||||
ldconfig
|
||||
|
||||
# Dev Container w/ TRT
|
||||
FROM devcontainer AS devcontainer-trt
|
||||
|
||||
@@ -16,7 +16,8 @@ Use of a [Google Coral Accelerator](https://coral.ai/products/) is optional, but
|
||||
- Communicates over MQTT for easy integration into other systems
|
||||
- Records video with retention settings based on detected objects
|
||||
- 24/7 recording
|
||||
- Re-streaming via RTMP to reduce the number of connections to your camera
|
||||
- Re-streaming via RTSP to reduce the number of connections to your camera
|
||||
- WebRTC & MSE support for low-latency live view
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -25,6 +25,22 @@ tar -zxf ${VOD_MODULE_VERSION}.tar.gz -C /tmp/nginx-vod-module --strip-component
|
||||
rm ${VOD_MODULE_VERSION}.tar.gz
|
||||
# Patch MAX_CLIPS to allow more clips to be added than the default 128
|
||||
sed -i 's/MAX_CLIPS (128)/MAX_CLIPS (1080)/g' /tmp/nginx-vod-module/vod/media_set.h
|
||||
patch -d /tmp/nginx-vod-module/ -p1 << 'EOF'
|
||||
--- a/vod/avc_hevc_parser.c 2022-06-27 11:38:10.000000000 +0000
|
||||
+++ b/vod/avc_hevc_parser.c 2023-01-16 11:25:10.900521298 +0000
|
||||
@@ -3,6 +3,9 @@
|
||||
bool_t
|
||||
avc_hevc_parser_rbsp_trailing_bits(bit_reader_state_t* reader)
|
||||
{
|
||||
+ // https://github.com/blakeblackshear/frigate/issues/4572
|
||||
+ return TRUE;
|
||||
+
|
||||
uint32_t one_bit;
|
||||
|
||||
if (reader->stream.eof_reached)
|
||||
EOF
|
||||
|
||||
|
||||
mkdir /tmp/nginx-secure-token-module
|
||||
wget https://github.com/kaltura/nginx-secure-token-module/archive/refs/tags/${SECURE_TOKEN_MODULE_VERSION}.tar.gz
|
||||
tar -zxf ${SECURE_TOKEN_MODULE_VERSION}.tar.gz -C /tmp/nginx-secure-token-module --strip-components=1
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
# shellcheck shell=bash
|
||||
# Start the fake Frigate service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
# Tell S6-Overlay not to restart this service
|
||||
s6-svc -O .
|
||||
|
||||
while true; do
|
||||
echo "The fake Frigate service is running..."
|
||||
echo "[INFO] The fake Frigate service is running..."
|
||||
sleep 5s
|
||||
done
|
||||
|
||||
@@ -10,7 +10,9 @@ apt-get -qq install --no-install-recommends -y \
|
||||
wget \
|
||||
procps vainfo \
|
||||
unzip locales tzdata libxml2 xz-utils \
|
||||
python3-pip
|
||||
python3-pip \
|
||||
curl \
|
||||
jq
|
||||
|
||||
mkdir -p -m 600 /root/.gnupg
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
s6_version="3.1.2.1"
|
||||
s6_version="3.1.3.0"
|
||||
|
||||
if [[ "${TARGETARCH}" == "amd64" ]]; then
|
||||
s6_arch="x86_64"
|
||||
|
||||
5
docker/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf
Normal file
5
docker/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
/usr/local/lib/python3.9/dist-packages/nvidia/cudnn/lib
|
||||
/usr/local/lib/python3.9/dist-packages/nvidia/cuda_runtime/lib
|
||||
/usr/local/lib/python3.9/dist-packages/nvidia/cublas/lib
|
||||
/usr/local/lib/python3.9/dist-packages/nvidia/cuda_nvrtc/lib
|
||||
/usr/local/lib/python3.9/dist-packages/tensorrt
|
||||
@@ -0,0 +1 @@
|
||||
frigate
|
||||
@@ -0,0 +1 @@
|
||||
frigate-pipeline
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate-log/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate-log/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
30
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/finish
Executable file
30
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/finish
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service exits
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
declare exit_code_container
|
||||
exit_code_container=$(cat /run/s6-linux-init-container-results/exitcode)
|
||||
readonly exit_code_container
|
||||
readonly exit_code_service="${1}"
|
||||
readonly exit_code_signal="${2}"
|
||||
readonly service="Frigate"
|
||||
|
||||
echo "Service ${service} exited with code ${exit_code_service} (by signal ${exit_code_signal})" >&2
|
||||
|
||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo $((128 + exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
else
|
||||
# Exit code 0 is expected when Frigate is restarted by the user. In this case,
|
||||
# we create a signal for the go2rtc finish script to tolerate the restart.
|
||||
touch /dev/shm/restarting-frigate
|
||||
fi
|
||||
|
||||
exec /run/s6/basedir/bin/halt
|
||||
@@ -0,0 +1 @@
|
||||
frigate-log
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
cd /opt/frigate
|
||||
# Tell S6-Overlay not to restart this service
|
||||
s6-svc -O .
|
||||
|
||||
echo "[INFO] Starting Frigate..." >&2
|
||||
|
||||
cd /opt/frigate || echo "[ERROR] Failed to change working directory to /opt/frigate" >&2
|
||||
|
||||
# Replace the bash process with the Frigate process, redirecting stderr to stdout
|
||||
exec 2>&1
|
||||
@@ -0,0 +1 @@
|
||||
120000
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
@@ -0,0 +1 @@
|
||||
go2rtc
|
||||
@@ -0,0 +1 @@
|
||||
go2rtc-pipeline
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc-log/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc-log/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
32
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/finish
Executable file
32
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/finish
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service exits
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
declare exit_code_container
|
||||
exit_code_container=$(cat /run/s6-linux-init-container-results/exitcode)
|
||||
readonly exit_code_container
|
||||
readonly exit_code_service="${1}"
|
||||
readonly exit_code_signal="${2}"
|
||||
readonly service="go2rtc"
|
||||
|
||||
echo "Service ${service} exited with code ${exit_code_service} (by signal ${exit_code_signal})" >&2
|
||||
|
||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo $((128 + exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
else
|
||||
# go2rtc is not supposed to exit, so even when it exits with 0 we make the
|
||||
# container with 1. We only tolerate it when Frigate is restarting.
|
||||
if [[ "${exit_code_container}" -eq 0 && ! -f /dev/shm/restarting-frigate ]]; then
|
||||
echo "1" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
fi
|
||||
|
||||
exec /run/s6/basedir/bin/halt
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/producer-for
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/producer-for
Normal file
@@ -0,0 +1 @@
|
||||
go2rtc-log
|
||||
60
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run
Executable file
60
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Start the go2rtc service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
# Tell S6-Overlay not to restart this service
|
||||
s6-svc -O .
|
||||
|
||||
function get_ip_and_port_from_supervisor() {
|
||||
local ip_address
|
||||
# Example: 192.168.1.10/24
|
||||
local ip_regex='^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/[0-9]{1,2}$'
|
||||
if ip_address=$(
|
||||
curl -fsSL \
|
||||
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
http://supervisor/network/interface/default/info |
|
||||
jq --exit-status --raw-output '.data.ipv4.address[0]'
|
||||
) && [[ "${ip_address}" =~ ${ip_regex} ]]; then
|
||||
ip_address="${BASH_REMATCH[1]}"
|
||||
echo "[INFO] Got IP address from supervisor: ${ip_address}" >&2
|
||||
else
|
||||
echo "[WARN] Failed to get IP address from supervisor" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
local webrtc_port
|
||||
local port_regex='^([0-9]{1,5})$'
|
||||
if webrtc_port=$(
|
||||
curl -fsSL \
|
||||
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
http://supervisor/addons/self/info |
|
||||
jq --exit-status --raw-output '.data.network["8555/tcp"]'
|
||||
) && [[ "${webrtc_port}" =~ ${port_regex} ]]; then
|
||||
webrtc_port="${BASH_REMATCH[1]}"
|
||||
echo "[INFO] Got WebRTC port from supervisor: ${ip_address}" >&2
|
||||
else
|
||||
echo "[WARN] Failed to get WebRTC port from supervisor" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
export FRIGATE_GO2RTC_WEBRTC_CANDIDATE_INTERNAL="${ip_address}:${webrtc_port}"
|
||||
}
|
||||
|
||||
echo "[INFO] Preparing go2rtc config..." >&2
|
||||
|
||||
if [[ -n "${SUPERVISOR_TOKEN:-}" ]]; then
|
||||
# Running as a Home Assistant add-on, infer the IP address and port
|
||||
get_ip_and_port_from_supervisor
|
||||
fi
|
||||
|
||||
raw_config=$(python3 /usr/local/go2rtc/create_config.py)
|
||||
|
||||
echo "[INFO] Starting go2rtc..." >&2
|
||||
|
||||
# Replace the bash process with the go2rtc process, redirecting stderr to stdout
|
||||
exec 2>&1
|
||||
exec go2rtc -config="${raw_config}"
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/timeout-kill
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/timeout-kill
Normal file
@@ -0,0 +1 @@
|
||||
30000
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/type
Normal file
@@ -0,0 +1 @@
|
||||
oneshot
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/up
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/up
Normal file
@@ -0,0 +1 @@
|
||||
/etc/s6-overlay/s6-rc.d/log-prepare/run
|
||||
@@ -0,0 +1 @@
|
||||
nginx
|
||||
@@ -0,0 +1 @@
|
||||
nginx-pipeline
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx-log/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx-log/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
28
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish
Executable file
28
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service fails
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
declare exit_code_container
|
||||
exit_code_container=$(cat /run/s6-linux-init-container-results/exitcode)
|
||||
readonly exit_code_container
|
||||
readonly exit_code_service="${1}"
|
||||
readonly exit_code_signal="${2}"
|
||||
readonly service="NGINX"
|
||||
|
||||
echo "Service ${service} exited with code ${exit_code_service} (by signal ${exit_code_signal})" >&2
|
||||
|
||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo $((128 + exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
if [[ "${exit_code_signal}" -eq 15 ]]; then
|
||||
exec /run/s6/basedir/bin/halt
|
||||
fi
|
||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
exec /run/s6/basedir/bin/halt
|
||||
fi
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/producer-for
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/producer-for
Normal file
@@ -0,0 +1 @@
|
||||
nginx-log
|
||||
@@ -2,6 +2,10 @@
|
||||
# shellcheck shell=bash
|
||||
# Start the NGINX service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
echo "[INFO] Starting NGINX..." >&2
|
||||
|
||||
# Replace the bash process with the NGINX process, redirecting stderr to stdout
|
||||
exec 2>&1
|
||||
exec nginx
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/timeout-kill
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/timeout-kill
Normal file
@@ -0,0 +1 @@
|
||||
30000
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service exits
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
# Prepare exit code
|
||||
if [[ "${1}" -eq 256 ]]; then
|
||||
exit_code="$((128 + ${2}))"
|
||||
else
|
||||
exit_code="${1}"
|
||||
fi
|
||||
|
||||
# Make the container exit with the same exit code as the service
|
||||
echo "${exit_code}" > /run/s6-linux-init-container-results/exitcode
|
||||
exec /run/s6/basedir/bin/halt
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service fails, or restart it
|
||||
# otherwise
|
||||
|
||||
if [[ "${1}" -ne 0 && "${1}" -ne 256 ]]; then
|
||||
exec /run/s6/basedir/bin/halt
|
||||
fi
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Start the go2rtc service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
if [[ -f "/config/frigate-go2rtc.yaml" ]]; then
|
||||
config_path="/config/frigate-go2rtc.yaml"
|
||||
else
|
||||
config_path="/usr/local/go2rtc/go2rtc.yaml"
|
||||
fi
|
||||
|
||||
# Replace the bash process with the go2rtc process, redirecting stderr to stdout
|
||||
exec 2>&1
|
||||
exec go2rtc -config="${config_path}"
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service fails, or restart it
|
||||
# otherwise
|
||||
|
||||
if [[ "${1}" -ne 0 && "${1}" -ne 256 ]]; then
|
||||
exec /run/s6/basedir/bin/halt
|
||||
fi
|
||||
75
docker/rootfs/usr/local/go2rtc/create_config.py
Normal file
75
docker/rootfs/usr/local/go2rtc/create_config.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""Creates a go2rtc config file."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
|
||||
BTBN_PATH = "/usr/lib/btbn-ffmpeg"
|
||||
FRIGATE_ENV_VARS = {k: v for k, v in os.environ.items() if k.startswith("FRIGATE_")}
|
||||
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
|
||||
|
||||
# Check if we can use .yaml instead of .yml
|
||||
config_file_yaml = config_file.replace(".yml", ".yaml")
|
||||
if os.path.isfile(config_file_yaml):
|
||||
config_file = config_file_yaml
|
||||
|
||||
with open(config_file) as f:
|
||||
raw_config = f.read()
|
||||
|
||||
if config_file.endswith((".yaml", ".yml")):
|
||||
config = yaml.safe_load(raw_config)
|
||||
elif config_file.endswith(".json"):
|
||||
config = json.loads(raw_config)
|
||||
|
||||
go2rtc_config: dict[str, any] = config.get("go2rtc", {})
|
||||
|
||||
# we want to ensure that logs are easy to read
|
||||
if go2rtc_config.get("log") is None:
|
||||
go2rtc_config["log"] = {"format": "text"}
|
||||
elif go2rtc_config["log"].get("format") is None:
|
||||
go2rtc_config["log"]["format"] = "text"
|
||||
|
||||
if not go2rtc_config.get("webrtc", {}).get("candidates", []):
|
||||
default_candidates = []
|
||||
# use internal candidate if it was discovered when running through the add-on
|
||||
internal_candidate = os.environ.get("FRIGATE_GO2RTC_WEBRTC_CANDIDATE_INTERNAL", None)
|
||||
if internal_candidate is not None:
|
||||
default_candidates.append(internal_candidate)
|
||||
# should set default stun server so webrtc can work
|
||||
default_candidates.append("stun:8555")
|
||||
|
||||
go2rtc_config["webrtc"] = {"candidates": default_candidates}
|
||||
else:
|
||||
print("[INFO] Not injecting WebRTC candidates into go2rtc config as it has been set manually", file=sys.stderr)
|
||||
|
||||
# sets default RTSP response to be equivalent to ?video=h264,h265&audio=aac
|
||||
# this means user does not need to specify audio codec when using restream
|
||||
# as source for frigate and the integration supports HLS playback
|
||||
if go2rtc_config.get("rtsp") is None:
|
||||
go2rtc_config["rtsp"] = {"default_query": "mp4"}
|
||||
elif go2rtc_config["rtsp"].get("default_query") is None:
|
||||
go2rtc_config["rtsp"]["default_query"] = "mp4"
|
||||
|
||||
# need to replace ffmpeg command when using ffmpeg4
|
||||
if not os.path.exists(BTBN_PATH):
|
||||
if go2rtc_config.get("ffmpeg") is None:
|
||||
go2rtc_config["ffmpeg"] = {
|
||||
"rtsp": "-fflags nobuffer -flags low_delay -stimeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_transport tcp -i {input}"
|
||||
}
|
||||
elif go2rtc_config["ffmpeg"].get("rtsp") is None:
|
||||
go2rtc_config["ffmpeg"][
|
||||
"rtsp"
|
||||
] = "-fflags nobuffer -flags low_delay -stimeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_transport tcp -i {input}"
|
||||
|
||||
for name in go2rtc_config.get("streams", {}):
|
||||
stream = go2rtc_config["streams"][name]
|
||||
|
||||
if isinstance(stream, str):
|
||||
go2rtc_config["streams"][name] = go2rtc_config["streams"][name].format(**FRIGATE_ENV_VARS)
|
||||
elif isinstance(stream, list):
|
||||
for i, stream in enumerate(stream):
|
||||
go2rtc_config["streams"][name][i] = stream.format(**FRIGATE_ENV_VARS)
|
||||
|
||||
print(json.dumps(go2rtc_config))
|
||||
@@ -1,7 +0,0 @@
|
||||
log:
|
||||
format: text
|
||||
|
||||
webrtc:
|
||||
listen: ":8555"
|
||||
candidates:
|
||||
- stun:8555
|
||||
@@ -105,6 +105,6 @@ To do this:
|
||||
|
||||
1. Download your ffmpeg build and uncompress to a folder on the host (let's use `/home/appdata/frigate/custom-ffmpeg` for this example).
|
||||
2. Update your docker-compose or docker CLI to include `'/home/appdata/frigate/custom-ffmpeg':'/usr/lib/btbn-ffmpeg':'ro'` in the volume mappings.
|
||||
3. Restart frigate and the custom version will be used if the mapping was done correctly.
|
||||
3. Restart Frigate and the custom version will be used if the mapping was done correctly.
|
||||
|
||||
NOTE: The folder that is mapped from the host needs to be the folder that contains `/bin`. So if the full structure is `/home/appdata/frigate/custom-ffmpeg/bin/ffmpeg` then `/home/appdata/frigate/custom-ffmpeg` needs to be mapped to `/usr/lib/btbn-ffmpeg`.
|
||||
|
||||
@@ -14,19 +14,19 @@ This page makes use of presets of FFmpeg args. For more information on presets,
|
||||
Note that mjpeg cameras require encoding the video into h264 for recording, and restream roles. This will use significantly more CPU than if the cameras supported h264 feeds directly. It is recommended to use the restream role to create an h264 restream and then use that as the source for ffmpeg.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
mjpeg_cam: ffmpeg:{your_mjpeg_stream_url}#video=h264#hardware # <- use hardware acceleration to create an h264 stream usable for other components.
|
||||
|
||||
cameras:
|
||||
...
|
||||
mjpeg_cam:
|
||||
ffmpeg:
|
||||
inputs:
|
||||
- path: rtsp://localhost:8554/mjpeg_cam
|
||||
- path: rtsp://127.0.0.1:8554/mjpeg_cam
|
||||
roles:
|
||||
- detect
|
||||
- record
|
||||
- path: {your_mjpeg_stream_url}
|
||||
roles:
|
||||
- restream
|
||||
restream:
|
||||
enabled: true
|
||||
video_encoding: h264
|
||||
```
|
||||
|
||||
## JPEG Stream Cameras
|
||||
@@ -106,16 +106,24 @@ If available, recommended settings are:
|
||||
According to [this discussion](https://github.com/blakeblackshear/frigate/issues/3235#issuecomment-1135876973), the http video streams seem to be the most reliable for Reolink.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
reolink:
|
||||
- http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=username&password=password
|
||||
- ffmpeg:reolink#audio=opus
|
||||
reolink_sub:
|
||||
- http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=username&password=password
|
||||
|
||||
cameras:
|
||||
reolink:
|
||||
ffmpeg:
|
||||
input_args: preset-http-reolink
|
||||
inputs:
|
||||
- path: http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=username&password=password
|
||||
- path: rtsp://127.0.0.1:8554/reolink?video=copy&audio=aac
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- record
|
||||
- rtmp
|
||||
- path: http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=username&password=password
|
||||
- path: rtsp://127.0.0.1:8554/reolink_sub?video=copy
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- detect
|
||||
detect:
|
||||
|
||||
@@ -15,7 +15,6 @@ Each role can only be assigned to one input per camera. The options for roles ar
|
||||
| ---------- | ---------------------------------------------------------------------------------------- |
|
||||
| `detect` | Main feed for object detection |
|
||||
| `record` | Saves segments of the video feed based on configuration settings. [docs](record.md) |
|
||||
| `restream` | Broadcast as RTSP feed and use the full res stream for live view. [docs](restream.md) |
|
||||
| `rtmp` | Deprecated: Broadcast as an RTMP feed for other services to consume. [docs](restream.md) |
|
||||
|
||||
```yaml
|
||||
@@ -29,7 +28,7 @@ cameras:
|
||||
- path: rtsp://viewer:{FRIGATE_RTSP_PASSWORD}@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
|
||||
roles:
|
||||
- detect
|
||||
- rtmp
|
||||
- rtmp # <- deprecated, recommend using restream instead
|
||||
- path: rtsp://viewer:{FRIGATE_RTSP_PASSWORD}@10.0.10.10:554/live
|
||||
roles:
|
||||
- record
|
||||
|
||||
@@ -184,7 +184,7 @@ chmod +x tensorrt_models.sh
|
||||
docker run --gpus=all --rm -it -v `pwd`/trt-models:/tensorrt_models -v `pwd`/tensorrt_models.sh:/tensorrt_models.sh nvcr.io/nvidia/tensorrt:22.07-py3 /tensorrt_models.sh
|
||||
```
|
||||
|
||||
The `trt-models` folder can then be mapped into your frigate container as `trt-models` and the models referenced from the config.
|
||||
The `trt-models` folder can then be mapped into your Frigate container as `trt-models` and the models referenced from the config.
|
||||
|
||||
If your GPU does not support FP16 operations, you can pass the environment variable `-e USE_FP16=False` to the `docker run` command to disable it.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Some presets of FFmpeg args are provided by default to make the configuration ea
|
||||
|
||||
### Hwaccel Presets
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
See [the hwaccel docs](/configuration/hardware_acceleration.md) for more info on how to setup hwaccel for your GPU / iGPU.
|
||||
|
||||
@@ -26,15 +26,16 @@ See [the hwaccel docs](/configuration/hardware_acceleration.md) for more info on
|
||||
|
||||
Input args presets help make the config more readable and handle use cases for different types of streams to ensure maximum compatibility.
|
||||
|
||||
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 info 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 when using rtsp restream as source |
|
||||
| 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 |
|
||||
|
||||
@@ -67,9 +68,10 @@ cameras:
|
||||
Output args presets help make the config more readable and handle use cases for different types of streams to ensure consistent recordings.
|
||||
|
||||
| Preset | Usage | Other Notes |
|
||||
| --------------------------- | --------------------------------- | --------------------------------------------- |
|
||||
| -------------------------------- | --------------------------------- | --------------------------------------------- |
|
||||
| preset-record-generic | Record WITHOUT audio | This is the default when nothing is specified |
|
||||
| preset-record-generic-audio | Record WITH audio | Use this to enable audio in recordings |
|
||||
| preset-record-generic-audio-aac | Record WITH aac audio | Use this to enable audio in recordings |
|
||||
| preset-record-generic-audio-copy | Record WITH original audio | Use this to enable audio in recordings |
|
||||
| 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 |
|
||||
|
||||
@@ -7,12 +7,6 @@ It is recommended to update your configuration to enable hardware accelerated de
|
||||
|
||||
### Raspberry Pi 3/4
|
||||
|
||||
:::caution
|
||||
|
||||
There is currently a bug in ffmpeg that causes hwaccel to not work for the RPi kernel 5.15.61 and above. For more information see https://github.com/blakeblackshear/frigate/issues/3780
|
||||
|
||||
:::
|
||||
|
||||
Ensure you increase the allocated RAM for your GPU to at least 128 (raspi-config > Performance Options > GPU Memory).
|
||||
**NOTICE**: If you are using the addon, you may need to turn off `Protection mode` for hardware acceleration.
|
||||
|
||||
@@ -45,19 +39,25 @@ ffmpeg:
|
||||
hwaccel_args: preset-vaapi
|
||||
```
|
||||
|
||||
### NVIDIA GPU
|
||||
### NVIDIA GPUs
|
||||
|
||||
[Supported Nvidia GPUs for Decoding](https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new)
|
||||
While older GPUs may work, it is recommended to use modern, supported GPUs. NVIDIA provides a [matrix of supported GPUs and features](https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new). If your card is on the list and supports CUVID/NVDEC, it will most likely work with Frigate for decoding. However, you must also use [a driver version that will work with FFmpeg](https://github.com/FFmpeg/nv-codec-headers/blob/master/README). Older driver versions may be missing symbols and fail to work, and older cards are not supported by newer driver versions. The only way around this is to [provide your own FFmpeg](/configuration/advanced#custom-ffmpeg-build) that will work with your driver version, but this is unsupported and may not work well if at all.
|
||||
|
||||
These instructions are based on the [jellyfin documentation](https://jellyfin.org/docs/general/administration/hardware-acceleration.html#nvidia-hardware-acceleration-on-docker-linux)
|
||||
A more complete list of cards and ther compatible drivers is available in the [driver release readme](https://download.nvidia.com/XFree86/Linux-x86_64/525.85.05/README/supportedchips.html).
|
||||
|
||||
If your distribution does not offer NVIDIA driver packages, you can [download them here](https://www.nvidia.com/en-us/drivers/unix/).
|
||||
|
||||
#### Docker Configuration
|
||||
|
||||
Additional configuration is needed for the Docker container to be able to access the NVIDIA GPU. The supported method for this is to install the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#docker) and specify the GPU to Docker. How you do this depends on how Docker is being run:
|
||||
|
||||
##### Docker Compose
|
||||
|
||||
Add `--gpus all` to your docker run command or update your compose file.
|
||||
If you have multiple Nvidia graphic card, you can add them with their ids obtained via `nvidia-smi` command
|
||||
```yaml
|
||||
services:
|
||||
frigate:
|
||||
...
|
||||
image: blakeblackshear/frigate:stable
|
||||
image: ghcr.io/blakeblackshear/frigate:stable
|
||||
deploy: # <------------- Add this section
|
||||
resources:
|
||||
reservations:
|
||||
@@ -68,11 +68,23 @@ services:
|
||||
capabilities: [gpu]
|
||||
```
|
||||
|
||||
##### Docker Run CLI
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name frigate \
|
||||
...
|
||||
--gpus=all \
|
||||
ghcr.io/blakeblackshear/frigate:stable
|
||||
```
|
||||
|
||||
#### Setup Decoder
|
||||
|
||||
The decoder you need to pass in the `hwaccel_args` will depend on the input video.
|
||||
|
||||
A list of supported codecs (you can use `ffmpeg -decoders | grep cuvid` in the container to get a list)
|
||||
A list of supported codecs (you can use `ffmpeg -decoders | grep cuvid` in the container to get the ones your card supports)
|
||||
|
||||
```shell
|
||||
```
|
||||
V..... h263_cuvid Nvidia CUVID H263 decoder (codec h263)
|
||||
V..... h264_cuvid Nvidia CUVID H264 decoder (codec h264)
|
||||
V..... hevc_cuvid Nvidia CUVID HEVC decoder (codec hevc)
|
||||
@@ -93,12 +105,12 @@ ffmpeg:
|
||||
```
|
||||
|
||||
If everything is working correctly, you should see a significant improvement in performance.
|
||||
Verify that hardware decoding is working by running `nvidia-smi`, which should show the ffmpeg
|
||||
Verify that hardware decoding is working by running `nvidia-smi`, which should show `ffmpeg`
|
||||
processes:
|
||||
|
||||
:::note
|
||||
|
||||
nvidia-smi may not show ffmpeg processes when run inside the container [due to docker limitations](https://github.com/NVIDIA/nvidia-docker/issues/179#issuecomment-645579458)
|
||||
`nvidia-smi` may not show `ffmpeg` processes when run inside the container [due to docker limitations](https://github.com/NVIDIA/nvidia-docker/issues/179#issuecomment-645579458).
|
||||
|
||||
:::
|
||||
|
||||
@@ -129,3 +141,7 @@ nvidia-smi may not show ffmpeg processes when run inside the container [due to d
|
||||
| 0 N/A N/A 12827 C ffmpeg 417MiB |
|
||||
+-----------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
If you do not see these processes, check the `docker logs` for the container and look for decoding errors.
|
||||
|
||||
These instructions were originally based on the [Jellyfin documentation](https://jellyfin.org/docs/general/administration/hardware-acceleration.html#nvidia-hardware-acceleration-on-docker-linux).
|
||||
|
||||
@@ -27,7 +27,7 @@ cameras:
|
||||
|
||||
### VSCode Configuration Schema
|
||||
|
||||
VSCode (and VSCode addon) supports the JSON schemas which will automatically validate the config. This can be added by adding `# yaml-language-server: $schema=http://frigate_host:5000/api/config/schema.json` to the top of the config file. `frigate_host` being the IP address of frigate or `ccab4aaf-frigate` if running in the addon.
|
||||
VSCode (and VSCode addon) supports the JSON schemas which will automatically validate the config. This can be added by adding `# yaml-language-server: $schema=http://frigate_host:5000/api/config/schema.json` to the top of the config file. `frigate_host` being the IP address of Frigate or `ccab4aaf-frigate` if running in the addon.
|
||||
|
||||
### Full configuration reference:
|
||||
|
||||
@@ -126,6 +126,9 @@ environment_vars:
|
||||
birdseye:
|
||||
# Optional: Enable birdseye view (default: shown below)
|
||||
enabled: True
|
||||
# Optional: Restream birdseye via RTSP (default: shown below)
|
||||
# NOTE: Enabling this will set birdseye to run 24/7 which may increase CPU usage somewhat.
|
||||
restream: False
|
||||
# Optional: Width of the output resolution (default: shown below)
|
||||
width: 1280
|
||||
# Optional: Height of the output resolution (default: shown below)
|
||||
@@ -171,7 +174,7 @@ detect:
|
||||
# Optional: enables detection for the camera (default: True)
|
||||
# This value can be set via MQTT and will be updated in startup based on retained value
|
||||
enabled: True
|
||||
# Optional: Number of frames without a detection before frigate considers an object to be gone. (default: 5x the frame rate)
|
||||
# Optional: Number of frames without a detection before Frigate considers an object to be gone. (default: 5x the frame rate)
|
||||
max_disappeared: 25
|
||||
# Optional: Configuration for stationary object tracking
|
||||
stationary:
|
||||
@@ -268,11 +271,6 @@ record:
|
||||
# Optional: Enable recording (default: shown below)
|
||||
# WARNING: If recording is disabled in the config, turning it on via
|
||||
# the UI or MQTT later will have no effect.
|
||||
# WARNING: Frigate does not currently support limiting recordings based
|
||||
# on available disk space automatically. If using recordings,
|
||||
# you must specify retention settings for a number of days that
|
||||
# will fit within the available disk space of your drive or Frigate
|
||||
# will crash.
|
||||
enabled: False
|
||||
# Optional: Number of minutes to wait between cleanup runs (default: shown below)
|
||||
# This can be used to reduce the frequency of deleting recording segments from disk if you want to minimize i/o
|
||||
@@ -352,21 +350,14 @@ rtmp:
|
||||
enabled: False
|
||||
|
||||
# Optional: Restream configuration
|
||||
# NOTE: Can be overridden at the camera level
|
||||
restream:
|
||||
# Optional: Enable the restream (default: True)
|
||||
enabled: True
|
||||
# Optional: Force audio compatibility with browsers (default: shown below)
|
||||
force_audio: True
|
||||
# Optional: Video encoding to be used. By default the codec will be copied but
|
||||
# it can be switched to another or an MJPEG stream can be encoded and restreamed
|
||||
# as h264 (default: shown below)
|
||||
video_encoding: "copy"
|
||||
# Optional: Restream birdseye via RTSP (default: shown below)
|
||||
# NOTE: Enabling this will set birdseye to run 24/7 which may increase CPU usage somewhat.
|
||||
birdseye: False
|
||||
# Optional: jsmpeg stream configuration for WebUI
|
||||
jsmpeg:
|
||||
# Uses https://github.com/AlexxIT/go2rtc (v1.1.1)
|
||||
go2rtc:
|
||||
|
||||
# Optional: jsmpeg stream configuration for WebUI
|
||||
live:
|
||||
# Optional: Set the name of the stream that should be used for live view
|
||||
# in frigate WebUI. (default: name of camera)
|
||||
stream_name: camera_name
|
||||
# Optional: Set the height of the jsmpeg stream. (default: 720)
|
||||
# This must be less than or equal to the height of the detect stream. Lower resolutions
|
||||
# reduce bandwidth required for viewing the jsmpeg stream. Width is computed to match known aspect ratio.
|
||||
@@ -486,4 +477,19 @@ cameras:
|
||||
order: 0
|
||||
# Optional: Whether or not to show the camera in the Frigate UI (default: shown below)
|
||||
dashboard: True
|
||||
|
||||
# Optional
|
||||
ui:
|
||||
# Optional: Set the default live mode for cameras in the UI (default: shown below)
|
||||
live_mode: mse
|
||||
# Optional: Set a timezone to use in the UI (default: use browser local time)
|
||||
timezone: None
|
||||
# Optional: Use an experimental recordings / camera view UI (default: shown below)
|
||||
experimental_ui: False
|
||||
|
||||
# Optional: Telemetry configuration
|
||||
telemetry:
|
||||
# Optional: Enable the latest version outbound check (default: shown below)
|
||||
# NOTE: If you use the HomeAssistant integration, disabling this will prevent it from reporting new versions
|
||||
version_check: True
|
||||
```
|
||||
|
||||
@@ -3,42 +3,102 @@ id: live
|
||||
title: Live View
|
||||
---
|
||||
|
||||
Frigate has different live view options, some of which require [restream](restream.md) to be enabled.
|
||||
Frigate has different live view options, some of which require the bundled `go2rtc` to be configured as shown in the [step by step guide](/guides/configuring_go2rtc).
|
||||
|
||||
## Live View Options
|
||||
|
||||
Live view options can be selected while viewing the live stream. The options are:
|
||||
|
||||
| Source | Latency | Frame Rate | Resolution | Audio | Requires Restream | Other Limitations |
|
||||
| ------ | ------- | -------------------------------------- | -------------- | ---------------------------- | ----------------- | -------------------------------- |
|
||||
| Source | Latency | Frame Rate | Resolution | Audio | Requires go2rtc | Other Limitations |
|
||||
| ------ | ------- | ------------------------------------- | -------------- | ---------------------------- | --------------- | -------------------------------------------- |
|
||||
| jsmpeg | low | same as `detect -> fps`, capped at 10 | same as detect | no | no | none |
|
||||
| mse | low | native | native | yes (depends on audio codec) | yes | not supported on iOS or Firefox |
|
||||
| webrtc | lowest | native | native | yes (depends on audio codec) | yes | requires extra config |
|
||||
| mse | low | native | native | yes (depends on audio codec) | yes | not supported on iOS, Firefox is h.264 only |
|
||||
| webrtc | lowest | native | native | yes (depends on audio codec) | yes | requires extra config, doesn't support h.265 |
|
||||
|
||||
### Audio Support
|
||||
|
||||
MSE Requires AAC audio, WebRTC requires PCMU/PCMA, or opus audio. If you want to support both MSE and WebRTC then your restream config needs to make sure both are enabled.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
rtsp_cam: # <- for RTSP streams
|
||||
- rtsp://192.168.1.5:554/live0 # <- stream which supports video & aac audio
|
||||
- "ffmpeg:rtsp_cam#audio=opus" # <- copy of the stream which transcodes audio to the missing codec (usually will be opus)
|
||||
http_cam: # <- for http streams
|
||||
- http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=user&password=password # <- stream which supports video & aac audio
|
||||
- "ffmpeg:http_cam#audio=opus" # <- copy of the stream which transcodes audio to the missing codec (usually will be opus)
|
||||
```
|
||||
|
||||
### Setting Stream For Live UI
|
||||
|
||||
There may be some cameras that you would prefer to use the sub stream for live view, but the main stream for recording. This can be done via `live -> stream_name`.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
rtsp_cam:
|
||||
- rtsp://192.168.1.5:554/live0 # <- stream which supports video & aac audio.
|
||||
- "ffmpeg:rtsp_cam#audio=opus" # <- copy of the stream which transcodes audio to opus
|
||||
rtsp_cam_sub:
|
||||
- rtsp://192.168.1.5:554/substream # <- stream which supports video & aac audio.
|
||||
- "ffmpeg:rtsp_cam_sub#audio=opus" # <- copy of the stream which transcodes audio to opus
|
||||
|
||||
cameras:
|
||||
test_cam:
|
||||
ffmpeg:
|
||||
output_args:
|
||||
record: preset-record-generic-audio-copy
|
||||
inputs:
|
||||
- path: rtsp://127.0.0.1:8554/test_cam # <--- the name here must match the name of the camera in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- record
|
||||
- path: rtsp://127.0.0.1:8554/test_cam_sub # <--- the name here must match the name of the camera_sub in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- detect
|
||||
live:
|
||||
stream_name: test_cam_sub
|
||||
```
|
||||
|
||||
### WebRTC extra configuration:
|
||||
|
||||
webRTC works by creating a websocket connection on extra ports. One of the following is required for webRTC to work:
|
||||
* Frigate is run with `network_mode: host` to support automatic UDP port pass through locally and remotely. See https://github.com/AlexxIT/go2rtc#module-webrtc for more details
|
||||
* Frigate is run with `network_mode: bridge` and has:
|
||||
* Router setup to forward port `8555` to port `8555` on the frigate device.
|
||||
* For local webRTC, you will need to create your own go2rtc config:
|
||||
WebRTC works by creating a TCP or UDP connection on port `8555`. However, it requires additional configuration:
|
||||
|
||||
```yaml
|
||||
log:
|
||||
format: text
|
||||
- For external access, over the internet, setup your router to forward port `8555` to port `8555` on the Frigate device, for both TCP and UDP.
|
||||
- For internal/local access, unless you are running through the add-on, you will also need to set the WebRTC candidates list in the go2rtc config. For example, if `192.168.1.10` is the local IP of the device running Frigate:
|
||||
|
||||
webrtc:
|
||||
listen: ":8555"
|
||||
```yaml title="/config/frigate.yaml"
|
||||
go2rtc:
|
||||
streams:
|
||||
test_cam: ...
|
||||
webrtc:
|
||||
candidates:
|
||||
- <frigate host ip address>:8555 # <--- enter frigate host IP here
|
||||
- 192.168.1.10:8555
|
||||
- stun:8555
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
This extra configuration may not be required if Frigate has been installed as a Home Assistant add-on, as Frigate uses the Supervisor's API to generate a WebRTC candidate.
|
||||
|
||||
However, it is recommended if issues occur to define the candidates manually. You should do this if the Frigate add-on fails to generate a valid candidate. If an error occurs you will see some warnings like the below in the add-on logs page during the initialization:
|
||||
|
||||
```log
|
||||
[WARN] Failed to get IP address from supervisor
|
||||
[WARN] Failed to get WebRTC port from supervisor
|
||||
```
|
||||
|
||||
and pass that config to frigate via docker or `frigate-go2rtc.yaml` for addon users:
|
||||
:::
|
||||
|
||||
See https://github.com/AlexxIT/go2rtc#module-webrtc for more details
|
||||
:::note
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /path/to/your/go2rtc.yaml:/config/frigate-go2rtc.yaml:ro
|
||||
```
|
||||
If you are having difficulties getting WebRTC to work and you are running Frigate with docker, you may want to try changing the container network mode:
|
||||
|
||||
- `network: host`, in this mode you don't need to forward any ports. The services inside of the Frigate container will have full access to the network interfaces of your host machine as if they were running natively and not in a container. Any port conflicts will need to be resolved. This network mode is recommended by go2rtc, but we recommend you only use it if necessary.
|
||||
- `network: bridge` creates a virtual network interface for the container, and the container will have full access to it. You also don't need to forward any ports, however, the IP for accessing Frigate locally will differ from the IP of the host machine. Your router will see Frigate as if it was a new device connected in the network.
|
||||
|
||||
:::
|
||||
|
||||
See https://github.com/AlexxIT/go2rtc#module-webrtc for more information about this.
|
||||
|
||||
@@ -7,7 +7,7 @@ There are two types of masks available:
|
||||
|
||||
**Motion masks**: Motion masks are used to prevent unwanted types of motion from triggering detection. Try watching the debug feed with `Motion Boxes` enabled to see what may be regularly detected as motion. For example, you want to mask out your timestamp, the sky, rooftops, etc. Keep in mind that this mask only prevents motion from being detected and does not prevent objects from being detected if object detection was started due to motion in unmasked areas. Motion is also used during object tracking to refine the object detection area in the next frame. Over masking will make it more difficult for objects to be tracked. To see this effect, create a mask, and then watch the video feed with `Motion Boxes` enabled again.
|
||||
|
||||
**Object filter masks**: Object filter masks are used to filter out false positives for a given object type based on location. These should be used to filter any areas where it is not possible for an object of that type to be. The bottom center of the detected object's bounding box is evaluated against the mask. If it is in a masked area, it is assumed to be a false positive. For example, you may want to mask out rooftops, walls, the sky, treetops for people. For cars, masking locations other than the street or your driveway will tell frigate that anything in your yard is a false positive.
|
||||
**Object filter masks**: Object filter masks are used to filter out false positives for a given object type based on location. These should be used to filter any areas where it is not possible for an object of that type to be. The bottom center of the detected object's bounding box is evaluated against the mask. If it is in a masked area, it is assumed to be a false positive. For example, you may want to mask out rooftops, walls, the sky, treetops for people. For cars, masking locations other than the street or your driveway will tell Frigate that anything in your yard is a false positive.
|
||||
|
||||
To create a poly mask:
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ Using Frigate UI, HomeAssistant, or MQTT, cameras can be automated to only recor
|
||||
|
||||
Frigate saves from the stream with the `record` role in 10 second segments. These options determine which recording segments are kept for 24/7 recording (but can also affect events).
|
||||
|
||||
Let's say you have frigate configured so that your doorbell camera would retain the last **2** days of 24/7 recording.
|
||||
Let's say you have Frigate configured so that your doorbell camera would retain the last **2** days of 24/7 recording.
|
||||
- With the `all` option all 48 hours of those two days would be kept and viewable.
|
||||
- With the `motion` option the only parts of those 48 hours would be segments that frigate detected motion. This is the middle ground option that won't keep all 48 hours, but will likely keep all segments of interest along with the potential for some extra segments.
|
||||
- With the `motion` option the only parts of those 48 hours would be segments that Frigate detected motion. This is the middle ground option that won't keep all 48 hours, but will likely keep all segments of interest along with the potential for some extra segments.
|
||||
- With the `active_objects` option the only segments that would be kept are those where there was a true positive object that was not considered stationary.
|
||||
|
||||
The same options are available with events. Let's consider a scenario where you drive up and park in your driveway, go inside, then come back out 4 hours later.
|
||||
|
||||
@@ -3,35 +3,32 @@ id: restream
|
||||
title: Restream
|
||||
---
|
||||
|
||||
### RTSP
|
||||
## RTSP
|
||||
|
||||
Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp://<frigate_host>:8554/<camera_name>`. Port 8554 must be open. [This allows you to use a video feed for detection in frigate and Home Assistant live view at the same time without having to make two separate connections to the camera](#reduce-connections-to-camera). The video feed is copied from the original video feed directly to avoid re-encoding. This feed does not include any annotation by Frigate.
|
||||
Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp://<frigate_host>:8554/<camera_name>`. Port 8554 must be open. [This allows you to use a video feed for detection in Frigate and Home Assistant live view at the same time without having to make two separate connections to the camera](#reduce-connections-to-camera). The video feed is copied from the original video feed directly to avoid re-encoding. This feed does not include any annotation by Frigate.
|
||||
|
||||
#### Force Audio
|
||||
Frigate uses [go2rtc](https://github.com/AlexxIT/go2rtc) to provide its restream and MSE/WebRTC capabilities. The go2rtc config is hosted at the `go2rtc` in the config, see [go2rtc docs](https://github.com/AlexxIT/go2rtc#configuration) for more advanced configurations and features.
|
||||
|
||||
Different live view technologies (ex: MSE, WebRTC) support different audio codecs. The `restream -> force_audio` flag tells the restream to make multiple streams available so that all live view technologies are supported. Some camera streams don't work well with this, in which case `restream -> force_audio` should be disabled.
|
||||
### Birdseye Restream
|
||||
|
||||
#### Birdseye Restream
|
||||
Birdseye RTSP restream can be enabled at `birdseye -> restream` and accessed at `rtsp://<frigate_host>:8554/birdseye`. Enabling the restream will cause birdseye to run 24/7 which may increase CPU usage somewhat.
|
||||
|
||||
Birdseye RTSP restream can be enabled at `restream -> birdseye` and accessed at `rtsp://<frigate_host>:8554/birdseye`. Enabling the restream will cause birdseye to run 24/7 which may increase CPU usage somewhat.
|
||||
### Securing Restream With Authentication
|
||||
|
||||
#### Changing Restream Codec
|
||||
|
||||
Generally it is recommended to let the codec from the camera be copied. But there may be some cases where h265 needs to be transcoded as h264 or an MJPEG stream can be encoded and restreamed as h264. In this case the encoding will need to be set, if any hardware acceleration presets are set then that will be used to encode the stream.
|
||||
The go2rtc restream can be secured with RTSP based username / password authentication. Ex:
|
||||
|
||||
```yaml
|
||||
ffmpeg:
|
||||
hwaccel_args: your-hwaccel-preset # <- highly recommended so the GPU is used
|
||||
|
||||
cameras:
|
||||
mjpeg_cam:
|
||||
ffmpeg:
|
||||
go2rtc:
|
||||
rtsp:
|
||||
username: "admin"
|
||||
password: "pass"
|
||||
streams:
|
||||
...
|
||||
restream:
|
||||
video_encoding: h264
|
||||
```
|
||||
|
||||
### RTMP (Deprecated)
|
||||
**NOTE:** This does not apply to localhost requests, there is no need to provide credentials when using the restream as a source for frigate cameras.
|
||||
|
||||
## RTMP (Deprecated)
|
||||
|
||||
In previous Frigate versions RTMP was used for re-streaming. RTMP has disadvantages however including being incompatible with H.265, high bitrates, and certain audio codecs. RTMP is deprecated and it is recommended to move to the new restream role.
|
||||
|
||||
@@ -44,17 +41,36 @@ Some cameras only support one active connection or you may just want to have a s
|
||||
One connection is made to the camera. One for the restream, `detect` and `record` connect to the restream.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
rtsp_cam: # <- for RTSP streams
|
||||
- rtsp://192.168.1.5:554/live0 # <- stream which supports video & aac audio
|
||||
- "ffmpeg:rtsp_cam#audio=opus" # <- copy of the stream which transcodes audio to the missing codec (usually will be opus)
|
||||
http_cam: # <- for other streams
|
||||
- http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=user&password=password # <- stream which supports video & aac audio
|
||||
- "ffmpeg:http_cam#audio=opus" # <- copy of the stream which transcodes audio to the missing codec (usually will be opus)
|
||||
|
||||
cameras:
|
||||
test_cam:
|
||||
rtsp_cam:
|
||||
ffmpeg:
|
||||
output_args:
|
||||
record: preset-record-generic-audio-copy
|
||||
inputs:
|
||||
- path: rtsp://localhost:8554/test_cam # <--- the name here must match the name of the camera
|
||||
- path: rtsp://127.0.0.1:8554/rtsp_cam # <--- the name here must match the name of the camera in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- record
|
||||
- detect
|
||||
- path: rtsp://192.168.1.5:554/live0 # <--- 1 connection to camera stream
|
||||
http_cam:
|
||||
ffmpeg:
|
||||
output_args:
|
||||
record: preset-record-generic-audio-copy
|
||||
inputs:
|
||||
- path: rtsp://127.0.0.1:8554/http_cam # <--- the name here must match the name of the camera in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- restream
|
||||
- record
|
||||
- detect
|
||||
```
|
||||
|
||||
### With Sub Stream
|
||||
@@ -62,17 +78,58 @@ cameras:
|
||||
Two connections are made to the camera. One for the sub stream, one for the restream, `record` connects to the restream.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
rtsp_cam:
|
||||
- rtsp://192.168.1.5:554/live0 # <- stream which supports video & aac audio. This is only supported for rtsp streams, http must use ffmpeg
|
||||
- "ffmpeg:rtsp_cam#audio=opus" # <- copy of the stream which transcodes audio to opus
|
||||
rtsp_cam_sub:
|
||||
- rtsp://192.168.1.5:554/substream # <- stream which supports video & aac audio. This is only supported for rtsp streams, http must use ffmpeg
|
||||
- "ffmpeg:rtsp_cam_sub#audio=opus" # <- copy of the stream which transcodes audio to opus
|
||||
http_cam:
|
||||
- http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=user&password=password # <- stream which supports video & aac audio. This is only supported for rtsp streams, http must use ffmpeg
|
||||
- "ffmpeg:http_cam#audio=opus" # <- copy of the stream which transcodes audio to opus
|
||||
http_cam_sub:
|
||||
- http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=user&password=password # <- stream which supports video & aac audio. This is only supported for rtsp streams, http must use ffmpeg
|
||||
- "ffmpeg:http_cam_sub#audio=opus" # <- copy of the stream which transcodes audio to opus
|
||||
|
||||
cameras:
|
||||
test_cam:
|
||||
rtsp_cam:
|
||||
ffmpeg:
|
||||
output_args:
|
||||
record: preset-record-generic-audio-copy
|
||||
inputs:
|
||||
- path: rtsp://localhost:8554/test_cam # <--- the name here must match the name of the camera
|
||||
- path: rtsp://127.0.0.1:8554/rtsp_cam # <--- the name here must match the name of the camera in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- record
|
||||
- path: rtsp://192.168.1.5:554/stream # <--- camera high res stream
|
||||
- path: rtsp://127.0.0.1:8554/rtsp_cam_sub # <--- the name here must match the name of the camera_sub in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- restream
|
||||
- path: rtsp://192.168.1.5:554/substream # <--- camera sub stream
|
||||
- detect
|
||||
http_cam:
|
||||
ffmpeg:
|
||||
output_args:
|
||||
record: preset-record-generic-audio-copy
|
||||
inputs:
|
||||
- path: rtsp://127.0.0.1:8554/http_cam # <--- the name here must match the name of the camera in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- record
|
||||
- path: rtsp://127.0.0.1:8554/http_cam_sub # <--- the name here must match the name of the camera_sub in restream
|
||||
input_args: preset-rtsp-restream
|
||||
roles:
|
||||
- detect
|
||||
```
|
||||
|
||||
## Advanced Restream Configurations
|
||||
|
||||
The [exec](https://github.com/AlexxIT/go2rtc#source-exec) source in go2rtc can be used for custom ffmpeg commands. An example is below:
|
||||
|
||||
NOTE: The output will need to be passed with two curly braces `{{output}}`
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
stream1: exec:ffmpeg -hide_banner -re -stream_loop -1 -i /media/BigBuckBunny.mp4 -c copy -rtsp_transport tcp -f rtsp {{output}}
|
||||
```
|
||||
|
||||
@@ -42,7 +42,7 @@ Fork [blakeblackshear/frigate-hass-integration](https://github.com/blakeblackshe
|
||||
|
||||
#### 1. Open the repo with Visual Studio Code
|
||||
|
||||
Upon opening, you should be prompted to open the project in a remote container. This will build a container on top of the base frigate container with all the development dependencies installed. This ensures everyone uses a consistent development environment without the need to install any dependencies on your host machine.
|
||||
Upon opening, you should be prompted to open the project in a remote container. This will build a container on top of the base Frigate container with all the development dependencies installed. This ensures everyone uses a consistent development environment without the need to install any dependencies on your host machine.
|
||||
|
||||
#### 2. Modify your local config file for testing
|
||||
|
||||
@@ -62,7 +62,6 @@ cameras:
|
||||
input_args: -re -stream_loop -1 -fflags +genpts
|
||||
roles:
|
||||
- detect
|
||||
- rtmp
|
||||
detect:
|
||||
height: 1080
|
||||
width: 1920
|
||||
@@ -75,7 +74,7 @@ These input args tell ffmpeg to read the mp4 file in an infinite loop. You can u
|
||||
|
||||
Create and place these files in a `debug` folder in the root of the repo. This is also where recordings will be created if you enable them in your test config. Update your config from step 2 above to point at the right file. You can check the `docker-compose.yml` file in the repo to see how the volumes are mapped.
|
||||
|
||||
#### 4. Run frigate from the command line
|
||||
#### 4. Run Frigate from the command line
|
||||
|
||||
VSCode will start the docker compose file for you and open a terminal window connected to `frigate-dev`.
|
||||
|
||||
|
||||
@@ -23,22 +23,14 @@ Larger resolutions **do** improve performance if the objects are very small in t
|
||||
|
||||
For the Dahua/Loryta 5442 camera, I use the following settings:
|
||||
|
||||
**Main Stream (Recording)**
|
||||
**Main Stream (Recording & RTSP)**
|
||||
|
||||
- Encode Mode: H.264
|
||||
- Resolution: 2688\*1520
|
||||
- Frame Rate(FPS): 15
|
||||
- I Frame Interval: 30
|
||||
|
||||
**Sub Stream 1 (RTMP)**
|
||||
|
||||
- Enable: Sub Stream 1
|
||||
- Encode Mode: H.264
|
||||
- Resolution: 720\*576
|
||||
- Frame Rate: 10
|
||||
- I Frame Interval: 10
|
||||
|
||||
**Sub Stream 2 (Detection)**
|
||||
**Sub Stream (Detection)**
|
||||
|
||||
- Enable: Sub Stream 2
|
||||
- Encode Mode: H.264
|
||||
@@ -29,7 +29,7 @@ My current favorite is the Minisforum GK41 because of the dual NICs that allow y
|
||||
| Minisforum GK41 (<a href="https://amzn.to/3ptnb8D" target="_blank" rel="nofollow noopener sponsored">Amazon</a>) | 9-10ms | USB | Dual gigabit NICs for easy isolated camera network. Easily handles several 1080p cameras. |
|
||||
| Beelink GK55 (<a href="https://amzn.to/35E79BC" target="_blank" rel="nofollow noopener sponsored">Amazon</a>) | 9-10ms | USB | Dual gigabit NICs for easy isolated camera network. Easily handles several 1080p cameras. |
|
||||
| Intel NUC (<a href="https://amzn.to/3psFlHi" target="_blank" rel="nofollow noopener sponsored">Amazon</a>) | 8-10ms | USB | Overkill for most, but great performance. Can handle many cameras at 5fps depending on typical amounts of motion. Requires extra parts. |
|
||||
| BMAX B2 Plus (<a href="https://amzn.to/3a6TBh8" target="_blank" rel="nofollow noopener sponsored">Amazon</a>) | 10-12ms | USB | Good balance of performance and cost. Also capable of running many other services at the same time as frigate. |
|
||||
| BMAX B2 Plus (<a href="https://amzn.to/3a6TBh8" target="_blank" rel="nofollow noopener sponsored">Amazon</a>) | 10-12ms | USB | Good balance of performance and cost. Also capable of running many other services at the same time as Frigate. |
|
||||
| Atomic Pi (<a href="https://amzn.to/2YjpY9m" target="_blank" rel="nofollow noopener sponsored">Amazon</a>) | 16ms | USB | Good option for a dedicated low power board with a small number of cameras. Can leverage Intel QuickSync for stream decoding. |
|
||||
| Raspberry Pi 4 (64bit) (<a href="https://amzn.to/2YhSGHH" target="_blank" rel="nofollow noopener sponsored">Amazon</a>) | 10-15ms | USB | Can handle a small number of cameras. |
|
||||
|
||||
@@ -58,11 +58,20 @@ More information is available [in the detector docs](/configuration/detectors#op
|
||||
Inference speeds vary greatly depending on the CPU, GPU, or VPU used, some known examples are below:
|
||||
|
||||
| Name | Inference Speed | Notes |
|
||||
| ------------------- | --------------- | --------------------------------------------------------------------- |
|
||||
| Intel Celeron J4105 | ~ 25 ms | Inference speeds on CPU were ~ 150 ms |
|
||||
| Intel Celeron N4020 | 50 - 200 ms | Inference speeds on CPU were ~ 800 ms, greatly depends on other loads |
|
||||
| -------------------- | --------------- | --------------------------------------------------------------------- |
|
||||
| Intel NCS2 VPU | 60 - 65 ms | May vary based on host device |
|
||||
| Intel Celeron J4105 | ~ 25 ms | Inference speeds on CPU were 150 - 200 ms |
|
||||
| Intel Celeron N3060 | 130 - 150 ms | Inference speeds on CPU were ~ 550 ms |
|
||||
| Intel Celeron N3205U | ~ 120 ms | Inference speeds on CPU were ~ 380 ms |
|
||||
| Intel Celeron N4020 | 50 - 200 ms | Inference speeds on CPU were ~ 800 ms, greatly depends on other loads |
|
||||
| Intel i3 6100T | 15 - 35 ms | Inference speeds on CPU were 60 - 120 ms |
|
||||
| Intel i3 8100 | ~ 15 ms | Inference speeds on CPU were ~ 65 ms |
|
||||
| Intel i5 4590 | ~ 20 ms | Inference speeds on CPU were ~ 230 ms |
|
||||
| Intel i5 6500 | ~ 15 ms | Inference speeds on CPU were ~ 150 ms |
|
||||
| Intel i5 7200u | 15 - 25 ms | Inference speeds on CPU were ~ 150 ms |
|
||||
| Intel i5 7500 | ~ 15 ms | Inference speeds on CPU were ~ 260 ms |
|
||||
| Intel i5 1135G7 | 10 - 15 ms | |
|
||||
| Intel i5 12600K | ~ 15 ms | Inference speeds on CPU were ~ 35 ms |
|
||||
|
||||
### TensorRT
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Frigate uses the following locations for read/write operations in the container.
|
||||
|
||||
- `/media/frigate/clips`: Used for snapshot storage. In the future, it will likely be renamed from `clips` to `snapshots`. The file structure here cannot be modified and isn't intended to be browsed or managed manually.
|
||||
- `/media/frigate/recordings`: Internal system storage for recording segments. The file structure here cannot be modified and isn't intended to be browsed or managed manually.
|
||||
- `/media/frigate/frigate.db`: Default location for the sqlite database. You will also see several files alongside this file while frigate is running. If moving the database location (often needed when using a network drive at `/media/frigate`), it is recommended to mount a volume with docker at `/db` and change the storage location of the database to `/db/frigate.db` in the config file.
|
||||
- `/media/frigate/frigate.db`: Default location for the sqlite database. You will also see several files alongside this file while Frigate is running. If moving the database location (often needed when using a network drive at `/media/frigate`), it is recommended to mount a volume with docker at `/db` and change the storage location of the database to `/db/frigate.db` in the config file.
|
||||
- `/tmp/cache`: Cache location for recording segments. Initial recordings are written here before being checked and converted to mp4 and moved to the recordings folder.
|
||||
- `/dev/shm`: It is not recommended to modify this directory or map it with docker. This is the location for raw decoded frames in shared memory and it's size is impacted by the `shm-size` calculations below.
|
||||
- `/config/config.yml`: Default location of the config file.
|
||||
@@ -74,17 +74,29 @@ database:
|
||||
|
||||
### Calculating required shm-size
|
||||
|
||||
Frigate utilizes shared memory to store frames during processing. The default `shm-size` provided by Docker is 64m.
|
||||
Frigate utilizes shared memory to store frames during processing. The default `shm-size` provided by Docker is **64MB**.
|
||||
|
||||
The default shm-size of 64m is fine for setups with 2 or less 1080p cameras. If frigate is exiting with "Bus error" messages, it is likely because you have too many high resolution cameras and you need to specify a higher shm size.
|
||||
The default shm size of **64MB** is fine for setups with **2 cameras** detecting at **720p**. If Frigate is exiting with "Bus error" messages, it is likely because you have too many high resolution cameras and you need to specify a higher shm size.
|
||||
|
||||
You can calculate the necessary shm-size for each camera with the following formula using the resolution specified for detect:
|
||||
The Frigate container also stores logs in shm, which can take up to **30MB**, so make sure to take this into account in your math as well.
|
||||
|
||||
```
|
||||
(width * height * 1.5 * 9 + 270480)/1048576 = <shm size in mb>
|
||||
You can calculate the necessary shm size for each camera with the following formula using the resolution specified for detect:
|
||||
|
||||
```console
|
||||
# Replace <width> and <height>
|
||||
$ python -c 'print("{:.2f}MB".format((<width> * <height> * 1.5 * 9 + 270480) / 1048576))'
|
||||
|
||||
# Example for 1280x720
|
||||
$ python -c 'print("{:.2f}MB".format((1280 * 720 * 1.5 * 9 + 270480) / 1048576))'
|
||||
12.12MB
|
||||
|
||||
# Example for eight cameras detecting at 1280x720, including logs
|
||||
$ python -c 'print("{:.2f}MB".format(((1280 * 720 * 1.5 * 9 + 270480) / 1048576) * 8 + 30))'
|
||||
126.99MB
|
||||
```
|
||||
|
||||
The shm size cannot be set per container for Home Assistant Addons. You must set `default-shm-size` in `/etc/docker/daemon.json` to increase the default shm size. This will increase the shm size for all of your docker containers. This may or may not cause issues with your setup. https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
|
||||
The shm size cannot be set per container for Home Assistant add-ons. However, this is probably not required since by default Home Assistant Supervisor allocates `/dev/shm` with half the size of your total memory. If your machine has 8GB of memory, chances are that Frigate will have access to up to 4GB without any additional configuration.
|
||||
|
||||
|
||||
### Raspberry Pi 3/4
|
||||
|
||||
@@ -103,7 +115,7 @@ services:
|
||||
container_name: frigate
|
||||
privileged: true # this may not be necessary for all setups
|
||||
restart: unless-stopped
|
||||
image: blakeblackshear/frigate:stable
|
||||
image: ghcr.io/blakeblackshear/frigate:stable
|
||||
shm_size: "64mb" # update for your cameras based on calculation above
|
||||
devices:
|
||||
- /dev/bus/usb:/dev/bus/usb # passes the USB Coral, needs to be modified for other versions
|
||||
@@ -119,7 +131,9 @@ services:
|
||||
size: 1000000000
|
||||
ports:
|
||||
- "5000:5000"
|
||||
- "1935:1935" # RTMP feeds
|
||||
- "8554:8554" # RTSP feeds
|
||||
- "8555:8555/tcp" # WebRTC over tcp
|
||||
- "8555:8555/udp" # WebRTC over udp
|
||||
environment:
|
||||
FRIGATE_RTSP_PASSWORD: "password"
|
||||
```
|
||||
@@ -139,8 +153,10 @@ docker run -d \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
-e FRIGATE_RTSP_PASSWORD='password' \
|
||||
-p 5000:5000 \
|
||||
-p 1935:1935 \
|
||||
blakeblackshear/frigate:stable
|
||||
-p 8554:8554 \
|
||||
-p 8555:8555/tcp \
|
||||
-p 8555:8555/udp \
|
||||
ghcr.io/blakeblackshear/frigate:stable
|
||||
```
|
||||
|
||||
## Home Assistant Operating System (HassOS)
|
||||
|
||||
59
docs/docs/guides/configuring_go2rtc.md
Normal file
59
docs/docs/guides/configuring_go2rtc.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
id: configuring_go2rtc
|
||||
title: Configuring go2rtc
|
||||
---
|
||||
|
||||
Use of the bundled go2rtc is optional. You can still configure FFmpeg to connect directly to your cameras. However, adding go2rtc to your configuration is required for the following features:
|
||||
|
||||
- WebRTC or MSE for live viewing with higher resolutions and frame rates than the jsmpeg stream which is limited to the detect stream
|
||||
- RTSP (instead of RTMP) relay for use with Home Assistant or other consumers to reduce the number of connections to your camera streams
|
||||
|
||||
# Setup a go2rtc stream
|
||||
|
||||
First, you will want to configure go2rtc to connect to your camera stream by adding the stream you want to use for live view in your Frigate config file. If you set the stream name under go2rtc to match the name of your camera, it will automatically be mapped and you will get additional live view options for the camera. Avoid changing any other parts of your config at this step. Note that go2rtc supports [many different stream types](https://github.com/AlexxIT/go2rtc#module-streams), not just rtsp.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
back:
|
||||
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
|
||||
```
|
||||
|
||||
The easiest live view to get working is MSE. After adding this to the config, restart Frigate and try to watch the live stream by selecting MSE in the dropdown after clicking on the camera.
|
||||
|
||||
### What if my video doesn't play?
|
||||
|
||||
If you are unable to see your video feed, first check the go2rtc logs in the Frigate UI under Logs in the sidebar. If go2rtc is having difficulty connecting to your camera, you should see some error messages in the log. If you do not see any errors, then video codec of the stream may not be supported in your browser. If your camera stream is set to H265, try switching to H264. You can see more information about [video codec compatibility](https://github.com/AlexxIT/go2rtc#codecs-madness) in the go2rtc documentation. If you are not able to switch your camera settings from H265 to H264 or your stream is a different format such as MJPEG, you can use go2rtc to re-encode the video using the [FFmpeg parameters](https://github.com/AlexxIT/go2rtc#source-ffmpeg). It supports rotating and resizing video feeds and hardware acceleration. Keep in mind that transcoding video from one format to another is a resource intensive task and you may be better off using the built-in jsmpeg view. Here is an example of a config that will re-encode the stream to H264 without hardware acceleration:
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
back:
|
||||
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
|
||||
- "ffmpeg:back#video=h264"
|
||||
```
|
||||
|
||||
If you can see the video but do not have audio, this is most likely because your camera's audio stream is not AAC. If possible, update your camera's audio settings to AAC. If your cameras do not support AAC audio, you will need to tell go2rtc to re-encode the audio to AAC on demand if you want audio. This will use additional CPU and add some latency. To add AAC audio on demand, you can update your go2rtc config as follows:
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
back:
|
||||
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
|
||||
- "ffmpeg:back#audio=aac"
|
||||
```
|
||||
|
||||
If you need to convert **both** the audio and video streams, you can use the following:
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
back:
|
||||
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
|
||||
- "ffmpeg:back#video=h264#audio=aac"
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
1. If the stream you added to go2rtc is also used by Frigate for the `record` or `detect` role, you can migrate your config to pull from the RTSP restream to reduce the number of connections to your camera as shown [here](/configuration/restream#reduce-connections-to-camera).
|
||||
1. You may also prefer to [setup WebRTC](/configuration/live#webrtc-extra-configuration) for slightly lower latency than MSE. Note that WebRTC only supports h264 and specific audio formats.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
id: events_setup
|
||||
title: Setting Up Events
|
||||
---
|
||||
|
||||
[Snapshots](../configuration/snapshots.md) and/or [Recordings](../configuration/record.md) must be enabled for events to be created for detected objects.
|
||||
|
||||
## Limiting Events to Areas of Interest
|
||||
|
||||
The best way to limit events to areas of interest is to use [zones](../configuration/zones.md) along with `required_zones` for events and snapshots to only have events created in areas of interest.
|
||||
@@ -20,4 +20,4 @@ For object filters in your configuration, any single detection below `min_score`
|
||||
| 5 | 0.88 | 0.7, 0.85, 0.95, 0.90, 0.88 | 0.88 | Yes |
|
||||
| 6 | 0.95 | 0.7, 0.85, 0.95, 0.90, 0.88, 0.95 | 0.89 | Yes |
|
||||
|
||||
In frame 2, the score is below the `min_score` value, so frigate ignores it and it becomes a 0.0. The computed score is the median of the score history (padding to at least 3 values), and only when that computed score crosses the `threshold` is the object marked as a true positive. That happens in frame 4 in the example.
|
||||
In frame 2, the score is below the `min_score` value, so Frigate ignores it and it becomes a 0.0. The computed score is the median of the score history (padding to at least 3 values), and only when that computed score crosses the `threshold` is the object marked as a true positive. That happens in frame 4 in the example.
|
||||
|
||||
@@ -1,124 +1,81 @@
|
||||
---
|
||||
id: getting_started
|
||||
title: Creating a config file
|
||||
title: Getting started
|
||||
---
|
||||
|
||||
This guide walks through the steps to build a configuration file for Frigate. It assumes that you already have an environment setup as described in [Installation](../frigate/installation.md). You should also configure your cameras according to the [camera setup guide](/guides/camera_setup)
|
||||
This guide walks through the steps to build a configuration file for Frigate. It assumes that you already have an environment setup as described in [Installation](../frigate/installation.md). You should also configure your cameras according to the [camera setup guide](/frigate/camera_setup). Pay particular attention to the section on choosing a detect resolution.
|
||||
|
||||
### Step 1: Configure the MQTT server (Optional)
|
||||
### Step 1: Add a detect stream
|
||||
|
||||
Use of a functioning MQTT server is optional for frigate, but required for the home assistant integration. Start by adding the mqtt section at the top level in your config:
|
||||
First we will add the detect stream for the camera:
|
||||
|
||||
If using mqtt:
|
||||
```yaml
|
||||
mqtt:
|
||||
host: <ip of your mqtt server>
|
||||
```
|
||||
|
||||
If not using mqtt:
|
||||
```yaml
|
||||
mqtt:
|
||||
enabled: False
|
||||
```
|
||||
|
||||
If using the Mosquitto Addon in Home Assistant, a username and password is required. For example:
|
||||
|
||||
```yaml
|
||||
mqtt:
|
||||
host: <ip of your mqtt server>
|
||||
user: <username>
|
||||
password: <password>
|
||||
```
|
||||
|
||||
Frigate supports many configuration options for mqtt. See the [configuration reference](../configuration/index.md#full-configuration-reference) for more info.
|
||||
|
||||
### Step 2: Configure detectors
|
||||
|
||||
By default, Frigate will use a single CPU detector. If you have a USB Coral, you will need to add a detectors section to your config.
|
||||
|
||||
```yaml
|
||||
mqtt:
|
||||
host: <ip of your mqtt server>
|
||||
|
||||
detectors:
|
||||
coral:
|
||||
type: edgetpu
|
||||
device: usb
|
||||
```
|
||||
|
||||
More details on available detectors can be found [here](../configuration/detectors.md).
|
||||
|
||||
### Step 3: Add a minimal camera configuration
|
||||
|
||||
Now let's add the first camera:
|
||||
|
||||
```yaml
|
||||
mqtt:
|
||||
host: <ip of your mqtt server>
|
||||
|
||||
detectors:
|
||||
coral:
|
||||
type: edgetpu
|
||||
device: usb
|
||||
|
||||
cameras:
|
||||
camera_1: # <------ Name the camera
|
||||
ffmpeg:
|
||||
inputs:
|
||||
- path: rtsp://10.0.10.10:554/rtsp # <----- Update for your camera
|
||||
- path: rtsp://10.0.10.10:554/rtsp # <----- The stream you want to use for detection
|
||||
roles:
|
||||
- detect
|
||||
- rtmp
|
||||
rtmp:
|
||||
enabled: False # <-- RTMP should be disabled if your stream is not H264
|
||||
detect:
|
||||
enabled: False # <---- disable detection until you have a working camera feed
|
||||
width: 1280 # <---- update for your camera's resolution
|
||||
height: 720 # <---- update for your camera's resolution
|
||||
```
|
||||
|
||||
### Step 4: Start Frigate
|
||||
### Step 2: Start Frigate
|
||||
|
||||
At this point you should be able to start Frigate and see the the video feed in the UI.
|
||||
|
||||
If you get a green image from the camera, this means ffmpeg was not able to get the video feed from your camera. Check the logs for error messages from ffmpeg. The default ffmpeg arguments are designed to work with H264 RTSP cameras that support TCP connections. If you do not have H264 cameras, make sure you have disabled RTMP. It is possible to enable it, but you must tell ffmpeg to re-encode the video with customized output args.
|
||||
If you get an error image from the camera, this means ffmpeg was not able to get the video feed from your camera. Check the logs for error messages from ffmpeg. The default ffmpeg arguments are designed to work with H264 RTSP cameras that support TCP connections.
|
||||
|
||||
FFmpeg arguments for other types of cameras can be found [here](../configuration/camera_specific.md).
|
||||
|
||||
### Step 5: Configure hardware acceleration (optional)
|
||||
### Step 3: Configure hardware acceleration (recommended)
|
||||
|
||||
Now that you have a working camera configuration, you want to setup hardware acceleration to minimize the CPU required to decode your video streams. See the [hardware acceleration](../configuration/hardware_acceleration.md) config reference for examples applicable to your hardware.
|
||||
|
||||
In order to best evaluate the performance impact of hardware acceleration, it is recommended to temporarily disable detection.
|
||||
Here is an example configuration with hardware acceleration configured for Intel processors with an integrated GPU using the [preset](../configuration/ffmpeg_presets.md):
|
||||
|
||||
```yaml
|
||||
mqtt: ...
|
||||
|
||||
detectors: ...
|
||||
|
||||
cameras:
|
||||
camera_1:
|
||||
ffmpeg: ...
|
||||
detect:
|
||||
enabled: False
|
||||
...
|
||||
```
|
||||
|
||||
Here is an example configuration with hardware acceleration configured:
|
||||
|
||||
```yaml
|
||||
mqtt: ...
|
||||
|
||||
detectors: ...
|
||||
|
||||
cameras:
|
||||
camera_1:
|
||||
ffmpeg:
|
||||
inputs: ...
|
||||
hwaccel_args: -c:v h264_v4l2m2m
|
||||
hwaccel_args: preset-vaapi
|
||||
detect: ...
|
||||
```
|
||||
|
||||
### Step 6: Setup motion masks
|
||||
### Step 4: Configure detectors
|
||||
|
||||
By default, Frigate will use a single CPU detector. If you have a USB Coral, you will need to add a detectors section to your config.
|
||||
|
||||
```yaml
|
||||
mqtt: ...
|
||||
|
||||
detectors: # <---- add detectors
|
||||
coral:
|
||||
type: edgetpu
|
||||
device: usb
|
||||
|
||||
cameras:
|
||||
camera_1:
|
||||
ffmpeg: ...
|
||||
detect:
|
||||
enabled: True # <---- turn on detection
|
||||
...
|
||||
```
|
||||
|
||||
More details on available detectors can be found [here](../configuration/detectors.md).
|
||||
|
||||
Restart Frigate and you should start seeing detections for `person`. If you want to track other objects, they will need to be added according to the [configuration file reference](../configuration/index.md#full-configuration-reference).
|
||||
|
||||
### Step 5: Setup motion masks
|
||||
|
||||
Now that you have optimized your configuration for decoding the video stream, you will want to check to see where to implement motion masks. To do this, navigate to the camera in the UI, select "Debug" at the top, and enable "Motion boxes" in the options below the video feed. Watch for areas that continuously trigger unwanted motion to be detected. Common areas to mask include camera timestamps and trees that frequently blow in the wind. The goal is to avoid wasting object detection cycles looking at these areas.
|
||||
|
||||
@@ -134,7 +91,7 @@ Your configuration should look similar to this now.
|
||||
|
||||
```yaml
|
||||
mqtt:
|
||||
host: mqtt.local
|
||||
enabled: False
|
||||
|
||||
detectors:
|
||||
coral:
|
||||
@@ -148,7 +105,6 @@ cameras:
|
||||
- path: rtsp://10.0.10.10:554/rtsp
|
||||
roles:
|
||||
- detect
|
||||
- rtmp
|
||||
detect:
|
||||
width: 1280
|
||||
height: 720
|
||||
@@ -157,9 +113,13 @@ cameras:
|
||||
- 0,461,3,0,1919,0,1919,843,1699,492,1344,458,1346,336,973,317,869,375,866,432
|
||||
```
|
||||
|
||||
### Step 7: Enable recording (optional)
|
||||
### Step 6: Enable recording and/or snapshots
|
||||
|
||||
To enable recording video, add the `record` role to a stream and enable it in the config.
|
||||
In order to see Events in the Frigate UI, either snapshots or record will need to be enabled.
|
||||
|
||||
#### Record
|
||||
|
||||
To enable recording video, add the `record` role to a stream and enable it in the config. If record is disabled in the config, turning it on via the UI will not have any effect.
|
||||
|
||||
```yaml
|
||||
mqtt: ...
|
||||
@@ -173,8 +133,7 @@ cameras:
|
||||
- path: rtsp://10.0.10.10:554/rtsp
|
||||
roles:
|
||||
- detect
|
||||
- rtmp
|
||||
- path: rtsp://10.0.10.10:554/high_res_stream # <----- Add high res stream
|
||||
- path: rtsp://10.0.10.10:554/high_res_stream # <----- Add stream you want to record from
|
||||
roles:
|
||||
- record
|
||||
detect: ...
|
||||
@@ -187,9 +146,9 @@ If you don't have separate streams for detect and record, you would just add the
|
||||
|
||||
By default, Frigate will retain video of all events for 10 days. The full set of options for recording can be found [here](../configuration/index.md#full-configuration-reference).
|
||||
|
||||
### Step 8: Enable snapshots (optional)
|
||||
#### Snapshots
|
||||
|
||||
To enable snapshots of your events, just enable it in the config.
|
||||
To enable snapshots of your events, just enable it in the config. Snapshots are taken from the detect stream because it is the only stream decoded.
|
||||
|
||||
```yaml
|
||||
mqtt: ...
|
||||
@@ -206,3 +165,10 @@ cameras:
|
||||
```
|
||||
|
||||
By default, Frigate will retain snapshots of all events for 10 days. The full set of options for snapshots can be found [here](../configuration/index.md#full-configuration-reference).
|
||||
|
||||
### Step 7: Follow up guides
|
||||
|
||||
Now that you have a working install, you can use the following guides for additional features:
|
||||
|
||||
1. [Configuring go2rtc](configuring_go2rtc) - Additional live view options and RTSP relay
|
||||
2. [Home Assistant Integration](../integrations/home-assistant.md) - Integrate with Home Assistant
|
||||
|
||||
@@ -7,7 +7,7 @@ The best way to get started with notifications for Frigate is to use the [Bluepr
|
||||
|
||||
It is generally recommended to trigger notifications based on the `frigate/events` mqtt topic. This provides the event_id needed to fetch [thumbnails/snapshots/clips](../integrations/home-assistant.md#notification-api) and other useful information to customize when and where you want to receive alerts. The data is published in the form of a change feed, which means you can reference the "previous state" of the object in the `before` section and the "current state" of the object in the `after` section. You can see an example [here](../integrations/mqtt.md#frigateevents).
|
||||
|
||||
Here is a simple example of a notification automation of events which will update the existing notification for each change. This means the image you see in the notification will update as frigate finds a "better" image.
|
||||
Here is a simple example of a notification automation of events which will update the existing notification for each change. This means the image you see in the notification will update as Frigate finds a "better" image.
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: reverse_proxy
|
||||
title: Setting up a Reverse Proxy
|
||||
title: Setting up a reverse proxy
|
||||
---
|
||||
|
||||
This guide outlines the basic configuration steps needed to expose your Frigate UI to the internet.
|
||||
@@ -8,6 +8,7 @@ A common way of accomplishing this is to use a reverse proxy webserver between y
|
||||
A reverse proxy accepts HTTP requests from the public internet and redirects them transparently to internal webserver(s) on your network.
|
||||
|
||||
The suggested steps are:
|
||||
|
||||
- **Configure** a 'proxy' HTTP webserver (such as [Apache2](https://httpd.apache.org/docs/current/) or [NPM](https://github.com/NginxProxyManager/nginx-proxy-manager)) and only expose ports 80/443 from this webserver to the internet
|
||||
- **Encrypt** content from the proxy webserver by installing SSL (such as with [Let's Encrypt](https://letsencrypt.org/)). Note that SSL is then not required on your Frigate webserver as the proxy encrypts all requests for you
|
||||
- **Restrict** access to your Frigate instance at the proxy using, for example, password authentication
|
||||
@@ -31,6 +32,7 @@ On Debian Apache2 the configuration file will be named along the lines of `/etc/
|
||||
|
||||
Make life easier for yourself by presenting your Frigate interface as a DNS sub-domain rather than as a sub-folder of your main domain.
|
||||
Here we access Frigate via https://cctv.mydomain.co.uk
|
||||
|
||||
```xml
|
||||
<VirtualHost *:443>
|
||||
ServerName cctv.mydomain.co.uk
|
||||
|
||||
@@ -26,7 +26,7 @@ You can access a higher resolution mjpeg stream by appending `h=height-in-pixels
|
||||
|
||||
### `GET /api/<camera_name>/latest.jpg[?h=300]`
|
||||
|
||||
The most recent frame that frigate has finished processing. It is a full resolution image by default.
|
||||
The most recent frame that Frigate has finished processing. It is a full resolution image by default.
|
||||
|
||||
Accepts the following query string parameters:
|
||||
|
||||
@@ -63,7 +63,7 @@ Sample response:
|
||||
"camera_fps": 5.0,
|
||||
/***************
|
||||
* Number of times detection is run per second. This can be higher than
|
||||
* your camera FPS because frigate often looks at the same frame multiple times
|
||||
* your camera FPS because Frigate often looks at the same frame multiple times
|
||||
* or in multiple locations
|
||||
***************/
|
||||
"detection_fps": 1.5,
|
||||
@@ -76,11 +76,11 @@ Sample response:
|
||||
***************/
|
||||
"pid": 34,
|
||||
/***************
|
||||
* Frames per second being processed by frigate.
|
||||
* Frames per second being processed by Frigate.
|
||||
***************/
|
||||
"process_fps": 5.1,
|
||||
/***************
|
||||
* Frames per second skip for processing by frigate.
|
||||
* Frames per second skip for processing by Frigate.
|
||||
***************/
|
||||
"skipped_fps": 0.0
|
||||
},
|
||||
|
||||
@@ -45,7 +45,7 @@ that card.
|
||||
|
||||
## Configuration
|
||||
|
||||
When configuring the integration, you will be asked for the `URL` of your frigate instance which is the URL you use to access Frigate in the browser. This may look like `http://<host>:5000/`. If you are using HassOS with the addon, the URL should be one of the following depending on which addon version you are using. Note that if you are using the Proxy Addon, you do NOT point the integration at the proxy URL. Just enter the URL used to access frigate directly from your network.
|
||||
When configuring the integration, you will be asked for the `URL` of your Frigate instance which is the URL you use to access Frigate in the browser. This may look like `http://<host>:5000/`. If you are using HassOS with the addon, the URL should be one of the following depending on which addon version you are using. Note that if you are using the Proxy Addon, you do NOT point the integration at the proxy URL. Just enter the URL used to access Frigate directly from your network.
|
||||
|
||||
| Addon Version | URL |
|
||||
| ------------------------------ | -------------------------------------- |
|
||||
@@ -64,7 +64,7 @@ Home Assistant > Configuration > Integrations > Frigate > Options
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| RTMP URL Template | A [jinja2](https://jinja.palletsprojects.com/) template that is used to override the standard RTMP stream URL (e.g. for use with reverse proxies). This option is only shown to users who have [advanced mode](https://www.home-assistant.io/blog/2019/07/17/release-96/#advanced-mode) enabled. See [RTMP streams](#streams) below. |
|
||||
| RTSP URL Template | A [jinja2](https://jinja.palletsprojects.com/) template that is used to override the standard RTMP stream URL (e.g. for use with reverse proxies). This option is only shown to users who have [advanced mode](https://www.home-assistant.io/blog/2019/07/17/release-96/#advanced-mode) enabled. See [RTSP streams](#streams) below. |
|
||||
|
||||
## Entities Provided
|
||||
|
||||
@@ -124,13 +124,17 @@ https://HA_URL/api/frigate/notifications/<event-id>/clip.mp4
|
||||
|
||||
## RTMP stream
|
||||
|
||||
In order for the live streams to function they need to be accessible on the RTMP
|
||||
port (default: `1935`) at `<frigatehost>:1935`. Home Assistant will directly
|
||||
RTMP is deprecated and it is recommended to switch to use RTSP restreams.
|
||||
|
||||
## RTSP stream
|
||||
|
||||
In order for the live streams to function they need to be accessible on the RTSP
|
||||
port (default: `8554`) at `<frigatehost>:8554`. Home Assistant will directly
|
||||
connect to that streaming port when the live camera is viewed.
|
||||
|
||||
#### RTMP URL Template
|
||||
#### RTSP URL Template
|
||||
|
||||
For advanced usecases, this behavior can be changed with the [RTMP URL
|
||||
For advanced usecases, this behavior can be changed with the [RTSP URL
|
||||
template](#options) option. When set, this string will override the default stream
|
||||
address that is derived from the default behavior described above. This option supports
|
||||
[jinja2 templates](https://jinja.palletsprojects.com/) and has the `camera` dict
|
||||
@@ -142,24 +146,24 @@ This is potentially useful when Frigate is behind a reverse proxy, and/or when
|
||||
the default stream port is otherwise not accessible to Home Assistant (e.g.
|
||||
firewall rules).
|
||||
|
||||
###### RTMP URL Template Examples
|
||||
###### RTSP URL Template Examples
|
||||
|
||||
Use a different port number:
|
||||
|
||||
```
|
||||
rtmp://<frigate_host>:2000/live/front_door
|
||||
rtsp://<frigate_host>:2000/front_door
|
||||
```
|
||||
|
||||
Use the camera name in the stream URL:
|
||||
|
||||
```
|
||||
rtmp://<frigate_host>:2000/live/{{ name }}
|
||||
rtsp://<frigate_host>:2000/{{ name }}
|
||||
```
|
||||
|
||||
Use the camera name in the stream URL, converting it to lowercase first:
|
||||
|
||||
```
|
||||
rtmp://<frigate_host>:2000/live/{{ name|lower }}
|
||||
rtsp://<frigate_host>:2000/{{ name|lower }}
|
||||
```
|
||||
|
||||
## Multiple Instance Support
|
||||
|
||||
@@ -8,12 +8,12 @@ These are the MQTT messages generated by Frigate. The default topic_prefix is `f
|
||||
### `frigate/available`
|
||||
|
||||
Designed to be used as an availability topic with Home Assistant. Possible message are:
|
||||
"online": published when frigate is running (on startup)
|
||||
"offline": published right before frigate stops
|
||||
"online": published when Frigate is running (on startup)
|
||||
"offline": published right before Frigate stops
|
||||
|
||||
### `frigate/restart`
|
||||
|
||||
Causes frigate to exit. Docker should be configured to automatically restart the container on exit.
|
||||
Causes Frigate to exit. Docker should be configured to automatically restart the container on exit.
|
||||
|
||||
### `frigate/<camera_name>/<object_name>`
|
||||
|
||||
@@ -34,7 +34,7 @@ The height and crop of snapshots can be configured in the config.
|
||||
|
||||
### `frigate/events`
|
||||
|
||||
Message published for each changed event. The first message is published when the tracked object is no longer marked as a false_positive. When frigate finds a better snapshot of the tracked object or when a zone change occurs, it will publish a message with the same id. When the event ends, a final message is published with `end_time` set.
|
||||
Message published for each changed event. The first message is published when the tracked object is no longer marked as a false_positive. When Frigate finds a better snapshot of the tracked object or when a zone change occurs, it will publish a message with the same id. When the event ends, a final message is published with `end_time` set.
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -7,38 +7,25 @@ title: Frequently Asked Questions
|
||||
|
||||
This error message is due to a shm-size that is too small. Try updating your shm-size according to [this guide](../frigate/installation.md#calculating-required-shm-size).
|
||||
|
||||
### I am seeing a solid green image for my camera.
|
||||
|
||||
A solid green image means that frigate has not received any frames from ffmpeg. Check the logs to see why ffmpeg is exiting and adjust your ffmpeg args accordingly.
|
||||
|
||||
### How can I get sound or audio in my recordings? {#audio-in-recordings}
|
||||
|
||||
By default, Frigate removes audio from recordings to reduce the likelihood of failing for invalid data. If you would like to include audio, you need to override the output args to remove `-an` for where you want to include audio. The recommended audio codec is `aac`. Not all audio codecs are supported by RTMP, so you may need to re-encode your audio with `-c:a aac`. The default ffmpeg args are shown [here](../configuration/index.md/#full-configuration-reference).
|
||||
By default, Frigate removes audio from recordings to reduce the likelihood of failing for invalid data. If you would like to include audio, you need to set a [FFmpeg preset](/configuration/ffmpeg_presets) that supports audio:
|
||||
|
||||
:::tip
|
||||
|
||||
When using `-c:a aac`, do not forget to replace `-c copy` with `-c:v copy`. Example:
|
||||
|
||||
```diff title="frigate.yml"
|
||||
```yaml title="frigate.yml"
|
||||
ffmpeg:
|
||||
output_args:
|
||||
- record: -f segment -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1 -c copy -an
|
||||
+ record: -f segment -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1 -c:v copy -c:a aac
|
||||
record: preset-record-generic-audio-aac
|
||||
```
|
||||
|
||||
This is needed because the `-c` flag (without `:a` or `:v`) applies for both audio and video, thus making it conflicting with `-c:a aac`.
|
||||
|
||||
:::
|
||||
|
||||
### My mjpeg stream or snapshots look green and crazy
|
||||
|
||||
This almost always means that the width/height defined for your camera are not correct. Double check the resolution with vlc or another player. Also make sure you don't have the width and height values backwards.
|
||||
This almost always means that the width/height defined for your camera are not correct. Double check the resolution with VLC or another player. Also make sure you don't have the width and height values backwards.
|
||||
|
||||

|
||||
|
||||
### I can't view events or recordings in the Web UI.
|
||||
|
||||
Ensure your cameras send h264 encoded video
|
||||
Ensure your cameras send h264 encoded video, or [transcode them](/configuration/restream.md).
|
||||
|
||||
### "[mov,mp4,m4a,3gp,3g2,mj2 @ 0x5639eeb6e140] moov atom not found"
|
||||
|
||||
@@ -46,8 +33,8 @@ These messages in the logs are expected in certain situations. Frigate checks th
|
||||
|
||||
### "On connect called"
|
||||
|
||||
If you see repeated "On connect called" messages in your config, 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 "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`.
|
||||
|
||||
### Error: Database Is Locked
|
||||
|
||||
sqlite does not work well on a network share, if the `/media` folder is mapped to a network share then [this guide](../configuration/advanced.md#database) should be used to move the database to a location on the internal drive.
|
||||
SQLite does not work well on a network share, if the `/media` folder is mapped to a network share then [this guide](../configuration/advanced.md#database) should be used to move the database to a location on the internal drive.
|
||||
|
||||
@@ -4,11 +4,11 @@ module.exports = {
|
||||
"frigate/index",
|
||||
"frigate/hardware",
|
||||
"frigate/installation",
|
||||
"frigate/camera_setup",
|
||||
],
|
||||
Guides: [
|
||||
"guides/camera_setup",
|
||||
"guides/getting_started",
|
||||
"guides/events_setup",
|
||||
"guides/configuring_go2rtc",
|
||||
"guides/false_positives",
|
||||
"guides/ha_notifications",
|
||||
"guides/stationary_objects",
|
||||
|
||||
@@ -3,6 +3,7 @@ import multiprocessing as mp
|
||||
from multiprocessing.queues import Queue
|
||||
from multiprocessing.synchronize import Event as MpEvent
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import sys
|
||||
from typing import Optional
|
||||
@@ -116,6 +117,9 @@ class FrigateApp:
|
||||
if not "werkzeug" in self.config.logger.logs:
|
||||
logging.getLogger("werkzeug").setLevel("ERROR")
|
||||
|
||||
if not "ws4py" in self.config.logger.logs:
|
||||
logging.getLogger("ws4py").setLevel("ERROR")
|
||||
|
||||
def init_queues(self) -> None:
|
||||
# Queues for clip processing
|
||||
self.event_queue: Queue = mp.Queue()
|
||||
@@ -155,7 +159,9 @@ class FrigateApp:
|
||||
self.db.bind(models)
|
||||
|
||||
def init_stats(self) -> None:
|
||||
self.stats_tracking = stats_init(self.camera_metrics, self.detectors)
|
||||
self.stats_tracking = stats_init(
|
||||
self.config, self.camera_metrics, self.detectors
|
||||
)
|
||||
|
||||
def init_web_server(self) -> None:
|
||||
self.flask_app = create_app(
|
||||
@@ -327,6 +333,22 @@ class FrigateApp:
|
||||
self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
|
||||
self.frigate_watchdog.start()
|
||||
|
||||
def check_shm(self) -> None:
|
||||
available_shm = round(shutil.disk_usage("/dev/shm").total / 1000000, 1)
|
||||
min_req_shm = 30
|
||||
|
||||
for _, camera in self.config.cameras.items():
|
||||
min_req_shm += round(
|
||||
(camera.detect.width * camera.detect.height * 1.5 * 9 + 270480)
|
||||
/ 1048576,
|
||||
1,
|
||||
)
|
||||
|
||||
if available_shm < min_req_shm:
|
||||
logger.warning(
|
||||
f"The current SHM size of {available_shm}MB is too small, recommend increasing it to at least {min_req_shm}MB."
|
||||
)
|
||||
|
||||
def start(self) -> None:
|
||||
self.init_logger()
|
||||
logger.info(f"Starting Frigate ({VERSION})")
|
||||
@@ -375,6 +397,7 @@ class FrigateApp:
|
||||
self.start_recording_cleanup()
|
||||
self.start_stats_emitter()
|
||||
self.start_watchdog()
|
||||
self.check_shm()
|
||||
# self.zeroconf = broadcast_zeroconf(self.config.mqtt.client_id)
|
||||
|
||||
def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Handle communication between frigate and other applications."""
|
||||
"""Handle communication between Frigate and other applications."""
|
||||
|
||||
import logging
|
||||
|
||||
@@ -29,7 +29,7 @@ class Communicator(ABC):
|
||||
|
||||
|
||||
class Dispatcher:
|
||||
"""Handle communication between frigate and communicators."""
|
||||
"""Handle communication between Frigate and communicators."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -139,33 +139,28 @@ class MqttClient(Communicator): # type: ignore[misc]
|
||||
)
|
||||
|
||||
# register callbacks
|
||||
callback_types = [
|
||||
"recordings",
|
||||
"snapshots",
|
||||
"detect",
|
||||
"motion",
|
||||
"improve_contrast",
|
||||
"motion_threshold",
|
||||
"motion_contour_area",
|
||||
]
|
||||
|
||||
for name in self.config.cameras.keys():
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/recordings/set",
|
||||
self.on_mqtt_command,
|
||||
for callback in callback_types:
|
||||
# We need to pre-clear existing set topics because in previous
|
||||
# versions the webUI retained on the /set topic but this is
|
||||
# no longer the case.
|
||||
self.client.publish(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/{callback}/set",
|
||||
None,
|
||||
retain=True,
|
||||
)
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/snapshots/set",
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/detect/set",
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/motion/set",
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/improve_contrast/set",
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/motion_threshold/set",
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
self.client.message_callback_add(
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/motion_contour_area/set",
|
||||
f"{self.mqtt_config.topic_prefix}/{name}/{callback}/set",
|
||||
self.on_mqtt_command,
|
||||
)
|
||||
|
||||
|
||||
@@ -60,10 +60,24 @@ class FrigateBaseModel(BaseModel):
|
||||
extra = Extra.forbid
|
||||
|
||||
|
||||
class LiveModeEnum(str, Enum):
|
||||
jsmpeg = "jsmpeg"
|
||||
mse = "mse"
|
||||
webrtc = "webrtc"
|
||||
|
||||
|
||||
class UIConfig(FrigateBaseModel):
|
||||
live_mode: LiveModeEnum = Field(
|
||||
default=LiveModeEnum.mse, title="Default Live Mode."
|
||||
)
|
||||
timezone: Optional[str] = Field(title="Override UI timezone.")
|
||||
use_experimental: bool = Field(default=False, title="Experimental UI")
|
||||
|
||||
|
||||
class TelemetryConfig(FrigateBaseModel):
|
||||
version_check: bool = Field(default=True, title="Enable latest version check.")
|
||||
|
||||
|
||||
class MqttConfig(FrigateBaseModel):
|
||||
enabled: bool = Field(title="Enable MQTT Communication.", default=True)
|
||||
host: str = Field(default="", title="MQTT Host")
|
||||
@@ -334,6 +348,7 @@ class BirdseyeModeEnum(str, Enum):
|
||||
|
||||
class BirdseyeConfig(FrigateBaseModel):
|
||||
enabled: bool = Field(default=True, title="Enable birdseye view.")
|
||||
restream: bool = Field(default=False, title="Restream birdseye via RTSP.")
|
||||
width: int = Field(default=1280, title="Birdseye width.")
|
||||
height: int = Field(default=720, title="Birdseye height.")
|
||||
quality: int = Field(
|
||||
@@ -395,7 +410,6 @@ class FfmpegConfig(FrigateBaseModel):
|
||||
|
||||
class CameraRoleEnum(str, Enum):
|
||||
record = "record"
|
||||
restream = "restream"
|
||||
rtmp = "rtmp"
|
||||
detect = "detect"
|
||||
|
||||
@@ -509,29 +523,15 @@ class RtmpConfig(FrigateBaseModel):
|
||||
enabled: bool = Field(default=False, title="RTMP restreaming enabled.")
|
||||
|
||||
|
||||
class JsmpegStreamConfig(FrigateBaseModel):
|
||||
height: int = Field(default=720, title="Live camera view height.")
|
||||
quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality.")
|
||||
class CameraLiveConfig(FrigateBaseModel):
|
||||
stream_name: str = Field(default="", title="Name of restream to use as live view.")
|
||||
height: int = Field(default=720, title="Live camera view height")
|
||||
quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality")
|
||||
|
||||
|
||||
class RestreamCodecEnum(str, Enum):
|
||||
copy = "copy"
|
||||
h264 = "h264"
|
||||
h265 = "h265"
|
||||
|
||||
|
||||
class RestreamConfig(FrigateBaseModel):
|
||||
enabled: bool = Field(default=True, title="Restreaming enabled.")
|
||||
video_encoding: RestreamCodecEnum = Field(
|
||||
default=RestreamCodecEnum.copy, title="Method for encoding the restream."
|
||||
)
|
||||
force_audio: bool = Field(
|
||||
default=True, title="Force audio compatibility with the browser."
|
||||
)
|
||||
birdseye: bool = Field(default=False, title="Restream the birdseye feed via RTSP.")
|
||||
jsmpeg: JsmpegStreamConfig = Field(
|
||||
default_factory=JsmpegStreamConfig, title="Jsmpeg Stream Configuration."
|
||||
)
|
||||
class RestreamConfig(BaseModel):
|
||||
class Config:
|
||||
extra = Extra.allow
|
||||
|
||||
|
||||
class CameraUiConfig(FrigateBaseModel):
|
||||
@@ -558,8 +558,8 @@ class CameraConfig(FrigateBaseModel):
|
||||
rtmp: RtmpConfig = Field(
|
||||
default_factory=RtmpConfig, title="RTMP restreaming configuration."
|
||||
)
|
||||
restream: RestreamConfig = Field(
|
||||
default_factory=RestreamConfig, title="Restreaming configuration."
|
||||
live: CameraLiveConfig = Field(
|
||||
default_factory=CameraLiveConfig, title="Live playback settings."
|
||||
)
|
||||
snapshots: SnapshotsConfig = Field(
|
||||
default_factory=SnapshotsConfig, title="Snapshot configuration."
|
||||
@@ -601,7 +601,6 @@ class CameraConfig(FrigateBaseModel):
|
||||
config["ffmpeg"]["inputs"][0]["roles"] = [
|
||||
"record",
|
||||
"detect",
|
||||
"restream",
|
||||
]
|
||||
|
||||
if has_rtmp:
|
||||
@@ -738,9 +737,17 @@ def verify_config_roles(camera_config: CameraConfig) -> None:
|
||||
f"Camera {camera_config.name} has rtmp enabled, but rtmp is not assigned to an input."
|
||||
)
|
||||
|
||||
if camera_config.restream.enabled and not "restream" in assigned_roles:
|
||||
raise ValueError(
|
||||
f"Camera {camera_config.name} has restream enabled, but restream is not assigned to an input."
|
||||
|
||||
def verify_valid_live_stream_name(
|
||||
frigate_config: FrigateConfig, camera_config: CameraConfig
|
||||
) -> None:
|
||||
"""Verify that a restream exists to use for live view."""
|
||||
if (
|
||||
camera_config.live.stream_name
|
||||
not in frigate_config.go2rtc.dict().get("streams", {}).keys()
|
||||
):
|
||||
return ValueError(
|
||||
f"No restream with name {camera_config.live.stream_name} exists for camera {camera_config.name}."
|
||||
)
|
||||
|
||||
|
||||
@@ -815,6 +822,9 @@ class FrigateConfig(FrigateBaseModel):
|
||||
default_factory=dict, title="Frigate environment variables."
|
||||
)
|
||||
ui: UIConfig = Field(default_factory=UIConfig, title="UI configuration.")
|
||||
telemetry: TelemetryConfig = Field(
|
||||
default_factory=TelemetryConfig, title="Telemetry configuration."
|
||||
)
|
||||
model: ModelConfig = Field(
|
||||
default_factory=ModelConfig, title="Detection model configuration."
|
||||
)
|
||||
@@ -834,7 +844,10 @@ class FrigateConfig(FrigateBaseModel):
|
||||
rtmp: RtmpConfig = Field(
|
||||
default_factory=RtmpConfig, title="Global RTMP restreaming configuration."
|
||||
)
|
||||
restream: RestreamConfig = Field(
|
||||
live: CameraLiveConfig = Field(
|
||||
default_factory=CameraLiveConfig, title="Live playback settings."
|
||||
)
|
||||
go2rtc: RestreamConfig = Field(
|
||||
default_factory=RestreamConfig, title="Global restream configuration."
|
||||
)
|
||||
birdseye: BirdseyeConfig = Field(
|
||||
@@ -875,7 +888,7 @@ class FrigateConfig(FrigateBaseModel):
|
||||
"record": ...,
|
||||
"snapshots": ...,
|
||||
"rtmp": ...,
|
||||
"restream": ...,
|
||||
"live": ...,
|
||||
"objects": ...,
|
||||
"motion": ...,
|
||||
"detect": ...,
|
||||
@@ -948,7 +961,12 @@ class FrigateConfig(FrigateBaseModel):
|
||||
**camera_config.motion.dict(exclude_unset=True),
|
||||
)
|
||||
|
||||
# Set live view stream if none is set
|
||||
if not camera_config.live.stream_name:
|
||||
camera_config.live.stream_name = name
|
||||
|
||||
verify_config_roles(camera_config)
|
||||
verify_valid_live_stream_name(config, camera_config)
|
||||
verify_old_retain_config(camera_config)
|
||||
verify_recording_retention(camera_config)
|
||||
verify_recording_segments_setup_with_reasonable_time(camera_config)
|
||||
|
||||
@@ -75,12 +75,6 @@ class TensorRtDetector(DetectionApi):
|
||||
|
||||
def _load_engine(self, model_path):
|
||||
try:
|
||||
ctypes.cdll.LoadLibrary(
|
||||
"/usr/local/lib/python3.9/dist-packages/nvidia/cuda_runtime/lib/libcudart.so.11.0"
|
||||
)
|
||||
ctypes.cdll.LoadLibrary(
|
||||
"/usr/local/lib/python3.9/dist-packages/tensorrt/libnvinfer.so.8"
|
||||
)
|
||||
trt.init_libnvinfer_plugins(self.trt_logger, "")
|
||||
|
||||
ctypes.cdll.LoadLibrary("/trt-models/libyolo_layer.so")
|
||||
|
||||
@@ -38,6 +38,8 @@ PRESETS_HW_ACCEL_DECODE = {
|
||||
"h264_qsv",
|
||||
],
|
||||
"preset-intel-qsv-h265": [
|
||||
"-load_plugin",
|
||||
"hevc_hw",
|
||||
"-hwaccel",
|
||||
"qsv",
|
||||
"-qsv_device",
|
||||
@@ -80,44 +82,14 @@ PRESETS_HW_ACCEL_DECODE = {
|
||||
}
|
||||
|
||||
PRESETS_HW_ACCEL_SCALE = {
|
||||
"preset-rpi-32-h264": ["-f", "rawvideo", "-pix_fmt", "yuv420p"],
|
||||
"preset-rpi-64-h264": ["-f", "rawvideo", "-pix_fmt", "yuv420p"],
|
||||
"preset-vaapi": [
|
||||
"-vf",
|
||||
"fps={},scale_vaapi=w={}:h={},hwdownload,format=yuv420p",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
],
|
||||
"preset-intel-qsv-h264": [
|
||||
"-vf",
|
||||
"vpp_qsv=framerate={},scale_qsv=w={}:h={}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
],
|
||||
"preset-intel-qsv-h265": [
|
||||
"-vf",
|
||||
"vpp_qsv=framerate={},scale_qsv=w={}:h={}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
],
|
||||
"preset-nvidia-h264": [
|
||||
"-vf",
|
||||
"fps={},scale_cuda=w={}:h={}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
],
|
||||
"preset-nvidia-h265": [
|
||||
"-vf",
|
||||
"fps={},scale_cuda=w={}:h={}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
],
|
||||
"default": [
|
||||
"-r",
|
||||
"{}",
|
||||
"-s",
|
||||
"{}",
|
||||
],
|
||||
"preset-rpi-32-h264": "-r {0} -s {1}x{2} -f rawvideo -pix_fmt yuv420p",
|
||||
"preset-rpi-64-h264": "-r {0} -s {1}x{2} -f rawvideo -pix_fmt yuv420p",
|
||||
"preset-vaapi": "-r {0} -vf fps={0},scale_vaapi=w={1}:h={2},hwdownload,format=yuv420p -f rawvideo",
|
||||
"preset-intel-qsv-h264": "-r {0} -vf vpp_qsv=framerate={0}:w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p -f rawvideo",
|
||||
"preset-intel-qsv-h265": "-r {0} -vf vpp_qsv=framerate={0}:w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p -f rawvideo",
|
||||
"preset-nvidia-h264": "-r {0} -vf fps={0},scale_cuda=w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p -f rawvideo",
|
||||
"preset-nvidia-h265": "-r {0} -vf fps={0},scale_cuda=w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p -f rawvideo",
|
||||
"default": "-r {0} -s {1}x{2}",
|
||||
}
|
||||
|
||||
PRESETS_HW_ACCEL_ENCODE = {
|
||||
@@ -130,17 +102,6 @@ PRESETS_HW_ACCEL_ENCODE = {
|
||||
"default": "ffmpeg -hide_banner {0} -c:v libx264 -g 50 -profile:v high -level:v 4.1 -preset:v superfast -tune:v zerolatency {1}",
|
||||
}
|
||||
|
||||
PRESETS_HW_ACCEL_GO2RTC_ENGINE = {
|
||||
"preset-rpi-32-h264": "v4l2m2m",
|
||||
"preset-rpi-64-h264": "v4l2m2m",
|
||||
"preset-intel-vaapi": "vaapi",
|
||||
"preset-intel-qsv-h264": "vaapi", # go2rtc doesn't support qsv
|
||||
"preset-intel-qsv-h265": "vaapi",
|
||||
"preset-amd-vaapi": "vaapi",
|
||||
"preset-nvidia-h264": "cuda",
|
||||
"preset-nvidia-h265": "cuda",
|
||||
}
|
||||
|
||||
|
||||
def parse_preset_hardware_acceleration_decode(arg: Any) -> list[str]:
|
||||
"""Return the correct preset if in preset format otherwise return None."""
|
||||
@@ -159,14 +120,17 @@ def parse_preset_hardware_acceleration_scale(
|
||||
) -> list[str]:
|
||||
"""Return the correct scaling preset or default preset if none is set."""
|
||||
if not isinstance(arg, str) or " " in arg:
|
||||
scale = PRESETS_HW_ACCEL_SCALE["default"].copy()
|
||||
scale[1] = str(fps)
|
||||
scale[3] = f"{width}x{height}"
|
||||
scale = PRESETS_HW_ACCEL_SCALE["default"].format(fps, width, height).split(" ")
|
||||
scale.extend(detect_args)
|
||||
return scale
|
||||
|
||||
scale = PRESETS_HW_ACCEL_SCALE.get(arg, PRESETS_HW_ACCEL_SCALE["default"]).copy()
|
||||
scale[1] = scale[1].format(fps, width, height)
|
||||
scale = PRESETS_HW_ACCEL_SCALE.get(arg, "")
|
||||
|
||||
if scale:
|
||||
return scale.format(fps, width, height).split(" ")
|
||||
else:
|
||||
scale = scale.format(fps, width, height).split(" ")
|
||||
scale.extend(detect_args)
|
||||
return scale
|
||||
|
||||
|
||||
@@ -181,14 +145,6 @@ def parse_preset_hardware_acceleration_encode(arg: Any, input: str, output: str)
|
||||
)
|
||||
|
||||
|
||||
def parse_preset_hardware_acceleration_go2rtc_engine(arg: Any) -> list[str]:
|
||||
"""Return the correct engine for the preset otherwise returns None."""
|
||||
if not isinstance(arg, str):
|
||||
return None
|
||||
|
||||
return PRESETS_HW_ACCEL_GO2RTC_ENGINE.get(arg)
|
||||
|
||||
|
||||
PRESETS_INPUT = {
|
||||
"preset-http-jpeg-generic": _user_agent_args
|
||||
+ [
|
||||
@@ -274,6 +230,17 @@ PRESETS_INPUT = {
|
||||
"-use_wallclock_as_timestamps",
|
||||
"1",
|
||||
],
|
||||
"preset-rtsp-restream": _user_agent_args
|
||||
+ [
|
||||
"-rtsp_transport",
|
||||
"tcp",
|
||||
TIMEOUT_PARAM,
|
||||
"5000000",
|
||||
"-fflags",
|
||||
"nobuffer",
|
||||
"-flags",
|
||||
"low_delay",
|
||||
],
|
||||
"preset-rtsp-udp": _user_agent_args
|
||||
+ [
|
||||
"-avoid_negative_ts",
|
||||
@@ -338,7 +305,7 @@ PRESETS_RECORD_OUTPUT = {
|
||||
"copy",
|
||||
"-an",
|
||||
],
|
||||
"preset-record-generic-audio": [
|
||||
"preset-record-generic-audio-aac": [
|
||||
"-f",
|
||||
"segment",
|
||||
"-segment_time",
|
||||
@@ -354,6 +321,20 @@ PRESETS_RECORD_OUTPUT = {
|
||||
"-c:a",
|
||||
"aac",
|
||||
],
|
||||
"preset-record-generic-audio-copy": [
|
||||
"-f",
|
||||
"segment",
|
||||
"-segment_time",
|
||||
"10",
|
||||
"-segment_format",
|
||||
"mp4",
|
||||
"-reset_timestamps",
|
||||
"1",
|
||||
"-strftime",
|
||||
"1",
|
||||
"-c",
|
||||
"copy",
|
||||
],
|
||||
"preset-record-mjpeg": [
|
||||
"-f",
|
||||
"segment",
|
||||
|
||||
@@ -41,6 +41,7 @@ from frigate.util import (
|
||||
ffprobe_stream,
|
||||
restart_frigate,
|
||||
vainfo_hwaccel,
|
||||
get_tz_modifiers,
|
||||
)
|
||||
from frigate.storage import StorageMaintainer
|
||||
from frigate.version import VERSION
|
||||
@@ -91,7 +92,7 @@ def is_healthy():
|
||||
@bp.route("/events/summary")
|
||||
def events_summary():
|
||||
tz_name = request.args.get("timezone", default="utc", type=str)
|
||||
tz_offset = f"{int(datetime.now(pytz.timezone(tz_name)).utcoffset().total_seconds()/60/60)} hour"
|
||||
hour_modifier, minute_modifier = get_tz_modifiers(tz_name)
|
||||
has_clip = request.args.get("has_clip", type=int)
|
||||
has_snapshot = request.args.get("has_snapshot", type=int)
|
||||
|
||||
@@ -111,7 +112,10 @@ def events_summary():
|
||||
Event.camera,
|
||||
Event.label,
|
||||
fn.strftime(
|
||||
"%Y-%m-%d", fn.datetime(Event.start_time, "unixepoch", tz_offset)
|
||||
"%Y-%m-%d",
|
||||
fn.datetime(
|
||||
Event.start_time, "unixepoch", hour_modifier, minute_modifier
|
||||
),
|
||||
).alias("day"),
|
||||
Event.zones,
|
||||
fn.COUNT(Event.id).alias("count"),
|
||||
@@ -121,7 +125,10 @@ def events_summary():
|
||||
Event.camera,
|
||||
Event.label,
|
||||
fn.strftime(
|
||||
"%Y-%m-%d", fn.datetime(Event.start_time, "unixepoch", tz_offset)
|
||||
"%Y-%m-%d",
|
||||
fn.datetime(
|
||||
Event.start_time, "unixepoch", hour_modifier, minute_modifier
|
||||
),
|
||||
),
|
||||
Event.zones,
|
||||
)
|
||||
@@ -710,6 +717,8 @@ def config_raw():
|
||||
|
||||
@bp.route("/config/save", methods=["POST"])
|
||||
def config_save():
|
||||
save_option = request.args.get("save_option")
|
||||
|
||||
new_config = request.get_data().decode()
|
||||
|
||||
if not new_config:
|
||||
@@ -747,19 +756,25 @@ def config_save():
|
||||
jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": f"Could not write config file, be sure that frigate has write permission on the config file.",
|
||||
"message": f"Could not write config file, be sure that Frigate has write permission on the config file.",
|
||||
}
|
||||
),
|
||||
400,
|
||||
)
|
||||
|
||||
if save_option == "restart":
|
||||
try:
|
||||
restart_frigate()
|
||||
except Exception as e:
|
||||
logging.error(f"Error restarting frigate: {e}")
|
||||
return "Config successfully saved, unable to restart frigate", 200
|
||||
logging.error(f"Error restarting Frigate: {e}")
|
||||
return "Config successfully saved, unable to restart Frigate", 200
|
||||
|
||||
return "Config successfully saved, restarting...", 200
|
||||
return (
|
||||
"Config successfully saved, restarting (this can take up to one minute)...",
|
||||
200,
|
||||
)
|
||||
else:
|
||||
return "Config successfully saved.", 200
|
||||
|
||||
|
||||
@bp.route("/config/schema.json")
|
||||
@@ -855,7 +870,7 @@ def latest_frame(camera_name):
|
||||
response.headers["Content-Type"] = "image/jpeg"
|
||||
response.headers["Cache-Control"] = "no-store"
|
||||
return response
|
||||
elif camera_name == "birdseye" and current_app.frigate_config.restream.birdseye:
|
||||
elif camera_name == "birdseye" and current_app.frigate_config.birdseye.restream:
|
||||
frame = cv2.cvtColor(
|
||||
current_app.detected_frames_processor.get_current_frame(camera_name),
|
||||
cv2.COLOR_YUV2BGR_I420,
|
||||
@@ -884,6 +899,10 @@ def get_recordings_storage_usage():
|
||||
current_app.stats_tracking,
|
||||
current_app.hwaccel_errors,
|
||||
)["service"]["storage"][RECORD_DIR]
|
||||
|
||||
if not recording_stats:
|
||||
return jsonify({})
|
||||
|
||||
total_mb = recording_stats["total"]
|
||||
|
||||
camera_usages: dict[
|
||||
@@ -903,12 +922,14 @@ def get_recordings_storage_usage():
|
||||
@bp.route("/<camera_name>/recordings/summary")
|
||||
def recordings_summary(camera_name):
|
||||
tz_name = request.args.get("timezone", default="utc", type=str)
|
||||
tz_offset = f"{int(datetime.now(pytz.timezone(tz_name)).utcoffset().total_seconds()/60/60)} hour"
|
||||
hour_modifier, minute_modifier = get_tz_modifiers(tz_name)
|
||||
recording_groups = (
|
||||
Recordings.select(
|
||||
fn.strftime(
|
||||
"%Y-%m-%d %H",
|
||||
fn.datetime(Recordings.start_time, "unixepoch", tz_offset),
|
||||
fn.datetime(
|
||||
Recordings.start_time, "unixepoch", hour_modifier, minute_modifier
|
||||
),
|
||||
).alias("hour"),
|
||||
fn.SUM(Recordings.duration).alias("duration"),
|
||||
fn.SUM(Recordings.motion).alias("motion"),
|
||||
@@ -918,13 +939,17 @@ def recordings_summary(camera_name):
|
||||
.group_by(
|
||||
fn.strftime(
|
||||
"%Y-%m-%d %H",
|
||||
fn.datetime(Recordings.start_time, "unixepoch", tz_offset),
|
||||
fn.datetime(
|
||||
Recordings.start_time, "unixepoch", hour_modifier, minute_modifier
|
||||
),
|
||||
)
|
||||
)
|
||||
.order_by(
|
||||
fn.strftime(
|
||||
"%Y-%m-%d H",
|
||||
fn.datetime(Recordings.start_time, "unixepoch", tz_offset),
|
||||
fn.datetime(
|
||||
Recordings.start_time, "unixepoch", hour_modifier, minute_modifier
|
||||
),
|
||||
).desc()
|
||||
)
|
||||
)
|
||||
@@ -933,7 +958,9 @@ def recordings_summary(camera_name):
|
||||
Event.select(
|
||||
fn.strftime(
|
||||
"%Y-%m-%d %H",
|
||||
fn.datetime(Event.start_time, "unixepoch", tz_offset),
|
||||
fn.datetime(
|
||||
Event.start_time, "unixepoch", hour_modifier, minute_modifier
|
||||
),
|
||||
).alias("hour"),
|
||||
fn.COUNT(Event.id).alias("count"),
|
||||
)
|
||||
@@ -941,7 +968,9 @@ def recordings_summary(camera_name):
|
||||
.group_by(
|
||||
fn.strftime(
|
||||
"%Y-%m-%d %H",
|
||||
fn.datetime(Event.start_time, "unixepoch", tz_offset),
|
||||
fn.datetime(
|
||||
Event.start_time, "unixepoch", hour_modifier, minute_modifier
|
||||
),
|
||||
),
|
||||
)
|
||||
.objects()
|
||||
@@ -1138,17 +1167,11 @@ def vod_hour_no_timezone(year_month, day, hour, camera_name):
|
||||
# TODO make this nicer when vod module is removed
|
||||
@bp.route("/vod/<year_month>/<day>/<hour>/<camera_name>/<tz_name>")
|
||||
def vod_hour(year_month, day, hour, camera_name, tz_name):
|
||||
tz_offset = int(
|
||||
datetime.now(pytz.timezone(tz_name.replace(",", "/")))
|
||||
.utcoffset()
|
||||
.total_seconds()
|
||||
/ 60
|
||||
/ 60
|
||||
)
|
||||
parts = year_month.split("-")
|
||||
start_date = datetime(
|
||||
int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc
|
||||
) - timedelta(hours=tz_offset)
|
||||
start_date = (
|
||||
datetime(int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc)
|
||||
- datetime.now(pytz.timezone(tz_name.replace(",", "/"))).utcoffset()
|
||||
)
|
||||
end_date = start_date + timedelta(hours=1) - timedelta(milliseconds=1)
|
||||
start_ts = start_date.timestamp()
|
||||
end_ts = end_date.timestamp()
|
||||
|
||||
@@ -148,7 +148,7 @@ class BirdsEyeFrameManager:
|
||||
self.yuv_shape = (height * 3 // 2, width)
|
||||
self.frame = np.ndarray(self.yuv_shape, dtype=np.uint8)
|
||||
|
||||
# initialize the frame as black and with the frigate logo
|
||||
# initialize the frame as black and with the Frigate logo
|
||||
self.blank_frame = np.zeros(self.yuv_shape, np.uint8)
|
||||
self.blank_frame[:] = 128
|
||||
self.blank_frame[0 : self.frame_shape[0], 0 : self.frame_shape[1]] = 16
|
||||
@@ -176,7 +176,7 @@ class BirdsEyeFrameManager:
|
||||
x_offset : x_offset + transparent_layer.shape[0],
|
||||
] = transparent_layer
|
||||
else:
|
||||
logger.warning("Unable to read frigate logo")
|
||||
logger.warning("Unable to read Frigate logo")
|
||||
|
||||
self.frame[:] = self.blank_frame
|
||||
|
||||
@@ -415,15 +415,15 @@ def output_frames(config: FrigateConfig, video_output_queue):
|
||||
|
||||
for camera, cam_config in config.cameras.items():
|
||||
width = int(
|
||||
cam_config.restream.jsmpeg.height
|
||||
cam_config.live.height
|
||||
* (cam_config.frame_shape[1] / cam_config.frame_shape[0])
|
||||
)
|
||||
converters[camera] = FFMpegConverter(
|
||||
cam_config.frame_shape[1],
|
||||
cam_config.frame_shape[0],
|
||||
width,
|
||||
cam_config.restream.jsmpeg.height,
|
||||
cam_config.restream.jsmpeg.quality,
|
||||
cam_config.live.height,
|
||||
cam_config.live.quality,
|
||||
)
|
||||
broadcasters[camera] = BroadcastThread(
|
||||
camera, converters[camera], websocket_server
|
||||
@@ -436,7 +436,7 @@ def output_frames(config: FrigateConfig, video_output_queue):
|
||||
config.birdseye.width,
|
||||
config.birdseye.height,
|
||||
config.birdseye.quality,
|
||||
config.restream.birdseye,
|
||||
config.birdseye.restream,
|
||||
)
|
||||
broadcasters["birdseye"] = BroadcastThread(
|
||||
"birdseye", converters["birdseye"], websocket_server
|
||||
@@ -449,7 +449,7 @@ def output_frames(config: FrigateConfig, video_output_queue):
|
||||
|
||||
birdseye_manager = BirdsEyeFrameManager(config, frame_manager)
|
||||
|
||||
if config.restream.birdseye:
|
||||
if config.birdseye.restream:
|
||||
birdseye_buffer = frame_manager.create(
|
||||
"birdseye",
|
||||
birdseye_manager.yuv_shape[0] * birdseye_manager.yuv_shape[1],
|
||||
@@ -479,7 +479,7 @@ def output_frames(config: FrigateConfig, video_output_queue):
|
||||
converters[camera].write(frame.tobytes())
|
||||
|
||||
if config.birdseye.enabled and (
|
||||
config.restream.birdseye
|
||||
config.birdseye.restream
|
||||
or any(
|
||||
ws.environ["PATH_INFO"].endswith("birdseye")
|
||||
for ws in websocket_server.manager
|
||||
@@ -494,7 +494,7 @@ def output_frames(config: FrigateConfig, video_output_queue):
|
||||
):
|
||||
frame_bytes = birdseye_manager.frame.tobytes()
|
||||
|
||||
if config.restream.birdseye:
|
||||
if config.birdseye.restream:
|
||||
birdseye_buffer[:] = frame_bytes
|
||||
|
||||
converters["birdseye"].write(frame_bytes)
|
||||
|
||||
@@ -100,19 +100,12 @@ class RecordingMaintainer(threading.Thread):
|
||||
for camera in grouped_recordings.keys():
|
||||
segment_count = len(grouped_recordings[camera])
|
||||
if segment_count > keep_count:
|
||||
####
|
||||
# Need to find a way to tell if these are aging out based on retention settings or if the system is overloaded.
|
||||
####
|
||||
# logger.warning(
|
||||
# f"Too many recording segments in cache for {camera}. Keeping the {keep_count} most recent segments out of {segment_count}, discarding the rest..."
|
||||
# )
|
||||
logger.warning(
|
||||
f"Unable to keep up with recording segments in cache for {camera}. Keeping the {keep_count} most recent segments out of {segment_count} and discarding the rest..."
|
||||
)
|
||||
to_remove = grouped_recordings[camera][:-keep_count]
|
||||
for f in to_remove:
|
||||
cache_path = f["cache_path"]
|
||||
####
|
||||
# Need to find a way to tell if these are aging out based on retention settings or if the system is overloaded.
|
||||
####
|
||||
# logger.warning(f"Discarding a recording segment: {cache_path}")
|
||||
Path(cache_path).unlink(missing_ok=True)
|
||||
self.end_time_cache.pop(cache_path, None)
|
||||
grouped_recordings[camera] = grouped_recordings[camera][-keep_count:]
|
||||
@@ -227,6 +220,19 @@ class RecordingMaintainer(threading.Thread):
|
||||
cache_path,
|
||||
record_mode,
|
||||
)
|
||||
# if it doesn't overlap with an event, go ahead and drop the segment
|
||||
# if it ends more than the configured pre_capture for the camera
|
||||
else:
|
||||
pre_capture = self.config.cameras[
|
||||
camera
|
||||
].record.events.pre_capture
|
||||
most_recently_processed_frame_time = self.recordings_info[
|
||||
camera
|
||||
][-1][0]
|
||||
retain_cutoff = most_recently_processed_frame_time - pre_capture
|
||||
if end_time.timestamp() < retain_cutoff:
|
||||
Path(cache_path).unlink(missing_ok=True)
|
||||
self.end_time_cache.pop(cache_path, None)
|
||||
# else retain days includes this segment
|
||||
else:
|
||||
record_mode = self.config.cameras[camera].record.retain.mode
|
||||
@@ -411,6 +417,10 @@ class RecordingCleanup(threading.Thread):
|
||||
logger.debug(f"Checking tmp clip {p}.")
|
||||
if p.stat().st_mtime < (datetime.datetime.now().timestamp() - 60 * 1):
|
||||
logger.debug("Deleting tmp clip.")
|
||||
|
||||
# empty contents of file before unlinking https://github.com/blakeblackshear/frigate/issues/4769
|
||||
with open(p, "w"):
|
||||
pass
|
||||
p.unlink(missing_ok=True)
|
||||
|
||||
def expire_recordings(self):
|
||||
|
||||
@@ -4,34 +4,15 @@
|
||||
import logging
|
||||
import requests
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from frigate.config import FrigateConfig, RestreamCodecEnum
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.const import BIRDSEYE_PIPE
|
||||
from frigate.ffmpeg_presets import (
|
||||
parse_preset_hardware_acceleration_encode,
|
||||
parse_preset_hardware_acceleration_go2rtc_engine,
|
||||
)
|
||||
from frigate.util import escape_special_characters
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_manual_go2rtc_stream(
|
||||
camera_url: str, codec: RestreamCodecEnum, engine: Optional[str]
|
||||
) -> str:
|
||||
"""Get a manual stream for go2rtc."""
|
||||
if codec == RestreamCodecEnum.copy:
|
||||
return f"ffmpeg:{camera_url}#video=copy#audio=aac#audio=opus"
|
||||
|
||||
if engine:
|
||||
return (
|
||||
f"ffmpeg:{camera_url}#video={codec}#hardware={engine}#audio=aac#audio=opus"
|
||||
)
|
||||
|
||||
return f"ffmpeg:{camera_url}#video={codec}#audio=aac#audio=opus"
|
||||
|
||||
|
||||
class RestreamApi:
|
||||
"""Control go2rtc relay API."""
|
||||
|
||||
@@ -42,30 +23,7 @@ class RestreamApi:
|
||||
"""Add cameras to go2rtc."""
|
||||
self.relays: dict[str, str] = {}
|
||||
|
||||
for cam_name, camera in self.config.cameras.items():
|
||||
if not camera.restream.enabled:
|
||||
continue
|
||||
|
||||
for input in camera.ffmpeg.inputs:
|
||||
if "restream" in input.roles:
|
||||
if (
|
||||
input.path.startswith("rtsp")
|
||||
and not camera.restream.force_audio
|
||||
):
|
||||
self.relays[
|
||||
cam_name
|
||||
] = f"{escape_special_characters(input.path)}#backchannel=0"
|
||||
else:
|
||||
# go2rtc only supports rtsp for direct relay, otherwise ffmpeg is used
|
||||
self.relays[cam_name] = get_manual_go2rtc_stream(
|
||||
escape_special_characters(input.path),
|
||||
camera.restream.video_encoding,
|
||||
parse_preset_hardware_acceleration_go2rtc_engine(
|
||||
self.config.ffmpeg.hwaccel_args
|
||||
),
|
||||
)
|
||||
|
||||
if self.config.restream.birdseye:
|
||||
if self.config.birdseye.restream:
|
||||
self.relays[
|
||||
"birdseye"
|
||||
] = f"exec:{parse_preset_hardware_acceleration_encode(self.config.ffmpeg.hwaccel_args, f'-f rawvideo -pix_fmt yuv420p -video_size {self.config.birdseye.width}x{self.config.birdseye.height} -r 10 -i {BIRDSEYE_PIPE}', '-rtsp_transport tcp -f rtsp {output}')}"
|
||||
|
||||
@@ -22,7 +22,11 @@ from frigate.object_detection import ObjectDetectProcess
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_latest_version() -> str:
|
||||
def get_latest_version(config: FrigateConfig) -> str:
|
||||
|
||||
if not config.telemetry.version_check:
|
||||
return "disabled"
|
||||
|
||||
try:
|
||||
request = requests.get(
|
||||
"https://api.github.com/repos/blakeblackshear/frigate/releases/latest",
|
||||
@@ -40,6 +44,7 @@ def get_latest_version() -> str:
|
||||
|
||||
|
||||
def stats_init(
|
||||
config: FrigateConfig,
|
||||
camera_metrics: dict[str, CameraMetricsTypes],
|
||||
detectors: dict[str, ObjectDetectProcess],
|
||||
) -> StatsTrackingTypes:
|
||||
@@ -47,7 +52,8 @@ def stats_init(
|
||||
"camera_metrics": camera_metrics,
|
||||
"detectors": detectors,
|
||||
"started": int(time.time()),
|
||||
"latest_frigate_version": get_latest_version(),
|
||||
"latest_frigate_version": get_latest_version(config),
|
||||
"last_updated": int(time.time()),
|
||||
}
|
||||
return stats_tracking
|
||||
|
||||
@@ -215,6 +221,7 @@ def stats_snapshot(
|
||||
"process_fps": round(camera_stats["process_fps"].value, 2),
|
||||
"skipped_fps": round(camera_stats["skipped_fps"].value, 2),
|
||||
"detection_fps": round(camera_stats["detection_fps"].value, 2),
|
||||
"detection_enabled": camera_stats["detection_enabled"].value,
|
||||
"pid": pid,
|
||||
"capture_pid": cpid,
|
||||
"ffmpeg_pid": ffmpeg_pid,
|
||||
@@ -238,10 +245,15 @@ def stats_snapshot(
|
||||
"latest_version": stats_tracking["latest_frigate_version"],
|
||||
"storage": {},
|
||||
"temperatures": get_temperatures(),
|
||||
"last_updated": int(time.time()),
|
||||
}
|
||||
|
||||
for path in [RECORD_DIR, CLIPS_DIR, CACHE_DIR, "/dev/shm"]:
|
||||
try:
|
||||
storage_stats = shutil.disk_usage(path)
|
||||
except FileNotFoundError:
|
||||
stats["service"]["storage"][path] = {}
|
||||
|
||||
stats["service"]["storage"][path] = {
|
||||
"total": round(storage_stats.total / 1000000, 1),
|
||||
"used": round(storage_stats.used / 1000000, 1),
|
||||
|
||||
@@ -621,7 +621,7 @@ class TestConfig(unittest.TestCase):
|
||||
"inputs": [
|
||||
{
|
||||
"path": "rtsp://10.0.0.1:554/video",
|
||||
"roles": ["detect", "rtmp", "restream"],
|
||||
"roles": ["detect", "rtmp"],
|
||||
},
|
||||
{"path": "rtsp://10.0.0.1:554/record", "roles": ["record"]},
|
||||
]
|
||||
@@ -883,7 +883,6 @@ class TestConfig(unittest.TestCase):
|
||||
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"restream": {"enabled": False},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
@@ -1096,30 +1095,6 @@ class TestConfig(unittest.TestCase):
|
||||
assert runtime_config.cameras["back"].snapshots.height == 150
|
||||
assert runtime_config.cameras["back"].snapshots.enabled
|
||||
|
||||
def test_global_restream(self):
|
||||
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"restream": {"enabled": True},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
"inputs": [
|
||||
{
|
||||
"path": "rtsp://10.0.0.1:554/video",
|
||||
"roles": ["detect"],
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
frigate_config = FrigateConfig(**config)
|
||||
assert config == frigate_config.dict(exclude_unset=True)
|
||||
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert runtime_config.cameras["back"].restream.enabled
|
||||
|
||||
def test_global_rtmp_disabled(self):
|
||||
|
||||
config = {
|
||||
@@ -1166,56 +1141,6 @@ class TestConfig(unittest.TestCase):
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert not runtime_config.cameras["back"].rtmp.enabled
|
||||
|
||||
def test_default_restream(self):
|
||||
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
"inputs": [
|
||||
{
|
||||
"path": "rtsp://10.0.0.1:554/video",
|
||||
"roles": ["detect"],
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
frigate_config = FrigateConfig(**config)
|
||||
assert config == frigate_config.dict(exclude_unset=True)
|
||||
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert runtime_config.cameras["back"].restream.enabled
|
||||
|
||||
def test_global_restream_merge(self):
|
||||
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"restream": {"enabled": False},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
"inputs": [
|
||||
{
|
||||
"path": "rtsp://10.0.0.1:554/video",
|
||||
"roles": ["detect"],
|
||||
},
|
||||
]
|
||||
},
|
||||
"restream": {
|
||||
"enabled": True,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
frigate_config = FrigateConfig(**config)
|
||||
assert config == frigate_config.dict(exclude_unset=True)
|
||||
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert runtime_config.cameras["back"].restream.enabled
|
||||
|
||||
def test_global_rtmp_merge(self):
|
||||
|
||||
config = {
|
||||
@@ -1247,7 +1172,6 @@ class TestConfig(unittest.TestCase):
|
||||
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"restream": {"enabled": False},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
@@ -1275,7 +1199,7 @@ class TestConfig(unittest.TestCase):
|
||||
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"restream": {"jsmpeg": {"quality": 4}},
|
||||
"live": {"quality": 4},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
@@ -1293,7 +1217,7 @@ class TestConfig(unittest.TestCase):
|
||||
assert config == frigate_config.dict(exclude_unset=True)
|
||||
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert runtime_config.cameras["back"].restream.jsmpeg.quality == 4
|
||||
assert runtime_config.cameras["back"].live.quality == 4
|
||||
|
||||
def test_default_live(self):
|
||||
|
||||
@@ -1316,13 +1240,13 @@ class TestConfig(unittest.TestCase):
|
||||
assert config == frigate_config.dict(exclude_unset=True)
|
||||
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert runtime_config.cameras["back"].restream.jsmpeg.quality == 8
|
||||
assert runtime_config.cameras["back"].live.quality == 8
|
||||
|
||||
def test_global_live_merge(self):
|
||||
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"restream": {"jsmpeg": {"quality": 4, "height": 480}},
|
||||
"live": {"quality": 4, "height": 480},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
@@ -1333,10 +1257,8 @@ class TestConfig(unittest.TestCase):
|
||||
},
|
||||
]
|
||||
},
|
||||
"restream": {
|
||||
"jsmpeg": {
|
||||
"live": {
|
||||
"quality": 7,
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -1345,8 +1267,8 @@ class TestConfig(unittest.TestCase):
|
||||
assert config == frigate_config.dict(exclude_unset=True)
|
||||
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert runtime_config.cameras["back"].restream.jsmpeg.quality == 7
|
||||
assert runtime_config.cameras["back"].restream.jsmpeg.height == 480
|
||||
assert runtime_config.cameras["back"].live.quality == 7
|
||||
assert runtime_config.cameras["back"].live.height == 480
|
||||
|
||||
def test_global_timestamp_style(self):
|
||||
|
||||
|
||||
@@ -136,10 +136,10 @@ class TestFfmpegPresets(unittest.TestCase):
|
||||
def test_ffmpeg_output_record_preset(self):
|
||||
self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["output_args"][
|
||||
"record"
|
||||
] = "preset-record-generic-audio"
|
||||
] = "preset-record-generic-audio-aac"
|
||||
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||
assert "preset-record-generic-audio" not in (
|
||||
assert "preset-record-generic-audio-aac" not in (
|
||||
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||
)
|
||||
assert "-c:v copy -c:a aac" in (
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
"""Test restream.py."""
|
||||
|
||||
from unittest import TestCase, main
|
||||
from unittest.mock import patch
|
||||
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.restream import RestreamApi
|
||||
|
||||
|
||||
class TestRestream(TestCase):
|
||||
def setUp(self) -> None:
|
||||
"""Setup the tests."""
|
||||
self.config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"restream": {"enabled": False},
|
||||
"cameras": {
|
||||
"back": {
|
||||
"ffmpeg": {
|
||||
"inputs": [
|
||||
{
|
||||
"path": "rtsp://10.0.0.1:554/video",
|
||||
"roles": ["detect", "restream"],
|
||||
},
|
||||
]
|
||||
},
|
||||
"restream": {
|
||||
"enabled": True,
|
||||
"force_audio": False,
|
||||
},
|
||||
},
|
||||
"front": {
|
||||
"ffmpeg": {
|
||||
"inputs": [
|
||||
{
|
||||
"path": "http://10.0.0.1:554/video/stream",
|
||||
"roles": ["detect", "restream"],
|
||||
},
|
||||
]
|
||||
},
|
||||
"restream": {
|
||||
"enabled": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@patch("frigate.restream.requests")
|
||||
def test_rtsp_stream(
|
||||
self, mock_request
|
||||
) -> None: # need to ensure restream doesn't try to call API
|
||||
"""Test that the normal rtsp stream is sent plainly."""
|
||||
frigate_config = FrigateConfig(**self.config)
|
||||
restream = RestreamApi(frigate_config)
|
||||
restream.add_cameras()
|
||||
assert restream.relays["back"].startswith("rtsp")
|
||||
|
||||
@patch("frigate.restream.requests")
|
||||
def test_http_stream(
|
||||
self, mock_request
|
||||
) -> None: # need to ensure restream doesn't try to call API
|
||||
"""Test that the http stream is sent via ffmpeg."""
|
||||
frigate_config = FrigateConfig(**self.config)
|
||||
restream = RestreamApi(frigate_config)
|
||||
restream.add_cameras()
|
||||
assert not restream.relays["front"].startswith("rtsp")
|
||||
|
||||
@patch("frigate.restream.requests")
|
||||
def test_restream_codec_change(
|
||||
self, mock_request
|
||||
) -> None: # need to ensure restream doesn't try to call API
|
||||
"""Test that the http stream is sent via ffmpeg."""
|
||||
self.config["cameras"]["front"]["restream"]["video_encoding"] = "h265"
|
||||
self.config["ffmpeg"] = {"hwaccel_args": "preset-nvidia-h264"}
|
||||
frigate_config = FrigateConfig(**self.config)
|
||||
restream = RestreamApi(frigate_config)
|
||||
restream.add_cameras()
|
||||
assert "#hardware=cuda" in restream.relays["front"]
|
||||
assert "#video=h265" in restream.relays["front"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(verbosity=2)
|
||||
@@ -29,3 +29,4 @@ class StatsTrackingTypes(TypedDict):
|
||||
detectors: dict[str, ObjectDetectProcess]
|
||||
started: int
|
||||
latest_frigate_version: str
|
||||
last_updated: int
|
||||
|
||||
@@ -14,12 +14,13 @@ from abc import ABC, abstractmethod
|
||||
from collections import Counter
|
||||
from collections.abc import Mapping
|
||||
from multiprocessing import shared_memory
|
||||
from typing import Any, AnyStr
|
||||
from typing import Any, AnyStr, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import os
|
||||
import psutil
|
||||
import pytz
|
||||
|
||||
from frigate.const import REGEX_HTTP_CAMERA_USER_PASS, REGEX_RTSP_CAMERA_USER_PASS
|
||||
|
||||
@@ -1040,3 +1041,14 @@ class SharedMemoryFrameManager(FrameManager):
|
||||
self.shm_store[name].close()
|
||||
self.shm_store[name].unlink()
|
||||
del self.shm_store[name]
|
||||
|
||||
|
||||
def get_tz_modifiers(tz_name: str) -> Tuple[str, str]:
|
||||
seconds_offset = (
|
||||
datetime.datetime.now(pytz.timezone(tz_name)).utcoffset().total_seconds()
|
||||
)
|
||||
hours_offset = int(seconds_offset / 60 / 60)
|
||||
minutes_offset = int(seconds_offset / 60 - hours_offset * 60)
|
||||
hour_modifier = f"{hours_offset} hour"
|
||||
minute_modifier = f"{minutes_offset} minute"
|
||||
return hour_modifier, minute_modifier
|
||||
|
||||
@@ -269,7 +269,20 @@ class CameraWatchdog(threading.Thread):
|
||||
self.logger.info("Waiting for ffmpeg to exit gracefully...")
|
||||
self.ffmpeg_detect_process.communicate(timeout=30)
|
||||
except sp.TimeoutExpired:
|
||||
self.logger.info("FFmpeg didnt exit. Force killing...")
|
||||
self.logger.info("FFmpeg did not exit. Force killing...")
|
||||
self.ffmpeg_detect_process.kill()
|
||||
self.ffmpeg_detect_process.communicate()
|
||||
elif self.camera_fps.value >= (self.config.detect.fps + 10):
|
||||
self.camera_fps.value = 0
|
||||
self.logger.info(
|
||||
f"{self.camera_name} exceeded fps limit. Exiting ffmpeg..."
|
||||
)
|
||||
self.ffmpeg_detect_process.terminate()
|
||||
try:
|
||||
self.logger.info("Waiting for ffmpeg to exit gracefully...")
|
||||
self.ffmpeg_detect_process.communicate(timeout=30)
|
||||
except sp.TimeoutExpired:
|
||||
self.logger.info("FFmpeg did not exit. Force killing...")
|
||||
self.ffmpeg_detect_process.kill()
|
||||
self.ffmpeg_detect_process.communicate()
|
||||
|
||||
@@ -391,6 +404,9 @@ def capture_camera(name, config: CameraConfig, process_info):
|
||||
signal.signal(signal.SIGTERM, receiveSignal)
|
||||
signal.signal(signal.SIGINT, receiveSignal)
|
||||
|
||||
threading.current_thread().name = f"capture:{name}"
|
||||
setproctitle(f"frigate.capture:{name}")
|
||||
|
||||
frame_queue = process_info["frame_queue"]
|
||||
camera_watchdog = CameraWatchdog(
|
||||
name,
|
||||
|
||||
@@ -36,7 +36,7 @@ class FrigateWatchdog(threading.Thread):
|
||||
detector.detect_process is not None
|
||||
and not detector.detect_process.is_alive()
|
||||
):
|
||||
logger.info("Detection appears to have stopped. Exiting frigate...")
|
||||
logger.info("Detection appears to have stopped. Exiting Frigate...")
|
||||
restart_frigate()
|
||||
|
||||
logger.info(f"Exiting watchdog...")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user