diff options
-rw-r--r-- | DOCS/man/options.rst | 2 | ||||
-rw-r--r-- | DOCS/man/vo.rst | 8 | ||||
-rw-r--r-- | generated/wayland/meson.build | 6 | ||||
-rw-r--r-- | meson.build | 6 | ||||
-rw-r--r-- | video/out/drm_common.c | 449 | ||||
-rw-r--r-- | video/out/drm_common.h | 2 | ||||
-rw-r--r-- | video/out/vo.c | 5 | ||||
-rw-r--r-- | video/out/vo_vaapi_wayland.c | 401 | ||||
-rw-r--r-- | video/out/wayland_common.c | 86 | ||||
-rw-r--r-- | video/out/wayland_common.h | 11 | ||||
-rw-r--r-- | wscript | 5 | ||||
-rw-r--r-- | wscript_build.py | 15 |
12 files changed, 992 insertions, 4 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 7e9323c67c..21a984e480 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1249,7 +1249,7 @@ Video :videotoolbox: requires ``--vo=gpu`` (macOS 10.8 and up), or ``--vo=libmpv`` (iOS 9.0 and up) :videotoolbox-copy: copies video back into system RAM (macOS 10.8 or iOS 9.0 and up) - :vaapi: requires ``--vo=gpu`` or ``--vo=vaapi`` (Linux only) + :vaapi: requires ``--vo=gpu``, ``--vo=vaapi`` or ``--vo=vaapi-wayland`` (Linux only) :vaapi-copy: copies video back into system RAM (Linux with some GPUs only) :nvdec: requires ``--vo=gpu`` (Any platform CUDA is available) :nvdec-copy: copies video back to system RAM (Any platform CUDA is available) diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index e453fdcca1..13d4050dc4 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -286,6 +286,14 @@ Available video output drivers are: ``--sdl-switch-mode`` Instruct SDL to switch the monitor video mode when going fullscreen. +``vaapi-wayland`` + Experimental Wayland output driver designed for use with VA API hardware decoding. + The driver is designed to avoid any GPU to CPU copies, and to perform scaling and + color space conversion using fixed-function hardware, if available, + rather than GPU shaders. This frees up GPU resources for other tasks. + Currently this driver is experimental and only works with the ``--hwdec=vaapi`` driver; + OSD is also not supported. Supported compositors : Weston and Sway. + ``vaapi`` Intel VA API video output driver with support for hardware decoding. Note that there is absolutely no reason to use this, other than compatibility. diff --git a/generated/wayland/meson.build b/generated/wayland/meson.build index 19ed4bc023..79247409b1 100644 --- a/generated/wayland/meson.build +++ b/generated/wayland/meson.build @@ -1,7 +1,9 @@ wl_protocol_dir = wayland['deps'][2].get_variable(pkgconfig: 'pkgdatadir') -protocols = [[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], - [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], +protocols = [[wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], + [wl_protocol_dir, 'stable/viewporter/viewporter.xml'], + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml']] wl_protocols_source = [] wl_protocols_headers = [] diff --git a/meson.build b/meson.build index 25f399da64..0b07582a33 100644 --- a/meson.build +++ b/meson.build @@ -1432,10 +1432,16 @@ vaapi_wayland = { 'deps': dependency('libva-wayland', version: '>= 1.1.0', required: get_option('vaapi-wayland')), } vaapi_wayland += {'use': vaapi['use'] and egl_wayland['use'] and vaapi_wayland['deps'].found()} + if vaapi_wayland['use'] features += vaapi_wayland['name'] endif +if vaapi_wayland['use'] and memfd_create + features += 'vaapi-wayland-memfd' + sources += files('video/out/vo_vaapi_wayland.c') +endif + vaapi_x11 = { 'name': 'vaapi-x11', 'deps': dependency('libva-x11', version: '>= 1.1.0', required: get_option('vaapi-x11')), diff --git a/video/out/drm_common.c b/video/out/drm_common.c index dad9aaf2fb..cd79f3ee42 100644 --- a/video/out/drm_common.c +++ b/video/out/drm_common.c @@ -25,6 +25,7 @@ #include <limits.h> #include <math.h> #include <time.h> +#include <drm_fourcc.h> #include "config.h" @@ -1099,3 +1100,451 @@ fail: *closure->waiting_for_flip = false; talloc_free(closure); } + +const char* drm_format_string(uint drm_format) { + + switch (drm_format) { + /* Reserve 0 for the invalid format specifier */ + case DRM_FORMAT_INVALID: + return "DRM_FORMAT_INVALID"; + break; + /* color index */ + case DRM_FORMAT_C8: + return "DRM_FORMAT_C8"; + break; + /* 8 bpp Red */ + case DRM_FORMAT_R8: + return "DRM_FORMAT_R8"; + break; + /* these formats are not in the header for some reason */ +#if 0 + /* 10 bpp Red */ + case DRM_FORMAT_R10: + return "DRM_FORMAT_R10"; + break; + /* 12 bpp Red */ + case DRM_FORMAT_R12: + return "DRM_FORMAT_R12"; + break; +#endif + /* 16 bpp Red */ + case DRM_FORMAT_R16: + return "DRM_FORMAT_R16"; + break; + /* 16 bpp RG */ + case DRM_FORMAT_RG88: + return "DRM_FORMAT_RG88"; + break; + case DRM_FORMAT_GR88: + return "DRM_FORMAT_GR88"; + break; + /* 32 bpp RG */ + case DRM_FORMAT_RG1616: + return "DRM_FORMAT_RG1616"; + break; + case DRM_FORMAT_GR1616: + return "DRM_FORMAT_GR1616"; + break; + /* 8 bpp RGB */ + case DRM_FORMAT_RGB332: + return "DRM_FORMAT_RGB332"; + break; + case DRM_FORMAT_BGR233: + return "DRM_FORMAT_BGR233"; + break; + /* 16 bpp RGB */ + case DRM_FORMAT_XRGB4444: + return "DRM_FORMAT_XRGB4444"; + break; + case DRM_FORMAT_XBGR4444: + return "DRM_FORMAT_XBGR4444"; + break; + case DRM_FORMAT_RGBX4444: + return "DRM_FORMAT_RGBX4444"; + break; + case DRM_FORMAT_BGRX4444: + return "DRM_FORMAT_BGRX4444"; + break; + case DRM_FORMAT_ARGB4444: + return "DRM_FORMAT_ARGB4444"; + break; + case DRM_FORMAT_ABGR4444: + return "DRM_FORMAT_ABGR4444"; + break; + case DRM_FORMAT_RGBA4444: + return "DRM_FORMAT_RGBA4444"; + break; + case DRM_FORMAT_BGRA4444: + return "DRM_FORMAT_BGRA4444"; + break; + case DRM_FORMAT_XRGB1555: + return "DRM_FORMAT_XRGB1555"; + break; + case DRM_FORMAT_XBGR1555: + return "DRM_FORMAT_XBGR1555"; + break; + case DRM_FORMAT_RGBX5551: + return "DRM_FORMAT_RGBX5551"; + break; + case DRM_FORMAT_BGRX5551: + return "DRM_FORMAT_BGRX5551"; + break; + case DRM_FORMAT_ARGB1555: + return "DRM_FORMAT_ARGB1555"; + break; + case DRM_FORMAT_ABGR1555: + return "DRM_FORMAT_ABGR1555"; + break; + case DRM_FORMAT_RGBA5551: + return "DRM_FORMAT_RGBA5551"; + break; + case DRM_FORMAT_BGRA5551: + return "DRM_FORMAT_BGRA5551"; + break; + case DRM_FORMAT_RGB565: + return "DRM_FORMAT_RGB565"; + break; + case DRM_FORMAT_BGR565: + return "DRM_FORMAT_BGR565"; + break; + /* 24 bpp RGB */ + case DRM_FORMAT_RGB888: + return "DRM_FORMAT_RGB888"; + break; + case DRM_FORMAT_BGR888: + return "DRM_FORMAT_BGR888"; + break; + /* 32 bpp RGB */ + case DRM_FORMAT_XRGB8888: + return "DRM_FORMAT_XRGB8888"; + break; + case DRM_FORMAT_XBGR8888: + return "DRM_FORMAT_XBGR8888"; + break; + case DRM_FORMAT_RGBX8888: + return "DRM_FORMAT_RGBX8888"; + break; + case DRM_FORMAT_BGRX8888: + return "DRM_FORMAT_BGRX8888"; + break; + case DRM_FORMAT_ARGB8888: + return "DRM_FORMAT_ARGB8888"; + break; + case DRM_FORMAT_ABGR8888: + return "DRM_FORMAT_ABGR8888"; + break; + case DRM_FORMAT_RGBA8888: + return "DRM_FORMAT_RGBA8888"; + break; + case DRM_FORMAT_BGRA8888: + return "DRM_FORMAT_BGRA8888"; + break; + case DRM_FORMAT_XRGB2101010: + return "DRM_FORMAT_XRGB2101010"; + break; + case DRM_FORMAT_XBGR2101010: + return "DRM_FORMAT_XBGR2101010"; + break; + case DRM_FORMAT_RGBX1010102: + return "DRM_FORMAT_RGBX1010102"; + break; + case DRM_FORMAT_BGRX1010102: + return "DRM_FORMAT_BGRX1010102"; + break; + case DRM_FORMAT_ARGB2101010: + return "DRM_FORMAT_ARGB2101010"; + break; + case DRM_FORMAT_ABGR2101010: + return "DRM_FORMAT_ABGR2101010"; + break; + case DRM_FORMAT_RGBA1010102: + return "DRM_FORMAT_RGBA1010102"; + break; + case DRM_FORMAT_BGRA1010102: + return "DRM_FORMAT_BGRA1010102"; + break; + /* 64 bpp RGB */ + case DRM_FORMAT_XRGB16161616: + return "DRM_FORMAT_XRGB16161616"; + break; + case DRM_FORMAT_XBGR16161616: + return "DRM_FORMAT_XBGR16161616"; + break; + case DRM_FORMAT_ARGB16161616: + return "DRM_FORMAT_ARGB16161616"; + break; + case DRM_FORMAT_ABGR16161616: + return "DRM_FORMAT_ABGR16161616"; + break; + /* + * Floating point 64bpp RGB + * IEEE 754-2008 binary16 half-precision float + * [15:0] sign:exponent:mantissa 1:5:10 + */ + case DRM_FORMAT_XRGB16161616F: + return "DRM_FORMAT_XRGB16161616F"; + break; + case DRM_FORMAT_XBGR16161616F: + return "DRM_FORMAT_XBGR16161616F"; + break; + case DRM_FORMAT_ARGB16161616F: + return "DRM_FORMAT_ARGB16161616F"; + break; + case DRM_FORMAT_ABGR16161616F: + return "DRM_FORMAT_ABGR16161616F"; + break; + /* + * RGBA format with 10-bit components packed in 64-bit per pixel, with 6 bits + * of unused padding per component: + */ + case DRM_FORMAT_AXBXGXRX106106106106: + return "DRM_FORMAT_AXBXGXRX106106106106"; + break; + /* packed YCbCr */ + case DRM_FORMAT_YUYV: + return "DRM_FORMAT_YUYV"; + break; + case DRM_FORMAT_YVYU: + return "DRM_FORMAT_YVYU"; + break; + case DRM_FORMAT_UYVY: + return "DRM_FORMAT_UYVY"; + break; + case DRM_FORMAT_VYUY: + return "DRM_FORMAT_VYUY"; + break; + case DRM_FORMAT_AYUV: + return "DRM_FORMAT_AYUV"; + break; + case DRM_FORMAT_XYUV8888: + return "DRM_FORMAT_XYUV8888"; + break; + case DRM_FORMAT_VUY888: + return "DRM_FORMAT_VUY888"; + break; + case DRM_FORMAT_VUY101010: + return "DRM_FORMAT_VUY101010"; + break; + /* + * packed Y2xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb + */ + case DRM_FORMAT_Y210: + return "DRM_FORMAT_Y210"; + break; + case DRM_FORMAT_Y212: + return "DRM_FORMAT_Y212"; + break; + case DRM_FORMAT_Y216: + return "DRM_FORMAT_Y216"; + break; + /* + * packed Y4xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb except Y410 + */ + case DRM_FORMAT_Y410: + return "DRM_FORMAT_Y410"; + break; + case DRM_FORMAT_Y412: + return "DRM_FORMAT_Y412"; + break; + case DRM_FORMAT_Y416: + return "DRM_FORMAT_Y416"; + break; + case DRM_FORMAT_XVYU2101010: + return "DRM_FORMAT_XVYU2101010"; + break; + case DRM_FORMAT_XVYU12_16161616: + return "DRM_FORMAT_XVYU12_16161616"; + break; + case DRM_FORMAT_XVYU16161616: + return "DRM_FORMAT_XVYU16161616"; + break; + /* + * packed YCbCr420 2x2 tiled formats + * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile + */ + /* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ + case DRM_FORMAT_Y0L0: + return "DRM_FORMAT_Y0L0"; + break; + /* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ + case DRM_FORMAT_X0L0: + return "DRM_FORMAT_X0L0"; + break; + /* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ + case DRM_FORMAT_Y0L2: + return "DRM_FORMAT_Y0L2"; + break; + /* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ + case DRM_FORMAT_X0L2: + return "DRM_FORMAT_X0L2"; + break; + /* + * 1-plane YUV 4:2:0 + * In these formats, the component ordering is specified (Y, followed by U + * then V), but the exact Linear layout is undefined. + * These formats can only be used with a non-Linear modifier. + */ + case DRM_FORMAT_YUV420_8BIT: + return "DRM_FORMAT_YUV420_8BIT"; + break; + case DRM_FORMAT_YUV420_10BIT: + return "DRM_FORMAT_YUV420_10BIT"; + break; + /* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ + case DRM_FORMAT_XRGB8888_A8: + return "DRM_FORMAT_XRGB8888_A8"; + break; + case DRM_FORMAT_XBGR8888_A8: + return "DRM_FORMAT_XBGR8888_A8"; + break; + case DRM_FORMAT_RGBX8888_A8: + return "DRM_FORMAT_RGBX8888_A8"; + break; + case DRM_FORMAT_BGRX8888_A8: + return "DRM_FORMAT_BGRX8888_A8"; + break; + case DRM_FORMAT_RGB888_A8: + return "DRM_FORMAT_RGB888_A8"; + break; + case DRM_FORMAT_BGR888_A8: + return "DRM_FORMAT_BGR888_A8"; + break; + case DRM_FORMAT_RGB565_A8: + return "DRM_FORMAT_RGB565_A8"; + break; + case DRM_FORMAT_BGR565_A8: + return "DRM_FORMAT_BGR565_A8"; + break; + /* + * 2 plane YCbCr + * index 0 = Y plane, [7:0] Y + * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian + * or + * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian + */ + case DRM_FORMAT_NV12: + return "DRM_FORMAT_NV12"; + break; + case DRM_FORMAT_NV21: + return "DRM_FORMAT_NV21"; + break; + case DRM_FORMAT_NV16: + return "DRM_FORMAT_NV16"; + break; + case DRM_FORMAT_NV61: + return "DRM_FORMAT_NV61"; + break; + case DRM_FORMAT_NV24: + return "DRM_FORMAT_NV24"; + break; + case DRM_FORMAT_NV42: + return "DRM_FORMAT_NV42"; + break; /* + * 2 plane YCbCr + * index 0 = Y plane, [39:0] Y3:Y2:Y1:Y0 little endian + * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian + */ + case DRM_FORMAT_NV15: + return "DRM_FORMAT_NV15"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ + case DRM_FORMAT_P210: + return "DRM_FORMAT_P210"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ + case DRM_FORMAT_P010: + return "DRM_FORMAT_P010"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [12:4] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [12:4:12:4] little endian + */ + case DRM_FORMAT_P012: + return "DRM_FORMAT_P012"; + break; + /* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y little endian + * index 1 = Cr:Cb plane, [31:0] Cr:Cb [16:16] little endian + */ + case DRM_FORMAT_P016: + return "DRM_FORMAT_P016"; + break; + /* 3 plane non-subsampled (444) YCbCr + * 16 bits per component, but only 10 bits are used and 6 bits are padded + * index 0: Y plane, [15:0] Y:x [10:6] little endian + * index 1: Cb plane, [15:0] Cb:x [10:6] little endian + * index 2: Cr plane, [15:0] Cr:x [10:6] little endian + */ + case DRM_FORMAT_Q410: + return "DRM_FORMAT_Q410"; + break; + /* 3 plane non-subsampled (444) YCrCb + * 16 bits per component, but only 10 bits are used and 6 bits are padded + * index 0: Y plane, [15:0] Y:x [10:6] little endian + * index 1: Cr plane, [15:0] Cr:x [10:6] little endian + * index 2: Cb plane, [15:0] Cb:x [10:6] little endian + */ + case DRM_FORMAT_Q401: + return "DRM_FORMAT_Q401"; + break; + /* + * 3 plane YCbCr + * index 0: Y plane, [7:0] Y + * index 1: Cb plane, [7:0] Cb + * index 2: Cr plane, [7:0] Cr + * or + * index 1: Cr plane, [7:0] Cr + * index 2: Cb plane, [7:0] Cb + */ + case DRM_FORMAT_YUV410: + return "DRM_FORMAT_YUV410"; + break; + case DRM_FORMAT_YVU410: + return "DRM_FORMAT_YVU410"; + break; + case DRM_FORMAT_YUV411: + return "DRM_FORMAT_YUV411"; + break; + case DRM_FORMAT_YVU411: + return "DRM_FORMAT_YVU411"; + break; + case DRM_FORMAT_YUV420: + return "DRM_FORMAT_YUV420"; + break; + case DRM_FORMAT_YVU420: + return "DRM_FORMAT_YVU420"; + break; + case DRM_FORMAT_YUV422: + return "DRM_FORMAT_YUV422"; + break; + case DRM_FORMAT_YVU422: + return "DRM_FORMAT_YVU422"; + break; + case DRM_FORMAT_YUV444: + return "DRM_FORMAT_YUV444"; + break; + case DRM_FORMAT_YVU444: + return "DRM_FORMAT_YVU444"; + break; + default: + return "DRM_FORMAT_UNKNOWN"; + break; + } +} + diff --git a/video/out/drm_common.h b/video/out/drm_common.h index 5d884e3c80..cd7f87e506 100644 --- a/video/out/drm_common.h +++ b/video/out/drm_common.h @@ -97,4 +97,6 @@ double kms_get_display_fps(const struct kms *kms); void drm_pflip_cb(int fd, unsigned int msc, unsigned int sec, unsigned int usec, void *data); +const char* drm_format_string(uint drm_format); + #endif diff --git a/video/out/vo.c b/video/out/vo.c index 11ef596227..6b38711acf 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -62,6 +62,7 @@ extern const struct vo_driver video_out_drm; extern const struct vo_driver video_out_direct3d; extern const struct vo_driver video_out_sdl; extern const struct vo_driver video_out_vaapi; +extern const struct vo_driver video_out_vaapi_wayland; extern const struct vo_driver video_out_wlshm; extern const struct vo_driver video_out_rpi; extern const struct vo_driver video_out_tct; @@ -92,7 +93,9 @@ const struct vo_driver *const video_out_drivers[] = #if HAVE_SDL2_VIDEO &video_out_sdl, #endif -#if HAVE_VAAPI_X11 && HAVE_GPL +#if HAVE_VAAPI_WAYLAND + &video_out_vaapi_wayland, +#elif HAVE_VAAPI_X11 && HAVE_GPL &video_out_vaapi, #endif #if HAVE_X11 diff --git a/video/out/vo_vaapi_wayland.c b/video/out/vo_vaapi_wayland.c new file mode 100644 index 0000000000..931b88229a --- /dev/null +++ b/video/out/vo_vaapi_wayland.c @@ -0,0 +1,401 @@ +/* + * This file is part of mpv video player. + * + * mpv 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. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#include <va/va_wayland.h> +#include <va/va_drmcommon.h> + +#include "sub/osd.h" +#include "video/vaapi.h" +#include "wayland_common.h" +#include "drm_common.h" +#include "generated/wayland/linux-dmabuf-unstable-v1.h" +#include "generated/wayland/viewporter.h" + +#define VA_POOL_NUM_ALLOCATED_INIT 30 + +struct va_pool_entry { + /* key */ + VASurfaceID surface; + + VADRMPRIMESurfaceDescriptor desc; + struct wl_buffer *buffer; + struct zwp_linux_buffer_params_v1 *params; + uint drm_format; +}; +struct va_pool { + struct vo *vo; + struct va_pool_entry **entries; + uint num_entries; + uint num_allocated; +}; + +struct priv { + struct vo *vo; + struct mp_rect src; + struct mp_rect dst; + struct mp_osd_res osd; + struct mp_log *log; + + VADisplay display; + struct mp_vaapi_ctx *mpvaapi; + struct va_image_formats *image_formats; + struct wl_shm_pool *solid_buffer_pool; + struct wl_buffer *solid_buffer; + struct va_pool *va_pool; +}; + +static void va_close_surface_descriptor(VADRMPRIMESurfaceDescriptor desc) { + for (uint i = 0; i < desc.num_objects; i++) { + close(desc.objects[i].fd); + desc.objects[i].fd = 0; + } +} + +static void va_free_entry(struct va_pool_entry *entry) { + if (!entry) + return; + va_close_surface_descriptor(entry->desc); + if (entry->buffer) + wl_buffer_destroy(entry->buffer); + if (entry->params) + zwp_linux_buffer_params_v1_destroy(entry->params); + talloc_free(entry); +} + +static VAStatus va_export_surface_handle(VADisplay display, VASurfaceID surface, + VADRMPRIMESurfaceDescriptor *desc) { + return vaExportSurfaceHandle(display, surface, + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_COMPOSED_LAYERS | VA_EXPORT_SURFACE_READ_ONLY, + desc); +} + +static struct va_pool_entry* va_alloc_entry(struct vo *vo, struct mp_image *src) { + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + VAStatus status; + struct va_pool_entry *entry = talloc(NULL, struct va_pool_entry); + memset(entry, 0, sizeof(struct va_pool_entry)); + + /* extract dmabuf surface descriptor */ + entry->surface = va_surface_id(src); + status = va_export_surface_handle(p->display, entry->surface, &entry->desc); + if (status == VA_STATUS_ERROR_INVALID_SURFACE) { + MP_VERBOSE(vo, "VA export to composed layers not supported.\n"); + va_free_entry(entry); + return NULL; + } else if (!vo_wayland_supported_format(vo, + entry->desc.layers[0].drm_format)) { + MP_VERBOSE(vo, "%s not supported.\n", + drm_format_string(entry->desc.layers[0].drm_format)); + va_free_entry(entry); + return NULL; + } else if (!CHECK_VA_STATUS(vo, "vaExportSurfaceHandle()")) { + va_free_entry(entry); + return NULL; + } else { + uint i, j, plane = 0; + + entry->params = zwp_linux_dmabuf_v1_create_params(wl->dmabuf); + for (i = 0; i < entry->desc.num_layers; i++) { + entry->drm_format = entry->desc.layers[i].drm_format; + for (j = 0; j < entry->desc.layers[i].num_planes; ++j) { + int object = entry->desc.layers[i].object_index[j]; + uint64_t modifier = + entry->desc.objects[object].drm_format_modifier; + zwp_linux_buffer_params_v1_add(entry->params, + entry->desc.objects[object].fd, plane++, + entry->desc.layers[i].offset[j], + entry->desc.layers[i].pitch[j], modifier >> 32, + modifier & 0xffffffff); + } + } + } + entry->buffer = zwp_linux_buffer_params_v1_create_immed(entry->params, + src->params.w, src->params.h, entry->drm_format, 0); + + return entry; +} +static void va_pool_clean(struct va_pool *pool) { + if (!pool) + return; + + for (uint i = 0; i < pool->num_entries; ++i) + va_free_entry(pool->entries[i]); + pool->num_entries = 0; +} + +static void va_pool_free(struct va_pool *pool) { + if (!pool) + return; + + va_pool_clean(pool); + talloc_free(pool->entries); + talloc_free(pool); +} +static struct va_pool* va_pool_alloc(struct vo *vo) { + struct va_pool *pool = talloc(NULL, struct va_pool); + memset(pool, 0, sizeof(struct va_pool)); + pool->num_allocated = VA_POOL_NUM_ALLOCATED_INIT; + pool->entries = talloc_array(NULL,struct va_pool_entry*, pool->num_allocated); + memset(pool->entries,0,pool->num_allocated * sizeof(struct va_pool_entry*)); + pool->vo = vo; + + return pool; +} +static struct va_pool_entry* va_pool_alloc_entry(struct vo *vo, + struct va_pool *pool, struct mp_image *src) { + VASurfaceID surface; + + if (!pool) + return NULL; + + surface = va_surface_id(src); + for (uint i = 0; i < pool->num_entries; ++i) { + struct va_pool_entry *item = pool->entries[i]; + if (item->surface == surface) + return pool->entries[i]; + } + + struct va_pool_entry *entry = va_alloc_entry(pool->vo, src); + if (!entry) + return NULL; + + if (pool->num_entries == pool->num_allocated) { + uint current_num_allocated = pool->num_allocated; + pool->num_allocated *= 2; + pool->entries = talloc_realloc(NULL,pool->entries,struct va_pool_entry*, pool->num_allocated); + for (uint i = current_num_allocated; i < pool->num_allocated; ++i) + pool->entries[i] = NULL; + } + pool->entries[pool->num_entries++] = entry; + + return entry; +} + +struct va_image_formats { + VAImageFormat *entries; + int num; +}; + +static void va_get_formats(struct priv *ctx) { + struct va_image_formats *formats = talloc_ptrtype(ctx, formats); + + formats->num = vaMaxNumImageFormats(ctx->display); + formats->entries = talloc_array(formats, VAImageFormat, formats->num); + VAStatus status = vaQueryImageFormats(ctx->display, formats->entries, + &formats->num); + if (!CHECK_VA_STATUS(ctx, "vaQueryImageFormats()")) + return; + MP_VERBOSE(ctx, "%d image formats available:\n", formats->num); + for (int i = 0; i < formats->num; i++) + MP_VERBOSE(ctx, " %s\n", mp_tag_str(formats->entries[i].fourcc)); + ctx->image_formats = formats; +} + +static void uninit(struct vo *vo) { + struct priv *p = vo->priv; + + va_pool_free(p->va_pool); + if (p->solid_buffer_pool) + wl_shm_pool_destroy(p->solid_buffer_pool); + if (p->solid_buffer) + wl_buffer_destroy(p->solid_buffer); + vo_wayland_uninit(vo); + + if (vo->hwdec_devs) { + hwdec_devices_remove(vo->hwdec_devs, &p->mpvaapi->hwctx); + hwdec_devices_destroy(vo->hwdec_devs); + } + if (p->mpvaapi) + va_destroy(p->mpvaapi); +} +static int allocate_memfd(size_t size) { + int fd = memfd_create("mpv", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd < 0) + return VO_ERROR; + + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + + if (posix_fallocate(fd, 0, size) == 0) + return fd; + + close(fd); + return VO_ERROR; +} + +static int preinit(struct vo *vo) { + struct priv *p = vo->priv; + + p->vo = vo; + p->log = vo->log; + if (!vo_wayland_init(vo)) + return VO_ERROR; + p->display = vaGetDisplayWl(vo->wl->display); + if (!p->display) + return VO_ERROR; + p->mpvaapi = va_initialize(p->display, p->log, false); + if (!p->mpvaapi) { + vaTerminate(p->display); + p->display = NULL; + goto fail; + } + va_get_formats(p); + if (!p->image_formats) + goto fail; + + vo->hwdec_devs = hwdec_devices_create(); + hwdec_devices_add(vo->hwdec_devs, &p->mpvaapi->hwctx); + p->va_pool = va_pool_alloc(vo); + + return 0; + +fail: + uninit(vo); + + return VO_ERROR; +} + +static int query_format(struct vo *vo, int format) { + return format == IMGFMT_VAAPI; +} + +static int reconfig(struct vo *vo, struct mp_image_params *params) { + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + + if (!p->solid_buffer_pool) { + int width = 1; + int height = 1; + int stride = MP_ALIGN_UP(width * 4, 16); + int fd = allocate_memfd(stride); + if (fd < 0) + return VO_ERROR; + p->solid_buffer_pool = wl_shm_create_pool(wl->shm, fd, height * stride); + if (!p->solid_buffer_pool) + return VO_ERROR; + p->solid_buffer = wl_shm_pool_create_buffer(p->solid_buffer_pool, 0, + width, height, stride, WL_SHM_FORMAT_XRGB8888); + if (!p->solid_buffer) + return VO_ERROR; + } + if (!vo_wayland_reconfig(vo)) + return VO_ERROR; + + return 0; +} + +static int resize(struct vo *vo) { + struct vo_wayland_state *wl = vo->wl; + struct priv *p = vo->priv; + + wl_subsurface_set_sync(wl->video_subsurface); + vo_wayland_set_opaque_region(wl, 0); + const int32_t width = wl->scaling * mp_rect_w(wl->geometry); + const int32_t height = wl->scaling * mp_rect_h(wl->geometry); + vo->dwidth = width; + vo->dheight = height; + vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd); + wp_viewport_set_destination(wl->viewport, + (p->dst.x0 << 1) + mp_rect_w(p->dst), + (p->dst.y0 << 1) + mp_rect_h(p->dst)); + wp_viewport_set_destination(wl->video_viewport, mp_rect_w(p->dst), + mp_rect_h(p->dst)); + wl_subsurface_set_position(wl->video_subsurface, p->dst.x0, p->dst.y0); + vo->want_redraw = true; + wl_subsurface_set_desync(wl->video_subsurface); + + return VO_TRUE; +} + +static int control(struct vo *vo, uint32_t request, void *data) { + struct priv *p = vo->priv; + int events = 0; + int ret; + + switch (request) { + /* need to clean pool after seek to avoid artifacts */ + case VOCTRL_RESET: + va_pool_clean(p->va_pool); + break; + default: + break; + } + ret = vo_wayland_control(vo, &events, request, data); + if (events & VO_EVENT_RESIZE) + ret = resize(vo); + if (events & VO_EVENT_EXPOSE) + vo->want_redraw = true; + vo_event(vo, events); + + return ret; +} + +static void draw_frame(struct vo *vo, struct vo_frame *frame) { + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + + if (!vo_wayland_check_visible(vo)) + return; + + struct va_pool_entry *entry = va_pool_alloc_entry(vo, p->va_pool, + frame->current); + if (!entry) + return; + + wl_surface_attach(wl->surface, p->solid_buffer, 0, 0); + wl_surface_attach(wl->video_surface, entry->buffer, 0, 0); + wl_surface_damage_buffer(wl->video_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(wl->video_surface); + wl_surface_commit(wl->surface); + + if (!wl->opts->disable_vsync) + vo_wayland_wait_frame(wl); + if (wl->presentation) + vo_wayland_sync_swap(wl); +} +static void flip_page(struct vo *vo) { + /* no-op */ +} + +static void get_vsync(struct vo *vo, struct vo_vsync_info *info) { + struct vo_wayland_state *wl = vo->wl; + if (wl->presentation) { + info->vsync_duration = wl->vsync_duration; + info->skipped_vsyncs = wl->last_skipped_vsyncs; + info->last_queue_display_time = wl->last_queue_display_time; + } +} + +const struct vo_driver video_out_vaapi_wayland = { .description = + "VA API with Wayland video output", + .name = "vaapi-wayland", + .preinit = preinit, + .query_format = query_format, + .reconfig = reconfig, + .control = control, + .draw_frame = draw_frame, + .flip_page = flip_page, + .get_vsync = get_vsync, + .wakeup = vo_wayland_wakeup, + .wait_events = vo_wayland_wait_events, + .uninit = uninit, + .priv_size = sizeof(struct priv) +}; diff --git a/video/out/wayland_co |