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->createOffscreenElement(initialSize);
117 m_cameraOutput->updateVideoElementGeometry(initialRect);
119 const auto cameras = QMediaDevices::videoInputs();
121 if (ranges::contains(cameras, camera)) {
122 m_cameraDev = camera;
123 createCamera(m_cameraDev);
124 emit cameraIsReady();
126 m_cameraOutput->start();
130 if (cameras.count() > 0) {
131 m_cameraDev = camera;
132 createCamera(m_cameraDev);
133 emit cameraIsReady();
135 m_cameraOutput->start();
137 updateError(QCamera::CameraError, QStringLiteral(
"Failed to find a camera"));
143 m_cameraFormat = format;
144 m_cameraOutput->setVideoConstraints(format.resolution(), format.minFrameRate(), format.maxFrameRate());
151 if (m_CaptureSession == captureSession)
154 m_CaptureSession = captureSession;
156 if (m_shouldBeActive)
162 if (!isFocusModeSupported(mode))
165 static constexpr std::string_view focusModeString =
"focusMode";
166 if (mode == QCamera::FocusModeManual)
167 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val(
"manual"));
168 if (mode == QCamera::FocusModeAuto)
169 m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val(
"continuous"));
170 focusModeChanged(mode);
175 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
176 if (caps.isUndefined())
179 emscripten::val focusMode = caps[
"focusMode"];
180 if (focusMode.isUndefined())
183 std::vector<std::string> focalModes;
185 for (
int i = 0; i < focusMode[
"length"].as<
int>(); i++)
186 focalModes.push_back(focusMode[i].as<std::string>());
194 case QCamera::FocusModeAuto:
195 return ranges::contains(focalModes,
"continuous")
196 || ranges::contains(focalModes,
"single-shot");
197 case QCamera::FocusModeAutoNear:
198 case QCamera::FocusModeAutoFar:
199 case QCamera::FocusModeHyperfocal:
200 case QCamera::FocusModeInfinity:
202 case QCamera::FocusModeManual:
203 found = ranges::contains(focalModes,
"manual");
210 if (!isTorchModeSupported(mode))
213 if (m_wasmTorchMode == mode)
216 static constexpr std::string_view torchModeString =
"torchMode";
217 bool hasChanged =
false;
219 case QCamera::TorchOff:
220 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(
false));
223 case QCamera::TorchOn:
224 m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(
true));
227 case QCamera::TorchAuto:
230 m_wasmTorchMode = mode;
232 torchModeChanged(m_wasmTorchMode);
237 if (!m_cameraIsReady)
240 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
241 if (caps.isUndefined())
244 emscripten::val exposureMode = caps[
"torch"];
245 if (exposureMode.isUndefined())
248 return (mode != QCamera::TorchAuto);
254 if (!isExposureModeSupported(mode))
257 if (m_wasmExposureMode == mode)
260 bool hasChanged =
false;
261 static constexpr std::string_view exposureModeString =
"exposureMode";
263 case QCamera::ExposureManual:
264 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val(
"manual"));
267 case QCamera::ExposureAuto:
268 m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val(
"continuous"));
276 m_wasmExposureMode = mode;
277 exposureModeChanged(m_wasmExposureMode);
283 if (!m_cameraIsReady)
286 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
287 if (caps.isUndefined())
290 emscripten::val exposureMode = caps[
"exposureMode"];
291 if (exposureMode.isUndefined())
294 std::vector<std::string> exposureModes;
296 for (
int i = 0; i < exposureMode[
"length"].as<
int>(); i++)
297 exposureModes.push_back(exposureMode[i].as<std::string>());
301 case QCamera::ExposureAuto:
302 found = ranges::contains(exposureModes,
"continuous");
304 case QCamera::ExposureManual:
305 found = ranges::contains(exposureModes,
"manual");
316 if (!m_cameraIsReady)
319 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
320 if (caps.isUndefined())
323 emscripten::val exposureComp = caps[
"exposureCompensation"];
324 if (exposureComp.isUndefined())
326 if (m_wasmExposureCompensation == bias)
329 static constexpr std::string_view exposureCompensationModeString =
"exposureCompensation";
330 m_cameraOutput->setDeviceSetting(exposureCompensationModeString.data(), emscripten::val(bias));
331 m_wasmExposureCompensation = bias;
332 emit exposureCompensationChanged(m_wasmExposureCompensation);
337 if (m_wasmExposureTime == secs)
340 if (!m_cameraIsReady)
343 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
344 emscripten::val exposureTime = caps[
"exposureTime"];
345 if (exposureTime.isUndefined())
347 static constexpr std::string_view exposureTimeString =
"exposureTime";
348 m_cameraOutput->setDeviceSetting(exposureTimeString.data(), emscripten::val(secs));
349 m_wasmExposureTime = secs;
350 emit exposureTimeChanged(m_wasmExposureTime);
355 if (!m_cameraIsReady)
358 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
359 if (caps.isUndefined())
362 emscripten::val isoSpeed = caps[
"iso"];
363 if (isoSpeed.isUndefined())
366 return isoSpeed.as<
double>();
371 if (!m_cameraIsReady)
374 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
375 if (caps.isUndefined())
378 emscripten::val isoSpeed = caps[
"iso"];
379 if (isoSpeed.isUndefined())
381 if (m_wasmIsoSensitivity == sens)
383 static constexpr std::string_view isoString =
"iso";
384 m_cameraOutput->setDeviceSetting(isoString.data(), emscripten::val(sens));
385 m_wasmIsoSensitivity = sens;
386 emit isoSensitivityChanged(m_wasmIsoSensitivity);
391 if (!m_cameraIsReady)
394 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
395 if (caps.isUndefined())
398 emscripten::val whiteBalanceMode = caps[
"whiteBalanceMode"];
399 if (whiteBalanceMode.isUndefined())
402 if (mode == QCamera::WhiteBalanceAuto || mode == QCamera::WhiteBalanceManual)
410 if (!isWhiteBalanceModeSupported(mode))
413 if (m_wasmWhiteBalanceMode == mode)
416 bool hasChanged =
false;
417 static constexpr std::string_view whiteBalanceModeString =
"whiteBalanceMode";
419 case QCamera::WhiteBalanceAuto:
420 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val(
"auto"));
423 case QCamera::WhiteBalanceManual:
424 m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val(
"manual"));
432 m_wasmWhiteBalanceMode = mode;
433 emit whiteBalanceModeChanged(m_wasmWhiteBalanceMode);
439 if (!m_cameraIsReady)
442 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
443 if (caps.isUndefined())
446 emscripten::val whiteBalanceMode = caps[
"colorTemperature"];
447 if (whiteBalanceMode.isUndefined())
449 if(m_wasmColorTemperature == temperature)
452 static constexpr std::string_view colorBalanceString =
"colorTemperature";
453 m_cameraOutput->setDeviceSetting(colorBalanceString.data(), emscripten::val(temperature));
454 m_wasmColorTemperature = temperature;
455 colorTemperatureChanged(m_wasmColorTemperature);
458void QWasmCamera::createCamera(
const QCameraDevice &camera)
460 m_cameraOutput->addCameraSourceElement(camera.id().toStdString());
465 if (!m_cameraIsReady)
468 emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
469 if (caps.isUndefined())
472 QCamera::Features cameraFeatures;
474 if (!caps[
"colorTemperature"].isUndefined())
475 cameraFeatures |= QCamera::Feature::ColorTemperature;
477 if (!caps[
"exposureCompensation"].isUndefined())
478 cameraFeatures |= QCamera::Feature::ExposureCompensation;
480 if (!caps[
"iso"].isUndefined())
481 cameraFeatures |= QCamera::Feature::IsoSensitivity;
483 if (!caps[
"exposureTime"].isUndefined())
484 cameraFeatures |= QCamera::Feature::ManualExposureTime;
486 if (!caps[
"focusDistance"].isUndefined())
487 cameraFeatures |= QCamera::Feature::FocusDistance;
489 supportedFeaturesChanged(cameraFeatures);
490 updateVideoFormats(caps);
493void QWasmCamera::updateVideoFormats(
const emscripten::val &cababilities)
498 emscripten::val widthCapabilities = cababilities[
"width"];
499 emscripten::val heightCapabilities = cababilities[
"height"];
500 emscripten::val frameRateCapabilities = cababilities[
"frameRate"];
502 if (widthCapabilities.isUndefined() || heightCapabilities.isUndefined()
503 || frameRateCapabilities.isUndefined())
506 const int maxWidth = widthCapabilities[
"max"].as<
int>();
507 const int maxHeight = heightCapabilities[
"max"].as<
int>();
508 const float minFrameRate = frameRateCapabilities[
"min"].as<
double>();
509 const float maxFrameRate = frameRateCapabilities[
"max"].as<
double>();
511 static const QSize standardResolutions[] = {
519 static const float standardFrameRates[] = { 15.f, 24.f, 25.f, 29.97f, 30.f,
520 60.f, 100.f, 120.f };
522 QList<QCameraFormat> formats;
523 for (
const QSize &resolution : standardResolutions) {
524 if (resolution.width() > maxWidth || resolution.height() > maxHeight)
526 for (
float rate : standardFrameRates) {
527 if (rate < minFrameRate || rate > maxFrameRate + 0.5f)
529 auto *oneFormat =
new QCameraFormatPrivate{
531 QVideoFrameFormat::Format_RGBA8888,
535 formats << oneFormat->create();
539 if (formats.isEmpty())
542 auto *devicePrivate =
543 const_cast<QCameraDevicePrivate *>(QCameraDevicePrivate::handle(m_cameraDev));
544 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")