Revision control
Copy as Markdown
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwasmmediacapturesession_p.h"
#include "mediacapture/qwasmimagecapture_p.h"
#include "qwasmcamera_p.h"
#include "qwasmscreencapture_p.h"
#include "qwasmwindowcapture_p.h"
#include "qwasmaudioinput_p.h"
#include <QtMultimedia/qaudiodevice.h>
#include <QtMultimedia/private/qcapturablewindow_p.h>
#include <QtMultimedia/private/qplatformaudioinput_p.h>
#include <QtMultimedia/private/qplatformcapturablewindows_p.h>
#include <QtMultimedia/private/qplatformmediaintegration_p.h>
#include <QtMultimedia/private/qwasmmediadevices_p.h>
#include <QtCore/quuid.h>
Q_LOGGING_CATEGORY(qWasmMediaCaptureSession, "qt.multimedia.wasm.capturesession")
QWasmMediaCaptureSession::QWasmMediaCaptureSession() :
QPlatformMediaCaptureSession()
{
}
QWasmMediaCaptureSession::~QWasmMediaCaptureSession() = default;
QPlatformCamera *QWasmMediaCaptureSession::camera()
{
return m_camera;
}
void QWasmMediaCaptureSession::setCamera(QPlatformCamera *camera)
{
if (!camera) {
if (m_camera == nullptr)
return;
m_camera->setActive(false);
m_camera = nullptr;
} else {
QWasmCamera *wasmCamera = static_cast<QWasmCamera *>(camera);
if (m_camera == wasmCamera)
return;
m_camera = wasmCamera;
m_camera->setCaptureSession(this);
}
Q_EMIT cameraChanged();
}
QPlatformImageCapture *QWasmMediaCaptureSession::imageCapture()
{
return m_imageCapture;
}
void QWasmMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCapture)
{
if (m_imageCapture == imageCapture)
return;
if (m_imageCapture)
m_imageCapture->setCaptureSession(nullptr);
m_imageCapture = static_cast<QWasmImageCapture *>(imageCapture);
if (m_imageCapture) {
m_imageCapture->setCaptureSession(this);
if (m_camera && m_camera->isActive())
m_imageCapture->setReadyForCapture(true);
Q_EMIT imageCaptureChanged();
}
}
QPlatformMediaRecorder *QWasmMediaCaptureSession::mediaRecorder()
{
return m_mediaRecorder;
}
void QWasmMediaCaptureSession::setMediaRecorder(QPlatformMediaRecorder *mediaRecorder)
{
if (m_mediaRecorder == mediaRecorder)
return;
if (m_mediaRecorder)
m_mediaRecorder->setCaptureSession(nullptr);
m_mediaRecorder = static_cast<QWasmMediaRecorder *>(mediaRecorder);
if (m_mediaRecorder)
m_mediaRecorder->setCaptureSession(this);
}
void QWasmMediaCaptureSession::setAudioInput(QPlatformAudioInput *input)
{
if (m_audioInput == input)
return;
m_needsAudio = (bool)input;
m_audioInput = input;
}
bool QWasmMediaCaptureSession::hasAudio()
{
return m_needsAudio;
}
void QWasmMediaCaptureSession::setVideoPreview(QVideoSink *sink)
{
if (!sink || m_wasmSink == sink)
return;
m_wasmSink = sink;
}
void QWasmMediaCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
{
if (m_audioOutput == output)
return;
m_audioOutput = output;
}
void QWasmMediaCaptureSession::setReadyForCapture(bool ready)
{
if (m_imageCapture)
m_imageCapture->setReadyForCapture(ready);
}
QPlatformSurfaceCapture *QWasmMediaCaptureSession::screenCapture()
{
return m_screenCapture;
}
void QWasmMediaCaptureSession::setScreenCapture(QPlatformSurfaceCapture *surfaceCapture)
{
m_screenCapture = surfaceCapture;
if (surfaceCapture == nullptr) {
// clear stuff
m_displaySurface = "";
if (m_videoOutput && !m_videoOutput->currentVideoElement().isUndefined()) {
m_videoOutput->stop();
m_videoOutput->removeCurrentVideoElement();
}
m_mediaCaptureStream = emscripten::val::undefined();
} else {
m_displaySurface = "browser";
QWasmScreenCapture *wasmScreenCapture =
static_cast<QWasmScreenCapture *>(m_screenCapture);
wasmScreenCapture->setCaptureSession(this);
}
}
QPlatformSurfaceCapture *QWasmMediaCaptureSession::windowCapture()
{
return m_windowCapture;
}
void QWasmMediaCaptureSession::setWindowCapture(QPlatformSurfaceCapture *surfaceCapture)
{
m_windowCapture = surfaceCapture;
if (surfaceCapture == nullptr) {
// clear stuff
m_displaySurface = "";
if (m_videoOutput && !m_videoOutput->currentVideoElement().isUndefined()) {
m_videoOutput->stop();
m_videoOutput->removeCurrentVideoElement();
}
return;
} else {
// is window capture window or monitor ?
m_displaySurface = "window";
QWasmWindowCapture *wasmWindowCapture =
static_cast<QWasmWindowCapture *>(m_windowCapture);
wasmWindowCapture->setCaptureSession(this);
}
}
void QWasmMediaCaptureSession::setVideoSource(std::string surfacetype)
{
m_displaySurface = surfacetype;
if (surfacetype.empty()) {
// Jane, stop this crazy thing!
if (m_videoOutput && !m_videoOutput->currentVideoElement().isUndefined()) {
m_videoOutput->stop();
m_videoOutput->removeCurrentVideoElement();
}
m_mediaCaptureStream = emscripten::val::undefined();
return;
}
emscripten::val navigator = emscripten::val::global("navigator");
emscripten::val mediaDevices = navigator["mediaDevices"];
if (mediaDevices.isNull() || mediaDevices.isUndefined()) {
qWarning() << "No media devices found";
return;
}
m_videoOutput = std::make_unique<QWasmVideoOutput>();
m_videoOutput->setVideoMode(QWasmVideoOutput::SurfaceCapture);
m_videoOutput->setSurface(m_wasmSink);
emscripten::val constraints = emscripten::val::object();
emscripten::val videoConstraint = emscripten::val::object();
emscripten::val audioConstraint = emscripten::val::object();
videoConstraint.set("displaySurface", m_displaySurface);
videoConstraint.set("resizeMode", std::string("crop-and-scale"));
constraints.set("monitorTypeSurfaces", surfacetype == "window" ?
std::string("exclude") : std::string("include")); // whole monitor
constraints.set("surfaceSwitching", std::string("exclude"));
constraints.set("selfBrowserSurface", "include"); // mirror effect
constraints.set("logicalSurface", true);
audioConstraint.set("systemAudio", true);
constraints.set("video", videoConstraint);
constraints.set("audio", audioConstraint);
// will ask for permissions!
qstdweb::PromiseCallbacks getDisplayMediaCallback{
// default
.thenFunc =
[this](emscripten::val stream) {
qCDebug(qWasmMediaCaptureSession) << "getDisplayMediaSuccess"
<< m_displaySurface;
if (stream.isUndefined() || stream.isNull()) {
qWarning() << "No media stream found error";
return;
}
m_mediaCaptureStream = stream;
// new Video element
constexpr QSize initialSize(0, 0);
constexpr QRect initialRect(QPoint(0, 0), initialSize);
QUuid videoElementId = QUuid::createUuid();
m_videoOutput->createVideoElement(m_displaySurface + "_capture_"
+ videoElementId.toString(QUuid::WithoutBraces).toStdString()); // videoElementId
m_videoOutput->doElementCallbacks();
m_videoOutput->updateVideoElementGeometry(initialRect);
// set video element src to this
if (m_displaySurface == "window") {
QWasmWindowCapture *wasmWindowCapture =
static_cast<QWasmWindowCapture *>(m_windowCapture);
wasmWindowCapture->setVideoOutput(m_videoOutput.get());
QUuid uid(QString::fromStdString(stream["id"].as<std::string>()));
m_capuredWindows.push_back(QCapturableWindowPrivate::create(
static_cast<QCapturableWindowPrivate::Id>(uid.toByteArray().toLong()),
QString::fromStdString(stream["id"].as<std::string>())));
wasmWindowCapture->setVideoStream(stream);
Q_EMIT windowCaptureChanged();
} else {
QWasmScreenCapture *wasmScreenCapture =
static_cast<QWasmScreenCapture *>(m_screenCapture);
wasmScreenCapture->setVideoOutput(m_videoOutput.get());
wasmScreenCapture->setVideoStream(stream);
Q_EMIT screenCaptureChanged();
}
},
.catchFunc =
[](emscripten::val error) {
qCDebug(qWasmMediaCaptureSession)
<< "getDisplayMedia fail"
<< QString::fromStdString(error["name"].as<std::string>())
<< QString::fromStdString(error["message"].as<std::string>());
// permission denied or error
// "NotAllowedError "Permission Denied"
},
.finallyFunc = {}
};
qstdweb::Promise::make(mediaDevices, QStringLiteral("getDisplayMedia"),
std::move(getDisplayMediaCallback), constraints);
}