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
qwasmcamera.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd and/or its subsidiary(-ies).
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 "qwasmcamera_p.h"
5#include "qmediadevices.h"
6#include <qcameradevice.h>
7#include "private/qabstractvideobuffer_p.h"
8#include "private/qplatformvideosink_p.h"
9#include <private/qmemoryvideobuffer_p.h>
10#include <private/qvideotexturehelper_p.h>
11#include <private/qwasmmediadevices_p.h>
12
15
16#include <emscripten/val.h>
17#include <emscripten/bind.h>
18#include <emscripten/html5.h>
19#include <QUuid>
20#include <QTimer>
21
22#include <private/qstdweb_p.h>
23
24Q_LOGGING_CATEGORY(qWasmCamera, "qt.multimedia.wasm.camera")
25
28 m_cameraOutput(new QWasmVideoOutput),
29 m_cameraIsReady(false)
30{
31 QWasmMediaDevices *wasmMediaDevices =
32 static_cast<QWasmMediaDevices *>(QPlatformMediaIntegration::instance()->mediaDevices());
33
34 connect(wasmMediaDevices, &QWasmMediaDevices::videoInputsChanged,this, [this]() {
35 const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
36
37 if (!cameras.isEmpty()) {
38 if (m_cameraDev.id().isEmpty())
39 setCamera(cameras.at(0)); // default camera
40 else
41 setCamera(m_cameraDev);
42 return;
43 }
44 });
45
46 connect(this, &QWasmCamera::cameraIsReady, this, [this]() {
47 m_cameraIsReady = true;
48 if (m_cameraShouldStartActive) {
49 QTimer::singleShot(50, this, [this]() {
50 setActive(true);
51 });
52 }
53 });
54}
55
57
59{
60 return m_cameraActive;
61}
62
63void QWasmCamera::setActive(bool active)
64{
65
66 if (!m_CaptureSession) {
67 updateError(QCamera::CameraError, QStringLiteral("video surface error"));
68 m_shouldBeActive = true;
69 return;
70 }
71
72 if (!m_cameraIsReady) {
73 m_cameraShouldStartActive = true;
74 return;
75 }
76
77 QVideoSink *sink = m_CaptureSession->videoSink();
78 if (!sink) {
79 qWarning() << Q_FUNC_INFO << "sink not ready";
80 return;
81 }
82
83 m_cameraOutput->setSurface(m_CaptureSession->videoSink());
84 m_cameraActive = active;
85 m_shouldBeActive = false;
86
87 if (m_cameraActive)
88 m_cameraOutput->start();
89 else
90 m_cameraOutput->pause();
91
92 updateCameraFeatures();
93 emit activeChanged(active);
94}
95
97{
98 if (!m_cameraDev.id().isEmpty())
99 return;
100
101 m_cameraOutput->setVideoMode(QWasmVideoOutput::Camera);
102
103 constexpr QSize initialSize(0, 0);
104 constexpr QRect initialRect(QPoint(0, 0), initialSize);
105 m_cameraOutput->createVideoElement(camera.id().toStdString()); // videoElementId
106 m_cameraOutput->createOffscreenElement(initialSize);
107 m_cameraOutput->updateVideoElementGeometry(initialRect);
108
109 const auto cameras = QMediaDevices::videoInputs();
110
111 if (std::find(cameras.begin(), cameras.end(), camera) != cameras.end()) {
112 m_cameraDev = camera;
113 createCamera(m_cameraDev);
115 return;
116 }
117
118 if (cameras.count() > 0) {
119 m_cameraDev = camera;
120 createCamera(m_cameraDev);
122 } else {
123 updateError(QCamera::CameraError, QStringLiteral("Failed to find a camera"));
124 }
125}
126
128{
130
131 return true;
132}
133
135{
136 QWasmMediaCaptureSession *captureSession = static_cast<QWasmMediaCaptureSession *>(session);
137 if (m_CaptureSession == captureSession)
138 return;
139
140 m_CaptureSession = captureSession;
141
142 if (m_shouldBeActive)
143 setActive(true);
144}
145
147{
149 return;
150
151 static constexpr std::string_view focusModeString = "focusMode";
153 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("manual"));
155 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("continuous"));
157}
158
160{
161 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
162 if (caps.isUndefined())
163 return false;
164
165 emscripten::val focusMode = caps["focusMode"];
166 if (focusMode.isUndefined())
167 return false;
168
169 std::vector<std::string> focalModes;
170
171 for (int i = 0; i < focusMode["length"].as<int>(); i++)
172 focalModes.push_back(focusMode[i].as<std::string>());
173
174 // Do we need to take into account focusDistance
175 // it is not always available, and what distance
176 // would be far/near
177
178 bool found = false;
179 switch (mode) {
181 return std::find(focalModes.begin(), focalModes.end(), "continuous") != focalModes.end()
182 || std::find(focalModes.begin(), focalModes.end(), "single-shot")
183 != focalModes.end();
188 break;
190 found = std::find(focalModes.begin(), focalModes.end(), "manual") != focalModes.end();
191 };
192 return found;
193}
194
196{
198 return;
199
200 if (m_wasmTorchMode == mode)
201 return;
202
203 static constexpr std::string_view torchModeString = "torchMode";
204 bool hasChanged = false;
205 switch (mode) {
207 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(false));
208 hasChanged = true;
209 break;
210 case QCamera::TorchOn:
211 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(true));
212 hasChanged = true;
213 break;
215 break;
216 };
217 m_wasmTorchMode = mode;
218 if (hasChanged)
219 torchModeChanged(m_wasmTorchMode);
220}
221
223{
224 if (!m_cameraIsReady)
225 return false;
226
227 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
228 if (caps.isUndefined())
229 return false;
230
231 emscripten::val exposureMode = caps["torch"];
232 if (exposureMode.isUndefined())
233 return false;
234
235 return (mode != QCamera::TorchAuto);
236}
237
239{
240 // TODO manually come up with exposureTime values ?
242 return;
243
244 if (m_wasmExposureMode == mode)
245 return;
246
247 bool hasChanged = false;
248 static constexpr std::string_view exposureModeString = "exposureMode";
249 switch (mode) {
251 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("manual"));
252 hasChanged = true;
253 break;
255 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("continuous"));
256 hasChanged = true;
257 break;
258 default:
259 break;
260 };
261
262 if (hasChanged) {
263 m_wasmExposureMode = mode;
264 exposureModeChanged(m_wasmExposureMode);
265 }
266}
267
269{
270 if (!m_cameraIsReady)
271 return false;
272
273 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
274 if (caps.isUndefined())
275 return false;
276
277 emscripten::val exposureMode = caps["exposureMode"];
278 if (exposureMode.isUndefined())
279 return false;
280
281 std::vector<std::string> exposureModes;
282
283 for (int i = 0; i < exposureMode["length"].as<int>(); i++)
284 exposureModes.push_back(exposureMode[i].as<std::string>());
285
286 bool found = false;
287 switch (mode) {
289 found = std::find(exposureModes.begin(), exposureModes.end(), "continuous")
290 != exposureModes.end();
291 break;
293 found = std::find(exposureModes.begin(), exposureModes.end(), "manual")
294 != exposureModes.end();
295 break;
296 default:
297 break;
298 };
299
300 return found;
301}
302
304{
305 if (!m_cameraIsReady)
306 return;
307
308 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
309 if (caps.isUndefined())
310 return;
311
312 emscripten::val exposureComp = caps["exposureCompensation"];
313 if (exposureComp.isUndefined())
314 return;
315 if (m_wasmExposureCompensation == bias)
316 return;
317
318 static constexpr std::string_view exposureCompensationModeString = "exposureCompensation";
319 m_cameraOutput->setDeviceSetting(exposureCompensationModeString.data(), emscripten::val(bias));
320 m_wasmExposureCompensation = bias;
321 emit exposureCompensationChanged(m_wasmExposureCompensation);
322}
323
325{
326 if (m_wasmExposureTime == secs)
327 return;
328
329 if (!m_cameraIsReady)
330 return;
331
332 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
333 emscripten::val exposureTime = caps["exposureTime"];
334 if (exposureTime.isUndefined())
335 return;
336 static constexpr std::string_view exposureTimeString = "exposureTime";
337 m_cameraOutput->setDeviceSetting(exposureTimeString.data(), emscripten::val(secs));
338 m_wasmExposureTime = secs;
339 emit exposureTimeChanged(m_wasmExposureTime);
340}
341
343{
344 if (!m_cameraIsReady)
345 return 0;
346
347 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
348 if (caps.isUndefined())
349 return false;
350
351 emscripten::val isoSpeed = caps["iso"];
352 if (isoSpeed.isUndefined())
353 return 0;
354
355 return isoSpeed.as<double>();
356}
357
359{
360 if (!m_cameraIsReady)
361 return;
362
363 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
364 if (caps.isUndefined())
365 return;
366
367 emscripten::val isoSpeed = caps["iso"];
368 if (isoSpeed.isUndefined())
369 return;
370 if (m_wasmIsoSensitivity == sens)
371 return;
372 static constexpr std::string_view isoString = "iso";
373 m_cameraOutput->setDeviceSetting(isoString.data(), emscripten::val(sens));
374 m_wasmIsoSensitivity = sens;
375 emit isoSensitivityChanged(m_wasmIsoSensitivity);
376}
377
379{
380 if (!m_cameraIsReady)
381 return false;
382
383 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
384 if (caps.isUndefined())
385 return false;
386
387 emscripten::val whiteBalanceMode = caps["whiteBalanceMode"];
388 if (whiteBalanceMode.isUndefined())
389 return false;
390
392 return true;
393
394 return false;
395}
396
398{
400 return;
401
402 if (m_wasmWhiteBalanceMode == mode)
403 return;
404
405 bool hasChanged = false;
406 static constexpr std::string_view whiteBalanceModeString = "whiteBalanceMode";
407 switch (mode) {
409 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val("auto"));
410 hasChanged = true;
411 break;
413 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val("manual"));
414 hasChanged = true;
415 break;
416 default:
417 break;
418 };
419
420 if (hasChanged) {
421 m_wasmWhiteBalanceMode = mode;
422 emit whiteBalanceModeChanged(m_wasmWhiteBalanceMode);
423 }
424}
425
427{
428 if (!m_cameraIsReady)
429 return;
430
431 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
432 if (caps.isUndefined())
433 return;
434
435 emscripten::val whiteBalanceMode = caps["colorTemperature"];
436 if (whiteBalanceMode.isUndefined())
437 return;
438 if(m_wasmColorTemperature == temperature)
439 return;
440
441 static constexpr std::string_view colorBalanceString = "colorTemperature";
442 m_cameraOutput->setDeviceSetting(colorBalanceString.data(), emscripten::val(temperature));
443 m_wasmColorTemperature = temperature;
444 colorTemperatureChanged(m_wasmColorTemperature);
445}
446
447void QWasmCamera::createCamera(const QCameraDevice &camera)
448{
449 m_cameraOutput->addCameraSourceElement(camera.id().toStdString());
450}
451
452void QWasmCamera::updateCameraFeatures()
453{
454 if (!m_cameraIsReady)
455 return;
456
457 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
458 if (caps.isUndefined())
459 return;
460
461 QCamera::Features cameraFeatures;
462
463 if (!caps["colorTemperature"].isUndefined())
464 cameraFeatures |= QCamera::Feature::ColorTemperature;
465
466 if (!caps["exposureCompensation"].isUndefined())
468
469 if (!caps["iso"].isUndefined())
470 cameraFeatures |= QCamera::Feature::IsoSensitivity;
471
472 if (!caps["exposureTime"].isUndefined())
473 cameraFeatures |= QCamera::Feature::ManualExposureTime;
474
475 if (!caps["focusDistance"].isUndefined())
476 cameraFeatures |= QCamera::Feature::FocusDistance;
477
478 supportedFeaturesChanged(cameraFeatures);
479}
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
The QCameraDevice class provides general information about camera devices.
QByteArray id
\qmlproperty string QtMultimedia::cameraDevice::id
The QCameraFormat class describes a video format supported by a camera device. \inmodule QtMultimedia...
The QCamera class provides interface for system camera devices.
Definition qcamera.h:28
WhiteBalanceMode
\value WhiteBalanceAuto Auto white balance mode.
Definition qcamera.h:112
@ WhiteBalanceManual
Definition qcamera.h:114
@ WhiteBalanceAuto
Definition qcamera.h:113
TorchMode
\value TorchOff Torch is Off.
Definition qcamera.h:84
@ TorchOn
Definition qcamera.h:86
@ TorchAuto
Definition qcamera.h:87
@ TorchOff
Definition qcamera.h:85
FocusMode
\value FocusModeAuto Continuous auto focus mode.
Definition qcamera.h:67
@ FocusModeAutoNear
Definition qcamera.h:69
@ FocusModeInfinity
Definition qcamera.h:72
@ FocusModeAutoFar
Definition qcamera.h:70
@ FocusModeAuto
Definition qcamera.h:68
@ FocusModeManual
Definition qcamera.h:73
@ FocusModeHyperfocal
Definition qcamera.h:71
ExposureMode
\value ExposureAuto Automatic mode.
Definition qcamera.h:91
@ ExposureManual
Definition qcamera.h:93
@ ExposureAuto
Definition qcamera.h:92
@ CameraError
Definition qcamera.h:63
QList< QCameraDevice > videoInputs
\qmlproperty list<cameraDevice> QtMultimedia::MediaDevices::videoInputs Contains a list of cameras on...
void isoSensitivityChanged(int iso)
void updateError(QCamera::Error error, const QString &errorString)
void torchModeChanged(QCamera::TorchMode mode)
QCamera::WhiteBalanceMode whiteBalanceMode() const
void focusModeChanged(QCamera::FocusMode mode)
void exposureCompensationChanged(float compensation)
void whiteBalanceModeChanged(QCamera::WhiteBalanceMode mode)
QCamera::FocusMode focusMode() const
virtual float exposureTime() const
QCamera::ExposureMode exposureMode() const
void colorTemperatureChanged(int temperature)
void exposureModeChanged(QCamera::ExposureMode mode)
void supportedFeaturesChanged(QCamera::Features)
QCameraFormat m_cameraFormat
void exposureTimeChanged(float speed)
static QPlatformMediaIntegration * instance()
void activeChanged(bool)
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
Definition qsize.h:25
bool singleShot
whether the timer is a single-shot timer
Definition qtimer.h:22
The QVideoSink class represents a generic sink for video data.
Definition qvideosink.h:22
void cameraIsReady()
bool isTorchModeSupported(QCamera::TorchMode mode) const override
void setManualExposureTime(float) override
void setExposureCompensation(float bias) override
void setColorTemperature(int temperature) override
int isoSensitivity() const override
void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override
void setTorchMode(QCamera::TorchMode mode) override
bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override
void setExposureMode(QCamera::ExposureMode mode) override
bool setCameraFormat(const QCameraFormat &format) override
void setManualIsoSensitivity(int) override
void setFocusMode(QCamera::FocusMode mode) override
void setActive(bool active) override
bool isFocusModeSupported(QCamera::FocusMode mode) const override
bool isActive() const override
void setCaptureSession(QPlatformMediaCaptureSession *session) override
bool isExposureModeSupported(QCamera::ExposureMode mode) const override
void setCamera(const QCameraDevice &camera) override
void addCameraSourceElement(const std::string &id)
void updateVideoElementGeometry(const QRect &windowGeometry)
bool setDeviceSetting(const std::string &key, emscripten::val value)
emscripten::val getDeviceCapabilities()
void setVideoMode(QWasmVideoOutput::WasmVideoMode mode)
void createVideoElement(const std::string &id)
void setSurface(QVideoSink *surface)
void createOffscreenElement(const QSize &offscreenSize)
QCamera * camera
Definition camera.cpp:19
#define Q_FUNC_INFO
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
GLenum mode
GLint GLsizei GLsizei GLenum format
GLfloat bias
GLsizei GLenum GLboolean sink
#define QStringLiteral(str)
#define emit
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)