diff options
author | Matthias Hopf <mat@mshopf.de> | 2004-11-23 15:01:07 +0000 |
---|---|---|
committer | Matthias Hopf <mat@mshopf.de> | 2004-11-23 15:01:07 +0000 |
commit | 3ef7a1eeb76be9d5f009e34c374362fca7dcbc87 (patch) | |
tree | 0229d2681c5ab1c411562789626ba843cda60ca1 | |
parent | b5a6bc32771696f7046c806608f9c802ad4f13e8 (diff) | |
download | xine-lib-3ef7a1eeb76be9d5f009e34c374362fca7dcbc87.tar.gz xine-lib-3ef7a1eeb76be9d5f009e34c374362fca7dcbc87.tar.bz2 |
Rewrote OpenGL output plugin.
CVS patchset: 7150
CVS date: 2004/11/23 15:01:07
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | doc/README.opengl | 51 | ||||
-rw-r--r-- | src/video_out/Makefile.am | 4 | ||||
-rw-r--r-- | src/video_out/video_out_opengl.c | 2045 |
4 files changed, 1266 insertions, 835 deletions
@@ -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 } }; - |