qtqmlqcamera

Operate QCamera with QML


I am trying to get QVideoFrame s from QCamera using probe to process it while at the same time I need to show the content to QML VideoOutput. This is what I have done:

#include <QObject>
#include <QAbstractVideoSurface>
#include <QVideoSurfaceFormat>

class FrameProvider: public QObject {
    Q_OBJECT
    Q_PROPERTY(QAbstractVideoSurface *videoSurface READ getVideoSurface WRITE setVideoSurface NOTIFY videoSurfaceChanged)

public:
    FrameProvider(QObject *parent = nullptr)
        : QObject(parent) {}

    QAbstractVideoSurface* getVideoSurface() const { return m_surface; }

    void setVideoSurface(QAbstractVideoSurface *surface) {
        if (m_surface != surface && m_surface && m_surface->isActive()) {
            m_surface->stop();
        }

        m_surface = surface;

        if (m_surface && m_format.isValid()) {
            m_format = m_surface->nearestFormat(m_format);
            m_surface->start(m_format);
        }
        emit videoSurfaceChanged();
    }


public slots:
    void setFrame(const QVideoFrame &frame) {
        if (m_surface) {
            //do processing
            m_surface->present(frame);
        }
    }

signals:
    void videoSurfaceChanged();

private:
    QAbstractVideoSurface *m_surface = NULL;
    QVideoSurfaceFormat m_format;
};

The FrameProvider receives the frames from QVideoProbe in its getFrames method and processes it and then sends it to its present() method for displaying in QML.

main(...) {
//some code
    auto ctxt = engine.rootContext();
    auto camera = new QCamera();
    camera->setCaptureMode(QCamera::CaptureVideo);

    FrameProvider fp;

    auto probe = new QVideoProbe();
    if(probe->setSource(camera)) {
        QObject::connect(probe, SIGNAL(videoFrameProbed(QVideoFrame)), &fp, SLOT(setFrame(QVideoFrame)));
    }

    ctxt->setContextProperty("frameProvider", &fp);

    camera->start();

    engine.load(url);
}

In QML, I simply display it as,

VideoOutput {
        source: frameProvider
        width: 320
        height: 480
    }

I followed the QT Documentation, this example and that example. I get signals that getFrame is receiving probed frames but QML window is completely blank. I am running KDE Plasma on top of Arch Linux, if the platform matters. What am I missing?


Solution

  • Ok so I found an answer for this problem. Actually the problem is with the format conversion between QCamera and VideoOutput. I just added this code in FrameProvider,

    void setFormat(int width, int height, QVideoFrame::PixelFormat frameFormat) {
            QSize size(width, height);
            QVideoSurfaceFormat format(size, frameFormat);
            m_format = format;
            if (m_surface) {
                if (m_surface->isActive())
                    m_surface->stop();
                m_format = m_surface->nearestFormat(m_format);
                m_surface->start(m_format);
            }
        }
    

    and call it for conversion whenever a new frame is made available like,

    void setFrame(const QVideoFrame &frame) {
            if (m_surface) {
                //do other processing with the received frame
                ...
                //after you are done with processing
                setFormat(frame.width(), frame.height(), frame.pixelFormat());
                m_surface->present(frame);
            }
        }
    

    And that solved my problem.