[banshee/stable-vis] Synced up latest Clutter video sink
- From: Aaron Bockover <abock src gnome org>
- To: svn-commits-list gnome org
- Subject: [banshee/stable-vis] Synced up latest Clutter video sink
- Date: Fri, 8 May 2009 20:08:51 -0400 (EDT)
commit f517f29ca9375df45715e0d57525c88a0832c85c
Author: Aaron Bockover <abockover novell com>
Date: Fri May 8 20:10:45 2009 -0400
Synced up latest Clutter video sink
---
configure.ac | 4 +-
libbanshee/Makefile.am | 1 +
libbanshee/clutter-gst-video-sink.c | 980 ++++++++++++++++++++++++++---------
libbanshee/shaders/I420.h | 36 ++
libbanshee/shaders/YV12.h | 36 ++
5 files changed, 821 insertions(+), 236 deletions(-)
diff --git a/configure.ac b/configure.ac
index 61a8b9c..726e7b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
dnl Automake setup
-AC_INIT([banshee-1], [1.4.4beta1])
-DISPLAY_VERSION="1.4.4 Beta 1"
+AC_INIT([banshee-1], [1.4.4beta2])
+DISPLAY_VERSION="1.4.4 Beta 2"
API_VERSION="1.4"
ASM_VERSION="$API_VERSION.0.0"
AC_SUBST(API_VERSION)
diff --git a/libbanshee/Makefile.am b/libbanshee/Makefile.am
index 4864c79..bad4ae1 100644
--- a/libbanshee/Makefile.am
+++ b/libbanshee/Makefile.am
@@ -1,5 +1,6 @@
INCLUDES = \
-I$(top_srcdir) \
+ -I$(srcdir)/shaders \
-Wall \
-ggdb3 \
-D_FORTIFY_SOURCE=2 \
diff --git a/libbanshee/clutter-gst-video-sink.c b/libbanshee/clutter-gst-video-sink.c
index 58a80f0..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>
@@ -54,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;"
@@ -88,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);
@@ -156,10 +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,8 +206,12 @@ struct _ClutterGstVideoSinkPrivate
gboolean use_shaders;
gboolean shaders_init;
+ ClutterGstSymbols syms; /* extra OpenGL functions */
GLUNIFORM1IPROC glUniform1iARB;
- GLACTIVETEXTUREPROC glActiveTexture;
+
+ GSList *renderers;
+ GstCaps *caps;
+ ClutterGstRenderer *renderer;
};
@@ -195,92 +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);
-
- priv->async_queue = g_async_queue_new ();
+ ClutterGstVideoSinkPrivate *priv = sink->priv;
-#ifdef CLUTTER_COGL_HAS_GL
- priv->glUniform1iARB = (GLUNIFORM1IPROC)
- cogl_get_proc_address ("glUniform1iARB");
+ glEnable (GL_FRAGMENT_PROGRAM_ARB);
+ priv->syms.glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, priv->fp);
- priv->glActiveTexture = (GLACTIVETEXTUREPROC)
- cogl_get_proc_address ("glActiveTexture");
-#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);
- priv->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);
- 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)
+clutter_gst_video_sink_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
+ if (priv->program)
+ cogl_program_use (priv->program);
}
static void
@@ -338,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)
{
@@ -348,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);
@@ -465,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)
@@ -475,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
@@ -516,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;
@@ -559,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);
@@ -588,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;
}
@@ -602,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);
@@ -626,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);
}
@@ -655,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:
@@ -692,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;
}
@@ -741,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));
}
@@ -784,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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]