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 | |
20 | class QDummyIntegration : public QPlatformMediaIntegration |
21 | { |
22 | public: |
23 | QDummyIntegration() |
24 | { |
25 | qCritical(msg: "QtMultimedia is not currently supported on this platform or compiler."); |
26 | } |
27 | }; |
28 | |
29 | static Q_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin") |
30 | |
31 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, |
32 | (QPlatformMediaPlugin_iid, |
33 | QLatin1String("/multimedia"))) |
34 | |
35 | static const auto FFmpegBackend = QStringLiteral("ffmpeg"); |
36 | |
37 | static 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 | |
52 | static 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 | |
74 | QT_BEGIN_NAMESPACE |
75 | |
76 | struct 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 | |
84 | QPlatformMediaIntegration *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 | */ |
115 | void 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 | |
122 | QList<QCameraDevice> QPlatformMediaIntegration::videoInputs() |
123 | { |
124 | return m_videoDevices ? m_videoDevices->videoDevices() : QList<QCameraDevice>{}; |
125 | } |
126 | |
127 | QMaybe<QPlatformAudioInput *> QPlatformMediaIntegration::createAudioInput(QAudioInput *q) |
128 | { |
129 | return new QPlatformAudioInput(q); |
130 | } |
131 | |
132 | QMaybe<QPlatformAudioOutput *> QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q) |
133 | { |
134 | return new QPlatformAudioOutput(q); |
135 | } |
136 | |
137 | QList<QCapturableWindow> QPlatformMediaIntegration::capturableWindows() |
138 | { |
139 | return m_capturableWindows ? m_capturableWindows->windows() : QList<QCapturableWindow>{}; |
140 | } |
141 | |
142 | bool QPlatformMediaIntegration::isCapturableWindowValid(const QCapturableWindowPrivate &window) |
143 | { |
144 | return m_capturableWindows && m_capturableWindows->isWindowValid(window); |
145 | } |
146 | |
147 | const 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 | |
156 | QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo() |
157 | { |
158 | return new QPlatformMediaFormatInfo; |
159 | } |
160 | |
161 | QPlatformMediaIntegration::QPlatformMediaIntegration() = default; |
162 | |
163 | QPlatformMediaIntegration::~QPlatformMediaIntegration() = default; |
164 | |
165 | QT_END_NAMESPACE |
166 |