39bb9e33c3f439c27b05ec6119186f239aa12f5d
[platform/upstream/gstreamer.git] / libs / gst / controller / gstinterpolationcontrolsource.c
1 /* GStreamer
2  *
3  * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *
5  * gstinterpolationcontrolsource.c: Control source that provides several
6  *                                  interpolation methods
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:gstinterpolationcontrolsource
26  * @short_description: interpolation control source
27  *
28  * #GstInterpolationControlSource is a #GstControlSource, that interpolates values between user-given
29  * control points. It supports several interpolation modes and property types.
30  *
31  * To use #GstInterpolationControlSource get a new instance by calling
32  * gst_interpolation_control_source_new(), bind it to a #GParamSpec and set some
33  * control points by calling gst_timed_value_control_source_set().
34  *
35  * All functions are MT-safe.
36  *
37  */
38
39 #include <glib-object.h>
40 #include <gst/gst.h>
41
42 #include "gstinterpolationcontrolsource.h"
43 #include "gst/glib-compat-private.h"
44 #include "gst/math-compat.h"
45
46 #define GST_CAT_DEFAULT controller_debug
47 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
48
49 /* helper functions */
50
51 static inline gboolean
52 _get_nearest_control_points (GstTimedValueControlSource * self,
53     GstClockTime ts, GstControlPoint ** cp1, GstControlPoint ** cp2)
54 {
55   GSequenceIter *iter;
56
57   iter = gst_timed_value_control_source_find_control_point_iter (self, ts);
58   if (iter) {
59     *cp1 = g_sequence_get (iter);
60     iter = g_sequence_iter_next (iter);
61     if (iter && !g_sequence_iter_is_end (iter)) {
62       *cp2 = g_sequence_get (iter);
63     } else {
64       *cp2 = NULL;
65     }
66     return TRUE;
67   }
68   return FALSE;
69 }
70
71 static inline void
72 _get_nearest_control_points2 (GstTimedValueControlSource * self,
73     GstClockTime ts, GstControlPoint ** cp1, GstControlPoint ** cp2,
74     GstClockTime * next_ts)
75 {
76   GSequenceIter *iter1, *iter2 = NULL;
77
78   *cp1 = *cp2 = NULL;
79   iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
80   if (iter1) {
81     *cp1 = g_sequence_get (iter1);
82     iter2 = g_sequence_iter_next (iter1);
83   } else {
84     if (G_LIKELY (self->values)) {
85       /* all values in the control point list come after the given timestamp */
86       iter2 = g_sequence_get_begin_iter (self->values);
87       /* why this? if !cp1 we don't interpolate anyway
88        * if we can eliminate this, we can also use _get_nearest_control_points()
89        * here, is this just to set next_ts? */
90     } else {
91       /* no values */
92       iter2 = NULL;
93     }
94   }
95
96   if (iter2 && !g_sequence_iter_is_end (iter2)) {
97     *cp2 = g_sequence_get (iter2);
98     *next_ts = (*cp2)->timestamp;
99   } else {
100     *next_ts = GST_CLOCK_TIME_NONE;
101   }
102 }
103
104
105 /*  steps-like (no-)interpolation, default */
106 /*  just returns the value for the most recent key-frame */
107 static inline gdouble
108 _interpolate_none (GstTimedValueControlSource * self, GstControlPoint * cp)
109 {
110   return cp->value;
111 }
112
113 static gboolean
114 interpolate_none_get (GstTimedValueControlSource * self, GstClockTime timestamp,
115     gdouble * value)
116 {
117   gboolean ret = FALSE;
118   GSequenceIter *iter;
119   GstControlPoint *cp;
120
121   g_mutex_lock (&self->lock);
122
123   iter =
124       gst_timed_value_control_source_find_control_point_iter (self, timestamp);
125   if (iter) {
126     cp = g_sequence_get (iter);
127     *value = _interpolate_none (self, cp);
128     ret = TRUE;
129   }
130   g_mutex_unlock (&self->lock);
131   return ret;
132 }
133
134 static gboolean
135 interpolate_none_get_value_array (GstTimedValueControlSource * self,
136     GstClockTime timestamp, GstClockTime interval, guint n_values,
137     gdouble * values)
138 {
139   gboolean ret = FALSE;
140   guint i;
141   GstClockTime ts = timestamp;
142   GstClockTime next_ts = 0;
143   GstControlPoint *cp1 = NULL, *cp2 = NULL;
144
145   g_mutex_lock (&self->lock);
146
147   for (i = 0; i < n_values; i++) {
148     GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT,
149         i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts));
150     if (ts >= next_ts) {
151       _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts);
152     }
153     if (cp1) {
154       *values = _interpolate_none (self, cp1);
155       ret = TRUE;
156       GST_LOG ("values[%3d]=%lf", i, *values);
157     } else {
158       *values = NAN;
159       GST_LOG ("values[%3d]=-", i);
160     }
161     ts += interval;
162     values++;
163   }
164   g_mutex_unlock (&self->lock);
165   return ret;
166 }
167
168
169
170 /*  linear interpolation */
171 /*  smoothes inbetween values */
172 static inline gdouble
173 _interpolate_linear (GstClockTime timestamp1, gdouble value1,
174     GstClockTime timestamp2, gdouble value2, GstClockTime timestamp)
175 {
176   if (GST_CLOCK_TIME_IS_VALID (timestamp2)) {
177     gdouble slope;
178
179     slope =
180         (value2 - value1) / gst_guint64_to_gdouble (timestamp2 - timestamp1);
181     return value1 + (gst_guint64_to_gdouble (timestamp - timestamp1) * slope);
182   } else {
183     return value1;
184   }
185 }
186
187 static gboolean
188 interpolate_linear_get (GstTimedValueControlSource * self,
189     GstClockTime timestamp, gdouble * value)
190 {
191   gboolean ret = FALSE;
192   GstControlPoint *cp1, *cp2;
193
194   g_mutex_lock (&self->lock);
195
196   if (_get_nearest_control_points (self, timestamp, &cp1, &cp2)) {
197     *value = _interpolate_linear (cp1->timestamp, cp1->value,
198         (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE),
199         (cp2 ? cp2->value : 0.0), timestamp);
200     ret = TRUE;
201   }
202   g_mutex_unlock (&self->lock);
203   return ret;
204 }
205
206 static gboolean
207 interpolate_linear_get_value_array (GstTimedValueControlSource * self,
208     GstClockTime timestamp, GstClockTime interval, guint n_values,
209     gdouble * values)
210 {
211   gboolean ret = FALSE;
212   guint i;
213   GstClockTime ts = timestamp;
214   GstClockTime next_ts = 0;
215   GstControlPoint *cp1 = NULL, *cp2 = NULL;
216
217   g_mutex_lock (&self->lock);
218
219   for (i = 0; i < n_values; i++) {
220     GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT,
221         i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts));
222     if (ts >= next_ts) {
223       _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts);
224     }
225     if (cp1) {
226       *values = _interpolate_linear (cp1->timestamp, cp1->value,
227           (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE),
228           (cp2 ? cp2->value : 0.0), ts);
229       ret = TRUE;
230       GST_LOG ("values[%3d]=%lf", i, *values);
231     } else {
232       *values = NAN;
233       GST_LOG ("values[%3d]=-", i);
234     }
235     ts += interval;
236     values++;
237   }
238   g_mutex_unlock (&self->lock);
239   return ret;
240 }
241
242
243
244 /*  cubic interpolation */
245
246 /* The following functions implement a natural cubic spline interpolator.
247  * For details look at http://en.wikipedia.org/wiki/Spline_interpolation
248  *
249  * Instead of using a real matrix with n^2 elements for the linear system
250  * of equations we use three arrays o, p, q to hold the tridiagonal matrix
251  * as following to save memory:
252  *
253  * p[0] q[0]    0    0    0
254  * o[1] p[1] q[1]    0    0
255  *    0 o[2] p[2] q[2]    .
256  *    .    .    .    .    .
257  */
258
259 static void
260 _interpolate_cubic_update_cache (GstTimedValueControlSource * self)
261 {
262   gint i, n = self->nvalues;
263   gdouble *o = g_new0 (gdouble, n);
264   gdouble *p = g_new0 (gdouble, n);
265   gdouble *q = g_new0 (gdouble, n);
266
267   gdouble *h = g_new0 (gdouble, n);
268   gdouble *b = g_new0 (gdouble, n);
269   gdouble *z = g_new0 (gdouble, n);
270
271   GSequenceIter *iter;
272   GstControlPoint *cp;
273   GstClockTime x, x_next;
274   gdouble y_prev, y, y_next;
275
276   /* Fill linear system of equations */
277   iter = g_sequence_get_begin_iter (self->values);
278   cp = g_sequence_get (iter);
279   x = cp->timestamp;
280   y = cp->value;
281
282   p[0] = 1.0;
283
284   iter = g_sequence_iter_next (iter);
285   cp = g_sequence_get (iter);
286   x_next = cp->timestamp;
287   y_next = cp->value;
288   h[0] = gst_guint64_to_gdouble (x_next - x);
289
290   for (i = 1; i < n - 1; i++) {
291     /* Shuffle x and y values */
292     y_prev = y;
293     x = x_next;
294     y = y_next;
295     iter = g_sequence_iter_next (iter);
296     cp = g_sequence_get (iter);
297     x_next = cp->timestamp;
298     y_next = cp->value;
299
300     h[i] = gst_guint64_to_gdouble (x_next - x);
301     o[i] = h[i - 1];
302     p[i] = 2.0 * (h[i - 1] + h[i]);
303     q[i] = h[i];
304     b[i] = (y_next - y) / h[i] - (y - y_prev) / h[i - 1];
305   }
306   p[n - 1] = 1.0;
307
308   /* Use Gauss elimination to set everything below the diagonal to zero */
309   for (i = 1; i < n - 1; i++) {
310     gdouble a = o[i] / p[i - 1];
311     p[i] -= a * q[i - 1];
312     b[i] -= a * b[i - 1];
313   }
314
315   /* Solve everything else from bottom to top */
316   for (i = n - 2; i > 0; i--)
317     z[i] = (b[i] - q[i] * z[i + 1]) / p[i];
318
319   /* Save cache next in the GstControlPoint */
320
321   iter = g_sequence_get_begin_iter (self->values);
322   for (i = 0; i < n; i++) {
323     cp = g_sequence_get (iter);
324     cp->cache.cubic.h = h[i];
325     cp->cache.cubic.z = z[i];
326     iter = g_sequence_iter_next (iter);
327   }
328
329   /* Free our temporary arrays */
330   g_free (o);
331   g_free (p);
332   g_free (q);
333   g_free (h);
334   g_free (b);
335   g_free (z);
336 }
337
338 static inline gdouble
339 _interpolate_cubic (GstTimedValueControlSource * self, GstControlPoint * cp1,
340     gdouble value1, GstControlPoint * cp2, gdouble value2,
341     GstClockTime timestamp)
342 {
343   if (!self->valid_cache) {
344     _interpolate_cubic_update_cache (self);
345     self->valid_cache = TRUE;
346   }
347
348   if (cp2) {
349     gdouble diff1, diff2;
350     gdouble out;
351
352     diff1 = gst_guint64_to_gdouble (timestamp - cp1->timestamp);
353     diff2 = gst_guint64_to_gdouble (cp2->timestamp - timestamp);
354
355     out =
356         (cp2->cache.cubic.z * diff1 * diff1 * diff1 +
357         cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h;
358     out +=
359         (value2 / cp1->cache.cubic.h -
360         cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1;
361     out +=
362         (value1 / cp1->cache.cubic.h -
363         cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2;
364     return out;
365   } else {
366     return value1;
367   }
368 }
369
370 static gboolean
371 interpolate_cubic_get (GstTimedValueControlSource * self,
372     GstClockTime timestamp, gdouble * value)
373 {
374   gboolean ret = FALSE;
375   GstControlPoint *cp1, *cp2 = NULL;
376
377   if (self->nvalues <= 2)
378     return interpolate_linear_get (self, timestamp, value);
379
380   g_mutex_lock (&self->lock);
381
382   if (_get_nearest_control_points (self, timestamp, &cp1, &cp2)) {
383     *value = _interpolate_cubic (self, cp1, cp1->value, cp2,
384         (cp2 ? cp2->value : 0.0), timestamp);
385     ret = TRUE;
386   }
387   g_mutex_unlock (&self->lock);
388   return ret;
389 }
390
391 static gboolean
392 interpolate_cubic_get_value_array (GstTimedValueControlSource * self,
393     GstClockTime timestamp, GstClockTime interval, guint n_values,
394     gdouble * values)
395 {
396   gboolean ret = FALSE;
397   guint i;
398   GstClockTime ts = timestamp;
399   GstClockTime next_ts = 0;
400   GstControlPoint *cp1 = NULL, *cp2 = NULL;
401
402   if (self->nvalues <= 2)
403     return interpolate_linear_get_value_array (self, timestamp, interval,
404         n_values, values);
405
406   g_mutex_lock (&self->lock);
407
408   for (i = 0; i < n_values; i++) {
409     GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT,
410         i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts));
411     if (ts >= next_ts) {
412       _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts);
413     }
414     if (cp1) {
415       *values = _interpolate_cubic (self, cp1, cp1->value, cp2,
416           (cp2 ? cp2->value : 0.0), ts);
417       ret = TRUE;
418       GST_LOG ("values[%3d]=%lf", i, *values);
419     } else {
420       *values = NAN;
421       GST_LOG ("values[%3d]=-", i);
422     }
423     ts += interval;
424     values++;
425   }
426   g_mutex_unlock (&self->lock);
427   return ret;
428 }
429
430
431 /*  monotonic cubic interpolation */
432
433 /* the following functions implement monotonic cubic spline interpolation.
434  * For details: http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
435  *
436  * In contrast to the previous cubic mode, the values won't overshoot.
437  */
438
439 static void
440 _interpolate_cubic_monotonic_update_cache (GstTimedValueControlSource * self)
441 {
442   gint i, n = self->nvalues;
443   gdouble *dxs = g_new0 (gdouble, n);
444   gdouble *dys = g_new0 (gdouble, n);
445   gdouble *ms = g_new0 (gdouble, n);
446   gdouble *c1s = g_new0 (gdouble, n);
447
448   GSequenceIter *iter;
449   GstControlPoint *cp;
450   GstClockTime x, x_next, dx;
451   gdouble y, y_next, dy;
452
453   /* Get consecutive differences and slopes */
454   iter = g_sequence_get_begin_iter (self->values);
455   cp = g_sequence_get (iter);
456   x_next = cp->timestamp;
457   y_next = cp->value;
458   for (i = 0; i < n - 1; i++) {
459     x = x_next;
460     y = y_next;
461     iter = g_sequence_iter_next (iter);
462     cp = g_sequence_get (iter);
463     x_next = cp->timestamp;
464     y_next = cp->value;
465
466     dx = gst_guint64_to_gdouble (x_next - x);
467     dy = y_next - y;
468     dxs[i] = dx;
469     dys[i] = dy;
470     ms[i] = dy / dx;
471   }
472
473   /* Get degree-1 coefficients */
474   c1s[0] = ms[0];
475   for (i = 1; i < n; i++) {
476     gdouble m = ms[i - 1];
477     gdouble m_next = ms[i];
478
479     if (m * m_next <= 0) {
480       c1s[i] = 0.0;
481     } else {
482       gdouble dx_next, dx_sum;
483
484       dx = dxs[i], dx_next = dxs[i + 1], dx_sum = dx + dx_next;
485       c1s[i] = 3.0 * dx_sum / ((dx_sum + dx_next) / m + (dx_sum + dx) / m_next);
486     }
487   }
488   c1s[n - 1] = ms[n - 1];
489
490   /* Get degree-2 and degree-3 coefficients */
491   iter = g_sequence_get_begin_iter (self->values);
492   for (i = 0; i < n - 1; i++) {
493     gdouble c1, m, inv_dx, common;
494     cp = g_sequence_get (iter);
495
496     c1 = c1s[i];
497     m = ms[i];
498     inv_dx = 1.0 / dxs[i];
499     common = c1 + c1s[i + 1] - m - m;
500
501     cp->cache.cubic_monotonic.c1s = c1;
502     cp->cache.cubic_monotonic.c2s = (m - c1 - common) * inv_dx;
503     cp->cache.cubic_monotonic.c3s = common * inv_dx * inv_dx;
504
505     iter = g_sequence_iter_next (iter);
506   }
507
508   /* Free our temporary arrays */
509   g_free (dxs);
510   g_free (dys);
511   g_free (ms);
512   g_free (c1s);
513 }
514
515 static inline gdouble
516 _interpolate_cubic_monotonic (GstTimedValueControlSource * self,
517     GstControlPoint * cp1, gdouble value1, GstControlPoint * cp2,
518     gdouble value2, GstClockTime timestamp)
519 {
520   if (!self->valid_cache) {
521     _interpolate_cubic_monotonic_update_cache (self);
522     self->valid_cache = TRUE;
523   }
524
525   if (cp2) {
526     gdouble diff = gst_guint64_to_gdouble (timestamp - cp1->timestamp);
527     gdouble diff2 = diff * diff;
528     gdouble out;
529
530     out = value1 + cp1->cache.cubic_monotonic.c1s * diff;
531     out += cp1->cache.cubic_monotonic.c2s * diff2;
532     out += cp1->cache.cubic_monotonic.c3s * diff * diff2;
533     return out;
534   } else {
535     return value1;
536   }
537 }
538
539 static gboolean
540 interpolate_cubic_monotonic_get (GstTimedValueControlSource * self,
541     GstClockTime timestamp, gdouble * value)
542 {
543   gboolean ret = FALSE;
544   GstControlPoint *cp1, *cp2 = NULL;
545
546   if (self->nvalues <= 2)
547     return interpolate_linear_get (self, timestamp, value);
548
549   g_mutex_lock (&self->lock);
550
551   if (_get_nearest_control_points (self, timestamp, &cp1, &cp2)) {
552     *value = _interpolate_cubic_monotonic (self, cp1, cp1->value, cp2,
553         (cp2 ? cp2->value : 0.0), timestamp);
554     ret = TRUE;
555   }
556   g_mutex_unlock (&self->lock);
557   return ret;
558 }
559
560 static gboolean
561 interpolate_cubic_monotonic_get_value_array (GstTimedValueControlSource * self,
562     GstClockTime timestamp, GstClockTime interval, guint n_values,
563     gdouble * values)
564 {
565   gboolean ret = FALSE;
566   guint i;
567   GstClockTime ts = timestamp;
568   GstClockTime next_ts = 0;
569   GstControlPoint *cp1 = NULL, *cp2 = NULL;
570
571   if (self->nvalues <= 2)
572     return interpolate_linear_get_value_array (self, timestamp, interval,
573         n_values, values);
574
575   g_mutex_lock (&self->lock);
576
577   for (i = 0; i < n_values; i++) {
578     GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT,
579         i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts));
580     if (ts >= next_ts) {
581       _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts);
582     }
583     if (cp1) {
584       *values = _interpolate_cubic_monotonic (self, cp1, cp1->value, cp2,
585           (cp2 ? cp2->value : 0.0), ts);
586       ret = TRUE;
587       GST_LOG ("values[%3d]=%lf", i, *values);
588     } else {
589       *values = NAN;
590       GST_LOG ("values[%3d]=-", i);
591     }
592     ts += interval;
593     values++;
594   }
595   g_mutex_unlock (&self->lock);
596   return ret;
597 }
598
599
600 static struct
601 {
602   GstControlSourceGetValue get;
603   GstControlSourceGetValueArray get_value_array;
604 } interpolation_modes[] = {
605   {
606   (GstControlSourceGetValue) interpolate_none_get,
607         (GstControlSourceGetValueArray) interpolate_none_get_value_array}, {
608   (GstControlSourceGetValue) interpolate_linear_get,
609         (GstControlSourceGetValueArray) interpolate_linear_get_value_array}, {
610   (GstControlSourceGetValue) interpolate_cubic_get,
611         (GstControlSourceGetValueArray) interpolate_cubic_get_value_array}, {
612     (GstControlSourceGetValue) interpolate_cubic_monotonic_get,
613         (GstControlSourceGetValueArray)
614 interpolate_cubic_monotonic_get_value_array}};
615
616 static const guint num_interpolation_modes = G_N_ELEMENTS (interpolation_modes);
617
618 enum
619 {
620   PROP_MODE = 1
621 };
622
623 GType
624 gst_interpolation_mode_get_type (void)
625 {
626   static gsize gtype = 0;
627   static const GEnumValue values[] = {
628     {GST_INTERPOLATION_MODE_NONE, "GST_INTERPOLATION_MODE_NONE", "none"},
629     {GST_INTERPOLATION_MODE_LINEAR, "GST_INTERPOLATION_MODE_LINEAR", "linear"},
630     {GST_INTERPOLATION_MODE_CUBIC, "GST_INTERPOLATION_MODE_CUBIC", "cubic"},
631     {GST_INTERPOLATION_MODE_CUBIC_MONOTONIC,
632         "GST_INTERPOLATION_MODE_CUBIC_MONOTONIC", "cubic-monotonic"},
633     {0, NULL, NULL}
634   };
635
636   if (g_once_init_enter (&gtype)) {
637     GType tmp = g_enum_register_static ("GstInterpolationMode", values);
638     g_once_init_leave (&gtype, tmp);
639   }
640
641   return (GType) gtype;
642 }
643
644
645 #define _do_init \
646   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "interpolation control source", 0, \
647     "timeline value interpolating control source")
648
649 G_DEFINE_TYPE_WITH_CODE (GstInterpolationControlSource,
650     gst_interpolation_control_source, GST_TYPE_TIMED_VALUE_CONTROL_SOURCE,
651     _do_init);
652
653 struct _GstInterpolationControlSourcePrivate
654 {
655   GstInterpolationMode interpolation_mode;
656 };
657
658 /**
659  * gst_interpolation_control_source_new:
660  *
661  * This returns a new, unbound #GstInterpolationControlSource.
662  *
663  * Returns: (transfer full): a new, unbound #GstInterpolationControlSource.
664  */
665 GstControlSource *
666 gst_interpolation_control_source_new (void)
667 {
668   return g_object_newv (GST_TYPE_INTERPOLATION_CONTROL_SOURCE, 0, NULL);
669 }
670
671 static gboolean
672     gst_interpolation_control_source_set_interpolation_mode
673     (GstInterpolationControlSource * self, GstInterpolationMode mode)
674 {
675   GstControlSource *csource = GST_CONTROL_SOURCE (self);
676
677   if (mode >= num_interpolation_modes || (int) mode < 0) {
678     GST_WARNING ("interpolation mode %d invalid or not implemented yet", mode);
679     return FALSE;
680   }
681
682   GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (self);
683   csource->get_value = interpolation_modes[mode].get;
684   csource->get_value_array = interpolation_modes[mode].get_value_array;
685
686   gst_timed_value_control_invalidate_cache ((GstTimedValueControlSource *)
687       csource);
688   self->priv->interpolation_mode = mode;
689
690   GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (self);
691
692   return TRUE;
693 }
694
695 static void
696 gst_interpolation_control_source_init (GstInterpolationControlSource * self)
697 {
698   self->priv =
699       G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_INTERPOLATION_CONTROL_SOURCE,
700       GstInterpolationControlSourcePrivate);
701   gst_interpolation_control_source_set_interpolation_mode (self,
702       GST_INTERPOLATION_MODE_NONE);
703 }
704
705 static void
706 gst_interpolation_control_source_set_property (GObject * object, guint prop_id,
707     const GValue * value, GParamSpec * pspec)
708 {
709   GstInterpolationControlSource *self =
710       GST_INTERPOLATION_CONTROL_SOURCE (object);
711
712   switch (prop_id) {
713     case PROP_MODE:
714       gst_interpolation_control_source_set_interpolation_mode (self,
715           (GstInterpolationMode) g_value_get_enum (value));
716       break;
717     default:
718       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
719       break;
720   }
721 }
722
723 static void
724 gst_interpolation_control_source_get_property (GObject * object, guint prop_id,
725     GValue * value, GParamSpec * pspec)
726 {
727   GstInterpolationControlSource *self =
728       GST_INTERPOLATION_CONTROL_SOURCE (object);
729
730   switch (prop_id) {
731     case PROP_MODE:
732       g_value_set_enum (value, self->priv->interpolation_mode);
733       break;
734     default:
735       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
736       break;
737   }
738 }
739
740 static void
741 gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass
742     * klass)
743 {
744   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
745   //GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
746
747   g_type_class_add_private (klass,
748       sizeof (GstInterpolationControlSourcePrivate));
749
750   gobject_class->set_property = gst_interpolation_control_source_set_property;
751   gobject_class->get_property = gst_interpolation_control_source_get_property;
752
753   g_object_class_install_property (gobject_class, PROP_MODE,
754       g_param_spec_enum ("mode", "Mode", "Interpolation mode",
755           GST_TYPE_INTERPOLATION_MODE, GST_INTERPOLATION_MODE_NONE,
756           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
757 }