From 336caa9db3d7b0a92244cd4f22c545c5eb2a4e3d Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 10 Oct 2021 00:09:32 +0300 Subject: [PATCH] doc: add tutorials as Doxygen examples --- doc/Doxyfile.in | 3 +- doc/meson.build | 6 ++ doc/tutorial1.c | 19 ++++++ doc/tutorial1.dox | 15 +---- doc/tutorial2.c | 56 +++++++++++++++++ doc/tutorial2.dox | 52 +--------------- doc/tutorial3.c | 86 ++++++++++++++++++++++++++ doc/tutorial3.dox | 114 +--------------------------------- doc/tutorial4.c | 112 +++++++++++++++++++++++++++++++++ doc/tutorial4.dox | 145 +------------------------------------------ doc/tutorial5.c | 137 +++++++++++++++++++++++++++++++++++++++++ doc/tutorial5.dox | 154 +--------------------------------------------- doc/tutorial6.c | 97 +++++++++++++++++++++++++++++ doc/tutorial6.dox | 126 ++----------------------------------- 14 files changed, 528 insertions(+), 594 deletions(-) create mode 100644 doc/tutorial1.c create mode 100644 doc/tutorial2.c create mode 100644 doc/tutorial3.c create mode 100644 doc/tutorial4.c create mode 100644 doc/tutorial5.c create mode 100644 doc/tutorial6.c diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 82ec2edb6..abbbaee8b 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -23,7 +23,8 @@ 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@/spa/examples" \ + "@top_srcdir@/doc" EXAMPLE_PATTERNS = "*.c" REFERENCED_BY_RELATION = NO diff --git a/doc/meson.build b/doc/meson.build index b4ae72f76..139b00109 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -86,6 +86,12 @@ cssfiles = [ # Example files (in order from simple to esoteric) example_files = [ + 'tutorial1.c', + 'tutorial2.c', + 'tutorial3.c', + 'tutorial4.c', + 'tutorial5.c', + 'tutorial6.c', 'audio-src.c', 'audio-dsp-src.c', 'audio-dsp-filter.c', diff --git a/doc/tutorial1.c b/doc/tutorial1.c new file mode 100644 index 000000000..050aa88d2 --- /dev/null +++ b/doc/tutorial1.c @@ -0,0 +1,19 @@ +/* + [title] + \ref page_tutorial1 + [title] + */ +/* [code] */ +#include + +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] */ diff --git a/doc/tutorial1.dox b/doc/tutorial1.dox index 9d3636bff..cca9cfccc 100644 --- a/doc/tutorial1.dox +++ b/doc/tutorial1.dox @@ -11,20 +11,7 @@ environment. Let get started with the simplest application. -\code{.c} -#include - -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; -} -\endcode +\snippet tutorial1.c code Before you can use any PipeWire functions, you need to call `pw_init()`. diff --git a/doc/tutorial2.c b/doc/tutorial2.c new file mode 100644 index 000000000..66647d7dc --- /dev/null +++ b/doc/tutorial2.c @@ -0,0 +1,56 @@ +/* + [title] + \ref page_tutorial2 + [title] + */ +/* [code] */ +#include + +static void registry_event_global(void *data, uint32_t id, + uint32_t permissions, const char *type, uint32_t version, + const struct spa_dict *props) +{ + printf("object: id:%u type:%s/%d\n", id, type, version); +} + +static const struct pw_registry_events registry_events = { + PW_VERSION_REGISTRY_EVENTS, + .global = registry_event_global, +}; + +int main(int argc, char *argv[]) +{ + struct pw_main_loop *loop; + struct pw_context *context; + struct pw_core *core; + struct pw_registry *registry; + struct spa_hook registry_listener; + + pw_init(&argc, &argv); + + loop = pw_main_loop_new(NULL /* properties */); + context = pw_context_new(pw_main_loop_get_loop(loop), + NULL /* properties */, + 0 /* user_data size */); + + core = pw_context_connect(context, + NULL /* properties */, + 0 /* user_data size */); + + registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, + 0 /* user_data size */); + + spa_zero(registry_listener); + pw_registry_add_listener(registry, ®istry_listener, + ®istry_events, NULL); + + pw_main_loop_run(loop); + + pw_proxy_destroy((struct pw_proxy*)registry); + pw_core_disconnect(core); + pw_context_destroy(context); + pw_main_loop_destroy(loop); + + return 0; +} +/* [code] */ diff --git a/doc/tutorial2.dox b/doc/tutorial2.dox index 19af9f407..84c9bd7cc 100644 --- a/doc/tutorial2.dox +++ b/doc/tutorial2.dox @@ -7,57 +7,7 @@ enumerate the objects that it has. Let take a look at the following application to start. -\code{.c} -#include - -static void registry_event_global(void *data, uint32_t id, - uint32_t permissions, const char *type, uint32_t version, - const struct spa_dict *props) -{ - printf("object: id:%u type:%s/%d\n", id, type, version); -} - -static const struct pw_registry_events registry_events = { - PW_VERSION_REGISTRY_EVENTS, - .global = registry_event_global, -}; - -int main(int argc, char *argv[]) -{ - struct pw_main_loop *loop; - struct pw_context *context; - struct pw_core *core; - struct pw_registry *registry; - struct spa_hook registry_listener; - - pw_init(&argc, &argv); - - loop = pw_main_loop_new(NULL /* properties */); - context = pw_context_new(pw_main_loop_get_loop(loop), - NULL /* properties */, - 0 /* user_data size */); - - core = pw_context_connect(context, - NULL /* properties */, - 0 /* user_data size */); - - registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, - 0 /* user_data size */); - - spa_zero(registry_listener); - pw_registry_add_listener(registry, ®istry_listener, - ®istry_events, NULL); - - pw_main_loop_run(loop); - - pw_proxy_destroy((struct pw_proxy*)registry); - pw_core_disconnect(core); - pw_context_destroy(context); - pw_main_loop_destroy(loop); - - return 0; -} -\endcode +\snippet tutorial2.c code To compile the simple test application, copy it into a tutorial2.c file and use: diff --git a/doc/tutorial3.c b/doc/tutorial3.c new file mode 100644 index 000000000..c7dd792ca --- /dev/null +++ b/doc/tutorial3.c @@ -0,0 +1,86 @@ +/* + [title] + \ref page_tutorial3 + [title] + */ +/* [code] */ +#include + +/* [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, ®istry_listener, + ®istry_events, NULL); + + roundtrip(core, loop); + + pw_proxy_destroy((struct pw_proxy*)registry); + pw_core_disconnect(core); + pw_context_destroy(context); + pw_main_loop_destroy(loop); + + return 0; +} +/* [code] */ diff --git a/doc/tutorial3.dox b/doc/tutorial3.dox index 3b0ee5021..17dcc18c9 100644 --- a/doc/tutorial3.dox +++ b/doc/tutorial3.dox @@ -5,41 +5,12 @@ 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 "Tutorial2" slightly +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: -\code{.c} -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; -} -\endcode +\snippet tutorial3.c roundtrip Let's take a look at what this method does. @@ -117,86 +88,7 @@ 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. - -\code{.c} -#include - -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; -} - -static void registry_event_global(void *data, uint32_t id, - uint32_t permissions, const char *type, uint32_t version, - const struct spa_dict *props) -{ - printf("object: id:%u type:%s/%d\n", id, type, version); -} - -static const struct pw_registry_events registry_events = { - PW_VERSION_REGISTRY_EVENTS, - .global = registry_event_global, -}; - -int main(int argc, char *argv[]) -{ - struct pw_main_loop *loop; - struct pw_context *context; - struct pw_core *core; - struct pw_registry *registry; - struct spa_hook registry_listener; - - pw_init(&argc, &argv); - - loop = pw_main_loop_new(NULL /* properties */); - context = pw_context_new(pw_main_loop_get_loop(loop), - NULL /* properties */, - 0 /* user_data size */); - - core = pw_context_connect(context, - NULL /* properties */, - 0 /* user_data size */); - - registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, - 0 /* user_data size */); - - spa_zero(registry_listener); - pw_registry_add_listener(registry, ®istry_listener, - ®istry_events, NULL); - - 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; -} -\endcode +\snippet tutorial3.c code To compile the simple test application, copy it into a tutorial3.c file and use: diff --git a/doc/tutorial4.c b/doc/tutorial4.c new file mode 100644 index 000000000..94fb6d3e2 --- /dev/null +++ b/doc/tutorial4.c @@ -0,0 +1,112 @@ +/* + [title] + \ref page_tutorial4 + [title] + */ +/* [code] */ +#include + +#include + +#include + +#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] */ diff --git a/doc/tutorial4.dox b/doc/tutorial4.dox index 476057f55..9579a88a2 100644 --- a/doc/tutorial4.dox +++ b/doc/tutorial4.dox @@ -6,111 +6,7 @@ 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: -\code{.c} -#include - -#include - -#include - -#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; -}; - -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); -} - -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; -} -\endcode +\snippet tutorial4.c code Save as tutorial4.c and compile with: @@ -250,44 +146,7 @@ The main program flow of the process function is: * adjust buffer with number of written bytes, offset, stride, * `pw_stream_queue_buffer()` to queue the buffer for playback. -\code{.c} -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); -} -\endcode +\snippet tutorial4.c on_process Check out the docs for \ref page_spa_buffer for more information about how to work with buffers. diff --git a/doc/tutorial5.c b/doc/tutorial5.c new file mode 100644 index 000000000..754fc025d --- /dev/null +++ b/doc/tutorial5.c @@ -0,0 +1,137 @@ +/* + [title] + \ref page_tutorial5 + [title] + */ +/* [code] */ +#include +#include +#include + +#include + +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] */ diff --git a/doc/tutorial5.dox b/doc/tutorial5.dox index b08891ace..7026fdfe0 100644 --- a/doc/tutorial5.dox +++ b/doc/tutorial5.dox @@ -11,136 +11,7 @@ example is very similar to \ref page_tutorial4. Let's take a look at the code before we break it down: -\code{.c} -#include -#include -#include - -#include - -struct data { - struct pw_main_loop *loop; - struct pw_stream *stream; - - struct spa_video_info format; -}; - -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); -} - -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; -} -\endcode +\snippet tutorial5.c code Save as tutorial5.c and compile with: @@ -337,28 +208,7 @@ deal with the format. After negotiation, the process function is called for each new frame. Check out \ref page_tutorial4 for another example. -\code{.c} -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); -} -\endcode +\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. diff --git a/doc/tutorial6.c b/doc/tutorial6.c new file mode 100644 index 000000000..45d448528 --- /dev/null +++ b/doc/tutorial6.c @@ -0,0 +1,97 @@ +/* + [title] + \ref page_tutorial6 + [title] + */ +/* [code] */ +#include + +struct data { + struct pw_main_loop *loop; + struct pw_context *context; + struct pw_core *core; + + struct pw_registry *registry; + struct spa_hook registry_listener; + + struct pw_client *client; + struct spa_hook client_listener; +}; + +/* [client_info] */ +static void client_info(void *object, const struct pw_client_info *info) +{ + struct data *data = object; + const struct spa_dict_item *item; + + printf("client: id:%u\n", info->id); + printf("\tprops:\n"); + spa_dict_for_each(item, info->props) + printf("\t\t%s: \"%s\"\n", item->key, item->value); + + pw_main_loop_quit(data->loop); +} + +static const struct pw_client_events client_events = { + PW_VERSION_CLIENT_EVENTS, + .info = client_info, +}; +/* [client_info] */ + +/* [registry_event_global] */ +static void registry_event_global(void *_data, uint32_t id, + uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) +{ + struct data *data = _data; + if (data->client != NULL) + return; + + if (strcmp(type, PW_TYPE_INTERFACE_Client) == 0) { + data->client = pw_registry_bind(data->registry, + id, type, PW_VERSION_CLIENT, 0); + pw_client_add_listener(data->client, + &data->client_listener, + &client_events, data); + } +} +/* [registry_event_global] */ + +static const struct pw_registry_events registry_events = { + PW_VERSION_REGISTRY_EVENTS, + .global = registry_event_global, +}; + +int main(int argc, char *argv[]) +{ + struct data data; + + spa_zero(data); + + pw_init(&argc, &argv); + + data.loop = pw_main_loop_new(NULL /* properties */ ); + data.context = pw_context_new(pw_main_loop_get_loop(data.loop), + NULL /* properties */ , + 0 /* user_data size */ ); + + data.core = pw_context_connect(data.context, NULL /* properties */ , + 0 /* user_data size */ ); + + data.registry = pw_core_get_registry(data.core, PW_VERSION_REGISTRY, + 0 /* user_data size */ ); + + pw_registry_add_listener(data.registry, &data.registry_listener, + ®istry_events, &data); + + pw_main_loop_run(data.loop); + + pw_proxy_destroy((struct pw_proxy *)data.client); + pw_proxy_destroy((struct pw_proxy *)data.registry); + pw_core_disconnect(data.core); + pw_context_destroy(data.context); + pw_main_loop_destroy(data.loop); + + return 0; +} +/* [code] */ diff --git a/doc/tutorial6.dox b/doc/tutorial6.dox index 82ef26f3f..4005c5a8b 100644 --- a/doc/tutorial6.dox +++ b/doc/tutorial6.dox @@ -7,94 +7,7 @@ receive events and call methods on the object. Let take a look at the following application to start. -\code{.c} -#include - -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; -}; - -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, -}; - -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); - } -} - -static const struct pw_registry_events registry_events = { - PW_VERSION_REGISTRY_EVENTS, - .global = registry_event_global, -}; - -int main(int argc, char *argv[]) -{ - struct data data; - - spa_zero(data); - - pw_init(&argc, &argv); - - data.loop = pw_main_loop_new(NULL /* properties */ ); - data.context = pw_context_new(pw_main_loop_get_loop(data.loop), - NULL /* properties */ , - 0 /* user_data size */ ); - - data.core = pw_context_connect(data.context, NULL /* properties */ , - 0 /* user_data size */ ); - - data.registry = pw_core_get_registry(data.core, PW_VERSION_REGISTRY, - 0 /* user_data size */ ); - - pw_registry_add_listener(data.registry, &data.registry_listener, - ®istry_events, &data); - - pw_main_loop_run(data.loop); - - pw_proxy_destroy((struct pw_proxy *)data.client); - pw_proxy_destroy((struct pw_proxy *)data.registry); - pw_core_disconnect(data.core); - pw_context_destroy(data.context); - pw_main_loop_destroy(data.loop); - - return 0; -} -\endcode +\snippet tutorial6.c code To compile the simple test application, copy it into a tutorial6.c file and use: @@ -107,22 +20,7 @@ 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: -\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) -{ - 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); - /* ... */ - } -} -\endcode +\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`. @@ -136,25 +34,9 @@ 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 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, -}; - static void registry_event_global(void *_data, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const struct spa_dict *props)