[banshee] Forward-ported Clutter support from stable-vis
- From: Aaron Bockover <abock src gnome org>
- To: svn-commits-list gnome org
- Subject: [banshee] Forward-ported Clutter support from stable-vis
- Date: Fri, 15 May 2009 18:04:20 -0400 (EDT)
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]