summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-09-25 00:20:57 +0200
committerwm4 <wm4@nowhere>2015-09-25 00:26:19 +0200
commit8d8a2045bde250a50228df7fa8f24124ec5b0251 (patch)
tree2ac12b399b6cb84db51204cffb6baafa1352154b /video
parent0e9cfa6b642266af66cba59a259e0edef44d60f0 (diff)
downloadmpv-8d8a2045bde250a50228df7fa8f24124ec5b0251.tar.bz2
mpv-8d8a2045bde250a50228df7fa8f24124ec5b0251.tar.xz
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.)
Diffstat (limited to 'video')
-rw-r--r--video/out/opengl/hwdec.c4
-rw-r--r--video/out/opengl/hwdec_vaegl.c247
2 files changed, 251 insertions, 0 deletions
diff --git a/video/out/opengl/hwdec.c b/video/out/opengl/hwdec.c
index ee602fa191..d93dc99397 100644
--- a/video/out/opengl/hwdec.c
+++ b/video/out/opengl/hwdec.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
diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c
new file mode 100644
index 0000000000..b084d7203d
--- /dev/null
+++ b/video/out/opengl/hwdec_vaegl.c
@@ -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,
+};