]> git.0d.be Git - empathy.git/blob - src/empathy-video-src.c
video-src: factor out dup_color_balance()
[empathy.git] / src / empathy-video-src.c
1 /*
2  * empathy-gst-video-src.c - Source for EmpathyGstVideoSrc
3  * Copyright (C) 2008 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@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 Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 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  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <gst/interfaces/colorbalance.h>
26
27 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
28 #include <libempathy/empathy-debug.h>
29
30 #include "empathy-video-src.h"
31
32 G_DEFINE_TYPE(EmpathyGstVideoSrc, empathy_video_src, GST_TYPE_BIN)
33
34 /* Keep in sync with EmpathyGstVideoSrcChannel */
35 static const gchar *channel_names[NR_EMPATHY_GST_VIDEO_SRC_CHANNELS] = {
36   "contrast", "brightness", "gamma" };
37
38 /* signal enum */
39 #if 0
40 enum
41 {
42     LAST_SIGNAL
43 };
44
45 static guint signals[LAST_SIGNAL] = {0};
46 #endif
47
48 /* private structure */
49 typedef struct _EmpathyGstVideoSrcPrivate EmpathyGstVideoSrcPrivate;
50
51 struct _EmpathyGstVideoSrcPrivate
52 {
53   gboolean dispose_has_run;
54   GstElement *src;
55   /* Element implementing a ColorBalance interface */
56   GstElement *balance;
57 };
58
59 #define EMPATHY_GST_VIDEO_SRC_GET_PRIVATE(o) \
60   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_VIDEO_SRC, \
61     EmpathyGstVideoSrcPrivate))
62 /**
63  * empathy_gst_add_to_bin - create a new gst element, add to bin and link it.
64  * @bin - bin to add the new element to.
65  * @src - src element for the new element (may be NULL).
66  * @name - name of the factory for the new element
67  *
68  * Returns: The newly created element ot %NULL on failure
69  */
70 static GstElement *
71 empathy_gst_add_to_bin (GstBin *bin,
72   GstElement *src,
73   const gchar *factoryname)
74 {
75   GstElement *ret;
76
77   if ((ret = gst_element_factory_make (factoryname, NULL)) == NULL)
78   {
79     g_message ("Element factory \"%s\" not found.", factoryname);
80     goto error;
81   }
82
83   if (!gst_bin_add (bin, ret))
84   {
85     g_warning ("Couldn't add \"%s\" to bin.", factoryname);
86     goto error;
87   }
88
89   /* do not link if src == NULL, just exit here */
90   if (src == NULL)
91     return ret;
92
93   if (!gst_element_link (src, ret))
94   {
95     g_warning ("Failed to link \"%s\".", factoryname);
96     gst_bin_remove (bin, ret);
97     goto error;
98   }
99
100   return ret;
101
102 error:
103   if (ret != NULL)
104     gst_object_unref (ret);
105   return NULL;
106 }
107
108
109 static void
110 empathy_video_src_init (EmpathyGstVideoSrc *obj)
111 {
112   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (obj);
113   GstElement *element, *element_back;
114   GstPad *ghost, *src;
115   GstCaps *caps;
116   gchar *str;
117
118   /* allocate caps here, so we can update it by optional elements */
119   caps = gst_caps_new_simple ("video/x-raw-yuv",
120     "width", G_TYPE_INT, 320,
121     "height", G_TYPE_INT, 240,
122     NULL);
123
124   /* allocate any data required by the object here */
125   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
126       NULL, "v4l2src")) == NULL)
127     g_error ("Couldn't add \"v4l2src\" (gst-plugins-good missing?)");
128
129   /* we need to save our source to priv->src */
130   priv->src = element;
131
132   /* videomaxrate is optional as it's part of gst-plugins-bad. So don't
133    * fail if it doesn't exist. */
134   element_back = element;
135   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
136       element, "videomaxrate")) == NULL)
137     {
138       g_message ("Couldn't add \"videomaxrate\" (gst-plugins-bad missing?)");
139       element = element_back;
140     }
141   else
142     {
143       gst_caps_set_simple (caps,
144         "framerate", GST_TYPE_FRACTION, 15, 1,
145         NULL);
146     }
147
148   str = gst_caps_to_string (caps);
149   DEBUG ("Current video src caps are : %s", str);
150   g_free (str);
151
152   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
153       element, "ffmpegcolorspace")) == NULL)
154     g_error ("Failed to add \"ffmpegcolorspace\" (gst-plugins-base missing?)");
155
156   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
157       element, "videoscale")) == NULL)
158     g_error ("Failed to add \"videoscale\", (gst-plugins-base missing?)");
159
160   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
161       element, "capsfilter")) == NULL)
162     g_error (
163       "Failed to add \"capsfilter\" (gstreamer core elements missing?)");
164
165   g_object_set (G_OBJECT (element), "caps", caps, NULL);
166
167
168   /* optionally add postproc_tmpnoise to improve the performance of encoders */
169   element_back = element;
170   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
171       element, "postproc_tmpnoise")) == NULL)
172     {
173       g_message ("Failed to add \"postproc_tmpnoise\" (gst-ffmpeg missing?)");
174       element = element_back;
175     }
176
177   src = gst_element_get_static_pad (element, "src");
178   g_assert (src != NULL);
179
180   ghost = gst_ghost_pad_new ("src", src);
181   if (ghost == NULL)
182     g_error ("Unable to create ghost pad for the videosrc");
183
184   if (!gst_element_add_pad (GST_ELEMENT (obj), ghost))
185     g_error ("pad with the same name already existed or "
186             "the pad already had another parent.");
187
188   gst_object_unref (G_OBJECT (src));
189 }
190
191 static void empathy_video_src_dispose (GObject *object);
192 static void empathy_video_src_finalize (GObject *object);
193
194 static void
195 empathy_video_src_class_init (EmpathyGstVideoSrcClass *empathy_video_src_class)
196 {
197   GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_src_class);
198
199   g_type_class_add_private (empathy_video_src_class,
200     sizeof (EmpathyGstVideoSrcPrivate));
201
202   object_class->dispose = empathy_video_src_dispose;
203   object_class->finalize = empathy_video_src_finalize;
204 }
205
206 void
207 empathy_video_src_dispose (GObject *object)
208 {
209   EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
210   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
211
212   if (priv->dispose_has_run)
213     return;
214
215   priv->dispose_has_run = TRUE;
216
217   /* release any references held by the object here */
218
219   if (G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose)
220     G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose (object);
221 }
222
223 void
224 empathy_video_src_finalize (GObject *object)
225 {
226   //EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
227   //EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
228
229   /* free any data held directly by the object here */
230
231   G_OBJECT_CLASS (empathy_video_src_parent_class)->finalize (object);
232 }
233
234 GstElement *
235 empathy_video_src_new (void)
236 {
237   static gboolean registered = FALSE;
238
239   if (!registered) {
240     if (!gst_element_register (NULL, "empathyvideosrc",
241             GST_RANK_NONE, EMPATHY_TYPE_GST_VIDEO_SRC))
242       return NULL;
243     registered = TRUE;
244   }
245   return gst_element_factory_make ("empathyvideosrc", NULL);
246 }
247
248 static GstColorBalance *
249 dup_color_balance (GstElement *src)
250 {
251   GstElement *color;
252
253   /* Find something supporting GstColorBalance */
254   color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE);
255
256   if (color == NULL)
257     return NULL;
258
259   return GST_COLOR_BALANCE (color);
260 }
261
262 void
263 empathy_video_src_set_channel (GstElement *src,
264   EmpathyGstVideoSrcChannel channel, guint percent)
265 {
266   GstColorBalance *balance;
267   const GList *channels;
268   GList *l;
269
270   balance = dup_color_balance (src);
271   if (balance == NULL)
272     return;
273
274   channels = gst_color_balance_list_channels (balance);
275
276   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
277     {
278       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
279
280       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
281         {
282           gst_color_balance_set_value (balance, c,
283             ((c->max_value - c->min_value) * percent)/100
284               + c->min_value);
285           break;
286         }
287     }
288
289   g_object_unref (balance);
290 }
291
292 guint
293 empathy_video_src_get_channel (GstElement *src,
294   EmpathyGstVideoSrcChannel channel)
295 {
296   GstColorBalance *balance;
297   const GList *channels;
298   GList *l;
299   guint percent = 0;
300
301   balance = dup_color_balance (src);
302   if (balance == NULL)
303     return percent;
304
305   channels = gst_color_balance_list_channels (balance);
306
307   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
308     {
309       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
310
311       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
312         {
313           percent =
314             ((gst_color_balance_get_value (balance, c)
315                 - c->min_value) * 100) /
316               (c->max_value - c->min_value);
317
318           break;
319         }
320     }
321
322   g_object_unref (balance);
323
324   return percent;
325 }
326
327
328 guint
329 empathy_video_src_get_supported_channels (GstElement *src)
330 {
331   GstColorBalance *balance;
332   const GList *channels;
333   GList *l;
334   guint result = 0;
335
336   balance = dup_color_balance (src);
337   if (balance == NULL)
338     goto out;
339
340   channels = gst_color_balance_list_channels (balance);
341
342   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
343     {
344       GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL (l->data);
345       int i;
346
347       for (i = 0; i < NR_EMPATHY_GST_VIDEO_SRC_CHANNELS; i++)
348         {
349           if (g_ascii_strcasecmp (channel->label, channel_names[i]) == 0)
350             {
351               result |= (1 << i);
352               break;
353             }
354         }
355     }
356
357   g_object_unref (balance);
358
359 out:
360   return result;
361 }
362
363 void
364 empathy_video_src_change_device (EmpathyGstVideoSrc *self,
365     const gchar *device)
366 {
367   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
368   GstState state;
369
370   gst_element_get_state (priv->src, &state, NULL, 0);
371
372   g_return_if_fail (state == GST_STATE_NULL);
373
374   g_object_set (priv->src, "device", device, NULL);
375 }
376
377 gchar *
378 empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
379 {
380   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
381   gchar *device;
382
383   g_object_get (priv->src, "device", &device, NULL);
384
385   return device;
386 }