[vlc-devel] [PATCH v2 1/1] wayland: add background surface and center video

Alexandre Janniaux ajanni at videolabs.io
Tue Feb 26 16:31:47 CET 2019


Add a subsurface which displays the video, allowing to change its
position from the vout display to center it, and use the vout window
surface to render the black border when the video aspect ratio doesn't
match the window size.

The black surface needs to be provided so that the parent surface is
correctly mapped, and will be needed with new UI needing transparency.
It's also a way to let the interface control the layering of the video.
---
 modules/video_output/wayland/shm.c | 209 ++++++++++++++++++++++++++---
 1 file changed, 194 insertions(+), 15 deletions(-)

diff --git a/modules/video_output/wayland/shm.c b/modules/video_output/wayland/shm.c
index 5d772269be..67605b8bd8 100644
--- a/modules/video_output/wayland/shm.c
+++ b/modules/video_output/wayland/shm.c
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <unistd.h>
+#include <fcntl.h>
 
 #include <wayland-client.h>
 #include "viewporter-client-protocol.h"
@@ -50,6 +51,17 @@ struct vout_display_sys_t
     struct wl_shm *shm;
     struct wp_viewporter *viewporter;
     struct wp_viewport *viewport;
+    struct wl_compositor *compositor;
+    struct wl_subcompositor *subcompositor;
+
+    struct wl_surface *surface;
+    struct wl_subsurface *subsurface;
+
+    struct
+    {
+        struct wl_buffer *buffer;
+        struct wp_viewport *viewport;
+    } back_bg;
 
     size_t active_buffers;
 
@@ -82,13 +94,52 @@ static const struct wl_buffer_listener buffer_cbs =
     buffer_release_cb,
 };
 
+static void background_buffer_release_cb(void *data, struct wl_buffer *buffer)
+{
+    vout_display_t *vd = data;
+    vout_display_sys_t *sys = vd->sys;
+
+    if (sys->back_bg.buffer == buffer)
+        sys->back_bg.buffer = NULL;
+
+    wl_buffer_destroy(buffer);
+}
+
+static const struct wl_buffer_listener background_buffer_cbs =
+{
+    background_buffer_release_cb,
+};
+
+static struct wl_buffer* CreateBackgroundBuffer(struct wl_shm *shm,
+                                                uint32_t width,
+                                                uint32_t height)
+{
+    int back_bg_fd = open("/dev/zero", O_RDWR);
+    if (back_bg_fd < 0)
+        return NULL;
+
+    struct wl_shm_pool *pool = wl_shm_create_pool(shm, back_bg_fd,
+                                                  width*height);
+    close(back_bg_fd);
+
+    if (pool == NULL)
+        return NULL;
+
+    struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0,
+                                                         width, height, width,
+                                                         WL_SHM_FORMAT_XRGB8888);
+    wl_shm_pool_destroy(pool);
+
+    return buffer;
+}
+
 static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic,
                     vlc_tick_t date)
 {
     VLC_UNUSED(date);
     vout_display_sys_t *sys = vd->sys;
     struct wl_display *display = sys->embed->display.wl;
-    struct wl_surface *surface = sys->embed->handle.wl;
+    struct wl_surface *surface = sys->surface;
     struct picture_buffer_t *picbuf = pic->p_sys;
 
     if (picbuf->fd == -1)
@@ -144,9 +195,8 @@ static void Display(vout_display_t *vd, picture_t *pic)
 {
     vout_display_sys_t *sys = vd->sys;
     struct wl_display *display = sys->embed->display.wl;
-    struct wl_surface *surface = sys->embed->handle.wl;
 
-    wl_surface_commit(surface);
+    wl_surface_commit(sys->surface);
     wl_display_roundtrip_queue(display, sys->eventq);
 
     (void) pic;
@@ -192,14 +242,15 @@ static int Control(vout_display_t *vd, int query, va_list ap)
             sys->display_width = cfg->display.width;
             sys->display_height = cfg->display.height;
 
-            if (sys->viewport != NULL)
-            {
-                video_format_t fmt;
-                vout_display_place_t place;
+            video_format_t fmt;
+            vout_display_place_t place;
+
+            video_format_ApplyRotation(&fmt, &vd->source);
+            vout_display_PlacePicture(&place, &vd->source, cfg);
 
-                video_format_ApplyRotation(&fmt, &vd->source);
-                vout_display_PlacePicture(&place, &vd->source, cfg);
 
+            if (sys->viewport != NULL)
+            {
                 wp_viewport_set_source(sys->viewport,
                                 wl_fixed_from_int(fmt.i_x_offset),
                                 wl_fixed_from_int(fmt.i_y_offset),
@@ -207,9 +258,47 @@ static int Control(vout_display_t *vd, int query, va_list ap)
                                 wl_fixed_from_int(fmt.i_visible_height));
                 wp_viewport_set_destination(sys->viewport,
                                             place.width, place.height);
+
+            }
+
+            /* wl_subsurface_set_position is double buffered by the
+             * parent surface, so update it when we change the position */
+            if (sys->viewport == NULL && sys->display_width < fmt.i_width)
+                place.x = 0;
+            if (sys->viewport == NULL && sys->display_height < fmt.i_height)
+                place.y = 0;
+
+            wl_subsurface_set_position(sys->subsurface, place.x, place.y);
+
+            if (sys->back_bg.viewport)
+            {
+                wp_viewport_set_source(sys->back_bg.viewport,
+                                       wl_fixed_from_int(0),
+                                       wl_fixed_from_int(0),
+                                       wl_fixed_from_int(1),
+                                       wl_fixed_from_int(1));
+                wp_viewport_set_destination(sys->back_bg.viewport,
+                                            cfg->display.width,
+                                            cfg->display.height);
             }
             else
-                return VLC_EGENERIC;
+            {
+                sys->back_bg.buffer =
+                    CreateBackgroundBuffer(sys->shm,
+                                           sys->display_width,
+                                           sys->display_height);
+                if (sys->back_bg.buffer)
+                {
+                    wl_buffer_add_listener(sys->back_bg.buffer,
+                                           &background_buffer_cbs, vd);
+                    wl_surface_attach(sys->embed->handle.wl,
+                                      sys->back_bg.buffer, 0, 0);
+                    wl_surface_damage(sys->embed->handle.wl, 0, 0,
+                                      sys->display_width, sys->display_height);
+                }
+            }
+            wl_surface_commit(sys->embed->handle.wl);
+
             break;
         }
         default:
@@ -254,7 +343,16 @@ static void registry_global_cb(void *data, struct wl_registry *registry,
                                            &wp_viewporter_interface, 1);
     else
     if (!strcmp(iface, "wl_compositor"))
+    {
         sys->use_buffer_transform = vers >= 2;
+        sys->compositor = wl_registry_bind(registry, name,
+                                           &wl_compositor_interface,
+                                           __MIN(2, vers));
+    }
+    else
+    if (!strcmp(iface, "wl_subcompositor"))
+        sys->subcompositor = wl_registry_bind(registry, name,
+                                              &wl_subcompositor_interface, 1);
 }
 
 static void registry_global_remove_cb(void *data, struct wl_registry *registry,
@@ -287,10 +385,17 @@ static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
     sys->eventq = NULL;
     sys->shm = NULL;
     sys->viewporter = NULL;
+    sys->compositor = NULL;
+    sys->subcompositor = NULL;
     sys->active_buffers = 0;
     sys->display_width = cfg->display.width;
     sys->display_height = cfg->display.height;
     sys->use_buffer_transform = false;
+    sys->surface = NULL;
+    sys->subsurface = NULL;
+
+    sys->back_bg.buffer = NULL;
+    sys->back_bg.viewport = NULL;
 
     /* Get window */
     sys->embed = cfg->window;
@@ -311,15 +416,39 @@ static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
     wl_display_roundtrip_queue(display, sys->eventq);
     wl_registry_destroy(registry);
 
-    if (sys->shm == NULL)
+    if (sys->shm == NULL || sys->compositor == NULL || sys->subcompositor == NULL)
         goto error;
 
     wl_shm_add_listener(sys->shm, &shm_cbs, vd);
     wl_display_roundtrip_queue(display, sys->eventq);
 
-    struct wl_surface *surface = sys->embed->handle.wl;
+    /* Create the subsurface containing the video */
+    sys->surface = wl_compositor_create_surface(sys->compositor);
+    if (!sys->surface)
+        goto error;
+
+    sys->subsurface = wl_subcompositor_get_subsurface(sys->subcompositor,
+                                                      sys->surface,
+                                                      sys->embed->handle.wl);
+    if (!sys->subsurface)
+        goto error;
+
+    /* UI surface is updated less frequently than the video so we need to be
+     * in desync mode */
+    wl_subsurface_set_desync(sys->subsurface);
+    wl_subsurface_place_above(sys->subsurface, sys->embed->handle.wl);
+
+    /* HACK: Clients like Qt can expect every surface to be their surface, and
+     * get events from wl_pointer object, independently of the wl_event_queue
+     * set so we avoid these event by disabling them on this surface. */
+    struct wl_region *input_region = wl_compositor_create_region(sys->compositor);
+    wl_region_add(input_region, 0, 0, 0, 0);
+    wl_surface_set_input_region(sys->surface, input_region);
+    wl_region_destroy(input_region);
+
     if (sys->viewporter != NULL)
-        sys->viewport = wp_viewporter_get_viewport(sys->viewporter, surface);
+        sys->viewport = wp_viewporter_get_viewport(sys->viewporter,
+                                                   sys->surface);
     else
         sys->viewport = NULL;
 
@@ -337,7 +466,7 @@ static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
 
     if (sys->use_buffer_transform)
     {
-        wl_surface_set_buffer_transform(surface,
+        wl_surface_set_buffer_transform(sys->surface,
                                         transforms[fmtp->orientation]);
     }
     else
@@ -346,6 +475,31 @@ static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
         video_format_ApplyRotation(fmtp, &fmt);
     }
 
+    /* When the viewporter is available, it's more efficient to rescale a
+     * 1x1 pixel buffer than re-upload large black textures */
+    if (sys->viewporter != NULL)
+    {
+        sys->back_bg.viewport = wp_viewporter_get_viewport(sys->viewporter, sys->embed->handle.wl);
+        sys->back_bg.buffer = CreateBackgroundBuffer(sys->shm, 1, 1);
+    }
+    else
+    {
+        sys->back_bg.viewport = NULL;
+        sys->back_bg.buffer = CreateBackgroundBuffer(sys->shm,
+                                                     sys->display_width,
+                                                     sys->display_height);
+    }
+
+    if (sys->back_bg.buffer == NULL)
+        goto error;
+    else
+        wl_buffer_add_listener(sys->back_bg.buffer, &background_buffer_cbs, vd);
+
+    wl_surface_attach(sys->embed->handle.wl, sys->back_bg.buffer, 0, 0);
+    wl_surface_damage(sys->embed->handle.wl, 0, 0,
+                      sys->display_width, sys->display_height);
+    wl_surface_commit(sys->embed->handle.wl);
+
     fmtp->i_chroma = VLC_CODEC_RGB32;
 
     vd->info.has_pictures_invalid = sys->viewport == NULL;
@@ -358,9 +512,21 @@ static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
     return VLC_SUCCESS;
 
 error:
+    if (sys->subsurface != NULL)
+        wl_subsurface_destroy(sys->subsurface);
+
+    if (sys->surface != NULL)
+        wl_surface_destroy(sys->surface);
+
     if (sys->viewporter != NULL)
         wp_viewporter_destroy(sys->viewporter);
 
+    if (sys->subcompositor != NULL)
+        wl_subcompositor_destroy(sys->subcompositor);
+
+    if (sys->compositor != NULL)
+        wl_compositor_destroy(sys->compositor);
+
     if (sys->shm != NULL)
         wl_shm_destroy(sys->shm);
 
@@ -374,7 +540,7 @@ static void Close(vout_display_t *vd)
 {
     vout_display_sys_t *sys = vd->sys;
     struct wl_display *display = sys->embed->display.wl;
-    struct wl_surface *surface = sys->embed->handle.wl;
+    struct wl_surface *surface = sys->surface;
 
     wl_surface_attach(surface, NULL, 0, 0);
     wl_surface_commit(surface);
@@ -388,9 +554,22 @@ static void Close(vout_display_t *vd)
 
     if (sys->viewport != NULL)
         wp_viewport_destroy(sys->viewport);
+
+    if (sys->back_bg.viewport)
+        wp_viewport_destroy(sys->back_bg.viewport);
+
+    if (sys->back_bg.buffer)
+        wl_buffer_destroy(sys->back_bg.buffer);
+
     if (sys->viewporter != NULL)
         wp_viewporter_destroy(sys->viewporter);
+
+    wl_subsurface_destroy(sys->subsurface);
+    wl_surface_destroy(sys->surface);
+    wl_subcompositor_destroy(sys->subcompositor);
+    wl_compositor_destroy(sys->compositor);
     wl_shm_destroy(sys->shm);
+
     wl_display_flush(display);
     wl_event_queue_destroy(sys->eventq);
     free(sys);
-- 
2.21.0



More information about the vlc-devel mailing list