2 * empathy-gst-video-src.c - Source for EmpathyGstVideoSrc
3 * Copyright (C) 2008 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
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.
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.
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
24 #include <gst/video/colorbalance.h>
26 #include <gst/interfaces/colorbalance.h>
29 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
30 #include <libempathy/empathy-debug.h>
32 #include "empathy-video-src.h"
34 G_DEFINE_TYPE(EmpathyGstVideoSrc, empathy_video_src, GST_TYPE_BIN)
36 /* Keep in sync with EmpathyGstVideoSrcChannel */
37 static const gchar *channel_names[NR_EMPATHY_GST_VIDEO_SRC_CHANNELS] = {
38 "contrast", "brightness", "gamma" };
47 static guint signals[LAST_SIGNAL] = {0};
50 /* private structure */
51 typedef struct _EmpathyGstVideoSrcPrivate EmpathyGstVideoSrcPrivate;
53 struct _EmpathyGstVideoSrcPrivate
55 gboolean dispose_has_run;
57 /* Element implementing a ColorBalance interface */
59 /* Elements for resolution and framerate adjustment */
60 GstElement *capsfilter;
61 GstElement *videorate;
67 #define EMPATHY_GST_VIDEO_SRC_GET_PRIVATE(o) \
68 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_VIDEO_SRC, \
69 EmpathyGstVideoSrcPrivate))
71 * empathy_gst_add_to_bin - create a new gst element, add to bin and link it.
72 * @bin - bin to add the new element to.
73 * @src - src element for the new element (may be NULL).
74 * @name - name of the factory for the new element
76 * Returns: The newly created element ot %NULL on failure
79 empathy_gst_add_to_bin (GstBin *bin,
81 const gchar *factoryname)
85 if ((ret = gst_element_factory_make (factoryname, NULL)) == NULL)
87 g_message ("Element factory \"%s\" not found.", factoryname);
91 if (!gst_bin_add (bin, ret))
93 g_warning ("Couldn't add \"%s\" to bin.", factoryname);
97 /* do not link if src == NULL, just exit here */
101 if (!gst_element_link (src, ret))
103 g_warning ("Failed to link \"%s\".", factoryname);
104 gst_bin_remove (bin, ret);
112 gst_object_unref (ret);
117 static GstPadProbeReturn
118 empathy_video_src_drop_eos (GstPad *pad,
119 GstPadProbeInfo *info,
122 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) == GST_EVENT_EOS)
123 return GST_PAD_PROBE_DROP;
125 return GST_PAD_PROBE_OK;
129 empathy_video_src_drop_eos (GstPad *pad, GstEvent *event, gpointer user_data)
131 return GST_EVENT_TYPE (event) != GST_EVENT_EOS;
136 empathy_video_src_init (EmpathyGstVideoSrc *obj)
138 EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (obj);
139 GstElement *element, *element_back;
144 /* allocate caps here, so we can update it by optional elements */
146 caps = gst_caps_new_simple ("video/x-raw",
148 caps = gst_caps_new_simple ("video/x-raw-yuv",
150 "width", G_TYPE_INT, 320,
151 "height", G_TYPE_INT, 240,
154 /* allocate any data required by the object here */
155 if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
156 NULL, "v4l2src")) == NULL)
157 g_error ("Couldn't add \"v4l2src\" (gst-plugins-good missing?)");
159 /* we need to save our source to priv->src */
162 /* Drop EOS events, so that our sinks don't get confused when we restart the
163 * source (triggering an EOS) */
164 src = gst_element_get_static_pad (element, "src");
167 gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
168 empathy_video_src_drop_eos, NULL, NULL);
170 gst_pad_add_event_probe (src, G_CALLBACK (empathy_video_src_drop_eos), NULL);
173 gst_object_unref (src);
175 /* videorate with the required properties optional as it needs a currently
176 * unreleased gst-plugins-base 0.10.36 */
177 element_back = element;
178 element = empathy_gst_add_to_bin (GST_BIN (obj), element, "videorate");
180 if (element != NULL && g_object_class_find_property (
181 G_OBJECT_GET_CLASS (element), "max-rate") != NULL)
183 priv->videorate = element;
184 g_object_set (G_OBJECT (element),
186 "average-period", GST_SECOND/2,
191 g_message ("videorate missing or doesn't have max-rate property, not"
192 "doing dynamic framerate changes (Needs gst-plugins-base >= 0.10.36)");
193 /* Refcount owned by the bin */
194 gst_bin_remove (GST_BIN (obj), element);
195 element = element_back;
198 gst_caps_set_simple (caps,
199 "framerate", GST_TYPE_FRACTION_RANGE, 1, 1, 30, 1,
202 str = gst_caps_to_string (caps);
203 DEBUG ("Current video src caps are : %s", str);
207 if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
208 element, "videoconvert")) == NULL)
209 g_error ("Failed to add \"videoconvert\" (gst-plugins-base missing?)");
211 if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
212 element, "ffmpegcolorspace")) == NULL)
213 g_error ("Failed to add \"ffmpegcolorspace\" (gst-plugins-base missing?)");
216 if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
217 element, "videoscale")) == NULL)
218 g_error ("Failed to add \"videoscale\", (gst-plugins-base missing?)");
220 if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
221 element, "capsfilter")) == NULL)
223 "Failed to add \"capsfilter\" (gstreamer core elements missing?)");
225 priv->capsfilter = element;
226 g_object_set (G_OBJECT (element), "caps", caps, NULL);
229 /* optionally add postproc_tmpnoise to improve the performance of encoders */
230 element_back = element;
231 if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
232 element, "postproc_tmpnoise")) == NULL)
234 g_message ("Failed to add \"postproc_tmpnoise\" (gst-ffmpeg missing?)");
235 element = element_back;
238 src = gst_element_get_static_pad (element, "src");
239 g_assert (src != NULL);
241 ghost = gst_ghost_pad_new ("src", src);
243 g_error ("Unable to create ghost pad for the videosrc");
245 if (!gst_element_add_pad (GST_ELEMENT (obj), ghost))
246 g_error ("pad with the same name already existed or "
247 "the pad already had another parent.");
249 gst_object_unref (G_OBJECT (src));
252 static void empathy_video_src_dispose (GObject *object);
253 static void empathy_video_src_finalize (GObject *object);
256 empathy_video_src_class_init (EmpathyGstVideoSrcClass *empathy_video_src_class)
258 GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_src_class);
260 g_type_class_add_private (empathy_video_src_class,
261 sizeof (EmpathyGstVideoSrcPrivate));
263 object_class->dispose = empathy_video_src_dispose;
264 object_class->finalize = empathy_video_src_finalize;
268 empathy_video_src_dispose (GObject *object)
270 EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
271 EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
273 if (priv->dispose_has_run)
276 priv->dispose_has_run = TRUE;
278 /* release any references held by the object here */
280 if (G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose)
281 G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose (object);
285 empathy_video_src_finalize (GObject *object)
287 //EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object);
288 //EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
290 /* free any data held directly by the object here */
292 G_OBJECT_CLASS (empathy_video_src_parent_class)->finalize (object);
296 empathy_video_src_new (void)
298 static gboolean registered = FALSE;
301 if (!gst_element_register (NULL, "empathyvideosrc",
302 GST_RANK_NONE, EMPATHY_TYPE_GST_VIDEO_SRC))
306 return gst_element_factory_make ("empathyvideosrc", NULL);
309 static GstColorBalance *
310 dup_color_balance (GstElement *src)
314 /* Find something supporting GstColorBalance */
315 color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE);
320 /* colorbalance is wrapped by GstImplementsInterface, we
321 * need to check if it is actually supported for this instance
322 * in its current state before trying to use it */
323 if (!GST_IS_COLOR_BALANCE (color))
325 g_object_unref (color);
329 return GST_COLOR_BALANCE (color);
333 empathy_video_src_set_channel (GstElement *src,
334 EmpathyGstVideoSrcChannel channel, guint percent)
336 GstColorBalance *balance;
337 const GList *channels;
340 balance = dup_color_balance (src);
344 channels = gst_color_balance_list_channels (balance);
346 for (l = (GList *) channels; l != NULL; l = g_list_next (l))
348 GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
350 if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
352 gst_color_balance_set_value (balance, c,
353 ((c->max_value - c->min_value) * percent)/100
359 g_object_unref (balance);
363 empathy_video_src_get_channel (GstElement *src,
364 EmpathyGstVideoSrcChannel channel)
366 GstColorBalance *balance;
367 const GList *channels;
371 balance = dup_color_balance (src);
375 channels = gst_color_balance_list_channels (balance);
377 for (l = (GList *) channels; l != NULL; l = g_list_next (l))
379 GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data);
381 if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0)
384 ((gst_color_balance_get_value (balance, c)
385 - c->min_value) * 100) /
386 (c->max_value - c->min_value);
392 g_object_unref (balance);
399 empathy_video_src_get_supported_channels (GstElement *src)
401 GstColorBalance *balance;
402 const GList *channels;
406 balance = dup_color_balance (src);
410 channels = gst_color_balance_list_channels (balance);
412 for (l = (GList *) channels; l != NULL; l = g_list_next (l))
414 GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL (l->data);
417 for (i = 0; i < NR_EMPATHY_GST_VIDEO_SRC_CHANNELS; i++)
419 if (g_ascii_strcasecmp (channel->label, channel_names[i]) == 0)
427 g_object_unref (balance);
434 empathy_video_src_change_device (EmpathyGstVideoSrc *self,
437 EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
440 gst_element_get_state (priv->src, &state, NULL, 0);
442 g_return_if_fail (state == GST_STATE_NULL);
444 g_object_set (priv->src, "device", device, NULL);
448 empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
450 EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
453 g_object_get (priv->src, "device", &device, NULL);
459 empathy_video_src_set_framerate (GstElement *src,
462 EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
466 g_object_set (G_OBJECT (priv->videorate), "max-rate", framerate, NULL);
471 empathy_video_src_set_resolution (GstElement *src,
475 EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (src);
477 GstPad *srcpad, *peer;
479 g_return_if_fail (priv->capsfilter != NULL);
481 gst_element_set_locked_state (priv->src, TRUE);
482 gst_element_set_state (priv->src, GST_STATE_NULL);
484 srcpad = gst_element_get_static_pad (priv->src, "src");
485 peer = gst_pad_get_peer (srcpad);
487 /* Keep a ref as removing it from the bin will loose our reference */
488 gst_object_ref (priv->src);
489 gst_bin_remove (GST_BIN (src), priv->src);
491 g_object_get (priv->capsfilter, "caps", &caps, NULL);
492 caps = gst_caps_make_writable (caps);
494 gst_caps_set_simple (caps,
495 "width", G_TYPE_INT, width,
496 "height", G_TYPE_INT, height,
499 g_object_set (priv->capsfilter, "caps", caps, NULL);
500 gst_caps_unref (caps);
502 gst_bin_add (GST_BIN (src), priv->src);
503 /* We as the bin own the source again, so drop the temporary ref */
504 gst_object_unref (priv->src);
506 gst_pad_link (srcpad, peer);
508 gst_element_set_locked_state (priv->src, FALSE);
509 gst_element_sync_state_with_parent (priv->src);
511 gst_object_unref (srcpad);
512 gst_object_unref (peer);