1 /* GStreamer
2 *
3 * Copyright (C) <2005> Stefan Kost <ensonic at users dot sf dot net>
4 * Copyright (C) 2007-2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 *
6 * gstinterpolation.c: Interpolation methods for dynamic properties
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., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include "gstinterpolationcontrolsource.h"
29 #include "gstinterpolationcontrolsourceprivate.h"
31 #define GST_CAT_DEFAULT controller_debug
32 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
34 #define EMPTY(x) (x)
36 /* common helper */
38 static gint
39 gst_control_point_find (gconstpointer p1, gconstpointer p2)
40 {
41 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
42 GstClockTime ct2 = *(GstClockTime *) p2;
44 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
45 }
47 /*
48 * gst_interpolation_control_source_find_control_point_iter:
49 * @self: the interpolation control source to search in
50 * @timestamp: the search key
51 *
52 * Find last value before given timestamp in control point list.
53 * If all values in the control point list come after the given
54 * timestamp or no values exist, %NULL is returned.
55 *
56 * Returns: the found #GSequenceIter or %NULL
57 */
58 static GSequenceIter *gst_interpolation_control_source_find_control_point_iter
59 (GstInterpolationControlSource * self, GstClockTime timestamp)
60 {
61 GSequenceIter *iter;
63 if (!self->priv->values)
64 return NULL;
66 iter =
67 g_sequence_search (self->priv->values, ×tamp,
68 (GCompareDataFunc) gst_control_point_find, NULL);
70 /* g_sequence_search() returns the iter where timestamp
71 * would be inserted, i.e. the iter > timestamp, so
72 * we need to get the previous one. And of course, if
73 * there is no previous one, we return NULL. */
74 if (g_sequence_iter_is_begin (iter))
75 return NULL;
77 return g_sequence_iter_prev (iter);
78 }
80 /* steps-like (no-)interpolation, default */
81 /* just returns the value for the most recent key-frame */
82 static inline const GValue *
83 _interpolate_none_get (GstInterpolationControlSource * self,
84 GSequenceIter * iter)
85 {
86 const GValue *ret;
88 if (iter) {
89 GstControlPoint *cp = g_sequence_get (iter);
91 ret = &cp->value;
92 } else {
93 ret = &self->priv->default_value;
94 }
95 return ret;
96 }
98 #define DEFINE_NONE_GET_FUNC_COMPARABLE(type) \
99 static inline const GValue * \
100 _interpolate_none_get_##type (GstInterpolationControlSource *self, GSequenceIter *iter) \
101 { \
102 const GValue *ret; \
103 \
104 if (iter) { \
105 GstControlPoint *cp = g_sequence_get (iter); \
106 g##type ret_val = g_value_get_##type (&cp->value); \
107 \
108 if (g_value_get_##type (&self->priv->minimum_value) > ret_val) \
109 ret = &self->priv->minimum_value; \
110 else if (g_value_get_##type (&self->priv->maximum_value) < ret_val) \
111 ret = &self->priv->maximum_value; \
112 else \
113 ret = &cp->value; \
114 } else { \
115 ret = &self->priv->default_value; \
116 } \
117 return ret; \
118 }
120 #define DEFINE_NONE_GET(type,ctype,get_func) \
121 static gboolean \
122 interpolate_none_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
123 { \
124 const GValue *ret; \
125 GSequenceIter *iter; \
126 \
127 g_mutex_lock (self->lock); \
128 \
129 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
130 ret = get_func (self, iter); \
131 g_value_copy (ret, value); \
132 g_mutex_unlock (self->lock); \
133 return TRUE; \
134 } \
135 \
136 static gboolean \
137 interpolate_none_get_##type##_value_array (GstInterpolationControlSource *self, \
138 GstClockTime timestamp, GstValueArray * value_array) \
139 { \
140 gint i; \
141 GstClockTime ts = timestamp; \
142 GstClockTime next_ts = 0; \
143 ctype *values = (ctype *) value_array->values; \
144 const GValue *ret_val = NULL; \
145 ctype ret = 0; \
146 GSequenceIter *iter1 = NULL, *iter2 = NULL; \
147 \
148 g_mutex_lock (self->lock); \
149 for(i = 0; i < value_array->nbsamples; i++) { \
150 if (!ret_val || ts >= next_ts) { \
151 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
152 if (!iter1) { \
153 if (G_LIKELY (self->priv->values)) \
154 iter2 = g_sequence_get_begin_iter (self->priv->values); \
155 else \
156 iter2 = NULL; \
157 } else { \
158 iter2 = g_sequence_iter_next (iter1); \
159 } \
160 \
161 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
162 GstControlPoint *cp; \
163 \
164 cp = g_sequence_get (iter2); \
165 next_ts = cp->timestamp; \
166 } else { \
167 next_ts = GST_CLOCK_TIME_NONE; \
168 } \
169 \
170 ret_val = get_func (self, iter1); \
171 ret = g_value_get_##type (ret_val); \
172 } \
173 *values = ret; \
174 ts += value_array->sample_interval; \
175 values++; \
176 } \
177 g_mutex_unlock (self->lock); \
178 return TRUE; \
179 }
181 DEFINE_NONE_GET_FUNC_COMPARABLE (int);
182 DEFINE_NONE_GET (int, gint, _interpolate_none_get_int);
183 DEFINE_NONE_GET_FUNC_COMPARABLE (uint);
184 DEFINE_NONE_GET (uint, guint, _interpolate_none_get_uint);
185 DEFINE_NONE_GET_FUNC_COMPARABLE (long);
186 DEFINE_NONE_GET (long, glong, _interpolate_none_get_long);
187 DEFINE_NONE_GET_FUNC_COMPARABLE (ulong);
188 DEFINE_NONE_GET (ulong, gulong, _interpolate_none_get_ulong);
189 DEFINE_NONE_GET_FUNC_COMPARABLE (int64);
190 DEFINE_NONE_GET (int64, gint64, _interpolate_none_get_int64);
191 DEFINE_NONE_GET_FUNC_COMPARABLE (uint64);
192 DEFINE_NONE_GET (uint64, guint64, _interpolate_none_get_uint64);
193 DEFINE_NONE_GET_FUNC_COMPARABLE (float);
194 DEFINE_NONE_GET (float, gfloat, _interpolate_none_get_float);
195 DEFINE_NONE_GET_FUNC_COMPARABLE (double);
196 DEFINE_NONE_GET (double, gdouble, _interpolate_none_get_double);
198 DEFINE_NONE_GET (boolean, gboolean, _interpolate_none_get);
199 DEFINE_NONE_GET (enum, gint, _interpolate_none_get);
200 DEFINE_NONE_GET (string, const gchar *, _interpolate_none_get);
202 static GstInterpolateMethod interpolate_none = {
203 (GstControlSourceGetValue) interpolate_none_get_int,
204 (GstControlSourceGetValueArray) interpolate_none_get_int_value_array,
205 (GstControlSourceGetValue) interpolate_none_get_uint,
206 (GstControlSourceGetValueArray) interpolate_none_get_uint_value_array,
207 (GstControlSourceGetValue) interpolate_none_get_long,
208 (GstControlSourceGetValueArray) interpolate_none_get_long_value_array,
209 (GstControlSourceGetValue) interpolate_none_get_ulong,
210 (GstControlSourceGetValueArray) interpolate_none_get_ulong_value_array,
211 (GstControlSourceGetValue) interpolate_none_get_int64,
212 (GstControlSourceGetValueArray) interpolate_none_get_int64_value_array,
213 (GstControlSourceGetValue) interpolate_none_get_uint64,
214 (GstControlSourceGetValueArray) interpolate_none_get_uint64_value_array,
215 (GstControlSourceGetValue) interpolate_none_get_float,
216 (GstControlSourceGetValueArray) interpolate_none_get_float_value_array,
217 (GstControlSourceGetValue) interpolate_none_get_double,
218 (GstControlSourceGetValueArray) interpolate_none_get_double_value_array,
219 (GstControlSourceGetValue) interpolate_none_get_boolean,
220 (GstControlSourceGetValueArray) interpolate_none_get_boolean_value_array,
221 (GstControlSourceGetValue) interpolate_none_get_enum,
222 (GstControlSourceGetValueArray) interpolate_none_get_enum_value_array,
223 (GstControlSourceGetValue) interpolate_none_get_string,
224 (GstControlSourceGetValueArray) interpolate_none_get_string_value_array
225 };
227 /* returns the default value of the property, except for times with specific values */
228 /* needed for one-shot events, such as notes and triggers */
229 static inline const GValue *
230 _interpolate_trigger_get (GstInterpolationControlSource * self,
231 GSequenceIter * iter, GstClockTime timestamp)
232 {
233 GstControlPoint *cp;
235 /* check if there is a value at the registered timestamp */
236 if (iter) {
237 cp = g_sequence_get (iter);
238 if (timestamp == cp->timestamp) {
239 return &cp->value;
240 }
241 }
242 if (self->priv->nvalues > 0)
243 return &self->priv->default_value;
244 else
245 return NULL;
246 }
248 #define DEFINE_TRIGGER_GET_FUNC_COMPARABLE(type) \
249 static inline const GValue * \
250 _interpolate_trigger_get_##type (GstInterpolationControlSource *self, GSequenceIter *iter, GstClockTime timestamp) \
251 { \
252 GstControlPoint *cp; \
253 \
254 /* check if there is a value at the registered timestamp */ \
255 if (iter) { \
256 cp = g_sequence_get (iter); \
257 if (timestamp == cp->timestamp) { \
258 g##type ret = g_value_get_##type (&cp->value); \
259 if (g_value_get_##type (&self->priv->minimum_value) > ret) \
260 return &self->priv->minimum_value; \
261 else if (g_value_get_##type (&self->priv->maximum_value) < ret) \
262 return &self->priv->maximum_value; \
263 else \
264 return &cp->value; \
265 } \
266 } \
267 \
268 if (self->priv->nvalues > 0) \
269 return &self->priv->default_value; \
270 else \
271 return NULL; \
272 }
274 #define DEFINE_TRIGGER_GET(type, ctype, get_func) \
275 static gboolean \
276 interpolate_trigger_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
277 { \
278 const GValue *ret; \
279 GSequenceIter *iter; \
280 \
281 g_mutex_lock (self->lock); \
282 \
283 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
284 ret = get_func (self, iter, timestamp); \
285 if (!ret) { \
286 g_mutex_unlock (self->lock); \
287 return FALSE; \
288 } \
289 \
290 g_value_copy (ret, value); \
291 g_mutex_unlock (self->lock); \
292 return TRUE; \
293 } \
294 \
295 static gboolean \
296 interpolate_trigger_get_##type##_value_array (GstInterpolationControlSource *self, \
297 GstClockTime timestamp, GstValueArray * value_array) \
298 { \
299 gint i; \
300 GstClockTime ts = timestamp; \
301 GstClockTime next_ts = 0; \
302 ctype *values = (ctype *) value_array->values; \
303 const GValue *ret_val = NULL; \
304 ctype ret = 0; \
305 GSequenceIter *iter1 = NULL, *iter2 = NULL; \
306 gboolean triggered = FALSE; \
307 \
308 g_mutex_lock (self->lock); \
309 for(i = 0; i < value_array->nbsamples; i++) { \
310 if (!ret_val || ts >= next_ts) { \
311 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
312 if (!iter1) { \
313 if (G_LIKELY (self->priv->values)) \
314 iter2 = g_sequence_get_begin_iter (self->priv->values); \
315 else \
316 iter2 = NULL; \
317 } else { \
318 iter2 = g_sequence_iter_next (iter1); \
319 } \
320 \
321 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
322 GstControlPoint *cp; \
323 \
324 cp = g_sequence_get (iter2); \
325 next_ts = cp->timestamp; \
326 } else { \
327 next_ts = GST_CLOCK_TIME_NONE; \
328 } \
329 \
330 ret_val = get_func (self, iter1, ts); \
331 if (!ret_val) { \
332 g_mutex_unlock (self->lock); \
333 return FALSE; \
334 } \
335 ret = g_value_get_##type (ret_val); \
336 triggered = TRUE; \
337 } else if (triggered) { \
338 ret_val = get_func (self, iter1, ts); \
339 if (!ret_val) { \
340 g_mutex_unlock (self->lock); \
341 return FALSE; \
342 } \
343 ret = g_value_get_##type (ret_val); \
344 triggered = FALSE; \
345 } \
346 *values = ret; \
347 ts += value_array->sample_interval; \
348 values++; \
349 } \
350 g_mutex_unlock (self->lock); \
351 return TRUE; \
352 }
354 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (int);
355 DEFINE_TRIGGER_GET (int, gint, _interpolate_trigger_get_int);
356 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (uint);
357 DEFINE_TRIGGER_GET (uint, guint, _interpolate_trigger_get_uint);
358 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (long);
359 DEFINE_TRIGGER_GET (long, glong, _interpolate_trigger_get_long);
360 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (ulong);
361 DEFINE_TRIGGER_GET (ulong, gulong, _interpolate_trigger_get_ulong);
362 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (int64);
363 DEFINE_TRIGGER_GET (int64, gint64, _interpolate_trigger_get_int64);
364 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (uint64);
365 DEFINE_TRIGGER_GET (uint64, guint64, _interpolate_trigger_get_uint64);
366 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (float);
367 DEFINE_TRIGGER_GET (float, gfloat, _interpolate_trigger_get_float);
368 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (double);
369 DEFINE_TRIGGER_GET (double, gdouble, _interpolate_trigger_get_double);
371 DEFINE_TRIGGER_GET (boolean, gboolean, _interpolate_trigger_get);
372 DEFINE_TRIGGER_GET (enum, gint, _interpolate_trigger_get);
373 DEFINE_TRIGGER_GET (string, const gchar *, _interpolate_trigger_get);
375 static GstInterpolateMethod interpolate_trigger = {
376 (GstControlSourceGetValue) interpolate_trigger_get_int,
377 (GstControlSourceGetValueArray) interpolate_trigger_get_int_value_array,
378 (GstControlSourceGetValue) interpolate_trigger_get_uint,
379 (GstControlSourceGetValueArray) interpolate_trigger_get_uint_value_array,
380 (GstControlSourceGetValue) interpolate_trigger_get_long,
381 (GstControlSourceGetValueArray) interpolate_trigger_get_long_value_array,
382 (GstControlSourceGetValue) interpolate_trigger_get_ulong,
383 (GstControlSourceGetValueArray) interpolate_trigger_get_ulong_value_array,
384 (GstControlSourceGetValue) interpolate_trigger_get_int64,
385 (GstControlSourceGetValueArray) interpolate_trigger_get_int64_value_array,
386 (GstControlSourceGetValue) interpolate_trigger_get_uint64,
387 (GstControlSourceGetValueArray) interpolate_trigger_get_uint64_value_array,
388 (GstControlSourceGetValue) interpolate_trigger_get_float,
389 (GstControlSourceGetValueArray) interpolate_trigger_get_float_value_array,
390 (GstControlSourceGetValue) interpolate_trigger_get_double,
391 (GstControlSourceGetValueArray) interpolate_trigger_get_double_value_array,
392 (GstControlSourceGetValue) interpolate_trigger_get_boolean,
393 (GstControlSourceGetValueArray) interpolate_trigger_get_boolean_value_array,
394 (GstControlSourceGetValue) interpolate_trigger_get_enum,
395 (GstControlSourceGetValueArray) interpolate_trigger_get_enum_value_array,
396 (GstControlSourceGetValue) interpolate_trigger_get_string,
397 (GstControlSourceGetValueArray) interpolate_trigger_get_string_value_array
398 };
400 /* linear interpolation */
401 /* smoothes inbetween values */
402 #define DEFINE_LINEAR_GET(vtype, round, convert) \
403 static inline void \
404 _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) \
405 { \
406 if (GST_CLOCK_TIME_IS_VALID (timestamp2)) { \
407 gdouble slope; \
408 \
409 slope = ((gdouble) convert (value2) - (gdouble) convert (value1)) / gst_guint64_to_gdouble (timestamp2 - timestamp1); \
410 \
411 if (round) \
412 *ret = (g##vtype) (convert (value1) + gst_guint64_to_gdouble (timestamp - timestamp1) * slope + 0.5); \
413 else \
414 *ret = (g##vtype) (convert (value1) + gst_guint64_to_gdouble (timestamp - timestamp1) * slope); \
415 } else { \
416 *ret = value1; \
417 } \
418 *ret = CLAMP (*ret, min, max); \
419 } \
420 \
421 static gboolean \
422 interpolate_linear_get_##vtype (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
423 { \
424 g##vtype ret, min, max; \
425 GSequenceIter *iter; \
426 GstControlPoint *cp1, *cp2 = NULL, cp = {0, }; \
427 \
428 g_mutex_lock (self->lock); \
429 \
430 min = g_value_get_##vtype (&self->priv->minimum_value); \
431 max = g_value_get_##vtype (&self->priv->maximum_value); \
432 \
433 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
434 if (iter) { \
435 cp1 = g_sequence_get (iter); \
436 iter = g_sequence_iter_next (iter); \
437 } else { \
438 cp.timestamp = G_GUINT64_CONSTANT(0); \
439 g_value_init (&cp.value, self->priv->type); \
440 g_value_copy (&self->priv->default_value, &cp.value); \
441 cp1 = &cp; \
442 if (G_LIKELY (self->priv->values)) \
443 iter = g_sequence_get_begin_iter (self->priv->values); \
444 } \
445 if (iter && !g_sequence_iter_is_end (iter)) \
446 cp2 = g_sequence_get (iter); \
447 \
448 _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); \
449 g_value_set_##vtype (value, ret); \
450 g_mutex_unlock (self->lock); \
451 if (cp1 == &cp) \
452 g_value_unset (&cp.value); \
453 return TRUE; \
454 } \
455 \
456 static gboolean \
457 interpolate_linear_get_##vtype##_value_array (GstInterpolationControlSource *self, \
458 GstClockTime timestamp, GstValueArray * value_array) \
459 { \
460 gint i; \
461 GstClockTime ts = timestamp; \
462 GstClockTime next_ts = 0; \
463 g##vtype *values = (g##vtype *) value_array->values; \
464 GSequenceIter *iter1, *iter2 = NULL; \
465 GstControlPoint *cp1 = NULL, *cp2 = NULL, cp = {0, }; \
466 g##vtype val1 = 0, val2 = 0, min, max; \
467 \
468 g_mutex_lock (self->lock); \
469 \
470 cp.timestamp = G_GUINT64_CONSTANT(0); \
471 g_value_init (&cp.value, self->priv->type); \
472 g_value_copy (&self->priv->default_value, &cp.value); \
473 \
474 min = g_value_get_##vtype (&self->priv->minimum_value); \
475 max = g_value_get_##vtype (&self->priv->maximum_value); \
476 \
477 for(i = 0; i < value_array->nbsamples; i++) { \
478 if (timestamp >= next_ts) { \
479 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
480 if (!iter1) { \
481 cp1 = &cp; \
482 if (G_LIKELY (self->priv->values)) \
483 iter2 = g_sequence_get_begin_iter (self->priv->values); \
484 else \
485 iter2 = NULL; \
486 } else { \
487 cp1 = g_sequence_get (iter1); \
488 iter2 = g_sequence_iter_next (iter1); \
489 } \
490 \
491 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
492 cp2 = g_sequence_get (iter2); \
493 next_ts = cp2->timestamp; \
494 } else { \
495 next_ts = GST_CLOCK_TIME_NONE; \
496 } \
497 val1 = g_value_get_##vtype (&cp1->value); \
498 if (cp2) \
499 val2 = g_value_get_##vtype (&cp2->value); \
500 } \
501 _interpolate_linear_internal_##vtype (cp1->timestamp, val1, (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE), (cp2 ? val2 : 0), ts, min, max, values); \
502 ts += value_array->sample_interval; \
503 values++; \
504 } \
505 g_mutex_unlock (self->lock); \
506 g_value_unset (&cp.value); \
507 return TRUE; \
508 }
510 DEFINE_LINEAR_GET (int, TRUE, EMPTY);
511 DEFINE_LINEAR_GET (uint, TRUE, EMPTY);
512 DEFINE_LINEAR_GET (long, TRUE, EMPTY);
513 DEFINE_LINEAR_GET (ulong, TRUE, EMPTY);
514 DEFINE_LINEAR_GET (int64, TRUE, EMPTY);
515 DEFINE_LINEAR_GET (uint64, TRUE, gst_guint64_to_gdouble);
516 DEFINE_LINEAR_GET (float, FALSE, EMPTY);
517 DEFINE_LINEAR_GET (double, FALSE, EMPTY);
519 static GstInterpolateMethod interpolate_linear = {
520 (GstControlSourceGetValue) interpolate_linear_get_int,
521 (GstControlSourceGetValueArray) interpolate_linear_get_int_value_array,
522 (GstControlSourceGetValue) interpolate_linear_get_uint,
523 (GstControlSourceGetValueArray) interpolate_linear_get_uint_value_array,
524 (GstControlSourceGetValue) interpolate_linear_get_long,
525 (GstControlSourceGetValueArray) interpolate_linear_get_long_value_array,
526 (GstControlSourceGetValue) interpolate_linear_get_ulong,
527 (GstControlSourceGetValueArray) interpolate_linear_get_ulong_value_array,
528 (GstControlSourceGetValue) interpolate_linear_get_int64,
529 (GstControlSourceGetValueArray) interpolate_linear_get_int64_value_array,
530 (GstControlSourceGetValue) interpolate_linear_get_uint64,
531 (GstControlSourceGetValueArray) interpolate_linear_get_uint64_value_array,
532 (GstControlSourceGetValue) interpolate_linear_get_float,
533 (GstControlSourceGetValueArray) interpolate_linear_get_float_value_array,
534 (GstControlSourceGetValue) interpolate_linear_get_double,
535 (GstControlSourceGetValueArray) interpolate_linear_get_double_value_array,
536 (GstControlSourceGetValue) NULL,
537 (GstControlSourceGetValueArray) NULL,
538 (GstControlSourceGetValue) NULL,
539 (GstControlSourceGetValueArray) NULL,
540 (GstControlSourceGetValue) NULL,
541 (GstControlSourceGetValueArray) NULL
542 };
544 /* square interpolation */
546 /* cubic interpolation */
548 /* The following functions implement a natural cubic spline interpolator.
549 * For details look at http://en.wikipedia.org/wiki/Spline_interpolation
550 *
551 * Instead of using a real matrix with n^2 elements for the linear system
552 * of equations we use three arrays o, p, q to hold the tridiagonal matrix
553 * as following to save memory:
554 *
555 * p[0] q[0] 0 0 0
556 * o[1] p[1] q[1] 0 0
557 * 0 o[2] p[2] q[2] .
558 * . . . . .
559 */
561 #define DEFINE_CUBIC_GET(vtype,round, convert) \
562 static void \
563 _interpolate_cubic_update_cache_##vtype (GstInterpolationControlSource *self) \
564 { \
565 gint i, n = self->priv->nvalues; \
566 gdouble *o = g_new0 (gdouble, n); \
567 gdouble *p = g_new0 (gdouble, n); \
568 gdouble *q = g_new0 (gdouble, n); \
569 \
570 gdouble *h = g_new0 (gdouble, n); \
571 gdouble *b = g_new0 (gdouble, n); \
572 gdouble *z = g_new0 (gdouble, n); \
573 \
574 GSequenceIter *iter; \
575 GstControlPoint *cp; \
576 GstClockTime x, x_next; \
577 g##vtype y_prev, y, y_next; \
578 \
579 /* Fill linear system of equations */ \
580 iter = g_sequence_get_begin_iter (self->priv->values); \
581 cp = g_sequence_get (iter); \
582 x = cp->timestamp; \
583 y = g_value_get_##vtype (&cp->value); \
584 \
585 p[0] = 1.0; \
586 \
587 iter = g_sequence_iter_next (iter); \
588 cp = g_sequence_get (iter); \
589 x_next = cp->timestamp; \
590 y_next = g_value_get_##vtype (&cp->value); \
591 h[0] = gst_guint64_to_gdouble (x_next - x); \
592 \
593 for (i = 1; i < n-1; i++) { \
594 /* Shuffle x and y values */ \
595 y_prev = y; \
596 x = x_next; \
597 y = y_next; \
598 iter = g_sequence_iter_next (iter); \
599 cp = g_sequence_get (iter); \
600 x_next = cp->timestamp; \
601 y_next = g_value_get_##vtype (&cp->value); \
602 \
603 h[i] = gst_guint64_to_gdouble (x_next - x); \
604 o[i] = h[i-1]; \
605 p[i] = 2.0 * (h[i-1] + h[i]); \
606 q[i] = h[i]; \
607 b[i] = convert (y_next - y) / h[i] - convert (y - y_prev) / h[i-1]; \
608 } \
609 p[n-1] = 1.0; \
610 \
611 /* Use Gauss elimination to set everything below the \
612 * diagonal to zero */ \
613 for (i = 1; i < n-1; i++) { \
614 gdouble a = o[i] / p[i-1]; \
615 p[i] -= a * q[i-1]; \
616 b[i] -= a * b[i-1]; \
617 } \
618 \
619 /* Solve everything else from bottom to top */ \
620 for (i = n-2; i > 0; i--) \
621 z[i] = (b[i] - q[i] * z[i+1]) / p[i]; \
622 \
623 /* Save cache next in the GstControlPoint */ \
624 \
625 iter = g_sequence_get_begin_iter (self->priv->values); \
626 for (i = 0; i < n; i++) { \
627 cp = g_sequence_get (iter); \
628 cp->cache.cubic.h = h[i]; \
629 cp->cache.cubic.z = z[i]; \
630 iter = g_sequence_iter_next (iter); \
631 } \
632 \
633 /* Free our temporary arrays */ \
634 g_free (o); \
635 g_free (p); \
636 g_free (q); \
637 g_free (h); \
638 g_free (b); \
639 g_free (z); \
640 } \
641 \
642 static inline void \
643 _interpolate_cubic_get_##vtype (GstInterpolationControlSource *self, GstControlPoint *cp1, g##vtype value1, GstControlPoint *cp2, g##vtype value2, GstClockTime timestamp, g##vtype min, g##vtype max, g##vtype *ret) \
644 { \
645 if (!self->priv->valid_cache) { \
646 _interpolate_cubic_update_cache_##vtype (self); \
647 self->priv->valid_cache = TRUE; \
648 } \
649 \
650 if (cp2) { \
651 gdouble diff1, diff2; \
652 gdouble out; \
653 \
654 diff1 = gst_guint64_to_gdouble (timestamp - cp1->timestamp); \
655 diff2 = gst_guint64_to_gdouble (cp2->timestamp - timestamp); \
656 \
657 out = (cp2->cache.cubic.z * diff1 * diff1 * diff1 + cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h; \
658 out += (convert (value2) / cp1->cache.cubic.h - cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1; \
659 out += (convert (value1) / cp1->cache.cubic.h - cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2; \
660 \
661 if (round) \
662 *ret = (g##vtype) (out + 0.5); \
663 else \
664 *ret = (g##vtype) out; \
665 } \
666 else { \
667 *ret = value1; \
668 } \
669 *ret = CLAMP (*ret, min, max); \
670 } \
671 \
672 static gboolean \
673 interpolate_cubic_get_##vtype (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
674 { \
675 g##vtype ret, min, max; \
676 GSequenceIter *iter; \
677 GstControlPoint *cp1, *cp2 = NULL, cp = {0, }; \
678 \
679 if (self->priv->nvalues <= 2) \
680 return interpolate_linear_get_##vtype (self, timestamp, value); \
681 \
682 g_mutex_lock (self->lock); \
683 \
684 min = g_value_get_##vtype (&self->priv->minimum_value); \
685 max = g_value_get_##vtype (&self->priv->maximum_value); \
686 \
687 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
688 if (iter) { \
689 cp1 = g_sequence_get (iter); \
690 iter = g_sequence_iter_next (iter); \
691 } else { \
692 cp.timestamp = G_GUINT64_CONSTANT(0); \
693 g_value_init (&cp.value, self->priv->type); \
694 g_value_copy (&self->priv->default_value, &cp.value); \
695 cp1 = &cp; \
696 if (G_LIKELY (self->priv->values)) \
697 iter = g_sequence_get_begin_iter (self->priv->values); \
698 } \
699 if (iter && !g_sequence_iter_is_end (iter)) \
700 cp2 = g_sequence_get (iter); \
701 \
702 _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); \
703 g_value_set_##vtype (value, ret); \
704 g_mutex_unlock (self->lock); \
705 if (cp1 == &cp) \
706 g_value_unset (&cp.value); \
707 return TRUE; \
708 } \
709 \
710 static gboolean \
711 interpolate_cubic_get_##vtype##_value_array (GstInterpolationControlSource *self, \
712 GstClockTime timestamp, GstValueArray * value_array) \
713 { \
714 gint i; \
715 GstClockTime ts = timestamp; \
716 GstClockTime next_ts = 0; \
717 g##vtype *values = (g##vtype *) value_array->values; \
718 GSequenceIter *iter1, *iter2 = NULL; \
719 GstControlPoint *cp1 = NULL, *cp2 = NULL, cp = {0, }; \
720 g##vtype val1 = 0, val2 = 0, min, max; \
721 \
722 if (self->priv->nvalues <= 2) \
723 return interpolate_linear_get_##vtype##_value_array (self, timestamp, value_array); \
724 \
725 g_mutex_lock (self->lock); \
726 \
727 cp.timestamp = G_GUINT64_CONSTANT(0); \
728 g_value_init (&cp.value, self->priv->type); \
729 g_value_copy (&self->priv->default_value, &cp.value); \
730 \
731 min = g_value_get_##vtype (&self->priv->minimum_value); \
732 max = g_value_get_##vtype (&self->priv->maximum_value); \
733 \
734 for(i = 0; i < value_array->nbsamples; i++) { \
735 if (timestamp >= next_ts) { \
736 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
737 if (!iter1) { \
738 cp1 = &cp; \
739 if (G_LIKELY (self->priv->values)) \
740 iter2 = g_sequence_get_begin_iter (self->priv->values); \
741 else \
742 iter2 = NULL; \
743 } else { \
744 cp1 = g_sequence_get (iter1); \
745 iter2 = g_sequence_iter_next (iter1); \
746 } \
747 \
748 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
749 cp2 = g_sequence_get (iter2); \
750 next_ts = cp2->timestamp; \
751 } else { \
752 next_ts = GST_CLOCK_TIME_NONE; \
753 } \
754 val1 = g_value_get_##vtype (&cp1->value); \
755 if (cp2) \
756 val2 = g_value_get_##vtype (&cp2->value); \
757 } \
758 _interpolate_cubic_get_##vtype (self, cp1, val1, cp2, val2, timestamp, min, max, values); \
759 ts += value_array->sample_interval; \
760 values++; \
761 } \
762 g_mutex_unlock (self->lock); \
763 g_value_unset (&cp.value); \
764 return TRUE; \
765 }
767 DEFINE_CUBIC_GET (int, TRUE, EMPTY);
768 DEFINE_CUBIC_GET (uint, TRUE, EMPTY);
769 DEFINE_CUBIC_GET (long, TRUE, EMPTY);
770 DEFINE_CUBIC_GET (ulong, TRUE, EMPTY);
771 DEFINE_CUBIC_GET (int64, TRUE, EMPTY);
772 DEFINE_CUBIC_GET (uint64, TRUE, gst_guint64_to_gdouble);
773 DEFINE_CUBIC_GET (float, FALSE, EMPTY);
774 DEFINE_CUBIC_GET (double, FALSE, EMPTY);
776 static GstInterpolateMethod interpolate_cubic = {
777 (GstControlSourceGetValue) interpolate_cubic_get_int,
778 (GstControlSourceGetValueArray) interpolate_cubic_get_int_value_array,
779 (GstControlSourceGetValue) interpolate_cubic_get_uint,
780 (GstControlSourceGetValueArray) interpolate_cubic_get_uint_value_array,
781 (GstControlSourceGetValue) interpolate_cubic_get_long,
782 (GstControlSourceGetValueArray) interpolate_cubic_get_long_value_array,
783 (GstControlSourceGetValue) interpolate_cubic_get_ulong,
784 (GstControlSourceGetValueArray) interpolate_cubic_get_ulong_value_array,
785 (GstControlSourceGetValue) interpolate_cubic_get_int64,
786 (GstControlSourceGetValueArray) interpolate_cubic_get_int64_value_array,
787 (GstControlSourceGetValue) interpolate_cubic_get_uint64,
788 (GstControlSourceGetValueArray) interpolate_cubic_get_uint64_value_array,
789 (GstControlSourceGetValue) interpolate_cubic_get_float,
790 (GstControlSourceGetValueArray) interpolate_cubic_get_float_value_array,
791 (GstControlSourceGetValue) interpolate_cubic_get_double,
792 (GstControlSourceGetValueArray) interpolate_cubic_get_double_value_array,
793 (GstControlSourceGetValue) NULL,
794 (GstControlSourceGetValueArray) NULL,
795 (GstControlSourceGetValue) NULL,
796 (GstControlSourceGetValueArray) NULL,
797 (GstControlSourceGetValue) NULL,
798 (GstControlSourceGetValueArray) NULL
799 };
801 /* register all interpolation methods */
802 GstInterpolateMethod *priv_gst_interpolation_methods[] = {
803 &interpolate_none,
804 &interpolate_trigger,
805 &interpolate_linear,
806 &interpolate_cubic,
807 &interpolate_cubic
808 };
810 guint priv_gst_num_interpolation_methods =
811 G_N_ELEMENTS (priv_gst_interpolation_methods);