Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / gst / videofilter / gstvideobalance.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  * Copyright (C) <2010> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * This file was (probably) generated from gstvideobalance.c,
24  * gstvideobalance.c,v 1.7 2003/11/08 02:48:59 dschleef Exp 
25  */
26
27 /**
28  * SECTION:element-videobalance
29  *
30  * Adjusts brightness, contrast, hue, saturation on a video stream.
31  *
32  * <refsect2>
33  * <title>Example launch line</title>
34  * |[
35  * gst-launch videotestsrc ! videobalance saturation=0.0 ! ffmpegcolorspace ! ximagesink
36  * ]| This pipeline converts the image to black and white by setting the
37  * saturation to 0.0.
38  * </refsect2>
39  *
40  * Last reviewed on 2010-04-18 (0.10.22)
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include <gst/math-compat.h>
48
49 #include "gstvideobalance.h"
50 #include <string.h>
51
52 #include <gst/video/colorbalance.h>
53
54 GST_DEBUG_CATEGORY_STATIC (videobalance_debug);
55 #define GST_CAT_DEFAULT videobalance_debug
56
57 /* GstVideoBalance properties */
58 #define DEFAULT_PROP_CONTRAST           1.0
59 #define DEFAULT_PROP_BRIGHTNESS         0.0
60 #define DEFAULT_PROP_HUE                0.0
61 #define DEFAULT_PROP_SATURATION         1.0
62
63 enum
64 {
65   PROP_0,
66   PROP_CONTRAST,
67   PROP_BRIGHTNESS,
68   PROP_HUE,
69   PROP_SATURATION
70 };
71
72 static GstStaticPadTemplate gst_video_balance_src_template =
73 GST_STATIC_PAD_TEMPLATE ("src",
74     GST_PAD_SRC,
75     GST_PAD_ALWAYS,
76     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
77             "ARGB, BGRA, ABGR, RGBA, Y444, xRGB, RGBx, "
78             "xBGR, BGRx, RGB, BGR, Y42B, YUY2, UYVY, YVYU, "
79             "I420, YV12, IYUV, Y41B }"))
80     );
81
82 static GstStaticPadTemplate gst_video_balance_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
84     GST_PAD_SINK,
85     GST_PAD_ALWAYS,
86     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
87             "ARGB, BGRA, ABGR, RGBA, Y444, xRGB, RGBx, "
88             "xBGR, BGRx, RGB, BGR, Y42B, YUY2, UYVY, YVYU, "
89             "I420, YV12, IYUV, Y41B }"))
90     );
91
92 static void gst_video_balance_colorbalance_init (GstColorBalanceInterface *
93     iface);
94
95 static void gst_video_balance_set_property (GObject * object, guint prop_id,
96     const GValue * value, GParamSpec * pspec);
97 static void gst_video_balance_get_property (GObject * object, guint prop_id,
98     GValue * value, GParamSpec * pspec);
99
100 #define gst_video_balance_parent_class parent_class
101 G_DEFINE_TYPE_WITH_CODE (GstVideoBalance, gst_video_balance,
102     GST_TYPE_VIDEO_FILTER,
103     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
104         gst_video_balance_colorbalance_init));
105
106 /*
107  * look-up tables (LUT).
108  */
109 static void
110 gst_video_balance_update_tables (GstVideoBalance * vb)
111 {
112   gint i, j;
113   gdouble y, u, v, hue_cos, hue_sin;
114
115   /* Y */
116   for (i = 0; i < 256; i++) {
117     y = 16 + ((i - 16) * vb->contrast + vb->brightness * 255);
118     if (y < 0)
119       y = 0;
120     else if (y > 255)
121       y = 255;
122     vb->tabley[i] = rint (y);
123   }
124
125   hue_cos = cos (G_PI * vb->hue);
126   hue_sin = sin (G_PI * vb->hue);
127
128   /* U/V lookup tables are 2D, since we need both U/V for each table
129    * separately. */
130   for (i = -128; i < 128; i++) {
131     for (j = -128; j < 128; j++) {
132       u = 128 + ((i * hue_cos + j * hue_sin) * vb->saturation);
133       v = 128 + ((-i * hue_sin + j * hue_cos) * vb->saturation);
134       if (u < 0)
135         u = 0;
136       else if (u > 255)
137         u = 255;
138       if (v < 0)
139         v = 0;
140       else if (v > 255)
141         v = 255;
142       vb->tableu[i + 128][j + 128] = rint (u);
143       vb->tablev[i + 128][j + 128] = rint (v);
144     }
145   }
146 }
147
148 static gboolean
149 gst_video_balance_is_passthrough (GstVideoBalance * videobalance)
150 {
151   return videobalance->contrast == 1.0 &&
152       videobalance->brightness == 0.0 &&
153       videobalance->hue == 0.0 && videobalance->saturation == 1.0;
154 }
155
156 static void
157 gst_video_balance_update_properties (GstVideoBalance * videobalance)
158 {
159   gboolean passthrough = gst_video_balance_is_passthrough (videobalance);
160   GstBaseTransform *base = GST_BASE_TRANSFORM (videobalance);
161
162   base->passthrough = passthrough;
163
164   if (!passthrough)
165     gst_video_balance_update_tables (videobalance);
166 }
167
168 static void
169 gst_video_balance_planar_yuv (GstVideoBalance * videobalance,
170     GstVideoFrame * frame)
171 {
172   gint x, y;
173   guint8 *ydata;
174   guint8 *udata, *vdata;
175   gint ystride, ustride, vstride;
176   gint width, height;
177   gint width2, height2;
178   guint8 *tabley = videobalance->tabley;
179   guint8 **tableu = videobalance->tableu;
180   guint8 **tablev = videobalance->tablev;
181
182   width = GST_VIDEO_FRAME_WIDTH (frame);
183   height = GST_VIDEO_FRAME_HEIGHT (frame);
184
185   ydata = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
186   ystride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
187
188   for (y = 0; y < height; y++) {
189     guint8 *yptr;
190
191     yptr = ydata + y * ystride;
192     for (x = 0; x < width; x++) {
193       *yptr = tabley[*yptr];
194       yptr++;
195     }
196   }
197
198   width2 = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1);
199   height2 = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1);
200
201   udata = GST_VIDEO_FRAME_PLANE_DATA (frame, 1);
202   vdata = GST_VIDEO_FRAME_PLANE_DATA (frame, 2);
203   ustride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 1);
204   vstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 2);
205
206   for (y = 0; y < height2; y++) {
207     guint8 *uptr, *vptr;
208     guint8 u1, v1;
209
210     uptr = udata + y * ustride;
211     vptr = vdata + y * vstride;
212
213     for (x = 0; x < width2; x++) {
214       u1 = *uptr;
215       v1 = *vptr;
216
217       *uptr++ = tableu[u1][v1];
218       *vptr++ = tablev[u1][v1];
219     }
220   }
221 }
222
223 static void
224 gst_video_balance_packed_yuv (GstVideoBalance * videobalance,
225     GstVideoFrame * frame)
226 {
227   gint x, y, stride;
228   guint8 *ydata, *udata, *vdata;
229   gint yoff, uoff, voff;
230   gint width, height;
231   gint width2, height2;
232   guint8 *tabley = videobalance->tabley;
233   guint8 **tableu = videobalance->tableu;
234   guint8 **tablev = videobalance->tablev;
235
236   width = GST_VIDEO_FRAME_WIDTH (frame);
237   height = GST_VIDEO_FRAME_HEIGHT (frame);
238
239   stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
240   ydata = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
241   yoff = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
242
243   for (y = 0; y < height; y++) {
244     guint8 *yptr;
245
246     yptr = ydata + y * stride;
247     for (x = 0; x < width; x++) {
248       *yptr = tabley[*yptr];
249       yptr += yoff;
250     }
251   }
252
253   width2 = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1);
254   height2 = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1);
255
256   udata = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
257   vdata = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
258   uoff = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 1);
259   voff = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 2);
260
261   for (y = 0; y < height2; y++) {
262     guint8 *uptr, *vptr;
263     guint8 u1, v1;
264
265     uptr = udata + y * stride;
266     vptr = vdata + y * stride;
267
268     for (x = 0; x < width2; x++) {
269       u1 = *uptr;
270       v1 = *vptr;
271
272       *uptr = tableu[u1][v1];
273       *vptr = tablev[u1][v1];
274
275       uptr += uoff;
276       vptr += voff;
277     }
278   }
279 }
280
281 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
282   298, 0, 409, -57068,
283   298, -100, -208, 34707,
284   298, 516, 0, -70870,
285 };
286
287 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
288   66, 129, 25, 4096,
289   -38, -74, 112, 32768,
290   112, -94, -18, 32768,
291 };
292
293 #define APPLY_MATRIX(m,o,v1,v2,v3) ((m[o*4] * v1 + m[o*4+1] * v2 + m[o*4+2] * v3 + m[o*4+3]) >> 8)
294
295 static void
296 gst_video_balance_packed_rgb (GstVideoBalance * videobalance,
297     GstVideoFrame * frame)
298 {
299   gint i, j, height;
300   gint width, stride, row_wrap;
301   gint pixel_stride;
302   guint8 *data;
303   gint offsets[3];
304   gint r, g, b;
305   gint y, u, v;
306   gint u_tmp, v_tmp;
307   guint8 *tabley = videobalance->tabley;
308   guint8 **tableu = videobalance->tableu;
309   guint8 **tablev = videobalance->tablev;
310
311   width = GST_VIDEO_FRAME_WIDTH (frame);
312   height = GST_VIDEO_FRAME_HEIGHT (frame);
313
314   offsets[0] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 0);
315   offsets[1] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 1);
316   offsets[2] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 2);
317
318   data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
319   stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
320
321   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
322   row_wrap = stride - pixel_stride * width;
323
324   for (i = 0; i < height; i++) {
325     for (j = 0; j < width; j++) {
326       r = data[offsets[0]];
327       g = data[offsets[1]];
328       b = data[offsets[2]];
329
330       y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
331       u_tmp = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
332       v_tmp = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
333
334       y = CLAMP (y, 0, 255);
335       u_tmp = CLAMP (u_tmp, 0, 255);
336       v_tmp = CLAMP (v_tmp, 0, 255);
337
338       y = tabley[y];
339       u = tableu[u_tmp][v_tmp];
340       v = tablev[u_tmp][v_tmp];
341
342       r = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 0, y, u, v);
343       g = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 1, y, u, v);
344       b = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 2, y, u, v);
345
346       data[offsets[0]] = CLAMP (r, 0, 255);
347       data[offsets[1]] = CLAMP (g, 0, 255);
348       data[offsets[2]] = CLAMP (b, 0, 255);
349       data += pixel_stride;
350     }
351     data += row_wrap;
352   }
353 }
354
355 /* get notified of caps and plug in the correct process function */
356 static gboolean
357 gst_video_balance_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
358     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
359 {
360   GstVideoBalance *videobalance = GST_VIDEO_BALANCE (vfilter);
361
362   GST_DEBUG_OBJECT (videobalance,
363       "in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps, outcaps);
364
365   videobalance->process = NULL;
366
367   switch (GST_VIDEO_INFO_FORMAT (in_info)) {
368     case GST_VIDEO_FORMAT_I420:
369     case GST_VIDEO_FORMAT_YV12:
370     case GST_VIDEO_FORMAT_Y41B:
371     case GST_VIDEO_FORMAT_Y42B:
372     case GST_VIDEO_FORMAT_Y444:
373       videobalance->process = gst_video_balance_planar_yuv;
374       break;
375     case GST_VIDEO_FORMAT_YUY2:
376     case GST_VIDEO_FORMAT_UYVY:
377     case GST_VIDEO_FORMAT_AYUV:
378     case GST_VIDEO_FORMAT_YVYU:
379       videobalance->process = gst_video_balance_packed_yuv;
380       break;
381     case GST_VIDEO_FORMAT_ARGB:
382     case GST_VIDEO_FORMAT_ABGR:
383     case GST_VIDEO_FORMAT_RGBA:
384     case GST_VIDEO_FORMAT_BGRA:
385     case GST_VIDEO_FORMAT_xRGB:
386     case GST_VIDEO_FORMAT_xBGR:
387     case GST_VIDEO_FORMAT_RGBx:
388     case GST_VIDEO_FORMAT_BGRx:
389     case GST_VIDEO_FORMAT_RGB:
390     case GST_VIDEO_FORMAT_BGR:
391       videobalance->process = gst_video_balance_packed_rgb;
392       break;
393     default:
394       goto unknown_format;
395       break;
396   }
397
398   return TRUE;
399
400   /* ERRORS */
401 unknown_format:
402   {
403     GST_ERROR_OBJECT (videobalance, "unknown format %" GST_PTR_FORMAT, incaps);
404     return FALSE;
405   }
406 }
407
408 static void
409 gst_video_balance_before_transform (GstBaseTransform * base, GstBuffer * buf)
410 {
411   GstVideoBalance *balance = GST_VIDEO_BALANCE (base);
412   GstClockTime timestamp, stream_time;
413
414   timestamp = GST_BUFFER_TIMESTAMP (buf);
415   stream_time =
416       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
417
418   GST_DEBUG_OBJECT (balance, "sync to %" GST_TIME_FORMAT,
419       GST_TIME_ARGS (timestamp));
420
421   if (GST_CLOCK_TIME_IS_VALID (stream_time))
422     gst_object_sync_values (GST_OBJECT (balance), stream_time);
423 }
424
425 static GstFlowReturn
426 gst_video_balance_transform_frame_ip (GstVideoFilter * vfilter,
427     GstVideoFrame * frame)
428 {
429   GstVideoBalance *videobalance = GST_VIDEO_BALANCE (vfilter);
430
431   if (!videobalance->process)
432     goto not_negotiated;
433
434   /* if no change is needed, we are done */
435   if (GST_BASE_TRANSFORM (vfilter)->passthrough)
436     goto done;
437
438   GST_OBJECT_LOCK (videobalance);
439   videobalance->process (videobalance, frame);
440   GST_OBJECT_UNLOCK (videobalance);
441
442 done:
443   return GST_FLOW_OK;
444
445   /* ERRORS */
446 not_negotiated:
447   {
448     GST_ERROR_OBJECT (videobalance, "Not negotiated yet");
449     return GST_FLOW_NOT_NEGOTIATED;
450   }
451 }
452
453 static void
454 gst_video_balance_finalize (GObject * object)
455 {
456   GList *channels = NULL;
457   GstVideoBalance *balance = GST_VIDEO_BALANCE (object);
458
459   g_free (balance->tableu[0]);
460
461   channels = balance->channels;
462   while (channels) {
463     GstColorBalanceChannel *channel = channels->data;
464
465     g_object_unref (channel);
466     channels->data = NULL;
467     channels = g_list_next (channels);
468   }
469
470   if (balance->channels)
471     g_list_free (balance->channels);
472
473   G_OBJECT_CLASS (parent_class)->finalize (object);
474 }
475
476 static void
477 gst_video_balance_class_init (GstVideoBalanceClass * klass)
478 {
479   GObjectClass *gobject_class = (GObjectClass *) klass;
480   GstElementClass *gstelement_class = (GstElementClass *) klass;
481   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
482   GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
483
484   GST_DEBUG_CATEGORY_INIT (videobalance_debug, "videobalance", 0,
485       "videobalance");
486
487   gobject_class->finalize = gst_video_balance_finalize;
488   gobject_class->set_property = gst_video_balance_set_property;
489   gobject_class->get_property = gst_video_balance_get_property;
490
491   g_object_class_install_property (gobject_class, PROP_CONTRAST,
492       g_param_spec_double ("contrast", "Contrast", "contrast",
493           0.0, 2.0, DEFAULT_PROP_CONTRAST,
494           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
495   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
496       g_param_spec_double ("brightness", "Brightness", "brightness", -1.0, 1.0,
497           DEFAULT_PROP_BRIGHTNESS,
498           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
499   g_object_class_install_property (gobject_class, PROP_HUE,
500       g_param_spec_double ("hue", "Hue", "hue", -1.0, 1.0, DEFAULT_PROP_HUE,
501           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
502   g_object_class_install_property (gobject_class, PROP_SATURATION,
503       g_param_spec_double ("saturation", "Saturation", "saturation", 0.0, 2.0,
504           DEFAULT_PROP_SATURATION,
505           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
506
507   gst_element_class_set_details_simple (gstelement_class, "Video balance",
508       "Filter/Effect/Video",
509       "Adjusts brightness, contrast, hue, saturation on a video stream",
510       "David Schleef <ds@schleef.org>");
511
512   gst_element_class_add_pad_template (gstelement_class,
513       gst_static_pad_template_get (&gst_video_balance_sink_template));
514   gst_element_class_add_pad_template (gstelement_class,
515       gst_static_pad_template_get (&gst_video_balance_src_template));
516
517   trans_class->before_transform =
518       GST_DEBUG_FUNCPTR (gst_video_balance_before_transform);
519
520   vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_balance_set_info);
521   vfilter_class->transform_frame_ip =
522       GST_DEBUG_FUNCPTR (gst_video_balance_transform_frame_ip);
523 }
524
525 static void
526 gst_video_balance_init (GstVideoBalance * videobalance)
527 {
528   const gchar *channels[4] = { "HUE", "SATURATION",
529     "BRIGHTNESS", "CONTRAST"
530   };
531   gint i;
532
533   /* Initialize propertiews */
534   videobalance->contrast = DEFAULT_PROP_CONTRAST;
535   videobalance->brightness = DEFAULT_PROP_BRIGHTNESS;
536   videobalance->hue = DEFAULT_PROP_HUE;
537   videobalance->saturation = DEFAULT_PROP_SATURATION;
538
539   videobalance->tableu[0] = g_new (guint8, 256 * 256 * 2);
540   for (i = 0; i < 256; i++) {
541     videobalance->tableu[i] =
542         videobalance->tableu[0] + i * 256 * sizeof (guint8);
543     videobalance->tablev[i] =
544         videobalance->tableu[0] + 256 * 256 * sizeof (guint8) +
545         i * 256 * sizeof (guint8);
546   }
547
548   gst_video_balance_update_properties (videobalance);
549
550   /* Generate the channels list */
551   for (i = 0; i < G_N_ELEMENTS (channels); i++) {
552     GstColorBalanceChannel *channel;
553
554     channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
555     channel->label = g_strdup (channels[i]);
556     channel->min_value = -1000;
557     channel->max_value = 1000;
558
559     videobalance->channels = g_list_append (videobalance->channels, channel);
560   }
561 }
562
563 static const GList *
564 gst_video_balance_colorbalance_list_channels (GstColorBalance * balance)
565 {
566   GstVideoBalance *videobalance = GST_VIDEO_BALANCE (balance);
567
568   g_return_val_if_fail (videobalance != NULL, NULL);
569   g_return_val_if_fail (GST_IS_VIDEO_BALANCE (videobalance), NULL);
570
571   return videobalance->channels;
572 }
573
574 static void
575 gst_video_balance_colorbalance_set_value (GstColorBalance * balance,
576     GstColorBalanceChannel * channel, gint value)
577 {
578   GstVideoBalance *vb = GST_VIDEO_BALANCE (balance);
579   gdouble new_val;
580   gboolean changed = FALSE;
581
582   g_return_if_fail (vb != NULL);
583   g_return_if_fail (GST_IS_VIDEO_BALANCE (vb));
584   g_return_if_fail (GST_IS_VIDEO_FILTER (vb));
585   g_return_if_fail (channel->label != NULL);
586
587   GST_BASE_TRANSFORM_LOCK (vb);
588   GST_OBJECT_LOCK (vb);
589   if (!g_ascii_strcasecmp (channel->label, "HUE")) {
590     new_val = (value + 1000.0) * 2.0 / 2000.0 - 1.0;
591     changed = new_val != vb->hue;
592     vb->hue = new_val;
593   } else if (!g_ascii_strcasecmp (channel->label, "SATURATION")) {
594     new_val = (value + 1000.0) * 2.0 / 2000.0;
595     changed = new_val != vb->saturation;
596     vb->saturation = new_val;
597   } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) {
598     new_val = (value + 1000.0) * 2.0 / 2000.0 - 1.0;
599     changed = new_val != vb->brightness;
600     vb->brightness = new_val;
601   } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) {
602     new_val = (value + 1000.0) * 2.0 / 2000.0;
603     changed = new_val != vb->contrast;
604     vb->contrast = new_val;
605   }
606
607   if (changed)
608     gst_video_balance_update_properties (vb);
609   GST_OBJECT_UNLOCK (vb);
610   GST_BASE_TRANSFORM_UNLOCK (vb);
611
612   if (changed) {
613     gst_color_balance_value_changed (balance, channel,
614         gst_color_balance_get_value (balance, channel));
615   }
616 }
617
618 static gint
619 gst_video_balance_colorbalance_get_value (GstColorBalance * balance,
620     GstColorBalanceChannel * channel)
621 {
622   GstVideoBalance *vb = GST_VIDEO_BALANCE (balance);
623   gint value = 0;
624
625   g_return_val_if_fail (vb != NULL, 0);
626   g_return_val_if_fail (GST_IS_VIDEO_BALANCE (vb), 0);
627   g_return_val_if_fail (channel->label != NULL, 0);
628
629   if (!g_ascii_strcasecmp (channel->label, "HUE")) {
630     value = (vb->hue + 1) * 2000.0 / 2.0 - 1000.0;
631   } else if (!g_ascii_strcasecmp (channel->label, "SATURATION")) {
632     value = vb->saturation * 2000.0 / 2.0 - 1000.0;
633   } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) {
634     value = (vb->brightness + 1) * 2000.0 / 2.0 - 1000.0;
635   } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) {
636     value = vb->contrast * 2000.0 / 2.0 - 1000.0;
637   }
638
639   return value;
640 }
641
642 static void
643 gst_video_balance_colorbalance_init (GstColorBalanceInterface * iface)
644 {
645   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_SOFTWARE;
646   iface->list_channels = gst_video_balance_colorbalance_list_channels;
647   iface->set_value = gst_video_balance_colorbalance_set_value;
648   iface->get_value = gst_video_balance_colorbalance_get_value;
649 }
650
651 static GstColorBalanceChannel *
652 gst_video_balance_find_channel (GstVideoBalance * balance, const gchar * label)
653 {
654   GList *l;
655
656   for (l = balance->channels; l; l = l->next) {
657     GstColorBalanceChannel *channel = l->data;
658
659     if (g_ascii_strcasecmp (channel->label, label) == 0)
660       return channel;
661   }
662   return NULL;
663 }
664
665 static void
666 gst_video_balance_set_property (GObject * object, guint prop_id,
667     const GValue * value, GParamSpec * pspec)
668 {
669   GstVideoBalance *balance = GST_VIDEO_BALANCE (object);
670   gdouble d;
671   const gchar *label = NULL;
672
673   GST_BASE_TRANSFORM_LOCK (balance);
674   GST_OBJECT_LOCK (balance);
675   switch (prop_id) {
676     case PROP_CONTRAST:
677       d = g_value_get_double (value);
678       GST_DEBUG_OBJECT (balance, "Changing contrast from %lf to %lf",
679           balance->contrast, d);
680       if (d != balance->contrast)
681         label = "CONTRAST";
682       balance->contrast = d;
683       break;
684     case PROP_BRIGHTNESS:
685       d = g_value_get_double (value);
686       GST_DEBUG_OBJECT (balance, "Changing brightness from %lf to %lf",
687           balance->brightness, d);
688       if (d != balance->brightness)
689         label = "BRIGHTNESS";
690       balance->brightness = d;
691       break;
692     case PROP_HUE:
693       d = g_value_get_double (value);
694       GST_DEBUG_OBJECT (balance, "Changing hue from %lf to %lf", balance->hue,
695           d);
696       if (d != balance->hue)
697         label = "HUE";
698       balance->hue = d;
699       break;
700     case PROP_SATURATION:
701       d = g_value_get_double (value);
702       GST_DEBUG_OBJECT (balance, "Changing saturation from %lf to %lf",
703           balance->saturation, d);
704       if (d != balance->saturation)
705         label = "SATURATION";
706       balance->saturation = d;
707       break;
708     default:
709       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
710       break;
711   }
712
713   gst_video_balance_update_properties (balance);
714   GST_OBJECT_UNLOCK (balance);
715   GST_BASE_TRANSFORM_UNLOCK (balance);
716
717   if (label) {
718     GstColorBalanceChannel *channel =
719         gst_video_balance_find_channel (balance, label);
720     gst_color_balance_value_changed (GST_COLOR_BALANCE (balance), channel,
721         gst_color_balance_get_value (GST_COLOR_BALANCE (balance), channel));
722   }
723 }
724
725 static void
726 gst_video_balance_get_property (GObject * object, guint prop_id, GValue * value,
727     GParamSpec * pspec)
728 {
729   GstVideoBalance *balance = GST_VIDEO_BALANCE (object);
730
731   switch (prop_id) {
732     case PROP_CONTRAST:
733       g_value_set_double (value, balance->contrast);
734       break;
735     case PROP_BRIGHTNESS:
736       g_value_set_double (value, balance->brightness);
737       break;
738     case PROP_HUE:
739       g_value_set_double (value, balance->hue);
740       break;
741     case PROP_SATURATION:
742       g_value_set_double (value, balance->saturation);
743       break;
744     default:
745       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
746       break;
747   }
748 }