#ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* 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 #include #include #include #include #include #include //#include #include #include #include // 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; printf ("Checking NVidia OpenGL extensions.\n"); 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, context %p !\n", glXGetCurrentContext ()); /* we don't handle these image information */ if (xinfo == NULL) { printf ("Invalid XInfo struct !\n"); 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 ("nvimage_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 be in the same thread as put_image 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 //glClearColor(0,0.5, 0.3,1.0); // a test color glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); glTranslatef (0.0, 0.0, -5.0); glDisable (GL_TEXTURE_2D); if (xinfo->info.demo) { //g_print("Putting image, context is %p\n", glXGetCurrentContext()); glTranslatef (0.0, 0.0, -5.0); // make it avoid the clipping plane, zoom 2.0 instead 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 > 2.0) xinfo->zoomdir = -0.01; if (xinfo->zoom < 1.0) 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 = aspect; //g_print("Drawing vertices, context is %p\n", glXGetCurrentContext()); glNormal3f (0, -1, 0); glMultiTexCoord2fARB (GL_TEXTURE0_ARB, 0, 0); glMultiTexCoord2fARB (GL_TEXTURE1_ARB, 0, 0); glMultiTexCoord2fARB (GL_TEXTURE2_ARB, 0, 0); glVertex3f (-hor, 1, 0); glMultiTexCoord2fARB (GL_TEXTURE0_ARB, 0, ymax); glMultiTexCoord2fARB (GL_TEXTURE1_ARB, 0, ymax); glMultiTexCoord2fARB (GL_TEXTURE2_ARB, 0, ymax); glVertex3f (-hor, -1, 0); glMultiTexCoord2fARB (GL_TEXTURE0_ARB, xmax, ymax); glMultiTexCoord2fARB (GL_TEXTURE1_ARB, xmax, ymax); glMultiTexCoord2fARB (GL_TEXTURE2_ARB, xmax, ymax); glVertex3f (hor, -1, 0); glMultiTexCoord2fARB (GL_TEXTURE0_ARB, xmax, 0); glMultiTexCoord2fARB (GL_TEXTURE1_ARB, xmax, 0); glMultiTexCoord2fARB (GL_TEXTURE2_ARB, xmax, 0); glVertex3f (hor, 1, 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 glsink from GStreamer\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); } g_print ("Done\n"); } 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); }