0
0
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:
wm4 2015-09-25 00:20:57 +02:00
parent 0e9cfa6b64
commit 8d8a2045bd
6 changed files with 262 additions and 0 deletions

View File

@ -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;"

View File

@ -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 \

View File

@ -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

View 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,
};

View File

@ -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',

View File

@ -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" ),