From 13d846ec3834ea24986af50c0547af1b81d54b3c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 28 Jul 2015 17:05:03 +0200 Subject: [PATCH] Improve introspection Add instrospection of client and source-output. Add properties to source-output and to CreateSourceOutput/Input Add helper to fill properties of context. Add client-name to pinossrc and pinossink Improve test-subscribe to show all new introspection details. --- src/client/context.c | 15 ++- src/client/introspect.c | 223 +++++++++++++++++++++++++++++-- src/client/introspect.h | 77 ++++++++++- src/client/pinos.c | 59 ++++++++ src/client/pinos.h | 5 + src/client/private.h | 2 + src/client/stream.c | 15 ++- src/dbus/org.pinos.xml | 7 + src/gst/gstpinosdeviceprovider.c | 11 -- src/gst/gstpinossink.c | 37 ++++- src/gst/gstpinossink.h | 1 + src/gst/gstpinossrc.c | 22 ++- src/gst/gstpinossrc.h | 1 + src/modules/gst/gst-source.c | 12 +- src/server/client-source.c | 14 +- src/server/client-source.h | 1 + src/server/client.c | 14 +- src/server/source-output.c | 24 ++++ src/server/source.c | 32 +++-- src/server/source.h | 22 +-- src/tests/test-subscribe.c | 162 ++++++++++++++++++++-- 21 files changed, 684 insertions(+), 72 deletions(-) diff --git a/src/client/context.c b/src/client/context.c index 060d63bea..5e3c6bac9 100644 --- a/src/client/context.c +++ b/src/client/context.c @@ -278,6 +278,7 @@ pinos_context_init (PinosContext * context) /** * pinos_context_new: + * @context: a #GMainContext to run in * @name: an application name * @properties: optional properties * @@ -293,7 +294,9 @@ pinos_context_new (GMainContext *context, g_return_val_if_fail (name != NULL, NULL); if (properties == NULL) - properties = pinos_properties_new ("media.name", name, NULL); + properties = pinos_properties_new ("application.name", name, NULL); + + pinos_fill_context_properties (properties); return g_object_new (PINOS_TYPE_CONTEXT, "main-context", context, @@ -419,7 +422,11 @@ subscription_cb (PinosSubscribe *subscribe, break; case PINOS_SUBSCRIPTION_FLAGS_CLIENT: - if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) { + if (event == PINOS_SUBSCRIPTION_EVENT_NEW) { + priv->clients = g_list_prepend (priv->clients, object); + } else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) { + priv->clients = g_list_remove (priv->clients, object); + if (object == priv->client) { priv->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CLOSED, "Client disappeared"); context_set_state (context, PINOS_CONTEXT_STATE_ERROR); @@ -435,6 +442,10 @@ subscription_cb (PinosSubscribe *subscribe, break; case PINOS_SUBSCRIPTION_FLAGS_SOURCE_OUTPUT: + if (event == PINOS_SUBSCRIPTION_EVENT_NEW) + priv->source_outputs = g_list_prepend (priv->source_outputs, object); + else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) + priv->source_outputs = g_list_remove (priv->source_outputs, object); break; } diff --git a/src/client/introspect.c b/src/client/introspect.c index ff5c15e9b..af61564ce 100644 --- a/src/client/introspect.c +++ b/src/client/introspect.c @@ -104,7 +104,102 @@ pinos_context_get_daemon_info (PinosContext *context, } static void -client_fill_info (PinosSourceInfo *info, GDBusProxy *proxy) +client_fill_info (PinosClientInfo *info, GDBusProxy *proxy) +{ + GVariant *variant; + + info->id = proxy; + + if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Name"))) { + info->name = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } else { + info->name = "Unknown"; + } + + info->properties = pinos_properties_from_variant ( + g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Properties")); +} + +static void +client_clear_info (PinosClientInfo *info) +{ + if (info->properties) + pinos_properties_free (info->properties); +} + + +/** + * pinos_context_list_client_info: + * @context: a connected #PinosContext + * @flags: extra #PinosClientInfoFlags + * @cb: a #PinosClientInfoCallback + * @cancelable: a #GCancellable + * @user_data: user data passed to @cb + * + * Call @cb for each client. @cb will be called with NULL when there + * are no more clients to list. + */ +void +pinos_context_list_client_info (PinosContext *context, + PinosClientInfoFlags flags, + PinosClientInfoCallback cb, + GCancellable *cancellable, + gpointer user_data) +{ + GList *walk; + PinosContextPrivate *priv = context->priv; + + g_return_if_fail (PINOS_IS_CONTEXT (context)); + g_return_if_fail (cb != NULL); + + for (walk = priv->clients; walk; walk = g_list_next (walk)) { + GDBusProxy *proxy = walk->data; + PinosClientInfo info; + + client_fill_info (&info, proxy); + cb (context, &info, user_data); + client_clear_info (&info); + } + cb (context, NULL, user_data); +} + +/** + * pinos_context_get_client_info_by_id: + * @context: a connected #PinosContext + * @id: a client id + * @flags: extra #PinosClientInfoFlags + * @cb: a #PinosClientInfoCallback + * @cancelable: a #GCancellable + * @user_data: user data passed to @cb + * + * Call @cb for the client with @id. Then @cb will be called with NULL. + */ +void +pinos_context_get_client_info_by_id (PinosContext *context, + gpointer id, + PinosClientInfoFlags flags, + PinosClientInfoCallback cb, + GCancellable *cancellable, + gpointer user_data) +{ + PinosClientInfo info; + GDBusProxy *proxy; + + g_return_if_fail (PINOS_IS_CONTEXT (context)); + g_return_if_fail (id != NULL); + g_return_if_fail (cb != NULL); + + proxy = G_DBUS_PROXY (id); + + client_fill_info (&info, proxy); + cb (context, &info, user_data); + client_clear_info (&info); + cb (context, NULL, user_data); +} + +static void +source_fill_info (PinosSourceInfo *info, GDBusProxy *proxy) { GVariant *variant; @@ -140,7 +235,7 @@ client_fill_info (PinosSourceInfo *info, GDBusProxy *proxy) } static void -client_clear_info (PinosSourceInfo *info) +source_clear_info (PinosSourceInfo *info) { if (info->properties) pinos_properties_free (info->properties); @@ -176,15 +271,15 @@ pinos_context_list_source_info (PinosContext *context, GDBusProxy *proxy = walk->data; PinosSourceInfo info; - client_fill_info (&info, proxy); + source_fill_info (&info, proxy); cb (context, &info, user_data); - client_clear_info (&info); + source_clear_info (&info); } cb (context, NULL, user_data); } /** - * pinos_context_get_source_info: + * pinos_context_get_source_info_by_id: * @context: a connected #PinosContext * @id: a source id * @flags: extra #PinosSourceInfoFlags @@ -192,8 +287,7 @@ pinos_context_list_source_info (PinosContext *context, * @cancelable: a #GCancellable * @user_data: user data passed to @cb * - * Call @cb for each source. @cb will be called with NULL when there - * are no more sources to list. + * Call @cb for the source with @id. Then @cb will be called with NULL. */ void pinos_context_get_source_info_by_id (PinosContext *context, @@ -212,8 +306,119 @@ pinos_context_get_source_info_by_id (PinosContext *context, proxy = G_DBUS_PROXY (id); - client_fill_info (&info, proxy); + source_fill_info (&info, proxy); cb (context, &info, user_data); - client_clear_info (&info); + source_clear_info (&info); cb (context, NULL, user_data); } + +static void +source_output_fill_info (PinosSourceOutputInfo *info, GDBusProxy *proxy) +{ + GVariant *variant; + + info->id = proxy; + + if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Client"))) { + info->client_path = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } else { + info->client_path = "Unknown"; + } + if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Source"))) { + info->source_path = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } else { + info->source_path = "Unknown"; + } + if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "PossibleFormats"))) { + gsize len; + gchar *formats = g_variant_dup_string (variant, &len); + info->possible_formats = g_bytes_new_take (formats, len + 1); + g_variant_unref (variant); + } else { + info->possible_formats = NULL; + } + info->properties = pinos_properties_from_variant ( + g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Properties")); + +} + +static void +source_output_clear_info (PinosSourceOutputInfo *info) +{ + if (info->possible_formats) + g_bytes_unref (info->possible_formats); + if (info->properties) + pinos_properties_free (info->properties); +} + +/** + * pinos_context_list_source_output_info: + * @context: a connected #PinosContext + * @flags: extra #PinosSourceOutputInfoFlags + * @cb: a #PinosSourceOutputInfoCallback + * @cancelable: a #GCancellable + * @user_data: user data passed to @cb + * + * Call @cb for each source-output. @cb will be called with NULL when there + * are no more outputs to list. + */ +void +pinos_context_list_source_output_info (PinosContext *context, + PinosSourceOutputInfoFlags flags, + PinosSourceOutputInfoCallback cb, + GCancellable *cancellable, + gpointer user_data) +{ + GList *walk; + PinosContextPrivate *priv = context->priv; + + g_return_if_fail (PINOS_IS_CONTEXT (context)); + g_return_if_fail (cb != NULL); + + for (walk = priv->source_outputs; walk; walk = g_list_next (walk)) { + GDBusProxy *proxy = walk->data; + PinosSourceOutputInfo info; + + source_output_fill_info (&info, proxy); + cb (context, &info, user_data); + source_output_clear_info (&info); + } + cb (context, NULL, user_data); +} + +/** + * pinos_context_get_source_output_info_by_id: + * @context: a connected #PinosContext + * @id: a source output id + * @flags: extra #PinosSourceOutputInfoFlags + * @cb: a #PinosSourceOutputInfoCallback + * @cancelable: a #GCancellable + * @user_data: user data passed to @cb + * + * Call @cb for the source output with @id. Then @cb will be called with NULL. + */ +void +pinos_context_get_source_output_info_by_id (PinosContext *context, + gpointer id, + PinosSourceOutputInfoFlags flags, + PinosSourceOutputInfoCallback cb, + GCancellable *cancellable, + gpointer user_data) +{ + PinosSourceOutputInfo info; + GDBusProxy *proxy; + + g_return_if_fail (PINOS_IS_CONTEXT (context)); + g_return_if_fail (id != NULL); + g_return_if_fail (cb != NULL); + + proxy = G_DBUS_PROXY (id); + + source_output_fill_info (&info, proxy); + cb (context, &info, user_data); + source_output_clear_info (&info); + cb (context, NULL, user_data); +} + diff --git a/src/client/introspect.h b/src/client/introspect.h index 44f75764c..3409d5cf3 100644 --- a/src/client/introspect.h +++ b/src/client/introspect.h @@ -29,7 +29,7 @@ G_BEGIN_DECLS /** - * PinosDeamonInfo: + * PinosDaemonInfo: * @id: generic id of the daemon * @user_name: name of the user that started the daemon * @host_name: name of the machine the daemon is running on @@ -63,6 +63,39 @@ void pinos_context_get_daemon_info (PinosContext *context, GCancellable *cancellable, gpointer user_data); +/** + * PinosClientInfo: + * @id: generic id of the client + * @name: name of client + * @properties: extra properties + * + * The client information. Extra information can be added in later + * versions. + */ +typedef struct { + gpointer id; + const char *name; + PinosProperties *properties; +} PinosClientInfo; + +typedef enum { + PINOS_CLIENT_INFO_FLAGS_NONE = 0, +} PinosClientInfoFlags; + +typedef gboolean (*PinosClientInfoCallback) (PinosContext *c, const PinosClientInfo *info, gpointer userdata); + +void pinos_context_list_client_info (PinosContext *context, + PinosClientInfoFlags flags, + PinosClientInfoCallback cb, + GCancellable *cancellable, + gpointer user_data); +void pinos_context_get_client_info_by_id (PinosContext *context, + gpointer id, + PinosClientInfoFlags flags, + PinosClientInfoCallback cb, + GCancellable *cancellable, + gpointer user_data); + /** * PinosSourceState: * @PINOS_SOURCE_STATE_ERROR: the source is in error @@ -131,6 +164,48 @@ void pinos_context_get_source_info_by_id (PinosContext *context, GCancellable *cancellable, gpointer user_data); +/** + * PinosSourceOutputInfo: + * @id: generic id of the output + * @client_path: the owner client + * @source_path: the source path + * @possible_formats: the possible formats + * @properties: the properties of the source + * + * The source information. Extra information can be added in later + * versions. + */ +typedef struct { + gpointer id; + const char *client_path; + const char *source_path; + GBytes *possible_formats; + PinosProperties *properties; +} PinosSourceOutputInfo; + +/** + * PinosSourceOutputInfoFlags: + * @PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE: no flags + * + * Extra flags to pass to pinos_context_get_source_output_info_list. + */ +typedef enum { + PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE = 0, +} PinosSourceOutputInfoFlags; + +typedef gboolean (*PinosSourceOutputInfoCallback) (PinosContext *c, const PinosSourceOutputInfo *info, gpointer userdata); + +void pinos_context_list_source_output_info (PinosContext *context, + PinosSourceOutputInfoFlags flags, + PinosSourceOutputInfoCallback cb, + GCancellable *cancellable, + gpointer user_data); +void pinos_context_get_source_output_info_by_id (PinosContext *context, + gpointer id, + PinosSourceOutputInfoFlags flags, + PinosSourceOutputInfoCallback cb, + GCancellable *cancellable, + gpointer user_data); G_END_DECLS #endif /* __PINOS_INTROSPECT_H__ */ diff --git a/src/client/pinos.c b/src/client/pinos.c index 85be697bc..cb1c560f3 100644 --- a/src/client/pinos.c +++ b/src/client/pinos.c @@ -34,3 +34,62 @@ pinos_init (int *argc, char **argv[]) { gst_init (argc, argv); } + +/** + * pinos_client_name: + * + * Make a new pinos client name that can be used to construct a context. + */ +gchar * +pinos_client_name (void) +{ + const char *c; + + if ((c = g_get_application_name ())) + return g_strdup (c); + else if ((c = g_get_prgname ())) + return g_strdup (c); + else + return g_strdup_printf ("pinos-pid-%lu", (gulong) getpid ()); +} + +void +pinos_fill_context_properties (PinosProperties *properties) +{ + g_return_if_fail (properties != NULL); + + if (!pinos_properties_get (properties, "application.name")) + pinos_properties_set (properties, "application.name", g_get_application_name ()); + + if (!pinos_properties_get (properties, "application.prgname")) + pinos_properties_set (properties, "application.prgname", g_get_prgname ()); + + if (!pinos_properties_get (properties, "application.language")) { + const gchar *str = g_getenv ("LANG"); + if (str) + pinos_properties_set (properties, "application.language", str); + } + if (!pinos_properties_get (properties, "application.process.id")) { + gchar *str = g_strdup_printf ("%lu", (gulong) getpid()); + pinos_properties_set (properties, "application.process.id", str); + g_free (str); + } + if (!pinos_properties_get (properties, "application.process.user")) + pinos_properties_set (properties, "application.process.user", g_get_user_name ()); + + if (!pinos_properties_get (properties, "application.process.host")) + pinos_properties_set (properties, "application.process.host", g_get_host_name ()); + + if (!pinos_properties_get (properties, "application.process.session_id")) { + const gchar *str = g_getenv ("XDG_SESSION_ID"); + if (str) + pinos_properties_set (properties, "application.process.session_id", str); + } +} + +void +pinos_fill_stream_properties (PinosProperties *properties) +{ + g_return_if_fail (properties != NULL); +} + diff --git a/src/client/pinos.h b/src/client/pinos.h index 9bb9a128e..fb186daf3 100644 --- a/src/client/pinos.h +++ b/src/client/pinos.h @@ -35,4 +35,9 @@ void pinos_init (int *argc, char **argv[]); +gchar *pinos_client_name (void); + +void pinos_fill_context_properties (PinosProperties *properties); +void pinos_fill_stream_properties (PinosProperties *properties); + #endif /* __PINOS_H__ */ diff --git a/src/client/private.h b/src/client/private.h index a77486dc0..bcd108034 100644 --- a/src/client/private.h +++ b/src/client/private.h @@ -38,5 +38,7 @@ struct _PinosContextPrivate PinosSubscriptionFlags subscription_mask; PinosSubscribe *subscribe; + GList *clients; GList *sources; + GList *source_outputs; }; diff --git a/src/client/stream.c b/src/client/stream.c index deb4b825f..c9864e780 100644 --- a/src/client/stream.c +++ b/src/client/stream.c @@ -399,6 +399,12 @@ pinos_stream_new (PinosContext *context, g_return_val_if_fail (PINOS_IS_CONTEXT (context), NULL); g_return_val_if_fail (name != NULL, NULL); + if (props == NULL) { + props = pinos_properties_new ("media.name", name, NULL); + } else if (!pinos_properties_get (props, "media.name")) { + pinos_properties_set (props, "media.name", name); + } + return g_object_new (PINOS_TYPE_STREAM, "context", context, "name", name, @@ -532,9 +538,10 @@ do_connect_capture (PinosStream *stream) g_dbus_proxy_call (context->priv->client, "CreateSourceOutput", - g_variant_new ("(ss)", + g_variant_new ("(ss@a{sv})", (priv->source_path ? priv->source_path : ""), - g_bytes_get_data (priv->accepted_formats, NULL)), + g_bytes_get_data (priv->accepted_formats, NULL), + pinos_properties_to_variant (priv->properties)), G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* GCancellable *cancellable */ @@ -595,7 +602,9 @@ do_connect_provide (PinosStream *stream) g_dbus_proxy_call (context->priv->client, "CreateSourceInput", - g_variant_new ("(s)", g_bytes_get_data (priv->possible_formats, NULL)), + g_variant_new ("(s@a{sv})", + g_bytes_get_data (priv->possible_formats, NULL), + pinos_properties_to_variant (priv->properties)), G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* GCancellable *cancellable */ diff --git a/src/dbus/org.pinos.xml b/src/dbus/org.pinos.xml index 26bc2ca25..f6b4d4db8 100644 --- a/src/dbus/org.pinos.xml +++ b/src/dbus/org.pinos.xml @@ -56,6 +56,7 @@ + @@ -121,6 +125,9 @@ against the accepted_formats when creating the source output. --> + + +