Index: bindings/python/rb.override
===================================================================
--- bindings/python/rb.override	(revision 4772)
+++ bindings/python/rb.override	(working copy)
@@ -12,6 +12,10 @@ headers
 #include "rb-dialog.h"
 #include "rb-entry-view.h"
 #include "rb-library-browser.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-plugin.h"
 #include "rb-property-view.h"
 #include "rb-removable-media-manager.h"
Index: bindings/python/rb.defs
===================================================================
--- bindings/python/rb.defs	(revision 4773)
+++ bindings/python/rb.defs	(working copy)
@@ -85,6 +85,31 @@
   (gtype-id "RB_TYPE_SONG_INFO")
 )
 
+(define-interface Player
+  (in-module "RB")
+  (c-name "RBPlayer")
+  (gtype-id "RB_TYPE_PLAYER")
+)
+
+(define-interface PlayerGstFilter
+  (in-module "RB")
+  (c-name "RBPlayerGstFilter")
+  (gtype-id "RB_TYPE_PLAYER_GST_FILTER")
+)
+
+(define-interface PlayerGstTee
+  (in-module "RB")
+  (c-name "RBPlayerGstTee")
+  (gtype-id "RB_TYPE_PLAYER_GST_TEE")
+)
+
+(define-interface PlayerGstDataTee
+  (in-module "RB")
+  (c-name "RBPlayerGstDataTee")
+  (gtype-id "RB_TYPE_PLAYER_GST_DATA_TEE")
+)
+
+
 ;; Enumerations and flags ...
 
 (define-enum ShellPlayerError
@@ -99,6 +124,17 @@
   )
 )
 
+(define-enum PlayerError
+  (in-module "RB")
+  (c-name "RBPlayerError")
+  (gtype-id "RB_TYPE_PLAYER_ERROR")
+  (values
+    '("no-audio" "RB_PLAYER_ERROR_NO_AUDIO")
+    '("general" "RB_PLAYER_ERROR_GENERAL")
+    '("internal" "RB_PLAYER_ERROR_INTERNAL")
+  )
+)
+
 (define-enum SourceEOFType
   (in-module "RB")
   (c-name "RBSourceEOFType")
@@ -1730,3 +1766,175 @@
   )
 )
 
+
+;; From ../../backends/rb-player.h
+
+(define-function rb_player_error_quark
+  (c-name "rb_player_error_quark")
+  (return-type "GQuark")
+)
+
+(define-function rb_player_get_type
+  (c-name "rb_player_get_type")
+  (return-type "GType")
+)
+
+(define-function rb_player_new
+  (c-name "rb_player_new")
+  (is-constructor-of "RBPlayer")
+  (return-type "RBPlayer*")
+  (parameters
+    '("GError**" "error")
+  )
+)
+
+(define-method open
+  (of-object "RBPlayer")
+  (c-name "rb_player_open")
+  (return-type "gboolean")
+  (parameters
+    '("const-char*" "uri")
+    '("GError**" "error")
+  )
+)
+
+(define-method opened
+  (of-object "RBPlayer")
+  (c-name "rb_player_opened")
+  (return-type "gboolean")
+)
+
+(define-method close
+  (of-object "RBPlayer")
+  (c-name "rb_player_close")
+  (return-type "gboolean")
+  (parameters
+    '("GError**" "error")
+  )
+)
+
+(define-method play
+  (of-object "RBPlayer")
+  (c-name "rb_player_play")
+  (return-type "gboolean")
+  (parameters
+    '("GError**" "error")
+  )
+)
+
+(define-method pause
+  (of-object "RBPlayer")
+  (c-name "rb_player_pause")
+  (return-type "none")
+)
+
+(define-method playing
+  (of-object "RBPlayer")
+  (c-name "rb_player_playing")
+  (return-type "gboolean")
+)
+
+(define-method set_volume
+  (of-object "RBPlayer")
+  (c-name "rb_player_set_volume")
+  (return-type "none")
+  (parameters
+    '("float" "volume")
+  )
+)
+
+(define-method get_volume
+  (of-object "RBPlayer")
+  (c-name "rb_player_get_volume")
+  (return-type "float")
+)
+
+(define-method set_replaygain
+  (of-object "RBPlayer")
+  (c-name "rb_player_set_replaygain")
+  (return-type "none")
+  (parameters
+    '("double" "track_gain")
+    '("double" "track_peak")
+    '("double" "album_gain")
+    '("double" "album_peak")
+  )
+)
+
+(define-method seekable
+  (of-object "RBPlayer")
+  (c-name "rb_player_seekable")
+  (return-type "gboolean")
+)
+
+(define-method set_time
+  (of-object "RBPlayer")
+  (c-name "rb_player_set_time")
+  (return-type "none")
+  (parameters
+    '("long" "time")
+  )
+)
+
+(define-method get_time
+  (of-object "RBPlayer")
+  (c-name "rb_player_get_time")
+  (return-type "long")
+)
+
+
+
+(define-method add_tee
+  (of-object "RBPlayerGstTee")
+  (c-name "rb_player_gst_tee_add_tee")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
+
+(define-method remove_tee
+  (of-object "RBPlayerGstTee")
+  (c-name "rb_player_gst_tee_remove_tee")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
+
+(define-method add_data_tee
+  (of-object "RBPlayerGstDataTee")
+  (c-name "rb_player_gst_data_tee_add_data_tee")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
+
+(define-method remove_data_tee
+  (of-object "RBPlayerGstDataTee")
+  (c-name "rb_player_gst_data_tee_remove_data_tee")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
+
+(define-method add_filter
+  (of-object "RBPlayerGstFilter")
+  (c-name "rb_player_gst_filter_add_filter")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
+
+(define-method remove_filter
+  (of-object "RBPlayerGstFilter")
+  (c-name "rb_player_gst_filter_remove_filter")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
+
Index: bindings/python/Makefile.am
===================================================================
--- bindings/python/Makefile.am	(revision 4772)
+++ bindings/python/Makefile.am	(working copy)
@@ -17,14 +17,15 @@ rb_la_LIBADD = \
 rb_la_CFLAGS = \
 	-I$(top_srcdir)			\
 	-I$(top_srcdir)/lib             \
-	-I$(top_srcdir)/metadata                       	\
-	-I$(top_srcdir)/player                       	\
-	-I$(top_srcdir)/rhythmdb                       	\
-	-I$(top_srcdir)/widgets                    	\
-	-I$(top_srcdir)/sources                    	\
-	-I$(top_srcdir)/iradio                    	\
-	-I$(top_srcdir)/podcast                    	\
-	-I$(top_srcdir)/plugins				\
+	-I$(top_srcdir)/metadata	\
+	-I$(top_srcdir)/player		\
+	-I$(top_srcdir)/rhythmdb	\
+	-I$(top_srcdir)/widgets		\
+	-I$(top_srcdir)/sources		\
+	-I$(top_srcdir)/iradio		\
+	-I$(top_srcdir)/podcast		\
+	-I$(top_srcdir)/plugins		\
+	-I$(top_srcdir)/backends	\
 	-I$(top_srcdir)/shell		\
 	$(RHYTHMBOX_CFLAGS)		\
 	$(PYGTK_CFLAGS)			\
@@ -46,6 +47,8 @@ rb.c: rb.defs rb.override
 		--prefix py$* $(<F) ) > $@ 
 
 BINDING_HEADERS_SRCDIR_IN = \
+	backends/rb-player.h			\
+	backends/gstreamer/rb-player-gst.h	\
 	plugins/rb-plugin.h			\
 	rhythmdb/rhythmdb.h			\
 	rhythmdb/rhythmdb-property-model.h	\
Index: shell/Makefile.am
===================================================================
--- shell/Makefile.am	(revision 4772)
+++ shell/Makefile.am	(working copy)
@@ -142,6 +142,7 @@ rhythmbox_LDADD += $(DBUS_LIBS)
 endif
 
 rhythmbox_LDADD += \
+	$(top_builddir)/backends/librbbackends.la	\
 	$(GDA_LIBS)					\
 	$(TOTEM_PLPARSER_LIBS)				\
 	$(HAL_LIBS)					\
Index: backends/rb-player.h
===================================================================
--- backends/rb-player.h	(revision 4772)
+++ backends/rb-player.h	(working copy)
@@ -35,9 +35,11 @@ typedef enum
 	RB_PLAYER_ERROR_INTERNAL
 } RBPlayerError;
 
+GType rb_player_error_get_type (void);
+#define RB_TYPE_PLAYER_ERROR	(rb_player_error_get_type())
+GQuark rb_player_error_quark (void);
 #define RB_PLAYER_ERROR rb_player_error_quark ()
 
-GQuark rb_player_error_quark (void);
 
 #define RB_TYPE_PLAYER         (rb_player_get_type ())
 #define RB_PLAYER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_PLAYER, RBPlayer))
Index: backends/gstreamer/rb-player-gst.c
===================================================================
--- backends/gstreamer/rb-player-gst.c	(revision 4772)
+++ backends/gstreamer/rb-player-gst.c	(working copy)
@@ -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 void rb_player_gst_set_time (RBPl
 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 @@ struct _RBPlayerGstPrivate
 	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_finalize (GObject *object)
 		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);
 }
 
@@ -637,7 +664,8 @@ rb_player_gst_construct (RBPlayerGst *mp
 #ifdef HAVE_GSTREAMER_0_8
 	/* Output sink */
 	sink = gst_gconf_get_default_audio_sink ();
-	g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", sink, NULL);
+	if (sink != NULL)
+		g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", sink, NULL);
 #endif
 #ifdef HAVE_GSTREAMER_0_10
 	gst_bus_add_watch (gst_element_get_bus (GST_ELEMENT (mp->priv->playbin)),
@@ -665,6 +693,53 @@ rb_player_gst_construct (RBPlayerGst *mp
 	} else {
 		g_object_unref (sink);
 	}
+
+	{
+		GstPad *pad;
+		GList *l;
+		GstElement *queue;
+		GstElement *audioconvert;
+		GstPad *ghostpad;
+
+		/* setup filterbin,and insert the leading audioconvert */
+		mp->priv->filterbin = gst_bin_new (NULL);
+		audioconvert = gst_element_factory_make ("audioconvert", NULL);
+		gst_bin_add (GST_BIN (mp->priv->filterbin), audioconvert);
+	
+		/* ghost it to the bin */
+		ghostpad = gst_ghost_pad_new ("sink", gst_element_get_pad (audioconvert, "sink"));
+		gst_element_add_pad (mp->priv->filterbin, ghostpad);
+		ghostpad = gst_ghost_pad_new ("src", gst_element_get_pad (audioconvert, "src"));
+		gst_element_add_pad (mp->priv->filterbin, ghostpad);
+
+
+		/* set up the sinkbin with it's tee element */
+		mp->priv->sinkbin = gst_bin_new (NULL);
+		mp->priv->tee = gst_element_factory_make ("tee", NULL);
+		queue = gst_element_factory_make ("queue", NULL);
+
+		/* link it all together and insert */
+		gst_bin_add_many (GST_BIN (mp->priv->sinkbin), mp->priv->filterbin, mp->priv->tee, queue, sink, NULL);
+		gst_element_link_many (mp->priv->filterbin, mp->priv->tee, queue, sink, NULL);
+
+		pad = gst_ghost_pad_new ("sink", gst_element_get_pad (mp->priv->filterbin, "sink"));
+		gst_element_add_pad (mp->priv->sinkbin, pad);
+		g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", mp->priv->sinkbin, NULL);
+
+
+		/* add any tees and filters that were waiting for us */
+		for (l = mp->priv->waiting_tees; l != NULL; l = g_list_next (l)) {
+			rb_player_gst_tee_add_tee (RB_PLAYER_GST_TEE (mp), GST_ELEMENT (l->data));
+		}
+		g_list_free (mp->priv->waiting_tees);
+		mp->priv->waiting_tees = NULL;
+
+		for (l = mp->priv->waiting_filters; l != NULL; l = g_list_next (l)) {
+			rb_player_gst_filter_add_filter (RB_PLAYER_GST_FILTER(mp), GST_ELEMENT (l->data));
+		}
+		g_list_free (mp->priv->waiting_filters);
+		mp->priv->waiting_filters = NULL;
+	}
 #endif
 
 	/* Use fakesink for video if there's no video sink yet */
@@ -1151,3 +1226,260 @@ rb_player_gst_get_time (RBPlayer *player
 	} 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 teee 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 teee insertion");
+				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 teee insertion");
+			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;
+
+	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 teee 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 */
+	ghostpad = gst_ghost_pad_new ("sink", gst_element_get_pad (element, "sink"));
+	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 teee insertion");
+				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 teee insertion");
+			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
Index: backends/Makefile.am
===================================================================
--- backends/Makefile.am	(revision 4772)
+++ backends/Makefile.am	(working copy)
@@ -7,6 +7,12 @@ SUBDIRS = gstreamer 
 librbbackends_la_SOURCES =				\
 	rb-player.h					\
 	rb-player.c					\
+	rb-player-gst-filter.h				\
+	rb-player-gst-filter.c				\
+	rb-player-gst-tee.h				\
+	rb-player-gst-tee.c				\
+	rb-player-gst-data-tee.h			\
+	rb-player-gst-data-tee.c			\
 	$(NULL)
 
 librbbackends_la_LIBADD =				\
Index: backends/rb-player.c
===================================================================
--- backends/rb-player.c	(revision 4772)
+++ backends/rb-player.c	(working copy)
@@ -275,3 +275,26 @@ rb_player_error_quark (void)
 
 	return quark;
 }
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+rb_player_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0)	{
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (RB_PLAYER_ERROR_NO_AUDIO, "Stream contains no audio"),
+			ENUM_ENTRY (RB_PLAYER_ERROR_GENERAL, "General error"),
+			ENUM_ENTRY (RB_PLAYER_ERROR_INTERNAL, "Internal backend error"),
+			{ 0, 0, 0 }
+		};
+
+		etype = g_enum_register_static ("RBPlayerError", values);
+	}
+
+	return etype;
+}
+
