Unverified Commit 7eb0c197 authored by Martin Weinelt's avatar Martin Weinelt Committed by GitHub
Browse files

frigate: coral tpu support, audio model, nvidia ffmpeg hwaccel, other fixes (#357717)

parents 21e00d7f 591ebd39
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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).
+38 −0
Original line number Diff line number Diff line
{
  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"
      '';
    })
  ];
}
+1 −0
Original line number Diff line number Diff line
@@ -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
+68 −6
Original line number Diff line number Diff line
@@ -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";

+18 −1
Original line number Diff line number Diff line
@@ -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
Loading