From 660d9c071ad02785559f0688d08c0756e88df7eb Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Tue, 24 Jan 2012 21:53:14 +0100 Subject: [PATCH] controller: cleanup the control-binding construction This is now bindings firendly as _new is just a classic c convenience and all the work is done in a constructor. As a side effect _new never fails. Fix the tests. --- gst/gstcontrolbinding.c | 125 +++++ gst/gstcontrolbinding.h | 4 + libs/gst/controller/gstcontrolbindingargb.c | 172 ++++-- libs/gst/controller/gstcontrolbindingdirect.c | 504 ++++++++++-------- libs/gst/controller/gstcontrolbindingdirect.h | 7 +- tests/check/gst/gstcontroller.c | 193 ++++--- win32/common/libgstcontroller.def | 4 + 7 files changed, 642 insertions(+), 367 deletions(-) diff --git a/gst/gstcontrolbinding.c b/gst/gstcontrolbinding.c index 2d2e440739..f3aba1dcce 100644 --- a/gst/gstcontrolbinding.c +++ b/gst/gstcontrolbinding.c @@ -42,12 +42,50 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolbinding", 0, \ "dynamic parameter control source attachment"); +static GObject *gst_control_binding_constructor (GType type, + guint n_construct_params, GObjectConstructParam * construct_params); +static void gst_control_binding_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_control_binding_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_control_binding_dispose (GObject * object); +static void gst_control_binding_finalize (GObject * object); + G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstControlBinding, gst_control_binding, GST_TYPE_OBJECT, _do_init); +enum +{ + PROP_0, + PROP_OBJECT, + PROP_NAME, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + static void gst_control_binding_class_init (GstControlBindingClass * klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructor = gst_control_binding_constructor; + gobject_class->set_property = gst_control_binding_set_property; + gobject_class->get_property = gst_control_binding_get_property; + gobject_class->dispose = gst_control_binding_dispose; + gobject_class->finalize = gst_control_binding_finalize; + + properties[PROP_OBJECT] = + g_param_spec_object ("object", "Object", + "The object of the property", GST_TYPE_OBJECT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + properties[PROP_NAME] = + g_param_spec_string ("name", "Name", "The name of the property", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + + g_object_class_install_properties (gobject_class, PROP_LAST, properties); } static void @@ -55,6 +93,93 @@ gst_control_binding_init (GstControlBinding * self) { } +static GObject * +gst_control_binding_constructor (GType type, guint n_construct_params, + GObjectConstructParam * construct_params) +{ + GstControlBinding *self; + GParamSpec *pspec; + + self = GST_CONTROL_BINDING (G_OBJECT_CLASS (gst_control_binding_parent_class) + ->constructor (type, n_construct_params, construct_params)); + + GST_INFO_OBJECT (self->object, "trying to put property '%s' under control", + self->name); + + /* check if the object has a property of that name */ + if ((pspec = + g_object_class_find_property (G_OBJECT_GET_CLASS (self->object), + self->name))) { + GST_DEBUG_OBJECT (self->object, " psec->flags : 0x%08x", pspec->flags); + + /* check if this param is witable && controlable && !construct-only */ + if ((pspec->flags & (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE | + G_PARAM_CONSTRUCT_ONLY)) == + (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE)) { + self->pspec = pspec; + } + } else { + GST_WARNING_OBJECT (self->object, "class '%s' has no property '%s'", + G_OBJECT_TYPE_NAME (self->object), self->name); + } + return (GObject *) self; +} + +static void +gst_control_binding_dispose (GObject * object) +{ + GstControlBinding *self = GST_CONTROL_BINDING (object); + + if (self->object) + gst_object_replace (&self->object, NULL); +} + +static void +gst_control_binding_finalize (GObject * object) +{ + GstControlBinding *self = GST_CONTROL_BINDING (object); + + g_free (self->name); +} + +static void +gst_control_binding_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstControlBinding *self = GST_CONTROL_BINDING (object); + + switch (prop_id) { + case PROP_OBJECT: + self->object = g_value_dup_object (value); + break; + case PROP_NAME: + self->name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_control_binding_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstControlBinding *self = GST_CONTROL_BINDING (object); + + switch (prop_id) { + case PROP_OBJECT: + g_value_set_object (value, self->object); + break; + case PROP_NAME: + g_value_set_string (value, self->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + /* functions */ /** diff --git a/gst/gstcontrolbinding.h b/gst/gstcontrolbinding.h index d4629494d0..78b3661cd4 100644 --- a/gst/gstcontrolbinding.h +++ b/gst/gstcontrolbinding.h @@ -71,6 +71,8 @@ struct _GstControlBinding { GParamSpec *pspec; /* GParamSpec for this property */ /*< private >*/ + GstObject *object; /* GstObject owning the property + * (== parent when bound) */ gboolean disabled; gpointer _gst_reserved[GST_PADDING]; @@ -97,6 +99,8 @@ struct _GstControlBindingClass gpointer _gst_reserved[GST_PADDING]; }; +#define GST_CONTROL_BINDING_PSPEC(cb) (((GstControlBinding *) cb)->pspec) + GType gst_control_binding_get_type (void); /* Functions */ diff --git a/libs/gst/controller/gstcontrolbindingargb.c b/libs/gst/controller/gstcontrolbindingargb.c index 12c576e02f..d8cae986dc 100644 --- a/libs/gst/controller/gstcontrolbindingargb.c +++ b/libs/gst/controller/gstcontrolbindingargb.c @@ -38,6 +38,12 @@ #define GST_CAT_DEFAULT control_binding_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); +static GObject *gst_control_binding_argb_constructor (GType type, + guint n_construct_params, GObjectConstructParam * construct_params); +static void gst_control_binding_argb_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_control_binding_argb_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); static void gst_control_binding_argb_dispose (GObject * object); static void gst_control_binding_argb_finalize (GObject * object); @@ -56,6 +62,20 @@ static gboolean gst_control_binding_argb_get_value_array (GstControlBinding * G_DEFINE_TYPE_WITH_CODE (GstControlBindingARGB, gst_control_binding_argb, GST_TYPE_CONTROL_BINDING, _do_init); +enum +{ + PROP_0, + PROP_CS_A, + PROP_CS_R, + PROP_CS_G, + PROP_CS_B, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +/* vmethods */ + static void gst_control_binding_argb_class_init (GstControlBindingARGBClass * klass) { @@ -63,6 +83,9 @@ gst_control_binding_argb_class_init (GstControlBindingARGBClass * klass) GstControlBindingClass *control_binding_class = GST_CONTROL_BINDING_CLASS (klass); + gobject_class->constructor = gst_control_binding_argb_constructor; + gobject_class->set_property = gst_control_binding_argb_set_property; + gobject_class->get_property = gst_control_binding_argb_get_property; gobject_class->dispose = gst_control_binding_argb_dispose; gobject_class->finalize = gst_control_binding_argb_finalize; @@ -70,6 +93,32 @@ gst_control_binding_argb_class_init (GstControlBindingARGBClass * klass) control_binding_class->get_value = gst_control_binding_argb_get_value; control_binding_class->get_value_array = gst_control_binding_argb_get_value_array; + + properties[PROP_CS_A] = + g_param_spec_object ("control-source-a", "ControlSource A", + "The control source for the alpha color component", + GST_TYPE_CONTROL_SOURCE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + properties[PROP_CS_R] = + g_param_spec_object ("control-source-r", "ControlSource R", + "The control source for the red color component", + GST_TYPE_CONTROL_SOURCE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + properties[PROP_CS_G] = + g_param_spec_object ("control-source-g", "ControlSource G", + "The control source for the green color component", + GST_TYPE_CONTROL_SOURCE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + properties[PROP_CS_B] = + g_param_spec_object ("control-source-b", "ControlSource B", + "The control source for the blue color component", + GST_TYPE_CONTROL_SOURCE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, PROP_LAST, properties); } static void @@ -77,6 +126,80 @@ gst_control_binding_argb_init (GstControlBindingARGB * self) { } +static GObject * +gst_control_binding_argb_constructor (GType type, guint n_construct_params, + GObjectConstructParam * construct_params) +{ + GstControlBindingARGB *self; + + self = + GST_CONTROL_BINDING_ARGB (G_OBJECT_CLASS + (gst_control_binding_argb_parent_class) + ->constructor (type, n_construct_params, construct_params)); + + if (GST_CONTROL_BINDING_PSPEC (self)) { + if (!(G_PARAM_SPEC_VALUE_TYPE (GST_CONTROL_BINDING_PSPEC (self)) == + G_TYPE_UINT)) { + GST_WARNING ("can't bind to paramspec type '%s'", + G_PARAM_SPEC_TYPE_NAME (GST_CONTROL_BINDING_PSPEC (self))); + GST_CONTROL_BINDING_PSPEC (self) = NULL; + } else { + g_value_init (&self->cur_value, G_TYPE_UINT); + } + } + return (GObject *) self; +} + +static void +gst_control_binding_argb_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstControlBindingARGB *self = GST_CONTROL_BINDING_ARGB (object); + + switch (prop_id) { + case PROP_CS_A: + self->cs_a = g_value_get_object (value); + break; + case PROP_CS_R: + self->cs_r = g_value_get_object (value); + break; + case PROP_CS_G: + self->cs_r = g_value_get_object (value); + break; + case PROP_CS_B: + self->cs_g = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_control_binding_argb_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstControlBindingARGB *self = GST_CONTROL_BINDING_ARGB (object); + + switch (prop_id) { + case PROP_CS_A: + g_value_set_object (value, self->cs_a); + break; + case PROP_CS_R: + g_value_set_object (value, self->cs_r); + break; + case PROP_CS_G: + g_value_set_object (value, self->cs_g); + break; + case PROP_CS_B: + g_value_set_object (value, self->cs_b); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void gst_control_binding_argb_dispose (GObject * object) { @@ -109,6 +232,7 @@ gst_control_binding_argb_sync_values (GstControlBinding * _self, gboolean ret = TRUE; g_return_val_if_fail (GST_IS_CONTROL_BINDING_ARGB (self), FALSE); + g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp)); @@ -158,6 +282,7 @@ gst_control_binding_argb_get_value (GstControlBinding * _self, g_return_val_if_fail (GST_IS_CONTROL_BINDING_ARGB (self), NULL); g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL); + g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); /* get current value via control source */ if (self->cs_a) @@ -200,6 +325,7 @@ gst_control_binding_argb_get_value_array (GstControlBinding * _self, g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); g_return_val_if_fail (values, FALSE); + g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); if (self->cs_a) { src_val_a = g_new0 (gdouble, n_values); @@ -250,7 +376,7 @@ gst_control_binding_argb_get_value_array (GstControlBinding * _self, return ret; } -/* mapping function */ +/* functions */ /** * gst_control_binding_argb_new: @@ -271,45 +397,11 @@ gst_control_binding_argb_new (GstObject * object, const gchar * property_name, GstControlSource * cs_a, GstControlSource * cs_r, GstControlSource * cs_g, GstControlSource * cs_b) { - GstControlBindingARGB *self = NULL; - GParamSpec *pspec; - - GST_INFO_OBJECT (object, "trying to put property '%s' under control", - property_name); - - /* check if the object has a property of that name */ - if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), - property_name))) { - GST_DEBUG_OBJECT (object, " psec->flags : 0x%08x", pspec->flags); - - /* check if this param is witable && controlable && !construct-only */ - g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE | - GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) == - (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL); - - g_return_val_if_fail (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_UINT, NULL); - - if ((self = (GstControlBindingARGB *) - g_object_newv (GST_TYPE_CONTROL_BINDING_ARGB, 0, NULL))) { - // move below to construct() - ((GstControlBinding *) self)->pspec = pspec; - ((GstControlBinding *) self)->name = pspec->name; - if (cs_a) - self->cs_a = gst_object_ref (cs_a); - if (cs_r) - self->cs_r = gst_object_ref (cs_r); - if (cs_g) - self->cs_g = gst_object_ref (cs_g); - if (cs_b) - self->cs_b = gst_object_ref (cs_b); - - g_value_init (&self->cur_value, G_TYPE_UINT); - } - } else { - GST_WARNING_OBJECT (object, "class '%s' has no property '%s'", - G_OBJECT_TYPE_NAME (object), property_name); - } - return (GstControlBinding *) self; + return (GstControlBinding *) g_object_new (GST_TYPE_CONTROL_BINDING_ARGB, + "object", object, "name", property_name, + "control-source-a", cs_a, + "control-source-r", cs_r, + "control-source-g", cs_g, "control-source-b", cs_b, NULL); } /* functions */ diff --git a/libs/gst/controller/gstcontrolbindingdirect.c b/libs/gst/controller/gstcontrolbindingdirect.c index 5148193d70..90bef49e35 100644 --- a/libs/gst/controller/gstcontrolbindingdirect.c +++ b/libs/gst/controller/gstcontrolbindingdirect.c @@ -37,6 +37,12 @@ #define GST_CAT_DEFAULT control_binding_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); +static GObject *gst_control_binding_direct_constructor (GType type, + guint n_construct_params, GObjectConstructParam * construct_params); +static void gst_control_binding_direct_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_control_binding_direct_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); static void gst_control_binding_direct_dispose (GObject * object); static void gst_control_binding_direct_finalize (GObject * object); @@ -55,148 +61,17 @@ static gboolean gst_control_binding_direct_get_value_array (GstControlBinding * G_DEFINE_TYPE_WITH_CODE (GstControlBindingDirect, gst_control_binding_direct, GST_TYPE_CONTROL_BINDING, _do_init); -static void -gst_control_binding_direct_class_init (GstControlBindingDirectClass * klass) +enum { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstControlBindingClass *control_binding_class = - GST_CONTROL_BINDING_CLASS (klass); + PROP_0, + PROP_CS, + PROP_LAST +}; - gobject_class->dispose = gst_control_binding_direct_dispose; - gobject_class->finalize = gst_control_binding_direct_finalize; - - control_binding_class->sync_values = gst_control_binding_direct_sync_values; - control_binding_class->get_value = gst_control_binding_direct_get_value; - control_binding_class->get_value_array = - gst_control_binding_direct_get_value_array; -} - -static void -gst_control_binding_direct_init (GstControlBindingDirect * self) -{ -} - -static void -gst_control_binding_direct_dispose (GObject * object) -{ - GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (object); - - if (self->csource) - gst_object_replace ((GstObject **) & self->csource, NULL); -} - -static void -gst_control_binding_direct_finalize (GObject * object) -{ - GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (object); - - g_value_unset (&self->cur_value); -} - -static gboolean -gst_control_binding_direct_sync_values (GstControlBinding * _self, - GstObject * object, GstClockTime timestamp, GstClockTime last_sync) -{ - GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (_self); - gdouble src_val; - gboolean ret; - - g_return_val_if_fail (GST_IS_CONTROL_BINDING_DIRECT (self), FALSE); - - GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT, - _self->name, GST_TIME_ARGS (timestamp)); - - ret = gst_control_source_get_value (self->csource, timestamp, &src_val); - if (G_LIKELY (ret)) { - GST_LOG_OBJECT (object, " new value %lf", src_val); - /* always set the value for first time, but then only if it changed - * this should limit g_object_notify invocations. - * FIXME: can we detect negative playback rates? - */ - if ((timestamp < last_sync) || (src_val != self->last_value)) { - GValue *dst_val = &self->cur_value; - - GST_LOG_OBJECT (object, " mapping %s to value of type %s", _self->name, - G_VALUE_TYPE_NAME (dst_val)); - /* run mapping function to convert gdouble to GValue */ - self->convert (self, src_val, dst_val); - /* we can make this faster - * http://bugzilla.gnome.org/show_bug.cgi?id=536939 - */ - g_object_set_property ((GObject *) object, _self->name, dst_val); - self->last_value = src_val; - } - } else { - GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name); - } - return (ret); -} - -static GValue * -gst_control_binding_direct_get_value (GstControlBinding * _self, - GstClockTime timestamp) -{ - GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (_self); - GValue *dst_val = NULL; - gdouble src_val; - - g_return_val_if_fail (GST_IS_CONTROL_BINDING_DIRECT (self), NULL); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL); - - /* get current value via control source */ - if (gst_control_source_get_value (self->csource, timestamp, &src_val)) { - dst_val = g_new0 (GValue, 1); - g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (_self->pspec)); - self->convert (self, src_val, dst_val); - } else { - GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT, - _self->name, GST_TIME_ARGS (timestamp)); - } - - return dst_val; -} - -static gboolean -gst_control_binding_direct_get_value_array (GstControlBinding * _self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - GValue * values) -{ - GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (_self); - gint i; - gdouble *src_val; - gboolean res = FALSE; - GType type; - GstControlBindingDirectConvert convert; - - g_return_val_if_fail (GST_IS_CONTROL_BINDING_DIRECT (self), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); - g_return_val_if_fail (values, FALSE); - - convert = self->convert; - type = G_PARAM_SPEC_VALUE_TYPE (_self->pspec); - - src_val = g_new0 (gdouble, n_values); - if ((res = gst_control_source_get_value_array (self->csource, timestamp, - interval, n_values, src_val))) { - for (i = 0; i < n_values; i++) { - if (!isnan (src_val[i])) { - g_value_init (&values[i], type); - convert (self, src_val[i], &values[i]); - } else { - GST_LOG ("no control value for property %s at index %d", _self->name, - i); - } - } - } else { - GST_LOG ("failed to get control value for property %s at ts %" - GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp)); - } - g_free (src_val); - return res; -} +static GParamSpec *properties[PROP_LAST]; /* mapping functions */ + #define DEFINE_CONVERT(type,Type,TYPE) \ static void \ convert_to_##type (GstControlBindingDirect *self, gdouble s, GValue *d) \ @@ -238,6 +113,260 @@ convert_to_enum (GstControlBindingDirect * self, gdouble s, GValue * d) g_value_set_enum (d, e->values[v].value); } +/* vmethods */ + +static void +gst_control_binding_direct_class_init (GstControlBindingDirectClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstControlBindingClass *control_binding_class = + GST_CONTROL_BINDING_CLASS (klass); + + gobject_class->constructor = gst_control_binding_direct_constructor; + gobject_class->set_property = gst_control_binding_direct_set_property; + gobject_class->get_property = gst_control_binding_direct_get_property; + gobject_class->dispose = gst_control_binding_direct_dispose; + gobject_class->finalize = gst_control_binding_direct_finalize; + + control_binding_class->sync_values = gst_control_binding_direct_sync_values; + control_binding_class->get_value = gst_control_binding_direct_get_value; + control_binding_class->get_value_array = + gst_control_binding_direct_get_value_array; + + properties[PROP_CS] = + g_param_spec_object ("control-source", "ControlSource", + "The control source", + GST_TYPE_CONTROL_SOURCE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, PROP_LAST, properties); +} + +static void +gst_control_binding_direct_init (GstControlBindingDirect * self) +{ +} + +static GObject * +gst_control_binding_direct_constructor (GType type, guint n_construct_params, + GObjectConstructParam * construct_params) +{ + GstControlBindingDirect *self; + + self = + GST_CONTROL_BINDING_DIRECT (G_OBJECT_CLASS + (gst_control_binding_direct_parent_class) + ->constructor (type, n_construct_params, construct_params)); + + if (GST_CONTROL_BINDING_PSPEC (self)) { + GType type, base; + + base = type = G_PARAM_SPEC_VALUE_TYPE (GST_CONTROL_BINDING_PSPEC (self)); + g_value_init (&self->cur_value, type); + while ((type = g_type_parent (type))) + base = type; + + GST_DEBUG (" using type %s", g_type_name (base)); + + // select mapping function + switch (base) { + case G_TYPE_INT: + self->convert = convert_to_int; + break; + case G_TYPE_UINT: + self->convert = convert_to_uint; + break; + case G_TYPE_LONG: + self->convert = convert_to_long; + break; + case G_TYPE_ULONG: + self->convert = convert_to_ulong; + break; + case G_TYPE_INT64: + self->convert = convert_to_int64; + break; + case G_TYPE_UINT64: + self->convert = convert_to_uint64; + break; + case G_TYPE_FLOAT: + self->convert = convert_to_float; + break; + case G_TYPE_DOUBLE: + self->convert = convert_to_double; + break; + case G_TYPE_BOOLEAN: + self->convert = convert_to_boolean; + break; + case G_TYPE_ENUM: + self->convert = convert_to_enum; + break; + default: + GST_WARNING ("incomplete implementation for paramspec type '%s'", + G_PARAM_SPEC_TYPE_NAME (GST_CONTROL_BINDING_PSPEC (self))); + GST_CONTROL_BINDING_PSPEC (self) = NULL; + break; + } + } + return (GObject *) self; +} + +static void +gst_control_binding_direct_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (object); + + switch (prop_id) { + case PROP_CS: + self->cs = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_control_binding_direct_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (object); + + switch (prop_id) { + case PROP_CS: + g_value_set_object (value, self->cs); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_control_binding_direct_dispose (GObject * object) +{ + GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (object); + + if (self->cs) + gst_object_replace ((GstObject **) & self->cs, NULL); +} + +static void +gst_control_binding_direct_finalize (GObject * object) +{ + GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (object); + + g_value_unset (&self->cur_value); +} + +static gboolean +gst_control_binding_direct_sync_values (GstControlBinding * _self, + GstObject * object, GstClockTime timestamp, GstClockTime last_sync) +{ + GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (_self); + gdouble src_val; + gboolean ret; + + g_return_val_if_fail (GST_IS_CONTROL_BINDING_DIRECT (self), FALSE); + g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); + + GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT, + _self->name, GST_TIME_ARGS (timestamp)); + + ret = gst_control_source_get_value (self->cs, timestamp, &src_val); + if (G_LIKELY (ret)) { + GST_LOG_OBJECT (object, " new value %lf", src_val); + /* always set the value for first time, but then only if it changed + * this should limit g_object_notify invocations. + * FIXME: can we detect negative playback rates? + */ + if ((timestamp < last_sync) || (src_val != self->last_value)) { + GValue *dst_val = &self->cur_value; + + GST_LOG_OBJECT (object, " mapping %s to value of type %s", _self->name, + G_VALUE_TYPE_NAME (dst_val)); + /* run mapping function to convert gdouble to GValue */ + self->convert (self, src_val, dst_val); + /* we can make this faster + * http://bugzilla.gnome.org/show_bug.cgi?id=536939 + */ + g_object_set_property ((GObject *) object, _self->name, dst_val); + self->last_value = src_val; + } + } else { + GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name); + } + return (ret); +} + +static GValue * +gst_control_binding_direct_get_value (GstControlBinding * _self, + GstClockTime timestamp) +{ + GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (_self); + GValue *dst_val = NULL; + gdouble src_val; + + g_return_val_if_fail (GST_IS_CONTROL_BINDING_DIRECT (self), NULL); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL); + g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); + + /* get current value via control source */ + if (gst_control_source_get_value (self->cs, timestamp, &src_val)) { + dst_val = g_new0 (GValue, 1); + g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (_self->pspec)); + self->convert (self, src_val, dst_val); + } else { + GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT, + _self->name, GST_TIME_ARGS (timestamp)); + } + + return dst_val; +} + +static gboolean +gst_control_binding_direct_get_value_array (GstControlBinding * _self, + GstClockTime timestamp, GstClockTime interval, guint n_values, + GValue * values) +{ + GstControlBindingDirect *self = GST_CONTROL_BINDING_DIRECT (_self); + gint i; + gdouble *src_val; + gboolean res = FALSE; + GType type; + GstControlBindingDirectConvert convert; + + g_return_val_if_fail (GST_IS_CONTROL_BINDING_DIRECT (self), FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); + g_return_val_if_fail (values, FALSE); + g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); + + convert = self->convert; + type = G_PARAM_SPEC_VALUE_TYPE (_self->pspec); + + src_val = g_new0 (gdouble, n_values); + if ((res = gst_control_source_get_value_array (self->cs, timestamp, + interval, n_values, src_val))) { + for (i = 0; i < n_values; i++) { + if (!isnan (src_val[i])) { + g_value_init (&values[i], type); + convert (self, src_val[i], &values[i]); + } else { + GST_LOG ("no control value for property %s at index %d", _self->name, + i); + } + } + } else { + GST_LOG ("failed to get control value for property %s at ts %" + GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp)); + } + g_free (src_val); + return res; +} + +/* functions */ + /** * gst_control_binding_direct_new: * @object: the object of the property @@ -251,101 +380,8 @@ convert_to_enum (GstControlBindingDirect * self, gdouble s, GValue * d) */ GstControlBinding * gst_control_binding_direct_new (GstObject * object, const gchar * property_name, - GstControlSource * csource) + GstControlSource * cs) { - GstControlBindingDirect *self = NULL; - GParamSpec *pspec; - - GST_INFO_OBJECT (object, "trying to put property '%s' under control", - property_name); - - /* check if the object has a property of that name */ - if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), - property_name))) { - GST_DEBUG_OBJECT (object, " psec->flags : 0x%08x", pspec->flags); - - /* check if this param is witable && controlable && !construct-only */ - g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE | - GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) == - (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL); - - if ((self = (GstControlBindingDirect *) - g_object_newv (GST_TYPE_CONTROL_BINDING_DIRECT, 0, NULL))) { - GType type = G_PARAM_SPEC_VALUE_TYPE (pspec), base; - - // add pspec as a construction parameter and move below to construct() - ((GstControlBinding *) self)->pspec = pspec; - ((GstControlBinding *) self)->name = pspec->name; - self->csource = gst_object_ref (csource); - - g_value_init (&self->cur_value, type); - - base = type = G_PARAM_SPEC_VALUE_TYPE (pspec); - while ((type = g_type_parent (type))) - base = type; - - GST_DEBUG_OBJECT (object, " using type %s", g_type_name (base)); - - // select mapping function - // FIXME: only select mapping if super class hasn't set any? - switch (base) { - case G_TYPE_INT: - self->convert = convert_to_int; - break; - case G_TYPE_UINT: - self->convert = convert_to_uint; - break; - case G_TYPE_LONG: - self->convert = convert_to_long; - break; - case G_TYPE_ULONG: - self->convert = convert_to_ulong; - break; - case G_TYPE_INT64: - self->convert = convert_to_int64; - break; - case G_TYPE_UINT64: - self->convert = convert_to_uint64; - break; - case G_TYPE_FLOAT: - self->convert = convert_to_float; - break; - case G_TYPE_DOUBLE: - self->convert = convert_to_double; - break; - case G_TYPE_BOOLEAN: - self->convert = convert_to_boolean; - break; - case G_TYPE_ENUM: - self->convert = convert_to_enum; - break; - default: - // FIXME: return NULL? - GST_WARNING ("incomplete implementation for paramspec type '%s'", - G_PARAM_SPEC_TYPE_NAME (pspec)); - break; - } - } - } else { - GST_WARNING_OBJECT (object, "class '%s' has no property '%s'", - G_OBJECT_TYPE_NAME (object), property_name); - } - return (GstControlBinding *) self; -} - -/* functions */ - -/** - * gst_control_binding_direct_get_control_source: - * @self: the control binding - * - * Get the control source. - * - * Returns: the control source. Unref when done with it. - */ -GstControlSource * -gst_control_binding_direct_get_control_source (GstControlBindingDirect * self) -{ - g_return_val_if_fail (GST_IS_CONTROL_BINDING_DIRECT (self), NULL); - return g_object_ref (self->csource); + return (GstControlBinding *) g_object_new (GST_TYPE_CONTROL_BINDING_DIRECT, + "object", object, "name", property_name, "control-source", cs, NULL); } diff --git a/libs/gst/controller/gstcontrolbindingdirect.h b/libs/gst/controller/gstcontrolbindingdirect.h index 09926a6fbe..cad96e183b 100644 --- a/libs/gst/controller/gstcontrolbindingdirect.h +++ b/libs/gst/controller/gstcontrolbindingdirect.h @@ -67,7 +67,7 @@ struct _GstControlBindingDirect { GstControlBinding parent; /*< private >*/ - GstControlSource *csource; /* GstControlSource for this property */ + GstControlSource *cs; /* GstControlSource for this property */ GValue cur_value; gdouble last_value; @@ -97,10 +97,7 @@ GType gst_control_binding_direct_get_type (void); /* Functions */ GstControlBinding * gst_control_binding_direct_new (GstObject * object, const gchar * property_name, - GstControlSource * csource); - -GstControlSource * gst_control_binding_direct_get_control_source (GstControlBindingDirect * self); - + GstControlSource * cs); G_END_DECLS #endif /* __GST_CONTROL_BINDING_DIRECT_H__ */ diff --git a/tests/check/gst/gstcontroller.c b/tests/check/gst/gstcontroller.c index 79fe2d9854..c25c92bd8a 100644 --- a/tests/check/gst/gstcontroller.c +++ b/tests/check/gst/gstcontroller.c @@ -261,7 +261,6 @@ gst_test_control_source_get_value_array (GstTestControlSource * self, return TRUE; } - static void gst_test_control_source_init (GstTestControlSource * self) { @@ -300,7 +299,78 @@ gst_test_control_source_get_type (void) return test_countrol_source_type; } +/* test control binding */ +#define GST_TYPE_TEST_CONTROL_BINDING (gst_test_control_binding_get_type ()) +#define GST_TEST_CONTROL_BINDING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TEST_CONTROL_BINDING, GstTestControlBinding)) +#define GST_TEST_CONTROL_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TEST_CONTROL_BINDING, GstTestControlBindingClass)) +#define GST_IS_TEST_CONTROL_BINDING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TEST_CONTROL_BINDING)) +#define GST_IS_TEST_CONTROL_BINDING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TEST_CONTROL_BINDING)) +#define GST_TEST_CONTROL_BINDING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TEST_CONTROL_BINDING, GstTestControlBindingClass)) + +typedef struct _GstTestControlBinding GstTestControlBinding; +typedef struct _GstTestControlBindingClass GstTestControlBindingClass; + +struct _GstTestControlBinding +{ + GstControlBinding parent; + + GstControlSource *cs; +}; +struct _GstTestControlBindingClass +{ + GstControlBindingClass parent_class; +}; + +static GType gst_test_control_binding_get_type (void); + +static GstControlBinding * +gst_test_control_binding_new (GstObject * object, const gchar * property_name, + GstControlSource * cs) +{ + GstTestControlBinding *self; + self = (GstTestControlBinding *) g_object_new (GST_TYPE_TEST_CONTROL_BINDING, + "object", object, "name", property_name, NULL); + + self->cs = gst_object_ref (cs); + + return (GstControlBinding *) self; +} + +static GstControlSource * +gst_test_control_binding_get_control_source (GstTestControlBinding * self) +{ + g_return_val_if_fail (GST_IS_TEST_CONTROL_BINDING (self), NULL); + + return self->cs ? gst_object_ref (self->cs) : NULL; +} + +static GType +gst_test_control_binding_get_type (void) +{ + static volatile gsize test_countrol_source_type = 0; + + if (g_once_init_enter (&test_countrol_source_type)) { + GType type; + static const GTypeInfo info = { + (guint16) sizeof (GstTestControlBindingClass), + NULL, // base_init + NULL, // base_finalize + NULL, // class_init + NULL, // class_finalize + NULL, // class_data + (guint16) sizeof (GstTestControlBinding), + 0, // n_preallocs + NULL, // instance_init + NULL // value_table + }; + type = + g_type_register_static (GST_TYPE_CONTROL_BINDING, + "GstTestControlBinding", &info, 0); + g_once_init_leave (&test_countrol_source_type, type); + } + return test_countrol_source_type; +} static void @@ -324,14 +394,15 @@ GST_START_TEST (controller_new_fail1) GstTestControlSource *cs; GstControlBinding *cb; - elem = gst_element_factory_make ("fakesrc", NULL); + elem = gst_element_factory_make ("testobj", NULL); cs = gst_test_control_source_new (); /* that property should not exist */ - cb = gst_control_binding_direct_new (GST_OBJECT (elem), "_schrompf_", + cb = gst_test_control_binding_new (GST_OBJECT (elem), "_schrompf_", GST_CONTROL_SOURCE (cs)); - fail_unless (cb == NULL, NULL); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb) == NULL, NULL); + gst_object_unref (cb); gst_object_unref (cs); gst_object_unref (elem); } @@ -349,10 +420,11 @@ GST_START_TEST (controller_new_fail2) cs = gst_test_control_source_new (); /* that property should exist and but is readonly */ - ASSERT_CRITICAL (cb = gst_control_binding_direct_new (GST_OBJECT (elem), - "readonly", GST_CONTROL_SOURCE (cs))); - fail_unless (cb == NULL, NULL); + cb = gst_test_control_binding_new (GST_OBJECT (elem), "readonly", + GST_CONTROL_SOURCE (cs)); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb) == NULL, NULL); + gst_object_unref (cb); gst_object_unref (cs); gst_object_unref (elem); } @@ -370,10 +442,11 @@ GST_START_TEST (controller_new_fail3) cs = gst_test_control_source_new (); /* that property should exist and but is not controlable */ - ASSERT_CRITICAL (cb = gst_control_binding_direct_new (GST_OBJECT (elem), - "static", GST_CONTROL_SOURCE (cs))); - fail_unless (cb == NULL, NULL); + cb = gst_test_control_binding_new (GST_OBJECT (elem), "static", + GST_CONTROL_SOURCE (cs)); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb) == NULL, NULL); + gst_object_unref (cb); gst_object_unref (cs); gst_object_unref (elem); } @@ -391,11 +464,11 @@ GST_START_TEST (controller_new_fail4) cs = gst_test_control_source_new (); /* that property should exist and but is construct-only */ - ASSERT_CRITICAL (cb = - gst_control_binding_direct_new (GST_OBJECT (elem), "construct-only", - GST_CONTROL_SOURCE (cs))); - fail_unless (cb == NULL, NULL); + cb = gst_test_control_binding_new (GST_OBJECT (elem), "construct-only", + GST_CONTROL_SOURCE (cs)); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb) == NULL, NULL); + gst_object_unref (cb); gst_object_unref (cs); gst_object_unref (elem); } @@ -414,9 +487,9 @@ GST_START_TEST (controller_new_okay1) cs = gst_test_control_source_new (); /* that property should exist and should be controllable */ - cb = gst_control_binding_direct_new (GST_OBJECT (elem), "int", + cb = gst_test_control_binding_new (GST_OBJECT (elem), "int", GST_CONTROL_SOURCE (cs)); - fail_unless (cb != NULL, NULL); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb) != NULL, NULL); gst_object_unref (cb); gst_object_unref (cs); @@ -437,13 +510,13 @@ GST_START_TEST (controller_new_okay2) cs2 = gst_test_control_source_new (); /* these properties should exist and should be controllable */ - cb1 = gst_control_binding_direct_new (GST_OBJECT (elem), "int", + cb1 = gst_test_control_binding_new (GST_OBJECT (elem), "int", GST_CONTROL_SOURCE (cs1)); - fail_unless (cb1 != NULL, NULL); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb1) != NULL, NULL); - cb2 = gst_control_binding_direct_new (GST_OBJECT (elem), "boolean", + cb2 = gst_test_control_binding_new (GST_OBJECT (elem), "boolean", GST_CONTROL_SOURCE (cs2)); - fail_unless (cb2 != NULL, NULL); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb2) != NULL, NULL); gst_object_unref (cb1); gst_object_unref (cb2); @@ -466,9 +539,9 @@ GST_START_TEST (controller_param_twice) cs = gst_test_control_source_new (); /* that property should exist and should be controllable */ - cb = gst_control_binding_direct_new (GST_OBJECT (elem), "int", + cb = gst_test_control_binding_new (GST_OBJECT (elem), "int", GST_CONTROL_SOURCE (cs)); - fail_unless (cb != NULL, NULL); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb) != NULL, NULL); cb = gst_object_ref (cb); res = gst_object_add_control_binding (GST_OBJECT (elem), cb); @@ -527,15 +600,17 @@ GST_START_TEST (controller_controlsource_refcounts) fail_unless_equals_int (G_OBJECT (cs)->ref_count, 1); - cb = gst_control_binding_direct_new (GST_OBJECT (elem), "int", cs); - fail_unless (cb != NULL, NULL); + cb = gst_test_control_binding_new (GST_OBJECT (elem), "int", cs); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb) != NULL, NULL); fail_unless_equals_int (G_OBJECT (cs)->ref_count, 2); fail_unless (gst_object_add_control_binding (GST_OBJECT (elem), cb)); test_cb = gst_object_get_control_binding (GST_OBJECT (elem), "int"); fail_unless (test_cb != NULL, NULL); - test_cs = gst_control_binding_get_control_source (cb); + test_cs = + gst_test_control_binding_get_control_source (GST_TEST_CONTROL_BINDING + (test_cb)); fail_unless (test_cs != NULL, NULL); fail_unless (test_cs == cs); fail_unless_equals_int (G_OBJECT (cs)->ref_count, 3); @@ -548,7 +623,7 @@ GST_START_TEST (controller_controlsource_refcounts) GST_END_TEST; -/* tests if we can bnd a control source twice */ +/* tests if we can bind a control source twice */ GST_START_TEST (controller_bind_twice) { GstElement *elem; @@ -560,10 +635,10 @@ GST_START_TEST (controller_bind_twice) cs = (GstControlSource *) gst_test_control_source_new (); fail_unless (cs != NULL, NULL); - cb1 = gst_control_binding_direct_new (GST_OBJECT (elem), "int", cs); - fail_unless (cb1 != NULL, NULL); - cb2 = gst_control_binding_direct_new (GST_OBJECT (elem), "double", cs); - fail_unless (cb2 != NULL, NULL); + cb1 = gst_test_control_binding_new (GST_OBJECT (elem), "int", cs); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb1) != NULL, NULL); + cb2 = gst_test_control_binding_new (GST_OBJECT (elem), "double", cs); + fail_unless (GST_CONTROL_BINDING_PSPEC (cb2) != NULL, NULL); gst_object_unref (cb1); gst_object_unref (cb2); @@ -573,62 +648,6 @@ GST_START_TEST (controller_bind_twice) GST_END_TEST; -/* tests synching a value */ -GST_START_TEST (controller_sync1) -{ - GstElement *elem; - GstTestControlSource *csource; - - elem = gst_element_factory_make ("testobj", NULL); - - csource = gst_test_control_source_new (); - fail_unless (csource != NULL, NULL); - - fail_unless (gst_object_add_control_binding (GST_OBJECT (elem), - gst_control_binding_direct_new (GST_OBJECT (elem), "int", - (GstControlSource *) csource))); - - csource->value = 0.5; - fail_unless (gst_object_sync_values (GST_OBJECT (elem), 0LL)); - fail_unless_equals_int (GST_TEST_OBJ (elem)->val_int, 50); - - gst_object_unref (csource); - - gst_object_unref (elem); -} - -GST_END_TEST; - -/* tests synching a value */ -GST_START_TEST (controller_sync2) -{ - GstElement *elem; - GstTestControlSource *csource; - - elem = gst_element_factory_make ("testobj", NULL); - - csource = gst_test_control_source_new (); - fail_unless (csource != NULL, NULL); - - fail_unless (gst_object_add_control_binding (GST_OBJECT (elem), - gst_control_binding_direct_new (GST_OBJECT (elem), "int", - (GstControlSource *) csource))); - fail_unless (gst_object_add_control_binding (GST_OBJECT (elem), - gst_control_binding_direct_new (GST_OBJECT (elem), "double", - (GstControlSource *) csource))); - - csource->value = 0.5; - fail_unless (gst_object_sync_values (GST_OBJECT (elem), 0LL)); - fail_unless_equals_int (GST_TEST_OBJ (elem)->val_int, 50); - fail_unless_equals_float (GST_TEST_OBJ (elem)->val_double, 50.0); - - gst_object_unref (csource); - - gst_object_unref (elem); -} - -GST_END_TEST; - static Suite * gst_controller_suite (void) @@ -648,8 +667,6 @@ gst_controller_suite (void) tcase_add_test (tc, controller_any_gobject); tcase_add_test (tc, controller_controlsource_refcounts); tcase_add_test (tc, controller_bind_twice); - tcase_add_test (tc, controller_sync1); - tcase_add_test (tc, controller_sync2); return s; } diff --git a/win32/common/libgstcontroller.def b/win32/common/libgstcontroller.def index bde16b90d7..e1eaaad35c 100644 --- a/win32/common/libgstcontroller.def +++ b/win32/common/libgstcontroller.def @@ -1,4 +1,8 @@ EXPORTS + gst_control_binding_argb_get_type + gst_control_binding_argb_new + gst_control_binding_direct_get_type + gst_control_binding_direct_new gst_interpolation_control_source_get_type gst_interpolation_control_source_new gst_interpolation_mode_get_type