diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:28:17 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:28:17 +0000 |
commit | 7a46c07230b8d8108c0e8e80df4522d0ac116538 (patch) | |
tree | d483300dab478b994fe199a5d19d18d74153718a /doc/tutorial4.dox | |
parent | Initial commit. (diff) | |
download | pipewire-upstream/0.3.65.tar.xz pipewire-upstream/0.3.65.zip |
Adding upstream version 0.3.65.upstream/0.3.65upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'doc/tutorial4.dox')
-rw-r--r-- | doc/tutorial4.dox | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/doc/tutorial4.dox b/doc/tutorial4.dox new file mode 100644 index 0000000..b8d1707 --- /dev/null +++ b/doc/tutorial4.dox @@ -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 + +*/ |