mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
gstwaylandsink: Add rotate-method property
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>
This commit is contained in:
parent
5fc953e838
commit
6aa0b0cae2
5 changed files with 195 additions and 17 deletions
|
@ -230601,6 +230601,18 @@
|
|||
"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"
|
||||
|
|
|
@ -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:
|
||||
*
|
||||
|
@ -192,6 +207,43 @@ gst_wayland_sink_set_fullscreen (GstWaylandSink * sink, gboolean fullscreen)
|
|||
g_mutex_unlock (&sink->render_lock);
|
||||
}
|
||||
|
||||
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, "
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue