]> git.0d.be Git - empathy.git/blob - src/empathy-video-src.c
Merge remote-tracking branch 'pochu/error-dialog'
[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   /* colorbalance is wrapped by GstImplementsInterface, we
260    * need to check if it is actually supported for this instance
261    * in its current state before trying to use it */
262   if (!GST_IS_COLOR_BALANCE (color))
263     {
264       g_object_unref (color);
265       return NULL;
266     }
267
268   return GST_COLOR_BALANCE (color);
269 }
270
271 void
272 empathy_video_src_set_channel (GstElement *src,
273   EmpathyGstVideoSrcChannel channel, guint percent)
274 {
275   GstColorBalance *balance;
276   const GList *channels;
277   GList *l;
278
279   balance = dup_color_balance (src);
280   if (balance == NULL)
281     return;
282
283   channels = gst_color_balance_list_channels (balance);
284
285   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
286     {
287       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
288
289       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
290         {
291           gst_color_balance_set_value (balance, c,
292             ((c->max_value - c->min_value) * percent)/100
293               + c->min_value);
294           break;
295         }
296     }
297
298   g_object_unref (balance);
299 }
300
301 guint
302 empathy_video_src_get_channel (GstElement *src,
303   EmpathyGstVideoSrcChannel channel)
304 {
305   GstColorBalance *balance;
306   const GList *channels;
307   GList *l;
308   guint percent = 0;
309
310   balance = dup_color_balance (src);
311   if (balance == NULL)
312     return percent;
313
314   channels = gst_color_balance_list_channels (balance);
315
316   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
317     {
318       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
319
320       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
321         {
322           percent =
323             ((gst_color_balance_get_value (balance, c)
324                 - c->min_value) * 100) /
325               (c->max_value - c->min_value);
326
327           break;
328         }
329     }
330
331   g_object_unref (balance);
332
333   return percent;
334 }
335
336
337 guint
338 empathy_video_src_get_supported_channels (GstElement *src)
339 {
340   GstColorBalance *balance;
341   const GList *channels;
342   GList *l;
343   guint result = 0;
344
345   balance = dup_color_balance (src);
346   if (balance == NULL)
347     goto out;
348
349   channels = gst_color_balance_list_channels (balance);
350
351   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
352     {
353       GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL (l->data);
354       int i;
355
356       for (i = 0; i < NR_EMPATHY_GST_VIDEO_SRC_CHANNELS; i++)
357         {
358           if (g_ascii_strcasecmp (channel->label, channel_names[i]) == 0)
359             {
360               result |= (1 << i);
361               break;
362             }
363         }
364     }
365
366   g_object_unref (balance);
367
368 out:
369   return result;
370 }
371
372 void
373 empathy_video_src_change_device (EmpathyGstVideoSrc *self,
374     const gchar *device)
375 {
376   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
377   GstState state;
378
379   gst_element_get_state (priv->src, &state, NULL, 0);
380
381   g_return_if_fail (state == GST_STATE_NULL);
382
383   g_object_set (priv->src, "device", device, NULL);
384 }
385
386 gchar *
387 empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
388 {
389   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
390   gchar *device;
391
392   g_object_get (priv->src, "device", &device, NULL);
393
394   return device;
395 }