Compare commits

...

60 Commits

Author SHA1 Message Date
James L
12d51d3c73 System page: fix 0 FPS for disabled camera (#5155) 2023-01-18 18:05:18 -06:00
Nicolas Mowen
5fa1fbafed Use frigate env var substitution (#5153) 2023-01-18 17:51:35 -06:00
Felipe Santos
d36ab05bf1 Do not wait forever for services to exit (#5149)
* Fix frigate service restarting inside devcontainer

* Add timeouts to services exit process

So s6 won't wait forever.
2023-01-18 17:50:51 -06:00
Nicolas Mowen
fb85c8ca54 Optimize restream docs (#5132)
* Update live.md

* Update restream.md

* Update live.md

* Update camera_specific.md

* Update restream.md

* Update live.md

* Update live.md

* Update restream.md

* Typo

Co-authored-by: Blake Blackshear <blakeb@blakeshome.com>
2023-01-18 17:50:06 -06:00
Blake Blackshear
b2a2a9d6c7 only run the latest commit to avoid cache overwrites (#5154) 2023-01-18 17:30:49 -06:00
Felipe Santos
e2239d36c9 Auto discover internal WebRTC candidate for add-on (#5089)
* Auto discover internal WebRTC candidate for add-on

* Write logs to stderr

* Fix port number

* Integrate with newest changes

* Update docs

* Use local variable more

* Use Python to write file, fix JSON->YAML

* Store into variable

* Update docs/docs/configuration/live.md

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>

* Update docs/docs/configuration/live.md

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>

* Update docs/docs/configuration/live.md

* Update docs/docs/configuration/live.md

* Refator s6 scripts to the new format

* Remove unneeded workaround

* Update docker/rootfs/usr/local/go2rtc/create_config.py

* Migrate logging to new s6 format

* Remove more unnecessary s6 variables

* Fix prepare-log and when go2rtc is not present in config

* Restart the whole container if either Frigate or go2rtc fails

* D

* Fix service name in finish

* Fix nginx finish comment

* Restart improvements

* Fix devcontainer

* Fix format

* Update Dockerfile

Co-authored-by: Felipe Santos <felipecassiors@gmail.com>

* Improve scripts logging

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2023-01-18 17:23:40 -06:00
Nicolas Mowen
6620236bc3 Catch case where go2rtc streams is not defined (#5150) 2023-01-18 17:20:44 -06:00
Felipe Santos
02df2a8bbd Refactor s6 scripts to the new format (#5135)
* Refator s6 scripts to the new format

* Remove unneeded workaround

* Migrate logging to new s6 format

* Remove more unnecessary s6 variables

* Fix prepare-log and when go2rtc is not present in config

* Restart the whole container if either Frigate or go2rtc fails

* D

* Fix service name in finish

* Fix nginx finish comment

* Restart improvements

* Fix devcontainer

* Fix format

* Update Dockerfile

Co-authored-by: Felipe Santos <felipecassiors@gmail.com>

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2023-01-18 07:53:53 -06:00
Felipe Santos
89513e469a Fix shm size calculation (#5123)
* Fix shm size calculation

* Improve shm examples

* Add note about default shm size for add-ons

* adjust language

Co-authored-by: Blake Blackshear <blakeb@blakeshome.com>
2023-01-18 07:46:57 -06:00
Nicolas Mowen
f5466426df Fix go2rtc error when not set (#5133) 2023-01-17 22:38:17 -06:00
Nicolas Mowen
2631a4c35b Fix not using custom set stream name (#5134) 2023-01-17 22:36:52 -06:00
Nicolas Mowen
ef9338f5af Remove rpi warning about not working hwaccel (#5145) 2023-01-17 22:36:27 -06:00
Nicolas Mowen
19afb035ff Rewrite restream (#5106)
* Tear out restream config

* Rework birdseye restream

* Create go2rtc config handler

* Fix bug

* Write start script

* Rework style

* Fix python run syntax

* Output as json instead of yaml

* Put old live config back and fix birdseye references

* Fix camera webUI

* Add frigate env var subsitutions

* Fix webui checks

* Check keys

* Remove unused prest

* Fix tests

* Update restream docs

* Update restream docs

* Update live docs

* Update camera specific recommendation

* Update more docs

* add links for the docs

Co-authored-by: Felipe Santos <felipecassiors@gmail.com>

* Update note about supported audio codecs

* Move restream to go2rtc

* Docs fixes

* Add verification of stream name

* Ensure that webUI uses camera name

* Update docs to reflect new live stream name

* Fix check

* Formatting

* Remove audio from detect

Co-authored-by: Felipe Santos <felipecassiors@gmail.com>

* Fix docs

* Don't handle env variable substitution

* Add go2rtc version

* Clarify docs

Co-authored-by: Felipe Santos <felipecassiors@gmail.com>
2023-01-16 17:50:35 -06:00
Felipe Santos
a7751f210b Add dependabot auto merge workflow (#5105) 2023-01-16 17:30:48 -06:00
Blake Blackshear
3bec28ffef handle timezones with partial hour offsets (#5115)
* handle timezones with partial hour offsets

* cleanup
2023-01-16 17:17:03 -06:00
Nicolas Mowen
81b3fdb423 Pre clear retained messagse (#5117) 2023-01-16 17:16:46 -06:00
Ryan Mounce
30f520f6f0 Patch nginx-vod-module to ignore RBSP trailing bits (#5114)
Works around https://github.com/blakeblackshear/frigate/issues/4572
2023-01-16 07:01:04 -06:00
Felipe Santos
0de1da5943 Upgrade go2rtc from v0.1-rc.8 to v0.1-rc.9 (#5104) 2023-01-15 22:28:20 -06:00
yeahme49
e39fb51dec Add Save Only button to config editor (#5090)
* Add Save Only button to save config without restarting

* Fixes

* fix formatting

* change to query parameter from header

* lint fixes
2023-01-15 11:25:49 -06:00
Bernt Christian Egeland
621aa0cf61 Rework events page to include timeago (#5097)
* new timeago component

* added timeago to event

* clock icon

* clock icon. flex items center

* dense prop

* moved clipDuration. cleaner jsx, sm hidden

* renamed clipduration => getDurationFromTimestamps

* func description

* duration in parenthesis
2023-01-15 09:43:40 -06:00
Nicolas Mowen
367ac28a94 Fix qsv h265 (#5095) 2023-01-15 09:41:13 -06:00
Nicolas Mowen
99577a57e6 Add specific presets for restream and record with audio (#5094)
* Add more ffmpeg presets

* Update docs

* Update tests

* Update docs to optimize setup
2023-01-15 09:40:42 -06:00
Bernt Christian Egeland
65bc644d03 Rework storage page to show sizes with relevant units (#5093)
* new getUnitSize function

* check if isNaN
2023-01-15 09:39:03 -06:00
Nicolas Mowen
01b9d4d848 Rework audio encoding for restream (#5092)
* Use memo for recordings timezone

* Add audio encoding field and simplify stream creation

* Update docs and tests

* Fix bad logic
2023-01-15 09:38:19 -06:00
Felipe Santos
daadd206dd Update live view documentation to match newest go2rtc (#5083)
* Remove host network suggestion from docs

* Mention alternative network modes

* Update docs/docs/configuration/live.md

* Update docs/docs/configuration/live.md

* Wording tweaks

* Update docs/docs/configuration/live.md

* Update docs/docs/configuration/live.md

Co-authored-by: Blake Blackshear <blakeb@blakeshome.com>
2023-01-15 09:35:21 -06:00
Nate Meyer
60b2315028 Update library loading for tensorrt (#5087)
* Update library loading for tensorrt

* Add symlink to libnvrtc
2023-01-14 13:14:27 -06:00
Nicolas Mowen
19d17c8c81 Use memo for recordings timezone (#5086) 2023-01-14 11:03:29 -06:00
Felipe Santos
24410849b7 Revisit FAQs (#5084) 2023-01-14 11:02:20 -06:00
Nicolas Mowen
e0b3b27b8a Add option for live mode & timezone config, fix MSE check for iPad (#5079)
* Add config fields

* Clean up camera default values

* Set recordings timezone with config if available

* Adjust for timezone config

* Cleanup setting of the timezone

* Don't fail on MSE check iPad

* Fix MSE check for birdseye

* Add docs

* Fix test
2023-01-13 17:27:16 -06:00
Nicolas Mowen
170899bd71 Update to Go2rtc rc8 (#5078)
* Remove redundant default port

* Update version

* Suggest port mapping 8555 tcp and udp
2023-01-13 17:18:18 -06:00
James L
2031c3146f System page: make clear if detect disabled (#5075) 2023-01-13 17:14:07 -06:00
Nicolas Mowen
dafc7edb59 Show recording and snapshot storage separately if different sizes (#5072)
* Show recording and snapshot storage separately if different sizes

* Fix preload

* Fix compare
2023-01-13 17:13:21 -06:00
Nicolas Mowen
7fbfdb3cb8 Replace localhost with explicit address (#5070)
* Replace localhost

* Replace localhost
2023-01-13 17:12:37 -06:00
dependabot[bot]
e0a0d129df Bump actions/setup-python from 4.4.0 to 4.5.0 (#5064)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.4.0...v4.5.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-13 07:23:23 -06:00
Nicolas Mowen
2d76363da5 Catch FileNotFoundError when getting file system stats (#5056) 2023-01-13 07:22:47 -06:00
Nicolas Mowen
54bfa4679c Simplify hwaccel filter args and fix qsv bugs (#5054)
* Simplify hwaccel filter args and fix qsv bugs

* Formatting

* Remove dead code

* Set framerate and size for rpi as well
2023-01-13 07:22:19 -06:00
Nicolas Mowen
3c708d7672 Update docs to point to ghcr (#5053)
* Update install docs to point to ghcr

* Update hwaccel image mention
2023-01-13 07:20:49 -06:00
Nicolas Mowen
b4fb6086b3 Clarify docs about rtmp (#5052)
* Update readme features

* Remove RTMP setup from setup guide

* Update integration for RTSP

* Remove rtmp from faq

* Remove RTMP stream from guide

* Remove rtmp from install

* Remove rtmp from dev config
2023-01-13 07:20:25 -06:00
Nicolas Mowen
ab44a65fe3 Make live docs more clear (#5050) 2023-01-13 07:18:44 -06:00
Felipe Santos
95596b39fd Capitalize the Frigate word (#5044) 2023-01-13 07:18:15 -06:00
Nicolas Mowen
cf2466c8c1 Fix recordings storage (#5031)
* Fix recordings storage

* Update record.py

* Update record.py

* Formatting
2023-01-12 05:53:38 -06:00
James L
6b123675c4 System page: add detector process mem% (#5028) 2023-01-11 18:11:45 -06:00
James L
ddcae2d4aa Fix mem usage reporting when using docker limits (#5011)
* Fix mem usage reporting when using docker limits

* format code

* wip
2023-01-11 17:39:26 -06:00
sergeknystautas
731db8fb8f Add in_progress parameter to /api/events to filter the results. (#5013)
* Add in_progress parameter to /api/events to filter the results.

* Change in_progress to default to no filtering, 0 means no in progress, 1 means only in progress.

* Fix code format with black.

* Clear blank line.
2023-01-11 17:22:39 -06:00
Nicolas Mowen
cb0c5c2587 Disable backchannel audio since it is not used (#5021)
* Disable backchannel audio since it is not used

* Don't need to block bachchannel audio for ffmpeg streams

* Formatting
2023-01-11 17:21:48 -06:00
Nicolas Mowen
1643b4d108 Clean up go2rtc logs to not show color text unicode (#5027)
* Use color logs for go2rtc

* Update docs to show need for formatted logs

* Fix log selector
2023-01-11 17:21:13 -06:00
James L
acd1fb9e3e System page: various minor UI tweaks (#4985)
* System page: various minor UI tweaks

* Be consistent with capitalisation

* Change detection start epoch to running/idle

* Remove detection start column entirely
2023-01-11 06:11:57 -06:00
Nicolas Mowen
ddde477770 Use scale_qsv and don't apply deinterlacing (#4997) 2023-01-11 06:11:26 -06:00
Nicolas Mowen
3edbb8dc41 Camera WebUI fixes (#5010)
* Show jsmpeg when restream is disabled

* Fix debug button status not showing correctly when switching cameras

* Change label to make clear only motion masks are shown
2023-01-11 06:09:58 -06:00
Nicolas Mowen
581c2591ae Use library to handle copying to clipboard (#4989)
* Use library to handle copying

* Typo
2023-01-10 06:23:04 -06:00
Nicolas Mowen
d49359e26a Fix None filter for sub labels (#4981) 2023-01-09 19:44:26 -06:00
Nicolas Mowen
e79eab711a Fix RPi preset (#4968) 2023-01-08 20:58:06 -06:00
Nicolas Mowen
5b7cd9ce64 Update go2rtc to rc7 (#4965) 2023-01-08 13:43:11 -07:00
Blake Blackshear
3cb96091ec update before install (#4966) 2023-01-08 14:39:39 -06:00
Nicolas Mowen
fdd2cc972e Update record.md (#4964) 2023-01-08 14:28:48 -06:00
Nicolas Mowen
61243ad34b Make error messages for gpu stats more clear (#4962)
* Make error messages for gpu stats more clear

* Make error messages for gpu stats more clear
2023-01-08 14:00:46 -06:00
Nicolas Mowen
5f4c439f57 Fix btbn pulling wrong build (#4961) 2023-01-08 09:23:58 -06:00
Blake Blackshear
1f963ec5aa Fix raspberry pi hwaccel (#4955)
* use ffmpeg from raspbian repo

* dynamically choose timeout param

* invert logic

* Add hwaccel presets for rpi

Co-authored-by: Nick Mowen <nickmowen213@gmail.com>
2023-01-08 07:04:58 -06:00
Pierre Belanger
bcbf0061ff Support for dynamic MQTT user configuration #4883 (#4956)
* Support for dynamic MQTT user configuration #4883

* Fix substitute condition & docs
2023-01-07 17:10:48 -06:00
Nicolas Mowen
57dce4ec38 Make label colors consistent (#4951) 2023-01-07 12:05:11 -06:00
108 changed files with 1303 additions and 839 deletions

View File

@@ -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

View 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 }}

View File

@@ -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

View File

@@ -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.6/go2rtc_linux_${TARGETARCH}" \
RUN wget -qO go2rtc "https://github.com/AlexxIT/go2rtc/releases/download/v0.1-rc.9/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

View File

@@ -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

View File

@@ -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
@@ -47,4 +63,4 @@ cd /tmp/nginx
--with-cc-opt="-O3 -Wno-error=implicit-fallthrough"
make -j$(nproc) && make install
rm -rf /usr/local/nginx/html /usr/local/nginx/conf/*.default
rm -rf /usr/local/nginx/html /usr/local/nginx/conf/*.default

View File

@@ -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

View File

@@ -10,12 +10,11 @@ apt-get -qq install --no-install-recommends -y \
wget \
procps vainfo \
unzip locales tzdata libxml2 xz-utils \
python3-pip
python3-pip \
curl \
jq
# add raspberry pi repo
mkdir -p -m 600 /root/.gnupg
gpg --no-default-keyring --keyring /usr/share/keyrings/raspbian.gpg --keyserver keyserver.ubuntu.com --recv-keys 9165938D90FDDD2E
echo "deb [signed-by=/usr/share/keyrings/raspbian.gpg] http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi" | tee /etc/apt/sources.list.d/raspi.list
# add coral repo
wget --quiet -O /usr/share/keyrings/google-edgetpu.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
@@ -30,21 +29,29 @@ apt-get -qq update
apt-get -qq install --no-install-recommends --no-install-suggests -y \
libedgetpu1-max python3-tflite-runtime python3-pycoral
# btbn-ffmpeg -> amd64 / arm64
if [[ "${TARGETARCH}" == "amd64" || "${TARGETARCH}" == "arm64" ]]; then
if [[ "${TARGETARCH}" == "amd64" ]]; then
btbn_arch="64"
else
btbn_arch="arm64"
fi
# btbn-ffmpeg -> amd64
if [[ "${TARGETARCH}" == "amd64" ]]; then
mkdir -p /usr/lib/btbn-ffmpeg
wget -qO btbn-ffmpeg.tar.xz "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2022-07-31-12-37/ffmpeg-n5.1-2-g915ef932a3-linux${btbn_arch}-gpl-5.1.tar.xz"
wget -qO btbn-ffmpeg.tar.xz "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2022-07-31-12-37/ffmpeg-n5.1-2-g915ef932a3-linux64-gpl-5.1.tar.xz"
tar -xf btbn-ffmpeg.tar.xz -C /usr/lib/btbn-ffmpeg --strip-components 1
rm -rf btbn-ffmpeg.tar.xz /usr/lib/btbn-ffmpeg/doc /usr/lib/btbn-ffmpeg/bin/ffplay
fi
# ffmpeg -> arm32
if [[ "${TARGETARCH}" == "arm" ]]; then
# add raspberry pi repo
gpg --no-default-keyring --keyring /usr/share/keyrings/raspbian.gpg --keyserver keyserver.ubuntu.com --recv-keys 9165938D90FDDD2E
echo "deb [signed-by=/usr/share/keyrings/raspbian.gpg] http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi" | tee /etc/apt/sources.list.d/raspi.list
apt-get -qq update
apt-get -qq install --no-install-recommends --no-install-suggests -y ffmpeg
fi
# ffmpeg -> arm64
if [[ "${TARGETARCH}" == "arm64" ]]; then
# add raspberry pi repo
gpg --no-default-keyring --keyring /usr/share/keyrings/raspbian.gpg --keyserver keyserver.ubuntu.com --recv-keys 82B129927FA3303E
echo "deb [signed-by=/usr/share/keyrings/raspbian.gpg] https://archive.raspberrypi.org/debian/ bullseye main" | tee /etc/apt/sources.list.d/raspi.list
apt-get -qq update
apt-get -qq install --no-install-recommends --no-install-suggests -y ffmpeg
fi

View 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

View File

@@ -0,0 +1 @@
frigate

View File

@@ -0,0 +1 @@
frigate-pipeline

View File

@@ -0,0 +1 @@
longrun

View 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

View File

@@ -0,0 +1 @@
frigate-log

View File

@@ -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

View File

@@ -0,0 +1 @@
120000

View File

@@ -0,0 +1 @@
longrun

View File

@@ -0,0 +1 @@
go2rtc

View File

@@ -0,0 +1 @@
go2rtc-pipeline

View File

@@ -0,0 +1 @@
longrun

View 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

View File

@@ -0,0 +1 @@
go2rtc-log

View 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}\.{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["22/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}"

View File

@@ -0,0 +1 @@
30000

View File

@@ -0,0 +1 @@
longrun

View File

@@ -0,0 +1 @@
oneshot

View File

@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/log-prepare/run

View File

@@ -0,0 +1 @@
nginx

View File

@@ -0,0 +1 @@
nginx-pipeline

View File

@@ -0,0 +1 @@
longrun

View 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

View File

@@ -0,0 +1 @@
nginx-log

View File

@@ -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

View File

@@ -0,0 +1 @@
30000

View File

@@ -0,0 +1 @@
longrun

View File

@@ -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

View File

@@ -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

View File

@@ -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}"

View File

@@ -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

View File

@@ -0,0 +1,64 @@
"""Creates a go2rtc config file."""
import json
import os
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}
# 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))

View File

@@ -1,4 +0,0 @@
webrtc:
listen: ":8555"
candidates:
- stun:8555

View File

@@ -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`.

View File

@@ -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,20 @@ 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:
reolink: ffmpeg:http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=username&password=password#video=copy#audio=copy#audio=opus
reolink_sub: ffmpeg: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?video=copy
input_args: preset-rtsp-restream
roles:
- detect
detect:

View File

@@ -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

View File

@@ -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.

View File

@@ -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,17 +26,18 @@ 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-udp | RTSP Stream via UDP | Use when camera is UDP only |
| preset-rtsp-blue-iris | Blue Iris RTSP Stream | Use when consuming a stream from Blue Iris |
| Preset | 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 |
:::caution
@@ -66,10 +67,11 @@ 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-mjpeg | Record an mjpeg stream | Recommend restreaming mjpeg stream instead |
| preset-record-jpeg | Record live jpeg | Recommend restreaming live jpeg instead |
| preset-record-ubiquiti | Record ubiquiti stream with audio | Recordings with ubiquiti non-standard audio |
| Preset | Usage | Other Notes |
| -------------------------------- | --------------------------------- | --------------------------------------------- |
| preset-record-generic | Record WITHOUT audio | This is the default when nothing is specified |
| 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 |

View File

@@ -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.
@@ -57,7 +51,7 @@ If you have multiple Nvidia graphic card, you can add them with their ids obtain
services:
frigate:
...
image: blakeblackshear/frigate:stable
image: ghcr.io/blakeblackshear/frigate:stable
deploy: # <------------- Add this section
resources:
reservations:

View File

@@ -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:
@@ -52,6 +52,8 @@ mqtt:
# NOTE: must be unique if you are running multiple instances
client_id: frigate
# Optional: user
# NOTE: MQTT user can be specified with an environment variables that must begin with 'FRIGATE_'.
# e.g. user: '{FRIGATE_MQTT_USER}'
user: mqtt_user
# Optional: password
# NOTE: MQTT password can be specified with an environment variables that must begin with 'FRIGATE_'.
@@ -124,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)
@@ -169,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:
@@ -350,28 +355,21 @@ 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:
# 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.
height: 720
# Optional: Set the encode quality of the jsmpeg stream (default: shown below)
# 1 is the highest quality, and 31 is the lowest. Lower quality feeds utilize less CPU resources.
quality: 8
# Uses https://github.com/AlexxIT/go2rtc (v0.1-rc9)
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.
height: 720
# Optional: Set the encode quality of the jsmpeg stream (default: shown below)
# 1 is the highest quality, and 31 is the lowest. Lower quality feeds utilize less CPU resources.
quality: 8
# Optional: in-feed timestamp style configuration
# NOTE: Can be overridden at the camera level
@@ -484,4 +482,13 @@ 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
```

View File

@@ -9,33 +9,95 @@ Frigate has different live view options, some of which require [restream](restre
Live view options can be selected while viewing the live stream. The options are:
| Source | Latency | Frame Rate | Resolution | Audio | Requires Restream | 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 |
| Source | Latency | Frame Rate | Resolution | Audio | Requires Restream | 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, 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
- "ffmpeg:http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=user&password=password#video=copy#audio=copy#audio=opus" # <- http streams must use ffmpeg to set all types
```
### 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. 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
cameras:
test_cam:
ffmpeg:
output_args:
record: preset-record-generic-audio-copy
inputs:
- path: rtsp://127.0.0.1:8554/test_cam?video=copy&audio=aac # <--- 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?video=copy # <--- 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
webrtc:
listen: ":8555"
candidates:
- <frigate host ip address>:8555 # <--- enter frigate host IP here
- stun:8555
- 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:
```yaml title="/config/frigate.yaml"
go2rtc:
streams:
test_cam: ...
webrtc:
candidates:
- 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.

View File

@@ -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:

View File

@@ -3,9 +3,11 @@ id: record
title: Recording
---
Recordings can be enabled and are stored at `/media/frigate/recordings`. The folder structure for the recordings is `YYYY-MM/DD/HH/<camera_name>/MM.SS.mp4`. These recordings are written directly from your camera stream without re-encoding. Each camera supports a configurable retention policy in the config. Frigate chooses the largest matching retention value between the recording retention and the event retention when determining if a recording should be removed.
Recordings can be enabled and are stored at `/media/frigate/recordings`. The folder structure for the recordings is `YYYY-MM-DD/HH/<camera_name>/MM.SS.mp4`. These recordings are written directly from your camera stream without re-encoding. Each camera supports a configurable retention policy in the config. Frigate chooses the largest matching retention value between the recording retention and the event retention when determining if a recording should be removed.
H265 recordings can be viewed in Edge and Safari only. All other browsers require recordings to be encoded with H264.
New recording segments are written from the camera stream to cache, they are only moved to disk if they match the setup recording retention policy.
H265 recordings can be viewed in Chrome 108+, Edge and Safari only. All other browsers require recordings to be encoded with H264.
## Will Frigate delete old recordings if my storage runs out?
@@ -37,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.

View File

@@ -5,31 +5,13 @@ title: Restream
### 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
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.
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.
#### Birdseye Restream
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.
#### 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.
```yaml
ffmpeg:
hwaccel_args: your-hwaccel-preset # <- highly recommended so the GPU is used
cameras:
mjpeg_cam:
ffmpeg:
...
restream:
video_encoding: h264
```
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.
### RTMP (Deprecated)
@@ -44,35 +26,80 @@ 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 http streams
- "ffmpeg:http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=user&password=password#video=copy#audio=copy#audio=opus" # <- http streams must use ffmpeg to set all types
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?video=copy&audio=aac # <--- 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?video=copy&audio=aac # <--- the name here must match the name of the camera in restream
input_args: preset-rtsp-restream
roles:
- restream
- record
- detect
```
### With Sub Stream
#### With Sub Stream
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:
- "ffmpeg:http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=user&password=password#video=copy#audio=copy#audio=opus" # <- http streams must use ffmpeg to set all types
http_cam_sub:
- "ffmpeg:http://192.168.50.155/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=user&password=password#video=copy#audio=copy#audio=opus" # <- http streams must use ffmpeg to set all types
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?video=copy&audio=aac # <--- 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?video=copy&audio=aac # <--- 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?video=copy&audio=aac # <--- 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?video=copy&audio=aac # <--- the name here must match the name of the camera_sub in restream
input_args: preset-rtsp-restream
roles:
- detect
```

View File

@@ -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`.

View File

@@ -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. |

View File

@@ -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)

View File

@@ -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

View File

@@ -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.

View File

@@ -7,7 +7,7 @@ This guide walks through the steps to build a configuration file for Frigate. It
### Step 1: Configure the MQTT server (Optional)
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:
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:
If using mqtt:
```yaml
@@ -68,9 +68,6 @@ cameras:
- path: rtsp://10.0.10.10:554/rtsp # <----- Update for your camera
roles:
- detect
- rtmp
rtmp:
enabled: False # <-- RTMP should be disabled if your stream is not H264
detect:
width: 1280 # <---- update for your camera's resolution
height: 720 # <---- update for your camera's resolution
@@ -80,7 +77,7 @@ cameras:
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).
@@ -148,7 +145,6 @@ cameras:
- path: rtsp://10.0.10.10:554/rtsp
roles:
- detect
- rtmp
detect:
width: 1280
height: 720
@@ -173,7 +169,6 @@ 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
roles:
- record

View File

@@ -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:

View File

@@ -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
},
@@ -166,6 +166,7 @@ Events from the database. Accepts the following query string parameters:
| `has_snapshot` | int | Filter to events that have snapshots (0 or 1) |
| `has_clip` | int | Filter to events that have clips (0 or 1) |
| `include_thumbnails` | int | Include thumbnails in the response (0 or 1) |
| `in_progress` | int | Limit to events in progress (0 or 1) |
### `GET /api/events/summary`

View File

@@ -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

View File

@@ -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
{

View File

@@ -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.
![mismatched-resolution](/img/mismatched-resolution-min.jpg)
### 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.

View File

@@ -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,

View File

@@ -139,35 +139,30 @@ 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,
)
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",
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}/{callback}/set",
self.on_mqtt_command,
)
self.client.message_callback_add(
f"{self.mqtt_config.topic_prefix}/restart", self.on_mqtt_command

View File

@@ -60,7 +60,17 @@ 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")
@@ -334,6 +344,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 +406,6 @@ class FfmpegConfig(FrigateBaseModel):
class CameraRoleEnum(str, Enum):
record = "record"
restream = "restream"
rtmp = "rtmp"
detect = "detect"
@@ -509,29 +519,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 +554,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 +597,6 @@ class CameraConfig(FrigateBaseModel):
config["ffmpeg"]["inputs"][0]["roles"] = [
"record",
"detect",
"restream",
]
if has_rtmp:
@@ -738,9 +733,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}."
)
@@ -834,7 +837,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(
@@ -863,8 +869,9 @@ class FrigateConfig(FrigateBaseModel):
"""Merge camera config with globals."""
config = self.copy(deep=True)
# MQTT password substitution
if config.mqtt.password:
# MQTT user/password substitutions
if config.mqtt.user or config.mqtt.password:
config.mqtt.user = config.mqtt.user.format(**FRIGATE_ENV_VARS)
config.mqtt.password = config.mqtt.password.format(**FRIGATE_ENV_VARS)
# Global config to propagate down to camera level
@@ -874,7 +881,7 @@ class FrigateConfig(FrigateBaseModel):
"record": ...,
"snapshots": ...,
"rtmp": ...,
"restream": ...,
"live": ...,
"objects": ...,
"motion": ...,
"detect": ...,
@@ -947,7 +954,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)
@@ -968,7 +980,7 @@ class FrigateConfig(FrigateBaseModel):
for _, camera in config.cameras.items():
enabled_labels.update(camera.objects.track)
config.model.create_colormap(enabled_labels)
config.model.create_colormap(sorted(enabled_labels))
for key, detector in config.detectors.items():
detector_config: DetectorConfig = parse_obj_as(DetectorConfig, detector)

View File

@@ -7,6 +7,7 @@ YAML_EXT = (".yaml", ".yml")
PLUS_ENV_VAR = "PLUS_API_KEY"
PLUS_API_HOST = "https://api.frigate.video"
MAX_SEGMENT_DURATION = 600
BTBN_PATH = "/usr/lib/btbn-ffmpeg"
# Regex Consts

View File

@@ -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")

View File

@@ -1,8 +1,13 @@
"""Handles inserting and maintaining ffmpeg presets."""
import os
from typing import Any
from frigate.version import VERSION
from frigate.const import BTBN_PATH
TIMEOUT_PARAM = "-timeout" if os.path.exists(BTBN_PATH) else "-stimeout"
_user_agent_args = [
"-user_agent",
@@ -33,6 +38,8 @@ PRESETS_HW_ACCEL_DECODE = {
"h264_qsv",
],
"preset-intel-qsv-h265": [
"-load_plugin",
"hevc_hw",
"-hwaccel",
"qsv",
"-qsv_device",
@@ -75,45 +82,19 @@ PRESETS_HW_ACCEL_DECODE = {
}
PRESETS_HW_ACCEL_SCALE = {
"preset-vaapi": [
"-vf",
"fps={},scale_vaapi=w={}:h={},hwdownload,format=yuv420p",
"-f",
"rawvideo",
],
"preset-intel-qsv-h264": [
"-vf",
"vpp_qsv=framerate={}:scale_mode=1:w={}:h={}:detail=50:denoise=100:deinterlace=2:format=nv12,hwdownload,format=nv12,format=yuv420p",
"-f",
"rawvideo",
],
"preset-intel-qsv-h265": [
"-vf",
"vpp_qsv=framerate={}:scale_mode=1:w={}:h={}:detail=50:denoise=100:deinterlace=2: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 {} -s {}x{} -f rawvideo -pix_fmt yuv420p",
"preset-rpi-64-h264": "-r {} -s {}x{} -f rawvideo -pix_fmt yuv420p",
"preset-vaapi": "-vf fps={},scale_vaapi=w={}:h={},hwdownload,format=yuv420p -f rawvideo",
"preset-intel-qsv-h264": "-r {} -vf vpp_qsv=w={}:h={}:format=nv12,hwdownload,format=nv12,format=yuv420p -f rawvideo",
"preset-intel-qsv-h265": "-r {} -vf vpp_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 {}x{}",
}
PRESETS_HW_ACCEL_ENCODE = {
"preset-rpi-32-h264": "ffmpeg -hide_banner {0} -c:v h264_v4l2m2m -g 50 -bf 0 {1}",
"preset-rpi-64-h264": "ffmpeg -hide_banner {0} -c:v h264_v4l2m2m -g 50 -bf 0 {1}",
"preset-intel-qsv-h264": "ffmpeg -hide_banner {0} -c:v h264_qsv -g 50 -bf 0 -profile:v high -level:v 4.1 -async_depth:v 1 {1}",
"preset-intel-qsv-h265": "ffmpeg -hide_banner {0} -c:v h264_qsv -g 50 -bf 0 -profile:v high -level:v 4.1 -async_depth:v 1 {1}",
"preset-nvidia-h264": "ffmpeg -hide_banner {0} -c:v h264_nvenc -g 50 -profile:v high -level:v auto -preset:v p2 -tune:v ll {1}",
@@ -121,15 +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-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."""
@@ -148,15 +120,18 @@ 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)
return scale
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
def parse_preset_hardware_acceleration_encode(arg: Any, input: str, output: str) -> str:
@@ -170,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
+ [
@@ -258,11 +225,18 @@ PRESETS_INPUT = {
"+genpts+discardcorrupt",
"-rtsp_transport",
"tcp",
"-timeout",
TIMEOUT_PARAM,
"5000000",
"-use_wallclock_as_timestamps",
"1",
],
"preset-rtsp-restream": _user_agent_args
+ [
"-rtsp_transport",
"tcp",
TIMEOUT_PARAM,
"5000000",
],
"preset-rtsp-udp": _user_agent_args
+ [
"-avoid_negative_ts",
@@ -271,7 +245,7 @@ PRESETS_INPUT = {
"+genpts+discardcorrupt",
"-rtsp_transport",
"udp",
"-timeout",
TIMEOUT_PARAM,
"5000000",
"-use_wallclock_as_timestamps",
"1",
@@ -290,7 +264,7 @@ PRESETS_INPUT = {
"+genpts+discardcorrupt",
"-rtsp_transport",
"tcp",
"-timeout",
TIMEOUT_PARAM,
"5000000",
"-use_wallclock_as_timestamps",
"1",
@@ -327,7 +301,7 @@ PRESETS_RECORD_OUTPUT = {
"copy",
"-an",
],
"preset-record-generic-audio": [
"preset-record-generic-audio-aac": [
"-f",
"segment",
"-segment_time",
@@ -343,6 +317,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",

View File

@@ -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,
)
@@ -565,6 +572,7 @@ def events():
before = request.args.get("before", type=float)
has_clip = request.args.get("has_clip", type=int)
has_snapshot = request.args.get("has_snapshot", type=int)
in_progress = request.args.get("in_progress", type=int)
include_thumbnails = request.args.get("include_thumbnails", default=1, type=int)
favorites = request.args.get("favorites", type=int)
@@ -602,8 +610,13 @@ def events():
# for example a sub label 'bob' would get events
# with sub labels 'bob' and 'bob, john'
sub_label_clauses = []
filtered_sub_labels = sub_labels.split(",")
for label in sub_labels.split(","):
if "None" in filtered_sub_labels:
filtered_sub_labels.remove("None")
sub_label_clauses.append((Event.sub_label.is_null()))
for label in filtered_sub_labels:
sub_label_clauses.append((Event.sub_label.cast("text") % f"*{label}*"))
sub_label_clause = reduce(operator.or_, sub_label_clauses)
@@ -613,8 +626,13 @@ def events():
# use matching so events with multiple zones
# still match on a search where any zone matches
zone_clauses = []
filtered_zones = zones.split(",")
for zone in zones.split(","):
if "None" in filtered_zones:
filtered_zones.remove("None")
zone_clauses.append((Event.zones.length() == 0))
for zone in filtered_zones:
zone_clauses.append((Event.zones.cast("text") % f'*"{zone}"*'))
zone_clause = reduce(operator.or_, zone_clauses)
@@ -632,6 +650,9 @@ def events():
if not has_snapshot is None:
clauses.append((Event.has_snapshot == has_snapshot))
if not in_progress is None:
clauses.append((Event.end_time.is_null(in_progress)))
if not include_thumbnails:
excluded_fields.append(Event.thumbnail)
else:
@@ -696,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:
@@ -733,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,
)
try:
restart_frigate()
except Exception as e:
logging.error(f"Error restarting frigate: {e}")
return "Config successfully saved, unable to restart frigate", 200
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
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")
@@ -870,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[
@@ -889,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"),
@@ -904,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()
)
)
@@ -919,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"),
)
@@ -927,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()
@@ -1124,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()

View File

@@ -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)

View File

@@ -278,9 +278,7 @@ class RecordingMaintainer(threading.Thread):
directory = os.path.join(
RECORD_DIR,
start_time.replace(tzinfo=datetime.timezone.utc)
.astimezone(tz=None)
.strftime("%Y-%m-%d/%H"),
start_time.astimezone(tz=datetime.timezone.utc).strftime("%Y-%m-%d/%H"),
camera,
)

View File

@@ -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,28 +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] = escape_special_characters(input.path)
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}')}"

View File

@@ -215,6 +215,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,
@@ -241,7 +242,11 @@ def stats_snapshot(
}
for path in [RECORD_DIR, CLIPS_DIR, CACHE_DIR, "/dev/shm"]:
storage_stats = shutil.disk_usage(path)
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),

View File

@@ -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": {
"quality": 7,
}
"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):

View File

@@ -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 (

View File

@@ -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)

View File

@@ -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
@@ -627,13 +628,8 @@ def clipped(obj, frame_shape):
def restart_frigate():
proc = psutil.Process(1)
# if this is running via s6, sigterm pid 1
if proc.name() == "s6-svscan":
proc.terminate()
# otherwise, just try and exit frigate
else:
os.kill(os.getpid(), signal.SIGTERM)
# S6 overlay is configured to exit once the Frigate process exits
os.kill(os.getpid(), signal.SIGTERM)
class EventsPerSecond:
@@ -738,12 +734,69 @@ def escape_special_characters(path: str) -> str:
return path
def get_cgroups_version() -> str:
"""Determine what version of cgroups is enabled"""
stat_command = ["stat", "-fc", "%T", "/sys/fs/cgroup"]
p = sp.run(
stat_command,
encoding="ascii",
capture_output=True,
)
if p.returncode == 0:
value: str = p.stdout.strip().lower()
if value == "cgroup2fs":
return "cgroup2"
elif value == "tmpfs":
return "cgroup"
else:
logger.debug(
f"Could not determine cgroups version: unhandled filesystem {value}"
)
else:
logger.debug(f"Could not determine cgroups version: {p.stderr}")
return "unknown"
def get_docker_memlimit_bytes() -> int:
"""Get mem limit in bytes set in docker if present. Returns -1 if no limit detected"""
# check running a supported cgroups version
if get_cgroups_version() == "cgroup2":
memlimit_command = ["cat", "/sys/fs/cgroup/memory.max"]
p = sp.run(
memlimit_command,
encoding="ascii",
capture_output=True,
)
if p.returncode == 0:
value: str = p.stdout.strip()
if value.isnumeric():
return int(value)
elif value.lower() == "max":
return -1
else:
logger.debug(f"Unable to get docker memlimit: {p.stderr}")
return -1
def get_cpu_stats() -> dict[str, dict]:
"""Get cpu usages for each process id"""
usages = {}
# -n=2 runs to ensure extraneous values are not included
top_command = ["top", "-b", "-n", "2"]
docker_memlimit = get_docker_memlimit_bytes() / 1024
p = sp.run(
top_command,
encoding="ascii",
@@ -759,9 +812,18 @@ def get_cpu_stats() -> dict[str, dict]:
for line in lines:
stats = list(filter(lambda a: a != "", line.strip().split(" ")))
try:
if docker_memlimit > 0:
mem_res = int(stats[5])
mem_pct = str(
round((float(mem_res) / float(docker_memlimit)) * 100, 1)
)
else:
mem_pct = stats[9]
usages[stats[0]] = {
"cpu": stats[8],
"mem": stats[9],
"mem": mem_pct,
}
except:
continue
@@ -780,7 +842,7 @@ def get_amd_gpu_stats() -> dict[str, str]:
)
if p.returncode != 0:
logger.error(p.stderr)
logger.error(f"Unable to poll radeon GPU stats: {p.stderr}")
return None
else:
usages = p.stdout.split(",")
@@ -816,7 +878,7 @@ def get_intel_gpu_stats() -> dict[str, str]:
# timeout has a non-zero returncode when timeout is reached
if p.returncode != 124:
logger.error(p.stderr)
logger.error(f"Unable to poll intel GPU stats: {p.stderr}")
return None
else:
reading = "".join(p.stdout.split())
@@ -866,7 +928,7 @@ def get_nvidia_gpu_stats() -> dict[str, str]:
)
if p.returncode != 0:
logger.error(p.stderr)
logger.error(f"Unable to poll nvidia GPU stats: {p.stderr}")
return None
else:
usages = p.stdout.split("\n")[1].strip().split(",")
@@ -974,3 +1036,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

View File

@@ -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...")

View File

@@ -5,4 +5,5 @@ cuda-python == 11.7; platform_machine == 'x86_64'
cython == 0.29.*; platform_machine == 'x86_64'
nvidia-cuda-runtime-cu11 == 11.7.*; platform_machine == 'x86_64'
nvidia-cublas-cu11 == 11.11.*; platform_machine == 'x86_64'
nvidia-cudnn-cu11 == 8.7.*; platform_machine == 'x86_64'
nvidia-cudnn-cu11 == 8.7.*; platform_machine == 'x86_64'
nvidia-cuda-nvrtc-cu11 == 11.7.*; platform_machine == 'x86_64'

View File

@@ -14,6 +14,7 @@
"dependencies": {
"@cycjimmy/jsmpeg-player": "^6.0.5",
"axios": "^1.2.2",
"copy-to-clipboard": "3.3.3",
"date-fns": "^2.29.3",
"idb-keyval": "^6.2.0",
"immer": "^9.0.16",

View File

@@ -78,7 +78,7 @@ export default function AppBar() {
{showDialogWait ? (
<Prompt
title="Restart in progress"
text="Please wait a few seconds for the restart to complete before reloading the page."
text="This can take up to one minute, please wait before reloading the page."
/>
) : null}
</Fragment>

View File

@@ -3,13 +3,14 @@ import { useCallback, useState } from 'preact/hooks';
export default function ButtonsTabbed({
viewModes = [''],
currentViewMode = '',
setViewMode = null,
setHeader = null,
headers = [''],
className = 'text-gray-600 py-0 px-4 block hover:text-gray-500',
selectedClassName = `${className} focus:outline-none border-b-2 font-medium border-gray-500`,
}) {
const [selected, setSelected] = useState(0);
const [selected, setSelected] = useState(viewModes ? viewModes.indexOf(currentViewMode) : 0);
const captitalize = (str) => {
return `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
};

View File

@@ -0,0 +1,61 @@
import { h } from 'preact';
const timeAgo = ({ time, dense = false }) => {
if (!time) return 'Invalid Time';
try {
const currentTime = new Date();
const pastTime = new Date(time);
const elapsedTime = currentTime - pastTime;
if (elapsedTime < 0) return 'Invalid Time';
const timeUnits = [
{ unit: 'ye', full: 'year', value: 31536000 },
{ unit: 'mo', full: 'month', value: 0 },
{ unit: 'day', full: 'day', value: 86400 },
{ unit: 'h', full: 'hour', value: 3600 },
{ unit: 'm', full: 'minute', value: 60 },
{ unit: 's', full: 'second', value: 1 },
];
let elapsed = elapsedTime / 1000;
if (elapsed < 60) {
return 'just now';
}
for (let i = 0; i < timeUnits.length; i++) {
// if months
if (i === 1) {
// Get the month and year for the time provided
const pastMonth = pastTime.getUTCMonth();
const pastYear = pastTime.getUTCFullYear();
// get current month and year
const currentMonth = currentTime.getUTCMonth();
const currentYear = currentTime.getUTCFullYear();
let monthDiff = (currentYear - pastYear) * 12 + (currentMonth - pastMonth);
// check if the time provided is the previous month but not exceeded 1 month ago.
if (currentTime.getUTCDate() < pastTime.getUTCDate()) {
monthDiff--;
}
if (monthDiff > 0) {
const unitAmount = monthDiff;
return `${unitAmount}${dense ? timeUnits[i].unit[0] : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
}
} else if (elapsed >= timeUnits[i].value) {
const unitAmount = Math.floor(elapsed / timeUnits[i].value);
return `${unitAmount}${dense ? timeUnits[i].unit[0] : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
}
}
} catch {
return 'Invalid Time';
}
};
const TimeAgo = (props) => {
return <span>{timeAgo({ ...props })}</span>;
};
export default TimeAgo;

24
web/src/icons/Clock.jsx Normal file
View File

@@ -0,0 +1,24 @@
import { h } from 'preact';
import { memo } from 'preact/compat';
export function Clock({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
className={className}
fill={fill}
viewBox="0 0 24 24"
stroke={stroke}
onClick={onClick}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
);
}
export default memo(Clock);

View File

@@ -6,7 +6,6 @@ import Heading from '../components/Heading';
import WebRtcPlayer from '../components/WebRtcPlayer';
import MsePlayer from '../components/MsePlayer';
import useSWR from 'swr';
import videojs from 'video.js';
export default function Birdseye() {
const { data: config } = useSWR('config');
@@ -19,16 +18,8 @@ export default function Birdseye() {
}
let player;
if (viewSource == 'mse' && config.restream.birdseye) {
if (videojs.browser.IS_IOS) {
player = (
<Fragment>
<div className="w-5xl text-center text-sm">
MSE is not supported on iOS devices. You'll need to use jsmpeg or webRTC. See the docs for more info.
</div>
</Fragment>
);
} else {
if (viewSource == 'mse' && config.birdseye.restream) {
if ('MediaSource' in window) {
player = (
<Fragment>
<div className="max-w-5xl">
@@ -36,8 +27,16 @@ export default function Birdseye() {
</div>
</Fragment>
);
} else {
player = (
<Fragment>
<div className="w-5xl text-center text-sm">
MSE is not supported on iOS devices. You'll need to use jsmpeg or webRTC. See the docs for more info.
</div>
</Fragment>
);
}
} else if (viewSource == 'webrtc' && config.restream.birdseye) {
} else if (viewSource == 'webrtc' && config.birdseye.restream) {
player = (
<Fragment>
<div className="max-w-5xl">
@@ -62,7 +61,7 @@ export default function Birdseye() {
Birdseye
</Heading>
{config.restream.birdseye && (
{config.birdseye.restream && (
<select
className="basis-1/8 cursor-pointer rounded dark:bg-slate-800"
value={viewSource}

Some files were not shown because too many files have changed in this diff Show More