controller: move GValue handling from control-sources to -binding

ControlSources are now gdouble based. A control source is mapped to a
particullar GObject property using a ControlBinding.
This commit is contained in:
Stefan Sauer 2011-12-22 23:48:30 +01:00
parent a80401b22c
commit 39d6b7462f
24 changed files with 1908 additions and 2854 deletions

View file

@ -562,8 +562,11 @@ GST_USING_PRINTF_EXTENSION
<TITLE>GstControlBinding</TITLE>
GstControlBinding
GstControlBindingClass
GstControlBindingConvert
gst_control_binding_new
gst_control_binding_sync_values
gst_control_binding_get_value
gst_control_binding_get_value_array
gst_control_binding_get_control_source
gst_control_binding_set_disabled
gst_control_binding_is_disabled
@ -583,11 +586,9 @@ gst_control_binding_get_type
<TITLE>GstControlSource</TITLE>
GstControlSource
GstControlSourceClass
GstControlSourceBind
GstControlSourceGetValue
GstControlSourceGetValueArray
GstTimedValue
gst_control_source_bind
gst_control_source_get_value
gst_control_source_get_value_array
<SUBSECTION Standard>
@ -1553,7 +1554,6 @@ gst_object_get_control_binding
gst_object_get_control_source
gst_object_set_control_source
gst_object_get_value
gst_object_get_value_arrays
gst_object_get_value_array
gst_object_get_control_rate
gst_object_set_control_rate

View file

@ -426,16 +426,20 @@ The 0.11 porting guide
gst_tag_list_new_full*() have been renamed to gst_tag_list_new*().
* GstController:
has now been merged into GstObject. The control sources are in the
controller library still.
has now been merged into GstObject. It does not exists as a individual
object anymore. In addition core contains a GstControlSource base class and
the GstControlBinding. The actual control sources are in the controller
library as before. The 2nd big change is that control sources generate
a sequence of gdouble values and those are mapped to the property type and
value range by GstControlBindings.
For plugins the effect is that gst_controller_init() is gone and
gst_object_sync_values() is taking a GstObject * instead of GObject *.
For applications the effect is larger. The whole gst_controller_* API is
gone and now available in simplified form under gst_object_*. There is no
more GstController object. Attach a control source to a property to control
it and attach NULL to un-control it.
gone and now available in simplified form under gst_object_*. ControlSources
are now attached via GstControlBinding to properties. There are no GValue
arguments used anymore when programming control sources.
gst_controller_new* -> gst_object_set_control_source
gst_controller_add_properties -> gst_object_set_control_source

View file

@ -33,6 +33,8 @@
#include "gstcontrolbinding.h"
#include <math.h>
#define GST_CAT_DEFAULT control_binding_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@ -66,8 +68,6 @@ gst_control_binding_dispose (GObject * object)
GstControlBinding *self = GST_CONTROL_BINDING (object);
if (self->csource) {
// FIXME: hack
self->csource->bound = FALSE;
g_object_unref (self->csource);
self->csource = NULL;
}
@ -79,7 +79,35 @@ gst_control_binding_finalize (GObject * object)
GstControlBinding *self = GST_CONTROL_BINDING (object);
g_value_unset (&self->cur_value);
g_value_unset (&self->last_value);
}
/* mapping functions */
#define DEFINE_CONVERT(type,Type,TYPE) \
static void \
convert_to_##type (GstControlBinding *self, gdouble s, GValue *d) \
{ \
GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (self->pspec); \
g##type v; \
\
s = CLAMP (s, 0.0, 1.0); \
v = pspec->minimum + (g##type) ((pspec->maximum - pspec->minimum) * s); \
g_value_set_##type (d, v); \
}
DEFINE_CONVERT (int, Int, INT);
DEFINE_CONVERT (uint, UInt, UINT);
DEFINE_CONVERT (long, Long, LONG);
DEFINE_CONVERT (ulong, ULong, ULONG);
DEFINE_CONVERT (int64, Int64, INT64);
DEFINE_CONVERT (uint64, UInt64, UINT64);
DEFINE_CONVERT (float, Float, FLOAT);
DEFINE_CONVERT (double, Double, DOUBLE);
static void
convert_to_boolean (GstControlBinding * self, gdouble s, GValue * d)
{
s = CLAMP (s, 0.0, 1.0);
g_value_set_boolean (d, (gboolean) (s + 0.5));
}
/**
@ -113,16 +141,59 @@ gst_control_binding_new (GstObject * object, const gchar * property_name,
GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) ==
(G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL);
if (gst_control_source_bind (csource, pspec)) {
if ((self = (GstControlBinding *) g_object_newv (GST_TYPE_CONTROL_BINDING,
0, NULL))) {
self->pspec = pspec;
self->name = pspec->name;
self->csource = g_object_ref (csource);
self->disabled = FALSE;
if ((self = (GstControlBinding *) g_object_newv (GST_TYPE_CONTROL_BINDING,
0, NULL))) {
GType type = G_PARAM_SPEC_VALUE_TYPE (pspec), base;
g_value_init (&self->cur_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_value_init (&self->last_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
// add pspec as a construction parameter and move below to construct()
self->pspec = pspec;
self->name = pspec->name;
self->csource = g_object_ref (csource);
self->disabled = FALSE;
g_value_init (&self->cur_value, type);
base = type = G_PARAM_SPEC_VALUE_TYPE (pspec);
while ((type = g_type_parent (type)))
base = type;
GST_DEBUG_OBJECT (object, " using type %s", g_type_name (base));
// select mapping function
// FIXME: only select mapping if super class hasn't set any?
switch (base) {
case G_TYPE_INT:
self->convert = convert_to_int;
break;
case G_TYPE_UINT:
self->convert = convert_to_uint;
break;
case G_TYPE_LONG:
self->convert = convert_to_long;
break;
case G_TYPE_ULONG:
self->convert = convert_to_ulong;
break;
case G_TYPE_INT64:
self->convert = convert_to_int64;
break;
case G_TYPE_UINT64:
self->convert = convert_to_uint64;
break;
case G_TYPE_FLOAT:
self->convert = convert_to_float;
break;
case G_TYPE_DOUBLE:
self->convert = convert_to_double;
break;
case G_TYPE_BOOLEAN:
self->convert = convert_to_boolean;
break;
default:
// FIXME: return NULL?
GST_WARNING ("incomplete implementation for paramspec type '%s'",
G_PARAM_SPEC_TYPE_NAME (pspec));
break;
}
}
} else {
@ -154,7 +225,8 @@ gboolean
gst_control_binding_sync_values (GstControlBinding * self, GstObject * object,
GstClockTime timestamp, GstClockTime last_sync)
{
GValue *value;
GValue *dst_val;
gdouble src_val;
gboolean ret;
g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), FALSE);
@ -165,20 +237,24 @@ gst_control_binding_sync_values (GstControlBinding * self, GstObject * object,
GST_LOG_OBJECT (object, "property '%s' at ts=%" G_GUINT64_FORMAT,
self->name, timestamp);
value = &self->cur_value;
ret = gst_control_source_get_value (self->csource, timestamp, value);
dst_val = &self->cur_value;
ret = gst_control_source_get_value (self->csource, timestamp, &src_val);
if (G_LIKELY (ret)) {
GST_LOG_OBJECT (object, " new value %lf", src_val);
/* always set the value for first time, but then only if it changed
* this should limit g_object_notify invocations.
* FIXME: can we detect negative playback rates?
*/
if ((timestamp < last_sync) ||
gst_value_compare (value, &self->last_value) != GST_VALUE_EQUAL) {
if ((timestamp < last_sync) || (src_val != self->last_value)) {
GST_LOG_OBJECT (object, " mapping %s to value of type %s", self->name,
G_VALUE_TYPE_NAME (dst_val));
/* run mapping function to convert gdouble to GValue */
self->convert (self, src_val, dst_val);
/* we can make this faster
* http://bugzilla.gnome.org/show_bug.cgi?id=536939
*/
g_object_set_property ((GObject *) object, self->name, value);
g_value_copy (value, &self->last_value);
g_object_set_property ((GObject *) object, self->name, dst_val);
self->last_value = src_val;
}
} else {
GST_DEBUG_OBJECT (object, "no control value for param %s", self->name);
@ -186,6 +262,87 @@ gst_control_binding_sync_values (GstControlBinding * self, GstObject * object,
return (ret);
}
/**
* gst_control_binding_get_value:
* @self: the control binding
* @timestamp: the time the control-change should be read from
*
* Gets the value for the given controlled property at the requested time.
*
* Returns: the GValue of the property at the given time, or %NULL if the
* property isn't controlled.
*/
GValue *
gst_control_binding_get_value (GstControlBinding * self, GstClockTime timestamp)
{
GValue *dst_val = NULL;
gdouble src_val;
g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), NULL);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
/* get current value via control source */
if (gst_control_source_get_value (self->csource, timestamp, &src_val)) {
dst_val = g_new0 (GValue, 1);
g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (self->pspec));
self->convert (self, src_val, dst_val);
}
return dst_val;
}
/**
* gst_control_binding_get_value_array:
* @self: the control binding
* @timestamp: the time that should be processed
* @interval: the time spacing between subsequent values
* @n_values: the number of values
* @values: array to put control-values in
*
* Gets a number of values for the given controllered property starting at the
* requested time. The array @values need to hold enough space for @n_values of
* the same type as the objects property's type.
*
* This function is useful if one wants to e.g. draw a graph of the control
* curve or apply a control curve sample by sample.
*
* Returns: %TRUE if the given array could be filled, %FALSE otherwise
*/
gboolean
gst_control_binding_get_value_array (GstControlBinding * self,
GstClockTime timestamp, GstClockTime interval, guint n_values,
GValue * values)
{
gint i;
gdouble *src_val;
gboolean res = FALSE;
GType type;
GstControlBindingConvert convert;
g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
g_return_val_if_fail (values, FALSE);
convert = self->convert;
type = G_PARAM_SPEC_VALUE_TYPE (self->pspec);
src_val = g_new0 (gdouble, n_values);
if ((res = gst_control_source_get_value_array (self->csource, timestamp,
interval, n_values, src_val))) {
for (i = 0; i < n_values; i++) {
if (!isnan (src_val[i])) {
g_value_init (&values[i], type);
convert (self, src_val[i], &values[i]);
}
}
}
g_free (src_val);
return res;
}
/**
* gst_control_binding_get_control_source:
* @self: the control binding

View file

@ -47,28 +47,44 @@ G_BEGIN_DECLS
typedef struct _GstControlBinding GstControlBinding;
typedef struct _GstControlBindingClass GstControlBindingClass;
/**
* GstControlBindingConvert:
* @self: the #GstControlBinding instance
* @src_value: the value returned by the cotnrol source
* @dest_value: the target GValue
*
* Function to map a control-value to the target GValue.
*/
typedef void (* GstControlBindingConvert) (GstControlBinding *self, gdouble src_value, GValue *dest_value);
/**
* GstControlBinding:
* @name: name of the property of this binding
*
* The instance structure of #GstControlBinding.
*/
struct _GstControlBinding {
GObject parent;
/*< public >*/
GParamSpec *pspec; /* GParamSpec for this property */
const gchar *name; /* name of the property */
GstControlSource *csource; /* GstControlSource for this property */
gboolean disabled;
GValue cur_value, last_value;
/*< private >*/
GParamSpec *pspec; /* GParamSpec for this property */
GstControlSource *csource; /* GstControlSource for this property */
gboolean disabled;
GValue cur_value;
gdouble last_value;
GstControlBindingConvert convert;
gpointer _gst_reserved[GST_PADDING];
};
/**
* GstControlBindingClass:
* @parent_class: Parent class
* @convert: Class method to convert control-values
*
* The class structure of #GstControlBinding.
*/
@ -90,6 +106,10 @@ GstControlBinding * gst_control_binding_new (GstObject * object,
gboolean gst_control_binding_sync_values (GstControlBinding * self, GstObject *object,
GstClockTime timestamp, GstClockTime last_sync);
GValue * gst_control_binding_get_value (GstControlBinding *binding,
GstClockTime timestamp);
gboolean gst_control_binding_get_value_array (GstControlBinding *binding, GstClockTime timestamp,
GstClockTime interval, guint n_values, GValue *values);
GstControlSource * gst_control_binding_get_control_source (GstControlBinding * self);
void gst_control_binding_set_disabled (GstControlBinding * self, gboolean disabled);
gboolean gst_control_binding_is_disabled (GstControlBinding * self);

View file

@ -61,10 +61,6 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstControlSource, gst_control_source,
static void
gst_control_source_class_init (GstControlSourceClass * klass)
{
//GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
/* Has to be implemented by children */
klass->bind = NULL;
}
static void
@ -72,7 +68,6 @@ gst_control_source_init (GstControlSource * self)
{
self->get_value = NULL;
self->get_value_array = NULL;
self->bound = FALSE;
}
/**
@ -87,7 +82,7 @@ gst_control_source_init (GstControlSource * self)
*/
gboolean
gst_control_source_get_value (GstControlSource * self, GstClockTime timestamp,
GValue * value)
gdouble * value)
{
g_return_val_if_fail (GST_IS_CONTROL_SOURCE (self), FALSE);
@ -102,23 +97,19 @@ gst_control_source_get_value (GstControlSource * self, GstClockTime timestamp,
/**
* gst_control_source_get_value_array:
* @self: the #GstControlSource object
* @timestamp: the time that should be processed
* @timestamp: the first timestamp
* @interval: the time steps
* @n_values: the number of values to fetch
* @value_array: array to put control-values in
*
* Gets an array of values for one element property.
*
* All fields of @value_array must be filled correctly. Especially the
* @value_array->values array must be big enough to keep the requested amount
* of values.
*
* The type of the values in the array is the same as the property's type.
* Gets an array of values for for this #GstControlSource.
*
* Returns: %TRUE if the given array could be filled, %FALSE otherwise
*/
gboolean
gst_control_source_get_value_array (GstControlSource * self,
GstClockTime timestamp, GstClockTime interval, guint n_values,
gpointer values)
gdouble * values)
{
g_return_val_if_fail (GST_IS_CONTROL_SOURCE (self), FALSE);
@ -129,34 +120,3 @@ gst_control_source_get_value_array (GstControlSource * self,
return FALSE;
}
}
/**
* gst_control_source_bind:
* @self: the #GstControlSource object
* @pspec: #GParamSpec for the property for which this #GstControlSource should generate values.
*
* Binds a #GstControlSource to a specific property. This must be called only once for a
* #GstControlSource.
*
* Returns: %TRUE if the #GstControlSource was bound correctly, %FALSE otherwise.
*/
gboolean
gst_control_source_bind (GstControlSource * self, GParamSpec * pspec)
{
gboolean ret = FALSE;
g_return_val_if_fail (GST_IS_CONTROL_SOURCE (self), FALSE);
g_return_val_if_fail (GST_CONTROL_SOURCE_GET_CLASS (self)->bind, FALSE);
g_return_val_if_fail (!self->bound, FALSE);
ret = GST_CONTROL_SOURCE_GET_CLASS (self)->bind (self, pspec);
if (ret) {
GST_DEBUG ("bound control source for param %s", pspec->name);
self->bound = TRUE;
} else {
GST_DEBUG ("failed to bind control source for param %s", pspec->name);
}
return ret;
}

View file

@ -59,7 +59,7 @@ typedef struct _GstValueArray GstValueArray;
struct _GstTimedValue
{
GstClockTime timestamp;
GValue value;
gdouble value;
};
/**
@ -74,7 +74,7 @@ struct _GstTimedValue
*
*/
typedef gboolean (* GstControlSourceGetValue) (GstControlSource *self,
GstClockTime timestamp, GValue *value);
GstClockTime timestamp, gdouble *value);
/**
* GstControlSourceGetValueArray
@ -90,18 +90,7 @@ typedef gboolean (* GstControlSourceGetValue) (GstControlSource *self,
*
*/
typedef gboolean (* GstControlSourceGetValueArray) (GstControlSource *self,
GstClockTime timestamp, GstClockTime interval, guint n_values, gpointer values);
/**
* GstControlSourceBind
* @self: the #GstControlSource instance
* @pspec: #GParamSpec that should be bound to
*
* Function for binding a #GstControlSource to a #GParamSpec.
*
* Returns: %TRUE if the property could be bound to the #GstControlSource, %FALSE otherwise.
*/
typedef gboolean (* GstControlSourceBind) (GstControlSource *self, GParamSpec *pspec);
GstClockTime timestamp, GstClockTime interval, guint n_values, gdouble *values);
/**
* GstControlSource:
@ -118,7 +107,6 @@ struct _GstControlSource {
GstControlSourceGetValueArray get_value_array; /* Returns values for a property in a given timespan */
/*< private >*/
gboolean bound;
gpointer _gst_reserved[GST_PADDING];
};
@ -134,8 +122,6 @@ struct _GstControlSourceClass
{
GObjectClass parent_class;
GstControlSourceBind bind; /* Binds the GstControlSource to a specific GParamSpec */
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
@ -144,13 +130,10 @@ GType gst_control_source_get_type (void);
/* Functions */
gboolean gst_control_source_get_value (GstControlSource *self, GstClockTime timestamp,
GValue *value);
gdouble *value);
gboolean gst_control_source_get_value_array (GstControlSource *self, GstClockTime timestamp,
GstClockTime interval, guint n_values,
gpointer values);
gboolean gst_control_source_bind (GstControlSource *self, GParamSpec *pspec);
gdouble *values);
G_END_DECLS
#endif /* __GST_CONTROL_SOURCE_H__ */

View file

@ -1109,6 +1109,8 @@ gst_object_sync_values (GstObject * object, GstClockTime timestamp)
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
GST_LOG_OBJECT (object, "sync_values");
if (!object->control_bindings)
return TRUE;
/* FIXME: this deadlocks */
/* GST_OBJECT_LOCK (object); */
@ -1353,7 +1355,7 @@ gst_object_get_control_source (GstObject * object, const gchar * property_name)
* @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 controllered property at the requested time.
* Gets the value for the given controlled property at the requested time.
*
* Returns: the GValue of the property at the given time, or %NULL if the
* property isn't controlled.
@ -1371,14 +1373,7 @@ gst_object_get_value (GstObject * object, const gchar * property_name,
GST_OBJECT_LOCK (object);
if ((binding = gst_object_find_control_binding (object, property_name))) {
val = g_new0 (GValue, 1);
g_value_init (val, G_PARAM_SPEC_VALUE_TYPE (binding->pspec));
/* get current value via control source */
if (!gst_control_source_get_value (binding->csource, timestamp, val)) {
g_free (val);
val = NULL;
}
val = gst_control_binding_get_value (binding, timestamp);
}
GST_OBJECT_UNLOCK (object);
@ -1406,7 +1401,7 @@ gst_object_get_value (GstObject * object, const gchar * property_name,
gboolean
gst_object_get_value_array (GstObject * object, const gchar * property_name,
GstClockTime timestamp, GstClockTime interval, guint n_values,
gpointer values)
GValue * values)
{
gboolean res = FALSE;
GstControlBinding *binding;
@ -1419,8 +1414,9 @@ gst_object_get_value_array (GstObject * object, const gchar * property_name,
GST_OBJECT_LOCK (object);
if ((binding = gst_object_find_control_binding (object, property_name))) {
res = gst_control_source_get_value_array (binding->csource, timestamp,
interval, n_values, values);
/* FIXME: use binding: */
res = gst_control_binding_get_value_array (binding, timestamp, interval,
n_values, values);
}
GST_OBJECT_UNLOCK (object);
return res;

View file

@ -257,7 +257,7 @@ GValue * gst_object_get_value (GstObject * object, const gchar
GstClockTime timestamp);
gboolean gst_object_get_value_array (GstObject * object, const gchar * property_name,
GstClockTime timestamp, GstClockTime interval,
guint n_values, gpointer values);
guint n_values, GValue *values);
GstClockTime gst_object_get_control_rate (GstObject * object);
void gst_object_set_control_rate (GstObject * object, GstClockTime control_rate);

View file

@ -7,13 +7,8 @@ libgstcontroller_@GST_MAJORMINOR@_include_HEADERS = \
gsttriggercontrolsource.h \
gstlfocontrolsource.h
noinst_HEADERS = \
gstinterpolationcontrolsourceprivate.h \
gstlfocontrolsourceprivate.h
libgstcontroller_@GST_MAJORMINOR@_la_SOURCES = \
gsttimedvaluecontrolsource.c \
gstinterpolation.c \
gstinterpolationcontrolsource.c \
gsttriggercontrolsource.c \
gstlfocontrolsource.c

View file

@ -1,592 +0,0 @@
/* GStreamer
*
* Copyright (C) <2005> Stefan Kost <ensonic at users dot sf dot net>
* Copyright (C) 2007-2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* gstinterpolation.c: Interpolation methods for dynamic properties
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gstinterpolationcontrolsource.h"
#include "gstinterpolationcontrolsourceprivate.h"
#define GST_CAT_DEFAULT controller_debug
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
#define EMPTY(x) (x)
/* steps-like (no-)interpolation, default */
/* just returns the value for the most recent key-frame */
static inline const GValue *
_interpolate_none_get (GstTimedValueControlSource * self, GSequenceIter * iter)
{
const GValue *ret;
if (iter) {
GstControlPoint *cp = g_sequence_get (iter);
ret = &cp->value;
} else {
ret = &self->default_value;
}
return ret;
}
#define DEFINE_NONE_GET_FUNC_COMPARABLE(type) \
static inline const GValue * \
_interpolate_none_get_##type (GstTimedValueControlSource *self, GSequenceIter *iter) \
{ \
const GValue *ret; \
\
if (iter) { \
GstControlPoint *cp = g_sequence_get (iter); \
g##type ret_val = g_value_get_##type (&cp->value); \
\
if (g_value_get_##type (&self->minimum_value) > ret_val) \
ret = &self->minimum_value; \
else if (g_value_get_##type (&self->maximum_value) < ret_val) \
ret = &self->maximum_value; \
else \
ret = &cp->value; \
} else { \
ret = &self->default_value; \
} \
return ret; \
}
#define DEFINE_NONE_GET(type,ctype,get_func) \
static gboolean \
interpolate_none_get_##type (GstTimedValueControlSource *self, GstClockTime timestamp, GValue *value) \
{ \
const GValue *ret; \
GSequenceIter *iter; \
\
g_mutex_lock (self->lock); \
\
iter = gst_timed_value_control_source_find_control_point_iter (self, timestamp); \
ret = get_func (self, iter); \
g_value_copy (ret, value); \
g_mutex_unlock (self->lock); \
return TRUE; \
} \
\
static gboolean \
interpolate_none_get_##type##_value_array (GstTimedValueControlSource *self, \
GstClockTime timestamp, GstClockTime interval, guint n_values, gpointer _values) \
{ \
guint i; \
GstClockTime ts = timestamp; \
GstClockTime next_ts = 0; \
ctype *values = (ctype *) _values; \
const GValue *ret_val = NULL; \
ctype ret = 0; \
GSequenceIter *iter1 = NULL, *iter2 = NULL; \
\
g_mutex_lock (self->lock); \
for(i = 0; i < n_values; i++) { \
if (!ret_val || ts >= next_ts) { \
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts); \
if (!iter1) { \
if (G_LIKELY (self->values)) \
iter2 = g_sequence_get_begin_iter (self->values); \
else \
iter2 = NULL; \
} else { \
iter2 = g_sequence_iter_next (iter1); \
} \
\
if (iter2 && !g_sequence_iter_is_end (iter2)) { \
GstControlPoint *cp; \
\
cp = g_sequence_get (iter2); \
next_ts = cp->timestamp; \
} else { \
next_ts = GST_CLOCK_TIME_NONE; \
} \
\
ret_val = get_func (self, iter1); \
ret = g_value_get_##type (ret_val); \
} \
*values = ret; \
ts += interval; \
values++; \
} \
g_mutex_unlock (self->lock); \
return TRUE; \
}
DEFINE_NONE_GET_FUNC_COMPARABLE (int);
DEFINE_NONE_GET (int, gint, _interpolate_none_get_int);
DEFINE_NONE_GET_FUNC_COMPARABLE (uint);
DEFINE_NONE_GET (uint, guint, _interpolate_none_get_uint);
DEFINE_NONE_GET_FUNC_COMPARABLE (long);
DEFINE_NONE_GET (long, glong, _interpolate_none_get_long);
DEFINE_NONE_GET_FUNC_COMPARABLE (ulong);
DEFINE_NONE_GET (ulong, gulong, _interpolate_none_get_ulong);
DEFINE_NONE_GET_FUNC_COMPARABLE (int64);
DEFINE_NONE_GET (int64, gint64, _interpolate_none_get_int64);
DEFINE_NONE_GET_FUNC_COMPARABLE (uint64);
DEFINE_NONE_GET (uint64, guint64, _interpolate_none_get_uint64);
DEFINE_NONE_GET_FUNC_COMPARABLE (float);
DEFINE_NONE_GET (float, gfloat, _interpolate_none_get_float);
DEFINE_NONE_GET_FUNC_COMPARABLE (double);
DEFINE_NONE_GET (double, gdouble, _interpolate_none_get_double);
DEFINE_NONE_GET (boolean, gboolean, _interpolate_none_get);
DEFINE_NONE_GET (enum, gint, _interpolate_none_get);
DEFINE_NONE_GET (string, const gchar *, _interpolate_none_get);
static GstInterpolateMethod interpolate_none = {
(GstControlSourceGetValue) interpolate_none_get_int,
(GstControlSourceGetValueArray) interpolate_none_get_int_value_array,
(GstControlSourceGetValue) interpolate_none_get_uint,
(GstControlSourceGetValueArray) interpolate_none_get_uint_value_array,
(GstControlSourceGetValue) interpolate_none_get_long,
(GstControlSourceGetValueArray) interpolate_none_get_long_value_array,
(GstControlSourceGetValue) interpolate_none_get_ulong,
(GstControlSourceGetValueArray) interpolate_none_get_ulong_value_array,
(GstControlSourceGetValue) interpolate_none_get_int64,
(GstControlSourceGetValueArray) interpolate_none_get_int64_value_array,
(GstControlSourceGetValue) interpolate_none_get_uint64,
(GstControlSourceGetValueArray) interpolate_none_get_uint64_value_array,
(GstControlSourceGetValue) interpolate_none_get_float,
(GstControlSourceGetValueArray) interpolate_none_get_float_value_array,
(GstControlSourceGetValue) interpolate_none_get_double,
(GstControlSourceGetValueArray) interpolate_none_get_double_value_array,
(GstControlSourceGetValue) interpolate_none_get_boolean,
(GstControlSourceGetValueArray) interpolate_none_get_boolean_value_array,
(GstControlSourceGetValue) interpolate_none_get_enum,
(GstControlSourceGetValueArray) interpolate_none_get_enum_value_array,
(GstControlSourceGetValue) interpolate_none_get_string,
(GstControlSourceGetValueArray) interpolate_none_get_string_value_array
};
/* linear interpolation */
/* smoothes inbetween values */
#define DEFINE_LINEAR_GET(vtype, round, convert) \
static inline void \
_interpolate_linear_internal_##vtype (GstClockTime timestamp1, g##vtype value1, GstClockTime timestamp2, g##vtype value2, GstClockTime timestamp, g##vtype min, g##vtype max, g##vtype *ret) \
{ \
if (GST_CLOCK_TIME_IS_VALID (timestamp2)) { \
gdouble slope; \
\
slope = ((gdouble) convert (value2) - (gdouble) convert (value1)) / gst_guint64_to_gdouble (timestamp2 - timestamp1); \
\
if (round) \
*ret = (g##vtype) (convert (value1) + gst_guint64_to_gdouble (timestamp - timestamp1) * slope + 0.5); \
else \
*ret = (g##vtype) (convert (value1) + gst_guint64_to_gdouble (timestamp - timestamp1) * slope); \
} else { \
*ret = value1; \
} \
*ret = CLAMP (*ret, min, max); \
} \
\
static gboolean \
interpolate_linear_get_##vtype (GstTimedValueControlSource *self, GstClockTime timestamp, GValue *value) \
{ \
g##vtype ret, min, max; \
GSequenceIter *iter; \
GstControlPoint *cp1, *cp2 = NULL, cp = {0, }; \
\
g_mutex_lock (self->lock); \
\
min = g_value_get_##vtype (&self->minimum_value); \
max = g_value_get_##vtype (&self->maximum_value); \
\
iter = gst_timed_value_control_source_find_control_point_iter (self, timestamp); \
if (iter) { \
cp1 = g_sequence_get (iter); \
iter = g_sequence_iter_next (iter); \
} else { \
cp.timestamp = G_GUINT64_CONSTANT(0); \
g_value_init (&cp.value, self->type); \
g_value_copy (&self->default_value, &cp.value); \
cp1 = &cp; \
if (G_LIKELY (self->values)) \
iter = g_sequence_get_begin_iter (self->values); \
} \
if (iter && !g_sequence_iter_is_end (iter)) \
cp2 = g_sequence_get (iter); \
\
_interpolate_linear_internal_##vtype (cp1->timestamp, g_value_get_##vtype (&cp1->value), (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE), (cp2 ? g_value_get_##vtype (&cp2->value) : 0), timestamp, min, max, &ret); \
g_value_set_##vtype (value, ret); \
g_mutex_unlock (self->lock); \
if (cp1 == &cp) \
g_value_unset (&cp.value); \
return TRUE; \
} \
\
static gboolean \
interpolate_linear_get_##vtype##_value_array (GstTimedValueControlSource *self, \
GstClockTime timestamp, GstClockTime interval, guint n_values, gpointer _values) \
{ \
guint i; \
GstClockTime ts = timestamp; \
GstClockTime next_ts = 0; \
g##vtype *values = (g##vtype *) _values; \
GSequenceIter *iter1, *iter2 = NULL; \
GstControlPoint *cp1 = NULL, *cp2 = NULL, cp = {0, }; \
g##vtype val1 = 0, val2 = 0, min, max; \
\
g_mutex_lock (self->lock); \
\
cp.timestamp = G_GUINT64_CONSTANT(0); \
g_value_init (&cp.value, self->type); \
g_value_copy (&self->default_value, &cp.value); \
\
min = g_value_get_##vtype (&self->minimum_value); \
max = g_value_get_##vtype (&self->maximum_value); \
\
for(i = 0; i < n_values; i++) { \
if (timestamp >= next_ts) { \
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts); \
if (!iter1) { \
cp1 = &cp; \
if (G_LIKELY (self->values)) \
iter2 = g_sequence_get_begin_iter (self->values); \
else \
iter2 = NULL; \
} else { \
cp1 = g_sequence_get (iter1); \
iter2 = g_sequence_iter_next (iter1); \
} \
\
if (iter2 && !g_sequence_iter_is_end (iter2)) { \
cp2 = g_sequence_get (iter2); \
next_ts = cp2->timestamp; \
} else { \
next_ts = GST_CLOCK_TIME_NONE; \
} \
val1 = g_value_get_##vtype (&cp1->value); \
if (cp2) \
val2 = g_value_get_##vtype (&cp2->value); \
} \
_interpolate_linear_internal_##vtype (cp1->timestamp, val1, (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE), (cp2 ? val2 : 0), ts, min, max, values); \
ts += interval; \
values++; \
} \
g_mutex_unlock (self->lock); \
g_value_unset (&cp.value); \
return TRUE; \
}
DEFINE_LINEAR_GET (int, TRUE, EMPTY);
DEFINE_LINEAR_GET (uint, TRUE, EMPTY);
DEFINE_LINEAR_GET (long, TRUE, EMPTY);
DEFINE_LINEAR_GET (ulong, TRUE, EMPTY);
DEFINE_LINEAR_GET (int64, TRUE, EMPTY);
DEFINE_LINEAR_GET (uint64, TRUE, gst_guint64_to_gdouble);
DEFINE_LINEAR_GET (float, FALSE, EMPTY);
DEFINE_LINEAR_GET (double, FALSE, EMPTY);
static GstInterpolateMethod interpolate_linear = {
(GstControlSourceGetValue) interpolate_linear_get_int,
(GstControlSourceGetValueArray) interpolate_linear_get_int_value_array,
(GstControlSourceGetValue) interpolate_linear_get_uint,
(GstControlSourceGetValueArray) interpolate_linear_get_uint_value_array,
(GstControlSourceGetValue) interpolate_linear_get_long,
(GstControlSourceGetValueArray) interpolate_linear_get_long_value_array,
(GstControlSourceGetValue) interpolate_linear_get_ulong,
(GstControlSourceGetValueArray) interpolate_linear_get_ulong_value_array,
(GstControlSourceGetValue) interpolate_linear_get_int64,
(GstControlSourceGetValueArray) interpolate_linear_get_int64_value_array,
(GstControlSourceGetValue) interpolate_linear_get_uint64,
(GstControlSourceGetValueArray) interpolate_linear_get_uint64_value_array,
(GstControlSourceGetValue) interpolate_linear_get_float,
(GstControlSourceGetValueArray) interpolate_linear_get_float_value_array,
(GstControlSourceGetValue) interpolate_linear_get_double,
(GstControlSourceGetValueArray) interpolate_linear_get_double_value_array,
(GstControlSourceGetValue) NULL,
(GstControlSourceGetValueArray) NULL,
(GstControlSourceGetValue) NULL,
(GstControlSourceGetValueArray) NULL,
(GstControlSourceGetValue) NULL,
(GstControlSourceGetValueArray) NULL
};
/* square interpolation */
/* cubic interpolation */
/* The following functions implement a natural cubic spline interpolator.
* For details look at http://en.wikipedia.org/wiki/Spline_interpolation
*
* Instead of using a real matrix with n^2 elements for the linear system
* of equations we use three arrays o, p, q to hold the tridiagonal matrix
* as following to save memory:
*
* p[0] q[0] 0 0 0
* o[1] p[1] q[1] 0 0
* 0 o[2] p[2] q[2] .
* . . . . .
*/
#define DEFINE_CUBIC_GET(vtype,round, convert) \
static void \
_interpolate_cubic_update_cache_##vtype (GstTimedValueControlSource *self) \
{ \
gint i, n = self->nvalues; \
gdouble *o = g_new0 (gdouble, n); \
gdouble *p = g_new0 (gdouble, n); \
gdouble *q = g_new0 (gdouble, n); \
\
gdouble *h = g_new0 (gdouble, n); \
gdouble *b = g_new0 (gdouble, n); \
gdouble *z = g_new0 (gdouble, n); \
\
GSequenceIter *iter; \
GstControlPoint *cp; \
GstClockTime x, x_next; \
g##vtype y_prev, y, y_next; \
\
/* Fill linear system of equations */ \
iter = g_sequence_get_begin_iter (self->values); \
cp = g_sequence_get (iter); \
x = cp->timestamp; \
y = g_value_get_##vtype (&cp->value); \
\
p[0] = 1.0; \
\
iter = g_sequence_iter_next (iter); \
cp = g_sequence_get (iter); \
x_next = cp->timestamp; \
y_next = g_value_get_##vtype (&cp->value); \
h[0] = gst_guint64_to_gdouble (x_next - x); \
\
for (i = 1; i < n-1; i++) { \
/* Shuffle x and y values */ \
y_prev = y; \
x = x_next; \
y = y_next; \
iter = g_sequence_iter_next (iter); \
cp = g_sequence_get (iter); \
x_next = cp->timestamp; \
y_next = g_value_get_##vtype (&cp->value); \
\
h[i] = gst_guint64_to_gdouble (x_next - x); \
o[i] = h[i-1]; \
p[i] = 2.0 * (h[i-1] + h[i]); \
q[i] = h[i]; \
b[i] = convert (y_next - y) / h[i] - convert (y - y_prev) / h[i-1]; \
} \
p[n-1] = 1.0; \
\
/* Use Gauss elimination to set everything below the \
* diagonal to zero */ \
for (i = 1; i < n-1; i++) { \
gdouble a = o[i] / p[i-1]; \
p[i] -= a * q[i-1]; \
b[i] -= a * b[i-1]; \
} \
\
/* Solve everything else from bottom to top */ \
for (i = n-2; i > 0; i--) \
z[i] = (b[i] - q[i] * z[i+1]) / p[i]; \
\
/* Save cache next in the GstControlPoint */ \
\
iter = g_sequence_get_begin_iter (self->values); \
for (i = 0; i < n; i++) { \
cp = g_sequence_get (iter); \
cp->cache.cubic.h = h[i]; \
cp->cache.cubic.z = z[i]; \
iter = g_sequence_iter_next (iter); \
} \
\
/* Free our temporary arrays */ \
g_free (o); \
g_free (p); \
g_free (q); \
g_free (h); \
g_free (b); \
g_free (z); \
} \
\
static inline void \
_interpolate_cubic_get_##vtype (GstTimedValueControlSource *self, GstControlPoint *cp1, g##vtype value1, GstControlPoint *cp2, g##vtype value2, GstClockTime timestamp, g##vtype min, g##vtype max, g##vtype *ret) \
{ \
if (!self->valid_cache) { \
_interpolate_cubic_update_cache_##vtype (self); \
self->valid_cache = TRUE; \
} \
\
if (cp2) { \
gdouble diff1, diff2; \
gdouble out; \
\
diff1 = gst_guint64_to_gdouble (timestamp - cp1->timestamp); \
diff2 = gst_guint64_to_gdouble (cp2->timestamp - timestamp); \
\
out = (cp2->cache.cubic.z * diff1 * diff1 * diff1 + cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h; \
out += (convert (value2) / cp1->cache.cubic.h - cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1; \
out += (convert (value1) / cp1->cache.cubic.h - cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2; \
\
if (round) \
*ret = (g##vtype) (out + 0.5); \
else \
*ret = (g##vtype) out; \
} \
else { \
*ret = value1; \
} \
*ret = CLAMP (*ret, min, max); \
} \
\
static gboolean \
interpolate_cubic_get_##vtype (GstTimedValueControlSource *self, GstClockTime timestamp, GValue *value) \
{ \
g##vtype ret, min, max; \
GSequenceIter *iter; \
GstControlPoint *cp1, *cp2 = NULL, cp = {0, }; \
\
if (self->nvalues <= 2) \
return interpolate_linear_get_##vtype (self, timestamp, value); \
\
g_mutex_lock (self->lock); \
\
min = g_value_get_##vtype (&self->minimum_value); \
max = g_value_get_##vtype (&self->maximum_value); \
\
iter = gst_timed_value_control_source_find_control_point_iter (self, timestamp); \
if (iter) { \
cp1 = g_sequence_get (iter); \
iter = g_sequence_iter_next (iter); \
} else { \
cp.timestamp = G_GUINT64_CONSTANT(0); \
g_value_init (&cp.value, self->type); \
g_value_copy (&self->default_value, &cp.value); \
cp1 = &cp; \
if (G_LIKELY (self->values)) \
iter = g_sequence_get_begin_iter (self->values); \
} \
if (iter && !g_sequence_iter_is_end (iter)) \
cp2 = g_sequence_get (iter); \
\
_interpolate_cubic_get_##vtype (self, cp1, g_value_get_##vtype (&cp1->value), cp2, (cp2 ? g_value_get_##vtype (&cp2->value) : 0), timestamp, min, max, &ret); \
g_value_set_##vtype (value, ret); \
g_mutex_unlock (self->lock); \
if (cp1 == &cp) \
g_value_unset (&cp.value); \
return TRUE; \
} \
\
static gboolean \
interpolate_cubic_get_##vtype##_value_array (GstTimedValueControlSource *self, \
GstClockTime timestamp, GstClockTime interval, guint n_values, gpointer _values) \
{ \
guint i; \
GstClockTime ts = timestamp; \
GstClockTime next_ts = 0; \
g##vtype *values = (g##vtype *) _values; \
GSequenceIter *iter1, *iter2 = NULL; \
GstControlPoint *cp1 = NULL, *cp2 = NULL, cp = {0, }; \
g##vtype val1 = 0, val2 = 0, min, max; \
\
if (self->nvalues <= 2) \
return interpolate_linear_get_##vtype##_value_array (self, timestamp, interval, n_values, values); \
\
g_mutex_lock (self->lock); \
\
cp.timestamp = G_GUINT64_CONSTANT(0); \
g_value_init (&cp.value, self->type); \
g_value_copy (&self->default_value, &cp.value); \
\
min = g_value_get_##vtype (&self->minimum_value); \
max = g_value_get_##vtype (&self->maximum_value); \
\
for(i = 0; i < n_values; i++) { \
if (timestamp >= next_ts) { \
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts); \
if (!iter1) { \
cp1 = &cp; \
if (G_LIKELY (self->values)) \
iter2 = g_sequence_get_begin_iter (self->values); \
else \
iter2 = NULL; \
} else { \
cp1 = g_sequence_get (iter1); \
iter2 = g_sequence_iter_next (iter1); \
} \
\
if (iter2 && !g_sequence_iter_is_end (iter2)) { \
cp2 = g_sequence_get (iter2); \
next_ts = cp2->timestamp; \
} else { \
next_ts = GST_CLOCK_TIME_NONE; \
} \
val1 = g_value_get_##vtype (&cp1->value); \
if (cp2) \
val2 = g_value_get_##vtype (&cp2->value); \
} \
_interpolate_cubic_get_##vtype (self, cp1, val1, cp2, val2, timestamp, min, max, values); \
ts += interval; \
values++; \
} \
g_mutex_unlock (self->lock); \
g_value_unset (&cp.value); \
return TRUE; \
}
DEFINE_CUBIC_GET (int, TRUE, EMPTY);
DEFINE_CUBIC_GET (uint, TRUE, EMPTY);
DEFINE_CUBIC_GET (long, TRUE, EMPTY);
DEFINE_CUBIC_GET (ulong, TRUE, EMPTY);
DEFINE_CUBIC_GET (int64, TRUE, EMPTY);
DEFINE_CUBIC_GET (uint64, TRUE, gst_guint64_to_gdouble);
DEFINE_CUBIC_GET (float, FALSE, EMPTY);
DEFINE_CUBIC_GET (double, FALSE, EMPTY);
static GstInterpolateMethod interpolate_cubic = {
(GstControlSourceGetValue) interpolate_cubic_get_int,
(GstControlSourceGetValueArray) interpolate_cubic_get_int_value_array,
(GstControlSourceGetValue) interpolate_cubic_get_uint,
(GstControlSourceGetValueArray) interpolate_cubic_get_uint_value_array,
(GstControlSourceGetValue) interpolate_cubic_get_long,
(GstControlSourceGetValueArray) interpolate_cubic_get_long_value_array,
(GstControlSourceGetValue) interpolate_cubic_get_ulong,
(GstControlSourceGetValueArray) interpolate_cubic_get_ulong_value_array,
(GstControlSourceGetValue) interpolate_cubic_get_int64,
(GstControlSourceGetValueArray) interpolate_cubic_get_int64_value_array,
(GstControlSourceGetValue) interpolate_cubic_get_uint64,
(GstControlSourceGetValueArray) interpolate_cubic_get_uint64_value_array,
(GstControlSourceGetValue) interpolate_cubic_get_float,
(GstControlSourceGetValueArray) interpolate_cubic_get_float_value_array,
(GstControlSourceGetValue) interpolate_cubic_get_double,
(GstControlSourceGetValueArray) interpolate_cubic_get_double_value_array,
(GstControlSourceGetValue) NULL,
(GstControlSourceGetValueArray) NULL,
(GstControlSourceGetValue) NULL,
(GstControlSourceGetValueArray) NULL,
(GstControlSourceGetValue) NULL,
(GstControlSourceGetValueArray) NULL
};
/* register all interpolation methods */
GstInterpolateMethod *priv_gst_interpolation_methods[] = {
&interpolate_none,
&interpolate_linear,
&interpolate_cubic
};
guint priv_gst_num_interpolation_methods =
G_N_ELEMENTS (priv_gst_interpolation_methods);

View file

@ -40,12 +40,419 @@
#include <gst/gst.h>
#include "gstinterpolationcontrolsource.h"
#include "gstinterpolationcontrolsourceprivate.h"
#include "gst/glib-compat-private.h"
#include <math.h>
#define GST_CAT_DEFAULT controller_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
/* steps-like (no-)interpolation, default */
/* just returns the value for the most recent key-frame */
static inline const gdouble
_interpolate_none (GstTimedValueControlSource * self, GSequenceIter * iter)
{
GstControlPoint *cp = g_sequence_get (iter);
return cp->value;
}
static gboolean
interpolate_none_get (GstTimedValueControlSource * self, GstClockTime timestamp,
gdouble * value)
{
gboolean ret = FALSE;
GSequenceIter *iter;
g_mutex_lock (self->lock);
iter =
gst_timed_value_control_source_find_control_point_iter (self, timestamp);
if (iter) {
*value = _interpolate_none (self, iter);
ret = TRUE;
}
g_mutex_unlock (self->lock);
return ret;
}
static gboolean
interpolate_none_get_value_array (GstTimedValueControlSource * self,
GstClockTime timestamp, GstClockTime interval, guint n_values,
gdouble * values)
{
gboolean ret = FALSE;
guint i;
GstClockTime ts = timestamp;
GstClockTime next_ts = 0;
gdouble val;
GSequenceIter *iter1 = NULL, *iter2 = NULL;
g_mutex_lock (self->lock);
for (i = 0; i < n_values; i++) {
val = FP_NAN;
if (ts >= next_ts) {
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
if (!iter1) {
if (G_LIKELY (self->values))
iter2 = g_sequence_get_begin_iter (self->values);
else
iter2 = NULL;
} else {
iter2 = g_sequence_iter_next (iter1);
}
if (iter2 && !g_sequence_iter_is_end (iter2)) {
GstControlPoint *cp;
cp = g_sequence_get (iter2);
next_ts = cp->timestamp;
} else {
next_ts = GST_CLOCK_TIME_NONE;
}
if (iter1) {
val = _interpolate_none (self, iter1);
ret = TRUE;
}
}
*values = val;
ts += interval;
values++;
}
g_mutex_unlock (self->lock);
return ret;
}
/* linear interpolation */
/* smoothes inbetween values */
static inline gdouble
_interpolate_linear (GstClockTime timestamp1, gdouble value1,
GstClockTime timestamp2, gdouble value2, GstClockTime timestamp)
{
if (GST_CLOCK_TIME_IS_VALID (timestamp2)) {
gdouble slope;
slope =
(value2 - value1) / gst_guint64_to_gdouble (timestamp2 - timestamp1);
return value1 + (gst_guint64_to_gdouble (timestamp - timestamp1) * slope);
} else {
return value1;
}
}
static gboolean
interpolate_linear_get (GstTimedValueControlSource * self,
GstClockTime timestamp, gdouble * value)
{
gboolean ret = FALSE;
GSequenceIter *iter;
GstControlPoint *cp1, *cp2;
g_mutex_lock (self->lock);
iter =
gst_timed_value_control_source_find_control_point_iter (self, timestamp);
if (iter) {
cp1 = g_sequence_get (iter);
iter = g_sequence_iter_next (iter);
if (iter && !g_sequence_iter_is_end (iter)) {
cp2 = g_sequence_get (iter);
} else {
cp2 = NULL;
}
*value = _interpolate_linear (cp1->timestamp, cp1->value,
(cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE),
(cp2 ? cp2->value : 0.0), timestamp);
ret = TRUE;
}
g_mutex_unlock (self->lock);
return ret;
}
static gboolean
interpolate_linear_get_value_array (GstTimedValueControlSource * self,
GstClockTime timestamp, GstClockTime interval, guint n_values,
gdouble * values)
{
gboolean ret = FALSE;
guint i;
GstClockTime ts = timestamp;
GstClockTime next_ts = 0;
gdouble val;
GSequenceIter *iter1, *iter2 = NULL;
GstControlPoint *cp1 = NULL, *cp2 = NULL;
g_mutex_lock (self->lock);
for (i = 0; i < n_values; i++) {
val = FP_NAN;
if (ts >= next_ts) {
cp1 = cp2 = NULL;
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
if (!iter1) {
if (G_LIKELY (self->values))
iter2 = g_sequence_get_begin_iter (self->values);
else
iter2 = NULL;
} else {
cp1 = g_sequence_get (iter1);
iter2 = g_sequence_iter_next (iter1);
}
if (iter2 && !g_sequence_iter_is_end (iter2)) {
cp2 = g_sequence_get (iter2);
next_ts = cp2->timestamp;
} else {
next_ts = GST_CLOCK_TIME_NONE;
}
}
if (cp1) {
val = _interpolate_linear (cp1->timestamp, cp1->value,
(cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE),
(cp2 ? cp2->value : 0.0), ts);
ret = TRUE;
}
*values = val;
ts += interval;
values++;
}
g_mutex_unlock (self->lock);
return ret;
}
/* cubic interpolation */
/* The following functions implement a natural cubic spline interpolator.
* For details look at http://en.wikipedia.org/wiki/Spline_interpolation
*
* Instead of using a real matrix with n^2 elements for the linear system
* of equations we use three arrays o, p, q to hold the tridiagonal matrix
* as following to save memory:
*
* p[0] q[0] 0 0 0
* o[1] p[1] q[1] 0 0
* 0 o[2] p[2] q[2] .
* . . . . .
*/
static void
_interpolate_cubic_update_cache (GstTimedValueControlSource * self)
{
gint i, n = self->nvalues;
gdouble *o = g_new0 (gdouble, n);
gdouble *p = g_new0 (gdouble, n);
gdouble *q = g_new0 (gdouble, n);
gdouble *h = g_new0 (gdouble, n);
gdouble *b = g_new0 (gdouble, n);
gdouble *z = g_new0 (gdouble, n);
GSequenceIter *iter;
GstControlPoint *cp;
GstClockTime x, x_next;
gdouble y_prev, y, y_next;
/* Fill linear system of equations */
iter = g_sequence_get_begin_iter (self->values);
cp = g_sequence_get (iter);
x = cp->timestamp;
y = cp->value;
p[0] = 1.0;
iter = g_sequence_iter_next (iter);
cp = g_sequence_get (iter);
x_next = cp->timestamp;
y_next = cp->value;
h[0] = gst_guint64_to_gdouble (x_next - x);
for (i = 1; i < n - 1; i++) {
/* Shuffle x and y values */
y_prev = y;
x = x_next;
y = y_next;
iter = g_sequence_iter_next (iter);
cp = g_sequence_get (iter);
x_next = cp->timestamp;
y_next = cp->value;
h[i] = gst_guint64_to_gdouble (x_next - x);
o[i] = h[i - 1];
p[i] = 2.0 * (h[i - 1] + h[i]);
q[i] = h[i];
b[i] = (y_next - y) / h[i] - (y - y_prev) / h[i - 1];
}
p[n - 1] = 1.0;
/* Use Gauss elimination to set everything below the diagonal to zero */
for (i = 1; i < n - 1; i++) {
gdouble a = o[i] / p[i - 1];
p[i] -= a * q[i - 1];
b[i] -= a * b[i - 1];
}
/* Solve everything else from bottom to top */
for (i = n - 2; i > 0; i--)
z[i] = (b[i] - q[i] * z[i + 1]) / p[i];
/* Save cache next in the GstControlPoint */
iter = g_sequence_get_begin_iter (self->values);
for (i = 0; i < n; i++) {
cp = g_sequence_get (iter);
cp->cache.cubic.h = h[i];
cp->cache.cubic.z = z[i];
iter = g_sequence_iter_next (iter);
}
/* Free our temporary arrays */
g_free (o);
g_free (p);
g_free (q);
g_free (h);
g_free (b);
g_free (z);
}
static inline gdouble
_interpolate_cubic (GstTimedValueControlSource * self, GstControlPoint * cp1,
gdouble value1, GstControlPoint * cp2, gdouble value2,
GstClockTime timestamp)
{
if (!self->valid_cache) {
_interpolate_cubic_update_cache (self);
self->valid_cache = TRUE;
}
if (cp2) {
gdouble diff1, diff2;
gdouble out;
diff1 = gst_guint64_to_gdouble (timestamp - cp1->timestamp);
diff2 = gst_guint64_to_gdouble (cp2->timestamp - timestamp);
out =
(cp2->cache.cubic.z * diff1 * diff1 * diff1 +
cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h;
out +=
(value2 / cp1->cache.cubic.h -
cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1;
out +=
(value1 / cp1->cache.cubic.h -
cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2;
return out;
} else {
return value1;
}
}
static gboolean
interpolate_cubic_get (GstTimedValueControlSource * self,
GstClockTime timestamp, gdouble * value)
{
gboolean ret = FALSE;
GSequenceIter *iter;
GstControlPoint *cp1, *cp2 = NULL;
if (self->nvalues <= 2)
return interpolate_linear_get (self, timestamp, value);
g_mutex_lock (self->lock);
iter =
gst_timed_value_control_source_find_control_point_iter (self, timestamp);
if (iter) {
cp1 = g_sequence_get (iter);
iter = g_sequence_iter_next (iter);
if (iter && !g_sequence_iter_is_end (iter)) {
cp2 = g_sequence_get (iter);
} else {
cp2 = NULL;
}
*value = _interpolate_cubic (self, cp1, cp1->value, cp2,
(cp2 ? cp2->value : 0.0), timestamp);
ret = TRUE;
}
g_mutex_unlock (self->lock);
return ret;
}
static gboolean
interpolate_cubic_get_value_array (GstTimedValueControlSource * self,
GstClockTime timestamp, GstClockTime interval, guint n_values,
gdouble * values)
{
gboolean ret = FALSE;
guint i;
GstClockTime ts = timestamp;
GstClockTime next_ts = 0;
gdouble val;
GSequenceIter *iter1, *iter2 = NULL;
GstControlPoint *cp1 = NULL, *cp2 = NULL;
if (self->nvalues <= 2)
return interpolate_linear_get_value_array (self, timestamp, interval,
n_values, values);
g_mutex_lock (self->lock);
for (i = 0; i < n_values; i++) {
val = FP_NAN;
if (ts >= next_ts) {
cp1 = cp2 = NULL;
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
if (!iter1) {
if (G_LIKELY (self->values))
iter2 = g_sequence_get_begin_iter (self->values);
else
iter2 = NULL;
} else {
cp1 = g_sequence_get (iter1);
iter2 = g_sequence_iter_next (iter1);
}
if (iter2 && !g_sequence_iter_is_end (iter2)) {
cp2 = g_sequence_get (iter2);
next_ts = cp2->timestamp;
} else {
next_ts = GST_CLOCK_TIME_NONE;
}
if (cp1) {
val = _interpolate_cubic (self, cp1, cp1->value, cp2,
(cp2 ? cp2->value : 0.0), timestamp);
ret = TRUE;
}
}
*values = val;
ts += interval;
values++;
}
g_mutex_unlock (self->lock);
return ret;
}
static struct
{
GstControlSourceGetValue get;
GstControlSourceGetValueArray get_value_array;
} interpolation_modes[] = {
{
(GstControlSourceGetValue) interpolate_none_get,
(GstControlSourceGetValueArray) interpolate_none_get_value_array}, {
(GstControlSourceGetValue) interpolate_linear_get,
(GstControlSourceGetValueArray) interpolate_linear_get_value_array}, {
(GstControlSourceGetValue) interpolate_cubic_get,
(GstControlSourceGetValueArray) interpolate_cubic_get_value_array}
};
static const guint num_interpolation_modes = G_N_ELEMENTS (interpolation_modes);
enum
{
PROP_MODE = 1
@ -101,105 +508,24 @@ static gboolean
gst_interpolation_control_source_set_interpolation_mode
(GstInterpolationControlSource * self, GstInterpolationMode mode)
{
gboolean ret = TRUE;
GstControlSource *csource = GST_CONTROL_SOURCE (self);
if (mode >= priv_gst_num_interpolation_methods
|| priv_gst_interpolation_methods[mode] == NULL) {
if (mode >= num_interpolation_modes && (int) mode < 0) {
GST_WARNING ("interpolation mode %d invalid or not implemented yet", mode);
return FALSE;
}
GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (self);
switch (gst_timed_value_control_source_get_base_value_type (
(GstTimedValueControlSource *) self)) {
case G_TYPE_INT:
csource->get_value = priv_gst_interpolation_methods[mode]->get_int;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_int_value_array;
break;
case G_TYPE_UINT:
csource->get_value = priv_gst_interpolation_methods[mode]->get_uint;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_uint_value_array;
break;
case G_TYPE_LONG:
csource->get_value = priv_gst_interpolation_methods[mode]->get_long;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_long_value_array;
break;
case G_TYPE_ULONG:
csource->get_value = priv_gst_interpolation_methods[mode]->get_ulong;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_ulong_value_array;
break;
case G_TYPE_INT64:
csource->get_value = priv_gst_interpolation_methods[mode]->get_int64;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_int64_value_array;
break;
case G_TYPE_UINT64:
csource->get_value = priv_gst_interpolation_methods[mode]->get_uint64;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_uint64_value_array;
break;
case G_TYPE_FLOAT:
csource->get_value = priv_gst_interpolation_methods[mode]->get_float;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_float_value_array;
break;
case G_TYPE_DOUBLE:
csource->get_value = priv_gst_interpolation_methods[mode]->get_double;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_double_value_array;
break;
case G_TYPE_BOOLEAN:
csource->get_value = priv_gst_interpolation_methods[mode]->get_boolean;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_boolean_value_array;
break;
case G_TYPE_ENUM:
csource->get_value = priv_gst_interpolation_methods[mode]->get_enum;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_enum_value_array;
break;
case G_TYPE_STRING:
csource->get_value = priv_gst_interpolation_methods[mode]->get_string;
csource->get_value_array =
priv_gst_interpolation_methods[mode]->get_string_value_array;
break;
default:
ret = FALSE;
break;
}
csource->get_value = interpolation_modes[mode].get;
csource->get_value_array = interpolation_modes[mode].get_value_array;
/* Incomplete implementation */
if (!csource->get_value || !csource->get_value_array) {
ret = FALSE;
}
gst_timed_value_control_invalidate_cache ((GstTimedValueControlSource *)
csource);
self->priv->interpolation_mode = mode;
GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (self);
return ret;
}
static gboolean
gst_interpolation_control_source_bind (GstControlSource * csource,
GParamSpec * pspec)
{
if (GST_CONTROL_SOURCE_CLASS
(gst_interpolation_control_source_parent_class)->bind (csource, pspec)) {
GstInterpolationControlSource *self =
GST_INTERPOLATION_CONTROL_SOURCE (csource);
if (gst_interpolation_control_source_set_interpolation_mode (self,
self->priv->interpolation_mode))
return TRUE;
}
return FALSE;
return TRUE;
}
static void
@ -208,7 +534,8 @@ gst_interpolation_control_source_init (GstInterpolationControlSource * self)
self->priv =
G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_INTERPOLATION_CONTROL_SOURCE,
GstInterpolationControlSourcePrivate);
self->priv->interpolation_mode = GST_INTERPOLATION_MODE_NONE;
gst_interpolation_control_source_set_interpolation_mode (self,
GST_INTERPOLATION_MODE_NONE);
}
static void
@ -251,7 +578,7 @@ gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass
* klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
//GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
g_type_class_add_private (klass,
sizeof (GstInterpolationControlSourcePrivate));
@ -259,8 +586,6 @@ gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass
gobject_class->set_property = gst_interpolation_control_source_set_property;
gobject_class->get_property = gst_interpolation_control_source_get_property;
csource_class->bind = gst_interpolation_control_source_bind;
g_object_class_install_property (gobject_class, PROP_MODE,
g_param_spec_enum ("mode", "Mode", "Interpolation mode",
GST_TYPE_INTERPOLATION_MODE, GST_INTERPOLATION_MODE_NONE,

View file

@ -1,87 +0,0 @@
/* GStreamer
*
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
*
* gstinterpolationcontrolsourceprivate.h: Private declarations for the
* GstInterpolationControlSource
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_INTERPOLATION_CONTROL_SOURCE_PRIVATE_H__
#define __GST_INTERPOLATION_CONTROL_SOURCE_PRIVATE_H__
/**
* GstInterpolateMethod:
*
* Function pointer structure to do user-defined interpolation methods
*/
typedef struct _GstInterpolateMethod
{
GstControlSourceGetValue get_int;
GstControlSourceGetValueArray get_int_value_array;
GstControlSourceGetValue get_uint;
GstControlSourceGetValueArray get_uint_value_array;
GstControlSourceGetValue get_long;
GstControlSourceGetValueArray get_long_value_array;
GstControlSourceGetValue get_ulong;
GstControlSourceGetValueArray get_ulong_value_array;
GstControlSourceGetValue get_int64;
GstControlSourceGetValueArray get_int64_value_array;
GstControlSourceGetValue get_uint64;
GstControlSourceGetValueArray get_uint64_value_array;
GstControlSourceGetValue get_float;
GstControlSourceGetValueArray get_float_value_array;
GstControlSourceGetValue get_double;
GstControlSourceGetValueArray get_double_value_array;
GstControlSourceGetValue get_boolean;
GstControlSourceGetValueArray get_boolean_value_array;
GstControlSourceGetValue get_enum;
GstControlSourceGetValueArray get_enum_value_array;
GstControlSourceGetValue get_string;
GstControlSourceGetValueArray get_string_value_array;
} GstInterpolateMethod;
/**
* GstControlPoint:
*
* a internal structure for value+time and various temporary
* values used for interpolation. This "inherits" from
* GstTimedValue.
*/
typedef struct _GstControlPoint
{
/* fields from GstTimedValue. DO NOT CHANGE! */
GstClockTime timestamp; /* timestamp of the value change */
GValue value; /* the new value */
/* internal fields */
/* Caches for the interpolators */
union {
struct {
gdouble h;
gdouble z;
} cubic;
} cache;
} GstControlPoint;
extern GstInterpolateMethod *priv_gst_interpolation_methods[];
extern guint priv_gst_num_interpolation_methods;
#endif /* __GST_INTERPOLATION_CONTROL_SOURCE_PRIVATE_H__ */

File diff suppressed because it is too large Load diff

View file

@ -1,64 +0,0 @@
/* GStreamer
*
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
*
* gstlfocontrolsourceprivate.h: Private declarations for the
* GstLFOControlSource
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_LFO_CONTROL_SOURCE_PRIVATE_H__
#define __GST_LFO_CONTROL_SOURCE_PRIVATE_H__
typedef struct _GstWaveformImplementation
{
GstControlSourceGetValue get_int;
GstControlSourceGetValueArray get_int_value_array;
GstControlSourceGetValue get_uint;
GstControlSourceGetValueArray get_uint_value_array;
GstControlSourceGetValue get_long;
GstControlSourceGetValueArray get_long_value_array;
GstControlSourceGetValue get_ulong;
GstControlSourceGetValueArray get_ulong_value_array;
GstControlSourceGetValue get_int64;
GstControlSourceGetValueArray get_int64_value_array;
GstControlSourceGetValue get_uint64;
GstControlSourceGetValueArray get_uint64_value_array;
GstControlSourceGetValue get_float;
GstControlSourceGetValueArray get_float_value_array;
GstControlSourceGetValue get_double;
GstControlSourceGetValueArray get_double_value_array;
} GstWaveformImplementation;
struct _GstLFOControlSourcePrivate
{
GType type; /* type of the handled property */
GType base; /* base-type of the handled property */
GValue minimum_value; /* min value for the handled property */
GValue maximum_value; /* max value for the handled property */
GstLFOWaveform waveform;
gdouble frequency;
GstClockTime period;
GstClockTime timeshift;
GValue amplitude;
GValue offset;
};
#endif /* __GST_LFO_CONTROL_SOURCE_PRIVATE_H__ */

View file

@ -39,7 +39,6 @@
#include <gst/gst.h>
#include "gstinterpolationcontrolsource.h"
#include "gstinterpolationcontrolsourceprivate.h"
#include "gst/glib-compat-private.h"
#define GST_CAT_DEFAULT controller_debug
@ -64,7 +63,6 @@ gst_control_point_free (GstControlPoint * cp)
{
g_return_if_fail (cp);
g_value_unset (&cp->value);
g_slice_free (GstControlPoint, cp);
}
@ -76,15 +74,6 @@ gst_timed_value_control_source_reset (GstTimedValueControlSource * self)
csource->get_value = NULL;
csource->get_value_array = NULL;
self->type = self->base = G_TYPE_INVALID;
if (G_IS_VALUE (&self->default_value))
g_value_unset (&self->default_value);
if (G_IS_VALUE (&self->minimum_value))
g_value_unset (&self->minimum_value);
if (G_IS_VALUE (&self->maximum_value))
g_value_unset (&self->maximum_value);
if (self->values) {
g_sequence_free (self->values);
self->values = NULL;
@ -94,154 +83,6 @@ gst_timed_value_control_source_reset (GstTimedValueControlSource * self)
self->valid_cache = FALSE;
}
static gboolean
gst_timed_value_control_source_bind (GstControlSource * source,
GParamSpec * pspec)
{
GType type, base;
GstTimedValueControlSource *self = (GstTimedValueControlSource *) source;
gboolean ret = TRUE;
if (self->type != G_TYPE_INVALID) {
gst_timed_value_control_source_reset (self);
}
/* get the fundamental base type */
self->type = base = type = G_PARAM_SPEC_VALUE_TYPE (pspec);
while ((type = g_type_parent (type)))
base = type;
self->base = base;
/* restore type */
type = self->type;
switch (base) {
case G_TYPE_INT:{
GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
g_value_init (&self->default_value, type);
g_value_set_int (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_int (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_int (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_UINT:{
GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
g_value_init (&self->default_value, type);
g_value_set_uint (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_uint (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_uint (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_LONG:{
GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
g_value_init (&self->default_value, type);
g_value_set_long (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_long (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_long (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_ULONG:{
GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
g_value_init (&self->default_value, type);
g_value_set_ulong (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_ulong (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_ulong (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_INT64:{
GParamSpecInt64 *tpspec = G_PARAM_SPEC_INT64 (pspec);
g_value_init (&self->default_value, type);
g_value_set_int64 (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_int64 (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_int64 (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_UINT64:{
GParamSpecUInt64 *tpspec = G_PARAM_SPEC_UINT64 (pspec);
g_value_init (&self->default_value, type);
g_value_set_uint64 (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_uint64 (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_uint64 (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_FLOAT:{
GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
g_value_init (&self->default_value, type);
g_value_set_float (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_float (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_float (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_DOUBLE:{
GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
g_value_init (&self->default_value, type);
g_value_set_double (&self->default_value, tpspec->default_value);
g_value_init (&self->minimum_value, type);
g_value_set_double (&self->minimum_value, tpspec->minimum);
g_value_init (&self->maximum_value, type);
g_value_set_double (&self->maximum_value, tpspec->maximum);
break;
}
case G_TYPE_BOOLEAN:{
GParamSpecBoolean *tpspec = G_PARAM_SPEC_BOOLEAN (pspec);
g_value_init (&self->default_value, type);
g_value_set_boolean (&self->default_value, tpspec->default_value);
break;
}
case G_TYPE_ENUM:{
GParamSpecEnum *tpspec = G_PARAM_SPEC_ENUM (pspec);
g_value_init (&self->default_value, type);
g_value_set_enum (&self->default_value, tpspec->default_value);
break;
}
case G_TYPE_STRING:{
GParamSpecString *tpspec = G_PARAM_SPEC_STRING (pspec);
g_value_init (&self->default_value, type);
g_value_set_string (&self->default_value, tpspec->default_value);
break;
}
default:
GST_WARNING ("incomplete implementation for paramspec type '%s'",
G_PARAM_SPEC_TYPE_NAME (pspec));
ret = FALSE;
break;
}
if (ret) {
self->valid_cache = FALSE;
self->nvalues = 0;
} else {
gst_timed_value_control_source_reset (self);
}
return ret;
}
/*
* gst_control_point_compare:
* @p1: a pointer to a #GstControlPoint
@ -278,22 +119,21 @@ gst_control_point_find (gconstpointer p1, gconstpointer p2)
static GstControlPoint *
_make_new_cp (GstTimedValueControlSource * self, GstClockTime timestamp,
const GValue * value)
const gdouble value)
{
GstControlPoint *cp;
/* create a new GstControlPoint */
cp = g_slice_new0 (GstControlPoint);
cp->timestamp = timestamp;
g_value_init (&cp->value, self->type);
g_value_copy (value, &cp->value);
cp->value = value;
return cp;
}
static void
gst_timed_value_control_source_set_internal (GstTimedValueControlSource *
self, GstClockTime timestamp, const GValue * value)
self, GstClockTime timestamp, const gdouble value)
{
GSequenceIter *iter;
@ -311,13 +151,13 @@ gst_timed_value_control_source_set_internal (GstTimedValueControlSource *
/* If the timestamp is the same just update the control point value */
if (cp->timestamp == timestamp) {
/* update control point */
g_value_reset (&cp->value);
g_value_copy (value, &cp->value);
cp->value = value;
goto done;
}
}
} else {
self->values = g_sequence_new ((GDestroyNotify) gst_control_point_free);
GST_INFO ("create new timed value sequence");
}
/* sort new cp into the prop->values list */
@ -377,12 +217,10 @@ GSequenceIter *gst_timed_value_control_source_find_control_point_iter
*/
gboolean
gst_timed_value_control_source_set (GstTimedValueControlSource * self,
GstClockTime timestamp, const GValue * value)
GstClockTime timestamp, const gdouble value)
{
g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
g_return_val_if_fail (G_IS_VALUE (value), FALSE);
g_return_val_if_fail (G_VALUE_TYPE (value) == self->type, FALSE);
g_mutex_lock (self->lock);
gst_timed_value_control_source_set_internal (self, timestamp, value);
@ -416,15 +254,10 @@ gst_timed_value_control_source_set_from_list (GstTimedValueControlSource *
if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) {
GST_WARNING ("GstTimedValued with invalid timestamp passed to %s",
GST_FUNCTION);
} else if (!G_IS_VALUE (&tv->value)) {
GST_WARNING ("GstTimedValued with invalid value passed to %s",
GST_FUNCTION);
} else if (G_VALUE_TYPE (&tv->value) != self->type) {
GST_WARNING ("incompatible value type for property");
} else {
g_mutex_lock (self->lock);
gst_timed_value_control_source_set_internal (self, tv->timestamp,
&tv->value);
tv->value);
g_mutex_unlock (self->lock);
res = TRUE;
}
@ -546,23 +379,6 @@ gst_timed_value_control_source_get_count (GstTimedValueControlSource * self)
return self->nvalues;
}
/**
* gst_timed_value_control_source_get_base_value_type:
* @self: the #GstTimedValueControlSource
*
* Get the base #GType of the property value.
*
* Returns: the #GType, %G_TYPE_INVALID if not yet known.
*/
GType
gst_timed_value_control_source_get_base_value_type (GstTimedValueControlSource *
self)
{
g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self),
G_TYPE_INVALID);
return self->base;
}
/**
* gst_timed_value_control_invalidate_cache:
* @self: the #GstTimedValueControlSource
@ -600,8 +416,7 @@ gst_timed_value_control_source_class_init (GstTimedValueControlSourceClass
* klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
//GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
gobject_class->finalize = gst_timed_value_control_source_finalize;
csource_class->bind = gst_timed_value_control_source_bind;
}

View file

@ -49,6 +49,32 @@ typedef struct _GstTimedValueControlSource GstTimedValueControlSource;
typedef struct _GstTimedValueControlSourceClass GstTimedValueControlSourceClass;
typedef struct _GstTimedValueControlSourcePrivate GstTimedValueControlSourcePrivate;
/**
* GstControlPoint:
*
* a internal structure for value+time and various temporary
* values used for interpolation. This "inherits" from
* GstTimedValue.
*/
typedef struct _GstControlPoint
{
/* fields from GstTimedValue. DO NOT CHANGE! */
GstClockTime timestamp; /* timestamp of the value change */
gdouble value; /* the new value */
/* internal fields */
/* Caches for the interpolators */
// FIXME: we should not have this here already ...
union {
struct {
gdouble h;
gdouble z;
} cubic;
} cache;
} GstControlPoint;
/**
* GstTimedValueControlSource:
*
@ -60,13 +86,6 @@ struct _GstTimedValueControlSource {
/*< protected >*/
GMutex *lock;
GType type; /* type of the handled property */
GType base; /* base-type of the handled property */
GValue default_value; /* default value for the handled property */
GValue minimum_value; /* min value for the handled property */
GValue maximum_value; /* max value for the handled property */
GSequence *values; /* List of GstControlPoint */
gint nvalues; /* Number of control points */
gboolean valid_cache;
@ -97,7 +116,7 @@ GSequenceIter * gst_timed_value_control_source_find_control_point_iter (
gboolean gst_timed_value_control_source_set (GstTimedValueControlSource * self,
GstClockTime timestamp,
const GValue * value);
const gdouble value);
gboolean gst_timed_value_control_source_set_from_list (GstTimedValueControlSource * self,
const GSList * timedvalues);
gboolean gst_timed_value_control_source_unset (GstTimedValueControlSource * self,
@ -105,8 +124,6 @@ gboolean gst_timed_value_control_source_unset (GstTimedValueCont
void gst_timed_value_control_source_unset_all (GstTimedValueControlSource *self);
GList * gst_timed_value_control_source_get_all (GstTimedValueControlSource * self);
gint gst_timed_value_control_source_get_count (GstTimedValueControlSource * self);
GType gst_timed_value_control_source_get_base_value_type (
GstTimedValueControlSource * self);
void gst_timed_value_control_invalidate_cache (GstTimedValueControlSource * self);
G_END_DECLS

View file

@ -40,12 +40,128 @@
#include <gst/gst.h>
#include "gsttriggercontrolsource.h"
#include "gstinterpolationcontrolsourceprivate.h"
#include "gst/glib-compat-private.h"
#include <math.h>
#define GST_CAT_DEFAULT controller_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _GstTriggerControlSourcePrivate
{
gint64 tolerance;
};
/* control point accessors */
/* returns the default value of the property, except for times with specific values */
/* needed for one-shot events, such as notes and triggers */
static inline const gdouble
_interpolate_trigger (GstTimedValueControlSource * self, GSequenceIter * iter,
GstClockTime timestamp)
{
GstControlPoint *cp;
gint64 tolerance = ((GstTriggerControlSource *) self)->priv->tolerance;
gboolean found = FALSE;
cp = g_sequence_get (iter);
if (GST_CLOCK_DIFF (cp->timestamp, timestamp) <= tolerance) {
found = TRUE;
} else {
if ((iter = g_sequence_iter_next (iter))) {
cp = g_sequence_get (iter);
if (GST_CLOCK_DIFF (timestamp, cp->timestamp) <= tolerance) {
found = TRUE;
}
}
}
if (found) {
return cp->value;
}
}
static gboolean
interpolate_trigger_get (GstTimedValueControlSource * self,
GstClockTime timestamp, gdouble * value)
{
gboolean ret = FALSE;
GSequenceIter *iter;
g_mutex_lock (self->lock);
iter =
gst_timed_value_control_source_find_control_point_iter (self, timestamp);
if (iter) {
*value = _interpolate_trigger (self, iter, timestamp);
ret = TRUE;
}
g_mutex_unlock (self->lock);
return ret;
}
static gboolean
interpolate_trigger_get_value_array (GstTimedValueControlSource * self,
GstClockTime timestamp, GstClockTime interval, guint n_values,
gdouble * values)
{
gboolean ret = FALSE;
guint i;
GstClockTime ts = timestamp;
GstClockTime next_ts = 0;
gdouble val;
GSequenceIter *iter1 = NULL, *iter2 = NULL;
gboolean triggered = FALSE;
g_mutex_lock (self->lock);
for (i = 0; i < n_values; i++) {
val = FP_NAN;
if (ts >= next_ts) {
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
if (!iter1) {
if (G_LIKELY (self->values))
iter2 = g_sequence_get_begin_iter (self->values);
else
iter2 = NULL;
} else {
iter2 = g_sequence_iter_next (iter1);
}
if (iter2 && !g_sequence_iter_is_end (iter2)) {
GstControlPoint *cp;
cp = g_sequence_get (iter2);
next_ts = cp->timestamp;
} else {
next_ts = GST_CLOCK_TIME_NONE;
}
if (iter1) {
val = _interpolate_trigger (self, iter1, ts);
ret = TRUE;
} else {
g_mutex_unlock (self->lock);
return FALSE;
}
triggered = TRUE;
} else if (triggered) {
if (iter1) {
val = _interpolate_trigger (self, iter1, ts);
ret = TRUE;
} else {
g_mutex_unlock (self->lock);
return FALSE;
}
triggered = FALSE;
}
*values = val;
ts += interval;
values++;
}
g_mutex_unlock (self->lock);
return ret;
}
enum
{
PROP_TOLERANCE = 1,
@ -58,181 +174,6 @@ enum
G_DEFINE_TYPE_WITH_CODE (GstTriggerControlSource, gst_trigger_control_source,
GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, _do_init);
struct _GstTriggerControlSourcePrivate
{
gint64 tolerance;
};
/* control point accessors */
/* returns the default value of the property, except for times with specific values */
/* needed for one-shot events, such as notes and triggers */
static inline const GValue *
_interpolate_trigger_get (GstTimedValueControlSource * self,
GSequenceIter * iter, GstClockTime timestamp)
{
GstControlPoint *cp;
/* check if there is a value at the registered timestamp */
if (iter) {
gint64 tolerance = ((GstTriggerControlSource *) self)->priv->tolerance;
cp = g_sequence_get (iter);
if (GST_CLOCK_DIFF (cp->timestamp, timestamp) <= tolerance) {
return &cp->value;
} else {
if ((iter = g_sequence_iter_next (iter))) {
cp = g_sequence_get (iter);
if (GST_CLOCK_DIFF (timestamp, cp->timestamp) <= tolerance) {
return &cp->value;
}
}
}
}
if (self->nvalues > 0)
return &self->default_value;
else
return NULL;
}
#define DEFINE_TRIGGER_GET_FUNC_COMPARABLE(type) \
static inline const GValue * \
_interpolate_trigger_get_##type (GstTimedValueControlSource *self, GSequenceIter *iter, GstClockTime timestamp) \
{ \
GstControlPoint *cp; \
\
/* check if there is a value at the registered timestamp */ \
if (iter) { \
gint64 tolerance = ((GstTriggerControlSource *)self)->priv->tolerance; \
gboolean found = FALSE; \
cp = g_sequence_get (iter); \
if (GST_CLOCK_DIFF (cp->timestamp,timestamp) <= tolerance ) { \
found = TRUE; \
} else { \
if ((iter = g_sequence_iter_next (iter))) { \
cp = g_sequence_get (iter); \
if (GST_CLOCK_DIFF (timestamp, cp->timestamp) <= tolerance) { \
found = TRUE; \
} \
} \
} \
if (found) { \
g##type ret = g_value_get_##type (&cp->value); \
if (g_value_get_##type (&self->minimum_value) > ret) \
return &self->minimum_value; \
else if (g_value_get_##type (&self->maximum_value) < ret) \
return &self->maximum_value; \
else \
return &cp->value; \
} \
} \
\
if (self->nvalues > 0) \
return &self->default_value; \
else \
return NULL; \
}
#define DEFINE_TRIGGER_GET(type, ctype, get_func) \
static gboolean \
interpolate_trigger_get_##type (GstTimedValueControlSource *self, GstClockTime timestamp, GValue *value) \
{ \
const GValue *ret; \
GSequenceIter *iter; \
\
g_mutex_lock (self->lock); \
\
iter = gst_timed_value_control_source_find_control_point_iter (self, timestamp); \
ret = get_func (self, iter, timestamp); \
if (!ret) { \
g_mutex_unlock (self->lock); \
return FALSE; \
} \
\
g_value_copy (ret, value); \
g_mutex_unlock (self->lock); \
return TRUE; \
} \
\
static gboolean \
interpolate_trigger_get_##type##_value_array (GstTimedValueControlSource *self, \
GstClockTime timestamp, GstClockTime interval, guint n_values, gpointer _values) \
{ \
guint i; \
GstClockTime ts = timestamp; \
GstClockTime next_ts = 0; \
ctype *values = (ctype *) _values; \
const GValue *ret_val = NULL; \
ctype ret = 0; \
GSequenceIter *iter1 = NULL, *iter2 = NULL; \
gboolean triggered = FALSE; \
\
g_mutex_lock (self->lock); \
for(i = 0; i < n_values; i++) { \
if (!ret_val || ts >= next_ts) { \
iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts); \
if (!iter1) { \
if (G_LIKELY (self->values)) \
iter2 = g_sequence_get_begin_iter (self->values); \
else \
iter2 = NULL; \
} else { \
iter2 = g_sequence_iter_next (iter1); \
} \
\
if (iter2 && !g_sequence_iter_is_end (iter2)) { \
GstControlPoint *cp; \
\
cp = g_sequence_get (iter2); \
next_ts = cp->timestamp; \
} else { \
next_ts = GST_CLOCK_TIME_NONE; \
} \
\
ret_val = get_func (self, iter1, ts); \
if (!ret_val) { \
g_mutex_unlock (self->lock); \
return FALSE; \
} \
ret = g_value_get_##type (ret_val); \
triggered = TRUE; \
} else if (triggered) { \
ret_val = get_func (self, iter1, ts); \
if (!ret_val) { \
g_mutex_unlock (self->lock); \
return FALSE; \
} \
ret = g_value_get_##type (ret_val); \
triggered = FALSE; \
} \
*values = ret; \
ts += interval; \
values++; \
} \
g_mutex_unlock (self->lock); \
return TRUE; \
}
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (int);
DEFINE_TRIGGER_GET (int, gint, _interpolate_trigger_get_int);
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (uint);
DEFINE_TRIGGER_GET (uint, guint, _interpolate_trigger_get_uint);
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (long);
DEFINE_TRIGGER_GET (long, glong, _interpolate_trigger_get_long);
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (ulong);
DEFINE_TRIGGER_GET (ulong, gulong, _interpolate_trigger_get_ulong);
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (int64);
DEFINE_TRIGGER_GET (int64, gint64, _interpolate_trigger_get_int64);
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (uint64);
DEFINE_TRIGGER_GET (uint64, guint64, _interpolate_trigger_get_uint64);
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (float);
DEFINE_TRIGGER_GET (float, gfloat, _interpolate_trigger_get_float);
DEFINE_TRIGGER_GET_FUNC_COMPARABLE (double);
DEFINE_TRIGGER_GET (double, gdouble, _interpolate_trigger_get_double);
DEFINE_TRIGGER_GET (boolean, gboolean, _interpolate_trigger_get);
DEFINE_TRIGGER_GET (enum, gint, _interpolate_trigger_get);
DEFINE_TRIGGER_GET (string, const gchar *, _interpolate_trigger_get);
/**
* gst_trigger_control_source_new:
*
@ -246,107 +187,18 @@ gst_trigger_control_source_new (void)
return g_object_newv (GST_TYPE_TRIGGER_CONTROL_SOURCE, 0, NULL);
}
static gboolean
gst_trigger_control_source_bind (GstControlSource * csource, GParamSpec * pspec)
{
if (GST_CONTROL_SOURCE_CLASS
(gst_trigger_control_source_parent_class)->bind (csource, pspec)) {
gboolean ret = TRUE;
GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (csource);
switch (gst_timed_value_control_source_get_base_value_type (
(GstTimedValueControlSource *) csource)) {
case G_TYPE_INT:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_int;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_int_value_array;
break;
case G_TYPE_UINT:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_uint;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_uint_value_array;
break;
case G_TYPE_LONG:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_long;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_long_value_array;
break;
case G_TYPE_ULONG:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_ulong;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_ulong_value_array;
break;
case G_TYPE_INT64:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_int64;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_int64_value_array;
break;
case G_TYPE_UINT64:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_uint64;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_uint64_value_array;
break;
case G_TYPE_FLOAT:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_float;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_float_value_array;
break;
case G_TYPE_DOUBLE:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_double;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_double_value_array;
break;
case G_TYPE_BOOLEAN:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_boolean;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_boolean_value_array;
break;
case G_TYPE_ENUM:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_enum;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_enum_value_array;
break;
case G_TYPE_STRING:
csource->get_value =
(GstControlSourceGetValue) interpolate_trigger_get_string;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_string_value_array;
break;
default:
ret = FALSE;
break;
}
/* Incomplete implementation */
if (!csource->get_value || !csource->get_value_array) {
ret = FALSE;
}
gst_timed_value_control_invalidate_cache ((GstTimedValueControlSource *)
csource);
GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (csource);
return ret;
}
return FALSE;
}
static void
gst_trigger_control_source_init (GstTriggerControlSource * self)
{
GstControlSource *csource = (GstControlSource *) self;
self->priv =
G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_TRIGGER_CONTROL_SOURCE,
GstTriggerControlSourcePrivate);
csource->get_value = (GstControlSourceGetValue) interpolate_trigger_get;
csource->get_value_array = (GstControlSourceGetValueArray)
interpolate_trigger_get_value_array;
}
static void
@ -387,15 +239,12 @@ static void
gst_trigger_control_source_class_init (GstTriggerControlSourceClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
g_type_class_add_private (klass, sizeof (GstTriggerControlSourcePrivate));
gobject_class->set_property = gst_trigger_control_source_set_property;
gobject_class->get_property = gst_trigger_control_source_get_property;
csource_class->bind = gst_trigger_control_source_bind;
g_object_class_install_property (gobject_class, PROP_TOLERANCE,
g_param_spec_int64 ("tolerance", "Tolerance",
"Amount of ns a control time can be off to still trigger",

View file

@ -115,7 +115,6 @@ main (gint argc, gchar * argv[])
gst_object_set_control_source (GST_OBJECT (src), "freq",
GST_CONTROL_SOURCE (csource));
g_object_set (csource, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
g_value_init (&freq, G_TYPE_DOUBLE);
/* set control values, we set them in a linear order as we would when loading
@ -124,9 +123,8 @@ main (gint argc, gchar * argv[])
bt = gst_util_get_timestamp ();
for (i = 0; i < NUM_CP; i++) {
g_value_set_double (&freq, g_random_double_range (50.0, 3000.0));
gst_timed_value_control_source_set ((GstTimedValueControlSource *) csource,
i * tick, &freq);
i * tick, g_random_double_range (50.0, 3000.0));
}
ct = gst_util_get_timestamp ();
@ -142,9 +140,8 @@ main (gint argc, gchar * argv[])
for (i = 0; i < 100; i++) {
j = g_random_int_range (0, NUM_CP - 1);
g_value_set_double (&freq, g_random_double_range (50.0, 3000.0));
gst_timed_value_control_source_set ((GstTimedValueControlSource *) csource,
j * tick, &freq);
j * tick, g_random_double_range (50.0, 3000.0));
}
ct = gst_util_get_timestamp ();
@ -159,7 +156,7 @@ main (gint argc, gchar * argv[])
bt = gst_util_get_timestamp ();
gst_control_source_get_value_array (GST_CONTROL_SOURCE (csource), 0,
sample_duration, BLOCK_SIZE * NUM_CP, (gpointer) values);
sample_duration, BLOCK_SIZE * NUM_CP, values);
ct = gst_util_get_timestamp ();
g_free (values);
elapsed = GST_CLOCK_DIFF (bt, ct);

File diff suppressed because it is too large Load diff

View file

@ -36,9 +36,8 @@
#include <gst/base/gsttypefindhelper.h>
#include <gst/controller/gstinterpolationcontrolsource.h>
#include <gst/controller/gstinterpolationcontrolsourceprivate.h>
#include <gst/controller/gstlfocontrolsource.h>
#include <gst/controller/gstlfocontrolsourceprivate.h>
#include <gst/controller/gsttriggercontrolsource.h>
#include <gst/dataprotocol/dataprotocol.h>

View file

@ -1,5 +1,6 @@
audio-example
control-sources
*.bb
*.bbg
*.da
audio_example-audio-example.gcno
*.gcno

View file

@ -1,7 +1,8 @@
noinst_PROGRAMS = audio-example
noinst_PROGRAMS = audio-example control-sources
audio_example_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
audio_example_LDADD = \
AM_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
LDADD = \
$(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la \
$(GST_OBJ_LIBS)

View file

@ -17,10 +17,10 @@ main (gint argc, gchar ** argv)
GstElement *src, *sink;
GstElement *bin;
GstInterpolationControlSource *csource1, *csource2;
GstTimedValueControlSource *cs;
GstClock *clock;
GstClockID clock_id;
GstClockReturn wait_ret;
GValue vol = { 0, };
gst_init (&argc, &argv);
@ -62,25 +62,16 @@ main (gint argc, gchar ** argv)
g_object_set (csource2, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
/* set control values */
g_value_init (&vol, G_TYPE_DOUBLE);
g_value_set_double (&vol, 0.0);
gst_timed_value_control_source_set ((GstTimedValueControlSource *) csource1,
0 * GST_SECOND, &vol);
g_value_set_double (&vol, 1.0);
gst_timed_value_control_source_set ((GstTimedValueControlSource *) csource1,
5 * GST_SECOND, &vol);
cs = (GstTimedValueControlSource *) csource1;
gst_timed_value_control_source_set (cs, 0 * GST_SECOND, 0.0);
gst_timed_value_control_source_set (cs, 5 * GST_SECOND, 1.0);
g_object_unref (csource1);
g_value_set_double (&vol, 220.0);
gst_timed_value_control_source_set ((GstTimedValueControlSource *) csource2,
0 * GST_SECOND, &vol);
g_value_set_double (&vol, 3520.0);
gst_timed_value_control_source_set ((GstTimedValueControlSource *) csource2,
3 * GST_SECOND, &vol);
g_value_set_double (&vol, 440.0);
gst_timed_value_control_source_set ((GstTimedValueControlSource *) csource2,
6 * GST_SECOND, &vol);
cs = (GstTimedValueControlSource *) csource2;
gst_timed_value_control_source_set (cs, 0 * GST_SECOND, 220.0);
gst_timed_value_control_source_set (cs, 3 * GST_SECOND, 3520.0);
gst_timed_value_control_source_set (cs, 6 * GST_SECOND, 440.0);
g_object_unref (csource2);

View file

@ -0,0 +1,286 @@
/*
* control-sources.c
*
* Generates a datafile for various control sources.
*
* Needs gnuplot for plotting.
* plot "ctrl_interpolation.dat" using 1:2 with points title 'none', "" using 1:3 with points title 'linear', "" using 1:4 with points title 'cubic'
* plot "ctrl_lfo.dat" using 1:2 with points title 'sine', "" using 1:3 with points title 'saw', "" using 1:4 with points title 'square', "" using 1:5 with points title 'triangle'
*/
#include <stdio.h>
#include <stdlib.h>
#include <gst/gst.h>
#include <gst/controller/gstinterpolationcontrolsource.h>
#include <gst/controller/gstlfocontrolsource.h>
/* local test element */
enum
{
PROP_INT = 1,
PROP_FLOAT,
PROP_DOUBLE,
PROP_BOOLEAN,
PROP_COUNT
};
#define GST_TYPE_TEST_OBJ (gst_test_obj_get_type ())
#define GST_TEST_OBJ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TEST_OBJ, GstTestObj))
#define GST_TEST_OBJ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TEST_OBJ, GstTestObjClass))
#define GST_IS_TEST_OBJ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TEST_OBJ))
#define GST_IS_TEST_OBJ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TEST_OBJ))
#define GST_TEST_OBJ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TEST_OBJ, GstTestObjClass))
typedef struct _GstTestObj GstTestObj;
typedef struct _GstTestObjClass GstTestObjClass;
struct _GstTestObj
{
GstElement parent;
gint val_int;
gfloat val_float;
gdouble val_double;
gboolean val_boolean;
};
struct _GstTestObjClass
{
GstElementClass parent_class;
};
static GType gst_test_obj_get_type (void);
static void
gst_test_obj_get_property (GObject * object,
guint property_id, GValue * value, GParamSpec * pspec)
{
GstTestObj *self = GST_TEST_OBJ (object);
switch (property_id) {
case PROP_INT:
g_value_set_int (value, self->val_int);
break;
case PROP_FLOAT:
g_value_set_float (value, self->val_float);
break;
case PROP_DOUBLE:
g_value_set_double (value, self->val_double);
break;
case PROP_BOOLEAN:
g_value_set_boolean (value, self->val_boolean);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gst_test_obj_set_property (GObject * object,
guint property_id, const GValue * value, GParamSpec * pspec)
{
GstTestObj *self = GST_TEST_OBJ (object);
switch (property_id) {
case PROP_INT:
self->val_int = g_value_get_int (value);
GST_DEBUG ("test value int=%d", self->val_int);
break;
case PROP_FLOAT:
self->val_float = g_value_get_float (value);
GST_DEBUG ("test value float=%f", self->val_float);
break;
case PROP_DOUBLE:
self->val_double = g_value_get_double (value);
GST_DEBUG ("test value double=%f", self->val_double);
break;
case PROP_BOOLEAN:
self->val_boolean = g_value_get_boolean (value);
GST_DEBUG ("test value boolean=%d", self->val_boolean);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gst_test_obj_class_init (GstTestObjClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gst_test_obj_set_property;
gobject_class->get_property = gst_test_obj_get_property;
g_object_class_install_property (gobject_class, PROP_INT,
g_param_spec_int ("int",
"int prop",
"int number parameter for the TEST_OBJ",
0, 100, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, PROP_FLOAT,
g_param_spec_float ("float",
"float prop",
"float number parameter for the TEST_OBJ",
0.0, 100.0, 0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, PROP_DOUBLE,
g_param_spec_double ("double",
"double prop",
"double number parameter for the TEST_OBJ",
0.0, 100.0, 0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, PROP_BOOLEAN,
g_param_spec_boolean ("boolean",
"boolean prop",
"boolean parameter for the TEST_OBJ",
FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
}
static void
gst_test_obj_base_init (GstTestObjClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_set_details_simple (element_class,
"test object for unit tests",
"Test", "Use in unit tests", "Stefan Sauer <ensonic@users.sf.net>");
}
static GType
gst_test_obj_get_type (void)
{
static volatile gsize TEST_OBJ_type = 0;
if (g_once_init_enter (&TEST_OBJ_type)) {
GType type;
static const GTypeInfo info = {
(guint16) sizeof (GstTestObjClass),
(GBaseInitFunc) gst_test_obj_base_init, // base_init
NULL, // base_finalize
(GClassInitFunc) gst_test_obj_class_init, // class_init
NULL, // class_finalize
NULL, // class_data
(guint16) sizeof (GstTestObj),
0, // n_preallocs
NULL, // instance_init
NULL // value_table
};
type = g_type_register_static (GST_TYPE_ELEMENT, "GstTestObj", &info, 0);
g_once_init_leave (&TEST_OBJ_type, type);
}
return TEST_OBJ_type;
}
static void
test_interpolation (void)
{
GstObject *e;
GstInterpolationControlSource *ics;
GstTimedValueControlSource *tvcs;
GstControlSource *cs;
gint t, i1, i2, i3;
FILE *f;
e = (GstObject *) gst_element_factory_make ("testobj", NULL);
ics = gst_interpolation_control_source_new ();
tvcs = (GstTimedValueControlSource *) ics;
cs = (GstControlSource *) ics;
gst_object_set_control_source (e, "int", cs);
gst_timed_value_control_source_set (tvcs, 0 * GST_SECOND, 0.0);
gst_timed_value_control_source_set (tvcs, 10 * GST_SECOND, 1.0);
gst_timed_value_control_source_set (tvcs, 20 * GST_SECOND, 0.5);
gst_timed_value_control_source_set (tvcs, 30 * GST_SECOND, 0.2);
if (!(f = fopen ("ctrl_interpolation.dat", "w")))
exit (-1);
fprintf (f, "# Time None Linear Cubic\n");
for (t = 0; t < 40; t++) {
g_object_set (ics, "mode", GST_INTERPOLATION_MODE_NONE, NULL);
gst_object_sync_values (e, t * GST_SECOND);
i1 = GST_TEST_OBJ (e)->val_int;
g_object_set (ics, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
gst_object_sync_values (e, t * GST_SECOND);
i2 = GST_TEST_OBJ (e)->val_int;
g_object_set (ics, "mode", GST_INTERPOLATION_MODE_CUBIC, NULL);
gst_object_sync_values (e, t * GST_SECOND);
i3 = GST_TEST_OBJ (e)->val_int;
fprintf (f, "%d %d %d %d\n", t, i1, i2, i3);
}
fclose (f);
gst_object_unref (e);
}
static void
test_lfo (void)
{
GstObject *e;
GstLFOControlSource *lfocs;
GstControlSource *cs;
gint t, i1, i2, i3, i4;
FILE *f;
e = (GstObject *) gst_element_factory_make ("testobj", NULL);
lfocs = gst_lfo_control_source_new ();
cs = (GstControlSource *) lfocs;
gst_object_set_control_source (e, "int", cs);
g_object_set (lfocs,
"frequency", (gdouble) 0.05,
"timeshift", (GstClockTime) 0,
"amplitude", (gdouble) 0.5, "offset", (gdouble) 0.5, NULL);
if (!(f = fopen ("ctrl_lfo.dat", "w")))
exit (-1);
fprintf (f, "# Time Sine Saw Square Triangle\n");
for (t = 0; t < 40; t++) {
g_object_set (lfocs, "waveform", GST_LFO_WAVEFORM_SINE, NULL);
gst_object_sync_values (e, t * GST_SECOND);
i1 = GST_TEST_OBJ (e)->val_int;
g_object_set (lfocs, "waveform", GST_LFO_WAVEFORM_SAW, NULL);
gst_object_sync_values (e, t * GST_SECOND);
i2 = GST_TEST_OBJ (e)->val_int;
g_object_set (lfocs, "waveform", GST_LFO_WAVEFORM_SQUARE, NULL);
gst_object_sync_values (e, t * GST_SECOND);
i3 = GST_TEST_OBJ (e)->val_int;
g_object_set (lfocs, "waveform", GST_LFO_WAVEFORM_TRIANGLE, NULL);
gst_object_sync_values (e, t * GST_SECOND);
i4 = GST_TEST_OBJ (e)->val_int;
fprintf (f, "%d %d %d %d %d\n", t, i1, i2, i3, i4);
}
fclose (f);
gst_object_unref (e);
}
gint
main (gint argc, gchar ** argv)
{
gst_init (&argc, &argv);
gst_element_register (NULL, "testobj", GST_RANK_NONE, GST_TYPE_TEST_OBJ);
test_interpolation ();
test_lfo ();
return 0;
}