diff options
author | Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> | 2011-02-21 18:59:56 +0800 |
---|---|---|
committer | Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> | 2011-02-21 19:11:40 +0800 |
commit | f80ac5a563fe7e7b88f7c3f3b737907792dae355 (patch) | |
tree | 83cdf23e6746f59efd845837b2be41ffe38463c2 /src | |
parent | a11f7c047fbc1e02a28d5c8f4366e8f2d3e1e423 (diff) | |
download | gsoc2013-empathy-f80ac5a563fe7e7b88f7c3f3b737907792dae355.tar gsoc2013-empathy-f80ac5a563fe7e7b88f7c3f3b737907792dae355.tar.gz gsoc2013-empathy-f80ac5a563fe7e7b88f7c3f3b737907792dae355.tar.bz2 gsoc2013-empathy-f80ac5a563fe7e7b88f7c3f3b737907792dae355.tar.lz gsoc2013-empathy-f80ac5a563fe7e7b88f7c3f3b737907792dae355.tar.xz gsoc2013-empathy-f80ac5a563fe7e7b88f7c3f3b737907792dae355.tar.zst gsoc2013-empathy-f80ac5a563fe7e7b88f7c3f3b737907792dae355.zip |
Move farsight-using sources to src
And only build them for empathy-av, since they use farsight.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 8 | ||||
-rw-r--r-- | src/empathy-audio-sink.c | 235 | ||||
-rw-r--r-- | src/empathy-audio-sink.h | 67 | ||||
-rw-r--r-- | src/empathy-audio-src.c | 387 | ||||
-rw-r--r-- | src/empathy-audio-src.h | 66 | ||||
-rw-r--r-- | src/empathy-streamed-media-window.c | 8 | ||||
-rw-r--r-- | src/empathy-video-src.c | 360 | ||||
-rw-r--r-- | src/empathy-video-src.h | 86 | ||||
-rw-r--r-- | src/empathy-video-widget.c | 487 | ||||
-rw-r--r-- | src/empathy-video-widget.h | 72 |
10 files changed, 1772 insertions, 4 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 86aff82d7..4b1b4c416 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -105,6 +105,10 @@ empathy_debugger_SOURCES = \ empathy_av_SOURCES = \ empathy-av.c \ + empathy-audio-sink.c \ + empathy-audio-sink.h \ + empathy-audio-src.c \ + empathy-audio-src.h \ empathy-streamed-media-factory.c \ empathy-streamed-media-factory.h \ empathy-streamed-media-handler.c \ @@ -113,6 +117,10 @@ empathy_av_SOURCES = \ empathy-streamed-media-window-fullscreen.h \ empathy-streamed-media-window.c \ empathy-streamed-media-window.h \ + empathy-video-src.c \ + empathy-video-src.h \ + empathy-video-widget.c \ + empathy-video-widget.h \ ev-sidebar.c \ ev-sidebar.h \ $(NULL) diff --git a/src/empathy-audio-sink.c b/src/empathy-audio-sink.c new file mode 100644 index 000000000..1d2169593 --- /dev/null +++ b/src/empathy-audio-sink.c @@ -0,0 +1,235 @@ +/* + * empathy-gst-audio-sink.c - Source for EmpathyGstAudioSink + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <gst/farsight/fs-element-added-notifier.h> + +#include "empathy-audio-sink.h" + + +G_DEFINE_TYPE(EmpathyGstAudioSink, empathy_audio_sink, GST_TYPE_BIN) + +/* signal enum */ +#if 0 +enum +{ + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; +#endif + +enum { + PROP_VOLUME = 1, +}; + +/* private structure */ +typedef struct _EmpathyGstAudioSinkPrivate EmpathyGstAudioSinkPrivate; + +struct _EmpathyGstAudioSinkPrivate +{ + gboolean dispose_has_run; + GstElement *sink; + GstElement *volume; + FsElementAddedNotifier *notifier; +}; + +#define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SINK, \ + EmpathyGstAudioSinkPrivate)) + +static void +empathy_audio_sink_element_added_cb (FsElementAddedNotifier *notifier, + GstBin *bin, GstElement *element, EmpathyGstAudioSink *self) +{ + EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self); + + if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume")) + { + gdouble volume; + + volume = empathy_audio_sink_get_volume (self); + empathy_audio_sink_set_volume (self, 1.0); + + if (priv->volume != NULL) + g_object_unref (priv->volume); + priv->volume = g_object_ref (element); + + if (volume != 1.0) + empathy_audio_sink_set_volume (self, volume); + } +} + +static void +empathy_audio_sink_init (EmpathyGstAudioSink *obj) +{ + EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (obj); + GstElement *resample; + GstPad *ghost, *sink; + + priv->notifier = fs_element_added_notifier_new (); + g_signal_connect (priv->notifier, "element-added", + G_CALLBACK (empathy_audio_sink_element_added_cb), obj); + + resample = gst_element_factory_make ("audioresample", NULL); + + priv->volume = gst_element_factory_make ("volume", NULL); + g_object_ref (priv->volume); + + priv->sink = gst_element_factory_make ("gconfaudiosink", NULL); + + fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->sink)); + + gst_bin_add_many (GST_BIN (obj), resample, priv->volume, priv->sink, NULL); + gst_element_link_many (resample, priv->volume, priv->sink, NULL); + + sink = gst_element_get_static_pad (resample, "sink"); + + ghost = gst_ghost_pad_new ("sink", sink); + gst_element_add_pad (GST_ELEMENT (obj), ghost); + + gst_object_unref (G_OBJECT (sink)); +} + +static void empathy_audio_sink_dispose (GObject *object); +static void empathy_audio_sink_finalize (GObject *object); + +static void +empathy_audio_sink_set_property (GObject *object, + guint property_id, const GValue *value, GParamSpec *pspec) +{ + switch (property_id) + { + case PROP_VOLUME: + empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (object), + g_value_get_double (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +empathy_audio_sink_get_property (GObject *object, + guint property_id, GValue *value, GParamSpec *pspec) +{ + switch (property_id) + { + case PROP_VOLUME: + g_value_set_double (value, + empathy_audio_sink_get_volume (EMPATHY_GST_AUDIO_SINK (object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +empathy_audio_sink_class_init (EmpathyGstAudioSinkClass + *empathy_audio_sink_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_sink_class); + GParamSpec *param_spec; + + g_type_class_add_private (empathy_audio_sink_class, + sizeof (EmpathyGstAudioSinkPrivate)); + + object_class->dispose = empathy_audio_sink_dispose; + object_class->finalize = empathy_audio_sink_finalize; + + object_class->set_property = empathy_audio_sink_set_property; + object_class->get_property = empathy_audio_sink_get_property; + + param_spec = g_param_spec_double ("volume", "Volume", "volume control", + 0.0, 5.0, 1.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_VOLUME, param_spec); +} + +void +empathy_audio_sink_dispose (GObject *object) +{ + EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object); + EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + if (priv->notifier != NULL) + g_object_unref (priv->notifier); + priv->notifier = NULL; + + if (priv->volume != NULL) + g_object_unref (priv->volume); + priv->volume = NULL; + + if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose) + G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object); +} + +void +empathy_audio_sink_finalize (GObject *object) +{ + //EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object); + //EmpathyGstAudioSinkPrivate *priv = + // EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self); + + /* free any data held directly by the object here */ + + G_OBJECT_CLASS (empathy_audio_sink_parent_class)->finalize (object); +} + +GstElement * +empathy_audio_sink_new (void) +{ + static gboolean registered = FALSE; + + if (!registered) { + if (!gst_element_register (NULL, "empathyaudiosink", + GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SINK)) + return NULL; + registered = TRUE; + } + return gst_element_factory_make ("empathyaudiosink", NULL); +} + +void +empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume) +{ + EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink); + + g_object_set (G_OBJECT (priv->volume), "volume", volume, NULL); +} + +gdouble +empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink) +{ + EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink); + gdouble volume; + + g_object_get (G_OBJECT (priv->volume), "volume", &volume, NULL); + + return volume; +} diff --git a/src/empathy-audio-sink.h b/src/empathy-audio-sink.h new file mode 100644 index 000000000..21ebf2b5a --- /dev/null +++ b/src/empathy-audio-sink.h @@ -0,0 +1,67 @@ +/* + * empathy-gst-video-sink.h - Header for EmpathyGstAudioSink + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __EMPATHY_GST_AUDIO_SINK_H__ +#define __EMPATHY_GST_AUDIO_SINK_H__ + +#include <glib-object.h> +#include <gst/gst.h> + +G_BEGIN_DECLS + +typedef struct _EmpathyGstAudioSink EmpathyGstAudioSink; +typedef struct _EmpathyGstAudioSinkClass EmpathyGstAudioSinkClass; + +struct _EmpathyGstAudioSinkClass { + GstBinClass parent_class; +}; + +struct _EmpathyGstAudioSink { + GstBin parent; +}; + +GType empathy_audio_sink_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_GST_AUDIO_SINK \ + (empathy_audio_sink_get_type ()) +#define EMPATHY_GST_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_GST_AUDIO_SINK, \ + EmpathyGstAudioSink)) +#define EMPATHY_GST_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_GST_AUDIO_SINK, \ + EmpathyGstAudioSinkClass)) +#define EMPATHY_IS_GST_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_GST_AUDIO_SINK)) +#define EMPATHY_IS_GST_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_GST_AUDIO_SINK)) +#define EMPATHY_GST_AUDIO_SINK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_GST_AUDIO_SINK, \ + EmpathyGstAudioSinkClass)) + +GstElement *empathy_audio_sink_new (void); + +void empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume); + +gdouble empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_GST_AUDIO_SINK_H__*/ diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c new file mode 100644 index 000000000..a3416f2ea --- /dev/null +++ b/src/empathy-audio-src.c @@ -0,0 +1,387 @@ +/* + * empathy-gst-audio-src.c - Source for EmpathyGstAudioSrc + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <gst/farsight/fs-element-added-notifier.h> +#include "empathy-audio-src.h" + +G_DEFINE_TYPE(EmpathyGstAudioSrc, empathy_audio_src, GST_TYPE_BIN) + +/* signal enum */ +enum +{ + PEAK_LEVEL_CHANGED, + RMS_LEVEL_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +enum { + PROP_VOLUME = 1, + PROP_RMS_LEVEL, + PROP_PEAK_LEVEL, +}; + +/* private structure */ +typedef struct _EmpathyGstAudioSrcPrivate EmpathyGstAudioSrcPrivate; + +struct _EmpathyGstAudioSrcPrivate +{ + gboolean dispose_has_run; + GstElement *src; + GstElement *volume; + GstElement *level; + FsElementAddedNotifier *notifier; + + gdouble peak_level; + gdouble rms_level; + + GMutex *lock; + guint idle_id; +}; + +#define EMPATHY_GST_AUDIO_SRC_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \ + EmpathyGstAudioSrcPrivate)) + +static void +empathy_audio_src_element_added_cb (FsElementAddedNotifier *notifier, + GstBin *bin, GstElement *element, EmpathyGstAudioSrc *self) +{ + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self); + + if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume")) + { + gdouble volume; + + volume = empathy_audio_src_get_volume (self); + empathy_audio_src_set_volume (self, 1.0); + + if (priv->volume != NULL) + g_object_unref (priv->volume); + priv->volume = g_object_ref (element); + + if (volume != 1.0) + empathy_audio_src_set_volume (self, volume); + } +} + +static void +empathy_audio_src_init (EmpathyGstAudioSrc *obj) +{ + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (obj); + GstPad *ghost, *src; + + priv->peak_level = -G_MAXDOUBLE; + priv->lock = g_mutex_new (); + + priv->notifier = fs_element_added_notifier_new (); + g_signal_connect (priv->notifier, "element-added", + G_CALLBACK (empathy_audio_src_element_added_cb), obj); + + priv->src = gst_element_factory_make ("gconfaudiosrc", NULL); + gst_bin_add (GST_BIN (obj), priv->src); + + fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->src)); + + priv->volume = gst_element_factory_make ("volume", NULL); + g_object_ref (priv->volume); + + gst_bin_add (GST_BIN (obj), priv->volume); + gst_element_link (priv->src, priv->volume); + + priv->level = gst_element_factory_make ("level", NULL); + gst_bin_add (GST_BIN (obj), priv->level); + gst_element_link (priv->volume, priv->level); + + src = gst_element_get_static_pad (priv->level, "src"); + + ghost = gst_ghost_pad_new ("src", src); + gst_element_add_pad (GST_ELEMENT (obj), ghost); + + gst_object_unref (G_OBJECT (src)); +} + +static void empathy_audio_src_dispose (GObject *object); +static void empathy_audio_src_finalize (GObject *object); +static void empathy_audio_src_handle_message (GstBin *bin, + GstMessage *message); + +static gboolean empathy_audio_src_levels_updated (gpointer user_data); + +static void +empathy_audio_src_set_property (GObject *object, + guint property_id, const GValue *value, GParamSpec *pspec) +{ + switch (property_id) + { + case PROP_VOLUME: + empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (object), + g_value_get_double (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +empathy_audio_src_get_property (GObject *object, + guint property_id, GValue *value, GParamSpec *pspec) +{ + EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object); + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self); + + switch (property_id) + { + case PROP_VOLUME: + g_value_set_double (value, + empathy_audio_src_get_volume (self)); + break; + case PROP_PEAK_LEVEL: + g_mutex_lock (priv->lock); + g_value_set_double (value, priv->peak_level); + g_mutex_unlock (priv->lock); + break; + case PROP_RMS_LEVEL: + g_mutex_lock (priv->lock); + g_value_set_double (value, priv->rms_level); + g_mutex_unlock (priv->lock); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +empathy_audio_src_class_init (EmpathyGstAudioSrcClass + *empathy_audio_src_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_src_class); + GstBinClass *gstbin_class = GST_BIN_CLASS (empathy_audio_src_class); + GParamSpec *param_spec; + + g_type_class_add_private (empathy_audio_src_class, + sizeof (EmpathyGstAudioSrcPrivate)); + + object_class->dispose = empathy_audio_src_dispose; + object_class->finalize = empathy_audio_src_finalize; + + object_class->set_property = empathy_audio_src_set_property; + object_class->get_property = empathy_audio_src_get_property; + + gstbin_class->handle_message = + GST_DEBUG_FUNCPTR (empathy_audio_src_handle_message); + + param_spec = g_param_spec_double ("volume", "Volume", "volume contol", + 0.0, 5.0, 1.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_VOLUME, param_spec); + + param_spec = g_param_spec_double ("peak-level", "peak level", "peak level", + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_VOLUME, param_spec); + + signals[PEAK_LEVEL_CHANGED] = g_signal_new ("peak-level-changed", + G_TYPE_FROM_CLASS (empathy_audio_src_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, G_TYPE_DOUBLE); + + param_spec = g_param_spec_double ("rms-level", "RMS level", "RMS level", + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_VOLUME, param_spec); + + + signals[RMS_LEVEL_CHANGED] = g_signal_new ("rms-level-changed", + G_TYPE_FROM_CLASS (empathy_audio_src_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, G_TYPE_DOUBLE); +} + +void +empathy_audio_src_dispose (GObject *object) +{ + EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object); + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + if (priv->idle_id != 0) + g_source_remove (priv->idle_id); + + priv->idle_id = 0; + + /* release any references held by the object here */ + + if (G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose) + G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose (object); +} + +void +empathy_audio_src_finalize (GObject *object) +{ + EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object); + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self); + + /* free any data held directly by the object here */ + g_mutex_free (priv->lock); + + G_OBJECT_CLASS (empathy_audio_src_parent_class)->finalize (object); +} + +static gboolean +empathy_audio_src_levels_updated (gpointer user_data) +{ + EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data); + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self); + + g_mutex_lock (priv->lock); + + g_signal_emit (self, signals[PEAK_LEVEL_CHANGED], 0, priv->peak_level); + g_signal_emit (self, signals[RMS_LEVEL_CHANGED], 0, priv->rms_level); + priv->idle_id = 0; + + g_mutex_unlock (priv->lock); + + return FALSE; +} + +static void +empathy_audio_src_handle_message (GstBin *bin, GstMessage *message) +{ + EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (bin); + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self); + + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT && + GST_MESSAGE_SRC (message) == GST_OBJECT (priv->level)) + { + const GstStructure *s; + const gchar *name; + const GValue *list; + guint i, len; + gdouble peak = -G_MAXDOUBLE; + gdouble rms = -G_MAXDOUBLE; + + s = gst_message_get_structure (message); + name = gst_structure_get_name (s); + + if (g_strcmp0 ("level", name) != 0) + goto out; + + list = gst_structure_get_value (s, "peak"); + len = gst_value_list_get_size (list); + + for (i =0 ; i < len; i++) + { + const GValue *value; + gdouble db; + + value = gst_value_list_get_value (list, i); + db = g_value_get_double (value); + peak = MAX (db, peak); + } + + list = gst_structure_get_value (s, "rms"); + len = gst_value_list_get_size (list); + + for (i =0 ; i < len; i++) + { + const GValue *value; + gdouble db; + + value = gst_value_list_get_value (list, i); + db = g_value_get_double (value); + rms = MAX (db, rms); + } + + g_mutex_lock (priv->lock); + + priv->peak_level = peak; + priv->rms_level = rms; + if (priv->idle_id == 0) + priv->idle_id = g_idle_add (empathy_audio_src_levels_updated, self); + + g_mutex_unlock (priv->lock); + } + +out: + GST_BIN_CLASS (empathy_audio_src_parent_class)->handle_message (bin, + message); +} + +GstElement * +empathy_audio_src_new (void) +{ + static gboolean registered = FALSE; + + if (!registered) { + if (!gst_element_register (NULL, "empathyaudiosrc", + GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SRC)) + return NULL; + registered = TRUE; + } + return gst_element_factory_make ("empathyaudiosrc", NULL); +} + +void +empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume) +{ + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src); + GParamSpec *pspec; + GParamSpecDouble *pspec_double; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (priv->volume), + "volume"); + + g_assert (pspec != NULL); + + pspec_double = G_PARAM_SPEC_DOUBLE (pspec); + + volume = CLAMP (volume, pspec_double->minimum, pspec_double->maximum); + + g_object_set (G_OBJECT (priv->volume), "volume", volume, NULL); +} + +gdouble +empathy_audio_src_get_volume (EmpathyGstAudioSrc *src) +{ + EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src); + gdouble volume; + + g_object_get (G_OBJECT (priv->volume), "volume", &volume, NULL); + + return volume; +} + + diff --git a/src/empathy-audio-src.h b/src/empathy-audio-src.h new file mode 100644 index 000000000..4bca31b61 --- /dev/null +++ b/src/empathy-audio-src.h @@ -0,0 +1,66 @@ +/* + * empathy-gst-video-src.h - Header for EmpathyGstAudioSrc + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __EMPATHY_GST_AUDIO_SRC_H__ +#define __EMPATHY_GST_AUDIO_SRC_H__ + +#include <glib-object.h> +#include <gst/gst.h> + +G_BEGIN_DECLS + +typedef struct _EmpathyGstAudioSrc EmpathyGstAudioSrc; +typedef struct _EmpathyGstAudioSrcClass EmpathyGstAudioSrcClass; + +struct _EmpathyGstAudioSrcClass { + GstBinClass parent_class; +}; + +struct _EmpathyGstAudioSrc { + GstBin parent; +}; + +GType empathy_audio_src_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_GST_AUDIO_SRC \ + (empathy_audio_src_get_type ()) +#define EMPATHY_GST_AUDIO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_GST_AUDIO_SRC, \ + EmpathyGstAudioSrc)) +#define EMPATHY_GST_AUDIO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_GST_AUDIO_SRC, \ + EmpathyGstAudioSrcClass)) +#define EMPATHY_IS_GST_AUDIO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_GST_AUDIO_SRC)) +#define EMPATHY_IS_GST_AUDIO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_GST_AUDIO_SRC)) +#define EMPATHY_GST_AUDIO_SRC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_GST_AUDIO_SRC, \ + EmpathyGstAudioSrcClass)) + +GstElement *empathy_audio_src_new (void); + +void empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume); +gdouble empathy_audio_src_get_volume (EmpathyGstAudioSrc *src); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_GST_AUDIO_SRC_H__*/ diff --git a/src/empathy-streamed-media-window.c b/src/empathy-streamed-media-window.c index 2408f975a..3d0224516 100644 --- a/src/empathy-streamed-media-window.c +++ b/src/empathy-streamed-media-window.c @@ -38,10 +38,6 @@ #include <libempathy/empathy-tp-contact-factory.h> #include <libempathy/empathy-utils.h> #include <libempathy-gtk/empathy-avatar-image.h> -#include <libempathy-gtk/empathy-video-widget.h> -#include <libempathy-gtk/empathy-audio-src.h> -#include <libempathy-gtk/empathy-audio-sink.h> -#include <libempathy-gtk/empathy-video-src.h> #include <libempathy-gtk/empathy-ui-utils.h> #include <libempathy-gtk/empathy-sound-manager.h> #include <libempathy-gtk/empathy-geometry.h> @@ -52,6 +48,10 @@ #include "empathy-streamed-media-window.h" #include "empathy-streamed-media-window-fullscreen.h" +#include "empathy-video-widget.h" +#include "empathy-audio-src.h" +#include "empathy-audio-sink.h" +#include "empathy-video-src.h" #include "ev-sidebar.h" #define BUTTON_ID "empathy-call-dtmf-button-id" diff --git a/src/empathy-video-src.c b/src/empathy-video-src.c new file mode 100644 index 000000000..5c865daaa --- /dev/null +++ b/src/empathy-video-src.c @@ -0,0 +1,360 @@ +/* + * empathy-gst-video-src.c - Source for EmpathyGstVideoSrc + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <gst/interfaces/colorbalance.h> + +#include "empathy-video-src.h" + +G_DEFINE_TYPE(EmpathyGstVideoSrc, empathy_video_src, GST_TYPE_BIN) + +/* Keep in sync with EmpathyGstVideoSrcChannel */ +static const gchar *channel_names[NR_EMPATHY_GST_VIDEO_SRC_CHANNELS] = { + "contrast", "brightness", "gamma" }; + +/* signal enum */ +#if 0 +enum +{ + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; +#endif + +/* private structure */ +typedef struct _EmpathyGstVideoSrcPrivate EmpathyGstVideoSrcPrivate; + +struct _EmpathyGstVideoSrcPrivate +{ + gboolean dispose_has_run; + GstElement *src; + /* Element implementing a ColorBalance interface */ + GstElement *balance; +}; + +#define EMPATHY_GST_VIDEO_SRC_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_VIDEO_SRC, \ + EmpathyGstVideoSrcPrivate)) +/** + * empathy_gst_add_to_bin - create a new gst element, add to bin and link it. + * @bin - bin to add the new element to. + * @src - src element for the new element (may be NULL). + * @name - name of the factory for the new element + * + * Returns: The newly created element ot %NULL on failure + */ +static GstElement * +empathy_gst_add_to_bin (GstBin *bin, + GstElement *src, + const gchar *factoryname) +{ + GstElement *ret; + + if ((ret = gst_element_factory_make (factoryname, NULL)) == NULL) + { + g_message ("Element factory \"%s\" not found.", factoryname); + goto error; + } + + if (!gst_bin_add (bin, ret)) + { + g_warning ("Couldn't add \"%s\" to bin.", factoryname); + goto error; + } + + /* do not link if src == NULL, just exit here */ + if (src == NULL) + return ret; + + if (!gst_element_link (src, ret)) + { + g_warning ("Failed to link \"%s\".", factoryname); + gst_bin_remove (bin, ret); + goto error; + } + + return ret; + +error: + if (ret != NULL) + gst_object_unref (ret); + return NULL; +} + + +static void +empathy_video_src_init (EmpathyGstVideoSrc *obj) +{ + EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (obj); + GstElement *element, *element_back; + GstPad *ghost, *src; + GstCaps *caps; + gchar *str; + + /* allocate caps here, so we can update it by optional elements */ + caps = gst_caps_new_simple ("video/x-raw-yuv", + "width", G_TYPE_INT, 320, + "height", G_TYPE_INT, 240, + NULL); + + /* allocate any data required by the object here */ + if ((element = empathy_gst_add_to_bin (GST_BIN (obj), + NULL, "gconfvideosrc")) == NULL) + g_error ("Couldn't add \"gconfvideosrc\" (gst-plugins-good missing?)"); + + /* we need to save our source to priv->src */ + priv->src = element; + + /* videomaxrate is optional as it's part of gst-plugins-bad. So don't + * fail if it doesn't exist. */ + element_back = element; + if ((element = empathy_gst_add_to_bin (GST_BIN (obj), + element, "videomaxrate")) == NULL) + { + g_message ("Couldn't add \"videomaxrate\" (gst-plugins-bad missing?)"); + element = element_back; + } + else + { + gst_caps_set_simple (caps, + "framerate", GST_TYPE_FRACTION, 15, 1, + NULL); + } + + str = gst_caps_to_string (caps); + g_debug ("Current video src caps are : %s", str); + g_free (str); + + if ((element = empathy_gst_add_to_bin (GST_BIN (obj), + element, "ffmpegcolorspace")) == NULL) + g_error ("Failed to add \"ffmpegcolorspace\" (gst-plugins-base missing?)"); + + if ((element = empathy_gst_add_to_bin (GST_BIN (obj), + element, "videoscale")) == NULL) + g_error ("Failed to add \"videoscale\", (gst-plugins-base missing?)"); + + if ((element = empathy_gst_add_to_bin (GST_BIN (obj), + element, "capsfilter")) == NULL) + g_error ( + "Failed to add \"capsfilter\" (gstreamer core elements missing?)"); + + g_object_set (G_OBJECT (element), "caps", caps, NULL); + + + /* optionally add postproc_tmpnoise to improve the performance of encoders */ + element_back = element; + if ((element = empathy_gst_add_to_bin (GST_BIN (obj), + element, "postproc_tmpnoise")) == NULL) + { + g_message ("Failed to add \"postproc_tmpnoise\" (gst-ffmpeg missing?)"); + element = element_back; + } + + src = gst_element_get_static_pad (element, "src"); + g_assert (src != NULL); + + ghost = gst_ghost_pad_new ("src", src); + if (ghost == NULL) + g_error ("Unable to create ghost pad for the videosrc"); + + if (!gst_element_add_pad (GST_ELEMENT (obj), ghost)) + g_error ("pad with the same name already existed or " + "the pad already had another parent."); + + gst_object_unref (G_OBJECT (src)); +} + +static void empathy_video_src_dispose (GObject *object); +static void empathy_video_src_finalize (GObject *object); + +static void +empathy_video_src_class_init (EmpathyGstVideoSrcClass *empathy_video_src_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_src_class); + + g_type_class_add_private (empathy_video_src_class, + sizeof (EmpathyGstVideoSrcPrivate)); + + object_class->dispose = empathy_video_src_dispose; + object_class->finalize = empathy_video_src_finalize; +} + +void +empathy_video_src_dispose (GObject *object) +{ + EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object); + EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + /* release any references held by the object here */ + + if (G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose) + G_OBJECT_CLASS (empathy_video_src_parent_class)->dispose (object); +} + +void +empathy_video_src_finalize (GObject *object) +{ + //EmpathyGstVideoSrc *self = EMPATHY_GST_VIDEO_SRC (object); + //EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self); + + /* free any data held directly by the object here */ + + G_OBJECT_CLASS (empathy_video_src_parent_class)->finalize (object); +} + +GstElement * +empathy_video_src_new (void) +{ + static gboolean registered = FALSE; + + if (!registered) { + if (!gst_element_register (NULL, "empathyvideosrc", + GST_RANK_NONE, EMPATHY_TYPE_GST_VIDEO_SRC)) + return NULL; + registered = TRUE; + } + return gst_element_factory_make ("empathyvideosrc", NULL); +} + +void +empathy_video_src_set_channel (GstElement *src, + EmpathyGstVideoSrcChannel channel, guint percent) +{ + GstElement *color; + GstColorBalance *balance; + const GList *channels; + GList *l; + + /* Find something supporting GstColorBalance */ + color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE); + + if (color == NULL) + return; + + balance = GST_COLOR_BALANCE (color); + + channels = gst_color_balance_list_channels (balance); + + for (l = (GList *) channels; l != NULL; l = g_list_next (l)) + { + GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data); + + if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0) + { + gst_color_balance_set_value (balance, c, + ((c->max_value - c->min_value) * percent)/100 + + c->min_value); + break; + } + } + + g_object_unref (color); +} + +guint +empathy_video_src_get_channel (GstElement *src, + EmpathyGstVideoSrcChannel channel) +{ + GstElement *color; + GstColorBalance *balance; + const GList *channels; + GList *l; + guint percent = 0; + + /* Find something supporting GstColorBalance */ + color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE); + + if (color == NULL) + return percent; + + balance = GST_COLOR_BALANCE (color); + + channels = gst_color_balance_list_channels (balance); + + for (l = (GList *) channels; l != NULL; l = g_list_next (l)) + { + GstColorBalanceChannel *c = GST_COLOR_BALANCE_CHANNEL (l->data); + + if (g_ascii_strcasecmp (c->label, channel_names[channel]) == 0) + { + percent = + ((gst_color_balance_get_value (balance, c) + - c->min_value) * 100) / + (c->max_value - c->min_value); + + break; + } + } + + g_object_unref (color); + + return percent; +} + + +guint +empathy_video_src_get_supported_channels (GstElement *src) +{ + GstElement *color; + GstColorBalance *balance; + const GList *channels; + GList *l; + guint result = 0; + + /* Find something supporting GstColorBalance */ + color = gst_bin_get_by_interface (GST_BIN (src), GST_TYPE_COLOR_BALANCE); + + if (color == NULL) + goto out; + + balance = GST_COLOR_BALANCE (color); + + channels = gst_color_balance_list_channels (balance); + + for (l = (GList *) channels; l != NULL; l = g_list_next (l)) + { + GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL (l->data); + int i; + + for (i = 0; i < NR_EMPATHY_GST_VIDEO_SRC_CHANNELS; i++) + { + if (g_ascii_strcasecmp (channel->label, channel_names[i]) == 0) + { + result |= (1 << i); + break; + } + } + } + + g_object_unref (color); + +out: + return result; +} + diff --git a/src/empathy-video-src.h b/src/empathy-video-src.h new file mode 100644 index 000000000..fae5563eb --- /dev/null +++ b/src/empathy-video-src.h @@ -0,0 +1,86 @@ +/* + * empathy-gst-video-src.h - Header for EmpathyGstVideoSrc + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __EMPATHY_GST_VIDEO_SRC_H__ +#define __EMPATHY_GST_VIDEO_SRC_H__ + +#include <glib-object.h> +#include <gst/gst.h> + +G_BEGIN_DECLS + +typedef struct _EmpathyGstVideoSrc EmpathyGstVideoSrc; +typedef struct _EmpathyGstVideoSrcClass EmpathyGstVideoSrcClass; + +typedef enum { + EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST = 0, + EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS = 1, + EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA = 2, + NR_EMPATHY_GST_VIDEO_SRC_CHANNELS +} EmpathyGstVideoSrcChannel; + +#define EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST \ + (1 << EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST) +#define EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS \ + (1 << EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS) +#define EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA \ + (1 << EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA) + +struct _EmpathyGstVideoSrcClass { + GstBinClass parent_class; +}; + +struct _EmpathyGstVideoSrc { + GstBin parent; +}; + +GType empathy_video_src_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_GST_VIDEO_SRC \ + (empathy_video_src_get_type ()) +#define EMPATHY_GST_VIDEO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_GST_VIDEO_SRC, \ + EmpathyGstVideoSrc)) +#define EMPATHY_GST_VIDEO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_GST_VIDEO_SRC, \ + EmpathyGstVideoSrcClass)) +#define EMPATHY_IS_GST_VIDEO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_GST_VIDEO_SRC)) +#define EMPATHY_IS_GST_VIDEO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_GST_VIDEO_SRC)) +#define EMPATHY_GST_VIDEO_SRC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_GST_VIDEO_SRC, \ + EmpathyGstVideoSrcClass)) + +GstElement *empathy_video_src_new (void); + +guint +empathy_video_src_get_supported_channels (GstElement *src); + +void empathy_video_src_set_channel (GstElement *src, + EmpathyGstVideoSrcChannel channel, guint percent); + +guint empathy_video_src_get_channel (GstElement *src, + EmpathyGstVideoSrcChannel channel); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_GST_VIDEO_SRC_H__*/ diff --git a/src/empathy-video-widget.c b/src/empathy-video-widget.c new file mode 100644 index 000000000..b4b39ffdb --- /dev/null +++ b/src/empathy-video-widget.c @@ -0,0 +1,487 @@ +/* + * empathy-gst-gtk-widget.c - Source for EmpathyVideoWidget + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <gdk/gdkx.h> +#include <gst/interfaces/xoverlay.h> +#include <gst/farsight/fs-element-added-notifier.h> + +#include "empathy-video-widget.h" + +G_DEFINE_TYPE(EmpathyVideoWidget, empathy_video_widget, + GTK_TYPE_DRAWING_AREA) + +static void empathy_video_widget_element_added_cb ( + FsElementAddedNotifier *notifier, GstBin *bin, GstElement *element, + EmpathyVideoWidget *self); + +static void empathy_video_widget_sync_message_cb ( + GstBus *bus, GstMessage *message, EmpathyVideoWidget *self); + +/* signal enum */ +#if 0 +enum +{ + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; +#endif + +enum { + PROP_GST_ELEMENT = 1, + PROP_GST_BUS, + PROP_MIN_WIDTH, + PROP_MIN_HEIGHT, + PROP_SYNC, + PROP_ASYNC, +}; + +/* private structure */ +typedef struct _EmpathyVideoWidgetPriv EmpathyVideoWidgetPriv; + +struct _EmpathyVideoWidgetPriv +{ + gboolean dispose_has_run; + GstBus *bus; + GstElement *videosink; + GstPad *sink_pad; + GstElement *overlay; + FsElementAddedNotifier *notifier; + gint min_width; + gint min_height; + gboolean sync; + gboolean async; + + GMutex *lock; +}; + +#define GET_PRIV(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + EMPATHY_TYPE_VIDEO_WIDGET, EmpathyVideoWidgetPriv)) + +static void +empathy_video_widget_init (EmpathyVideoWidget *obj) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (obj); + GdkRGBA black; + + priv->lock = g_mutex_new (); + + priv->notifier = fs_element_added_notifier_new (); + g_signal_connect (priv->notifier, "element-added", + G_CALLBACK (empathy_video_widget_element_added_cb), + obj); + + if (gdk_rgba_parse (&black, "Black")) + gtk_widget_override_background_color (GTK_WIDGET (obj), 0, &black); + + gtk_widget_set_double_buffered (GTK_WIDGET (obj), FALSE); +} + +static void +empathy_video_widget_realized (GtkWidget *widget, gpointer user_data) +{ + /* requesting the XID forces the GdkWindow to be native in GTK+ 2.18 + * onwards, requesting the native window in a thread causes a BadWindowID, + * so we need to request it now. We could call gdk_window_ensure_native(), + * but that would mean we require GTK+ 2.18, so instead we call this */ + GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (widget))); +} + +static void +empathy_video_widget_constructed (GObject *object) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (object); + GstElement *colorspace, *videoscale, *sink; + GstPad *pad; + + g_signal_connect (object, "realize", + G_CALLBACK (empathy_video_widget_realized), NULL); + + priv->videosink = gst_bin_new (NULL); + + gst_object_ref (priv->videosink); + gst_object_sink (priv->videosink); + + priv->sink_pad = gst_element_get_static_pad (priv->videosink, "sink"); + + sink = gst_element_factory_make ("gconfvideosink", NULL); + g_assert (sink != NULL); + + videoscale = gst_element_factory_make ("videoscale", NULL); + g_assert (videoscale != NULL); + + g_object_set (videoscale, "qos", FALSE, NULL); + + colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL); + g_assert (colorspace != NULL); + + g_object_set (colorspace, "qos", FALSE, NULL); + + gst_bin_add_many (GST_BIN (priv->videosink), colorspace, videoscale, + sink, NULL); + + if (!gst_element_link (colorspace, videoscale)) + g_error ("Failed to link ffmpegcolorspace and videoscale"); + + if (!gst_element_link (videoscale, sink)) + g_error ("Failed to link videoscale and gconfvideosink"); + + pad = gst_element_get_static_pad (colorspace, "sink"); + g_assert (pad != NULL); + + priv->sink_pad = gst_ghost_pad_new ("sink", pad); + if (!gst_element_add_pad (priv->videosink, priv->sink_pad)) + g_error ("Couldn't add sink ghostpad to the bin"); + + gst_object_unref (pad); + + fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->videosink)); + gst_bus_enable_sync_message_emission (priv->bus); + + g_signal_connect (priv->bus, "sync-message", + G_CALLBACK (empathy_video_widget_sync_message_cb), object); + + gtk_widget_set_size_request (GTK_WIDGET (object), priv->min_width, + priv->min_height); +} + +static void empathy_video_widget_dispose (GObject *object); +static void empathy_video_widget_finalize (GObject *object); + + +static gboolean empathy_video_widget_draw (GtkWidget *widget, + cairo_t *cr); + +static void +empathy_video_widget_element_set_sink_properties (EmpathyVideoWidget *self); + +static void +empathy_video_widget_set_property (GObject *object, + guint property_id, const GValue *value, GParamSpec *pspec) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (object); + + switch (property_id) + { + case PROP_GST_BUS: + priv->bus = g_value_dup_object (value); + break; + case PROP_MIN_WIDTH: + priv->min_width = g_value_get_int (value); + break; + case PROP_MIN_HEIGHT: + priv->min_height = g_value_get_int (value); + break; + case PROP_SYNC: + priv->sync = g_value_get_boolean (value); + empathy_video_widget_element_set_sink_properties ( + EMPATHY_VIDEO_WIDGET (object)); + break; + case PROP_ASYNC: + priv->async = g_value_get_boolean (value); + empathy_video_widget_element_set_sink_properties ( + EMPATHY_VIDEO_WIDGET (object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +empathy_video_widget_get_property (GObject *object, + guint property_id, GValue *value, GParamSpec *pspec) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (object); + + switch (property_id) + { + case PROP_GST_ELEMENT: + g_value_set_object (value, priv->videosink); + break; + case PROP_GST_BUS: + g_value_set_object (value, priv->bus); + break; + case PROP_MIN_WIDTH: + g_value_set_int (value, priv->min_width); + break; + case PROP_MIN_HEIGHT: + g_value_set_int (value, priv->min_height); + break; + case PROP_SYNC: + g_value_set_boolean (value, priv->sync); + break; + case PROP_ASYNC: + g_value_set_boolean (value, priv->async); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + + +static void +empathy_video_widget_class_init ( + EmpathyVideoWidgetClass *empathy_video_widget_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_widget_class); + GtkWidgetClass *widget_class = + GTK_WIDGET_CLASS (empathy_video_widget_class); + GParamSpec *param_spec; + + g_type_class_add_private (empathy_video_widget_class, + sizeof (EmpathyVideoWidgetPriv)); + + object_class->dispose = empathy_video_widget_dispose; + object_class->finalize = empathy_video_widget_finalize; + object_class->constructed = empathy_video_widget_constructed; + + object_class->set_property = empathy_video_widget_set_property; + object_class->get_property = empathy_video_widget_get_property; + + widget_class->draw = empathy_video_widget_draw; + + param_spec = g_param_spec_object ("gst-element", + "gst-element", "The underlaying gstreamer element", + GST_TYPE_ELEMENT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_GST_ELEMENT, param_spec); + + param_spec = g_param_spec_object ("gst-bus", + "gst-bus", + "The toplevel bus from the pipeline in which this bin will be added", + GST_TYPE_BUS, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_GST_BUS, param_spec); + + param_spec = g_param_spec_int ("min-width", + "min-width", + "Minimal width of the widget", + 0, G_MAXINT, EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_MIN_WIDTH, param_spec); + + param_spec = g_param_spec_int ("min-height", + "min-height", + "Minimal height of the widget", + 0, G_MAXINT, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_MIN_HEIGHT, param_spec); + + param_spec = g_param_spec_boolean ("sync", + "sync", + "Whether the underlying sink should be sync or not", + TRUE, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SYNC, param_spec); + + param_spec = g_param_spec_boolean ("async", + "async", + "Whether the underlying sink should be async or not", + TRUE, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_ASYNC, param_spec); +} + +void +empathy_video_widget_dispose (GObject *object) +{ + EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object); + EmpathyVideoWidgetPriv *priv = GET_PRIV (self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + g_signal_handlers_disconnect_by_func (priv->bus, + empathy_video_widget_sync_message_cb, object); + + if (priv->bus != NULL) + g_object_unref (priv->bus); + + priv->bus = NULL; + + if (priv->videosink != NULL) + g_object_unref (priv->videosink); + + priv->videosink = NULL; + + + /* release any references held by the object here */ + + if (G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose) + G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose (object); +} + +void +empathy_video_widget_finalize (GObject *object) +{ + EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object); + EmpathyVideoWidgetPriv *priv = GET_PRIV (self); + + /* free any data held directly by the object here */ + g_mutex_free (priv->lock); + + G_OBJECT_CLASS (empathy_video_widget_parent_class)->finalize (object); +} + + +static void +empathy_video_widget_element_set_sink_properties_unlocked ( + EmpathyVideoWidget *self) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (self); + + if (priv->overlay == NULL) + return; + + if (g_object_class_find_property (G_OBJECT_GET_CLASS (priv->overlay), + "force-aspect-ratio")) + g_object_set (G_OBJECT (priv->overlay), "force-aspect-ratio", TRUE, NULL); + + if (g_object_class_find_property ( + G_OBJECT_GET_CLASS (priv->overlay), "sync")) + g_object_set (G_OBJECT (priv->overlay), "sync", priv->sync, NULL); + + if (g_object_class_find_property (G_OBJECT_GET_CLASS (priv->overlay), + "async")) + g_object_set (G_OBJECT (priv->overlay), "async", priv->async, NULL); +} + +static void +empathy_video_widget_element_set_sink_properties (EmpathyVideoWidget *self) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (self); + + g_mutex_lock (priv->lock); + empathy_video_widget_element_set_sink_properties_unlocked (self); + g_mutex_unlock (priv->lock); +} + +static void +empathy_video_widget_element_added_cb (FsElementAddedNotifier *notifier, + GstBin *bin, GstElement *element, EmpathyVideoWidget *self) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (self); + + /* We assume the overlay is the sink */ + g_mutex_lock (priv->lock); + if (priv->overlay == NULL && GST_IS_X_OVERLAY (element)) + { + priv->overlay = element; + g_object_add_weak_pointer (G_OBJECT (element), + (gpointer) &priv->overlay); + empathy_video_widget_element_set_sink_properties_unlocked (self); + gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay)); + } + g_mutex_unlock (priv->lock); +} + +static void +empathy_video_widget_sync_message_cb (GstBus *bus, GstMessage *message, + EmpathyVideoWidget *self) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (self); + const GstStructure *s; + + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return; + + if (GST_MESSAGE_SRC (message) != (GstObject *) priv->overlay) + return; + + s = gst_message_get_structure (message); + + if (gst_structure_has_name (s, "prepare-xwindow-id")) + { + g_assert (gtk_widget_get_realized (GTK_WIDGET (self))); + gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay), + GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (self)))); + } +} + +static gboolean +empathy_video_widget_draw (GtkWidget *widget, + cairo_t *cr) +{ + EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (widget); + EmpathyVideoWidgetPriv *priv = GET_PRIV (self); + GtkAllocation allocation; + + if (priv->overlay == NULL) + { + gtk_widget_get_allocation (widget, &allocation); + + gtk_render_frame (gtk_widget_get_style_context (widget), cr, + 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + return TRUE; + } + + gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay), + GDK_WINDOW_XID (gtk_widget_get_window (widget))); + + gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay)); + + return TRUE; +} + +GtkWidget * +empathy_video_widget_new_with_size (GstBus *bus, gint width, gint height) +{ + g_return_val_if_fail (bus != NULL, NULL); + + return GTK_WIDGET (g_object_new (EMPATHY_TYPE_VIDEO_WIDGET, + "gst-bus", bus, + "min-width", width, + "min-height", height, + NULL)); +} + +GtkWidget * +empathy_video_widget_new (GstBus *bus) +{ + g_return_val_if_fail (bus != NULL, NULL); + + return GTK_WIDGET (g_object_new (EMPATHY_TYPE_VIDEO_WIDGET, + "gst-bus", bus, + NULL)); +} + +GstPad * +empathy_video_widget_get_sink (EmpathyVideoWidget *widget) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (widget); + + return priv->sink_pad; +} + +GstElement * +empathy_video_widget_get_element (EmpathyVideoWidget *widget) +{ + EmpathyVideoWidgetPriv *priv = GET_PRIV (widget); + + return priv->videosink; +} diff --git a/src/empathy-video-widget.h b/src/empathy-video-widget.h new file mode 100644 index 000000000..cfdd0354c --- /dev/null +++ b/src/empathy-video-widget.h @@ -0,0 +1,72 @@ +/* + * empathy-gst-gtk-widget.h - Header for EmpathyVideoWidget + * Copyright (C) 2008 Collabora Ltd. + * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __EMPATHY_VIDEO_WIDGET_H__ +#define __EMPATHY_VIDEO_WIDGET_H__ + +#define EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH 320 +#define EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT 240 + +#include <glib-object.h> +#include <gst/gst.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _EmpathyVideoWidget EmpathyVideoWidget; +typedef struct _EmpathyVideoWidgetClass EmpathyVideoWidgetClass; + +struct _EmpathyVideoWidgetClass { + GtkDrawingAreaClass parent_class; +}; + +struct _EmpathyVideoWidget { + GtkDrawingArea parent; +}; + +GType empathy_video_widget_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_VIDEO_WIDGET \ + (empathy_video_widget_get_type ()) +#define EMPATHY_VIDEO_WIDGET(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_VIDEO_WIDGET, \ + EmpathyVideoWidget)) +#define EMPATHY_VIDEO_WIDGET_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_VIDEO_WIDGET, \ + EmpathyVideoWidgetClass)) +#define EMPATHY_IS_VIDEO_WIDGET(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_VIDEO_WIDGET)) +#define EMPATHY_IS_VIDEO_WIDGET_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_VIDEO_WIDGET)) +#define EMPATHY_VIDEO_WIDGET_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_VIDEO_WIDGET, \ + EmpathyVideoWidgetClass)) + +GtkWidget *empathy_video_widget_new (GstBus *bus); +GtkWidget *empathy_video_widget_new_with_size (GstBus *bus, + gint width, gint height); + +GstElement *empathy_video_widget_get_element (EmpathyVideoWidget *widget); +GstPad *empathy_video_widget_get_sink (EmpathyVideoWidget *widget); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_VIDEO_WIDGET_H__*/ |