Qt 6.x
The Qt SDK
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
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 = QPlatformMediaDevices::instance();
24}
25
27{
28 QWasmMediaDevices *wasmMediaDevices = reinterpret_cast<QWasmMediaDevices *>(m_mediaDevices);
29 return wasmMediaDevices ? wasmMediaDevices->videoInputs() : QList<QCameraDevice>();
30}
31
33{
35}
36
38{
39 if (m_initDone)
40 return;
41
42 m_initDone = true;
43 getOpenALAudioDevices();
44 getMediaDevices(); // asynchronous
45}
46
48{
49 return m_audioInputs.values();
50}
51
53{
54 return m_audioOutputs.values();
55}
56
58{
59 return m_cameraDevices.values();
60}
61
64{
65 return new QWasmAudioSource(deviceInfo.id(), parent);
66}
67
70{
71 return new QWasmAudioSink(deviceInfo.id(), parent);
72}
73
74void QWasmMediaDevices::getMediaDevices()
75{
76 emscripten::val navigator = emscripten::val::global("navigator");
77 m_jsMediaDevicesInterface = navigator["mediaDevices"];
78
79 if (m_jsMediaDevicesInterface.isNull() || m_jsMediaDevicesInterface.isUndefined()) {
80 qWarning() << "No media devices found";
81 return;
82 }
83
84 qstdweb::PromiseCallbacks enumerateDevicesCallback{
85 .thenFunc =
86 [&](emscripten::val devices) {
87 if (devices.isNull() || devices.isUndefined()) {
88 qWarning() << "Something went wrong enumerating devices";
89 return;
90 }
91
92 QList<std::string> cameraDevicesToRemove = m_cameraDevices.keys();
93 QList<std::string> audioInputsToRemove = m_audioInputs.keys();
94 QList<std::string> audioOutputsToRemove = m_audioOutputs.keys();
95
96 m_videoInputsAdded = false;
97 m_audioInputsAdded = false;
98 m_audioOutputsAdded = false;
99
100 bool m_videoInputsRemoved = false;
101 bool m_audioInputsRemoved = false;
102 bool m_audioOutputsRemoved = false;
103
104 for (int i = 0; i < devices["length"].as<int>(); i++) {
105
106 emscripten::val mediaDevice = devices[i];
107
108 std::string defaultDeviceLabel = "";
109
110 const std::string deviceKind = mediaDevice["kind"].as<std::string>();
111 const std::string label = mediaDevice["label"].as<std::string>();
112 const std::string deviceId = mediaDevice["deviceId"].as<std::string>();
113
114 qCDebug(qWasmMediaDevices) << QString::fromStdString(deviceKind)
115 << QString::fromStdString(deviceId)
117
118 if (deviceKind.empty())
119 continue;
120
121 if (deviceId == std::string("default")) {
122 // chrome specifies the default device with this as deviceId
123 // and then prepends "Default - " with the name of the device
124 // in the label
125 if (label.empty())
126 continue;
127
128 defaultDeviceLabel = label;
129 continue;
130 }
131
132 const bool isDefault =
133 (defaultDeviceLabel.find(label) != std::string::npos);
134
135 if (deviceKind == std::string("videoinput")) {
136 if (!m_cameraDevices.contains(deviceId)) {
138 camera->id = QString::fromStdString(deviceId).toUtf8();
139 camera->description = QString::fromUtf8(label.c_str());
140 camera->isDefault = isDefault;
141
142 m_cameraDevices.insert(deviceId, camera->create());
143 m_videoInputsAdded = true;
144 }
145 cameraDevicesToRemove.removeOne(deviceId);
146
147 } else if (deviceKind == std::string("audioinput")) {
148 if (!m_audioInputs.contains(deviceId)) {
149 m_audioInputs.insert(deviceId,
150 (new QWasmAudioDevice(deviceId.c_str(), label.c_str(),
151 isDefault, QAudioDevice::Input))
152 ->create());
153
154 m_audioInputsAdded = true;
155 }
156 audioInputsToRemove.removeOne(deviceId);
157 } else if (deviceKind == std::string("audiooutput")) {
158 if (!m_audioOutputs.contains(deviceId)) {
159 m_audioOutputs.insert(deviceId,
160 (new QWasmAudioDevice(deviceId.c_str(), label.c_str(),
161 isDefault, QAudioDevice::Input))
162 ->create());
163
164 m_audioOutputsAdded = true;
165 }
166 audioOutputsToRemove.removeOne(deviceId);
167 }
168 // if permissions are given label will hold the actual
169 // camera name, such as "Live! Cam Sync 1080p (041e:409d)"
170 }
171
172 // any left here were removed
173 int j = 0;
174 for (; j < cameraDevicesToRemove.count(); j++) {
175 m_cameraDevices.remove(cameraDevicesToRemove.at(j));
176 }
177 m_videoInputsRemoved = !cameraDevicesToRemove.isEmpty();
178
179 for (j = 0; j < audioInputsToRemove.count(); j++) {
180 m_audioInputs.remove(audioInputsToRemove.at(j));
181 }
182 m_audioInputsRemoved = !audioInputsToRemove.isEmpty();
183
184 for (j = 0; j < audioOutputsToRemove.count(); j++) {
185 m_audioOutputs.remove(audioOutputsToRemove.at(j));
186 }
187 m_audioOutputsRemoved = !audioOutputsToRemove.isEmpty();
188
189 if (m_videoInputsAdded || m_videoInputsRemoved)
191 if (m_audioInputsAdded || m_audioInputsRemoved)
193 if (m_audioOutputsAdded || m_audioOutputsRemoved)
195
196 },
197 .catchFunc =
198 [this](emscripten::val error) {
199 qWarning() << "mediadevices enumerateDevices fail"
200 << QString::fromStdString(error["name"].as<std::string>())
201 << QString::fromStdString(error["message"].as<std::string>());
202 m_initDone = false;
203 }
204 };
205
206 qstdweb::Promise::make(m_jsMediaDevicesInterface,
207 QStringLiteral("enumerateDevices"),
208 std::move(enumerateDevicesCallback));
209
210 // setup devicechange monitor
211 m_deviceChangedCallback = std::make_unique<qstdweb::EventCallback>(
212 m_jsMediaDevicesInterface, "devicechange",
213 [this, enumerateDevicesCallback](emscripten::val) {
214 qstdweb::Promise::make(m_jsMediaDevicesInterface,
215 QStringLiteral("enumerateDevices"),
216 std::move(enumerateDevicesCallback));
217 });
218}
219
220void QWasmMediaDevices::getOpenALAudioDevices()
221{
222 // VM3959:4 The AudioContext was not allowed to start.
223 // It must be resumed (or created) after a user gesture on the page. https://goo.gl/7K7WLu
224 auto capture = alcGetString(nullptr, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
225 // present even if there is no capture device
226 if (capture && !m_audioOutputs.contains(capture)) {
227 m_audioInputs.insert(capture,
228 (new QWasmAudioDevice(capture, "WebAssembly audio capture device",
229 true, QAudioDevice::Input))
230 ->create());
232 }
233
234 auto playback = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
235 // present even if there is no playback device
236 if (playback && !m_audioOutputs.contains(capture)) {
237 m_audioOutputs.insert(playback,
238 (new QWasmAudioDevice(playback, "WebAssembly audio playback device",
240 ->create());
242 }
243}
244
The QAudioDevice class provides an information about audio devices and their functionality.
QByteArray id
\qmlproperty string QtMultimedia::audioDevice::id
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
bool removeOne(const AT &t)
Definition qlist.h:581
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
qsizetype count() const noexcept
Definition qlist.h:387
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
bool contains(const Key &key) const
Definition qmap.h:340
QList< T > values() const
Definition qmap.h:396
size_type remove(const Key &key)
Definition qmap.h:299
QList< Key > keys() const
Definition qmap.h:382
\inmodule QtCore
Definition qobject.h:90
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
static QPlatformMediaDevices * instance()
static QString fromStdString(const std::string &s)
Definition qstring.h:1322
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QByteArray toUtf8() const &
Definition qstring.h:563
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:182
DBusConnection const char DBusError * error
EGLDeviceEXT * devices
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLuint GLsizei const GLchar * label
[43]
#define QStringLiteral(str)
#define emit
view create()
std::function< void(emscripten::val)> thenFunc
Definition qstdweb_p.h:173
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent