[Cogl] [PATCH 01/15] Include CoglGst
Lionel Landwerlin
llandwerlin at gmail.com
Sat Jun 22 01:58:45 PDT 2013
From: Plamena Manolova <plamena.n.manolova at intel.com>
CoglGst is a GStreamer integration library that facilitates
video playback using the Cogl API. It works by retrieving
each video frame from the GStreamer pipeline and attaching
it to a Cogl pipeline in the form of a Cogl texture along
with possible color model conversion shaders. The pipeline
is then retrieved by the user during each draw. An example
use of the CoglGst API is included in the examples directory.
Reviewed-by: Neil Roberts <neil at linux.intel.com>
(cherry picked from commit dfb94cf4b0b6b42d6465df362a0c0af780596890)
---
.gitignore | 1 +
Makefile.am | 7 +-
cogl-gst/Makefile.am | 75 +++
cogl-gst/cogl-gst-plugin.c | 62 ++
cogl-gst/cogl-gst-shader-private.h | 48 ++
cogl-gst/cogl-gst-shader.c | 75 +++
cogl-gst/cogl-gst-video-sink.c | 1174 ++++++++++++++++++++++++++++++++++++
cogl-gst/cogl-gst-video-sink.h | 121 ++++
cogl-gst/cogl-gst.h | 38 ++
cogl-gst/cogl-gst.pc.in | 13 +
configure.ac | 54 ++
examples/Makefile.am | 7 +
examples/cogl-basic-video-player.c | 248 ++++++++
13 files changed, 1922 insertions(+), 1 deletion(-)
create mode 100644 cogl-gst/Makefile.am
create mode 100644 cogl-gst/cogl-gst-plugin.c
create mode 100644 cogl-gst/cogl-gst-shader-private.h
create mode 100644 cogl-gst/cogl-gst-shader.c
create mode 100644 cogl-gst/cogl-gst-video-sink.c
create mode 100644 cogl-gst/cogl-gst-video-sink.h
create mode 100644 cogl-gst/cogl-gst.h
create mode 100644 cogl-gst/cogl-gst.pc.in
create mode 100644 examples/cogl-basic-video-player.c
diff --git a/.gitignore b/.gitignore
index 5829a8f..1891acf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,6 +70,7 @@ depcomp
/examples/cogl-x11-foreign
/examples/cogl-x11-tfp
/examples/cogland
+/examples/cogl-basic-video-player
gtk-doc.make
install-sh
libtool
diff --git a/Makefile.am b/Makefile.am
index 7a1eb7b..308e66c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,6 +8,10 @@ if BUILD_COGL_GLES2
SUBDIRS += cogl-gles2
endif
+if BUILD_COGL_GST
+SUBDIRS += cogl-gst
+endif
+
SUBDIRS += examples doc po build
ACLOCAL_AMFLAGS = -I build/autotools ${ACLOCAL_FLAGS}
@@ -32,6 +36,7 @@ DISTCHECK_CONFIGURE_FLAGS = \
--enable-xlib-egl-platform \
--enable-wayland-egl-platform \
--enable-glx \
- --enable-wayland-egl-server
+ --enable-wayland-egl-server \
+ --enable-cogl-gst
include $(top_srcdir)/build/autotools/Makefile.am.release
diff --git a/cogl-gst/Makefile.am b/cogl-gst/Makefile.am
new file mode 100644
index 0000000..02cf725
--- /dev/null
+++ b/cogl-gst/Makefile.am
@@ -0,0 +1,75 @@
+include $(top_srcdir)/build/autotools/Makefile.am.silent
+
+NULL =
+
+CLEANFILES =
+DISTCLEANFILES =
+
+EXTRA_DIST =
+
+source_c = \
+ cogl-gst-shader.c \
+ cogl-gst-video-sink.c \
+ $(NULL)
+
+source_h = \
+ cogl-gst.h \
+ cogl-gst-video-sink.h \
+ $(NULL)
+
+source_h_priv = \
+ cogl-gst-shader-private.h \
+ $(NULL)
+
+lib_LTLIBRARIES = libcogl-gst.la
+
+libcogl_gst_la_SOURCES = $(source_c) $(source_h) $(source_h_priv)
+libcogl_gst_la_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_GST_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) $(MAINTAINER_CFLAGS)
+libcogl_gst_la_LIBADD = $(top_builddir)/cogl/libcogl.la
+libcogl_gst_la_LIBADD += $(COGL_DEP_LIBS) $(COGL_GST_DEP_LIBS) $(COGL_EXTRA_LDFLAGS)
+libcogl_gst_la_LDFLAGS = \
+ -export-dynamic \
+ -export-symbols-regex "^cogl_gst_.*" \
+ -no-undefined \
+ -version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ \
+ -rpath $(libdir)
+
+AM_CPPFLAGS = \
+ -DCOGL_COMPILATION \
+ -DG_LOG_DOMAIN=\"CoglGst\" \
+ -I$(top_srcdir)/cogl \
+ -I$(top_builddir)/cogl \
+ -I$(top_srcdir)/cogl/winsys \
+ -I$(top_srcdir) \
+ -I$(top_builddir)
+
+cogl_gstheadersdir = $(includedir)/cogl/cogl-gst
+cogl_gstheaders_HEADERS = $(source_h)
+
+plugin_source_c = \
+ $(srcdir)/cogl-gst-plugin.c \
+ $(NULL)
+
+libgstcogl_la_SOURCES = \
+ $(plugin_source_c) \
+ $(NULL)
+
+plugin_LTLIBRARIES = libgstcogl.la
+
+libgstcogl_la_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_GST_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS) $(MAINTAINER_CFLAGS)
+libgstcogl_la_LIBADD = \
+ $(top_builddir)/cogl/libcogl.la \
+ $(builddir)/libcogl-gst.la
+libgstcogl_la_LIBADD += $(COGL_DEP_LIBS) $(COGL_GST_DEP_LIBS) $(COGL_EXTRA_LDFLAGS)
+libgstcogl_la_LDFLAGS = \
+ -avoid-version -no-undefined \
+ $(NULL)
+
+
+pc_files = cogl-gst.pc
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = $(pc_files)
+
+EXTRA_DIST += cogl-gst.pc.in
+DISTCLEANFILES += $(pc_files)
diff --git a/cogl-gst/cogl-gst-plugin.c b/cogl-gst/cogl-gst-plugin.c
new file mode 100644
index 0000000..9588146
--- /dev/null
+++ b/cogl-gst/cogl-gst-plugin.c
@@ -0,0 +1,62 @@
+/*
+ * Cogl-GStreamer.
+ *
+ * GStreamer integration library for Cogl.
+ *
+ * cogl-gst-video-sink.c - Gstreamer Video Sink that renders to a
+ * Cogl Pipeline.
+ *
+ * Authored by Jonathan Matthew <jonathan at kaolin.wh9.net>,
+ * Chris Lord <chris at openedhand.com>
+ * Damien Lespiau <damien.lespiau at intel.com>
+ * Matthew Allum <mallum at openedhand.com>
+ * Plamena Manolova <plamena.n.manolova at intel.com>
+ *
+ * Copyright (C) 2007, 2008 OpenedHand
+ * Copyright (C) 2009, 2010, 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+#include "cogl-gst-video-sink.h"
+
+#define PACKAGE "CoglGst"
+#define VERSION "0.0"
+
+static CoglBool
+_plugin_init (GstPlugin * coglgstvideosink)
+{
+ return gst_element_register (coglgstvideosink, "coglsink", GST_RANK_PRIMARY,
+ COGL_GST_TYPE_VIDEO_SINK);
+}
+
+GST_PLUGIN_DEFINE (
+ GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ cogl,
+ "Sends video data from GStreamer to a Cogl pipeline",
+ _plugin_init,
+ VERSION,
+ "LGPL",
+ "CoglGst",
+ "http://gstreamer.net/"
+)
diff --git a/cogl-gst/cogl-gst-shader-private.h b/cogl-gst/cogl-gst-shader-private.h
new file mode 100644
index 0000000..bdf7164
--- /dev/null
+++ b/cogl-gst/cogl-gst-shader-private.h
@@ -0,0 +1,48 @@
+/*
+ * Cogl-GStreamer.
+ *
+ * GStreamer integration library for Cogl.
+ *
+ * cogl-gst-video-sink-private.h - Miscellaneous video sink functions
+ *
+ * Authored by Jonathan Matthew <jonathan at kaolin.wh9.net>,
+ * Chris Lord <chris at openedhand.com>
+ * Damien Lespiau <damien.lespiau at intel.com>
+ * Matthew Allum <mallum at openedhand.com>
+ * Plamena Manolova <plamena.n.manolova at intel.com>
+ *
+ * Copyright (C) 2007, 2008 OpenedHand
+ * Copyright (C) 2009, 2010, 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef __COGL_GST_SHADER_PRIVATE_H__
+#define __COGL_GST_SHADER_PRIVATE_H__
+
+extern const char
+_cogl_gst_shader_rgba_to_rgba_decl[];
+
+extern const char
+_cogl_gst_shader_yv12_to_rgba_decl[];
+
+extern const char
+_cogl_gst_shader_ayuv_to_rgba_decl[];
+
+extern const char
+_cogl_gst_shader_default_sample[];
+
+#endif /* __COGL_GST_SHADER_PRIVATE_H__ */
diff --git a/cogl-gst/cogl-gst-shader.c b/cogl-gst/cogl-gst-shader.c
new file mode 100644
index 0000000..bd765bc
--- /dev/null
+++ b/cogl-gst/cogl-gst-shader.c
@@ -0,0 +1,75 @@
+/*
+ * Cogl-GStreamer.
+ *
+ * GStreamer integration library for Cogl.
+ *
+ * cogl-gst-video-sink-private.h - Miscellaneous video sink functions
+ *
+ * Authored by Jonathan Matthew <jonathan at kaolin.wh9.net>,
+ * Chris Lord <chris at openedhand.com>
+ * Damien Lespiau <damien.lespiau at intel.com>
+ * Matthew Allum <mallum at openedhand.com>
+ * Plamena Manolova <plamena.n.manolova at intel.com>
+ *
+ * Copyright (C) 2007, 2008 OpenedHand
+ * Copyright (C) 2009, 2010, 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-gst-shader-private.h"
+
+const char
+_cogl_gst_shader_rgba_to_rgba_decl[] =
+ "vec4 cogl_gst_sample_video (vec2 UV) {\n"
+ " return texture2D (cogl_sampler0, UV);\n"
+ "}";
+
+const char
+_cogl_gst_shader_yv12_to_rgba_decl[] =
+ "vec4 cogl_gst_sample_video (vec2 UV) {\n"
+ " float y = 1.1640625 * (texture2D (cogl_sampler0, UV).g - 0.0625);\n"
+ " float u = texture2D (cogl_sampler1, UV).g - 0.5;\n"
+ " float v = texture2D (cogl_sampler2, UV).g - 0.5;\n"
+ " vec4 color;\n"
+ " color.r = y + 1.59765625 * v;\n"
+ " color.g = y - 0.390625 * u - 0.8125 * v;\n"
+ " color.b = y + 2.015625 * u;\n"
+ " color.a = 1.0;\n"
+ " return color;\n"
+ "}";
+
+const char
+_cogl_gst_shader_ayuv_to_rgba_decl[] =
+ "vec4 cogl_gst_sample_video (vec2 UV) {\n"
+ " vec4 color = texture2D (cogl_sampler0, UV);\n"
+ " float y = 1.1640625 * (color.g - 0.0625);\n"
+ " float u = color.b - 0.5;\n"
+ " float v = color.a - 0.5;\n"
+ " color.a = color.r;\n"
+ " color.r = y + 1.59765625 * v;\n"
+ " color.g = y - 0.390625 * u - 0.8125 * v;\n"
+ " color.b = y + 2.015625 * u;\n"
+ " return color;\n"
+ "}";
+
+const char
+_cogl_gst_shader_default_sample[] =
+ "cogl_layer = cogl_gst_sample_video (cogl_tex_coord0_in.st);";
diff --git a/cogl-gst/cogl-gst-video-sink.c b/cogl-gst/cogl-gst-video-sink.c
new file mode 100644
index 0000000..a0786f0
--- /dev/null
+++ b/cogl-gst/cogl-gst-video-sink.c
@@ -0,0 +1,1174 @@
+/*
+ * Cogl-GStreamer.
+ *
+ * GStreamer integration library for Cogl.
+ *
+ * cogl-gst-video-sink.c - Gstreamer Video Sink that renders to a
+ * Cogl Pipeline.
+ *
+ * Authored by Jonathan Matthew <jonathan at kaolin.wh9.net>,
+ * Chris Lord <chris at openedhand.com>
+ * Damien Lespiau <damien.lespiau at intel.com>
+ * Matthew Allum <mallum at openedhand.com>
+ * Plamena Manolova <plamena.n.manolova at intel.com>
+ *
+ * Copyright (C) 2007, 2008 OpenedHand
+ * Copyright (C) 2009, 2010, 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/gstvalue.h>
+#include <gst/video/video.h>
+#include <gst/riff/riff-ids.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "cogl-gst-video-sink.h"
+#include "cogl-gst-shader-private.h"
+
+#define COGL_GST_TEXTURE_FLAGS \
+ (COGL_TEXTURE_NO_SLICING | COGL_TEXTURE_NO_ATLAS)
+#define COGL_GST_DEFAULT_PRIORITY (G_PRIORITY_HIGH_IDLE)
+#ifdef HAVE_HW_DECODER_SUPPORT
+#define GST_USE_UNSTABLE_API 1
+#include <gst/video/gstsurfacemeta.h>
+#endif
+
+#define BASE_SINK_CAPS "{ AYUV," \
+ "YV12," \
+ "I420," \
+ "RGBA," \
+ "BGRA," \
+ "RGB," \
+ "BGR }"
+
+
+#define SINK_CAPS GST_VIDEO_CAPS_MAKE(BASE_SINK_CAPS)
+
+static GstStaticPadTemplate sinktemplate_all = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SINK_CAPS));
+
+#define cogl_gst_video_sink_parent_class parent_class
+G_DEFINE_TYPE (CoglGstVideoSink, cogl_gst_video_sink, GST_TYPE_BASE_SINK);
+
+enum
+{
+ PROP_0,
+ PROP_UPDATE_PRIORITY
+};
+
+enum
+{
+ PIPELINE_READY_SIGNAL,
+ NEW_FRAME_SIGNAL,
+
+ LAST_SIGNAL
+};
+
+static guint video_sink_signals[LAST_SIGNAL] = { 0, };
+
+typedef enum
+{
+ COGL_GST_NOFORMAT,
+ COGL_GST_RGB32,
+ COGL_GST_RGB24,
+ COGL_GST_AYUV,
+ COGL_GST_YV12,
+ COGL_GST_SURFACE,
+ COGL_GST_I420
+}CoglGstVideoFormat;
+
+typedef enum
+{
+ COGL_GST_RENDERER_NEEDS_GLSL = (1 << 0)
+} CoglGstRendererFlag;
+
+typedef struct _CoglGstSource
+{
+ GSource source;
+ CoglGstVideoSink *sink;
+ GMutex buffer_lock;
+ GstBuffer *buffer;
+ CoglBool has_new_caps;
+}CoglGstSource;
+
+typedef void (CoglGstRendererPaint) (CoglGstVideoSink*);
+typedef void (CoglGstRendererPostPaint) (CoglGstVideoSink*);
+
+typedef struct _CoglGstRenderer
+{
+ const char *name;
+ CoglGstVideoFormat format;
+ int flags;
+ GstStaticCaps caps;
+ void (*init) (CoglGstVideoSink *sink);
+ void (*deinit) (CoglGstVideoSink *sink);
+ CoglBool (*upload) (CoglGstVideoSink *sink,
+ GstBuffer *buffer);
+}CoglGstRenderer;
+
+struct _CoglGstVideoSinkPrivate
+{
+ CoglContext *ctx;
+ CoglPipeline *pipeline;
+ CoglTexture *frame[3];
+ CoglBool frame_dirty;
+ CoglGstVideoFormat format;
+ CoglBool bgr;
+ CoglGstSource *source;
+ GSList *renderers;
+ GstCaps *caps;
+ CoglGstRenderer *renderer;
+ GstFlowReturn flow_return;
+ int free_layer;
+ GstVideoInfo info;
+ #ifdef HAVE_HW_DECODER_SUPPORT
+ GstSurfaceConverter *converter;
+ #endif
+};
+
+static void
+cogl_gst_source_finalize (GSource *source)
+{
+ CoglGstSource *gst_source = (CoglGstSource*) source;
+
+ g_mutex_lock (&gst_source->buffer_lock);
+ if (gst_source->buffer)
+ gst_buffer_unref (gst_source->buffer);
+ gst_source->buffer = NULL;
+ g_mutex_unlock (&gst_source->buffer_lock);
+ g_mutex_clear (&gst_source->buffer_lock);
+}
+
+int
+cogl_gst_video_sink_get_free_layer (CoglGstVideoSink* sink)
+{
+ return sink->priv->free_layer;
+}
+
+int
+cogl_gst_video_sink_attach_frame (CoglGstVideoSink *sink,
+ CoglPipeline *pln)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (priv->frame); i++)
+ if (priv->frame[i] != NULL)
+ cogl_pipeline_set_layer_texture (pln, i, priv->frame[i]);
+
+ return priv->free_layer;
+}
+
+static CoglBool
+cogl_gst_source_prepare (GSource *source,
+ int *timeout)
+{
+ CoglGstSource *gst_source = (CoglGstSource*) source;
+
+ *timeout = -1;
+
+ return gst_source->buffer != NULL;
+}
+
+static CoglBool
+cogl_gst_source_check (GSource *source)
+{
+ CoglGstSource *gst_source = (CoglGstSource*) source;
+
+ return gst_source->buffer != NULL;
+}
+
+static void
+cogl_gst_video_sink_set_priority (CoglGstVideoSink *sink,
+ int priority)
+{
+ if (sink->priv->source)
+ g_source_set_priority ((GSource*) sink->priv->source, priority);
+}
+
+/* We want to cache the snippets instead of recreating a new one every
+ * time we initialise a pipeline so that if we end up recreating the
+ * same pipeline again then Cogl will be able to use the pipeline
+ * cache to avoid linking a redundant identical shader program */
+typedef struct
+{
+ CoglSnippet *vertex_snippet;
+ CoglSnippet *fragment_snippet;
+} SnippetCache;
+
+static void
+create_template_pipeline (CoglGstVideoSink *sink,
+ const char *decl,
+ SnippetCache *snippet_cache,
+ int n_layers)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ priv->free_layer = n_layers;
+
+ if (priv->pipeline)
+ cogl_object_unref (priv->pipeline);
+ priv->pipeline = cogl_pipeline_new (priv->ctx);
+
+ if (decl)
+ {
+ static CoglSnippet *default_sample_snippet = NULL;
+
+ /* The global sampling function gets added to both the fragment
+ * and vertex stages. The hope is that the GLSL compiler will
+ * easily remove the dead code if it's not actually used */
+ if (snippet_cache->vertex_snippet == NULL)
+ snippet_cache->vertex_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS,
+ decl,
+ NULL /* post */);
+ cogl_pipeline_add_snippet (priv->pipeline,
+ snippet_cache->vertex_snippet);
+
+ if (snippet_cache->fragment_snippet == NULL)
+ snippet_cache->fragment_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS,
+ decl,
+ NULL /* post */);
+ cogl_pipeline_add_snippet (priv->pipeline,
+ snippet_cache->fragment_snippet);
+
+ /* Set the replace string for the last layer so that no
+ * redundant code for the previous layers will be generated. The
+ * application can also replace this default sampling by adding
+ * another layer */
+ if (default_sample_snippet == NULL)
+ {
+ default_sample_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, NULL, NULL);
+ cogl_snippet_set_replace (default_sample_snippet,
+ _cogl_gst_shader_default_sample);
+ }
+ cogl_pipeline_add_layer_snippet (priv->pipeline,
+ n_layers - 1,
+ default_sample_snippet);
+ }
+
+ priv->frame_dirty = TRUE;
+
+ g_signal_emit (sink, video_sink_signals[PIPELINE_READY_SIGNAL], 0, NULL);
+}
+
+CoglPipeline*
+cogl_gst_video_sink_get_pipeline (CoglGstVideoSink *vt)
+{
+ CoglGstVideoSinkPrivate *priv = vt->priv;
+
+ if (priv->frame_dirty)
+ {
+ CoglPipeline *pipeline = cogl_pipeline_copy (priv->pipeline);
+ cogl_object_unref (priv->pipeline);
+ priv->pipeline = pipeline;
+
+ cogl_gst_video_sink_attach_frame (vt, pipeline);
+
+ priv->frame_dirty = FALSE;
+ }
+
+ return vt->priv->pipeline;
+}
+
+static void
+cogl_gst_dummy_deinit (CoglGstVideoSink *sink)
+{
+}
+
+static void
+clear_frame_textures (CoglGstVideoSink *sink)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (priv->frame); i++)
+ {
+ if (priv->frame[i] == NULL)
+ break;
+ else
+ cogl_object_unref (priv->frame[i]);
+ }
+
+ memset (priv->frame, 0, sizeof (priv->frame));
+
+ priv->frame_dirty = TRUE;
+}
+
+static void
+cogl_gst_rgb_init (CoglGstVideoSink *sink)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+
+ if (cogl_has_feature (priv->ctx, COGL_FEATURE_ID_GLSL))
+ {
+ static SnippetCache snippet_cache;
+
+ create_template_pipeline (sink,
+ _cogl_gst_shader_rgba_to_rgba_decl,
+ &snippet_cache,
+ 1);
+ }
+ else
+ create_template_pipeline (sink, NULL, NULL, 1);
+}
+
+static CoglBool
+cogl_gst_rgb24_upload (CoglGstVideoSink *sink,
+ GstBuffer *buffer)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ CoglPixelFormat format;
+ GstVideoFrame frame;
+
+ if (priv->bgr)
+ format = COGL_PIXEL_FORMAT_BGR_888;
+ else
+ format = COGL_PIXEL_FORMAT_RGB_888;
+
+ if (!gst_video_frame_map (&frame, &priv->info, buffer, GST_MAP_READ))
+ goto map_fail;
+
+ clear_frame_textures (sink);
+
+ priv->frame[0] = cogl_texture_new_from_data (priv->info.width,
+ priv->info.height,
+ COGL_GST_TEXTURE_FLAGS,
+ format, format,
+ priv->info.stride[0],
+ frame.data[0]);
+
+ gst_video_frame_unmap (&frame);
+
+ return TRUE;
+
+map_fail:
+ {
+ GST_ERROR_OBJECT (sink, "Could not map incoming video frame");
+ return FALSE;
+ }
+}
+
+
+static CoglGstRenderer rgb24_renderer =
+{
+ "RGB 24",
+ COGL_GST_RGB24,
+ 0,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGB, BGR }")),
+ cogl_gst_rgb_init,
+ cogl_gst_dummy_deinit,
+ cogl_gst_rgb24_upload,
+};
+
+static CoglBool
+cogl_gst_rgb32_upload (CoglGstVideoSink *sink,
+ GstBuffer *buffer)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ CoglPixelFormat format;
+ GstVideoFrame frame;
+
+ if (priv->bgr)
+ format = COGL_PIXEL_FORMAT_BGRA_8888;
+ else
+ format = COGL_PIXEL_FORMAT_RGBA_8888;
+
+ if (!gst_video_frame_map (&frame, &priv->info, buffer, GST_MAP_READ))
+ goto map_fail;
+
+ clear_frame_textures (sink);
+
+ priv->frame[0] = cogl_texture_new_from_data (priv->info.width,
+ priv->info.height,
+ COGL_GST_TEXTURE_FLAGS,
+ format, format,
+ priv->info.stride[0],
+ frame.data[0]);
+
+ gst_video_frame_unmap (&frame);
+
+ return TRUE;
+
+map_fail:
+ {
+ GST_ERROR_OBJECT (sink, "Could not map incoming video frame");
+ return FALSE;
+ }
+}
+
+static CoglGstRenderer rgb32_renderer =
+{
+ "RGB 32",
+ COGL_GST_RGB32,
+ 0,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGBA, BGRA }")),
+ cogl_gst_rgb_init,
+ cogl_gst_dummy_deinit,
+ cogl_gst_rgb32_upload,
+};
+
+static CoglBool
+cogl_gst_yv12_upload (CoglGstVideoSink *sink,
+ GstBuffer *buffer)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ GstVideoFrame frame;
+ CoglPixelFormat format = COGL_PIXEL_FORMAT_G_8;
+
+ if (!gst_video_frame_map (&frame, &priv->info, buffer, GST_MAP_READ))
+ goto map_fail;
+
+ clear_frame_textures (sink);
+
+ priv->frame[0] =
+ cogl_texture_new_from_data (GST_VIDEO_INFO_COMP_WIDTH (&priv->info, 0),
+ GST_VIDEO_INFO_COMP_HEIGHT (&priv->info, 0),
+ COGL_GST_TEXTURE_FLAGS, format, format,
+ priv->info.stride[0], frame.data[0]);
+
+ priv->frame[1] =
+ cogl_texture_new_from_data (GST_VIDEO_INFO_COMP_WIDTH (&priv->info, 1),
+ GST_VIDEO_INFO_COMP_HEIGHT (&priv->info, 1),
+ COGL_GST_TEXTURE_FLAGS, format, format,
+ priv->info.stride[1], frame.data[1]);
+
+ priv->frame[2] =
+ cogl_texture_new_from_data (GST_VIDEO_INFO_COMP_WIDTH (&priv->info, 2),
+ GST_VIDEO_INFO_COMP_HEIGHT (&priv->info, 2),
+ COGL_GST_TEXTURE_FLAGS, format, format,
+ priv->info.stride[2], frame.data[2]);
+
+ gst_video_frame_unmap (&frame);
+
+ return TRUE;
+
+map_fail:
+ {
+ GST_ERROR_OBJECT (sink, "Could not map incoming video frame");
+ return FALSE;
+ }
+}
+
+static void
+cogl_gst_yv12_glsl_init (CoglGstVideoSink *sink)
+{
+ static SnippetCache snippet_cache;
+
+ create_template_pipeline (sink,
+ _cogl_gst_shader_yv12_to_rgba_decl,
+ &snippet_cache,
+ 3);
+}
+
+static CoglGstRenderer yv12_glsl_renderer =
+{
+ "YV12 glsl",
+ COGL_GST_YV12,
+ COGL_GST_RENDERER_NEEDS_GLSL,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("YV12")),
+ cogl_gst_yv12_glsl_init,
+ cogl_gst_dummy_deinit,
+ cogl_gst_yv12_upload,
+};
+
+static void
+cogl_gst_i420_glsl_init (CoglGstVideoSink *sink)
+{
+ static SnippetCache snippet_cache;
+
+ create_template_pipeline (sink,
+ _cogl_gst_shader_yv12_to_rgba_decl,
+ &snippet_cache,
+ 3);
+}
+
+static CoglGstRenderer i420_glsl_renderer =
+{
+ "I420 glsl",
+ COGL_GST_I420,
+ COGL_GST_RENDERER_NEEDS_GLSL,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420")),
+ cogl_gst_i420_glsl_init,
+ cogl_gst_dummy_deinit,
+ cogl_gst_yv12_upload,
+};
+
+static void
+cogl_gst_ayuv_glsl_init (CoglGstVideoSink *sink)
+{
+ static SnippetCache snippet_cache;
+
+ create_template_pipeline (sink,
+ _cogl_gst_shader_ayuv_to_rgba_decl,
+ &snippet_cache,
+ 1);
+}
+
+static CoglBool
+cogl_gst_ayuv_upload (CoglGstVideoSink *sink,
+ GstBuffer *buffer)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ CoglPixelFormat format = COGL_PIXEL_FORMAT_RGBA_8888;
+ GstVideoFrame frame;
+
+ if (!gst_video_frame_map (&frame, &priv->info, buffer, GST_MAP_READ))
+ goto map_fail;
+
+ clear_frame_textures (sink);
+
+ priv->frame[0] = cogl_texture_new_from_data (priv->info.width,
+ priv->info.height,
+ COGL_GST_TEXTURE_FLAGS,
+ format, format,
+ priv->info.stride[0],
+ frame.data[0]);
+
+ gst_video_frame_unmap (&frame);
+
+ return TRUE;
+
+map_fail:
+ {
+ GST_ERROR_OBJECT (sink, "Could not map incoming video frame");
+ return FALSE;
+ }
+}
+
+static CoglGstRenderer ayuv_glsl_renderer =
+{
+ "AYUV glsl",
+ COGL_GST_AYUV,
+ COGL_GST_RENDERER_NEEDS_GLSL,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("AYUV")),
+ cogl_gst_ayuv_glsl_init,
+ cogl_gst_dummy_deinit,
+ cogl_gst_ayuv_upload,
+};
+
+#ifdef HAVE_HW_DECODER_SUPPORT
+
+static void
+cogl_gst_hw_init (CoglGstVideoSink *sink)
+{
+ create_template_pipeline (sink, NULL, NULL, 1);
+}
+
+static void
+cogl_gst_hw_deinit (CoglGstVideoSink* sink)
+{
+ if (sink->priv->converter != NULL)
+ g_object_unref (sink->priv->converter);
+ sink->priv->converter = NULL;
+}
+
+static CoglBool
+cogl_gst_hw_upload (CoglGstVideoSink *sink,
+ GstBuffer *buffer)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ GstSurfaceMeta* surface = gst_buffer_get_surface_meta (buffer);
+
+ if (G_UNLIKELY (priv->converter == NULL))
+ {
+ unsigned int gl_tex;
+ unsigned int gl_tar;
+ GValue value = {0};
+
+ clear_frame_textures (sink);
+
+ priv->frame[0] = cogl_texture_new_with_size (priv->ctx, priv->info.width,
+ priv->info.height,
+ COGL_GST_TEXTURE_FLAGS,
+ COGL_PIXEL_FORMAT_BGRA_8888);
+
+ cogl_texture_get_gl_texture (priv->frame[0], &gl_tex, &gl_tar);
+
+ g_value_init (&value, G_TYPE_UINT);
+ g_value_set_uint (&value, gl_tex);
+
+ priv->converter = gst_surface_meta_create_converter (surface, "opengl",
+ &value);
+ }
+ gst_surface_converter_upload (priv->converter, buffer);
+
+ return TRUE;
+}
+
+static CoglGstRenderer hw_renderer = {
+ "HW surface",
+ COGL_GST_SURFACE,
+ 0,
+ GST_STATIC_CAPS ("x-video/surface, opengl=true"),
+ cogl_gst_hw_init,
+ cogl_gst_hw_deinit,
+ cogl_gst_hw_upload,
+};
+
+#endif
+
+static GSList*
+cogl_gst_build_renderers_list (CoglContext *ctx)
+{
+ GSList *list = NULL;
+ CoglBool has_glsl;
+ int i;
+ static CoglGstRenderer *const renderers[] =
+ {
+ &rgb24_renderer,
+ &rgb32_renderer,
+ &yv12_glsl_renderer,
+ &i420_glsl_renderer,
+ &ayuv_glsl_renderer,
+ #ifdef HAVE_HW_DECODER_SUPPORT
+ &hw_renderer,
+ #endif
+ NULL
+ };
+
+ has_glsl = cogl_has_feature (ctx, COGL_FEATURE_ID_GLSL);
+
+ for (i = 0; renderers[i]; i++)
+ if (has_glsl || !(renderers[i]->flags & COGL_GST_RENDERER_NEEDS_GLSL))
+ list = g_slist_prepend (list, renderers[i]);
+
+ return list;
+}
+
+static void
+append_cap (gpointer data,
+ gpointer user_data)
+{
+ CoglGstRenderer *renderer = (CoglGstRenderer*) 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*
+cogl_gst_build_caps (GSList *renderers)
+{
+ GstCaps *caps;
+
+ caps = gst_caps_new_empty ();
+
+ g_slist_foreach (renderers, append_cap, caps);
+
+ return caps;
+}
+
+void
+cogl_gst_video_sink_set_context (CoglGstVideoSink *vt, CoglContext *ctx)
+{
+ CoglGstVideoSinkPrivate *priv = vt->priv;
+
+ if (ctx)
+ ctx = cogl_object_ref (ctx);
+
+ if (priv->ctx)
+ {
+ cogl_object_unref (priv->ctx);
+ g_slist_free (priv->renderers);
+ priv->renderers = NULL;
+ if (priv->caps)
+ {
+ gst_caps_unref (priv->caps);
+ priv->caps = NULL;
+ }
+ }
+
+ if (ctx)
+ {
+ priv->ctx = ctx;
+ priv->renderers = cogl_gst_build_renderers_list (priv->ctx);
+ priv->caps = cogl_gst_build_caps (priv->renderers);
+ }
+}
+
+static CoglGstRenderer*
+cogl_gst_find_renderer_by_format (CoglGstVideoSink *sink,
+ CoglGstVideoFormat format)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ CoglGstRenderer *renderer = NULL;
+ GSList *element;
+
+ for (element = priv->renderers; element; element = g_slist_next (element))
+ {
+ CoglGstRenderer *candidate = (CoglGstRenderer*) element->data;
+ if (candidate->format == format)
+ {
+ renderer = candidate;
+ break;
+ }
+ }
+
+ return renderer;
+}
+
+static GstCaps*
+cogl_gst_video_sink_get_caps (GstBaseSink *bsink,
+ GstCaps *filter)
+{
+ CoglGstVideoSink *sink;
+ sink = COGL_GST_VIDEO_SINK (bsink);
+ return gst_caps_ref (sink->priv->caps);
+}
+
+static CoglBool
+cogl_gst_video_sink_parse_caps (GstCaps *caps,
+ CoglGstVideoSink *sink,
+ CoglBool save)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ GstCaps *intersection;
+ GstVideoInfo vinfo;
+ CoglGstVideoFormat format;
+ CoglBool bgr = FALSE;
+ CoglGstRenderer* renderer;
+
+ intersection = gst_caps_intersect (priv->caps, caps);
+ if (gst_caps_is_empty (intersection))
+ goto no_intersection;
+
+ gst_caps_unref (intersection);
+
+ if (!gst_video_info_from_caps (&vinfo, caps))
+ goto unknown_format;
+
+ switch (vinfo.finfo->format) {
+ case GST_VIDEO_FORMAT_YV12:
+ format = COGL_GST_YV12;
+ break;
+ case GST_VIDEO_FORMAT_I420:
+ format = COGL_GST_I420;
+ break;
+ case GST_VIDEO_FORMAT_AYUV:
+ format = COGL_GST_AYUV;
+ bgr = FALSE;
+ break;
+ case GST_VIDEO_FORMAT_RGB:
+ format = COGL_GST_RGB24;
+ bgr = FALSE;
+ break;
+ case GST_VIDEO_FORMAT_BGR:
+ format = COGL_GST_RGB24;
+ bgr = TRUE;
+ break;
+ case GST_VIDEO_FORMAT_RGBA:
+ format = COGL_GST_RGB32;
+ bgr = FALSE;
+ break;
+ case GST_VIDEO_FORMAT_BGRA:
+ format = COGL_GST_RGB32;
+ bgr = TRUE;
+ break;
+ default:
+ goto unhandled_format;
+ }
+
+ renderer = cogl_gst_find_renderer_by_format (sink, format);
+
+ if (G_UNLIKELY (renderer == NULL))
+ goto no_suitable_renderer;
+
+ GST_INFO_OBJECT (sink, "found the %s renderer", renderer->name);
+
+ if (save) {
+ priv->info = vinfo;
+
+ priv->format = format;
+ priv->bgr = bgr;
+
+ priv->renderer = renderer;
+ }
+
+ return TRUE;
+
+
+no_intersection:
+ {
+ GST_WARNING_OBJECT (sink,
+ "Incompatible caps, don't intersect with %" GST_PTR_FORMAT, priv->caps);
+ return FALSE;
+ }
+
+unknown_format:
+ {
+ GST_WARNING_OBJECT (sink, "Could not figure format of input caps");
+ return FALSE;
+ }
+
+unhandled_format:
+ {
+ GST_ERROR_OBJECT (sink, "Provided caps aren't supported by clutter-gst");
+ return FALSE;
+ }
+
+no_suitable_renderer:
+ {
+ GST_ERROR_OBJECT (sink, "could not find a suitable renderer");
+ return FALSE;
+ }
+}
+
+static CoglBool
+cogl_gst_video_sink_set_caps (GstBaseSink *bsink,
+ GstCaps *caps)
+{
+ CoglGstVideoSink *sink;
+ CoglGstVideoSinkPrivate *priv;
+
+ sink = COGL_GST_VIDEO_SINK (bsink);
+ priv = sink->priv;
+
+ if (!cogl_gst_video_sink_parse_caps (caps, sink, FALSE))
+ return FALSE;
+
+ g_mutex_lock (&priv->source->buffer_lock);
+ priv->source->has_new_caps = TRUE;
+ g_mutex_unlock (&priv->source->buffer_lock);
+
+ return TRUE;
+}
+
+static CoglBool
+cogl_gst_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ void* user_data)
+{
+ CoglGstSource *gst_source= (CoglGstSource*) source;
+ CoglGstVideoSinkPrivate *priv = gst_source->sink->priv;
+ GstBuffer *buffer;
+
+ g_mutex_lock (&gst_source->buffer_lock);
+
+ if (G_UNLIKELY (gst_source->has_new_caps))
+ {
+ GstCaps *caps =
+ gst_pad_get_current_caps (GST_BASE_SINK_PAD ((GST_BASE_SINK
+ (gst_source->sink))));
+
+ if (priv->renderer)
+ priv->renderer->deinit (gst_source->sink);
+
+ if (!cogl_gst_video_sink_parse_caps (caps, gst_source->sink, TRUE))
+ goto negotiation_fail;
+
+ priv->renderer->init (gst_source->sink);
+ gst_source->has_new_caps = FALSE;
+ }
+
+ buffer = gst_source->buffer;
+ gst_source->buffer = NULL;
+
+ g_mutex_unlock (&gst_source->buffer_lock);
+
+ if (buffer)
+ {
+ if (!priv->renderer->upload (gst_source->sink, buffer))
+ goto fail_upload;
+
+ g_signal_emit (gst_source->sink,
+ video_sink_signals[NEW_FRAME_SIGNAL], 0,
+ NULL);
+ gst_buffer_unref (buffer);
+ }
+ else
+ GST_WARNING_OBJECT (gst_source->sink, "No buffers available for display");
+
+ return TRUE;
+
+
+negotiation_fail:
+ {
+ GST_WARNING_OBJECT (gst_source->sink,
+ "Failed to handle caps. Stopping GSource");
+ priv->flow_return = GST_FLOW_NOT_NEGOTIATED;
+ g_mutex_unlock (&gst_source->buffer_lock);
+
+ return FALSE;
+ }
+
+fail_upload:
+ {
+ GST_WARNING_OBJECT (gst_source->sink, "Failed to upload buffer");
+ priv->flow_return = GST_FLOW_ERROR;
+ gst_buffer_unref (buffer);
+ return FALSE;
+ }
+}
+
+static GSourceFuncs gst_source_funcs =
+{
+ cogl_gst_source_prepare,
+ cogl_gst_source_check,
+ cogl_gst_source_dispatch,
+ cogl_gst_source_finalize
+};
+
+static CoglGstSource*
+cogl_gst_source_new (CoglGstVideoSink *sink)
+{
+ GSource *source;
+ CoglGstSource *gst_source;
+
+ source = g_source_new (&gst_source_funcs, sizeof (CoglGstSource));
+ gst_source = (CoglGstSource*) source;
+
+ g_source_set_can_recurse (source, TRUE);
+ g_source_set_priority (source, COGL_GST_DEFAULT_PRIORITY);
+
+ gst_source->sink = sink;
+ g_mutex_init (&gst_source->buffer_lock);
+ gst_source->buffer = NULL;
+
+ return gst_source;
+}
+
+static void
+cogl_gst_video_sink_init (CoglGstVideoSink *sink)
+{
+ CoglGstVideoSinkPrivate* priv;
+
+ sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (sink,
+ COGL_GST_TYPE_VIDEO_SINK,
+ CoglGstVideoSinkPrivate);
+}
+
+static GstFlowReturn
+_cogl_gst_video_sink_render (GstBaseSink *bsink,
+ GstBuffer *buffer)
+{
+ CoglGstVideoSink *sink = COGL_GST_VIDEO_SINK (bsink);
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ CoglGstSource *gst_source = priv->source;
+
+ g_mutex_lock (&gst_source->buffer_lock);
+
+ if (G_UNLIKELY (priv->flow_return != GST_FLOW_OK))
+ goto dispatch_flow_ret;
+
+ if (gst_source->buffer)
+ gst_buffer_unref (gst_source->buffer);
+
+ gst_source->buffer = gst_buffer_ref (buffer);
+ g_mutex_unlock (&gst_source->buffer_lock);
+
+ g_main_context_wakeup (NULL);
+
+ return GST_FLOW_OK;
+
+ dispatch_flow_ret:
+ {
+ g_mutex_unlock (&gst_source->buffer_lock);
+ return priv->flow_return;
+ }
+}
+
+static void
+cogl_gst_video_sink_dispose (GObject *object)
+{
+ CoglGstVideoSink *self;
+ CoglGstVideoSinkPrivate *priv;
+
+ self = COGL_GST_VIDEO_SINK (object);
+ priv = self->priv;
+
+ clear_frame_textures (self);
+
+ if (priv->renderer)
+ {
+ priv->renderer->deinit (self);
+ priv->renderer = NULL;
+ }
+
+ if (priv->pipeline)
+ {
+ cogl_object_unref (priv->pipeline);
+ priv->pipeline = NULL;
+ }
+
+ if (priv->caps)
+ {
+ gst_caps_unref (priv->caps);
+ priv->caps = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->dispose (object);
+}
+
+static void
+cogl_gst_video_sink_finalize (GObject *object)
+{
+ CoglGstVideoSink *self = COGL_GST_VIDEO_SINK (object);
+
+ cogl_gst_video_sink_set_context (self, NULL);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static CoglBool
+cogl_gst_video_sink_start (GstBaseSink *base_sink)
+{
+ CoglGstVideoSink *sink = COGL_GST_VIDEO_SINK (base_sink);
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+
+ priv->source = cogl_gst_source_new (sink);
+ g_source_attach ((GSource*) priv->source, NULL);
+ priv->flow_return = GST_FLOW_OK;
+ return TRUE;
+}
+
+static void
+cogl_gst_video_sink_set_property (GObject *object,
+ unsigned int prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CoglGstVideoSink *sink = COGL_GST_VIDEO_SINK (object);
+
+ switch (prop_id)
+ {
+ case PROP_UPDATE_PRIORITY:
+ cogl_gst_video_sink_set_priority (sink, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cogl_gst_video_sink_get_property (GObject *object,
+ unsigned int prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CoglGstVideoSink *sink = COGL_GST_VIDEO_SINK (object);
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+
+ switch (prop_id)
+ {
+ case PROP_UPDATE_PRIORITY:
+ g_value_set_int (value, g_source_get_priority ((GSource*) priv->source));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static CoglBool
+cogl_gst_video_sink_stop (GstBaseSink *base_sink)
+{
+ CoglGstVideoSink *sink = COGL_GST_VIDEO_SINK (base_sink);
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+
+ if (priv->source)
+ {
+ GSource *source = (GSource*) priv->source;
+ g_source_destroy (source);
+ g_source_unref (source);
+ priv->source = NULL;
+ }
+
+ return TRUE;
+}
+
+static void
+cogl_gst_video_sink_class_init (CoglGstVideoSinkClass *klass)
+{
+ GObjectClass *go_class = G_OBJECT_CLASS (klass);
+ GstBaseSinkClass *gb_class = GST_BASE_SINK_CLASS (klass);
+ GstElementClass *ge_class = GST_ELEMENT_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (CoglGstVideoSinkPrivate));
+ go_class->set_property = cogl_gst_video_sink_set_property;
+ go_class->get_property = cogl_gst_video_sink_get_property;
+ go_class->dispose = cogl_gst_video_sink_dispose;
+ go_class->finalize = cogl_gst_video_sink_finalize;
+
+ gst_element_class_add_pad_template (ge_class,
+ gst_static_pad_template_get (&sinktemplate_all));
+ gst_element_class_set_metadata (ge_class, "Cogl video sink", "Sink/Video",
+ "Sends video data from GStreamer to a Cogl pipeline",
+ "Jonathan Matthew <jonathan at kaolin.wh9.net>, "
+ "Matthew Allum <mallum at o-hand.com, "
+ "Chris Lord <chris at o-hand.com>, "
+ "Plamena Manolova <plamena.n.manolova at intel.com>");
+
+ gb_class->render = _cogl_gst_video_sink_render;
+ gb_class->preroll = _cogl_gst_video_sink_render;
+ gb_class->start = cogl_gst_video_sink_start;
+ gb_class->stop = cogl_gst_video_sink_stop;
+ gb_class->set_caps = cogl_gst_video_sink_set_caps;
+ gb_class->get_caps = cogl_gst_video_sink_get_caps;
+
+ pspec = g_param_spec_int ("update-priority",
+ "Update Priority",
+ "Priority of video updates in the thread",
+ -G_MAXINT, G_MAXINT,
+ COGL_GST_DEFAULT_PRIORITY,
+ COGL_GST_PARAM_READWRITE);
+
+ g_object_class_install_property (go_class, PROP_UPDATE_PRIORITY, pspec);
+
+ video_sink_signals[PIPELINE_READY_SIGNAL] =
+ g_signal_new ("pipeline-ready",
+ COGL_GST_TYPE_VIDEO_SINK,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CoglGstVideoSinkClass, pipeline_ready),
+ NULL, /* accumulator */
+ NULL, /* accu_data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0 /* n_params */);
+
+ video_sink_signals[NEW_FRAME_SIGNAL] =
+ g_signal_new ("new-frame",
+ COGL_GST_TYPE_VIDEO_SINK,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CoglGstVideoSinkClass, new_frame),
+ NULL, /* accumulator */
+ NULL, /* accu_data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0 /* n_params */);
+}
+
+CoglGstVideoSink*
+cogl_gst_video_sink_new (CoglContext *ctx)
+{
+ CoglGstVideoSink* sink = g_object_new (COGL_GST_TYPE_VIDEO_SINK, NULL);
+ cogl_gst_video_sink_set_context (sink, ctx);
+
+ return sink;
+}
diff --git a/cogl-gst/cogl-gst-video-sink.h b/cogl-gst/cogl-gst-video-sink.h
new file mode 100644
index 0000000..9ad4837
--- /dev/null
+++ b/cogl-gst/cogl-gst-video-sink.h
@@ -0,0 +1,121 @@
+/*
+ * Cogl-GStreamer.
+ *
+ * GStreamer integration library for Cogl.
+ *
+ * cogl-gst-video-sink.h - Gstreamer Video Sink that renders to a
+ * Cogl Pipeline.
+ *
+ * Authored by Jonathan Matthew <jonathan at kaolin.wh9.net>,
+ * Chris Lord <chris at openedhand.com>
+ * Damien Lespiau <damien.lespiau at intel.com>
+ * Matthew Allum <mallum at openedhand.com>
+ * Plamena Manolova <plamena.n.manolova at intel.com>
+ *
+ * Copyright (C) 2007, 2008 OpenedHand
+ * Copyright (C) 2009, 2010, 2013 Intel Corporation
+ *
+ * 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.
+ FLOP
+ */
+
+#ifndef __COGL_GST_VIDEO_SINK_H__
+#define __COGL_GST_VIDEO_SINK_H__
+#include <glib-object.h>
+#include <gst/base/gstbasesink.h>
+#include <cogl/cogl.h>
+
+G_BEGIN_DECLS
+
+#define COGL_GST_TYPE_VIDEO_SINK cogl_gst_video_sink_get_type()
+
+#define COGL_GST_VIDEO_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ COGL_GST_TYPE_VIDEO_SINK, CoglGstVideoSink))
+
+#define COGL_GST_VIDEO_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ COGL_GST_TYPE_VIDEO_SINK, CoglGstVideoSinkClass))
+
+#define COGL_GST_IS_VIDEO_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ COGL_GST_TYPE_VIDEO_SINK))
+
+#define COGL_GST_IS_VIDEO_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ COGL_GST_TYPE_VIDEO_SINK))
+
+#define COGL_GST_VIDEO_SINK_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ COGL_GST_TYPE_VIDEO_SINK, CoglGstVideoSinkClass))
+
+#define COGL_GST_PARAM_STATIC \
+ (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
+
+#define COGL_GST_PARAM_READABLE \
+ (G_PARAM_READABLE | COGL_GST_PARAM_STATIC)
+
+#define COGL_GST_PARAM_WRITABLE \
+ (G_PARAM_WRITABLE | COGL_GST_PARAM_STATIC)
+
+#define COGL_GST_PARAM_READWRITE \
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | COGL_GST_PARAM_STATIC)
+
+typedef struct _CoglGstVideoSink CoglGstVideoSink;
+typedef struct _CoglGstVideoSinkClass CoglGstVideoSinkClass;
+typedef struct _CoglGstVideoSinkPrivate CoglGstVideoSinkPrivate;
+
+struct _CoglGstVideoSink
+{
+ GstBaseSink parent;
+ CoglGstVideoSinkPrivate *priv;
+};
+
+struct _CoglGstVideoSinkClass
+{
+ GstBaseSinkClass parent_class;
+
+ void (* new_frame) (CoglGstVideoSink *sink);
+ void (* pipeline_ready) (CoglGstVideoSink *sink);
+
+ void *_padding_dummy[8];
+};
+
+GType cogl_gst_video_sink_get_type (void) G_GNUC_CONST;
+
+CoglGstVideoSink*
+cogl_gst_video_sink_new (CoglContext *ctx);
+
+CoglPipeline*
+cogl_gst_video_sink_get_pipeline (CoglGstVideoSink *vt);
+
+void
+cogl_gst_video_sink_set_context (CoglGstVideoSink *vt,
+ CoglContext *ctx);
+
+GMainLoop*
+cogl_gst_video_sink_get_main_loop (CoglGstVideoSink *loop);
+
+int
+cogl_gst_video_sink_get_free_layer (CoglGstVideoSink *sink);
+
+int
+cogl_gst_video_sink_attach_frame (CoglGstVideoSink *sink,
+ CoglPipeline *pln);
+
+G_END_DECLS
+
+#endif
diff --git a/cogl-gst/cogl-gst.h b/cogl-gst/cogl-gst.h
new file mode 100644
index 0000000..655134c
--- /dev/null
+++ b/cogl-gst/cogl-gst.h
@@ -0,0 +1,38 @@
+/*
+ * Cogl-GStreamer.
+ *
+ * GStreamer integration library for Cogl.
+ *
+ * cogl-gst.h - Top level header file
+ *
+ * Authored by Jonathan Matthew <jonathan at kaolin.wh9.net>,
+ * Chris Lord <chris at openedhand.com>
+ * Damien Lespiau <damien.lespiau at intel.com>
+ * Matthew Allum <mallum at openedhand.com>
+ * Plamena Manolova <plamena.n.manolova at intel.com>
+ *
+ * Copyright (C) 2007, 2008 OpenedHand
+ * Copyright (C) 2009, 2010, 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef __COGL_GST_H__
+#define __COGL_GST_H__
+
+#include <cogl-gst/cogl-gst-video-sink.h>
+
+#endif
diff --git a/cogl-gst/cogl-gst.pc.in b/cogl-gst/cogl-gst.pc.in
new file mode 100644
index 0000000..5333303
--- /dev/null
+++ b/cogl-gst/cogl-gst.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+apiversion=@COGL_API_VERSION@
+requires=@COGL_PKG_REQUIRES@ @COGL_GST_PKG_REQUIRES@
+
+Name: Cogl
+Description: An gstreamer integration library for Cogl
+Version: @COGL_VERSION@
+Libs: -L${libdir} -lcogl-gst
+Cflags: -I${includedir}/cogl -DCOGL_ENABLE_EXPERIMENTAL_2_0_API
+Requires: ${requires}
diff --git a/configure.ac b/configure.ac
index ea5bc17..2a8914d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -487,6 +487,40 @@ AS_IF([test "x$enable_cogl_pango" = "xyes"],
)
dnl ============================================================
+dnl Should cogl-gst be built?
+dnl ============================================================
+
+AS_IF([test "x$enable_glib" != "xyes"],
+ [
+ AS_IF([test "x$enable_cogl_gst" = "xyes"],
+ AC_MSG_ERROR([--enable-cogl-gst conflicts with --disable-glib]))
+ enable_cogl_gst=no
+ ]
+)
+
+AC_ARG_ENABLE(
+ [cogl-gst],
+ [AC_HELP_STRING([--enable-cogl-gst=@<:@no/yes@:>@], [Enable gstreamer support @<:@default=no@:>@])],
+ [],
+ enable_cogl_gst=no
+)
+AS_IF([test "x$enable_cogl_gst" = "xyes"],
+ [
+ COGL_GST_PKG_REQUIRES="$COGL_GST_PKG_REQUIRES gstreamer-1.0 gstreamer-fft-1.0 \
+ gstreamer-audio-1.0 gstreamer-base-1.0 \
+ gstreamer-video-1.0 gstreamer-plugins-base-1.0 \
+ gstreamer-tag-1.0 gstreamer-controller-1.0"
+
+ GST_MAJORMINOR=1.0
+
+ dnl define location of gstreamer plugin directory
+ plugindir="\$(libdir)/gstreamer-$GST_MAJORMINOR"
+ AC_SUBST(plugindir)
+ ]
+)
+
+
+dnl ============================================================
dnl Choose image loading backend
dnl ============================================================
AC_ARG_ENABLE(
@@ -1222,6 +1256,20 @@ AS_IF([test "x$enable_cogl_pango" = "xyes"],
)
AM_CONDITIONAL([BUILD_COGL_PANGO], [test "x$enable_cogl_pango" = "xyes"])
+AC_SUBST(COGL_GST_PKG_REQUIRES)
+
+AS_IF([test "x$enable_cogl_gst" = "xyes"],
+ [PKG_CHECK_MODULES(COGL_GST_DEP, [$COGL_GST_PKG_REQUIRES])]
+)
+AM_CONDITIONAL([BUILD_COGL_GST], [test "x$enable_cogl_gst" = "xyes"])
+
+have_hw_decoder_support=no
+#PKG_CHECK_MODULES(HW, [$COGL_GST_PKG_REQUIRES], [have_hw_decoder_support=yes
+# AC_DEFINE([HAVE_HW_DECODER_SUPPORT], [1],
+# ["Defined if building Cogl-Gst with HW decoder support"])
+# ],[ have_hw_decoder_support=no ])
+
+
dnl ================================================================
dnl Misc program dependencies.
@@ -1354,6 +1402,8 @@ AC_SUBST(COGL_DEP_CFLAGS)
AC_SUBST(COGL_DEP_LIBS)
AC_SUBST(COGL_PANGO_DEP_CFLAGS)
AC_SUBST(COGL_PANGO_DEP_LIBS)
+AC_SUBST(COGL_GST_DEP_CFLAGS)
+AC_SUBST(COGL_GST_DEP_LIBS)
AC_SUBST(COGL_EXTRA_CFLAGS)
AC_SUBST(COGL_EXTRA_LDFLAGS)
@@ -1387,6 +1437,8 @@ cogl-pango/Makefile
cogl-pango/cogl-pango-1.0.pc
cogl-pango/cogl-pango-2.0-experimental.pc
cogl-pango/cogl-pango.rc
+cogl-gst/Makefile
+cogl-gst/cogl-gst.pc
cogl-gles2/Makefile
cogl-gles2/cogl-gles2-experimental.pc
doc/Makefile
@@ -1442,6 +1494,8 @@ echo " Build libcogl-gles2 GLES 2.0 frontend api: ${enable_cogl_gles2}"
echo " Image backend: ${COGL_IMAGE_BACKEND}"
echo " Cogl Pango: ${enable_cogl_pango}"
echo " Profiling: ${enable_profile}"
+echo " CoglGst: ${enable_cogl_gst}"
+echo " CoglGst has HW suppport: ${have_hw_decoder_support}"
# Compiler/Debug related flags
echo ""
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 332fa13..5ad7cef 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -50,6 +50,13 @@ cogl_crate_CFLAGS = $(AM_CFLAGS) $(COGL_PANGO_DEP_CFLAGS)
examples_data_DATA += crate.jpg
endif
+if BUILD_COGL_GST
+programs += cogl-basic-video-player
+cogl_basic_video_player_SOURCES = cogl-basic-video-player.c
+cogl_basic_video_player_LDADD = $(common_ldadd) $(COGL_GST_DEP_LIBS) $(top_builddir)/cogl-gst/libcogl-gst.la
+cogl_basic_video_player_CFLAGS = $(AM_CFLAGS) $(COGL_GST_DEP_CFLAGS) -I$(top_builddir)/cogl-gst
+endif
+
if X11_TESTS
programs += cogl-x11-foreign cogl-x11-tfp
cogl_x11_foreign_SOURCES = cogl-x11-foreign.c
diff --git a/examples/cogl-basic-video-player.c b/examples/cogl-basic-video-player.c
new file mode 100644
index 0000000..ac6bb4b
--- /dev/null
+++ b/examples/cogl-basic-video-player.c
@@ -0,0 +1,248 @@
+#include <cogl/cogl.h>
+#include <cogl-gst/cogl-gst.h>
+
+typedef struct _Data
+{
+ CoglFramebuffer *fb;
+ CoglPipeline *pln;
+ CoglGstVideoSink *sink;
+ CoglBool draw_ready;
+ CoglBool frame_ready;
+ GMainLoop *main_loop;
+}Data;
+
+static CoglBool
+_bus_watch (GstBus *bus,
+ GstMessage *msg,
+ void *user_data)
+{
+ Data *data = (Data*) user_data;
+ switch (GST_MESSAGE_TYPE (msg))
+ {
+ case GST_MESSAGE_EOS:
+ {
+ g_main_loop_quit (data->main_loop);
+ break;
+ }
+ case GST_MESSAGE_ERROR:
+ {
+ char *debug;
+ GError *error = NULL;
+
+ gst_message_parse_error (msg, &error, &debug);
+ g_free (debug);
+
+ if (error != NULL)
+ {
+ g_error ("Playback error: %s\n", error->message);
+ g_error_free (error);
+ }
+ g_main_loop_quit (data->main_loop);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+_draw (Data *data)
+{
+ /*
+ The cogl pipeline needs to be retrieved from the sink before every draw.
+ This is due to the cogl-gst sink creating a new cogl pipeline for each frame
+ by copying the previous one and attaching the new frame to it.
+ */
+ CoglPipeline* current = cogl_gst_video_sink_get_pipeline (data->sink);
+
+ cogl_framebuffer_clear4f (data->fb,
+ COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_DEPTH, 0,
+ 0, 0, 1);
+ data->pln = current;
+
+ cogl_framebuffer_push_matrix (data->fb);
+ cogl_framebuffer_translate (data->fb, 640 / 2, 480 / 2, 0);
+ cogl_framebuffer_draw_textured_rectangle (data->fb, data->pln, -320, -240,
+ 320, 240, 0, 0, 1, 1);
+ cogl_framebuffer_pop_matrix (data->fb);
+
+ cogl_onscreen_swap_buffers (COGL_ONSCREEN (data->fb));
+}
+
+static void
+_check_draw (Data *data)
+{
+ /* The frame is only drawn once we know that a new buffer is ready
+ * from GStreamer and that Cogl is ready to accept some new
+ * rendering */
+ if (data->draw_ready && data->frame_ready)
+ {
+ _draw (data);
+ data->draw_ready = FALSE;
+ data->frame_ready = FALSE;
+ }
+}
+
+static void
+_frame_callback (CoglOnscreen *onscreen,
+ CoglFrameEvent event,
+ CoglFrameInfo *info,
+ void *user_data)
+{
+ Data *data = user_data;
+
+ if (event == COGL_FRAME_EVENT_SYNC)
+ {
+ data->draw_ready = TRUE;
+ _check_draw (data);
+ }
+}
+
+static void
+_new_frame_cb (CoglGstVideoSink *sink,
+ Data *data)
+{
+ data->frame_ready = TRUE;
+ _check_draw (data);
+}
+
+/*
+ A callback like this should be attached to the cogl-pipeline-ready
+ signal. This way requesting the cogl pipeline before its creation
+ by the sink is avoided. At this point, user textures and snippets can
+ be added to the cogl pipeline.
+*/
+
+static void
+_set_up_pipeline (gpointer instance,
+ gpointer user_data)
+{
+ Data* data = (Data*) user_data;
+
+ /*
+ The cogl-gst sink, depending on the video format, can use up to 3 texture
+ layers to render a frame. To avoid overwriting frame data, the first
+ free layer in the cogl pipeline needs to be queried before adding any
+ additional textures.
+ */
+
+ int free_layer = cogl_gst_video_sink_get_free_layer (data->sink);
+ data->pln = cogl_gst_video_sink_get_pipeline (data->sink);
+
+ while (free_layer > 0)
+ {
+ free_layer--;
+ cogl_pipeline_set_layer_filters (data->pln, free_layer,
+ COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR,
+ COGL_PIPELINE_FILTER_LINEAR);
+ }
+
+ cogl_onscreen_add_frame_callback (COGL_ONSCREEN (data->fb), _frame_callback,
+ data, NULL);
+
+ /*
+ The cogl-gst-new-frame signal is emitted when the cogl-gst sink has
+ retrieved a new frame and attached it to the cogl pipeline. This can be
+ used to make sure cogl doesn't do any unnecessary drawing i.e. keeps to the
+ frame-rate of the video.
+ */
+
+ g_signal_connect (data->sink, "new-frame", G_CALLBACK (_new_frame_cb), data);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ Data data;
+ CoglContext *ctx;
+ CoglOnscreen *onscreen;
+ CoglMatrix view;
+ float fovy, aspect, z_near, z_2d, z_far;
+ GstElement *pipeline;
+ GstElement *bin;
+ GSource *cogl_source;
+ GstBus *bus;
+ char *uri;
+
+ /* Set the necessary cogl elements */
+
+ ctx = cogl_context_new (NULL, NULL);
+ onscreen = cogl_onscreen_new (ctx, 640, 480);
+ data.fb = COGL_FRAMEBUFFER (onscreen);
+ cogl_onscreen_show (onscreen);
+
+ cogl_framebuffer_set_viewport (data.fb, 0, 0, 640, 480);
+ fovy = 60;
+ aspect = 640 / 480;
+ z_near = 0.1;
+ z_2d = 1000;
+ z_far = 2000;
+
+ cogl_framebuffer_perspective (data.fb, fovy, aspect, z_near, z_far);
+ cogl_matrix_init_identity (&view);
+ cogl_matrix_view_2d_in_perspective (&view, fovy, aspect, z_near, z_2d,
+ 640, 480);
+ cogl_framebuffer_set_modelview_matrix (data.fb, &view);
+
+ /* Intialize GStreamer */
+
+ gst_init (&argc, &argv);
+
+ /*
+ Create the cogl-gst video sink by calling the cogl_gst_video_sink_new
+ function and passing it a CoglContext (this is used to create the
+ CoglPipeline and the texures for each frame). Alternatively you can use
+ gst_element_factory_make ("coglsink", "some_name") and then set the
+ context with cogl_gst_video_sink_set_context.
+ */
+
+ data.sink = cogl_gst_video_sink_new (ctx);
+
+ pipeline = gst_pipeline_new ("gst-player");
+ bin = gst_element_factory_make ("playbin", "bin");
+
+ if (argc < 2)
+ uri = "http://docs.gstreamer.com/media/sintel_trailer-480p.webm";
+ else
+ uri = argv[1];
+
+ g_object_set (G_OBJECT (bin), "video-sink", GST_ELEMENT (data.sink), NULL);
+
+
+ gst_bin_add (GST_BIN (pipeline), bin);
+
+ g_object_set (G_OBJECT (bin), "uri", uri, NULL);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_watch (bus, _bus_watch, &data);
+
+ data.main_loop = g_main_loop_new (NULL, FALSE);
+
+ cogl_source = cogl_glib_source_new (ctx, G_PRIORITY_DEFAULT);
+ g_source_attach (cogl_source, NULL);
+
+ /*
+ The cogl-pipeline-ready signal tells you when the cogl pipeline is
+ initialized i.e. when cogl-gst has figured out the video format and
+ is prepared to retrieve and attach the first frame of the video.
+ */
+
+ g_signal_connect (data.sink, "pipeline-ready",
+ G_CALLBACK (_set_up_pipeline), &data);
+
+ data.draw_ready = TRUE;
+ data.frame_ready = FALSE;
+
+ g_main_loop_run (data.main_loop);
+
+ g_source_destroy (cogl_source);
+ g_source_unref (cogl_source);
+
+ g_main_loop_unref (data.main_loop);
+
+ return 0;
+}
--
1.8.3.1
More information about the Cogl
mailing list