From 2fcd5271eb045e235c281faa73d8676c4bcea4b3 Mon Sep 17 00:00:00 2001 From: Michael Forney Date: Mon, 27 Aug 2018 13:06:38 -0700 Subject: Reintroduce vo_wayland as vo_wlshm vo_wayland was removed during the wayland rewrite done in 0.28. However, it is still useful for systems that do not have OpenGL. The new wayland_common code makes vo_wayland much simpler, and eliminates many of the issues the previous vo_wayland had. --- DOCS/man/vo.rst | 6 + video/out/vo.c | 4 + video/out/vo_wlshm.c | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++ wscript_build.py | 1 + 4 files changed, 323 insertions(+) create mode 100644 video/out/vo_wlshm.c diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index f5fd08ca0d..ea2d3ee645 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -578,3 +578,9 @@ Available video output drivers are: To use hardware decoding with ``--vo=gpu`` instead, use ``--hwdec=mediacodec-copy`` along with ``--gpu-context=android``. + +``wlshm`` (Wayland only) + Shared memory video output driver without hardware acceleration that works + whenever Wayland is present. + + .. note:: This is a fallback only, and should not be normally used. diff --git a/video/out/vo.c b/video/out/vo.c index 53db20d3b4..545ff8bf0c 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -60,6 +60,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_wlshm; extern const struct vo_driver video_out_rpi; extern const struct vo_driver video_out_tct; @@ -76,6 +77,9 @@ const struct vo_driver *const video_out_drivers[] = #if HAVE_DIRECT3D &video_out_direct3d, #endif +#if HAVE_WAYLAND + &video_out_wlshm, +#endif #if HAVE_XV &video_out_xv, #endif diff --git a/video/out/vo_wlshm.c b/video/out/vo_wlshm.c new file mode 100644 index 0000000000..1577248407 --- /dev/null +++ b/video/out/vo_wlshm.c @@ -0,0 +1,312 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include + +#include + +#include "sub/osd.h" +#include "video/fmt-conversion.h" +#include "video/mp_image.h" +#include "video/sws_utils.h" +#include "vo.h" +#include "wayland_common.h" + +struct buffer { + struct vo *vo; + size_t size; + struct wl_shm_pool *pool; + struct wl_buffer *buffer; + struct mp_image mpi; + struct buffer *next; +}; + +struct priv { + struct mp_sws_context *sws; + struct buffer *free_buffers; + struct mp_rect src; + struct mp_rect dst; + struct mp_osd_res osd; +}; + +static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) +{ + struct buffer *buf = data; + struct vo *vo = buf->vo; + struct priv *p = vo->priv; + + if (buf->mpi.w == vo->dwidth && buf->mpi.h == vo->dheight) { + buf->next = p->free_buffers; + p->free_buffers = buf; + } else { + talloc_free(buf); + } +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_handle_release, +}; + +static void buffer_destroy(void *p) +{ + struct buffer *buf = p; + wl_buffer_destroy(buf->buffer); + wl_shm_pool_destroy(buf->pool); + munmap(buf->mpi.planes[0], buf->size); +} + +/* modeled after randname and mkostemps from musl */ +static int alloc_shm(size_t size) +{ + struct timespec ts; + unsigned long r; + int i, fd, retries = 100; + char template[] = "/mpv-XXXXXX"; + + do { + clock_gettime(CLOCK_REALTIME, &ts); + r = ts.tv_nsec * 65537 ^ ((uintptr_t)&ts / 16 + (uintptr_t)template); + for (i = 5; i < sizeof(template) - 1; i++, r >>= 5) + template[i] = 'A' + (r & 15) + (r & 16) * 2; + + fd = shm_open(template, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(template); + if (posix_fallocate(fd, 0, size) == 0) + return fd; + close(fd); + break; + } + } while (--retries && errno == EEXIST); + + return -1; +} + +static struct buffer *buffer_create(struct vo *vo, int width, int height) +{ + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + int fd; + int stride; + size_t size; + uint8_t *data; + struct buffer *buf; + + stride = MP_ALIGN_UP(width * 4, 16); + size = height * stride; + fd = alloc_shm(size); + if (fd < 0) + goto error0; + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + goto error1; + buf = talloc_zero(NULL, struct buffer); + if (!buf) + goto error2; + buf->vo = vo; + buf->size = size; + mp_image_set_params(&buf->mpi, &p->sws->dst); + buf->mpi.w = width; + buf->mpi.h = height; + buf->mpi.planes[0] = data; + buf->mpi.stride[0] = stride; + buf->pool = wl_shm_create_pool(wl->shm, fd, size); + if (!buf->pool) + goto error3; + buf->buffer = wl_shm_pool_create_buffer(buf->pool, 0, width, height, + stride, WL_SHM_FORMAT_XRGB8888); + if (!buf->buffer) + goto error4; + wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + close(fd); + talloc_set_destructor(buf, buffer_destroy); + + return buf; + +error4: + wl_shm_pool_destroy(buf->pool); +error3: + talloc_free(buf); +error2: + munmap(data, size); +error1: + close(fd); +error0: + return NULL; +} + +static int preinit(struct vo *vo) +{ + struct priv *p = vo->priv; + + if (!vo_wayland_init(vo)) + return -1; + p->sws = mp_sws_alloc(vo); + + return 0; +} + +static int query_format(struct vo *vo, int format) +{ + return sws_isSupportedInput(imgfmt2pixfmt(format)); +} + +static int reconfig(struct vo *vo, struct mp_image_params *params) +{ + struct priv *p = vo->priv; + + if (!vo_wayland_reconfig(vo)) + return -1; + mp_sws_set_from_cmdline(p->sws, vo->global); + p->sws->src = *params; + + return 0; +} + +static int resize(struct vo *vo) +{ + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + const int32_t width = wl->scaling * mp_rect_w(wl->geometry); + const int32_t height = wl->scaling * mp_rect_h(wl->geometry); + struct buffer *buf; + + vo->want_redraw = true; + vo->dwidth = width; + vo->dheight = height; + vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd); + p->sws->dst = (struct mp_image_params) { + .imgfmt = IMGFMT_BGR0, + .w = width, + .h = height, + .p_w = 1, + .p_h = 1, + }; + mp_image_params_guess_csp(&p->sws->dst); + while (p->free_buffers) { + buf = p->free_buffers; + p->free_buffers = buf->next; + talloc_free(buf); + } + return mp_sws_reinit(p->sws); +} + +static int control(struct vo *vo, uint32_t request, void *data) +{ + int events = 0; + int ret = vo_wayland_control(vo, &events, request, data); + + if (events & VO_EVENT_RESIZE) + ret = resize(vo); + vo_event(vo, events); + return ret; +} + +static void draw_image(struct vo *vo, struct mp_image *src) +{ + struct priv *p = vo->priv; + struct vo_wayland_state *wl = vo->wl; + struct buffer *buf; + + buf = p->free_buffers; + if (buf) { + p->free_buffers = buf->next; + } else { + buf = buffer_create(vo, vo->dwidth, vo->dheight); + if (!buf) { + wl_surface_attach(wl->surface, NULL, 0, 0); + return; + } + } + if (src) { + struct mp_image dst = buf->mpi; + struct mp_rect src_rc; + struct mp_rect dst_rc; + src_rc.x0 = MP_ALIGN_DOWN(p->src.x0, MPMAX(src->fmt.align_x, 4)); + src_rc.y0 = MP_ALIGN_DOWN(p->src.y0, MPMAX(src->fmt.align_y, 4)); + src_rc.x1 = p->src.x1 - (p->src.x0 - src_rc.x0); + src_rc.y1 = p->src.y1 - (p->src.y0 - src_rc.y0); + dst_rc.x0 = MP_ALIGN_DOWN(p->dst.x0, MPMAX(dst.fmt.align_x, 4)); + dst_rc.y0 = MP_ALIGN_DOWN(p->dst.y0, MPMAX(dst.fmt.align_y, 4)); + dst_rc.x1 = p->dst.x1 - (p->dst.x0 - dst_rc.x0); + dst_rc.y1 = p->dst.y1 - (p->dst.y0 - dst_rc.y0); + mp_image_crop_rc(src, src_rc); + mp_image_crop_rc(&dst, dst_rc); + mp_sws_scale(p->sws, &dst, src); + if (dst_rc.y0 > 0) + mp_image_clear(&buf->mpi, 0, 0, buf->mpi.w, dst_rc.y0); + if (buf->mpi.h > dst_rc.y1) + mp_image_clear(&buf->mpi, 0, dst_rc.y1, buf->mpi.w, buf->mpi.h); + if (dst_rc.x0 > 0) + mp_image_clear(&buf->mpi, 0, dst_rc.y0, dst_rc.x0, dst_rc.y1); + if (buf->mpi.w > dst_rc.x1) + mp_image_clear(&buf->mpi, dst_rc.x1, dst_rc.y0, buf->mpi.w, dst_rc.y1); + osd_draw_on_image(vo->osd, p->osd, src->pts, 0, &buf->mpi); + } else { + mp_image_clear(&buf->mpi, 0, 0, buf->mpi.w, buf->mpi.h); + osd_draw_on_image(vo->osd, p->osd, 0, 0, &buf->mpi); + } + talloc_free(src); + wl_surface_attach(wl->surface, buf->buffer, 0, 0); +} + +static void flip_page(struct vo *vo) +{ + struct vo_wayland_state *wl = vo->wl; + + wl_surface_damage(wl->surface, 0, 0, mp_rect_w(wl->geometry), + mp_rect_h(wl->geometry)); + wl_surface_commit(wl->surface); +} + +static void uninit(struct vo *vo) +{ + struct priv *p = vo->priv; + struct buffer *buf; + + while (p->free_buffers) { + buf = p->free_buffers; + p->free_buffers = buf->next; + talloc_free(buf); + } + vo_wayland_uninit(vo); +} + +#define OPT_BASE_STRUCT struct priv +static const m_option_t options[] = { + {0} +}; + +const struct vo_driver video_out_wlshm = { + .description = "Wayland SHM video output", + .name = "wlshm", + .preinit = preinit, + .query_format = query_format, + .reconfig = reconfig, + .control = control, + .draw_image = draw_image, + .flip_page = flip_page, + .wakeup = vo_wayland_wakeup, + .wait_events = vo_wayland_wait_events, + .uninit = uninit, + .priv_size = sizeof(struct priv), + .options = options, +}; diff --git a/wscript_build.py b/wscript_build.py index 63273898f3..1fed29ceea 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -488,6 +488,7 @@ def build(ctx): ( "video/out/vo_tct.c" ), ( "video/out/vo_vaapi.c", "vaapi-x11 && gpl" ), ( "video/out/vo_vdpau.c", "vdpau" ), + ( "video/out/vo_wlshm.c", "wayland" ), ( "video/out/vo_x11.c" , "x11" ), ( "video/out/vo_xv.c", "xv" ), ( "video/out/vulkan/context.c", "vulkan" ), -- cgit v1.2.3