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