diff --git a/config/hosts/beltane/nixos/virtualhosts.nix b/config/hosts/beltane/nixos/virtualhosts.nix index d250515e..52525c81 100644 --- a/config/hosts/beltane/nixos/virtualhosts.nix +++ b/config/hosts/beltane/nixos/virtualhosts.nix @@ -1,52 +1,74 @@ -{ config, ... }: +{ config, lib, ... }: + +with lib; { - services.nginx.virtualHosts = { - "beltane.net.kittywit.ch" = { - useACMEHost = "beltane.net.kittywit.ch"; - forceSSL = true; - locations = { - "/jellyfin/".proxyPass = "http://127.0.0.1:8096/jellyfin/"; - "/jellyfin/socket" = { - proxyPass = "http://127.0.0.1:8096/jellyfin/"; - extraConfig = '' - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - ''; - }; - "/tvheadend/".proxyPass = "http://192.168.1.245:9981"; - "/" = { - root = "/mnt/zraw/media/"; - extraConfig = "autoindex on;"; - }; - "/transmission" = { - proxyPass = "http://[::1]:9091"; - extraConfig = "proxy_pass_header X-Transmission-Session-Id;"; + services.nginx = { + virtualHosts = { + "beltane.net.kittywit.ch" = { + useACMEHost = "beltane.net.kittywit.ch"; + forceSSL = true; + locations = { + "/jellyfin/".proxyPass = "http://127.0.0.1:8096/jellyfin/"; + "/jellyfin/socket" = { + proxyPass = "http://127.0.0.1:8096/jellyfin/"; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + ''; + }; + "/tvheadend/".proxyPass = "http://127.0.0.1:9981"; + "/" = { + root = "/mnt/zraw/media/"; + extraConfig = "autoindex on;"; + }; + "/transmission" = { + proxyPass = "http://[::1]:9091"; + extraConfig = "proxy_pass_header X-Transmission-Session-Id;"; + }; }; }; - }; - "192.168.1.223" = { - locations = { - "/jellyfin/".proxyPass = "http://127.0.0.1:8096/jellyfin/"; - "/jellyfin/socket" = { - proxyPass = "http://127.0.0.1:8096/jellyfin/"; - extraConfig = '' - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - ''; - }; + "192.168.1.223" = { + locations = { + "/jellyfin/".proxyPass = "http://127.0.0.1:8096/jellyfin/"; + "/jellyfin/socket" = { + proxyPass = "http://127.0.0.1:8096/jellyfin/"; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + ''; + }; - "/share/" = { + "/share/" = { + alias = "/mnt/zraw/media/"; + extraConfig = "autoindex on;"; + }; + }; + }; + "100.103.111.44" = { + locations."/share/" = { alias = "/mnt/zraw/media/"; extraConfig = "autoindex on;"; }; }; }; - "100.103.111.44" = { - locations."/share/" = { - alias = "/mnt/zraw/media/"; - extraConfig = "autoindex on;"; - }; - }; + appendConfig = '' + rtmp { + server { + listen [::]:1935 ipv6only=off; + application stream { + live on; + + allow publish all; + allow play all; + } + } + } + ''; + }; + + kw.fw = { + private.tcp.ports = singleton 1935; + public.tcp.ports = singleton 1935; }; } diff --git a/config/hosts/ostara/nixos.nix b/config/hosts/ostara/nixos.nix new file mode 100644 index 00000000..25951c19 --- /dev/null +++ b/config/hosts/ostara/nixos.nix @@ -0,0 +1,61 @@ +{ lib, config, users, pkgs, profiles, services, ... }: + +with lib; + +{ + # Imports + + imports = [ + profiles.hardware.eeepc-1015pem + profiles.laptop + services.kattv + ]; + + # File Systems and Swap + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/469a684b-eb8f-48a8-8f98-be58528312c4"; + fsType = "ext4"; + }; + }; + + swapDevices = [{ device = "/dev/disk/by-uuid/2223e305-79c9-45b3-90d7-560dcc45775a"; }]; + + # Bootloader + + boot.loader.grub = { + enable = true; + version = 2; + device = "/dev/sda"; + }; + + # Hardware + + services.logind.lidSwitchExternalPower = "ignore"; + + # Networking + + networking = { + hostId = "9f89b327"; + useDHCP = false; + wireless.interfaces = [ "wlp2s0" ]; + interfaces = { + enp1s0.useDHCP = true; + wlp2s0.useDHCP = true; + }; + }; + + # Firewall + + kw.fw = { + public = { + interfaces = singleton "wlp2s0"; + tcp.ports = [ 9981 9982 ]; + }; + }; + + # State + + system.stateVersion = "20.09"; +} diff --git a/config/profiles/base/profiles.nix b/config/profiles/base/profiles.nix index 7e367895..310421e1 100644 --- a/config/profiles/base/profiles.nix +++ b/config/profiles/base/profiles.nix @@ -14,9 +14,10 @@ with lib; amdgpu = mkEnableOption "AMD GPU"; hcloud-imperative = mkEnableOption "Imperative Hetzner Cloud Setup"; intel = mkEnableOption "Intel CPU"; + ryzen = mkEnableOption "AMD Ryzen CPU"; ms-7b86 = mkEnableOption "MSI B450-A Pro Max"; rm-310 = mkEnableOption "Intel DQ67OW"; - ryzen = mkEnableOption "AMD Ryzen CPU"; + eeepc-1015pem = mkEnableOption "Asus Eee PC 1015PEM"; v330-14arr = mkEnableOption "Lenovo Ideapad v330-14ARR"; }; }; @@ -34,9 +35,10 @@ with lib; amdgpu = mkEnableOption "AMD GPU"; hcloud-imperative = mkEnableOption "Imperative Hetzner Cloud Setup"; intel = mkEnableOption "Intel CPU"; + ryzen = mkEnableOption "AMD Ryzen CPU"; ms-7b86 = mkEnableOption "MSI B450-A Pro Max"; rm-310 = mkEnableOption "Intel DQ67OW"; - ryzen = mkEnableOption "AMD Ryzen CPU"; + eeepc-1015pem = mkEnableOption "Asus Eee PC 1015PEM"; v330-14arr = mkEnableOption "Lenovo Ideapad v330-14ARR"; }; }; diff --git a/config/profiles/hardware/default.nix b/config/profiles/hardware/default.nix index 238403c6..3f74eb95 100644 --- a/config/profiles/hardware/default.nix +++ b/config/profiles/hardware/default.nix @@ -9,6 +9,12 @@ let profiles = with profiles; lib.modList { amdgpu ]; }; + rm-310 = { + imports = [ + rm-310-base + intel + ]; + }; v330-14arr = { imports = [ v330-14arr-base @@ -16,11 +22,11 @@ let profiles = with profiles; lib.modList { amdgpu ]; }; - rm-310 = { + eeepc-1015pem = { imports = [ - rm-310-base - intel - ]; - }; -}; in profiles; + eeepc-1015pem-base + intel + ]; + }; + }; in profiles; in { __functor = self: hardwareProfiles; isModule = false; } diff --git a/config/profiles/hardware/eeepc-1015pem-base/default.nix b/config/profiles/hardware/eeepc-1015pem-base/default.nix new file mode 100644 index 00000000..bac6ec74 --- /dev/null +++ b/config/profiles/hardware/eeepc-1015pem-base/default.nix @@ -0,0 +1,17 @@ +{ config, ... }: + +{ + deploy.profile.hardware.eeepc-1015pem = true; + + boot = { + initrd = { + availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "usb_storage" "sd_mod" ]; + kernelModules = [ ]; + }; + kernelModules = [ ]; + extraModulePackages = [ ]; + kernelParams = [ + "usbcore.autosuspend=-1" + ]; + }; +} diff --git a/config/services/kattv-ingest/default.nix b/config/services/kattv-ingest/default.nix new file mode 100644 index 00000000..55cc5357 --- /dev/null +++ b/config/services/kattv-ingest/default.nix @@ -0,0 +1,28 @@ +{ config, pkgs, lib , ... }: + +with lib; + +{ + kw.fw.public.tcp.ports = [ 4953 1935 ]; + + systemd.sockets.kattv = { + listenStreams = [ "0.0.0.0:4953" ]; + socketConfig = { + Accept = true; + Backlog = 0; + MaxConnections = 1; + }; + }; + + systemd.services."kattv@" = { + environment = pkgs.kat-tv-ingest.env; + script = "exec ${pkgs.gst_all_1.gstreamer.dev}/bin/gst-launch-1.0 -e --no-position ${pkgs.lib.gst.pipelineShellString pkgs.kat-tv-ingest.pipeline}"; + wantedBy = [ "multi-user.target" ]; + after = [ "nginx.service" ]; + description = "RTMP stream of kat cam"; + serviceConfig = { + Restart = "on-failure"; + RestartSec = "10s"; + }; + }; +} diff --git a/config/services/kattv/default.nix b/config/services/kattv/default.nix new file mode 100644 index 00000000..b58928b7 --- /dev/null +++ b/config/services/kattv/default.nix @@ -0,0 +1,21 @@ +{ config, pkgs, lib, ... }: + +{ + + services.udev.extraRules = '' + KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ATTR{index}=="0", ATTRS{idVendor}=="045e", ATTRS{idProduct}=="0779", SYMLINK+="videomew", TAG+="systemd" + ''; + + systemd.services.kattv = { + wantedBy = [ "dev-videomew.device" "multi-user.target" ]; + after = [ "dev-videomew.device" "nginx.service" ]; + description = "RTMP stream of kat cam"; + bindsTo = [ "dev-videomew.device" ]; + environment = pkgs.kat-tv.env; + script = "exec ${pkgs.gst_all_1.gstreamer.dev}/bin/gst-launch-1.0 -e --no-position ${pkgs.lib.gst.pipelineShellString pkgs.kat-tv.pipeline}"; + serviceConfig = { + Restart = "on-failure"; + RestartSec = "10s"; + }; + }; +} diff --git a/pkgs/default.nix b/pkgs/default.nix index 1ec0bdda..8107199c 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -72,13 +72,17 @@ let konawall = super.konawall.overide { swaySupport = true; }; + wezterm-terminfo = self.callPackage ./wezterm-terminfo { inherit (self) ncurses; }; + imv = super.imv.override { withBackends = [ "freeimage" "libjpeg" "libpng" "librsvg" "libnsgif" "libheif" "libtiff" ]; }; kat-glauca-dns = self.callPackage ./kat-glauca-dns { }; - wezterm-terminfo = self.callPackage ./wezterm-terminfo { inherit (self) ncurses; }; + kat-tv = import ./kat-tv { pkgs = self; lib = self.lib; }; + + kat-tv-ingest = import ./kat-tv-ingest { pkgs = self; lib = self.lib; }; kat-website = self.callPackage ./kat-website { }; @@ -102,6 +106,9 @@ let ]; config = { allowUnfree = true; + permittedInsecurePackages = [ + "ffmpeg-3.4.8" + ]; }; }; in pkgs diff --git a/pkgs/kat-tv-ingest/default.nix b/pkgs/kat-tv-ingest/default.nix new file mode 100644 index 00000000..fa1cc026 --- /dev/null +++ b/pkgs/kat-tv-ingest/default.nix @@ -0,0 +1,178 @@ +{ pkgs ? import { }, lib }: with lib; with pkgs.gst_all_1; let + queue_frame = { + element."queue" = { + leaky = "downstream"; + flush-on-eos = true; + max-size-buffers = 3; + }; + }; + queue_data = { + element.queue = { + leaky = "downstream"; + }; + }; + videoconvert_cpu = { + element.videoconvert = { + n-threads = 4; + dither = 0; + chroma-resampler = 0; + chroma-mode = 3; + }; + }; + videoconvert_gpu = [ + # TODO: vulkancolorconvert? + "glupload" + "glcolorconvert" + "gldownload" + ]; + cameracapture = { + element."v4l2src" = { + device = "/dev/videomew"; + saturation = 100; + brightness = 100; + extra-controls = "c,exposure_auto=3"; + }; + }; + v4l2src = [ + cameracapture + { + caps."image/jpeg" = { + width = 1280; + height = 720; + framerate = "30/1"; # "10/1" + }; + } + "jpegdec" + queue_frame + ]; + filesrc = [ + { + element.filesrc = { + location = "rawvid.mkv"; + }; + } + "matroskademux" + ]; + denoise = { + element.frei0r-filter-hqdn3d = { + spatial = 0.175; #0.325; + temporal = 0.06; #0.11; + }; + }; + encodeopts = { + speed-preset = "ultrafast"; + tune = "zerolatency"; + pass = "qual"; + #psy-tune = "film"; + #noise-reduction=0; + quantizer = 27; + bitrate = 8192; + rc-lookahead = 6; + }; + encode_intra = [ + { + element.x264enc = { + intra-refresh = true; + key-int-max = 0; + } // encodeopts; + } + { + caps."video/x-h264" = { + profile = "high-4:2:2-intra"; + }; + } + "h264parse" + ]; + encode_high = [ + { + element.x264enc = { + key-int-max = 150; + } // encodeopts; + } + { + caps."video/x-h264" = { + profile = "high"; + }; + } + "h264parse" + ]; + rtmpsink = [ + queue_data + "flvmux" + { + element.rtmp2sink = { + location = "rtmp://localhost:1935/stream/kattv"; + }; + } + ]; + filesink = [ + "matroskamux" + { + element.filesink = { + location = "out.mkv"; + }; + } + ]; + videosink = "autovideosink"; + pipeline = [ + #filesrc + #v4l2src + { + element.fdsrc = { + fd = 3; + }; + } + "matroskademux" + "jpegdec" + queue_frame + + videoconvert_cpu + denoise + + videoconvert_cpu + encode_high + + rtmpsink + #filesink + ]; + #gst-launch-1.0 $camera ! $videoconvert ! $queue ! $denoise ! $encode ! $outfile + env = { + FREI0R_PATH = "${pkgs.frei0r}/lib/frei0r-1"; + GST_PLUGIN_SYSTEM_PATH_1_0 = with pkgs.gst_all_1; lib.makeSearchPath "lib/gstreamer-1.0" [ + gstreamer + gst-plugins-base + gst-plugins-good + gst-plugins-bad + gst-plugins-ugly + ]; + }; + shell = pkgs.mkShell (env // { + nativeBuildInputs = [ gstreamer kattv ]; + }); +in +{ + inherit shell pipeline env; +} +#!/usr/bin/env bash +/* + + + + + #encode='x264enc intra-refresh=true key-int-max=0 speed-preset=superfast tune=zerolatency pass=qual psy-tune=film noise-reduction=250 quantizer=23 rc-lookahead=15 ! video/x-h264,profile=high-4:2:2-intra ! h264parse' + #encode="x264enc intra-refresh=true key-int-max=0 $encodeopts ! video/x-h264,profile=high-4:2:2-intra ! h264parse ! $bufqueue" + encode="x264enc key-int-max=50 $encodeopts ! video/x-h264,profile=high-4:2:2 ! h264parse ! $bufqueue" + + # original pipeline: + # gst-launch-1.0 v4l2src device=/dev/video0 ! "video/x-raw,format=YUY2,width=1280,height=800,framerate=10/1" ! videoconvert ! x264enc intra-refresh=true key-int-max=0 speed-preset=superfast tune=zerolatency pass=qual psy-tune=film noise-reduction=250 quantizer=23 rc-lookahead=15 ! video/x-h264,profile=high-4:2:2-intra ! h264parse ! flvmux ! rtmpsink location="rtmp://kittywit.ch/kattv/meowlymeowl live=1" + + + #gst-launch-1.0 filesrc location=rawvid.mkv ! mkvdemux ! "video/x-raw,format=YUY2,width=1280,height=800,framerate=10/1" ! videoconvert ! x264enc intra-refresh=true key-int-max=0 speed-preset=superfast tune=zerolatency pass=qual psy-tune=film noise-reduction=250 quantizer=23 rc-lookahead=15 ! video/x-h264,profile=high-4:2:2-intra ! h264parse ! flvmux ! filesink location=out.flv + + #gst-launch-1.0 filesrc location=rawvid.mkv ! matroskademux ! "video/x-raw,format=YUY2,width=1280,height=800,framerate=10/1" ! videoconvert ! "video/x-raw,format=RGBA" ! frei0r-filter-hqdn3d spatial=0.35 temporal=0.115 ! videoconvert ! autovideosink + + outfile='flvmux ! filesink location=out.flv' + #gst-launch-1.0 $camera ! $videoconvert ! $denoise ! autovideosink + gst-launch-1.0 $camera ! $videoconvert ! $queue ! $denoise ! $encode ! $outfile + #gst-launch-1.0 $camera ! $videoconvert ! $encode ! $outfile +*/ diff --git a/pkgs/kat-tv/default.nix b/pkgs/kat-tv/default.nix new file mode 100644 index 00000000..f2facd4a --- /dev/null +++ b/pkgs/kat-tv/default.nix @@ -0,0 +1,166 @@ +{ pkgs ? import { }, lib }: with lib; with pkgs.gst_all_1; let + queue_frame = { + element."queue" = { + leaky = "downstream"; + flush-on-eos = true; + max-size-buffers = 3; + }; + }; + queue_data = { + element.queue = { + leaky = "downstream"; + }; + }; + videoconvert_cpu = { + element.videoconvert = { + n-threads = 4; + dither = 0; + chroma-resampler = 0; + chroma-mode = 3; + }; + }; + videoconvert_gpu = [ + # TODO: vulkancolorconvert? + "glupload" + "glcolorconvert" + "gldownload" + ]; + cameracapture = { + element."v4l2src" = { + device = "/dev/videomew"; + saturation = 100; + brightness = 100; + extra-controls = "c,exposure_auto=3"; + }; + }; + v4l2src = [ + cameracapture + { + caps."image/jpeg" = { + width = 1280; + height = 720; + framerate = "30/1"; # "10/1" + }; + } + ]; + filesrc = [ + { + element.filesrc = { + location = "rawvid.mkv"; + }; + } + "matroskademux" + ]; + denoise = { + element.frei0r-filter-hqdn3d = { + spatial = 0.175; #0.325; + temporal = 0.06; #0.11; + }; + }; + encodeopts = { + speed-preset = "ultrafast"; + tune = "zerolatency"; + pass = "qual"; + #psy-tune = "film"; + #noise-reduction=0; + quantizer = 27; + bitrate = 8192; + rc-lookahead = 6; + }; + encode_intra = [ + { + element.x264enc = { + intra-refresh = true; + key-int-max = 0; + } // encodeopts; + } + { + caps."video/x-h264" = { + profile = "high-4:2:2-intra"; + }; + } + "h264parse" + ]; + encode_high = [ + { + element.x264enc = { + key-int-max = 150; + } // encodeopts; + } + { + caps."video/x-h264" = { + profile = "high"; + }; + } + "h264parse" + ]; + rtmpsink = [ + queue_data + "flvmux" + { + element.rtmp2sink = { + location = "rtmp://192.168.1.223:1935/stream/kattv"; + }; + } + ]; + filesink = [ + "matroskamux" + { + element.filesink = { + location = "out.mkv"; + }; + } + ]; + videosink = "autovideosink"; + pipeline = v4l2src ++ [ + #filesrc + { element.matroskamux.streamable = true; } + { + element.tcpclientsink = { + host = "192.168.1.223"; + port = "4953"; + sync = false; + }; + } + ]; + #gst-launch-1.0 $camera ! $videoconvert ! $queue ! $denoise ! $encode ! $outfile + env = { + FREI0R_PATH = "${pkgs.frei0r}/lib/frei0r-1"; + GST_PLUGIN_SYSTEM_PATH_1_0 = with pkgs.gst_all_1; lib.makeSearchPath "lib/gstreamer-1.0" [ + gstreamer + gst-plugins-base + gst-plugins-good + gst-plugins-bad + gst-plugins-ugly + ]; + }; + shell = pkgs.mkShell (env // { + nativeBuildInputs = [ gstreamer kattv ]; + }); +in +{ + inherit shell pipeline env; +} +#!/usr/bin/env bash +/* + + + + + #encode='x264enc intra-refresh=true key-int-max=0 speed-preset=superfast tune=zerolatency pass=qual psy-tune=film noise-reduction=250 quantizer=23 rc-lookahead=15 ! video/x-h264,profile=high-4:2:2-intra ! h264parse' + #encode="x264enc intra-refresh=true key-int-max=0 $encodeopts ! video/x-h264,profile=high-4:2:2-intra ! h264parse ! $bufqueue" + encode="x264enc key-int-max=50 $encodeopts ! video/x-h264,profile=high-4:2:2 ! h264parse ! $bufqueue" + + # original pipeline: + # gst-launch-1.0 v4l2src device=/dev/video0 ! "video/x-raw,format=YUY2,width=1280,height=800,framerate=10/1" ! videoconvert ! x264enc intra-refresh=true key-int-max=0 speed-preset=superfast tune=zerolatency pass=qual psy-tune=film noise-reduction=250 quantizer=23 rc-lookahead=15 ! video/x-h264,profile=high-4:2:2-intra ! h264parse ! flvmux ! rtmpsink location="rtmp://kittywit.ch/kattv/meowlymeowl live=1" + + + #gst-launch-1.0 filesrc location=rawvid.mkv ! mkvdemux ! "video/x-raw,format=YUY2,width=1280,height=800,framerate=10/1" ! videoconvert ! x264enc intra-refresh=true key-int-max=0 speed-preset=superfast tune=zerolatency pass=qual psy-tune=film noise-reduction=250 quantizer=23 rc-lookahead=15 ! video/x-h264,profile=high-4:2:2-intra ! h264parse ! flvmux ! filesink location=out.flv + + #gst-launch-1.0 filesrc location=rawvid.mkv ! matroskademux ! "video/x-raw,format=YUY2,width=1280,height=800,framerate=10/1" ! videoconvert ! "video/x-raw,format=RGBA" ! frei0r-filter-hqdn3d spatial=0.35 temporal=0.115 ! videoconvert ! autovideosink + + outfile='flvmux ! filesink location=out.flv' + #gst-launch-1.0 $camera ! $videoconvert ! $denoise ! autovideosink + gst-launch-1.0 $camera ! $videoconvert ! $queue ! $denoise ! $encode ! $outfile + #gst-launch-1.0 $camera ! $videoconvert ! $encode ! $outfile +*/