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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8086>
This commit is contained in:
Thibault Saunier 2024-12-05 18:52:31 -03:00 committed by GStreamer Marge Bot
parent 18d83192be
commit 5e18499372
6 changed files with 267 additions and 16 deletions

View 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

View 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;
}

View 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

View 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

View 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

View 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++;
}