doc: reorganize files

Separate various autogen files from the documentation .dox files.
Rename .dox files to match the intended tree structure.
This commit is contained in:
Pauli Virtanen 2023-11-18 15:13:05 +02:00 committed by Wim Taymans
parent eca773fc12
commit 77fad4ee13
41 changed files with 60 additions and 59 deletions

View file

@ -0,0 +1,21 @@
/** \page page_tutorial Tutorial
Welcome to the PipeWire API tutorial. The goal is to learn
PipeWire API step-by-step with simple short examples.
- \subpage page_tutorial1
- \subpage page_tutorial2
- \subpage page_tutorial3
- \subpage page_tutorial4
- \subpage page_tutorial5
- \subpage page_tutorial6
# More Example Programs
- \ref audio-src.c "": \snippet{doc} audio-src.c title
- \ref audio-dsp-filter.c "": \snippet{doc} audio-dsp-filter.c title
- \ref video-play.c "": \snippet{doc} video-play.c title
- \subpage page_examples
*/

View file

@ -0,0 +1,47 @@
/** \page page_tutorial1 Tutorial - Part 1: Getting Started
\ref page_tutorial "Index" | \ref page_tutorial2
In this tutorial we show the basics of a simple PipeWire application.
Use this tutorial to get started and help you set up your development
environment.
# Initialization
Let get started with the simplest application.
\snippet tutorial1.c code
Before you can use any PipeWire functions, you need to call `pw_init()`.
# Compilation
PipeWire provides a pkg-config file named `libpipewire-0.3` (note: the version
suffix may change with future releases of PipeWire).
To compile the simple test application, copy it into a test1.c file and
use pkg-config to provide the required dependencies:
gcc -Wall test1.c -o test1 $(pkg-config --cflags --libs libpipewire-0.3)
then run it with:
# ./test1
Compiled with libpipewire 0.3.5
Linked with libpipewire 0.3.5
#
Use your build system's pkg-config support to integrate it into your project.
For example, a minimal [meson.build](https://mesonbuild.com/) entry would look
like this:
project('test1', ['c'])
pipewire_dep = dependency('libpipewire-0.3')
executable('test1', 'test1.c',
dependencies: [pipewire_dep])
\ref page_tutorial "Index" | \ref page_tutorial2
*/

View file

@ -0,0 +1,129 @@
/** \page page_tutorial2 Tutorial - Part 2: Enumerating Objects
\ref page_tutorial1 | \ref page_tutorial "Index" | \ref page_tutorial3
In this tutorial we show how to connect to a PipeWire daemon and
enumerate the objects that it has.
Let take a look at the following application to start.
\snippet tutorial2.c code
To compile the simple test application, copy it into a tutorial2.c file and
use:
gcc -Wall tutorial2.c -o tutorial2 $(pkg-config --cflags --libs libpipewire-0.3)
Let's break this down:
First we need to initialize the PipeWire library with `pw_init()` as we
saw in the previous tutorial. This will load and configure the right
modules and setup logging and other tasks.
\code{.c}
...
pw_init(&argc, &argv);
...
\endcode
Next we need to create one of the `struct pw_loop` wrappers. PipeWire
ships with 2 types of mainloop implementations. We will use the
`struct pw_main_loop` implementation, we will see later how we can
use the `struct pw_thread_loop` implementation as well.
The mainloop is an abstraction of a big poll loop, waiting for events
to occur and things to do. Most of the PipeWire work will actually
be performed in the context of this loop and so we need to make one
first.
We then need to make a new context object with the loop. This context
object will manage the resources for us and will make it possible for
us to connect to a PipeWire daemon:
\code{.c}
struct pw_main_loop *loop;
struct pw_context *context;
loop = pw_main_loop_new(NULL /* properties */);
context = pw_context_new(pw_main_loop_get_loop(loop),
NULL /* properties */,
0 /* user_data size */);
\endcode
It is possible to give extra properties when making the mainloop or
context to tweak its features and functionality. It is also possible
to add extra data to the allocated objects for your user data. It will
stay alive for as long as the object is alive. We will use this
feature later.
A real implementation would also need to check if the allocation
succeeded and do some error handling, but we leave that out to make
the code easier to read.
With the context we can now connect to the PipeWire daemon:
\code{.c}
struct pw_core *core;
core = pw_context_connect(context,
NULL /* properties */,
0 /* user_data size */);
\endcode
This creates a socket between the client and the server and makes
a proxy object (with ID 0) for the core. Don't forget to check the
result here, a NULL value means that the connection failed.
At this point we can send messages to the server and receive events.
For now we're not going to handle events on this core proxy but
we're going to handle them on the registry object.
\code{.c}
struct pw_registry *registry;
struct spa_hook registry_listener;
registry = pw_core_get_registry(core, PW_VERSION_REGISTRY,
0 /* user_data size */);
spa_zero(registry_listener);
pw_registry_add_listener(registry, &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
*/

View file

@ -0,0 +1,119 @@
/** \page page_tutorial3 Tutorial - Part 3: Forcing A Roundtrip
\ref page_tutorial2 | \ref page_tutorial "Index" | \ref page_tutorial4
In this tutorial we show how to force a roundtrip to the server
to make sure an action completed.
We'll change our example from \ref page_tutorial2 "Tutorial 2" slightly
and add the extra code to implement the roundtrip.
Let's take the following small method first:
\snippet tutorial3.c roundtrip
Let's take a look at what this method does.
\code{.c}
struct spa_hook core_listener;
pw_core_add_listener(core, &core_listener, &core_events, &d);
\endcode
First of all we add a listener for the events of the core
object. We are only interested in the `done` event in this
tutorial. This is the event handler:
\code{.c}
static void on_core_done(void *data, uint32_t id, int seq)
{
struct roundtrip_data *d = data;
if (id == PW_ID_CORE && seq == d->pending)
pw_main_loop_quit(d->loop);
}
\endcode
When the done event is received for an object with id `PW_ID_CORE` and
a certain sequence number `seq`, this function will call `pw_main_loop_quit()`.
Next we do:
\code{.c}
d.pending = pw_core_sync(core, PW_ID_CORE, 0);
\endcode
This triggers the `sync` method on the core object with id
`PW_ID_CORE` and sequence number 0.
Because this is a method on a proxy object, it will be executed
asynchronously and the return value will reflect this. PipeWire
uses the return values of the underlying SPA (Simple Plugin API)
helper objects (See also \ref page_spa_design ).
Because all messages on the PipeWire server are handled sequentially,
the sync method will be executed after all previous methods are
completed. The PipeWire server will emit a `done` event with the
same ID and the return value of the original `pw_core_sync()`
method in the sequence number.
We then run the mainloop to send the messages to the server and
receive the events:
\code{.c}
pw_main_loop_run(loop);
\endcode
When we get the done event, we can compare it to the sync method
and then we know that we did a complete roundtrip and there are no
more pending methods on the server. We can quit the mainloop and
remove the listener:
\code{.c}
spa_hook_remove(&core_listener);
\endcode
If we add this roundtrip method to our code and call it instead of the
`pw_main_loop_run()` we will exit the program after all previous methods
are finished. This means that the `pw_core_get_registry()` call
completed and thus that we also received all events for the globals
on the server.
\snippet tutorial3.c code
To compile the simple test application, copy it into a tutorial3.c file and
use:
gcc -Wall tutorial3.c -o tutorial3 $(pkg-config --cflags --libs libpipewire-0.3)
Now that our program completes, we can take a look at how we can destroy
the objects we created. Let's destroy each of them in reverse order that we
created them:
\code{.c}
pw_proxy_destroy((struct pw_proxy*)registry);
\endcode
The registry is a proxy and can be destroyed with the generic proxy destroy
method. After destroying the object, you should not use it anymore. It is
an error to destroy an object more than once.
We can disconnect from the server with:
\code{.c}
pw_core_disconnect(core);
\endcode
This will also destroy the core proxy object and will remove the proxies
that might have been created on this connection.
We can finally destroy our context and mainloop to conclude this tutorial:
\code{.c}
pw_context_destroy(context);
pw_main_loop_destroy(loop);
\endcode
\ref page_tutorial2 | \ref page_tutorial "Index" | \ref page_tutorial4
*/

View file

@ -0,0 +1,158 @@
/** \page page_tutorial4 Tutorial - Part 4: Playing A Tone
\ref page_tutorial3 | \ref page_tutorial "Index" | \ref page_tutorial5
In this tutorial we show how to use a stream to play a tone.
Let's take a look at the code before we break it down:
\snippet tutorial4.c code
Save as tutorial4.c and compile with:
gcc -Wall tutorial4.c -o tutorial4 -lm $(pkg-config --cflags --libs libpipewire-0.3)
We start with the usual boilerplate, `pw_init()` and a `pw_main_loop_new()`.
We're going to store our objects in a structure so that we can pass them
around in callbacks later.
\code{.c}
struct data {
struct pw_main_loop *loop;
struct pw_stream *stream;
double accumulator;
};
int main(int argc, char *argv[])
{
struct data data = { 0, };
pw_init(&argc, &argv);
data.loop = pw_main_loop_new(NULL);
\endcode
Next we create a stream object. It takes the mainloop as first argument and
a stream name as the second. Next we provide some properties for the stream
and a callback + data.
\code{.c}
data.stream = pw_stream_new_simple(
pw_main_loop_get_loop(data.loop),
"audio-src",
pw_properties_new(
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE, "Music",
NULL),
&stream_events,
&data);
\endcode
We are using `pw_stream_new_simple()` but there is also a `pw_stream_new()` that
takes an existing `struct pw_core` as the first argument and that requires you
to add the event handle manually, for more control. The `pw_stream_new_simple()`
is, as the name implies, easier to use because it creates a `struct pw_context`
and `struct pw_core` automatically.
In the properties we need to give as much information about the stream as we
can so that the session manager can make good decisions about how and where
to route this stream. There are three important properties to configure:
- `PW_KEY_MEDIA_TYPE`: The media type; like Audio, Video, MIDI.
- `PW_KEY_MEDIA_CATEGORY`: The category; like Playback, Capture, Duplex, Monitor.
- `PW_KEY_MEDIA_ROLE`: The media role; like Movie, Music, Camera, Screen,
Communication, Game, Notification, DSP, Production, Accessibility, Test.
The properties are owned by the stream and freed when the stream is destroyed
later.
This is the event structure that we use to listen for events:
\code{.c}
static const struct pw_stream_events stream_events = {
PW_VERSION_STREAM_EVENTS,
.process = on_process,
};
\endcode
We are for the moment only interested now in the `process` event. This event
is called whenever we need to produce more data. We'll see how that function
is implemented but first we need to setup the format of the stream:
\code{.c}
const struct spa_pod *params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
#define DEFAULT_RATE 44100
#define DEFAULT_CHANNELS 2
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
&SPA_AUDIO_INFO_RAW_INIT(
.format = SPA_AUDIO_FORMAT_S16,
.channels = DEFAULT_CHANNELS,
.rate = DEFAULT_RATE ));
\endcode
This is using a `struct spa_pod_builder` to make a `struct spa_pod *` object
in the buffer array on the stack. The parameter is of type `SPA_PARAM_EnumFormat`
which means that it enumerates the possible formats for this stream. We have
only one, a Signed 16 bit stereo format at 44.1KHz.
We use `spa_format_audio_raw_build()` which is a helper function to make the param
with the builder. See \ref page_spa_pod for more information about how to
make these POD objects.
Now we're ready to connect the stream and run the main loop:
\code{.c}
pw_stream_connect(data.stream,
PW_DIRECTION_OUTPUT,
PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
params, 1);
pw_main_loop_run(data.loop);
\endcode
To connect we specify that we have a `PW_DIRECTION_OUTPUT` stream. The third argument
is always `PW_ID_ANY`. Next we set some flags:
- `PW_STREAM_FLAG_AUTOCONNECT`: Automatically connect this stream. This instructs
the session manager to link us to some consumer.
- `PW_STREAM_FLAG_MAP_BUFFERS`: mmap the buffers for us so we can access the
memory. If you don't set these flags you have either work with the fd or mmap
yourself.
- `PW_STREAM_FLAG_RT_PROCESS`: Run the process function in the realtime thread.
Only use this if the process function only uses functions that are realtime
safe, this means no allocation or file access or any locking.
And last we pass the extra parameters for our stream. Here we only have the
allowed formats (`SPA_PARAM_EnumFormat`).
Running the mainloop will then start processing and will result in our
`process` callback to be called. Let's have a look at that function now.
The main program flow of the process function is:
- `pw_stream_dequeue_buffer()` to obtain a buffer to write into.
- Get pointers in buffer memory to write to.
- Write data into buffer.
- Adjust buffer with number of written bytes, offset, stride.
- `pw_stream_queue_buffer()` to queue the buffer for playback.
\snippet tutorial4.c on_process
Check out the docs for \ref page_spa_buffer for more information
about how to work with buffers.
Try to change the number of channels, samplerate or format; the stream
will automatically convert to the format on the server.
\ref page_tutorial3 | \ref page_tutorial "Index" | \ref page_tutorial5
*/

View file

@ -0,0 +1,223 @@
/** \page page_tutorial5 Tutorial - Part 5: Capturing Video Frames
\ref page_tutorial4 | \ref page_tutorial "Index" | \ref page_tutorial6
In this tutorial we show how to use a stream to capture a
stream of video frames.
Even though we are now working with a different media type and
we are capturing instead of playback, you will see that this
example is very similar to \ref page_tutorial4.
Let's take a look at the code before we break it down:
\snippet tutorial5.c code
Save as tutorial5.c and compile with:
gcc -Wall tutorial5.c -o tutorial5 -lm $(pkg-config --cflags --libs libpipewire-0.3)
Most of the application is structured like \ref page_tutorial4.
We create a stream object with different properties to make it a Camera
Video Capture stream.
\code{.c}
props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE, "Camera",
NULL);
if (argc > 1)
pw_properties_set(props, PW_KEY_TARGET_OBJECT, argv[1]);
data.stream = pw_stream_new_simple(
pw_main_loop_get_loop(data.loop),
"video-capture",
props,
&stream_events,
&data);
\endcode
We also optionally allow the user to pass the name of the target node where the session
manager is supposed to connect the node. The user may also give the value of the
unique target node serial (`PW_KEY_OBJECT_SERIAL`) as the value.
In addition to the `process` event, we are also going to listen to a new event,
`param_changed`:
\code{.c}
static const struct pw_stream_events stream_events = {
PW_VERSION_STREAM_EVENTS,
.param_changed = on_param_changed,
.process = on_process,
};
\endcode
Because we capture a stream of a wide range of different
video formats and resolutions, we have to describe our accepted formats in
a different way:
\code{.c}
const struct spa_pod *params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
params[0] = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(7,
SPA_VIDEO_FORMAT_RGB,
SPA_VIDEO_FORMAT_RGB,
SPA_VIDEO_FORMAT_RGBA,
SPA_VIDEO_FORMAT_RGBx,
SPA_VIDEO_FORMAT_BGRx,
SPA_VIDEO_FORMAT_YUY2,
SPA_VIDEO_FORMAT_I420),
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(
&SPA_RECTANGLE(320, 240),
&SPA_RECTANGLE(1, 1),
&SPA_RECTANGLE(4096, 4096)),
SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction(
&SPA_FRACTION(25, 1),
&SPA_FRACTION(0, 1),
&SPA_FRACTION(1000, 1)));
\endcode
This is using a `struct spa_pod_builder` to make a `struct spa_pod *` object
in the buffer array on the stack. The parameter is of type `SPA_PARAM_EnumFormat`
which means that it enumerates the possible formats for this stream.
In this example we use the builder to create some `CHOICE` entries for
the format properties.
We have an enumeration of formats, we need to first give the amount of enumerations
that follow, then the default (preferred) value, followed by alternatives in order
of preference:
\code{.c}
SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(7,
SPA_VIDEO_FORMAT_RGB, /* default */
SPA_VIDEO_FORMAT_RGB, /* alternative 1 */
SPA_VIDEO_FORMAT_RGBA, /* alternative 2 */
SPA_VIDEO_FORMAT_RGBx, /* .. etc.. */
SPA_VIDEO_FORMAT_BGRx,
SPA_VIDEO_FORMAT_YUY2,
SPA_VIDEO_FORMAT_I420),
\endcode
We also have a `RANGE` of values for the size. We need to give a default (preferred)
size and then a min and max value:
\code{.c}
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(
&SPA_RECTANGLE(320, 240), /* default */
&SPA_RECTANGLE(1, 1), /* min */
&SPA_RECTANGLE(4096, 4096)), /* max */
\endcode
We have something similar for the framerate.
Note that there are other video parameters that we don't specify here. This
means that we don't have any restrictions for their values.
See \ref page_spa_pod for more information about how to make these
POD objects.
Now we're ready to connect the stream and run the main loop:
\code{.c}
pw_stream_connect(data.stream,
PW_DIRECTION_INPUT,
PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS,
params, 1);
pw_main_loop_run(data.loop);
\endcode
To connect we specify that we have a `PW_DIRECTION_INPUT` stream. The third
argument is always `PW_ID_ANY`.
We're setting the `PW_STREAM_FLAG_AUTOCONNECT` flag to make an automatic
connection to a suitable camera and `PW_STREAM_FLAG_MAP_BUFFERS` to let the
stream mmap the data for us.
And last we pass the extra parameters for our stream. Here we only have the
allowed formats (`SPA_PARAM_EnumFormat`).
Running the mainloop will start the connection and negotiation process.
First our `param_changed` event will be called with the format that was
negotiated between our stream and the camera. This is always something that
is compatible with what we enumerated in the EnumFormat param when we
connected.
Let's take a look at how we can parse the format in the `param_changed`
event:
\code{.c}
static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod *param)
{
struct data *data = userdata;
if (param == NULL || id != SPA_PARAM_Format)
return;
\endcode
First check if there is a param. A NULL param means that it is cleared. The ID
of the param tells you what param it is. We are only interested in Format
param (`SPA_PARAM_Format`).
We can parse the media type and subtype as below and ensure that it is
of the right type. In our example this will always be true but when your
EnumFormat contains different media types or subtypes, this is how you can
parse them:
\code{.c}
if (spa_format_parse(param,
&data->format.media_type,
&data->format.media_subtype) < 0)
return;
if (data->format.media_type != SPA_MEDIA_TYPE_video ||
data->format.media_subtype != SPA_MEDIA_SUBTYPE_raw)
return;
\endcode
For the `video/raw` media type/subtype there is a utility function to
parse out the values into a `struct spa_video_info`. This makes it easier
to deal with.
\code{.c}
if (spa_format_video_raw_parse(param, &data->format.info.raw) < 0)
return;
printf("got video format:\n");
printf(" format: %d (%s)\n", data->format.info.raw.format,
spa_debug_type_find_name(spa_type_video_format,
data->format.info.raw.format));
printf(" size: %dx%d\n", data->format.info.raw.size.width,
data->format.info.raw.size.height);
printf(" framerate: %d/%d\n", data->format.info.raw.framerate.num,
data->format.info.raw.framerate.denom);
/** prepare to render video of this size */
}
\endcode
In this example we dump the video size and parameters but in a real playback
or capture application you might want to set up the screen or encoder to
deal with the format.
After negotiation, the process function is called for each new frame. Check out
\ref page_tutorial4 for another example.
\snippet tutorial5.c on_process
In a real playback application, one would do something with the data, like
copy it to the screen or encode it into a file.
\ref page_tutorial4 | \ref page_tutorial "Index" | \ref page_tutorial6
*/

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"
*/