Pipewire – это мультимедийный Сервер и фреймворк, доступный в качестве звукового сервера по умолчанию в последних версиях Fedora. По своей сути он предназначен для маршрутизации и обработки аудио- и видеопотоков с низкой задержкой. Он способен мультиплексировать мультимедийные потоки для нескольких клиентов – важная особенность современных операционных систем.
Общие соображения
Эволюция звуковой подсистемы Linux происходила послойно. Самый нижний уровень – это аппаратный уровень с различными аудиоустройствами. Для взаимодействия с драйверами оборудования в Linux существует стандартизированный API под названием Advanced Linux Sound Architecture(ALSA). Другой слой над ALSA, звуковой сервер, должен обрабатывать взаимодействие с приложениями пользовательского пространства. Изначально этим уровнем были PulseAudio и Jack, но недавно он был заменен на PipeWire. Это первые шаги стороннего наблюдателя в работе с Pipewire. Текущая версия – 1.2.7 на Fedora 41 Workstation.
Pipewire – это активируемая сокетами пользовательская служба systemd. Обратите внимание, что менеджер сессий является псевдонимом для wireplumber.service.
rg@f41:~$ systemctl --user list-unit-files "pipewire*" UNIT FILE STATE PRESET pipewire.service disabled disabled pipewire.socket enabled enabled pipewire-session-manager.service alias -
Разрешения и владение
Текущий пользователь является владельцем процесса. Однако каждый вошедший в систему пользователь имеет свой экземпляр pipewire.
rg@f41:~$ ps -eo pid,uid,gid,user,comm,label | grep pipewire 2216 1000 1000 rg pipewire unconfined_t
Большинство файлов мультимедийных устройств, к которым обращается этот процесс, расположены на /dev/snd и /dev/video .
rg@f41:~$ ls -laZ /dev/video* /dev/media* /dev/snd/* crw-rw----+ root video system_u:object_r:v4l_device_t:s0 /dev/video0 crw-rw----+ root video system_u:object_r:v4l_device_t:s0 /dev/media0 crw-rw----+ root audio system_u:object_r:sound_device_t:s0 hwC0D0 crw-rw----+ root audio system_u:object_r:sound_device_t:s0 pcmC0D0c ...
Разрешения равны 660 и ограничены владельцами root:video и root:audio. Обратите внимание на дополнительный знак, указывающий на ACL-разрешения. Systemd-logind – это менеджер входа в систему Fedora. Эта служба, как часть процесса входа в систему, выполнит двоичный файл по адресу /usr/lib/systemd/systemd-logind. Он создаст сессию и назначит место. При входе в систему он изменит список контроля доступа к файлам мультимедийных устройств для текущего пользователя. Так Pipewire получает доступ к файлам устройств, которые в противном случае принадлежат root:audio или root:video. Чтобы проверить текущий ACL файлов:
rg@f41:~$ getfacl /dev/video0 file: dev/video0 owner: root group: video user::rw- user:rg:rw- # current user group::rw- mask::rw- other::---
Команда lsof может показать все процессы, обращающиеся к нашим звуковым и видеоустройствам:
rg@f41:~$ lsof | grep -E '/dev/snd|/dev/video*|/dev/media*' COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME pipewire 53799 rg 44u CHR 116,9 0t0 913 /dev/snd/controlC0 pipewire 53799 rg 83u CHR 81,0 0t0 884 /dev/video0 wireplumb 53801 rg 24u CHR 116,9 0t0 913 /dev/snd/controlC0 ...
Вы должны ожидать, что только Pipewire и его менеджер сессий (wireplumber) будут обращаться к нашим мультимедийным устройствам. Устаревшие приложения, обращающиеся к этим подсистемам напрямую, также будут отображаться здесь.
Настройка
Конфигурационный файл /usr/share/pipewire/pipewire.conf принадлежит root:root и содержит системные настройки по умолчанию. Обязательно проверьте комментарии; среди прочего вы найдете список загруженных модулей. Сервис настраивается с помощью конфигурационных файлов с выпадающим адресом homedir. Процесс является легковесным и состоит всего из 3 потоков. Ниже в качестве небольшого теста демонстрируется увеличение количества циклов данных.
rg@f41:~$ cat <<EOF > ~/.config/pipewire/pipewire.conf.d/loops.conf
context.properties = {
context.num-data-loops = -1
}
EOF
rg@f41:~$ systemctl --user daemon-reload
rg@f41:~$ systemctl --user restart pipewire.service
rg@f41:~$ ps -eLo pid,tid,command,comm,pmem,pcpu | grep pipewire
PID TID COMMAND COMMAND PMEM PCPU
40187 40187 /usr/bin/pipewire pipewire 0.3 0.0
40187 40193 /usr/bin/pipewire module-rt 0.3 0.0
40187 40194 /usr/bin/pipewire data-loop.0 0.3 0.0
40187 40195 /usr/bin/pipewire data-loop.1 0.3 0.0
40187 40196 /usr/bin/pipewire data-loop.2 0.3 0.0
...
С помощью pw-config вы можете проверить состояние нашей обновленной конфигурации:
rg@f41:~$ pw-config
{
"config.path": "/usr/share/pipewire/pipewire.conf",
"override.config.path": "~/.config/pipewire/pipewire.conf.d/loops.conf"
}
Некоторые архитектурные соображения
Pipewire состоит примерно из 30 тысяч строк кода на языке программирования C. Архитектура напоминает событийно-управляемый шаблон производитель-потребитель. Управляющие структуры представляют собой графоподобные структуры с узлами-производителями и узлами-потребителями. Одной из причин достижения низкой задержки и низкого уровня загрузки процессора является использование концепции, называемой нулевым копированием через memfd_create. Сигнализация между слабосвязанными компонентами осуществляется с помощью eventfd. Код является расширяемым и модульным.
Все состояние Pipewire представляет собой граф со следующими основными элементами:
- Узлы (с портами ввода/вывода)
- Связи (ребра)
- Клиенты (пользовательские процессы)
- Модули (разделяемые объекты)
- Фабрики (библиотеки модулей)
- Метаданные (настройки)
Граф состоит из узлов и ребер/связей. Узлы, имеющие только входные порты, называются стоками, а узлы, имеющие только выходные порты, называются источниками. Узлы с обоими портами обычно называются фильтрами.
Модули
Каждый модуль обеспечивает определенную функциональность сервера. Потратьте немного времени на просмотр названий модулей. Есть модуль, управляющий доступом между компонентами графа, модуль, управляющий приоритетами потоков, модуль, эмулирующий бывшую Pulseaudio, модуль для XDG Portal, модуль для профилирования и даже модуль, реализующий потоковую передачу по сети через RTP.
rg@f41:~$ ls -la /usr/lib64/pipewire-0.3/ -rwxr-xr-x. 1 root root 28296 Nov 26 01:00 libpipewire-module-access.so -rwxr-xr-x. 1 root root 98808 Nov 26 01:00 libpipewire-module-avb.so ...
Плагины
Плагины управляют взаимодействием с устройствами, включая создание кольцевых буферов, которые активно используются в мультимедийных процессах. Есть плагин, который управляет взаимодействием с ALSA, плагин, который управляет взаимодействием с Bluetooth, libcamera и т.д.
rg@f41:~$ ls -la /usr/lib64/spa-0.2/ drwxr-xr-x. 1 root root 28 Dec 11 12:13 alsa drwxr-xr-x. 1 root root 420 Dec 11 12:13 bluez5 ...
Плагины расширяют общий API, называемый SPA, или Simple Plugin API. Эти самодостаточные разделяемые библиотеки предоставляют фабрики, содержащие интерфейсы. Поскольку они являются автономными, некоторые из этих плагинов используются менеджером сессий. Утилита _spa-inspect _ позволяет запрашивать общие объекты на предмет фабрик и интерфейсов.
Полезные команды
Мы можем увидеть ®unning узлы с помощью pw-top. В следующем примере в настоящее время работают 3 узла. Любая потоковая передача, осуществляемая через Pipewire, будет отображаться в этой таблице вывода.
rg@f41:~$ pw-top S ID QUANT RATE WAIT BUSY FORMAT NAME R 90 512 48000 139.8us 23.0us S16LE 2 48000 bluez_output.. R 99 900 48000 66.7us 37.7us F32LE 2 48000 + Firefox R 112 4320 48000 105.5us 11.4us S16LE 2 48000 + Videos
Квантование (QUANT) можно рассматривать как количество аудиосэмплов (размер буфера), которые будут обрабатываться в каждом цикле графа. Это зависит от типа устройства и может быть настроено или согласовано с помощью конфигурационных файлов. Например, в приложении Videos размер буфера составляет 4320 сэмплов, а в узле вывода Bluetooth – только 512.
Rate (RATE) – это частота обработки графика. В приведенном выше выводе график работает на частоте 48 кГц, то есть каждую секунду график может обрабатывать 48 000 образцов.
Соотношение между Quantum и Rate – это задержка в секундах. В случае Bluetooth-гарнитур задержка составляет 11 мс, а для приложения «Видео» – 90 мс. На man-странице pw-top есть ссылки на более подробные объяснения. Понимание этих показателей может помочь повысить производительность.
Еще одна мощная утилита – pw-cli. Она открывает оболочку и позволяет интерактивно работать с графом Pipewire во время выполнения программы. Для постоянных изменений необходимо внести поправки в конфигурационные файлы.
rg@f41:~$ pw-cli h Available commands: help | h Show this help load-module | lm Load a module. unload-module | um Unload a module. connect | con Connect to a remote. disconnect | dis Disconnect from a remote. list-remotes | lr List connected remotes. switch-remote | sr Switch between current remotes. list-objects | ls List objects or current remote. info | i Get info about an object. create-device | cd Create a device from a factory. create-node | cn Create a node from a factory. destroy | d Destroy a global object. create-link | cl Create a link between nodes. export-node | en Export a local node enum-params | e Enumerate params of an object set-param | s Set param of an object permissions | sp Set permissions for a client get-permissions | gp Get permissions of a client send-command | c Send a command <object-id> quit | q Quit
Например, мы можем распечатать приведенные выше потоковые узлы с помощью pw-cli info и просмотреть все их свойства.
Визуализация графиков
Все производные Утилиты обрабатывают один и тот же большой JSON-граф, который представляет собой состояние Pipewire. С помощью pw-dump мы получим это состояние. Но вывод трудно читать. Другая утилита pw-dot даст нам удобный для восприятия граф. Запомните следующие 3 шага, так как вам придется повторять их в течение этого урока. Утилита dot, используемая в следующем примере, доступна при установке Graphviz.
rg@f41:~$ pw-dot --detail --all rg@f41:~$ dot -Tpng pw.dot -o pw.png rg@f41:~$ loupe pw.png
Это большой график. Потратьте минуту, чтобы рассмотреть различные узлы, типы узлов, выделенные разными цветами, и некоторые значимые свойства, такие как порты, класс носителя и ссылки. Описание этих свойств можно найти в man’е. Обратите внимание на свойство media.name, которое указывает на текущую песню, проигрываемую в Firefox.

Большинство потоков, которые вы увидите здесь, будут потоками Source/Audio.
Новое приложение камеры по умолчанию в Fedora 41 Workstation – Snapshot. Запустите это приложение, и вы заметите, что оно видно в Pipewire. Давайте оставим Snapshot запущенным и откроем другое приложение камеры. Не забудьте установить ffmpeg-free для следующих шагов.
rg@f41:~$ ffplay -f v4l2 -i /dev/video0 /dev/video0: Device or resource busy
ffplay пытается получить доступ к /dev/video0 напрямую. Согласно документации ядра, общие потоки данных должны быть реализованы прокси в пользовательском пространстве, а не драйверами v4l2. Pipewire уже использует libcamera api, как вы видели в списке плагинов, и предлагает pw-v4l2 в качестве совместимой обертки. Давайте снова воспользуемся той же командой.
rg@f41:~$ pw-v4l2 ffplay -f v4l2 -i /dev/video0
На этот раз все получилось, и у нас есть два приложения, которые используют один и тот же поток камеры. Это и есть дополнительная польза от высокопроизводительного мультимедийного сервера. На графике мы также видим оба потока Source/Video. То же самое можно сказать и о браузере. В настройках браузера вы должны иметь возможность выбрать, какие устройства использовать – Pipewire или v4l2.
Вы также можете использовать Helvum или Qpwgraph. Протестируйте любое из этих приложений. Вы обнаружите упрощенный граф с определенными типами узлов и функциями перетаскивания. Вы оцените простоту использования, но иногда вам понадобится полный граф и полный набор свойств узлов. В таких случаях вы вернетесь к pw-dump, pw-cli и pw-dot.
Управление узлами
Одним из способов управления временными узлами графа является использование pw-cli. Например, здесь мы создаем временный тестовый узел, действующий до тех пор, пока открыта сессия. Имена фабрик можно посмотреть в исходном коде, а некоторые полезные примеры – в комментариях к странице conf.
rg@f41:~$ PIPEWIRE_DEBUG=2 pw-cli pipewire-0>> create-node spa-node-factory factory.name=support.node.driver node.name=mynewnode
Другой способ создания узлов, но на этот раз в нашей постоянной конфигурации, описан в комментариях к конфигурации:
rg@f41:~$ cat <<EOF > ~/.config/pipewire/pipewire.conf.d/mynewnode.conf
context.objects = [{
factory = adapter
args = {
factory.name = api.alsa.pcm.source
node.name = "alsa-testnode"
node.description = "PCM TEST"
media.class = "Audio/Source"
api.alsa.path = "hw:0"
api.alsa.period-size = 1024
audio.format = "S16LE"
audio.channels = 2
audio.position = "FL,FR" }
}]
EOF
rg@f41:~$ systemctl --user daemon-reload
rg@f41:~$ systemctl --user restart pipewire.service
Более быстрый и простой способ создания узлов – использовать Вспомогательные утилиты pw-record и pw-play для создания стоков и источников. В последнем параграфе мы приведем небольшой пример.
Соображения безопасности
Двоичный файл принадлежит root и может быть выполнен другими пользователями.
rg@f41:~$ ls -la /usr/bin/pipewire -rwxr-xr-x. 1 root root 20104 Nov 26 01:00 /usr/bin/pipewire
Протокол, используемый для взаимодействия с сокетами, называется native protocol. Сокеты имеют большие права доступа, как и другие временные файлы systemd. Полезно помнить, что /run – это файловая система tmpfs в памяти.
rg@f41:~$ ls -laZ /run/user/1000/pipewire* srw-rw-rw-. rg rg object_r:user_tmp_t:s0 pipewire-0 -rw-r-----. rg rg object_r:user_tmp_t:s0 pipewire-0.lock srw-rw-rw-. rg rg object_r:user_tmp_t:s0 pipewire-0-manager -rw-r-----. rg rg object_r:user_tmp_t:s0 pipewire-0-manager.lock
Сокеты systemd создают структуры потоков; после этого аудиоузлы будут взаимодействовать напрямую через memfd. Согласно man-странице, memfd_create создает анонимный файл in-memory, что очень важно для производительности. Исходный код Pipewire использует эти вызовы с определенными флагами, такими как MFD_HUGETLB, а также с флагами MFD_ALLOW_SEALING, MFD_CLOEXEC и т. д., чтобы запечатать и предотвратить утечку памяти в другие процессы.
Pipewire поставляется с модулем под названием protocol-pulse. Модуль имеет отдельную пользовательскую службу systemd, активируемую через сокет. Он эмулирует сервер PulseAudio для совместимости с большим количеством клиентов, все еще использующих библиотеки PulseAudio.
rg@f41:~$ ls -laZ /run/user/1000/pulse/native srw-rw-rw-. rg rg user_tmp_t:s0 /run/user/1000/pulse/native
Systemd предлагает способы анализа и «песочницы» сервисов. Некоторые рекомендации приведены ниже, но они не гарантируют защиту на уровне системы.
rg@f41:~$ systemd-analyze --user --no-pager security pipewire.service
Любое приложение пользовательского пространства может просматривать граф, обращаться к сокету и создавать узлы. Свойства узлов в некоторых случаях содержат информацию, которая может быть приватной. Примером может служить свойство media.name. Этого не происходит для приватных вкладок Firefox. Это свойство может быть полезно для некоторых функций графического интерфейса, например для уведомлений в трее Gnome.
Возможно, вы заметили узел speech-dispatcher, случайно подключенный к вашему динамику. Эта функция доступности может запрашиваться различными программами, включая ваш браузер. Попробуйте понять такое поведение в ваших любимых мультимедийных приложениях.
Пример
В качестве небольшого примера попробуем создать аудиокнигу – поток распознавания голоса с узлами-источниками и узлами-стоками Pipewire. Создадим узел-источник с помощью pw-play. В других сценариях _pw-play _может читать из stdin (-).
rg@f41:~$ curl https://ia600707.us.archive.org/8/items/alice_in_wonderland_librivox/wonderland_ch_10_64kb.mp3 --output test.mp3
rg@f41:~$ ffmpeg -i test.mp3 -ar 48000 -ac 1 -sample_fmt s16 test.wav
rg@f41:~$ pw-play --target=0 --format=s16 \
--quality=14 --media-type=Audio --channels=1 \
--properties='{node.name=mytestsource}' test.wav
Автоподключение процесса к колонкам не произошло из-за аргумента target. Давайте создадим узел поглотителя. Для простоты мы будем использовать пакет pocketsphinx для тестирования распознавания голоса.
rg@f41:~$ dnf install pocketsphinx
rg@f41:~$ pw-record --media-type=Audio --target=0 --rate=16000 \
--channels=1 --quality=14 --properties='{node.name=mytestsink}' - \
| pocketsphinx live -
Как вы можете видеть, мы создали узел поглотителя. Автоподключение не произошло ни к одному узлу-источнику, о чем свидетельствует аргумент target. Остальные аргументы, например количество каналов, являются рекомендациями с man-страниц Pocketsphinx. Мы будем создавать связи вручную. Используйте pw-dot, чтобы найти идентификаторы узлов и портов источника и поглотителя. Также вы можете использовать любое из двух приложений Flatpak для создания связей путем перетаскивания.
rg@f41:~$ PIPEWIRE_DEBUG=2 pw-cli pipewire-0>> create-link <nodeid> <portid> <nodeid> <portid>
Мы создали поток между источником и поглотителем и уже можем видеть некоторые выходные данные в терминале Pocketsphinx. Задействованные процессы видны Pipewire и могут использовать все его возможности. Аналогичным образом вы можете взаимодействовать с вашим любимым Flatpak. Многие из этих приложений существуют в «песочнице» и взаимодействуют с базовой системой с помощью API XDG Portal.
Еще один пример
Давайте попробуем привести еще один пример, связанный с фильтрами. Ранее мы уже видели, что в Pipewire есть модуль под названием filter-chain. Прежде всего, нам нужно будет установить нужные плагины фильтров. В репозиториях DNF есть большое количество плагинов фильтров LADSPA и LV2. В Pipewire также есть несколько встроенных фильтров, доступных благодаря плагину audiomixer.
rg@f41:~$ dnf install ladspa ladspa-rev-plugins rg@f41:~$ rpm -ql ladspa-rev-plugins /usr/lib64/ladspa/g2reverb.so
Как мы видим, пакет устанавливает общий объект. Для плагинов LADSPA мы можем извлечь метаданные с помощью утилиты analyseplugin. Важную информацию можно увидеть в следующем выводе:
rg@f41:~$ analyseplugin /usr/lib64/ladspa/g2reverb.so Plugin Name: "Stereo reverb" Plugin Label: "G2reverb" Ports: ... "Room size" input, control, 10 to 150 "Reverb time" input, control, 1 to 20 "Input BW" input, control, 0 to 1 "Damping" input, control, 0 to 1 "Dry sound" input, control, -80 to 0 "Reflections" input, control, -80 to 0 "Reverb tail" input, control, -80 to 0
На основе этого мы можем создать файл фильтра. Убедитесь, что имя, расположение, метка и тип плагина указаны правильно.
rg@f41:~$ cat <<EOF > ~/.config/pipewire/pipewire.conf.d/myfilter.conf
context.modules = [
{
name = libpipewire-module-filter-chain
args = {
node.description = "My filter"
media.name = "My filter"
filter.graph = {
nodes = [
{
type = ladspa
name = "Stereo reverb"
plugin = "/usr/lib64/ladspa/g2reverb.so"
label = "G2reverb"
control = {
"Reverb time" = 2
}
}
]
}
audio.channels = 2
audio.position = [ FL FR ]
capture.props = {
node.name = "myfilter"
media.class = Audio/Sink
node.target = 0
}
playback.props = {
node.name = "myfilter"
media.class = Audio/Source
node.target = 0
}
}
}
]
EOF
rg@f41:~$ systemctl --user restart pipewire.service
Проверьте граф, например с помощью Qpwgraph, чтобы показать пару поглотитель/источник фильтра. Обратите внимание, что узел поглотителя также имеет порты монитора. Эти порты позволяют просматривать поток перед обработкой. Теперь создайте узел источника, как и раньше:
rg@f41:~$ pw-play --target=0 --format=s16 --media-type=Audio \
--properties='{node.name=mytestsource}' test.mp3
Подключите узлы вручную, перетаскивая их на динамик аудиоконтроллера. Ваша настройка должна выглядеть так, как показано ниже:

Подтвердите, что голос диктора имеет эффект реверберации. Теперь не стесняйтесь экспериментировать с любимыми плагинами или добавлять больше узлов в фильтр. Папка filter-chain содержит другие полезные примеры. Не забудьте удалить тестовые конфигурации из папки .config, когда закончите исследование.
Заключение
Pipewire – это интересная часть низкоуровневого высокопроизводительного программного обеспечения. Мы обсудили основные команды, параметры конфигурации и взаимодействие с файловой системой. Настоятельно рекомендуем прочитать документацию по Pipewire. Книга «Звуковое Программирование в Linux» Яна Ньюмарча также даст представление об исторических аспектах. Надеюсь, у вас будет достаточно контекста, чтобы лучше понимать встречающиеся термины. Спасибо людям, поддерживающим упомянутые пакеты.




Комментарии (0)