mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-02 09:02:15 +03:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93d39370b6 | ||
|
|
cea210d800 | ||
|
|
7b65bcf13c | ||
|
|
335b7564d5 | ||
|
|
202e9ad9ce | ||
|
|
99d27c154e | ||
|
|
5943fc1895 | ||
|
|
9efc20e58a | ||
|
|
6d8234fa27 | ||
|
|
ad76c28a66 | ||
|
|
131d07e649 | ||
|
|
776bb79f0b | ||
|
|
aedfaa3641 |
@@ -33,9 +33,9 @@ runs:
|
|||||||
with:
|
with:
|
||||||
string: ${{ github.repository }}
|
string: ${{ github.repository }}
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc
|
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc
|
||||||
with:
|
with:
|
||||||
|
|||||||
+22
-15
@@ -19,7 +19,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64_build:
|
amd64_build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: AMD64 Build
|
name: AMD64 Build
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
tags: ${{ steps.setup.outputs.image-name }}-amd64
|
tags: ${{ steps.setup.outputs.image-name }}-amd64
|
||||||
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64
|
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64
|
||||||
arm64_build:
|
arm64_build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: ARM Build
|
name: ARM Build
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
@@ -66,8 +66,9 @@ jobs:
|
|||||||
${{ steps.setup.outputs.image-name }}-standard-arm64
|
${{ steps.setup.outputs.image-name }}-standard-arm64
|
||||||
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
||||||
- name: Build and push RPi build
|
- name: Build and push RPi build
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
|
source: .
|
||||||
push: true
|
push: true
|
||||||
targets: rpi
|
targets: rpi
|
||||||
files: docker/rpi/rpi.hcl
|
files: docker/rpi/rpi.hcl
|
||||||
@@ -76,7 +77,7 @@ jobs:
|
|||||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
||||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64,mode=max
|
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64,mode=max
|
||||||
jetson_jp4_build:
|
jetson_jp4_build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: Jetson Jetpack 4
|
name: Jetson Jetpack 4
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
@@ -94,8 +95,9 @@ jobs:
|
|||||||
BASE_IMAGE: timongentzsch/l4t-ubuntu20-opencv:latest
|
BASE_IMAGE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||||
SLIM_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
SLIM_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||||
TRT_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
TRT_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
|
source: .
|
||||||
push: true
|
push: true
|
||||||
targets: tensorrt
|
targets: tensorrt
|
||||||
files: docker/tensorrt/trt.hcl
|
files: docker/tensorrt/trt.hcl
|
||||||
@@ -104,7 +106,7 @@ jobs:
|
|||||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp4
|
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp4
|
||||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp4,mode=max
|
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp4,mode=max
|
||||||
jetson_jp5_build:
|
jetson_jp5_build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: Jetson Jetpack 5
|
name: Jetson Jetpack 5
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
@@ -122,8 +124,9 @@ jobs:
|
|||||||
BASE_IMAGE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
BASE_IMAGE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||||
SLIM_BASE: 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
|
TRT_BASE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
|
source: .
|
||||||
push: true
|
push: true
|
||||||
targets: tensorrt
|
targets: tensorrt
|
||||||
files: docker/tensorrt/trt.hcl
|
files: docker/tensorrt/trt.hcl
|
||||||
@@ -132,7 +135,7 @@ jobs:
|
|||||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp5
|
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp5
|
||||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp5,mode=max
|
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-jp5,mode=max
|
||||||
amd64_extra_builds:
|
amd64_extra_builds:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: AMD64 Extra Build
|
name: AMD64 Extra Build
|
||||||
needs:
|
needs:
|
||||||
- amd64_build
|
- amd64_build
|
||||||
@@ -149,8 +152,9 @@ jobs:
|
|||||||
- name: Build and push TensorRT (x86 GPU)
|
- name: Build and push TensorRT (x86 GPU)
|
||||||
env:
|
env:
|
||||||
COMPUTE_LEVEL: "50 60 70 80 90"
|
COMPUTE_LEVEL: "50 60 70 80 90"
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
|
source: .
|
||||||
push: true
|
push: true
|
||||||
targets: tensorrt
|
targets: tensorrt
|
||||||
files: docker/tensorrt/trt.hcl
|
files: docker/tensorrt/trt.hcl
|
||||||
@@ -159,7 +163,7 @@ jobs:
|
|||||||
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64
|
*.cache-from=type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64
|
||||||
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64,mode=max
|
*.cache-to=type=registry,ref=${{ steps.setup.outputs.cache-name }}-amd64,mode=max
|
||||||
arm64_extra_builds:
|
arm64_extra_builds:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: ARM Extra Build
|
name: ARM Extra Build
|
||||||
needs:
|
needs:
|
||||||
- arm64_build
|
- arm64_build
|
||||||
@@ -174,8 +178,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push Rockchip build
|
- name: Build and push Rockchip build
|
||||||
uses: docker/bake-action@v3
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
|
source: .
|
||||||
push: true
|
push: true
|
||||||
targets: rk
|
targets: rk
|
||||||
files: docker/rockchip/rk.hcl
|
files: docker/rockchip/rk.hcl
|
||||||
@@ -183,7 +188,7 @@ jobs:
|
|||||||
rk.tags=${{ steps.setup.outputs.image-name }}-rk
|
rk.tags=${{ steps.setup.outputs.image-name }}-rk
|
||||||
*.cache-from=type=gha
|
*.cache-from=type=gha
|
||||||
combined_extra_builds:
|
combined_extra_builds:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: Combined Extra Builds
|
name: Combined Extra Builds
|
||||||
needs:
|
needs:
|
||||||
- amd64_build
|
- amd64_build
|
||||||
@@ -199,8 +204,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push Hailo-8l build
|
- name: Build and push Hailo-8l build
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
|
source: .
|
||||||
push: true
|
push: true
|
||||||
targets: h8l
|
targets: h8l
|
||||||
files: docker/hailo8l/h8l.hcl
|
files: docker/hailo8l/h8l.hcl
|
||||||
@@ -212,8 +218,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
AMDGPU: gfx
|
AMDGPU: gfx
|
||||||
HSA_OVERRIDE: 0
|
HSA_OVERRIDE: 0
|
||||||
uses: docker/bake-action@v3
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
|
source: .
|
||||||
push: true
|
push: true
|
||||||
targets: rocm
|
targets: rocm
|
||||||
files: docker/rocm/rocm.hcl
|
files: docker/rocm/rocm.hcl
|
||||||
@@ -223,7 +230,7 @@ jobs:
|
|||||||
# The majority of users running arm64 are rpi users, so the rpi
|
# The majority of users running arm64 are rpi users, so the rpi
|
||||||
# build should be the primary arm64 image
|
# build should be the primary arm64 image
|
||||||
assemble_default_build:
|
assemble_default_build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: Assemble and push default build
|
name: Assemble and push default build
|
||||||
needs:
|
needs:
|
||||||
- amd64_build
|
- amd64_build
|
||||||
|
|||||||
@@ -116,6 +116,12 @@ genai:
|
|||||||
model: gpt-4o
|
model: gpt-4o
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::: note
|
||||||
|
|
||||||
|
To use a different OpenAI-compatible API endpoint, set the `OPENAI_BASE_URL` environment variable to your provider's API URL.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## Azure OpenAI
|
## Azure OpenAI
|
||||||
|
|
||||||
Microsoft offers several vision models through Azure OpenAI. A subscription is required.
|
Microsoft offers several vision models through Azure OpenAI. A subscription is required.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ title: Configuring go2rtc
|
|||||||
|
|
||||||
Use of the bundled go2rtc is optional. You can still configure FFmpeg to connect directly to your cameras. However, adding go2rtc to your configuration is required for the following features:
|
Use of the bundled go2rtc is optional. You can still configure FFmpeg to connect directly to your cameras. However, adding go2rtc to your configuration is required for the following features:
|
||||||
|
|
||||||
- WebRTC or MSE for live viewing with higher resolutions and frame rates than the jsmpeg stream which is limited to the detect stream
|
- WebRTC or MSE for live viewing with audio, higher resolutions and frame rates than the jsmpeg stream which is limited to the detect stream and does not support audio
|
||||||
- Live stream support for cameras in Home Assistant Integration
|
- Live stream support for cameras in Home Assistant Integration
|
||||||
- RTSP relay for use with other consumers to reduce the number of connections to your camera streams
|
- RTSP relay for use with other consumers to reduce the number of connections to your camera streams
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ You cannot use the `environment_vars` section of your Frigate configuration file
|
|||||||
|
|
||||||
## Submit examples
|
## Submit examples
|
||||||
|
|
||||||
Once your API key is configured, you can submit examples directly from the Explore page in Frigate using the `Frigate+` button.
|
Once your API key is configured, you can submit examples directly from the Explore page in Frigate. From the More Filters menu, select "Has a Snapshot - Yes" and "Submitted to Frigate+ - No", and press Apply at the bottom of the pane. Then, click on a thumbnail and select the Snapshot tab.
|
||||||
|
|
||||||
|
You can use your keyboard's left and right arrow keys to quickly navigate between the tracked object snapshots.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
||||||
@@ -37,8 +39,6 @@ Snapshots must be enabled to be able to submit examples to Frigate+
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Annotate and verify
|
### Annotate and verify
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ ffmpeg:
|
|||||||
record: preset-record-generic-audio-aac
|
record: preset-record-generic-audio-aac
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### How can I get sound in live view?
|
||||||
|
|
||||||
|
Audio is only supported for live view when go2rtc is configured, see [the live docs](../configuration/live.md) for more information.
|
||||||
|
|
||||||
### I can't view recordings in the Web UI.
|
### I can't view recordings in the Web UI.
|
||||||
|
|
||||||
Ensure your cameras send h264 encoded video, or [transcode them](/configuration/restream.md).
|
Ensure your cameras send h264 encoded video, or [transcode them](/configuration/restream.md).
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ def config(request: Request):
|
|||||||
|
|
||||||
config["plus"] = {"enabled": request.app.frigate_config.plus_api.is_active()}
|
config["plus"] = {"enabled": request.app.frigate_config.plus_api.is_active()}
|
||||||
config["model"]["colormap"] = config_obj.model.colormap
|
config["model"]["colormap"] = config_obj.model.colormap
|
||||||
|
config["model"]["all_attributes"] = config_obj.model.all_attributes
|
||||||
|
|
||||||
# use merged labelamp
|
# use merged labelamp
|
||||||
for detector_config in config["detectors"].values():
|
for detector_config in config["detectors"].values():
|
||||||
|
|||||||
@@ -26,14 +26,13 @@ from frigate.storage import StorageMaintainer
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def check_csrf(request: Request):
|
def check_csrf(request: Request) -> bool:
|
||||||
if request.method in ["GET", "HEAD", "OPTIONS", "TRACE"]:
|
if request.method in ["GET", "HEAD", "OPTIONS", "TRACE"]:
|
||||||
pass
|
return True
|
||||||
if "origin" in request.headers and "x-csrf-token" not in request.headers:
|
if "origin" in request.headers and "x-csrf-token" not in request.headers:
|
||||||
return JSONResponse(
|
return False
|
||||||
content={"success": False, "message": "Missing CSRF header"},
|
|
||||||
status_code=401,
|
return True
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Used to retrieve the remote-user header: https://starlette-context.readthedocs.io/en/latest/plugins.html#easy-mode
|
# Used to retrieve the remote-user header: https://starlette-context.readthedocs.io/en/latest/plugins.html#easy-mode
|
||||||
@@ -71,7 +70,12 @@ def create_fastapi_app(
|
|||||||
@app.middleware("http")
|
@app.middleware("http")
|
||||||
async def frigate_middleware(request: Request, call_next):
|
async def frigate_middleware(request: Request, call_next):
|
||||||
# Before request
|
# Before request
|
||||||
check_csrf(request)
|
if not check_csrf(request):
|
||||||
|
return JSONResponse(
|
||||||
|
content={"success": False, "message": "Missing CSRF header"},
|
||||||
|
status_code=401,
|
||||||
|
)
|
||||||
|
|
||||||
if database.is_closed():
|
if database.is_closed():
|
||||||
database.connect()
|
database.connect()
|
||||||
|
|
||||||
|
|||||||
@@ -111,12 +111,12 @@ PRESETS_HW_ACCEL_ENCODE_BIRDSEYE = {
|
|||||||
"preset-rpi-64-h265": "{0} -hide_banner {1} -c:v hevc_v4l2m2m {2}",
|
"preset-rpi-64-h265": "{0} -hide_banner {1} -c:v hevc_v4l2m2m {2}",
|
||||||
FFMPEG_HWACCEL_VAAPI: "{0} -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device {3} {1} -c:v h264_vaapi -g 50 -bf 0 -profile:v high -level:v 4.1 -sei:v 0 -an -vf format=vaapi|nv12,hwupload {2}",
|
FFMPEG_HWACCEL_VAAPI: "{0} -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device {3} {1} -c:v h264_vaapi -g 50 -bf 0 -profile:v high -level:v 4.1 -sei:v 0 -an -vf format=vaapi|nv12,hwupload {2}",
|
||||||
"preset-intel-qsv-h264": "{0} -hide_banner {1} -c:v h264_qsv -g 50 -bf 0 -profile:v high -level:v 4.1 -async_depth:v 1 {2}",
|
"preset-intel-qsv-h264": "{0} -hide_banner {1} -c:v h264_qsv -g 50 -bf 0 -profile:v high -level:v 4.1 -async_depth:v 1 {2}",
|
||||||
"preset-intel-qsv-h265": "{0} -hide_banner {1} -c:v h264_qsv -g 50 -bf 0 -profile:v high -level:v 4.1 -async_depth:v 1 {2}",
|
"preset-intel-qsv-h265": "{0} -hide_banner {1} -c:v h264_qsv -g 50 -bf 0 -profile:v main -level:v 4.1 -async_depth:v 1 {2}",
|
||||||
FFMPEG_HWACCEL_NVIDIA: "{0} -hide_banner {1} -c:v h264_nvenc -g 50 -profile:v high -level:v auto -preset:v p2 -tune:v ll {2}",
|
FFMPEG_HWACCEL_NVIDIA: "{0} -hide_banner {1} -c:v h264_nvenc -g 50 -profile:v high -level:v auto -preset:v p2 -tune:v ll {2}",
|
||||||
"preset-jetson-h264": "{0} -hide_banner {1} -c:v h264_nvmpi -profile high {2}",
|
"preset-jetson-h264": "{0} -hide_banner {1} -c:v h264_nvmpi -profile high {2}",
|
||||||
"preset-jetson-h265": "{0} -hide_banner {1} -c:v h264_nvmpi -profile high {2}",
|
"preset-jetson-h265": "{0} -hide_banner {1} -c:v h264_nvmpi -profile main {2}",
|
||||||
"preset-rk-h264": "{0} -hide_banner {1} -c:v h264_rkmpp -profile:v high {2}",
|
"preset-rk-h264": "{0} -hide_banner {1} -c:v h264_rkmpp -profile:v high {2}",
|
||||||
"preset-rk-h265": "{0} -hide_banner {1} -c:v hevc_rkmpp -profile:v high {2}",
|
"preset-rk-h265": "{0} -hide_banner {1} -c:v hevc_rkmpp -profile:v main {2}",
|
||||||
"default": "{0} -hide_banner {1} -c:v libx264 -g 50 -profile:v high -level:v 4.1 -preset:v superfast -tune:v zerolatency {2}",
|
"default": "{0} -hide_banner {1} -c:v libx264 -g 50 -profile:v high -level:v 4.1 -preset:v superfast -tune:v zerolatency {2}",
|
||||||
}
|
}
|
||||||
PRESETS_HW_ACCEL_ENCODE_BIRDSEYE["preset-nvidia-h264"] = (
|
PRESETS_HW_ACCEL_ENCODE_BIRDSEYE["preset-nvidia-h264"] = (
|
||||||
@@ -131,13 +131,13 @@ PRESETS_HW_ACCEL_ENCODE_TIMELAPSE = {
|
|||||||
"preset-rpi-64-h265": "{0} -hide_banner {1} -c:v hevc_v4l2m2m -pix_fmt yuv420p {2}",
|
"preset-rpi-64-h265": "{0} -hide_banner {1} -c:v hevc_v4l2m2m -pix_fmt yuv420p {2}",
|
||||||
FFMPEG_HWACCEL_VAAPI: "{0} -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device {3} {1} -c:v h264_vaapi {2}",
|
FFMPEG_HWACCEL_VAAPI: "{0} -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device {3} {1} -c:v h264_vaapi {2}",
|
||||||
"preset-intel-qsv-h264": "{0} -hide_banner {1} -c:v h264_qsv -profile:v high -level:v 4.1 -async_depth:v 1 {2}",
|
"preset-intel-qsv-h264": "{0} -hide_banner {1} -c:v h264_qsv -profile:v high -level:v 4.1 -async_depth:v 1 {2}",
|
||||||
"preset-intel-qsv-h265": "{0} -hide_banner {1} -c:v hevc_qsv -profile:v high -level:v 4.1 -async_depth:v 1 {2}",
|
"preset-intel-qsv-h265": "{0} -hide_banner {1} -c:v hevc_qsv -profile:v main -level:v 4.1 -async_depth:v 1 {2}",
|
||||||
FFMPEG_HWACCEL_NVIDIA: "{0} -hide_banner -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 8 {1} -c:v h264_nvenc {2}",
|
FFMPEG_HWACCEL_NVIDIA: "{0} -hide_banner -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 8 {1} -c:v h264_nvenc {2}",
|
||||||
"preset-nvidia-h265": "{0} -hide_banner -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 8 {1} -c:v hevc_nvenc {2}",
|
"preset-nvidia-h265": "{0} -hide_banner -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 8 {1} -c:v hevc_nvenc {2}",
|
||||||
"preset-jetson-h264": "{0} -hide_banner {1} -c:v h264_nvmpi -profile high {2}",
|
"preset-jetson-h264": "{0} -hide_banner {1} -c:v h264_nvmpi -profile high {2}",
|
||||||
"preset-jetson-h265": "{0} -hide_banner {1} -c:v hevc_nvmpi -profile high {2}",
|
"preset-jetson-h265": "{0} -hide_banner {1} -c:v hevc_nvmpi -profile main {2}",
|
||||||
"preset-rk-h264": "{0} -hide_banner {1} -c:v h264_rkmpp -profile:v high {2}",
|
"preset-rk-h264": "{0} -hide_banner {1} -c:v h264_rkmpp -profile:v high {2}",
|
||||||
"preset-rk-h265": "{0} -hide_banner {1} -c:v hevc_rkmpp -profile:v high {2}",
|
"preset-rk-h265": "{0} -hide_banner {1} -c:v hevc_rkmpp -profile:v main {2}",
|
||||||
"default": "{0} -hide_banner {1} -c:v libx264 -preset:v ultrafast -tune:v zerolatency {2}",
|
"default": "{0} -hide_banner {1} -c:v libx264 -preset:v ultrafast -tune:v zerolatency {2}",
|
||||||
}
|
}
|
||||||
PRESETS_HW_ACCEL_ENCODE_TIMELAPSE["preset-nvidia-h264"] = (
|
PRESETS_HW_ACCEL_ENCODE_TIMELAPSE["preset-nvidia-h264"] = (
|
||||||
|
|||||||
@@ -411,19 +411,19 @@ class OnvifController:
|
|||||||
# The onvif spec says this can report as +INF and -INF, so this may need to be modified
|
# The onvif spec says this can report as +INF and -INF, so this may need to be modified
|
||||||
pan = numpy.interp(
|
pan = numpy.interp(
|
||||||
pan,
|
pan,
|
||||||
|
[-1, 1],
|
||||||
[
|
[
|
||||||
self.cams[camera_name]["relative_fov_range"]["XRange"]["Min"],
|
self.cams[camera_name]["relative_fov_range"]["XRange"]["Min"],
|
||||||
self.cams[camera_name]["relative_fov_range"]["XRange"]["Max"],
|
self.cams[camera_name]["relative_fov_range"]["XRange"]["Max"],
|
||||||
],
|
],
|
||||||
[-1, 1],
|
|
||||||
)
|
)
|
||||||
tilt = numpy.interp(
|
tilt = numpy.interp(
|
||||||
tilt,
|
tilt,
|
||||||
|
[-1, 1],
|
||||||
[
|
[
|
||||||
self.cams[camera_name]["relative_fov_range"]["YRange"]["Min"],
|
self.cams[camera_name]["relative_fov_range"]["YRange"]["Min"],
|
||||||
self.cams[camera_name]["relative_fov_range"]["YRange"]["Max"],
|
self.cams[camera_name]["relative_fov_range"]["YRange"]["Max"],
|
||||||
],
|
],
|
||||||
[-1, 1],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
move_request.Speed = {
|
move_request.Speed = {
|
||||||
@@ -536,11 +536,11 @@ class OnvifController:
|
|||||||
# function takes in 0 to 1 for zoom, interpolate to the values of the camera.
|
# function takes in 0 to 1 for zoom, interpolate to the values of the camera.
|
||||||
zoom = numpy.interp(
|
zoom = numpy.interp(
|
||||||
zoom,
|
zoom,
|
||||||
|
[0, 1],
|
||||||
[
|
[
|
||||||
self.cams[camera_name]["absolute_zoom_range"]["XRange"]["Min"],
|
self.cams[camera_name]["absolute_zoom_range"]["XRange"]["Min"],
|
||||||
self.cams[camera_name]["absolute_zoom_range"]["XRange"]["Max"],
|
self.cams[camera_name]["absolute_zoom_range"]["XRange"]["Max"],
|
||||||
],
|
],
|
||||||
[0, 1],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
move_request.Speed = {"Zoom": speed}
|
move_request.Speed = {"Zoom": speed}
|
||||||
|
|||||||
@@ -121,22 +121,29 @@ class RecordingCleanup(threading.Thread):
|
|||||||
review_start = 0
|
review_start = 0
|
||||||
deleted_recordings = set()
|
deleted_recordings = set()
|
||||||
kept_recordings: list[tuple[float, float]] = []
|
kept_recordings: list[tuple[float, float]] = []
|
||||||
|
recording: Recordings
|
||||||
for recording in recordings:
|
for recording in recordings:
|
||||||
keep = False
|
keep = False
|
||||||
mode = None
|
mode = None
|
||||||
# Now look for a reason to keep this recording segment
|
# Now look for a reason to keep this recording segment
|
||||||
for idx in range(review_start, len(reviews)):
|
for idx in range(review_start, len(reviews)):
|
||||||
review: ReviewSegment = reviews[idx]
|
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
|
# if the review starts in the future, stop checking reviews
|
||||||
# and let this recording segment expire
|
# and let this recording segment expire
|
||||||
if review.start_time > recording.end_time:
|
if review.start_time - pre_capture > recording.end_time:
|
||||||
keep = False
|
keep = False
|
||||||
break
|
break
|
||||||
|
|
||||||
# if the review is in progress or ends after the recording starts, keep it
|
# if the review is in progress or ends after the recording starts, keep it
|
||||||
# and stop looking at reviews
|
# 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
|
keep = True
|
||||||
mode = (
|
mode = (
|
||||||
config.record.alerts.retain.mode
|
config.record.alerts.retain.mode
|
||||||
@@ -149,7 +156,7 @@ class RecordingCleanup(threading.Thread):
|
|||||||
# this review and check the next review for an overlap.
|
# this review and check the next review for an overlap.
|
||||||
# since the review and recordings are sorted, we can skip review
|
# since the review and recordings are sorted, we can skip review
|
||||||
# that end before the previous recording segment started on future segments
|
# 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
|
review_start = idx
|
||||||
|
|
||||||
# Delete recordings outside of the retention window or based on the retention mode
|
# Delete recordings outside of the retention window or based on the retention mode
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useApiHost } from "@/api";
|
import { useApiHost } from "@/api";
|
||||||
import { getIconForLabel } from "@/utils/iconUtil";
|
import { getIconForLabel } from "@/utils/iconUtil";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@@ -33,6 +33,16 @@ export default function SearchThumbnail({
|
|||||||
onClick(searchResult, true, false);
|
onClick(searchResult, true, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleOnClick = useCallback(
|
||||||
|
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (e.metaKey) {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClick(searchResult, true, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[searchResult, onClick],
|
||||||
|
);
|
||||||
|
|
||||||
const objectLabel = useMemo(() => {
|
const objectLabel = useMemo(() => {
|
||||||
if (
|
if (
|
||||||
!config ||
|
!config ||
|
||||||
@@ -57,6 +67,7 @@ export default function SearchThumbnail({
|
|||||||
<div className={`size-full ${imgLoaded ? "visible" : "invisible"}`}>
|
<div className={`size-full ${imgLoaded ? "visible" : "invisible"}`}>
|
||||||
<img
|
<img
|
||||||
ref={imgRef}
|
ref={imgRef}
|
||||||
|
onClick={handleOnClick}
|
||||||
className={cn(
|
className={cn(
|
||||||
"size-full select-none object-cover object-center opacity-100 transition-opacity",
|
"size-full select-none object-cover object-center opacity-100 transition-opacity",
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ export default function SearchFilterGroup({
|
|||||||
}
|
}
|
||||||
const cameraConfig = config.cameras[camera];
|
const cameraConfig = config.cameras[camera];
|
||||||
cameraConfig.objects.track.forEach((label) => {
|
cameraConfig.objects.track.forEach((label) => {
|
||||||
labels.add(label);
|
if (!config.model.all_attributes.includes(label)) {
|
||||||
|
labels.add(label);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cameraConfig.audio.enabled_in_config) {
|
if (cameraConfig.audio.enabled_in_config) {
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ type SearchDetailDialogProps = {
|
|||||||
setSearch: (search: SearchResult | undefined) => void;
|
setSearch: (search: SearchResult | undefined) => void;
|
||||||
setSearchPage: (page: SearchTab) => void;
|
setSearchPage: (page: SearchTab) => void;
|
||||||
setSimilarity?: () => void;
|
setSimilarity?: () => void;
|
||||||
|
setInputFocused: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
export default function SearchDetailDialog({
|
export default function SearchDetailDialog({
|
||||||
search,
|
search,
|
||||||
@@ -92,6 +93,7 @@ export default function SearchDetailDialog({
|
|||||||
setSearch,
|
setSearch,
|
||||||
setSearchPage,
|
setSearchPage,
|
||||||
setSimilarity,
|
setSimilarity,
|
||||||
|
setInputFocused,
|
||||||
}: SearchDetailDialogProps) {
|
}: SearchDetailDialogProps) {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config", {
|
const { data: config } = useSWR<FrigateConfig>("config", {
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
@@ -232,6 +234,7 @@ export default function SearchDetailDialog({
|
|||||||
config={config}
|
config={config}
|
||||||
setSearch={setSearch}
|
setSearch={setSearch}
|
||||||
setSimilarity={setSimilarity}
|
setSimilarity={setSimilarity}
|
||||||
|
setInputFocused={setInputFocused}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{page == "snapshot" && (
|
{page == "snapshot" && (
|
||||||
@@ -266,12 +269,14 @@ type ObjectDetailsTabProps = {
|
|||||||
config?: FrigateConfig;
|
config?: FrigateConfig;
|
||||||
setSearch: (search: SearchResult | undefined) => void;
|
setSearch: (search: SearchResult | undefined) => void;
|
||||||
setSimilarity?: () => void;
|
setSimilarity?: () => void;
|
||||||
|
setInputFocused: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
function ObjectDetailsTab({
|
function ObjectDetailsTab({
|
||||||
search,
|
search,
|
||||||
config,
|
config,
|
||||||
setSearch,
|
setSearch,
|
||||||
setSimilarity,
|
setSimilarity,
|
||||||
|
setInputFocused,
|
||||||
}: ObjectDetailsTabProps) {
|
}: ObjectDetailsTabProps) {
|
||||||
const apiHost = useApiHost();
|
const apiHost = useApiHost();
|
||||||
|
|
||||||
@@ -283,6 +288,14 @@ function ObjectDetailsTab({
|
|||||||
|
|
||||||
const [desc, setDesc] = useState(search?.data.description);
|
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
|
// we have to make sure the current selected search item stays in sync
|
||||||
useEffect(() => setDesc(search?.data.description ?? ""), [search]);
|
useEffect(() => setDesc(search?.data.description ?? ""), [search]);
|
||||||
|
|
||||||
@@ -499,6 +512,8 @@ function ObjectDetailsTab({
|
|||||||
placeholder="Description of the tracked object"
|
placeholder="Description of the tracked object"
|
||||||
value={desc}
|
value={desc}
|
||||||
onChange={(e) => setDesc(e.target.value)}
|
onChange={(e) => setDesc(e.target.value)}
|
||||||
|
onFocus={handleDescriptionFocus}
|
||||||
|
onBlur={handleDescriptionBlur}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ export interface FrigateConfig {
|
|||||||
width: number;
|
width: number;
|
||||||
colormap: { [key: string]: [number, number, number] };
|
colormap: { [key: string]: [number, number, number] };
|
||||||
attributes_map: { [key: string]: [string] };
|
attributes_map: { [key: string]: [string] };
|
||||||
|
all_attributes: [string];
|
||||||
};
|
};
|
||||||
|
|
||||||
motion: Record<string, unknown> | null;
|
motion: Record<string, unknown> | null;
|
||||||
|
|||||||
@@ -444,6 +444,7 @@ export default function SearchView({
|
|||||||
setSimilarity={
|
setSimilarity={
|
||||||
searchDetail && (() => setSimilaritySearch(searchDetail))
|
searchDetail && (() => setSimilaritySearch(searchDetail))
|
||||||
}
|
}
|
||||||
|
setInputFocused={setInputFocused}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user