// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_CONFIG(permissions) #include #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(); } 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); connect(ui->exposureCompensation, &QAbstractSlider::valueChanged, this, &Camera::setExposureCompensation); setCamera(QMediaDevices::defaultVideoInput()); } void Camera::setCamera(const QCameraDevice &cameraDevice) { m_camera.reset(new QCamera(cameraDevice)); m_captureSession.setCamera(m_camera.data()); connect(m_camera.data(), &QCamera::activeChanged, this, &Camera::updateCameraActive); connect(m_camera.data(), &QCamera::errorOccurred, this, &Camera::displayCameraError); if (!m_mediaRecorder) { m_mediaRecorder.reset(new QMediaRecorder); m_captureSession.setRecorder(m_mediaRecorder.data()); connect(m_mediaRecorder.data(), &QMediaRecorder::recorderStateChanged, this, &Camera::updateRecorderState); connect(m_mediaRecorder.data(), &QMediaRecorder::durationChanged, this, &Camera::updateRecordTime); connect(m_mediaRecorder.data(), &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::configureCaptureSettings() { if (m_doImageCapture) configureImageSettings(); else configureVideoSettings(); } void Camera::configureVideoSettings() { VideoSettings settingsDialog(m_mediaRecorder.data()); 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->actionSettings->setEnabled(true); } else { ui->actionStartCamera->setEnabled(true); ui->actionStopCamera->setEnabled(false); ui->captureWidget->setEnabled(false); ui->actionSettings->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::setExposureCompensation(int index) { m_camera->setExposureCompensation(index * 0.5); } 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(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 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(i); switch (key) { case QMediaMetaData::CoverArtImage: { QImage coverArt(val); data.insert(key, coverArt); break; } case QMediaMetaData::ThumbnailImage: { QImage thumbnail(val); data.insert(key, thumbnail); 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"