]> git.0d.be Git - empathy.git/blob - src/empathy-video-src.c
8c9c7501548c6f6182d8845a72981717b58e5998
[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 #include <gst/interfaces/colorbalance.h>
27
28 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
29 #include <libempathy/empathy-debug.h>
30
31 #include "empathy-video-src.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 static gboolean
116 empathy_video_src_drop_eos (GstPad *pad, GstEvent *event, gpointer user_data)
117 {
118   return GST_EVENT_TYPE (event) != GST_EVENT_EOS;
119 }
120
121 static void
122 empathy_video_src_init (EmpathyGstVideoSrc *obj)
123 {
124   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (obj);
125   GstElement *element, *element_back;
126   GstPad *ghost, *src;
127   GstCaps *caps;
128   gchar *str;
129
130   /* allocate caps here, so we can update it by optional elements */
131   caps = gst_caps_new_simple ("video/x-raw-yuv",
132     "width", G_TYPE_INT, 320,
133     "height", G_TYPE_INT, 240,
134     NULL);
135
136   /* allocate any data required by the object here */
137   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
138       NULL, "v4l2src")) == NULL)
139     g_error ("Couldn't add \"v4l2src\" (gst-plugins-good missing?)");
140
141   /* we need to save our source to priv->src */
142   priv->src = element;
143
144   /* Drop EOS events, so that our sinks don't get confused when we restart the
145    * source (triggering an EOS) */
146   src = gst_element_get_static_pad (element, "src");
147   gst_pad_add_event_probe (src, G_CALLBACK (empathy_video_src_drop_eos), NULL);
148   gst_object_unref (src);
149
150   /* videorate with the required properties optional as it needs a currently
151    * unreleased gst-plugins-base 0.10.36 */
152   element_back = element;
153   element = empathy_gst_add_to_bin (GST_BIN (obj), element, "videorate");
154
155   if (element != NULL && g_object_class_find_property (
156       G_OBJECT_GET_CLASS (element), "max-rate") != NULL)
157     {
158       priv->videorate = element;
159       g_object_set (G_OBJECT (element),
160         "drop-only", TRUE,
161         "average-period", GST_SECOND/2,
162         NULL);
163     }
164   else
165     {
166       g_message ("videorate missing or doesn't have max-rate property, not"
167         "doing dynamic framerate changes (Needs gst-plugins-base >= 0.10.36)");
168       /* Refcount owned by the bin */
169       gst_bin_remove (GST_BIN (obj), element);
170       element = element_back;
171     }
172
173   gst_caps_set_simple (caps,
174       "framerate", GST_TYPE_FRACTION_RANGE, 1, 1, 30, 1,
175       NULL);
176
177   str = gst_caps_to_string (caps);
178   DEBUG ("Current video src caps are : %s", str);
179   g_free (str);
180
181   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
182       element, "ffmpegcolorspace")) == NULL)
183     g_error ("Failed to add \"ffmpegcolorspace\" (gst-plugins-base missing?)");
184
185   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
186       element, "videoscale")) == NULL)
187     g_error ("Failed to add \"videoscale\", (gst-plugins-base missing?)");
188
189   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
190       element, "capsfilter")) == NULL)
191     g_error (
192       "Failed to add \"capsfilter\" (gstreamer core elements missing?)");
193
194   priv->capsfilter = element;
195   g_object_set (G_OBJECT (element), "caps", caps, NULL);
196
197
198   /* optionally add postproc_tmpnoise to improve the performance of encoders */
199   element_back = element;
200   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
201       element, "postproc_tmpnoise")) == NULL)
202     {
203       g_message ("Failed to add \"postproc_tmpnoise\" (gst-ffmpeg missing?)");
204       element = element_back;
205     }
206
207   src = gst_element_get_static_pad (element, "src");
208   g_assert (src != NULL);
209
210   ghost = gst_ghost_pad_new ("src", src);
211   if (ghost == NULL)
212     g_error ("Unable to create ghost pad for the videosrc");
213
214   if (!gst_element_add_pad (GST_ELEMENT (obj), ghost))
215     g_error ("pad with the same name already existed or "
216             "the pad already had another parent.");
217
218   gst_object_unref (G_OBJECT (src));
219 }
220
221 static void empathy_video_src_dispose (GObject *object);
222 static void empathy_video_src_finalize (GObject *object);
223
224 static void
225 empathy_video_src_class_init (EmpathyGstVideoSrcClass *empathy_video_src_class)
226 {
227   GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_src_class);
228
229   g_type_class_add_private (empathy_video_src_class,
230     sizeof (EmpathyGstVideoSrcPrivate));
231
232   object_class->dispose = empathy_video_src_dispose;
233   object_class->finalize = empathy_video_src_finalize;
234 }
235
236 void
237 empathy_video_src_dispose (GObject *object)
238 {
239   EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
240   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
241
242   if (priv->dispose_has_run)
243     return;
244
245   priv->dispose_has_run = TRUE;
246
247   /* release any references held by the object here */
248
249   if (G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose)
250     G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose (object);
251 }
252
253 void
254 empathy_video_src_finalize (GObject *object)
255 {
256   //EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
257   //EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
258
259   /* free any data held directly by the object here */
260
261   G_OBJECT_CLASS (empathy_video_src_parent_class)->finalize (object);
262 }
263
264 GstElement *
265 empathy_video_src_new (void)
266 {
267   static gboolean registered = FALSE;
268
269   if (!registered) {
270     if (!gst_element_register (NULL, "empathyvideosrc",
271             GST_RANK_NONE, EMPATHY_TYPE_GST_VIDEO_SRC))
272       return NULL;
273     registered = TRUE;
274   }
275   return gst_element_factory_make ("empathyvideosrc", NULL);
276 }
277
278 static GstColorBalance *
279 dup_color_balance (GstElement *src)
280 {
281   GstElement *color;
282
283   /* Find something supporting GstColorBalance */
284   color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE);
285
286   if (color == NULL)
287     return NULL;
288
289   /* colorbalance is wrapped by GstImplementsInterface, we
290    * need to check if it is actually supported for this instance
291    * in its current state before trying to use it */
292   if (!GST_IS_COLOR_BALANCE (color))
293     {
294       g_object_unref (color);
295       return NULL;
296     }
297
298   return GST_COLOR_BALANCE (color);
299 }
300
301 void
302 empathy_video_src_set_channel (GstElement *src,
303   EmpathyGstVideoSrcChannel channel, guint percent)
304 {
305   GstColorBalance *balance;
306   const GList *channels;
307   GList *l;
308
309   balance = dup_color_balance (src);
310   if (balance == NULL)
311     return;
312
313   channels = gst_color_balance_list_channels (balance);
314
315   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
316     {
317       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
318
319       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
320         {
321           gst_color_balance_set_value (balance, c,
322             ((c->max_value - c->min_value) * percent)/100
323               + c->min_value);
324           break;
325         }
326     }
327
328   g_object_unref (balance);
329 }
330
331 guint
332 empathy_video_src_get_channel (GstElement *src,
333   EmpathyGstVideoSrcChannel channel)
334 {
335   GstColorBalance *balance;
336   const GList *channels;
337   GList *l;
338   guint percent = 0;
339
340   balance = dup_color_balance (src);
341   if (balance == NULL)
342     return percent;
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           percent =
353             ((gst_color_balance_get_value (balance, c)
354                 - c->min_value) * 100) /
355               (c->max_value - c->min_value);
356
357           break;
358         }
359     }
360
361   g_object_unref (balance);
362
363   return percent;
364 }
365
366
367 guint
368 empathy_video_src_get_supported_channels (GstElement *src)
369 {
370   GstColorBalance *balance;
371   const GList *channels;
372   GList *l;
373   guint result = 0;
374
375   balance = dup_color_balance (src);
376   if (balance == NULL)
377     goto out;
378
379   channels = gst_color_balance_list_channels (balance);
380
381   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
382     {
383       GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL (l->data);
384       int i;
385
386       for (i = 0; i < NR_EMPATHY_GST_VIDEO_SRC_CHANNELS; i++)
387         {
388           if (g_ascii_strcasecmp (channel->label, channel_names[i]) == 0)
389             {
390               result |= (1 << i);
391               break;
392             }
393         }
394     }
395
396   g_object_unref (balance);
397
398 out:
399   return result;
400 }
401
402 void
403 empathy_video_src_change_device (EmpathyGstVideoSrc *self,
404     const gchar *device)
405 {
406   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
407   GstState state;
408
409   gst_element_get_state (priv->src, &state, NULL, 0);
410
411   g_return_if_fail (state == GST_STATE_NULL);
412
413   g_object_set (priv->src, "device", device, NULL);
414 }
415
416 gchar *
417 empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
418 {
419   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
420   gchar *device;
421
422   g_object_get (priv->src, "device", &device, NULL);
423
424   return device;
425 }
426
427 void
428 empathy_video_src_set_framerate (GstElement *src,
429     guint framerate)
430 {
431   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
432
433   if (priv->videorate)
434     {
435       g_object_set (G_OBJECT (priv->videorate), "max-rate", framerate, NULL);
436     }
437 }
438
439 void
440 empathy_video_src_set_resolution (GstElement *src,
441     guint width,
442     guint height)
443 {
444   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
445   GstCaps *caps;
446   GstPad *srcpad, *peer;
447
448   g_return_if_fail (priv->capsfilter != NULL);
449
450   gst_element_set_locked_state (priv->src, TRUE);
451   gst_element_set_state (priv->src, GST_STATE_NULL);
452
453   srcpad = gst_element_get_static_pad (priv->src, "src");
454   peer = gst_pad_get_peer (srcpad);
455
456   /* Keep a ref as removing it from the bin will loose our reference */
457   gst_object_ref (priv->src);
458   gst_bin_remove (GST_BIN (src), priv->src);
459
460   g_object_get (priv->capsfilter, "caps", &caps, NULL);
461   caps = gst_caps_make_writable (caps);
462
463   gst_caps_set_simple (caps,
464       "width", G_TYPE_INT, width,
465       "height", G_TYPE_INT, height,
466       NULL);
467
468   g_object_set (priv->capsfilter, "caps", caps, NULL);
469   gst_caps_unref (caps);
470
471   gst_bin_add (GST_BIN (src), priv->src);
472   /* We as the bin own the source again, so drop the temporary ref */
473   gst_object_unref (priv->src);
474
475   gst_pad_link (srcpad, peer);
476
477   gst_element_set_locked_state (priv->src, FALSE);
478   gst_element_sync_state_with_parent (priv->src);
479
480   gst_object_unref (srcpad);
481   gst_object_unref (peer);
482 }