diff options
author | Benjamin Otte <otte@redhat.com> | 2010-03-25 18:39:38 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2010-03-25 18:39:38 +0100 |
commit | b2f829feb06d3bc274a1ae5235679b3b4257fe4a (patch) | |
tree | 3ec044dabed694deb72dc705035a3713a89181ec | |
parent | 816d4553644191dcce24cedabec03bda69a0070e (diff) |
Add an experimental GLX sink
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | ext/xlib/Makefile.am | 2 | ||||
-rw-r--r-- | ext/xlib/gstcairoglxsink.c | 426 | ||||
-rw-r--r-- | ext/xlib/gstcairoglxsink.h | 67 | ||||
-rw-r--r-- | ext/xlib/gstcairoxlib.c | 7 |
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); } |