From 67a27d80c642b46c670e1655caf5ea6d4e8b0f67 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 6 Mar 2022 14:10:55 +0200 Subject: [PATCH] gst: use target.object instead of node.target, soft-deprecate ids Use target-object= instead of path= for specifying sink/src targets. Deprecate path= argument. Change device provider to preferably expose serials instead of ids. --- src/gst/gstpipewiredeviceprovider.c | 44 ++++++++++++++++++++------ src/gst/gstpipewiredeviceprovider.h | 1 + src/gst/gstpipewiresink.c | 45 ++++++++++++++++++++++++++- src/gst/gstpipewiresink.h | 1 + src/gst/gstpipewiresrc.c | 48 +++++++++++++++++++++++++++-- src/gst/gstpipewiresrc.h | 1 + 6 files changed, 128 insertions(+), 12 deletions(-) diff --git a/src/gst/gstpipewiredeviceprovider.c b/src/gst/gstpipewiredeviceprovider.c index 454815dca..0d2942d77 100644 --- a/src/gst/gstpipewiredeviceprovider.c +++ b/src/gst/gstpipewiredeviceprovider.c @@ -44,6 +44,7 @@ G_DEFINE_TYPE (GstPipeWireDevice, gst_pipewire_device, GST_TYPE_DEVICE); enum { PROP_ID = 1, + PROP_SERIAL, }; static GstElement * @@ -51,12 +52,16 @@ gst_pipewire_device_create_element (GstDevice * device, const gchar * name) { GstPipeWireDevice *pipewire_dev = GST_PIPEWIRE_DEVICE (device); GstElement *elem; - gchar *str; + gchar *id_str, *serial_str; elem = gst_element_factory_make (pipewire_dev->element, name); - str = g_strdup_printf ("%u", pipewire_dev->id); - g_object_set (elem, "path", str, NULL); - g_free (str); + + /* XXX: eventually only add target-object here */ + id_str = g_strdup_printf ("%u", pipewire_dev->id); + serial_str = g_strdup_printf ("%"PRIu64, pipewire_dev->serial); + g_object_set (elem, "path", id_str, "target-object", serial_str, NULL); + g_free (id_str); + g_free (serial_str); return elem; } @@ -65,7 +70,7 @@ static gboolean gst_pipewire_device_reconfigure_element (GstDevice * device, GstElement * element) { GstPipeWireDevice *pipewire_dev = GST_PIPEWIRE_DEVICE (device); - gchar *str; + gchar *id_str, *serial_str; if (spa_streq(pipewire_dev->element, "pipewiresrc")) { if (!GST_IS_PIPEWIRE_SRC (element)) @@ -77,9 +82,12 @@ gst_pipewire_device_reconfigure_element (GstDevice * device, GstElement * elemen g_assert_not_reached (); } - str = g_strdup_printf ("%u", pipewire_dev->id); - g_object_set (element, "path", str, NULL); - g_free (str); + /* XXX: eventually only add target-object here */ + id_str = g_strdup_printf ("%u", pipewire_dev->id); + serial_str = g_strdup_printf ("%"PRIu64, pipewire_dev->serial); + g_object_set (element, "path", id_str, "target-object", serial_str, NULL); + g_free (id_str); + g_free (serial_str); return TRUE; } @@ -97,6 +105,9 @@ gst_pipewire_device_get_property (GObject * object, guint prop_id, case PROP_ID: g_value_set_uint (value, device->id); break; + case PROP_SERIAL: + g_value_set_uint64 (value, device->serial); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -115,6 +126,9 @@ gst_pipewire_device_set_property (GObject * object, guint prop_id, case PROP_ID: device->id = g_value_get_uint (value); break; + case PROP_SERIAL: + device->serial = g_value_get_uint64 (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -144,6 +158,11 @@ gst_pipewire_device_class_init (GstPipeWireDeviceClass * klass) g_param_spec_uint ("id", "Id", "The internal id of the PipeWire device", 0, G_MAXUINT32, SPA_ID_INVALID, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_SERIAL, + g_param_spec_uint64 ("serial", "Serial", + "The internal serial of the PipeWire device", 0, G_MAXUINT64, SPA_ID_INVALID, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void @@ -176,6 +195,7 @@ struct node_data { struct pw_node *proxy; struct spa_hook proxy_listener; uint32_t id; + uint64_t serial; struct spa_hook node_listener; struct pw_node_info *info; GstCaps *caps; @@ -187,6 +207,7 @@ struct port_data { struct pw_port *proxy; struct spa_hook proxy_listener; uint32_t id; + uint64_t serial; struct spa_hook port_listener; }; @@ -236,9 +257,10 @@ new_node (GstPipeWireDeviceProvider *self, struct node_data *data) gstdev = g_object_new (GST_TYPE_PIPEWIRE_DEVICE, "display-name", name, "caps", data->caps, "device-class", klass, - "id", data->id, "properties", props, NULL); + "id", data->id, "serial", data->serial, "properties", props, NULL); gstdev->id = data->id; + gstdev->serial = data->serial; gstdev->type = type; gstdev->element = element; if (props) @@ -476,6 +498,8 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions, nd->self = self; nd->proxy = node; nd->id = id; + if (!props || !spa_atou64(spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL), &nd->serial, 0)) + nd->serial = SPA_ID_INVALID; spa_list_append(&rd->nodes, &nd->link); pw_node_add_listener(node, &nd->node_listener, &node_events, nd); pw_proxy_add_listener((struct pw_proxy*)node, &nd->proxy_listener, &proxy_node_events, nd); @@ -500,6 +524,8 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions, pd->node_data = nd; pd->proxy = port; pd->id = id; + if (!props || !spa_atou64(spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL), &pd->serial, 0)) + pd->serial = SPA_ID_INVALID; pw_port_add_listener(port, &pd->port_listener, &port_events, pd); pw_proxy_add_listener((struct pw_proxy*)port, &pd->proxy_listener, &proxy_port_events, pd); resync(self); diff --git a/src/gst/gstpipewiredeviceprovider.h b/src/gst/gstpipewiredeviceprovider.h index badb3837f..3740ec0f3 100644 --- a/src/gst/gstpipewiredeviceprovider.h +++ b/src/gst/gstpipewiredeviceprovider.h @@ -56,6 +56,7 @@ struct _GstPipeWireDevice { GstPipeWireDeviceType type; uint32_t id; + uint64_t serial; const gchar *element; }; diff --git a/src/gst/gstpipewiresink.c b/src/gst/gstpipewiresink.c index 89f6b8253..a69520853 100644 --- a/src/gst/gstpipewiresink.c +++ b/src/gst/gstpipewiresink.c @@ -60,6 +60,7 @@ enum { PROP_0, PROP_PATH, + PROP_TARGET_OBJECT, PROP_CLIENT_NAME, PROP_STREAM_PROPERTIES, PROP_MODE, @@ -124,6 +125,7 @@ gst_pipewire_sink_finalize (GObject * object) if (pwsink->properties) gst_structure_free (pwsink->properties); g_free (pwsink->path); + g_free (pwsink->target_object); g_free (pwsink->client_name); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -160,6 +162,16 @@ gst_pipewire_sink_class_init (GstPipeWireSinkClass * klass) "The sink path to connect to (NULL = default)", NULL, G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_DEPRECATED)); + + g_object_class_install_property (gobject_class, + PROP_TARGET_OBJECT, + g_param_spec_string ("target-object", + "Target object", + "The sink name/serial to connect to (NULL = default)", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, @@ -339,6 +351,11 @@ gst_pipewire_sink_set_property (GObject * object, guint prop_id, pwsink->path = g_value_dup_string (value); break; + case PROP_TARGET_OBJECT: + g_free (pwsink->target_object); + pwsink->target_object = g_value_dup_string (value); + break; + case PROP_CLIENT_NAME: g_free (pwsink->client_name); pwsink->client_name = g_value_dup_string (value); @@ -376,6 +393,10 @@ gst_pipewire_sink_get_property (GObject * object, guint prop_id, g_value_set_string (value, pwsink->path); break; + case PROP_TARGET_OBJECT: + g_value_set_string (value, pwsink->target_object); + break; + case PROP_CLIENT_NAME: g_value_set_string (value, pwsink->client_name); break; @@ -522,15 +543,37 @@ gst_pipewire_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) if (state == PW_STREAM_STATE_UNCONNECTED) { enum pw_stream_flags flags = 0; + uint32_t target_id; if (pwsink->mode != GST_PIPEWIRE_SINK_MODE_PROVIDE) flags |= PW_STREAM_FLAG_AUTOCONNECT; else flags |= PW_STREAM_FLAG_DRIVER; + target_id = pwsink->path ? (uint32_t)atoi(pwsink->path) : PW_ID_ANY; + + if (pwsink->target_object) { + struct spa_dict_item items[2] = { + SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsink->target_object), + SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL), + }; + struct spa_dict dict = SPA_DICT_INIT_ARRAY(items); + uint64_t serial; + + /* If target.object is a name, set it also to node.target */ + if (spa_atou64(pwsink->target_object, &serial, 0)) { + dict.n_items = 1; + } else { + target_id = PW_ID_ANY; + items[1].value = pwsink->target_object; + } + + pw_stream_update_properties (pwsink->stream, &dict); + } + pw_stream_connect (pwsink->stream, PW_DIRECTION_OUTPUT, - pwsink->path ? (uint32_t)atoi(pwsink->path) : PW_ID_ANY, + target_id, flags, (const struct spa_pod **) possible->pdata, possible->len); diff --git a/src/gst/gstpipewiresink.h b/src/gst/gstpipewiresink.h index a93fd77df..1a821484e 100644 --- a/src/gst/gstpipewiresink.h +++ b/src/gst/gstpipewiresink.h @@ -78,6 +78,7 @@ struct _GstPipeWireSink { /*< private >*/ gchar *path; + gchar *target_object; gchar *client_name; int fd; diff --git a/src/gst/gstpipewiresrc.c b/src/gst/gstpipewiresrc.c index fc80ef997..0187f7cd9 100644 --- a/src/gst/gstpipewiresrc.c +++ b/src/gst/gstpipewiresrc.c @@ -67,6 +67,7 @@ enum { PROP_0, PROP_PATH, + PROP_TARGET_OBJECT, PROP_CLIENT_NAME, PROP_STREAM_PROPERTIES, PROP_ALWAYS_COPY, @@ -116,6 +117,11 @@ gst_pipewire_src_set_property (GObject * object, guint prop_id, pwsrc->path = g_value_dup_string (value); break; + case PROP_TARGET_OBJECT: + g_free (pwsrc->target_object); + pwsrc->target_object = g_value_dup_string (value); + break; + case PROP_CLIENT_NAME: g_free (pwsrc->client_name); pwsrc->client_name = g_value_dup_string (value); @@ -169,6 +175,10 @@ gst_pipewire_src_get_property (GObject * object, guint prop_id, g_value_set_string (value, pwsrc->path); break; + case PROP_TARGET_OBJECT: + g_value_set_string (value, pwsrc->target_object); + break; + case PROP_CLIENT_NAME: g_value_set_string (value, pwsrc->client_name); break; @@ -244,6 +254,7 @@ gst_pipewire_src_finalize (GObject * object) if (pwsrc->clock) gst_object_unref (pwsrc->clock); g_free (pwsrc->path); + g_free (pwsrc->target_object); g_free (pwsrc->client_name); g_object_unref(pwsrc->pool); @@ -274,6 +285,16 @@ gst_pipewire_src_class_init (GstPipeWireSrcClass * klass) "The source path to connect to (NULL = default)", NULL, G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_DEPRECATED)); + + g_object_class_install_property (gobject_class, + PROP_TARGET_OBJECT, + g_param_spec_string ("target-object", + "Target object", + "The source name/serial to connect to (NULL = default)", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, @@ -675,6 +696,7 @@ gst_pipewire_src_negotiate (GstBaseSrc * basesrc) GPtrArray *possible; const char *error = NULL; struct timespec abstime; + uint32_t target_id; /* first see what is possible on our source pad */ thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); @@ -727,11 +749,33 @@ gst_pipewire_src_negotiate (GstBaseSrc * basesrc) } } - GST_DEBUG_OBJECT (basesrc, "connect capture with path %s", pwsrc->path); + target_id = pwsrc->path ? (uint32_t)atoi(pwsrc->path) : PW_ID_ANY; + + if (pwsrc->target_object) { + struct spa_dict_item items[2] = { + SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsrc->target_object), + SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL), + }; + struct spa_dict dict = SPA_DICT_INIT_ARRAY(items); + uint64_t serial; + + /* If target.object is a name, set it also to node.target */ + if (spa_atou64(pwsrc->target_object, &serial, 0)) { + dict.n_items = 1; + } else { + target_id = PW_ID_ANY; + items[1].value = pwsrc->target_object; + } + + pw_stream_update_properties (pwsrc->stream, &dict); + } + + GST_DEBUG_OBJECT (basesrc, "connect capture with path %s, target-object %s", + pwsrc->path, pwsrc->target_object); pwsrc->negotiated = FALSE; pw_stream_connect (pwsrc->stream, PW_DIRECTION_INPUT, - pwsrc->path ? (uint32_t)atoi(pwsrc->path) : PW_ID_ANY, + target_id, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_DONT_RECONNECT, (const struct spa_pod **)possible->pdata, possible->len); diff --git a/src/gst/gstpipewiresrc.h b/src/gst/gstpipewiresrc.h index 6b5094b56..e1faa3d19 100644 --- a/src/gst/gstpipewiresrc.h +++ b/src/gst/gstpipewiresrc.h @@ -60,6 +60,7 @@ struct _GstPipeWireSrc { /*< private >*/ gchar *path; + gchar *target_object; gchar *client_name; gboolean always_copy; gint min_buffers;