diff --git a/ChangeLog b/ChangeLog index a602e91beb..ac4709dd28 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,81 @@ +2007-07-06 Sebastian Dröge + + Reviewed by: Stefan Kost + + * libs/gst/controller/Makefile.am: + * libs/gst/controller/gstcontroller.c: + (gst_controlled_property_add_interpolation_control_source), + (gst_controlled_property_new), (gst_controlled_property_free), + (gst_controller_find_controlled_property), + (gst_controller_new_valist), (gst_controller_new_list), + (gst_controller_new), (gst_controller_remove_properties_valist), + (gst_controller_remove_properties_list), + (gst_controller_remove_properties), + (gst_controller_set_property_disabled), + (gst_controller_set_disabled), (gst_controller_set_control_source), + (gst_controller_get_control_source), (gst_controller_get), + (gst_controller_sync_values), (gst_controller_get_value_array), + (_gst_controller_dispose), (gst_controller_get_type), + (gst_controlled_property_set_interpolation_mode), + (gst_controller_set), (gst_controller_set_from_list), + (gst_controller_unset), (gst_controller_unset_all), + (gst_controller_get_all), (gst_controller_set_interpolation_mode): + * libs/gst/controller/gstcontroller.h: + * libs/gst/controller/gstcontrollerprivate.h: + * libs/gst/controller/gstcontrolsource.c: + (gst_control_source_class_init), (gst_control_source_init), + (gst_control_source_get_value), + (gst_control_source_get_value_array), (gst_control_source_bind): + * libs/gst/controller/gstcontrolsource.h: + * libs/gst/controller/gsthelper.c: (gst_object_set_control_source), + (gst_object_get_control_source): + * libs/gst/controller/gstinterpolation.c: + (gst_interpolation_control_source_find_control_point_node), + (gst_interpolation_control_source_get_first_value), + (_interpolate_none_get), (interpolate_none_get), + (interpolate_none_get_boolean_value_array), + (interpolate_none_get_enum_value_array), + (interpolate_none_get_string_value_array), + (_interpolate_trigger_get), (interpolate_trigger_get), + (interpolate_trigger_get_boolean_value_array), + (interpolate_trigger_get_enum_value_array), + (interpolate_trigger_get_string_value_array): + * libs/gst/controller/gstinterpolationcontrolsource.c: + (gst_control_point_free), (gst_interpolation_control_source_reset), + (gst_interpolation_control_source_new), + (gst_interpolation_control_source_set_interpolation_mode), + (gst_interpolation_control_source_bind), + (gst_control_point_compare), (gst_control_point_find), + (gst_interpolation_control_source_set_internal), + (gst_interpolation_control_source_set), + (gst_interpolation_control_source_set_from_list), + (gst_interpolation_control_source_unset), + (gst_interpolation_control_source_unset_all), + (gst_interpolation_control_source_get_all), + (gst_interpolation_control_source_get_count), + (gst_interpolation_control_source_init), + (gst_interpolation_control_source_finalize), + (gst_interpolation_control_source_dispose), + (gst_interpolation_control_source_class_init): + * libs/gst/controller/gstinterpolationcontrolsource.h: + * libs/gst/controller/gstinterpolationcontrolsourceprivate.h: + API: Refactor GstController into the core controller which can take + a GstControlSource for providing actual values for timestamps. + Implement a interpolation control source and use this for backward + compatibility, deprecate a bunch of functions that are now handled + by GstControlSource or GstInterpolationControlSource. + Make it possible to disable the controller completely or only for + specific properties. Fixes #450711. + * docs/libs/gstreamer-libs-docs.sgml: + * docs/libs/gstreamer-libs-sections.txt: + * docs/libs/gstreamer-libs.types: + Add new functions and classes to the docs. + * tests/check/libs/controller.c: (GST_START_TEST), + (gst_controller_suite): + * tests/examples/controller/audio-example.c: (main): + Port unit test and example to the new API and add some new + unit tests. + 2007-07-05 Wim Taymans Patch by: Mark Nauwelaerts diff --git a/docs/libs/gstreamer-libs-docs.sgml b/docs/libs/gstreamer-libs-docs.sgml index 37e6cd89e9..081cb81db4 100644 --- a/docs/libs/gstreamer-libs-docs.sgml +++ b/docs/libs/gstreamer-libs-docs.sgml @@ -17,6 +17,8 @@ + + @@ -66,6 +68,8 @@ GStreamer Dynamic Parameter Control &GstController; + &GstControlSource; + &GstInterpolationControlSource; &GstControllerGObject; diff --git a/docs/libs/gstreamer-libs-sections.txt b/docs/libs/gstreamer-libs-sections.txt index ca4f1adf50..85efa35908 100644 --- a/docs/libs/gstreamer-libs-sections.txt +++ b/docs/libs/gstreamer-libs-sections.txt @@ -59,7 +59,6 @@ gst_dp_version_get_type GstController gst/controller/gstcontroller.h GstController -GstInterpolateMode gst_controller_init gst_controller_new gst_controller_new_list @@ -67,16 +66,20 @@ gst_controller_new_valist gst_controller_remove_properties gst_controller_remove_properties_list gst_controller_remove_properties_valist +gst_controller_set_disabled +gst_controller_set_property_disabled +gst_controller_suggest_next_sync +gst_controller_sync_values +gst_controller_get_control_source +gst_controller_set_control_source +gst_controller_get +gst_controller_get_value_arrays +gst_controller_get_value_array gst_controller_set gst_controller_set_from_list gst_controller_unset gst_controller_unset_all -gst_controller_get gst_controller_get_all -gst_controller_suggest_next_sync -gst_controller_sync_values -gst_controller_get_value_arrays -gst_controller_get_value_array gst_controller_set_interpolation_mode GST_PARAM_CONTROLLABLE @@ -89,12 +92,62 @@ GST_IS_CONTROLLER_CLASS GST_CONTROLLER_GET_CLASS GST_TYPE_CONTROLLER -InterpolateGet -InterpolateGetValueArray GST_CONTROLLED_PROPERTY gst_controller_get_type +
+gstcontrolsource +GstControlSource +libs/controller/gstcontrolsource.h +GstControlSource +GstControlSourceClass +GstControlSourceBind +GstControlSourceGetValue +GstControlSourceGetValueArray +GstTimedValue +GstValueArray +gst_control_source_bind +gst_control_source_get_value +gst_control_source_get_value_array + +GST_CONTROL_SOURCE +GST_IS_CONTROL_SOURCE +GST_CONTROL_SOURCE_CLASS +GST_IS_CONTROL_SOURCE_CLASS +GST_CONTROL_SOURCE_GET_CLASS +GST_TYPE_CONTROL_SOURCE + +gst_control_source_get_type +
+ +
+gstinterpolationcontrolsource +GstInterpolationControlSource +libs/controller/gstinterpolationcontrolsource.h +GstInterpolationControlSource +GstInterpolateMode +gst_interpolation_control_source_new +gst_interpolation_control_source_set +gst_interpolation_control_source_set_from_list +gst_interpolation_control_source_set_interpolation_mode +gst_interpolation_control_source_get_all +gst_interpolation_control_source_unset +gst_interpolation_control_source_unset_all +gst_interpolation_control_source_get_count + +GstInterpolationControlSourceClass +GstInterpolationControlSourcePrivate +GST_INTERPOLATION_CONTROL_SOURCE +GST_IS_INTERPOLATION_CONTROL_SOURCE +GST_INTERPOLATION_CONTROL_SOURCE_CLASS +GST_IS_INTERPOLATION_CONTROL_SOURCE_CLASS +GST_INTERPOLATION_CONTROL_SOURCE_GET_CLASS +GST_TYPE_INTERPOLATION_CONTROL_SOURCE + +gst_interpolation_control_source_get_type +
+
gstcontrollergobject GstControllerGObject @@ -105,6 +158,8 @@ gst_object_get_controller gst_object_set_controller gst_object_suggest_next_sync gst_object_sync_values +gst_object_get_control_source +gst_object_set_control_source gst_object_get_value_arrays gst_object_get_value_array gst_object_get_control_rate @@ -113,7 +168,6 @@ gst_object_set_control_rate
- # base classes
diff --git a/docs/libs/gstreamer-libs.types b/docs/libs/gstreamer-libs.types index c44b021afa..6730d778d9 100644 --- a/docs/libs/gstreamer-libs.types +++ b/docs/libs/gstreamer-libs.types @@ -2,6 +2,10 @@ #include gst_controller_get_type +#include +gst_control_source_get_type +#include +gst_interpolation_control_source_get_type #include gst_adapter_get_type diff --git a/libs/gst/controller/Makefile.am b/libs/gst/controller/Makefile.am index 1c0ccda96e..4aeded673c 100644 --- a/libs/gst/controller/Makefile.am +++ b/libs/gst/controller/Makefile.am @@ -2,14 +2,21 @@ lib_LTLIBRARIES = libgstcontroller-@GST_MAJORMINOR@.la libgstcontroller_@GST_MAJORMINOR@_includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/controller libgstcontroller_@GST_MAJORMINOR@_include_HEADERS = \ - gstcontroller.h -noinst_HEADERS = gstcontrollerprivate.h + gstcontroller.h \ + gstcontrolsource.h \ + gstinterpolationcontrolsource.h + +noinst_HEADERS = \ + gstcontrollerprivate.h \ + gstinterpolationcontrolsourceprivate.h libgstcontroller_@GST_MAJORMINOR@_la_SOURCES = \ lib.c \ gstcontroller.c \ gstinterpolation.c \ - gsthelper.c + gsthelper.c \ + gstcontrolsource.c \ + gstinterpolationcontrolsource.c libgstcontroller_@GST_MAJORMINOR@_la_CFLAGS = $(GST_OBJ_CFLAGS) libgstcontroller_@GST_MAJORMINOR@_la_LIBADD = $(GST_OBJ_LIBS) diff --git a/libs/gst/controller/gstcontroller.c b/libs/gst/controller/gstcontroller.c index f6b94a1d12..3df8f979a8 100644 --- a/libs/gst/controller/gstcontroller.c +++ b/libs/gst/controller/gstcontroller.c @@ -53,13 +53,15 @@ * controller = g_object_control_properties(object, "prop1", "prop2",...); * * - * set how the controller will smooth inbetween values. - * gst_controller_set_interpolation_mode(controller,"prop1",mode); + * Get a #GstControlSource for the property and set it up. + * csource = gst_interpolation_control_source_new (); + * gst_interpolation_control_source_set_interpolation_mode(csource, mode); + * gst_interpolation_control_source_set (csource,0 * GST_SECOND, value1); + * gst_interpolation_control_source_set (csource,1 * GST_SECOND, value2); * * - * set key values - * gst_controller_set (controller, "prop1" ,0 * GST_SECOND, value1); - * gst_controller_set (controller, "prop1" ,1 * GST_SECOND, value2); + * Set the #GstControlSource in the controller. + * gst_controller_set_control_source (controller, "prop1", csource); * * * start your pipeline @@ -70,8 +72,11 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include "gstcontrollerprivate.h" + #include "gstcontroller.h" +#include "gstcontrollerprivate.h" +#include "gstcontrolsource.h" +#include "gstinterpolationcontrolsource.h" #define GST_CAT_DEFAULT gst_controller_debug GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT); @@ -91,188 +96,20 @@ struct _GstControllerPrivate GstClockTime last_sync; }; -/* imports from gst-interpolation.c */ - -extern GList - * gst_controlled_property_find_control_point_node (GstControlledProperty * - prop, GstClockTime timestamp); -extern GstInterpolateMethod *interpolation_methods[]; -extern guint num_interpolation_methods; - -/* callbacks */ - -void -on_object_controlled_property_changed (const GObject * object, GParamSpec * arg, - gpointer user_data) -{ - GstControlledProperty *prop = GST_CONTROLLED_PROPERTY (user_data); - GstController *ctrl; - - GST_LOG ("notify for '%s'", prop->name); - - ctrl = g_object_get_qdata (G_OBJECT (object), __gst_controller_key); - g_return_if_fail (ctrl); - - if (g_mutex_trylock (ctrl->lock)) { - if (!G_IS_VALUE (&prop->live_value.value)) { - g_value_init (&prop->live_value.value, prop->type); - } - g_object_get_property (G_OBJECT (object), prop->name, - &prop->live_value.value); - prop->live_value.timestamp = prop->last_value.timestamp; - g_mutex_unlock (ctrl->lock); - GST_DEBUG ("-> is live update : ts=%" G_GUINT64_FORMAT, - prop->live_value.timestamp); - } -} - /* helper */ -/* - * gst_control_point_compare: - * @p1: a pointer to a #GstControlPoint - * @p2: a pointer to a #GstControlPoint - * - * Compare function for g_list operations that operates on two #GstControlPoint - * parameters. - */ -static gint -gst_control_point_compare (gconstpointer p1, gconstpointer p2) -{ - GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp; - GstClockTime ct2 = ((GstControlPoint *) p2)->timestamp; - - return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1)); -/* this does not produce an gint :( - return ((ct1 - ct2)); -*/ -} - -/* - * gst_control_point_find: - * @p1: a pointer to a #GstControlPoint - * @p2: a pointer to a #GstClockTime - * - * Compare function for g_list operations that operates on a #GstControlPoint and - * a #GstClockTime. - */ -static gint -gst_control_point_find (gconstpointer p1, gconstpointer p2) -{ - GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp; - GstClockTime ct2 = *(GstClockTime *) p2; - - return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1)); -/* this does not produce an gint :( - return ((ct1 - ct2)); -*/ -} - -/* - * gst_controlled_property_set_interpolation_mode: - * @self: the controlled property object to change - * @mode: the new interpolation mode - * - * Sets the given Interpolation mode for the controlled property and activates - * the respective interpolation hooks. - * - * Returns: %TRUE for success - */ -static gboolean -gst_controlled_property_set_interpolation_mode (GstControlledProperty * self, - GstInterpolateMode mode) -{ - gboolean res = TRUE; - - if (mode >= num_interpolation_methods || interpolation_methods[mode] == NULL) { - GST_WARNING ("interpolation mode %d invalid or not implemented yet", mode); - return FALSE; - } - - self->interpolation = mode; - if (mode != GST_INTERPOLATE_USER) { - switch (self->base) { - case G_TYPE_INT: - self->get = interpolation_methods[mode]->get_int; - self->get_value_array = - interpolation_methods[mode]->get_int_value_array; - break; - case G_TYPE_UINT: - self->get = interpolation_methods[mode]->get_uint; - self->get_value_array = - interpolation_methods[mode]->get_uint_value_array; - break; - case G_TYPE_LONG: - self->get = interpolation_methods[mode]->get_long; - self->get_value_array = - interpolation_methods[mode]->get_long_value_array; - break; - case G_TYPE_ULONG: - self->get = interpolation_methods[mode]->get_ulong; - self->get_value_array = - interpolation_methods[mode]->get_ulong_value_array; - break; - case G_TYPE_FLOAT: - self->get = interpolation_methods[mode]->get_float; - self->get_value_array = - interpolation_methods[mode]->get_float_value_array; - break; - case G_TYPE_DOUBLE: - self->get = interpolation_methods[mode]->get_double; - self->get_value_array = - interpolation_methods[mode]->get_double_value_array; - break; - case G_TYPE_BOOLEAN: - self->get = interpolation_methods[mode]->get_boolean; - self->get_value_array = - interpolation_methods[mode]->get_boolean_value_array; - break; - case G_TYPE_ENUM: - self->get = interpolation_methods[mode]->get_enum; - self->get_value_array = - interpolation_methods[mode]->get_enum_value_array; - break; - case G_TYPE_STRING: - self->get = interpolation_methods[mode]->get_string; - self->get_value_array = - interpolation_methods[mode]->get_string_value_array; - break; - default: - self->get = NULL; - self->get_value_array = NULL; - } - if (!self->get || !self->get_value_array) { - GST_WARNING ("incomplete implementation for type %lu/%lu:'%s'/'%s'", - self->type, self->base, - g_type_name (self->type), g_type_name (self->base)); - res = FALSE; - } - if (mode == GST_INTERPOLATE_QUADRATIC) { - GST_WARNING ("Quadratic interpolation mode is deprecated, using cubic" - "interpolation mode"); - } - } else { - /* TODO shouldn't this also get a GstInterpolateMethod *user_method - for the case mode==GST_INTERPOLATE_USER - */ - res = FALSE; - } - - self->valid_cache = FALSE; - - return (res); -} - static void -gst_controlled_property_prepend_default (GstControlledProperty * prop) +gst_controlled_property_add_interpolation_control_source (GstControlledProperty + * self) { - GstControlPoint *cp = g_new0 (GstControlPoint, 1); + GstControlSource *csource = + GST_CONTROL_SOURCE (gst_interpolation_control_source_new ()); - cp->timestamp = 0; - g_value_init (&cp->value, prop->type); - g_value_copy (&prop->default_value, &cp->value); - prop->values = g_list_prepend (prop->values, cp); - prop->nvalues++; + GST_INFO + ("Adding a GstInterpolationControlSource because of backward compatibility"); + g_return_if_fail (!self->csource); + gst_control_source_bind (GST_CONTROL_SOURCE (csource), self->pspec); + self->csource = csource; } /* @@ -305,162 +142,19 @@ gst_controlled_property_new (GObject * object, const gchar * name) /* check if this param is not construct-only */ g_return_val_if_fail (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY), NULL); - /* TODO do sanity checks - we don't control some pspec->value_type: - G_TYPE_PARAM_BOXED - G_TYPE_PARAM_ENUM - G_TYPE_PARAM_FLAGS - G_TYPE_PARAM_OBJECT - G_TYPE_PARAM_PARAM - G_TYPE_PARAM_POINTER - G_TYPE_PARAM_STRING - */ - if ((prop = g_new0 (GstControlledProperty, 1))) { - gchar *signal_name; - GType base; - - prop->name = pspec->name; /* so we don't use the same mem twice */ - prop->type = G_PARAM_SPEC_VALUE_TYPE (pspec); - /* get the fundamental base type */ - prop->base = prop->type; - while ((base = g_type_parent (prop->base))) { - prop->base = base; - } - /* initialize mode specific accessor callbacks */ - if (!gst_controlled_property_set_interpolation_mode (prop, - GST_INTERPOLATE_NONE)) - goto Error; - /* prepare our gvalues */ - g_value_init (&prop->default_value, prop->type); - g_value_init (&prop->result_value, prop->type); - g_value_init (&prop->last_value.value, prop->type); - switch (prop->base) { - case G_TYPE_INT:{ - GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec); - - g_value_set_int (&prop->default_value, tpspec->default_value); - g_value_init (&prop->min_value, prop->type); - g_value_set_int (&prop->min_value, tpspec->minimum); - g_value_init (&prop->max_value, prop->type); - g_value_set_int (&prop->max_value, tpspec->maximum); - } - break; - case G_TYPE_UINT:{ - GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec); - - g_value_set_uint (&prop->default_value, tpspec->default_value); - g_value_init (&prop->min_value, prop->type); - g_value_set_uint (&prop->min_value, tpspec->minimum); - g_value_init (&prop->max_value, prop->type); - g_value_set_uint (&prop->max_value, tpspec->maximum); - } - break; - case G_TYPE_LONG:{ - GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec); - - g_value_set_long (&prop->default_value, tpspec->default_value); - g_value_init (&prop->min_value, prop->type); - g_value_set_long (&prop->min_value, tpspec->minimum); - g_value_init (&prop->max_value, prop->type); - g_value_set_long (&prop->max_value, tpspec->maximum); - } - break; - case G_TYPE_ULONG:{ - GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec); - - g_value_set_ulong (&prop->default_value, tpspec->default_value); - g_value_init (&prop->min_value, prop->type); - g_value_set_ulong (&prop->min_value, tpspec->minimum); - g_value_init (&prop->max_value, prop->type); - g_value_set_ulong (&prop->max_value, tpspec->maximum); - } - break; - case G_TYPE_FLOAT:{ - GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec); - - g_value_set_float (&prop->default_value, tpspec->default_value); - g_value_init (&prop->min_value, prop->type); - g_value_set_float (&prop->min_value, tpspec->minimum); - g_value_init (&prop->max_value, prop->type); - g_value_set_float (&prop->max_value, tpspec->maximum); - } - break; - case G_TYPE_DOUBLE:{ - GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec); - - g_value_set_double (&prop->default_value, tpspec->default_value); - g_value_init (&prop->min_value, prop->type); - g_value_set_double (&prop->min_value, tpspec->minimum); - g_value_init (&prop->max_value, prop->type); - g_value_set_double (&prop->max_value, tpspec->maximum); - } - break; - case G_TYPE_BOOLEAN:{ - GParamSpecBoolean *tpspec = G_PARAM_SPEC_BOOLEAN (pspec); - - g_value_set_boolean (&prop->default_value, tpspec->default_value); - } - break; - case G_TYPE_ENUM:{ - GParamSpecEnum *tpspec = G_PARAM_SPEC_ENUM (pspec); - - g_value_set_enum (&prop->default_value, tpspec->default_value); - } - break; - case G_TYPE_STRING:{ - GParamSpecString *tpspec = G_PARAM_SPEC_STRING (pspec); - - g_value_set_string (&prop->default_value, tpspec->default_value); - } - break; - default: - GST_WARNING ("incomplete implementation for paramspec type '%s'", - G_PARAM_SPEC_TYPE_NAME (pspec)); - } - - prop->valid_cache = FALSE; - prop->nvalues = 0; - - /* Add a control point at timestamp 0 with the default value - * to make the life of interpolators easier. */ - gst_controlled_property_prepend_default (prop); - - signal_name = g_alloca (8 + 1 + strlen (name)); - g_sprintf (signal_name, "notify::%s", name); - prop->notify_handler_id = - g_signal_connect (object, signal_name, - G_CALLBACK (on_object_controlled_property_changed), (gpointer) prop); + prop->pspec = pspec; + prop->name = pspec->name; + prop->disabled = FALSE; } } else { GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object), name); } - return (prop); -Error: - if (prop) - g_free (prop); - return (NULL); + return prop; } /* - * gst_control_point_free: - * @prop: the object to free - * - * Private method which frees all data allocated by a #GstControlPoint - * instance. - */ -static void -gst_control_point_free (GstControlPoint * cp) -{ - g_return_if_fail (cp); - - g_value_unset (&cp->value); - g_free (cp); -} - -/* - * gst_controlled_property_free: * @prop: the object to free * @@ -470,20 +164,8 @@ gst_control_point_free (GstControlPoint * cp) static void gst_controlled_property_free (GstControlledProperty * prop) { - g_list_foreach (prop->values, (GFunc) gst_control_point_free, NULL); - g_list_free (prop->values); - - g_value_unset (&prop->default_value); - g_value_unset (&prop->result_value); - g_value_unset (&prop->last_value.value); - if (G_IS_VALUE (&prop->live_value.value)) - g_value_unset (&prop->live_value.value); - - if (G_IS_VALUE (&prop->min_value)) - g_value_unset (&prop->min_value); - if (G_IS_VALUE (&prop->max_value)) - g_value_unset (&prop->max_value); - + if (prop->csource) + g_object_unref (prop->csource); g_free (prop); } @@ -507,12 +189,12 @@ gst_controller_find_controlled_property (GstController * self, for (node = self->properties; node; node = g_list_next (node)) { prop = node->data; if (!strcmp (prop->name, name)) { - return (prop); + return prop; } } GST_DEBUG ("controller does not (yet) manage property '%s'", name); - return (NULL); + return NULL; } /* methods */ @@ -538,17 +220,6 @@ gst_controller_new_valist (GObject * object, va_list var_args) GST_INFO ("setting up a new controller"); - /* TODO should this method check if the given object implements GstParent and - if so instantiate a GstParentController ? - - BilboEd: This is too specific to be put here, don't we want - GstController to be as generic as possible ? - - Ensonic: So we will have gst_parent_controller_new as well and maybe a - convinience function that automatically chooses the right one (how to name it)? - GstParent will be in core after all. - */ - self = g_object_get_qdata (object, __gst_controller_key); /* create GstControlledProperty for each property */ while ((name = va_arg (var_args, gchar *))) { @@ -585,7 +256,7 @@ gst_controller_new_valist (GObject * object, va_list var_args) if (self) GST_INFO ("controller->ref_count=%d", G_OBJECT (self)->ref_count); - return (self); + return self; } /** @@ -646,7 +317,7 @@ gst_controller_new_list (GObject * object, GList * list) if (self) GST_INFO ("controller->ref_count=%d", G_OBJECT (self)->ref_count); - return (self); + return self; } /** @@ -670,7 +341,7 @@ gst_controller_new (GObject * object, ...) self = gst_controller_new_valist (object, var_args); va_end (var_args); - return (self); + return self; } /** @@ -696,7 +367,7 @@ gst_controller_remove_properties_valist (GstController * self, va_list var_args) g_mutex_lock (self->lock); if ((prop = gst_controller_find_controlled_property (self, name))) { self->properties = g_list_remove (self->properties, prop); - g_signal_handler_disconnect (self->object, prop->notify_handler_id); + //g_signal_handler_disconnect (self->object, prop->notify_handler_id); gst_controlled_property_free (prop); } else { res = FALSE; @@ -704,7 +375,7 @@ gst_controller_remove_properties_valist (GstController * self, va_list var_args) g_mutex_unlock (self->lock); } - return (res); + return res; } /** @@ -733,7 +404,7 @@ gst_controller_remove_properties_list (GstController * self, GList * list) g_mutex_lock (self->lock); if ((prop = gst_controller_find_controlled_property (self, name))) { self->properties = g_list_remove (self->properties, prop); - g_signal_handler_disconnect (self->object, prop->notify_handler_id); + //g_signal_handler_disconnect (self->object, prop->notify_handler_id); gst_controlled_property_free (prop); } else { res = FALSE; @@ -741,7 +412,7 @@ gst_controller_remove_properties_list (GstController * self, GList * list) g_mutex_unlock (self->lock); } - return (res); + return res; } /** @@ -765,210 +436,138 @@ gst_controller_remove_properties (GstController * self, ...) res = gst_controller_remove_properties_valist (self, var_args); va_end (var_args); - return (res); -} - -static gboolean -gst_controller_set_unlocked (GstController * self, GstControlledProperty * prop, - GstClockTime timestamp, GValue * value) -{ - gboolean res = FALSE; - - if (G_VALUE_TYPE (value) == prop->type) { - GstControlPoint *cp; - GList *node; - - /* check if a control point for the timestamp already exists */ - if ((node = g_list_find_custom (prop->values, ×tamp, - gst_control_point_find))) { - cp = node->data; - g_value_reset (&cp->value); - g_value_copy (value, &cp->value); - } else { - /* create a new GstControlPoint */ - cp = g_new0 (GstControlPoint, 1); - cp->timestamp = timestamp; - g_value_init (&cp->value, prop->type); - g_value_copy (value, &cp->value); - /* and sort it into the prop->values list */ - prop->values = - g_list_insert_sorted (prop->values, cp, gst_control_point_compare); - prop->nvalues++; - } - prop->valid_cache = FALSE; - res = TRUE; - } else { - GST_WARNING ("incompatible value type for property '%s'", prop->name); - } - return res; } /** - * gst_controller_set: - * @self: the controller object which handles the properties - * @property_name: the name of the property to set - * @timestamp: the time the control-change is schedules for - * @value: the control-value + * gst_controller_set_property_disabled: + * @self: the #GstController which should be disabled + * @property_name: property to disable + * @disabled: boolean that specifies whether to disable the controller + * or not. * - * Set the value of given controller-handled property at a certain time. + * This function is used to disable the #GstController on a property for + * some time, i.e. gst_controller_sync_values() will do nothing for the + * property. * - * Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise + * Since: 0.10.14 */ -gboolean -gst_controller_set (GstController * self, gchar * property_name, - GstClockTime timestamp, GValue * value) + +void +gst_controller_set_property_disabled (GstController * self, + gchar * property_name, gboolean disabled) { - gboolean res = FALSE; GstControlledProperty *prop; - g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); - g_return_val_if_fail (property_name, FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - g_return_val_if_fail (G_IS_VALUE (value), FALSE); + g_return_if_fail (GST_IS_CONTROLLER (self)); + g_return_if_fail (property_name); g_mutex_lock (self->lock); if ((prop = gst_controller_find_controlled_property (self, property_name))) { - res = gst_controller_set_unlocked (self, prop, timestamp, value); + prop->disabled = disabled; } g_mutex_unlock (self->lock); +} - return (res); + +/** + * gst_controller_set_disabled: + * @self: the #GstController which should be disabled + * @disabled: boolean that specifies whether to disable the controller + * or not. + * + * This function is used to disable all properties of the #GstController + * for some time, i.e. gst_controller_sync_values() will do nothing. + * + * Since: 0.10.14 + */ + +void +gst_controller_set_disabled (GstController * self, gboolean disabled) +{ + GList *node; + GstControlledProperty *prop; + + g_return_if_fail (GST_IS_CONTROLLER (self)); + + g_mutex_lock (self->lock); + for (node = self->properties; node; node = node->next) { + prop = node->data; + prop->disabled = disabled; + } + g_mutex_unlock (self->lock); } /** - * gst_controller_set_from_list: - * @self: the controller object which handles the properties - * @property_name: the name of the property to set - * @timedvalues: a list with #GstTimedValue items + * gst_controller_set_control_source: + * @self: the controller object + * @property_name: name of the property for which the #GstControlSource should be set + * @csource: the #GstControlSource that should be used for the property * - * Sets multiple timed values at once. + * Sets the #GstControlSource for @property_name. If there already was a #GstControlSource + * for this property it will be unreferenced. * - * Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise + * Returns: %FALSE if the given property isn't handled by the controller or the new #GstControlSource + * couldn't be bound to the property, %TRUE if everything worked as expected. + * + * Since: 0.10.14 */ - gboolean -gst_controller_set_from_list (GstController * self, gchar * property_name, - GSList * timedvalues) +gst_controller_set_control_source (GstController * self, gchar * property_name, + GstControlSource * csource) { - gboolean res = FALSE; GstControlledProperty *prop; - GSList *node; - GstTimedValue *tv; - - g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); - g_return_val_if_fail (property_name, FALSE); + gboolean ret = FALSE; g_mutex_lock (self->lock); if ((prop = gst_controller_find_controlled_property (self, property_name))) { - for (node = timedvalues; node; node = g_slist_next (node)) { - tv = node->data; - if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) { - GST_WARNING ("GstTimedValued with invalid timestamp passed to %s " - "for property '%s'", GST_FUNCTION, property_name); - } else if (!G_IS_VALUE (&tv->value)) { - GST_WARNING ("GstTimedValued with invalid value passed to %s " - "for property '%s'", GST_FUNCTION, property_name); - } else { - res = - gst_controller_set_unlocked (self, prop, tv->timestamp, &tv->value); - } + GstControlSource *old = prop->csource; + + if (csource && (ret = gst_control_source_bind (csource, prop->pspec))) { + g_object_ref (csource); + prop->csource = csource; + } else if (!csource) { + ret = TRUE; + prop->csource = NULL; } + + if (ret && old) + g_object_unref (old); } g_mutex_unlock (self->lock); - return (res); + return ret; } /** - * gst_controller_unset: - * @self: the controller object which handles the properties - * @property_name: the name of the property to unset - * @timestamp: the time the control-change should be removed from + * gst_controller_get_control_source: + * @self: the controller object + * @property_name: name of the property for which the #GstControlSource should be get * - * Used to remove the value of given controller-handled property at a certain - * time. + * Gets the corresponding #GstControlSource for the property. This should be unreferenced + * again after use. * - * Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise + * Returns: the #GstControlSource for @property_name or NULL if the property is not + * controlled by this controller or no #GstControlSource was assigned yet. + * + * Since: 0.10.14 */ -gboolean -gst_controller_unset (GstController * self, gchar * property_name, - GstClockTime timestamp) +GstControlSource * +gst_controller_get_control_source (GstController * self, gchar * property_name) { - gboolean res = FALSE; GstControlledProperty *prop; - - g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); - g_return_val_if_fail (property_name, FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + GstControlSource *ret = NULL; g_mutex_lock (self->lock); if ((prop = gst_controller_find_controlled_property (self, property_name))) { - GList *node; - - /* check if a control point for the timestamp exists */ - if ((node = g_list_find_custom (prop->values, ×tamp, - gst_control_point_find))) { - GstControlPoint *cp = node->data; - - if (cp->timestamp == 0) { - /* Restore the default node */ - g_value_reset (&cp->value); - g_value_copy (&prop->default_value, &cp->value); - } else { - if (node == prop->last_requested_value) - prop->last_requested_value = NULL; - gst_control_point_free (node->data); /* free GstControlPoint */ - prop->values = g_list_delete_link (prop->values, node); - prop->nvalues--; - } - prop->valid_cache = FALSE; - res = TRUE; - } + ret = prop->csource; } g_mutex_unlock (self->lock); - return (res); -} + if (ret) + g_object_ref (ret); -/** - * gst_controller_unset_all: - * @self: the controller object which handles the properties - * @property_name: the name of the property to unset - * - * Used to remove all time-stamped values of given controller-handled property - * - * Returns: %FALSE if the values couldn't be unset (ex : properties not handled - * by controller), %TRUE otherwise - * Since: 0.10.5 - */ -gboolean -gst_controller_unset_all (GstController * self, gchar * property_name) -{ - gboolean res = FALSE; - GstControlledProperty *prop; - - g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); - g_return_val_if_fail (property_name, FALSE); - - g_mutex_lock (self->lock); - if ((prop = gst_controller_find_controlled_property (self, property_name))) { - /* free GstControlPoint structures */ - g_list_foreach (prop->values, (GFunc) gst_control_point_free, NULL); - g_list_free (prop->values); - prop->last_requested_value = NULL; - prop->values = NULL; - prop->nvalues = 0; - prop->valid_cache = FALSE; - - /* Insert the default control point again */ - gst_controlled_property_prepend_default (prop); - - res = TRUE; - } - g_mutex_unlock (self->lock); - - return (res); + return ret; } /** @@ -995,42 +594,24 @@ gst_controller_get (GstController * self, gchar * property_name, g_mutex_lock (self->lock); if ((prop = gst_controller_find_controlled_property (self, property_name))) { - /* get current value via interpolator */ - val = prop->get (prop, timestamp); + val = g_new0 (GValue, 1); + g_value_init (val, G_PARAM_SPEC_VALUE_TYPE (prop->pspec)); + if (prop->csource) { + gboolean res; + + /* get current value via control source */ + res = gst_control_source_get_value (prop->csource, timestamp, val); + if (!res) { + g_free (val); + val = FALSE; + } + } else { + g_object_get_property (self->object, prop->name, val); + } } g_mutex_unlock (self->lock); - return (val); -} - -/** - * gst_controller_get_all: - * @self: the controller to get the list from - * @property_name: the name of the property to get the list for - * - * Returns a read-only copy of the list of #GstTimedValue for the given property. - * Free the list after done with it. - * - * This doesn't modify the controlled GObject property! - * - * Returns: a copy of the list, or %NULL if the property isn't handled by the controller - */ -const GList * -gst_controller_get_all (GstController * self, gchar * property_name) -{ - GList *res = NULL; - GstControlledProperty *prop; - - g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL); - g_return_val_if_fail (property_name, NULL); - - g_mutex_lock (self->lock); - if ((prop = gst_controller_find_controlled_property (self, property_name))) { - res = g_list_copy (prop->values); - } - g_mutex_unlock (self->lock); - - return (res); + return val; } /** @@ -1081,8 +662,7 @@ gst_controller_sync_values (GstController * self, GstClockTime timestamp) { GstControlledProperty *prop; GList *node; - GValue *value; - gboolean live = FALSE; + gboolean ret = FALSE; g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); @@ -1092,46 +672,27 @@ gst_controller_sync_values (GstController * self, GstClockTime timestamp) g_mutex_lock (self->lock); /* go over the controlled properties of the controller */ for (node = self->properties; node; node = g_list_next (node)) { + GValue value = { 0, }; prop = node->data; + GST_DEBUG (" property '%s' at ts=%" G_GUINT64_FORMAT, prop->name, timestamp); - live = FALSE; - if (G_UNLIKELY (G_IS_VALUE (&prop->live_value.value))) { - GList *lnode = - gst_controlled_property_find_control_point_node (prop, timestamp); - if (G_UNLIKELY (!lnode)) { - GST_DEBUG (" no control changes in the queue"); - live = TRUE; - } else { - GstControlPoint *cp = lnode->data; + if (!prop->csource || prop->disabled) + continue; - if (prop->live_value.timestamp < cp->timestamp) { - g_value_unset (&prop->live_value.value); - GST_DEBUG (" live value resetted"); - } else if (prop->live_value.timestamp < timestamp) { - live = TRUE; - } - } - } - if (G_LIKELY (!live)) { - /* get current value via interpolator */ - value = prop->get (prop, timestamp); - prop->last_value.timestamp = timestamp; - g_value_copy (value, &prop->last_value.value); - g_object_set_property (self->object, prop->name, value); + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec)); + ret = gst_control_source_get_value (prop->csource, timestamp, &value); + if (ret) { + g_object_set_property (self->object, prop->name, &value); + g_value_unset (&value); } } - if (G_LIKELY (!live)) - self->priv->last_sync = timestamp; + self->priv->last_sync = timestamp; g_mutex_unlock (self->lock); - /* TODO what can here go wrong, to return FALSE ? - BilboEd : Nothing I guess, as long as all the checks are made when creating the controller, - adding/removing controlled properties, etc... - */ - return (TRUE); + return ret; } /** @@ -1207,47 +768,19 @@ gst_controller_get_value_array (GstController * self, GstClockTime timestamp, if ((prop = gst_controller_find_controlled_property (self, value_array->property_name))) { - /* get current value_array via interpolator */ - res = prop->get_value_array (prop, timestamp, value_array); + /* get current value_array via control source */ + + if (!prop->csource) + goto out; + + res = + gst_control_source_get_value_array (prop->csource, timestamp, + value_array); } +out: g_mutex_unlock (self->lock); - return (res); -} - -/** - * gst_controller_set_interpolation_mode: - * @self: the controller object - * @property_name: the name of the property for which to change the interpolation - * @mode: interpolation mode - * - * Sets the given interpolation mode on the given property. - * - * User interpolation is not yet available and quadratic interpolation - * is deprecated and maps to cubic interpolation. - * - * Returns: %TRUE if the property is handled by the controller, %FALSE otherwise - */ -gboolean -gst_controller_set_interpolation_mode (GstController * self, - gchar * property_name, GstInterpolateMode mode) -{ - gboolean res = FALSE; - GstControlledProperty *prop; - - g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); - g_return_val_if_fail (property_name, FALSE); - - g_mutex_lock (self->lock); - if ((prop = gst_controller_find_controlled_property (self, property_name))) { - /* TODO shouldn't this also get a GstInterpolateMethod *user_method - for the case mode==GST_INTERPOLATE_USER - */ - res = gst_controlled_property_set_interpolation_mode (prop, mode); - } - g_mutex_unlock (self->lock); - - return (res); + return res; } /* gobject handling */ @@ -1322,7 +855,6 @@ _gst_controller_dispose (GObject * object) for (node = self->properties; node; node = g_list_next (node)) { GstControlledProperty *prop = node->data; - g_signal_handler_disconnect (self->object, prop->notify_handler_id); gst_controlled_property_free (prop); } g_list_free (self->properties); @@ -1391,7 +923,7 @@ _gst_controller_class_init (GstControllerClass * klass) } GType -gst_controller_get_type (void) +gst_controller_get_type () { static GType type = 0; @@ -1412,3 +944,277 @@ gst_controller_get_type (void) } return type; } + +/* FIXME: backward compatibility functions */ + +/* + * gst_controlled_property_set_interpolation_mode: + * @self: the controlled property object to change + * @mode: the new interpolation mode + * + * Sets the given Interpolation mode for the controlled property and activates + * the respective interpolation hooks. + * + * Deprecated: Use #GstControlSource, for example #GstInterpolationControlSource + * directly. + * + * Returns: %TRUE for success + */ +static gboolean +gst_controlled_property_set_interpolation_mode (GstControlledProperty * self, + GstInterpolateMode mode) +{ + GstInterpolationControlSource *icsource; + + /* FIXME: backward compat, add GstInterpolationControlSource */ + if (!self->csource) + gst_controlled_property_add_interpolation_control_source (self); + + g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self->csource), + FALSE); + + icsource = GST_INTERPOLATION_CONTROL_SOURCE (self->csource); + + return gst_interpolation_control_source_set_interpolation_mode (icsource, + mode); +} + +/** + * gst_controller_set: + * @self: the controller object which handles the properties + * @property_name: the name of the property to set + * @timestamp: the time the control-change is schedules for + * @value: the control-value + * + * Set the value of given controller-handled property at a certain time. + * + * Deprecated: Use #GstControlSource, for example #GstInterpolationControlSource + * directly. + * + * Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise + */ +gboolean +gst_controller_set (GstController * self, gchar * property_name, + GstClockTime timestamp, GValue * value) +{ + gboolean res = FALSE; + GstControlledProperty *prop; + + g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); + g_return_val_if_fail (property_name, FALSE); + + g_mutex_lock (self->lock); + if ((prop = gst_controller_find_controlled_property (self, property_name))) { + /* FIXME: backward compat, add GstInterpolationControlSource */ + if (!prop->csource) + gst_controlled_property_add_interpolation_control_source (prop); + + if (!GST_IS_INTERPOLATION_CONTROL_SOURCE (prop->csource)) + goto out; + res = + gst_interpolation_control_source_set (GST_INTERPOLATION_CONTROL_SOURCE + (prop->csource), timestamp, value); + } + +out: + g_mutex_unlock (self->lock); + + return res; +} + +/** + * gst_controller_set_from_list: + * @self: the controller object which handles the properties + * @property_name: the name of the property to set + * @timedvalues: a list with #GstTimedValue items + * + * Sets multiple timed values at once. + * + * Deprecated: Use #GstControlSource, for example #GstInterpolationControlSource + * directly. + * + * Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise + */ + +gboolean +gst_controller_set_from_list (GstController * self, gchar * property_name, + GSList * timedvalues) +{ + gboolean res = FALSE; + GstControlledProperty *prop; + + g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); + g_return_val_if_fail (property_name, FALSE); + + g_mutex_lock (self->lock); + if ((prop = gst_controller_find_controlled_property (self, property_name))) { + /* FIXME: backward compat, add GstInterpolationControlSource */ + if (!prop->csource) + gst_controlled_property_add_interpolation_control_source (prop); + + if (!GST_IS_INTERPOLATION_CONTROL_SOURCE (prop->csource)) + goto out; + + res = + gst_interpolation_control_source_set_from_list + (GST_INTERPOLATION_CONTROL_SOURCE (prop->csource), timedvalues); + } + +out: + g_mutex_unlock (self->lock); + + return res; +} + +/** + * gst_controller_unset: + * @self: the controller object which handles the properties + * @property_name: the name of the property to unset + * @timestamp: the time the control-change should be removed from + * + * Used to remove the value of given controller-handled property at a certain + * time. + * + * Deprecated: Use #GstControlSource, for example #GstInterpolationControlSource + * directly. + * + * Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise + */ +gboolean +gst_controller_unset (GstController * self, gchar * property_name, + GstClockTime timestamp) +{ + gboolean res = FALSE; + GstControlledProperty *prop; + + g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); + g_return_val_if_fail (property_name, FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + + g_mutex_lock (self->lock); + if ((prop = gst_controller_find_controlled_property (self, property_name))) { + if (!prop->csource || !GST_IS_INTERPOLATION_CONTROL_SOURCE (prop->csource)) + goto out; + + res = + gst_interpolation_control_source_unset (GST_INTERPOLATION_CONTROL_SOURCE + (prop->csource), timestamp); + } + +out: + g_mutex_unlock (self->lock); + + return res; +} + +/** + * gst_controller_unset_all: + * @self: the controller object which handles the properties + * @property_name: the name of the property to unset + * + * Used to remove all time-stamped values of given controller-handled property + * + * Deprecated: Use #GstControlSource, for example #GstInterpolationControlSource + * directly. + * + * Returns: %FALSE if the values couldn't be unset (ex : properties not handled + * by controller), %TRUE otherwise + * Since: 0.10.5 + */ +gboolean +gst_controller_unset_all (GstController * self, gchar * property_name) +{ + GstControlledProperty *prop; + + g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); + g_return_val_if_fail (property_name, FALSE); + + g_mutex_lock (self->lock); + if ((prop = gst_controller_find_controlled_property (self, property_name))) { + if (!prop->csource || !GST_IS_INTERPOLATION_CONTROL_SOURCE (prop->csource)) + goto out; + + gst_interpolation_control_source_unset_all (GST_INTERPOLATION_CONTROL_SOURCE + (prop->csource)); + } + +out: + g_mutex_unlock (self->lock); + + return TRUE; +} + +/** + * gst_controller_get_all: + * @self: the controller to get the list from + * @property_name: the name of the property to get the list for + * + * Returns a read-only copy of the list of #GstTimedValue for the given property. + * Free the list after done with it. + * + * This doesn't modify the controlled GObject property! + * + * Deprecated: Use #GstControlSource, for example #GstInterpolationControlSource + * directly. + * + * Returns: a copy of the list, or %NULL if the property isn't handled by the controller + */ +const GList * +gst_controller_get_all (GstController * self, gchar * property_name) +{ + const GList *res = NULL; + GstControlledProperty *prop; + + g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL); + g_return_val_if_fail (property_name, NULL); + + g_mutex_lock (self->lock); + if ((prop = gst_controller_find_controlled_property (self, property_name))) { + if (!prop->csource || !GST_IS_INTERPOLATION_CONTROL_SOURCE (prop->csource)) + goto out; + + res = + gst_interpolation_control_source_get_all + (GST_INTERPOLATION_CONTROL_SOURCE (prop->csource)); + } + +out: + g_mutex_unlock (self->lock); + + return res; +} + +/** + * gst_controller_set_interpolation_mode: + * @self: the controller object + * @property_name: the name of the property for which to change the interpolation + * @mode: interpolation mode + * + * Sets the given interpolation mode on the given property. + * + * User interpolation is not yet available and quadratic interpolation + * is deprecated and maps to cubic interpolation. + * + * Deprecated: Use #GstControlSource, for example #GstInterpolationControlSource + * directly. + * + * Returns: %TRUE if the property is handled by the controller, %FALSE otherwise + */ +gboolean +gst_controller_set_interpolation_mode (GstController * self, + gchar * property_name, GstInterpolateMode mode) +{ + gboolean res = FALSE; + GstControlledProperty *prop; + + g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE); + g_return_val_if_fail (property_name, FALSE); + + g_mutex_lock (self->lock); + if ((prop = gst_controller_find_controlled_property (self, property_name))) { + res = gst_controlled_property_set_interpolation_mode (prop, mode); + } + g_mutex_unlock (self->lock); + + return res; +} diff --git a/libs/gst/controller/gstcontroller.h b/libs/gst/controller/gstcontroller.h index 2f2bbcd8e9..d8514e1c35 100644 --- a/libs/gst/controller/gstcontroller.h +++ b/libs/gst/controller/gstcontroller.h @@ -30,6 +30,9 @@ #include #include +#include "gstcontrolsource.h" +#include "gstinterpolationcontrolsource.h" + G_BEGIN_DECLS /** @@ -41,59 +44,6 @@ G_BEGIN_DECLS */ #define GST_PARAM_CONTROLLABLE (1 << (G_PARAM_USER_SHIFT + 1)) - -/** - * GstTimedValue: - * - * a structure for value+time - */ -typedef struct _GstTimedValue -{ - GstClockTime timestamp; /* timestamp of the value change */ - GValue value; /* the new value */ -} GstTimedValue; - - -/** - * GstValueArray: - * @property_name: the name of the property this array belongs to - * @nbsamples: number of samples requested - * @sample_interval: interval between each sample - * @values: pointer to the array - * - * Structure to receive multiple values at once. - */ -typedef struct _GstValueArray -{ - gchar *property_name; - gint nbsamples; - GstClockTime sample_interval; - gpointer *values; -} GstValueArray; - - -/** - * GstInterpolateMode: - * @GST_INTERPOLATE_NONE: steps-like interpolation, default - * @GST_INTERPOLATE_TRIGGER: returns the default value of the property, - * except for times with specific values - * @GST_INTERPOLATE_LINEAR: linear interpolation - * @GST_INTERPOLATE_QUADRATIC: square interpolation (not yet available) - * @GST_INTERPOLATE_CUBIC: cubic interpolation (not yet available) - * @GST_INTERPOLATE_USER: user-provided interpolation (not yet available) - * - * The various interpolation modes available. - */ -typedef enum -{ - GST_INTERPOLATE_NONE, - GST_INTERPOLATE_TRIGGER, - GST_INTERPOLATE_LINEAR, - GST_INTERPOLATE_QUADRATIC, - GST_INTERPOLATE_CUBIC, - GST_INTERPOLATE_USER -} GstInterpolateMode; - /* type macros */ #define GST_TYPE_CONTROLLER (gst_controller_get_type ()) @@ -107,7 +57,6 @@ typedef struct _GstController GstController; typedef struct _GstControllerClass GstControllerClass; typedef struct _GstControllerPrivate GstControllerPrivate; - /** * GstController: * @@ -135,7 +84,7 @@ struct _GstControllerClass gpointer _gst_reserved[GST_PADDING]; }; -GType gst_controller_get_type (void); +GType gst_controller_get_type (); /* GstController functions */ @@ -149,33 +98,22 @@ gboolean gst_controller_remove_properties_list (GstController * self, GList *list); gboolean gst_controller_remove_properties (GstController * self, ...) G_GNUC_NULL_TERMINATED; -gboolean gst_controller_set (GstController * self, gchar * property_name, - GstClockTime timestamp, GValue * value); -gboolean gst_controller_set_from_list (GstController * self, - gchar * property_name, GSList * timedvalues); - -gboolean gst_controller_unset (GstController * self, gchar * property_name, - GstClockTime timestamp); -gboolean gst_controller_unset_all (GstController * self, gchar * property_name); - -GValue *gst_controller_get (GstController * self, gchar * property_name, - GstClockTime timestamp); -const GList *gst_controller_get_all (GstController * self, - gchar * property_name); +void gst_controller_set_disabled (GstController *self, gboolean disabled); +void gst_controller_set_property_disabled (GstController *self, gchar * property_name, gboolean disabled); +gboolean gst_controller_set_control_source (GstController *self, gchar * property_name, GstControlSource *csource); +GstControlSource * gst_controller_get_control_source (GstController *self, gchar * property_name); GstClockTime gst_controller_suggest_next_sync (GstController *self); gboolean gst_controller_sync_values (GstController * self, GstClockTime timestamp); +GValue *gst_controller_get (GstController * self, gchar * property_name, + GstClockTime timestamp); gboolean gst_controller_get_value_arrays (GstController * self, GstClockTime timestamp, GSList * value_arrays); gboolean gst_controller_get_value_array (GstController * self, GstClockTime timestamp, GstValueArray * value_array); -gboolean gst_controller_set_interpolation_mode (GstController * self, - gchar * property_name, GstInterpolateMode mode); - - /* GObject convenience functions */ GstController *gst_object_control_properties (GObject * object, ...) G_GNUC_NULL_TERMINATED; @@ -187,6 +125,9 @@ gboolean gst_object_set_controller (GObject * object, GstController * controller GstClockTime gst_object_suggest_next_sync (GObject * object); gboolean gst_object_sync_values (GObject * object, GstClockTime timestamp); +gboolean gst_object_set_control_source (GObject *object, gchar * property_name, GstControlSource *csource); +GstControlSource * gst_object_get_control_source (GObject *object, gchar * property_name); + gboolean gst_object_get_value_arrays (GObject * object, GstClockTime timestamp, GSList * value_arrays); gboolean gst_object_get_value_array (GObject * object, @@ -199,5 +140,25 @@ void gst_object_set_control_rate (GObject * object, GstClockTime control_rate); gboolean gst_controller_init (int * argc, char ***argv); + +/* FIXME: deprecated functions */ +#ifndef GST_DISABLE_DEPRECATED +gboolean gst_controller_set (GstController * self, gchar * property_name, + GstClockTime timestamp, GValue * value); +gboolean gst_controller_set_from_list (GstController * self, + gchar * property_name, GSList * timedvalues); + +gboolean gst_controller_unset (GstController * self, gchar * property_name, + GstClockTime timestamp); +gboolean gst_controller_unset_all (GstController * self, gchar * property_name); + +const GList *gst_controller_get_all (GstController * self, + gchar * property_name); + +gboolean gst_controller_set_interpolation_mode (GstController * self, + gchar * property_name, GstInterpolateMode mode); +#endif /* GST_DISABLE_DEPRECATED */ + G_END_DECLS + #endif /* __GST_CONTROLLER_H__ */ diff --git a/libs/gst/controller/gstcontrollerprivate.h b/libs/gst/controller/gstcontrollerprivate.h index f5a7a2c520..e4fafee634 100644 --- a/libs/gst/controller/gstcontrollerprivate.h +++ b/libs/gst/controller/gstcontrollerprivate.h @@ -28,97 +28,19 @@ #include #include "gstcontroller.h" +#include "gstcontrolsource.h" G_BEGIN_DECLS -struct _GstControlledProperty; - -typedef GValue *(*InterpolateGet) (struct _GstControlledProperty * prop, - GstClockTime timestamp); -typedef gboolean (*InterpolateGetValueArray) (struct _GstControlledProperty * prop, - GstClockTime timestamp, GstValueArray * value_array); - -/** - * GstInterpolateMethod: - * - * Function pointer structure to do user-defined interpolation methods - */ -typedef struct _GstInterpolateMethod -{ - InterpolateGet get_int; - InterpolateGetValueArray get_int_value_array; - InterpolateGet get_uint; - InterpolateGetValueArray get_uint_value_array; - InterpolateGet get_long; - InterpolateGetValueArray get_long_value_array; - InterpolateGet get_ulong; - InterpolateGetValueArray get_ulong_value_array; - InterpolateGet get_float; - InterpolateGetValueArray get_float_value_array; - InterpolateGet get_double; - InterpolateGetValueArray get_double_value_array; - InterpolateGet get_boolean; - InterpolateGetValueArray get_boolean_value_array; - InterpolateGet get_enum; - InterpolateGetValueArray get_enum_value_array; - InterpolateGet get_string; - InterpolateGetValueArray get_string_value_array; -} GstInterpolateMethod; - -/** - * GstControlPoint: - * - * a internal structure for value+time and various temporary - * values used for interpolation. This "inherits" from - * GstTimedValue. - */ -/* FIXME 0.11: This should be merged with GstTimedValue for 0.11 */ -typedef struct _GstControlPoint -{ - /* fields from GstTimedValue. DO NOT CHANGE! */ - GstClockTime timestamp; /* timestamp of the value change */ - GValue value; /* the new value */ - - /* internal fields */ - - /* Caches for the interpolators */ - union { - struct { - gdouble h; - gdouble z; - } cubic; - } cache; - -} GstControlPoint; - /** * GstControlledProperty: */ typedef struct _GstControlledProperty { + GParamSpec *pspec; /* GParamSpec for this property */ gchar *name; /* name of the property */ - GType type; /* type of the handled property */ - GType base; /* base-type of the handled property */ - GValue default_value; /* default value for the handled property */ - GValue min_value; /* min value for the handled property */ - GValue max_value; /* max value for the handled property */ - GValue result_value; /* result value location for the interpolation method */ - GstControlPoint last_value; /* the last value a _sink call wrote */ - GstControlPoint live_value; /* temporary value override for live input */ - gulong notify_handler_id; /* id of the notify:: signal handler */ - GstInterpolateMode interpolation; /* Interpolation mode */ - /* TODO instead of *method, have pointers to get() and get_value_array() here - gst_controller_set_interpolation_mode() will pick the right ones for the - properties value type - GstInterpolateMethod *method; // User-implemented handler (if interpolation == GST_INTERPOLATE_USER) - */ - InterpolateGet get; - InterpolateGetValueArray get_value_array; - - GList *values; /* List of GstControlPoint */ - gint nvalues; /* Number of control points */ - GList *last_requested_value; /* last search result, can be used for incremental searches */ - gboolean valid_cache; + GstControlSource *csource; /* GstControlSource for this property */ + gboolean disabled; } GstControlledProperty; #define GST_CONTROLLED_PROPERTY(obj) ((GstControlledProperty *)(obj)) diff --git a/libs/gst/controller/gstcontrolsource.c b/libs/gst/controller/gstcontrolsource.c new file mode 100644 index 0000000000..ad705f0cb1 --- /dev/null +++ b/libs/gst/controller/gstcontrolsource.c @@ -0,0 +1,158 @@ +/* GStreamer + * + * Copyright (C) 2007 Sebastian Dröge + * + * gstcontrolsource.c: Interface declaration for control sources + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gstcontrolsource + * @short_description: base class for control source sources + * + * The #GstControlSource is a base class for control value sources that could + * be used by #GstController to get timestamp-value pairs. + * + * A #GstControlSource is used by first getting an instance, binding it to a + * #GParamSpec (for example by using gst_controller_set_control_source()) and + * then by having it used by the #GstController or calling + * gst_control_source_get_value() or gst_control_source_get_value_array(). + * + * For implementing a new #GstControlSource one has to implement a + * #GstControlSourceBind method, which will depending on the #GParamSpec set up + * the control source for use and sets the #GstControlSourceGetValue and + * #GstControlSourceGetValueArray functions. These are then used by + * gst_control_source_get_value() or gst_control_source_get_value_array() + * to get values for specific timestamps. + * + */ + +#include +#include + +#include "gstcontrolsource.h" + +#define GST_CAT_DEFAULT gst_controller_debug +GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT); + +static void gst_control_source_class_init (GstControlSourceClass * klass); +static void gst_control_source_init (GstControlSource * self); + +G_DEFINE_ABSTRACT_TYPE (GstControlSource, gst_control_source, G_TYPE_OBJECT); + +static GObjectClass *parent_class = NULL; + +static void +gst_control_source_class_init (GstControlSourceClass * klass) +{ + //GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + /* Has to be implemented by children */ + klass->bind = NULL; +} + +static void +gst_control_source_init (GstControlSource * self) +{ + /* Set default handlers that print a warning */ + self->get_value = NULL; + self->get_value_array = NULL; + self->bound = FALSE; +} + +/** + * gst_control_source_get_value: + * @self: the #GstControlSource object + * @timestamp: the time for which the value should be returned + * @value: the value + * + * Gets the value for this #GstControlSource at a given timestamp. + * + * Returns: FALSE if the value couldn't be returned, TRUE otherwise. + */ +gboolean +gst_control_source_get_value (GstControlSource * self, GstClockTime timestamp, + GValue * value) +{ + g_return_val_if_fail (GST_IS_CONTROL_SOURCE (self), FALSE); + + if (self->get_value) { + return self->get_value (self, timestamp, value); + } else { + GST_ERROR ("Not bound to a specific property yet!"); + return FALSE; + } +} + +/** + * gst_control_source_get_value_array: + * @self: the #GstControlSource object + * @timestamp: the time that should be processed + * @value_array: array to put control-values in + * + * Gets an array of values for one element property. + * + * All fields of @value_array must be filled correctly. Especially the + * @value_array->values array must be big enough to keep the requested amount + * of values. + * + * The type of the values in the array is the same as the property's type. + * + * Returns: %TRUE if the given array could be filled, %FALSE otherwise + */ +gboolean +gst_control_source_get_value_array (GstControlSource * self, + GstClockTime timestamp, GstValueArray * value_array) +{ + g_return_val_if_fail (GST_IS_CONTROL_SOURCE (self), FALSE); + + if (self->get_value_array) { + return self->get_value_array (self, timestamp, value_array); + } else { + GST_ERROR ("Not bound to a specific property yet!"); + return FALSE; + } +} + +/** + * gst_control_source_bind: + * @self: the #GstControlSource object + * @pspec: #GParamSpec for the property for which this #GstControlSource should generate values. + * + * Binds a #GstControlSource to a specific property. This must be called only once for a + * #GstControlSource. + * + * Returns: %TRUE if the #GstControlSource was bound correctly, %FALSE otherwise. + */ +gboolean +gst_control_source_bind (GstControlSource * self, GParamSpec * pspec) +{ + gboolean ret = FALSE; + + g_return_val_if_fail (GST_IS_CONTROL_SOURCE (self), FALSE); + g_return_val_if_fail (GST_CONTROL_SOURCE_GET_CLASS (self)->bind, FALSE); + g_return_val_if_fail (!self->bound, FALSE); + + ret = GST_CONTROL_SOURCE_GET_CLASS (self)->bind (self, pspec); + + if (ret) + self->bound = TRUE; + + return ret; +} diff --git a/libs/gst/controller/gstcontrolsource.h b/libs/gst/controller/gstcontrolsource.h new file mode 100644 index 0000000000..68897abd66 --- /dev/null +++ b/libs/gst/controller/gstcontrolsource.h @@ -0,0 +1,161 @@ +/* GStreamer + * + * Copyright (C) 2007 Sebastian Dröge + * + * gstcontrolsource.h: Interface declaration for control sources + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_CONTROL_SOURCE_H__ +#define __GST_CONTROL_SOURCE_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_CONTROL_SOURCE \ + (gst_control_source_get_type()) +#define GST_CONTROL_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CONTROL_SOURCE,GstControlSource)) +#define GST_CONTROL_SOURCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CONTROL_SOURCE,GstControlSourceClass)) +#define GST_IS_CONTROL_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CONTROL_SOURCE)) +#define GST_IS_CONTROL_SOURCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CONTROL_SOURCE)) +#define GST_CONTROL_SOURCE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTOL_SOURCE, GstControlSourceClass)) + +typedef struct _GstControlSource GstControlSource; +typedef struct _GstControlSourceClass GstControlSourceClass; +typedef struct _GstTimedValue GstTimedValue; +typedef struct _GstValueArray GstValueArray; + +/** + * GstTimedValue: + * + * Structure for saving a timestamp and a value. + */ +struct _GstTimedValue +{ + GstClockTime timestamp; /* timestamp of the value change */ + GValue value; /* the corresponding value */ +}; + +/** + * GstValueArray: + * @property_name: the name of the property this array belongs to + * @nbsamples: number of samples requested + * @sample_interval: interval between each sample + * @values: pointer to the array + * + * Structure to receive multiple values at once. + */ +struct _GstValueArray +{ + gchar *property_name; + gint nbsamples; + GstClockTime sample_interval; + gpointer *values; +}; + +/** + * GstControlSourceGetValue + * @self: the #GstControlSource instance + * @timestamp: timestamp for which a value should be calculated + * @value: a #GValue which will be set to the result. It must be initialized to the correct type. + * + * Function for returning a value for a given timestamp. + * + * Returns: %TRUE if the value was successfully calculated. + * + */ +typedef gboolean (* GstControlSourceGetValue) (GstControlSource *self, GstClockTime timestamp, GValue *value); + +/** + * GstControlSourceGetValueArray + * @self: the #GstControlSource instance + * @timestamp: timestamp for which a value should be calculated + * @value_array: array to put control-values in + * + * Function for returning a #GstValueArray for a given timestamp. + * + * Returns: %TRUE if the values were successfully calculated. + * + */ +typedef gboolean (* GstControlSourceGetValueArray) (GstControlSource *self, GstClockTime timestamp, GstValueArray *value_array); + +/** + * GstControlSourceBind + * @self: the #GstControlSource instance + * @pspec: #GParamSpec that should be bound to + * + * Function for binding a #GstControlSource to a #GParamSpec. + * + * Returns: %TRUE if the property could be bound to the #GstControlSource, %FALSE otherwise. + */ +typedef gboolean (* GstControlSourceBind) (GstControlSource *self, GParamSpec *pspec); + +/** + * GstControlSource: + * @get_value: Function for returning a value for a given timestamp + * @get_value_array: Function for returning a #GstValueArray for a given timestamp + * + * The instance structure of #GstControlSource. + */ +struct _GstControlSource { + GObject parent; + + /*< public >*/ + GstControlSourceGetValue get_value; /* Returns the value for a property at a given timestamp */ + GstControlSourceGetValueArray get_value_array; /* Returns values for a property in a given timespan */ + + /*< private >*/ + gboolean bound; + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstControlSourceClass: + * @parent_class: Parent class + * @bind: Class method for binding the #GstControlSource to a specific GParamSpec + * + * The class structure of #GstControlSource. + */ + +struct _GstControlSourceClass +{ + GObjectClass parent_class; + + GstControlSourceBind bind; /* Binds the GstControlSource to a specific GParamSpec */ + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_control_source_get_type (); + +/* Functions */ + +gboolean gst_control_source_get_value (GstControlSource *self, GstClockTime timestamp, GValue *value); +gboolean gst_control_source_get_value_array (GstControlSource *self, GstClockTime timestamp, GstValueArray *value_array); +gboolean gst_control_source_bind (GstControlSource *self, GParamSpec *pspec); + +G_END_DECLS + +#endif /* __GST_CONTROL_SOURCE_H__ */ diff --git a/libs/gst/controller/gsthelper.c b/libs/gst/controller/gsthelper.c index 3320599a2d..9c87f60a6a 100644 --- a/libs/gst/controller/gsthelper.c +++ b/libs/gst/controller/gsthelper.c @@ -148,7 +148,6 @@ gst_object_set_controller (GObject * object, GstController * controller) /** * gst_object_suggest_next_sync: * @object: the object that has controlled properties - * @timestamp: the time that should be processed * * Convenience function for GObject * @@ -192,6 +191,61 @@ gst_object_sync_values (GObject * object, GstClockTime timestamp) return (FALSE); } +/** + * gst_object_set_control_source: + * @object: the controller object + * @property_name: name of the property for which the #GstControlSource should be set + * @csource: the #GstControlSource that should be used for the property + * + * Sets the #GstControlSource for @property_name. If there already was a #GstControlSource + * for this property it will be unreferenced. + * + * Returns: %FALSE if the given property isn't handled by the controller or the new #GstControlSource + * couldn't be bound to the property, %TRUE if everything worked as expected. + * + * Since: 0.10.14 + */ +gboolean +gst_object_set_control_source (GObject * object, gchar * property_name, + GstControlSource * csource) +{ + GstController *ctrl = NULL; + + g_return_val_if_fail (G_IS_OBJECT (object), FALSE); + g_return_val_if_fail (GST_IS_CONTROL_SOURCE (csource), FALSE); + + if ((ctrl = g_object_get_qdata (object, __gst_controller_key))) { + return gst_controller_set_control_source (ctrl, property_name, csource); + } + return FALSE; +} + +/** + * gst_object_get_control_source: + * @object: the object + * @property_name: name of the property for which the #GstControlSource should be get + * + * Gets the corresponding #GstControlSource for the property. This should be unreferenced + * again after use. + * + * Returns: the #GstControlSource for @property_name or NULL if the property is not + * controlled by this controller or no #GstControlSource was assigned yet. + * + * Since: 0.10.14 + */ +GstControlSource * +gst_object_get_control_source (GObject * object, gchar * property_name) +{ + GstController *ctrl = NULL; + + g_return_val_if_fail (G_IS_OBJECT (object), FALSE); + + if ((ctrl = g_object_get_qdata (object, __gst_controller_key))) { + return gst_controller_get_control_source (ctrl, property_name); + } + return FALSE; +} + /** * gst_object_get_value_arrays: * @object: the object that has controlled properties diff --git a/libs/gst/controller/gstinterpolation.c b/libs/gst/controller/gstinterpolation.c index d0e7584c1b..4a74e6bab5 100644 --- a/libs/gst/controller/gstinterpolation.c +++ b/libs/gst/controller/gstinterpolation.c @@ -24,8 +24,9 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include "gstcontrollerprivate.h" -#include "gstcontroller.h" + +#include "gstinterpolationcontrolsource.h" +#include "gstinterpolationcontrolsourceprivate.h" #define GST_CAT_DEFAULT gst_controller_debug GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT); @@ -33,28 +34,29 @@ GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT); /* common helper */ /* - * gst_controlled_property_find_control_point_node: - * @prop: the controlled property to search in + * gst_interpolation_control_source_find_control_point_node: + * @self: the interpolation control source to search in * @timestamp: the search key * * Find last value before given timestamp in control point list. * * Returns: the found #GList node or %NULL */ -GList * -gst_controlled_property_find_control_point_node (GstControlledProperty * prop, - GstClockTime timestamp) +static GList *gst_interpolation_control_source_find_control_point_node + (GstInterpolationControlSource * self, GstClockTime timestamp) { - GList *prev_node = g_list_last (prop->values); + GList *prev_node = g_list_last (self->priv->values); GList *node; GstControlPoint *cp; - node = prop->values; - if (prop->last_requested_value) { - GstControlPoint *last_cp = prop->last_requested_value->data; + /* Check if we can start from the last requested value + * to save some time */ + node = self->priv->values; + if (self->priv->last_requested_value) { + GstControlPoint *last_cp = self->priv->last_requested_value->data; if (timestamp > last_cp->timestamp) - node = prop->last_requested_value; + node = self->priv->last_requested_value; } /* iterate over timed value list */ @@ -68,286 +70,476 @@ gst_controlled_property_find_control_point_node (GstControlledProperty * prop, } } + /* If we have something to return save it as a + * potential start position for the next search */ if (prev_node) - prop->last_requested_value = prev_node; + self->priv->last_requested_value = prev_node; return prev_node; } +/* + * gst_interpolation_control_source_get_first_value: + * @self: the interpolation control source to search in + * + * Find the first value and return it. + * + * Returns: the found #GValue or %NULL if there are none. + */ +static inline GValue * +gst_interpolation_control_source_get_first_value (GstInterpolationControlSource + * self) +{ + if (self->priv->values && self->priv->nvalues > 0) { + GstControlPoint *cp = self->priv->values->data; + + return &cp->value; + } else { + return NULL; + } +} + /* steps-like (no-)interpolation, default */ /* just returns the value for the most recent key-frame */ -static GValue * -interpolate_none_get (GstControlledProperty * prop, GstClockTime timestamp) -{ - GList *node; - - if ((node = - gst_controlled_property_find_control_point_node (prop, timestamp))) { - GstControlPoint *cp = node->data; - - return &cp->value; - } - return &prop->default_value; -} - -#define interpolate_none_get_boolean interpolate_none_get - #define DEFINE_NONE_GET(type) \ -static GValue * \ -interpolate_none_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \ +static inline GValue * \ +_interpolate_none_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp) \ { \ + GValue *ret; \ GList *node; \ \ if ((node = \ - gst_controlled_property_find_control_point_node (prop, timestamp))) { \ + gst_interpolation_control_source_find_control_point_node (self, timestamp))) { \ GstControlPoint *cp = node->data; \ - g##type ret = g_value_get_##type (&cp->value); \ - if (g_value_get_##type (&prop->min_value) > ret) \ - return &prop->min_value; \ - else if (g_value_get_##type (&prop->max_value) < ret) \ - return &prop->max_value; \ - return &cp->value; \ + g##type ret_val = g_value_get_##type (&cp->value); \ + \ + if (g_value_get_##type (&self->priv->minimum_value) > ret_val) \ + ret = &self->priv->minimum_value; \ + else if (g_value_get_##type (&self->priv->maximum_value) < ret_val) \ + ret = &self->priv->maximum_value; \ + else \ + ret = &cp->value; \ + } else { \ + ret = gst_interpolation_control_source_get_first_value (self); \ } \ - return &prop->default_value; \ + return ret; \ +} \ +\ +static gboolean \ +interpolate_none_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \ +{ \ + GValue *ret; \ + g_mutex_lock (self->lock); \ + \ + ret = _interpolate_none_get_##type (self, timestamp); \ + if (!ret) { \ + g_mutex_unlock (self->lock); \ + return FALSE; \ + } \ + g_value_copy (ret, value); \ + g_mutex_unlock (self->lock); \ + return TRUE; \ +} \ +\ +static gboolean \ +interpolate_none_get_##type##_value_array (GstInterpolationControlSource *self, \ + GstClockTime timestamp, GstValueArray * value_array) \ +{ \ + gint i; \ + GstClockTime ts = timestamp; \ + g##type *values = (g##type *) value_array->values; \ + GValue *ret; \ + \ + g_mutex_lock (self->lock); \ + for(i = 0; i < value_array->nbsamples; i++) { \ + ret = _interpolate_none_get_##type (self, timestamp); \ + if (!ret) { \ + g_mutex_unlock (self->lock); \ + return FALSE; \ + } \ + *values = g_value_get_##type (ret); \ + ts += value_array->sample_interval; \ + values++; \ + } \ + g_mutex_unlock (self->lock); \ + return TRUE; \ } + DEFINE_NONE_GET (int); DEFINE_NONE_GET (uint); DEFINE_NONE_GET (long); DEFINE_NONE_GET (ulong); +DEFINE_NONE_GET (int64); +DEFINE_NONE_GET (uint64); DEFINE_NONE_GET (float); DEFINE_NONE_GET (double); -#define DEFINE_NONE_GET_VALUE_ARRAY(type) \ -static gboolean \ -interpolate_none_get_##type##_value_array (GstControlledProperty * prop, \ - GstClockTime timestamp, GstValueArray * value_array) \ -{ \ - gint i; \ - GstClockTime ts = timestamp; \ - g##type *values = (g##type *) value_array->values; \ - \ - for(i = 0; i < value_array->nbsamples; i++) { \ - *values = g_value_get_##type (interpolate_none_get_##type (prop,ts)); \ - ts += value_array->sample_interval; \ - values++; \ - } \ - return TRUE; \ +static inline GValue * +_interpolate_none_get (GstInterpolationControlSource * self, + GstClockTime timestamp) +{ + GList *node; + GValue *ret; + + if ((node = + gst_interpolation_control_source_find_control_point_node (self, + timestamp))) { + GstControlPoint *cp = node->data; + + ret = &cp->value; + } else { + ret = gst_interpolation_control_source_get_first_value (self); + } + return ret; } -DEFINE_NONE_GET_VALUE_ARRAY (int); +static gboolean +interpolate_none_get (GstInterpolationControlSource * self, + GstClockTime timestamp, GValue * value) +{ + GValue *ret; -DEFINE_NONE_GET_VALUE_ARRAY (uint); -DEFINE_NONE_GET_VALUE_ARRAY (long); + g_mutex_lock (self->lock); + ret = _interpolate_none_get (self, timestamp); -DEFINE_NONE_GET_VALUE_ARRAY (ulong); -DEFINE_NONE_GET_VALUE_ARRAY (float); -DEFINE_NONE_GET_VALUE_ARRAY (double); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } -DEFINE_NONE_GET_VALUE_ARRAY (boolean); + g_value_copy (ret, value); + g_mutex_unlock (self->lock); + return TRUE; +} static gboolean -interpolate_none_get_enum_value_array (GstControlledProperty * prop, +interpolate_none_get_boolean_value_array (GstInterpolationControlSource * self, + GstClockTime timestamp, GstValueArray * value_array) +{ + gint i; + GstClockTime ts = timestamp; + gboolean *values = (gboolean *) value_array->values; + GValue *ret; + + g_mutex_lock (self->lock); + for (i = 0; i < value_array->nbsamples; i++) { + ret = _interpolate_none_get (self, timestamp); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } + *values = g_value_get_boolean (ret); + ts += value_array->sample_interval; + values++; + } + g_mutex_unlock (self->lock); + return TRUE; +} + +static gboolean +interpolate_none_get_enum_value_array (GstInterpolationControlSource * self, GstClockTime timestamp, GstValueArray * value_array) { gint i; GstClockTime ts = timestamp; gint *values = (gint *) value_array->values; + GValue *ret; + g_mutex_lock (self->lock); for (i = 0; i < value_array->nbsamples; i++) { - *values = g_value_get_enum (interpolate_none_get (prop, ts)); + ret = _interpolate_none_get (self, timestamp); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } + *values = g_value_get_enum (ret); ts += value_array->sample_interval; values++; } + g_mutex_unlock (self->lock); return TRUE; } static gboolean -interpolate_none_get_string_value_array (GstControlledProperty * prop, +interpolate_none_get_string_value_array (GstInterpolationControlSource * self, GstClockTime timestamp, GstValueArray * value_array) { gint i; GstClockTime ts = timestamp; gchar **values = (gchar **) value_array->values; + GValue *ret; + g_mutex_lock (self->lock); for (i = 0; i < value_array->nbsamples; i++) { - *values = (gchar *) g_value_get_string (interpolate_none_get (prop, ts)); + ret = _interpolate_none_get (self, timestamp); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } + *values = (gchar *) g_value_get_string (ret); ts += value_array->sample_interval; values++; } + g_mutex_unlock (self->lock); return TRUE; } static GstInterpolateMethod interpolate_none = { - interpolate_none_get_int, - interpolate_none_get_int_value_array, - interpolate_none_get_uint, - interpolate_none_get_uint_value_array, - interpolate_none_get_long, - interpolate_none_get_long_value_array, - interpolate_none_get_ulong, - interpolate_none_get_ulong_value_array, - interpolate_none_get_float, - interpolate_none_get_float_value_array, - interpolate_none_get_double, - interpolate_none_get_double_value_array, - interpolate_none_get, - interpolate_none_get_boolean_value_array, - interpolate_none_get, - interpolate_none_get_enum_value_array, - interpolate_none_get, - interpolate_none_get_string_value_array + (GstControlSourceGetValue) interpolate_none_get_int, + (GstControlSourceGetValueArray) interpolate_none_get_int_value_array, + (GstControlSourceGetValue) interpolate_none_get_uint, + (GstControlSourceGetValueArray) interpolate_none_get_uint_value_array, + (GstControlSourceGetValue) interpolate_none_get_long, + (GstControlSourceGetValueArray) interpolate_none_get_long_value_array, + (GstControlSourceGetValue) interpolate_none_get_ulong, + (GstControlSourceGetValueArray) interpolate_none_get_ulong_value_array, + (GstControlSourceGetValue) interpolate_none_get_int64, + (GstControlSourceGetValueArray) interpolate_none_get_int64_value_array, + (GstControlSourceGetValue) interpolate_none_get_uint64, + (GstControlSourceGetValueArray) interpolate_none_get_uint64_value_array, + (GstControlSourceGetValue) interpolate_none_get_float, + (GstControlSourceGetValueArray) interpolate_none_get_float_value_array, + (GstControlSourceGetValue) interpolate_none_get_double, + (GstControlSourceGetValueArray) interpolate_none_get_double_value_array, + (GstControlSourceGetValue) interpolate_none_get, + (GstControlSourceGetValueArray) interpolate_none_get_boolean_value_array, + (GstControlSourceGetValue) interpolate_none_get, + (GstControlSourceGetValueArray) interpolate_none_get_enum_value_array, + (GstControlSourceGetValue) interpolate_none_get, + (GstControlSourceGetValueArray) interpolate_none_get_string_value_array }; /* returns the default value of the property, except for times with specific values */ /* needed for one-shot events, such as notes and triggers */ -static GValue * -interpolate_trigger_get (GstControlledProperty * prop, GstClockTime timestamp) -{ - GList *node; - GstControlPoint *cp; - - /* check if there is a value at the registered timestamp */ - if ((node = - gst_controlled_property_find_control_point_node (prop, timestamp))) { - cp = node->data; - if (timestamp == cp->timestamp) { - return &cp->value; - } - } - - return &prop->default_value; -} - -#define interpolate_trigger_get_boolean interpolate_trigger_get - #define DEFINE_TRIGGER_GET(type) \ -static GValue * \ -interpolate_trigger_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \ +static inline GValue * \ +_interpolate_trigger_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp) \ { \ GList *node; \ GstControlPoint *cp; \ \ /* check if there is a value at the registered timestamp */ \ if ((node = \ - gst_controlled_property_find_control_point_node (prop, timestamp))) { \ + gst_interpolation_control_source_find_control_point_node (self, timestamp))) { \ cp = node->data; \ if (timestamp == cp->timestamp) { \ g##type ret = g_value_get_##type (&cp->value); \ - if (g_value_get_##type (&prop->min_value) > ret) \ - return &prop->min_value; \ - else if (g_value_get_##type (&prop->max_value) < ret) \ - return &prop->max_value; \ - return &cp->value; \ + if (g_value_get_##type (&self->priv->minimum_value) > ret) \ + return &self->priv->minimum_value; \ + else if (g_value_get_##type (&self->priv->maximum_value) < ret) \ + return &self->priv->maximum_value; \ + else \ + return &cp->value; \ } \ } \ \ - return &prop->default_value; \ -} - -DEFINE_TRIGGER_GET (int); -DEFINE_TRIGGER_GET (uint); -DEFINE_TRIGGER_GET (long); - -DEFINE_TRIGGER_GET (ulong); -DEFINE_TRIGGER_GET (float); -DEFINE_TRIGGER_GET (double); - -#define DEFINE_TRIGGER_GET_VALUE_ARRAY(type) \ + if (self->priv->nvalues > 0) \ + return &self->priv->default_value; \ + else \ + return NULL; \ +} \ +\ static gboolean \ -interpolate_trigger_get_##type##_value_array (GstControlledProperty * prop, \ +interpolate_trigger_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \ +{ \ + GValue *ret; \ + g_mutex_lock (self->lock); \ + ret = _interpolate_trigger_get_##type (self, timestamp); \ + if (!ret) { \ + g_mutex_unlock (self->lock); \ + return FALSE; \ + } \ + g_value_copy (ret, value); \ + g_mutex_unlock (self->lock); \ + return TRUE; \ +} \ +\ +static gboolean \ +interpolate_trigger_get_##type##_value_array (GstInterpolationControlSource *self, \ GstClockTime timestamp, GstValueArray * value_array) \ { \ gint i; \ GstClockTime ts = timestamp; \ g##type *values = (g##type *) value_array->values; \ + GValue *ret; \ \ + g_mutex_lock (self->lock); \ for(i = 0; i < value_array->nbsamples; i++) { \ - *values = g_value_get_##type (interpolate_trigger_get_##type (prop,ts)); \ + ret = _interpolate_trigger_get_##type (self, timestamp); \ + if (!ret) { \ + g_mutex_unlock (self->lock); \ + return FALSE; \ + } \ + *values = g_value_get_##type (ret); \ ts += value_array->sample_interval; \ values++; \ } \ + g_mutex_unlock (self->lock); \ return TRUE; \ } -DEFINE_TRIGGER_GET_VALUE_ARRAY (int); -DEFINE_TRIGGER_GET_VALUE_ARRAY (uint); -DEFINE_TRIGGER_GET_VALUE_ARRAY (long); +DEFINE_TRIGGER_GET (int); -DEFINE_TRIGGER_GET_VALUE_ARRAY (ulong); -DEFINE_TRIGGER_GET_VALUE_ARRAY (float); -DEFINE_TRIGGER_GET_VALUE_ARRAY (double); +DEFINE_TRIGGER_GET (uint); +DEFINE_TRIGGER_GET (long); -DEFINE_TRIGGER_GET_VALUE_ARRAY (boolean); +DEFINE_TRIGGER_GET (ulong); +DEFINE_TRIGGER_GET (int64); +DEFINE_TRIGGER_GET (uint64); +DEFINE_TRIGGER_GET (float); +DEFINE_TRIGGER_GET (double); + +static inline GValue * +_interpolate_trigger_get (GstInterpolationControlSource * self, + GstClockTime timestamp) +{ + GList *node; + GstControlPoint *cp; + + /* check if there is a value at the registered timestamp */ + if ((node = + gst_interpolation_control_source_find_control_point_node (self, + timestamp))) { + cp = node->data; + if (timestamp == cp->timestamp) { + return &cp->value; + } + } + if (self->priv->nvalues > 0) + return &self->priv->default_value; + else + return NULL; +} static gboolean -interpolate_trigger_get_enum_value_array (GstControlledProperty * prop, +interpolate_trigger_get (GstInterpolationControlSource * self, + GstClockTime timestamp, GValue * value) +{ + GValue *ret; + + g_mutex_lock (self->lock); + ret = _interpolate_trigger_get (self, timestamp); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } + g_value_copy (ret, value); + g_mutex_unlock (self->lock); + return TRUE; +} + +static gboolean +interpolate_trigger_get_boolean_value_array (GstInterpolationControlSource * + self, GstClockTime timestamp, GstValueArray * value_array) +{ + gint i; + GstClockTime ts = timestamp; + gint *values = (gint *) value_array->values; + GValue *ret; + + g_mutex_lock (self->lock); + for (i = 0; i < value_array->nbsamples; i++) { + ret = _interpolate_trigger_get (self, timestamp); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } + *values = g_value_get_boolean (ret); + ts += value_array->sample_interval; + values++; + } + g_mutex_unlock (self->lock); + return TRUE; +} + +static gboolean +interpolate_trigger_get_enum_value_array (GstInterpolationControlSource * self, GstClockTime timestamp, GstValueArray * value_array) { gint i; GstClockTime ts = timestamp; gint *values = (gint *) value_array->values; + GValue *ret; + g_mutex_lock (self->lock); for (i = 0; i < value_array->nbsamples; i++) { - *values = g_value_get_enum (interpolate_trigger_get (prop, ts)); + ret = _interpolate_trigger_get (self, timestamp); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } + *values = g_value_get_enum (ret); ts += value_array->sample_interval; values++; } + g_mutex_unlock (self->lock); return TRUE; } static gboolean -interpolate_trigger_get_string_value_array (GstControlledProperty * prop, - GstClockTime timestamp, GstValueArray * value_array) +interpolate_trigger_get_string_value_array (GstInterpolationControlSource * + self, GstClockTime timestamp, GstValueArray * value_array) { gint i; GstClockTime ts = timestamp; gchar **values = (gchar **) value_array->values; + GValue *ret; + g_mutex_lock (self->lock); for (i = 0; i < value_array->nbsamples; i++) { - *values = (gchar *) g_value_get_string (interpolate_trigger_get (prop, ts)); + ret = _interpolate_trigger_get (self, timestamp); + if (!ret) { + g_mutex_unlock (self->lock); + return FALSE; + } + *values = (gchar *) g_value_get_string (ret); ts += value_array->sample_interval; values++; } + g_mutex_unlock (self->lock); return TRUE; } static GstInterpolateMethod interpolate_trigger = { - interpolate_trigger_get_int, - interpolate_trigger_get_int_value_array, - interpolate_trigger_get_uint, - interpolate_trigger_get_uint_value_array, - interpolate_trigger_get_long, - interpolate_trigger_get_long_value_array, - interpolate_trigger_get_ulong, - interpolate_trigger_get_ulong_value_array, - interpolate_trigger_get_float, - interpolate_trigger_get_float_value_array, - interpolate_trigger_get_double, - interpolate_trigger_get_double_value_array, - interpolate_trigger_get, - interpolate_trigger_get_boolean_value_array, - interpolate_trigger_get, - interpolate_trigger_get_enum_value_array, - interpolate_trigger_get, - interpolate_trigger_get_string_value_array + (GstControlSourceGetValue) interpolate_trigger_get_int, + (GstControlSourceGetValueArray) interpolate_trigger_get_int_value_array, + (GstControlSourceGetValue) interpolate_trigger_get_uint, + (GstControlSourceGetValueArray) interpolate_trigger_get_uint_value_array, + (GstControlSourceGetValue) interpolate_trigger_get_long, + (GstControlSourceGetValueArray) interpolate_trigger_get_long_value_array, + (GstControlSourceGetValue) interpolate_trigger_get_ulong, + (GstControlSourceGetValueArray) interpolate_trigger_get_ulong_value_array, + (GstControlSourceGetValue) interpolate_trigger_get_int64, + (GstControlSourceGetValueArray) interpolate_trigger_get_int64_value_array, + (GstControlSourceGetValue) interpolate_trigger_get_uint64, + (GstControlSourceGetValueArray) interpolate_trigger_get_uint64_value_array, + (GstControlSourceGetValue) interpolate_trigger_get_float, + (GstControlSourceGetValueArray) interpolate_trigger_get_float_value_array, + (GstControlSourceGetValue) interpolate_trigger_get_double, + (GstControlSourceGetValueArray) interpolate_trigger_get_double_value_array, + (GstControlSourceGetValue) interpolate_trigger_get, + (GstControlSourceGetValueArray) interpolate_trigger_get_boolean_value_array, + (GstControlSourceGetValue) interpolate_trigger_get, + (GstControlSourceGetValueArray) interpolate_trigger_get_enum_value_array, + (GstControlSourceGetValue) interpolate_trigger_get, + (GstControlSourceGetValueArray) interpolate_trigger_get_string_value_array }; /* linear interpolation */ /* smoothes inbetween values */ #define DEFINE_LINEAR_GET(type) \ -static g##type \ -_interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \ +static inline gboolean \ +_interpolate_linear_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, g##type *ret) \ { \ GList *node; \ - g##type ret; \ \ - if ((node = gst_controlled_property_find_control_point_node (prop, timestamp))) { \ + if ((node = gst_interpolation_control_source_find_control_point_node (self, timestamp))) { \ GstControlPoint *cp1, *cp2; \ \ cp1 = node->data; \ @@ -361,38 +553,53 @@ _interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime times value2 = g_value_get_##type (&cp2->value); \ slope = (gdouble) (value2 - value1) / gst_guint64_to_gdouble (cp2->timestamp - cp1->timestamp); \ \ - ret = (g##type) (value1 + gst_guint64_to_gdouble (timestamp - cp1->timestamp) * slope); \ + *ret = (g##type) (value1 + gst_guint64_to_gdouble (timestamp - cp1->timestamp) * slope); \ } \ else { \ - ret = g_value_get_##type (&cp1->value); \ + *ret = g_value_get_##type (&cp1->value); \ } \ } else { \ - ret = g_value_get_##type (&prop->default_value); \ + GValue *first = gst_interpolation_control_source_get_first_value (self); \ + if (!first) \ + return FALSE; \ + *ret = g_value_get_##type (first); \ } \ - ret = CLAMP (ret, g_value_get_##type (&prop->min_value), g_value_get_##type (&prop->max_value)); \ - return ret; \ -} \ -\ -static GValue * \ -interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \ -{ \ - g_value_set_##type (&prop->result_value,_interpolate_linear_get_##type (prop,timestamp)); \ - return &prop->result_value; \ + *ret = CLAMP (*ret, g_value_get_##type (&self->priv->minimum_value), g_value_get_##type (&self->priv->maximum_value)); \ + return TRUE; \ } \ \ static gboolean \ -interpolate_linear_get_##type##_value_array (GstControlledProperty * prop, \ +interpolate_linear_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \ +{ \ + g##type ret; \ + g_mutex_lock (self->lock); \ + if (_interpolate_linear_get_##type (self, timestamp, &ret)) { \ + g_value_set_##type (value, ret); \ + g_mutex_unlock (self->lock); \ + return TRUE; \ + } \ + g_mutex_unlock (self->lock); \ + return FALSE; \ +} \ +\ +static gboolean \ +interpolate_linear_get_##type##_value_array (GstInterpolationControlSource *self, \ GstClockTime timestamp, GstValueArray * value_array) \ { \ gint i; \ GstClockTime ts = timestamp; \ g##type *values = (g##type *) value_array->values; \ \ + g_mutex_lock (self->lock); \ for(i = 0; i < value_array->nbsamples; i++) { \ - *values = _interpolate_linear_get_##type (prop, ts); \ + if (! _interpolate_linear_get_##type (self, ts, values)) { \ + g_mutex_unlock (self->lock); \ + return FALSE; \ + } \ ts += value_array->sample_interval; \ values++; \ } \ + g_mutex_unlock (self->lock); \ return TRUE; \ } @@ -402,28 +609,34 @@ DEFINE_LINEAR_GET (uint); DEFINE_LINEAR_GET (long); DEFINE_LINEAR_GET (ulong); +DEFINE_LINEAR_GET (int64); +DEFINE_LINEAR_GET (uint64); DEFINE_LINEAR_GET (float); DEFINE_LINEAR_GET (double); static GstInterpolateMethod interpolate_linear = { - interpolate_linear_get_int, - interpolate_linear_get_int_value_array, - interpolate_linear_get_uint, - interpolate_linear_get_uint_value_array, - interpolate_linear_get_long, - interpolate_linear_get_long_value_array, - interpolate_linear_get_ulong, - interpolate_linear_get_ulong_value_array, - interpolate_linear_get_float, - interpolate_linear_get_float_value_array, - interpolate_linear_get_double, - interpolate_linear_get_double_value_array, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL + (GstControlSourceGetValue) interpolate_linear_get_int, + (GstControlSourceGetValueArray) interpolate_linear_get_int_value_array, + (GstControlSourceGetValue) interpolate_linear_get_uint, + (GstControlSourceGetValueArray) interpolate_linear_get_uint_value_array, + (GstControlSourceGetValue) interpolate_linear_get_long, + (GstControlSourceGetValueArray) interpolate_linear_get_long_value_array, + (GstControlSourceGetValue) interpolate_linear_get_ulong, + (GstControlSourceGetValueArray) interpolate_linear_get_ulong_value_array, + (GstControlSourceGetValue) interpolate_linear_get_int64, + (GstControlSourceGetValueArray) interpolate_linear_get_int64_value_array, + (GstControlSourceGetValue) interpolate_linear_get_uint64, + (GstControlSourceGetValueArray) interpolate_linear_get_uint64_value_array, + (GstControlSourceGetValue) interpolate_linear_get_float, + (GstControlSourceGetValueArray) interpolate_linear_get_float_value_array, + (GstControlSourceGetValue) interpolate_linear_get_double, + (GstControlSourceGetValueArray) interpolate_linear_get_double_value_array, + (GstControlSourceGetValue) NULL, + (GstControlSourceGetValueArray) NULL, + (GstControlSourceGetValue) NULL, + (GstControlSourceGetValueArray) NULL, + (GstControlSourceGetValue) NULL, + (GstControlSourceGetValueArray) NULL }; /* square interpolation */ @@ -445,9 +658,9 @@ static GstInterpolateMethod interpolate_linear = { #define DEFINE_CUBIC_GET(type) \ static void \ -_interpolate_cubic_update_cache_##type (GstControlledProperty *prop) \ +_interpolate_cubic_update_cache_##type (GstInterpolationControlSource *self) \ { \ - gint i, n = prop->nvalues; \ + gint i, n = self->priv->nvalues; \ gdouble *o = g_new0 (gdouble, n); \ gdouble *p = g_new0 (gdouble, n); \ gdouble *q = g_new0 (gdouble, n); \ @@ -462,7 +675,7 @@ _interpolate_cubic_update_cache_##type (GstControlledProperty *prop) \ g##type y_prev, y, y_next; \ \ /* Fill linear system of equations */ \ - node = prop->values; \ + node = self->priv->values; \ cp = node->data; \ x = cp->timestamp; \ y = g_value_get_##type (&cp->value); \ @@ -508,7 +721,7 @@ _interpolate_cubic_update_cache_##type (GstControlledProperty *prop) \ \ /* Save cache next in the GstControlPoint */ \ \ - node = prop->values; \ + node = self->priv->values; \ for (i = 0; i < n; i++) { \ cp = node->data; \ cp->cache.cubic.h = h[i]; \ @@ -525,21 +738,20 @@ _interpolate_cubic_update_cache_##type (GstControlledProperty *prop) \ g_free (z); \ } \ \ -static g##type \ -_interpolate_cubic_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \ +static inline gboolean \ +_interpolate_cubic_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, g##type *ret) \ { \ GList *node; \ - g##type ret; \ \ - if (prop->nvalues <= 2) \ - return _interpolate_linear_get_##type (prop, timestamp); \ + if (self->priv->nvalues <= 2) \ + return _interpolate_linear_get_##type (self, timestamp, ret); \ \ - if (!prop->valid_cache) { \ - _interpolate_cubic_update_cache_##type (prop); \ - prop->valid_cache = TRUE; \ + if (!self->priv->valid_cache) { \ + _interpolate_cubic_update_cache_##type (self); \ + self->priv->valid_cache = TRUE; \ } \ \ - if ((node = gst_controlled_property_find_control_point_node (prop, timestamp))) { \ + if ((node = gst_interpolation_control_source_find_control_point_node (self, timestamp))) { \ GstControlPoint *cp1, *cp2; \ \ cp1 = node->data; \ @@ -560,38 +772,53 @@ _interpolate_cubic_get_##type (GstControlledProperty * prop, GstClockTime timest out += (value2 / cp1->cache.cubic.h - cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1; \ out += (value1 / cp1->cache.cubic.h - cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2; \ \ - ret = (g##type) out; \ + *ret = (g##type) out; \ } \ else { \ - ret = g_value_get_##type (&cp1->value); \ + *ret = g_value_get_##type (&cp1->value); \ } \ - } else {\ - ret = g_value_get_##type (&prop->default_value); \ + } else { \ + GValue *first = gst_interpolation_control_source_get_first_value (self); \ + if (!first) \ + return FALSE; \ + *ret = g_value_get_##type (first); \ } \ - ret = CLAMP (ret, g_value_get_##type (&prop->min_value), g_value_get_##type (&prop->max_value)); \ - return ret; \ -} \ -\ -static GValue * \ -interpolate_cubic_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \ -{ \ - g_value_set_##type (&prop->result_value,_interpolate_cubic_get_##type (prop,timestamp)); \ - return &prop->result_value; \ + *ret = CLAMP (*ret, g_value_get_##type (&self->priv->minimum_value), g_value_get_##type (&self->priv->maximum_value)); \ + return TRUE; \ } \ \ static gboolean \ -interpolate_cubic_get_##type##_value_array (GstControlledProperty * prop, \ +interpolate_cubic_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \ +{ \ + g##type ret; \ + g_mutex_lock (self->lock); \ + if (_interpolate_cubic_get_##type (self, timestamp, &ret)) { \ + g_value_set_##type (value, ret); \ + g_mutex_unlock (self->lock); \ + return TRUE; \ + } \ + g_mutex_unlock (self->lock); \ + return FALSE; \ +} \ +\ +static gboolean \ +interpolate_cubic_get_##type##_value_array (GstInterpolationControlSource *self, \ GstClockTime timestamp, GstValueArray * value_array) \ { \ gint i; \ GstClockTime ts = timestamp; \ g##type *values = (g##type *) value_array->values; \ \ + g_mutex_lock (self->lock); \ for(i = 0; i < value_array->nbsamples; i++) { \ - *values = _interpolate_cubic_get_##type (prop, ts); \ + if (! _interpolate_cubic_get_##type (self, ts, values)) { \ + g_mutex_unlock (self->lock); \ + return FALSE; \ + } \ ts += value_array->sample_interval; \ values++; \ } \ + g_mutex_unlock (self->lock); \ return TRUE; \ } @@ -601,28 +828,34 @@ DEFINE_CUBIC_GET (uint); DEFINE_CUBIC_GET (long); DEFINE_CUBIC_GET (ulong); +DEFINE_CUBIC_GET (int64); +DEFINE_CUBIC_GET (uint64); DEFINE_CUBIC_GET (float); DEFINE_CUBIC_GET (double); static GstInterpolateMethod interpolate_cubic = { - interpolate_cubic_get_int, - interpolate_cubic_get_int_value_array, - interpolate_cubic_get_uint, - interpolate_cubic_get_uint_value_array, - interpolate_cubic_get_long, - interpolate_cubic_get_long_value_array, - interpolate_cubic_get_ulong, - interpolate_cubic_get_ulong_value_array, - interpolate_cubic_get_float, - interpolate_cubic_get_float_value_array, - interpolate_cubic_get_double, - interpolate_cubic_get_double_value_array, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL + (GstControlSourceGetValue) interpolate_cubic_get_int, + (GstControlSourceGetValueArray) interpolate_cubic_get_int_value_array, + (GstControlSourceGetValue) interpolate_cubic_get_uint, + (GstControlSourceGetValueArray) interpolate_cubic_get_uint_value_array, + (GstControlSourceGetValue) interpolate_cubic_get_long, + (GstControlSourceGetValueArray) interpolate_cubic_get_long_value_array, + (GstControlSourceGetValue) interpolate_cubic_get_ulong, + (GstControlSourceGetValueArray) interpolate_cubic_get_ulong_value_array, + (GstControlSourceGetValue) interpolate_cubic_get_int64, + (GstControlSourceGetValueArray) interpolate_cubic_get_int64_value_array, + (GstControlSourceGetValue) interpolate_cubic_get_uint64, + (GstControlSourceGetValueArray) interpolate_cubic_get_uint64_value_array, + (GstControlSourceGetValue) interpolate_cubic_get_float, + (GstControlSourceGetValueArray) interpolate_cubic_get_float_value_array, + (GstControlSourceGetValue) interpolate_cubic_get_double, + (GstControlSourceGetValueArray) interpolate_cubic_get_double_value_array, + (GstControlSourceGetValue) NULL, + (GstControlSourceGetValueArray) NULL, + (GstControlSourceGetValue) NULL, + (GstControlSourceGetValueArray) NULL, + (GstControlSourceGetValue) NULL, + (GstControlSourceGetValueArray) NULL }; diff --git a/libs/gst/controller/gstinterpolationcontrolsource.c b/libs/gst/controller/gstinterpolationcontrolsource.c new file mode 100644 index 0000000000..30e98660de --- /dev/null +++ b/libs/gst/controller/gstinterpolationcontrolsource.c @@ -0,0 +1,667 @@ +/* GStreamer + * + * Copyright (C) 2007 Sebastian Dröge + * + * gstinterpolationcontrolsource.c: Control source that provides several + * interpolation methods + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gstinterpolationcontrolsource + * @short_description: interpolation control source + * + * #GstInterpolationControlSource is a #GstControlSource, that interpolates values between user-given + * control points. It supports several interpolation modes and property types. + * + * To use #GstInterpolationControlSource get a new instance by calling + * gst_interpolation_control_source_new(), bind it to a #GParamSpec, select a interpolation mode with + * gst_interpolation_control_source_set_interpolation_mode() and set some control points by calling + * gst_interpolation_control_source_set(). + * + * All functions are MT-safe. + * + */ + +#include +#include + +#include "gstcontrolsource.h" +#include "gstinterpolationcontrolsource.h" +#include "gstinterpolationcontrolsourceprivate.h" + +extern GstInterpolateMethod *interpolation_methods[]; +extern guint num_interpolation_methods; + +static void gst_interpolation_control_source_init (GstInterpolationControlSource + * self); +static void +gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass + * klass); + +G_DEFINE_TYPE (GstInterpolationControlSource, gst_interpolation_control_source, + GST_TYPE_CONTROL_SOURCE); + +static GObjectClass *parent_class = NULL; + +/* + * gst_control_point_free: + * @prop: the object to free + * + * Private method which frees all data allocated by a #GstControlPoint + * instance. + */ +static void +gst_control_point_free (GstControlPoint * cp) +{ + g_return_if_fail (cp); + + g_value_unset (&cp->value); + g_free (cp); +} + +static void +gst_interpolation_control_source_reset (GstInterpolationControlSource * self) +{ + GstControlSource *csource = GST_CONTROL_SOURCE (self); + + csource->get_value = NULL; + csource->get_value_array = NULL; + + self->priv->type = self->priv->base = G_TYPE_INVALID; + + if (G_IS_VALUE (&self->priv->default_value)) + g_value_unset (&self->priv->default_value); + if (G_IS_VALUE (&self->priv->minimum_value)) + g_value_unset (&self->priv->minimum_value); + if (G_IS_VALUE (&self->priv->maximum_value)) + g_value_unset (&self->priv->maximum_value); + + if (self->priv->values) { + g_list_foreach (self->priv->values, (GFunc) gst_control_point_free, NULL); + g_list_free (self->priv->values); + self->priv->values = NULL; + } + + self->priv->nvalues = 0; + self->priv->last_requested_value = NULL; + self->priv->valid_cache = FALSE; +} + +/** + * gst_interpolation_control_source_new: + * + * This returns a new, unbound #GstInterpolationControlSource. + * + * Returns: a new, unbound #GstInterpolationControlSource. + */ +GstInterpolationControlSource * +gst_interpolation_control_source_new () +{ + return g_object_new (GST_TYPE_INTERPOLATION_CONTROL_SOURCE, NULL); +} + +/** + * gst_interpolation_control_source_set_interpolation_mode: + * @self: the #GstInterpolationControlSource object + * @mode: interpolation mode + * + * Sets the given interpolation mode. + * + * User interpolation is not yet available and quadratic interpolation + * is deprecated and maps to cubic interpolation. + * + * Returns: %TRUE if the interpolation mode could be set, %FALSE otherwise + */ +gboolean + gst_interpolation_control_source_set_interpolation_mode + (GstInterpolationControlSource * self, GstInterpolateMode mode) { + gboolean ret = TRUE; + GstControlSource *csource = GST_CONTROL_SOURCE (self); + + if (mode >= num_interpolation_methods || interpolation_methods[mode] == NULL) { + GST_WARNING ("interpolation mode %d invalid or not implemented yet", mode); + return FALSE; + } + + if (mode == GST_INTERPOLATE_QUADRATIC) { + GST_WARNING ("Quadratic interpolation mode is deprecated, using cubic" + "interpolation mode"); + } + + if (mode == GST_INTERPOLATE_USER) { + GST_WARNING ("User interpolation mode is not implemented yet"); + return FALSE; + } + + g_mutex_lock (self->lock); + switch (self->priv->base) { + case G_TYPE_INT: + csource->get_value = interpolation_methods[mode]->get_int; + csource->get_value_array = + interpolation_methods[mode]->get_int_value_array; + break; + case G_TYPE_UINT:{ + csource->get_value = interpolation_methods[mode]->get_uint; + csource->get_value_array = + interpolation_methods[mode]->get_uint_value_array; + break; + } + case G_TYPE_LONG:{ + csource->get_value = interpolation_methods[mode]->get_long; + csource->get_value_array = + interpolation_methods[mode]->get_long_value_array; + break; + } + case G_TYPE_ULONG:{ + csource->get_value = interpolation_methods[mode]->get_ulong; + csource->get_value_array = + interpolation_methods[mode]->get_ulong_value_array; + break; + } + case G_TYPE_INT64:{ + csource->get_value = interpolation_methods[mode]->get_int64; + csource->get_value_array = + interpolation_methods[mode]->get_int64_value_array; + break; + } + case G_TYPE_UINT64:{ + csource->get_value = interpolation_methods[mode]->get_uint64; + csource->get_value_array = + interpolation_methods[mode]->get_uint64_value_array; + break; + } + case G_TYPE_FLOAT:{ + csource->get_value = interpolation_methods[mode]->get_float; + csource->get_value_array = + interpolation_methods[mode]->get_float_value_array; + break; + } + case G_TYPE_DOUBLE:{ + csource->get_value = interpolation_methods[mode]->get_double; + csource->get_value_array = + interpolation_methods[mode]->get_double_value_array; + break; + } + case G_TYPE_BOOLEAN:{ + csource->get_value = interpolation_methods[mode]->get_boolean; + csource->get_value_array = + interpolation_methods[mode]->get_boolean_value_array; + break; + } + case G_TYPE_ENUM:{ + csource->get_value = interpolation_methods[mode]->get_enum; + csource->get_value_array = + interpolation_methods[mode]->get_enum_value_array; + break; + } + case G_TYPE_STRING:{ + csource->get_value = interpolation_methods[mode]->get_string; + csource->get_value_array = + interpolation_methods[mode]->get_string_value_array; + break; + } + default: + ret = FALSE; + break; + } + + /* Incomplete implementation */ + if (!ret || !csource->get_value || !csource->get_value_array) { + gst_interpolation_control_source_reset (self); + ret = FALSE; + } + + self->priv->valid_cache = FALSE; + self->priv->interpolation_mode = mode; + + g_mutex_unlock (self->lock); + + return ret; +} + +static gboolean +gst_interpolation_control_source_bind (GstControlSource * source, + GParamSpec * pspec) +{ + GType type, base; + GstInterpolationControlSource *self = + GST_INTERPOLATION_CONTROL_SOURCE (source); + gboolean ret = TRUE; + + /* get the fundamental base type */ + self->priv->type = base = type = G_PARAM_SPEC_VALUE_TYPE (pspec); + while ((type = g_type_parent (type))) + base = type; + + self->priv->base = base; + /* restore type */ + type = self->priv->type; + + if (!gst_interpolation_control_source_set_interpolation_mode (self, + self->priv->interpolation_mode)) + return FALSE; + + switch (base) { + case G_TYPE_INT:{ + GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_int (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_int (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_int (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_UINT:{ + GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_uint (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_uint (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_uint (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_LONG:{ + GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_long (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_long (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_long (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_ULONG:{ + GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_ulong (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_ulong (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_ulong (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_INT64:{ + GParamSpecInt64 *tpspec = G_PARAM_SPEC_INT64 (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_int64 (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_int64 (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_int64 (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_UINT64:{ + GParamSpecUInt64 *tpspec = G_PARAM_SPEC_UINT64 (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_uint64 (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_uint64 (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_uint64 (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_FLOAT:{ + GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_float (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_float (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_float (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_DOUBLE:{ + GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_double (&self->priv->default_value, tpspec->default_value); + g_value_init (&self->priv->minimum_value, type); + g_value_set_double (&self->priv->minimum_value, tpspec->minimum); + g_value_init (&self->priv->maximum_value, type); + g_value_set_double (&self->priv->maximum_value, tpspec->maximum); + break; + } + case G_TYPE_BOOLEAN:{ + GParamSpecBoolean *tpspec = G_PARAM_SPEC_BOOLEAN (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_boolean (&self->priv->default_value, tpspec->default_value); + break; + } + case G_TYPE_ENUM:{ + GParamSpecEnum *tpspec = G_PARAM_SPEC_ENUM (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_enum (&self->priv->default_value, tpspec->default_value); + break; + } + case G_TYPE_STRING:{ + GParamSpecString *tpspec = G_PARAM_SPEC_STRING (pspec); + + g_value_init (&self->priv->default_value, type); + g_value_set_string (&self->priv->default_value, tpspec->default_value); + break; + } + default: + GST_WARNING ("incomplete implementation for paramspec type '%s'", + G_PARAM_SPEC_TYPE_NAME (pspec)); + ret = FALSE; + break; + } + + if (ret) { + self->priv->valid_cache = FALSE; + self->priv->nvalues = 0; + } else { + gst_interpolation_control_source_reset (self); + } + + return ret; +} + +/* + * gst_control_point_compare: + * @p1: a pointer to a #GstControlPoint + * @p2: a pointer to a #GstControlPoint + * + * Compare function for g_list operations that operates on two #GstControlPoint + * parameters. + */ +static gint +gst_control_point_compare (gconstpointer p1, gconstpointer p2) +{ + GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp; + GstClockTime ct2 = ((GstControlPoint *) p2)->timestamp; + + return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1)); +} + +/* + * gst_control_point_find: + * @p1: a pointer to a #GstControlPoint + * @p2: a pointer to a #GstClockTime + * + * Compare function for g_list operations that operates on a #GstControlPoint and + * a #GstClockTime. + */ +static gint +gst_control_point_find (gconstpointer p1, gconstpointer p2) +{ + GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp; + GstClockTime ct2 = *(GstClockTime *) p2; + + return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1)); +} + +static void +gst_interpolation_control_source_set_internal (GstInterpolationControlSource * + self, GstClockTime timestamp, GValue * value) +{ + GstControlPoint *cp; + GList *node; + + /* check if a control point for the timestamp already exists */ + if ((node = g_list_find_custom (self->priv->values, ×tamp, + gst_control_point_find))) { + cp = node->data; + g_value_reset (&cp->value); + g_value_copy (value, &cp->value); + } else { + /* create a new GstControlPoint */ + cp = g_new0 (GstControlPoint, 1); + cp->timestamp = timestamp; + g_value_init (&cp->value, self->priv->type); + g_value_copy (value, &cp->value); + /* and sort it into the prop->values list */ + self->priv->values = + g_list_insert_sorted (self->priv->values, cp, + gst_control_point_compare); + self->priv->nvalues++; + } + self->priv->valid_cache = FALSE; +} + +/** + * gst_interpolation_control_source_set: + * @self: the #GstInterpolationControlSource object + * @timestamp: the time the control-change is scheduled for + * @value: the control-value + * + * Set the value of given controller-handled property at a certain time. + * + * Returns: FALSE if the values couldn't be set, TRUE otherwise. + */ +gboolean +gst_interpolation_control_source_set (GstInterpolationControlSource * self, + GstClockTime timestamp, GValue * value) +{ + g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + g_return_val_if_fail (G_VALUE_TYPE (value) == self->priv->type, FALSE); + + g_mutex_lock (self->lock); + gst_interpolation_control_source_set_internal (self, timestamp, value); + g_mutex_unlock (self->lock); + + return TRUE; +} + +/** + * gst_interpolation_control_source_set_from_list: + * @self: the #GstInterpolationControlSource object + * @timedvalues: a list with #GstTimedValue items + * + * Sets multiple timed values at once. + * + * Returns: FALSE if the values couldn't be set, TRUE otherwise. + */ +gboolean +gst_interpolation_control_source_set_from_list (GstInterpolationControlSource * + self, GSList * timedvalues) +{ + GSList *node; + GstTimedValue *tv; + gboolean res = FALSE; + + g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), FALSE); + + for (node = timedvalues; node; node = g_slist_next (node)) { + tv = node->data; + if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) { + GST_WARNING ("GstTimedValued with invalid timestamp passed to %s", + GST_FUNCTION); + } else if (!G_IS_VALUE (&tv->value)) { + GST_WARNING ("GstTimedValued with invalid value passed to %s", + GST_FUNCTION); + } else if (G_VALUE_TYPE (&tv->value) != self->priv->type) { + GST_WARNING ("incompatible value type for property"); + } else { + g_mutex_lock (self->lock); + gst_interpolation_control_source_set_internal (self, tv->timestamp, + &tv->value); + g_mutex_unlock (self->lock); + res = TRUE; + } + } + return res; +} + +/** + * gst_interpolation_control_source_unset: + * @self: the #GstInterpolationControlSource object + * @timestamp: the time the control-change should be removed from + * + * Used to remove the value of given controller-handled property at a certain + * time. + * + * Returns: FALSE if the value couldn't be unset (i.e. not found, TRUE otherwise. + */ +gboolean +gst_interpolation_control_source_unset (GstInterpolationControlSource * self, + GstClockTime timestamp) +{ + GList *node; + gboolean res = FALSE; + + g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + + g_mutex_lock (self->lock); + /* check if a control point for the timestamp exists */ + if ((node = g_list_find_custom (self->priv->values, ×tamp, + gst_control_point_find))) { + GstControlPoint *cp = node->data; + + if (cp->timestamp == 0) { + /* Restore the default node */ + g_value_reset (&cp->value); + g_value_copy (&self->priv->default_value, &cp->value); + } else { + if (node == self->priv->last_requested_value) + self->priv->last_requested_value = NULL; + gst_control_point_free (node->data); /* free GstControlPoint */ + self->priv->values = g_list_delete_link (self->priv->values, node); + self->priv->nvalues--; + } + self->priv->valid_cache = FALSE; + res = TRUE; + } + g_mutex_unlock (self->lock); + + return res; +} + +/** + * gst_interpolation_control_source_unset_all: + * @self: the #GstInterpolationControlSource object + * + * Used to remove all time-stamped values of given controller-handled property + * + */ +void +gst_interpolation_control_source_unset_all (GstInterpolationControlSource * + self) +{ + g_return_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self)); + + g_mutex_lock (self->lock); + /* free GstControlPoint structures */ + g_list_foreach (self->priv->values, (GFunc) gst_control_point_free, NULL); + g_list_free (self->priv->values); + self->priv->last_requested_value = NULL; + self->priv->values = NULL; + self->priv->nvalues = 0; + self->priv->valid_cache = FALSE; + + g_mutex_unlock (self->lock); +} + +/** + * gst_interpolation_control_source_get_all: + * @self: the #GstInterpolationControlSource to get the list from + * + * Returns a read-only copy of the list of #GstTimedValue for the given property. + * Free the list after done with it. + * + * Returns: a copy of the list, or %NULL if the property isn't handled by the controller + */ +GList * +gst_interpolation_control_source_get_all (GstInterpolationControlSource * self) +{ + GList *res = NULL; + + g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), NULL); + + g_mutex_lock (self->lock); + if (self->priv->values) + res = g_list_copy (self->priv->values); + g_mutex_unlock (self->lock); + + return res; +} + +/** + * gst_interpolation_control_source_get_count: + * @self: the #GstInterpolationControlSource to get the number of values from + * + * Returns the number of control points that are set. + * + * Returns: the number of control points that are set. + * + */ +gint +gst_interpolation_control_source_get_count (GstInterpolationControlSource * + self) +{ + g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), 0); + return self->priv->nvalues; +} + +static void +gst_interpolation_control_source_init (GstInterpolationControlSource * self) +{ + self->lock = g_mutex_new (); + self->priv = + G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_INTERPOLATION_CONTROL_SOURCE, + GstInterpolationControlSourcePrivate); + self->priv->interpolation_mode = GST_INTERPOLATE_NONE; +} + +static void +gst_interpolation_control_source_finalize (GObject * obj) +{ + GstInterpolationControlSource *self = GST_INTERPOLATION_CONTROL_SOURCE (obj); + + g_mutex_lock (self->lock); + gst_interpolation_control_source_reset (self); + g_mutex_unlock (self->lock); + g_mutex_free (self->lock); + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_interpolation_control_source_dispose (GObject * obj) +{ + G_OBJECT_CLASS (parent_class)->dispose (obj); +} + +static void +gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass + * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (klass, + sizeof (GstInterpolationControlSourcePrivate)); + + gobject_class->finalize = gst_interpolation_control_source_finalize; + gobject_class->dispose = gst_interpolation_control_source_dispose; + csource_class->bind = gst_interpolation_control_source_bind; +} diff --git a/libs/gst/controller/gstinterpolationcontrolsource.h b/libs/gst/controller/gstinterpolationcontrolsource.h new file mode 100644 index 0000000000..233909d576 --- /dev/null +++ b/libs/gst/controller/gstinterpolationcontrolsource.h @@ -0,0 +1,109 @@ +/* GStreamer + * + * Copyright (C) 2007 Sebastian Dröge + * + * gstinterpolationcontrolsource.h: Control source that provides several + * interpolation methods + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_INTERPOLATION_CONTROL_SOURCE_H__ +#define __GST_INTERPOLATION_CONTROL_SOURCE_H__ + +#include +#include + +#include "gstcontrolsource.h" + +G_BEGIN_DECLS + +#define GST_TYPE_INTERPOLATION_CONTROL_SOURCE \ + (gst_interpolation_control_source_get_type ()) +#define GST_INTERPOLATION_CONTROL_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INTERPOLATION_CONTROL_SOURCE, GstInterpolationControlSource)) +#define GST_INTERPOLATION_CONTROL_SOURCE_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_CAST ((vtable), GST_TYPE_INTERPOLATION_CONTROL_SOURCE, GstInterpolationControlSourceClass)) +#define GST_IS_INTERPOLATION_CONTROL_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INTERPOLATION_CONTROL_SOURCE)) +#define GST_IS_INTERPOLATION_CONTROL_SOURCE_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_TYPE ((vtable), GST_TYPE_INTERPOLATION_CONTROL_SOURCE)) +#define GST_INTERPOLATION_CONTROL_SOURCE_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_CLASS ((inst), GST_TYPE_INTERPOLATION_CONTROL_SOURCE, GstInterpolationControlSourceClass)) + +typedef struct _GstInterpolationControlSource GstInterpolationControlSource; +typedef struct _GstInterpolationControlSourceClass GstInterpolationControlSourceClass; +typedef struct _GstInterpolationControlSourcePrivate GstInterpolationControlSourcePrivate; + +/** + * GstInterpolateMode: + * @GST_INTERPOLATE_NONE: steps-like interpolation, default + * @GST_INTERPOLATE_TRIGGER: returns the default value of the property, + * except for times with specific values + * @GST_INTERPOLATE_LINEAR: linear interpolation + * @GST_INTERPOLATE_QUADRATIC: square interpolation (deprecated, maps to cubic) + * @GST_INTERPOLATE_CUBIC: cubic interpolation + * @GST_INTERPOLATE_USER: user-provided interpolation (not yet available) + * + * The various interpolation modes available. + */ +typedef enum +{ + GST_INTERPOLATE_NONE, + GST_INTERPOLATE_TRIGGER, + GST_INTERPOLATE_LINEAR, + GST_INTERPOLATE_QUADRATIC, + GST_INTERPOLATE_CUBIC, + GST_INTERPOLATE_USER +} GstInterpolateMode; + +/** + * GstInterpolationControlSource: + * + * The instance structure of #GstControlSource. + */ +struct _GstInterpolationControlSource { + GstControlSource parent; + + /* */ + GMutex *lock; + GstInterpolationControlSourcePrivate *priv; + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstInterpolationControlSourceClass { + GstControlSourceClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_interpolation_control_source_get_type (); + +/* Functions */ + +GstInterpolationControlSource *gst_interpolation_control_source_new (); +gboolean gst_interpolation_control_source_set_interpolation_mode (GstInterpolationControlSource *self, GstInterpolateMode mode); +gboolean gst_interpolation_control_source_set (GstInterpolationControlSource * self, GstClockTime timestamp, GValue * value); +gboolean gst_interpolation_control_source_set_from_list (GstInterpolationControlSource * self, GSList * timedvalues); +gboolean gst_interpolation_control_source_unset (GstInterpolationControlSource * self, GstClockTime timestamp); +void gst_interpolation_control_source_unset_all (GstInterpolationControlSource *self); +GList *gst_interpolation_control_source_get_all (GstInterpolationControlSource * self); +gint gst_interpolation_control_source_get_count (GstInterpolationControlSource * self); + +G_END_DECLS + +#endif /* __GST_INTERPOLATION_CONTROL_SOURCE_H__ */ diff --git a/libs/gst/controller/gstinterpolationcontrolsourceprivate.h b/libs/gst/controller/gstinterpolationcontrolsourceprivate.h new file mode 100644 index 0000000000..5258d56d4d --- /dev/null +++ b/libs/gst/controller/gstinterpolationcontrolsourceprivate.h @@ -0,0 +1,100 @@ +/* GStreamer + * + * Copyright (C) 2007 Sebastian Dröge + * + * gstinterpolationcontrolsourceprivate.h: Private declarations for the + * GstInterpolationControlSource + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_INTERPOLATION_CONTROL_SOURCE_PRIVATE_H__ +#define __GST_INTERPOLATION_CONTROL_SOURCE_PRIVATE_H__ + +/** + * GstInterpolateMethod: + * + * Function pointer structure to do user-defined interpolation methods + */ +typedef struct _GstInterpolateMethod +{ + GstControlSourceGetValue get_int; + GstControlSourceGetValueArray get_int_value_array; + GstControlSourceGetValue get_uint; + GstControlSourceGetValueArray get_uint_value_array; + GstControlSourceGetValue get_long; + GstControlSourceGetValueArray get_long_value_array; + GstControlSourceGetValue get_ulong; + GstControlSourceGetValueArray get_ulong_value_array; + GstControlSourceGetValue get_int64; + GstControlSourceGetValueArray get_int64_value_array; + GstControlSourceGetValue get_uint64; + GstControlSourceGetValueArray get_uint64_value_array; + GstControlSourceGetValue get_float; + GstControlSourceGetValueArray get_float_value_array; + GstControlSourceGetValue get_double; + GstControlSourceGetValueArray get_double_value_array; + GstControlSourceGetValue get_boolean; + GstControlSourceGetValueArray get_boolean_value_array; + GstControlSourceGetValue get_enum; + GstControlSourceGetValueArray get_enum_value_array; + GstControlSourceGetValue get_string; + GstControlSourceGetValueArray get_string_value_array; +} GstInterpolateMethod; + +/** + * GstControlPoint: + * + * a internal structure for value+time and various temporary + * values used for interpolation. This "inherits" from + * GstTimedValue. + */ +typedef struct _GstControlPoint +{ + /* fields from GstTimedValue. DO NOT CHANGE! */ + GstClockTime timestamp; /* timestamp of the value change */ + GValue value; /* the new value */ + + /* internal fields */ + + /* Caches for the interpolators */ + union { + struct { + gdouble h; + gdouble z; + } cubic; + } cache; + +} GstControlPoint; + +struct _GstInterpolationControlSourcePrivate +{ + GType type; /* type of the handled property */ + GType base; /* base-type of the handled property */ + + GValue default_value; /* default value for the handled property */ + GValue minimum_value; /* min value for the handled property */ + GValue maximum_value; /* max value for the handled property */ + GstInterpolateMode interpolation_mode; + + GList *values; /* List of GstControlPoint */ + gint nvalues; /* Number of control points */ + GList *last_requested_value; /* last search result, can be used for incremental searches */ + gboolean valid_cache; +}; + +#endif /* __GST_INTERPOLATION_CONTROL_SOURCE_PRIVATE_H__ */ + diff --git a/tests/check/libs/controller.c b/tests/check/libs/controller.c index ac9ac17443..08e0aa59ef 100644 --- a/tests/check/libs/controller.c +++ b/tests/check/libs/controller.c @@ -2,7 +2,8 @@ * * unit test for the controller library * - * Copyright (C) <2005> Stefan Kost + * Copyright (C) <2005> Stefan Kost + * Copyright (C) <2006> Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,6 +27,8 @@ #include #include #include +#include +#include /* LOCAL TEST ELEMENT */ @@ -507,10 +510,53 @@ GST_START_TEST (controller_finalize) GST_END_TEST; +/* tests if we cleanup properly */ +GST_START_TEST (controller_controlsource_refcounts) +{ + GstController *ctrl; + GstElement *elem; + GstControlSource *csource, *test_csource; + + gst_controller_init (NULL, NULL); + + elem = gst_element_factory_make ("testmonosource", "test_source"); + + /* that property should exist and should be controllable */ + ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); + fail_unless (ctrl != NULL, NULL); + + csource = (GstControlSource *) gst_interpolation_control_source_new (); + fail_unless (csource != NULL, NULL); + + fail_unless_equals_int (G_OBJECT (csource)->ref_count, 1); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", csource)); + fail_unless_equals_int (G_OBJECT (csource)->ref_count, 2); + + g_object_unref (G_OBJECT (csource)); + + test_csource = gst_controller_get_control_source (ctrl, "ulong"); + fail_unless (test_csource != NULL, NULL); + fail_unless (test_csource == csource); + fail_unless_equals_int (G_OBJECT (csource)->ref_count, 2); + g_object_unref (csource); + + /* free the controller */ + g_object_unref (ctrl); + + /* object shouldn't have a controller anymore */ + ctrl = gst_object_get_controller (G_OBJECT (elem)); + fail_unless (ctrl == NULL, NULL); + + gst_object_unref (elem); +} + +GST_END_TEST; + /* test timed value handling without interpolation */ GST_START_TEST (controller_interpolate_none) { GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gboolean res; GValue val_ulong = { 0, }; @@ -523,18 +569,35 @@ GST_START_TEST (controller_interpolate_none) ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_NONE)); + fail_unless (gst_interpolation_control_source_get_count (csource) == 0); + /* set control values */ g_value_init (&val_ulong, G_TYPE_ULONG); g_value_set_ulong (&val_ulong, 0); - res = gst_controller_set (ctrl, "ulong", 0 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); + fail_unless (gst_interpolation_control_source_get_count (csource) == 1); g_value_set_ulong (&val_ulong, 100); - res = gst_controller_set (ctrl, "ulong", 2 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); + fail_unless (gst_interpolation_control_source_get_count (csource) == 2); + + g_object_unref (G_OBJECT (csource)); /* now pull in values for some timestamps */ gst_controller_sync_values (ctrl, 0 * GST_SECOND); @@ -555,6 +618,7 @@ GST_END_TEST; GST_START_TEST (controller_interpolate_trigger) { GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gboolean res; GValue val_ulong = { 0, }; @@ -567,29 +631,50 @@ GST_START_TEST (controller_interpolate_trigger) ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_TRIGGER)); - /* set control values */ g_value_init (&val_ulong, G_TYPE_ULONG); + fail_if (gst_control_source_get_value (GST_CONTROL_SOURCE (csource), + 0 * GST_SECOND, &val_ulong)); + + /* set control values */ g_value_set_ulong (&val_ulong, 50); - res = gst_controller_set (ctrl, "ulong", 0 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); g_value_set_ulong (&val_ulong, 100); - res = gst_controller_set (ctrl, "ulong", 2 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); + /* now pull in values for some timestamps */ + fail_unless (gst_control_source_get_value (GST_CONTROL_SOURCE (csource), + 0 * GST_SECOND, &val_ulong)); gst_controller_sync_values (ctrl, 0 * GST_SECOND); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); - GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; + fail_unless (gst_control_source_get_value (GST_CONTROL_SOURCE (csource), + 1 * GST_SECOND, &val_ulong)); gst_controller_sync_values (ctrl, 1 * GST_SECOND); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (gst_control_source_get_value (GST_CONTROL_SOURCE (csource), + 2 * GST_SECOND, &val_ulong)); gst_controller_sync_values (ctrl, 2 * GST_SECOND); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); + g_object_unref (G_OBJECT (csource)); g_object_unref (ctrl); gst_object_unref (elem); } @@ -600,6 +685,7 @@ GST_END_TEST; GST_START_TEST (controller_interpolate_linear) { GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gboolean res; GValue val_ulong = { 0, }; @@ -612,19 +698,32 @@ GST_START_TEST (controller_interpolate_linear) ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_LINEAR)); /* set control values */ g_value_init (&val_ulong, G_TYPE_ULONG); g_value_set_ulong (&val_ulong, 0); - res = gst_controller_set (ctrl, "ulong", 0 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); g_value_set_ulong (&val_ulong, 100); - res = gst_controller_set (ctrl, "ulong", 2 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); + g_object_unref (G_OBJECT (csource)); + /* now pull in values for some timestamps */ gst_controller_sync_values (ctrl, 0 * GST_SECOND); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); @@ -644,6 +743,7 @@ GST_END_TEST; GST_START_TEST (controller_interpolate_cubic) { GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gboolean res; GValue val_double = { 0, }; @@ -656,25 +756,42 @@ GST_START_TEST (controller_interpolate_cubic) ctrl = gst_controller_new (G_OBJECT (elem), "double", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "double", + GST_CONTROL_SOURCE (csource))); + /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "double", + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_CUBIC)); /* set control values */ g_value_init (&val_double, G_TYPE_DOUBLE); g_value_set_double (&val_double, 0.0); - res = gst_controller_set (ctrl, "double", 0 * GST_SECOND, &val_double); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_double); fail_unless (res, NULL); g_value_set_double (&val_double, 5.0); - res = gst_controller_set (ctrl, "double", 1 * GST_SECOND, &val_double); + res = + gst_interpolation_control_source_set (csource, 1 * GST_SECOND, + &val_double); fail_unless (res, NULL); g_value_set_double (&val_double, 2.0); - res = gst_controller_set (ctrl, "double", 2 * GST_SECOND, &val_double); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_double); fail_unless (res, NULL); g_value_set_double (&val_double, 8.0); - res = gst_controller_set (ctrl, "double", 4 * GST_SECOND, &val_double); + res = + gst_interpolation_control_source_set (csource, 4 * GST_SECOND, + &val_double); fail_unless (res, NULL); + g_object_unref (G_OBJECT (csource)); + /* now pull in values for some timestamps */ gst_controller_sync_values (ctrl, 0 * GST_SECOND); fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_double, 0.0); @@ -702,6 +819,7 @@ GST_END_TEST; GST_START_TEST (controller_interpolate_unimplemented) { GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gst_controller_init (NULL, NULL); @@ -712,10 +830,19 @@ GST_START_TEST (controller_interpolate_unimplemented) ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + /* set completely bogus interpolation mode */ - fail_if (gst_controller_set_interpolation_mode (ctrl, "ulong", + fail_if (gst_interpolation_control_source_set_interpolation_mode (csource, (GstInterpolateMode) 93871)); + g_object_unref (G_OBJECT (csource)); + g_object_unref (ctrl); gst_object_unref (elem); } @@ -723,9 +850,10 @@ GST_START_TEST (controller_interpolate_unimplemented) GST_END_TEST; /* test _unset() */ -GST_START_TEST (controller_unset) +GST_START_TEST (controller_interpolation_unset) { GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gboolean res; GValue val_ulong = { 0, }; @@ -738,20 +866,33 @@ GST_START_TEST (controller_unset) ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_NONE)); /* set control values */ g_value_init (&val_ulong, G_TYPE_ULONG); g_value_set_ulong (&val_ulong, 0); - res = gst_controller_set (ctrl, "ulong", 0 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); g_value_set_ulong (&val_ulong, 100); - res = gst_controller_set (ctrl, "ulong", 1 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 1 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); g_value_set_ulong (&val_ulong, 50); - res = gst_controller_set (ctrl, "ulong", 2 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); /* verify values */ @@ -763,9 +904,11 @@ GST_START_TEST (controller_unset) fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); /* unset second */ - res = gst_controller_unset (ctrl, "ulong", 1 * GST_SECOND); + res = gst_interpolation_control_source_unset (csource, 1 * GST_SECOND); fail_unless (res, NULL); + g_object_unref (csource); + /* verify value again */ gst_controller_sync_values (ctrl, 1 * GST_SECOND); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); @@ -780,9 +923,10 @@ GST_START_TEST (controller_unset) GST_END_TEST; /* test _unset_all() */ -GST_START_TEST (controller_unset_all) +GST_START_TEST (controller_interpolation_unset_all) { GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gboolean res; GValue val_ulong = { 0, }; @@ -795,17 +939,28 @@ GST_START_TEST (controller_unset_all) ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_NONE)); /* set control values */ g_value_init (&val_ulong, G_TYPE_ULONG); g_value_set_ulong (&val_ulong, 0); - res = gst_controller_set (ctrl, "ulong", 0 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); g_value_set_ulong (&val_ulong, 100); - res = gst_controller_set (ctrl, "ulong", 1 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 1 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); /* verify values */ @@ -815,12 +970,13 @@ GST_START_TEST (controller_unset_all) fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); /* unset second */ - res = gst_controller_unset_all (ctrl, "ulong"); - fail_unless (res, NULL); + gst_interpolation_control_source_unset_all (csource); + + g_object_unref (csource); /* verify value again */ gst_controller_sync_values (ctrl, 1 * GST_SECOND); - fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); g_object_unref (ctrl); @@ -829,10 +985,144 @@ GST_START_TEST (controller_unset_all) GST_END_TEST; -/* test live value handling */ -GST_START_TEST (controller_live) +/* test retrieval of an array of values with get_value_array() */ +GST_START_TEST (controller_interpolation_linear_value_array) { GstController *ctrl; + GstInterpolationControlSource *csource; + GstElement *elem; + gboolean res; + GValue val_ulong = { 0, }; + GstValueArray values = { 0, }; + + gst_controller_init (NULL, NULL); + + elem = gst_element_factory_make ("testmonosource", "test_source"); + + /* that property should exist and should be controllable */ + ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); + fail_unless (ctrl != NULL, NULL); + + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + + /* set interpolation mode */ + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, + GST_INTERPOLATE_LINEAR)); + + /* set control values */ + g_value_init (&val_ulong, G_TYPE_ULONG); + g_value_set_ulong (&val_ulong, 0); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); + fail_unless (res, NULL); + g_value_set_ulong (&val_ulong, 100); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_ulong); + fail_unless (res, NULL); + + /* now pull in values for some timestamps */ + values.property_name = "ulong"; + values.nbsamples = 3; + values.sample_interval = GST_SECOND; + values.values = (gpointer) g_new (gulong, 3); + + fail_unless (gst_control_source_get_value_array (GST_CONTROL_SOURCE (csource), + 0, &values)); + fail_unless_equals_int (((gulong *) values.values)[0], 0); + fail_unless_equals_int (((gulong *) values.values)[1], 50); + fail_unless_equals_int (((gulong *) values.values)[2], 100); + + g_object_unref (csource); + + GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); + g_free (values.values); + g_object_unref (ctrl); + gst_object_unref (elem); +} + +GST_END_TEST; + +/* test if values below minimum and above maximum are clipped */ +GST_START_TEST (controller_interpolation_linear_invalid_values) +{ + GstController *ctrl; + GstInterpolationControlSource *csource; + GstElement *elem; + gboolean res; + GValue val_float = { 0, }; + + gst_controller_init (NULL, NULL); + + elem = gst_element_factory_make ("testmonosource", "test_source"); + + /* that property should exist and should be controllable */ + ctrl = gst_controller_new (G_OBJECT (elem), "float", NULL); + fail_unless (ctrl != NULL, NULL); + + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "float", + GST_CONTROL_SOURCE (csource))); + + /* set interpolation mode */ + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, + GST_INTERPOLATE_LINEAR)); + + /* set control values */ + g_value_init (&val_float, G_TYPE_FLOAT); + g_value_set_float (&val_float, 200.0); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_float); + fail_unless (res, NULL); + g_value_set_float (&val_float, -200.0); + res = + gst_interpolation_control_source_set (csource, 4 * GST_SECOND, + &val_float); + fail_unless (res, NULL); + + g_object_unref (csource); + + /* now pull in values for some timestamps and see if clipping works */ + /* 200.0 */ + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 100.0); + /* 100.0 */ + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 100.0); + /* 50.0 */ + gst_controller_sync_values (ctrl, 1 * GST_SECOND + 500 * GST_MSECOND); + fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 50.0); + /* 0.0 */ + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 0.0); + /* -100.0 */ + gst_controller_sync_values (ctrl, 3 * GST_SECOND); + fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 0.0); + /* -200.0 */ + gst_controller_sync_values (ctrl, 4 * GST_SECOND); + fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 0.0); + + GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); + g_object_unref (ctrl); + gst_object_unref (elem); +} + +GST_END_TEST; + +GST_START_TEST (controller_interpolation_linear_default_values) +{ + GstController *ctrl; + GstInterpolationControlSource *csource; GstElement *elem; gboolean res; GValue val_ulong = { 0, }; @@ -845,38 +1135,227 @@ GST_START_TEST (controller_live) ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); fail_unless (ctrl != NULL, NULL); + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, GST_INTERPOLATE_LINEAR)); + /* Should fail if no value was set yet */ + g_value_init (&val_ulong, G_TYPE_ULONG); + fail_if (gst_control_source_get_value (GST_CONTROL_SOURCE (csource), + 0 * GST_SECOND, &val_ulong)); + + /* set control values */ + g_value_set_ulong (&val_ulong, 0); + res = + gst_interpolation_control_source_set (csource, 1 * GST_SECOND, + &val_ulong); + fail_unless (res, NULL); + g_value_set_ulong (&val_ulong, 100); + res = + gst_interpolation_control_source_set (csource, 3 * GST_SECOND, + &val_ulong); + fail_unless (res, NULL); + + /* now pull in values for some timestamps */ + /* should give the value of the first control point for timestamps before it */ + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); + gst_controller_sync_values (ctrl, 3 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); + + /* set control values */ + g_value_set_ulong (&val_ulong, 0); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); + fail_unless (res, NULL); + g_value_set_ulong (&val_ulong, 100); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_ulong); + fail_unless (res, NULL); + + /* unset the old ones */ + res = gst_interpolation_control_source_unset (csource, 1 * GST_SECOND); + fail_unless (res, NULL); + res = gst_interpolation_control_source_unset (csource, 3 * GST_SECOND); + fail_unless (res, NULL); + + /* now pull in values for some timestamps */ + /* should now give our value for timestamp 0 */ + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); + + g_object_unref (G_OBJECT (csource)); + + GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); + g_object_unref (ctrl); + gst_object_unref (elem); +} + +GST_END_TEST; + +/* test gst_controller_set_disabled() with linear interpolation */ +GST_START_TEST (controller_interpolate_linear_disabled) +{ + GstController *ctrl; + GstInterpolationControlSource *csource, *csource2; + GstElement *elem; + gboolean res; + GValue val_ulong = { 0, } + , val_double = { + 0,}; + + gst_controller_init (NULL, NULL); + + elem = gst_element_factory_make ("testmonosource", "test_source"); + + /* that property should exist and should be controllable */ + ctrl = gst_controller_new (G_OBJECT (elem), "ulong", "double", NULL); + fail_unless (ctrl != NULL, NULL); + + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + csource2 = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + fail_unless (csource2 != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "double", + GST_CONTROL_SOURCE (csource2))); + + /* set interpolation mode */ + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, + GST_INTERPOLATE_LINEAR)); + fail_unless (gst_interpolation_control_source_set_interpolation_mode + (csource2, GST_INTERPOLATE_LINEAR)); + /* set control values */ g_value_init (&val_ulong, G_TYPE_ULONG); g_value_set_ulong (&val_ulong, 0); - res = gst_controller_set (ctrl, "ulong", 0 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 0 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); g_value_set_ulong (&val_ulong, 100); - res = gst_controller_set (ctrl, "ulong", 4 * GST_SECOND, &val_ulong); - fail_unless (res, NULL); - g_value_set_ulong (&val_ulong, 200); - res = gst_controller_set (ctrl, "ulong", 8 * GST_SECOND, &val_ulong); + res = + gst_interpolation_control_source_set (csource, 2 * GST_SECOND, + &val_ulong); fail_unless (res, NULL); - /* verify value */ - gst_controller_sync_values (ctrl, 2 * GST_SECOND); + g_object_unref (G_OBJECT (csource)); + +/* set control values */ + g_value_init (&val_double, G_TYPE_DOUBLE); + g_value_set_double (&val_double, 2.0); + res = + gst_interpolation_control_source_set (csource2, 0 * GST_SECOND, + &val_double); + fail_unless (res, NULL); + g_value_set_double (&val_double, 4.0); + res = + gst_interpolation_control_source_set (csource2, 2 * GST_SECOND, + &val_double); + fail_unless (res, NULL); + + g_object_unref (G_OBJECT (csource2)); + + /* now pull in values for some timestamps */ + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); - - /* set live value */ - g_object_set (elem, "ulong", 500, NULL); - - /* we should still get the live value */ - gst_controller_sync_values (ctrl, 3 * GST_SECOND); - fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 500, NULL); - - /* we should not get the live value anymore */ - gst_controller_sync_values (ctrl, 4 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 3.0, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); - gst_controller_sync_values (ctrl, 6 * GST_SECOND); - fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 150, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 4.0, NULL); + + /* now pull in values for some timestamps, prop double disabled */ + GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; + GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; + gst_controller_set_property_disabled (ctrl, "double", TRUE); + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); + + /* now pull in values for some timestamps, after enabling double again */ + GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; + GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; + gst_controller_set_property_disabled (ctrl, "double", FALSE); + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 3.0, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 4.0, NULL); + + /* now pull in values for some timestamps, after disabling all props */ + GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; + GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; + gst_controller_set_disabled (ctrl, TRUE); + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); + + /* now pull in values for some timestamps, enabling double again */ + GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; + GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; + gst_controller_set_property_disabled (ctrl, "double", FALSE); + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 3.0, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 4.0, NULL); + + /* now pull in values for some timestamps, enabling all */ + GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; + GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; + gst_controller_set_disabled (ctrl, FALSE); + gst_controller_sync_values (ctrl, 0 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 0, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL); + gst_controller_sync_values (ctrl, 1 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 50, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 3.0, NULL); + gst_controller_sync_values (ctrl, 2 * GST_SECOND); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_ulong == 100, NULL); + fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 4.0, NULL); GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); g_object_unref (ctrl); @@ -885,6 +1364,61 @@ GST_START_TEST (controller_live) GST_END_TEST; + +GST_START_TEST (controller_interpolation_set_from_list) +{ + GstController *ctrl; + GstInterpolationControlSource *csource; + GstTimedValue *tval; + GstElement *elem; + GSList *list = NULL; + + gst_controller_init (NULL, NULL); + + /* test that an invalid timestamp throws a warning of some sort */ + elem = gst_element_factory_make ("testmonosource", "test_source"); + + /* that property should exist and should be controllable */ + ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); + fail_unless (ctrl != NULL, NULL); + + /* Get interpolation control source */ + csource = gst_interpolation_control_source_new (); + + fail_unless (csource != NULL); + fail_unless (gst_controller_set_control_source (ctrl, "ulong", + GST_CONTROL_SOURCE (csource))); + + /* set interpolation mode */ + fail_unless (gst_interpolation_control_source_set_interpolation_mode (csource, + GST_INTERPOLATE_LINEAR)); + + /* set control value */ + tval = g_new0 (GstTimedValue, 1); + tval->timestamp = GST_CLOCK_TIME_NONE; + g_value_init (&tval->value, G_TYPE_ULONG); + g_value_set_ulong (&tval->value, 0); + + list = g_slist_append (list, tval); + + fail_if (gst_interpolation_control_source_set_from_list (csource, list)); + + /* try again with a valid stamp, should work now */ + tval->timestamp = 0; + fail_unless (gst_interpolation_control_source_set_from_list (csource, list)); + + g_object_unref (csource); + + /* allocated GstTimedValue now belongs to the controller, but list not */ + g_value_unset (&tval->value); + g_free (tval); + g_slist_free (list); + g_object_unref (ctrl); + gst_object_unref (elem); +} + +GST_END_TEST; + /* tests if we can run helper methods against any GObject */ GST_START_TEST (controller_helper_any_gobject) { @@ -904,50 +1438,6 @@ GST_START_TEST (controller_helper_any_gobject) GST_END_TEST; -GST_START_TEST (controller_misc) -{ - GstController *ctrl; - GstTimedValue *tval; - GstElement *elem; - GSList *list = NULL; - - gst_controller_init (NULL, NULL); - - /* test that an invalid timestamp throws a warning of some sort */ - elem = gst_element_factory_make ("testmonosource", "test_source"); - - /* that property should exist and should be controllable */ - ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); - fail_unless (ctrl != NULL, NULL); - - /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", - GST_INTERPOLATE_LINEAR)); - - /* set control value */ - tval = g_new0 (GstTimedValue, 1); - tval->timestamp = GST_CLOCK_TIME_NONE; - g_value_init (&tval->value, G_TYPE_ULONG); - g_value_set_ulong (&tval->value, 0); - - list = g_slist_append (list, tval); - - fail_if (gst_controller_set_from_list (ctrl, "ulong", list)); - - /* try again with a valid stamp, should work now */ - tval->timestamp = 0; - fail_unless (gst_controller_set_from_list (ctrl, "ulong", list)); - - /* allocated GstTimedValue now belongs to the controller, but list not */ - g_value_unset (&tval->value); - g_free (tval); - g_slist_free (list); - g_object_unref (ctrl); - gst_object_unref (elem); -} - -GST_END_TEST; - GST_START_TEST (controller_refcount_new_list) { GstController *ctrl, *ctrl2; @@ -1026,113 +1516,6 @@ GST_START_TEST (controller_refcount_new_list) GST_END_TEST; - -/* test retrieval of an array of values with get_value_array() */ -GST_START_TEST (controller_interpolate_linear_value_array) -{ - GstController *ctrl; - GstElement *elem; - gboolean res; - GValue val_ulong = { 0, }; - GstValueArray values = { 0, }; - - gst_controller_init (NULL, NULL); - - elem = gst_element_factory_make ("testmonosource", "test_source"); - - /* that property should exist and should be controllable */ - ctrl = gst_controller_new (G_OBJECT (elem), "ulong", NULL); - fail_unless (ctrl != NULL, NULL); - - /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "ulong", - GST_INTERPOLATE_LINEAR)); - - /* set control values */ - g_value_init (&val_ulong, G_TYPE_ULONG); - g_value_set_ulong (&val_ulong, 0); - res = gst_controller_set (ctrl, "ulong", 0 * GST_SECOND, &val_ulong); - fail_unless (res, NULL); - g_value_set_ulong (&val_ulong, 100); - res = gst_controller_set (ctrl, "ulong", 2 * GST_SECOND, &val_ulong); - fail_unless (res, NULL); - - /* now pull in values for some timestamps */ - values.property_name = "ulong"; - values.nbsamples = 3; - values.sample_interval = GST_SECOND; - values.values = (gpointer) g_new (gulong, 3); - - fail_unless (gst_controller_get_value_array (ctrl, 0, &values)); - fail_unless_equals_int (((gulong *) values.values)[0], 0); - fail_unless_equals_int (((gulong *) values.values)[1], 50); - fail_unless_equals_int (((gulong *) values.values)[2], 100); - - GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); - g_free (values.values); - g_object_unref (ctrl); - gst_object_unref (elem); -} - -GST_END_TEST; - - -/* test if values below minimum and above maximum are clipped */ -GST_START_TEST (controller_linear_invalid_values) -{ - GstController *ctrl; - GstElement *elem; - gboolean res; - GValue val_float = { 0, }; - - gst_controller_init (NULL, NULL); - - elem = gst_element_factory_make ("testmonosource", "test_source"); - - /* that property should exist and should be controllable */ - ctrl = gst_controller_new (G_OBJECT (elem), "float", NULL); - fail_unless (ctrl != NULL, NULL); - - /* set interpolation mode */ - fail_unless (gst_controller_set_interpolation_mode (ctrl, "float", - GST_INTERPOLATE_LINEAR)); - - /* set control values */ - g_value_init (&val_float, G_TYPE_FLOAT); - g_value_set_float (&val_float, 200.0); - res = gst_controller_set (ctrl, "float", 0 * GST_SECOND, &val_float); - fail_unless (res, NULL); - g_value_set_float (&val_float, -200.0); - res = gst_controller_set (ctrl, "float", 4 * GST_SECOND, &val_float); - fail_unless (res, NULL); - - /* now pull in values for some timestamps and see if clipping works */ - /* 200.0 */ - gst_controller_sync_values (ctrl, 0 * GST_SECOND); - fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 100.0); - /* 100.0 */ - gst_controller_sync_values (ctrl, 1 * GST_SECOND); - fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 100.0); - /* 50.0 */ - gst_controller_sync_values (ctrl, 1 * GST_SECOND + 500 * GST_MSECOND); - fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 50.0); - /* 0.0 */ - gst_controller_sync_values (ctrl, 2 * GST_SECOND); - fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 0.0); - /* -100.0 */ - gst_controller_sync_values (ctrl, 3 * GST_SECOND); - fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 0.0); - /* -200.0 */ - gst_controller_sync_values (ctrl, 4 * GST_SECOND); - fail_unless_equals_float (GST_TEST_MONO_SOURCE (elem)->val_float, 0.0); - - GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count); - g_object_unref (ctrl); - gst_object_unref (elem); -} - -GST_END_TEST; - static Suite * gst_controller_suite (void) { @@ -1152,18 +1535,20 @@ gst_controller_suite (void) tcase_add_test (tc, controller_new_okay3); tcase_add_test (tc, controller_param_twice); tcase_add_test (tc, controller_finalize); + tcase_add_test (tc, controller_controlsource_refcounts); tcase_add_test (tc, controller_interpolate_none); tcase_add_test (tc, controller_interpolate_trigger); tcase_add_test (tc, controller_interpolate_linear); tcase_add_test (tc, controller_interpolate_cubic); tcase_add_test (tc, controller_interpolate_unimplemented); - tcase_add_test (tc, controller_unset); - tcase_add_test (tc, controller_unset_all); - tcase_add_test (tc, controller_live); + tcase_add_test (tc, controller_interpolation_unset); + tcase_add_test (tc, controller_interpolation_unset_all); + tcase_add_test (tc, controller_interpolation_linear_value_array); + tcase_add_test (tc, controller_interpolation_linear_invalid_values); + tcase_add_test (tc, controller_interpolation_linear_default_values); + tcase_add_test (tc, controller_interpolate_linear_disabled); + tcase_add_test (tc, controller_interpolation_set_from_list); tcase_add_test (tc, controller_helper_any_gobject); - tcase_add_test (tc, controller_misc); - tcase_add_test (tc, controller_interpolate_linear_value_array); - tcase_add_test (tc, controller_linear_invalid_values); return s; } diff --git a/tests/examples/controller/audio-example.c b/tests/examples/controller/audio-example.c index 81d39586a1..2045fd8736 100644 --- a/tests/examples/controller/audio-example.c +++ b/tests/examples/controller/audio-example.c @@ -9,6 +9,7 @@ #include #include +#include gint main (gint argc, gchar ** argv) @@ -17,6 +18,7 @@ main (gint argc, gchar ** argv) GstElement *src, *sink; GstElement *bin; GstController *ctrl; + GstInterpolationControlSource *csource1, *csource2; GstClock *clock; GstClockID clock_id; GstClockReturn wait_ret; @@ -46,23 +48,38 @@ main (gint argc, gchar ** argv) goto Error; } - /* set interpolation */ - gst_controller_set_interpolation_mode (ctrl, "volume", + csource1 = gst_interpolation_control_source_new (); + csource2 = gst_interpolation_control_source_new (); + + gst_controller_set_control_source (ctrl, "volume", + GST_CONTROL_SOURCE (csource1)); + gst_controller_set_control_source (ctrl, "freq", + GST_CONTROL_SOURCE (csource2)); + + /* Set interpolation mode */ + + gst_interpolation_control_source_set_interpolation_mode (csource1, + GST_INTERPOLATE_LINEAR); + gst_interpolation_control_source_set_interpolation_mode (csource2, GST_INTERPOLATE_LINEAR); - gst_controller_set_interpolation_mode (ctrl, "freq", GST_INTERPOLATE_LINEAR); /* set control values */ g_value_init (&vol, G_TYPE_DOUBLE); g_value_set_double (&vol, 0.0); - gst_controller_set (ctrl, "volume", 0 * GST_SECOND, &vol); + gst_interpolation_control_source_set (csource1, 0 * GST_SECOND, &vol); g_value_set_double (&vol, 1.0); - gst_controller_set (ctrl, "volume", 5 * GST_SECOND, &vol); + gst_interpolation_control_source_set (csource1, 5 * GST_SECOND, &vol); + + g_object_unref (csource1); + g_value_set_double (&vol, 220.0); - gst_controller_set (ctrl, "freq", 0 * GST_SECOND, &vol); + gst_interpolation_control_source_set (csource2, 0 * GST_SECOND, &vol); g_value_set_double (&vol, 3520.0); - gst_controller_set (ctrl, "freq", 3 * GST_SECOND, &vol); + gst_interpolation_control_source_set (csource2, 3 * GST_SECOND, &vol); g_value_set_double (&vol, 440.0); - gst_controller_set (ctrl, "freq", 6 * GST_SECOND, &vol); + gst_interpolation_control_source_set (csource2, 6 * GST_SECOND, &vol); + + g_object_unref (csource2); clock_id = gst_clock_new_single_shot_id (clock,