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