Refactor frigate config to add a bunch of features
All checks were successful
Check Flake / check-flake (push) Successful in 2h20m26s

- Enable vaapi GPU video encode/decode support
- Use go2rtc. This allows for watching high resolution camera feeds
- Split nix config into pieces that are easier to understand
- Add utilities for easily adding new cameras in the future
- misc changes
This commit is contained in:
Zuckerberg 2024-06-30 12:49:24 -06:00
parent 91874b9d53
commit 66bfc62566

View File

@ -3,7 +3,63 @@
let let
frigateHostname = "frigate.s0.neet.dev"; frigateHostname = "frigate.s0.neet.dev";
mkEsp32Cam = address: { mkGo2RtcStream = name: url: withAudio: {
${name} = [
url
"ffmpeg:${name}#video=copy${if withAudio then "#audio=copy" else ""}"
];
};
# Assumes camera is set to output:
# - rtsp
# - H.264 + AAC
# - a downscaled substream for detection
mkCamera = name: primaryUrl: detectUrl: {
# Reference https://docs.frigate.video/configuration/reference/
services.frigate.settings = {
cameras.${name} = {
ffmpeg = {
# Camera feeds are relayed through go2rtc
inputs = [
{
path = "rtsp://127.0.0.1:8554/${name}";
# input_args = "preset-rtsp-restream";
input_args = "preset-rtsp-restream-low-latency";
roles = [ "record" ];
}
{
path = detectUrl;
roles = [ "detect" ];
}
];
output_args = {
record = "preset-record-generic-audio-copy";
};
};
};
};
services.go2rtc.settings.streams = lib.mkMerge [
(mkGo2RtcStream name primaryUrl false)
# Sadly having the detection stream go through go2rpc too makes the stream unreadable by frigate for some reason.
# It might need to be re-encoded to work. But I am not interested in wasting the processing power if only frigate
# need the detection stream anyway. So just let frigate grab the stream directly since it works.
# (mkGo2RtcStream detectName detectUrl false)
];
};
mkDahuaCamera = name: address:
let
# go2rtc and frigate have a slightly different syntax for inserting env vars. So the URLs are not interchangable :(
# - go2rtc: ${VAR}
# - frigate: {VAR}
primaryUrl = "rtsp://admin:\${FRIGATE_RTSP_PASSWORD}@${address}/cam/realmonitor?channel=1&subtype=0";
detectUrl = "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@${address}/cam/realmonitor?channel=1&subtype=1";
in
mkCamera name primaryUrl detectUrl;
mkEsp32Camera = name: address: {
services.frigate.settings.cameras.${name} = {
ffmpeg = { ffmpeg = {
input_args = ""; input_args = "";
inputs = [{ inputs = [{
@ -13,41 +69,13 @@ let
output_args.record = "-f segment -pix_fmt yuv420p -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1 -c:v libx264 -preset ultrafast -an "; output_args.record = "-f segment -pix_fmt yuv420p -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1 -c:v libx264 -preset ultrafast -an ";
}; };
detect = {
enabled = true;
width = 800;
height = 600;
fps = 10;
};
objects = {
track = [ "person" ];
};
};
mkDahuaCam = address: {
ffmpeg = {
inputs = [
{
path = "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@${address}/cam/realmonitor?channel=1&subtype=0";
roles = [ "record" ];
}
{
path = "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@${address}/cam/realmonitor?channel=1&subtype=1";
roles = [ "detect" ];
}
];
};
detect.enabled = true;
objects = {
track = [ "person" "dog" ];
}; };
}; };
in in
lib.mkMerge [
(mkDahuaCamera "dog-cam" "192.168.10.31")
# (mkEsp32Camera "dahlia-cam" "dahlia-cam.lan")
{ {
networking.firewall.allowedTCPPorts = [
# 1883 # mqtt
];
services.frigate = { services.frigate = {
enable = true; enable = true;
hostname = frigateHostname; hostname = frigateHostname;
@ -63,7 +91,7 @@ in
}; };
record = { record = {
enabled = true; enabled = true;
# sync_recordings = true; # detect if recordings were deleted outside of frigate # sync_recordings = true; # detect if recordings were deleted outside of frigate (expensive)
retain = { retain = {
days = 2; # Keep video for 2 days days = 2; # Keep video for 2 days
mode = "motion"; mode = "motion";
@ -76,32 +104,52 @@ in
}; };
}; };
}; };
cameras = { # Make frigate aware of the go2rtc streams
dahlia-cam = mkEsp32Cam "dahlia-cam.lan"; go2rtc.streams = config.services.go2rtc.settings.streams;
dog-cam = mkDahuaCam "192.168.10.31"; detect.enabled = true;
}; objects = {
# ffmpeg = { track = [ "person" "dog" ];
# hwaccel_args = "preset-vaapi";
# };
detectors.coral = {
type = "edgetpu";
device = "pci";
}; };
}; };
}; };
# Pass in env file with secrets to frigate services.go2rtc = {
enable = true;
settings = {
rtsp.listen = ":8554";
webrtc.listen = ":8555";
};
};
# Pass in env file with secrets to frigate/go2rtc
systemd.services.frigate.serviceConfig.EnvironmentFile = "/run/agenix/frigate-credentials"; systemd.services.frigate.serviceConfig.EnvironmentFile = "/run/agenix/frigate-credentials";
systemd.services.go2rtc.serviceConfig.EnvironmentFile = "/run/agenix/frigate-credentials";
age.secrets.frigate-credentials.file = ../../../secrets/frigate-credentials.age; age.secrets.frigate-credentials.file = ../../../secrets/frigate-credentials.age;
}
# AMD GPU for vaapi {
systemd.services.frigate.environment.LIBVA_DRIVER_NAME = "radeonsi"; # hardware encode/decode with amdgpu vaapi
systemd.services.frigate = {
environment.LIBVA_DRIVER_NAME = "radeonsi";
serviceConfig = {
SupplementaryGroups = [ "render" "video" ]; # for access to dev/dri/*
AmbientCapabilities = "CAP_PERFMON";
};
};
services.frigate.settings.ffmpeg.hwaccel_args = "preset-vaapi";
}
{
# Coral TPU for frigate # Coral TPU for frigate
services.udev.packages = [ pkgs.libedgetpu ]; services.udev.packages = [ pkgs.libedgetpu ];
users.groups.apex = { }; users.groups.apex = { };
systemd.services.frigate.environment.LD_LIBRARY_PATH = "${pkgs.libedgetpu}/lib"; systemd.services.frigate.environment.LD_LIBRARY_PATH = "${pkgs.libedgetpu}/lib";
systemd.services.frigate.serviceConfig.SupplementaryGroups = "apex"; systemd.services.frigate.serviceConfig.SupplementaryGroups = [ "apex" ];
# Coral PCIe driver # Coral PCIe driver
kernel.enableGasketKernelModule = true; kernel.enableGasketKernelModule = true;
services.frigate.settings.detectors.coral = {
type = "edgetpu";
device = "pci";
};
} }
]