Revision control
++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ #include "camera.h"
++ #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
++ # include "ui_camera_mobile.h"
++ #else
++ # include "ui_camera.h"
++ #endif
++
++ #include "imagesettings.h"
++ #include "metadatadialog.h"
++ #include "videosettings.h"
++
++ #include <QAudioDevice>
++ #include <QAudioInput>
++ #include <QCameraDevice>
++ #include <QMediaDevices>
++ #include <QMediaFormat>
++ #include <QMediaMetaData>
++ #include <QMediaRecorder>
++ #include <QVideoWidget>
++
++ #include <QLineEdit>
++ #include <QMessageBox>
++
++ #include <QAction>
++ #include <QActionGroup>
++ #include <QImage>
++ #include <QKeyEvent>
++ #include <QPalette>
++
++ #include <QDir>
++ #include <QTimer>
++
++ #if QT_CONFIG(permissions)
++ #include <QPermission>
++ #endif
++
++ Camera::Camera() : ui(new Ui::Camera)
++ {
++ ui->setupUi(this);
++ // disable all buttons by default
++ updateCameraActive(false);
++ readyForCapture(false);
++ ui->recordButton->setEnabled(false);
++ ui->pauseButton->setEnabled(false);
++ ui->stopButton->setEnabled(false);
++ ui->metaDataButton->setEnabled(false);
++
++ // try to actually initialize camera & mic
++ init();
++ }
++
++ Camera::~Camera() = default;
++
++ void Camera::init()
++ {
++ #if QT_CONFIG(permissions)
++ // camera
++ QCameraPermission cameraPermission;
++ switch (qApp->checkPermission(cameraPermission)) {
++ case Qt::PermissionStatus::Undetermined:
++ qApp->requestPermission(cameraPermission, this, &Camera::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, &Camera::init);
++ return;
++ case Qt::PermissionStatus::Denied:
++ qWarning("Microphone permission is not granted!");
++ return;
++ case Qt::PermissionStatus::Granted:
++ break;
++ }
++ #endif
++
++ m_audioInput.reset(new QAudioInput);
++ m_captureSession.setAudioInput(m_audioInput.get());
++
++ // Camera devices:
++
++ videoDevicesGroup = new QActionGroup(this);
++ videoDevicesGroup->setExclusive(true);
++ updateCameras();
++ connect(&m_devices, &QMediaDevices::videoInputsChanged, this, &Camera::updateCameras);
++
++ connect(videoDevicesGroup, &QActionGroup::triggered, this, &Camera::updateCameraDevice);
++ connect(ui->captureWidget, &QTabWidget::currentChanged, this, &Camera::updateCaptureMode);
++
++ connect(ui->metaDataButton, &QPushButton::clicked, this, &Camera::showMetaDataDialog);
++
++ setCamera(QMediaDevices::defaultVideoInput());
++ }
++
++ void Camera::setCamera(const QCameraDevice &cameraDevice)
++ {
++ m_camera.reset(new QCamera(cameraDevice));
++ m_captureSession.setCamera(m_camera.get());
++
++ connect(m_camera.get(), &QCamera::activeChanged, this, &Camera::updateCameraActive);
++ connect(m_camera.get(), &QCamera::errorOccurred, this, &Camera::displayCameraError);
++
++ if (!m_mediaRecorder) {
++ m_mediaRecorder.reset(new QMediaRecorder);
++ m_captureSession.setRecorder(m_mediaRecorder.get());
++ connect(m_mediaRecorder.get(), &QMediaRecorder::recorderStateChanged, this,
++ &Camera::updateRecorderState);
++ connect(m_mediaRecorder.get(), &QMediaRecorder::durationChanged, this,
++ &Camera::updateRecordTime);
++ connect(m_mediaRecorder.get(), &QMediaRecorder::errorChanged, this,
++ &Camera::displayRecorderError);
++ }
++
++ if (!m_imageCapture) {
++ m_imageCapture.reset(new QImageCapture);
++ m_captureSession.setImageCapture(m_imageCapture.get());
++ connect(m_imageCapture.get(), &QImageCapture::readyForCaptureChanged, this,
++ &Camera::readyForCapture);
++ connect(m_imageCapture.get(), &QImageCapture::imageCaptured, this,
++ &Camera::processCapturedImage);
++ connect(m_imageCapture.get(), &QImageCapture::imageSaved, this, &Camera::imageSaved);
++ connect(m_imageCapture.get(), &QImageCapture::errorOccurred, this,
++ &Camera::displayCaptureError);
++ }
++
++ m_captureSession.setVideoOutput(ui->viewfinder);
++
++ updateCameraActive(m_camera->isActive());
++ updateRecorderState(m_mediaRecorder->recorderState());
++ readyForCapture(m_imageCapture->isReadyForCapture());
++
++ updateCaptureMode();
++
++ m_camera->start();
++ }
++
++ void Camera::keyPressEvent(QKeyEvent *event)
++ {
++ if (event->isAutoRepeat())
++ return;
++
++ switch (event->key()) {
++ case Qt::Key_CameraFocus:
++ displayViewfinder();
++ event->accept();
++ break;
++ case Qt::Key_Camera:
++ if (m_doImageCapture) {
++ takeImage();
++ } else {
++ if (m_mediaRecorder->recorderState() == QMediaRecorder::RecordingState)
++ stop();
++ else
++ record();
++ }
++ event->accept();
++ break;
++ default:
++ QMainWindow::keyPressEvent(event);
++ }
++ }
++
++ void Camera::updateRecordTime()
++ {
++ QString str = tr("Recorded %1 sec").arg(m_mediaRecorder->duration() / 1000);
++ ui->statusbar->showMessage(str);
++ }
++
++ void Camera::processCapturedImage(int requestId, const QImage &img)
++ {
++ Q_UNUSED(requestId);
++ QImage scaledImage =
++ img.scaled(ui->viewfinder->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
++
++ ui->lastImagePreviewLabel->setPixmap(QPixmap::fromImage(scaledImage));
++
++ // Display captured image for 4 seconds.
++ displayCapturedImage();
++ QTimer::singleShot(4000, this, &Camera::displayViewfinder);
++ }
++
++ void Camera::configureVideoSettings()
++ {
++ VideoSettings settingsDialog(m_mediaRecorder.get());
++
++ if (settingsDialog.exec())
++ settingsDialog.applySettings();
++ }
++
++ void Camera::configureImageSettings()
++ {
++ ImageSettings settingsDialog(m_imageCapture.get());
++
++ if (settingsDialog.exec() == QDialog::Accepted)
++ settingsDialog.applyImageSettings();
++ }
++
++ void Camera::record()
++ {
++ m_mediaRecorder->record();
++ updateRecordTime();
++ }
++
++ void Camera::pause()
++ {
++ m_mediaRecorder->pause();
++ }
++
++ void Camera::stop()
++ {
++ m_mediaRecorder->stop();
++ }
++
++ void Camera::setMuted(bool muted)
++ {
++ m_captureSession.audioInput()->setMuted(muted);
++ }
++
++ void Camera::takeImage()
++ {
++ m_isCapturingImage = true;
++ m_imageCapture->captureToFile();
++ }
++
++ void Camera::displayCaptureError(int id, const QImageCapture::Error error,
++ const QString &errorString)
++ {
++ Q_UNUSED(id);
++ Q_UNUSED(error);
++ QMessageBox::warning(this, tr("Image Capture Error"), errorString);
++ m_isCapturingImage = false;
++ }
++
++ void Camera::startCamera()
++ {
++ m_camera->start();
++ }
++
++ void Camera::stopCamera()
++ {
++ m_camera->stop();
++ }
++
++ void Camera::updateCaptureMode()
++ {
++ int tabIndex = ui->captureWidget->currentIndex();
++ m_doImageCapture = (tabIndex == 0);
++ }
++
++ void Camera::updateCameraActive(bool active)
++ {
++ if (active) {
++ ui->actionStartCamera->setEnabled(false);
++ ui->actionStopCamera->setEnabled(true);
++ ui->captureWidget->setEnabled(true);
++ ui->actionImageSettings->setEnabled(true);
++ ui->actionVideoSettings->setEnabled(true);
++ } else {
++ ui->actionStartCamera->setEnabled(true);
++ ui->actionStopCamera->setEnabled(false);
++ ui->captureWidget->setEnabled(false);
++ ui->actionImageSettings->setEnabled(false);
++ ui->actionVideoSettings->setEnabled(false);
++ }
++ }
++
++ void Camera::updateRecorderState(QMediaRecorder::RecorderState state)
++ {
++ switch (state) {
++ case QMediaRecorder::StoppedState:
++ ui->recordButton->setEnabled(true);
++ ui->pauseButton->setEnabled(true);
++ ui->stopButton->setEnabled(false);
++ ui->metaDataButton->setEnabled(true);
++ break;
++ case QMediaRecorder::PausedState:
++ ui->recordButton->setEnabled(true);
++ ui->pauseButton->setEnabled(false);
++ ui->stopButton->setEnabled(true);
++ ui->metaDataButton->setEnabled(false);
++ break;
++ case QMediaRecorder::RecordingState:
++ ui->recordButton->setEnabled(false);
++ ui->pauseButton->setEnabled(true);
++ ui->stopButton->setEnabled(true);
++ ui->metaDataButton->setEnabled(false);
++ break;
++ }
++ }
++
++ void Camera::displayRecorderError()
++ {
++ if (m_mediaRecorder->error() != QMediaRecorder::NoError)
++ QMessageBox::warning(this, tr("Capture Error"), m_mediaRecorder->errorString());
++ }
++
++ void Camera::displayCameraError()
++ {
++ if (m_camera->error() != QCamera::NoError)
++ QMessageBox::warning(this, tr("Camera Error"), m_camera->errorString());
++ }
++
++ void Camera::updateCameraDevice(QAction *action)
++ {
++ setCamera(qvariant_cast<QCameraDevice>(action->data()));
++ }
++
++ void Camera::displayViewfinder()
++ {
++ ui->stackedWidget->setCurrentIndex(0);
++ }
++
++ void Camera::displayCapturedImage()
++ {
++ ui->stackedWidget->setCurrentIndex(1);
++ }
++
++ void Camera::readyForCapture(bool ready)
++ {
++ ui->takeImageButton->setEnabled(ready);
++ }
++
++ void Camera::imageSaved(int id, const QString &fileName)
++ {
++ Q_UNUSED(id);
++ ui->statusbar->showMessage(tr("Captured \"%1\"").arg(QDir::toNativeSeparators(fileName)));
++
++ m_isCapturingImage = false;
++ if (m_applicationExiting)
++ close();
++ }
++
++ void Camera::closeEvent(QCloseEvent *event)
++ {
++ if (m_isCapturingImage) {
++ setEnabled(false);
++ m_applicationExiting = true;
++ event->ignore();
++ } else {
++ event->accept();
++ }
++ }
++
++ void Camera::updateCameras()
++ {
++ ui->menuDevices->clear();
++ const QList<QCameraDevice> availableCameras = QMediaDevices::videoInputs();
++ for (const QCameraDevice &cameraDevice : availableCameras) {
++ QAction *videoDeviceAction = new QAction(cameraDevice.description(), videoDevicesGroup);
++ videoDeviceAction->setCheckable(true);
++ videoDeviceAction->setData(QVariant::fromValue(cameraDevice));
++ if (cameraDevice == QMediaDevices::defaultVideoInput())
++ videoDeviceAction->setChecked(true);
++
++ ui->menuDevices->addAction(videoDeviceAction);
++ }
++ }
++
++ void Camera::showMetaDataDialog()
++ {
++ if (!m_metaDataDialog)
++ m_metaDataDialog = new MetaDataDialog(this);
++ m_metaDataDialog->setAttribute(Qt::WA_DeleteOnClose, false);
++ if (m_metaDataDialog->exec() == QDialog::Accepted)
++ saveMetaData();
++ }
++
++ void Camera::saveMetaData()
++ {
++ QMediaMetaData data;
++ for (int i = 0; i < QMediaMetaData::NumMetaData; i++) {
++ QString val = m_metaDataDialog->m_metaDataFields[i]->text();
++ if (!val.isEmpty()) {
++ const auto key = static_cast<QMediaMetaData::Key>(i);
++ switch (key) {
++ case QMediaMetaData::CoverArtImage: {
++ QImage coverArt(val);
++ data.insert(key, coverArt);
++ break;
++ }
++ case QMediaMetaData::Date: {
++ QDateTime date = QDateTime::fromString(val);
++ data.insert(key, date);
++ break;
++ }
++ case QMediaMetaData::HasHdrContent:
++ break;
++ default:
++ data.insert(key, val);
++ }
++ }
++ }
++ m_mediaRecorder->setMetaData(data);
++ }
++
++ #include "moc_camera.cpp"