at master 5.6 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8let 9 10 inherit (lib) 11 attrValues 12 concatStringsSep 13 filterAttrs 14 length 15 listToAttrs 16 literalExpression 17 makeSearchPathOutput 18 mkEnableOption 19 mkIf 20 mkOption 21 nameValuePair 22 optionals 23 types 24 ; 25 inherit (utils) escapeSystemdPath; 26 27 cfg = config.services.v4l2-relayd; 28 29 kernelPackages = config.boot.kernelPackages; 30 31 gst = ( 32 with pkgs.gst_all_1; 33 [ 34 gst-plugins-bad 35 gst-plugins-base 36 gst-plugins-good 37 gstreamer.out 38 ] 39 ); 40 41 instanceOpts = 42 { name, ... }: 43 { 44 options = { 45 enable = mkEnableOption "this v4l2-relayd instance"; 46 47 name = mkOption { 48 type = types.str; 49 default = name; 50 description = '' 51 The name of the instance. 52 ''; 53 }; 54 55 cardLabel = mkOption { 56 type = types.str; 57 description = '' 58 The name the camera will show up as. 59 ''; 60 }; 61 62 extraPackages = mkOption { 63 type = with types; listOf package; 64 default = [ ]; 65 description = '' 66 Extra packages to add to {env}`GST_PLUGIN_PATH` for the instance. 67 ''; 68 }; 69 70 input = { 71 pipeline = mkOption { 72 type = types.str; 73 description = '' 74 The gstreamer-pipeline to use for the input-stream. 75 ''; 76 }; 77 78 format = mkOption { 79 type = types.str; 80 default = "YUY2"; 81 description = '' 82 The video-format to read from input-stream. 83 ''; 84 }; 85 86 width = mkOption { 87 type = types.ints.positive; 88 default = 1280; 89 description = '' 90 The width to read from input-stream. 91 ''; 92 }; 93 94 height = mkOption { 95 type = types.ints.positive; 96 default = 720; 97 description = '' 98 The height to read from input-stream. 99 ''; 100 }; 101 102 framerate = mkOption { 103 type = types.ints.positive; 104 default = 30; 105 description = '' 106 The framerate to read from input-stream. 107 ''; 108 }; 109 }; 110 111 output = { 112 format = mkOption { 113 type = types.str; 114 default = "YUY2"; 115 description = '' 116 The video-format to write to output-stream. 117 ''; 118 }; 119 }; 120 121 }; 122 }; 123 124in 125{ 126 127 options.services.v4l2-relayd = { 128 129 instances = mkOption { 130 type = with types; attrsOf (submodule instanceOpts); 131 default = { }; 132 example = literalExpression '' 133 { 134 example = { 135 cardLabel = "Example card"; 136 input.pipeline = "videotestsrc"; 137 }; 138 } 139 ''; 140 description = '' 141 v4l2-relayd instances to be created. 142 ''; 143 }; 144 145 }; 146 147 config = 148 let 149 150 mkInstanceService = instance: { 151 description = "Streaming relay for v4l2loopback using GStreamer"; 152 153 after = [ 154 "modprobe@v4l2loopback.service" 155 "systemd-logind.service" 156 ]; 157 wantedBy = [ "multi-user.target" ]; 158 159 serviceConfig = { 160 Type = "simple"; 161 Restart = "always"; 162 PrivateNetwork = true; 163 PrivateTmp = true; 164 LimitNPROC = 1; 165 }; 166 167 environment = { 168 GST_PLUGIN_PATH = makeSearchPathOutput "lib" "lib/gstreamer-1.0" (gst ++ instance.extraPackages); 169 V4L2_DEVICE_FILE = "/run/v4l2-relayd-${instance.name}/device"; 170 }; 171 172 script = 173 let 174 appsrcOptions = concatStringsSep "," [ 175 "caps=video/x-raw" 176 "format=${instance.input.format}" 177 "width=${toString instance.input.width}" 178 "height=${toString instance.input.height}" 179 "framerate=${toString instance.input.framerate}/1" 180 ]; 181 182 outputPipeline = [ 183 "appsrc name=appsrc ${appsrcOptions}" 184 "videoconvert" 185 ] 186 ++ optionals (instance.input.format != instance.output.format) [ 187 "video/x-raw,format=${instance.output.format}" 188 "queue" 189 ] 190 ++ [ "v4l2sink name=v4l2sink device=$(cat $V4L2_DEVICE_FILE)" ]; 191 in 192 '' 193 exec ${pkgs.v4l2-relayd}/bin/v4l2-relayd -i "${instance.input.pipeline}" -o "${concatStringsSep " ! " outputPipeline}" 194 ''; 195 196 preStart = '' 197 mkdir -p $(dirname $V4L2_DEVICE_FILE) 198 ${kernelPackages.v4l2loopback.bin}/bin/v4l2loopback-ctl add -x 1 -n "${instance.cardLabel}" > $V4L2_DEVICE_FILE 199 ''; 200 201 postStop = '' 202 ${kernelPackages.v4l2loopback.bin}/bin/v4l2loopback-ctl delete $(cat $V4L2_DEVICE_FILE) 203 rm -rf $(dirname $V4L2_DEVICE_FILE) 204 ''; 205 }; 206 207 mkInstanceServices = 208 instances: 209 listToAttrs ( 210 map ( 211 instance: 212 nameValuePair "v4l2-relayd-${escapeSystemdPath instance.name}" (mkInstanceService instance) 213 ) instances 214 ); 215 216 enabledInstances = attrValues (filterAttrs (n: v: v.enable) cfg.instances); 217 218 in 219 { 220 221 boot = mkIf ((length enabledInstances) > 0) { 222 extraModulePackages = [ kernelPackages.v4l2loopback ]; 223 kernelModules = [ "v4l2loopback" ]; 224 }; 225 226 systemd.services = mkInstanceServices enabledInstances; 227 228 }; 229 230 meta.maintainers = with lib.maintainers; [ betaboon ]; 231}