/** * @file display.c * @brief OpenGL video output module */ /***************************************************************************** * Copyright © 2010-2011 Rémi Denis-Courmont * * This program is free software; you can redistribute it 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. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "vout_helper.h" /* Plugin callbacks */ static int Open (vlc_object_t *); static void Close (vlc_object_t *); #define GL_TEXT N_("OpenGL extension") #define GLES2_TEXT N_("OpenGL ES 2 extension") #define PROVIDER_LONGTEXT N_( \ "Extension through which to use the Open Graphics Library (OpenGL).") vlc_module_begin () #if defined (USE_OPENGL_ES2) # define API VLC_OPENGL_ES2 # define MODULE_VARNAME "gles2" set_shortname (N_("OpenGL ES2")) set_description (N_("OpenGL for Embedded Systems 2 video output")) set_capability ("vout display", 165) set_callbacks (Open, Close) add_shortcut ("opengles2", "gles2") add_module ("gles2", "opengl es2", NULL, GLES2_TEXT, PROVIDER_LONGTEXT, true) #else # define API VLC_OPENGL # define MODULE_VARNAME "gl" set_shortname (N_("OpenGL")) set_description (N_("OpenGL video output")) set_category (CAT_VIDEO) set_subcategory (SUBCAT_VIDEO_VOUT) set_capability ("vout display", 170) set_callbacks (Open, Close) add_shortcut ("opengl", "gl") add_module ("gl", "opengl", NULL, GL_TEXT, PROVIDER_LONGTEXT, true) #endif add_glopts () vlc_module_end () struct vout_display_sys_t { vout_display_opengl_t *vgl; vlc_gl_t *gl; picture_pool_t *pool; }; /* Display callbacks */ static picture_pool_t *Pool (vout_display_t *, unsigned); static void PictureRender (vout_display_t *, picture_t *, subpicture_t *); static void PictureDisplay (vout_display_t *, picture_t *, subpicture_t *); static int Control (vout_display_t *, int, va_list); /** * Allocates a surface and an OpenGL context for video output. */ static int Open (vlc_object_t *obj) { vout_display_t *vd = (vout_display_t *)obj; vout_display_sys_t *sys = malloc (sizeof (*sys)); if (unlikely(sys == NULL)) return VLC_ENOMEM; sys->gl = NULL; sys->pool = NULL; vout_window_t *surface = vout_display_NewWindow (vd, VOUT_WINDOW_TYPE_INVALID); if (surface == NULL) { msg_Err (vd, "parent window not available"); goto error; } const char *gl_name = "$" MODULE_VARNAME; /* VDPAU GL interop works only with GLX. Override the "gl" option to force * it. */ #ifndef USE_OPENGL_ES2 if (surface->type == VOUT_WINDOW_TYPE_XID) { switch (vd->fmt.i_chroma) { case VLC_CODEC_VDPAU_VIDEO_444: case VLC_CODEC_VDPAU_VIDEO_422: case VLC_CODEC_VDPAU_VIDEO_420: { /* Force the option only if it was not previously set */ char *str = var_InheritString(surface, MODULE_VARNAME); if (str == NULL || str[0] == 0 || strcmp(str, "any") == 0) gl_name = "glx"; free(str); break; } default: break; } } #endif sys->gl = vlc_gl_Create (surface, API, gl_name); if (sys->gl == NULL) goto error; vlc_gl_Resize (sys->gl, vd->cfg->display.width, vd->cfg->display.height); /* Initialize video display */ const vlc_fourcc_t *spu_chromas; if (vlc_gl_MakeCurrent (sys->gl)) goto error; sys->vgl = vout_display_opengl_New (&vd->fmt, &spu_chromas, sys->gl, &vd->cfg->viewpoint); vlc_gl_ReleaseCurrent (sys->gl); if (sys->vgl == NULL) goto error; vd->sys = sys; vd->info.has_pictures_invalid = false; vd->info.subpicture_chromas = spu_chromas; vd->pool = Pool; vd->prepare = PictureRender; vd->display = PictureDisplay; vd->control = Control; return VLC_SUCCESS; error: if (sys->gl != NULL) vlc_gl_Release (sys->gl); if (surface != NULL) vout_display_DeleteWindow (vd, surface); free (sys); return VLC_EGENERIC; } /** * Destroys the OpenGL context. */ static void Close (vlc_object_t *obj) { vout_display_t *vd = (vout_display_t *)obj; vout_display_sys_t *sys = vd->sys; vlc_gl_t *gl = sys->gl; vout_window_t *surface = gl->surface; vlc_gl_MakeCurrent (gl); vout_display_opengl_Delete (sys->vgl); vlc_gl_ReleaseCurrent (gl); vlc_gl_Release (gl); vout_display_DeleteWindow (vd, surface); free (sys); } /** * Returns picture buffers */ static picture_pool_t *Pool (vout_display_t *vd, unsigned count) { vout_display_sys_t *sys = vd->sys; if (!sys->pool && vlc_gl_MakeCurrent (sys->gl) == VLC_SUCCESS) { sys->pool = vout_display_opengl_GetPool (sys->vgl, count); vlc_gl_ReleaseCurrent (sys->gl); } return sys->pool; } static void PictureRender (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; if (vlc_gl_MakeCurrent (sys->gl) == VLC_SUCCESS) { vout_display_opengl_Prepare (sys->vgl, pic, subpicture); vlc_gl_ReleaseCurrent (sys->gl); } } static void PictureDisplay (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; if (vlc_gl_MakeCurrent (sys->gl) == VLC_SUCCESS) { vout_display_opengl_Display (sys->vgl, &vd->source); vlc_gl_ReleaseCurrent (sys->gl); } picture_Release (pic); if (subpicture != NULL) subpicture_Delete(subpicture); } static int Control (vout_display_t *vd, int query, va_list ap) { vout_display_sys_t *sys = vd->sys; switch (query) { #ifndef NDEBUG case VOUT_DISPLAY_RESET_PICTURES: // not needed vlc_assert_unreachable(); #endif case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: case VOUT_DISPLAY_CHANGE_ZOOM: { vout_display_cfg_t c = *va_arg (ap, const vout_display_cfg_t *); const video_format_t *src = &vd->source; vout_display_place_t place; /* Reverse vertical alignment as the GL tex are Y inverted */ if (c.align.vertical == VOUT_DISPLAY_ALIGN_TOP) c.align.vertical = VOUT_DISPLAY_ALIGN_BOTTOM; else if (c.align.vertical == VOUT_DISPLAY_ALIGN_BOTTOM) c.align.vertical = VOUT_DISPLAY_ALIGN_TOP; vout_display_PlacePicture (&place, src, &c, false); vlc_gl_Resize (sys->gl, place.width, place.height); if (vlc_gl_MakeCurrent (sys->gl) != VLC_SUCCESS) return VLC_EGENERIC; vout_display_opengl_SetWindowAspectRatio(sys->vgl, (float)place.width / place.height); vout_display_opengl_Viewport(sys->vgl, place.x, place.y, place.width, place.height); vlc_gl_ReleaseCurrent (sys->gl); return VLC_SUCCESS; } case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: case VOUT_DISPLAY_CHANGE_SOURCE_CROP: { const vout_display_cfg_t *cfg = vd->cfg; vout_display_place_t place; vout_display_PlacePicture (&place, &vd->source, cfg, false); if (vlc_gl_MakeCurrent (sys->gl) != VLC_SUCCESS) return VLC_EGENERIC; vout_display_opengl_SetWindowAspectRatio(sys->vgl, (float)place.width / place.height); vout_display_opengl_Viewport(sys->vgl, place.x, place.y, place.width, place.height); vlc_gl_ReleaseCurrent (sys->gl); return VLC_SUCCESS; } case VOUT_DISPLAY_CHANGE_VIEWPOINT: return vout_display_opengl_SetViewpoint (sys->vgl, &va_arg (ap, const vout_display_cfg_t* )->viewpoint); default: msg_Err (vd, "Unknown request %d", query); } return VLC_EGENERIC; }