diff options
Diffstat (limited to 'ext/gconf')
-rw-r--r-- | ext/gconf/Makefile.am | 2 | ||||
-rw-r--r-- | ext/gconf/gconf.c | 48 | ||||
-rw-r--r-- | ext/gconf/gconf.h | 9 | ||||
-rw-r--r-- | ext/gconf/gstgconfaudiosink.c | 174 | ||||
-rw-r--r-- | ext/gconf/gstgconfaudiosink.h | 9 | ||||
-rw-r--r-- | ext/gconf/gstswitchsink.c | 265 | ||||
-rw-r--r-- | ext/gconf/gstswitchsink.h | 68 |
7 files changed, 439 insertions, 136 deletions
diff --git a/ext/gconf/Makefile.am b/ext/gconf/Makefile.am index 942aa283..c11063e8 100644 --- a/ext/gconf/Makefile.am +++ b/ext/gconf/Makefile.am @@ -6,6 +6,7 @@ libgstgconfelements_la_SOURCES = \ gstgconfelements.c \ gstgconfvideosink.c \ gstgconfvideosrc.c \ + gstswitchsink.c \ gconf.c DIR_CFLAGS = -DGST_GCONF_DIR=\"/system/gstreamer/@GST_MAJORMINOR@\" @@ -19,4 +20,5 @@ noinst_HEADERS = \ gstgconfelements.h \ gstgconfvideosink.h \ gstgconfvideosrc.h \ + gstswitchsink.h \ gconf.h diff --git a/ext/gconf/gconf.c b/ext/gconf/gconf.c index 690e6d4b..1f24b97f 100644 --- a/ext/gconf/gconf.c +++ b/ext/gconf/gconf.c @@ -1,4 +1,5 @@ /* GStreamer + * nf_get_default_audio_sink * Copyright (C) <2002> Thomas Vander Stichele <thomas@apestaart.org> * Copyright (C) <2006> Jürg Billeter <j@bitron.ch> * @@ -26,6 +27,8 @@ #include "config.h" #endif +#include <gst/gst.h> + #include "gconf.h" #include "gstgconfelements.h" /* for debug category */ @@ -63,8 +66,12 @@ gst_gconf_get_string (const gchar * key) { GError *error = NULL; gchar *value = NULL; - gchar *full_key = g_strdup_printf ("%s/%s", GST_GCONF_DIR, key); + gchar *full_key; + if (!g_str_has_prefix (key, GST_GCONF_DIR)) + full_key = g_strdup_printf ("%s/%s", GST_GCONF_DIR, key); + else + full_key = g_strdup (key); value = gconf_client_get_string (gst_gconf_get_client (), full_key, &error); g_free (full_key); @@ -79,20 +86,20 @@ gst_gconf_get_string (const gchar * key) } const gchar * -gst_gconf_get_key_for_sink_profile (guint profile) +gst_gconf_get_key_for_sink_profile (GstGConfProfile profile) { switch (profile) { case GCONF_PROFILE_SOUNDS: - return GST_GCONF_AUDIOSINK_KEY; + return GST_GCONF_DIR "/" GST_GCONF_AUDIOSINK_KEY; case GCONF_PROFILE_MUSIC: - return GST_GCONF_MUSIC_AUDIOSINK_KEY; + return GST_GCONF_DIR "/" GST_GCONF_MUSIC_AUDIOSINK_KEY; case GCONF_PROFILE_CHAT: - return GST_GCONF_CHAT_AUDIOSINK_KEY; + return GST_GCONF_DIR "/" GST_GCONF_CHAT_AUDIOSINK_KEY; default: break; } - g_return_val_if_reached (GST_GCONF_AUDIOSINK_KEY); + g_return_val_if_reached (GST_GCONF_DIR "/" GST_GCONF_AUDIOSINK_KEY); } /** @@ -106,7 +113,12 @@ void gst_gconf_set_string (const gchar * key, const gchar * value) { GError *error = NULL; - gchar *full_key = g_strdup_printf ("%s/%s", GST_GCONF_DIR, key); + gchar *full_key; + + if (!g_str_has_prefix (key, GST_GCONF_DIR)) + full_key = g_strdup_printf ("%s/%s", GST_GCONF_DIR, key); + else + full_key = g_strdup (key); gconf_client_set_string (gst_gconf_get_client (), full_key, value, &error); if (error) { @@ -149,27 +161,25 @@ gst_gconf_render_bin_from_key (const gchar * key) } /** - * gst_gconf_get_default_audio_sink: - * @profile: the appropriate application profile. + * gst_gconf_render_bin_with_default: + * @bin: a #gchar string corresponding to pipeline to construct. + * @default: a pipeline description to use as default if the GConf key + * pipeline fails to construct. * - * Render audio output bin from GStreamer GConf key : "default/audiosink". - * If key is invalid, the default audio sink for the platform is used - * (typically osssink or sunaudiosink). + * Render bin from GConf key @key using @default as a fallback. * - * Returns: a #GstElement containing the audio output bin, or NULL if - * everything failed. + * Returns: a #GstElement containing the rendered bin. */ GstElement * -gst_gconf_get_default_audio_sink (int profile) +gst_gconf_render_bin_with_default (const gchar * bin, + const gchar * default_sink) { GstElement *ret; - const gchar *key; - key = gst_gconf_get_key_for_sink_profile (profile); - ret = gst_gconf_render_bin_from_key (key); + ret = gst_element_factory_make (bin, NULL); if (!ret) { - ret = gst_element_factory_make (DEFAULT_AUDIOSINK, NULL); + ret = gst_element_factory_make (default_sink, NULL); if (!ret) g_warning ("No GConf default audio sink key and %s doesn't work", diff --git a/ext/gconf/gconf.h b/ext/gconf/gconf.h index ca802969..4204a29d 100644 --- a/ext/gconf/gconf.h +++ b/ext/gconf/gconf.h @@ -37,12 +37,13 @@ G_BEGIN_DECLS #define GST_GCONF_VIDEOSRC_KEY "default/videosrc" #define GST_GCONF_VIDEOSINK_KEY "default/videosink" -enum +typedef enum { GCONF_PROFILE_SOUNDS, GCONF_PROFILE_MUSIC, - GCONF_PROFILE_CHAT -}; + GCONF_PROFILE_CHAT, + GCONF_PROFILE_NONE /* Internal value only */ +} GstGConfProfile; gchar * gst_gconf_get_string (const gchar *key); void gst_gconf_set_string (const gchar *key, @@ -51,6 +52,8 @@ void gst_gconf_set_string (const gchar *key, const gchar * gst_gconf_get_key_for_sink_profile (guint profile); GstElement * gst_gconf_render_bin_from_key (const gchar *key); +GstElement * gst_gconf_render_bin_with_default (const gchar *bin, + const gchar *default_sink); GstElement * gst_gconf_get_default_video_sink (void); GstElement * gst_gconf_get_default_audio_sink (int profile); diff --git a/ext/gconf/gstgconfaudiosink.c b/ext/gconf/gstgconfaudiosink.c index 375c2ede..25b06e32 100644 --- a/ext/gconf/gstgconfaudiosink.c +++ b/ext/gconf/gstgconfaudiosink.c @@ -28,11 +28,13 @@ #include "gstgconfaudiosink.h" static void gst_gconf_audio_sink_dispose (GObject * object); -static void cb_toggle_element (GConfClient * client, +static void cb_change_child (GConfClient * client, guint connection_id, GConfEntry * entry, gpointer data); static GstStateChangeReturn gst_gconf_audio_sink_change_state (GstElement * element, GstStateChange transition); +static void gst_gconf_switch_profile (GstGConfAudioSink * sink, + GstGConfProfile profile); enum { @@ -40,7 +42,8 @@ enum PROP_PROFILE }; -GST_BOILERPLATE (GstGConfAudioSink, gst_gconf_audio_sink, GstBin, GST_TYPE_BIN); +GST_BOILERPLATE (GstGConfAudioSink, gst_gconf_audio_sink, GstSwitchSink, + GST_TYPE_SWITCH_SINK); static void gst_gconf_audio_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -55,14 +58,8 @@ gst_gconf_audio_sink_base_init (gpointer klass) GST_ELEMENT_DETAILS ("GConf audio sink", "Sink/Audio", "Audio sink embedding the GConf-settings for audio output", - "Ronald Bultje <rbultje@ronald.bitfreak.net>"); - GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - - gst_element_class_add_pad_template (eklass, - gst_static_pad_template_get (&sink_template)); + "Ronald Bultje <rbultje@ronald.bitfreak.net>\n" + "Jan Schmidt <thaytan@mad.scientist.com>"); gst_element_class_set_details (eklass, &gst_gconf_audio_sink_details); } @@ -101,68 +98,28 @@ gst_gconf_audio_sink_class_init (GstGConfAudioSinkClass * klass) GST_TYPE_GCONF_PROFILE, GCONF_PROFILE_SOUNDS, G_PARAM_READWRITE)); } -/* - * Hack to make negotiation work. - */ - static void gst_gconf_audio_sink_reset (GstGConfAudioSink * sink) { - GstPad *targetpad; - - /* fakesink */ - if (sink->kid) { - gst_element_set_state (sink->kid, GST_STATE_NULL); - gst_bin_remove (GST_BIN (sink), sink->kid); - } - sink->kid = gst_element_factory_make ("fakesink", "testsink"); - gst_bin_add (GST_BIN (sink), sink->kid); - - targetpad = gst_element_get_pad (sink->kid, "sink"); - gst_ghost_pad_set_target (GST_GHOST_PAD (sink->pad), targetpad); - gst_object_unref (targetpad); + gst_switch_sink_set_child (GST_SWITCH_SINK (sink), NULL); g_free (sink->gconf_str); sink->gconf_str = NULL; - if (sink->connection) { - gconf_client_notify_remove (sink->client, sink->connection); - sink->connection = 0; - } -} - -static const gchar * -get_gconf_key_for_profile (int profile) -{ - switch (profile) { - case GCONF_PROFILE_SOUNDS: - return GST_GCONF_DIR GST_GCONF_AUDIOSINK_KEY; - case GCONF_PROFILE_MUSIC: - return GST_GCONF_DIR GST_GCONF_MUSIC_AUDIOSINK_KEY; - case GCONF_PROFILE_CHAT: - return GST_GCONF_DIR GST_GCONF_CHAT_AUDIOSINK_KEY; - default: - g_return_val_if_reached (NULL); - } + gst_gconf_switch_profile (sink, GCONF_PROFILE_NONE); } static void gst_gconf_audio_sink_init (GstGConfAudioSink * sink, GstGConfAudioSinkClass * g_class) { - sink->pad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK); - gst_element_add_pad (GST_ELEMENT (sink), sink->pad); - gst_gconf_audio_sink_reset (sink); sink->client = gconf_client_get_default (); - gconf_client_add_dir (sink->client, GST_GCONF_DIR, + gconf_client_add_dir (sink->client, GST_GCONF_DIR "/default", GCONF_CLIENT_PRELOAD_RECURSIVE, NULL); - sink->profile = GCONF_PROFILE_SOUNDS; - sink->connection = gconf_client_notify_add (sink->client, - get_gconf_key_for_profile (sink->profile), cb_toggle_element, - sink, NULL, NULL); + gst_gconf_switch_profile (sink, GCONF_PROFILE_SOUNDS); } static void @@ -171,11 +128,7 @@ gst_gconf_audio_sink_dispose (GObject * object) GstGConfAudioSink *sink = GST_GCONF_AUDIO_SINK (object); if (sink->client) { - if (sink->connection) { - gconf_client_notify_remove (sink->client, sink->connection); - sink->connection = 0; - } - + gst_gconf_switch_profile (sink, GCONF_PROFILE_NONE); g_object_unref (G_OBJECT (sink->client)); sink->client = NULL; } @@ -187,12 +140,11 @@ gst_gconf_audio_sink_dispose (GObject * object) } static gboolean -do_toggle_element (GstGConfAudioSink * sink) +do_change_child (GstGConfAudioSink * sink) { const gchar *key; - GstPad *targetpad; gchar *new_gconf_str; - GstState cur, next; + GstElement *new_kid; key = gst_gconf_get_key_for_sink_profile (sink->profile); new_gconf_str = gst_gconf_get_string (key); @@ -204,57 +156,65 @@ do_toggle_element (GstGConfAudioSink * sink) (strlen (new_gconf_str) == 0 || strcmp (sink->gconf_str, new_gconf_str) == 0)) { g_free (new_gconf_str); - GST_DEBUG_OBJECT (sink, "GConf key was updated, but it didn't change"); - return TRUE; - } - - /* 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 */ - GST_OBJECT_LOCK (sink); - cur = GST_STATE (sink); - next = GST_STATE_PENDING (sink); - GST_OBJECT_UNLOCK (sink); - - if (cur > GST_STATE_READY || next == GST_STATE_PAUSED) { GST_DEBUG_OBJECT (sink, - "Auto-sink is already running. Ignoring GConf change"); + "GConf key was updated, but it didn't change. Ignoring"); return TRUE; } GST_DEBUG_OBJECT (sink, "GConf key changed: '%s' to '%s'", GST_STR_NULL (sink->gconf_str), GST_STR_NULL (new_gconf_str)); - g_free (sink->gconf_str); - sink->gconf_str = new_gconf_str; - - /* kill old element */ - if (sink->kid) { - GST_DEBUG_OBJECT (sink, "Removing old kid"); - gst_element_set_state (sink->kid, GST_STATE_NULL); - gst_bin_remove (GST_BIN (sink), sink->kid); - sink->kid = NULL; - } + GST_DEBUG_OBJECT (sink, "Creating new child for profile %d", sink->profile); + new_kid = + gst_gconf_render_bin_with_default (new_gconf_str, DEFAULT_AUDIOSINK); - GST_DEBUG_OBJECT (sink, "Creating new kid"); - if (!(sink->kid = gst_gconf_get_default_audio_sink (sink->profile))) { + if (new_kid == NULL) { GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL), ("Failed to render audio sink from GConf")); - g_free (sink->gconf_str); - sink->gconf_str = NULL; - return FALSE; + 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; } - gst_element_set_state (sink->kid, GST_STATE (sink)); - gst_bin_add (GST_BIN (sink), sink->kid); - - /* re-attach ghostpad */ - GST_DEBUG_OBJECT (sink, "Creating new ghostpad"); - targetpad = gst_element_get_pad (sink->kid, "sink"); - gst_ghost_pad_set_target (GST_GHOST_PAD (sink->pad), targetpad); - gst_object_unref (targetpad); + + g_free (sink->gconf_str); + sink->gconf_str = new_gconf_str; + GST_DEBUG_OBJECT (sink, "done changing gconf audio sink"); return TRUE; + +fail: + g_free (new_gconf_str); + return FALSE; +} + +static void +gst_gconf_switch_profile (GstGConfAudioSink * sink, GstGConfProfile profile) +{ + if (sink->client == NULL) + return; + + if (sink->connection) { + const gchar *key = gst_gconf_get_key_for_sink_profile (sink->profile); + + GST_DEBUG_OBJECT (sink, "Unsubscribing old key %s for profile %d", + key, sink->profile); + gconf_client_notify_remove (sink->client, sink->connection); + sink->connection = 0; + } + + sink->profile = profile; + if (profile != GCONF_PROFILE_NONE) { + const gchar *key = gst_gconf_get_key_for_sink_profile (sink->profile); + + GST_DEBUG_OBJECT (sink, "Subscribing to key %s for profile %d", + key, profile); + sink->connection = gconf_client_notify_add (sink->client, key, + cb_change_child, sink, NULL, NULL); + } } static void @@ -267,13 +227,7 @@ gst_gconf_audio_sink_set_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_PROFILE: - sink->profile = g_value_get_enum (value); - if (sink->connection) { - gconf_client_notify_remove (sink->client, sink->connection); - } - sink->connection = gconf_client_notify_add (sink->client, - get_gconf_key_for_profile (sink->profile), cb_toggle_element, - sink, NULL, NULL); + gst_gconf_switch_profile (sink, g_value_get_enum (value)); break; default: break; @@ -299,10 +253,10 @@ gst_gconf_audio_sink_get_property (GObject * object, guint prop_id, } static void -cb_toggle_element (GConfClient * client, +cb_change_child (GConfClient * client, guint connection_id, GConfEntry * entry, gpointer data) { - do_toggle_element (GST_GCONF_AUDIO_SINK (data)); + do_change_child (GST_GCONF_AUDIO_SINK (data)); } static GstStateChangeReturn @@ -314,8 +268,10 @@ gst_gconf_audio_sink_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: - if (!do_toggle_element (sink)) + if (!do_change_child (sink)) { + gst_gconf_audio_sink_reset (sink); return GST_STATE_CHANGE_FAILURE; + } break; default: break; diff --git a/ext/gconf/gstgconfaudiosink.h b/ext/gconf/gstgconfaudiosink.h index 02a397da..c323b131 100644 --- a/ext/gconf/gstgconfaudiosink.h +++ b/ext/gconf/gstgconfaudiosink.h @@ -22,6 +22,7 @@ #include <gst/gst.h> #include <gconf/gconf-client.h> +#include "gstswitchsink.h" G_BEGIN_DECLS @@ -39,21 +40,19 @@ G_BEGIN_DECLS (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GCONF_AUDIO_SINK)) typedef struct _GstGConfAudioSink { - GstBin parent; + GstSwitchSink parent; /* explicit pointers to stuff used */ GConfClient *client; - int profile; + GstGConfProfile profile; guint connection; - GstElement *kid; - GstPad *pad; /* Current gconf string */ gchar *gconf_str; } GstGConfAudioSink; typedef struct _GstGConfAudioSinkClass { - GstBinClass parent_class; + GstSwitchSinkClass parent_class; } GstGConfAudioSinkClass; GType gst_gconf_audio_sink_get_type (void); diff --git a/ext/gconf/gstswitchsink.c b/ext/gconf/gstswitchsink.c new file mode 100644 index 00000000..d00336e9 --- /dev/null +++ b/ext/gconf/gstswitchsink.c @@ -0,0 +1,265 @@ +/* GStreamer + * (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net> + * (c) 2006 Jürg Billeter <j@bitron.ch> + * (c) 2007 Jan Schmidt <thaytan@noraisin.net> + * + * 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 <string.h> + +#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_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_switch_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +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); + GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + GstPadTemplate *child_pad_templ; + + oklass->set_property = gst_switch_sink_set_property; + oklass->get_property = gst_switch_sink_get_property; + 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 void +gst_switch_sink_reset (GstSwitchSink * sink) +{ + /* fakesink */ + if (sink->kid == NULL) { + gst_switch_sink_set_child (sink, NULL); + } +} + +static void +gst_switch_sink_init (GstSwitchSink * sink, GstSwitchSinkClass * g_class) +{ + sink->pad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK); + gst_element_add_pad (GST_ELEMENT (sink), sink->pad); + + gst_switch_sink_reset (sink); +} + +static void +gst_switch_sink_dispose (GObject * object) +{ + GstSwitchSink *sink = GST_SWITCH_SINK (object); + GstElement **p_kid = &sink->kid; + + gst_object_replace ((GstObject **) p_kid, NULL); + + GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +gboolean +gst_switch_commit_new_kid (GstSwitchSink * sink) +{ + GstPad *targetpad; + GstState cur_state; + GstElement *new_kid, *old_kid; + gboolean is_fakesink = FALSE; + + /* need locking around member accesses */ + GST_OBJECT_LOCK (sink); + cur_state = GST_STATE (sink); + new_kid = sink->new_kid; + 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"); + g_object_set (new_kid, "sync", TRUE, NULL); + is_fakesink = TRUE; + } else { + GST_DEBUG_OBJECT (sink, "Setting new kid"); + } + + if (gst_element_set_state (new_kid, cur_state) == GST_STATE_CHANGE_FAILURE) { + GST_ELEMENT_ERROR (sink, CORE, STATE_CHANGE, (NULL), + ("Failed to set state on new child.")); + gst_object_unref (new_kid); + return FALSE; + } + 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; + 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); + } + + /* re-attach ghostpad */ + GST_DEBUG_OBJECT (sink, "Creating new ghostpad"); + targetpad = gst_element_get_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 */ + + /* Unblock the target pad if necessary */ + if (sink->awaiting_block) { + gst_pad_set_blocked (sink->pad, FALSE); + sink->awaiting_block = FALSE; + } + + 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 don't have one anyway */ + if (new_kid == NULL && sink->have_kid == FALSE) + return TRUE; + + 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 (cur == GST_STATE_PAUSED && next == GST_STATE_READY) { + return gst_switch_commit_new_kid (sink); + } + /* 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_commit_new_kid (sink); +} + +static void +gst_switch_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstSwitchSink *sink; + + g_return_if_fail (GST_IS_SWITCH_SINK (object)); + + sink = GST_SWITCH_SINK (object); + + switch (prop_id) { + break; + default: + break; + } +} + +#if 0 +static gboolean +gst_switch_sink_handle_event (GstPad * pad, GstEvent * event) +{ +} +#endif + +static void +gst_switch_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstSwitchSink *sink; + + g_return_if_fail (GST_IS_SWITCH_SINK (object)); + + sink = GST_SWITCH_SINK (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +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: + gst_switch_sink_reset (sink); + break; + default: + break; + } + + return ret; +} diff --git a/ext/gconf/gstswitchsink.h b/ext/gconf/gstswitchsink.h new file mode 100644 index 00000000..c85c90b2 --- /dev/null +++ b/ext/gconf/gstswitchsink.h @@ -0,0 +1,68 @@ +/* GStreamer + * (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net> + * (c) 2007 Jan Schmidt <thaytan@mad.scientist.com> + * + * 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 <gst/gst.h> + +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; + + /* Track incoming segment info for switchover */ + GstSegment segment; + + /* If a custom child has been set... */ + gboolean have_kid; + + /* If waiting for a pad block to complete on the ghost pad */ + gboolean awaiting_block; +} 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__ */ |