summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog33
-rw-r--r--ext/gconf/Makefile.am2
-rw-r--r--ext/gconf/gconf.c48
-rw-r--r--ext/gconf/gconf.h9
-rw-r--r--ext/gconf/gstgconfaudiosink.c174
-rw-r--r--ext/gconf/gstgconfaudiosink.h9
-rw-r--r--ext/gconf/gstswitchsink.c265
-rw-r--r--ext/gconf/gstswitchsink.h68
-rw-r--r--gst/autodetect/gstautoaudiosink.c40
-rw-r--r--gst/autodetect/gstautovideosink.c41
10 files changed, 533 insertions, 156 deletions
diff --git a/ChangeLog b/ChangeLog
index 84da77ba..161ad53c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,36 @@
+2007-02-13 Jan Schmidt <thaytan@mad.scientist.com>
+
+ * ext/gconf/Makefile.am:
+ * ext/gconf/gconf.c: (gst_gconf_get_string),
+ (gst_gconf_get_key_for_sink_profile), (gst_gconf_set_string),
+ (gst_gconf_render_bin_with_default):
+ * ext/gconf/gconf.h:
+ * ext/gconf/gstgconfaudiosink.c: (gst_gconf_audio_sink_base_init),
+ (gst_gconf_audio_sink_reset), (gst_gconf_audio_sink_init),
+ (gst_gconf_audio_sink_dispose), (do_change_child),
+ (gst_gconf_switch_profile), (gst_gconf_audio_sink_set_property),
+ (cb_change_child), (gst_gconf_audio_sink_change_state):
+ * ext/gconf/gstgconfaudiosink.h:
+ * ext/gconf/gstswitchsink.c: (gst_switch_sink_base_init),
+ (gst_switch_sink_class_init), (gst_switch_sink_reset),
+ (gst_switch_sink_init), (gst_switch_sink_dispose),
+ (gst_switch_commit_new_kid), (gst_switch_sink_set_child),
+ (gst_switch_sink_set_property), (gst_switch_sink_handle_event),
+ (gst_switch_sink_get_property), (gst_switch_sink_change_state):
+ * ext/gconf/gstswitchsink.h:
+ * gst/autodetect/gstautoaudiosink.c:
+ (gst_auto_audio_sink_class_init), (gst_auto_audio_sink_dispose),
+ (gst_auto_audio_sink_clear_kid), (gst_auto_audio_sink_reset),
+ (gst_auto_audio_sink_detect):
+ * gst/autodetect/gstautovideosink.c:
+ (gst_auto_video_sink_class_init), (gst_auto_video_sink_dispose),
+ (gst_auto_video_sink_clear_kid), (gst_auto_video_sink_reset),
+ (gst_auto_video_sink_detect):
+ Re-factor the gconfaudiosink into a "GstSwitchSink" base class
+ and a child that implements the GConf key monitoring. The end goal of
+ this is an audio sink that can be changed on the fly, but at the
+ moment it still only changes on the next READY transition.
+
2007-02-13 Stefan Kost <ensonic@users.sf.net>
* gst/avi/gstavidemux.c: (gst_avi_demux_handle_src_query),
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__ */
diff --git a/gst/autodetect/gstautoaudiosink.c b/gst/autodetect/gstautoaudiosink.c
index ccaa413a..379113af 100644
--- a/gst/autodetect/gstautoaudiosink.c
+++ b/gst/autodetect/gstautoaudiosink.c
@@ -1,5 +1,6 @@
/* GStreamer
* (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 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
@@ -49,6 +50,8 @@
static GstStateChangeReturn
gst_auto_audio_sink_change_state (GstElement * element,
GstStateChange transition);
+static void gst_auto_audio_sink_dispose (GstAutoAudioSink * sink);
+static void gst_auto_audio_sink_clear_kid (GstAutoAudioSink * sink);
GST_BOILERPLATE (GstAutoAudioSink, gst_auto_audio_sink, GstBin, GST_TYPE_BIN);
@@ -56,7 +59,8 @@ static const GstElementDetails gst_auto_audio_sink_details =
GST_ELEMENT_DETAILS ("Auto audio sink",
"Sink/Audio",
"Wrapper audio sink for automatically detected audio sink",
- "Ronald Bultje <rbultje@ronald.bitfreak.net>");
+ "Ronald Bultje <rbultje@ronald.bitfreak.net>\n"
+ "Jan Schmidt <thaytan@noraisin.net");
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
@@ -77,13 +81,35 @@ gst_auto_audio_sink_base_init (gpointer klass)
static void
gst_auto_audio_sink_class_init (GstAutoAudioSinkClass * klass)
{
+ GObjectClass *gobject_class;
GstElementClass *eklass;
+ gobject_class = G_OBJECT_CLASS (klass);
eklass = GST_ELEMENT_CLASS (klass);
+ gobject_class->dispose =
+ (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_auto_audio_sink_dispose);
eklass->change_state = GST_DEBUG_FUNCPTR (gst_auto_audio_sink_change_state);
}
+static void
+gst_auto_audio_sink_dispose (GstAutoAudioSink * sink)
+{
+ gst_auto_audio_sink_clear_kid (sink);
+
+ G_OBJECT_CLASS (parent_class)->dispose ((GObject *) sink);
+}
+
+static void
+gst_auto_audio_sink_clear_kid (GstAutoAudioSink * sink)
+{
+ if (sink->kid) {
+ gst_element_set_state (sink->kid, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (sink), sink->kid);
+ sink->kid = NULL;
+ }
+}
+
/*
* Hack to make initial linking work; ideally, this'd work even when
* no target has been assigned to the ghostpad yet.
@@ -94,11 +120,9 @@ gst_auto_audio_sink_reset (GstAutoAudioSink * sink)
{
GstPad *targetpad;
+ gst_auto_audio_sink_clear_kid (sink);
+
/* fakesink placeholder */
- 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", "tempsink");
gst_bin_add (GST_BIN (sink), sink->kid);
@@ -258,11 +282,7 @@ gst_auto_audio_sink_detect (GstAutoAudioSink * sink)
GstElement *esink;
GstPad *targetpad;
- if (sink->kid) {
- gst_element_set_state (sink->kid, GST_STATE_NULL);
- gst_bin_remove (GST_BIN (sink), sink->kid);
- sink->kid = NULL;
- }
+ gst_auto_audio_sink_clear_kid (sink);
/* find element */
GST_DEBUG_OBJECT (sink, "Creating new kid");
diff --git a/gst/autodetect/gstautovideosink.c b/gst/autodetect/gstautovideosink.c
index 085dea1b..0e0bc7ec 100644
--- a/gst/autodetect/gstautovideosink.c
+++ b/gst/autodetect/gstautovideosink.c
@@ -1,5 +1,6 @@
/* GStreamer
* (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 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
@@ -49,6 +50,8 @@
static GstStateChangeReturn
gst_auto_video_sink_change_state (GstElement * element,
GstStateChange transition);
+static void gst_auto_video_sink_dispose (GstAutoVideoSink * sink);
+static void gst_auto_video_sink_clear_kid (GstAutoVideoSink * sink);
GST_BOILERPLATE (GstAutoVideoSink, gst_auto_video_sink, GstBin, GST_TYPE_BIN);
@@ -56,7 +59,8 @@ static const GstElementDetails gst_auto_video_sink_details =
GST_ELEMENT_DETAILS ("Auto video sink",
"Sink/Video",
"Wrapper video sink for automatically detected video sink",
- "Ronald Bultje <rbultje@ronald.bitfreak.net>");
+ "Ronald Bultje <rbultje@ronald.bitfreak.net>\n"
+ "Jan Schmidt <thaytan@noraisin.net");
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
@@ -76,11 +80,33 @@ gst_auto_video_sink_base_init (gpointer klass)
static void
gst_auto_video_sink_class_init (GstAutoVideoSinkClass * klass)
{
+ GObjectClass *gobject_class;
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->dispose =
+ (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_auto_video_sink_dispose);
eklass->change_state = GST_DEBUG_FUNCPTR (gst_auto_video_sink_change_state);
}
+static void
+gst_auto_video_sink_dispose (GstAutoVideoSink * sink)
+{
+ gst_auto_video_sink_clear_kid (sink);
+
+ G_OBJECT_CLASS (parent_class)->dispose ((GObject *) sink);
+}
+
+static void
+gst_auto_video_sink_clear_kid (GstAutoVideoSink * sink)
+{
+ if (sink->kid) {
+ gst_element_set_state (sink->kid, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (sink), sink->kid);
+ sink->kid = NULL;
+ }
+}
+
/*
* Hack to make initial linking work; ideally, this'd work even when
* no target has been assigned to the ghostpad yet.
@@ -91,11 +117,10 @@ gst_auto_video_sink_reset (GstAutoVideoSink * sink)
{
GstPad *targetpad;
+ /* Remove any existing element */
+ gst_auto_video_sink_clear_kid (sink);
+
/* fakesink placeholder */
- 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", "tempsink");
gst_bin_add (GST_BIN (sink), sink->kid);
@@ -204,11 +229,7 @@ gst_auto_video_sink_detect (GstAutoVideoSink * sink)
GstElement *esink;
GstPad *targetpad;
- if (sink->kid) {
- gst_element_set_state (sink->kid, GST_STATE_NULL);
- gst_bin_remove (GST_BIN (sink), sink->kid);
- sink->kid = NULL;
- }
+ gst_auto_video_sink_clear_kid (sink);
/* find element */
GST_DEBUG_OBJECT (sink, "Creating new kid");