mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-23 20:29:02 +03:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d8234fa27 | ||
|
|
ad76c28a66 | ||
|
|
131d07e649 | ||
|
|
776bb79f0b | ||
|
|
aedfaa3641 | ||
|
|
83ac42cbdc | ||
|
|
a5ce8d0d77 | ||
|
|
0ee2e404da | ||
|
|
3947e79086 | ||
|
|
91ab1071d2 | ||
|
|
409e911752 | ||
|
|
9983bd8d92 | ||
|
|
32c71c4108 | ||
|
|
ef6952e3ea |
@@ -33,9 +33,9 @@ runs:
|
||||
with:
|
||||
string: ${{ github.repository }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc
|
||||
with:
|
||||
|
||||
+22
-15
@@ -19,7 +19,7 @@ env:
|
||||
|
||||
jobs:
|
||||
amd64_build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: AMD64 Build
|
||||
steps:
|
||||
- name: Check out code
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
tags: ${{ steps.setup.outputs.image-name }}-amd64
|
||||
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64
|
||||
arm64_build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ARM Build
|
||||
steps:
|
||||
- name: Check out code
|
||||
@@ -66,8 +66,9 @@ jobs:
|
||||
${{ steps.setup.outputs.image-name }}-standard-arm64
|
||||
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
||||
- name: Build and push RPi build
|
||||
uses: docker/bake-action@v4
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: rpi
|
||||
files: docker/rpi/rpi.hcl
|
||||
@@ -76,7 +77,7 @@ jobs:
|
||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64,mode=max
|
||||
jetson_jp4_build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: Jetson Jetpack 4
|
||||
steps:
|
||||
- name: Check out code
|
||||
@@ -94,8 +95,9 @@ jobs:
|
||||
BASE_IMAGE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||
SLIM_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||
TRT_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||
uses: docker/bake-action@v4
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: tensorrt
|
||||
files: docker/tensorrt/trt.hcl
|
||||
@@ -104,7 +106,7 @@ jobs:
|
||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp4
|
||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp4,mode=max
|
||||
jetson_jp5_build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: Jetson Jetpack 5
|
||||
steps:
|
||||
- name: Check out code
|
||||
@@ -122,8 +124,9 @@ jobs:
|
||||
BASE_IMAGE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||
SLIM_BASE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||
TRT_BASE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||
uses: docker/bake-action@v4
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: tensorrt
|
||||
files: docker/tensorrt/trt.hcl
|
||||
@@ -132,7 +135,7 @@ jobs:
|
||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp5
|
||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp5,mode=max
|
||||
amd64_extra_builds:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: AMD64 Extra Build
|
||||
needs:
|
||||
- amd64_build
|
||||
@@ -149,8 +152,9 @@ jobs:
|
||||
- name: Build and push TensorRT (x86 GPU)
|
||||
env:
|
||||
COMPUTE_LEVEL: "50 60 70 80 90"
|
||||
uses: docker/bake-action@v4
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: tensorrt
|
||||
files: docker/tensorrt/trt.hcl
|
||||
@@ -159,7 +163,7 @@ jobs:
|
||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64
|
||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64,mode=max
|
||||
arm64_extra_builds:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ARM Extra Build
|
||||
needs:
|
||||
- arm64_build
|
||||
@@ -174,8 +178,9 @@ jobs:
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push Rockchip build
|
||||
uses: docker/bake-action@v3
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: rk
|
||||
files: docker/rockchip/rk.hcl
|
||||
@@ -183,7 +188,7 @@ jobs:
|
||||
rk.tags=${{ steps.setup.outputs.image-name }}-rk
|
||||
*.cache-from=type=gha
|
||||
combined_extra_builds:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: Combined Extra Builds
|
||||
needs:
|
||||
- amd64_build
|
||||
@@ -199,8 +204,9 @@ jobs:
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push Hailo-8l build
|
||||
uses: docker/bake-action@v4
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: h8l
|
||||
files: docker/hailo8l/h8l.hcl
|
||||
@@ -212,8 +218,9 @@ jobs:
|
||||
env:
|
||||
AMDGPU: gfx
|
||||
HSA_OVERRIDE: 0
|
||||
uses: docker/bake-action@v3
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
targets: rocm
|
||||
files: docker/rocm/rocm.hcl
|
||||
@@ -223,7 +230,7 @@ jobs:
|
||||
# The majority of users running arm64 are rpi users, so the rpi
|
||||
# build should be the primary arm64 image
|
||||
assemble_default_build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: Assemble and push default build
|
||||
needs:
|
||||
- amd64_build
|
||||
|
||||
@@ -215,7 +215,6 @@ ENV TRANSFORMERS_NO_ADVISORY_WARNINGS=1
|
||||
ENV OPENCV_FFMPEG_LOGLEVEL=8
|
||||
|
||||
ENV PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PATH}"
|
||||
ENV LIBAVFORMAT_VERSION_MAJOR=60
|
||||
|
||||
# Install dependencies
|
||||
RUN --mount=type=bind,source=docker/main/install_deps.sh,target=/deps/install_deps.sh \
|
||||
|
||||
@@ -42,8 +42,14 @@ function migrate_db_path() {
|
||||
fi
|
||||
}
|
||||
|
||||
function set_libva_version() {
|
||||
local ffmpeg_path=$(python3 /usr/local/ffmpeg/get_ffmpeg_path.py)
|
||||
export LIBAVFORMAT_VERSION_MAJOR=$($ffmpeg_path -version | grep -Po "libavformat\W+\K\d+")
|
||||
}
|
||||
|
||||
echo "[INFO] Preparing Frigate..."
|
||||
migrate_db_path
|
||||
set_libva_version
|
||||
echo "[INFO] Starting Frigate..."
|
||||
|
||||
cd /opt/frigate || echo "[ERROR] Failed to change working directory to /opt/frigate"
|
||||
|
||||
@@ -43,6 +43,11 @@ function get_ip_and_port_from_supervisor() {
|
||||
export FRIGATE_GO2RTC_WEBRTC_CANDIDATE_INTERNAL="${ip_address}:${webrtc_port}"
|
||||
}
|
||||
|
||||
function set_libva_version() {
|
||||
local ffmpeg_path=$(python3 /usr/local/ffmpeg/get_ffmpeg_path.py)
|
||||
export LIBAVFORMAT_VERSION_MAJOR=$($ffmpeg_path -version | grep -Po "libavformat\W+\K\d+")
|
||||
}
|
||||
|
||||
if [[ -f "/dev/shm/go2rtc.yaml" ]]; then
|
||||
echo "[INFO] Removing stale config from last run..."
|
||||
rm /dev/shm/go2rtc.yaml
|
||||
@@ -61,6 +66,8 @@ else
|
||||
echo "[WARNING] Unable to remove existing go2rtc config. Changes made to your frigate config file may not be recognized. Please remove the /dev/shm/go2rtc.yaml from your docker host manually."
|
||||
fi
|
||||
|
||||
set_libva_version
|
||||
|
||||
readonly config_path="/config"
|
||||
|
||||
if [[ -x "${config_path}/go2rtc" ]]; then
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
sys.path.insert(0, "/opt/frigate")
|
||||
from frigate.const import (
|
||||
DEFAULT_FFMPEG_VERSION,
|
||||
INCLUDED_FFMPEG_VERSIONS,
|
||||
)
|
||||
|
||||
sys.path.remove("/opt/frigate")
|
||||
|
||||
yaml = YAML()
|
||||
|
||||
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
|
||||
|
||||
try:
|
||||
with open(config_file) as f:
|
||||
raw_config = f.read()
|
||||
|
||||
if config_file.endswith((".yaml", ".yml")):
|
||||
config: dict[str, any] = yaml.load(raw_config)
|
||||
elif config_file.endswith(".json"):
|
||||
config: dict[str, any] = json.loads(raw_config)
|
||||
except FileNotFoundError:
|
||||
config: dict[str, any] = {}
|
||||
|
||||
path = config.get("ffmpeg", {}).get("path", "default")
|
||||
if path == "default":
|
||||
if shutil.which("ffmpeg") is None:
|
||||
print(f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg")
|
||||
else:
|
||||
print("ffmpeg")
|
||||
elif path in INCLUDED_FFMPEG_VERSIONS:
|
||||
print(f"/usr/lib/ffmpeg/{path}/bin/ffmpeg")
|
||||
else:
|
||||
print(f"{path}/bin/ffmpeg")
|
||||
@@ -22,6 +22,6 @@ ADD https://github.com/MarcA711/rknn-toolkit2/releases/download/v2.0.0/librknnrt
|
||||
|
||||
RUN rm -rf /usr/lib/btbn-ffmpeg/bin/ffmpeg
|
||||
RUN rm -rf /usr/lib/btbn-ffmpeg/bin/ffprobe
|
||||
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-5/ffmpeg /usr/lib/ffmpeg/6.0/bin/
|
||||
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-5/ffprobe /usr/lib/ffmpeg/6.0/bin/
|
||||
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-7/ffmpeg /usr/lib/ffmpeg/6.0/bin/
|
||||
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-7/ffprobe /usr/lib/ffmpeg/6.0/bin/
|
||||
ENV PATH="/usr/lib/ffmpeg/6.0/bin/:${PATH}"
|
||||
|
||||
@@ -12,7 +12,5 @@ RUN rm -rf /usr/lib/btbn-ffmpeg/
|
||||
RUN --mount=type=bind,source=docker/rpi/install_deps.sh,target=/deps/install_deps.sh \
|
||||
/deps/install_deps.sh
|
||||
|
||||
ENV LIBAVFORMAT_VERSION_MAJOR=58
|
||||
|
||||
WORKDIR /opt/frigate/
|
||||
COPY --from=rootfs / /
|
||||
|
||||
@@ -548,6 +548,8 @@ genai:
|
||||
|
||||
# Optional: Restream configuration
|
||||
# Uses https://github.com/AlexxIT/go2rtc (v1.9.2)
|
||||
# NOTE: The default go2rtc API port (1984) must be used,
|
||||
# changing this port for the integrated go2rtc instance is not supported.
|
||||
go2rtc:
|
||||
|
||||
# Optional: Live stream configuration for WebUI.
|
||||
|
||||
@@ -47,7 +47,7 @@ that card.
|
||||
|
||||
## Configuration
|
||||
|
||||
When configuring the integration, you will be asked for the `URL` of your Frigate instance which needs to be pointed at the internal unauthenticated port (`5000`) for your instance. This may look like `http://<host>:5000/`.
|
||||
When configuring the integration, you will be asked for the `URL` of your Frigate instance which can be pointed at the internal unauthenticated port (`5000`) or the authenticated port (`8971`) for your instance. This may look like `http://<host>:5000/`.
|
||||
|
||||
### Docker Compose Examples
|
||||
|
||||
@@ -55,7 +55,7 @@ If you are running Home Assistant Core and Frigate with Docker Compose on the sa
|
||||
|
||||
#### Home Assistant running with host networking
|
||||
|
||||
It is not recommended to run Frigate in host networking mode. In this example, you would use `http://172.17.0.1:5000` when configuring the integration.
|
||||
It is not recommended to run Frigate in host networking mode. In this example, you would use `http://172.17.0.1:5000` or `http://172.17.0.1:8971` when configuring the integration.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -75,7 +75,7 @@ services:
|
||||
|
||||
#### Home Assistant _not_ running with host networking or in a separate compose file
|
||||
|
||||
In this example, you would use `http://frigate:5000` when configuring the integration. There is no need to map the port for the Frigate container.
|
||||
In this example, it is recommended to connect to the authenticated port, for example, `http://frigate:8971` when configuring the integration. There is no need to map the port for the Frigate container.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -103,14 +103,15 @@ If you are using HassOS with the addon, the URL should be one of the following d
|
||||
| Frigate NVR (Full Access) | `http://ccab4aaf-frigate-fa:5000` |
|
||||
| Frigate NVR Beta | `http://ccab4aaf-frigate-beta:5000` |
|
||||
| Frigate NVR Beta (Full Access) | `http://ccab4aaf-frigate-fa-beta:5000` |
|
||||
| Frigate NVR HailoRT Beta | `http://ccab4aaf-frigate-hailo-beta:5000` |
|
||||
|
||||
### Frigate running on a separate machine
|
||||
|
||||
If you run Frigate on a separate device within your local network, Home Assistant will need access to port 5000.
|
||||
If you run Frigate on a separate device within your local network, Home Assistant will need access to port 8971.
|
||||
|
||||
#### Local network
|
||||
|
||||
Use `http://<frigate_device_ip>:5000` as the URL for the integration. If you want to protect access to port 5000, you can use firewall rules to limit access to the device running Home Assistant.
|
||||
Use `http://<frigate_device_ip>:8971` as the URL for the integration so that authentication is required.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -118,7 +119,7 @@ services:
|
||||
image: ghcr.io/blakeblackshear/frigate:stable
|
||||
...
|
||||
ports:
|
||||
- "5000:5000"
|
||||
- "8971:8971"
|
||||
...
|
||||
```
|
||||
|
||||
@@ -195,12 +196,30 @@ To load a snapshot for a tracked object:
|
||||
https://HA_URL/api/frigate/notifications/<event-id>/snapshot.jpg
|
||||
```
|
||||
|
||||
To load a video clip of a tracked object:
|
||||
To load a video clip of a tracked object using an Android device:
|
||||
|
||||
```
|
||||
https://HA_URL/api/frigate/notifications/<event-id>/clip.mp4
|
||||
```
|
||||
|
||||
To load a video clip of a tracked object using an iOS device:
|
||||
|
||||
```
|
||||
https://HA_URL/api/frigate/notifications/<event-id>/master.m3u8
|
||||
```
|
||||
|
||||
To load a preview gif of a tracked object:
|
||||
|
||||
```
|
||||
https://HA_URL/api/frigate/notifications/<event-id>/event_preview.gif
|
||||
```
|
||||
|
||||
To load a preview gif of a review item:
|
||||
|
||||
```
|
||||
https://HA_URL/api/frigate/notifications/<review-id>/review_preview.gif
|
||||
```
|
||||
|
||||
<a name="streams"></a>
|
||||
|
||||
## RTSP stream
|
||||
|
||||
+17
-11
@@ -133,6 +133,15 @@ def latest_frame(
|
||||
"regions": params.regions,
|
||||
}
|
||||
quality = params.quality
|
||||
mime_type = extension
|
||||
|
||||
if extension == "png":
|
||||
quality_params = None
|
||||
elif extension == "webp":
|
||||
quality_params = [int(cv2.IMWRITE_WEBP_QUALITY), quality]
|
||||
else:
|
||||
quality_params = [int(cv2.IMWRITE_JPEG_QUALITY), quality]
|
||||
mime_type = "jpeg"
|
||||
|
||||
if camera_name in request.app.frigate_config.cameras:
|
||||
frame = frame_processor.get_current_frame(camera_name, draw_options)
|
||||
@@ -173,13 +182,11 @@ def latest_frame(
|
||||
|
||||
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
||||
|
||||
ret, img = cv2.imencode(
|
||||
f".{extension}", frame, [int(cv2.IMWRITE_WEBP_QUALITY), quality]
|
||||
)
|
||||
ret, img = cv2.imencode(f".{extension}", frame, quality_params)
|
||||
return Response(
|
||||
content=img.tobytes(),
|
||||
media_type=f"image/{extension}",
|
||||
headers={"Content-Type": f"image/{extension}", "Cache-Control": "no-store"},
|
||||
media_type=f"image/{mime_type}",
|
||||
headers={"Content-Type": f"image/{mime_type}", "Cache-Control": "no-store"},
|
||||
)
|
||||
elif camera_name == "birdseye" and request.app.frigate_config.birdseye.restream:
|
||||
frame = cv2.cvtColor(
|
||||
@@ -192,13 +199,11 @@ def latest_frame(
|
||||
|
||||
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
||||
|
||||
ret, img = cv2.imencode(
|
||||
f".{extension}", frame, [int(cv2.IMWRITE_WEBP_QUALITY), quality]
|
||||
)
|
||||
ret, img = cv2.imencode(f".{extension}", frame, quality_params)
|
||||
return Response(
|
||||
content=img.tobytes(),
|
||||
media_type=f"image/{extension}",
|
||||
headers={"Content-Type": f"image/{extension}", "Cache-Control": "no-store"},
|
||||
media_type=f"image/{mime_type}",
|
||||
headers={"Content-Type": f"image/{mime_type}", "Cache-Control": "no-store"},
|
||||
)
|
||||
else:
|
||||
return JSONResponse(
|
||||
@@ -241,6 +246,7 @@ def get_snapshot_from_recording(
|
||||
recording: Recordings = recording_query.get()
|
||||
time_in_segment = frame_time - recording.start_time
|
||||
codec = "png" if format == "png" else "mjpeg"
|
||||
mime_type = "png" if format == "png" else "jpeg"
|
||||
config: FrigateConfig = request.app.frigate_config
|
||||
|
||||
image_data = get_image_from_recording(
|
||||
@@ -257,7 +263,7 @@ def get_snapshot_from_recording(
|
||||
),
|
||||
status_code=404,
|
||||
)
|
||||
return Response(image_data, headers={"Content-Type": f"image/{format}"})
|
||||
return Response(image_data, headers={"Content-Type": f"image/{mime_type}"})
|
||||
except DoesNotExist:
|
||||
return JSONResponse(
|
||||
content={
|
||||
|
||||
@@ -50,16 +50,9 @@ class LibvaGpuSelector:
|
||||
return ""
|
||||
|
||||
|
||||
FPS_VFR_PARAM = (
|
||||
"-fps_mode vfr"
|
||||
if int(os.getenv("LIBAVFORMAT_VERSION_MAJOR", "59") or "59") >= 59
|
||||
else "-vsync 2"
|
||||
)
|
||||
TIMEOUT_PARAM = (
|
||||
"-timeout"
|
||||
if int(os.getenv("LIBAVFORMAT_VERSION_MAJOR", "59") or "59") >= 59
|
||||
else "-stimeout"
|
||||
)
|
||||
LIBAV_VERSION = int(os.getenv("LIBAVFORMAT_VERSION_MAJOR", "59") or "59")
|
||||
FPS_VFR_PARAM = "-fps_mode vfr" if LIBAV_VERSION >= 59 else "-vsync 2"
|
||||
TIMEOUT_PARAM = "-timeout" if LIBAV_VERSION >= 59 else "-stimeout"
|
||||
|
||||
_gpu_selector = LibvaGpuSelector()
|
||||
_user_agent_args = [
|
||||
@@ -71,8 +64,8 @@ PRESETS_HW_ACCEL_DECODE = {
|
||||
"preset-rpi-64-h264": "-c:v:1 h264_v4l2m2m",
|
||||
"preset-rpi-64-h265": "-c:v:1 hevc_v4l2m2m",
|
||||
FFMPEG_HWACCEL_VAAPI: f"-hwaccel_flags allow_profile_mismatch -hwaccel vaapi -hwaccel_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format vaapi",
|
||||
"preset-intel-qsv-h264": f"-hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v h264_qsv -bsf:v dump_extra", # https://trac.ffmpeg.org/ticket/9766#comment:17
|
||||
"preset-intel-qsv-h265": f"-load_plugin hevc_hw -hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v hevc_qsv -bsf:v dump_extra", # https://trac.ffmpeg.org/ticket/9766#comment:17
|
||||
"preset-intel-qsv-h264": f"-hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v h264_qsv{' -bsf:v dump_extra' if LIBAV_VERSION >= 61 else ''}", # https://trac.ffmpeg.org/ticket/9766#comment:17
|
||||
"preset-intel-qsv-h265": f"-load_plugin hevc_hw -hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv{' -bsf:v dump_extra' if LIBAV_VERSION >= 61 else ''}", # https://trac.ffmpeg.org/ticket/9766#comment:17
|
||||
FFMPEG_HWACCEL_NVIDIA: "-hwaccel cuda -hwaccel_output_format cuda",
|
||||
"preset-jetson-h264": "-c:v h264_nvmpi -resize {1}x{2}",
|
||||
"preset-jetson-h265": "-c:v hevc_nvmpi -resize {1}x{2}",
|
||||
|
||||
@@ -121,22 +121,29 @@ class RecordingCleanup(threading.Thread):
|
||||
review_start = 0
|
||||
deleted_recordings = set()
|
||||
kept_recordings: list[tuple[float, float]] = []
|
||||
recording: Recordings
|
||||
for recording in recordings:
|
||||
keep = False
|
||||
mode = None
|
||||
# Now look for a reason to keep this recording segment
|
||||
for idx in range(review_start, len(reviews)):
|
||||
review: ReviewSegment = reviews[idx]
|
||||
severity = review.severity
|
||||
pre_capture = config.record.get_review_pre_capture(severity)
|
||||
post_capture = config.record.get_review_post_capture(severity)
|
||||
|
||||
# if the review starts in the future, stop checking reviews
|
||||
# and let this recording segment expire
|
||||
if review.start_time > recording.end_time:
|
||||
if review.start_time - pre_capture > recording.end_time:
|
||||
keep = False
|
||||
break
|
||||
|
||||
# if the review is in progress or ends after the recording starts, keep it
|
||||
# and stop looking at reviews
|
||||
if review.end_time is None or review.end_time >= recording.start_time:
|
||||
if (
|
||||
review.end_time is None
|
||||
or review.end_time + post_capture >= recording.start_time
|
||||
):
|
||||
keep = True
|
||||
mode = (
|
||||
config.record.alerts.retain.mode
|
||||
@@ -149,7 +156,7 @@ class RecordingCleanup(threading.Thread):
|
||||
# this review and check the next review for an overlap.
|
||||
# since the review and recordings are sorted, we can skip review
|
||||
# that end before the previous recording segment started on future segments
|
||||
if review.end_time < recording.start_time:
|
||||
if review.end_time + post_capture < recording.start_time:
|
||||
review_start = idx
|
||||
|
||||
# Delete recordings outside of the retention window or based on the retention mode
|
||||
|
||||
@@ -85,6 +85,7 @@ type SearchDetailDialogProps = {
|
||||
setSearch: (search: SearchResult | undefined) => void;
|
||||
setSearchPage: (page: SearchTab) => void;
|
||||
setSimilarity?: () => void;
|
||||
setInputFocused: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
export default function SearchDetailDialog({
|
||||
search,
|
||||
@@ -92,6 +93,7 @@ export default function SearchDetailDialog({
|
||||
setSearch,
|
||||
setSearchPage,
|
||||
setSimilarity,
|
||||
setInputFocused,
|
||||
}: SearchDetailDialogProps) {
|
||||
const { data: config } = useSWR<FrigateConfig>("config", {
|
||||
revalidateOnFocus: false,
|
||||
@@ -232,6 +234,7 @@ export default function SearchDetailDialog({
|
||||
config={config}
|
||||
setSearch={setSearch}
|
||||
setSimilarity={setSimilarity}
|
||||
setInputFocused={setInputFocused}
|
||||
/>
|
||||
)}
|
||||
{page == "snapshot" && (
|
||||
@@ -266,12 +269,14 @@ type ObjectDetailsTabProps = {
|
||||
config?: FrigateConfig;
|
||||
setSearch: (search: SearchResult | undefined) => void;
|
||||
setSimilarity?: () => void;
|
||||
setInputFocused: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
function ObjectDetailsTab({
|
||||
search,
|
||||
config,
|
||||
setSearch,
|
||||
setSimilarity,
|
||||
setInputFocused,
|
||||
}: ObjectDetailsTabProps) {
|
||||
const apiHost = useApiHost();
|
||||
|
||||
@@ -283,6 +288,14 @@ function ObjectDetailsTab({
|
||||
|
||||
const [desc, setDesc] = useState(search?.data.description);
|
||||
|
||||
const handleDescriptionFocus = useCallback(() => {
|
||||
setInputFocused(true);
|
||||
}, [setInputFocused]);
|
||||
|
||||
const handleDescriptionBlur = useCallback(() => {
|
||||
setInputFocused(false);
|
||||
}, [setInputFocused]);
|
||||
|
||||
// we have to make sure the current selected search item stays in sync
|
||||
useEffect(() => setDesc(search?.data.description ?? ""), [search]);
|
||||
|
||||
@@ -499,6 +512,8 @@ function ObjectDetailsTab({
|
||||
placeholder="Description of the tracked object"
|
||||
value={desc}
|
||||
onChange={(e) => setDesc(e.target.value)}
|
||||
onFocus={handleDescriptionFocus}
|
||||
onBlur={handleDescriptionBlur}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -543,16 +558,16 @@ function ObjectDetailsTab({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{(config?.cameras[search.camera].genai.enabled && search.end_time) ||
|
||||
(!config?.cameras[search.camera].genai.enabled && (
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Save"
|
||||
onClick={updateDescription}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
))}
|
||||
{((config?.cameras[search.camera].genai.enabled && search.end_time) ||
|
||||
!config?.cameras[search.camera].genai.enabled) && (
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Save"
|
||||
onClick={updateDescription}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -444,6 +444,7 @@ export default function SearchView({
|
||||
setSimilarity={
|
||||
searchDetail && (() => setSimilaritySearch(searchDetail))
|
||||
}
|
||||
setInputFocused={setInputFocused}
|
||||
/>
|
||||
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user