Merge pull request #299299 from eclairevoyant/pipewire-docs

nixos/{pipewire,wireplumber}: document examples for `*.configPackages` and cleanup
This commit is contained in:
K900 2024-03-27 08:43:44 +03:00 committed by GitHub
commit 1b08f32c98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 143 additions and 90 deletions

View file

@ -32,6 +32,11 @@ In addition to numerous new and upgraded packages, this release has the followin
- Julia environments can now be built with arbitrary packages from the ecosystem using the `.withPackages` function. For example: `julia.withPackages ["Plots"]`.
- The PipeWire and WirePlumber modules have removed support for using
`environment.etc."pipewire/..."` and `environment.etc."wireplumber/..."`.
Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for PipeWire and
`services.pipewire.wireplumber.configPackages` for WirePlumber instead."
- A new option `systemd.sysusers.enable` was added. If enabled, users and
groups are created with systemd-sysusers instead of with a custom perl script.

View file

@ -1,14 +1,21 @@
# PipeWire service.
{ config, lib, pkgs, ... }:
with lib;
let
inherit (builtins) attrNames concatMap length;
inherit (lib) maintainers teams;
inherit (lib.attrsets) attrByPath attrsToList concatMapAttrs filterAttrs;
inherit (lib.lists) flatten optional optionals;
inherit (lib.modules) mkIf mkRemovedOptionModule;
inherit (lib.options) literalExpression mkEnableOption mkOption mkPackageOption;
inherit (lib.strings) concatMapStringsSep hasPrefix optionalString;
inherit (lib.types) attrsOf bool listOf package;
json = pkgs.formats.json {};
mapToFiles = location: config: concatMapAttrs (name: value: { "share/pipewire/${location}.conf.d/${name}.conf" = json.generate "${name}" value; }) config;
extraConfigPkgFromFiles = locations: filesSet: pkgs.runCommand "pipewire-extra-config" { } ''
mkdir -p ${lib.concatMapStringsSep " " (l: "$out/share/pipewire/${l}.conf.d") locations}
${lib.concatMapStringsSep ";" ({name, value}: "ln -s ${value} $out/${name}") (lib.attrsToList filesSet)}
mkdir -p ${concatMapStringsSep " " (l: "$out/share/pipewire/${l}.conf.d") locations}
${concatMapStringsSep ";" ({name, value}: "ln -s ${value} $out/${name}") (attrsToList filesSet)}
'';
cfg = config.services.pipewire;
enable32BitAlsaPlugins = cfg.alsa.support32Bit
@ -40,15 +47,15 @@ let
name = "pipewire-configs";
paths = configPackages
++ [ extraConfigPkg ]
++ lib.optionals cfg.wireplumber.enable cfg.wireplumber.configPackages;
++ optionals cfg.wireplumber.enable cfg.wireplumber.configPackages;
pathsToLink = [ "/share/pipewire" ];
};
requiredLv2Packages = lib.flatten
requiredLv2Packages = flatten
(
lib.concatMap
concatMap
(p:
lib.attrByPath ["passthru" "requiredLv2Packages"] [] p
attrByPath ["passthru" "requiredLv2Packages"] [] p
)
configPackages
);
@ -59,58 +66,58 @@ let
pathsToLink = [ "/lib/lv2" ];
};
in {
meta.maintainers = teams.freedesktop.members ++ [ lib.maintainers.k900 ];
meta.maintainers = teams.freedesktop.members ++ [ maintainers.k900 ];
###### interface
options = {
services.pipewire = {
enable = mkEnableOption (lib.mdDoc "PipeWire service");
enable = mkEnableOption "PipeWire service";
package = mkPackageOption pkgs "pipewire" { };
socketActivation = mkOption {
default = true;
type = types.bool;
description = lib.mdDoc ''
type = bool;
description = ''
Automatically run PipeWire when connections are made to the PipeWire socket.
'';
};
audio = {
enable = lib.mkOption {
type = lib.types.bool;
enable = mkOption {
type = bool;
# this is for backwards compatibility
default = cfg.alsa.enable || cfg.jack.enable || cfg.pulse.enable;
defaultText = lib.literalExpression "config.services.pipewire.alsa.enable || config.services.pipewire.jack.enable || config.services.pipewire.pulse.enable";
description = lib.mdDoc "Whether to use PipeWire as the primary sound server";
defaultText = literalExpression "config.services.pipewire.alsa.enable || config.services.pipewire.jack.enable || config.services.pipewire.pulse.enable";
description = "Whether to use PipeWire as the primary sound server";
};
};
alsa = {
enable = mkEnableOption (lib.mdDoc "ALSA support");
support32Bit = mkEnableOption (lib.mdDoc "32-bit ALSA support on 64-bit systems");
enable = mkEnableOption "ALSA support";
support32Bit = mkEnableOption "32-bit ALSA support on 64-bit systems";
};
jack = {
enable = mkEnableOption (lib.mdDoc "JACK audio emulation");
enable = mkEnableOption "JACK audio emulation";
};
raopOpenFirewall = mkOption {
type = lib.types.bool;
type = bool;
default = false;
description = lib.mdDoc ''
description = ''
Opens UDP/6001-6002, required by RAOP/Airplay for timing and control data.
'';
};
pulse = {
enable = mkEnableOption (lib.mdDoc "PulseAudio server emulation");
enable = mkEnableOption "PulseAudio server emulation";
};
systemWide = lib.mkOption {
type = lib.types.bool;
systemWide = mkOption {
type = bool;
default = false;
description = lib.mdDoc ''
description = ''
If true, a system-wide PipeWire service and socket is enabled
allowing all users in the "pipewire" group to use it simultaneously.
If false, then user units are used instead, restricting access to
@ -124,7 +131,7 @@ in {
extraConfig = {
pipewire = mkOption {
type = lib.types.attrsOf json.type;
type = attrsOf json.type;
default = {};
example = {
"10-clock-rate" = {
@ -138,7 +145,7 @@ in {
};
};
};
description = lib.mdDoc ''
description = ''
Additional configuration for the PipeWire server.
Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/pipewire.conf.d`.
@ -157,7 +164,7 @@ in {
'';
};
client = mkOption {
type = lib.types.attrsOf json.type;
type = attrsOf json.type;
default = {};
example = {
"10-no-resample" = {
@ -166,7 +173,7 @@ in {
};
};
};
description = lib.mdDoc ''
description = ''
Additional configuration for the PipeWire client library, used by most applications.
Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/client.conf.d`.
@ -177,7 +184,7 @@ in {
'';
};
client-rt = mkOption {
type = lib.types.attrsOf json.type;
type = attrsOf json.type;
default = {};
example = {
"10-alsa-linear-volume" = {
@ -186,7 +193,7 @@ in {
};
};
};
description = lib.mdDoc ''
description = ''
Additional configuration for the PipeWire client library, used by real-time applications and legacy ALSA clients.
Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/client-rt.conf.d`.
@ -198,7 +205,7 @@ in {
'';
};
jack = mkOption {
type = lib.types.attrsOf json.type;
type = attrsOf json.type;
default = {};
example = {
"20-hide-midi" = {
@ -207,7 +214,7 @@ in {
};
};
};
description = lib.mdDoc ''
description = ''
Additional configuration for the PipeWire JACK server and client library.
Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/jack.conf.d`.
@ -218,7 +225,7 @@ in {
'';
};
pipewire-pulse = mkOption {
type = lib.types.attrsOf json.type;
type = attrsOf json.type;
default = {};
example = {
"15-force-s16-info" = {
@ -232,7 +239,7 @@ in {
}];
};
};
description = lib.mdDoc ''
description = ''
Additional configuration for the PipeWire PulseAudio server.
Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/pipewire-pulse.conf.d`.
@ -248,10 +255,32 @@ in {
};
};
configPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
configPackages = mkOption {
type = listOf package;
default = [];
description = lib.mdDoc ''
example = literalExpression ''[
(pkgs.writeTextDir "share/pipewire/pipewire.conf.d/10-loopback.conf" '''
context.modules = [
{ name = libpipewire-module-loopback
args = {
node.description = "Scarlett Focusrite Line 1"
capture.props = {
audio.position = [ FL ]
stream.dont-remix = true
node.target = "alsa_input.usb-Focusrite_Scarlett_Solo_USB_Y7ZD17C24495BC-00.analog-stereo"
node.passive = true
}
playback.props = {
node.name = "SF_mono_in_1"
media.class = "Audio/Source"
audio.position = [ MONO ]
}
}
}
]
''')
]'';
description = ''
List of packages that provide PipeWire configuration, in the form of
`share/pipewire/*/*.conf` files.
@ -260,11 +289,11 @@ in {
'';
};
extraLv2Packages = lib.mkOption {
type = lib.types.listOf lib.types.package;
extraLv2Packages = mkOption {
type = listOf package;
default = [];
example = lib.literalExpression "[ pkgs.lsp-plugins ]";
description = lib.mdDoc ''
example = literalExpression "[ pkgs.lsp-plugins ]";
description = ''
List of packages that provide LV2 plugins in `lib/lv2` that should
be made available to PipeWire for [filter chains][wiki-filter-chain].
@ -279,11 +308,11 @@ in {
};
imports = [
(lib.mkRemovedOptionModule ["services" "pipewire" "config"] ''
(mkRemovedOptionModule ["services" "pipewire" "config"] ''
Overriding default PipeWire configuration through NixOS options never worked correctly and is no longer supported.
Please create drop-in configuration files via `services.pipewire.extraConfig` instead.
'')
(lib.mkRemovedOptionModule ["services" "pipewire" "media-session"] ''
(mkRemovedOptionModule ["services" "pipewire" "media-session"] ''
pipewire-media-session is no longer supported upstream and has been removed.
Please switch to `services.pipewire.wireplumber` instead.
'')
@ -306,12 +335,12 @@ in {
message = "Using PipeWire's ALSA/PulseAudio compatibility layers requires running PipeWire as the sound server. Set `services.pipewire.audio.enable` to true.";
}
{
assertion = builtins.length
(builtins.attrNames
assertion = length
(attrNames
(
lib.filterAttrs
filterAttrs
(name: value:
lib.hasPrefix "pipewire/" name || name == "pipewire"
hasPrefix "pipewire/" name || name == "pipewire"
)
config.environment.etc
)) == 1;
@ -320,7 +349,7 @@ in {
];
environment.systemPackages = [ cfg.package ]
++ lib.optional cfg.jack.enable jack-libs;
++ optional cfg.jack.enable jack-libs;
systemd.packages = [ cfg.package ];
@ -336,16 +365,16 @@ in {
systemd.user.sockets.pipewire.enable = !cfg.systemWide;
systemd.user.services.pipewire.enable = !cfg.systemWide;
systemd.services.pipewire.environment.LV2_PATH = lib.mkIf cfg.systemWide "${lv2Plugins}/lib/lv2";
systemd.user.services.pipewire.environment.LV2_PATH = lib.mkIf (!cfg.systemWide) "${lv2Plugins}/lib/lv2";
systemd.services.pipewire.environment.LV2_PATH = mkIf cfg.systemWide "${lv2Plugins}/lib/lv2";
systemd.user.services.pipewire.environment.LV2_PATH = mkIf (!cfg.systemWide) "${lv2Plugins}/lib/lv2";
# Mask pw-pulse if it's not wanted
systemd.user.services.pipewire-pulse.enable = cfg.pulse.enable;
systemd.user.sockets.pipewire-pulse.enable = cfg.pulse.enable;
systemd.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
systemd.user.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
systemd.user.sockets.pipewire-pulse.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
systemd.sockets.pipewire.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
systemd.user.sockets.pipewire.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
systemd.user.sockets.pipewire-pulse.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
services.udev.packages = [ cfg.package ];
@ -377,18 +406,18 @@ in {
};
environment.sessionVariables.LD_LIBRARY_PATH =
lib.mkIf cfg.jack.enable [ "${cfg.package.jack}/lib" ];
mkIf cfg.jack.enable [ "${cfg.package.jack}/lib" ];
networking.firewall.allowedUDPPorts = lib.mkIf cfg.raopOpenFirewall [ 6001 6002 ];
networking.firewall.allowedUDPPorts = mkIf cfg.raopOpenFirewall [ 6001 6002 ];
users = lib.mkIf cfg.systemWide {
users = mkIf cfg.systemWide {
users.pipewire = {
uid = config.ids.uids.pipewire;
group = "pipewire";
extraGroups = [
"audio"
"video"
] ++ lib.optional config.security.rtkit.enable "rtkit";
] ++ optional config.security.rtkit.enable "rtkit";
description = "PipeWire system service user";
isSystemUser = true;
home = "/var/lib/pipewire";

View file

@ -1,46 +1,65 @@
{ config, lib, pkgs, ... }:
let
inherit (builtins) attrNames concatMap length;
inherit (lib) maintainers;
inherit (lib.attrsets) attrByPath filterAttrs;
inherit (lib.lists) flatten optional;
inherit (lib.modules) mkIf;
inherit (lib.options) literalExpression mkOption;
inherit (lib.strings) hasPrefix;
inherit (lib.types) bool listOf package;
pwCfg = config.services.pipewire;
cfg = pwCfg.wireplumber;
pwUsedForAudio = pwCfg.audio.enable;
in
{
meta.maintainers = [ lib.maintainers.k900 ];
meta.maintainers = [ maintainers.k900 ];
options = {
services.pipewire.wireplumber = {
enable = lib.mkOption {
type = lib.types.bool;
default = config.services.pipewire.enable;
defaultText = lib.literalExpression "config.services.pipewire.enable";
description = lib.mdDoc "Whether to enable WirePlumber, a modular session / policy manager for PipeWire";
enable = mkOption {
type = bool;
default = pwCfg.enable;
defaultText = literalExpression "config.services.pipewire.enable";
description = "Whether to enable WirePlumber, a modular session / policy manager for PipeWire";
};
package = lib.mkOption {
type = lib.types.package;
package = mkOption {
type = package;
default = pkgs.wireplumber;
defaultText = lib.literalExpression "pkgs.wireplumber";
description = lib.mdDoc "The WirePlumber derivation to use.";
defaultText = literalExpression "pkgs.wireplumber";
description = "The WirePlumber derivation to use.";
};
configPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
configPackages = mkOption {
type = listOf package;
default = [ ];
description = lib.mdDoc ''
example = literalExpression ''[
(pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/10-bluez.conf" '''
monitor.bluez.properties = {
bluez5.roles = [ a2dp_sink a2dp_source bap_sink bap_source hsp_hs hsp_ag hfp_hf hfp_ag ]
bluez5.codecs = [ sbc sbc_xq aac ]
bluez5.enable-sbc-xq = true
bluez5.hfphsp-backend = "native"
}
''')
]'';
description = ''
List of packages that provide WirePlumber configuration, in the form of
`share/wireplumber/*/*.lua` files.
`share/wireplumber/*/*.conf` files.
LV2 dependencies will be picked up from config packages automatically
via `passthru.requiredLv2Packages`.
'';
};
extraLv2Packages = lib.mkOption {
type = lib.types.listOf lib.types.package;
extraLv2Packages = mkOption {
type = listOf package;
default = [];
example = lib.literalExpression "[ pkgs.lsp-plugins ]";
description = lib.mdDoc ''
example = literalExpression "[ pkgs.lsp-plugins ]";
description = ''
List of packages that provide LV2 plugins in `lib/lv2` that should
be made available to WirePlumber for [filter chains][wiki-filter-chain].
@ -78,8 +97,8 @@ in
'';
configPackages = cfg.configPackages
++ lib.optional (!pwUsedForAudio) pwNotForAudioConfigPkg
++ lib.optional config.services.pipewire.systemWide systemwideConfigPkg;
++ optional (!pwUsedForAudio) pwNotForAudioConfigPkg
++ optional pwCfg.systemWide systemwideConfigPkg;
configs = pkgs.buildEnv {
name = "wireplumber-configs";
@ -87,11 +106,11 @@ in
pathsToLink = [ "/share/wireplumber" ];
};
requiredLv2Packages = lib.flatten
requiredLv2Packages = flatten
(
lib.concatMap
concatMap
(p:
lib.attrByPath ["passthru" "requiredLv2Packages"] [] p
attrByPath ["passthru" "requiredLv2Packages"] [] p
)
configPackages
);
@ -102,19 +121,19 @@ in
pathsToLink = [ "/lib/lv2" ];
};
in
lib.mkIf cfg.enable {
mkIf cfg.enable {
assertions = [
{
assertion = !config.hardware.bluetooth.hsphfpd.enable;
message = "Using WirePlumber conflicts with hsphfpd, as it provides the same functionality. `hardware.bluetooth.hsphfpd.enable` needs be set to false";
}
{
assertion = builtins.length
(builtins.attrNames
assertion = length
(attrNames
(
lib.filterAttrs
filterAttrs
(name: value:
lib.hasPrefix "wireplumber/" name || name == "wireplumber"
hasPrefix "wireplumber/" name || name == "wireplumber"
)
config.environment.etc
)) == 1;
@ -128,19 +147,19 @@ in
systemd.packages = [ cfg.package ];
systemd.services.wireplumber.enable = config.services.pipewire.systemWide;
systemd.user.services.wireplumber.enable = !config.services.pipewire.systemWide;
systemd.services.wireplumber.enable = pwCfg.systemWide;
systemd.user.services.wireplumber.enable = !pwCfg.systemWide;
systemd.services.wireplumber.wantedBy = [ "pipewire.service" ];
systemd.user.services.wireplumber.wantedBy = [ "pipewire.service" ];
systemd.services.wireplumber.environment = lib.mkIf config.services.pipewire.systemWide {
systemd.services.wireplumber.environment = mkIf pwCfg.systemWide {
# Force WirePlumber to use system dbus.
DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/dbus/system_bus_socket";
LV2_PATH = "${lv2Plugins}/lib/lv2";
};
systemd.user.services.wireplumber.environment.LV2_PATH =
lib.mkIf (!config.services.pipewire.systemWide) "${lv2Plugins}/lib/lv2";
mkIf (!pwCfg.systemWide) "${lv2Plugins}/lib/lv2";
};
}