PipeWire: Difference between revisions

imported>Iopq
more details on how to increase log level
imported>Fufexan
Update Advanced & Low-latency sections to newer config & WirePlumber
Line 1: Line 1:
PipeWire is a new low-level multimedia framework. It aims to offer capture and playback for both audio and video with minimal latency and support for PulseAudio-, JACK-, ALSA- and GStreamer-based applications. PipeWire has a great bluetooth support: because Pulseaudio was [https://github.com/NixOS/nixpkgs/issues/123784 reported to have troubles with bluetooth], PipeWire can be a good alternative.  
PipeWire is a new low-level multimedia framework. It aims to offer capture and playback for both audio and video with minimal latency and support for PulseAudio-, JACK-, ALSA- and GStreamer-based applications. PipeWire has a great bluetooth support: because Pulseaudio was [https://github.com/NixOS/nixpkgs/issues/123784 reported to have troubles with bluetooth], PipeWire can be a good alternative.  


Line 103: Line 104:


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.pipewire = {
environment.etc = let
   config.pipewire = {
  json = pkgs.formats.json {};
     "context.objects" = [
in {
   "pipewire/pipewire.d/91-null-sinks.conf".source = json.generate "91-null-sinks.conf" {
     context.objects = [
       {
       {
         # A default dummy driver. This handles nodes marked with the "node.always-driver"
         # A default dummy driver. This handles nodes marked with the "node.always-driver"
Line 111: Line 114:
         factory = "spa-node-factory";
         factory = "spa-node-factory";
         args = {
         args = {
           "factory.name"     = "support.node.driver";
           factory.name    = "support.node.driver";
           "node.name"       = "Dummy-Driver";
           node.name        = "Dummy-Driver";
           "priority.driver" = 8000;
           priority.driver  = 8000;
         };
         };
       }
       }
Line 119: Line 122:
         factory = "adapter";
         factory = "adapter";
         args = {
         args = {
           "factory.name"     = "support.null-audio-sink";
           factory.name    = "support.null-audio-sink";
           "node.name"       = "Microphone-Proxy";
           node.name        = "Microphone-Proxy";
           "node.description" = "Microphone";
           node.description = "Microphone";
           "media.class"     = "Audio/Source/Virtual";
           media.class      = "Audio/Source/Virtual";
           "audio.position"   = "MONO";
           audio.position  = "MONO";
         };
         };
       }
       }
Line 129: Line 132:
         factory = "adapter";
         factory = "adapter";
         args = {
         args = {
           "factory.name"     = "support.null-audio-sink";
           factory.name    = "support.null-audio-sink";
           "node.name"       = "Main-Output-Proxy";
           node.name        = "Main-Output-Proxy";
           "node.description" = "Main Output";
           node.description = "Main Output";
           "media.class"     = "Audio/Sink";
           media.class      = "Audio/Sink";
           "audio.position"   = "FL,FR";
           audio.position  = "FL,FR";
         };
         };
       }
       }
Line 160: Line 163:
The minimum period size controls how small a buffer can be. The lower it is, the less latency there is. PipeWire has a value of 32/48000 by default, which amounts to 0.667ms. It can be brought lower if needed:
The minimum period size controls how small a buffer can be. The lower it is, the less latency there is. PipeWire has a value of 32/48000 by default, which amounts to 0.667ms. It can be brought lower if needed:
<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.pipewire = {
environment.etc = let
   config.pipewire = {
   json = pkgs.generators.json {};
    "context.properties" = {
in {
      "link.max-buffers" = 16;
  "pipewire/pipewire.d/92-low-latency.conf".source = json.generate "92-low-latency.conf" {
      "log.level" = 2;
    context.properties = {
       "default.clock.rate" = 48000;
       default.clock.rate = 48000;
       "default.clock.quantum" = 32;
       default.clock.quantum = 32;
       "default.clock.min-quantum" = 32;
       default.clock.min-quantum = 32;
       "default.clock.max-quantum" = 32;
       default.clock.max-quantum = 32;
      "core.daemon" = true;
      "core.name" = "pipewire-0";
     };
     };
    "context.modules" = [
      {
        name = "libpipewire-module-rtkit";
        args = {
          "nice.level" = -15;
          "rt.prio" = 88;
          "rt.time.soft" = 200000;
          "rt.time.hard" = 200000;
        };
        flags = [ "ifexists" "nofail" ];
      }
      { name = "libpipewire-module-protocol-native"; }
      { name = "libpipewire-module-profiler"; }
      { name = "libpipewire-module-metadata"; }
      { name = "libpipewire-module-spa-device-factory"; }
      { name = "libpipewire-module-spa-node-factory"; }
      { name = "libpipewire-module-client-node"; }
      { name = "libpipewire-module-client-device"; }
      {
        name = "libpipewire-module-portal";
        flags = [ "ifexists" "nofail" ];
      }
      {
        name = "libpipewire-module-access";
        args = {};
      }
      { name = "libpipewire-module-adapter"; }
      { name = "libpipewire-module-link-factory"; }
      { name = "libpipewire-module-session-manager"; }
    ];
   };
   };
};
};
Line 211: Line 182:
Applications using the Pulse backend have a separate configuration. The default minimum value is 1024, so it needs to be tweaked if low-latency audio is desired.
Applications using the Pulse backend have a separate configuration. The default minimum value is 1024, so it needs to be tweaked if low-latency audio is desired.
<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.pipewire = {
environment.etc = let
   config.pipewire-pulse = {
   json = pkgs.generators.json {};
    "context.properties" = {
in {
      "log.level" = 2;
  "pipewire/pipewire-pulse.d/92-low-latency.conf".source = json.generate "92-low-latency.conf" {
    };
    context.modules = [
    "context.modules" = [
      {
        name = "libpipewire-module-rtkit";
        args = {
          "nice.level" = -15;
          "rt.prio" = 88;
          "rt.time.soft" = 200000;
          "rt.time.hard" = 200000;
        };
        flags = [ "ifexists" "nofail" ];
      }
      { name = "libpipewire-module-protocol-native"; }
      { name = "libpipewire-module-client-node"; }
      { name = "libpipewire-module-adapter"; }
      { name = "libpipewire-module-metadata"; }
       {
       {
         name = "libpipewire-module-protocol-pulse";
         name = "libpipewire-module-protocol-pulse";
         args = {
         args = {
           "pulse.min.req" = "32/48000";
           pulse.min.req = "32/48000";
           "pulse.default.req" = "32/48000";
           pulse.default.req = "32/48000";
           "pulse.max.req" = "32/48000";
           pulse.max.req = "32/48000";
           "pulse.min.quantum" = "32/48000";
           pulse.min.quantum = "32/48000";
           "pulse.max.quantum" = "32/48000";
           pulse.max.quantum = "32/48000";
          "server.address" = [ "unix:native" ];
         };
         };
       }
       }
     ];
     ];
     "stream.properties" = {
     stream.properties = {
       "node.latency" = "32/48000";
       node.latency = "32/48000";
       "resample.quality" = 1;
       resample.quality = 1;
     };
     };
   };
   };
Line 252: Line 207:
As a general rule, the values in <code>pipewire-pulse</code> should not be lower than the ones in <code>pipewire</code>.
As a general rule, the values in <code>pipewire-pulse</code> should not be lower than the ones in <code>pipewire</code>.
===Controlling the ALSA devices===
===Controlling the ALSA devices===
{{outdated|Pipewire now uses Wireplumber by default, instead of media-session}}
It is possible to configure various aspects of soundcards through PipeWire, including format, period size and batch mode:
It is possible to configure various aspects of soundcards through PipeWire, including format, period size and batch mode:
<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.pipewire = {
environment.etc.
  media-session.config.alsa-monitor = {
"wireplumber/main.lua.d/99-alsa-lowlatency.lua".text = ''
    rules = [
  alsa_monitor.rules = {
      {
    {
        matches = [ { "node.name" = "alsa_output.*"; } ];
      matches = {{{ "node.name", "matches", "alsa_output.*" }}};
        actions = {
      apply_properties = {
          update-props = {
        ["audio.format"] = "S32LE",
            "audio.format" = "S32LE";
        ["audio.rate"] = "96000", -- for USB soundcards it should be twice your desired rate
            "audio.rate" = 96000; # for USB soundcards it should be twice your desired rate
        ["api.alsa.period-size"] = 2, -- defaults to 1024, tweak by trial-and-error
            "api.alsa.period-size" = 32; # defaults to 1024, tweak by trial-and-error
        -- ["api.alsa.disable-batch"] = true, -- generally, USB soundcards use the batch mode
            #"api.alsa.disable-batch" = true; # generally, USB soundcards use the batch mode
       },
          };
     },
        };
   }
       }
'';</syntaxHighlight>
     ];
The <code>matches</code> attribute applies the <code>actions</code> to the devices/properties listed there. It is usually used with soundcard names, like shown in the config above. <code><matches></code> can match any of the outputs of
   };
};
</syntaxHighlight>
The <code>matches</code> attribute applies the <code>actions</code> to the devices/properties listed there. It is usually used with soundcard names, like shown in the config above. <code><alsa_device></code> can be one of the outputs of
<syntaxHighlight lang="bash">
<syntaxHighlight lang="bash">
$ pw-dump | grep node.name | grep alsa
$ pw-dump | grep node.name | grep alsa