|
PipeWire 1.7.0
|
Today’s Linux systems use ALSA (Advanced Linux Sound Architecture) to play and record sound and video. ALSA is built directly into the Linux Kernel including drivers for sound cards, and applications can use ALSA to play sound. However, each sound card can only be used by one application at a time – which is one reason why another layer like PipeWire is required: It mixes multiple audio streams together and sends that to ALSA directly. In addition, PipeWire also supports video and MIDI streams.
PipeWire has 3 core strengths that make it so popular today:
There are a number of popular sound servers for Linux like JACK for low latency or PulseAudio. PipeWire is the newest one, combining the advantages of its predecessors.
Applications are programmed to support a certain backend, or sometimes they support more than one. Even if they do not support PipeWire, they still work with PipeWire because it provides interfaces that look like e.g. ALSA or PulseAudio. Even configuration tools like pavucontrol, which configures audio devices like volume, profile (Stereo or 7.1 etc.), and so on, work for PipeWire!
Routing choices are made whenever you play back audio. (Same for recording and video/MIDI, but for simplicity we will focus on audio for now.)
When you play back an mp3 file with vlc, does it play on your laptop speakers or on your HDMI screen? What happens when you plug in your USB headphones, or connect your wireless earbuds? Every time a decision needs to be made where the audio stream goes after it leaves vlc. This is called routing, and it works a bit like connecting parts with cables, for example vlc and the earbuds.
PipeWire can do that, but it can do much more.
To illustrate routing, let’s start with a simple setup: VLC plays sound directly on the Jack output of your device.
Now we can place an equaliser in-between to lift some frequencies.
Actually, let’s lift the frequencies of the left speaker differently, and then add a reverb filter from LADSPA.
Or, we want to use the first 6 channels of an 8-channel interface as 5.1 and the remaining two channels as stereo output.
Most of these settings need configuration files, there is no GUI yet. To unlock the full power of PipeWire, you will have to dive into the deeper!
Normally, a system with PipeWire also runs WirePlumber.
While PipeWire provides the functionality for transporting and transforming audio and video, it does not actively react to events like connecting Bluetooth earbuds. This is the task of the session manager which uses PipeWire.
There is one PipeWire server which is used by a number of PipeWire clients (the processes that produce/consume multimedia). PipeWire, as well as WirePlumber, run in userspace, so interfacing with them with systemd (and journald etc.) happens in user context with the --user flag, for example systemctl --user status pipewire.service to check the service status or journalctl --user -fu wireplumber.service to see the live logs.
The session manager is normally WirePlumber – it describes its work on Session Management: It enables new devices when they appear on ALSA, creates and configures nodes, create links between nodes to route sound from an application to a consumer, etc.
PipeWire alone includes a considerable number of programs. This is just a short selection of tools that you will need more often.
This section gives a short configuration overview, for the complete reference see the Configuration reference.
For configuration, you need a basic understanding of the PipeWire terms, especially:
A Device corresponds to e.g. a sound card A Node produces or consumes sound; this can be e.g. a sink (output) to a device, a media player, or an audio filter A Port represents a channel input/output; a stereo sink e.g. has a L and a R port A Link connects two ports so the sound stream flows from the source (like a media player) to the sink (like e.g. ultimately a headset)
They are explained in more detail in the chapter Object Types later on this page.
Both PipeWire and WirePlumber have a set of config files for configuring different sections like node.rules. They use the same “SPA JSON” format. Configuration files are loaded from different files/directories and then merged based on their priority. For example, settings from $HOME/.config/pipewire/pipewire.conf have priority over those in /etc/pipewire/pipewire.conf, and all settings in config files in $HOME/.config/pipewire/pipewire.conf.d/ have least priority.
The PipeWire server configuration configures the PipeWire instance, defines which modules PipeWire should load, adds device rules, etc.
The PipeWire client configuration contains configuration for PipeWire and ALSA clients. For example, if VLC uses the PipeWire or ALSA backend, its runtime behaviour can be modified with a rule in stream.rules which defines to always route vlc sound output to Bluetooth earbuds and pw-play to a stereo headset.
The PulseAudio/JACK configuration contains configuration for PipeWire’s PulseAudio and JACK servers.
The WirePlumber configuration configures general WirePlumber aspects (should it even bring up ALSA devices or save/restore user settings configured with e.g. pavucontrol) and also ALSA/Bluetooth monitor aspects (choosing a default profile like Stereo or 7.1, setting device priorities that affect default routing, setting device properties, etc.).
Rules in config file can define default outputs for specific nodes (e.g. VLC sound always goes to the 7.1 sound card). ArchLinux: WirePlumber gives a short introduction to using them.
Rules often need IDs of nodes or other components. One way to find them is by using the pwdump tool and then searching in the JSON output with e.g. pwdump | less. It can also print specific types only, for example pwdump Node | less.
Rules can use regular expression when strings start with ~, for example "~alsa.*". This is explained in PipeWire: Working with rules.
PipeWire is a graph-based processing framework, that focuses on handling multimedia data (audio, video and MIDI mainly).
A PipeWire graph is composed of nodes. Each node takes an arbitrary number of inputs called ports, does some processing over this multimedia data, and sends data out of its output ports. The edges in the graph are here called links. They are capable of connecting an output port to an input port.
Nodes can have an arbitrary number of ports. A node with only output ports is often called a source, and a sink is a node that only possesses input ports.
The PipeWire server provides the implementation of some of these nodes itself. Most importantly, it uses alsa-lib like any other ALSA client to expose statically configured ALSA devices as nodes. For example
Similar mechanisms exist to interface with and accommodate applications which use JACK or Pulseaudio.
NOTE: pw-jack modifies the LD_LIBRARY_PATH environment variable so that applications will load PipeWire’s reimplementation of the JACK client libraries instead of JACK’s own libraries. This results in JACK clients being redirected to PipeWire.
Other nodes are implemented by PipeWire clients.
PipeWire clients can be any process. They can speak to the PipeWire server through a UNIX domain socket using the PipeWire native protocol. Besides implementing nodes, they may control the graph.
The PipeWire server itself does not perform any management of the graph; context-dependent behaviour such as monitoring for new ALSA devices, and configuring them so that they appear as nodes, or linking nodes is not done automatically. It rather provides an API that allows spawning, linking and controlling these nodes. This API is then relied upon by clients to control the graph structure, without having to worry about the graph execution process.
A recommended pattern that is often used is a single client be a daemon that deals with the session and policy management. Two implementations are known as of today:
With the nodes which they implement, clients can send multimedia data into the graph or obtain multimedia data from the graph. A client can create multiple PipeWire nodes. That allows one to create more complex applications; a browser would for example be able to create a node per tab that requests the ability to play audio, letting the session manager handle the routing: This allows the user to route different tab sources to different sinks. Another example would be an application that requires many inputs.
The current state of the PipeWire server and its capabilities, and the PipeWire graph are exposed towards clients – including introspection tools like pw-dump – as a collection of objects, each of which has a specific type. These objects have associated parameters, and properties, methods, events, and permissions.
Parameters of an object are data with a specific, well defined meaning, which can be modified and read-out in a controlled fashion through the PipeWire API. They are used to configure the object at run-time. Parameters are the key that allow WirePlumber to negotiate data formats and port configuration with nodes by providing information such as:
Properties of an object are additional data which have been attached on the behalf of modules and of which the PipeWire server has no native understanding. Certain properties are, by convention, expected for specific object types.
Each object type has a list of methods that it needs to implement.
The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and five flags. The five flags are:
The following are the known types and their most important, specialized parameters and methods:
The core is the heart of the PipeWire server. There can only be one core per server and it has the identifier zero. It represents global properties of the server.
A client object is the representation of an open connection with a client process with the server.
Modules are dynamic libraries that are loaded at run time in the clients and in the server and do arbitrary things, such as creating devices or provide methods to create links, nodes, etc.
Modules in PipeWire can only be loaded in their own process. A client, for example, can not load a module in the server.
Nodes are the core data processing entities in PipeWire. They may produce data (capture devices, signal generators, ...), consume data (playback devices, network endpoints, ...) or both (filters like a reverb effect). Nodes have a method process, which eats up data from input ports and provides data for each output port.
Ports are the entry and exit point of data for a Node. A port can either be used for input or output (but not both). A stereo sink, for example, typically has two input ports labeled FL and FR for front left and front right, which may receive data from e.g. vlc which has two output ports FL and FR. (Physically, the sound card can have a stereo jack output, or two chinch outputs, for example, but that is not in the scope of PipeWire.)
For nodes that work with audio, one type of configuration is whether they have dsp ports or a passthrough port. In dsp mode, there is one port for channel of multichannel audio (so two ports for stereo audio, for example), and data is always in 32-bit floating point format. In passthrough mode, there is one port for multichannel data in a format that is negotiated between ports.
Data flows between nodes when there is a Link between their ports. Ports can have multiple incoming/outgoing links, so PipeWire can e.g. send the same vlc audio stream to both the stereo headset and a bluetooth headset and an audio recorder, where each VLC port would have 3 outgoing links.
Links may be "passive" in which case the existence of the link does not automatically cause data to flow between those nodes (some link in the graph must be "active" for the graph to have data flow).
A device is a handle representing an underlying API, which is then used to create nodes or other devices. Examples of devices are ALSA PCM cards or V4L2 devices.
A device has an active Profile that can be chosen from a list of profile. It defines properties like channel setup. For example, a sound card can have a stereo profile where only two ports are exposed, or a surround7.1 profile with 8 ports available.
A factory is an object whose sole capability is to create other objects. Once a factory is created, it can only emit the type of object it declared. Those are most often delivered as a module: the module creates the factory and stays alive to keep it accessible for clients.
Every object implement at least the add_listener method, that allows any client to register event listeners. Events are used through the PipeWire API to expose information about an object that might change over time (the state of a node for example).
The PipeWire server and PipeWire clients use the PipeWire API through their respective pw_context, the so called PipeWire context. When a PipeWire context is created, it finds and parses a configuration file from the filesystem according to the rules of loading configuration files.