"Fossies" - the Fresh Open Source Software Archive

Member "gstreamer-1.16.1/libs/gst/controller/gstinterpolationcontrolsource.c" (19 Apr 2019, 20672 Bytes) of package /linux/misc/gstreamer-1.16.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gstinterpolationcontrolsource.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.14.4_vs_1.16.0.

    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
   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
   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
  112 _interpolate_none (GstTimedValueControlSource * self, GstControlPoint * cp)
  113 {
  114   return cp->value;
  115 }
  116 
  117 static gboolean
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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 *
  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
  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
  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
  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
  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
  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 }