waylandsink: stack the video subsurface into another subsurface that covers the whole...
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Tue, 1 Jul 2014 08:43:20 +0000 (11:43 +0300)
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Sat, 11 Oct 2014 12:57:14 +0000 (14:57 +0200)
The main reason behind this is that when the video caps change and the video
subsurface needs to resize and change position, the wl_subsurface.set_position
call needs a commit in its parent in order to take effect. Previously,
the parent was the application's surface, over which there is no control.
Now, the parent is inside the sink, so we can commit it and change size smoothly.

As a side effect, this also allows the sink to draw its black borders on
its own, without the need for the application to do that. And another side
effect is that this can now allow resizing the sink when it is in top-level
mode and have it respect the aspect ratio.

ext/wayland/gstwaylandsink.c
ext/wayland/wlbuffer.c
ext/wayland/wlbuffer.h
ext/wayland/wlvideoformat.h
ext/wayland/wlwindow.c
ext/wayland/wlwindow.h

index 0b9b899..363b31b 100644 (file)
@@ -333,11 +333,7 @@ gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
           g_clear_object (&sink->window);
         } else {
           /* remove buffer from surface, show nothing */
-          wl_surface_attach (sink->window->surface, NULL, 0, 0);
-          wl_surface_damage (sink->window->surface, 0, 0,
-              sink->window->surface_width, sink->window->surface_height);
-          wl_surface_commit (sink->window->surface);
-          wl_display_flush (sink->display->display);
+          gst_wl_window_render (sink->window, NULL, NULL);
         }
       }
       break;
@@ -558,6 +554,7 @@ static void
 render_last_buffer (GstWaylandSink * sink)
 {
   GstWlBuffer *wlbuffer;
+  const GstVideoInfo *info = NULL;
   struct wl_surface *surface;
   struct wl_callback *callback;
 
@@ -568,12 +565,11 @@ render_last_buffer (GstWaylandSink * sink)
   callback = wl_surface_frame (surface);
   wl_callback_add_listener (callback, &frame_callback_listener, sink);
 
-  gst_wl_buffer_attach (wlbuffer, sink->window);
-  wl_surface_damage (surface, 0, 0, sink->window->surface_width,
-      sink->window->surface_height);
-
-  wl_surface_commit (surface);
-  wl_display_flush (sink->display->display);
+  if (G_UNLIKELY (sink->video_info_changed)) {
+    info = &sink->video_info;
+    sink->video_info_changed = FALSE;
+  }
+  gst_wl_window_render (sink->window, wlbuffer, info);
 }
 
 static GstFlowReturn
@@ -595,33 +591,19 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
     g_mutex_lock (&sink->render_lock);
 
-    if (sink->window) {
-      /* inform the window about our caps */
-      gst_wl_window_set_video_info (sink->window, &sink->video_info);
-    } else {
+    if (!sink->window) {
       /* if we were not provided a window, create one ourselves */
       sink->window =
           gst_wl_window_new_toplevel (sink->display, &sink->video_info);
     }
-    sink->video_info_changed = FALSE;
   }
 
   /* drop buffers until we get a frame callback */
   if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
     goto done;
 
-  if (G_UNLIKELY (sink->video_info_changed)) {
-    gst_wl_window_set_video_info (sink->window, &sink->video_info);
-    sink->video_info_changed = FALSE;
-  }
-
-  /* now that we have for sure set the video info on the window, it must have
-   * a valid size, otherwise this means that the application has called
-   * set_window_handle() without calling set_render_rectangle(), which is
-   * absolutely necessary for us.
-   */
-  if (G_UNLIKELY (sink->window->surface_width == 0 ||
-          sink->window->surface_height == 0))
+  /* make sure that the application has called set_render_rectangle() */
+  if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
     goto no_window_size;
 
   wlbuffer = gst_buffer_get_wl_buffer (buffer);
@@ -822,14 +804,14 @@ gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
   g_return_if_fail (sink != NULL);
 
   g_mutex_lock (&sink->render_lock);
-  if (!sink->window || !sink->window->subsurface) {
+  if (!sink->window || !sink->window->area_subsurface) {
     g_mutex_unlock (&sink->render_lock);
     GST_INFO_OBJECT (sink,
         "begin_geometry_change called without window, ignoring");
     return;
   }
 
-  wl_subsurface_set_sync (sink->window->subsurface);
+  wl_subsurface_set_sync (sink->window->area_subsurface);
   g_mutex_unlock (&sink->render_lock);
 }
 
@@ -840,14 +822,14 @@ gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
   g_return_if_fail (sink != NULL);
 
   g_mutex_lock (&sink->render_lock);
-  if (!sink->window || !sink->window->subsurface) {
+  if (!sink->window || !sink->window->area_subsurface) {
     g_mutex_unlock (&sink->render_lock);
     GST_INFO_OBJECT (sink,
         "end_geometry_change called without window, ignoring");
     return;
   }
 
-  wl_subsurface_set_desync (sink->window->subsurface);
+  wl_subsurface_set_desync (sink->window->area_subsurface);
   g_mutex_unlock (&sink->render_lock);
 }
 
index c2bf320..d93fedb 100644 (file)
@@ -120,7 +120,7 @@ static const struct wl_buffer_listener buffer_listener = {
   buffer_release
 };
 
-void
+GstWlBuffer *
 gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer,
     GstWlDisplay * display)
 {
@@ -137,6 +137,8 @@ gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer,
 
   gst_mini_object_set_qdata ((GstMiniObject *) gstbuffer,
       gst_wl_buffer_qdata_quark (), self, g_object_unref);
+
+  return self;
 }
 
 GstWlBuffer *
@@ -172,11 +174,11 @@ gst_wl_buffer_force_release_and_unref (GstWlBuffer * self)
 }
 
 void
-gst_wl_buffer_attach (GstWlBuffer * self, GstWlWindow * target)
+gst_wl_buffer_attach (GstWlBuffer * self, struct wl_surface *surface)
 {
   g_return_if_fail (self->used_by_compositor == FALSE);
 
-  wl_surface_attach (target->surface, self->wlbuffer, 0, 0);
+  wl_surface_attach (surface, self->wlbuffer, 0, 0);
 
   /* Add a reference to the buffer. This represents the fact that
    * the compositor is using the buffer and it should not return
index b434df4..cbb50f7 100644 (file)
@@ -21,7 +21,7 @@
 #ifndef __GST_WL_BUFFER_H__
 #define __GST_WL_BUFFER_H__
 
-#include "wlwindow.h"
+#include "wldisplay.h"
 
 G_BEGIN_DECLS
 
@@ -54,13 +54,13 @@ struct _GstWlBufferClass
 
 GType gst_wl_buffer_get_type (void);
 
-void gst_buffer_add_wl_buffer (GstBuffer * gstbuffer,
+GstWlBuffer * gst_buffer_add_wl_buffer (GstBuffer * gstbuffer,
     struct wl_buffer * wlbuffer, GstWlDisplay * display);
 GstWlBuffer * gst_buffer_get_wl_buffer (GstBuffer * gstbuffer);
 
 void gst_wl_buffer_force_release_and_unref (GstWlBuffer * self);
 
-void gst_wl_buffer_attach (GstWlBuffer * self, GstWlWindow * target);
+void gst_wl_buffer_attach (GstWlBuffer * self, struct wl_surface *surface);
 
 G_END_DECLS
 
index 833b98d..e8ec7ae 100644 (file)
@@ -24,7 +24,7 @@
 #ifndef __GST_WL_VIDEO_FORMAT_H__
 #define __GST_WL_VIDEO_FORMAT_H__
 
-#include <wayland-client.h>
+#include <wayland-client-protocol.h>
 #include <gst/video/video.h>
 
 G_BEGIN_DECLS
index f58df09..a4da2c6 100644 (file)
@@ -25,6 +25,8 @@
 #endif
 
 #include "wlwindow.h"
+#include "wlshmallocator.h"
+#include "wlbuffer.h"
 
 GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug);
 #define GST_CAT_DEFAULT gstwayland_debug
@@ -78,12 +80,15 @@ gst_wl_window_finalize (GObject * gobject)
     wl_shell_surface_destroy (self->shell_surface);
   }
 
-  if (self->subsurface) {
-    wl_subsurface_destroy (self->subsurface);
-  }
+  wl_viewport_destroy (self->video_viewport);
+  wl_subsurface_destroy (self->video_subsurface);
+  wl_surface_destroy (self->video_surface);
 
-  wl_viewport_destroy (self->viewport);
-  wl_surface_destroy (self->surface);
+  if (self->area_subsurface) {
+    wl_subsurface_destroy (self->area_subsurface);
+  }
+  wl_viewport_destroy (self->area_viewport);
+  wl_surface_destroy (self->area_surface);
 
   g_clear_object (&self->display);
 
@@ -91,44 +96,84 @@ gst_wl_window_finalize (GObject * gobject)
 }
 
 static GstWlWindow *
-gst_wl_window_new_internal (GstWlDisplay * display, struct wl_surface *surface)
+gst_wl_window_new_internal (GstWlDisplay * display)
 {
   GstWlWindow *window;
+  GstVideoInfo info;
+  GstBuffer *buf;
+  GstMapInfo mapinfo;
+  struct wl_buffer *wlbuf;
+  GstWlBuffer *gwlbuf;
   struct wl_region *region;
 
-  g_return_val_if_fail (surface != NULL, NULL);
-
   window = g_object_new (GST_TYPE_WL_WINDOW, NULL);
   window->display = g_object_ref (display);
-  window->surface = surface;
 
-  /* make sure the surface runs on our local queue */
-  wl_proxy_set_queue ((struct wl_proxy *) surface, display->queue);
-
-  window->viewport = wl_scaler_get_viewport (display->scaler, window->surface);
+  window->area_surface = wl_compositor_create_surface (display->compositor);
+  window->video_surface = wl_compositor_create_surface (display->compositor);
+
+  wl_proxy_set_queue ((struct wl_proxy *) window->area_surface, display->queue);
+  wl_proxy_set_queue ((struct wl_proxy *) window->video_surface,
+      display->queue);
+
+  /* embed video_surface in area_surface */
+  window->video_subsurface =
+      wl_subcompositor_get_subsurface (display->subcompositor,
+      window->video_surface, window->area_surface);
+  wl_subsurface_set_desync (window->video_subsurface);
+
+  window->area_viewport = wl_scaler_get_viewport (display->scaler,
+      window->area_surface);
+  window->video_viewport = wl_scaler_get_viewport (display->scaler,
+      window->video_surface);
+
+  /* draw the area_subsurface */
+  gst_video_info_set_format (&info,
+      /* we want WL_SHM_FORMAT_XRGB8888 */
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+      GST_VIDEO_FORMAT_xRGB,
+#else
+      GST_VIDEO_FORMAT_BGRx,
+#endif
+      1, 1);
+
+  buf = gst_buffer_new_allocate (gst_wl_shm_allocator_get (), info.size, NULL);
+  gst_buffer_map (buf, &mapinfo, GST_MAP_WRITE);
+  *((guint32 *) mapinfo.data) = 0;      /* paint it black */
+  gst_buffer_unmap (buf, &mapinfo);
+  wlbuf =
+      gst_wl_shm_memory_construct_wl_buffer (gst_buffer_peek_memory (buf, 0),
+      display, &info);
+  gwlbuf = gst_buffer_add_wl_buffer (buf, wlbuf, display);
+  gst_wl_buffer_attach (gwlbuf, window->area_surface);
+
+  /* at this point, the GstWlBuffer keeps the buffer
+   * alive and will free it on wl_buffer::release */
+  gst_buffer_unref (buf);
 
   /* do not accept input */
   region = wl_compositor_create_region (display->compositor);
-  wl_surface_set_input_region (surface, region);
+  wl_surface_set_input_region (window->area_surface, region);
+  wl_region_destroy (region);
+
+  region = wl_compositor_create_region (display->compositor);
+  wl_surface_set_input_region (window->video_surface, region);
   wl_region_destroy (region);
 
   return window;
 }
 
 GstWlWindow *
-gst_wl_window_new_toplevel (GstWlDisplay * display, GstVideoInfo * video_info)
+gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info)
 {
   GstWlWindow *window;
+  gint width;
 
-  window = gst_wl_window_new_internal (display,
-      wl_compositor_create_surface (display->compositor));
-
-  gst_wl_window_set_video_info (window, video_info);
-  gst_wl_window_set_render_rectangle (window, 0, 0, window->video_width,
-      window->video_height);
+  window = gst_wl_window_new_internal (display);
 
+  /* go toplevel */
   window->shell_surface = wl_shell_get_shell_surface (display->shell,
-      window->surface);
+      window->area_surface);
 
   if (window->shell_surface) {
     wl_shell_surface_add_listener (window->shell_surface,
@@ -141,6 +186,11 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, GstVideoInfo * video_info)
     return NULL;
   }
 
+  /* set the initial size to be the same as the reported video size */
+  width =
+      gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d);
+  gst_wl_window_set_render_rectangle (window, 0, 0, width, info->height);
+
   return window;
 }
 
@@ -149,13 +199,13 @@ gst_wl_window_new_in_surface (GstWlDisplay * display,
     struct wl_surface * parent)
 {
   GstWlWindow *window;
+  window = gst_wl_window_new_internal (display);
 
-  window = gst_wl_window_new_internal (display,
-      wl_compositor_create_surface (display->compositor));
-
-  window->subsurface = wl_subcompositor_get_subsurface (display->subcompositor,
-      window->surface, parent);
-  wl_subsurface_set_desync (window->subsurface);
+  /* embed in parent */
+  window->area_subsurface =
+      wl_subcompositor_get_subsurface (display->subcompositor,
+      window->area_surface, parent);
+  wl_subsurface_set_desync (window->area_subsurface);
 
   return window;
 }
@@ -173,7 +223,7 @@ gst_wl_window_get_wl_surface (GstWlWindow * window)
 {
   g_return_val_if_fail (window != NULL, NULL);
 
-  return window->surface;
+  return window->video_surface;
 }
 
 gboolean
@@ -185,22 +235,21 @@ gst_wl_window_is_toplevel (GstWlWindow * window)
 }
 
 static void
-gst_wl_window_resize_internal (GstWlWindow * window, gboolean commit)
+gst_wl_window_resize_video_surface (GstWlWindow * window, gboolean commit)
 {
   GstVideoRectangle src, res;
 
+  /* center the video_subsurface inside area_subsurface */
   src.w = window->video_width;
   src.h = window->video_height;
   gst_video_sink_center_rect (src, window->render_rectangle, &res, TRUE);
 
-  if (window->subsurface)
-    wl_subsurface_set_position (window->subsurface,
-        window->render_rectangle.x + res.x, window->render_rectangle.y + res.y);
-  wl_viewport_set_destination (window->viewport, res.w, res.h);
+  wl_subsurface_set_position (window->video_subsurface, res.x, res.y);
+  wl_viewport_set_destination (window->video_viewport, res.w, res.h);
 
   if (commit) {
-    wl_surface_damage (window->surface, 0, 0, res.w, res.h);
-    wl_surface_commit (window->surface);
+    wl_surface_damage (window->video_surface, 0, 0, res.w, res.h);
+    wl_surface_commit (window->video_surface);
   }
 
   /* this is saved for use in wl_surface_damage */
@@ -209,16 +258,37 @@ gst_wl_window_resize_internal (GstWlWindow * window, gboolean commit)
 }
 
 void
-gst_wl_window_set_video_info (GstWlWindow * window, GstVideoInfo * info)
+gst_wl_window_render (GstWlWindow * window, GstWlBuffer * buffer,
+    const GstVideoInfo * info)
 {
-  g_return_if_fail (window != NULL);
+  if (G_UNLIKELY (info)) {
+    window->video_width =
+        gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d);
+    window->video_height = info->height;
 
-  window->video_width =
-      gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d);
-  window->video_height = info->height;
+    wl_subsurface_set_sync (window->video_subsurface);
+    gst_wl_window_resize_video_surface (window, FALSE);
+  }
+
+  if (G_LIKELY (buffer))
+    gst_wl_buffer_attach (buffer, window->video_surface);
+  else
+    wl_surface_attach (window->video_surface, NULL, 0, 0);
+
+  wl_surface_damage (window->video_surface, 0, 0, window->surface_width,
+      window->surface_height);
+  wl_surface_commit (window->video_surface);
+
+  if (G_UNLIKELY (info)) {
+    /* commit also the parent (area_surface) in order to change
+     * the position of the video_subsurface */
+    wl_surface_damage (window->area_surface, 0, 0, window->render_rectangle.w,
+        window->render_rectangle.h);
+    wl_surface_commit (window->area_surface);
+    wl_subsurface_set_desync (window->video_subsurface);
+  }
 
-  if (window->render_rectangle.w != 0)
-    gst_wl_window_resize_internal (window, FALSE);
+  wl_display_flush (window->display->display);
 }
 
 void
@@ -232,6 +302,21 @@ gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y,
   window->render_rectangle.w = w;
   window->render_rectangle.h = h;
 
+  /* position the area inside the parent - needs a parent commit to apply */
+  if (window->area_subsurface)
+    wl_subsurface_set_position (window->area_subsurface, x, y);
+
+  /* change the size of the area */
+  wl_viewport_set_destination (window->area_viewport, w, h);
+
+  if (window->video_width != 0) {
+    wl_subsurface_set_sync (window->video_subsurface);
+    gst_wl_window_resize_video_surface (window, TRUE);
+  }
+
+  wl_surface_damage (window->area_surface, 0, 0, w, h);
+  wl_surface_commit (window->area_surface);
+
   if (window->video_width != 0)
-    gst_wl_window_resize_internal (window, TRUE);
+    wl_subsurface_set_desync (window->video_subsurface);
 }
index f4cb360..e22cb26 100644 (file)
@@ -22,6 +22,7 @@
 #define __GST_WL_WINDOW_H__
 
 #include "wldisplay.h"
+#include "wlbuffer.h"
 #include <gst/video/video.h>
 
 G_BEGIN_DECLS
@@ -41,16 +42,19 @@ struct _GstWlWindow
   GObject parent_instance;
 
   GstWlDisplay *display;
-  struct wl_surface *surface;
-  struct wl_subsurface *subsurface;
-  struct wl_viewport *viewport;
+  struct wl_surface *area_surface;
+  struct wl_subsurface *area_subsurface;
+  struct wl_viewport *area_viewport;
+  struct wl_surface *video_surface;
+  struct wl_subsurface *video_subsurface;
+  struct wl_viewport *video_viewport;
   struct wl_shell_surface *shell_surface;
 
-  /* the size of the destination area where we are overlaying our subsurface */
+  /* the size and position of the area_(sub)surface */
   GstVideoRectangle render_rectangle;
   /* the size of the video in the buffers */
   gint video_width, video_height;
-  /* the size of the (sub)surface */
+  /* the size of the video_(sub)surface */
   gint surface_width, surface_height;
 };
 
@@ -62,7 +66,7 @@ struct _GstWlWindowClass
 GType gst_wl_window_get_type (void);
 
 GstWlWindow *gst_wl_window_new_toplevel (GstWlDisplay * display,
-        GstVideoInfo * video_info);
+        const GstVideoInfo * info);
 GstWlWindow *gst_wl_window_new_in_surface (GstWlDisplay * display,
         struct wl_surface * parent);
 
@@ -70,8 +74,8 @@ GstWlDisplay *gst_wl_window_get_display (GstWlWindow * window);
 struct wl_surface *gst_wl_window_get_wl_surface (GstWlWindow * window);
 gboolean gst_wl_window_is_toplevel (GstWlWindow *window);
 
-/* functions to manipulate the size on non-toplevel windows */
-void gst_wl_window_set_video_info (GstWlWindow * window, GstVideoInfo * info);
+void gst_wl_window_render (GstWlWindow * window, GstWlBuffer * buffer,
+        const GstVideoInfo * info);
 void gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y,
         gint w, gint h);