1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 **   * Redistributions of source code must retain the above copyright
25 **     notice, this list of conditions and the following disclaimer.
26 **   * Redistributions in binary form must reproduce the above copyright
27 **     notice, this list of conditions and the following disclaimer in
28 **     the documentation and/or other materials provided with the
29 **     distribution.
30 **   * Neither the name of The Qt Company Ltd nor the names of its
31 **     contributors may be used to endorse or promote products derived
32 **     from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 #include "camera.h"
52 #include "ui_camera.h"
53 #include "videosettings.h"
54 #include "imagesettings.h"
55 
56 #include <QMediaService>
57 #include <QMediaRecorder>
58 #include <QCameraViewfinder>
59 #include <QCameraInfo>
60 #include <QMediaMetaData>
61 
62 #include <QMessageBox>
63 #include <QPalette>
64 
65 #include <QtWidgets>
66 
Q_DECLARE_METATYPE(QCameraInfo)67 Q_DECLARE_METATYPE(QCameraInfo)
68 
69 Camera::Camera() : ui(new Ui::Camera)
70 {
71     ui->setupUi(this);
72 
73     //Camera devices:
74 
75     QActionGroup *videoDevicesGroup = new QActionGroup(this);
76     videoDevicesGroup->setExclusive(true);
77     const QList<QCameraInfo> availableCameras = QCameraInfo::availableCameras();
78     for (const QCameraInfo &cameraInfo : availableCameras) {
79         QAction *videoDeviceAction = new QAction(cameraInfo.description(), videoDevicesGroup);
80         videoDeviceAction->setCheckable(true);
81         videoDeviceAction->setData(QVariant::fromValue(cameraInfo));
82         if (cameraInfo == QCameraInfo::defaultCamera())
83             videoDeviceAction->setChecked(true);
84 
85         ui->menuDevices->addAction(videoDeviceAction);
86     }
87 
88     connect(videoDevicesGroup, &QActionGroup::triggered, this, &Camera::updateCameraDevice);
89     connect(ui->captureWidget, &QTabWidget::currentChanged, this, &Camera::updateCaptureMode);
90 
91     setCamera(QCameraInfo::defaultCamera());
92 }
93 
setCamera(const QCameraInfo & cameraInfo)94 void Camera::setCamera(const QCameraInfo &cameraInfo)
95 {
96     m_camera.reset(new QCamera(cameraInfo));
97 
98     connect(m_camera.data(), &QCamera::stateChanged, this, &Camera::updateCameraState);
99     connect(m_camera.data(), QOverload<QCamera::Error>::of(&QCamera::error), this, &Camera::displayCameraError);
100 
101     m_mediaRecorder.reset(new QMediaRecorder(m_camera.data()));
102     connect(m_mediaRecorder.data(), &QMediaRecorder::stateChanged, this, &Camera::updateRecorderState);
103 
104     m_imageCapture.reset(new QCameraImageCapture(m_camera.data()));
105 
106     connect(m_mediaRecorder.data(), &QMediaRecorder::durationChanged, this, &Camera::updateRecordTime);
107     connect(m_mediaRecorder.data(), QOverload<QMediaRecorder::Error>::of(&QMediaRecorder::error),
108             this, &Camera::displayRecorderError);
109 
110     m_mediaRecorder->setMetaData(QMediaMetaData::Title, QVariant(QLatin1String("Test Title")));
111 
112     connect(ui->exposureCompensation, &QAbstractSlider::valueChanged, this, &Camera::setExposureCompensation);
113 
114     m_camera->setViewfinder(ui->viewfinder);
115 
116     updateCameraState(m_camera->state());
117     updateLockStatus(m_camera->lockStatus(), QCamera::UserRequest);
118     updateRecorderState(m_mediaRecorder->state());
119 
120     connect(m_imageCapture.data(), &QCameraImageCapture::readyForCaptureChanged, this, &Camera::readyForCapture);
121     connect(m_imageCapture.data(), &QCameraImageCapture::imageCaptured, this, &Camera::processCapturedImage);
122     connect(m_imageCapture.data(), &QCameraImageCapture::imageSaved, this, &Camera::imageSaved);
123     connect(m_imageCapture.data(), QOverload<int, QCameraImageCapture::Error, const QString &>::of(&QCameraImageCapture::error),
124             this, &Camera::displayCaptureError);
125 
126     connect(m_camera.data(), QOverload<QCamera::LockStatus, QCamera::LockChangeReason>::of(&QCamera::lockStatusChanged),
127             this, &Camera::updateLockStatus);
128 
129     ui->captureWidget->setTabEnabled(0, (m_camera->isCaptureModeSupported(QCamera::CaptureStillImage)));
130     ui->captureWidget->setTabEnabled(1, (m_camera->isCaptureModeSupported(QCamera::CaptureVideo)));
131 
132     updateCaptureMode();
133     m_camera->start();
134 }
135 
keyPressEvent(QKeyEvent * event)136 void Camera::keyPressEvent(QKeyEvent * event)
137 {
138     if (event->isAutoRepeat())
139         return;
140 
141     switch (event->key()) {
142     case Qt::Key_CameraFocus:
143         displayViewfinder();
144         m_camera->searchAndLock();
145         event->accept();
146         break;
147     case Qt::Key_Camera:
148         if (m_camera->captureMode() == QCamera::CaptureStillImage) {
149             takeImage();
150         } else {
151             if (m_mediaRecorder->state() == QMediaRecorder::RecordingState)
152                 stop();
153             else
154                 record();
155         }
156         event->accept();
157         break;
158     default:
159         QMainWindow::keyPressEvent(event);
160     }
161 }
162 
keyReleaseEvent(QKeyEvent * event)163 void Camera::keyReleaseEvent(QKeyEvent *event)
164 {
165     if (event->isAutoRepeat())
166         return;
167 
168     switch (event->key()) {
169     case Qt::Key_CameraFocus:
170         m_camera->unlock();
171         break;
172     default:
173         QMainWindow::keyReleaseEvent(event);
174     }
175 }
176 
updateRecordTime()177 void Camera::updateRecordTime()
178 {
179     QString str = QString("Recorded %1 sec").arg(m_mediaRecorder->duration()/1000);
180     ui->statusbar->showMessage(str);
181 }
182 
processCapturedImage(int requestId,const QImage & img)183 void Camera::processCapturedImage(int requestId, const QImage& img)
184 {
185     Q_UNUSED(requestId);
186     QImage scaledImage = img.scaled(ui->viewfinder->size(),
187                                     Qt::KeepAspectRatio,
188                                     Qt::SmoothTransformation);
189 
190     ui->lastImagePreviewLabel->setPixmap(QPixmap::fromImage(scaledImage));
191 
192     // Display captured image for 4 seconds.
193     displayCapturedImage();
194     QTimer::singleShot(4000, this, &Camera::displayViewfinder);
195 }
196 
configureCaptureSettings()197 void Camera::configureCaptureSettings()
198 {
199     switch (m_camera->captureMode()) {
200     case QCamera::CaptureStillImage:
201         configureImageSettings();
202         break;
203     case QCamera::CaptureVideo:
204         configureVideoSettings();
205         break;
206     default:
207         break;
208     }
209 }
210 
configureVideoSettings()211 void Camera::configureVideoSettings()
212 {
213     VideoSettings settingsDialog(m_mediaRecorder.data());
214     settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
215 
216     settingsDialog.setAudioSettings(m_audioSettings);
217     settingsDialog.setVideoSettings(m_videoSettings);
218     settingsDialog.setFormat(m_videoContainerFormat);
219 
220     if (settingsDialog.exec()) {
221         m_audioSettings = settingsDialog.audioSettings();
222         m_videoSettings = settingsDialog.videoSettings();
223         m_videoContainerFormat = settingsDialog.format();
224 
225         m_mediaRecorder->setEncodingSettings(
226                     m_audioSettings,
227                     m_videoSettings,
228                     m_videoContainerFormat);
229 
230         m_camera->unload();
231         m_camera->start();
232     }
233 }
234 
configureImageSettings()235 void Camera::configureImageSettings()
236 {
237     ImageSettings settingsDialog(m_imageCapture.data());
238     settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
239 
240     settingsDialog.setImageSettings(m_imageSettings);
241 
242     if (settingsDialog.exec()) {
243         m_imageSettings = settingsDialog.imageSettings();
244         m_imageCapture->setEncodingSettings(m_imageSettings);
245     }
246 }
247 
record()248 void Camera::record()
249 {
250     m_mediaRecorder->record();
251     updateRecordTime();
252 }
253 
pause()254 void Camera::pause()
255 {
256     m_mediaRecorder->pause();
257 }
258 
stop()259 void Camera::stop()
260 {
261     m_mediaRecorder->stop();
262 }
263 
setMuted(bool muted)264 void Camera::setMuted(bool muted)
265 {
266     m_mediaRecorder->setMuted(muted);
267 }
268 
toggleLock()269 void Camera::toggleLock()
270 {
271     switch (m_camera->lockStatus()) {
272     case QCamera::Searching:
273     case QCamera::Locked:
274         m_camera->unlock();
275         break;
276     case QCamera::Unlocked:
277         m_camera->searchAndLock();
278     }
279 }
280 
updateLockStatus(QCamera::LockStatus status,QCamera::LockChangeReason reason)281 void Camera::updateLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
282 {
283     QColor indicationColor = Qt::black;
284 
285     switch (status) {
286     case QCamera::Searching:
287         indicationColor = Qt::yellow;
288         ui->statusbar->showMessage(tr("Focusing..."));
289         ui->lockButton->setText(tr("Focusing..."));
290         break;
291     case QCamera::Locked:
292         indicationColor = Qt::darkGreen;
293         ui->lockButton->setText(tr("Unlock"));
294         ui->statusbar->showMessage(tr("Focused"), 2000);
295         break;
296     case QCamera::Unlocked:
297         indicationColor = reason == QCamera::LockFailed ? Qt::red : Qt::black;
298         ui->lockButton->setText(tr("Focus"));
299         if (reason == QCamera::LockFailed)
300             ui->statusbar->showMessage(tr("Focus Failed"), 2000);
301     }
302 
303     QPalette palette = ui->lockButton->palette();
304     palette.setColor(QPalette::ButtonText, indicationColor);
305     ui->lockButton->setPalette(palette);
306 }
307 
takeImage()308 void Camera::takeImage()
309 {
310     m_isCapturingImage = true;
311     m_imageCapture->capture();
312 }
313 
displayCaptureError(int id,const QCameraImageCapture::Error error,const QString & errorString)314 void Camera::displayCaptureError(int id, const QCameraImageCapture::Error error, const QString &errorString)
315 {
316     Q_UNUSED(id);
317     Q_UNUSED(error);
318     QMessageBox::warning(this, tr("Image Capture Error"), errorString);
319     m_isCapturingImage = false;
320 }
321 
startCamera()322 void Camera::startCamera()
323 {
324     m_camera->start();
325 }
326 
stopCamera()327 void Camera::stopCamera()
328 {
329     m_camera->stop();
330 }
331 
updateCaptureMode()332 void Camera::updateCaptureMode()
333 {
334     int tabIndex = ui->captureWidget->currentIndex();
335     QCamera::CaptureModes captureMode = tabIndex == 0 ? QCamera::CaptureStillImage : QCamera::CaptureVideo;
336 
337     if (m_camera->isCaptureModeSupported(captureMode))
338         m_camera->setCaptureMode(captureMode);
339 }
340 
updateCameraState(QCamera::State state)341 void Camera::updateCameraState(QCamera::State state)
342 {
343     switch (state) {
344     case QCamera::ActiveState:
345         ui->actionStartCamera->setEnabled(false);
346         ui->actionStopCamera->setEnabled(true);
347         ui->captureWidget->setEnabled(true);
348         ui->actionSettings->setEnabled(true);
349         break;
350     case QCamera::UnloadedState:
351     case QCamera::LoadedState:
352         ui->actionStartCamera->setEnabled(true);
353         ui->actionStopCamera->setEnabled(false);
354         ui->captureWidget->setEnabled(false);
355         ui->actionSettings->setEnabled(false);
356     }
357 }
358 
updateRecorderState(QMediaRecorder::State state)359 void Camera::updateRecorderState(QMediaRecorder::State state)
360 {
361     switch (state) {
362     case QMediaRecorder::StoppedState:
363         ui->recordButton->setEnabled(true);
364         ui->pauseButton->setEnabled(true);
365         ui->stopButton->setEnabled(false);
366         break;
367     case QMediaRecorder::PausedState:
368         ui->recordButton->setEnabled(true);
369         ui->pauseButton->setEnabled(false);
370         ui->stopButton->setEnabled(true);
371         break;
372     case QMediaRecorder::RecordingState:
373         ui->recordButton->setEnabled(false);
374         ui->pauseButton->setEnabled(true);
375         ui->stopButton->setEnabled(true);
376         break;
377     }
378 }
379 
setExposureCompensation(int index)380 void Camera::setExposureCompensation(int index)
381 {
382     m_camera->exposure()->setExposureCompensation(index*0.5);
383 }
384 
displayRecorderError()385 void Camera::displayRecorderError()
386 {
387     QMessageBox::warning(this, tr("Capture Error"), m_mediaRecorder->errorString());
388 }
389 
displayCameraError()390 void Camera::displayCameraError()
391 {
392     QMessageBox::warning(this, tr("Camera Error"), m_camera->errorString());
393 }
394 
updateCameraDevice(QAction * action)395 void Camera::updateCameraDevice(QAction *action)
396 {
397     setCamera(qvariant_cast<QCameraInfo>(action->data()));
398 }
399 
displayViewfinder()400 void Camera::displayViewfinder()
401 {
402     ui->stackedWidget->setCurrentIndex(0);
403 }
404 
displayCapturedImage()405 void Camera::displayCapturedImage()
406 {
407     ui->stackedWidget->setCurrentIndex(1);
408 }
409 
readyForCapture(bool ready)410 void Camera::readyForCapture(bool ready)
411 {
412     ui->takeImageButton->setEnabled(ready);
413 }
414 
imageSaved(int id,const QString & fileName)415 void Camera::imageSaved(int id, const QString &fileName)
416 {
417     Q_UNUSED(id);
418     ui->statusbar->showMessage(tr("Captured \"%1\"").arg(QDir::toNativeSeparators(fileName)));
419 
420     m_isCapturingImage = false;
421     if (m_applicationExiting)
422         close();
423 }
424 
closeEvent(QCloseEvent * event)425 void Camera::closeEvent(QCloseEvent *event)
426 {
427     if (m_isCapturingImage) {
428         setEnabled(false);
429         m_applicationExiting = true;
430         event->ignore();
431     } else {
432         event->accept();
433     }
434 }
435