[banshee/stable-vis] Added Clutter video support to GStreamer pipeline



commit 6e9026ba481fd7a9b8ac6fce23808f6f8bc5ce44
Author: Aaron Bockover <abockover novell com>
Date:   Mon May 4 23:07:57 2009 -0400

    Added Clutter video support to GStreamer pipeline
    
    This commit adds the Clutter video sink to libbanshee and an API for overriding
    the video pipeline setup phase.
    
    One can enable Clutter video rendering by calling the following method before
    the engine has been initialized, passing a pointer to an already allocated
    ClutterTexture actor:
    
      Banshee.GStreamer.PlayerEngine.EnableClutterVideoSink (ClutterTexture *)
    
    There is no UI integration in this commit, and none will be added to the
    stable branch of Banshee. This is mainly for Cubano/other clients targeting
    the stable branch.
    
    By default this Clutter patch does not modify the pipeline's behavior and
    must be manually enabled through the EnableClutterVideoSink call.
---
 build/m4/banshee/libbanshee.m4                     |   11 +
 configure.ac                                       |   50 +-
 libbanshee/Makefile.am                             |    9 +-
 libbanshee/banshee-player-private.h                |    2 +
 libbanshee/banshee-player-video.c                  |   14 +
 libbanshee/banshee-player.c                        |   10 +-
 libbanshee/clutter-gst-shaders.h                   |   43 ++
 libbanshee/clutter-gst-video-sink.c                |  787 ++++++++++++++++++++
 libbanshee/libbanshee.cproj                        |    6 +-
 .../Banshee.GStreamer.Engine/PlayerEngine.cs       |   38 +
 .../Banshee.GStreamer/Banshee.GStreamer.csproj     |    2 -
 .../Banshee.GStreamer/PlayerEngine.cs              |   47 ++-
 12 files changed, 989 insertions(+), 30 deletions(-)

diff --git a/build/m4/banshee/libbanshee.m4 b/build/m4/banshee/libbanshee.m4
index ca1f23f..403539f 100644
--- a/build/m4/banshee/libbanshee.m4
+++ b/build/m4/banshee/libbanshee.m4
@@ -25,8 +25,19 @@ AC_DEFUN([BANSHEE_CHECK_LIBBANSHEE],
 		PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.8)
 	fi
 
+	PKG_CHECK_MODULES(CLUTTER,
+		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,
+			[Define if the video sink should be Clutter])
+	fi
+
 	AM_CONDITIONAL(HAVE_X11, test "x$GRAPHICS_SUBSYSTEM" = "xX11")
 	AM_CONDITIONAL(HAVE_QUARTZ, test "x$GRAPHICS_SUBSYSTEM" = "xQuartz")
+	AM_CONDITIONAL(HAVE_CLUTTER, test "x$enable_clutter" = "xyes")
 
 	AC_SUBST(GRAPHICS_SUBSYSTEM)
 	AC_SUBST(LIBBANSHEE_CFLAGS)
diff --git a/configure.ac b/configure.ac
index 8791b64..61a8b9c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,9 +249,11 @@ src/Extensions/Banshee.Torrent/Makefile
 src/Extensions/Banshee.RemoteAudio/Makefile
 ])
 
-echo "
+cat <<EOF
+
 ${PACKAGE}-${VERSION}
 
+  Build Environment
     Install Prefix:    ${prefix}
     Datadir:           ${expanded_datadir}
     Libdir:            ${expanded_libdir}
@@ -260,36 +262,44 @@ ${PACKAGE}-${VERSION}
     Mono C# Compiler:  ${MCS} ${GMCS_FLAGS}
     Mono Runtime:      ${MONO}
 
-    Digital Audio Player (DAP) Support:
-      Mass Storage:    yes
-      MTP:             ${enable_libmtp}
-      iPod:            ${enable_ipodsharp}
-      Karma:           ${enable_karmasharp}
+  Video/Graphics:
+    Graphics System:   ${GRAPHICS_SUBSYSTEM}
+    X11 Video:         ${have_xvidmode}
+    Clutter Video:     ${enable_clutter}
+
+  Operating System/Desktop Environment:
+    GNOME Support:     ${enable_gnome}
+    OSX Support:       ${enable_osx}
+
+  Digital Audio Player (DAP) Support:
+    Mass Storage:      yes
+    MTP:               ${enable_libmtp}
+    iPod:              ${enable_ipodsharp}
+    Karma:             ${enable_karmasharp}
 
+  Extra Features:
     DAAP Support:      ${enable_daap}
     Podcast Support:   ${enable_podcast}
     Boo Scripting:     ${enable_boo}
-    X11 Video Support: ${have_xvidmode}
-    GNOME Support:     ${enable_gnome}
-    OSX Support:       ${enable_osx}
-    Graphics System:   ${GRAPHICS_SUBSYSTEM}
 
-    Unit Tests:        ${do_tests}	(requires nunit >= ${NUNIT_REQUIRED})
+  Build/Development:
+    Unit Tests:        ${do_tests} (requires nunit >= ${NUNIT_REQUIRED})
     Release Build:     ${enable_release}
     Vendor Build ID:   ${vendor_build_id}
-"
+
+EOF
 
 # Unstable/in-development features; only show them if they were manually enabled
-if test "x$enable_moonlight" = "xyes";    then echo "    Moonlight Effects: ${enable_moonlight}"; fi
-if test "x$enable_remote_audio" = "xyes"; then echo "    Remote Audio:      ${enable_remote_audio}"; fi
-if test "x$enable_mediaweb" = "xyes";     then echo "    MediaWeb:          ${enable_mediaweb}"; fi
-if test "x$enable_torrent" = "xyes";      then echo "    Torrent Podcasts:  ${enable_torrent}"; fi
+if test "x$enable_moonlight" = "xyes";    then br=yes; echo "  Moonlight Effects:   ${enable_moonlight}";    fi
+if test "x$enable_remote_audio" = "xyes"; then br=yes; echo "  Remote Audio:        ${enable_remote_audio}"; fi
+if test "x$enable_mediaweb" = "xyes";     then br=yes; echo "  MediaWeb:            ${enable_mediaweb}";     fi
+if test "x$enable_torrent" = "xyes";      then br=yes; echo "  Torrent Podcasts:    ${enable_torrent}";      fi
 
 if test -d ${expanded_libdir}/${PACKAGE}; then
-	echo
-	echo "WARNING: An existing Banshee install is in ${expanded_libdir}/${PACKAGE}"
-	echo "         Remove the existing install before installing this build."
-	echo "         Installing over an existing install will cause conflicts!"
+	if test x$br = xyes; then echo; fi
+	echo "  WARNING: An existing Banshee install is in ${expanded_libdir}/${PACKAGE}"
+	echo "           Remove the existing install before installing this build."
+	echo "           Installing over an existing install will cause conflicts!"
 	echo 
 fi
 
diff --git a/libbanshee/Makefile.am b/libbanshee/Makefile.am
index f911800..4864c79 100644
--- a/libbanshee/Makefile.am
+++ b/libbanshee/Makefile.am
@@ -24,6 +24,10 @@ libbanshee_la_SOURCES =  \
 	banshee-tagger.c \
 	banshee-transcoder.c
 
+if HAVE_CLUTTER
+libbanshee_la_SOURCES += clutter-gst-video-sink.c
+endif
+
 noinst_HEADERS =  \
 	banshee-gst.h \
 	banshee-player-cdda.h \
@@ -34,7 +38,10 @@ noinst_HEADERS =  \
 	banshee-player-replaygain.h \
 	banshee-player-video.h \
 	banshee-player-vis.h \
-	banshee-tagger.h
+	banshee-tagger.h \
+	clutter-gst-shaders.h \
+	clutter-gst-video-sink.c \
+	clutter-gst-video-sink.h
 
 libbanshee_la_LIBADD = \
 	$(LIBBANSHEE_LIBS) \
diff --git a/libbanshee/banshee-player-private.h b/libbanshee/banshee-player-private.h
index cfa2d2c..c3319da 100644
--- a/libbanshee/banshee-player-private.h
+++ b/libbanshee/banshee-player-private.h
@@ -67,6 +67,7 @@ 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);
 
 struct BansheePlayer {
     // Player Callbacks
@@ -77,6 +78,7 @@ struct BansheePlayer {
     BansheePlayerBufferingCallback buffering_cb;
     BansheePlayerTagFoundCallback tag_found_cb;
     BansheePlayerVisDataCallback vis_data_cb;
+    BansheePlayerVideoPipelineSetupCallback video_pipeline_setup_cb;
 
     // Pipeline Elements
     GstElement *playbin;
diff --git a/libbanshee/banshee-player-video.c b/libbanshee/banshee-player-video.c
index 1d9a7e2..4f45833 100644
--- a/libbanshee/banshee-player-video.c
+++ b/libbanshee/banshee-player-video.c
@@ -126,6 +126,14 @@ _bp_video_pipeline_setup (BansheePlayer *player, GstBus *bus)
 {
     GstElement *videosink;
     
+    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);
+            return;
+        }
+    }
+    
     #ifdef GDK_WINDOWING_X11
     
     g_return_if_fail (IS_BANSHEE_PLAYER (player));
@@ -153,6 +161,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
 // ---------------------------------------------------------------------------
diff --git a/libbanshee/banshee-player.c b/libbanshee/banshee-player.c
index 677fb16..2ff8038 100644
--- a/libbanshee/banshee-player.c
+++ b/libbanshee/banshee-player.c
@@ -126,12 +126,18 @@ bp_new ()
     
     _bp_replaygain_init (player);
     
+    return player;
+}
+
+P_INVOKE gboolean
+bp_initialize_pipeline (BansheePlayer *player)
+{
     if (!_bp_pipeline_construct (player)) {
         bp_destroy (player);
-        return NULL;
+        return FALSE;
     }
     
-    return player;
+    return TRUE;
 }
 
 P_INVOKE gboolean
diff --git a/libbanshee/clutter-gst-shaders.h b/libbanshee/clutter-gst-shaders.h
new file mode 100644
index 0000000..a570956
--- /dev/null
+++ b/libbanshee/clutter-gst-shaders.h
@@ -0,0 +1,43 @@
+
+#ifndef CLUTTER_GST_SHADERS_H
+#define CLUTTER_GST_SHADERS_H
+
+#include <clutter/clutter.h>
+
+/* Copied from test-shaders */
+
+/* These variables are used instead of the standard GLSL variables on
+   GLES 2 */
+#ifdef HAVE_COGL_GLES2
+
+#define GLES2_VARS              \
+  "precision mediump float;\n"  \
+  "varying vec2 tex_coord;\n"   \
+  "varying vec4 frag_color;\n"
+#define TEX_COORD "tex_coord"
+#define COLOR_VAR "frag_color"
+
+#else /* HAVE_COGL_GLES2 */
+
+#define GLES2_VARS ""
+#define TEX_COORD "gl_TexCoord[0]"
+#define COLOR_VAR "gl_Color"
+
+#endif /* HAVE_COGL_GLES2 */
+
+/* a couple of boilerplate defines that are common amongst all the
+ * sample shaders
+ */
+
+#define FRAGMENT_SHADER_VARS      \
+  GLES2_VARS
+
+/* FRAGMENT_SHADER_END: apply the changed color to the output buffer correctly
+ * blended with the gl specified color (makes the opacity of actors work
+ * correctly).
+ */
+#define FRAGMENT_SHADER_END                             \
+     "  gl_FragColor = gl_FragColor * " COLOR_VAR ";"
+
+#endif
+
diff --git a/libbanshee/clutter-gst-video-sink.c b/libbanshee/clutter-gst-video-sink.c
new file mode 100644
index 0000000..58a80f0
--- /dev/null
+++ b/libbanshee/clutter-gst-video-sink.c
@@ -0,0 +1,787 @@
+/*
+ * Clutter-GStreamer.
+ *
+ * GStreamer integration library for Clutter.
+ *
+ * clutter-gst-video-sink.h - Gstreamer Video Sink that renders to a
+ *                            Clutter Texture.
+ *
+ * Authored by Jonathan Matthew  <jonathan kaolin wh9 net>,
+ *             Chris Lord        <chris openedhand com>
+ *
+ * Copyright (C) 2007,2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:clutter-gst-video-sink
+ * @short_description: GStreamer video sink
+ *
+ * #ClutterGstVideoSink is a GStreamer sink element that sends
+ * data to a #ClutterTexture.
+ */
+
+#include "config.h"
+
+#include "clutter-gst-video-sink.h"
+#include "clutter-gst-shaders.h"
+
+#include <gst/gst.h>
+#include <gst/gstvalue.h>
+#include <gst/video/video.h>
+#include <gst/riff/riff-ids.h>
+
+#include <glib.h>
+#include <string.h>
+
+static gchar *ayuv_to_rgba_shader = \
+     FRAGMENT_SHADER_VARS
+     "uniform sampler2D tex;"
+     "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;"
+     "  color.g = y - 0.390625 * u - 0.8125 * v;"
+     "  color.b = y + 2.015625 * u;"
+     "  gl_FragColor = color;"
+     FRAGMENT_SHADER_END
+     "}";
+
+static gchar *dummy_shader = \
+     FRAGMENT_SHADER_VARS
+     "void main () {"
+     "}";
+
+static gchar *yv12_to_rgba_shader = \
+     FRAGMENT_SHADER_VARS
+     "uniform sampler2D ytex;"
+     "uniform sampler2D utex;"
+     "uniform sampler2D vtex;"
+     "void main () {"
+     "  vec2 coord = vec2(" TEX_COORD ");"
+     "  float y = 1.1640625 * (texture2D (ytex, coord).g - 0.0625);"
+     "  float u = texture2D (utex, coord).g - 0.5;"
+     "  float v = texture2D (vtex, coord).g - 0.5;"
+     "  vec4 color;"
+     "  color.r = y + 1.59765625 * v;"
+     "  color.g = y - 0.390625 * u - 0.8125 * v;"
+     "  color.b = y + 2.015625 * u;"
+     "  color.a = 1.0;"
+     "  gl_FragColor = color;"
+     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_VIDEO_CAPS_BGR));
+
+GST_DEBUG_CATEGORY_STATIC (clutter_gst_video_sink_debug);
+#define GST_CAT_DEFAULT clutter_gst_video_sink_debug
+
+static GstElementDetails clutter_gst_video_sink_details =
+  GST_ELEMENT_DETAILS ("Clutter video sink",
+      "Sink/Video",
+      "Sends video data from a GStreamer pipeline to a Clutter texture",
+      "Jonathan Matthew <jonathan kaolin wh9 net>, "
+      "Matthew Allum <mallum o-hand com, "
+      "Chris Lord <chris o-hand com>");
+
+enum
+{
+  PROP_0,
+  PROP_TEXTURE,
+  PROP_USE_SHADERS,
+};
+
+typedef enum
+{
+  CLUTTER_GST_NOFORMAT,
+  CLUTTER_GST_RGB32,
+  CLUTTER_GST_RGB24,
+  CLUTTER_GST_AYUV,
+  CLUTTER_GST_YV12,
+} ClutterGstVideoFormat;
+
+typedef void (*GLUNIFORM1IPROC)(COGLint location, COGLint value);
+typedef void (*GLACTIVETEXTUREPROC)(GLenum unit);
+
+struct _ClutterGstVideoSinkPrivate
+{
+  ClutterTexture        *texture;
+  CoglHandle             u_tex;
+  CoglHandle             v_tex;
+  CoglHandle             program;
+  CoglHandle             shader;
+  GAsyncQueue           *async_queue;
+  ClutterGstVideoFormat  format;
+  gboolean               bgr;
+  int                    width;
+  int                    height;
+  int                    fps_n, fps_d;
+  int                    par_n, par_d;
+  gboolean               use_shaders;
+  gboolean               shaders_init;
+  
+  GLUNIFORM1IPROC        glUniform1iARB;
+  GLACTIVETEXTUREPROC    glActiveTexture;
+};
+
+
+#define _do_init(bla) \
+  GST_DEBUG_CATEGORY_INIT (clutter_gst_video_sink_debug, \
+                                 "cluttersink", \
+                                 0, \
+                                 "clutter video sink")
+
+GST_BOILERPLATE_FULL (ClutterGstVideoSink,
+                          clutter_gst_video_sink,
+                      GstBaseSink,
+                      GST_TYPE_BASE_SINK,
+                      _do_init);
+
+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->async_queue = g_async_queue_new ();
+
+#ifdef CLUTTER_COGL_HAS_GL
+  priv->glUniform1iARB = (GLUNIFORM1IPROC)
+    cogl_get_proc_address ("glUniform1iARB");
+
+  priv->glActiveTexture = (GLACTIVETEXTUREPROC)
+    cogl_get_proc_address ("glActiveTexture");
+#endif
+}
+
+static void
+clutter_gst_video_sink_paint (ClutterActor        *actor,
+                              ClutterGstVideoSink *sink)
+{
+  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);
+      priv->glActiveTexture (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->glActiveTexture (GL_TEXTURE2);
+      glEnable (GL_TEXTURE_2D);
+      glBindTexture (GL_TEXTURE_2D, texture);
+    }
+  
+  priv->glActiveTexture (GL_TEXTURE0_ARB);
+#endif
+}
+
+static void
+clutter_gst_yv12_post_paint (ClutterActor        *actor,
+                             ClutterGstVideoSink *sink)
+{
+#ifdef CLUTTER_COGL_HAS_GL
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+
+  /* Disable the extra texture units */
+  priv->glActiveTexture (GL_TEXTURE1);
+  glDisable (GL_TEXTURE_2D);
+  priv->glActiveTexture (GL_TEXTURE2);
+  glDisable (GL_TEXTURE_2D);
+  priv->glActiveTexture (GL_TEXTURE0);
+#endif
+}
+
+static void
+clutter_gst_video_sink_set_shader (ClutterGstVideoSink *sink,
+                                   const gchar         *shader_src)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  
+  priv->shaders_init = FALSE;
+  if (priv->texture)
+    clutter_actor_set_shader (CLUTTER_ACTOR (priv->texture), NULL);
+
+  if (priv->program)
+    {
+      cogl_program_unref (priv->program);
+      priv->program = NULL;
+    }
+  
+  if (priv->shader)
+    {
+      cogl_program_unref (priv->shader);
+      priv->shader = NULL;
+    }
+  
+  if (shader_src)
+    {
+      ClutterShader *shader;
+
+      /* Set a dummy shader so we don't interfere with the shader stack */
+      shader = clutter_shader_new ();
+      clutter_shader_set_fragment_source (shader, dummy_shader, -1);
+      clutter_actor_set_shader (CLUTTER_ACTOR (priv->texture), shader);
+      g_object_unref (shader);
+
+      /* Create shader through COGL - necessary as we need to be able to set
+       * integer uniform variables for multi-texturing.
+       */
+      priv->shader = cogl_create_shader (CGL_FRAGMENT_SHADER);
+      cogl_shader_source (priv->shader, shader_src);
+      cogl_shader_compile (priv->shader);
+      
+      priv->program = cogl_create_program ();
+      cogl_program_attach_shader (priv->program, priv->shader);
+      cogl_program_link (priv->program);
+
+      /* Hook onto the pre-paint signal to replace the dummy shader with
+       * the real shader.
+       */
+      g_signal_connect (priv->texture,
+                        "paint",
+                        G_CALLBACK (clutter_gst_video_sink_paint),
+                        sink);
+      
+      priv->shaders_init = TRUE;
+    }
+}
+
+static gboolean
+clutter_gst_video_sink_idle_func (gpointer data)
+{
+  ClutterGstVideoSink        *sink;
+  ClutterGstVideoSinkPrivate *priv;
+  GstBuffer                  *buffer;
+
+  sink = data;
+  priv = sink->priv;
+
+  buffer = g_async_queue_try_pop (priv->async_queue);
+  if (buffer == NULL || G_UNLIKELY (!GST_IS_BUFFER (buffer)))
+    {
+      return FALSE;
+    }
+
+  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);
+
+      /* Initialise AYUV shader */
+      if ((priv->format == CLUTTER_GST_AYUV) && !priv->shaders_init)
+        clutter_gst_video_sink_set_shader (sink,
+                                           ayuv_to_rgba_shader);
+    }
+  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);
+    }
+  else if (priv->format == CLUTTER_GST_YV12)
+    {
+      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 */
+      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);
+          
+          g_signal_connect (priv->texture,
+                            "paint",
+                            G_CALLBACK (clutter_gst_yv12_paint),
+                            sink);
+          g_signal_connect_after (priv->texture,
+                                  "paint",
+                                  G_CALLBACK (clutter_gst_yv12_post_paint),
+                                  sink);
+#endif
+        }
+    }
+
+  gst_buffer_unref (buffer);
+  
+  return FALSE;
+}
+
+static GstFlowReturn
+clutter_gst_video_sink_render (GstBaseSink *bsink,
+                               GstBuffer   *buffer)
+{
+  ClutterGstVideoSink *sink;
+  ClutterGstVideoSinkPrivate *priv;
+
+  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);
+
+  return GST_FLOW_OK;
+}
+
+static GstCaps *
+clutter_gst_video_sink_get_caps (GstBaseSink *sink)
+{
+  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);
+}
+
+static gboolean
+clutter_gst_video_sink_set_caps (GstBaseSink *bsink,
+                                 GstCaps     *caps)
+{
+  ClutterGstVideoSink        *sink;
+  ClutterGstVideoSinkPrivate *priv;
+  GstCaps                    *intersection;
+  GstStructure               *structure;
+  gboolean                    ret;
+  const GValue               *fps;
+  const GValue               *par;
+  gint                        width, height;
+  guint32                     fourcc;
+  int                         red_mask, blue_mask;
+
+  sink = CLUTTER_GST_VIDEO_SINK(bsink);
+  priv = sink->priv;
+
+  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);
+
+  if (gst_caps_is_empty (intersection)) 
+    return FALSE;
+
+  gst_caps_unref (intersection);
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  ret  = gst_structure_get_int (structure, "width", &width);
+  ret &= gst_structure_get_int (structure, "height", &height);
+  fps  = gst_structure_get_value (structure, "framerate");
+  ret &= (fps != NULL);
+
+  par  = gst_structure_get_value (structure, "pixel-aspect-ratio");
+
+  if (!ret)
+    return FALSE;
+
+  priv->width  = width;
+  priv->height = height;
+
+  /* We dont yet use fps or pixel aspect into but handy to have */
+  priv->fps_n  = gst_value_get_fraction_numerator (fps);
+  priv->fps_d  = gst_value_get_fraction_denominator (fps);
+
+  if (par) 
+    {
+      priv->par_n = gst_value_get_fraction_numerator (par);
+      priv->par_d = gst_value_get_fraction_denominator (par);
+    } 
+  else 
+    priv->par_n = priv->par_d = 1;
+
+  ret = gst_structure_get_fourcc (structure, "format", &fourcc);
+  if (ret && (fourcc == GST_RIFF_YV12))
+    {
+      priv->format = CLUTTER_GST_YV12;
+    }
+  else if (ret && (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V')))
+    {
+      priv->format = CLUTTER_GST_AYUV;
+      priv->bgr = FALSE;
+    }
+  else
+    {
+      guint32 width;
+      gst_structure_get_int (structure, "red_mask", &red_mask);
+      gst_structure_get_int (structure, "blue_mask", &blue_mask);
+      
+      width = red_mask | blue_mask;
+      if (width < 0x1000000)
+        {
+          priv->format = CLUTTER_GST_RGB24;
+          priv->bgr = (red_mask == 0xff0000) ? FALSE : TRUE;
+        }
+      else
+        {
+          priv->format = CLUTTER_GST_RGB32;
+          priv->bgr = (red_mask == 0xff000000) ? FALSE : TRUE;
+        }
+    }
+
+  return TRUE;
+}
+
+static void
+clutter_gst_video_sink_dispose (GObject *object)
+{
+  ClutterGstVideoSink *self;
+  ClutterGstVideoSinkPrivate *priv;
+
+  self = CLUTTER_GST_VIDEO_SINK (object);
+  priv = self->priv;
+
+  clutter_gst_video_sink_set_shader (self, NULL);
+
+  if (priv->texture)
+    {
+      g_object_unref (priv->texture);
+      priv->texture = NULL;
+    }
+
+  if (priv->async_queue)
+    {
+      g_async_queue_unref (priv->async_queue);
+      priv->async_queue = NULL;
+    }
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+clutter_gst_video_sink_finalize (GObject *object)
+{
+  ClutterGstVideoSink *self;
+  ClutterGstVideoSinkPrivate *priv;
+
+  self = CLUTTER_GST_VIDEO_SINK (object);
+  priv = self->priv;
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+clutter_gst_video_sink_set_property (GObject *object,
+                                     guint prop_id,
+                                     const GValue *value,
+                                     GParamSpec *pspec)
+{
+  ClutterGstVideoSink *sink;
+  ClutterGstVideoSinkPrivate *priv;
+  gboolean use_shaders;
+
+  sink = CLUTTER_GST_VIDEO_SINK (object);
+  priv = sink->priv;
+
+  switch (prop_id) 
+    {
+    case PROP_TEXTURE:
+      if (priv->texture)
+        g_object_unref (priv->texture);
+
+      priv->texture = CLUTTER_TEXTURE (g_value_dup_object (value));
+      break;
+    case PROP_USE_SHADERS:
+      use_shaders = g_value_get_boolean (value);
+      if (priv->use_shaders != use_shaders)
+        {
+          priv->use_shaders = use_shaders;
+          g_object_notify (object, "use_shaders");
+        }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_gst_video_sink_get_property (GObject *object,
+                                     guint prop_id,
+                                     GValue *value,
+                                     GParamSpec *pspec)
+{
+  ClutterGstVideoSink *sink;
+
+  sink = CLUTTER_GST_VIDEO_SINK (object);
+
+  switch (prop_id) 
+    {
+    case PROP_TEXTURE:
+      g_value_set_object (value, sink->priv->texture);
+      break;
+    case PROP_USE_SHADERS:
+      g_value_set_boolean (value, sink->priv->use_shaders);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+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);
+
+  /* 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;
+}
+
+static void
+clutter_gst_video_sink_class_init (ClutterGstVideoSinkClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstBaseSinkClass *gstbase_sink_class = GST_BASE_SINK_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ClutterGstVideoSinkPrivate));
+
+  gobject_class->set_property = clutter_gst_video_sink_set_property;
+  gobject_class->get_property = clutter_gst_video_sink_get_property;
+
+  gobject_class->dispose = clutter_gst_video_sink_dispose;
+  gobject_class->finalize = clutter_gst_video_sink_finalize;
+
+  gstbase_sink_class->render = clutter_gst_video_sink_render;
+  gstbase_sink_class->preroll = clutter_gst_video_sink_render;
+  gstbase_sink_class->stop = clutter_gst_video_sink_stop;
+  gstbase_sink_class->set_caps = clutter_gst_video_sink_set_caps;
+  gstbase_sink_class->get_caps = clutter_gst_video_sink_get_caps;
+
+  g_object_class_install_property 
+              (gobject_class, PROP_TEXTURE,
+               g_param_spec_object ("texture",
+                                    "texture",
+                                    "Target ClutterTexture object",
+                                    CLUTTER_TYPE_TEXTURE,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property 
+              (gobject_class, PROP_USE_SHADERS,
+               g_param_spec_boolean ("use_shaders",
+                                     "Use shaders",
+                                     "Use a fragment shader to accelerate "
+                                     "colour-space conversion.",
+                                     FALSE,
+                                     G_PARAM_READWRITE));
+}
+
+/**
+ * clutter_gst_video_sink_new:
+ * @texture: a #ClutterTexture
+ *
+ * Creates a new GStreamer video sink which uses @texture as the target
+ * for sinking a video stream from GStreamer.
+ *
+ * Return value: a #GstElement for the newly created video sink
+ */
+GstElement *
+clutter_gst_video_sink_new (ClutterTexture *texture)
+{
+  return g_object_new (CLUTTER_GST_TYPE_VIDEO_SINK,
+                       "texture", texture,
+                       NULL);
+}
+
+static gboolean
+plugin_init (GstPlugin *plugin)
+{
+  gboolean ret = gst_element_register (plugin,
+                                             "cluttersink",
+                                       GST_RANK_PRIMARY,
+                                       CLUTTER_GST_TYPE_VIDEO_SINK);
+  return ret;
+}
+
+GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
+                          GST_VERSION_MINOR,
+                          "cluttersink",
+                          "Element to render to Clutter textures",
+                          plugin_init,
+                          VERSION,
+                          "LGPL", /* license */
+                          PACKAGE,
+                          "");
diff --git a/libbanshee/libbanshee.cproj b/libbanshee/libbanshee.cproj
index 7c88f33..2749c1b 100644
--- a/libbanshee/libbanshee.cproj
+++ b/libbanshee/libbanshee.cproj
@@ -5,9 +5,6 @@
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProductVersion>8.0.50727</ProductVersion>
     <ProjectGuid>{6B781836-AB65-49EF-BECD-CCC193C5D589}</ProjectGuid>
-    <Packages>
-      <Packages />
-    </Packages>
     <Compiler>
       <Compiler ctype="GccCompiler" />
     </Compiler>
@@ -56,6 +53,9 @@
     <None Include="banshee-player-equalizer.h" />
     <None Include="banshee-player-replaygain.h" />
     <None Include="banshee-player-vis.h" />
+    <None Include="clutter-gst-shaders.h" />
+    <None Include="clutter-gst-video-sink.c" />
+    <None Include="clutter-gst-video-sink.h" />
   </ItemGroup>
   <ProjectExtensions>
     <MonoDevelop>
diff --git a/src/Backends/Banshee.GStreamer/Banshee.GStreamer.Engine/PlayerEngine.cs b/src/Backends/Banshee.GStreamer/Banshee.GStreamer.Engine/PlayerEngine.cs
new file mode 100644
index 0000000..f7fca27
--- /dev/null
+++ b/src/Backends/Banshee.GStreamer/Banshee.GStreamer.Engine/PlayerEngine.cs
@@ -0,0 +1,38 @@
+// 
+// PlayerEngine.cs
+//  
+// Author:
+//       Aaron Bockover <abockover novell com>
+// 
+// Copyright 2009 Aaron Bockover
+// 
+// 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;
+using System.Runtime.InteropServices;
+
+namespace Banshee.GStreamer.Engine
+{
+    public class PlayerEngine
+    {
+        public PlayerEngine ()
+        {
+        }
+    }
+}
diff --git a/src/Backends/Banshee.GStreamer/Banshee.GStreamer.csproj b/src/Backends/Banshee.GStreamer/Banshee.GStreamer.csproj
index 54a005d..af3c219 100644
--- a/src/Backends/Banshee.GStreamer/Banshee.GStreamer.csproj
+++ b/src/Backends/Banshee.GStreamer/Banshee.GStreamer.csproj
@@ -18,7 +18,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
-    <StartupObject />
     <AssemblyKeyFile>.</AssemblyKeyFile>
     <CustomCommands>
       <CustomCommands>
@@ -65,7 +64,6 @@
   <ProjectExtensions>
     <MonoDevelop>
       <Properties>
-        <GtkDesignInfo />
         <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="Makefile.am">
           <BuildFilesVar Sync="true" Name="SOURCES" />
           <DeployFilesVar />
diff --git a/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs b/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
index 920d07d..a27b8e2 100644
--- a/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
+++ b/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
@@ -57,6 +57,7 @@ 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);
     
@@ -68,6 +69,9 @@ namespace Banshee.GStreamer
         private uint GST_STREAM_ERROR = 0; 
     
         private HandleRef handle;
+        private IntPtr clutter_video_sink;
+        private IntPtr clutter_video_texture;
+        bool clutter_video_sink_enabled;
         
         private BansheePlayerEosCallback eos_callback;
         private BansheePlayerErrorCallback error_callback;
@@ -75,6 +79,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 +147,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,6 +156,12 @@ 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)) {
+                handle = new HandleRef (this, IntPtr.Zero);
+                throw new ApplicationException (Catalog.GetString ("Could not initialize GStreamer library"));
+            }
             
             OnStateChanged (PlayerState.Ready);
             
@@ -159,8 +171,18 @@ namespace Banshee.GStreamer
             
             InstallPreferences ();
             ReplayGainEnabled = ReplayGainEnabledSchema.Get ();
-
-            bp_set_vis_data_callback (handle, vis_data_callback);
+        }
+        
+        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 override void Dispose ()
@@ -214,6 +236,17 @@ namespace Banshee.GStreamer
         {
             bp_video_window_expose (handle, window, direct);
         }
+        
+        private IntPtr OnVideoPipelineSetup (IntPtr player, IntPtr bus)
+        {
+            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;
+            }
+            
+            return clutter_video_sink;
+        }
 
         public override IntPtr [] GetBaseElements ()
         {
@@ -540,6 +573,9 @@ namespace Banshee.GStreamer
         private static extern void bp_destroy (HandleRef player);
         
         [DllImport ("libbanshee")]
+        private static extern bool bp_initialize_pipeline (HandleRef player);
+        
+        [DllImport ("libbanshee")]
         private static extern void bp_set_eos_callback (HandleRef player, BansheePlayerEosCallback cb);
         
         [DllImport ("libbanshee")]
@@ -559,6 +595,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,
@@ -638,5 +678,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);
     }
 }



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