diff options
Diffstat (limited to 'sys/glsink/gstgl_nvimage.c')
-rw-r--r-- | sys/glsink/gstgl_nvimage.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/sys/glsink/gstgl_nvimage.c b/sys/glsink/gstgl_nvimage.c new file mode 100644 index 00000000..e7ee1b62 --- /dev/null +++ b/sys/glsink/gstgl_nvimage.c @@ -0,0 +1,404 @@ +#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> + + +#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 <math.h> + +// too lazy to write an API for this ;) +#include "regcomb_yuvrgb.c" + +#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 (!GL_ARB_multitexture_Init()) return FALSE; + if (!GL_EXT_paletted_texture_Init()) return FALSE; + if (!GL_NV_register_combiners_Init()) return FALSE; + +#if 0 + if (display == NULL) + return FALSE; + if (Success == XvQueryExtension (display,&ver,&rel,&req,&ev,&err)) + return TRUE; +#endif + + return TRUE; +} + +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); + + g_warning("nvimage get caps called !\n"); + /* 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/x-raw-yuv", + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('Y', 'V', '1', '2')), + "width", GST_PROPS_INT_RANGE (0, 1024), + "height", GST_PROPS_INT_RANGE (0, 1024)) + ); + g_warning("nvimage returns caps !\n"); + 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); + + // maybe I should a bit more checking here, e.g. maximum size smaller than maximum texture extents + if (format != GST_MAKE_FOURCC ('Y', 'V', '1', '2')) + { + GST_DEBUG ("GL_NVImage: Format is invalid !\n"); + return 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; + } + + GST_DEBUG ("GL_NVImage: caps %p are ok, creating image", caps); + 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); + + /* checks */ + if (xinfo == NULL) return NULL; + if (nvconn == NULL) return NULL; + + // I should also check the current GLX context ! + // Ah, Don't have to, I am guarantueed to always be in the same thread + + image = g_new0(GstNvImage, 1); + + image->data.size = nvconn->w * nvconn->h * 3/2; + + //g_warning("Allocating %d bytes from main memory !", image->data.size); + 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); + + int img_width = im->conn->w; + int img_height = im->conn->h; + int uv_width = img_width >> 1; + int uv_height = img_height >> 1; + + unsigned char *buf_y = im->data.data; + unsigned char *buf_v = (buf_y + img_width * img_height); + unsigned char *buf_u = buf_v + ((img_width/2) * (img_height/2)); + + /* checks omitted for speed (and lazyness), do we need them? */ + g_assert (xinfo != NULL); + + // both upload the video, and redraw the screen + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0, 0.0, -25.0); + glEnable(GL_TEXTURE_2D); + + if (xinfo->info.demo) + { + + glRotatef(180.0*sin(xinfo->rotX),1,0,0); + glRotatef(180.0*cos(xinfo->rotY),0,1,0); + + xinfo->rotX += 0.01; + xinfo->rotY -= 0.015; + float zoom = xinfo->zoom; + glScalef(zoom,zoom,zoom); + //glScalef(0.1,0.1,0.1); + + if (xinfo->zoom > 1.0) + xinfo->zoomdir = -0.01; + + if (xinfo->zoom < 0.5) + xinfo->zoomdir = 0.01; + + xinfo->zoom += xinfo->zoomdir; + } + + //Draws the surface rectangle + + if (Ywidth != im->conn->w || Yheight != im->conn->h) + { + Ywidth = im->conn->w; Yheight = im->conn->h; UVwidth = im->conn->w/2; UVheight = im->conn->h/2; + Initialize_Backend(Ywidth,Yheight,UVwidth,UVheight,GL_LINEAR); + } + + LoadYUVPlanes(Yhandle,Uhandle,Vhandle,img_width,img_height,uv_width,uv_height,buf_y,buf_u,buf_v); + float xmax = (float)(im->conn->w-1)/tex_xsize; + float ymax = (float)(im->conn->h-1)/tex_ysize; + + /* Upload the texture here */ + //g_warning("PUTTING IMAGE %f %f %d %d\n", xmax, ymax, tex_xsize, tex_ysize); + + //glColor4f(1,1,1,1); // do NOT set a color here ! Done by Initialize_Backend, or actually SetConst ! + glBegin(GL_QUADS); + + float aspect = img_width/(float)img_height; + float hor = 4 * aspect; + + glNormal3f(0, -1, 0); + glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,0); + glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,0); + glMultiTexCoord2fARB(GL_TEXTURE2_ARB,0,0); + glVertex3f(-hor,4,0); + + glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,ymax); + glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,ymax); + glMultiTexCoord2fARB(GL_TEXTURE2_ARB,0,ymax); + glVertex3f(-hor,-4,0); + + glMultiTexCoord2fARB(GL_TEXTURE0_ARB,xmax,ymax); + glMultiTexCoord2fARB(GL_TEXTURE1_ARB,xmax,ymax); + glMultiTexCoord2fARB(GL_TEXTURE2_ARB,xmax,ymax); + glVertex3f(hor,-4,0); + + glMultiTexCoord2fARB(GL_TEXTURE0_ARB,xmax,0); + glMultiTexCoord2fARB(GL_TEXTURE1_ARB,xmax,0); + glMultiTexCoord2fARB(GL_TEXTURE2_ARB,xmax,0); + glVertex3f(hor,4,0); + + glEnd(); + + if (xinfo->info.dumpvideo) + { + static int framenr = 0; + char capfilename[255]; + static guint8 *cap_image_data = NULL, *cap_image_data2 = NULL; + int i; + + // hmmmm, is this reentrant ?! + if (cap_image_data == NULL) + cap_image_data = (guint8 *)malloc(img_width * img_height * 3); + + if (cap_image_data2 == NULL) + cap_image_data2 = (guint8 *)malloc(img_width * img_height * 3); + + printf("Recording frame #%d\n", framenr); + glReadPixels(0,0,img_width,img_height,GL_RGB,GL_UNSIGNED_BYTE,cap_image_data); + // invert the pixels + for (i = 0; i < img_height; i++) + memcpy(cap_image_data2 + i * img_width * 3, cap_image_data + (img_height-1-i) * img_width * 3, img_width*3); + + sprintf(capfilename, "cap%04d.ppm", framenr); + FILE *outfile = fopen(capfilename, "wb"); + if (outfile != NULL) + { + fprintf(outfile, "P6\n"); + fprintf(outfile,"# created by raw_zb\n"); + fprintf(outfile,"%d %d\n",img_width,img_height); + fprintf(outfile,"255\n"); + fwrite(cap_image_data2, sizeof(char), img_width*img_height*3, outfile); + fclose(outfile); + } + framenr++; + } + + + 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); + + g_print("Opening NVidia connection; OpenGL on Nvidia, using register combiners.\n"); + { + Ywidth = TEX_XSIZE; Yheight = TEX_YSIZE; UVwidth = TEX_XSIZE/2; UVheight = TEX_YSIZE/2; + Initialize_Backend(Ywidth,Yheight,UVwidth,UVheight,GL_LINEAR); + } +} + +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); +} + |