mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
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:
parent
a80401b22c
commit
39d6b7462f
24 changed files with 1908 additions and 2854 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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__ */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
@ -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>
|
||||
|
||||
|
|
3
tests/examples/controller/.gitignore
vendored
3
tests/examples/controller/.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
audio-example
|
||||
control-sources
|
||||
*.bb
|
||||
*.bbg
|
||||
*.da
|
||||
audio_example-audio-example.gcno
|
||||
*.gcno
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
286
tests/examples/controller/control-sources.c
Normal file
286
tests/examples/controller/control-sources.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue