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