Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwasmmediadevices.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#include "private/qcameradevice_p.h"
6#include "private/qplatformmediaintegration_p.h"
8#include "qwasmaudiosink_p.h"
10#include <AL/al.h>
11#include <AL/alc.h>
12
13#include <QMap>
14#include <QDebug>
15
17
18Q_LOGGING_CATEGORY(qWasmMediaDevices, "qt.multimedia.wasm.mediadevices")
19
21 : QPlatformVideoDevices(integration)
22{
23 m_mediaDevices = QPlatformMediaIntegration::instance()->mediaDevices();
24}
25
26QList<QCameraDevice> QWasmCameraDevices::videoDevices() const
27{
28 QWasmMediaDevices *wasmMediaDevices = reinterpret_cast<QWasmMediaDevices *>(m_mediaDevices);
29 return wasmMediaDevices ? wasmMediaDevices->videoInputs() : QList<QCameraDevice>();
30}
31
36
38{
39 if (m_initDone)
40 return;
41
42 m_initDone = true;
43 getOpenALAudioDevices();
44 getMediaDevices(); // asynchronous
45}
46
47QList<QAudioDevice> QWasmMediaDevices::audioInputs() const
48{
49 return m_audioInputs.values();
50}
51
52QList<QAudioDevice> QWasmMediaDevices::audioOutputs() const
53{
54 return m_audioOutputs.values();
55}
56
57QList<QCameraDevice> QWasmMediaDevices::videoInputs() const
58{
59 return m_cameraDevices.values();
60}
61
63 QObject *parent)
64{
65 return new QWasmAudioSource(deviceInfo.id(), parent);
66}
67
69 QObject *parent)
70{
71 return new QWasmAudioSink(deviceInfo.id(), parent);
72}
73
74void QWasmMediaDevices::parseDevices(emscripten::val devices)
75{
76 if (devices.isNull() || devices.isUndefined()) {
77 qWarning() << "Something went wrong enumerating devices";
78 return;
79 }
80
81 QList<std::string> cameraDevicesToRemove = m_cameraDevices.keys();
82 QList<std::string> audioOutputsToRemove;
83 QList<std::string> audioInputsToRemove;
84
85 if (m_firstInit) {
86 m_firstInit = false;
87 qWarning() << "m_audioInputs count" << m_audioInputs.count();
88
89 } else {
90 audioOutputsToRemove = m_audioOutputs.keys();
91 audioInputsToRemove = m_audioInputs.keys();
92 m_audioInputsAdded = false;
93 m_audioOutputsAdded = false;
94 }
95 m_videoInputsAdded = false;
96
97 bool m_videoInputsRemoved = false;
98 bool m_audioInputsRemoved = false;
99 bool m_audioOutputsRemoved = false;
100
101 for (int i = 0; i < devices["length"].as<int>(); i++) {
102
103 emscripten::val mediaDevice = devices[i];
104
105 std::string defaultDeviceLabel = "";
106
107 const std::string deviceKind = mediaDevice["kind"].as<std::string>();
108 const std::string label = mediaDevice["label"].as<std::string>();
109 const std::string deviceId = mediaDevice["deviceId"].as<std::string>();
110
111 qCDebug(qWasmMediaDevices) << QString::fromStdString(deviceKind)
112 << QString::fromStdString(deviceId)
114
115 if (deviceKind.empty())
116 continue;
117
118 if (deviceId == std::string("default")) {
119 // chrome specifies the default device with this as deviceId
120 // and then prepends "Default - " with the name of the device
121 // in the label
122 if (label.empty())
123 continue;
124
125 defaultDeviceLabel = label;
126 continue;
127 }
128
129 const bool isDefault = false; // FIXME
130 // (defaultDeviceLabel.find(label) != std::string::npos);
131
132 if (deviceKind == std::string("videoinput")) {
133 if (!m_cameraDevices.contains(deviceId)) {
135 camera->id = QString::fromStdString(deviceId).toUtf8();
136 camera->description = QString::fromUtf8(label.c_str());
137 camera->isDefault = isDefault;
138
139 m_cameraDevices.insert(deviceId, camera->create());
140 m_videoInputsAdded = true;
141 }
142 cameraDevicesToRemove.removeOne(deviceId);
143 } else if (deviceKind == std::string("audioinput")) {
144 if (!m_audioInputs.contains(deviceId)) {
145 m_audioInputs.insert(deviceId,
146 (new QWasmAudioDevice(deviceId.c_str(), label.c_str(),
147 isDefault, QAudioDevice::Input))
148 ->create());
149
150 m_audioInputsAdded = true;
151 }
152 audioInputsToRemove.removeOne(deviceId);
153 } else if (deviceKind == std::string("audiooutput")) {
154 if (!m_audioOutputs.contains(deviceId)) {
155 m_audioOutputs.insert(deviceId,
156 (new QWasmAudioDevice(deviceId.c_str(), label.c_str(),
157 isDefault, QAudioDevice::Input))
158 ->create());
159
160 m_audioOutputsAdded = true;
161 }
162 audioOutputsToRemove.removeOne(deviceId);
163 }
164 // if permissions are given label will hold the actual
165 // camera name, such as "Live! Cam Sync 1080p (041e:409d)"
166 }
167 if (!m_firstInit)
168 getOpenALAudioDevices();
169
170 // any left here were removed
171 int j = 0;
172 for (; j < cameraDevicesToRemove.count(); j++) {
173 m_cameraDevices.remove(cameraDevicesToRemove.at(j));
174 }
175 m_videoInputsRemoved = !cameraDevicesToRemove.isEmpty();
176
177 for (j = 0; j < audioInputsToRemove.count(); j++) {
178 m_audioInputs.remove(audioInputsToRemove.at(j));
179 }
180 m_audioInputsRemoved = !audioInputsToRemove.isEmpty();
181
182 for (j = 0; j < audioOutputsToRemove.count(); j++) {
183 m_audioOutputs.remove(audioOutputsToRemove.at(j));
184 }
185 m_audioOutputsRemoved = !audioOutputsToRemove.isEmpty();
186
187 if (m_videoInputsAdded || m_videoInputsRemoved)
189 if (m_audioInputsAdded || m_audioInputsRemoved)
191 if (m_audioOutputsAdded || m_audioOutputsRemoved)
193
194 m_firstInit = false;
195
196}
197
198void QWasmMediaDevices::getMediaDevices()
199{
200 emscripten::val navigator = emscripten::val::global("navigator");
201 m_jsMediaDevicesInterface = navigator["mediaDevices"];
202
203 if (m_jsMediaDevicesInterface.isNull() || m_jsMediaDevicesInterface.isUndefined()) {
204 qWarning() << "No media devices found";
205 return;
206 }
207
208 if (qstdweb::haveAsyncify()) {
209#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY
210 emscripten::val devicesList = m_jsMediaDevicesInterface.call<emscripten::val>("enumerateDevices").await();
211 if (devicesList.isNull() || devicesList.isUndefined()) {
212 qWarning() << "devices list error";
213 return;
214 }
215
216 parseDevices(devicesList);
217#endif
218 } else {
219 qstdweb::PromiseCallbacks enumerateDevicesCallback{
220 .thenFunc =
221 [&](emscripten::val devices) {
222 parseDevices(devices);
223 },
224 .catchFunc =
225 [this](emscripten::val error) {
226 qWarning() << "mediadevices enumerateDevices fail"
227 << QString::fromStdString(error["name"].as<std::string>())
228 << QString::fromStdString(error["message"].as<std::string>());
229 m_initDone = false;
230 }
231 };
232
233 qstdweb::Promise::make(m_jsMediaDevicesInterface,
234 QStringLiteral("enumerateDevices"),
235 std::move(enumerateDevicesCallback));
236
237 // setup devicechange monitor
238 m_deviceChangedCallback = std::make_unique<qstdweb::EventCallback>(
239 m_jsMediaDevicesInterface, "devicechange",
240 [this, enumerateDevicesCallback](emscripten::val) {
241 qstdweb::Promise::make(m_jsMediaDevicesInterface,
242 QStringLiteral("enumerateDevices"),
243 std::move(enumerateDevicesCallback));
244 });
245 }
246
247}
248
249void QWasmMediaDevices::getOpenALAudioDevices()
250{
251 // VM3959:4 The AudioContext was not allowed to start.
252 // It must be resumed (or created) after a user gesture on the page. https://goo.gl/7K7WLu
253 auto capture = alcGetString(nullptr, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
254 // present even if there is no capture device
255 if (capture && !m_audioOutputs.contains(capture)) {
256 m_audioInputs.insert(capture,
257 (new QWasmAudioDevice(capture, "WebAssembly audio capture device",
258 true, QAudioDevice::Input))
259 ->create());
260 m_audioInputsAdded = true;
262 }
263
264 auto playback = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
265 // present even if there is no playback device
266 if (playback && !m_audioOutputs.contains(capture)) {
267 m_audioOutputs.insert(playback,
268 (new QWasmAudioDevice(playback, "WebAssembly audio playback device",
270 ->create());
272 }
273 m_firstInit = true;
274}
275
The QAudioDevice class provides an information about audio devices and their functionality.
QByteArray id
\qmlproperty string QtMultimedia::audioDevice::id
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
size_type count(const Key &key) const
Definition qmap.h:404
QList< T > values() const
Definition qmap.h:397
size_type remove(const Key &key)
Definition qmap.h:300
QList< Key > keys() const
Definition qmap.h:383
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static std::unique_ptr< QPlatformMediaDevices > create()
static QPlatformMediaIntegration * instance()
static QString fromStdString(const std::string &s)
Definition qstring.h:1447
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QList< QCameraDevice > videoDevices() const override
QList< QAudioDevice > audioInputs() const override
QList< QAudioDevice > audioOutputs() const override
QList< QCameraDevice > videoInputs() const
QPlatformAudioSource * createAudioSource(const QAudioDevice &deviceInfo, QObject *parent) override
QPlatformAudioSink * createAudioSink(const QAudioDevice &deviceInfo, QObject *parent) override
QCamera * camera
Definition camera.cpp:19
Combined button and popup list for selecting options.
void make(emscripten::val target, QString methodName, PromiseCallbacks callbacks, Args... args)
Definition qstdweb_p.h:221
bool haveAsyncify()
Definition qstdweb.cpp:829
static QT_BEGIN_NAMESPACE bool await(IAsyncOperation< T > &&asyncInfo, T &result, uint timeout=0)
DBusConnection const char DBusError * error
EGLDeviceEXT * devices
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLuint GLsizei const GLchar * label
[43]
#define QStringLiteral(str)
#define emit
std::function< void(emscripten::val)> thenFunc
Definition qstdweb_p.h:212