Compare commits

...
Author SHA1 Message Date
93d39370b6 update docs to be more clear regarding audio support and go2rtc requi… (#16232)
* update docs to be more clear regarding audio support and go2rtc requirement

Signed-off-by: Ben Clouser <dev@benclouser.com>

* Update docs/docs/troubleshooting/faqs.md

* Update docs/docs/troubleshooting/faqs.md

* Update docs/docs/troubleshooting/faqs.md

* Clarify title

* Cleanup

---------

Signed-off-by: Ben Clouser <dev@benclouser.com>
Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2025-01-30 11:23:38 -07:00
Nicolas MowenandGitHub cea210d800 Fix csrf (#16230)
* Fix csrf check

* Simplify
2025-01-30 11:27:38 -06:00
Josh HawkinsandGitHub 7b65bcf13c Fix interpolation for autotracking cameras (#16211) 2025-01-29 06:44:13 -07:00
Josh HawkinsandGitHub 335b7564d5 Update plus submission docs and remove 0.14 UI image (#16199) 2025-01-28 11:39:12 -06:00
Josh HawkinsandGitHub 202e9ad9ce Document OPENAI_BASE_URL env var (#16195) 2025-01-28 08:58:15 -07:00
Nicolas MowenandGitHub 99d27c154e Don't show sub labels in main label filter list (#16168) 2025-01-27 08:07:49 -06:00
Nicolas MowenandGitHub 5943fc1895 Fix h265 encoding presets (#16158) 2025-01-26 17:14:02 -07:00
Josh HawkinsandGitHub 9efc20e58a Fix selection of tracked objects in Explore on desktop Safari (#16153)
* ensure meta click works on desktop safari to select objects in explore

* don't break mobile
2025-01-26 10:57:38 -07:00
Nicolas MowenandGitHub 6d8234fa27 Fix build (#16119)
* Update to bake v6

* Update setup actions

* Temp

* Use ubuntu 22.04 for build

* Remove temp
2025-01-24 09:45:46 -06:00
Nicolas MowenandGitHub ad76c28a66 Downgrade to bake-action v5 (#16098) 2025-01-23 09:18:15 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
131d07e649 Bump docker/bake-action from 3 to 6 (#15892)
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 3 to 6.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/v3...v6)

---
updated-dependencies:
- dependency-name: docker/bake-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 07:58:42 -07:00
Nicolas MowenandGitHub 776bb79f0b Consider pre and post capture when cleaning up recordings based on review segments (#16096) 2025-01-23 08:26:53 -06:00
Josh HawkinsandGitHub aedfaa3641 Don't prevent default when tracked object details description input is focused (#16064) 2025-01-20 06:23:22 -07:00
16 changed files with 101 additions and 42 deletions
+2 -2
View File
@@ -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
View File
@@ -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
+6
View File
@@ -116,6 +116,12 @@ genai:
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
Microsoft offers several vision models through Azure OpenAI. A subscription is required.
+1 -1
View File
@@ -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:
- 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
- RTSP relay for use with other consumers to reduce the number of connections to your camera streams
+3 -3
View File
@@ -29,7 +29,9 @@ You cannot use the `environment_vars` section of your Frigate configuration file
## 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
@@ -37,8 +39,6 @@ Snapshots must be enabled to be able to submit examples to Frigate+
:::
![Send To Plus](/img/plus/send-to-plus.jpg)
![Submit To Plus](/img/plus/submit-to-plus.jpg)
### Annotate and verify
+4
View File
@@ -17,6 +17,10 @@ ffmpeg:
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.
Ensure your cameras send h264 encoded video, or [transcode them](/configuration/restream.md).
+1
View File
@@ -153,6 +153,7 @@ def config(request: Request):
config["plus"] = {"enabled": request.app.frigate_config.plus_api.is_active()}
config["model"]["colormap"] = config_obj.model.colormap
config["model"]["all_attributes"] = config_obj.model.all_attributes
# use merged labelamp
for detector_config in config["detectors"].values():
+11 -7
View File
@@ -26,14 +26,13 @@ from frigate.storage import StorageMaintainer
logger = logging.getLogger(__name__)
def check_csrf(request: Request):
def check_csrf(request: Request) -> bool:
if request.method in ["GET", "HEAD", "OPTIONS", "TRACE"]:
pass
return True
if "origin" in request.headers and "x-csrf-token" not in request.headers:
return JSONResponse(
content={"success": False, "message": "Missing CSRF header"},
status_code=401,
)
return False
return True
# 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")
async def frigate_middleware(request: Request, call_next):
# 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():
database.connect()
+6 -6
View File
@@ -111,12 +111,12 @@ PRESETS_HW_ACCEL_ENCODE_BIRDSEYE = {
"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}",
"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}",
"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-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}",
}
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}",
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-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}",
"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-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-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}",
}
PRESETS_HW_ACCEL_ENCODE_TIMELAPSE["preset-nvidia-h264"] = (
+3 -3
View File
@@ -411,19 +411,19 @@ class OnvifController:
# The onvif spec says this can report as +INF and -INF, so this may need to be modified
pan = numpy.interp(
pan,
[-1, 1],
[
self.cams[camera_name]["relative_fov_range"]["XRange"]["Min"],
self.cams[camera_name]["relative_fov_range"]["XRange"]["Max"],
],
[-1, 1],
)
tilt = numpy.interp(
tilt,
[-1, 1],
[
self.cams[camera_name]["relative_fov_range"]["YRange"]["Min"],
self.cams[camera_name]["relative_fov_range"]["YRange"]["Max"],
],
[-1, 1],
)
move_request.Speed = {
@@ -536,11 +536,11 @@ class OnvifController:
# function takes in 0 to 1 for zoom, interpolate to the values of the camera.
zoom = numpy.interp(
zoom,
[0, 1],
[
self.cams[camera_name]["absolute_zoom_range"]["XRange"]["Min"],
self.cams[camera_name]["absolute_zoom_range"]["XRange"]["Max"],
],
[0, 1],
)
move_request.Speed = {"Zoom": speed}
+10 -3
View File
@@ -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
+12 -1
View File
@@ -1,4 +1,4 @@
import { useMemo } from "react";
import { useCallback, useMemo } from "react";
import { useApiHost } from "@/api";
import { getIconForLabel } from "@/utils/iconUtil";
import useSWR from "swr";
@@ -33,6 +33,16 @@ export default function SearchThumbnail({
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(() => {
if (
!config ||
@@ -57,6 +67,7 @@ export default function SearchThumbnail({
<div className={`size-full ${imgLoaded ? "visible" : "invisible"}`}>
<img
ref={imgRef}
onClick={handleOnClick}
className={cn(
"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];
cameraConfig.objects.track.forEach((label) => {
labels.add(label);
if (!config.model.all_attributes.includes(label)) {
labels.add(label);
}
});
if (cameraConfig.audio.enabled_in_config) {
@@ -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}
/>
</>
)}
+1
View File
@@ -343,6 +343,7 @@ export interface FrigateConfig {
width: number;
colormap: { [key: string]: [number, number, number] };
attributes_map: { [key: string]: [string] };
all_attributes: [string];
};
motion: Record<string, unknown> | null;
+1
View File
@@ -444,6 +444,7 @@ export default function SearchView({
setSimilarity={
searchDetail && (() => setSimilaritySearch(searchDetail))
}
setInputFocused={setInputFocused}
/>
<div