Skip to content Skip to sidebar Skip to footer

Qvideoframe::map() Crashes

I am trying to get images from the camera using QCamera. I thought I should derive from QAbstractVideoSurface and implement present(), where a QVideoFrame, representing the current

Solution 1:

I finally managed to make it work. Apparently QCamera has some problems on Android, so I had to use a QML Camera, and display it with a VideoOutput, and apply a filter through which I can get my images.

This filter is made in two parts : one deriving from QAbstractVideoFilter, the other deriving from QVideoFilterRunnable.

Here is the code :

////////////////////////////////////////////////////////////////////
// main.cpp
////////////////////////////////////////////////////////////////////#include <QGuiApplication>#include <QQmlApplicationEngine>#include "myfilter.hpp"int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    qmlRegisterType<MyFilter>("example.myfilter", 1, 0, "MyFilter");

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}


////////////////////////////////////////////////////////////////////
// myfilter.hpp
////////////////////////////////////////////////////////////////////#ifndef MYFILTER_H#define MYFILTER_H#include <QAbstractVideoFilter>

class MyFilterRunnable : public QVideoFilterRunnable {
public:
    QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags);
};


class MyFilter : public QAbstractVideoFilter
{
public:
    QVideoFilterRunnable* createFilterRunnable();
};

#endif // MYFILTER_H

////////////////////////////////////////////////////////////////////
// myfilter.cpp
////////////////////////////////////////////////////////////////////#include "myfilter.hpp"#include <QOpenGLContext>#include <QOpenGLFunctions>

QVideoFrame MyFilterRunnable::run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, QVideoFilterRunnable::RunFlags flags)
{
    QImage img(input->width(), input->height(), QImage::Format_RGBA8888);
    bool success = false;
    if (input->handleType() == QAbstractVideoBuffer::GLTextureHandle) {
        GLuint textureId = input->handle().toUInt();
        QOpenGLContext *ctx = QOpenGLContext::currentContext();
        QOpenGLFunctions *f = ctx->functions();
        GLuint fbo;
        f->glGenFramebuffers(1, &fbo);
        GLuint prevFbo;
        f->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
        f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
        f->glReadPixels(0, 0, input->width(), input->height(), GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
        f->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
        success = true;
    } // else handle other types

    if( success ) {
        // Process image
        return QVideoFrame(img);
    } else {
        return *input; //Could not apply filter, return unmodified input
    }
}

QVideoFilterRunnable *MyFilter::createFilterRunnable()
{
    return new MyFilterRunnable;
}


////////////////////////////////////////////////////////////////////
// main.qml
////////////////////////////////////////////////////////////////////

import QtQuick 2.11
import QtQuick.Controls 2.2
import QtMultimedia 5.9
import example.myfilter 1.0

ApplicationWindow {
    id: window

    visible: true
    width: 640
    height: 480

    Camera {
        id: camera
    }
    MyFilter {
        id: filter
    }
    VideoOutput {
        source: camera
        autoOrientation: true
        filters: [ filter ]
        anchors.fill: parent
    }
}

(My run implementation is adapted from here : http://code.qt.io/cgit/qt/qtmultimedia.git/tree/examples/multimedia/video/qmlvideofilter_opencl/rgbframehelper.h)

Solution 2:

...unless someone have a better suggestion...

My simple way to capture images from Camera object in QML is calling grabToImage and pass the image to C++ to process it!

This method does not oblige you to process all the frames of the active camera, you can grab only when you are ready or desire it!

Keep in mind, especially in android, that the VideoOutput object needs to be visible in order to grab anything, you can't, for example, grab images with screen turned off on Android. However you not need to fill the screen, you only have to gave some area of the screen to the VideoOutput and keep it visible and on top.

Also, note that reduce the size of VideoOutput does not reduce the maximum size you can capture, for example you can capture a 1280x720 image with a VideoOutput with smaller size.

Also the aspect ratio must be preserved when resizing VideoOutput to keep the aspect ratio of the image.

Code:

You can see full source here!

main.cpp:

#include<QGuiApplication>#include<QQmlApplicationEngine>#include"camerahelper.h"intmain(int argc, char *argv[]){
#if defined(Q_OS_WIN)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endifQGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return-1;

    CameraHelper camera_helper;
    camera_helper.start(engine.rootObjects().first());

    return app.exec();
}

camerahelper.h:

#ifndef CAMERAHELPER_H#define CAMERAHELPER_H#include<QtCore>#include<QtGui>#include<QtQuick>classCameraHelper : public QObject
{
    Q_OBJECT
public:
    explicitCameraHelper(QObject *parent = nullptr);

signals:

public slots:
    voidstart(QObject *qml_obj);
    voidgrab();

private slots:
    voidframeReady(const QVariant &frame_variant);

private:
    QObject *m_qml_obj;
};

#endif// CAMERAHELPER_H

camerahelper.cpp:

#include "camerahelper.h"

CameraHelper::CameraHelper(QObject *parent) : QObject(parent)
{

}

void CameraHelper::start(QObject *qml_obj)
{
    m_qml_obj = qml_obj;

    //Connect the QML frameReady SIGNAL to our frameReady SLOT
    connect(m_qml_obj, SIGNAL(frameReady(QVariant)), this, SLOT(frameReady(QVariant)));

    //Do the first grab
    grab();
}

void CameraHelper::grab()
{
    //Size of the captured image
    QSize size = QSize(320, 240);

    //Pass grab size to QML and wait for captured image on the frameReady SIGNAL
    QMetaObject::invokeMethod(m_qml_obj, "grab", Q_ARG(QVariant, size.width()), Q_ARG(QVariant, size.height()));
}

void CameraHelper::frameReady(const QVariant &frame_variant)
{
    QQuickItemGrabResult *grab_result = qvariant_cast<QQuickItemGrabResult*>(frame_variant); //Cast from QVariant
    QImage frame = grab_result->image(); //Get the QImage
    grab_result->deleteLater(); //Release QQuickItemGrabResult//Depending on OS the image can have different formats,//use convertToFormat to unify all possibles formats to one
    frame = frame.convertToFormat(QImage::Format_RGB32);

    //Frame is ready to use

    grab(); //Do the next frame grab
}

main.qml:

importQtQuick2.10importQtQuick.Window2.10importQtMultimedia5.8Window
{
    visible: truecolor: "black"width: 640height: 480title: qsTr("Hello World")

    signal frameReady(var frame)

    Camera
    {
        id: camera
        viewfinder.resolution: "320x240"
    }

    VideoOutput
    {
        id: videoOutput
        source: camera
        autoOrientation: truevisible: true
        anchors.fill: parent
    }

    functiongrab(grab_width, grab_height)
    {
        if (!visible)
            return

        videoOutput.grabToImage(function(frame)
        {
            frameReady(frame) //Emit frameReady SIGNAL
        }, Qt.size(grab_width, grab_height))
    }
}

Post a Comment for "Qvideoframe::map() Crashes"