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