1# pipewire service.
2{ config, lib, pkgs, ... }:
3
4with lib;
5
6let
7 cfg = config.services.pipewire;
8 enable32BitAlsaPlugins = cfg.alsa.support32Bit
9 && pkgs.stdenv.isx86_64
10 && pkgs.pkgsi686Linux.pipewire != null;
11
12 # The package doesn't output to $out/lib/pipewire directly so that the
13 # overlays can use the outputs to replace the originals in FHS environments.
14 #
15 # This doesn't work in general because of missing development information.
16 jack-libs = pkgs.runCommand "jack-libs" {} ''
17 mkdir -p "$out/lib"
18 ln -s "${cfg.package.jack}/lib" "$out/lib/pipewire"
19 '';
20in {
21 meta.maintainers = teams.freedesktop.members ++ [ lib.maintainers.k900 ];
22
23 ###### interface
24 options = {
25 services.pipewire = {
26 enable = mkEnableOption (lib.mdDoc "pipewire service");
27
28 package = mkOption {
29 type = types.package;
30 default = pkgs.pipewire;
31 defaultText = literalExpression "pkgs.pipewire";
32 description = lib.mdDoc ''
33 The pipewire derivation to use.
34 '';
35 };
36
37 socketActivation = mkOption {
38 default = true;
39 type = types.bool;
40 description = lib.mdDoc ''
41 Automatically run pipewire when connections are made to the pipewire socket.
42 '';
43 };
44
45 audio = {
46 enable = lib.mkOption {
47 type = lib.types.bool;
48 # this is for backwards compatibility
49 default = cfg.alsa.enable || cfg.jack.enable || cfg.pulse.enable;
50 defaultText = lib.literalExpression "config.services.pipewire.alsa.enable || config.services.pipewire.jack.enable || config.services.pipewire.pulse.enable";
51 description = lib.mdDoc "Whether to use PipeWire as the primary sound server";
52 };
53 };
54
55 alsa = {
56 enable = mkEnableOption (lib.mdDoc "ALSA support");
57 support32Bit = mkEnableOption (lib.mdDoc "32-bit ALSA support on 64-bit systems");
58 };
59
60 jack = {
61 enable = mkEnableOption (lib.mdDoc "JACK audio emulation");
62 };
63
64 pulse = {
65 enable = mkEnableOption (lib.mdDoc "PulseAudio server emulation");
66 };
67
68 systemWide = lib.mkOption {
69 type = lib.types.bool;
70 default = false;
71 description = lib.mdDoc ''
72 If true, a system-wide PipeWire service and socket is enabled
73 allowing all users in the "pipewire" group to use it simultaneously.
74 If false, then user units are used instead, restricting access to
75 only one user.
76
77 Enabling system-wide PipeWire is however not recommended and disabled
78 by default according to
79 https://github.com/PipeWire/pipewire/blob/master/NEWS
80 '';
81 };
82 };
83 };
84
85 imports = [
86 (lib.mkRemovedOptionModule ["services" "pipewire" "config"] ''
87 Overriding default Pipewire configuration through NixOS options never worked correctly and is no longer supported.
88 Please create drop-in files in /etc/pipewire/pipewire.conf.d/ to make the desired setting changes instead.
89 '')
90
91 (lib.mkRemovedOptionModule ["services" "pipewire" "media-session"] ''
92 pipewire-media-session is no longer supported upstream and has been removed.
93 Please switch to `services.pipewire.wireplumber` instead.
94 '')
95 ];
96
97 ###### implementation
98 config = mkIf cfg.enable {
99 assertions = [
100 {
101 assertion = cfg.audio.enable -> !config.hardware.pulseaudio.enable;
102 message = "Using PipeWire as the sound server conflicts with PulseAudio. This option requires `hardware.pulseaudio.enable` to be set to false";
103 }
104 {
105 assertion = cfg.jack.enable -> !config.services.jack.jackd.enable;
106 message = "PipeWire based JACK emulation doesn't use the JACK service. This option requires `services.jack.jackd.enable` to be set to false";
107 }
108 {
109 # JACK intentionally not checked, as PW-on-JACK setups are a thing that some people may want
110 assertion = (cfg.alsa.enable || cfg.pulse.enable) -> cfg.audio.enable;
111 message = "Using PipeWire's ALSA/PulseAudio compatibility layers requires running PipeWire as the sound server. Set `services.pipewire.audio.enable` to true.";
112 }
113 ];
114
115 environment.systemPackages = [ cfg.package ]
116 ++ lib.optional cfg.jack.enable jack-libs;
117
118 systemd.packages = [ cfg.package ];
119
120 # PipeWire depends on DBUS but doesn't list it. Without this booting
121 # into a terminal results in the service crashing with an error.
122 systemd.services.pipewire.bindsTo = [ "dbus.service" ];
123 systemd.user.services.pipewire.bindsTo = [ "dbus.service" ];
124
125 # Enable either system or user units. Note that for pipewire-pulse there
126 # are only user units, which work in both cases.
127 systemd.sockets.pipewire.enable = cfg.systemWide;
128 systemd.services.pipewire.enable = cfg.systemWide;
129 systemd.user.sockets.pipewire.enable = !cfg.systemWide;
130 systemd.user.services.pipewire.enable = !cfg.systemWide;
131
132 # Mask pw-pulse if it's not wanted
133 systemd.user.services.pipewire-pulse.enable = cfg.pulse.enable;
134 systemd.user.sockets.pipewire-pulse.enable = cfg.pulse.enable;
135
136 systemd.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
137 systemd.user.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
138 systemd.user.sockets.pipewire-pulse.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
139
140 services.udev.packages = [ cfg.package ];
141
142 # If any paths are updated here they must also be updated in the package test.
143 environment.etc."alsa/conf.d/49-pipewire-modules.conf" = mkIf cfg.alsa.enable {
144 text = ''
145 pcm_type.pipewire {
146 libs.native = ${cfg.package}/lib/alsa-lib/libasound_module_pcm_pipewire.so ;
147 ${optionalString enable32BitAlsaPlugins
148 "libs.32Bit = ${pkgs.pkgsi686Linux.pipewire}/lib/alsa-lib/libasound_module_pcm_pipewire.so ;"}
149 }
150 ctl_type.pipewire {
151 libs.native = ${cfg.package}/lib/alsa-lib/libasound_module_ctl_pipewire.so ;
152 ${optionalString enable32BitAlsaPlugins
153 "libs.32Bit = ${pkgs.pkgsi686Linux.pipewire}/lib/alsa-lib/libasound_module_ctl_pipewire.so ;"}
154 }
155 '';
156 };
157 environment.etc."alsa/conf.d/50-pipewire.conf" = mkIf cfg.alsa.enable {
158 source = "${cfg.package}/share/alsa/alsa.conf.d/50-pipewire.conf";
159 };
160 environment.etc."alsa/conf.d/99-pipewire-default.conf" = mkIf cfg.alsa.enable {
161 source = "${cfg.package}/share/alsa/alsa.conf.d/99-pipewire-default.conf";
162 };
163
164 environment.sessionVariables.LD_LIBRARY_PATH =
165 lib.mkIf cfg.jack.enable [ "${cfg.package.jack}/lib" ];
166
167 users = lib.mkIf cfg.systemWide {
168 users.pipewire = {
169 uid = config.ids.uids.pipewire;
170 group = "pipewire";
171 extraGroups = [
172 "audio"
173 "video"
174 ] ++ lib.optional config.security.rtkit.enable "rtkit";
175 description = "Pipewire system service user";
176 isSystemUser = true;
177 home = "/var/lib/pipewire";
178 createHome = true;
179 };
180 groups.pipewire.gid = config.ids.gids.pipewire;
181 };
182 };
183}