Import Upstream version 0.3.48

This commit is contained in:
Lu zhiping 2022-06-07 09:31:28 +08:00
commit ad5582a6b5
954 changed files with 344474 additions and 0 deletions

24
.cirrus.yml Normal file
View File

@ -0,0 +1,24 @@
task:
freebsd_instance:
matrix:
- image_family: freebsd-13-0-snap
env:
# /usr/ports/Mk/Uses/localbase.mk localbase:ldflags
LOCALBASE: /usr/local
CFLAGS: -isystem $LOCALBASE/include
CPPFLAGS: $CFLAGS
CXXFLAGS: $CFLAGS
LDFLAGS: -L$LOCALBASE/lib
deps_script:
- sed -i.bak -e 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
- pkg install -y meson pkgconf git-lite dbus glib libepoll-shim libudev-devd vulkan-loader vulkan-headers v4l_compat gstreamer1 gstreamer1-plugins libinotify gettext libsndfile sdl2 alsa-lib
- sysrc dbus_enable=YES
- service dbus restart
build_script:
- mkdir build
- cd build
- meson setup -Dalsa=enabled -Draop=enabled -Dv4l2=enabled -Dpipewire-alsa=enabled -Dbluez5=disabled -Djack=disabled -Dpipewire-jack=enabled -Dpw-cat=enabled -Dpipewire-v4l2=disabled -Dsdl2=enabled -Dsystemd=disabled -Dsession-managers=media-session ..
- ninja
test_script:
- cd build
- ninja test

20
.codespell-ignore Normal file
View 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
View 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

43
.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
.tarball-version
.version
.*.swp
ABOUT-NLS
*~
*.tar.gz
*.tar.xz
*.o
build/
builddir/
config.h.meson
cscope.out
cscope.in.out
cscope.po.out
Makefile
subprojects/lua*
subprojects/wireplumber
subprojects/media-session
subprojects/packagecache
# 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

446
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,446 @@
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: '2022-02-16.0'
FDO_DISTRIBUTION_VERSION: '35'
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
libcanberra-devel
libldac-devel
libsndfile-devel
libusb-devel
lilv-devel
libv4l-devel
libva-devel
libX11-devel
openssl-devel
pulseaudio-libs-devel
python3-docutils
sbc-devel
ShellCheck
SDL2-devel
systemd-devel
vulkan-loader-devel
webrtc-audio-processing-devel
which
valgrind
ninja-build
pkgconf
python3-pip
FDO_DISTRIBUTION_EXEC: >-
pip3 install meson
.ubuntu:
variables:
# Update this tag when you want to trigger a rebuild
FDO_DISTRIBUTION_TAG: '2022-01-27.0'
FDO_DISTRIBUTION_VERSION: '20.04'
FDO_DISTRIBUTION_PACKAGES: >-
debhelper-compat
findutils
git
libasound2-dev
libavcodec-dev
libavfilter-dev
libavformat-dev
libdbus-1-dev
libglib2.0-dev
libgstreamer1.0-dev
libgstreamer-plugins-base1.0-dev
libsbc-dev
libsdl2-dev
libudev-dev
libva-dev
libv4l-dev
libx11-dev
ninja-build
pkg-config
python3-docutils
systemd
python3-pip
FDO_DISTRIBUTION_EXEC: >-
pip3 install meson
.alpine:
variables:
# Update this tag when you want to trigger a rebuild
FDO_DISTRIBUTION_TAG: '2022-01-28.2'
FDO_DISTRIBUTION_VERSION: '3.15'
FDO_DISTRIBUTION_PACKAGES: >-
alsa-lib-dev
avahi-dev
bash
bluez-dev
gcc
g++
dbus-dev
doxygen
eudev-dev
fdk-aac-dev
git
glib-dev
graphviz
gst-plugins-base-dev
gstreamer-dev
jack-dev
libfreeaptx-dev
libusb-dev
libx11-dev
meson
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
only:
variables:
- $COVERITY
.not_coverity:
except:
variables:
- $COVERITY
.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
NINJA_ARGS="-j$FDO_CI_CONCURRENT $NINJA_ARGS"
export NINJA_ARGS
fi
script:
- echo "Building with meson options $MESON_OPTIONS"
- meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
- ninja $NINJA_ARGS -C "$BUILD_DIR"
- ninja $NINJA_ARGS -C "$BUILD_DIR" test
- ninja $NINJA_ARGS -C "$BUILD_DIR" install
- ./check_missing_headers.sh
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
.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
-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
artifacts:
name: pipewire-$CI_COMMIT_SHA
when: always
paths:
- build-*/meson-logs
- prefix-*
build_on_alpine:
extends:
- .alpine
- .not_coverity
- .fdo.distribution-image@alpine
- .build
stage: build
# build with all auto() options enabled
build_all:
extends:
- .build_on_fedora
variables:
# Fedora doesn't have libfreeaptx or roc
# libcamera has no stable API, so let's not chase that target
MESON_OPTIONS: "-Dauto_features=enabled -Dbluez5-codec-aptx=disabled -Droc=disabled -Dlibcamera=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: ""
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 "$BUILD_DIR" . --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE"
- ninja $NINJA_ARGS -C "$BUILD_DIR"
- ninja $NINJA_ARGS -C "$BUILD_DIR" test
# 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"
parallel:
matrix:
- CC: [gcc, clang]
build_session_managers:
extends:
- .build_on_fedora
script:
- echo "Building with meson options $MESON_OPTIONS"
- meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
- ninja $NINJA_ARGS -C "$BUILD_DIR"
- ninja $NINJA_ARGS -C "$BUILD_DIR" install
variables:
MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS"
parallel:
matrix:
- SESSION_MANAGERS: ["[]", "wireplumber", "media-session", "media-session,wireplumber", "wireplumber,media-session" ]
allow_failure: true
build_meson_prerelease:
extends:
- .build_on_fedora
script:
- pip3 install --upgrade --pre meson
- echo "Building with meson options $MESON_OPTIONS"
- meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
- ninja $NINJA_ARGS -C "$BUILD_DIR"
- ninja $NINJA_ARGS -C "$BUILD_DIR" install
variables:
MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session"
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)
- pip3 uninstall --yes meson
- pip3 install "meson==$meson_version"
- echo "Building with meson options $MESON_OPTIONS"
- meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
- ninja $NINJA_ARGS -C "$BUILD_DIR"
- ninja $NINJA_ARGS -C "$BUILD_DIR" install
variables:
MESON_OPTIONS: "-Dsession-managers=[]"
valgrind:
extends:
- .build_on_fedora
script:
- echo "Building with meson options $MESON_OPTIONS"
- meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
- meson test -C "$BUILD_DIR" --setup=valgrind
build_with_coverity:
extends:
- .fedora
- .coverity
- .fdo.suffixed-image@fedora
- .build
stage: analysis
script:
- export PATH=/opt/coverity/bin:$PATH
- meson "$BUILD_DIR" . --prefix="$PREFIX"
-Ddocs=disabled
-Dbluez5-backend-hsphfpd=enabled
-Daudiotestsrc=enabled
-Dtest=enabled
-Dvideotestsrc=enabled
-Dvolume=enabled
-Dvulkan=enabled
-Dsdl2=enabled
-Dsndfile=enabled
- 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 ninja $NINJA_ARGS -C "$BUILD_DIR"
- 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
script:
- shellcheck $(git grep -l "#\!/.*bin/.*sh")
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
pages:
extends:
- .not_coverity
stage: pages
dependencies:
- build_on_fedora
script:
- mkdir public
- cp -R prefix-*/share/doc/pipewire/html/* public/
artifacts:
paths:
- public
only:
- master

View File

View 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):

View 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
View 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
View 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

219
INSTALL.md Normal file
View File

@ -0,0 +1,219 @@
## Building
PipeWire uses a build tool called *Meson* 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*,
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:
```
$ ninja -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 environment
variable like so:
```
cd builddir/
PIPEWIRE_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
inside `builddir`:
```
sudo meson install
```
to install everything onto the system into the specified prefix.
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.

11
LICENSE Normal file
View 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

74
Makefile.in Normal file
View File

@ -0,0 +1,74 @@
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 \
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 \
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 valgrind --trace-children=yes"
test: all
ninja -C $(BUILD_ROOT) test
benchmark: all
ninja -C $(BUILD_ROOT) benchmark
monitor: all
SPA_PLUGIN_DIR=$(BUILD_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 \
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

3447
NEWS Normal file

File diff suppressed because it is too large Load Diff

211
README.md Normal file
View File

@ -0,0 +1,211 @@
# 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 attempt to change
the graph samplerate to `denom` and use the
specified `num` as the buffer size.
* `PIPEWIRE_NODE=<id>` to request a link to the specified 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.md).
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).

18
autogen.sh Executable file
View 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

28
check_missing_headers.sh Executable file
View 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

69
doc/Doxyfile.in Normal file
View File

@ -0,0 +1,69 @@
PROJECT_NAME = PipeWire
PROJECT_NUMBER = @PACKAGE_VERSION@
OUTPUT_DIRECTORY = doc
FULL_PATH_NAMES = NO
JAVADOC_AUTOBRIEF = YES
TAB_SIZE = 8
OPTIMIZE_OUTPUT_FOR_C = YES
EXTRACT_ALL = YES
EXTRACT_STATIC = YES
FULL_PATH_NAMES = 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
QUIET = YES
WARN_NO_PARAMDOC = YES
HAVE_DOT = @HAVE_DOT@
INPUT = @inputs@
FILTER_PATTERNS = "*.c=@c_input_filter@" "*.h=@h_input_filter@"
FILE_PATTERNS = "*.h" "*.c"
RECURSIVE = YES
EXAMPLE_PATH = "@top_srcdir@/src/examples" \
"@top_srcdir@/spa/examples" \
"@top_srcdir@/doc"
EXAMPLE_PATTERNS = "*.c"
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
IGNORE_PREFIX = pw_ \
PW_ \
spa_ \
SPA_
GENERATE_TREEVIEW = YES
SEARCHENGINE = NO
GENERATE_LATEX = NO
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"

126
doc/api-tree.dox Normal file
View File

@ -0,0 +1,126 @@
/**
\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_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
\{
\}
\defgroup pwtest Test Suite
\{
\}
*/

89
doc/api.dox Normal file
View File

@ -0,0 +1,89 @@
/** \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_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
*/

19
doc/custom.css Normal file
View File

@ -0,0 +1,19 @@
: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;
}
}

154
doc/dma-buf.dox Normal file
View File

@ -0,0 +1,154 @@
/** \page page_dma_buf DMA-BUF sharing
PipeWire supports sharing Direct Memory Access buffers (DMA-BUFs) between
clients via the `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, which has to be invoked with the intersection
of all supported modifiers of all clients. As a result the fixation of the
modifier has to be delegated from PipeWire to the node responsible for
allocating the buffers.
## pw_stream_connect
The stream parameters should contain two `SPA_PARAM_EnumFormat` objects for
each format: the first one is used for DMA-BUFs, the second one for shared
memory buffers as a fallback.
Query the list of all supported modifiers from your graphics API of choice.
Add a `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 `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 `SPA_POD_PROP_FLAG_DONT_FIXATE` (see param_changed hook, For producers).
The second stream parameter should not contain any `SPA_FORMAT_VIDEO_modifier`
property.
To prioritise DMA-BUFs place those `SPA_PARAM_EnumFormat` containing modifiers
first, when emitting them to PipeWire.
## param_changed hook
When the `param_changed` hook is called for a `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
`SPA_PARAM_BUFFERS_dataType` property to `1 << SPA_DATA_DmaBuf`. If they were
not negotiated, fall back to shared memory by setting the
`SPA_PARAM_BUFFERS_dataType` property to `1 << SPA_DATA_MemFd`,
`1 << SPA_DATA_MemPtr`, or both.
While consumers only have to parse the resulting `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 `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 `SPA_PARAM_Format` contains the modifier
property as described above. Is that 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
`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 `EnumFormat` by announcing a `SPA_PARAM_EnumFormat` with
only one modifier in the `SPA_CHOICE_Enum` and without the
`SPA_POD_PROP_FLAG_DONT_FIXATE` flag, followed by the previous announced
`EnumFormat`s. This will retrigger the `param_changed` event with an
`SPA_PARAM_Format` as described below.
If the `SPA_PARAM_Format` contains a modifier key, without the flag
`SPA_POD_PROP_FLAG_DONT_FIXATE`, it should only contain one value in the
`SPA_CHOICE_Enum`. In this case announce the `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.
Note: When test allocating a buffer, collect all possible modifiers, while omitting
`DRM_FORMAT_MOD_INVALID` from the `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 `SPA_DATA_MemFd` or
`SPA_DATA_MemPtr` use the fallback SHM import mechanism.
If it's `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: 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.
# 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.
*/

1364
doc/doxygen-awesome.css Normal file

File diff suppressed because it is too large Load Diff

9
doc/examples.dox.in Normal file
View File

@ -0,0 +1,9 @@
/**
\page page_examples List of example programs
@example_ref@
@example_doxygen@
*/

40
doc/index.dox Normal file
View File

@ -0,0 +1,40 @@
/** \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.
### 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_tools 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
### More Documentation
See our [Wiki](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home) for
More information on how to configure and use PipeWire.
### Resources
- [LAC 2020 Paper](https://lac2020.sciencesconf.org/307881/document)
- [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)
*/

32
doc/input-filter-h.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/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/@;')
echo "/** \file"
echo "\`$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.
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@;' \
< "$FILENAME"

10
doc/input-filter.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
#
# Doxygen input filter that adds \privatesection to all files,
# and removes macros.
#
# This is used for .c files, and causes Doxygen to not include
# any symbols from them, unless they also appeared in a header file.
#
echo -n "/** \privatesection */ "
sed -e 's/#define.*//' < "$1"

5
doc/manpage.dox.in Normal file
View File

@ -0,0 +1,5 @@
/** \page @pagename@ @title@
\verbinclude @filename@
*/

161
doc/meson.build Normal file
View File

@ -0,0 +1,161 @@
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())
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
# api-tree.dox should be first to determine ordering of Modules.
extra_docs = [
'api-tree.dox',
'index.dox',
'overview.dox',
'pipewire.dox',
'pipewire-design.dox',
'pipewire-access.dox',
'pipewire-midi.dox',
'pipewire-portal.dox',
'pipewire-daemon.dox',
'pipewire-library.dox',
'pipewire-modules.dox',
'pipewire-session-manager.dox',
'pipewire-objects-design.dox',
'pipewire-audio.dox',
'tutorial.dox',
'tutorial1.dox',
'tutorial2.dox',
'tutorial3.dox',
'tutorial4.dox',
'tutorial5.dox',
'tutorial6.dox',
'api.dox',
'spa-index.dox',
'spa-plugins.dox',
'spa-design.dox',
'spa-pod.dox',
'spa-buffer.dox',
'pulseaudio.dox',
'dma-buf.dox',
]
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
inputs += meson.project_source_root() / 'test' / 'pwtest.h'
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',
]
foreach h : examples
example_files += [h + '.c']
endforeach
foreach h : spa_examples
example_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' ]
man_doxygen = []
man_subpages = []
foreach m : manpages
manconf = configuration_data()
pagename = 'page_man_' + m.split('.rst.in').get(0).replace('.', '_').replace('-', '_')
filename = m.split('.rst.in').get(0) + '.dox'
manconf.set('pagename', pagename)
manconf.set('title', m.split('.rst.in').get(0).replace('.1','').replace('.5',''))
manconf.set('filename', meson.project_source_root() / 'man' / m)
manfile = configure_file(input: 'manpage.dox.in',
output: filename,
configuration: manconf)
man_doxygen += [manfile]
man_subpages += ['- \subpage ' + pagename]
input_dirs += [ 'doc/' + filename ]
endforeach
pw_tools_dox_conf = configuration_data()
pw_tools_dox_conf.set('man_subpages', '\n'.join(man_subpages))
pw_tools_dox = configure_file(input: 'pipewire-tools.dox.in',
output: 'pipewire-tools.dox',
configuration: pw_tools_dox_conf)
input_dirs += [ 'doc/pipewire-tools.dox' ]
doxyfile_conf.set('inputs', ' '.join(inputs + input_dirs))
doxyfile_conf.set('cssfiles', ' '.join(cssfiles))
doxyfile_conf.set('path_prefixes', ' '.join(path_prefixes))
doxyfile_conf.set('c_input_filter', meson.project_source_root() / 'doc' / 'input-filter.sh')
doxyfile_conf.set('h_input_filter', meson.project_source_root() / 'doc' / 'input-filter-h.sh')
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
html_target = custom_target('pipewire-docs',
input: [ doxyfile, examples_dox, pw_tools_dox ] + inputs + cssfiles + man_doxygen,
output: [ 'html' ],
command: [ doxygen, doxyfile ],
install: true,
install_dir: docdir)

42
doc/overview.dox Normal file
View File

@ -0,0 +1,42 @@
/** \page page_overview Overview
PipeWire is a new low-level multimedia framework designed from scratch that
aims to provide
- 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
- achieve very low-latency for both audio and video processing
The framework is used to build a modular daemon that can be configured to:
- be a low-latency audio server with features like pulseaudio and/or jack
- a video capture server that can manage hardware video capture devices and
provide access to them
- a central hub where video can be made available for other applications
such as the gnome-shell screencast API.
## Motivation
Linux has no unified framework for exchanging multimedia content between
applications or even devices. In most cases, developers realized that
a user-space daemon is needed to make this possible:
- For video content, we typically rely on the compositor to render our
data.
- For video capture, we usually go directly to the hardware devices, with
all security implications and inflexible routing that this brings.
- For consumer audio, we use PulseAudio to manage and mix multiple streams
from clients
- For Pro audio, we use JACK to manage the graph of nodes.
None of these solutions (except perhaps to some extent Wayland), however,
were designed to support the security features that are required when
dealing with flatpaks or other containerized applications. PipeWire
aims to solve this problem and provides a unified framework to run both
consumer and Pro audio as well as video capture and processing in a
secure way.
*/

126
doc/pipewire-access.dox Normal file
View File

@ -0,0 +1,126 @@
/** \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
(e.g. 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, particularly on the values documented below. Depending on the
value of the \ref PW_KEY_ACCESS property one the following happens:
- `"allowed"`, `"unrestricted"`: ALL permissions are set on the core
object and the client will be able to resume.
- `"restricted"`, `"flatpak"`, `"$access.force"`: no permissions are set on
the core object and the client will be suspended.
- `"rejected"`: an error is sent to the client and the client is
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 suspended with `"restricted"`, `"flatpak"` or
`"$access.force"` access, 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 (i.e. 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.
*/

View File

128
doc/pipewire-audio.dox Normal file
View File

@ -0,0 +1,128 @@
/** \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 (e.g. 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 Node that it creates to control their behavior. This
is implemented with match rules.
*/

179
doc/pipewire-daemon.dox Normal file
View File

@ -0,0 +1,179 @@
/** \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 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.
\section sec_config 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 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 config
directory, subdirectory and filename, respectively.
\subsection sec_config_format 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.
\section sec_logging Logging
The `PIPEWIRE_DEBUG` environment variable can be used to enable
more debugging. This variable supports one of two formats:
- `PIPEWIRE_DEBUG=<level>` where `<level>` is either a numerical log level or it's
respective key, see below.
- `PIPEWIRE_DEBUG=<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. 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
*/

79
doc/pipewire-design.dox Normal file
View File

@ -0,0 +1,79 @@
/** \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.
- Pro 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 datastructures
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_pipewire_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
*/

242
doc/pipewire-library.dox Normal file
View File

@ -0,0 +1,242 @@
/** \page page_library PipeWire Library
There are 2 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 advertized 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.
*/

102
doc/pipewire-midi.dox Normal file
View File

@ -0,0 +1,102 @@
/** \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 autoconnect 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.
## 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
## pipewire-media-session
PipeWire media session 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 types. On output ports, the JACK
event stream is converted to control messages in a similar way.
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.
*/

83
doc/pipewire-modules.dox Normal file
View File

@ -0,0 +1,83 @@
/**
\page page_pipewire_modules PipeWire 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 }
...
]
```
And 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_client_device
- \subpage page_module_client_node
- \subpage page_module_echo_cancel
- \subpage page_module_example_sink
- \subpage page_module_example_source
- \subpage page_module_fallback_sink
- \subpage page_module_filter_chain
- \subpage page_module_link_factory
- \subpage page_module_loopback
- \subpage page_module_metadata
- \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_rt
- \subpage page_module_session_manager
- \subpage page_module_x11_bell
- \subpage page_module_zeroconf_discover
*/

View File

@ -0,0 +1,333 @@
/** \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 2 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 2 streams from 2 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 she wants 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 (i.e. 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 2 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 APIs
### 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/pipewire-portal.dox Normal file
View 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/portal-docs.html#gdbus-org.freedesktop.portal.Camera)
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/portal-docs.html#gdbus-org.freedesktop.impl.portal.PermissionStore).
\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.
*/

View File

@ -0,0 +1,43 @@
/** \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
consistent 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
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.
*/

View File

@ -0,0 +1,7 @@
/** \page page_tools PipeWire Tools
Manual pages:
@man_subpages@
*/

24
doc/pipewire.dox Normal file
View File

@ -0,0 +1,24 @@
/** \page page_pipewire PipeWire Design
## 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
## Components
- \subpage page_daemon
- \subpage page_tools
- \subpage page_session_manager
## Backends
- \subpage page_pulseaudio
*/

72
doc/pulseaudio.dox Normal file
View File

@ -0,0 +1,72 @@
/** \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 1 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 1 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.
*/

71
doc/spa-buffer.dox Normal file
View 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 herself.
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/spa-design.dox Normal file
View 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 statis 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
*/

84
doc/spa-index.dox Normal file
View File

@ -0,0 +1,84 @@
/** \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 and 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
*/

355
doc/spa-plugins.dox Normal file
View File

@ -0,0 +1,355 @@
/** \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_PATH/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_PATH=$(pkg-config --variable plugindir libspa-0.2)
$ spa-inspect ${SPA_PLUGIN_PATH}/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 2 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_PATH /usr/lib64/spa-0.2/"
void *hnd = dlopen(SPA_PLUGIN_PATH"/support/libspa-support.so", RTLD_NOW);
\endcode
The environment variable `SPA_PLUGIN_PATH` 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
*/

526
doc/spa-pod.dox Normal file
View File

@ -0,0 +1,526 @@
/** \page page_spa_pod SPA POD
\ref spa_pod (plain old data) is a sort of data container. It is comparable to
DBus Variant or LV2 Atom.
A POD can express nested structures of Objects (with properties), Vectors,
Arrays, sequences and various primitives types. All information in the POD
is laid out sequentially in memory and can be written directly to
storage or exchanged between processes or threads without additional
marshalling.
Each POD is made of a 32 bits size followed by a 32 bits type field,
followed by the pod contents. This makes it possible to skip over unknown
POD types. The POD start is always aligned to 8 bytes.
PODs can be efficiently constructed and parsed in real-time threads without
requiring memory allocations.
PODs use the SPA type system for the basic types and containers. See
the SPA types for more info.
## Types
PODs can contain a number of basic SPA types:
- `SPA_TYPE_None`: no value or a NULL pointer.
- `SPA_TYPE_Bool`: a boolean value
- `SPA_TYPE_Id`: an enumerated value
- `SPA_TYPE_Int`, `SPA_TYPE_Long`, `SPA_TYPE_Float`, `SPA_TYPE_Double`:
- various numeral types, 32 and 64 bits.
- `SPA_TYPE_String`: a string
- `SPA_TYPE_Bytes`: a byte array
- `SPA_TYPE_Rectangle`: a rectangle with width and height
- `SPA_TYPE_Fraction`: a fraction with numerator and denominator
- `SPA_TYPE_Bitmap`: an array of bits
PODs can be grouped together in these container types:
- `SPA_TYPE_Array`: an array of equal sized objects
- `SPA_TYPE_Struct`: a collection of types and objects
- `SPA_TYPE_Object`: an object with properties
- `SPA_TYPE_Sequence`: a timed sequence of PODs
PODs can also contain some extra types:
- `SPA_TYPE_Pointer`: a typed pointer in memory
- `SPA_TYPE_Fd`: a file descriptor
- `SPA_TYPE_Choice`: a choice of values
- `SPA_TYPE_Pod`: a generic type for the POD itself
# Constructing a POD
A POD is usually constructed with a `struct spa_pod_builder`. The builder
needs to be initialized with a memory region to write into. It is
also possible to dynamically grow the memory as needed.
The most common way to construct a POD is on the stack. This does
not require any memory allocations. The size of the POD can be
estimated pretty easily and if the buffer is not large enough, an
appropriate error will be generated.
The code fragment below initializes a pod builder to write into
the stack allocated buffer.
\code{.c}
uint8_t buffer[4096];
struct spa_pod_builder b;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
\endcode
Next we need to write some object into the builder. Let's write
a simple struct with an Int and Float in it. Structs are comparable
to JSON arrays.
\code{.c}
struct spa_pod_frame f;
spa_pod_builder_push_struct(&b, &f);
\endcode
First we open the struct container, the `struct spa_pod_frame` keeps
track of the container context. Next we add some values to
the container like this:
\code{.c}
spa_pod_builder_int(&b, 5);
spa_pod_builder_float(&b, 3.1415f);
\endcode
Then we close the container by popping the frame again:
\code{.c}
struct spa_pod *pod;
pod = spa_pod_builder_pop(&b, &f);
\endcode
`spa_pod_builder_pop()` returns a reference to the object we completed
on the stack.
## Using varargs builder.
We can also use the following construct to make POD objects:
\code{.c}
spa_pod_builder_push_struct(&b, &f);
spa_pod_builder_add(&b,
SPA_POD_Int(5),
SPA_POD_Float(3.1415f));
pod = spa_pod_builder_pop(&b, &f);
\endcode
Or even shorter:
\code{.c}
pod = spa_pod_builder_add_struct(&b,
SPA_POD_Int(5),
SPA_POD_Float(3.1415f));
\endcode
It's not possible to use the varargs builder to make a Sequence or
Array, use the normal builder methods for that.
## Making objects
POD objects are containers for properties and are comparable to JSON
objects.
Start by pushing an object:
\code{.c}
spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
\endcode
An object requires an object type (`SPA_TYPE_OBJECT_Props`) and a context
id (`SPA_PARAM_Props`). The object type defines the properties that can be
added to the object and their meaning. The SPA type system allows you to
make this connection (See the type system).
Next we can push some properties in the object:
\code{.c}
spa_pod_builder_prop(&b, SPA_PROP_device, 0);
spa_pod_builder_string(&b, "hw:0");
spa_pod_builder_prop(&b, SPA_PROP_frequency, 0);
spa_pod_builder_float(&b, 440.0);
\endcode
As can be seen, we always need to push a prop (with key and flags)
and then the associated value. For performance reasons it is a good
idea to always push (and parse) the object keys in ascending order.
Don't forget to pop the result when the object is finished:
\code{.c}
pod = spa_pod_builder_pop(&b, &f);
\endcode
There is a shortcut for making objects:
\code{.c}
pod = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
SPA_PROP_device, SPA_POD_String("hw:0"),
SPA_PROP_frequency, SPA_POD_Float(440.0f));
\endcode
## Choice values
It is possible to express ranges or enumerations of possible
values for properties (and to some extend structs). This is achieved
with Choice values.
Choice values are really just a choice type and an array of choice values
(of the same type). Depending on the choice type, the array values are
interpreted in different ways:
* `SPA_CHOICE_None`: no choice, first value is current
* `SPA_CHOICE_Range`: range: default, min, max
* `SPA_CHOICE_Step`: range with step: default, min, max, step
* `SPA_CHOICE_Enum`: enum: default, alternative,...
* `SPA_CHOICE_Flags`: bitmask of flags
Let's illustrate this with a Props object that specifies a range of
possible values for the frequency:
\code{.c}
struct spa_pod_frame f2;
spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
spa_pod_builder_prop(&b, SPA_PROP_frequency, 0);
spa_pod_builder_push_choice(&b, &f2, SPA_CHOICE_Range, 0);
spa_pod_builder_float(&b, 440.0); // default
spa_pod_builder_float(&b, 110.0); // min
spa_pod_builder_float(&b, 880.0); // min
pod = spa_pod_builder_pop(&b, &f2);
pod = spa_pod_builder_pop(&b, &f);
\endcode
As you can see, first push the choice as a Range, then the values. A Range
choice expects at least 3 values, the default value, minimum and maximum
values. There is a shortcut for this as well using varargs:
\code{.c}
pod = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
SPA_PROP_frequency, SPA_POD_CHOICE_RANGE_Float(440.0f, 110.0f, 880.0f));
\endcode
## Choice examples
This is a description of a possible `SPA_TYPE_OBJECT_Format` as used when
enumerating allowed formats (`SPA_PARAM_EnumFormat`) in SPA objects:
\code{.c}
pod = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
// specify the media type and subtype
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
// audio/raw properties
SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id(
SPA_AUDIO_FORMAT_S16, // default
SPA_AUDIO_FORMAT_S16, // alternative1
SPA_AUDIO_FORMAT_S32, // alternative2
SPA_AUDIO_FORMAT_f32 // alternative3
),
SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int(
44100, // default
8000, // min
192000 // max
),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2));
\endcode
## Fixate
We can remove all choice values from the object with the
`spa_pod_object_fixate()` method. This modifies the pod in-place and sets all
choice properties to `SPA_CHOICE_None`, forcing the default value as the
only available value in the choice.
Running fixate on our previous example would result in an object equivalent
to:
\code{.c}
pod = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
// specify the media type and subtype
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
// audio/raw properties
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16),
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(44100),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2));
\endcode
# Parsing a POD
Parsing a POD usually consists of
* validating if raw bytes + size can contain a valid pod
* inspecting the type of a pod
* looping over the items in an object or struct
* getting data out of PODs.
## Validating bytes
Use `spa_pod_from_data()` to check if maxsize of bytes in data contain
a POD at the size bytes starting at offset. This function checks that
the POD size will fit and not overflow.
\code{.c}
struct spa_pod *pod;
pod = spa_pod_from_data(data, maxsize, offset, size);
\endcode
## Checking the type of POD
Use one of `spa_pod_is_bool()`, `spa_pod_is_int()`, etc to check
for the type of the pod. For simple (non-container) types,
`spa_pod_get_bool()`, `spa_pod_get_int()` etc can be used to
extract the value of the pod.
`spa_pod_is_object_type()` can be used to check if the POD contains
an object of the expected type.
## Struct fields
To iterate over the fields of a Struct use:
\code{.c}
struct spa_pod *pod, *obj;
SPA_POD_STRUCT_FOREACH(obj, pod) {
printf("field type:%d\n", pod->type);
}
\endcode
For parsing Structs it is usually much easier to use the parser
below.
## Object Properties
To iterate over the properties in an object you can do:
\code{.c}
struct spa_pod_prop *prop;
struct spa_pod_object *obj = (struct spa_pod_object*)pod;
SPA_POD_OBJECT_FOREACH(pod, prop) {
printf("prop key:%d\n", prop->key);
}
\endcode
There is a function to retrieve the property for a certain key
in the object. If the properties of the object are in ascending
order, you can start searching from the previous key.
\code{.c}
struct spa_pod_prop *prop;
prop = spa_pod_find_prop(obj, NULL, SPA_FORMAT_AUDIO_format);
// .. use first prop
prop = spa_pod_find_prop(obj, prop, SPA_FORMAT_AUDIO_rate);
// .. use next prop
\endcode
## Parser
Similar to the builder, there is a parser object as well.
If the fields in a struct are known, it is much easier to use the
parser. Similarly, if the object type (and thus its keys) are known,
the parser is easier.
First initialize a `struct spa_pod_parser`:
\code{.c}
struct spa_pod_parser p;
spa_pod_parser_pod(&p, obj);
\endcode
You can then enter containers such as objects or structs with a push
operation:
\code{.c}
struct spa_pod_frame f;
spa_pod_parser_push_struct(&p, &f);
\endcode
You need to store the context in a `struct spa_pod_frame` to be able
to exit the container again later.
You can then parse each field. The parser takes care of moving to the
next field.
\code{.c}
uint32_t id, val;
spa_pod_parser_get_id(&p, &id);
spa_pod_parser_get_int(&p, &val);
...
\endcode
And finally exit the container again:
\code{.c}
spa_pod_parser_pop(&p, &f);
\endcode
## Parser with variable arguments
In most cases, parsing objects is easier with the variable argument
functions. The parse function look like the mirror image of the builder
functions.
To parse a struct:
\code{.c}
spa_pod_parser_get_struct(&p,
SPA_POD_Id(&id),
SPA_POD_Int(&val));
\endcode
To parse properties in an object:
\code{.c}
uint32_t type, subtype, format, rate, channels;
spa_pod_parser_get_object(&p,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(&type),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(&subtype),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(&format),
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(&channels));
\endcode
When parsing objects it is possible to have optional fields. You can
make a field optional be parsing it with the `SPA_POD_OPT_` prefix
for the type.
In the next example, the rate and channels fields are optional
and when they are not present, the variables will not be changed.
\code{.c}
uint32_t type, subtype, format, rate = 0, channels = 0;
spa_pod_parser_get_object(&p,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(&type),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(&subtype),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(&format),
SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&channels));
\endcode
It is not possible to parse a Sequence or Array with the parser.
Use the iterator for this.
## Choice values
The parser will handle Choice values as long as they are of type
None. It will then parse the single value from the choice. When
dealing with other choice values, it's possible to parse the
property values into a `struct spa_pod` and then inspect the Choice
manually, if needed.
Here is an example of parsing the format values as a POD:
\code{.c}
uint32_t type, subtype;
struct spa_pod *format;
spa_pod_parser_get_object(&p,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(&type),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(&subtype),
SPA_FORMAT_AUDIO_format, SPA_POD_Pod(&format));
\endcode
`spa_pod_get_values()` is a useful function. It returns a
`struct spa_pod*` with and array of values. For normal PODs
and Choice None values, it simply returns the POD and 1 value.
For other Choice values it returns the Choice type and an array
of values:
\code{.c}
struct spa_pod *value;
uint32_t n_vals, choice;
value = spa_pod_get_values(pod, &n_vals, &choice);
switch (choice) {
case SPA_CHOICE_None:
// one single value
break;
case SPA_CHOICE_Range:
// array of values of type of pod, cast to right type
// to iterate.
uint32_t *v = SPA_POD_BODY(values);
if (n_vals < 3)
break;
printf("default value: %u\n", v[0]);
printf("min value: %u\n", v[1]);
printf("max value: %u\n", v[2]);
break;
// ...
default:
break;
}
\endcode
# Filter
Given 2 pod objects of the same type (Object, Struct, ..) one can
run a filter and generate a new pod that only contains values that
are compatible with both input pods.
This is, for example, used to find a compatible format between two ports.
As an example we can run a filter on two simple PODs:
\code{.c}
pod = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id(
SPA_AUDIO_FORMAT_S16, // default
SPA_AUDIO_FORMAT_S16, // alternative1
SPA_AUDIO_FORMAT_S32, // alternative2
SPA_AUDIO_FORMAT_f32 // alternative3
));
filter = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id(
SPA_AUDIO_FORMAT_S16, // default
SPA_AUDIO_FORMAT_S16, // alternative1
SPA_AUDIO_FORMAT_f64 // alternative2
));
struct spa_pod *result;
if (spa_pod_filter(&b, &result, pod, filter) < 0)
goto exit_error;
\endcode
Filter will contain a POD equivalent to:
\code{.c}
result = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_AUDIO_format, SPA_AUDIO_FORMAT_S16);
\endcode
# POD layout
Each POD has a 32 bits size field, followed by a 32 bits type field. The size
field specifies the size following the type field.
Each POD is aligned to an 8 byte boundary.
\addtogroup spa_pod
See: \ref page_spa_pod
*/

20
doc/tutorial.dox Normal file
View File

@ -0,0 +1,20 @@
/** \page page_tutorial Tutorial
Welcome to the PipeWire 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
*/

19
doc/tutorial1.c Normal file
View 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] */

45
doc/tutorial1.dox Normal file
View File

@ -0,0 +1,45 @@
/** \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
*/

56
doc/tutorial2.c Normal file
View 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, &registry_listener,
&registry_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] */

128
doc/tutorial2.dox Normal file
View File

@ -0,0 +1,128 @@
/** \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, &registry_listener,
&registry_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
*/

86
doc/tutorial3.c Normal file
View File

@ -0,0 +1,86 @@
/*
[title]
\ref page_tutorial3
[title]
*/
/* [code] */
#include <pipewire/pipewire.h>
/* [roundtrip] */
static int roundtrip(struct pw_core *core, struct pw_main_loop *loop)
{
struct spa_hook core_listener;
int pending, done = 0;
void core_event_done(void *object, uint32_t id, int seq) {
if (id == PW_ID_CORE && seq == pending) {
done = 1;
pw_main_loop_quit(loop);
}
}
const struct pw_core_events core_events = {
PW_VERSION_CORE_EVENTS,
.done = core_event_done,
};
spa_zero(core_listener);
pw_core_add_listener(core, &core_listener,
&core_events, NULL);
pending = pw_core_sync(core, PW_ID_CORE, 0);
while (!done) {
pw_main_loop_run(loop);
}
spa_hook_remove(&core_listener);
return 0;
}
/* [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 */);
spa_zero(registry_listener);
pw_registry_add_listener(registry, &registry_listener,
&registry_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] */

128
doc/tutorial3.dox Normal file
View File

@ -0,0 +1,128 @@
/** \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;
spa_zero(core_listener);
pw_core_add_listener(core, &core_listener,
&core_events, NULL);
\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}
int pending, done = 0;
void core_event_done(void *object, uint32_t id, int seq) {
if (id == PW_ID_CORE && seq == pending) {
done = 1;
pw_main_loop_quit(loop);
}
}
const struct pw_core_events core_events = {
PW_VERSION_CORE_EVENTS,
.done = core_event_done,
};
\endcode
When the done event is received for an object with id `PW_ID_CORE`
and a certain sequence number `seq`, this function will set the done
variable to 1 and call `pw_main_loop_quit()`.
Next we do:
\code{.c}
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 returns value will reflect this. PipeWire
uses the return values of the underlying SPA (Simple Plugin API)
helper objects (See also [error codes](spa-design.md#error-codes)).
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}
while (!done) {
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
*/

112
doc/tutorial4.c Normal file
View File

@ -0,0 +1,112 @@
/*
[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;
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;
val = sin(data->accumulator) * DEFAULT_VOLUME * 16767.f;
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,
argc > 1 ? (uint32_t)atoi(argv[1]) : 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] */

160
doc/tutorial4.dox Normal file
View File

@ -0,0 +1,160 @@
/** \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 3 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. `PW_ID_ANY`
means that we are ok with connecting to any consumer. 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
*/

137
doc/tutorial5.c Normal file
View File

@ -0,0 +1,137 @@
/*
[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));
pw_init(&argc, &argv);
data.loop = pw_main_loop_new(NULL);
data.stream = pw_stream_new_simple(
pw_main_loop_get_loop(data.loop),
"video-capture",
pw_properties_new(
PW_KEY_MEDIA_TYPE, "Video",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE, "Camera",
NULL),
&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,
argc > 1 ? (uint32_t)atoi(argv[1]) : 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] */

218
doc/tutorial5.dox Normal file
View File

@ -0,0 +1,218 @@
/** \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}
data.stream = pw_stream_new_simple(
pw_main_loop_get_loop(data.loop),
"video-capture",
pw_properties_new(
PW_KEY_MEDIA_TYPE, "Video",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE, "Camera",
NULL),
&stream_events,
&data);
\endcode
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,
argc > 1 ? (uint32_t)atoi(argv[1]) : 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. `PW_ID_ANY`
means that we are ok with connecting to any producer. We also allow the user
to pass an optional target id.
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
*/

97
doc/tutorial6.c Normal file
View 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,
&registry_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] */

69
doc/tutorial6.dox Normal file
View 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"
*/

302
include/valgrind/memcheck.h Normal file
View File

@ -0,0 +1,302 @@
/*
----------------------------------------------------------------
Notice that the following BSD-style license applies to this one
file (memcheck.h) only. The rest of Valgrind is licensed under the
terms of the GNU General Public License, version 2, unless
otherwise indicated. See the COPYING file in the source
distribution for details.
----------------------------------------------------------------
This file is part of MemCheck, a heavyweight Valgrind tool for
detecting memory errors.
Copyright (C) 2000-2013 Julian Seward. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
3. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
4. The name of the author may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------
Notice that the above BSD-style license applies to this one file
(memcheck.h) only. The entire rest of Valgrind is licensed under
the terms of the GNU General Public License, version 2. See the
COPYING file in the source distribution for details.
----------------------------------------------------------------
*/
#ifndef __MEMCHECK_H
#define __MEMCHECK_H
/* This file is for inclusion into client (your!) code.
You can use these macros to manipulate and query memory permissions
inside your own programs.
See comment near the top of valgrind.h on how to use them.
*/
#include "valgrind.h"
/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
This enum comprises an ABI exported by Valgrind to programs
which use client requests. DO NOT CHANGE THE ORDER OF THESE
ENTRIES, NOR DELETE ANY -- add new ones at the end. */
typedef
enum {
VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'),
VG_USERREQ__MAKE_MEM_UNDEFINED,
VG_USERREQ__MAKE_MEM_DEFINED,
VG_USERREQ__DISCARD,
VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,
VG_USERREQ__CHECK_MEM_IS_DEFINED,
VG_USERREQ__DO_LEAK_CHECK,
VG_USERREQ__COUNT_LEAKS,
VG_USERREQ__GET_VBITS,
VG_USERREQ__SET_VBITS,
VG_USERREQ__CREATE_BLOCK,
VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE,
/* Not next to VG_USERREQ__COUNT_LEAKS because it was added later. */
VG_USERREQ__COUNT_LEAK_BLOCKS,
VG_USERREQ__ENABLE_ADDR_ERROR_REPORTING_IN_RANGE,
VG_USERREQ__DISABLE_ADDR_ERROR_REPORTING_IN_RANGE,
/* This is just for memcheck's internal use - don't use it */
_VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR
= VG_USERREQ_TOOL_BASE('M','C') + 256
} Vg_MemCheckClientRequest;
/* Client-code macros to manipulate the state of memory. */
/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */
#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__MAKE_MEM_NOACCESS, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
/* Similarly, mark memory at _qzz_addr as addressable but undefined
for _qzz_len bytes. */
#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__MAKE_MEM_UNDEFINED, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
/* Similarly, mark memory at _qzz_addr as addressable and defined
for _qzz_len bytes. */
#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__MAKE_MEM_DEFINED, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is
not altered: bytes which are addressable are marked as defined,
but those which are not addressable are left unchanged. */
#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
/* Create a block-description handle. The description is an ascii
string which is included in any messages pertaining to addresses
within the specified memory range. Has no other effect on the
properties of the memory range. */
#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__CREATE_BLOCK, \
(_qzz_addr), (_qzz_len), (_qzz_desc), \
0, 0)
/* Discard a block-description-handle. Returns 1 for an
invalid handle, 0 for a valid handle. */
#define VALGRIND_DISCARD(_qzz_blkindex) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__DISCARD, \
0, (_qzz_blkindex), 0, 0, 0)
/* Client-code macros to check the state of memory. */
/* Check that memory at _qzz_addr is addressable for _qzz_len bytes.
If suitable addressibility is not established, Valgrind prints an
error message and returns the address of the first offending byte.
Otherwise it returns zero. */
#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
/* Check that memory at _qzz_addr is addressable and defined for
_qzz_len bytes. If suitable addressibility and definedness are not
established, Valgrind prints an error message and returns the
address of the first offending byte. Otherwise it returns zero. */
#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
VG_USERREQ__CHECK_MEM_IS_DEFINED, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
/* Use this macro to force the definedness and addressibility of an
lvalue to be checked. If suitable addressibility and definedness
are not established, Valgrind prints an error message and returns
the address of the first offending byte. Otherwise it returns
zero. */
#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \
VALGRIND_CHECK_MEM_IS_DEFINED( \
(volatile unsigned char *)&(__lvalue), \
(unsigned long)(sizeof (__lvalue)))
/* Do a full memory leak check (like --leak-check=full) mid-execution. */
#define VALGRIND_DO_LEAK_CHECK \
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
0, 0, 0, 0, 0)
/* Same as VALGRIND_DO_LEAK_CHECK but only showing the entries for
which there was an increase in leaked bytes or leaked nr of blocks
since the previous leak search. */
#define VALGRIND_DO_ADDED_LEAK_CHECK \
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
0, 1, 0, 0, 0)
/* Same as VALGRIND_DO_ADDED_LEAK_CHECK but showing entries with
increased or decreased leaked bytes/blocks since previous leak
search. */
#define VALGRIND_DO_CHANGED_LEAK_CHECK \
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
0, 2, 0, 0, 0)
/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */
#define VALGRIND_DO_QUICK_LEAK_CHECK \
VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
1, 0, 0, 0, 0)
/* Return number of leaked, dubious, reachable and suppressed bytes found by
all previous leak checks. They must be lvalues. */
#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \
/* For safety on 64-bit platforms we assign the results to private
unsigned long variables, then assign these to the lvalues the user
specified, which works no matter what type 'leaked', 'dubious', etc
are. We also initialise '_qzz_leaked', etc because
VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
defined. */ \
{ \
unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
VALGRIND_DO_CLIENT_REQUEST_STMT( \
VG_USERREQ__COUNT_LEAKS, \
&_qzz_leaked, &_qzz_dubious, \
&_qzz_reachable, &_qzz_suppressed, 0); \
leaked = _qzz_leaked; \
dubious = _qzz_dubious; \
reachable = _qzz_reachable; \
suppressed = _qzz_suppressed; \
}
/* Return number of leaked, dubious, reachable and suppressed bytes found by
all previous leak checks. They must be lvalues. */
#define VALGRIND_COUNT_LEAK_BLOCKS(leaked, dubious, reachable, suppressed) \
/* For safety on 64-bit platforms we assign the results to private
unsigned long variables, then assign these to the lvalues the user
specified, which works no matter what type 'leaked', 'dubious', etc
are. We also initialise '_qzz_leaked', etc because
VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
defined. */ \
{ \
unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
VALGRIND_DO_CLIENT_REQUEST_STMT( \
VG_USERREQ__COUNT_LEAK_BLOCKS, \
&_qzz_leaked, &_qzz_dubious, \
&_qzz_reachable, &_qzz_suppressed, 0); \
leaked = _qzz_leaked; \
dubious = _qzz_dubious; \
reachable = _qzz_reachable; \
suppressed = _qzz_suppressed; \
}
/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it
into the provided zzvbits array. Return values:
0 if not running on valgrind
1 success
2 [previously indicated unaligned arrays; these are now allowed]
3 if any parts of zzsrc/zzvbits are not addressable.
The metadata is not copied in cases 0, 2 or 3 so it should be
impossible to segfault your system by using this call.
*/
#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \
(unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
VG_USERREQ__GET_VBITS, \
(const char*)(zza), \
(char*)(zzvbits), \
(zznbytes), 0, 0)
/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it
from the provided zzvbits array. Return values:
0 if not running on valgrind
1 success
2 [previously indicated unaligned arrays; these are now allowed]
3 if any parts of zza/zzvbits are not addressable.
The metadata is not copied in cases 0, 2 or 3 so it should be
impossible to segfault your system by using this call.
*/
#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \
(unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
VG_USERREQ__SET_VBITS, \
(const char*)(zza), \
(const char*)(zzvbits), \
(zznbytes), 0, 0 )
/* Disable and re-enable reporting of addressing errors in the
specified address range. */
#define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__DISABLE_ADDR_ERROR_REPORTING_IN_RANGE, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
#define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(_qzz_addr,_qzz_len) \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
VG_USERREQ__ENABLE_ADDR_ERROR_REPORTING_IN_RANGE, \
(_qzz_addr), (_qzz_len), 0, 0, 0)
#endif

6647
include/valgrind/valgrind.h Normal file

File diff suppressed because it is too large Load Diff

42
man/meson.build Normal file
View File

@ -0,0 +1,42 @@
manpage_conf = configuration_data()
manpage_conf.set('PACKAGE_NAME', meson.project_name())
manpage_conf.set('PACKAGE_VERSION', meson.project_version())
manpage_conf.set('PACKAGE_URL', 'https://pipewire.org')
manpage_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/pipewire/pipewire/issues')
manpage_conf.set('PIPEWIRE_CONFIG_DIR', pipewire_configdir)
manpage_conf.set('PIPEWIRE_CONFDATADIR', pipewire_confdatadir)
manpages = [
'pipewire.1.rst.in',
'pipewire-pulse.1.rst.in',
'pipewire.conf.5.rst.in',
'pw-cat.1.rst.in',
'pw-cli.1.rst.in',
'pw-dot.1.rst.in',
'pw-metadata.1.rst.in',
'pw-mididump.1.rst.in',
'pw-mon.1.rst.in',
'pw-profiler.1.rst.in',
]
if get_option('pipewire-jack').allowed()
manpages += 'pw-jack.1.rst.in'
endif
if not generate_manpages
subdir_done()
endif
foreach m : manpages
file = m.split('.rst.in').get(0)
rst = configure_file(input : m,
output : file + '.rst',
configuration : manpage_conf)
section = file.split('.').get(-1)
custom_target(file + '.target',
output : file,
input : rst,
command : [rst2man, '@INPUT@', '@OUTPUT@'],
install : true,
install_dir : get_option('mandir') / 'man' + section)
endforeach

View File

@ -0,0 +1,48 @@
pipewire-pulse
##############
-----------------------------------
The PipeWire PulseAudio replacement
-----------------------------------
:Manual section: 1
:Manual group: General Commands Manual
SYNOPSIS
========
| **pipewire-pulse** [*options*]
DESCRIPTION
===========
**pipewire-pulse** starts a PulseAudio-compatible daemon that integrates with
the PipeWire media server. This daemon is a drop-in replacement for the
PulseAudio daemon.
OPTIONS
=======
-h | --help
Show help.
-v | --verbose
Increase the verbosity by one level. This option may be specified multiple
times.
--version
Show version information.
-c | --config=FILE
Load the given config file (Default: pipewire-pulse.conf).
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>;
PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pipewire(1)``

52
man/pipewire.1.rst.in Normal file
View File

@ -0,0 +1,52 @@
pipewire
########
-------------------------
The PipeWire media server
-------------------------
:Manual section: 1
:Manual group: General Commands Manual
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
``pipewire.conf(5)`` manual page.
OPTIONS
=======
-h | --help
Show help.
-v | --verbose
Increase the verbosity by one level. This option may be specified multiple
times.
--version
Show version information.
-c | --config=FILE
Load the given config file (Default: pipewire.conf).
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>;
PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pw-mon(1)``,
``pw-cat(1)``,
``pw-cli(1)``,

113
man/pipewire.conf.5.rst.in Normal file
View File

@ -0,0 +1,113 @@
pipewire.conf
#############
--------------------------------------
The PipeWire server configuration file
--------------------------------------
:Manual section: 5
:Manual group: File Formats Manual
.. _synopsis:
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 files are loaded in the order listed in the 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.
Next to the configuration file can be a directory with the same name as
the file with a ``.d/`` suffix. All directories in the SYNOPSIS_ directory
search paths are traversed in the listed order and the contents of the
``*.conf`` files inside them are appended to the main configuration file
as overrides. Object sections are merged and array sections are appended.
CONFIGURATION FILE FORMAT
=========================
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:
name = value # simple assignment
name = { key1 = value1 key2 = value2 } # a dictionary with two
entries
name = [ value1 value2 ] # an array with two entries
name = [ { k = v1 } { k = v2 } ] # an array of dictionaries
The configuration files can be expressed in full JSON syntax but for ease
of use, a relaxed format may be used where:
* ``:`` to delimit keys and values can be substuted by ``=`` or a space.
* ``"`` around keys and string can be omited 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
===========================
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 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.
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.
context.exec
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.
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pipewire(1)``,
``pw-mon(1)``,

150
man/pw-cat.1.rst.in Normal file
View File

@ -0,0 +1,150 @@
pw-cat
######
-----------------------------------
Play and record media with PipeWire
-----------------------------------
:Manual section: 1
:Manual group: General Commands Manual
SYNOPSIS
========
| **pw-cat** [*options*] [*FILE*]
| **pw-play** [*options*] [*FILE*]
| **pw-record** [*options*] [*FILE*]
| **pw-midiplay** [*options*] [*FILE*]
| **pw-midirecord** [*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``.
OPTIONS
=======
-h | --help
Show help.
--version
Show version information.
-v | --verbose
Verbose operation.
-R | --remote=NAME
The name the *remote* instance to connect to. If left unspecified,
a connection is made to the default PipeWire instance.
-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.
-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.
-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.
--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.
--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.
--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.
--target=VALUE
Set a node target (default auto). The value can be:
auto
Automatically select (Default)
0
Don't try to link this node
<id>
The id of a target node
--latency=VALUE[*units*]
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.
--list-targets
List the available targets for **--target**
-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.
--rate=VALUE
The sample rate, default 48000.
--channels=VALUE
The number of channels, default 2.
--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**
--format=VALUE
The sample format to use. One of:
**s8**, **s16** (default), **s24**, **s32**,
**f32**, **f64**.
--volume=VALUE
The stream volume, default 1.000.
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``PipeWire(1)``,
``pw-mon(1)``,

143
man/pw-cli.1.rst.in Normal file
View File

@ -0,0 +1,143 @@
pw-cli
######
-----------------------------------
The PipeWire Command Line Interface
-----------------------------------
:Manual section: 1
:Manual group: General Commands Manual
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. Some commands operate on the current
instance and some on the local instance.
Use the 'help' command to list the available commands.
GENERAL COMMANDS
================
help
Show a quick help on the commands available.
quit
Exit from **pw-cli**
MODULE MANAGEMENT
=================
| Modules are loaded and unloaded in the local instance and can add
| functionality or objects to the local instance.
load-module *name* [*arguments...*]
Load a module specified by its name and arguments. 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.
unload-module *module-var*
Unload a module, specified either by its variable.
OBJECT INTROSPECTION
====================
list-objects
List the objects of the current instance.
Objects are listed with their *id*, *type* and *version*.
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
====================
connect [*remote-name*]
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*.
This command returns a remote var that can be used to disconnect or
switch remotes.
disconnect [*remote-var*]
Disconnect from a *remote instance*.
If no remote name is specified, the current instance is disconnected.
list-remotes
List all *remote instances*.
switch-remote [*remote-var*]
Make the specified *remote* the current instance.
If no remote name is specified, the local instance is made current.
NODE MANAGEMENT
===============
create-node *factory-name* [*properties...*]
Create a node from a factory in the current instance.
Properties are key=value pairs separated by whitespace.
This command returns a *node variable*.
destroy-node *node-var*
Destroy a node.
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.
LINK MANAGEMENT
===============
create-link *node-id* *port-id* *node-id* *port-id* [*properties...*]
Create a link between 2 nodes and ports.
Port *ids* can be *-1* to automatically select an available port.
Properties are key=value pairs separated by whitespace.
This command returns a *link variable*.
destroy-link *link-var*
Destroy a link.
EXAMPLES
========
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pipewire(1)``,
``pw-mon(1)``,

65
man/pw-dot.1.rst.in Normal file
View File

@ -0,0 +1,65 @@
pw-dot
######
---------------------------
The PipeWire dot graph dump
---------------------------
:Manual section: 1
:Manual group: General Commands Manual
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
=======
-r | --remote=NAME
The name the remote instance to connect to. If left unspecified,
a connection is made to the default PipeWire instance.
-h | --help
Show help.
--version
Show version information.
-a | --all
Show all object types.
-s | --smart
Show linked objects only.
-d | --detail
Show all object properties.
-o FILE | --output=FILE
Output file name (Default pw.dot). Use - for stdout.
-L | --lr
Lay the graph from left to right, instead of dot's default top to bottom.
-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
========
``pipewire(1)``,
``pw-cli(1)``,
``pw-mon(1)``,

65
man/pw-jack.1.rst.in Normal file
View File

@ -0,0 +1,65 @@
pw-jack
#######
----------------------------
Use PipeWire instead of JACK
----------------------------
:Manual section: 1
:Manual group: General Commands Manual
SYNOPSIS
========
| **pw-jack** [*options*] *COMMAND* [*FILE*]
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
=======
-h
Show help.
-r NAME
The name of the remote instance to connect to. If left
unspecified, a connection is made to the default PipeWire
instance.
-v
Verbose operation.
EXAMPLES
========
| **pw-jack** sndfile-jackplay /usr/share/sounds/freedesktop/stereo/bell.oga
NOTES
=====
Using PipeWire for audio is currently considered to be
experimental.
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>;
PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pipewire(1)``,
``jackd(1)``,

67
man/pw-metadata.1.rst.in Normal file
View File

@ -0,0 +1,67 @@
pw-metadata
###########
---------------------
The PipeWire metadata
---------------------
:Manual section: 1
:Manual group: General Commands Manual
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
=======
-r | --remote=NAME
The name the remote instance to use. If left unspecified,
a connection is made to the default PipeWire instance.
-h | --help
Show help.
--version
Show version information.
-m | --monitor
Keeps running and log the changes to the metadata.
-d | --delete
Delete all metadata for *id* or for the
specified *key* of object *id*
Without any option, all metadata is removed
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pipewire(1)``,
``pw-mon(1)``,
``pw-cli(1)``,

49
man/pw-mididump.1.rst.in Normal file
View File

@ -0,0 +1,49 @@
pw-mididump
###########
----------------------
The PipeWire MIDI dump
----------------------
:Manual section: 1
:Manual group: General Commands Manual
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
=======
-r | --remote=NAME
The name the remote instance to monitor. If left unspecified,
a connection is made to the default PipeWire instance.
-h | --help
Show help.
--version
Show version information.
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pipewire(1)``,
``pw-cat(1)``,

46
man/pw-mon.1.rst.in Normal file
View File

@ -0,0 +1,46 @@
pw-mon
######
--------------------
The PipeWire monitor
--------------------
:Manual section: 1
:Manual group: General Commands Manual
SYNOPSIS
========
| **pw-mon** [*options*]
DESCRIPTION
===========
Monitor objects on the PipeWire instance.
OPTIONS
=======
-r | --remote=NAME
The name the *remote* instance to monitor. If left unspecified,
a connection is made to the default PipeWire instance.
-h | --help
Show help.
--version
Show version information.
-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
========
``pipewire(1)``,

54
man/pw-profiler.1.rst.in Normal file
View File

@ -0,0 +1,54 @@
pw-profiler
###########
---------------------
The PipeWire profiler
---------------------
:Manual section: 1
:Manual group: General Commands Manual
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.
OPTIONS
=======
-r | --remote=NAME
The name the remote instance to monitor. If left unspecified,
a connection is made to the default PipeWire instance.
-h | --help
Show help.
--version
Show version information.
-o | --output=FILE
Profiler output name (default "profiler.log").
AUTHORS
=======
The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@
SEE ALSO
========
``pipewire(1)``,

471
meson.build Normal file
View File

@ -0,0 +1,471 @@
project('pipewire', ['c' ],
version : '0.3.48',
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
meson_version : '>= 0.59.0',
default_options : [ 'warning_level=3',
'c_std=gnu99',
'cpp_std=c++17',
'b_pie=true',
#'b_sanitize=address,undefined',
'buildtype=debugoptimized' ])
pipewire_version = meson.project_version()
version_arr = pipewire_version.split('.')
pipewire_version_major = version_arr[0]
pipewire_version_minor = version_arr[1]
pipewire_version_micro = version_arr[2]
if version_arr.length() == 4
pipewire_version_nano = version_arr[3]
else
pipewire_version_nano = 0
endif
spaversion = '0.2'
apiversion = '0.3'
soversion = 0
libversion = '@0@.@1@.0'.format(soversion, pipewire_version_minor.to_int() * 100 + pipewire_version_micro.to_int())
pipewire_name = 'pipewire-@0@'.format(apiversion)
spa_name = 'spa-@0@'.format(spaversion)
prefix = get_option('prefix')
pipewire_bindir = prefix / get_option('bindir')
pipewire_datadir = prefix / get_option('datadir')
pipewire_libdir = prefix / get_option('libdir')
pipewire_libexecdir = prefix / get_option('libexecdir')
pipewire_localedir = prefix / get_option('localedir')
pipewire_sysconfdir = prefix / get_option('sysconfdir')
pipewire_configdir = pipewire_sysconfdir / 'pipewire'
pipewire_confdatadir = pipewire_datadir / 'pipewire'
modules_install_dir = pipewire_libdir / pipewire_name
if host_machine.system() == 'linux'
# glibc ld.so interprets ${LIB} in a library loading path with an
# appropriate value for the current architecture, typically something
# like lib, lib64 or lib/x86_64-linux-gnu.
# This allows the same pw-jack script to work for both 32- and 64-bit
# applications on biarch/multiarch distributions, by setting something
# like LD_LIBRARY_PATH='/usr/${LIB}/pipewire-0.3/jack'.
# Note that ${LIB} is a special token expanded by the runtime linker,
# not an environment variable, and must be passed through literally.
modules_install_dir_dlopen = prefix / '${LIB}' / pipewire_name
else
modules_install_dir_dlopen = modules_install_dir
endif
spa_plugindir = pipewire_libdir / spa_name
spa_datadir = pipewire_datadir / spa_name
alsadatadir = pipewire_datadir / 'alsa-card-profile' / 'mixer'
pipewire_headers_dir = pipewire_name / 'pipewire'
pkgconfig = import('pkgconfig')
cc = meson.get_compiler('c')
common_flags = [
'-fvisibility=hidden',
'-Werror=suggest-attribute=format',
'-Wsign-compare',
'-Wpointer-arith',
'-Wpointer-sign',
'-Wformat',
'-Wformat-security',
'-Wimplicit-fallthrough',
'-Wmissing-braces',
'-Wtype-limits',
'-Wvariadic-macros',
'-Wmaybe-uninitialized',
'-Wno-missing-field-initializers',
'-Wno-unused-parameter',
'-Wno-pedantic',
'-Wold-style-declaration',
'-Wdeprecated-declarations',
'-Wunused-result',
]
cc_flags = common_flags + [
'-D_GNU_SOURCE',
'-DFASTPATH',
# '-DSPA_DEBUG_MEMCPY',
]
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
have_cpp = add_languages('cpp', native: false, required : false)
if have_cpp
cxx = meson.get_compiler('cpp')
cxx_flags = common_flags
add_project_arguments(cxx.get_supported_arguments(cxx_flags), language: 'cpp')
endif
sse_args = '-msse'
sse2_args = '-msse2'
ssse3_args = '-mssse3'
sse41_args = '-msse4.1'
fma_args = '-mfma'
avx_args = '-mavx'
avx2_args = '-mavx2'
have_sse = cc.has_argument(sse_args)
have_sse2 = cc.has_argument(sse2_args)
have_ssse3 = cc.has_argument(ssse3_args)
have_sse41 = cc.has_argument(sse41_args)
have_fma = cc.has_argument(fma_args)
have_avx = cc.has_argument(avx_args)
have_avx2 = cc.has_argument(avx2_args)
have_neon = false
if host_machine.cpu_family() == 'aarch64'
if cc.compiles('''
#include <arm_neon.h>
int main () {
float *s;
asm volatile(
" ld1 { v0.4s }, [%[s]], #16\n"
" fcvtzs v0.4s, v0.4s, #31\n"
: [s] "+r" (s) : :);
}
''',
name : 'aarch64 Neon Support')
neon_args = []
have_neon = true
endif
elif cc.has_argument('-mfpu=neon')
if cc.compiles('''
#include <arm_neon.h>
int main () {
float *s;
asm volatile(
" vld1.32 { q0 }, [%[s]]!\n"
" vcvt.s32.f32 q0, q0, #31\n"
: [s] "+r" (s) : :);
}
''',
args: '-mfpu=neon',
name : 'arm Neon Support')
neon_args = ['-mfpu=neon']
have_neon = true
endif
endif
libatomic = cc.find_library('atomic', required : false)
test_8_byte_atomic = '''
#include <stdint.h>
int main(void)
{
int64_t eight;
__atomic_fetch_add(&eight, 123, __ATOMIC_SEQ_CST);
return 0;
}
'''
# We currently assume that libatomic is unnecessary for 4-byte atomic
# operations on any reasonable architecture.
if cc.links(
test_8_byte_atomic,
name : '8-byte __atomic_fetch_add without libatomic')
atomic_dep = dependency('', required: false)
elif cc.links(
test_8_byte_atomic,
dependencies : libatomic,
name : '8-byte __atomic_fetch_add with libatomic')
atomic_dep = libatomic
else
error('8-byte atomic operations are required')
endif
versiondata = configuration_data()
versiondata.set('PIPEWIRE_VERSION_MAJOR', pipewire_version_major)
versiondata.set('PIPEWIRE_VERSION_MINOR', pipewire_version_minor)
versiondata.set('PIPEWIRE_VERSION_MICRO', pipewire_version_micro)
versiondata.set('PIPEWIRE_VERSION_NANO', pipewire_version_nano)
versiondata.set_quoted('PIPEWIRE_API_VERSION', apiversion)
cdata = configuration_data()
cdata.set_quoted('PIPEWIRE_CONFDATADIR', pipewire_confdatadir)
cdata.set_quoted('LOCALEDIR', pipewire_localedir)
cdata.set_quoted('LIBDIR', pipewire_libdir)
cdata.set_quoted('GETTEXT_PACKAGE', meson.project_name())
cdata.set_quoted('PACKAGE', 'pipewire')
cdata.set_quoted('PACKAGE_NAME', 'PipeWire')
cdata.set_quoted('PACKAGE_STRING', 'PipeWire @0@'.format(pipewire_version))
cdata.set_quoted('PACKAGE_TARNAME', 'pipewire')
cdata.set_quoted('PACKAGE_URL', 'https://pipewire.org')
cdata.set_quoted('PACKAGE_VERSION', pipewire_version)
cdata.set_quoted('MODULEDIR', modules_install_dir)
cdata.set_quoted('PIPEWIRE_CONFIG_DIR', pipewire_configdir)
cdata.set_quoted('PLUGINDIR', spa_plugindir)
cdata.set_quoted('SPADATADIR', spa_datadir)
cdata.set_quoted('PA_ALSA_PATHS_DIR', alsadatadir / 'paths')
cdata.set_quoted('PA_ALSA_PROFILE_SETS_DIR', alsadatadir / 'profile-sets')
if host_machine.endian() == 'big'
cdata.set('WORDS_BIGENDIAN', 1)
endif
check_headers = [
['sys/mount.h', 'HAVE_SYS_MOUNT_H'],
['sys/param.h', 'HAVE_SYS_PARAM_H'],
['sys/random.h', 'HAVE_SYS_RANDOM_H'],
['sys/vfs.h', 'HAVE_SYS_VFS_H'],
['pwd.h', 'HAVE_PWD_H'],
]
foreach h : check_headers
cdata.set(h.get(1), cc.has_header(h.get(0)))
endforeach
cdata.set('HAVE_PIDFD_OPEN',
cc.get_define('SYS_pidfd_open', prefix: '#include <sys/syscall.h>') != '')
systemd = dependency('systemd', required: get_option('systemd'))
systemd_dep = dependency('libsystemd',required: get_option('systemd'))
summary({'systemd conf data': systemd.found()}, bool_yn: true)
summary({'libsystemd': systemd_dep.found()}, bool_yn: true)
cdata.set('HAVE_SYSTEMD', systemd.found() and systemd_dep.found())
configinc = include_directories('.')
includes_inc = include_directories('include')
pipewire_inc = include_directories('src')
makedata = configuration_data()
makedata.set('BUILD_ROOT', meson.project_build_root())
makedata.set('SOURCE_ROOT', meson.project_source_root())
makedata.set('VERSION', pipewire_version)
if version_arr.length() == 4
makedata.set('TAG', 'HEAD')
else
makedata.set('TAG', pipewire_version)
endif
configure_file(input : 'Makefile.in',
output : 'Makefile',
configuration : makedata)
# Find dependencies
mathlib = cc.find_library('m', required : false)
rt_lib = cc.find_library('rt', required : false) # clock_gettime
dl_lib = cc.find_library('dl', required : false)
pthread_lib = dependency('threads')
dbus_dep = dependency('dbus-1', required : get_option('dbus'))
summary({'dbus (Bluetooth, rt, portal, pw-reserve)': dbus_dep.found()}, bool_yn: true, section: 'Misc dependencies')
cdata.set('HAVE_DBUS', dbus_dep.found())
sdl_dep = dependency('sdl2', required : get_option('sdl2'))
summary({'SDL2 (video examples)': sdl_dep.found()}, bool_yn: true, section: 'Misc dependencies')
drm_dep = dependency('libdrm', required : false)
readline_dep = dependency('readline', required : false)
if not readline_dep.found()
readline_dep = cc.find_library('readline', required: false)
endif
summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies')
ncurses_dep = dependency('ncursesw', required : false)
sndfile_dep = dependency('sndfile', version : '>= 1.0.20', required : get_option('sndfile'))
summary({'sndfile': sndfile_dep.found()}, bool_yn: true, section: 'pw-cat/pw-play/pw-dump/filter-chain')
cdata.set('HAVE_SNDFILE', sndfile_dep.found())
pulseaudio_dep = dependency('libpulse', required : get_option('libpulse'))
summary({'libpulse': pulseaudio_dep.found()}, bool_yn: true, section: 'Streaming between daemons')
avahi_dep = dependency('avahi-client', required : get_option('avahi'))
summary({'Avahi DNS-SD (Zeroconf)': avahi_dep.found()}, bool_yn: true,
section: 'Streaming between daemons')
x11_dep = dependency('x11-xcb', required : get_option('x11'))
summary({'X11 (x11-bell)': x11_dep.found()}, bool_yn: true,
section: 'Misc dependencies')
xfixes_dep = dependency('xfixes', required : get_option('x11-xfixes'), version: '>= 6')
cdata.set('HAVE_XFIXES_6', xfixes_dep.found())
canberra_dep = dependency('libcanberra', required : get_option('libcanberra'))
summary({'libcanberra (x11-bell)': canberra_dep.found()}, bool_yn: true,
section: 'Misc dependencies')
libusb_dep = dependency('libusb-1.0', required : get_option('libusb'))
summary({'libusb (Bluetooth quirks)': libusb_dep.found()}, bool_yn: true, section: 'Backend')
cdata.set('HAVE_LIBUSB', libusb_dep.found())
cap_lib = dependency('libcap', required : false)
cdata.set('HAVE_LIBCAP', cap_lib.found())
gst_option = get_option('gstreamer')
gst_deps_def = {
'glib-2.0': {'version': '>=2.32.0'},
'gobject-2.0': {},
'gmodule-2.0': {},
'gio-2.0': {},
'gio-unix-2.0': {},
'gstreamer-1.0': {'version': '>= 1.10.0'},
'gstreamer-plugins-base-1.0': {},
'gstreamer-video-1.0': {},
'gstreamer-audio-1.0': {},
'gstreamer-allocators-1.0': {},
}
gst_dep = []
foreach depname, kwargs: gst_deps_def
dep = dependency(depname, required: gst_option, kwargs: kwargs)
summary({depname: dep.found()}, bool_yn: true, section: 'GStreamer modules')
if not dep.found()
# Beware, there's logic below depending on the array clear here!
gst_dep = []
if get_option('gstreamer-device-provider').enabled()
error('`gstreamer-device-provider` is enabled but `@0@` was not found.'.format(depname))
endif
break
endif
gst_dep += [dep]
endforeach
# This code relies on the array being empty if any dependency was not found
gst_dp_found = gst_dep.length() > 0
summary({'gstreamer-device-provider': gst_dp_found}, bool_yn: true, section: 'Backend')
cdata.set('HAVE_GSTREAMER_DEVICE_PROVIDER', get_option('gstreamer-device-provider').allowed())
webrtc_dep = dependency('webrtc-audio-processing',
version : ['>= 0.2', '< 1.0'],
required : get_option('echo-cancel-webrtc'))
summary({'WebRTC Echo Canceling': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
cdata.set('HAVE_WEBRTC', webrtc_dep.found())
# On FreeBSD, epoll-shim library is required for eventfd() and timerfd()
epoll_shim_dep = (build_machine.system() == 'freebsd'
? dependency('epoll-shim', required: true)
: dependency('', required: false))
libinotify_dep = (build_machine.system() == 'freebsd'
? dependency('libinotify', required: true)
: dependency('', required: false))
# On FreeBSD, libintl library is required for gettext
libintl_dep = cc.find_library('intl', required: false)
if not libintl_dep.found()
libintl_dep = dependency('intl', required: false)
endif
summary({'intl support': libintl_dep.found()}, bool_yn: true)
need_alsa = get_option('pipewire-alsa').enabled() or 'media-session' in get_option('session-managers')
alsa_dep = dependency('alsa', version : '>=1.1.7', required: need_alsa)
summary({'pipewire-alsa': alsa_dep.found()}, bool_yn: true)
if build_machine.system() == 'freebsd'
# On FreeBSD the OpenSSL library may come from base or a package.
# Check for a package first and fallback to the base library if we can't find it via pkgconfig
openssl_lib = dependency('openssl', required: false)
if not openssl_lib.found()
openssl_lib = declare_dependency(link_args : [ '-lssl', '-lcrypto'])
endif
else
openssl_lib = dependency('openssl', required: get_option('raop'))
endif
summary({'OpenSSL (for raop-sink)': openssl_lib.found()}, bool_yn: true)
lilv_lib = dependency('lilv-0', required: get_option('lv2'))
summary({'lilv (for lv2 plugins)': lilv_lib.found()}, bool_yn: true)
cdata.set('HAVE_LILV', lilv_lib.found())
check_functions = [
['gettid', '#include <unistd.h>', ['-D_GNU_SOURCE'], []],
['memfd_create', '#include <sys/mman.h>', ['-D_GNU_SOURCE'], []],
['getrandom', '#include <stddef.h>\n#include <sys/random.h>', ['-D_GNU_SOURCE'], []],
['sigabbrev_np', '#include <string.h>', ['-D_GNU_SOURCE'], []],
['XSetIOErrorExitHandler', '#include <X11/Xlib.h>', [], [x11_dep]],
]
foreach f : check_functions
cdata.set('HAVE_' + f.get(0).to_upper(),
cc.has_function(f.get(0),
prefix: f.get(1),
args: f.get(2),
dependencies: f.get(3)))
endforeach
installed_tests_metadir = pipewire_datadir / 'installed-tests' / pipewire_name
installed_tests_execdir = pipewire_libexecdir / 'installed-tests' / pipewire_name
installed_tests_enabled = get_option('installed_tests').allowed()
installed_tests_template = files('template.test.in')
if get_option('tests').allowed()
gstack = find_program('gstack', required : false)
cdata.set('HAVE_GSTACK', gstack.found())
endif
subdir('po')
subdir('spa')
subdir('src')
if get_option('tests').allowed()
subdir('test')
endif
configure_file(output : 'config.h',
configuration : cdata)
if get_option('pipewire-jack').allowed()
subdir('pipewire-jack')
endif
if get_option('pipewire-v4l2').allowed()
subdir('pipewire-v4l2')
endif
if alsa_dep.found()
subdir('pipewire-alsa/alsa-plugins')
subdir('pipewire-alsa/conf')
subdir('pipewire-alsa/tests')
endif
generate_manpages = false
if get_option('man').allowed()
rst2man = find_program('rst2man', required: false)
if not rst2man.found()
rst2man = find_program('rst2man.py', required: get_option('man'))
endif
if rst2man.found()
generate_manpages = true
endif
endif
summary({'Manpage generation': generate_manpages}, bool_yn: true)
subdir('man')
doxygen = find_program('doxygen', required : get_option('docs'))
if doxygen.found()
subdir('doc')
endif
setenv = find_program('pw-uninstalled.sh')
run_target('pw-uninstalled',
command : [setenv,
'-b@0@'.format(meson.project_build_root()),
'-v@0@'.format(pipewire_version)]
)
devenv = environment()
builddir = meson.project_build_root()
srcdir = meson.project_source_root()
devenv.set('PIPEWIRE_CONFIG_DIR', pipewire_dep.get_variable('confdatadir'))
devenv.set('PIPEWIRE_MODULE_DIR', pipewire_dep.get_variable('moduledir'))
devenv.set('SPA_PLUGIN_DIR', spa_dep.get_variable('plugindir'))
devenv.set('SPA_DATA_DIR', spa_dep.get_variable('datadir'))
devenv.set('GST_PLUGIN_PATH', builddir / 'src'/ 'gst')
devenv.set('ALSA_PLUGIN_DIR', builddir / 'pipewire-alsa' / 'alsa-plugins')
devenv.set('ACP_PATHS_DIR', srcdir / 'spa' / 'plugins' / 'alsa' / 'mixer' / 'paths')
devenv.set('ACP_PROFILES_DIR', srcdir / 'spa' / 'plugins' / 'alsa' / 'mixer' / 'profile-sets')
devenv.set('LD_LIBRARY_PATH', builddir / 'pipewire-jack' / 'src')
devenv.set('PW_UNINSTALLED', '1')
meson.add_devenv(devenv)

239
meson_options.txt Normal file
View File

@ -0,0 +1,239 @@
option('docdir',
type : 'string',
description : 'Directory for installing documentation to (defaults to pipewire_datadir/doc/meson.project_name() )')
option('docs',
description: 'Build documentation',
type: 'feature',
value: 'disabled')
option('examples',
description: 'Build examples',
type: 'feature',
value: 'enabled')
option('man',
description: 'Build manpages',
type: 'feature',
value: 'auto')
option('tests',
description: 'Build tests',
type: 'feature',
value: 'enabled',
yield : true)
option('installed_tests',
description: 'Install manual and automated test executables',
type: 'feature',
value: 'disabled')
option('gstreamer',
description: 'Build GStreamer plugins',
type: 'feature',
value: 'auto')
option('gstreamer-device-provider',
description: 'Build GStreamer device provider plugin',
type: 'feature',
value: 'auto')
option('systemd',
description: 'Enable systemd integration',
type: 'feature',
value: 'auto')
option('systemd-system-service',
description: 'Install systemd system service file',
type: 'feature',
value: 'disabled')
option('systemd-user-service',
description: 'Install systemd user service file (ignored without systemd)',
type: 'feature',
value: 'enabled')
option('pipewire-alsa',
description: 'Enable pipewire-alsa integration',
type: 'feature',
value: 'auto')
option('pipewire-jack',
description: 'Enable pipewire-jack integration',
type: 'feature',
value: 'enabled')
option('pipewire-v4l2',
description: 'Enable pipewire-v4l2 integration',
type: 'feature',
value: 'enabled')
option('jack-devel',
description: 'Install jack development files',
type: 'boolean',
value: 'false')
option('libjack-path',
description: 'Where to install the libjack.so library',
type: 'string')
option('libv4l2-path',
description: 'Where to install the libpw-v4l2.so library',
type: 'string')
option('spa-plugins',
description: 'Enable spa plugins integration',
type: 'feature',
value: 'enabled')
option('alsa',
description: 'Enable alsa spa plugin integration',
type: 'feature',
value: 'auto')
option('audiomixer',
description: 'Enable audiomixer spa plugin integration',
type: 'feature',
value: 'enabled')
option('audioconvert',
description: 'Enable audioconvert spa plugin integration',
type: 'feature',
value: 'enabled')
option('bluez5',
description: 'Enable bluez5 spa plugin integration',
type: 'feature',
value: 'auto')
option('bluez5-backend-hsp-native',
description: 'Enable HSP in native backend in bluez5 spa plugin',
type: 'feature',
value: 'enabled')
option('bluez5-backend-hfp-native',
description: 'Enable HFP in native backend in bluez5 spa plugin',
type: 'feature',
value: 'enabled')
option('bluez5-backend-ofono',
description: 'Enable oFono HFP backend in bluez5 spa plugin (no dependency on oFono)',
type: 'feature',
value: 'enabled')
option('bluez5-backend-hsphfpd',
description: 'Enable hsphfpd backend in bluez5 spa plugin (no dependency on hsphfpd)',
type: 'feature',
value: 'enabled')
option('bluez5-codec-aptx',
description: 'Enable AptX Qualcomm open source codec implementation',
type: 'feature',
value: 'auto')
option('bluez5-codec-ldac',
description: 'Enable LDAC Sony open source codec implementation',
type: 'feature',
value: 'auto')
option('bluez5-codec-aac',
description: 'Enable Fraunhofer FDK AAC open source codec implementation',
type: 'feature',
value: 'auto')
option('control',
description: 'Enable control spa plugin integration',
type: 'feature',
value: 'enabled')
option('audiotestsrc',
description: 'Enable audiotestsrc spa plugin integration',
type: 'feature',
value: 'enabled')
option('ffmpeg',
description: 'Enable ffmpeg spa plugin integration',
type: 'feature',
value: 'disabled')
option('jack',
description: 'Enable jack spa plugin integration',
type: 'feature',
value: 'auto')
option('support',
description: 'Enable support spa plugin integration',
type: 'feature',
value: 'enabled')
option('evl',
description: 'Enable EVL support spa plugin integration',
type: 'feature',
value: 'disabled')
option('test',
description: 'Enable test spa plugin integration',
type: 'feature',
value: 'disabled')
option('v4l2',
description: 'Enable v4l2 spa plugin integration',
type: 'feature',
value: 'auto')
option('dbus',
description: 'Enable code that depends on dbus',
type: 'feature',
value: 'enabled')
option('libcamera',
description: 'Enable libcamera spa plugin integration',
type: 'feature',
value: 'auto')
option('videoconvert',
description: 'Enable videoconvert spa plugin integration',
type: 'feature',
value: 'enabled')
option('videotestsrc',
description: 'Enable videotestsrc spa plugin integration',
type: 'feature',
value: 'enabled')
option('volume',
description: 'Enable volume spa plugin integration',
type: 'feature',
value: 'enabled')
option('vulkan',
description: 'Enable vulkan spa plugin integration',
type: 'feature',
value: 'disabled')
option('pw-cat',
description: 'Build pw-cat/pw-play/pw-record',
type: 'feature',
value: 'auto')
option('udev',
description: 'Enable Udev integration',
type: 'feature',
value: 'auto')
option('udevrulesdir',
type : 'string',
description : 'Directory for udev rules (defaults to /lib/udev/rules.d)')
option('systemd-system-unit-dir',
type : 'string',
description : 'Directory for system systemd units (defaults to /usr/lib/systemd/system)')
option('systemd-user-unit-dir',
type : 'string',
description : 'Directory for user systemd units (defaults to /usr/lib/systemd/user)')
option('sdl2',
description: 'Enable code that depends on SDL 2',
type: 'feature',
value: 'auto')
option('sndfile',
description: 'Enable code that depends on libsndfile',
type: 'feature',
value: 'auto')
option('libpulse',
description: 'Enable code that depends on libpulse',
type: 'feature',
value: 'auto')
option('roc',
description: 'Enable code that depends on roc toolkit',
type: 'feature',
value: 'auto')
option('avahi',
description: 'Enable code that depends on avahi',
type: 'feature',
value: 'auto')
option('echo-cancel-webrtc',
description : 'Enable WebRTC-based echo canceller',
type : 'feature',
value : 'auto')
option('libusb',
description: 'Enable code that depends on libusb',
type: 'feature',
value: 'auto')
option('session-managers',
description : 'Session managers to build (can be [] for none or an absolute path)',
type : 'array',
value : ['media-session'])
option('raop',
description: 'Enable module for Remote Audio Output Protocol',
type: 'feature',
value: 'auto')
option('lv2',
description: 'Enable loading of LV2 plugins',
type: 'feature',
value: 'auto')
option('x11',
description: 'Enable code that depends on X11',
type: 'feature',
value: 'auto')
option('x11-xfixes',
description: 'Enable code that depends on XFixes',
type: 'feature',
value: 'auto')
option('libcanberra',
description: 'Enable code that depends on libcanberra',
type: 'feature',
value: 'auto')

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
pipewire_alsa_plugin_pcm_sources = [
'pcm_pipewire.c',
]
pipewire_alsa_plugin_ctl_sources = [
'ctl_pipewire.c',
]
pipewire_alsa_plugin_c_args = [
'-DPIC',
]
pipewire_alsa_pcm_plugin = shared_library('asound_module_pcm_pipewire',
pipewire_alsa_plugin_pcm_sources,
c_args : pipewire_alsa_plugin_c_args,
include_directories : [configinc],
dependencies : [pipewire_dep, alsa_dep],
install : true,
install_dir : pipewire_libdir / 'alsa-lib',
)
pipewire_alsa_ctl_plugin = shared_library('asound_module_ctl_pipewire',
pipewire_alsa_plugin_ctl_sources,
c_args : pipewire_alsa_plugin_c_args,
include_directories : [configinc],
dependencies : [pipewire_dep, alsa_dep],
install : true,
install_dir : pipewire_libdir / 'alsa-lib',
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
# Add a specific named PipeWire pcm
defaults.pipewire.server "pipewire-0"
defaults.pipewire.node "-1"
defaults.pipewire.exclusive false
defaults.pipewire.role ""
pcm.pipewire {
@args [ SERVER NODE EXCLUSIVE ROLE ]
@args.SERVER {
type string
default {
@func refer
name defaults.pipewire.server
}
}
@args.NODE {
type string
default {
@func refer
name defaults.pipewire.node
}
}
@args.EXCLUSIVE {
type integer
default {
@func refer
name defaults.pipewire.exclusive
}
}
@args.ROLE {
type string
default {
@func refer
name defaults.pipewire.role
}
}
type pipewire
server $SERVER
playback_node $NODE
capture_node $NODE
exclusive $EXCLUSIVE
role $ROLE
hint {
show on
description "PipeWire Sound Server"
}
}
ctl.pipewire {
@args.SERVER {
type string
default {
@func refer
name defaults.pipewire.server
}
}
type pipewire
server $SERVER
}

View File

@ -0,0 +1,13 @@
pcm.!default {
type pipewire
playback_node "-1"
capture_node "-1"
hint {
show on
description "Default ALSA Output (currently PipeWire Media Server)"
}
}
ctl.!default {
type pipewire
}

View File

@ -0,0 +1,5 @@
alsaconfdir = pipewire_datadir / 'alsa' / 'alsa.conf.d'
install_data(['50-pipewire.conf', '99-pipewire-default.conf'],
install_dir: alsaconfdir,
)

View File

@ -0,0 +1,23 @@
test_apps = [
[ 'test-pipewire-alsa-stress', [alsa_dep, pthread_lib] ],
]
foreach a : test_apps
executable('pw-' + a[0], a[0] + '.c',
dependencies : a[1],
include_directories: [includes_inc],
install : installed_tests_enabled,
install_dir : installed_tests_execdir
)
if installed_tests_enabled
test_conf = configuration_data()
test_conf.set('exec', installed_tests_execdir / 'pw-' + a[0])
configure_file(
input: installed_tests_template,
output: 'pw-' + a[0] + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf
)
endif
endforeach

View File

@ -0,0 +1,151 @@
/* PipeWire
*
* Copyright © 2021 Axis Communications AB
*
* 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.
*/
/*
[title]
Stress test using pipewire-alsa.
[title]
*/
#include <alsa/asoundlib.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#define DEFAULT_PCM "pipewire"
#define DEFAULT_RATE 44100
#define DEFAULT_CHANNELS 2
#define N_THREADS 20
static void *
thread_func(void *data)
{
snd_pcm_t *pcm = NULL;
snd_pcm_hw_params_t *params;
int res;
long n = (long)data;
unsigned int sample_rate = DEFAULT_RATE;
res = snd_pcm_open(&pcm, DEFAULT_PCM, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (res < 0) {
fprintf(stderr, "open failed: %s\n", snd_strerror(res));
pcm = NULL;
goto fail;
}
printf("opened %ld\n", n);
snd_pcm_hw_params_alloca(&params);
res = snd_pcm_hw_params_any(pcm, params);
if (res < 0) {
fprintf(stderr, "params_any failed: %s\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (res < 0) {
fprintf(stderr, "set_access failed: %s\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S32_LE);
if (res < 0) {
fprintf(stderr, "set_format failed: %s\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_rate_near(pcm, params, &sample_rate, 0);
if (res < 0) {
fprintf(stderr, "set_rate_near failed: %s\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params_set_channels(pcm, params, DEFAULT_CHANNELS);
if (res < 0) {
fprintf(stderr, "set_channels failed: %s\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_hw_params(pcm, params);
if (res < 0) {
fprintf(stderr, "params failed: %s\n", snd_strerror(res));
goto fail;
}
res = snd_pcm_prepare(pcm);
if (res < 0) {
fprintf(stderr, "prepare failed: %s (%d)\n", snd_strerror(res), res);
goto fail;
}
printf("prepared %ld\n", n);
res = snd_pcm_close(pcm);
if (res < 0) {
fprintf(stderr, "close failed: %s\n", snd_strerror(res));
pcm = NULL;
goto fail;
}
printf("closed %ld\n", n);
return NULL;
fail:
if (pcm != NULL) {
res = snd_pcm_close(pcm);
if (res < 0) {
fprintf(stderr, "close failed: %s\n", snd_strerror(res));
}
}
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
pthread_t t[N_THREADS] = { 0 };
long n;
int s;
/* avoid rtkit in this test */
setenv("PIPEWIRE_CONFIG_NAME", "client.conf", false);
while (true) {
for (n=0; n < N_THREADS; n++) {
if ((s = pthread_create(&(t[n]), NULL, thread_func, (void *)n)) != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(s));
exit(EXIT_FAILURE);
}
printf("created %ld\n", n);
}
for (n=0; n < N_THREADS; n++) {
if (t[n] != 0 && (s = pthread_join(t[n], NULL)) != 0) {
fprintf(stderr, "pthread_join: %s\n", strerror(s));
exit(EXIT_FAILURE);
}
printf("joined %ld\n", n);
t[n] = 0;
}
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,203 @@
/* PipeWire
*
* Copyright © 2019 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.
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <SDL2/SDL.h>
#include <jack/jack.h>
#include <pipewire-jack-extensions.h>
#define MAX_BUFFERS 64
#define JACK_DEFAULT_VIDEO_TYPE "32 bit float RGBA video"
#define CLAMP(v,low,high) \
({ \
__typeof__(v) _v = (v); \
__typeof__(low) _low = (low); \
__typeof__(high) _high = (high); \
(_v < _low) ? _low : (_v > _high) ? _high : _v; \
})
struct pixel {
float r, g, b, a;
};
struct data {
const char *path;
SDL_Renderer *renderer;
SDL_Window *window;
SDL_Texture *texture;
SDL_Texture *cursor;
jack_client_t *client;
const char *client_name;
jack_port_t *in_port;
jack_image_size_t size;
int counter;
SDL_Rect rect;
SDL_Rect cursor_rect;
};
static void handle_events(struct data *data)
{
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
exit(0);
break;
}
}
}
static int
process (jack_nframes_t nframes, void *arg)
{
struct data *data = (struct data*)arg;
void *sdata, *ddata;
int sstride, dstride;
uint32_t i, j;
uint8_t *src, *dst;
sdata = jack_port_get_buffer (data->in_port, nframes);
handle_events(data);
if (SDL_LockTexture(data->texture, NULL, &ddata, &dstride) < 0) {
fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError());
goto done;
}
/* copy video image in texture */
sstride = data->size.stride;
src = sdata;
dst = ddata;
for (i = 0; i < data->size.height; i++) {
struct pixel *p = (struct pixel *) src;
for (j = 0; j < data->size.width; j++) {
dst[j * 4 + 0] = CLAMP(lrintf(p[j].r * 255.0f), 0, 255);
dst[j * 4 + 1] = CLAMP(lrintf(p[j].g * 255.0f), 0, 255);
dst[j * 4 + 2] = CLAMP(lrintf(p[j].b * 255.0f), 0, 255);
dst[j * 4 + 3] = CLAMP(lrintf(p[j].a * 255.0f), 0, 255);
}
src += sstride;
dst += dstride;
}
SDL_UnlockTexture(data->texture);
SDL_RenderClear(data->renderer);
SDL_RenderCopy(data->renderer, data->texture, &data->rect, NULL);
SDL_RenderPresent(data->renderer);
done:
return 0;
}
int main(int argc, char *argv[])
{
struct data data = { 0, };
jack_options_t options = JackNullOption;
jack_status_t status;
int res;
data.client = jack_client_open ("video-dsp-play", options, &status);
if (data.client == NULL) {
fprintf (stderr, "jack_client_open() failed, "
"status = 0x%2.0x\n", status);
if (status & JackServerFailed) {
fprintf (stderr, "Unable to connect to JACK server\n");
}
exit (1);
}
if (status & JackServerStarted) {
fprintf (stderr, "JACK server started\n");
}
if (status & JackNameNotUnique) {
data.client_name = jack_get_client_name(data.client);
fprintf (stderr, "unique name `%s' assigned\n", data.client_name);
}
jack_set_process_callback (data.client, process, &data);
if ((res = jack_get_video_image_size(data.client, &data.size)) < 0) {
fprintf(stderr, "can't get video size: %d %s\n", res, strerror(-res));
return -1;
}
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "can't initialize SDL: %s\n", SDL_GetError());
return -1;
}
if (SDL_CreateWindowAndRenderer
(data.size.width, data.size.height, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) {
fprintf(stderr, "can't create window: %s\n", SDL_GetError());
return -1;
}
data.texture = SDL_CreateTexture(data.renderer,
SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STREAMING,
data.size.width,
data.size.height);
data.rect.x = 0;
data.rect.y = 0;
data.rect.w = data.size.width;
data.rect.h = data.size.height;
data.in_port = jack_port_register (data.client, "input",
JACK_DEFAULT_VIDEO_TYPE,
JackPortIsInput, 0);
if (data.in_port == NULL) {
fprintf(stderr, "no more JACK ports available\n");
exit (1);
}
if (jack_activate (data.client)) {
fprintf (stderr, "cannot activate client");
exit (1);
}
while (1) {
sleep (1);
}
jack_client_close (data.client);
SDL_DestroyTexture(data.texture);
SDL_DestroyRenderer(data.renderer);
SDL_DestroyWindow(data.window);
return 0;
}

View File

@ -0,0 +1,658 @@
/* -*- Mode: C ; c-basic-offset: 4 -*- */
/*
JACK control API
Copyright (C) 2008 Nedko Arnaudov
Copyright (C) 2008 GRAME
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/**
* @file jack/control.h
* @ingroup publicheader
* @brief JACK control API
*
*/
#ifndef JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED
#define JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED
#include <jack/types.h>
#include <jack/jslist.h>
#include <jack/systemdeps.h>
#if !defined(sun) && !defined(__sun__)
#include <stdbool.h>
#endif
/** Parameter types, intentionally similar to jack_driver_param_type_t */
typedef enum
{
JackParamInt = 1, /**< @brief value type is a signed integer */
JackParamUInt, /**< @brief value type is an unsigned integer */
JackParamChar, /**< @brief value type is a char */
JackParamString, /**< @brief value type is a string with max size of ::JACK_PARAM_STRING_MAX+1 chars */
JackParamBool, /**< @brief value type is a boolean */
} jackctl_param_type_t;
/** Driver types */
typedef enum
{
JackMaster = 1, /**< @brief master driver */
JackSlave /**< @brief slave driver */
} jackctl_driver_type_t;
/** @brief Max value that jackctl_param_type_t type can have */
#define JACK_PARAM_MAX (JackParamBool + 1)
/** @brief Max length of string parameter value, excluding terminating null char */
#define JACK_PARAM_STRING_MAX 127
/** @brief Type for parameter value */
/* intentionally similar to jack_driver_param_value_t */
union jackctl_parameter_value
{
uint32_t ui; /**< @brief member used for ::JackParamUInt */
int32_t i; /**< @brief member used for ::JackParamInt */
char c; /**< @brief member used for ::JackParamChar */
char str[JACK_PARAM_STRING_MAX + 1]; /**< @brief member used for ::JackParamString */
bool b; /**< @brief member used for ::JackParamBool */
};
/** opaque type for server object */
typedef struct jackctl_server jackctl_server_t;
/** opaque type for driver object */
typedef struct jackctl_driver jackctl_driver_t;
/** opaque type for internal client object */
typedef struct jackctl_internal jackctl_internal_t;
/** opaque type for parameter object */
typedef struct jackctl_parameter jackctl_parameter_t;
/** opaque type for sigmask object */
typedef struct jackctl_sigmask jackctl_sigmask_t;
#ifdef __cplusplus
extern "C" {
#endif
#if 0
} /* Adjust editor indent */
#endif
/**
* @defgroup ControlAPI The API for starting and controlling a JACK server
* @{
*/
/**
* Call this function to setup process signal handling. As a general
* rule, it is required for proper operation for the server object.
*
* @param flags signals setup flags, use 0 for none. Currently no
* flags are defined
*
* @return the configurated signal set.
*/
jackctl_sigmask_t *
jackctl_setup_signals(
unsigned int flags);
/**
* Call this function to wait on a signal set.
*
* @param signals signals set to wait on
*/
void
jackctl_wait_signals(
jackctl_sigmask_t * signals);
/**
* \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN
* NEW JACK PROJECTS
*
* @deprecated Please use jackctl_server_create2().
*/
jackctl_server_t *
jackctl_server_create(
bool (* on_device_acquire)(const char * device_name),
void (* on_device_release)(const char * device_name));
/**
* Call this function to create server object.
*
* @param on_device_acquire - Optional callback to be called before device is acquired. If false is returned, device usage will fail
* @param on_device_release - Optional callback to be called after device is released.
* @param on_device_reservation_loop - Optional callback to be called when looping/idling the reservation.
*
* @return server object handle, NULL if creation of server object
* failed. Successfully created server object must be destroyed with
* paired call to ::jackctl_server_destroy
*/
jackctl_server_t *
jackctl_server_create2(
bool (* on_device_acquire)(const char * device_name),
void (* on_device_release)(const char * device_name),
void (* on_device_reservation_loop)(void));
/**
* Call this function to destroy server object.
*
* @param server server object handle to destroy
*/
void
jackctl_server_destroy(
jackctl_server_t * server);
/**
* Call this function to open JACK server
*
* @param server server object handle
* @param driver driver to use
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_open(
jackctl_server_t * server,
jackctl_driver_t * driver);
/**
* Call this function to start JACK server
*
* @param server server object handle
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_start(
jackctl_server_t * server);
/**
* Call this function to stop JACK server
*
* @param server server object handle
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_stop(
jackctl_server_t * server);
/**
* Call this function to close JACK server
*
* @param server server object handle
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_close(
jackctl_server_t * server);
/**
* Call this function to get list of available drivers. List node data
* pointers is a driver object handle (::jackctl_driver_t).
*
* @param server server object handle to get drivers for
*
* @return Single linked list of driver object handles. Must not be
* modified. Always same for same server object.
*/
const JSList *
jackctl_server_get_drivers_list(
jackctl_server_t * server);
/**
* Call this function to get list of server parameters. List node data
* pointers is a parameter object handle (::jackctl_parameter_t).
*
* @param server server object handle to get parameters for
*
* @return Single linked list of parameter object handles. Must not be
* modified. Always same for same server object.
*/
const JSList *
jackctl_server_get_parameters(
jackctl_server_t * server);
/**
* Call this function to get list of available internal clients. List node data
* pointers is a internal client object handle (::jackctl_internal_t).
*
* @param server server object handle to get internal clients for
*
* @return Single linked list of internal client object handles. Must not be
* modified. Always same for same server object.
*/
const JSList *
jackctl_server_get_internals_list(
jackctl_server_t * server);
/**
* Call this function to load one internal client.
* (can be used when the server is running)
*
* @param server server object handle
* @param internal internal to use
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_load_internal(
jackctl_server_t * server,
jackctl_internal_t * internal);
/**
* Call this function to unload one internal client.
* (can be used when the server is running)
*
* @param server server object handle
* @param internal internal to unload
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_unload_internal(
jackctl_server_t * server,
jackctl_internal_t * internal);
/**
* Call this function to load a session file.
* (can be used when the server is running)
*
* @param server server object handle
* @param file the session file to load, containing a list of
* internal clients and connections to be made.
*
* @return success status: true - success, false - fail
*/
bool jackctl_server_load_session_file(
jackctl_server_t * server_ptr,
const char * file);
/**
* Call this function to add a slave in the driver slave list.
* (cannot be used when the server is running that is between
* jackctl_server_start and jackctl_server_stop)
*
* @param server server object handle
* @param driver driver to add in the driver slave list.
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_add_slave(jackctl_server_t * server,
jackctl_driver_t * driver);
/**
* Call this function to remove a slave from the driver slave list.
* (cannot be used when the server is running that is between
* jackctl_server_start and jackctl_server_stop)
*
* @param server server object handle
* @param driver driver to remove from the driver slave list.
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_remove_slave(jackctl_server_t * server,
jackctl_driver_t * driver);
/**
* Call this function to switch master driver.
*
* @param server server object handle
* @param driver driver to switch to
*
* @return success status: true - success, false - fail
*/
bool
jackctl_server_switch_master(jackctl_server_t * server,
jackctl_driver_t * driver);
/**
* Call this function to get name of driver.
*
* @param driver driver object handle to get name of
*
* @return driver name. Must not be modified. Always same for same
* driver object.
*/
const char *
jackctl_driver_get_name(
jackctl_driver_t * driver);
/**
* Call this function to get type of driver.
*
* @param driver driver object handle to get name of
*
* @return driver type. Must not be modified. Always same for same
* driver object.
*/
jackctl_driver_type_t
jackctl_driver_get_type(
jackctl_driver_t * driver);
/**
* Call this function to get list of driver parameters. List node data
* pointers is a parameter object handle (::jackctl_parameter_t).
*
* @param driver driver object handle to get parameters for
*
* @return Single linked list of parameter object handles. Must not be
* modified. Always same for same driver object.
*/
const JSList *
jackctl_driver_get_parameters(
jackctl_driver_t * driver);
/**
* Call this function to parse parameters for a driver.
*
* @param driver driver object handle
* @param argc parameter list len
* @param argv parameter list, as an array of char*
*
* @return success status: true - success, false - fail
*/
int
jackctl_driver_params_parse(
jackctl_driver_t * driver,
int argc,
char* argv[]);
/**
* Call this function to get name of internal client.
*
* @param internal internal object handle to get name of
*
* @return internal name. Must not be modified. Always same for same
* internal object.
*/
const char *
jackctl_internal_get_name(
jackctl_internal_t * internal);
/**
* Call this function to get list of internal parameters. List node data
* pointers is a parameter object handle (::jackctl_parameter_t).
*
* @param internal internal object handle to get parameters for
*
* @return Single linked list of parameter object handles. Must not be
* modified. Always same for same internal object.
*/
const JSList *
jackctl_internal_get_parameters(
jackctl_internal_t * internal);
/**
* Call this function to get parameter name.
*
* @param parameter parameter object handle to get name of
*
* @return parameter name. Must not be modified. Always same for same
* parameter object.
*/
const char *
jackctl_parameter_get_name(
jackctl_parameter_t * parameter);
/**
* Call this function to get parameter short description.
*
* @param parameter parameter object handle to get short description of
*
* @return parameter short description. Must not be modified. Always
* same for same parameter object.
*/
const char *
jackctl_parameter_get_short_description(
jackctl_parameter_t * parameter);
/**
* Call this function to get parameter long description.
*
* @param parameter parameter object handle to get long description of
*
* @return parameter long description. Must not be modified. Always
* same for same parameter object.
*/
const char *
jackctl_parameter_get_long_description(
jackctl_parameter_t * parameter);
/**
* Call this function to get parameter type.
*
* @param parameter parameter object handle to get type of
*
* @return parameter type. Always same for same parameter object.
*/
jackctl_param_type_t
jackctl_parameter_get_type(
jackctl_parameter_t * parameter);
/**
* Call this function to get parameter character.
*
* @param parameter parameter object handle to get character of
*
* @return character.
*/
char
jackctl_parameter_get_id(
jackctl_parameter_t * parameter);
/**
* Call this function to check whether parameter has been set, or its
* default value is being used.
*
* @param parameter parameter object handle to check
*
* @return true - parameter is set, false - parameter is using default
* value.
*/
bool
jackctl_parameter_is_set(
jackctl_parameter_t * parameter);
/**
* Call this function to reset parameter to its default value.
*
* @param parameter parameter object handle to reset value of
*
* @return success status: true - success, false - fail
*/
bool
jackctl_parameter_reset(
jackctl_parameter_t * parameter);
/**
* Call this function to get parameter value.
*
* @param parameter parameter object handle to get value of
*
* @return parameter value.
*/
union jackctl_parameter_value
jackctl_parameter_get_value(
jackctl_parameter_t * parameter);
/**
* Call this function to set parameter value.
*
* @param parameter parameter object handle to get value of
* @param value_ptr pointer to variable containing parameter value
*
* @return success status: true - success, false - fail
*/
bool
jackctl_parameter_set_value(
jackctl_parameter_t * parameter,
const union jackctl_parameter_value * value_ptr);
/**
* Call this function to get parameter default value.
*
* @param parameter parameter object handle to get default value of
*
* @return parameter default value.
*/
union jackctl_parameter_value
jackctl_parameter_get_default_value(
jackctl_parameter_t * parameter);
/**
* Call this function check whether parameter has range constraint.
*
* @param parameter object handle of parameter to check
*
* @return whether parameter has range constraint.
*/
bool
jackctl_parameter_has_range_constraint(
jackctl_parameter_t * parameter);
/**
* Call this function check whether parameter has enumeration constraint.
*
* @param parameter object handle of parameter to check
*
* @return whether parameter has enumeration constraint.
*/
bool
jackctl_parameter_has_enum_constraint(
jackctl_parameter_t * parameter);
/**
* Call this function get how many enumeration values parameter has.
*
* @param parameter object handle of parameter
*
* @return number of enumeration values
*/
uint32_t
jackctl_parameter_get_enum_constraints_count(
jackctl_parameter_t * parameter);
/**
* Call this function to get parameter enumeration value.
*
* @param parameter object handle of parameter
* @param index index of parameter enumeration value
*
* @return enumeration value.
*/
union jackctl_parameter_value
jackctl_parameter_get_enum_constraint_value(
jackctl_parameter_t * parameter,
uint32_t index);
/**
* Call this function to get parameter enumeration value description.
*
* @param parameter object handle of parameter
* @param index index of parameter enumeration value
*
* @return enumeration value description.
*/
const char *
jackctl_parameter_get_enum_constraint_description(
jackctl_parameter_t * parameter,
uint32_t index);
/**
* Call this function to get parameter range.
*
* @param parameter object handle of parameter
* @param min_ptr pointer to variable receiving parameter minimum value
* @param max_ptr pointer to variable receiving parameter maximum value
*/
void
jackctl_parameter_get_range_constraint(
jackctl_parameter_t * parameter,
union jackctl_parameter_value * min_ptr,
union jackctl_parameter_value * max_ptr);
/**
* Call this function to check whether parameter constraint is strict,
* i.e. whether supplying non-matching value will not work for sure.
*
* @param parameter parameter object handle to check
*
* @return whether parameter constraint is strict.
*/
bool
jackctl_parameter_constraint_is_strict(
jackctl_parameter_t * parameter);
/**
* Call this function to check whether parameter has fake values,
* i.e. values have no user meaningful meaning and only value
* description is meaningful to user.
*
* @param parameter parameter object handle to check
*
* @return whether parameter constraint is strict.
*/
bool
jackctl_parameter_constraint_is_fake_value(
jackctl_parameter_t * parameter);
/**
* Call this function to log an error message.
*
* @param format string
*/
void
jack_error(
const char *format,
...);
/**
* Call this function to log an information message.
*
* @param format string
*/
void
jack_info(
const char *format,
...);
/**
* Call this function to log an information message but only when
* verbose mode is enabled.
*
* @param format string
*/
void
jack_log(
const char *format,
...);
/* @} */
#if 0
{ /* Adjust editor indent */
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* #ifndef JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED */

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2004 Jack O'Quin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __jack_intclient_h__
#define __jack_intclient_h__
#ifdef __cplusplus
extern "C"
{
#endif
#include <jack/types.h>
/**
* Get an internal client's name. This is useful when @ref
* JackUseExactName was not specified on jack_internal_client_load()
* and @ref JackNameNotUnique status was returned. In that case, the
* actual name will differ from the @a client_name requested.
*
* @param client requesting JACK client's handle.
*
* @param intclient handle returned from jack_internal_client_load()
* or jack_internal_client_handle().
*
* @return NULL if unsuccessful, otherwise pointer to the internal
* client name obtained from the heap via malloc(). The caller should
* jack_free() this storage when no longer needed.
*/
char *jack_get_internal_client_name (jack_client_t *client,
jack_intclient_t intclient);
/**
* Return the @ref jack_intclient_t handle for an internal client
* running in the JACK server.
*
* @param client requesting JACK client's handle.
*
* @param client_name for the internal client of no more than
* jack_client_name_size() characters. The name scope is local to the
* current server.
*
* @param status (if non-NULL) an address for JACK to return
* information from this operation. This status word is formed by
* OR-ing together the relevant @ref JackStatus bits.
*
* @return Opaque internal client handle if successful. If 0, the
* internal client was not found, and @a *status includes the @ref
* JackNoSuchClient and @ref JackFailure bits.
*/
jack_intclient_t jack_internal_client_handle (jack_client_t *client,
const char *client_name,
jack_status_t *status);
/**
* Load an internal client into the JACK server.
*
* Internal clients run inside the JACK server process. They can use
* most of the same functions as external clients. Each internal
* client is built as a shared object module, which must declare
* jack_initialize() and jack_finish() entry points called at load and
* unload times. See @ref inprocess.c for an example.
*
* @param client loading JACK client's handle.
*
* @param client_name of at most jack_client_name_size() characters
* for the internal client to load. The name scope is local to the
* current server.
*
* @param options formed by OR-ing together @ref JackOptions bits.
* Only the @ref JackLoadOptions bits are valid.
*
* @param status (if non-NULL) an address for JACK to return
* information from the load operation. This status word is formed by
* OR-ing together the relevant @ref JackStatus bits.
*
* <b>Optional parameters:</b> depending on corresponding [@a options
* bits] additional parameters may follow @a status (in this order).
*
* @arg [@ref JackLoadName] <em>(char *) load_name</em> is the shared
* object file from which to load the new internal client (otherwise
* use the @a client_name).
*
* @arg [@ref JackLoadInit] <em>(char *) load_init</em> an arbitrary
* string passed to the internal client's jack_initialize() routine
* (otherwise NULL), of no more than @ref JACK_LOAD_INIT_LIMIT bytes.
*
* @return Opaque internal client handle if successful. If this is 0,
* the load operation failed, the internal client was not loaded, and
* @a *status includes the @ref JackFailure bit.
*/
jack_intclient_t jack_internal_client_load (jack_client_t *client,
const char *client_name,
jack_options_t options,
jack_status_t *status, ...);
/**
* Unload an internal client from a JACK server. This calls the
* intclient's jack_finish() entry point then removes it. See @ref
* inprocess.c for an example.
*
* @param client unloading JACK client's handle.
*
* @param intclient handle returned from jack_internal_client_load() or
* jack_internal_client_handle().
*
* @return 0 if successful, otherwise @ref JackStatus bits.
*/
jack_status_t jack_internal_client_unload (jack_client_t *client,
jack_intclient_t intclient);
#ifdef __cplusplus
}
#endif
#endif /* __jack_intclient_h__ */

1477
pipewire-jack/jack/jack.h Normal file

File diff suppressed because it is too large Load Diff

293
pipewire-jack/jack/jslist.h Normal file
View File

@ -0,0 +1,293 @@
/*
Based on gslist.c from glib-1.2.9 (LGPL).
Adaption to JACK, Copyright (C) 2002 Kai Vehmanen.
- replaced use of gtypes with normal ANSI C types
- glib's memory allocation routines replaced with
malloc/free calls
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __jack_jslist_h__
#define __jack_jslist_h__
#include <stdlib.h>
#include <jack/systemdeps.h>
#ifdef sun
#define __inline__
#endif
typedef struct _JSList JSList;
typedef int (*JCompareFunc) (void* a, void* b);
struct _JSList
{
void *data;
JSList *next;
};
static __inline__
JSList*
jack_slist_alloc (void)
{
JSList *new_list;
new_list = (JSList*)malloc(sizeof(JSList));
if (new_list) {
new_list->data = NULL;
new_list->next = NULL;
}
return new_list;
}
static __inline__
JSList*
jack_slist_prepend (JSList* list, void* data)
{
JSList *new_list;
new_list = (JSList*)malloc(sizeof(JSList));
if (new_list) {
new_list->data = data;
new_list->next = list;
}
return new_list;
}
#define jack_slist_next(slist) ((slist) ? (((JSList *)(slist))->next) : NULL)
static __inline__
JSList*
jack_slist_last (JSList *list)
{
if (list) {
while (list->next)
list = list->next;
}
return list;
}
static __inline__
JSList*
jack_slist_remove_link (JSList *list,
JSList *link)
{
JSList *tmp;
JSList *prev;
prev = NULL;
tmp = list;
while (tmp) {
if (tmp == link) {
if (prev)
prev->next = tmp->next;
if (list == tmp)
list = list->next;
tmp->next = NULL;
break;
}
prev = tmp;
tmp = tmp->next;
}
return list;
}
static __inline__
void
jack_slist_free (JSList *list)
{
while (list) {
JSList *next = list->next;
free(list);
list = next;
}
}
static __inline__
void
jack_slist_free_1 (JSList *list)
{
if (list) {
free(list);
}
}
static __inline__
JSList*
jack_slist_remove (JSList *list,
void *data)
{
JSList *tmp;
JSList *prev;
prev = NULL;
tmp = list;
while (tmp) {
if (tmp->data == data) {
if (prev)
prev->next = tmp->next;
if (list == tmp)
list = list->next;
tmp->next = NULL;
jack_slist_free (tmp);
break;
}
prev = tmp;
tmp = tmp->next;
}
return list;
}
static __inline__
unsigned int
jack_slist_length (JSList *list)
{
unsigned int length;
length = 0;
while (list) {
length++;
list = list->next;
}
return length;
}
static __inline__
JSList*
jack_slist_find (JSList *list,
void *data)
{
while (list) {
if (list->data == data)
break;
list = list->next;
}
return list;
}
static __inline__
JSList*
jack_slist_copy (JSList *list)
{
JSList *new_list = NULL;
if (list) {
JSList *last;
new_list = jack_slist_alloc ();
new_list->data = list->data;
last = new_list;
list = list->next;
while (list) {
last->next = jack_slist_alloc ();
last = last->next;
last->data = list->data;
list = list->next;
}
}
return new_list;
}
static __inline__
JSList*
jack_slist_append (JSList *list,
void *data)
{
JSList *new_list;
JSList *last;
new_list = jack_slist_alloc ();
new_list->data = data;
if (list) {
last = jack_slist_last (list);
last->next = new_list;
return list;
} else
return new_list;
}
static __inline__
JSList*
jack_slist_sort_merge (JSList *l1,
JSList *l2,
JCompareFunc compare_func)
{
JSList list, *l;
l = &list;
while (l1 && l2) {
if (compare_func(l1->data, l2->data) < 0) {
l = l->next = l1;
l1 = l1->next;
} else {
l = l->next = l2;
l2 = l2->next;
}
}
l->next = l1 ? l1 : l2;
return list.next;
}
static __inline__
JSList*
jack_slist_sort (JSList *list,
JCompareFunc compare_func)
{
JSList *l1, *l2;
if (!list)
return NULL;
if (!list->next)
return list;
l1 = list;
l2 = list->next;
while ((l2 = l2->next) != NULL) {
if ((l2 = l2->next) == NULL)
break;
l1 = l1->next;
}
l2 = l1->next;
l1->next = NULL;
return jack_slist_sort_merge (jack_slist_sort (list, compare_func),
jack_slist_sort (l2, compare_func),
compare_func);
}
#endif /* __jack_jslist_h__ */

View File

@ -0,0 +1,322 @@
/*
Copyright (C) 2011-2014 David Robillard
Copyright (C) 2013 Paul Davis
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
* @file jack/metadata.h
* @ingroup publicheader
* @brief JACK Metadata API
*
*/
#ifndef __jack_metadata_h__
#define __jack_metadata_h__
#include <jack/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup Metadata Metadata API.
* @{
*/
/**
* A single property (key:value pair).
*
* Although there is no semantics imposed on metadata keys and values, it is
* much less useful to use it to associate highly structured data with a port
* (or client), since this then implies the need for some (presumably
* library-based) code to parse the structure and be able to use it.
*
* The real goal of the metadata API is to be able to tag ports (and clients)
* with small amounts of data that is outside of the core JACK API but
* nevertheless useful.
*/
typedef struct {
/** The key of this property (URI string). */
const char* key;
/** The property value (null-terminated string). */
const char* data;
/**
* Type of data, either a MIME type or URI.
*
* If type is NULL or empty, the data is assumed to be a UTF-8 encoded
* string (text/plain). The data is a null-terminated string regardless of
* type, so values can always be copied, but clients should not try to
* interpret values of an unknown type.
*
* Example values:
* - image/png;base64 (base64 encoded PNG image)
* - http://www.w3.org/2001/XMLSchema#int (integer)
*
* Official types are preferred, but clients may use any syntactically
* valid MIME type (which start with a type and slash, like "text/...").
* If a URI type is used, it must be a complete absolute URI
* (which start with a scheme and colon, like "http:").
*/
const char* type;
} jack_property_t;
/**
* Set a property on @p subject.
*
* See the above documentation for rules about @p subject and @p key.
* @param subject The subject to set the property on.
* @param key The key of the property.
* @param value The value of the property.
* @param type The type of the property. See the discussion of
* types in the definition of jack_property_t above.
* @return 0 on success.
*/
int
jack_set_property(jack_client_t*,
jack_uuid_t subject,
const char* key,
const char* value,
const char* type);
/**
* Get a property on @p subject.
*
* @param subject The subject to get the property from.
* @param key The key of the property.
* @param value Set to the value of the property if found, or NULL otherwise.
* The caller must free this value with jack_free().
* @param type The type of the property if set, or NULL. See the discussion
* of types in the definition of jack_property_t above.
* If non-null, the caller must free this value with jack_free().
*
* @return 0 on success, -1 if the @p subject has no @p key property.
*/
int
jack_get_property(jack_uuid_t subject,
const char* key,
char** value,
char** type);
/**
* A description of a subject (a set of properties).
*/
typedef struct {
jack_uuid_t subject; /**< Subject being described. */
uint32_t property_cnt; /**< Number of elements in "properties". */
jack_property_t* properties; /**< Array of properties. */
uint32_t property_size; /**< Private, do not use. */
} jack_description_t;
/**
* Free a description.
*
* @param desc a jack_description_t whose associated memory will all be released
* @param free_description_itself if non-zero, then @param desc will also be passed to free()
*/
void
jack_free_description (jack_description_t* desc, int free_description_itself);
/**
* Get a description of @p subject.
* @param subject The subject to get all properties of.
* @param desc Set to the description of subject if found, or NULL otherwise.
* The caller must free this value with jack_free_description().
* @return the number of properties, -1 if no @p subject with any properties exists.
*/
int
jack_get_properties (jack_uuid_t subject,
jack_description_t* desc);
/**
* Get descriptions for all subjects with metadata.
* @param descs Set to an array of descriptions.
* The caller must free each of these with jack_free_description(),
* and the array itself with jack_free().
* @return the number of descriptions, or -1 on error.
*/
int
jack_get_all_properties (jack_description_t** descs);
/**
* Remove a single property on a subject.
*
* @param client The JACK client making the request to remove the property.
* @param subject The subject to remove the property from.
* @param key The key of the property to be removed.
*
* @return 0 on success, -1 otherwise
*/
int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key);
/**
* Remove all properties on a subject.
*
* @param client The JACK client making the request to remove some properties.
* @param subject The subject to remove all properties from.
*
* @return a count of the number of properties removed, or -1 on error.
*/
int jack_remove_properties (jack_client_t* client, jack_uuid_t subject);
/**
* Remove all properties.
*
* WARNING!! This deletes all metadata managed by a running JACK server.
* Data lost cannot be recovered (though it can be recreated by new calls
* to jack_set_property()).
*
* @param client The JACK client making the request to remove all properties
*
* @return 0 on success, -1 otherwise
*/
int jack_remove_all_properties (jack_client_t* client);
typedef enum {
PropertyCreated,
PropertyChanged,
PropertyDeleted
} jack_property_change_t;
/**
* Prototype for the client supplied function that is called by the
* engine anytime a property or properties have been modified.
*
* Note that when the key is empty, it means all properties have been
* modified. This is often used to indicate that the removal of all keys.
*
* @param subject The subject the change relates to, this can be either a client or port
* @param key The key of the modified property (URI string)
* @param change Wherever the key has been created, changed or deleted
* @param arg pointer to a client supplied structure
*/
typedef void (*JackPropertyChangeCallback)(jack_uuid_t subject,
const char* key,
jack_property_change_t change,
void* arg);
/**
* Arrange for @p client to call @p callback whenever a property is created,
* changed or deleted.
*
* @param client the JACK client making the request
* @param callback the function to be invoked when a property change occurs
* @param arg the argument to be passed to @param callback when it is invoked
*
* @return 0 success, -1 otherwise.
*/
int jack_set_property_change_callback (jack_client_t* client,
JackPropertyChangeCallback callback,
void* arg);
/**
* A value that identifies what the hardware port is connected to (an external
* device of some kind). Possible values might be "E-Piano" or "Master 2 Track".
*/
extern const char* JACK_METADATA_CONNECTED;
/**
* The supported event types of an event port.
*
* This is a kludge around Jack only supporting MIDI, particularly for OSC.
* This property is a comma-separated list of event types, currently "MIDI" or
* "OSC". If this contains "OSC", the port may carry OSC bundles (first byte
* '#') or OSC messages (first byte '/'). Note that the "status byte" of both
* OSC events is not a valid MIDI status byte, so MIDI clients that check the
* status byte will gracefully ignore OSC messages if the user makes an
* inappropriate connection.
*/
extern const char* JACK_METADATA_EVENT_TYPES;
/**
* A value that should be shown when attempting to identify the
* specific hardware outputs of a client. Typical values might be
* "ADAT1", "S/PDIF L" or "MADI 43".
*/
extern const char* JACK_METADATA_HARDWARE;
/**
* A value with a MIME type of "image/png;base64" that is an encoding of an
* NxN (with 32 < N <= 128) image to be used when displaying a visual
* representation of that client or port.
*/
extern const char* JACK_METADATA_ICON_LARGE;
/**
* The name of the icon for the subject (typically client).
*
* This is used for looking up icons on the system, possibly with many sizes or
* themes. Icons should be searched for according to the freedesktop Icon
*
* Theme Specification:
* https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
extern const char* JACK_METADATA_ICON_NAME;
/**
* A value with a MIME type of "image/png;base64" that is an encoding of an
* NxN (with N <=32) image to be used when displaying a visual representation
* of that client or port.
*/
extern const char* JACK_METADATA_ICON_SMALL;
/**
* Order for a port.
*
* This is used to specify the best order to show ports in user interfaces.
* The value MUST be an integer. There are no other requirements, so there may
* be gaps in the orders for several ports. Applications should compare the
* orders of ports to determine their relative order, but must not assign any
* other relevance to order values.
*
* It is encouraged to use http://www.w3.org/2001/XMLSchema#int as the type.
*/
extern const char* JACK_METADATA_ORDER;
/**
* A value that should be shown to the user when displaying a port to the user,
* unless the user has explicitly overridden that a request to show the port
* name, or some other key value.
*/
extern const char* JACK_METADATA_PRETTY_NAME;
/**
*/
extern const char* JACK_METADATA_PORT_GROUP;
/**
* The type of an audio signal.
*
* This property allows audio ports to be tagged with a "meaning". The value
* is a simple string. Currently, the only type is "CV", for "control voltage"
* ports. Hosts SHOULD be take care to not treat CV ports as audible and send
* their output directly to speakers. In particular, CV ports are not
* necessarily periodic at all and may have very high DC.
*/
extern const char* JACK_METADATA_SIGNAL_TYPE;
/**
* @}
*/
#ifdef __cplusplus
} /* namespace */
#endif
#endif /* __jack_metadata_h__ */

View File

@ -0,0 +1,197 @@
/*
Copyright (C) 2004 Ian Esten
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JACK_MIDIPORT_H
#define __JACK_MIDIPORT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <jack/weakmacros.h>
#include <jack/types.h>
#include <stdlib.h>
/** Type for raw event data contained in @ref jack_midi_event_t. */
typedef unsigned char jack_midi_data_t;
/** A Jack MIDI event. */
typedef struct _jack_midi_event
{
jack_nframes_t time; /**< Sample index at which event is valid */
size_t size; /**< Number of bytes of data in \a buffer */
jack_midi_data_t *buffer; /**< Raw MIDI data */
} jack_midi_event_t;
/**
* @defgroup MIDIAPI Reading and writing MIDI data
* @{
*/
/** Get number of events in a port buffer.
*
* @param port_buffer Port buffer from which to retrieve event.
* @return number of events inside @a port_buffer
*/
uint32_t
jack_midi_get_event_count(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
/** Get a MIDI event from an event port buffer.
*
* Jack MIDI is normalised, the MIDI event returned by this function is
* guaranteed to be a complete MIDI event (the status byte will always be
* present, and no realtime events will interspersed with the event).
*
* This rule does not apply to System Exclusive MIDI messages
* since they can be of arbitrary length.
* To maintain smooth realtime operation such events CAN be delivered
* as multiple, non-normalised events.
* The maximum size of one event "chunk" depends on the MIDI backend in use.
* For example the midiseq driver will create chunks of 256 bytes.
* The first SysEx "chunked" event starts with 0xF0 and the last
* delivered chunk ends with 0xF7.
* To receive the full SysEx message, a caller of jack_midi_event_get()
* must concatenate chunks until a chunk ends with 0xF7.
*
* @param event Event structure to store retrieved event in.
* @param port_buffer Port buffer from which to retrieve event.
* @param event_index Index of event to retrieve.
* @return 0 on success, ENODATA if buffer is empty.
*/
int
jack_midi_event_get(jack_midi_event_t *event,
void *port_buffer,
uint32_t event_index) JACK_OPTIONAL_WEAK_EXPORT;
/** Clear an event buffer.
*
* This should be called at the beginning of each process cycle before calling
* @ref jack_midi_event_reserve or @ref jack_midi_event_write. This
* function may not be called on an input port's buffer.
*
* @param port_buffer Port buffer to clear (must be an output port buffer).
*/
void
jack_midi_clear_buffer(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
/** Reset an event buffer (from data allocated outside of JACK).
*
* This should be called at the beginning of each process cycle before calling
* @ref jack_midi_event_reserve or @ref jack_midi_event_write. This
* function may not be called on an input port's buffer.
*
* @deprecated Please use jack_midi_clear_buffer().
*
* @param port_buffer Port buffer to reset.
*/
void
jack_midi_reset_buffer(void *port_buffer) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
/** Get the size of the largest event that can be stored by the port.
*
* This function returns the current space available, taking into account
* events already stored in the port.
*
* @param port_buffer Port buffer to check size of.
*/
size_t
jack_midi_max_event_size(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
/** Allocate space for an event to be written to an event port buffer.
*
* Clients are to write the actual event data to be written starting at the
* pointer returned by this function. Clients must not write more than
* @a data_size bytes into this buffer. Clients must write normalised
* MIDI data to the port - no running status and no (1-byte) realtime
* messages interspersed with other messages (realtime messages are fine
* when they occur on their own, like other messages).
*
* Events must be written in order, sorted by their sample offsets.
* JACK will not sort the events for you, and will refuse to store
* out-of-order events.
*
* @param port_buffer Buffer to write event to.
* @param time Sample offset of event.
* @param data_size Length of event's raw data in bytes.
* @return Pointer to the beginning of the reserved event's data buffer, or
* NULL on error (ie not enough space).
*/
jack_midi_data_t*
jack_midi_event_reserve(void *port_buffer,
jack_nframes_t time,
size_t data_size) JACK_OPTIONAL_WEAK_EXPORT;
/** Write an event into an event port buffer.
*
* This function is simply a wrapper for @ref jack_midi_event_reserve
* which writes the event data into the space reserved in the buffer.
*
* Clients must not write more than
* @a data_size bytes into this buffer. Clients must write normalised
* MIDI data to the port - no running status and no (1-byte) realtime
* messages interspersed with other messages (realtime messages are fine
* when they occur on their own, like other messages).
*
* Events must be written in order, sorted by their sample offsets.
* JACK will not sort the events for you, and will refuse to store
* out-of-order events.
*
* @param port_buffer Buffer to write event to.
* @param time Sample offset of event.
* @param data Message data to be written.
* @param data_size Length of @a data in bytes.
* @return 0 on success, ENOBUFS if there's not enough space in buffer for event.
*/
int
jack_midi_event_write(void *port_buffer,
jack_nframes_t time,
const jack_midi_data_t *data,
size_t data_size) JACK_OPTIONAL_WEAK_EXPORT;
/** Get the number of events that could not be written to @a port_buffer.
*
* This function returning a non-zero value implies @a port_buffer is full.
* Currently the only way this can happen is if events are lost on port mixdown.
*
* @param port_buffer Port to receive count for.
* @returns Number of events that could not be written to @a port_buffer.
*/
uint32_t
jack_midi_get_lost_event_count(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
/*@}*/
#ifdef __cplusplus
}
#endif
#endif /* __JACK_MIDIPORT_H */

429
pipewire-jack/jack/net.h Normal file
View File

@ -0,0 +1,429 @@
/*
Copyright (C) 2009-2010 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __net_h__
#define __net_h__
#ifdef __cplusplus
extern "C"
{
#endif
#include <jack/systemdeps.h>
#include <jack/types.h>
#include <jack/weakmacros.h>
#define DEFAULT_MULTICAST_IP "225.3.19.154"
#define DEFAULT_PORT 19000
#define DEFAULT_MTU 1500
#define MASTER_NAME_SIZE 256
// Possible error codes
#define NO_ERROR 0
#define SOCKET_ERROR -1
#define SYNC_PACKET_ERROR -2
#define DATA_PACKET_ERROR -3
#define RESTART_CB_API 1
enum JackNetEncoder {
JackFloatEncoder = 0, // samples are transmitted as float
JackIntEncoder = 1, // samples are transmitted as 16 bits integer
JackCeltEncoder = 2, // samples are transmitted using CELT codec (http://www.celt-codec.org/)
JackOpusEncoder = 3, // samples are transmitted using OPUS codec (http://www.opus-codec.org/)
};
typedef struct {
int audio_input; // from master or to slave (-1 to take master audio physical inputs)
int audio_output; // to master or from slave (-1 to take master audio physical outputs)
int midi_input; // from master or to slave (-1 to take master MIDI physical inputs)
int midi_output; // to master or from slave (-1 to take master MIDI physical outputs)
int mtu; // network Maximum Transmission Unit
int time_out; // in second, -1 means infinite
int encoder; // encoder type (one of JackNetEncoder)
int kbps; // KB per second for CELT or OPUS codec
int latency; // network latency in number of buffers
} jack_slave_t;
typedef struct {
int audio_input; // master audio physical outputs (-1 to take slave wanted audio inputs)
int audio_output; // master audio physical inputs (-1 to take slave wanted audio outputs)
int midi_input; // master MIDI physical outputs (-1 to take slave wanted MIDI inputs)
int midi_output; // master MIDI physical inputs (-1 to take slave wanted MIDI outputs)
jack_nframes_t buffer_size; // master buffer size
jack_nframes_t sample_rate; // master sample rate
char master_name[MASTER_NAME_SIZE]; // master machine name
int time_out; // in second, -1 means infinite
int partial_cycle; // if 'true', partial buffers will be used
} jack_master_t;
/**
* jack_net_slave_t is an opaque type. You may only access it using the
* API provided.
*/
typedef struct _jack_net_slave jack_net_slave_t;
/**
* Open a network connection with the master machine.
*
* @param ip the multicast address of the master
* @param port the connection port
* @param name the JACK client name
* @param request a connection request structure
* @param result a connection result structure
*
* @return Opaque net handle if successful or NULL in case of error.
*/
jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result);
/**
* Close the network connection with the master machine.
*
* @param net the network connection to be closed
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_net_slave_close(jack_net_slave_t* net);
/**
* Prototype for Process callback.
*
* @param nframes buffer size
* @param audio_input number of audio inputs
* @param audio_input_buffer an array of audio input buffers (from master)
* @param midi_input number of MIDI inputs
* @param midi_input_buffer an array of MIDI input buffers (from master)
* @param audio_output number of audio outputs
* @param audio_output_buffer an array of audio output buffers (to master)
* @param midi_output number of MIDI outputs
* @param midi_output_buffer an array of MIDI output buffers (to master)
* @param arg pointer to a client supplied structure supplied by jack_set_net_process_callback()
*
* @return zero on success, non-zero on error
*/
typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size,
int audio_input,
float** audio_input_buffer,
int midi_input,
void** midi_input_buffer,
int audio_output,
float** audio_output_buffer,
int midi_output,
void** midi_output_buffer,
void* data);
/**
* Set network process callback.
*
* @param net the network connection
* @param net_callback the process callback
* @param arg pointer to a client supplied structure
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_net_slave_process_callback(jack_net_slave_t * net, JackNetSlaveProcessCallback net_callback, void *arg);
/**
* Start processing thread, the net_callback will start to be called.
*
* @param net the network connection
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_net_slave_activate(jack_net_slave_t* net);
/**
* Stop processing thread.
*
* @param net the network connection
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_net_slave_deactivate(jack_net_slave_t* net);
/**
* Test if slave is still active.
*
* @param net the network connection
*
* @return a boolean
*/
int jack_net_slave_is_active(jack_net_slave_t* net);
/**
* Prototype for BufferSize callback.
*
* @param nframes buffer size
* @param arg pointer to a client supplied structure supplied by jack_set_net_buffer_size_callback()
*
* @return zero on success, non-zero on error
*/
typedef int (*JackNetSlaveBufferSizeCallback)(jack_nframes_t nframes, void *arg);
/**
* Set network buffer size callback.
*
* @param net the network connection
* @param bufsize_callback the buffer size callback
* @param arg pointer to a client supplied structure
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg);
/**
* Prototype for SampleRate callback.
*
* @param nframes sample rate
* @param arg pointer to a client supplied structure supplied by jack_set_net_sample_rate_callback()
*
* @return zero on success, non-zero on error
*/
typedef int (*JackNetSlaveSampleRateCallback)(jack_nframes_t nframes, void *arg);
/**
* Set network sample rate callback.
*
* @param net the network connection
* @param samplerate_callback the sample rate callback
* @param arg pointer to a client supplied structure
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg);
/**
* Prototype for server Shutdown callback (if not set, the client will just restart, waiting for an available master again).
*
* @param arg pointer to a client supplied structure supplied by jack_set_net_shutdown_callback()
*/
typedef void (*JackNetSlaveShutdownCallback)(void* arg);
/**
* Set network shutdown callback.
*
* @param net the network connection
* @param shutdown_callback the shutdown callback
* @param arg pointer to a client supplied structure
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
/**
* Prototype for server Restart callback : this is the new preferable way to be notified when the master has disappeared.
* The client may want to retry connecting a certain number of time (which will be done using the time_out value given in jack_net_slave_open)
* by returning 0. Otherwise returning a non-zero error code will definitively close the connection
* (and jack_net_slave_is_active will later on return false).
* If both Shutdown and Restart are supplied, Restart callback will be used.
*
* @param arg pointer to a client supplied structure supplied by jack_set_net_restart_callback()
*
* @return 0 on success, otherwise a non-zero error code
*/
typedef int (*JackNetSlaveRestartCallback)(void* arg);
/**
* Set network restart callback.
*
* @param net the network connection
* @param restart_callback the shutdown callback
* @param arg pointer to a client supplied structure
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_net_slave_restart_callback(jack_net_slave_t *net, JackNetSlaveRestartCallback restart_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Prototype for server Error callback.
*
* @param error_code an error code (see "Possible error codes")
* @param arg pointer to a client supplied structure supplied by jack_set_net_error_callback()
*/
typedef void (*JackNetSlaveErrorCallback) (int error_code, void* arg);
/**
* Set error restart callback.
*
* @param net the network connection
* @param error_callback the error callback
* @param arg pointer to a client supplied structure
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_net_slave_error_callback(jack_net_slave_t *net, JackNetSlaveErrorCallback error_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
/**
* jack_net_master_t is an opaque type, you may only access it using the API provided.
*/
typedef struct _jack_net_master jack_net_master_t;
/**
* Open a network connection with the slave machine.
*
* @param ip the multicast address of the master
* @param port the connection port
* @param request a connection request structure
* @param result a connection result structure
*
* @return Opaque net handle if successful or NULL in case of error.
*/
jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result);
/**
* Close the network connection with the slave machine.
*
* @param net the network connection to be closed
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_net_master_close(jack_net_master_t* net);
/**
* Receive sync and data from the network (complete buffer).
*
* @param net the network connection
* @param audio_input number of audio inputs
* @param audio_input_buffer an array of audio input buffers
* @param midi_input number of MIDI inputs
* @param midi_input_buffer an array of MIDI input buffers
*
* @return zero on success, non-zero on error
*/
int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer);
/**
* Receive sync and data from the network (incomplete buffer).
*
* @param net the network connection
* @param audio_input number of audio inputs
* @param audio_input_buffer an array of audio input buffers
* @param midi_input number of MIDI inputs
* @param midi_input_buffer an array of MIDI input buffers
* @param frames the number of frames to receive
*
* @return zero on success, non-zero on error
*/
int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames);
/**
* Send sync and data to the network (complete buffer).
*
* @param net the network connection
* @param audio_output number of audio outputs
* @param audio_output_buffer an array of audio output buffers
* @param midi_output number of MIDI outputs
* @param midi_output_buffer an array of MIDI output buffers
*
* @return zero on success, non-zero on error
*/
int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer);
/**
* Send sync and data to the network (incomplete buffer).
*
* @param net the network connection
* @param audio_output number of audio outputs
* @param audio_output_buffer an array of audio output buffers
* @param midi_output number of MIDI outputs
* @param midi_output_buffer an array of MIDI output buffers
* @param frames the number of frames to send
*
* @return zero on success, non-zero on error
*/
int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames);
// Experimental Adapter API
/**
* jack_adapter_t is an opaque type, you may only access it using the API provided.
*/
typedef struct _jack_adapter jack_adapter_t;
/**
* Create an adapter.
*
* @param input number of audio inputs
* @param output of audio outputs
* @param host_buffer_size the host buffer size in frames
* @param host_sample_rate the host buffer sample rate
* @param adapted_buffer_size the adapted buffer size in frames
* @param adapted_sample_rate the adapted buffer sample rate
*
* @return 0 on success, otherwise a non-zero error code
*/
jack_adapter_t* jack_create_adapter(int input, int output,
jack_nframes_t host_buffer_size,
jack_nframes_t host_sample_rate,
jack_nframes_t adapted_buffer_size,
jack_nframes_t adapted_sample_rate);
/**
* Destroy an adapter.
*
* @param adapter the adapter to be destroyed
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_destroy_adapter(jack_adapter_t* adapter);
/**
* Flush internal state of an adapter.
*
* @param adapter the adapter to be flushed
*
* @return 0 on success, otherwise a non-zero error code
*/
void jack_flush_adapter(jack_adapter_t* adapter);
/**
* Push input to and pull output from adapter ringbuffer.
*
* @param adapter the adapter
* @param input an array of audio input buffers
* @param output an array of audio output buffers
* @param frames number of frames
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames);
/**
* Pull input from and push output to adapter ringbuffer.
*
* @param adapter the adapter
* @param input an array of audio input buffers
* @param output an array of audio output buffers
* @param frames number of frames
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames);
#ifdef __cplusplus
}
#endif
#endif /* __net_h__ */

View File

@ -0,0 +1,243 @@
/*
Copyright (C) 2000 Paul Davis
Copyright (C) 2003 Rohan Drape
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _RINGBUFFER_H
#define _RINGBUFFER_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <sys/types.h>
/** @file ringbuffer.h
*
* A set of library functions to make lock-free ringbuffers available
* to JACK clients. The `capture_client.c' (in the example_clients
* directory) is a fully functioning user of this API.
*
* The key attribute of a ringbuffer is that it can be safely accessed
* by two threads simultaneously -- one reading from the buffer and
* the other writing to it -- without using any synchronization or
* mutual exclusion primitives. For this to work correctly, there can
* only be a single reader and a single writer thread. Their
* identities cannot be interchanged.
*/
typedef struct {
char *buf;
size_t len;
}
jack_ringbuffer_data_t ;
typedef struct {
char *buf;
volatile size_t write_ptr;
volatile size_t read_ptr;
size_t size;
size_t size_mask;
int mlocked;
}
jack_ringbuffer_t ;
/**
* Allocates a ringbuffer data structure of a specified size. The
* caller must arrange for a call to jack_ringbuffer_free() to release
* the memory associated with the ringbuffer.
*
* @param sz the ringbuffer size in bytes.
*
* @return a pointer to a new jack_ringbuffer_t, if successful; NULL
* otherwise.
*/
jack_ringbuffer_t *jack_ringbuffer_create(size_t sz);
/**
* Frees the ringbuffer data structure allocated by an earlier call to
* jack_ringbuffer_create().
*
* @param rb a pointer to the ringbuffer structure.
*/
void jack_ringbuffer_free(jack_ringbuffer_t *rb);
/**
* Fill a data structure with a description of the current readable
* data held in the ringbuffer. This description is returned in a two
* element array of jack_ringbuffer_data_t. Two elements are needed
* because the data to be read may be split across the end of the
* ringbuffer.
*
* The first element will always contain a valid @a len field, which
* may be zero or greater. If the @a len field is non-zero, then data
* can be read in a contiguous fashion using the address given in the
* corresponding @a buf field.
*
* If the second element has a non-zero @a len field, then a second
* contiguous stretch of data can be read from the address given in
* its corresponding @a buf field.
*
* @param rb a pointer to the ringbuffer structure.
* @param vec a pointer to a 2 element array of jack_ringbuffer_data_t.
*
*/
void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb,
jack_ringbuffer_data_t *vec);
/**
* Fill a data structure with a description of the current writable
* space in the ringbuffer. The description is returned in a two
* element array of jack_ringbuffer_data_t. Two elements are needed
* because the space available for writing may be split across the end
* of the ringbuffer.
*
* The first element will always contain a valid @a len field, which
* may be zero or greater. If the @a len field is non-zero, then data
* can be written in a contiguous fashion using the address given in
* the corresponding @a buf field.
*
* If the second element has a non-zero @a len field, then a second
* contiguous stretch of data can be written to the address given in
* the corresponding @a buf field.
*
* @param rb a pointer to the ringbuffer structure.
* @param vec a pointer to a 2 element array of jack_ringbuffer_data_t.
*/
void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb,
jack_ringbuffer_data_t *vec);
/**
* Read data from the ringbuffer.
*
* @param rb a pointer to the ringbuffer structure.
* @param dest a pointer to a buffer where data read from the
* ringbuffer will go.
* @param cnt the number of bytes to read.
*
* @return the number of bytes read, which may range from 0 to cnt.
*/
size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt);
/**
* Read data from the ringbuffer. Opposed to jack_ringbuffer_read()
* this function does not move the read pointer. Thus it's
* a convenient way to inspect data in the ringbuffer in a
* continuous fashion. The price is that the data is copied
* into a user provided buffer. For "raw" non-copy inspection
* of the data in the ringbuffer use jack_ringbuffer_get_read_vector().
*
* @param rb a pointer to the ringbuffer structure.
* @param dest a pointer to a buffer where data read from the
* ringbuffer will go.
* @param cnt the number of bytes to read.
*
* @return the number of bytes read, which may range from 0 to cnt.
*/
size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt);
/**
* Advance the read pointer.
*
* After data have been read from the ringbuffer using the pointers
* returned by jack_ringbuffer_get_read_vector(), use this function to
* advance the buffer pointers, making that space available for future
* write operations.
*
* @param rb a pointer to the ringbuffer structure.
* @param cnt the number of bytes read.
*/
void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt);
/**
* Return the number of bytes available for reading.
*
* @param rb a pointer to the ringbuffer structure.
*
* @return the number of bytes available to read.
*/
size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb);
/**
* Lock a ringbuffer data block into memory.
*
* Uses the mlock() system call. This is not a realtime operation.
*
* @param rb a pointer to the ringbuffer structure.
*/
int jack_ringbuffer_mlock(jack_ringbuffer_t *rb);
/**
* Reset the read and write pointers, making an empty buffer.
*
* This is not thread safe.
*
* @param rb a pointer to the ringbuffer structure.
*/
void jack_ringbuffer_reset(jack_ringbuffer_t *rb);
/**
* Reset the internal "available" size, and read and write pointers, making an empty buffer.
*
* This is not thread safe.
*
* @param rb a pointer to the ringbuffer structure.
* @param sz the new size, that must be less than allocated size.
*/
void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz);
/**
* Write data into the ringbuffer.
*
* @param rb a pointer to the ringbuffer structure.
* @param src a pointer to the data to be written to the ringbuffer.
* @param cnt the number of bytes to write.
*
* @return the number of bytes write, which may range from 0 to cnt
*/
size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src,
size_t cnt);
/**
* Advance the write pointer.
*
* After data have been written the ringbuffer using the pointers
* returned by jack_ringbuffer_get_write_vector(), use this function
* to advance the buffer pointer, making the data available for future
* read operations.
*
* @param rb a pointer to the ringbuffer structure.
* @param cnt the number of bytes written.
*/
void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt);
/**
* Return the number of bytes available for writing.
*
* @param rb a pointer to the ringbuffer structure.
*
* @return the amount of free space (in bytes) available for writing.
*/
size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,302 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004 Jack O'Quin
Copyright (C) 2010 Torben Hohn
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __jack_session_h__
#define __jack_session_h__
#ifdef __cplusplus
extern "C" {
#endif
#include <jack/types.h>
#include <jack/weakmacros.h>
/**
* @defgroup SessionClientFunctions Session API for clients.
*
* @deprecated Use of JACK-Session is currently deprecated and unsupported.
* JACK developers recommend the use of NSM instead.
* See https://github.com/linuxaudio/new-session-manager
* @{
*/
/**
* Session event type.
*
* If a client can't save templates, i might just do a normal save.
*
* There is no "quit without saving" event because a client might refuse to
* quit when it has unsaved data, but other clients may have already quit.
* This results in too much confusion, so it is unsupported.
*/
enum JackSessionEventType {
/**
* Save the session completely.
*
* The client may save references to data outside the provided directory,
* but it must do so by creating a link inside the provided directory and
* referring to that in any save files. The client must not refer to data
* files outside the provided directory directly in save files, because
* this makes it impossible for the session manager to create a session
* archive for distribution or archival.
*/
JackSessionSave = 1,
/**
* Save the session completely, then quit.
*
* The rules for saving are exactly the same as for JackSessionSave.
*/
JackSessionSaveAndQuit = 2,
/**
* Save a session template.
*
* A session template is a "skeleton" of the session, but without any data.
* Clients must save a session that, when restored, will create the same
* ports as a full save would have. However, the actual data contained in
* the session may not be saved (e.g. a DAW would create the necessary
* tracks, but not save the actual recorded data).
*/
JackSessionSaveTemplate = 3
};
typedef enum JackSessionEventType jack_session_event_type_t;
/**
* @ref jack_session_flags_t bits
*/
enum JackSessionFlags {
/**
* An error occurred while saving.
*/
JackSessionSaveError = 0x01,
/**
* Client needs to be run in a terminal.
*/
JackSessionNeedTerminal = 0x02
};
/**
* Session flags.
*/
typedef enum JackSessionFlags jack_session_flags_t;
struct _jack_session_event {
/**
* The type of this session event.
*/
jack_session_event_type_t type;
/**
* Session directory path, with trailing separator.
*
* This directory is exclusive to the client; when saving the client may
* create any files it likes in this directory.
*/
const char *session_dir;
/**
* Client UUID which must be passed to jack_client_open on session load.
*
* The client can specify this in the returned command line, or save it
* in a state file within the session directory.
*/
const char *client_uuid;
/**
* Reply (set by client): the command line needed to restore the client.
*
* This is a platform dependent command line. It must contain
* ${SESSION_DIR} instead of the actual session directory path. More
* generally, just as in session files, clients should not include any
* paths outside the session directory here as this makes
* archival/distribution impossible.
*
* This field is set to NULL by Jack when the event is delivered to the
* client. The client must set to allocated memory that is safe to
* free(). This memory will be freed by jack_session_event_free.
*/
char *command_line;
/**
* Reply (set by client): Session flags.
*/
jack_session_flags_t flags;
/**
* Future flags. Set to zero for now.
*/
uint32_t future;
};
typedef struct _jack_session_event jack_session_event_t;
/**
* Prototype for the client supplied function that is called
* whenever a session notification is sent via jack_session_notify().
*
* Ownership of the memory of @a event is passed to the application.
* It must be freed using jack_session_event_free when it's not used anymore.
*
* The client must promptly call jack_session_reply for this event.
*
* @deprecated Use of JACK-Session is currently deprecated and unsupported.
* JACK developers recommend the use of NSM instead.
* See https://github.com/linuxaudio/new-session-manager
*
* @param event The event structure.
* @param arg Pointer to a client supplied structure.
*/
typedef void (*JackSessionCallback)(jack_session_event_t *event,
void *arg);
/**
* Tell the JACK server to call @a session_callback when a session event
* is to be delivered.
*
* setting more than one session_callback per process is probably a design
* error. if you have a multiclient application its more sensible to create
* a jack_client with only a session callback set.
*
* @deprecated Use of JACK-Session is currently deprecated and unsupported.
* JACK developers recommend the use of NSM instead.
* See https://github.com/linuxaudio/new-session-manager
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_session_callback (jack_client_t *client,
JackSessionCallback session_callback,
void *arg) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
/**
* Reply to a session event.
*
* This can either be called directly from the callback, or later from a
* different thread. For example, it is possible to push the event through a
* queue and execute the save code from the GUI thread.
*
* @deprecated Use of JACK-Session is currently deprecated and unsupported.
* JACK developers recommend the use of NSM instead.
* See https://github.com/linuxaudio/new-session-manager
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_session_reply (jack_client_t *client,
jack_session_event_t *event) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
/**
* Free memory used by a jack_session_event_t.
*
* This also frees the memory used by the command_line pointer, if its non NULL.
*
* @deprecated Use of JACK-Session is currently deprecated and unsupported.
* JACK developers recommend the use of NSM instead.
* See https://github.com/linuxaudio/new-session-manager
*/
void jack_session_event_free (jack_session_event_t *event) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
/**
* Get the assigned uuid for client.
* Safe to call from callback and all other threads.
*
* The caller is responsible for calling jack_free(3) on any non-NULL
* returned value.
*/
char *jack_client_get_uuid (jack_client_t *client) JACK_WEAK_EXPORT;
/**
* @}
*/
/**
* @defgroup JackSessionManagerAPI API for a session manager.
*
* @{
*/
typedef struct {
const char *uuid;
const char *client_name;
const char *command;
jack_session_flags_t flags;
} jack_session_command_t;
/**
* Send an event to all clients listening for session callbacks.
*
* The returned strings of the clients are accumulated and returned as an array
* of jack_session_command_t. its terminated by ret[i].uuid == NULL target ==
* NULL means send to all interested clients. otherwise a clientname
*/
jack_session_command_t *jack_session_notify (
jack_client_t* client,
const char *target,
jack_session_event_type_t type,
const char *path) JACK_WEAK_EXPORT;
/**
* Free the memory allocated by a session command.
*
* @deprecated Use of JACK-Session is currently deprecated and unsupported.
* JACK developers recommend the use of NSM instead.
* See https://github.com/linuxaudio/new-session-manager
*/
void jack_session_commands_free (jack_session_command_t *cmds) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
/**
* Reserve a client name and associate it with a UUID.
*
* When a client later calls jack_client_open() and specifies the UUID, jackd
* will assign the reserved name. This allows a session manager to know in
* advance under which client name its managed clients will appear.
*
* @return 0 on success, otherwise a non-zero error code
*/
int
jack_reserve_client_name (jack_client_t *client,
const char *name,
const char *uuid) JACK_WEAK_EXPORT;
/**
* Find out whether a client has set up a session callback.
*
* @deprecated Use of JACK-Session is currently deprecated and unsupported.
* JACK developers recommend the use of NSM instead.
* See https://github.com/linuxaudio/new-session-manager
*
* @return 0 when the client has no session callback, 1 when it has one.
* -1 on error.
*/
int
jack_client_has_session_callback (jack_client_t *client, const char *client_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2004 Rui Nuno Capela, Lee Revell
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
#ifndef __statistics_h__
#define __statistics_h__
#ifdef __cplusplus
extern "C"
{
#endif
#include <jack/types.h>
/**
* @return the maximum delay reported by the backend since
* startup or reset. When compared to the period size in usecs, this
* can be used to estimate the ideal period size for a given setup.
*/
float jack_get_max_delayed_usecs (jack_client_t *client);
/**
* @return the delay in microseconds due to the most recent XRUN
* occurrence. This probably only makes sense when called from a @ref
* JackXRunCallback defined using jack_set_xrun_callback().
*/
float jack_get_xrun_delayed_usecs (jack_client_t *client);
/**
* Reset the maximum delay counter. This would be useful
* to estimate the effect that a change to the configuration of a running
* system (e.g. toggling kernel preemption) has on the delay
* experienced by JACK, without having to restart the JACK engine.
*/
void jack_reset_max_delayed_usecs (jack_client_t *client);
#ifdef __cplusplus
}
#endif
#endif /* __statistics_h__ */

View File

@ -0,0 +1,141 @@
/*
Copyright (C) 2004-2012 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __jack_systemdeps_h__
#define __jack_systemdeps_h__
#ifndef POST_PACKED_STRUCTURE
#ifdef __GNUC__
/* POST_PACKED_STRUCTURE needs to be a macro which
expands into a compiler directive. The directive must
tell the compiler to arrange the preceding structure
declaration so that it is packed on byte-boundaries rather
than use the natural alignment of the processor and/or
compiler.
*/
#define PRE_PACKED_STRUCTURE
#define POST_PACKED_STRUCTURE __attribute__((__packed__))
#else
#ifdef _MSC_VER
#define PRE_PACKED_STRUCTURE1 __pragma(pack(push,1))
#define PRE_PACKED_STRUCTURE PRE_PACKED_STRUCTURE1
/* PRE_PACKED_STRUCTURE needs to be a macro which
expands into a compiler directive. The directive must
tell the compiler to arrange the following structure
declaration so that it is packed on byte-boundaries rather
than use the natural alignment of the processor and/or
compiler.
*/
#define POST_PACKED_STRUCTURE ;__pragma(pack(pop))
/* and POST_PACKED_STRUCTURE needs to be a macro which
restores the packing to its previous setting */
#else
#define PRE_PACKED_STRUCTURE
#define POST_PACKED_STRUCTURE
#endif /* _MSC_VER */
#endif /* __GNUC__ */
#endif
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(GNU_WIN32)
#ifdef __MINGW32__
# include <winsock2.h> // mingw gives warning if we include windows.h before winsock2.h
#endif
#include <windows.h>
#ifdef _MSC_VER /* Microsoft compiler */
#define __inline__ inline
#if (!defined(int8_t) && !defined(_STDINT_H))
#define __int8_t_defined
typedef INT8 int8_t;
typedef UINT8 uint8_t;
typedef INT16 int16_t;
typedef UINT16 uint16_t;
typedef INT32 int32_t;
typedef UINT32 uint32_t;
typedef INT64 int64_t;
typedef UINT64 uint64_t;
#endif
#elif __MINGW32__ /* MINGW */
#include <stdint.h>
#include <sys/types.h>
#else /* other compilers ...*/
#include <inttypes.h>
#include <pthread.h>
#include <sys/types.h>
#endif
#if !defined(_PTHREAD_H) && !defined(PTHREAD_WIN32)
/**
* to make jack API independent of different thread implementations,
* we define jack_native_thread_t to HANDLE here.
*/
typedef HANDLE jack_native_thread_t;
#else
#ifdef PTHREAD_WIN32 // Added by JE - 10-10-2011
#include <ptw32/pthread.h> // Makes sure we #include the ptw32 version !
#endif
/**
* to make jack API independent of different thread implementations,
* we define jack_native_thread_t to pthread_t here.
*/
typedef pthread_t jack_native_thread_t;
#endif
#endif /* _WIN32 && !__CYGWIN__ && !GNU_WIN32 */
#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32)
#if defined(__CYGWIN__) || defined(GNU_WIN32)
#include <stdint.h>
#endif
#include <inttypes.h>
#include <pthread.h>
#include <sys/types.h>
/**
* to make jack API independent of different thread implementations,
* we define jack_native_thread_t to pthread_t here.
*/
typedef pthread_t jack_native_thread_t;
#endif /* __APPLE__ || __linux__ || __sun__ || sun */
#if (defined(__arm__) || defined(__aarch64__) || defined(__mips__) || defined(__ppc__) || defined(__powerpc__)) && !defined(__APPLE__)
#undef POST_PACKED_STRUCTURE
#define POST_PACKED_STRUCTURE
#endif /* __arm__ || __aarch64__ || __mips__ || __ppc__ || __powerpc__ */
/** define JACK_LIB_EXPORT, useful for internal clients */
#if defined(_WIN32)
#define JACK_LIB_EXPORT __declspec(dllexport)
#elif defined(__GNUC__)
#define JACK_LIB_EXPORT __attribute__((visibility("default")))
#else
#define JACK_LIB_EXPORT
#endif
#endif /* __jack_systemdeps_h__ */

160
pipewire-jack/jack/thread.h Normal file
View File

@ -0,0 +1,160 @@
/*
Copyright (C) 2004 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __jack_thread_h__
#define __jack_thread_h__
#ifdef __cplusplus
extern "C"
{
#endif
#include <jack/systemdeps.h>
#include <jack/weakmacros.h>
/* use 512KB stack per thread - the default is way too high to be feasible
* with mlockall() on many systems */
#define THREAD_STACK 524288
/** @file thread.h
*
* Library functions to standardize thread creation for JACK and its
* clients. These interfaces hide some system variations in the
* handling of realtime scheduling and associated privileges.
*/
/**
* @defgroup ClientThreads Creating and managing client threads
* @{
*/
/**
* @returns if JACK is running with realtime scheduling, this returns
* the priority that any JACK-created client threads will run at.
* Otherwise returns -1.
*/
int jack_client_real_time_priority (jack_client_t*) JACK_OPTIONAL_WEAK_EXPORT;
/**
* @returns if JACK is running with realtime scheduling, this returns
* the maximum priority that a JACK client thread should use if the thread
* is subject to realtime scheduling. Otherwise returns -1.
*/
int jack_client_max_real_time_priority (jack_client_t*) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Attempt to enable realtime scheduling for a thread. On some
* systems that may require special privileges.
*
* @param thread POSIX thread ID.
* @param priority requested thread priority.
*
* @returns 0, if successful; EPERM, if the calling process lacks
* required realtime privileges; otherwise some other error number.
*/
int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Create a thread for JACK or one of its clients. The thread is
* created executing @a start_routine with @a arg as its sole
* argument.
*
* @param client the JACK client for whom the thread is being created. May be
* NULL if the client is being created within the JACK server.
* @param thread place to return POSIX thread ID.
* @param priority thread priority, if realtime.
* @param realtime true for the thread to use realtime scheduling. On
* some systems that may require special privileges.
* @param start_routine function the thread calls when it starts.
* @param arg parameter passed to the @a start_routine.
*
* @returns 0, if successful; otherwise some error number.
*/
int jack_client_create_thread (jack_client_t* client,
jack_native_thread_t *thread,
int priority,
int realtime, /* boolean */
void *(*start_routine)(void*),
void *arg) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Drop realtime scheduling for a thread.
*
* @param thread POSIX thread ID.
*
* @returns 0, if successful; otherwise an error number.
*/
int jack_drop_real_time_scheduling (jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Stop the thread, waiting for the thread handler to terminate.
*
* @param thread POSIX thread ID.
*
* @returns 0, if successful; otherwise an error number.
*/
int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Kill the thread.
*
* @param thread POSIX thread ID.
*
* @returns 0, if successful; otherwise an error number.
*/
int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT;
#ifndef _WIN32
typedef int (*jack_thread_creator_t)(pthread_t*,
const pthread_attr_t*,
void* (*function)(void*),
void* arg);
/**
* This function can be used in very very specialized cases
* where it is necessary that client threads created by JACK
* are created by something other than pthread_create(). After
* it is used, any threads that JACK needs for the client will
* will be created by calling the function passed to this
* function.
*
* No normal application/client should consider calling this.
* The specific case for which it was created involves running
* win32/x86 plugins under Wine on Linux, where it is necessary
* that all threads that might call win32 functions are known
* to Wine.
*
* Set it to NULL to restore thread creation function.
*
* @param creator a function that creates a new thread
*
*/
void jack_set_thread_creator (jack_thread_creator_t creator) JACK_OPTIONAL_WEAK_EXPORT;
#endif
/* @} */
#ifdef __cplusplus
}
#endif
#endif /* __jack_thread_h__ */

View File

@ -0,0 +1,247 @@
/*
Copyright (C) 2002 Paul Davis
Copyright (C) 2003 Jack O'Quin
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __jack_transport_h__
#define __jack_transport_h__
#ifdef __cplusplus
extern "C" {
#endif
#include <jack/types.h>
#include <jack/weakmacros.h>
/**
* @defgroup TransportControl Transport and Timebase control
* @{
*/
/**
* Called by the timebase master to release itself from that
* responsibility.
*
* If the timebase master releases the timebase or leaves the JACK
* graph for any reason, the JACK engine takes over at the start of
* the next process cycle. The transport state does not change. If
* rolling, it continues to play, with frame numbers as the only
* available position information.
*
* @see jack_set_timebase_callback
*
* @param client the JACK client structure.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_release_timebase (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Register (or unregister) as a slow-sync client, one that cannot
* respond immediately to transport position changes.
*
* The @a sync_callback will be invoked at the first available
* opportunity after its registration is complete. If the client is
* currently active this will be the following process cycle,
* otherwise it will be the first cycle after calling jack_activate().
* After that, it runs according to the ::JackSyncCallback rules.
* Clients that don't set a @a sync_callback are assumed to be ready
* immediately any time the transport wants to start.
*
* @param client the JACK client structure.
* @param sync_callback is a realtime function that returns TRUE when
* the client is ready. Setting @a sync_callback to NULL declares that
* this client no longer requires slow-sync processing.
* @param arg an argument for the @a sync_callback function.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_set_sync_callback (jack_client_t *client,
JackSyncCallback sync_callback,
void *arg) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Set the timeout value for slow-sync clients.
*
* This timeout prevents unresponsive slow-sync clients from
* completely halting the transport mechanism. The default is two
* seconds. When the timeout expires, the transport starts rolling,
* even if some slow-sync clients are still unready. The @a
* sync_callbacks of these clients continue being invoked, giving them
* a chance to catch up.
*
* @see jack_set_sync_callback
*
* @param client the JACK client structure.
* @param timeout is delay (in microseconds) before the timeout expires.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_set_sync_timeout (jack_client_t *client,
jack_time_t timeout) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Register as timebase master for the JACK subsystem.
*
* The timebase master registers a callback that updates extended
* position information such as beats or timecode whenever necessary.
* Without this extended information, there is no need for this
* function.
*
* There is never more than one master at a time. When a new client
* takes over, the former @a timebase_callback is no longer called.
* Taking over the timebase may be done conditionally, so it fails if
* there was a master already.
*
* @param client the JACK client structure.
* @param conditional non-zero for a conditional request.
* @param timebase_callback is a realtime function that returns
* position information.
* @param arg an argument for the @a timebase_callback function.
*
* @return
* - 0 on success;
* - EBUSY if a conditional request fails because there was already a
* timebase master;
* - other non-zero error code.
*/
int jack_set_timebase_callback (jack_client_t *client,
int conditional,
JackTimebaseCallback timebase_callback,
void *arg) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Reposition the transport to a new frame number.
*
* May be called at any time by any client. The new position takes
* effect in two process cycles. If there are slow-sync clients and
* the transport is already rolling, it will enter the
* ::JackTransportStarting state and begin invoking their @a
* sync_callbacks until ready. This function is realtime-safe.
*
* @see jack_transport_reposition, jack_set_sync_callback
*
* @param client the JACK client structure.
* @param frame frame number of new transport position.
*
* @return 0 if valid request, non-zero otherwise.
*/
int jack_transport_locate (jack_client_t *client,
jack_nframes_t frame) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Query the current transport state and position.
*
* This function is realtime-safe, and can be called from any thread.
* If called from the process thread, @a pos corresponds to the first
* frame of the current cycle and the state returned is valid for the
* entire cycle.
*
* @param client the JACK client structure.
* @param pos pointer to structure for returning current transport
* position; @a pos->valid will show which fields contain valid data.
* If @a pos is NULL, do not return position information.
*
* @return Current transport state.
*/
jack_transport_state_t jack_transport_query (const jack_client_t *client,
jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Return an estimate of the current transport frame,
* including any time elapsed since the last transport
* positional update.
*
* @param client the JACK client structure
*/
jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Request a new transport position.
*
* May be called at any time by any client. The new position takes
* effect in two process cycles. If there are slow-sync clients and
* the transport is already rolling, it will enter the
* ::JackTransportStarting state and begin invoking their @a
* sync_callbacks until ready. This function is realtime-safe.
*
* @see jack_transport_locate, jack_set_sync_callback
*
* @param client the JACK client structure.
* @param pos requested new transport position.
*
* @return 0 if valid request, EINVAL if position structure rejected.
*/
int jack_transport_reposition (jack_client_t *client,
const jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Start the JACK transport rolling.
*
* Any client can make this request at any time. It takes effect no
* sooner than the next process cycle, perhaps later if there are
* slow-sync clients. This function is realtime-safe.
*
* @see jack_set_sync_callback
*
* @param client the JACK client structure.
*/
void jack_transport_start (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Stop the JACK transport.
*
* Any client can make this request at any time. It takes effect on
* the next process cycle. This function is realtime-safe.
*
* @param client the JACK client structure.
*/
void jack_transport_stop (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Gets the current transport info structure (deprecated).
*
* @param client the JACK client structure.
* @param tinfo current transport info structure. The "valid" field
* describes which fields contain valid data.
*
* @deprecated This is for compatibility with the earlier transport
* interface. Use jack_transport_query(), instead.
*
* @pre Must be called from the process thread.
*/
void jack_get_transport_info (jack_client_t *client,
jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT;
/**
* Set the transport info structure (deprecated).
*
* @deprecated This function still exists for compatibility with the
* earlier transport interface, but it does nothing. Instead, define
* a ::JackTimebaseCallback.
*/
void jack_set_transport_info (jack_client_t *client,
jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT;
/*@}*/
#ifdef __cplusplus
}
#endif
#endif /* __jack_transport_h__ */

Some files were not shown because too many files have changed in this diff Show More