diff -u -p -u -r1.1 rb-player.c
--- backends/rb-player.c	26 Apr 2006 08:25:44 -0000	1.1
+++ backends/rb-player.c	17 Jun 2006 10:02:03 -0000
@@ -262,3 +262,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;
+}
+
diff -u -p -u -r1.12 rb-player.h
--- backends/rb-player.h	26 Apr 2006 08:25:45 -0000	1.12
+++ backends/rb-player.h	17 Jun 2006 10:02:03 -0000
@@ -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))
diff -u -p -u -r1.62 rb-player-gst.c
--- backends/gstreamer/rb-player-gst.c	7 Jun 2006 04:12:41 -0000	1.62
+++ backends/gstreamer/rb-player-gst.c	17 Jun 2006 10:02:04 -0000
@@ -94,6 +94,11 @@ struct _RBPlayerGstPrivate
 	guint error_signal_id;
 	guint buffering_signal_id;
 #endif
+#ifdef HAVE_GSTREAMER_0_10
+	GList *waiting_tees;
+	GstElement *sinkbin;
+	GstElement *tee;
+#endif
 
 	float cur_volume;
 
@@ -197,6 +202,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);
 }
 
@@ -535,18 +547,46 @@ rb_player_gst_construct (RBPlayerGst *mp
 
 	/* Output sink */
 	sink = gst_gconf_get_default_audio_sink ();
+	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)),
+	{
+		GstPad *pad;
+		GList *l;
+		GstElement *queue;
+
+		gst_bus_add_watch (gst_element_get_bus (GST_ELEMENT (mp->priv->playbin)),
 			     (GstBusFunc) rb_player_gst_bus_cb, mp);
 
-	/* Output sink */
-	sink = gst_element_factory_make ("gconfaudiosink", "audiosink");
-#endif
+		/* Output sink */
+		sink = gst_element_factory_make ("gconfaudiosink", "audiosink");
 	
-	/* if we could create the gconf sink use that, otherwise let playbin decide */
-	if (sink != NULL)
-		g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", sink, NULL);
+		/* if we could create the gconf sink use that, otherwise let playbin decide */
+		if (sink == NULL) {
+			g_assert_not_reached ();
+			g_object_get (G_OBJECT (mp->priv->playbin), "audio-sink", &sink, NULL);
+		}
+
+		/* 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);
+
+		gst_bin_add_many (GST_BIN (mp->priv->sinkbin), mp->priv->tee, queue, sink, NULL);
+		gst_element_link_many (mp->priv->tee, queue, sink, NULL);
+
+		pad = gst_ghost_pad_new ("sink", gst_element_get_pad (mp->priv->tee, "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 that were waiting for us */
+		for (l = mp->priv->waiting_tees; l != NULL; l = g_list_next (l)) {
+			rb_player_gst_add_tee_element (mp, GST_ELEMENT (l->data));
+		}
+		g_list_free (mp->priv->waiting_tees);
+		mp->priv->waiting_tees = NULL;
+	}
+#endif
 
 	if (mp->priv->cur_volume > 1.0)
 		mp->priv->cur_volume = 1.0;
@@ -1038,5 +1078,87 @@ rb_player_gst_get_time (RBPlayer *player
 		return (long) position;
 	} else
 		return -1;
+}
+
+
+void
+rb_player_gst_add_tee_element (RBPlayerGst *mp, GstElement *element)
+{
+#ifdef HAVE_GSTREAMER_0_10
+	GstElement *queue, *audioconvert, *bin;
+	GstPad *pad;
+
+	if (mp->priv->tee == NULL) {
+		mp->priv->waiting_tees = g_list_prepend (mp->priv->waiting_tees, element);
+		return;
+	}
+	
+	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");
+			}
+		}
+	}
+
+	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);
+	}
+#endif
+}
+
+void
+rb_player_gst_remove_tee_element (RBPlayerGst *mp, GstElement *element)
+{
+#ifdef HAVE_GSTREAMER_0_10
+	GstElement *bin;
+
+	if (mp->priv->tee == NULL) {
+		gst_object_sink (element);
+		mp->priv->waiting_tees = g_list_remove (mp->priv->waiting_tees, element);
+		return;
+	}
+
+	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");
+			}
+		}
+	}
+
+	/* 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");
+		}
+	}
+
+	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);
+	}
+#endif
 }
 
diff -u -p -u -r1.1 rb-player-gst.h
--- backends/gstreamer/rb-player-gst.h	26 Apr 2006 08:25:45 -0000	1.1
+++ backends/gstreamer/rb-player-gst.h	17 Jun 2006 10:02:04 -0000
@@ -22,6 +22,7 @@
 #define __RB_PLAYER_GST_H__
 
 #include <glib-object.h>
+#include <gst/gst.h>
 
 #include "rb-player.h"
 
@@ -48,6 +49,8 @@ typedef struct
 RBPlayer*	rb_player_gst_new (GError **error);
 GType		rb_player_gst_get_type (void);
 
+void		rb_player_gst_add_tee_element (RBPlayerGst *player, GstElement *element);
+void		rb_player_gst_remove_tee_element (RBPlayerGst *player, GstElement *element);
 
 G_END_DECLS
 
diff -u -p -u -r1.11 Makefile.am
--- bindings/python/Makefile.am	17 May 2006 03:52:54 -0000	1.11
+++ bindings/python/Makefile.am	17 Jun 2006 10:02:05 -0000
@@ -15,21 +15,23 @@ rb_la_LIBADD = \
 	$(PYGTK_LIBS)
 
 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)					\
+	-I$(top_srcdir)/backends			\
+	-I$(top_srcdir)/backends/gstreamer		\
+	-I$(top_srcdir)/iradio				\
+	-I$(top_srcdir)/lib				\
+	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/player				\
 	-I$(top_srcdir)/plugins				\
-	-I$(top_srcdir)/shell		\
-	$(RHYTHMBOX_CFLAGS)		\
-	$(PYGTK_CFLAGS)			\
-	$(PYTHON_CFLAGS)		\
-	$(AM_CFLAGS)			\
+	-I$(top_srcdir)/podcast				\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/sources				\
+	-I$(top_srcdir)/widgets				\
+	$(RHYTHMBOX_CFLAGS)				\
+	$(PYGTK_CFLAGS)					\
+	$(PYTHON_CFLAGS)				\
+	$(AM_CFLAGS)					\
 	$(WNOERROR_CFLAGS)
 
 rhythmdb.c: rhythmdb.defs rhythmdb.override
@@ -46,6 +48,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	\
diff -u -p -u -r1.22 rb.defs
--- bindings/python/rb.defs	4 Jun 2006 21:20:26 -0000	1.22
+++ bindings/python/rb.defs	17 Jun 2006 10:02:05 -0000
@@ -85,6 +85,21 @@
   (gtype-id "RB_TYPE_SONG_INFO")
 )
 
+(define-interface Player
+  (in-module "RB")
+  (c-name "RBPlayer")
+  (gtype-id "RB_TYPE_PLAYER")
+)
+
+(define-object PlayerGst
+  (in-module "RB")
+  (parent "GObject")
+  (interface "RBPlayer")
+  (c-name "RBPlayerGst")
+  (gtype-id "RB_TYPE_PLAYER_GST")
+)
+
+
 ;; Enumerations and flags ...
 
 (define-enum ShellPlayerError
@@ -99,6 +114,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")
@@ -1588,3 +1614,152 @@
   )
 )
 
+
+;; 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")
+)
+
+;; From ../../backends/gstreamer/rb-player-gst.h
+
+(define-function rb_player_gst_new
+  (c-name "rb_player_gst_new")
+  (is-constructor-of "RbPlayerGst")
+  (return-type "RBPlayer*")
+  (parameters
+    '("GError**" "error")
+  )
+)
+
+(define-function rb_player_gst_get_type
+  (c-name "rb_player_gst_get_type")
+  (return-type "GType")
+)
+
+(define-method add_tee_element
+  (of-object "RBPlayerGst")
+  (c-name "rb_player_gst_add_tee_element")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
+
+(define-method remove_tee_element
+  (of-object "RBPlayerGst")
+  (c-name "rb_player_gst_remove_tee_element")
+  (return-type "none")
+  (parameters
+    '("GObject*" "element")
+  )
+)
diff -u -p -u -r1.17 rb.override
--- bindings/python/rb.override	17 May 2006 03:52:55 -0000	1.17
+++ bindings/python/rb.override	17 Jun 2006 10:02:05 -0000
@@ -10,6 +10,8 @@ headers
 #include "rb-browser-source.h"
 #include "rb-entry-view.h"
 #include "rb-library-browser.h"
+#include "rb-player.h"
+#include "rb-player-gst.h"
 #include "rb-plugin.h"
 #include "rb-property-view.h"
 #include "rb-removable-media-manager.h"
diff -u -p -u -r1.245 rb-shell-player.c
--- shell/rb-shell-player.c	17 Jun 2006 04:12:52 -0000	1.245
+++ shell/rb-shell-player.c	17 Jun 2006 10:02:07 -0000
@@ -224,7 +224,8 @@ enum
 	PROP_QUEUE_SOURCE,
 	PROP_QUEUE_ONLY,
 	PROP_STREAM_SONG,
-	PROP_PLAYING_FROM_QUEUE
+	PROP_PLAYING_FROM_QUEUE,
+	PROP_PLAYER,
 };
 
 enum
@@ -345,6 +346,14 @@ rb_shell_player_class_init (RBShellPlaye
 							       FALSE,
 							       G_PARAM_READABLE));
 	
+	g_object_class_install_property (object_class,
+					 PROP_PLAYER,
+					 g_param_spec_object ("player",
+							      "RBPlayer",
+							      "RBPlayer object",
+							      G_TYPE_OBJECT,
+							      G_PARAM_READABLE));
+
 	/* If you change these, be sure to update the CORBA interface
 	 * in rb-remote-bonobo.c! */
 	g_object_class_install_property (object_class,
@@ -812,6 +821,9 @@ rb_shell_player_get_property (GObject *o
 		break;
 	case PROP_PLAYING_FROM_QUEUE:
 		g_value_set_boolean (value, player->priv->current_playing_source == RB_SOURCE (player->priv->queue_source));
+		break;
+	case PROP_PLAYER:
+		g_value_set_object (value, player->priv->mmplayer);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
