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 <qplatformvideodevices_p.h>
11#include <qmediadevices.h>
12#include <qcameradevice.h>
13#include <qloggingcategory.h>
14
15#include "qplatformcapturablewindows_p.h"
16#include "QtCore/private/qfactoryloader_p.h"
17#include "private/qplatformmediaformatinfo_p.h"
18#include "qplatformmediaplugin_p.h"
19
20class QDummyIntegration : public QPlatformMediaIntegration
21{
22public:
23 QDummyIntegration()
24 {
25 qCritical(msg: "QtMultimedia is not currently supported on this platform or compiler.");
26 }
27};
28
29static Q_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin")
30
31Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
32 (QPlatformMediaPlugin_iid,
33 QLatin1String("/multimedia")))
34
35static const auto FFmpegBackend = QStringLiteral("ffmpeg");
36
37static QStringList availableBackends()
38{
39 QStringList list;
40
41 if (QFactoryLoader *fl = loader()) {
42 const auto keyMap = fl->keyMap();
43 for (auto it = keyMap.constBegin(); it != keyMap.constEnd(); ++it)
44 if (!list.contains(str: it.value()))
45 list << it.value();
46 }
47
48 qCDebug(qLcMediaPlugin) << "Available backends" << list;
49 return list;
50}
51
52static QString defaultBackend(const QStringList &backends)
53{
54#ifdef QT_DEFAULT_MEDIA_BACKEND
55 auto backend = QString::fromUtf8(QT_DEFAULT_MEDIA_BACKEND);
56 if (backends.contains(backend))
57 return backend;
58#endif
59
60#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
61 // Return ffmpeg backend by default.
62 // Platform backends for the OS list are optionally available but have limited support.
63 if (backends.contains(str: FFmpegBackend))
64 return FFmpegBackend;
65#else
66 // Return platform backend (non-ffmpeg) by default.
67 if (backends.size() > 1 && backends[0] == FFmpegBackend)
68 return backends[1];
69#endif
70
71 return backends[0];
72}
73
74QT_BEGIN_NAMESPACE
75
76struct QPlatformMediaIntegration::InstanceHolder
77{
78 // TODO: replace the mutex with std::once
79 QBasicMutex mutex;
80 std::unique_ptr<QPlatformMediaIntegration> instance;
81 Factory factory;
82} instanceHolder;
83
84QPlatformMediaIntegration *QPlatformMediaIntegration::instance()
85{
86 QMutexLocker locker(&instanceHolder.mutex);
87 if (instanceHolder.instance)
88 return instanceHolder.instance.get();
89
90 if (instanceHolder.factory) {
91 instanceHolder.instance = instanceHolder.factory();
92 return instanceHolder.instance.get();
93 }
94
95 const auto backends = availableBackends();
96 QString backend = QString::fromUtf8(ba: qgetenv(varName: "QT_MEDIA_BACKEND"));
97 if (backend.isEmpty() && !backends.isEmpty())
98 backend = defaultBackend(backends);
99
100 qCDebug(qLcMediaPlugin) << "loading backend" << backend;
101 instanceHolder.instance.reset(
102 p: qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader: loader(), key: backend));
103
104 if (!instanceHolder.instance) {
105 qWarning() << "could not load multimedia backend" << backend;
106 instanceHolder.instance = std::make_unique<QDummyIntegration>();
107 }
108
109 return instanceHolder.instance.get();
110}
111
112/*
113 This API is there to be able to test with a mock backend.
114*/
115void QPlatformMediaIntegration::setPlatformFactory(Factory factory)
116{
117 Q_ASSERT((factory == nullptr) ^ (instanceHolder.factory == nullptr));
118 instanceHolder.instance.reset();
119 instanceHolder.factory = std::move(factory);
120}
121
122QList<QCameraDevice> QPlatformMediaIntegration::videoInputs()
123{
124 return m_videoDevices ? m_videoDevices->videoDevices() : QList<QCameraDevice>{};
125}
126
127QMaybe<QPlatformAudioInput *> QPlatformMediaIntegration::createAudioInput(QAudioInput *q)
128{
129 return new QPlatformAudioInput(q);
130}
131
132QMaybe<QPlatformAudioOutput *> QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q)
133{
134 return new QPlatformAudioOutput(q);
135}
136
137QList<QCapturableWindow> QPlatformMediaIntegration::capturableWindows()
138{
139 return m_capturableWindows ? m_capturableWindows->windows() : QList<QCapturableWindow>{};
140}
141
142bool QPlatformMediaIntegration::isCapturableWindowValid(const QCapturableWindowPrivate &window)
143{
144 return m_capturableWindows && m_capturableWindows->isWindowValid(window);
145}
146
147const QPlatformMediaFormatInfo *QPlatformMediaIntegration::formatInfo()
148{
149 std::call_once(once&: m_formatInfoOnceFlg, f: [this]() {
150 m_formatInfo.reset(p: createFormatInfo());
151 Q_ASSERT(m_formatInfo);
152 });
153 return m_formatInfo.get();
154}
155
156QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo()
157{
158 return new QPlatformMediaFormatInfo;
159}
160
161QPlatformMediaIntegration::QPlatformMediaIntegration() = default;
162
163QPlatformMediaIntegration::~QPlatformMediaIntegration() = default;
164
165QT_END_NAMESPACE
166

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