Adding upstream version 1.4.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
parent
5c5d7a378e
commit
6b016a712f
1265 changed files with 465320 additions and 0 deletions
20
.codespell-ignore
Normal file
20
.codespell-ignore
Normal file
|
@ -0,0 +1,20 @@
|
|||
ba
|
||||
capela
|
||||
cas
|
||||
crasher
|
||||
datas
|
||||
endcode
|
||||
files'
|
||||
goin
|
||||
hda
|
||||
hist
|
||||
hve
|
||||
inport
|
||||
nd
|
||||
mmaped
|
||||
od
|
||||
ot
|
||||
parm
|
||||
sinc
|
||||
stdio
|
||||
uint
|
27
.editorconfig
Normal file
27
.editorconfig
Normal file
|
@ -0,0 +1,27 @@
|
|||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
# Use 2 spaces for meson files
|
||||
[*.build]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{conf,conf.in}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{xml,xml.in}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
test/data/*.txt diff
|
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
.*
|
||||
!.gitlab
|
||||
ABOUT-NLS
|
||||
*~
|
||||
*.tar.gz
|
||||
*.tar.xz
|
||||
*.o
|
||||
cscope.out
|
||||
cscope.in.out
|
||||
cscope.po.out
|
||||
Makefile
|
||||
subprojects/lua*
|
||||
subprojects/wireplumber
|
||||
subprojects/media-session
|
||||
subprojects/packagecache
|
||||
subprojects/googletest*
|
||||
subprojects/gtest.wrap
|
||||
subprojects/libyaml.wrap
|
||||
subprojects/libyaml
|
||||
subprojects/libcamera
|
||||
subprojects/webrtc-audio-processing
|
||||
|
||||
# Created by https://www.gitignore.io/api/vim
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
|
||||
# End of https://www.gitignore.io/api/vim
|
590
.gitlab-ci.yml
Normal file
590
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,590 @@
|
|||
# Create merge request pipelines for open merge requests, branch pipelines
|
||||
# otherwise. This allows MRs for new users to run CI, and prevents duplicate
|
||||
# pipelines for branches with open MRs.
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
|
||||
stages:
|
||||
- container
|
||||
- container_coverity
|
||||
- build
|
||||
- analysis
|
||||
- pages
|
||||
|
||||
variables:
|
||||
FDO_UPSTREAM_REPO: 'pipewire/pipewire'
|
||||
|
||||
# ci-templates as of Jan 27th 2022
|
||||
.templates_sha: &templates_sha 0c312d9c7255f46e741d43bcd1930f09cd12efe7
|
||||
|
||||
include:
|
||||
- project: 'freedesktop/ci-templates'
|
||||
ref: *templates_sha
|
||||
file: '/templates/fedora.yml'
|
||||
- project: 'freedesktop/ci-templates'
|
||||
ref: *templates_sha
|
||||
file: '/templates/ubuntu.yml'
|
||||
- project: 'freedesktop/ci-templates'
|
||||
ref: *templates_sha
|
||||
file: '/templates/alpine.yml'
|
||||
|
||||
.fedora:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
FDO_DISTRIBUTION_TAG: '2024-12-10.0'
|
||||
FDO_DISTRIBUTION_VERSION: '40'
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
alsa-lib-devel
|
||||
avahi-devel
|
||||
bluez-libs-devel
|
||||
clang
|
||||
dbus-devel
|
||||
doxygen
|
||||
fdk-aac-free-devel
|
||||
findutils
|
||||
gcc
|
||||
gcc-c++
|
||||
git
|
||||
glib-devel
|
||||
graphviz
|
||||
gstreamer1-devel
|
||||
gstreamer1-plugins-base-devel
|
||||
jack-audio-connection-kit-devel
|
||||
libasan
|
||||
libcanberra-devel
|
||||
libebur128-devel
|
||||
libffado-devel
|
||||
libldac-devel
|
||||
libmysofa-devel
|
||||
libsndfile-devel
|
||||
libubsan
|
||||
libusb1-devel
|
||||
lilv-devel
|
||||
libv4l-devel
|
||||
libva-devel
|
||||
libX11-devel
|
||||
ModemManager-devel
|
||||
meson
|
||||
openssl-devel
|
||||
pulseaudio-libs-devel
|
||||
python3-docutils
|
||||
python3-pip
|
||||
sbc-devel
|
||||
ShellCheck
|
||||
SDL2-devel
|
||||
systemd-devel
|
||||
vulkan-loader-devel
|
||||
webrtc-audio-processing-devel
|
||||
which
|
||||
valgrind
|
||||
ninja-build
|
||||
pkgconf
|
||||
pulseaudio-utils
|
||||
openal-soft
|
||||
readline-devel
|
||||
pandoc
|
||||
# Uncommenting the following two lines and disabling the meson entry above
|
||||
# will re-enable use of Meson via pip but please consider using a newer distro
|
||||
# image first or making the build system compatible instead! This is because
|
||||
# using pip or another 3rd party repo defeats the point testing the particular
|
||||
# distro for regressions.
|
||||
# NOTE: If you do end up using pip3 for meson, be sure to also update the
|
||||
# build_meson_prerelease and build_meson_exact_release build instructions
|
||||
# to uninstall the pip3 version again and probably to not call dnf remove
|
||||
# FDO_DISTRIBUTION_EXEC: >-
|
||||
# pip3 install meson
|
||||
|
||||
.ubuntu:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
FDO_DISTRIBUTION_TAG: '2024-01-12.0'
|
||||
FDO_DISTRIBUTION_VERSION: '22.04'
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
debhelper-compat
|
||||
findutils
|
||||
git
|
||||
libapparmor-dev
|
||||
libasound2-dev
|
||||
libavcodec-dev
|
||||
libavfilter-dev
|
||||
libavformat-dev
|
||||
libdbus-1-dev
|
||||
libbluetooth-dev
|
||||
libglib2.0-dev
|
||||
libgstreamer1.0-dev
|
||||
libgstreamer-plugins-base1.0-dev
|
||||
libsbc-dev
|
||||
libsdl2-dev
|
||||
libsnapd-glib-dev
|
||||
libudev-dev
|
||||
libva-dev
|
||||
libv4l-dev
|
||||
libx11-dev
|
||||
meson
|
||||
ninja-build
|
||||
pkg-config
|
||||
python3-docutils
|
||||
systemd
|
||||
# Uncommenting the following three lines and disabling the meson entry above
|
||||
# will re-enable use of Meson via pip but please consider using a newer distro
|
||||
# image first or making the build system compatible instead! This is because
|
||||
# using pip or another 3rd party repo defeats the point testing the particular
|
||||
# distro for regressions.
|
||||
# python3-pip
|
||||
# FDO_DISTRIBUTION_EXEC: >-
|
||||
# pip3 install meson
|
||||
|
||||
.alpine:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
FDO_DISTRIBUTION_TAG: '2024-09-20.0'
|
||||
FDO_DISTRIBUTION_VERSION: '3.20'
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
alsa-lib-dev
|
||||
avahi-dev
|
||||
bash
|
||||
bluez-dev
|
||||
gcc
|
||||
g++
|
||||
dbus-dev
|
||||
doxygen
|
||||
elogind-dev
|
||||
eudev-dev
|
||||
fdk-aac-dev
|
||||
git
|
||||
glib-dev
|
||||
graphviz
|
||||
gst-plugins-base-dev
|
||||
gstreamer-dev
|
||||
jack-dev
|
||||
libfreeaptx-dev
|
||||
libusb-dev
|
||||
libx11-dev
|
||||
meson
|
||||
modemmanager-dev
|
||||
ncurses-dev
|
||||
pulseaudio-dev
|
||||
readline-dev
|
||||
sbc-dev
|
||||
vulkan-loader-dev
|
||||
xmltoman
|
||||
|
||||
.coverity:
|
||||
variables:
|
||||
FDO_REPO_SUFFIX: 'coverity'
|
||||
FDO_BASE_IMAGE: registry.freedesktop.org/$FDO_UPSTREAM_REPO/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
curl
|
||||
FDO_DISTRIBUTION_EXEC: >-
|
||||
mkdir -p /opt ;
|
||||
cd /opt ;
|
||||
curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/cxx/linux64
|
||||
--form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN ;
|
||||
tar xf /tmp/cov-analysis-linux64.tgz ;
|
||||
mv cov-analysis-linux64-* coverity ;
|
||||
rm /tmp/cov-analysis-linux64.tgz
|
||||
rules:
|
||||
- if: $COVERITY != null
|
||||
|
||||
.not_coverity:
|
||||
rules:
|
||||
- if: $COVERITY == null
|
||||
|
||||
.build:
|
||||
before_script:
|
||||
# setup the environment
|
||||
- export BUILD_ID="$CI_JOB_ID"
|
||||
- export PREFIX="$PWD/prefix-$BUILD_ID"
|
||||
- export BUILD_DIR="$PWD/build-$BUILD_ID"
|
||||
- export XDG_RUNTIME_DIR="$(mktemp -p $PWD -d xdg-runtime-XXXXXX)"
|
||||
- |
|
||||
if [ -n "$FDO_CI_CONCURRENT" ]; then
|
||||
COMPILE_ARGS="-j$FDO_CI_CONCURRENT"
|
||||
export COMPILE_ARGS
|
||||
fi
|
||||
script:
|
||||
- echo "Building with meson options $MESON_OPTIONS"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- meson test -C "$BUILD_DIR" --no-rebuild
|
||||
- meson install -C "$BUILD_DIR" --no-rebuild
|
||||
artifacts:
|
||||
name: pipewire-$CI_COMMIT_SHA
|
||||
when: always
|
||||
paths:
|
||||
- build-*/meson-logs
|
||||
|
||||
container_ubuntu:
|
||||
extends:
|
||||
- .ubuntu
|
||||
- .fdo.container-build@ubuntu
|
||||
stage: container
|
||||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
|
||||
container_fedora:
|
||||
extends:
|
||||
- .fedora
|
||||
- .fdo.container-build@fedora
|
||||
stage: container
|
||||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
|
||||
container_alpine:
|
||||
extends:
|
||||
- .alpine
|
||||
- .fdo.container-build@alpine
|
||||
stage: container
|
||||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
|
||||
container_coverity:
|
||||
extends:
|
||||
- .fedora
|
||||
- .coverity
|
||||
- .fdo.container-build@fedora
|
||||
stage: container_coverity
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
|
||||
build_on_ubuntu:
|
||||
extends:
|
||||
- .ubuntu
|
||||
- .not_coverity
|
||||
- .fdo.distribution-image@ubuntu
|
||||
- .build
|
||||
stage: build
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=enabled"
|
||||
|
||||
.build_on_fedora:
|
||||
extends:
|
||||
- .fedora
|
||||
- .not_coverity
|
||||
- .fdo.distribution-image@fedora
|
||||
- .build
|
||||
stage: build
|
||||
|
||||
build_on_fedora:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
variables:
|
||||
MESON_OPTIONS: >-
|
||||
-Ddocs=enabled
|
||||
-Dman=enabled
|
||||
-Ddoc-prefix-value=/usr
|
||||
-Ddoc-sysconfdir-value=/etc
|
||||
-Dinstalled_tests=enabled
|
||||
-Dsystemd-system-service=enabled
|
||||
-Dbluez5-backend-hsphfpd=enabled
|
||||
-Daudiotestsrc=enabled
|
||||
-Dtest=enabled
|
||||
-Dvideotestsrc=enabled
|
||||
-Dvolume=enabled
|
||||
-Dvulkan=enabled
|
||||
-Dsdl2=enabled
|
||||
-Dsndfile=enabled
|
||||
-Dsession-managers=[]
|
||||
-Dsnap=disabled
|
||||
artifacts:
|
||||
name: pipewire-$CI_COMMIT_SHA
|
||||
when: always
|
||||
paths:
|
||||
- build-*/meson-logs
|
||||
- prefix-*
|
||||
|
||||
build_on_fedora_html_docs:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
variables:
|
||||
MESON_OPTIONS: >-
|
||||
-Ddocs=enabled
|
||||
-Dman=enabled
|
||||
-Ddoc-prefix-value=/usr
|
||||
-Ddoc-sysconfdir-value=/etc
|
||||
-Dinstalled_tests=enabled
|
||||
-Dsystemd-system-service=enabled
|
||||
-Dbluez5-backend-hsphfpd=enabled
|
||||
-Daudiotestsrc=enabled
|
||||
-Dtest=enabled
|
||||
-Dvideotestsrc=enabled
|
||||
-Dvolume=enabled
|
||||
-Dvulkan=enabled
|
||||
-Dsdl2=enabled
|
||||
-Dsndfile=enabled
|
||||
-Dsession-managers=[]
|
||||
before_script:
|
||||
- git fetch origin 1.0 1.2 master
|
||||
- git branch -f 1.0 origin/1.0
|
||||
- git clone -b 1.0 . branch-1.0
|
||||
- git branch -f 1.2 origin/1.2
|
||||
- git clone -b 1.2 . branch-1.2
|
||||
- git branch -f master origin/master
|
||||
- git clone -b master . branch-master
|
||||
- !reference [.build, before_script]
|
||||
script:
|
||||
- cd branch-1.0
|
||||
- meson setup builddir $MESON_OPTIONS
|
||||
- meson compile -C builddir doc/pipewire-docs
|
||||
- cd ..
|
||||
- cd branch-1.2
|
||||
- meson setup builddir $MESON_OPTIONS
|
||||
- meson compile -C builddir doc/pipewire-docs
|
||||
- cd ..
|
||||
- cd branch-master
|
||||
- meson setup builddir $MESON_OPTIONS
|
||||
- meson compile -C builddir doc/pipewire-docs
|
||||
artifacts:
|
||||
name: pipewire-$CI_COMMIT_SHA
|
||||
when: always
|
||||
paths:
|
||||
- branch-*/builddir/meson-logs
|
||||
- branch-*/builddir/doc/html
|
||||
rules:
|
||||
- !reference [pages, rules]
|
||||
|
||||
build_on_alpine:
|
||||
extends:
|
||||
- .alpine
|
||||
- .not_coverity
|
||||
- .fdo.distribution-image@alpine
|
||||
- .build
|
||||
stage: build
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=disabled -Dlogind=enabled -Dlogind-provider=libelogind"
|
||||
|
||||
# build with all auto() options enabled
|
||||
build_all:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
variables:
|
||||
# Fedora doesn't have libfreeaptx, lc3plus, lc3, or roc
|
||||
# libcamera has no stable API, so let's not chase that target
|
||||
MESON_OPTIONS: >-
|
||||
-Dauto_features=enabled
|
||||
-Dbluez5-codec-aptx=disabled
|
||||
-Dbluez5-codec-lc3plus=disabled
|
||||
-Dbluez5-codec-lc3=disabled
|
||||
-Droc=disabled
|
||||
-Dlibcamera=disabled
|
||||
-Dsession-managers=[]
|
||||
-Dc_args=['-UFASTPATH']
|
||||
-Dcpp_args=['-UFASTPATH']
|
||||
-Dsnap=disabled
|
||||
parallel:
|
||||
matrix:
|
||||
- CC: [gcc, clang]
|
||||
|
||||
# build with all options on auto() or their default values
|
||||
build_with_no_commandline_options:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=disabled"
|
||||
parallel:
|
||||
matrix:
|
||||
- CC: [gcc, clang]
|
||||
|
||||
# build with a set of options enabled or disabled
|
||||
build_with_custom_options:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
parallel:
|
||||
matrix:
|
||||
- MESON_OPTION: [docs, installed_tests, systemd-system-service, bluez5-backend-hsphfpd,
|
||||
audiotestsrc, test, videotestsrc, volume, vulkan, sdl2, sndfile]
|
||||
MESON_OPTION_VALUE: [enabled, disabled]
|
||||
script:
|
||||
- echo "Building with -D$MESON_OPTION=$MESON_OPTION_VALUE"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE" -Dsession-managers=[]
|
||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- meson test -C "$BUILD_DIR" --no-rebuild
|
||||
|
||||
build_with_asan_ubsan:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
script:
|
||||
- echo "Building with ASan and UBSan"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" -D debug=true -D optimization=g -D b_sanitize=address,undefined -D session-managers=[]
|
||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- env UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 meson test -C "$BUILD_DIR" --no-rebuild
|
||||
|
||||
# A release build with NDEBUG, all options on auto() but tests explicitly
|
||||
# enabled. This should show issues with tests failing due to different
|
||||
# optimization or relying on assert.
|
||||
build_release:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dtest=enabled -Dbuildtype=release -Db_ndebug=true -Dsession-managers=[] -Dsnap=disabled"
|
||||
parallel:
|
||||
matrix:
|
||||
- CC: [gcc, clang]
|
||||
|
||||
build_session_managers:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
script:
|
||||
- echo "Building with meson options $MESON_OPTIONS"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- meson install -C "$BUILD_DIR" --no-rebuild
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS -Dsnap=disabled"
|
||||
parallel:
|
||||
matrix:
|
||||
- SESSION_MANAGERS: ["[]", "wireplumber", "media-session", "media-session,wireplumber", "wireplumber,media-session" ]
|
||||
allow_failure: true
|
||||
|
||||
build_meson_prerelease:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
script:
|
||||
- dnf remove --assumeyes meson
|
||||
- pip3 install --upgrade --pre meson
|
||||
- echo "Building with meson options $MESON_OPTIONS"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- meson install -C "$BUILD_DIR" --no-rebuild
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session -Dsnap=disabled"
|
||||
allow_failure: true
|
||||
|
||||
build_meson_exact_release:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
script:
|
||||
- meson_version=$(head -n 5 meson.build | grep 'meson_version' | sed -e 's/.*\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/')
|
||||
- echo "Requiring meson version $meson_version"
|
||||
- test -n "$meson_version" || (echo "Meson version parser failed" && exit 1)
|
||||
- dnf remove --assumeyes meson
|
||||
# - pip3 uninstall --yes meson
|
||||
- pip3 install "meson==$meson_version"
|
||||
- echo "Building with meson options $MESON_OPTIONS"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- meson install -C "$BUILD_DIR" --no-rebuild
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=disabled"
|
||||
|
||||
valgrind:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
script:
|
||||
- echo "Building with meson options $MESON_OPTIONS"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||
- meson test -C "$BUILD_DIR" --setup=valgrind
|
||||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=[]"
|
||||
|
||||
build_with_coverity:
|
||||
extends:
|
||||
- .fedora
|
||||
- .coverity
|
||||
- .fdo.suffixed-image@fedora
|
||||
- .build
|
||||
stage: analysis
|
||||
script:
|
||||
- export PATH=/opt/coverity/bin:$PATH
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX"
|
||||
-Ddocs=disabled
|
||||
-Dbluez5-backend-hsphfpd=enabled
|
||||
-Daudiotestsrc=enabled
|
||||
-Dtest=enabled
|
||||
-Dvideotestsrc=enabled
|
||||
-Dvolume=enabled
|
||||
-Dvulkan=enabled
|
||||
-Dsdl2=enabled
|
||||
-Dsndfile=enabled
|
||||
-Dsession-managers=[]
|
||||
- cov-configure --config coverity_conf.xml
|
||||
--comptype gcc --compiler cc --template
|
||||
--xml-option=append_arg@C:--ppp_translator
|
||||
--xml-option=append_arg@C:"replace/_sd_deprecated_\s+=/ ="
|
||||
--xml-option=append_arg@C:--ppp_translator
|
||||
--xml-option=append_arg@C:"replace/GLIB_(DEPRECATED|AVAILABLE)_ENUMERATOR_IN_\d_\d\d(_FOR\(\w+\)|)\s+=/ ="
|
||||
--xml-option=append_arg@C:--ppp_translator
|
||||
--xml-option=append_arg@C:"replace/(__has_builtin|_GLIBCXX_HAS_BUILTIN)\(\w+\)/1"
|
||||
- cov-build --dir cov-int --config coverity_conf.xml meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- tar czf cov-int.tar.gz cov-int
|
||||
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
|
||||
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
|
||||
--form file=@cov-int.tar.gz --form version="`git describe --tags`"
|
||||
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID "
|
||||
artifacts:
|
||||
name: pipewire-coverity-$CI_COMMIT_SHA
|
||||
when: always
|
||||
paths:
|
||||
- build-*/meson-logs
|
||||
- cov-int/build-log.txt
|
||||
|
||||
shellcheck:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
stage: analysis
|
||||
variables:
|
||||
MESON_OPTIONS: >-
|
||||
-Dpipewire-v4l2=enabled
|
||||
-Dpipewire-jack=enabled
|
||||
script:
|
||||
- echo "Configuring with meson options $MESON_OPTIONS"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||
- shellcheck $(git ls-files '*.sh')
|
||||
- shellcheck $(grep -rl "#\!/.*bin/.*sh" "$BUILD_DIR")
|
||||
|
||||
spellcheck:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
stage: analysis
|
||||
script:
|
||||
- git ls-files | grep -v .gitlab-ci.yml | xargs -d '\n' sed -i 's/Pipewire/PipeWire/g'
|
||||
- git diff --exit-code || (echo "Please fix the above spelling mistakes" && exit 1)
|
||||
|
||||
doccheck:
|
||||
extends:
|
||||
- .build_on_fedora
|
||||
stage: analysis
|
||||
script:
|
||||
# Check that each pipewire module has a \subpage entry
|
||||
- git grep -h -o -e "\\\page page_module_\w\+" | cut -f2 -d ' ' > pipewire_module_pages
|
||||
- cat pipewire_module_pages
|
||||
- |
|
||||
for page in $(cat pipewire_module_pages); do
|
||||
git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/pipewire-modules.dox" && false)
|
||||
done
|
||||
|
||||
check_missing_headers:
|
||||
extends:
|
||||
- .fedora
|
||||
- .not_coverity
|
||||
- .fdo.distribution-image@fedora
|
||||
stage: analysis
|
||||
dependencies:
|
||||
- build_on_fedora
|
||||
script:
|
||||
- export PREFIX=`find -name prefix-*`
|
||||
- ./.gitlab/ci/check_missing_headers.sh
|
||||
|
||||
pages:
|
||||
extends:
|
||||
- .not_coverity
|
||||
stage: pages
|
||||
dependencies:
|
||||
- build_on_fedora_html_docs
|
||||
script:
|
||||
- mkdir public public/1.0 public/1.2 public/devel
|
||||
- cp -R branch-1.0/builddir/doc/html/* public/1.0/
|
||||
- cp -R branch-1.2/builddir/doc/html/* public/1.2/
|
||||
- cp -R branch-master/builddir/doc/html/* public/devel/
|
||||
- (cd public && ln -s 1.2/* .)
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'master'
|
||||
- if: $CI_COMMIT_BRANCH == '1.0'
|
||||
- if: $CI_COMMIT_BRANCH == '1.2'
|
28
.gitlab/ci/check_missing_headers.sh
Executable file
28
.gitlab/ci/check_missing_headers.sh
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script will tell you if there are headers in the source tree
|
||||
# that have not been installed in $PREFIX
|
||||
|
||||
LIST=""
|
||||
|
||||
for i in $(find spa/include -name '*.h' | sed s#spa/include/##);
|
||||
do
|
||||
[ -f "$PREFIX/include/spa-0.2/$i" ] || LIST="$i $LIST"
|
||||
done
|
||||
|
||||
for i in $(find src/pipewire -name '*.h' -a -not -name '*private.h' | sed s#src/##);
|
||||
do
|
||||
[ -f "$PREFIX/include/pipewire-0.3/$i" ] || LIST="$i $LIST"
|
||||
done
|
||||
|
||||
for i in $LIST;
|
||||
do
|
||||
echo "$i not installed"
|
||||
done
|
||||
|
||||
if [ "$LIST" != "" ];
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
0
.gitlab/issue_templates/.gitkeep
Normal file
0
.gitlab/issue_templates/.gitkeep
Normal file
43
.gitlab/issue_templates/bluetooth issue.md
Normal file
43
.gitlab/issue_templates/bluetooth issue.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!-- If you are filing this issue with a regular release please try master as it might already be fixed. -->
|
||||
|
||||
<!-- If you can, test also with Pulseaudio and list `pulseaudio --version`. -->
|
||||
|
||||
- PipeWire version (`pipewire --version`):
|
||||
- Distribution and distribution version (`PRETTY_NAME` from `/etc/os-release`):
|
||||
- Desktop Environment:
|
||||
- Kernel version (`uname -r`):
|
||||
- BlueZ version (`bluetoothctl --version`):
|
||||
- `lsusb`:
|
||||
```
|
||||
# paste the output of "lsusb" here
|
||||
```
|
||||
- Bluetooth devices:
|
||||
|
||||
```
|
||||
# paste the output of "bluetoothctl devices" here
|
||||
```
|
||||
|
||||
## Description of Problem:
|
||||
|
||||
|
||||
## How Reproducible:
|
||||
|
||||
|
||||
### Steps to Reproduce:
|
||||
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
|
||||
### Actual Results:
|
||||
|
||||
|
||||
### Expected Results:
|
||||
|
||||
|
||||
# Additional Info (as attachments):
|
||||
|
||||
- `pw-dump > pw-dump.log`:
|
||||
- Bluetooth debug log, see [here](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Troubleshooting#bluetooth):
|
29
.gitlab/issue_templates/issue.md
Normal file
29
.gitlab/issue_templates/issue.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!-- If you are filing this issue with a regular release please try master as it might already be fixed. -->
|
||||
|
||||
- PipeWire version (`pipewire --version`):
|
||||
- Distribution and distribution version (`PRETTY_NAME` from `/etc/os-release`):
|
||||
- Desktop Environment:
|
||||
- Kernel version (`uname -r`):
|
||||
|
||||
## Description of Problem:
|
||||
|
||||
|
||||
## How Reproducible:
|
||||
|
||||
|
||||
### Steps to Reproduce:
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
|
||||
### Actual Results:
|
||||
|
||||
|
||||
### Expected Results:
|
||||
|
||||
|
||||
# Additional Info (as attachments):
|
||||
|
||||
- `pw-dump > pw-dump.log`:
|
77
CODE_OF_CONDUCT.md
Normal file
77
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,77 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at pipewire-maintainers@lists.freedesktop.org. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
|
26
COPYING
Normal file
26
COPYING
Normal file
|
@ -0,0 +1,26 @@
|
|||
Copyright © 2018 Wim Taymans
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
The above is the version of the MIT "Expat" License used by X.org:
|
||||
|
||||
http://cgit.freedesktop.org/xorg/xserver/tree/COPYING
|
237
INSTALL.md
Normal file
237
INSTALL.md
Normal file
|
@ -0,0 +1,237 @@
|
|||
## Building
|
||||
|
||||
PipeWire uses a build tool called [*Meson*](https://mesonbuild.com) as a basis for its build
|
||||
process. It's a tool with some resemblance to Autotools and CMake. Meson
|
||||
again generates build files for a lower level build tool called [*Ninja*](https://ninja-build.org/),
|
||||
working in about the same level of abstraction as more familiar GNU Make
|
||||
does.
|
||||
|
||||
Meson uses a user-specified build directory and all files produced by Meson
|
||||
are in that build directory. This build directory will be called `builddir`
|
||||
in this document.
|
||||
|
||||
Generate the build files for Ninja:
|
||||
|
||||
```
|
||||
$ meson setup builddir
|
||||
```
|
||||
|
||||
For distribution-specific build dependencies, please check our
|
||||
[CI pipeline](https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/.gitlab-ci.yml)
|
||||
(search for `FDO_DISTRIBUTION_PACKAGES`). Note that some dependencies are
|
||||
optional and depend on options passed to meson.
|
||||
|
||||
Once this is done, the next step is to review the build options:
|
||||
|
||||
```
|
||||
$ meson configure builddir
|
||||
```
|
||||
|
||||
Define the installation prefix:
|
||||
|
||||
```
|
||||
$ meson configure builddir -Dprefix=/usr # Default: /usr/local
|
||||
```
|
||||
|
||||
PipeWire specific build options are listed in the "Project options"
|
||||
section. They are defined in `meson_options.txt`.
|
||||
|
||||
Finally, invoke the build:
|
||||
|
||||
```
|
||||
$ meson compile -C builddir
|
||||
```
|
||||
|
||||
Just to avoid any confusion: `autogen.sh` is a script invoked by *Jhbuild*,
|
||||
which orchestrates multi-component builds.
|
||||
|
||||
## Running
|
||||
|
||||
If you want to run PipeWire without installing it on your system, there is a
|
||||
script that you can run. This puts you in an environment in which PipeWire can
|
||||
be run from the build directory, and ALSA, PulseAudio and JACK applications
|
||||
will use the PipeWire emulation libraries automatically
|
||||
in this environment. You can get into this environment with:
|
||||
|
||||
```
|
||||
$ ./pw-uninstalled.sh -b builddir
|
||||
```
|
||||
|
||||
In most cases you would want to run the default pipewire daemon. Look
|
||||
below for how to make this daemon start automatically using systemd.
|
||||
If you want to run pipewire from the build directory, you can do this
|
||||
by doing:
|
||||
|
||||
```
|
||||
cd builddir/
|
||||
make run
|
||||
```
|
||||
|
||||
This will use the default config file to configure and start the daemon.
|
||||
The default config will also start `pipewire-media-session`, a default
|
||||
example media session and `pipewire-pulse`, a PulseAudio compatible server.
|
||||
|
||||
You can also enable more debugging with the `PIPEWIRE_DEBUG` and
|
||||
`WIREPLUMBER_DEBUG` environment variables like so:
|
||||
|
||||
```
|
||||
cd builddir/
|
||||
PIPEWIRE_DEBUG="D" WIREPLUMBER_DEBUG="D" make run
|
||||
```
|
||||
|
||||
You might have to stop the pipewire service/socket that might have been
|
||||
started already, with:
|
||||
|
||||
```
|
||||
systemctl --user stop pipewire.service \
|
||||
pipewire.socket \
|
||||
pipewire-media-session.service \
|
||||
pipewire-pulse.service \
|
||||
pipewire-pulse.socket
|
||||
```
|
||||
|
||||
## Installing
|
||||
|
||||
PipeWire comes with quite a bit of libraries and tools, run:
|
||||
|
||||
```
|
||||
meson install -C builddir
|
||||
```
|
||||
|
||||
to install everything onto the system into the specified prefix.
|
||||
Depending on the configured installation prefix, the above command
|
||||
may need to be run with elevated privileges (e.g. with `sudo`).
|
||||
Some additional steps will have to be performed to integrate
|
||||
with the distribution as shown below.
|
||||
|
||||
### PipeWire daemon
|
||||
|
||||
A correctly installed PipeWire system should have a pipewire
|
||||
process, a pipewire-media-session (or alternative) and an (optional)
|
||||
pipewire-pulse process running. PipeWire is usually started as a
|
||||
systemd unit using socket activation or as a service.
|
||||
|
||||
Configuration of the PipeWire daemon can be found in
|
||||
`/usr/share/pipewire/pipewire.conf`. Please refer to the comments in the
|
||||
config file for more information about the configuration options.
|
||||
|
||||
The daemon is started with:
|
||||
```
|
||||
systemctl --user start pipewire.service pipewire.socket
|
||||
```
|
||||
|
||||
If you did not start the media-session in pipewire.conf, you will
|
||||
also need to start it like this:
|
||||
```
|
||||
systemctl --user start pipewire-media-session.service
|
||||
```
|
||||
To make it start on system startup:
|
||||
```
|
||||
systemctl --user enable pipewire-media-session.service
|
||||
```
|
||||
you can write ```enable --now``` to start service immediately.
|
||||
|
||||
### ALSA plugin
|
||||
|
||||
The ALSA plugin is usually installed in:
|
||||
|
||||
On Fedora:
|
||||
```
|
||||
/usr/lib64/alsa-lib/libasound_module_pcm_pipewire.so
|
||||
```
|
||||
On Ubuntu:
|
||||
```
|
||||
/usr/lib/x86_64-linux-gnu/alsa-lib/libasound_module_pcm_pipewire.so
|
||||
```
|
||||
|
||||
There is also a config file installed in:
|
||||
|
||||
```
|
||||
/usr/share/alsa/alsa.conf.d/50-pipewire.conf
|
||||
```
|
||||
|
||||
The plugin will be picked up by alsa when the following files
|
||||
are in `/etc/alsa/conf.d/`:
|
||||
|
||||
```
|
||||
/etc/alsa/conf.d/50-pipewire.conf -> /usr/share/alsa/alsa.conf.d/50-pipewire.conf
|
||||
/etc/alsa/conf.d/99-pipewire-default.conf
|
||||
```
|
||||
|
||||
With this setup, `aplay -l` should list a pipewire device that can be used as
|
||||
a regular alsa device for playback and record.
|
||||
|
||||
### JACK emulation
|
||||
|
||||
PipeWire reimplements the 3 libraries that JACK applications use to make
|
||||
them run on top of PipeWire.
|
||||
|
||||
These libraries are found here:
|
||||
|
||||
```
|
||||
/usr/lib64/pipewire-0.3/jack/libjacknet.so -> libjacknet.so.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjacknet.so.0 -> libjacknet.so.0.304.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjacknet.so.0.304.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjackserver.so -> libjackserver.so.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjackserver.so.0 -> libjackserver.so.0.304.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjackserver.so.0.304.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjack.so -> libjack.so.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjack.so.0 -> libjack.so.0.304.0
|
||||
/usr/lib64/pipewire-0.3/jack/libjack.so.0.304.0
|
||||
|
||||
```
|
||||
|
||||
The provided `pw-jack` script uses `LD_LIBRARY_PATH` to set the library
|
||||
search path to these replacement libraries. This allows you to run
|
||||
jack apps on both the real JACK server or on PipeWire with the script.
|
||||
|
||||
It is also possible to completely replace the JACK libraries by adding
|
||||
a file `pipewire-jack-x86_64.conf` to `/etc/ld.so.conf.d/` with
|
||||
contents like:
|
||||
|
||||
```
|
||||
/usr/lib64/pipewire-0.3/jack/
|
||||
```
|
||||
|
||||
Note that when JACK is replaced by PipeWire, the SPA JACK plugin (installed
|
||||
in `/usr/lib64/spa-0.2/jack/libspa-jack.so`) is not useful anymore and
|
||||
distributions should make them conflict.
|
||||
|
||||
|
||||
### PulseAudio replacement
|
||||
|
||||
PipeWire reimplements the PulseAudio server protocol as a small service
|
||||
that runs on top of PipeWire.
|
||||
|
||||
The binary is normally placed here:
|
||||
|
||||
```
|
||||
/usr/bin/pipewire-pulse
|
||||
```
|
||||
|
||||
The server can be started with provided systemd activation files or
|
||||
from PipeWire itself. (See `/usr/share/pipewire/pipewire.conf`)
|
||||
|
||||
```
|
||||
systemctl --user start pipewire-pulse.service pipewire-pulse.socket
|
||||
```
|
||||
|
||||
You can also start additional PulseAudio servers listening on other
|
||||
sockets with the `-a` option. See `pipewire-pulse -h` for more info.
|
||||
|
||||
|
||||
## Uninstalling
|
||||
|
||||
To uninstall, run:
|
||||
|
||||
```
|
||||
ninja -C builddir uninstall
|
||||
```
|
||||
|
||||
Depending on the configured installation prefix, the above command
|
||||
may need to be run with elevated privileges (e.g. with `sudo`).
|
||||
|
||||
Note that at the time of writing uninstallation only works with the
|
||||
same build directory that was used for installation. Meson stores the
|
||||
list of installed files in the build directory, and this list is
|
||||
necessary for uninstallation to work.
|
11
LICENSE
Normal file
11
LICENSE
Normal file
|
@ -0,0 +1,11 @@
|
|||
All PipeWire source files are licensed under the MIT License.
|
||||
(see file COPYING for details)
|
||||
|
||||
With the exception of:
|
||||
|
||||
libspa-alsa.so in spa/plugins/alsa, which contains LGPL code from
|
||||
Pulseaudio and is thus licensed as LGPL.
|
||||
|
||||
libjackserver.so which links against the GPL2 jack/control.h, which
|
||||
makes it GPL2
|
||||
|
78
Makefile.in
Normal file
78
Makefile.in
Normal file
|
@ -0,0 +1,78 @@
|
|||
VERSION = @VERSION@
|
||||
TAG = @TAG@
|
||||
SOURCE_ROOT = @SOURCE_ROOT@
|
||||
BUILD_ROOT = @BUILD_ROOT@
|
||||
|
||||
all:
|
||||
ninja -C $(BUILD_ROOT)
|
||||
|
||||
install:
|
||||
ninja -C $(BUILD_ROOT) install
|
||||
|
||||
uninstall:
|
||||
ninja -C $(BUILD_ROOT) uninstall
|
||||
|
||||
clean:
|
||||
ninja -C $(BUILD_ROOT) clean
|
||||
|
||||
run: all
|
||||
SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
|
||||
SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
|
||||
PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules \
|
||||
PATH=$(BUILD_ROOT)/src/examples:$(PATH) \
|
||||
PIPEWIRE_CONFIG_DIR=$(BUILD_ROOT)/src/daemon \
|
||||
ACP_PATHS_DIR=$(SOURCE_ROOT)/spa/plugins/alsa/mixer/paths \
|
||||
ACP_PROFILES_DIR=$(SOURCE_ROOT)/spa/plugins/alsa/mixer/profile-sets \
|
||||
$(DBG) $(BUILD_ROOT)/src/daemon/pipewire-uninstalled
|
||||
|
||||
run-pulse: all
|
||||
SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
|
||||
SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
|
||||
PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules \
|
||||
PIPEWIRE_CONFIG_DIR=$(BUILD_ROOT)/src/daemon \
|
||||
ACP_PATHS_DIR=$(SOURCE_ROOT)/spa/plugins/alsa/mixer/paths \
|
||||
ACP_PROFILES_DIR=$(SOURCE_ROOT)/spa/plugins/alsa/mixer/profile-sets \
|
||||
$(DBG) $(BUILD_ROOT)/src/daemon/pipewire-pulse
|
||||
|
||||
gdb:
|
||||
$(MAKE) run DBG=gdb
|
||||
|
||||
valgrind:
|
||||
$(MAKE) run DBG="DISABLE_RTKIT=1 PIPEWIRE_DLCLOSE=false valgrind --trace-children=yes --leak-check=full"
|
||||
|
||||
test: all
|
||||
ninja -C $(BUILD_ROOT) test
|
||||
|
||||
benchmark: all
|
||||
ninja -C $(BUILD_ROOT) benchmark
|
||||
|
||||
monitor: all
|
||||
SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
|
||||
SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
|
||||
PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules/ \
|
||||
$(BUILD_ROOT)/src/tools/pw-mon
|
||||
|
||||
cli: all
|
||||
SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \
|
||||
SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \
|
||||
PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules/ \
|
||||
$(BUILD_ROOT)/src/tools/pw-cli
|
||||
|
||||
shell: all
|
||||
ninja -C $(BUILD_ROOT) pw-uninstalled
|
||||
|
||||
dist: all
|
||||
git archive --prefix=pipewire-$(VERSION)/ -o pipewire-$(VERSION).tar.gz $(TAG)
|
||||
|
||||
rpm: dist
|
||||
rpmbuild -ta pipewire-$(VERSION).tar.gz
|
||||
|
||||
publish: all
|
||||
git branch -D gh-pages 2>/dev/null || true && \
|
||||
git branch -D draft 2>/dev/null || true && \
|
||||
git checkout -b draft && \
|
||||
git add -f $(BUILD_ROOT)/doc/html && \
|
||||
git commit -anm "Deploy on gh-pages" && \
|
||||
git subtree split --prefix $(BUILD_ROOT)/doc/html -b gh-pages && \
|
||||
git push --force origin gh-pages:gh-pages && \
|
||||
git checkout work 2>/dev/null
|
217
README.md
Normal file
217
README.md
Normal file
|
@ -0,0 +1,217 @@
|
|||
# PipeWire
|
||||
|
||||
[PipeWire](https://pipewire.org) is a server and user space API to
|
||||
deal with multimedia pipelines. This includes:
|
||||
|
||||
- Making available sources of video (such as from a capture devices or
|
||||
application provided streams) and multiplexing this with
|
||||
clients.
|
||||
- Accessing sources of video for consumption.
|
||||
- Generating graphs for audio and video processing.
|
||||
|
||||
Nodes in the graph can be implemented as separate processes,
|
||||
communicating with sockets and exchanging multimedia content using fd
|
||||
passing.
|
||||
|
||||
## Building and installation
|
||||
|
||||
The preferred way to install PipeWire is to install it with your
|
||||
distribution package system. This ensures PipeWire is integrated
|
||||
into the rest of your system for the best experience.
|
||||
|
||||
If you want to build and install PipeWire yourself, refer to
|
||||
[install](INSTALL.md) for instructions.
|
||||
|
||||
## Usage
|
||||
|
||||
The most important purpose of PipeWire is to run your favorite apps.
|
||||
|
||||
Some applications use the native PipeWire API, such as most compositors
|
||||
(gnome-shell, wayland, ...) to implement screen sharing. These apps will
|
||||
just work automatically.
|
||||
|
||||
Most audio applications can use either ALSA, JACK or PulseAudio as a
|
||||
backend. PipeWire provides support for all 3 backends. Depending on how
|
||||
your distribution has configured things this should just work automatically
|
||||
or with the provided scripts shown below.
|
||||
|
||||
PipeWire can use environment variables to control the behaviour of
|
||||
applications:
|
||||
|
||||
* `PIPEWIRE_DEBUG=<level>` to increase the debug level (or use one of
|
||||
`XEWIDT` for none, error, warnings, info,
|
||||
debug, or trace, respectively).
|
||||
* `PIPEWIRE_LOG=<filename>` to redirect log to filename
|
||||
* `PIPEWIRE_LOG_SYSTEMD=false` to disable logging to systemd journal
|
||||
* `PIPEWIRE_LATENCY=<num/denom>` to configure latency as a fraction. 10/1000
|
||||
configures a 10ms latency. Usually this is
|
||||
expressed as a fraction of the samplerate,
|
||||
like 256/48000, which uses 256 samples at a
|
||||
samplerate of 48KHz for a latency of 5.33ms.
|
||||
This function does not attempt to configure
|
||||
the samplerate.
|
||||
* `PIPEWIRE_RATE=<num/denom>` to configure a rate for the graph.
|
||||
* `PIPEWIRE_QUANTUM=<num/denom>` to configure latency as a fraction and a
|
||||
samplerate. This function will force the graph samplerate to
|
||||
`denom` and force the specified `num` as the buffer size.
|
||||
* `PIPEWIRE_NODE=<id>` to request a link to the specified node. The
|
||||
id can be a node.name or object.serial of the target node.
|
||||
|
||||
### Using tools
|
||||
|
||||
`pw-cat` can be used to play and record audio and midi. Use `pw-cat -h` to get
|
||||
some more help. There are some aliases like `pw-play` and `pw-record` to make
|
||||
things easier:
|
||||
|
||||
```
|
||||
$ pw-play /home/wim/data/01.\ Firepower.wav
|
||||
```
|
||||
|
||||
### Running JACK applications
|
||||
|
||||
Depending on how the system was configured, you can either run PipeWire and
|
||||
JACK side-by-side or have PipeWire take over the functionality of JACK
|
||||
completely.
|
||||
|
||||
In dual mode, JACK apps will by default use the JACK server. To direct a JACK
|
||||
app to PipeWire, you can use the `pw-jack` script like this:
|
||||
|
||||
```
|
||||
$ pw-jack <appname>
|
||||
```
|
||||
|
||||
If you replaced JACK with PipeWire completely, `pw-jack` does not have any
|
||||
effect and can be omitted.
|
||||
|
||||
JACK applications will automatically use the buffer-size chosen by the
|
||||
server. You can force a maximum buffer size (latency) by setting the
|
||||
`PIPEWIRE_LATENCY` environment variable like so:
|
||||
|
||||
```
|
||||
PIPEWIRE_LATENCY=128/48000 jack_simple_client
|
||||
```
|
||||
Requests the `jack_simple_client` to run with a buffer of 128 or
|
||||
less samples.
|
||||
|
||||
|
||||
### Running PulseAudio applications
|
||||
|
||||
PipeWire can run a PulseAudio compatible replacement server. You can't
|
||||
use both servers at the same time. Usually your package manager will
|
||||
make the server conflict so that you can only install one or the
|
||||
other.
|
||||
|
||||
PulseAudio applications still use the regular PulseAudio client
|
||||
libraries and you don't need to do anything else than change the
|
||||
server implementation.
|
||||
|
||||
A successful swap of the server can be verified by checking the
|
||||
output of
|
||||
|
||||
```
|
||||
pactl info
|
||||
```
|
||||
It should include the string:
|
||||
```
|
||||
...
|
||||
Server Name: PulseAudio (on PipeWire 0.3.x)
|
||||
...
|
||||
```
|
||||
|
||||
You can use pavucontrol to change profiles and ports, change volumes
|
||||
or redirect streams, just like with PulseAudio.
|
||||
|
||||
|
||||
### Running ALSA applications
|
||||
|
||||
If the PipeWire alsa module is installed, it can be seen with
|
||||
|
||||
```
|
||||
$ aplay -L
|
||||
```
|
||||
|
||||
ALSA applications can then use the `pipewire:` device to use PipeWire
|
||||
as the audio system.
|
||||
|
||||
### Running GStreamer applications
|
||||
|
||||
PipeWire includes 2 GStreamer elements called `pipewiresrc` and
|
||||
`pipewiresink`. They can be used in pipelines such as this:
|
||||
|
||||
```
|
||||
$ gst-launch-1.0 pipewiresrc ! videoconvert ! autovideosink
|
||||
```
|
||||
|
||||
Or to play a beeping sound:
|
||||
|
||||
```
|
||||
$ gst-launch-1.0 audiotestsrc ! pipewiresink
|
||||
```
|
||||
|
||||
PipeWire provides a device monitor as well so that
|
||||
|
||||
```
|
||||
$ gst-device-monitor-1.0
|
||||
```
|
||||
|
||||
shows the PipeWire devices and applications like cheese will
|
||||
automatically use the PipeWire video source when possible.
|
||||
|
||||
### Inspecting the PipeWire state
|
||||
|
||||
To inspect and manipulate the PipeWire graph via GUI, you can use [Helvum](https://gitlab.freedesktop.org/ryuukyu/helvum).
|
||||
|
||||
Alternatively, you can use use one of the excellent JACK tools, such as `Carla`,
|
||||
`catia`, `qjackctl`, ...
|
||||
However, you will not be able to see all features like the video
|
||||
ports.
|
||||
|
||||
`pw-mon` dumps and monitors the state of the PipeWire daemon.
|
||||
|
||||
`pw-dot` can dump a graph of the pipeline, check out the help for
|
||||
how to do this.
|
||||
|
||||
`pw-top` monitors the real-time status of the graph. This is handy to
|
||||
find out what clients are running and how much DSP resources they
|
||||
use.
|
||||
|
||||
`pw-dump` dumps the state of the PipeWire daemon in JSON format. This
|
||||
can be used to find out the properties and parameters of the objects
|
||||
in the PipeWire daemon.
|
||||
|
||||
There is a more complicated tool to inspect the state of the server
|
||||
with `pw-cli`. This tool can be used interactively or it can execute
|
||||
single commands like this to get the server information:
|
||||
|
||||
```
|
||||
$ pw-cli info 0
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Find tutorials and design documentation [here](doc/index.dox).
|
||||
|
||||
The (incomplete) autogenerated API docs are [here](https://docs.pipewire.org).
|
||||
|
||||
The Wiki can be found [here](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home)
|
||||
|
||||
## Contributing
|
||||
|
||||
PipeWire is Free Software and is developed in the open. It is mostly
|
||||
licensed under the [MIT license](COPYING). Check [LICENSE](LICENSE) for
|
||||
more details about the exceptions.
|
||||
|
||||
Contributors are encouraged to submit merge requests or file bugs on
|
||||
[gitlab](https://gitlab.freedesktop.org/pipewire).
|
||||
|
||||
Join us on IRC at #pipewire on [OFTC](https://www.oftc.net/).
|
||||
|
||||
We adhere to the Contributor Covenant for our [code of conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
[Donate using Liberapay](https://liberapay.com/PipeWire/donate).
|
||||
|
||||
## Getting help
|
||||
|
||||
You can ask for help on the IRC channel (see above). You can also ask
|
||||
questions by [raising](https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/new)
|
||||
a gitlab issue.
|
18
autogen.sh
Executable file
18
autogen.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Only there to make jhbuild happy
|
||||
|
||||
if [ -z "$MESON" ]; then
|
||||
MESON=$(which meson)
|
||||
fi
|
||||
if [ -z "$MESON" ]; then
|
||||
echo "error: Meson not found."
|
||||
echo "Install meson to configure and build PipeWire. If meson" \
|
||||
"is already installed, set the environment variable MESON" \
|
||||
"to the binary's path."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
mkdir -p builddir
|
||||
$MESON setup "$@" builddir # use 'autogen.sh --reconfigure' to update
|
||||
ln -sf builddir/Makefile Makefile
|
76
doc/Doxyfile.in
Normal file
76
doc/Doxyfile.in
Normal file
|
@ -0,0 +1,76 @@
|
|||
PROJECT_NAME = PipeWire
|
||||
PROJECT_NUMBER = @PACKAGE_VERSION@
|
||||
OUTPUT_DIRECTORY = "@output_directory@"
|
||||
FULL_PATH_NAMES = YES
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
TAB_SIZE = 8
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_STATIC = YES
|
||||
STRIP_FROM_PATH = @path_prefixes@
|
||||
STRIP_FROM_INC_PATH = @path_prefixes@
|
||||
SHOW_FILES = NO
|
||||
SHOW_INCLUDE_FILES = NO
|
||||
GENERATE_TODOLIST = NO
|
||||
GENERATE_TESTLIST = NO
|
||||
GENERATE_BUGLIST = NO
|
||||
GENERATE_DEPRECATEDLIST= NO
|
||||
QUIET = YES
|
||||
WARN_NO_PARAMDOC = YES
|
||||
HAVE_DOT = @HAVE_DOT@
|
||||
INPUT = @inputs@
|
||||
FILTER_PATTERNS = "*.c=@c_input_filter@" "*.h=@h_input_filter@" "*.md=@md_input_filter@"
|
||||
FILE_PATTERNS = "*.h" "*.c"
|
||||
RECURSIVE = YES
|
||||
EXAMPLE_PATH = "@top_srcdir@/src/examples" \
|
||||
"@top_srcdir@/spa/examples" \
|
||||
"@top_srcdir@/doc/examples" \
|
||||
"@top_srcdir@/doc/dox"
|
||||
EXAMPLE_PATTERNS = "*.c" "*.inc"
|
||||
|
||||
GENERATE_MAN = YES
|
||||
MAN_EXTENSION = 3
|
||||
|
||||
REFERENCED_BY_RELATION = NO
|
||||
REFERENCES_RELATION = NO
|
||||
IGNORE_PREFIX = pw_ \
|
||||
PW_ \
|
||||
spa_ \
|
||||
SPA_
|
||||
GENERATE_TREEVIEW = YES
|
||||
SEARCHENGINE = YES
|
||||
GENERATE_LATEX = NO
|
||||
|
||||
TOC_INCLUDE_HEADINGS = 0
|
||||
LAYOUT_FILE = @layout@
|
||||
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = PA_C_DECL_BEGIN= \
|
||||
PA_C_DECL_END= \
|
||||
__USE_ISOC11 \
|
||||
SPA_EXPORT \
|
||||
SPA_PRINTF_FUNC \
|
||||
SPA_DEPRECATED \
|
||||
SPA_SENTINEL \
|
||||
SPA_UNUSED \
|
||||
SPA_NORETURN \
|
||||
SPA_RESTRICT
|
||||
HTML_EXTRA_STYLESHEET = @cssfiles@
|
||||
|
||||
MAX_INITIALIZER_LINES = 1
|
||||
SORT_MEMBER_DOCS = NO
|
||||
|
||||
CALL_GRAPH = NO
|
||||
CALLER_GRAPH = NO
|
||||
CLASS_GRAPH = NO
|
||||
COLLABORATION_GRAPH = NO
|
||||
GROUP_GRAPHS = NO
|
||||
INCLUDED_BY_GRAPH = NO
|
||||
INCLUDE_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = NO
|
||||
DIRECTORY_GRAPH = NO
|
||||
TEMPLATE_RELATIONS = NO
|
||||
|
||||
# Fix up some apparent Doxygen mis-parsing
|
||||
EXCLUDE_SYMBOLS = "desc" "methods" "msgid_plural" "n" "name" "props" "utils" "start"
|
269
doc/DoxygenLayout.xml
Normal file
269
doc/DoxygenLayout.xml
Normal file
|
@ -0,0 +1,269 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doxygenlayout version="1.0">
|
||||
<!-- Generated by doxygen 1.10.0 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="Pages" intro=""/>
|
||||
<tab type="topics" visible="yes" title="API Reference" intro=""/>
|
||||
<tab type="modules" visible="yes" title="API Reference" intro="">
|
||||
<tab type="modulelist" visible="yes" title="" intro=""/>
|
||||
<tab type="modulemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="namespaces" visible="no" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="concepts" visible="no" title="">
|
||||
</tab>
|
||||
<tab type="interfaces" visible="no" title="">
|
||||
<tab type="interfacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="yes" title="">
|
||||
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="structs" visible="no" title="">
|
||||
<tab type="structlist" visible="yes" title="" intro=""/>
|
||||
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
</tab>
|
||||
<tab type="exceptions" visible="no" title="">
|
||||
<tab type="exceptionlist" visible="yes" title="" intro=""/>
|
||||
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="files" visible="yes" title="">
|
||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||
<tab type="globals" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="examples" visible="yes" title="" intro=""/>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="no"/>
|
||||
<collaborationgraph visible="no"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<publicslots title=""/>
|
||||
<signals title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<protectedtypes title=""/>
|
||||
<protectedslots title=""/>
|
||||
<protectedmethods title=""/>
|
||||
<protectedstaticmethods title=""/>
|
||||
<protectedattributes title=""/>
|
||||
<protectedstaticattributes title=""/>
|
||||
<packagetypes title=""/>
|
||||
<packagemethods title=""/>
|
||||
<packagestaticmethods title=""/>
|
||||
<packageattributes title=""/>
|
||||
<packagestaticattributes title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
<privatetypes title=""/>
|
||||
<privateslots title=""/>
|
||||
<privatemethods title=""/>
|
||||
<privatestaticmethods title=""/>
|
||||
<privateattributes title=""/>
|
||||
<privatestaticattributes title=""/>
|
||||
<friends title=""/>
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<constructors title=""/>
|
||||
<functions title=""/>
|
||||
<related title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a concept page -->
|
||||
<concept>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<definition visible="yes" title=""/>
|
||||
<detaileddescription title=""/>
|
||||
<authorsection visible="yes"/>
|
||||
</concept>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="no"/>
|
||||
<includedbygraph visible="no"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="no"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<defines title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<defines title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a C++20 module page -->
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<enums title=""/>
|
||||
<typedefs title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
</module>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="no"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
60
doc/custom.css
Normal file
60
doc/custom.css
Normal file
|
@ -0,0 +1,60 @@
|
|||
:root {
|
||||
/* --page-background-color: #729fcf; */
|
||||
--primary-color: #729fcf;
|
||||
--primary-dark-color: #729fcf;
|
||||
--header-background: #729fcf;
|
||||
--header-foreground: rgba(255, 255, 255, 0.7);
|
||||
--font-family: 'Source Sans Pro', 'Source Sans', sans-serif;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--code-background: #f5f5f5;
|
||||
--code-foreground: #333333;
|
||||
--fragment-background: #f5f5f5;
|
||||
--fragment-foreground: #333333;
|
||||
--fragment-keyword: #c7254e;
|
||||
--fragment-link: #729fcf;
|
||||
}
|
||||
}
|
||||
|
||||
#nav-tree .arrow {
|
||||
opacity: 1;
|
||||
padding-right: 0.25em;
|
||||
}
|
||||
|
||||
.textblock h1 {
|
||||
font-size: 150%;
|
||||
border-bottom: 1px solid var(--page-foreground-color);
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.textblock h2 {
|
||||
font-size: 120%;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.textblock h3, .textblock h4, .textblock h5, .textblock h6 {
|
||||
font-size: 100%;
|
||||
font-style: italic;
|
||||
font-size: medium;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.textblock dl.section dd {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
ul.multicol li {
|
||||
word-break: break-word;
|
||||
padding-left: 3em;
|
||||
text-indent: -3em;
|
||||
}
|
||||
|
||||
ul.multicol li a.el {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div.contents div.toc li {
|
||||
word-break: break-word;
|
||||
padding-left: 2em;
|
||||
text-indent: -2em;
|
||||
}
|
90
doc/dox/api/index.dox
Normal file
90
doc/dox/api/index.dox
Normal file
|
@ -0,0 +1,90 @@
|
|||
/** \page page_api PipeWire API
|
||||
|
||||
The PipeWire API consists of several parts:
|
||||
|
||||
- The \ref pw_stream for a convenient way to send and receive data streams from/to PipeWire.
|
||||
|
||||
- The \ref pw_filter for a convenient way to implement processing filters.
|
||||
|
||||
- The \ref api_pw_core to access a PipeWire instance. This API is used
|
||||
by all clients that need to communicate with the \ref page_daemon and provides
|
||||
the necessary structs to interface with the daemon.
|
||||
|
||||
- The \ref api_pw_impl is primarily used by the \ref page_daemon itself but also by the
|
||||
\ref page_session_manager and modules/extensions that need to build objects in
|
||||
the graph.
|
||||
|
||||
- The \ref api_pw_util containing various utility functions and structures.
|
||||
|
||||
- The \ref api_pw_ext for interfacing with certain extension modules.
|
||||
|
||||
The APIs work through proxy objects, so that calling a method on an object
|
||||
invokes that same method on the remote side. Marshalling and de-marshalling is
|
||||
handled transparently by the \ref page_module_protocol_native.
|
||||
The below graph illustrates this approach:
|
||||
|
||||
\dot
|
||||
digraph API {
|
||||
compound=true;
|
||||
node [shape="box"];
|
||||
rankdir="RL";
|
||||
|
||||
subgraph cluster_daemon {
|
||||
rankdir="TB";
|
||||
label="PipeWire daemon";
|
||||
style="dashed";
|
||||
|
||||
impl_core [label="Core Impl. Object"];
|
||||
impl_device [label="Device Impl. Object"];
|
||||
impl_node [label="Node Impl. Object"];
|
||||
}
|
||||
|
||||
subgraph cluster_client {
|
||||
rankdir="TB";
|
||||
label="PipeWire client";
|
||||
style="dashed";
|
||||
|
||||
obj_core [label="Core Object"];
|
||||
obj_device [label="Device Object"];
|
||||
obj_node [label="Node Object"];
|
||||
}
|
||||
|
||||
obj_core -> impl_core;
|
||||
obj_device -> impl_device;
|
||||
obj_node -> impl_node;
|
||||
|
||||
}
|
||||
\enddot
|
||||
|
||||
It is common for clients to use both the \ref api_pw_core and the \ref api_pw_impl
|
||||
and both APIs are provided by the same library.
|
||||
|
||||
- \subpage page_spa
|
||||
- \subpage page_client_impl
|
||||
- \subpage page_proxy
|
||||
- \subpage page_streams
|
||||
- \subpage page_thread_loop
|
||||
|
||||
|
||||
\addtogroup api_pw_core Core API
|
||||
|
||||
The Core API to access a PipeWire instance. This API is used by all
|
||||
clients to communicate with the \ref page_daemon.
|
||||
|
||||
If you are familiar with Wayland implementation, the Core API is
|
||||
roughly equivalent to libwayland-client.
|
||||
|
||||
See: \ref page_api
|
||||
|
||||
|
||||
\addtogroup api_pw_impl Implementation API
|
||||
|
||||
The implementation API provides the tools to build new objects and
|
||||
modules.
|
||||
|
||||
If you are familiar with Wayland implementation, the Implementation API is
|
||||
roughly equivalent to libwayland-server.
|
||||
|
||||
See: \ref page_api
|
||||
|
||||
*/
|
71
doc/dox/api/spa-buffer.dox
Normal file
71
doc/dox/api/spa-buffer.dox
Normal file
|
@ -0,0 +1,71 @@
|
|||
/** \page page_spa_buffer SPA Buffers
|
||||
|
||||
> What is the array of `spa_data` in `spa_buffer`?
|
||||
|
||||
A \ref spa_buffer "SPA Buffer" contains metadata and data. There can be many metadata items (headers, color info, cursor position, etc) in the buffer. The metadata items are stored in the metas array. In the same way, the buffer can contain multiple data blocks in the datas array. Each data block is, for example, a video plane or an audio channel. There are `n_datas` of those blocks.
|
||||
|
||||
> What is the `void*` data pointer in `spa_data`?
|
||||
|
||||
The data information either has a file descriptor or a data pointer. The type of the `spa_data` tells you what to expect. For a file descriptor, the data pointer can optionally be set when the FD is mapped into memory. Otherwise the user has to mmap the data themselves.
|
||||
|
||||
Also associated with each `spa_data` is a chunk, which is read/write and contains the valid region in the `spa_data` (offset, size, stride and some flags).
|
||||
|
||||
The reason why is this set up like this is that the metadata memory, the data and chunks can be directly transported in shared memory while the buffer structure can be negotiated separately (describing the shared memory). This way buffers can be shared but no process can destroy the structure of the buffers.
|
||||
|
||||
|
||||
* The buffer skeleton is placed in memory like below and can
|
||||
* be accessed as a regular structure.
|
||||
*
|
||||
* +==============================+
|
||||
* | struct spa_buffer |
|
||||
* | uint32_t n_metas | number of metas
|
||||
* | uint32_t n_datas | number of datas
|
||||
* +-| struct spa_meta *metas | pointer to array of metas
|
||||
* +|-| struct spa_data *datas | pointer to array of datas
|
||||
* || +------------------------------+
|
||||
* |+>| struct spa_meta |
|
||||
* | | uint32_t type | metadata
|
||||
* | | uint32_t size | size of metadata
|
||||
* +|--| void *data | pointer to metadata
|
||||
* || | ... <n_metas> | more spa_meta follow
|
||||
* || +------------------------------+
|
||||
* |+->| struct spa_data |
|
||||
* | | uint32_t type | memory type
|
||||
* | | uint32_t flags |
|
||||
* | | int fd | fd of shared memory block
|
||||
* | | uint32_t mapoffset | offset in shared memory of data
|
||||
* | | uint32_t maxsize | size of data block
|
||||
* | +-| void *data | pointer to data
|
||||
* |+|-| struct spa_chunk *chunk | pointer to chunk
|
||||
* ||| | ... <n_datas> | more spa_data follow
|
||||
* ||| +==============================+
|
||||
* VVV
|
||||
*
|
||||
* metadata, chunk and memory can either be placed right
|
||||
* after the skeleton (inlined) or in a separate piece of memory.
|
||||
*
|
||||
* vvv
|
||||
* ||| +==============================+
|
||||
* +-->| meta data memory | metadata memory, 8 byte aligned
|
||||
* || | ... <n_metas> |
|
||||
* || +------------------------------+
|
||||
* +->| struct spa_chunk | memory for n_datas chunks
|
||||
* | | uint32_t offset |
|
||||
* | | uint32_t size |
|
||||
* | | int32_t stride |
|
||||
* | | int32_t dummy |
|
||||
* | | ... <n_datas> chunks |
|
||||
* | +------------------------------+
|
||||
* +>| data | memory for n_datas data, aligned
|
||||
* | ... <n_datas> blocks | according to alignments
|
||||
* +==============================+
|
||||
|
||||
Taken from [here](https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/11f95fe11e07192cec19fddb4fafc708e023e49c/spa/include/spa/buffer/alloc.h).
|
||||
|
||||
|
||||
|
||||
\addtogroup spa_buffer
|
||||
|
||||
See: \ref page_spa_buffer
|
||||
|
||||
*/
|
35
doc/dox/api/spa-design.dox
Normal file
35
doc/dox/api/spa-design.dox
Normal file
|
@ -0,0 +1,35 @@
|
|||
/** \page page_spa_design SPA Design
|
||||
|
||||
# Conventions
|
||||
|
||||
## Types
|
||||
|
||||
Types are generally divided into two categories:
|
||||
|
||||
- String types: They identify interfaces and highlevel object types.
|
||||
- Integer types: These are enumerations used in the parts where high
|
||||
performance/ease of use/low space overhead is needed.
|
||||
|
||||
The SPA type is system is static and very simple but still allows you
|
||||
to make and introspect complex object type hierarchies.
|
||||
|
||||
See the type system docs for more info.
|
||||
|
||||
## Error Codes
|
||||
|
||||
SPA uses negative integers as errno style error codes. Functions that return an
|
||||
int result code generated an error when < 0. `spa_strerror()` can be used to
|
||||
get a string representation of the error code.
|
||||
|
||||
SPA also has a way to encode asynchronous results. This is done by setting a
|
||||
high bit (bit 30, the `ASYNC_BIT`) in the result code and a sequence number
|
||||
in the lower bits. This result is normally identified as a positive success
|
||||
result code and the sequence number can later be matched to the completion
|
||||
event.
|
||||
|
||||
## Useful Macros
|
||||
|
||||
SPA comes with some useful macros defined in `<spa/utils/defs.h>` and a
|
||||
number of utility functions, see \ref spa_utils.
|
||||
|
||||
*/
|
88
doc/dox/api/spa-index.dox
Normal file
88
doc/dox/api/spa-index.dox
Normal file
|
@ -0,0 +1,88 @@
|
|||
/** \page page_spa SPA (Simple Plugin API)
|
||||
|
||||
\ref api_spa (Simple Plugin API) is an extensible API to implement all kinds of
|
||||
plugins.
|
||||
|
||||
It is inspired by many other plugin APIs, mostly LV2 and
|
||||
GStreamer. SPA provides two parts:
|
||||
|
||||
- A header-only API with no external dependencies.
|
||||
- A set of support libraries ("plugins") for commonly used functionality.
|
||||
|
||||
The usual approach is that PipeWire and PipeWire clients can use the
|
||||
header-only functions to interact with the plugins. Those plugins are
|
||||
usually loaded at runtime (through `dlopen(3)`).
|
||||
|
||||
|
||||
# Motivation
|
||||
|
||||
SPA was designed with the following goals in mind:
|
||||
|
||||
- No dependencies, SPA is shipped as a set of header files that have no dependencies except for the standard C library.
|
||||
- Very efficient both in space and in time.
|
||||
- Very configurable and usable in many different environments. All aspects
|
||||
of the plugin environment can be configured and changed, like logging,
|
||||
poll loops, system calls, etc.
|
||||
- Consistent API.
|
||||
- Extensible; new API can be added with minimal effort, existing API can be updated and versioned.
|
||||
|
||||
The original user of SPA is PipeWire, which uses SPA to implement the
|
||||
low-level multimedia processing plugins, device detection, mainloops, CPU
|
||||
detection, logging, among other things. SPA however can be used outside
|
||||
of PipeWire with minimal problems.
|
||||
|
||||
|
||||
# The SPA Header-Only API
|
||||
|
||||
A very simple example on how SPA headers work are the \ref spa_utils, a set
|
||||
of utilities commonly required by C projects. SPA functions use the `spa_`
|
||||
namespace and are easy to identify.
|
||||
|
||||
\code
|
||||
/* cc $(pkg-config --cflags libspa-0.2) -o spa-test spa-test.c */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <spa/utils/string.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t val;
|
||||
|
||||
if (spa_atoi32(argv[1], &val, 16))
|
||||
printf("argv[1] is hex %#x\n", val);
|
||||
else
|
||||
printf("argv[1] is not a hex number\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
|
||||
# SPA Plugins
|
||||
|
||||
SPA plugins are shared libraries (`.so` files) that can be loaded at
|
||||
runtime. Each library provides one or more "factories", each of which may
|
||||
implement several "interfaces". Code that uses SPA plugins then uses those
|
||||
interfaces (through SPA header files) to interact with the plugin.
|
||||
|
||||
For example, the PipeWire daemon can load the normal `printf`-based logger
|
||||
or a systemd journal-based logger. Both of those provide the \ref spa_log
|
||||
interface and once instantiated, PipeWire no longer has to differentiate
|
||||
between the two logging facilities.
|
||||
|
||||
Please see \ref page_spa_plugins for the details on how to use SPA plugins.
|
||||
|
||||
|
||||
# Further details
|
||||
|
||||
- \ref api_spa
|
||||
- \subpage page_spa_design
|
||||
- \subpage page_spa_plugins
|
||||
- \subpage page_spa_pod
|
||||
- \subpage page_spa_buffer
|
||||
|
||||
|
||||
\addtogroup api_spa
|
||||
|
||||
See: \ref page_spa, \ref page_spa_design
|
||||
|
||||
*/
|
360
doc/dox/api/spa-plugins.dox
Normal file
360
doc/dox/api/spa-plugins.dox
Normal file
|
@ -0,0 +1,360 @@
|
|||
/** \page page_spa_plugins SPA Plugins
|
||||
|
||||
\ref spa_handle "SPA plugins" are dynamically loadable objects that contain objects and interfaces that
|
||||
can be introspected and used at runtime in any application. This document
|
||||
introduces the basic concepts of SPA plugins. It first covers using the API
|
||||
and then talks about implementing new plugins.
|
||||
|
||||
|
||||
# Outline
|
||||
|
||||
To use a plugin, the following steps are required:
|
||||
|
||||
- **Load** the shared library.
|
||||
- **Enumerate** the available factories.
|
||||
- **Enumerate** the interfaces in each factory.
|
||||
- **Instantiate** the desired interface.
|
||||
- **Use** the interface-specific functions.
|
||||
|
||||
In pseudo-code, loading a logger interface looks like this:
|
||||
|
||||
\code{.py}
|
||||
handle = dlopen("$SPA_PLUGIN_DIR/support/libspa-support.so")
|
||||
factory_enumeration_func = dlsym(handle, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)
|
||||
spa_log *logger = NULL
|
||||
|
||||
while True:
|
||||
factory = get_next_factory(factory_enumeration_func):
|
||||
if factory != SPA_NAME_SUPPORT_LOG: # <spa/utils/name.h>
|
||||
continue
|
||||
|
||||
interface_info = get_next_interface_info(factory)
|
||||
if info->type != SPA_TYPE_INTERFACE_Log: # </spa/support/log.h>
|
||||
continue
|
||||
|
||||
interface = spa_load_interface(handle, interface_info->type)
|
||||
logger = (struct spa_log *)interface
|
||||
break
|
||||
|
||||
spa_log_error(log, "This is an error message\n")
|
||||
\endcode
|
||||
|
||||
SPA does not specify where plugins need to live, although plugins are
|
||||
normally installed in `/usr/lib64/spa-0.2/` or equivalent. Plugins and API
|
||||
are versioned and many versions can live on the same system.
|
||||
|
||||
\note The directory the SPA plugins reside in is available through
|
||||
`pkg-config --variable plugindir libspa-0.2`
|
||||
|
||||
The `spa-inspect` tool provides a CLI interface to inspect SPA plugins:
|
||||
|
||||
\verbatim
|
||||
$ export SPA_PLUGIN_DIR=$(pkg-config --variable plugindir libspa-0.2)
|
||||
$ spa-inspect ${SPA_PLUGIN_DIR}/support/libspa-support.so
|
||||
...
|
||||
factory version: 1
|
||||
factory name: 'support.cpu'
|
||||
factory info:
|
||||
none
|
||||
factory interfaces:
|
||||
interface: 'Spa:Pointer:Interface:CPU'
|
||||
factory instance:
|
||||
interface: 'Spa:Pointer:Interface:CPU'
|
||||
skipping unknown interface
|
||||
factory version: 1
|
||||
factory name: 'support.loop'
|
||||
factory info:
|
||||
none
|
||||
factory interfaces:
|
||||
interface: 'Spa:Pointer:Interface:Loop'
|
||||
interface: 'Spa:Pointer:Interface:LoopControl'
|
||||
interface: 'Spa:Pointer:Interface:LoopUtils'
|
||||
...
|
||||
\endverbatim
|
||||
|
||||
|
||||
# Open A Plugin
|
||||
|
||||
A plugin is opened with a platform specific API. In this example we use
|
||||
`dlopen()` as the method used on Linux.
|
||||
|
||||
A plugin always consists of two parts, the vendor path and then the .so file.
|
||||
|
||||
As an example we will load the "support/libspa-support.so" plugin. You will
|
||||
usually use some mapping between functionality and plugin path as we'll see
|
||||
later, instead of hardcoding the plugin name.
|
||||
|
||||
To `dlopen` a plugin we then need to prefix the plugin path like this:
|
||||
|
||||
\code{.c}
|
||||
#define SPA_PLUGIN_DIR /usr/lib64/spa-0.2/"
|
||||
void *hnd = dlopen(SPA_PLUGIN_DIR"/support/libspa-support.so", RTLD_NOW);
|
||||
\endcode
|
||||
|
||||
The environment variable `SPA_PLUGIN_DIR` and `pkg-config` variable
|
||||
`plugindir` are usually used to find the location of the plugins. You will
|
||||
have to do some more work to construct the shared object path.
|
||||
|
||||
The plugin must have exactly one public symbol, called
|
||||
`spa_handle_factory_enum`, which is defined with the macro
|
||||
`SPA_HANDLE_FACTORY_ENUM_FUNC_NAME` to get some compile time checks and avoid
|
||||
typos in the symbol name. We can get the symbol like so:
|
||||
|
||||
\code{.c}
|
||||
spa_handle_factory_enum_func_t enum_func;
|
||||
enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME));
|
||||
\endcode
|
||||
|
||||
If this symbol is not available, the library is not a valid SPA plugin.
|
||||
|
||||
|
||||
# Enumerating Factories
|
||||
|
||||
With the `enum_func` we can now enumerate all the factories in the plugin:
|
||||
|
||||
\code{.c}
|
||||
uint32_t i;
|
||||
const struct spa_handle_factory *factory = NULL;
|
||||
for (i = 0;;) {
|
||||
if (enum_func(&factory, &i) <= 0)
|
||||
break;
|
||||
// check name and version, introspect interfaces,
|
||||
// do something with the factory.
|
||||
}
|
||||
\endcode
|
||||
|
||||
A factory has a version, a name, some properties and a couple of functions
|
||||
that we can check and use. The main use of a factory is to create an
|
||||
actual new object from it.
|
||||
|
||||
We can enumerate the interfaces that we will find on this new object with
|
||||
the `spa_handle_factory_enum_interface_info()` method. Interface types
|
||||
are simple strings that uniquely define the interface (see also the type
|
||||
system).
|
||||
|
||||
The name of the factory is a well-known name that describes the functionality
|
||||
of the objects created from the factory. `<spa/utils/names.h>` contains
|
||||
definitions for common functionality, for example:
|
||||
|
||||
\code{.c}
|
||||
#define SPA_NAME_SUPPORT_CPU "support.cpu" // A CPU interface
|
||||
#define SPA_NAME_SUPPORT_LOG "support.log" // A Log interface
|
||||
#define SPA_NAME_SUPPORT_DBUS "support.dbus" // A DBUS interface
|
||||
\endcode
|
||||
|
||||
Usually the name will be mapped to a specific plugin. This way an
|
||||
alternative compatible implementation can be made in a different library.
|
||||
|
||||
|
||||
# Making A Handle
|
||||
|
||||
Once we have a suitable factory, we need to allocate memory for the object
|
||||
it can create. SPA usually does not allocate memory itself but relies on
|
||||
the application and the stack for storage.
|
||||
|
||||
First get the size of the required memory:
|
||||
|
||||
\code{.c}
|
||||
struct spa_dict *extra_params = NULL;
|
||||
size_t size = spa_handle_factory_get_size(factory, extra_params);
|
||||
\endcode
|
||||
|
||||
Sometimes the memory can depend on the extra parameters given in
|
||||
`_get_size()`. Next we need to allocate the memory and initialize the object
|
||||
in it:
|
||||
|
||||
\code{.c}
|
||||
handle = calloc(1, size);
|
||||
spa_handle_factory_init(factory, handle,
|
||||
NULL, // info
|
||||
NULL, // support
|
||||
0 // n_support
|
||||
);
|
||||
\endcode
|
||||
|
||||
The info parameter should contain the same extra properties given in
|
||||
`spa_handle_factory_get_size()`.
|
||||
|
||||
The support parameter is an array of `struct spa_support` items. They
|
||||
contain a string type and a pointer to extra support objects. This can
|
||||
be a logging API or a main loop API for example. Some plugins require
|
||||
certain support libraries to function.
|
||||
|
||||
|
||||
# Retrieving An Interface
|
||||
|
||||
When a SPA handle is made, you can retrieve any of the interfaces that
|
||||
it provides:
|
||||
|
||||
\code{.c}
|
||||
void *iface;
|
||||
spa_handle_get_interface(handle, SPA_NAME_SUPPORT_LOG, &iface);
|
||||
\endcode
|
||||
|
||||
If this method succeeds, you can cast the `iface` variable to
|
||||
`struct spa_log *` and start using the log interface methods.
|
||||
|
||||
\code{.c}
|
||||
struct spa_log *log = iface;
|
||||
spa_log_warn(log, "Hello World!\n");
|
||||
\endcode
|
||||
|
||||
|
||||
# Clearing An Object
|
||||
|
||||
After you are done with a handle you can clear it with
|
||||
`spa_handle_clear()` and you can unload the library with `dlclose()`.
|
||||
|
||||
|
||||
# SPA Interfaces
|
||||
|
||||
We briefly talked about retrieving an interface from a plugin in the
|
||||
previous section. Now we will explore what an interface actually is
|
||||
and how to use it.
|
||||
|
||||
When you retrieve an interface from a handle, you get a reference to
|
||||
a small structure that contains the type (string) of the interface,
|
||||
a version and a structure with a set of methods (and data) that are
|
||||
the implementation of the interface. Calling a method on the interface
|
||||
will just call the appropriate method in the implementation.
|
||||
|
||||
Interfaces are defined in a header file (for example see
|
||||
`<spa/support/log.h>` for the logger API). It is a self contained
|
||||
definition that you can just use in your application after you `dlopen()`
|
||||
the plugin.
|
||||
|
||||
Some interfaces also provide extra fields in the interface, like the
|
||||
log interface above that has the log level as a read/write parameter.
|
||||
|
||||
See \ref spa_interface for some implementation details on interfaces.
|
||||
|
||||
|
||||
# SPA Events
|
||||
|
||||
Some interfaces will also allow you to register a callback (a hook or
|
||||
listener) to be notified of events. This is usually when something
|
||||
changed internally in the interface and it wants to notify the registered
|
||||
listeners about this.
|
||||
|
||||
For example, the `struct spa_node` interface has a method to register such
|
||||
an event handler like this:
|
||||
|
||||
\code{.c}
|
||||
static void node_info(void *data, const struct spa_node_info *info)
|
||||
{
|
||||
printf("got node info!\n");
|
||||
}
|
||||
|
||||
static struct spa_node_events node_events = {
|
||||
SPA_VERSION_NODE_EVENTS,
|
||||
.info = node_info,
|
||||
};
|
||||
|
||||
struct spa_hook listener;
|
||||
spa_zero(listener);
|
||||
spa_node_add_listener(node, &listener, &node_event, my_data);
|
||||
\endcode
|
||||
|
||||
You make a structure with pointers to the events you are interested in
|
||||
and then use `spa_node_add_listener()` to register a listener. The
|
||||
`struct spa_hook` is used by the interface to keep track of registered
|
||||
event listeners.
|
||||
|
||||
Whenever the node information is changed, your `node_info` method will
|
||||
be called with `my_data` as the first data field. The events are usually
|
||||
also triggered when the listener is added, to enumerate the current
|
||||
state of the object.
|
||||
|
||||
Events have a `version` field, set to `SPA_VERSION_NODE_EVENTS` in the
|
||||
above example. It should contain the version of the event structure
|
||||
you compiled with. When new events are added later, the version field
|
||||
will be checked and the new signal will be ignored for older versions.
|
||||
|
||||
You can remove your listener with:
|
||||
|
||||
\code{.c}
|
||||
spa_hook_remove(&listener);
|
||||
\endcode
|
||||
|
||||
|
||||
# API Results
|
||||
|
||||
Some interfaces provide API that gives you a list or enumeration of
|
||||
objects/values. To avoid allocation overhead and ownership problems,
|
||||
SPA uses events to push results to the application. This makes it
|
||||
possible for the plugin to temporarily create complex objects on the
|
||||
stack and push this to the application without allocation or ownership
|
||||
problems. The application can look at the pushed result and keep/copy
|
||||
only what it wants to keep.
|
||||
|
||||
## Synchronous Results
|
||||
|
||||
Here is an example of enumerating parameters on a node interface.
|
||||
|
||||
First install a listener for the result:
|
||||
|
||||
\code{.c}
|
||||
static void node_result(void *data, int seq, int res,
|
||||
uint32_t type, const void *result)
|
||||
{
|
||||
const struct spa_result_node_params *r =
|
||||
(const struct spa_result_node_params *) result;
|
||||
printf("got param:\n");
|
||||
spa_debug_pod(0, NULL, r->param);
|
||||
}
|
||||
|
||||
struct spa_hook listener = { 0 };
|
||||
static const struct spa_node_events node_events = {
|
||||
SPA_VERSION_NODE_EVENTS,
|
||||
.result = node_result,
|
||||
};
|
||||
|
||||
spa_node_add_listener(node, &listener, &node_events, node);
|
||||
\endcode
|
||||
|
||||
Then perform the `enum_param` method:
|
||||
|
||||
\code{.c}
|
||||
int res = spa_node_enum_params(node, 0, SPA_PARAM_EnumFormat, 0, MAXINT, NULL);
|
||||
\endcode
|
||||
|
||||
This triggers the result event handler with a 0 sequence number for each
|
||||
supported format. After this completes, remove the listener again:
|
||||
|
||||
\code{.c}
|
||||
spa_hook_remove(&listener);
|
||||
\endcode
|
||||
|
||||
## Asynchronous Results
|
||||
|
||||
Asynchronous results are pushed to the application in the same way as
|
||||
synchronous results, they are just pushed later. You can check that
|
||||
a result is asynchronous by the return value of the enum function:
|
||||
|
||||
\code{.c}
|
||||
int res = spa_node_enum_params(node, 0, SPA_PARAM_EnumFormat, 0, MAXINT, NULL);
|
||||
|
||||
if (SPA_RESULT_IS_ASYNC(res)) {
|
||||
// result will be received later
|
||||
...
|
||||
}
|
||||
\endcode
|
||||
|
||||
In the case of async results, the result callback will be called with the
|
||||
sequence number of the async result code, which can be obtained with:
|
||||
|
||||
\code{.c}
|
||||
expected_seq = SPA_RESULT_ASYNC_SEQ(res);
|
||||
\endcode
|
||||
|
||||
# Implementing A New Plugin
|
||||
|
||||
***FIXME***
|
||||
|
||||
|
||||
|
||||
\addtogroup spa_handle
|
||||
|
||||
See: \ref page_spa_plugins
|
||||
|
||||
*/
|
1034
doc/dox/api/spa-pod.dox
Normal file
1034
doc/dox/api/spa-pod.dox
Normal file
File diff suppressed because it is too large
Load diff
53
doc/dox/config/index.md
Normal file
53
doc/dox/config/index.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
\page page_config Configuration
|
||||
|
||||
One of the design goals of PipeWire is to be able to closely control
|
||||
and configure all aspects of the processing graph.
|
||||
|
||||
A fully configured PipeWire setup runs various pieces, each with their
|
||||
configuration options and files:
|
||||
|
||||
- **pipewire**: The PipeWire main daemon that runs and coordinates the processing.
|
||||
|
||||
- **pipewire-pulse**: The PipeWire PulseAudio replacement server. It also configures
|
||||
the properties of the PulseAudio clients connecting to it.
|
||||
|
||||
- **wireplumber**: Most configuration of devices is performed by the session manager.
|
||||
It typically loads ALSA and other devices and configures the profiles, port volumes and more.
|
||||
The session manager also configures new clients and links them to the targets, as configured
|
||||
in the session manager policy.
|
||||
|
||||
- **PipeWire clients**: Each native PipeWire client also loads a configuration file.
|
||||
Emulated JACK client also have separate configuration.
|
||||
|
||||
# Configuration Settings
|
||||
|
||||
Configuration of daemons:
|
||||
|
||||
- \ref page_man_pipewire_conf_5 "PipeWire daemon configuration reference"
|
||||
- \ref page_man_pipewire-pulse_conf_5 "PipeWire Pulseaudio daemon configuration reference"
|
||||
- [WirePlumber daemon configuration](https://pipewire.pages.freedesktop.org/wireplumber/)
|
||||
|
||||
Configuration of devices:
|
||||
|
||||
- [WirePlumber configuration](https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration.html)
|
||||
- \ref page_man_pipewire-props_7 "Object property reference"
|
||||
- \subpage page_config_xref "Configuration Index"
|
||||
|
||||
Configuration for client applications, either connecting via the
|
||||
native PipeWire interface, or the emulated ALSA, JACK, or PulseAudio
|
||||
interfaces:
|
||||
|
||||
- \ref page_man_pipewire-client_conf_5 "PipeWire native and ALSA client configuration reference"
|
||||
- \ref page_man_pipewire-jack_conf_5 "PipeWire JACK client configuration reference"
|
||||
- \ref page_man_pipewire-pulse_conf_5 "PipeWire Pulseaudio client configuration reference"
|
||||
|
||||
# Manual Pages
|
||||
|
||||
- \subpage page_man_pipewire_conf_5
|
||||
- \subpage page_man_pipewire-client_conf_5
|
||||
- \subpage page_man_pipewire-pulse_conf_5
|
||||
- \subpage page_man_pipewire-jack_conf_5
|
||||
- \subpage page_man_pipewire-filter-chain_conf_5
|
||||
- \subpage page_man_pipewire-props_7
|
||||
- \subpage page_man_pipewire-pulse-modules_7
|
||||
- \subpage page_man_libpipewire-modules_7
|
44
doc/dox/config/libpipewire-modules.7.md
Normal file
44
doc/dox/config/libpipewire-modules.7.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
\page page_man_libpipewire-modules_7 libpipewire-modules
|
||||
|
||||
PipeWire modules
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
A PipeWire module is effectively a PipeWire client running inside
|
||||
`pipewire(1)` which can host multiple modules. Usually modules are
|
||||
loaded when they are listed in the configuration files. For example the
|
||||
default configuration file loads several modules:
|
||||
|
||||
context.modules = [
|
||||
...
|
||||
# The native communication protocol.
|
||||
{ name = libpipewire-module-protocol-native }
|
||||
|
||||
# The profile module. Allows application to access profiler
|
||||
# and performance data. It provides an interface that is used
|
||||
# by pw-top and pw-profiler.
|
||||
{ name = libpipewire-module-profiler }
|
||||
|
||||
# Allows applications to create metadata objects. It creates
|
||||
# a factory for Metadata objects.
|
||||
{ name = libpipewire-module-metadata }
|
||||
|
||||
# Creates a factory for making devices that run in the
|
||||
# context of the PipeWire server.
|
||||
{ name = libpipewire-module-spa-device-factory }
|
||||
...
|
||||
]
|
||||
|
||||
# KNOWN MODULES
|
||||
|
||||
$(LIBPIPEWIRE_MODULES)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pipewire_conf_5 "pipewire.conf(5)"
|
235
doc/dox/config/pipewire-client.conf.5.md
Normal file
235
doc/dox/config/pipewire-client.conf.5.md
Normal file
|
@ -0,0 +1,235 @@
|
|||
\page page_man_pipewire-client_conf_5 client.conf
|
||||
|
||||
The PipeWire client configuration file.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/client.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/client.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/client.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/client.conf.d/*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/client.conf.d/*
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/client.conf.d/*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Configuration for PipeWire native clients, and for PipeWire's ALSA
|
||||
plugin.
|
||||
|
||||
A PipeWire native client program selects the default config to load,
|
||||
and if nothing is specified, it usually loads `client.conf`.
|
||||
|
||||
The configuration file format and lookup logic is the same as for \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
|
||||
|
||||
Drop-in configuration files `client.conf.d/*.conf` can be used, and are recommended.
|
||||
See \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
|
||||
|
||||
# CONFIGURATION FILE SECTIONS @IDX@ client.conf
|
||||
|
||||
\par stream.properties
|
||||
Configures options for native client streams.
|
||||
|
||||
\par stream.rules
|
||||
Configures rules for native client streams.
|
||||
|
||||
\par alsa.properties
|
||||
ALSA client configuration.
|
||||
|
||||
\par alsa.rules
|
||||
ALSA client match rules.
|
||||
|
||||
In addition, the PipeWire context configuration sections
|
||||
may also be specified, see \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
|
||||
|
||||
# STREAM PROPERTIES @IDX@ client.conf
|
||||
|
||||
The client configuration files contain a stream.properties section that configures the options for client streams:
|
||||
```css
|
||||
# ~/.config/pipewire/client.conf.d/custom.conf
|
||||
|
||||
stream.properties = {
|
||||
#node.latency = 1024/48000
|
||||
#node.autoconnect = true
|
||||
#resample.disable = false
|
||||
#resample.quality = 4
|
||||
#monitor.channel-volumes = false
|
||||
#channelmix.disable = false
|
||||
#channelmix.min-volume = 0.0
|
||||
#channelmix.max-volume = 10.0
|
||||
#channelmix.normalize = false
|
||||
#channelmix.lock-volume = false
|
||||
#channelmix.mix-lfe = true
|
||||
#channelmix.upmix = true
|
||||
#channelmix.upmix-method = psd # none, simple
|
||||
#channelmix.lfe-cutoff = 150.0
|
||||
#channelmix.fc-cutoff = 12000.0
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.stereo-widen = 0.0
|
||||
#channelmix.hilbert-taps = 0
|
||||
#dither.noise = 0
|
||||
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
|
||||
#debug.wav-path = ""
|
||||
}
|
||||
```
|
||||
|
||||
Some of the properties refer to different aspects of the stream:
|
||||
|
||||
* General stream properties to identify the stream.
|
||||
* General stream properties to classify the stream.
|
||||
* How it is going to be scheduled by the graph.
|
||||
* How it is going to be linked by the session manager.
|
||||
* How the internal processing will be done.
|
||||
* Properties to configure the media format.
|
||||
|
||||
A list of object properties that can be applied to streams can be found in
|
||||
\ref props__common_node_properties "pipewire-props(7) Common Node Properties"
|
||||
and
|
||||
\ref props__audio_converter_properties "pipewire-props(7) Audio Adapter Properties"
|
||||
|
||||
# STREAM RULES @IDX@ client.conf
|
||||
|
||||
You can add \ref pipewire_conf__match_rules "match rules, see pipewire(1)"
|
||||
to set properties for certain streams and filters.
|
||||
|
||||
`stream.rules` and `filter.rules` provides an `update-props` action
|
||||
that takes an object with properties that are updated on the node
|
||||
object of the stream and filter.
|
||||
|
||||
Add a `stream.rules` or `filter.rules` section in the config file like
|
||||
this:
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/client.conf.d/custom.conf
|
||||
|
||||
stream.rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
# all keys must match the value. ! negates. ~ starts regex.
|
||||
application.process.binary = "firefox"
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
update-props = {
|
||||
node.name = "My Name"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Will set the node.name of Firefox to "My Name".
|
||||
|
||||
# ALSA CLIENT PROPERTIES @IDX@ client.conf
|
||||
|
||||
An `alsa.properties` section can be added to configure client applications
|
||||
that connect via the PipeWire ALSA plugin.
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/client.conf.d/custom.conf
|
||||
|
||||
alsa.properties = {
|
||||
#alsa.deny = false
|
||||
#alsa.format = 0
|
||||
#alsa.rate = 0
|
||||
#alsa.channels = 0
|
||||
#alsa.period-bytes = 0
|
||||
#alsa.buffer-bytes = 0
|
||||
#alsa.volume-method = cubic # linear, cubic
|
||||
}
|
||||
```
|
||||
|
||||
@PAR@ client.conf alsa.deny
|
||||
Denies ALSA access for the client. Useful in rules or PIPEWIRE_ALSA environment variable.
|
||||
|
||||
@PAR@ client.conf alsa.format
|
||||
The ALSA format to use for the client. This is an ALSA format name. default 0, which is to
|
||||
allow all formats.
|
||||
|
||||
@PAR@ client.conf alsa.rate
|
||||
The samplerate to use for the client. The default is 0, which is to allow all rates.
|
||||
|
||||
@PAR@ client.conf alsa.channels
|
||||
The number of channels for the client. The default is 0, which is to allow any number of channels.
|
||||
|
||||
@PAR@ client.conf alsa.period-bytes
|
||||
The number of bytes per period. The default is 0 which is to allow any number of period bytes.
|
||||
|
||||
@PAR@ client.conf alsa.buffer-bytes
|
||||
The number of bytes in the alsa buffer. The default is 0, which is to allow any number of bytes.
|
||||
|
||||
@PAR@ client.conf alsa.volume-method = cubic | linear
|
||||
This controls the volume curve used on the ALSA mixer. Possible values are `cubic` and
|
||||
`linear`. The default is to use `cubic`.
|
||||
|
||||
# ALSA CLIENT RULES @IDX@ client.conf
|
||||
|
||||
It is possible to set ALSA client specific properties by using
|
||||
\ref pipewire_conf__match_rules "Match rules, see pipewire(1)". You can
|
||||
set any of the above ALSA properties or any of the `stream.properties`.
|
||||
|
||||
### Example
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/client.conf.d/custom.conf
|
||||
|
||||
alsa.rules = [
|
||||
{ matches = [ { application.process.binary = "resolve" } ]
|
||||
actions = {
|
||||
update-props = {
|
||||
alsa.buffer-bytes = 131072
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
# ENVIRONMENT VARIABLES @IDX@ client-env
|
||||
|
||||
See \ref page_man_pipewire_1 "pipewire(1)" for common environment
|
||||
variables. Many of these also apply to client applications.
|
||||
|
||||
The environment variables also influence ALSA applications that are
|
||||
using PipeWire's ALSA plugin.
|
||||
|
||||
@PAR@ client-env PIPEWIRE_ALSA
|
||||
\parblock
|
||||
This can be an object with properties from `alsa.properties` or `stream.properties` that will
|
||||
be used to construct the client and streams.
|
||||
|
||||
For example:
|
||||
```
|
||||
PIPEWIRE_ALSA='{ alsa.buffer-bytes=16384 node.name=foo }' aplay ...
|
||||
```
|
||||
Starts aplay with custom properties.
|
||||
\endparblock
|
||||
|
||||
@PAR@ client-env PIPEWIRE_NODE
|
||||
\parblock
|
||||
Instructs the ALSA client to link to a particular sink or source `object.serial` or `node.name`.
|
||||
|
||||
For example:
|
||||
```
|
||||
PIPEWIRE_NODE=alsa_output.pci-0000_00_1b.0.analog-stereo aplay ...
|
||||
```
|
||||
Makes aplay play on the give audio sink.
|
||||
\endparblock
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)",
|
||||
\ref page_man_pipewire_conf_5 "pipewire.conf(5)",
|
||||
\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)",
|
||||
\ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)"
|
42
doc/dox/config/pipewire-filter-chain.conf.5.md
Normal file
42
doc/dox/config/pipewire-filter-chain.conf.5.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
\page page_man_pipewire-filter-chain_conf_5 filter-chain.conf
|
||||
|
||||
PipeWire example configuration for running audio filters.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/filter-chain.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/filter-chain.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/filter-chain.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/filter-chain.conf.d/*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/filter-chain.conf.d/*
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/filter-chain.conf.d/*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
When \ref page_man_pipewire_1 "pipewire(1)" is run using
|
||||
this configuration file, `pipewire -c filter-chain.conf`,
|
||||
it starts a PipeWire client application that publishes
|
||||
nodes that apply various audio filters to their input.
|
||||
|
||||
It is a normal PipeWire client application in all respects.
|
||||
|
||||
Drop-in configuration files `filter-chain.conf.d/*.conf` can be used
|
||||
to modify the filter configuration, see \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
|
||||
Some examples are in *$(PIPEWIRE_CONFDATADIR)/filter-chain/*
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pipewire_conf_5 "pipewire.conf(5)"
|
342
doc/dox/config/pipewire-jack.conf.5.md
Normal file
342
doc/dox/config/pipewire-jack.conf.5.md
Normal file
|
@ -0,0 +1,342 @@
|
|||
\page page_man_pipewire-jack_conf_5 jack.conf
|
||||
|
||||
The PipeWire JACK client configuration file.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/jack.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/jack.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/jack.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/jack.conf.d/*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/jack.conf.d/*
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/jack.conf.d/*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Configuration for PipeWire JACK clients.
|
||||
|
||||
The configuration file format and lookup logic is the same as for \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
|
||||
|
||||
Drop-in configuration files `jack.conf.d/*.conf` can be used, and are recommended.
|
||||
See \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
|
||||
|
||||
# CONFIGURATION FILE SECTIONS @IDX@ jack.conf
|
||||
|
||||
\par jack.properties
|
||||
JACK client configuration.
|
||||
|
||||
\par jack.rules
|
||||
JACK client match rules.
|
||||
|
||||
In addition, the PipeWire context configuration sections
|
||||
may also be specified, see \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
|
||||
|
||||
# JACK PROPERTIES @IDX@ jack.conf
|
||||
|
||||
The configuration file can contain an extra JACK specific section called `jack.properties` like this:
|
||||
```css
|
||||
# ~/.config/pipewire/jack.conf.d/custom.conf
|
||||
|
||||
jack.properties = {
|
||||
#rt.prio = 88
|
||||
#node.latency = 1024/48000
|
||||
#node.lock-quantum = true
|
||||
#node.force-quantum = 0
|
||||
#jack.show-monitor = true
|
||||
#jack.merge-monitor = true
|
||||
#jack.show-midi = true
|
||||
#jack.short-name = false
|
||||
#jack.filter-name = false
|
||||
#jack.filter-char = " "
|
||||
#
|
||||
# allow: Don't restrict self connect requests
|
||||
# fail-external: Fail self connect requests to external ports only
|
||||
# ignore-external: Ignore self connect requests to external ports only
|
||||
# fail-all: Fail all self connect requests
|
||||
# ignore-all: Ignore all self connect requests
|
||||
#jack.self-connect-mode = allow
|
||||
#jack.locked-process = true
|
||||
#jack.default-as-system = false
|
||||
#jack.fix-midi-events = true
|
||||
#jack.global-buffer-size = false
|
||||
#jack.passive-links = false
|
||||
#jack.max-client-ports = 768
|
||||
#jack.fill-aliases = false
|
||||
#jack.writable-input = false
|
||||
#jack.flag-midi2 = false
|
||||
}
|
||||
```
|
||||
|
||||
See `stream.properties` in
|
||||
\ref client_conf__stream_properties "pipewire-client.conf(5)" for
|
||||
an explanation of the generic node properties.
|
||||
|
||||
It is also possible to have per-client settings, see Match Rules below.
|
||||
|
||||
@PAR@ jack.conf rt.prio
|
||||
To limit the realtime priority that jack clients can acquire.
|
||||
|
||||
@PAR@ jack.conf node.latency
|
||||
To force a specific minimum buffer size for the JACK applications, configure:
|
||||
```
|
||||
node.latency = 1024/48000
|
||||
```
|
||||
This configures a buffer-size of 1024 samples at 48KHz. If the graph is running at a different sample rate, the buffer-size will be adjusted accordingly.
|
||||
|
||||
@PAR@ jack.conf node.lock-quantum
|
||||
To make sure that no automatic quantum is changes while JACK applications are running, configure:
|
||||
```
|
||||
node.lock-quantum = true
|
||||
```
|
||||
The quantum can then only be changed by metadata or when an application is started with node.force-quantum. JACK Applications will also be able to use jack_set_buffersize() to override the quantum.
|
||||
|
||||
@PAR@ jack.conf node.force-quantum
|
||||
To force the quantum to a certain value and avoid changes to it:
|
||||
```
|
||||
node.force-quantum = 1024
|
||||
```
|
||||
The quantum can then only be changed by metadata or when an application is started with node.force-quantum (or JACK applications that use jack_set_buffersize() to override the quantum).
|
||||
|
||||
@PAR@ jack.conf jack.show-monitor
|
||||
Show the Monitor client and its ports.
|
||||
|
||||
@PAR@ jack.conf jack.merge-monitor
|
||||
\parblock
|
||||
Exposes the capture ports and monitor ports on the same JACK device client. This is how JACK presents monitor ports to the clients. The default is however *not* to merge them together because this results in more user friendly user interfaces, usually. An extra client with a `Monitor` suffix is created that contains the monitor ports.
|
||||
|
||||
For example, this is (part of) the output of `jack_lsp` with the default setting (`jack.merge-monitor = false`):
|
||||
|
||||
Compare:
|
||||
|
||||
| `jack.merge-monitor = true` | `jack.merge-monitor = false` |
|
||||
|:--|:--|
|
||||
| Built-in Audio Analog Stereo:playback_FL | Built-in Audio Analog Stereo:playback_FL
|
||||
| Built-in Audio Analog Stereo:monitor_FL | Built-in Audio Analog Stereo Monitor:monitor_FL
|
||||
| Built-in Audio Analog Stereo:playback_FR | Built-in Audio Analog Stereo:playback_FR
|
||||
| Built-in Audio Analog Stereo:monitor_FR |Built-in Audio Analog Stereo Monitor:monitor_FR
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack.conf jack.show-midi
|
||||
Show the MIDI clients and their ports.
|
||||
|
||||
@PAR@ jack.conf jack.short-name
|
||||
\parblock
|
||||
To use shorter names for the device client names use `jack.short-name = true`. Compare:
|
||||
|
||||
| `jack.short-name = true` | `jack.short-name = false` |
|
||||
|:--|:--|
|
||||
| HDA Intel PCH:playback_FL | Built-in Audio Analog Stereo:playback_FL
|
||||
| HDA Intel PCH Monitor:monitor_FL | Built-in Audio Analog Stereo Monitor:monitor_FL
|
||||
| HDA Intel PCH:playback_FR | Built-in Audio Analog Stereo:playback_FR
|
||||
| HDA Intel PCH Monitor:monitor_FR |Built-in Audio Analog Stereo Monitor:monitor_FR
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack.conf jack.filter-name
|
||||
@PAR@ jack.conf jack.filter-char
|
||||
Will replace all special characters with `jack.filter-char`. For clients the special characters are ` ()[].:*$` and for ports they are ` ()[].*$`. Use this option when a client is not able to deal with the special characters. (and older version of PortAudio was known to use the client and port names as a regex, and thus failing when there are regex special characters in the name).
|
||||
|
||||
@PAR@ jack.conf jack.self-connect-mode
|
||||
\parblock
|
||||
Restrict a client from making connections to and from itself. Possible values and their meaning are summarized as:
|
||||
|
||||
| Value | Behavior
|
||||
|:--|:--|
|
||||
| `allow` | Don't restrict self connect requests.
|
||||
| `fail-external` | Fail self connect requests to external ports only.
|
||||
| `ignore-external` | Ignore self connect requests to external ports only.
|
||||
| `fail-all` | Fail all self connect requests.
|
||||
| `ignore-all` | Ignore all self connect requests.
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack.conf jack.locked-process
|
||||
Make sure the process and callbacks can not be called at the same time. This is the
|
||||
normal operation but it can be disabled in case a specific client can handle this.
|
||||
|
||||
@PAR@ jack.conf jack.default-as-system
|
||||
\parblock
|
||||
Name the default source and sink as `system` and number the ports to maximize
|
||||
compatibility with JACK programs.
|
||||
|
||||
| `jack.default-as-system = false` | `jack.default-as-system = true` |
|
||||
|:--|:--|
|
||||
| HDA Intel PCH:playback_FL | system:playback_1
|
||||
| HDA Intel PCH Monitor:monitor_FL | system:monitor_1
|
||||
| HDA Intel PCH:playback_FR | system:playback_2
|
||||
| HDA Intel PCH Monitor:monitor_FR | system:monitor_2
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack.conf jack.fix-midi-events
|
||||
Fix NoteOn events with a 0 velocity to NoteOff. This is standard behaviour in JACK and is thus
|
||||
enabled by default to maximize compatibility. Especially LV2 plugins do not allow NoteOn
|
||||
with 0 velocity.
|
||||
|
||||
@PAR@ jack.conf jack.global-buffer-size
|
||||
When a client has this option, buffersize changes will be applied globally and permanently for all PipeWire clients using the metadata.
|
||||
|
||||
@PAR@ jack.conf jack.passive-links
|
||||
Makes JACK clients make passive links. This option only works when the server link-factory was configured with the `allow.link.passive` option.
|
||||
|
||||
@PAR@ jack.conf jack.max-client-ports
|
||||
Limit the number of allowed ports per client to this value.
|
||||
|
||||
@PAR@ jack.conf jack.fill-aliases
|
||||
Automatically set the port alias1 and alias2 on the ports.
|
||||
|
||||
@PAR@ jack.conf jack.writable-input
|
||||
\parblock
|
||||
Makes the input buffers writable. This is the default because some JACK clients write to the
|
||||
input buffer. This however can cause corruption in other clients when they are also reading
|
||||
from the buffer.
|
||||
|
||||
Set this to true to avoid buffer corruption if you are only dealing with non-buggy clients.
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack.conf jack.flag-midi2
|
||||
\parblock
|
||||
Use the new JACK MIDI2 port flag on MIDI2 (UMP) ports. This is disabled by default because most
|
||||
JACK apps don't know about this flag yet and refuse to show the port.
|
||||
|
||||
Set this to true for applications that know how to handle MIDI2 ports.
|
||||
\endparblock
|
||||
|
||||
# MATCH RULES @IDX@ jack.conf
|
||||
|
||||
`jack.rules` provides an `update-props` action that takes an object with properties that are updated
|
||||
on the client and node object of the jack client.
|
||||
|
||||
Add a `jack.rules` section in the config file like this:
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/jack.conf.d/custom.conf
|
||||
|
||||
jack.rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
# all keys must match the value. ! negates. ~ starts regex.
|
||||
application.process.binary = "jack_simple_client"
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
update-props = {
|
||||
node.latency = 512/48000
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
client.name = "catia"
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
update-props = {
|
||||
jack.merge-monitor = true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
Will set the latency of jack_simple_client to 512/48000 and makes Catia see the monitor client merged with the playback client.
|
||||
|
||||
# ENVIRONMENT VARIABLES @IDX@ jack-env
|
||||
|
||||
See \ref page_man_pipewire_1 "pipewire(1)" for common environment
|
||||
variables. Many of these also apply to JACK client applications.
|
||||
|
||||
Environment variables can be used to control the behavior of the PipeWire JACK client library.
|
||||
|
||||
@PAR@ jack-env PIPEWIRE_NOJACK
|
||||
@PAR@ jack-env PIPEWIRE_INTERNAL
|
||||
When any of these variables is set, the JACK client library will refuse to open a client. The `PIPEWIRE_INTERNAL` variable is set by the PipeWire main daemon to avoid self connections.
|
||||
|
||||
@PAR@ jack-env PIPEWIRE_PROPS
|
||||
Adds/overrides the properties specified in the `jack.conf` file. Check out the output of this:
|
||||
```
|
||||
> PIPEWIRE_PROPS='{ jack.short-name=true jack.merge-monitor=true }' jack_lsp
|
||||
...
|
||||
HDA Intel PCH:playback_FL
|
||||
HDA Intel PCH:monitor_FL
|
||||
HDA Intel PCH:playback_FR
|
||||
HDA Intel PCH:monitor_FR
|
||||
...
|
||||
```
|
||||
|
||||
@PAR@ jack-env PIPEWIRE_LATENCY
|
||||
\parblock
|
||||
```
|
||||
PIPEWIRE_LATENCY=<samples>/<rate> <application>
|
||||
```
|
||||
A quick way to configure the maximum buffer-size for a client. It will run this client with the specified buffer-size (or smaller).
|
||||
|
||||
`PIPEWIRE_LATENCY=256/48000 jack_lsp` is equivalent to `PIPEWIRE_PROPS='{ node.latency=256/48000 }' jack_lsp`
|
||||
|
||||
A better way to start a jack session in a specific buffer-size is to force it with:
|
||||
```
|
||||
pw-metadata -n settings 0 clock.force-quantum <quantum>
|
||||
```
|
||||
This always works immediately and the buffer size will not change until the quantum is changed back to 0.
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack-env PIPEWIRE_RATE
|
||||
\parblock
|
||||
```
|
||||
PIPEWIRE_RATE=1/<rate> <application>
|
||||
```
|
||||
|
||||
A quick way to configure the rate of the graph. It will try to switch the samplerate of the graph. This can usually only be done with the graph is idle and the rate is part of the allowed sample rates.
|
||||
|
||||
`PIPEWIRE_RATE=1/48000 jack_lsp` is equivalent to `PIPEWIRE_PROPS='{ node.rate=1/48000 }' jack_lsp`
|
||||
|
||||
A better way to start a jack session in a specific rate is to force the rate with:
|
||||
```
|
||||
pw-metadata -n settings 0 clock.force-rate <rate>
|
||||
```
|
||||
This always works and the samplerate does not need to be in the allowed rates. The rate will also not change until it is set back to 0.
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack-env PIPEWIRE_QUANTUM
|
||||
\parblock
|
||||
```
|
||||
PIPEWIRE_QUANTUM=<buffersize>/<rate> <application>
|
||||
```
|
||||
|
||||
Is similar to using `PIPEWIRE_LATENCY=<buffersize>/<rate>` and `PIPEWIRE_RATE=1/<rate>` (see above), except that it is not just a suggestion but it actively *forces* the graph to change the rate and quantum. It can be used to set both a buffersize and samplerate at the same time.
|
||||
|
||||
When 2 applications force a quantum, the last one wins. When the winning app is stopped, the quantum of the previous app is restored.
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack-env PIPEWIRE_LINK_PASSIVE
|
||||
\parblock
|
||||
```
|
||||
PIPEWIRE_LINK_PASSIVE=true qjackctl
|
||||
```
|
||||
Make this client create passive links only. All links created by the client will be marked passive and will not keep the sink/source busy.
|
||||
|
||||
You can use this to link filters to devices. When there is no client connected to the filter, only passive links remain between the filter and the device and the device will become idle and suspended.
|
||||
\endparblock
|
||||
|
||||
@PAR@ jack-env PIPEWIRE_NODE
|
||||
\parblock
|
||||
```
|
||||
PIPEWIRE_NODE=<id> <application>
|
||||
```
|
||||
Will sort the ports so that only the ports of the node with <id> are listed. You can use this to force an application to only deal with the ports of a certain node, for example when auto connecting.
|
||||
\endparblock
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pw-jack_1 "pw-jack(1)",
|
||||
\ref page_man_pipewire_conf_5 "pipewire.conf(5)"
|
1214
doc/dox/config/pipewire-props.7.md
Normal file
1214
doc/dox/config/pipewire-props.7.md
Normal file
File diff suppressed because it is too large
Load diff
20
doc/dox/config/pipewire-pulse-modules.7.md
Normal file
20
doc/dox/config/pipewire-pulse-modules.7.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
\page page_man_pipewire-pulse-modules_7 pipewire-pulse-modules
|
||||
|
||||
PipeWire Pulseaudio modules
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
\include{doc} pulse-modules.inc
|
||||
|
||||
# BUILT-IN MODULES
|
||||
|
||||
$(PIPEWIRE_PULSE_MODULES)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)"
|
195
doc/dox/config/pipewire-pulse.conf.5.md
Normal file
195
doc/dox/config/pipewire-pulse.conf.5.md
Normal file
|
@ -0,0 +1,195 @@
|
|||
\page page_man_pipewire-pulse_conf_5 pipewire-pulse.conf
|
||||
|
||||
The PipeWire Pulseaudio server configuration file
|
||||
|
||||
\tableofcontents
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/pipewire-pulse.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/pipewire-pulse.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/pipewire-pulse.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/pipewire-pulse.conf.d/*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/pipewire-pulse.conf.d/*
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/pipewire-pulse.conf.d/*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Configuration for PipeWire's PulseAudio-compatible daemon.
|
||||
|
||||
The configuration file format and lookup logic is the same as for \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
|
||||
|
||||
Drop-in configuration files `pipewire-pulse.conf.d/*.conf` can be used, and are recommended.
|
||||
See \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
|
||||
|
||||
# CONFIGURATION FILE SECTIONS @IDX@ pipewire-pulse.conf
|
||||
|
||||
\par stream.properties
|
||||
Dictionary. These properties configure the PipeWire Pulseaudio server
|
||||
properties.
|
||||
|
||||
\par stream.rules
|
||||
Dictionary. These properties configure the PipeWire Pulseaudio server
|
||||
properties.
|
||||
|
||||
\par pulse.properties
|
||||
Dictionary. These properties configure the PipeWire Pulseaudio server
|
||||
properties.
|
||||
|
||||
\par pulse.cmd
|
||||
Array of dictionaries. A set of commands to be executed on startup.
|
||||
|
||||
\par pulse.rules
|
||||
Array of dictionaries. A set of match rules and actions to apply to
|
||||
clients.
|
||||
|
||||
See \ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)"
|
||||
for the detailed description.
|
||||
|
||||
In addition, the PipeWire context configuration sections
|
||||
may also be specified, see \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
|
||||
|
||||
# STREAM PROPERTIES @IDX@ pipewire-pulse.conf
|
||||
|
||||
The `stream.properties` section contains properties for streams created
|
||||
by the pipewire-pulse server.
|
||||
|
||||
Available options are described in
|
||||
\ref client_conf__stream_properties "pipewire-client.conf(5) stream.properties".
|
||||
|
||||
Some of these properties map to the PulseAudio `/etc/pulse/default.pa` config entries as follows:
|
||||
|
||||
| PulseAudio | PipeWire | Notes |
|
||||
| ------------------------------ | --------------------- | -------------------- |
|
||||
| remixing-use-all-sink-channels | channelmix.upmix | |
|
||||
| remixing-produce-lfe | channelmix.lfe-cutoff | Set to > 0 to enable |
|
||||
| remixing-consume-lfe | channelmix.mix-lfe | |
|
||||
| lfe-crossover-freq | channelmix.lfe-cutoff | |
|
||||
|
||||
## Example
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/pipewire-pulse.conf.d/custom.conf
|
||||
|
||||
stream.properties = {
|
||||
#node.latency = 1024/48000
|
||||
#node.autoconnect = true
|
||||
#resample.disable = false
|
||||
#resample.quality = 4
|
||||
#monitor.channel-volumes = false
|
||||
#channelmix.disable = false
|
||||
#channelmix.min-volume = 0.0
|
||||
#channelmix.max-volume = 10.0
|
||||
#channelmix.normalize = false
|
||||
#channelmix.mix-lfe = true
|
||||
#channelmix.upmix = true
|
||||
#channelmix.upmix-method = psd # none, simple
|
||||
#channelmix.lfe-cutoff = 150.0
|
||||
#channelmix.fc-cutoff = 12000.0
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.stereo-widen = 0.0
|
||||
#channelmix.hilbert-taps = 0
|
||||
#dither.noise = 0
|
||||
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
|
||||
#debug.wav-path = ""
|
||||
}
|
||||
```
|
||||
|
||||
# STREAM RULES @IDX@ pipewire-pulse.conf
|
||||
|
||||
The `stream.rules` section works the same as
|
||||
\ref client_conf__stream_rules "pipewire-client.conf(5) stream.rules".
|
||||
|
||||
# PULSEAUDIO PROPERTIES @IDX@ pipewire-pulse.conf
|
||||
|
||||
For `pulse.properties` section,
|
||||
see \ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)"
|
||||
for available options.
|
||||
|
||||
# PULSEAUDIO RULES @IDX@ pipewire-pulse.conf
|
||||
|
||||
For each client, a set of rules can be written in `pulse.rules`
|
||||
section to configure quirks of the client or to force some pulse
|
||||
specific stream configuration.
|
||||
|
||||
The general look of this section is as follows and follows the layout of
|
||||
\ref pipewire_conf__match_rules "match rules, see pipewire(1)".
|
||||
|
||||
See \ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)"
|
||||
for available options.
|
||||
|
||||
## Example
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/pipewire-pulse.conf.d/custom.conf
|
||||
|
||||
pulse.rules = [
|
||||
{
|
||||
# skype does not want to use devices that don't have an S16 sample format.
|
||||
matches = [
|
||||
{ application.process.binary = "teams" }
|
||||
{ application.process.binary = "teams-insiders" }
|
||||
{ application.process.binary = "skypeforlinux" }
|
||||
]
|
||||
actions = { quirks = [ force-s16-info ] }
|
||||
}
|
||||
{
|
||||
# speech dispatcher asks for too small latency and then underruns.
|
||||
matches = [ { application.name = "~speech-dispatcher*" } ]
|
||||
actions = {
|
||||
update-props = {
|
||||
pulse.min.req = 1024/48000 # 21ms
|
||||
pulse.min.quantum = 1024/48000 # 21ms
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
# PULSEAUDIO COMMANDS @IDX@ pipewire-pulse.conf
|
||||
|
||||
As part of the server startup procedure you can execute some
|
||||
additional commands with the `pulse.cmd` section in
|
||||
`pipewire-pulse.conf`.
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/pipewire-pulse.conf.d/custom.conf
|
||||
|
||||
pulse.cmd = [
|
||||
{ cmd = "load-module" args = "module-always-sink" flags = [ ] }
|
||||
{ cmd = "load-module" args = "module-switch-on-connect" }
|
||||
{ cmd = "load-module" args = "module-gsettings" flags = [ "nofail" ] }
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
Additional commands can also be run via the
|
||||
\ref pipewire_conf__context_exec "context.exec section, see pipewire.conf(5)".
|
||||
|
||||
Supported commands:
|
||||
|
||||
@PAR@ pipewire-pulse.conf load-module
|
||||
Load the specified Pulseaudio module on startup, as if using **pactl(1)**
|
||||
to load the module.
|
||||
|
||||
# PULSEAUDIO MODULES @IDX@ pipewire-pulse.conf
|
||||
|
||||
For contents of section `pulse.modules`,
|
||||
see \ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)".
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)",
|
||||
\ref page_man_pipewire_conf_5 "pipewire.conf(5)",
|
||||
\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)",
|
||||
\ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)"
|
696
doc/dox/config/pipewire.conf.5.md
Normal file
696
doc/dox/config/pipewire.conf.5.md
Normal file
|
@ -0,0 +1,696 @@
|
|||
\page page_man_pipewire_conf_5 pipewire.conf
|
||||
|
||||
The PipeWire server configuration file
|
||||
|
||||
\tableofcontents
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/pipewire.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/pipewire.conf*
|
||||
|
||||
*$(PIPEWIRE_CONFDATADIR)/pipewire.conf.d/*
|
||||
|
||||
*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf.d/*
|
||||
|
||||
*$XDG_CONFIG_HOME/pipewire/pipewire.conf.d/*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
PipeWire is a service that facilitates sharing of multimedia content
|
||||
between devices and applications.
|
||||
|
||||
On startup, the daemon reads a main configuration file to configure
|
||||
itself. It executes a series of commands listed in the config file.
|
||||
|
||||
The config file is looked up in the order listed in the
|
||||
[SYNOPSIS](#synopsis). The environment variables `PIPEWIRE_CONFIG_DIR`,
|
||||
`PIPEWIRE_CONFIG_PREFIX` and `PIPEWIRE_CONFIG_NAME` can be used to
|
||||
specify an alternative config directory, subdirectory and file
|
||||
respectively.
|
||||
|
||||
Other PipeWire configuration files generally follow the same lookup
|
||||
logic, replacing `pipewire.conf` with the name of the particular
|
||||
config file.
|
||||
|
||||
# DROP-IN CONFIGURATION FILES @IDX@ pipewire.conf
|
||||
|
||||
All `*.conf` files in the `pipewire.conf.d/` directories are loaded
|
||||
and merged into the configuration. Dictionary sections are merged,
|
||||
overriding properties if they already existed, and array sections are
|
||||
appended to. The drop-in files have same format as the main
|
||||
configuration file, but only contain the settings to be modified.
|
||||
|
||||
As the `pipewire.conf` configuration file contains various parts
|
||||
that must be present for correct functioning, using drop-in files
|
||||
for configuration is recommended.
|
||||
|
||||
## Example
|
||||
|
||||
A configuration file `~/.config/pipewire/pipewire.conf.d/custom.conf`
|
||||
to change the value of the `default.clock.min-quantum` setting in `pipewire.conf`:
|
||||
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.properties = {
|
||||
default.clock.min-quantum = 128
|
||||
}
|
||||
```
|
||||
|
||||
# CONFIGURATION FILE FORMAT @IDX@ pipewire.conf
|
||||
|
||||
The configuration file is in "SPA" JSON format.
|
||||
|
||||
The configuration file contains top-level keys, which are the sections.
|
||||
The value of a section is either a dictionary, `{ }`, or an
|
||||
array, `[ ]`. Section and dictionary item declarations
|
||||
have `KEY = VALUE` form, and are separated by whitespace.
|
||||
For example:
|
||||
|
||||
```
|
||||
context.properties = { # top-level dictionary section
|
||||
|
||||
key1 = value # a simple value
|
||||
|
||||
key2 = { key1 = value1 key2 = value2 } # a dictionary with two entries
|
||||
|
||||
key3 = [ value1 value2 ] # an array with two entries
|
||||
|
||||
key4 = [ { k = v1 } { k = v2 } ] # an array of dictionaries
|
||||
|
||||
}
|
||||
|
||||
context.modules = [ # top-level array section
|
||||
|
||||
value1
|
||||
|
||||
value2
|
||||
|
||||
]
|
||||
```
|
||||
|
||||
The configuration files can also be written in standard JSON syntax,
|
||||
but for easier manual editing, the relaxed "SPA" variant is allowed.
|
||||
In "SPA" JSON:
|
||||
|
||||
- `:` to delimit keys and values can be substituted by `=` or a space.
|
||||
- <tt>\"</tt> around keys and string can be omitted as long as no special
|
||||
characters are used in the strings.
|
||||
- `,` to separate objects can be replaced with a whitespace character.
|
||||
- `#` can be used to start a comment until the line end
|
||||
|
||||
# CONFIGURATION FILE SECTIONS @IDX@ pipewire.conf
|
||||
|
||||
\par context.properties
|
||||
Dictionary. These properties configure the PipeWire instance.
|
||||
|
||||
\par context.spa-libs
|
||||
Dictionary. Maps plugin features with globs to a spa library.
|
||||
|
||||
\par context.modules
|
||||
Array of dictionaries. Each entry in the array is a dictionary with the
|
||||
*name* of the module to load, including optional *args* and *flags*.
|
||||
Most modules support being loaded multiple times.
|
||||
|
||||
\par context.objects
|
||||
Array of dictionaries. Each entry in the array is a dictionary
|
||||
containing the *factory* to create an object from and optional extra
|
||||
arguments specific to that factory.
|
||||
|
||||
\par context.exec
|
||||
\parblock
|
||||
Array of dictionaries. Each entry in the array is dictionary containing
|
||||
the *path* of a program to execute on startup and optional *args*.
|
||||
|
||||
This array used to contain an entry to start the session manager but
|
||||
this mode of operation has since been demoted to development aid. Avoid
|
||||
starting a session manager in this way in production environment.
|
||||
\endparblock
|
||||
|
||||
\par node.rules
|
||||
Array of dictionaries. Match rules for modifying node properties
|
||||
on the server.
|
||||
|
||||
\par device.rules
|
||||
Array of dictionaries. Match rules for modifying device properties
|
||||
on the server.
|
||||
|
||||
|
||||
# CONTEXT PROPERTIES @IDX@ pipewire.conf
|
||||
|
||||
Available PipeWire properties in `context.properties` and possible
|
||||
default values.
|
||||
|
||||
@PAR@ pipewire.conf clock.power-of-two-quantum = true
|
||||
The quantum requests from the clients and the final graph quantum are
|
||||
rounded down to a power of two. A power of two quantum can be more
|
||||
efficient for many processing tasks.
|
||||
|
||||
@PAR@ pipewire.conf context.data-loop.library.name.system
|
||||
The name of the shared library to use for the system functions for the data processing
|
||||
thread. This can typically be changed if the data thread is running on a realtime
|
||||
kernel such as EVL.
|
||||
|
||||
@PAR@ pipewire.conf loop.rt-prio = -1
|
||||
The priority of the data loops. The data loops are used to schedule the nodes in the graph.
|
||||
A value of -1 uses the default realtime priority from the module-rt. A value of 0 disables
|
||||
realtime scheduling for the data loops.
|
||||
|
||||
@PAR@ pipewire.conf loop.class = [ data.rt .. ]
|
||||
An array of classes of the data loops. Normally nodes are assigned to a loop by name or by class.
|
||||
Nodes are by default assigned to the data.rt class so it is good to have a data loop
|
||||
of this class as well.
|
||||
|
||||
@PAR@ pipewire.conf context.num-data-loops = 1
|
||||
The number of data loops to create. By default 1 data-loop is created and all nodes are
|
||||
scheduled in this thread. A value of 0 disables the real-time data loops and schedules
|
||||
all nodes in the main thread. A value of -1 spawns as many data threads as there are
|
||||
cpu cores.
|
||||
|
||||
@PAR@ pipewire.conf context.data-loops = [ ... ]
|
||||
This controls the data loops that will be created for the context. Is is an array of
|
||||
data loop specifications, one entry for each data loop to start:
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.data-loops = [
|
||||
{
|
||||
#library.name.system = support/libspa-support
|
||||
loop.rt-prio = -1
|
||||
loop.class = [ data.rt .. ]
|
||||
thread.name = data-loop.0
|
||||
thread.affinity = [ 0 1 ]
|
||||
}
|
||||
...
|
||||
]
|
||||
```
|
||||
A specific priority, classes and name can be given with loop.rt-prio, loop.class and
|
||||
thread.name respectively. It is also possible to pin the data loop to specific CPU
|
||||
cores with the thread.affinity property.
|
||||
|
||||
@PAR@ pipewire.conf core.daemon = false
|
||||
Makes the PipeWire process, started with this config, a daemon
|
||||
process. This means that it will manage and schedule a graph for
|
||||
clients. You would also want to configure a core.name to give it a
|
||||
well known name.
|
||||
|
||||
@PAR@ pipewire.conf core.name = pipewire-0
|
||||
The name of the PipeWire context. This will also be the name of the
|
||||
PipeWire socket clients can connect to.
|
||||
|
||||
@PAR@ pipewire.conf cpu.zero.denormals = false
|
||||
Configures the CPU to zero denormals automatically. This will be
|
||||
enabled for the data processing thread only, when enabled.
|
||||
|
||||
@PAR@ pipewire.conf cpu.vm.name = null
|
||||
This will be set automatically when the context is created and will
|
||||
contain the name of the VM. It is typically used to write match rules
|
||||
to set extra properties.
|
||||
|
||||
@PAR@ pipewire.conf default.clock.rate = 48000
|
||||
The default clock rate determines the real time duration of the
|
||||
min/max/default quantums. You might want to change the quantums when
|
||||
you change the default clock rate to maintain the same duration for
|
||||
the quantums.
|
||||
|
||||
@PAR@ pipewire.conf default.clock.allowed-rates = [ ]
|
||||
It is possible to specify up to 32 alternative sample rates. The graph
|
||||
sample rate will be switched when devices are idle. Note that this is
|
||||
not enabled by default for now because of various kernel and Bluetooth
|
||||
issues. Note that the min/max/default quantum values are scaled when
|
||||
the samplerate changes.
|
||||
|
||||
@PAR@ pipewire.conf default.clock.min-quantum = 32
|
||||
Default minimum quantum.
|
||||
|
||||
@PAR@ pipewire.conf default.clock.max-quantum = 8192
|
||||
Default maximum quantum.
|
||||
|
||||
@PAR@ pipewire.conf default.clock.quantum = 1024
|
||||
Default quantum used when no client specifies one.
|
||||
|
||||
@PAR@ pipewire.conf default.clock.quantum-limit = 8192
|
||||
Maximum quantum to reserve space for. This is the maximum buffer size used
|
||||
in the graph, regardless of the samplerate.
|
||||
|
||||
@PAR@ pipewire.conf default.clock.quantum-floor = 4
|
||||
Minimum quantum to reserve space for. This is the minimum buffer size used
|
||||
in the graph, regardless of the samplerate.
|
||||
|
||||
@PAR@ pipewire.conf default.video.width
|
||||
Default video width
|
||||
|
||||
@PAR@ pipewire.conf default.video.height
|
||||
Default video height
|
||||
|
||||
@PAR@ pipewire.conf default.video.rate.num
|
||||
Default video rate numerator
|
||||
|
||||
@PAR@ pipewire.conf default.video.rate.denom
|
||||
Default video rate denominator
|
||||
|
||||
@PAR@ pipewire.conf library.name.system = support/libspa-support
|
||||
The name of the shared library to use for the system functions for the main thread.
|
||||
|
||||
@PAR@ pipewire.conf link.max-buffers = 64
|
||||
The maximum number of buffers to negotiate between nodes. Note that version < 3 clients
|
||||
can only support 16 buffers. More buffers is almost always worse than less, latency
|
||||
and memory wise.
|
||||
|
||||
@PAR@ pipewire.conf log.level = 2
|
||||
The default log level used by the process.
|
||||
|
||||
@PAR@ pipewire.conf mem.allow-mlock = true
|
||||
Try to mlock the memory for the realtime processes. Locked memory will
|
||||
not be swapped out by the kernel and avoid hickups in the processing
|
||||
threads.
|
||||
|
||||
@PAR@ pipewire.conf mem.warn-mlock = false
|
||||
Warn about failures to lock memory.
|
||||
|
||||
@PAR@ pipewire.conf mem.mlock-all = false
|
||||
Try to mlock all current and future memory by the process.
|
||||
|
||||
@PAR@ pipewire.conf settings.check-quantum = false
|
||||
Check if the quantum in the settings metadata update is compatible
|
||||
with the configured limits.
|
||||
|
||||
@PAR@ pipewire.conf settings.check-rate = false
|
||||
Check if the rate in the settings metadata update is compatible
|
||||
with the configured limits.
|
||||
|
||||
@PAR@ pipewire.conf support.dbus = true
|
||||
Enable DBus support. This will enable DBus support in the various modules that require
|
||||
it. Disable this if you want to globally disable DBus support in the process.
|
||||
|
||||
@PAR@ pipewire.conf vm.overrides = { default.clock.min-quantum = 1024 }
|
||||
Any property in the vm.overrides property object will override the property
|
||||
in the context.properties when PipeWire detects it is running in a VM. This
|
||||
is deprecated, use the context.properties.rules instead.
|
||||
|
||||
@PAR@ pipewire.conf context.modules.allow-empty = false
|
||||
By default, a warning is logged when there are no context.modules loaded because this
|
||||
likely indicates there is a problem. Some applications might load the modules themselves
|
||||
and when they set this property to true, no warning will be logged.
|
||||
|
||||
The context properties may also contain custom values. For example,
|
||||
the `context.modules` and `context.objects` sections can declare
|
||||
additional conditions that control whether a module or object is loaded
|
||||
depending on what properties are present.
|
||||
|
||||
# SPA LIBRARIES @IDX@ pipewire.conf
|
||||
|
||||
SPA plugins are loaded based on their factory-name. This is a well
|
||||
known name that uniquely describes the features that the plugin should
|
||||
have. The `context.spa-libs` section provides a mapping between the
|
||||
factory-name and the plugin where the factory can be found.
|
||||
|
||||
Factory names can contain a wildcard to group several related factories into one
|
||||
plugin. The plugin is loaded from the first matching factory-name.
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.spa-libs = {
|
||||
audio.convert.* = audioconvert/libspa-audioconvert
|
||||
avb.* = avb/libspa-avb
|
||||
api.alsa.* = alsa/libspa-alsa
|
||||
api.v4l2.* = v4l2/libspa-v4l2
|
||||
api.libcamera.* = libcamera/libspa-libcamera
|
||||
api.bluez5.* = bluez5/libspa-bluez5
|
||||
api.vulkan.* = vulkan/libspa-vulkan
|
||||
api.jack.* = jack/libspa-jack
|
||||
support.* = support/libspa-support
|
||||
video.convert.* = videoconvert/libspa-videoconvert
|
||||
}
|
||||
```
|
||||
|
||||
# MODULES @IDX@ pipewire.conf
|
||||
|
||||
PipeWire modules to be loaded. See
|
||||
\ref page_man_libpipewire-modules_7 "libpipewire-modules(7)".
|
||||
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.modules = [
|
||||
#{ name = MODULENAME
|
||||
# ( args = { KEY = VALUE ... } )
|
||||
# ( flags = [ ( ifexists ) ( nofail ) ] )
|
||||
# ( condition = [ { KEY = VALUE ... } ... ] )
|
||||
#}
|
||||
#
|
||||
]
|
||||
```
|
||||
|
||||
\par name
|
||||
Name of module to be loaded
|
||||
|
||||
\par args = { }
|
||||
Arguments passed to the module
|
||||
|
||||
\par flags = [ ]
|
||||
Loading flags. `ifexists` to only load module if it exists,
|
||||
and `nofail` to not fail PipeWire startup if the module fails to load.
|
||||
|
||||
\par condition = [ ]
|
||||
A \ref pipewire_conf__match_rules "match rule" `matches` condition.
|
||||
The module is loaded only if one of the expressions in the array matches
|
||||
to a context property.
|
||||
|
||||
# CONTEXT OBJECTS @IDX@ pipewire.conf
|
||||
|
||||
The `context.objects` section allows you to make some objects from factories (usually created
|
||||
by loading modules in `context.modules`).
|
||||
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.objects = [
|
||||
#{ factory = <factory-name>
|
||||
# ( args = { <key> = <value> ... } )
|
||||
# ( flags = [ ( nofail ) ] )
|
||||
# ( condition = [ { <key> = <value> ... } ... ] )
|
||||
#}
|
||||
]
|
||||
```
|
||||
This section can be used to make nodes or links between nodes.
|
||||
|
||||
\par factory
|
||||
Name of the factory to create the object.
|
||||
|
||||
\par args = { }
|
||||
Arguments passed to the factory.
|
||||
|
||||
\par flags = [ ]
|
||||
Flag `nofail` to not fail PipeWire startup if the object fails to load.
|
||||
|
||||
\par condition = [ ]
|
||||
A \ref pipewire_conf__match_rules "match rule" `matches` condition.
|
||||
The object is created only if one of the expressions in the array matches
|
||||
to a context property.
|
||||
|
||||
## Example
|
||||
|
||||
This fragment creates a new dummy driver node, but only if
|
||||
`core.daemon` property is true:
|
||||
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.objects = [
|
||||
{ factory = spa-node-factory
|
||||
args = {
|
||||
factory.name = support.node.driver
|
||||
node.name = Dummy-Driver
|
||||
node.group = pipewire.dummy
|
||||
priority.driver = 20000
|
||||
},
|
||||
condition = [ { core.daemon = true } ]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
# COMMAND EXECUTION @IDX@ pipewire.conf
|
||||
|
||||
The `context.exec` section can be used to start arbitrary commands as
|
||||
part of the initialization of the PipeWire program.
|
||||
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.exec = [
|
||||
#{ path = <program-name>
|
||||
# ( args = "<arguments>" | [ <arg1> <arg2> ... ] )
|
||||
# ( condition = [ { <key> = <value> ... } ... ] )
|
||||
#}
|
||||
]
|
||||
```
|
||||
|
||||
\par path
|
||||
Program to execute.
|
||||
|
||||
\par args
|
||||
Arguments to the program.
|
||||
|
||||
\par condition
|
||||
A \ref pipewire_conf__match_rules "match rule" `matches` condition.
|
||||
The object is created only if one of the expressions in the array matches
|
||||
to a context property.
|
||||
|
||||
## Example
|
||||
|
||||
The following fragment executes a pactl command with the given arguments:
|
||||
|
||||
```
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.exec = [
|
||||
{ path = "pactl" args = "load-module module-always-sink" }
|
||||
]
|
||||
```
|
||||
|
||||
# MATCH RULES @IDX@ pipewire.conf
|
||||
|
||||
Some configuration file sections contain match rules. This makes it
|
||||
possible to perform some action when an object (usually a node or
|
||||
stream) is created/updated that matches certain properties.
|
||||
|
||||
The general rules object follows the following pattern:
|
||||
```css
|
||||
<rules> = [
|
||||
{
|
||||
matches = [
|
||||
# any of the following sets of properties are matched, if
|
||||
# any matches, the actions are executed
|
||||
{
|
||||
# <key> = <value>
|
||||
# all keys must match the value. ! negates. ~ starts regex.
|
||||
#application.process.binary = "teams"
|
||||
#application.name = "~speech-dispatcher.*"
|
||||
|
||||
# Absence of property can be tested by comparing to null
|
||||
#pipewire.sec.flatpak = null
|
||||
}
|
||||
{
|
||||
# more matches here...
|
||||
}
|
||||
...
|
||||
]
|
||||
actions = {
|
||||
<action-name> = <action value>
|
||||
...
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
Match rules are an array of rules.
|
||||
|
||||
A rule is always a JSON object with two keys: matches and actions. The matches key is used to
|
||||
define the conditions that need to be met for the rule to be evaluated as true, and the actions
|
||||
key is used to define the actions that are performed when the rule is evaluated as true.
|
||||
|
||||
The matches key is always a JSON array of objects, where each object defines a condition that needs
|
||||
to be met. Each condition is a list of key-value pairs, where the key is the name of the property
|
||||
that is being matched, and the value is the value that the property needs to have. Within a condition,
|
||||
all the key-value pairs are combined with a logical AND, and all the conditions in the matches
|
||||
array are combined with a logical OR.
|
||||
|
||||
The actions key is always a JSON object, where each key-value pair defines an action that is
|
||||
performed when the rule is evaluated as true. The action name is specific to the rule and is
|
||||
defined by the rule’s documentation, but most frequently you will see the update-props action,
|
||||
which is used to update the properties of the matched object.
|
||||
|
||||
In the matches array, it is also possible to use regular expressions to match property values.
|
||||
For example, to match all nodes with a name that starts with my_, you can use the following condition:
|
||||
|
||||
```css
|
||||
matches = [
|
||||
{
|
||||
node.name = "~my_.*"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The ~ character signifies that the value is a regular expression. The exact syntax of the regular
|
||||
expressions is the POSIX extended regex syntax, as described in the regex (7) man page.
|
||||
|
||||
In addition to regular expressions, you may also use the ! character to negate a condition. For
|
||||
example, to match all nodes with a name that does not start with my_, you can use the following condition:
|
||||
|
||||
```css
|
||||
matches = [
|
||||
{
|
||||
node.name = "!~my_.*"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The ! character can be used with or without a regular expression. For example, to match all
|
||||
nodes with a name that is not equal to my_node, you can use the following condition:
|
||||
|
||||
```css
|
||||
matches = [
|
||||
{
|
||||
node.name = "!my_node"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The null value has a special meaning; it checks if the property is not available
|
||||
(or unset). To check if a property is not set:
|
||||
|
||||
```css
|
||||
matches = [
|
||||
{
|
||||
node.name = null
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
To check the existence of a property, one can use the !null condition, for example:
|
||||
|
||||
```css
|
||||
matches = [
|
||||
{
|
||||
node.name = "!null"
|
||||
}
|
||||
{
|
||||
node.name = !null # simplified syntax
|
||||
}
|
||||
]
|
||||
```
|
||||
To handle the "null" string, one needs to escape the string. For example, to check
|
||||
if a property has the string value "null", use:
|
||||
|
||||
```css
|
||||
matches = [
|
||||
{
|
||||
node.name = "null"
|
||||
}
|
||||
]
|
||||
```
|
||||
To handle anything but the "null" string, use:
|
||||
|
||||
```css
|
||||
matches = [
|
||||
{
|
||||
node.name = "!\"null\""
|
||||
}
|
||||
{
|
||||
node.name = !"null" # simplified syntax
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
# CONTEXT PROPERTIES RULES @IDX@ pipewire.conf
|
||||
|
||||
`context.properties.rules` can be used to dynamically update the properties
|
||||
based on other properties.
|
||||
|
||||
A typical case is to update custom settings when running inside a VM.
|
||||
The `cpu.vm.name` is automatically set when running in a VM with the name of
|
||||
the VM. A match rule can be written to set custom properties like this:
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
context.properties.rules = [
|
||||
{ matches = [ { cpu.vm.name = !null } ]
|
||||
actions = {
|
||||
update-props = {
|
||||
# These overrides are only applied when running in a vm.
|
||||
default.clock.min-quantum = 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# NODE RULES @IDX@ pipewire.conf
|
||||
|
||||
The node.rules are evaluated every time the properties on a node are set
|
||||
or updated. This can be used on the server side to override client set
|
||||
properties on arbitrary nodes.
|
||||
|
||||
`node.rules` provides an `update-props` action that takes an object with
|
||||
properties that are updated on the node object.
|
||||
|
||||
Add a `node.rules` section in the config file like this:
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
node.rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
# all keys must match the value. ! negates. ~ starts regex.
|
||||
client.name = "jack_simple_client"
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
update-props = {
|
||||
node.force-quantum = 512
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Will set the `node.force-quantum` property of `jack_simple_client` to 512.
|
||||
|
||||
# DEVICE RULES @IDX@ pipewire.conf
|
||||
|
||||
The device.rules are evaluated every time the properties on a device are set
|
||||
or updated. This can be used on the server side to override client set
|
||||
properties on arbitrary devices.
|
||||
|
||||
`device.rules` provides an `update-props` action that takes an object with
|
||||
properties that are updated on the device object.
|
||||
|
||||
Add a `device.rules` section in the config file like this:
|
||||
|
||||
```css
|
||||
# ~/.config/pipewire/pipewire.conf.d/custom.conf
|
||||
|
||||
device.rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
# all keys must match the value. ! negates. ~ starts regex.
|
||||
device.name = ""v4l2_device.pci-0000_00_14.0-usb-0_1.2_1.0
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
update-props = {
|
||||
device.description = "My Webcam"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Will set the `device.description` property of the device with the given `device.name`
|
||||
to "My Webcam".
|
||||
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-mon_1 "pw-mon(1)",
|
||||
\ref page_man_libpipewire-modules_7 "libpipewire-modules(7)"
|
||||
\ref page_man_pipewire-pulse_conf_5 "pipewire-pulse.conf(5)"
|
||||
\ref page_man_pipewire-client_conf_5 "pipewire-client.conf(5)"
|
51
doc/dox/config/xref.md
Normal file
51
doc/dox/config/xref.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
\page page_config_xref Index
|
||||
|
||||
\ref page_man_pipewire_conf_5 "pipewire.conf"
|
||||
|
||||
@SECREF@ pipewire.conf
|
||||
|
||||
\ref page_man_pipewire-pulse_conf_5 "pipewire-pulse.conf"
|
||||
|
||||
@SECREF@ pipewire-pulse.conf
|
||||
|
||||
\ref page_man_pipewire-client_conf_5 "client.conf"
|
||||
|
||||
@SECREF@ client.conf
|
||||
|
||||
\ref page_man_pipewire-jack_conf_5 "jack.conf"
|
||||
|
||||
@SECREF@ jack.conf
|
||||
|
||||
**Runtime settings**
|
||||
|
||||
@SECREF@ pipewire-settings
|
||||
|
||||
**Environment variables**
|
||||
|
||||
@SECREF@ pipewire-env client-env jack-env pulse-env
|
||||
|
||||
**Object properties**
|
||||
|
||||
@SECREF@ props
|
||||
|
||||
**Monitor properties**
|
||||
|
||||
@SECREF@ monitor-prop
|
||||
|
||||
**Device properties**
|
||||
|
||||
@SECREF@ device-prop
|
||||
|
||||
**Node properties**
|
||||
|
||||
@SECREF@ node-prop
|
||||
|
||||
**Port properties**
|
||||
|
||||
@SECREF@ port-prop
|
||||
|
||||
**Client properties**
|
||||
|
||||
@SECREF@ client-prop
|
||||
|
||||
\see pw_keys in API documentation.
|
52
doc/dox/index.dox
Normal file
52
doc/dox/index.dox
Normal file
|
@ -0,0 +1,52 @@
|
|||
/** \mainpage PipeWire
|
||||
|
||||
PipeWire is low-level multimedia framework that provides:
|
||||
|
||||
- Graph based processing.
|
||||
- Support for out-of-process processing graphs with minimal overhead.
|
||||
- Flexible and extensible media format negotiation and buffer allocation.
|
||||
- Hard real-time capable plugins.
|
||||
- Very low-latency for both audio and video processing.
|
||||
|
||||
See \ref page_overview for an overview of PipeWire and \ref page_design
|
||||
for the design principles guiding PipeWire.
|
||||
|
||||
# Documentation
|
||||
|
||||
- \ref page_config
|
||||
- \ref page_programs
|
||||
- \ref page_modules
|
||||
- \ref page_pulse_modules
|
||||
|
||||
See our [Wiki](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home) for
|
||||
more information on how to configure and use PipeWire.
|
||||
|
||||
# Components
|
||||
|
||||
PipeWire ships with the following components:
|
||||
|
||||
- A \ref page_daemon that implements the IPC and graph processing.
|
||||
- An example \ref page_session_manager that manages objects in the \ref page_daemon.
|
||||
- A set of \ref page_programs to introspect and use the \ref page_daemon.
|
||||
- A \ref page_library to develop PipeWire applications and plugins (\ref
|
||||
page_tutorial "tutorial").
|
||||
- The \ref page_spa used by both the \ref page_daemon and in the \ref
|
||||
page_library.
|
||||
|
||||
# API Documentation
|
||||
|
||||
See \ref page_api.
|
||||
|
||||
# Resources
|
||||
|
||||
- [PipeWire and AGL](https://wiki.automotivelinux.org/_media/pipewire_agl_20181206.pdf)
|
||||
- [LAC 2020 Paper](https://lac2020.sciencesconf.org//data/proceedings.pdf) and
|
||||
[Video](https://tube.aquilenet.fr/w/uy8PJyMnBrpBFNEZ9D48Uu)
|
||||
- [PipeWire Under The Hood](https://venam.nixers.net/blog/unix/2021/06/23/pipewire-under-the-hood.html)
|
||||
- [PipeWire: The Linux audio/video bus (LWN)](https://lwn.net/Articles/847412)
|
||||
- [PipeWire Wikipedia](https://en.wikipedia.org/wiki/PipeWire)
|
||||
- [Bluetooth, PipeWire and Whatsapp calls](https://gjhenrique.com/pipewire.html)
|
||||
- [Intoduction to PipeWire](https://bootlin.com/blog/an-introduction-to-pipewire/)
|
||||
- [A custom PipeWire node](https://bootlin.com/blog/a-custom-pipewire-node/)
|
||||
|
||||
*/
|
124
doc/dox/internals/access.dox
Normal file
124
doc/dox/internals/access.dox
Normal file
|
@ -0,0 +1,124 @@
|
|||
/** \page page_access Access Control
|
||||
|
||||
This document explains how access control is designed and implemented.
|
||||
|
||||
PipeWire implements per client permissions on the objects in the graph.
|
||||
Permissions include `R` (read), `W` (write), `X` (execute) and `M` (metadata).
|
||||
|
||||
- `R`: An object with permission `R` is visible to the client. The client will receive
|
||||
registry events for the object and can interact with it.
|
||||
- `W`: An object with permission `W` can be modified. This is usually done
|
||||
through a method that modifies the state of the object. The `W` permission
|
||||
usually implies the `X` permission.
|
||||
- `X`: An object with permission `X` allows invoking methods on the object.
|
||||
Some of those methods will only query state, others will modify the object.
|
||||
As said above, modifying the object through one of these methods requires
|
||||
the `W` permission.
|
||||
- `M`: An object with `M` permission can be used as the subject in metadata.
|
||||
|
||||
Clients with all permissions set are referred to as "ALL" in the
|
||||
documentation.
|
||||
|
||||
|
||||
# Use Cases
|
||||
|
||||
## New Clients Need Their Permissions Configured
|
||||
|
||||
A new client is not allowed to communicate with the PipeWire daemon until
|
||||
it has been configured with permissions.
|
||||
|
||||
## Flatpaks Can't Modify Other Stream/Device Volumes
|
||||
|
||||
An application running as Flatpak should not be able to modify the state of
|
||||
certain objects. Permissions of the relevant PipeWire objects should not have
|
||||
the `W` permission to avoid this.
|
||||
|
||||
## Flatpaks Can't Move Other Streams To Different Devices
|
||||
|
||||
Streams are moved to another device by setting the `target.node` metadata
|
||||
on the node ID. By not setting the `M` bit on the other objects, this can be
|
||||
avoided.
|
||||
|
||||
## Application Should Be Restricted In What They Can See
|
||||
|
||||
In general, applications should only be able to see the objects that they are
|
||||
allowed to see. For example, a web browser that was given access to a camera
|
||||
should not be able to see (and thus receive input data from) audio devices.
|
||||
|
||||
## "Manager" Applications Require Full Access
|
||||
|
||||
Some applications require full access to the PipeWire graph, including
|
||||
moving streams between nodes (by setting metadata) and modifying properties
|
||||
(eg. volume). These applications must work even when running as Flatpak.
|
||||
|
||||
|
||||
# Design
|
||||
|
||||
## The PipeWire Daemon
|
||||
|
||||
Immediately after a new client connects to the PipeWire daemon and updates
|
||||
its properties, the client will be registered and made visible to other
|
||||
clients.
|
||||
|
||||
The PipeWire core will emit a `check_access` event in the \ref pw_context_events
|
||||
context for the the new client. The implementer of this event is responsible
|
||||
for assigning permissions to the client.
|
||||
|
||||
Clients with permission `R` on the core object can continue communicating
|
||||
with the daemon. Clients without permission `R` on the core are suspended
|
||||
and are not able to send more messages.
|
||||
|
||||
A suspended client can only resume processing after some other client
|
||||
sets the core permissions to `R`. This other client is usually a session
|
||||
manager, see e.g. \ref page_session_manager.
|
||||
|
||||
## The PipeWire Access Module
|
||||
|
||||
The \ref page_module_access hooks into the `check_access` event that is
|
||||
emitted when a new client is registered. The module checks the permissions of
|
||||
the client and stores those in the \ref PW_KEY_ACCESS
|
||||
property on the client object. If this property is already set, the access
|
||||
module does nothing.
|
||||
|
||||
If the property is not set it will go through a set of checks to determine
|
||||
the permissions for a client. See the \ref page_module_access documentation
|
||||
for details.
|
||||
|
||||
Depending on the resolution, it grants permissions to the client as follows:
|
||||
|
||||
- `"unrestricted"`: ALL permissions are set on the core
|
||||
object and the client will be able to resume.
|
||||
- any other value: No permissions are set on the core object
|
||||
and the client will be suspended.
|
||||
|
||||
As detailed above, the client may be suspended. In that case the session
|
||||
manager or another client is required to configure permissions on the object
|
||||
for it to resume.
|
||||
|
||||
## The Session Manager
|
||||
|
||||
The session manager listens for new clients to appear. It will use the
|
||||
\ref PW_KEY_ACCESS property to determine what to do.
|
||||
|
||||
For clients that are not unrestricted, the session manager needs to set permissions on the
|
||||
client for the various PipeWire objects in the graph that it is allowed to
|
||||
interact with. To resume a client, the session manager needs to set
|
||||
permission `R` on the core object for the client.
|
||||
|
||||
Permissions of objects for a client can be changed at any time by the
|
||||
session manager. Removing the client core permission `R` will suspend the
|
||||
client.
|
||||
|
||||
The session manager needs to do additional checks to determine if the
|
||||
manager permissions can be given to the particular client and then
|
||||
configure ALL permissions on the client. Possible checks include
|
||||
permission store checks or ask the user if the application is allowed
|
||||
full access.
|
||||
|
||||
Manager applications (ie. applications that need to modify the graph) will
|
||||
set the \ref PW_KEY_MEDIA_CATEGORY property in the client object to "Manager".
|
||||
|
||||
For details on the pipewire-media-session implementation of access control,
|
||||
see \ref page_media_session.
|
||||
|
||||
*/
|
127
doc/dox/internals/audio.dox
Normal file
127
doc/dox/internals/audio.dox
Normal file
|
@ -0,0 +1,127 @@
|
|||
/** \page page_audio Audio
|
||||
|
||||
This document explains how Audio is implemented.
|
||||
|
||||
# Use Cases
|
||||
|
||||
## Audio Devices Are Made Available As Processing Nodes/Ports
|
||||
|
||||
Applications need to be able to see a port for each stream of an
|
||||
audio device.
|
||||
|
||||
## Audio Devices Can Be Plugged and Unplugged
|
||||
|
||||
When devices are plugged and unplugged the associated nodes/ports
|
||||
need to be created and removed.
|
||||
|
||||
## Audio Port In Canonical Format
|
||||
|
||||
It must be possible to make individual audio channels available
|
||||
as a single mono stream with a fixed format and samplerate.
|
||||
|
||||
This makes it possible to link any of the audio ports together
|
||||
without doing conversions.
|
||||
|
||||
## Applications Can Connect To Audio Devices
|
||||
|
||||
Applications can create ports that can connect to the audio ports
|
||||
so that data can be provided to or consumed from them.
|
||||
|
||||
It should be possible to automatically connect an application to
|
||||
a sink/source when it requests this.
|
||||
|
||||
## Default Audio Sink and Sources
|
||||
|
||||
It should be possible to mark a source or sink as the default source
|
||||
and sink so that applications are routed to them by default.
|
||||
|
||||
It should be possible to change the default audio sink/source.
|
||||
|
||||
## Application Should Be Able To Move Between Sinks/Sources
|
||||
|
||||
It should be possible to move an application from one device to
|
||||
another dynamically.
|
||||
|
||||
## Exclusive Access
|
||||
|
||||
Application should be able to connect to a device in exclusive mode.
|
||||
This allows the application to negotiate a specific format with the
|
||||
device such as a compressed format.
|
||||
|
||||
Exclusive access means that only one application can access the device
|
||||
because mixing is in general not possible when negotiating
|
||||
compressed formats.
|
||||
|
||||
|
||||
# Design
|
||||
|
||||
## SPA
|
||||
|
||||
Audio devices are implemented with an \ref spa_device "SPA Device" object.
|
||||
|
||||
This object is then responsible for controlling the \ref spa_node "SPA Nodes" that
|
||||
provide the audio ports to interface with the device.
|
||||
|
||||
The nodes operate on the native audio formats supported by the device.
|
||||
This includes the sample rate as well as the number of channels and
|
||||
the audio format.
|
||||
|
||||
## Audio Adapter
|
||||
|
||||
An SPA Node called the "adapter" is usually used with the SPA device node as
|
||||
the internal node.
|
||||
|
||||
The function of the adapter is to convert the device native format to
|
||||
the required external format. This can include format or samplerate
|
||||
conversion but also channel remixing/remapping.
|
||||
|
||||
The audio adapter is also responsible for exposing the audio channels
|
||||
as separate mono ports. This is called the DSP setup.
|
||||
|
||||
The audio adapter can also be configured in passthrough mode when it
|
||||
will not do any conversions but simply pass through the port information
|
||||
of the internal node. This can be used to implement exclusive access.
|
||||
|
||||
Setup of the different configurations of the adapter can be done with
|
||||
the PortConfig parameter.
|
||||
|
||||
## The Session Manager
|
||||
|
||||
The session manager is responsible for creating nodes and ports for
|
||||
the various audio devices. It will need to wrap them into an audio
|
||||
adapter so that the specific configuration of the node can be
|
||||
decided by the policy mode.
|
||||
|
||||
The session manager should create name and description for the
|
||||
devices and nodes.
|
||||
|
||||
The session manager is responsible for assigning priorities to the
|
||||
nodes. At least \ref PW_KEY_PRIORITY_SESSION and \ref PW_KEY_PRIORITY_DRIVER
|
||||
need to be set.
|
||||
|
||||
The session manager might need to work with other services to gain
|
||||
exclusive access to the device (eg. DBus).
|
||||
|
||||
|
||||
# Implementation
|
||||
|
||||
## PipeWire Media Session (alsa-monitor)
|
||||
|
||||
PipeWire media session uses the \ref SPA_NAME_API_ALSA_ENUM_UDEV plugin
|
||||
for enumerating the ALSA devices. For each device it does:
|
||||
|
||||
- Try to acquire the DBus device reservation object to gain exclusive
|
||||
access to the device.
|
||||
- Create an SPA device instance for the device and monitor this device instance.
|
||||
- For each node created by the device, create an adapter with
|
||||
an ALSA PCM node in the context of the PipeWire daemon.
|
||||
|
||||
The session manager will also create suitable names and descriptions
|
||||
for the devices and nodes that it creates as well as assign session
|
||||
and driver priorities.
|
||||
|
||||
The session manager has the option to add extra properties on the
|
||||
devices and nodes that it creates to control their behavior. This
|
||||
is implemented with match rules.
|
||||
|
||||
*/
|
177
doc/dox/internals/daemon.dox
Normal file
177
doc/dox/internals/daemon.dox
Normal file
|
@ -0,0 +1,177 @@
|
|||
/** \page page_daemon PipeWire Daemon
|
||||
|
||||
The PipeWire daemon is the central process that manages data exchange between
|
||||
devices and clients.
|
||||
|
||||
Typically general, users run one PipeWire daemon that listens for incoming
|
||||
connections and manages devices. Clients (including the \ref
|
||||
page_session_manager) are separate processes that talk to the daemon using the
|
||||
PipeWire socket (default: `$XDG_RUNTIME_DIR/pipewire-0`). This approach
|
||||
provides address-space separation between the privileged daemon and
|
||||
non-privileged clients.
|
||||
|
||||
\dot
|
||||
digraph pw {
|
||||
compound=true;
|
||||
node [shape="box"];
|
||||
|
||||
subgraph cluster_pw {
|
||||
rankdir="TB";
|
||||
label="PipeWire daemon";
|
||||
style="dashed";
|
||||
|
||||
subgraph cluster_prot_native {
|
||||
label="pipewire-module-protocol-native";
|
||||
style="solid";
|
||||
socket [label="$XDG_RUNTIME_DIR/pipewire-0"];
|
||||
mod_impl [label="module implementation"];
|
||||
|
||||
socket -> mod_impl;
|
||||
}
|
||||
core [label="PipeWire Core"];
|
||||
alsa [label="PipeWire ALSA support"];
|
||||
|
||||
mod_impl -> core;
|
||||
core -> alsa;
|
||||
}
|
||||
|
||||
kernel
|
||||
|
||||
client1 [ label="Media Player" ];
|
||||
client2 [ label="Audio Software" ];
|
||||
sm [ label="Session Manager", style="dotted" ];
|
||||
|
||||
client1 -> socket;
|
||||
client2 -> socket;
|
||||
sm -> socket;
|
||||
alsa -> kernel;
|
||||
}
|
||||
\enddot
|
||||
|
||||
As shown above, the protocol is handled by the \ref
|
||||
page_module_protocol_native. From PipeWire's point-of-view this module is just
|
||||
another module.
|
||||
|
||||
# Configuration Files
|
||||
|
||||
On startup, the daemon reads a configuration file to configure itself.
|
||||
It executes a series of commands listed in the config file. The lookup order
|
||||
for configuration files are:
|
||||
|
||||
- `$XDG_CONFIG_HOME/pipewire/pipewire.conf` (usually `$HOME/.config/pipewire/pipewire.conf`)
|
||||
- `$sysconfdir/pipewire/pipewire.conf` (usually `/etc/pipewire/pipewire.conf`)
|
||||
- `$datadir/pipewire/pipewire.conf` (usually `/usr/share/pipewire/pipewire.conf`)
|
||||
|
||||
The first configuration file found is loaded as the base configuration.
|
||||
|
||||
Next, configuration sections (from files ending with a .conf extension) are collected
|
||||
in the directories in this order:
|
||||
|
||||
- `$datadir/pipewire/pipewire.conf.d/` (usually `/usr/share/pipewire/pipewire.conf.d/`)
|
||||
- `$sysconfdir/pipewire/pipewire.conf.d/` (usually `/etc/pipewire/pipewire.conf.d/`)
|
||||
- `$XDG_CONFIG_HOME/pipewire/pipewire.conf.d/` (usually `$HOME/.config/pipewire/pipewire.conf.d/`)
|
||||
|
||||
They are applied to the global configuration file. Properties are overwritten
|
||||
and array elements are appended. This makes it possible to make small custom customizations
|
||||
or additions to the main configuration file.
|
||||
|
||||
The environment variables `PIPEWIRE_CONFIG_DIR`, `PIPEWIRE_CONFIG_PREFIX`,
|
||||
and `PIPEWIRE_CONFIG_NAME`. Can be used to specify an alternative configuration
|
||||
directory, subdirectory, and filename respectively.
|
||||
|
||||
## Configuration File Format
|
||||
|
||||
PipeWire's configuration file format is JSON. In addition to true JSON
|
||||
PipeWire also understands a more compact JSON representation. Where
|
||||
`"` can be omitted around strings, no trailing commas are required and
|
||||
`:` or `=` can be used to separate object keys from their values.
|
||||
Also, `#` can be used to start a comment until the end of the line.
|
||||
|
||||
The configuration file format is grouped into sections. A section is
|
||||
either a dictionary (`{}`) or an array (`[]`). Dictionary and array entries
|
||||
are separated by whitespace and may be simple value assignment, an array or
|
||||
a dictionary. For example:
|
||||
|
||||
```
|
||||
# A dictionary section
|
||||
context.properties = {
|
||||
# Keys often have a dot notation
|
||||
core.daemon = true
|
||||
}
|
||||
|
||||
# An array section containing three dictionary objects
|
||||
context.modules = [
|
||||
# a dictionary object with one key assigned to a string
|
||||
{ name = libpipewire-module-protocol-native }
|
||||
{ name = libpipewire-module-profiler }
|
||||
|
||||
# a dictionary object with two keys, one assigned to a string
|
||||
# the other one to an array of strings
|
||||
{ name = libpipewire-module-portal
|
||||
flags = [ ifexists nofail ]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Allowed configuration file sections are:
|
||||
|
||||
- **context.properties** (dictionary): These properties configure the
|
||||
pipewire instance.
|
||||
- **context.spa-libs** (dictionary): Maps plugin features with globs to a
|
||||
spa library.
|
||||
- **context.modules** (array): Each entry in the array is a dictionary with
|
||||
the name of the module to load, including optional args and flags. Most
|
||||
modules support being loaded multiple times.
|
||||
- **context.objects** (array): Each entry in the array is a dictionary con‐
|
||||
taining the factory to create an object from and optional extra argu‐
|
||||
ments specific to that factory.
|
||||
- **context.exec** (array): Each entry in the array is dictionary containing
|
||||
the path of a program to execute on startup and optional args. This ar‐
|
||||
ray usually contains an entry to start the session manager.
|
||||
|
||||
|
||||
# Logging
|
||||
|
||||
The `PIPEWIRE_DEBUG` environment variable can be used to enable
|
||||
more debugging. This variable supports the following format:
|
||||
|
||||
- `PIPEWIRE_DEBUG=[<level>][,<glob1>:<level1>][,<glob2>:<level2>,...]` where the globs are
|
||||
shell globs to match on log topics and the levels are the respective
|
||||
log level to set for that topic. Globs are applied in order and a matching
|
||||
glob overrides an earlier glob for that category. A level without a glob
|
||||
prefix will set the global log level and is a more performant version of
|
||||
`*:<level>`. For example, `PIPEWIRE_DEBUG=E,mod.*:D,mod.foo:X` enables global error messages,
|
||||
debugging on all modules but no messages on the foo module.
|
||||
- `<level>` specifies the log level:
|
||||
|
||||
+ `X` or `0`: No logging is enabled.
|
||||
+ `E` or `1`: Error logging is enabled.
|
||||
+ `W` or `2`: Warnings are enabled.
|
||||
+ `I` or `3`: Informational messages are enabled.
|
||||
+ `D` or `4`: Debug messages are enabled.
|
||||
+ `T` or `5`: Trace messages are enabled. These messages can be logged
|
||||
from the realtime threads.
|
||||
|
||||
PipeWire uses a `category.topic` naming scheme, with the following categories:
|
||||
|
||||
- `pw.*`: PipeWire internal topics.
|
||||
- `mod.*`: Module topics, for example `mod.foo` would usually refer to the
|
||||
`foo` module.
|
||||
- `ms.*`: Media session topics.
|
||||
- `ms.mod.*`: Media session modules, for example `ms.foo` would usually refer
|
||||
to the `media-session-foo` module.
|
||||
- `conn.*`: Connection specific topics such as printing raw messages sent over
|
||||
a communication socket. These are in a separate namespace as they are
|
||||
usually vastly more verbose than the normal debugging topics.
|
||||
This namespace must be explicitly enabled with a `conn.<glob>` glob.
|
||||
|
||||
The behavior of the logging can be further controlled with the following
|
||||
environment variables:
|
||||
|
||||
- `PIPEWIRE_LOG_SYSTEMD=false`: Disable logging to the systemd journal.
|
||||
- `PIPEWIRE_LOG=<filename>`: Redirect the log to the given filename.
|
||||
- `PIPEWIRE_LOG_LINE=false`: Don't log filename, function, and source code line.
|
||||
- `PIPEWIRE_LOG_COLOR=true/false/force`: Enable/disable color logging, and optionally force
|
||||
colors even when logging to a file.
|
||||
|
||||
*/
|
70
doc/dox/internals/design.dox
Normal file
70
doc/dox/internals/design.dox
Normal file
|
@ -0,0 +1,70 @@
|
|||
/** \page page_design Design
|
||||
|
||||
A short overview of PipeWire's design.
|
||||
|
||||
PipeWire is a media server that can run graphs of multimedia nodes.
|
||||
Nodes can run inside the server process or in separate processes,
|
||||
communicating with the server.
|
||||
|
||||
PipeWire was designed to:
|
||||
|
||||
- Be efficient for raw video using fd passing and audio with
|
||||
shared ringbuffers.
|
||||
- Be able to provide/consume/process media from any process.
|
||||
- Provide policy to restrict access to devices and streams.
|
||||
- Be easily extensible.
|
||||
|
||||
Although an initial goal, the design is not limited to raw video
|
||||
only and should be able to handle compressed video and other
|
||||
media as well.
|
||||
|
||||
PipeWire uses the \ref page_spa "SPA plugin API" for the nodes in the graph.
|
||||
SPA is designed for low-latency and efficient processing of any multimedia
|
||||
format. SPA also provides a number of helper utilities that are not available
|
||||
in the standard C library.
|
||||
|
||||
Some of the application we intend to build:
|
||||
|
||||
- v4l2 device provider: Provide controlled access to v4l2 devices
|
||||
and share one device between multiple processes.
|
||||
- gnome-shell video provider: GNOME Shell provides a node that
|
||||
gives the contents of the frame buffer for screen sharing or
|
||||
screen recording.
|
||||
- Audio server: Mix and playback multiple audio streams. The design
|
||||
is more like CRAS (Chromium audio server) than PulseAudio and with
|
||||
the added benefit that processing can be arranged in a graph.
|
||||
- Professional audio graph processing like JACK.
|
||||
- Media playback backend.
|
||||
|
||||
|
||||
# Protocol
|
||||
|
||||
The native protocol and object model is similar to
|
||||
[Wayland](https://wayland.freedesktop.org) but with custom
|
||||
serialization/deserialization of messages. This is because the data structures
|
||||
in the messages are more complicated and not easily expressible in XML.
|
||||
See \ref page_module_protocol_native for details.
|
||||
|
||||
|
||||
# Extensibility
|
||||
|
||||
The functionality of the server is implemented and extended with modules and
|
||||
extensions. Modules are server side bits of logic that hook into various
|
||||
places to provide extra features. This mostly means controlling the processing
|
||||
graph in some way. See \ref page_modules for a list of current
|
||||
modules.
|
||||
|
||||
Extensions are the client side version of the modules. Most extensions provide
|
||||
both a client side and server side init function. New interfaces or new object
|
||||
implementation can easily be added with modules/extensions.
|
||||
|
||||
Some of the extensions that can be written:
|
||||
|
||||
- Protocol extensions: A client/server side API (.h) together with protocol
|
||||
extensions and server/client side logic to implement a new object or
|
||||
interface.
|
||||
- A module to check security of method calls.
|
||||
- A module to automatically create, link or relink nodes.
|
||||
- A module to suspend idle nodes.
|
||||
|
||||
*/
|
305
doc/dox/internals/dma-buf.dox
Normal file
305
doc/dox/internals/dma-buf.dox
Normal file
|
@ -0,0 +1,305 @@
|
|||
/** \page page_dma_buf DMA-BUF Sharing
|
||||
|
||||
PipeWire supports sharing Direct Memory Access buffers (DMA-BUFs) between
|
||||
clients via the \ref SPA_DATA_DmaBuf data type. However properly negotiating
|
||||
DMA-BUF support on both the producer and the consumer side require following
|
||||
a specific procedure. This page describes said procedure by using events and
|
||||
methods from the filter or stream API.
|
||||
|
||||
Note: This article focuses mostly on DMA-BUF sharing from arbitrary devices,
|
||||
like discrete GPUs. For using DMA-BUFs created by v4l2 please refer to the
|
||||
corresponding paragraph.
|
||||
|
||||
# Capability Negotiations
|
||||
|
||||
The capability negotiation for DMA-BUFs is complicated by the fact that a
|
||||
usable and preferred optimal modifier for a given format can only be
|
||||
determined by the allocator. This allocator has to be invoked with the intersection
|
||||
of all supported modifiers for every client. As a result, the fixation of the
|
||||
modifier is delegated from PipeWire to the node responsible for
|
||||
allocating the buffers.
|
||||
|
||||
## pw_stream_connect
|
||||
|
||||
The stream parameters should contain two \ref SPA_PARAM_EnumFormat objects for
|
||||
each format: one for DMA-BUFs, one for shared memory buffers as a fallback.
|
||||
|
||||
Query the list of all supported modifiers from your graphics API of choice.
|
||||
Add a \ref SPA_FORMAT_VIDEO_modifier property to the first stream parameter with
|
||||
the flags `SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE`. The
|
||||
value of the property should be set to a \ref SPA_CHOICE_Enum containing one
|
||||
`long` choice per supported modifier, plus `DRM_FORMAT_MOD_INVALID` if the
|
||||
graphics API supports modifier-less buffers.
|
||||
|
||||
Note: When a producer is only supporting modifier-less buffers it can omit
|
||||
the \ref SPA_POD_PROP_FLAG_DONT_FIXATE (see param_changed hook, For producers).
|
||||
|
||||
The second stream parameter should not contain any \ref SPA_FORMAT_VIDEO_modifier
|
||||
property.
|
||||
|
||||
To prioritise DMA-BUFs place those \ref SPA_PARAM_EnumFormat containing modifiers
|
||||
first, when emitting them to PipeWire.
|
||||
|
||||
## param_changed Hook
|
||||
|
||||
When the `param_changed` hook is called for a \ref SPA_PARAM_Format the client
|
||||
has to parse the `spa_pod` directly. Use
|
||||
`spa_pod_find_prop(param, NULL, SPA_FORMAT_VIDEO_modifier)` to check
|
||||
whether modifiers were negotiated. If they were negotiated, set the
|
||||
\ref SPA_PARAM_BUFFERS_dataType property to `1 << SPA_DATA_DmaBuf`. If they were
|
||||
not negotiated, fall back to shared memory by setting the
|
||||
\ref SPA_PARAM_BUFFERS_dataType property to `1 << SPA_DATA_MemFd`,
|
||||
`1 << SPA_DATA_MemPtr`, or both.
|
||||
|
||||
While consumers only have to parse the resulting \ref SPA_PARAM_Format for any
|
||||
format related information, it's up to the producer to fixate onto a single
|
||||
format modifier pair. The producer is also responsible to check if all clients
|
||||
announce sufficient capabilities or fallback to shared memory buffers when
|
||||
possible.
|
||||
|
||||
### For Consumers
|
||||
|
||||
Use \ref spa_format_video_raw_parse to get the format and modifier.
|
||||
|
||||
### For Producers
|
||||
|
||||
Producers have to handle two cases when it comes to modifiers wrt. to the
|
||||
previous announced capabilities: Using only the modifier-less API, only the
|
||||
modifier-aware one, or supporting both.
|
||||
|
||||
- modifier-less:
|
||||
In this case only the modifier `DRM_FORMAT_MOD_INVALID` was announced with
|
||||
the format.
|
||||
It is sufficient to check if the \ref SPA_PARAM_Format contains the modifier
|
||||
property as described above. If that is the case, use DMA-BUFs for screen-sharing,
|
||||
else fall back to SHM, if possible.
|
||||
- modifier-aware:
|
||||
In this case a list with all supported modifiers will be returned in the format.
|
||||
(using `DRM_FORMAT_MOD_INVALID` as the token for the modifier-less API).
|
||||
On the `param_changed` event check if the modifier key is present and has the flag
|
||||
\ref SPA_POD_PROP_FLAG_DONT_FIXATE attached to it. In this case, extract all modifiers
|
||||
from the list and do a test allocation with your allocator to choose the preferred
|
||||
modifier. Fixate on that \ref EnumFormat by announcing a \ref SPA_PARAM_EnumFormat with
|
||||
only one modifier in the \ref SPA_CHOICE_Enum and without the
|
||||
\ref SPA_POD_PROP_FLAG_DONT_FIXATE flag, followed by the previous announced
|
||||
\ref EnumFormat. This will retrigger the `param_changed` event with an
|
||||
\ref SPA_PARAM_Format as described below.
|
||||
If the \ref SPA_PARAM_Format contains a modifier key, without the flag
|
||||
\ref SPA_POD_PROP_FLAG_DONT_FIXATE, it should only contain one value in the
|
||||
\ref SPA_CHOICE_Enum. In this case announce the \ref SPA_PARAM_Buffers accordingly
|
||||
to the selected format and modifier. It is important to query the plane count
|
||||
of the used format modifier pair and set `SPA_PARAM_BUFFERS_blocks` accordingly.
|
||||
You might also want to add the option of adding explicit sync support to the
|
||||
buffers, as explained below.
|
||||
|
||||
Note: When test allocating a buffer, collect all possible modifiers, while omitting
|
||||
`DRM_FORMAT_MOD_INVALID` from the \ref SPA_FORMAT_VIDEO_modifier property and
|
||||
pass them all to the graphics API. If the allocation fails and the list of
|
||||
possible modifiers contains `DRM_FORMAT_MOD_INVALID`, fall back to allocating
|
||||
without an explicit modifier if the graphics API allows it.
|
||||
|
||||
## add_buffer Hook
|
||||
|
||||
This is relevant for producers.
|
||||
|
||||
Allocate a DMA-BUF only using the negotiated format and modifier.
|
||||
|
||||
## on_event Hook
|
||||
|
||||
This is relevant for consumers.
|
||||
|
||||
Check the type of the dequeued buffer. If its \ref SPA_DATA_MemFd or
|
||||
\ref SPA_DATA_MemPtr use the fallback SHM import mechanism.
|
||||
If it's \ref SPA_DATA_DmaBuf
|
||||
get the DMA-BUF FDs (the plane count is encoded in the `n_datas` variable of the
|
||||
`spa_buffer` struct) and import them with the graphics API. Note: that the n_datas
|
||||
might also contain extra fds for things like sync_timelime metadata, you need
|
||||
to take this into account when persing the planes.
|
||||
|
||||
Note: Some graphics APIs have separated functions for the modifier-less case
|
||||
(`DRM_FORMAT_MOD_INVALID`) or are omitting the modifier, since it might be used
|
||||
for error handling.
|
||||
|
||||
## Example Programs
|
||||
|
||||
- \ref video-src-fixate.c "": \snippet{doc} video-src-fixate.c title
|
||||
- \ref video-play-fixate.c "": \snippet{doc} video-play-fixate.c title
|
||||
|
||||
# DMA-BUF Mapping Warning
|
||||
|
||||
It's important to make sure all consumers of the PipeWire stream are prepared
|
||||
to deal with DMA-BUFs. Most DMA-BUFs cannot be treated like shared memory in general
|
||||
because of the following issues:
|
||||
|
||||
- DMA-BUFs can use hardware-specific tiling and compression as described by
|
||||
modifiers. Thus, a `mmap(3)` on the DMA-BUF FD will not give a linear view of
|
||||
the buffer contents.
|
||||
- DMA-BUFs need to be properly synchronized with the asynchronous reads and
|
||||
writes of the hardware. A `mmap(3)` call is not enough to guarantee proper
|
||||
synchronization. (Maybe add link to linux syscall doc??)
|
||||
- Blindly accessing the DMA-BUFs via `mmap(3)` can be extremely slow if the
|
||||
buffer has been allocated on discrete hardware. Consumers are better off
|
||||
using a proper graphics API (such as EGL, Vulkan or VA-API) to process the
|
||||
DMA-BUFs.
|
||||
|
||||
# Size of DMA-BUFs
|
||||
|
||||
When importing a DMA-BUF with a proper graphics API the size of a single buffer plane
|
||||
is no relevant property since it will be derived by the driver from the other properties.
|
||||
Therefore consumers should ignore the field `maxsize` of a `spa_data` and the field
|
||||
`size` of a `spa_chunk` struct. Producers are allowed to set both to 0.
|
||||
In cases where mapping a single plane is required the size should be obtained locally
|
||||
via the filedescriptor.
|
||||
|
||||
# SPA param video format helpers
|
||||
|
||||
SPA offers helper functions to parse and build a spa_pod object to/from the spa_video_info_*
|
||||
struct. The flags \ref SPA_VIDEO_FLAG_MODIFIER and \ref SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED
|
||||
are used to indicate modifier usage with the format. `SPA_VIDEO_FLAG_MODIFIER` declares the
|
||||
parsed/provided spa_video_info_* struct contains valid modifier information. For legacy
|
||||
reasons `spa_format_video_*_build` will announce any modifier != 0 even when this flag is
|
||||
unused. `SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED` is exclusive to the parse helpers and
|
||||
declares that the parsed spa_pod contains modifier information which needs to be fixated as
|
||||
described above. The list of available modifiers has to be parsed manually from the spa_pod
|
||||
object.
|
||||
|
||||
- \ref spa_video_info_raw, \ref spa_format_video_raw_parse, \ref spa_format_video_raw_build
|
||||
- \ref spa_video_info_dsp, \ref spa_format_video_dsp_parse, \ref spa_format_video_dsp_build
|
||||
|
||||
# v4l2
|
||||
|
||||
Another use case for streaming via DMA-BUFs are exporting a camera feed from v4l2
|
||||
as DMA-BUFs. Those are located in the main memory where it is possible to mmap them.
|
||||
This should be done as follows: Neither producer nor consumer should announce a
|
||||
modifier, but both should include `1 << SPA_DATA_DmaBuf` in the
|
||||
`SPA_PARAM_BUFFERS_dataType` property. It's the the responsibility of the producer
|
||||
while the `add_buffer` event to choose DMA-BUF as the used buffer type even though
|
||||
no modifier is present, if it can guarantee, that the used buffer is mmapable.
|
||||
|
||||
Note: For now v4l2 uses planar buffers without modifiers. This is the reason for
|
||||
this special case.
|
||||
|
||||
# Explicit sync
|
||||
|
||||
In addition to DMABUF, a set of synchronization primitives (a SyncObjTimeline) and
|
||||
associated metadata can be negotiated on the buffers.
|
||||
|
||||
The explicit sync step is performed *after* the Format has been negotiated.
|
||||
|
||||
## Query support for explicit sync in the driver.
|
||||
|
||||
You might first want to check that the drm render you are using is capable of explicit
|
||||
sync by checking support for DRM_CAP_SYNCOBJ and DRM_CAP_SYNCOBJ_TIMELINE before
|
||||
attempting to negotiate explicit sync.
|
||||
|
||||
## Provide space in the buffer for explicit sync
|
||||
|
||||
Explicit sync requires two extra fds in the buffers and an extra
|
||||
\ref SPA_META_SyncTimeline metadata structure.
|
||||
|
||||
The metadata structure will only be allocated when both sides support explicit
|
||||
sync. We can use this to make a fallback \ref SPA_PARAM_Buffers so that we can
|
||||
support both explicit sync and a fallback to implicit sync.
|
||||
|
||||
So, first announce support for \ref SPA_META_SyncTimeline by adding the
|
||||
\ref SPA_TYPE_OBJECT_ParamMeta object to the stream:
|
||||
|
||||
```
|
||||
params[n_params++] = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
||||
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_SyncTimeline),
|
||||
SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_sync_timeline)));
|
||||
```
|
||||
|
||||
Next make a \ref SPA_PARAM_Buffers that depends on the negotiation of the SyncTimelime metadata:
|
||||
|
||||
```
|
||||
spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers);
|
||||
spa_pod_builder_add(&b,
|
||||
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 2, MAX_BUFFERS),
|
||||
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(3),
|
||||
SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
|
||||
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(data->stride),
|
||||
SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int((1<<SPA_DATA_DmaBuf)),
|
||||
0);
|
||||
spa_pod_builder_prop(&b, SPA_PARAM_BUFFERS_metaType, SPA_POD_PROP_FLAG_MANDATORY);
|
||||
spa_pod_builder_int(&b, 1<<SPA_META_SyncTimeline);
|
||||
params[n_params++] = spa_pod_builder_pop(&b, &f);
|
||||
```
|
||||
|
||||
Note the mandatory \ref SPA_PARAM_BUFFERS_metaType with the \ref SPA_META_SyncTimeline
|
||||
bit set. This forces this buffer layout to be used when SyncTimeline metadata was
|
||||
negotiated. Also note the \ref SPA_PARAM_BUFFERS_blocks that is now set to the number
|
||||
of DMABUF planes + 2. In this case we have 1 plane/fd for the DMABUF and 2 fds for the
|
||||
SyncObjTimelines.
|
||||
|
||||
You can also add a fallback \ref SPA_PARAM_Buffers when the \ref SPA_META_SyncTimeline
|
||||
was not negotiated:
|
||||
|
||||
```
|
||||
params[n_params++] = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
||||
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 2, MAX_BUFFERS),
|
||||
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
|
||||
SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
|
||||
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(data->stride),
|
||||
SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int((1<<SPA_DATA_DmaBuf)));
|
||||
```
|
||||
|
||||
This one has just 1 data block with the DMABUF fd and plane info.
|
||||
|
||||
## Check if SPA_META_SyncTimeline was negotiated
|
||||
|
||||
After sending the \ref SPA_PARAM_Buffers, the buffer will be allocated by the PipeWire
|
||||
server.
|
||||
|
||||
In the pw-stream::add_buffer event, check if the \ref SPA_META_SyncTimeline is available
|
||||
on the buffer:
|
||||
|
||||
```
|
||||
struct spa_meta_sync_timeline *stl;
|
||||
stl = spa_buffer_find_meta_data(buf, SPA_META_SyncTimeline, sizeof(*stl));
|
||||
```
|
||||
|
||||
When the metadata is available, the SyncObj fds are in the last 2 data planes
|
||||
of the buffer, the acquire and release syncobj respectively. You can keep a ref to the
|
||||
\ref struct spa_meta_sync_timeline because we will need this later when processing
|
||||
the buffers.
|
||||
|
||||
If the producer is allocating buffers, when the stream has the \ref PW_STREAM_FLAG_ALLOC_BUFFERS
|
||||
flag, it should allocate the DMABUF and syncobj now and place them in the buffer data.
|
||||
First the plane fds and then the 2 syncobj fds.
|
||||
|
||||
The consumer can directly use the fds. The SyncObj fds can be converted to a handle,
|
||||
for example, to make things easier later:
|
||||
|
||||
```
|
||||
uint32_t acquire_handle, release_handle;
|
||||
drmSyncobjFDToHandle(drm_fd, buf->datas[buf->n_datas - 2].fd, &acquire_handle);
|
||||
drmSyncobjFDToHandle(drm_fd, buf->datas[buf->n_datas - 1].fd, &release_handle);
|
||||
```
|
||||
|
||||
## Use the SPA_META_SyncTimeline when processing buffers
|
||||
|
||||
The \ref struct spa_meta_sync_timeline contains 2 fields: the acquire_point and
|
||||
release_point.
|
||||
|
||||
Producers will start a render operation on the DMABUF of the buffer and place
|
||||
the acquire_point in the \ref struct spa_meta_sync_timeline. When the rendering is
|
||||
complete, the producer should signal the acquire_point on the acquire SyncObjTimeline.
|
||||
|
||||
Producers will also add a release_point on the release SyncObjTimeline. They are
|
||||
only allowed to reuse the buffer when the release_point has been signaled.
|
||||
|
||||
Consumers use the acquire_point to wait for rendering to complete before processing
|
||||
the buffer. This can be offloaded to the hardware when submitting the rendering
|
||||
operation or it can be done explicitly with drmSyncobjTimelineWait() on the acquire
|
||||
SyncObjTimeline handle and the acquire_point of the metadata.
|
||||
|
||||
Consumers should then also signal the release_point on the release SyncObjTimeline when
|
||||
they complete processing the buffer. This can be done in the hardware as part of
|
||||
the render pipeline or explicitly with drmSyncobjTimelineSignal() on the release
|
||||
handle and the release_point of the metadata.
|
||||
|
||||
|
||||
*/
|
26
doc/dox/internals/index.dox
Normal file
26
doc/dox/internals/index.dox
Normal file
|
@ -0,0 +1,26 @@
|
|||
/** \page page_internals Internals
|
||||
|
||||
# Internals
|
||||
|
||||
- \subpage page_design
|
||||
- \subpage page_audio
|
||||
- \subpage page_access
|
||||
- \subpage page_portal
|
||||
- \subpage page_midi
|
||||
- \subpage page_objects_design
|
||||
- \subpage page_library
|
||||
- \subpage page_dma_buf
|
||||
- \subpage page_scheduling
|
||||
- \subpage page_native_protocol
|
||||
|
||||
|
||||
# Components
|
||||
|
||||
- \subpage page_daemon
|
||||
- \subpage page_session_manager
|
||||
|
||||
# Backends
|
||||
|
||||
- \subpage page_pulseaudio
|
||||
|
||||
*/
|
240
doc/dox/internals/library.dox
Normal file
240
doc/dox/internals/library.dox
Normal file
|
@ -0,0 +1,240 @@
|
|||
/** \page page_library PipeWire Library
|
||||
|
||||
There are two main components that make up the PipeWire library:
|
||||
|
||||
1. An implementation of a graph based media processing engine.
|
||||
2. An asynchronous IPC mechanism to manipulate and introspect
|
||||
a graph in another process.
|
||||
|
||||
There is usually a daemon that implements the global graph and
|
||||
clients that operate on this graph.
|
||||
|
||||
The IPC mechanism in PipeWire is inspired by Wayland in that it
|
||||
follows the same design principles of objects and methods/events
|
||||
along with how this API is presented to the user.
|
||||
|
||||
PipeWire has a plugin architecture that allows new features to
|
||||
be added (or removed) by the user. Plugins can hook into many
|
||||
aspects of PipeWire and change the behaviour or number of
|
||||
features dynamically.
|
||||
|
||||
|
||||
# Principles
|
||||
|
||||
The PipeWire API is an object oriented asynchronous protocol.
|
||||
All requests and replies are method invocations on some object.
|
||||
|
||||
Objects are identified with a unique ID. Each object implements an
|
||||
interface and requests result in invocations of methods on the
|
||||
interface.
|
||||
|
||||
The protocol is message based. A message sent by a client to the
|
||||
server is called a method. A message from the server to the client
|
||||
is called an event. Unlike Wayland, these messages are not (yet)
|
||||
described in an external protocol file but implemented directly in
|
||||
a protocol plugin. Protocol plugins can be added to add new
|
||||
objects or even protocols when required.
|
||||
|
||||
Messages are encoded with \ref page_spa_pod, which make it
|
||||
possible to encode complex objects with right types.
|
||||
|
||||
Events from the server can be a reply to a method or can be emitted
|
||||
when the server state changes.
|
||||
|
||||
Upon connecting to a server, it will broadcast its state. Clients
|
||||
should listen for these state changes and cache them. There is no
|
||||
need (or mechanism) to query the state of the server.
|
||||
|
||||
The server also has a registry object that, when listening to,
|
||||
will broadcast the presence of global objects and any changes in
|
||||
their state.
|
||||
|
||||
State about objects can be obtained by binding to them and listening
|
||||
for state changes.
|
||||
|
||||
|
||||
# Versioning
|
||||
|
||||
All interfaces have a version number. The maximum supported version
|
||||
number of an interface is advertised in the registry global event.
|
||||
|
||||
A client asks for a specific version of an interface when it binds
|
||||
to them. It is the task of the server to adapt to the version of the
|
||||
client.
|
||||
|
||||
Interfaces increase their version number when new methods or events
|
||||
are added. Methods or events should never be removed or changed for
|
||||
simplicity.
|
||||
|
||||
|
||||
# Proxies and Resources
|
||||
|
||||
When a client connects to a PipeWire daemon, a new `struct pw_proxy`
|
||||
object is created with ID 0. The `struct pw_core` interface is
|
||||
assigned to the proxy.
|
||||
|
||||
On the server side there is an equivalent `struct pw_resource` with
|
||||
ID 0. Whenever the client sends a message on the proxy (by calling
|
||||
a method on the interface of the proxy) it will transparently result
|
||||
in a callback on the resource with the same ID.
|
||||
|
||||
Likewise if the server sends a message (an event) on a resource, it
|
||||
will result in an event on the client proxy with the same ID.
|
||||
|
||||
PipeWire will notify a client when a resource ID (and thus also proxy
|
||||
ID) becomes unused. The client is responsible for destroying the
|
||||
proxy when it no longer wants to use it.
|
||||
|
||||
|
||||
# Interfaces
|
||||
|
||||
## struct pw_loop
|
||||
|
||||
An abstraction for a `poll(2)` loop. It is usually part of one of:
|
||||
|
||||
- `struct pw_main_loop`: A helper that can run and stop a `pw_loop`.
|
||||
- `struct pw_thread_loop`: A helper that can run and stop a `pw_loop`
|
||||
in a different thread. It also has some helper
|
||||
functions for various thread related synchronization
|
||||
issues.
|
||||
- `struct pw_data_loop`: A helper that can run and stop a `pw_loop`
|
||||
in a real-time thread along with some useful helper
|
||||
functions.
|
||||
|
||||
## struct pw_context
|
||||
|
||||
The main context for PipeWire resources. It keeps track of the mainloop,
|
||||
loaded modules, the processing graph and proxies to remote PipeWire
|
||||
instances.
|
||||
|
||||
An application has to select an implementation of a `struct pw_loop`
|
||||
when creating a context.
|
||||
|
||||
The context has methods to create the various objects you can use to
|
||||
build a server or client application.
|
||||
|
||||
## struct pw_core
|
||||
|
||||
A proxy to a remote PipeWire instance. This is used to send messages
|
||||
to a remote PipeWire daemon and to receive events from it.
|
||||
|
||||
A core proxy can be used to receive errors from the remote daemon
|
||||
or to perform a roundtrip message to flush out pending requests.
|
||||
|
||||
Other core methods and events are used internally for the object
|
||||
life cycle management.
|
||||
|
||||
## struct pw_registry
|
||||
|
||||
A proxy to a PipeWire registry object. It emits events about the
|
||||
available objects on the server and can be used to bind to those
|
||||
objects in order to call methods or receive events from them.
|
||||
|
||||
## struct pw_module
|
||||
|
||||
A proxy to a loadable module. Modules implement functionality such
|
||||
as provide new objects or policy.
|
||||
|
||||
## struct pw_factory
|
||||
|
||||
A proxy to an object that can create other objects.
|
||||
|
||||
## struct pw_device
|
||||
|
||||
A proxy to a device object. Device objects model a physical hardware
|
||||
or software device in the system and can create other objects
|
||||
such as nodes or other devices.
|
||||
|
||||
## struct pw_node
|
||||
|
||||
A Proxy to a processing node in the graph. Nodes can have input and
|
||||
output ports and the ports can be linked together to form a graph.
|
||||
|
||||
## struct pw_port
|
||||
|
||||
A Proxy to an input or output port of a node. They can be linked
|
||||
together to form a processing graph.
|
||||
|
||||
## struct pw_link
|
||||
|
||||
A proxy to a link between in output and input port. A link negotiates
|
||||
a format and buffers between ports. A port can be linked to many other
|
||||
ports and PipeWire will manage mixing and duplicating the buffers.
|
||||
|
||||
|
||||
# High Level Helper Objects
|
||||
|
||||
Some high level objects are implemented to make it easier to interface
|
||||
with a PipeWire graph.
|
||||
|
||||
## struct pw_filter
|
||||
|
||||
A `struct pw_filter` allows you implement a processing filter that can
|
||||
be added to a PipeWire graph. It is comparable to a JACK client.
|
||||
|
||||
## struct pw_stream
|
||||
|
||||
A `struct pw_stream` makes it easy to implement a playback or capture
|
||||
client for the graph. It takes care of format conversion and buffer
|
||||
sizes. It is comparable to Core Audio AudioQueue or a PulseAudio
|
||||
stream.
|
||||
|
||||
|
||||
# Security
|
||||
|
||||
With the default native protocol, clients connect to PipeWire using
|
||||
a named socket. This results in a client socket that is used to
|
||||
send messages.
|
||||
|
||||
For sandboxed clients, it is possible to get the client socket via
|
||||
other ways, like using the portal. In that case, a portal will
|
||||
do the connection for the client and then hands the connection socket
|
||||
to the client.
|
||||
|
||||
All objects in PipeWire have per client permission bits, currently
|
||||
READ, WRITE, EXECUTE and METADATA. A client can not see an object
|
||||
unless it has READ permissions. Similarly, a client can only execute
|
||||
methods on an object when the EXECUTE bit is set and to modify the
|
||||
state of an object, the client needs WRITE permissions.
|
||||
|
||||
A client (the portal after it makes a connection) can drop permissions
|
||||
on an object. Once dropped, it can never reacquire the permission.
|
||||
|
||||
Clients with WRITE/EXECUTE permissions on another client can
|
||||
add and remove permissions for the client at will.
|
||||
|
||||
Clients with MODIFY permissions on another object can set or remove
|
||||
metadata on that object.
|
||||
|
||||
Clients that need permissions assigned to them can be started in
|
||||
blocked mode and resume when permissions are assigned to them by
|
||||
a session manager or portal, for example.
|
||||
|
||||
PipeWire uses memfd (`memfd_create(2)`) or DMA-BUF for sharing media
|
||||
and data between clients. Clients can thus not look at other clients
|
||||
data unless they can see the objects and connect to them.
|
||||
|
||||
|
||||
# Implementation
|
||||
|
||||
PipeWire also exposes an API to implement the server side objects in
|
||||
a graph.
|
||||
|
||||
|
||||
# Error Reporting
|
||||
|
||||
Functions return either NULL with errno set or a negative int error
|
||||
code when an error occurs. Error codes are used from the SPA plugin
|
||||
library on which PipeWire is built.
|
||||
|
||||
Some functions might return asynchronously. The error code for such
|
||||
functions is positive and SPA_RESULT_IS_ASYNC() will return true.
|
||||
SPA_RESULT_ASYNC_SEQ() can be used to get the unique sequence number
|
||||
associated with the async operation.
|
||||
|
||||
The object returning the async result code will have some way to
|
||||
signal the completion of the async operation (with, for example, a
|
||||
callback). The sequence number can be used to see which operation
|
||||
completed.
|
||||
|
||||
*/
|
115
doc/dox/internals/midi.dox
Normal file
115
doc/dox/internals/midi.dox
Normal file
|
@ -0,0 +1,115 @@
|
|||
/** \page page_midi MIDI Support
|
||||
|
||||
This document explains how MIDI is implemented.
|
||||
|
||||
|
||||
# Use Cases
|
||||
|
||||
## MIDI Devices Are Made Available As Processing Nodes/Ports
|
||||
|
||||
Applications need to be able to see a port for each stream of a
|
||||
MIDI device.
|
||||
|
||||
## MIDI Devices Can Be Plugged and Unplugged
|
||||
|
||||
When devices are plugged and unplugged the associated nodes/ports
|
||||
need to be created and removed.
|
||||
|
||||
## Applications Can Connect To MIDI Devices
|
||||
|
||||
Applications can create ports that can connect to the MIDI ports
|
||||
so that data can be provided to or consumed from them.
|
||||
|
||||
## Some MIDI Devices Are Sinks Or Sources For MIDI Data
|
||||
|
||||
It should be possible to create a MIDI sink or source that routes the
|
||||
MIDI events to specific MIDI ports.
|
||||
|
||||
One example of such a sink would be in front of a software MIDI
|
||||
renderer.
|
||||
|
||||
An example of a MIDI source would be after a virtual keyboard or
|
||||
as a mix from many MIDI input devices.
|
||||
|
||||
## Applications Should Auto-connect To MIDI Sinks Or Sources
|
||||
|
||||
An application should be able to be connected to a MIDI sink when
|
||||
it wants to play MIDI data.
|
||||
|
||||
An application should be able to connect to a MIDI source when it
|
||||
wants to capture MIDI data.
|
||||
|
||||
|
||||
# Design
|
||||
|
||||
## SPA
|
||||
|
||||
MIDI devices/streams are implemented with an \ref spa_node with generic
|
||||
control input and output Ports. These ports have a media type of
|
||||
`"application/control"` and the data transported over these ports
|
||||
are of type \ref spa_pod_sequence with the \ref spa_pod_control type set to
|
||||
\ref SPA_CONTROL_Midi.
|
||||
|
||||
This means that every MIDI event is timestamped with the sample
|
||||
offset against the current graph clock cycle to get sample accurate
|
||||
midi events that can be aligned with the corresponding sample data.
|
||||
|
||||
Since the MIDI events are embedded in the generic control stream,
|
||||
they can be interleaved with other control message types, such as
|
||||
property updates or OSC messages.
|
||||
|
||||
As of 1.4, SPA_CONTROL_UMP (Universal Midi Packet) is the prefered format
|
||||
for the MIDI 1.0 and 2.0 messages in the \ref spa_pod_sequence. Conversion
|
||||
to SPA_CONTROL_Midi is performed for legacy applications.
|
||||
|
||||
## The PipeWire Daemon
|
||||
|
||||
Nothing special is implemented for MIDI. Negotiation of formats
|
||||
happens between `"application/control"` media types and buffers are
|
||||
negotiated in the same way as any generic format.
|
||||
|
||||
## The Session Manager
|
||||
|
||||
The session manager needs to create the MIDI nodes/ports for the available
|
||||
devices.
|
||||
|
||||
This can either be done as a single node with ports per device/stream
|
||||
or as separate nodes created by a MIDI device monitor.
|
||||
|
||||
The session manager needs to be aware of the various MIDI sinks and sources
|
||||
in order to route MIDI streams to them from applications that want this.
|
||||
|
||||
|
||||
# Implementation
|
||||
|
||||
## Session manager (Wireplumber)
|
||||
|
||||
The session manager uses the \ref SPA_NAME_API_ALSA_SEQ_BRIDGE plugin for
|
||||
the MIDI features. This creates a single SPA Node with ports per
|
||||
MIDI client/stream.
|
||||
|
||||
The media session will check the permissions on `/dev/snd/seq` before
|
||||
attempting to create this node. It will also use inotify to wait
|
||||
until the sequencer device node is accessible.
|
||||
|
||||
## JACK
|
||||
|
||||
JACK assumes all `"application/control"` ports are MIDI ports.
|
||||
|
||||
The control messages are converted to the JACK event format by
|
||||
filtering out the \ref SPA_CONTROL_Midi, \ref SPA_CONTROL_OSC and
|
||||
\ref SPA_CONTROL_UMP types. On output ports, the JACK event stream is
|
||||
converted to control messages in a similar way.
|
||||
|
||||
Normally, all MIDI and UMP messages are converted to MIDI1 jack events unless
|
||||
the JACK port was created with an explcit "32 bits raw UMP" format, in which
|
||||
case the raw UMP is passed to the JACK application directly. For output ports,
|
||||
the JACK events are assumed to be MIDI1 and converted to UMP unless the port
|
||||
has the "32 bit raw UMP" format, in which case the UMP messages are simply
|
||||
passed on.
|
||||
|
||||
There is a 1 to 1 mapping between the JACK events and control
|
||||
messages so there is no information loss or need for complicated
|
||||
conversions.
|
||||
|
||||
*/
|
347
doc/dox/internals/objects.dox
Normal file
347
doc/dox/internals/objects.dox
Normal file
|
@ -0,0 +1,347 @@
|
|||
/** \page page_objects_design Objects Design
|
||||
|
||||
This document is a design reference on the various objects that exist
|
||||
in the PipeWire media and session management graphs. Explaining what these
|
||||
objects are, how they are meant to be used, and how they relate to other
|
||||
kinds of objects and concepts that exist in subsystems or other libraries.
|
||||
|
||||
|
||||
# The Media Graph
|
||||
|
||||
The media graph represents and enables the media flow inside the PipeWire
|
||||
daemon and between the daemon and its clients. It consists of nodes, ports
|
||||
and links.
|
||||
|
||||
```
|
||||
+------------+ +------------+
|
||||
| | | |
|
||||
| +--------+ Link +--------+ |
|
||||
| Node | Port |--------| Port | Node |
|
||||
| +--------+ +--------+ |
|
||||
| | | |
|
||||
+------------+ +------------+
|
||||
```
|
||||
|
||||
## Node
|
||||
|
||||
A **node** is a media processing element. It consumes and/or produces buffers
|
||||
that contain data, such as audio or video.
|
||||
|
||||
A node may operate entirely inside the PipeWire daemon or it may be operating
|
||||
in a client process. In the second case, media is transferred to/from that
|
||||
client using the PipeWire protocol.
|
||||
|
||||
In an analogy to GStreamer, a _node_ is similar (but not equal) to a
|
||||
GStreamer _element_.
|
||||
|
||||
## Port
|
||||
|
||||
A **port** is attached on a **node** and provides an interface for input
|
||||
or output of media on the node. A node may have multiple ports.
|
||||
|
||||
A port always has a direction, input or output:
|
||||
|
||||
- Input: it allows media input into the node (in other terms, it is a _sink_)
|
||||
- Output: it outputs media out of the node (in other terms, it is a _source_)
|
||||
|
||||
In an analogy to GStreamer, a _port_ is similar (but not equal) to a
|
||||
GStreamer _pad_.
|
||||
|
||||
## Link
|
||||
|
||||
A **link** connects two ports of opposite direction, making media flow from
|
||||
the output port to the input port.
|
||||
|
||||
|
||||
# The Session Management Graph
|
||||
|
||||
The session management graph is a virtual, higher level representation of the
|
||||
media flow. It is created entirely by the session manager and it can affect
|
||||
the routing on the media graph only through the session manager's actions.
|
||||
|
||||
The session management graph is useful to abstract the complexity of the
|
||||
actual media flow both for the target user and for the policy management
|
||||
codebase.
|
||||
|
||||
```
|
||||
+---------------------+ +----------------------+
|
||||
| | | |
|
||||
| +----------------+ Endpoint Link +----------------+ |
|
||||
| Endpoint |Endpoint Stream |-----------------|Endpoint Stream | Endpoint |
|
||||
| +----------------+ +----------------+ |
|
||||
| | | |
|
||||
+---------------------+ +----------------------+
|
||||
```
|
||||
|
||||
## Endpoint
|
||||
|
||||
An **endpoint** is a session management object that provides a representation
|
||||
of user conceivable places where media can be routed to/from.
|
||||
|
||||
Examples of endpoints associated with hardware on a desktop-like system:
|
||||
|
||||
- Laptop speakers.
|
||||
- USB webcam.
|
||||
- Bluetooth headset microphone.
|
||||
- Line out stereo jack port.
|
||||
|
||||
Examples of endpoints associated with hardware in a car:
|
||||
|
||||
- Speakers amplifier.
|
||||
- Front right seat microphone array.
|
||||
- Rear left seat headphones.
|
||||
- Bluetooth phone voice gateway.
|
||||
- Hardware FM radio device.
|
||||
|
||||
Examples of endpoints associated with software:
|
||||
|
||||
- Desktop screen capture source.
|
||||
- Media player application.
|
||||
- Camera application.
|
||||
|
||||
In most cases an endpoint maps to a node on the media graph, but this is not
|
||||
always the case. An endpoint may be backed by several nodes or no nodes at all.
|
||||
Different endpoints may also be sharing nodes in some cases.
|
||||
|
||||
An endpoint that does not map to any node may be useful to represent hardware
|
||||
that the session manager needs to be able to control, but there is no way
|
||||
to route media to/from that hardware through the PipeWire media graph. For
|
||||
example, in a car we may have a CD player device that is directly wired to the
|
||||
speakers amplifier and therefore audio flows between them without passing
|
||||
through the controlling CPU. However, it is useful for the session manager to
|
||||
be able to represent the *CD player endpoint* and the _endpoint link_ between
|
||||
it and the amplifier, so that it can apply audio policy that takes into account
|
||||
whether the CD player is playing or not.
|
||||
|
||||
### Target
|
||||
|
||||
An **endpoint** may be grouping together targets that can be reached by
|
||||
following the same route and they are mutually exclusive with each other.
|
||||
|
||||
For example, the speakers and the headphones jack on a laptop are usually
|
||||
mutually exclusive by hardware design (hardware mutes the speakers when the
|
||||
headphones are enabled) and they share the same ALSA PCM device, so audio still
|
||||
follows the same route to reach both.
|
||||
|
||||
In this case, a session manager may choose to group these two targets into the
|
||||
same endpoint, using a parameter on the _endpoint_ object to allow the user
|
||||
to choose the target (if the hardware allows configuring this at all).
|
||||
|
||||
## Endpoint Stream
|
||||
|
||||
An **endpoint stream** is attached to an **endpoint** and represents a logical
|
||||
path that can be taken to reach this endpoint, often associated with
|
||||
a _use case_.
|
||||
|
||||
For example, the "Speakers amplifier" endpoint in a car might have the
|
||||
following streams:
|
||||
|
||||
- _Music_: A path to play music;
|
||||
the implementation will output this to all speakers, using the volume
|
||||
that has been configured for the "Music" use case.
|
||||
- _Voice_: A path to play a voice message; such as a navigation message or
|
||||
feedback from a voice assistant, the implementation will output this
|
||||
to the front speakers only. Lowering the volume of the music (if any)
|
||||
on these speakers at the same time.
|
||||
- _Emergency_: A path to play an emergency situation sound (a beep,
|
||||
or equivalent); the implementation will output this on all speakers.
|
||||
Increasing the volume to a factory defined value if necessary (to ensure
|
||||
that it is audible) while muting audio from all other streams at the
|
||||
same time.
|
||||
|
||||
In another example, a microphone that can be used for activating a voice
|
||||
assistant might have the following streams:
|
||||
|
||||
- _Capture_: A path to capture directly from the microphone; this can be used
|
||||
by an application that listens for the assistant's wake-word in order
|
||||
to activate the full voice recognition engine.
|
||||
- _CaptureDelayed_: A path to capture with a constant delay (meaning that
|
||||
starting capturing now will actually capture something that was spoken
|
||||
a little earlier); this can be used by the full voice recognition engine,
|
||||
allowing it to start after the wake-word has been spoken while capturing
|
||||
audio that also includes the wake-word.
|
||||
|
||||
Endpoint streams may be mutually exclusive or they may used simultaneously,
|
||||
depending on the implementation.
|
||||
|
||||
Endpoint streams may be implemented in many ways:
|
||||
|
||||
- By plugging additional nodes in the media graph that link to the device node
|
||||
(ex. a simple buffering node linked to an alsa source node could implement
|
||||
the _CaptureDelayed_ stream in the above microphone example).
|
||||
- By using a different device node (ex. different ALSA device on the same card)
|
||||
that has a special meaning for the hardware.
|
||||
- By triggering switches on the hardware (ex. modify ALSA controls on the
|
||||
same device).
|
||||
|
||||
## Endpoint Link
|
||||
|
||||
An **endpoint link** connects two streams from two different endpoints, creating
|
||||
a logical representation of media flow between the endpoints.
|
||||
|
||||
An **endpoint link** may be implemented by creating one or more _links_ in the
|
||||
underlying media graph, or it may be implemented by configuring hardware
|
||||
resources to enable media flow, in case the flow does not pass through the
|
||||
media graph.
|
||||
|
||||
### Constructing
|
||||
|
||||
Constructing an **endpoint link** is done by asking the _endpoint stream_
|
||||
objects to prepare it. First, the source stream is asked to provide linking
|
||||
information. When the information is retrieved, the sink stream is asked to
|
||||
use this information to prepare and to provide its own linking information.
|
||||
When this is done, the session manager is asked to create the link using the
|
||||
provided information.
|
||||
|
||||
This mechanism allows stream implementations:
|
||||
|
||||
- To prepare for linking, adjusting hardware paths if necessary.
|
||||
- To check for stream linking compatibility; not all streams can be connected
|
||||
to all others (ex. streams with media flow in the hardware cannot be linked
|
||||
to streams that are backed by nodes in the media graph).
|
||||
- To provide implementation specific information for linking; in the standard
|
||||
case this is going to be a list of _ports_ to be linked in the media graph,
|
||||
but in a hardware-flow case it can be any kind of hardware-specific detail.
|
||||
|
||||
|
||||
# Other Related Objects
|
||||
|
||||
## Device
|
||||
|
||||
A **device** represents a handle to an underlying API that is used to create
|
||||
higher level objects, such as nodes, or other devices.
|
||||
|
||||
Well-known devices include:
|
||||
|
||||
| Device API | Description |
|
||||
| :--- | :--- |
|
||||
| alsa.pcm.device | A handle to an ALSA card (ex. `hw:0`, `hw:1`, etc). |
|
||||
| alsa.seq.device | A handle to an ALSA Midi device. |
|
||||
| v4l2.device | A handle to a V4L2 device (`/dev/video0`, `/dev/video1`, etc..). |
|
||||
| jack.device | A JACK client, allowing PipeWire to slave to JACK for audio input/output. |
|
||||
|
||||
A device may have a _profile_, which allows the user to choose between
|
||||
multiple configurations that the device may be capable of having, or to simply
|
||||
turn the device _off_, which means that the handle is closed and not used
|
||||
by PipeWire.
|
||||
|
||||
## Session
|
||||
|
||||
The **session** represents the session manager and can be used to expose
|
||||
global properties or methods that affect the session management.
|
||||
|
||||
### Default Endpoints
|
||||
|
||||
The session is responsible for book-keeping the default device endpoints (one
|
||||
for each kind of device) that is to be used to link new clients when
|
||||
simulating a PulseAudio-like behavior, where the user can choose from the UI
|
||||
device preferences.
|
||||
|
||||
For example, a system may have both "Speakers" and "HDMI" endpoints on the
|
||||
"Audio Output" category and the user may be offered to make a choice within
|
||||
the UI to select which endpoint they want to use by default for audio output.
|
||||
This preference is meant to be stored in the session object.
|
||||
|
||||
### Multiple Sessions
|
||||
|
||||
It is not currently defined whether it is allowed to have multiple sessions
|
||||
or not and how the system should behave if this happens.
|
||||
|
||||
|
||||
# Mappings To Underlying Subsystem Objects
|
||||
|
||||
## ALSA UCM
|
||||
|
||||
This is a ***proposal***
|
||||
|
||||
| ALSA / UCM | PipeWire |
|
||||
| :--- | :--- |
|
||||
| ALSA card | device |
|
||||
| UCM verb | device profile |
|
||||
| UCM device | endpoint (+ target, grouping conflicting devices into the same endpoint) |
|
||||
| UCM modifier | endpoint stream |
|
||||
| PCM stream | node |
|
||||
|
||||
In UCM mode, an ALSA card is represented as a PipeWire device, with the
|
||||
available UCM verbs listed as profiles of the device.
|
||||
|
||||
Activating a profile (ie. a verb) will create the necessary nodes for the
|
||||
available PCM streams and at the same time it will also create one endpoint
|
||||
for each UCM device. Optionally conflicting UCM devices can be grouped in
|
||||
the same endpoint, listing the conflicting options as targets of the endpoint.
|
||||
|
||||
The available UCM modifiers for each UCM device will be added as streams, plus
|
||||
one "default" stream for accessing the device with no modifiers.
|
||||
|
||||
## ALSA Fallback
|
||||
|
||||
| ALSA | PipeWire |
|
||||
| :--- | :--- |
|
||||
| card | device |
|
||||
| PCM stream | node + endpoint |
|
||||
|
||||
In the case where UCM (or another similar mechanism) is not available,
|
||||
ALSA cards are represented as PipeWire devices with only two profiles on/off.
|
||||
|
||||
When the on profile is activated, a node and an associated endpoint are created
|
||||
for every available PCM stream.
|
||||
|
||||
Endpoints in this case have only one "default" stream, unless they are extended
|
||||
by the session manager to have software-backed streams.
|
||||
|
||||
## V4L2
|
||||
|
||||
***FIXME***
|
||||
|
||||
| V4L2 | PipeWire |
|
||||
| :--- | :--- |
|
||||
| device | device + node |
|
||||
|
||||
|
||||
# Relationship To Other API's
|
||||
|
||||
## PulseAudio
|
||||
|
||||
### Mapping PipeWire Objects For Access By PulseAudio Clients
|
||||
|
||||
| PipeWire | PulseAudio |
|
||||
| :--- | :--- |
|
||||
| device | card |
|
||||
| device profile | card profile |
|
||||
| endpoint (associated with a device) | sink / source |
|
||||
| endpoint (associated with a client) | sink-input / source-output |
|
||||
| endpoint target | port |
|
||||
| endpoint stream | N/A, PA clients will be limited to the default stream |
|
||||
|
||||
### Mapping PulseAudio Clients To PipeWire
|
||||
|
||||
| PulseAudio | PipeWire |
|
||||
| :--- | :--- |
|
||||
| stream | client + node + endpoint (no targets, 1 default stream) |
|
||||
|
||||
## Jack
|
||||
|
||||
Note: This section is about JACK clients connecting to PipeWire through the
|
||||
JACK compatibility library. The scenario where PipeWire connects to another
|
||||
JACK server as a client is out of scope here.
|
||||
|
||||
### Mapping PipeWire Objects For Access By JACK Clients
|
||||
|
||||
| PipeWire | JACK |
|
||||
| :--- | :--- |
|
||||
| node | client |
|
||||
| port | port |
|
||||
| device | N/A |
|
||||
| endpoint | N/A |
|
||||
|
||||
### Mapping JACK Clients To PipeWire
|
||||
|
||||
| JACK | PipeWire |
|
||||
| :--- | :--- |
|
||||
| client | client + node |
|
||||
| port | port |
|
||||
|
||||
JACK clients do not create endpoints. A session manager should be JACK aware
|
||||
in order to anticipate direct node linking.
|
||||
|
||||
*/
|
215
doc/dox/internals/portal.dox
Normal file
215
doc/dox/internals/portal.dox
Normal file
|
@ -0,0 +1,215 @@
|
|||
/** \page page_portal Portal Access Control
|
||||
|
||||
This document explains how clients from the portal are handled.
|
||||
|
||||
The portal is a DBus service that exposes interfaces to
|
||||
request access to the PipeWire daemon to perform a certain set of
|
||||
functions. The PipeWire daemon runs outside the sandbox, the portal is a way
|
||||
for clients inside the sandbox to connect to and use PipeWire.
|
||||
|
||||
The PipeWire socket is not exposed in the sandbox. Instead, The portal
|
||||
connects to PipeWire on behalf of the client, informing PipeWire that this
|
||||
client is a portal-managed client. PipeWire can detect and enforce
|
||||
extra permission checks on the portal managed clients.
|
||||
|
||||
Once such portal is the [camera
|
||||
portal](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Camera.html)
|
||||
that provides a PipeWire session to stream video from a camera.
|
||||
|
||||
|
||||
# Use Cases
|
||||
|
||||
## New Portal Managed Clients Need Device Permissions Configured
|
||||
|
||||
When a new client is detected, the available objects need to be
|
||||
scanned and permissions configured for each of them.
|
||||
|
||||
Only the devices belonging to the media_roles given by the
|
||||
portal are considered.
|
||||
|
||||
## New Devices Need To Be Made Visible To Portal Managed Clients
|
||||
|
||||
Newly created objects are made visible to a client when the client
|
||||
is allowed to interact with it.
|
||||
|
||||
Only the devices belonging to the media_roles given by the
|
||||
portal are considered.
|
||||
|
||||
## Permissions For A Device Need To Be Revoked
|
||||
|
||||
The session manager listens to changes in the permissions of devices
|
||||
and will remove the client permissions accordingly.
|
||||
|
||||
Usually this is implemented by listening to the permission store
|
||||
DBus object. The desktop environment might provide a configuration panel
|
||||
where these permissions can be managed.
|
||||
|
||||
|
||||
# Design
|
||||
|
||||
## The Portal
|
||||
|
||||
A sandboxed client cannot connect to PipeWire directly. Instead, it connects
|
||||
to the sandbox side of the portal which then connects the PipeWire daemon to
|
||||
configure the session. The portal then hands the file descriptor of the
|
||||
PipeWire connection to the client and the client can use this file descriptor
|
||||
to interface with the PipeWire session directly.
|
||||
|
||||
When the portal connects, it will set the following properties on the
|
||||
client object:
|
||||
|
||||
- `"pipewire.access.portal.is_portal" = true` for the connection of the
|
||||
portal itself (as opposed to a client managed by the portal).
|
||||
- `"pipewire.access.portal.app_id"` the [application ID](https://docs.flatpak.org/en/latest/conventions.html#application-ids) of the client.
|
||||
- `"pipewire.access.portal.media_roles"` media roles of the client.
|
||||
Currently only `"Camera"` is defined.
|
||||
|
||||
Before returning the connection to a client, the portal configures
|
||||
minimal permissions on the client. No objects are initially visible. It is
|
||||
the task of the \ref page_session_manager to make the objects in the graph
|
||||
visible, depending on the client's `media_roles` (see also \ref
|
||||
PW_KEY_MEDIA_ROLE).
|
||||
|
||||
## The PipeWire Portal Module
|
||||
|
||||
The PipeWire daemon uses the \ref page_module_portal to find the PID of the
|
||||
processes that owns the DBus name `org.freedesktop.portal.Desktop`
|
||||
(see the [XDG Desktop Portal](https://github.com/flatpak/xdg-desktop-portal)).
|
||||
|
||||
Client connections from this PID are tagged as \ref PW_KEY_ACCESS
|
||||
`"portal"` (see \ref page_module_access). It will also set ALL permissions for
|
||||
this client so that it can resume.
|
||||
|
||||
\dot
|
||||
digraph pw {
|
||||
compound=true;
|
||||
node [shape="box"];
|
||||
rankdir="TB";
|
||||
|
||||
dbus [label="org.freedesktop.portal.Desktop"];
|
||||
|
||||
portal_access [label="PipeWire (mod: Portal Access)"];
|
||||
portal [label="xdg-desktop-portal"];
|
||||
|
||||
dbus -> portal_access [arrowhead="dot"];
|
||||
dbus -> portal [arrowhead="dot"];
|
||||
|
||||
portal_access -> portal [label="pipewire.access = portal"];
|
||||
|
||||
{ rank="same"; portal_access; portal}
|
||||
}
|
||||
\enddot
|
||||
|
||||
## The Client
|
||||
|
||||
A client can ask the portal for a connection to the PipeWire daemon.
|
||||
|
||||
\dot
|
||||
digraph pw {
|
||||
compound=true;
|
||||
node [shape="box"];
|
||||
rankdir="LR";
|
||||
|
||||
pw [label="PipeWire"];
|
||||
portal [label="xdg-desktop-portal"];
|
||||
client [label="client"];
|
||||
|
||||
client -> portal;
|
||||
portal -> pw [label="portal.is_portal=true", arrowhead="none"]
|
||||
|
||||
{rank="min"; pw};
|
||||
{rank="max"; client};
|
||||
}
|
||||
\enddot
|
||||
|
||||
The portal maintains an (unrestricted) connection to the PipeWire daemon with
|
||||
`"pipewire.access.portal.is_portal" = true` to identify the nodes the client
|
||||
needs access to. It then creates a new restricted connection for the client,
|
||||
tagged with additional information.
|
||||
|
||||
\dot
|
||||
digraph pw {
|
||||
compound=true;
|
||||
node [shape="box"];
|
||||
rankdir="LR";
|
||||
|
||||
pw [label="PipeWire"];
|
||||
portal [label="xdg-desktop-portal"];
|
||||
client [label="client"];
|
||||
|
||||
client -> portal [arrowhead="none"];
|
||||
portal -> pw [label="portal.is_portal=true", arrowhead="none"]
|
||||
portal -> pw [label="portal.app_id = $appid"]
|
||||
|
||||
{rank="min"; pw};
|
||||
{rank="max"; client};
|
||||
}
|
||||
\enddot
|
||||
|
||||
The file descriptor for this restricted connection is passed back to the
|
||||
client which can now make use of the resources it has been permitted to
|
||||
access.
|
||||
|
||||
\dot
|
||||
digraph pw {
|
||||
compound=true;
|
||||
node [shape="box"];
|
||||
rankdir="LR";
|
||||
|
||||
pw [label="PipeWire"];
|
||||
portal [label="xdg-desktop-portal"];
|
||||
client [label="client"];
|
||||
|
||||
portal -> pw [label="portal.is_portal=true", arrowhead="none"]
|
||||
|
||||
pw->client [label="restricted connection"];
|
||||
|
||||
{rank="min"; pw};
|
||||
{rank="max"; client};
|
||||
}
|
||||
\enddot
|
||||
|
||||
## The Session Manager
|
||||
|
||||
The session manager listens for new clients to appear. It will use the
|
||||
\ref PW_KEY_ACCESS property to find portal connections. For client connections
|
||||
from the portal the session manager checks the requested `media_roles` and
|
||||
enables or disables access to the respective PipeWire objects.
|
||||
It might have to consult a database to decide what is allowed, for example the
|
||||
[org.freedesktop.impl.portal.PermissionStore](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.impl.portal.PermissionStore.html).
|
||||
|
||||
\dot
|
||||
strict digraph pw {
|
||||
compound=true;
|
||||
node [shape="box"];
|
||||
rankdir="LR";
|
||||
|
||||
portal [label="xdg-desktop-portal"];
|
||||
client [label="client"];
|
||||
|
||||
|
||||
subgraph {
|
||||
rankdir="TB";
|
||||
permissions [label="PermissionStore"];
|
||||
|
||||
sm->permissions;
|
||||
|
||||
sm [label="Session Manager"];
|
||||
pw [label="PipeWire"];
|
||||
sm -> pw [headlabel="allow $media.roles"];
|
||||
pw -> sm;
|
||||
portal -> pw [label="portal.app_id = $appid"];
|
||||
}
|
||||
|
||||
client -> portal [arrowhead="none"];
|
||||
|
||||
{rank="min"; sm, pw};
|
||||
{rank="max"; client};
|
||||
}
|
||||
\enddot
|
||||
|
||||
In the case of the [XDG Desktop
|
||||
Portal](https://github.com/flatpak/xdg-desktop-portal), the portal itself
|
||||
queries the PermissionStore directly.
|
||||
|
||||
*/
|
1691
doc/dox/internals/protocol.dox
Normal file
1691
doc/dox/internals/protocol.dox
Normal file
File diff suppressed because it is too large
Load diff
69
doc/dox/internals/pulseaudio.dox
Normal file
69
doc/dox/internals/pulseaudio.dox
Normal file
|
@ -0,0 +1,69 @@
|
|||
/** \page page_pulseaudio PulseAudio Compatibility
|
||||
|
||||
# Internals - Mapping Between ALSA and Streams
|
||||
|
||||
This explains the mapping between alsa cards and streams and session manager
|
||||
objects.
|
||||
|
||||
## ALSA Cards
|
||||
|
||||
An ALSA card is exposed as a PipeWire device.
|
||||
|
||||
## Streams
|
||||
|
||||
Each ALSA PCM is opened and a node is created for each PCM stream.
|
||||
|
||||
|
||||
# Session Manager
|
||||
|
||||
## ALSA UCM
|
||||
|
||||
The mapping of the PipeWire object hierarchy to the ALSA object hierarchy is the following:
|
||||
|
||||
One PipeWire device is created for every ALSA card.
|
||||
|
||||
- For each UCM verb, a node is created for the associated PCM devices.
|
||||
- For each UCM verb, an endpoint is created.
|
||||
|
||||
In a first step: For each available combination of UCM device and modifier,
|
||||
a stream is created. Streams are marked with compatible other streams.
|
||||
|
||||
Streams with the same modifier and mutually exclusive devices are grouped
|
||||
into one stream and the UCM devices are exposed on the endpoint as destinations.
|
||||
|
||||
## ALSA Fallback
|
||||
|
||||
Each PCM stream (node) becomes an endpoint. The endpoint references the
|
||||
ALSA device ID.
|
||||
|
||||
Each endpoint has one stream (for now) called HiFi Playback / HiFi Capture.
|
||||
|
||||
More streams can be created depending on the format of the node.
|
||||
|
||||
## ALSA Pulse UCM
|
||||
|
||||
Using the ALSA backend of PulseAudio we can create the following streams.
|
||||
|
||||
## ALSA Pulse Fallback
|
||||
|
||||
The pulse ALSA backend will use the mixer controls and some probing to
|
||||
create the following nodes and endpoints.
|
||||
|
||||
|
||||
# PulseAudio
|
||||
|
||||
PulseAudio uses the session manager API to construct cards with profiles
|
||||
and sink/source with ports.
|
||||
|
||||
If an endpoint references a device, a card object is created for the device.
|
||||
|
||||
Each endpoint becomes a sink/source.
|
||||
|
||||
Each Stream in the endpoint becomes a profile on the PulseAudio card. Because
|
||||
only one profile is selected on the device, only one stream is visible on
|
||||
the endpoint. This clashes with the notion that multiple streams can be
|
||||
active at the same time but is a PulseAudio limitation.
|
||||
|
||||
Each Endpoint destination becomes a port on the sink/source.
|
||||
|
||||
*/
|
348
doc/dox/internals/scheduling.dox
Normal file
348
doc/dox/internals/scheduling.dox
Normal file
|
@ -0,0 +1,348 @@
|
|||
/** \page page_scheduling Graph Scheduling
|
||||
|
||||
This document tries to explain how the PipeWire graph is scheduled.
|
||||
|
||||
Graph are constructed from linked nodes together with their ports. This
|
||||
results in a dependency graph between nodes. Special care is taken for
|
||||
loopback links so that the graph remains a directed graph.
|
||||
|
||||
# Processing threads
|
||||
|
||||
The server (and clients) have two processing threads:
|
||||
|
||||
- A main thread that will do all IPC with clients and server and configures the
|
||||
nodes in the graph for processing.
|
||||
- A (or more) data processing thread that only does the data processing.
|
||||
|
||||
|
||||
The data processing threads are given realtime priority and are designed to
|
||||
run with as little overhead as possible. All of the node resources such as
|
||||
buffers, io areas and metadata will be set up in shared memory before the
|
||||
node is scheduled to run.
|
||||
|
||||
This document describes the processing that happens in the data processing
|
||||
thread after the main-thread has configured it.
|
||||
|
||||
# Nodes
|
||||
|
||||
Nodes are objects with 0 or more input and output ports.
|
||||
|
||||
Each node also has:
|
||||
|
||||
- an eventfd to signal the node that it can start processing
|
||||
- an activation record that lives in shared memory with memfd.
|
||||
|
||||
```
|
||||
eventfd
|
||||
+-^---------+
|
||||
| |
|
||||
in out
|
||||
| |
|
||||
+-v---------+
|
||||
activation {
|
||||
status:OK, // bitmask of NEED_DATA, HAVE_DATA or OK
|
||||
pending:0, // number of unsatisfied dependencies to be able to run
|
||||
required:0 // number of dependencies with other nodes
|
||||
}
|
||||
```
|
||||
|
||||
The activation record has the following information:
|
||||
|
||||
- processing state and pending dependencies. As long as there are pending dependencies
|
||||
the node can not be processed. This is the only relevant information for actually
|
||||
scheduling the graph and is shown in the above illustration.
|
||||
- Current status of the node and profiling info (TRIGGERED, AWAKE, FINISHED, timestamps
|
||||
when the node changed state).
|
||||
- Timing information, mostly for drivers when the processing started, the time, duration
|
||||
and rate (quantum) etc..
|
||||
- Information about repositions (seek) and timebase owners.
|
||||
|
||||
|
||||
# Links
|
||||
|
||||
When two nodes are linked together, the output node becomes a dependency for the input
|
||||
node. This means the input node can only start processing when the output node is finished.
|
||||
|
||||
This dependency is reflected in the required counter in the activation record. In below
|
||||
illustration, B's required field is incremented with 1. The pending field is set to the
|
||||
required field when the graph is started. Node A will keep a list of all targets (B) that it
|
||||
is a dependency of.
|
||||
|
||||
This dependency update is only performed when the link is ready (negotiated) and the nodes
|
||||
are ready to schedule (runnable).
|
||||
|
||||
|
||||
```
|
||||
eventfd eventfd
|
||||
+-^---------+ +-^---------+
|
||||
| | link | |
|
||||
in A out ---------------------> in B out
|
||||
| | | |
|
||||
+-v---------+ +-v---------+
|
||||
activation { target activation {
|
||||
status:OK, --------------------> status:OK,
|
||||
pending:0, pending:1,
|
||||
required:0 required:1
|
||||
} }
|
||||
```
|
||||
|
||||
Multiple links between A and B will only result in 1 target link between A and B.
|
||||
|
||||
|
||||
# Drivers
|
||||
|
||||
The graph can only run if there is a driver node that is in some way linked to an
|
||||
active node.
|
||||
|
||||
The driver is special because it will have to initiate the processing in the graph. It
|
||||
will use a timer or some sort of interrupt from hardware to start the cycle.
|
||||
|
||||
Any node can also be a candidate for a driver (when the node.driver property is true).
|
||||
PipeWire will select the node with the highest priority.driver property as the driver.
|
||||
|
||||
Nodes will be assigned to the driver node they will be scheduled with. Each node holds
|
||||
a reference to the driver and increments the required field of the driver.
|
||||
|
||||
When a node is ready to be scheduled, the driver adds the node to its list of targets
|
||||
and increments the required field.
|
||||
|
||||
|
||||
```
|
||||
eventfd eventfd
|
||||
+-^---------+ +-^---------+
|
||||
| | link | |
|
||||
in A out ---------------------> in B out
|
||||
| | | |
|
||||
+-v---------+ +-v---------+
|
||||
activation { target activation {
|
||||
status:OK, --------------------> status:OK,
|
||||
pending:0, pending:0,
|
||||
required:1 required:2
|
||||
} }
|
||||
| ^ ^
|
||||
| | / /
|
||||
| | / /
|
||||
| | / /
|
||||
| | / /
|
||||
| | / /
|
||||
v | /-------------/ /
|
||||
activation { /
|
||||
status:OK, V---------------/
|
||||
pending:0,
|
||||
required:2
|
||||
}
|
||||
+-^---------+
|
||||
| |
|
||||
| driver |
|
||||
| |
|
||||
+-v---------+
|
||||
eventfd
|
||||
```
|
||||
|
||||
As seen in the illustration above, the driver holds a link to each node it needs to
|
||||
schedule and each node holds a link to the driver. Some nodes hold a link to other
|
||||
nodes.
|
||||
|
||||
It is possible that the driver is the same as a node in the graph (for example node A)
|
||||
but conceptually, the links above are still valid.
|
||||
|
||||
The driver will then start processing the graph by emitting the ready signal. PipeWire
|
||||
will then:
|
||||
|
||||
- Check the previous cycle. Did it complete? Mark xrun on unfinished nodes.
|
||||
- Perform reposition requests if any, timebase changes, etc..
|
||||
- The pending counter of each follower node is set to the required field.
|
||||
- It then loops over all targets of the driver and atomically decrements the required
|
||||
field of the activation record. When the required field is 0, the eventfd is signaled
|
||||
and the node can be scheduled.
|
||||
|
||||
In our example above, Node A and B will have their pending state decremented. Node A
|
||||
will be 0 and will be triggered first (node B has 2 pending dependencies to start with and
|
||||
will not be triggered yet). The driver itself also has 2 dependencies left and will not
|
||||
be triggered (complete) yet.
|
||||
|
||||
## Scheduling node A
|
||||
|
||||
When the eventfd is signaled on a node, we say the node is triggered and it will be able
|
||||
to process data. It consumes the input on the input ports and produces more data on the
|
||||
output ports.
|
||||
|
||||
After processing, node A goes through the list of targets and decrements each pending
|
||||
field (node A has a reference to B and the driver).
|
||||
|
||||
In our above example, the driver is decremented (from 2 to 1) but is not yet triggered.
|
||||
node B is decremented (from 1 to 0) and is triggered by writing to the eventfd.
|
||||
|
||||
## Scheduling node B
|
||||
|
||||
Node B is scheduled and processes the input from node A. It then goes through the list of
|
||||
targets and decrements the pending fields. It decrements the pending field of the
|
||||
driver (from 1 to 0) and triggers the driver.
|
||||
|
||||
## Scheduling the driver
|
||||
|
||||
The graph always completes after the driver is triggered and scheduled. All required
|
||||
fields from all the nodes in the target list of the driver are now 0.
|
||||
|
||||
The driver calculates some stats about cpu time etc.
|
||||
|
||||
# Remote nodes.
|
||||
|
||||
For remote nodes, the eventfd and the activation is transferred from the server
|
||||
to the client.
|
||||
|
||||
This means that writing to the remote client eventfd will wake the client directly
|
||||
without going to the server first.
|
||||
|
||||
All remote clients also get the activation and eventfd of the peer and driver they
|
||||
are linked to and can directly trigger peers and drivers without going to the
|
||||
server first.
|
||||
|
||||
## Remote driver nodes.
|
||||
|
||||
Remote drivers start the graph cycle directly without going to the server first.
|
||||
|
||||
After they complete (and only when the profiler is active), they will trigger an
|
||||
extra eventfd to signal the server that the graph completed. This is used by the
|
||||
server to generate the profiler info.
|
||||
|
||||
## Lazy scheduling
|
||||
|
||||
Normally, a driver will wake up the graph and all the followers need to process
|
||||
the data in sync. There are cases where:
|
||||
|
||||
1. the follower might not be ready to process the data
|
||||
2. the driver rate is not ideal, the follower rate is better
|
||||
3. the driver might not know when new data is available in the follower and
|
||||
might wake up the graph too often.
|
||||
|
||||
In these cases, the driver and follower roles need to be reversed and a mechanism
|
||||
needs to be provided so that the follower can know when it is worth processing the
|
||||
graph.
|
||||
|
||||
For notifying when the graph is ready to be processed, (non driver) nodes can send
|
||||
a RequestProcess event which will arrive as a RequestProcess command in the driver.
|
||||
The driver can then decide to run the graph or not.
|
||||
|
||||
When the graph is started or partially controlled by RequestProcess events and
|
||||
commands we say we have lazy scheduling. The driver is not always scheduling according
|
||||
to its own rhythm but also depending on the follower.
|
||||
|
||||
We can't just enable lazy scheduling when no follower will emit RequestProcess events
|
||||
or when no driver will listen for RequestProcess commands. Two new node properties are
|
||||
defined:
|
||||
|
||||
- node.supports-lazy = 0 | 1 | ...
|
||||
|
||||
0 means lazy scheduling as a driver is not supported
|
||||
>1 means lazy scheduling as a driver is supported with increasing preference
|
||||
|
||||
- node.supports-request
|
||||
|
||||
0 means request events as a follower are not supported
|
||||
>1 means request events as a follower are supported with increasing preference
|
||||
|
||||
We can only enable lazy scheduling when both the driver and (at least one) follower
|
||||
has the node.supports-lazy and node.supports-request property respectively.
|
||||
|
||||
Node can end up as a driver (is_driver()) and lazy scheduling can be enabled (is_lazy()),
|
||||
which results in the following cases:
|
||||
|
||||
driver producer
|
||||
-> node.driver = true
|
||||
-> is_driving() && !is_lazy()
|
||||
-> calls trigger_process() to start the graph
|
||||
|
||||
lazy producer
|
||||
-> node.driver = true
|
||||
-> node.supports-lazy = 1
|
||||
-> is_driving() && is_lazy()
|
||||
-> listens for RequestProcess and calls trigger_process() to start the graph
|
||||
|
||||
requesting producer
|
||||
-> node.supports-request = 1
|
||||
-> !is_driving() && is_lazy()
|
||||
-> emits RequestProcess to suggest starting the graph
|
||||
|
||||
follower producer
|
||||
-> !is_driving() && !is_lazy()
|
||||
|
||||
|
||||
driver consumer
|
||||
-> node.driver = true
|
||||
-> is_driving() && !is_lazy()
|
||||
-> calls trigger_process() to start the graph
|
||||
|
||||
lazy consumer
|
||||
-> node.driver = true
|
||||
-> node.supports-lazy = 1
|
||||
-> is_driving() && is_lazy()
|
||||
-> listens for RequestProcess and calls trigger_process() to start the graph
|
||||
|
||||
requesting consumer
|
||||
-> node.supports-request = 1
|
||||
-> !is_driving() && is_lazy()
|
||||
-> emits RequestProcess to suggest starting the graph
|
||||
|
||||
follower consumer
|
||||
-> !is_driving() && !is_lazy()
|
||||
|
||||
|
||||
Some use cases:
|
||||
|
||||
1. Screensharing - driver producer, follower consumer
|
||||
- The producer starts the graph when a new frame is available.
|
||||
- The consumer consumes the new frames.
|
||||
-> throttles to the rate of the producer and idles when no frames
|
||||
are available.
|
||||
|
||||
producer
|
||||
- node.driver = true
|
||||
|
||||
consumer
|
||||
- node.driver = false
|
||||
|
||||
-> producer selected as driver, consumer is simple follower.
|
||||
lazy scheduling inactive (no lazy driver or no request follower)
|
||||
|
||||
|
||||
2. headless server - requesting producer, (semi) lazy driver consumer
|
||||
|
||||
- The producer emits RequestProcess when new frames are available.
|
||||
- The consumer requests new frames from the producer according to its
|
||||
refresh rate when there are RequestProcess commands.
|
||||
-> this throttles the framerate to the consumer but idles when there is
|
||||
no activity on the producer.
|
||||
|
||||
producer
|
||||
- node.driver = true
|
||||
- node.supports-request = 1
|
||||
|
||||
consumer
|
||||
- node.driver = true
|
||||
- node.supports-lazy = 2
|
||||
|
||||
-> consumer is selected as driver (lazy > request)
|
||||
lazy scheduling active (1 lazy driver and at least 1 request follower)
|
||||
|
||||
|
||||
3. frame encoder - lazy driver producer, requesting follower consumer
|
||||
|
||||
- The consumer pulls a frame when it is ready to encode the next one.
|
||||
- The producer produces the next frame on demand.
|
||||
-> throttles the speed to the consumer without idle.
|
||||
|
||||
producer
|
||||
- node.driver = true
|
||||
- node.supports-lazy = 1
|
||||
|
||||
consumer
|
||||
- node.driver = true
|
||||
- node.supports-request = 1
|
||||
|
||||
-> producer is selected as driver (lazy <= request)
|
||||
lazy scheduling active (1 lazy driver and at least 1 request follower)
|
||||
|
||||
|
||||
|
||||
*/
|
46
doc/dox/internals/session-manager.dox
Normal file
46
doc/dox/internals/session-manager.dox
Normal file
|
@ -0,0 +1,46 @@
|
|||
/** \page page_session_manager PipeWire Session Manager
|
||||
|
||||
The \ref page_daemon is primarily a framework that allows devices and
|
||||
applications to exchange data.
|
||||
|
||||
It provides the mechanism to do so but the policy deciding which components
|
||||
can talk to each other and when is controlled by the session manager. As
|
||||
outlined in \ref page_objects_design, PipeWire provides a media graph
|
||||
consisting of devices, nodes and ports. The session manager is the one that
|
||||
decides on the links between those elements.
|
||||
|
||||
Two prominent session managers currently exist:
|
||||
|
||||
- [PipeWire Media Session](https://gitlab.freedesktop.org/pipewire/media-session), the
|
||||
example session manager.
|
||||
- [WirePlumber](https://gitlab.freedesktop.org/pipewire/wireplumber), a
|
||||
modular session manager based on GObject.
|
||||
[Documentation](https://pipewire.pages.freedesktop.org/wireplumber/)
|
||||
|
||||
This page describes some of the requirements for session managers in general.
|
||||
|
||||
|
||||
# Client Management
|
||||
|
||||
PipeWire provides a \ref page_access "permission system" to limit client's
|
||||
access to resources but only \ref page_module_access "basic permission
|
||||
handling". The session manager is expected to decide whether clients may
|
||||
access specific resources.
|
||||
|
||||
|
||||
# Device Management
|
||||
|
||||
PipeWire's responsibility is to open devices, however the decision on which
|
||||
devices should be opened is the job of a session manager, including the
|
||||
configuration of those devices.
|
||||
|
||||
|
||||
# Endpoint Grouping
|
||||
|
||||
An endpoint is, effectively, a group of nodes that are a logical unit that can
|
||||
consume or produce media data. For example, a Bluetooth speaker may present as
|
||||
several nodes but is only one logical unit to stream audio to.
|
||||
|
||||
See \ref page_objects_design for details on Endpoints.
|
||||
|
||||
*/
|
100
doc/dox/modules.dox
Normal file
100
doc/dox/modules.dox
Normal file
|
@ -0,0 +1,100 @@
|
|||
/** \page page_modules Modules
|
||||
|
||||
A PipeWire module is effectively a PipeWire client in an `.so` file that
|
||||
shares the \ref pw_context with the loading entity. Usually modules are
|
||||
loaded when they are listed in the configuration files. For example the
|
||||
default configuration file loads several modules:
|
||||
|
||||
```
|
||||
context.modules = [
|
||||
...
|
||||
# The native communication protocol.
|
||||
{ name = libpipewire-module-protocol-native }
|
||||
|
||||
# The profile module. Allows application to access profiler
|
||||
# and performance data. It provides an interface that is used
|
||||
# by pw-top and pw-profiler.
|
||||
{ name = libpipewire-module-profiler }
|
||||
|
||||
# Allows applications to create metadata objects. It creates
|
||||
# a factory for Metadata objects.
|
||||
{ name = libpipewire-module-metadata }
|
||||
|
||||
# Creates a factory for making devices that run in the
|
||||
# context of the PipeWire server.
|
||||
{ name = libpipewire-module-spa-device-factory }
|
||||
...
|
||||
]
|
||||
```
|
||||
The matching libraries are:
|
||||
```
|
||||
$ ls -1 /usr/lib64/pipewire-0.3/libpipewire-module*
|
||||
...
|
||||
/usr/lib64/pipewire-0.3/libpipewire-module-metadata.so
|
||||
/usr/lib64/pipewire-0.3/libpipewire-module-profiler.so
|
||||
/usr/lib64/pipewire-0.3/libpipewire-module-protocol-native.so
|
||||
/usr/lib64/pipewire-0.3/libpipewire-module-spa-device-factory.so
|
||||
...
|
||||
```
|
||||
|
||||
A module's entry point is the `pipewire__module_init` function, see \ref
|
||||
PIPEWIRE_SYMBOL_MODULE_INIT.
|
||||
|
||||
\code
|
||||
int pipewire__module_init(struct pw_impl_module *module, const char *args).`
|
||||
\endcode
|
||||
|
||||
See the \ref page_module_example_sink and \ref page_module_example_source
|
||||
modules for a general oveview of how modules look like.
|
||||
|
||||
List of known modules:
|
||||
|
||||
- \subpage page_module_access
|
||||
- \subpage page_module_adapter
|
||||
- \subpage page_module_avb
|
||||
- \subpage page_module_client_device
|
||||
- \subpage page_module_client_node
|
||||
- \subpage page_module_combine_stream
|
||||
- \subpage page_module_echo_cancel
|
||||
- \subpage page_module_example_filter
|
||||
- \subpage page_module_example_sink
|
||||
- \subpage page_module_example_source
|
||||
- \subpage page_module_fallback_sink
|
||||
- \subpage page_module_ffado_driver
|
||||
- \subpage page_module_filter_chain
|
||||
- \subpage page_module_jackdbus_detect
|
||||
- \subpage page_module_jack_tunnel
|
||||
- \subpage page_module_link_factory
|
||||
- \subpage page_module_loopback
|
||||
- \subpage page_module_metadata
|
||||
- \subpage page_module_netjack2_driver
|
||||
- \subpage page_module_netjack2_manager
|
||||
- \subpage page_module_parametric_equalizer
|
||||
- \subpage page_module_pipe_tunnel
|
||||
- \subpage page_module_portal
|
||||
- \subpage page_module_profiler
|
||||
- \subpage page_module_protocol_native
|
||||
- \subpage page_module_protocol_pulse
|
||||
- \subpage page_module_protocol_simple
|
||||
- \subpage page_module_pulse_tunnel
|
||||
- \subpage page_module_raop_sink
|
||||
- \subpage page_module_raop_discover
|
||||
- \subpage page_module_roc_sink
|
||||
- \subpage page_module_roc_source
|
||||
- \subpage page_module_rtp_sap
|
||||
- \subpage page_module_rtp_sink
|
||||
- \subpage page_module_rtp_source
|
||||
- \subpage page_module_rtp_session
|
||||
- \subpage page_module_rt
|
||||
- \subpage page_module_spa_node
|
||||
- \subpage page_module_spa_node_factory
|
||||
- \subpage page_module_spa_device
|
||||
- \subpage page_module_spa_device_factory
|
||||
- \subpage page_module_session_manager
|
||||
- \subpage page_module_snapcast_discover
|
||||
- \subpage page_module_vban_recv
|
||||
- \subpage page_module_vban_send
|
||||
- \subpage page_module_x11_bell
|
||||
- \subpage page_module_zeroconf_discover
|
||||
|
||||
*/
|
149
doc/dox/overview.dox
Normal file
149
doc/dox/overview.dox
Normal file
|
@ -0,0 +1,149 @@
|
|||
/** \page page_overview Overview
|
||||
|
||||
# Concepts
|
||||
|
||||
## The PipeWire Server
|
||||
|
||||
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
|
||||
|
||||
- a stereo ALSA PCM playback device can appear as a sink with two input ports: front-left and front-right or
|
||||
- a virtual ALSA device, to which clients which attempt to use ALSA directly connect, can appear as a source with two output ports: front-left and front right.
|
||||
|
||||
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.
|
||||
|
||||
## The 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.
|
||||
|
||||
### Graph control
|
||||
|
||||
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:
|
||||
|
||||
- pipewire-media-session, which was the first implementation of a session manager.c
|
||||
Today, it is used mainly in debugging scenarios.
|
||||
- WirePlumber, which takes a modular approach:
|
||||
It provides another, higher-level API compared to the PipeWire one, and runs Lua scripts that implement the management logic using the said API.
|
||||
It ships with default scripts and configuration that handle linking policies as well as monitoring and automatic spawning of ALSA, bluez, libcamera and v4l2 devices.
|
||||
The API is available for any process, not only from WirePlumber’s Lua scripts.
|
||||
|
||||
### Node implementation
|
||||
|
||||
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.
|
||||
|
||||
## API Semantics
|
||||
|
||||
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:
|
||||
|
||||
- Multiple, supported sample rates
|
||||
- Channel count
|
||||
- Positions sample format
|
||||
- Available monitor ports
|
||||
|
||||
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 four flags. The four flags are:
|
||||
|
||||
- Read: the object can be seen and events can be received;
|
||||
- Write: the object can be modified, usually through methods (which requires the execute flag)
|
||||
- eXecute: methods can be called;
|
||||
- Metadata: metadata can be set on the object.
|
||||
- Link: any link can be made even to a port that is not visible by the owner of the port.
|
||||
|
||||
### Object types
|
||||
|
||||
The following are the known types and their most important, specialized parameters and methods:
|
||||
|
||||
#### Core
|
||||
|
||||
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.
|
||||
|
||||
#### Clients
|
||||
|
||||
A client object is the representation of an open connection with a client process with the server.
|
||||
|
||||
#### Modules
|
||||
|
||||
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
|
||||
|
||||
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).
|
||||
Notes have a method `process`, which eats up data from input ports and provides data for each output port.
|
||||
|
||||
#### Ports
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
#### Links
|
||||
|
||||
Data flows between nodes when there is a Link between their ports.
|
||||
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).
|
||||
|
||||
#### Devices
|
||||
|
||||
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 a profile, which allows one to configure them.
|
||||
|
||||
#### Factories
|
||||
|
||||
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.
|
||||
|
||||
### Common parameters and methods
|
||||
|
||||
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).
|
||||
|
||||
## Context
|
||||
|
||||
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.
|
||||
|
||||
*/
|
27
doc/dox/programs/index.md
Normal file
27
doc/dox/programs/index.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
\page page_programs Programs
|
||||
|
||||
Manual pages:
|
||||
|
||||
- \subpage page_man_pipewire_1
|
||||
- \subpage page_man_pipewire-pulse_1
|
||||
- \subpage page_man_pw-cat_1
|
||||
- \subpage page_man_pw-cli_1
|
||||
- \subpage page_man_pw-config_1
|
||||
- \subpage page_man_pw-container_1
|
||||
- \subpage page_man_pw-dot_1
|
||||
- \subpage page_man_pw-dump_1
|
||||
- \subpage page_man_pw-jack_1
|
||||
- \subpage page_man_pw-link_1
|
||||
- \subpage page_man_pw-loopback_1
|
||||
- \subpage page_man_pw-metadata_1
|
||||
- \subpage page_man_pw-mididump_1
|
||||
- \subpage page_man_pw-mon_1
|
||||
- \subpage page_man_pw-profiler_1
|
||||
- \subpage page_man_pw-reserve_1
|
||||
- \subpage page_man_pw-top_1
|
||||
- \subpage page_man_pw-v4l2_1
|
||||
- \subpage page_man_spa-acp-tool_1
|
||||
- \subpage page_man_spa-inspect_1
|
||||
- \subpage page_man_spa-json-dump_1
|
||||
- \subpage page_man_spa-monitor_1
|
||||
- \subpage page_man_spa-resample_1
|
58
doc/dox/programs/pipewire-pulse.1.md
Normal file
58
doc/dox/programs/pipewire-pulse.1.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
\page page_man_pipewire-pulse_1 pipewire-pulse
|
||||
|
||||
The PipeWire PulseAudio replacement
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pipewire-pulse** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
**pipewire-pulse** starts a PulseAudio-compatible daemon that integrates
|
||||
with the PipeWire media server, by running a pipewire process through a
|
||||
systemd service. This daemon is a drop-in replacement for the PulseAudio
|
||||
daemon.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par -v | \--verbose
|
||||
Increase the verbosity by one level. This option may be specified
|
||||
multiple times.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -c | \--config=FILE
|
||||
Load the given config file (Default: pipewire-pulse.conf).
|
||||
|
||||
# ENVIRONMENT VARIABLES
|
||||
|
||||
The generic \ref pipewire-env "pipewire(1) environment variables"
|
||||
are supported.
|
||||
|
||||
In addition:
|
||||
|
||||
@PAR@ pulse-env PULSE_RUNTIME_PATH
|
||||
|
||||
@PAR@ pulse-env XDG_RUNTIME_DIR
|
||||
Directory where to create the native protocol pulseaudio socket.
|
||||
|
||||
@PAR@ pulse-env PULSE_LATENCY_MSEC
|
||||
Extra buffering latency in milliseconds. This controls buffering
|
||||
logic in `libpulse` and may be set for PulseAudio client applications
|
||||
to adjust their buffering. (Setting it on the `pipewire-pulse` server
|
||||
has no effect.)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire-pulse_conf_5 "pipewire-pulse.conf(5)",
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)"
|
251
doc/dox/programs/pipewire.1.md
Normal file
251
doc/dox/programs/pipewire.1.md
Normal file
|
@ -0,0 +1,251 @@
|
|||
\page page_man_pipewire_1 pipewire
|
||||
|
||||
The PipeWire media server
|
||||
|
||||
\tableofcontents
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pipewire** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
PipeWire is a service that facilitates sharing of multimedia content
|
||||
between devices and applications.
|
||||
|
||||
The **pipewire** daemon reads a config file that is further documented
|
||||
in \ref page_man_pipewire_conf_5 "pipewire.conf(5)" manual page.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par -v | \--verbose
|
||||
Increase the verbosity by one level. This option may be specified
|
||||
multiple times.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -c | \--config=FILE
|
||||
Load the given config file (Default: pipewire.conf).
|
||||
|
||||
\par -P | \--properties=PROPS
|
||||
Add the given properties as a SPA JSON object to the context.
|
||||
|
||||
# RUNTIME SETTINGS @IDX@ pipewire
|
||||
|
||||
A PipeWire daemon will also expose a settings metadata object that can
|
||||
be used to change some settings at runtime.
|
||||
|
||||
Normally these settings can bypass any of the restrictions listed in
|
||||
the config options above, such as quantum and samplerate values.
|
||||
|
||||
The settings can be modified using \ref page_man_pw-metadata_1 "pw-metadata(1)":
|
||||
```
|
||||
pw-metadata -n settings # list settings
|
||||
pw-metadata -n settings 0 # list server settings
|
||||
pw-metadata -n settings 0 log.level 2 # modify a server setting
|
||||
```
|
||||
|
||||
@PAR@ pipewire-settings log.level = INTEGER
|
||||
Change the log level of the PipeWire daemon.
|
||||
|
||||
@PAR@ pipewire-settings clock.rate = INTEGER
|
||||
The default samplerate.
|
||||
|
||||
@PAR@ pipewire-settings clock.allowed-rates = [ RATE1 RATE2... ]
|
||||
The allowed samplerates.
|
||||
|
||||
@PAR@ pipewire-settings clock.force-rate = INTEGER
|
||||
\parblock
|
||||
Temporarily forces the graph to operate in a fixed sample rate.
|
||||
Both DSP processing and devices will switch to the new rate immediately.
|
||||
Running streams (PulseAudio, native and ALSA applications) will automatically
|
||||
resample to match the new rate.
|
||||
|
||||
Set the value to 0 to allow the sample rate to vary again.
|
||||
\endparblock
|
||||
|
||||
@PAR@ pipewire-settings clock.quantum = INTEGER
|
||||
The default quantum (buffer size).
|
||||
|
||||
@PAR@ pipewire-settings clock.min-quantum = INTEGER
|
||||
Smallest quantum to be used.
|
||||
|
||||
@PAR@ pipewire-settings clock.max-quantum = INTEGER
|
||||
Largest quantum to be used.
|
||||
|
||||
@PAR@ pipewire-settings clock.force-quantum = INTEGER
|
||||
\parblock
|
||||
Temporarily force the graph to operate in a fixed quantum.
|
||||
|
||||
Set the value to 0 to allow the quantum to vary again.
|
||||
\endparblock
|
||||
|
||||
# ENVIRONMENT VARIABLES @IDX@ pipewire-env
|
||||
|
||||
## Socket directories
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_RUNTIME_DIR
|
||||
|
||||
@PAR@ pipewire-env XDG_RUNTIME_DIR
|
||||
|
||||
@PAR@ pipewire-env USERPROFILE
|
||||
Used to find the PipeWire socket on the server (and native clients).
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_CORE
|
||||
Name of the socket to make.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_REMOTE
|
||||
Name of the socket to connect to.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_DAEMON
|
||||
If set to true then the process becomes a new PipeWire server.
|
||||
|
||||
## Config directories, config file name and prefix
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_CONFIG_DIR
|
||||
|
||||
@PAR@ pipewire-env XDG_CONFIG_HOME
|
||||
|
||||
@PAR@ pipewire-env HOME
|
||||
Used to find the config file directories.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_CONFIG_PREFIX
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_CONFIG_NAME
|
||||
Used to override the application provided
|
||||
config prefix and config name.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_NO_CONFIG
|
||||
Enables (false) or disables (true) overriding on the default configuration.
|
||||
|
||||
## Context information
|
||||
|
||||
As part of a client context, the following information is collected
|
||||
from environment variables and placed in the context properties:
|
||||
|
||||
@PAR@ pipewire-env LANG
|
||||
The current language in `application.language`.
|
||||
|
||||
@PAR@ pipewire-env XDG_SESSION_ID
|
||||
Set as the `application.process.session-id` property.
|
||||
|
||||
@PAR@ pipewire-env DISPLAY
|
||||
Is set as the `window.x11.display` property.
|
||||
|
||||
## Modules
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_MODULE_DIR
|
||||
Sets the directory where to find PipeWire modules.
|
||||
|
||||
@PAR@ pipewire-env SPA_SUPPORT_LIB
|
||||
The name of the SPA support lib to load. This can be used to switch to
|
||||
an alternative support library, for example, to run on the EVL realtime kernel.
|
||||
|
||||
## Logging options
|
||||
|
||||
@PAR@ pipewire-env JOURNAL_STREAM
|
||||
Is used to parse the stream used for the journal. This is usually configured by
|
||||
systemd.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_LOG_LINE
|
||||
Enables the logging of line numbers. Default true.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_LOG_TIMESTAMP
|
||||
Logging timestamp type: "local", "monotonic", "realtime", "none".
|
||||
Default "local".
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_LOG
|
||||
Specifies a log file to use instead of the default logger.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_LOG_SYSTEMD
|
||||
Enables the use of systemd for the logger, default true.
|
||||
|
||||
## Other settings
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_CPU
|
||||
Selects the CPU and flags. This is a bitmask of any of the \ref CPU flags
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_VM
|
||||
Selects the Virtual Machine PipeWire is running on. This can be any of the \ref CPU "VM"
|
||||
types.
|
||||
|
||||
@PAR@ pipewire-env DISABLE_RTKIT
|
||||
Disables the use of RTKit or the Realtime Portal for realtime scheduling.
|
||||
|
||||
@PAR@ pipewire-env NO_COLOR
|
||||
Disables the use of colors in the console output.
|
||||
|
||||
## Debugging options
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_DLCLOSE
|
||||
Enables (true) or disables (false) the use of dlclose when a shared library
|
||||
is no longer in use. When debugging, it might make sense to disable dlclose to be able to get
|
||||
debugging symbols from the object.
|
||||
|
||||
## Stream options
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_NODE
|
||||
Makes a stream connect to a specific `object.serial` or `node.name`.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_PROPS
|
||||
Adds extra properties to a stream or filter.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_QUANTUM
|
||||
Forces a specific rate and buffer-size for the stream or filter.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_LATENCY
|
||||
Sets a specific latency for a stream or filter. This is only a suggestion but
|
||||
the configured latency will not be larger.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_RATE
|
||||
Sets a rate for a stream or filter. This is only a suggestion. The rate will be
|
||||
switched when the graph is idle.
|
||||
|
||||
@PAR@ pipewire-env PIPEWIRE_AUTOCONNECT
|
||||
Overrides the default stream autoconnect settings.
|
||||
|
||||
## Plugin options
|
||||
|
||||
@PAR@ pipewire-env SPA_PLUGIN_DIR
|
||||
Is used to locate SPA plugins.
|
||||
|
||||
@PAR@ pipewire-env SPA_DATA_DIR
|
||||
Is used to locate plugin specific config files. This is used by the
|
||||
bluetooth plugin currently to locate the quirks database.
|
||||
|
||||
@PAR@ pipewire-env SPA_DEBUG
|
||||
Set the log level for SPA plugins. This is usually controlled by the `PIPEWIRE_DEBUG` variable
|
||||
when the plugins are managed by PipeWire but some standalone tools (like spa-inspect) uses this
|
||||
variable.
|
||||
|
||||
@PAR@ pipewire-env ACP_BUILDDIR
|
||||
If set, the ACP profiles are loaded from the builddir.
|
||||
|
||||
@PAR@ pipewire-env ACP_PATHS_DIR
|
||||
|
||||
@PAR@ pipewire-env ACP_PROFILES_DIR
|
||||
Used to locate the ACP paths and profile directories respectively.
|
||||
|
||||
@PAR@ pipewire-env LADSPA_PATH
|
||||
Comma separated list of directories where the ladspa plugins can be found.
|
||||
|
||||
@PAR@ pipewire-env LIBJACK_PATH
|
||||
Directory where the jack1 or jack2 libjack.so can be found.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pw-top_1 "pw-top(1)",
|
||||
\ref page_man_pw-dump_1 "pw-dump(1)",
|
||||
\ref page_man_pw-mon_1 "pw-mon(1)",
|
||||
\ref page_man_pw-cat_1 "pw-cat(1)",
|
||||
\ref page_man_pw-cli_1 "pw-cli(1)",
|
||||
\ref page_man_libpipewire-modules_7 "libpipewire-modules(7)"
|
163
doc/dox/programs/pw-cat.1.md
Normal file
163
doc/dox/programs/pw-cat.1.md
Normal file
|
@ -0,0 +1,163 @@
|
|||
\page page_man_pw-cat_1 pw-cat
|
||||
|
||||
Play and record media with PipeWire
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-cat** \[*options*\] \[*FILE* \| -\]
|
||||
|
||||
**pw-play** \[*options*\] \[*FILE* \| -\]
|
||||
|
||||
**pw-record** \[*options*\] \[*FILE* \| -\]
|
||||
|
||||
**pw-midiplay** \[*options*\] \[*FILE* \| -\]
|
||||
|
||||
**pw-midirecord** \[*options*\] \[*FILE* \| -\]
|
||||
|
||||
**pw-dsdplay** \[*options*\] \[*FILE* \| -\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
**pw-cat** is a simple tool for playing back or capturing raw or encoded
|
||||
media files on a PipeWire server. It understands all audio file formats
|
||||
supported by `libsndfile` for PCM capture and playback. When capturing
|
||||
PCM, the filename extension is used to guess the file format with the
|
||||
WAV file format as the default.
|
||||
|
||||
It understands standard MIDI files for playback and recording. This tool
|
||||
will not render MIDI files, it will simply make the MIDI events
|
||||
available to the graph. You need a MIDI renderer such as qsynth,
|
||||
timidity or a hardware MIDI rendered to hear the MIDI.
|
||||
|
||||
DSD playback is supported with the DSF file format. This tool will only
|
||||
work with native DSD capable hardware and will produce an error when no
|
||||
such hardware was found.
|
||||
|
||||
When the *FILE* is - input and output will be raw data from STDIN and
|
||||
STDOUT respectively.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -v | \--verbose
|
||||
Verbose operation.
|
||||
|
||||
\par -R | \--remote=NAME
|
||||
The name the *remote* instance to connect to. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -p | \--playback
|
||||
Playback mode. Read data from the specified file, and play it back. If
|
||||
the tool is called under the name **pw-play** or **pw-midiplay** this is
|
||||
the default.
|
||||
|
||||
\par -r | \--record
|
||||
Recording mode. Capture data and write it to the specified file. If the
|
||||
tool is called under the name **pw-record** or **pw-midirecord** this is
|
||||
the default.
|
||||
|
||||
\par -m | \--midi
|
||||
MIDI mode. *FILE* is a MIDI file. If the tool is called under the name
|
||||
**pw-midiplay** or **pw-midirecord** this is the default. Note that this
|
||||
program will *not* render the MIDI events into audible samples, it will
|
||||
simply provide the MIDI events in the graph. You need a separate MIDI
|
||||
renderer such as qsynth, timidity or a hardware renderer to hear the
|
||||
MIDI.
|
||||
|
||||
\par -d | \--dsd
|
||||
DSD mode. *FILE* is a DSF file. If the tool is called under the name
|
||||
**pw-dsdplay** this is the default. Note that this program will *not*
|
||||
render the DSD audio. You need a DSD capable device to play DSD content
|
||||
or this program will exit with an error.
|
||||
|
||||
\par \--media-type=VALUE
|
||||
Set the media type property (default Audio/Midi depending on mode). The
|
||||
media type is used by the session manager to select a suitable target to
|
||||
link to.
|
||||
|
||||
\par \--media-category=VALUE
|
||||
Set the media category property (default Playback/Capture depending on
|
||||
mode). The media type is used by the session manager to select a
|
||||
suitable target to link to.
|
||||
|
||||
\par \--media-role=VALUE
|
||||
Set the media role property (default Music). The media type is used by
|
||||
the session manager to select a suitable target to link to.
|
||||
|
||||
\par \--target=VALUE
|
||||
\parblock
|
||||
Set a node target (default auto). The value can be:
|
||||
|
||||
- **auto**: Automatically select (Default)
|
||||
|
||||
- **0**: Don't try to link this node
|
||||
|
||||
- <b>\<id\></b>: The object.serial or the node.name of a target node
|
||||
\endparblock
|
||||
|
||||
\par \--latency=VALUE\[*units*\]
|
||||
\parblock
|
||||
Set the node latency (default 100ms)
|
||||
|
||||
The latency determines the minimum amount of time it takes for a sample
|
||||
to travel from application to device (playback) and from device to
|
||||
application (capture).
|
||||
|
||||
The latency determines the size of the buffers that the application will
|
||||
be able to fill. Lower latency means smaller buffers but higher
|
||||
overhead. Higher latency means larger buffers and lower overhead.
|
||||
|
||||
Units can be **s** for seconds, **ms** for milliseconds, **us** for
|
||||
microseconds, **ns** for nanoseconds. If no units are given, the latency
|
||||
value is samples with the samplerate of the file.
|
||||
\endparblock
|
||||
|
||||
\par -P | \--properties=VALUE
|
||||
Set extra stream properties as a JSON object.
|
||||
|
||||
\par -q | \--quality=VALUE
|
||||
Resampler quality. When the samplerate of the source or destination file
|
||||
does not match the samplerate of the server, the data will be resampled.
|
||||
Higher quality uses more CPU. Values between 0 and 15 are allowed, the
|
||||
default quality is 4.
|
||||
|
||||
\par \--rate=VALUE
|
||||
The sample rate, default 48000.
|
||||
|
||||
\par \--channels=VALUE
|
||||
The number of channels, default 2.
|
||||
|
||||
\par \--channel-map=VALUE
|
||||
The channelmap. Possible values include: **mono**, **stereo**,
|
||||
**surround-21**, **quad**, **surround-22**, **surround-40**,
|
||||
**surround-31**, **surround-41**, **surround-50**, **surround-51**,
|
||||
**surround-51r**, **surround-70**, **surround-71** or a comma separated
|
||||
list of channel names: **FL**, **FR**, **FC**, **LFE**, **SL**, **SR**,
|
||||
**FLC**, **FRC**, **RC**, **RL**, **RR**, **TC**, **TFL**, **TFC**,
|
||||
**TFR**, **TRL**, **TRC**, **TRR**, **RLC**, **RRC**, **FLW**, **FRW**,
|
||||
**LFE2**, **FLH**, **FCH**, **FRH**, **TFLC**, **TFRC**, **TSL**,
|
||||
**TSR**, **LLFR**, **RLFE**, **BC**, **BLC**, **BRC**
|
||||
|
||||
\par \--format=VALUE
|
||||
The sample format to use. One of: **u8**, **s8**, **s16** (default),
|
||||
**s24**, **s32**, **f32**, **f64**.
|
||||
|
||||
\par \--volume=VALUE
|
||||
The stream volume, default 1.000. Depending on the locale you have
|
||||
configured, "," or "." may be used as a decimal separator. Check with
|
||||
**locale** command.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-mon_1 "pw-mon(1)",
|
192
doc/dox/programs/pw-cli.1.md
Normal file
192
doc/dox/programs/pw-cli.1.md
Normal file
|
@ -0,0 +1,192 @@
|
|||
\page page_man_pw-cli_1 pw-cli
|
||||
|
||||
The PipeWire Command Line Interface
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-cli** \[*command*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Interact with a PipeWire instance.
|
||||
|
||||
When a command is given, **pw-cli** will execute the command and exit
|
||||
|
||||
When no command is given, **pw-cli** starts an interactive session with
|
||||
the default PipeWire instance *pipewire-0*.
|
||||
|
||||
Connections to other, remote instances can be made. The current instance
|
||||
name is displayed at the prompt.
|
||||
|
||||
Note that **pw-cli** also creates a local PipeWire instance. Some
|
||||
commands operate on the current (remote) instance and some on the local
|
||||
instance, such as module loading.
|
||||
|
||||
Use the 'help' command to list the available commands.
|
||||
|
||||
# GENERAL COMMANDS
|
||||
|
||||
\par help | h
|
||||
Show a quick help on the commands available. It also lists the aliases
|
||||
for many commands.
|
||||
|
||||
\par quit | q
|
||||
Exit from **pw-cli**
|
||||
|
||||
# MODULE MANAGEMENT
|
||||
|
||||
Modules are loaded and unloaded in the local instance, thus the pw-cli
|
||||
binary itself and can add functionality or objects to the local
|
||||
instance. It is not possible in PipeWire to load modules in another
|
||||
instance.
|
||||
|
||||
\par load-module *name* \[*arguments...*\]
|
||||
\parblock
|
||||
Load a module specified by its name and arguments in the local instance.
|
||||
For most modules it is OK to be loaded more than once.
|
||||
|
||||
This command returns a module variable that can be used to unload the
|
||||
module.
|
||||
|
||||
The locally module is *not* visible in the remote instance. It is not
|
||||
possible in PipeWire to load modules in a remote instance.
|
||||
\endparblock
|
||||
|
||||
\par unload-module *module-var*
|
||||
Unload a module, specified either by its variable.
|
||||
|
||||
# OBJECT INTROSPECTION
|
||||
|
||||
\par list-objects
|
||||
List the objects of the current instance.
|
||||
|
||||
Objects are listed with their *id*, *type* and *version*.
|
||||
|
||||
\par info *id* | *all*
|
||||
Get information about a specific object or *all* objects.
|
||||
|
||||
Requesting info about an object will also notify you of changes.
|
||||
|
||||
# WORKING WITH REMOTES
|
||||
|
||||
\par connect \[*remote-name*\]
|
||||
\parblock
|
||||
Connect to a remote instance and make this the new current instance.
|
||||
|
||||
If no remote name is specified, a connection is made to the default
|
||||
remote instance, usually *pipewire-0*.
|
||||
|
||||
The special remote name called *internal* can be used to connect to the
|
||||
local **pw-cli** PipeWire instance.
|
||||
|
||||
This command returns a remote var that can be used to disconnect or
|
||||
switch remotes.
|
||||
\endparblock
|
||||
|
||||
\par disconnect \[*remote-var*\]
|
||||
\parblock
|
||||
Disconnect from a *remote instance*.
|
||||
|
||||
If no remote name is specified, the current instance is disconnected.
|
||||
\endparblock
|
||||
|
||||
\par list-remotes
|
||||
List all *remote instances*.
|
||||
|
||||
\par switch-remote \[*remote-var*\]
|
||||
\parblock
|
||||
Make the specified *remote* the current instance.
|
||||
|
||||
If no remote name is specified, the first instance is made current.
|
||||
\endparblock
|
||||
|
||||
# NODE MANAGEMENT
|
||||
|
||||
\par create-node *factory-name* \[*properties...*\]
|
||||
\parblock
|
||||
Create a node from a factory in the current instance.
|
||||
|
||||
Properties are key=value pairs separated by whitespace.
|
||||
|
||||
This command returns a *node variable*.
|
||||
\endparblock
|
||||
|
||||
\par export-node *node-id* \[*remote-var*\]
|
||||
Export a node from the local instance to the specified instance. When no
|
||||
instance is specified, the node will be exported to the current
|
||||
instance.
|
||||
|
||||
# DEVICE MANAGEMENT
|
||||
|
||||
\par create-device *factory-name* \[*properties...*\]
|
||||
\parblock
|
||||
Create a device from a factory in the current instance.
|
||||
|
||||
Properties are key=value pairs separated by whitespace.
|
||||
|
||||
This command returns a *device variable*.
|
||||
\endparblock
|
||||
|
||||
# LINK MANAGEMENT
|
||||
|
||||
\par create-link *node-id* *port-id* *node-id* *port-id* \[*properties...*\]
|
||||
\parblock
|
||||
Create a link between 2 nodes and ports.
|
||||
|
||||
Port *ids* and Node *ids* can be set to `-` to automatically select a node or
|
||||
a port.
|
||||
|
||||
Port *ids* can be `*` to automatically link matching ports ids in the nodes.
|
||||
|
||||
Properties are key=value pairs separated by whitespace.
|
||||
|
||||
This command returns one or more *link variables*.
|
||||
\endparblock
|
||||
|
||||
# GLOBALS MANAGEMENT
|
||||
|
||||
\par destroy *object-id*
|
||||
Destroy a global object.
|
||||
|
||||
# PARAMETER MANAGEMENT
|
||||
|
||||
\par enum-params *object-id* *param-id*
|
||||
\parblock
|
||||
Enumerate params of an object.
|
||||
|
||||
*param-id* can also be given as the param short name.
|
||||
\endparblock
|
||||
|
||||
\par set-param *object-id* *param-id* *param-json*
|
||||
\parblock
|
||||
Set param of an object.
|
||||
|
||||
*param-id* can also be given as the param short name.
|
||||
\endparblock
|
||||
|
||||
# PERMISSION MANAGEMENT
|
||||
|
||||
\par permissions *client-id* *object-id* *permission*
|
||||
\parblock
|
||||
Set permissions for a client.
|
||||
|
||||
*object-id* can be *-1* to set the default permissions.
|
||||
\endparblock
|
||||
|
||||
\par get-permissions *client-id*
|
||||
Get permissions of a client.
|
||||
|
||||
# COMMAND MANAGEMENT
|
||||
|
||||
\par send-command *object-id*
|
||||
Send a command to an object.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-mon_1 "pw-mon(1)",
|
97
doc/dox/programs/pw-config.1.md
Normal file
97
doc/dox/programs/pw-config.1.md
Normal file
|
@ -0,0 +1,97 @@
|
|||
\page page_man_pw-config_1 pw-config
|
||||
|
||||
Debug PipeWire Config parsing
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-config** \[*options*\] paths
|
||||
|
||||
**pw-config** \[*options*\] list \[*SECTION*\]
|
||||
|
||||
**pw-config** \[*options*\] merge *SECTION*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
List config paths and config sections and display the parsed output.
|
||||
|
||||
This tool can be used to get an overview of the config file that will be
|
||||
parsed by the PipeWire server and clients.
|
||||
|
||||
# COMMON OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -n | \--name=NAME
|
||||
Config Name (default 'pipewire.conf')
|
||||
|
||||
\par -p | \--prefix=PREFIX
|
||||
Config Prefix (default '')
|
||||
|
||||
\par -L | \--no-newline
|
||||
Omit newlines after values
|
||||
|
||||
\par -r | \--recurse
|
||||
Reformat config sections recursively
|
||||
|
||||
\par -N | \--no-colors
|
||||
Disable color output
|
||||
|
||||
\par -C | \-color\[=WHEN\]
|
||||
whether to enable color support. WHEN is
|
||||
*never*, *always*, or *auto*
|
||||
|
||||
# LISTING PATHS
|
||||
|
||||
Specify the paths command. It will display all the config files that
|
||||
will be parsed and in what order.
|
||||
|
||||
# LISTING CONFIG SECTIONS
|
||||
|
||||
Specify the list command with an optional *SECTION* to list the
|
||||
configuration fragments used for *SECTION*. Without a *SECTION*, all
|
||||
sections will be listed.
|
||||
|
||||
Use the -r options to reformat the sections.
|
||||
|
||||
# MERGING A CONFIG SECTION
|
||||
|
||||
With the merge option and a *SECTION*, pw-config will merge all config
|
||||
files into a merged config section and dump the results. This will be
|
||||
the section used by the client or server.
|
||||
|
||||
Use the -r options to reformat the sections.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
\par pw-config
|
||||
List all config files that will be used
|
||||
|
||||
\par pw-config -n pipewire-pulse.conf
|
||||
List all config files that will be used by the PipeWire pulseaudio
|
||||
server.
|
||||
|
||||
\par pw-config -n pipewire-pulse.conf list
|
||||
List all config sections used by the PipeWire pulseaudio server
|
||||
|
||||
\par pw-config -n jack.conf list context.properties
|
||||
List the context.properties fragments used by the JACK clients
|
||||
|
||||
\par pw-config -n jack.conf merge context.properties
|
||||
List the merged context.properties used by the JACK clients
|
||||
|
||||
\par pw-config -n pipewire.conf -r merge context.modules
|
||||
List the merged context.modules used by the PipeWire server and reformat
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-dump_1 "pw-dump(1)",
|
71
doc/dox/programs/pw-container.1.md
Normal file
71
doc/dox/programs/pw-container.1.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
\page page_man_pw-container_1 pw-container
|
||||
|
||||
The PipeWire container utility
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-container** \[*options*\] \[*PROGRAM*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Run a program in a new security context [1].
|
||||
|
||||
**pw-container** will create a new temporary unix socket and uses the
|
||||
SecurityContext extension API to create a server on this socket with
|
||||
the given properties. Clients created from this server socket will have
|
||||
the security properties attached to them.
|
||||
|
||||
This can be used to simulate the behaviour of Flatpak or other containers.
|
||||
|
||||
Without any arguments, **pw-container** simply creates the new socket
|
||||
and prints the address on stdout. Other PipeWire programs can then be run
|
||||
with `PIPEWIRE_REMOTE=<socket-address>` to connect through this security
|
||||
context.
|
||||
|
||||
When *PROGRAM* is given, the `PIPEWIRE_REMOTE` env variable will be set
|
||||
and *PROGRAM* will be passed to system(). Argument to *PROGRAM* need to be
|
||||
properly quoted.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -P | \--properties=VALUE
|
||||
Set extra context properties as a JSON object.
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the *remote* instance to connect to. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
# EXIT STATUS
|
||||
|
||||
If the security context was successfully created, **pw-container** does
|
||||
not exit until terminated with a signal. It exits with status 0 if terminated by
|
||||
SIGINT or SIGTERM in this case.
|
||||
|
||||
Otherwise, it exits with nonzero exit status.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**pw-container** 'pw-dump i 0'
|
||||
|
||||
Run pw-dump of the Core object. Note the difference in the object permissions
|
||||
when running pw-dump with and without **pw-container**.
|
||||
|
||||
**pw-container** 'pw-dump pw-dump'
|
||||
|
||||
Run pw-dump of itself. Note the difference in the Client security tokens when
|
||||
running pw-dump with and without **pw-container**.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
[1] https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/staging/security-context/security-context-v1.xml - Creating a security context
|
56
doc/dox/programs/pw-dot.1.md
Normal file
56
doc/dox/programs/pw-dot.1.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
\page page_man_pw-dot_1 pw-dot
|
||||
|
||||
The PipeWire dot graph dump
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-dot** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Create a .dot file of the PipeWire graph.
|
||||
|
||||
The .dot file can then be visualized with a tool like **dotty** or
|
||||
rendered to a PNG file with `dot -Tpng pw.dot -o pw.png`.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the remote instance to connect to. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -a | \--all
|
||||
Show all object types.
|
||||
|
||||
\par -s | \--smart
|
||||
Show linked objects only.
|
||||
|
||||
\par -d | \--detail
|
||||
Show all object properties.
|
||||
|
||||
\par -o FILE | \--output=FILE
|
||||
Output file name (Default pw.dot). Use - for stdout.
|
||||
|
||||
\par -L | \--lr
|
||||
Lay the graph from left to right, instead of dot's default top to
|
||||
bottom.
|
||||
|
||||
\par -9 | \--90
|
||||
Lay the graph using 90-degree angles in edges.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-cli_1 "pw-cli(1)",
|
||||
\ref page_man_pw-mon_1 "pw-mon(1)",
|
43
doc/dox/programs/pw-dump.1.md
Normal file
43
doc/dox/programs/pw-dump.1.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
\page page_man_pw-dump_1 pw-dump
|
||||
|
||||
The PipeWire state dumper
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-dump** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *pw-dump* program produces a representation of the current PipeWire
|
||||
state as JSON, including the information on nodes, devices, modules,
|
||||
ports, and other objects.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name of the *remote* instance to dump. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -m | \--monitor
|
||||
Monitor PipeWire state changes, and output JSON arrays describing
|
||||
changes.
|
||||
|
||||
\par -N | \--no-colors
|
||||
Disable color output.
|
||||
|
||||
\par -C | \--color=WHEN
|
||||
Whether to enable color support. WHEN is `never`, `always`, or `auto`.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-cli_1 "pw-cli(1)",
|
||||
\ref page_man_pw-top_1 "pw-top(1)",
|
45
doc/dox/programs/pw-jack.1.md
Normal file
45
doc/dox/programs/pw-jack.1.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
\page page_man_pw-jack_1 pw-jack
|
||||
|
||||
Use PipeWire instead of JACK
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-jack** \[*options*\] *COMMAND* \[*ARGUMENTS...*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
**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.
|
||||
|
||||
If PipeWire's reimplementation of the JACK client libraries has been
|
||||
installed as a system-wide replacement for JACK's own libraries, then
|
||||
the whole system already behaves in that way, in which case **pw-jack**
|
||||
has no practical effect.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h
|
||||
Show help.
|
||||
|
||||
\par -r NAME
|
||||
The name of the remote instance to connect to. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -v
|
||||
Verbose operation.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
\par pw-jack sndfile-jackplay /usr/share/sounds/freedesktop/stereo/bell.oga
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
**jackd(1)**,
|
135
doc/dox/programs/pw-link.1.md
Normal file
135
doc/dox/programs/pw-link.1.md
Normal file
|
@ -0,0 +1,135 @@
|
|||
\page page_man_pw-link_1 pw-link
|
||||
|
||||
The PipeWire Link Command
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-link** \[*options*\] -o-l \[*out-pattern*\] \[*in-pattern*\]
|
||||
|
||||
**pw-link** \[*options*\] *output* *input*
|
||||
|
||||
**pw-link** \[*options*\] -d *output* *input*
|
||||
|
||||
**pw-link** \[*options*\] -d *link-id*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
List, create and destroy links between PipeWire ports.
|
||||
|
||||
# COMMON OPTIONS
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the *remote* instance to monitor. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
# LISTING PORTS AND LINKS
|
||||
|
||||
Specify one of -o, -i or -l to list the matching optional input and
|
||||
output ports and their links.
|
||||
|
||||
\par -o | \--output
|
||||
List output ports
|
||||
|
||||
\par -i | \--input
|
||||
List input ports
|
||||
|
||||
\par -l | \--links
|
||||
List links
|
||||
|
||||
\par -m | \--monitor
|
||||
Monitor links and ports. **pw-link** will not exit but monitor and print
|
||||
new and destroyed ports or links.
|
||||
|
||||
\par -I | \--id
|
||||
List IDs. Also list the unique link and port ids.
|
||||
|
||||
\par -v | \--verbose
|
||||
Verbose port properties. Also list the port-object-path and the
|
||||
port-alias.
|
||||
|
||||
# CONNECTING PORTS
|
||||
|
||||
Without any list option (-i, -o or -l), the given ports will be linked.
|
||||
Valid port specifications are:
|
||||
|
||||
*port-id*
|
||||
|
||||
As obtained with the -I option when listing ports.
|
||||
|
||||
*node-name:port-name*
|
||||
|
||||
As obtained when listing ports.
|
||||
|
||||
*port-object-path*
|
||||
|
||||
As obtained from the first alternative name for the port when listing
|
||||
them with the -v option.
|
||||
|
||||
*port-alias*
|
||||
|
||||
As obtained from the second alternative name for the ports when listing
|
||||
them with the -v option.
|
||||
|
||||
Extra options when linking can be given:
|
||||
|
||||
\par -L | \--linger
|
||||
Linger. Will create a link that exists after **pw-link** is destroyed.
|
||||
This is the default behaviour, unless the -m option is given.
|
||||
|
||||
\par -P | \--passive
|
||||
Passive link. A passive link will keep both nodes it links inactive
|
||||
unless another non-passive link is activating the nodes. You can use
|
||||
this to link a sink to a filter and have them both suspended when
|
||||
nothing else is linked to either of them.
|
||||
|
||||
\par -p | \--props=PROPS
|
||||
Properties as JSON object. Give extra properties when creaing the link.
|
||||
|
||||
# DISCONNECTING PORTS
|
||||
|
||||
When the -d option is given, an existing link between port is destroyed.
|
||||
|
||||
To disconnect port, a single *link-id*, as obtained when listing links
|
||||
with the -I option, or two port specifications can be given. See the
|
||||
connecting ports section for valid port specifications.
|
||||
|
||||
\par -d | \--disconnect
|
||||
Disconnect ports
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**pw-link** -iol
|
||||
|
||||
List all port and their links.
|
||||
|
||||
**pw-link** -lm
|
||||
|
||||
List all links and monitor changes until **pw-link** is stopped.
|
||||
|
||||
**pw-link** paplay:output_FL alsa_output.pci-0000_00_1b.0.analog-stereo:playback_FL
|
||||
|
||||
Link the given output port to the input port.
|
||||
|
||||
**pw-link** -lI
|
||||
|
||||
List links and their Id.
|
||||
|
||||
**pw-link** -d 89
|
||||
|
||||
Destroy the link with id 89.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-cli_1 "pw-cli(1)"
|
67
doc/dox/programs/pw-loopback.1.md
Normal file
67
doc/dox/programs/pw-loopback.1.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
\page page_man_pw-loopback_1 pw-loopback
|
||||
|
||||
PipeWire loopback client
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-loopback** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *pw-loopback* program is a PipeWire client that uses the PipeWire
|
||||
loopback module to create loopback nodes, with configuration given via
|
||||
the command-line options.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name of the *remote* instance to connect to. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -n | \--name=NAME
|
||||
Name of the loopback node
|
||||
|
||||
\par -g | \--group=NAME
|
||||
Name of the loopback node group
|
||||
|
||||
\par -c | \--channels=NUMBER
|
||||
Number of channels to provide
|
||||
|
||||
\par -m | \--channel-map=MAP
|
||||
Channel map (default `[ FL, FR ]`)
|
||||
|
||||
\par -l | \--latency=LATENCY
|
||||
Desired latency in ms
|
||||
|
||||
\par -d | \--delay=DELAY
|
||||
Added delay in seconds (floating point allowed)
|
||||
|
||||
\par -C | \--capture=TARGET
|
||||
Target device to capture from
|
||||
|
||||
\par -P | \--playback=TARGET
|
||||
Target device to play to
|
||||
|
||||
\par -i | \--capture-props=PROPS
|
||||
Wanted properties of capture node (in JSON)
|
||||
|
||||
\par -o | \--playback-props=PROPS
|
||||
Wanted properties of capture node (in JSON)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-cat_1 "pw-cat(1)",
|
||||
**pactl(1)**
|
||||
|
||||
Other ways to create loopback nodes are adding the loopback module in
|
||||
the configuration of a PipeWire daemon, or loading the loopback module
|
||||
using Pulseaudio commands (`pactl load-module module-loopback ...`).
|
73
doc/dox/programs/pw-metadata.1.md
Normal file
73
doc/dox/programs/pw-metadata.1.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
\page page_man_pw-metadata_1 pw-metadata
|
||||
|
||||
The PipeWire metadata
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-metadata** \[*options*\] \[*id* \[*key* \[*value* \[*type* \] \] \] \]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Monitor, set and delete metadata on PipeWire objects.
|
||||
|
||||
Metadata are key/type/value triplets attached to objects identified by
|
||||
*id*. The metadata is shared between all applications binding to the
|
||||
same metadata object. When an object is destroyed, all its metadata is
|
||||
automatically removed.
|
||||
|
||||
When no *value* is given, **pw-metadata** will query and log the
|
||||
metadata matching the optional arguments *id* and *key*. Without any
|
||||
arguments, all metadata is displayed.
|
||||
|
||||
When *value* is given, **pw-metadata** will set the metadata for *id*
|
||||
and *key* to *value* and an optional *type*.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the remote instance to use. If left unspecified, a connection
|
||||
is made to the default PipeWire instance.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -l | \--list
|
||||
List available metadata objects
|
||||
|
||||
\par -m | \--monitor
|
||||
Keeps running and log the changes to the metadata.
|
||||
|
||||
\par -d | \--delete
|
||||
Delete all metadata for *id* or for the specified *key* of object *id*.
|
||||
Without any option, all metadata is removed.
|
||||
|
||||
\par -n | \--name
|
||||
Metadata name (Default: "default").
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**pw-metadata**
|
||||
|
||||
Show metadata in default name.
|
||||
|
||||
**pw-metadata** -n settings 0
|
||||
|
||||
Display settings.
|
||||
|
||||
**pw-metadata** -n settings 0 clock.quantum 1024
|
||||
|
||||
Change clock.quantum to 1024.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-mon_1 "pw-mon(1)",
|
||||
\ref page_man_pw-cli_1 "pw-cli(1)",
|
38
doc/dox/programs/pw-mididump.1.md
Normal file
38
doc/dox/programs/pw-mididump.1.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
\page page_man_pw-mididump_1 pw-mididump
|
||||
|
||||
The PipeWire MIDI dump
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-mididump** \[*options*\] \[*FILE*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Dump MIDI messages to stdout.
|
||||
|
||||
When a MIDI file is given, the events inside the file are printed.
|
||||
|
||||
When no file is given, **pw-mididump** creates a PipeWire MIDI input
|
||||
stream and will print all MIDI events received on the port to stdout.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the remote instance to monitor. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-cat_1 "pw-cat(1)"
|
36
doc/dox/programs/pw-mon.1.md
Normal file
36
doc/dox/programs/pw-mon.1.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
\page page_man_pw-mon_1 pw-mon
|
||||
|
||||
The PipeWire monitor
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-mon** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Monitor objects on the PipeWire instance.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the *remote* instance to monitor. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -N | \--color=WHEN
|
||||
Whether to use color, one of 'never', 'always', or 'auto'. The default
|
||||
is 'auto'. **-N** is equivalent to **--color=never**.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)"
|
46
doc/dox/programs/pw-profiler.1.md
Normal file
46
doc/dox/programs/pw-profiler.1.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
\page page_man_pw-profiler_1 pw-profiler
|
||||
|
||||
The PipeWire profiler
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-profiler** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Start profiling a PipeWire instance.
|
||||
|
||||
If the server has the profiler module loaded, this program will connect
|
||||
to it and log the profiler data. Profiler data contains times and
|
||||
durations when processing nodes and devices started and completed.
|
||||
|
||||
When this program is stopped, a set of **gnuplot** files and a script to
|
||||
generate SVG files from the .plot files is generated, along with a .html
|
||||
file to visualize the profiling results in a browser.
|
||||
|
||||
This function uses the same data used by *pw-top*.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the remote instance to monitor. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
\par -o | \--output=FILE
|
||||
Profiler output name (default "profiler.log").
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-top_1 "pw-top(1)"
|
79
doc/dox/programs/pw-reserve.1.md
Normal file
79
doc/dox/programs/pw-reserve.1.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
\page page_man_pw-reserve_1 pw-reserve
|
||||
|
||||
The PipeWire device reservation utility
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-reserve** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Reserves a device using the DBus `org.freedesktop.ReserveDevice1`
|
||||
device reservation scheme [1], waiting until terminated by `SIGINT` or
|
||||
another signal.
|
||||
|
||||
It can also request other applications to release a device. This can
|
||||
be used to make audio servers such as PipeWire, Pulseaudio, JACK, or
|
||||
other applications that respect the device reservation protocol, to
|
||||
ignore a device, or to release a sound device they are already using
|
||||
so that it can be used by other applications.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -r | \--release
|
||||
Request any client currently holding the device to release it, and try
|
||||
to reserve it after that. If this option is not given and the device
|
||||
is already in use, **pw-reserve** will exit with error status.
|
||||
|
||||
\par -n NAME | \--name=NAME
|
||||
\parblock
|
||||
Name of the device to reserve. By convention, this is
|
||||
|
||||
- Audio<em>N</em>: for ALSA card number <em>N</em>
|
||||
|
||||
**pw-reserve** can reserve any device name, however PipeWire does
|
||||
not currently support other values than listed above.
|
||||
\endparblock
|
||||
|
||||
\par -a NAME | \--appname=NAME
|
||||
Application name to use when reserving the device.
|
||||
|
||||
\par -p PRIO | \--priority=PRIO
|
||||
Priority to use when reserving the device.
|
||||
|
||||
\par -m | \--monitor
|
||||
Monitor reservations of a given device, instead of reserving it.
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par \--version
|
||||
Show version information.
|
||||
|
||||
# EXIT STATUS
|
||||
|
||||
If the device reservation succeeds, **pw-reserve** does not exit until
|
||||
terminated with a signal. It exits with status 0 if terminated by
|
||||
SIGINT or SIGTERM in this case.
|
||||
|
||||
Otherwise, it exits with nonzero exit status.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**pw-reserve** -n Audio0
|
||||
|
||||
Reserve ALSA card 0, and exit with error if it is already reserved.
|
||||
|
||||
**pw-reserve** -n Audio0 -r
|
||||
|
||||
Reserve ALSA card 0, requesting any applications that have reserved
|
||||
the device to release it for us.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
[1] https://git.0pointer.net/reserve.git/tree/reserve.txt - A simple device reservation scheme with DBus
|
209
doc/dox/programs/pw-top.1.md
Normal file
209
doc/dox/programs/pw-top.1.md
Normal file
|
@ -0,0 +1,209 @@
|
|||
\page page_man_pw-top_1 pw-top
|
||||
|
||||
The PipeWire process viewer
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-top** \[*options*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *pw-top* program provides a dynamic real-time view of the pipewire
|
||||
node and device statistics.
|
||||
|
||||
A hierarchical view is shown of Driver nodes and follower nodes. The
|
||||
Driver nodes are actively using a timer to schedule dataflow in the
|
||||
followers. The followers of a driver node as shown below their driver
|
||||
with a + sign in a tree-like representation.
|
||||
|
||||
The columns presented are as follows:
|
||||
|
||||
\par S
|
||||
\parblock
|
||||
Node status.
|
||||
|
||||
- E = ERROR
|
||||
- C = CREATING
|
||||
- S = SUSPENDED
|
||||
- I = IDLE
|
||||
- R = RUNNING
|
||||
- t = RUNNING + transport starting
|
||||
- T = RUNNING + transport running
|
||||
\endparblock
|
||||
|
||||
\par ID
|
||||
The ID of the pipewire node/device, as found in *pw-dump* and
|
||||
*pw-cli*
|
||||
|
||||
\par QUANT
|
||||
\parblock
|
||||
The current quantum (for drivers) and the suggested quantum for
|
||||
follower nodes.
|
||||
|
||||
The quantum by itself needs to be divided by the RATE column to
|
||||
calculate the duration of a scheduling period in fractions of a
|
||||
second.
|
||||
|
||||
For a QUANT of 1024 and a RATE of 48000, the duration of one period
|
||||
in the graph is 1024/48000 or 21.3 milliseconds.
|
||||
|
||||
Follower nodes can have a 0 QUANT field, which means that the node
|
||||
does not have a suggestion for the quantum and thus uses what the
|
||||
driver selected.
|
||||
|
||||
The driver will use the lowest quantum of any of the followers. If
|
||||
none of the followers select a quantum, the default quantum in the
|
||||
pipewire configuration file will be used.
|
||||
|
||||
The QUANT on the drivers usually translates directly into the number
|
||||
of audio samples processed per processing cycle of the graph.
|
||||
|
||||
See also
|
||||
<https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>
|
||||
\endparblock
|
||||
|
||||
\par RATE
|
||||
\parblock
|
||||
The current rate (for drivers) and the suggested rate for follower
|
||||
nodes.
|
||||
|
||||
This is the rate at which the *graph* processes data and needs to be
|
||||
combined with the QUANT value to derive the duration of a processing
|
||||
cycle in the graph.
|
||||
|
||||
Some nodes can have a 0 RATE, which means that they don\'t have any
|
||||
rate suggestion for the graph. Nodes that suggest a rate can make
|
||||
the graph switch rates if the graph is otherwise idle and the new
|
||||
rate is allowed as a possible graph rate (see the pipewire
|
||||
configuration file).
|
||||
|
||||
The RATE on (audio) driver nodes usually also translates directly to
|
||||
the samplerate used by the device. Although some devices might not
|
||||
be able to operate at the given samplerate, in which case resampling
|
||||
will need to be done. The negotiated samplerate with the device and
|
||||
stream can be found in the FORMAT column.
|
||||
|
||||
\endparblock
|
||||
|
||||
\par WAIT
|
||||
\parblock
|
||||
The waiting time of a node is the elapsed time between when the node
|
||||
is ready to start processing and when it actually started
|
||||
processing.
|
||||
|
||||
For Driver nodes, this is the time between when the node wakes up to
|
||||
start processing the graph and when the driver (and thus also the
|
||||
graph) completes a cycle. The WAIT time for driver is thus the
|
||||
elapsed time processing the graph.
|
||||
|
||||
For follower nodes, it is the time spent between being woken up
|
||||
(when all dependencies of the node are satisfied) and when
|
||||
processing starts. The WAIT time for follower nodes is thus mostly
|
||||
caused by context switching.
|
||||
|
||||
A value of \-\-- means that the node was not signaled. A value of
|
||||
+++ means that the node was signaled but not awake.
|
||||
\endparblock
|
||||
|
||||
\par BUSY
|
||||
\parblock
|
||||
The processing time is started when the node starts processing until
|
||||
it completes and wakes up the next nodes in the graph.
|
||||
|
||||
A value of \-\-- means that the node was not started. A value of +++
|
||||
means that the node was started but did not complete.
|
||||
\endparblock
|
||||
|
||||
\par W/Q
|
||||
\parblock
|
||||
Ratio of WAIT / QUANT.
|
||||
|
||||
The W/Q time of the driver node is a good measure of the graph load.
|
||||
The running averages of the driver W/Q ratios are used as the DSP
|
||||
load in other (JACK) tools.
|
||||
|
||||
Values of \-\-- and +++ are copied from the WAIT column.
|
||||
|
||||
\endparblock
|
||||
|
||||
\par B/Q
|
||||
\parblock
|
||||
Ratio of BUSY / QUANT
|
||||
|
||||
This is a good measure of the load of a particular driver or
|
||||
follower node.
|
||||
|
||||
Values of \-\-- and +++ are copied from the BUSY column.
|
||||
\endparblock
|
||||
|
||||
\par ERR
|
||||
\parblock
|
||||
Total of Xruns and Errors
|
||||
|
||||
Xruns for drivers are when the graph did not complete a cycle. This
|
||||
can be because a node in the graph also has an Xrun. It can also be
|
||||
caused when scheduling delays cause a deadline to be missed, causing
|
||||
a hardware Xrun.
|
||||
|
||||
Xruns for followers are incremented when the node started processing
|
||||
but did not complete before the end of the graph cycle deadline.
|
||||
\endparblock
|
||||
|
||||
\par FORMAT
|
||||
\parblock
|
||||
The format used by the driver node or the stream. This is the
|
||||
hardware format negotiated with the device or stream.
|
||||
|
||||
If the stream of driver has a different rate than the graph,
|
||||
resampling will be done.
|
||||
|
||||
For raw audio formats, the layout is \<sampleformat\> \<channels\>
|
||||
\<samplerate\>.
|
||||
|
||||
For IEC958 passthrough audio formats, the layout is IEC958 \<codec\>
|
||||
\<samplerate\>.
|
||||
|
||||
For DSD formats, the layout is \<dsd-rate\> \<channels\>.
|
||||
|
||||
For Video formats, the layout is \<pixelformat\>
|
||||
\<width\>x\<height\>.
|
||||
\endparblock
|
||||
|
||||
\par NAME
|
||||
\parblock
|
||||
Name assigned to the device/node, as found in *pw-dump* node.name
|
||||
|
||||
Names are prefixed by *+* when they are linked to a driver (entry
|
||||
above with no +)
|
||||
\endparblock
|
||||
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help.
|
||||
|
||||
\par -b | \--batch-mode
|
||||
Run in non-interactive batch mode, similar to top\'s batch mode.
|
||||
|
||||
\par -n | \--iterations=NUMBER
|
||||
Exit after NUMBER of batch iterations. Only used in batch mode.
|
||||
|
||||
\par -r | \--remote=NAME
|
||||
The name the *remote* instance to monitor. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -V | \--version
|
||||
Show version information.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
||||
\ref page_man_pw-dump_1 "pw-dump(1)",
|
||||
\ref page_man_pw-cli_1 "pw-cli(1)",
|
||||
\ref page_man_pw-profiler_1 "pw-profiler(1)"
|
40
doc/dox/programs/pw-v4l2.1.md
Normal file
40
doc/dox/programs/pw-v4l2.1.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
\page page_man_pw-v4l2_1 pw-v4l2
|
||||
|
||||
Use PipeWire instead of V4L2
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**pw-v4l2** \[*options*\] *COMMAND* \[*ARGUMENTS...*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
**pw-v4l2** runs a command using a compatibility layer that maps PipeWire
|
||||
video devices to be visible to applications using V4L2.
|
||||
|
||||
This is implemented by preloading a shared library via LD_PRELOAD,
|
||||
which translates library calls that try to access V4L2 devices.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h
|
||||
Show help.
|
||||
|
||||
\par -r NAME
|
||||
The name of the remote instance to connect to. If left unspecified, a
|
||||
connection is made to the default PipeWire instance.
|
||||
|
||||
\par -v
|
||||
Verbose operation.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**pw-v4l2** v4l2-ctl --list-devices
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)",
|
94
doc/dox/programs/spa-acp-tool.1.md
Normal file
94
doc/dox/programs/spa-acp-tool.1.md
Normal file
|
@ -0,0 +1,94 @@
|
|||
\page page_man_spa-acp-tool_1 spa-acp-tool
|
||||
|
||||
The PipeWire ALSA profile debugging utility
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**spa-acp-tool** \[*OPTIONS*\] \[*COMMAND*\]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Debug tool for exercising the ALSA card profile probing code, without
|
||||
running PipeWire.
|
||||
|
||||
May be used to debug problems where PipeWire has incorrectly
|
||||
functioning ALSA card profiles.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -h | \--help
|
||||
Show help
|
||||
|
||||
\par -v | \--verbose
|
||||
Increase verbosity by one level
|
||||
|
||||
\par -c NUMBER | \--card NUMBER
|
||||
Select which card to probe
|
||||
|
||||
\par -p | \--properties
|
||||
Additional properties to pass to ACP, e.g. `key=value ...`.
|
||||
|
||||
# COMMANDS
|
||||
|
||||
\par help | h
|
||||
Show available commands
|
||||
|
||||
\par quit | q
|
||||
Quit
|
||||
|
||||
\par card ID | c ID
|
||||
Probe card
|
||||
|
||||
\par info | i
|
||||
List card info
|
||||
|
||||
\par list | l
|
||||
List all objects
|
||||
|
||||
\par list-verbose | lv
|
||||
List all data
|
||||
|
||||
\par list-profiles [ID] | lpr [ID]
|
||||
List profiles
|
||||
|
||||
\par set-profile ID | spr ID
|
||||
Activate a profile
|
||||
|
||||
\par list-ports [ID] | lp [ID]
|
||||
List ports
|
||||
|
||||
\par set-port ID | sp ID
|
||||
Activate a port
|
||||
|
||||
\par list-devices [ID] | ld [ID]
|
||||
List available devices
|
||||
|
||||
\par get-volume ID | gv ID
|
||||
Get volume from device
|
||||
|
||||
\par set-volume ID VOL | v ID VOL
|
||||
Set volume on device
|
||||
|
||||
\par inc-volume ID | v+ ID
|
||||
Increase volume on device
|
||||
|
||||
\par dec-volume ID | v- ID
|
||||
Decrease volume on device
|
||||
|
||||
\par get-mute ID | gm ID
|
||||
Get mute state from device
|
||||
|
||||
\par set-mute ID VAL | sm ID VAL
|
||||
Set mute on device
|
||||
|
||||
\par toggle-mute ID | m ID
|
||||
Toggle mute on device
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)"
|
28
doc/dox/programs/spa-inspect.1.md
Normal file
28
doc/dox/programs/spa-inspect.1.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
\page page_man_spa-inspect_1 spa-inspect
|
||||
|
||||
The PipeWire SPA plugin information utility
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**spa-inspect** *FILE*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Displays information about a SPA plugin.
|
||||
|
||||
Lists the SPA factories contained, and tries to instantiate them.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**spa-inspect** $(SPA_PLUGINDIR)/bluez5/libspa-codec-bluez5-sbc.so
|
||||
|
||||
Display information about a plugin.
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)"
|
24
doc/dox/programs/spa-json-dump.1.md
Normal file
24
doc/dox/programs/spa-json-dump.1.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
\page page_man_spa-json-dump_1 spa-json-dump
|
||||
|
||||
SPA JSON to JSON converter
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**spa-json** *[FILE]*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Reads a SPA JSON file or stdin, and outputs it as standard JSON.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**spa-json-dump** $(PIPEWIRE_CONFDATADIR)/pipewire.conf
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)"
|
26
doc/dox/programs/spa-monitor.1.md
Normal file
26
doc/dox/programs/spa-monitor.1.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
\page page_man_spa-monitor_1 spa-monitor
|
||||
|
||||
The PipeWire SPA device debugging utility
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**spa-monitor** *FILE*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Load a SPA plugin and instantiate a device from it.
|
||||
|
||||
This is only useful for debugging device plugins.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**spa-monitor** $(SPA_PLUGINDIR)/jack/libspa-jack.so
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)"
|
47
doc/dox/programs/spa-resample.1.md
Normal file
47
doc/dox/programs/spa-resample.1.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
\page page_man_spa-resample_1 spa-resample
|
||||
|
||||
The PipeWire resampler debugging utility
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**spa-resample** \[*OPTIONS*\] *INFILE* *OUTFILE*
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Use the PipeWire resampler to resample input file to output file,
|
||||
following the given options.
|
||||
|
||||
This is useful only for testing the resampler.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
\par -r RATE | \--rate=RATE
|
||||
Output sample rate.
|
||||
|
||||
\par -f FORMAT | \--format=FORMAT
|
||||
Output sample format (s8 | s16 | s32 | f32 | f64).
|
||||
|
||||
\par -q QUALITY | \--quality=QUALITY
|
||||
Resampler output quality (0-14).
|
||||
|
||||
\par -c FLAGS | \--cpuflags=FLAGS
|
||||
See \ref spa_cpu "spa/support/cpu.h".
|
||||
|
||||
\par -h
|
||||
Show help.
|
||||
|
||||
\par -v
|
||||
Verbose operation.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
**spa-resample** -r 48000 -f s32 in.wav out.wav
|
||||
|
||||
# AUTHORS
|
||||
|
||||
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
|
||||
PipeWire is available from <$(PACKAGE_URL)>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
\ref page_man_pipewire_1 "pipewire(1)"
|
42
doc/dox/pulse-modules.dox
Normal file
42
doc/dox/pulse-modules.dox
Normal file
|
@ -0,0 +1,42 @@
|
|||
/** \page page_pulse_modules Pulseaudio Modules
|
||||
|
||||
\include{doc} pulse-modules.inc
|
||||
|
||||
# List of built-in modules:
|
||||
|
||||
- \subpage page_pulse_module_alsa_sink
|
||||
- \subpage page_pulse_module_alsa_source
|
||||
- \subpage page_pulse_module_always_sink
|
||||
- \subpage page_pulse_module_combine_sink
|
||||
- \subpage page_pulse_module_device_manager
|
||||
- \subpage page_pulse_module_device_restore
|
||||
- \subpage page_pulse_module_echo_cancel
|
||||
- \subpage page_pulse_module_gsettings
|
||||
- \subpage page_pulse_module_jackdbus_detect
|
||||
- \subpage page_pulse_module_ladspa_sink
|
||||
- \subpage page_pulse_module_ladspa_source
|
||||
- \subpage page_pulse_module_loopback
|
||||
- \subpage page_pulse_module_native_protocol_tcp
|
||||
- \subpage page_pulse_module_null_sink
|
||||
- \subpage page_pulse_module_pipe_sink
|
||||
- \subpage page_pulse_module_pipe_source
|
||||
- \subpage page_pulse_module_raop_discover
|
||||
- \subpage page_pulse_module_remap_sink
|
||||
- \subpage page_pulse_module_remap_source
|
||||
- \subpage page_pulse_module_roc_sink
|
||||
- \subpage page_pulse_module_roc_sink_input
|
||||
- \subpage page_pulse_module_roc_source
|
||||
- \subpage page_pulse_module_rtp_recv
|
||||
- \subpage page_pulse_module_rtp_send
|
||||
- \subpage page_pulse_module_simple_protocol_tcp
|
||||
- \subpage page_pulse_module_stream_restore
|
||||
- \subpage page_pulse_module_switch_on_connect
|
||||
- \subpage page_pulse_module_tunnel_sink
|
||||
- \subpage page_pulse_module_tunnel_source
|
||||
- \subpage page_pulse_module_virtual_sink
|
||||
- \subpage page_pulse_module_virtual_source
|
||||
- \subpage page_pulse_module_x11_bell
|
||||
- \subpage page_pulse_module_zeroconf_discover
|
||||
- \subpage page_pulse_module_zeroconf_publish
|
||||
|
||||
*/
|
113
doc/dox/pulse-modules.inc
Normal file
113
doc/dox/pulse-modules.inc
Normal file
|
@ -0,0 +1,113 @@
|
|||
PipeWire's Pulseaudio emulation implements several Pulseaudio modules.
|
||||
It only supports its own built-in modules, and cannot load external
|
||||
modules written for Pulseaudio.
|
||||
|
||||
# Loading modules
|
||||
|
||||
The built-in modules can be loaded using Pulseaudio client programs,
|
||||
for example `pactl load-module <module-name> <module-options>`. They
|
||||
can also added to `pipewire-pulse.conf`, typically by a drop-in file
|
||||
in `~/.config/pipewire/pipewire-pulse.conf.d/` containing the module
|
||||
name and its arguments
|
||||
```
|
||||
# ~/.config/pipewire/pipewire-pulse.conf.d/custom.conf
|
||||
|
||||
pulse.cmd = [
|
||||
{ cmd = "load-module" args = "module-null-sink sink_name=foo" flags = [ ] }
|
||||
]
|
||||
```
|
||||
|
||||
To list all modules currently loaded, with their arguments:
|
||||
```
|
||||
pactl list modules
|
||||
```
|
||||
|
||||
For a short list of loaded modules:
|
||||
```
|
||||
pactl list modules short
|
||||
```
|
||||
|
||||
Modules may be unloaded using either the module-name or index number:
|
||||
|
||||
```
|
||||
pactl load-module <module-name> <parameters>
|
||||
pactl unload-module <module-name|index#>
|
||||
```
|
||||
|
||||
# Common module options
|
||||
|
||||
Most modules that create streams/devices support the following properties:
|
||||
|
||||
## sink_name, source_name
|
||||
|
||||
Name for the sink (resp. source). Allowed characters in the name are a-z, A-Z, numbers, period (.) and underscore (_). The length must be 1-128 characters.
|
||||
|
||||
## format
|
||||
|
||||
The sample format. The supported audio formats are:
|
||||
|
||||
### PCM
|
||||
- u8: unsigned 8-bit integer
|
||||
- aLaw: A-law encoded 8-bit integer
|
||||
- uLaw: μ-law encoded 8-bit integer
|
||||
- s16le: signed 16-bit little-endian integer
|
||||
- s16be: signed 16-bit big-endian integer
|
||||
- s16, s16ne: native-endian aliases for s16le or s16be
|
||||
- s16re: reverse-endian alias for s16le or s16be
|
||||
- float32le: 32-bit little-endian float
|
||||
- float32be: 32-bit big-endian float
|
||||
- float32, float32ne: native-endian aliases for float32le or float32be
|
||||
- float32re: reverse-endian alias for float32le or float32be
|
||||
- s32le: signed 32-bit little-endian integer
|
||||
- s32be: signed 32-bit big-endian integer
|
||||
- s32, s32ne: native-endian aliases for s32le or s32be
|
||||
- s32re: reverse-endian alias for s32le or s32be
|
||||
- s24le: signed 24-bit little-endian integer (note: ALSA calls this "S24_3LE")
|
||||
- s24be: signed 24-bit big-endian integer (note: ALSA calls this "S24_3BE")
|
||||
- s24, s24ne: native-endian aliases for s24le or s24be
|
||||
- s24re: reverse-endian alias for s24le or s24be
|
||||
- s24-32le: signed 24-bit little-endian integer, packed into a 32-bit integer so that the 8 most significant bits are ignored (note: ALSA calls this "S24_LE")
|
||||
- s24-32be: signed 24-bit big-endian integer, packed into a 32-bit integer so that the 8 most significant bits are ignored (note: ALSA calls this "S24_BE")
|
||||
- s24-32, s24-32ne: native-endian aliases for s24-32le or s24-32be
|
||||
- s24-32re: reverse-endian alias for s24-32le or s24-32be
|
||||
|
||||
### Compressed audio formats
|
||||
|
||||
Below is a list of all supported compressed formats. The code at the beginning of each line is used whenever a textual identifier for a format is needed (for example in configuration files or on the command line). The formats whose identifier ends with -iec61937 have to be wrapped in IEC 61937 frames, which makes the compressed audio behave more like normal PCM audio.
|
||||
|
||||
- ac3-iec61937: Dolby Digital (DD / AC-3 / A/52)
|
||||
- eac3-iec61937: Dolby Digital Plus (DD+ / E-AC-3)
|
||||
- mpeg-iec61937: MPEG-1 or MPEG-2 Part 3 (not MPEG-2 AAC)
|
||||
- dts-iec61937: DTS
|
||||
- mpeg2-aac-iec61937: MPEG-2 AAC (supported since PulseAudio 4.0)
|
||||
- truehd-iec61937: Dolby TrueHD (added in PulseAudio 13.0, but doesn't work yet in practice)
|
||||
- dtshd-iec61937: DTS-HD Master Audio (added in PulseAudio 13.0, but doesn't work yet in practice)
|
||||
- pcm: PCM (not a compressed format, but listed here, because pcm is one of the recognized encoding identifiers)
|
||||
- any: (special identifier for indicating that any encoding can be used)
|
||||
|
||||
|
||||
## rate
|
||||
|
||||
The sample rate.
|
||||
|
||||
##channels
|
||||
|
||||
Number of audio channels.
|
||||
|
||||
## channel_map
|
||||
|
||||
A channel map. A list of comma-separated channel names. The currently defined channel names are:
|
||||
`left`, `right`, `mono`, `center`, `front-left`, `front-right`, `front-center`,
|
||||
`rear-center`, `rear-left`, `rear-right`, `lfe`, `subwoofer`, `front-left-of-center`,
|
||||
`front-right-of-center`, `side-left`, `side-right`, `aux0`, `aux1` to `aux15`, `top-center`,
|
||||
`top-front-left`, `top-front-right`, `top-front-center`, `top-rear-left`, `top-rear-right`,
|
||||
`top-rear-center`
|
||||
|
||||
## sink_properties, source_properties
|
||||
|
||||
Set additional properties of the sink/source. For example, you can set the description directly
|
||||
when the module is loaded by setting this parameter.
|
||||
|
||||
```
|
||||
load-module module-alsa-sink sink_name=headphones sink_properties=device.description=Headphones
|
||||
```
|
21
doc/dox/tutorial/index.dox
Normal file
21
doc/dox/tutorial/index.dox
Normal file
|
@ -0,0 +1,21 @@
|
|||
/** \page page_tutorial API Tutorial
|
||||
|
||||
Welcome to the PipeWire API tutorial. The goal is to learn
|
||||
PipeWire API step-by-step with simple short examples.
|
||||
|
||||
- \subpage page_tutorial1
|
||||
- \subpage page_tutorial2
|
||||
- \subpage page_tutorial3
|
||||
- \subpage page_tutorial4
|
||||
- \subpage page_tutorial5
|
||||
- \subpage page_tutorial6
|
||||
|
||||
|
||||
# More Example Programs
|
||||
|
||||
- \ref audio-src.c "": \snippet{doc} audio-src.c title
|
||||
- \ref audio-dsp-filter.c "": \snippet{doc} audio-dsp-filter.c title
|
||||
- \ref video-play.c "": \snippet{doc} video-play.c title
|
||||
- \subpage page_examples
|
||||
|
||||
*/
|
47
doc/dox/tutorial/tutorial1.dox
Normal file
47
doc/dox/tutorial/tutorial1.dox
Normal file
|
@ -0,0 +1,47 @@
|
|||
/** \page page_tutorial1 Tutorial - Part 1: Getting Started
|
||||
|
||||
|
||||
\ref page_tutorial "Index" | \ref page_tutorial2
|
||||
|
||||
In this tutorial we show the basics of a simple PipeWire application.
|
||||
Use this tutorial to get started and help you set up your development
|
||||
environment.
|
||||
|
||||
|
||||
# Initialization
|
||||
|
||||
Let get started with the simplest application.
|
||||
|
||||
\snippet tutorial1.c code
|
||||
|
||||
Before you can use any PipeWire functions, you need to call `pw_init()`.
|
||||
|
||||
|
||||
# Compilation
|
||||
|
||||
PipeWire provides a pkg-config file named `libpipewire-0.3` (note: the version
|
||||
suffix may change with future releases of PipeWire).
|
||||
To compile the simple test application, copy it into a test1.c file and
|
||||
use pkg-config to provide the required dependencies:
|
||||
|
||||
gcc -Wall test1.c -o test1 $(pkg-config --cflags --libs libpipewire-0.3)
|
||||
|
||||
then run it with:
|
||||
|
||||
# ./test1
|
||||
Compiled with libpipewire 0.3.5
|
||||
Linked with libpipewire 0.3.5
|
||||
#
|
||||
|
||||
Use your build system's pkg-config support to integrate it into your project.
|
||||
For example, a minimal [meson.build](https://mesonbuild.com/) entry would look
|
||||
like this:
|
||||
|
||||
project('test1', ['c'])
|
||||
pipewire_dep = dependency('libpipewire-0.3')
|
||||
executable('test1', 'test1.c',
|
||||
dependencies: [pipewire_dep])
|
||||
|
||||
\ref page_tutorial "Index" | \ref page_tutorial2
|
||||
|
||||
*/
|
129
doc/dox/tutorial/tutorial2.dox
Normal file
129
doc/dox/tutorial/tutorial2.dox
Normal file
|
@ -0,0 +1,129 @@
|
|||
/** \page page_tutorial2 Tutorial - Part 2: Enumerating Objects
|
||||
|
||||
\ref page_tutorial1 | \ref page_tutorial "Index" | \ref page_tutorial3
|
||||
|
||||
In this tutorial we show how to connect to a PipeWire daemon and
|
||||
enumerate the objects that it has.
|
||||
|
||||
Let take a look at the following application to start.
|
||||
|
||||
\snippet tutorial2.c code
|
||||
|
||||
To compile the simple test application, copy it into a tutorial2.c file and
|
||||
use:
|
||||
|
||||
gcc -Wall tutorial2.c -o tutorial2 $(pkg-config --cflags --libs libpipewire-0.3)
|
||||
|
||||
Let's break this down:
|
||||
|
||||
First we need to initialize the PipeWire library with `pw_init()` as we
|
||||
saw in the previous tutorial. This will load and configure the right
|
||||
modules and setup logging and other tasks.
|
||||
|
||||
\code{.c}
|
||||
...
|
||||
pw_init(&argc, &argv);
|
||||
...
|
||||
\endcode
|
||||
|
||||
Next we need to create one of the `struct pw_loop` wrappers. PipeWire
|
||||
ships with 2 types of mainloop implementations. We will use the
|
||||
`struct pw_main_loop` implementation, we will see later how we can
|
||||
use the `struct pw_thread_loop` implementation as well.
|
||||
|
||||
The mainloop is an abstraction of a big poll loop, waiting for events
|
||||
to occur and things to do. Most of the PipeWire work will actually
|
||||
be performed in the context of this loop and so we need to make one
|
||||
first.
|
||||
|
||||
We then need to make a new context object with the loop. This context
|
||||
object will manage the resources for us and will make it possible for
|
||||
us to connect to a PipeWire daemon:
|
||||
|
||||
\code{.c}
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_context *context;
|
||||
|
||||
loop = pw_main_loop_new(NULL /* properties */);
|
||||
context = pw_context_new(pw_main_loop_get_loop(loop),
|
||||
NULL /* properties */,
|
||||
0 /* user_data size */);
|
||||
\endcode
|
||||
|
||||
It is possible to give extra properties when making the mainloop or
|
||||
context to tweak its features and functionality. It is also possible
|
||||
to add extra data to the allocated objects for your user data. It will
|
||||
stay alive for as long as the object is alive. We will use this
|
||||
feature later.
|
||||
|
||||
A real implementation would also need to check if the allocation
|
||||
succeeded and do some error handling, but we leave that out to make
|
||||
the code easier to read.
|
||||
|
||||
With the context we can now connect to the PipeWire daemon:
|
||||
|
||||
\code{.c}
|
||||
struct pw_core *core;
|
||||
core = pw_context_connect(context,
|
||||
NULL /* properties */,
|
||||
0 /* user_data size */);
|
||||
\endcode
|
||||
|
||||
This creates a socket between the client and the server and makes
|
||||
a proxy object (with ID 0) for the core. Don't forget to check the
|
||||
result here, a NULL value means that the connection failed.
|
||||
|
||||
At this point we can send messages to the server and receive events.
|
||||
For now we're not going to handle events on this core proxy but
|
||||
we're going to handle them on the registry object.
|
||||
|
||||
|
||||
\code{.c}
|
||||
struct pw_registry *registry;
|
||||
struct spa_hook registry_listener;
|
||||
|
||||
registry = pw_core_get_registry(core, PW_VERSION_REGISTRY,
|
||||
0 /* user_data size */);
|
||||
|
||||
spa_zero(registry_listener);
|
||||
pw_registry_add_listener(registry, ®istry_listener,
|
||||
®istry_events, NULL);
|
||||
\endcode
|
||||
|
||||
From the core we get the registry proxy object and when we use
|
||||
`pw_registry_add_listener()` to listen for events. We need a
|
||||
small `struct spa_hook` to keep track of the listener and a
|
||||
reference to the `struct pw_registry_events` that contains the
|
||||
events we want to listen to.
|
||||
|
||||
This is how we define the event handler and the function to
|
||||
handle the events:
|
||||
|
||||
\code{.c}
|
||||
static const struct pw_registry_events registry_events = {
|
||||
PW_VERSION_REGISTRY_EVENTS,
|
||||
.global = registry_event_global,
|
||||
};
|
||||
|
||||
static void registry_event_global(void *data, uint32_t id,
|
||||
uint32_t permissions, const char *type, uint32_t version,
|
||||
const struct spa_dict *props)
|
||||
{
|
||||
printf("object: id:%u type:%s/%d\n", id, type, version);
|
||||
}
|
||||
\endcode
|
||||
|
||||
Now that everything is set up we can start the mainloop and let
|
||||
the communication between client and server continue:
|
||||
|
||||
\code{.c}
|
||||
pw_main_loop_run(loop);
|
||||
\endcode
|
||||
|
||||
Since we don't call `pw_main_loop_quit()` anywhere, this loop will
|
||||
continue forever. In the next tutorial we'll see how we can nicely
|
||||
exit our application after we received all server objects.
|
||||
|
||||
\ref page_tutorial1 | \ref page_tutorial "Index" | \ref page_tutorial3
|
||||
|
||||
*/
|
119
doc/dox/tutorial/tutorial3.dox
Normal file
119
doc/dox/tutorial/tutorial3.dox
Normal file
|
@ -0,0 +1,119 @@
|
|||
/** \page page_tutorial3 Tutorial - Part 3: Forcing A Roundtrip
|
||||
|
||||
\ref page_tutorial2 | \ref page_tutorial "Index" | \ref page_tutorial4
|
||||
|
||||
In this tutorial we show how to force a roundtrip to the server
|
||||
to make sure an action completed.
|
||||
|
||||
We'll change our example from \ref page_tutorial2 "Tutorial 2" slightly
|
||||
and add the extra code to implement the roundtrip.
|
||||
|
||||
Let's take the following small method first:
|
||||
|
||||
\snippet tutorial3.c roundtrip
|
||||
|
||||
Let's take a look at what this method does.
|
||||
|
||||
\code{.c}
|
||||
struct spa_hook core_listener;
|
||||
|
||||
pw_core_add_listener(core, &core_listener, &core_events, &d);
|
||||
\endcode
|
||||
|
||||
First of all we add a listener for the events of the core
|
||||
object. We are only interested in the `done` event in this
|
||||
tutorial. This is the event handler:
|
||||
|
||||
\code{.c}
|
||||
static void on_core_done(void *data, uint32_t id, int seq)
|
||||
{
|
||||
struct roundtrip_data *d = data;
|
||||
|
||||
if (id == PW_ID_CORE && seq == d->pending)
|
||||
pw_main_loop_quit(d->loop);
|
||||
}
|
||||
\endcode
|
||||
|
||||
When the done event is received for an object with id `PW_ID_CORE` and
|
||||
a certain sequence number `seq`, this function will call `pw_main_loop_quit()`.
|
||||
|
||||
Next we do:
|
||||
|
||||
\code{.c}
|
||||
d.pending = pw_core_sync(core, PW_ID_CORE, 0);
|
||||
\endcode
|
||||
|
||||
This triggers the `sync` method on the core object with id
|
||||
`PW_ID_CORE` and sequence number 0.
|
||||
|
||||
Because this is a method on a proxy object, it will be executed
|
||||
asynchronously and the return value will reflect this. PipeWire
|
||||
uses the return values of the underlying SPA (Simple Plugin API)
|
||||
helper objects (See also \ref page_spa_design ).
|
||||
|
||||
Because all messages on the PipeWire server are handled sequentially,
|
||||
the sync method will be executed after all previous methods are
|
||||
completed. The PipeWire server will emit a `done` event with the
|
||||
same ID and the return value of the original `pw_core_sync()`
|
||||
method in the sequence number.
|
||||
|
||||
We then run the mainloop to send the messages to the server and
|
||||
receive the events:
|
||||
|
||||
\code{.c}
|
||||
pw_main_loop_run(loop);
|
||||
\endcode
|
||||
|
||||
When we get the done event, we can compare it to the sync method
|
||||
and then we know that we did a complete roundtrip and there are no
|
||||
more pending methods on the server. We can quit the mainloop and
|
||||
remove the listener:
|
||||
|
||||
\code{.c}
|
||||
spa_hook_remove(&core_listener);
|
||||
\endcode
|
||||
|
||||
If we add this roundtrip method to our code and call it instead of the
|
||||
`pw_main_loop_run()` we will exit the program after all previous methods
|
||||
are finished. This means that the `pw_core_get_registry()` call
|
||||
completed and thus that we also received all events for the globals
|
||||
on the server.
|
||||
|
||||
\snippet tutorial3.c code
|
||||
|
||||
To compile the simple test application, copy it into a tutorial3.c file and
|
||||
use:
|
||||
|
||||
gcc -Wall tutorial3.c -o tutorial3 $(pkg-config --cflags --libs libpipewire-0.3)
|
||||
|
||||
Now that our program completes, we can take a look at how we can destroy
|
||||
the objects we created. Let's destroy each of them in reverse order that we
|
||||
created them:
|
||||
|
||||
\code{.c}
|
||||
pw_proxy_destroy((struct pw_proxy*)registry);
|
||||
\endcode
|
||||
|
||||
The registry is a proxy and can be destroyed with the generic proxy destroy
|
||||
method. After destroying the object, you should not use it anymore. It is
|
||||
an error to destroy an object more than once.
|
||||
|
||||
We can disconnect from the server with:
|
||||
|
||||
\code{.c}
|
||||
pw_core_disconnect(core);
|
||||
\endcode
|
||||
|
||||
This will also destroy the core proxy object and will remove the proxies
|
||||
that might have been created on this connection.
|
||||
|
||||
We can finally destroy our context and mainloop to conclude this tutorial:
|
||||
|
||||
\code{.c}
|
||||
pw_context_destroy(context);
|
||||
pw_main_loop_destroy(loop);
|
||||
\endcode
|
||||
|
||||
\ref page_tutorial2 | \ref page_tutorial "Index" | \ref page_tutorial4
|
||||
|
||||
*/
|
158
doc/dox/tutorial/tutorial4.dox
Normal file
158
doc/dox/tutorial/tutorial4.dox
Normal file
|
@ -0,0 +1,158 @@
|
|||
/** \page page_tutorial4 Tutorial - Part 4: Playing A Tone
|
||||
|
||||
\ref page_tutorial3 | \ref page_tutorial "Index" | \ref page_tutorial5
|
||||
|
||||
In this tutorial we show how to use a stream to play a tone.
|
||||
|
||||
Let's take a look at the code before we break it down:
|
||||
|
||||
\snippet tutorial4.c code
|
||||
|
||||
Save as tutorial4.c and compile with:
|
||||
|
||||
gcc -Wall tutorial4.c -o tutorial4 -lm $(pkg-config --cflags --libs libpipewire-0.3)
|
||||
|
||||
We start with the usual boilerplate, `pw_init()` and a `pw_main_loop_new()`.
|
||||
We're going to store our objects in a structure so that we can pass them
|
||||
around in callbacks later.
|
||||
|
||||
\code{.c}
|
||||
struct data {
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_stream *stream;
|
||||
double accumulator;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct data data = { 0, };
|
||||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
data.loop = pw_main_loop_new(NULL);
|
||||
\endcode
|
||||
|
||||
Next we create a stream object. It takes the mainloop as first argument and
|
||||
a stream name as the second. Next we provide some properties for the stream
|
||||
and a callback + data.
|
||||
|
||||
\code{.c}
|
||||
data.stream = pw_stream_new_simple(
|
||||
pw_main_loop_get_loop(data.loop),
|
||||
"audio-src",
|
||||
pw_properties_new(
|
||||
PW_KEY_MEDIA_TYPE, "Audio",
|
||||
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||
PW_KEY_MEDIA_ROLE, "Music",
|
||||
NULL),
|
||||
&stream_events,
|
||||
&data);
|
||||
\endcode
|
||||
|
||||
We are using `pw_stream_new_simple()` but there is also a `pw_stream_new()` that
|
||||
takes an existing `struct pw_core` as the first argument and that requires you
|
||||
to add the event handle manually, for more control. The `pw_stream_new_simple()`
|
||||
is, as the name implies, easier to use because it creates a `struct pw_context`
|
||||
and `struct pw_core` automatically.
|
||||
|
||||
In the properties we need to give as much information about the stream as we
|
||||
can so that the session manager can make good decisions about how and where
|
||||
to route this stream. There are three important properties to configure:
|
||||
|
||||
- `PW_KEY_MEDIA_TYPE`: The media type; like Audio, Video, MIDI.
|
||||
- `PW_KEY_MEDIA_CATEGORY`: The category; like Playback, Capture, Duplex, Monitor.
|
||||
- `PW_KEY_MEDIA_ROLE`: The media role; like Movie, Music, Camera, Screen,
|
||||
Communication, Game, Notification, DSP, Production, Accessibility, Test.
|
||||
|
||||
The properties are owned by the stream and freed when the stream is destroyed
|
||||
later.
|
||||
|
||||
This is the event structure that we use to listen for events:
|
||||
|
||||
\code{.c}
|
||||
static const struct pw_stream_events stream_events = {
|
||||
PW_VERSION_STREAM_EVENTS,
|
||||
.process = on_process,
|
||||
};
|
||||
\endcode
|
||||
|
||||
We are for the moment only interested now in the `process` event. This event
|
||||
is called whenever we need to produce more data. We'll see how that function
|
||||
is implemented but first we need to setup the format of the stream:
|
||||
|
||||
\code{.c}
|
||||
const struct spa_pod *params[1];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
|
||||
#define DEFAULT_RATE 44100
|
||||
#define DEFAULT_CHANNELS 2
|
||||
|
||||
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
|
||||
&SPA_AUDIO_INFO_RAW_INIT(
|
||||
.format = SPA_AUDIO_FORMAT_S16,
|
||||
.channels = DEFAULT_CHANNELS,
|
||||
.rate = DEFAULT_RATE ));
|
||||
\endcode
|
||||
|
||||
This is using a `struct spa_pod_builder` to make a `struct spa_pod *` object
|
||||
in the buffer array on the stack. The parameter is of type `SPA_PARAM_EnumFormat`
|
||||
which means that it enumerates the possible formats for this stream. We have
|
||||
only one, a Signed 16 bit stereo format at 44.1KHz.
|
||||
|
||||
We use `spa_format_audio_raw_build()` which is a helper function to make the param
|
||||
with the builder. See \ref page_spa_pod for more information about how to
|
||||
make these POD objects.
|
||||
|
||||
Now we're ready to connect the stream and run the main loop:
|
||||
|
||||
\code{.c}
|
||||
pw_stream_connect(data.stream,
|
||||
PW_DIRECTION_OUTPUT,
|
||||
PW_ID_ANY,
|
||||
PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||
PW_STREAM_FLAG_RT_PROCESS,
|
||||
params, 1);
|
||||
|
||||
pw_main_loop_run(data.loop);
|
||||
\endcode
|
||||
|
||||
To connect we specify that we have a `PW_DIRECTION_OUTPUT` stream. The third argument
|
||||
is always `PW_ID_ANY`. Next we set some flags:
|
||||
|
||||
- `PW_STREAM_FLAG_AUTOCONNECT`: Automatically connect this stream. This instructs
|
||||
the session manager to link us to some consumer.
|
||||
- `PW_STREAM_FLAG_MAP_BUFFERS`: mmap the buffers for us so we can access the
|
||||
memory. If you don't set these flags you have either work with the fd or mmap
|
||||
yourself.
|
||||
- `PW_STREAM_FLAG_RT_PROCESS`: Run the process function in the realtime thread.
|
||||
Only use this if the process function only uses functions that are realtime
|
||||
safe, this means no allocation or file access or any locking.
|
||||
|
||||
And last we pass the extra parameters for our stream. Here we only have the
|
||||
allowed formats (`SPA_PARAM_EnumFormat`).
|
||||
|
||||
Running the mainloop will then start processing and will result in our
|
||||
`process` callback to be called. Let's have a look at that function now.
|
||||
|
||||
The main program flow of the process function is:
|
||||
|
||||
- `pw_stream_dequeue_buffer()` to obtain a buffer to write into.
|
||||
- Get pointers in buffer memory to write to.
|
||||
- Write data into buffer.
|
||||
- Adjust buffer with number of written bytes, offset, stride.
|
||||
- `pw_stream_queue_buffer()` to queue the buffer for playback.
|
||||
|
||||
\snippet tutorial4.c on_process
|
||||
|
||||
Check out the docs for \ref page_spa_buffer for more information
|
||||
about how to work with buffers.
|
||||
|
||||
Try to change the number of channels, samplerate or format; the stream
|
||||
will automatically convert to the format on the server.
|
||||
|
||||
|
||||
\ref page_tutorial3 | \ref page_tutorial "Index" | \ref page_tutorial5
|
||||
|
||||
*/
|
223
doc/dox/tutorial/tutorial5.dox
Normal file
223
doc/dox/tutorial/tutorial5.dox
Normal file
|
@ -0,0 +1,223 @@
|
|||
/** \page page_tutorial5 Tutorial - Part 5: Capturing Video Frames
|
||||
|
||||
\ref page_tutorial4 | \ref page_tutorial "Index" | \ref page_tutorial6
|
||||
|
||||
In this tutorial we show how to use a stream to capture a
|
||||
stream of video frames.
|
||||
|
||||
Even though we are now working with a different media type and
|
||||
we are capturing instead of playback, you will see that this
|
||||
example is very similar to \ref page_tutorial4.
|
||||
|
||||
Let's take a look at the code before we break it down:
|
||||
|
||||
\snippet tutorial5.c code
|
||||
|
||||
Save as tutorial5.c and compile with:
|
||||
|
||||
gcc -Wall tutorial5.c -o tutorial5 -lm $(pkg-config --cflags --libs libpipewire-0.3)
|
||||
|
||||
Most of the application is structured like \ref page_tutorial4.
|
||||
|
||||
We create a stream object with different properties to make it a Camera
|
||||
Video Capture stream.
|
||||
|
||||
\code{.c}
|
||||
props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video",
|
||||
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||
PW_KEY_MEDIA_ROLE, "Camera",
|
||||
NULL);
|
||||
if (argc > 1)
|
||||
pw_properties_set(props, PW_KEY_TARGET_OBJECT, argv[1]);
|
||||
|
||||
data.stream = pw_stream_new_simple(
|
||||
pw_main_loop_get_loop(data.loop),
|
||||
"video-capture",
|
||||
props,
|
||||
&stream_events,
|
||||
&data);
|
||||
\endcode
|
||||
|
||||
We also optionally allow the user to pass the name of the target node where the session
|
||||
manager is supposed to connect the node. The user may also give the value of the
|
||||
unique target node serial (`PW_KEY_OBJECT_SERIAL`) as the value.
|
||||
|
||||
In addition to the `process` event, we are also going to listen to a new event,
|
||||
`param_changed`:
|
||||
|
||||
\code{.c}
|
||||
static const struct pw_stream_events stream_events = {
|
||||
PW_VERSION_STREAM_EVENTS,
|
||||
.param_changed = on_param_changed,
|
||||
.process = on_process,
|
||||
};
|
||||
\endcode
|
||||
|
||||
Because we capture a stream of a wide range of different
|
||||
video formats and resolutions, we have to describe our accepted formats in
|
||||
a different way:
|
||||
|
||||
\code{.c}
|
||||
const struct spa_pod *params[1];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
|
||||
params[0] = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(7,
|
||||
SPA_VIDEO_FORMAT_RGB,
|
||||
SPA_VIDEO_FORMAT_RGB,
|
||||
SPA_VIDEO_FORMAT_RGBA,
|
||||
SPA_VIDEO_FORMAT_RGBx,
|
||||
SPA_VIDEO_FORMAT_BGRx,
|
||||
SPA_VIDEO_FORMAT_YUY2,
|
||||
SPA_VIDEO_FORMAT_I420),
|
||||
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(
|
||||
&SPA_RECTANGLE(320, 240),
|
||||
&SPA_RECTANGLE(1, 1),
|
||||
&SPA_RECTANGLE(4096, 4096)),
|
||||
SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction(
|
||||
&SPA_FRACTION(25, 1),
|
||||
&SPA_FRACTION(0, 1),
|
||||
&SPA_FRACTION(1000, 1)));
|
||||
\endcode
|
||||
|
||||
This is using a `struct spa_pod_builder` to make a `struct spa_pod *` object
|
||||
in the buffer array on the stack. The parameter is of type `SPA_PARAM_EnumFormat`
|
||||
which means that it enumerates the possible formats for this stream.
|
||||
|
||||
In this example we use the builder to create some `CHOICE` entries for
|
||||
the format properties.
|
||||
|
||||
We have an enumeration of formats, we need to first give the amount of enumerations
|
||||
that follow, then the default (preferred) value, followed by alternatives in order
|
||||
of preference:
|
||||
|
||||
\code{.c}
|
||||
SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(7,
|
||||
SPA_VIDEO_FORMAT_RGB, /* default */
|
||||
SPA_VIDEO_FORMAT_RGB, /* alternative 1 */
|
||||
SPA_VIDEO_FORMAT_RGBA, /* alternative 2 */
|
||||
SPA_VIDEO_FORMAT_RGBx, /* .. etc.. */
|
||||
SPA_VIDEO_FORMAT_BGRx,
|
||||
SPA_VIDEO_FORMAT_YUY2,
|
||||
SPA_VIDEO_FORMAT_I420),
|
||||
\endcode
|
||||
|
||||
We also have a `RANGE` of values for the size. We need to give a default (preferred)
|
||||
size and then a min and max value:
|
||||
|
||||
\code{.c}
|
||||
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(
|
||||
&SPA_RECTANGLE(320, 240), /* default */
|
||||
&SPA_RECTANGLE(1, 1), /* min */
|
||||
&SPA_RECTANGLE(4096, 4096)), /* max */
|
||||
\endcode
|
||||
|
||||
We have something similar for the framerate.
|
||||
|
||||
Note that there are other video parameters that we don't specify here. This
|
||||
means that we don't have any restrictions for their values.
|
||||
|
||||
See \ref page_spa_pod for more information about how to make these
|
||||
POD objects.
|
||||
|
||||
Now we're ready to connect the stream and run the main loop:
|
||||
|
||||
\code{.c}
|
||||
pw_stream_connect(data.stream,
|
||||
PW_DIRECTION_INPUT,
|
||||
PW_ID_ANY,
|
||||
PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS,
|
||||
params, 1);
|
||||
|
||||
pw_main_loop_run(data.loop);
|
||||
\endcode
|
||||
|
||||
To connect we specify that we have a `PW_DIRECTION_INPUT` stream. The third
|
||||
argument is always `PW_ID_ANY`.
|
||||
|
||||
We're setting the `PW_STREAM_FLAG_AUTOCONNECT` flag to make an automatic
|
||||
connection to a suitable camera and `PW_STREAM_FLAG_MAP_BUFFERS` to let the
|
||||
stream mmap the data for us.
|
||||
|
||||
And last we pass the extra parameters for our stream. Here we only have the
|
||||
allowed formats (`SPA_PARAM_EnumFormat`).
|
||||
|
||||
Running the mainloop will start the connection and negotiation process.
|
||||
First our `param_changed` event will be called with the format that was
|
||||
negotiated between our stream and the camera. This is always something that
|
||||
is compatible with what we enumerated in the EnumFormat param when we
|
||||
connected.
|
||||
|
||||
Let's take a look at how we can parse the format in the `param_changed`
|
||||
event:
|
||||
|
||||
\code{.c}
|
||||
static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod *param)
|
||||
{
|
||||
struct data *data = userdata;
|
||||
|
||||
if (param == NULL || id != SPA_PARAM_Format)
|
||||
return;
|
||||
\endcode
|
||||
|
||||
First check if there is a param. A NULL param means that it is cleared. The ID
|
||||
of the param tells you what param it is. We are only interested in Format
|
||||
param (`SPA_PARAM_Format`).
|
||||
|
||||
We can parse the media type and subtype as below and ensure that it is
|
||||
of the right type. In our example this will always be true but when your
|
||||
EnumFormat contains different media types or subtypes, this is how you can
|
||||
parse them:
|
||||
|
||||
\code{.c}
|
||||
if (spa_format_parse(param,
|
||||
&data->format.media_type,
|
||||
&data->format.media_subtype) < 0)
|
||||
return;
|
||||
|
||||
if (data->format.media_type != SPA_MEDIA_TYPE_video ||
|
||||
data->format.media_subtype != SPA_MEDIA_SUBTYPE_raw)
|
||||
return;
|
||||
\endcode
|
||||
|
||||
For the `video/raw` media type/subtype there is a utility function to
|
||||
parse out the values into a `struct spa_video_info`. This makes it easier
|
||||
to deal with.
|
||||
|
||||
\code{.c}
|
||||
if (spa_format_video_raw_parse(param, &data->format.info.raw) < 0)
|
||||
return;
|
||||
|
||||
printf("got video format:\n");
|
||||
printf(" format: %d (%s)\n", data->format.info.raw.format,
|
||||
spa_debug_type_find_name(spa_type_video_format,
|
||||
data->format.info.raw.format));
|
||||
printf(" size: %dx%d\n", data->format.info.raw.size.width,
|
||||
data->format.info.raw.size.height);
|
||||
printf(" framerate: %d/%d\n", data->format.info.raw.framerate.num,
|
||||
data->format.info.raw.framerate.denom);
|
||||
|
||||
/** prepare to render video of this size */
|
||||
}
|
||||
\endcode
|
||||
|
||||
In this example we dump the video size and parameters but in a real playback
|
||||
or capture application you might want to set up the screen or encoder to
|
||||
deal with the format.
|
||||
|
||||
After negotiation, the process function is called for each new frame. Check out
|
||||
\ref page_tutorial4 for another example.
|
||||
|
||||
\snippet tutorial5.c on_process
|
||||
|
||||
In a real playback application, one would do something with the data, like
|
||||
copy it to the screen or encode it into a file.
|
||||
|
||||
\ref page_tutorial4 | \ref page_tutorial "Index" | \ref page_tutorial6
|
||||
|
||||
*/
|
69
doc/dox/tutorial/tutorial6.dox
Normal file
69
doc/dox/tutorial/tutorial6.dox
Normal file
|
@ -0,0 +1,69 @@
|
|||
/** \page page_tutorial6 Tutorial - Part 6: Binding Objects
|
||||
|
||||
\ref page_tutorial5 | \ref page_tutorial "Index"
|
||||
|
||||
In this tutorial we show how to bind to an object so that we can
|
||||
receive events and call methods on the object.
|
||||
|
||||
Let take a look at the following application to start.
|
||||
|
||||
\snippet tutorial6.c code
|
||||
|
||||
To compile the simple test application, copy it into a tutorial6.c file and
|
||||
use:
|
||||
|
||||
gcc -Wall tutorial6.c -o tutorial6 $(pkg-config --cflags --libs libpipewire-0.3)
|
||||
|
||||
Most of this is the same as \ref page_tutorial2 where we simply
|
||||
enumerated all objects on the server. Instead of just printing the object
|
||||
id and some other properties, in this example we also bind to the object.
|
||||
|
||||
We use the `pw_registry_bind()` method on our registry object like this:
|
||||
|
||||
\snippet tutorial6.c registry_event_global
|
||||
|
||||
We bind to the first client object that we see. This gives us a pointer
|
||||
to a `struct pw_proxy` that we can also cast to a `struct pw_client`.
|
||||
|
||||
On the proxy we can call methods and listen for events. PipeWire will
|
||||
automatically serialize the method calls and events between client and
|
||||
server for us.
|
||||
|
||||
We can now listen for events by adding a listener. We're going to
|
||||
listen to the info event on the client object that is emitted right
|
||||
after we bind to it or when it changes. This is not very different
|
||||
from the registry listener we added before:
|
||||
|
||||
\snippet tutorial6.c client_info
|
||||
|
||||
\code{.c}
|
||||
static void registry_event_global(void *_data, uint32_t id,
|
||||
uint32_t permissions, const char *type,
|
||||
uint32_t version, const struct spa_dict *props)
|
||||
{
|
||||
/* ... */
|
||||
pw_client_add_listener(data->client,
|
||||
&data->client_listener,
|
||||
&client_events, data);
|
||||
/* ... */
|
||||
}
|
||||
\endcode
|
||||
|
||||
We're also quitting the mainloop after we get the info to nicely stop
|
||||
our tutorial application.
|
||||
|
||||
When we stop the application, don't forget to destroy all proxies that
|
||||
you created. Otherwise, they will be leaked:
|
||||
|
||||
\code{.c}
|
||||
/* ... */
|
||||
pw_proxy_destroy((struct pw_proxy *)data.client);
|
||||
/* ... */
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\ref page_tutorial5 | \ref page_tutorial "Index"
|
||||
|
||||
*/
|
1364
doc/doxygen-awesome.css
Normal file
1364
doc/doxygen-awesome.css
Normal file
File diff suppressed because it is too large
Load diff
9
doc/examples.dox.in
Normal file
9
doc/examples.dox.in
Normal file
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
|
||||
\page page_examples List of example programs
|
||||
|
||||
@example_ref@
|
||||
|
||||
@example_doxygen@
|
||||
|
||||
*/
|
19
doc/examples/tutorial1.c
Normal file
19
doc/examples/tutorial1.c
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
[title]
|
||||
\ref page_tutorial1
|
||||
[title]
|
||||
*/
|
||||
/* [code] */
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
fprintf(stdout, "Compiled with libpipewire %s\n"
|
||||
"Linked with libpipewire %s\n",
|
||||
pw_get_headers_version(),
|
||||
pw_get_library_version());
|
||||
return 0;
|
||||
}
|
||||
/* [code] */
|
56
doc/examples/tutorial2.c
Normal file
56
doc/examples/tutorial2.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
[title]
|
||||
\ref page_tutorial2
|
||||
[title]
|
||||
*/
|
||||
/* [code] */
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
static void registry_event_global(void *data, uint32_t id,
|
||||
uint32_t permissions, const char *type, uint32_t version,
|
||||
const struct spa_dict *props)
|
||||
{
|
||||
printf("object: id:%u type:%s/%d\n", id, type, version);
|
||||
}
|
||||
|
||||
static const struct pw_registry_events registry_events = {
|
||||
PW_VERSION_REGISTRY_EVENTS,
|
||||
.global = registry_event_global,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_context *context;
|
||||
struct pw_core *core;
|
||||
struct pw_registry *registry;
|
||||
struct spa_hook registry_listener;
|
||||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
loop = pw_main_loop_new(NULL /* properties */);
|
||||
context = pw_context_new(pw_main_loop_get_loop(loop),
|
||||
NULL /* properties */,
|
||||
0 /* user_data size */);
|
||||
|
||||
core = pw_context_connect(context,
|
||||
NULL /* properties */,
|
||||
0 /* user_data size */);
|
||||
|
||||
registry = pw_core_get_registry(core, PW_VERSION_REGISTRY,
|
||||
0 /* user_data size */);
|
||||
|
||||
spa_zero(registry_listener);
|
||||
pw_registry_add_listener(registry, ®istry_listener,
|
||||
®istry_events, NULL);
|
||||
|
||||
pw_main_loop_run(loop);
|
||||
|
||||
pw_proxy_destroy((struct pw_proxy*)registry);
|
||||
pw_core_disconnect(core);
|
||||
pw_context_destroy(context);
|
||||
pw_main_loop_destroy(loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* [code] */
|
91
doc/examples/tutorial3.c
Normal file
91
doc/examples/tutorial3.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
[title]
|
||||
\ref page_tutorial3
|
||||
[title]
|
||||
*/
|
||||
/* [code] */
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
/* [roundtrip] */
|
||||
struct roundtrip_data {
|
||||
int pending;
|
||||
struct pw_main_loop *loop;
|
||||
};
|
||||
|
||||
static void on_core_done(void *data, uint32_t id, int seq)
|
||||
{
|
||||
struct roundtrip_data *d = data;
|
||||
|
||||
if (id == PW_ID_CORE && seq == d->pending)
|
||||
pw_main_loop_quit(d->loop);
|
||||
}
|
||||
|
||||
static void roundtrip(struct pw_core *core, struct pw_main_loop *loop)
|
||||
{
|
||||
static const struct pw_core_events core_events = {
|
||||
PW_VERSION_CORE_EVENTS,
|
||||
.done = on_core_done,
|
||||
};
|
||||
|
||||
struct roundtrip_data d = { .loop = loop };
|
||||
struct spa_hook core_listener;
|
||||
int err;
|
||||
|
||||
pw_core_add_listener(core, &core_listener, &core_events, &d);
|
||||
|
||||
d.pending = pw_core_sync(core, PW_ID_CORE, 0);
|
||||
|
||||
if ((err = pw_main_loop_run(loop)) < 0)
|
||||
printf("main_loop_run error:%d!\n", err);
|
||||
|
||||
spa_hook_remove(&core_listener);
|
||||
}
|
||||
/* [roundtrip] */
|
||||
|
||||
static void registry_event_global(void *data, uint32_t id,
|
||||
uint32_t permissions, const char *type, uint32_t version,
|
||||
const struct spa_dict *props)
|
||||
{
|
||||
printf("object: id:%u type:%s/%d\n", id, type, version);
|
||||
}
|
||||
|
||||
static const struct pw_registry_events registry_events = {
|
||||
PW_VERSION_REGISTRY_EVENTS,
|
||||
.global = registry_event_global,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_context *context;
|
||||
struct pw_core *core;
|
||||
struct pw_registry *registry;
|
||||
struct spa_hook registry_listener;
|
||||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
loop = pw_main_loop_new(NULL /* properties */);
|
||||
context = pw_context_new(pw_main_loop_get_loop(loop),
|
||||
NULL /* properties */,
|
||||
0 /* user_data size */);
|
||||
|
||||
core = pw_context_connect(context,
|
||||
NULL /* properties */,
|
||||
0 /* user_data size */);
|
||||
|
||||
registry = pw_core_get_registry(core, PW_VERSION_REGISTRY,
|
||||
0 /* user_data size */);
|
||||
|
||||
pw_registry_add_listener(registry, ®istry_listener,
|
||||
®istry_events, NULL);
|
||||
|
||||
roundtrip(core, loop);
|
||||
|
||||
pw_proxy_destroy((struct pw_proxy*)registry);
|
||||
pw_core_disconnect(core);
|
||||
pw_context_destroy(context);
|
||||
pw_main_loop_destroy(loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* [code] */
|
120
doc/examples/tutorial4.c
Normal file
120
doc/examples/tutorial4.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
[title]
|
||||
\ref page_tutorial4
|
||||
[title]
|
||||
*/
|
||||
/* [code] */
|
||||
#include <math.h>
|
||||
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#define M_PI_M2 ( M_PI + M_PI )
|
||||
|
||||
#define DEFAULT_RATE 44100
|
||||
#define DEFAULT_CHANNELS 2
|
||||
#define DEFAULT_VOLUME 0.7
|
||||
|
||||
struct data {
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_stream *stream;
|
||||
double accumulator;
|
||||
};
|
||||
|
||||
/* [on_process] */
|
||||
static void on_process(void *userdata)
|
||||
{
|
||||
struct data *data = userdata;
|
||||
struct pw_buffer *b;
|
||||
struct spa_buffer *buf;
|
||||
int i, c, n_frames, stride;
|
||||
int16_t *dst, val;
|
||||
|
||||
if ((b = pw_stream_dequeue_buffer(data->stream)) == NULL) {
|
||||
pw_log_warn("out of buffers: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
buf = b->buffer;
|
||||
if ((dst = buf->datas[0].data) == NULL)
|
||||
return;
|
||||
|
||||
stride = sizeof(int16_t) * DEFAULT_CHANNELS;
|
||||
n_frames = buf->datas[0].maxsize / stride;
|
||||
if (b->requested)
|
||||
n_frames = SPA_MIN(b->requested, n_frames);
|
||||
|
||||
for (i = 0; i < n_frames; i++) {
|
||||
data->accumulator += M_PI_M2 * 440 / DEFAULT_RATE;
|
||||
if (data->accumulator >= M_PI_M2)
|
||||
data->accumulator -= M_PI_M2;
|
||||
|
||||
/* sin() gives a value between -1.0 and 1.0, we first apply
|
||||
* the volume and then scale with 32767.0 to get a 16 bits value
|
||||
* between [-32767 32767].
|
||||
* Another common method to convert a double to
|
||||
* 16 bits is to multiple by 32768.0 and then clamp to
|
||||
* [-32768 32767] to get the full 16 bits range. */
|
||||
val = sin(data->accumulator) * DEFAULT_VOLUME * 32767.0;
|
||||
for (c = 0; c < DEFAULT_CHANNELS; c++)
|
||||
*dst++ = val;
|
||||
}
|
||||
|
||||
buf->datas[0].chunk->offset = 0;
|
||||
buf->datas[0].chunk->stride = stride;
|
||||
buf->datas[0].chunk->size = n_frames * stride;
|
||||
|
||||
pw_stream_queue_buffer(data->stream, b);
|
||||
}
|
||||
/* [on_process] */
|
||||
|
||||
static const struct pw_stream_events stream_events = {
|
||||
PW_VERSION_STREAM_EVENTS,
|
||||
.process = on_process,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct data data = { 0, };
|
||||
const struct spa_pod *params[1];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
data.loop = pw_main_loop_new(NULL);
|
||||
|
||||
data.stream = pw_stream_new_simple(
|
||||
pw_main_loop_get_loop(data.loop),
|
||||
"audio-src",
|
||||
pw_properties_new(
|
||||
PW_KEY_MEDIA_TYPE, "Audio",
|
||||
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||
PW_KEY_MEDIA_ROLE, "Music",
|
||||
NULL),
|
||||
&stream_events,
|
||||
&data);
|
||||
|
||||
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
|
||||
&SPA_AUDIO_INFO_RAW_INIT(
|
||||
.format = SPA_AUDIO_FORMAT_S16,
|
||||
.channels = DEFAULT_CHANNELS,
|
||||
.rate = DEFAULT_RATE ));
|
||||
|
||||
pw_stream_connect(data.stream,
|
||||
PW_DIRECTION_OUTPUT,
|
||||
PW_ID_ANY,
|
||||
PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||
PW_STREAM_FLAG_RT_PROCESS,
|
||||
params, 1);
|
||||
|
||||
pw_main_loop_run(data.loop);
|
||||
|
||||
pw_stream_destroy(data.stream);
|
||||
pw_main_loop_destroy(data.loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* [code] */
|
141
doc/examples/tutorial5.c
Normal file
141
doc/examples/tutorial5.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
[title]
|
||||
\ref page_tutorial5
|
||||
[title]
|
||||
*/
|
||||
/* [code] */
|
||||
#include <spa/param/video/format-utils.h>
|
||||
#include <spa/debug/types.h>
|
||||
#include <spa/param/video/type-info.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
struct data {
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_stream *stream;
|
||||
|
||||
struct spa_video_info format;
|
||||
};
|
||||
|
||||
/* [on_process] */
|
||||
static void on_process(void *userdata)
|
||||
{
|
||||
struct data *data = userdata;
|
||||
struct pw_buffer *b;
|
||||
struct spa_buffer *buf;
|
||||
|
||||
if ((b = pw_stream_dequeue_buffer(data->stream)) == NULL) {
|
||||
pw_log_warn("out of buffers: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
buf = b->buffer;
|
||||
if (buf->datas[0].data == NULL)
|
||||
return;
|
||||
|
||||
/** copy frame data to screen */
|
||||
printf("got a frame of size %d\n", buf->datas[0].chunk->size);
|
||||
|
||||
pw_stream_queue_buffer(data->stream, b);
|
||||
}
|
||||
/* [on_process] */
|
||||
|
||||
static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod *param)
|
||||
{
|
||||
struct data *data = userdata;
|
||||
|
||||
if (param == NULL || id != SPA_PARAM_Format)
|
||||
return;
|
||||
|
||||
if (spa_format_parse(param,
|
||||
&data->format.media_type,
|
||||
&data->format.media_subtype) < 0)
|
||||
return;
|
||||
|
||||
if (data->format.media_type != SPA_MEDIA_TYPE_video ||
|
||||
data->format.media_subtype != SPA_MEDIA_SUBTYPE_raw)
|
||||
return;
|
||||
|
||||
if (spa_format_video_raw_parse(param, &data->format.info.raw) < 0)
|
||||
return;
|
||||
|
||||
printf("got video format:\n");
|
||||
printf(" format: %d (%s)\n", data->format.info.raw.format,
|
||||
spa_debug_type_find_name(spa_type_video_format,
|
||||
data->format.info.raw.format));
|
||||
printf(" size: %dx%d\n", data->format.info.raw.size.width,
|
||||
data->format.info.raw.size.height);
|
||||
printf(" framerate: %d/%d\n", data->format.info.raw.framerate.num,
|
||||
data->format.info.raw.framerate.denom);
|
||||
|
||||
/** prepare to render video of this size */
|
||||
}
|
||||
|
||||
static const struct pw_stream_events stream_events = {
|
||||
PW_VERSION_STREAM_EVENTS,
|
||||
.param_changed = on_param_changed,
|
||||
.process = on_process,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct data data = { 0, };
|
||||
const struct spa_pod *params[1];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
struct pw_properties *props;
|
||||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
data.loop = pw_main_loop_new(NULL);
|
||||
|
||||
props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video",
|
||||
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||
PW_KEY_MEDIA_ROLE, "Camera",
|
||||
NULL);
|
||||
if (argc > 1)
|
||||
pw_properties_set(props, PW_KEY_TARGET_OBJECT, argv[1]);
|
||||
|
||||
data.stream = pw_stream_new_simple(
|
||||
pw_main_loop_get_loop(data.loop),
|
||||
"video-capture",
|
||||
props,
|
||||
&stream_events,
|
||||
&data);
|
||||
|
||||
params[0] = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(7,
|
||||
SPA_VIDEO_FORMAT_RGB,
|
||||
SPA_VIDEO_FORMAT_RGB,
|
||||
SPA_VIDEO_FORMAT_RGBA,
|
||||
SPA_VIDEO_FORMAT_RGBx,
|
||||
SPA_VIDEO_FORMAT_BGRx,
|
||||
SPA_VIDEO_FORMAT_YUY2,
|
||||
SPA_VIDEO_FORMAT_I420),
|
||||
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(
|
||||
&SPA_RECTANGLE(320, 240),
|
||||
&SPA_RECTANGLE(1, 1),
|
||||
&SPA_RECTANGLE(4096, 4096)),
|
||||
SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction(
|
||||
&SPA_FRACTION(25, 1),
|
||||
&SPA_FRACTION(0, 1),
|
||||
&SPA_FRACTION(1000, 1)));
|
||||
|
||||
pw_stream_connect(data.stream,
|
||||
PW_DIRECTION_INPUT,
|
||||
PW_ID_ANY,
|
||||
PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS,
|
||||
params, 1);
|
||||
|
||||
pw_main_loop_run(data.loop);
|
||||
|
||||
pw_stream_destroy(data.stream);
|
||||
pw_main_loop_destroy(data.loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* [code] */
|
97
doc/examples/tutorial6.c
Normal file
97
doc/examples/tutorial6.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
[title]
|
||||
\ref page_tutorial6
|
||||
[title]
|
||||
*/
|
||||
/* [code] */
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
struct data {
|
||||
struct pw_main_loop *loop;
|
||||
struct pw_context *context;
|
||||
struct pw_core *core;
|
||||
|
||||
struct pw_registry *registry;
|
||||
struct spa_hook registry_listener;
|
||||
|
||||
struct pw_client *client;
|
||||
struct spa_hook client_listener;
|
||||
};
|
||||
|
||||
/* [client_info] */
|
||||
static void client_info(void *object, const struct pw_client_info *info)
|
||||
{
|
||||
struct data *data = object;
|
||||
const struct spa_dict_item *item;
|
||||
|
||||
printf("client: id:%u\n", info->id);
|
||||
printf("\tprops:\n");
|
||||
spa_dict_for_each(item, info->props)
|
||||
printf("\t\t%s: \"%s\"\n", item->key, item->value);
|
||||
|
||||
pw_main_loop_quit(data->loop);
|
||||
}
|
||||
|
||||
static const struct pw_client_events client_events = {
|
||||
PW_VERSION_CLIENT_EVENTS,
|
||||
.info = client_info,
|
||||
};
|
||||
/* [client_info] */
|
||||
|
||||
/* [registry_event_global] */
|
||||
static void registry_event_global(void *_data, uint32_t id,
|
||||
uint32_t permissions, const char *type,
|
||||
uint32_t version, const struct spa_dict *props)
|
||||
{
|
||||
struct data *data = _data;
|
||||
if (data->client != NULL)
|
||||
return;
|
||||
|
||||
if (strcmp(type, PW_TYPE_INTERFACE_Client) == 0) {
|
||||
data->client = pw_registry_bind(data->registry,
|
||||
id, type, PW_VERSION_CLIENT, 0);
|
||||
pw_client_add_listener(data->client,
|
||||
&data->client_listener,
|
||||
&client_events, data);
|
||||
}
|
||||
}
|
||||
/* [registry_event_global] */
|
||||
|
||||
static const struct pw_registry_events registry_events = {
|
||||
PW_VERSION_REGISTRY_EVENTS,
|
||||
.global = registry_event_global,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct data data;
|
||||
|
||||
spa_zero(data);
|
||||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
data.loop = pw_main_loop_new(NULL /* properties */ );
|
||||
data.context = pw_context_new(pw_main_loop_get_loop(data.loop),
|
||||
NULL /* properties */ ,
|
||||
0 /* user_data size */ );
|
||||
|
||||
data.core = pw_context_connect(data.context, NULL /* properties */ ,
|
||||
0 /* user_data size */ );
|
||||
|
||||
data.registry = pw_core_get_registry(data.core, PW_VERSION_REGISTRY,
|
||||
0 /* user_data size */ );
|
||||
|
||||
pw_registry_add_listener(data.registry, &data.registry_listener,
|
||||
®istry_events, &data);
|
||||
|
||||
pw_main_loop_run(data.loop);
|
||||
|
||||
pw_proxy_destroy((struct pw_proxy *)data.client);
|
||||
pw_proxy_destroy((struct pw_proxy *)data.registry);
|
||||
pw_core_disconnect(data.core);
|
||||
pw_context_destroy(data.context);
|
||||
pw_main_loop_destroy(data.loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* [code] */
|
37
doc/input-filter-h.sh
Executable file
37
doc/input-filter-h.sh
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Doxygen input filter, which tries to fix documentation of callback
|
||||
# method macros.
|
||||
#
|
||||
# This is used for .h files.
|
||||
#
|
||||
|
||||
FILENAME="$1"
|
||||
|
||||
# Add \ingroup commands for the file, for each \addgroup in it
|
||||
BASEFILE=$(echo "$FILENAME" | sed -e 's@.*src/pipewire/@pipewire/@; s@.*spa/include/spa/@spa/@; s@.*src/test/@test/@;')
|
||||
|
||||
printf "/** \\\\file\n\`%s\`\n" "$BASEFILE"
|
||||
sed -n -e '/.*\\addtogroup [a-zA-Z0-9_].*/ { s/.*addtogroup /\\ingroup /; p; }' < "$FILENAME" | sort | uniq
|
||||
echo " */"
|
||||
|
||||
# Add \sa and \copydoc for (struct *methods) callback macros.
|
||||
# #define pw_core_add_listener(...) pw_core_method(c,add_listener,...) -> add \sa and \copydoc
|
||||
# #define spa_system_read(...) spa_system_method_r(c,read,...) -> add \sa and \copydoc
|
||||
#
|
||||
# Also:
|
||||
# Ensure all macros are included (also those defined inside a struct),
|
||||
# by adding /** \ingroup XXX */ before each definition.
|
||||
# Also ensure all opaque structs get included.
|
||||
# Strip SPA_FORMAT_ARG_FUNC(1) etc. things that confuse doxygen
|
||||
sed -e 's@^\(#define .*[[:space:]]\)\(.*_method\)\((.,[[:space:]]*\)\([a-z_]\+\)\(.*)[[:space:]]*\)$@\1\2\3\4\5 /**< \\copydoc \2s.\4\n\n\\sa \2s.\4 */@;' \
|
||||
-e 's@^\(#define .*[[:space:]]\)\(.*_method\)\(_[rvs](.,[[:space:]]*\)\([a-z_]\+\)\(.*)[[:space:]]*\)$@\1\2\3\4\5 /**< \\copydoc \2s.\4\n\n\\sa \2s.\4 */@;' \
|
||||
-e '/\\addtogroup/ { h; s@.*\\addtogroup \(.*\).*@/** \\ingroup \1 */@; x; }' \
|
||||
-e '/#define \(PW\|SPA\)_[A-Z].*[^\\][ ]*$/ { x; p; x; }' \
|
||||
-e 's@^\([ ]*struct \)\([a-zA-Z0-9_]*\)\(;.*\)$@/** \\struct \2 */\n\1\2\3@;' \
|
||||
-e 's@^[ ]*SPA_FORMAT_ARG_FUNC([0-9, ]*)@@;' \
|
||||
-e 's@[ ]*SPA_PRINTF_FUNC([0-9, ]*);@;@;' \
|
||||
-e 's@^[ ]*SPA_WARN_UNUSED_RESULT@ @;' \
|
||||
-e 's@ SPA_SENTINEL;@;@;' \
|
||||
-e 's@ SPA_UNUSED,@,@;' \
|
||||
< "$FILENAME"
|
167
doc/input-filter-md.py
Executable file
167
doc/input-filter-md.py
Executable file
|
@ -0,0 +1,167 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- mode: python; coding: utf-8; eval: (blacken-mode); -*-
|
||||
r"""input-filter-md.py FILENAME
|
||||
input-filter-md.py --index FILENAMES...
|
||||
|
||||
Doxygen .md input filter that adds extended syntax.
|
||||
With --index, generates an index file.
|
||||
Assumes BUILD_DIR environment variable is set.
|
||||
|
||||
@PAR@ <section> <name> (...)
|
||||
|
||||
Adds an index item and expands to
|
||||
|
||||
\anchor <key>
|
||||
\par <name> (...)
|
||||
|
||||
@SECREF@ <section>
|
||||
|
||||
Expands to
|
||||
|
||||
\secreflist
|
||||
\refitem ...
|
||||
...
|
||||
\endsecreflist
|
||||
|
||||
containing all index items from the specified section.
|
||||
|
||||
# Section title @IDX@ <section>
|
||||
|
||||
Adds the section title to the index, and expands to an anchor
|
||||
|
||||
# Section title {#key}
|
||||
|
||||
The index keys can be used in \ref and have format
|
||||
|
||||
{section}__{name}
|
||||
|
||||
where the parts are converted to lowercase and _ replaces
|
||||
non-alphanumerics.
|
||||
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
def index_key(section, name):
|
||||
key = f"{section}__{name}".lower()
|
||||
return re.sub(r"[^A-Za-z0-9_-]", "_", key)
|
||||
|
||||
|
||||
BUILD_DIR = os.environ["BUILD_DIR"]
|
||||
PAR_RE = r"^@PAR@\s+([^\s]*)[ \t]+(\S+)(.*)$"
|
||||
IDX_RE = r"^(#+)(.*)@IDX@[ \t]+(\S+)[ \t]*$"
|
||||
SECREF_RE = r"^@SECREF@[ \t]+([^\n]*)[ \t]*$"
|
||||
|
||||
|
||||
def main(args):
|
||||
fn = args[0]
|
||||
with open(fn, "r") as f:
|
||||
text = f.read()
|
||||
|
||||
def par(m):
|
||||
section = m.group(1)
|
||||
name = m.group(2)
|
||||
rest = m.group(3).strip()
|
||||
key = index_key(section, name)
|
||||
return f"\\anchor {key}\n\\par {name} {rest}"
|
||||
|
||||
def idx(m):
|
||||
level = m.group(1)
|
||||
title = name = m.group(2).strip()
|
||||
section = m.group(3)
|
||||
if title == title.upper():
|
||||
name = name.capitalize()
|
||||
key = index_key(section, name)
|
||||
return f"{level} {title} {{#{key}}}"
|
||||
|
||||
def secref(m):
|
||||
import os
|
||||
import json
|
||||
|
||||
secs = m.group(1).split()
|
||||
|
||||
with open(os.path.join(BUILD_DIR, "index.json"), "r") as f:
|
||||
index = json.load(f)
|
||||
|
||||
items = {}
|
||||
|
||||
for sec in secs:
|
||||
if sec not in index:
|
||||
print(f"{fn}: no index '{sec}'", file=sys.stderr)
|
||||
else:
|
||||
for name, key in index[sec].items():
|
||||
if name in items:
|
||||
pkey, psec = items.pop(name)
|
||||
nname = f"{name} ({sec})"
|
||||
items[nname] = (key, sec)
|
||||
if pkey is not None:
|
||||
pname = f"{name} ({psec})"
|
||||
items[pname] = (pkey, psec)
|
||||
items[name] = (None, None)
|
||||
else:
|
||||
items[name] = (key, sec)
|
||||
|
||||
text = [r"\secreflist"]
|
||||
for name, (key, sec) in sorted(items.items()):
|
||||
if key is not None:
|
||||
text.append(rf'\refitem {key} "{name}"')
|
||||
text.append(r"\endsecreflist")
|
||||
text = "\n".join(text)
|
||||
return f"{text}\n"
|
||||
|
||||
text = re.sub(PAR_RE, par, text, flags=re.M)
|
||||
text = re.sub(IDX_RE, idx, text, flags=re.M)
|
||||
text = re.sub(SECREF_RE, secref, text, flags=re.M)
|
||||
|
||||
print(text)
|
||||
|
||||
|
||||
def main_index(args):
|
||||
import json
|
||||
|
||||
sections = {}
|
||||
|
||||
for fn in set(args):
|
||||
with open(fn, "r") as f:
|
||||
load_index(sections, f.read())
|
||||
|
||||
result = {}
|
||||
|
||||
for section, items in sections.items():
|
||||
for name in items:
|
||||
key = index_key(section, name)
|
||||
result.setdefault(section, {})[name] = key
|
||||
|
||||
with open(os.path.join(BUILD_DIR, "index.json"), "w") as f:
|
||||
json.dump(result, f)
|
||||
|
||||
|
||||
def load_index(sections, text):
|
||||
def par(m):
|
||||
section = m.group(1)
|
||||
name = m.group(2)
|
||||
sections.setdefault(section, []).append(name)
|
||||
return ""
|
||||
|
||||
def idx(m):
|
||||
name = m.group(2).strip()
|
||||
section = m.group(3)
|
||||
if name == name.upper():
|
||||
name = name.capitalize()
|
||||
sections.setdefault(section, []).append(name)
|
||||
return ""
|
||||
|
||||
text = re.sub(PAR_RE, par, text, flags=re.M)
|
||||
text = re.sub(IDX_RE, idx, text, flags=re.M)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) >= 2 and sys.argv[1] == "--index":
|
||||
main_index(sys.argv[2:])
|
||||
elif len(sys.argv) == 2:
|
||||
main(sys.argv[1:])
|
||||
else:
|
||||
print(__doc__.strip())
|
||||
sys.exit(1)
|
53
doc/input-filter.py
Executable file
53
doc/input-filter.py
Executable file
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- mode: python; coding: utf-8; eval: (blacken-mode); -*-
|
||||
r"""
|
||||
Doxygen input filter that:
|
||||
|
||||
- adds \privatesection to all files
|
||||
- removes macros
|
||||
- parses pulse_module_options and substitutes it into @pulse_module_options@
|
||||
|
||||
This is used for .c files, and causes Doxygen to not include
|
||||
any symbols from them, unless they also appeared in a header file.
|
||||
|
||||
The Pulse module option parsing is used in documentation of Pulseaudio modules.
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
def main():
|
||||
fn = sys.argv[1]
|
||||
with open(fn, "r") as f:
|
||||
text = f.read()
|
||||
|
||||
text = re.sub("#define.*", "", text)
|
||||
|
||||
if "@pulse_module_options@" in text:
|
||||
m = re.search(
|
||||
r"static const char[* ]*const pulse_module_options\s+=\s+(.*?\")\s*;\s*$",
|
||||
text,
|
||||
re.M | re.S,
|
||||
)
|
||||
if m:
|
||||
res = []
|
||||
for line in m.group(1).splitlines():
|
||||
m = re.match(r"\s*\"\s*([a-z0-9_]+)\s*=\s*(.*)\"\s*$", line)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
value = m.group(2).strip().strip("<>")
|
||||
res.append(f"- `{name}`: {value}")
|
||||
|
||||
res = "\n * ".join(res)
|
||||
text = text.replace("@pulse_module_options@", res)
|
||||
|
||||
if os.path.basename(fn).startswith("module-") and fn.endswith(".c"):
|
||||
text = re.sub(r"^ \* ##", r" * #", text, flags=re.M)
|
||||
|
||||
print("/** \\privatesection */")
|
||||
print(text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
100
doc/man-fixup.py
Executable file
100
doc/man-fixup.py
Executable file
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- mode: python; coding: utf-8; eval: (blacken-mode); -*-
|
||||
r"""
|
||||
Fetch right Doxygen man file, replace dummy parts, and fixup nroff
|
||||
"""
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from subprocess import call
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.strip())
|
||||
p.add_argument("htmldir", type=Path)
|
||||
p.add_argument("page")
|
||||
p.add_argument("name")
|
||||
p.add_argument("section")
|
||||
p.add_argument("version")
|
||||
args = p.parse_args()
|
||||
|
||||
page, name, section, version = args.page, args.name, args.section, args.version
|
||||
|
||||
mandir = args.htmldir / ".." / "man" / "man3"
|
||||
fn = mandir / f"{page}.3"
|
||||
|
||||
# Doxygen < 1.9.7 names .md file output differently...
|
||||
if not fn.exists():
|
||||
page2 = page.replace("page_man_", "md_doc_dox_programs_").replace("-", "_")
|
||||
fn = mandir / f"{page2}.3"
|
||||
if not fn.exists():
|
||||
page2 = page.replace("page_man_", "md_doc_dox_config_").replace("-", "_")
|
||||
fn = mandir / f"{page2}.3"
|
||||
else:
|
||||
page2 = None
|
||||
|
||||
try:
|
||||
with open(fn, "r") as f:
|
||||
text = f.read()
|
||||
except:
|
||||
print(f"ERROR: man file {fn} missing!", file=sys.stderr)
|
||||
call(["ls", "-R", str(args.htmldir / ".." / "man")], stdout=sys.stderr)
|
||||
raise
|
||||
|
||||
text = text.replace(page, name)
|
||||
if page2 is not None:
|
||||
text = text.replace(page2, name)
|
||||
|
||||
# Replace bad nroff header
|
||||
text = re.sub(
|
||||
r"^(\.TH[^\n]*)\n",
|
||||
rf'.TH "{name}" {section} "{version}" "PipeWire" \\" -*- nroff -*-\n',
|
||||
text,
|
||||
)
|
||||
|
||||
# Fixup name field (can't be done in Doxygen, otherwise HTML looks bac)
|
||||
text = re.sub(
|
||||
rf"^\.SH NAME\s*\n{name} \\- {name}\s*\n\.PP\n *",
|
||||
rf".SH NAME\n{name} \\- ",
|
||||
text,
|
||||
count=1,
|
||||
flags=re.M,
|
||||
)
|
||||
|
||||
# Add DESCRIPTION section if missing and NAME field has extra stuff
|
||||
if not re.search(r"^\.SH DESCRIPTION\s*\n", text):
|
||||
text = re.sub(
|
||||
r"^(.SH NAME\s*\n[^\.].*\n)\.PP\s*\n([^\.\n ]+)",
|
||||
r"\1.SH DESCRIPTION\n.PP\n\2",
|
||||
text,
|
||||
count=1,
|
||||
flags=re.M,
|
||||
)
|
||||
|
||||
# Upcase titles
|
||||
def upcase(m):
|
||||
return m.group(0).upper()
|
||||
|
||||
text = re.sub(r"^\.SH .*?$", upcase, text, flags=re.M)
|
||||
|
||||
# Replace PW_KEY_*, SPA_KEY_* by their values
|
||||
def pw_key(m):
|
||||
key = m.group(0)
|
||||
key = key.replace("PW_KEY_", "").lower().replace("_", ".")
|
||||
if key in ("protocol", "access", "client.access") or key.startswith("sec."):
|
||||
return f"pipewire.{key}"
|
||||
return key
|
||||
|
||||
def spa_key(m):
|
||||
key = m.group(0)
|
||||
return key.replace("SPA_KEY_", "").lower().replace("_", ".")
|
||||
|
||||
text = re.sub(r"PW_KEY_[A-Z_]+", pw_key, text, flags=re.S)
|
||||
text = re.sub(r"SPA_KEY_[A-Z_]+", spa_key, text, flags=re.S)
|
||||
|
||||
print(text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
288
doc/meson.build
Normal file
288
doc/meson.build
Normal file
|
@ -0,0 +1,288 @@
|
|||
fs = import('fs')
|
||||
|
||||
doxyfile_conf = configuration_data()
|
||||
doxyfile_conf.set('PACKAGE_NAME', meson.project_name())
|
||||
doxyfile_conf.set('PACKAGE_VERSION', meson.project_version())
|
||||
doxyfile_conf.set('top_srcdir', meson.project_source_root())
|
||||
doxyfile_conf.set('top_builddir', meson.project_build_root())
|
||||
doxyfile_conf.set('output_directory', meson.current_build_dir())
|
||||
|
||||
doc_prefix_value = get_option('doc-prefix-value')
|
||||
doc_sysconfdir_value = get_option('doc-sysconfdir-value')
|
||||
|
||||
if doc_prefix_value == '' and doc_sysconfdir_value == ''
|
||||
doc_spa_plugindir = spa_plugindir
|
||||
doc_pipewire_configdir = pipewire_configdir
|
||||
doc_pipewire_confdatadir = pipewire_confdatadir
|
||||
else
|
||||
if doc_prefix_value == ''
|
||||
doc_prefix_value = get_option('prefix')
|
||||
endif
|
||||
if doc_sysconfdir_value == ''
|
||||
doc_sysconfdir_value = get_option('sysconfdir')
|
||||
endif
|
||||
doc_spa_plugindir = doc_prefix_value / get_option('libdir') / spa_name
|
||||
doc_pipewire_configdir = doc_prefix_value / doc_sysconfdir_value / 'pipewire'
|
||||
doc_pipewire_confdatadir = doc_prefix_value / get_option('datadir') / 'pipewire'
|
||||
endif
|
||||
|
||||
doxygen_env = environment()
|
||||
doxygen_env.set('PACKAGE_NAME', meson.project_name())
|
||||
doxygen_env.set('PACKAGE_VERSION', meson.project_version())
|
||||
doxygen_env.set('PACKAGE_URL', 'https://pipewire.org')
|
||||
doxygen_env.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/pipewire/pipewire/issues')
|
||||
doxygen_env.set('PIPEWIRE_CONFIG_DIR', doc_pipewire_configdir)
|
||||
doxygen_env.set('PIPEWIRE_CONFDATADIR', doc_pipewire_confdatadir)
|
||||
doxygen_env.set('SPA_PLUGINDIR', doc_spa_plugindir)
|
||||
doxygen_env.set('BUILD_DIR', meson.current_build_dir())
|
||||
|
||||
dot_found = find_program('dot', required: false).found()
|
||||
summary({'dot (used with doxygen)': dot_found}, bool_yn: true, section: 'Optional programs')
|
||||
if dot_found
|
||||
doxyfile_conf.set('HAVE_DOT', 'YES')
|
||||
else
|
||||
doxyfile_conf.set('HAVE_DOT', 'NO')
|
||||
endif
|
||||
|
||||
# Note: order here is how doxygen will expose the pages in the sidebar
|
||||
# tree.dox should be first to determine the ordering.
|
||||
extra_docs = [
|
||||
'tree.dox',
|
||||
'dox/index.dox',
|
||||
'dox/overview.dox',
|
||||
'dox/modules.dox',
|
||||
'dox/pulse-modules.dox',
|
||||
'dox/programs/index.md',
|
||||
'dox/config/index.md',
|
||||
'dox/config/xref.md',
|
||||
'dox/internals/index.dox',
|
||||
'dox/internals/design.dox',
|
||||
'dox/internals/access.dox',
|
||||
'dox/internals/midi.dox',
|
||||
'dox/internals/portal.dox',
|
||||
'dox/internals/daemon.dox',
|
||||
'dox/internals/library.dox',
|
||||
'dox/internals/session-manager.dox',
|
||||
'dox/internals/objects.dox',
|
||||
'dox/internals/audio.dox',
|
||||
'dox/internals/scheduling.dox',
|
||||
'dox/internals/protocol.dox',
|
||||
'dox/internals/pulseaudio.dox',
|
||||
'dox/internals/dma-buf.dox',
|
||||
'dox/tutorial/index.dox',
|
||||
'dox/tutorial/tutorial1.dox',
|
||||
'dox/tutorial/tutorial2.dox',
|
||||
'dox/tutorial/tutorial3.dox',
|
||||
'dox/tutorial/tutorial4.dox',
|
||||
'dox/tutorial/tutorial5.dox',
|
||||
'dox/tutorial/tutorial6.dox',
|
||||
'dox/api/index.dox',
|
||||
'dox/api/spa-index.dox',
|
||||
'dox/api/spa-plugins.dox',
|
||||
'dox/api/spa-design.dox',
|
||||
'dox/api/spa-pod.dox',
|
||||
'dox/api/spa-buffer.dox',
|
||||
]
|
||||
|
||||
manpage_docs = [
|
||||
'dox/config/pipewire-pulse.conf.5.md',
|
||||
'dox/config/pipewire.conf.5.md',
|
||||
'dox/config/pipewire-client.conf.5.md',
|
||||
'dox/config/pipewire-jack.conf.5.md',
|
||||
'dox/config/pipewire-props.7.md',
|
||||
'dox/config/pipewire-filter-chain.conf.5.md',
|
||||
'dox/config/pipewire-pulse-modules.7.md',
|
||||
'dox/config/libpipewire-modules.7.md',
|
||||
'dox/programs/pipewire-pulse.1.md',
|
||||
'dox/programs/pipewire.1.md',
|
||||
'dox/programs/pw-cat.1.md',
|
||||
'dox/programs/pw-cli.1.md',
|
||||
'dox/programs/pw-config.1.md',
|
||||
'dox/programs/pw-container.1.md',
|
||||
'dox/programs/pw-dot.1.md',
|
||||
'dox/programs/pw-dump.1.md',
|
||||
'dox/programs/pw-jack.1.md',
|
||||
'dox/programs/pw-link.1.md',
|
||||
'dox/programs/pw-loopback.1.md',
|
||||
'dox/programs/pw-metadata.1.md',
|
||||
'dox/programs/pw-mididump.1.md',
|
||||
'dox/programs/pw-mon.1.md',
|
||||
'dox/programs/pw-profiler.1.md',
|
||||
'dox/programs/pw-reserve.1.md',
|
||||
'dox/programs/pw-top.1.md',
|
||||
'dox/programs/pw-v4l2.1.md',
|
||||
'dox/programs/spa-acp-tool.1.md',
|
||||
'dox/programs/spa-inspect.1.md',
|
||||
'dox/programs/spa-json-dump.1.md',
|
||||
'dox/programs/spa-monitor.1.md',
|
||||
'dox/programs/spa-resample.1.md',
|
||||
]
|
||||
|
||||
manpages = []
|
||||
|
||||
foreach m : manpage_docs
|
||||
name = fs.stem(fs.name(m))
|
||||
pagepart = name.replace('.', '_')
|
||||
manpages += [[name, f'page_man_@pagepart@']]
|
||||
extra_docs += m
|
||||
endforeach
|
||||
|
||||
inputs = []
|
||||
foreach extra : extra_docs
|
||||
inputs += meson.project_source_root() / 'doc' / extra
|
||||
endforeach
|
||||
foreach h : pipewire_headers
|
||||
inputs += meson.project_source_root() / 'src' / 'pipewire' / h
|
||||
endforeach
|
||||
foreach h : pipewire_ext_headers
|
||||
inputs += meson.project_source_root() / 'src' / 'pipewire' / 'extensions' / h
|
||||
endforeach
|
||||
foreach h : pipewire_ext_sm_headers
|
||||
inputs += meson.project_source_root() / 'src' / 'pipewire' / 'extensions' / h
|
||||
endforeach
|
||||
foreach h : pipewire_sources
|
||||
inputs += meson.project_source_root() / 'src' / 'pipewire' / h
|
||||
endforeach
|
||||
foreach h : module_sources
|
||||
inputs += meson.project_source_root() / 'src' / 'modules' / h
|
||||
endforeach
|
||||
foreach h : pipewire_module_protocol_pulse_sources
|
||||
inputs += meson.project_source_root() / 'src' / 'modules' / h
|
||||
endforeach
|
||||
input_dirs = [ meson.project_source_root() / 'spa' / 'include' / 'spa' ]
|
||||
|
||||
path_prefixes = [
|
||||
meson.project_source_root() / 'src',
|
||||
meson.project_source_root() / 'spa' / 'include',
|
||||
meson.project_source_root(),
|
||||
]
|
||||
|
||||
cssfiles = [
|
||||
meson.project_source_root() / 'doc' / 'doxygen-awesome.css',
|
||||
meson.project_source_root() / 'doc' / 'custom.css'
|
||||
]
|
||||
|
||||
# Example files (in order from simple to esoteric)
|
||||
example_files = [
|
||||
'tutorial1.c',
|
||||
'tutorial2.c',
|
||||
'tutorial3.c',
|
||||
'tutorial4.c',
|
||||
'tutorial5.c',
|
||||
'tutorial6.c',
|
||||
]
|
||||
example_dep_files = []
|
||||
foreach h : example_files
|
||||
example_dep_files += ['examples/' + h]
|
||||
endforeach
|
||||
foreach h : examples
|
||||
example_files += [h + '.c']
|
||||
example_dep_files += ['../src/examples/' + h + '.c']
|
||||
endforeach
|
||||
foreach h : spa_examples
|
||||
example_files += ['spa/examples/' + h + '.c']
|
||||
example_dep_files += ['../spa/examples/' + h + '.c']
|
||||
endforeach
|
||||
|
||||
example_doxygen = []
|
||||
example_ref = []
|
||||
foreach h : example_files
|
||||
example_doxygen += ['\\example ' + h,
|
||||
'\\snippet{doc} ' + h + ' title',
|
||||
'<br>',
|
||||
'\\snippet{doc} ' + h + ' doc']
|
||||
example_ref += ['- \\ref ' + h + ' "": \snippet{doc} ' + h + ' title']
|
||||
endforeach
|
||||
|
||||
examples_dox_conf = configuration_data()
|
||||
examples_dox_conf.set('example_doxygen', '\n'.join(example_doxygen))
|
||||
examples_dox_conf.set('example_ref', '\n'.join(example_ref))
|
||||
examples_dox = configure_file(input: 'examples.dox.in',
|
||||
output: 'examples.dox',
|
||||
configuration: examples_dox_conf)
|
||||
|
||||
input_dirs += [ 'doc/examples.dox' ]
|
||||
|
||||
module_manpage_list = []
|
||||
foreach m : module_sources
|
||||
name = fs.stem(m)
|
||||
pagepart = name.replace('-', '_')
|
||||
module_manpage_list += f'\\ref page_@pagepart@ "libpipewire-@name@(7)"'
|
||||
manpages += [[f'libpipewire-@name@.7', f'page_@pagepart@']]
|
||||
endforeach
|
||||
|
||||
doxygen_env.set('LIBPIPEWIRE_MODULES', '<ul><li>' + '</li><li>'.join(module_manpage_list) + '</li></ul>')
|
||||
|
||||
pulse_module_manpage_list = []
|
||||
foreach m : pipewire_module_protocol_pulse_sources
|
||||
name = fs.stem(fs.name(m))
|
||||
if m.contains('/modules/') and name.startswith('module-')
|
||||
pagepart = name.replace('-', '_')
|
||||
pulse_module_manpage_list += f'\\ref page_pulse_@pagepart@ "pipewire-pulse-@name@(7)"'
|
||||
manpages += [[f'pipewire-pulse-@name@.7', f'page_pulse_@pagepart@']]
|
||||
endif
|
||||
endforeach
|
||||
|
||||
doxygen_env.set('PIPEWIRE_PULSE_MODULES', '<ul><li>' + '</li><li>'.join(pulse_module_manpage_list) + '</li></ul>')
|
||||
|
||||
doxygen_layout = meson.project_source_root() / 'doc' / 'DoxygenLayout.xml'
|
||||
doxygen_filter_c = meson.project_source_root() / 'doc' / 'input-filter.py'
|
||||
doxygen_filter_h = meson.project_source_root() / 'doc' / 'input-filter-h.sh'
|
||||
doxygen_filter_md = meson.project_source_root() / 'doc' / 'input-filter-md.py'
|
||||
|
||||
doxyfile_conf.set('inputs', ' '.join(inputs + input_dirs))
|
||||
doxyfile_conf.set('cssfiles', ' '.join(cssfiles))
|
||||
doxyfile_conf.set('layout', doxygen_layout)
|
||||
doxyfile_conf.set('path_prefixes', ' '.join(path_prefixes))
|
||||
doxyfile_conf.set('c_input_filter', doxygen_filter_c)
|
||||
doxyfile_conf.set('h_input_filter', doxygen_filter_h)
|
||||
doxyfile_conf.set('md_input_filter', doxygen_filter_md)
|
||||
|
||||
doxyfile = configure_file(input: 'Doxyfile.in',
|
||||
output: 'Doxyfile',
|
||||
configuration: doxyfile_conf)
|
||||
|
||||
docdir = get_option('docdir')
|
||||
if docdir == ''
|
||||
docdir = pipewire_datadir / 'doc' / meson.project_name()
|
||||
endif
|
||||
|
||||
index_json = custom_target('index.json',
|
||||
command: [ doxygen_filter_md, '--index', '@INPUT@' ],
|
||||
input: extra_docs + manpage_docs,
|
||||
output: 'index.json',
|
||||
env: doxygen_env
|
||||
)
|
||||
|
||||
html_target = custom_target('pipewire-docs',
|
||||
input: [ doxyfile, doxygen_layout, example_dep_files, examples_dox,
|
||||
doxygen_filter_c, doxygen_filter_h, index_json ] + inputs + cssfiles,
|
||||
output: [ 'html' ],
|
||||
command: [ doxygen, doxyfile ],
|
||||
env: doxygen_env,
|
||||
install: install_docs,
|
||||
install_tag: 'doc',
|
||||
install_dir: docdir)
|
||||
|
||||
|
||||
man_fixup = files('man-fixup.py')
|
||||
|
||||
manfiles = []
|
||||
|
||||
foreach m : manpages
|
||||
file = m.get(0)
|
||||
page = m.get(1)
|
||||
name = fs.stem(file)
|
||||
section = file.split('.').get(-1)
|
||||
|
||||
manfiles += custom_target(file,
|
||||
command : [ python, man_fixup, '@INPUT@', page, name, section, meson.project_version() ],
|
||||
output : file,
|
||||
input : html_target,
|
||||
depend_files : [ man_fixup ],
|
||||
capture : true,
|
||||
install : install_man,
|
||||
install_tag: 'man',
|
||||
install_dir : get_option('mandir') / 'man' + section
|
||||
)
|
||||
endforeach
|
134
doc/tree.dox
Normal file
134
doc/tree.dox
Normal file
|
@ -0,0 +1,134 @@
|
|||
/**
|
||||
|
||||
This determines the ordering of items in Doxygen sidebar.
|
||||
|
||||
\defgroup api_pw_core Core API
|
||||
\brief PipeWire Core API
|
||||
\{
|
||||
\addtogroup pw_pipewire
|
||||
\addtogroup pw_main_loop
|
||||
\addtogroup pw_context
|
||||
\addtogroup pw_client
|
||||
\addtogroup pw_core
|
||||
\addtogroup pw_device
|
||||
\addtogroup pw_factory
|
||||
\addtogroup pw_link
|
||||
\addtogroup pw_loop
|
||||
\addtogroup pw_module
|
||||
\addtogroup pw_node
|
||||
\addtogroup pw_permission
|
||||
\addtogroup pw_port
|
||||
\addtogroup pw_proxy
|
||||
\addtogroup pw_registry
|
||||
\addtogroup pw_type
|
||||
\addtogroup pw_keys
|
||||
\}
|
||||
|
||||
\defgroup api_pw_impl Implementation API
|
||||
\brief PipeWire Object Implementation API
|
||||
\{
|
||||
\addtogroup pw_impl_client
|
||||
\addtogroup pw_impl_core
|
||||
\addtogroup pw_impl_device
|
||||
\addtogroup pw_impl_factory
|
||||
\addtogroup pw_impl_link
|
||||
\addtogroup pw_impl_metadata
|
||||
\addtogroup pw_impl_module
|
||||
\addtogroup pw_impl_node
|
||||
\addtogroup pw_impl_port
|
||||
\addtogroup pw_buffers
|
||||
\addtogroup pw_control
|
||||
\addtogroup pw_data_loop
|
||||
\addtogroup pw_global
|
||||
\addtogroup pw_protocol
|
||||
\addtogroup pw_resource
|
||||
\addtogroup pw_thread_loop
|
||||
\addtogroup pw_work_queue
|
||||
\}
|
||||
|
||||
\defgroup api_pw_util Utilities
|
||||
\brief PipeWire Utilities
|
||||
\{
|
||||
\addtogroup pw_array
|
||||
\addtogroup pw_conf
|
||||
\addtogroup pw_gettext
|
||||
\addtogroup pw_log
|
||||
\addtogroup pw_map
|
||||
\addtogroup pw_memblock
|
||||
\addtogroup pw_properties
|
||||
\addtogroup pw_thread
|
||||
\addtogroup pw_utils
|
||||
\}
|
||||
|
||||
\defgroup api_pw_ext Extensions
|
||||
\brief PipeWire Extensions
|
||||
\{
|
||||
\addtogroup pw_client_node
|
||||
\addtogroup pw_metadata
|
||||
\addtogroup pw_profiler
|
||||
\addtogroup pw_protocol_native
|
||||
\addtogroup pw_session_manager
|
||||
\}
|
||||
|
||||
\defgroup api_spa SPA
|
||||
\brief Simple Plugin API
|
||||
\{
|
||||
\addtogroup spa_buffer
|
||||
\addtogroup spa_control
|
||||
\addtogroup spa_debug
|
||||
\addtogroup spa_device
|
||||
\addtogroup spa_graph
|
||||
\addtogroup spa_node
|
||||
\addtogroup spa_param
|
||||
\addtogroup spa_pod
|
||||
\defgroup spa_utils Utilities
|
||||
Utility data structures, macros, etc.
|
||||
\{
|
||||
\addtogroup spa_ansi
|
||||
\addtogroup spa_utils_defs
|
||||
\addtogroup spa_dict
|
||||
\addtogroup spa_list
|
||||
\addtogroup spa_hooks
|
||||
\addtogroup spa_interfaces
|
||||
\addtogroup spa_json
|
||||
\addtogroup spa_json_pod
|
||||
\addtogroup spa_keys
|
||||
\addtogroup spa_names
|
||||
\addtogroup spa_result
|
||||
\addtogroup spa_ringbuffer
|
||||
\addtogroup spa_string
|
||||
\addtogroup spa_types
|
||||
\}
|
||||
\defgroup spa_support Support
|
||||
Support interfaces provided by host
|
||||
\{
|
||||
\addtogroup spa_cpu
|
||||
\addtogroup spa_dbus
|
||||
\addtogroup spa_i18n
|
||||
\addtogroup spa_log
|
||||
\addtogroup spa_loop
|
||||
\addtogroup spa_handle
|
||||
\addtogroup spa_plugin_loader
|
||||
\addtogroup spa_system
|
||||
\addtogroup spa_thread
|
||||
\}
|
||||
\}
|
||||
|
||||
\defgroup pw_stream Stream
|
||||
\{
|
||||
\}
|
||||
|
||||
\defgroup pw_filter Filter
|
||||
\{
|
||||
\}
|
||||
|
||||
\page page_overview
|
||||
\page page_config
|
||||
\page page_programs
|
||||
\page page_modules
|
||||
\page page_pulse_modules
|
||||
\page page_internals
|
||||
\page page_api
|
||||
\page page_tutorial
|
||||
|
||||
*/
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue