diff --git a/src/QrCodeScannerFilter.cpp b/src/QrCodeScannerFilter.cpp
index 9d44e5a..b20255e 100644
--- a/src/QrCodeScannerFilter.cpp
+++ b/src/QrCodeScannerFilter.cpp
@@ -1,127 +1,139 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
#include "QrCodeScannerFilter.h"
#include
#include
QrCodeScannerFilter::QrCodeScannerFilter(QObject *parent)
: QAbstractVideoFilter(parent),
m_decoder(new QrCodeDecoder(this))
{
connect(m_decoder, &QrCodeDecoder::decodingFailed,
this, &QrCodeScannerFilter::scanningFailed);
connect(m_decoder, &QrCodeDecoder::decodingSucceeded,
this, &QrCodeScannerFilter::scanningSucceeded);
}
QrCodeScannerFilter::~QrCodeScannerFilter()
{
if (!m_processThread.isFinished()) {
m_processThread.cancel();
m_processThread.waitForFinished();
}
}
QrCodeDecoder *QrCodeScannerFilter::decoder()
{
return m_decoder;
}
QVideoFilterRunnable *QrCodeScannerFilter::createFilterRunnable()
{
return new QrCodeScannerFilterRunnable(this);
}
+void QrCodeScannerFilter::setCameraDefaultVideoFormat(QObject *qmlCamera)
+{
+ QCamera *camera = qvariant_cast(qmlCamera->property("mediaObject"));
+ if (camera) {
+ QCameraViewfinderSettings settings = camera->viewfinderSettings();
+ settings.setPixelFormat(QVideoFrame::Format_RGB24);
+ camera->setViewfinderSettings(settings);
+ } else {
+ qWarning() << "Could not set pixel format of QML camera";
+ }
+}
+
QrCodeScannerFilterRunnable::QrCodeScannerFilterRunnable(QrCodeScannerFilter *filter)
: QObject(nullptr),
m_filter(filter)
{
}
QVideoFrame QrCodeScannerFilterRunnable::run(
QVideoFrame *input,
const QVideoSurfaceFormat &,
RunFlags
) {
// Only one frame is processed at a time.
if (input == nullptr
|| !input->isValid()
|| !m_filter->m_processThread.isFinished()) {
return *input;
}
// Copy the data to be filtered.
m_filter->m_frame.setData(*input);
// Run a separate thread for processing the data.
m_filter->m_processThread = QtConcurrent::run(
this,
&QrCodeScannerFilterRunnable::processVideoFrameProbed,
m_filter->m_frame,
m_filter
);
return *input;
}
void QrCodeScannerFilterRunnable::processVideoFrameProbed(
QrCodeVideoFrame videoFrame,
QrCodeScannerFilter *filter
) {
// Return if the frame is empty.
- if (videoFrame.data().length() < 1)
+ if (videoFrame.data().isEmpty())
return;
// Create an image from the frame.
const QImage *image = videoFrame.toGrayscaleImage();
// Return if conversion from the frame to the image failed.
if (image->isNull()) {
// dirty hack: write QVideoFrame::PixelFormat as string to format using QDebug
// QMetaEnum::valueToKey() did not work
QString format;
- QDebug(&format) << videoFrame.pixelFormat();
+ QDebug(&format).nospace() << videoFrame.pixelFormat();
qDebug() << "QrCodeScannerFilterRunnable error: Cannot create image file to process.";
qDebug() << "Maybe it was a format conversion problem.";
qDebug() << "VideoFrame format:" << format;
qDebug() << "Image corresponding format:"
<< QVideoFrame::imageFormatFromPixelFormat(videoFrame.pixelFormat());
emit filter->unsupportedFormatReceived(format);
return;
}
// Decode the image.
m_filter->decoder()->decodeImage(*image);
delete image;
}
diff --git a/src/QrCodeScannerFilter.h b/src/QrCodeScannerFilter.h
index 07a50f9..dd4e5f5 100644
--- a/src/QrCodeScannerFilter.h
+++ b/src/QrCodeScannerFilter.h
@@ -1,125 +1,131 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
#ifndef QRCODESCANNERFILTER_H
#define QRCODESCANNERFILTER_H
#include
#include
#include
#include
#include "QrCodeDecoder.h"
#include "QrCodeVideoFrame.h"
/**
* video filter to be registered in C++, instantiated and attached in QML
*/
class QrCodeScannerFilter : public QAbstractVideoFilter
{
friend class QrCodeScannerFilterRunnable;
Q_OBJECT
- Q_PROPERTY(QrCodeDecoder* decoder READ decoder)
public:
/**
* Instantiates a QR code scanner filter.
*
* @param parent parent object
*/
explicit QrCodeScannerFilter(QObject *parent = nullptr);
~QrCodeScannerFilter() override;
/**
* @return decoder for decoding a video frame
*/
QrCodeDecoder *decoder();
QVideoFilterRunnable *createFilterRunnable() override;
+ /**
+ * Sets the video frame format of the QML Camera object to our default format
+ *
+ * @param qmlCamera Camera object from QML
+ */
+ Q_INVOKABLE void setCameraDefaultVideoFormat(QObject *qmlCamera);
+
signals:
/**
* Emitted when the scanning of an image did not succeed, i.e. no valid QR code was found.
*/
void scanningFailed();
/**
* Emitted when the scanning of an image succeeded, i.e. a valid QR code was found and decoded.
*
* @param result decoded content of the QR code
*/
void scanningSucceeded(const QString& result);
/**
* Emitted when a video frame with an unsupported format is received.
*
* @param format format of the video frame which is not supported
*/
void unsupportedFormatReceived(const QString& format);
private:
QrCodeDecoder *m_decoder;
/**
* frame of the video which may contain a QR code
*/
QrCodeVideoFrame m_frame;
QFuture m_processThread;
};
/**
* runnable which is created everytime the filter gets a new frame
*/
class QrCodeScannerFilterRunnable : public QObject, public QVideoFilterRunnable
{
Q_OBJECT
public:
explicit QrCodeScannerFilterRunnable(QrCodeScannerFilter *m_filter);
/**
* Runs the decoding in a new thread whenever a new frame is taken by the camera.
*/
QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags) override;
/**
* Converts a given frame, which may contain a QR code, to an image and then tries to decode it.
*
* @param videoFrame frame to be converted and which may contain a QR code to be decoded
* @param filter filter of the current execution
*/
void processVideoFrameProbed(QrCodeVideoFrame videoFrame, QrCodeScannerFilter *filter);
private:
QrCodeScannerFilter *m_filter;
};
#endif // QRCODESCANNERFILTER_H
diff --git a/src/qml/QrCodeScannerPage.qml b/src/qml/QrCodeScannerPage.qml
index 02dd46d..7aa5c03 100644
--- a/src/qml/QrCodeScannerPage.qml
+++ b/src/qml/QrCodeScannerPage.qml
@@ -1,99 +1,103 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
import QtQuick 2.0
import QtQuick.Controls 2.3 as Controls
import QtMultimedia 5.9
import org.kde.kirigami 2.4 as Kirigami
import im.kaidan.kaidan 1.0
// QR code scanner output and decoding for logging in by a decoded XMPP URI
Kirigami.Page {
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
title: qsTr("Scan QR code")
// message to be shown if no camera can be found
Kirigami.InlineMessage {
visible: {
camera.availability === Camera.Unavailable ||
camera.availability === Camera.ResourceMissing
}
anchors.centerIn: parent
width: 300
height: 60
text: qsTr("There is no camera available.")
}
// message to be shown if the found camera is not usable
Kirigami.InlineMessage {
visible: camera.availability === Camera.Busy
anchors.centerIn: parent
width: 300
height: 60
text: qsTr("Your camera is busy.\nTry to close other applications using the camera.")
}
// video output from the camera which is shown on the screen and decoded by a filter
VideoOutput {
id: viewfinder
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectCrop
source: camera
autoOrientation: true
filters: [scannerFilter]
}
// filter which converst the video frames to images and decodes a containing QR code
QrCodeScannerFilter {
id: scannerFilter
onScanningSucceeded: {
pageStack.layers.pop()
// login by the data from the decoded QR code
kaidan.loginByUri(result)
}
onUnsupportedFormatReceived: {
pageStack.layers.pop()
passiveNotification(qsTr("The camera format '%1' is not supported.").arg(format))
}
}
// camera with continuous focus in the center of the video
Camera {
id: camera
focus {
focusMode: Camera.FocusContinuous
focusPointMode: Camera.FocusPointCenter
}
+
+ Component.onCompleted: {
+ scannerFilter.setCameraDefaultVideoFormat(camera);
+ }
}
}