Revision control
Copy as Markdown
// 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"