| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the examples of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:BSD$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** BSD License Usage |
| ** Alternatively, you may use this file under the terms of the BSD license |
| ** as follows: |
| ** |
| ** "Redistribution and use in source and binary forms, with or without |
| ** modification, are permitted provided that the following conditions are |
| ** met: |
| ** * Redistributions of source code must retain the above copyright |
| ** notice, this list of conditions and the following disclaimer. |
| ** * Redistributions in binary form must reproduce the above copyright |
| ** notice, this list of conditions and the following disclaimer in |
| ** the documentation and/or other materials provided with the |
| ** distribution. |
| ** * Neither the name of The Qt Company Ltd nor the names of its |
| ** contributors may be used to endorse or promote products derived |
| ** from this software without specific prior written permission. |
| ** |
| ** |
| ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "camera.h" |
| #include "ui_camera.h" |
| #include "videosettings.h" |
| #include "imagesettings.h" |
| |
| #include <QMediaService> |
| #include <QMediaRecorder> |
| #include <QCameraViewfinder> |
| #include <QCameraInfo> |
| #include <QMediaMetaData> |
| |
| #include <QMessageBox> |
| #include <QPalette> |
| |
| #include <QtWidgets> |
| |
| Q_DECLARE_METATYPE(QCameraInfo) |
| |
| Camera::Camera() : ui(new Ui::Camera) |
| { |
| ui->setupUi(this); |
| |
| //Camera devices: |
| |
| QActionGroup *videoDevicesGroup = new QActionGroup(this); |
| videoDevicesGroup->setExclusive(true); |
| const QList<QCameraInfo> availableCameras = QCameraInfo::availableCameras(); |
| for (const QCameraInfo &cameraInfo : availableCameras) { |
| QAction *videoDeviceAction = new QAction(cameraInfo.description(), videoDevicesGroup); |
| videoDeviceAction->setCheckable(true); |
| videoDeviceAction->setData(QVariant::fromValue(cameraInfo)); |
| if (cameraInfo == QCameraInfo::defaultCamera()) |
| videoDeviceAction->setChecked(true); |
| |
| ui->menuDevices->addAction(videoDeviceAction); |
| } |
| |
| connect(videoDevicesGroup, &QActionGroup::triggered, this, &Camera::updateCameraDevice); |
| connect(ui->captureWidget, &QTabWidget::currentChanged, this, &Camera::updateCaptureMode); |
| |
| setCamera(QCameraInfo::defaultCamera()); |
| } |
| |
| void Camera::setCamera(const QCameraInfo &cameraInfo) |
| { |
| m_camera.reset(new QCamera(cameraInfo)); |
| |
| connect(m_camera.data(), &QCamera::stateChanged, this, &Camera::updateCameraState); |
| connect(m_camera.data(), QOverload<QCamera::Error>::of(&QCamera::error), this, &Camera::displayCameraError); |
| |
| m_mediaRecorder.reset(new QMediaRecorder(m_camera.data())); |
| connect(m_mediaRecorder.data(), &QMediaRecorder::stateChanged, this, &Camera::updateRecorderState); |
| |
| m_imageCapture.reset(new QCameraImageCapture(m_camera.data())); |
| |
| connect(m_mediaRecorder.data(), &QMediaRecorder::durationChanged, this, &Camera::updateRecordTime); |
| connect(m_mediaRecorder.data(), QOverload<QMediaRecorder::Error>::of(&QMediaRecorder::error), |
| this, &Camera::displayRecorderError); |
| |
| m_mediaRecorder->setMetaData(QMediaMetaData::Title, QVariant(QLatin1String("Test Title"))); |
| |
| connect(ui->exposureCompensation, &QAbstractSlider::valueChanged, this, &Camera::setExposureCompensation); |
| |
| m_camera->setViewfinder(ui->viewfinder); |
| |
| updateCameraState(m_camera->state()); |
| updateLockStatus(m_camera->lockStatus(), QCamera::UserRequest); |
| updateRecorderState(m_mediaRecorder->state()); |
| |
| connect(m_imageCapture.data(), &QCameraImageCapture::readyForCaptureChanged, this, &Camera::readyForCapture); |
| connect(m_imageCapture.data(), &QCameraImageCapture::imageCaptured, this, &Camera::processCapturedImage); |
| connect(m_imageCapture.data(), &QCameraImageCapture::imageSaved, this, &Camera::imageSaved); |
| connect(m_imageCapture.data(), QOverload<int, QCameraImageCapture::Error, const QString &>::of(&QCameraImageCapture::error), |
| this, &Camera::displayCaptureError); |
| |
| connect(m_camera.data(), QOverload<QCamera::LockStatus, QCamera::LockChangeReason>::of(&QCamera::lockStatusChanged), |
| this, &Camera::updateLockStatus); |
| |
| ui->captureWidget->setTabEnabled(0, (m_camera->isCaptureModeSupported(QCamera::CaptureStillImage))); |
| ui->captureWidget->setTabEnabled(1, (m_camera->isCaptureModeSupported(QCamera::CaptureVideo))); |
| |
| updateCaptureMode(); |
| m_camera->start(); |
| } |
| |
| void Camera::keyPressEvent(QKeyEvent * event) |
| { |
| if (event->isAutoRepeat()) |
| return; |
| |
| switch (event->key()) { |
| case Qt::Key_CameraFocus: |
| displayViewfinder(); |
| m_camera->searchAndLock(); |
| event->accept(); |
| break; |
| case Qt::Key_Camera: |
| if (m_camera->captureMode() == QCamera::CaptureStillImage) { |
| takeImage(); |
| } else { |
| if (m_mediaRecorder->state() == QMediaRecorder::RecordingState) |
| stop(); |
| else |
| record(); |
| } |
| event->accept(); |
| break; |
| default: |
| QMainWindow::keyPressEvent(event); |
| } |
| } |
| |
| void Camera::keyReleaseEvent(QKeyEvent *event) |
| { |
| if (event->isAutoRepeat()) |
| return; |
| |
| switch (event->key()) { |
| case Qt::Key_CameraFocus: |
| m_camera->unlock(); |
| break; |
| default: |
| QMainWindow::keyReleaseEvent(event); |
| } |
| } |
| |
| void Camera::updateRecordTime() |
| { |
| QString str = QString("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() |
| { |
| switch (m_camera->captureMode()) { |
| case QCamera::CaptureStillImage: |
| configureImageSettings(); |
| break; |
| case QCamera::CaptureVideo: |
| configureVideoSettings(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void Camera::configureVideoSettings() |
| { |
| VideoSettings settingsDialog(m_mediaRecorder.data()); |
| settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); |
| |
| settingsDialog.setAudioSettings(m_audioSettings); |
| settingsDialog.setVideoSettings(m_videoSettings); |
| settingsDialog.setFormat(m_videoContainerFormat); |
| |
| if (settingsDialog.exec()) { |
| m_audioSettings = settingsDialog.audioSettings(); |
| m_videoSettings = settingsDialog.videoSettings(); |
| m_videoContainerFormat = settingsDialog.format(); |
| |
| m_mediaRecorder->setEncodingSettings( |
| m_audioSettings, |
| m_videoSettings, |
| m_videoContainerFormat); |
| |
| m_camera->unload(); |
| m_camera->start(); |
| } |
| } |
| |
| void Camera::configureImageSettings() |
| { |
| ImageSettings settingsDialog(m_imageCapture.data()); |
| settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); |
| |
| settingsDialog.setImageSettings(m_imageSettings); |
| |
| if (settingsDialog.exec()) { |
| m_imageSettings = settingsDialog.imageSettings(); |
| m_imageCapture->setEncodingSettings(m_imageSettings); |
| } |
| } |
| |
| 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_mediaRecorder->setMuted(muted); |
| } |
| |
| void Camera::toggleLock() |
| { |
| switch (m_camera->lockStatus()) { |
| case QCamera::Searching: |
| case QCamera::Locked: |
| m_camera->unlock(); |
| break; |
| case QCamera::Unlocked: |
| m_camera->searchAndLock(); |
| } |
| } |
| |
| void Camera::updateLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason) |
| { |
| QColor indicationColor = Qt::black; |
| |
| switch (status) { |
| case QCamera::Searching: |
| indicationColor = Qt::yellow; |
| ui->statusbar->showMessage(tr("Focusing...")); |
| ui->lockButton->setText(tr("Focusing...")); |
| break; |
| case QCamera::Locked: |
| indicationColor = Qt::darkGreen; |
| ui->lockButton->setText(tr("Unlock")); |
| ui->statusbar->showMessage(tr("Focused"), 2000); |
| break; |
| case QCamera::Unlocked: |
| indicationColor = reason == QCamera::LockFailed ? Qt::red : Qt::black; |
| ui->lockButton->setText(tr("Focus")); |
| if (reason == QCamera::LockFailed) |
| ui->statusbar->showMessage(tr("Focus Failed"), 2000); |
| } |
| |
| QPalette palette = ui->lockButton->palette(); |
| palette.setColor(QPalette::ButtonText, indicationColor); |
| ui->lockButton->setPalette(palette); |
| } |
| |
| void Camera::takeImage() |
| { |
| m_isCapturingImage = true; |
| m_imageCapture->capture(); |
| } |
| |
| void Camera::displayCaptureError(int id, const QCameraImageCapture::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(); |
| QCamera::CaptureModes captureMode = tabIndex == 0 ? QCamera::CaptureStillImage : QCamera::CaptureVideo; |
| |
| if (m_camera->isCaptureModeSupported(captureMode)) |
| m_camera->setCaptureMode(captureMode); |
| } |
| |
| void Camera::updateCameraState(QCamera::State state) |
| { |
| switch (state) { |
| case QCamera::ActiveState: |
| ui->actionStartCamera->setEnabled(false); |
| ui->actionStopCamera->setEnabled(true); |
| ui->captureWidget->setEnabled(true); |
| ui->actionSettings->setEnabled(true); |
| break; |
| case QCamera::UnloadedState: |
| case QCamera::LoadedState: |
| ui->actionStartCamera->setEnabled(true); |
| ui->actionStopCamera->setEnabled(false); |
| ui->captureWidget->setEnabled(false); |
| ui->actionSettings->setEnabled(false); |
| } |
| } |
| |
| void Camera::updateRecorderState(QMediaRecorder::State state) |
| { |
| switch (state) { |
| case QMediaRecorder::StoppedState: |
| ui->recordButton->setEnabled(true); |
| ui->pauseButton->setEnabled(true); |
| ui->stopButton->setEnabled(false); |
| break; |
| case QMediaRecorder::PausedState: |
| ui->recordButton->setEnabled(true); |
| ui->pauseButton->setEnabled(false); |
| ui->stopButton->setEnabled(true); |
| break; |
| case QMediaRecorder::RecordingState: |
| ui->recordButton->setEnabled(false); |
| ui->pauseButton->setEnabled(true); |
| ui->stopButton->setEnabled(true); |
| break; |
| } |
| } |
| |
| void Camera::setExposureCompensation(int index) |
| { |
| m_camera->exposure()->setExposureCompensation(index*0.5); |
| } |
| |
| void Camera::displayRecorderError() |
| { |
| QMessageBox::warning(this, tr("Capture Error"), m_mediaRecorder->errorString()); |
| } |
| |
| void Camera::displayCameraError() |
| { |
| QMessageBox::warning(this, tr("Camera Error"), m_camera->errorString()); |
| } |
| |
| void Camera::updateCameraDevice(QAction *action) |
| { |
| setCamera(qvariant_cast<QCameraInfo>(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(); |
| } |
| } |