diff options
-rw-r--r-- | acconfig.h | 3 | ||||
-rw-r--r-- | configure.in | 11 | ||||
-rw-r--r-- | misc/xine-lib.spec.in | 13 | ||||
-rw-r--r-- | src/video_out/Makefile.am | 10 | ||||
-rw-r--r-- | src/video_out/video_out_opengl.c | 1037 | ||||
-rw-r--r-- | src/video_out/video_out_x11.h | 7 |
6 files changed, 1079 insertions, 2 deletions
diff --git a/acconfig.h b/acconfig.h index 4ac95a5a3..ef2234091 100644 --- a/acconfig.h +++ b/acconfig.h @@ -60,6 +60,9 @@ /* Define this if you have libXv installed */ #undef HAVE_XV +/* Define this if you have OpenGL support available */ +#undef HAVE_OPENGL + /* Define this if you have libXinerama installed */ #undef HAVE_XINERAMA diff --git a/configure.in b/configure.in index f72ac2ea8..4e00fec01 100644 --- a/configure.in +++ b/configure.in @@ -240,6 +240,17 @@ dnl but only dynamic linking is possible when using libtool < 1.4.0 AC_PREREQ_LIBTOOL(1.4.0, xv_lib="libXv.a", xv_lib="libXv.so") +dnl +dnl Check for OpenGL +dnl +AC_CHECK_LIB(GL, glBegin, + OPENGL_LIBS="-lGL" + ac_have_opengl="yes",, + -lGL) +AC_SUBST(OPENGL_LIBS) +AM_CONDITIONAL(HAVE_OPENGL, test x$ac_have_opengl = "xyes") + + host_or_hostalias="$host" if test "$host_or_hostalias" = ""; then dnl user has called ./configure with a host parameter unknown to diff --git a/misc/xine-lib.spec.in b/misc/xine-lib.spec.in index dcafc33c9..d46dc0c53 100644 --- a/misc/xine-lib.spec.in +++ b/misc/xine-lib.spec.in @@ -56,6 +56,11 @@ BuildRoot: /tmp/%{name}-root @HAVE_AA_TRUE@Group: Development/Libraries @HAVE_AA_TRUE@Requires: xine-lib >= %{ver} +@HAVE_OPENGL_TRUE@%package opengl +@HAVE_OPENGL_TRUE@Summary: XINE - OpenGL support. +@HAVE_OPENGL_TRUE@Group: Development/Libraries +@HAVE_OPENGL_TRUE@Requires: xine-lib >= %{ver} + @W32DLL_DEP@%package w32dll @W32DLL_DEP@Summary: XINE - win32dll decoder support. @W32DLL_DEP@Group: Development/Libraries @@ -130,6 +135,9 @@ ler qualquer vídeo com máximo desempenho. @HAVE_AA_TRUE@%description aa @HAVE_AA_TRUE@video plugin using Ascii Art library. +@HAVE_OPENGL_TRUE@%description opengl +@HAVE_OPENGL_TRUE@video plugin using OpenGL library. + @W32DLL_DEP@%description w32dll @W32DLL_DEP@win32dll decoder support. @@ -317,6 +325,11 @@ rm -rf $RPM_BUILD_ROOT @HAVE_AA_TRUE@%{prefix}/lib/xine/plugins/xineplug_vo_out_aa.la @HAVE_AA_TRUE@%{prefix}/lib/xine/plugins/xineplug_vo_out_aa.so +@HAVE_OPENGL_TRUE@%files opengl +@HAVE_OPENGL_TRUE@%defattr(-,root,root) +@HAVE_OPENGL_TRUE@%{prefix}/lib/xine/plugins/xineplug_vo_out_opengl.la +@HAVE_OPENGL_TRUE@%{prefix}/lib/xine/plugins/xineplug_vo_out_opengl.so + @W32DLL_DEP@%files w32dll @W32DLL_DEP@%defattr(-,root,root) @W32DLL_DEP@%{prefix}/lib/xine/plugins/xineplug_decode_w32dll.la diff --git a/src/video_out/Makefile.am b/src/video_out/Makefile.am index 978203c27..ee6844f17 100644 --- a/src/video_out/Makefile.am +++ b/src/video_out/Makefile.am @@ -11,6 +11,9 @@ syncfb_module = xineplug_vo_out_syncfb.la if HAVE_XV xv_module = xineplug_vo_out_xv.la endif +if HAVE_OPENGL +opengl_module = xineplug_vo_out_opengl.la +endif endif if HAVE_AA @@ -33,7 +36,7 @@ endif # scheme "xineplug_vo_out_" # lib_LTLIBRARIES = $(xv_module) $(syncfb_module) $(xshm_module) $(aa_module) \ - $(fb_module) $(sdl_module) + $(fb_module) $(sdl_module) $(opengl_module) xineplug_vo_out_xv_la_SOURCES = deinterlace.c alphablend.c video_out_xv.c xineplug_vo_out_xv_la_LIBADD = $(XV_LIB) $(X_LIBS) -lXext @@ -44,6 +47,11 @@ xineplug_vo_out_xshm_la_SOURCES = yuv2rgb.c yuv2rgb_mmx.c yuv2rgb_mlib.c \ xineplug_vo_out_xshm_la_LIBADD = $(X_LIBS) -lXext xineplug_vo_out_xshm_la_LDFLAGS = -avoid-version -module +xineplug_vo_out_opengl_la_SOURCES = yuv2rgb.c yuv2rgb_mmx.c yuv2rgb_mlib.c \ + alphablend.c video_out_opengl.c +xineplug_vo_out_opengl_la_LIBADD = $(OPENGL_LIBS) $(X_LIBS) +xineplug_vo_out_opengl_la_LDFLAGS = -avoid-version -module + xineplug_vo_out_syncfb_la_SOURCES = alphablend.c video_out_syncfb.c xineplug_vo_out_syncfb_la_LDFLAGS = -avoid-version -module diff --git a/src/video_out/video_out_opengl.c b/src/video_out/video_out_opengl.c new file mode 100644 index 000000000..923fe8278 --- /dev/null +++ b/src/video_out/video_out_opengl.c @@ -0,0 +1,1037 @@ +/* + * Copyright (C) 2000, 2001 the xine project + * + * This file is part of xine, a unix video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.1 2002/01/22 20:15:00 mshopf Exp $ + * + * video_out_glut.c, glut based OpenGL rendering interface for xine + * Matthias Hopf <mat@mshopf.de> + * + * Based on video_out_xshm.c and video_out_xv.c + */ + +/* + * TODO: + * + * - Race Condition? Sometimes the output window remains empty... + * - Strange frame when stopping playback + * - Works only with first video so far - the next video seems to be + * rendered by a different thread, without notification :-( + * - Textures instead of glDraw() + * - Use GL_RGBA vs GL_BGRA (endianess?!?) + * - Check extensions + * - Color conversion in hardware? + * - Video extension + * - ColorMatrix (OpenGL-1.2 or SGI_color_matrix) + * - Alpha Blending for overlays using texture hardware + * - Check overlay colors (still YUV?!?) + */ + + +#if 0 +# define DEBUGF(x) fprintf x +#else +# define DEBUGF(x) ((void) 0) +#endif + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <assert.h> + +#include <X11/Xlib.h> + +#include "video_out.h" +#include "video_out_x11.h" + +#include <GL/gl.h> +#include <GL/glx.h> + +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/time.h> + +#include <pthread.h> +#include <netinet/in.h> + +#include "xine_internal.h" +#include "alphablend.h" +#include "yuv2rgb.h" +#include "xineutils.h" + + +#define STRIPE_HEIGHT 16 + +#define BYTES_PER_PIXEL 4 + +# if (BYTES_PER_PIXEL != 4) +/* currently nothing else is supported */ +# error "BYTES_PER_PIXEL bad" +# endif +#ifdef WORDS_BIGENDIAN +# define RGB_TEXTURE_FORMAT GL_RGBA +# define YUV_FORMAT MODE_32_BGR +# define YUV_SWAP_MODE 1 +#else +/* FIXME: check extension */ +# define RGB_TEXTURE_FORMAT GL_BGRA +# define YUV_FORMAT MODE_32_RGB +# define YUV_SWAP_MODE 0 +#endif + +typedef struct opengl_frame_s { + vo_frame_t vo_frame; + uint8_t *chunk[3]; + + int width, height; + int ratio_code; + int format; + int stripe_inc; + + uint8_t *rgb_dst; + uint8_t *texture; +} opengl_frame_t; + +typedef struct opengl_driver_s { + + vo_driver_t vo_driver; + + config_values_t *config; + + opengl_frame_t *cur_frame; + vo_overlay_t *overlay; + + /* X11 / Xv related stuff */ + Display *display; + int screen; + Drawable drawable; + + /* OpenGL related */ + volatile GLXContext context; + volatile int context_state; /* is the context ok, or reload? */ + XVisualInfo *vinfo; + pthread_t renderthread; +/* int texwidth, texheight; */ + + /* size / aspect ratio calculations */ + /* delivered from the decoder */ + int delivered_width; + int delivered_height; + int delivered_ratio_code; + int delivered_flags; + double ratio_factor; /* output frame must fulfill: height = width * ratio_factor */ + + /* output area */ + int window_width; + int window_height; + int output_width; + int output_height; + int user_ratio; + + /* last displayed frame */ + int last_frame_width; /* original size */ + int last_frame_height; /* original size */ + int last_frame_ratio_code; + + /* software yuv2rgb related */ + yuv2rgb_t *yuv2rgb; + int prof_yuv2rgb; + int yuv_width; /* width/height yuv2rgb is configured for */ + int yuv_stride; + + /* TODO: check */ + int zoom_mpeg1; + + /* display anatomy */ + double display_ratio; /* given by visual parameter from init function */ + void *user_data; + + /* gui callbacks */ + + void (*request_dest_size) (void *user_data, + int video_width, int video_height, + int *dest_x, int *dest_y, + int *dest_height, int *dest_width); + + void (*calc_dest_size) (void *user_data, + int video_width, int video_height, + int *dest_width, int *dest_height); + +} opengl_driver_t; + +#define CONTEXT_BAD 0 +#define CONTEXT_SAME_DRAWABLE 1 +#define CONTEXT_SET 2 +#define CONTEXT_RELOAD 3 + + +/* + * first, some utility functions + */ +vo_info_t *get_video_out_plugin_info(); + +static void *my_malloc_aligned (size_t alignment, size_t size, uint8_t **chunk) { + + uint8_t *pMem; + + pMem = xine_xmalloc (size+alignment); + + *chunk = pMem; + + while ((int) pMem % alignment) + pMem++; + + return pMem; +} + + +/* + * and now, the driver functions + */ + +static uint32_t opengl_get_capabilities (vo_driver_t *this_gen) { + return VO_CAP_COPIES_IMAGE | VO_CAP_YV12 | VO_CAP_YUY2; +} + + +static void opengl_frame_copy (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->instance->driver; + +/*fprintf (stderr, "*** frame_copy\n"); */ + xine_profiler_start_count (this->prof_yuv2rgb); + + if (frame->format == IMGFMT_YV12) { + this->yuv2rgb->yuv2rgb_fun (this->yuv2rgb, frame->rgb_dst, + src[0], src[1], src[2]); + } else { + + this->yuv2rgb->yuy22rgb_fun (this->yuv2rgb, frame->rgb_dst, + src[0]); + + } + + xine_profiler_stop_count (this->prof_yuv2rgb); + + frame->rgb_dst += frame->stripe_inc; +} + +static void opengl_frame_field (vo_frame_t *vo_img, int which_field) { + + opengl_frame_t *frame = (opengl_frame_t *) vo_img ; + +/*fprintf (stderr, "*** frame_field\n"); */ + switch (which_field) { + case VO_TOP_FIELD: + frame->rgb_dst = frame->texture; + frame->stripe_inc = 2*STRIPE_HEIGHT * frame->width * BYTES_PER_PIXEL; + break; + case VO_BOTTOM_FIELD: + frame->rgb_dst = frame->texture + frame->width * BYTES_PER_PIXEL ; + frame->stripe_inc = 2*STRIPE_HEIGHT * frame->width * BYTES_PER_PIXEL; + break; + case VO_BOTH_FIELDS: + frame->rgb_dst = frame->texture; + frame->stripe_inc = STRIPE_HEIGHT * frame->width * BYTES_PER_PIXEL; + break; + } +} + +static void opengl_frame_dispose (vo_frame_t *vo_img) { + + opengl_frame_t *frame = (opengl_frame_t *) vo_img ; + +/*fprintf (stderr, "*** frame_dispose ***\n"); */ + if (frame) + { + free (frame->texture); + free (frame->chunk[0]); + free (frame->chunk[1]); + free (frame->chunk[2]); + } + free (frame); +} + + +static vo_frame_t *opengl_alloc_frame (vo_driver_t *this_gen) { + opengl_frame_t *frame ; + +/*fprintf (stderr, "*** alloc_frame ***\n"); */ + frame = (opengl_frame_t *) calloc (1, sizeof (opengl_frame_t)); + if (frame==NULL) { + printf ("opengl_alloc_frame: out of memory\n"); + return NULL; + } + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + + /* + * supply required functions + */ + + frame->vo_frame.copy = opengl_frame_copy; + frame->vo_frame.field = opengl_frame_field; + frame->vo_frame.dispose = opengl_frame_dispose; + + return (vo_frame_t *) frame; +} + + +static void opengl_adapt_to_output_area (opengl_driver_t *this, + int dest_width, int dest_height) +{ + this->window_width = dest_width; + this->window_height = dest_height; + + /* + * make the frames fit into the given destination area + */ + if ( ((double) dest_width / this->ratio_factor) < dest_height ) { + this->output_width = dest_width ; + this->output_height = (double) dest_width / this->ratio_factor + .5; + } else { + this->output_width = (double) dest_height * this->ratio_factor + .5; + this->output_height = dest_height; + } + + /* Force reload / state reinitialization / clear */ + if (this->context_state == CONTEXT_SET) + this->context_state = CONTEXT_RELOAD; +} + + +static void opengl_calc_format (opengl_driver_t *this, + int width, int height, int ratio_code) { + double image_ratio, desired_ratio; + double corr_factor; + int ideal_width, ideal_height; + int dest_x, dest_y, dest_width, dest_height; + + this->delivered_width = width; + this->delivered_height = height; + this->delivered_ratio_code = ratio_code; + + if ( (!width) || (!height) ) + return; + + /* + * aspect ratio calculation + */ + image_ratio = + (double) this->delivered_width / (double) this->delivered_height; + + switch (this->user_ratio) { + case ASPECT_AUTO: + switch (ratio_code) { + case XINE_ASPECT_RATIO_ANAMORPHIC: /* anamorphic */ + desired_ratio = 16.0 /9.0; + break; + case XINE_ASPECT_RATIO_211_1: /* 2.11:1 */ + desired_ratio = 2.11/1.0; + break; + case XINE_ASPECT_RATIO_SQUARE: /* square pels */ + case XINE_ASPECT_RATIO_DONT_TOUCH: /* probably non-mpeg stream => don't tou +ch aspect ratio */ + desired_ratio = image_ratio; + break; + case 0: /* forbidden -> 4:3 */ + printf ("video_out_opengl: invalid ratio, using 4:3\n"); + default: + printf ("video_out_opengl: unknown aspect ratio (%d) in stream => using +4:3\n", + this->delivered_ratio_code); + case XINE_ASPECT_RATIO_4_3: /* 4:3 */ + desired_ratio = 4.0 / 3.0; + break; + } + break; + case ASPECT_ANAMORPHIC: + desired_ratio = 16.0 / 9.0; + break; + case ASPECT_DVB: + desired_ratio = 2.0 / 1.0; + break; + case ASPECT_SQUARE: + desired_ratio = image_ratio; + break; + case ASPECT_FULL: + default: + desired_ratio = 4.0 / 3.0; + } + + this->ratio_factor = this->display_ratio * desired_ratio; + + /* + * calc ideal output frame size + */ + + corr_factor = this->ratio_factor / image_ratio ; + + if (fabs(corr_factor - 1.0) < 0.005) { + ideal_width = this->delivered_width; + ideal_height = this->delivered_height; + } + else if (corr_factor >= 1.0) { + ideal_width = this->delivered_width * corr_factor + 0.5; + ideal_height = this->delivered_height; + } + else { + ideal_width = this->delivered_width; + ideal_height = this->delivered_height / corr_factor + 0.5; + } + + /* little hack to zoom mpeg1 / other small streams by default*/ + if (this->zoom_mpeg1 && (this->delivered_width<400)) { + ideal_width *= 2; + ideal_height *= 2; + } + + /* yuv2rgb_mmx prefers "width%8 == 0" */ + /* but don't change if it would introduce scaling */ + if( ideal_width != this->delivered_width || + ideal_height != this->delivered_height ) + ideal_width &= ~7; + + /* + * ask gui to adapt to this size + */ + this->request_dest_size (this->user_data, + ideal_width, ideal_height, + &dest_x, &dest_y, &dest_width, &dest_height); + DEBUGF ((stderr, "*** request_dest_size %dx%d -> %dx%d\n", + ideal_width, ideal_height, dest_width, dest_height)); + + opengl_adapt_to_output_area (this, dest_width, dest_height); +} + + +static void opengl_update_frame_format (vo_driver_t *this_gen, + vo_frame_t *frame_gen, + uint32_t width, uint32_t height, + int ratio_code, int format, int flags) { + + opengl_driver_t *this = (opengl_driver_t *) this_gen; + opengl_frame_t *frame = (opengl_frame_t *) frame_gen; + int setup_yuv = 0; + +/*fprintf (stderr, "*** update_frame_format ***\n"); */ + flags &= VO_BOTH_FIELDS; + + if ((width != this->delivered_width) + || (height != this->delivered_height) + || (ratio_code != this->delivered_ratio_code) + || (flags != this->delivered_flags)) { + + this->delivered_width = width; + this->delivered_height = height; + this->delivered_ratio_code = ratio_code; + this->delivered_flags = flags; + setup_yuv = 1; + } + + if ((frame->width != width) || (frame->height != height) + || (frame->format != format)) { + + int image_size; + + DEBUGF ((stderr, "*** updating frame to %d x %d\n", width, height)); + + XLockDisplay (this->display); + + free (frame->texture); + free (frame->chunk[0]); + free (frame->chunk[1]); + free (frame->chunk[2]); + + frame->texture = calloc (1, BYTES_PER_PIXEL * width * height); + assert (frame->texture); + + + if (format == IMGFMT_YV12) { + image_size = width * height; + frame->vo_frame.base[0] = my_malloc_aligned(16,image_size, &frame->chunk[0]); + frame->vo_frame.base[1] = my_malloc_aligned(16,image_size/4, &frame->chunk[1]); + frame->vo_frame.base[2] = my_malloc_aligned(16,image_size/4, &frame->chunk[2]); + } else { + image_size = width * height; + frame->vo_frame.base[0] = my_malloc_aligned(16,image_size*2, &frame->chunk[0]); + } + + frame->format = format; + frame->width = width; + frame->height = height; + + XUnlockDisplay (this->display); + } + frame->ratio_code = ratio_code; + + opengl_frame_field ((vo_frame_t *)frame, flags); + + if (flags == VO_BOTH_FIELDS) { + if (this->yuv_stride != frame->width * BYTES_PER_PIXEL) + setup_yuv = 1; + } else { /* VO_TOP_FIELD, VO_BOTTOM_FIELD */ + if (this->yuv_stride != (frame->width * BYTES_PER_PIXEL * 2)) + setup_yuv = 1; + } + + if (setup_yuv || (this->yuv_width != width)) { + switch (flags) { + case VO_TOP_FIELD: + case VO_BOTTOM_FIELD: + yuv2rgb_setup (this->yuv2rgb, + this->delivered_width, + 16, + this->delivered_width*2, + this->delivered_width, + width, + STRIPE_HEIGHT, + width * BYTES_PER_PIXEL * 2); + this->yuv_stride = frame->width * BYTES_PER_PIXEL * 2; + break; + case VO_BOTH_FIELDS: + yuv2rgb_setup (this->yuv2rgb, + this->delivered_width, + 16, + this->delivered_width, + this->delivered_width/2, + width, + STRIPE_HEIGHT, + width * BYTES_PER_PIXEL); + this->yuv_stride = frame->width * BYTES_PER_PIXEL; + break; + } + this->yuv_width = width; + } +} + +static void opengl_overlay_clut_yuv2rgb(opengl_driver_t *this, vo_overlay_t *overlay) +{ + int i; + clut_t* clut = (clut_t*) overlay->color; + + for (i = 0; i < sizeof(overlay->color)/sizeof(overlay->color[0]); i++) { + *((uint32_t *)&clut[i]) = + this->yuv2rgb->yuv2rgb_single_pixel_fun(this->yuv2rgb, + clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->rgb_clut++; +} + +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->rgb_clut ) + opengl_overlay_clut_yuv2rgb(this,overlay); + + assert (this->delivered_width == frame->width); + assert (this->delivered_height == frame->height); +# if BYTES_PER_PIXEL == 3 + blend_rgb24 ((uint8_t *)frame->texture, overlay, + frame->width, frame->height, + this->delivered_width, this->delivered_height); +# elif BYTES_PER_PIXEL == 4 + blend_rgb32 ((uint8_t *)frame->texture, overlay, + frame->width, frame->height, + this->delivered_width, this->delivered_height); +# else +# error "bad BYTES_PER_PIXEL" +# endif + } +} + +static void opengl_render_image (opengl_driver_t *this, opengl_frame_t *frame, + GLXContext ctx) +{ + int xoffset; + int yoffset; + 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" )); + + if (((ctx == this->context || ! ctx) && + (this->context_state == CONTEXT_BAD || + this->context_state == CONTEXT_SAME_DRAWABLE)) || + (self != this->renderthread)) + { + assert (this->vinfo); + if ((this->context_state == CONTEXT_SAME_DRAWABLE) && (self == this->renderthread)) + { +/*fprintf (stderr, "destroy: %p\n", this->context); */ + /* Unfortunately for _BAD the drawable is already destroyed. + * This cannot be resolved right now and may be a memory leak. */ + if (this->context) + glXDestroyContext (this->display, this->context); + } +/*fprintf (stderr, "create\n"); */ + ctx = glXCreateContext (this->display, this->vinfo, NULL, True); + assert (ctx); + this->context = ctx; + this->context_state = CONTEXT_RELOAD; + this->renderthread = self; + } + + if (this->context_state == CONTEXT_RELOAD && ! ctx) + ctx = this->context; + + if (ctx) + { +/*fprintf (stderr, "set context %p\n", ctx); */ + /* Set and initialize context */ + if (! glXMakeCurrent (this->display, this->drawable, ctx)) + { + fprintf (stderr, "video_out_opengl: no OpenGL support available (glXMakeCurrent)\n"); + exit (1); + } +/*fprintf (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->window_width, this->window_height); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, this->window_width, this->window_height, 0, -1, 1); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + glDisable (GL_BLEND); + glDisable (GL_DEPTH_TEST); + glDisable (GL_CULL_FACE); + glDepthMask (GL_FALSE); + glClearColor (0, 0, 0, 0); + /* for fullscreen and misratioed modes, clear unused areas of old + * video area */ + if (this->window_width != this->output_width + || this->window_height != this->output_height) + glClear (GL_COLOR_BUFFER_BIT); + } + + if (frame) + { + xoffset = (this->window_width - this->output_width) / 2; + yoffset = (this->window_height - this->output_height) / 2; + if (xoffset < 0) + xoffset = 0; + if (yoffset < 0) + yoffset = 0; + +/*fprintf (stderr, "render %p %p: +%d+%d\n", frame, ctx, xoffset, yoffset); */ + glPixelZoom (((float)this->output_width) / frame->width, + - ((float)this->output_height) / frame->height); + glRasterPos2i (xoffset, yoffset); + glDrawPixels (frame->width, frame->height, RGB_TEXTURE_FORMAT, + GL_UNSIGNED_BYTE, frame->texture); +/*fprintf (stderr, "render done\n"); */ + } + glFlush (); + /* Note: no glFinish() - work concurrently to the graphics pipe */ +} + + +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; + +/*fprintf (stderr, "*** display_frame ***\n"); */ + if( this->cur_frame ) + this->cur_frame->vo_frame.displayed (&this->cur_frame->vo_frame); + this->cur_frame = frame; + + if ( (frame->width != this->last_frame_width) || + (frame->height != this->last_frame_height) || + (frame->ratio_code != this->delivered_ratio_code) ) + { + opengl_calc_format (this, frame->width, frame->height, frame->ratio_code); + this->last_frame_width = frame->width; + this->last_frame_height = frame->height; + this->last_frame_ratio_code = frame->ratio_code; + + printf ("video_out_opengl: window size %d x %d, frame size %d x %d\n", + this->window_width, this->window_height, + this->output_width, this->output_height); + } + + XLockDisplay (this->display); + opengl_render_image (this, frame, NULL); + 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 this doesn't work, 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) */ +#if 1 + frame->vo_frame.displayed (&frame->vo_frame); + this->cur_frame = NULL; +#endif +/*fprintf (stderr, "done display_frame\n"); */ +} + + +static int opengl_get_property (vo_driver_t *this_gen, int property) { + + opengl_driver_t *this = (opengl_driver_t *) this_gen; + + if ( property == VO_PROP_ASPECT_RATIO) { + return this->user_ratio ; + } else if ( property == VO_PROP_BRIGHTNESS) { + return yuv2rgb_get_gamma(this->yuv2rgb); + } else { + printf ("video_out_opengl: tried to get unsupported property %d\n", property); + } + + return 0; +} + + +static char *aspect_ratio_name(int a) +{ + switch (a) { + case ASPECT_AUTO: + return "auto"; + case ASPECT_SQUARE: + return "square"; + case ASPECT_FULL: + return "4:3"; + case ASPECT_ANAMORPHIC: + return "16:9"; + case ASPECT_DVB: + return "2:1"; + default: + return "unknown"; + } +} + +static int opengl_set_property (vo_driver_t *this_gen, + int property, int value) { + + opengl_driver_t *this = (opengl_driver_t *) this_gen; + + if ( property == VO_PROP_ASPECT_RATIO) { + if (value>=NUM_ASPECT_RATIOS) + value = ASPECT_AUTO; + this->user_ratio = value; + opengl_calc_format (this, this->delivered_width, this->delivered_height, + this->delivered_ratio_code); + printf("video_out_opengl: aspect ratio changed to %s\n", + aspect_ratio_name(value)); +#if 0 + } else if ( property == VO_PROP_BRIGHTNESS) { + yuv2rgb_set_gamma(this->yuv2rgb,value); + + printf("video_out_opengl: gamma changed to %d\n",value); +#endif + } else { + printf ("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) { + +#if 0 + /* opengl_driver_t *this = (opengl_driver_t *) this_gen; */ + if ( property == VO_PROP_BRIGHTNESS) { + *min = -100; + *max = +100; + } else { +#endif + *min = 0; + *max = 0; +#if 0 + } +#endif +} + + +static int is_fullscreen_size (opengl_driver_t *this, int w, int h) +{ + return w == DisplayWidth(this->display, this->screen) + && h == DisplayHeight(this->display, this->screen); +} + +static void opengl_translate_gui2video(opengl_driver_t *this, + int x, int y, + int *vid_x, int *vid_y) +{ +/*fprintf (stderr, "*** translate_gui2video ***\n"); */ + if (this->output_width > 0 && this->output_height > 0) { + /* + * the driver may center a small output area inside a larger + * gui area. This is the case in fullscreen mode, where we often + * have black borders on the top/bottom/left/right side. + */ + /* Fixme: not true. Simplify? */ + x -= (this->window_width - this->output_width) >> 1; + y -= (this->window_height - this->output_height) >> 1; + + /* + * the driver scales the delivered area into an output area. + * translate output area coordianates into the delivered area + * coordiantes. + */ + x = x * this->delivered_width / this->output_width; + y = y * this->delivered_height / this->output_height; + } + + *vid_x = x; + *vid_y = y; +} + +static int opengl_gui_data_exchange (vo_driver_t *this_gen, + int data_type, void *data) { + + opengl_driver_t *this = (opengl_driver_t *) this_gen; + x11_rectangle_t *area; + static int glxAttrib[] = { + GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None + } ; + +/*fprintf (stderr, "*** gui_data_exchange ***\n"); */ + + switch (data_type) { + case GUI_SELECT_VISUAL: +/*fprintf (stderr, "*** gui_select_visual ***\n"); */ + XLockDisplay (this->display); + this->vinfo = glXChooseVisual (this->display, this->screen, glxAttrib); + XUnlockDisplay (this->display); + if (this->vinfo == NULL) + fprintf (stderr, "video_out_opengl: no OpenGL support available (glXChooseVisual)\n"); + *(XVisualInfo**)data = this->vinfo; +/*fprintf (stderr, "*** visual %p depth %d\n", this->vinfo->visual, this->vinfo->depth); */ + break; + + case GUI_DATA_EX_DEST_POS_SIZE_CHANGED: +/*fprintf (stderr, "*** gui_dest_pos_size_changed ***\n"); */ + + area = (x11_rectangle_t *) data; + + if (this->window_width != area->w || this->window_height != area->h) + { + XLockDisplay (this->display); + DEBUGF ((stderr, "*** video window size changed from %d x %d to %d x %d\n", + this->window_width, this->window_height, + area->w, area->h)); + opengl_adapt_to_output_area (this, area->w, area->h); + XUnlockDisplay (this->display); + } + break; +// ????? What's this? +#if 0 + case GUI_DATA_EX_COMPLETION_EVENT: { + + XShmCompletionEvent *cev = (XShmCompletionEvent *) data; + + if (cev->drawable == this->drawable) { + this->expecting_event = 0; + + if (this->cur_frame) { + this->cur_frame->vo_frame.displayed (&this->cur_frame->vo_frame); + this->cur_frame = NULL; + } + } + + } + break; +#endif + + case GUI_DATA_EX_EXPOSE_EVENT: +/*fprintf (stderr, "*** gui_expose ***\n"); */ + + /* FIXME: called for EVERY SINGLE expose event (no peek so far) */ + if (this->cur_frame) { + + XExposeEvent * xev = (XExposeEvent *) data; + + if (xev->count == 0) { + /* 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 this is currently disabled */ +#if 0 + GLXContext ctx; + XLockDisplay (this->display); +// FIXME: this does not work - at least on linux... +fprintf (stderr, "create/gui\n"); +assert (this->vinfo); +ctx = glXCreateContext (this->display, this->vinfo, NULL, True); +assert (ctx); +opengl_render_image (this, this->cur_frame, ctx); +glXMakeCurrent (this->display, None, NULL); +glXDestroyContext (this->display, ctx); +XUnlockDisplay (this->display); +#endif + } + } + if (this->context_state == CONTEXT_SET || this->context_state == CONTEXT_RELOAD) + this->context_state = CONTEXT_RELOAD; + break; + + case GUI_DATA_EX_DRAWABLE_CHANGED: + DEBUGF ((stderr, "*** gui_drawable_changed: %ld\n", (Drawable) data)); + 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 :( */ +#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->drawable == (Drawable) data) + DEBUGF ((stderr, "*** drawable changed, state now bad\n")); + this->last_frame_width = this->last_frame_height = 0; + this->drawable = (Drawable) data; + XUnlockDisplay (this->display); + break; + + case GUI_DATA_EX_TRANSLATE_GUI_TO_VIDEO: +/*fprintf (stderr, "*** gui_translate_gui_to_video ***\n"); */ + { + int x1, y1, x2, y2; + x11_rectangle_t *rect = data; + + opengl_translate_gui2video(this, rect->x, rect->y, + &x1, &y1); + opengl_translate_gui2video(this, 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; + + default: + return -1; + } + +/*fprintf (stderr, "done gui_data_exchange\n"); */ + return 0; +} + + +static void opengl_exit (vo_driver_t *this_gen) { + opengl_driver_t *this = (opengl_driver_t *) this_gen; + glXMakeCurrent (this->display, None, NULL); + glXDestroyContext (this->display, this->context); + this->context = NULL; +} + + +vo_driver_t *init_video_out_plugin (config_values_t *config, void *visual_gen) { + + opengl_driver_t *this; + x11_visual_t *visual = (x11_visual_t *) visual_gen; + Display *display = NULL; + + visual = (x11_visual_t *) visual_gen; + display = visual->display; + + fprintf (stderr, "EXPERIMENTAL opengl output plugin\n"); + /* + * allocate plugin struct + */ + + this = malloc (sizeof (opengl_driver_t)); + + if (!this) { + printf ("video_out_opengl: malloc failed\n"); + return NULL; + } + + memset (this, 0, sizeof(opengl_driver_t)); + + this->config = config; + this->display = visual->display; + this->screen = visual->screen; + this->display_ratio = visual->display_ratio; + this->request_dest_size = visual->request_dest_size; + this->calc_dest_size = visual->calc_dest_size; + this->user_data = visual->user_data; + this->output_width = 0; + this->output_height = 0; + this->window_width = 0; + this->window_height = 0; + this->zoom_mpeg1 = config->register_bool (config, "video.zoom_mpeg1", 1, + "Zoom small video formats to double size", + NULL, NULL, NULL); + this->drawable = None; /* We need a different one with a dedicated visual anyway */ + this->context_state = CONTEXT_BAD; + + this->prof_yuv2rgb = xine_profiler_allocate_slot ("xshm yuv2rgb convert"); + + 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_blend = opengl_overlay_blend; + 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.exit = opengl_exit; + this->vo_driver.get_info = get_video_out_plugin_info; + + this->yuv2rgb = yuv2rgb_init (YUV_FORMAT, YUV_SWAP_MODE, NULL); + + yuv2rgb_set_gamma(this->yuv2rgb, config->register_range (config, "video.opengl_gamma", 0, + -100, 100, "(software) gamma correction for OpenGL driver", + NULL, NULL, NULL)); + return &this->vo_driver; +} + +static vo_info_t vo_info_shm = { + 3, + "OpenGL", + "xine video output plugin using the MIT X shared memory extension", + VISUAL_TYPE_X11, + 8 +}; + +vo_info_t *get_video_out_plugin_info() { + return &vo_info_shm; +} + diff --git a/src/video_out/video_out_x11.h b/src/video_out/video_out_x11.h index 438eca758..ae8a1a39b 100644 --- a/src/video_out/video_out_x11.h +++ b/src/video_out/video_out_x11.h @@ -17,7 +17,7 @@ * 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_x11.h,v 1.11 2002/01/03 22:41:54 jcdutton Exp $ + * $Id: video_out_x11.h,v 1.12 2002/01/22 20:15:00 mshopf Exp $ * * structs and defines specific to all x11 related output plugins * (any x11 base xine ui should include this) @@ -110,6 +110,11 @@ typedef struct { /* int *data */ #define GUI_DATA_EX_FULLSCREEN 7 +/* *data contains chosen visual, select a new one or change it to NULL + * to indicate the visual to use or that no visual will work */ +/* XVisualInfo **data */ +#define GUI_SELECT_VISUAL 8 + #ifdef __cplusplus } #endif |