summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--doc/README.opengl51
-rw-r--r--src/video_out/Makefile.am4
-rw-r--r--src/video_out/video_out_opengl.c2045
4 files changed, 1266 insertions, 835 deletions
diff --git a/ChangeLog b/ChangeLog
index 07fa37dfb..5ef43aee8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,5 @@
xine-lib (1-xxx)
+ * Rewrote OpenGL output plugin.
* Fixed segfault when seeking with the "xvmc" and "xxmc" plugins playing
files with IDCT / mocomp XvMC acceleration.
* polypaudio sound server support
diff --git a/doc/README.opengl b/doc/README.opengl
index 85fd68c85..99bbf93f9 100644
--- a/doc/README.opengl
+++ b/doc/README.opengl
@@ -1,10 +1,3 @@
-Please Note:
-
-The 'opengl' output plugin is still in alpha stage.
-Expect crashes and strange behaviour.
-Most times it seems to run fine, though.
-
-
Startup
-------
@@ -13,32 +6,38 @@ different color depth than you would prefer. Choose an appropriate visual
with 'glxinfo' and select it with '-V opengl --visual xyz' in this case.
-Known Problems
---------------
+Problems you might encounter
+----------------------------
-* Sometimes I only see a black screen and no image
-* Sometimes xine crashes when changing the stream or the position in the stream
+* Output is extremely slow
- This seems to relate to some race conditions that are still present
- in the current code. This will change in time.
+ Run 'glxinfo' and check the third output line saying 'direct rendering:'
+ If it doesn't say 'Yes', you are not running an accelerated OpenGL setup.
+ Check your installation.
+ Image_Pipeline based rendering is typically slower than 2D_Textures.
+ Note that OpenGL output is always slower than XVideo, and often faster
+ than X shared memory.
- Jun. 25, 2002: I think this issue is resolved now. If you still encounter
- any problems, please let me know. Otherwise I will remove this statement
- in the future.
+* xine complains about a memory leak
-* There is a (small) memory leak
-
- This is known and - unfortunately - cannot be resolved right now.
+ This is known and - unfortunately - cannot be solved without changing
+ the user interface code. This will happen at least for xine-ui in the
+ near future.
Background: The output plugin does not get notified, when the XWindow
- is destroyed. But even when it would be notified, this wouldn't help,
- as the OpenGL context could only be destroyed by the render thread.
+ is destroyed, and the OpenGL context can only be destroyed while the
+ drawable is still accessible.
+
-* Expose events just display a black image when playback is paused.
+Known issues
+------------
- This is known and cannot be resolved right now.
- Background: The OpenGL context can only be used by the render thread and
- not by the GUI thread. I tried to use a second context within the same
- drawable, but this destabilized the plugin to an unusable state.
+* GL_BGRA (used on little endian machines, i.e. ix86) needs an extension
+ check. This has not been implemented yet.
+* Big endian machines have not been checked yet.
+* VO_CAP_UNSCALED_OVERLAY reduces performance a lot during the first few
+ frames.
+* Cropping is not yet implemented - this is done by video_out.c
+* XINE_GUI_SEND_WILL_DESTROY_DRAWABLE not yet sent by any GUI, thus untested
Matthias Hopf <mat@mshopf.de>
diff --git a/src/video_out/Makefile.am b/src/video_out/Makefile.am
index 11b272ac2..f0258c24b 100644
--- a/src/video_out/Makefile.am
+++ b/src/video_out/Makefile.am
@@ -112,9 +112,9 @@ xineplug_vo_out_xxmc_la_LIBADD = $(XXMC_LIB) $(XV_LIB) $(X_LIBS) -lXext $(XINE_L
xineplug_vo_out_xxmc_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_vo_out_opengl_la_SOURCES = yuv2rgb.c yuv2rgb_mmx.c yuv2rgb_mlib.c \
- alphablend.c video_out_opengl.c
+ alphablend.c video_out_opengl.c $(X11OSD)
xineplug_vo_out_opengl_la_LIBADD = $(MLIB_LIBS) $(OPENGL_LIBS) $(GLUT_LIBS) \
- $(GLU_LIBS) $(X_LIBS) $(XINE_LIB)
+ $(GLU_LIBS) $(X_LIBS) -lXext $(XINE_LIB)
xineplug_vo_out_opengl_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_vo_out_syncfb_la_SOURCES = alphablend.c video_out_syncfb.c
diff --git a/src/video_out/video_out_opengl.c b/src/video_out/video_out_opengl.c
index d6365771a..c440002e3 100644
--- a/src/video_out/video_out_opengl.c
+++ b/src/video_out/video_out_opengl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2000-2004 the xine project
+ * Copyright (C) 2000-2003 the xine project
*
* This file is part of xine, a free video player.
*
@@ -17,40 +17,31 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: video_out_opengl.c,v 1.41 2004/10/09 06:45:48 mroi Exp $
+ * $Id: video_out_opengl.c,v 1.42 2004/11/23 15:01:23 mshopf Exp $
*
- * video_out_glut.c, glut based OpenGL rendering interface for xine
- * Matthias Hopf <mat@mshopf.de>
+ * video_out_opengl.c, OpenGL based interface for xine
*
- * Based on video_out_xshm.c (1.70) and video_out_xv.c (1.108)
+ * Written by Matthias Hopf <mat@mshopf.de>,
+ * based on the xshm and xv video output plugins.
+ *
*/
-/*
- * TODO:
- *
- * - BUG: xitk is creating images in the wrong visual type on e.g. SGI
- * (due to driver visual selection?). this is a xitk issue.
- * this creates a strange looking gui interface and all dialogs fail
- * (X_PutImage: BadMatch).
- * - Rendering method to be chosen on runtime
- * - glut autoconf detection buggy
- * - Check extensions (GL_BGRA)
- * - Color conversion in hardware?
- * - Video extension
- * - ColorMatrix (OpenGL-1.2 or SGI_color_matrix) ?possible? don't think so
- * - Alpha Blending for overlays using texture hardware
- */
+/* #define LOG */
+#define LOG_MODULE "video_out_opengl"
-#if 1 /* set to 1 for debugging messages */
-# define DEBUGF(x) fprintf x
-#else
-# define DEBUGF(x) ((void) 0)
-#endif
+#define BYTES_PER_PIXEL 4
+#define NUM_FRAMES_BACKLOG 4 /* Allow thread some time to render frames */
+#define SECONDS_PER_CYCLE 60 /* Animation parameters */
+#define CYCLE_FACTOR1 3
+#define CYCLE_FACTOR2 5
-#define USE_SPHERE 0 /* 1 for some fun! */ /* FIXME: untested */
-#define USE_TEXTURES 1 /* 1 Use texture hardware 0 glDrawPixels */
+#ifdef NDEBUG
+#define CHECKERR(a) ((void)0)
+#else
+#define CHECKERR(a) do { int i = glGetError (); if (i != GL_NO_ERROR) fprintf (stderr, " *** %s: 0x%x = %s\n", a, i, gluErrorString (i)); } while (0)
+#endif
#ifdef HAVE_CONFIG_H
@@ -61,118 +52,106 @@
#include <stdlib.h>
#include <string.h>
#include <math.h>
-
-#include <X11/Xlib.h>
-
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <sys/time.h>
-
+#include <errno.h>
#include <pthread.h>
-#include <netinet/in.h>
#include <GL/gl.h>
#include <GL/glx.h>
-#ifdef HAVE_GLUT
-#include <GL/glut.h>
-#else
-#define USE_SPHERE 0 /* unable to do that w/o glut */
-#ifdef HAVE_GLU
#include <GL/glu.h>
-#endif
-#endif
#include "xine.h"
#include "video_out.h"
+
#include "xine_internal.h"
#include "alphablend.h"
#include "yuv2rgb.h"
#include "xineutils.h"
-#include "vo_scale.h"
-
-
-#define STRIPE_HEIGHT 16
-#define BYTES_PER_PIXEL 4
+#include "x11osd.h"
-# if (BYTES_PER_PIXEL != 4)
+#if (BYTES_PER_PIXEL != 4)
/* currently nothing else is supported */
# error "BYTES_PER_PIXEL bad"
-# endif
+#endif
+/* TODO: haven't checked bigendian so far... */
#ifdef WORDS_BIGENDIAN
# define RGB_TEXTURE_FORMAT GL_RGBA
# define YUV_FORMAT MODE_32_BGR
# define YUV_SWAP_MODE 1
#else
-/* FIXME: check extension */
+/* TODO: GL_BGRA needs extension check */
+/* TODO: GL_RGBA / MODE_32_BGR does not need extension, but might be slower
+ * on ix86, and overlays would use wrong pixel order */
+/* TODO: MODE_32_BGR may not work with some accelerated yuv2rgb routines */
# define RGB_TEXTURE_FORMAT GL_BGRA
# define YUV_FORMAT MODE_32_RGB
# define YUV_SWAP_MODE 0
#endif
+#define MY_2PI 6.2831853
-typedef struct opengl_frame_s {
- vo_frame_t vo_frame;
-
- /* frame properties as delivered by the decoder: */
- int width, height;
- double ratio;
- int format, flags;
-
- /* opengl only data */
- uint8_t *rgb_dst;
- yuv2rgb_t *yuv2rgb;
- uint8_t *chunk[3];
- int stripe_inc;
- uint8_t *texture;
-} opengl_frame_t;
-
-typedef struct opengl_driver_s {
-
- vo_driver_t vo_driver;
-
- vo_scale_t sc;
- vo_overlay_t *overlay;
- config_values_t *config;
-
- /* X11 / Xv related stuff */
- Display *display;
- int screen;
- Drawable drawable;
+typedef struct {
+ vo_frame_t vo_frame;
- /* OpenGL related */
- GLXContext context;
- volatile int context_state; /* is the context ok, or reload? */
- XVisualInfo *vinfo;
- pthread_t renderthread;
+ int width, height, format, flags;
+ double ratio;
- /* current texture size - this is not frame dependend! */
- int texture_width, texture_height;
+ uint8_t *chunk[4]; /* mem alloc by xmalloc_aligned */
+ uint8_t *rgb, *rgb_dst;
+
+ yuv2rgb_t *yuv2rgb; /* yuv2rgb converter set up for this frame */
- /* last frame delivered from the decoder for frame change detection */
- int last_width;
- int last_height;
- double last_ratio;
+} opengl_frame_t;
-#if 0
- /* ideal size */
- int ideal_width, ideal_height;
- int user_ratio;
- /* gui size */
- int gui_width, gui_height;
- int gui_x, gui_y, gui_win_x, gui_win_y;
+/* RENDER_DRAW to RENDER_SETUP are asynchronous actions, but later actions
+ * imply former actions -> only check '>' on update */
+/* RENDER_CREATE and later are synchronous actions and override async ones */
+enum render_e { RENDER_NONE=0, RENDER_DRAW, RENDER_CLEAN, RENDER_SETUP,
+ RENDER_CREATE, RENDER_VISUAL, RENDER_RELEASE, RENDER_EXIT };
- /* output size */
- int output_width, output_height;
- int output_xoffset, output_yoffset;
-#endif
- /* software yuv2rgb related */
- int yuv2rgb_brightness;
- uint8_t *yuv2rgb_cmap;
- yuv2rgb_factory_t *yuv2rgb_factory;
+typedef struct {
- xine_t *xine;
+ vo_driver_t vo_driver;
+ vo_scale_t sc;
+
+ /* X11 related stuff */
+ Display *display;
+ int screen;
+ Drawable drawable;
+
+ /* Render thread */
+ pthread_t render_thread;
+ enum render_e render_action;
+ int render_frame_changed;
+ pthread_mutex_t render_action_mutex;
+ pthread_cond_t render_action_cond;
+ pthread_cond_t render_return_cond;
+ int last_width, last_height;
+
+ /* Render parameters */
+ int render_fun_id;
+ int render_min_fps;
+ int render_double_buffer;
+ int gui_width, gui_height;
+
+ /* OpenGL state */
+ GLXContext context;
+ XVisualInfo *vinfo;
+ int tex_width, tex_height; /* independend of frame */
+
+ int yuv2rgb_brightness;
+ int yuv2rgb_contrast;
+ int yuv2rgb_saturation;
+ uint8_t *yuv2rgb_cmap;
+ yuv2rgb_factory_t *yuv2rgb_factory;
+
+ /* Frame state */
+ opengl_frame_t *frame[NUM_FRAMES_BACKLOG];
+ x11osd *xoverlay;
+ int ovl_changed;
+
+ xine_t *xine;
} opengl_driver_t;
typedef struct {
@@ -181,830 +160,1284 @@ typedef struct {
xine_t *xine;
} opengl_class_t;
-
-enum { CONTEXT_BAD = 0, CONTEXT_SAME_DRAWABLE, CONTEXT_RELOAD, CONTEXT_SET };
+typedef void *(*thread_run_t)(void *);
/*
- * and now, the driver functions
+ * Render functions
*/
-
-static uint32_t opengl_get_capabilities (vo_driver_t *this_gen) {
- return VO_CAP_YV12 | VO_CAP_YUY2;
+/* Static 2d texture based display */
+static void render_tex2d (opengl_driver_t *this, opengl_frame_t *frame) {
+ int x1, x2, y1, y2;
+ float tx, ty;
+
+ /* Calc texture/rectangle coords */
+ x1 = this->sc.output_xoffset;
+ y1 = this->sc.output_yoffset;
+ x2 = x1 + this->sc.output_width;
+ y2 = y1 + this->sc.output_height;
+ tx = (float) frame->width / this->tex_width;
+ ty = (float) frame->height / this->tex_height;
+ /* Draw quad */
+ glBegin (GL_QUADS);
+ glTexCoord2f (tx, ty); glVertex2i (x2, y2);
+ glTexCoord2f (0, ty); glVertex2i (x1, y2);
+ glTexCoord2f (0, 0); glVertex2i (x1, y1);
+ glTexCoord2f (tx, 0); glVertex2i (x2, y1);
+ glEnd ();
}
+/* Static image pipline based display */
+static void render_draw (opengl_driver_t *this, opengl_frame_t *frame) {
+ glPixelZoom (((float)this->sc.output_width) / frame->width,
+ - ((float)this->sc.output_height) / frame->height);
+ glRasterPos2i (this->sc.output_xoffset, this->sc.output_yoffset);
+ glDrawPixels (frame->width, frame->height, RGB_TEXTURE_FORMAT,
+ GL_UNSIGNED_BYTE, frame->rgb);
+}
-static void opengl_frame_proc_slice (vo_frame_t *vo_img, uint8_t **src) {
- opengl_frame_t *frame = (opengl_frame_t *) vo_img ;
+/* Animated spinning cylinder */
+#define CYL_TESSELATION 128
+#define CYL_WIDTH 2.5
+#define CYL_HEIGHT 3.0
+static void render_cyl (opengl_driver_t *this, opengl_frame_t *frame) {
+ int i;
+ float off;
+ float tx, ty;
+ struct timeval curtime;
- vo_img->proc_called = 1;
-
-/* DEBUGF ((stderr, "*** %p: frame_copy src %p/%p/%p to %p\n", frame, src[0], src[1], src[2], frame->rgb_dst)); */
-
- if ((char *) frame->rgb_dst + frame->stripe_inc > (char *) frame->texture
- + frame->width * frame->height
- * BYTES_PER_PIXEL) {
- /* frame->rgb_dst can walk off the end of the frame's image data when
- * xshm_frame_field, which resets it, is not called properly. This can
- * happen with corrupt MPEG streams
- * FIXME: Is there a way to ensure frame->rgb_dst validity?
- */
- DEBUGF ((stderr, "video_out_opengl: corrupt value of frame->rgb_dst -- skipping\n"));
- return;
- }
- if (frame->format == XINE_IMGFMT_YV12) {
- frame->yuv2rgb->yuv2rgb_fun (frame->yuv2rgb, frame->rgb_dst,
- src[0], src[1], src[2]);
- } else {
-
- frame->yuv2rgb->yuy22rgb_fun (frame->yuv2rgb, frame->rgb_dst,
- src[0]);
-
- }
-
- frame->rgb_dst += frame->stripe_inc;
-/* DEBUGF ((stderr, "frame_copy done\n")); */
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ /* Calc timing + texture coords */
+ gettimeofday (&curtime, NULL);
+ off = ((curtime.tv_sec % SECONDS_PER_CYCLE) + curtime.tv_usec * 1e-6)
+ * (360.0 / SECONDS_PER_CYCLE);
+ tx = (float) frame->width / this->tex_width;
+ ty = (float) frame->height / this->tex_height;
+
+ /* Spin it */
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ glTranslatef (0, 0, -10);
+ glRotatef (off * CYCLE_FACTOR1, 1, 0, 0);
+ glRotatef (off, 0, 0, 1);
+ glRotatef (off * CYCLE_FACTOR2, 0, 1, 0);
+
+ /* Note that this is not aspect ratio corrected */
+ glBegin (GL_QUADS);
+ for (i = 0; i < CYL_TESSELATION; i++) {
+ float x1 = CYL_WIDTH * sin (i * MY_2PI / CYL_TESSELATION);
+ float x2 = CYL_WIDTH * sin ((i+1) * MY_2PI / CYL_TESSELATION);
+ float z1 = CYL_WIDTH * cos (i * MY_2PI / CYL_TESSELATION);
+ float z2 = CYL_WIDTH * cos ((i+1) * MY_2PI / CYL_TESSELATION);
+ float tx1 = tx * i / CYL_TESSELATION;
+ float tx2 = tx * (i+1) / CYL_TESSELATION;
+ glTexCoord2f (tx1, 0); glVertex3f (x1, CYL_HEIGHT, z1);
+ glTexCoord2f (tx2, 0); glVertex3f (x2, CYL_HEIGHT, z2);
+ glTexCoord2f (tx2, ty); glVertex3f (x2, -CYL_HEIGHT, z2);
+ glTexCoord2f (tx1, ty); glVertex3f (x1, -CYL_HEIGHT, z1);
+ }
+ glEnd ();
}
-static void opengl_frame_field (vo_frame_t *vo_img, int which_field) {
-
- opengl_frame_t *frame = (opengl_frame_t *) vo_img ;
+/* Animated spinning environment mapped torus */
+#define DIST_FACTOR 16.568542 /* 2 * (sqrt(2)-1) * 20 */
+static void render_env_tor (opengl_driver_t *this, opengl_frame_t *frame) {
+ float off;
+ float x1, y1, x2, y2, tx, ty;
+ struct timeval curtime;
+
+ /* No glClear() necessary - rendering background w/ depth test success */
+
+ /* Calc timing + texture coords */
+ gettimeofday (&curtime, NULL);
+ off = ((curtime.tv_sec % SECONDS_PER_CYCLE) + curtime.tv_usec * 1e-6)
+ * (360.0 / SECONDS_PER_CYCLE);
+ /* Fovy is angle in y direction */
+ x1 = (this->sc.output_xoffset - this->gui_width/2.0)
+ * DIST_FACTOR / this->gui_height;
+ x2 = (this->sc.output_xoffset+this->sc.output_width - this->gui_width/2.0)
+ * DIST_FACTOR / this->gui_height;
+ y1 = (this->sc.output_yoffset - this->gui_height/2.0)
+ * DIST_FACTOR / this->gui_height;
+ y2 = (this->sc.output_yoffset+this->sc.output_height - this->gui_height/2.0)
+ * DIST_FACTOR / this->gui_height;
+
+ tx = (float) frame->width / this->tex_width;
+ ty = (float) frame->height / this->tex_height;
+
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+
+ /* Draw background, Y swapped */
+ glMatrixMode (GL_TEXTURE);
+ glPushMatrix ();
+ glLoadIdentity ();
+ glDepthFunc (GL_ALWAYS);
+
+ glBegin (GL_QUADS);
+ glColor3f (1, 1, 1);
+ glTexCoord2f (tx, 0); glVertex3f (x2, y2, -20);
+ glTexCoord2f (0, 0); glVertex3f (x1, y2, -20);
+ glTexCoord2f (0, ty); glVertex3f (x1, y1, -20);
+ glTexCoord2f (tx, ty); glVertex3f (x2, y1, -20);
+ glEnd ();
+
+ glPopMatrix ();
+ glDepthFunc (GL_LEQUAL);
+
+ /* Spin it */
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ glTranslatef (0, 0, -10);
+ glRotatef (off * CYCLE_FACTOR1, 1, 0, 0);
+ glRotatef (off, 0, 0, 1);
+ glRotatef (off * CYCLE_FACTOR2, 0, 1, 0);
+ glEnable (GL_TEXTURE_GEN_S);
+ glEnable (GL_TEXTURE_GEN_T);
+ glColor3f (1, 0.8, 0.6);
+ glCallList (1);
+ glDisable (GL_TEXTURE_GEN_S);
+ glDisable (GL_TEXTURE_GEN_T);
+}
- switch (which_field & VO_BOTH_FIELDS) {
- case VO_TOP_FIELD:
- frame->rgb_dst = frame->texture;
- frame->stripe_inc = 2 * STRIPE_HEIGHT * BYTES_PER_PIXEL * frame->width;
- break;
- case VO_BOTTOM_FIELD:
- frame->rgb_dst = frame->texture + BYTES_PER_PIXEL * frame->width;
- frame->stripe_inc = 2 * STRIPE_HEIGHT * BYTES_PER_PIXEL * frame->width;
- break;
- case VO_BOTH_FIELDS:
- frame->rgb_dst = frame->texture;
- frame->stripe_inc = STRIPE_HEIGHT * BYTES_PER_PIXEL * frame->width;
- break;
- }
-/* DEBUGF ((stderr, "*** %p: frame_field: rgb_dst %p\n", frame, frame->rgb_dst)); */
+/*
+ * Image setup functions
+ */
+static void render_image_nop (opengl_driver_t *this, opengl_frame_t *frame) {
}
+static void render_image_tex (opengl_driver_t *this, opengl_frame_t *frame) {
+ int tex_w, tex_h;
+
+ /* check necessary texture size and allocate */
+ if (frame->width != this->last_width ||
+ frame->height != this->last_height ||
+ ! this->tex_width || ! this->tex_height) {
+ tex_w = tex_h = 16;
+ while (tex_w < frame->width)
+ tex_w <<= 1;
+ while (tex_h < frame->height)
+ tex_h <<= 1;
+
+ if (tex_w != this->tex_width || tex_h != this->tex_height) {
+ char *tmp = malloc (tex_w * tex_h * BYTES_PER_PIXEL);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, tex_w, tex_h,
+ 0, RGB_TEXTURE_FORMAT, GL_UNSIGNED_BYTE, tmp);
+ CHECKERR ("texImage");
+ free (tmp);
+ this->tex_width = tex_w;
+ this->tex_height = tex_h;
+ lprintf ("* new texsize: %dx%d\n", tex_w, tex_h);
+ }
+ this->last_width = frame->width;
+ this->last_height = frame->height;
+ }
+ /* Load texture */
+ glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
+ (GLsizei)(frame->width),
+ (GLsizei)(frame->height),
+ RGB_TEXTURE_FORMAT, GL_UNSIGNED_BYTE,
+ frame->rgb);
+ CHECKERR ("texsubimage");
+}
-static void opengl_frame_dispose (vo_frame_t *vo_img) {
+static void render_image_envtex (opengl_driver_t *this, opengl_frame_t *frame) {
+ static float mTex[] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+
+ /* update texture matrix if frame size changed */
+ if (frame->width != this->last_width ||
+ frame->height != this->last_height ||
+ ! this->tex_width || ! this->tex_height) {
+ render_image_tex (this, frame);
+ /* Texture matrix has to skale/shift tex origin + swap y coords */
+ mTex[0] = 1.0 * frame->width / this->tex_width;
+ mTex[5] = -1.0 * frame->height / this->tex_height;
+ mTex[12] = (-2.0 * mTex[0]) / mTex[0];
+ mTex[13] = -mTex[5];
+ glMatrixMode (GL_TEXTURE);
+ glLoadMatrixf (mTex);
+ } else {
+ render_image_tex (this, frame);
+ }
+}
- opengl_frame_t *frame = (opengl_frame_t *) vo_img ;
- opengl_driver_t *this = (opengl_driver_t *) vo_img->driver;
- DEBUGF ((stderr, "*** frame_dispose ***\n"));
- if (frame)
- {
- XLockDisplay (this->display);
- free (frame->texture);
- free (frame->chunk[0]);
- free (frame->chunk[1]);
- free (frame->chunk[2]);
- frame->texture = NULL;
- frame->chunk[0] = frame->chunk[1] = frame->chunk[2] = NULL;
- XUnlockDisplay (this->display);
- }
- free (frame);
+/*
+ * Render setup functions
+ */
+static void render_help_setup_tex (opengl_driver_t *this) {
+ CHECKERR ("pre-tex_setup");
+ glEnable (GL_TEXTURE_2D);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glMatrixMode (GL_TEXTURE);
+ glLoadIdentity ();
+ CHECKERR ("post-tex_setup");
}
+static void render_setup_2d (opengl_driver_t *this) {
+ CHECKERR ("pre-frustum_setup");
+ glViewport (0, 0, this->gui_width, this->gui_height);
+ glDepthRange (-1, 1);
+ glClearColor (0, 0, 0, 0);
+ glClearDepth (1.0f);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0, this->gui_width, this->gui_height, 0, -1, 1);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ glDisable (GL_BLEND);
+ glDisable (GL_DEPTH_TEST);
+ glDepthMask (GL_FALSE);
+ glDisable (GL_CULL_FACE);
+ glShadeModel (GL_FLAT);
+ glDisable (GL_TEXTURE_2D);
+ glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+ CHECKERR ("post-frustum_setup");
+}
-static vo_frame_t *opengl_alloc_frame (vo_driver_t *this_gen) {
+static void render_setup_tex2d (opengl_driver_t *this) {
+ render_setup_2d (this);
+ render_help_setup_tex (this);
+}
- opengl_frame_t *frame ;
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
+static void render_setup_3d (opengl_driver_t *this) {
+ CHECKERR ("pre-3dfrustum_setup");
+ glViewport (0, 0, this->gui_width, this->gui_height);
+ glDepthRange (0, 1);
+ glClearColor (0, 0, 0, 0);
+ glClearDepth (1.0f);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ gluPerspective (45.0f,
+ (GLfloat)(this->gui_width) / (GLfloat)(this->gui_height),
+ 1.0f, 1000.0f);
+ glDisable (GL_BLEND);
+ glEnable (GL_DEPTH_TEST);
+ glDepthFunc (GL_LEQUAL);
+ glDepthMask (GL_TRUE);
+ glDisable (GL_CULL_FACE);
+ glShadeModel (GL_FLAT);
+ glDisable (GL_TEXTURE_2D);
+ glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+ CHECKERR ("post-3dfrustum_setup");
+}
- DEBUGF ((stderr, "*** alloc_frame ***\n"));
- frame = (opengl_frame_t *) xine_xmalloc (sizeof (opengl_frame_t));
- if (!frame)
- return NULL;
+static void render_setup_cyl (opengl_driver_t *this) {
+ render_setup_3d (this);
+ render_help_setup_tex (this);
+ glClearColor (0, .2, .3, 0);
+}
- pthread_mutex_init (&frame->vo_frame.mutex, NULL);
+#define TOR_TESSELATION_B 128
+#define TOR_TESSELATION_S 64
+#define TOR_RADIUS_B 2.5
+#define TOR_RADIUS_S 1.0
- /*
- * supply required functions/fields
- */
- frame->vo_frame.proc_slice = opengl_frame_proc_slice;
- frame->vo_frame.proc_frame = NULL;
- frame->vo_frame.field = opengl_frame_field;
- frame->vo_frame.dispose = opengl_frame_dispose;
- frame->vo_frame.driver = this_gen;
+static void render_setup_torus (opengl_driver_t *this) {
+ int i, j, k;
- /*
- * colorspace converter for this frame
- */
- frame->yuv2rgb = this->yuv2rgb_factory->create_converter (this->yuv2rgb_factory);
-
- return (vo_frame_t *) frame;
+ render_setup_3d (this);
+ render_help_setup_tex (this);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+
+ /* create display list */
+ glNewList (1, GL_COMPILE);
+ for (i = 0; i < TOR_TESSELATION_B; i++) {
+ glBegin (GL_QUAD_STRIP);
+ for (j = 0; j <= TOR_TESSELATION_S; j++) {
+ float phi = MY_2PI * j / TOR_TESSELATION_S;
+ for (k = 0; k <= 1; k++) {
+ float theta = MY_2PI * (i + k) / TOR_TESSELATION_B;
+ float nx = TOR_RADIUS_S * cos(phi) * cos(theta);
+ float ny = TOR_RADIUS_S * cos(phi) * sin(theta);
+ float nz = TOR_RADIUS_S * sin(phi);
+ float nnorm = 1.0 / sqrt (nx*nx + ny*ny + nz*nz);
+ float x = (TOR_RADIUS_B + TOR_RADIUS_S * cos(phi)) * cos(theta);
+ float y = (TOR_RADIUS_B + TOR_RADIUS_S * cos(phi)) * sin(theta);
+ float z = TOR_RADIUS_S * sin(phi);
+ glNormal3f (nx * nnorm, ny * nnorm, nz * nnorm);
+ glVertex3f (x, y, z);
+ }
+ }
+ glEnd ();
+ }
+ glEndList ();
}
+static char *opengl_render_fun_names[] = {
+ "2D_Textures", "Image_Pipeline", "Cylinder", "Environment_Mapped_Torus", NULL
+};
+static void (*opengl_display_funs[])(opengl_driver_t *, opengl_frame_t *) = {
+ render_tex2d, render_draw, render_cyl, render_env_tor
+};
+static void (*opengl_image_funs[])(opengl_driver_t *, opengl_frame_t *) = {
+ render_image_tex, render_image_nop, render_image_tex, render_image_envtex
+};
+static void (*opengl_setup_funs[])(opengl_driver_t *) = {
+ render_setup_tex2d, render_setup_2d, render_setup_cyl,
+ render_setup_torus
+};
+static enum render_e opengl_default_action[] = {
+ RENDER_NONE, RENDER_NONE, RENDER_DRAW, RENDER_DRAW
+};
-static void opengl_compute_ideal_size (opengl_driver_t *this) {
-
- _x_vo_scale_compute_ideal_size (&this->sc);
+/*
+ * GFX state management
+ */
+static void render_gfx_vinfo (opengl_driver_t *this) {
+ static int glxAttrib[] = {
+ GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
+ GLX_DEPTH_SIZE, 1, None, None
+ } ;
+ if (this->render_double_buffer)
+ glxAttrib[9] = GLX_DOUBLEBUFFER;
+ else
+ glxAttrib[9] = None;
+ this->vinfo = glXChooseVisual (this->display, this->screen, glxAttrib);
+ CHECKERR ("choosevisual");
}
-
-static void opengl_update_frame_format (vo_driver_t *this_gen,
- vo_frame_t *frame_gen,
- uint32_t width, uint32_t height,
- double ratio, int format, int flags) {
-
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
- opengl_frame_t *frame = (opengl_frame_t *) frame_gen;
-
-/* DEBUGF ((stderr, "*** %p: update_frame_format ***\n", frame)); */
- flags &= VO_BOTH_FIELDS;
-
- if ((frame->width != width)
- || (frame->height != height)
- || (frame->format != format)
- || (frame->flags != flags)) {
-
- int image_size = width * height;
-
- DEBUGF ((stderr, "video_out_opengl: updating frame to %dx%d (ratio=%f, format=%c%c%c%c)\n",
- width, height, ratio, format&0xff, (format>>8)&0xff,
- (format>>16)&0xff, (format>>24)&0xff));
-
- /* update frame allocated data */
-
+/*
+ * Render thread
+ */
+static void *render_run (opengl_driver_t *this) {
+ int action, changed;
+ opengl_frame_t *frame;
+ struct timeval curtime;
+ struct timespec timeout;
+
+ lprintf ("* render thread created\n");
+ while (1) {
+
+ /* Wait for render action */
+ pthread_mutex_lock (&this->render_action_mutex);
+ if (! this->render_action) {
+ this->render_action = opengl_default_action [this->render_fun_id];
+ if (this->render_action) {
+ /* we have to animate even static images */
+ gettimeofday (&curtime, NULL);
+ timeout.tv_nsec = 1000 * curtime.tv_usec + 1e9L / this->render_min_fps;
+ timeout.tv_sec = curtime.tv_sec;
+ if (timeout.tv_nsec > 1e9L) {
+ timeout.tv_nsec -= 1e9L;
+ timeout.tv_sec += 1;
+ }
+ pthread_cond_timedwait (&this->render_action_cond,
+ &this->render_action_mutex, &timeout);
+ } else {
+ pthread_cond_wait (&this->render_action_cond,
+ &this->render_action_mutex);
+ }
+ }
+ action = this->render_action;
+ changed = this->render_frame_changed;
+ /* frame may be updated/deleted outside mutex, but still atomically */
+ /* we do not (yet) care to check frames for validity - this is a race.. */
+ /* but we do not delete/change frames for at least 4 frames after update */
+ frame = this->frame[0];
+
+ lprintf ("* render action: %d frame %d changed %d drawable %lx\n",
+ action, frame ? frame->vo_frame.id : -1, changed, this->drawable);
+ switch (action) {
+
+ case RENDER_NONE:
+ pthread_mutex_unlock (&this->render_action_mutex);
+ break;
+
+ case RENDER_DRAW:
+ this->render_action = RENDER_NONE;
+ this->render_frame_changed = 0;
+ pthread_mutex_unlock (&this->render_action_mutex);
+ if (this->context && frame) {
XLockDisplay (this->display);
+ CHECKERR ("pre-render");
+ if (changed)
+ (opengl_image_funs [this->render_fun_id]) (this, frame);
+ (opengl_display_funs [this->render_fun_id]) (this, frame);
+ glXSwapBuffers(this->display, this->drawable);
+ /* Note: no glFinish() - work concurrently to the graphics pipe */
+ CHECKERR ("post-render");
+ XUnlockDisplay (this->display);
+ }
+ break;
- free (frame->texture);
- free (frame->chunk[0]);
- free (frame->chunk[1]);
- free (frame->chunk[2]);
- frame->chunk[0] = frame->chunk[1] = frame->chunk[2] = NULL;
-
- frame->texture = calloc (1, BYTES_PER_PIXEL * image_size);
- _x_assert(frame->texture);
-
- switch (format) {
- case XINE_IMGFMT_YV12:
- frame->vo_frame.pitches[0] = 8*((width + 7) / 8);
- frame->vo_frame.pitches[1] = 8*((width + 15) / 16);
- frame->vo_frame.pitches[2] = 8*((width + 15) / 16);
- frame->vo_frame.base[0] = xine_xmalloc_aligned(16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]);
- frame->vo_frame.base[1] = xine_xmalloc_aligned(16, frame->vo_frame.pitches[1] * ((height+1)/2), (void **) &frame->chunk[1]);
- frame->vo_frame.base[2] = xine_xmalloc_aligned(16, frame->vo_frame.pitches[2] * ((height+1)/2), (void **) &frame->chunk[2]);
- break;
- case XINE_IMGFMT_YUY2:
- frame->vo_frame.pitches[0] = 8*((width + 3) / 4);
- frame->vo_frame.base[0] = xine_xmalloc_aligned(16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]);
- break;
- default:
- xprintf (this->xine, XINE_VERBOSITY_DEBUG,
- "video_out_opengl: image format %d not supported, update video driver!\n", format);
- return;
+ case RENDER_CLEAN:
+ this->render_action = RENDER_DRAW;
+ this->render_frame_changed = 0;
+ pthread_mutex_unlock (&this->render_action_mutex);
+ if (this->context && frame) {
+ XLockDisplay (this->display);
+ CHECKERR ("pre-clean");
+ if (changed)
+ (opengl_image_funs [this->render_fun_id]) (this, frame);
+ if (this->render_double_buffer) {
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
+ | GL_STENCIL_BUFFER_BIT);
+ (opengl_display_funs [this->render_fun_id]) (this, frame);
+ glXSwapBuffers(this->display, this->drawable);
}
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
+ | GL_STENCIL_BUFFER_BIT);
+ XUnlockDisplay (this->display);
+ }
+ break;
- switch (flags) {
- case VO_TOP_FIELD:
- case VO_BOTTOM_FIELD:
- frame->yuv2rgb->configure (frame->yuv2rgb,
- width, 16, width*2, width, width,
- STRIPE_HEIGHT,
- width * BYTES_PER_PIXEL * 2);
- break;
- case VO_BOTH_FIELDS:
- frame->yuv2rgb->configure (frame->yuv2rgb,
- width, 16, width, width/2, width,
- STRIPE_HEIGHT,
- width * BYTES_PER_PIXEL);
- break;
- }
-
- frame->width = width;
- frame->height = height;
- frame->format = format;
- frame->flags = flags;
-
+ case RENDER_SETUP:
+ this->render_action = RENDER_CLEAN;
+ this->render_frame_changed = 1;
+ pthread_mutex_unlock (&this->render_action_mutex);
+ if (this->context) {
+ XLockDisplay (this->display);
+ (opengl_setup_funs [this->render_fun_id]) (this);
XUnlockDisplay (this->display);
- }
+ this->tex_width = this->tex_height = 0;
+ }
+ break;
+
+ case RENDER_CREATE:
+ this->render_action = RENDER_NONE;
+ pthread_mutex_unlock (&this->render_action_mutex);
+ _x_assert (this->vinfo);
+ if (this->context)
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "video_out_opengl: last context not destroyed\n"
+ " (frontend does not support XINE_GUI_SEND_WILL_DESTROY_DRAWABLE)\n"
+ " This will be a memory leak.\n");
+ XLockDisplay (this->display);
+ this->context = glXCreateContext (this->display, this->vinfo, NULL, True);
+ if (this->context) {
+ glXMakeCurrent (this->display, this->drawable, this->context);
+ CHECKERR ("create+makecurrent");
+ }
+ XUnlockDisplay (this->display);
+ break;
+
+ case RENDER_VISUAL:
+ this->render_action = RENDER_NONE;
+ pthread_mutex_unlock (&this->render_action_mutex);
+ XLockDisplay (this->display);
+ render_gfx_vinfo (this);
+ XUnlockDisplay (this->display);
+ if (this->vinfo == NULL)
+ xprintf (this->xine, XINE_VERBOSITY_NONE,
+ "video_out_opengl: no OpenGL support available (glXChooseVisual)\n");
+ else
+ lprintf ("* visual %p id %lx depth %d\n", this->vinfo->visual,
+ this->vinfo->visualid, this->vinfo->depth);
+ break;
+
+ case RENDER_RELEASE:
+ this->render_action = RENDER_NONE;
+ pthread_mutex_unlock (&this->render_action_mutex);
+ if (this->context) {
+ XLockDisplay (this->display);
+ glXMakeCurrent (this->display, None, NULL);
+ glXDestroyContext (this->display, this->context);
+ CHECKERR ("release");
+ XUnlockDisplay (this->display);
+ this->context = NULL;
+ }
+ break;
- frame->ratio = ratio;
- opengl_frame_field ((vo_frame_t *)frame, flags);
-}
+ case RENDER_EXIT:
+ pthread_mutex_unlock (&this->render_action_mutex);
+ if (this->context) {
+ XLockDisplay (this->display);
+ glXMakeCurrent (this->display, None, NULL);
+ glXDestroyContext (this->display, this->context);
+ CHECKERR ("exit");
+ XUnlockDisplay (this->display);
+ }
+ pthread_exit (NULL);
+ break;
+ default:
+ this->render_action = RENDER_NONE;
+ pthread_mutex_unlock (&this->render_action_mutex);
+ _x_assert (!action); /* unknown action */
+ }
+ lprintf ("* render action: %d frame %d done\n", action,
+ frame ? frame->vo_frame.id : -1);
+ pthread_cond_signal (&this->render_return_cond);
+ }
+ /* NOTREACHED */
+ return NULL;
+}
-static void opengl_compute_output_size (opengl_driver_t *this) {
- int old_width = this->sc.output_width;
- int old_height = this->sc.output_height;
- int old_x = this->sc.output_xoffset;
- int old_y = this->sc.output_yoffset;
+/*
+ * and now, the driver functions
+ */
- _x_vo_scale_compute_output_size (&this->sc);
+static uint32_t opengl_get_capabilities (vo_driver_t *this_gen) {
+/* opengl_driver_t *this = (opengl_driver_t *) this_gen; */
+ uint32_t capabilities = VO_CAP_YV12 | VO_CAP_YUY2;
- /* avoid problems in yuv2rgb */
- if (this->sc.output_height < ((this->sc.delivered_height + 15) >> 4))
- this->sc.output_height = ((this->sc.delivered_height + 15) >> 4);
- if (this->sc.output_width < 8)
- this->sc.output_width = 8;
- if (this->sc.output_width & 1) /* yuv2rgb_mlib needs an even YUV2 width */
- this->sc.output_width++;
- DEBUGF ((stderr, "video_out_opengl: this source %d x %d => screen output %d x %d\n",
- this->sc.delivered_width, this->sc.delivered_height,
- this->sc.output_width, this->sc.output_height));
+ /* TODO: somehow performance goes down during the first few frames */
+/* if (this->xoverlay) */
+/* capabilities |= VO_CAP_UNSCALED_OVERLAY; */
- /* Force state reinitialization / clear */
- if ( (old_width != this->sc.output_width ||
- old_height != this->sc.output_height ||
- old_x != this->sc.output_xoffset ||
- old_y != this->sc.output_yoffset)
- && this->context_state == CONTEXT_SET)
- this->context_state = CONTEXT_RELOAD;
+ return capabilities;
}
+static void opengl_frame_proc_slice (vo_frame_t *vo_img, uint8_t **src) {
+ opengl_frame_t *frame = (opengl_frame_t *) vo_img ;
+ /*opengl_driver_t *this = (opengl_driver_t *) vo_img->driver; */
+
+ vo_img->proc_called = 1;
-static void opengl_overlay_clut_yuv2rgb (opengl_driver_t *this,
- vo_overlay_t *overlay,
- opengl_frame_t *frame) {
- int i;
- clut_t* clut = (clut_t*) overlay->color;
- if (!overlay->rgb_clut) {
- for (i = 0; i < sizeof(overlay->color)/sizeof(overlay->color[0]); i++) {
- *((uint32_t *)&clut[i]) =
- frame->yuv2rgb->yuv2rgb_single_pixel_fun(frame->yuv2rgb,
- clut[i].y, clut[i].cb, clut[i].cr);
- }
- overlay->rgb_clut++;
- }
- if (!overlay->clip_rgb_clut) {
- clut = (clut_t*) overlay->clip_color;
- for (i = 0; i < sizeof(overlay->color)/sizeof(overlay->color[0]); i++) {
- *((uint32_t *)&clut[i]) =
- frame->yuv2rgb->yuv2rgb_single_pixel_fun(frame->yuv2rgb,
- clut[i].y, clut[i].cb, clut[i].cr);
- }
- overlay->clip_rgb_clut++;
- }
+/* lprintf ("%p: frame_copy src %p/%p/%p to %p\n", frame, src[0], src[1], src[2], frame->rgb_dst); */
+ if( frame->vo_frame.crop_left || frame->vo_frame.crop_top ||
+ frame->vo_frame.crop_right || frame->vo_frame.crop_bottom )
+ {
+ /* TODO: opengl *could* support this?!? */
+ /* cropping will be performed by video_out.c */
+ return;
+ }
+
+ if (frame->format == XINE_IMGFMT_YV12)
+ frame->yuv2rgb->yuv2rgb_fun (frame->yuv2rgb, frame->rgb_dst,
+ src[0], src[1], src[2]);
+ else
+ frame->yuv2rgb->yuy22rgb_fun (frame->yuv2rgb, frame->rgb_dst,
+ src[0]);
+
+/* lprintf ("frame_copy...done\n"); */
}
+static void opengl_frame_field (vo_frame_t *vo_img, int which_field) {
+ opengl_frame_t *frame = (opengl_frame_t *) vo_img ;
+ /* opengl_driver_t *this = (opengl_driver_t *) vo_img->driver; */
+
+/* lprintf ("%p: frame_field image %p which_field %x\n", frame, frame->image->data, which_field); */
+
+ switch (which_field) {
+ case VO_TOP_FIELD:
+ frame->rgb_dst = (uint8_t *)frame->rgb;
+ break;
+ case VO_BOTTOM_FIELD:
+ frame->rgb_dst = (uint8_t *)frame->rgb + frame->width * BYTES_PER_PIXEL;
+ break;
+ case VO_BOTH_FIELDS:
+ frame->rgb_dst = (uint8_t *)frame->rgb;
+ break;
+ }
+
+ frame->yuv2rgb->next_slice (frame->yuv2rgb, NULL);
+/* lprintf ("frame_field...done\n"); */
+}
-static void opengl_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame_gen,
- vo_overlay_t *overlay) {
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
- opengl_frame_t *frame = (opengl_frame_t *) frame_gen;
+static void opengl_frame_dispose (vo_frame_t *vo_img) {
+ opengl_frame_t *frame = (opengl_frame_t *) vo_img ;
- DEBUGF ((stderr, "*** overlay_blend\n"));
- /* Alpha Blend here */
- if (overlay->rle) {
- if( !overlay->rgb_clut || !overlay->clip_rgb_clut)
- opengl_overlay_clut_yuv2rgb (this,overlay, frame);
+ frame->yuv2rgb->dispose (frame->yuv2rgb);
-# if BYTES_PER_PIXEL == 3
- blend_rgb24 ((uint8_t *)frame->texture, overlay,
- frame->width, frame->height,
- frame->width, frame->height);
-# elif BYTES_PER_PIXEL == 4
- blend_rgb32 ((uint8_t *)frame->texture, overlay,
- frame->width, frame->height,
- frame->width, frame->height);
-# else
-# error "bad BYTES_PER_PIXEL"
-# endif
- }
+ free (frame->chunk[0]);
+ free (frame->chunk[1]);
+ free (frame->chunk[2]);
+ free (frame->chunk[3]);
+ free (frame);
}
-static int opengl_redraw_needed (vo_driver_t *this_gen) {
+static vo_frame_t *opengl_alloc_frame (vo_driver_t *this_gen) {
+ opengl_frame_t *frame;
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+
+ frame = (opengl_frame_t *) xine_xmalloc (sizeof (opengl_frame_t));
+ if (!frame)
+ return NULL;
+
+ pthread_mutex_init (&frame->vo_frame.mutex, NULL);
+
+ /*
+ * supply required functions/fields
+ */
+ frame->vo_frame.proc_slice = opengl_frame_proc_slice;
+ frame->vo_frame.proc_frame = NULL;
+ frame->vo_frame.field = opengl_frame_field;
+ frame->vo_frame.dispose = opengl_frame_dispose;
+ frame->vo_frame.driver = this_gen;
+
+ /*
+ * colorspace converter for this frame
+ */
+ frame->yuv2rgb = this->yuv2rgb_factory->create_converter (this->yuv2rgb_factory);
+
+ return (vo_frame_t *) frame;
+}
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
+static void opengl_compute_ideal_size (opengl_driver_t *this) {
+ _x_vo_scale_compute_ideal_size( &this->sc );
+}
- DEBUGF ((stderr, "*** redraw_needed %dx%d\n", this->sc.delivered_width, this->sc.delivered_height));
+static void opengl_compute_rgb_size (opengl_driver_t *this) {
+ _x_vo_scale_compute_output_size( &this->sc );
+}
- if (_x_vo_scale_redraw_needed (&this->sc)) {
- opengl_compute_output_size (this);
- /* Actually, the output area is cleared in render_image */
- return 1;
- }
- return 0;
-}
-
-
-static void opengl_render_image (opengl_driver_t *this, opengl_frame_t *frame,
- GLXContext ctx)
-{
- pthread_t self = pthread_self ();
-
- DEBUGF ((stderr, "*** render_image %p frame %p %dx%d ctx %p%s\n",
- this, frame, frame->width, frame->height, ctx,
- self != this->renderthread ? " THREAD" :
- ctx && ctx != this->context ? " gui" :
- this->context_state == CONTEXT_SET ?
- ctx ? " set, but reload anyway" : " set" :
- this->context_state == CONTEXT_RELOAD ? " reload" :
- this->context_state == CONTEXT_SAME_DRAWABLE ? " DESTROY+CREATE" :
- " CREATE" ));
-
- /* already initialized? */
- if (! this->drawable || ! this->vinfo)
- {
- xprintf (this->xine, XINE_VERBOSITY_DEBUG,
- "video_out_opengl: early exit due to missing drawable %lx vinfo %p\n", this->drawable, this->vinfo);
- return;
+static void opengl_update_frame_format (vo_driver_t *this_gen,
+ vo_frame_t *frame_gen,
+ uint32_t width, uint32_t height,
+ double ratio, int format, int flags) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+ opengl_frame_t *frame = (opengl_frame_t *) frame_gen;
+ int g_width, g_height;
+ double g_pixel_aspect;
+
+ /* Check output size to signal render thread output size changes */
+ this->sc.dest_size_cb (this->sc.user_data, width, height,
+ this->sc.video_pixel_aspect, &g_width, &g_height,
+ &g_pixel_aspect);
+ lprintf ("update_frame_format %dx%d output %dx%d\n", width, height,
+ g_width, g_height);
+
+ if (g_width != this->gui_width || g_height != this->gui_height) {
+ this->gui_width = g_width;
+ this->gui_height = g_height;
+ pthread_mutex_lock (&this->render_action_mutex);
+ if (this->render_action <= RENDER_SETUP) {
+ this->render_action = RENDER_SETUP;
+ pthread_cond_signal (&this->render_action_cond);
}
-
- /*
- * check for size changes
- */
- if (frame->width != this->last_width ||
- frame->height != this->last_height ||
- frame->ratio != this->last_ratio) {
-
- this->last_width = frame->width;
- this->last_height = frame->height;
- this->last_ratio = frame->ratio;
-
- DEBUGF ((stderr, "video_out_opengl: display format changed\n"));
- opengl_compute_ideal_size (this);
- opengl_compute_output_size (this);
- }
+ pthread_mutex_unlock (&this->render_action_mutex);
+ }
- /*
- * Check texture size
- */
- if (this->texture_width < frame->width || this->texture_height < frame->height)
- this->context_state = CONTEXT_RELOAD;
-
- /*
- * check whether a new context has to be created
- */
- DEBUGF ((stderr, "video_out_opengl: CHECK\n"));
- if (((ctx == this->context || ! ctx) &&
- (this->context_state == CONTEXT_BAD ||
- this->context_state == CONTEXT_SAME_DRAWABLE)) ||
- (self != this->renderthread))
- {
-
- static int glxAttrib[] = {
- GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None
- } ;
-
- _x_assert(this->vinfo);
-
- if ((this->context_state == CONTEXT_SAME_DRAWABLE) &&
- (self == this->renderthread))
- {
- DEBUGF ((stderr, "destroy: %p\n", this->context));
- /* Unfortunately for _BAD the drawable is already destroyed.
- * This cannot be resolved right now and will be a memory leak. */
- if (this->context)
- glXDestroyContext (this->display, this->context);
- }
-
- DEBUGF ((stderr, "glXChooseVisual\n"));
+ /* Check frame size and format and reallocate if necessary */
+ if ((frame->width != width)
+ || (frame->height != height)
+ || (frame->format != format)
+ || (frame->flags != flags)) {
+ lprintf ("updating frame to %d x %d (ratio=%g, format=%08x)\n",
+ width, height, ratio, format);
- this->vinfo = glXChooseVisual (this->display, this->screen, glxAttrib);
- DEBUGF ((stderr, "create display %p vinfo %p\n", this->display, this->vinfo));
- ctx = glXCreateContext (this->display, this->vinfo, NULL, True);
- DEBUGF ((stderr, "created\n"));
-
- _x_assert(ctx);
+ flags &= VO_BOTH_FIELDS;
+
+ XLockDisplay (this->display);
- this->context = ctx;
- this->context_state = CONTEXT_RELOAD;
- this->renderthread = self;
+ /* (re-) allocate render space */
+ free (frame->chunk[0]);
+ free (frame->chunk[1]);
+ free (frame->chunk[2]);
+ free (frame->chunk[3]);
+
+ if (format == XINE_IMGFMT_YV12) {
+ frame->vo_frame.pitches[0] = 8*((width + 7) / 8);
+ frame->vo_frame.pitches[1] = 8*((width + 15) / 16);
+ frame->vo_frame.pitches[2] = 8*((width + 15) / 16);
+ frame->vo_frame.base[0] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]);
+ frame->vo_frame.base[1] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[1] * ((height+1)/2), (void **) &frame->chunk[1]);
+ frame->vo_frame.base[2] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[2] * ((height+1)/2), (void **) &frame->chunk[2]);
+ } else {
+ frame->vo_frame.pitches[0] = 8*((width + 3) / 4);
+ frame->vo_frame.base[0] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]);
+ frame->chunk[1] = NULL;
+ frame->chunk[2] = NULL;
}
+ frame->rgb = xine_xmalloc_aligned (16, BYTES_PER_PIXEL*width*height,
+ (void **) &frame->chunk[3]);
- if (this->context_state == CONTEXT_RELOAD && ! ctx)
- ctx = this->context;
-
- /*
- * reload and initialize context and clear display
- * this is handled together due to close relationship
- */
- if (ctx)
- {
- void *texture_data;
-
- DEBUGF ((stderr, "set context %p\n", ctx));
- /*
- * Set and initialize context
- */
- if (! glXMakeCurrent (this->display, this->drawable, ctx)) {
- xprintf (this->xine, XINE_VERBOSITY_DEBUG,
- "video_out_opengl: no OpenGL support available (glXMakeCurrent)\n"
- " The drawable does not seem to be updated correctly.\n");
- _x_abort();
- }
- DEBUGF ((stderr, "set context done\n"));
- if (ctx == this->context)
- this->context_state = CONTEXT_SET;
- else if (this->context_state == CONTEXT_SET ||
- this->context_state == CONTEXT_RELOAD)
- this->context_state = CONTEXT_RELOAD;
- glViewport (0, 0, this->sc.gui_width, this->sc.gui_height);
- glMatrixMode (GL_MODELVIEW);
- glLoadIdentity ();
- glClearColor (0, 0, 0, 0);
- glClearDepth (1.0f);
- glDisable (GL_BLEND);
- glDisable (GL_DEPTH_TEST);
- glDepthMask (GL_FALSE);
- glDisable (GL_CULL_FACE);
- glShadeModel (GL_FLAT);
- glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
-
-#if USE_SPHERE
- glDepthFunc (GL_LEQUAL);
- glEnable (GL_DEPTH_TEST);
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glScalef((float)(frame->width)/(float)(this->texture_width),
- (float)(frame->height)/(float)(this->texture_height),
- 1.0f);
- glMatrixMode (GL_PROJECTION);
- glLoadIdentity ();
- gluPerspective (45.0f,
- (GLfloat)(this->gui_width)/
- (GLfloat)(this->gui_height),
- 1.0f, 1000.0f);
- glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
- glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ /* set up colorspace converter */
+ switch (flags) {
+ case VO_TOP_FIELD:
+ case VO_BOTTOM_FIELD:
+ frame->yuv2rgb->configure (frame->yuv2rgb,
+ width,
+ height,
+ 2*frame->vo_frame.pitches[0],
+ 2*frame->vo_frame.pitches[1],
+ width,
+ height,
+ BYTES_PER_PIXEL*width * 2);
+ break;
+ case VO_BOTH_FIELDS:
+ frame->yuv2rgb->configure (frame->yuv2rgb,
+ width,
+ height,
+ frame->vo_frame.pitches[0],
+ frame->vo_frame.pitches[1],
+ width,
+ height,
+ BYTES_PER_PIXEL*width);
+ break;
+ }
-#else
- glMatrixMode (GL_PROJECTION);
- glLoadIdentity ();
- glOrtho (0, this->sc.gui_width, this->sc.gui_height, 0, -1, 1);
-#endif
+ frame->width = width;
+ frame->height = height;
+ frame->format = format;
-#if USE_TEXTURES
- glEnable (GL_TEXTURE_2D);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-
- /*
- * check necessary texture size and allocate
- */
- this->texture_width = 1;
- while (this->texture_width < frame->width)
- this->texture_width <<= 1;
- this->texture_height = 1;
- while (this->texture_height < frame->height)
- this->texture_height <<= 1;
-
- texture_data = malloc (this->texture_width * this->texture_height * 3);
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB,
- this->texture_width, this->texture_height,
- 0, GL_RGB, GL_UNSIGNED_BYTE, texture_data);
- free (texture_data);
-#endif
- }
+ XUnlockDisplay (this->display);
- if (ctx || opengl_redraw_needed ((vo_driver_t *) this)) {
-#if USE_SPHERE
- glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-#else
- glClear (GL_COLOR_BUFFER_BIT);
-#endif
- }
+ opengl_frame_field ((vo_frame_t *)frame, flags);
+ }
- if (frame)
- {
- /*
- * Render one image
- */
-#if USE_TEXTURES
- int x1 = this->sc.output_xoffset, y1 = this->sc.output_yoffset;
- int x2 = x1 + this->sc.output_width, y2 = y1 + this->sc.output_height;
- float tx = (float) frame->width / this->texture_width;
- float ty = (float) frame->height / this->texture_height;
-
- glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
- (GLsizei)(frame->width),
- (GLsizei)(frame->height),
- RGB_TEXTURE_FORMAT, GL_UNSIGNED_BYTE,
- frame->texture);
-
- glBegin (GL_QUADS);
- glTexCoord2f (tx, ty); glVertex2i (x2, y2);
- glTexCoord2f (0, ty); glVertex2i (x1, y2);
- glTexCoord2f (0, 0); glVertex2i (x1, y1);
- glTexCoord2f (tx, 0); glVertex2i (x2, y1);
- glEnd ();
-
-#if USE_SPHERE
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
+ frame->ratio = ratio;
+ lprintf ("done...update_frame_format\n");
+}
- glTranslatef(0.0f, 0.0f, -10.0f);
- glutSolidSphere(3.0f, 20, 10);
- glDisable(GL_TEXTURE_GEN_S);
- glDisable(GL_TEXTURE_GEN_T);
-#endif
+static void opengl_overlay_clut_yuv2rgb(opengl_driver_t *this, vo_overlay_t *overlay,
+ opengl_frame_t *frame) {
+ int i;
+ clut_t* clut = (clut_t*) overlay->color;
-#else
- glPixelZoom (((float)this->output_width) / frame->width,
- - ((float)this->output_height) / frame->height);
- glRasterPos2i (this->output_xoffset, this->output_yoffset);
- glDrawPixels (frame->width, frame->height, RGB_TEXTURE_FORMAT,
- GL_UNSIGNED_BYTE, frame->texture);
-#endif
- DEBUGF ((stderr, "render done\n"));
+ if (!overlay->rgb_clut) {
+ for (i = 0; i < sizeof(overlay->color)/sizeof(overlay->color[0]); i++) {
+ *((uint32_t *)&clut[i]) =
+ frame->yuv2rgb->yuv2rgb_single_pixel_fun (frame->yuv2rgb, clut[i].y,
+ clut[i].cb, clut[i].cr);
}
-
- glFlush ();
- DEBUGF ((stderr, "video_output_opengl: OpenGL error: '%s'\n", gluErrorString (glGetError ())));
- /* Note: no glFinish() - work concurrently to the graphics pipe */
+ overlay->rgb_clut++;
+ }
+ if (!overlay->clip_rgb_clut) {
+ clut = (clut_t*) overlay->clip_color;
+ for (i = 0; i < sizeof(overlay->color)/sizeof(overlay->color[0]); i++) {
+ *((uint32_t *)&clut[i]) =
+ frame->yuv2rgb->yuv2rgb_single_pixel_fun(frame->yuv2rgb, clut[i].y,
+ clut[i].cb, clut[i].cr);
+ }
+ overlay->clip_rgb_clut++;
+ }
}
+static void opengl_overlay_begin (vo_driver_t *this_gen,
+ vo_frame_t *frame_gen, int changed) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
-static void opengl_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) {
-
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
- opengl_frame_t *frame = (opengl_frame_t *) frame_gen;
+ this->ovl_changed += changed;
- DEBUGF ((stderr, "*** display_frame ***\n"));
- DEBUGF ((stderr, "video_out_opengl: about to draw frame %d x %d...\n", frame->width, frame->height));
+ if (this->ovl_changed && this->xoverlay) {
XLockDisplay (this->display);
- opengl_render_image (this, frame, NULL);
+ x11osd_clear(this->xoverlay);
XUnlockDisplay (this->display);
-
- /* Theoretically, the frame data is not used immedeately, and the
- * graphics system might address altered data - but only if we
- * are faster than the graphics hardware... */
- /* If the image seems to be clobbered, remove the following two lines */
- /* Note: We cannot do expose events, when the frame is deleted. */
- /* Note: We cannot do expose events anyway right now (errors with
- * multiple threads rendering in many OpenGL implementations) */
- /* FIXME: check that */
-#if 1
- frame->vo_frame.free (&frame->vo_frame);
-#endif
- DEBUGF ((stderr, "done display_frame\n"));
+ }
}
+static void opengl_overlay_end (vo_driver_t *this_gen, vo_frame_t *vo_img) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
-static int opengl_get_property (vo_driver_t *this_gen, int property) {
+ if (this->ovl_changed && this->xoverlay) {
+ XLockDisplay (this->display);
+ x11osd_expose(this->xoverlay);
+ XUnlockDisplay (this->display);
+ }
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
-
- DEBUGF ((stderr, "*** get_property\n"));
- switch (property) {
- case VO_PROP_ASPECT_RATIO:
- return this->sc.user_ratio ;
- case VO_PROP_BRIGHTNESS:
- return this->yuv2rgb_brightness;
- case VO_PROP_WINDOW_WIDTH:
- return this->sc.gui_width;
- case VO_PROP_WINDOW_HEIGHT:
- return this->sc.gui_height;
- default:
- xprintf (this->xine, XINE_VERBOSITY_DEBUG,
- "video_out_opengl: tried to get unsupported property %d\n", property);
+ this->ovl_changed = 0;
+}
+
+static void opengl_overlay_blend (vo_driver_t *this_gen,
+ vo_frame_t *frame_gen, vo_overlay_t *overlay) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+ opengl_frame_t *frame = (opengl_frame_t *) frame_gen;
+
+ /* Alpha Blend here */
+ if (overlay->rle) {
+ if (overlay->unscaled) {
+ if (this->ovl_changed && this->xoverlay) {
+ XLockDisplay (this->display);
+ x11osd_blend (this->xoverlay, overlay);
+ XUnlockDisplay (this->display);
+ }
+ } else {
+ if (!overlay->rgb_clut || !overlay->clip_rgb_clut)
+ opengl_overlay_clut_yuv2rgb (this, overlay, frame);
+
+# if BYTES_PER_PIXEL == 3
+ blend_rgb24 ((uint8_t *)frame->rgb, overlay,
+ frame->width, frame->height,
+ frame->width, frame->height);
+# elif BYTES_PER_PIXEL == 4
+ blend_rgb32 ((uint8_t *)frame->rgb, overlay,
+ frame->width, frame->height,
+ frame->width, frame->height);
+# else
+# error "bad BYTES_PER_PIXEL"
+# endif
}
+ }
+}
- return 0;
+static int opengl_redraw_needed (vo_driver_t *this_gen) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+ int ret = 0;
+
+/* lprintf ("redraw_needed\n"); */
+ if (this->frame[0]) {
+ this->sc.delivered_height = this->frame[0]->height;
+ this->sc.delivered_width = this->frame[0]->width;
+ this->sc.delivered_ratio = this->frame[0]->ratio;
+
+ this->sc.crop_left = this->frame[0]->vo_frame.crop_left;
+ this->sc.crop_right = this->frame[0]->vo_frame.crop_right;
+ this->sc.crop_top = this->frame[0]->vo_frame.crop_top;
+ this->sc.crop_bottom = this->frame[0]->vo_frame.crop_bottom;
+
+ opengl_compute_ideal_size(this);
+
+ if( _x_vo_scale_redraw_needed( &this->sc ) ) {
+ opengl_compute_rgb_size(this);
+ pthread_mutex_lock (&this->render_action_mutex);
+ if (this->render_action <= RENDER_CLEAN) {
+ this->render_action = RENDER_CLEAN;
+ pthread_cond_signal (&this->render_action_cond);
+ }
+ pthread_mutex_unlock (&this->render_action_mutex);
+ ret = 1;
+ }
+ }
+ else
+ ret = 1;
+
+/* lprintf ("done...redraw_needed: %d\n", ret); */
+ return ret;
}
+static void opengl_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+ opengl_frame_t *frame = (opengl_frame_t *) frame_gen;
+ int i;
+
+ lprintf ("about to draw frame (%d) %d x %d...\n", frame->vo_frame.id, frame->width, frame->height);
+
+/* lprintf ("video_out_opengl: freeing frame %d\n", this->frame[NUM_FRAMES_BACKLOG-1] ? this->frame[NUM_FRAMES_BACKLOG-1]->vo_frame.id : -1); */
+ if (this->frame[NUM_FRAMES_BACKLOG-1])
+ this->frame[NUM_FRAMES_BACKLOG-1]->vo_frame.free (&this->frame[NUM_FRAMES_BACKLOG-1]->vo_frame);
+ for (i = NUM_FRAMES_BACKLOG-1; i > 0; i--)
+ this->frame[i] = this->frame[i-1];
+ this->frame[0] = frame;
+ this->render_frame_changed = 1;
+/* lprintf ("video_out_opengl: cur_frame updated to %d\n", frame->vo_frame.id); */
+
+ /*
+ * let's see if this frame is different in size / aspect
+ * ratio from the previous one
+ */
+ if ( (frame->width != this->sc.delivered_width)
+ || (frame->height != this->sc.delivered_height)
+ || (frame->ratio != this->sc.delivered_ratio) ) {
+ lprintf("frame format changed\n");
+ this->sc.force_redraw = 1; /* trigger re-calc of output size */
+ }
+
+ /*
+ * tell gui that we are about to display a frame,
+ * ask for offset and output size
+ */
+ opengl_redraw_needed (this_gen);
+
+ pthread_mutex_lock (&this->render_action_mutex);
+ if (this->render_action <= RENDER_DRAW) {
+ this->render_action = RENDER_DRAW;
+ pthread_cond_signal (&this->render_action_cond);
+ }
+ pthread_mutex_unlock (&this->render_action_mutex);
+
+ lprintf ("display frame done\n");
+}
-static int opengl_set_property (vo_driver_t *this_gen,
- int property, int value) {
+static int opengl_get_property (vo_driver_t *this_gen, int property) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+
+ switch (property) {
+ case VO_PROP_ASPECT_RATIO:
+ return this->sc.user_ratio;
+ case VO_PROP_MAX_NUM_FRAMES:
+ return 15;
+ case VO_PROP_BRIGHTNESS:
+ return this->yuv2rgb_brightness;
+ case VO_PROP_CONTRAST:
+ return this->yuv2rgb_contrast;
+ case VO_PROP_SATURATION:
+ return this->yuv2rgb_saturation;
+ case VO_PROP_WINDOW_WIDTH:
+ return this->sc.gui_width;
+ case VO_PROP_WINDOW_HEIGHT:
+ return this->sc.gui_height;
+ default:
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "video_out_opengl: tried to get unsupported property %d\n", property);
+ }
+
+ return 0;
+}
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
-
- DEBUGF ((stderr, "*** set_property\n"));
- switch (property) {
- case VO_PROP_ASPECT_RATIO:
- if (value >= XINE_VO_ASPECT_NUM_RATIOS)
- value = XINE_VO_ASPECT_AUTO;
- this->sc.user_ratio = value;
- xprintf (this->xine, XINE_VERBOSITY_DEBUG, "video_out_opengl: aspect ratio changed to %s\n",
- _x_vo_scale_aspect_ratio_name (value));
- opengl_compute_ideal_size (this);
-// opengl_redraw_needed ((vo_driver_t *) this);
- break;
- case VO_PROP_BRIGHTNESS:
- this->yuv2rgb_brightness = value;
- this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory, value, 128, 128);
- xprintf(this->xine, XINE_VERBOSITY_DEBUG, "video_out_opengl: brightness changed to %d\n",value);
- break;
- default:
- xprintf (this->xine, XINE_VERBOSITY_DEBUG,
- "video_out_opengl: tried to set unsupported property %d\n", property);
- }
-
- return value;
+static int opengl_set_property (vo_driver_t *this_gen,
+ int property, int value) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+
+ switch (property) {
+ case VO_PROP_ASPECT_RATIO:
+ if (value>=XINE_VO_ASPECT_NUM_RATIOS)
+ value = XINE_VO_ASPECT_AUTO;
+ this->sc.user_ratio = value;
+ opengl_compute_ideal_size (this);
+ this->sc.force_redraw = 1; /* trigger re-calc of output size */
+
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "video_out_opengl: aspect ratio changed to %s\n", _x_vo_scale_aspect_ratio_name(value));
+ break;
+ case VO_PROP_BRIGHTNESS:
+ this->yuv2rgb_brightness = value;
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+ this->sc.force_redraw = 1;
+ break;
+ case VO_PROP_CONTRAST:
+ this->yuv2rgb_contrast = value;
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+ this->sc.force_redraw = 1;
+ break;
+ case VO_PROP_SATURATION:
+ this->yuv2rgb_saturation = value;
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+ this->sc.force_redraw = 1;
+ break;
+ default:
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "video_out_opengl: tried to set unsupported property %d\n", property);
+ }
+
+ return value;
}
static void opengl_get_property_min_max (vo_driver_t *this_gen,
- int property, int *min, int *max) {
-
- DEBUGF ((stderr, "get_property_min_max\n"));
- /* opengl_driver_t *this = (opengl_driver_t *) this_gen; */
- if ( property == VO_PROP_BRIGHTNESS) {
- *min = -100;
- *max = +100;
- } else {
- *min = 0;
- *max = 0;
- }
+ int property, int *min, int *max) {
+ /* opengl_driver_t *this = (opengl_driver_t *) this_gen; */
+
+ switch (property) {
+ case VO_PROP_BRIGHTNESS:
+ *min = -128; *max = 127; break;
+ case VO_PROP_CONTRAST:
+ *min = 0; *max = 255; break;
+ case VO_PROP_SATURATION:
+ *min = 0; *max = 255; break;
+ default:
+ *min = 0; *max = 0;
+ }
}
-
static int opengl_gui_data_exchange (vo_driver_t *this_gen,
- int data_type, void *data) {
+ int data_type, void *data) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
- static int glxAttrib[] = {
- GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None
- } ;
-
- DEBUGF ((stderr, "*** gui_data_exchange ***\n"));
+ switch (data_type) {
+#ifndef XINE_DISABLE_DEPRECATED_FEATURES
+ case XINE_GUI_SEND_COMPLETION_EVENT:
+ break;
+#endif
- switch (data_type) {
+ case XINE_GUI_SEND_EXPOSE_EVENT:
+
+ lprintf ("expose event\n");
- case XINE_GUI_SEND_SELECT_VISUAL:
- DEBUGF ((stderr, "*** gui_select_visual ***\n"));
- XLockDisplay (this->display);
- this->vinfo = glXChooseVisual (this->display, this->screen, glxAttrib);
- XUnlockDisplay (this->display);
- if (this->vinfo == NULL)
- xprintf (this->xine, XINE_VERBOSITY_DEBUG,
- "video_out_opengl: no OpenGL support available (glXChooseVisual)\n");
- *(XVisualInfo**)data = this->vinfo;
- DEBUGF ((stderr, "*** visual %p depth %d\n", this->vinfo->visual, this->vinfo->depth));
- break;
+ if (this->frame[0]) {
+ XExposeEvent * xev = (XExposeEvent *) data;
- case XINE_GUI_SEND_EXPOSE_EVENT:
- DEBUGF ((stderr, "*** gui_expose ***\n"));
-
- /* Note that the global GLX context is not available on all
- * architectures in this thread */
- /* Note that using different contextes in different threads
- * for the same drawable seems to be broken with several
- * OpenGL implementations */
- /* Thus nothing is updated here, just the render thread is
- * notified */
- if (this->context_state == CONTEXT_SET)
- this->context_state = CONTEXT_RELOAD;
- break;
-
- case XINE_GUI_SEND_DRAWABLE_CHANGED:
- DEBUGF ((stderr, "*** gui_drawable_changed: %ld\n", (Drawable) data));
+ if (xev && xev->count == 0) {
+ pthread_mutex_lock (&this->render_action_mutex);
+ if (this->render_action <= RENDER_CLEAN) {
+ this->render_action = RENDER_CLEAN;
+ pthread_cond_signal (&this->render_action_cond);
+ }
+ pthread_mutex_unlock (&this->render_action_mutex);
XLockDisplay (this->display);
- /* Unfortunately, the last drawable is already gone, so we cannot
- * destroy the former context. This is a memory leak. Unfortunately. */
- /* Even if the drawable remains the same, this does not seem to
- * work :( */
- /* FIXME: check that */
-#if 0
- if (this->drawable == (Drawable) data)
- this->context_state = CONTEXT_SAME_DRAWABLE;
- else
- this->context_state = CONTEXT_BAD;
-#else
- this->context_state = CONTEXT_BAD;
-#endif
- if (this->context_state == CONTEXT_BAD)
- DEBUGF ((stderr, "*** drawable changed, state now bad\n"));
- this->drawable = (Drawable) data;
+ if(this->xoverlay)
+ x11osd_expose(this->xoverlay);
+ XSync(this->display, False);
XUnlockDisplay (this->display);
- break;
-
- case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO: {
- x11_rectangle_t *rect = data;
- int x1, y1, x2, y2;
-/* DEBUGF ((stderr, "*** gui_translate_gui_to_video ***\n")); */
+ }
+ }
+ break;
+
+ case XINE_GUI_SEND_SELECT_VISUAL:
+ if (! this->context) {
+ pthread_mutex_lock (&this->render_action_mutex);
+ this->render_action = RENDER_VISUAL;
+ pthread_cond_signal (&this->render_action_cond);
+ pthread_cond_wait (&this->render_return_cond,
+ &this->render_action_mutex);
+ pthread_mutex_unlock (&this->render_action_mutex);
+ *(XVisualInfo**)data = this->vinfo;
+ }
+ break;
+ /* TODO: this event is yet to be implemented in the gui */
+ case XINE_GUI_SEND_WILL_DESTROY_DRAWABLE:
+ pthread_mutex_lock (&this->render_action_mutex);
+ this->render_action = RENDER_RELEASE;
+ pthread_cond_signal (&this->render_action_cond);
+ pthread_cond_wait (&this->render_return_cond,
+ &this->render_action_mutex);
+ pthread_mutex_unlock (&this->render_action_mutex);
+ break;
+
+ case XINE_GUI_SEND_DRAWABLE_CHANGED:
- _x_vo_scale_translate_gui2video(&this->sc, rect->x, rect->y,
- &x1, &y1);
- _x_vo_scale_translate_gui2video(&this->sc,
- rect->x + rect->w, rect->y + rect->h,
- &x2, &y2);
- rect->x = x1;
- rect->y = y1;
- rect->w = x2-x1;
- rect->h = y2-y1;
- }
- break;
+ this->drawable = (Drawable) data;
+ pthread_mutex_lock (&this->render_action_mutex);
+ this->render_action = RENDER_CREATE;
+ pthread_cond_signal (&this->render_action_cond);
+ pthread_cond_wait (&this->render_return_cond,
+ &this->render_action_mutex);
+ pthread_mutex_unlock (&this->render_action_mutex);
+ if (! this->context)
+ xprintf (this->xine, XINE_VERBOSITY_NONE,
+ "video_out_opengl: cannot create OpenGL capable visual.\n"
+ " plugin will not work.\n");
+ XLockDisplay (this->display);
+ if(this->xoverlay)
+ x11osd_drawable_changed(this->xoverlay, this->drawable);
+ this->ovl_changed = 1;
+ XUnlockDisplay (this->display);
+ break;
- default:
- return -1;
+ case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO:
+
+ if (this->frame[0]) {
+ x11_rectangle_t *rect = data;
+ int x1, y1, x2, y2;
+
+ _x_vo_scale_translate_gui2video(&this->sc,
+ rect->x, rect->y,
+ &x1, &y1);
+ _x_vo_scale_translate_gui2video(&this->sc,
+ rect->x + rect->w, rect->y + rect->h,
+ &x2, &y2);
+ rect->x = x1;
+ rect->y = y1;
+ rect->w = x2-x1;
+ rect->h = y2-y1;
}
+ break;
-/* DEBUGF ((stderr, "done gui_data_exchange\n")); */
- return 0;
-}
+ default:
+ return -1;
+ }
+ return 0;
+}
static void opengl_dispose (vo_driver_t *this_gen) {
- opengl_driver_t *this = (opengl_driver_t *) this_gen;
-
-// XLockDisplay (this->display);
-// if (this->cur_frame)
-// this->cur_frame->vo_frame.dispose (&this->cur_frame->vo_frame);
-// XUnlockDisplay (this->display);
-
- glXMakeCurrent (this->display, None, NULL);
- glXDestroyContext (this->display, this->context);
- this->context = NULL;
-
- free (this);
-}
-
-
-static vo_driver_t *opengl_open_plugin (video_driver_class_t *class_gen,
- const void *visual_gen) {
- opengl_class_t *class = (opengl_class_t *) class_gen;
- x11_visual_t *visual = (x11_visual_t *) visual_gen;
-
- opengl_driver_t *this;
-
- xprintf (class->xine, XINE_VERBOSITY_DEBUG, "EXPERIMENTAL opengl output plugin TNG\n");
-
- /*
- * allocate plugin struct
- */
- this = calloc (1, sizeof (opengl_driver_t));
- _x_assert(this);
-
- this->config = class->config;
- this->xine = class->xine;
- this->display = visual->display;
- this->screen = visual->screen;
-
- _x_vo_scale_init (&this->sc, 0, 0, class->config);
-
- this->sc.frame_output_cb = visual->frame_output_cb;
- this->sc.dest_size_cb = visual->dest_size_cb;
- this->sc.user_data = visual->user_data;
- this->sc.user_ratio = XINE_VO_ASPECT_AUTO;
-
- /* We will not be able to use the current drawable... */
- this->drawable = None;
- this->context_state = CONTEXT_BAD;
-
- this->vo_driver.get_capabilities = opengl_get_capabilities;
- this->vo_driver.alloc_frame = opengl_alloc_frame;
- this->vo_driver.update_frame_format = opengl_update_frame_format;
- this->vo_driver.overlay_begin = NULL; /* not used */
- this->vo_driver.overlay_blend = opengl_overlay_blend;
- this->vo_driver.overlay_end = NULL; /* not used */
- this->vo_driver.display_frame = opengl_display_frame;
- this->vo_driver.get_property = opengl_get_property;
- this->vo_driver.set_property = opengl_set_property;
- this->vo_driver.get_property_min_max = opengl_get_property_min_max;
- this->vo_driver.gui_data_exchange = opengl_gui_data_exchange;
- this->vo_driver.dispose = opengl_dispose;
- this->vo_driver.redraw_needed = opengl_redraw_needed;
-
- this->yuv2rgb_brightness =
- this->config->register_range(this->config, "video.opengl_gamma", 0,
- -100, 100,
- _("brightness correction"),
- _("The brightness correction can be used to lighten or darken the image. "
- "It changes the blacklevel without modifying the contrast, but it "
- "limits the tonal range."),
- 0, NULL, NULL);
- this->yuv2rgb_factory = yuv2rgb_factory_init (YUV_FORMAT, YUV_SWAP_MODE,
- this->yuv2rgb_cmap);
- this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
- this->yuv2rgb_brightness, 128, 128);
- return &this->vo_driver;
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+ int i;
+
+ pthread_mutex_lock (&this->render_action_mutex);
+ this->render_action = RENDER_EXIT;
+ pthread_cond_signal (&this->render_action_cond);
+ pthread_mutex_unlock (&this->render_action_mutex);
+ pthread_join (this->render_thread, NULL);
+ pthread_mutex_destroy (&this->render_action_mutex);
+ pthread_cond_destroy (&this->render_action_cond);
+ pthread_cond_destroy (&this->render_return_cond);
+
+ for (i = 0; i < NUM_FRAMES_BACKLOG; i++)
+ if (this->frame[i])
+ this->frame[i]->vo_frame.dispose (&this->frame[i]->vo_frame);
+
+ this->yuv2rgb_factory->dispose (this->yuv2rgb_factory);
+
+ if (this->xoverlay) {
+ XLockDisplay (this->display);
+ x11osd_destroy (this->xoverlay);
+ XUnlockDisplay (this->display);
+ }
+
+ free (this);
+}
+
+static void opengl_cb_render_fun (void *this_gen, xine_cfg_entry_t *entry) {
+ opengl_driver_t *this = (opengl_driver_t *) this_gen;
+ this->render_fun_id = entry->num_value;
+ pthread_mutex_lock (&this->render_action_mutex);
+ if (this->render_action <= RENDER_SETUP) {
+ this->render_action = RENDER_SETUP;
+ pthread_cond_signal (&this->render_action_cond);
+ }
+ pthread_mutex_unlock (&this->render_action_mutex);
+}
+
+static void opengl_cb_default (void *val_gen, xine_cfg_entry_t *entry) {
+ int *val = (int *) val_gen;
+ *val = entry->num_value;
+}
+
+static vo_driver_t *opengl_open_plugin (video_driver_class_t *class_gen, const void *visual_gen) {
+ opengl_class_t *class = (opengl_class_t *) class_gen;
+ config_values_t *config = class->config;
+ x11_visual_t *visual = (x11_visual_t *) visual_gen;
+ opengl_driver_t *this;
+
+ this = (opengl_driver_t *) xine_xmalloc (sizeof (opengl_driver_t));
+
+ if (!this)
+ return NULL;
+
+ this->display = visual->display;
+ this->screen = visual->screen;
+
+ _x_vo_scale_init (&this->sc, 0, 0, config);
+ this->sc.frame_output_cb = visual->frame_output_cb;
+ this->sc.dest_size_cb = visual->dest_size_cb;
+ this->sc.user_data = visual->user_data;
+ this->sc.user_ratio = XINE_VO_ASPECT_AUTO;
+
+ this->drawable = visual->d;
+ this->gui_width = this->gui_height = -1;
+ this->last_width = this->last_height = -1;
+
+ this->xoverlay = NULL;
+ this->ovl_changed = 0;
+ this->xine = class->xine;
+
+ this->vo_driver.get_capabilities = opengl_get_capabilities;
+ this->vo_driver.alloc_frame = opengl_alloc_frame;
+ this->vo_driver.update_frame_format = opengl_update_frame_format;
+ this->vo_driver.overlay_begin = opengl_overlay_begin;
+ this->vo_driver.overlay_blend = opengl_overlay_blend;
+ this->vo_driver.overlay_end = opengl_overlay_end;
+ this->vo_driver.display_frame = opengl_display_frame;
+ this->vo_driver.get_property = opengl_get_property;
+ this->vo_driver.set_property = opengl_set_property;
+ this->vo_driver.get_property_min_max = opengl_get_property_min_max;
+ this->vo_driver.gui_data_exchange = opengl_gui_data_exchange;
+ this->vo_driver.dispose = opengl_dispose;
+ this->vo_driver.redraw_needed = opengl_redraw_needed;
+
+ this->yuv2rgb_brightness = config->register_range (config, "video.opengl_gamma", 0,
+ -128, 127,
+ _("brightness correction"),
+ _("The brightness correction can be used to "
+ "lighten or darken the image. It changes the "
+ "blacklevel without modifying the contrast, "
+ "but it limits the tonal range."),
+ 0, NULL, NULL);
+ this->yuv2rgb_contrast = 128;
+ this->yuv2rgb_saturation = 128;
+
+ this->yuv2rgb_factory = yuv2rgb_factory_init (YUV_FORMAT, YUV_SWAP_MODE,
+ NULL);
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+
+ XLockDisplay (this->display);
+ this->xoverlay = x11osd_create (this->xine, this->display, this->screen,
+ this->drawable, X11OSD_SHAPED);
+ XUnlockDisplay (this->display);
+
+ this->render_fun_id = config->register_enum (config, "video.opengl_renderer",
+ 0, opengl_render_fun_names,
+ _("OpenGL renderer"),
+ _("The OpenGL plugin provides several render modules:\n\n"
+ "2D_Textures\n"
+ "This module downloads the images as 2D textures and renders a textured slice.\n"
+ "This is typically the fastest method.\n\n"
+ "Image_Pipeline\n"
+ "This module uses glDraw() to render the images.\n"
+ "Only accelerated on few drivers.\n"
+ "Does not interpolate on scaling.\n\n"
+ "Cylinder\n"
+ "Shows images on a rotating cylinder. Nice effect :)\n\n"
+ "Environment_Mapped_Torus\n"
+ "Show images reflected in a spinning torus. Way cool =)"),
+ 10, opengl_cb_render_fun, this);
+ this->render_min_fps = config->register_range (config,
+ "video.opengl_min_fps",
+ 20, 1, 120,
+ _("OpenGL minimum framerate"),
+ _("Minimum framerate for animated render routines.\n"
+ "Ignored for static render routines.\n"),
+ 20, opengl_cb_default,
+ &this->render_min_fps);
+ this->render_double_buffer = config->register_bool (config, "video.opengl_double_buffer", 1,
+ _("enable double buffering"),
+ _("For OpenGL double buffering does not only remove tearing artifacts,\n"
+ "it also reduces flickering a lot.\n"
+ " It should not have any performance impact."),
+ 20, NULL, NULL);
+
+ pthread_mutex_init (&this->render_action_mutex, NULL);
+ pthread_cond_init (&this->render_action_cond, NULL);
+ pthread_cond_init (&this->render_return_cond, NULL);
+ pthread_create (&this->render_thread, NULL, (thread_run_t) render_run, this);
+
+ /* Check for OpenGL capable visual */
+ pthread_mutex_lock (&this->render_action_mutex);
+ this->render_action = RENDER_VISUAL;
+ pthread_cond_signal (&this->render_action_cond);
+ pthread_cond_wait (&this->render_return_cond,
+ &this->render_action_mutex);
+ if (this->vinfo) {
+ /* Create context if possible w/o drawable change */
+ this->render_action = RENDER_CREATE;
+ pthread_cond_signal (&this->render_action_cond);
+ pthread_cond_wait (&this->render_return_cond,
+ &this->render_action_mutex);
+ }
+ pthread_mutex_unlock (&this->render_action_mutex);
+
+ if (! this->vinfo) {
+ /* no OpenGL capable visual available */
+ opengl_dispose (&this->vo_driver);
+ return NULL;
+ }
+ if (! this->context)
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "video_out_opengl: default visual not OpenGL capable\n"
+ " plugin will only work with clients supporting XINE_GUI_SEND_SELECT_VISUAL.\n");
+
+ return &this->vo_driver;
}
/*
- * Class Functions
+ * class functions
*/
+
static char* opengl_get_identifier (video_driver_class_t *this_gen) {
- return "OpenGL";
+ return "opengl";
}
static char* opengl_get_description (video_driver_class_t *this_gen) {
- return _("xine video output plugin using OpenGL - TNG");
+ return _("xine video output plugin using the MIT X shared memory extension");
}
-static void opengl_dispose_class (video_driver_class_t *this) {
+static void opengl_dispose_class (video_driver_class_t *this_gen) {
+ opengl_class_t *this = (opengl_class_t *) this_gen;
- free (this);
+ free (this);
}
static void *opengl_init_class (xine_t *xine, void *visual_gen) {
+ opengl_class_t *this = (opengl_class_t *) xine_xmalloc (sizeof (opengl_class_t));
- opengl_class_t *this = (opengl_class_t *) xine_xmalloc (sizeof (opengl_class_t));
-
- this->driver_class.open_plugin = opengl_open_plugin;
- this->driver_class.get_identifier = opengl_get_identifier;
- this->driver_class.get_description = opengl_get_description;
- this->driver_class.dispose = opengl_dispose_class;
+ this->driver_class.open_plugin = opengl_open_plugin;
+ this->driver_class.get_identifier = opengl_get_identifier;
+ this->driver_class.get_description = opengl_get_description;
+ this->driver_class.dispose = opengl_dispose_class;
+ this->config = xine->config;
+ this->xine = xine;
- this->config = xine->config;
- this->xine = xine;
-
- return this;
+ return this;
}
static vo_info_t vo_info_opengl = {
- 3, /* priority */
+ 7, /* priority */
XINE_VISUAL_TYPE_X11 /* visual type */
};
@@ -1014,9 +1447,7 @@ static vo_info_t vo_info_opengl = {
*/
plugin_info_t xine_plugin_info[] = {
- /* type, API, "name", version, special_info, init_function */
- { PLUGIN_VIDEO_OUT, 20, "opengl", XINE_VERSION_CODE,
- &vo_info_opengl, opengl_init_class },
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_VIDEO_OUT, 20, "opengl", XINE_VERSION_CODE, &vo_info_opengl, opengl_init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
-