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