vlc/modules/video_output/opengl/vout_helper.c

401 lines
12 KiB
C

/*****************************************************************************
* vout_helper.c: OpenGL and OpenGL ES output common code
*****************************************************************************
* Copyright (C) 2004-2016 VLC authors and VideoLAN
* Copyright (C) 2009, 2011 Laurent Aimar
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
* Ilkka Ollakka <ileoo@videolan.org>
* Rémi Denis-Courmont
* Adrien Maglo <magsoft at videolan dot org>
* Felix Paul Kühne <fkuehne at videolan dot org>
* Pierre d'Herbemont <pdherbemont at videolan dot org>
*
* 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 "config.h"
#endif
#include <assert.h>
#include <math.h>
#include <vlc_common.h>
#include <vlc_configuration.h>
#include <vlc_list.h>
#include <vlc_subpicture.h>
#include <vlc_opengl.h>
#include <vlc_modules.h>
#include <vlc_vout.h>
#include <vlc_viewpoint.h>
#include "filters.h"
#include "gl_api.h"
#include "gl_util.h"
#include "vout_helper.h"
#include "renderer.h"
#include "sub_renderer.h"
struct vout_display_opengl_t {
vlc_gl_t *gl;
struct vlc_gl_api api;
struct vlc_gl_interop *interop;
struct vlc_gl_renderer *renderer; /**< weak reference */
struct vlc_gl_filters *filters;
struct vlc_gl_interop *sub_interop;
struct vlc_gl_sub_renderer *sub_renderer;
};
static const vlc_fourcc_t gl_subpicture_chromas[] = {
VLC_CODEC_RGBA,
0
};
static void
ResizeFormatToGLMaxTexSize(video_format_t *fmt, unsigned int max_tex_size)
{
if (fmt->i_width > fmt->i_height)
{
unsigned int const vis_w = fmt->i_visible_width;
unsigned int const vis_h = fmt->i_visible_height;
unsigned int const nw_w = max_tex_size;
unsigned int const nw_vis_w = nw_w * vis_w / fmt->i_width;
fmt->i_height = nw_w * fmt->i_height / fmt->i_width;
fmt->i_width = nw_w;
fmt->i_visible_height = nw_vis_w * vis_h / vis_w;
fmt->i_visible_width = nw_vis_w;
}
else
{
unsigned int const vis_w = fmt->i_visible_width;
unsigned int const vis_h = fmt->i_visible_height;
unsigned int const nw_h = max_tex_size;
unsigned int const nw_vis_h = nw_h * vis_h / fmt->i_height;
fmt->i_width = nw_h * fmt->i_width / fmt->i_height;
fmt->i_height = nw_h;
fmt->i_visible_width = nw_vis_h * vis_w / vis_h;
fmt->i_visible_height = nw_vis_h;
}
}
static struct vlc_gl_filters *
CreateFilters(vlc_gl_t *gl, const struct vlc_gl_api *api,
struct vlc_gl_interop *interop,
struct vlc_gl_renderer **out_renderer)
{
struct vlc_gl_filters *filters = vlc_gl_filters_New(gl, api, interop, gl->orientation);
if (!filters)
{
msg_Err(gl, "Could not create filters");
return NULL;
}
const opengl_vtable_t *vt = &api->vt;
const video_format_t *fmt_in = &interop->fmt_in;
int upscaler = var_InheritInteger(gl, "gl-upscaler");
int downscaler = var_InheritInteger(gl, "gl-downscaler");
int has_dovi = fmt_in->dovi.rpu_present && !fmt_in->dovi.el_present; /* can't handle EL yet */
if (upscaler || downscaler || has_dovi)
{
char upscaler_value[12];
char downscaler_value[12];
snprintf(upscaler_value, sizeof(upscaler_value), "%d", upscaler);
snprintf(downscaler_value, sizeof(downscaler_value), "%d", downscaler);
upscaler_value[sizeof(upscaler_value) - 1] = '\0';
downscaler_value[sizeof(downscaler_value) - 1] = '\0';
config_chain_t cfg = {
.psz_name = (char *) "upscaler",
.psz_value = upscaler_value,
.p_next = &(config_chain_t) {
.psz_name = (char *) "downscaler",
.psz_value = downscaler_value,
},
};
struct vlc_gl_filter *scale_filter =
vlc_gl_filters_Append(filters, "pl_scale", &cfg);
if (!scale_filter)
{
if (upscaler)
msg_Err(gl, "Could not apply upscaler filter, "
"ignoring --gl-upscaler=%d", upscaler);
if (downscaler)
msg_Err(gl, "Could not apply downscaler filter, "
"ignoring --gl-downscaler=%d", downscaler);
}
}
struct vlc_gl_filter *renderer_filter =
vlc_gl_filters_Append(filters, "renderer", NULL);
if (!renderer_filter)
{
msg_Warn(gl, "Could not create renderer for %4.4s",
(const char *) &fmt_in->i_chroma);
goto error;
}
GL_ASSERT_NOERROR(vt);
int ret = vlc_gl_filters_InitFramebuffers(filters);
if (ret != VLC_SUCCESS)
{
msg_Err(gl, "Could not init filters framebuffers");
goto error;
}
/* The renderer is a special filter: we need its concrete instance to
* forward SetViewpoint() */
*out_renderer = renderer_filter->sys;
return filters;
error:
vlc_gl_filters_Delete(filters);
return NULL;
}
vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
const vlc_fourcc_t **subpicture_chromas,
vlc_gl_t *gl,
const vlc_viewpoint_t *viewpoint,
vlc_video_context *context)
{
vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
if (!vgl)
return NULL;
vgl->gl = gl;
int ret = vlc_gl_api_Init(&vgl->api, gl);
if (ret != VLC_SUCCESS)
goto free_vgl;
const struct vlc_gl_api *api = &vgl->api;
const opengl_vtable_t *vt = &api->vt;
#if !defined(USE_OPENGL_ES2)
const unsigned char *ogl_version = vt->GetString(GL_VERSION);
bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
if (!supports_shaders)
{
msg_Err(gl, "shaders not supported, bailing out");
goto free_vgl;
}
#endif
/* Resize the format if it is greater than the maximum texture size
* supported by the hardware */
GLint max_tex_size;
vt->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
if ((GLint)fmt->i_width > max_tex_size ||
(GLint)fmt->i_height > max_tex_size)
ResizeFormatToGLMaxTexSize(fmt, max_tex_size);
vgl->interop = vlc_gl_interop_New(gl, context, fmt);
if (!vgl->interop)
{
msg_Err(gl, "Could not create interop");
goto free_vgl;
}
GL_ASSERT_NOERROR(vt);
vgl->filters = CreateFilters(gl, api, vgl->interop, &vgl->renderer);
if (!vgl->filters)
{
msg_Err(gl, "Could not create filters");
goto delete_interop;
}
GL_ASSERT_NOERROR(vt);
vgl->sub_interop = vlc_gl_interop_NewForSubpictures(gl);
if (!vgl->sub_interop)
{
msg_Err(gl, "Could not create sub interop");
goto delete_filters;
}
GL_ASSERT_NOERROR(vt);
vgl->sub_renderer =
vlc_gl_sub_renderer_New(gl, api, vgl->sub_interop);
if (!vgl->sub_renderer)
{
msg_Err(gl, "Could not create sub renderer");
goto delete_sub_interop;
}
GL_ASSERT_NOERROR(vt);
if (fmt->projection_mode != PROJECTION_MODE_RECTANGULAR
&& vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
msg_Err(gl, "Could not set viewpoint");
/* Forward to the core the changes to the input format requested by the
* interop */
video_format_Clean(fmt);
video_format_Copy(fmt, &vgl->interop->fmt_in);
if (subpicture_chromas) {
*subpicture_chromas = gl_subpicture_chromas;
}
GL_ASSERT_NOERROR(vt);
return vgl;
delete_sub_interop:
vlc_gl_interop_Delete(vgl->sub_interop);
delete_filters:
vlc_gl_filters_Delete(vgl->filters);
delete_interop:
vlc_gl_interop_Delete(vgl->interop);
free_vgl:
free(vgl);
return NULL;
}
int vout_display_opengl_UpdateFormat(vout_display_opengl_t *vgl,
const video_format_t *fmt,
vlc_video_context *vctx)
{
/* If the format can't be changed, the state must remain valid to accept the
* initial format. */
vlc_gl_t *gl = vgl->gl;
const struct vlc_gl_api *api = &vgl->api;
assert(!fmt->p_palette);
video_format_t in_fmt = *fmt;
struct vlc_gl_interop *interop = vlc_gl_interop_New(gl, vctx, &in_fmt);
if (!interop)
{
msg_Err(gl, "Could not create interop");
return VLC_EGENERIC;
}
if ( !video_format_IsSameChroma( &in_fmt, fmt ) )
{
msg_Warn(gl, "Could not update format, the interop changed the "
"requested chroma from %4.4s to %4.4s\n",
(char *) &fmt->i_chroma, (char *) &in_fmt.i_chroma);
vlc_gl_interop_Delete(interop);
return VLC_EGENERIC;
}
struct vlc_gl_renderer *renderer;
struct vlc_gl_filters *filters = CreateFilters(gl, api, interop, &renderer);
if (!filters)
{
vlc_gl_interop_Delete(interop);
return VLC_EGENERIC;
}
/* We created everything necessary, it worked, now the old ones could be
* replaced. */
vlc_gl_filters_Delete(vgl->filters);
vlc_gl_interop_Delete(vgl->interop);
vgl->interop = interop;
vgl->filters = filters;
vgl->renderer = renderer;
return VLC_SUCCESS;
}
void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
{
const opengl_vtable_t *vt = &vgl->api.vt;
GL_ASSERT_NOERROR(vt);
/* */
vt->Finish();
vt->Flush();
vlc_gl_sub_renderer_Delete(vgl->sub_renderer);
vlc_gl_interop_Delete(vgl->sub_interop);
vlc_gl_filters_Delete(vgl->filters);
vlc_gl_interop_Delete(vgl->interop);
GL_ASSERT_NOERROR(vt);
free(vgl);
}
int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
const vlc_viewpoint_t *p_vp)
{
return vlc_gl_renderer_SetViewpoint(vgl->renderer, p_vp);
}
void vout_display_opengl_SetOutputSize(vout_display_opengl_t *vgl,
unsigned width, unsigned height)
{
int ret = vlc_gl_filters_SetOutputSize(vgl->filters, width, height);
/* The renderer, last filter in the chain, necessarily accepts the new
* output size */
assert(ret == VLC_SUCCESS);
vlc_gl_sub_renderer_SetOutputSize(vgl->sub_renderer, width, height);
(void) ret;
}
void vout_display_opengl_Viewport(vout_display_opengl_t *vgl, int x, int y,
unsigned width, unsigned height)
{
vlc_gl_filters_SetViewport(vgl->filters, x, y, width, height);
}
int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
picture_t *picture,
const vlc_render_subpicture *subpicture)
{
GL_ASSERT_NOERROR(&vgl->api.vt);
int ret = vlc_gl_filters_UpdatePicture(vgl->filters, picture);
if (ret != VLC_SUCCESS)
return ret;
ret = vlc_gl_sub_renderer_Prepare(vgl->sub_renderer, subpicture);
GL_ASSERT_NOERROR(&vgl->api.vt);
return ret;
}
int vout_display_opengl_Display(vout_display_opengl_t *vgl)
{
GL_ASSERT_NOERROR(&vgl->api.vt);
int ret = vlc_gl_filters_Draw(vgl->filters);
if (ret != VLC_SUCCESS)
return ret;
ret = vlc_gl_sub_renderer_Draw(vgl->sub_renderer);
if (ret != VLC_SUCCESS)
return ret;
GL_ASSERT_NOERROR(&vgl->api.vt);
return VLC_SUCCESS;
}