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., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 /**
23  * SECTION:gstdirectcontrolbinding
24  * @title: GstDirectControlBinding
25  * @short_description: direct attachment for control sources
26  *
27  * A value mapping object that attaches control sources to gobject properties. It
28  * will map the control values directly to the target property range. If a
29  * non-absolute direct control binding is used, the value range [0.0 ... 1.0]
30  * is mapped to full target property range, and all values outside the range
31  * will be clipped. An absolute control binding will not do any value
32  * transformations.
33  */
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <glib-object.h>
39 #include <gst/gst.h>
40 
41 #include "gstdirectcontrolbinding.h"
42 
43 #include <gst/math-compat.h>
44 
45 #define GST_CAT_DEFAULT control_binding_debug
46 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
47 
48 
49 static GObject *gst_direct_control_binding_constructor (GType type,
50     guint n_construct_params, GObjectConstructParam * construct_params);
51 static void gst_direct_control_binding_set_property (GObject * object,
52     guint prop_id, const GValue * value, GParamSpec * pspec);
53 static void gst_direct_control_binding_get_property (GObject * object,
54     guint prop_id, GValue * value, GParamSpec * pspec);
55 static void gst_direct_control_binding_dispose (GObject * object);
56 static void gst_direct_control_binding_finalize (GObject * object);
57 
58 static gboolean gst_direct_control_binding_sync_values (GstControlBinding *
59     _self, GstObject * object, GstClockTime timestamp, GstClockTime last_sync);
60 static GValue *gst_direct_control_binding_get_value (GstControlBinding * _self,
61     GstClockTime timestamp);
62 static gboolean gst_direct_control_binding_get_value_array (GstControlBinding *
63     _self, GstClockTime timestamp, GstClockTime interval, guint n_values,
64     gpointer values);
65 static gboolean gst_direct_control_binding_get_g_value_array (GstControlBinding
66     * _self, GstClockTime timestamp, GstClockTime interval, guint n_values,
67     GValue * values);
68 
69 #define _do_init \
70   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstdirectcontrolbinding", 0, \
71       "dynamic parameter control source attachment");
72 
73 #define gst_direct_control_binding_parent_class parent_class
74 G_DEFINE_TYPE_WITH_CODE (GstDirectControlBinding, gst_direct_control_binding,
75     GST_TYPE_CONTROL_BINDING, _do_init);
76 
77 enum
78 {
79   PROP_0,
80   PROP_CS,
81   PROP_ABSOLUTE,
82   PROP_LAST
83 };
84 
85 static GParamSpec *properties[PROP_LAST];
86 
87 /* mapping functions */
88 
89 #define DEFINE_CONVERT(type,Type,TYPE,ROUNDING_OP) \
90 static void \
91 convert_g_value_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \
92 { \
93   GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \
94   g##type v; \
95   \
96   s = CLAMP (s, 0.0, 1.0); \
97   v = (g##type) ROUNDING_OP (pspec->minimum * (1-s)) + (g##type) ROUNDING_OP (pspec->maximum * s); \
98   g_value_set_##type (d, v); \
99 } \
100 \
101 static void \
102 convert_value_to_##type (GstDirectControlBinding *self, gdouble s, gpointer d_) \
103 { \
104   GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \
105   g##type *d = (g##type *)d_; \
106   \
107   s = CLAMP (s, 0.0, 1.0); \
108   *d = (g##type) ROUNDING_OP (pspec->minimum * (1-s)) + (g##type) ROUNDING_OP (pspec->maximum * s); \
109 } \
110 \
111 static void \
112 abs_convert_g_value_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \
113 { \
114   g##type v; \
115   v = (g##type) ROUNDING_OP (s); \
116   g_value_set_##type (d, v); \
117 } \
118 \
119 static void \
120 abs_convert_value_to_##type (GstDirectControlBinding *self, gdouble s, gpointer d_) \
121 { \
122   g##type *d = (g##type *)d_; \
123   *d = (g##type) ROUNDING_OP (s); \
124 }
125 
126 DEFINE_CONVERT (int, Int, INT, rint);
127 DEFINE_CONVERT (uint, UInt, UINT, rint);
128 DEFINE_CONVERT (long, Long, LONG, rint);
129 DEFINE_CONVERT (ulong, ULong, ULONG, rint);
130 DEFINE_CONVERT (int64, Int64, INT64, rint);
131 DEFINE_CONVERT (uint64, UInt64, UINT64, rint);
132 DEFINE_CONVERT (float, Float, FLOAT, /*NOOP*/);
133 DEFINE_CONVERT (double, Double, DOUBLE, /*NOOP*/);
134 
135 static void
convert_g_value_to_boolean(GstDirectControlBinding * self,gdouble s,GValue * d)136 convert_g_value_to_boolean (GstDirectControlBinding * self, gdouble s,
137     GValue * d)
138 {
139   s = CLAMP (s, 0.0, 1.0);
140   g_value_set_boolean (d, (gboolean) (s + 0.5));
141 }
142 
143 static void
convert_value_to_boolean(GstDirectControlBinding * self,gdouble s,gpointer d_)144 convert_value_to_boolean (GstDirectControlBinding * self, gdouble s,
145     gpointer d_)
146 {
147   gboolean *d = (gboolean *) d_;
148 
149   s = CLAMP (s, 0.0, 1.0);
150   *d = (gboolean) (s + 0.5);
151 }
152 
153 static void
convert_g_value_to_enum(GstDirectControlBinding * self,gdouble s,GValue * d)154 convert_g_value_to_enum (GstDirectControlBinding * self, gdouble s, GValue * d)
155 {
156   GParamSpecEnum *pspec =
157       G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec);
158   GEnumClass *e = pspec->enum_class;
159   gint v;
160 
161   s = CLAMP (s, 0.0, 1.0);
162   v = s * (e->n_values - 1);
163   g_value_set_enum (d, e->values[v].value);
164 }
165 
166 static void
convert_value_to_enum(GstDirectControlBinding * self,gdouble s,gpointer d_)167 convert_value_to_enum (GstDirectControlBinding * self, gdouble s, gpointer d_)
168 {
169   GParamSpecEnum *pspec =
170       G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec);
171   GEnumClass *e = pspec->enum_class;
172   gint *d = (gint *) d_;
173 
174   s = CLAMP (s, 0.0, 1.0);
175   *d = e->values[(gint) (s * (e->n_values - 1))].value;
176 }
177 
178 /* vmethods */
179 
180 static void
gst_direct_control_binding_class_init(GstDirectControlBindingClass * klass)181 gst_direct_control_binding_class_init (GstDirectControlBindingClass * klass)
182 {
183   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
184   GstControlBindingClass *control_binding_class =
185       GST_CONTROL_BINDING_CLASS (klass);
186 
187   gobject_class->constructor = gst_direct_control_binding_constructor;
188   gobject_class->set_property = gst_direct_control_binding_set_property;
189   gobject_class->get_property = gst_direct_control_binding_get_property;
190   gobject_class->dispose = gst_direct_control_binding_dispose;
191   gobject_class->finalize = gst_direct_control_binding_finalize;
192 
193   control_binding_class->sync_values = gst_direct_control_binding_sync_values;
194   control_binding_class->get_value = gst_direct_control_binding_get_value;
195   control_binding_class->get_value_array =
196       gst_direct_control_binding_get_value_array;
197   control_binding_class->get_g_value_array =
198       gst_direct_control_binding_get_g_value_array;
199 
200   properties[PROP_CS] =
201       g_param_spec_object ("control-source", "ControlSource",
202       "The control source",
203       GST_TYPE_CONTROL_SOURCE,
204       G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
205 
206   properties[PROP_ABSOLUTE] =
207       g_param_spec_boolean ("absolute", "Absolute",
208       "Whether the control values are absolute",
209       FALSE,
210       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
211 
212   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
213 }
214 
215 static void
gst_direct_control_binding_init(GstDirectControlBinding * self)216 gst_direct_control_binding_init (GstDirectControlBinding * self)
217 {
218 }
219 
220 static GObject *
gst_direct_control_binding_constructor(GType type,guint n_construct_params,GObjectConstructParam * construct_params)221 gst_direct_control_binding_constructor (GType type, guint n_construct_params,
222     GObjectConstructParam * construct_params)
223 {
224   GstDirectControlBinding *self;
225 
226   self =
227       GST_DIRECT_CONTROL_BINDING (G_OBJECT_CLASS (parent_class)->constructor
228       (type, n_construct_params, construct_params));
229 
230   if (GST_CONTROL_BINDING_PSPEC (self)) {
231     GType type, base;
232 
233     base = type = G_PARAM_SPEC_VALUE_TYPE (GST_CONTROL_BINDING_PSPEC (self));
234     g_value_init (&self->cur_value, type);
235     while ((type = g_type_parent (type)))
236       base = type;
237 
238     GST_DEBUG ("  using type %s", g_type_name (base));
239 
240     /* select mapping function */
241 
242 #define SET_CONVERT_FUNCTION(type) \
243     if (self->ABI.abi.want_absolute) { \
244         self->convert_g_value = abs_convert_g_value_to_##type; \
245         self->convert_value = abs_convert_value_to_##type; \
246     } \
247     else { \
248         self->convert_g_value = convert_g_value_to_##type; \
249         self->convert_value = convert_value_to_##type; \
250     } \
251     self->byte_size = sizeof (g##type);
252 
253 
254     switch (base) {
255       case G_TYPE_INT:
256         SET_CONVERT_FUNCTION (int);
257         break;
258       case G_TYPE_UINT:
259         SET_CONVERT_FUNCTION (uint);
260         break;
261       case G_TYPE_LONG:
262         SET_CONVERT_FUNCTION (long);
263         break;
264       case G_TYPE_ULONG:
265         SET_CONVERT_FUNCTION (ulong);
266         break;
267       case G_TYPE_INT64:
268         SET_CONVERT_FUNCTION (int64);
269         break;
270       case G_TYPE_UINT64:
271         SET_CONVERT_FUNCTION (uint64);
272         break;
273       case G_TYPE_FLOAT:
274         SET_CONVERT_FUNCTION (float);
275         break;
276       case G_TYPE_DOUBLE:
277         SET_CONVERT_FUNCTION (double);
278         break;
279       case G_TYPE_BOOLEAN:
280         self->convert_g_value = convert_g_value_to_boolean;
281         self->convert_value = convert_value_to_boolean;
282         self->byte_size = sizeof (gboolean);
283         break;
284       case G_TYPE_ENUM:
285         self->convert_g_value = convert_g_value_to_enum;
286         self->convert_value = convert_value_to_enum;
287         self->byte_size = sizeof (gint);
288         break;
289       default:
290         GST_WARNING ("incomplete implementation for paramspec type '%s'",
291             G_PARAM_SPEC_TYPE_NAME (GST_CONTROL_BINDING_PSPEC (self)));
292         GST_CONTROL_BINDING_PSPEC (self) = NULL;
293         break;
294     }
295   }
296   return (GObject *) self;
297 }
298 
299 static void
gst_direct_control_binding_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)300 gst_direct_control_binding_set_property (GObject * object, guint prop_id,
301     const GValue * value, GParamSpec * pspec)
302 {
303   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
304 
305   switch (prop_id) {
306     case PROP_CS:
307       self->cs = g_value_dup_object (value);
308       break;
309     case PROP_ABSOLUTE:
310       self->ABI.abi.want_absolute = g_value_get_boolean (value);
311       break;
312     default:
313       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
314       break;
315   }
316 }
317 
318 static void
gst_direct_control_binding_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)319 gst_direct_control_binding_get_property (GObject * object, guint prop_id,
320     GValue * value, GParamSpec * pspec)
321 {
322   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
323 
324   switch (prop_id) {
325     case PROP_CS:
326       g_value_set_object (value, self->cs);
327       break;
328     case PROP_ABSOLUTE:
329       g_value_set_boolean (value, self->ABI.abi.want_absolute);
330       break;
331     default:
332       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
333       break;
334   }
335 }
336 
337 static void
gst_direct_control_binding_dispose(GObject * object)338 gst_direct_control_binding_dispose (GObject * object)
339 {
340   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
341 
342   if (self->cs)
343     gst_object_replace ((GstObject **) & self->cs, NULL);
344 
345   G_OBJECT_CLASS (parent_class)->dispose (object);
346 }
347 
348 static void
gst_direct_control_binding_finalize(GObject * object)349 gst_direct_control_binding_finalize (GObject * object)
350 {
351   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
352 
353   if (G_IS_VALUE (&self->cur_value))
354     g_value_unset (&self->cur_value);
355 
356   G_OBJECT_CLASS (parent_class)->finalize (object);
357 }
358 
359 static gboolean
gst_direct_control_binding_sync_values(GstControlBinding * _self,GstObject * object,GstClockTime timestamp,GstClockTime last_sync)360 gst_direct_control_binding_sync_values (GstControlBinding * _self,
361     GstObject * object, GstClockTime timestamp, GstClockTime last_sync)
362 {
363   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
364   gdouble src_val;
365   gboolean ret;
366 
367   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
368   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
369 
370   GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT,
371       _self->name, GST_TIME_ARGS (timestamp));
372 
373   ret = gst_control_source_get_value (self->cs, timestamp, &src_val);
374   if (G_LIKELY (ret)) {
375     GST_LOG_OBJECT (object, "  new value %lf", src_val);
376     /* always set the value for first time, but then only if it changed
377      * this should limit g_object_notify invocations.
378      * FIXME: can we detect negative playback rates?
379      */
380     if ((timestamp < last_sync) || (src_val != self->last_value)) {
381       GValue *dst_val = &self->cur_value;
382 
383       GST_LOG_OBJECT (object, "  mapping %s to value of type %s", _self->name,
384           G_VALUE_TYPE_NAME (dst_val));
385       /* run mapping function to convert gdouble to GValue */
386       self->convert_g_value (self, src_val, dst_val);
387       /* we can make this faster
388        * http://bugzilla.gnome.org/show_bug.cgi?id=536939
389        */
390       g_object_set_property ((GObject *) object, _self->name, dst_val);
391       self->last_value = src_val;
392     }
393   } else {
394     GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name);
395   }
396   return (ret);
397 }
398 
399 static GValue *
gst_direct_control_binding_get_value(GstControlBinding * _self,GstClockTime timestamp)400 gst_direct_control_binding_get_value (GstControlBinding * _self,
401     GstClockTime timestamp)
402 {
403   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
404   GValue *dst_val = NULL;
405   gdouble src_val;
406 
407   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), NULL);
408   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
409   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
410 
411   /* get current value via control source */
412   if (gst_control_source_get_value (self->cs, timestamp, &src_val)) {
413     dst_val = g_new0 (GValue, 1);
414     g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (_self->pspec));
415     self->convert_g_value (self, src_val, dst_val);
416   } else {
417     GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT,
418         _self->name, GST_TIME_ARGS (timestamp));
419   }
420 
421   return dst_val;
422 }
423 
424 static gboolean
gst_direct_control_binding_get_value_array(GstControlBinding * _self,GstClockTime timestamp,GstClockTime interval,guint n_values,gpointer values_)425 gst_direct_control_binding_get_value_array (GstControlBinding * _self,
426     GstClockTime timestamp, GstClockTime interval, guint n_values,
427     gpointer values_)
428 {
429   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
430   gint i;
431   gdouble *src_val;
432   gboolean res = FALSE;
433   GstDirectControlBindingConvertValue convert;
434   gint byte_size;
435   guint8 *values = (guint8 *) values_;
436 
437   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
438   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
439   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
440   g_return_val_if_fail (values, FALSE);
441   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
442 
443   convert = self->convert_value;
444   byte_size = self->byte_size;
445 
446   src_val = g_new0 (gdouble, n_values);
447   if ((res = gst_control_source_get_value_array (self->cs, timestamp,
448               interval, n_values, src_val))) {
449     for (i = 0; i < n_values; i++) {
450       /* we will only get NAN for sparse control sources, such as triggers */
451       if (!isnan (src_val[i])) {
452         convert (self, src_val[i], (gpointer) values);
453       } else {
454         GST_LOG ("no control value for property %s at index %d", _self->name,
455             i);
456       }
457       values += byte_size;
458     }
459   } else {
460     GST_LOG ("failed to get control value for property %s at ts %"
461         GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
462   }
463   g_free (src_val);
464   return res;
465 }
466 
467 static gboolean
gst_direct_control_binding_get_g_value_array(GstControlBinding * _self,GstClockTime timestamp,GstClockTime interval,guint n_values,GValue * values)468 gst_direct_control_binding_get_g_value_array (GstControlBinding * _self,
469     GstClockTime timestamp, GstClockTime interval, guint n_values,
470     GValue * values)
471 {
472   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
473   gint i;
474   gdouble *src_val;
475   gboolean res = FALSE;
476   GType type;
477   GstDirectControlBindingConvertGValue convert;
478 
479   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
480   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
481   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
482   g_return_val_if_fail (values, FALSE);
483   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
484 
485   convert = self->convert_g_value;
486   type = G_PARAM_SPEC_VALUE_TYPE (_self->pspec);
487 
488   src_val = g_new0 (gdouble, n_values);
489   if ((res = gst_control_source_get_value_array (self->cs, timestamp,
490               interval, n_values, src_val))) {
491     for (i = 0; i < n_values; i++) {
492       /* we will only get NAN for sparse control sources, such as triggers */
493       if (!isnan (src_val[i])) {
494         g_value_init (&values[i], type);
495         convert (self, src_val[i], &values[i]);
496       } else {
497         GST_LOG ("no control value for property %s at index %d", _self->name,
498             i);
499       }
500     }
501   } else {
502     GST_LOG ("failed to get control value for property %s at ts %"
503         GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
504   }
505   g_free (src_val);
506   return res;
507 }
508 
509 /* functions */
510 
511 /**
512  * gst_direct_control_binding_new:
513  * @object: the object of the property
514  * @property_name: the property-name to attach the control source
515  * @cs: the control source
516  *
517  * Create a new control-binding that attaches the #GstControlSource to the
518  * #GObject property. It will map the control source range [0.0 ... 1.0] to
519  * the full target property range, and clip all values outside this range.
520  *
521  * Returns: (transfer floating): the new #GstDirectControlBinding
522  */
523 GstControlBinding *
gst_direct_control_binding_new(GstObject * object,const gchar * property_name,GstControlSource * cs)524 gst_direct_control_binding_new (GstObject * object, const gchar * property_name,
525     GstControlSource * cs)
526 {
527   return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING,
528       "object", object, "name", property_name, "control-source", cs, NULL);
529 }
530 
531 /**
532  * gst_direct_control_binding_new_absolute:
533  * @object: the object of the property
534  * @property_name: the property-name to attach the control source
535  * @cs: the control source
536  *
537  * Create a new control-binding that attaches the #GstControlSource to the
538  * #GObject property. It will directly map the control source values to the
539  * target property range without any transformations.
540  *
541  * Returns: (transfer floating): the new #GstDirectControlBinding
542  *
543  * Since: 1.6
544  */
545 GstControlBinding *
gst_direct_control_binding_new_absolute(GstObject * object,const gchar * property_name,GstControlSource * cs)546 gst_direct_control_binding_new_absolute (GstObject * object,
547     const gchar * property_name, GstControlSource * cs)
548 {
549   return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING,
550       "object", object, "name", property_name, "control-source", cs, "absolute",
551       TRUE, NULL);
552 }
553