我找到了问题的答案(目标是从相机获取帧并在 Android 和 Windows 上的 QWidget 上显示)
首先在 QML 中:获取相机和滤镜:
Item {
signal submitFrame(var img)//used to send QImage to C++ after filter process
Camera {
id: camera
}
MyFilter {
id: filter
onFinished: submitFrame(result)
}
VideoOutput {
source: camera
filters: [ filter ]
}
}
QAbstractVideoFilter 子类:
class MyFilter : public QAbstractVideoFilter
{
Q_OBJECT
public:
MyFilter();
QVideoFilterRunnable *createFilterRunnable();
signals:
void finished(const QVariant& result); //send QImage got to QML
public slots:
};
QVideoFilterRunnable 子类:
class MyFilterRunnable : public QVideoFilterRunnable
{
public:
MyFilterRunnable(MyFilter *filter);
~MyFilterRunnable();
QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags) Q_DECL_OVERRIDE;
private:
MyFilter *m_filter;
};
处理帧(将QVideoframe转换为QImage)
QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags) Q_DECL_OVERRIDE;
//On Android
if(frame->handleType() == QAbstractVideoBuffer::GLTextureHandle){
/* ...
Need treatment with OpenGL or other to "convert" frame to QImage.
see here : http://code.qt.io/cgit/qt/qtmultimedia.git/tree/examples/multimedia/video/qmlvideofilter_opencl in file rgbframehelper.h
... */
emit m_filter->finished(QVariant (imgToDisplay));// send QImage got to QML
}
//On Windows
else{
//easy convert VideoFrame to QImage
QImage::Format format = VideoFrame::imageFormatFromPixelFormat(frame->pixelFormat());
if (fmt != QImage::Format_Invalid){
QImage img=QImage(frame->bits(), frame->width(), frame->height(), format);
QImage imgToDisplay = img.convertToFormat(QImage::Format_RGB888);
emit m_filter->finished(QVariant(imgToDisplay));// send QImage got to QML
}
}
现在在 C++,主窗口:
qmlRegisterType<MyFilter>("my.uri", 1, 0, "MyFilter");
//load QML
QQuickView *view=new QQuickView();
view->setSource(QUrl("qrc:///main.qml"));
view->show();
//connect to get QImage as QVariant from QML to this C++ class
QObject *topLevel =view->rootObject();
QObject::connect(topLevel, SIGNAL(submitFrame(const QVariant &)),this, SLOT(handleFrame(const QVariant &)));
和显示槽:
void MainWindow::handleFrame(const QVariant &in)
{
//cast Qvariant =>QImage
QImage image = in.value<QImage>();
// QImage to QPixmap for display
ui->label->setPixmap(QPixmap::fromImage(image));
ui->label->resize(image.size());
ui->label->update();
}
总结:QML 相机“发送”帧到 c++ 过滤器。过滤器将帧转换为 QImage 并将这些 QImage 作为 QVariant 发送到 QML。从 C++ 显示类中,通过 connect 获取 QVariant 并转换为 QImage 以在 QWidget 上显示。
这当然不是最好的方法(Android 将帧转换为 QImage 太慢了,并且可能与 QML 和 C++ 有很大的关系)但它对我的项目来说已经足够了(我得到 ~15 fps)。
如果有人有更好的想法,请分享。