diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 4690bf70cfe8a..2ba0bf5a7fe2b 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -87,6 +87,8 @@ ## New Modules {#sec-release-24.11-new-modules} +- [Coral](https://coral.ai/), hardware support for Coral.ai Edge TPU devices. Available as [hardware.coral.usb.enable](#opt-hardware.coral.usb.enable) and [hardware.coral.pcie.enable](#opt-hardware.coral.pcie.enable). + - [Cyrus IMAP](https://github.com/cyrusimap/cyrus-imapd), an email, contacts and calendar server. Available as [services.cyrus-imap](#opt-services.cyrus-imap.enable) service. - [TaskChampion Sync-Server](https://github.com/GothenburgBitFactory/taskchampion-sync-server), a [Taskwarrior 3](https://taskwarrior.org/docs/upgrade-3/) sync server. Available as [services.taskchampion-sync-server](#opt-services.taskchampion-sync-server.enable). diff --git a/nixos/modules/hardware/coral.nix b/nixos/modules/hardware/coral.nix new file mode 100644 index 0000000000000..4b0cadfc3cf00 --- /dev/null +++ b/nixos/modules/hardware/coral.nix @@ -0,0 +1,38 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (lib) + mkEnableOption + mkIf + mkMerge + ; + + cfg = config.hardware.coral; +in + +{ + options.hardware.coral = { + usb.enable = mkEnableOption "Coral USB support"; + pcie.enable = mkEnableOption "Coral PCIe support"; + }; + + config = mkMerge [ + (mkIf (cfg.usb.enable || cfg.pcie.enable) { + users.groups.coral = { }; + }) + (mkIf cfg.usb.enable { + services.udev.packages = with pkgs; [ libedgetpu ]; + }) + (mkIf cfg.pcie.enable { + boot.extraModulePackages = with config.boot.kernelPackages; [ gasket ]; + services.udev.extraRules = '' + SUBSYSTEM=="apex",MODE="0660",GROUP="coral" + ''; + }) + ]; +} diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index cc107eefe5056..a1b8f52c07730 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -52,6 +52,7 @@ ./hardware/bladeRF.nix ./hardware/brillo.nix ./hardware/ckb-next.nix + ./hardware/coral.nix ./hardware/corectrl.nix ./hardware/cpu/amd-microcode.nix ./hardware/cpu/amd-sev.nix diff --git a/nixos/modules/services/video/frigate.nix b/nixos/modules/services/video/frigate.nix index 72e96df5442eb..bc0f5d496afb8 100644 --- a/nixos/modules/services/video/frigate.nix +++ b/nixos/modules/services/video/frigate.nix @@ -6,19 +6,28 @@ let inherit (lib) - literalExpression + any + attrValues + converge + elem + filterAttrsRecursive + hasPrefix + makeLibraryPath + match mkDefault mkEnableOption mkPackageOption mkIf mkOption + optionalAttrs + optionals types; cfg = config.services.frigate; format = pkgs.formats.yaml { }; - filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! lib.elem v [ null ])) cfg.settings; + filteredConfig = converge (filterAttrsRecursive (_: v: ! elem v [ null ])) cfg.settings; cameraFormat = with types; submodule { freeformType = format.type; @@ -94,6 +103,15 @@ let proxy_connect_timeout 360; ''; + # Discover configured detectors for acceleration support + detectors = attrValues cfg.settings.detectors or {}; + withCoralUSB = any (d: d.type == "edgetpu" && hasPrefix "usb" d.device or "") detectors; + withCoralPCI = any (d: d.type == "edgetpu" && hasPrefix "pci" d.device or "") detectors; + withCoral = withCoralPCI || withCoralUSB; + + # Provide ffmpeg-full for NVIDIA hardware acceleration + ffmpegArgs = cfg.settings.ffmpeg.hwaccel_args or ""; + ffmpeg' = if match "/nvidia/" ffmpegArgs != null then pkgs.ffmpeg-full else pkgs.ffmpeg-headless; in { @@ -114,6 +132,27 @@ in ''; }; + vaapiDriver = mkOption { + type = nullOr (enum [ "i965" "iHD" "nouveau" "vdpau" "nvidia" "radeonsi" ]); + default = null; + example = "radeonsi"; + description = '' + Force usage of a particular VA-API driver for video acceleration. Use together with `settings.ffmpeg.hwaccel_args`. + + Setting this *is not required* for VA-API to work, but it can help steer VA-API towards the correct card if you have multiple. + + :::{.note} + For VA-API to work you must enable {option}`hardware.graphics.enable` (sufficient for AMDGPU) and pass for example + `pkgs.intel-media-driver` (required for Intel 5th Gen. and newer) into {option}`hardware.graphics.extraPackages`. + ::: + + See also: + + - https://docs.frigate.video/configuration/hardware_acceleration + - https://docs.frigate.video/configuration/ffmpeg_presets#hwaccel-presets + ''; + }; + settings = mkOption { type = submodule { freeformType = format.type; @@ -171,7 +210,6 @@ in set-misc vod ]; - recommendedProxySettings = mkDefault true; recommendedGzipSettings = mkDefault true; mapHashBucketSize = mkDefault 128; upstreams = { @@ -202,6 +240,7 @@ in # auth_location.conf "/auth" = { proxyPass = "http://frigate-api/auth"; + recommendedProxySettings = true; extraConfig = '' internal; @@ -306,11 +345,13 @@ in }; "/ws" = { proxyPass = "http://frigate-mqtt-ws/"; + recommendedProxySettings = true; proxyWebsockets = true; extraConfig = nginxAuthRequest + nginxProxySettings; }; "/live/jsmpeg" = { proxyPass = "http://frigate-jsmpeg/"; + recommendedProxySettings = true; proxyWebsockets = true; extraConfig = nginxAuthRequest + nginxProxySettings; }; @@ -318,6 +359,7 @@ in "/live/mse/api/ws" = { proxyPass = "http://frigate-go2rtc/api/ws"; proxyWebsockets = true; + recommendedProxySettings = true; extraConfig = nginxAuthRequest + nginxProxySettings + '' limit_except GET { deny all; @@ -327,6 +369,7 @@ in "/live/webrtc/api/ws" = { proxyPass = "http://frigate-go2rtc/api/ws"; proxyWebsockets = true; + recommendedProxySettings = true; extraConfig = nginxAuthRequest + nginxProxySettings + '' limit_except GET { deny all; @@ -336,6 +379,7 @@ in # pass through go2rtc player "/live/webrtc/webrtc.html" = { proxyPass = "http://frigate-go2rtc/webrtc.html"; + recommendedProxySettings = true; extraConfig = nginxAuthRequest + nginxProxySettings + '' limit_except GET { deny all; @@ -345,6 +389,7 @@ in # frontend uses this to fetch the version "/api/go2rtc/api" = { proxyPass = "http://frigate-go2rtc/api"; + recommendedProxySettings = true; extraConfig = nginxAuthRequest + nginxProxySettings + '' limit_except GET { deny all; @@ -355,6 +400,7 @@ in "/api/go2rtc/webrtc" = { proxyPass = "http://frigate-go2rtc/api/webrtc"; proxyWebsockets = true; + recommendedProxySettings = true; extraConfig = nginxAuthRequest + nginxProxySettings + '' limit_except GET { deny all; @@ -363,12 +409,14 @@ in }; "~* /api/.*\.(jpg|jpeg|png|webp|gif)$" = { proxyPass = "http://frigate-api"; + recommendedProxySettings = true; extraConfig = nginxAuthRequest + nginxProxySettings + '' rewrite ^/api/(.*)$ $1 break; ''; }; "/api/" = { proxyPass = "http://frigate-api/"; + recommendedProxySettings = true; extraConfig = nginxAuthRequest + nginxProxySettings + '' add_header Cache-Control "no-store"; expires off; @@ -492,6 +540,11 @@ in "frigate" ]; + hardware.coral = { + usb.enable = mkDefault withCoralUSB; + pcie.enable = mkDefault withCoralPCI; + }; + users.users.frigate = { isSystemUser = true; group = "frigate"; @@ -510,26 +563,35 @@ in CONFIG_FILE = format.generate "frigate.yml" filteredConfig; HOME = "/var/lib/frigate"; PYTHONPATH = cfg.package.pythonPath; + } // optionalAttrs (cfg.vaapiDriver != null) { + LIBVA_DRIVER_NAME = cfg.vaapiDriver; + } // optionalAttrs withCoral { + LD_LIBRARY_PATH = makeLibraryPath (with pkgs; [ libedgetpu ]); }; path = with pkgs; [ # unfree: # config.boot.kernelPackages.nvidiaPackages.latest.bin - ffmpeg-headless + ffmpeg' libva-utils procps radeontop - ] ++ lib.optionals (!stdenv.hostPlatform.isAarch64) [ + ] ++ optionals (!stdenv.hostPlatform.isAarch64) [ # not available on aarch64-linux intel-gpu-tools ]; serviceConfig = { - ExecStartPre = "-rm /var/cache/frigate/*.mp4"; + ExecStartPre = pkgs.writeShellScript "frigate-clear-cache" '' + rm --recursive --force /var/cache/frigate/* + ''; ExecStart = "${cfg.package.python.interpreter} -m frigate"; Restart = "on-failure"; SyslogIdentifier = "frigate"; User = "frigate"; Group = "frigate"; + SupplementaryGroups = [ "render" ] ++ optionals withCoral [ "coral" ]; + + AmbientCapabilities = optionals (elem cfg.vaapiDriver [ "i965" "iHD" ]) [ "CAP_PERFMON" ]; # for intel_gpu_top UMask = "0027"; diff --git a/pkgs/by-name/fr/frigate/package.nix b/pkgs/by-name/fr/frigate/package.nix index 7ee4f6eba606f..4578d709a0aca 100644 --- a/pkgs/by-name/fr/frigate/package.nix +++ b/pkgs/by-name/fr/frigate/package.nix @@ -29,6 +29,12 @@ let }; }; + # Tensorflow audio model + tflite_audio_model = fetchurl { + url = "https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download"; + hash = "sha256-G5cbITJ2AnOl+49dxQToZ4OyeFO7MTXVVa4G8eHjZfM="; + }; + # Tensorflow Lite models # https://github.com/blakeblackshear/frigate/blob/v0.13.0/docker/main/Dockerfile#L96-L97 tflite_cpu_model = fetchurl { @@ -72,14 +78,22 @@ python.pkgs.buildPythonApplication rec { substituteInPlace frigate/detectors/detector_config.py \ --replace-fail "/labelmap.txt" "${placeholder "out"}/share/frigate/labelmap.txt" + substituteInPlace frigate/output/birdseye.py \ + --replace-fail "/opt/frigate/" "${placeholder "out"}/${python.sitePackages}/" + # work around onvif-zeep idiosyncrasy substituteInPlace frigate/ptz/onvif.py \ --replace-fail dist-packages site-packages + # provide default paths for models and maps that are shipped with frigate substituteInPlace frigate/config.py \ --replace-fail "/cpu_model.tflite" "${tflite_cpu_model}" \ --replace-fail "/edgetpu_model.tflite" "${tflite_edgetpu_model}" + substituteInPlace frigate/events/audio.py \ + --replace-fail "/cpu_audio_model.tflite" "${placeholder "out"}/share/frigate/cpu_audio_model.tflite" \ + --replace-fail "/audio-labelmap.txt" "${placeholder "out"}/share/frigate/audio-labelmap.txt" + substituteInPlace frigate/test/test_config.py \ --replace-fail "(MODEL_CACHE_DIR" "('/build/model_cache'" \ --replace-fail "/config/model_cache" "/build/model_cache" @@ -131,7 +145,10 @@ python.pkgs.buildPythonApplication rec { cp -R frigate/* $out/${python.sitePackages}/frigate/ mkdir -p $out/share/frigate - cp -R {migrations,labelmap.txt} $out/share/frigate/ + cp -R {migrations,labelmap.txt,audio-labelmap.txt} $out/share/frigate/ + + tar --extract --gzip --file ${tflite_audio_model} + cp --no-preserve=mode ./1.tflite $out/share/frigate/cpu_audio_model.tflite cp --no-preserve=mode ${openvino_model} $out/share/frigate/coco_91cl_bkgr.txt sed -i 's/truck/car/g' $out/share/frigate/coco_91cl_bkgr.txt diff --git a/pkgs/by-name/li/libedgetpu/package.nix b/pkgs/by-name/li/libedgetpu/package.nix index 9ead9c5f60b33..563e4d56dda1c 100644 --- a/pkgs/by-name/li/libedgetpu/package.nix +++ b/pkgs/by-name/li/libedgetpu/package.nix @@ -43,6 +43,12 @@ stdenv.mkDerivation { }) ]; + postPatch = '' + # Use dedicated group for coral devices + substituteInPlace debian/edgetpu-accelerator.rules \ + --replace-fail "plugdev" "coral" + ''; + makeFlags = [ "-f" "makefile_build/Makefile" diff --git a/pkgs/os-specific/linux/gasket/default.nix b/pkgs/os-specific/linux/gasket/default.nix index a25bbeee40333..65516be7dcd17 100644 --- a/pkgs/os-specific/linux/gasket/default.nix +++ b/pkgs/os-specific/linux/gasket/default.nix @@ -1,4 +1,10 @@ -{ stdenv, lib, fetchFromGitHub, kernel }: +{ + stdenv, + lib, + fetchFromGitHub, + fetchpatch2, + kernel +}: stdenv.mkDerivation rec { pname = "gasket"; @@ -11,6 +17,20 @@ stdenv.mkDerivation rec { sha256 = "O17+msok1fY5tdX1DvqYVw6plkUDF25i8sqwd6mxYf8="; }; + patches = [ + (fetchpatch2 { + # https://github.com/google/gasket-driver/issues/36 + # https://github.com/google/gasket-driver/pull/35 + name = "linux-6.12-compat.patch"; + url = "https://github.com/google/gasket-driver/commit/4b2a1464f3b619daaf0f6c664c954a42c4b7ce00.patch"; + hash = "sha256-UOoOSEnpUMa4QXWVFpGFxBoF5szXaLEfcWtfKatO5XY="; + }) + ]; + + postPatch = '' + cd src + ''; + makeFlags = kernel.makeFlags ++ [ "-C" "${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" @@ -21,7 +41,6 @@ stdenv.mkDerivation rec { installFlags = [ "INSTALL_MOD_PATH=${placeholder "out"}" ]; installTargets = [ "modules_install" ]; - sourceRoot = "${src.name}/src"; hardeningDisable = [ "pic" "format" ]; nativeBuildInputs = kernel.moduleBuildDependencies;