From 5e184993726cc06eb19e0c27889ae7dfb2994155 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 5 Dec 2024 18:52:31 -0300 Subject: [PATCH] tracers: Simplify params handling using GstStructure and object properties Instead of having each tracer implement its own parameter parsing, centralize the handling in the tracer subsystem using GstStructure. This simplifies tracer implementations and provides a consistent way to handle properties. It also allows for much better documentation by forcing tracer object to expose properties Part-of: --- girs/Gst-1.0.gir | 36 ++++ subprojects/gstreamer/gst/gsttracer.c | 57 +++++- subprojects/gstreamer/gst/gsttracer.h | 6 + subprojects/gstreamer/gst/gsttracerfactory.c | 1 + subprojects/gstreamer/gst/gsttracerfactory.h | 1 + subprojects/gstreamer/gst/gsttracerutils.c | 182 +++++++++++++++++-- 6 files changed, 267 insertions(+), 16 deletions(-) diff --git a/girs/Gst-1.0.gir b/girs/Gst-1.0.gir index d676d5baa2..d149589b2b 100644 --- a/girs/Gst-1.0.gir +++ b/girs/Gst-1.0.gir @@ -49595,6 +49595,42 @@ contextual data, which they must not modify. + + Sets whether the tracer should use structure parameters for configuration. +This function configures how parameters should be passed when instantiating +the tracer. + +This is typically called in the tracer's class initialization function to +indicate its parameter handling preference. + + + + + + + + + + %TRUE to use structure parameters, %FALSE otherwise + + + + + + If set, the tracer subsystem will consider parameters passed to the +`GST_TRACERS` environment variable as a #GstStructure and use its +fields as properties to instanciate the tracer. + + + %TRUE if the tracer uses structure parameters, %FALSE otherwise + + + + + + + + Use gst_tracer_factory_get_list() to get a list of tracer factories known to diff --git a/subprojects/gstreamer/gst/gsttracer.c b/subprojects/gstreamer/gst/gsttracer.c index 1849142745..e69561cac9 100644 --- a/subprojects/gstreamer/gst/gsttracer.c +++ b/subprojects/gstreamer/gst/gsttracer.c @@ -52,6 +52,11 @@ enum static GParamSpec *properties[PROP_LAST]; +typedef struct +{ + gboolean use_structure_params; +} GstTracerClassPrivate; + static void gst_tracer_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_tracer_get_property (GObject * object, guint prop_id, @@ -62,8 +67,13 @@ struct _GstTracerPrivate gchar *params; }; +#define _do_init \ + g_type_add_class_private (g_define_type_id, \ + sizeof (GstTracerClassPrivate)); + #define gst_tracer_parent_class parent_class -G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstTracer, gst_tracer, GST_TYPE_OBJECT); +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstTracer, gst_tracer, GST_TYPE_OBJECT, + G_ADD_PRIVATE (GstTracer) _do_init); static void gst_tracer_dispose (GObject * object) @@ -192,3 +202,48 @@ gst_tracer_register (GstPlugin * plugin, const gchar * name, GType type) return TRUE; } + +/** + * gst_tracer_class_uses_structure_params: + * @klass: the #GstTracerClass to to check + * + * If set, the tracer subsystem will consider parameters passed to the + * `GST_TRACERS` environment variable as a #GstStructure and use its + * fields as properties to instanciate the tracer. + * + * Returns: %TRUE if the tracer uses structure parameters, %FALSE otherwise + * + * Since: 1.26 + */ +gboolean +gst_tracer_class_uses_structure_params (GstTracerClass * klass) +{ + g_return_val_if_fail (GST_IS_TRACER_CLASS (klass), FALSE); + + return G_TYPE_CLASS_GET_PRIVATE (klass, GST_TYPE_TRACER, + GstTracerClassPrivate)->use_structure_params; +} + +/** + * gst_tracer_class_set_use_structure_params: + * @klass: the #GstTracerFactoryClass to mark as using structure parameters + * @use_structure_params: %TRUE to use structure parameters, %FALSE otherwise + * + * Sets whether the tracer should use structure parameters for configuration. + * This function configures how parameters should be passed when instantiating + * the tracer. + * + * This is typically called in the tracer's class initialization function to + * indicate its parameter handling preference. + * + * Since: 1.26 + */ +void +gst_tracer_class_set_use_structure_params (GstTracerClass * klass, + gboolean use_structure_params) +{ + g_return_if_fail (GST_IS_TRACER_CLASS (klass)); + + G_TYPE_CLASS_GET_PRIVATE (klass, GST_TYPE_TRACER, + GstTracerClassPrivate)->use_structure_params = use_structure_params; +} diff --git a/subprojects/gstreamer/gst/gsttracer.h b/subprojects/gstreamer/gst/gsttracer.h index 6560296af1..c1a597aefb 100644 --- a/subprojects/gstreamer/gst/gsttracer.h +++ b/subprojects/gstreamer/gst/gsttracer.h @@ -75,6 +75,12 @@ gboolean gst_tracer_register (GstPlugin * plugin, const gchar * name, GType type GST_API GList* gst_tracing_get_active_tracers (void); +GST_API +gboolean gst_tracer_class_uses_structure_params (GstTracerClass *tracer_class); +GST_API +void gst_tracer_class_set_use_structure_params (GstTracerClass *tracer_class, + gboolean use_structure_params); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstTracer, gst_object_unref) G_END_DECLS diff --git a/subprojects/gstreamer/gst/gsttracerfactory.c b/subprojects/gstreamer/gst/gsttracerfactory.c index 3942acebb8..b0efcc2ce4 100644 --- a/subprojects/gstreamer/gst/gsttracerfactory.c +++ b/subprojects/gstreamer/gst/gsttracerfactory.c @@ -32,6 +32,7 @@ #include "gstinfo.h" #include "gsttracerfactory.h" #include "gstregistry.h" +#include "gsttracer.h" GST_DEBUG_CATEGORY (tracer_debug); #define GST_CAT_DEFAULT tracer_debug diff --git a/subprojects/gstreamer/gst/gsttracerfactory.h b/subprojects/gstreamer/gst/gsttracerfactory.h index ff88a4a605..a4bd24bcaa 100644 --- a/subprojects/gstreamer/gst/gsttracerfactory.h +++ b/subprojects/gstreamer/gst/gsttracerfactory.h @@ -57,6 +57,7 @@ GList * gst_tracer_factory_get_list (void); GST_API GType gst_tracer_factory_get_tracer_type (GstTracerFactory * factory); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstTracerFactory, gst_object_unref) G_END_DECLS diff --git a/subprojects/gstreamer/gst/gsttracerutils.c b/subprojects/gstreamer/gst/gsttracerutils.c index eccf6602a3..a443a92fd1 100644 --- a/subprojects/gstreamer/gst/gsttracerutils.c +++ b/subprojects/gstreamer/gst/gsttracerutils.c @@ -34,6 +34,7 @@ #include "gst_private.h" #include "gsttracer.h" #include "gsttracerfactory.h" +#include "gstvalue.h" #include "gsttracerutils.h" #ifndef GST_DISABLE_GST_TRACER_HOOKS @@ -66,6 +67,169 @@ GQuark _priv_gst_tracer_quark_table[GST_TRACER_QUARK_MAX]; gboolean _priv_tracer_enabled = FALSE; GHashTable *_priv_tracers = NULL; +static gchar * +list_available_tracer_properties (GObjectClass * class) +{ + GParamSpec **properties; + guint n_properties; + GString *props_str; + guint i; + + props_str = g_string_new (NULL); + properties = g_object_class_list_properties (class, &n_properties); + + if (n_properties == 0) { + g_string_append (props_str, "No properties available"); + g_free (properties); + return g_string_free (props_str, FALSE); + } + + g_string_append (props_str, "Available properties:"); + + for (i = 0; i < n_properties; i++) { + GParamSpec *prop = properties[i]; + + if (!((prop->flags & G_PARAM_CONSTRUCT) + || (prop->flags & G_PARAM_CONSTRUCT_ONLY)) + || !(prop->flags & G_PARAM_WRITABLE)) + continue; + + if (!g_strcmp0 (g_param_spec_get_name (prop), "parent")) + continue; + if (!g_strcmp0 (g_param_spec_get_name (prop), "params")) + continue; + + const gchar *type_name = G_PARAM_SPEC_TYPE_NAME (prop); + GValue default_value = G_VALUE_INIT; + + /* Get default value if possible */ + g_value_init (&default_value, prop->value_type); + g_param_value_set_default (prop, &default_value); + gchar *default_str = g_strdup_value_contents (&default_value); + + g_string_append_printf (props_str, + "\n '%s' (%s) (Default: %s): %s", + g_param_spec_get_name (prop), + type_name, + default_str, + g_param_spec_get_blurb (prop) ? g_param_spec_get_blurb (prop) : + "(no description available)"); + + g_free (default_str); + g_value_unset (&default_value); + } + + g_free (properties); + return g_string_free (props_str, FALSE); +} + +static void +gst_tracer_utils_create_tracer (GstTracerFactory * factory, const gchar * name, + const gchar * params) +{ + gchar *available_props = NULL; + GObjectClass *gobject_class = g_type_class_ref (factory->type); + GstTracer *tracer; + const gchar **names = NULL; + GValue *values = NULL; + gint n_properties = 1; + + if (gst_tracer_class_uses_structure_params (GST_TRACER_CLASS (gobject_class))) { + GST_DEBUG ("Use structure parameters for %s", params); + + if (!params) { + n_properties = 0; + goto create; + } + + gchar *struct_str = g_strdup_printf ("%s,%s", name, params); + GstStructure *structure = gst_structure_from_string (struct_str, NULL); + g_free (struct_str); + + if (!structure) { + available_props = list_available_tracer_properties (gobject_class); + g_warning + ("Can't instantiate `%s` tracer: invalid parameters '%s'\n %s\n", + name, params, available_props); + goto done; + } + n_properties = gst_structure_n_fields (structure); + + names = g_new0 (const gchar *, n_properties); + values = g_new0 (GValue, n_properties); + for (gint i = 0; i < n_properties; i++) { + const gchar *field_name = gst_structure_nth_field_name (structure, i); + const GValue *field_value = + gst_structure_get_value (structure, field_name); + GParamSpec *pspec = + g_object_class_find_property (gobject_class, field_name); + + if (!pspec) { + available_props = list_available_tracer_properties (gobject_class); + g_warning + ("Can't instantiate `%s` tracer: property '%s' not found\n %s\n", + name, field_name, available_props); + goto done; + } + + if (G_VALUE_TYPE (field_value) == pspec->value_type) { + names[i] = field_name; + g_value_init (&values[i], G_VALUE_TYPE (field_value)); + g_value_copy (field_value, &values[i]); + } else if (G_VALUE_TYPE (field_value) == G_TYPE_STRING) { + names[i] = field_name; + g_value_init (&values[i], G_PARAM_SPEC_VALUE_TYPE (pspec)); + if (!gst_value_deserialize_with_pspec (&values[i], + g_value_get_string (field_value), pspec)) { + available_props = list_available_tracer_properties (gobject_class); + g_warning + ("Can't instantiate `%s` tracer: invalid property '%s' value: '%s'\n %s\n", + name, field_name, g_value_get_string (field_value), + available_props); + goto done; + } + } else { + available_props = list_available_tracer_properties (gobject_class); + g_warning + ("Can't instantiate `%s` tracer: property '%s' type mismatch, expected %s, got %s\n %s\n", + name, field_name, g_type_name (pspec->value_type), + g_type_name (G_VALUE_TYPE (field_value)), available_props); + goto done; + } + } + + g_type_class_unref (gobject_class); + } else { + names = g_new0 (const gchar *, n_properties); + names[0] = (const gchar *) "params"; + values = g_new0 (GValue, 1); + g_value_init (&values[0], G_TYPE_STRING); + g_value_set_string (&values[0], name); + } + GST_INFO_OBJECT (factory, "creating tracer: type-id=%u", + (guint) factory->type); + +create: + tracer = + GST_TRACER (g_object_new_with_properties (factory->type, + n_properties, names, values)); + + for (gint j = 0; j < n_properties; j++) { + g_value_unset (&values[j]); + } + g_free (names); + g_free (values); + + /* Clear floating flag */ + gst_object_ref_sink (tracer); + + /* tracers register them self to the hooks */ + gst_object_unref (tracer); + +done: + g_free (available_props); +} + /* Initialize the tracing system */ void _priv_gst_tracing_init (void) @@ -128,24 +292,12 @@ _priv_gst_tracing_init (void) if ((feature = gst_registry_lookup_feature (registry, t[i]))) { factory = GST_TRACER_FACTORY (gst_plugin_feature_load (feature)); if (factory) { - GstTracer *tracer; - - GST_INFO_OBJECT (factory, "creating tracer: type-id=%u", - (guint) factory->type); - - tracer = g_object_new (factory->type, "params", params, NULL); - - /* Clear floating flag */ - gst_object_ref_sink (tracer); - - /* tracers register them self to the hooks */ - gst_object_unref (tracer); + gst_tracer_utils_create_tracer (factory, t[i], params); } else { - GST_WARNING_OBJECT (feature, - "loading plugin containing feature %s failed!", t[i]); + g_warning ("loading plugin containing feature %s failed!", t[i]); } } else { - GST_WARNING ("no tracer named '%s'", t[i]); + g_warning ("no tracer named '%s'", t[i]); } i++; }