From cd64b3bccbcf19cd50660ff3473f35b413dd5b75 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Fri, 5 Apr 2024 15:45:13 -0500 Subject: [PATCH] app/frigate: Add schema, templates for Frigate [Frigate] is an open source network video recording software with advanced motion detection using machine learning object detection. It uses `ffmpeg` to stream video from one or more RTSP-capable IP video cameras and passes the images through an object detection process. To improve the performance of the machine learning model, it supports using a Coral EdgeTPU device, which requires special drivers: `gasket` and `apex`. Frigate is configured via a (rather compex) YAML document, some of the schema of which is modeled in `schema.cue` (the parts I need, anyway). [Frigate]: https://frigate.video/ --- app/frigate/schema/schema.cue | 132 +++++++++++++++++++ app/frigate/templates.cue | 68 ++++++++++ templates/frigate/65-apex.rules | 5 + templates/frigate/config.yml | 1 + templates/frigate/frigate-watchdog.container | 18 +++ templates/frigate/frigate.container | 34 +++++ templates/frigate/frigate.env | 9 ++ templates/frigate/frigate.sysusers | 2 + templates/frigate/gasket-driver.container | 17 +++ 9 files changed, 286 insertions(+) create mode 100644 app/frigate/schema/schema.cue create mode 100644 app/frigate/templates.cue create mode 100644 templates/frigate/65-apex.rules create mode 100644 templates/frigate/config.yml create mode 100644 templates/frigate/frigate-watchdog.container create mode 100644 templates/frigate/frigate.container create mode 100644 templates/frigate/frigate.env create mode 100644 templates/frigate/frigate.sysusers create mode 100644 templates/frigate/gasket-driver.container diff --git a/app/frigate/schema/schema.cue b/app/frigate/schema/schema.cue new file mode 100644 index 0000000..c997b31 --- /dev/null +++ b/app/frigate/schema/schema.cue @@ -0,0 +1,132 @@ +package schema + +#Frigate: { + env?: [string]: string + + config: #FrigateConfig +} + +#FrigateConfig: { + ffmpeg?: #FfmpegConfig + cameras: [string]: #CameraConfig + go2rtc?: #Go2RtcConfig + detectors?: [string]: #DetectorConfig + birdseye?: #BirdseyeConfig + mqtt: #MqttConfig +} + +#FfmpegConfig: { + hwaccel_args?: string +} + +#CameraConfig: { + detect?: { + height: int + width: int + } + ffmpeg: { + inputs: [...#FfmpegInput] + } + objects?: { + track: [...string] + filters?: [string]: #ObjectFilter + } + record?: #RecordConfig + rtmp?: { + enabled?: bool + } + snapshots?: #SnapshotsConfig + zones?: [string]: #ZoneConfig + motion?: { + mask: [...string] + } +} + +#FfmpegInput: { + path: string + input_args?: string + roles: [...string] +} + +#ObjectFilter: { + threshold?: float +} + +#RecordConfig: { + enabled?: bool + retain?: { + days: int + mode?: #RetainMode + } + events: { + pre_capture?: int + post_capture?: int + objects?: [...string] + required_zones?: [...string] + retain?: { + default?: int + mode?: #RetainMode + objects?: [string]: int + } + } +} + +#SnapshotsConfig: { + enabled?: bool + required_zones?: [...string] + retain?: { + default?: int + objects?: [string]: int + } +} + +#RetainMode: "all" | "motion" | "active_objects" + +#ZoneConfig: { + coordinates: string + inertia?: int + objects?: [...string] + filters?: [string]: { + min_area?: int + maax_area?: int + threshold?: float + } +} + +#Go2RtcConfig: { + streams: [string]: [...string] +} + +#DetectorConfig: #CpuDetector | #EdgeTpuDetector + +#CpuDetector: { + type: "cpu" + num_threads?: int +} + +#EdgeTpuDetector: { + type: "edgetpu" + device: string +} + +#BirdseyeConfig: { + enabled?: bool + restream?: bool + width?: int + height?: int + quality?: int + mode?: "objects" | "motion" | "continuous" + inactivity_threshold?: int + layout?: { + scaling_factor?: float + max_cameras?: int + } +} + +#MqttConfig: { + host: string + password?: string + port?: int + tls_ca_certs?: string + user?: string +} diff --git a/app/frigate/templates.cue b/app/frigate/templates.cue new file mode 100644 index 0000000..5f6385c --- /dev/null +++ b/app/frigate/templates.cue @@ -0,0 +1,68 @@ +package frigate + +import "du5t1n.me/cfg/base/schema/instructions" + +templates: [...instructions.#RenderInstruction] & [ + { + template: "frigate/frigate.sysusers" + dest: "/etc/sysusers.d/frigate.conf" + hooks: changed: [ + { + run: "systemd-sysusers /etc/sysusers.d/frigate.conf" + immediate: true + }, + ] + }, + { + template: "frigate/frigate.env" + dest: "/etc/frigate/environ" + mode: "u=rw,go=" + hooks: { + changed: [{run: "systemctl restart frigate"}] + } + }, + { + template: "frigate/config.yml" + dest: "/etc/frigate/config.yml" + hooks: { + changed: [{run: "systemctl restart frigate"}] + } + }, + { + template: "frigate/65-apex.rules" + dest: "/etc/udev/rules.d/65-apex.rules" + hooks: { + changed: [{run: "udevadm control --reload", immediate: true}] + } + }, + { + template: "frigate/gasket-driver.container" + dest: "/etc/containers/systemd/gasket-driver.container" + hooks: { + changed: [ + {run: "systemctl daemon-reload", immediate: true}, + {run: "systemctl restart gasket-driver"}, + ] + } + }, + { + template: "frigate/frigate.container" + dest: "/etc/containers/systemd/frigate.container" + hooks: { + changed: [ + {run: "systemctl daemon-reload", immediate: true}, + {run: "systemctl restart frigate"}, + ] + } + }, + { + template: "frigate/frigate-watchdog.container" + dest: "/etc/containers/systemd/frigate-watchdog.container" + hooks: { + changed: [ + {run: "systemctl daemon-reload", immediate: true}, + {run: "systemctl restart frigate-watchdog"}, + ] + } + }, +] diff --git a/templates/frigate/65-apex.rules b/templates/frigate/65-apex.rules new file mode 100644 index 0000000..effbe77 --- /dev/null +++ b/templates/frigate/65-apex.rules @@ -0,0 +1,5 @@ +# Add systemd tag to apex devices so systemd will create .device +# units for them. This allows Frigate to wait for the device to be +# available before starting, by adding an After= dependency on the +# .device unit. +SUBSYSTEM=="apex", MODE="0666", TAG+="systemd" diff --git a/templates/frigate/config.yml b/templates/frigate/config.yml new file mode 100644 index 0000000..390b542 --- /dev/null +++ b/templates/frigate/config.yml @@ -0,0 +1 @@ +{{ frigate.yaml -}} diff --git a/templates/frigate/frigate-watchdog.container b/templates/frigate/frigate-watchdog.container new file mode 100644 index 0000000..819ab54 --- /dev/null +++ b/templates/frigate/frigate-watchdog.container @@ -0,0 +1,18 @@ +[Unit] +Description=Frigate watchdog +After=frigate.service + +[Container] +Image=git.pyrocufflink.blue/containerimages/frigate-watchdog +Pull=newer +Exec=-u frigate +Volume=/run/log:/run/log:ro +Volume=/var/log:/var/log:ro +Volume=/etc/machine-id:/etc/machine-id:ro +ReadOnly=true +VolatileTmp=true +PodmanArgs=--privileged --uts=host --cgroupns=host --ipc=host --pid=host +Network=host + +[Install] +WantedBy=frigate.service diff --git a/templates/frigate/frigate.container b/templates/frigate/frigate.container new file mode 100644 index 0000000..b252119 --- /dev/null +++ b/templates/frigate/frigate.container @@ -0,0 +1,34 @@ +[Unit] +Description=Frigate NVR +Wants=network-online.target +After=network-online.target +Requires=dev-apex_0.device +After=dev-apex_0.device +RequiresMountsFor=/var/lib/frigate + +[Container] +Image=ghcr.io/blakeblackshear/frigate:0.12.1 +PodmanArgs=--uidmap 0:209:1 +PodmanArgs=--gidmap 0:209:1 +PodmanArgs=--uidmap 1:6000001:65536 +PodmanArgs=--gidmap 1:6000001:65536 +PodmanArgs=--shm-size 256m +EnvironmentFile=/etc/frigate/environ +Volume=/var/lib/frigate/media:/media/frigate:rw,z,U +Volume=/var/lib/frigate/tmp:/tmp:rw,z,U +Volume=/etc/frigate/config.yml:/config/config.yml:ro +AddDevice=/dev/apex_0 +AddDevice=/dev/dri/renderD128 +AddCapability=CAP_PERFMON +Network=host +Annotation=org.systemd.property.KillMode='none' + +[Service] +UMask=0077 +Restart=always +TimeoutStopSec=infinity +StateDirectory=%N/tmp +StateDirectory=%N/media + +[Install] +WantedBy=multi-user.target diff --git a/templates/frigate/frigate.env b/templates/frigate/frigate.env new file mode 100644 index 0000000..02e2388 --- /dev/null +++ b/templates/frigate/frigate.env @@ -0,0 +1,9 @@ +{% if frigate.env is defined -%} +{% for k, v in frigate.env -%} +{% if v is starting_with("-----BEGIN AGE") -%} +{{ k }}={{ v | decrypt }} +{% else %} +{{ k }}={{ v }} +{% endif -%} +{% endfor -%} +{% endif -%} diff --git a/templates/frigate/frigate.sysusers b/templates/frigate/frigate.sysusers new file mode 100644 index 0000000..35c365e --- /dev/null +++ b/templates/frigate/frigate.sysusers @@ -0,0 +1,2 @@ +g frigate 209 +u frigate 209:209 "Frigate" /var/lib/frigate /sbin/nologin diff --git a/templates/frigate/gasket-driver.container b/templates/frigate/gasket-driver.container new file mode 100644 index 0000000..3883f64 --- /dev/null +++ b/templates/frigate/gasket-driver.container @@ -0,0 +1,17 @@ +[Unit] +Description=Install the gasket/apex kernel modules +Wants=network-online.target +After=network-online.target + +[Container] +Image=git.pyrocufflink.net/containerimages/gasket-driver:%v +PodmanArgs=--privileged + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStop=/usr/sbin/rmmod apex +ExecStop=/usr/sbin/rmmod gasket + +[Install] +WantedBy=multi-user.target