Qvideoframe::map() Crashes
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"