summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorn Potter <lorn.potter@gmail.com>2023-04-14 15:34:30 +1000
committerLorn Potter <lorn.potter@gmail.com>2023-05-05 08:29:58 +1000
commitddbbe562e0079205563b431826d3a7e5c08f5388 (patch)
tree2380f29388ef1433ea1c75fb6da79eddfe3429d6
parent0ecf83de7bc1d012a563a0eb25023eb0e608f3a1 (diff)
downloadqtmultimedia-ddbbe562e0079205563b431826d3a7e5c08f5388.tar.gz
wasm: add manual camera test
this also tests local video playback Pick-to: 6.5 Change-Id: I82fff701d82f61ed06aa150d357fdede712ae6c0 Reviewed-by: MikoĊ‚aj Boc <Mikolaj.Boc@qt.io>
-rw-r--r--tests/manual/wasm/CMakeLists.txt6
-rw-r--r--tests/manual/wasm/camera/CMakeLists.txt42
-rw-r--r--tests/manual/wasm/camera/camera-test.pro69
-rw-r--r--tests/manual/wasm/camera/main.cpp17
-rw-r--r--tests/manual/wasm/camera/mainwindow.cpp261
-rw-r--r--tests/manual/wasm/camera/mainwindow.h53
-rw-r--r--tests/manual/wasm/camera/mainwindow.ui107
7 files changed, 555 insertions, 0 deletions
diff --git a/tests/manual/wasm/CMakeLists.txt b/tests/manual/wasm/CMakeLists.txt
new file mode 100644
index 000000000..1d3c623c4
--- /dev/null
+++ b/tests/manual/wasm/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(QT_FEATURE_widgets)
+ add_subdirectory(camera)
+endif()
diff --git a/tests/manual/wasm/camera/CMakeLists.txt b/tests/manual/wasm/camera/CMakeLists.txt
new file mode 100644
index 000000000..f63c60bbf
--- /dev/null
+++ b/tests/manual/wasm/camera/CMakeLists.txt
@@ -0,0 +1,42 @@
+# Generated from camera-test.pro.
+
+cmake_minimum_required(VERSION 3.16)
+project(camera-test VERSION 1.0 LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+set(CMAKE_BUILD_TYPE Release)
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}")
+
+find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Multimedia MultimediaWidgets)
+find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Widgets)
+
+qt_add_executable(camera-test WIN32 MACOSX_BUNDLE
+ main.cpp
+ mainwindow.cpp mainwindow.h mainwindow.ui
+)
+target_link_libraries(camera-test PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Multimedia
+ Qt::MultimediaWidgets
+ Qt::CorePrivate
+ Qt::Widgets
+)
+
+set(CMAKE_BUILD_TYPE Debug)
+# uncomment to enable asyncify
+# target_link_options(wasm-camera PUBLIC -sASYNCIFY -Os)
+
+install(TARGETS camera-test
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/wasm/camera/camera-test.pro b/tests/manual/wasm/camera/camera-test.pro
new file mode 100644
index 000000000..f13ba3596
--- /dev/null
+++ b/tests/manual/wasm/camera/camera-test.pro
@@ -0,0 +1,69 @@
+QT += core gui multimedia multimediawidgets
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++17 debug
+
+# uncomment this to use asyncify
+#wasm: QMAKE_LFLAGS += -s ASYNCIFY -Os
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main.cpp \
+ mainwindow.cpp
+
+HEADERS += \
+ mainwindow.h
+
+FORMS += \
+ mainwindow.ui
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+macos {
+ PRODUCT_NAME = $$TARGET
+ macx-xcode: PRODUCT_NAME = $${LITERAL_DOLLAR}{PRODUCT_NAME}
+ INFOPLIST = \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" \
+ "<plist version=\"1.0\">" \
+ "<dict>" \
+ " <key>CFBundleIconFile</key>" \
+ " <string></string>" \
+ " <key>CFBundlePackageType</key>" \
+ " <string>APPL</string>" \
+ " <key>CFBundleGetInfoString</key>" \
+ " <string>Created by Qt/QMake</string>" \
+ " <key>CFBundleSignature</key>" \
+ " <string>????</string>" \
+ " <key>CFBundleExecutable</key>" \
+ " <string>$$TARGET</string>" \
+ " <key>CFBundleIdentifier</key>" \
+ " <string>com.digia.$${LITERAL_DOLLAR}{PRODUCT_NAME:rfc1034identifier}</string>" \
+ " <key>CFBundleDisplayName</key>" \
+ " <string>$$PRODUCT_NAME</string>" \
+ " <key>CFBundleName</key>" \
+ " <string>$$PRODUCT_NAME</string>" \
+ " <key>CFBundleShortVersionString</key>" \
+ " <string>1.0</string>" \
+ " <key>CFBundleVersion</key>" \
+ " <string>1.0</string>" \
+ " <key>NSPrincipalClass</key>" \
+ " <string>NSApplication</string>" \
+ " <key>NSCameraUsageDescription</key>" \
+ " <string>Qt Multimedia Example</string>" \
+ " <key>NSMicrophoneUsageDescription</key>" \
+ " <string>Qt Multimedia Example</string>" \
+ " <key>NOTE</key>" \
+ " <string>This file was generated by Qt/QMake.</string>" \
+ "</dict>" \
+ "</plist>"
+ write_file($$OUT_PWD/Info.plist, INFOPLIST)|error()
+ QMAKE_INFO_PLIST = $$OUT_PWD/Info.plist
+}
diff --git a/tests/manual/wasm/camera/main.cpp b/tests/manual/wasm/camera/main.cpp
new file mode 100644
index 000000000..40af4ea41
--- /dev/null
+++ b/tests/manual/wasm/camera/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 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 "mainwindow.h"
+
+#include <QApplication>
+#include <QLoggingCategory>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ QLoggingCategory::setFilterRules("*.debug=false\n"
+ "qt.multimedia.wasm.*=true");
+ MainWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/tests/manual/wasm/camera/mainwindow.cpp b/tests/manual/wasm/camera/mainwindow.cpp
new file mode 100644
index 000000000..a7f379bab
--- /dev/null
+++ b/tests/manual/wasm/camera/mainwindow.cpp
@@ -0,0 +1,261 @@
+// Copyright (C) 2023 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 "mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include <QAudioInput>
+#include <QCamera>
+#include <QCameraDevice>
+#include <QGraphicsScene>
+#include <QGraphicsVideoItem>
+#include <QImageCapture>
+#include <QMediaCaptureSession>
+#include <QMediaDevices>
+#include <QMediaFormat>
+#include <QApplication>
+#include <QTimer>
+#include <QVideoWidget>
+#include <QLabel>
+#include <QFileDialog>
+#include <QScreen>
+#include <QMediaPlayer>
+
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent),
+ ui(new Ui::MainWindow),
+ m_recorder(nullptr)
+{
+ ui->setupUi(this);
+ init();
+}
+
+MainWindow::~MainWindow()
+{
+ if (m_recorder)
+ delete m_recorder;
+ delete ui;
+}
+
+void MainWindow::init()
+{
+#if QT_CONFIG(permissions)
+ // camera
+ QCameraPermission cameraPermission;
+ switch (qApp->checkPermission(cameraPermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(cameraPermission, this, &MainWindow::init);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Camera permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+ // microphone
+ QMicrophonePermission microphonePermission;
+ switch (qApp->checkPermission(microphonePermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(microphonePermission, this, &MainWindow::init);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
+ m_captureSession = new QMediaCaptureSession(this);
+
+ m_mediaDevices = new QMediaDevices(this);
+ // wait until devices list is ready
+ connect(m_mediaDevices, &QMediaDevices::videoInputsChanged,
+ [this]() { doCamera(); });
+}
+
+void MainWindow::doCamera()
+{
+ m_audioInput.reset(new QAudioInput);
+ m_captureSession->setAudioInput(m_audioInput.get());
+ const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
+
+ ui->camerasComboBox->clear();
+ ui->camerasComboBox->setPlaceholderText("select camera");
+
+ for (const QCameraDevice &cameraDevice : cameras) {
+ if (ui->camerasComboBox->findText(cameraDevice.description()) == -1)
+ ui->camerasComboBox->addItem(cameraDevice.description(), cameraDevice.id());
+ }
+
+ if (cameras.count() == 0) {
+ qWarning() << "No camera found";
+ } else {
+
+ QGraphicsVideoItem *videoItem = new QGraphicsVideoItem();
+ QGraphicsScene *scene = new QGraphicsScene(this);
+ m_captureSession->setVideoOutput(videoItem); // sets videoSink
+
+ ui->graphicsView->setScene(scene);
+ ui->graphicsView->scene()->addItem(videoItem);
+ ui->graphicsView->show();
+ }
+}
+
+void MainWindow::on_startButton_clicked()
+{
+ m_camera.data()->start();
+}
+
+void MainWindow::on_stopButton_clicked()
+{
+ m_camera.data()->stop();
+}
+
+void MainWindow::on_captureButton_clicked()
+{
+ connect(m_camera.data(), &QCamera::errorOccurred,
+ [](QCamera::Error error, const QString &errorString) {
+ qWarning() << "Error occurred" << error << errorString;
+ });
+
+ QImageCapture *m_imageCapture = new QImageCapture(this);
+ connect(m_imageCapture, &QImageCapture::readyForCaptureChanged, [] (bool ready) {
+
+ qWarning() << "MainWindow::readyForCaptureChanged" << ready;
+ });
+
+ connect(m_imageCapture,
+ &QImageCapture::imageCaptured,
+ [] (int id, const QImage &preview) {
+ qWarning() << "MainWindow::imageCaptured" << id << preview;
+
+ });
+
+ connect(m_imageCapture, &QImageCapture::imageSaved,
+ [this] (int id, const QString &fileName) {
+ Q_UNUSED(id)
+ QFileInfo fi(fileName);
+ if (!fi.exists()) {
+ qWarning() << fileName << "Does not exist";
+ } else {
+ QDialog *dlg = new QDialog(this);
+ dlg->setWindowTitle(fi.fileName());
+ QHBoxLayout *l = new QHBoxLayout(dlg);
+ QLabel *label = new QLabel(this);
+ l->addWidget(label);
+ label->setPixmap(QPixmap(fileName));
+ dlg->show();
+ }
+
+ });
+ connect(m_imageCapture,
+ &QImageCapture::errorOccurred, []
+ (int id, QImageCapture::Error error, const QString &errorString) {
+ qWarning() << "MainWindow::errorOccurred" << id << error << errorString;
+ });
+
+ m_captureSession->setImageCapture(m_imageCapture);
+
+ // take photo
+ if (m_imageCapture->isReadyForCapture())
+ m_imageCapture->captureToFile(QStringLiteral("/home/web_user/image.png"));
+}
+
+void MainWindow::on_openButton_clicked()
+{
+ // open
+ QFileDialog *dialog = new QFileDialog(this);
+ dialog->setNameFilter(tr("All Files (*.*)"));
+ connect(dialog, &QFileDialog::fileSelected,
+ [this](const QString &file) {
+ qWarning() << "open this file" << file;
+ showFile(file);
+ });
+
+ dialog->show();
+}
+
+void MainWindow::on_camerasComboBox_textActivated(const QString &arg1)
+{
+ const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
+ for (const QCameraDevice &cameraDevice :cameras) {
+ if (arg1 == cameraDevice.description()) {
+ m_camera.reset(new QCamera(cameraDevice));
+ connect(m_captureSession, &QMediaCaptureSession::cameraChanged,
+ [this]() {
+ enableButtons(true);
+ });
+ m_captureSession->setCamera(m_camera.data());
+ break;
+ }
+ }
+}
+
+void MainWindow::on_recordButton_clicked()
+{
+ if (!isRecording) {
+ if (m_recorder)
+ delete m_recorder;
+ m_recorder = new QMediaRecorder();
+ connect(m_recorder, &QMediaRecorder::durationChanged,
+ [](qint64 duration) {
+ qWarning() << "MainWindow::durationChanged"
+ << duration;
+ });
+ m_captureSession->setRecorder(m_recorder);
+
+ m_recorder->setQuality(QMediaRecorder::HighQuality);
+ m_recorder->setOutputLocation(QUrl::fromLocalFile("test.mp4"));
+ m_recorder->record();
+ isRecording = true;
+ ui->recordButton->setText(QStringLiteral("Stop"));
+ } else {
+ m_recorder->stop();
+ isRecording = false;
+ m_recorder->deleteLater();
+ ui->recordButton->setText(QStringLiteral("Record"));
+ }
+}
+
+void MainWindow::enableButtons(bool ok)
+{
+ ui->captureButton->setEnabled(ok);
+ ui->startButton->setEnabled(ok);
+ ui->stopButton->setEnabled(ok);
+ ui->openButton->setEnabled(ok);
+ ui->recordButton->setEnabled(ok);
+}
+
+void MainWindow::showFile(const QString &fileName)
+{
+
+ const QSize screenGeometry = screen()->availableSize();
+ QFileInfo fi(fileName);
+ QDialog *dlg = new QDialog(this);
+ dlg->setWindowTitle(fi.fileName());
+ QHBoxLayout *layout = new QHBoxLayout(dlg);
+ QMediaPlayer *player = new QMediaPlayer(dlg);
+ connect(player, &QMediaPlayer::errorOccurred, [=] (QMediaPlayer::Error error, const QString &errorString) {
+ qWarning() << "MediaPlayer erro!:" << error << errorString;
+ });
+
+ QGraphicsVideoItem *m_videoItem = new QGraphicsVideoItem();
+ QSizeF dialogSize(screenGeometry.width() / 2, screenGeometry.height() / 2);
+ m_videoItem->setSize(dialogSize);
+ player->setVideoOutput(m_videoItem);
+ dlg->setGeometry(20, 20, dialogSize.width() + 40, dialogSize.height() + 40);
+
+ QGraphicsScene *scene = new QGraphicsScene(dlg);
+ QGraphicsView *graphicsView = new QGraphicsView(scene);
+ scene->addItem(m_videoItem);
+ layout->addWidget(graphicsView);
+
+ player->setSource(QUrl(fileName));
+
+ dlg->show();
+ player->play();
+}
diff --git a/tests/manual/wasm/camera/mainwindow.h b/tests/manual/wasm/camera/mainwindow.h
new file mode 100644
index 000000000..0efb09e7d
--- /dev/null
+++ b/tests/manual/wasm/camera/mainwindow.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 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
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QImageCapture>
+#include <QMediaCaptureSession>
+#include <QGraphicsVideoItem>
+#include <QCamera>
+#include <QMediaDevices>
+#include <QMediaRecorder>
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+
+class QMediaCaptureSession;
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+ void init();
+
+private slots:
+ void doCamera();
+ void on_startButton_clicked();
+ void on_stopButton_clicked();
+ void on_captureButton_clicked();
+ void on_openButton_clicked();
+ void on_recordButton_clicked();
+ void on_camerasComboBox_textActivated(const QString &arg1);
+
+private:
+ Ui::MainWindow *ui;
+
+ void enableButtons(bool ok);
+ void showFile(const QString &filename);
+
+ QScopedPointer<QCamera> m_camera;
+ QMediaCaptureSession *m_captureSession;
+ QScopedPointer<QAudioInput> m_audioInput;
+ QMediaDevices *m_mediaDevices;
+ QMediaRecorder *m_recorder;
+ bool isRecording = false;
+};
+
+QT_END_NAMESPACE
+#endif // MAINWINDOW_H
diff --git a/tests/manual/wasm/camera/mainwindow.ui b/tests/manual/wasm/camera/mainwindow.ui
new file mode 100644
index 000000000..b5de38348
--- /dev/null
+++ b/tests/manual/wasm/camera/mainwindow.ui
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QComboBox" name="camerasComboBox">
+ <property name="currentIndex">
+ <number>-1</number>
+ </property>
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGraphicsView" name="graphicsView"/>
+ </item>
+ <item row="2" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="startButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>start camera</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="stopButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>stop camera</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="captureButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>capture photo</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="recordButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>record</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="openButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>open</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>24</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>