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/qcameradevice_p.h>
11#include <private/qvideotexturehelper_p.h>
12#include <private/qwasmmediadevices_p.h>
13#include <QtMultimedia/private/qmultimedia_ranges_p.h>
16#include <common/qwasmvideooutput_p.h>
18#include <emscripten/val.h>
19#include <emscripten/bind.h>
20#include <emscripten/html5.h>
24#include <private/qstdweb_p.h>
28namespace ranges = QtMultimediaPrivate::ranges;
31 : QPlatformCamera(camera),
32 m_cameraOutput(
new QWasmVideoOutput),
33 m_cameraIsReady(
false)
35 connect(
this, &QWasmCamera::cameraIsReady,
this, [
this]() {
36 m_cameraIsReady =
true;
37 if (m_cameraShouldStartActive) {
38 QTimer::singleShot(50,
this, [
this]() {
49 return m_cameraActive;
54 if (m_cameraActive == active)
56 if (!m_CaptureSession) {
57 updateError(QCamera::CameraError, QStringLiteral(
"video surface error"));
58 m_shouldBeActive =
true;
61 if (m_cameraActive && !active)
62 m_cameraOutput->stop();
64 m_shouldBeActive = active;
66 if (!m_cameraIsReady) {
67 m_cameraShouldStartActive =
true;
71 QVideoSink *sink = m_CaptureSession->videoSink();
73 qWarning() << Q_FUNC_INFO <<
"sink not ready";
77 m_cameraOutput->setSurface(m_CaptureSession->videoSink());
78 m_cameraActive = active;
79 m_shouldBeActive =
false;
82 updateCameraFeatures();
83 emit activeChanged(active);
84 if (m_CaptureSession->imageCapture()) {
86 m_readyChangedConnection = connect(cameraOutput(), &QWasmVideoOutput::readyChanged,
this, [
this] () {
87 m_CaptureSession->setReadyForCapture(
true);
91 if (!active && m_readyChangedConnection) {
92 QObject::disconnect(m_readyChangedConnection);
96 m_cameraOutput->start();
101 if (camera.id().isEmpty() || (m_cameraDev.id() == camera.id())) {
105 const bool wasActive = m_cameraActive;
107 m_cameraOutput->stop();
109 m_cameraOutput->setVideoMode(QWasmVideoOutput::Camera);
111 constexpr QSize initialSize(0, 0);
112 constexpr QRect initialRect(QPoint(0, 0), initialSize);
113 m_cameraOutput->createVideoElement(
114 QUuid::createUuid().toString(QUuid::WithoutBraces).toStdString());
115 m_cameraOutput->doElementCallbacks();
116 m_cameraOutput->updateVideoElementGeometry(initialRect);
118 const auto cameras = QMediaDevices::videoInputs();
120 if (ranges::contains(cameras, camera)) {
121 m_cameraDev = camera;
122 createCamera(m_cameraDev);
123 emit cameraIsReady();
125 m_cameraOutput->start();
129 if (cameras.count() > 0) {
130 m_cameraDev = camera;
131 createCamera(m_cameraDev);
132 emit cameraIsReady();
134 m_cameraOutput->start();
136 updateError(QCamera::CameraError, QStringLiteral(
"Failed to find a camera"));
142 m_cameraFormat = format;
143 m_cameraOutput->setVideoConstraints(format.resolution(), format.minFrameRate(), format.maxFrameRate());
150 if (m_CaptureSession == captureSession)
153 m_CaptureSession = captureSession;
155 if (m_shouldBeActive)
161 if (!isFocusModeSupported(mode))
164 static constexpr std::string_view focusModeString =
"focusMode";
165 if (mode == QCamera::FocusModeManual)
166 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val(
"manual"));
167 if (mode == QCamera::FocusModeAuto)
168 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val(
"continuous"));
169 focusModeChanged(mode);
174 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
175 if (caps.isUndefined())
178 emscripten::val focusMode = caps[
"focusMode"];
179 if (focusMode.isUndefined())
182 std::vector<std::string> focalModes;
184 for (
int i = 0; i < focusMode[
"length"].as<
int>(); i++)
185 focalModes.push_back(focusMode[i].as<std::string>());
193 case QCamera::FocusModeAuto:
194 return ranges::contains(focalModes,
"continuous")
195 || ranges::contains(focalModes,
"single-shot");
196 case QCamera::FocusModeAutoNear:
197 case QCamera::FocusModeAutoFar:
198 case QCamera::FocusModeHyperfocal:
199 case QCamera::FocusModeInfinity:
201 case QCamera::FocusModeManual:
202 found = ranges::contains(focalModes,
"manual");
209 if (!isTorchModeSupported(mode))
212 if (m_wasmTorchMode == mode)
215 static constexpr std::string_view torchModeString =
"torchMode";
216 bool hasChanged =
false;
218 case QCamera::TorchOff:
219 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(
false));
222 case QCamera::TorchOn:
223 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(
true));
226 case QCamera::TorchAuto:
229 m_wasmTorchMode = mode;
231 torchModeChanged(m_wasmTorchMode);
236 if (!m_cameraIsReady)
239 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
240 if (caps.isUndefined())
243 emscripten::val exposureMode = caps[
"torch"];
244 if (exposureMode.isUndefined())
247 return (mode != QCamera::TorchAuto);
253 if (!isExposureModeSupported(mode))
256 if (m_wasmExposureMode == mode)
259 bool hasChanged =
false;
260 static constexpr std::string_view exposureModeString =
"exposureMode";
262 case QCamera::ExposureManual:
263 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val(
"manual"));
266 case QCamera::ExposureAuto:
267 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val(
"continuous"));
275 m_wasmExposureMode = mode;
276 exposureModeChanged(m_wasmExposureMode);
282 if (!m_cameraIsReady)
285 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
286 if (caps.isUndefined())
289 emscripten::val exposureMode = caps[
"exposureMode"];
290 if (exposureMode.isUndefined())
293 std::vector<std::string> exposureModes;
295 for (
int i = 0; i < exposureMode[
"length"].as<
int>(); i++)
296 exposureModes.push_back(exposureMode[i].as<std::string>());
300 case QCamera::ExposureAuto:
301 found = ranges::contains(exposureModes,
"continuous");
303 case QCamera::ExposureManual:
304 found = ranges::contains(exposureModes,
"manual");
315 if (!m_cameraIsReady)
318 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
319 if (caps.isUndefined())
322 emscripten::val exposureComp = caps[
"exposureCompensation"];
323 if (exposureComp.isUndefined())
325 if (m_wasmExposureCompensation == bias)
328 static constexpr std::string_view exposureCompensationModeString =
"exposureCompensation";
329 m_cameraOutput->setDeviceSetting(exposureCompensationModeString.data(), emscripten::val(bias));
330 m_wasmExposureCompensation = bias;
331 emit exposureCompensationChanged(m_wasmExposureCompensation);
336 if (m_wasmExposureTime == secs)
339 if (!m_cameraIsReady)
342 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
343 emscripten::val exposureTime = caps[
"exposureTime"];
344 if (exposureTime.isUndefined())
346 static constexpr std::string_view exposureTimeString =
"exposureTime";
347 m_cameraOutput->setDeviceSetting(exposureTimeString.data(), emscripten::val(secs));
348 m_wasmExposureTime = secs;
349 emit exposureTimeChanged(m_wasmExposureTime);
354 if (!m_cameraIsReady)
357 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
358 if (caps.isUndefined())
361 emscripten::val isoSpeed = caps[
"iso"];
362 if (isoSpeed.isUndefined())
365 return isoSpeed.as<
double>();
370 if (!m_cameraIsReady)
373 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
374 if (caps.isUndefined())
377 emscripten::val isoSpeed = caps[
"iso"];
378 if (isoSpeed.isUndefined())
380 if (m_wasmIsoSensitivity == sens)
382 static constexpr std::string_view isoString =
"iso";
383 m_cameraOutput->setDeviceSetting(isoString.data(), emscripten::val(sens));
384 m_wasmIsoSensitivity = sens;
385 emit isoSensitivityChanged(m_wasmIsoSensitivity);
390 if (!m_cameraIsReady)
393 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
394 if (caps.isUndefined())
397 emscripten::val whiteBalanceMode = caps[
"whiteBalanceMode"];
398 if (whiteBalanceMode.isUndefined())
401 if (mode == QCamera::WhiteBalanceAuto || mode == QCamera::WhiteBalanceManual)
409 if (!isWhiteBalanceModeSupported(mode))
412 if (m_wasmWhiteBalanceMode == mode)
415 bool hasChanged =
false;
416 static constexpr std::string_view whiteBalanceModeString =
"whiteBalanceMode";
418 case QCamera::WhiteBalanceAuto:
419 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val(
"auto"));
422 case QCamera::WhiteBalanceManual:
423 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val(
"manual"));
431 m_wasmWhiteBalanceMode = mode;
432 emit whiteBalanceModeChanged(m_wasmWhiteBalanceMode);
438 if (!m_cameraIsReady)
441 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
442 if (caps.isUndefined())
445 emscripten::val whiteBalanceMode = caps[
"colorTemperature"];
446 if (whiteBalanceMode.isUndefined())
448 if(m_wasmColorTemperature == temperature)
451 static constexpr std::string_view colorBalanceString =
"colorTemperature";
452 m_cameraOutput->setDeviceSetting(colorBalanceString.data(), emscripten::val(temperature));
453 m_wasmColorTemperature = temperature;
454 colorTemperatureChanged(m_wasmColorTemperature);
457void QWasmCamera::createCamera(
const QCameraDevice &camera)
459 m_cameraOutput->addCameraSourceElement(camera.id().toStdString());
464 if (!m_cameraIsReady)
467 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
468 if (caps.isUndefined())
471 QCamera::Features cameraFeatures;
473 if (!caps[
"colorTemperature"].isUndefined())
474 cameraFeatures |= QCamera::Feature::ColorTemperature;
476 if (!caps[
"exposureCompensation"].isUndefined())
477 cameraFeatures |= QCamera::Feature::ExposureCompensation;
479 if (!caps[
"iso"].isUndefined())
480 cameraFeatures |= QCamera::Feature::IsoSensitivity;
482 if (!caps[
"exposureTime"].isUndefined())
483 cameraFeatures |= QCamera::Feature::ManualExposureTime;
485 if (!caps[
"focusDistance"].isUndefined())
486 cameraFeatures |= QCamera::Feature::FocusDistance;
488 supportedFeaturesChanged(cameraFeatures);
489 updateVideoFormats(caps);
492void QWasmCamera::updateVideoFormats(
const emscripten::val &cababilities)
497 emscripten::val widthCapabilities = cababilities[
"width"];
498 emscripten::val heightCapabilities = cababilities[
"height"];
499 emscripten::val frameRateCapabilities = cababilities[
"frameRate"];
501 if (widthCapabilities.isUndefined() || heightCapabilities.isUndefined()
502 || frameRateCapabilities.isUndefined())
505 const int maxWidth = widthCapabilities[
"max"].as<
int>();
506 const int maxHeight = heightCapabilities[
"max"].as<
int>();
507 const float minFrameRate = frameRateCapabilities[
"min"].as<
double>();
508 const float maxFrameRate = frameRateCapabilities[
"max"].as<
double>();
510 static const QSize standardResolutions[] = {
518 static const float standardFrameRates[] = { 15.f, 24.f, 25.f, 29.97f, 30.f,
519 60.f, 100.f, 120.f };
521 QList<QCameraFormat> formats;
522 for (
const QSize &resolution : standardResolutions) {
523 if (resolution.width() > maxWidth || resolution.height() > maxHeight)
525 for (
float rate : standardFrameRates) {
526 if (rate < minFrameRate || rate > maxFrameRate + 0.5f)
528 auto *oneFormat =
new QCameraFormatPrivate{
530 QVideoFrameFormat::Format_RGBA8888,
534 formats << oneFormat->create();
538 if (formats.isEmpty())
542 devicePrivate->videoFormats = formats;
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")