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