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
26namespace {
27
28class QFallbackIntegration : public QPlatformMediaIntegration
29{
30public:
31 QFallbackIntegration() : QPlatformMediaIntegration(QLatin1String("fallback"))
32 {
33 qWarning("No QtMultimedia backends found. Only QMediaDevices, QAudioDevice, QSoundEffect, QAudioSink, and QAudioSource are available.");
34 }
35};
36
37Q_STATIC_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin")
38
39Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
40 (QPlatformMediaPlugin_iid,
41 QLatin1String("/multimedia")))
42
43static const auto FFmpegBackend = QStringLiteral("ffmpeg");
44
45[[nodiscard]] static QString &preferredBackend()
46{
47 static QString backend;
48 return backend;
49}
50
51static QString defaultBackend(const QStringList &backends)
52{
53#ifdef QT_DEFAULT_MEDIA_BACKEND
54 auto backend = QString::fromUtf8(QT_DEFAULT_MEDIA_BACKEND);
55 if (backends.contains(backend))
56 return backend;
57#endif
58
59#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
60 // Return ffmpeg backend by default.
61 // Platform backends for the OS list are optionally available but have limited support.
62 if (backends.contains(FFmpegBackend))
63 return FFmpegBackend;
64#else
65 // Return platform backend (non-ffmpeg) by default.
66 if (backends.size() > 1 && backends[0] == FFmpegBackend)
67 return backends[1];
68#endif
69
70 return backends[0];
71}
72
73struct InstanceHolder
74{
75 InstanceHolder()
76 {
77 init();
78 }
79
80 void init()
81 {
82 if (!QCoreApplication::instance())
83 qCCritical(qLcMediaPlugin()) << "Qt Multimedia requires a QCoreApplication instance";
84
85 QStringList backends = QPlatformMediaIntegration::availableBackends();
86 QString backend = QString::fromUtf8(qgetenv("QT_MEDIA_BACKEND")).toLower();
87 if (backend.isEmpty() && backends.contains(preferredBackend()))
88 backend = preferredBackend();
89 if (backend.isEmpty() && !backends.isEmpty())
90 backend = defaultBackend(backends);
91
92 qCDebug(qLcMediaPlugin) << "Loading media backend" << backend;
93 instance.reset(
94 qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader(), backend));
95
96 if (instance)
97 return;
98
99 backends.removeAll(backend);
100
101 for (const QString &backend : std::as_const(backends)) {
102 qCDebug(qLcMediaPlugin) << "Loading alternative backend" << backend;
103 instance.reset(qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader(),
104 backend));
105 if (instance)
106 return;
107 }
108
109 // No backends found. Use fallback to support basic functionality
110 instance = std::make_unique<QFallbackIntegration>();
111 }
112
113 ~InstanceHolder()
114 {
115 instance.reset();
116 qCDebug(qLcMediaPlugin) << "Released media backend";
117 }
118
119 std::unique_ptr<QPlatformMediaIntegration> instance;
120};
121
122Q_APPLICATION_STATIC(InstanceHolder, s_instanceHolder);
123
124} // namespace
125
127
128QPlatformMediaIntegration *QPlatformMediaIntegration::instance()
129{
130 return s_instanceHolder->instance.get();
131}
132
133void QPlatformMediaIntegration::resetInstance()
134{
135 s_instanceHolder->init(); // tests only
136}
137
138q23::expected<std::unique_ptr<QPlatformAudioResampler>, QString>
139QPlatformMediaIntegration::createAudioResampler(const QAudioFormat &, const QAudioFormat &)
140{
141 return q23::unexpected(notAvailable);
142}
143
144q23::expected<QPlatformAudioInput *, QString> QPlatformMediaIntegration::createAudioInput(QAudioInput *q)
145{
146 return new QPlatformAudioInput(q);
147}
148
149q23::expected<QPlatformAudioOutput *, QString> QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q)
150{
151 return new QPlatformAudioOutput(q);
152}
153
154QList<QCapturableWindow> QPlatformMediaIntegration::capturableWindowsList()
155{
156 const auto capturableWindows = this->capturableWindows();
157 return capturableWindows ? capturableWindows->windows() : QList<QCapturableWindow>{};
158}
159
160bool QPlatformMediaIntegration::isCapturableWindowValid(const QCapturableWindowPrivate &window)
161{
162 const auto capturableWindows = this->capturableWindows();
163 return capturableWindows && capturableWindows->isWindowValid(window);
164}
165
166q23::expected<QCapturableWindow, QString> QPlatformMediaIntegration::capturableWindowFromQWindow(QWindow *window)
167{
168 const auto capturableWindows = this->capturableWindows();
169 if (!capturableWindows)
170 return q23::unexpected{ QStringLiteral("No windowcapture platform implementation") };
171 if (window == nullptr)
172 return q23::unexpected{ QStringLiteral("QWindow is nullptr") };
173 if (!window->isTopLevel())
174 return q23::unexpected{ QStringLiteral("QWindow is not top-level.") };
175 return capturableWindows->fromQWindow(window);
176}
177
178const QPlatformMediaFormatInfo *QPlatformMediaIntegration::formatInfo()
179{
180 std::call_once(m_formatInfoOnceFlg, [this]() {
181 m_formatInfo.reset(createFormatInfo());
182 Q_ASSERT(m_formatInfo);
183 });
184 return m_formatInfo.get();
185}
186
187QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo()
188{
189 return new QPlatformMediaFormatInfo;
190}
191
192std::unique_ptr<QPlatformAudioDevices> QPlatformMediaIntegration::createAudioDevices()
193{
194 return QPlatformAudioDevices::create();
195}
196
197// clang-format off
198QPlatformVideoDevices *QPlatformMediaIntegration::videoDevices()
199{
200 std::call_once(m_videoDevicesOnceFlag,
201 [this]() {
202 m_videoDevices.reset(createVideoDevices());
203 });
204 return m_videoDevices.get();
205}
206
207QPlatformCapturableWindows *QPlatformMediaIntegration::capturableWindows()
208{
209 std::call_once(m_capturableWindowsOnceFlag,
210 [this]() {
211 m_capturableWindows.reset(createCapturableWindows());
212 });
213 return m_capturableWindows.get();
214}
215
216QPlatformAudioDevices *QPlatformMediaIntegration::audioDevices()
217{
218 std::call_once(m_audioDevicesOnceFlag, [this] {
219 m_audioDevices = createAudioDevices();
220 });
221 return m_audioDevices.get();
222}
223
224// clang-format on
225
226QStringList QPlatformMediaIntegration::availableBackends()
227{
228 QStringList list;
229
230 if (QFactoryLoader *fl = loader()) {
231 const auto keyMap = fl->keyMap();
232 for (auto it = keyMap.constBegin(); it != keyMap.constEnd(); ++it)
233 if (!list.contains(it.value()))
234 list << it.value();
235 }
236
237 qCDebug(qLcMediaPlugin) << "Available backends" << list;
238 return list;
239}
240
241// Must be called before any backend is established.
242// Sets the backend to be used when the QT_MEDIA_BACKEND environment variable is not set,
243// overriding the built-in default. Must be called before any media backend
244// is initialized.
245void QPlatformMediaIntegration::setPreferredBackend(const QString &backend)
246{
247 Q_ASSERT(!s_instanceHolder.exists());
248 preferredBackend() = backend.toLower();
249}
250
251QLatin1String QPlatformMediaIntegration::name()
252{
253 return m_backendName;
254}
255
256QVideoFrame QPlatformMediaIntegration::convertVideoFrame(QVideoFrame &,
257 const QVideoFrameFormat &)
258{
259 return {};
260}
261
262QLatin1String QPlatformMediaIntegration::audioBackendName()
263{
264 return QPlatformMediaIntegration::instance()->audioDevices()->backendName();
265}
266
267QPlatformMediaIntegration::QPlatformMediaIntegration(QLatin1String name) : m_backendName(name) { }
268
269QPlatformMediaIntegration::~QPlatformMediaIntegration() = default;
270
271QT_END_NAMESPACE
272
273#include "moc_qplatformmediaintegration_p.cpp"