Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
6#include <qcameradevice.h>
7#include <private/qplatformvideosink_p.h>
8#include <private/qplatformvideodevices_p.h>
9#include <private/qmemoryvideobuffer_p.h>
10#include <private/qvideotexturehelper_p.h>
11#include <private/qwasmmediadevices_p.h>
12
14#include <common/qwasmvideooutput_p.h>
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
27 : QPlatformCamera(camera),
28 m_cameraOutput(new QWasmVideoOutput),
29 m_cameraIsReady(false)
30{
31 connect(this, &QWasmCamera::cameraIsReady, this, [this]() {
32 m_cameraIsReady = true;
33 if (m_cameraShouldStartActive) {
34 QTimer::singleShot(50, this, [this]() {
35 setActive(true);
36 });
37 }
38 });
39}
40
41QWasmCamera::~QWasmCamera() = default;
42
43bool QWasmCamera::isActive() const
44{
45 return m_cameraActive;
46}
47
48void QWasmCamera::setActive(bool active)
49{
50 if (m_cameraActive == active)
51 return;
52 if (!m_CaptureSession) {
53 updateError(QCamera::CameraError, QStringLiteral("video surface error"));
54 m_shouldBeActive = true;
55 return;
56 }
57 if (m_cameraActive && !active)
58 m_cameraOutput->stop();
59
60 m_shouldBeActive = active;
61
62 if (!m_cameraIsReady) {
63 m_cameraShouldStartActive = true;
64 return;
65 }
66
67 QVideoSink *sink = m_CaptureSession->videoSink();
68 if (!sink) {
69 qWarning() << Q_FUNC_INFO << "sink not ready";
70 return;
71 }
72
73 m_cameraOutput->setSurface(m_CaptureSession->videoSink());
74 m_cameraActive = active;
75 m_shouldBeActive = false;
76
77 if (active)
78 updateCameraFeatures();
79 emit activeChanged(active);
80 if (m_CaptureSession->imageCapture()) {
81 if (active) {
82 m_readyChangedConnection = connect(cameraOutput(), &QWasmVideoOutput::readyChanged, this, [this] () {
83 m_CaptureSession->setReadyForCapture(true);
84 });
85 }
86 }
87 if (!active && m_readyChangedConnection) {
88 QObject::disconnect(m_readyChangedConnection);
89 }
90
91 if (m_cameraActive)
92 m_cameraOutput->start();
93}
94
95void QWasmCamera::setCamera(const QCameraDevice &camera)
96{
97 if (camera.id().isEmpty() || (m_cameraDev.id() == camera.id())) {
98 return;
99 }
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->doElementCallbacks();
107 m_cameraOutput->createOffscreenElement(initialSize);
108 m_cameraOutput->updateVideoElementGeometry(initialRect);
109
110 const auto cameras = QMediaDevices::videoInputs();
111
112 if (std::find(cameras.begin(), cameras.end(), camera) != cameras.end()) {
113 m_cameraDev = camera;
114 createCamera(m_cameraDev);
115 emit cameraIsReady();
116 return;
117 }
118
119 if (cameras.count() > 0) {
120 m_cameraDev = camera;
121 createCamera(m_cameraDev);
122 emit cameraIsReady();
123 } else {
124 updateError(QCamera::CameraError, QStringLiteral("Failed to find a camera"));
125 }
126}
127
128bool QWasmCamera::setCameraFormat(const QCameraFormat &format)
129{
130 m_cameraFormat = format;
131
132 return true;
133}
134
135void QWasmCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
136{
137 QWasmMediaCaptureSession *captureSession = static_cast<QWasmMediaCaptureSession *>(session);
138 if (m_CaptureSession == captureSession)
139 return;
140
141 m_CaptureSession = captureSession;
142
143 if (m_shouldBeActive)
144 setActive(true);
145}
146
147void QWasmCamera::setFocusMode(QCamera::FocusMode mode)
148{
149 if (!isFocusModeSupported(mode))
150 return;
151
152 static constexpr std::string_view focusModeString = "focusMode";
153 if (mode == QCamera::FocusModeManual)
154 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("manual"));
155 if (mode == QCamera::FocusModeAuto)
156 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("continuous"));
157 focusModeChanged(mode);
158}
159
160bool QWasmCamera::isFocusModeSupported(QCamera::FocusMode mode) const
161{
162 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
163 if (caps.isUndefined())
164 return false;
165
166 emscripten::val focusMode = caps["focusMode"];
167 if (focusMode.isUndefined())
168 return false;
169
170 std::vector<std::string> focalModes;
171
172 for (int i = 0; i < focusMode["length"].as<int>(); i++)
173 focalModes.push_back(focusMode[i].as<std::string>());
174
175 // Do we need to take into account focusDistance
176 // it is not always available, and what distance
177 // would be far/near
178
179 bool found = false;
180 switch (mode) {
181 case QCamera::FocusModeAuto:
182 return std::find(focalModes.begin(), focalModes.end(), "continuous") != focalModes.end()
183 || std::find(focalModes.begin(), focalModes.end(), "single-shot")
184 != focalModes.end();
185 case QCamera::FocusModeAutoNear:
186 case QCamera::FocusModeAutoFar:
187 case QCamera::FocusModeHyperfocal:
188 case QCamera::FocusModeInfinity:
189 break;
190 case QCamera::FocusModeManual:
191 found = std::find(focalModes.begin(), focalModes.end(), "manual") != focalModes.end();
192 };
193 return found;
194}
195
196void QWasmCamera::setTorchMode(QCamera::TorchMode mode)
197{
198 if (!isTorchModeSupported(mode))
199 return;
200
201 if (m_wasmTorchMode == mode)
202 return;
203
204 static constexpr std::string_view torchModeString = "torchMode";
205 bool hasChanged = false;
206 switch (mode) {
207 case QCamera::TorchOff:
208 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(false));
209 hasChanged = true;
210 break;
211 case QCamera::TorchOn:
212 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(true));
213 hasChanged = true;
214 break;
215 case QCamera::TorchAuto:
216 break;
217 };
218 m_wasmTorchMode = mode;
219 if (hasChanged)
220 torchModeChanged(m_wasmTorchMode);
221}
222
223bool QWasmCamera::isTorchModeSupported(QCamera::TorchMode mode) const
224{
225 if (!m_cameraIsReady)
226 return false;
227
228 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
229 if (caps.isUndefined())
230 return false;
231
232 emscripten::val exposureMode = caps["torch"];
233 if (exposureMode.isUndefined())
234 return false;
235
236 return (mode != QCamera::TorchAuto);
237}
238
239void QWasmCamera::setExposureMode(QCamera::ExposureMode mode)
240{
241 // TODO manually come up with exposureTime values ?
242 if (!isExposureModeSupported(mode))
243 return;
244
245 if (m_wasmExposureMode == mode)
246 return;
247
248 bool hasChanged = false;
249 static constexpr std::string_view exposureModeString = "exposureMode";
250 switch (mode) {
251 case QCamera::ExposureManual:
252 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("manual"));
253 hasChanged = true;
254 break;
255 case QCamera::ExposureAuto:
256 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("continuous"));
257 hasChanged = true;
258 break;
259 default:
260 break;
261 };
262
263 if (hasChanged) {
264 m_wasmExposureMode = mode;
265 exposureModeChanged(m_wasmExposureMode);
266 }
267}
268
269bool QWasmCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
270{
271 if (!m_cameraIsReady)
272 return false;
273
274 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
275 if (caps.isUndefined())
276 return false;
277
278 emscripten::val exposureMode = caps["exposureMode"];
279 if (exposureMode.isUndefined())
280 return false;
281
282 std::vector<std::string> exposureModes;
283
284 for (int i = 0; i < exposureMode["length"].as<int>(); i++)
285 exposureModes.push_back(exposureMode[i].as<std::string>());
286
287 bool found = false;
288 switch (mode) {
289 case QCamera::ExposureAuto:
290 found = std::find(exposureModes.begin(), exposureModes.end(), "continuous")
291 != exposureModes.end();
292 break;
293 case QCamera::ExposureManual:
294 found = std::find(exposureModes.begin(), exposureModes.end(), "manual")
295 != exposureModes.end();
296 break;
297 default:
298 break;
299 };
300
301 return found;
302}
303
305{
306 if (!m_cameraIsReady)
307 return;
308
309 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
310 if (caps.isUndefined())
311 return;
312
313 emscripten::val exposureComp = caps["exposureCompensation"];
314 if (exposureComp.isUndefined())
315 return;
316 if (m_wasmExposureCompensation == bias)
317 return;
318
319 static constexpr std::string_view exposureCompensationModeString = "exposureCompensation";
320 m_cameraOutput->setDeviceSetting(exposureCompensationModeString.data(), emscripten::val(bias));
321 m_wasmExposureCompensation = bias;
322 emit exposureCompensationChanged(m_wasmExposureCompensation);
323}
324
326{
327 if (m_wasmExposureTime == secs)
328 return;
329
330 if (!m_cameraIsReady)
331 return;
332
333 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
334 emscripten::val exposureTime = caps["exposureTime"];
335 if (exposureTime.isUndefined())
336 return;
337 static constexpr std::string_view exposureTimeString = "exposureTime";
338 m_cameraOutput->setDeviceSetting(exposureTimeString.data(), emscripten::val(secs));
339 m_wasmExposureTime = secs;
340 emit exposureTimeChanged(m_wasmExposureTime);
341}
342
344{
345 if (!m_cameraIsReady)
346 return 0;
347
348 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
349 if (caps.isUndefined())
350 return false;
351
352 emscripten::val isoSpeed = caps["iso"];
353 if (isoSpeed.isUndefined())
354 return 0;
355
356 return isoSpeed.as<double>();
357}
358
360{
361 if (!m_cameraIsReady)
362 return;
363
364 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
365 if (caps.isUndefined())
366 return;
367
368 emscripten::val isoSpeed = caps["iso"];
369 if (isoSpeed.isUndefined())
370 return;
371 if (m_wasmIsoSensitivity == sens)
372 return;
373 static constexpr std::string_view isoString = "iso";
374 m_cameraOutput->setDeviceSetting(isoString.data(), emscripten::val(sens));
375 m_wasmIsoSensitivity = sens;
376 emit isoSensitivityChanged(m_wasmIsoSensitivity);
377}
378
379bool QWasmCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
380{
381 if (!m_cameraIsReady)
382 return false;
383
384 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
385 if (caps.isUndefined())
386 return false;
387
388 emscripten::val whiteBalanceMode = caps["whiteBalanceMode"];
389 if (whiteBalanceMode.isUndefined())
390 return false;
391
392 if (mode == QCamera::WhiteBalanceAuto || mode == QCamera::WhiteBalanceManual)
393 return true;
394
395 return false;
396}
397
398void QWasmCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
399{
400 if (!isWhiteBalanceModeSupported(mode))
401 return;
402
403 if (m_wasmWhiteBalanceMode == mode)
404 return;
405
406 bool hasChanged = false;
407 static constexpr std::string_view whiteBalanceModeString = "whiteBalanceMode";
408 switch (mode) {
409 case QCamera::WhiteBalanceAuto:
410 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val("auto"));
411 hasChanged = true;
412 break;
413 case QCamera::WhiteBalanceManual:
414 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val("manual"));
415 hasChanged = true;
416 break;
417 default:
418 break;
419 };
420
421 if (hasChanged) {
422 m_wasmWhiteBalanceMode = mode;
423 emit whiteBalanceModeChanged(m_wasmWhiteBalanceMode);
424 }
425}
426
427void QWasmCamera::setColorTemperature(int temperature)
428{
429 if (!m_cameraIsReady)
430 return;
431
432 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
433 if (caps.isUndefined())
434 return;
435
436 emscripten::val whiteBalanceMode = caps["colorTemperature"];
437 if (whiteBalanceMode.isUndefined())
438 return;
439 if(m_wasmColorTemperature == temperature)
440 return;
441
442 static constexpr std::string_view colorBalanceString = "colorTemperature";
443 m_cameraOutput->setDeviceSetting(colorBalanceString.data(), emscripten::val(temperature));
444 m_wasmColorTemperature = temperature;
445 colorTemperatureChanged(m_wasmColorTemperature);
446}
447
448void QWasmCamera::createCamera(const QCameraDevice &camera)
449{
450 m_cameraOutput->addCameraSourceElement(camera.id().toStdString());
451}
452
453void QWasmCamera::updateCameraFeatures()
454{
455 if (!m_cameraIsReady)
456 return;
457
458 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
459 if (caps.isUndefined())
460 return;
461
462 QCamera::Features cameraFeatures;
463
464 if (!caps["colorTemperature"].isUndefined())
465 cameraFeatures |= QCamera::Feature::ColorTemperature;
466
467 if (!caps["exposureCompensation"].isUndefined())
468 cameraFeatures |= QCamera::Feature::ExposureCompensation;
469
470 if (!caps["iso"].isUndefined())
471 cameraFeatures |= QCamera::Feature::IsoSensitivity;
472
473 if (!caps["exposureTime"].isUndefined())
474 cameraFeatures |= QCamera::Feature::ManualExposureTime;
475
476 if (!caps["focusDistance"].isUndefined())
477 cameraFeatures |= QCamera::Feature::FocusDistance;
478
479 supportedFeaturesChanged(cameraFeatures);
480}
The QCamera class provides interface for system camera devices.
Definition qcamera.h:25
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
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")