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
4#include <qtmultimediaglobal_p.h>
5#include "qplatformmediaintegration_p.h"
6#include <qatomic.h>
7#include <qmutex.h>
8#include <qplatformaudioinput_p.h>
9#include <qplatformaudiooutput_p.h>
10#include <qplatformaudioresampler_p.h>
11#include <qplatformvideodevices_p.h>
12#include <qmediadevices.h>
13#include <qcameradevice.h>
14#include <qloggingcategory.h>
15#include <QtCore/qcoreapplication.h>
16#include <QtCore/qapplicationstatic.h>
17
18#include "qplatformcapturablewindows_p.h"
19#include "qplatformaudiodevices_p.h"
20#include <QtCore/private/qfactoryloader_p.h>
21#include <QtCore/private/qcoreapplication_p.h>
22#include <private/qplatformmediaformatinfo_p.h>
23#include "qplatformmediaplugin_p.h"
24
25namespace {
26
27class QFallbackIntegration : public QPlatformMediaIntegration
28{
29public:
30 QFallbackIntegration() : QPlatformMediaIntegration(QLatin1String("fallback"))
31 {
32 qWarning(msg: "No QtMultimedia backends found. Only QMediaDevices, QAudioDevice, QSoundEffect, QAudioSink, and QAudioSource are available.");
33 }
34};
35
36static Q_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin")
37
38Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
39 (QPlatformMediaPlugin_iid,
40 QLatin1String("/multimedia")))
41
42static const auto FFmpegBackend = QStringLiteral("ffmpeg");
43
44static QString defaultBackend(const QStringList &backends)
45{
46#ifdef QT_DEFAULT_MEDIA_BACKEND
47 auto backend = QString::fromUtf8(QT_DEFAULT_MEDIA_BACKEND);
48 if (backends.contains(backend))
49 return backend;
50#endif
51
52#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
53 // Return ffmpeg backend by default.
54 // Platform backends for the OS list are optionally available but have limited support.
55 if (backends.contains(str: FFmpegBackend))
56 return FFmpegBackend;
57#else
58 // Return platform backend (non-ffmpeg) by default.
59 if (backends.size() > 1 && backends[0] == FFmpegBackend)
60 return backends[1];
61#endif
62
63 return backends[0];
64}
65
66struct InstanceHolder
67{
68 InstanceHolder()
69 {
70 init();
71 }
72
73 void init()
74 {
75 if (!QCoreApplication::instance())
76 qCCritical(qLcMediaPlugin()) << "Qt Multimedia requires a QCoreApplication instance";
77
78 const QStringList backends = QPlatformMediaIntegration::availableBackends();
79 QString backend = QString::fromUtf8(ba: qgetenv(varName: "QT_MEDIA_BACKEND")).toLower();
80 if (backend.isEmpty() && !backends.isEmpty())
81 backend = defaultBackend(backends);
82
83 qCDebug(qLcMediaPlugin) << "Loading media backend" << backend;
84 instance.reset(
85 p: qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader: loader(), key: backend));
86
87 if (!instance) {
88 // No backends found. Use fallback to support basic functionality
89 instance = std::make_unique<QFallbackIntegration>();
90 }
91 }
92
93 ~InstanceHolder()
94 {
95 instance.reset();
96 qCDebug(qLcMediaPlugin) << "Released media backend";
97 }
98
99 std::unique_ptr<QPlatformMediaIntegration> instance;
100};
101
102Q_APPLICATION_STATIC(InstanceHolder, s_instanceHolder);
103
104} // namespace
105
106QT_BEGIN_NAMESPACE
107
108QPlatformMediaIntegration *QPlatformMediaIntegration::instance()
109{
110 return s_instanceHolder->instance.get();
111}
112
113void QPlatformMediaIntegration::resetInstance()
114{
115 s_instanceHolder->init(); // tests only
116}
117
118QMaybe<std::unique_ptr<QPlatformAudioResampler>>
119QPlatformMediaIntegration::createAudioResampler(const QAudioFormat &, const QAudioFormat &)
120{
121 return notAvailable;
122}
123
124QMaybe<QPlatformAudioInput *> QPlatformMediaIntegration::createAudioInput(QAudioInput *q)
125{
126 return new QPlatformAudioInput(q);
127}
128
129QMaybe<QPlatformAudioOutput *> QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q)
130{
131 return new QPlatformAudioOutput(q);
132}
133
134QList<QCapturableWindow> QPlatformMediaIntegration::capturableWindowsList()
135{
136 const auto capturableWindows = this->capturableWindows();
137 return capturableWindows ? capturableWindows->windows() : QList<QCapturableWindow>{};
138}
139
140bool QPlatformMediaIntegration::isCapturableWindowValid(const QCapturableWindowPrivate &window)
141{
142 const auto capturableWindows = this->capturableWindows();
143 return capturableWindows && capturableWindows->isWindowValid(window);
144}
145
146const QPlatformMediaFormatInfo *QPlatformMediaIntegration::formatInfo()
147{
148 std::call_once(once&: m_formatInfoOnceFlg, f: [this]() {
149 m_formatInfo.reset(p: createFormatInfo());
150 Q_ASSERT(m_formatInfo);
151 });
152 return m_formatInfo.get();
153}
154
155QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo()
156{
157 return new QPlatformMediaFormatInfo;
158}
159
160std::unique_ptr<QPlatformAudioDevices> QPlatformMediaIntegration::createAudioDevices()
161{
162 return QPlatformAudioDevices::create();
163}
164
165// clang-format off
166QPlatformVideoDevices *QPlatformMediaIntegration::videoDevices()
167{
168 std::call_once(once&: m_videoDevicesOnceFlag,
169 f: [this]() {
170 m_videoDevices.reset(p: createVideoDevices());
171 });
172 return m_videoDevices.get();
173}
174
175QPlatformCapturableWindows *QPlatformMediaIntegration::capturableWindows()
176{
177 std::call_once(once&: m_capturableWindowsOnceFlag,
178 f: [this]() {
179 m_capturableWindows.reset(p: createCapturableWindows());
180 });
181 return m_capturableWindows.get();
182}
183
184QPlatformAudioDevices *QPlatformMediaIntegration::audioDevices()
185{
186 std::call_once(once&: m_audioDevicesOnceFlag, f: [this] {
187 m_audioDevices = createAudioDevices();
188 });
189 return m_audioDevices.get();
190}
191
192// clang-format on
193
194QStringList QPlatformMediaIntegration::availableBackends()
195{
196 QStringList list;
197
198 if (QFactoryLoader *fl = loader()) {
199 const auto keyMap = fl->keyMap();
200 for (auto it = keyMap.constBegin(); it != keyMap.constEnd(); ++it)
201 if (!list.contains(str: it.value()))
202 list << it.value();
203 }
204
205 qCDebug(qLcMediaPlugin) << "Available backends" << list;
206 return list;
207}
208
209QLatin1String QPlatformMediaIntegration::name()
210{
211 return m_backendName;
212}
213
214QVideoFrame QPlatformMediaIntegration::convertVideoFrame(QVideoFrame &,
215 const QVideoFrameFormat &)
216{
217 return {};
218}
219
220QLatin1String QPlatformMediaIntegration::audioBackendName()
221{
222 return QPlatformMediaIntegration::instance()->audioDevices()->backendName();
223}
224
225QPlatformMediaIntegration::QPlatformMediaIntegration(QLatin1String name) : m_backendName(name) { }
226
227QPlatformMediaIntegration::~QPlatformMediaIntegration() = default;
228
229QT_END_NAMESPACE
230
231#include "moc_qplatformmediaintegration_p.cpp"
232

source code of qtmultimedia/src/multimedia/platform/qplatformmediaintegration.cpp