added controller code removed dparam pc files
authorStefan Kost <ensonic@users.sourceforge.net>
Tue, 2 Aug 2005 21:35:34 +0000 (21:35 +0000)
committerStefan Kost <ensonic@users.sourceforge.net>
Tue, 2 Aug 2005 21:35:34 +0000 (21:35 +0000)
Original commit message from CVS:
added controller code
removed dparam pc files

39 files changed:
ChangeLog
configure.ac
docs/gst/tmpl/gstevent.sgml
docs/gst/tmpl/gstfakesrc.sgml
docs/libs/Makefile.am
docs/libs/gstreamer-libs-docs.sgml
docs/libs/gstreamer-libs-sections.txt
docs/libs/gstreamer-libs.types
examples/Makefile.am
examples/controller/.gitignore [new file with mode: 0644]
examples/controller/Makefile.am [new file with mode: 0644]
examples/controller/audio-example.c [new file with mode: 0644]
libs/gst/Makefile.am
libs/gst/controller/.gitignore [new file with mode: 0644]
libs/gst/controller/Makefile.am [new file with mode: 0644]
libs/gst/controller/gst-controller.c [new file with mode: 0644]
libs/gst/controller/gst-controller.h [new file with mode: 0644]
libs/gst/controller/gst-helper.c [new file with mode: 0644]
libs/gst/controller/gst-interpolation.c [new file with mode: 0644]
libs/gst/controller/gstcontroller.c [new file with mode: 0644]
libs/gst/controller/gstcontroller.h [new file with mode: 0644]
libs/gst/controller/gsthelper.c [new file with mode: 0644]
libs/gst/controller/gstinterpolation.c [new file with mode: 0644]
libs/gst/controller/lib.c [new file with mode: 0644]
pkgconfig/Makefile.am
pkgconfig/gstreamer-control-uninstalled.pc.in [deleted file]
pkgconfig/gstreamer-control.pc.in [deleted file]
tests/old/examples/Makefile.am
tests/old/examples/controller/.gitignore [new file with mode: 0644]
tests/old/examples/controller/Makefile.am [new file with mode: 0644]
tests/old/examples/controller/audio-example.c [new file with mode: 0644]
tests/old/testsuite/Makefile.am
tests/old/testsuite/controller/.gitignore [new file with mode: 0644]
tests/old/testsuite/controller/Makefile.am [new file with mode: 0644]
tests/old/testsuite/controller/interpolator.c [new file with mode: 0644]
testsuite/Makefile.am
testsuite/controller/.gitignore [new file with mode: 0644]
testsuite/controller/Makefile.am [new file with mode: 0644]
testsuite/controller/interpolator.c [new file with mode: 0644]

index ede39c7..6be440e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,53 @@
+2005-08-03  Stefan Kost  <ensonic@users.sf.net>
+
+       * configure.ac:
+       * docs/libs/Makefile.am:
+       * docs/libs/gstreamer-libs-docs.sgml:
+       * docs/libs/gstreamer-libs-sections.txt:
+       * docs/libs/gstreamer-libs.types:
+       * examples/Makefile.am:
+       * examples/controller/.cvsignore:
+       * examples/controller/Makefile.am:
+       * examples/controller/audio-example.c: (main):
+       * libs/gst/Makefile.am:
+       * libs/gst/controller/.cvsignore:
+       * libs/gst/controller/Makefile.am:
+       * libs/gst/controller/gst-controller.c:
+       (on_object_controlled_property_changed), (gst_timed_value_compare),
+       (gst_timed_value_find),
+       (gst_controlled_property_set_interpolation_mode),
+       (gst_controlled_property_new), (gst_controlled_property_free),
+       (gst_controller_find_controlled_property),
+       (gst_controller_new_valist), (gst_controller_new),
+       (gst_controller_remove_properties_valist),
+       (gst_controller_remove_properties), (gst_controller_set),
+       (gst_controller_set_from_list), (gst_controller_unset),
+       (gst_controller_get), (gst_controller_get_all),
+       (gst_controller_sink_values), (gst_controller_get_value_arrays),
+       (gst_controller_get_value_array),
+       (gst_controller_set_interpolation_mode),
+       (_gst_controller_finalize), (_gst_controller_init),
+       (_gst_controller_class_init), (gst_controller_get_type):
+       * libs/gst/controller/gst-controller.h:
+       * libs/gst/controller/gst-helper.c: (g_object_control_properties),
+       (g_object_uncontrol_properties), (g_object_get_controller),
+       (g_object_set_controller), (g_object_sink_values),
+       (g_object_get_value_arrays), (g_object_get_value_array):
+       * libs/gst/controller/gst-interpolation.c:
+       (gst_controlled_property_find_timed_value_node),
+       (interpolate_none_get), (interpolate_trigger_get),
+       (interpolate_trigger_get_value_array):
+       * libs/gst/controller/lib.c: (gst_controller_init):
+       * pkgconfig/Makefile.am:
+       * pkgconfig/gstreamer-control-uninstalled.pc.in:
+       * pkgconfig/gstreamer-control.pc.in:
+       * testsuite/Makefile.am:
+       * testsuite/controller/.cvsignore:
+       * testsuite/controller/Makefile.am:
+       * testsuite/controller/interpolator.c: (main):
+          added controller code
+          removed dparam pc files
+
 2005-08-01  Jan Schmidt  <thaytan@mad.scientist.com>
        * gst/base/gstcollectpads.c: (gst_collectpads_finalize),
        (gst_collectpads_stop):
index 4e9b3d3..be04d2b 100644 (file)
@@ -630,6 +630,7 @@ gst/parse/Makefile
 gst/registries/Makefile
 libs/Makefile
 libs/gst/Makefile
+libs/gst/controller/Makefile
 libs/gst/dataprotocol/Makefile
 libs/gst/getbits/Makefile
 po/Makefile.in
@@ -645,6 +646,7 @@ testsuite/Makefile
 testsuite/bytestream/Makefile
 testsuite/caps/Makefile
 testsuite/cleanup/Makefile
+testsuite/controller/Makefile
 testsuite/debug/Makefile
 testsuite/dlopen/Makefile
 testsuite/elements/Makefile
@@ -659,6 +661,7 @@ testsuite/states/Makefile
 testsuite/threads/Makefile
 testsuite/trigger/Makefile
 examples/Makefile
+examples/controller/Makefile
 examples/cutter/Makefile
 examples/helloworld/Makefile
 examples/launch/Makefile
@@ -691,8 +694,8 @@ pkgconfig/gstreamer.pc
 pkgconfig/gstreamer-uninstalled.pc
 pkgconfig/gstreamer-base.pc
 pkgconfig/gstreamer-base-uninstalled.pc
-pkgconfig/gstreamer-control.pc
-pkgconfig/gstreamer-control-uninstalled.pc
+pkgconfig/gstreamer-controller.pc
+pkgconfig/gstreamer-controller-uninstalled.pc
 pkgconfig/gstreamer-dataprotocol.pc
 pkgconfig/gstreamer-dataprotocol-uninstalled.pc
 gstreamer.spec,
index f4575c4..4872e40 100644 (file)
@@ -223,8 +223,6 @@ Copy the event using the event specific copy function
 
 @taglist: 
 @Returns: 
-<!-- # Unused Parameters # -->
-@list: 
 
 
 <!-- ##### FUNCTION gst_event_parse_tag ##### -->
index 57e11cc..9b4080e 100644 (file)
@@ -32,6 +32,9 @@ GstFakeSrc
 @arg1: 
 @:
 @:
+@:
+@:
+@:
 @: 
 
 <!-- ##### ARG GstFakeSrc:data ##### -->
index 4036328..6a68742 100644 (file)
@@ -47,7 +47,7 @@ SCAN_OPTIONS=--deprecated-guards="GST_DISABLE_DEPRECATED"
 #EXTRA_DIST = gstreamer.types.in gstreamer.hierarchy $(DOC_MODULE)-sections.txt gstreamer-sections.txt $(DOC_MAIN_SGML_FILE)
 
 # Extra options to supply to gtkdoc-mkdb.
-MKDB_OPTIONS=--sgml-mode --ignore-files=trio
+MKDB_OPTIONS= --output-format=xml --sgml-mode --ignore-files=trio
 
 # Extra options to supply to gtkdoc-fixref.
 FIXXREF_OPTIONS=--extra-dir=../gst/html
@@ -56,13 +56,14 @@ FIXXREF_OPTIONS=--extra-dir=../gst/html
 HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.h
 CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.c
 
-# this is a wingo addition
-# thomasvs: another nice wingo addition would be an explanation on why
-# this is useful ;)
-
+# Dependencies for the intermediate scanobj tool
 #SCANOBJ_DEPS = $(top_builddir)/gst/elements/libgstelements.la \
 #      $(top_builddir)/gst/schedulers/libgstbasicomegascheduler.la \
 #      $(top_builddir)/libs/gst/control/libgstcontrol-@GST_MAJORMINOR@.la
+SCANOBJ_DEPS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
+
+# Extra options to pass to gtkdoc-scanobj or gtkdoc-scangobj.
+SCANOBJ_OPTIONS=--type-init-func="g_type_init();gst_init(&argc,&argv)"
 
 # Header files to ignore when scanning.
 IGNORE_HFILES = \
@@ -80,7 +81,7 @@ extra_files =
 
 # CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib
 # contains GtkObjects/GObjects and you want to document signals and properties.
-GTKDOC_CFLAGS = $(GST_OBJ_CFLAGS) $(POPT_CFLAGS) -I$(top_builddir)
+GTKDOC_CFLAGS = $(GST_OBJ_CFLAGS) $(POPT_CFLAGS) -I$(top_builddir) -I$(top_builddir)/libs
 GTKDOC_LIBS = $(GST_OBJ_LIBS) $(POPT_LIBS) $(SCANOBJ_DEPS)
 
 GTKDOC_CC=$(LIBTOOL) --mode=compile $(CC)
index 55a8120..c978128 100644 (file)
@@ -4,6 +4,7 @@
 <!ENTITY % version-entities SYSTEM "version.entities">
 %version-entities;
 <!ENTITY GstBytestream SYSTEM "xml/gstbytestream.xml">
+<!ENTITY GstController SYSTEM "xml/gstcontroller.xml">
 <!ENTITY GstGetbits SYSTEM "xml/gstgetbits.xml">
 <!-- has not yet been written
 <!ENTITY GstPutbits SYSTEM "xml/gstputbits.xml">
     <!-- has not yet been written
     &GstPutbits;
     -->
-    <!--link linkend="GObject">GObject</link-->
-    
+
+    <chapter id="gstreamer-control">
+      <title>gstcontrol</title>
+      &GstController;
+      <!--&GstControllerGObject; -->
+    </chapter>
+
   </part>
 
   <part id="gstreamer-libs-hierarchy">
index 8c59a4b..bb6ec06 100644 (file)
@@ -111,3 +111,34 @@ gst_dp_validate_packet
 <SUBSECTION Standard>
 </SECTION>
 
+<SECTION>
+<FILE>gstcontroller</FILE>
+<TITLE>GstController</TITLE>
+<INCLUDE>libs/controller/gstcontroller.h</INCLUDE>
+GstController
+gst_controller_init
+gst_controller_new
+gst_controller_new_valist
+gst_controller_remove_properties
+gst_controller_remove_properties_valist
+gst_controller_set
+gst_controller_set_from_list
+gst_controller_unset
+gst_controller_get
+gst_controller_get_all
+gst_controller_sink_values
+gst_controller_get_value_arrays
+gst_controller_get_value_array
+gst_controller_set_interpolation_mode
+GST_PARAM_CONTROLLABLE
+<SUBSECTION Standard>
+GstControllerClass
+GST_CONTROLLER
+GST_IS_CONTROLLER
+GST_CONTROLLER_CLASS
+GST_IS_CONTROLLER_CLASS
+GST_CONTROLLER_GET_CLASS
+GST_TYPE_CONTROLLER
+<SUBSECTION Private>
+gst_controller_get_type
+</SECTION>
index aaaf415..7ee9f80 100644 (file)
@@ -1,3 +1,4 @@
 #include <gst/gst.h>
+#include <gst/controller/gst-controller.h>
 
-
+gst_controller_get_type
index 1d1d247..ec9b8b1 100644 (file)
@@ -11,6 +11,7 @@ dirs = \
        thread                          \
        plugins                         \
        mixer                           \
+       controller                      \
        cutter                          \
        pingpong                        \
        manual                          \
diff --git a/examples/controller/.gitignore b/examples/controller/.gitignore
new file mode 100644 (file)
index 0000000..228a5fa
--- /dev/null
@@ -0,0 +1,4 @@
+audio-example
+*.bb
+*.bbg
+*.da
diff --git a/examples/controller/Makefile.am b/examples/controller/Makefile.am
new file mode 100644 (file)
index 0000000..4ab183e
--- /dev/null
@@ -0,0 +1,5 @@
+noinst_PROGRAMS = audio-example
+
+audio_example_CFLAGS  = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
+audio_example_LDADD = $(GST_OBJ_LIBS)
+audio_example_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
diff --git a/examples/controller/audio-example.c b/examples/controller/audio-example.c
new file mode 100644 (file)
index 0000000..8193ed7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * audio-example.c
+ * 
+ * Build a pipeline with testaudiosource->alsasink
+ * and sweep frequency and volume
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/controller/gst-controller.h>
+
+gint
+main (gint argc, gchar ** argv)
+{
+  gint res = 1;
+  GstElement *src, *sink;
+  GstBin *bin;
+  GstController *ctrl;
+  GValue vol = { 0, };
+
+  gst_init (&argc, &argv);
+  gst_controller_init (&argc, &argv);
+
+  // build pipeline
+  bin = GST_BIN (gst_pipeline_new ("pipeline"));
+  /* TODO make this "testaudiosrc", when its ready */
+  src = gst_element_factory_make ("sinesrc", "gen_audio");
+  sink = gst_element_factory_make ("alsasink", "play_audio");
+  gst_bin_add_many (bin, src, sink, NULL);
+
+  // add a controller to the source
+  if (!(ctrl =
+          gst_controller_new (G_OBJECT (src), "frequency", "volume", NULL))) {
+    goto Error;
+  }
+  // set interpolation
+  gst_controller_set_interpolation_mode (ctrl, "volume",
+      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);
+  g_value_set_double (&vol, 1.0);
+  gst_controller_set (ctrl, "volume", 1 * GST_SECOND, &vol);
+
+  // iterate two seconds
+  /*
+     if(gst_element_set_state (bin, GST_STATE_PLAYING))
+     {
+     while (gst_bin_iterate (bin))
+     {
+     }
+     }
+     gst_element_set_state (bin, GST_STATE_NULL);
+   */
+
+  // cleanup
+  g_object_unref (G_OBJECT (ctrl));
+  g_object_unref (G_OBJECT (bin));
+  res = 0;
+Error:
+  return (res);
+}
index 0495153..481cb6e 100644 (file)
@@ -1 +1 @@
-SUBDIRS = dataprotocol getbits 
+SUBDIRS = controller dataprotocol getbits 
diff --git a/libs/gst/controller/.gitignore b/libs/gst/controller/.gitignore
new file mode 100644 (file)
index 0000000..1d74972
--- /dev/null
@@ -0,0 +1,5 @@
+*.bb
+*.bbg
+*.da
+*.def
+*.gcno
diff --git a/libs/gst/controller/Makefile.am b/libs/gst/controller/Makefile.am
new file mode 100644 (file)
index 0000000..52350df
--- /dev/null
@@ -0,0 +1,15 @@
+lib_LTLIBRARIES = libgstcontroller-@GST_MAJORMINOR@.la
+
+libgstcontroller_@GST_MAJORMINOR@_includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/controller
+libgstcontroller_@GST_MAJORMINOR@_include_HEADERS = \
+       gst-controller.h
+
+libgstcontroller_@GST_MAJORMINOR@_la_SOURCES = \
+       lib.c \
+       gst-controller.c \
+       gst-interpolation.c \
+       gst-helper.c
+
+libgstcontroller_@GST_MAJORMINOR@_la_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_srcdir)/libs 
+libgstcontroller_@GST_MAJORMINOR@_la_LDFLAGS = @GST_LIB_LDFLAGS@
+libgstcontroller_@GST_MAJORMINOR@_la_LIBADD = $(GST_OBJ_LIBS)
diff --git a/libs/gst/controller/gst-controller.c b/libs/gst/controller/gst-controller.c
new file mode 100644 (file)
index 0000000..9c4e998
--- /dev/null
@@ -0,0 +1,929 @@
+/*
+ * gst-controller.c
+ * 
+ * New dynamic properties
+ *
+ */
+
+/* What needs to be done in plugins?
+Very little - it is just two steps to make a plugin controllable!
+
+1) Just mark gobject-properties that make sense to be controlled,
+   by GST_PARAM_CONTROLLABLE for a start.
+
+2) When processing data (get, chain, loop function) at the beginning call
+   gst_element_sink_values(element,timestamp).
+   This will made the controller to update all gobject properties that are under
+   control with the current values based on timestamp.
+*/
+
+/* What needs to be done in applications?
+
+1) First put some properties under control, by calling 
+   controller=g_object_control_properties(object, "prop1", "prop2",...);
+
+2) Set how the controller will smooth inbetween values.
+   gst_controller_set_interpolation_mode(controller,"prop1",mode);
+
+3) Set key values
+   gst_controller_set(controller,"prop1",0*GST_SECOND,value1);
+   gst_controller_set(controller,"prop1",1*GST_SECOND,value2);
+
+4) Start your pipeline ;-)
+
+5) Live control params from the GUI
+   g_object_set_live_value(object, "prop1", timestamp, value);
+*/
+
+#include "config.h"
+#include "gst-controller.h"
+
+#define GST_CAT_DEFAULT gst_controller_debug
+GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
+
+static GObjectClass *parent_class = NULL;
+GQuark controller_key;
+
+
+/* imports from gst-interpolation.c */
+
+extern GList
+    *gst_controlled_property_find_timed_value_node (GstControlledProperty *
+    prop, GstClockTime timestamp);
+extern GstInterpolateMethod *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_INFO ("notify for '%s'", prop->name);
+
+  ctrl = g_object_get_qdata (G_OBJECT (object), controller_key);
+  g_return_if_fail (ctrl);
+
+  if (g_mutex_trylock (ctrl->lock)) {
+    if (!G_IS_VALUE (&prop->live_value.value)) {
+      //g_value_unset (&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);
+  }
+  //else {
+  //GST_DEBUG ("-> is control change");
+  //}
+}
+
+/* helper */
+
+/*
+ * gst_timed_value_compare:
+ * @p1: a pointer to a #GstTimedValue
+ * @p2: a pointer to a #GstTimedValue
+ *
+ * Compare function for g_list operations that operates on two #GstTimedValue
+ * parameters.
+ */
+static gint
+gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
+{
+  GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
+  GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
+
+  return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
+/* this does not produce an gint :(
+  return ((ct1 - ct2));
+*/
+}
+
+/*
+ * gst_timed_value_find:
+ * @p1: a pointer to a #GstTimedValue
+ * @p2: a pointer to a #GstClockTime
+ *
+ * Compare function for g_list operations that operates on a #GstTimedValue and
+ * a #GstClockTime.
+ */
+static gint
+gst_timed_value_find (gconstpointer p1, gconstpointer p2)
+{
+  GstClockTime ct1 = ((GstTimedValue *) 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.
+ */
+static gboolean
+gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
+    GstInterpolateMode mode)
+{
+  self->interpolation = mode;
+  if (mode != GST_INTERPOLATE_USER) {
+    switch (self->type) {
+      case G_TYPE_INT:
+      case G_TYPE_UINT:
+        self->get = interpolation_methods[mode]->get_int;
+        self->get_value_array =
+            interpolation_methods[mode]->get_int_value_array;
+        break;
+      case G_TYPE_LONG:
+      case G_TYPE_ULONG:
+        self->get = interpolation_methods[mode]->get_long;
+        self->get_value_array =
+            interpolation_methods[mode]->get_long_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;
+      default:
+        self->get = NULL;
+        self->get_value_array = NULL;
+        GST_WARNING ("incomplete implementation for type '%d'", self->type);
+    }
+  } else {
+    /* TODO shouldn't this also get a GstInterpolateMethod *user_method
+       for the case mode==GST_INTERPOLATE_USER
+     */
+  }
+  return (TRUE);
+}
+
+/*
+ * gst_controlled_property_new:
+ * @object: for which object the controlled property should be set up
+ * @name: the name of the property to be controlled
+ *
+ * Private method which initializes the fields of a new controlled property
+ * structure.
+ *
+ * Returns: a freshly allocated structure or %NULL
+ */
+static GstControlledProperty *
+gst_controlled_property_new (GObject * object, const gchar * name)
+{
+  GstControlledProperty *prop = NULL;
+  GParamSpec *pspec;
+
+  GST_INFO ("trying to put property '%s' under control", name);
+
+  // check if the object has a property of that name
+  if ((pspec =
+          g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
+    GST_DEBUG ("  psec->flags : 0x%08x", pspec->flags);
+
+    // check if this param is controlable
+    g_return_val_if_fail (!(pspec->
+            flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)), NULL);
+    //g_return_val_if_fail((pspec->flags&GST_PARAM_CONTROLLABLE),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;
+
+      prop->name = pspec->name; // so we don't use the same mem twice
+      prop->object = object;
+      prop->type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+      gst_controlled_property_set_interpolation_mode (prop,
+          GST_INTERPOLATE_NONE);
+      /* 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->type) {
+        case G_TYPE_INT:{
+          GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
+
+          g_value_set_int (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_UINT:{
+          GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
+
+          g_value_set_uint (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_LONG:{
+          GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
+
+          g_value_set_long (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_ULONG:{
+          GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
+
+          g_value_set_ulong (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_FLOAT:{
+          GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
+
+          g_value_set_float (&prop->default_value, tpspec->default_value);
+        }
+        case G_TYPE_DOUBLE:{
+          GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
+
+          g_value_set_double (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        default:
+          GST_WARNING ("incomplete implementation for paramspec type '%s'",
+              G_PARAM_SPEC_TYPE_NAME (pspec));
+      }
+      /* TODO what about adding a timedval with timestamp=0 and value=default
+         + a bit easier for interpolators, example:
+         * first timestamp is at 5
+         * requested value if for timestamp=3
+         * LINEAR and Co. would need to interpolate from default value
+         to value at timestamp 5 
+       */
+      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);
+    }
+  } else {
+    GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
+        name);
+  }
+
+  return (prop);
+}
+
+/*
+ * gst_controlled_property_free:
+ * @prop: the object to free
+ *
+ * Private method which frees all data allocated by a #GstControlledProperty
+ * instance.
+ */
+static void
+gst_controlled_property_free (GstControlledProperty * prop)
+{
+  GList *node;
+
+  g_signal_handler_disconnect (prop->object, prop->notify_handler_id);
+  for (node = prop->values; node; node = g_list_next (node)) {
+    g_free (node->data);
+  }
+  g_list_free (prop->values);
+  g_free (prop);
+}
+
+/*
+ * gst_controller_find_controlled_property:
+ * @self: the controller object to search for a property in
+ * @name: the gobject property name to look for
+ *
+ * Searches the list of properties under control.
+ *
+ * Returns: a #GstControlledProperty object of %NULL if the property is not
+ * being controlled.
+ */
+static GstControlledProperty *
+gst_controller_find_controlled_property (GstController * self,
+    const gchar * name)
+{
+  GstControlledProperty *prop;
+  GList *node;
+
+  for (node = self->properties; node; node = g_list_next (node)) {
+    prop = node->data;
+    if (!strcmp (prop->name, name)) {
+      return (prop);
+    }
+  }
+  GST_WARNING ("controller does not manage property '%s'", name);
+
+  return (NULL);
+}
+
+/* methods */
+
+/**
+ * gst_controller_new_valist:
+ * @object: the object of which some properties should be controlled
+ * @var_args: %NULL terminated list of property names that should be controlled
+ *
+ * Creates a new GstController for the given object's properties
+ *
+ * Returns: the new controller.
+ */
+GstController *
+gst_controller_new_valist (GObject * object, va_list var_args)
+{
+  GstController *self;
+  GstControlledProperty *prop;
+  gchar *name;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
+
+  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, controller_key);
+  if (!self) {
+    self = g_object_new (GST_TYPE_CONTROLLER, NULL);
+    self->lock = g_mutex_new ();
+    // store the controller
+    g_object_set_qdata (object, controller_key, self);
+  }
+  // create GstControlledProperty for each property
+  while ((name = va_arg (var_args, gchar *))) {
+    // create GstControlledProperty and add to self->propeties List
+    if ((prop = gst_controlled_property_new (object, name)))
+      self->properties = g_list_prepend (self->properties, prop);
+  }
+  va_end (var_args);
+
+  return (self);
+}
+
+/**
+ * gst_controller_new:
+ * @object: the object of which some properties should be controlled
+ * @...: %NULL terminated list of property names that should be controlled
+ *
+ * Creates a new GstController for the given object's properties
+ *
+ * Returns: the new controller.
+ */
+GstController *
+gst_controller_new (GObject * object, ...)
+{
+  GstController *self;
+  va_list var_args;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
+
+  va_start (var_args, object);
+  self = gst_controller_new_valist (object, var_args);
+  va_end (var_args);
+
+  return (self);
+}
+
+/**
+ * gst_controller_remove_properties:
+ * @self: the controller object from which some properties should be removed
+ * @var_args: %NULL terminated list of property names that should be removed
+ *
+ * Removes the given object properties from the controller
+ *
+ * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
+ */
+gboolean
+gst_controller_remove_properties_valist (GstController * self, va_list var_args)
+{
+  gboolean res = TRUE;
+  GstControlledProperty *prop;
+  gchar *name;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+
+  while ((name = va_arg (var_args, gchar *))) {
+    // find the property in the properties list of the controller, remove and free it
+    g_mutex_lock (self->lock);
+    if ((prop = gst_controller_find_controlled_property (self, name))) {
+      self->properties = g_list_remove (self->properties, prop);
+      gst_controlled_property_free (prop);
+    } else {
+      res = FALSE;
+    }
+    g_mutex_unlock (self->lock);
+  }
+
+  return (res);
+}
+
+/**
+ * gst_controller_remove_properties:
+ * @self: the controller object from which some properties should be removed
+ * @...: %NULL terminated list of property names that should be removed
+ *
+ * Removes the given object properties from the controller
+ *
+ * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
+ */
+gboolean
+gst_controller_remove_properties (GstController * self, ...)
+{
+  gboolean res;
+  va_list var_args;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+
+  va_start (var_args, self);
+  res = gst_controller_remove_properties_valist (self, var_args);
+  va_end (var_args);
+
+  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
+ *
+ * Set the value of given controller-handled property at a certain time.
+ *
+ * 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_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+  g_return_val_if_fail (G_IS_VALUE (value), FALSE);
+
+  g_mutex_lock (self->lock);
+  if ((prop = gst_controller_find_controlled_property (self, property_name))) {
+    if (G_VALUE_TYPE (value) == prop->type) {
+      GstTimedValue *tv;
+      GList *node;
+
+      // check if a timed_value for the timestamp already exists
+      if ((node = g_list_find_custom (prop->values, &timestamp,
+                  gst_timed_value_find))) {
+        tv = node->data;
+        memcpy (&tv->value, value, sizeof (GValue));
+      } else {
+        // create a new GstTimedValue
+        tv = g_new (GstTimedValue, 1);
+        tv->timestamp = timestamp;
+        memcpy (&tv->value, value, sizeof (GValue));
+        // and sort it into the prop->values list
+        prop->values =
+            g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
+      }
+      res = TRUE;
+    } else {
+      GST_WARNING ("incompatible value type for property '%s'", prop->name);
+    }
+  }
+  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.
+ *
+ * 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;
+  GSList *node;
+  GstTimedValue *tv;
+
+  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))) {
+    for (node = timedvalues; node; node = g_slist_next (node)) {
+      tv = node->data;
+      if (G_VALUE_TYPE (&tv->value) == prop->type) {
+        g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (tv->timestamp), FALSE);
+        /* TODO copy the timed value or just link in? */
+        prop->values =
+            g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
+        res = TRUE;
+      } else {
+        GST_WARNING ("incompatible value type for property '%s'", prop->name);
+      }
+    }
+  }
+  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.
+ *
+ * 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))) {
+    prop->values = g_list_remove (prop->values, prop);
+    res = TRUE;
+  }
+  g_mutex_unlock (self->lock);
+
+  return (res);
+}
+
+/**
+ * gst_controller_get:
+ * @self: the controller object which handles the properties
+ * @property_name: the name of the property to get
+ * timestamp: the time the control-change should be read from
+ *
+ * Gets the value for the given controller-handled property at the requested
+ * time.
+ *
+ * Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
+ */
+
+GValue *
+gst_controller_get (GstController * self, gchar * property_name,
+    GstClockTime timestamp)
+{
+  GstControlledProperty *prop;
+  GValue *val = NULL;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
+  g_return_val_if_fail (property_name, NULL);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
+
+  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);
+  }
+  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.
+ *
+ * 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);
+}
+
+/**
+ * gst_controller_sink_values:
+ * @self: the controller that handles the values
+ * @timestamp: the time that should be processed
+ *
+ * Sets the properties of the element, according to the controller that (maybe)
+ * handles them and for the given timestamp.
+ *
+ * Returns: %TRUE if the controller values could be applied to the object
+ * properties, %FALSE otherwise
+ */
+gboolean
+gst_controller_sink_values (GstController * self, GstClockTime timestamp)
+{
+  GstControlledProperty *prop;
+  GList *node;
+  GValue *value;
+  gboolean live;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  GST_INFO ("sink_values");
+
+  g_mutex_lock (self->lock);
+  // go over the controlled properties of the controller
+  for (node = self->properties; node; node = g_list_next (node)) {
+    prop = node->data;
+    GST_DEBUG ("  property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
+        timestamp);
+
+    live = FALSE;
+    if (G_IS_VALUE (&prop->live_value.value)) {
+      GList *lnode =
+          gst_controlled_property_find_timed_value_node (prop, timestamp);
+      if (!lnode) {
+        GST_DEBUG ("    no control changes in the queue");
+        live = TRUE;
+      } else {
+        GstTimedValue *tv = lnode->data;
+
+        //GST_DEBUG ("live.ts %"G_UINT64_FORMAT" <-> now %"G_UINT64_FORMAT, prop->live_value.timestamp, tv->timestamp);
+        if (prop->live_value.timestamp < tv->timestamp) {
+          g_value_unset (&prop->live_value.value);
+          GST_DEBUG ("    live value resetted");
+        } else if (prop->live_value.timestamp < timestamp) {
+          live = TRUE;
+        }
+      }
+    }
+    if (!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 (prop->object, prop->name, value);
+    }
+  }
+  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);
+}
+
+/**
+ * gst_controller_get_value_arrays:
+ * @self: the controller that handles the values
+ * @timestamp: the time that should be processed
+ * @value_arrays: list to return the control-values in
+ *
+ * Function to be able to get an array of values for one or more given element
+ * properties.
+ *
+ * If the GstValueArray->values array in list nodes is NULL, it will be created 
+ * by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+gst_controller_get_value_arrays (GstController * self,
+    GstClockTime timestamp, GSList * value_arrays)
+{
+  gboolean res = TRUE;
+  GSList *node;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+  g_return_val_if_fail (value_arrays, FALSE);
+
+  for (node = value_arrays; (res && node); node = g_slist_next (node)) {
+    res = gst_controller_get_value_array (self, timestamp, node->data);
+  }
+
+  return (res);
+}
+
+/**
+ * gst_controller_get_value_array:
+ * @self: the controller that handles the values
+ * @timestamp: the time that should be processed
+ * @value_array: array to put control-values in
+ *
+ * Function to be able to get an array of values for one element properties
+ *
+ * If the GstValueArray->values array is NULL, it will be created by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
+    GstValueArray * value_array)
+{
+  gboolean res = FALSE;
+  GstControlledProperty *prop;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+  g_return_val_if_fail (value_array, FALSE);
+  g_return_val_if_fail (value_array->property_name, FALSE);
+
+  /* TODO and if GstValueArray->values is not NULL, the caller is resposible that
+     is is big enough for nbsamples values, right?
+   */
+
+  g_mutex_lock (self->lock);
+  if ((prop =
+          gst_controller_find_controlled_property (self,
+              value_array->property_name))) {
+    if (!value_array->values) {
+      /* TODO from where to get the base size
+         value_array->values=g_new(sizeof(???),nbsamples);
+       */
+    }
+    //get current value_array via interpolator
+    res = prop->get_value_array (prop, timestamp, value_array);
+  }
+  g_mutex_unlock (self->lock);
+  return (res);
+}
+
+/**
+ * gst_controller_set_interpolation_mode:
+ * @controller:
+ * @property_name:
+ * @mode: interpolation mode
+ *
+ * Sets the given interpolation mode on the given property.
+ *
+ * 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
+     */
+    gst_controlled_property_set_interpolation_mode (prop, mode);
+    res = TRUE;
+  }
+  g_mutex_unlock (self->lock);
+
+  return (res);
+}
+
+/*
+void
+gst_controller_set_live_value(GstController * self, gchar *property_name,
+    GstClockTime timestamp, GValue *value)
+{
+  GstControlledProperty *prop;
+
+  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))) {
+    g_value_unset (&prop->live_value.value);
+    g_value_init (&prop->live_value.value, prop->type);
+    g_value_copy (value, &prop->live_value.value);
+    prop->live_value.timestamp = timestamp;
+  }
+  g_mutex_unlock (self->lock);
+}
+
+*/
+
+/* gobject handling */
+
+static void
+_gst_controller_finalize (GObject * object)
+{
+  GstController *self = GST_CONTROLLER (object);
+  GList *node;
+
+  // free list of properties
+  if (self->properties) {
+    for (node = self->properties; node; node = g_list_next (node)) {
+      gst_controlled_property_free (node->data);
+    }
+    g_list_free (self->properties);
+    self->properties = NULL;
+  }
+  g_mutex_free (self->lock);
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    (G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+static void
+_gst_controller_init (GTypeInstance * instance, gpointer g_class)
+{
+  GstController *self = GST_CONTROLLER (instance);
+
+  self->lock = g_mutex_new ();
+
+}
+
+static void
+_gst_controller_class_init (GstControllerClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  parent_class = g_type_class_ref (G_TYPE_OBJECT);
+
+  gobject_class->finalize = _gst_controller_finalize;
+
+  controller_key = g_quark_from_string ("gst::controller");
+
+  // register properties
+  // register signals
+  // set defaults for overridable methods
+  /* TODO which of theses do we need ? 
+     BilboEd : none :)
+   */
+}
+
+GType
+gst_controller_get_type (void)
+{
+  static GType type = 0;
+
+  if (type == 0) {
+    static const GTypeInfo info = {
+      sizeof (GstControllerClass),
+      NULL,                     // base_init
+      NULL,                     // base_finalize
+      (GClassInitFunc) _gst_controller_class_init,      // class_init
+      NULL,                     // class_finalize
+      NULL,                     // class_data
+      sizeof (GstController),
+      0,                        // n_preallocs
+      (GInstanceInitFunc) _gst_controller_init, // instance_init
+      NULL                      // value_table
+    };
+    type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
+  }
+  return type;
+}
diff --git a/libs/gst/controller/gst-controller.h b/libs/gst/controller/gst-controller.h
new file mode 100644 (file)
index 0000000..a9472c4
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * gst-controller.h
+ * 
+ * New dynamic properties
+ */
+
+#ifndef __GST_CONTROLLER_H__
+#define __GST_CONTROLLER_H__
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gprintf.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GST_PARAM_CONTROLLABLE:
+ *
+ * Use this flag on GstElement properties you wish to be (eventually) handled
+ * by a GstController.
+ * TODO: needs to go to gstelemnt.h (to avoid clashes on G_PARAM_USER_SHIFT)
+ */
+#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
+  /* TODO what about storing the difference to next timestamp and value here
+     + make calculations slightly easier and faster
+     - determining the GType for the value_dif is not simple
+     e.g. if value is G_TYPE_UCHAR value_diff needs to be G_TYPE_INT
+   */
+} GstTimedValue;
+
+
+/**
+ * GstValueArray:
+ *
+ * Structure to receive multiple values at once
+ */
+typedef struct _GstValueArray
+{
+  gchar *property_name;
+  gint nbsamples;               // Number of samples requested
+  GstClockTime sample_interval; // Interval between each sample
+  gpointer *values;             // pointer to the array (so it can be filled if NULL)
+} 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
+ * @GST_INTERPOLATE_CUBIC: cubic interpolation
+ * @GST_INTERPOLATE_USER: user-provided interpolation
+ *
+ * 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;
+
+
+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_long;
+  InterpolateGetValueArray get_long_value_array;
+  InterpolateGet get_float;
+  InterpolateGetValueArray get_float_value_array;
+  InterpolateGet get_double;
+  InterpolateGetValueArray get_double_value_array;
+} GstInterpolateMethod;
+
+/**
+ * GstControlledProperty:
+ */
+typedef struct _GstControlledProperty
+{
+  gchar *name;                  // name of the property
+  GObject *object;              // the object we control
+  GType type;                   // type of the handled property
+  GValue default_value;         // default value for the handled property
+  GValue result_value;          // result value location for the interpolation method
+  GstTimedValue last_value;     // the last value a _sink call wrote
+  GstTimedValue live_value;     // temporary value override for live input
+  gulong notify_handler_id;     // id of the notify::<name> 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 GstTimedValue
+  /* TODO keep the last search result to be able to continue
+     GList      *last_value;                    // last search result, can be used for incremental searches
+   */
+} GstControlledProperty;
+
+#define GST_CONTROLLED_PROPERTY(obj)    ((GstControlledProperty *)(obj))
+
+/* type macros */
+
+#define GST_TYPE_CONTROLLER           (gst_controller_get_type ())
+#define GST_CONTROLLER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CONTROLLER, GstController))
+#define GST_CONTROLLER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CONTROLLER, GstControllerClass))
+#define GST_IS_CONTROLLER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CONTROLLER))
+#define GST_IS_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CONTROLLERE))
+#define GST_CONTROLLER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTROLLER, GstControllerClass))
+
+typedef struct _GstController GstController;
+typedef struct _GstControllerClass GstControllerClass;
+
+//typedef struct _GstControllerPrivate GstControllerPrivate;
+
+/**
+ * GstController:
+ *
+ * The instance structure of GstController
+ */
+
+struct _GstController
+{
+  GObject parent;
+
+  GList *properties;  // List of GstControlledProperty
+  GMutex *lock;       // Secure property access, elements will access from threads
+};
+
+struct _GstControllerClass
+{
+  GObjectClass parent_class;
+};
+
+GType gst_controller_get_type (void);
+
+/* GstController functions */
+
+GstController *gst_controller_new_valist (GObject * object, va_list var_args);
+GstController *gst_controller_new (GObject * object, ...);
+
+gboolean gst_controller_remove_properties_valist (GstController * self,
+    va_list var_args);
+gboolean gst_controller_remove_properties (GstController * self, ...);
+
+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);
+
+
+GValue *gst_controller_get (GstController * self, gchar * property_name,
+    GstClockTime timestamp);
+const GList *gst_controller_get_all (GstController * self,
+    gchar * property_name);
+
+
+gboolean gst_controller_sink_values (GstController * self,
+    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 *g_object_control_properties (GObject * object, ...);
+gboolean g_object_uncontrol_properties (GObject * object, ...);
+
+GstController *g_object_get_controller (GObject * object);
+gboolean g_object_set_controller (GObject * object, GstController * controller);
+
+gboolean g_object_sink_values (GObject * object, GstClockTime timestamp);
+
+gboolean g_object_get_value_arrays (GObject * object,
+    GstClockTime timestamp, GSList * value_arrays);
+gboolean g_object_get_value_array (GObject * object,
+    GstClockTime timestamp, GstValueArray * value_array);
+
+/* lib init/done */
+
+gboolean gst_controller_init (int * argc, char ***argv);
+
+G_END_DECLS
+#endif /* __GST_CONTROLLER_H__ */
diff --git a/libs/gst/controller/gst-helper.c b/libs/gst/controller/gst-helper.c
new file mode 100644 (file)
index 0000000..de221cb
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * gst-helper.c
+ * 
+ * GObject convinience methods for using dynamic properties
+ *
+ */
+
+#include "config.h"
+#include "gst-controller.h"
+
+#define GST_CAT_DEFAULT gst_controller_debug
+GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
+
+extern GQuark controller_key;
+
+/**
+ * g_object_control_properties:
+ * @object: the object of which some properties should be controlled
+ * @var_args: %NULL terminated list of property names that should be controlled
+ *
+ * Convenience function for GObject
+ *
+ * Creates a GstController that allows you to dynamically control one, or more, GObject properties.
+ * If the given GObject already has a GstController, it adds the given properties to the existing 
+ * controller and returns that controller.
+ *
+ * Returns: The GstController with which the user can control the given properties dynamically or NULL if
+ * one or more of the given properties aren't available, or cannot be controlled, for the given element.
+ */
+GstController *
+g_object_control_properties (GObject * object, ...)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+  va_list var_args;
+
+  va_start (var_args, object);
+  ctrl = gst_controller_new_valist (object, var_args);
+  va_end (var_args);
+  return (ctrl);
+}
+
+/**
+ * g_object_uncontrol_properties:
+ * @object: the object of which some properties should not be controlled anymore
+ * @var_args: %NULL terminated list of property names that should be controlled
+ *
+ * Convenience function for GObject
+ *
+ * Removes the given element's properties from it's controller
+ *
+ * Returns: %FALSE if one of the given property names isn't handled by the
+ * controller, %TRUE otherwise
+ */
+gboolean
+g_object_uncontrol_properties (GObject * object, ...)
+{
+  gboolean res = FALSE;
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+  if ((ctrl = g_object_get_qdata (object, controller_key))) {
+    va_list var_args;
+
+    va_start (var_args, object);
+    res = gst_controller_remove_properties_valist (ctrl, var_args);
+    va_end (var_args);
+  }
+  return (res);
+}
+
+/**
+ * g_object_get_controller:
+ * @object: the object that has controlled properties
+ *
+ * Returns: the controller handling some of the given element's properties,
+ * %NULL if no controller
+ */
+GstController *
+g_object_get_controller (GObject * object)
+{
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+  return (g_object_get_qdata (object, controller_key));
+}
+
+/**
+ * g_object_set_controller:
+ * @object: the object that should get the controller
+ * @controller: the controller object to plug in
+ *
+ * Sets the controller on the given GObject
+ *
+ * Returns: %FALSE if the GObject already has an controller, %TRUE otherwise
+ */
+gboolean
+g_object_set_controller (GObject * object, GstController * controller)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (controller, FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (!ctrl, FALSE);
+  g_object_set_qdata (object, controller_key, controller);
+  return (TRUE);
+}
+
+/**
+ * g_object_sink_values:
+ * @object: the object that has controlled properties
+ * @timestamp: the time that should be processed
+ *
+ * Convenience function for GObject
+ *
+ * Returns: same thing as gst_controller_sink_values()
+ */
+gboolean
+g_object_sink_values (GObject * object, GstClockTime timestamp)
+{
+  GstController *ctrl = NULL;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (ctrl, FALSE);
+  return gst_controller_sink_values (ctrl, timestamp);
+}
+
+/**
+ * g_object_get_value_arrays:
+ * @object: the object that has controlled properties
+ * @timestamp: the time that should be processed
+ * @value_arrays: list to return the control-values in
+ *
+ * Function to be able to get an array of values for one or more given element
+ * properties.
+ *
+ * If the GstValueArray->values array in list nodes is NULL, it will be created 
+ * by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * The g_object_* functions are just convenience functions for GObject
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+g_object_get_value_arrays (GObject * object, GstClockTime timestamp,
+    GSList * value_arrays)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (ctrl, FALSE);
+  return gst_controller_get_value_arrays (ctrl, timestamp, value_arrays);
+}
+
+/**
+ * g_object_get_value_array:
+ * @self: the object that has controlled properties
+ * @timestamp: the time that should be processed
+ * @value_array: array to put control-values in
+ *
+ * Function to be able to get an array of values for one element properties
+ *
+ * If the GstValueArray->values array is NULL, it will be created by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * The g_object_* functions are just convenience functions for GObject
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+g_object_get_value_array (GObject * object, GstClockTime timestamp,
+    GstValueArray * value_array)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (ctrl, FALSE);
+
+  return gst_controller_get_value_array (ctrl, timestamp, value_array);
+}
diff --git a/libs/gst/controller/gst-interpolation.c b/libs/gst/controller/gst-interpolation.c
new file mode 100644 (file)
index 0000000..0c309af
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * gst-interpolation.h
+ * 
+ * Interpolation methodws for dynamic properties
+ */
+
+#include "config.h"
+#include "gst-controller.h"
+
+#define GST_CAT_DEFAULT gst_controller_debug
+GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
+
+// common helper
+
+/*
+ * gst_controlled_property_find_timed_value_node:
+ * @prop: the controlled property to search in
+ * @timestamp: the search key
+ *
+ * Find last value before given timestamp in timed value list.
+ *
+ * Returns: the found #GList node or %NULL
+ */
+GList *
+gst_controlled_property_find_timed_value_node (GstControlledProperty * prop,
+    GstClockTime timestamp)
+{
+  //GList *prev_node = NULL;
+  GList *prev_node = g_list_last (prop->values);
+  GList *node;
+  GstTimedValue *tv;
+
+  /*
+     if((prop->last_value) &&
+     (timestamp>((GstTimedValue *)(prop->last_value->data))->timestamp)) {
+     node=prop->last_value;
+     }
+     else {
+     node=prop->values;
+     }
+   */
+
+  /* iterate over timed value list */
+  for (node = prop->values; node; node = g_list_next (node)) {
+    tv = node->data;
+    /* this timestamp is newer that the one we look for */
+    if (timestamp < tv->timestamp) {
+      /* get previous one again */
+      prev_node = g_list_previous (node);
+      break;
+    }
+  }
+  /*
+     if(node) {
+     prop->last_value=prev_node;
+     }
+   */
+  return (prev_node);
+}
+
+// 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_timed_value_node (prop, timestamp))) {
+    GstTimedValue *tv = node->data;
+
+    return (&tv->value);
+  }
+  return (&prop->default_value);
+}
+
+#define DEFINE_NONE_GET(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 (prop,ts)); \
+    ts+=value_array->sample_interval; \
+    values++; \
+  } \
+  return (TRUE); \
+}
+
+DEFINE_NONE_GET (int)
+    DEFINE_NONE_GET (long)
+DEFINE_NONE_GET (float)
+DEFINE_NONE_GET (double)
+
+     static GstInterpolateMethod interpolate_none = {
+       interpolate_none_get,
+       interpolate_none_get_int_value_array,
+       interpolate_none_get,
+       interpolate_none_get_long_value_array,
+       interpolate_none_get,
+       interpolate_none_get_float_value_array,
+       interpolate_none_get,
+       interpolate_none_get_double_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;
+  GstTimedValue *tv;
+
+  /* check if there is a value at the registered timestamp */
+  for (node = prop->values; node; node = g_list_next (node)) {
+    tv = node->data;
+    if (timestamp == tv->timestamp) {
+      return (&tv->value);
+    }
+  }
+
+  return (&prop->default_value);
+}
+
+static gboolean
+interpolate_trigger_get_value_array (GstControlledProperty * prop,
+    GstClockTime timestamp, GstValueArray * value_array)
+{
+  return (FALSE);
+}
+
+static GstInterpolateMethod interpolate_trigger = {
+  interpolate_trigger_get,
+  interpolate_trigger_get_value_array,
+  interpolate_trigger_get,
+  NULL,
+  interpolate_trigger_get,
+  NULL,
+  interpolate_trigger_get,
+  NULL
+};
+
+// linear interpolation
+// smoothes inbetween values
+
+#define DEFINE_LINEAR_GET(type) \
+static g##type \
+_interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
+{ \
+  GList *node; \
+  \
+  if ((node = gst_controlled_property_find_timed_value_node (prop, timestamp))) { \
+    GstTimedValue *tv1, *tv2; \
+    \
+    tv1 = node->data; \
+    if ((node = g_list_next (node))) { \
+      gdouble timediff,valuediff; \
+      g##type value1,value2; \
+      \
+      tv2 = node->data; \
+      \
+      timediff = (gdouble)(tv2->timestamp - tv1->timestamp); \
+      value1 = g_value_get_##type (&tv1->value); \
+      value2 = g_value_get_##type (&tv2->value); \
+      valuediff = (gdouble)(value2-value1); \
+      \
+      return((g##type)(value1+valuediff*((timestamp-tv1->timestamp)/timediff))); \
+    } \
+    else { \
+      return (g_value_get_##type (&tv1->value)); \
+    } \
+  } \
+  return (g_value_get_##type (&prop->default_value)); \
+} \
+\
+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); \
+} \
+\
+static gboolean \
+interpolate_linear_get_##type##_value_array (GstControlledProperty * prop, \
+    GstClockTime timestamp, GstValueArray * value_array) \
+{ \
+  gint i; \
+  GstClockTime ts=timestamp; \
+  gint *values=(gint *)value_array->values; \
+  \
+  for(i=0;i<value_array->nbsamples;i++) { \
+    *values=_interpolate_linear_get_##type (prop,ts); \
+    ts+=value_array->sample_interval; \
+    values++; \
+  } \
+  return (TRUE); \
+}
+
+DEFINE_LINEAR_GET (int)
+DEFINE_LINEAR_GET (long)
+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_long,
+       interpolate_linear_get_long_value_array,
+       interpolate_linear_get_float,
+       interpolate_linear_get_float_value_array,
+       interpolate_linear_get_double,
+       interpolate_linear_get_double_value_array,
+     };
+
+// square interpolation
+
+// cubic interpolation
+
+// register all interpolation methods
+GstInterpolateMethod *interpolation_methods[] = {
+  &interpolate_none,
+  &interpolate_trigger,
+  &interpolate_linear,
+  NULL,
+  NULL
+};
diff --git a/libs/gst/controller/gstcontroller.c b/libs/gst/controller/gstcontroller.c
new file mode 100644 (file)
index 0000000..9c4e998
--- /dev/null
@@ -0,0 +1,929 @@
+/*
+ * gst-controller.c
+ * 
+ * New dynamic properties
+ *
+ */
+
+/* What needs to be done in plugins?
+Very little - it is just two steps to make a plugin controllable!
+
+1) Just mark gobject-properties that make sense to be controlled,
+   by GST_PARAM_CONTROLLABLE for a start.
+
+2) When processing data (get, chain, loop function) at the beginning call
+   gst_element_sink_values(element,timestamp).
+   This will made the controller to update all gobject properties that are under
+   control with the current values based on timestamp.
+*/
+
+/* What needs to be done in applications?
+
+1) First put some properties under control, by calling 
+   controller=g_object_control_properties(object, "prop1", "prop2",...);
+
+2) Set how the controller will smooth inbetween values.
+   gst_controller_set_interpolation_mode(controller,"prop1",mode);
+
+3) Set key values
+   gst_controller_set(controller,"prop1",0*GST_SECOND,value1);
+   gst_controller_set(controller,"prop1",1*GST_SECOND,value2);
+
+4) Start your pipeline ;-)
+
+5) Live control params from the GUI
+   g_object_set_live_value(object, "prop1", timestamp, value);
+*/
+
+#include "config.h"
+#include "gst-controller.h"
+
+#define GST_CAT_DEFAULT gst_controller_debug
+GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
+
+static GObjectClass *parent_class = NULL;
+GQuark controller_key;
+
+
+/* imports from gst-interpolation.c */
+
+extern GList
+    *gst_controlled_property_find_timed_value_node (GstControlledProperty *
+    prop, GstClockTime timestamp);
+extern GstInterpolateMethod *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_INFO ("notify for '%s'", prop->name);
+
+  ctrl = g_object_get_qdata (G_OBJECT (object), controller_key);
+  g_return_if_fail (ctrl);
+
+  if (g_mutex_trylock (ctrl->lock)) {
+    if (!G_IS_VALUE (&prop->live_value.value)) {
+      //g_value_unset (&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);
+  }
+  //else {
+  //GST_DEBUG ("-> is control change");
+  //}
+}
+
+/* helper */
+
+/*
+ * gst_timed_value_compare:
+ * @p1: a pointer to a #GstTimedValue
+ * @p2: a pointer to a #GstTimedValue
+ *
+ * Compare function for g_list operations that operates on two #GstTimedValue
+ * parameters.
+ */
+static gint
+gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
+{
+  GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
+  GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
+
+  return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
+/* this does not produce an gint :(
+  return ((ct1 - ct2));
+*/
+}
+
+/*
+ * gst_timed_value_find:
+ * @p1: a pointer to a #GstTimedValue
+ * @p2: a pointer to a #GstClockTime
+ *
+ * Compare function for g_list operations that operates on a #GstTimedValue and
+ * a #GstClockTime.
+ */
+static gint
+gst_timed_value_find (gconstpointer p1, gconstpointer p2)
+{
+  GstClockTime ct1 = ((GstTimedValue *) 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.
+ */
+static gboolean
+gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
+    GstInterpolateMode mode)
+{
+  self->interpolation = mode;
+  if (mode != GST_INTERPOLATE_USER) {
+    switch (self->type) {
+      case G_TYPE_INT:
+      case G_TYPE_UINT:
+        self->get = interpolation_methods[mode]->get_int;
+        self->get_value_array =
+            interpolation_methods[mode]->get_int_value_array;
+        break;
+      case G_TYPE_LONG:
+      case G_TYPE_ULONG:
+        self->get = interpolation_methods[mode]->get_long;
+        self->get_value_array =
+            interpolation_methods[mode]->get_long_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;
+      default:
+        self->get = NULL;
+        self->get_value_array = NULL;
+        GST_WARNING ("incomplete implementation for type '%d'", self->type);
+    }
+  } else {
+    /* TODO shouldn't this also get a GstInterpolateMethod *user_method
+       for the case mode==GST_INTERPOLATE_USER
+     */
+  }
+  return (TRUE);
+}
+
+/*
+ * gst_controlled_property_new:
+ * @object: for which object the controlled property should be set up
+ * @name: the name of the property to be controlled
+ *
+ * Private method which initializes the fields of a new controlled property
+ * structure.
+ *
+ * Returns: a freshly allocated structure or %NULL
+ */
+static GstControlledProperty *
+gst_controlled_property_new (GObject * object, const gchar * name)
+{
+  GstControlledProperty *prop = NULL;
+  GParamSpec *pspec;
+
+  GST_INFO ("trying to put property '%s' under control", name);
+
+  // check if the object has a property of that name
+  if ((pspec =
+          g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
+    GST_DEBUG ("  psec->flags : 0x%08x", pspec->flags);
+
+    // check if this param is controlable
+    g_return_val_if_fail (!(pspec->
+            flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)), NULL);
+    //g_return_val_if_fail((pspec->flags&GST_PARAM_CONTROLLABLE),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;
+
+      prop->name = pspec->name; // so we don't use the same mem twice
+      prop->object = object;
+      prop->type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+      gst_controlled_property_set_interpolation_mode (prop,
+          GST_INTERPOLATE_NONE);
+      /* 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->type) {
+        case G_TYPE_INT:{
+          GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
+
+          g_value_set_int (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_UINT:{
+          GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
+
+          g_value_set_uint (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_LONG:{
+          GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
+
+          g_value_set_long (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_ULONG:{
+          GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
+
+          g_value_set_ulong (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        case G_TYPE_FLOAT:{
+          GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
+
+          g_value_set_float (&prop->default_value, tpspec->default_value);
+        }
+        case G_TYPE_DOUBLE:{
+          GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
+
+          g_value_set_double (&prop->default_value, tpspec->default_value);
+        }
+          break;
+        default:
+          GST_WARNING ("incomplete implementation for paramspec type '%s'",
+              G_PARAM_SPEC_TYPE_NAME (pspec));
+      }
+      /* TODO what about adding a timedval with timestamp=0 and value=default
+         + a bit easier for interpolators, example:
+         * first timestamp is at 5
+         * requested value if for timestamp=3
+         * LINEAR and Co. would need to interpolate from default value
+         to value at timestamp 5 
+       */
+      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);
+    }
+  } else {
+    GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
+        name);
+  }
+
+  return (prop);
+}
+
+/*
+ * gst_controlled_property_free:
+ * @prop: the object to free
+ *
+ * Private method which frees all data allocated by a #GstControlledProperty
+ * instance.
+ */
+static void
+gst_controlled_property_free (GstControlledProperty * prop)
+{
+  GList *node;
+
+  g_signal_handler_disconnect (prop->object, prop->notify_handler_id);
+  for (node = prop->values; node; node = g_list_next (node)) {
+    g_free (node->data);
+  }
+  g_list_free (prop->values);
+  g_free (prop);
+}
+
+/*
+ * gst_controller_find_controlled_property:
+ * @self: the controller object to search for a property in
+ * @name: the gobject property name to look for
+ *
+ * Searches the list of properties under control.
+ *
+ * Returns: a #GstControlledProperty object of %NULL if the property is not
+ * being controlled.
+ */
+static GstControlledProperty *
+gst_controller_find_controlled_property (GstController * self,
+    const gchar * name)
+{
+  GstControlledProperty *prop;
+  GList *node;
+
+  for (node = self->properties; node; node = g_list_next (node)) {
+    prop = node->data;
+    if (!strcmp (prop->name, name)) {
+      return (prop);
+    }
+  }
+  GST_WARNING ("controller does not manage property '%s'", name);
+
+  return (NULL);
+}
+
+/* methods */
+
+/**
+ * gst_controller_new_valist:
+ * @object: the object of which some properties should be controlled
+ * @var_args: %NULL terminated list of property names that should be controlled
+ *
+ * Creates a new GstController for the given object's properties
+ *
+ * Returns: the new controller.
+ */
+GstController *
+gst_controller_new_valist (GObject * object, va_list var_args)
+{
+  GstController *self;
+  GstControlledProperty *prop;
+  gchar *name;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
+
+  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, controller_key);
+  if (!self) {
+    self = g_object_new (GST_TYPE_CONTROLLER, NULL);
+    self->lock = g_mutex_new ();
+    // store the controller
+    g_object_set_qdata (object, controller_key, self);
+  }
+  // create GstControlledProperty for each property
+  while ((name = va_arg (var_args, gchar *))) {
+    // create GstControlledProperty and add to self->propeties List
+    if ((prop = gst_controlled_property_new (object, name)))
+      self->properties = g_list_prepend (self->properties, prop);
+  }
+  va_end (var_args);
+
+  return (self);
+}
+
+/**
+ * gst_controller_new:
+ * @object: the object of which some properties should be controlled
+ * @...: %NULL terminated list of property names that should be controlled
+ *
+ * Creates a new GstController for the given object's properties
+ *
+ * Returns: the new controller.
+ */
+GstController *
+gst_controller_new (GObject * object, ...)
+{
+  GstController *self;
+  va_list var_args;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
+
+  va_start (var_args, object);
+  self = gst_controller_new_valist (object, var_args);
+  va_end (var_args);
+
+  return (self);
+}
+
+/**
+ * gst_controller_remove_properties:
+ * @self: the controller object from which some properties should be removed
+ * @var_args: %NULL terminated list of property names that should be removed
+ *
+ * Removes the given object properties from the controller
+ *
+ * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
+ */
+gboolean
+gst_controller_remove_properties_valist (GstController * self, va_list var_args)
+{
+  gboolean res = TRUE;
+  GstControlledProperty *prop;
+  gchar *name;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+
+  while ((name = va_arg (var_args, gchar *))) {
+    // find the property in the properties list of the controller, remove and free it
+    g_mutex_lock (self->lock);
+    if ((prop = gst_controller_find_controlled_property (self, name))) {
+      self->properties = g_list_remove (self->properties, prop);
+      gst_controlled_property_free (prop);
+    } else {
+      res = FALSE;
+    }
+    g_mutex_unlock (self->lock);
+  }
+
+  return (res);
+}
+
+/**
+ * gst_controller_remove_properties:
+ * @self: the controller object from which some properties should be removed
+ * @...: %NULL terminated list of property names that should be removed
+ *
+ * Removes the given object properties from the controller
+ *
+ * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
+ */
+gboolean
+gst_controller_remove_properties (GstController * self, ...)
+{
+  gboolean res;
+  va_list var_args;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+
+  va_start (var_args, self);
+  res = gst_controller_remove_properties_valist (self, var_args);
+  va_end (var_args);
+
+  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
+ *
+ * Set the value of given controller-handled property at a certain time.
+ *
+ * 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_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+  g_return_val_if_fail (G_IS_VALUE (value), FALSE);
+
+  g_mutex_lock (self->lock);
+  if ((prop = gst_controller_find_controlled_property (self, property_name))) {
+    if (G_VALUE_TYPE (value) == prop->type) {
+      GstTimedValue *tv;
+      GList *node;
+
+      // check if a timed_value for the timestamp already exists
+      if ((node = g_list_find_custom (prop->values, &timestamp,
+                  gst_timed_value_find))) {
+        tv = node->data;
+        memcpy (&tv->value, value, sizeof (GValue));
+      } else {
+        // create a new GstTimedValue
+        tv = g_new (GstTimedValue, 1);
+        tv->timestamp = timestamp;
+        memcpy (&tv->value, value, sizeof (GValue));
+        // and sort it into the prop->values list
+        prop->values =
+            g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
+      }
+      res = TRUE;
+    } else {
+      GST_WARNING ("incompatible value type for property '%s'", prop->name);
+    }
+  }
+  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.
+ *
+ * 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;
+  GSList *node;
+  GstTimedValue *tv;
+
+  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))) {
+    for (node = timedvalues; node; node = g_slist_next (node)) {
+      tv = node->data;
+      if (G_VALUE_TYPE (&tv->value) == prop->type) {
+        g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (tv->timestamp), FALSE);
+        /* TODO copy the timed value or just link in? */
+        prop->values =
+            g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
+        res = TRUE;
+      } else {
+        GST_WARNING ("incompatible value type for property '%s'", prop->name);
+      }
+    }
+  }
+  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.
+ *
+ * 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))) {
+    prop->values = g_list_remove (prop->values, prop);
+    res = TRUE;
+  }
+  g_mutex_unlock (self->lock);
+
+  return (res);
+}
+
+/**
+ * gst_controller_get:
+ * @self: the controller object which handles the properties
+ * @property_name: the name of the property to get
+ * timestamp: the time the control-change should be read from
+ *
+ * Gets the value for the given controller-handled property at the requested
+ * time.
+ *
+ * Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
+ */
+
+GValue *
+gst_controller_get (GstController * self, gchar * property_name,
+    GstClockTime timestamp)
+{
+  GstControlledProperty *prop;
+  GValue *val = NULL;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
+  g_return_val_if_fail (property_name, NULL);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
+
+  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);
+  }
+  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.
+ *
+ * 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);
+}
+
+/**
+ * gst_controller_sink_values:
+ * @self: the controller that handles the values
+ * @timestamp: the time that should be processed
+ *
+ * Sets the properties of the element, according to the controller that (maybe)
+ * handles them and for the given timestamp.
+ *
+ * Returns: %TRUE if the controller values could be applied to the object
+ * properties, %FALSE otherwise
+ */
+gboolean
+gst_controller_sink_values (GstController * self, GstClockTime timestamp)
+{
+  GstControlledProperty *prop;
+  GList *node;
+  GValue *value;
+  gboolean live;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  GST_INFO ("sink_values");
+
+  g_mutex_lock (self->lock);
+  // go over the controlled properties of the controller
+  for (node = self->properties; node; node = g_list_next (node)) {
+    prop = node->data;
+    GST_DEBUG ("  property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
+        timestamp);
+
+    live = FALSE;
+    if (G_IS_VALUE (&prop->live_value.value)) {
+      GList *lnode =
+          gst_controlled_property_find_timed_value_node (prop, timestamp);
+      if (!lnode) {
+        GST_DEBUG ("    no control changes in the queue");
+        live = TRUE;
+      } else {
+        GstTimedValue *tv = lnode->data;
+
+        //GST_DEBUG ("live.ts %"G_UINT64_FORMAT" <-> now %"G_UINT64_FORMAT, prop->live_value.timestamp, tv->timestamp);
+        if (prop->live_value.timestamp < tv->timestamp) {
+          g_value_unset (&prop->live_value.value);
+          GST_DEBUG ("    live value resetted");
+        } else if (prop->live_value.timestamp < timestamp) {
+          live = TRUE;
+        }
+      }
+    }
+    if (!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 (prop->object, prop->name, value);
+    }
+  }
+  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);
+}
+
+/**
+ * gst_controller_get_value_arrays:
+ * @self: the controller that handles the values
+ * @timestamp: the time that should be processed
+ * @value_arrays: list to return the control-values in
+ *
+ * Function to be able to get an array of values for one or more given element
+ * properties.
+ *
+ * If the GstValueArray->values array in list nodes is NULL, it will be created 
+ * by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+gst_controller_get_value_arrays (GstController * self,
+    GstClockTime timestamp, GSList * value_arrays)
+{
+  gboolean res = TRUE;
+  GSList *node;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+  g_return_val_if_fail (value_arrays, FALSE);
+
+  for (node = value_arrays; (res && node); node = g_slist_next (node)) {
+    res = gst_controller_get_value_array (self, timestamp, node->data);
+  }
+
+  return (res);
+}
+
+/**
+ * gst_controller_get_value_array:
+ * @self: the controller that handles the values
+ * @timestamp: the time that should be processed
+ * @value_array: array to put control-values in
+ *
+ * Function to be able to get an array of values for one element properties
+ *
+ * If the GstValueArray->values array is NULL, it will be created by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
+    GstValueArray * value_array)
+{
+  gboolean res = FALSE;
+  GstControlledProperty *prop;
+
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+  g_return_val_if_fail (value_array, FALSE);
+  g_return_val_if_fail (value_array->property_name, FALSE);
+
+  /* TODO and if GstValueArray->values is not NULL, the caller is resposible that
+     is is big enough for nbsamples values, right?
+   */
+
+  g_mutex_lock (self->lock);
+  if ((prop =
+          gst_controller_find_controlled_property (self,
+              value_array->property_name))) {
+    if (!value_array->values) {
+      /* TODO from where to get the base size
+         value_array->values=g_new(sizeof(???),nbsamples);
+       */
+    }
+    //get current value_array via interpolator
+    res = prop->get_value_array (prop, timestamp, value_array);
+  }
+  g_mutex_unlock (self->lock);
+  return (res);
+}
+
+/**
+ * gst_controller_set_interpolation_mode:
+ * @controller:
+ * @property_name:
+ * @mode: interpolation mode
+ *
+ * Sets the given interpolation mode on the given property.
+ *
+ * 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
+     */
+    gst_controlled_property_set_interpolation_mode (prop, mode);
+    res = TRUE;
+  }
+  g_mutex_unlock (self->lock);
+
+  return (res);
+}
+
+/*
+void
+gst_controller_set_live_value(GstController * self, gchar *property_name,
+    GstClockTime timestamp, GValue *value)
+{
+  GstControlledProperty *prop;
+
+  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))) {
+    g_value_unset (&prop->live_value.value);
+    g_value_init (&prop->live_value.value, prop->type);
+    g_value_copy (value, &prop->live_value.value);
+    prop->live_value.timestamp = timestamp;
+  }
+  g_mutex_unlock (self->lock);
+}
+
+*/
+
+/* gobject handling */
+
+static void
+_gst_controller_finalize (GObject * object)
+{
+  GstController *self = GST_CONTROLLER (object);
+  GList *node;
+
+  // free list of properties
+  if (self->properties) {
+    for (node = self->properties; node; node = g_list_next (node)) {
+      gst_controlled_property_free (node->data);
+    }
+    g_list_free (self->properties);
+    self->properties = NULL;
+  }
+  g_mutex_free (self->lock);
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    (G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+static void
+_gst_controller_init (GTypeInstance * instance, gpointer g_class)
+{
+  GstController *self = GST_CONTROLLER (instance);
+
+  self->lock = g_mutex_new ();
+
+}
+
+static void
+_gst_controller_class_init (GstControllerClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  parent_class = g_type_class_ref (G_TYPE_OBJECT);
+
+  gobject_class->finalize = _gst_controller_finalize;
+
+  controller_key = g_quark_from_string ("gst::controller");
+
+  // register properties
+  // register signals
+  // set defaults for overridable methods
+  /* TODO which of theses do we need ? 
+     BilboEd : none :)
+   */
+}
+
+GType
+gst_controller_get_type (void)
+{
+  static GType type = 0;
+
+  if (type == 0) {
+    static const GTypeInfo info = {
+      sizeof (GstControllerClass),
+      NULL,                     // base_init
+      NULL,                     // base_finalize
+      (GClassInitFunc) _gst_controller_class_init,      // class_init
+      NULL,                     // class_finalize
+      NULL,                     // class_data
+      sizeof (GstController),
+      0,                        // n_preallocs
+      (GInstanceInitFunc) _gst_controller_init, // instance_init
+      NULL                      // value_table
+    };
+    type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
+  }
+  return type;
+}
diff --git a/libs/gst/controller/gstcontroller.h b/libs/gst/controller/gstcontroller.h
new file mode 100644 (file)
index 0000000..a9472c4
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * gst-controller.h
+ * 
+ * New dynamic properties
+ */
+
+#ifndef __GST_CONTROLLER_H__
+#define __GST_CONTROLLER_H__
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gprintf.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GST_PARAM_CONTROLLABLE:
+ *
+ * Use this flag on GstElement properties you wish to be (eventually) handled
+ * by a GstController.
+ * TODO: needs to go to gstelemnt.h (to avoid clashes on G_PARAM_USER_SHIFT)
+ */
+#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
+  /* TODO what about storing the difference to next timestamp and value here
+     + make calculations slightly easier and faster
+     - determining the GType for the value_dif is not simple
+     e.g. if value is G_TYPE_UCHAR value_diff needs to be G_TYPE_INT
+   */
+} GstTimedValue;
+
+
+/**
+ * GstValueArray:
+ *
+ * Structure to receive multiple values at once
+ */
+typedef struct _GstValueArray
+{
+  gchar *property_name;
+  gint nbsamples;               // Number of samples requested
+  GstClockTime sample_interval; // Interval between each sample
+  gpointer *values;             // pointer to the array (so it can be filled if NULL)
+} 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
+ * @GST_INTERPOLATE_CUBIC: cubic interpolation
+ * @GST_INTERPOLATE_USER: user-provided interpolation
+ *
+ * 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;
+
+
+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_long;
+  InterpolateGetValueArray get_long_value_array;
+  InterpolateGet get_float;
+  InterpolateGetValueArray get_float_value_array;
+  InterpolateGet get_double;
+  InterpolateGetValueArray get_double_value_array;
+} GstInterpolateMethod;
+
+/**
+ * GstControlledProperty:
+ */
+typedef struct _GstControlledProperty
+{
+  gchar *name;                  // name of the property
+  GObject *object;              // the object we control
+  GType type;                   // type of the handled property
+  GValue default_value;         // default value for the handled property
+  GValue result_value;          // result value location for the interpolation method
+  GstTimedValue last_value;     // the last value a _sink call wrote
+  GstTimedValue live_value;     // temporary value override for live input
+  gulong notify_handler_id;     // id of the notify::<name> 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 GstTimedValue
+  /* TODO keep the last search result to be able to continue
+     GList      *last_value;                    // last search result, can be used for incremental searches
+   */
+} GstControlledProperty;
+
+#define GST_CONTROLLED_PROPERTY(obj)    ((GstControlledProperty *)(obj))
+
+/* type macros */
+
+#define GST_TYPE_CONTROLLER           (gst_controller_get_type ())
+#define GST_CONTROLLER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CONTROLLER, GstController))
+#define GST_CONTROLLER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CONTROLLER, GstControllerClass))
+#define GST_IS_CONTROLLER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CONTROLLER))
+#define GST_IS_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CONTROLLERE))
+#define GST_CONTROLLER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTROLLER, GstControllerClass))
+
+typedef struct _GstController GstController;
+typedef struct _GstControllerClass GstControllerClass;
+
+//typedef struct _GstControllerPrivate GstControllerPrivate;
+
+/**
+ * GstController:
+ *
+ * The instance structure of GstController
+ */
+
+struct _GstController
+{
+  GObject parent;
+
+  GList *properties;  // List of GstControlledProperty
+  GMutex *lock;       // Secure property access, elements will access from threads
+};
+
+struct _GstControllerClass
+{
+  GObjectClass parent_class;
+};
+
+GType gst_controller_get_type (void);
+
+/* GstController functions */
+
+GstController *gst_controller_new_valist (GObject * object, va_list var_args);
+GstController *gst_controller_new (GObject * object, ...);
+
+gboolean gst_controller_remove_properties_valist (GstController * self,
+    va_list var_args);
+gboolean gst_controller_remove_properties (GstController * self, ...);
+
+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);
+
+
+GValue *gst_controller_get (GstController * self, gchar * property_name,
+    GstClockTime timestamp);
+const GList *gst_controller_get_all (GstController * self,
+    gchar * property_name);
+
+
+gboolean gst_controller_sink_values (GstController * self,
+    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 *g_object_control_properties (GObject * object, ...);
+gboolean g_object_uncontrol_properties (GObject * object, ...);
+
+GstController *g_object_get_controller (GObject * object);
+gboolean g_object_set_controller (GObject * object, GstController * controller);
+
+gboolean g_object_sink_values (GObject * object, GstClockTime timestamp);
+
+gboolean g_object_get_value_arrays (GObject * object,
+    GstClockTime timestamp, GSList * value_arrays);
+gboolean g_object_get_value_array (GObject * object,
+    GstClockTime timestamp, GstValueArray * value_array);
+
+/* lib init/done */
+
+gboolean gst_controller_init (int * argc, char ***argv);
+
+G_END_DECLS
+#endif /* __GST_CONTROLLER_H__ */
diff --git a/libs/gst/controller/gsthelper.c b/libs/gst/controller/gsthelper.c
new file mode 100644 (file)
index 0000000..de221cb
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * gst-helper.c
+ * 
+ * GObject convinience methods for using dynamic properties
+ *
+ */
+
+#include "config.h"
+#include "gst-controller.h"
+
+#define GST_CAT_DEFAULT gst_controller_debug
+GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
+
+extern GQuark controller_key;
+
+/**
+ * g_object_control_properties:
+ * @object: the object of which some properties should be controlled
+ * @var_args: %NULL terminated list of property names that should be controlled
+ *
+ * Convenience function for GObject
+ *
+ * Creates a GstController that allows you to dynamically control one, or more, GObject properties.
+ * If the given GObject already has a GstController, it adds the given properties to the existing 
+ * controller and returns that controller.
+ *
+ * Returns: The GstController with which the user can control the given properties dynamically or NULL if
+ * one or more of the given properties aren't available, or cannot be controlled, for the given element.
+ */
+GstController *
+g_object_control_properties (GObject * object, ...)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+  va_list var_args;
+
+  va_start (var_args, object);
+  ctrl = gst_controller_new_valist (object, var_args);
+  va_end (var_args);
+  return (ctrl);
+}
+
+/**
+ * g_object_uncontrol_properties:
+ * @object: the object of which some properties should not be controlled anymore
+ * @var_args: %NULL terminated list of property names that should be controlled
+ *
+ * Convenience function for GObject
+ *
+ * Removes the given element's properties from it's controller
+ *
+ * Returns: %FALSE if one of the given property names isn't handled by the
+ * controller, %TRUE otherwise
+ */
+gboolean
+g_object_uncontrol_properties (GObject * object, ...)
+{
+  gboolean res = FALSE;
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+  if ((ctrl = g_object_get_qdata (object, controller_key))) {
+    va_list var_args;
+
+    va_start (var_args, object);
+    res = gst_controller_remove_properties_valist (ctrl, var_args);
+    va_end (var_args);
+  }
+  return (res);
+}
+
+/**
+ * g_object_get_controller:
+ * @object: the object that has controlled properties
+ *
+ * Returns: the controller handling some of the given element's properties,
+ * %NULL if no controller
+ */
+GstController *
+g_object_get_controller (GObject * object)
+{
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+  return (g_object_get_qdata (object, controller_key));
+}
+
+/**
+ * g_object_set_controller:
+ * @object: the object that should get the controller
+ * @controller: the controller object to plug in
+ *
+ * Sets the controller on the given GObject
+ *
+ * Returns: %FALSE if the GObject already has an controller, %TRUE otherwise
+ */
+gboolean
+g_object_set_controller (GObject * object, GstController * controller)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (controller, FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (!ctrl, FALSE);
+  g_object_set_qdata (object, controller_key, controller);
+  return (TRUE);
+}
+
+/**
+ * g_object_sink_values:
+ * @object: the object that has controlled properties
+ * @timestamp: the time that should be processed
+ *
+ * Convenience function for GObject
+ *
+ * Returns: same thing as gst_controller_sink_values()
+ */
+gboolean
+g_object_sink_values (GObject * object, GstClockTime timestamp)
+{
+  GstController *ctrl = NULL;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (ctrl, FALSE);
+  return gst_controller_sink_values (ctrl, timestamp);
+}
+
+/**
+ * g_object_get_value_arrays:
+ * @object: the object that has controlled properties
+ * @timestamp: the time that should be processed
+ * @value_arrays: list to return the control-values in
+ *
+ * Function to be able to get an array of values for one or more given element
+ * properties.
+ *
+ * If the GstValueArray->values array in list nodes is NULL, it will be created 
+ * by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * The g_object_* functions are just convenience functions for GObject
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+g_object_get_value_arrays (GObject * object, GstClockTime timestamp,
+    GSList * value_arrays)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (ctrl, FALSE);
+  return gst_controller_get_value_arrays (ctrl, timestamp, value_arrays);
+}
+
+/**
+ * g_object_get_value_array:
+ * @self: the object that has controlled properties
+ * @timestamp: the time that should be processed
+ * @value_array: array to put control-values in
+ *
+ * Function to be able to get an array of values for one element properties
+ *
+ * If the GstValueArray->values array is NULL, it will be created by the function.
+ * The type of the values in the array are the same as the property's type.
+ *
+ * The g_object_* functions are just convenience functions for GObject
+ *
+ * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
+ */
+gboolean
+g_object_get_value_array (GObject * object, GstClockTime timestamp,
+    GstValueArray * value_array)
+{
+  GstController *ctrl;
+
+  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
+
+  ctrl = g_object_get_qdata (object, controller_key);
+  g_return_val_if_fail (ctrl, FALSE);
+
+  return gst_controller_get_value_array (ctrl, timestamp, value_array);
+}
diff --git a/libs/gst/controller/gstinterpolation.c b/libs/gst/controller/gstinterpolation.c
new file mode 100644 (file)
index 0000000..0c309af
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * gst-interpolation.h
+ * 
+ * Interpolation methodws for dynamic properties
+ */
+
+#include "config.h"
+#include "gst-controller.h"
+
+#define GST_CAT_DEFAULT gst_controller_debug
+GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
+
+// common helper
+
+/*
+ * gst_controlled_property_find_timed_value_node:
+ * @prop: the controlled property to search in
+ * @timestamp: the search key
+ *
+ * Find last value before given timestamp in timed value list.
+ *
+ * Returns: the found #GList node or %NULL
+ */
+GList *
+gst_controlled_property_find_timed_value_node (GstControlledProperty * prop,
+    GstClockTime timestamp)
+{
+  //GList *prev_node = NULL;
+  GList *prev_node = g_list_last (prop->values);
+  GList *node;
+  GstTimedValue *tv;
+
+  /*
+     if((prop->last_value) &&
+     (timestamp>((GstTimedValue *)(prop->last_value->data))->timestamp)) {
+     node=prop->last_value;
+     }
+     else {
+     node=prop->values;
+     }
+   */
+
+  /* iterate over timed value list */
+  for (node = prop->values; node; node = g_list_next (node)) {
+    tv = node->data;
+    /* this timestamp is newer that the one we look for */
+    if (timestamp < tv->timestamp) {
+      /* get previous one again */
+      prev_node = g_list_previous (node);
+      break;
+    }
+  }
+  /*
+     if(node) {
+     prop->last_value=prev_node;
+     }
+   */
+  return (prev_node);
+}
+
+// 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_timed_value_node (prop, timestamp))) {
+    GstTimedValue *tv = node->data;
+
+    return (&tv->value);
+  }
+  return (&prop->default_value);
+}
+
+#define DEFINE_NONE_GET(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 (prop,ts)); \
+    ts+=value_array->sample_interval; \
+    values++; \
+  } \
+  return (TRUE); \
+}
+
+DEFINE_NONE_GET (int)
+    DEFINE_NONE_GET (long)
+DEFINE_NONE_GET (float)
+DEFINE_NONE_GET (double)
+
+     static GstInterpolateMethod interpolate_none = {
+       interpolate_none_get,
+       interpolate_none_get_int_value_array,
+       interpolate_none_get,
+       interpolate_none_get_long_value_array,
+       interpolate_none_get,
+       interpolate_none_get_float_value_array,
+       interpolate_none_get,
+       interpolate_none_get_double_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;
+  GstTimedValue *tv;
+
+  /* check if there is a value at the registered timestamp */
+  for (node = prop->values; node; node = g_list_next (node)) {
+    tv = node->data;
+    if (timestamp == tv->timestamp) {
+      return (&tv->value);
+    }
+  }
+
+  return (&prop->default_value);
+}
+
+static gboolean
+interpolate_trigger_get_value_array (GstControlledProperty * prop,
+    GstClockTime timestamp, GstValueArray * value_array)
+{
+  return (FALSE);
+}
+
+static GstInterpolateMethod interpolate_trigger = {
+  interpolate_trigger_get,
+  interpolate_trigger_get_value_array,
+  interpolate_trigger_get,
+  NULL,
+  interpolate_trigger_get,
+  NULL,
+  interpolate_trigger_get,
+  NULL
+};
+
+// linear interpolation
+// smoothes inbetween values
+
+#define DEFINE_LINEAR_GET(type) \
+static g##type \
+_interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
+{ \
+  GList *node; \
+  \
+  if ((node = gst_controlled_property_find_timed_value_node (prop, timestamp))) { \
+    GstTimedValue *tv1, *tv2; \
+    \
+    tv1 = node->data; \
+    if ((node = g_list_next (node))) { \
+      gdouble timediff,valuediff; \
+      g##type value1,value2; \
+      \
+      tv2 = node->data; \
+      \
+      timediff = (gdouble)(tv2->timestamp - tv1->timestamp); \
+      value1 = g_value_get_##type (&tv1->value); \
+      value2 = g_value_get_##type (&tv2->value); \
+      valuediff = (gdouble)(value2-value1); \
+      \
+      return((g##type)(value1+valuediff*((timestamp-tv1->timestamp)/timediff))); \
+    } \
+    else { \
+      return (g_value_get_##type (&tv1->value)); \
+    } \
+  } \
+  return (g_value_get_##type (&prop->default_value)); \
+} \
+\
+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); \
+} \
+\
+static gboolean \
+interpolate_linear_get_##type##_value_array (GstControlledProperty * prop, \
+    GstClockTime timestamp, GstValueArray * value_array) \
+{ \
+  gint i; \
+  GstClockTime ts=timestamp; \
+  gint *values=(gint *)value_array->values; \
+  \
+  for(i=0;i<value_array->nbsamples;i++) { \
+    *values=_interpolate_linear_get_##type (prop,ts); \
+    ts+=value_array->sample_interval; \
+    values++; \
+  } \
+  return (TRUE); \
+}
+
+DEFINE_LINEAR_GET (int)
+DEFINE_LINEAR_GET (long)
+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_long,
+       interpolate_linear_get_long_value_array,
+       interpolate_linear_get_float,
+       interpolate_linear_get_float_value_array,
+       interpolate_linear_get_double,
+       interpolate_linear_get_double_value_array,
+     };
+
+// square interpolation
+
+// cubic interpolation
+
+// register all interpolation methods
+GstInterpolateMethod *interpolation_methods[] = {
+  &interpolate_none,
+  &interpolate_trigger,
+  &interpolate_linear,
+  NULL,
+  NULL
+};
diff --git a/libs/gst/controller/lib.c b/libs/gst/controller/lib.c
new file mode 100644 (file)
index 0000000..e3d3af3
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * lib.c
+ * 
+ * New dynamic properties
+ *
+ */
+
+#include "config.h"
+#include <gst/gst.h>
+
+/* library initialisation */
+
+#define GST_CAT_DEFAULT gst_controller_debug
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+/**
+ * gst_controller_init:
+ * @argc: pointer to the commandline argument count
+ * @argv: pointer to the commandline argument values
+ *
+ * Initializes the use of the controller library. Suggested to be called right
+ * after gst_init().
+ *
+ * Returns: the %TRUE for success.
+ */
+gboolean
+gst_controller_init (int *argc, char ***argv)
+{
+  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontroller", 0,
+      "dynamic parameter control for gstreamer elements");
+
+  return TRUE;
+}
index 0179a98..6229990 100644 (file)
@@ -2,13 +2,13 @@
 pcfiles =                                                      \
        gstreamer-@GST_MAJORMINOR@.pc                           \
        gstreamer-base-@GST_MAJORMINOR@.pc                      \
-       gstreamer-control-@GST_MAJORMINOR@.pc                   \
+       gstreamer-controller-@GST_MAJORMINOR@.pc                        \
        gstreamer-dataprotocol-@GST_MAJORMINOR@.pc
 
 pcfiles_uninstalled =                                          \
        gstreamer-@GST_MAJORMINOR@-uninstalled.pc               \
        gstreamer-base-@GST_MAJORMINOR@-uninstalled.pc          \
-       gstreamer-control-@GST_MAJORMINOR@-uninstalled.pc       \
+       gstreamer-controller-@GST_MAJORMINOR@-uninstalled.pc    \
        gstreamer-dataprotocol-@GST_MAJORMINOR@-uninstalled.pc
 
 all-local: $(pcfiles) $(pcfiles_uninstalled)
@@ -27,8 +27,8 @@ EXTRA_DIST =                                                  \
        gstreamer-uninstalled.pc.in                             \
        gstreamer-base.pc.in                                    \
        gstreamer-base-uninstalled.pc.in                        \
-       gstreamer-control.pc.in                                 \
-       gstreamer-control-uninstalled.pc.in                     \
+       gstreamer-controller.pc.in                                      \
+       gstreamer-controller-uninstalled.pc.in                  \
        gstreamer-dataprotocol.pc.in                            \
        gstreamer-dataprotocol-uninstalled.pc.in
 
diff --git a/pkgconfig/gstreamer-control-uninstalled.pc.in b/pkgconfig/gstreamer-control-uninstalled.pc.in
deleted file mode 100644 (file)
index a2f0a46..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# the standard variables don't make sense for an uninstalled copy
-prefix=
-exec_prefix=
-libdir=${pcfiledir}/../libs
-includedir=${pcfiledir}/..
-gstcontrol_libs=-lgstcontrol-@GST_MAJORMINOR@
-
-Name: GStreamer control library, uninstalled
-Description: Dynamic parameters for plug-ins
-Requires: gstreamer-@GST_MAJORMINOR@ = @VERSION@
-Version: @VERSION@
-
-Libs: ${libdir}/gst/control/libgstcontrol-@GST_MAJORMINOR@.la
-Cflags: -I${includedir} -I${includedir}/libs @GST_PKG_CFLAGS@
diff --git a/pkgconfig/gstreamer-control.pc.in b/pkgconfig/gstreamer-control.pc.in
deleted file mode 100644 (file)
index 563f6c9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@/gstreamer-@GST_MAJORMINOR@
-
-Name: GStreamer control library
-Description: Dynamic parameters for plug-ins
-Requires: gstreamer-@GST_MAJORMINOR@
-Version: @VERSION@
-Libs: -L${libdir} -lgstcontrol-@GST_MAJORMINOR@
-Cflags: -I${includedir} @GST_PKG_CFLAGS@
index 1d1d247..ec9b8b1 100644 (file)
@@ -11,6 +11,7 @@ dirs = \
        thread                          \
        plugins                         \
        mixer                           \
+       controller                      \
        cutter                          \
        pingpong                        \
        manual                          \
diff --git a/tests/old/examples/controller/.gitignore b/tests/old/examples/controller/.gitignore
new file mode 100644 (file)
index 0000000..228a5fa
--- /dev/null
@@ -0,0 +1,4 @@
+audio-example
+*.bb
+*.bbg
+*.da
diff --git a/tests/old/examples/controller/Makefile.am b/tests/old/examples/controller/Makefile.am
new file mode 100644 (file)
index 0000000..4ab183e
--- /dev/null
@@ -0,0 +1,5 @@
+noinst_PROGRAMS = audio-example
+
+audio_example_CFLAGS  = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
+audio_example_LDADD = $(GST_OBJ_LIBS)
+audio_example_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
diff --git a/tests/old/examples/controller/audio-example.c b/tests/old/examples/controller/audio-example.c
new file mode 100644 (file)
index 0000000..8193ed7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * audio-example.c
+ * 
+ * Build a pipeline with testaudiosource->alsasink
+ * and sweep frequency and volume
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/controller/gst-controller.h>
+
+gint
+main (gint argc, gchar ** argv)
+{
+  gint res = 1;
+  GstElement *src, *sink;
+  GstBin *bin;
+  GstController *ctrl;
+  GValue vol = { 0, };
+
+  gst_init (&argc, &argv);
+  gst_controller_init (&argc, &argv);
+
+  // build pipeline
+  bin = GST_BIN (gst_pipeline_new ("pipeline"));
+  /* TODO make this "testaudiosrc", when its ready */
+  src = gst_element_factory_make ("sinesrc", "gen_audio");
+  sink = gst_element_factory_make ("alsasink", "play_audio");
+  gst_bin_add_many (bin, src, sink, NULL);
+
+  // add a controller to the source
+  if (!(ctrl =
+          gst_controller_new (G_OBJECT (src), "frequency", "volume", NULL))) {
+    goto Error;
+  }
+  // set interpolation
+  gst_controller_set_interpolation_mode (ctrl, "volume",
+      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);
+  g_value_set_double (&vol, 1.0);
+  gst_controller_set (ctrl, "volume", 1 * GST_SECOND, &vol);
+
+  // iterate two seconds
+  /*
+     if(gst_element_set_state (bin, GST_STATE_PLAYING))
+     {
+     while (gst_bin_iterate (bin))
+     {
+     }
+     }
+     gst_element_set_state (bin, GST_STATE_NULL);
+   */
+
+  // cleanup
+  g_object_unref (G_OBJECT (ctrl));
+  g_object_unref (G_OBJECT (bin));
+  res = 0;
+Error:
+  return (res);
+}
index b6e96a2..4d4b322 100644 (file)
@@ -14,7 +14,7 @@ GST_DEBUG_DIRS = debug
 endif
 
 SUBDIRS = \
-       bytestream caps cleanup \
+       bytestream caps cleanup controller \
        $(GST_DEBUG_DIRS) \
        dlopen \
        elements indexers negotiation pad \
@@ -22,7 +22,7 @@ SUBDIRS = \
        plugin refcounting schedulers states threads trigger
 
 DIST_SUBDIRS = \
-       bytestream caps cleanup \
+       bytestream caps cleanup controller \
        debug \
        dlopen \
        elements indexers negotiation pad \
diff --git a/tests/old/testsuite/controller/.gitignore b/tests/old/testsuite/controller/.gitignore
new file mode 100644 (file)
index 0000000..1f6c376
--- /dev/null
@@ -0,0 +1,11 @@
+Makefile
+Makefile.in
+*.o
+*.lo
+*.la
+*.bb
+*.bbg
+*.da
+.deps
+.libs
+interpolator
diff --git a/tests/old/testsuite/controller/Makefile.am b/tests/old/testsuite/controller/Makefile.am
new file mode 100644 (file)
index 0000000..8b31b1a
--- /dev/null
@@ -0,0 +1,9 @@
+include ../Rules
+
+tests_pass = interpolator
+tests_fail = 
+tests_ignore =
+
+interpolator_SOURCES = interpolator.c
+interpolator_CFLAGS  = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
+interpolator_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
diff --git a/tests/old/testsuite/controller/interpolator.c b/tests/old/testsuite/controller/interpolator.c
new file mode 100644 (file)
index 0000000..3d34a9e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * interpolator.c
+ * 
+ * test interpolator methods
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/controller/gst-controller.h>
+
+extern GstInterpolateMethod *interpolation_methods[];
+
+gint
+main (gint argc, gchar ** argv)
+{
+  gint res = 1;
+  GstControlledProperty *prop = NULL;
+  GType type = G_TYPE_INT;
+  GstTimedValue tv1 = { 0, }, tv2 = {
+  0,}, tv3 = {
+  0,};
+  GValue *val;
+  gint i;
+
+  gst_init (&argc, &argv);
+  gst_controller_init (&argc, &argv);
+
+  // build fake controlled property
+
+  if ((prop = g_new0 (GstControlledProperty, 1))) {
+    prop->name = "test";
+    //prop->parent_type = G_OBJECT_TYPE (object);
+    prop->type = type;
+
+    g_value_init (&prop->default_value, type);
+    g_value_set_int (&prop->default_value, 0);
+    g_value_init (&prop->result_value, type);
+
+    // set timed values
+    tv1.timestamp = 0;
+    g_value_init (&tv1.value, type);
+    g_value_set_int (&tv1.value, 0);
+    prop->values = g_list_append (prop->values, &tv1);
+
+    tv2.timestamp = 10 * GST_SECOND;
+    g_value_init (&tv2.value, type);
+    g_value_set_int (&tv2.value, 100);
+    prop->values = g_list_append (prop->values, &tv2);
+
+    tv3.timestamp = 20 * GST_SECOND;
+    g_value_init (&tv3.value, type);
+    g_value_set_int (&tv3.value, 50);
+    prop->values = g_list_append (prop->values, &tv3);
+
+    g_print ("# time trig none line\n");
+
+    // test interpolator
+    for (i = 0; i < 25; i++) {
+      g_print ("  %4d", i);
+
+      prop->interpolation = GST_INTERPOLATE_TRIGGER;
+      prop->get = interpolation_methods[prop->interpolation]->get_int;
+      prop->get_value_array =
+          interpolation_methods[prop->interpolation]->get_int_value_array;
+      val = prop->get (prop, i * GST_SECOND);
+      g_print (" %4d", (val ? g_value_get_int (val) : 0));
+
+      prop->interpolation = GST_INTERPOLATE_NONE;
+      prop->get = interpolation_methods[prop->interpolation]->get_int;
+      prop->get_value_array =
+          interpolation_methods[prop->interpolation]->get_int_value_array;
+      val = prop->get (prop, i * GST_SECOND);
+      g_print (" %4d", (val ? g_value_get_int (val) : 0));
+
+      prop->interpolation = GST_INTERPOLATE_LINEAR;
+      prop->get = interpolation_methods[prop->interpolation]->get_int;
+      prop->get_value_array =
+          interpolation_methods[prop->interpolation]->get_int_value_array;
+      val = prop->get (prop, i * GST_SECOND);
+      g_print (" %4d", (val ? g_value_get_int (val) : 0));
+
+      g_print ("\n");
+    }
+
+    g_list_free (prop->values);
+    g_free (prop);
+    res = 0;
+  }
+  return (res);
+}
index b6e96a2..4d4b322 100644 (file)
@@ -14,7 +14,7 @@ GST_DEBUG_DIRS = debug
 endif
 
 SUBDIRS = \
-       bytestream caps cleanup \
+       bytestream caps cleanup controller \
        $(GST_DEBUG_DIRS) \
        dlopen \
        elements indexers negotiation pad \
@@ -22,7 +22,7 @@ SUBDIRS = \
        plugin refcounting schedulers states threads trigger
 
 DIST_SUBDIRS = \
-       bytestream caps cleanup \
+       bytestream caps cleanup controller \
        debug \
        dlopen \
        elements indexers negotiation pad \
diff --git a/testsuite/controller/.gitignore b/testsuite/controller/.gitignore
new file mode 100644 (file)
index 0000000..1f6c376
--- /dev/null
@@ -0,0 +1,11 @@
+Makefile
+Makefile.in
+*.o
+*.lo
+*.la
+*.bb
+*.bbg
+*.da
+.deps
+.libs
+interpolator
diff --git a/testsuite/controller/Makefile.am b/testsuite/controller/Makefile.am
new file mode 100644 (file)
index 0000000..8b31b1a
--- /dev/null
@@ -0,0 +1,9 @@
+include ../Rules
+
+tests_pass = interpolator
+tests_fail = 
+tests_ignore =
+
+interpolator_SOURCES = interpolator.c
+interpolator_CFLAGS  = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
+interpolator_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
diff --git a/testsuite/controller/interpolator.c b/testsuite/controller/interpolator.c
new file mode 100644 (file)
index 0000000..3d34a9e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * interpolator.c
+ * 
+ * test interpolator methods
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/controller/gst-controller.h>
+
+extern GstInterpolateMethod *interpolation_methods[];
+
+gint
+main (gint argc, gchar ** argv)
+{
+  gint res = 1;
+  GstControlledProperty *prop = NULL;
+  GType type = G_TYPE_INT;
+  GstTimedValue tv1 = { 0, }, tv2 = {
+  0,}, tv3 = {
+  0,};
+  GValue *val;
+  gint i;
+
+  gst_init (&argc, &argv);
+  gst_controller_init (&argc, &argv);
+
+  // build fake controlled property
+
+  if ((prop = g_new0 (GstControlledProperty, 1))) {
+    prop->name = "test";
+    //prop->parent_type = G_OBJECT_TYPE (object);
+    prop->type = type;
+
+    g_value_init (&prop->default_value, type);
+    g_value_set_int (&prop->default_value, 0);
+    g_value_init (&prop->result_value, type);
+
+    // set timed values
+    tv1.timestamp = 0;
+    g_value_init (&tv1.value, type);
+    g_value_set_int (&tv1.value, 0);
+    prop->values = g_list_append (prop->values, &tv1);
+
+    tv2.timestamp = 10 * GST_SECOND;
+    g_value_init (&tv2.value, type);
+    g_value_set_int (&tv2.value, 100);
+    prop->values = g_list_append (prop->values, &tv2);
+
+    tv3.timestamp = 20 * GST_SECOND;
+    g_value_init (&tv3.value, type);
+    g_value_set_int (&tv3.value, 50);
+    prop->values = g_list_append (prop->values, &tv3);
+
+    g_print ("# time trig none line\n");
+
+    // test interpolator
+    for (i = 0; i < 25; i++) {
+      g_print ("  %4d", i);
+
+      prop->interpolation = GST_INTERPOLATE_TRIGGER;
+      prop->get = interpolation_methods[prop->interpolation]->get_int;
+      prop->get_value_array =
+          interpolation_methods[prop->interpolation]->get_int_value_array;
+      val = prop->get (prop, i * GST_SECOND);
+      g_print (" %4d", (val ? g_value_get_int (val) : 0));
+
+      prop->interpolation = GST_INTERPOLATE_NONE;
+      prop->get = interpolation_methods[prop->interpolation]->get_int;
+      prop->get_value_array =
+          interpolation_methods[prop->interpolation]->get_int_value_array;
+      val = prop->get (prop, i * GST_SECOND);
+      g_print (" %4d", (val ? g_value_get_int (val) : 0));
+
+      prop->interpolation = GST_INTERPOLATE_LINEAR;
+      prop->get = interpolation_methods[prop->interpolation]->get_int;
+      prop->get_value_array =
+          interpolation_methods[prop->interpolation]->get_int_value_array;
+      val = prop->get (prop, i * GST_SECOND);
+      g_print (" %4d", (val ? g_value_get_int (val) : 0));
+
+      g_print ("\n");
+    }
+
+    g_list_free (prop->values);
+    g_free (prop);
+    res = 0;
+  }
+  return (res);
+}