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); + } } }