tracers: Simplify params handling using GstStructure and object properties
authorThibault Saunier <tsaunier@igalia.com>
Thu, 5 Dec 2024 21:52:31 +0000 (18:52 -0300)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 10 Dec 2024 09:35:35 +0000 (09:35 +0000)
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8086>

girs/Gst-1.0.gir
subprojects/gstreamer/gst/gsttracer.c
subprojects/gstreamer/gst/gsttracer.h
subprojects/gstreamer/gst/gsttracerfactory.c
subprojects/gstreamer/gst/gsttracerfactory.h
subprojects/gstreamer/gst/gsttracerutils.c

index d676d5baa2ac18556e9ccb7fb02fd17437bc7322..d149589b2b110427e6f5a9aefb07634c5a8ec104 100644 (file)
@@ -49595,6 +49595,42 @@ contextual data, which they must not modify.</doc>
           <type name="gpointer" c:type="gpointer"/>
         </array>
       </field>
+      <method name="set_use_structure_params" c:identifier="gst_tracer_class_set_use_structure_params" version="1.26">
+        <doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gsttracer.c">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.</doc>
+        <source-position filename="../subprojects/gstreamer/gst/gsttracer.h"/>
+        <return-value transfer-ownership="none">
+          <type name="none" c:type="void"/>
+        </return-value>
+        <parameters>
+          <instance-parameter name="tracer_class" transfer-ownership="none">
+            <type name="TracerClass" c:type="GstTracerClass*"/>
+          </instance-parameter>
+          <parameter name="use_structure_params" transfer-ownership="none">
+            <doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gsttracer.c">%TRUE to use structure parameters, %FALSE otherwise</doc>
+            <type name="gboolean" c:type="gboolean"/>
+          </parameter>
+        </parameters>
+      </method>
+      <method name="uses_structure_params" c:identifier="gst_tracer_class_uses_structure_params" version="1.26">
+        <doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gsttracer.c">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.</doc>
+        <source-position filename="../subprojects/gstreamer/gst/gsttracer.h"/>
+        <return-value transfer-ownership="none">
+          <doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gsttracer.c">%TRUE if the tracer uses structure parameters, %FALSE otherwise</doc>
+          <type name="gboolean" c:type="gboolean"/>
+        </return-value>
+        <parameters>
+          <instance-parameter name="tracer_class" transfer-ownership="none">
+            <type name="TracerClass" c:type="GstTracerClass*"/>
+          </instance-parameter>
+        </parameters>
+      </method>
     </record>
     <class name="TracerFactory" c:symbol-prefix="tracer_factory" c:type="GstTracerFactory" version="1.8" parent="PluginFeature" glib:type-name="GstTracerFactory" glib:get-type="gst_tracer_factory_get_type" glib:type-struct="TracerFactoryClass">
       <doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gsttracerfactory.c">Use gst_tracer_factory_get_list() to get a list of tracer factories known to
index 1849142745754a3ef7ed29288e7ece704e392779..e69561cac9e31290507fad14950e56ca2177c110 100644 (file)
@@ -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;
+}
index 6560296af1fac760700f1497dc6d125ee6a9a2b1..c1a597aefb97a40facaad41ce7b33341d6198a6c 100644 (file)
@@ -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
index 3942acebb85964e456a5b8e7c8043fc713a9dd72..b0efcc2ce459c54ca9de390fb19f98a69cb9e4a1 100644 (file)
@@ -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
index ff88a4a605c657242ae0333360e1d5a7836ea963..a4bd24bcaa4a9166e86a4f1d2205ee626f94d03b 100644 (file)
@@ -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
index eccf6602a3811051e2dcb1266ce238da9866df63..a443a92fd1e4916404145edf8fbcd8d98b534d56 100644 (file)
@@ -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++;
     }