diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fce33b39c73d1728e4a97f72ebfd0584c331199..bef49119c32dbdca2e4b99ea0381ed001c9c1963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory(gallery_controls2) add_subdirectory(rasterwindow) add_subdirectory(principledmaterial) add_subdirectory(hellogl) +add_subdirectory(camera) # (slate is built separately) \ No newline at end of file diff --git a/camera/CMakeLists.txt b/camera/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f04c6bf7c612affe3890109cbbed46611e6b0926 --- /dev/null +++ b/camera/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.16) +project(camera VERSION 1.0 LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +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 + main.cpp + mainwindow.cpp mainwindow.h mainwindow.ui +) +target_link_libraries(camera PUBLIC + Qt::Core + Qt::Gui + Qt::Multimedia + Qt::MultimediaWidgets + Qt::CorePrivate + Qt::Widgets +) diff --git a/camera/main.cpp b/camera/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8370ffb87dfebd13fb4751fc1d1db061e74a9501 --- /dev/null +++ b/camera/main.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial 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/camera/mainwindow.cpp b/camera/mainwindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..78c6a75848878bb6e297085520775842d08f1688 --- /dev/null +++ b/camera/mainwindow.cpp @@ -0,0 +1,261 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial 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/camera/mainwindow.h b/camera/mainwindow.h new file mode 100644 index 0000000000000000000000000000000000000000..854c00935ecb6cfe4b0e68d92739bb4e7d84dd7d --- /dev/null +++ b/camera/mainwindow.h @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial 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/camera/mainwindow.ui b/camera/mainwindow.ui new file mode 100644 index 0000000000000000000000000000000000000000..b5de383480f07ec94b1483b8232de2a8b6613ef2 --- /dev/null +++ b/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> diff --git a/deploy.sh b/deploy.sh index a02531a107259f65d2e14c5d8a0571451a4b6e83..c480c518d3243cac8e17c19123ba81e260cc0eac 100755 --- a/deploy.sh +++ b/deploy.sh @@ -34,6 +34,7 @@ deploy "multicanvas" deploy "rasterwindow" deploy "principledmaterial" deploy "hellogl" +deploy "camera" # slate rm -rf slate