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