mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-09 19:09:41 +00:00
429 lines
12 KiB
C
429 lines
12 KiB
C
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#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;
|
|
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);
|
|
}
|