forked from Github/frigate
Compare commits
20 Commits
v0.12.0-be
...
v0.12.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf2466c8c1 | ||
|
|
6b123675c4 | ||
|
|
ddcae2d4aa | ||
|
|
731db8fb8f | ||
|
|
cb0c5c2587 | ||
|
|
1643b4d108 | ||
|
|
acd1fb9e3e | ||
|
|
ddde477770 | ||
|
|
3edbb8dc41 | ||
|
|
581c2591ae | ||
|
|
d49359e26a | ||
|
|
e79eab711a | ||
|
|
5b7cd9ce64 | ||
|
|
3cb96091ec | ||
|
|
fdd2cc972e | ||
|
|
61243ad34b | ||
|
|
5f4c439f57 | ||
|
|
1f963ec5aa | ||
|
|
bcbf0061ff | ||
|
|
57dce4ec38 |
@@ -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.7/go2rtc_linux_${TARGETARCH}" \
|
||||
&& chmod +x go2rtc
|
||||
|
||||
|
||||
|
||||
@@ -12,10 +12,7 @@ apt-get -qq install --no-install-recommends -y \
|
||||
unzip locales tzdata libxml2 xz-utils \
|
||||
python3-pip
|
||||
|
||||
# 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 +27,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
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
log:
|
||||
format: text
|
||||
|
||||
webrtc:
|
||||
listen: ":8555"
|
||||
candidates:
|
||||
|
||||
@@ -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_'.
|
||||
|
||||
@@ -24,6 +24,9 @@ webRTC works by creating a websocket connection on extra ports. One of the follo
|
||||
* For local webRTC, you will need to create your own go2rtc config:
|
||||
|
||||
```yaml
|
||||
log:
|
||||
format: text
|
||||
|
||||
webrtc:
|
||||
listen: ":8555"
|
||||
candidates:
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
@@ -863,8 +863,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
|
||||
@@ -968,7 +969,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
@@ -75,6 +80,8 @@ PRESETS_HW_ACCEL_DECODE = {
|
||||
}
|
||||
|
||||
PRESETS_HW_ACCEL_SCALE = {
|
||||
"preset-rpi-32-h264": ["-f", "rawvideo", "-pix_fmt", "yuv420p"],
|
||||
"preset-rpi-64-h264": ["-f", "rawvideo", "-pix_fmt", "yuv420p"],
|
||||
"preset-vaapi": [
|
||||
"-vf",
|
||||
"fps={},scale_vaapi=w={}:h={},hwdownload,format=yuv420p",
|
||||
@@ -83,13 +90,13 @@ PRESETS_HW_ACCEL_SCALE = {
|
||||
],
|
||||
"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",
|
||||
"vpp_qsv=framerate={},scale_qsv=w={}:h={}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
],
|
||||
"preset-intel-qsv-h265": [
|
||||
"-vf",
|
||||
"vpp_qsv=framerate={}:scale_mode=1:w={}:h={}:detail=50:denoise=100:deinterlace=2:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||
"vpp_qsv=framerate={},scale_qsv=w={}:h={}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
],
|
||||
@@ -114,6 +121,8 @@ PRESETS_HW_ACCEL_SCALE = {
|
||||
}
|
||||
|
||||
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}",
|
||||
@@ -122,6 +131,8 @@ PRESETS_HW_ACCEL_ENCODE = {
|
||||
}
|
||||
|
||||
PRESETS_HW_ACCEL_GO2RTC_ENGINE = {
|
||||
"preset-rpi-32-h264": "v4l2m2m",
|
||||
"preset-rpi-64-h264": "v4l2m2m",
|
||||
"preset-intel-vaapi": "vaapi",
|
||||
"preset-intel-qsv-h264": "vaapi", # go2rtc doesn't support qsv
|
||||
"preset-intel-qsv-h265": "vaapi",
|
||||
@@ -258,7 +269,7 @@ PRESETS_INPUT = {
|
||||
"+genpts+discardcorrupt",
|
||||
"-rtsp_transport",
|
||||
"tcp",
|
||||
"-timeout",
|
||||
TIMEOUT_PARAM,
|
||||
"5000000",
|
||||
"-use_wallclock_as_timestamps",
|
||||
"1",
|
||||
@@ -271,7 +282,7 @@ PRESETS_INPUT = {
|
||||
"+genpts+discardcorrupt",
|
||||
"-rtsp_transport",
|
||||
"udp",
|
||||
"-timeout",
|
||||
TIMEOUT_PARAM,
|
||||
"5000000",
|
||||
"-use_wallclock_as_timestamps",
|
||||
"1",
|
||||
@@ -290,7 +301,7 @@ PRESETS_INPUT = {
|
||||
"+genpts+discardcorrupt",
|
||||
"-rtsp_transport",
|
||||
"tcp",
|
||||
"-timeout",
|
||||
TIMEOUT_PARAM,
|
||||
"5000000",
|
||||
"-use_wallclock_as_timestamps",
|
||||
"1",
|
||||
|
||||
@@ -565,6 +565,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 +603,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 +619,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 +643,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:
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@ class RestreamApi:
|
||||
input.path.startswith("rtsp")
|
||||
and not camera.restream.force_audio
|
||||
):
|
||||
self.relays[cam_name] = escape_special_characters(input.path)
|
||||
self.relays[
|
||||
cam_name
|
||||
] = f"{escape_special_characters(input.path)}#backchannel=0"
|
||||
else:
|
||||
# go2rtc only supports rtsp for direct relay, otherwise ffmpeg is used
|
||||
self.relays[cam_name] = get_manual_go2rtc_stream(
|
||||
|
||||
@@ -738,12 +738,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 +816,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 +846,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 +882,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 +932,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(",")
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)}`;
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function Camera({ camera }) {
|
||||
? Math.round(cameraConfig.restream.jsmpeg.height * (cameraConfig.detect.width / cameraConfig.detect.height))
|
||||
: 0;
|
||||
const [viewSource, setViewSource, sourceIsLoaded] = usePersistence(`${camera}-source`, 'mse');
|
||||
const sourceValues = cameraConfig && cameraConfig.restream.enabled ? ['mse', 'webrtc', 'jsmpeg'] : ['mse'];
|
||||
const sourceValues = cameraConfig && cameraConfig.restream.enabled ? ['mse', 'webrtc', 'jsmpeg'] : ['jsmpeg'];
|
||||
const [options, setOptions] = usePersistence(`${camera}-feed`, emptyObject);
|
||||
|
||||
const handleSetOption = useCallback(
|
||||
@@ -77,7 +77,7 @@ export default function Camera({ camera }) {
|
||||
labelPosition="after"
|
||||
/>
|
||||
<Switch checked={options['zones']} id="zones" onChange={handleSetOption} label="Zones" labelPosition="after" />
|
||||
<Switch checked={options['mask']} id="mask" onChange={handleSetOption} label="Masks" labelPosition="after" />
|
||||
<Switch checked={options['mask']} id="mask" onChange={handleSetOption} label="Motion Masks" labelPosition="after" />
|
||||
<Switch
|
||||
checked={options['motion']}
|
||||
id="motion"
|
||||
@@ -98,7 +98,7 @@ export default function Camera({ camera }) {
|
||||
|
||||
let player;
|
||||
if (viewMode === 'live') {
|
||||
if (viewSource == 'mse') {
|
||||
if (viewSource == 'mse' && cameraConfig.restream.enabled) {
|
||||
if (videojs.browser.IS_IOS) {
|
||||
player = (
|
||||
<Fragment>
|
||||
@@ -116,7 +116,7 @@ export default function Camera({ camera }) {
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
} else if (viewSource == 'webrtc') {
|
||||
} else if (viewSource == 'webrtc' && cameraConfig.restream.enabled) {
|
||||
player = (
|
||||
<Fragment>
|
||||
<div className="max-w-5xl">
|
||||
@@ -170,7 +170,7 @@ export default function Camera({ camera }) {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<ButtonsTabbed viewModes={['live', 'debug']} setViewMode={setViewMode} />
|
||||
<ButtonsTabbed viewModes={['live', 'debug']} currentViewMode={viewMode} setViewMode={setViewMode} />
|
||||
|
||||
{player}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useEffect, useState } from 'preact/hooks';
|
||||
import Button from '../components/Button';
|
||||
import { editor, Uri } from 'monaco-editor';
|
||||
import { setDiagnosticsOptions } from 'monaco-yaml';
|
||||
import copy from 'copy-to-clipboard';
|
||||
|
||||
export default function Config() {
|
||||
const apiHost = useApiHost();
|
||||
@@ -40,7 +41,7 @@ export default function Config() {
|
||||
};
|
||||
|
||||
const handleCopyConfig = async () => {
|
||||
await window.navigator.clipboard.writeText(window.editor.getValue());
|
||||
copy(window.editor.getValue());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function Logs() {
|
||||
<div className="space-y-4 p-2 px-4">
|
||||
<Heading>Logs</Heading>
|
||||
|
||||
<ButtonsTabbed viewModes={['frigate', 'go2rtc', 'nginx']} setViewMode={setLogService} />
|
||||
<ButtonsTabbed viewModes={['frigate', 'go2rtc', 'nginx']} currentViewMode={logService} setViewMode={setLogService} />
|
||||
|
||||
<Button className="" onClick={handleCopyLogs}>
|
||||
Copy to Clipboard
|
||||
|
||||
@@ -9,6 +9,7 @@ import axios from 'axios';
|
||||
import { Table, Tbody, Thead, Tr, Th, Td } from '../components/Table';
|
||||
import { useState } from 'preact/hooks';
|
||||
import Dialog from '../components/Dialog';
|
||||
import copy from 'copy-to-clipboard';
|
||||
|
||||
const emptyObject = Object.freeze({});
|
||||
|
||||
@@ -54,7 +55,7 @@ export default function System() {
|
||||
};
|
||||
|
||||
const onCopyFfprobe = async () => {
|
||||
await window.navigator.clipboard.writeText(JSON.stringify(state.ffprobe, null, 2));
|
||||
copy(JSON.stringify(state.ffprobe, null, 2));
|
||||
setState({ ...state, ffprobe: '', showFfprobe: false });
|
||||
};
|
||||
|
||||
@@ -73,7 +74,7 @@ export default function System() {
|
||||
};
|
||||
|
||||
const onCopyVainfo = async () => {
|
||||
await window.navigator.clipboard.writeText(JSON.stringify(state.vaifp, null, 2));
|
||||
copy(JSON.stringify(state.vainfo, null, 2));
|
||||
setState({ ...state, vainfo: '', showVainfo: false });
|
||||
};
|
||||
|
||||
@@ -141,15 +142,17 @@ export default function System() {
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>P-ID</Th>
|
||||
<Th>Detection Start</Th>
|
||||
<Th>Inference Speed</Th>
|
||||
<Th>CPU %</Th>
|
||||
<Th>Memory %</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<Tr>
|
||||
<Td>{detectors[detector]['pid']}</Td>
|
||||
<Td>{detectors[detector]['detection_start']}</Td>
|
||||
<Td>{detectors[detector]['inference_speed']}</Td>
|
||||
<Td>{detectors[detector]['inference_speed']} ms</Td>
|
||||
<Td>{cpu_usages[detectors[detector]['pid']]?.['cpu'] || '- '}%</Td>
|
||||
<Td>{cpu_usages[detectors[detector]['pid']]?.['mem'] || '- '}%</Td>
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
@@ -184,7 +187,7 @@ export default function System() {
|
||||
<Table className="w-full">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Gpu %</Th>
|
||||
<Th>GPU %</Th>
|
||||
<Th>Memory %</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -219,20 +222,27 @@ export default function System() {
|
||||
<Tr>
|
||||
<Th>Process</Th>
|
||||
<Th>P-ID</Th>
|
||||
<Th>fps</Th>
|
||||
<Th>Cpu %</Th>
|
||||
<Th>FPS</Th>
|
||||
<Th>CPU %</Th>
|
||||
<Th>Memory %</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<Tr key="capture" index="0">
|
||||
<Tr key="ffmpeg" index="0">
|
||||
<Td>ffmpeg</Td>
|
||||
<Td>{cameras[camera]['ffmpeg_pid'] || '- '}</Td>
|
||||
<Td>{cameras[camera]['camera_fps'] || '- '}</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]?.['cpu'] || '- '}%</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]?.['mem'] || '- '}%</Td>
|
||||
</Tr>
|
||||
<Tr key="capture" index="1">
|
||||
<Td>Capture</Td>
|
||||
<Td>{cameras[camera]['capture_pid'] || '- '}</Td>
|
||||
<Td>{cameras[camera]['process_fps'] || '- '}</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['capture_pid']]?.['cpu'] || '- '}%</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['capture_pid']]?.['mem'] || '- '}%</Td>
|
||||
</Tr>
|
||||
<Tr key="detect" index="1">
|
||||
<Tr key="detect" index="2">
|
||||
<Td>Detect</Td>
|
||||
<Td>{cameras[camera]['pid'] || '- '}</Td>
|
||||
<Td>
|
||||
@@ -241,13 +251,6 @@ export default function System() {
|
||||
<Td>{cpu_usages[cameras[camera]['pid']]?.['cpu'] || '- '}%</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['pid']]?.['mem'] || '- '}%</Td>
|
||||
</Tr>
|
||||
<Tr key="ffmpeg" index="2">
|
||||
<Td>ffmpeg</Td>
|
||||
<Td>{cameras[camera]['ffmpeg_pid'] || '- '}</Td>
|
||||
<Td>{cameras[camera]['camera_fps'] || '- '}</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]?.['cpu'] || '- '}%</Td>
|
||||
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]?.['mem'] || '- '}%</Td>
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user