diff --git a/hosts/kameramann/nvr.nix b/hosts/kameramann/nvr.nix index 137b96e..2588827 100644 --- a/hosts/kameramann/nvr.nix +++ b/hosts/kameramann/nvr.nix @@ -1,4 +1,4 @@ -{ config, lib, ... }: +{ config, lib, pkgs, ... }: let cameras = { "ulfried" = { @@ -35,8 +35,7 @@ let }; }; - # Uppercase camera name for use as systemd credential / env var name - varName = name: lib.toUpper (lib.replaceStrings [ "-" ] [ "_" ] name); + varName = name: "FRIGATE_" + lib.toUpper (lib.replaceStrings [ "-" ] [ "_" ] name); go2rtcCameraStreams = name: cam: @@ -45,11 +44,11 @@ let in { "${name}" = [ - "\${${varName name}_URL}" + "{${varName name}_URL}" "ffmpeg:${name}#audio=opus#audio=aac${video}" ]; "${name}_sub" = [ - "\${${varName name}_SUB_URL}" + "{${varName name}_SUB_URL}" "ffmpeg:${name}_sub#audio=opus#audio=aac${video}" ]; }; @@ -63,95 +62,119 @@ let { path = "rtsp://127.0.0.1:8554/${name}_sub?timeout=30"; input_args = "preset-rtsp-restream"; - roles = [ - "detect" - "audio" - ]; + roles = [ "detect" "audio" ]; } ]; - # Only cameras with a frigate key get a frigate camera entry frigCameras = lib.filterAttrs (_: cam: cam ? frigate) cameras; -in -{ - age.secrets = lib.mkMerge ( - lib.mapAttrsToList (name: _: { - "camera-${name}-url".file = ../../secrets/camera-${name}-url.age; - "camera-${name}-sub-url".file = ../../secrets/camera-${name}-sub-url.age; - }) cameras - ); - systemd.services.go2rtc.serviceConfig.LoadCredential = lib.concatMap (name: [ - "${varName name}_URL:${config.age.secrets."camera-${name}-url".path}" - "${varName name}_SUB_URL:${config.age.secrets."camera-${name}-sub-url".path}" - ]) (lib.attrNames cameras); - - services.go2rtc = { - enable = true; - settings = { + frigateConfig = { + auth.enabled = false; + proxy.default_role = "admin"; + go2rtc = { rtsp.listen = ":8554"; webrtc.listen = ":8555"; streams = lib.foldl' lib.mergeAttrs { } (lib.mapAttrsToList go2rtcCameraStreams cameras); }; + mqtt = { + enabled = true; + host = "192.168.178.33"; + user = "frigate"; + password = "frigate"; + }; + ffmpeg.hwaccel_args = "preset-vaapi"; + detectors.ov_0 = { + type = "openvino"; + device = "GPU"; + }; + model = { + model_type = "yolo-generic"; + width = 320; + height = 320; + input_tensor = "nchw"; + input_dtype = "float"; + path = "/config/model_cache/yolov9-t-320.onnx"; + labelmap_path = "/labelmap/coco-80.txt"; + }; + detect.enabled = true; + snapshots.enabled = true; + semantic_search = { enabled = false; model_size = "small"; }; + face_recognition = { enabled = true; model_size = "large"; }; + lpr.enabled = false; + objects.track = [ "person" "bird" "car" ]; + telemetry.stats.intel_gpu_device = "sys:/sys/devices/pci0000:00/0000:00:02.0"; + classification.bird.enabled = true; + cameras = lib.mapAttrs ( + name: cam: cam.frigate // { ffmpeg.inputs = frigateInputs name; } + ) frigCameras; }; - services.frigate = { - enable = true; - vaapiDriver = "iHD"; - hostname = "kameramann.lan.baubs.net"; - settings = { - auth.enabled = false; - go2rtc.streams = lib.foldl' lib.mergeAttrs { } ( - lib.mapAttrsToList (name: _: { - "${name}" = [ ]; - "${name}_sub" = [ ]; - }) cameras - ); - mqtt = { - enabled = true; - host = "192.168.178.33"; - user = "frigate"; - password = "frigate"; - }; - ffmpeg.hwaccel_args = "preset-vaapi"; - detectors.ov_0 = { - type = "openvino"; - device = "GPU"; - }; - model = { - model_type = "yolo-generic"; - width = 320; - height = 320; - input_tensor = "nchw"; - input_dtype = "float"; - path = "${./models/yolov9-t-320.onnx}"; - labelmap_path = "${./models/coco-80.txt}"; - }; - detect.enabled = true; - snapshots.enabled = true; - semantic_search = { enabled = false; model_size = "small"; }; - face_recognition = { enabled = true; model_size = "large"; }; - lpr.enabled = false; - proxy.default_role = "admin"; - objects.track = [ - "person" - "bird" - "car" + configFile = (pkgs.formats.yaml { }).generate "frigate-config.yml" frigateConfig; +in +{ + age.secrets = lib.mkMerge ( + lib.mapAttrsToList (name: _: { + "camera-${name}-url".file = ../../secrets/camera-${name}-url.age; + "camera-${name}-sub-url".file = ../../secrets/camera-${name}-sub-url.age; + }) cameras + ); + + # Assemble env file from individual agenix secrets for the container + systemd.services.frigate-env = { + description = "Generate Frigate environment file from secrets"; + before = [ "podman-frigate.service" ]; + requiredBy = [ "podman-frigate.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + umask 077 + { + ${lib.concatMapStringsSep "\n" (name: '' + printf '%s=%s\n' "${varName name}_URL" "$(cat ${config.age.secrets."camera-${name}-url".path})" + printf '%s=%s\n' "${varName name}_SUB_URL" "$(cat ${config.age.secrets."camera-${name}-sub-url".path})" + '') (lib.attrNames cameras)} + } > /run/frigate-env + ''; + }; + + systemd.tmpfiles.rules = [ + "d /var/lib/frigate 0755 root root -" + "d /var/lib/frigate-media 0755 root root -" + ]; + + virtualisation.podman.enable = true; + virtualisation.oci-containers = { + backend = "podman"; + containers.frigate = { + image = "ghcr.io/blakeblackshear/frigate:stable"; + autoStart = true; + volumes = [ + "/var/lib/frigate:/config" + "${configFile}:/config/config.yml:ro" + "${./models/yolov9-t-320.onnx}:/config/model_cache/yolov9-t-320.onnx:ro" + "${./models/coco-80.txt}:/labelmap/coco-80.txt:ro" + "/var/lib/frigate-media:/media/frigate" + ]; + environment.LIBVA_DRIVER_NAME = "iHD"; + environmentFiles = [ /run/frigate-env ]; + ports = [ + "5000:5000" + #"8971:8971" + "1984:1984" + "8554:8554" + "8555:8555/tcp" + "8555:8555/udp" + ]; + extraOptions = [ + "--shm-size=512m" + "--mount=type=tmpfs,target=/tmp/cache,tmpfs-size=1000000000" + "--device=/dev/dri/renderD128:/dev/dri/renderD128" ]; - telemetry.stats.intel_gpu_device = "sys:/sys/devices/pci0000:00/0000:00:02.0"; - classification.bird.enabled = true; - cameras = lib.mapAttrs ( - name: cam: cam.frigate // { ffmpeg.inputs = frigateInputs name; } - ) frigCameras; }; }; - networking.firewall.allowedTCPPorts = [ - 5000 - 1984 - 80 - 8555 - 8554 - ]; + networking.firewall.allowedTCPPorts = [ 5000 1984 8554 8555 ]; networking.firewall.allowedUDPPorts = [ 8555 8554 ]; }