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#include <QtMultimedia/private/qmultimedia_ranges_p.h>
13
15#include <common/qwasmvideooutput_p.h>
16
17#include <emscripten/val.h>
18#include <emscripten/bind.h>
19#include <emscripten/html5.h>
20#include <QUuid>
21#include <QTimer>
22
23#include <private/qstdweb_p.h>
24
25Q_LOGGING_CATEGORY(qWasmCamera, "qt.multimedia.wasm.camera")
26
27namespace ranges = QtMultimediaPrivate::ranges;
28
29QWasmCamera::QWasmCamera(QCamera *camera)
30 : QPlatformCamera(camera),
31 m_cameraOutput(new QWasmVideoOutput),
32 m_cameraIsReady(false)
33{
34 connect(this, &QWasmCamera::cameraIsReady, this, [this]() {
35 m_cameraIsReady = true;
36 if (m_cameraShouldStartActive) {
37 QTimer::singleShot(50, this, [this]() {
38 setActive(true);
39 });
40 }
41 });
42}
43
44QWasmCamera::~QWasmCamera() = default;
45
46bool QWasmCamera::isActive() const
47{
48 return m_cameraActive;
49}
50
51void QWasmCamera::setActive(bool active)
52{
53 if (m_cameraActive == active)
54 return;
55 if (!m_CaptureSession) {
56 updateError(QCamera::CameraError, QStringLiteral("video surface error"));
57 m_shouldBeActive = true;
58 return;
59 }
60 if (m_cameraActive && !active)
61 m_cameraOutput->stop();
62
63 m_shouldBeActive = active;
64
65 if (!m_cameraIsReady) {
66 m_cameraShouldStartActive = true;
67 return;
68 }
69
70 QVideoSink *sink = m_CaptureSession->videoSink();
71 if (!sink) {
72 qWarning() << Q_FUNC_INFO << "sink not ready";
73 return;
74 }
75
76 m_cameraOutput->setSurface(m_CaptureSession->videoSink());
77 m_cameraActive = active;
78 m_shouldBeActive = false;
79
80 if (active)
81 updateCameraFeatures();
82 emit activeChanged(active);
83 if (m_CaptureSession->imageCapture()) {
84 if (active) {
85 m_readyChangedConnection = connect(cameraOutput(), &QWasmVideoOutput::readyChanged, this, [this] () {
86 m_CaptureSession->setReadyForCapture(true);
87 });
88 }
89 }
90 if (!active && m_readyChangedConnection) {
91 QObject::disconnect(m_readyChangedConnection);
92 }
93
94 if (m_cameraActive)
95 m_cameraOutput->start();
96}
97
98void QWasmCamera::setCamera(const QCameraDevice &camera)
99{
100 if (camera.id().isEmpty() || (m_cameraDev.id() == camera.id())) {
101 return;
102 }
103
104 m_cameraOutput->setVideoMode(QWasmVideoOutput::Camera);
105
106 constexpr QSize initialSize(0, 0);
107 constexpr QRect initialRect(QPoint(0, 0), initialSize);
108 m_cameraOutput->createVideoElement(camera.id().toStdString()); // videoElementId
109 m_cameraOutput->doElementCallbacks();
110 m_cameraOutput->createOffscreenElement(initialSize);
111 m_cameraOutput->updateVideoElementGeometry(initialRect);
112
113 const auto cameras = QMediaDevices::videoInputs();
114
115 if (ranges::contains(cameras, camera)) {
116 m_cameraDev = camera;
117 createCamera(m_cameraDev);
118 emit cameraIsReady();
119 return;
120 }
121
122 if (cameras.count() > 0) {
123 m_cameraDev = camera;
124 createCamera(m_cameraDev);
125 emit cameraIsReady();
126 } else {
127 updateError(QCamera::CameraError, QStringLiteral("Failed to find a camera"));
128 }
129}
130
131bool QWasmCamera::setCameraFormat(const QCameraFormat &format)
132{
133 m_cameraFormat = format;
134
135 return true;
136}
137
138void QWasmCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
139{
140 QWasmMediaCaptureSession *captureSession = static_cast<QWasmMediaCaptureSession *>(session);
141 if (m_CaptureSession == captureSession)
142 return;
143
144 m_CaptureSession = captureSession;
145
146 if (m_shouldBeActive)
147 setActive(true);
148}
149
150void QWasmCamera::setFocusMode(QCamera::FocusMode mode)
151{
152 if (!isFocusModeSupported(mode))
153 return;
154
155 static constexpr std::string_view focusModeString = "focusMode";
156 if (mode == QCamera::FocusModeManual)
157 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("manual"));
158 if (mode == QCamera::FocusModeAuto)
159 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("continuous"));
160 focusModeChanged(mode);
161}
162
163bool QWasmCamera::isFocusModeSupported(QCamera::FocusMode mode) const
164{
165 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
166 if (caps.isUndefined())
167 return false;
168
169 emscripten::val focusMode = caps["focusMode"];
170 if (focusMode.isUndefined())
171 return false;
172
173 std::vector<std::string> focalModes;
174
175 for (int i = 0; i < focusMode["length"].as<int>(); i++)
176 focalModes.push_back(focusMode[i].as<std::string>());
177
178 // Do we need to take into account focusDistance
179 // it is not always available, and what distance
180 // would be far/near
181
182 bool found = false;
183 switch (mode) {
184 case QCamera::FocusModeAuto:
185 return ranges::contains(focalModes, "continuous")
186 || ranges::contains(focalModes, "single-shot");
187 case QCamera::FocusModeAutoNear:
188 case QCamera::FocusModeAutoFar:
189 case QCamera::FocusModeHyperfocal:
190 case QCamera::FocusModeInfinity:
191 break;
192 case QCamera::FocusModeManual:
193 found = ranges::contains(focalModes, "manual");
194 };
195 return found;
196}
197
198void QWasmCamera::setTorchMode(QCamera::TorchMode mode)
199{
200 if (!isTorchModeSupported(mode))
201 return;
202
203 if (m_wasmTorchMode == mode)
204 return;
205
206 static constexpr std::string_view torchModeString = "torchMode";
207 bool hasChanged = false;
208 switch (mode) {
209 case QCamera::TorchOff:
210 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(false));
211 hasChanged = true;
212 break;
213 case QCamera::TorchOn:
214 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(true));
215 hasChanged = true;
216 break;
217 case QCamera::TorchAuto:
218 break;
219 };
220 m_wasmTorchMode = mode;
221 if (hasChanged)
222 torchModeChanged(m_wasmTorchMode);
223}
224
225bool QWasmCamera::isTorchModeSupported(QCamera::TorchMode mode) const
226{
227 if (!m_cameraIsReady)
228 return false;
229
230 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
231 if (caps.isUndefined())
232 return false;
233
234 emscripten::val exposureMode = caps["torch"];
235 if (exposureMode.isUndefined())
236 return false;
237
238 return (mode != QCamera::TorchAuto);
239}
240
241void QWasmCamera::setExposureMode(QCamera::ExposureMode mode)
242{
243 // TODO manually come up with exposureTime values ?
244 if (!isExposureModeSupported(mode))
245 return;
246
247 if (m_wasmExposureMode == mode)
248 return;
249
250 bool hasChanged = false;
251 static constexpr std::string_view exposureModeString = "exposureMode";
252 switch (mode) {
253 case QCamera::ExposureManual:
254 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("manual"));
255 hasChanged = true;
256 break;
257 case QCamera::ExposureAuto:
258 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("continuous"));
259 hasChanged = true;
260 break;
261 default:
262 break;
263 };
264
265 if (hasChanged) {
266 m_wasmExposureMode = mode;
267 exposureModeChanged(m_wasmExposureMode);
268 }
269}
270
271bool QWasmCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
272{
273 if (!m_cameraIsReady)
274 return false;
275
276 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
277 if (caps.isUndefined())
278 return false;
279
280 emscripten::val exposureMode = caps["exposureMode"];
281 if (exposureMode.isUndefined())
282 return false;
283
284 std::vector<std::string> exposureModes;
285
286 for (int i = 0; i < exposureMode["length"].as<int>(); i++)
287 exposureModes.push_back(exposureMode[i].as<std::string>());
288
289 bool found = false;
290 switch (mode) {
291 case QCamera::ExposureAuto:
292 found = ranges::contains(exposureModes, "continuous");
293 break;
294 case QCamera::ExposureManual:
295 found = ranges::contains(exposureModes, "manual");
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}
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")