controlbindings: add comments for sparse control source
[platform/upstream/gstreamer.git] / libs / gst / controller / gstdirectcontrolbinding.c
1 /* GStreamer
2  *
3  * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
4  *
5  * gstdirectcontrolbinding.c: Direct attachment for control sources
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 /**
23  * SECTION:gstdirectcontrolbinding
24  * @short_description: direct attachment for control source sources
25  *
26  * A value mapping object that attaches control sources to gobject properties.
27  */
28
29
30 #include <glib-object.h>
31 #include <gst/gst.h>
32
33 #include "gstdirectcontrolbinding.h"
34
35 #include <math.h>
36
37 #define GST_CAT_DEFAULT control_binding_debug
38 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
39
40 static GObject *gst_direct_control_binding_constructor (GType type,
41     guint n_construct_params, GObjectConstructParam * construct_params);
42 static void gst_direct_control_binding_set_property (GObject * object,
43     guint prop_id, const GValue * value, GParamSpec * pspec);
44 static void gst_direct_control_binding_get_property (GObject * object,
45     guint prop_id, GValue * value, GParamSpec * pspec);
46 static void gst_direct_control_binding_dispose (GObject * object);
47 static void gst_direct_control_binding_finalize (GObject * object);
48
49 static gboolean gst_direct_control_binding_sync_values (GstControlBinding *
50     _self, GstObject * object, GstClockTime timestamp, GstClockTime last_sync);
51 static GValue *gst_direct_control_binding_get_value (GstControlBinding * _self,
52     GstClockTime timestamp);
53 static gboolean gst_direct_control_binding_get_value_array (GstControlBinding *
54     _self, GstClockTime timestamp, GstClockTime interval, guint n_values,
55     gpointer values);
56 static gboolean gst_direct_control_binding_get_g_value_array (GstControlBinding
57     * _self, GstClockTime timestamp, GstClockTime interval, guint n_values,
58     GValue * values);
59
60 #define _do_init \
61   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstdirectcontrolbinding", 0, \
62       "dynamic parameter control source attachment");
63
64 #define gst_direct_control_binding_parent_class parent_class
65 G_DEFINE_TYPE_WITH_CODE (GstDirectControlBinding, gst_direct_control_binding,
66     GST_TYPE_CONTROL_BINDING, _do_init);
67
68 enum
69 {
70   PROP_0,
71   PROP_CS,
72   PROP_LAST
73 };
74
75 static GParamSpec *properties[PROP_LAST];
76
77 /* mapping functions */
78
79 #define DEFINE_CONVERT(type,Type,TYPE) \
80 static void \
81 convert_g_value_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \
82 { \
83   GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \
84   g##type v; \
85   \
86   s = CLAMP (s, 0.0, 1.0); \
87   v = pspec->minimum + (g##type) ((pspec->maximum - pspec->minimum) * s); \
88   g_value_set_##type (d, v); \
89 } \
90 \
91 static void \
92 convert_value_to_##type (GstDirectControlBinding *self, gdouble s, gpointer d_) \
93 { \
94   GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \
95   g##type *d = (g##type *)d_; \
96   \
97   s = CLAMP (s, 0.0, 1.0); \
98   *d = pspec->minimum + (g##type) ((pspec->maximum - pspec->minimum) * s); \
99 }
100
101
102 DEFINE_CONVERT (int, Int, INT);
103 DEFINE_CONVERT (uint, UInt, UINT);
104 DEFINE_CONVERT (long, Long, LONG);
105 DEFINE_CONVERT (ulong, ULong, ULONG);
106 DEFINE_CONVERT (int64, Int64, INT64);
107 DEFINE_CONVERT (uint64, UInt64, UINT64);
108 DEFINE_CONVERT (float, Float, FLOAT);
109 DEFINE_CONVERT (double, Double, DOUBLE);
110
111 static void
112 convert_g_value_to_boolean (GstDirectControlBinding * self, gdouble s,
113     GValue * d)
114 {
115   s = CLAMP (s, 0.0, 1.0);
116   g_value_set_boolean (d, (gboolean) (s + 0.5));
117 }
118
119 static void
120 convert_value_to_boolean (GstDirectControlBinding * self, gdouble s,
121     gpointer d_)
122 {
123   gboolean *d = (gboolean *) d_;
124
125   s = CLAMP (s, 0.0, 1.0);
126   *d = (gboolean) (s + 0.5);
127 }
128
129 static void
130 convert_g_value_to_enum (GstDirectControlBinding * self, gdouble s, GValue * d)
131 {
132   GParamSpecEnum *pspec =
133       G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec);
134   GEnumClass *e = pspec->enum_class;
135   gint v;
136
137   s = CLAMP (s, 0.0, 1.0);
138   v = s * (e->n_values - 1);
139   g_value_set_enum (d, e->values[v].value);
140 }
141
142 static void
143 convert_value_to_enum (GstDirectControlBinding * self, gdouble s, gpointer d_)
144 {
145   GParamSpecEnum *pspec =
146       G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec);
147   GEnumClass *e = pspec->enum_class;
148   gint *d = (gint *) d_;
149
150   s = CLAMP (s, 0.0, 1.0);
151   *d = e->values[(gint) (s * (e->n_values - 1))].value;
152 }
153
154 /* vmethods */
155
156 static void
157 gst_direct_control_binding_class_init (GstDirectControlBindingClass * klass)
158 {
159   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
160   GstControlBindingClass *control_binding_class =
161       GST_CONTROL_BINDING_CLASS (klass);
162
163   gobject_class->constructor = gst_direct_control_binding_constructor;
164   gobject_class->set_property = gst_direct_control_binding_set_property;
165   gobject_class->get_property = gst_direct_control_binding_get_property;
166   gobject_class->dispose = gst_direct_control_binding_dispose;
167   gobject_class->finalize = gst_direct_control_binding_finalize;
168
169   control_binding_class->sync_values = gst_direct_control_binding_sync_values;
170   control_binding_class->get_value = gst_direct_control_binding_get_value;
171   control_binding_class->get_value_array =
172       gst_direct_control_binding_get_value_array;
173   control_binding_class->get_g_value_array =
174       gst_direct_control_binding_get_g_value_array;
175
176   properties[PROP_CS] =
177       g_param_spec_object ("control-source", "ControlSource",
178       "The control source",
179       GST_TYPE_CONTROL_SOURCE,
180       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
181
182   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
183 }
184
185 static void
186 gst_direct_control_binding_init (GstDirectControlBinding * self)
187 {
188 }
189
190 static GObject *
191 gst_direct_control_binding_constructor (GType type, guint n_construct_params,
192     GObjectConstructParam * construct_params)
193 {
194   GstDirectControlBinding *self;
195
196   self =
197       GST_DIRECT_CONTROL_BINDING (G_OBJECT_CLASS (parent_class)->constructor
198       (type, n_construct_params, construct_params));
199
200   if (GST_CONTROL_BINDING_PSPEC (self)) {
201     GType type, base;
202
203     base = type = G_PARAM_SPEC_VALUE_TYPE (GST_CONTROL_BINDING_PSPEC (self));
204     g_value_init (&self->cur_value, type);
205     while ((type = g_type_parent (type)))
206       base = type;
207
208     GST_DEBUG ("  using type %s", g_type_name (base));
209
210     // select mapping function
211     switch (base) {
212       case G_TYPE_INT:
213         self->convert_g_value = convert_g_value_to_int;
214         self->convert_value = convert_value_to_int;
215         self->byte_size = sizeof (gint);
216         break;
217       case G_TYPE_UINT:
218         self->convert_g_value = convert_g_value_to_uint;
219         self->convert_value = convert_value_to_uint;
220         self->byte_size = sizeof (guint);
221         break;
222       case G_TYPE_LONG:
223         self->convert_g_value = convert_g_value_to_long;
224         self->convert_value = convert_value_to_long;
225         self->byte_size = sizeof (glong);
226         break;
227       case G_TYPE_ULONG:
228         self->convert_g_value = convert_g_value_to_ulong;
229         self->convert_value = convert_value_to_ulong;
230         self->byte_size = sizeof (gulong);
231         break;
232       case G_TYPE_INT64:
233         self->convert_g_value = convert_g_value_to_int64;
234         self->convert_value = convert_value_to_int64;
235         self->byte_size = sizeof (gint64);
236         break;
237       case G_TYPE_UINT64:
238         self->convert_g_value = convert_g_value_to_uint64;
239         self->convert_value = convert_value_to_uint64;
240         self->byte_size = sizeof (guint64);
241         break;
242       case G_TYPE_FLOAT:
243         self->convert_g_value = convert_g_value_to_float;
244         self->convert_value = convert_value_to_float;
245         self->byte_size = sizeof (gfloat);
246         break;
247       case G_TYPE_DOUBLE:
248         self->convert_g_value = convert_g_value_to_double;
249         self->convert_value = convert_value_to_double;
250         self->byte_size = sizeof (gdouble);
251         break;
252       case G_TYPE_BOOLEAN:
253         self->convert_g_value = convert_g_value_to_boolean;
254         self->convert_value = convert_value_to_boolean;
255         self->byte_size = sizeof (gboolean);
256         break;
257       case G_TYPE_ENUM:
258         self->convert_g_value = convert_g_value_to_enum;
259         self->convert_value = convert_value_to_enum;
260         self->byte_size = sizeof (gint);
261         break;
262       default:
263         GST_WARNING ("incomplete implementation for paramspec type '%s'",
264             G_PARAM_SPEC_TYPE_NAME (GST_CONTROL_BINDING_PSPEC (self)));
265         GST_CONTROL_BINDING_PSPEC (self) = NULL;
266         break;
267     }
268   }
269   return (GObject *) self;
270 }
271
272 static void
273 gst_direct_control_binding_set_property (GObject * object, guint prop_id,
274     const GValue * value, GParamSpec * pspec)
275 {
276   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
277
278   switch (prop_id) {
279     case PROP_CS:
280       self->cs = g_value_dup_object (value);
281       break;
282     default:
283       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
284       break;
285   }
286 }
287
288 static void
289 gst_direct_control_binding_get_property (GObject * object, guint prop_id,
290     GValue * value, GParamSpec * pspec)
291 {
292   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
293
294   switch (prop_id) {
295     case PROP_CS:
296       g_value_set_object (value, self->cs);
297       break;
298     default:
299       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
300       break;
301   }
302 }
303
304 static void
305 gst_direct_control_binding_dispose (GObject * object)
306 {
307   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
308
309   if (self->cs)
310     gst_object_replace ((GstObject **) & self->cs, NULL);
311
312   G_OBJECT_CLASS (parent_class)->dispose (object);
313 }
314
315 static void
316 gst_direct_control_binding_finalize (GObject * object)
317 {
318   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
319
320   g_value_unset (&self->cur_value);
321
322   G_OBJECT_CLASS (parent_class)->finalize (object);
323 }
324
325 static gboolean
326 gst_direct_control_binding_sync_values (GstControlBinding * _self,
327     GstObject * object, GstClockTime timestamp, GstClockTime last_sync)
328 {
329   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
330   gdouble src_val;
331   gboolean ret;
332
333   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
334   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
335
336   GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT,
337       _self->name, GST_TIME_ARGS (timestamp));
338
339   ret = gst_control_source_get_value (self->cs, timestamp, &src_val);
340   if (G_LIKELY (ret)) {
341     GST_LOG_OBJECT (object, "  new value %lf", src_val);
342     /* always set the value for first time, but then only if it changed
343      * this should limit g_object_notify invocations.
344      * FIXME: can we detect negative playback rates?
345      */
346     if ((timestamp < last_sync) || (src_val != self->last_value)) {
347       GValue *dst_val = &self->cur_value;
348
349       GST_LOG_OBJECT (object, "  mapping %s to value of type %s", _self->name,
350           G_VALUE_TYPE_NAME (dst_val));
351       /* run mapping function to convert gdouble to GValue */
352       self->convert_g_value (self, src_val, dst_val);
353       /* we can make this faster
354        * http://bugzilla.gnome.org/show_bug.cgi?id=536939
355        */
356       g_object_set_property ((GObject *) object, _self->name, dst_val);
357       self->last_value = src_val;
358     }
359   } else {
360     GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name);
361   }
362   return (ret);
363 }
364
365 static GValue *
366 gst_direct_control_binding_get_value (GstControlBinding * _self,
367     GstClockTime timestamp)
368 {
369   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
370   GValue *dst_val = NULL;
371   gdouble src_val;
372
373   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), NULL);
374   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
375   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
376
377   /* get current value via control source */
378   if (gst_control_source_get_value (self->cs, timestamp, &src_val)) {
379     dst_val = g_new0 (GValue, 1);
380     g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (_self->pspec));
381     self->convert_g_value (self, src_val, dst_val);
382   } else {
383     GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT,
384         _self->name, GST_TIME_ARGS (timestamp));
385   }
386
387   return dst_val;
388 }
389
390 static gboolean
391 gst_direct_control_binding_get_value_array (GstControlBinding * _self,
392     GstClockTime timestamp, GstClockTime interval, guint n_values,
393     gpointer values_)
394 {
395   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
396   gint i;
397   gdouble *src_val;
398   gboolean res = FALSE;
399   GstDirectControlBindingConvertValue convert;
400   gint byte_size;
401   guint8 *values = (guint8 *) values_;
402
403   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
404   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
405   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
406   g_return_val_if_fail (values, FALSE);
407   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
408
409   convert = self->convert_value;
410   byte_size = self->byte_size;
411
412   src_val = g_new0 (gdouble, n_values);
413   if ((res = gst_control_source_get_value_array (self->cs, timestamp,
414               interval, n_values, src_val))) {
415     for (i = 0; i < n_values; i++) {
416       /* we will only get NAN for sparse control sources, such as triggers */
417       if (!isnan (src_val[i])) {
418         convert (self, src_val[i], (gpointer) values);
419       } else {
420         GST_LOG ("no control value for property %s at index %d", _self->name,
421             i);
422       }
423       values += byte_size;
424     }
425   } else {
426     GST_LOG ("failed to get control value for property %s at ts %"
427         GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
428   }
429   g_free (src_val);
430   return res;
431 }
432
433 static gboolean
434 gst_direct_control_binding_get_g_value_array (GstControlBinding * _self,
435     GstClockTime timestamp, GstClockTime interval, guint n_values,
436     GValue * values)
437 {
438   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
439   gint i;
440   gdouble *src_val;
441   gboolean res = FALSE;
442   GType type;
443   GstDirectControlBindingConvertGValue convert;
444
445   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
446   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
447   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
448   g_return_val_if_fail (values, FALSE);
449   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
450
451   convert = self->convert_g_value;
452   type = G_PARAM_SPEC_VALUE_TYPE (_self->pspec);
453
454   src_val = g_new0 (gdouble, n_values);
455   if ((res = gst_control_source_get_value_array (self->cs, timestamp,
456               interval, n_values, src_val))) {
457     for (i = 0; i < n_values; i++) {
458       /* we will only get NAN for sparse control sources, such as triggers */
459       if (!isnan (src_val[i])) {
460         g_value_init (&values[i], type);
461         convert (self, src_val[i], &values[i]);
462       } else {
463         GST_LOG ("no control value for property %s at index %d", _self->name,
464             i);
465       }
466     }
467   } else {
468     GST_LOG ("failed to get control value for property %s at ts %"
469         GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
470   }
471   g_free (src_val);
472   return res;
473 }
474
475 /* functions */
476
477 /**
478  * gst_direct_control_binding_new:
479  * @object: the object of the property
480  * @property_name: the property-name to attach the control source
481  * @csource: the control source
482  *
483  * Create a new control-binding that attaches the #GstControlSource to the
484  * #GObject property.
485  *
486  * Returns: (transfer floating): the new #GstDirectControlBinding
487  */
488 GstControlBinding *
489 gst_direct_control_binding_new (GstObject * object, const gchar * property_name,
490     GstControlSource * cs)
491 {
492   return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING,
493       "object", object, "name", property_name, "control-source", cs, NULL);
494 }