Commit 89549247 authored by Raghavendra Rao's avatar Raghavendra Rao Committed by George Kiagiadakis
Browse files

docs: Replace hotdoc with Doxygen & Sphinx to generate documentation

parent 3e6edcb0
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'WirePlumber'
copyright = '2020, Collabora'
author = 'Collabora'
# The full version, including alpha/beta/rc tags
release = '2020'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'breathe',
]
breathe_projects = { "WirePlumber": "@BUILD_ROOT@/xml" }
breathe_default_members = ('members', 'undoc-members')
breathe_default_project = "WirePlumber"
# Exclude '.c' extension from breathe_implementation_filename_extensions so that the documented code in C files is parsed and the ReStructured Text files are built from xml files
breathe_implementation_filename_extensions = ['.cc', '.cpp']
# Let breathe know that our domain is 'C'
breathe_domain_by_extension = {
"h" : "c",
"c" : "c",
}
# See, https://breathe.readthedocs.io/en/latest/directives.html#config-values for more information
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if not on_rtd: # only import and set the theme if we're building docs locally
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Tell sphinx what the primary language being documented is.
primary_domain = 'cpp'
# Testing
## Automated unit tests
WirePlumber has automated tests that you can easily run with:
```
$ meson test -C build
```
This will automatically compile all test dependencies, so you can be sure
that this always tests your latest changes.
If you wish to run a specific test instead of all of them, you can run:
```
$ meson test -C build test-name
```
When debugging a single test, you can additionally enable verbose test output
by appending `-v` and you can also run the test in gdb by appending `--gdb`.
For more information on how to use `meson test`, please refer to
[meson's manual](https://mesonbuild.com/Unit-tests.html)
> When submitting changes for review, always ensure that all tests pass
Please note that many WirePlumber tests require specific SPA test plugins
to be available in your PipeWire installation. More specifically, PipeWire
needs to be configured with the following options enabled:
```
-Dvideotestsrc=true -Daudiotestsrc=true -Dtest=true
```
If these SPA plugins are not found in the system, some tests will fail.
This is expected.
## WirePlumber examples
WirePlumber ships examples in `test/examples`.
Execute them from the top-level directory like this:
```
$ WIREPLUMBER_MODULE_DIR=build/modules ./build/tests/examples/audiotestsrc-play
```
Assuming there is no other process actively using `hw:0,0` from alsa, the above
example should play a test tone on `hw:0,0` without errors.
## Native API clients
### pw-cat
Using the default endpoint:
```
$ wpctl status # verify the default endpoints
$ pw-record test.wav
$ pw-play test.wav
```
Using a non-default endpoint:
```
$ pw-record --list-targets # find the node id
$ pw-record --target <node_id> test.wav
$ pw-play --list-targets # find the node id
$ pw-play --target <node_id> test.wav
```
or
```
$ wpctl status # find the capture & playback endpoint ids
$ pw-record --target <endpoint_id> test.wav
$ pw-play --target <endpoint_id> test.wav
```
> Note: node ids and endpoint ids can be used interchangeably when specifying
targets in all use cases.
### video-play
Using the default endpoint:
```
$ cd path/to/pipewire-source-dir
$ ./build/src/examples/video-play
```
Using a non-default endpoint:
```
$ wpctl status # find the endpoint id from the list
$ cd path/to/pipewire-source-dir
$ ./build/src/examples/video-play <endpoint_id>
```
> Tip: enable videotestsrc in wireplumber's configuration to have more video
sources available (see `videotestsrc.node.disabled` in the configuration directory)
## PulseAudio compat API clients
### pacat
Using the default endpoint:
```
$ wpctl status # verify the default endpoints
$ pw-pulse parecord test.wav
$ pw-pulse paplay test.wav
```
Using a non-default endpoint:
```
$ wpctl status # find the capture & playback endpoint ids
$ PIPEWIRE_NODE=<endpoint_id> pw-pulse parecord test.wav
$ PIPEWIRE_NODE=<endpoint_id> pw-pulse paplay test.wav
```
### pavucontrol
```
$ pw-pulse pavucontrol
```
* Volume level meters should work
* Changing the volume should work
## ALSA compat API clients
### aplay / arecord
> Note: unless you have installed PipeWire in the default system prefix
(`/usr`), the ALSA compat API will not work, unless you copy
`libasound_module_pcm_pipewire.so` in the alsa plugins directory
(usually `/usr/<libdir>/alsa-lib/`) and that you add the contents of
`pipewire-alsa/conf/50-pipewire.conf` in your `~/.asoundrc`
(or anywhere else, system-wide, where libasound can read it)
Using the default endpoint:
```
$ wpctl status # verify the default endpoints
$ arecord -D pipewire -f S16_LE -r 48000 test.wav
$ aplay -D pipewire test.wav
```
Using a non-default endpoint:
```
$ wpctl status # find the capture & playback endpoint ids
$ PIPEWIRE_NODE=<endpoint_id> arecord -D pipewire -f S16_LE -r 48000 test.wav
$ PIPEWIRE_NODE=<endpoint_id> aplay -D pipewire test.wav
```
or
```
$ wpctl status # find the capture & playback endpoint ids
$ arecord -D pipewire:NODE=<endpoint_id> -f S16_LE -r 48000 test.wav
$ aplay -D pipewire:NODE=<endpoint_id> test.wav
```
## JACK compat API clients
### qjackctl
```
pw-jack qjackctl
```
* This should correctly connect.
* The "Graph" window should show the PipeWire graph.
### jack_simple_client
```
$ wpctl status # find the target endpoint id
$ wpctl inspect <endpoint_id> # find the node.id
$ PIPEWIRE_NODE=<node_id> pw-jack jack_simple_client
```
> The JACK layer is not controlled by the session manager, it creates its own
links; which is why it is required to specify a node id (endpoint id will not
work)
## Device Reservation
### with PulseAudio
1. With PulseAudio running, start a pulseaudio client:
```
gst-launch-1.0 audiotestsrc ! pulsesink
```
2. Start PipeWire & WirePlumber
- The device in use by PA will not be available in PW
3. Stop the PA client
- A few seconds later, WirePlumber should assume control of the device
4. `wpctl status` should be able to confirm that the device is available
5. Start a PA client again
- It should not be able to play; it will just freeze
6. Stop WirePlumber
- The PA client should immediately start playing
### with JACK
1. Start PipeWire & WirePlumber
- All devices should be available
2. Start `jackdbus`
- through `qjackctl`:
- Enable `Setup` -> `Misc` -> `Enable JACK D-Bus interface`
- Click `Start` on the main window
- or manually:
- Run `jackdbus auto`
- Run `qdbus org.jackaudio.service /org/jackaudio/Controller org.jackaudio.JackControl.StartServer`
3. Wait a few seconds and run `wpctl status` to inspect
- The devices taken by JACK should no longer be available
- There should be two `JACK System` endpoints (sink & source)
4. Run an audio client on PipeWire (ex `pw-play test.wav`)
- Notice how audio now goes through JACK
5. Stop JACK
- through `qjackctl`, click `Stop`
- or manually: `qdbus org.jackaudio.service /org/jackaudio/Controller org.jackaudio.JackControl.StopServer`
6. Wait a few seconds and run `wpctl status` to inspect
- The devices that were release by JACK should again be available
- There should be no `JACK System` endpoint
> You may also start WirePlumber *after* starting JACK. It should immediately
go to the state described in step 3
# Running the WirePlumber Daemon
## Configure PipeWire
PipeWire 0.3 comes with an example session manager that you will need
to disable and replace with WirePlumber. This can be achieved by editing
`src/daemon/pipewire.conf.in` in the PipeWire git tree or
`/etc/pipewire/pipewire.conf` in an existing installation:
```
diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in
index cebded96..dee1743b 100644
--- a/src/daemon/pipewire.conf.in
+++ b/src/daemon/pipewire.conf.in
@@ -99,7 +99,8 @@ exec = {
# Start the session manager. Run the session manager with -h for
# options.
#
- "@media_session_path@" = { args = ""}
+ #"@media_session_path@" = { args = ""}
+ "wireplumber" = {}
#
# You can optionally start the pulseaudio-server here as well
# but it better to start it as a systemd service.
```
This setup assumes that WirePlumber is *installed* on the target system.
## Run independently or without installing
If you wish to debug WirePlumber, it may be useful to run it separately from
PipeWire or run it directly from the source tree without installing.
To do so:
1. Comment out with `#` the `"wireplumber" = {}` line from `pipewire.conf`
2. Run pipewire:
- if it is installed, execute `pipewire`
- if it is **not** installed, execute `make run` in the **pipewire** source tree
3. Without stopping pipewire, run wireplumber:
- if it is installed, execute `wireplumber`
- if it is **not** installed, execute `make run` in the **wireplumber** source tree
This diff is collapsed.
# WirePlumber
WirePlumber is a modular session / policy manager for
[PipeWire](https://pipewire.org) and a GObject-based high-level library
that wraps PipeWire's API, providing convenience for writing the daemon's
modules as well as external tools for managing PipeWire.
* [Installing WirePlumber](installation/from-source.md)
## The WirePlumber Daemon
The WirePlumber daemon implements the session & policy management service.
It follows a modular design, having plugins that implement the actual
management functionality.
* [Running the WirePlumber Daemon](daemon/running.md)
* [Daemon Configuration](daemon/configuration.md)
* [Debug Logging](daemon/log.md)
## The WirePlumber Library
The WirePlumber Library provides API that allows you
to extend the WirePlumber daemon, to write management or status tools
for PipeWire (apps that don't do actual media streaming)
and to write custom session managers for embedded devices.
* [API Reference](gi-index)
## Resources
* [Contribute to WirePlumber](contributing/contributing.md)
* [Reach out to the community](community.md)
Wireplumber
===========
WirePlumber is a modular session / policy manager for `PipeWire <https://pipewire.org>`_ and a GObject-based high-level library that wraps PipeWire's API, providing convenience for writing the daemon's modules as well as external tools for managing PipeWire.
.. toctree::
:maxdepth: 1
:caption: Contents:
toc/installing-wireplumber.rst
toc/running-wireplumber-daemon.rst
toc/daemon-configuration.rst
toc/daemon-logging.rst
toc/contributing.rst
toc/community.rst
toc/testing.rst
api/library_root.rst
The WirePlumber Daemon
----------------------
The WirePlumber daemon implements the session & policy management service. It follows a modular design, having plugins that implement the actual management functionality.
* :ref:`running-wireplumber-daemon`
* :ref:`daemon-configuration`
* :ref:`logging`
The WirePlumber library
-----------------------
The WirePlumber Library provides API that allows you to extend the WirePlumber daemon, to write management or status tools for PipeWire (apps that don't do actual media streaming) and to write custom session managers for embedded devices.
Resources
---------
* :ref:`contributing`
* :ref:`testing`
* :ref:`community`
Subpages
--------
* :ref:`installing-wireplumber`
* :ref:`running-wireplumber-daemon`
* :ref:`daemon-configuration`
* :ref:`contributing`
* :ref:`testing`
* :ref:`community`
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
\ No newline at end of file
# Installing WirePlumber
## Dependencies
In order to compile WirePlumber you will need:
* GLib >= 2.58
* PipeWire 0.3 (>= 0.3.5 highly recommended)
For building gobject-introspection data, you will also need `g-ir-scanner`,
which is usually shipped together with the gobject-introspection development
files.
For building documentation, you will also need [hotdoc](https://hotdoc.github.io/).
Most distributions do not ship this, but you can install it easily using python's
`pip` package manager.
## Compilation
WirePlumber uses the [meson build system](https://mesonbuild.com/).
To configure the project, you need to first run `meson`.
The basic syntax is shown below:
```
meson [build directory] [source directory] [--prefix=/path] [...options...]
```
Assuming you want to build in a directory called `build` inside the source
tree, you can run:
```
$ meson build . --prefix=/usr
$ ninja -C build
```
### Additional options
- `-Dintrospection=[enabled|disabled|auto]`: Force enable or force disable
building gobject-introspection data. The default value is `auto`, which means
that g-i will be built if `g-ir-scanner` is found and skipped otherwise.
- `-Ddocs=[enabled|disabled|auto]`: Force enable or force disable building
documentation. The default value is `auto`, which means that documentation
will be built if `hotdoc` is found and skipped otherwise. Note that building
the documentation also requires gobject-introspection data to be built.
## Installation
To install, simply run the `install` target with ninja:
```
$ ninja -C build install
```
To revert the installation, there is also an `uninstall` target:
```
$ ninja -C build uninstall
```
hotdoc_p = find_program('hotdoc', required: get_option('doc'))
if not hotdoc_p.found()
message('Hotdoc not found, not building the documentation')
sphinx_p = find_program('sphinx-build', required: get_option('doc'))
if not sphinx_p.found()
message('Sphinx not found, not building the documentation')
subdir_done()
endif
hotdoc = import('hotdoc')
required_hotdoc_extensions = ['gi-extension']
foreach extension: required_hotdoc_extensions
if not hotdoc.has_extensions(extension)
if get_option('doc').enabled()
error('Documentation enabled but @0@ missing'.format(extension))
endif
doxygen_p = find_program('doxygen', required: get_option('doc'))
if not doxygen_p.found()
message('Doxygen not found, not building the documentation')
subdir_done()
endif
message('@0@ extension not found, not building documentation'.format(extension))
subdir_done()
endif
endforeach
breathe_p = find_program('breathe-apidoc', required: get_option('doc'))
if not breathe_p.found()
message('Breathe not found, not building the documentation')
subdir_done()
endif
sphinx_c = run_command(sphinx_p, '--version')
breathe_c = run_command(breathe_p, '--version')
doxygen_c = run_command(doxygen_p, '--version')
sphinx_v = sphinx_c.stdout().split(' ')[1].strip()
breathe_v = breathe_c.stdout().split(' ')[2].strip()
doxygen_v = doxygen_c.stdout().strip()
if sphinx_v.version_compare('< 2.1.0')
error('Use at least sphinx version 2.1.0, found ' + sphinx_v)
endif
if breathe_v.version_compare('< 4.11')
error('Use at least breathe version 4.11, found ' + breathe_v)
endif
if doxygen_v.version_compare('< 1.8')
error('Use at least doxygen version 1.8, found ' + doxygen_v)
endif
if not build_gir
if get_option('doc').enabled()
error('Documentation enabled but introspection not built.')
endif
message('Introspection not built, can\'t build the documentation')
subdir_done()
# message('Introspection not built, can\'t build the documentation')
# subdir_done()
endif
wp_doc = hotdoc.generate_doc('wireplumber',
project_version: wireplumber_api_version,
sitemap: 'sitemap.txt',
index: 'index.md',
gi_index: 'api-reference.md',
gi_smart_index: true,
gi_sources: [wp_gir[0].full_path()],
gi_c_sources: [wp_lib_sources, wp_lib_headers, wpenums_c, wpenums_h],
gi_c_source_roots: [join_paths(meson.current_source_dir(), '../lib/wp/'), ],
languages: ['c'],
dependencies: [wp_dep],
build_by_default: true,
install: true,
doxygen_database = meson.current_build_dir() + '/doxygen_doc'
# modify the sphinx configuration and the breathe doxygen XML database
# to point where its being generated by doxygen
sphinx_conf_data = configuration_data()
sphinx_conf_data.set('BUILD_ROOT', doxygen_database)
sphinx_conf_data.set('VERSION', meson.project_version())
sphinx_conf = configure_file(
input: 'conf.py.in',
output: 'conf.py',
configuration: sphinx_conf_data
)
doxy_conf_data = configuration_data()
doxy_conf_data.set('SRC_ROOT', meson.source_root())
doxy_conf_data.set('OUTPUT_DIR', doxygen_database)
doxygen_conf_wireplumber = configure_file(
input: 'doxyfile.in',
output: 'doxyfile',
configuration: doxy_conf_data
)
dir_prefix = get_option('prefix')
dir_data = join_paths(dir_prefix, get_option('datadir'))
script_data = configuration_data()
script_data.set('SRCDIR', meson.current_build_dir())
script_data.set('OUTDIR', meson.current_build_dir() + '/docs')
script_data.set('DOXYGEN_CONF', meson.current_build_dir() + '/doxyfile')
# Set a different directory for doctrees to avoid installing them
script_data.set('DOCTREES_DIR', meson.current_build_dir() + '/doctrees')
script_data.set('DOXYGEN_CMD', doxygen_p.path())
script_data.set('SPHINX_CMD', sphinx_p.path())
script_doxy_sphinx = configure_file(
input: 'run_doxygen_sphinx.sh.in',
output: 'run_doxygen_sphinx.sh',
configuration: script_data
)
# copy everything to build_dir, if you plan on adding other files in the top
# rootdir of sourcedir, please add them here as well, otherwise use 'toc/'s
# meson.build file
sphinx_files = ['index.rst']
foreach file : sphinx_files
configure_file(input: file, output: file, copy: true)
endforeach
# and those in toc
subdir('toc')
sphinx_doc = custom_target(
'breathe',
command: script_doxy_sphinx,
output: 'docs',
build_by_default: true,
)
# we need this because we will have a stale 'docs' directory
# and this forces it to be rebuilt
docs = run_target(
'docs',
command: script_doxy_sphinx,
)
install_subdir(
sphinx_doc.full_path(),
install_dir: join_paths(dir_data, 'doc', 'wireplumber', 'html'),
exclude_files: '.buildinfo',
strip_directory: true,
)
bs4 # BeautifulSoup!
lxml # We need the lxml backend for BeautifulSoup.
sphinx>=1.6.1 # 1.6 introduces logging API.
breathe # The directives used for documentation come from the excellent Breathe.
six # Primarily for Unicode string types
#!/bin/sh
@DOXYGEN_CMD@ @DOXYGEN_CONF@ && @SPHINX_CMD@ -E -Q -j auto -d @DOCTREES_DIR@ @SRCDIR@ @OUTDIR@