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