1From c4e12604a6a59a81ea8ba49e89eb52060987b8a0 Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Wed, 28 Nov 2018 21:31:49 +0800
4Subject: [PATCH 03/17] gsttools: videooverlay: Support waylandsink and kmssink
5
6Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
7---
8 src/gsttools/qgstreamervideooverlay.cpp | 181 ++++++++++++++++++++----
9 src/gsttools/qgstreamervideooverlay_p.h |   2 +
10 src/gsttools/qgstreamervideowidget.cpp  |  16 ++-
11 3 files changed, 170 insertions(+), 29 deletions(-)
12
13diff --git a/src/gsttools/qgstreamervideooverlay.cpp b/src/gsttools/qgstreamervideooverlay.cpp
14index d410be7..8d2df0a 100644
15--- a/src/gsttools/qgstreamervideooverlay.cpp
16+++ b/src/gsttools/qgstreamervideooverlay.cpp
17@@ -39,8 +39,13 @@
18
19 #include "qgstreamervideooverlay_p.h"
20
21+#include <QtCore/qthread.h>
22 #include <QtGui/qguiapplication.h>
23+#include <QtGui/qwindow.h>
24+#include <QtGui/qpa/qplatformwindow.h>
25+#include <QtGui/qpa/qplatformnativeinterface.h>
26 #include "qgstutils_p.h"
27+#include "qdebug.h"
28
29 #if !GST_CHECK_VERSION(1,0,0)
30 #include <gst/interfaces/xoverlay.h>
31@@ -48,6 +53,10 @@
32 #include <gst/video/videooverlay.h>
33 #endif
34
35+#ifdef ENABLE_WAYLAND_PLATFORM
36+#include <wayland-client-protocol.h>
37+#endif
38+
39 #include <QtMultimedia/private/qtmultimediaglobal_p.h>
40
41 QT_BEGIN_NAMESPACE
42@@ -445,29 +454,127 @@ QSize QGstreamerVideoOverlay::nativeVideoSize() const
43
44 void QGstreamerVideoOverlay::setWindowHandle(WId id)
45 {
46+#ifndef ENABLE_WAYLAND_PLATFORM
47+    if (m_windowId == id)
48+        return;
49+#endif
50+
51     m_windowId = id;
52
53-    if (isActive())
54-        setWindowHandle_helper(id);
55+    setWindowHandle_helper(id);
56+}
57+
58+static QWindow *findWindow(WId id) {
59+    const auto allWindows = QGuiApplication::allWindows();
60+    for (QWindow *window : allWindows)
61+        if (window->winId() == id)
62+            return window;
63+
64+    return NULL;
65+}
66+
67+static QWindow *getVideoWindow(WId id) {
68+    QWindow *window = findWindow(id);
69+
70+    QVideoWindowAbstractInterface *intf =
71+        dynamic_cast<QVideoWindowAbstractInterface *>(window);
72+
73+    return intf ? findWindow(intf->videoWinId()) : window;
74 }
75
76 void QGstreamerVideoOverlay::setWindowHandle_helper(WId id)
77 {
78-#if GST_CHECK_VERSION(1,0,0)
79-    if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) {
80-        gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), id);
81-#else
82-    if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) {
83-# if GST_CHECK_VERSION(0,10,31)
84-        gst_x_overlay_set_window_handle(GST_X_OVERLAY(m_videoSink), id);
85-# else
86-        gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), id);
87-# endif
88+#ifdef ENABLE_WAYLAND_PLATFORM
89+    QPlatformNativeInterface *native =
90+            QGuiApplication::platformNativeInterface();
91+    wl_surface *surface = NULL;
92+    wl_compositor *compositor = NULL;
93+    QWindow *window;
94+#endif
95+    GstVideoOverlay *overlay;
96+    QPoint position;
97+
98+    bool main_thread =
99+        QThread::currentThread() == QGuiApplication::instance()->thread();
100+
101+#if !GST_CHECK_VERSION(1,0,0)
102+    qWarning("Only support gstreamer-1.0\n");
103+    goto out;
104+#endif
105+
106+    if (!m_videoSink || !GST_IS_VIDEO_OVERLAY(m_videoSink))
107+        goto out;
108+
109+    overlay = GST_VIDEO_OVERLAY(m_videoSink);
110+
111+#ifdef ENABLE_WAYLAND_PLATFORM
112+    window = (id && main_thread) ? getVideoWindow(id) : NULL;
113+    if (!window) {
114+        gst_video_overlay_set_window_handle(overlay, 0);
115+        goto set_rectangle;
116+    }
117+
118+    // HACK: Force updating decoration
119+    window->setFlags(window->flags());
120+
121+    id = window->winId();
122 #endif
123
124-        // Properties need to be reset when changing the winId.
125-        m_sinkProperties->reset();
126+#ifdef ENABLE_WAYLAND_PLATFORM
127+    if (native) {
128+        surface = (wl_surface*) native->nativeResourceForWindow("surface", window);
129+        compositor = (wl_compositor*) native->nativeResourceForWindow("compositor", window);
130     }
131+
132+    // It's wayland platform, using wl_surface as window handle.
133+    if (compositor) {
134+        if (!isActive())
135+            surface = NULL;
136+
137+        gst_video_overlay_set_window_handle(overlay, (WId) surface);
138+
139+        if (m_rect.width() <= 0 || m_rect.height() <= 0)
140+            goto out;
141+
142+        position = QPoint(window->frameMargins().left(),
143+                          window->frameMargins().top());
144+
145+        // HACK: kmssink is using global position
146+        if (strstr(GST_ELEMENT_NAME(m_videoSink), "kmssink"))
147+            position += window->geometry().topLeft();
148+
149+        gst_video_overlay_set_render_rectangle(overlay,
150+                                               position.x() + m_rect.x(),
151+                                               position.y() + m_rect.y(),
152+                                               m_rect.width(), m_rect.height());
153+
154+        if (!surface)
155+            goto out;
156+
157+        // HACK: Tell wayland server about the video rectangle
158+        struct wl_region *region = wl_compositor_create_region(compositor);
159+        wl_region_add(region, position.x() + m_rect.x(),
160+                      position.y() + m_rect.y(),
161+                      m_rect.width(), m_rect.height());
162+        wl_region_add(region, -1, -1, 1, 1);
163+        wl_surface_set_opaque_region(surface, region);
164+        wl_region_destroy(region);
165+        wl_surface_set_opaque_region(surface, NULL);
166+
167+        goto out;
168+    }
169+#endif // ENABLE_WAYLAND_PLATFORM
170+
171+    gst_video_overlay_set_window_handle(overlay, id);
172+
173+set_rectangle:
174+    if (m_rect.width() > 0 && m_rect.height() > 0)
175+        gst_video_overlay_set_render_rectangle(overlay,
176+            m_rect.x(), m_rect.y(), m_rect.width(), m_rect.height());
177+
178+out:
179+    // Properties need to be reset when changing the winId.
180+    m_sinkProperties->reset();
181 }
182
183 void QGstreamerVideoOverlay::expose()
184@@ -499,24 +606,45 @@ void QGstreamerVideoOverlay::setRenderRectangle(const QRect &rect)
185         h = rect.height();
186     }
187
188-#if GST_CHECK_VERSION(1,0,0)
189-    if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink))
190-        gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink), x, y, w, h);
191-#elif GST_CHECK_VERSION(0, 10, 29)
192-    if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink))
193-        gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(m_videoSink), x, y , w , h);
194-#else
195-    Q_UNUSED(x)
196-    Q_UNUSED(y)
197-    Q_UNUSED(w)
198-    Q_UNUSED(h)
199-#endif
200+    m_rect = QRect(x, y, w, h);
201+
202+    setWindowHandle_helper(m_windowId);
203 }
204
205 bool QGstreamerVideoOverlay::processSyncMessage(const QGstreamerMessage &message)
206 {
207     GstMessage* gm = message.rawMessage();
208
209+#if GST_CHECK_VERSION(1,0,0)
210+
211+#ifdef ENABLE_WAYLAND_PLATFORM
212+#define GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE "GstWaylandDisplayHandleContextType"
213+#define GST_WL_DISPLAY_HANDLE_CONTEXT_TYPE "GstWlDisplayHandleContextType"
214+    if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_NEED_CONTEXT)) {
215+        const gchar *type = NULL;
216+
217+        if (gst_message_parse_context_type(gm, &type) &&
218+                (!g_strcmp0(type, GST_WL_DISPLAY_HANDLE_CONTEXT_TYPE) ||
219+                 !g_strcmp0(type, GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE))) {
220+            GstContext *context =
221+                gst_context_new(type, TRUE);
222+            QPlatformNativeInterface *native =
223+                QGuiApplication::platformNativeInterface();
224+            void *handle = NULL;
225+
226+            if (native)
227+                handle = native->nativeResourceForWindow("display", NULL);
228+
229+            gst_structure_set(gst_context_writable_structure(context),
230+                    "handle", G_TYPE_POINTER, handle, NULL);
231+            gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(gm)), context);
232+            gst_context_unref(context);
233+            return true;
234+        }
235+    }
236+#endif
237+#endif
238+
239 #if !GST_CHECK_VERSION(1,0,0)
240     if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) &&
241             gst_structure_has_name(gm->structure, "prepare-xwindow-id")) {
242@@ -574,6 +702,7 @@ void QGstreamerVideoOverlay::updateIsActive()
243     if (newIsActive != m_isActive) {
244         m_isActive = newIsActive;
245         emit activeChanged();
246+        setWindowHandle_helper(m_windowId);
247     }
248 }
249
250diff --git a/src/gsttools/qgstreamervideooverlay_p.h b/src/gsttools/qgstreamervideooverlay_p.h
251index f2ca8a2..32b3d93 100644
252--- a/src/gsttools/qgstreamervideooverlay_p.h
253+++ b/src/gsttools/qgstreamervideooverlay_p.h
254@@ -54,6 +54,7 @@
255 #include <private/qgstreamerbushelper_p.h>
256 #include <private/qgstreamerbufferprobe_p.h>
257 #include <QtGui/qwindowdefs.h>
258+#include <QtCore/qrect.h>
259 #include <QtCore/qsize.h>
260
261 QT_BEGIN_NAMESPACE
262@@ -119,6 +120,7 @@ private:
263
264     QGstreamerSinkProperties *m_sinkProperties = nullptr;
265     WId m_windowId = 0;
266+    QRect m_rect;
267 };
268
269 QT_END_NAMESPACE
270diff --git a/src/gsttools/qgstreamervideowidget.cpp b/src/gsttools/qgstreamervideowidget.cpp
271index 55f9157..95bfe6e 100644
272--- a/src/gsttools/qgstreamervideowidget.cpp
273+++ b/src/gsttools/qgstreamervideowidget.cpp
274@@ -43,10 +43,11 @@
275 #include <QtCore/qdebug.h>
276 #include <QtGui/qevent.h>
277 #include <QtGui/qpainter.h>
278+#include <QtGui/qwindow.h>
279
280 QT_BEGIN_NAMESPACE
281
282-class QGstreamerVideoWidget : public QWidget
283+class QGstreamerVideoWidget : public QWidget, public QVideoWindowAbstractInterface
284 {
285 public:
286     QGstreamerVideoWidget(QGstreamerVideoOverlay *overlay, QWidget *parent = 0)
287@@ -85,6 +86,15 @@ public:
288         painter.fillRect(rect(), palette().window());
289     }
290
291+    WId videoWinId() const Q_DECL_OVERRIDE {
292+#ifdef ENABLE_WAYLAND_PLATFORM
293+        QWidget *parent = parentWidget();
294+        if (parent)
295+            return parent->winId() ?: parent->internalWinId();
296+#endif
297+        return winId() ?: internalWinId();
298+    }
299+
300 protected:
301     void paintEvent(QPaintEvent *) override
302     {
303@@ -131,7 +141,7 @@ void QGstreamerVideoWidgetControl::createVideoWidget()
304     m_widget = new QGstreamerVideoWidget(&m_videoOverlay);
305
306     m_widget->installEventFilter(this);
307-    m_videoOverlay.setWindowHandle(m_windowId = m_widget->winId());
308+    m_videoOverlay.setWindowHandle(m_windowId = m_widget->videoWinId());
309 }
310
311 GstElement *QGstreamerVideoWidgetControl::videoSink()
312@@ -171,7 +181,7 @@ bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e)
313 {
314     if (m_widget && object == m_widget) {
315         if (e->type() == QEvent::ParentChange || e->type() == QEvent::Show || e->type() == QEvent::WinIdChange) {
316-            WId newWId = m_widget->winId();
317+            WId newWId = m_widget->videoWinId();
318             if (newWId != m_windowId)
319                 m_videoOverlay.setWindowHandle(m_windowId = newWId);
320         }
321--
3222.20.1
323
324