controller: allow different controlbindings
[platform/upstream/gstreamer.git] / libs / gst / controller / gstcontrolbindingargb.c
1 /* GStreamer
2  *
3  * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
4  *
5  * gstcontrolbindingargb.c: Attachment for multiple control sources to gargb
6  *                            properties
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 /**
24  * SECTION:gstcontrolbindingargb
25  * @short_description: attachment for control source sources to argb properties
26  *
27  * A value mapping object that attaches multiple control sources to a guint
28  * gobject properties representing a color.
29  */
30
31 #include <glib-object.h>
32 #include <gst/gst.h>
33
34 #include "gstcontrolbindingargb.h"
35
36 #include <math.h>
37
38 #define GST_CAT_DEFAULT control_binding_debug
39 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
40
41 static void gst_control_binding_argb_dispose (GObject * object);
42 static void gst_control_binding_argb_finalize (GObject * object);
43
44 static gboolean gst_control_binding_argb_sync_values (GstControlBinding * _self,
45     GstObject * object, GstClockTime timestamp, GstClockTime last_sync);
46 static GValue *gst_control_binding_argb_get_value (GstControlBinding * _self,
47     GstClockTime timestamp);
48 static gboolean gst_control_binding_argb_get_value_array (GstControlBinding *
49     _self, GstClockTime timestamp, GstClockTime interval, guint n_values,
50     GValue * values);
51
52 #define _do_init \
53   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolbindingargb", 0, \
54       "dynamic parameter control source attachment");
55
56 G_DEFINE_TYPE_WITH_CODE (GstControlBindingARGB, gst_control_binding_argb,
57     GST_TYPE_CONTROL_BINDING, _do_init);
58
59 static void
60 gst_control_binding_argb_class_init (GstControlBindingARGBClass * klass)
61 {
62   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
63   GstControlBindingClass *control_binding_class =
64       GST_CONTROL_BINDING_CLASS (klass);
65
66   gobject_class->dispose = gst_control_binding_argb_dispose;
67   gobject_class->finalize = gst_control_binding_argb_finalize;
68
69   control_binding_class->sync_values = gst_control_binding_argb_sync_values;
70   control_binding_class->get_value = gst_control_binding_argb_get_value;
71   control_binding_class->get_value_array =
72       gst_control_binding_argb_get_value_array;
73 }
74
75 static void
76 gst_control_binding_argb_init (GstControlBindingARGB * self)
77 {
78 }
79
80 static void
81 gst_control_binding_argb_dispose (GObject * object)
82 {
83   GstControlBindingARGB *self = GST_CONTROL_BINDING_ARGB (object);
84
85   if (self->cs_a)
86     gst_object_replace ((GstObject **) & self->cs_a, NULL);
87   if (self->cs_r)
88     gst_object_replace ((GstObject **) & self->cs_r, NULL);
89   if (self->cs_g)
90     gst_object_replace ((GstObject **) & self->cs_g, NULL);
91   if (self->cs_b)
92     gst_object_replace ((GstObject **) & self->cs_b, NULL);
93 }
94
95 static void
96 gst_control_binding_argb_finalize (GObject * object)
97 {
98   GstControlBindingARGB *self = GST_CONTROL_BINDING_ARGB (object);
99
100   g_value_unset (&self->cur_value);
101 }
102
103 static gboolean
104 gst_control_binding_argb_sync_values (GstControlBinding * _self,
105     GstObject * object, GstClockTime timestamp, GstClockTime last_sync)
106 {
107   GstControlBindingARGB *self = GST_CONTROL_BINDING_ARGB (_self);
108   gdouble src_val_a = 1.0, src_val_r = 0.0, src_val_g = 0.0, src_val_b = 0.0;
109   gboolean ret = TRUE;
110
111   g_return_val_if_fail (GST_IS_CONTROL_BINDING_ARGB (self), FALSE);
112
113   GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT,
114       _self->name, GST_TIME_ARGS (timestamp));
115
116   if (self->cs_a)
117     ret &= gst_control_source_get_value (self->cs_a, timestamp, &src_val_a);
118   if (self->cs_r)
119     ret &= gst_control_source_get_value (self->cs_r, timestamp, &src_val_r);
120   if (self->cs_g)
121     ret &= gst_control_source_get_value (self->cs_g, timestamp, &src_val_g);
122   if (self->cs_b)
123     ret &= gst_control_source_get_value (self->cs_b, timestamp, &src_val_b);
124   if (G_LIKELY (ret)) {
125     guint src_val = (((guint) (CLAMP (src_val_a, 0.0, 1.0) * 255)) << 24) |
126         (((guint) (CLAMP (src_val_r, 0.0, 1.0) * 255)) << 16) |
127         (((guint) (CLAMP (src_val_g, 0.0, 1.0) * 255)) << 8) |
128         ((guint) (CLAMP (src_val_b, 0.0, 1.0) * 255));
129     GST_LOG_OBJECT (object, "  new value 0x%08x", src_val);
130     /* always set the value for first time, but then only if it changed
131      * this should limit g_object_notify invocations.
132      * FIXME: can we detect negative playback rates?
133      */
134     if ((timestamp < last_sync) || (src_val != self->last_value)) {
135       GValue *dst_val = &self->cur_value;
136
137       g_value_set_uint (dst_val, src_val);
138       /* we can make this faster
139        * http://bugzilla.gnome.org/show_bug.cgi?id=536939
140        */
141       g_object_set_property ((GObject *) object, _self->name, dst_val);
142       self->last_value = src_val;
143     }
144   } else {
145     GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name);
146   }
147   return (ret);
148 }
149
150 static GValue *
151 gst_control_binding_argb_get_value (GstControlBinding * _self,
152     GstClockTime timestamp)
153 {
154   GstControlBindingARGB *self = GST_CONTROL_BINDING_ARGB (_self);
155   GValue *dst_val = NULL;
156   gdouble src_val_a = 1.0, src_val_r = 0.0, src_val_g = 0.0, src_val_b = 0.0;
157   gboolean ret = TRUE;
158
159   g_return_val_if_fail (GST_IS_CONTROL_BINDING_ARGB (self), NULL);
160   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
161
162   /* get current value via control source */
163   if (self->cs_a)
164     ret &= gst_control_source_get_value (self->cs_a, timestamp, &src_val_a);
165   if (self->cs_r)
166     ret &= gst_control_source_get_value (self->cs_r, timestamp, &src_val_r);
167   if (self->cs_g)
168     ret &= gst_control_source_get_value (self->cs_g, timestamp, &src_val_g);
169   if (self->cs_b)
170     ret &= gst_control_source_get_value (self->cs_b, timestamp, &src_val_b);
171   if (G_LIKELY (ret)) {
172     guint src_val = (((guint) (CLAMP (src_val_a, 0.0, 1.0) * 255)) << 24) |
173         (((guint) (CLAMP (src_val_r, 0.0, 1.0) * 255)) << 16) |
174         (((guint) (CLAMP (src_val_g, 0.0, 1.0) * 255)) << 8) |
175         ((guint) (CLAMP (src_val_b, 0.0, 1.0) * 255));
176     dst_val = g_new0 (GValue, 1);
177     g_value_init (dst_val, G_TYPE_UINT);
178     g_value_set_uint (dst_val, src_val);
179   } else {
180     GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT,
181         _self->name, GST_TIME_ARGS (timestamp));
182   }
183
184   return dst_val;
185 }
186
187 static gboolean
188 gst_control_binding_argb_get_value_array (GstControlBinding * _self,
189     GstClockTime timestamp, GstClockTime interval, guint n_values,
190     GValue * values)
191 {
192   GstControlBindingARGB *self = GST_CONTROL_BINDING_ARGB (_self);
193   gint i;
194   gdouble *src_val_a = NULL, *src_val_r = NULL, *src_val_g = NULL, *src_val_b =
195       NULL;
196   guint src_val;
197   gboolean ret = TRUE;
198
199   g_return_val_if_fail (GST_IS_CONTROL_BINDING_ARGB (self), FALSE);
200   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
201   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE);
202   g_return_val_if_fail (values, FALSE);
203
204   if (self->cs_a) {
205     src_val_a = g_new0 (gdouble, n_values);
206     ret &= gst_control_source_get_value_array (self->cs_a, timestamp,
207         interval, n_values, src_val_a);
208   }
209   if (self->cs_r) {
210     src_val_r = g_new0 (gdouble, n_values);
211     ret &= gst_control_source_get_value_array (self->cs_r, timestamp,
212         interval, n_values, src_val_r);
213   }
214   if (self->cs_g) {
215     src_val_g = g_new0 (gdouble, n_values);
216     ret &= gst_control_source_get_value_array (self->cs_g, timestamp,
217         interval, n_values, src_val_g);
218   }
219   if (self->cs_b) {
220     src_val_b = g_new0 (gdouble, n_values);
221     ret &= gst_control_source_get_value_array (self->cs_b, timestamp,
222         interval, n_values, src_val_b);
223   }
224   if (G_LIKELY (ret)) {
225     for (i = 0; i < n_values; i++) {
226       gdouble a = 1.0, r = 0.0, g = 0.0, b = 0.0;
227       if (src_val_a && !isnan (src_val_a[i]))
228         a = src_val_a[i];
229       if (src_val_r && !isnan (src_val_r[i]))
230         r = src_val_r[i];
231       if (src_val_g && !isnan (src_val_g[i]))
232         g = src_val_g[i];
233       if (src_val_b && !isnan (src_val_b[i]))
234         b = src_val_b[i];
235       src_val = (((guint) (CLAMP (a, 0.0, 1.0) * 255)) << 24) |
236           (((guint) (CLAMP (r, 0.0, 1.0) * 255)) << 16) |
237           (((guint) (CLAMP (g, 0.0, 1.0) * 255)) << 8) |
238           ((guint) (CLAMP (b, 0.0, 1.0) * 255));
239       g_value_init (&values[i], G_TYPE_UINT);
240       g_value_set_uint (&values[i], src_val);
241     }
242   } else {
243     GST_LOG ("failed to get control value for property %s at ts %"
244         GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
245   }
246   g_free (src_val_a);
247   g_free (src_val_r);
248   g_free (src_val_g);
249   g_free (src_val_b);
250   return ret;
251 }
252
253 /* mapping function */
254
255 /**
256  * gst_control_binding_argb_new:
257  * @object: the object of the property
258  * @property_name: the property-name to attach the control source
259  * @cs_a: the control source for the alpha channel
260  * @cs_r: the control source for the red channel
261  * @cs_g: the control source for the green channel
262  * @cs_b: the control source for the blue channel
263  *
264  * Create a new control-binding that attaches the given #GstControlSource to the
265  * #GObject property.
266  *
267  * Returns: the new #GstControlBindingARGB
268  */
269 GstControlBinding *
270 gst_control_binding_argb_new (GstObject * object, const gchar * property_name,
271     GstControlSource * cs_a, GstControlSource * cs_r, GstControlSource * cs_g,
272     GstControlSource * cs_b)
273 {
274   GstControlBindingARGB *self = NULL;
275   GParamSpec *pspec;
276
277   GST_INFO_OBJECT (object, "trying to put property '%s' under control",
278       property_name);
279
280   /* check if the object has a property of that name */
281   if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
282               property_name))) {
283     GST_DEBUG_OBJECT (object, "  psec->flags : 0x%08x", pspec->flags);
284
285     /* check if this param is witable && controlable && !construct-only */
286     g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE |
287                 GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) ==
288         (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL);
289
290     g_return_val_if_fail (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_UINT, NULL);
291
292     if ((self = (GstControlBindingARGB *)
293             g_object_newv (GST_TYPE_CONTROL_BINDING_ARGB, 0, NULL))) {
294       // move below to construct()
295       ((GstControlBinding *) self)->pspec = pspec;
296       ((GstControlBinding *) self)->name = pspec->name;
297       if (cs_a)
298         self->cs_a = gst_object_ref (cs_a);
299       if (cs_r)
300         self->cs_r = gst_object_ref (cs_r);
301       if (cs_g)
302         self->cs_g = gst_object_ref (cs_g);
303       if (cs_b)
304         self->cs_b = gst_object_ref (cs_b);
305
306       g_value_init (&self->cur_value, G_TYPE_UINT);
307     }
308   } else {
309     GST_WARNING_OBJECT (object, "class '%s' has no property '%s'",
310         G_OBJECT_TYPE_NAME (object), property_name);
311   }
312   return (GstControlBinding *) self;
313 }
314
315 /* functions */