Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qplatformmediaintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
5
6#include <QtMultimedia/private/qplatformaudiodevices_p.h>
7#include <QtMultimedia/private/qplatformaudioinput_p.h>
8#include <QtMultimedia/private/qplatformaudiooutput_p.h>
9#include <QtMultimedia/private/qplatformaudioresampler_p.h>
10#include <QtMultimedia/private/qplatformcapturablewindows_p.h>
11#include <QtMultimedia/private/qplatformmediaformatinfo_p.h>
12#include <QtMultimedia/private/qplatformmediaplugin_p.h>
13#include <QtMultimedia/private/qplatformvideodevices_p.h>
14#include <QtMultimedia/private/qtmultimediaglobal_p.h>
15#include <QtMultimedia/qcameradevice.h>
16#include <QtMultimedia/qmediadevices.h>
17#include <QtGui/qwindow.h>
18#include <QtCore/private/qcoreapplication_p.h>
19#include <QtCore/private/qfactoryloader_p.h>
20#include <QtCore/qapplicationstatic.h>
21#include <QtCore/qatomic.h>
22#include <QtCore/qcoreapplication.h>
23#include <QtCore/qloggingcategory.h>
24#include <QtCore/qmutex.h>
25
26#include <optional>
27
28namespace {
29
30class QFallbackIntegration : public QPlatformMediaIntegration
31{
32public:
33 QFallbackIntegration() : QPlatformMediaIntegration(QLatin1String("fallback"))
34 {
35 qWarning("No QtMultimedia backends found. Only QMediaDevices, QAudioDevice, QSoundEffect, QAudioSink, and QAudioSource are available.");
36 }
37};
38
39Q_STATIC_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin")
40
41Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
42 (QPlatformMediaPlugin_iid,
43 QLatin1String("/multimedia")))
44
45static const auto FFmpegBackend = QStringLiteral("ffmpeg");
46
47[[nodiscard]] static std::optional<QString> &requestedBackend()
48{
49 static std::optional<QString> backend;
50 return backend;
51}
52
53[[nodiscard]] static QString defaultBackend(const QStringList &backends)
54{
55 if (backends.isEmpty())
56 return {};
57
58#ifdef QT_DEFAULT_MEDIA_BACKEND
59 auto backend = QString::fromUtf8(QT_DEFAULT_MEDIA_BACKEND);
60 if (backends.contains(backend))
61 return backend;
62#endif
63
64#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
65 // Return ffmpeg backend by default.
66 // Platform backends for the OS list are optionally available but have limited support.
67 if (backends.contains(FFmpegBackend))
68 return FFmpegBackend;
69#else
70 // Return platform backend (non-ffmpeg) by default.
71 if (backends.size() > 1 && backends[0] == FFmpegBackend)
72 return backends[1];
73#endif
74
75 return backends[0];
76}
77
78struct InstanceHolder
79{
80 InstanceHolder()
81 {
82 init();
83 }
84
85 void init()
86 {
87 if (!QCoreApplication::instance())
88 qCCritical(qLcMediaPlugin()) << "Qt Multimedia requires a QCoreApplication instance";
89
90 QStringList backends = QPlatformMediaIntegration::availableBackends();
91
92 // A backend explicitly requested via setBackend() takes precedence over
93 // the QT_MEDIA_BACKEND environment variable and the built-in default.
94 const std::optional<QString> &requested = requestedBackend();
95 QString backend = requested ? *requested : QString();
96 if (backend.isEmpty())
97 backend = QString::fromUtf8(qgetenv("QT_MEDIA_BACKEND")).toLower();
98 if (backend.isEmpty() && !backends.isEmpty())
99 backend = defaultBackend(backends);
100
101 qCDebug(qLcMediaPlugin) << "Loading media backend" << backend;
102 instance.reset(
103 qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader(), backend));
104
105 if (instance)
106 return;
107
108 backends.removeAll(backend);
109
110 // A backend requested via setBackend() disables the fallback to other
111 // backends.
112 if (!requested) {
113 for (const QString &backend : std::as_const(backends)) {
114 qCDebug(qLcMediaPlugin) << "Loading alternative backend" << backend;
115 instance.reset(qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(
116 loader(),
117 backend));
118 if (instance)
119 return;
120 }
121 } else if (!backend.isEmpty()) {
122 qCWarning(qLcMediaPlugin)
123 << "Media backend" << backend
124 << "failed to load and was explicitly requested; no media backend loaded";
125 }
126
127 // No backends found. Use fallback to support basic functionality
128 instance = std::make_unique<QFallbackIntegration>();
129 }
130
131 ~InstanceHolder()
132 {
133 instance.reset();
134 qCDebug(qLcMediaPlugin) << "Released media backend";
135 }
136
137 std::unique_ptr<QPlatformMediaIntegration> instance;
138};
139
140Q_APPLICATION_STATIC(InstanceHolder, s_instanceHolder);
141
142} // namespace
143
145
146QPlatformMediaIntegration *QPlatformMediaIntegration::instance()
147{
148 return s_instanceHolder->instance.get();
149}
150
151void QPlatformMediaIntegration::resetInstance()
152{
153 s_instanceHolder->init(); // tests only
154}
155
156q23::expected<std::unique_ptr<QPlatformAudioResampler>, QString>
157QPlatformMediaIntegration::createAudioResampler(const QAudioFormat &, const QAudioFormat &)
158{
159 return q23::unexpected(notAvailable);
160}
161
162q23::expected<QPlatformAudioInput *, QString> QPlatformMediaIntegration::createAudioInput(QAudioInput *q)
163{
164 return new QPlatformAudioInput(q);
165}
166
167q23::expected<QPlatformAudioOutput *, QString> QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q)
168{
169 return new QPlatformAudioOutput(q);
170}
171
172QList<QCapturableWindow> QPlatformMediaIntegration::capturableWindowsList()
173{
174 const auto capturableWindows = this->capturableWindows();
175 return capturableWindows ? capturableWindows->windows() : QList<QCapturableWindow>{};
176}
177
178bool QPlatformMediaIntegration::isCapturableWindowValid(const QCapturableWindowPrivate &window)
179{
180 const auto capturableWindows = this->capturableWindows();
181 return capturableWindows && capturableWindows->isWindowValid(window);
182}
183
184q23::expected<QCapturableWindow, QString> QPlatformMediaIntegration::capturableWindowFromQWindow(QWindow *window)
185{
186 const auto capturableWindows = this->capturableWindows();
187 if (!capturableWindows)
188 return q23::unexpected{ QStringLiteral("No windowcapture platform implementation") };
189 if (window == nullptr)
190 return q23::unexpected{ QStringLiteral("QWindow is nullptr") };
191 if (!window->isTopLevel())
192 return q23::unexpected{ QStringLiteral("QWindow is not top-level.") };
193 return capturableWindows->fromQWindow(window);
194}
195
196const QPlatformMediaFormatInfo *QPlatformMediaIntegration::formatInfo()
197{
198 std::call_once(m_formatInfoOnceFlg, [this]() {
199 m_formatInfo.reset(createFormatInfo());
200 Q_ASSERT(m_formatInfo);
201 });
202 return m_formatInfo.get();
203}
204
205QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo()
206{
207 return new QPlatformMediaFormatInfo;
208}
209
210std::unique_ptr<QPlatformAudioDevices> QPlatformMediaIntegration::createAudioDevices()
211{
212 return QPlatformAudioDevices::create();
213}
214
215// clang-format off
216QPlatformVideoDevices *QPlatformMediaIntegration::videoDevices()
217{
218 std::call_once(m_videoDevicesOnceFlag,
219 [this]() {
220 m_videoDevices.reset(createVideoDevices());
221 });
222 return m_videoDevices.get();
223}
224
225QPlatformCapturableWindows *QPlatformMediaIntegration::capturableWindows()
226{
227 std::call_once(m_capturableWindowsOnceFlag,
228 [this]() {
229 m_capturableWindows.reset(createCapturableWindows());
230 });
231 return m_capturableWindows.get();
232}
233
234QPlatformAudioDevices *QPlatformMediaIntegration::audioDevices()
235{
236 std::call_once(m_audioDevicesOnceFlag, [this] {
237 m_audioDevices = createAudioDevices();
238 });
239 return m_audioDevices.get();
240}
241
242// clang-format on
243
244QStringList QPlatformMediaIntegration::availableBackends()
245{
246 QStringList list;
247
248 if (QFactoryLoader *fl = loader()) {
249 const auto keyMap = fl->keyMap();
250 for (auto it = keyMap.constBegin(); it != keyMap.constEnd(); ++it)
251 if (!list.contains(it.value()))
252 list << it.value();
253 }
254
255 qCDebug(qLcMediaPlugin) << "Available backends" << list;
256 return list;
257}
258
259// Returns the name of the backend that would be loaded by default on this
260// platform when neither the QT_MEDIA_BACKEND environment variable nor
261// setBackend() selects one. Returns an empty string if no backend is available.
262QString QPlatformMediaIntegration::defaultBackend()
263{
264 return ::defaultBackend(availableBackends());
265}
266
267// Selects the media backend to load, taking precedence over both the
268// QT_MEDIA_BACKEND environment variable and the built-in default. No fallback to
269// other available backends is performed if the requested backend fails to load.
270// Must be called before any media backend is initialized.
271void QPlatformMediaIntegration::setBackend(const QString &backend)
272{
273 Q_ASSERT(!s_instanceHolder.exists());
274 requestedBackend() = backend.toLower();
275}
276
277QLatin1String QPlatformMediaIntegration::name()
278{
279 return m_backendName;
280}
281
282QVideoFrame QPlatformMediaIntegration::convertVideoFrame(QVideoFrame &,
283 const QVideoFrameFormat &)
284{
285 return {};
286}
287
288QLatin1String QPlatformMediaIntegration::audioBackendName()
289{
290 return QPlatformMediaIntegration::instance()->audioDevices()->backendName();
291}
292
293QPlatformMediaIntegration::QPlatformMediaIntegration(QLatin1String name) : m_backendName(name) { }
294
295QPlatformMediaIntegration::~QPlatformMediaIntegration() = default;
296
297QT_END_NAMESPACE
298
299#include "moc_qplatformmediaintegration_p.cpp"