gstwaylandsink: Add rotate-method property
authorRobert Mader <robert.mader@collabora.com>
Thu, 2 Jun 2022 12:22:21 +0000 (14:22 +0200)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 20 Jun 2022 18:30:56 +0000 (18:30 +0000)
Similar to and inspired by glimagesink and gtkglsink.

Using the Wayland buffer transform API allows to offload
rotate operations to the Wayland compositor. This can have
several advantages:
 - The Wayland compositor may be able to use hardware plane
   capabilities to do the rotation.
 - In case of pre-rotated content on rotated outputs the
   rotations may equal out, potentially allowing the
   compositor to use hardware planes even if they don't
   support rotate operations.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2543>

subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json
subprojects/gst-plugins-bad/ext/wayland/gstwaylandsink.c
subprojects/gst-plugins-bad/ext/wayland/gstwaylandsink.h
subprojects/gst-plugins-bad/gst-libs/gst/wayland/gstwlwindow.c
subprojects/gst-plugins-bad/gst-libs/gst/wayland/gstwlwindow.h

index 5855250..b0e7668 100644 (file)
                         "readable": false,
                         "type": "GstValueArray",
                         "writable": true
+                    },
+                    "rotate-method": {
+                        "blurb": "rotate method",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "identity (0)",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstVideoOrientationMethod",
+                        "writable": true
                     }
                 },
                 "rank": "marginal"
index 1a2eed2..87ed2b8 100644 (file)
@@ -60,6 +60,7 @@ enum
   PROP_0,
   PROP_DISPLAY,
   PROP_FULLSCREEN,
+  PROP_ROTATE_METHOD,
   PROP_LAST
 };
 
@@ -90,6 +91,7 @@ static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element,
 static void gst_wayland_sink_set_context (GstElement * element,
     GstContext * context);
 
+static gboolean gst_wayland_sink_event (GstBaseSink * sink, GstEvent * event);
 static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink,
     GstCaps * filter);
 static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
@@ -144,6 +146,7 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
   gstelement_class->set_context =
       GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context);
 
+  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_wayland_sink_event);
   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
   gstbasesink_class->propose_allocation =
@@ -162,6 +165,18 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
           "Whether the surface should be made fullscreen ", FALSE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * waylandsink:rotate-method:
+   *
+   * Since: 1.22
+   */
+  g_object_class_install_property (gobject_class, PROP_ROTATE_METHOD,
+      g_param_spec_enum ("rotate-method",
+          "rotate method",
+          "rotate method",
+          GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
  /**
   * waylandsink:render-rectangle:
   *
@@ -193,6 +208,43 @@ gst_wayland_sink_set_fullscreen (GstWaylandSink * sink, gboolean fullscreen)
 }
 
 static void
+gst_wayland_sink_set_rotate_method (GstWaylandSink * sink,
+    GstVideoOrientationMethod method, gboolean from_tag)
+{
+  GstVideoOrientationMethod new_method;
+
+  if (method == GST_VIDEO_ORIENTATION_CUSTOM) {
+    GST_WARNING_OBJECT (sink, "unsupported custom orientation");
+    return;
+  }
+
+  GST_OBJECT_LOCK (sink);
+  if (from_tag)
+    sink->tag_rotate_method = method;
+  else
+    sink->sink_rotate_method = method;
+
+  if (sink->sink_rotate_method == GST_VIDEO_ORIENTATION_AUTO)
+    new_method = sink->tag_rotate_method;
+  else
+    new_method = sink->sink_rotate_method;
+
+  if (new_method != sink->current_rotate_method) {
+    GST_DEBUG_OBJECT (sink, "Changing method from %d to %d",
+        sink->current_rotate_method, new_method);
+
+    if (sink->window) {
+      g_mutex_lock (&sink->render_lock);
+      gst_wl_window_set_rotate_method (sink->window, new_method);
+      g_mutex_unlock (&sink->render_lock);
+    }
+
+    sink->current_rotate_method = new_method;
+  }
+  GST_OBJECT_UNLOCK (sink);
+}
+
+static void
 gst_wayland_sink_get_property (GObject * object,
     guint prop_id, GValue * value, GParamSpec * pspec)
 {
@@ -209,6 +261,11 @@ gst_wayland_sink_get_property (GObject * object,
       g_value_set_boolean (value, sink->fullscreen);
       GST_OBJECT_UNLOCK (sink);
       break;
+    case PROP_ROTATE_METHOD:
+      GST_OBJECT_LOCK (sink);
+      g_value_set_enum (value, sink->current_rotate_method);
+      GST_OBJECT_UNLOCK (sink);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -232,6 +289,10 @@ gst_wayland_sink_set_property (GObject * object,
       gst_wayland_sink_set_fullscreen (sink, g_value_get_boolean (value));
       GST_OBJECT_UNLOCK (sink);
       break;
+    case PROP_ROTATE_METHOD:
+      gst_wayland_sink_set_rotate_method (sink, g_value_get_enum (value),
+          FALSE);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -420,6 +481,34 @@ gst_wayland_sink_set_context (GstElement * element, GstContext * context)
     GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
 }
 
+static gboolean
+gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event)
+{
+  GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
+  GstTagList *taglist;
+  GstVideoOrientationMethod method;
+  gboolean ret;
+
+  GST_DEBUG_OBJECT (sink, "handling %s event", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_TAG:
+      gst_event_parse_tag (event, &taglist);
+
+      if (gst_video_orientation_from_tag (taglist, &method)) {
+        gst_wayland_sink_set_rotate_method (sink, method, TRUE);
+      }
+
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
+
+  return ret;
+}
+
 static GstCaps *
 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
 {
@@ -679,6 +768,8 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
           &sink->video_info, sink->fullscreen, &sink->render_lock);
       g_signal_connect_object (sink->window, "closed",
           G_CALLBACK (on_window_closed), sink, 0);
+      gst_wl_window_set_rotate_method (sink->window,
+          sink->current_rotate_method);
     }
   }
 
@@ -922,6 +1013,8 @@ gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
       } else {
         sink->window = gst_wl_window_new_in_surface (sink->display, surface,
             &sink->render_lock);
+        gst_wl_window_set_rotate_method (sink->window,
+            sink->current_rotate_method);
       }
     } else {
       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
index f36c639..3243d8c 100644 (file)
@@ -63,6 +63,10 @@ struct _GstWaylandSink
   GMutex render_lock;
   GstBuffer *last_buffer;
 
+  GstVideoOrientationMethod sink_rotate_method;
+  GstVideoOrientationMethod tag_rotate_method;
+  GstVideoOrientationMethod current_rotate_method;
+
   struct wl_callback *callback;
 };
 
index d50dc6a..4aa53c3 100644 (file)
@@ -63,6 +63,8 @@ typedef struct _GstWlWindowPrivate
   /* the size of the video in the buffers */
   gint video_width, video_height;
 
+  enum wl_output_transform buffer_transform;
+
   /* when this is not set both the area_surface and the video_surface are not
    * visible and certain steps should be skipped */
   gboolean is_area_surface_mapped;
@@ -420,12 +422,27 @@ gst_wl_window_resize_video_surface (GstWlWindow * self, gboolean commit)
   GstVideoRectangle dst = { 0, };
   GstVideoRectangle res;
 
-  /* center the video_subsurface inside area_subsurface */
-  src.w = priv->video_width;
-  src.h = priv->video_height;
+  switch (priv->buffer_transform) {
+    case WL_OUTPUT_TRANSFORM_NORMAL:
+    case WL_OUTPUT_TRANSFORM_180:
+    case WL_OUTPUT_TRANSFORM_FLIPPED:
+    case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+      src.w = priv->video_width;
+      src.h = priv->video_height;
+      break;
+    case WL_OUTPUT_TRANSFORM_90:
+    case WL_OUTPUT_TRANSFORM_270:
+    case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+    case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+      src.w = priv->video_height;
+      src.h = priv->video_width;
+      break;
+  }
+
   dst.w = priv->render_rectangle.w;
   dst.h = priv->render_rectangle.h;
 
+  /* center the video_subsurface inside area_subsurface */
   if (priv->video_viewport) {
     gst_video_center_rect (&src, &dst, &res, TRUE);
     wp_viewport_set_destination (priv->video_viewport, res.w, res.h);
@@ -434,6 +451,8 @@ gst_wl_window_resize_video_surface (GstWlWindow * self, gboolean commit)
   }
 
   wl_subsurface_set_position (priv->video_subsurface, res.x, res.y);
+  wl_surface_set_buffer_transform (priv->video_surface_wrapper,
+      priv->buffer_transform);
 
   if (commit)
     wl_surface_commit (priv->video_surface_wrapper);
@@ -567,24 +586,16 @@ gst_wl_window_update_borders (GstWlWindow * self)
   g_object_unref (alloc);
 }
 
-void
-gst_wl_window_set_render_rectangle (GstWlWindow * self, gint x, gint y,
-    gint w, gint h)
+static void
+gst_wl_window_update_geometry (GstWlWindow * self)
 {
   GstWlWindowPrivate *priv = gst_wl_window_get_instance_private (self);
 
-  if (priv->render_rectangle.x == x && priv->render_rectangle.y == y &&
-      priv->render_rectangle.w == w && priv->render_rectangle.h == h)
-    return;
-
-  priv->render_rectangle.x = x;
-  priv->render_rectangle.y = y;
-  priv->render_rectangle.w = w;
-  priv->render_rectangle.h = h;
-
   /* position the area inside the parent - needs a parent commit to apply */
-  if (priv->area_subsurface)
-    wl_subsurface_set_position (priv->area_subsurface, x, y);
+  if (priv->area_subsurface) {
+    wl_subsurface_set_position (priv->area_subsurface, priv->render_rectangle.x,
+        priv->render_rectangle.y);
+  }
 
   if (priv->is_area_surface_mapped)
     gst_wl_window_update_borders (self);
@@ -603,6 +614,24 @@ gst_wl_window_set_render_rectangle (GstWlWindow * self, gint x, gint y,
     wl_subsurface_set_desync (priv->video_subsurface);
 }
 
+void
+gst_wl_window_set_render_rectangle (GstWlWindow * self, gint x, gint y,
+    gint w, gint h)
+{
+  GstWlWindowPrivate *priv = gst_wl_window_get_instance_private (self);
+
+  if (priv->render_rectangle.x == x && priv->render_rectangle.y == y &&
+      priv->render_rectangle.w == w && priv->render_rectangle.h == h)
+    return;
+
+  priv->render_rectangle.x = x;
+  priv->render_rectangle.y = y;
+  priv->render_rectangle.w = w;
+  priv->render_rectangle.h = h;
+
+  gst_wl_window_update_geometry (self);
+}
+
 const GstVideoRectangle *
 gst_wl_window_get_render_rectangle (GstWlWindow * self)
 {
@@ -610,3 +639,39 @@ gst_wl_window_get_render_rectangle (GstWlWindow * self)
 
   return &priv->render_rectangle;
 }
+
+static enum wl_output_transform
+output_transform_from_orientation_method (GstVideoOrientationMethod method)
+{
+  switch (method) {
+    case GST_VIDEO_ORIENTATION_IDENTITY:
+      return WL_OUTPUT_TRANSFORM_NORMAL;
+    case GST_VIDEO_ORIENTATION_90R:
+      return WL_OUTPUT_TRANSFORM_90;
+    case GST_VIDEO_ORIENTATION_180:
+      return WL_OUTPUT_TRANSFORM_180;
+    case GST_VIDEO_ORIENTATION_90L:
+      return WL_OUTPUT_TRANSFORM_270;
+    case GST_VIDEO_ORIENTATION_HORIZ:
+      return WL_OUTPUT_TRANSFORM_FLIPPED;
+    case GST_VIDEO_ORIENTATION_VERT:
+      return WL_OUTPUT_TRANSFORM_FLIPPED_180;
+    case GST_VIDEO_ORIENTATION_UL_LR:
+      return WL_OUTPUT_TRANSFORM_FLIPPED_90;
+    case GST_VIDEO_ORIENTATION_UR_LL:
+      return WL_OUTPUT_TRANSFORM_FLIPPED_270;
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+void
+gst_wl_window_set_rotate_method (GstWlWindow * self,
+    GstVideoOrientationMethod method)
+{
+  GstWlWindowPrivate *priv = gst_wl_window_get_instance_private (self);
+
+  priv->buffer_transform = output_transform_from_orientation_method (method);
+
+  gst_wl_window_update_geometry (self);
+}
index 78871d7..06c4001 100644 (file)
@@ -68,4 +68,8 @@ void gst_wl_window_set_render_rectangle (GstWlWindow * self, gint x, gint y,
 GST_WL_API
 const GstVideoRectangle *gst_wl_window_get_render_rectangle (GstWlWindow * self);
 
+GST_WL_API
+void gst_wl_window_set_rotate_method (GstWlWindow               *self,
+        GstVideoOrientationMethod  rotate_method);
+
 G_END_DECLS