diff options
-rw-r--r-- | sys/glsink/Makefile.am | 10 | ||||
-rw-r--r-- | sys/glsink/gstgl_nvimage.c | 388 | ||||
-rw-r--r-- | sys/glsink/gstgl_rgbimage.c | 330 | ||||
-rw-r--r-- | sys/glsink/gstglsink.c | 650 | ||||
-rw-r--r-- | sys/glsink/gstglsink.h | 94 | ||||
-rw-r--r-- | sys/glsink/gstglxwindow.c | 239 |
6 files changed, 1711 insertions, 0 deletions
diff --git a/sys/glsink/Makefile.am b/sys/glsink/Makefile.am new file mode 100644 index 00000000..8c7838c8 --- /dev/null +++ b/sys/glsink/Makefile.am @@ -0,0 +1,10 @@ +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +plugin_LTLIBRARIES = libgstglsink.la + +libgstglsink_la_SOURCES = gstglsink.c gstglxwindow.c gstgl_rgbimage.c gstgl_nvimage.c +libgstglsink_la_CFLAGS = $(GST_CFLAGS) +libgstglsink_la_LIBADD = $(X_LIBS) $(XVIDEO_LIBS) -lGL -lGLU -lglut +libgstglsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstglsink.h diff --git a/sys/glsink/gstgl_nvimage.c b/sys/glsink/gstgl_nvimage.c new file mode 100644 index 00000000..faa5224c --- /dev/null +++ b/sys/glsink/gstgl_nvimage.c @@ -0,0 +1,388 @@ +#include "config.h" + +#include <gst/gst.h> +/* gcc -ansi -pedantic on GNU/Linux causes warnings and errors + * unless this is defined: + * warning: #warning "Files using this header must be compiled with _SVID_SOURCE or _XOPEN_SOURCE" + */ +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> + +#define GL_GLEXT_PROTOTYPES + +// VERY dangerous: +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <GL/glx.h> +#include <GL/gl.h> +#include <GL/glext.h> +#include <GL/glu.h> +#include <string.h> + +#include "gstglsink.h" + +typedef struct _GstGLImageConnection GstGLImageConnection; +struct _GstGLImageConnection { + GstImageConnection conn; + Display *dpy; + gint w, h; + gint bpp; + + int ytex_id; + int uvtex_id; + int septex_id; + unsigned char *m_memory; + int m_bufslots[4]; +}; + +#define TEX_XSIZE 1024 +#define TEX_YSIZE 1024 +#define YUVTEX_SIZE ((TEX_XSIZE * TEX_YSIZE) * 3 /2) + +#define AGP_BUFSLOTS 4 + +typedef struct _GstNvImage GstNvImage; +struct _GstNvImage +{ + GstImageData data; + int slot; // < AGP_BUFSLOTS: allocated from AGP mem, otherwise from CPU mem + GstGLImageConnection *conn; +}; + +static GstGLImageInfo * gst_gl_nvimage_info (GstImageInfo *info); +static GstGLImageConnection * gst_gl_nvimage_connection (GstImageConnection *conn); +static gboolean gst_gl_nvimage_check_xvideo (); + +static GstCaps * gst_gl_nvimage_get_caps (GstImageInfo *info); +static GstImageConnection * gst_gl_nvimage_set_caps (GstImageInfo *info, GstCaps *caps); +static GstImageData * gst_gl_nvimage_get_image (GstImageInfo *info, GstImageConnection *conn); +static void gst_gl_nvimage_put_image (GstImageInfo *info, GstImageData *image); +static void gst_gl_nvimage_free_image (GstImageData *image); +static void gst_gl_nvimage_open_conn (GstImageConnection *conn, GstImageInfo *info); +static void gst_gl_nvimage_close_conn (GstImageConnection *conn, GstImageInfo *info); +static void gst_gl_nvimage_free_conn (GstImageConnection *conn); + +GstImagePlugin* get_gl_nvimage_plugin(void) +{ + static GstImagePlugin plugin = { gst_gl_nvimage_get_caps, + gst_gl_nvimage_set_caps, + gst_gl_nvimage_get_image, + gst_gl_nvimage_put_image, + gst_gl_nvimage_free_image}; + + return &plugin; +} + + +static GstGLImageInfo * +gst_gl_nvimage_info (GstImageInfo *info) +{ + if (info == NULL || info->id != GST_MAKE_FOURCC ('X', 'l', 'i', 'b')) + { + return NULL; + } + return (GstGLImageInfo *) info; +} + +static GstGLImageConnection * +gst_gl_nvimage_connection (GstImageConnection *conn) +{ + if (conn == NULL || conn->free_conn != gst_gl_nvimage_free_conn) + return NULL; + return (GstGLImageConnection *) conn; +} + +gboolean +gst_gl_nvimage_check_xvideo () +{ + int ver, rel, req, ev, err; + +#if 0 + if (display == NULL) + return FALSE; + if (Success == XvQueryExtension (display,&ver,&rel,&req,&ev,&err)) + return TRUE; +#endif + + return FALSE; +} + +static GstCaps * +gst_gl_nvimage_get_caps (GstImageInfo *info) +{ + gint i; + int adaptors; + int formats; + GstCaps *caps = NULL; + GstGLImageInfo *xinfo = gst_gl_nvimage_info (info); + + /* we don't handle these image information */ + if (xinfo == NULL) return NULL; + + if (gst_gl_nvimage_check_xvideo () == FALSE) + { + g_warning("GL_NVImage: Server has no NVidia extension support\n"); + return NULL; + } + + caps = gst_caps_append (caps, GST_CAPS_NEW ( + "xvimage_caps", + "video/raw", + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('Y', 'C', '1', '2')), + "width", GST_PROPS_INT_RANGE (0, 1024), + "height", GST_PROPS_INT_RANGE (0, 1024)) + ); + return caps; +} + +static GstImageConnection * +gst_gl_nvimage_set_caps (GstImageInfo *info, GstCaps *caps) +{ + gint i, j = 0; + int adaptors; + int formats; + GstGLImageConnection *conn; + GstGLImageInfo *xinfo = gst_gl_nvimage_info (info); + guint32 format; + + /* we don't handle these image information */ + if (xinfo == NULL) return NULL; + + conn = g_new0 (GstGLImageConnection, 1); + conn->conn.open_conn = gst_gl_nvimage_open_conn; + conn->conn.close_conn = gst_gl_nvimage_close_conn; + conn->conn.free_conn = gst_gl_nvimage_free_conn; + + gst_caps_get (caps, + "width", &conn->w, + "height", &conn->h, + "format", &format, + NULL); + + if (0) //conn->port == (XvPortID) -1) + { + /* this happens if the plugin can't handle the caps, so no warning */ + g_free (conn); + return NULL; + } + + return (GstImageConnection *) conn; +} + +static GstImageData * +gst_gl_nvimage_get_image (GstImageInfo *info, GstImageConnection *conn) +{ + GstNvImage *image; + GstGLImageInfo *xinfo = gst_gl_nvimage_info (info); + GstGLImageConnection *nvconn = gst_gl_nvimage_connection (conn); + int slot = 0; + + /* checks */ + if (xinfo == NULL) return NULL; + if (nvconn == NULL) return NULL; + + // I should also check the current GLX context ! Don't have to, I am guarantueed to always be in the same thread + + image = g_new0(GstNvImage, 1); + + for (slot = 0; slot < AGP_BUFSLOTS; slot++) + { + if (!nvconn->m_bufslots[slot]) break; + } + + image->data.size = nvconn->w * nvconn->h * 3/2; + + if (slot < AGP_BUFSLOTS) // found an AGP buffer slot + { + image->data.data = nvconn->m_memory + slot * YUVTEX_SIZE; + image->slot = slot; // store for freeing + nvconn->m_bufslots[slot] = 1; // it is now taken + } + else + { + g_warning("Allocating from main memory !"); + image->data.data = g_malloc(image->data.size); + image->slot = AGP_BUFSLOTS; // no AGP slot + } + image->conn = nvconn; + + if (image->data.data == NULL) + { + g_warning ("GL_NvImage: data allocation failed!"); + g_free (image); + return NULL; + } + + return (GstImageData *) image; +} + +static void +gst_gl_nvimage_put_image (GstImageInfo *info, GstImageData *image) +{ + GstNvImage *im = (GstNvImage *) image; + GstGLImageInfo *xinfo = gst_gl_nvimage_info (info); + + /* checks omitted for speed (and lazyness), do we need them? */ + g_assert (xinfo != NULL); + + /* Upload the texture here */ + g_warning("PUTTING IMAGE - BROOOKEN"); + + // both upload the video, and redraw the screen + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glEnable(GL_TEXTURE_2D); + + glPushMatrix(); + //glTranslatef(0,1,0); + glRotatef(xinfo->rotX-250,1,0,0); + glRotatef(xinfo->rotY,0,1,0); + int zoom = xinfo->zoom; + glScaled(zoom,zoom,zoom); + //Draws the surface rectangle + + glBindTexture(GL_TEXTURE_2D, im->conn->ytex_id); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, im->conn->w, im->conn->h, GL_RGBA, + GL_UNSIGNED_BYTE, im->data.data); + float xmax = (float)im->conn->w/TEX_XSIZE; + float ymax = (float)im->conn->h/TEX_YSIZE; + + glColor4f(1,1,1,1); + glBegin(GL_QUADS); + + glNormal3f(0, -1, 0); + + glTexCoord2f(xmax, 0); + glVertex3f(4,0,-4); + + glTexCoord2f(0, 0); + glVertex3f(-4,0,-4); + + glTexCoord2f(0, ymax); + glVertex3f(-4,0,4); + + glTexCoord2f(xmax, ymax); + glVertex3f(4,0,4); + + glEnd(); + + glPopMatrix(); + + glXSwapBuffers(xinfo->dpy, xinfo->win); +} + +static void +gst_gl_nvimage_free_image (GstImageData *image) +{ + GstNvImage *im = (GstNvImage *) image; + g_return_if_fail (im != NULL); + GstGLImageConnection *nvconn = im->conn; + + if (im->slot < AGP_BUFSLOTS) + { + nvconn->m_bufslots[im->slot] = 0; + } + else + g_free(im->data.data); + + g_free (im); +} + +static void +gst_gl_nvimage_open_conn (GstImageConnection *conn, GstImageInfo *info) +{ + GstGLImageInfo *xinfo = gst_gl_nvimage_info (info); + GstGLImageConnection *xconn = gst_gl_nvimage_connection (conn); + + unsigned char data_sep[2][2] = {{0, 255}, {0, 255}}; + int slot; + + g_warning("Opening NVidia Connection"); + xconn->m_memory = (unsigned char*)glXAllocateMemoryNV(AGP_BUFSLOTS*YUVTEX_SIZE, 0, 1.0, 1.0); + + if (!xconn->m_memory) + { + printf("Unable to acquire graphics card mem... will acquire in normal memory.\n"); + for (slot = 0; slot < AGP_BUFSLOTS; slot++) + xconn->m_bufslots[slot] = 1; + } + else + { + // maybe this fast writable memory, awfully slow to read from, though + glPixelDataRangeNV(GL_WRITE_PIXEL_DATA_RANGE_NV, AGP_BUFSLOTS*YUVTEX_SIZE, xconn->m_memory); + glEnableClientState(GL_WRITE_PIXEL_DATA_RANGE_NV); + + for (slot = 0; slot < AGP_BUFSLOTS; slot++) + xconn->m_bufslots[slot] = 0; + } + + glGenTextures(1, &xconn->ytex_id); + glBindTexture(GL_TEXTURE_2D, xconn->ytex_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8_ALPHA8, TEX_XSIZE, TEX_YSIZE, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + + glActiveTextureARB(GL_TEXTURE1_ARB); + glGenTextures(1, &xconn->uvtex_id); + glBindTexture(GL_TEXTURE_2D, xconn->uvtex_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8_ALPHA8, TEX_XSIZE/2, TEX_YSIZE/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); + + glActiveTextureARB(GL_TEXTURE2_ARB); + glGenTextures(1, &xconn->septex_id); + glBindTexture(GL_TEXTURE_2D, xconn->septex_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, 2, 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data_sep); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); + + glFlushPixelDataRangeNV(GL_WRITE_PIXEL_DATA_RANGE_NV); + //glEnable(GL_TEXTURE_2D); + glActiveTextureARB(GL_TEXTURE0_ARB); + glEnable(GL_TEXTURE_2D); + glActiveTextureARB(GL_TEXTURE1_ARB); + glEnable(GL_TEXTURE_2D); + glActiveTextureARB(GL_TEXTURE2_ARB); + glEnable(GL_TEXTURE_2D); + glActiveTextureARB(GL_TEXTURE0_ARB); +} + +static void +gst_gl_nvimage_close_conn (GstImageConnection *conn, GstImageInfo *info) +{ + GstGLImageConnection *xconn = gst_gl_nvimage_connection (conn); + GstGLImageInfo *xinfo = gst_gl_nvimage_info (info); + + // anything needed in here ? Oh, maybe drawing de-init, or something + glDeleteTextures(1, &xconn->ytex_id); + glDeleteTextures(1, &xconn->uvtex_id); + glDeleteTextures(1, &xconn->septex_id); +} + +static void +gst_gl_nvimage_free_conn (GstImageConnection *conn) +{ + GstGLImageConnection *nvconn = gst_gl_nvimage_connection (conn); + + g_free (nvconn); +} + diff --git a/sys/glsink/gstgl_rgbimage.c b/sys/glsink/gstgl_rgbimage.c new file mode 100644 index 00000000..31ac16ae --- /dev/null +++ b/sys/glsink/gstgl_rgbimage.c @@ -0,0 +1,330 @@ +#include <gst/gst.h> + +/* gcc -ansi -pedantic on GNU/Linux causes warnings and errors + * unless this is defined: + * warning: #warning "Files using this header must be compiled with _SVID_SOURCE or _XOPEN_SOURCE" + */ +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <GL/glx.h> +#include <GL/gl.h> +#include <GL/glu.h> + +#include "gstglsink.h" + +typedef struct _GstGLImageConnection GstGLImageConnection; + +// this contains everything to draw an image, including all necessary graphics card data. +struct _GstGLImageConnection { + GstImageConnection conn; + Display *dpy; // the Xlib drawing context + GLXContext ctx; // The GLX drawing context + gint w, h; + gint bpp; + + int rgbatex_id; + unsigned char *m_memory; +}; + +#define TEX_XSIZE 1024 +#define TEX_YSIZE 1024 + + +typedef struct _GstGLImage GstGLImage; +struct _GstGLImage +{ + GstImageData data; + GstGLImageConnection *conn; +}; + +static GstGLImageInfo * gst_gl_rgbimage_info (GstImageInfo *info); +static GstGLImageConnection * gst_gl_rgbimage_connection (GstImageConnection *conn); + +static GstCaps * gst_gl_rgbimage_get_caps (GstImageInfo *info); +static GstImageConnection * gst_gl_rgbimage_set_caps (GstImageInfo *info, GstCaps *caps); +static GstImageData * gst_gl_rgbimage_get_image (GstImageInfo *info, GstImageConnection *conn); +static void gst_gl_rgbimage_put_image (GstImageInfo *info, GstImageData *image); +static void gst_gl_rgbimage_free_image (GstImageData *image); +static void gst_gl_rgbimage_open_conn (GstImageConnection *conn, GstImageInfo *info); +static void gst_gl_rgbimage_close_conn (GstImageConnection *conn, GstImageInfo *info); +static void gst_gl_rgbimage_free_conn (GstImageConnection *conn); + +GstImagePlugin* get_gl_rgbimage_plugin(void) +{ + static GstImagePlugin plugin = { gst_gl_rgbimage_get_caps, + gst_gl_rgbimage_set_caps, + gst_gl_rgbimage_get_image, + gst_gl_rgbimage_put_image, + gst_gl_rgbimage_free_image}; + + return &plugin; +} + +static GstGLImageInfo * +gst_gl_rgbimage_info (GstImageInfo *info) +{ + if (info == NULL || info->id != GST_MAKE_FOURCC ('X', 'l', 'i', 'b')) + { + return NULL; + } + return (GstGLImageInfo *) info; +} + +static GstGLImageConnection * +gst_gl_rgbimage_connection (GstImageConnection *conn) +{ + if (conn == NULL || conn->free_conn != gst_gl_rgbimage_free_conn) + return NULL; + return (GstGLImageConnection *) conn; +} + +GstCaps * +gst_gl_rgbimage_get_caps (GstImageInfo *info) +{ + GstCaps *caps = NULL; + Visual *visual; + int xpad; + XWindowAttributes attrib; + XImage *ximage; + GstGLImageInfo *xinfo = gst_gl_rgbimage_info (info); + + /* we don't handle this image information */ + if (xinfo == NULL) return NULL; + + XGetWindowAttributes(xinfo->dpy, xinfo->win, &attrib); + + visual = attrib.visual; + if (attrib.depth <= 8) + xpad = 8; + else if (attrib.depth <= 16) + xpad = 16; + else + xpad = 32; + + // create a temporary image + ximage = XCreateImage (xinfo->dpy, visual, attrib.depth, ZPixmap, 0, NULL, + 100, 100, xpad, (attrib.depth + 7) / 8 * 100); + if (ximage != NULL) { + caps = + GST_CAPS_NEW ( + "forcing Video RGB", + "video/raw", + "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("RGB ")), + "bpp", GST_PROPS_INT(32), + "depth", GST_PROPS_INT(24), + "red_mask", GST_PROPS_INT(0xff), + "green_mask", GST_PROPS_INT(0xff00), + "blue_mask", GST_PROPS_INT(0xff0000), + "endianness", GST_PROPS_INT(G_LITTLE_ENDIAN), /*= 1234/4321 (INT) <- endianness */ + + "width", GST_PROPS_INT_RANGE (0, TEX_XSIZE), /* can't have videos larger than TEX_SIZE */ + "height", GST_PROPS_INT_RANGE (0, TEX_YSIZE) + ); + XDestroyImage (ximage); + } + + GST_DEBUG (GST_CAT_PLUGIN_INFO, "GL_RGBImage: returning caps at %p", caps); + return caps; +} + +static GstImageConnection * +gst_gl_rgbimage_set_caps (GstImageInfo *info, GstCaps *caps) +{ + GstGLImageConnection *new = NULL; + Visual *visual; + XWindowAttributes attrib; + GstGLImageInfo *xinfo = gst_gl_rgbimage_info (info); + guint32 format; + gint depth; + gint endianness; + gint red_mask, green_mask, blue_mask; + gint width, height, bpp; + + /* check if this is the right image info */ + if (xinfo == NULL) return NULL; + + XGetWindowAttributes(xinfo->dpy, xinfo->win, &attrib); + + visual = attrib.visual; + + gst_caps_get (caps, + "format", &format, + "depth", &depth, + "endianness", &endianness, + "red_mask", &red_mask, + "green_mask", &green_mask, + "blue_mask", &blue_mask, + "width", &width, + "height", &height, + "bpp", &bpp, + NULL); + + /* check if the caps are ok */ + if (format != GST_MAKE_FOURCC ('R', 'G', 'B', ' ')) return NULL; + /* if (gst_caps_get_int (caps, "bpp") != ???) return NULL; */ + //if (depth != attrib.depth) return NULL; + //if (endianness != ((ImageByteOrder (xinfo->dpy) == LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN)) return NULL; + //if (red_mask != visual->red_mask) return NULL; + //if (green_mask != visual->green_mask) return NULL; + //if (blue_mask != visual->blue_mask) return NULL; + GST_DEBUG (GST_CAT_PLUGIN_INFO, "GL_RGBImage: caps %p are ok, creating image", caps); + + new = g_new (GstGLImageConnection, 1); + new->conn.open_conn = gst_gl_rgbimage_open_conn; + new->conn.close_conn = gst_gl_rgbimage_close_conn; + new->conn.free_conn = gst_gl_rgbimage_free_conn; + new->dpy = xinfo->dpy; + new->ctx = xinfo->ctx; + new->w = width; + new->h = height; + new->bpp = bpp; + + return (GstImageConnection *) new; +} + +static GstImageData * +gst_gl_rgbimage_get_image (GstImageInfo *info, GstImageConnection *conn) +{ + GstGLImage *image; + XWindowAttributes attrib; + GstGLImageInfo *xinfo = gst_gl_rgbimage_info (info); + GstGLImageConnection *xconn = gst_gl_rgbimage_connection (conn); + + image = g_new (GstGLImage, 1); + + /* checks */ + if (xinfo == NULL) return NULL; + if (xconn == NULL) return NULL; + if (xinfo->dpy != xconn->dpy) + { + g_warning ("XImage: wrong x display specified in 'get_image'\n"); + return NULL; + } + + image->conn = xconn; + image->data.size = xconn->w * xconn->h * 4; + image->data.data = g_malloc(image->data.size); + if (image->data.data == NULL) + { + g_warning ("GL_RGBImage: data allocation failed!"); + g_free (image); + return NULL; + } + + return (GstImageData *) image; +} + + +static void +gst_gl_rgbimage_put_image (GstImageInfo *info, GstImageData *image) +{ + float xmax, ymax; + + GstGLImageInfo *xinfo = gst_gl_rgbimage_info (info); + GstGLImage *im = (GstGLImage *) image; + + g_assert (xinfo != NULL); + g_warning("PUTTING IMAGE"); + + // both upload the video, and redraw the screen + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glEnable(GL_TEXTURE_2D); + + glPushMatrix(); + //glTranslatef(0,1,0); + glRotatef(xinfo->rotX-250,1,0,0); + glRotatef(xinfo->rotY,0,1,0); + int zoom = xinfo->zoom; + glScaled(zoom,zoom,zoom); + //Draws the surface rectangle + + glBindTexture(GL_TEXTURE_2D, im->conn->rgbatex_id); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, im->conn->w, im->conn->h, GL_RGBA, + GL_UNSIGNED_BYTE, im->data.data); + xmax = (float)im->conn->w/TEX_XSIZE; + ymax = (float)im->conn->h/TEX_YSIZE; + + glColor4f(1,1,1,1); + glBegin(GL_QUADS); + + glNormal3f(0, -1, 0); + + glTexCoord2f(xmax, 0); + glVertex3f(4,0,-4); + + glTexCoord2f(0, 0); + glVertex3f(-4,0,-4); + + glTexCoord2f(0, ymax); + glVertex3f(-4,0,4); + + glTexCoord2f(xmax, ymax); + glVertex3f(4,0,4); + + glEnd(); + + glPopMatrix(); + + glXSwapBuffers(xinfo->dpy, xinfo->win); +} + +void +gst_gl_rgbimage_free_image (GstImageData *image) +{ + GstGLImage *im = (GstGLImage *) image; + + g_warning ("gst_gl_rgbimage_free_image doesn't do anything yet -> freeing image\n"); + g_free (im->data.data); + g_free (im); +} + +/* Creates an OpenGL texture to upload the picture over */ +static void +gst_gl_rgbimage_open_conn (GstImageConnection *conn, GstImageInfo *info) +{ + g_warning("!!! Opening Connection !!!"); + + GstGLImageInfo *xinfo = gst_gl_rgbimage_info (info); + GstGLImageConnection *xconn = gst_gl_rgbimage_connection (conn); + + glGenTextures(1, &xconn->rgbatex_id); + glBindTexture(GL_TEXTURE_2D, xconn->rgbatex_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_XSIZE, TEX_YSIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); +} + +/* Deletes the creates OpenGL textures */ +static void +gst_gl_rgbimage_close_conn (GstImageConnection *conn, GstImageInfo *info) +{ + GstGLImageConnection *xconn = gst_gl_rgbimage_connection (conn); + GstGLImageInfo *xinfo = gst_gl_rgbimage_info (info); + + glDeleteTextures(1, &xconn->rgbatex_id); + } + +static void +gst_gl_rgbimage_free_conn (GstImageConnection *conn) +{ + GstGLImageConnection *xconn = gst_gl_rgbimage_connection (conn); + + g_assert (xconn != NULL); + + g_free (xconn); +} + + diff --git a/sys/glsink/gstglsink.c b/sys/glsink/gstglsink.c new file mode 100644 index 00000000..d3e53f22 --- /dev/null +++ b/sys/glsink/gstglsink.c @@ -0,0 +1,650 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * 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. + */ + +#include <config.h> +#include <string.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <GL/glx.h> +#include <GL/gl.h> +#include <GL/glu.h> +#include <sys/time.h> + +/*#define GST_DEBUG_FORCE_DISABLE*/ + +#include "gstglsink.h" + +/* elementfactory information */ +static GstElementDetails gst_glsink_details = { + "OpenGL Sink/GLX", + "Sink/GLVideo", + "LGPL", + "An OpenGL based video sink - uses OpenGL and GLX to draw video, utilizing different acceleration options", + VERSION, + "Gernot Ziegler <gz@lysator.liu.se>", + "(C) 2002", +}; + +/* glsink signals and args */ +enum { + LAST_SIGNAL +}; + + +enum { + ARG_0, + ARG_WIDTH, + ARG_HEIGHT, + ARG_FRAMES_DISPLAYED, + ARG_FRAME_TIME, + ARG_HOOK, + ARG_MUTE, + ARG_REPAINT +}; + +/* GLsink class */ +#define GST_TYPE_GLSINK (gst_glsink_get_type()) +#define GST_GLSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_GLSINK,GstGLSink)) +#define GST_GLSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_GLSINK,GstGLSink)) +#define GST_IS_GLSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GLSINK)) +#define GST_IS_GLSINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_GLSINK)) + +typedef struct _GstGLSink GstGLSink; +typedef struct _GstGLSinkClass GstGLSinkClass; + +struct _GstGLSink { + GstElement element; + + GstPad *sinkpad; + + gint frames_displayed; + guint64 frame_time; + gint width, height; + gboolean muted; + GstBuffer *last_image; /* not thread safe ? */ + + GstClock *clock; + + /* bufferpool stuff */ + GstBufferPool *bufferpool; + GMutex *cache_lock; + GList *cache; + + /* plugins */ + GstImagePlugin* plugin; + GstImageConnection *conn; + + /* allow anybody to hook in here */ + GstImageInfo *hook; +}; + +struct _GstGLSinkClass { + GstElementClass parent_class; + + /* plugins */ + GList *plugins; +}; + + +static GType gst_glsink_get_type (void); +static void gst_glsink_class_init (GstGLSinkClass *klass); +static void gst_glsink_init (GstGLSink *sink); +/* static void gst_glsink_dispose (GObject *object); */ + +static void gst_glsink_chain (GstPad *pad, GstBuffer *buf); +static void gst_glsink_set_clock (GstElement *element, GstClock *clock); +static GstElementStateReturn gst_glsink_change_state (GstElement *element); +static GstPadLinkReturn gst_glsink_sinkconnect (GstPad *pad, GstCaps *caps); +static GstCaps * gst_glsink_getcaps (GstPad *pad, GstCaps *caps); +static GstBufferPool* gst_glsink_get_bufferpool (GstPad *pad); + +static void gst_glsink_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec); +static void gst_glsink_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); + +static void gst_glsink_release_conn (GstGLSink *sink); +static void gst_glsink_append_cache (GstGLSink *sink, GstImageData *image); +static gboolean gst_glsink_set_caps (GstGLSink *sink, GstCaps *caps); +/* bufferpool stuff */ +static GstBuffer* gst_glsink_buffer_new (GstBufferPool *pool, + gint64 location, + guint size, gpointer user_data); +static void gst_glsink_buffer_free (GstBufferPool *pool, + GstBuffer *buffer, + gpointer user_data); + +/* prototypes from plugins */ +extern GstImagePlugin* get_gl_rgbimage_plugin (void); +extern GstImagePlugin* get_gl_nvimage_plugin (void); +/* default output */ +extern void gst_glxwindow_new (GstGLSink *sink); + + +static GstPadTemplate *sink_template; + +static GstElementClass *parent_class = NULL; +/* static guint gst_glsink_signals[LAST_SIGNAL] = { 0 }; */ + +static GType +gst_glsink_get_type (void) +{ + static GType videosink_type = 0; + + if (!videosink_type) { + static const GTypeInfo videosink_info = { + sizeof(GstGLSinkClass), + NULL, + NULL, + (GClassInitFunc) gst_glsink_class_init, + NULL, + NULL, + sizeof(GstGLSink), + 0, + (GInstanceInitFunc) gst_glsink_init, + }; + videosink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstGLSink", &videosink_info, 0); + } + return videosink_type; +} + +static void +gst_glsink_class_init (GstGLSinkClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + gobject_class->set_property = gst_glsink_set_property; + gobject_class->get_property = gst_glsink_get_property; + + g_object_class_install_property (gobject_class, ARG_WIDTH, + g_param_spec_int ("width", "Width", "The video width", + G_MININT, G_MAXINT, 0, G_PARAM_READABLE)); /* CHECKME */ + g_object_class_install_property (gobject_class, ARG_HEIGHT, + g_param_spec_int ("height", "Height", "The video height", + G_MININT, G_MAXINT, 0, G_PARAM_READABLE)); /* CHECKME */ + g_object_class_install_property (gobject_class, ARG_FRAMES_DISPLAYED, + g_param_spec_int ("frames_displayed", "Frames Displayed", "The number of frames displayed so far", + G_MININT,G_MAXINT, 0, G_PARAM_READWRITE)); /* CHECKME */ + g_object_class_install_property (gobject_class, ARG_FRAME_TIME, + g_param_spec_int ("frame_time", "Frame time", "The interval between frames", + G_MININT, G_MAXINT, 0, G_PARAM_READWRITE)); /* CHECKME */ + g_object_class_install_property (gobject_class, ARG_HOOK, + g_param_spec_pointer ("hook", "Hook", "The object receiving the output", G_PARAM_WRITABLE)); + g_object_class_install_property (gobject_class, ARG_MUTE, + g_param_spec_boolean ("mute", "Mute", "mute the output ?", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, ARG_REPAINT, + g_param_spec_boolean ("repaint", "Repaint", "repaint the current frame", FALSE, G_PARAM_WRITABLE)); + + /* gobject_class->dispose = gst_glsink_dispose; */ + + gstelement_class->change_state = gst_glsink_change_state; + gstelement_class->set_clock = gst_glsink_set_clock; + + /* plugins */ + klass->plugins = NULL; + //klass->plugins = g_list_append (klass->plugins, get_xvimage_plugin ()); + klass->plugins = g_list_append (klass->plugins, get_gl_rgbimage_plugin ()); +} + + +/* + GLSink has its own Buffer management - this allows special plugins to create special memory areas for + buffer upload +*/ +static void +gst_glsink_init (GstGLSink *sink) +{ + sink->sinkpad = gst_pad_new_from_template (sink_template, "sink"); + gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad); + gst_pad_set_chain_function (sink->sinkpad, gst_glsink_chain); + gst_pad_set_link_function (sink->sinkpad, gst_glsink_sinkconnect); + gst_pad_set_getcaps_function (sink->sinkpad, gst_glsink_getcaps); + gst_pad_set_bufferpool_function (sink->sinkpad, gst_glsink_get_bufferpool); + + sink->last_image = NULL; + sink->width = 0; + sink->height = 0; + sink->muted = FALSE; + sink->clock = NULL; + GST_FLAG_SET(sink, GST_ELEMENT_THREAD_SUGGESTED); + GST_FLAG_SET (sink, GST_ELEMENT_EVENT_AWARE); + + /* create bufferpool and image cache */ + GST_DEBUG (0, "glsink: creating bufferpool"); + sink->bufferpool = gst_buffer_pool_new ( + NULL, + NULL, + (GstBufferPoolBufferNewFunction)gst_glsink_buffer_new, + NULL, + (GstBufferPoolBufferFreeFunction)gst_glsink_buffer_free, + sink); + sink->cache_lock = g_mutex_new(); + sink->cache = NULL; + + /* plugins */ + sink->plugin = NULL; + sink->conn = NULL; + + /* do initialization of default hook here */ + gst_glxwindow_new (sink); +} + +static void +gst_glsink_release_conn (GstGLSink *sink) +{ + if (sink->conn == NULL) return; + + /* free last image if any */ + if (sink->last_image != NULL) + { + gst_buffer_unref (sink->last_image); + sink->last_image = NULL; + } + /* free cache */ + g_mutex_lock (sink->cache_lock); + while (sink->cache) + { + sink->plugin->free_image ((GstImageData *) sink->cache->data); + sink->cache = g_list_delete_link (sink->cache, sink->cache); + } + g_mutex_unlock (sink->cache_lock); + + /* release connection */ + sink->conn->free_conn (sink->conn); + sink->conn = NULL; +} + +static void +gst_glsink_append_cache (GstGLSink *sink, GstImageData *image) +{ + g_mutex_lock (sink->cache_lock); + sink->cache = g_list_prepend (sink->cache, image); + g_mutex_unlock (sink->cache_lock); +} + +/* + Create a new buffer to hand up the chain. + This allows the plugins to make its own decoding buffers + */ +static GstBuffer* +gst_glsink_buffer_new (GstBufferPool *pool, gint64 location, + guint size, gpointer user_data) +{ + GstGLSink *sink; + GstBuffer *buffer; + GstImageData *image; + + sink = GST_GLSINK (user_data); + + if (sink->cache != NULL) { + g_mutex_lock (sink->cache_lock); + image = (GstImageData *) sink->cache->data; + sink->cache = g_list_delete_link (sink->cache, sink->cache); + g_mutex_unlock (sink->cache_lock); + } else { + image = sink->plugin->get_image (sink->hook, sink->conn); + } + + buffer = gst_buffer_new (); + GST_BUFFER_DATA (buffer) = image->data; + GST_BUFFER_SIZE (buffer) = image->size; + GST_BUFFER_POOL_PRIVATE (buffer) = image; + + return buffer; +} + +/* + Free a buffer that the chain doesn't need anymore. +*/ +static void +gst_glsink_buffer_free (GstBufferPool *pool, GstBuffer *buffer, gpointer user_data) +{ + GstGLSink *sink = GST_GLSINK (gst_buffer_pool_get_user_data (GST_BUFFER_BUFFERPOOL (buffer))); + + gst_glsink_append_cache (sink, (GstImageData *) GST_BUFFER_POOL_PRIVATE (buffer)); + + /* set to NULL so the data is not freed */ + GST_BUFFER_DATA (buffer) = NULL; + + gst_buffer_default_free (buffer); +} + +static GstBufferPool* +gst_glsink_get_bufferpool (GstPad *pad) +{ + GstGLSink *sink = GST_GLSINK (gst_pad_get_parent (pad)); + + return sink->bufferpool; +} + +/* + Set the caps that the application desires. + Go through the plugin list, finding the plugin that first fits the given parameters +*/ +static gboolean +gst_glsink_set_caps (GstGLSink *sink, GstCaps *caps) +{ + GList *list = ((GstGLSinkClass *) G_OBJECT_GET_CLASS (sink))->plugins; + GstImageConnection *conn = NULL; + while (list) + { + GstImagePlugin *plugin = (GstImagePlugin *) list->data; + if ((conn = plugin->set_caps (sink->hook, caps)) != NULL) + { + gst_glsink_release_conn (sink); + sink->conn = conn; + sink->plugin = plugin; + sink->conn->open_conn (sink->conn, sink->hook); + return TRUE; + } + list = g_list_next (list); + } + return FALSE; +} + +/** +Link the input video sink internally. +*/ +static GstPadLinkReturn +gst_glsink_sinkconnect (GstPad *pad, GstCaps *caps) +{ + GstGLSink *sink; + guint32 fourcc, print_format; + + sink = GST_GLSINK (gst_pad_get_parent (pad)); + + /* we are not going to act on variable caps */ + if (!GST_CAPS_IS_FIXED (caps)) + return GST_PAD_LINK_DELAYED; + + /* try to set the caps on the output */ + if (gst_glsink_set_caps (sink, caps) == FALSE) + { + return GST_PAD_LINK_REFUSED; + } + + /* remember width & height */ + gst_caps_get_int (caps, "width", &sink->width); + gst_caps_get_int (caps, "height", &sink->height); + + gst_caps_get_fourcc_int (caps, "format", &fourcc); + print_format = GULONG_FROM_LE (fourcc); + GST_DEBUG (0, "glsink: setting %08x (%4.4s) %dx%d\n", + fourcc, (gchar*)&print_format, sink->width, sink->height); + + /* emit signal */ + g_object_freeze_notify (G_OBJECT (sink)); + g_object_notify (G_OBJECT (sink), "width"); + g_object_notify (G_OBJECT (sink), "height"); + g_object_thaw_notify (G_OBJECT (sink)); + + return GST_PAD_LINK_OK; +} +static GstCaps * +gst_glsink_getcaps (GstPad *pad, GstCaps *caps) +{ + /* what is the "caps" parameter good for? */ + GstGLSink *sink = GST_GLSINK (gst_pad_get_parent (pad)); + GstCaps *ret = NULL; + GList *list = ((GstGLSinkClass *) G_OBJECT_GET_CLASS (sink))->plugins; + + while (list) + { + ret = gst_caps_append (ret, ((GstImagePlugin *) list->data)->get_caps (sink->hook)); + list = g_list_next (list); + } + + return ret; +} + +static void +gst_glsink_set_clock (GstElement *element, GstClock *clock) +{ + GstGLSink *sink = GST_GLSINK (element); + + sink->clock = clock; +} +static void +gst_glsink_chain (GstPad *pad, GstBuffer *buf) +{ + GstGLSink *sink; + GstClockTime time = GST_BUFFER_TIMESTAMP (buf); + GstBuffer *buffer; + static int frame_drops = 0; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + + sink = GST_GLSINK (gst_pad_get_parent (pad)); + + if (GST_IS_EVENT (buf)) { + GstEvent *event = GST_EVENT (buf); + + switch (GST_EVENT_TYPE (event)) { + default: + gst_pad_event_default (pad, event); + } + return; + } + GST_DEBUG (0,"glsink: clock wait: %llu %u", + GST_BUFFER_TIMESTAMP (buf), GST_BUFFER_SIZE (buf)); + +#if 0 + if (sink->clock && time != -1) { + if (time < gst_clock_get_time(sink->clock)) + { + g_warning("Frame drop (%d consecutive) !!", frame_drops); + /* we are going to drop late buffers */ + gst_buffer_unref (buf); + frame_drops++; + return; + } + frame_drops = 0; // we made it - reset time + + GstClockReturn ret; + GstClockID id = gst_clock_new_single_shot_id (sink->clock, GST_BUFFER_TIMESTAMP (buf)); + + ret = gst_element_clock_wait (GST_ELEMENT (sink), id, NULL); + gst_clock_id_free (id); + + /* we are going to drop early buffers */ + if (ret == GST_CLOCK_EARLY) { + gst_buffer_unref (buf); + return; + } + } +#endif + + /* call the notify _before_ displaying so the handlers can react */ + sink->frames_displayed++; + g_object_notify (G_OBJECT (sink), "frames_displayed"); + + if (!sink->muted) + { + /* free last_image, if any */ + if (sink->last_image != NULL) + gst_buffer_unref (sink->last_image); + if (sink->bufferpool && GST_BUFFER_BUFFERPOOL (buf) == sink->bufferpool) { + sink->plugin->put_image (sink->hook, (GstImageData *) GST_BUFFER_POOL_PRIVATE (buf)); + sink->last_image = buf; + } else { + buffer = gst_buffer_new_from_pool (gst_glsink_get_bufferpool (sink->sinkpad), + 0, GST_BUFFER_SIZE (buf)); + memcpy (GST_BUFFER_DATA (buffer), GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf) > GST_BUFFER_SIZE (buffer) ? + GST_BUFFER_SIZE (buffer) : GST_BUFFER_SIZE (buf)); + + sink->plugin->put_image (sink->hook, (GstImageData *) GST_BUFFER_POOL_PRIVATE (buffer)); + + sink->last_image = buffer; + gst_buffer_unref (buf); + } + } + +} + + +static void +gst_glsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstGLSink *sink; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_GLSINK (object)); + + sink = GST_GLSINK (object); + + switch (prop_id) { + case ARG_FRAMES_DISPLAYED: + sink->frames_displayed = g_value_get_int (value); + g_object_notify (object, "frames_displayed"); + break; + case ARG_FRAME_TIME: + sink->frame_time = g_value_get_int (value); + break; + case ARG_HOOK: + if (sink->hook) + { + sink->hook->free_info (sink->hook); + } + sink->hook = g_value_get_pointer (value); + break; + case ARG_MUTE: + sink->muted = g_value_get_boolean (value); + g_object_notify (object, "mute"); + break; + case ARG_REPAINT: + if (sink->last_image != NULL) { + sink->plugin->put_image (sink->hook, (GstImageData *) GST_BUFFER_POOL_PRIVATE (sink->last_image)); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_glsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstGLSink *sink; + + /* it's not null if we got it, but it might not be ours */ + sink = GST_GLSINK(object); + + switch (prop_id) { + case ARG_WIDTH: + g_value_set_int (value, sink->width); + break; + case ARG_HEIGHT: + g_value_set_int (value, sink->height); + break; + case ARG_FRAMES_DISPLAYED: + g_value_set_int (value, sink->frames_displayed); + break; + case ARG_FRAME_TIME: + g_value_set_int (value, sink->frame_time/1000000); + break; + case ARG_MUTE: + g_value_set_boolean (value, sink->muted); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static GstElementStateReturn +gst_glsink_change_state (GstElement *element) +{ + GstGLSink *sink; + + sink = GST_GLSINK (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + break; + case GST_STATE_READY_TO_PAUSED: + { + //g_warning("Going GST_STATE_READY_TO_PAUSED: %p", sink->conn); + } + break; + case GST_STATE_PAUSED_TO_PLAYING: + { + //g_warning("Going GST_STATE_PAUSED_TO_PLAYING: %p", sink->conn); + } + break; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + if (sink->conn) + sink->conn->close_conn (sink->conn, sink->hook); + if (sink->last_image) { + gst_buffer_unref (sink->last_image); + sink->last_image = NULL; + } + break; + case GST_STATE_READY_TO_NULL: + gst_glsink_release_conn (sink); + break; + } + + parent_class->change_state (element); + + return GST_STATE_SUCCESS; +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* create an elementfactory for the xvideosink element */ + factory = gst_element_factory_new("glsink",GST_TYPE_GLSINK, + &gst_glsink_details); + g_return_val_if_fail(factory != NULL, FALSE); + + sink_template = gst_pad_template_new ( + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + NULL); + + gst_element_factory_add_pad_template (factory, sink_template); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "glsink", + plugin_init +}; diff --git a/sys/glsink/gstglsink.h b/sys/glsink/gstglsink.h new file mode 100644 index 00000000..8cedccc1 --- /dev/null +++ b/sys/glsink/gstglsink.h @@ -0,0 +1,94 @@ +/* GStreamer + * Copyright (C) 2002 The GStreamer Team + * <gstreamer-devel@lists.sourceforge.net> + * + * 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_VIDEOSINK_H__ +#define __GST_VIDEOSINK_H__ + +#include <gst/gst.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MAX_FLIP_BUFFERS 1 + +typedef struct _GstImageInfo GstImageInfo; +struct _GstImageInfo { + gulong id; + void (*free_info) (GstImageInfo *info); +}; + + +typedef struct _GstImageData GstImageData; +struct _GstImageData { + gint size; + gchar *data; +}; + +typedef struct _GstImageConnection GstImageConnection; +struct _GstImageConnection { + void (*open_conn) (GstImageConnection *conn, GstImageInfo *info); + void (*close_conn) (GstImageConnection *conn, GstImageInfo *info); + void (*free_conn) (GstImageConnection *conn); +}; + +typedef GstCaps * (*GstImagePluginGetCapsFunc) (GstImageInfo *info); +typedef GstImageConnection * (*GstImagePluginSetCapsFunc) (GstImageInfo *info, GstCaps *caps); +typedef GstImageData* (*GstImagePluginGetImageFunc) (GstImageInfo *info, GstImageConnection *conn); +typedef void (*GstImagePluginPutImageFunc) (GstImageInfo *info, GstImageData *image); +typedef void (*GstImagePluginFreeImageFunc) (GstImageData *image); + +typedef struct _GstImagePlugin GstImagePlugin; +struct _GstImagePlugin { + GstImagePluginGetCapsFunc get_caps; + GstImagePluginSetCapsFunc set_caps; + GstImagePluginGetImageFunc get_image; + GstImagePluginPutImageFunc put_image; + GstImagePluginFreeImageFunc free_image; +}; + +typedef struct _GstGLImageInfo GstGLImageInfo; +/* stuff about our window grouped together */ +struct _GstGLImageInfo { + GstImageInfo info; + Display *dpy; + int screen; + Window win; + GLXContext ctx; + XSetWindowAttributes attr; + Bool fs; + //XF86VidModeModeInfo deskMode; + int x, y; + unsigned int width, height; + unsigned int depth; + /* window specific from here */ + GstElement *sink; + gulong handler_id; + float rotX,rotY,zoom; +}; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_VIDEOSINK_H__ */ diff --git a/sys/glsink/gstglxwindow.c b/sys/glsink/gstglxwindow.c new file mode 100644 index 00000000..96ec673a --- /dev/null +++ b/sys/glsink/gstglxwindow.c @@ -0,0 +1,239 @@ +/* gcc -ansi -pedantic on GNU/Linux causes warnings and errors + * unless this is defined: + * warning: #warning "Files using this header must be compiled with _SVID_SOURCE or _XOPEN_SOURCE" + */ +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 1 +#endif + +#include <X11/Xlib.h> +#include <GL/glx.h> +#include <GL/gl.h> +#include <GL/glu.h> +#include "gstglsink.h" +#include <string.h> /* strncmp */ + +/* attributes for a single buffered visual in RGBA format with at least + * 4 bits per color and a 16 bit depth buffer */ +static int attrListSgl[] = +{ + GLX_RGBA, GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +/* attributes for a double buffered visual in RGBA format with at least + * 4 bits per color and a 16 bit depth buffer */ +static int attrListDbl[] = + { + GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None + }; + + +GLfloat LightAmbient[] = { 0.1, 0.1, 0.1, 1.0 }; /* reddish ambient light */ +GLfloat LightDiffuse[] = { 0.6, 0.6, 0.6, 1.0 }; /* bluish diffuse light. */ +GLfloat LightPosition[] = { 1.5, 1.5, 1.5, 0.0 }; /* position */ + + +static void +gst_glxwindow_free (GstImageInfo *info) +{ + GstGLImageInfo *window = (GstGLImageInfo *) info; + + g_signal_handler_disconnect (window->sink, window->handler_id); + + if (window->ctx) + { + if (!glXMakeCurrent(window->dpy, None, NULL)) + { + printf("Could not release drawing context.\n"); + } + glXDestroyContext(window->dpy, window->ctx); + window->ctx = NULL; + } +#if 0 + /* switch back to original desktop resolution if we were in fs */ + if (GLWin.fs) + { + XF86VidModeSwitchToMode(GLWin.dpy, GLWin.screen, &GLWin.deskMode); + XF86VidModeSetViewPort(GLWin.dpy, GLWin.screen, 0, 0); + } +#endif + XCloseDisplay (window->dpy); + g_free (window); +} + +static void +gst_glxwindow_callback(GObject *object, GParamSpec *pspec, GstGLImageInfo *data) +{ + XWindowAttributes attr; + XGetWindowAttributes(data->dpy, data->win, &attr); + + if (strncmp (pspec->name, "width", 5) == 0 || strncmp (pspec->name, "height", 6) == 0) + { + gint w = 0; + gint h = 0; + g_object_get (object, "width", &w, NULL); + g_object_get (object, "height", &h, NULL); + if (w != attr.width || h != attr.height) + { + attr.width = w; + attr.height = h; + XResizeWindow (data->dpy, data->win, attr.width, attr.height); + XMapRaised (data->dpy, data->win); + + // resize OpenGL + g_warning("resizing in OpenGL"); + glViewport(0, 0, (GLint) w, (GLint) h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + GLfloat aspect = (GLfloat) h / (GLfloat) w; + glFrustum(-1.0, 1.0, -aspect, aspect, 5.0, 500.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0, -3.0, -50.0); + } + } + if (attr.width != data->width || attr.height != data->height) + { + data->width = attr.width; + data->height = attr.height; + } + +} + +void +gst_glxwindow_new (GstElement *sink) +{ + XGCValues values; + GstGLImageInfo *new; + int glxMajorVersion, glxMinorVersion; + XSetWindowAttributes attrib; + XVisualInfo *vi; + Atom wmDelete; + Window winDummy; + unsigned int borderDummy; + Colormap cmap; + char* title = "GLSink (experimental)"; + + new = g_new0 (GstGLImageInfo, 1); + + if (sink == NULL) + { + sink = gst_element_factory_make ("glsink", "glsink"); + g_assert (sink != NULL); + } + + /* fill in the ImageInfo */ + new->info.id = GST_MAKE_FOURCC ('X', 'l', 'i', 'b'); + new->info.free_info = gst_glxwindow_free; + + new->dpy = XOpenDisplay (NULL); + if (!new->dpy) { + g_warning ("open display failed!\n"); + g_free (new); + return; + } + new->screen = DefaultScreen(new->dpy); + /* get an appropriate visual */ + vi = glXChooseVisual(new->dpy, new->screen, attrListDbl); + if (vi == NULL) + { + vi = glXChooseVisual(new->dpy, new->screen, attrListSgl); + g_warning("Only Singlebuffered Visual!\n"); + } + else + { + g_warning("Got Doublebuffered Visual!\n"); + } + glXQueryVersion(new->dpy, &glxMajorVersion, &glxMinorVersion); + g_warning("glX-Version %d.%d\n", glxMajorVersion, glxMinorVersion); + + /* create a GLX context */ + new->ctx = glXCreateContext(new->dpy, vi, 0, GL_TRUE); + /* create a color map */ + cmap = XCreateColormap(new->dpy, RootWindow(new->dpy, vi->screen), + vi->visual, AllocNone); + new->attr.colormap = cmap; + new->attr.border_pixel = 0; + + /* set sizes */ + new->x = 0; + new->y = 0; + new->width = 10; + new->height = 10; + + new->rotX = 0; + new->rotY = 0; + new->zoom = 2; + + { + /* create a window in window mode*/ + new->attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | + StructureNotifyMask; + new->win = XCreateWindow(new->dpy, RootWindow(new->dpy, vi->screen), + new->x, new->y, new->width, new->height, 0, vi->depth, InputOutput, vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &new->attr); + if (!new->win) + { + g_warning ("create window failed\n"); + g_free (new); + return; + } + /* only set window title and handle wm_delete_events if in windowed mode */ + wmDelete = XInternAtom(new->dpy, "WM_DELETE_WINDOW", True); + XSetWMProtocols(new->dpy, new->win, &wmDelete, 1); + XSetStandardProperties(new->dpy, new->win, title, + title, None, NULL, 0, NULL); + XMapRaised(new->dpy, new->win); + } + /* connect the glx-context to the window */ + glXMakeCurrent(new->dpy, new->win, new->ctx); + XGetGeometry(new->dpy, new->win, &winDummy, &new->x, &new->y, + &new->width, &new->height, &borderDummy, &new->depth); + printf("Depth %d\n", new->depth); + if (glXIsDirect(new->dpy, new->ctx)) + g_warning("Congrats, you have Direct Rendering!\n"); + else + g_warning("Sorry, no Direct Rendering possible!\n"); + + g_warning("Initializing OpenGL parameters"); + /* initialize OpenGL drawing */ + glEnable(GL_DEPTH_TEST); + //glShadeModel(GL_SMOOTH); + + glEnable(GL_TEXTURE_2D); + glDisable(GL_CULL_FACE); + glClearDepth(1.0f); + glClearColor(0, 0, 0, 0); + + glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient); /* add lighting. (ambient) */ + glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse); /* add lighting. (diffuse). */ + glLightfv(GL_LIGHT0, GL_POSITION,LightPosition); /* set light position. */ + + //glEnable(GL_LIGHT0); // Quick And Dirty Lighting (Assumes Light0 Is Set Up) + //glEnable(GL_LIGHTING); // Enable Lighting + glDisable(GL_COLOR_MATERIAL); // Enable Material Coloring + glEnable(GL_AUTO_NORMAL); // let OpenGL generate the Normals + + glDisable(GL_BLEND); + + glPolygonMode(GL_FRONT, GL_FILL); + glPolygonMode(GL_BACK, GL_FILL); + + XSelectInput (new->dpy, new->win, ExposureMask | StructureNotifyMask); + + g_object_set (sink, "hook", new, NULL); + new->sink = sink; + new->handler_id = g_signal_connect (sink, "notify", G_CALLBACK (gst_glxwindow_callback), new); +} + |