Многие пользователи Linux годами смирялись с заиканиями при воспроизведении, конфликтами устройств вывода и нестабильной работой Bluetooth-наушников. Причиной часто была архитектура PulseAudio, которая не справлялась с современными задачами обработки звука. Появление PipeWire кардинально изменило ситуацию: новый медиасервер объединил низкую задержку JACK, совместимость с PulseAudio и нативную поддержку видеопотоков в одном процессе. В этой статье разберём, как правильно удалить PulseAudio, настроить PipeWire и WirePlumber, добиться одновременного звука на двух парах Bluetooth-наушников и устранить типичные проблемы.

Архитектурные преимущества новой подсистемы

PipeWire не просто заменяет PulseAudio - он перестраивает всю графовую модель обработки звука. Вместо жесткой иерархии приёмник - источник, PipeWire строит динамический граф узлов, где каждый поток может быть соединён с любым другим. Фоновая служба pipewire работает как единственный процесс, управляющий всеми медиапотоками, а WirePlumber выступает в роли менеджера сессий, автоматически связывая устройства и приложения по заданным правилам.

В основе лежит SPA - Simple Plugin API, набор C-интерфейсов для плагинов обработки звука и видео. Каждый узел графа представляет собой поток данных с входными и выходными портами. Форматы согласовываются динамически: при соединении узлы обмениваются возможностями через параметр EnumFormat и выбирают общий формат без пересэмплирования, если это возможно. Это исключает потерю качества при цепочке преобразований.

Ключевое отличие - поддержка профессионального аудио с задержкой ниже 1 мс без отдельного запуска JACK. Приложения, написанные под PulseAudio, JACK или ALSA, продолжают работать без изменений благодаря модулям совместимости pipewire-pulse, pipewire-jack и pipewire-alsa. Для контейнеров Flatpak и песочниц это означает единый путь доступа к звуковому оборудованию без лишних прослоек.

Bluetooth-стек в PipeWire переписан с нуля. Вместо старого модуля module-bluez5-device теперь используется нативный SPA-плагин libspa-bluez5, который напрямую общается с BlueZ через D-Bus. Это даёт поддержку современных профилей LE Audio и кодека LC3, аппаратное управление громкостью VCP/VCS и корректную работу TWS-наушников, где каждое ухо - отдельное устройство. Синхронизация каналов достигается на уровне графа с точностью до субсэмплов.

Видеоподдержка интегрирована в тот же граф: захват экрана, веб-камеры и аппаратное кодирование через VAAPI/VDPAU работают как обычные узлы. Приложения Wayland (GNOME Shell, OBS, браузеры) запрашивают видеопотоки через PipeWire вместо отдельных фоновых служб, что упрощает конвейер записи экрана и видеоконференций.

Удаление PulseAudio и установка PipeWire

Полная замена требует установки пакетов PipeWire и одновременного удаления конфликтующих пакетов PulseAudio. В Arch Linux и производных выполните:

sudo pacman -Rns pulseaudio pulseaudio-alsa pulseaudio-bluetooth pavucontrol
sudo pacman -S pipewire wireplumber pipewire-pulse pipewire-alsa pipewire-jack libpipewire03 gst-plugin-pipewire helvum

В Fedora используйте dnf swap:

sudo dnf swap pulseaudio pipewire --allowerasing
sudo dnf install wireplumber pipewire-pulseaudio pipewire-alsa pipewire-jack pipewire-libs gstreamer1-plugin-pipewire helvum

В Ubuntu и Debian 12+ переключение делается через метапакет:

sudo apt install pipewire wireplumber pipewire-pulse pipewire-alsa pipewire-jack libspa-02-bluetooth libpipewire-0.3-modules gstreamer1.0-pipewire helvum
sudo apt remove pulseaudio pulseaudio-module-bluetooth pavucontrol

После установки включите пользовательские службы:

systemctl --user enable --now pipewire wireplumber pipewire-pulse

Проверьте, что сервер запущен и виден клиентам:

pactl info | grep "Server Name"
wpctl status

Вывод pactl info должен показать Server Name: PulseAudio (on PipeWire 1.2.x) - это подтверждает, что модуль совместимости работает. Команда wpctl status выдаст дерево устройств с метками * у активных приёмников.

Настройка WirePlumber для стабильной работы Bluetooth

WirePlumber управляет политиками подключения устройств через Lua-скрипты в /usr/share/wireplumber/main.lua.d/ и пользовательские переопределения в ~/.config/wireplumber/. Для Bluetooth критически важны два параметра: автоматическое переключение профиля и приоритет кодеков.

Создайте файл ~/.config/wireplumber/main.lua.d/50-bluetooth-policy.lua со следующим содержимым:

-- Принудительное включение A2DP Sink для наушников
rule = {
  matches = {
    {
      { "device.api", "=", "bluez" },
      { "device.profile", "=", "a2dp-sink" },
    },
  },
  apply_properties = {
    ["bluez5.auto-connect"] = "[ a2dp-sink ]",
    ["bluez5.codecs"] = "[ aac aptx aptx-hd ldac ]",
  },
}

Этот скрипт заставляет WirePlumber сразу переключать подключенные наушники в профиль a2dp-sink с приоритетом высококачественных кодеков. По умолчанию может выбираться hfp или headset-head-unit, что даёт моно-звук низкого качества.

Для LE Audio устройств (современные TWS-наушники с поддержкой LC3) добавьте в /etc/bluetooth/main.conf:

[General]
ControllerMode = le
Experimental = true
KernelExperimental = true

Перезапустите службу Bluetooth:

sudo systemctl restart bluetooth

Теперь устройства с поддержкой LE Audio будут соединяться по новому профилю bap-sink или bap-duplex, обеспечивая стерео-звук с кодеком LC3 и аппаратную синхронизацию ушных модулей.

Решение проблем с заиканиями и xrun

Заикания (xrun) возникают, когда графический процесс не успевает обработать буфер за квант времени. В PipeWire основные параметры задержки задаются в /etc/pipewire/pipewire.conf секции context.properties:

context.properties = {
    default.clock.rate = 48000
    default.clock.allowed-rates = [ 48000 44100 96000 192000 ]
    default.clock.quantum = 1024
    default.clock.min-quantum = 32
    default.clock.max-quantum = 8192
}

Параметр default.clock.quantum - размер буфера в семплах. При 48 кГц значение 1024 даёт задержку 21,3 мс. Для низколатентных задач (игры, мониторинг) уменьшите до 256 или 128, но это повышает нагрузку на CPU. Если xrun остаются, добавьте в context.modules модуль реального времени:

{   name = libpipewire-module-rt
    args = {
        nice.level   = -11
        rt.prio      = 88
        rt.time.soft = 200000
        rt.time.hard = 200000
    }
}

Модуль запрашивает планировщик SCHED_FIFO с приоритетом 88. Требует настройки лимитов в /etc/security/limits.conf:

@audio - rtprio 90
@audio - memlock unlimited

Добавьте пользователя в группу audio:

sudo usermod -aG audio $USER

После перелогинивания заикания при высокой нагрузке должны исчезнуть. Проверить наличие xrun можно командой:

pw-top -p

Колонка ERR покажет количество пропусков буфера. Ненулевые значения - повод увеличить quantum или снизить приоритет реального времени.

Частая причина xrun - конфликт частот дискретизации. Если одно приложение просит 44,1 кГц, а устройство работает на 48 кГц, включится ресэмплер. В PipeWire используется нативный ресэмплер со скосом, но при малом quantum он может не укладываться. Добавьте в context.properties принудительную частоту для проблемных узлов:

default.clock.force-rate = 48000
default.clock.force-quantum = 1024

Также проверьте, не занят ли CPU другими процессами. htop покажет загрузку по ядрам. Если одно ядро на 100%, увеличьте quantum до 2048 или перенесите PipeWire на выделенное ядро через taskset:

systemctl --user edit pipewire
# Добавьте в секцию [Service]:
# ExecStartPre=/bin/sh -c 'taskset -cp 1 $(pgrep -f "pipewire$")'

Одновременный вывод на две пары Bluetooth-наушников

PipeWire позволяет объединить несколько приёмников в один виртуальный приёмник через модуль libpipewire-module-combine-stream. Это нативный аналог module-combine-sink из PulseAudio, но с компенсацией задержки и работой в графе.

Создайте файл ~/.config/pipewire/pipewire.conf.d/99-combine-bluetooth.conf:

context.modules = [
    {
        name = libpipewire-module-combine-stream
        args = {
            combine.mode = sink
            node.name = "combined_bt_sink"
            node.description = "Две пары Bluetooth-наушников"
            combine.latency-compensate = true
            combine.props = {
                audio.position = [ FL FR ]
            }
            stream.props = {
                stream.dont-remix = true
                node.passive = true
            }
            stream.rules = [
                {
                    matches = [
                        { media.class = "Audio/Sink" }
                        { device.api = "bluez" }
                        { node.name = "~bluez_output.*" }
                    ]
                    actions = { create-stream = { } }
                }
            ]
        }
    }
]

Правило stream.rules автоматически находит все Bluetooth-приёмники (device.api = "bluez") и создаёт для каждого поток в комбинированном приёмнике. Параметр combine.latency-compensate = true добавляет задержки к более быстрым устройствам, чтобы звук играл синхронно.

Перезапустите PipeWire:

systemctl --user restart pipewire wireplumber

В pavucontrol или wpctl status появится новый приёмник "Две пары Bluetooth-наушников". Выберите его как устройство вывода по умолчанию:

wpctl set-default $(wpctl status | awk '/combined_bt_sink/ {print $2}' | tr -d '.')

Теперь звук из любого приложения пойдёт на обе пары наушников одновременно. Громкость каждой пары регулируется отдельно через pavucontrol - виртуальный приёмник имеет монитор-каналы для каждого входящего потока.

Если нужно объединить конкретные устройства по MAC-адресу, замените stream.rules на явные совпадения:

stream.rules = [
    {
        matches = [
            { media.class = "Audio/Sink" }
            { node.name = "bluez_output.XX_XX_XX_XX_XX_XX.a2dp-sink" }
        ]
        actions = { create-stream = { combine.audio.position = [ FL FR ] } }
    },
    {
        matches = [
            { media.class = "Audio/Sink" }
            { node.name = "bluez_output.YY_YY_YY_YY_YY_YY.a2dp-sink" }
        ]
        actions = { create-stream = { combine.audio.position = [ FL FR ] } }
    }
]

Узнайте точные имена узлов командой wpctl status или pw-dump | jq '.[] | select(.info.props["media.class"] == "Audio/Sink") | .info.props["node.name"]'.

Диагностика и устранение конфликтов устройств

Когда несколько приложений борются за одно устройство, или приёмник "пропадает" после сна, помогает анализ графа PipeWire. Установите helvum - графический патчбей для визуального управления связями:

helvum

В интерфейсе увидите все узлы (приложения, устройства, виртуальные приёмники) и связи между ними. Перетаскиванием проводов можно вручную перенаправить поток, клик правой кнопкой на узле - посмотреть свойства и формат.

Для консольной диагностики используйте pw-dump - он выдаёт полный JSON-снимок графа:

pw-dump | jq '.[] | select(.type == "PipeWire:Interface:Node") | {id, name: .info.props["node.name"], class: .info.props["media.class"], device: .info.props["device.api"]}'

Покажет таблицу всех узлов с классами и API устройств. Ищите узлы с media.class = "Audio/Sink" и device.api = "bluez" - это ваши Bluetooth-приёмники.

Если устройство не переключается на нужный профиль, проверьте доступные профили:

pactl list cards | grep -A 20 "bluez_card"

И принудительно установите профиль:

pactl set-card-profile bluez_card.XX_XX_XX_XX_XX_XX a2dp-sink

Для постоянного решения добавьте правило в WirePlumber (файл ~/.config/wireplumber/main.lua.d/51-force-a2dp.lua):

rule = {
  matches = {
    {
      { "device.api", "=", "bluez" },
      { "device.profile", "=", "a2dp-sink" },
    },
  },
  apply_properties = {
    ["session.profile"] = "a2dp-sink",
  },
}

При проблемах с автоподключением после сна проверьте настройки bluez5.auto-connect в свойствах устройства через pw-dump и при необходимости задайте явно:

apply_properties = {
  ["bluez5.auto-connect"] = "[ a2dp-sink hfp ]",
}

Настройка приоритетов устройств и автоматического переключения

По умолчанию WirePlumber выбирает приёмник по приоритету: выше приоритет у устройств с priority.session и priority.driver. Посмотреть текущие значения:

pw-metadata -n default 0

Ключи default.configured.audio.sink и default.audio.sink хранят настроенное и текущее устройство. Изменить приоритет навсегда:

pw-metadata -n default 0 default.configured.audio.sink '{ "name": "bluez_output.XX_XX_XX_XX_XX_XX.a2dp-sink" }'

Для автоматического переключения на недавно подключённое устройство загрузите модуль module-switch-on-connect в pipewire-pulse.conf:

pulse.cmd = [
    { cmd = "load-module" args = "module-switch-on-connect" flags = [ ] }
]

Или через context.exec в pipewire.conf:

context.exec = [
    { path = "pactl" args = "load-module module-switch-on-connect" }
]

Теперь при подключении новых наушников звук автоматически переключится на них, а при отключении - вернётся на предыдущий приёмник. В GNOME и KDE это поведение также настраивается в системных настройках звука - переключатель "Автоматически переключаться на новые устройства".