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