gstreamer/libs/gst/controller/gstinterpolation.c
Stefan Sauer 503b739b23 controller: cleanup interpolation modes
Remove deprecated/unimplemented modes. Turn interpolation mode into a gobject
property. Update docs and examples.
2011-12-19 23:36:26 +01:00

592 lines
21 KiB
C

/* 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);