1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qgstreamervideooverlay_p.h"
5
6#include <QtGui/qguiapplication.h>
7#include "qgstutils_p.h"
8#include "qgst_p.h"
9#include "qgstreamermessage_p.h"
10#include "qgstreamervideosink_p.h"
11
12#include <gst/video/videooverlay.h>
13
14#include <QtMultimedia/private/qtmultimediaglobal_p.h>
15
16QT_BEGIN_NAMESPACE
17
18struct ElementMap
19{
20 const char *qtPlatform;
21 const char *gstreamerElement;
22};
23
24// Ordered by descending priority
25static constexpr ElementMap elementMap[] =
26{
27 { .qtPlatform: "xcb", .gstreamerElement: "xvimagesink" },
28 { .qtPlatform: "xcb", .gstreamerElement: "ximagesink" },
29
30 // wayland
31 { .qtPlatform: "wayland", .gstreamerElement: "waylandsink" }
32};
33
34static bool qt_gst_element_is_functioning(QGstElement element)
35{
36 GstStateChangeReturn ret = element.setState(GST_STATE_READY);
37 if (ret == GST_STATE_CHANGE_SUCCESS) {
38 element.setState(GST_STATE_NULL);
39 return true;
40 }
41
42 return false;
43}
44
45static QGstElement findBestVideoSink()
46{
47 QString platform = QGuiApplication::platformName();
48
49 // First, try some known video sinks, depending on the Qt platform plugin in use.
50 for (auto i : elementMap) {
51 if (platform != QLatin1String(i.qtPlatform))
52 continue;
53 QGstElement choice(i.gstreamerElement, i.gstreamerElement);
54 if (choice.isNull())
55 continue;
56
57 if (qt_gst_element_is_functioning(element: choice))
58 return choice;
59 }
60
61 // We need a native window ID to use the GstVideoOverlay interface.
62 // Bail out if the Qt platform plugin in use cannot provide a sensible WId.
63 if (platform != QLatin1String("xcb") && platform != QLatin1String("wayland"))
64 return {};
65
66 QGstElement choice;
67 // If none of the known video sinks are available, try to find one that implements the
68 // GstVideoOverlay interface and has autoplugging rank.
69 GList *list = qt_gst_video_sinks();
70 for (GList *item = list; item != nullptr; item = item->next) {
71 GstElementFactory *f = GST_ELEMENT_FACTORY(item->data);
72
73 if (!gst_element_factory_has_interface(factory: f, interfacename: "GstVideoOverlay"))
74 continue;
75
76 choice = QGstElement(gst_element_factory_create(factory: f, name: nullptr));
77 if (choice.isNull())
78 continue;
79
80 if (qt_gst_element_is_functioning(element: choice))
81 break;
82 choice = {};
83 }
84
85 gst_plugin_feature_list_free(list);
86 if (choice.isNull())
87 qWarning() << "Could not find a valid windowed video sink";
88
89 return choice;
90}
91
92QGstreamerVideoOverlay::QGstreamerVideoOverlay(QGstreamerVideoSink *parent, const QByteArray &elementName)
93 : QObject(parent)
94 , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps)
95 , m_gstreamerVideoSink(parent)
96{
97 QGstElement sink;
98 if (!elementName.isEmpty())
99 sink = QGstElement(elementName.constData(), nullptr);
100 else
101 sink = findBestVideoSink();
102
103 setVideoSink(sink);
104}
105
106QGstreamerVideoOverlay::~QGstreamerVideoOverlay()
107{
108 if (!m_videoSink.isNull()) {
109 QGstPad pad = m_videoSink.staticPad(name: "sink");
110 removeProbeFromPad(pad: pad.pad());
111 }
112}
113
114QGstElement QGstreamerVideoOverlay::videoSink() const
115{
116 return m_videoSink;
117}
118
119void QGstreamerVideoOverlay::setVideoSink(QGstElement sink)
120{
121 if (sink.isNull())
122 return;
123
124 m_videoSink = sink;
125
126 QGstPad pad = m_videoSink.staticPad(name: "sink");
127 addProbeToPad(pad: pad.pad());
128
129 auto *klass = G_OBJECT_GET_CLASS(m_videoSink.object());
130 m_hasForceAspectRatio = g_object_class_find_property(oclass: klass, property_name: "force-aspect-ratio");
131 m_hasFullscreen = g_object_class_find_property(oclass: klass, property_name: "fullscreen");
132}
133
134QSize QGstreamerVideoOverlay::nativeVideoSize() const
135{
136 return m_nativeVideoSize;
137}
138
139void QGstreamerVideoOverlay::setWindowHandle(WId id)
140{
141 m_windowId = id;
142
143 if (!m_videoSink.isNull() && GST_IS_VIDEO_OVERLAY(m_videoSink.object())) {
144 applyRenderRect();
145
146 // Properties need to be reset when changing the winId.
147 setAspectRatioMode(m_aspectRatioMode);
148 setFullScreen(m_fullScreen);
149 applyRenderRect();
150 }
151}
152
153void QGstreamerVideoOverlay::setRenderRectangle(const QRect &rect)
154{
155 renderRect = rect;
156 applyRenderRect();
157}
158
159void QGstreamerVideoOverlay::applyRenderRect()
160{
161 if (!m_windowId)
162 return;
163
164 int x = -1;
165 int y = -1;
166 int w = -1;
167 int h = -1;
168
169 if (!renderRect.isEmpty()) {
170 x = renderRect.x();
171 y = renderRect.y();
172 w = renderRect.width();
173 h = renderRect.height();
174 QSize scaledVideo = m_nativeVideoSize.scaled(w, h, mode: m_aspectRatioMode);
175 x += (w - scaledVideo.width())/2;
176 y += (h - scaledVideo.height())/2;
177 w = scaledVideo.width();
178 h = scaledVideo.height();
179 }
180
181 if (!m_videoSink.isNull() && GST_IS_VIDEO_OVERLAY(m_videoSink.object()))
182 gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink.object()), x, y, width: w, height: h);
183}
184
185void QGstreamerVideoOverlay::probeCaps(GstCaps *caps)
186{
187 QSize size = QGstCaps(caps, QGstCaps::NeedsRef).at(index: 0).resolution();
188 if (size != m_nativeVideoSize) {
189 m_nativeVideoSize = size;
190 m_gstreamerVideoSink->setNativeSize(m_nativeVideoSize);
191 applyRenderRect();
192 }
193}
194
195void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode)
196{
197 m_aspectRatioMode = mode;
198 if (m_hasForceAspectRatio)
199 m_videoSink.set(property: "force-aspect-ratio", b: (mode == Qt::KeepAspectRatio));
200}
201
202void QGstreamerVideoOverlay::setFullScreen(bool fullscreen)
203{
204 m_fullScreen = fullscreen;
205 if (m_hasFullscreen)
206 m_videoSink.set(property: "fullscreen", b: fullscreen);
207}
208
209bool QGstreamerVideoOverlay::processSyncMessage(const QGstreamerMessage &message)
210{
211 if (!gst_is_video_overlay_prepare_window_handle_message(msg: message.rawMessage()))
212 return false;
213 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink.object()), handle: m_windowId);
214 return true;
215}
216
217QT_END_NAMESPACE
218
219#include "moc_qgstreamervideooverlay_p.cpp"
220

source code of qtmultimedia/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp