module-pipewire and simple-endpoint: use the new Proxy API

This commit is contained in:
Julian Bouzas
2019-06-17 14:20:38 -04:00
parent 8877e0438d
commit 1f2386c08d
2 changed files with 275 additions and 159 deletions

View File

@@ -27,150 +27,258 @@ struct module_data
{
WpModule *module;
/* Registry */
struct pw_registry_proxy *registry_proxy;
struct spa_hook registry_listener;
struct pw_core_proxy *core_proxy;
struct spa_hook core_listener;
GQueue *done_queue;
/* Client nodes info */
GHashTable *client_nodes_info;
};
typedef void (*WpDoneCallback)(gpointer, gpointer);
struct done_data
struct endpoint_info
{
WpDoneCallback callback;
gpointer data;
GDestroyNotify data_destroy;
gchar *name;
gchar *media_class;
const struct pw_proxy *proxy;
};
struct proxy_info
{
const struct module_data *data;
uint32_t node_id;
WpProxyPort *proxy_port;
};
static void
done_data_destroy(gpointer p)
endpoint_info_destroy(gpointer p)
{
struct done_data *dd = p;
if (dd->data_destroy) {
dd->data_destroy(dd->data);
dd->data = NULL;
struct endpoint_info *ei = p;
/* Free the name */
if (ei->name) {
g_free (ei->name);
ei->name = NULL;
}
g_slice_free (struct done_data, dd);
}
static void
sync_core_with_callback(struct module_data* impl, WpDoneCallback callback,
gpointer data, GDestroyNotify data_destroy)
{
struct done_data *dd = g_new0(struct done_data, 1);
/* Set the data */
dd->callback = callback;
dd->data = data;
dd->data_destroy = data_destroy;
/* Add the data to the queue */
g_queue_push_tail (impl->done_queue, dd);
/* Sync the core */
pw_core_proxy_sync(impl->core_proxy, 0, 0);
}
static void
core_done(void *d, uint32_t id, int seq)
{
struct module_data * impl = d;
struct done_data * dd = NULL;
/* Process all the done_data queue */
while ((dd = g_queue_pop_head(impl->done_queue))) {
if (dd->callback)
dd->callback(impl, dd->data);
done_data_destroy(dd);
/* Free the media class */
if (ei->media_class) {
g_free (ei->media_class);
ei->media_class = NULL;
}
/* Clean up */
g_slice_free (struct endpoint_info, p);
}
static const struct pw_core_proxy_events core_events = {
PW_VERSION_CORE_EVENTS,
.done = core_done
};
static void
register_endpoint (struct module_data* data, WpEndpoint *ep)
proxy_info_destroy(gpointer p)
{
g_autoptr (WpCore) core = NULL;
core = wp_module_get_core (data->module);
g_return_if_fail (core != NULL);
wp_endpoint_register (ep, core);
struct proxy_info *pi = p;
/* Unref the proxy port */
if (pi->proxy_port) {
g_object_unref (pi->proxy_port);
pi->proxy_port = NULL;
}
/* Clean up */
g_slice_free (struct proxy_info, p);
}
static void
registry_global (void * d, uint32_t id, uint32_t parent_id,
uint32_t permissions, uint32_t type, uint32_t version,
proxy_node_created(GObject *initable, GAsyncResult *res, gpointer d)
{
struct proxy_info *pi = d;
const struct module_data *data = pi->data;
g_autoptr (WpCore) core = wp_module_get_core (data->module);
WpProxyNode *proxy_node = NULL;
struct endpoint_info *ei = NULL;
WpEndpoint *endpoint = NULL;
g_autoptr (GVariant) endpoint_props = NULL;
GVariantBuilder b;
/* Get the proxy */
proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
if (!proxy_node)
return;
/* Register the proxy node */
wp_proxy_register(WP_PROXY(proxy_node));
/* Get the client node info */
ei = g_hash_table_lookup(data->client_nodes_info,
GINT_TO_POINTER(pi->node_id));
if (!ei)
return;
/* Set the properties */
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&b, "{sv}",
"name", ei->name ? g_variant_new_string (ei->name) :
g_variant_new_take_string (
g_strdup_printf ("Stream %u", pi->node_id)));
g_variant_builder_add (&b, "{sv}",
"media-class", g_variant_new_string (ei->media_class));
g_variant_builder_add (&b, "{sv}",
"proxy-node", g_variant_new_uint64 ((guint64) proxy_node));
g_variant_builder_add (&b, "{sv}",
"proxy-port", g_variant_new_uint64 ((guint64)
g_object_ref(pi->proxy_port)));
endpoint_props = g_variant_builder_end (&b);
/* Create the endpoint */
endpoint = wp_factory_make (core, "pipewire-simple-endpoint",
WP_TYPE_ENDPOINT, endpoint_props);
/* Register the endpoint */
wp_endpoint_register (endpoint, core);
/* Clean up */
proxy_info_destroy (pi);
}
static void
proxy_port_created(GObject *initable, GAsyncResult *res, gpointer d)
{
struct proxy_info *pi = d;
const struct module_data *data = pi->data;
g_autoptr (WpCore) core = wp_module_get_core (data->module);
WpProxyPort *proxy_port = NULL;
struct pw_proxy *proxy = NULL;
/* Get the proxy port */
proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
if (!proxy_port)
return;
/* Register the proxy port */
wp_proxy_register(WP_PROXY(proxy_port));
/* Forward the proxy port */
pi->proxy_port = proxy_port;
/* Get the node proxy */
proxy = pw_registry_proxy_bind (data->registry_proxy, pi->node_id,
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0);
if (!proxy)
return;
/* Create the proxy node asynchronically */
wp_proxy_node_new(core, proxy, proxy_node_created, pi);
}
static void
handle_node (struct module_data *data, uint32_t id, uint32_t parent_id,
const struct spa_dict * props)
{
struct module_data *data = d;
struct endpoint_info *ei = NULL;
const gchar *name;
const gchar *media_class;
struct pw_proxy *proxy;
GVariantBuilder b;
g_autoptr (GVariant) endpoint_props = NULL;
g_autoptr (WpCore) core = NULL;
g_autoptr (WpEndpoint) endpoint = NULL;
struct spa_audio_info_raw format = { 0, };
struct spa_pod *param;
struct spa_pod_builder pod_builder = { 0, };
char buf[1024];
/* listen for client "Stream" nodes and create endpoints for them */
if (type == PW_TYPE_INTERFACE_Node &&
props && (media_class = spa_dict_lookup(props, "media.class")) &&
g_str_has_prefix (media_class, "Stream/"))
{
name = spa_dict_lookup (props, "media.name");
if (!name)
name = spa_dict_lookup (props, "node.name");
/* Make sure the node has properties */
if (!props) {
g_warning("node has no properties, skipping...");
return;
}
g_debug ("found stream node: id:%u ; name:%s ; media_class:%s", id, name,
media_class);
/* Get the media_class */
media_class = spa_dict_lookup(props, "media.class");
proxy = pw_registry_proxy_bind (data->registry_proxy,
id, type, PW_VERSION_NODE, 0);
/* Only handle client Stream nodes */
if (!g_str_has_prefix (media_class, "Stream/"))
return;
/* TODO: we need to get this from the EnumFormat event */
format.format = SPA_AUDIO_FORMAT_F32P;
format.flags = 1;
format.rate = 48000;
format.channels = 2;
format.position[0] = 0;
format.position[1] = 0;
/* Get the name */
name = spa_dict_lookup (props, "media.name");
if (!name)
name = spa_dict_lookup (props, "node.name");
/* Set the profile */
spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
param = spa_pod_builder_add_object(&pod_builder,
SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
SPA_PARAM_PROFILE_direction, SPA_POD_Id(PW_DIRECTION_OUTPUT),
SPA_PARAM_PROFILE_format, SPA_POD_Pod(param));
pw_node_proxy_set_param((struct pw_node_proxy*)proxy,
SPA_PARAM_Profile, 0, param);
g_debug ("found stream node: id:%u ; name:%s ; media_class:%s", id, name,
media_class);
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&b, "{sv}", "node-id", g_variant_new_uint32 (id));
g_variant_builder_add (&b, "{sv}",
"name", name ? g_variant_new_string (name) :
g_variant_new_take_string (g_strdup_printf ("Stream %u", id)));
g_variant_builder_add (&b, "{sv}",
"media-class", g_variant_new_string (media_class));
g_variant_builder_add (&b, "{sv}",
"node-proxy", g_variant_new_uint64 ((guint64) proxy));
endpoint_props = g_variant_builder_end (&b);
/* Get the proxy */
proxy = pw_registry_proxy_bind (data->registry_proxy, id,
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0);
core = wp_module_get_core (data->module);
g_return_if_fail (core != NULL);
/* TODO: Assume all clients have this format for now */
format.format = SPA_AUDIO_FORMAT_F32P;
format.flags = 1;
format.rate = 48000;
format.channels = 1;
format.position[0] = 0;
endpoint = wp_factory_make (core, "pipewire-simple-endpoint",
WP_TYPE_ENDPOINT, endpoint_props);
sync_core_with_callback (data, (WpDoneCallback) register_endpoint,
g_steal_pointer (&endpoint), g_object_unref);
/* Set the profile */
spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
param = spa_pod_builder_add_object(&pod_builder,
SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
SPA_PARAM_PROFILE_direction, SPA_POD_Id(PW_DIRECTION_OUTPUT),
SPA_PARAM_PROFILE_format, SPA_POD_Pod(param));
pw_node_proxy_set_param((struct pw_node_proxy*)proxy,
SPA_PARAM_Profile, 0, param);
/* Create the endpoint info */
ei = g_new0(struct endpoint_info, 1);
ei->name = g_strdup(name);
ei->media_class = g_strdup(media_class);
ei->proxy = proxy;
/* Insert the client node info in the hash table */
g_hash_table_insert(data->client_nodes_info, GINT_TO_POINTER (id), ei);
}
static void
handle_port(struct module_data *data, uint32_t id, uint32_t parent_id,
const struct spa_dict *props)
{
g_autoptr (WpCore) core = wp_module_get_core (data->module);
struct proxy_info *pi = NULL;
struct pw_proxy *proxy = NULL;
/* Only handle ports whose parent is an alsa node */
if (!g_hash_table_contains(data->client_nodes_info,
GINT_TO_POINTER (parent_id)))
return;
/* Get the port proxy */
proxy = pw_registry_proxy_bind (data->registry_proxy, id,
PW_TYPE_INTERFACE_Port, PW_VERSION_PORT, 0);
if (!proxy)
return;
/* Create the port info */
pi = g_new0(struct proxy_info, 1);
pi->data = data;
pi->node_id = parent_id;
pi->proxy_port = NULL;
/* Create the proxy port asynchronically */
wp_proxy_port_new(core, proxy, proxy_port_created, pi);
}
static void
registry_global(void *d, uint32_t id, uint32_t parent_id,
uint32_t permissions, uint32_t type, uint32_t version,
const struct spa_dict *props)
{
struct module_data *data = d;
switch (type) {
case PW_TYPE_INTERFACE_Node:
handle_node(data, id, parent_id, props);
break;
case PW_TYPE_INTERFACE_Port:
handle_port(data, id, parent_id, props);
break;
default:
break;
}
}
@@ -188,9 +296,7 @@ on_remote_connected (WpRemote *remote, WpRemoteState state,
g_object_get (remote, "pw-remote", &pw_remote, NULL);
core_proxy = data->core_proxy = pw_remote_get_core_proxy (pw_remote);
pw_core_proxy_add_listener(data->core_proxy, &data->core_listener,
&core_events, data);
core_proxy = pw_remote_get_core_proxy (pw_remote);
data->registry_proxy = pw_core_proxy_get_registry (core_proxy,
PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
pw_registry_proxy_add_listener(data->registry_proxy,
@@ -202,7 +308,13 @@ module_destroy (gpointer d)
{
struct module_data *data = d;
g_queue_free_full(data->done_queue, done_data_destroy);
/* Destroy the hash table */
if (data->client_nodes_info) {
g_hash_table_destroy(data->client_nodes_info);
data->client_nodes_info = NULL;
}
/* Clean up */
g_slice_free (struct module_data, data);
}
@@ -221,9 +333,12 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
return;
}
/* Create the module data */
data = g_slice_new0 (struct module_data);
data->module = module;
data->done_queue = g_queue_new();
data->client_nodes_info = g_hash_table_new_full (g_direct_hash,
g_direct_equal, NULL, endpoint_info_destroy);
wp_module_set_destroy_callback (module, module_destroy, data);
g_signal_connect (remote, "state-changed::connected",