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 "qtcamera.h" 52#include <QApplication> 53#include <QMediaService> 54#include <QMediaRecorder> 55#include <QCameraViewfinder> 56#include <QCameraInfo> 57#include <QMediaMetaData> 58 59#include <QMessageBox> 60#include <QPalette> 61#include <QTabWidget> 62#include <QtWidgets> 63#include <QHBoxLayout> 64#include <QVBoxLayout> 65 66 67#define QCAMERA_CAPTURE_MODE "Image Mode" 68#define QCAMERA_VIDEO_MODE "Video Mode" 69#define DIR_USERDATA "/userdata" 70#define DIR_HOME QStandardPaths::writableLocation(QStandardPaths::HomeLocation) 71Q_DECLARE_METATYPE(QCameraInfo) 72 73qtCamera::qtCamera() 74{ 75 initlayout(); 76 QFileInfo fi(DIR_USERDATA); 77 if(fi.isDir()){ 78 locationDir = DIR_USERDATA; 79 }else { 80 QFileInfo fi(DIR_HOME); 81 if(fi.isDir()){ 82 locationDir = DIR_HOME; 83 } 84 } 85 imageCnt = videoCnt = 0; 86 setCamera(QCameraInfo::defaultCamera()); 87} 88 89void qtCamera::initlayout() 90{ 91 QBoxLayout *vLayout = new QVBoxLayout(); 92 const QRect availableGeometry = QApplication::desktop()->availableGeometry(this); 93 resize(availableGeometry.width(), availableGeometry.height()); 94 95 const QList<QCameraInfo> availableCameras = QCameraInfo::availableCameras(); 96 for (const QCameraInfo &cameraInfo : availableCameras) { 97// qDebug() << cameraInfo.description(); 98 QPushButton *camera = getButton(); 99 camera->setText(cameraInfo.description()); 100 camera->setCheckable(true); 101 if (cameraInfo == QCameraInfo::defaultCamera()){ 102 camera->setDefault(true); 103 }else { 104 camera->setDefault(false); 105 } 106 connect(camera, SIGNAL(clicked(bool)), this, SLOT(on_cameraSwitch())); 107 vLayout->addWidget(camera); 108 } 109 110 modeButton = getButton(); 111 modeButton->setText(cameraMode); 112 connect(modeButton, SIGNAL(clicked(bool)), this, SLOT(updateCaptureMode())); 113 114 captureButton = getButton(); 115 captureButton->setText(tr("Capture")); 116 connect(captureButton, SIGNAL(clicked(bool)), this, SLOT(on_captureClicked())); 117 118 exitButton = getButton(); 119 exitButton->setText(tr("Exit")); 120 connect(exitButton, SIGNAL(clicked(bool)), this, SLOT(on_exitClicked())); 121 122 vLayout->addWidget(modeButton); 123 vLayout->addWidget(captureButton); 124 vLayout->addWidget(exitButton); 125 vLayout->setAlignment(Qt::AlignTop); 126 127 viewfinder.setWindowFlag(Qt::FramelessWindowHint); 128 viewfinder.setFixedSize(availableGeometry.width() - 150, availableGeometry.height()); 129 130 QBoxLayout *hlayout = new QHBoxLayout; 131 hlayout->setMargin(0); 132 hlayout->addWidget(&viewfinder); 133 hlayout->addLayout(vLayout); 134 135 QWidget *widget = new QWidget; 136 widget->setLayout(hlayout); 137 setCentralWidget(widget); 138// setWindowState(Qt::WindowMaximized); 139 setWindowFlags(Qt::FramelessWindowHint); 140} 141 142void qtCamera::setCamera(const QCameraInfo &cameraInfo) 143{ 144 m_camera.reset(new QCamera(cameraInfo)); 145 146 connect(m_camera.data(), &QCamera::stateChanged, this, &qtCamera::updateCameraState); 147 connect(m_camera.data(), QOverload<QCamera::Error>::of(&QCamera::error), this, &qtCamera::displayCameraError); 148 149 m_mediaRecorder.reset(new QMediaRecorder(m_camera.data())); 150 connect(m_mediaRecorder.data(), &QMediaRecorder::stateChanged, this, &qtCamera::updateRecorderState); 151 152 m_imageCapture.reset(new QCameraImageCapture(m_camera.data())); 153 154 connect(m_mediaRecorder.data(), &QMediaRecorder::durationChanged, this, &qtCamera::updateRecordTime); 155 connect(m_mediaRecorder.data(), QOverload<QMediaRecorder::Error>::of(&QMediaRecorder::error), 156 this, &qtCamera::displayRecorderError); 157 158 m_mediaRecorder->setMetaData(QMediaMetaData::Title, QVariant(QLatin1String("Test Title"))); 159 160 configureCaptureSettings(); 161 162 m_camera->setViewfinder(&viewfinder); 163 164 updateCameraState(m_camera->state()); 165 updateRecorderState(m_mediaRecorder->state()); 166 167 connect(m_imageCapture.data(), &QCameraImageCapture::imageSaved, this, &qtCamera::imageSaved); 168 connect(m_imageCapture.data(), QOverload<int, QCameraImageCapture::Error, const QString &>::of(&QCameraImageCapture::error), 169 this, &qtCamera::displayCaptureError); 170 171 updateCaptureMode(); 172} 173 174void qtCamera::configureCaptureSettings() 175{ 176 QSize size(640, 480); 177 178 m_imageSettings.setCodec("jpeg"); 179 m_imageSettings.setQuality(QMultimedia::VeryHighQuality); 180 m_imageSettings.setResolution(size); 181 m_imageCapture->setEncodingSettings(m_imageSettings); 182 183 m_audioSettings.setCodec("audio/x-adpcm"); 184 m_audioSettings.setChannelCount(2); 185 m_audioSettings.setQuality(QMultimedia::NormalQuality); 186 m_mediaRecorder->setAudioSettings(m_audioSettings); 187 188 m_videoSettings.setCodec("video/x-h264"); 189 m_videoSettings.setResolution(size); 190 m_videoSettings.setQuality(QMultimedia::NormalQuality); 191 m_mediaRecorder->setVideoSettings(m_videoSettings); 192 193 m_mediaRecorder->setContainerFormat("video/quicktime"); 194 195 if (0) { 196 QList<QSize> supportedResolutions; 197 supportedResolutions = m_imageCapture->supportedResolutions(); 198 for (const QSize &resolution : supportedResolutions) { 199 qDebug() << "image resolution: " << resolution.width() << "x" << resolution.height(); 200 } 201 202 supportedResolutions = m_mediaRecorder->supportedResolutions(); 203 for (const QSize &resolution : supportedResolutions) { 204 qDebug() << "video resolution: " << resolution.width() << "x" << resolution.height(); 205 } 206 207 const QStringList supportedAudioCodecs = m_mediaRecorder->supportedAudioCodecs(); 208 for (const QString &codecName : supportedAudioCodecs) { 209 QString description = m_mediaRecorder->audioCodecDescription(codecName); 210 qDebug() << "audio codec:" << codecName + ": " + description; 211 } 212 213 const QStringList supportedVideoCodecs = m_mediaRecorder->supportedVideoCodecs(); 214 for (const QString &codecName : supportedVideoCodecs) { 215 QString description = m_mediaRecorder->videoCodecDescription(codecName); 216 qDebug() << "video codec:" << codecName + ": " + description; 217 } 218 219 const QStringList formats = m_mediaRecorder->supportedContainers(); 220 for (const QString &format : formats) { 221<<<<<<< Updated upstream 222 QString description = m_mediaRecorder->containerDescription(format); 223 qDebug() << "container: " << format << ": " << description; 224 } 225======= 226 qDebug() << format; 227 } 228 229 supportedResolutions = m_mediaRecorder->supportedResolutions(); 230 for (const QSize &resolution : supportedResolutions) { 231 if(size.width()<resolution.width() && size.height()<resolution.height()) 232 size = resolution; 233 } 234 235 m_audioSettings.setCodec("audio/mpeg"); 236 m_audioSettings.setQuality(QMultimedia::VeryHighQuality); 237 m_videoSettings.setCodec("video/x-h264"); 238 m_videoSettings.setQuality(QMultimedia::VeryHighQuality); 239 m_videoSettings.setResolution(size); 240 m_mediaRecorder->setAudioSettings(m_audioSettings); 241 m_mediaRecorder->setVideoSettings(m_videoSettings); 242 m_mediaRecorder->setContainerFormat("video/quicktime, variant=(string)iso"); 243 break; 244 } 245 default: 246 break; 247>>>>>>> Stashed changes 248 } 249} 250 251QPushButton* qtCamera::getButton() 252{ 253 QPushButton *button = new QPushButton; 254 button->setFixedSize(144, 70); 255 return button; 256} 257 258void qtCamera::updateRecordTime() 259{ 260 QString str = QString("Recorded %1 sec").arg(m_mediaRecorder->duration()/1000); 261 statusBar()->showMessage(str); 262} 263 264void qtCamera::record() 265{ 266 QFileInfo fi; 267 QString lo; 268 269 lo = locationDir + "/" + "VIDEO" + QString::number(videoCnt) + ".mov"; 270 fi = QFileInfo(lo); 271 272 while(fi.isFile()){ 273 videoCnt++; 274 lo = locationDir + "/" + "VIDEO" + QString::number(videoCnt) + ".mov"; 275 fi = QFileInfo(lo); 276 } 277 278 m_mediaRecorder->setOutputLocation(QUrl::fromLocalFile(lo)); 279 m_mediaRecorder->record(); 280 updateRecordTime(); 281} 282 283void qtCamera::stop() 284{ 285 m_mediaRecorder->stop(); 286} 287 288void qtCamera::takeImage() 289{ 290 m_isCapturingImage = true; 291 QFileInfo fi; 292 QString lo; 293 294 lo = locationDir + "/" + "PIC" + QString::number(imageCnt) + ".jpg"; 295 fi = QFileInfo(lo); 296 297 while(fi.isFile()){ 298 imageCnt++; 299 lo = locationDir + "/" + "PIC" + QString::number(imageCnt) + ".jpg"; 300 fi = QFileInfo(lo); 301 } 302 303 m_imageCapture->capture(lo); 304} 305 306void qtCamera::displayCaptureError(int id, const QCameraImageCapture::Error error, const QString &errorString) 307{ 308 Q_UNUSED(id); 309 Q_UNUSED(error); 310 QMessageBox::warning(this, tr("Image Capture Error"), errorString); 311 m_isCapturingImage = false; 312} 313 314void qtCamera::updateCaptureMode() 315{ 316 QCamera::CaptureModes captureMode; 317 QString capture; 318 if (cameraMode.compare(QCAMERA_CAPTURE_MODE)){ 319 captureMode = QCamera::CaptureStillImage ; 320 }else { 321 captureMode = QCamera::CaptureVideo; 322 } 323 324 if (m_camera->isCaptureModeSupported(captureMode)){ 325 m_camera->unload(); 326 m_camera->setCaptureMode(captureMode); 327 m_camera->start(); 328 if(captureMode == QCamera::CaptureStillImage){ 329 cameraMode = QString(QCAMERA_CAPTURE_MODE); 330 capture = "Capture"; 331 }else { 332 cameraMode = QString(QCAMERA_VIDEO_MODE); 333 capture = "Record"; 334 } 335 modeButton->setText(cameraMode); 336 captureButton->setText(capture); 337 } 338} 339 340void qtCamera::updateCameraState(QCamera::State state) 341{ 342 switch (state) { 343 case QCamera::ActiveState: 344 break; 345 case QCamera::UnloadedState: 346 case QCamera::LoadedState: 347 break; 348 } 349} 350 351void qtCamera::updateRecorderState(QMediaRecorder::State state) 352{ 353 switch (state) { 354 case QMediaRecorder::StoppedState: 355 captureButton->setText(tr("Record")); 356 break; 357 case QMediaRecorder::PausedState: 358 break; 359 case QMediaRecorder::RecordingState: 360 captureButton->setText(tr("Recording")); 361 break; 362 } 363} 364 365void qtCamera::displayRecorderError() 366{ 367 QMessageBox::warning(this, tr("Capture Error"), m_mediaRecorder->errorString()); 368} 369 370void qtCamera::displayCameraError() 371{ 372 QMessageBox::warning(this, tr("Camera Error"), m_camera->errorString()); 373} 374 375void qtCamera::imageSaved(int id, const QString &fileName) 376{ 377 Q_UNUSED(id); 378 statusBar()->showMessage(tr("Captured \"%1\"").arg(QDir::toNativeSeparators(fileName))); 379 statusBar()->show(); 380 m_isCapturingImage = false; 381 if (m_applicationExiting) 382 close(); 383} 384 385void qtCamera::closeEvent(QCloseEvent *event) 386{ 387 if (m_isCapturingImage) { 388 setEnabled(false); 389 m_applicationExiting = true; 390 event->ignore(); 391 } else { 392 event->accept(); 393 } 394} 395 396void qtCamera::on_cameraSwitch() 397{ 398 QList<QPushButton *> buttons = centralWidget()->findChildren<QPushButton *>(); 399 for(auto *but: buttons){ 400 if(but->isChecked()){ 401 for(auto *button: buttons){ 402 if(button->isDefault()) 403 button->setDefault(false); 404 } 405 but->setDefault(true); 406 but->setChecked(false); 407 qDebug() << "switch to " + but->text(); 408 const QList<QCameraInfo> availableCameras = QCameraInfo::availableCameras(); 409 for (const QCameraInfo &cameraInfo : availableCameras) { 410 if(! but->text().compare(cameraInfo.description())){ 411 qDebug() << cameraInfo.description(); 412 setCamera(cameraInfo); 413 } 414 } 415 break; 416 } 417 } 418} 419 420void qtCamera::on_captureClicked() 421{ 422 if (m_camera->captureMode() == QCamera::CaptureStillImage) { 423 takeImage(); 424 } else { 425 if (m_mediaRecorder->state() == QMediaRecorder::RecordingState) 426 stop(); 427 else 428 record(); 429 } 430} 431 432void qtCamera::on_exitClicked() 433{ 434 qApp->exit(0); 435} 436