1From 3241f7b42c92410f9d667ed26e5f65dbcb8b2c92 Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Tue, 30 Mar 2021 07:08:06 +0800
4Subject: [PATCH 20/33] waylandsink: Support window fill-mode property
5
6Tested with:
7gst-launch-1.0 videotestsrc ! waylandsink fullscreen=1 fill-mode=crop
8
9Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
10---
11 ext/wayland/gstwaylandsink.c | 55 ++++++++++++++++++++++++++++++++++++
12 ext/wayland/gstwaylandsink.h |  1 +
13 ext/wayland/wlwindow.c       | 28 +++++++++++++++++-
14 ext/wayland/wlwindow.h       |  9 ++++++
15 4 files changed, 92 insertions(+), 1 deletion(-)
16
17diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
18index ff79ddf..24fd7bd 100644
19--- a/ext/wayland/gstwaylandsink.c
20+++ b/ext/wayland/gstwaylandsink.c
21@@ -66,9 +66,12 @@ enum
22   PROP_FULLSCREEN,
23   PROP_LAYER,
24   PROP_ALPHA,
25+  PROP_FILL_MODE,
26   PROP_LAST
27 };
28
29+static GstWlWindowFillMode DEFAULT_FILL_MODE = GST_WL_WINDOW_FIT;
30+
31 GST_DEBUG_CATEGORY (gstwayland_debug);
32 #define GST_CAT_DEFAULT gstwayland_debug
33
34@@ -185,6 +188,24 @@ gst_wl_window_layer_get_type (void)
35   return layer;
36 }
37
38+#define GST_TYPE_WL_WINDOW_FILL_MODE (gst_wl_window_fill_mode_get_type ())
39+static GType
40+gst_wl_window_fill_mode_get_type (void)
41+{
42+  static GType mode = 0;
43+
44+  if (!mode) {
45+    static const GEnumValue modes[] = {
46+      {GST_WL_WINDOW_STRETCH, "Ignore aspect ratio", "stretch"},
47+      {GST_WL_WINDOW_FIT, "Keep aspect ratio", "fit"},
48+      {GST_WL_WINDOW_CROP, "Keep aspect ratio by expanding", "crop"},
49+      {0, NULL, NULL}
50+    };
51+    mode = g_enum_register_static ("GstWlWindowFillMode", modes);
52+  }
53+  return mode;
54+}
55+
56 static void
57 gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
58 {
59@@ -244,6 +265,15 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
60           "Wayland window alpha", 0.0, 1.0, 1.0,
61           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
62
63+  if (g_getenv ("WAYLANDSINK_STRETCH"))
64+    DEFAULT_FILL_MODE = GST_WL_WINDOW_STRETCH;
65+
66+  g_object_class_install_property (gobject_class, PROP_FILL_MODE,
67+      g_param_spec_enum ("fill-mode", "Window fill mode",
68+          "Wayland window fill mode",
69+          GST_TYPE_WL_WINDOW_FILL_MODE, DEFAULT_FILL_MODE,
70+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
71+
72   gst_video_overlay_install_properties (gobject_class, PROP_LAST);
73
74   gst_type_mark_as_plugin_api (GST_TYPE_WAYLAND_VIDEO, 0);
75@@ -258,6 +288,7 @@ gst_wayland_sink_init (GstWaylandSink * sink)
76   sink->window_handle = 1;
77   sink->layer = GST_WL_WINDOW_LAYER_NORMAL;
78   sink->alpha = 1.0;
79+  sink->fill_mode = DEFAULT_FILL_MODE;
80 }
81
82 static void
83@@ -296,6 +327,19 @@ gst_wayland_sink_set_alpha (GstWaylandSink * sink, gdouble alpha)
84   g_mutex_unlock (&sink->render_lock);
85 }
86
87+static void
88+gst_wayland_sink_set_fill_mode (GstWaylandSink * sink,
89+    GstWlWindowFillMode fill_mode)
90+{
91+  if (fill_mode == sink->fill_mode)
92+    return;
93+
94+  g_mutex_lock (&sink->render_lock);
95+  sink->fill_mode = fill_mode;
96+  sink->resend_info = FALSE;
97+  g_mutex_unlock (&sink->render_lock);
98+}
99+
100 static void
101 gst_wayland_sink_get_property (GObject * object,
102     guint prop_id, GValue * value, GParamSpec * pspec)
103@@ -323,6 +367,11 @@ gst_wayland_sink_get_property (GObject * object,
104       g_value_set_double (value, sink->alpha);
105       GST_OBJECT_UNLOCK (sink);
106       break;
107+    case PROP_FILL_MODE:
108+      GST_OBJECT_LOCK (sink);
109+      g_value_set_enum (value, sink->fill_mode);
110+      GST_OBJECT_UNLOCK (sink);
111+      break;
112     default:
113       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
114       break;
115@@ -356,6 +405,11 @@ gst_wayland_sink_set_property (GObject * object,
116       gst_wayland_sink_set_alpha (sink, g_value_get_double (value));
117       GST_OBJECT_UNLOCK (sink);
118       break;
119+    case PROP_FILL_MODE:
120+      GST_OBJECT_LOCK (sink);
121+      gst_wayland_sink_set_fill_mode (sink, g_value_get_enum (value));
122+      GST_OBJECT_UNLOCK (sink);
123+      break;
124     default:
125       if (!gst_video_overlay_set_property (object, PROP_LAST, prop_id, value))
126         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127@@ -763,6 +817,7 @@ render_last_buffer (GstWaylandSink * sink, gboolean redraw)
128     sink->video_info_changed = FALSE;
129     sink->resend_info = FALSE;
130   }
131+  sink->window->fill_mode = sink->fill_mode;
132   gst_wl_window_render (sink->window, wlbuffer, info);
133 }
134
135diff --git a/ext/wayland/gstwaylandsink.h b/ext/wayland/gstwaylandsink.h
136index f798969..6fd77e8 100644
137--- a/ext/wayland/gstwaylandsink.h
138+++ b/ext/wayland/gstwaylandsink.h
139@@ -64,6 +64,7 @@ struct _GstWaylandSink
140   gboolean fullscreen;
141   GstWlWindowLayer layer;
142   gdouble alpha;
143+  GstWlWindowFillMode fill_mode;
144
145   gchar *display_name;
146
147diff --git a/ext/wayland/wlwindow.c b/ext/wayland/wlwindow.c
148index 58e65dd..f4e7ca9 100644
149--- a/ext/wayland/wlwindow.c
150+++ b/ext/wayland/wlwindow.c
151@@ -467,7 +467,33 @@ gst_wl_window_resize_video_surface (GstWlWindow * window, gboolean commit)
152   dst.h = window->render_rectangle.h;
153
154   if (window->video_viewport) {
155-    gst_video_sink_center_rect (src, dst, &res, TRUE);
156+    if (window->fill_mode == GST_WL_WINDOW_STRETCH) {
157+      res = dst;
158+    } else if (window->fill_mode == GST_WL_WINDOW_FIT) {
159+      gst_video_sink_center_rect (src, dst, &res, TRUE);
160+    } else if (window->fill_mode == GST_WL_WINDOW_CROP) {
161+      gdouble src_ratio, dst_ratio;
162+
163+      src_ratio = (gdouble) src.w / src.h;
164+      dst_ratio = (gdouble) dst.w / dst.h;
165+
166+      if (src_ratio < dst_ratio) {
167+        int h = src.w / dst_ratio;
168+        src.y = (src.h - h) / 2;
169+        src.h = h;
170+      } else if (src_ratio > dst_ratio) {
171+        int w = src.h * dst_ratio;
172+        src.x = (src.w - w) / 2;
173+        src.w = w;
174+      }
175+
176+      wp_viewport_set_source (window->video_viewport,
177+          wl_fixed_from_int (src.x), wl_fixed_from_int (src.y),
178+          wl_fixed_from_int (src.w), wl_fixed_from_int (src.h));
179+
180+      res = dst;
181+    }
182+
183     wp_viewport_set_destination (window->video_viewport, res.w, res.h);
184   } else {
185     gst_video_sink_center_rect (src, dst, &res, FALSE);
186diff --git a/ext/wayland/wlwindow.h b/ext/wayland/wlwindow.h
187index 6fb8285..35d9d3c 100644
188--- a/ext/wayland/wlwindow.h
189+++ b/ext/wayland/wlwindow.h
190@@ -37,6 +37,13 @@ G_BEGIN_DECLS
191 typedef struct _GstWlWindow GstWlWindow;
192 typedef struct _GstWlWindowClass GstWlWindowClass;
193
194+typedef enum
195+{
196+  GST_WL_WINDOW_STRETCH = 0,
197+  GST_WL_WINDOW_FIT = 1,
198+  GST_WL_WINDOW_CROP = 2,
199+} GstWlWindowFillMode;
200+
201 struct _GstWlWindow
202 {
203   GObject parent_instance;
204@@ -71,6 +78,8 @@ struct _GstWlWindow
205   /* when this is not set both the area_surface and the video_surface are not
206    * visible and certain steps should be skipped */
207   gboolean is_area_surface_mapped;
208+
209+  GstWlWindowFillMode fill_mode;
210 };
211
212 struct _GstWlWindowClass
213--
2142.20.1
215
216