0

I have a simple Qt/C++ program which gathers a webcam image out of one of my LAN devices using a cv::VideoCapture object. The application is being built using Qt Quick and has an Image QML Item which is being served with a picture every 500 milliseconds via a custom QQuickImageProvider implementation:

WebcamImageProvider::WebcamImageProvider()
    : QQuickImageProvider(ImageType::Image)
{
    connect(&_timer, &QTimer::timeout, this, &WebcamImageProvider::updateImage);

    // refresh our picture every .5 seconds
    _timer.start(500);
}

// overridden function from base class
QImage WebcamImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    Q_UNUSED(id)
    Q_UNUSED(size)
    Q_UNUSED(requestedSize)

    return _img;
}

My main function looks like this:

// Qt Quick
#include <QQuickItem>
#include <QQuickWindow>

// QML + GUI
#include <QQmlContext>

#include <QGuiApplication>
#include <QQmlApplicationEngine>

// webcam stuff
#include "webcamimageprovider.h"

/*
 * Here we go
 */
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);

    // we're adding our self-made image provider instance here
    WebcamImageProvider wip;
    engine.addImageProvider("webcam", &wip);
    engine.rootContext()->setContextProperty("webcamImageProvider", &wip);

    engine.load(url);

    return app.exec();

The code of my main.qml file should not be important here.

Inside my image provider class, I have provided the updateImage slot as follows:

void WebcamImageProvider::updateImage()
{
    /*
     * Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
     */

    if (!_cap.open(_videoUrl)) {
        qDebug() << "Error opening stream.";
    }

    if (!_cap.read(_mat)) {
        qDebug() << "No video capture or webcam error.";
    }

    // via https://stackoverflow.com/a/12312326/4217759
    _img = QImage(
        static_cast<uchar*>(_mat.data),
        _mat.cols,
        _mat.rows,
        static_cast<int>(_mat.step),
        QImage::Format_BGR888
    );

    emit imageChanged(_img);
}

My problem here is that when my device is not reachable via network, the application will freeze completely as it gets stuck in the _cap.open() function. Therefore, I am trying to outsource this function to a future so the image will be loaded asynchronously. Since I have literally zero idea of threading and futures, I randomly gave it a shot with using QFutures:

void WebcamImageProvider::updateImage()
{
    /*
     * Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
     */

    QFuture<void> future = QtConcurrent::run([&](){ _cap.open(_videoUrl); });

    if (!future.isRunning()) {
        qDebug() << "Error opening stream.";
    }

    if (future.isFinished()) {
        if (!_cap.read(_mat)) {
            qDebug() << "No video capture or webcam error.";
        }

        // via https://stackoverflow.com/a/12312326/4217759
        _img = QImage(
            static_cast<uchar*>(_mat.data),
            _mat.cols,
            _mat.rows,
            static_cast<int>(_mat.step),
            QImage::Format_BGR888
        );

        emit imageChanged(_img);
    }
}

However, I won't get any image output from that.

Can someone help me on how to structure the code correctly so I can load my image asynchronously here?

Thanks a lot in advance.

tai
  • 477
  • 1
  • 5
  • 16

1 Answers1

-1

1- you should know something , create setter for _img and emit changed in that ! because it is possible one day you need set Image in another functions and if you don't do this , you should duplicate emit fooChanged().

2- when you check these and is's not responsible for working in your method , you should throw an exception then handle them and if you wanted use qDebug().

3- my suggest is that (if I understood completely your job) create an thread that working in loop and always getting new images then create a queue worker and create another thread (worker) for process your scenario(here updateImage method).

H.M
  • 425
  • 2
  • 16
  • excuse me, but I wanted help for multithreading not for how to set signals correctly or exception handling, which is both completely subjective. for the third point, I'm sorry but that answer was not quite helpful. – tai Apr 25 '21 at 17:15