diff --git a/backends/gstreamer/Makefile.am b/backends/gstreamer/Makefile.am
index efb3bcb..b85e51c 100644
--- a/backends/gstreamer/Makefile.am
+++ b/backends/gstreamer/Makefile.am
@@ -8,7 +8,7 @@ librbbackendsgstreamer_la_SOURCES =			\
 	rb-player-gst-helper.c				\
 	rb-player-gst-helper.h				\
 	rb-player-gst.h					\
-	rb-player-gst.c					\
+	rb-player-gst2.c				\
 	rb-player-gst-xfade.h				\
 	rb-player-gst-xfade.c				\
 	$(NULL)
diff --git a/backends/gstreamer/rb-player-gst2.c b/backends/gstreamer/rb-player-gst2.c
new file mode 100644
index 0000000..bec5399
--- /dev/null
+++ b/backends/gstreamer/rb-player-gst2.c
@@ -0,0 +1,1346 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2003 Jorn Baayen <jorn@nl.linux.org>
+ *  Copyright (C) 2003,2004 Colin Walters <walters@debian.org>
+ *  Copyright (C) 2009 Jonathan Matthew  <jonathan@d14n.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include <gst/tag/tag.h>
+#include <gst/pbutils/pbutils.h>
+
+#include "rb-debug.h"
+#include "rb-marshal.h"
+#include "rb-util.h"
+
+#include "rb-player.h"
+#include "rb-player-gst.h"
+#include "rb-player-gst-filter.h"
+#include "rb-player-gst-tee.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);
+
+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_GST_FILTER, rb_player_gst_filter_init)
+			G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER_GST_TEE, rb_player_gst_tee_init)
+			)
+
+#define MAX_NETWORK_BUFFER_SIZE		(2048)
+
+#define RB_PLAYER_GST_TICK_HZ 5
+#define STATE_CHANGE_MESSAGE_TIMEOUT 5
+
+enum
+{
+	PROP_0,
+	PROP_PLAYBIN,
+	PROP_BUFFER_SIZE
+};
+
+enum
+{
+	MISSING_PLUGINS,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _RBPlayerGstPrivate
+{
+	char *uri;
+	gpointer stream_data;
+	GDestroyNotify stream_data_destroy;
+
+	GstElement *playbin;
+	GstElement *audio_sink;
+	guint buffer_size;
+
+	gboolean playing;
+	gboolean buffering;
+
+	gboolean stream_change_pending;
+	gboolean current_track_finishing;
+
+	gboolean emitted_error;
+
+	float cur_volume;
+	float replaygain_scale;
+
+	guint tick_timeout_id;
+
+	GList *waiting_tees;
+	GstElement *sinkbin;
+	GstElement *tee;
+
+	GList *waiting_filters; /* in reverse order */
+	GstElement *filterbin;
+};
+
+static gboolean
+tick_timeout (RBPlayerGst *mp)
+{
+	if (mp->priv->playing) {
+		_rb_player_emit_tick (RB_PLAYER (mp),
+				      mp->priv->stream_data,
+				      rb_player_get_time (RB_PLAYER (mp)),
+				      -1);
+	}
+	return TRUE;
+}
+
+
+static void
+about_to_finish_cb (GstElement *playbin, RBPlayerGst *player)
+{
+	if (player->priv->stream_change_pending == TRUE) {
+		/* this probably shouldn't happen, but it's OK if it does, I think */
+		rb_debug ("got about-to-finish, but we already have a stream change pending.");
+		return;
+	}
+
+	/* emit EOS now and hope we get something to play */
+	player->priv->current_track_finishing = TRUE;
+
+	/* hmm, might need to do this from an idle handler.. */
+	_rb_player_emit_eos (RB_PLAYER (player), player->priv->stream_data);
+}
+
+static gboolean
+emit_volume_changed_idle (RBPlayerGst *player)
+{
+	_rb_player_emit_volume_changed (RB_PLAYER (player), player->priv->cur_volume);
+	return FALSE;
+}
+
+static void
+volume_notify_cb (GObject *element, GParamSpec *pspec, RBPlayerGst *player)
+{
+	gdouble v;
+	g_object_get (element, "volume", &v, NULL);
+	player->priv->cur_volume = v;
+
+	g_idle_add ((GSourceFunc) emit_volume_changed_idle, player);
+}
+
+static void
+process_tag (const GstTagList *list, const gchar *tag, RBPlayerGst *player)
+{
+	int count;
+	RBMetaDataField field;
+	const GValue *val;
+	GValue newval = {0,};
+
+	count = gst_tag_list_get_tag_size (list, tag);
+	if (count < 1)
+		return;
+
+	/* only handle the subset of fields we use for iradio */
+	if (!strcmp (tag, GST_TAG_TITLE))
+		field = RB_METADATA_FIELD_TITLE;
+	else if (!strcmp (tag, GST_TAG_GENRE))
+		field = RB_METADATA_FIELD_GENRE;
+	else if (!strcmp (tag, GST_TAG_COMMENT))
+		field = RB_METADATA_FIELD_COMMENT;
+	else if (!strcmp (tag, GST_TAG_BITRATE))
+		field = RB_METADATA_FIELD_BITRATE;
+#ifdef GST_TAG_MUSICBRAINZ_TRACKID
+	else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_TRACKID))
+		field = RB_METADATA_FIELD_MUSICBRAINZ_TRACKID;
+#endif
+	else
+		return;
+
+	/* of those, all except bitrate are strings */
+	switch (field) {
+	case RB_METADATA_FIELD_BITRATE:
+		g_value_init (&newval, G_TYPE_ULONG);
+		break;
+
+	default:
+		g_value_init (&newval, G_TYPE_STRING);
+		break;
+	}
+
+	val = gst_tag_list_get_value_index (list, tag, 0);
+	if (!g_value_transform (val, &newval)) {
+		rb_debug ("Could not transform tag value type %s into %s",
+			  g_type_name (G_VALUE_TYPE (val)),
+			  g_type_name (G_VALUE_TYPE (&newval)));
+
+		return;
+	}
+
+	_rb_player_emit_info (RB_PLAYER (player), player->priv->stream_data, field, &newval);
+	g_value_unset (&newval);
+}
+
+static void
+handle_missing_plugin_message (RBPlayerGst *player, GstMessage *message)
+{
+	char **details;
+	char **descriptions;
+	char *detail;
+	char *description;
+	int count;
+
+	rb_debug ("got missing-plugin message from %s: %s",
+		  GST_OBJECT_NAME (GST_MESSAGE_SRC (message)),
+		  gst_missing_plugin_message_get_installer_detail (message));
+
+	/* probably need to wait to collect any subsequent missing-plugin
+	 * messages, but I think we'd need to wait for state changes to do
+	 * that.  for now, we can only handle a single message.
+	 */
+	count = 1;
+
+	details = g_new0 (char *, count + 1);
+	descriptions = g_new0 (char *, count + 1);
+
+	detail = gst_missing_plugin_message_get_installer_detail (message);
+	description = gst_missing_plugin_message_get_description (message);
+	details[0] = g_strdup (detail);
+	descriptions[0] = g_strdup (description);
+
+	g_signal_emit (player, signals[MISSING_PLUGINS], 0, player->priv->stream_data, details, descriptions);
+	g_strfreev (details);
+	g_strfreev (descriptions);
+}
+
+static gboolean
+bus_cb (GstBus *bus, GstMessage *message, RBPlayerGst *mp)
+{
+	const GstStructure *structure;
+	g_return_val_if_fail (mp != NULL, FALSE);
+
+	switch (GST_MESSAGE_TYPE (message)) {
+	case GST_MESSAGE_ERROR: {
+		char *debug;
+		GError *error, *sig_error;
+		int code;
+		gboolean emit = TRUE;
+
+		gst_message_parse_error (message, &error, &debug);
+
+		/* If we've already got an error, ignore 'internal data flow error'
+		 * type messages, as they're too generic to be helpful.
+		 */
+		if (mp->priv->emitted_error &&
+		    error->domain == GST_STREAM_ERROR &&
+		    error->code == GST_STREAM_ERROR_FAILED) {
+			rb_debug ("Ignoring generic error \"%s\"", error->message);
+			emit = FALSE;
+		}
+
+		if ((error->domain == GST_CORE_ERROR)
+			|| (error->domain == GST_LIBRARY_ERROR)
+			|| (error->domain == GST_RESOURCE_ERROR && error->code == GST_RESOURCE_ERROR_BUSY)) {
+			code = RB_PLAYER_ERROR_NO_AUDIO;
+		} else {
+			code = RB_PLAYER_ERROR_GENERAL;
+		}
+
+		if (emit) {
+			sig_error = g_error_new_literal (RB_PLAYER_ERROR,
+							 code,
+							 error->message);
+			mp->priv->emitted_error = TRUE;
+			_rb_player_emit_error (RB_PLAYER (mp), mp->priv->stream_data, sig_error);
+		}
+
+		/* close if not already closing */
+		if (mp->priv->uri != NULL)
+			rb_player_close (RB_PLAYER (mp), NULL, NULL);
+
+		g_error_free (error);
+		g_free (debug);
+		break;
+	}
+
+	case GST_MESSAGE_EOS:
+		rb_debug ("got EOS.. why haven't you told me what to do yet?");
+		_rb_player_emit_eos (RB_PLAYER (mp), mp->priv->stream_data);
+		break;
+
+	case GST_MESSAGE_TAG: {
+		GstTagList *tags;
+		gst_message_parse_tag (message, &tags);
+
+		gst_tag_list_foreach (tags, (GstTagForeachFunc) process_tag, mp);
+		gst_tag_list_free (tags);
+		break;
+	}
+
+	case GST_MESSAGE_BUFFERING: {
+		gint progress;
+
+		structure = gst_message_get_structure (message);
+		if (!gst_structure_get_int (structure, "buffer-percent", &progress)) {
+			g_warning ("Could not get value from BUFFERING message");
+			break;
+		}
+		if (progress >= 100) {
+			mp->priv->buffering = FALSE;
+			if (mp->priv->playing) {
+				rb_debug ("buffering done, setting pipeline back to PLAYING");
+				gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+			} else {
+				rb_debug ("buffering done, leaving pipeline PAUSED");
+			}
+		} else if (mp->priv->buffering == FALSE && mp->priv->playing) {
+			GstState cur_state;
+
+			gst_element_get_state (mp->priv->playbin, &cur_state, NULL, 0);
+			if (cur_state == GST_STATE_PLAYING) {
+				rb_debug ("buffering - temporarily pausing playback");
+				gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED);
+			} else {
+				rb_debug ("buffering - during preroll; doing nothing");
+			}
+			mp->priv->buffering = TRUE;
+		}
+
+		_rb_player_emit_buffering (RB_PLAYER (mp), mp->priv->stream_data, progress);
+		break;
+	}
+
+	case GST_MESSAGE_APPLICATION:
+		structure = gst_message_get_structure (message);
+		_rb_player_emit_event (RB_PLAYER (mp), mp->priv->stream_data, gst_structure_get_name (structure), NULL);
+		break;
+
+	case GST_MESSAGE_ELEMENT:
+		if (gst_is_missing_plugin_message (message)) {
+			handle_missing_plugin_message (mp, message);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	/* emit message signals too, so plugins can process messages */
+	gst_bus_async_signal_func (bus, message, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+construct_pipeline (RBPlayerGst *mp, GError **error)
+{
+	GstElement *sink;
+
+	mp->priv->playbin = gst_element_factory_make ("playbin2", NULL);
+	if (mp->priv->playbin == NULL) {
+		g_set_error (error,
+			     RB_PLAYER_ERROR,
+			     RB_PLAYER_ERROR_GENERAL,
+			     _("Failed to create playbin2 element; check your GStreamer installation"));
+		return FALSE;
+	}
+	g_signal_connect_object (G_OBJECT (mp->priv->playbin),
+				 "about-to-finish",
+				 G_CALLBACK (about_to_finish_cb),
+				 mp, 0);
+	g_signal_connect_object (G_OBJECT (mp->priv->playbin),
+				 "notify::volume",
+				 G_CALLBACK (volume_notify_cb),
+				 mp, 0);
+	if (mp->priv->buffer_size != 0) {
+		g_object_set (mp->priv->playbin, "buffer-size", mp->priv->buffer_size * 1024, NULL);
+	}
+
+	gst_bus_add_watch (gst_element_get_bus (mp->priv->playbin),
+			   (GstBusFunc) bus_cb,
+			   mp);
+
+	/* let plugins add bits to playbin */
+	g_object_notify (G_OBJECT (mp), "playbin");
+
+	/* Use gconfaudiosink for audio if there's no audio sink yet */
+	g_object_get (mp->priv->playbin, "audio-sink", &mp->priv->audio_sink, NULL);
+	if (mp->priv->audio_sink == NULL) {
+		mp->priv->audio_sink = gst_element_factory_make ("gconfaudiosink", NULL);
+		if (mp->priv->audio_sink == NULL) {
+			/* fall back to autoaudiosink */
+			rb_debug ("falling back to autoaudiosink");
+			mp->priv->audio_sink = gst_element_factory_make ("autoaudiosink", NULL);
+		} else {
+			rb_debug ("using gconfaudiosink");
+		}
+
+		if (mp->priv->audio_sink != NULL) {
+			/* set the profile property on the gconfaudiosink to "music and movies" */
+			if (g_object_class_find_property (G_OBJECT_GET_CLASS (mp->priv->audio_sink), "profile")) {
+				rb_debug ("setting profile property on audio sink");
+				g_object_set (mp->priv->audio_sink, "profile", 1, NULL);
+			}
+
+			g_object_set (mp->priv->playbin, "audio-sink", mp->priv->audio_sink, NULL);
+		}
+	} else {
+		rb_debug ("existing audio sink found");
+		g_object_unref (mp->priv->audio_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 */
+		pad = gst_element_get_pad (audioconvert, "sink");
+		ghostpad = gst_ghost_pad_new ("sink", pad);
+		gst_element_add_pad (mp->priv->filterbin, ghostpad);
+		gst_object_unref (pad);
+
+		pad = gst_element_get_pad (audioconvert, "src");
+		ghostpad = gst_ghost_pad_new ("src", pad);
+		gst_element_add_pad (mp->priv->filterbin, ghostpad);
+		gst_object_unref (pad);
+
+		/* set up the sinkbin with its 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, mp->priv->audio_sink, NULL);
+		gst_element_link_many (mp->priv->filterbin, mp->priv->tee, queue, mp->priv->audio_sink, NULL);
+
+		pad = gst_element_get_pad (mp->priv->filterbin, "sink");
+		ghostpad = gst_ghost_pad_new ("sink", pad);
+		gst_element_add_pad (mp->priv->sinkbin, ghostpad);
+		gst_object_unref (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;
+	}
+
+	/* Use fakesink for video if there's no video sink yet */
+	g_object_get (mp->priv->playbin, "video-sink", &sink, NULL);
+	if (sink == NULL) {
+		sink = gst_element_factory_make ("fakesink", NULL);
+		g_object_set (mp->priv->playbin, "video-sink", sink, NULL);
+	} else {
+		g_object_unref (sink);
+	}
+
+	if (mp->priv->cur_volume > 1.0)
+		mp->priv->cur_volume = 1.0;
+	if (mp->priv->cur_volume < 0.0)
+		mp->priv->cur_volume = 0;
+	mp->priv->replaygain_scale = 1.0f;
+
+	rb_player_set_volume (RB_PLAYER (mp), mp->priv->cur_volume);
+
+	rb_debug ("pipeline construction complete");
+	return TRUE;
+}
+
+static gboolean
+message_from_sink (GstElement *sink, GstMessage *message)
+{
+	GstElement *src;
+	GstElement *match;
+	char *name;
+
+	src = GST_ELEMENT (GST_MESSAGE_SRC (message));
+
+	if (GST_IS_BIN (sink) == FALSE) {
+		return (src == sink);
+	}
+
+	name = gst_element_get_name (src);
+	match = gst_bin_get_by_name (GST_BIN (sink), name);
+	g_free (name);
+
+	if (match != NULL) {
+		g_object_unref (match);
+		return (match == src);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+set_state_and_wait (RBPlayerGst *player, GstState target, GError **error)
+{
+	GstBus *bus;
+	gboolean waiting;
+	gboolean result;
+
+	g_assert (player->priv->playbin != NULL);
+	/* XXX probably need to remove bus watch here if we're not on the main thread */
+	/* .. probably shouldn't be doing this much anyway .. */
+
+	rb_debug ("setting playbin state to %s", gst_element_state_get_name (target));
+
+	switch (gst_element_set_state (player->priv->playbin, target)) {
+	case GST_STATE_CHANGE_SUCCESS:
+		rb_debug ("state change was successful");
+		return TRUE;
+
+	case GST_STATE_CHANGE_NO_PREROLL:
+		rb_debug ("state change was successful (no preroll)");
+		return TRUE;
+
+	case GST_STATE_CHANGE_ASYNC:
+		rb_debug ("state is changing asynchronously");
+		result = TRUE;
+		break;
+
+	case GST_STATE_CHANGE_FAILURE:
+		rb_debug ("state change failed");
+		result = FALSE;
+		break;
+
+	default:
+		rb_debug ("unknown state change return..");
+		result = TRUE;
+		break;
+	}
+
+	bus = gst_element_get_bus (player->priv->playbin);
+	waiting = TRUE;
+	while (waiting) {
+		GstMessage *message;
+
+		message = gst_bus_timed_pop (bus, GST_SECOND * STATE_CHANGE_MESSAGE_TIMEOUT);
+		if (message == NULL) {
+			rb_debug ("state change is taking too long..");
+			break;
+		}
+
+		switch (GST_MESSAGE_TYPE (message)) {
+		case GST_MESSAGE_ERROR:
+			{
+				char *debug;
+				GError *gst_error = NULL;
+
+				gst_message_parse_error (message, &gst_error, &debug);
+
+				if (message_from_sink (player->priv->audio_sink, message)) {
+					rb_debug ("got error from sink: %s (%s)", gst_error->message, debug);
+					g_set_error (error,
+						     RB_PLAYER_ERROR,
+						     RB_PLAYER_ERROR_INTERNAL,
+						     _("Failed to open output device: %s"),
+						     gst_error->message);
+				} else {
+					rb_debug ("got error from stream: %s (%s)", gst_error->message, debug);
+					g_set_error (error,
+						     RB_PLAYER_ERROR,
+						     RB_PLAYER_ERROR_GENERAL,
+						     "%s",
+						     gst_error->message);
+				}
+
+				g_error_free (gst_error);
+				g_free (debug);
+
+				waiting = FALSE;
+				result = FALSE;
+				break;
+			}
+
+		case GST_MESSAGE_STATE_CHANGED:
+			{
+				GstState oldstate;
+				GstState newstate;
+				GstState pending;
+				gst_message_parse_state_changed (message, &oldstate, &newstate, &pending);
+				if (GST_MESSAGE_SRC (message) == GST_OBJECT (player->priv->playbin)) {
+					rb_debug ("playbin reached state %s", gst_element_state_get_name (newstate));
+					if (pending == GST_STATE_VOID_PENDING && newstate == target) {
+						waiting = FALSE;
+					}
+				}
+				break;
+			}
+
+		default:
+			/* pass back to regular message handler */
+			bus_cb (bus, message, player);
+			break;
+		}
+	}
+
+	if (result == FALSE && *error == NULL) {
+		g_set_error (error,
+			     RB_PLAYER_ERROR,
+			     RB_PLAYER_ERROR_GENERAL,
+			     _("Unable to start playback pipeline"));
+	}
+
+	return result;
+}
+
+static void
+_destroy_stream_data (RBPlayerGst *player)
+{
+	if (player->priv->stream_data && player->priv->stream_data_destroy) {
+		player->priv->stream_data_destroy (player->priv->stream_data);
+	}
+	player->priv->stream_data = NULL;
+	player->priv->stream_data_destroy = NULL;
+}
+
+static gboolean
+impl_close (RBPlayer *player, const char *uri, GError **error)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+
+	mp->priv->playing = FALSE;
+	mp->priv->buffering = FALSE;
+
+	if ((uri != NULL) && (mp->priv->uri != NULL) && strcmp (mp->priv->uri, uri) == 0) {
+		rb_debug ("URI doesn't match current playing URI; ignoring");
+		return TRUE;
+	}
+
+	_destroy_stream_data (mp);
+	g_free (mp->priv->uri);
+	mp->priv->uri = NULL;
+
+	mp->priv->replaygain_scale = 1.0f;
+
+	if (mp->priv->tick_timeout_id != 0) {
+		g_source_remove (mp->priv->tick_timeout_id);
+		mp->priv->tick_timeout_id = 0;
+	}
+
+	if (mp->priv->playbin == NULL)
+		return TRUE;
+
+	return set_state_and_wait (mp, GST_STATE_READY, error);
+}
+
+static gboolean
+impl_open (RBPlayer *player,
+	   const char *uri,
+	   gpointer stream_data,
+	   GDestroyNotify stream_data_destroy,
+	   GError **error)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+
+	if (mp->priv->playbin == NULL) {
+		if (!construct_pipeline (mp, error))
+			return FALSE;
+	}
+
+	g_assert (mp->priv->playbin != NULL);
+
+	if (uri == NULL) {
+		return impl_close (player, NULL, error);
+	}
+
+	rb_debug ("setting new uri to %s", uri);
+	_destroy_stream_data (mp);
+	g_free (mp->priv->uri);
+	mp->priv->uri = g_strdup (uri);
+	mp->priv->stream_data = stream_data;
+	mp->priv->stream_data_destroy = stream_data_destroy;
+	mp->priv->emitted_error = FALSE;
+	mp->priv->stream_change_pending = TRUE;
+
+	return TRUE;
+}
+
+static gboolean
+impl_opened (RBPlayer *player)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+
+	return mp->priv->uri != NULL;
+}
+
+static gboolean
+impl_play (RBPlayer *player, gint crossfade, GError **error)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+	gboolean result;
+
+	g_return_val_if_fail (mp->priv->playbin != NULL, FALSE);
+
+	if (mp->priv->stream_change_pending == FALSE) {
+		rb_debug ("no stream change pending, just restarting playback");
+		result = set_state_and_wait (mp, GST_STATE_PLAYING, error);
+
+	} else if (mp->priv->current_track_finishing) {
+		rb_debug ("current track finishing -> just setting URI on playbin");
+		g_object_set (mp->priv->playbin, "uri", mp->priv->uri, NULL);
+
+		_rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data);
+
+		result = TRUE;
+	} else {
+		rb_debug ("not in transition, stopping current track to start the new one");
+		result = set_state_and_wait (mp, GST_STATE_READY, error);
+		if (result == TRUE) {
+			g_object_set (mp->priv->playbin, "uri", mp->priv->uri, NULL);
+
+			_rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data);
+			result = set_state_and_wait (mp, GST_STATE_PLAYING, error);
+		}
+	}
+
+	mp->priv->stream_change_pending = FALSE;
+
+	if (result) {
+		mp->priv->current_track_finishing = FALSE;
+		mp->priv->buffering = FALSE;
+		mp->priv->playing = TRUE;
+
+		if (mp->priv->tick_timeout_id == 0) {
+			mp->priv->tick_timeout_id =
+				g_timeout_add (1000 / RB_PLAYER_GST_TICK_HZ,
+					       (GSourceFunc) tick_timeout,
+					       mp);
+		}
+	}
+
+	return result;
+}
+
+static void
+impl_pause (RBPlayer *player)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+	GError *error = NULL;
+
+	if (!mp->priv->playing)
+		return;
+
+	mp->priv->playing = FALSE;
+
+	g_return_if_fail (mp->priv->playbin != NULL);
+
+	if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+		g_warning ("unable to pause playback: %s\n", error->message);
+		g_error_free (error);
+	}
+
+	if (mp->priv->tick_timeout_id != 0) {
+		g_source_remove (mp->priv->tick_timeout_id);
+		mp->priv->tick_timeout_id = 0;
+	}
+}
+
+static gboolean
+impl_playing (RBPlayer *player)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+
+	return mp->priv->playing;
+}
+
+static void
+impl_set_replaygain (RBPlayer *player,
+		     const char *uri,
+		     double track_gain,
+		     double track_peak,
+		     double album_gain,
+		     double album_peak)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+	double scale;
+	double gain = 0;
+	double peak = 0;
+
+	if (album_gain != 0)
+		gain = album_gain;
+	else
+		gain = track_gain;
+
+	if (gain == 0)
+		return;
+
+	scale = pow (10., gain / 20);
+
+	/* anti clip */
+	if (album_peak != 0)
+		peak = album_peak;
+	else
+		peak = track_peak;
+
+	if (peak != 0 && (scale * peak) > 1)
+		scale = 1.0 / peak;
+
+	/* For security */
+	if (scale > 15)
+		scale = 15;
+
+	rb_debug ("Scale : %f New volume : %f", scale, mp->priv->cur_volume * scale);
+	mp->priv->replaygain_scale = scale;
+
+	if (mp->priv->playbin != NULL) {
+		g_object_set (mp->priv->playbin, "volume", mp->priv->cur_volume * scale, NULL);
+	}
+}
+
+static void
+impl_set_volume (RBPlayer *player,
+		 float volume)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+	g_return_if_fail (volume >= 0.0 && volume <= 1.0);
+
+	if (mp->priv->playbin != NULL) {
+		g_object_set (mp->priv->playbin, "volume", volume * mp->priv->replaygain_scale, NULL);
+	}
+
+	mp->priv->cur_volume = volume;
+}
+
+static float
+impl_get_volume (RBPlayer *player)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+
+	return mp->priv->cur_volume;
+}
+
+static gboolean
+impl_seekable (RBPlayer *player)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+	gboolean can_seek = TRUE;
+	GstQuery *query;
+
+	if (mp->priv->playbin == NULL)
+		return FALSE;
+
+	query = gst_query_new_seeking (GST_FORMAT_TIME);
+	if (gst_element_query (mp->priv->playbin, query)) {
+		gst_query_parse_seeking (query, NULL, &can_seek, NULL, NULL);
+	} else {
+		gst_query_unref (query);
+
+		query = gst_query_new_duration (GST_FORMAT_TIME);
+		can_seek = gst_element_query (mp->priv->playbin, query);
+	}
+	gst_query_unref (query);
+
+	return can_seek;
+}
+
+static void
+impl_set_time (RBPlayer *player, long time)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+	GError *error = NULL;
+
+	g_return_if_fail (time >= 0);
+
+	g_return_if_fail (mp->priv->playbin != NULL);
+
+	if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+		g_warning ("got error while pausing the pipelink for seeking: %s\n", error->message);
+		g_clear_error (&error);
+
+		/* keep going anyway? */
+	}
+
+	gst_element_seek (mp->priv->playbin, 1.0,
+			  GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+			  GST_SEEK_TYPE_SET, time * GST_SECOND,
+			  GST_SEEK_TYPE_NONE, -1);
+
+	if (mp->priv->playing) {
+		if (set_state_and_wait (mp, GST_STATE_PLAYING, &error) == FALSE) {
+			g_warning ("unable to resume playback after seeking: %s\n", error->message);
+			g_clear_error (&error);
+		}
+	}
+}
+
+static long
+impl_get_time (RBPlayer *player)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (player);
+
+	if (mp->priv->playbin != NULL) {
+		gint64 position = -1;
+		GstFormat fmt = GST_FORMAT_TIME;
+
+		gst_element_query_position (mp->priv->playbin, &fmt, &position);
+		if (position != -1)
+			position /= GST_SECOND;
+
+		return (long) position;
+	} else {
+		return -1;
+	}
+}
+
+static gboolean
+impl_add_tee (RBPlayerGstTee *player, GstElement *element)
+{
+	RBPlayerGst *mp;
+	GstElement *queue, *audioconvert, *bin;
+	GstPad *pad, *ghostpad;
+
+	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) {
+		GError *error = NULL;
+		if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+			g_warning ("Failed to pause pipeline before tee insertion: %s", error->message);
+			g_error_free (error);
+			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_element_get_pad (queue, "sink");
+	ghostpad = gst_ghost_pad_new ("sink", pad);
+	gst_element_add_pad (bin, ghostpad);
+	gst_object_unref (pad);
+
+	gst_element_link (mp->priv->tee, bin);
+
+	if (mp->priv->playing)
+		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+
+	_rb_player_gst_tee_emit_tee_inserted (player, element);
+
+	return TRUE;
+}
+
+static gboolean
+impl_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;
+	}
+
+	_rb_player_gst_tee_emit_tee_pre_remove (player, element);
+
+	if (mp->priv->playing) {
+		GError *error = NULL;
+		if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+			g_warning ("Failed to pause pipeline before tee removal: %s", error->message);
+			g_error_free (error);
+			return FALSE;
+		}
+	}
+
+	/* get the containing bin and unlink it */
+	bin = GST_ELEMENT (gst_element_get_parent (element));
+
+	gst_element_set_state (bin, GST_STATE_NULL);
+
+	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
+impl_add_filter (RBPlayerGstFilter *player, GstElement *element)
+{
+	RBPlayerGst *mp;
+	GstElement *audioconvert, *bin;
+	GstPad *ghostpad, *realpad;
+	GstPad *binsinkpad, *binsrcpad;
+	gpointer element_sink_pad;
+	GstIterator *sink_pads;
+	gboolean sink_pad_found, stop_scan;
+	GstPadLinkReturn link;
+
+	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) {
+		GError *error = NULL;
+		if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+			g_warning ("Failed to pause pipeline before filter insertion: %s", error->message);
+			g_error_free (error);
+			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) {
+		gpointer *esp_pointer = &element_sink_pad; /* stop type-punning warnings */
+		switch (gst_iterator_next (sink_pads, esp_pointer)) {
+			case GST_ITERATOR_OK:
+				sink_pad_found = !gst_pad_is_linked (GST_PAD(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;
+				break;
+		}
+	}
+	gst_iterator_free (sink_pads);
+
+	if (!sink_pad_found) {
+		g_warning ("Could not find a free sink pad on filter");
+		return FALSE;
+	}
+
+	binsinkpad = gst_ghost_pad_new ("sink", GST_PAD (element_sink_pad));
+	gst_element_add_pad (bin, binsinkpad);
+
+	realpad = gst_element_get_pad (audioconvert, "src");
+	binsrcpad = gst_ghost_pad_new ("src", realpad);
+	gst_element_add_pad (bin, binsrcpad);
+	gst_object_unref (realpad);
+
+	/* 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), binsrcpad);
+	gst_object_unref (ghostpad);
+
+	link = gst_pad_link (realpad, binsinkpad);
+	gst_object_unref (realpad);
+	if (link != 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);
+
+	_rb_player_gst_filter_emit_filter_inserted (player, element);
+
+	return TRUE;
+}
+
+static gboolean
+impl_remove_filter (RBPlayerGstFilter *player, GstElement *element)
+{
+	RBPlayerGst *mp;
+	GstPad *mypad;
+	GstPad *prevpad, *nextpad;
+	GstPad *ghostpad;
+	GstPad *targetpad;
+	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;
+	}
+
+	_rb_player_gst_filter_emit_filter_pre_remove (player, element);
+
+	if (mp->priv->playing) {
+		GError *error = NULL;
+		if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+			g_warning ("Failed to pause pipeline before filter removal: %s", error->message);
+			g_error_free (error);
+			return FALSE;
+		}
+	}
+
+	/* get the containing bin and unlink it */
+	bin = GST_ELEMENT (gst_element_get_parent (element));
+
+	gst_element_set_state (bin, GST_STATE_NULL);
+
+	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");
+
+	targetpad = gst_ghost_pad_get_target (GST_GHOST_PAD (nextpad));
+	if (targetpad == 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_object_unref (ghostpad);
+	gst_object_unref (targetpad);
+
+	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 void
+rb_player_gst_filter_init (RBPlayerGstFilterIface *iface)
+{
+	iface->add_filter = impl_add_filter;
+	iface->remove_filter = impl_remove_filter;
+}
+
+static void
+rb_player_gst_tee_init (RBPlayerGstTeeIface *iface)
+{
+	iface->add_tee = impl_add_tee;
+	iface->remove_tee = impl_remove_tee;
+}
+
+
+
+RBPlayer *
+rb_player_gst_new (GError **error)
+{
+	return RB_PLAYER (g_object_new (RB_TYPE_PLAYER_GST, NULL, NULL));
+}
+
+
+static void
+rb_player_gst_init (RBPlayerGst *mp)
+{
+	mp->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((mp),
+		    RB_TYPE_PLAYER_GST,
+		    RBPlayerGstPrivate));
+}
+
+static void
+impl_get_property (GObject *object,
+		   guint prop_id,
+		   GValue *value,
+		   GParamSpec *pspec)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (object);
+
+	switch (prop_id) {
+	case PROP_PLAYBIN:
+		g_value_set_object (value, mp->priv->playbin);
+		break;
+	case PROP_BUFFER_SIZE:
+		g_value_set_uint (value, mp->priv->buffer_size);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+impl_set_property (GObject *object,
+		   guint prop_id,
+		   const GValue *value,
+		   GParamSpec *pspec)
+{
+	RBPlayerGst *mp = RB_PLAYER_GST (object);
+
+	switch (prop_id) {
+	case PROP_BUFFER_SIZE:
+		mp->priv->buffer_size = g_value_get_uint (value);
+		if (mp->priv->playbin != NULL) {
+			rb_debug ("setting buffer size on playbin: %d", mp->priv->buffer_size * 1024);
+			g_object_set (mp->priv->playbin, "buffer-size", mp->priv->buffer_size * 1024, NULL);
+		}
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+impl_dispose (GObject *object)
+{
+	RBPlayerGst *mp;
+
+	mp = RB_PLAYER_GST (object);
+
+	if (mp->priv->tick_timeout_id != 0) {
+		g_source_remove (mp->priv->tick_timeout_id);
+		mp->priv->tick_timeout_id = 0;
+	}
+
+	if (mp->priv->playbin != NULL) {
+		gst_element_set_state (mp->priv->playbin, GST_STATE_NULL);
+		g_object_unref (mp->priv->playbin);
+		mp->priv->playbin = NULL;
+		mp->priv->audio_sink = NULL;
+	}
+
+	if (mp->priv->waiting_tees != NULL) {
+		g_list_foreach (mp->priv->waiting_tees, (GFunc)gst_object_sink, NULL);
+		g_list_free (mp->priv->waiting_tees);
+		mp->priv->waiting_tees = NULL;
+	}
+
+	if (mp->priv->waiting_filters != NULL) {
+		g_list_foreach (mp->priv->waiting_filters, (GFunc)gst_object_sink, NULL);
+		g_list_free (mp->priv->waiting_filters);
+		mp->priv->waiting_filters = NULL;
+	}
+
+	G_OBJECT_CLASS (rb_player_gst_parent_class)->dispose (object);
+}
+
+static void
+rb_player_init (RBPlayerIface *iface)
+{
+	iface->open = impl_open;
+	iface->opened = impl_opened;
+	iface->close = impl_close;
+	iface->play = impl_play;
+	iface->pause = impl_pause;
+	iface->playing = impl_playing;
+	iface->set_volume = impl_set_volume;
+	iface->get_volume = impl_get_volume;
+	iface->set_replaygain = impl_set_replaygain;
+	iface->seekable = impl_seekable;
+	iface->set_time = impl_set_time;
+	iface->get_time = impl_get_time;
+	iface->multiple_open = (RBPlayerFeatureFunc) rb_false_function;
+}
+
+static void
+rb_player_gst_class_init (RBPlayerGstClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = impl_dispose;
+	object_class->get_property = impl_get_property;
+	object_class->set_property = impl_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_PLAYBIN,
+					 g_param_spec_object ("playbin",
+							      "playbin",
+							      "playbin element",
+							      GST_TYPE_ELEMENT,
+							      G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_BUFFER_SIZE,
+					 g_param_spec_uint ("buffer-size",
+							    "buffer size",
+							    "Buffer size for network streams, in kB",
+							    64, MAX_NETWORK_BUFFER_SIZE, 128,
+							    G_PARAM_READWRITE));
+
+	signals[MISSING_PLUGINS] =
+		g_signal_new ("missing-plugins",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,	/* no point handling this internally */
+			      NULL, NULL,
+			      rb_marshal_VOID__POINTER_POINTER_POINTER,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_POINTER, G_TYPE_STRV, G_TYPE_STRV);
+
+	g_type_class_add_private (klass, sizeof (RBPlayerGstPrivate));
+}
+
diff --git a/plugins/visualizer/Makefile.am b/plugins/visualizer/Makefile.am
index 33a1c90..b9298e2 100644
--- a/plugins/visualizer/Makefile.am
+++ b/plugins/visualizer/Makefile.am
@@ -6,8 +6,7 @@ plugin_LTLIBRARIES = libvisualizer.la
 libvisualizer_la_SOURCES = \
 	rb-visualizer-plugin.c				\
 	rb-vis-widget.c					\
-	rb-vis-widget.h					\
-	rb-fake-visualizer.c
+	rb-vis-widget.h
 
 libvisualizer_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
 
diff --git a/plugins/visualizer/rb-fake-visualizer.c b/plugins/visualizer/rb-fake-visualizer.c
deleted file mode 100644
index 2b81232..0000000
--- a/plugins/visualizer/rb-fake-visualizer.c
+++ /dev/null
@@ -1,609 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- *  Copyright (C) 2004 Benjamin Otte <otte@gnome.org>
- *  Copyright (C) 2006 Jonathan Matthew
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  The Rhythmbox authors hereby grant permission for non-GPL compatible
- *  GStreamer plugins to be used and distributed together with GStreamer
- *  and Rhythmbox. This permission is above and beyond the permissions granted
- *  by the GPL license by which Rhythmbox is covered. If you modify this code
- *  you may extend this exception to your version of the code, but you are not
- *  obligated to do so. If you do not wish to do so, delete this exception
- *  statement from your version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
- */
-
-/* fake visualizer element for evil rhythmbox purposes */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-
-#include <gst/gst.h>
-#include <gst/base/gstadapter.h>
-#include <gst/video/video.h>
-#include <gst/audio/audio.h>
-
-#define RB_TYPE_FAKE_VIS (rb_fake_vis_get_type())
-#define RB_IS_FAKE_VIS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_FAKE_VIS))
-#define RB_FAKE_VIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_FAKE_VIS,RBFakeVis))
-#define RB_IS_FAKE_VIS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_FAKE_VIS))
-#define RB_FAKE_VIS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_FAKE_VIS,RBFakeVisClass))
-#define RB_FAKE_VIS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), RB_TYPE_FAKE_VIS, RBFakeVisClass))
-
-typedef struct _RBFakeVis RBFakeVis;
-typedef struct _RBFakeVisClass RBFakeVisClass;
-
-GST_DEBUG_CATEGORY_STATIC (rb_fake_vis_debug);
-#define GST_CAT_DEFAULT (rb_fake_vis_debug)
-
-struct _RBFakeVis
-{
-  GstElement element;
-
-  /* pads */
-  GstPad *sinkpad;
-  GstPad *srcpad;
-  GstClockTime next_ts;
-  GstSegment segment;
-
-  /* audio/video state */
-  gint channels;
-  gint rate;                    /* Input samplerate */
-  gint bps;
-
-  /* framerate numerator & denominator */
-  gint fps_n;
-  gint fps_d;
-  gint width;
-  gint height;
-  gint depth;
-  GstClockTime duration;
-
-  /* samples per frame based on caps */
-  guint spf;
-
-  /* state stuff */
-  /*GstAdapter *adapter;*/
-  guint avail;
-  gboolean first_frame;
-
-  /* QoS stuff *//* with LOCK */
-  gdouble proportion;
-  GstClockTime earliest_time;
-};
-
-struct _RBFakeVisClass
-{
-  GstElementClass parent_class;
-};
-
-GType rb_fake_vis_get_type (void);
-
-
-static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
-    GST_PAD_SRC,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
-    );
-
-static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
-    GST_PAD_SINK,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("audio/x-raw-int, "
-        "width = (int) 16, "
-        "depth = (int) 16, "
-        "endianness = (int) BYTE_ORDER, "
-        "signed = (boolean) TRUE, "
-        "channels = (int) { 1, 2 }, " "rate = (int) [ 1000, MAX ]")
-    );
-
-static GstStateChangeReturn rb_fake_vis_change_state (GstElement * element,
-    GstStateChange transition);
-static GstFlowReturn rb_fake_vis_chain (GstPad * pad, GstBuffer * buffer);
-static gboolean rb_fake_vis_sink_event (GstPad * pad, GstEvent * event);
-static gboolean rb_fake_vis_src_event (GstPad * pad, GstEvent * event);
-
-static gboolean rb_fake_vis_sink_setcaps (GstPad * pad, GstCaps * caps);
-static gboolean rb_fake_vis_src_setcaps (GstPad * pad, GstCaps * caps);
-static GstCaps *rb_fake_vis_getcaps (GstPad * pad);
-
-static GstElementDetails rb_fake_vis_details =
-	GST_ELEMENT_DETAILS ("RB fake visualizer",
-			     "Visualization",
-			     "pretend to generate visualization from audio input",
-			     "Benjamin Otte <otte@gnome.org>, Jonathan Matthew <jonathan@kaolin.wh9.net>");
-
-static void
-_do_init (GType fake_vis_type)
-{
-  GST_DEBUG_CATEGORY_INIT (rb_fake_vis_debug,
-			   "fakevis", GST_DEBUG_FG_WHITE,
-			   "Rhythmbox built-in fake visualizer");
-}
-
-GST_BOILERPLATE_FULL (RBFakeVis, rb_fake_vis, GstElement, GST_TYPE_ELEMENT, _do_init);
-
-static void
-rb_fake_vis_base_init (gpointer g_class)
-{
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-  gst_element_class_set_details (element_class, &rb_fake_vis_details);
-  gst_element_class_add_pad_template (element_class,
-				      gst_static_pad_template_get (&src_template));
-  gst_element_class_add_pad_template (element_class,
-				      gst_static_pad_template_get (&sink_template));
-}
-
-static void
-rb_fake_vis_class_init (RBFakeVisClass *klass)
-{
-  GstElementClass *element = GST_ELEMENT_CLASS (klass);
-  element->change_state = rb_fake_vis_change_state;
-}
-
-static void
-rb_fake_vis_init (RBFakeVis * visual, RBFakeVisClass *klass)
-{
-  /* create the sink and src pads */
-  visual->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
-  gst_pad_set_setcaps_function (visual->sinkpad, rb_fake_vis_sink_setcaps);
-  gst_pad_set_chain_function (visual->sinkpad, rb_fake_vis_chain);
-  gst_pad_set_event_function (visual->sinkpad, rb_fake_vis_sink_event);
-  gst_element_add_pad (GST_ELEMENT (visual), visual->sinkpad);
-
-  visual->srcpad = gst_pad_new_from_static_template (&src_template, "src");
-  gst_pad_set_setcaps_function (visual->srcpad, rb_fake_vis_src_setcaps);
-  gst_pad_set_getcaps_function (visual->srcpad, rb_fake_vis_getcaps);
-  gst_pad_set_event_function (visual->srcpad, rb_fake_vis_src_event);
-  gst_element_add_pad (GST_ELEMENT (visual), visual->srcpad);
-}
-
-static void
-rb_fake_vis_reset (RBFakeVis * visual)
-{
-  visual->next_ts = -1;
-  visual->avail = 0;
-  gst_segment_init (&visual->segment, GST_FORMAT_UNDEFINED);
-
-  GST_OBJECT_LOCK (visual);
-  visual->proportion = 1.0;
-  visual->earliest_time = -1;
-  visual->first_frame = FALSE;
-  GST_OBJECT_UNLOCK (visual);
-}
-
-static gboolean
-rb_fake_vis_src_setcaps (GstPad * pad, GstCaps * caps)
-{
-  RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-  GstStructure *structure;
-
-  structure = gst_caps_get_structure (caps, 0);
-
-  GST_DEBUG_OBJECT (visual, "src pad got caps %" GST_PTR_FORMAT, caps);
-
-  if (!gst_structure_get_int (structure, "width", &visual->width))
-    goto error;
-  if (!gst_structure_get_int (structure, "height", &visual->height))
-    goto error;
-  if (!gst_structure_get_int (structure, "bpp", &visual->depth))
-    goto error;
-  if (!gst_structure_get_fraction (structure, "framerate", &visual->fps_n,
-          &visual->fps_d))
-    goto error;
-
-  visual->spf =
-      gst_util_uint64_scale_int (visual->rate, visual->fps_d, visual->fps_n);
-  visual->duration =
-      gst_util_uint64_scale_int (GST_SECOND, visual->fps_d, visual->fps_n);
-
-  gst_object_unref (visual);
-  return TRUE;
-
-  /* ERRORS */
-error:
-  {
-    GST_DEBUG_OBJECT (visual, "error parsing caps");
-    gst_object_unref (visual);
-    return FALSE;
-  }
-}
-
-
-static gboolean
-rb_fake_vis_sink_setcaps (GstPad * pad, GstCaps * caps)
-{
-  RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-  GstStructure *structure;
-
-  structure = gst_caps_get_structure (caps, 0);
-
-  gst_structure_get_int (structure, "channels", &visual->channels);
-  gst_structure_get_int (structure, "rate", &visual->rate);
-
-  if (visual->fps_n != 0) {
-    visual->spf =
-        gst_util_uint64_scale_int (visual->rate, visual->fps_d, visual->fps_n);
-  }
-  visual->bps = visual->channels * sizeof (gint16);
-
-  gst_object_unref (visual);
-  return TRUE;
-}
-
-
-static GstCaps *
-rb_fake_vis_getcaps (GstPad * pad)
-{
-  GstCaps *ret;
-  RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-
-  ret = gst_caps_copy (gst_pad_get_pad_template_caps (visual->srcpad));
-
-  GST_DEBUG_OBJECT (visual, "returning caps %" GST_PTR_FORMAT, ret);
-  gst_object_unref (visual);
-  return ret;
-}
-
-
-static gboolean
-rb_fake_vis_src_negotiate (RBFakeVis * visual)
-{
-  GstCaps *othercaps, *target, *intersect;
-  GstStructure *structure;
-  const GstCaps *templ;
-
-  templ = gst_pad_get_pad_template_caps (visual->srcpad);
-
-  /* see what the peer can do */
-  othercaps = gst_pad_peer_get_caps (visual->srcpad);
-  if (othercaps) {
-    intersect = gst_caps_intersect (othercaps, templ);
-    gst_caps_unref (othercaps);
-
-    if (gst_caps_is_empty (intersect))
-      goto no_format;
-
-    target = gst_caps_copy_nth (intersect, 0);
-    gst_caps_unref (intersect);
-  } else {
-    target = gst_caps_ref ((GstCaps *) templ);
-  }
-
-  structure = gst_caps_get_structure (target, 0);
-  gst_structure_fixate_field_nearest_int (structure, "width", 1);
-  gst_structure_fixate_field_nearest_int (structure, "height", 1);
-  gst_structure_fixate_field_nearest_fraction (structure, "framerate", 1, 1);
-
-  gst_pad_set_caps (visual->srcpad, target);
-  gst_caps_unref (target);
-
-  return TRUE;
-
-  /* ERRORS */
-no_format:
-  {
-    GST_ELEMENT_ERROR (visual, STREAM, FORMAT, (NULL),
-        ("could not negotiate output format"));
-    gst_caps_unref (intersect);
-    return FALSE;
-  }
-}
-
-static gboolean
-rb_fake_vis_sink_event (GstPad * pad, GstEvent * event)
-{
-  RBFakeVis *visual;
-  gboolean res;
-
-  visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_FLUSH_START:
-      res = gst_pad_push_event (visual->srcpad, event);
-      break;
-    case GST_EVENT_FLUSH_STOP:
-      rb_fake_vis_reset (visual);
-      res = gst_pad_push_event (visual->srcpad, event);
-      break;
-    case GST_EVENT_NEWSEGMENT:
-    {
-      GstFormat format;
-      gdouble rate, arate;
-      gint64 start, stop, time;
-      gboolean update;
-
-      /* the newsegment values are used to clip the input samples
-       * and to convert the incoming timestamps to running time so
-       * we can do QoS */
-      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
-          &start, &stop, &time);
-
-      /* now configure the values */
-      gst_segment_set_newsegment_full (&visual->segment, update,
-          rate, arate, format, start, stop, time);
-
-      /* and forward */
-      res = gst_pad_push_event (visual->srcpad, event);
-      break;
-    }
-    default:
-      res = gst_pad_push_event (visual->srcpad, event);
-      break;
-  }
-
-  gst_object_unref (visual);
-  return res;
-}
-
-static gboolean
-rb_fake_vis_src_event (GstPad * pad, GstEvent * event)
-{
-  RBFakeVis *visual;
-  gboolean res;
-
-  visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_QOS:
-    {
-      gdouble proportion;
-      GstClockTimeDiff diff;
-      GstClockTime timestamp;
-
-      gst_event_parse_qos (event, &proportion, &diff, &timestamp);
-
-      /* save stuff for the _chain function */
-      GST_OBJECT_LOCK (visual);
-      visual->proportion = proportion;
-      if (diff >= 0)
-        /* we're late, this is a good estimate for next displayable
-         * frame (see part-qos.txt) */
-        visual->earliest_time = timestamp + 2 * diff + visual->duration;
-      else
-        visual->earliest_time = timestamp + diff;
-
-      GST_OBJECT_UNLOCK (visual);
-
-      res = gst_pad_push_event (visual->sinkpad, event);
-      break;
-    }
-    default:
-      res = gst_pad_push_event (visual->sinkpad, event);
-      break;
-  }
-
-  gst_object_unref (visual);
-  return res;
-}
-
-/* allocate and output buffer, if no format was negotiated, this
- * function will negotiate one. After calling this function, a
- * reverse negotiation could have happened. */
-static GstFlowReturn
-get_buffer (RBFakeVis * visual, GstBuffer ** outbuf)
-{
-  GstFlowReturn ret;
-  guint outsize;
-
-  /* we don't know an output format yet, pick one */
-  if (GST_PAD_CAPS (visual->srcpad) == NULL) {
-    if (!rb_fake_vis_src_negotiate (visual))
-      return GST_FLOW_NOT_NEGOTIATED;
-  }
-
-  outsize = visual->height * visual->width * (visual->depth / 8);
-
-  GST_DEBUG_OBJECT (visual, "allocating output buffer with caps %"
-      GST_PTR_FORMAT, GST_PAD_CAPS (visual->srcpad));
-
-  /* now allocate a buffer with the last negotiated format.
-   * Downstream could renegotiate a new format, which will trigger
-   * our setcaps function on the source pad. */
-  ret =
-      gst_pad_alloc_buffer_and_set_caps (visual->srcpad,
-      GST_BUFFER_OFFSET_NONE, outsize,
-      GST_PAD_CAPS (visual->srcpad), outbuf);
-
-  /* no buffer allocated, we don't care why. */
-  if (ret != GST_FLOW_OK)
-    return ret;
-
-  /* this is bad and should not happen. When the alloc function
-   * returns _OK, core ensures we have a valid buffer. */
-  if (*outbuf == NULL)
-    return GST_FLOW_ERROR;
-
-  memset (GST_BUFFER_DATA (*outbuf), 0, outsize);
-
-  return GST_FLOW_OK;
-}
-
-static GstFlowReturn
-rb_fake_vis_chain (GstPad * pad, GstBuffer * buffer)
-{
-  GstBuffer *outbuf = NULL;
-  RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-  GstFlowReturn ret = GST_FLOW_OK;
-
-  GST_DEBUG_OBJECT (visual, "chain function called");
-
-  /* If we don't have an output format yet, preallocate a buffer to try and
-   * set one */
-  if (GST_PAD_CAPS (visual->srcpad) == NULL) {
-    GST_DEBUG_OBJECT (visual, "calling buffer alloc to set caps");
-    ret = get_buffer (visual, &outbuf);
-    if (ret != GST_FLOW_OK) {
-      GST_DEBUG_OBJECT (visual, "couldn't allocate buffer: %s", gst_flow_get_name (ret));
-      gst_buffer_unref (buffer);
-      goto beach;
-    }
-  }
-
-  /* resync on DISCONT */
-  if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
-    visual->avail = 0;
-    visual->next_ts = -1;
-  }
-
-  /* Try to push a frame as soon as possible to avoid stalling the pipeline */
-  if (visual->first_frame == FALSE) {
-    if (outbuf == NULL) {
-      ret = get_buffer (visual, &outbuf);
-      if (ret != GST_FLOW_OK) {
-	goto beach;
-      }
-    }
-    GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
-    ret = gst_pad_push (visual->srcpad, outbuf);
-    if (ret != GST_FLOW_OK) {
-      goto beach;
-    }
-    outbuf = NULL;
-    visual->first_frame = TRUE;
-  }
-
-  /* Match timestamps from the incoming audio */
-  if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE)
-    visual->next_ts = GST_BUFFER_TIMESTAMP (buffer);
-
-  GST_DEBUG_OBJECT (visual,
-      "Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
-      GST_BUFFER_SIZE (buffer) / visual->bps, GST_BUFFER_TIMESTAMP (buffer));
-
-  visual->avail += GST_BUFFER_SIZE (buffer);
-  gst_buffer_unref (buffer);
-
-  while (visual->avail > MAX (512, visual->spf) * visual->bps) {
-    gboolean need_skip;
-
-    GST_DEBUG_OBJECT (visual, "processing buffer (%u avail)", visual->avail);
-
-    if (visual->next_ts != -1) {
-      gint64 qostime;
-
-      /* QoS is done on running time */
-      qostime = gst_segment_to_running_time (&visual->segment, GST_FORMAT_TIME,
-          visual->next_ts);
-
-      GST_OBJECT_LOCK (visual);
-      /* check for QoS, don't compute buffers that are known to be late */
-      need_skip = visual->earliest_time != -1 &&
-          qostime <= visual->earliest_time;
-      GST_OBJECT_UNLOCK (visual);
-
-      if (need_skip) {
-        GST_WARNING_OBJECT (visual,
-            "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
-            GST_TIME_ARGS (qostime), GST_TIME_ARGS (visual->earliest_time));
-        goto skip;
-      }
-    }
-    /* alloc a buffer if we don't have one yet, this happens
-     * when we pushed a buffer in this while loop before */
-    if (outbuf == NULL) {
-      ret = get_buffer (visual, &outbuf);
-      if (ret != GST_FLOW_OK) {
-        goto beach;
-      }
-    }
-
-    GST_BUFFER_TIMESTAMP (outbuf) = visual->next_ts;
-    GST_BUFFER_DURATION (outbuf) = visual->duration;
-
-    ret = gst_pad_push (visual->srcpad, outbuf);
-    outbuf = NULL;
-
-    GST_DEBUG_OBJECT (visual, "finished frame, flushing %u samples from input",
-        visual->spf);
-  skip:
-    /* interpolate next timestamp */
-    if (visual->next_ts != -1)
-      visual->next_ts += visual->duration;
-
-    /* Flush out the number of samples per frame * channels * sizeof (gint16) */
-    if (visual->avail < visual->spf * visual->bps)
-      visual->avail = 0;
-    else
-      visual->avail -= visual->spf * visual->bps;
-
-    /* quit the loop if something was wrong */
-    if (ret != GST_FLOW_OK)
-      break;
-  }
-
-  if (outbuf != NULL)
-    gst_buffer_unref (outbuf);
-
-beach:
-  gst_object_unref (visual);
-
-  GST_DEBUG_OBJECT (visual, "leaving chain function");
-  return ret;
-}
-
-static GstStateChangeReturn
-rb_fake_vis_change_state (GstElement * element, GstStateChange transition)
-{
-  RBFakeVis *visual = RB_FAKE_VIS (element);
-  GstStateChangeReturn ret;
-
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      rb_fake_vis_reset (visual);
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-      break;
-    default:
-      break;
-  }
-
-  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
-  switch (transition) {
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
-      break;
-    default:
-      break;
-  }
-
-  return ret;
-}
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
-  return gst_element_register (plugin, "rbfakevis", GST_RANK_NONE, RB_TYPE_FAKE_VIS);
-}
-
-GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR, GST_VERSION_MINOR,
-			  "rbfakevis",
-			  "fake visualizer",
-			  plugin_init,
-			  VERSION,
-			  "GPL",
-			  PACKAGE,
-			  "");
-
diff --git a/plugins/visualizer/rb-visualizer-plugin.c b/plugins/visualizer/rb-visualizer-plugin.c
index 41f3601..f4e3926 100644
--- a/plugins/visualizer/rb-visualizer-plugin.c
+++ b/plugins/visualizer/rb-visualizer-plugin.c
@@ -82,9 +82,6 @@
 
 #include "rb-vis-widget.h"
 
-/* not going to create a new header just for this */
-extern GType rb_fake_vis_get_type (void);
-
 /* preferences */
 #define CONF_VIS_PREFIX  CONF_PREFIX "/plugins/visualizer"
 #define CONF_VIS_ELEMENT CONF_VIS_PREFIX "/element"
@@ -108,6 +105,8 @@ extern GType rb_fake_vis_get_type (void);
 
 #define VISUALIZER_DBUS_PATH	"/org/gnome/Rhythmbox/Visualizer"
 
+/* playbin2 flag(s) */
+#define PLAYBIN2_FLAG_VIS	0x08
 
 typedef struct {
 	char *name;
@@ -240,13 +239,6 @@ static const VisualizerQuality vis_quality[] = {
 	{ N_("Extra Large"),	800,	600,	30,	1 },
 };
 
-/* "quality" to use for fake visualization; should be small and
- * low framerate, but not so slow it makes a noticeable difference
- * to playback start time.  the video sink needs to preroll too,
- * so if the framerate is 1/10, that will take 10 seconds.
- */
-static const VisualizerQuality fake_vis_quality = { "", 60, 60, 1, 1 };
-
 static const VisualizerModeName vis_mode_name[] = {
 	{ N_("Embedded"),	EMBEDDED },
 	{ N_("Fullscreen"),	FULLSCREEN },
@@ -443,12 +435,7 @@ bus_sync_message_cb (GstBus *bus, GstMessage *msg, RBVisualizerPlugin *plugin)
 	case EXTERNAL_WINDOW:
 		if (plugin->vis_widget != NULL) {
 			g_object_get (plugin->vis_widget, "window-xid", &window, NULL);
-			if (window == 0) {
-				window = GDK_WINDOW_XWINDOW (plugin->fake_window);
-				rb_debug ("setting fake window id %lu", window);
-			} else {
-				rb_debug ("setting window id %lu in prepare-xwindow-id handler", window);
-			}
+			rb_debug ("setting window id %lu in prepare-xwindow-id handler", window);
 		}
 		break;
 	case DESKTOP_WINDOW:
@@ -457,7 +444,7 @@ bus_sync_message_cb (GstBus *bus, GstMessage *msg, RBVisualizerPlugin *plugin)
 		break;
 	}
 
-	if (plugin->xoverlay != NULL)
+	if (plugin->xoverlay != NULL && window != 0)
 		gst_x_overlay_set_xwindow_id (plugin->xoverlay, window);
 	plugin->window_id_set = TRUE;
 }
@@ -477,10 +464,7 @@ fixate_vis_caps (RBVisualizerPlugin *pi, GstElement *vis_element, GstElement *ca
 	if (quality < 0 || quality > G_N_ELEMENTS (vis_quality))
 		quality = DEFAULT_VIS_QUALITY;
 
-	if (pi->active)
-		q = &vis_quality[quality];
-	else
-		q = &fake_vis_quality;
+	q = &vis_quality[quality];
 
 	pad = gst_element_get_pad (vis_element, "src");
 	template_caps = gst_pad_get_pad_template_caps (pad);
@@ -554,12 +538,24 @@ update_playbin_visualizer (RBVisualizerPlugin *plugin,
 {
 	GstPad *pad;
 	GstElement *vis_plugin;
+	int playbin_flags;
 
 	if (plugin->playbin == NULL)
 		return;
 
-	if (plugin->visualizer)
+	if (plugin->visualizer) {
 		g_object_unref (plugin->visualizer);
+		plugin->visualizer = NULL;
+	}
+
+	g_object_get (plugin->playbin, "flags", &playbin_flags, NULL);
+
+	if (plugin->active == FALSE) {
+		playbin_flags &= ~PLAYBIN2_FLAG_VIS;
+		rb_debug ("disabling vis; new playbin2 flags %d", playbin_flags);
+		g_object_set (plugin->playbin, "flags", playbin_flags, NULL);
+		return;
+	}
 
 	plugin->visualizer = gst_bin_new (NULL);
 
@@ -572,13 +568,8 @@ update_playbin_visualizer (RBVisualizerPlugin *plugin,
 	gst_object_unref (pad);
 
 	/* set up visualizer */
-	if (plugin->active) {
-		vis_plugin = create_visualizer_element (vis_override);
-		gst_bin_add (GST_BIN (plugin->visualizer), vis_plugin);
-	} else {
-		vis_plugin = g_object_new (rb_fake_vis_get_type (), NULL);
-		gst_bin_add (GST_BIN (plugin->visualizer), vis_plugin);
-	}
+	vis_plugin = create_visualizer_element (vis_override);
+	gst_bin_add (GST_BIN (plugin->visualizer), vis_plugin);
 
 	pad = gst_element_get_pad (vis_plugin, "sink");
 	gst_element_add_pad (plugin->visualizer, gst_ghost_pad_new ("sink", pad));
@@ -589,7 +580,12 @@ update_playbin_visualizer (RBVisualizerPlugin *plugin,
 
 	g_object_ref (plugin->visualizer);
 
-	g_object_set (plugin->playbin, "vis-plugin", plugin->visualizer, NULL);
+	playbin_flags |= PLAYBIN2_FLAG_VIS;
+	rb_debug ("enabling vis; new playbin2 flags %d", playbin_flags);
+	g_object_set (plugin->playbin,
+		      "vis-plugin", plugin->visualizer,
+		      "flags", playbin_flags,
+		      NULL);
 }
 
 static void
@@ -1618,8 +1614,6 @@ impl_activate (RBPlugin *plugin,
 	GtkAction *action;
 	char *ui_file;
 
-	rb_fake_vis_get_type ();
-
 	pi->shell = shell;
 
 	/* find the player backend and connect to its pipeline mutation signal */
@@ -1913,10 +1907,6 @@ vis_plugin_filter (GstPluginFeature *feature, gpointer data)
 {
 	GstElementFactory *f;
 
-	/* skip our fake visualizer */
-	if (strcmp (gst_plugin_feature_get_name (feature), "rbfakevis") == 0)
-		return FALSE;
-
 	if  (!GST_IS_ELEMENT_FACTORY (feature))
 		return FALSE;
 	f = GST_ELEMENT_FACTORY (feature);
diff --git a/shell/rb-shell-preferences.c b/shell/rb-shell-preferences.c
index c2cea8c..86b707e 100644
--- a/shell/rb-shell-preferences.c
+++ b/shell/rb-shell-preferences.c
@@ -671,7 +671,6 @@ update_playback_prefs_sensitivity (RBShellPreferences *preferences)
 	backend = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preferences->priv->xfade_backend_check));
 
 	gtk_widget_set_sensitive (preferences->priv->album_crossfade_check, backend);
-	gtk_widget_set_sensitive (preferences->priv->network_buffer_size, backend);
 	gtk_widget_set_sensitive (preferences->priv->transition_duration, backend);
 }
 
