forked from Github/frigate
Compare commits
12 Commits
v0.12.0-be
...
v0.12.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12d51d3c73 | ||
|
|
5fa1fbafed | ||
|
|
d36ab05bf1 | ||
|
|
fb85c8ca54 | ||
|
|
b2a2a9d6c7 | ||
|
|
e2239d36c9 | ||
|
|
6620236bc3 | ||
|
|
02df2a8bbd | ||
|
|
89513e469a | ||
|
|
f5466426df | ||
|
|
2631a4c35b | ||
|
|
ef9338f5af |
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -6,6 +6,11 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
# only run the latest commit to avoid cache overwrites
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: 3.9
|
||||
|
||||
|
||||
16
Dockerfile
16
Dockerfile
@@ -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 \
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
# shellcheck shell=bash
|
||||
# Start the fake Frigate service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
# Tell S6-Overlay not to restart this service
|
||||
s6-svc -O .
|
||||
|
||||
while true; do
|
||||
echo "The fake Frigate service is running..."
|
||||
echo "[INFO] The fake Frigate service is running..."
|
||||
sleep 5s
|
||||
done
|
||||
|
||||
@@ -10,7 +10,9 @@ apt-get -qq install --no-install-recommends -y \
|
||||
wget \
|
||||
procps vainfo \
|
||||
unzip locales tzdata libxml2 xz-utils \
|
||||
python3-pip
|
||||
python3-pip \
|
||||
curl \
|
||||
jq
|
||||
|
||||
mkdir -p -m 600 /root/.gnupg
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
frigate
|
||||
@@ -0,0 +1 @@
|
||||
frigate-pipeline
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate-log/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate-log/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
30
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/finish
Executable file
30
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/finish
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service exits
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
declare exit_code_container
|
||||
exit_code_container=$(cat /run/s6-linux-init-container-results/exitcode)
|
||||
readonly exit_code_container
|
||||
readonly exit_code_service="${1}"
|
||||
readonly exit_code_signal="${2}"
|
||||
readonly service="Frigate"
|
||||
|
||||
echo "Service ${service} exited with code ${exit_code_service} (by signal ${exit_code_signal})" >&2
|
||||
|
||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo $((128 + exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
else
|
||||
# Exit code 0 is expected when Frigate is restarted by the user. In this case,
|
||||
# we create a signal for the go2rtc finish script to tolerate the restart.
|
||||
touch /dev/shm/restarting-frigate
|
||||
fi
|
||||
|
||||
exec /run/s6/basedir/bin/halt
|
||||
@@ -0,0 +1 @@
|
||||
frigate-log
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
cd /opt/frigate
|
||||
# Tell S6-Overlay not to restart this service
|
||||
s6-svc -O .
|
||||
|
||||
echo "[INFO] Starting Frigate..." >&2
|
||||
|
||||
cd /opt/frigate || echo "[ERROR] Failed to change working directory to /opt/frigate" >&2
|
||||
|
||||
# Replace the bash process with the Frigate process, redirecting stderr to stdout
|
||||
exec 2>&1
|
||||
@@ -0,0 +1 @@
|
||||
120000
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/frigate/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
@@ -0,0 +1 @@
|
||||
go2rtc
|
||||
@@ -0,0 +1 @@
|
||||
go2rtc-pipeline
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc-log/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc-log/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
32
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/finish
Executable file
32
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/finish
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service exits
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
declare exit_code_container
|
||||
exit_code_container=$(cat /run/s6-linux-init-container-results/exitcode)
|
||||
readonly exit_code_container
|
||||
readonly exit_code_service="${1}"
|
||||
readonly exit_code_signal="${2}"
|
||||
readonly service="go2rtc"
|
||||
|
||||
echo "Service ${service} exited with code ${exit_code_service} (by signal ${exit_code_signal})" >&2
|
||||
|
||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo $((128 + exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
else
|
||||
# go2rtc is not supposed to exit, so even when it exits with 0 we make the
|
||||
# container with 1. We only tolerate it when Frigate is restarting.
|
||||
if [[ "${exit_code_container}" -eq 0 && ! -f /dev/shm/restarting-frigate ]]; then
|
||||
echo "1" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
fi
|
||||
|
||||
exec /run/s6/basedir/bin/halt
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/producer-for
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/producer-for
Normal file
@@ -0,0 +1 @@
|
||||
go2rtc-log
|
||||
60
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run
Executable file
60
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Start the go2rtc service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
# Tell S6-Overlay not to restart this service
|
||||
s6-svc -O .
|
||||
|
||||
function get_ip_and_port_from_supervisor() {
|
||||
local ip_address
|
||||
# Example: 192.168.1.10/24
|
||||
local ip_regex='^([0-9]{1,3}\.{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}"
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/timeout-kill
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/timeout-kill
Normal file
@@ -0,0 +1 @@
|
||||
30000
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/type
Normal file
@@ -0,0 +1 @@
|
||||
oneshot
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/up
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/up
Normal file
@@ -0,0 +1 @@
|
||||
/etc/s6-overlay/s6-rc.d/log-prepare/run
|
||||
@@ -0,0 +1 @@
|
||||
nginx
|
||||
@@ -0,0 +1 @@
|
||||
nginx-pipeline
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx-log/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx-log/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
28
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish
Executable file
28
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service fails
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
declare exit_code_container
|
||||
exit_code_container=$(cat /run/s6-linux-init-container-results/exitcode)
|
||||
readonly exit_code_container
|
||||
readonly exit_code_service="${1}"
|
||||
readonly exit_code_signal="${2}"
|
||||
readonly service="NGINX"
|
||||
|
||||
echo "Service ${service} exited with code ${exit_code_service} (by signal ${exit_code_signal})" >&2
|
||||
|
||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo $((128 + exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
if [[ "${exit_code_signal}" -eq 15 ]]; then
|
||||
exec /run/s6/basedir/bin/halt
|
||||
fi
|
||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
||||
fi
|
||||
exec /run/s6/basedir/bin/halt
|
||||
fi
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/producer-for
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/producer-for
Normal file
@@ -0,0 +1 @@
|
||||
nginx-log
|
||||
@@ -2,6 +2,10 @@
|
||||
# shellcheck shell=bash
|
||||
# Start the NGINX service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
echo "[INFO] Starting NGINX..." >&2
|
||||
|
||||
# Replace the bash process with the NGINX process, redirecting stderr to stdout
|
||||
exec 2>&1
|
||||
exec nginx
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/timeout-kill
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/timeout-kill
Normal file
@@ -0,0 +1 @@
|
||||
30000
|
||||
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/type
Normal file
1
docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/type
Normal file
@@ -0,0 +1 @@
|
||||
longrun
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service exits
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
# Prepare exit code
|
||||
if [[ "${1}" -eq 256 ]]; then
|
||||
exit_code="$((128 + ${2}))"
|
||||
else
|
||||
exit_code="${1}"
|
||||
fi
|
||||
|
||||
# Make the container exit with the same exit code as the service
|
||||
echo "${exit_code}" > /run/s6-linux-init-container-results/exitcode
|
||||
exec /run/s6/basedir/bin/halt
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Take down the S6 supervision tree when the service fails, or restart it
|
||||
# otherwise
|
||||
|
||||
if [[ "${1}" -ne 0 && "${1}" -ne 256 ]]; then
|
||||
exec /run/s6/basedir/bin/halt
|
||||
fi
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
# Start the go2rtc service
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
raw_config=$(python3 /usr/local/go2rtc/create_config.py)
|
||||
|
||||
# Replace the bash process with the go2rtc process, redirecting stderr to stdout
|
||||
exec 2>&1
|
||||
exec go2rtc -config="${raw_config}"
|
||||
@@ -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
|
||||
@@ -5,6 +5,8 @@ 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
|
||||
@@ -20,12 +22,43 @@ if config_file.endswith((".yaml", ".yml")):
|
||||
elif config_file.endswith(".json"):
|
||||
config = json.loads(raw_config)
|
||||
|
||||
go2rtc_config: dict[str, any] = config["go2rtc"]
|
||||
go2rtc_config: dict[str, any] = config.get("go2rtc", {})
|
||||
|
||||
if not go2rtc_config.get("log", {}).get("format"):
|
||||
# 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", []):
|
||||
go2rtc_config["webrtc"] = {"candidates": ["stun:8555"]}
|
||||
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")
|
||||
|
||||
print(json.dumps(go2rtc_config))
|
||||
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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -17,30 +17,16 @@ Live view options can be selected while viewing the live stream. The options are
|
||||
|
||||
### 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 use ffmpeg to set both.
|
||||
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:
|
||||
test_cam: ffmpeg:rtsp://192.168.1.5:554/live0#video=copy#audio=aac#audio=opus
|
||||
```
|
||||
|
||||
However, chances are that your camera already provides at least one usable audio type, so you just need restream to add the missing one. For example, if your camera outputs audio in AAC format:
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
test_cam: ffmpeg:rtsp://192.168.1.5:554/live0#video=copy#audio=copy#audio=opus
|
||||
```
|
||||
|
||||
Which will reuse your camera AAC audio, while also adding one track in OPUS format.
|
||||
|
||||
If your camera uses RTSP and supports the audio type for the live view you want to use, then you can pass the camera stream to go2rtc without ffmpeg.
|
||||
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
test_cam: rtsp://192.168.1.5:554/live0
|
||||
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
|
||||
@@ -50,8 +36,12 @@ There may be some cameras that you would prefer to use the sub stream for live v
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
test_cam: ffmpeg:rtsp://192.168.1.5:554/live0#video=copy#audio=aac#audio=opus
|
||||
test_cam_sub: ffmpeg:rtsp://192.168.1.5:554/substream#video=copy#audio=aac#audio=opus
|
||||
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:
|
||||
@@ -76,11 +66,9 @@ cameras:
|
||||
WebRTC works by creating a TCP or UDP connection on port `8555`. However, it requires additional configuration:
|
||||
|
||||
- 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, you will need to use a custom go2rtc config:
|
||||
- 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:
|
||||
|
||||
1. Add your internal IP to the list of `candidates`. Here is an example, assuming that `192.168.1.10` is the local IP of the device running Frigate:
|
||||
|
||||
```yaml
|
||||
```yaml title="/config/frigate.yaml"
|
||||
go2rtc:
|
||||
streams:
|
||||
test_cam: ...
|
||||
@@ -90,6 +78,19 @@ WebRTC works by creating a TCP or UDP connection on port `8555`. However, it req
|
||||
- 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
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note
|
||||
|
||||
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:
|
||||
|
||||
@@ -28,42 +28,77 @@ One connection is made to the camera. One for the restream, `detect` and `record
|
||||
```yaml
|
||||
go2rtc:
|
||||
streams:
|
||||
test_cam: ffmpeg:rtsp://192.168.1.5:554/live0#video=copy#audio=aac#audio=opus
|
||||
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://127.0.0.1:8554/test_cam?video=copy&audio=aac # <--- the name here must match the name of the camera in restream
|
||||
- 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
|
||||
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
|
||||
- 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:
|
||||
test_cam: ffmpeg:rtsp://192.168.1.5:554/live0#video=copy#audio=aac#audio=opus
|
||||
test_cam_sub: ffmpeg:rtsp://192.168.1.5:554/substream#video=copy#audio=aac#audio=opus
|
||||
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://127.0.0.1:8554/test_cam?video=copy&audio=aac # <--- the name here must match the name of the camera in restream
|
||||
- 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://127.0.0.1:8554/test_cam_sub?video=copy&audio=aac # <--- the name here must match the name of the camera_sub in restream
|
||||
- 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:
|
||||
- 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -769,7 +769,10 @@ def config_save():
|
||||
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
|
||||
|
||||
|
||||
@@ -628,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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function Camera({ camera }) {
|
||||
const [viewMode, setViewMode] = useState('live');
|
||||
|
||||
const cameraConfig = config?.cameras[camera];
|
||||
const restreamEnabled = cameraConfig && Object.keys(config.go2rtc.streams).includes(cameraConfig.live.stream_name);
|
||||
const restreamEnabled = cameraConfig && Object.keys(config.go2rtc.streams || {}).includes(cameraConfig.live.stream_name);
|
||||
const jsmpegWidth = cameraConfig
|
||||
? Math.round(cameraConfig.live.height * (cameraConfig.detect.width / cameraConfig.detect.height))
|
||||
: 0;
|
||||
@@ -112,7 +112,7 @@ export default function Camera({ camera }) {
|
||||
player = (
|
||||
<Fragment>
|
||||
<div className="max-w-5xl">
|
||||
<MsePlayer camera={camera} />
|
||||
<MsePlayer camera={cameraConfig.live.stream_name} />
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
@@ -129,7 +129,7 @@ export default function Camera({ camera }) {
|
||||
player = (
|
||||
<Fragment>
|
||||
<div className="max-w-5xl">
|
||||
<WebRtcPlayer camera={camera} />
|
||||
<WebRtcPlayer camera={cameraConfig.live.stream_name} />
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -246,11 +246,14 @@ export default function System() {
|
||||
<Td>Detect</Td>
|
||||
<Td>{cameras[camera]['pid'] || '- '}</Td>
|
||||
|
||||
{cameras[camera]['detection_enabled'] == 1 ? (
|
||||
<Td>{cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped)</Td>
|
||||
) : (
|
||||
<Td>disabled</Td>
|
||||
)}
|
||||
{(() => {
|
||||
if (cameras[camera]['pid'] && cameras[camera]['detection_enabled'] == 1)
|
||||
return <Td>{cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped)</Td>
|
||||
else if (cameras[camera]['pid'] && cameras[camera]['detection_enabled'] == 0)
|
||||
return <Td>disabled</Td>
|
||||
|
||||
return <Td>- </Td>
|
||||
})()}
|
||||
|
||||
<Td>{cpu_usages[cameras[camera]['pid']]?.['cpu'] || '- '}%</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['pid']]?.['mem'] || '- '}%</Td>
|
||||
|
||||
Reference in New Issue
Block a user