summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2010-03-25 18:39:38 +0100
committerBenjamin Otte <otte@redhat.com>2010-03-25 18:39:38 +0100
commitb2f829feb06d3bc274a1ae5235679b3b4257fe4a (patch)
tree3ec044dabed694deb72dc705035a3713a89181ec
parent816d4553644191dcce24cedabec03bda69a0070e (diff)
Add an experimental GLX sink
-rw-r--r--configure.ac8
-rw-r--r--ext/xlib/Makefile.am2
-rw-r--r--ext/xlib/gstcairoglxsink.c426
-rw-r--r--ext/xlib/gstcairoglxsink.h67
-rw-r--r--ext/xlib/gstcairoxlib.c7
5 files changed, 509 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index d38e882..c316f97 100644
--- a/configure.ac
+++ b/configure.ac
@@ -204,11 +204,17 @@ AG_GST_CHECK_FEATURE(PANGO, [Pango text rendering], pango, [
])
dnl *** xlib ***
-translit(dnm, m, l) AM_CONDITIONAL(USE_PANGO, true)
+translit(dnm, m, l) AM_CONDITIONAL(USE_XLIB, true)
AG_GST_CHECK_FEATURE(XLIB, [Xlib rendering], xlib , [
AG_GST_PKG_CHECK_MODULES(XLIB, cairo-xlib gstreamer-interfaces-0.10)
])
+dnl *** xlib ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_GLX, true)
+AG_GST_CHECK_FEATURE(GLX, [GLX rendering], glx, [
+ AG_GST_PKG_CHECK_MODULES(GLX, cairo-glx)
+])
+
dnl *** sys plug-ins ***
echo
diff --git a/ext/xlib/Makefile.am b/ext/xlib/Makefile.am
index 5ea5afb..7455e48 100644
--- a/ext/xlib/Makefile.am
+++ b/ext/xlib/Makefile.am
@@ -2,11 +2,13 @@
plugin_LTLIBRARIES = libgstcairoxlib.la
noinst_HEADERS = \
+ gstcairoglxsink.h \
gstcairoxconvert.h \
gstcairoxsink.h \
gstcairoxsource.h
libgstcairoxlib_la_SOURCES = \
+ gstcairoglxsink.c \
gstcairoxconvert.c \
gstcairoxlib.c \
gstcairoxsink.c \
diff --git a/ext/xlib/gstcairoglxsink.c b/ext/xlib/gstcairoglxsink.c
new file mode 100644
index 0000000..9d8638e
--- /dev/null
+++ b/ext/xlib/gstcairoglxsink.c
@@ -0,0 +1,426 @@
+/* GStreamer
+ * Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-cairo_glx_sink
+ *
+ * Convert video frames between a great variety of glx_sink formats.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v videotestsrc ! video/x-raw-yuv,format=\(fourcc\)YUY2 ! cairo_glx_sink ! ximagesink
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstcairoglxsink.h"
+
+#include <cairo-gl.h>
+#include <gst/interfaces/navigation.h>
+
+#include "gstcairoxsource.h"
+
+static void
+gst_cairo_glx_sink_navigation_send_event (GstNavigation * navigation,
+ GstStructure * structure)
+{
+ GstCairoGLXSink *xsink = GST_CAIRO_GLX_SINK (navigation);
+ GstEvent *event;
+ GstPad *pad;
+ double d;
+
+ pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xsink));
+ if (!pad) {
+ gst_structure_free (structure);
+ return;
+ }
+
+ if (gst_structure_get_double (structure, "pointer_x", &d))
+ gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, d *
+ gst_cairo_format_get_width (xsink->format) /
+ cairo_gl_surface_get_width (xsink->surface), NULL);
+ if (gst_structure_get_double (structure, "pointer_y", &d))
+ gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, d *
+ gst_cairo_format_get_height (xsink->format) /
+ cairo_gl_surface_get_height (xsink->surface), NULL);
+
+ event = gst_event_new_navigation (structure);
+
+ gst_pad_send_event (pad, event);
+ gst_object_unref (pad);
+}
+
+static void
+gst_cairo_glx_sink_navigation_init (GstNavigationInterface * iface)
+{
+ iface->send_event = gst_cairo_glx_sink_navigation_send_event;
+}
+
+static void
+_do_init (GType g_define_type_id)
+{
+ G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
+ gst_cairo_glx_sink_navigation_init);
+}
+
+GST_BOILERPLATE_FULL (GstCairoGLXSink, gst_cairo_glx_sink, GstElement,
+ GST_TYPE_VIDEO_SINK, _do_init)
+
+/* THREAD */
+ static void
+ gst_cairo_glx_sink_paint (GstCairoGLXSink * xsink, int x, int y,
+ int width, int height)
+{
+ GstBaseSink *bsink = GST_BASE_SINK (xsink);
+ cairo_surface_t *source;
+ GstCairoFormat *format;
+ GstBuffer *buffer;
+ cairo_t *cr;
+
+ buffer = gst_base_sink_get_last_buffer (bsink);
+ if (buffer == NULL)
+ return;
+
+ format = gst_cairo_format_new (GST_BUFFER_CAPS (buffer));
+ source = gst_cairo_create_surface (buffer, format);
+ cr = cairo_create (xsink->surface);
+
+ cairo_scale (cr,
+ (double) cairo_gl_surface_get_width (xsink->surface) /
+ gst_cairo_format_get_width (format),
+ (double) cairo_gl_surface_get_height (xsink->surface) /
+ gst_cairo_format_get_height (format));
+ cairo_set_source_surface (cr, source, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (source);
+
+ cairo_gl_surface_swapbuffers (xsink->surface);
+ gst_buffer_unref (buffer);
+ gst_cairo_format_free (format);
+}
+
+static gboolean
+gst_cairo_glx_sink_handle_event (XEvent * event, gpointer sink)
+{
+ GstCairoGLXSink *xsink = sink;
+
+ switch (event->type) {
+ case Expose:
+ {
+ XExposeEvent *expose = &event->xexpose;
+ gst_cairo_glx_sink_paint (xsink, expose->x, expose->y, expose->width,
+ expose->height);
+ break;
+ }
+ case ConfigureNotify:
+ {
+ XConfigureEvent *configure = &event->xconfigure;
+ cairo_gl_surface_set_size (xsink->surface, configure->width,
+ configure->height);
+ break;
+ }
+ case ButtonPress:
+ gst_navigation_send_mouse_event (GST_NAVIGATION (xsink),
+ "mouse-button-press", event->xbutton.button, event->xbutton.x,
+ event->xbutton.y);
+ break;
+ case ButtonRelease:
+ gst_navigation_send_mouse_event (GST_NAVIGATION (xsink),
+ "mouse-button-release", event->xbutton.button, event->xbutton.x,
+ event->xbutton.y);
+ break;
+ case MotionNotify:
+ gst_navigation_send_mouse_event (GST_NAVIGATION (xsink),
+ "mouse-move", 0, event->xmotion.x, event->xmotion.y);
+ break;
+ case KeyPress:
+ case KeyRelease:
+ {
+ KeySym keysym = XKeycodeToKeysym (xsink->display,
+ event->xkey.keycode, 0);
+ if (keysym != NoSymbol) {
+ char *key = NULL;
+
+ key = XKeysymToString (keysym);
+ if (key == NULL)
+ key = "unknown";
+ gst_navigation_send_key_event (GST_NAVIGATION (xsink),
+ event->type == KeyPress ? "key-press" : "key-release", key);
+ }
+ break;
+ }
+ case UnmapNotify:
+ case MapNotify:
+ case ReparentNotify:
+ /* can safely be ignored */
+ break;
+ default:
+ g_warning ("handle event %u\n", event->type);
+ break;
+ }
+
+ return TRUE;
+}
+
+static gpointer
+gst_cairo_glx_sink_run (gpointer sink)
+{
+ GstCairoGLXSink *xsink = sink;
+ GSource *source;
+
+ XSelectInput (xsink->display, xsink->window,
+ ExposureMask | StructureNotifyMask | PointerMotionMask | ButtonPressMask
+ | ButtonReleaseMask | KeyPressMask | KeyReleaseMask);
+
+ source =
+ gst_cairo_x_source_new (xsink->display,
+ gst_cairo_glx_sink_handle_event, xsink);
+ g_source_attach (source, xsink->context);
+ g_source_unref (source);
+ g_main_loop_run (xsink->loop);
+
+ return NULL;
+}
+
+static gboolean
+gst_cairo_glx_sink_repaint (gpointer sink)
+{
+ GstCairoGLXSink *xsink = sink;
+
+ gst_cairo_glx_sink_paint (xsink, 0, 0,
+ cairo_gl_surface_get_width (xsink->surface),
+ cairo_gl_surface_get_height (xsink->surface));
+
+ return FALSE;
+}
+
+static gboolean
+gst_cairo_glx_sink_stop_thread (gpointer sink)
+{
+ GstCairoGLXSink *xsink = sink;
+
+ g_main_loop_quit (xsink->loop);
+
+ return FALSE;
+}
+
+/*** MAIN THREAD ***/
+
+static gboolean
+gst_cairo_glx_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
+{
+ GstCairoGLXSink *xsink = GST_CAIRO_GLX_SINK (bsink);
+
+ gst_cairo_format_free (xsink->format);
+ xsink->format = gst_cairo_format_new (caps);
+
+ XResizeWindow (xsink->display, xsink->window,
+ gst_cairo_format_get_width (xsink->format),
+ gst_cairo_format_get_height (xsink->format));
+ XMapWindow (xsink->display, xsink->window);
+
+ return TRUE;
+}
+
+static gboolean
+gst_cairo_glx_sink_start (GstBaseSink * bsink)
+{
+ GstCairoGLXSink *glxsink = GST_CAIRO_GLX_SINK (bsink);
+ int rgba_attribs[] = { GLX_RGBA,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ GLX_ALPHA_SIZE, 1,
+ GLX_DOUBLEBUFFER,
+ None
+ };
+ XVisualInfo *visual;
+ GLXContext ctx;
+ XSetWindowAttributes attr;
+
+ glxsink->display = XOpenDisplay (NULL);
+ if (glxsink->display == NULL) {
+ GST_WARNING_OBJECT (glxsink, "Failed to open connection to X server");
+ return FALSE;
+ }
+
+ visual =
+ glXChooseVisual (glxsink->display, DefaultScreen (glxsink->display),
+ rgba_attribs);
+ if (visual == NULL) {
+ GST_WARNING_OBJECT (glxsink,
+ "Failed to create RGBA, double-buffered visual");
+ XCloseDisplay (glxsink->display);
+ return FALSE;
+ }
+
+ attr.colormap = XCreateColormap (glxsink->display,
+ RootWindow (glxsink->display, visual->screen), visual->visual, AllocNone);
+ attr.border_pixel = 0;
+ glxsink->window = XCreateWindow (glxsink->display,
+ DefaultRootWindow (glxsink->display), 0, 0, 400, 300,
+ 0, visual->depth, InputOutput, visual->visual,
+ CWBorderPixel | CWColormap, &attr);
+ XMapWindow (glxsink->display, glxsink->window);
+
+ ctx = glXCreateContext (glxsink->display, visual, NULL, True);
+ XFree (visual);
+
+ glxsink->device = cairo_glx_device_create (glxsink->display, ctx);
+ glxsink->surface = cairo_gl_surface_create_for_window (glxsink->device,
+ glxsink->window, 400, 300);
+
+ if (cairo_surface_status (glxsink->surface)) {
+ GST_WARNING_OBJECT (glxsink, "Failed to create GLX surface: %s",
+ cairo_status_to_string (cairo_surface_status (glxsink->surface)));
+ goto cleanup;
+ }
+
+ glxsink->context = g_main_context_new ();
+ glxsink->loop = g_main_loop_new (glxsink->context, TRUE);
+ glxsink->thread =
+ g_thread_create (gst_cairo_glx_sink_run, glxsink, TRUE, NULL);
+ if (!glxsink->thread) {
+ GST_WARNING_OBJECT (glxsink, "Failed to create event thread");
+ g_main_loop_unref (glxsink->loop);
+ glxsink->loop = NULL;
+ g_main_context_unref (glxsink->context);
+ glxsink->context = NULL;
+ goto cleanup;
+ }
+
+ return TRUE;
+
+cleanup:
+ cairo_surface_destroy (glxsink->surface);
+ glxsink->surface = NULL;
+ cairo_device_destroy (glxsink->device);
+ glxsink->device = NULL;
+ XDestroyWindow (glxsink->display, glxsink->window);
+ XCloseDisplay (glxsink->display);
+ glxsink->display = NULL;
+ return FALSE;
+}
+
+static gboolean
+gst_cairo_glx_sink_stop (GstBaseSink * bsink)
+{
+ GstCairoGLXSink *xsink = GST_CAIRO_GLX_SINK (bsink);
+ GSource *source;
+
+ source = g_idle_source_new ();
+ g_source_set_callback (source, gst_cairo_glx_sink_stop_thread, xsink, NULL);
+ g_source_attach (source, xsink->context);
+ g_source_unref (source);
+
+ g_thread_join (xsink->thread);
+
+ g_main_loop_unref (xsink->loop);
+ xsink->loop = NULL;
+ g_main_context_unref (xsink->context);
+ xsink->context = NULL;
+
+ cairo_surface_destroy (xsink->surface);
+ xsink->surface = NULL;
+ cairo_device_destroy (xsink->device);
+ xsink->device = NULL;
+ XDestroyWindow (xsink->display, xsink->window);
+
+ XCloseDisplay (xsink->display);
+ xsink->display = NULL;
+ gst_cairo_format_free (xsink->format);
+ xsink->format = NULL;
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_cairo_glx_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
+{
+ GstCairoGLXSink *xsink = GST_CAIRO_GLX_SINK (vsink);
+ GSource *source;
+
+ source = g_idle_source_new ();
+ g_source_set_callback (source, gst_cairo_glx_sink_repaint, xsink, NULL);
+ g_source_attach (source, xsink->context);
+ g_source_unref (source);
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_cairo_glx_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
+ guint size, GstCaps * caps, GstBuffer ** buf)
+{
+ GstCairoGLXSink *xsink = GST_CAIRO_GLX_SINK (bsink);
+ GstCairoFormat *format;
+
+ format = gst_cairo_format_new (caps);
+ if (gst_cairo_format_is_native (format)) {
+ g_assert (xsink->surface);
+ *buf = gst_cairo_buffer_new_similar (xsink->surface, format);
+ } else {
+ *buf = NULL;
+ }
+ gst_cairo_format_free (format);
+
+ return GST_FLOW_OK;
+}
+
+static void
+gst_cairo_glx_sink_base_init (gpointer klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_cairo_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS));
+ gst_element_class_set_details_simple (element_class,
+ "Cairo GLX Video Sink",
+ "Sink/Video",
+ "Outputs video to X11 using Cairo", "Benjamin Otte <otte@gnome.org>");
+}
+
+static void
+gst_cairo_glx_sink_class_init (GstCairoGLXSinkClass * klass)
+{
+ GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
+ GstVideoSinkClass *videosink_class = GST_VIDEO_SINK_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ basesink_class->start = gst_cairo_glx_sink_start;
+ basesink_class->stop = gst_cairo_glx_sink_stop;
+ basesink_class->set_caps = gst_cairo_glx_sink_set_caps;
+ basesink_class->buffer_alloc = gst_cairo_glx_sink_buffer_alloc;
+
+ videosink_class->show_frame = gst_cairo_glx_sink_show_frame;
+}
+
+static void
+gst_cairo_glx_sink_init (GstCairoGLXSink * xsink, GstCairoGLXSinkClass * klass)
+{
+}
diff --git a/ext/xlib/gstcairoglxsink.h b/ext/xlib/gstcairoglxsink.h
new file mode 100644
index 0000000..f96e63d
--- /dev/null
+++ b/ext/xlib/gstcairoglxsink.h
@@ -0,0 +1,67 @@
+/* GStreamer
+ * Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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 __GST_CAIRO_GLX_SINK_H__
+#define __GST_CAIRO_GLX_SINK_H__
+
+#include <gst/gst.h>
+#include <gst/cairo/gstcairo.h>
+#include <gst/video/gstvideosink.h>
+
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_CAIRO_GLX_SINK (gst_cairo_glx_sink_get_type())
+#define GST_CAIRO_GLX_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAIRO_GLX_SINK,GstCairoGLXSink))
+#define GST_CAIRO_GLX_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAIRO_GLX_SINK,GstCairoGLXSinkClass))
+#define GST_IS_CAIRO_GLX_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAIRO_GLX_SINK))
+#define GST_IS_CAIRO_GLX_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAIRO_GLX_SINK))
+
+typedef struct _GstCairoGLXSink GstCairoGLXSink;
+typedef struct _GstCairoGLXSinkClass GstCairoGLXSinkClass;
+
+/**
+ * GstCairoGLXSink:
+ *
+ * Opaque object data structure.
+ */
+struct _GstCairoGLXSink {
+ GstVideoSink sink;
+
+ Display * display; /* the Display in use */
+ Window window; /* window we draw onto */
+ cairo_device_t * device; /* device used for drawing */
+ cairo_surface_t * surface; /* surface used for drawing or NULL */
+ GMainContext * context; /* context of event thread */
+ GMainLoop * loop; /* loop running context in event thread */
+ GstCairoFormat * format; /* negotiated format on the sink side */
+ GThread * thread; /* the event thread */
+};
+
+struct _GstCairoGLXSinkClass
+{
+ GstVideoSinkClass sink_class;
+};
+
+GType gst_cairo_glx_sink_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_CAIRO_GLX_SINK_H__ */
diff --git a/ext/xlib/gstcairoxlib.c b/ext/xlib/gstcairoxlib.c
index 7d37c6e..3adb1c9 100644
--- a/ext/xlib/gstcairoxlib.c
+++ b/ext/xlib/gstcairoxlib.c
@@ -27,6 +27,9 @@
#include <gst/gst.h>
+#ifdef HAVE_GLX
+#include "gstcairoglxsink.h"
+#endif
#include "gstcairoxconvert.h"
#include "gstcairoxsink.h"
@@ -37,6 +40,10 @@ plugin_init (GstPlugin * plugin)
return gst_element_register (plugin, "cairoxsink",
GST_RANK_NONE, GST_TYPE_CAIRO_X_SINK) &&
+#ifdef HAVE_GLX
+ gst_element_register (plugin, "cairoglxsink",
+ GST_RANK_NONE, GST_TYPE_CAIRO_GLX_SINK) &&
+#endif
gst_element_register (plugin, "cairoxconvert",
GST_RANK_NONE, GST_TYPE_CAIRO_X_CONVERT);
}