parent
eef6bfaf9e
commit
702e0498ae
@ -0,0 +1,2 @@
|
|||||||
|
# qt-gstreamer
|
||||||
|
Video rendering using gstreamer over Qt5 surface (QML) using qtquick2videosink
|
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef VIDEOITEM_H
|
||||||
|
#define VIDEOITEM_H
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
|
#include <QtCore/QPointer>
|
||||||
|
#include <QtQuick/QSGNode>
|
||||||
|
#include <QtQuick/QSGFlatColorMaterial>
|
||||||
|
#include "VideoItem.h"
|
||||||
|
#include "videosurface.h"
|
||||||
|
|
||||||
|
class VideoSurface;
|
||||||
|
class VideoItem: public QQuickItem{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(VideoSurface* surface READ surface WRITE setSurface)
|
||||||
|
public:
|
||||||
|
explicit VideoItem(QQuickItem *parent = 0);
|
||||||
|
virtual ~VideoItem();
|
||||||
|
|
||||||
|
VideoSurface *surface();
|
||||||
|
void setSurface(VideoSurface *surface);
|
||||||
|
virtual QSGNode* updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *UpdatePaintNodeData);
|
||||||
|
struct Private;
|
||||||
|
Private* const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VIDEOITEM_H
|
@ -0,0 +1,58 @@
|
|||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QtQml/QQmlContext>
|
||||||
|
#include <QtQml/QQmlEngine>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QSet>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
|
#include <QtCore/QPointer>
|
||||||
|
#include <QtQuick/QSGNode>
|
||||||
|
#include <QtQuick/QSGFlatColorMaterial>
|
||||||
|
#include <VideoItem.h>
|
||||||
|
#include <videosurface.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
gst_init(NULL, NULL);
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
|
||||||
|
QGuiApplication app(argc, argv);
|
||||||
|
qmlRegisterType<VideoItem>("VideoItem", 1, 0,"VideoItem");
|
||||||
|
|
||||||
|
GstElement *pipeline = gst_pipeline_new(NULL);
|
||||||
|
GstElement *testsrc = gst_element_factory_make("videotestsrc", NULL);
|
||||||
|
GstElement *videoconvert = gst_element_factory_make("videoconvert", NULL);
|
||||||
|
GstElement *videosink = gst_element_factory_make("qt5videosink", NULL);
|
||||||
|
GstElement *testsrc1 = gst_element_factory_make("videotestsrc", NULL);
|
||||||
|
GstElement *videoconvert1 = gst_element_factory_make("videoconvert", NULL);
|
||||||
|
GstElement *videosink1 = gst_element_factory_make("qt5videosink", NULL);
|
||||||
|
QQmlApplicationEngine engine;
|
||||||
|
|
||||||
|
// QGLContext *gl;
|
||||||
|
// QGLContext::create(gl);
|
||||||
|
// gl->currentContext();
|
||||||
|
//engine.rootContext()->setContextProperty((QString)QGLContext::currentContext(),NULL);
|
||||||
|
|
||||||
|
// g_object_set(G_OBJECT(videosink),"glcontext",(GValue *)QGLContext::currentContext(), NULL);
|
||||||
|
// gl->doneCurrent();
|
||||||
|
VideoSurface *surface = new VideoSurface;
|
||||||
|
videosink = surface->videoSink();
|
||||||
|
engine.rootContext()->setContextProperty(QLatin1String("videoSurface1"), surface);
|
||||||
|
VideoSurface *surface1 = new VideoSurface;
|
||||||
|
videosink1 = surface1->videoSink();
|
||||||
|
engine.rootContext()->setContextProperty(QLatin1String("videoSurface2"), surface1);
|
||||||
|
|
||||||
|
gst_bin_add_many(GST_BIN(pipeline), testsrc, videoconvert, videosink, testsrc1, videoconvert1, videosink1, NULL);
|
||||||
|
gst_element_link_many(testsrc, videoconvert, videosink, NULL);
|
||||||
|
gst_element_link_many(testsrc1, videoconvert1, videosink1, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||||
|
if (engine.rootObjects().isEmpty())
|
||||||
|
return -1;
|
||||||
|
gst_element_set_state(pipeline, GST_STATE_PLAYING);
|
||||||
|
return app.exec();
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import VideoItem 1.0
|
||||||
|
import QtMultimedia 5.12
|
||||||
|
|
||||||
|
Window {
|
||||||
|
visible: true
|
||||||
|
width: 640
|
||||||
|
height: 480
|
||||||
|
title: qsTr("Hello World")
|
||||||
|
|
||||||
|
VideoItem{
|
||||||
|
id: video
|
||||||
|
|
||||||
|
width: 300
|
||||||
|
height: 300
|
||||||
|
surface: videoSurface1
|
||||||
|
z: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoItem{
|
||||||
|
id: video1
|
||||||
|
x: 300
|
||||||
|
y: 300
|
||||||
|
width: 300
|
||||||
|
height: 300
|
||||||
|
surface: videoSurface2
|
||||||
|
z: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// MediaPlayer{
|
||||||
|
// id: mediaplayer
|
||||||
|
// source: "gst-pipeline: videotestsrc ! xvimagesink name=\"qtvideosink\""
|
||||||
|
// autoPlay: true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// VideoOutput{
|
||||||
|
// source: mediaplayer
|
||||||
|
// width: 800
|
||||||
|
// height: 800
|
||||||
|
|
||||||
|
// Label{
|
||||||
|
// text: "2323232"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>main.qml</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
@ -0,0 +1,42 @@
|
|||||||
|
QT += quick
|
||||||
|
CONFIG += c++11
|
||||||
|
QT += opengl
|
||||||
|
QT += core
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which as been marked deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if you use deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += main.cpp \
|
||||||
|
videoitem.cpp \
|
||||||
|
videosurface.cpp \
|
||||||
|
videosurfaceprivate.cpp
|
||||||
|
|
||||||
|
RESOURCES += qml.qrc
|
||||||
|
|
||||||
|
CONFIG += link_pkgconfig
|
||||||
|
|
||||||
|
|
||||||
|
PKGCONFIG += gstreamer-1.0
|
||||||
|
|
||||||
|
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||||
|
QML_IMPORT_PATH =
|
||||||
|
|
||||||
|
# Additional import path used to resolve QML modules just for Qt Quick Designer
|
||||||
|
QML_DESIGNER_IMPORT_PATH =
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
videosurface.h \
|
||||||
|
videosurfaceprivate.h \
|
||||||
|
VideoItem.h
|
@ -0,0 +1,78 @@
|
|||||||
|
#include "VideoItem.h"
|
||||||
|
|
||||||
|
struct VideoItem::Private{
|
||||||
|
QPointer<VideoSurface> surface;
|
||||||
|
bool surfaceDirty;
|
||||||
|
QRectF targetArea;
|
||||||
|
};
|
||||||
|
|
||||||
|
VideoItem::VideoItem(QQuickItem *parent): QQuickItem(parent), d(new Private){
|
||||||
|
d->surfaceDirty = true;
|
||||||
|
setFlag(QQuickItem::ItemHasContents, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoItem::~VideoItem(){
|
||||||
|
setSurface(0);
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSurface *VideoItem::surface(){
|
||||||
|
return d->surface.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoItem::setSurface(VideoSurface *surface)
|
||||||
|
{
|
||||||
|
if(d->surface){
|
||||||
|
d->surface.data()->d->items.remove(this);
|
||||||
|
}
|
||||||
|
d->surface = surface;
|
||||||
|
d->surfaceDirty = true;
|
||||||
|
|
||||||
|
if(d->surface){
|
||||||
|
d->surface.data()->d->items.insert(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSGNode* VideoItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *UpdatePaintNodeData){
|
||||||
|
|
||||||
|
Q_UNUSED(UpdatePaintNodeData);
|
||||||
|
QRectF r = boundingRect();
|
||||||
|
QSGNode* newNode = 0;
|
||||||
|
|
||||||
|
if(d->surfaceDirty){
|
||||||
|
delete oldNode;
|
||||||
|
oldNode = 0;
|
||||||
|
d->surfaceDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!d->surface || d->surface.data()->d->videosink == NULL ){
|
||||||
|
if(!oldNode){
|
||||||
|
QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
|
||||||
|
material->setColor(Qt::black);
|
||||||
|
QSGGeometryNode *node = new QSGGeometryNode;
|
||||||
|
node->setMaterial(material);
|
||||||
|
node->setFlag(QSGNode::OwnsMaterial);
|
||||||
|
node->setFlag(QSGNode::OwnsGeometry);
|
||||||
|
newNode = node;
|
||||||
|
d->targetArea = QRectF();
|
||||||
|
}else{
|
||||||
|
newNode = oldNode;
|
||||||
|
}
|
||||||
|
if( r != d->targetArea ){
|
||||||
|
QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
|
||||||
|
geometry->vertexDataAsPoint2D()[0].set(r.x(),r.y());
|
||||||
|
geometry->vertexDataAsPoint2D()[1].set(r.x(), r.height());
|
||||||
|
geometry->vertexDataAsPoint2D()[2].set(r.width(), r.y());
|
||||||
|
geometry->vertexDataAsPoint2D()[3].set(r.width(), r.height());
|
||||||
|
QSGGeometryNode *node = static_cast<QSGGeometryNode*>(newNode);
|
||||||
|
node->setGeometry(geometry);
|
||||||
|
d->targetArea = r;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_signal_emit_by_name(d->surface.data()->d->videosink,"update-node", (void *)oldNode, r.x(), r.y(), r.width(), r.height(), (void**)&newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
#include "videosurface.h"
|
||||||
|
|
||||||
|
VideoSurface::VideoSurface(QObject *parent): QObject(parent), d(new VideoSurfacePrivate){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSurface:: ~VideoSurface(){
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
void VideoSurface::onUpdate(){
|
||||||
|
Q_FOREACH(QQuickItem *item, d->items){
|
||||||
|
item->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void VideoSurface::onUpdateThunk(GstElement *sink, gpointer data){
|
||||||
|
Q_UNUSED(sink);
|
||||||
|
VideoSurface *pThis = (VideoSurface *)data;
|
||||||
|
pThis->onUpdate();
|
||||||
|
}
|
||||||
|
GstElement *VideoSurface::videoSink(){
|
||||||
|
d->videosink = gst_element_factory_make("qtquick2videosink", NULL);
|
||||||
|
g_signal_connect(d->videosink,"update",G_CALLBACK(onUpdateThunk),(void *)this);
|
||||||
|
return d->videosink;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef VIDEOSURFACE_H
|
||||||
|
#define VIDEOSURFACE_H
|
||||||
|
#include <QSet>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
|
#include <QtCore/QPointer>
|
||||||
|
#include <QtQuick/QSGNode>
|
||||||
|
#include <QtQuick/QSGFlatColorMaterial>
|
||||||
|
#include "videosurfaceprivate.h"
|
||||||
|
|
||||||
|
|
||||||
|
class VideoSurface: public QObject{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(VideoSurface)
|
||||||
|
public:
|
||||||
|
explicit VideoSurface(QObject *parent = 0);
|
||||||
|
virtual ~VideoSurface();
|
||||||
|
GstElement *videoSink();
|
||||||
|
void onUpdate();
|
||||||
|
static void onUpdateThunk(GstElement* sink, gpointer data);
|
||||||
|
VideoSurfacePrivate *const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VIDEOSURFACE_H
|
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef VIDEOSURFACEPRIVATE_H
|
||||||
|
#define VIDEOSURFACEPRIVATE_H
|
||||||
|
#include <QSet>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include "videosurface.h"
|
||||||
|
#include "VideoItem.h"
|
||||||
|
class VideoItem;
|
||||||
|
class VideoSurfacePrivate {
|
||||||
|
public:
|
||||||
|
VideoSurfacePrivate(): videosink(NULL){}
|
||||||
|
QSet<VideoItem *> items;
|
||||||
|
GstElement* videosink;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // VIDEOSURFACEPRIVATE_H
|
Loading…
Reference in new issue