#include #include #include #include "config.h" #include "mp_msg.h" #include "video_out.h" #include "video/vfcap.h" #include "video/img_format.h" #include #include #include #include #include #define DEFAULT_INTERNAL_FORMAT GL_RGB static void unimpl () { mp_msg (MSGT_VO, MSGL_ERR, "[gl] unimplemented function!\n"); } struct vo_priv_s { Window window; Display *display; int img_width; int img_height; int d_width; int d_height; char numPlanes; char numTextures; // how many textures have been allocated by glGenTextures GLuint planeTexture[4]; GLint planeInternalFormat[4]; GLsizei planeTextureWidth[4]; GLsizei planeTextureHeight[4]; int planeWidth[4]; int planeHeight[4]; char planeBytesPerPixel[4]; GLenum planeFormat[4]; GLenum planeType[4]; char supportYUV; char useGL12; char listCreated; GLuint glDisplayList; XVisualInfo vinfo; GLXContext context; }; static void resize (struct vo_instance_s *vo, int src[4]) { mp_msg (MSGT_VO, MSGL_V, "[gl] resize "); mp_msg (MSGT_VO, MSGL_V, "(%i, %i, %i, %i),", vo->x, vo->y, vo->w, vo->h); if (src) mp_msg (MSGT_VO, MSGL_V, "(%i, %i, %i, %i)\n", src[0], src[1], src[2], src[3]); else mp_msg (MSGT_VO, MSGL_V, "NULL\n"); glViewport (vo->x, vo->y, vo->w, vo->h); if (src) { glMatrixMode (GL_PROJECTION); glLoadIdentity (); // FIXME: how do we find out when we should flip ?? glTranslatef (-src[0], -src[1], 0); glOrtho (0, src[2], src[3], 0, -1, 1); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); } } static int control (struct vo_instance_s *vo, int request, void *data, ...) { switch (request) { case VOCTRL_SET_WINDOW: vo->priv->window = *((Window *) data); if (!vo->priv->context) { vo->priv->context = glXCreateContext (vo->priv->display, &vo->priv->vinfo, NULL, True); } else glFinish(); glXMakeCurrent (vo->priv->display, vo->priv->window, vo->priv->context); return VO_TRUE; case VOCTRL_MOVE_DEST: vo->x = ((int *)data)[0]; vo->y = ((int *)data)[1]; resize(vo, NULL); return VO_TRUE; case VOCTRL_RESIZE_DEST: vo->w = ((int *)data)[0]; vo->h = ((int *)data)[1]; resize(vo, NULL); return VO_TRUE; case VOCTRL_RESIZE_SRC: resize(vo, data); return VO_TRUE; } return VO_NOTIMPL; } static unsigned int query_format (struct vo_instance_s *vo, unsigned int format, int width, int height) { const int supported = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_FLIP; switch (format) { case IMGFMT_BGR24: case IMGFMT_Y800: return supported; } return 0; } static vo_buffer_t *get_buffer (struct vo_instance_s *vo) { int i; vo_buffer_t *buffer = calloc (sizeof (vo_buffer_t), 1); buffer->status = 0; for (i = 0; i < vo->priv->numPlanes; i++) { buffer->planes[i] = calloc (vo->priv->planeWidth[i] * vo->priv->planeHeight[i] * vo->priv->planeBytesPerPixel[i], 1); buffer->stride[i] = vo->priv->planeWidth[i] * vo->priv->planeBytesPerPixel[i]; } return buffer; } static void release_buffer (struct vo_instance_s *vo, vo_buffer_t * buffer) { int i; for (i = 0; i < vo->priv->numPlanes; i++) free (buffer->planes[i]); free (buffer); } static void uninit (struct vo_instance_s *vo) { glDeleteTextures (vo->priv->numTextures, vo->priv->planeTexture); vo->priv->numTextures = 0; glDeleteLists (vo->priv->glDisplayList, 1); vo->priv->listCreated = 0; if (vo->priv->context) glXDestroyContext (vo->priv->display, vo->priv->context); free (vo->priv); free (vo); } typedef struct { int pixelBytes; GLint internalFormat; GLenum format; GLenum type; int width_div; int height_div; } PlaneInfo; typedef struct { unsigned int format; char numPlanes; PlaneInfo plane[4]; } FormatInfo; FormatInfo formatInfos[] = { { IMGFMT_RGB8, 1, { {1, 0, GL_RGB, GL_UNSIGNED_BYTE_2_3_3_REV, 1, 1}, } }, { IMGFMT_RGB15, 1, { {2, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 1, 1}, } }, { IMGFMT_RGB16, 1, { {2, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, 1, 1}, } }, { IMGFMT_RGB24, 1, { {3, 0, GL_RGB, GL_UNSIGNED_BYTE, 1, 1}, } }, { IMGFMT_RGB32, 1, { {4, 0, GL_RGBA, GL_UNSIGNED_BYTE, 1, 1}, } }, { IMGFMT_BGR8, 1, { {1, 0, GL_RGB, GL_UNSIGNED_BYTE_3_3_2, 1, 1}, } }, { IMGFMT_BGR15, 1, { {2, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 1, 1}, } }, { IMGFMT_BGR16, 1, { {2, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 1, 1}, } }, { IMGFMT_BGR24, 1, { {3, 0, GL_BGR, GL_UNSIGNED_BYTE, 1, 1}, } }, { IMGFMT_BGR32, 1, { {4, 0, GL_BGRA, GL_UNSIGNED_BYTE, 1, 1}, } }, { IMGFMT_Y800, 1, { {1, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 1}, } }, { IMGFMT_YV12, 3, { {1, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 1}, {1, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 2, 2}, {1, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 2, 2}, } }, { IMGFMT_444P, 3, { {1, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 1}, {1, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 1}, {1, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 1}, } }, { 0, 0, { {0, 0, 0, 0, 0, 0}, } } }; static void analyzeFormat (struct vo_instance_s *vo, unsigned int format) { int i = 0; FormatInfo *fi; while (formatInfos[i].format != 0 && formatInfos[i].format != format) i++; if (formatInfos[i].format != format) { mp_msg (MSGT_VO, MSGL_ERR, "[gl] Unknown image format. This shouldn't happen!\n"); }; fi = &formatInfos[i]; vo->priv->numPlanes = fi->numPlanes; for (i = 0; i < fi->numPlanes; i++) { vo->priv->planeWidth[i] = vo->priv->img_width / fi->plane[i].width_div; vo->priv->planeHeight[i] = vo->priv->img_height / fi->plane[i].height_div; vo->priv->planeBytesPerPixel[i] = fi->plane[i].pixelBytes; if (fi->plane[i].internalFormat != 0) vo->priv->planeInternalFormat[i] = fi->plane[i].internalFormat; else vo->priv->planeInternalFormat[i] = DEFAULT_INTERNAL_FORMAT; vo->priv->planeFormat[i] = fi->plane[i].format; vo->priv->planeType[i] = fi->plane[i].type; } } static void createTextures (struct vo_instance_s *vo) { int i; char *texInitData = NULL; char initVal = 0; int tmp, size; if (vo->priv->numTextures > 0) glDeleteTextures (vo->priv->numTextures, vo->priv->planeTexture); glGenTextures (vo->priv->numPlanes, vo->priv->planeTexture); vo->priv->numTextures = vo->priv->numPlanes; for (i = 0; i < vo->priv->numPlanes; i++) { tmp = 1; while (tmp < vo->priv->planeWidth[i]) tmp *= 2; vo->priv->planeTextureWidth[i] = tmp; tmp = 1; while (tmp < vo->priv->planeHeight[i]) tmp *= 2; vo->priv->planeTextureHeight[i] = tmp; mp_msg (MSGT_VO, MSGL_V, "[gl] Allocating texture of size %dx%d ...\n", vo->priv->planeTextureWidth[i], vo->priv->planeTextureHeight[i]); size = vo->priv->planeTextureWidth[i] * vo->priv->planeTextureHeight[i]; texInitData = malloc (size); if (i == 0) initVal = 0; else initVal = 128; // no green borders in YUV memset (texInitData, initVal, size); glBindTexture (GL_TEXTURE_2D, vo->priv->planeTexture[i]); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D (GL_TEXTURE_2D, 0, vo->priv->planeInternalFormat[i], vo->priv->planeTextureWidth[i], vo->priv->planeTextureHeight[i], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texInitData); mp_msg (MSGT_VO, MSGL_V, "[gl] glTexImage2D finished with %d\n", glGetError ()); free (texInitData); } } static void createDisplayList (struct vo_instance_s *vo) { int i; GLfloat xcov[4], ycov[4]; if (vo->priv->listCreated) glDeleteLists (vo->priv->glDisplayList, 1); vo->priv->glDisplayList = glGenLists (1); for (i = 0; i < vo->priv->numPlanes; i++) { xcov[i] = (GLfloat) vo->priv->planeWidth[i] / (GLfloat) vo->priv->planeTextureWidth[i]; ycov[i] = (GLfloat) vo->priv->planeHeight[i] / (GLfloat) vo->priv->planeTextureHeight[i]; } glNewList (vo->priv->glDisplayList, GL_COMPILE); if (vo->priv->numPlanes == 1) { i = 0; glBindTexture (GL_TEXTURE_2D, vo->priv->planeTexture[i]); glBegin (GL_QUADS); glTexCoord2f (0, 0); glVertex2i (0, 0); glTexCoord2f (xcov[i], 0); glVertex2i (vo->priv->img_width, 0); glTexCoord2f (xcov[i], ycov[i]); glVertex2i (vo->priv->img_width, vo->priv->img_height); glTexCoord2f (0, ycov[i]); glVertex2i (0, vo->priv->img_height); glEnd (); } else unimpl (); glEndList (); vo->priv->listCreated = 1; } static int config (struct vo_instance_s *vo, int width, int height, int d_width, int d_height, unsigned int format) { int dest_coords[] = {0, 0, width, height}; vo->priv->img_width = width; vo->priv->img_height = height; vo->priv->d_width = d_width; vo->priv->d_height = d_height; vo->buffer_w = width; vo->buffer_h = height; vo->w = d_width; vo->h = d_height; analyzeFormat (vo, format); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); glClearColor (0, 0, 0, 0); createTextures (vo); createDisplayList (vo); resize (vo, dest_coords); return VO_TRUE; } static void updateTexture (struct vo_instance_s *vo, int planeNr, int x, int y, int w, int h, unsigned char *data) { mp_msg (MSGT_VO, MSGL_DBG1, "[gl] updateTexture (%i, %i, %i, %i, %i)\n", planeNr, x, y, w, h); glBindTexture (GL_TEXTURE_2D, vo->priv->planeTexture[planeNr]); glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, w, h, vo->priv->planeFormat[planeNr], vo->priv->planeType[planeNr], data); } static void draw_slice (struct vo_instance_s *vo, vo_buffer_t * buffer, unsigned char *src[], int stride[], int w, int h, int x, int y) { mp_msg (MSGT_VO, MSGL_DBG1, "[gl] draw_slice\n",stride[0]); } static int frame_done (struct vo_instance_s *vo, vo_buffer_t * buffer) { int i; mp_msg (MSGT_VO, MSGL_DBG1, "[gl] frame_done\n"); for (i = 0; i < vo->priv->numPlanes; i++) updateTexture (vo, i, 0, 0, vo->buffer_w, vo->buffer_h, buffer->planes[i]); glCallList (vo->priv->glDisplayList); glFlush (); return 0; } static void show_frame (struct vo_instance_s *vo, vo_buffer_t * buffer, unsigned int time_pos) { mp_msg (MSGT_VO, MSGL_DBG1, "[gl] show_frame\n"); glXSwapBuffers (vo->priv->display, vo->priv->window); glClear (GL_COLOR_BUFFER_BIT); } static vo_instance_t *preinit (char *args, void *x11_display) { vo_instance_t *vo; if (!x11_display) return NULL; // X11 not available vo = calloc (sizeof (vo_instance_t), 1); vo->priv = calloc (sizeof (struct vo_priv_s), 1); memset (vo->priv, 0, sizeof (struct vo_priv_s)); vo->priv->numTextures = 0; vo->priv->useGL12 = 1; vo->priv->supportYUV = 0; vo->priv->display = x11_display; XMatchVisualInfo(vo->priv->display, DefaultScreen(vo->priv->display), 24, TrueColor, &vo->priv->vinfo); #if 0 if (arg) { if (strcmp (arg, "gl11") == 0) { useGL12 = 0; } else if (strcmp (arg, "glyuv") == 0) { supportYUV = 1; } else { printf ("[gl] Unknown subdevice: %s\n", arg); return ENOSYS; } } #endif vo->buffer_flags = 0; vo->num_buffers = 0; // functions: vo->query_format = query_format; vo->control = control; vo->config = config; vo->draw_slice = draw_slice; vo->frame_done = frame_done; vo->show_frame = show_frame; vo->uninit = uninit; vo->get_buffer = get_buffer; vo->release_buffer = release_buffer; return vo; } vo_info_t vo_driver_gl = { MODULE_TYPE_VO, MODULE_VERSION, preinit, "gl", "OpenGL driver", "Reimar Döffinger", "Reimar Döffinger", NULL, VO_MODULE_CAPS_X11 | VO_MODULE_CAPS_WINDOW | VO_MODULE_CAPS_GUI, NULL, NULL, 0 };