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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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