From 0d2fc6a7303cbd0db9675c14dd3e1dfc6ba0b2cc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 3 Jul 2010 16:15:34 +0200 Subject: [PATCH] gsettings: Initial version of GSettings plugin This provides audio/video sources and sinks. Fixes bug #616265. --- configure.ac | 12 + ext/Makefile.am | 8 + ext/gsettings/Makefile.am | 37 +++ ext/gsettings/gstgsettings.h | 40 +++ ext/gsettings/gstgsettingsaudiosink.c | 353 +++++++++++++++++++++ ext/gsettings/gstgsettingsaudiosink.h | 71 +++++ ext/gsettings/gstgsettingsaudiosrc.c | 245 ++++++++++++++ ext/gsettings/gstgsettingsaudiosrc.h | 62 ++++ ext/gsettings/gstgsettingsvideosink.c | 245 ++++++++++++++ ext/gsettings/gstgsettingsvideosink.h | 62 ++++ ext/gsettings/gstgsettingsvideosrc.c | 245 ++++++++++++++ ext/gsettings/gstgsettingsvideosrc.h | 62 ++++ ext/gsettings/gstswitchsink.c | 270 ++++++++++++++++ ext/gsettings/gstswitchsink.h | 62 ++++ ext/gsettings/gstswitchsrc.c | 258 +++++++++++++++ ext/gsettings/gstswitchsrc.h | 57 ++++ ...sktop.gstreamer.default-elements.gschema.xml.in | 94 ++++++ ext/gsettings/plugin.c | 50 +++ po/POTFILES.in | 1 + 19 files changed, 2234 insertions(+) create mode 100644 ext/gsettings/Makefile.am create mode 100644 ext/gsettings/gstgsettings.h create mode 100644 ext/gsettings/gstgsettingsaudiosink.c create mode 100644 ext/gsettings/gstgsettingsaudiosink.h create mode 100644 ext/gsettings/gstgsettingsaudiosrc.c create mode 100644 ext/gsettings/gstgsettingsaudiosrc.h create mode 100644 ext/gsettings/gstgsettingsvideosink.c create mode 100644 ext/gsettings/gstgsettingsvideosink.h create mode 100644 ext/gsettings/gstgsettingsvideosrc.c create mode 100644 ext/gsettings/gstgsettingsvideosrc.h create mode 100644 ext/gsettings/gstswitchsink.c create mode 100644 ext/gsettings/gstswitchsink.h create mode 100644 ext/gsettings/gstswitchsrc.c create mode 100644 ext/gsettings/gstswitchsrc.h create mode 100644 ext/gsettings/org.freedesktop.gstreamer.default-elements.gschema.xml.in create mode 100644 ext/gsettings/plugin.c diff --git a/configure.ac b/configure.ac index d6700d3..39d65a3 100644 --- a/configure.ac +++ b/configure.ac @@ -1529,6 +1529,15 @@ AG_GST_CHECK_FEATURE(RTMP, [rtmp library], rtmp, [ AG_GST_PKG_CHECK_MODULES(RTMP, librtmp) ]) +dnl *** GSettings *** +translit(dnm, m, l) AM_CONDITIONAL(USE_GSETTINGS, true) +AG_GST_CHECK_FEATURE(GSETTINGS, [GSettings plugin], gsettings, [ + AG_GST_PKG_CHECK_MODULES(GSETTINGS, gio-2.0 >= 2.25.0) + if test "x$HAVE_GSETTINGS" = "xyes"; then + GLIB_GSETTINGS + fi +]) + else dnl not building plugins with external dependencies, @@ -1579,6 +1588,7 @@ AM_CONDITIONAL(USE_SNDFILE, false) AM_CONDITIONAL(USE_SOUNDTOUCH, false) AM_CONDITIONAL(USE_SPC, false) AM_CONDITIONAL(USE_GME, false) +AM_CONDITIONAL(USE_GSETTINGS, false) AM_CONDITIONAL(USE_SWFDEC, false) AM_CONDITIONAL(USE_THEORADEC, false) AM_CONDITIONAL(USE_XVID, false) @@ -1793,6 +1803,8 @@ ext/sdl/Makefile ext/sndfile/Makefile ext/soundtouch/Makefile ext/gme/Makefile +ext/gsettings/Makefile +ext/gsettings/org.freedesktop.gstreamer.default-elements.gschema.xml ext/spc/Makefile ext/swfdec/Makefile ext/theora/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index ea252d5..b3509bb 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -348,6 +348,12 @@ else VP8_DIR= endif +if USE_GSETTINGS +GSETTINGS_DIR=gsettings +else +GSETTINGS_DIR= +endif + # if USE_XINE # XINE_DIR=xine # else @@ -395,6 +401,7 @@ SUBDIRS=\ $(FAAC_DIR) \ $(FAAD_DIR) \ $(FLITE_DIR) \ + $(GSETTINGS_DIR) \ $(GSM_DIR) \ $(G729_DIR) \ $(HERMES_DIR) \ @@ -451,6 +458,7 @@ DIST_SUBDIRS = \ faac \ faad \ flite \ + gsettings \ gsm \ ladspa \ jack \ diff --git a/ext/gsettings/Makefile.am b/ext/gsettings/Makefile.am new file mode 100644 index 0000000..92c964a --- /dev/null +++ b/ext/gsettings/Makefile.am @@ -0,0 +1,37 @@ +gsettings_SCHEMAS = org.freedesktop.gstreamer-@GST_MAJORMINOR@.default-elements.gschema.xml + +org.freedesktop.gstreamer-@GST_MAJORMINOR@.default-elements.gschema.xml: org.freedesktop.gstreamer.default-elements.gschema.xml + cp org.freedesktop.gstreamer.default-elements.gschema.xml org.freedesktop.gstreamer-@GST_MAJORMINOR@.default-elements.gschema.xml + +@GSETTINGS_RULES@ + +plugin_LTLIBRARIES = libgstgsettingselements.la + +libgstgsettingselements_la_SOURCES = \ + gstgsettingsaudiosink.c \ + gstgsettingsaudiosrc.c \ + gstgsettingsvideosink.c \ + gstgsettingsvideosrc.c \ + gstswitchsink.c \ + gstswitchsrc.c \ + plugin.c + +libgstgsettingselements_la_CFLAGS = $(GST_CFLAGS) $(GSETTINGS_CFLAGS) $(DIR_CFLAGS) \ + -DGstSwitchSrc=GstGSettingsSwitchSrc \ + -DGstSwitchSrcClass=GstGSettingsSwitchSrcClass \ + -DGstSwitchSink=GstGSettingsSwitchSink \ + -DGstSwitchSinkClass=GstGSettingsSwitchSinkClass +libgstgsettingselements_la_LIBADD = $(GST_LIBS) $(GSETTINGS_LIBS) +libgstgsettingselements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstgsettingselements_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = \ + gstgsettingsaudiosink.h \ + gstgsettingsaudiosrc.h \ + gstgsettingsvideosink.h \ + gstgsettingsvideosrc.h \ + gstswitchsink.h \ + gstswitchsrc.h \ + gstgsettings.h + +EXTRA_DIST = org.freedesktop.gstreamer.default-elements.gschema.xml diff --git a/ext/gsettings/gstgsettings.h b/ext/gsettings/gstgsettings.h new file mode 100644 index 0000000..1e36415 --- /dev/null +++ b/ext/gsettings/gstgsettings.h @@ -0,0 +1,40 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_GSETTINGS_H__ +#define __GST_GSETTINGS_H__ + +#include + +G_BEGIN_DECLS + +#define GST_GSETTINGS_SCHEMA "org.freedesktop.gstreamer-0.10.default-elements" +#define GST_GSETTINGS_PATH "/desktop/gstreamer/0.10/default-elements/" + +#define GST_GSETTINGS_KEY_SOUNDS_AUDIOSINK "sounds-audiosink" +#define GST_GSETTINGS_KEY_MUSIC_AUDIOSINK "music-audiosink" +#define GST_GSETTINGS_KEY_CHAT_AUDIOSINK "chat-audiosink" +#define GST_GSETTINGS_KEY_AUDIOSRC "audiosrc" +#define GST_GSETTINGS_KEY_VIDEOSINK "videosink" +#define GST_GSETTINGS_KEY_VIDEOSRC "videosrc" +#define GST_GSETTINGS_KEY_VISUALIZATION "visualization" + +G_END_DECLS + +#endif /* __GST_GSETTINGS_H__ */ diff --git a/ext/gsettings/gstgsettingsaudiosink.c b/ext/gsettings/gstgsettingsaudiosink.c new file mode 100644 index 0000000..be667ff --- /dev/null +++ b/ext/gsettings/gstgsettingsaudiosink.c @@ -0,0 +1,353 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:element-gsettingsaudiosink + * + * This element outputs sound to the audiosink that has been configured in + * GSettings by the user. + * + * + * Example launch line + * |[ + * gst-launch audiotestsrc ! audioconvert ! audioresample ! gsettingsaudiosink + * ]| Play on configured audiosink + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "gstgsettingsaudiosink.h" +#include "gstgsettings.h" + +#define GST_TYPE_GSETTINGS_AUDIOSINK_PROFILE (gst_gsettings_audiosink_profile_get_type()) +static GType +gst_gsettings_audiosink_profile_get_type (void) +{ + static GType gsettings_profile_type = 0; + static const GEnumValue gsettings_profiles[] = { + {GST_GSETTINGS_AUDIOSINK_PROFILE_SOUNDS, "Sound Events", "sounds"}, + {GST_GSETTINGS_AUDIOSINK_PROFILE_MUSIC, "Music and Movies (default)", + "music"}, + {GST_GSETTINGS_AUDIOSINK_PROFILE_CHAT, "Audio/Video Conferencing", "chat"}, + {0, NULL, NULL} + }; + + if (!gsettings_profile_type) { + gsettings_profile_type = + g_enum_register_static ("GstGSettingsAudioSinkProfile", + gsettings_profiles); + } + return gsettings_profile_type; +} + +enum +{ + PROP_0, + PROP_PROFILE +}; + +GST_BOILERPLATE (GstGSettingsAudioSink, gst_gsettings_audio_sink, GstSwitchSink, + GST_TYPE_SWITCH_SINK); + +static gboolean +gst_gsettings_audio_sink_change_child (GstGSettingsAudioSink * sink) +{ + const gchar *key = NULL; + gchar *new_string; + GError *err = NULL; + GstElement *new_kid; + + GST_OBJECT_LOCK (sink); + switch (sink->profile) { + case GST_GSETTINGS_AUDIOSINK_PROFILE_SOUNDS: + key = GST_GSETTINGS_KEY_SOUNDS_AUDIOSINK; + break; + case GST_GSETTINGS_AUDIOSINK_PROFILE_MUSIC: + key = GST_GSETTINGS_KEY_MUSIC_AUDIOSINK; + break; + case GST_GSETTINGS_AUDIOSINK_PROFILE_CHAT: + key = GST_GSETTINGS_KEY_CHAT_AUDIOSINK; + break; + default: + break; + } + + new_string = g_settings_get_string (sink->settings, key); + + if (new_string != NULL && sink->gsettings_str != NULL && + (strlen (new_string) == 0 || + strcmp (sink->gsettings_str, new_string) == 0)) { + g_free (new_string); + GST_DEBUG_OBJECT (sink, + "GSettings key was updated, but it didn't change. Ignoring"); + GST_OBJECT_UNLOCK (sink); + return TRUE; + } + GST_OBJECT_UNLOCK (sink); + + GST_DEBUG_OBJECT (sink, "GSettings key changed from '%s' to '%s'", + GST_STR_NULL (sink->gsettings_str), GST_STR_NULL (new_string)); + + if (new_string) { + new_kid = gst_parse_bin_from_description (new_string, TRUE, &err); + if (err) { + GST_ERROR_OBJECT ("error creating bin '%s': %s", new_string, + err->message); + g_error_free (err); + } + } else { + new_kid = NULL; + } + + if (new_kid == NULL) { + GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL), + ("Failed to render audio sink from GSettings")); + goto fail; + } + + if (!gst_switch_sink_set_child (GST_SWITCH_SINK (sink), new_kid)) { + GST_WARNING_OBJECT (sink, "Failed to update child element"); + goto fail; + } + + g_free (sink->gsettings_str); + sink->gsettings_str = new_string; + + return TRUE; + +fail: + g_free (new_string); + return FALSE; +} + +static gboolean +gst_gsettings_audio_sink_switch_profile (GstGSettingsAudioSink * sink, + GstGSettingsAudioSinkProfile profile) +{ + if (sink->settings == NULL) + return TRUE; + + GST_OBJECT_LOCK (sink); + sink->profile = profile; + GST_OBJECT_UNLOCK (sink); + + return gst_gsettings_audio_sink_change_child (sink); +} + +static void +on_changed (GSettings * settings, gchar * key, GstGSettingsAudioSink * sink) +{ + gboolean changed = FALSE; + if (!g_str_has_suffix (key, "audiosink")); + return; + + GST_OBJECT_LOCK (sink); + if ((sink->profile == GST_GSETTINGS_AUDIOSINK_PROFILE_SOUNDS && + g_str_equal (key, GST_GSETTINGS_KEY_SOUNDS_AUDIOSINK)) || + (sink->profile == GST_GSETTINGS_AUDIOSINK_PROFILE_MUSIC && + g_str_equal (key, GST_GSETTINGS_KEY_MUSIC_AUDIOSINK)) || + (sink->profile == GST_GSETTINGS_AUDIOSINK_PROFILE_CHAT && + g_str_equal (key, GST_GSETTINGS_KEY_CHAT_AUDIOSINK))) + changed = TRUE; + GST_OBJECT_UNLOCK (sink); + + if (changed) + gst_gsettings_audio_sink_change_child (sink); +} + +static gboolean +gst_gsettings_audio_sink_start (GstGSettingsAudioSink * sink) +{ + GError *err = NULL; + GThread *thread; + + sink->loop = g_main_loop_new (sink->context, FALSE); + + thread = + g_thread_create ((GThreadFunc) g_main_loop_run, sink->loop, FALSE, &err); + if (!thread) { + GST_ELEMENT_ERROR (sink, CORE, STATE_CHANGE, (NULL), + ("Failed to create new thread: %s", err->message)); + g_error_free (err); + g_main_loop_unref (sink->loop); + sink->loop = NULL; + return FALSE; + } + + g_main_context_push_thread_default (sink->context); + sink->settings = g_settings_new (GST_GSETTINGS_SCHEMA); + sink->changed_id = + g_signal_connect_data (G_OBJECT (sink->settings), "changed", + G_CALLBACK (on_changed), gst_object_ref (sink), + (GClosureNotify) gst_object_unref, 0); + g_main_context_pop_thread_default (sink->context); + + return TRUE; +} + +static gboolean +gst_gsettings_audio_sink_reset (GstGSettingsAudioSink * sink) +{ + gst_switch_sink_set_child (GST_SWITCH_SINK (sink), NULL); + + if (sink->changed_id) { + g_signal_handler_disconnect (sink->settings, sink->changed_id); + sink->changed_id = 0; + } + + if (sink->loop) { + g_main_loop_quit (sink->loop); + g_main_loop_unref (sink->loop); + sink->loop = NULL; + } + + if (sink->settings) { + g_object_unref (sink->settings); + sink->settings = NULL; + } + + GST_OBJECT_LOCK (sink); + g_free (sink->gsettings_str); + sink->gsettings_str = NULL; + GST_OBJECT_UNLOCK (sink); + + return TRUE; +} + +static void +gst_gsettings_audio_sink_finalize (GObject * object) +{ + GstGSettingsAudioSink *sink = GST_GSETTINGS_AUDIO_SINK (object); + + g_free (sink->gsettings_str); + g_main_context_unref (sink->context); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, ((GObject *) (sink))); +} + +static void +gst_gsettings_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGSettingsAudioSink *sink = GST_GSETTINGS_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_PROFILE: + gst_gsettings_audio_sink_switch_profile (sink, g_value_get_enum (value)); + break; + default: + break; + } +} + +static void +gst_gsettings_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGSettingsAudioSink *sink = GST_GSETTINGS_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_PROFILE: + g_value_set_enum (value, sink->profile); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_gsettings_audio_sink_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstGSettingsAudioSink *sink = GST_GSETTINGS_AUDIO_SINK (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_gsettings_audio_sink_start (sink)) + return GST_STATE_CHANGE_FAILURE; + + if (!gst_gsettings_audio_sink_change_child (sink)) { + gst_gsettings_audio_sink_reset (sink); + return GST_STATE_CHANGE_FAILURE; + } + break; + default: + break; + } + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_SUCCESS); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_gsettings_audio_sink_reset (sink); + break; + default: + break; + } + + return ret; +} + +static void +gst_gsettings_audio_sink_init (GstGSettingsAudioSink * sink, + GstGSettingsAudioSinkClass * g_class) +{ + sink->context = g_main_context_new (); + gst_gsettings_audio_sink_reset (sink); + + sink->profile = GST_GSETTINGS_AUDIOSINK_PROFILE_MUSIC; +} + +static void +gst_gsettings_audio_sink_base_init (gpointer klass) +{ + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details_simple (eklass, "GSettings audio sink", + "Sink/Audio", + "Audio sink embedding the GSettings preferences for audio output", + "Sebastian Dröge "); +} + +static void +gst_gsettings_audio_sink_class_init (GstGSettingsAudioSinkClass * klass) +{ + GObjectClass *oklass = G_OBJECT_CLASS (klass); + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + oklass->finalize = gst_gsettings_audio_sink_finalize; + oklass->set_property = gst_gsettings_audio_sink_set_property; + oklass->get_property = gst_gsettings_audio_sink_get_property; + + g_object_class_install_property (oklass, PROP_PROFILE, + g_param_spec_enum ("profile", "Profile", "Profile", + GST_TYPE_GSETTINGS_AUDIOSINK_PROFILE, + GST_GSETTINGS_AUDIOSINK_PROFILE_SOUNDS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + eklass->change_state = gst_gsettings_audio_sink_change_state; +} diff --git a/ext/gsettings/gstgsettingsaudiosink.h b/ext/gsettings/gstgsettingsaudiosink.h new file mode 100644 index 0000000..139a138 --- /dev/null +++ b/ext/gsettings/gstgsettingsaudiosink.h @@ -0,0 +1,71 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_GSETTINGS_AUDIO_SINK_H__ +#define __GST_GSETTINGS_AUDIO_SINK_H__ + +#include +#include +#include "gstswitchsink.h" + +G_BEGIN_DECLS + +#define GST_TYPE_GSETTINGS_AUDIO_SINK \ + (gst_gsettings_audio_sink_get_type ()) +#define GST_GSETTINGS_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GSETTINGS_AUDIO_SINK, \ + GstGSettingsAudioSink)) +#define GST_GSETTINGS_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GSETTINGS_AUDIO_SINK, \ + GstGSettingsAudioSinkClass)) +#define GST_IS_GSETTINGS_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_GSETTINGS_AUDIO_SINK)) +#define GST_IS_GSETTINGS_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GSETTINGS_AUDIO_SINK)) + +typedef enum +{ + GST_GSETTINGS_AUDIOSINK_PROFILE_SOUNDS, + GST_GSETTINGS_AUDIOSINK_PROFILE_MUSIC, + GST_GSETTINGS_AUDIOSINK_PROFILE_CHAT, + GST_GSETTINGS_AUDIOSINK_PROFILE_NONE /* Internal value only */ +} GstGSettingsAudioSinkProfile; + +typedef struct _GstGSettingsAudioSink { + GstSwitchSink parent; + + GSettings *settings; + + GMainContext *context; + GMainLoop *loop; + gulong changed_id; + + GstGSettingsAudioSinkProfile profile; + gchar *gsettings_str; +} GstGSettingsAudioSink; + +typedef struct _GstGSettingsAudioSinkClass { + GstSwitchSinkClass parent_class; +} GstGSettingsAudioSinkClass; + +GType gst_gsettings_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_GSETTINGS_AUDIO_SINK_H__ */ diff --git a/ext/gsettings/gstgsettingsaudiosrc.c b/ext/gsettings/gstgsettingsaudiosrc.c new file mode 100644 index 0000000..1eef412 --- /dev/null +++ b/ext/gsettings/gstgsettingsaudiosrc.c @@ -0,0 +1,245 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:element-gsettingsaudiosrc + * + * This element outputs sound to the audiosrc that has been configured in + * GSettings by the user. + * + * + * Example launch line + * |[ + * gst-launch gsettingsaudiosrc ! audioconvert ! audioresample ! autoaudiosink + * ]| Play from configured audiosrc + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "gstgsettingsaudiosrc.h" +#include "gstgsettings.h" + +GST_BOILERPLATE (GstGSettingsAudioSrc, gst_gsettings_audio_src, GstSwitchSrc, + GST_TYPE_SWITCH_SRC); + +static gboolean +gst_gsettings_audio_src_change_child (GstGSettingsAudioSrc * src) +{ + gchar *new_string; + GError *err = NULL; + GstElement *new_kid; + + GST_OBJECT_LOCK (src); + new_string = + g_settings_get_string (src->settings, GST_GSETTINGS_KEY_AUDIOSRC); + + if (new_string != NULL && src->gsettings_str != NULL && + (strlen (new_string) == 0 || + strcmp (src->gsettings_str, new_string) == 0)) { + g_free (new_string); + GST_DEBUG_OBJECT (src, + "GSettings key was updated, but it didn't change. Ignoring"); + GST_OBJECT_UNLOCK (src); + return TRUE; + } + GST_OBJECT_UNLOCK (src); + + GST_DEBUG_OBJECT (src, "GSettings key changed from '%s' to '%s'", + GST_STR_NULL (src->gsettings_str), GST_STR_NULL (new_string)); + + if (new_string) { + new_kid = gst_parse_bin_from_description (new_string, TRUE, &err); + if (err) { + GST_ERROR_OBJECT ("error creating bin '%s': %s", new_string, + err->message); + g_error_free (err); + } + } else { + new_kid = NULL; + } + + if (new_kid == NULL) { + GST_ELEMENT_ERROR (src, LIBRARY, SETTINGS, (NULL), + ("Failed to render audio src from GSettings")); + goto fail; + } + + if (!gst_switch_src_set_child (GST_SWITCH_SRC (src), new_kid)) { + GST_WARNING_OBJECT (src, "Failed to update child element"); + goto fail; + } + + g_free (src->gsettings_str); + src->gsettings_str = new_string; + + return TRUE; + +fail: + g_free (new_string); + return FALSE; +} + +static void +on_changed (GSettings * settings, gchar * key, GstGSettingsAudioSrc * src) +{ + if (!g_str_equal (key, "audiosrc")); + return; + + gst_gsettings_audio_src_change_child (src); +} + +static gboolean +gst_gsettings_audio_src_start (GstGSettingsAudioSrc * src) +{ + GError *err = NULL; + GThread *thread; + + src->loop = g_main_loop_new (src->context, FALSE); + + thread = + g_thread_create ((GThreadFunc) g_main_loop_run, src->loop, FALSE, &err); + if (!thread) { + GST_ELEMENT_ERROR (src, CORE, STATE_CHANGE, (NULL), + ("Failed to create new thread: %s", err->message)); + g_error_free (err); + g_main_loop_unref (src->loop); + src->loop = NULL; + return FALSE; + } + + g_main_context_push_thread_default (src->context); + src->settings = g_settings_new (GST_GSETTINGS_SCHEMA); + src->changed_id = + g_signal_connect_data (G_OBJECT (src->settings), "changed", + G_CALLBACK (on_changed), gst_object_ref (src), + (GClosureNotify) gst_object_unref, 0); + g_main_context_pop_thread_default (src->context); + + return TRUE; +} + +static gboolean +gst_gsettings_audio_src_reset (GstGSettingsAudioSrc * src) +{ + gst_switch_src_set_child (GST_SWITCH_SRC (src), NULL); + + if (src->changed_id) { + g_signal_handler_disconnect (src->settings, src->changed_id); + src->changed_id = 0; + } + + if (src->loop) { + g_main_loop_quit (src->loop); + g_main_loop_unref (src->loop); + src->loop = NULL; + } + + if (src->settings) { + g_object_unref (src->settings); + src->settings = NULL; + } + + GST_OBJECT_LOCK (src); + g_free (src->gsettings_str); + src->gsettings_str = NULL; + GST_OBJECT_UNLOCK (src); + + return TRUE; +} + +static void +gst_gsettings_audio_src_finalize (GObject * object) +{ + GstGSettingsAudioSrc *src = GST_GSETTINGS_AUDIO_SRC (object); + + g_free (src->gsettings_str); + g_main_context_unref (src->context); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, ((GObject *) (src))); +} + +static GstStateChangeReturn +gst_gsettings_audio_src_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstGSettingsAudioSrc *src = GST_GSETTINGS_AUDIO_SRC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_gsettings_audio_src_start (src)) + return GST_STATE_CHANGE_FAILURE; + + if (!gst_gsettings_audio_src_change_child (src)) { + gst_gsettings_audio_src_reset (src); + return GST_STATE_CHANGE_FAILURE; + } + break; + default: + break; + } + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_SUCCESS); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_gsettings_audio_src_reset (src); + break; + default: + break; + } + + return ret; +} + +static void +gst_gsettings_audio_src_init (GstGSettingsAudioSrc * src, + GstGSettingsAudioSrcClass * g_class) +{ + src->context = g_main_context_new (); + gst_gsettings_audio_src_reset (src); +} + +static void +gst_gsettings_audio_src_base_init (gpointer klass) +{ + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details_simple (eklass, "GSettings audio src", + "Src/Audio", + "Audio src embedding the GSettings preferences for audio input", + "Sebastian Dröge "); +} + +static void +gst_gsettings_audio_src_class_init (GstGSettingsAudioSrcClass * klass) +{ + GObjectClass *oklass = G_OBJECT_CLASS (klass); + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + oklass->finalize = gst_gsettings_audio_src_finalize; + + eklass->change_state = gst_gsettings_audio_src_change_state; +} diff --git a/ext/gsettings/gstgsettingsaudiosrc.h b/ext/gsettings/gstgsettingsaudiosrc.h new file mode 100644 index 0000000..affecf6 --- /dev/null +++ b/ext/gsettings/gstgsettingsaudiosrc.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_GSETTINGS_AUDIO_SRC_H__ +#define __GST_GSETTINGS_AUDIO_SRC_H__ + +#include +#include +#include "gstswitchsrc.h" + +G_BEGIN_DECLS + +#define GST_TYPE_GSETTINGS_AUDIO_SRC \ + (gst_gsettings_audio_src_get_type ()) +#define GST_GSETTINGS_AUDIO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GSETTINGS_AUDIO_SRC, \ + GstGSettingsAudioSrc)) +#define GST_GSETTINGS_AUDIO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GSETTINGS_AUDIO_SRC, \ + GstGSettingsAudioSrcClass)) +#define GST_IS_GSETTINGS_AUDIO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_GSETTINGS_AUDIO_SRC)) +#define GST_IS_GSETTINGS_AUDIO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GSETTINGS_AUDIO_SRC)) + +typedef struct _GstGSettingsAudioSrc { + GstSwitchSrc parent; + + GSettings *settings; + + GMainContext *context; + GMainLoop *loop; + gulong changed_id; + + gchar *gsettings_str; +} GstGSettingsAudioSrc; + +typedef struct _GstGSettingsAudioSrcClass { + GstSwitchSrcClass parent_class; +} GstGSettingsAudioSrcClass; + +GType gst_gsettings_audio_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_GSETTINGS_AUDIO_SRC_H__ */ diff --git a/ext/gsettings/gstgsettingsvideosink.c b/ext/gsettings/gstgsettingsvideosink.c new file mode 100644 index 0000000..f862d1b --- /dev/null +++ b/ext/gsettings/gstgsettingsvideosink.c @@ -0,0 +1,245 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:element-gsettingsvideosink + * + * This element outputs sound to the videosink that has been configured in + * GSettings by the user. + * + * + * Example launch line + * |[ + * gst-launch videotestsrc ! ffmpegcolorspace ! videoscale ! gsettingsvideosink + * ]| Play on configured videosink + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "gstgsettingsvideosink.h" +#include "gstgsettings.h" + +GST_BOILERPLATE (GstGSettingsVideoSink, gst_gsettings_video_sink, GstSwitchSink, + GST_TYPE_SWITCH_SINK); + +static gboolean +gst_gsettings_video_sink_change_child (GstGSettingsVideoSink * sink) +{ + gchar *new_string; + GError *err = NULL; + GstElement *new_kid; + + GST_OBJECT_LOCK (sink); + new_string = + g_settings_get_string (sink->settings, GST_GSETTINGS_KEY_VIDEOSINK); + + if (new_string != NULL && sink->gsettings_str != NULL && + (strlen (new_string) == 0 || + strcmp (sink->gsettings_str, new_string) == 0)) { + g_free (new_string); + GST_DEBUG_OBJECT (sink, + "GSettings key was updated, but it didn't change. Ignoring"); + GST_OBJECT_UNLOCK (sink); + return TRUE; + } + GST_OBJECT_UNLOCK (sink); + + GST_DEBUG_OBJECT (sink, "GSettings key changed from '%s' to '%s'", + GST_STR_NULL (sink->gsettings_str), GST_STR_NULL (new_string)); + + if (new_string) { + new_kid = gst_parse_bin_from_description (new_string, TRUE, &err); + if (err) { + GST_ERROR_OBJECT ("error creating bin '%s': %s", new_string, + err->message); + g_error_free (err); + } + } else { + new_kid = NULL; + } + + if (new_kid == NULL) { + GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL), + ("Failed to render video sink from GSettings")); + goto fail; + } + + if (!gst_switch_sink_set_child (GST_SWITCH_SINK (sink), new_kid)) { + GST_WARNING_OBJECT (sink, "Failed to update child element"); + goto fail; + } + + g_free (sink->gsettings_str); + sink->gsettings_str = new_string; + + return TRUE; + +fail: + g_free (new_string); + return FALSE; +} + +static void +on_changed (GSettings * settings, gchar * key, GstGSettingsVideoSink * sink) +{ + if (!g_str_has_suffix (key, "videosink")); + return; + + gst_gsettings_video_sink_change_child (sink); +} + +static gboolean +gst_gsettings_video_sink_start (GstGSettingsVideoSink * sink) +{ + GError *err = NULL; + GThread *thread; + + sink->loop = g_main_loop_new (sink->context, FALSE); + + thread = + g_thread_create ((GThreadFunc) g_main_loop_run, sink->loop, FALSE, &err); + if (!thread) { + GST_ELEMENT_ERROR (sink, CORE, STATE_CHANGE, (NULL), + ("Failed to create new thread: %s", err->message)); + g_error_free (err); + g_main_loop_unref (sink->loop); + sink->loop = NULL; + return FALSE; + } + + g_main_context_push_thread_default (sink->context); + sink->settings = g_settings_new (GST_GSETTINGS_SCHEMA); + sink->changed_id = + g_signal_connect_data (G_OBJECT (sink->settings), "changed", + G_CALLBACK (on_changed), gst_object_ref (sink), + (GClosureNotify) gst_object_unref, 0); + g_main_context_pop_thread_default (sink->context); + + return TRUE; +} + +static gboolean +gst_gsettings_video_sink_reset (GstGSettingsVideoSink * sink) +{ + gst_switch_sink_set_child (GST_SWITCH_SINK (sink), NULL); + + if (sink->changed_id) { + g_signal_handler_disconnect (sink->settings, sink->changed_id); + sink->changed_id = 0; + } + + if (sink->loop) { + g_main_loop_quit (sink->loop); + g_main_loop_unref (sink->loop); + sink->loop = NULL; + } + + if (sink->settings) { + g_object_unref (sink->settings); + sink->settings = NULL; + } + + GST_OBJECT_LOCK (sink); + g_free (sink->gsettings_str); + sink->gsettings_str = NULL; + GST_OBJECT_UNLOCK (sink); + + return TRUE; +} + +static void +gst_gsettings_video_sink_finalize (GObject * object) +{ + GstGSettingsVideoSink *sink = GST_GSETTINGS_VIDEO_SINK (object); + + g_free (sink->gsettings_str); + g_main_context_unref (sink->context); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, ((GObject *) (sink))); +} + +static GstStateChangeReturn +gst_gsettings_video_sink_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstGSettingsVideoSink *sink = GST_GSETTINGS_VIDEO_SINK (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_gsettings_video_sink_start (sink)) + return GST_STATE_CHANGE_FAILURE; + + if (!gst_gsettings_video_sink_change_child (sink)) { + gst_gsettings_video_sink_reset (sink); + return GST_STATE_CHANGE_FAILURE; + } + break; + default: + break; + } + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_SUCCESS); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_gsettings_video_sink_reset (sink); + break; + default: + break; + } + + return ret; +} + +static void +gst_gsettings_video_sink_init (GstGSettingsVideoSink * sink, + GstGSettingsVideoSinkClass * g_class) +{ + sink->context = g_main_context_new (); + gst_gsettings_video_sink_reset (sink); +} + +static void +gst_gsettings_video_sink_base_init (gpointer klass) +{ + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details_simple (eklass, "GSettings video sink", + "Sink/Video", + "Video sink embedding the GSettings preferences for video input", + "Sebastian Dröge "); +} + +static void +gst_gsettings_video_sink_class_init (GstGSettingsVideoSinkClass * klass) +{ + GObjectClass *oklass = G_OBJECT_CLASS (klass); + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + oklass->finalize = gst_gsettings_video_sink_finalize; + + eklass->change_state = gst_gsettings_video_sink_change_state; +} diff --git a/ext/gsettings/gstgsettingsvideosink.h b/ext/gsettings/gstgsettingsvideosink.h new file mode 100644 index 0000000..d4cdf5d --- /dev/null +++ b/ext/gsettings/gstgsettingsvideosink.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_GSETTINGS_VIDEO_SINK_H__ +#define __GST_GSETTINGS_VIDEO_SINK_H__ + +#include +#include +#include "gstswitchsink.h" + +G_BEGIN_DECLS + +#define GST_TYPE_GSETTINGS_VIDEO_SINK \ + (gst_gsettings_video_sink_get_type ()) +#define GST_GSETTINGS_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GSETTINGS_VIDEO_SINK, \ + GstGSettingsVideoSink)) +#define GST_GSETTINGS_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GSETTINGS_VIDEO_SINK, \ + GstGSettingsVideoSinkClass)) +#define GST_IS_GSETTINGS_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_GSETTINGS_VIDEO_SINK)) +#define GST_IS_GSETTINGS_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GSETTINGS_VIDEO_SINK)) + +typedef struct _GstGSettingsVideoSink { + GstSwitchSink parent; + + GSettings *settings; + + GMainContext *context; + GMainLoop *loop; + gulong changed_id; + + gchar *gsettings_str; +} GstGSettingsVideoSink; + +typedef struct _GstGSettingsVideoSinkClass { + GstSwitchSinkClass parent_class; +} GstGSettingsVideoSinkClass; + +GType gst_gsettings_video_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_GSETTINGS_VIDEO_SINK_H__ */ diff --git a/ext/gsettings/gstgsettingsvideosrc.c b/ext/gsettings/gstgsettingsvideosrc.c new file mode 100644 index 0000000..7eeaabe --- /dev/null +++ b/ext/gsettings/gstgsettingsvideosrc.c @@ -0,0 +1,245 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:element-gsettingsvideosrc + * + * This element outputs sound to the videosrc that has been configured in + * GSettings by the user. + * + * + * Example launch line + * |[ + * gst-launch gsettingsvideosrc ! ffmpegcolorspace ! videoscale ! autovideosink + * ]| Play from configured videosrc + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "gstgsettingsvideosrc.h" +#include "gstgsettings.h" + +GST_BOILERPLATE (GstGSettingsVideoSrc, gst_gsettings_video_src, GstSwitchSrc, + GST_TYPE_SWITCH_SRC); + +static gboolean +gst_gsettings_video_src_change_child (GstGSettingsVideoSrc * src) +{ + gchar *new_string; + GError *err = NULL; + GstElement *new_kid; + + GST_OBJECT_LOCK (src); + new_string = + g_settings_get_string (src->settings, GST_GSETTINGS_KEY_VIDEOSRC); + + if (new_string != NULL && src->gsettings_str != NULL && + (strlen (new_string) == 0 || + strcmp (src->gsettings_str, new_string) == 0)) { + g_free (new_string); + GST_DEBUG_OBJECT (src, + "GSettings key was updated, but it didn't change. Ignoring"); + GST_OBJECT_UNLOCK (src); + return TRUE; + } + GST_OBJECT_UNLOCK (src); + + GST_DEBUG_OBJECT (src, "GSettings key changed from '%s' to '%s'", + GST_STR_NULL (src->gsettings_str), GST_STR_NULL (new_string)); + + if (new_string) { + new_kid = gst_parse_bin_from_description (new_string, TRUE, &err); + if (err) { + GST_ERROR_OBJECT ("error creating bin '%s': %s", new_string, + err->message); + g_error_free (err); + } + } else { + new_kid = NULL; + } + + if (new_kid == NULL) { + GST_ELEMENT_ERROR (src, LIBRARY, SETTINGS, (NULL), + ("Failed to render video src from GSettings")); + goto fail; + } + + if (!gst_switch_src_set_child (GST_SWITCH_SRC (src), new_kid)) { + GST_WARNING_OBJECT (src, "Failed to update child element"); + goto fail; + } + + g_free (src->gsettings_str); + src->gsettings_str = new_string; + + return TRUE; + +fail: + g_free (new_string); + return FALSE; +} + +static void +on_changed (GSettings * settings, gchar * key, GstGSettingsVideoSrc * src) +{ + if (!g_str_equal (key, "videosrc")); + return; + + gst_gsettings_video_src_change_child (src); +} + +static gboolean +gst_gsettings_video_src_start (GstGSettingsVideoSrc * src) +{ + GError *err = NULL; + GThread *thread; + + src->loop = g_main_loop_new (src->context, FALSE); + + thread = + g_thread_create ((GThreadFunc) g_main_loop_run, src->loop, FALSE, &err); + if (!thread) { + GST_ELEMENT_ERROR (src, CORE, STATE_CHANGE, (NULL), + ("Failed to create new thread: %s", err->message)); + g_error_free (err); + g_main_loop_unref (src->loop); + src->loop = NULL; + return FALSE; + } + + g_main_context_push_thread_default (src->context); + src->settings = g_settings_new (GST_GSETTINGS_SCHEMA); + src->changed_id = + g_signal_connect_data (G_OBJECT (src->settings), "changed", + G_CALLBACK (on_changed), gst_object_ref (src), + (GClosureNotify) gst_object_unref, 0); + g_main_context_pop_thread_default (src->context); + + return TRUE; +} + +static gboolean +gst_gsettings_video_src_reset (GstGSettingsVideoSrc * src) +{ + gst_switch_src_set_child (GST_SWITCH_SRC (src), NULL); + + if (src->changed_id) { + g_signal_handler_disconnect (src->settings, src->changed_id); + src->changed_id = 0; + } + + if (src->loop) { + g_main_loop_quit (src->loop); + g_main_loop_unref (src->loop); + src->loop = NULL; + } + + if (src->settings) { + g_object_unref (src->settings); + src->settings = NULL; + } + + GST_OBJECT_LOCK (src); + g_free (src->gsettings_str); + src->gsettings_str = NULL; + GST_OBJECT_UNLOCK (src); + + return TRUE; +} + +static void +gst_gsettings_video_src_finalize (GObject * object) +{ + GstGSettingsVideoSrc *src = GST_GSETTINGS_VIDEO_SRC (object); + + g_free (src->gsettings_str); + g_main_context_unref (src->context); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, ((GObject *) (src))); +} + +static GstStateChangeReturn +gst_gsettings_video_src_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstGSettingsVideoSrc *src = GST_GSETTINGS_VIDEO_SRC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_gsettings_video_src_start (src)) + return GST_STATE_CHANGE_FAILURE; + + if (!gst_gsettings_video_src_change_child (src)) { + gst_gsettings_video_src_reset (src); + return GST_STATE_CHANGE_FAILURE; + } + break; + default: + break; + } + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_SUCCESS); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_gsettings_video_src_reset (src); + break; + default: + break; + } + + return ret; +} + +static void +gst_gsettings_video_src_init (GstGSettingsVideoSrc * src, + GstGSettingsVideoSrcClass * g_class) +{ + src->context = g_main_context_new (); + gst_gsettings_video_src_reset (src); +} + +static void +gst_gsettings_video_src_base_init (gpointer klass) +{ + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details_simple (eklass, "GSettings video src", + "Src/Video", + "Video src embedding the GSettings preferences for video input", + "Sebastian Dröge "); +} + +static void +gst_gsettings_video_src_class_init (GstGSettingsVideoSrcClass * klass) +{ + GObjectClass *oklass = G_OBJECT_CLASS (klass); + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + + oklass->finalize = gst_gsettings_video_src_finalize; + + eklass->change_state = gst_gsettings_video_src_change_state; +} diff --git a/ext/gsettings/gstgsettingsvideosrc.h b/ext/gsettings/gstgsettingsvideosrc.h new file mode 100644 index 0000000..9718865 --- /dev/null +++ b/ext/gsettings/gstgsettingsvideosrc.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_GSETTINGS_VIDEO_SRC_H__ +#define __GST_GSETTINGS_VIDEO_SRC_H__ + +#include +#include +#include "gstswitchsrc.h" + +G_BEGIN_DECLS + +#define GST_TYPE_GSETTINGS_VIDEO_SRC \ + (gst_gsettings_video_src_get_type ()) +#define GST_GSETTINGS_VIDEO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GSETTINGS_VIDEO_SRC, \ + GstGSettingsVideoSrc)) +#define GST_GSETTINGS_VIDEO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GSETTINGS_VIDEO_SRC, \ + GstGSettingsVideoSrcClass)) +#define GST_IS_GSETTINGS_VIDEO_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_GSETTINGS_VIDEO_SRC)) +#define GST_IS_GSETTINGS_VIDEO_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GSETTINGS_VIDEO_SRC)) + +typedef struct _GstGSettingsVideoSrc { + GstSwitchSrc parent; + + GSettings *settings; + + GMainContext *context; + GMainLoop *loop; + gulong changed_id; + + gchar *gsettings_str; +} GstGSettingsVideoSrc; + +typedef struct _GstGSettingsVideoSrcClass { + GstSwitchSrcClass parent_class; +} GstGSettingsVideoSrcClass; + +GType gst_gsettings_video_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_GSETTINGS_VIDEO_SRC_H__ */ diff --git a/ext/gsettings/gstswitchsink.c b/ext/gsettings/gstswitchsink.c new file mode 100644 index 0000000..1fccf68 --- /dev/null +++ b/ext/gsettings/gstswitchsink.c @@ -0,0 +1,270 @@ +/* GStreamer + * Copyright (c) 2005 Ronald S. Bultje + * Copyright (c) 2006 Jürg Billeter + * Copyright (c) 2007 Jan Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstswitchsink.h" + +GST_DEBUG_CATEGORY_STATIC (switch_debug); +#define GST_CAT_DEFAULT switch_debug + +static void gst_switch_sink_dispose (GObject * object); +static GstStateChangeReturn +gst_switch_sink_change_state (GstElement * element, GstStateChange transition); + +enum +{ + PROP_0 +}; + +GST_BOILERPLATE (GstSwitchSink, gst_switch_sink, GstBin, GST_TYPE_BIN); + +static void +gst_switch_sink_base_init (gpointer klass) +{ + GST_DEBUG_CATEGORY_INIT (switch_debug, "switchsink", 0, "switchsink element"); +} + +static void +gst_switch_sink_class_init (GstSwitchSinkClass * klass) +{ + GObjectClass *oklass = G_OBJECT_CLASS (klass); + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + GstPadTemplate *child_pad_templ; + + oklass->dispose = gst_switch_sink_dispose; + eklass->change_state = gst_switch_sink_change_state; + + /* Provide a default pad template if the child didn't */ + child_pad_templ = gst_element_class_get_pad_template (eklass, "sink"); + if (child_pad_templ == NULL) { + gst_element_class_add_pad_template (eklass, + gst_static_pad_template_get (&sink_template)); + } +} + +static gboolean +gst_switch_sink_reset (GstSwitchSink * sink) +{ + /* this will install fakesink if no other child has been set, + * otherwise we rely on the subclass to know when to unset its + * custom kid */ + if (sink->kid == NULL) { + return gst_switch_sink_set_child (sink, NULL); + } + + return TRUE; +} + +static void +gst_switch_sink_init (GstSwitchSink * sink, GstSwitchSinkClass * g_class) +{ + GstElementClass *eklass = GST_ELEMENT_GET_CLASS (sink); + GstPadTemplate *templ; + + templ = gst_element_class_get_pad_template (eklass, "sink"); + sink->pad = gst_ghost_pad_new_no_target_from_template ("sink", templ); + gst_element_add_pad (GST_ELEMENT (sink), sink->pad); + + gst_switch_sink_reset (sink); + + GST_OBJECT_FLAG_SET (sink, GST_ELEMENT_IS_SINK); +} + +static void +gst_switch_sink_dispose (GObject * object) +{ + GstSwitchSink *sink = GST_SWITCH_SINK (object); + GstObject *new_kid, *kid; + + GST_OBJECT_LOCK (sink); + new_kid = GST_OBJECT_CAST (sink->new_kid); + sink->new_kid = NULL; + + kid = GST_OBJECT_CAST (sink->kid); + sink->kid = NULL; + GST_OBJECT_UNLOCK (sink); + + gst_object_replace (&new_kid, NULL); + gst_object_replace (&kid, NULL); + + GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +static gboolean +gst_switch_sink_commit_new_kid (GstSwitchSink * sink) +{ + GstPad *targetpad; + GstState kid_state; + GstElement *new_kid, *old_kid; + gboolean is_fakesink = FALSE; + GstBus *bus; + + /* need locking around member accesses */ + GST_OBJECT_LOCK (sink); + /* If we're currently changing state, set the child to the next state + * we're transitioning too, rather than our current state which is + * about to change */ + if (GST_STATE_NEXT (sink) != GST_STATE_VOID_PENDING) + kid_state = GST_STATE_NEXT (sink); + else + kid_state = GST_STATE (sink); + + new_kid = sink->new_kid ? gst_object_ref (sink->new_kid) : NULL; + sink->new_kid = NULL; + GST_OBJECT_UNLOCK (sink); + + /* Fakesink by default if NULL is passed as the new child */ + if (new_kid == NULL) { + GST_DEBUG_OBJECT (sink, "Replacing kid with fakesink"); + new_kid = gst_element_factory_make ("fakesink", "testsink"); + if (new_kid == NULL) { + GST_ERROR_OBJECT (sink, "Failed to create fakesink"); + return FALSE; + } + /* Add a reference, as it would if the element came from sink->new_kid */ + gst_object_ref (new_kid); + g_object_set (new_kid, "sync", TRUE, NULL); + is_fakesink = TRUE; + } else { + GST_DEBUG_OBJECT (sink, "Setting new kid"); + } + + /* set temporary bus of our own to catch error messages from the child + * (could we just set our own bus on it, or would the state change messages + * from the not-yet-added element confuse the state change algorithm? Let's + * play it safe for now) */ + bus = gst_bus_new (); + gst_element_set_bus (new_kid, bus); + gst_object_unref (bus); + + if (gst_element_set_state (new_kid, kid_state) == GST_STATE_CHANGE_FAILURE) { + GstMessage *msg; + + /* check if child posted an error message and if so re-post it on our bus + * so that the application gets to see a decent error and not our generic + * fallback error message which is completely indecipherable to the user */ + msg = gst_bus_pop_filtered (GST_ELEMENT_BUS (new_kid), GST_MESSAGE_ERROR); + if (msg) { + GST_INFO_OBJECT (sink, "Forwarding kid error: %" GST_PTR_FORMAT, msg); + gst_element_post_message (GST_ELEMENT (sink), msg); + } + GST_ELEMENT_ERROR (sink, CORE, STATE_CHANGE, (NULL), + ("Failed to set state on new child.")); + gst_element_set_bus (new_kid, NULL); + gst_object_unref (new_kid); + return FALSE; + } + gst_element_set_bus (new_kid, NULL); + gst_bin_add (GST_BIN (sink), new_kid); + + /* Now, replace the existing child */ + GST_OBJECT_LOCK (sink); + old_kid = sink->kid; + sink->kid = new_kid; + /* Mark whether a custom kid or fakesink has been installed */ + sink->have_kid = !is_fakesink; + GST_OBJECT_UNLOCK (sink); + + /* kill old element */ + if (old_kid) { + GST_DEBUG_OBJECT (sink, "Removing old kid %" GST_PTR_FORMAT, old_kid); + gst_element_set_state (old_kid, GST_STATE_NULL); + gst_bin_remove (GST_BIN (sink), old_kid); + gst_object_unref (old_kid); + /* Don't lose the SINK flag */ + GST_OBJECT_FLAG_SET (sink, GST_ELEMENT_IS_SINK); + } + + /* re-attach ghostpad */ + GST_DEBUG_OBJECT (sink, "Creating new ghostpad"); + targetpad = gst_element_get_static_pad (sink->kid, "sink"); + gst_ghost_pad_set_target (GST_GHOST_PAD (sink->pad), targetpad); + gst_object_unref (targetpad); + GST_DEBUG_OBJECT (sink, "done changing child of switchsink"); + + /* FIXME: Push new-segment info and pre-roll buffer(s) into the kid */ + + return TRUE; +} + +gboolean +gst_switch_sink_set_child (GstSwitchSink * sink, GstElement * new_kid) +{ + GstState cur, next; + GstElement **p_kid; + + /* Nothing to do if clearing the child and we've already installed fakesink */ + if (new_kid == NULL && sink->kid != NULL && sink->have_kid == FALSE) + return TRUE; + + /* Store the new kid to be committed later */ + GST_OBJECT_LOCK (sink); + cur = GST_STATE (sink); + next = GST_STATE_NEXT (sink); + p_kid = &sink->new_kid; + gst_object_replace ((GstObject **) p_kid, (GstObject *) new_kid); + GST_OBJECT_UNLOCK (sink); + if (new_kid) + gst_object_unref (new_kid); + + /* Sometime, it would be lovely to allow sink changes even when + * already running, but this involves sending an appropriate new-segment + * and possibly prerolling etc */ + /* FIXME: Block the pad and replace the kid when it completes */ + if (cur > GST_STATE_READY || next == GST_STATE_PAUSED) { + GST_DEBUG_OBJECT (sink, + "Switch-sink is already running. Ignoring change of child."); + gst_object_unref (new_kid); + return TRUE; + } + + return gst_switch_sink_commit_new_kid (sink); +} + +static GstStateChangeReturn +gst_switch_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstSwitchSink *sink = GST_SWITCH_SINK (element); + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_SUCCESS); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + if (!gst_switch_sink_reset (sink)) + ret = GST_STATE_CHANGE_FAILURE; + break; + default: + break; + } + + return ret; +} diff --git a/ext/gsettings/gstswitchsink.h b/ext/gsettings/gstswitchsink.h new file mode 100644 index 0000000..556e755 --- /dev/null +++ b/ext/gsettings/gstswitchsink.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (c) 2005 Ronald S. Bultje + * Copyright (c) 2007 Jan Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_SWITCH_SINK_H__ +#define __GST_SWITCH_SINK_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SWITCH_SINK \ + (gst_switch_sink_get_type ()) +#define GST_SWITCH_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SWITCH_SINK, \ + GstSwitchSink)) +#define GST_SWITCH_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SWITCH_SINK, \ + GstSwitchSinkClass)) +#define GST_IS_SWITCH_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SWITCH_SINK)) +#define GST_IS_SWITCH_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SWITCH_SINK)) + +typedef struct _GstSwitchSink { + GstBin parent; + + GstElement *kid; + GstElement *new_kid; + GstPad *pad; + + /* If a custom child has been set... */ + gboolean have_kid; +} GstSwitchSink; + +typedef struct _GstSwitchSinkClass { + GstBinClass parent_class; +} GstSwitchSinkClass; + +GType gst_switch_sink_get_type (void); + +gboolean gst_switch_sink_set_child (GstSwitchSink *ssink, GstElement *new_kid); + +G_END_DECLS + +#endif /* __GST_SWITCH_SINK_H__ */ diff --git a/ext/gsettings/gstswitchsrc.c b/ext/gsettings/gstswitchsrc.c new file mode 100644 index 0000000..61ab31f --- /dev/null +++ b/ext/gsettings/gstswitchsrc.c @@ -0,0 +1,258 @@ +/* GStreamer + * Copyright (c) 2005 Ronald S. Bultje + * Copyright (c) 2006 Jürg Billeter + * Copyright (c) 2007 Jan Schmidt + * Copyright (c) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstswitchsrc.h" + +GST_DEBUG_CATEGORY_STATIC (switch_debug); +#define GST_CAT_DEFAULT switch_debug + +static void gst_switch_src_dispose (GObject * object); +static GstStateChangeReturn +gst_switch_src_change_state (GstElement * element, GstStateChange transition); + +GST_BOILERPLATE (GstSwitchSrc, gst_switch_src, GstBin, GST_TYPE_BIN); + +static void +gst_switch_src_base_init (gpointer klass) +{ + GST_DEBUG_CATEGORY_INIT (switch_debug, "switchsrc", 0, "switchsrc element"); +} + +static void +gst_switch_src_class_init (GstSwitchSrcClass * klass) +{ + GObjectClass *oklass = G_OBJECT_CLASS (klass); + GstElementClass *eklass = GST_ELEMENT_CLASS (klass); + static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + GstPadTemplate *child_pad_templ; + + oklass->dispose = gst_switch_src_dispose; + eklass->change_state = gst_switch_src_change_state; + + /* Provide a default pad template if the child didn't */ + child_pad_templ = gst_element_class_get_pad_template (eklass, "src"); + if (child_pad_templ == NULL) { + gst_element_class_add_pad_template (eklass, + gst_static_pad_template_get (&src_template)); + } +} + +static gboolean +gst_switch_src_reset (GstSwitchSrc * src) +{ + /* this will install fakesrc if no other child has been set, + * otherwise we rely on the subclass to know when to unset its + * custom kid */ + if (src->kid == NULL) { + return gst_switch_src_set_child (src, NULL); + } + + return TRUE; +} + +static void +gst_switch_src_init (GstSwitchSrc * src, GstSwitchSrcClass * g_class) +{ + GstElementClass *eklass = GST_ELEMENT_GET_CLASS (src); + GstPadTemplate *templ; + + templ = gst_element_class_get_pad_template (eklass, "src"); + src->pad = gst_ghost_pad_new_no_target_from_template ("src", templ); + gst_element_add_pad (GST_ELEMENT (src), src->pad); + + gst_switch_src_reset (src); +} + +static void +gst_switch_src_dispose (GObject * object) +{ + GstSwitchSrc *src = GST_SWITCH_SRC (object); + GstObject *new_kid, *kid; + + GST_OBJECT_LOCK (src); + new_kid = GST_OBJECT_CAST (src->new_kid); + src->new_kid = NULL; + + kid = GST_OBJECT_CAST (src->kid); + src->kid = NULL; + GST_OBJECT_UNLOCK (src); + + gst_object_replace (&new_kid, NULL); + gst_object_replace (&kid, NULL); + + GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +static gboolean +gst_switch_src_commit_new_kid (GstSwitchSrc * src) +{ + GstPad *targetpad; + GstState kid_state; + GstElement *new_kid, *old_kid; + gboolean is_fakesrc = FALSE; + GstBus *bus; + + /* need locking around member accesses */ + GST_OBJECT_LOCK (src); + /* If we're currently changing state, set the child to the next state + * we're transitioning too, rather than our current state which is + * about to change */ + if (GST_STATE_NEXT (src) != GST_STATE_VOID_PENDING) + kid_state = GST_STATE_NEXT (src); + else + kid_state = GST_STATE (src); + + new_kid = src->new_kid ? gst_object_ref (src->new_kid) : NULL; + src->new_kid = NULL; + GST_OBJECT_UNLOCK (src); + + /* Fakesrc by default if NULL is passed as the new child */ + if (new_kid == NULL) { + GST_DEBUG_OBJECT (src, "Replacing kid with fakesrc"); + new_kid = gst_element_factory_make ("fakesrc", "testsrc"); + if (new_kid == NULL) { + GST_ERROR_OBJECT (src, "Failed to create fakesrc"); + return FALSE; + } + /* Add a reference, as it would if the element came from src->new_kid */ + gst_object_ref (new_kid); + is_fakesrc = TRUE; + } else { + GST_DEBUG_OBJECT (src, "Setting new kid"); + } + + /* set temporary bus of our own to catch error messages from the child + * (could we just set our own bus on it, or would the state change messages + * from the not-yet-added element confuse the state change algorithm? Let's + * play it safe for now) */ + bus = gst_bus_new (); + gst_element_set_bus (new_kid, bus); + gst_object_unref (bus); + + if (gst_element_set_state (new_kid, kid_state) == GST_STATE_CHANGE_FAILURE) { + GstMessage *msg; + + /* check if child posted an error message and if so re-post it on our bus + * so that the application gets to see a decent error and not our generic + * fallback error message which is completely indecipherable to the user */ + msg = gst_bus_pop_filtered (GST_ELEMENT_BUS (new_kid), GST_MESSAGE_ERROR); + if (msg) { + GST_INFO_OBJECT (src, "Forwarding kid error: %" GST_PTR_FORMAT, msg); + gst_element_post_message (GST_ELEMENT (src), msg); + } + GST_ELEMENT_ERROR (src, CORE, STATE_CHANGE, (NULL), + ("Failed to set state on new child.")); + gst_element_set_bus (new_kid, NULL); + gst_object_unref (new_kid); + return FALSE; + } + gst_element_set_bus (new_kid, NULL); + gst_bin_add (GST_BIN (src), new_kid); + + /* Now, replace the existing child */ + GST_OBJECT_LOCK (src); + old_kid = src->kid; + src->kid = new_kid; + /* Mark whether a custom kid or fakesrc has been installed */ + src->have_kid = !is_fakesrc; + GST_OBJECT_UNLOCK (src); + + /* kill old element */ + if (old_kid) { + GST_DEBUG_OBJECT (src, "Removing old kid %" GST_PTR_FORMAT, old_kid); + gst_element_set_state (old_kid, GST_STATE_NULL); + gst_bin_remove (GST_BIN (src), old_kid); + gst_object_unref (old_kid); + } + + /* re-attach ghostpad */ + GST_DEBUG_OBJECT (src, "Creating new ghostpad"); + targetpad = gst_element_get_static_pad (src->kid, "src"); + gst_ghost_pad_set_target (GST_GHOST_PAD (src->pad), targetpad); + gst_object_unref (targetpad); + GST_DEBUG_OBJECT (src, "done changing child of switchsrc"); + + return TRUE; +} + +gboolean +gst_switch_src_set_child (GstSwitchSrc * src, GstElement * new_kid) +{ + GstState cur, next; + GstElement **p_kid; + + /* Nothing to do if clearing the child and we've already installed fakesrc */ + if (new_kid == NULL && src->kid != NULL && src->have_kid == FALSE) + return TRUE; + + /* Store the new kid to be committed later */ + GST_OBJECT_LOCK (src); + cur = GST_STATE (src); + next = GST_STATE_NEXT (src); + p_kid = &src->new_kid; + gst_object_replace ((GstObject **) p_kid, (GstObject *) new_kid); + GST_OBJECT_UNLOCK (src); + if (new_kid) + gst_object_unref (new_kid); + + /* Sometime, it would be lovely to allow src changes even when + * already running */ + /* FIXME: Block the pad and replace the kid when it completes */ + if (cur > GST_STATE_READY || next == GST_STATE_PAUSED) { + GST_DEBUG_OBJECT (src, + "Switch-src is already running. Ignoring change of child."); + gst_object_unref (new_kid); + return TRUE; + } + + return gst_switch_src_commit_new_kid (src); +} + +static GstStateChangeReturn +gst_switch_src_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstSwitchSrc *src = GST_SWITCH_SRC (element); + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_SUCCESS); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + if (!gst_switch_src_reset (src)) + ret = GST_STATE_CHANGE_FAILURE; + break; + default: + break; + } + + return ret; +} diff --git a/ext/gsettings/gstswitchsrc.h b/ext/gsettings/gstswitchsrc.h new file mode 100644 index 0000000..6c550ad --- /dev/null +++ b/ext/gsettings/gstswitchsrc.h @@ -0,0 +1,57 @@ +/* GStreamer + * + * Copyright (c) 2005 Ronald S. Bultje + * Copyright (c) 2005 Tim-Philipp Müller + * Copyright (c) 2007 Jan Schmidt + * Copyright (c) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_SWITCH_SRC_H__ +#define __GST_SWITCH_SRC_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SWITCH_SRC (gst_switch_src_get_type ()) +#define GST_SWITCH_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SWITCH_SRC, GstSwitchSrc)) +#define GST_SWITCH_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SWITCH_SRC, GstSwitchSrcClass)) +#define GST_IS_SWITCH_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SWITCH_SRC)) +#define GST_IS_SWITCH_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SWITCH_SRC)) + +typedef struct _GstSwitchSrc { + GstBin parent; + + GstElement *kid; + GstElement *new_kid; + GstPad *pad; + + /* If a custom child has been set... */ + gboolean have_kid; +} GstSwitchSrc; + +typedef struct _GstSwitchSrcClass { + GstBinClass parent_class; +} GstSwitchSrcClass; + +GType gst_switch_src_get_type (void); +gboolean gst_switch_src_set_child (GstSwitchSrc *ssrc, GstElement *new_kid); + +G_END_DECLS + +#endif /* __GST_SWITCH_SRC_H__ */ diff --git a/ext/gsettings/org.freedesktop.gstreamer.default-elements.gschema.xml.in b/ext/gsettings/org.freedesktop.gstreamer.default-elements.gschema.xml.in new file mode 100644 index 0000000..f10e44b --- /dev/null +++ b/ext/gsettings/org.freedesktop.gstreamer.default-elements.gschema.xml.in @@ -0,0 +1,94 @@ + + + + "@DEFAULT_AUDIOSINK@" + default GStreamer sound events audiosink + GStreamer can play audio using any number of + output elements. Some possible choices are osssink, pulsesink + and alsasink. The audiosink can be a partial pipeline instea + of just one element. + + + "Default" + description for default GStreamer sound events audiosink + Describes the selected audiosink element. + + + "@DEFAULT_AUDIOSINK@" + default GStreamer audiosink for Audio/Video Conferencing + GStreamer can play audio using any number of + output elements. Some possible choices are osssink, pulsesink + and alsasink. The audiosink can be a partial pipeline instea + of just one element. + + + "Default" + description for default GStreamer audiosink for Audio/Video Conferencing + Describes the selected audiosink element. + + + "@DEFAULT_AUDIOSINK@" + default GStreamer audiosink for Music and Movies + GStreamer can play audio using any number of + output elements. Some possible choices are osssink, pulsesink + and alsasink. The audiosink can be a partial pipeline instea + of just one element. + + + "Default" + description for default GStreamer audiosink for Music and Movies + Describes the selected audiosink element. + + + "@DEFAULT_VIDEOSINK@" + default GStreamer videosink + GStreamer can play video using any number of + output elements. Some possible choices are xvimagesink, + ximagesink, sdlvideosink and aasink. The videosink can be + a partial pipeline instead of just one element. + + + "Default" + description for default GStreamer videosink + Describes the selected videosink element. + + + "@DEFAULT_AUDIOSRC@" + default GStreamer audiosrc + GStreamer can record audio using any number of + input elements. Some possible choices are osssrc, pulsesrc and + alsasrc. The audio source can be a partial pipeline instead of + just one element. + + + "Default" + description for default GStreamer audiosrc + Describes the selected audiosrc element. + + + "@DEFAULT_VIDEOSRC@" + default GStreamer videosrc + GStreamer can record video from any number of input elements. + Some possible choices are v4lsrc, v4l2src and videotestsrc. The video + source can be a partial pipeline instead of just one element. + + + "Default" + description for default GStreamer videosrc + Describes the selected videosrc element. + + + "@DEFAULT_VISUALIZER@" + default GStreamer visualization + GStreamer can put visualization plugins in a pipeline to + transform audio streams in video frames. Some possible choices are + goom, goom2k1 and synaesthesia. The visualization plugin can be a + partial pipeline instead of just one element. + + + "Default" + description for default GStreamer visualization + Describes the selected visualization element. + + + diff --git a/ext/gsettings/plugin.c b/ext/gsettings/plugin.c new file mode 100644 index 0000000..6d562e8 --- /dev/null +++ b/ext/gsettings/plugin.c @@ -0,0 +1,50 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "gstgsettingsaudiosrc.h" +#include "gstgsettingsaudiosink.h" +#include "gstgsettingsvideosrc.h" +#include "gstgsettingsvideosink.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "gsettingsaudiosink", GST_RANK_NONE, + GST_TYPE_GSETTINGS_AUDIO_SINK) || + !gst_element_register (plugin, "gsettingsaudiosrc", GST_RANK_NONE, + GST_TYPE_GSETTINGS_AUDIO_SRC) || + !gst_element_register (plugin, "gsettingsvideosink", GST_RANK_NONE, + GST_TYPE_GSETTINGS_VIDEO_SINK) || + !gst_element_register (plugin, "gsettingsvideosrc", GST_RANK_NONE, + GST_TYPE_GSETTINGS_VIDEO_SRC)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "gsettings", + "GSettings plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/po/POTFILES.in b/po/POTFILES.in index 8ba34a2..9a92add 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,4 @@ +ext/gsettings/org.freedesktop.gstreamer.gschema.xml.in ext/resindvd/resindvdsrc.c ext/sndfile/gstsfsink.c ext/sndfile/gstsfsrc.c -- 2.7.4