cameraexample.cpp Example File

demos/mobile/qcamera/cameraexample.cpp
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 "cameraexample.h"
#include "messagehandling.h"
#include "contactsdlg.h"
#include "button.h"
#include "businesscardhandling.h"
#include <QDebug>

/*****************************************************************************
* MyVideoSurface
*/
MyVideoSurface::MyVideoSurface(QWidget* widget, VideoIF* target, QObject* parent)
    : QAbstractVideoSurface(parent)
{
    m_targetWidget = widget;
    m_target = target;
    m_imageFormat = QImage::Format_Invalid;
}

MyVideoSurface::~MyVideoSurface()
{
}

bool MyVideoSurface::start(const QVideoSurfaceFormat &format)
{
    m_videoFormat = format;
    const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
    const QSize size = format.frameSize();

    if (imageFormat != QImage::Format_Invalid && !size.isEmpty()) {
        m_imageFormat = imageFormat;
        QAbstractVideoSurface::start(format);
        return true;
    } else {
        return false;
    }
}

bool MyVideoSurface::present(const QVideoFrame &frame)
{
    m_frame = frame;
    if (surfaceFormat().pixelFormat() != m_frame.pixelFormat() ||
            surfaceFormat().frameSize() != m_frame.size()) {
        stop();
        return false;
    } else {
        m_target->updateVideo();
        return true;
    }
}

void MyVideoSurface::paint(QPainter *painter)
 {
     if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
         QImage image(
                 m_frame.bits(),
                 m_frame.width(),
                 m_frame.height(),
                 m_frame.bytesPerLine(),
                 m_imageFormat);

         QRect r = m_targetWidget->rect();
         QPoint centerPic((qAbs(r.size().width() - image.size().width())) / 2, (qAbs(
             r.size().height() - image.size().height())) / 2);

         if (!image.isNull()) {
            painter->drawImage(centerPic,image);
         }

         m_frame.unmap();
     }
 }

QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(
            QAbstractVideoBuffer::HandleType handleType) const
{
    if (handleType == QAbstractVideoBuffer::NoHandle) {
        return QList<QVideoFrame::PixelFormat>()
                << QVideoFrame::Format_RGB32
                << QVideoFrame::Format_ARGB32
                << QVideoFrame::Format_ARGB32_Premultiplied
                << QVideoFrame::Format_RGB565
                << QVideoFrame::Format_RGB555;
    } else {
        return QList<QVideoFrame::PixelFormat>();
    }
}

/*****************************************************************************
* CameraExample
*/
CameraExample::CameraExample(QWidget *parent) :
    QMainWindow(parent)
{
    setWindowTitle("QCameraExample");

    // Opitimizations for screen update and drawing qwidget
    setAutoFillBackground(false);

    // Prevent to screensaver to activate
    m_systemScreenSaver = new QSystemScreenSaver(this);
    m_systemScreenSaver->setScreenSaverInhibit();

    m_myVideoSurface = 0;
    pictureCaptured = false;
    showViewFinder = false;
    m_focusing = false;

    // MMS handling
    m_message = new Message(this);
    QObject::connect(m_message, SIGNAL(messageStateChanged(int)), this, SLOT(messageStateChanged(int)));
    QObject::connect(m_message, SIGNAL(messageReceived(QString,QString,QPixmap)), this, SLOT(messageReceived(QString,QString,QPixmap)));

    // Business card handling (Contact's avatar picture)
    m_businessCardHandling = new BusinessCardHandling(this);

    // Black background
    QPalette palette = this->palette();
    palette.setColor(QPalette::Background, Qt::black);
    setPalette(palette);

    // Main widget & layout
    QWidget* mainWidget = new QWidget(this);
    mainWidget->setPalette(palette);

    QHBoxLayout* hboxl = new QHBoxLayout;
    hboxl->setSpacing(0);
    hboxl->setMargin(0);

    // UI stack
    m_stackedWidget = new QStackedWidget();
    m_stackedWidget->setPalette(palette);

    // First widget to stack
    m_videoWidget = new QWidget();
    m_videoWidget->setPalette(palette);
    m_stackedWidget->addWidget(m_videoWidget);

    // Second widget to stack
    QWidget* secondWidget = new QWidget(this);
    secondWidget->setPalette(palette);
    m_stackedWidget->addWidget(secondWidget);
    m_stackedWidget->setCurrentIndex(0);

    hboxl->addWidget(m_stackedWidget);

    // Buttons
    QSize iconSize(80, 80);
    QVBoxLayout* vboxl = new QVBoxLayout;
    vboxl->setSpacing(0);
    vboxl->setMargin(0);

    // Exit button
    m_exit = new Button(this);
    QObject::connect(m_exit, SIGNAL(pressed()), qApp, SLOT(quit()));
    QPixmap p = QPixmap(":/icons/exit.png");
    m_exit->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    vboxl->addWidget(m_exit);
    vboxl->setAlignment(m_exit, Qt::AlignHCenter | Qt::AlignTop);

    // Camera button
    m_cameraBtn = new Button(this);
    QObject::connect(m_cameraBtn, SIGNAL(pressed()), this, SLOT(searchAndLock()));
    p = QPixmap(":/icons/camera.png");
    m_cameraBtn->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    vboxl->addWidget(m_cameraBtn);
    vboxl->setAlignment(m_cameraBtn, Qt::AlignCenter);

    // Send MMS button
    m_mms = new Button(this);
    QObject::connect(m_mms, SIGNAL(pressed()), this, SLOT(openContactsDlg()));
    p = QPixmap(":/icons/mms.png");
    m_mms->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    vboxl->addWidget(m_mms);
    vboxl->setAlignment(m_mms, Qt::AlignHCenter | Qt::AlignBottom);
#ifndef MESSAGING_ENABLED
    m_mms->disableBtn(true);
    m_mms->setEnabled(false);
#endif

    hboxl->addLayout(vboxl);
    mainWidget->setLayout(hboxl);

    setCentralWidget(mainWidget);

    // Enable camera after 1s, so that the application is started
    // and widget is created to landscape orientation
    QTimer::singleShot(1000,this,SLOT(enableCamera()));
}

CameraExample::~CameraExample()
{
if (m_myVideoSurface)
    m_myVideoSurface->stop();
    m_camera->stop();
    delete m_stackedWidget;
    delete m_stillImageCapture;
    delete m_camera;
}

void CameraExample::enableCamera()
{
    m_camera = new QCamera();
    m_camera->setCaptureMode(QCamera::CaptureStillImage);
    connect(m_camera, SIGNAL(error(QCamera::Error)), this, SLOT(error(QCamera::Error)));
    connect(m_camera, SIGNAL(lockStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)), this, SLOT(lockStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)));

    // Own video output drawing that shows camera view finder pictures
    QMediaService* ms = m_camera->service();
    QVideoRendererControl* vrc = ms->requestControl<QVideoRendererControl*>();
    m_myVideoSurface = new MyVideoSurface(this,this,this);
    vrc->setSurface(m_myVideoSurface);
    // Image capturer
    m_stillImageCapture = new QCameraImageCapture(m_camera);
    connect(m_stillImageCapture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(imageCaptured(int,QImage)));

    // Start camera
    if (m_camera->state() == QCamera::ActiveState) {
        m_camera->stop();
    }
    m_videoWidget->show();
    m_camera->start();
    showViewFinder = true;
}

void CameraExample::mousePressEvent(QMouseEvent *event)
{
    QMainWindow::mousePressEvent(event);

    if (pictureCaptured) {
        // Starting view finder
        pictureCaptured = false;
        m_stackedWidget->setCurrentIndex(0);
        if (m_myVideoSurface) {
            showViewFinder = true;
        }
    }
}

void CameraExample::searchAndLock()
{
    m_focusing = false;
    m_focusMessage.clear();

    if (pictureCaptured) {
        // Starting view finder again
        pictureCaptured = false;
        m_stackedWidget->setCurrentIndex(0);
        if (m_myVideoSurface) {
            showViewFinder = true;
        }
    }
    else {
        // Search and lock picture (=focus)
        if (m_camera->supportedLocks() & QCamera::LockFocus) {
            m_focusing = true;
            m_focusMessage = "Focusing...";
            m_camera->searchAndLock(QCamera::LockFocus);
        } else {
        // No focus functionality, take picture right away
            captureImage();
        }
    }
}

void CameraExample::lockStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason)
{
    if (status == QCamera::Locked) {
        if (reason == QCamera::LockAcquired) {
            // Focus locked
            m_focusMessage.clear();
            m_focusing = false;
            // Capture new image
            captureImage();
            // Unlock camera
            m_camera->unlock();
        } else {
            if (m_focusing)
                m_focusMessage = "No focus, try again";
        }
    } else if (status == QCamera::Unlocked && m_focusing) {
        m_focusMessage = "No focus, try again";
    }
}

void CameraExample::captureImage()
{
    if (pictureCaptured) {
        // Starting view finder again
        pictureCaptured = false;
        m_stackedWidget->setCurrentIndex(0);
        showViewFinder = true;
    }
    else {
        // Capturing image
        showViewFinder = false;
        // Get picture location where to store captured images
        QString path(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
        QDir dir(path);

        // Get next filename
        QStringList files = dir.entryList(QStringList() << "camera_*.jpg");
        int lastImage = 0;
        foreach ( QString fileName, files ) {
                int imgNumber = fileName.mid(7, fileName.size() - 11).toInt();
                lastImage = qMax(lastImage, imgNumber);
            }
        // Capture image
        if (m_stillImageCapture->isReadyForCapture()) {
            m_imageName = QString("camera_%1.jpg").arg(lastImage+1);
            m_stillImageCapture->capture(m_imageName);
        }
    }
}

void CameraExample::imageCaptured(int id, const QImage &preview)
{
    showViewFinder = false;
    m_focusing = false;

    // Image captured, show it to the user
    m_stackedWidget->setCurrentIndex(1);

    // Get picture location
    QString path(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
    m_imageName.prepend(path + "/");

    m_capturedImage = preview;

    // Set suitable size to the image
    QSize s = m_videoWidget->size();
    s = s - QSize(20, 20);
    m_capturedImage = m_capturedImage.scaled(s, Qt::KeepAspectRatio, Qt::SmoothTransformation);

    pictureCaptured = true;
    update();
}

void CameraExample::error(QCamera::Error e)
{
    switch (e) {
    case QCamera::NoError:
    {
        break;
    }
    case QCamera::CameraError:
    {
        QMessageBox::warning(this, "QCameraExample", "General Camera error");
        break;
    }
    case QCamera::InvalidRequestError:
    {
        QMessageBox::warning(this, "QCameraExample", "Camera invalid request error");
        break;
    }
    case QCamera::ServiceMissingError:
    {
        QMessageBox::warning(this, "QCameraExample", "Camera service missing error");
        break;
    }
    case QCamera::NotSupportedFeatureError :
    {
        QMessageBox::warning(this, "QCameraExample", "Camera not supported error");
        break;
    }
    };
}

void CameraExample::openContactsDlg()
{
    // Open dialog for showing contacts
    if (!m_contactsDialog) {

        if (m_capturedImage.isNull()) {
            QMessageBox::information(this, "QCameraExample", "Take picture first");
            return;
        }

        // Show dialog
        m_contactsDialog = new ContactsDialog(this);
        QObject::connect(m_contactsDialog, SIGNAL(contactSelected(QString)),
        this, SLOT(contactSelected(QString)));
        m_contactsDialog->exec();
        QObject::disconnect(m_contactsDialog, SIGNAL(contactSelected(QString)),
        this, SLOT(contactSelected(QString)));

        delete m_contactsDialog;
        m_contactsDialog = 0;
    }
}

void CameraExample::contactSelected(QString phoneNumber)
{
    m_phoneNumber = phoneNumber;
    QTimer::singleShot(1000,this,SLOT(sendMMS()));
}

void CameraExample::sendMMS()
{
#ifdef MESSAGING_ENABLED
    m_message->sendMMS(m_imageName, m_phoneNumber);
#endif
}

void CameraExample::messageStateChanged(int /*error*/)
{
}

void CameraExample::updateVideo()
{
    if (showViewFinder) {
        repaint();
    }
}

void CameraExample::paintEvent(QPaintEvent *event)
{
    //QMainWindow::paintEvent(event);

    QPainter painter(this);
    QRect r = this->rect();

    QFont font = painter.font();
    font.setPixelSize(20);
    painter.setFont(font);
    painter.setPen(Qt::white);

    if (showViewFinder && m_myVideoSurface && m_myVideoSurface->isActive()) {
        // Show view finder
        m_myVideoSurface->paint(&painter);

        // Paint focus message
        if (!m_focusMessage.isEmpty())
            painter.drawText(r, Qt::AlignCenter, m_focusMessage);

    } else {
        // Draw black
        painter.fillRect(event->rect(), palette().background());
        // Show captured image
        if (pictureCaptured) {
            // Paint captured image
            QPoint centerPic((qAbs(r.size().width() - m_capturedImage.size().width())) / 2, (qAbs(
                r.size().height() - m_capturedImage.size().height())) / 2);

            painter.drawImage(centerPic, m_capturedImage);

            // Paint filename
            painter.drawText(r, Qt::AlignBottom | Qt::AlignCenter, m_imageName);
        }
    }

}

void CameraExample::messageReceived(QString phoneNumber, QString filename, QPixmap pixmap)
{
#ifdef MESSAGING_ENABLED
    // MMS message received
    // Check that is came from some of our contact
    QContact contact;
    if (m_businessCardHandling->findContact(phoneNumber, contact)) {
        // Ask from user to store it as sender avatar picture into contacts
        if (QMessageBox::question(
            this,
            "QCameraExample",
            QString(
                "MMS picture message received from %1. Do you want to store it as sender avatar picture?").arg(
                contact.displayLabel()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {

            m_businessCardHandling->storeAvatarToContact(phoneNumber, filename, pixmap);
        }
    }
#endif
}

© 2016 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.