mirror of
https://github.com/mpv-player/mpv.git
synced 2024-09-20 03:52:22 +02:00
vo_opengl: support new VAAPI EGL interop
Should work much better than the old GLX interop code. Requires Mesa 11, and explicitly selecting the X11 EGL backend with: --vo=opengl:backend=x11egl Should it turn out that the new interop works well, we will try to autodetect EGL by default. This code still uses some bad assumptions, like expecting surfaces to be in NV12. (This is probably ok, because virtually all HW will use this format. But we should at least check this on init or so, instead of failing to render an image if our assumption doesn't hold up.) This repo was a lot of help: https://github.com/gbeauchesne/ffvademo The kodi code was also helpful (the magic FourCC it uses for EGL_LINUX_DRM_FOURCC_EXT are nowhere documented, and EGL_IMAGE_INTERNAL_FORMAT_EXT as used in ffvademo does not actually exist). (This is the 3rd VAAPI GL interop that was implemented in this player.)
This commit is contained in:
parent
0e9cfa6b64
commit
8d8a2045bd
@ -704,6 +704,10 @@ _vaapi_glx=no
|
||||
(test "$_gl_x11" = yes && test "$_vaapi" = yes) && _vaapi_glx=auto
|
||||
check_pkg_config "VAAPI with OpenGL/X11" $_vaapi_glx VAAPI_GLX 'libva-glx >= 0.32.0'
|
||||
|
||||
_vaapi_x_egl=no
|
||||
(test "$_gl_x11_egl" = yes && test "$_vaapi" = yes) && _vaapi_x_egl=yes
|
||||
check_yes_no $_vaapi_x_egl VAAPI_X_EGL
|
||||
|
||||
check_pkg_config "SDL 2.0" $_sdl2 SDL2 'sdl2'
|
||||
|
||||
check_statement_libs "OSS Audio" $_ossaudio OSS_AUDIO $_soundcard_header "int x = SNDCTL_DSP_SETFRAGMENT;"
|
||||
|
@ -91,6 +91,7 @@ SOURCES-$(VAAPI) += video/out/vo_vaapi.c \
|
||||
video/vaapi.c
|
||||
SOURCES-$(VAAPI_VPP) += video/filter/vf_vavpp.c
|
||||
SOURCES-$(VAAPI_GLX) += video/out/opengl/hwdec_vaglx.c
|
||||
SOURCES-$(VAAPI_X_EGL) += video/out/opengl/hwdec_vaegl.c
|
||||
|
||||
SOURCES-$(XV) += video/out/x11_common.c video/out/vo_xv.c
|
||||
SOURCES-$(WAYLAND) += video/out/vo_wayland.c \
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "common/msg.h"
|
||||
#include "hwdec.h"
|
||||
|
||||
extern const struct gl_hwdec_driver gl_hwdec_vaegl;
|
||||
extern const struct gl_hwdec_driver gl_hwdec_vaglx;
|
||||
extern const struct gl_hwdec_driver gl_hwdec_vda;
|
||||
extern const struct gl_hwdec_driver gl_hwdec_videotoolbox;
|
||||
@ -36,6 +37,9 @@ extern const struct gl_hwdec_driver gl_hwdec_vdpau;
|
||||
extern const struct gl_hwdec_driver gl_hwdec_dxva2;
|
||||
|
||||
static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = {
|
||||
#if HAVE_VAAPI_X_EGL
|
||||
&gl_hwdec_vaegl,
|
||||
#endif
|
||||
#if HAVE_VAAPI_GLX
|
||||
&gl_hwdec_vaglx,
|
||||
#endif
|
||||
|
247
video/out/opengl/hwdec_vaegl.c
Normal file
247
video/out/opengl/hwdec_vaegl.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* Parts based on the MPlayer VA-API patch (see vo_vaapi.c).
|
||||
*
|
||||
* mpv 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.
|
||||
*
|
||||
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
#include <va/va_drmcommon.h>
|
||||
|
||||
#include "video/out/x11_common.h"
|
||||
#include "hwdec.h"
|
||||
#include "video/vaapi.h"
|
||||
#include "video/img_fourcc.h"
|
||||
#include "common.h"
|
||||
|
||||
struct priv {
|
||||
struct mp_log *log;
|
||||
struct mp_vaapi_ctx *ctx;
|
||||
VADisplay *display;
|
||||
Display *xdisplay;
|
||||
GLuint gl_textures[4];
|
||||
EGLImageKHR images[4];
|
||||
VAImage current_image;
|
||||
struct mp_image *current_ref;
|
||||
};
|
||||
|
||||
static void unref_image(struct gl_hwdec *hw)
|
||||
{
|
||||
struct priv *p = hw->priv;
|
||||
VAStatus status;
|
||||
|
||||
for (int n = 0; n < 4; n++) {
|
||||
if (p->images[n])
|
||||
hw->gl->DestroyImageKHR(eglGetCurrentDisplay(), p->images[n]);
|
||||
}
|
||||
|
||||
va_lock(p->ctx);
|
||||
|
||||
if (p->current_image.buf != VA_INVALID_ID) {
|
||||
status = vaReleaseBufferHandle(p->display, p->current_image.buf);
|
||||
CHECK_VA_STATUS(p, "vaReleaseBufferHandle()");
|
||||
p->current_image.buf = VA_INVALID_ID;
|
||||
}
|
||||
if (p->current_image.image_id != VA_INVALID_ID) {
|
||||
status = vaDestroyImage(p->display, p->current_image.image_id);
|
||||
CHECK_VA_STATUS(p, "vaDestroyImage()");
|
||||
p->current_image.image_id = VA_INVALID_ID;
|
||||
}
|
||||
|
||||
mp_image_unrefp(&p->current_ref);
|
||||
|
||||
va_unlock(p->ctx);
|
||||
}
|
||||
|
||||
static void destroy_textures(struct gl_hwdec *hw)
|
||||
{
|
||||
struct priv *p = hw->priv;
|
||||
GL *gl = hw->gl;
|
||||
|
||||
gl->DeleteTextures(4, p->gl_textures);
|
||||
for (int n = 0; n < 4; n++)
|
||||
p->gl_textures[n] = 0;
|
||||
}
|
||||
|
||||
static void destroy(struct gl_hwdec *hw)
|
||||
{
|
||||
struct priv *p = hw->priv;
|
||||
destroy_textures(hw);
|
||||
va_destroy(p->ctx);
|
||||
}
|
||||
|
||||
static int create(struct gl_hwdec *hw)
|
||||
{
|
||||
GL *gl = hw->gl;
|
||||
|
||||
if (hw->hwctx)
|
||||
return -1;
|
||||
if (!eglGetCurrentDisplay())
|
||||
return -1;
|
||||
|
||||
Display *x11disp =
|
||||
hw->gl->MPGetNativeDisplay ? hw->gl->MPGetNativeDisplay("x11") : NULL;
|
||||
if (!x11disp)
|
||||
return -1;
|
||||
if (!gl->CreateImageKHR || !gl->EGLImageTargetTexture2DOES ||
|
||||
!strstr(gl->extensions, "EXT_image_dma_buf_import") ||
|
||||
!(gl->mpgl_caps & MPGL_CAP_TEX_RG))
|
||||
return -1;
|
||||
|
||||
struct priv *p = talloc_zero(hw, struct priv);
|
||||
hw->priv = p;
|
||||
for (int n = 0; n < 4; n++)
|
||||
p->current_image.buf = p->current_image.image_id = VA_INVALID_ID;
|
||||
p->log = hw->log;
|
||||
p->xdisplay = x11disp;
|
||||
p->display = vaGetDisplay(x11disp);
|
||||
if (!p->display)
|
||||
return -1;
|
||||
|
||||
p->ctx = va_initialize(p->display, p->log, true);
|
||||
if (!p->ctx) {
|
||||
vaTerminate(p->display);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hw->reject_emulated && va_guess_if_emulated(p->ctx)) {
|
||||
destroy(hw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MP_VERBOSE(p, "using VAAPI EGL interop\n");
|
||||
|
||||
hw->hwctx = &p->ctx->hwctx;
|
||||
hw->converted_imgfmt = IMGFMT_NV12;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
|
||||
{
|
||||
struct priv *p = hw->priv;
|
||||
GL *gl = hw->gl;
|
||||
|
||||
// Recreate them to get rid of all previous image data (possibly).
|
||||
destroy_textures(hw);
|
||||
|
||||
assert(params->imgfmt == hw->driver->imgfmt);
|
||||
|
||||
gl->GenTextures(4, p->gl_textures);
|
||||
for (int n = 0; n < 4; n++) {
|
||||
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
gl->BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADD_ATTRIB(name, value) \
|
||||
do { \
|
||||
assert(num_attribs + 3 < MP_ARRAY_SIZE(attribs)); \
|
||||
attribs[num_attribs++] = (name); \
|
||||
attribs[num_attribs++] = (value); \
|
||||
attribs[num_attribs] = EGL_NONE; \
|
||||
} while(0)
|
||||
|
||||
static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
|
||||
GLuint *out_textures)
|
||||
{
|
||||
struct priv *p = hw->priv;
|
||||
GL *gl = hw->gl;
|
||||
VAStatus status;
|
||||
VAImage *va_image = &p->current_image;
|
||||
|
||||
unref_image(hw);
|
||||
|
||||
mp_image_setrefp(&p->current_ref, hw_image);
|
||||
|
||||
va_lock(p->ctx);
|
||||
|
||||
status = vaDeriveImage(p->display, va_surface_id(hw_image), va_image);
|
||||
if (!CHECK_VA_STATUS(p, "vaDeriveImage()"))
|
||||
goto err;
|
||||
|
||||
int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc);
|
||||
if (mpfmt != IMGFMT_NV12) {
|
||||
MP_FATAL(p, "unsupported VA image format %s\n",
|
||||
VA_STR_FOURCC(va_image->format.fourcc));
|
||||
goto err;
|
||||
}
|
||||
|
||||
VABufferInfo buffer_info = {.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME};
|
||||
status = vaAcquireBufferHandle(p->display, va_image->buf, &buffer_info);
|
||||
if (!CHECK_VA_STATUS(p, "vaAcquireBufferHandle()"))
|
||||
goto err;
|
||||
|
||||
int num_planes = 2;
|
||||
int plane_sizes[2] = {1, 2};
|
||||
int plane_xs[2] = {0, 1};
|
||||
int plane_ys[2] = {0, 1};
|
||||
|
||||
// (it would be nice if we could use EGL_IMAGE_INTERNAL_FORMAT_EXT)
|
||||
int drm_fmts[4] = {MP_FOURCC('R', '8', ' ', ' '),
|
||||
MP_FOURCC('G', 'R', '8', '8'),
|
||||
0, 0};
|
||||
|
||||
for (int n = 0; n < num_planes; n++) {
|
||||
int attribs[20];
|
||||
int num_attribs = 0;
|
||||
|
||||
ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, drm_fmts[plane_sizes[n] - 1]);
|
||||
ADD_ATTRIB(EGL_WIDTH, mp_chroma_div_up(hw_image->w, plane_xs[n]));
|
||||
ADD_ATTRIB(EGL_HEIGHT, mp_chroma_div_up(hw_image->h, plane_ys[n]));
|
||||
ADD_ATTRIB(EGL_DMA_BUF_PLANE0_FD_EXT, buffer_info.handle);
|
||||
ADD_ATTRIB(EGL_DMA_BUF_PLANE0_OFFSET_EXT, va_image->offsets[n]);
|
||||
ADD_ATTRIB(EGL_DMA_BUF_PLANE0_PITCH_EXT, va_image->pitches[n]);
|
||||
|
||||
p->images[n] = hw->gl->CreateImageKHR(eglGetCurrentDisplay(),
|
||||
EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
|
||||
if (!p->images[n])
|
||||
goto err;
|
||||
|
||||
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
|
||||
gl->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]);
|
||||
|
||||
out_textures[n] = p->gl_textures[n];
|
||||
}
|
||||
gl->BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
va_unlock(p->ctx);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
va_unlock(p->ctx);
|
||||
MP_FATAL(p, "mapping VAAPI EGL image failed\n");
|
||||
unref_image(hw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct gl_hwdec_driver gl_hwdec_vaegl = {
|
||||
.api_name = "vaapi",
|
||||
.imgfmt = IMGFMT_VAAPI,
|
||||
.create = create,
|
||||
.reinit = reinit,
|
||||
.map_image = map_image,
|
||||
.destroy = destroy,
|
||||
};
|
5
wscript
5
wscript
@ -657,6 +657,11 @@ video_output_features = [
|
||||
'desc': 'VAAPI GLX',
|
||||
'deps': [ 'vaapi', 'gl-x11' ],
|
||||
'func': check_true,
|
||||
}, {
|
||||
'name': '--vaapi-x-egl',
|
||||
'desc': 'VAAPI EGL on X11',
|
||||
'deps': [ 'vaapi', 'egl-x11' ],
|
||||
'func': check_true,
|
||||
}, {
|
||||
'name': '--caca',
|
||||
'desc': 'CACA',
|
||||
|
@ -319,6 +319,7 @@ def build(ctx):
|
||||
( "video/out/opengl/rpi.c", "rpi" ),
|
||||
( "video/out/opengl/hwdec.c", "gl" ),
|
||||
( "video/out/opengl/hwdec_dxva2.c", "gl-win32" ),
|
||||
( "video/out/opengl/hwdec_vaegl.c", "vaapi-x-egl" ),
|
||||
( "video/out/opengl/hwdec_vaglx.c", "vaapi-glx" ),
|
||||
( "video/out/opengl/hwdec_vda.c", "videotoolbox-vda-gl" ),
|
||||
( "video/out/opengl/hwdec_vdpau.c", "vdpau-gl-x11" ),
|
||||
|
Loading…
Reference in New Issue
Block a user