Tutorial - Part 2: Enumerating objects

Tutorial - Part 1: Getting started | Index | Tutorial - Part 3: Forcing a roundtrip

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.

#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;
}
pw_context_destroy
void pw_context_destroy(struct pw_context *context)
destroy a context object, all resources except the main_loop will be destroyed
Definition: context.c:434
pw_context_new
struct pw_context * pw_context_new(struct pw_loop *main_loop, struct pw_properties *props, size_t user_data_size)
Make a new context object for a given main_loop.
Definition: context.c:206
pw_context_connect
struct pw_core * pw_context_connect(struct pw_context *context, struct pw_properties *properties, size_t user_data_size)
Connect to a PipeWire instance.
Definition: core.c:402
pw_core_disconnect
int pw_core_disconnect(struct pw_core *core)
disconnect and destroy a core
Definition: core.c:489
PW_VERSION_REGISTRY
#define PW_VERSION_REGISTRY
Definition: core.h:69
pw_core_get_registry
static struct pw_registry * pw_core_get_registry(struct pw_core *core, uint32_t version, size_t user_data_size)
Definition: core.h:405
pw_main_loop_new
struct pw_main_loop * pw_main_loop_new(const struct spa_dict *props)
Create a new main loop.
Definition: main-loop.c:80
pw_main_loop_destroy
void pw_main_loop_destroy(struct pw_main_loop *loop)
Destroy a loop.
Definition: main-loop.c:90
pw_main_loop_run
int pw_main_loop_run(struct pw_main_loop *loop)
Run a main loop.
Definition: main-loop.c:139
pw_main_loop_get_loop
struct pw_loop * pw_main_loop_get_loop(struct pw_main_loop *loop)
Get the loop implementation.
Definition: main-loop.c:113
pw_init
void pw_init(int *argc, char **argv[])
Initialize PipeWire.
Definition: pipewire.c:586
pw_proxy_destroy
void pw_proxy_destroy(struct pw_proxy *proxy)
destroy a proxy
Definition: proxy.c:231
PW_VERSION_REGISTRY_EVENTS
#define PW_VERSION_REGISTRY_EVENTS
Definition: core.h:483
pw_registry_add_listener
#define pw_registry_add_listener(p,...)
Registry.
Definition: core.h:566
spa_zero
#define spa_zero(x)
Definition: defs.h:385
pipewire.h
pipewire/pipewire.h
pw_context
pw_core
pw_main_loop
A main loop object.
pw_proxy
pw_registry_events
Registry events.
Definition: core.h:481
pw_registry
spa_dict
Definition: dict.h:59
spa_hook
A hook, contains the structure with functions and the data passed to the functions.
Definition: hook.h:342

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.

...
pw_init(&argc, &argv);
...

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:

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 */);

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:

struct pw_core *core;
core = pw_context_connect(context,
NULL /* properties */,
0 /* user_data size */);

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.

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);

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:

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);
}
pw_registry_events::version
uint32_t version
Definition: core.h:484

Now that everything is set up we can start the mainloop and let the communication between client and server continue:

pw_main_loop_run(loop);

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.

Tutorial - Part 1: Getting started | Index | Tutorial - Part 3: Forcing a roundtrip