]> git.0d.be Git - empathy.git/blob - src/empathy-video-src.c
Reset clock after changing video resolution
[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   /* Element for resolution and framerate adjustment */
58   GstElement *capsfilter;
59   guint width;
60   guint height;
61   guint framerate;
62 };
63
64 #define EMPATHY_GST_VIDEO_SRC_GET_PRIVATE(o) \
65   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_VIDEO_SRC, \
66     EmpathyGstVideoSrcPrivate))
67 /**
68  * empathy_gst_add_to_bin - create a new gst element, add to bin and link it.
69  * @bin - bin to add the new element to.
70  * @src - src element for the new element (may be NULL).
71  * @name - name of the factory for the new element
72  *
73  * Returns: The newly created element ot %NULL on failure
74  */
75 static GstElement *
76 empathy_gst_add_to_bin (GstBin *bin,
77   GstElement *src,
78   const gchar *factoryname)
79 {
80   GstElement *ret;
81
82   if ((ret = gst_element_factory_make (factoryname, NULL)) == NULL)
83   {
84     g_message ("Element factory \"%s\" not found.", factoryname);
85     goto error;
86   }
87
88   if (!gst_bin_add (bin, ret))
89   {
90     g_warning ("Couldn't add \"%s\" to bin.", factoryname);
91     goto error;
92   }
93
94   /* do not link if src == NULL, just exit here */
95   if (src == NULL)
96     return ret;
97
98   if (!gst_element_link (src, ret))
99   {
100     g_warning ("Failed to link \"%s\".", factoryname);
101     gst_bin_remove (bin, ret);
102     goto error;
103   }
104
105   return ret;
106
107 error:
108   if (ret != NULL)
109     gst_object_unref (ret);
110   return NULL;
111 }
112
113
114 static void
115 empathy_video_src_init (EmpathyGstVideoSrc *obj)
116 {
117   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (obj);
118   GstElement *element, *element_back;
119   GstPad *ghost, *src;
120   GstCaps *caps;
121   gchar *str;
122
123   /* allocate caps here, so we can update it by optional elements */
124   caps = gst_caps_new_simple ("video/x-raw-yuv",
125     "width", G_TYPE_INT, 320,
126     "height", G_TYPE_INT, 240,
127     NULL);
128
129   /* allocate any data required by the object here */
130   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
131       NULL, "v4l2src")) == NULL)
132     g_error ("Couldn't add \"v4l2src\" (gst-plugins-good missing?)");
133
134   /* we need to save our source to priv->src */
135   priv->src = element;
136
137   /* videomaxrate is optional as it's part of gst-plugins-bad. So don't
138    * fail if it doesn't exist. */
139   element_back = element;
140   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
141       element, "videomaxrate")) == NULL)
142     {
143       g_message ("Couldn't add \"videomaxrate\" (gst-plugins-bad missing?)");
144       element = element_back;
145     }
146   else
147     {
148       gst_caps_set_simple (caps,
149         "framerate", GST_TYPE_FRACTION, 15, 1,
150         NULL);
151     }
152
153   str = gst_caps_to_string (caps);
154   DEBUG ("Current video src caps are : %s", str);
155   g_free (str);
156
157   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
158       element, "ffmpegcolorspace")) == NULL)
159     g_error ("Failed to add \"ffmpegcolorspace\" (gst-plugins-base missing?)");
160
161   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
162       element, "videoscale")) == NULL)
163     g_error ("Failed to add \"videoscale\", (gst-plugins-base missing?)");
164
165   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
166       element, "capsfilter")) == NULL)
167     g_error (
168       "Failed to add \"capsfilter\" (gstreamer core elements missing?)");
169
170   priv->capsfilter = element;
171   g_object_set (G_OBJECT (element), "caps", caps, NULL);
172
173
174   /* optionally add postproc_tmpnoise to improve the performance of encoders */
175   element_back = element;
176   if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
177       element, "postproc_tmpnoise")) == NULL)
178     {
179       g_message ("Failed to add \"postproc_tmpnoise\" (gst-ffmpeg missing?)");
180       element = element_back;
181     }
182
183   src = gst_element_get_static_pad (element, "src");
184   g_assert (src != NULL);
185
186   ghost = gst_ghost_pad_new ("src", src);
187   if (ghost == NULL)
188     g_error ("Unable to create ghost pad for the videosrc");
189
190   if (!gst_element_add_pad (GST_ELEMENT (obj), ghost))
191     g_error ("pad with the same name already existed or "
192             "the pad already had another parent.");
193
194   gst_object_unref (G_OBJECT (src));
195 }
196
197 static void empathy_video_src_dispose (GObject *object);
198 static void empathy_video_src_finalize (GObject *object);
199
200 static void
201 empathy_video_src_class_init (EmpathyGstVideoSrcClass *empathy_video_src_class)
202 {
203   GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_src_class);
204
205   g_type_class_add_private (empathy_video_src_class,
206     sizeof (EmpathyGstVideoSrcPrivate));
207
208   object_class->dispose = empathy_video_src_dispose;
209   object_class->finalize = empathy_video_src_finalize;
210 }
211
212 void
213 empathy_video_src_dispose (GObject *object)
214 {
215   EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
216   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
217
218   if (priv->dispose_has_run)
219     return;
220
221   priv->dispose_has_run = TRUE;
222
223   /* release any references held by the object here */
224
225   if (G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose)
226     G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose (object);
227 }
228
229 void
230 empathy_video_src_finalize (GObject *object)
231 {
232   //EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
233   //EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
234
235   /* free any data held directly by the object here */
236
237   G_OBJECT_CLASS (empathy_video_src_parent_class)->finalize (object);
238 }
239
240 GstElement *
241 empathy_video_src_new (void)
242 {
243   static gboolean registered = FALSE;
244
245   if (!registered) {
246     if (!gst_element_register (NULL, "empathyvideosrc",
247             GST_RANK_NONE, EMPATHY_TYPE_GST_VIDEO_SRC))
248       return NULL;
249     registered = TRUE;
250   }
251   return gst_element_factory_make ("empathyvideosrc", NULL);
252 }
253
254 static GstColorBalance *
255 dup_color_balance (GstElement *src)
256 {
257   GstElement *color;
258
259   /* Find something supporting GstColorBalance */
260   color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE);
261
262   if (color == NULL)
263     return NULL;
264
265   /* colorbalance is wrapped by GstImplementsInterface, we
266    * need to check if it is actually supported for this instance
267    * in its current state before trying to use it */
268   if (!GST_IS_COLOR_BALANCE (color))
269     {
270       g_object_unref (color);
271       return NULL;
272     }
273
274   return GST_COLOR_BALANCE (color);
275 }
276
277 void
278 empathy_video_src_set_channel (GstElement *src,
279   EmpathyGstVideoSrcChannel channel, guint percent)
280 {
281   GstColorBalance *balance;
282   const GList *channels;
283   GList *l;
284
285   balance = dup_color_balance (src);
286   if (balance == NULL)
287     return;
288
289   channels = gst_color_balance_list_channels (balance);
290
291   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
292     {
293       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
294
295       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
296         {
297           gst_color_balance_set_value (balance, c,
298             ((c->max_value - c->min_value) * percent)/100
299               + c->min_value);
300           break;
301         }
302     }
303
304   g_object_unref (balance);
305 }
306
307 guint
308 empathy_video_src_get_channel (GstElement *src,
309   EmpathyGstVideoSrcChannel channel)
310 {
311   GstColorBalance *balance;
312   const GList *channels;
313   GList *l;
314   guint percent = 0;
315
316   balance = dup_color_balance (src);
317   if (balance == NULL)
318     return percent;
319
320   channels = gst_color_balance_list_channels (balance);
321
322   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
323     {
324       GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
325
326       if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
327         {
328           percent =
329             ((gst_color_balance_get_value (balance, c)
330                 - c->min_value) * 100) /
331               (c->max_value - c->min_value);
332
333           break;
334         }
335     }
336
337   g_object_unref (balance);
338
339   return percent;
340 }
341
342
343 guint
344 empathy_video_src_get_supported_channels (GstElement *src)
345 {
346   GstColorBalance *balance;
347   const GList *channels;
348   GList *l;
349   guint result = 0;
350
351   balance = dup_color_balance (src);
352   if (balance == NULL)
353     goto out;
354
355   channels = gst_color_balance_list_channels (balance);
356
357   for (l = (GList *) channels; l != NULL; l = g_list_next (l))
358     {
359       GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL (l->data);
360       int i;
361
362       for (i = 0; i < NR_EMPATHY_GST_VIDEO_SRC_CHANNELS; i++)
363         {
364           if (g_ascii_strcasecmp (channel->label, channel_names[i]) == 0)
365             {
366               result |= (1 << i);
367               break;
368             }
369         }
370     }
371
372   g_object_unref (balance);
373
374 out:
375   return result;
376 }
377
378 void
379 empathy_video_src_change_device (EmpathyGstVideoSrc *self,
380     const gchar *device)
381 {
382   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
383   GstState state;
384
385   gst_element_get_state (priv->src, &state, NULL, 0);
386
387   g_return_if_fail (state == GST_STATE_NULL);
388
389   g_object_set (priv->src, "device", device, NULL);
390 }
391
392 gchar *
393 empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
394 {
395   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
396   gchar *device;
397
398   g_object_get (priv->src, "device", &device, NULL);
399
400   return device;
401 }
402
403 void
404 empathy_video_src_set_framerate (GstElement *src,
405     guint framerate)
406 {
407   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
408   GstCaps *caps;
409
410   g_return_if_fail (priv->capsfilter != NULL);
411
412   g_object_get (priv->capsfilter, "caps", &caps, NULL);
413   caps = gst_caps_make_writable (caps);
414
415   gst_caps_set_simple (caps,
416       "framerate", GST_TYPE_FRACTION, framerate, 1,
417       NULL);
418
419   g_object_set (priv->capsfilter, "caps", caps, NULL);
420 }
421
422 void
423 empathy_video_src_set_resolution (GstElement *src,
424     guint width,
425     guint height)
426 {
427   EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
428   GstCaps *caps;
429   GstClock *gst_clock;
430
431   g_return_if_fail (priv->capsfilter != NULL);
432
433   gst_element_set_locked_state (priv->src, TRUE);
434   gst_element_set_state (priv->src, GST_STATE_NULL);
435
436   g_object_get (priv->capsfilter, "caps", &caps, NULL);
437   caps = gst_caps_make_writable (caps);
438
439   gst_caps_set_simple (caps,
440       "width", G_TYPE_INT, width,
441       "height", G_TYPE_INT, height,
442       NULL);
443
444   g_object_set (priv->capsfilter, "caps", caps, NULL);
445
446   /* Reset clock an base time, this is require for videotestsrc and hopefully
447    * has no side effect */
448   gst_clock = gst_element_get_clock (src);
449   gst_element_set_clock (priv->src, gst_clock);
450   gst_element_set_base_time (priv->src, gst_element_get_base_time (src));
451   gst_object_unref (gst_clock);
452
453   gst_element_set_locked_state (priv->src, FALSE);
454   gst_element_sync_state_with_parent (priv->src);
455 }