• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   self->last_value = G_MAXDOUBLE;
219 }
220 
221 static GObject *
gst_direct_control_binding_constructor(GType type,guint n_construct_params,GObjectConstructParam * construct_params)222 gst_direct_control_binding_constructor (GType type, guint n_construct_params,
223     GObjectConstructParam * construct_params)
224 {
225   GstDirectControlBinding *self;
226 
227   self =
228       GST_DIRECT_CONTROL_BINDING (G_OBJECT_CLASS (parent_class)->constructor
229       (type, n_construct_params, construct_params));
230 
231   if (GST_CONTROL_BINDING_PSPEC (self)) {
232     GType type, base;
233 
234     base = type = G_PARAM_SPEC_VALUE_TYPE (GST_CONTROL_BINDING_PSPEC (self));
235     g_value_init (&self->cur_value, type);
236     while ((type = g_type_parent (type)))
237       base = type;
238 
239     GST_DEBUG ("  using type %s", g_type_name (base));
240 
241     /* select mapping function */
242 
243 #define SET_CONVERT_FUNCTION(type) \
244     if (self->ABI.abi.want_absolute) { \
245         self->convert_g_value = abs_convert_g_value_to_##type; \
246         self->convert_value = abs_convert_value_to_##type; \
247     } \
248     else { \
249         self->convert_g_value = convert_g_value_to_##type; \
250         self->convert_value = convert_value_to_##type; \
251     } \
252     self->byte_size = sizeof (g##type);
253 
254 
255     switch (base) {
256       case G_TYPE_INT:
257         SET_CONVERT_FUNCTION (int);
258         break;
259       case G_TYPE_UINT:
260         SET_CONVERT_FUNCTION (uint);
261         break;
262       case G_TYPE_LONG:
263         SET_CONVERT_FUNCTION (long);
264         break;
265       case G_TYPE_ULONG:
266         SET_CONVERT_FUNCTION (ulong);
267         break;
268       case G_TYPE_INT64:
269         SET_CONVERT_FUNCTION (int64);
270         break;
271       case G_TYPE_UINT64:
272         SET_CONVERT_FUNCTION (uint64);
273         break;
274       case G_TYPE_FLOAT:
275         SET_CONVERT_FUNCTION (float);
276         break;
277       case G_TYPE_DOUBLE:
278         SET_CONVERT_FUNCTION (double);
279         break;
280       case G_TYPE_BOOLEAN:
281         self->convert_g_value = convert_g_value_to_boolean;
282         self->convert_value = convert_value_to_boolean;
283         self->byte_size = sizeof (gboolean);
284         break;
285       case G_TYPE_ENUM:
286         self->convert_g_value = convert_g_value_to_enum;
287         self->convert_value = convert_value_to_enum;
288         self->byte_size = sizeof (gint);
289         break;
290       default:
291         GST_WARNING ("incomplete implementation for paramspec type '%s'",
292             G_PARAM_SPEC_TYPE_NAME (GST_CONTROL_BINDING_PSPEC (self)));
293         GST_CONTROL_BINDING_PSPEC (self) = NULL;
294         break;
295     }
296   }
297   return (GObject *) self;
298 }
299 
300 static void
gst_direct_control_binding_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)301 gst_direct_control_binding_set_property (GObject * object, guint prop_id,
302     const GValue * value, GParamSpec * pspec)
303 {
304   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
305 
306   switch (prop_id) {
307     case PROP_CS:
308       self->cs = g_value_dup_object (value);
309       break;
310     case PROP_ABSOLUTE:
311       self->ABI.abi.want_absolute = g_value_get_boolean (value);
312       break;
313     default:
314       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
315       break;
316   }
317 }
318 
319 static void
gst_direct_control_binding_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)320 gst_direct_control_binding_get_property (GObject * object, guint prop_id,
321     GValue * value, GParamSpec * pspec)
322 {
323   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
324 
325   switch (prop_id) {
326     case PROP_CS:
327       g_value_set_object (value, self->cs);
328       break;
329     case PROP_ABSOLUTE:
330       g_value_set_boolean (value, self->ABI.abi.want_absolute);
331       break;
332     default:
333       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
334       break;
335   }
336 }
337 
338 static void
gst_direct_control_binding_dispose(GObject * object)339 gst_direct_control_binding_dispose (GObject * object)
340 {
341   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
342 
343   if (self->cs)
344     gst_object_replace ((GstObject **) & self->cs, NULL);
345 
346   G_OBJECT_CLASS (parent_class)->dispose (object);
347 }
348 
349 static void
gst_direct_control_binding_finalize(GObject * object)350 gst_direct_control_binding_finalize (GObject * object)
351 {
352   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
353 
354   if (G_IS_VALUE (&self->cur_value))
355     g_value_unset (&self->cur_value);
356 
357   G_OBJECT_CLASS (parent_class)->finalize (object);
358 }
359 
360 static gboolean
gst_direct_control_binding_sync_values(GstControlBinding * _self,GstObject * object,GstClockTime timestamp,GstClockTime last_sync)361 gst_direct_control_binding_sync_values (GstControlBinding * _self,
362     GstObject * object, GstClockTime timestamp, GstClockTime last_sync)
363 {
364   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
365   gdouble src_val;
366   gboolean ret;
367 
368   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
369   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
370 
371   GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT,
372       _self->name, GST_TIME_ARGS (timestamp));
373 
374   ret = gst_control_source_get_value (self->cs, timestamp, &src_val);
375   if (G_LIKELY (ret)) {
376     GST_LOG_OBJECT (object, "  new value %lf", src_val);
377     /* always set the value for first time, but then only if it changed
378      * this should limit g_object_notify invocations.
379      * FIXME: can we detect negative playback rates?
380      */
381     if ((timestamp < last_sync) || (src_val != self->last_value)) {
382       GValue *dst_val = &self->cur_value;
383 
384       GST_LOG_OBJECT (object, "  mapping %s to value of type %s", _self->name,
385           G_VALUE_TYPE_NAME (dst_val));
386       /* run mapping function to convert gdouble to GValue */
387       self->convert_g_value (self, src_val, dst_val);
388       /* we can make this faster
389        * http://bugzilla.gnome.org/show_bug.cgi?id=536939
390        */
391       g_object_set_property ((GObject *) object, _self->name, dst_val);
392       self->last_value = src_val;
393     }
394   } else {
395     GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name);
396   }
397   return (ret);
398 }
399 
400 static GValue *
gst_direct_control_binding_get_value(GstControlBinding * _self,GstClockTime timestamp)401 gst_direct_control_binding_get_value (GstControlBinding * _self,
402     GstClockTime timestamp)
403 {
404   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
405   GValue *dst_val = NULL;
406   gdouble src_val;
407 
408   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), NULL);
409   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
410   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
411 
412   /* get current value via control source */
413   if (gst_control_source_get_value (self->cs, timestamp, &src_val)) {
414     dst_val = g_new0 (GValue, 1);
415     g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (_self->pspec));
416     self->convert_g_value (self, src_val, dst_val);
417   } else {
418     GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT,
419         _self->name, GST_TIME_ARGS (timestamp));
420   }
421 
422   return dst_val;
423 }
424 
425 static gboolean
gst_direct_control_binding_get_value_array(GstControlBinding * _self,GstClockTime timestamp,GstClockTime interval,guint n_values,gpointer values_)426 gst_direct_control_binding_get_value_array (GstControlBinding * _self,
427     GstClockTime timestamp, GstClockTime interval, guint n_values,
428     gpointer values_)
429 {
430   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
431   guint i;
432   gdouble *src_val;
433   gboolean res = FALSE;
434   GstDirectControlBindingConvertValue convert;
435   gint byte_size;
436   guint8 *values = (guint8 *) values_;
437 
438   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
439   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
440   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
441   g_return_val_if_fail (values, FALSE);
442   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
443 
444   convert = self->convert_value;
445   byte_size = self->byte_size;
446 
447   src_val = g_new0 (gdouble, n_values);
448   if ((res = gst_control_source_get_value_array (self->cs, timestamp,
449               interval, n_values, src_val))) {
450     for (i = 0; i < n_values; i++) {
451       /* we will only get NAN for sparse control sources, such as triggers */
452       if (!isnan (src_val[i])) {
453         convert (self, src_val[i], (gpointer) values);
454       } else {
455         GST_LOG ("no control value for property %s at index %d", _self->name,
456             i);
457       }
458       values += byte_size;
459     }
460   } else {
461     GST_LOG ("failed to get control value for property %s at ts %"
462         GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
463   }
464   g_free (src_val);
465   return res;
466 }
467 
468 static gboolean
gst_direct_control_binding_get_g_value_array(GstControlBinding * _self,GstClockTime timestamp,GstClockTime interval,guint n_values,GValue * values)469 gst_direct_control_binding_get_g_value_array (GstControlBinding * _self,
470     GstClockTime timestamp, GstClockTime interval, guint n_values,
471     GValue * values)
472 {
473   GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
474   guint i;
475   gdouble *src_val;
476   gboolean res = FALSE;
477   GType type;
478   GstDirectControlBindingConvertGValue convert;
479 
480   g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
481   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
482   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
483   g_return_val_if_fail (values, FALSE);
484   g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
485 
486   convert = self->convert_g_value;
487   type = G_PARAM_SPEC_VALUE_TYPE (_self->pspec);
488 
489   src_val = g_new0 (gdouble, n_values);
490   if ((res = gst_control_source_get_value_array (self->cs, timestamp,
491               interval, n_values, src_val))) {
492     for (i = 0; i < n_values; i++) {
493       /* we will only get NAN for sparse control sources, such as triggers */
494       if (!isnan (src_val[i])) {
495         g_value_init (&values[i], type);
496         convert (self, src_val[i], &values[i]);
497       } else {
498         GST_LOG ("no control value for property %s at index %d", _self->name,
499             i);
500       }
501     }
502   } else {
503     GST_LOG ("failed to get control value for property %s at ts %"
504         GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
505   }
506   g_free (src_val);
507   return res;
508 }
509 
510 /* functions */
511 
512 /**
513  * gst_direct_control_binding_new:
514  * @object: the object of the property
515  * @property_name: the property-name to attach the control source
516  * @cs: the control source
517  *
518  * Create a new control-binding that attaches the #GstControlSource to the
519  * #GObject property. It will map the control source range [0.0 ... 1.0] to
520  * the full target property range, and clip all values outside this range.
521  *
522  * Returns: (transfer floating): the new #GstDirectControlBinding
523  */
524 GstControlBinding *
gst_direct_control_binding_new(GstObject * object,const gchar * property_name,GstControlSource * cs)525 gst_direct_control_binding_new (GstObject * object, const gchar * property_name,
526     GstControlSource * cs)
527 {
528   return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING,
529       "object", object, "name", property_name, "control-source", cs, NULL);
530 }
531 
532 /**
533  * gst_direct_control_binding_new_absolute:
534  * @object: the object of the property
535  * @property_name: the property-name to attach the control source
536  * @cs: the control source
537  *
538  * Create a new control-binding that attaches the #GstControlSource to the
539  * #GObject property. It will directly map the control source values to the
540  * target property range without any transformations.
541  *
542  * Returns: (transfer floating): the new #GstDirectControlBinding
543  *
544  * Since: 1.6
545  */
546 GstControlBinding *
gst_direct_control_binding_new_absolute(GstObject * object,const gchar * property_name,GstControlSource * cs)547 gst_direct_control_binding_new_absolute (GstObject * object,
548     const gchar * property_name, GstControlSource * cs)
549 {
550   return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING,
551       "object", object, "name", property_name, "control-source", cs, "absolute",
552       TRUE, NULL);
553 }
554