1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of The Qt Company Ltd nor the names of its
21 **     contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "cameraexample.h"
42 #include "messagehandling.h"
43 #include "contactsdlg.h"
44 #include "button.h"
45 #include "businesscardhandling.h"
46 #include <QDebug>
47 
48 /*****************************************************************************
49 * MyVideoSurface
50 */
MyVideoSurface(QWidget * widget,VideoIF * target,QObject * parent)51 MyVideoSurface::MyVideoSurface(QWidget* widget, VideoIF* target, QObject* parent)
52     : QAbstractVideoSurface(parent)
53 {
54     m_targetWidget = widget;
55     m_target = target;
56     m_imageFormat = QImage::Format_Invalid;
57 }
58 
~MyVideoSurface()59 MyVideoSurface::~MyVideoSurface()
60 {
61 }
62 
start(const QVideoSurfaceFormat & format)63 bool MyVideoSurface::start(const QVideoSurfaceFormat &format)
64 {
65     m_videoFormat = format;
66     const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
67     const QSize size = format.frameSize();
68 
69     if (imageFormat != QImage::Format_Invalid && !size.isEmpty()) {
70         m_imageFormat = imageFormat;
71         QAbstractVideoSurface::start(format);
72         return true;
73     } else {
74         return false;
75     }
76 }
77 
present(const QVideoFrame & frame)78 bool MyVideoSurface::present(const QVideoFrame &frame)
79 {
80     m_frame = frame;
81     if (surfaceFormat().pixelFormat() != m_frame.pixelFormat() ||
82             surfaceFormat().frameSize() != m_frame.size()) {
83         stop();
84         return false;
85     } else {
86         m_target->updateVideo();
87         return true;
88     }
89 }
90 
paint(QPainter * painter)91 void MyVideoSurface::paint(QPainter *painter)
92  {
93      if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
94          QImage image(
95                  m_frame.bits(),
96                  m_frame.width(),
97                  m_frame.height(),
98                  m_frame.bytesPerLine(),
99                  m_imageFormat);
100 
101          QRect r = m_targetWidget->rect();
102          QPoint centerPic((qAbs(r.size().width() - image.size().width())) / 2, (qAbs(
103              r.size().height() - image.size().height())) / 2);
104 
105          if (!image.isNull()) {
106             painter->drawImage(centerPic,image);
107          }
108 
109          m_frame.unmap();
110      }
111  }
112 
supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const113 QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(
114             QAbstractVideoBuffer::HandleType handleType) const
115 {
116     if (handleType == QAbstractVideoBuffer::NoHandle) {
117         return QList<QVideoFrame::PixelFormat>()
118                 << QVideoFrame::Format_RGB32
119                 << QVideoFrame::Format_ARGB32
120                 << QVideoFrame::Format_ARGB32_Premultiplied
121                 << QVideoFrame::Format_RGB565
122                 << QVideoFrame::Format_RGB555;
123     } else {
124         return QList<QVideoFrame::PixelFormat>();
125     }
126 }
127 
128 
129 /*****************************************************************************
130 * CameraExample
131 */
CameraExample(QWidget * parent)132 CameraExample::CameraExample(QWidget *parent) :
133     QMainWindow(parent)
134 {
135     setWindowTitle("QCameraExample");
136 
137     // Opitimizations for screen update and drawing qwidget
138     setAutoFillBackground(false);
139 
140     // Prevent to screensaver to activate
141     m_systemScreenSaver = new QSystemScreenSaver(this);
142     m_systemScreenSaver->setScreenSaverInhibit();
143 
144     m_myVideoSurface = 0;
145     pictureCaptured = false;
146     showViewFinder = false;
147     m_focusing = false;
148 
149     // MMS handling
150     m_message = new Message(this);
151     QObject::connect(m_message, SIGNAL(messageStateChanged(int)), this, SLOT(messageStateChanged(int)));
152     QObject::connect(m_message, SIGNAL(messageReceived(QString,QString,QPixmap)), this, SLOT(messageReceived(QString,QString,QPixmap)));
153 
154     // Business card handling (Contact's avatar picture)
155     m_businessCardHandling = new BusinessCardHandling(this);
156 
157     // Black background
158     QPalette palette = this->palette();
159     palette.setColor(QPalette::Background, Qt::black);
160     setPalette(palette);
161 
162     // Main widget & layout
163     QWidget* mainWidget = new QWidget(this);
164     mainWidget->setPalette(palette);
165 
166     QHBoxLayout* hboxl = new QHBoxLayout;
167     hboxl->setSpacing(0);
168     hboxl->setMargin(0);
169 
170     // UI stack
171     m_stackedWidget = new QStackedWidget();
172     m_stackedWidget->setPalette(palette);
173 
174     // First widget to stack
175     m_videoWidget = new QWidget();
176     m_videoWidget->setPalette(palette);
177     m_stackedWidget->addWidget(m_videoWidget);
178 
179     // Second widget to stack
180     QWidget* secondWidget = new QWidget(this);
181     secondWidget->setPalette(palette);
182     m_stackedWidget->addWidget(secondWidget);
183     m_stackedWidget->setCurrentIndex(0);
184 
185     hboxl->addWidget(m_stackedWidget);
186 
187     // Buttons
188     QSize iconSize(80, 80);
189     QVBoxLayout* vboxl = new QVBoxLayout;
190     vboxl->setSpacing(0);
191     vboxl->setMargin(0);
192 
193     // Exit button
194     m_exit = new Button(this);
195     QObject::connect(m_exit, SIGNAL(pressed()), qApp, SLOT(quit()));
196     QPixmap p = QPixmap(":/icons/exit.png");
197     m_exit->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
198     vboxl->addWidget(m_exit);
199     vboxl->setAlignment(m_exit, Qt::AlignHCenter | Qt::AlignTop);
200 
201     // Camera button
202     m_cameraBtn = new Button(this);
203     QObject::connect(m_cameraBtn, SIGNAL(pressed()), this, SLOT(searchAndLock()));
204     p = QPixmap(":/icons/camera.png");
205     m_cameraBtn->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
206     vboxl->addWidget(m_cameraBtn);
207     vboxl->setAlignment(m_cameraBtn, Qt::AlignCenter);
208 
209     // Send MMS button
210     m_mms = new Button(this);
211     QObject::connect(m_mms, SIGNAL(pressed()), this, SLOT(openContactsDlg()));
212     p = QPixmap(":/icons/mms.png");
213     m_mms->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
214     vboxl->addWidget(m_mms);
215     vboxl->setAlignment(m_mms, Qt::AlignHCenter | Qt::AlignBottom);
216 #ifndef MESSAGING_ENABLED
217     m_mms->disableBtn(true);
218     m_mms->setEnabled(false);
219 #endif
220 
221     hboxl->addLayout(vboxl);
222     mainWidget->setLayout(hboxl);
223 
224     setCentralWidget(mainWidget);
225 
226     // Enable camera after 1s, so that the application is started
227     // and widget is created to landscape orientation
228     QTimer::singleShot(1000,this,SLOT(enableCamera()));
229 }
230 
~CameraExample()231 CameraExample::~CameraExample()
232 {
233 if (m_myVideoSurface)
234     m_myVideoSurface->stop();
235     m_camera->stop();
236     delete m_stackedWidget;
237     delete m_stillImageCapture;
238     delete m_camera;
239 }
240 
241 
enableCamera()242 void CameraExample::enableCamera()
243 {
244     m_camera = new QCamera();
245     m_camera->setCaptureMode(QCamera::CaptureStillImage);
246     connect(m_camera, SIGNAL(error(QCamera::Error)), this, SLOT(error(QCamera::Error)));
247     connect(m_camera, SIGNAL(lockStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)), this, SLOT(lockStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)));
248 
249     // Own video output drawing that shows camera view finder pictures
250     //! [0]
251     QMediaService* ms = m_camera->service();
252     QVideoRendererControl* vrc = ms->requestControl<QVideoRendererControl*>();
253     m_myVideoSurface = new MyVideoSurface(this,this,this);
254     vrc->setSurface(m_myVideoSurface);
255 //! [0]
256     // Image capturer
257     m_stillImageCapture = new QCameraImageCapture(m_camera);
258     connect(m_stillImageCapture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(imageCaptured(int,QImage)));
259 
260     // Start camera
261     if (m_camera->state() == QCamera::ActiveState) {
262         m_camera->stop();
263     }
264     m_videoWidget->show();
265     m_camera->start();
266     showViewFinder = true;
267 }
268 
mousePressEvent(QMouseEvent * event)269 void CameraExample::mousePressEvent(QMouseEvent *event)
270 {
271     QMainWindow::mousePressEvent(event);
272 
273     if (pictureCaptured) {
274         // Starting view finder
275         pictureCaptured = false;
276         m_stackedWidget->setCurrentIndex(0);
277         if (m_myVideoSurface) {
278             showViewFinder = true;
279         }
280     }
281 }
282 
searchAndLock()283 void CameraExample::searchAndLock()
284 {
285     m_focusing = false;
286     m_focusMessage.clear();
287 
288     if (pictureCaptured) {
289         // Starting view finder again
290         pictureCaptured = false;
291         m_stackedWidget->setCurrentIndex(0);
292         if (m_myVideoSurface) {
293             showViewFinder = true;
294         }
295     }
296     else {
297         // Search and lock picture (=focus)
298         if (m_camera->supportedLocks() & QCamera::LockFocus) {
299             m_focusing = true;
300             m_focusMessage = "Focusing...";
301             m_camera->searchAndLock(QCamera::LockFocus);
302         } else {
303         // No focus functionality, take picture right away
304             captureImage();
305         }
306     }
307 }
308 
lockStatusChanged(QCamera::LockStatus status,QCamera::LockChangeReason reason)309 void CameraExample::lockStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason)
310 {
311     if (status == QCamera::Locked) {
312         if (reason == QCamera::LockAcquired) {
313             // Focus locked
314             m_focusMessage.clear();
315             m_focusing = false;
316             // Capture new image
317             captureImage();
318             // Unlock camera
319             m_camera->unlock();
320         } else {
321             if (m_focusing)
322                 m_focusMessage = "No focus, try again";
323         }
324     } else if (status == QCamera::Unlocked && m_focusing) {
325         m_focusMessage = "No focus, try again";
326     }
327 }
328 
captureImage()329 void CameraExample::captureImage()
330 {
331     if (pictureCaptured) {
332         // Starting view finder again
333         pictureCaptured = false;
334         m_stackedWidget->setCurrentIndex(0);
335         showViewFinder = true;
336     }
337     else {
338         // Capturing image
339         showViewFinder = false;
340         // Get picture location where to store captured images
341         QString path(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
342         QDir dir(path);
343 
344         // Get next filename
345         QStringList files = dir.entryList(QStringList() << "camera_*.jpg");
346         int lastImage = 0;
347         foreach ( QString fileName, files ) {
348                 int imgNumber = fileName.mid(7, fileName.size() - 11).toInt();
349                 lastImage = qMax(lastImage, imgNumber);
350             }
351         // Capture image
352         if (m_stillImageCapture->isReadyForCapture()) {
353             m_imageName = QString("camera_%1.jpg").arg(lastImage+1);
354             m_stillImageCapture->capture(m_imageName);
355         }
356     }
357 }
358 
imageCaptured(int id,const QImage & preview)359 void CameraExample::imageCaptured(int id, const QImage &preview)
360 {
361     showViewFinder = false;
362     m_focusing = false;
363 
364     // Image captured, show it to the user
365     m_stackedWidget->setCurrentIndex(1);
366 
367     // Get picture location
368     QString path(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
369     m_imageName.prepend(path + "/");
370 
371     m_capturedImage = preview;
372 
373     // Set suitable size to the image
374     QSize s = m_videoWidget->size();
375     s = s - QSize(20, 20);
376     m_capturedImage = m_capturedImage.scaled(s, Qt::KeepAspectRatio, Qt::SmoothTransformation);
377 
378     pictureCaptured = true;
379     update();
380 }
381 
error(QCamera::Error e)382 void CameraExample::error(QCamera::Error e)
383 {
384     switch (e) {
385     case QCamera::NoError:
386     {
387         break;
388     }
389     case QCamera::CameraError:
390     {
391         QMessageBox::warning(this, "QCameraExample", "General Camera error");
392         break;
393     }
394     case QCamera::InvalidRequestError:
395     {
396         QMessageBox::warning(this, "QCameraExample", "Camera invalid request error");
397         break;
398     }
399     case QCamera::ServiceMissingError:
400     {
401         QMessageBox::warning(this, "QCameraExample", "Camera service missing error");
402         break;
403     }
404     case QCamera::NotSupportedFeatureError :
405     {
406         QMessageBox::warning(this, "QCameraExample", "Camera not supported error");
407         break;
408     }
409     };
410 }
411 
openContactsDlg()412 void CameraExample::openContactsDlg()
413 {
414     // Open dialog for showing contacts
415     if (!m_contactsDialog) {
416 
417         if (m_capturedImage.isNull()) {
418             QMessageBox::information(this, "QCameraExample", "Take picture first");
419             return;
420         }
421 
422         // Show dialog
423         m_contactsDialog = new ContactsDialog(this);
424         QObject::connect(m_contactsDialog, SIGNAL(contactSelected(QString)),
425         this, SLOT(contactSelected(QString)));
426         m_contactsDialog->exec();
427         QObject::disconnect(m_contactsDialog, SIGNAL(contactSelected(QString)),
428         this, SLOT(contactSelected(QString)));
429 
430         delete m_contactsDialog;
431         m_contactsDialog = 0;
432     }
433 }
434 
contactSelected(QString phoneNumber)435 void CameraExample::contactSelected(QString phoneNumber)
436 {
437     m_phoneNumber = phoneNumber;
438     QTimer::singleShot(1000,this,SLOT(sendMMS()));
439 }
440 
sendMMS()441 void CameraExample::sendMMS()
442 {
443 #ifdef MESSAGING_ENABLED
444     m_message->sendMMS(m_imageName, m_phoneNumber);
445 #endif
446 }
447 
messageStateChanged(int)448 void CameraExample::messageStateChanged(int /*error*/)
449 {
450 }
451 
updateVideo()452 void CameraExample::updateVideo()
453 {
454     if (showViewFinder) {
455         repaint();
456     }
457 }
458 
paintEvent(QPaintEvent * event)459 void CameraExample::paintEvent(QPaintEvent *event)
460 {
461     //QMainWindow::paintEvent(event);
462 
463     QPainter painter(this);
464     QRect r = this->rect();
465 
466     QFont font = painter.font();
467     font.setPixelSize(20);
468     painter.setFont(font);
469     painter.setPen(Qt::white);
470 
471     if (showViewFinder && m_myVideoSurface && m_myVideoSurface->isActive()) {
472         // Show view finder
473         m_myVideoSurface->paint(&painter);
474 
475         // Paint focus message
476         if (!m_focusMessage.isEmpty())
477             painter.drawText(r, Qt::AlignCenter, m_focusMessage);
478 
479     } else {
480         // Draw black
481         painter.fillRect(event->rect(), palette().background());
482         // Show captured image
483         if (pictureCaptured) {
484             // Paint captured image
485             QPoint centerPic((qAbs(r.size().width() - m_capturedImage.size().width())) / 2, (qAbs(
486                 r.size().height() - m_capturedImage.size().height())) / 2);
487 
488             painter.drawImage(centerPic, m_capturedImage);
489 
490             // Paint filename
491             painter.drawText(r, Qt::AlignBottom | Qt::AlignCenter, m_imageName);
492         }
493     }
494 
495 }
496 
messageReceived(QString phoneNumber,QString filename,QPixmap pixmap)497 void CameraExample::messageReceived(QString phoneNumber, QString filename, QPixmap pixmap)
498 {
499 #ifdef MESSAGING_ENABLED
500     // MMS message received
501     // Check that is came from some of our contact
502     QContact contact;
503     if (m_businessCardHandling->findContact(phoneNumber, contact)) {
504         // Ask from user to store it as sender avatar picture into contacts
505         if (QMessageBox::question(
506             this,
507             "QCameraExample",
508             QString(
509                 "MMS picture message received from %1. Do you want to store it as sender avatar picture?").arg(
510                 contact.displayLabel()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
511 
512             m_businessCardHandling->storeAvatarToContact(phoneNumber, filename, pixmap);
513         }
514     }
515 #endif
516 }
517 
518