]> git.0d.be Git - empathy.git/blob - src/empathy-video-src.c
Updated Galician translations for docs
[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 #include "config.h"
22 #include "empathy-video-src.h"
23
24 #include <gst/video/colorbalance.h>
25
26 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
27 #include "empathy-debug.h"
28
29 G_DEFINE_TYPE(EmpathyGstVideoSrc, empathy_video_src, GST_TYPE_BIN)
30
31 /* Keep in sync with EmpathyGstVideoSrcChannel */
32 static const gchar *channel_names[NR_EMPATHY_GST_VIDEO_SRC_CHANNELS] = {
33   "contrast", "brightness", "gamma" };
34
35 /* signal enum */
36 #if 0
37 enum
38 {
39     LAST_SIGNAL
40 };
41
42 static guint signals[LAST_SIGNAL] = {0};
43 #endif
44
45 /* private structure */
46 typedef struct _EmpathyGstVideoSrcPrivate EmpathyGstVideoSrcPrivate;
47
48 struct _EmpathyGstVideoSrcPrivate
49 {
50   gboolean dispose_has_run;
51   GstElement *src;
52   /* Element implementing a ColorBalance interface */
53   GstElement *balance;
54   /* Elements for resolution and framerate adjustment */
55   GstElement *capsfilter;
56   GstElement *videorate;
57   guint width;
58   guint height;
59   guint framerate;
60 };
61
62 #define EMPATHY_GST_VIDEO_SRC_GET_PRIVATE(o) \
63   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_VIDEO_SRC, \
64     EmpathyGstVideoSrcPrivate))
65 /**
66  * empathy_gst_add_to_bin - create a new gst element, add to bin and link it.
67  * @bin - bin to add the new element to.
68  * @src - src element for the new element (may be NULL).
69  * @name - name of the factory for the new element
70  *
71  * Returns: The newly created element ot %NULL on failure
72  */
73 static GstElement *
74 empathy_gst_add_to_bin (GstBin *bin,
75   GstElement *src,
76   const gchar *factoryname)
77 {
78   GstElement *ret;
79
80   if ((ret = gst_element_factory_make (factoryname, NULL)) == NULL)
81   {
82     g_message ("Element factory \"%s\" not found.", factoryname);
83     goto error;
84   }
85
86   if (!gst_bin_add (bin, ret))
87   {
88     g_warning ("Couldn't add \"%s\" to bin.", factoryname);
89     goto error;
90   }
91
92   /* do not link if src == NULL, just exit here */
93   if (src == NULL)
94     return ret;
95
96   if (!gst_element_link (src, ret))
97   {
98     g_warning ("Failed to link \"%s\".", factoryname);
99     gst_bin_remove (bin, ret);
100     goto error;
101   }
102
103   return ret;
104
105 error:
106   if (ret != NULL)
107     gst_object_unref (ret);
108   return NULL;
109 }
110
111 static GstPadProbeReturn
112 empathy_video_src_drop_eos (GstPad *pad,
113   GstPadProbeInfo *info,
114   gpointer user_data)
115 {
116   if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) == GST_EVENT_EOS)
117     return GST_PAD_PROBE_DROP;
118
119   return GST_PAD_PROBE_OK;
120 }
121
122 static void
123 empathy_video_src_init (EmpathyGstVideoSrc *obj)
124 {
125   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (obj);
126   GstElement *element, *element_back;
127   GstPad *ghost, *src;
128   GstCaps *caps;
129   gchar *str;
130
131   /* allocate caps here, so we can update it by optional elements */
132   caps = gst_caps_new_simple ("video/x-raw",
133     "width", G_TYPE_INT, 320,
134     "height", G_TYPE_INT, 240,
135     NULL);
136
137   /* allocate any data required by the object here */
138   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
139       NULL, "v4l2src")) == NULL)
140     g_error ("Couldn't add \"v4l2src\" (gst-plugins-good missing?)");
141
142   /* we need to save our source to priv->src */
143   priv->src = element;
144
145   /* Drop EOS events, so that our sinks don't get confused when we restart the
146    * source (triggering an EOS) */
147   src = gst_element_get_static_pad (element, "src");
148
149   gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
150     empathy_video_src_drop_eos, NULL, NULL);
151
152   gst_object_unref (src);
153
154   /* videorate with the required properties optional as it needs a currently
155    * unreleased gst-plugins-base 0.10.36 */
156   element_back = element;
157   element = empathy_gst_add_to_bin (GST_BIN (obj), element, "videorate");
158
159   if (element != NULL && g_object_class_find_property (
160       G_OBJECT_GET_CLASS (element), "max-rate") != NULL)
161     {
162       priv->videorate = element;
163       g_object_set (G_OBJECT (element),
164         "drop-only", TRUE,
165         "average-period", GST_SECOND/2,
166         NULL);
167     }
168   else
169     {
170       g_message ("videorate missing or doesn't have max-rate property, not"
171         "doing dynamic framerate changes (Needs gst-plugins-base >= 0.10.36)");
172       /* Refcount owned by the bin */
173       gst_bin_remove (GST_BIN (obj), element);
174       element = element_back;
175     }
176
177   gst_caps_set_simple (caps,
178       "framerate", GST_TYPE_FRACTION_RANGE, 1, 1, 30, 1,
179       NULL);
180
181   str = gst_caps_to_string (caps);
182   DEBUG ("Current video src caps are : %s", str);
183   g_free (str);
184
185   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
186       element, "videoconvert")) == NULL)
187     g_error ("Failed to add \"videoconvert\" (gst-plugins-base missing?)");
188
189   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
190       element, "videoscale")) == NULL)
191     g_error ("Failed to add \"videoscale\", (gst-plugins-base missing?)");
192
193   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
194       element, "capsfilter")) == NULL)
195     g_error (
196       "Failed to add \"capsfilter\" (gstreamer core elements missing?)");
197
198   priv->capsfilter = element;
199   g_object_set (G_OBJECT (element), "caps", caps, NULL);
200
201
202   /* optionally add postproc_tmpnoise to improve the performance of encoders */
203   element_back = element;
204   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
205       element, "postproc_tmpnoise")) == NULL)
206     {
207       g_message ("Failed to add \"postproc_tmpnoise\" (gst-ffmpeg missing?)");
208       element = element_back;
209     }
210
211   src = gst_element_get_static_pad (element, "src");
212   g_assert (src != NULL);
213
214   ghost = gst_ghost_pad_new ("src", src);
215   if (ghost == NULL)
216     g_error ("Unable to create ghost pad for the videosrc");
217
218   if (!gst_element_add_pad (GST_ELEMENT (obj), ghost))
219     g_error ("pad with the same name already existed or "
220             "the pad already had another parent.");
221
222   gst_object_unref (G_OBJECT (src));
223 }
224
225 static void empathy_video_src_dispose (GObject *object);
226 static void empathy_video_src_finalize (GObject *object);
227
228 static void
229 empathy_video_src_class_init (EmpathyGstVideoSrcClass *empathy_video_src_class)
230 {
231   GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_src_class);
232
233   g_type_class_add_private (empathy_video_src_class,
234     sizeof (EmpathyGstVideoSrcPrivate));
235
236   object_class->dispose = empathy_video_src_dispose;
237   object_class->finalize = empathy_video_src_finalize;
238 }
239
240 void
241 empathy_video_src_dispose (GObject *object)
242 {
243   EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
244   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
245
246   if (priv->dispose_has_run)
247     return;
248
249   priv->dispose_has_run = TRUE;
250
251   /* release any references held by the object here */
252
253   if (G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose)
254     G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose (object);
255 }
256
257 void
258 empathy_video_src_finalize (GObject *object)
259 {
260   //EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
261   //EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
262
263   /* free any data held directly by the object here */
264
265   G_OBJECT_CLASS (empathy_video_src_parent_class)->finalize (object);
266 }
267
268 GstElement *
269 empathy_video_src_new (void)
270 {
271   static gboolean registered = FALSE;
272
273   if (!registered) {
274     if (!gst_element_register (NULL, "empathyvideosrc",
275             GST_RANK_NONE, EMPATHY_TYPE_GST_VIDEO_SRC))
276       return NULL;
277     registered = TRUE;
278   }
279   return gst_element_factory_make ("empathyvideosrc", NULL);
280 }
281
282 static GstColorBalance *
283 dup_color_balance (GstElement *src)
284 {
285   GstElement *color;
286
287   /* Find something supporting GstColorBalance */
288   color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE);
289
290   if (color == NULL)
291     return NULL;
292
293   /* colorbalance is wrapped by GstImplementsInterface, we
294    * need to check if it is actually supported for this instance
295    * in its current state before trying to use it */
296   if (!GST_IS_COLOR_BALANCE (color))
297     {
298       g_object_unref (color);
299       return NULL;
300     }
301
302   return GST_COLOR_BALANCE (color);
303 }
304
305 void
306 empathy_video_src_set_channel (GstElement *src,
307   EmpathyGstVideoSrcChannel channel, guint percent)
308 {
309   GstColorBalance *balance;
310   const GList *channels;
311   GList *l;
312
313   balance = dup_color_balance (src);
314   if (balance == NULL)
315     return;
316
317   channels = gst_color_balance_list_channels (balance);
318
319   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
320     {
321       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
322
323       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
324         {
325           gst_color_balance_set_value (balance, c,
326             ((c->max_value - c->min_value) * percent)/100
327               + c->min_value);
328           break;
329         }
330     }
331
332   g_object_unref (balance);
333 }
334
335 guint
336 empathy_video_src_get_channel (GstElement *src,
337   EmpathyGstVideoSrcChannel channel)
338 {
339   GstColorBalance *balance;
340   const GList *channels;
341   GList *l;
342   guint percent = 0;
343
344   balance = dup_color_balance (src);
345   if (balance == NULL)
346     return percent;
347
348   channels = gst_color_balance_list_channels (balance);
349
350   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
351     {
352       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
353
354       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
355         {
356           percent =
357             ((gst_color_balance_get_value (balance, c)
358                 - c->min_value) * 100) /
359               (c->max_value - c->min_value);
360
361           break;
362         }
363     }
364
365   g_object_unref (balance);
366
367   return percent;
368 }
369
370
371 guint
372 empathy_video_src_get_supported_channels (GstElement *src)
373 {
374   GstColorBalance *balance;
375   const GList *channels;
376   GList *l;
377   guint result = 0;
378
379   balance = dup_color_balance (src);
380   if (balance == NULL)
381     goto out;
382
383   channels = gst_color_balance_list_channels (balance);
384
385   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
386     {
387       GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL (l->data);
388       int i;
389
390       for (i = 0; i < NR_EMPATHY_GST_VIDEO_SRC_CHANNELS; i++)
391         {
392           if (g_ascii_strcasecmp (channel->label, channel_names[i]) == 0)
393             {
394               result |= (1 << i);
395               break;
396             }
397         }
398     }
399
400   g_object_unref (balance);
401
402 out:
403   return result;
404 }
405
406 void
407 empathy_video_src_change_device (EmpathyGstVideoSrc *self,
408     const gchar *device)
409 {
410   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
411   GstState state;
412
413   gst_element_get_state (priv->src, &state, NULL, 0);
414
415   g_return_if_fail (state == GST_STATE_NULL);
416
417   g_object_set (priv->src, "device", device, NULL);
418 }
419
420 gchar *
421 empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
422 {
423   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
424   gchar *device;
425
426   g_object_get (priv->src, "device", &device, NULL);
427
428   return device;
429 }
430
431 void
432 empathy_video_src_set_framerate (GstElement *src,
433     guint framerate)
434 {
435   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
436
437   if (priv->videorate)
438     {
439       g_object_set (G_OBJECT (priv->videorate), "max-rate", framerate, NULL);
440     }
441 }
442
443 void
444 empathy_video_src_set_resolution (GstElement *src,
445     guint width,
446     guint height)
447 {
448   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
449   GstCaps *caps;
450   GstPad *srcpad, *peer;
451
452   g_return_if_fail (priv->capsfilter != NULL);
453
454   gst_element_set_locked_state (priv->src, TRUE);
455   gst_element_set_state (priv->src, GST_STATE_NULL);
456
457   srcpad = gst_element_get_static_pad (priv->src, "src");
458   peer = gst_pad_get_peer (srcpad);
459
460   /* Keep a ref as removing it from the bin will loose our reference */
461   gst_object_ref (priv->src);
462   gst_bin_remove (GST_BIN (src), priv->src);
463
464   g_object_get (priv->capsfilter, "caps", &caps, NULL);
465   caps = gst_caps_make_writable (caps);
466
467   gst_caps_set_simple (caps,
468       "width", G_TYPE_INT, width,
469       "height", G_TYPE_INT, height,
470       NULL);
471
472   g_object_set (priv->capsfilter, "caps", caps, NULL);
473   gst_caps_unref (caps);
474
475   gst_bin_add (GST_BIN (src), priv->src);
476   /* We as the bin own the source again, so drop the temporary ref */
477   gst_object_unref (priv->src);
478
479   gst_pad_link (srcpad, peer);
480
481   gst_element_set_locked_state (priv->src, FALSE);
482   gst_element_sync_state_with_parent (priv->src);
483
484   gst_object_unref (srcpad);
485   gst_object_unref (peer);
486 }