[banshee] Forward-ported Clutter support from stable-vis



commit aa650bafc132c870e7bb8a0f8c61bd473d1698d8
Author: Aaron Bockover <abockover novell com>
Date:   Fri May 15 18:06:13 2009 -0400

    Forward-ported Clutter support from stable-vis
    
    Merged the new Clutter video texture support from the stable-vis branch, and
    introduced a new ISupportClutter interface for enabling/disabling the clutter
    texture/sink on a PlayerEngine.
    
    This is only the backend/engine support, there is no UI connected to this,
    so you won't actually notice anything :)
---
 build/m4/banshee/libbanshee.m4                     |    4 +-
 libbanshee/Makefile.am                             |   24 +-
 libbanshee/banshee-player-private.h                |   21 +-
 libbanshee/banshee-player-video.c                  |   84 +--
 libbanshee/banshee-player.c                        |   13 +-
 libbanshee/clutter-gst-video-sink.c                |  978 +++++++++++++++-----
 libbanshee/shaders/I420.h                          |   36 +
 libbanshee/shaders/YV12.h                          |   36 +
 .../Banshee.GStreamer/PlayerEngine.cs              |   72 ++-
 .../Banshee.MediaEngine/ISupportClutter.cs         |   39 +
 .../Banshee.MediaEngine/VideoDisplayContextType.cs |    2 +-
 src/Core/Banshee.Services/Banshee.Services.csproj  |    3 +-
 src/Core/Banshee.Services/Makefile.am              |    1 +
 .../Banshee.NowPlaying.Clutter/Makefile.am         |   12 +-
 14 files changed, 998 insertions(+), 327 deletions(-)

diff --git a/build/m4/banshee/libbanshee.m4 b/build/m4/banshee/libbanshee.m4
index a645e93..403539f 100644
--- a/build/m4/banshee/libbanshee.m4
+++ b/build/m4/banshee/libbanshee.m4
@@ -26,12 +26,12 @@ AC_DEFUN([BANSHEE_CHECK_LIBBANSHEE],
 	fi
 
 	PKG_CHECK_MODULES(CLUTTER,
-		clutter-0.8 >= 0.8.6,
+		clutter-0.9 >= 0.9.0,
 		enable_clutter=yes, enable_clutter=no)
 
 	if test "x$enable_clutter" = "xyes"; then
 		SHAMROCK_CONCAT_MODULE(LIBBANSHEE, CLUTTER)
-		AC_DEFINE(HAVE_CLUTTER, 1, 
+		AC_DEFINE(HAVE_CLUTTER, 1,
 			[Define if the video sink should be Clutter])
 	fi
 
diff --git a/libbanshee/Makefile.am b/libbanshee/Makefile.am
index 30d4638..a35eeb3 100644
--- a/libbanshee/Makefile.am
+++ b/libbanshee/Makefile.am
@@ -27,8 +27,27 @@ libbanshee_la_SOURCES =  \
 
 if HAVE_CLUTTER
 libbanshee_la_SOURCES += clutter-gst-video-sink.c
+INCLUDES += -I$(srcdir)/shaders
+else
+noinst_DATA = clutter-gst-video-sink.c
 endif
 
+noinst_HEADERS =  \
+	banshee-gst.h \
+	banshee-player-cdda.h \
+	banshee-player-equalizer.h \
+	banshee-player-missing-elements.h \
+	banshee-player-pipeline.h \
+	banshee-player-private.h \
+	banshee-player-replaygain.h \
+	banshee-player-video.h \
+	banshee-player-vis.h \
+	banshee-tagger.h \
+	clutter-gst-shaders.h \
+	clutter-gst-video-sink.h \
+	shaders/I420.h \
+	shaders/YV12.h
+
 libbanshee_la_LIBADD = \
 	$(LIBBANSHEE_LIBS) \
 	$(GST_LIBS)
@@ -41,7 +60,4 @@ $(top_builddir)/bin/libbanshee.so: libbanshee.la
 
 CLEANFILES = $(top_builddir)/bin/libbanshee.so
 MAINTAINERCLEANFILES = Makefile.in
-EXTRA_DIST = \
-	$(libbanshee_la_SOURCES) \
-	$(wildcard *.h) \
-	$(wildcard clutter-*.c)
+EXTRA_DIST = $(libbanshee_la_SOURCES)
diff --git a/libbanshee/banshee-player-private.h b/libbanshee/banshee-player-private.h
index b638412..513bdf4 100644
--- a/libbanshee/banshee-player-private.h
+++ b/libbanshee/banshee-player-private.h
@@ -48,10 +48,6 @@
 #  include <gst/interfaces/xoverlay.h>
 #endif
 
-#ifdef HAVE_CLUTTER
-#  include <clutter/clutter.h>
-#endif
-
 #include "banshee-gst.h"
 
 #define P_INVOKE
@@ -71,6 +67,13 @@ typedef void (* BansheePlayerIterateCallback)      (BansheePlayer *player);
 typedef void (* BansheePlayerBufferingCallback)    (BansheePlayer *player, gint buffering_progress);
 typedef void (* BansheePlayerTagFoundCallback)     (BansheePlayer *player, const gchar *tag, const GValue *value);
 typedef void (* BansheePlayerVisDataCallback)      (BansheePlayer *player, gint channels, gint samples, gfloat *data, gint bands, gfloat *spectrum);
+typedef GstElement * (* BansheePlayerVideoPipelineSetupCallback) (BansheePlayer *player, GstBus *bus);
+
+typedef enum {
+    BP_VIDEO_DISPLAY_CONTEXT_UNSUPPORTED = 0,
+    BP_VIDEO_DISPLAY_CONTEXT_GDK_WINDOW = 1,
+    BP_VIDEO_DISPLAY_CONTEXT_CUSTOM = 2
+} BpVideoDisplayContextType;
 
 struct BansheePlayer {
     // Player Callbacks
@@ -81,6 +84,7 @@ struct BansheePlayer {
     BansheePlayerBufferingCallback buffering_cb;
     BansheePlayerTagFoundCallback tag_found_cb;
     BansheePlayerVisDataCallback vis_data_cb;
+    BansheePlayerVideoPipelineSetupCallback video_pipeline_setup_cb;
 
     // Pipeline Elements
     GstElement *playbin;
@@ -99,17 +103,12 @@ struct BansheePlayer {
     gchar *cdda_device;
     
     // Video State
+    BpVideoDisplayContextType video_display_context_type;
     #ifdef GDK_WINDOWING_X11
     GstXOverlay *xoverlay;
     GdkWindow *video_window;
     #endif
-    
-    // Clutter State
-    #ifdef HAVE_CLUTTER
-    GstElement *clutter_sink;
-    ClutterTexture *clutter_texture;
-    #endif
-    
+       
     // Visualization State
     GstElement *vis_resampler;
     GstAdapter *vis_buffer;
diff --git a/libbanshee/banshee-player-video.c b/libbanshee/banshee-player-video.c
index 1792566..a9ee576 100644
--- a/libbanshee/banshee-player-video.c
+++ b/libbanshee/banshee-player-video.c
@@ -32,38 +32,6 @@
 // Private Functions
 // ---------------------------------------------------------------------------
 
-typedef enum {
-    BP_VIDEO_DISPLAY_CONTEXT_UNSUPPORTED = 0,
-    BP_VIDEO_DISPLAY_CONTEXT_GDK_WINDOW = 1,
-    BP_VIDEO_DISPLAY_CONTEXT_CLUTTER_TEXTURE = 2
-} BpVideoDisplayContextType;
-
-#ifdef HAVE_CLUTTER
-
-#include <clutter/clutter.h>
-#include "clutter-gst-video-sink.h"
-
-
-static gboolean
-bp_video_pipeline_setup_clutter (BansheePlayer *player, GstBus *bus)
-{
-    if (player->clutter_texture == NULL) {
-        player->clutter_texture = g_object_new (CLUTTER_TYPE_TEXTURE,
-            "sync-size", FALSE,
-            "disable-slicing", TRUE,
-            NULL);
-    }
-    
-    bp_debug ("Created ClutterTexture: %p", player->clutter_texture);
-    
-    player->clutter_sink = clutter_gst_video_sink_new (CLUTTER_TEXTURE (player->clutter_texture));
-    g_object_set (G_OBJECT (player->playbin), "video-sink", player->clutter_sink, NULL);
-
-    return TRUE;
-}
-
-#endif /* HAVE_CLUTTER */
-
 #ifdef GDK_WINDOWING_X11
 
 static gboolean
@@ -160,18 +128,24 @@ _bp_video_pipeline_setup (BansheePlayer *player, GstBus *bus)
     
     g_return_if_fail (IS_BANSHEE_PLAYER (player));
     
-    #if HAVE_CLUTTER
-    if (bp_video_pipeline_setup_clutter (player, bus)) {
-        return;
+     if (player->video_pipeline_setup_cb != NULL) {
+        videosink = player->video_pipeline_setup_cb (player, bus);
+        if (videosink != NULL && GST_IS_ELEMENT (videosink)) {
+            g_object_set (G_OBJECT (player->playbin), "video-sink", videosink, NULL);
+            player->video_display_context_type = BP_VIDEO_DISPLAY_CONTEXT_CUSTOM;
+            return;
+        }
     }
-    #endif
     
     #ifdef GDK_WINDOWING_X11
+
+    player->video_display_context_type = BP_VIDEO_DISPLAY_CONTEXT_GDK_WINDOW;
     
     videosink = gst_element_factory_make ("gconfvideosink", "videosink");
     if (videosink == NULL) {
         videosink = gst_element_factory_make ("ximagesink", "videosink");
         if (videosink == NULL) {
+            player->video_display_context_type = BP_VIDEO_DISPLAY_CONTEXT_UNSUPPORTED;
             videosink = gst_element_factory_make ("fakesink", "videosink");
             if (videosink != NULL) {
                 g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL);
@@ -190,6 +164,8 @@ _bp_video_pipeline_setup (BansheePlayer *player, GstBus *bus)
     
     #else
     
+    player->video_display_context_type = BP_VIDEO_DISPLAY_CONTEXT_UNSUPPORTED;
+
     videosink = gst_element_factory_make ("fakesink", "videosink");
     if (videosink != NULL) {
         g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL);
@@ -200,6 +176,12 @@ _bp_video_pipeline_setup (BansheePlayer *player, GstBus *bus)
     #endif
 }
 
+P_INVOKE void
+bp_set_video_pipeline_setup_callback (BansheePlayer *player, BansheePlayerVideoPipelineSetupCallback cb)
+{
+    SET_CALLBACK (video_pipeline_setup_cb);
+}
+
 // ---------------------------------------------------------------------------
 // Public Functions
 // ---------------------------------------------------------------------------
@@ -209,13 +191,7 @@ _bp_video_pipeline_setup (BansheePlayer *player, GstBus *bus)
 P_INVOKE BpVideoDisplayContextType
 bp_video_get_display_context_type (BansheePlayer *player)
 {
-    #ifdef HAVE_CLUTTER
-    return player->clutter_sink == NULL
-        ? BP_VIDEO_DISPLAY_CONTEXT_GDK_WINDOW
-        : BP_VIDEO_DISPLAY_CONTEXT_CLUTTER_TEXTURE;
-    #else
-    return BP_VIDEO_DISPLAY_CONTEXT_GDK_WINDOW; // bp_video_find_xoverlay (player);
-    #endif
+    return player->video_display_context_type;
 }
 
 P_INVOKE void
@@ -232,13 +208,7 @@ P_INVOKE gpointer
 bp_video_get_display_context (BansheePlayer *player)
 {
     g_return_val_if_fail (IS_BANSHEE_PLAYER (player), NULL);
-    
-    #ifdef HAVE_CLUTTER
-    if (bp_video_get_display_context_type (player) == BP_VIDEO_DISPLAY_CONTEXT_CLUTTER_TEXTURE) {
-        return player->clutter_texture;
-    }
-    #endif
-    
+   
     if (bp_video_get_display_context_type (player) == BP_VIDEO_DISPLAY_CONTEXT_GDK_WINDOW) {
         return player->video_window;
     }
@@ -281,13 +251,7 @@ bp_video_window_expose (BansheePlayer *player, GdkWindow *window, gboolean direc
 P_INVOKE BpVideoDisplayContextType
 bp_video_get_display_context_type (BansheePlayer *player)
 {
-    #ifdef HAVE_CLUTTER
-    return player->clutter_sink == NULL
-        ? BP_VIDEO_DISPLAY_CONTEXT_UNSUPPORTED
-        : BP_VIDEO_DISPLAY_CONTEXT_CLUTTER_TEXTURE;
-    #else
-    return BP_VIDEO_DISPLAY_CONTEXT_UNSUPPORTED;
-    #endif
+    return player->video_display_contex_type;
 }
 
 P_INVOKE void
@@ -298,12 +262,6 @@ bp_video_set_display_context (BansheePlayer *player, gpointer context)
 P_INVOKE gpointer
 bp_video_get_display_context (BansheePlayer *player)
 {
-    #ifdef HAVE_CLUTTER
-    if (bp_video_get_display_context_type (player) == BP_VIDEO_DISPLAY_CONTEXT_CLUTTER_TEXTURE) {
-        return player->clutter_texture;
-    }
-    #endif
-    
     return NULL;
 }
 
diff --git a/libbanshee/banshee-player.c b/libbanshee/banshee-player.c
index 678ed8d..4052d06 100644
--- a/libbanshee/banshee-player.c
+++ b/libbanshee/banshee-player.c
@@ -124,17 +124,18 @@ bp_new ()
     
     player->mutex = g_mutex_new ();
     
-    _bp_replaygain_init (player);
-    
-    if (!_bp_pipeline_construct (player)) {
-        bp_destroy (player);
-        return NULL;
-    }
+    _bp_replaygain_init (player); 
     
     return player;
 }
 
 P_INVOKE gboolean
+bp_initialize_pipeline (BansheePlayer *player)
+{
+    return _bp_pipeline_construct (player);
+}
+
+P_INVOKE gboolean
 bp_open (BansheePlayer *player, const gchar *uri)
 {
     GstState state;
diff --git a/libbanshee/clutter-gst-video-sink.c b/libbanshee/clutter-gst-video-sink.c
index 15993dd..f4e19b1 100644
--- a/libbanshee/clutter-gst-video-sink.c
+++ b/libbanshee/clutter-gst-video-sink.c
@@ -35,10 +35,15 @@
  * data to a #ClutterTexture.
  */
 
+#ifdef HAVE_CONFIG_H
 #include "config.h"
+#endif
 
 #include "clutter-gst-video-sink.h"
 #include "clutter-gst-shaders.h"
+/* include assembly shaders */
+#include "I420.h"
+#include "YV12.h"
 
 #include <gst/gst.h>
 #include <gst/gstvalue.h>
@@ -46,7 +51,6 @@
 #include <gst/riff/riff-ids.h>
 
 #include <glib.h>
-#include <clutter/clutter.h>
 #include <string.h>
 
 static gchar *ayuv_to_rgba_shader = \
@@ -55,7 +59,6 @@ static gchar *ayuv_to_rgba_shader = \
      "void main () {"
      "  vec4 color = texture2D (tex, vec2(" TEX_COORD "));"
      "  float y = 1.1640625 * (color.g - 0.0625);"
-     "  float u = color.b - 0.5;"
      "  float v = color.a - 0.5;"
      "  color.a = color.r;"
      "  color.r = y + 1.59765625 * v;"
@@ -89,47 +92,16 @@ static gchar *yv12_to_rgba_shader = \
      FRAGMENT_SHADER_END
      "}";
 
-static GstStaticPadTemplate sinktemplate 
- = GST_STATIC_PAD_TEMPLATE ("sink",
-                            GST_PAD_SINK,
-                            GST_PAD_ALWAYS,
-                            GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBx ";"   \
-                                             GST_VIDEO_CAPS_BGRx "; " \
-                                             GST_VIDEO_CAPS_RGB ";"   \
-                                             GST_VIDEO_CAPS_BGR \
-                                             ));
-
-/* Multi-texturing will only be available on GL, so decide on capabilties
- * accordingly.
- */
-
-#ifdef CLUTTER_COGL_HAS_GL
-#define YUV_CAPS GST_VIDEO_CAPS_YUV("AYUV") ";" GST_VIDEO_CAPS_YUV("YV12") ";"
-#else
-#define YUV_CAPS GST_VIDEO_CAPS_YUV("AYUV") ";"
-#endif
-
-/* Don't advertise RGB/BGR as it seems to override yv12, even when it's the
- * better choice. Unfortunately, RGBx/BGRx also override AYUV when it's the
- * better choice too, but that's not quite as bad.
- */
-
-static GstStaticPadTemplate sinktemplate_shaders 
- = GST_STATIC_PAD_TEMPLATE ("sink",
-                            GST_PAD_SINK,
-                            GST_PAD_ALWAYS,
-                            GST_STATIC_CAPS (YUV_CAPS \
-                                             GST_VIDEO_CAPS_RGBx ";" \
-                                             GST_VIDEO_CAPS_BGRx));
-
 static GstStaticPadTemplate sinktemplate_all 
  = GST_STATIC_PAD_TEMPLATE ("sink",
                             GST_PAD_SINK,
                             GST_PAD_ALWAYS,
-                            GST_STATIC_CAPS (YUV_CAPS \
-                                             GST_VIDEO_CAPS_RGBx ";" \
-                                             GST_VIDEO_CAPS_BGRx ";" \
-                                             GST_VIDEO_CAPS_RGB ";" \
+                            GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV("AYUV") ";" \
+                                             GST_VIDEO_CAPS_YUV("YV12") ";" \
+                                             GST_VIDEO_CAPS_YUV("I420") ";" \
+                                             GST_VIDEO_CAPS_RGBA        ";" \
+                                             GST_VIDEO_CAPS_BGRA        ";" \
+                                             GST_VIDEO_CAPS_RGB         ";" \
                                              GST_VIDEO_CAPS_BGR));
 
 GST_DEBUG_CATEGORY_STATIC (clutter_gst_video_sink_debug);
@@ -157,9 +129,60 @@ typedef enum
   CLUTTER_GST_RGB24,
   CLUTTER_GST_AYUV,
   CLUTTER_GST_YV12,
+  CLUTTER_GST_I420,
 } ClutterGstVideoFormat;
 
 typedef void (*GLUNIFORM1IPROC)(COGLint location, COGLint value);
+/* GL_ARB_fragment_program */
+typedef void (*GLGENPROGRAMSPROC)(GLsizei n, COGLuint *programs);
+typedef void (*GLBINDPROGRAMPROC)(GLenum target, COGLint program);
+typedef void (*GLPROGRAMSTRINGPROC)(GLenum target, GLenum format, GLsizei len,
+                                    const void *string);
+/* multi-texturing */
+typedef void (*GLACTIVETEXTUREPROC)(GLenum unit);
+typedef void (*GLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t);
+
+typedef struct _ClutterGstSymbols
+{
+  /* GL_ARB_fragment_program */
+  GLGENPROGRAMSPROC   glGenProgramsARB;
+  GLBINDPROGRAMPROC   glBindProgramARB;
+  GLPROGRAMSTRINGPROC glProgramStringARB;
+
+  /* multi-texturing */
+  GLACTIVETEXTUREPROC glActiveTextureARB;
+  GLMULTITEXCOORD2FPROC glMultiTexCoord2fARB;
+} ClutterGstSymbols;
+
+/*
+ * features: what does the underlaying video card supports ?
+ */
+typedef enum _ClutterGstFeatures
+{
+  CLUTTER_GST_FP             = 0x1, /* fragment programs (ARB fp1.0) */
+  CLUTTER_GST_GLSL           = 0x2, /* GLSL */
+  CLUTTER_GST_MULTI_TEXTURE  = 0x4, /* multi-texturing */
+} ClutterGstFeatures;
+
+/*
+ * renderer: abstracts a backend to render a frame.
+ */
+typedef struct _ClutterGstRenderer
+{
+ const char            *name;     /* user friendly name */
+ ClutterGstVideoFormat  format;   /* the format handled by this renderer */
+ int                    flags;    /* ClutterGstFeatures ORed flags */
+ GstStaticCaps          caps;     /* caps handled by the renderer */
+
+ void (*init)       (ClutterActor        *actor,
+                     ClutterGstVideoSink *sink);
+ void (*upload)     (ClutterGstVideoSink *sink,
+                     GstBuffer           *buffer);
+ void (*paint)      (ClutterActor        *actor,
+                     ClutterGstVideoSink *sink);
+ void (*post_paint) (ClutterActor        *actor,
+                     ClutterGstVideoSink *sink);
+} ClutterGstRenderer;
 
 struct _ClutterGstVideoSinkPrivate
 {
@@ -168,7 +191,12 @@ struct _ClutterGstVideoSinkPrivate
   CoglHandle             v_tex;
   CoglHandle             program;
   CoglHandle             shader;
-  GAsyncQueue           *async_queue;
+  COGLuint               fp;
+
+  GMutex                *buffer_lock;   /* mutex for the buffer and idle_id */
+  GstBuffer             *buffer;
+  guint                  idle_id;
+
   ClutterGstVideoFormat  format;
   gboolean               bgr;
   int                    width;
@@ -178,7 +206,12 @@ struct _ClutterGstVideoSinkPrivate
   gboolean               use_shaders;
   gboolean               shaders_init;
   
+  ClutterGstSymbols      syms;          /* extra OpenGL functions */
   GLUNIFORM1IPROC        glUniform1iARB;
+
+  GSList                *renderers;
+  GstCaps               *caps;
+  ClutterGstRenderer    *renderer;
 };
 
 
@@ -194,87 +227,70 @@ GST_BOILERPLATE_FULL (ClutterGstVideoSink,
                       GST_TYPE_BASE_SINK,
                       _do_init);
 
+/*
+ * Small helpers
+ */
+
 static void
-clutter_gst_video_sink_base_init (gpointer g_class)
+_string_array_to_char_array (char	*dst,
+                             const char *src[])
 {
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+  int i, n;
 
-  gst_element_class_add_pad_template 
-                     (element_class,
-                      gst_static_pad_template_get (&sinktemplate_all));
-
-  gst_element_class_set_details (element_class, 
-                                 &clutter_gst_video_sink_details);
+  for (i = 0; src[i]; i++) {
+      n = strlen (src[i]);
+      memcpy (dst, src[i], n);
+      dst += n;
+  }
+  *dst = '\0';
 }
 
 static void
-clutter_gst_video_sink_init (ClutterGstVideoSink      *sink,
-                             ClutterGstVideoSinkClass *klass)
+clutter_gst_video_sink_fp_paint (ClutterActor        *actor,
+                                ClutterGstVideoSink *sink)
 {
-  ClutterGstVideoSinkPrivate *priv;
-
-  sink->priv = priv =
-    G_TYPE_INSTANCE_GET_PRIVATE (sink, CLUTTER_GST_TYPE_VIDEO_SINK,
-                                 ClutterGstVideoSinkPrivate);
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
 
-  priv->async_queue = g_async_queue_new ();
+  glEnable (GL_FRAGMENT_PROGRAM_ARB);
+  priv->syms.glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, priv->fp);
 
-#ifdef CLUTTER_COGL_HAS_GL
-  priv->glUniform1iARB = (GLUNIFORM1IPROC)
-    cogl_get_proc_address ("glUniform1iARB");
-#endif
 }
 
 static void
-clutter_gst_video_sink_paint (ClutterActor        *actor,
-                              ClutterGstVideoSink *sink)
+clutter_gst_video_sink_set_fp_shader (ClutterGstVideoSink *sink,
+                                      const gchar         *shader_src,
+                                      const int            size)
 {
   ClutterGstVideoSinkPrivate *priv = sink->priv;
-  if (priv->program)
-    cogl_program_use (priv->program);
-}
 
-static void
-clutter_gst_yv12_paint (ClutterActor        *actor,
-                        ClutterGstVideoSink *sink)
-{
-#ifdef CLUTTER_COGL_HAS_GL
-  ClutterGstVideoSinkPrivate *priv = sink->priv;
-  GLuint texture;
-  
-  /* Bind the U and V textures in texture units 1 and 2 */
-  if (priv->u_tex)
-    {
-      cogl_texture_get_gl_texture (priv->u_tex, &texture, NULL);
-      glActiveTexture (GL_TEXTURE1);
-      glEnable (GL_TEXTURE_2D);
-      glBindTexture (GL_TEXTURE_2D, texture);
-    }
+  priv->shaders_init = FALSE;
+
+  glEnable (GL_FRAGMENT_PROGRAM_ARB);
+  priv->syms.glGenProgramsARB (1, &priv->fp);
+  priv->syms.glBindProgramARB (GL_FRAGMENT_PROGRAM_ARB, priv->fp);
+  priv->syms.glProgramStringARB (GL_FRAGMENT_PROGRAM_ARB,
+                                  GL_PROGRAM_FORMAT_ASCII_ARB,
+                                  size,
+                                  (const GLbyte *)shader_src);
+
+  glDisable(GL_FRAGMENT_PROGRAM_ARB);
+
+  /* Hook onto the pre-paint signal to bind the shader. */
+  g_signal_connect (priv->texture,
+                    "paint",
+                    G_CALLBACK (clutter_gst_video_sink_fp_paint),
+                    sink);
+  priv->shaders_init = TRUE;
 
-  if (priv->v_tex)
-    {
-      cogl_texture_get_gl_texture (priv->v_tex, &texture, NULL);
-      glActiveTexture (GL_TEXTURE2);
-      glEnable (GL_TEXTURE_2D);
-      glBindTexture (GL_TEXTURE_2D, texture);
-    }
-  
-  glActiveTexture (GL_TEXTURE0_ARB);
-#endif
 }
 
 static void
-clutter_gst_yv12_post_paint (ClutterActor        *actor,
-                             ClutterGstVideoSink *sink)
+clutter_gst_video_sink_paint (ClutterActor        *actor,
+                              ClutterGstVideoSink *sink)
 {
-#ifdef CLUTTER_COGL_HAS_GL
-  /* Disable the extra texture units */
-  glActiveTexture (GL_TEXTURE1);
-  glDisable (GL_TEXTURE_2D);
-  glActiveTexture (GL_TEXTURE2);
-  glDisable (GL_TEXTURE_2D);
-  glActiveTexture (GL_TEXTURE0);
-#endif
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  if (priv->program)
+    cogl_program_use (priv->program);
 }
 
 static void
@@ -332,6 +348,508 @@ clutter_gst_video_sink_set_shader (ClutterGstVideoSink *sink,
     }
 }
 
+/* some renderers don't need all the ClutterGstRenderer vtable */
+static void
+clutter_gst_dummy_init (ClutterActor        *actor,
+                        ClutterGstVideoSink *sink)
+{
+}
+
+/*
+ * RGB 24 / BGR 24
+ *
+ * 3 bytes per pixel, stride % 4 = 0.
+ */
+
+static void
+clutter_gst_rgb24_upload (ClutterGstVideoSink *sink,
+                          GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+
+  clutter_texture_set_from_rgb_data (priv->texture,
+                                     GST_BUFFER_DATA (buffer),
+                                     FALSE,
+                                     priv->width,
+                                     priv->height,
+                                     GST_ROUND_UP_4 (3 * priv->width),
+                                     3,
+                                     priv->bgr ?
+                                     CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
+                                     NULL);
+}
+
+static ClutterGstRenderer rgb24_renderer =
+{
+  "RGB 24",
+  CLUTTER_GST_RGB24,
+  0,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR),
+  clutter_gst_dummy_init,
+  clutter_gst_rgb24_upload,
+  NULL,
+  NULL,
+};
+
+/*
+ * RGBA / BGRA 8888
+ */
+
+static void
+clutter_gst_rgb32_upload (ClutterGstVideoSink *sink,
+                          GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+
+  clutter_texture_set_from_rgb_data (priv->texture,
+                                     GST_BUFFER_DATA (buffer),
+                                     TRUE,
+                                     priv->width,
+                                     priv->height,
+                                     GST_ROUND_UP_4 (4 * priv->width),
+                                     4,
+                                     priv->bgr ?
+                                     CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
+                                     NULL);
+}
+
+static ClutterGstRenderer rgb32_renderer =
+{
+  "RGB 32",
+  CLUTTER_GST_RGB32,
+  0,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA),
+  clutter_gst_dummy_init,
+  clutter_gst_rgb32_upload,
+  NULL,
+  NULL,
+};
+
+#ifdef CLUTTER_COGL_HAS_GL
+
+/*
+ * YV12
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled V and U planes.
+ */
+
+static void
+clutter_gst_yv12_glsl_init (ClutterActor        *actor,
+                            ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+  COGLint location;
+
+  clutter_gst_video_sink_set_shader (sink,
+                                     yv12_to_rgba_shader);
+
+  cogl_program_use (priv->program);
+  location = cogl_program_get_uniform_location (priv->program, "ytex");
+  priv->glUniform1iARB (location, 0);
+  location = cogl_program_get_uniform_location (priv->program, "utex");
+  priv->glUniform1iARB (location, 1);
+  location = cogl_program_get_uniform_location (priv->program, "vtex");
+  priv->glUniform1iARB (location, 2);
+
+  cogl_program_use (COGL_INVALID_HANDLE);
+}
+
+static void
+clutter_gst_yv12_upload (ClutterGstVideoSink *sink,
+                         GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+
+  CoglHandle y_tex = cogl_texture_new_from_data (priv->width,
+                                                 priv->height,
+                                                 -1,
+                                                 COGL_TEXTURE_NONE,
+                                                 COGL_PIXEL_FORMAT_G_8,
+                                                 COGL_PIXEL_FORMAT_G_8,
+                                                 priv->width,
+                                                 GST_BUFFER_DATA (buffer));
+  cogl_texture_set_filters (y_tex, CGL_LINEAR, CGL_LINEAR);
+  clutter_texture_set_cogl_texture (priv->texture, y_tex);
+  cogl_texture_unref (y_tex);
+
+  if (priv->u_tex)
+    cogl_texture_unref (priv->u_tex);
+
+  if (priv->v_tex)
+    cogl_texture_unref (priv->v_tex);
+
+  priv->v_tex = cogl_texture_new_from_data (priv->width/2,
+                                            priv->height/2,
+                                            -1,
+                                            COGL_TEXTURE_NONE,
+                                            COGL_PIXEL_FORMAT_G_8,
+                                            COGL_PIXEL_FORMAT_G_8,
+                                            priv->width/2,
+                                            GST_BUFFER_DATA (buffer) +
+                                            (priv->width * priv->height));
+  cogl_texture_set_filters (priv->v_tex, CGL_LINEAR, CGL_LINEAR);
+
+  priv->u_tex =
+    cogl_texture_new_from_data (priv->width/2,
+                                priv->height/2,
+                                -1,
+                                COGL_TEXTURE_NONE,
+                                COGL_PIXEL_FORMAT_G_8,
+                                COGL_PIXEL_FORMAT_G_8,
+                                priv->width/2,
+                                GST_BUFFER_DATA (buffer) +
+                                (priv->width * priv->height) +
+                                (priv->width/2 * priv->height/2));
+  cogl_texture_set_filters (priv->u_tex, CGL_LINEAR, CGL_LINEAR);
+}
+
+static void
+clutter_gst_yv12_paint (ClutterActor        *actor,
+                        ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  GLuint texture;
+
+  /* Bind the U and V textures in texture units 1 and 2 */
+  if (priv->u_tex)
+    {
+      cogl_texture_get_gl_texture (priv->u_tex, &texture, NULL);
+      priv->syms.glActiveTextureARB (GL_TEXTURE1);
+      glEnable (GL_TEXTURE_2D);
+      glBindTexture (GL_TEXTURE_2D, texture);
+    }
+
+  if (priv->v_tex)
+    {
+      cogl_texture_get_gl_texture (priv->v_tex, &texture, NULL);
+      priv->syms.glActiveTextureARB (GL_TEXTURE2);
+      glEnable (GL_TEXTURE_2D);
+      glBindTexture (GL_TEXTURE_2D, texture);
+    }
+
+  priv->syms.glActiveTextureARB (GL_TEXTURE0_ARB);
+}
+
+static void
+clutter_gst_yv12_glsl_post_paint (ClutterActor        *actor,
+                                  ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+
+  /* Disable the extra texture units */
+  priv->syms.glActiveTextureARB (GL_TEXTURE1);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE2);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE0);
+}
+
+static ClutterGstRenderer yv12_glsl_renderer =
+{
+  "YV12 glsl",
+  CLUTTER_GST_YV12,
+  CLUTTER_GST_GLSL | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YV12")),
+  clutter_gst_yv12_glsl_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_glsl_post_paint,
+};
+
+/*
+ * YV12 (fragment program version)
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled V and U planes.
+ */
+
+static void
+clutter_gst_yv12_fp_init (ClutterActor        *actor,
+                          ClutterGstVideoSink *sink)
+{
+  gchar *shader;
+
+  shader = g_malloc(YV12_FP_SZ + 1);
+  _string_array_to_char_array (shader, YV12_fp);
+
+  /* the size given to glProgramStringARB is without the trailing '\0', which
+   * is precisely YV12_FP_SZ */
+  clutter_gst_video_sink_set_fp_shader (sink, shader, YV12_FP_SZ);
+  g_free(shader);
+}
+
+static void
+clutter_gst_yv12_fp_post_paint (ClutterActor        *actor,
+                                ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+
+  /* Disable the extra texture units */
+  priv->syms.glActiveTextureARB (GL_TEXTURE1);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE2);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE0);
+
+  /* Disable the shader */
+  glDisable (GL_FRAGMENT_PROGRAM_ARB);
+}
+
+static ClutterGstRenderer yv12_fp_renderer =
+{
+  "YV12 fp",
+  CLUTTER_GST_YV12,
+  CLUTTER_GST_FP | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YV12")),
+  clutter_gst_yv12_fp_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_fp_post_paint,
+};
+
+/*
+ * I420
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled U and V planes.
+ * Basically the same as YV12, but with the 2 chroma planes switched.
+ */
+
+static void
+clutter_gst_i420_glsl_init (ClutterActor        *actor,
+                            ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  COGLint location;
+
+  clutter_gst_video_sink_set_shader (sink,
+                                     yv12_to_rgba_shader);
+
+  cogl_program_use (priv->program);
+  location = cogl_program_get_uniform_location (priv->program, "ytex");
+  priv->glUniform1iARB (location, 0);
+  location = cogl_program_get_uniform_location (priv->program, "vtex");
+  priv->glUniform1iARB (location, 1);
+  location = cogl_program_get_uniform_location (priv->program, "utex");
+  priv->glUniform1iARB (location, 2);
+  cogl_program_use (COGL_INVALID_HANDLE);
+}
+
+static ClutterGstRenderer i420_glsl_renderer =
+{
+  "I420 glsl",
+  CLUTTER_GST_I420,
+  CLUTTER_GST_GLSL | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")),
+  clutter_gst_i420_glsl_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_glsl_post_paint,
+};
+
+/*
+ * I420 (fragment program version)
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled U and V planes.
+ * Basically the same as YV12, but with the 2 chroma planes switched.
+ */
+
+static void
+clutter_gst_i420_fp_init (ClutterActor        *actor,
+                          ClutterGstVideoSink *sink)
+{
+  gchar *shader;
+
+  shader = g_malloc(I420_FP_SZ + 1);
+  _string_array_to_char_array (shader, I420_fp);
+
+  /* the size given to glProgramStringARB is without the trailing '\0', which
+   * is precisely I420_FP_SZ */
+  clutter_gst_video_sink_set_fp_shader (sink, shader, I420_FP_SZ);
+  g_free(shader);
+}
+
+static ClutterGstRenderer i420_fp_renderer =
+{
+  "I420 fp",
+  CLUTTER_GST_I420,
+  CLUTTER_GST_FP | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")),
+  clutter_gst_i420_fp_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_fp_post_paint,
+};
+
+#endif /* CLUTTER_COGL_HAS_GL */
+
+/*
+ * AYUV
+ *
+ * This is a 4:4:4 YUV format with 8 bit samples for each component along
+ * with an 8 bit alpha blend value per pixel. Component ordering is A Y U V
+ * (as the name suggests).
+ */
+
+static void
+clutter_gst_ayuv_glsl_init(ClutterActor        *actor,
+                           ClutterGstVideoSink *sink)
+{
+  clutter_gst_video_sink_set_shader (sink, ayuv_to_rgba_shader);
+}
+
+static void
+clutter_gst_ayuv_upload (ClutterGstVideoSink *sink,
+                         GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+
+  clutter_texture_set_from_rgb_data (priv->texture,
+                                     GST_BUFFER_DATA (buffer),
+                                     TRUE,
+                                     priv->width,
+                                     priv->height,
+                                     GST_ROUND_UP_4 (4 * priv->width),
+                                     4,
+                                     0,
+                                     NULL);
+}
+
+static ClutterGstRenderer ayuv_glsl_renderer =
+{
+  "AYUV glsl",
+  CLUTTER_GST_AYUV,
+  CLUTTER_GST_GLSL,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")),
+  clutter_gst_ayuv_glsl_init,
+  clutter_gst_ayuv_upload,
+  NULL,
+  NULL,
+};
+
+static GSList *
+clutter_gst_build_renderers_list (ClutterGstSymbols *syms)
+{
+  GSList             *list = NULL;
+  const gchar        *gl_extensions;
+  GLint               nb_texture_units;
+  gint                features = 0, i;
+  /* The order of the list of renderers is important. They will be prepended
+   * to a GSList and we'll iterate over that list to choose the first matching
+   * renderer. Thus if you want to use the fp renderer over the glsl one, the
+   * fp renderer has to be put after the glsl one in this array */
+  ClutterGstRenderer *renderers[] =
+    {
+      &rgb24_renderer,
+      &rgb32_renderer,
+      &yv12_glsl_renderer,
+      &yv12_fp_renderer,
+      &i420_glsl_renderer,
+      &i420_fp_renderer,
+      &ayuv_glsl_renderer,
+      NULL
+    };
+
+  /* get the features */
+  gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS);
+  if (cogl_check_extension ("GL_ARB_multitexture", gl_extensions))
+    {
+      /* we need 3 texture units for planar YUV */
+      glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &nb_texture_units);
+
+      syms->glActiveTextureARB = (GLACTIVETEXTUREPROC)
+        cogl_get_proc_address ("glActiveTextureARB");
+      syms->glMultiTexCoord2fARB = (GLMULTITEXCOORD2FPROC)
+        cogl_get_proc_address ("glMultiTexCoord2fARB");
+
+      if (nb_texture_units >= 3 &&
+          syms->glActiveTextureARB &&
+          syms->glMultiTexCoord2fARB)
+        {
+          features |= CLUTTER_GST_MULTI_TEXTURE;
+        }
+    }
+
+  if (cogl_check_extension ("GL_ARB_fragment_program", gl_extensions))
+    {
+      /* the shaders we'll feed to the GPU are simple enough, we don't need
+       * to check GL limits for GL_FRAGMENT_PROGRAM_ARB */
+
+      syms->glGenProgramsARB = (GLGENPROGRAMSPROC)
+        cogl_get_proc_address ("glGenProgramsARB");
+      syms->glBindProgramARB = (GLBINDPROGRAMPROC)
+        cogl_get_proc_address ("glBindProgramARB");
+      syms->glProgramStringARB = (GLPROGRAMSTRINGPROC)
+        cogl_get_proc_address ("glProgramStringARB");
+
+      if (syms->glGenProgramsARB &&
+          syms->glBindProgramARB &&
+          syms->glProgramStringARB)
+        {
+          features |= CLUTTER_GST_FP;
+        }
+    }
+
+  if (cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
+    features |= CLUTTER_GST_GLSL;
+
+  GST_INFO ("GL features: 0x%08x", features);
+
+  for (i = 0; renderers[i]; i++)
+    {
+      gint needed = renderers[i]->flags;
+
+      if ((needed & features) == needed)
+        list = g_slist_prepend (list, renderers[i]);
+    }
+
+  return list;
+}
+
+static void
+append_cap (gpointer data, gpointer user_data)
+{
+  ClutterGstRenderer *renderer = (ClutterGstRenderer *)data;
+  GstCaps *caps = (GstCaps *)user_data;
+  GstCaps *writable_caps;
+
+  writable_caps =
+    gst_caps_make_writable (gst_static_caps_get (&renderer->caps));
+  gst_caps_append (caps, writable_caps);
+}
+
+static GstCaps *
+clutter_gst_build_caps (GSList *renderers)
+{
+  GstCaps *caps;
+
+  caps = gst_caps_new_empty ();
+
+  g_slist_foreach (renderers, append_cap, caps);
+
+  return caps;
+}
+
+ClutterGstRenderer *
+clutter_gst_find_renderer_by_format (ClutterGstVideoSink  *sink,
+                                     ClutterGstVideoFormat format)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  ClutterGstRenderer *renderer = NULL;
+  GSList *element;
+
+  for (element = priv->renderers; element; element = g_slist_next(element))
+    {
+      ClutterGstRenderer *candidate = (ClutterGstRenderer *)element->data;
+
+      if (candidate->format == format)
+        {
+          renderer = candidate;
+          break;
+        }
+    }
+
+  return renderer;
+}
+
 static gboolean
 clutter_gst_video_sink_idle_func (gpointer data)
 {
@@ -342,116 +860,58 @@ clutter_gst_video_sink_idle_func (gpointer data)
   sink = data;
   priv = sink->priv;
 
-  buffer = g_async_queue_try_pop (priv->async_queue);
-  if (buffer == NULL || G_UNLIKELY (!GST_IS_BUFFER (buffer)))
+  g_mutex_lock (priv->buffer_lock);
+  if (!priv->buffer)
+    {
+      priv->idle_id = 0;
+      g_mutex_unlock (priv->buffer_lock);
+      return FALSE;
+    }
+
+  buffer = priv->buffer;
+  priv->buffer = NULL;
+
+  if (G_UNLIKELY (!GST_IS_BUFFER (buffer)))
     {
+      priv->idle_id = 0;
+      g_mutex_unlock (priv->buffer_lock);
       return FALSE;
     }
 
+  priv->idle_id = 0;
+  g_mutex_unlock (priv->buffer_lock);
+
+
   if ((priv->format == CLUTTER_GST_RGB32) || (priv->format == CLUTTER_GST_AYUV))
     {
-      clutter_texture_set_from_rgb_data (priv->texture,
-                                         GST_BUFFER_DATA (buffer),
-                                         TRUE,
-                                         priv->width,
-                                         priv->height,
-                                         GST_ROUND_UP_4 (4 * priv->width),
-                                         4,
-                                         priv->bgr ?
-                                         CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
-                                         NULL);
+      priv->renderer->upload (sink, buffer);
 
       /* Initialise AYUV shader */
       if ((priv->format == CLUTTER_GST_AYUV) && !priv->shaders_init)
-        clutter_gst_video_sink_set_shader (sink,
-                                           ayuv_to_rgba_shader);
+        priv->renderer->upload (sink, buffer);
     }
   else if (priv->format == CLUTTER_GST_RGB24)
     {
-      clutter_texture_set_from_rgb_data (priv->texture,
-                                         GST_BUFFER_DATA (buffer),
-                                         FALSE,
-                                         priv->width,
-                                         priv->height,
-                                         GST_ROUND_UP_4 (3 * priv->width),
-                                         3,
-                                         priv->bgr ?
-                                         CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
-                                         NULL);
+      priv->renderer->upload (sink, buffer);
     }
-  else if (priv->format == CLUTTER_GST_YV12)
+  else if (priv->format == CLUTTER_GST_YV12 || priv->format == CLUTTER_GST_I420)
     {
-      CoglHandle y_tex =
-        cogl_texture_new_from_data (priv->width,
-                                    priv->height,
-                                    -1,
-                                    FALSE,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    priv->width,
-                                    GST_BUFFER_DATA (buffer));
-      cogl_texture_set_filters (y_tex, CGL_LINEAR, CGL_LINEAR);
-      clutter_texture_set_cogl_texture (priv->texture, y_tex);
-      cogl_texture_unref (y_tex);
-      
-      if (priv->u_tex)
-        cogl_texture_unref (priv->u_tex);
-      
-      if (priv->v_tex)
-        cogl_texture_unref (priv->v_tex);
-      
-      priv->v_tex =
-        cogl_texture_new_from_data (priv->width/2,
-                                    priv->height/2,
-                                    -1,
-                                    FALSE,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    priv->width/2,
-                                    GST_BUFFER_DATA (buffer) +
-                                      (priv->width * priv->height));
-      cogl_texture_set_filters (priv->v_tex, CGL_LINEAR, CGL_LINEAR);
-
-      priv->u_tex =
-        cogl_texture_new_from_data (priv->width/2,
-                                    priv->height/2,
-                                    -1,
-                                    FALSE,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    priv->width/2,
-                                    GST_BUFFER_DATA (buffer) +
-                                      (priv->width * priv->height) +
-                                      (priv->width/2 * priv->height/2));
-      cogl_texture_set_filters (priv->u_tex, CGL_LINEAR, CGL_LINEAR);
       
-      /* Initialise YV12 shader */
+      priv->renderer->upload (sink, buffer);
+
+      /* Initialize renderer */
       if (!priv->shaders_init)
         {
-#ifdef CLUTTER_COGL_HAS_GL
-          COGLint location;
-          clutter_gst_video_sink_set_shader (sink,
-                                             yv12_to_rgba_shader);
-          
-          cogl_program_use (priv->program);
-          location = cogl_program_get_uniform_location (priv->program, "ytex");
-          priv->glUniform1iARB (location, 0);
-          location = cogl_program_get_uniform_location (priv->program, "utex");
-          priv->glUniform1iARB (location, 1);
-          location = cogl_program_get_uniform_location (priv->program, "vtex");
-          priv->glUniform1iARB (location, 2);
-          cogl_program_use (COGL_INVALID_HANDLE);
-          
+          priv->renderer->init (CLUTTER_ACTOR (priv->texture), sink);
           g_signal_connect (priv->texture,
                             "paint",
-                            G_CALLBACK (clutter_gst_yv12_paint),
+                            G_CALLBACK (priv->renderer->paint),
                             sink);
           g_signal_connect_after (priv->texture,
                                   "paint",
-                                  G_CALLBACK (clutter_gst_yv12_post_paint),
+                                  G_CALLBACK (priv->renderer->post_paint),
                                   sink);
-#endif
-        }
+        }
     }
 
   gst_buffer_unref (buffer);
@@ -459,6 +919,40 @@ clutter_gst_video_sink_idle_func (gpointer data)
   return FALSE;
 }
 
+static void
+clutter_gst_video_sink_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template
+                     (element_class,
+                      gst_static_pad_template_get (&sinktemplate_all));
+
+  gst_element_class_set_details (element_class,
+                                 &clutter_gst_video_sink_details);
+}
+
+static void
+clutter_gst_video_sink_init (ClutterGstVideoSink      *sink,
+                             ClutterGstVideoSinkClass *klass)
+{
+  ClutterGstVideoSinkPrivate *priv;
+
+  sink->priv = priv =
+    G_TYPE_INSTANCE_GET_PRIVATE (sink, CLUTTER_GST_TYPE_VIDEO_SINK,
+                                 ClutterGstVideoSinkPrivate);
+
+  priv->buffer_lock = g_mutex_new ();
+  priv->use_shaders = TRUE;
+  priv->renderers = clutter_gst_build_renderers_list (&priv->syms);
+  priv->caps = clutter_gst_build_caps (priv->renderers);
+
+#ifdef CLUTTER_COGL_HAS_GL
+  priv->glUniform1iARB = (GLUNIFORM1IPROC)
+    cogl_get_proc_address ("glUniform1iARB");
+#endif
+}
+
 static GstFlowReturn
 clutter_gst_video_sink_render (GstBaseSink *bsink,
                                GstBuffer   *buffer)
@@ -469,25 +963,36 @@ clutter_gst_video_sink_render (GstBaseSink *bsink,
   sink = CLUTTER_GST_VIDEO_SINK (bsink);
   priv = sink->priv;
 
-  g_async_queue_push (priv->async_queue, gst_buffer_ref (buffer));
 
-  clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
-                                 clutter_gst_video_sink_idle_func,
-                                 sink,
-                                 NULL);
+  g_mutex_lock (priv->buffer_lock);
+  if (priv->buffer)
+    { 
+      gst_buffer_unref (priv->buffer);
+    }
+  priv->buffer = gst_buffer_ref (buffer);
+
+  if (priv->idle_id == 0)
+    {
+      priv->idle_id = clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
+                                     clutter_gst_video_sink_idle_func,
+                                     sink,
+                                     NULL);
+      /* the lock must be held when adding this idle, if it is not the idle
+       * callback would be invoked before priv->idle_id had been assigned
+       */
+    }
+  g_mutex_unlock (priv->buffer_lock);
 
   return GST_FLOW_OK;
 }
 
 static GstCaps *
-clutter_gst_video_sink_get_caps (GstBaseSink *sink)
+clutter_gst_video_sink_get_caps (GstBaseSink *bsink)
 {
-  ClutterGstVideoSinkPrivate *priv = CLUTTER_GST_VIDEO_SINK (sink)->priv;
-  
-  if (priv->use_shaders && cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
-    return gst_static_pad_template_get_caps (&sinktemplate_shaders);
-  else
-    return gst_static_pad_template_get_caps (&sinktemplate);
+  ClutterGstVideoSink *sink;
+
+  sink = CLUTTER_GST_VIDEO_SINK (bsink);
+  return gst_caps_copy (sink->priv->caps);
 }
 
 static gboolean
@@ -510,17 +1015,7 @@ clutter_gst_video_sink_set_caps (GstBaseSink *bsink,
 
   clutter_gst_video_sink_set_shader (sink, NULL);
 
-  if (priv->use_shaders && cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
-    intersection 
-      = gst_caps_intersect 
-            (gst_static_pad_template_get_caps (&sinktemplate_shaders), 
-             caps);
-  else
-    intersection 
-      = gst_caps_intersect 
-            (gst_static_pad_template_get_caps (&sinktemplate), 
-             caps);
-
+  intersection = gst_caps_intersect (priv->caps, caps);
   if (gst_caps_is_empty (intersection)) 
     return FALSE;
 
@@ -553,17 +1048,23 @@ clutter_gst_video_sink_set_caps (GstBaseSink *bsink,
   else 
     priv->par_n = priv->par_d = 1;
 
+#if CLUTTER_COGL_HAS_GL
   ret = gst_structure_get_fourcc (structure, "format", &fourcc);
-  if (ret && (fourcc == GST_RIFF_YV12))
+  if (ret && (fourcc == GST_MAKE_FOURCC ('Y', 'V', '1', '2')))
     {
       priv->format = CLUTTER_GST_YV12;
     }
+  else if (ret && (fourcc == GST_MAKE_FOURCC ('I', '4', '2', '0')))
+    {
+      priv->format = CLUTTER_GST_I420;
+    }
   else if (ret && (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V')))
     {
       priv->format = CLUTTER_GST_AYUV;
       priv->bgr = FALSE;
     }
   else
+#endif
     {
       guint32 width;
       gst_structure_get_int (structure, "red_mask", &red_mask);
@@ -582,6 +1083,15 @@ clutter_gst_video_sink_set_caps (GstBaseSink *bsink,
         }
     }
 
+  priv->renderer = clutter_gst_find_renderer_by_format (sink, priv->format);
+  if (G_UNLIKELY (priv->renderer == NULL))
+    {
+      GST_ERROR_OBJECT (sink, "could not find a suitable renderer");
+      return FALSE;
+    }
+
+  GST_INFO_OBJECT (sink, "using the %s renderer", priv->renderer->name);
+
   return TRUE;
 }
 
@@ -596,16 +1106,28 @@ clutter_gst_video_sink_dispose (GObject *object)
 
   clutter_gst_video_sink_set_shader (self, NULL);
 
+  if (priv->idle_id > 0)
+    {
+      g_source_remove (priv->idle_id);
+      priv->idle_id = 0;
+    }
+
   if (priv->texture)
     {
       g_object_unref (priv->texture);
       priv->texture = NULL;
     }
 
-  if (priv->async_queue)
+  if (priv->buffer_lock)
     {
-      g_async_queue_unref (priv->async_queue);
-      priv->async_queue = NULL;
+      g_mutex_free (priv->buffer_lock);
+      priv->buffer_lock = NULL;
+    }
+
+  if (priv->caps)
+    {
+      gst_caps_unref (priv->caps);
+      priv->caps = NULL;
     }
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
@@ -620,6 +1142,8 @@ clutter_gst_video_sink_finalize (GObject *object)
   self = CLUTTER_GST_VIDEO_SINK (object);
   priv = self->priv;
 
+  g_slist_free (priv->renderers);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -649,7 +1173,7 @@ clutter_gst_video_sink_set_property (GObject *object,
       if (priv->use_shaders != use_shaders)
         {
           priv->use_shaders = use_shaders;
-          g_object_notify (object, "use_shaders");
+          g_object_notify (object, "use-shaders");
         }
       break;
     default:
@@ -686,21 +1210,15 @@ static gboolean
 clutter_gst_video_sink_stop (GstBaseSink *base_sink)
 {
   ClutterGstVideoSinkPrivate *priv;
-  GstBuffer *buffer;
 
   priv = CLUTTER_GST_VIDEO_SINK (base_sink)->priv;
 
-  g_async_queue_lock (priv->async_queue);
+  g_mutex_lock (priv->buffer_lock);
+  if (priv->buffer)
+    gst_buffer_unref (priv->buffer);
+  priv->buffer = NULL;
+  g_mutex_unlock (priv->buffer_lock);
 
-  /* Remove all remaining objects from the queue */
-  do
-    {
-      buffer = g_async_queue_try_pop_unlocked (priv->async_queue);
-      if (buffer)
-        gst_buffer_unref (buffer);
-    } while (buffer != NULL);
-
-  g_async_queue_unlock (priv->async_queue);
 
   return TRUE;
 }
@@ -735,11 +1253,11 @@ clutter_gst_video_sink_class_init (ClutterGstVideoSinkClass *klass)
 
   g_object_class_install_property 
               (gobject_class, PROP_USE_SHADERS,
-               g_param_spec_boolean ("use_shaders",
+               g_param_spec_boolean ("use-shaders",
                                      "Use shaders",
                                      "Use a fragment shader to accelerate "
                                      "colour-space conversion.",
-                                     FALSE,
+                                     TRUE,
                                      G_PARAM_READWRITE));
 }
 
@@ -778,4 +1296,4 @@ GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
                           VERSION,
                           "LGPL", /* license */
                           PACKAGE,
-                          "");
+                          "http://www.clutter-project.org";);
diff --git a/libbanshee/shaders/I420.h b/libbanshee/shaders/I420.h
new file mode 100644
index 0000000..f81a1d1
--- /dev/null
+++ b/libbanshee/shaders/I420.h
@@ -0,0 +1,36 @@
+/*
+ * This file was generated by pso2h.
+ */
+
+#ifndef __I420_H__
+#define __I420_H__
+
+/*
+ * This define is the size of the shader in bytes.  More precisely it's the
+ * sum of strlen() of every string in the array.
+ */
+#define I420_FP_SZ    526
+
+static const char *I420_fp[] =
+{
+  "!!ARBfp1.0\n",
+  "PARAM c[2] = { { 1, 1.1640625, 0.0625, 2.015625 },\n",
+  "{ 0.5, 0.390625, 0.8125, 1.5976562 } };\n",
+  "TEMP R0;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[0], 2D;\n",
+  "ADD R0.x, R0.y, -c[0].z;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[2], 2D;\n",
+  "ADD R0.z, R0.y, -c[1].x;\n",
+  "MUL R0.x, R0, c[0].y;\n",
+  "MAD result.color.z, R0, c[0].w, R0.x;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[1], 2D;\n",
+  "ADD R0.y, R0, -c[1].x;\n",
+  "MAD R0.z, -R0, c[1].y, R0.x;\n",
+  "MAD result.color.y, -R0, c[1].z, R0.z;\n",
+  "MAD result.color.x, R0.y, c[1].w, R0;\n",
+  "MOV result.color.w, c[0].x;\n",
+  "END\n",
+  NULL
+};
+
+#endif
diff --git a/libbanshee/shaders/YV12.h b/libbanshee/shaders/YV12.h
new file mode 100644
index 0000000..b36348a
--- /dev/null
+++ b/libbanshee/shaders/YV12.h
@@ -0,0 +1,36 @@
+/*
+ * This file was generated by pso2h.
+ */
+
+#ifndef __YV12_H__
+#define __YV12_H__
+
+/*
+ * This define is the size of the shader in bytes.  More precisely it's the
+ * sum of strlen() of every string in the array.
+ */
+#define YV12_FP_SZ    526
+
+static const char *YV12_fp[] =
+{
+  "!!ARBfp1.0\n",
+  "PARAM c[2] = { { 1, 1.1640625, 0.0625, 2.015625 },\n",
+  "{ 0.5, 0.390625, 0.8125, 1.5976562 } };\n",
+  "TEMP R0;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[0], 2D;\n",
+  "ADD R0.x, R0.y, -c[0].z;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[1], 2D;\n",
+  "ADD R0.z, R0.y, -c[1].x;\n",
+  "MUL R0.x, R0, c[0].y;\n",
+  "MAD result.color.z, R0, c[0].w, R0.x;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[2], 2D;\n",
+  "ADD R0.y, R0, -c[1].x;\n",
+  "MAD R0.z, -R0, c[1].y, R0.x;\n",
+  "MAD result.color.y, -R0, c[1].z, R0.z;\n",
+  "MAD result.color.x, R0.y, c[1].w, R0;\n",
+  "MOV result.color.w, c[0].x;\n",
+  "END\n",
+  NULL
+};
+
+#endif
diff --git a/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs b/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
index 2795d2c..5e4d2dc 100644
--- a/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
+++ b/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
@@ -57,10 +57,12 @@ namespace Banshee.GStreamer
     internal delegate void BansheePlayerIterateCallback (IntPtr player);
     internal delegate void BansheePlayerBufferingCallback (IntPtr player, int buffering_progress);
     internal delegate void BansheePlayerVisDataCallback (IntPtr player, int channels, int samples, IntPtr data, int bands, IntPtr spectrum);
+    internal delegate IntPtr VideoPipelineSetupHandler (IntPtr player, IntPtr bus);
 
     internal delegate void GstTaggerTagFoundCallback (IntPtr player, string tagName, ref GLib.Value value);
     
-    public class PlayerEngine : Banshee.MediaEngine.PlayerEngine, IEqualizer, IVisualizationDataSource
+    public class PlayerEngine : Banshee.MediaEngine.PlayerEngine, 
+        IEqualizer, IVisualizationDataSource, ISupportClutter
     {
         private uint GST_CORE_ERROR = 0;
         private uint GST_LIBRARY_ERROR = 0;
@@ -75,6 +77,7 @@ namespace Banshee.GStreamer
         private BansheePlayerIterateCallback iterate_callback;
         private BansheePlayerBufferingCallback buffering_callback;
         private BansheePlayerVisDataCallback vis_data_callback;
+        private VideoPipelineSetupHandler video_pipeline_setup_callback;
         private GstTaggerTagFoundCallback tag_found_callback;
         
         private bool buffering_finished;
@@ -142,6 +145,7 @@ namespace Banshee.GStreamer
             iterate_callback = new BansheePlayerIterateCallback (OnIterate);
             buffering_callback = new BansheePlayerBufferingCallback (OnBuffering);
             vis_data_callback = new BansheePlayerVisDataCallback (OnVisualizationData);
+            video_pipeline_setup_callback = new VideoPipelineSetupHandler (OnVideoPipelineSetup);
             tag_found_callback = new GstTaggerTagFoundCallback (OnTagFound);
             
             bp_set_eos_callback (handle, eos_callback);
@@ -150,7 +154,14 @@ namespace Banshee.GStreamer
             bp_set_state_changed_callback (handle, state_changed_callback);
             bp_set_buffering_callback (handle, buffering_callback);
             bp_set_tag_found_callback (handle, tag_found_callback);
+            bp_set_video_pipeline_setup_callback (handle, video_pipeline_setup_callback);
             
+            if (!bp_initialize_pipeline (handle)) {
+                bp_destroy (handle);
+                handle = new HandleRef (this, IntPtr.Zero);
+                throw new ApplicationException (Catalog.GetString ("Could not initialize GStreamer library"));
+            }
+
             OnStateChanged (PlayerState.Ready);
             
             if (pending_volume >= 0) {
@@ -159,8 +170,6 @@ namespace Banshee.GStreamer
             
             InstallPreferences ();
             ReplayGainEnabled = ReplayGainEnabledSchema.Get ();
-
-            bp_set_vis_data_callback (handle, vis_data_callback);
         }
         
         public override void Dispose ()
@@ -488,6 +497,53 @@ namespace Banshee.GStreamer
             get { return bp_replaygain_get_enabled (handle); }
             set { bp_replaygain_set_enabled (handle, value); }
         }
+
+#region ISupportClutter
+
+        private IntPtr clutter_video_sink;
+        private IntPtr clutter_video_texture;
+        private bool clutter_video_sink_enabled;
+
+        public void EnableClutterVideoSink (IntPtr videoTexture)
+        {
+            clutter_video_sink_enabled = true;
+            clutter_video_texture = videoTexture;
+        }
+
+        public void DisableClutterVideoSink ()
+        {
+            clutter_video_sink_enabled = false;
+            clutter_video_texture = IntPtr.Zero;
+        }
+
+        public bool IsClutterVideoSinkInitialized {
+            get { return 
+                clutter_video_sink_enabled &&
+                clutter_video_texture != IntPtr.Zero &&
+                clutter_video_sink != IntPtr.Zero;
+            }
+        }
+
+        private IntPtr OnVideoPipelineSetup (IntPtr player, IntPtr bus)
+        {
+            try {
+                if (clutter_video_sink_enabled && clutter_video_sink == IntPtr.Zero) {
+                    clutter_video_sink = clutter_gst_video_sink_new (clutter_video_texture);
+                } else if (!clutter_video_sink_enabled && clutter_video_sink != IntPtr.Zero) {
+                    clutter_video_sink = IntPtr.Zero;
+                    clutter_video_texture = IntPtr.Zero;
+                }
+            } catch {
+                Log.Warning ("Clutter support could not be initialized");
+                clutter_video_sink = IntPtr.Zero;
+                clutter_video_texture = IntPtr.Zero;
+                clutter_video_sink_enabled = false;
+            }
+
+            return clutter_video_sink;
+        }
+
+#endregion
         
 #region Preferences
 
@@ -531,6 +587,9 @@ namespace Banshee.GStreamer
         private static extern IntPtr bp_new ();
         
         [DllImport ("libbanshee")]
+        private static extern bool bp_initialize_pipeline (HandleRef player);
+
+        [DllImport ("libbanshee")]
         private static extern void bp_destroy (HandleRef player);
         
         [DllImport ("libbanshee")]
@@ -553,6 +612,10 @@ namespace Banshee.GStreamer
         [DllImport ("libbanshee")]
         private static extern void bp_set_buffering_callback (HandleRef player,
             BansheePlayerBufferingCallback cb);
+
+        [DllImport ("libbanshee")]
+        private static extern void bp_set_video_pipeline_setup_callback (HandleRef player,
+            VideoPipelineSetupHandler cb);
         
         [DllImport ("libbanshee")]
         private static extern void bp_set_tag_found_callback (HandleRef player,
@@ -635,5 +698,8 @@ namespace Banshee.GStreamer
         
         [DllImport ("libbanshee")]
         private static extern bool bp_replaygain_get_enabled (HandleRef player);
+
+        [DllImport ("libbanshee")]
+        private static extern IntPtr clutter_gst_video_sink_new (IntPtr texture);
     }
 }
diff --git a/src/Core/Banshee.Services/Banshee.MediaEngine/ISupportClutter.cs b/src/Core/Banshee.Services/Banshee.MediaEngine/ISupportClutter.cs
new file mode 100644
index 0000000..d200299
--- /dev/null
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/ISupportClutter.cs
@@ -0,0 +1,39 @@
+//
+// ISupportClutter.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright 2009 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace Banshee.MediaEngine
+{
+    public interface ISupportClutter
+    {
+        void EnableClutterVideoSink (IntPtr videoTexture);
+        void DisableClutterVideoSink ();
+        bool IsClutterVideoSinkInitialized { get; }
+    }
+}
diff --git a/src/Core/Banshee.Services/Banshee.MediaEngine/VideoDisplayContextType.cs b/src/Core/Banshee.Services/Banshee.MediaEngine/VideoDisplayContextType.cs
index 11c154c..413bb18 100644
--- a/src/Core/Banshee.Services/Banshee.MediaEngine/VideoDisplayContextType.cs
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/VideoDisplayContextType.cs
@@ -34,6 +34,6 @@ namespace Banshee.MediaEngine
     {
         Unsupported = 0,
         GdkWindow = 1,
-        ClutterTexture = 2
+        Custom = 2
     }
 }
diff --git a/src/Core/Banshee.Services/Banshee.Services.csproj b/src/Core/Banshee.Services/Banshee.Services.csproj
index 066f4a6..d4cd0d0 100644
--- a/src/Core/Banshee.Services/Banshee.Services.csproj
+++ b/src/Core/Banshee.Services/Banshee.Services.csproj
@@ -242,6 +242,7 @@
     <Compile Include="Banshee.Hardware\DeviceCommand.cs" />
     <Compile Include="Banshee.MediaEngine\VideoDisplayContextType.cs" />
     <Compile Include="Banshee.MediaEngine\IBpmDetector.cs" />
+    <Compile Include="Banshee.MediaEngine\ISupportClutter.cs" />
     <Compile Include="Banshee.Collection.Database\Tests\DatabaseAlbumInfoTests.cs" />
     <Compile Include="Banshee.Collection.Database\Tests\DatabaseArtistInfoTests.cs" />
     <Compile Include="Banshee.Collection.Database\Tests\DatabaseTrackInfoTests.cs" />
@@ -276,4 +277,4 @@
       </Properties>
     </MonoDevelop>
   </ProjectExtensions>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/Core/Banshee.Services/Makefile.am b/src/Core/Banshee.Services/Makefile.am
index a458bcb..3f6adb0 100644
--- a/src/Core/Banshee.Services/Makefile.am
+++ b/src/Core/Banshee.Services/Makefile.am
@@ -81,6 +81,7 @@ SOURCES =  \
 	Banshee.MediaEngine/IBpmDetector.cs \
 	Banshee.MediaEngine/IEqualizer.cs \
 	Banshee.MediaEngine/IPlayerEngineService.cs \
+	Banshee.MediaEngine/ISupportClutter.cs \
 	Banshee.MediaEngine/ITranscoder.cs \
 	Banshee.MediaEngine/IVisualizationDataSource.cs \
 	Banshee.MediaEngine/NullPlayerEngine.cs \
diff --git a/src/Extensions/Banshee.NowPlaying.Clutter/Makefile.am b/src/Extensions/Banshee.NowPlaying.Clutter/Makefile.am
index 1b05fdc..6d6ce60 100644
--- a/src/Extensions/Banshee.NowPlaying.Clutter/Makefile.am
+++ b/src/Extensions/Banshee.NowPlaying.Clutter/Makefile.am
@@ -15,12 +15,12 @@ SOURCES =  \
 
 RESOURCES = Banshee.NowPlaying.Clutter.addin.xml
 
-if HAVE_CLUTTER
-include $(top_srcdir)/build/build.mk
-module_SCRIPTS += Banshee.NowPlaying.Clutter.dll.config
-EXTRA_DIST += Banshee.NowPlaying.Clutter.dll.config
-else
+#if HAVE_CLUTTER
+#include $(top_srcdir)/build/build.mk
+#module_SCRIPTS += Banshee.NowPlaying.Clutter.dll.config
+#EXTRA_DIST += Banshee.NowPlaying.Clutter.dll.config
+#else
 EXTRA_DIST = $(SOURCES) $(RESOURCES) Banshee.NowPlaying.Clutter
-endif
+#endif
 
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]