--- rb-player-gst.c.orig	2007-02-05 23:37:56.000000000 +0100
+++ rb-player-gst.c.new	2007-02-08 22:27:52.000000000 +0100
@@ -36,12 +36,19 @@
 #include <libgnomevfs/gnome-vfs-utils.h>
 
 #include "rb-debug.h"
+#include "rb-marshal.h"
+
 #include "rb-player.h"
+#include "rb-player-gst-filter.h"
+#include "rb-player-gst-tee.h"
+/*#include "rb-player-gst-data-tee.h"*/
 #include "rb-player-gst.h"
-#include "rb-debug.h"
-#include "rb-marshal.h"
+
 
 static void rb_player_init (RBPlayerIface *iface);
+static void rb_player_gst_filter_init (RBPlayerGstFilterIface *iface);
+static void rb_player_gst_tee_init (RBPlayerGstTeeIface *iface);
+/*tatic void rb_player_gst_data_tee_init (RBPlayerGstDataTeeIface *iface);*/
 static void rb_player_gst_finalize (GObject *object);
 static void rb_player_gst_get_property (GObject *object,
 					guint prop_id,
@@ -66,8 +73,13 @@
 static long rb_player_gst_get_time (RBPlayer *player);
 
 G_DEFINE_TYPE_WITH_CODE(RBPlayerGst, rb_player_gst, G_TYPE_OBJECT,
-			G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER,
-					      rb_player_init))
+			G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER, rb_player_init)
+#ifdef HAVE_GSTREAMER_0_10
+			G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER_GST_FILTER, rb_player_gst_filter_init)
+			G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER_GST_TEE, rb_player_gst_tee_init)
+			/*G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER_GST_DATA_TEE, rb_player_gst_data_tee_init)*/
+#endif
+			)
 #define RB_PLAYER_GST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PLAYER_GST, RBPlayerGstPrivate))
 
 #define RB_PLAYER_GST_TICK_HZ 5
@@ -100,6 +112,14 @@
 	guint error_signal_id;
 	guint buffering_signal_id;
 #endif
+#ifdef HAVE_GSTREAMER_0_10
+	GList *waiting_tees;
+	GstElement *sinkbin;
+	GstElement *tee;
+
+	GList *waiting_filters; /* in reverse order */
+	GstElement *filterbin;
+#endif
 
 	float cur_volume;
 
@@ -230,6 +250,13 @@
 		rb_player_gst_gst_free_playbin (mp);
 	}
 
+#ifdef HAVE_GSTREAMER_0_10
+	if (mp->priv->waiting_tees) {
+		g_list_foreach (mp->priv->waiting_tees, (GFunc)gst_object_sink, NULL);
+	}
+	g_list_free (mp->priv->waiting_tees);
+#endif
+
 	G_OBJECT_CLASS (rb_player_gst_parent_class)->finalize (object);
 }
 
@@ -1199,3 +1226,286 @@
 	} else
 		return -1;
 }
+
+
+#ifdef HAVE_GSTREAMER_0_10
+static gboolean
+rb_player_gst_add_tee (RBPlayerGstTee *player, GstElement *element)
+{
+	RBPlayerGst *mp;
+	GstElement *queue, *audioconvert, *bin;
+	GstPad *pad;
+
+	mp = RB_PLAYER_GST (player);
+
+	if (mp->priv->tee == NULL) {
+		mp->priv->waiting_tees = g_list_prepend (mp->priv->waiting_tees, element);
+		return TRUE;
+	}
+	
+	if (mp->priv->playing) {
+		if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
+			/* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
+			if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
+				g_warning ("Failed to pause pipeline before tee insertion");
+				return FALSE;
+			}
+		}
+	}
+
+	bin = gst_bin_new (NULL);
+	queue = gst_element_factory_make ("queue", NULL);
+	audioconvert = gst_element_factory_make ("audioconvert", NULL);
+
+	/* set up the element's containing bin */
+	gst_bin_add_many (GST_BIN (bin), queue, audioconvert, element, NULL);
+	gst_bin_add (GST_BIN (mp->priv->sinkbin), bin);
+	gst_element_link_many (queue, audioconvert, element, NULL);
+
+	/* link it to the tee */
+	pad = gst_ghost_pad_new ("sink", gst_element_get_pad (queue, "sink"));
+	gst_element_add_pad (bin, pad);
+	gst_element_link (mp->priv->tee, bin);
+
+	if (mp->priv->playing)
+		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+
+	return TRUE;
+}
+
+static gboolean
+rb_player_gst_remove_tee (RBPlayerGstTee *player, GstElement *element)
+{
+	RBPlayerGst *mp;
+	GstElement *bin;
+
+	mp = RB_PLAYER_GST (player);
+
+	if (mp->priv->tee == NULL) {
+		gst_object_sink (element);
+		mp->priv->waiting_tees = g_list_remove (mp->priv->waiting_tees, element);
+		return TRUE;
+	}
+
+	if (mp->priv->playing) {
+		if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
+			/* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
+			if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
+				g_warning ("Failed to pause pipeline before tee removal");
+				return FALSE;
+			}
+		}
+	}
+
+	/* get the containing bin and unlink it */
+	bin = GST_ELEMENT (gst_element_get_parent (element));
+
+	if (gst_element_set_state (bin, GST_STATE_NULL) == GST_STATE_CHANGE_ASYNC) {
+		/* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
+		if (gst_element_get_state (bin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
+			g_warning ("Failed to pause pipeline before tee removal");
+			return FALSE;
+		}
+	}
+
+	gst_bin_remove (GST_BIN (mp->priv->sinkbin), bin);
+	gst_object_unref (bin);
+
+	if (mp->priv->playing)
+		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+
+	return TRUE;
+}
+
+static gboolean
+rb_player_gst_add_filter (RBPlayerGstFilter *player, GstElement *element)
+{
+	RBPlayerGst *mp;
+	GstElement *audioconvert, *bin;
+	GstPad *ghostpad, *realpad, *element_sink_pad;
+        GstIterator *sink_pads;
+        gboolean sink_pad_found, stop_scan;
+
+	mp = RB_PLAYER_GST (player);
+
+	if (mp->priv->filterbin == NULL) {
+		mp->priv->waiting_filters = g_list_prepend (mp->priv->waiting_filters, element);
+		return TRUE;
+	}
+	
+	if (mp->priv->playing) {
+		if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
+			/* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
+			if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
+				g_warning ("Failed to pause pipeline before filter insertion");
+				return FALSE;
+			}
+		}
+	}
+
+	bin = gst_bin_new (NULL);
+	audioconvert = gst_element_factory_make ("audioconvert", NULL);
+
+	/* set up the element's containing bin */
+	rb_debug ("adding element %p and audioconvert to bin", element);
+	gst_bin_add_many (GST_BIN (bin), element, audioconvert, NULL);
+	gst_element_link_many (element, audioconvert, NULL);
+
+	/* ghost to the bin */
+        /* retrieve the first unliked source pad */
+        sink_pad_found = FALSE;
+        stop_scan = FALSE;
+        sink_pads = gst_element_iterate_sink_pads (element);
+        while (!sink_pad_found && !stop_scan) {
+            switch (gst_iterator_next (sink_pads, &element_sink_pad)) {
+                case GST_ITERATOR_OK:
+                    sink_pad_found = ! gst_pad_is_linked (element_sink_pad);
+                break;
+                case GST_ITERATOR_RESYNC:
+                    gst_iterator_resync (sink_pads);
+                break;
+                case GST_ITERATOR_ERROR:
+                case GST_ITERATOR_DONE:
+                    stop_scan = TRUE;
+            }
+        }
+        gst_iterator_free (sink_pads);
+
+        if (!sink_pad_found) {
+            g_warning ("Could not find a free sink pad on filter");
+            return FALSE;
+        }
+
+	ghostpad = gst_ghost_pad_new ("sink", element_sink_pad);
+	gst_element_add_pad (bin, ghostpad);
+	ghostpad = gst_ghost_pad_new ("src", gst_element_get_pad (audioconvert, "src"));
+	gst_element_add_pad (bin, ghostpad);
+    
+	/* replace the filter chain ghost with the new bin */
+	gst_bin_add (GST_BIN (mp->priv->filterbin), bin);
+
+	ghostpad = gst_element_get_pad (mp->priv->filterbin, "src");
+	realpad = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad));
+	gst_ghost_pad_set_target (GST_GHOST_PAD (ghostpad),
+				  gst_element_get_pad (bin, "src"));
+
+	if (gst_pad_link (realpad, gst_element_get_pad (bin, "sink")) != GST_PAD_LINK_OK) {
+		g_warning ("could not link new filter into pipeline");
+		return FALSE;
+	}
+
+	if (mp->priv->playing)
+		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+
+	return TRUE;
+}
+
+static gboolean
+rb_player_gst_remove_filter (RBPlayerGstFilter *player, GstElement *element)
+{
+	RBPlayerGst *mp;
+	GstPad *mypad;
+	GstPad *prevpad, *nextpad;
+	GstPad *ghostpad;
+	GstElement *bin;
+	gboolean result = TRUE;
+
+	mp = RB_PLAYER_GST (player);
+
+	if (mp->priv->filterbin == NULL) {
+		gst_object_sink (element);
+		mp->priv->waiting_filters = g_list_remove (mp->priv->waiting_filters, element);
+		return TRUE;
+	}
+
+	if (mp->priv->playing) {
+		if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
+			/* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
+			if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
+				g_warning ("Failed to pause pipeline before filter removal");
+				return FALSE;
+			}
+		}
+	}
+
+	/* get the containing bin and unlink it */
+	bin = GST_ELEMENT (gst_element_get_parent (element));
+
+	if (gst_element_set_state (bin, GST_STATE_NULL) == GST_STATE_CHANGE_ASYNC) {
+		/* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
+		if (gst_element_get_state (bin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
+			g_warning ("Failed to pause pipeline before filter removal");
+			return FALSE;
+		}
+	}
+
+	mypad = gst_element_get_pad (bin, "sink");
+	prevpad = gst_pad_get_peer (mypad);
+	gst_pad_unlink (prevpad, mypad);
+	gst_object_unref (mypad);
+
+	ghostpad = gst_element_get_pad (bin, "src");
+	nextpad = gst_element_get_pad (mp->priv->filterbin, "src");
+
+	if (gst_ghost_pad_get_target (GST_GHOST_PAD (nextpad)) == ghostpad) {
+		/* we are at the end of the filter chain, so  redirect the ghostpad to the previous element */
+		gst_ghost_pad_set_target (GST_GHOST_PAD (nextpad), prevpad);
+	} else {
+		/* we are in the middle, so link the previous and next elements */
+		mypad = gst_element_get_pad (bin, "src");
+		gst_object_unref (nextpad);
+		nextpad = gst_pad_get_peer (mypad);
+		gst_pad_unlink (mypad, nextpad);
+		gst_object_unref (mypad);
+
+		if (gst_pad_link (prevpad, nextpad) != GST_PAD_LINK_OK)
+			result = FALSE;
+	}
+
+	gst_object_unref (nextpad);
+	gst_object_unref (prevpad);
+
+	gst_bin_remove (GST_BIN (mp->priv->filterbin), bin);
+	gst_object_unref (bin);
+
+	if (mp->priv->playing)
+		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+
+	return result;
+}
+
+/*static gboolean
+rb_player_gst_add_data_tee (RBPlayerGstDataTee *player, GstElement *element)
+{
+
+}
+
+static gboolean
+rb_player_gst_remove_data_tee (RBPlayerGstDataTee *player, GstElement *element)
+{
+
+}*/
+
+static void
+rb_player_gst_filter_init (RBPlayerGstFilterIface *iface)
+{
+	iface->add_filter = rb_player_gst_add_filter;
+	iface->remove_filter = rb_player_gst_remove_filter;
+}
+
+static void
+rb_player_gst_tee_init (RBPlayerGstTeeIface *iface)
+{
+	iface->add_tee = rb_player_gst_add_tee;
+	iface->remove_tee = rb_player_gst_remove_tee;
+}
+
+/*static void
+rb_player_gst_data_tee_init (RBPlayerGstDataTeeIface *iface)
+{
+	iface->add_data_tee = rb_player_gst_add_data_tee;
+	iface->remove_data_tee = rb_player_gst_remove_data_tee;
+}*/
+
+
+#endif
