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