diff --git a/libvo/vo_gles.c b/libvo/vo_gles.c new file mode 100644 index 0000000..cecc01a --- /dev/null +++ b/libvo/vo_gles.c @@ -0,0 +1,480 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * You can alternatively redistribute this file and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +// TODO +// - handle aspect +// - check for extensions +// - fallback if no support for NPOT textures +// - GLES 2.0 (YUV conversion using shader) +// MPlayer quirks +// - OSD resize delay +// - fixed-vo no resize event on nofs->fs->loop->nofs +// - 640x480 default on fs->nofs +// - drawing before check_events(resize) + +#include +#include + +#include "mp_core.h" +#include "video_out.h" +#include "video_out_internal.h" +#include "x11_common.h" +#include "sub/sub.h" + +#include +#include +#define GL_GLEXT_PROTOTYPES 1 +#include + +#ifndef GL_EXT_bgra // not defined in Khronos GLES/glext.h +#define GL_EXT_bgra 1 +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif + +//uncomment to disable extensions +#undef GL_OES_draw_texture +//#undef GL_EXT_bgra +//#undef GL_EXT_texture_format_BGRA8888 + +#define IS_ALIGNED(a, b) ((((uintptr_t)(const void *)(a)) & ((b) - 1)) == 0) +#define MAX_CONFIGS 10 + +//#define FAST_OSD 1 + +static const vo_info_t info = { + "OpenGL ES (X11)", + "gles", + "kirin_e at users.sourceforge.net", + "" +}; + +const LIBVO_EXTERN(gles) + +static int unshown = 0; +static unsigned int src_width = 0, src_height = 0; +static unsigned int win_width = 0, win_height = 0; +static unsigned int src_pixel_bytes = 0; +static GLenum tex_format = GL_RGBA; +static GLuint texture[2] = { 0, 0 }; + +#ifndef GL_OES_draw_texture +static const GLshort texcoord_buffer[16] = { 0, 1, 0, 0, 1, 1, 1, 0, + 0, 1, 0, 0, 1, 1, 1, 0 }; +static GLshort vertex_buffer[16] = { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; +#endif + +static void draw(uint8_t *src[], int stride[], int w, int h, int x, int y) +{ + int src_align, align, w_bytes_aligned, i; + + for (src_align = 8; !IS_ALIGNED(src[0], src_align); src_align >>= 1); + for (align = src_align; !IS_ALIGNED(stride[0], align); align >>= 1); + + glPixelStorei(GL_UNPACK_ALIGNMENT, align); + glBindTexture(GL_TEXTURE_2D, texture[0]); + + w_bytes_aligned = (w * src_pixel_bytes + align - 1) & ~(align - 1); + if (w_bytes_aligned == stride[0]) { + if (x != 0 || y != 0 || w != src_width || h != src_height) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, tex_format, GL_UNSIGNED_BYTE, src[0]); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, tex_format, w, h, 0, tex_format, GL_UNSIGNED_BYTE, src[0]); + } + } else { + for (i = 0; i < h; i++) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y + i, w, 1, tex_format, GL_UNSIGNED_BYTE, src[0] + stride[0] * i); + } + } +#ifndef GL_OES_draw_texture + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +#else + glDrawTexiOES(0, 0, 0, win_width, win_height); +#endif + unshown = 1; +} + +static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src, + unsigned char *srca, int stride) +{ + int src_align, align, w_bytes_aligned, i; +#ifdef FAST_OSD + GLenum osd_tex_format = GL_LUMINANCE; + int osd_pixel_bytes = 1; +#else + GLenum osd_tex_format = GL_LUMINANCE_ALPHA; + int osd_pixel_bytes = 2, j; + char src_la[h * stride * 2]; + + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + src_la[i * stride * 2 + j * 2] = src[i * stride + j]; + src_la[i * stride * 2 + j * 2 + 1] = srca[i * stride + j] ? srca[i * stride + j] : 255; + } + } + src = src_la; + stride = stride * 2; +#endif + + //printf("\n at draw_alpha: %ix%i xoffset %i yoffset %i stride %i (%p %p)\n", w, h, x0, y0, stride, src, srca); + for (src_align = 8; !IS_ALIGNED(src, src_align); src_align >>= 1); + for (align = src_align; !IS_ALIGNED(stride, align); align >>= 1); + + glPixelStorei(GL_UNPACK_ALIGNMENT, align); + glBindTexture(GL_TEXTURE_2D, texture[1]); +#ifdef GL_OES_draw_texture + { + GLint crop_rect[4] = { 0, h, w, -h }; + + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop_rect); + } +#endif + + w_bytes_aligned = (w * osd_pixel_bytes + align - 1) & ~(align - 1); + //printf("osd data alignment: %i, w_bytes_aligned: %i\n", align, w_bytes_aligned); + if (w_bytes_aligned == stride) { + glTexImage2D(GL_TEXTURE_2D, 0, osd_tex_format, w, h, 0, osd_tex_format, GL_UNSIGNED_BYTE, src); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, osd_tex_format, w, h, 0, osd_tex_format, GL_UNSIGNED_BYTE, NULL); + for (i = 0; i < h; i ++) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, w, 1, osd_tex_format, GL_UNSIGNED_BYTE, src + stride * i); + } + } + glEnable(GL_BLEND); +#ifndef GL_OES_draw_texture + { + int y0i = win_height - y0 - h; + + vertex_buffer[8] = x0; + vertex_buffer[9] = y0i; + vertex_buffer[10] = x0; + vertex_buffer[11] = y0i + h; + vertex_buffer[12] = x0 + w; + vertex_buffer[13] = y0i; + vertex_buffer[14] = x0 + w; + vertex_buffer[15] = y0i + h; + glDrawArrays(GL_TRIANGLE_STRIP, 4, 4); + } +#else + glDrawTexiOES(x0, win_height - y0 - h, 0, w, h); +#endif + glDisable(GL_BLEND); +} + +static void reshape(int width, int height) +{ +#ifndef GL_OES_draw_texture + vertex_buffer[3] = height; + vertex_buffer[4] = width; + vertex_buffer[6] = width; + vertex_buffer[7] = height; +#endif + glViewport(0, 0, (GLint) width, (GLint) height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthox(0 << 16, width << 16, 0 << 16, height << 16, -1 << 16, 1 << 16); + glMatrixMode(GL_MODELVIEW); +} + +static void gl_init(void) +{ + mp_msg(MSGT_VO, MSGL_V, "GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); + mp_msg(MSGT_VO, MSGL_V, "GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); + mp_msg(MSGT_VO, MSGL_V, "GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR)); + mp_msg(MSGT_VO, MSGL_V, "GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); + + reshape(win_width, win_height); + glGenTextures(2, texture); + + glBindTexture(GL_TEXTURE_2D, texture[0]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#ifdef GL_OES_draw_texture + { + GLint crop_rect[4] = { 0, src_height, src_width, -src_height }; + + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop_rect); + } +#endif + glTexImage2D(GL_TEXTURE_2D, 0, tex_format, src_width, src_height, 0, tex_format, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_2D, texture[1]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + +#ifndef GL_OES_draw_texture + glTexCoordPointer(2, GL_SHORT, 0, texcoord_buffer); + glVertexPointer(2, GL_SHORT, 0, vertex_buffer); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); +#endif + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_ONE, GL_SRC_ALPHA); +} + +// ----------------------------------------------- + +static Display *dpy = NULL; +static Window window = 0; +static Colormap colormap = 0; + +static EGLDisplay egldpy; +static EGLSurface eglwindow; +static EGLContext eglctx; + +static void createEGLWindow(int width, int height, char *name, uint32_t flags) +{ + const EGLint attrib_list[] = { EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_NONE }; + EGLConfig configs[MAX_CONFIGS]; + EGLint num_configs; + EGLint visual_id = 0, attrib; + + XVisualInfo *vinfo, vinfo_template; + int i, cfg, num_visuals; + + dpy = mDisplay; // set by vo_init() + + egldpy = eglGetDisplay(dpy); + if (egldpy == EGL_NO_DISPLAY) exit_player(EXIT_ERROR); + if (eglInitialize(egldpy, NULL, NULL) == EGL_FALSE) exit_player(EXIT_ERROR); + if (eglChooseConfig(egldpy, attrib_list, configs, MAX_CONFIGS, &num_configs) == EGL_FALSE) exit_player(EXIT_ERROR); + if (num_configs == 0) { + mp_msg(MSGT_VO, MSGL_ERR, "gles: no matching EGL frame buffer configurations found!\n"); + exit_player(EXIT_ERROR); + } + for (cfg = 0; cfg < num_configs && visual_id == 0; cfg++) { + mp_msg(MSGT_VO, MSGL_V, "gles: configs[%i]: ", cfg); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_NATIVE_VISUAL_ID, &visual_id); + mp_msg(MSGT_VO, MSGL_V, "VisualID: 0x%02x ", visual_id); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_BUFFER_SIZE, &attrib); + mp_msg(MSGT_VO, MSGL_V, "bpp: %i ", attrib); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_RED_SIZE, &attrib); + mp_msg(MSGT_VO, MSGL_V, "R: %i ", attrib); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_GREEN_SIZE, &attrib); + mp_msg(MSGT_VO, MSGL_V, "G: %i ", attrib); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_BLUE_SIZE, &attrib); + mp_msg(MSGT_VO, MSGL_V, "B: %i ", attrib); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_ALPHA_SIZE, &attrib); + mp_msg(MSGT_VO, MSGL_V, "A: %i ", attrib); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_DEPTH_SIZE, &attrib); + mp_msg(MSGT_VO, MSGL_V, "Z: %i ", attrib); + eglGetConfigAttrib(egldpy, configs[cfg], EGL_STENCIL_SIZE, &attrib); + mp_msg(MSGT_VO, MSGL_V, "S: %i\n", attrib); + } + if (visual_id == 0) { + mp_msg(MSGT_VO, MSGL_INFO, "gles: no useable VisualID found in configs, matching on depth instead!\n"); + cfg = 0; + eglGetConfigAttrib(egldpy, configs[cfg], EGL_BUFFER_SIZE, &vinfo_template.depth); + vinfo = XGetVisualInfo(dpy, VisualDepthMask, &vinfo_template, &num_visuals); + } else { + vinfo_template.visualid = visual_id; + vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_template, &num_visuals); + } + if (vinfo == NULL || num_visuals == 0) exit_player(EXIT_ERROR); + for (i = 0; i < num_visuals; i++) { + mp_msg(MSGT_VO, MSGL_V, "gles: visuals[%i]: visual depth: %i\n", i, vinfo[i].depth); + } + + colormap = XCreateColormap(dpy, RootWindow(dpy, vinfo->screen), vinfo->visual, AllocNone); + + vo_x11_create_vo_window(vinfo, 0, 0, width, height, flags, colormap, "EGL", name); + window = vo_window; // set by vo_x11_create_vo_window() + + XFree(vinfo); + + eglctx = eglCreateContext(egldpy, configs[cfg], EGL_NO_CONTEXT, NULL); + if (eglctx == EGL_NO_CONTEXT) exit_player(EXIT_ERROR); + eglwindow = eglCreateWindowSurface(egldpy, configs[cfg], (NativeWindowType) window, NULL); + if (eglwindow == EGL_NO_SURFACE) exit_player(EXIT_ERROR); + if (eglMakeCurrent(egldpy, eglwindow, eglwindow, eglctx) == EGL_FALSE) exit_player(EXIT_ERROR); + + win_width = width; + win_height = height; +} + +static void destroyEGLWindow(void) +{ + eglMakeCurrent(egldpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(egldpy, eglwindow); + eglDestroyContext(egldpy, eglctx); + eglTerminate(egldpy); +} + +static void postEGLWindow(void) +{ + eglSwapBuffers(egldpy, eglwindow); +} + +// ----------------------------------------------- + +static int query_format(uint32_t format) +{ + int flags = VFCAP_CSP_SUPPORTED | VFCAP_ACCEPT_STRIDE | VFCAP_OSD; + //uint32_t fourcc[2] = { format, 0 }; + + //printf("\n at query_format: 0x%08x (%s)\n", format, (char *) fourcc); + switch (format) { + case IMGFMT_RGB32: + case IMGFMT_RGB24: +#if defined(GL_EXT_bgra) || defined(GL_EXT_texture_format_BGRA8888) + case IMGFMT_BGR32: +#endif +#if defined(GL_EXT_bgra) + case IMGFMT_BGR24: +#endif + return flags; + } + + return 0; +} + +// ----------------------------------------------- + +static int draw_frame(uint8_t *src[]) +{ + int stride[] = { src_width * src_pixel_bytes }; + + //printf("\n at draw_frame\n"); + draw(src, stride, src_width, src_height, 0, 0); + + return 0; +} + +static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y) +{ + //printf("\n at draw_slice x: %i y: %i w: %i h: %i stride: %i src@%p\n", x, y, w, h, stride[0], src[0]); + draw(src, stride, w, h, x, y); + + return 0; +} + +static void draw_osd(void) +{ + vo_draw_text(win_width, win_height, draw_alpha); +} + +static void check_events(void) +{ + int event = vo_x11_check_events(dpy); + + if (event & VO_EVENT_RESIZE) { + XWindowAttributes window_attributes; + + XGetWindowAttributes(dpy, window, &window_attributes); + mp_msg(MSGT_VO, MSGL_INFO, "gles: resize event! (%ix%i, vo: %ix%i)\n", + window_attributes.width, window_attributes.height, vo_dwidth, vo_dheight); + if (unshown) mp_msg(MSGT_VO, MSGL_INFO, "gles: resizing with non-empty buffer!\n"); + if (win_width != window_attributes.width || win_height != window_attributes.height) { + win_width = window_attributes.width; + win_height = window_attributes.height; + reshape(win_width, win_height); + } else mp_msg(MSGT_VO, MSGL_INFO, "gles: hmm.. resize event without size change!\n"); + } +} + +static void flip_page(void) +{ + if (!unshown) mp_msg(MSGT_VO, MSGL_INFO, "gles: flipping empty buffer!\n"); + postEGLWindow(); + unshown = 0; +} + +static int config(uint32_t width, uint32_t height, uint32_t d_width, + uint32_t d_height, uint32_t flags, char *title, + uint32_t format) +{ + switch (format) { + case IMGFMT_RGB32: + tex_format = GL_RGBA; + src_pixel_bytes = 4; + break; + case IMGFMT_RGB24: + tex_format = GL_RGB; + src_pixel_bytes = 3; + break; +#if defined(GL_EXT_bgra) || defined(GL_EXT_texture_format_BGRA8888) + case IMGFMT_BGR32: + tex_format = GL_BGRA_EXT; + src_pixel_bytes = 4; + break; +#endif +#if defined(GL_EXT_bgra) + case IMGFMT_BGR24: + tex_format = GL_BGR_EXT; + src_pixel_bytes = 3; + break; +#endif + default: /* unsupported */ + return 1; + }; + + src_width = width; + src_height = height; + + if (!eglwindow) createEGLWindow(width, height, title, flags); + gl_init(); + + return 0; +} + +static int preinit(const char *arg) +{ + if (!vo_init()) return VO_FALSE; // x11 init + + return 0; +} + +static void uninit(void) +{ + if (eglwindow) destroyEGLWindow(); + vo_x11_uninit(); + if (colormap) XFreeColormap(dpy, colormap); +} + +static int control(uint32_t request, void *data) +{ + switch (request) { + case VOCTRL_QUERY_FORMAT: + return query_format(*((uint32_t *) data)); + case VOCTRL_ONTOP: + vo_x11_ontop(); + return VO_TRUE; + case VOCTRL_BORDER: + vo_x11_border(); + return VO_TRUE; + case VOCTRL_FULLSCREEN: + vo_x11_fullscreen(); + return VO_TRUE; + case VOCTRL_UPDATE_SCREENINFO: + update_xinerama_info(); + return VO_TRUE; + } + + return VO_NOTIMPL; +} -- 2.16.4