lol

nixos/v4l2-relayd: init

betaboon dd33a7a9 615c0d9b

+202
+2
nixos/doc/manual/release-notes/rl-2305.section.md
··· 101 101 102 102 - [ReGreet](https://github.com/rharish101/ReGreet), a clean and customizable greeter for greetd. Available as [programs.regreet](#opt-programs.regreet.enable). 103 103 104 + - [v4l2-relayd](https://git.launchpad.net/v4l2-relayd), a streaming relay for v4l2loopback using gstreamer. Available as [services.v4l2-relayd](#opt-services.v4l2-relayd.instances._name_.enable). 105 + 104 106 ## Backward Incompatibilities {#sec-release-23.05-incompatibilities} 105 107 106 108 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+1
nixos/modules/module-list.nix
··· 1130 1130 ./services/video/replay-sorcery.nix 1131 1131 ./services/video/rtsp-simple-server.nix 1132 1132 ./services/video/unifi-video.nix 1133 + ./services/video/v4l2-relayd.nix 1133 1134 ./services/wayland/cage.nix 1134 1135 ./services/web-apps/akkoma.nix 1135 1136 ./services/web-apps/alps.nix
+199
nixos/modules/services/video/v4l2-relayd.nix
··· 1 + { config, lib, pkgs, utils, ... }: 2 + let 3 + 4 + inherit (lib) attrValues concatStringsSep filterAttrs length listToAttrs literalExpression 5 + makeSearchPathOutput mkEnableOption mkIf mkOption nameValuePair optionals types; 6 + inherit (utils) escapeSystemdPath; 7 + 8 + cfg = config.services.v4l2-relayd; 9 + 10 + kernelPackages = config.boot.kernelPackages; 11 + 12 + gst = (with pkgs.gst_all_1; [ 13 + gst-plugins-bad 14 + gst-plugins-base 15 + gst-plugins-good 16 + gstreamer.out 17 + ]); 18 + 19 + instanceOpts = { name, ... }: { 20 + options = { 21 + enable = mkEnableOption (lib.mdDoc "this v4l2-relayd instance"); 22 + 23 + name = mkOption { 24 + type = types.str; 25 + default = name; 26 + description = lib.mdDoc '' 27 + The name of the instance. 28 + ''; 29 + }; 30 + 31 + cardLabel = mkOption { 32 + type = types.str; 33 + description = lib.mdDoc '' 34 + The name the camera will show up as. 35 + ''; 36 + }; 37 + 38 + extraPackages = mkOption { 39 + type = with types; listOf package; 40 + default = [ ]; 41 + description = lib.mdDoc '' 42 + Extra packages to add to {env}`GST_PLUGIN_PATH` for the instance. 43 + ''; 44 + }; 45 + 46 + input = { 47 + pipeline = mkOption { 48 + type = types.str; 49 + description = lib.mdDoc '' 50 + The gstreamer-pipeline to use for the input-stream. 51 + ''; 52 + }; 53 + 54 + format = mkOption { 55 + type = types.str; 56 + default = "YUY2"; 57 + description = lib.mdDoc '' 58 + The video-format to read from input-stream. 59 + ''; 60 + }; 61 + 62 + width = mkOption { 63 + type = types.ints.positive; 64 + default = 1280; 65 + description = lib.mdDoc '' 66 + The width to read from input-stream. 67 + ''; 68 + }; 69 + 70 + height = mkOption { 71 + type = types.ints.positive; 72 + default = 720; 73 + description = lib.mdDoc '' 74 + The height to read from input-stream. 75 + ''; 76 + }; 77 + 78 + framerate = mkOption { 79 + type = types.ints.positive; 80 + default = 30; 81 + description = lib.mdDoc '' 82 + The framerate to read from input-stream. 83 + ''; 84 + }; 85 + }; 86 + 87 + output = { 88 + format = mkOption { 89 + type = types.str; 90 + default = "YUY2"; 91 + description = lib.mdDoc '' 92 + The video-format to write to output-stream. 93 + ''; 94 + }; 95 + }; 96 + 97 + }; 98 + }; 99 + 100 + in 101 + { 102 + 103 + options.services.v4l2-relayd = { 104 + 105 + instances = mkOption { 106 + type = with types; attrsOf (submodule instanceOpts); 107 + default = { }; 108 + example = literalExpression '' 109 + { 110 + example = { 111 + cardLabel = "Example card"; 112 + input.pipeline = "videotestsrc"; 113 + }; 114 + } 115 + ''; 116 + description = lib.mdDoc '' 117 + v4l2-relayd instances to be created. 118 + ''; 119 + }; 120 + 121 + }; 122 + 123 + config = 124 + let 125 + 126 + mkInstanceService = instance: { 127 + description = "Streaming relay for v4l2loopback using GStreamer"; 128 + 129 + after = [ "modprobe@v4l2loopback.service" "systemd-logind.service" ]; 130 + wantedBy = [ "multi-user.target" ]; 131 + 132 + serviceConfig = { 133 + Type = "simple"; 134 + Restart = "always"; 135 + PrivateNetwork = true; 136 + PrivateTmp = true; 137 + LimitNPROC = 1; 138 + }; 139 + 140 + environment = { 141 + GST_PLUGIN_PATH = makeSearchPathOutput "lib" "lib/gstreamer-1.0" (gst ++ instance.extraPackages); 142 + V4L2_DEVICE_FILE = "/run/v4l2-relayd-${instance.name}/device"; 143 + }; 144 + 145 + script = 146 + let 147 + appsrcOptions = concatStringsSep "," [ 148 + "caps=video/x-raw" 149 + "format=${instance.input.format}" 150 + "width=${toString instance.input.width}" 151 + "height=${toString instance.input.height}" 152 + "framerate=${toString instance.input.framerate}/1" 153 + ]; 154 + 155 + outputPipeline = [ 156 + "appsrc name=appsrc ${appsrcOptions}" 157 + "videoconvert" 158 + ] ++ optionals (instance.input.format != instance.output.format) [ 159 + "video/x-raw,format=${instance.output.format}" 160 + "queue" 161 + ] ++ [ "v4l2sink name=v4l2sink device=$(cat $V4L2_DEVICE_FILE)" ]; 162 + in 163 + '' 164 + exec ${pkgs.v4l2-relayd}/bin/v4l2-relayd -i "${instance.input.pipeline}" -o "${concatStringsSep " ! " outputPipeline}" 165 + ''; 166 + 167 + preStart = '' 168 + mkdir -p $(dirname $V4L2_DEVICE_FILE) 169 + ${kernelPackages.v4l2loopback.bin}/bin/v4l2loopback-ctl add -x 1 -n "${instance.cardLabel}" > $V4L2_DEVICE_FILE 170 + ''; 171 + 172 + postStop = '' 173 + ${kernelPackages.v4l2loopback.bin}/bin/v4l2loopback-ctl delete $(cat $V4L2_DEVICE_FILE) 174 + rm -rf $(dirname $V4L2_DEVICE_FILE) 175 + ''; 176 + }; 177 + 178 + mkInstanceServices = instances: listToAttrs (map 179 + (instance: 180 + nameValuePair "v4l2-relayd-${escapeSystemdPath instance.name}" (mkInstanceService instance) 181 + ) 182 + instances); 183 + 184 + enabledInstances = attrValues (filterAttrs (n: v: v.enable) cfg.instances); 185 + 186 + in 187 + { 188 + 189 + boot = mkIf ((length enabledInstances) > 0) { 190 + extraModulePackages = [ kernelPackages.v4l2loopback ]; 191 + kernelModules = [ "v4l2loopback" ]; 192 + }; 193 + 194 + systemd.services = mkInstanceServices enabledInstances; 195 + 196 + }; 197 + 198 + meta.maintainers = with lib.maintainers; [ betaboon ]; 199 + }