view libvo/vo_gl.c @ 12225:eb79a6192b2b

New filter by Ville Saari (114263 at foo dot bar dot org) for removing duplicate frames from telecined video that was incorrectly deinterlaced. Minor bugfixes added by me.
author rfelker
date Sat, 17 Apr 2004 17:08:31 +0000
parents 2e8b305586a0
children d97398ed403e
line wrap: on
line source

#define TEXTUREFORMAT_32BPP

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <errno.h>

#include "mp_msg.h"
#include "config.h"
#include "video_out.h"
#include "video_out_internal.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
//#include <X11/keysym.h>
#include <GL/glx.h>
#include <errno.h>

#include <GL/gl.h>

#include "x11_common.h"
#include "aspect.h"

static vo_info_t info = 
{
	"X11 (OpenGL)",
	"gl",
	"Arpad Gereoffy <arpi@esp-team.scene.hu>",
	""
};

LIBVO_EXTERN(gl)

/* local data */
static unsigned char *ImageData=NULL;

static GLXContext wsGLXContext;
static int                  wsGLXAttrib[] = { GLX_RGBA,
                                       GLX_RED_SIZE,1,
                                       GLX_GREEN_SIZE,1,
                                       GLX_BLUE_SIZE,1,
                                       GLX_DOUBLEBUFFER,
                                       None };


static uint32_t image_width;
static uint32_t image_height;
static uint32_t image_bytes;
static int many_fmts;
static GLenum gl_format;
static GLenum gl_type;

static int int_pause;

static uint32_t texture_width;
static uint32_t texture_height;

static int slice_height=1;

static void resize(int x,int y){
  mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n",x,y);
  glViewport( 0, 0, x, y );

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, image_width, image_height, 0, -1,1);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

static int find_gl_format (uint32_t format)
{
  switch (format) {
    case IMGFMT_RGB24:
      gl_format = GL_RGB;
      gl_type = GL_UNSIGNED_BYTE;
      break;
    case IMGFMT_RGB32:
      gl_format = GL_RGBA;
      gl_type = GL_UNSIGNED_BYTE;
      break;
    case IMGFMT_Y800:
    case IMGFMT_Y8:
      gl_format = GL_LUMINANCE;
      gl_type = GL_UNSIGNED_BYTE;
      break;
#ifdef GL_VERSION_1_2
    case IMGFMT_RGB8:
      gl_format = GL_RGB;
      gl_type = GL_UNSIGNED_BYTE_3_3_2;
      break;
    case IMGFMT_RGB15:
      gl_format = GL_RGBA;
      gl_type = GL_UNSIGNED_SHORT_5_5_5_1;
      break;
    case IMGFMT_RGB16:
      gl_format = GL_RGB;
      gl_type = GL_UNSIGNED_SHORT_5_6_5;
      break;
    case IMGFMT_BGR8:
      // special case as red and blue have a differen number of bits.
      // GL_BGR and GL_UNSIGNED_BYTE_3_3_2 isn't supported at least
      // by nVidia drivers, and in addition would give more bits to
      // blue than to red, which isn't wanted
      gl_format = GL_RGB;
      gl_type = GL_UNSIGNED_BYTE_2_3_3_REV;
      break;
    case IMGFMT_BGR15:
      gl_format = GL_BGRA;
      gl_type = GL_UNSIGNED_SHORT_5_5_5_1;
      break;
    case IMGFMT_BGR16:
      gl_format = GL_RGB;
      gl_type = GL_UNSIGNED_SHORT_5_6_5_REV;
      break;
    case IMGFMT_BGR24:
      gl_format = GL_BGR;
      gl_type = GL_UNSIGNED_BYTE;
      break;
    case IMGFMT_BGR32:
      gl_format = GL_BGRA;
      gl_type = GL_UNSIGNED_BYTE;
      break;
#endif
    default:
      gl_format = GL_RGBA;
      gl_type = GL_UNSIGNED_BYTE;
      return 0;
  }
  return 1;
}

/* connect to server, create and map window,
 * allocate colors and (shared) memory
 */
static uint32_t 
config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format)
{
//	int screen;
	unsigned int fg, bg;
	XSizeHints hint;
	XVisualInfo *vinfo;
	XEvent xev;

//	XGCValues xgcv;

	image_height = height;
	image_width = width;
	find_gl_format (format);
    vo_dwidth = d_width;
    vo_dheight = d_height;

	int_pause = 0;

	aspect_save_orig(width,height);
	aspect_save_prescale(d_width,d_height);
	aspect_save_screenres(vo_screenwidth,vo_screenheight);

	aspect(&d_width,&d_height,A_NOZOOM);
#ifdef X11_FULLSCREEN
//        if( flags&0x01 ){ // (-fs)
//          aspect(&d_width,&d_height,A_ZOOM);
//        }
#endif
	hint.x = 0;
	hint.y = 0;
	hint.width = d_width;
	hint.height = d_height;
	hint.flags = PPosition | PSize;

	/* Get some colors */

	bg = WhitePixel(mDisplay, mScreen);
	fg = BlackPixel(mDisplay, mScreen);

	/* Make the window */

  vinfo=glXChooseVisual( mDisplay,mScreen,wsGLXAttrib );
  if (vinfo == NULL)
  {
    mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n");
    return -1;
  }



	if ( vo_window == None )
	 {
          vo_window = vo_x11_create_smooth_window(mDisplay, mRootWin, vinfo->visual, hint.x, hint.y, hint.width, hint.height,
			                          vinfo->depth, XCreateColormap(mDisplay, mRootWin, vinfo->visual, AllocNone));

      vo_x11_classhint( mDisplay,vo_window,"gl" );
      vo_hidecursor(mDisplay,vo_window);

//      if ( flags&0x01 ) vo_x11_decoration( mDisplay,vo_window,0 );
	  XSelectInput(mDisplay, vo_window, StructureNotifyMask);
	  /* Tell other applications about this window */
	  XSetStandardProperties(mDisplay, vo_window, title, title, None, NULL, 0, &hint);
	  /* Map window. */
	  XMapWindow(mDisplay, vo_window);
	  if ( flags&1 ) vo_x11_fullscreen();
#ifdef HAVE_XINERAMA
	  vo_x11_xinerama_move(mDisplay,vo_window);
#endif

	  /* Wait for map. */
	  do 
	  {
		XNextEvent(mDisplay, &xev);
	  }
	  while (xev.type != MapNotify || xev.xmap.event != vo_window);

	  XSelectInput(mDisplay, vo_window, NoEventMask);
	 }

	if ( vo_config_count ) glXDestroyContext( mDisplay,wsGLXContext );
    wsGLXContext=glXCreateContext( mDisplay,vinfo,NULL,True );
    glXMakeCurrent( mDisplay,vo_window,wsGLXContext );

	XFlush(mDisplay);
	XSync(mDisplay, False);

	vo_x11_selectinput_witherr(mDisplay, vo_window, StructureNotifyMask | KeyPressMask | PointerMotionMask
		     | ButtonPressMask | ButtonReleaseMask | ExposureMask
        );

  texture_width=32;
  while(texture_width<image_width || texture_width<image_height) texture_width*=2;
  texture_height=texture_width;

  image_bytes=(IMGFMT_RGB_DEPTH(format)+7)/8;

  if ( ImageData ) free( ImageData );
  ImageData=malloc(texture_width*texture_height*image_bytes);
  memset(ImageData,128,texture_width*texture_height*image_bytes);

  glDisable(GL_BLEND); 
  glDisable(GL_DEPTH_TEST);
  glDepthMask(GL_FALSE);
  glDisable(GL_CULL_FACE);

  glEnable(GL_TEXTURE_2D);

  mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n",texture_width,texture_height);

#if 1
//  glBindTexture(GL_TEXTURE_2D, texture_id);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  /* Old OpenGL 1.0 used the third parameter (known as internalFormat) as an
     integer, which indicated the bytes per pixel (bpp). Later in OpenGL 1.1
     they switched to constants, like GL_RGB8. GL_RGB8 means 8 bits for each
     channel (R,G,B), so it's equal to RGB24. It should be safe to pass the
     image_bytes to internalFormat with newer OpenGL versions.
     Anyway, I'm leaving this so as it was, it doesn't hurt, as OpenGL 1.1 is
     about 10 years old too. -- alex
  */
#ifdef TEXTUREFORMAT_32BPP
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, texture_width, texture_height, 0,
#else
  glTexImage2D(GL_TEXTURE_2D, 0, image_bytes, texture_width, texture_height, 0,
#endif
       (image_bytes==4)?GL_RGBA:GL_BGR, GL_UNSIGNED_BYTE, ImageData);
#endif

  resize(d_width,d_height);

  glClearColor( 1.0f,0.0f,1.0f,0.0f );
  glClear( GL_COLOR_BUFFER_BIT );

//  printf("OpenGL setup OK!\n");

      saver_off(mDisplay);  // turning off screen saver

      if (vo_ontop) vo_x11_setlayer(mDisplay, vo_window, vo_ontop);

	return 0;
}

static void check_events(void)
{
    int e=vo_x11_check_events(mDisplay);
    if(e&VO_EVENT_RESIZE) resize(vo_dwidth,vo_dheight);
    if(e&VO_EVENT_EXPOSE && int_pause) flip_page();
}

static void draw_osd(void)
{
}

static void
flip_page(void)
{

//  glEnable(GL_TEXTURE_2D);
//  glBindTexture(GL_TEXTURE_2D, texture_id);

  glColor3f(1,1,1);
  glBegin(GL_QUADS);
    glTexCoord2f(0,0);glVertex2i(0,0);
    glTexCoord2f(0,1);glVertex2i(0,texture_height);
    glTexCoord2f(1,1);glVertex2i(texture_width,texture_height);
    glTexCoord2f(1,0);glVertex2i(texture_width,0);
  glEnd();

//  glFlush();
  glFinish();
  glXSwapBuffers( mDisplay,vo_window );
 
}

//static inline uint32_t draw_slice_x11(uint8_t *src[], uint32_t slice_num)
static uint32_t draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y)
{
	return 0;
}


static uint32_t
draw_frame(uint8_t *src[])
{
int i;
uint8_t *ImageData=src[0];

  if (slice_height == 0)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height,
		    gl_format, gl_type, ImageData);
  else
    for(i=0;i<image_height;i+=slice_height){
      glTexSubImage2D( GL_TEXTURE_2D,  // target
		       0,              // level
		       0,              // x offset
//		       image_height-1-i,  // y offset
		       i,  // y offset
		       image_width,    // width
		       (i+slice_height<=image_height)?slice_height:image_height-i,              // height
		       gl_format,
		       gl_type,
		       ImageData+i*image_bytes*image_width );        // *pixels
    }

	return 0; 
}

static uint32_t
query_format(uint32_t format)
{
    if ((format == IMGFMT_RGB24) || (format == IMGFMT_RGB32))
        return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
    if (many_fmts && find_gl_format(format))
        return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
    return 0;
}


static void
uninit(void)
{
  if ( !vo_config_count ) return;
  saver_on(mDisplay); // screen saver back on
  vo_x11_uninit();
}

static uint32_t preinit(const char *arg)
{
    int parse_err = 0;
    many_fmts = 0;
    slice_height = 4;
    if(arg) 
    {
        char *parse_pos = &arg[0];
        while (parse_pos[0] && !parse_err) {
            if (strncmp (parse_pos, "manyfmts", 8) == 0) {
                parse_pos = &parse_pos[8];
                many_fmts = 1;
            } else if (strncmp (parse_pos, "slice-height=", 13) == 0) {
                parse_pos = &parse_pos[13];
                slice_height = strtol(parse_pos, &parse_pos, 0);
                if (slice_height < 0) parse_err = 1;
            }
            if (parse_pos[0] == ':') parse_pos = &parse_pos[1];
            else if (parse_pos[0]) parse_err = 1;
        }
    }
    if (parse_err) {
      mp_msg(MSGT_VO, MSGL_ERR,
              "\n-vo gl command line help:\n"
              "Example: mplayer -vo gl:slice-height=4\n"
              "\nOptions:\n"
              "  manyfmts\n"
              "    Enable extended color formats for OpenGL 1.2 and later\n"
              "  slice-height=<0-...>\n"
              "    Slice size for texture transfer, 0 for whole image\n"
              "\n" );
      return -1;
    }
    if (many_fmts)
      mp_msg (MSGT_VO, MSGL_WARN, "[gl] using extended formats.\n"
               "Make sure you have OpenGL >= 1.2 and used corresponding "
               "headers for compiling!\n");
    mp_msg (MSGT_VO, MSGL_INFO, "[gl] Using %d as slice height "
             "(0 means image height).\n", slice_height);
    if( !vo_init() ) return -1; // Can't open X11

    return 0;
}

static uint32_t control(uint32_t request, void *data, ...)
{
  switch (request) {
  case VOCTRL_PAUSE: return (int_pause=1);
  case VOCTRL_RESUME: return (int_pause=0);
  case VOCTRL_QUERY_FORMAT:
    return query_format(*((uint32_t*)data));
  case VOCTRL_ONTOP:
    vo_x11_ontop();
    return VO_TRUE;
  case VOCTRL_FULLSCREEN:
    vo_x11_fullscreen();
    return VO_TRUE;
  }
  return VO_NOTIMPL;
}