Unverified Commit 1d5c87d7 authored by Gus Grubba's avatar Gus Grubba Committed by GitHub

Merge pull request #8266 from andrewvoznytsa/PR-qmlglsink-v2

Switch from builtin video sink to qmlglsink (v2)
parents e36a8fbc 0f5be407
......@@ -7,3 +7,6 @@
[submodule "libs/OpenSSL/android_openssl"]
path = libs/OpenSSL/android_openssl
url = https://github.com/Auterion/android_openssl
[submodule "libs/gst-plugins-good"]
path = libs/gst-plugins-good
url = https://gitlab.freedesktop.org/andrew.voznytsa/gst-plugins-good.git
......@@ -16,12 +16,12 @@ matrix:
include:
- name: "Linux Installer"
os: linux
dist: xenial
dist: bionic
env: SPEC=linux-g++-64 CONFIG=installer
sudo: required
- name: "Linux Debug"
os: linux
dist: xenial
dist: bionic
env: SPEC=linux-g++-64 CONFIG=debug
services: xvfb
sudo: required
......
......@@ -45,7 +45,7 @@ installer {
LinuxBuild {
#-- TODO: This uses hardcoded paths. It should use $${DESTDIR}
QMAKE_POST_LINK += && mkdir -p release/package
QMAKE_POST_LINK += && tar --warning=no-file-changed -cjf release/package/QGroundControl.tar.bz2 release --exclude='package' --transform 's/release/qgroundcontrol/'
QMAKE_POST_LINK += && tar -cj --exclude='package' -f release/package/QGroundControl.tar.bz2 release --transform 's/release/qgroundcontrol/'
}
AndroidBuild {
QMAKE_POST_LINK += && mkdir -p $${DESTDIR}/package
......
gst-plugins-good @ 9d782fad
Subproject commit 9d782fad9dc0384ba86ecae64511c193f6149f93
......@@ -1374,18 +1374,13 @@ INCLUDEPATH += \
src/VideoStreaming
HEADERS += \
src/VideoStreaming/VideoItem.h \
src/VideoStreaming/VideoReceiver.h \
src/VideoStreaming/VideoStreaming.h \
src/VideoStreaming/VideoSurface.h \
src/VideoStreaming/VideoSurface_p.h \
src/VideoStreaming/SubtitleWriter.h \
SOURCES += \
src/VideoStreaming/VideoItem.cc \
src/VideoStreaming/VideoReceiver.cc \
src/VideoStreaming/VideoStreaming.cc \
src/VideoStreaming/VideoSurface.cc \
src/VideoStreaming/SubtitleWriter.cc \
contains (CONFIG, DISABLE_VIDEOSTREAMING) {
......
# FIXME: will be fixed after converting qmlglsink into .pro#static lib
QMAKE_CXXFLAGS_WARN_ON =
LinuxBuild {
DEFINES += HAVE_QT_X11 HAVE_QT_EGLFS HAVE_QT_WAYLAND
} else:MacBuild {
DEFINES += HAVE_QT_MAC
} else:iOSBuild {
DEFINES += HAVE_QT_IOS
} else:WindowsBuild {
DEFINES += HAVE_QT_WIN32
LIBS += opengl32.lib user32.lib
} else:AndroidBuild {
DEFINES += HAVE_QT_ANDROID
}
SOURCES += \
libs/gst-plugins-good/ext/qt/gstplugin.cc \
libs/gst-plugins-good/ext/qt/gstqtglutility.cc \
libs/gst-plugins-good/ext/qt/gstqsgtexture.cc \
libs/gst-plugins-good/ext/qt/gstqtsink.cc \
libs/gst-plugins-good/ext/qt/gstqtsrc.cc \
libs/gst-plugins-good/ext/qt/qtwindow.cc \
libs/gst-plugins-good/ext/qt/qtitem.cc
HEADERS += \
libs/gst-plugins-good/ext/qt/gstqsgtexture.h \
libs/gst-plugins-good/ext/qt/gstqtgl.h \
libs/gst-plugins-good/ext/qt/gstqtglutility.h \
libs/gst-plugins-good/ext/qt/gstqtsink.h \
libs/gst-plugins-good/ext/qt/gstqtsrc.h \
libs/gst-plugins-good/ext/qt/qtwindow.h \
libs/gst-plugins-good/ext/qt/qtitem.h
......@@ -79,8 +79,8 @@ Item {
id: videoBackgroundComponent
QGCVideoBackground {
id: videoContent
objectName: "videoContent"
receiver: _videoReceiver
display: _videoReceiver && _videoReceiver.videoSurface
Connections {
target: _videoReceiver
......@@ -125,13 +125,13 @@ Item {
Loader {
// GStreamer is causing crashes on Lenovo laptop OpenGL Intel drivers. In order to workaround this
// we don't load a QGCVideoBackground object when video is disabled. This prevents any video rendering
// code from running. Setting QGCVideoBackground.receiver/display = null does not work to prevent any
// code from running. Setting QGCVideoBackground.receiver = null does not work to prevent any
// video OpenGL from being generated. Hence the Loader to completely remove it.
height: parent.getHeight()
width: parent.getWidth()
anchors.centerIn: parent
visible: _videoReceiver && _videoReceiver.videoRunning
sourceComponent: videoDisabled ? null : videoBackgroundComponent
sourceComponent: videoBackgroundComponent
property bool videoDisabled: QGroundControl.settingsManager.videoSettings.videoSource.rawValue === QGroundControl.settingsManager.videoSettings.disabledVideoSource
}
......@@ -169,9 +169,9 @@ Item {
}
QGCVideoBackground {
id: thermalVideo
objectName: "thermalVideo"
anchors.fill: parent
receiver: QGroundControl.videoManager.thermalVideoReceiver
display: QGroundControl.videoManager.thermalVideoReceiver ? QGroundControl.videoManager.thermalVideoReceiver.videoSurface : null
opacity: _camera ? (_camera.thermalMode === QGCCameraControl.THERMAL_BLEND ? _camera.thermalOpacity / 100 : 1.0) : 0
}
}
......
......@@ -18,8 +18,6 @@
#include <QCameraInfo>
#endif
#include <VideoItem.h>
#include "ScreenToolsController.h"
#include "VideoManager.h"
#include "QGCToolbox.h"
......@@ -57,7 +55,6 @@ VideoManager::setToolbox(QGCToolbox *toolbox)
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
qmlRegisterUncreatableType<VideoManager> ("QGroundControl.VideoManager", 1, 0, "VideoManager", "Reference only");
qmlRegisterUncreatableType<VideoReceiver>("QGroundControl", 1, 0, "VideoReceiver","Reference only");
qmlRegisterUncreatableType<VideoSurface> ("QGroundControl", 1, 0, "VideoSurface", "Reference only");
_videoSettings = toolbox->settingsManager()->videoSettings();
QString videoSource = _videoSettings->videoSource()->rawValue().toString();
connect(_videoSettings->videoSource(), &Fact::rawValueChanged, this, &VideoManager::_videoSourceChanged);
......@@ -285,6 +282,113 @@ VideoManager::setfullScreen(bool f)
emit fullScreenChanged();
}
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
GstElement*
VideoManager::_makeVideoSink(const QString& widgetName)
{
GstElement* glupload = nullptr;
GstElement* glcolorconvert = nullptr;
GstElement* qmlglsink = nullptr;
GstElement* bin = nullptr;
GstElement* sink = nullptr;
do {
QQuickItem* root = qgcApp()->mainRootWindow();
if (root == nullptr) {
qCDebug(VideoManagerLog) << "VideoManager::_makeVideoSink() failed. No root window";
break;
}
QQuickItem* widget = root->findChild<QQuickItem*>(widgetName);
if (widget == nullptr) {
qCDebug(VideoManagerLog) << "VideoManager::_makeVideoSink() failed. Widget \'" << widgetName << "\' not found";
break;
}
if ((glupload = gst_element_factory_make("glupload", nullptr)) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_factory_make('glupload')";
break;
}
if ((glcolorconvert = gst_element_factory_make("glcolorconvert", nullptr)) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_factory_make('glcolorconvert')";
break;
}
if ((qmlglsink = gst_element_factory_make("qmlglsink", nullptr)) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_factory_make('qmlglsink')";
break;
}
g_object_set(qmlglsink, "widget", widget, NULL);
if ((bin = gst_bin_new("videosink")) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_bin_new('videosink')";
break;
}
GstPad* pad;
if ((pad = gst_element_get_static_pad(glupload, "sink")) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_get_static_pad(glupload, 'sink')";
break;
}
gst_bin_add_many(GST_BIN(bin), glupload, glcolorconvert, qmlglsink, nullptr);
gboolean ret = gst_element_link_many(glupload, glcolorconvert, qmlglsink, nullptr);
qmlglsink = glcolorconvert = glupload = nullptr;
if (!ret) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_link_many()";
break;
}
gst_element_add_pad(bin, gst_ghost_pad_new("sink", pad));
gst_object_unref(pad);
pad = nullptr;
sink = bin;
bin = nullptr;
} while(0);
if (bin != nullptr) {
gst_object_unref(bin);
bin = nullptr;
}
if (qmlglsink != nullptr) {
gst_object_unref(qmlglsink);
qmlglsink = nullptr;
}
if (glcolorconvert != nullptr) {
gst_object_unref(glcolorconvert);
glcolorconvert = nullptr;
}
if (glupload != nullptr) {
gst_object_unref(glupload);
glupload = nullptr;
}
return sink;
}
#endif
//-----------------------------------------------------------------------------
void
VideoManager::_initVideo()
{
#if defined(QGC_GST_STREAMING)
_videoReceiver->setVideoSink(_makeVideoSink("videoContent"));
_thermalVideoReceiver->setVideoSink(_makeVideoSink("thermalVideo"));
#endif
}
//-----------------------------------------------------------------------------
void
VideoManager::_updateSettings()
......
......@@ -103,6 +103,11 @@ protected slots:
void _connectionLostChanged (bool connectionLost);
protected:
friend class FinishVideoInitialization;
#if defined(QGC_GST_STREAMING)
GstElement* _makeVideoSink (const QString& widgetName);
#endif
void _initVideo ();
void _updateSettings ();
protected:
......
......@@ -14,13 +14,11 @@
* @author Gus Grubba <gus@auterion.com>
*/
import QtQuick 2.11
import QtQuick.Controls 2.4
import QGroundControl.QgcQtGStreamer 1.0
import QtQuick 2.11
import QtQuick.Controls 2.4
import org.freedesktop.gstreamer.GLVideoItem 1.0
VideoItem {
GstGLVideoItem {
id: videoBackground
property var display
property var receiver
surface: display
}
......@@ -26,6 +26,7 @@
#include <QStringListModel>
#include <QRegularExpression>
#include <QFontDatabase>
#include <QQuickWindow>
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
......@@ -68,7 +69,6 @@
#include "CoordinateVector.h"
#include "PlanMasterController.h"
#include "VideoManager.h"
#include "VideoSurface.h"
#include "VideoReceiver.h"
#include "LogDownloadController.h"
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR)
......@@ -128,6 +128,22 @@
#include "QGCMapEngine.h"
class FinishVideoInitialization : public QRunnable
{
public:
FinishVideoInitialization(VideoManager* manager)
: _manager(manager)
{}
void run () {
_manager->_initVideo();
}
private:
VideoManager* _manager;
};
QGCApplication* QGCApplication::_app = nullptr;
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
......@@ -560,6 +576,13 @@ bool QGCApplication::_initForNormalAppBoot()
_qmlAppEngine = toolbox()->corePlugin()->createRootWindow(this);
QQuickWindow* rootWindow = (QQuickWindow*)qgcApp()->mainRootWindow();
if (rootWindow) {
rootWindow->scheduleRenderJob (new FinishVideoInitialization (toolbox()->videoManager()),
QQuickWindow::BeforeSynchronizingStage);
}
// Safe to show popup error messages now that main window is created
UASMessageHandler* msgHandler = qgcApp()->toolbox()->uasMessageHandler();
if (msgHandler) {
......
#include "GLVideoItemStub.h"
GLVideoItemStub::GLVideoItemStub()
{
}
GLVideoItemStub::~GLVideoItemStub()
{
}
#pragma once
#include <QtQuick/QQuickItem>
class GLVideoItemStub : public QQuickItem
{
Q_OBJECT
public:
GLVideoItemStub();
~GLVideoItemStub();
protected:
private:
};
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Item
* @author Gus Grubba <gus@auterion.com>
*/
#include <QtCore/QPointer>
#include <QtQuick/QSGNode>
#include <QtQuick/QSGFlatColorMaterial>
#include "VideoItem.h"
#if defined(QGC_GST_STREAMING)
#include "VideoSurface_p.h"
#endif
#if defined(QGC_GST_STREAMING)
struct VideoItem::Private
{
QPointer<VideoSurface> surface;
bool surfaceDirty;
QRectF targetArea;
};
#endif
VideoItem::VideoItem(QQuickItem *parent)
: QQuickItem(parent)
#if defined(QGC_GST_STREAMING)
, _data(new Private)
#endif
{
#if defined(QGC_GST_STREAMING)
_data->surfaceDirty = true;
setFlag(QQuickItem::ItemHasContents, true);
#endif
}
VideoItem::~VideoItem()
{
#if defined(QGC_GST_STREAMING)
setSurface(0);
delete _data;
#endif
}
VideoSurface *VideoItem::surface() const
{
#if defined(QGC_GST_STREAMING)
return _data->surface.data();
#else
return nullptr;
#endif
}
void VideoItem::setSurface(VideoSurface *surface)
{
#if defined(QGC_GST_STREAMING)
if (_data->surface) {
_data->surface.data()->_data->items.remove(this);
}
_data->surface = surface;
_data->surfaceDirty = true;
if (_data->surface) {
_data->surface.data()->_data->items.insert(this);
}
#else
Q_UNUSED(surface)
#endif
}
#if defined(QGC_GST_STREAMING)
QSGGeometry* VideoItem::_createDefaultGeometry(QRectF& rectBound)
{
QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
geometry->vertexDataAsPoint2D()[0].set(rectBound.x(), rectBound.y());
geometry->vertexDataAsPoint2D()[1].set(rectBound.x(), rectBound.height());
geometry->vertexDataAsPoint2D()[2].set(rectBound.width(), rectBound.y());
geometry->vertexDataAsPoint2D()[3].set(rectBound.width(), rectBound.height());
return geometry;
}
QSGNode* VideoItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData*)
{
QRectF r = boundingRect();
QSGNode* newNode = nullptr;
if (_data->surfaceDirty) {
delete oldNode;
oldNode = nullptr;
_data->surfaceDirty = false;
}
if (!_data->surface || _data->surface.data()->_data->videoSink == nullptr) {
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;
_data->targetArea = QRectF(); //force geometry to be set
} else {
newNode = oldNode;
}
if (r != _data->targetArea) {
QSGGeometryNode *node = static_cast<QSGGeometryNode*>(newNode);
node->setGeometry(_createDefaultGeometry(r));
_data->targetArea = r;
}
} else {
g_signal_emit_by_name(_data->surface.data()->_data->videoSink, "update-node", (void*)oldNode, r.x(), r.y(), r.width(), r.height(), (void**)&newNode);
}
// Sometimes we can still end up here with no geometry when gstreamer fails to create it for whatever reason. If that happens it can
// cause crashes.
QSGGeometryNode *node = static_cast<QSGGeometryNode*>(newNode);
if (node->geometry() == nullptr) {
qDebug() << "Creating default geom";
node->setGeometry(_createDefaultGeometry(r));
}
return newNode;
}
#endif
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Item
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include <QtQuick/QQuickItem>
#include "VideoSurface.h"
class QSGGeometry;
class VideoItem : public QQuickItem
{
Q_OBJECT
Q_DISABLE_COPY(VideoItem)
Q_PROPERTY(VideoSurface* surface READ surface WRITE setSurface)
public:
explicit VideoItem(QQuickItem *parent = 0);
virtual ~VideoItem();
VideoSurface *surface() const;
void setSurface(VideoSurface *surface);
protected:
#if defined(QGC_GST_STREAMING)
/*! Reimplemented from QQuickItem. */
virtual QSGNode* updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
#endif
private:
#if defined(QGC_GST_STREAMING)
QSGGeometry* _createDefaultGeometry(QRectF& rectBound);
struct Private;
Private* const _data;
#endif
};
......@@ -64,24 +64,23 @@ VideoReceiver::VideoReceiver(QObject* parent)
, _pipeline(nullptr)
, _pipelineStopRec(nullptr)
, _videoSink(nullptr)
, _lastFrameId(G_MAXUINT64)
, _lastFrameTime(0)
, _restart_time_ms(1389)
, _socket(nullptr)
, _serverPresent(false)
, _tcpTestInterval_ms(5000)
, _udpReconnect_us(5000000)
#endif
, _videoSurface(nullptr)
, _videoRunning(false)
, _showFullScreen(false)
, _videoSettings(nullptr)
, _hwDecoderName(nullptr)
, _swDecoderName("avdec_h264")
{
_videoSurface = new VideoSurface;
_videoSettings = qgcApp()->toolbox()->settingsManager()->videoSettings();
#if defined(QGC_GST_STREAMING)
setVideoDecoder(H264_SW);
_setVideoSink(_videoSurface->videoSink());
_restart_timer.setSingleShot(true);
connect(&_restart_timer, &QTimer::timeout, this, &VideoReceiver::_restart_timeout);
_tcp_timer.setSingleShot(true);
......@@ -98,32 +97,9 @@ VideoReceiver::~VideoReceiver()
{
#if defined(QGC_GST_STREAMING)
stop();
if(_socket) {
delete _socket;
_socket = nullptr;
}
if (_videoSink) {
gst_object_unref(_videoSink);
}
setVideoSink(nullptr);
#endif
if(_videoSurface)
delete _videoSurface;
}
#if defined(QGC_GST_STREAMING)
void
VideoReceiver::_setVideoSink(GstElement* sink)
{
if (_videoSink) {
gst_object_unref(_videoSink);
_videoSink = nullptr;
}
if (sink) {
_videoSink = sink;
gst_object_ref_sink(_videoSink);
}
}
#endif
//-----------------------------------------------------------------------------
void
......@@ -287,6 +263,9 @@ VideoReceiver::start()
return;
}
_lastFrameId = G_MAXUINT64;
_lastFrameTime = 0;
bool running = false;
bool pipelineUp = false;
......@@ -458,11 +437,21 @@ VideoReceiver::start()
// If we failed before adding items to the pipeline, then clean up
if (!pipelineUp) {
if (queue1 != nullptr) {
gst_object_unref(queue1);
queue1 = nullptr;
}
if (decoder != nullptr) {
gst_object_unref(decoder);
decoder = nullptr;
}
if (queue != nullptr) {
gst_object_unref(queue);
queue = nullptr;
}
if (parser != nullptr) {
gst_object_unref(parser);
parser = nullptr;
......@@ -480,13 +469,9 @@ VideoReceiver::start()
if (_tee != nullptr) {
gst_object_unref(_tee);
dataSource = nullptr;
_tee = nullptr;
}
if (queue != nullptr) {
gst_object_unref(queue);
dataSource = nullptr;
}
}
_running = false;
......@@ -552,7 +537,6 @@ VideoReceiver::_shutdownPipeline() {
bus = nullptr;
}
gst_element_set_state(_pipeline, GST_STATE_NULL);
gst_bin_remove(GST_BIN(_pipeline), _videoSink);
gst_object_unref(_pipeline);
_pipeline = nullptr;
delete _sink;
......@@ -712,6 +696,38 @@ VideoReceiver::setVideoDecoder(VideoEncoding encoding)
}
}
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
void
VideoReceiver::setVideoSink(GstElement* videoSink)
{
if(_pipeline != nullptr) {
qCDebug(VideoReceiverLog) << "Video receiver pipeline is active, video sink change is not possible";
return;
}
if (_videoSink != nullptr) {
gst_object_unref(_videoSink);
_videoSink = nullptr;
}
if (videoSink != nullptr) {
_videoSink = videoSink;
gst_object_ref(_videoSink);
GstPad* pad = gst_element_get_static_pad(_videoSink, "sink");
if (pad != nullptr) {
gst_pad_add_probe(pad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BUFFER), _videoSinkProbe, this, nullptr);
gst_object_unref(pad);
pad = nullptr;
} else {
qCDebug(VideoReceiverLog) << "Unable to find sink pad of video sink";
}
}
}
#endif
//-----------------------------------------------------------------------------
// When we finish our pipeline will look like this:
//
......@@ -923,6 +939,30 @@ VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user
}
#endif
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
GstPadProbeReturn
VideoReceiver::_videoSinkProbe(GstPad* pad, GstPadProbeInfo* info, gpointer user_data)
{
Q_UNUSED(pad);
if(info != nullptr && user_data != nullptr) {
VideoReceiver* pThis = static_cast<VideoReceiver*>(user_data);
pThis->_noteVideoSinkFrame();
}
return GST_PAD_PROBE_OK;
}
#endif
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
void
VideoReceiver::_noteVideoSinkFrame()
{
_lastFrameTime = QDateTime::currentSecsSinceEpoch();
}
#endif
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
GstPadProbeReturn
......@@ -971,41 +1011,39 @@ void
VideoReceiver::_updateTimer()
{
#if defined(QGC_GST_STREAMING)
if(_videoSurface) {
if(_stopping || _starting) {
return;
}
if(_streaming) {
if(!_videoRunning) {
_videoSurface->setLastFrame(0);
_videoRunning = true;
emit videoRunningChanged();
}
} else {
if(_videoRunning) {
_videoRunning = false;
emit videoRunningChanged();
}
if(_stopping || _starting) {
return;
}
if(_streaming) {
if(!_videoRunning) {
_videoRunning = true;
emit videoRunningChanged();
}
} else {
if(_videoRunning) {
uint32_t timeout = 1;
if(qgcApp()->toolbox() && qgcApp()->toolbox()->settingsManager()) {
timeout = _videoSettings->rtspTimeout()->rawValue().toUInt();
}
time_t elapsed = 0;
time_t lastFrame = _videoSurface->lastFrame();
if(lastFrame != 0) {
elapsed = time(nullptr) - _videoSurface->lastFrame();
}
if(elapsed > static_cast<time_t>(timeout) && _videoSurface) {
stop();
// We want to start it back again with _updateTimer
_stop = false;
}
} else {
if(!_stop && _running && !_uri.isEmpty() && _videoSettings->streamEnabled()->rawValue().toBool()) {
start();
}
_videoRunning = false;
emit videoRunningChanged();
}
}
if(_videoRunning) {
uint32_t timeout = 1;
if(qgcApp()->toolbox() && qgcApp()->toolbox()->settingsManager()) {
timeout = _videoSettings->rtspTimeout()->rawValue().toUInt();
}
const qint64 now = QDateTime::currentSecsSinceEpoch();
if(now - _lastFrameTime > timeout) {
stop();
// We want to start it back again with _updateTimer
_stop = false;
}
} else {
// FIXME: AV: if pipeline is _running but not _streaming for some time then we need to restart
if(!_stop && !_running && !_uri.isEmpty() && _videoSettings->streamEnabled()->rawValue().toBool()) {
start();
}
}
#endif
......
......@@ -20,8 +20,6 @@
#include <QTimer>
#include <QTcpSocket>
#include "VideoSurface.h"
#if defined(QGC_GST_STREAMING)
#include <gst/gst.h>
#endif
......@@ -44,7 +42,6 @@ public:
#if defined(QGC_GST_STREAMING)
Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged)
#endif
Q_PROPERTY(VideoSurface* videoSurface READ videoSurface CONSTANT)
Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged)
Q_PROPERTY(QString imageFile READ imageFile NOTIFY imageFileChanged)
Q_PROPERTY(QString videoFile READ videoFile NOTIFY videoFileChanged)
......@@ -57,7 +54,6 @@ public:
virtual bool recording () { return _recording; }
#endif
virtual VideoSurface* videoSurface () { return _videoSurface; }
virtual bool videoRunning () { return _videoRunning; }
virtual QString imageFile () { return _imageFile; }
virtual QString videoFile () { return _videoFile; }
......@@ -68,6 +64,9 @@ public:
virtual void setShowFullScreen (bool show) { _showFullScreen = show; emit showFullScreenChanged(); }
void setVideoDecoder (VideoEncoding encoding);
#if defined(QGC_GST_STREAMING)
void setVideoSink (GstElement* videoSink);
#endif
signals:
void videoRunningChanged ();
......@@ -123,19 +122,23 @@ protected:
Sink* _sink;
GstElement* _tee;
void _noteVideoSinkFrame ();
static gboolean _onBusMessage (GstBus* bus, GstMessage* message, gpointer user_data);
static GstPadProbeReturn _unlinkCallBack (GstPad* pad, GstPadProbeInfo* info, gpointer user_data);
static GstPadProbeReturn _videoSinkProbe (GstPad* pad, GstPadProbeInfo* info, gpointer user_data);
static GstPadProbeReturn _keyframeWatch (GstPad* pad, GstPadProbeInfo* info, gpointer user_data);
virtual void _detachRecordingBranch (GstPadProbeInfo* info);
virtual void _shutdownRecordingBranch();
virtual void _shutdownPipeline ();
virtual void _cleanupOldVideos ();
virtual void _setVideoSink (GstElement* sink);
GstElement* _pipeline;
GstElement* _pipelineStopRec;
GstElement* _videoSink;
guint64 _lastFrameId;
qint64 _lastFrameTime;
//-- Wait for Video Server to show up before starting
QTimer _frameTimer;
......@@ -153,7 +156,6 @@ protected:
QString _uri;
QString _imageFile;
QString _videoFile;
VideoSurface* _videoSurface;
bool _videoRunning;
bool _showFullScreen;
VideoSettings* _videoSettings;
......
......@@ -25,16 +25,14 @@
#if defined(__ios__)
#include "gst_ios_init.h"
#endif
#else
#include "GLVideoItemStub.h"
#endif
#include "VideoStreaming.h"
#include "VideoItem.h"
#include "VideoSurface.h"
#if defined(QGC_GST_STREAMING)
G_BEGIN_DECLS
// Our own plugin
GST_PLUGIN_STATIC_DECLARE(QGC_VIDEOSINK_PLUGIN);
// The static plugins we use
#if defined(__mobile__)
GST_PLUGIN_STATIC_DECLARE(coreelements);
......@@ -47,10 +45,12 @@
GST_PLUGIN_STATIC_DECLARE(rtpmanager);
GST_PLUGIN_STATIC_DECLARE(isomp4);
GST_PLUGIN_STATIC_DECLARE(matroska);
GST_PLUGIN_STATIC_DECLARE(opengl);
#endif
#if defined(__android__)
GST_PLUGIN_STATIC_DECLARE(androidmedia);
#endif
GST_PLUGIN_STATIC_DECLARE(qmlgl);
G_END_DECLS
#endif
......@@ -148,8 +148,6 @@ void initializeVideoStreaming(int &argc, char* argv[], char* logpath, char* debu
g_error_free(error);
}
#endif
// Our own plugin
GST_PLUGIN_STATIC_REGISTER(QGC_VIDEOSINK_PLUGIN);
// The static plugins we use
#if defined(__android__)
GST_PLUGIN_STATIC_REGISTER(coreelements);
......@@ -164,12 +162,33 @@ void initializeVideoStreaming(int &argc, char* argv[], char* logpath, char* debu
GST_PLUGIN_STATIC_REGISTER(matroska);
GST_PLUGIN_STATIC_REGISTER(androidmedia);
#endif
#if defined(__mobile__)
GST_PLUGIN_STATIC_REGISTER(opengl);
#endif
/* the plugin must be loaded before loading the qml file to register the
* GstGLVideoItem qml item
* FIXME Add a QQmlExtensionPlugin into qmlglsink to register GstGLVideoItem
* with the QML engine, then remove this */
GstElement *sink = gst_element_factory_make("qmlglsink", nullptr);
if (sink == nullptr) {
GST_PLUGIN_STATIC_REGISTER(qmlgl);
sink = gst_element_factory_make("qmlglsink", nullptr);
}
if (sink != nullptr) {
gst_object_unref(sink);
sink = nullptr;
} else {
qCritical() << "unable to find qmlglsink - you need to build it yourself and add to GST_PLUGIN_PATH";
}
#else
Q_UNUSED(argc);
Q_UNUSED(argv);
Q_UNUSED(logpath);
Q_UNUSED(debuglevel);
qmlRegisterType<GLVideoItemStub> ("org.freedesktop.gstreamer.GLVideoItem", 1, 0, "GstGLVideoItem");
Q_UNUSED(argc)
Q_UNUSED(argv)
Q_UNUSED(logpath)
Q_UNUSED(debuglevel)
#endif
qmlRegisterType<VideoItem> ("QGroundControl.QgcQtGStreamer", 1, 0, "VideoItem");
qmlRegisterUncreatableType<VideoSurface>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoSurface", QStringLiteral("VideoSurface from QML is not supported"));
}
......@@ -17,5 +17,3 @@
#pragma once
extern void initializeVideoStreaming (int &argc, char *argv[], char* filename, char* debuglevel);
......@@ -12,9 +12,10 @@
#
LinuxBuild {
QT += x11extras waylandclient
CONFIG += link_pkgconfig
packagesExist(gstreamer-1.0) {
PKGCONFIG += gstreamer-1.0 gstreamer-video-1.0
PKGCONFIG += gstreamer-1.0 gstreamer-video-1.0 gstreamer-gl-1.0
CONFIG += VideoEnabled
}
} else:MacBuild {
......@@ -39,10 +40,11 @@ LinuxBuild {
exists($$GST_ROOT) {
CONFIG += VideoEnabled
LIBS += -L$$GST_ROOT/lib -lgstreamer-1.0 -lgstvideo-1.0 -lgstbase-1.0
LIBS += -L$$GST_ROOT/lib -lgstreamer-1.0 -lgstgl-1.0 -lgstvideo-1.0 -lgstbase-1.0
LIBS += -lglib-2.0 -lintl -lgobject-2.0
INCLUDEPATH += \
$$GST_ROOT/include \
$$GST_ROOT/include/gstreamer-1.0 \
$$GST_ROOT/include/glib-2.0 \
$$GST_ROOT/lib/gstreamer-1.0/include \
......@@ -86,10 +88,12 @@ LinuxBuild {
-lgstrtpmanager \
-lgstisomp4 \
-lgstmatroska \
-lgstandroidmedia
-lgstandroidmedia \
-lgstopengl
# Rest of GStreamer dependencies
LIBS += -L$$GST_ROOT/lib \
-lgraphene-1.0 -ljpeg -lpng16 \
-lgstfft-1.0 -lm \
-lgstnet-1.0 -lgio-2.0 \
-lgstphotography-1.0 -lgstgl-1.0 -lEGL \
......@@ -113,68 +117,16 @@ VideoEnabled {
message("Including support for video streaming")
DEFINES += \
QGC_GST_STREAMING \
GST_PLUGIN_BUILD_STATIC \
QTGLVIDEOSINK_NAME=qt5glvideosink \
QGC_VIDEOSINK_PLUGIN=qt5videosink
INCLUDEPATH += \
$$PWD/gstqtvideosink \
$$PWD/gstqtvideosink/delegates \
$$PWD/gstqtvideosink/painters \
$$PWD/gstqtvideosink/utils \
#-- QtGstreamer (gutted to our needs)
HEADERS += \
$$PWD/gstqtvideosink/delegates/basedelegate.h \
$$PWD/gstqtvideosink/delegates/qtquick2videosinkdelegate.h \
$$PWD/gstqtvideosink/delegates/qtvideosinkdelegate.h \
$$PWD/gstqtvideosink/delegates/qwidgetvideosinkdelegate.h \
$$PWD/gstqtvideosink/gstqtglvideosink.h \
$$PWD/gstqtvideosink/gstqtglvideosinkbase.h \
$$PWD/gstqtvideosink/gstqtquick2videosink.h \
$$PWD/gstqtvideosink/gstqtvideosink.h \
$$PWD/gstqtvideosink/gstqtvideosinkbase.h \
$$PWD/gstqtvideosink/gstqtvideosinkmarshal.h \
$$PWD/gstqtvideosink/gstqtvideosinkplugin.h \
$$PWD/gstqtvideosink/gstqwidgetvideosink.h \
$$PWD/gstqtvideosink/painters/abstractsurfacepainter.h \
$$PWD/gstqtvideosink/painters/genericsurfacepainter.h \
$$PWD/gstqtvideosink/painters/openglsurfacepainter.h \
$$PWD/gstqtvideosink/painters/videomaterial.h \
$$PWD/gstqtvideosink/painters/videonode.h \
$$PWD/gstqtvideosink/utils/bufferformat.h \
$$PWD/gstqtvideosink/utils/utils.h \
$$PWD/gstqtvideosink/utils/glutils.h \
SOURCES += \
$$PWD/gstqtvideosink/delegates/basedelegate.cpp \
$$PWD/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp \
$$PWD/gstqtvideosink/delegates/qtvideosinkdelegate.cpp \
$$PWD/gstqtvideosink/delegates/qwidgetvideosinkdelegate.cpp \
$$PWD/gstqtvideosink/gstqtglvideosink.cpp \
$$PWD/gstqtvideosink/gstqtglvideosinkbase.cpp \
$$PWD/gstqtvideosink/gstqtvideosinkmarshal.c \
$$PWD/gstqtvideosink/gstqtquick2videosink.cpp \
$$PWD/gstqtvideosink/gstqtvideosink.cpp \
$$PWD/gstqtvideosink/gstqtvideosinkbase.cpp \
$$PWD/gstqtvideosink/gstqtvideosinkplugin.cpp \
$$PWD/gstqtvideosink/gstqwidgetvideosink.cpp \
$$PWD/gstqtvideosink/painters/genericsurfacepainter.cpp \
$$PWD/gstqtvideosink/painters/openglsurfacepainter.cpp \
$$PWD/gstqtvideosink/painters/videomaterial.cpp \
$$PWD/gstqtvideosink/painters/videonode.cpp \
$$PWD/gstqtvideosink/utils/bufferformat.cpp \
$$PWD/gstqtvideosink/utils/utils.cpp \
QGC_GST_STREAMING
iOSBuild {
OBJECTIVE_SOURCES += \
$$PWD/ios/gst_ios_init.m
$$PWD/iOS/gst_ios_init.m
INCLUDEPATH += \
$$PWD/ios
$$PWD/iOS
}
include($$PWD/../../qmlglsink.pri)
} else {
LinuxBuild|MacBuild|iOSBuild|WindowsBuild|AndroidBuild {
message("Skipping support for video streaming (GStreamer libraries not installed)")
......@@ -182,5 +134,10 @@ VideoEnabled {
} else {
message("Skipping support for video streaming (Unsupported platform)")
}
}
SOURCES += \
$$PWD/GLVideoItemStub.cc
HEADERS += \
$$PWD/GLVideoItemStub.h
}
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Surface
* @author Gus Grubba <gus@auterion.com>
*/
#if defined(QGC_GST_STREAMING)
#include "VideoSurface_p.h"
#endif
#include "VideoSurface.h"
#include <QtCore/QDebug>
#include <QtQuick/QQuickItem>
VideoSurface::VideoSurface(QObject *parent)
: QObject(parent)
#if defined(QGC_GST_STREAMING)
, _data(new VideoSurfacePrivate)
, _lastFrame(0)
, _refed(false)
#endif
{
}
VideoSurface::~VideoSurface()
{
#if defined(QGC_GST_STREAMING)
if (!_refed && _data->videoSink != nullptr) {
gst_element_set_state(_data->videoSink, GST_STATE_NULL);
}
delete _data;
#endif
}
#if defined(QGC_GST_STREAMING)
GstElement* VideoSurface::videoSink()
{
if (_data->videoSink == nullptr) {
if ((_data->videoSink = gst_element_factory_make("qtquick2videosink", nullptr)) == nullptr) {
qCritical("Failed to create qtquick2videosink. Make sure it is installed correctly");
return nullptr;
}
g_object_set(G_OBJECT(_data->videoSink), "sync", gboolean(false), nullptr);
g_signal_connect(_data->videoSink, "update", G_CALLBACK(onUpdateThunk), (void* )this);
_refed = true;
}
return _data->videoSink;
}
void VideoSurface::onUpdate()
{
_lastFrame = time(nullptr);
Q_FOREACH(QQuickItem *item, _data->items) {
item->update();
}
}
void VideoSurface::onUpdateThunk(GstElement* sink, gpointer data)
{
Q_UNUSED(sink);
VideoSurface* pThis = (VideoSurface* )data;
pThis->onUpdate();
}
#endif
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Surface
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include <QtCore/QObject>
#if defined(QGC_GST_STREAMING)
#include <gst/gst.h>
#endif
#if defined(QGC_GST_STREAMING)
class VideoSurfacePrivate;
#endif
class VideoSurface : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(VideoSurface)
public:
explicit VideoSurface(QObject *parent = 0);
virtual ~VideoSurface();
/*! Returns the video sink element that provides this surface's image.
* The element will be constructed the first time that this function
* is called. The surface will always keep a reference to this element.
*/
#if defined(QGC_GST_STREAMING)
GstElement* videoSink();
time_t lastFrame() { return _lastFrame; }
void setLastFrame(time_t t) { _lastFrame = t; }
#endif
protected:
#if defined(QGC_GST_STREAMING)
void onUpdate();
static void onUpdateThunk(GstElement* sink, gpointer data);
#endif
private:
friend class VideoItem;
#if defined(QGC_GST_STREAMING)
VideoSurfacePrivate * const _data;
time_t _lastFrame;
bool _refed;
#endif
};
Q_DECLARE_METATYPE(VideoSurface*)
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Surface (Private Interface)
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "VideoSurface.h"
#include "VideoItem.h"
class VideoSurfacePrivate
{
public:
VideoSurfacePrivate()
: videoSink(nullptr)
{
}
QSet<VideoItem*> items;
GstElement* videoSink;
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "basedelegate.h"
#include <QCoreApplication>
BaseDelegate::BaseDelegate(GstElement * sink, QObject * parent)
: QObject(parent)
, m_colorsDirty(true)
, m_brightness(0)
, m_contrast(0)
, m_hue(0)
, m_saturation(0)
, m_pixelAspectRatio(1, 1)
, m_forceAspectRatioDirty(true)
, m_forceAspectRatio(false)
, m_formatDirty(true)
, m_isActive(false)
, m_buffer(nullptr)
, m_sink(sink)
{
}
BaseDelegate::~BaseDelegate()
{
Q_ASSERT(!isActive());
}
//-------------------------------------
bool BaseDelegate::isActive() const
{
QReadLocker l(&m_isActiveLock);
return m_isActive;
}
void BaseDelegate::setActive(bool active)
{
GST_INFO_OBJECT(m_sink, active ? "Activating" : "Deactivating");
QWriteLocker l(&m_isActiveLock);
m_isActive = active;
if (!active) {
QCoreApplication::postEvent(this, new DeactivateEvent());
}
}
//-------------------------------------
int BaseDelegate::brightness() const
{
QReadLocker l(&m_colorsLock);
return m_brightness;
}
void BaseDelegate::setBrightness(int brightness)
{
QWriteLocker l(&m_colorsLock);
m_brightness = qBound(-100, brightness, 100);
m_colorsDirty = true;
}
int BaseDelegate::contrast() const
{
QReadLocker l(&m_colorsLock);
return m_contrast;
}
void BaseDelegate::setContrast(int contrast)
{
QWriteLocker l(&m_colorsLock);
m_contrast = qBound(-100, contrast, 100);
m_colorsDirty = true;
}
int BaseDelegate::hue() const
{
QReadLocker l(&m_colorsLock);
return m_hue;
}
void BaseDelegate::setHue(int hue)
{
QWriteLocker l(&m_colorsLock);
m_hue = qBound(-100, hue, 100);
m_colorsDirty = true;
}
int BaseDelegate::saturation() const
{
QReadLocker l(&m_colorsLock);
return m_saturation;
}
void BaseDelegate::setSaturation(int saturation)
{
QWriteLocker l(&m_colorsLock);
m_saturation = qBound(-100, saturation, 100);
m_colorsDirty = true;
}
//-------------------------------------
Fraction BaseDelegate::pixelAspectRatio() const
{
QReadLocker l(&m_pixelAspectRatioLock);
return m_pixelAspectRatio;
}
void BaseDelegate::setPixelAspectRatio(const Fraction & f)
{
QWriteLocker l(&m_pixelAspectRatioLock);
m_pixelAspectRatio = f;
}
//-------------------------------------
bool BaseDelegate::forceAspectRatio() const
{
QReadLocker l(&m_forceAspectRatioLock);
return m_forceAspectRatio;
}
void BaseDelegate::setForceAspectRatio(bool force)
{
QWriteLocker l(&m_forceAspectRatioLock);
if (m_forceAspectRatio != force) {
m_forceAspectRatio = force;
m_forceAspectRatioDirty = true;
}
}
//-------------------------------------
bool BaseDelegate::event(QEvent *event)
{
switch((int) event->type()) {
case BufferEventType:
{
BufferEvent *bufEvent = dynamic_cast<BufferEvent*>(event);
Q_ASSERT(bufEvent);
GST_TRACE_OBJECT(m_sink, "Received buffer %" GST_PTR_FORMAT, bufEvent->buffer);
if (isActive()) {
gst_buffer_replace (&m_buffer, bufEvent->buffer);
update();
}
return true;
}
case BufferFormatEventType:
{
BufferFormatEvent *bufFmtEvent = dynamic_cast<BufferFormatEvent*>(event);
Q_ASSERT(bufFmtEvent);
GST_TRACE_OBJECT (m_sink, "Received buffer format event. New format: %s",
gst_video_format_to_string(bufFmtEvent->format.videoFormat()));
m_formatDirty = true;
m_bufferFormat = bufFmtEvent->format;
return true;
}
case DeactivateEventType:
{
GST_LOG_OBJECT(m_sink, "Received deactivate event");
gst_buffer_replace (&m_buffer, nullptr);
update();
return true;
}
default:
return QObject::event(event);
}
}
void BaseDelegate::update()
{
g_signal_emit_by_name(m_sink, "update");
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include <gst/gst.h>
#include "../gstqtvideosinkplugin.h" //for debug category
#include "../utils/bufferformat.h"
#include "../utils/utils.h"
#include <QObject>
#include <QEvent>
#include <QReadWriteLock>
class BaseDelegate : public QObject
{
Q_OBJECT
public:
enum EventType {
BufferEventType = QEvent::User,
BufferFormatEventType,
DeactivateEventType
};
//-------------------------------------
class BufferEvent : public QEvent
{
public:
inline BufferEvent(GstBuffer *buf)
: QEvent(static_cast<QEvent::Type>(BufferEventType)),
buffer(gst_buffer_ref(buf))
{}
virtual ~BufferEvent() {
gst_buffer_unref(buffer);
}
GstBuffer *buffer;
};
class BufferFormatEvent : public QEvent
{
public:
inline BufferFormatEvent(const BufferFormat &format)
: QEvent(static_cast<QEvent::Type>(BufferFormatEventType)),
format(format)
{}
BufferFormat format;
};
class DeactivateEvent : public QEvent
{
public:
inline DeactivateEvent()
: QEvent(static_cast<QEvent::Type>(DeactivateEventType))
{
}
};
//-------------------------------------
explicit BaseDelegate(GstElement *sink, QObject *parent = 0);
virtual ~BaseDelegate();
bool isActive() const;
void setActive(bool playing);
// GstColorBalance interface
int brightness() const;
void setBrightness(int brightness);
int contrast() const;
void setContrast(int contrast);
int hue() const;
void setHue(int hue);
int saturation() const;
void setSaturation(int saturation);
// pixel-aspect-ratio property
Fraction pixelAspectRatio() const;
void setPixelAspectRatio(const Fraction & f);
// force-aspect-ratio property
bool forceAspectRatio() const;
void setForceAspectRatio(bool force);
protected:
// internal event handling
virtual bool event(QEvent *event);
// tells the surface to repaint itself
virtual void update();
protected:
// colorbalance interface properties
mutable QReadWriteLock m_colorsLock;
bool m_colorsDirty;
int m_brightness;
int m_contrast;
int m_hue;
int m_saturation;
// pixel-aspect-ratio property
mutable QReadWriteLock m_pixelAspectRatioLock;
Fraction m_pixelAspectRatio;
// force-aspect-ratio property
mutable QReadWriteLock m_forceAspectRatioLock;
bool m_forceAspectRatioDirty;
bool m_forceAspectRatio;
// format caching
bool m_formatDirty;
BufferFormat m_bufferFormat;
PaintAreas m_areas;
// whether the sink is active (PAUSED or PLAYING)
mutable QReadWriteLock m_isActiveLock;
bool m_isActive;
// the buffer to be drawn next
GstBuffer *m_buffer;
// the video sink element
GstElement * const m_sink;
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "qtquick2videosinkdelegate.h"
#include "../painters/videonode.h"
QtQuick2VideoSinkDelegate::QtQuick2VideoSinkDelegate(GstElement *sink, QObject *parent)
: BaseDelegate(sink, parent)
{
}
QSGNode* QtQuick2VideoSinkDelegate::updateNode(QSGNode *node, const QRectF & targetArea)
{
GST_TRACE_OBJECT(m_sink, "updateNode called");
bool sgnodeFormatChanged = false;
VideoNode *vnode = dynamic_cast<VideoNode*>(node);
if (!vnode) {
GST_INFO_OBJECT(m_sink, "creating new VideoNode");
vnode = new VideoNode;
}
if (!m_buffer) {
if (vnode->materialType() != VideoNode::MaterialTypeSolidBlack) {
vnode->setMaterialTypeSolidBlack();
sgnodeFormatChanged = true;
}
if (sgnodeFormatChanged || targetArea != m_areas.targetArea) {
m_areas.targetArea = targetArea;
vnode->updateGeometry(m_areas);
}
} else {
//change format before geometry, so that we change QSGGeometry as well
if (m_formatDirty) {
vnode->changeFormat(m_bufferFormat);
sgnodeFormatChanged = true;
}
//recalculate the video area if needed
QReadLocker forceAspectRatioLocker(&m_forceAspectRatioLock);
if (sgnodeFormatChanged || targetArea != m_areas.targetArea || m_forceAspectRatioDirty) {
m_forceAspectRatioDirty = false;
QReadLocker pixelAspectRatioLocker(&m_pixelAspectRatioLock);
Qt::AspectRatioMode aspectRatioMode = m_forceAspectRatio ?
Qt::KeepAspectRatio : Qt::IgnoreAspectRatio;
m_areas.calculate(targetArea, m_bufferFormat.frameSize(),
m_bufferFormat.pixelAspectRatio(), m_pixelAspectRatio,
aspectRatioMode);
pixelAspectRatioLocker.unlock();
GST_LOG_OBJECT(m_sink,
"Recalculated paint areas: "
"Frame size: " QSIZE_FORMAT ", "
"target area: " QRECTF_FORMAT ", "
"video area: " QRECTF_FORMAT ", "
"black1: " QRECTF_FORMAT ", "
"black2: " QRECTF_FORMAT,
QSIZE_FORMAT_ARGS(m_bufferFormat.frameSize()),
QRECTF_FORMAT_ARGS(m_areas.targetArea),
QRECTF_FORMAT_ARGS(m_areas.videoArea),
QRECTF_FORMAT_ARGS(m_areas.blackArea1),
QRECTF_FORMAT_ARGS(m_areas.blackArea2)
);
vnode->updateGeometry(m_areas);
}
forceAspectRatioLocker.unlock();
if (m_formatDirty) {
m_formatDirty = false;
//make sure to update the colors after changing material
m_colorsDirty = true;
}
QReadLocker colorsLocker(&m_colorsLock);
if (m_colorsDirty) {
vnode->updateColors(m_brightness, m_contrast, m_hue, m_saturation);
m_colorsDirty = false;
}
colorsLocker.unlock();
vnode->setCurrentFrame(m_buffer);
}
return vnode;
}
/*
Copyright (C) 2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "basedelegate.h"
#include <QtQuick/QSGNode>
class QtQuick2VideoSinkDelegate : public BaseDelegate
{
Q_OBJECT
public:
explicit QtQuick2VideoSinkDelegate(GstElement * sink, QObject * parent = 0);
QSGNode *updateNode(QSGNode *node, const QRectF & targetArea);
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "qtvideosinkdelegate.h"
#include "../painters/genericsurfacepainter.h"
#include "../painters/openglsurfacepainter.h"
#include <QStack>
#include <QPainter>
#include "glutils.h"
QtVideoSinkDelegate::QtVideoSinkDelegate(GstElement *sink, QObject *parent)
: BaseDelegate(sink, parent)
, m_painter(0)
, m_supportedPainters(Generic)
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
, m_glContext(0)
#endif
{
}
QtVideoSinkDelegate::~QtVideoSinkDelegate()
{
destroyPainter();
}
void QtVideoSinkDelegate::paint(QPainter *painter, const QRectF & targetArea)
{
GST_TRACE_OBJECT(m_sink, "paint called");
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
if (m_glContext) {
Q_ASSERT_X(m_glContext == QGLContext::currentContext(),
"qtvideosink - paint",
"Please use a QPainter that is initialized to paint on the "
"GL surface that has the same context as the one given on the glcontext property"
);
}
#endif
if (!m_buffer) {
painter->fillRect(targetArea, Qt::black);
} else {
//recalculate the video area if needed
QReadLocker forceAspectRatioLocker(&m_forceAspectRatioLock);
if (targetArea != m_areas.targetArea || m_formatDirty
|| m_forceAspectRatioDirty)
{
m_forceAspectRatioDirty = false;
QReadLocker pixelAspectRatioLocker(&m_pixelAspectRatioLock);
Qt::AspectRatioMode aspectRatioMode = m_forceAspectRatio ?
Qt::KeepAspectRatio : Qt::IgnoreAspectRatio;
m_areas.calculate(targetArea, m_bufferFormat.frameSize(),
m_bufferFormat.pixelAspectRatio(), m_pixelAspectRatio,
aspectRatioMode);
pixelAspectRatioLocker.unlock();
GST_LOG_OBJECT(m_sink,
"Recalculated paint areas: "
"Frame size: " QSIZE_FORMAT ", "
"target area: " QRECTF_FORMAT ", "
"video area: " QRECTF_FORMAT ", "
"black1: " QRECTF_FORMAT ", "
"black2: " QRECTF_FORMAT,
QSIZE_FORMAT_ARGS(m_bufferFormat.frameSize()),
QRECTF_FORMAT_ARGS(m_areas.targetArea),
QRECTF_FORMAT_ARGS(m_areas.videoArea),
QRECTF_FORMAT_ARGS(m_areas.blackArea1),
QRECTF_FORMAT_ARGS(m_areas.blackArea2)
);
}
forceAspectRatioLocker.unlock();
//if either pixelFormat or frameSize have changed, we need to reset the painter
//and/or change painter, in case the current one does not handle the requested format
if ((m_formatDirty) || !m_painter)
{
changePainter(m_bufferFormat);
m_formatDirty = false;
//make sure to update the colors after changing painter
m_colorsDirty = true;
}
if (G_LIKELY(m_painter)) {
QReadLocker colorsLocker(&m_colorsLock);
if (m_colorsDirty) {
m_painter->updateColors(m_brightness, m_contrast, m_hue, m_saturation);
m_colorsDirty = false;
}
colorsLocker.unlock();
GstMapInfo mem_info;
if (gst_buffer_map(m_buffer, &mem_info, GST_MAP_READ)) {
m_painter->paint(mem_info.data, m_bufferFormat, painter, m_areas);
gst_buffer_unmap(m_buffer, &mem_info);
}
}
}
}
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
QGLContext *QtVideoSinkDelegate::glContext() const
{
return m_glContext;
}
void QtVideoSinkDelegate::setGLContext(QGLContext *context)
{
if (m_glContext == context)
return;
m_glContext = context;
m_supportedPainters = Generic;
if (m_glContext) {
m_glContext->makeCurrent();
QOpenGLFunctionsDef *funcs = getQOpenGLFunctions();
if (funcs) {
const QByteArray extensions(reinterpret_cast<const char *>(funcs->glGetString(GL_EXTENSIONS)));
GST_LOG_OBJECT(m_sink, "Available GL extensions: %s", extensions.constData());
#ifndef QT_OPENGL_ES
if (extensions.contains("ARB_fragment_program"))
m_supportedPainters |= ArbFp;
#endif
#ifndef QT_OPENGL_ES_2
if (QGLShaderProgram::hasOpenGLShaderPrograms(m_glContext)
&& extensions.contains("ARB_shader_objects"))
#endif
m_supportedPainters |= Glsl;
}
}
GST_LOG_OBJECT(m_sink, "Done setting GL context. m_supportedPainters=%x", (int) m_supportedPainters);
}
#endif
void QtVideoSinkDelegate::changePainter(const BufferFormat & format)
{
if (m_painter) {
m_painter->cleanup();
if (G_UNLIKELY(!m_painter->supportsFormat(format.videoFormat()))) {
destroyPainter();
}
}
QStack<PainterType> possiblePainters;
if (GenericSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
possiblePainters.push(Generic);
}
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
if (OpenGLSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
if (m_supportedPainters & ArbFp) {
possiblePainters.push(ArbFp);
}
if (m_supportedPainters & Glsl) {
possiblePainters.push(Glsl);
}
}
#endif
while (!possiblePainters.isEmpty()) {
if (!m_painter) {
PainterType type = possiblePainters.pop();
switch(type) {
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
case Glsl:
GST_LOG_OBJECT(m_sink, "Creating GLSL painter");
m_painter = new GlslSurfacePainter;
break;
# ifndef QT_OPENGL_ES
case ArbFp:
GST_LOG_OBJECT(m_sink, "Creating ARB Fragment Shader painter");
m_painter = new ArbFpSurfacePainter;
break;
# endif
#endif
case Generic:
GST_LOG_OBJECT(m_sink, "Creating Generic painter");
m_painter = new GenericSurfacePainter;
break;
default:
Q_ASSERT(false);
}
}
try {
m_painter->init(format);
return;
} catch (const QString & error) {
GST_ELEMENT_WARNING(m_sink, RESOURCE, FAILED,
("Failed to start painter"), ("%s", error.toUtf8().constData()));
delete m_painter;
m_painter = 0;
}
}
GST_ELEMENT_ERROR(m_sink, RESOURCE, FAILED,
("Failed to create a painter for the given format"), (nullptr));
}
void QtVideoSinkDelegate::destroyPainter()
{
GST_LOG_OBJECT(m_sink, "Destroying painter");
delete m_painter;
m_painter = 0;
}
bool QtVideoSinkDelegate::event(QEvent *event)
{
if (event->type() == (QEvent::Type)DeactivateEventType) {
if (m_painter) {
m_painter->cleanup();
destroyPainter();
}
}
return BaseDelegate::event(event);
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "basedelegate.h"
#include "../painters/abstractsurfacepainter.h"
class QGLContext;
class QtVideoSinkDelegate : public BaseDelegate
{
Q_OBJECT
public:
enum PainterType {
Generic = 0x00,
ArbFp = 0x01,
Glsl = 0x02
};
Q_DECLARE_FLAGS(PainterTypes, PainterType);
explicit QtVideoSinkDelegate(GstElement *sink, QObject *parent = 0);
virtual ~QtVideoSinkDelegate();
PainterTypes supportedPainterTypes() const { return m_supportedPainters; }
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
// glcontext property
QGLContext *glContext() const;
void setGLContext(QGLContext *context);
#endif
// paint action
void paint(QPainter *painter, const QRectF & targetArea);
protected:
// internal event handling
virtual bool event(QEvent *event);
private:
void changePainter(const BufferFormat & format);
void destroyPainter();
AbstractSurfacePainter *m_painter;
PainterTypes m_supportedPainters;
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
QGLContext *m_glContext;
#endif
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QtVideoSinkDelegate::PainterTypes)
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
Copyright (C) 2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "qwidgetvideosinkdelegate.h"
#include <QPainter>
QWidgetVideoSinkDelegate::QWidgetVideoSinkDelegate(GstElement * sink, QObject * parent)
: QtVideoSinkDelegate(sink, parent)
{
}
QWidgetVideoSinkDelegate::~QWidgetVideoSinkDelegate()
{
setWidget(nullptr);
}
QWidget *QWidgetVideoSinkDelegate::widget() const
{
return m_widget.data();
}
void QWidgetVideoSinkDelegate::setWidget(QWidget *widget)
{
GST_LOG_OBJECT(m_sink, "Setting \"widget\" property to %" GST_PTR_FORMAT, widget);
if (m_widget) {
m_widget.data()->removeEventFilter(this);
m_widget.data()->setAttribute(Qt::WA_OpaquePaintEvent, m_opaquePaintEventAttribute);
m_widget.data()->update();
m_widget = nullptr;
}
if (widget) {
widget->installEventFilter(this);
m_opaquePaintEventAttribute = widget->testAttribute(Qt::WA_OpaquePaintEvent);
widget->setAttribute(Qt::WA_OpaquePaintEvent, true);
widget->update();
m_widget = widget;
}
}
bool QWidgetVideoSinkDelegate::eventFilter(QObject *filteredObject, QEvent *event)
{
if (filteredObject == m_widget.data()) {
switch(event->type()) {
case QEvent::Paint:
{
QPainter painter(m_widget.data());
paint(&painter, m_widget.data()->rect());
return true;
}
default:
return false;
}
} else {
return QtVideoSinkDelegate::eventFilter(filteredObject, event);
}
}
void QWidgetVideoSinkDelegate::update()
{
if (m_widget) {
m_widget.data()->update();
}
}
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
Copyright (C) 2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "qtvideosinkdelegate.h"
#include <QEvent>
#include <QPointer>
#include <QWidget>
class QWidgetVideoSinkDelegate : public QtVideoSinkDelegate
{
Q_OBJECT
public:
explicit QWidgetVideoSinkDelegate(GstElement * sink, QObject * parent = 0);
virtual ~QWidgetVideoSinkDelegate();
// "widget" property
QWidget *widget() const;
void setWidget(QWidget *widget);
protected:
virtual bool eventFilter(QObject *filteredObject, QEvent *event);
virtual void update();
private:
// "widget" property
QPointer<QWidget> m_widget;
// original value of the Qt::WA_OpaquePaintEvent attribute
bool m_opaquePaintEventAttribute;
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
/*
* Note on the marshallers
*
* If it ever needs to be recreated, here is what you need to do:
*
* cat > marshaller.src
* VOID:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
* VOID:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
* POINTER:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
* POINTER:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
* Ctrl+D (EOF)
*
* glib-genmarshal --header marshaller.list > gstqtvideosinkmarshal.h
* glib-genmarshal --body marshaller.list > gstqtvideosinkmarshal.c
*
*/
#include "gstqtglvideosink.h"
#include "gstqtvideosinkmarshal.h"
#include "delegates/qtvideosinkdelegate.h"
guint GstQtGLVideoSink::s_signals[];
DEFINE_TYPE(GstQtGLVideoSink, GST_TYPE_QT_GL_VIDEO_SINK_BASE)
//------------------------------
void GstQtGLVideoSink::emit_update(gpointer sink)
{
g_signal_emit(sink, GstQtGLVideoSink::s_signals[UPDATE_SIGNAL], 0, NULL);
}
//------------------------------
void GstQtGLVideoSink::base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
gst_element_class_set_details_simple(element_class, "Qt GL video sink", "Sink/Video",
"A video sink that can draw on any Qt GL surface",
"George Kiagiadakis <george.kiagiadakis@collabora.com>");
}
void GstQtGLVideoSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
GObjectClass *object_class = G_OBJECT_CLASS(g_class);
object_class->set_property = GstQtGLVideoSink::set_property;
GstQtGLVideoSinkClass *qt_video_sink_class = reinterpret_cast<GstQtGLVideoSinkClass*>(g_class);
qt_video_sink_class->paint = GstQtGLVideoSink::paint;
/**
* GstQtGLVideoSink::paint
* @painter: A valid QPainter pointer that will be used to paint the video
* @x: The x coordinate of the target area rectangle
* @y: The y coordinate of the target area rectangle
* @width: The width of the target area rectangle
* @height: The height of the target area rectangle
*
* This is an action signal that you can call from your Qt surface class inside
* its paint function to render the video. It takes a QPainter* and the target
* area rectangle as arguments. You should schedule to call this function to
* repaint the surface whenever the ::update signal is emitted.
*
* Note that the x,y,width and height arguments are actually qreal. This means
* that on architectures like arm they will be float instead of double. You should
* cast the arguments to qreal if they are not already when emitting this signal.
*/
s_signals[PAINT_SIGNAL] =
g_signal_new("paint", G_TYPE_FROM_CLASS(g_class),
static_cast<GSignalFlags>(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_STRUCT_OFFSET(GstQtGLVideoSinkClass, paint),
nullptr, nullptr,
qRealIsDouble() ?
g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE :
g_cclosure_user_marshal_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT,
G_TYPE_NONE, 5,
G_TYPE_POINTER, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL);
/**
* GstQtGLVideoSink::update
*
* This signal is emitted when the surface should be repainted. It should
* be connected to QWidget::update() or QGraphicsItem::update() or any
* other similar function in your surface.
*/
s_signals[UPDATE_SIGNAL] =
g_signal_new("update", G_TYPE_FROM_CLASS(g_class),
G_SIGNAL_RUN_LAST,
0, nullptr, nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* GstQtGLVideoSink::glcontext
*
* This property holds a pointer to the QGLContext that will be used to render
* the video using OpenGL acceleration. You must set this to a valid QGLContext
* pointer before the element changes state to READY, or else the state change will fail.
**/
g_object_class_install_property(object_class, PROP_GLCONTEXT,
g_param_spec_pointer("glcontext", "GL context",
"The QGLContext that will be used to do OpenGL-accelerated rendering",
static_cast<GParamFlags>(G_PARAM_WRITABLE)));
}
void GstQtGLVideoSink::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(g_class);
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(instance);
sinkBase->delegate = new QtVideoSinkDelegate(GST_ELEMENT(sinkBase));
}
//------------------------------
void GstQtGLVideoSink::set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_id) {
case PROP_GLCONTEXT:
sinkBase->delegate->setGLContext(static_cast<QGLContext*>(g_value_get_pointer(value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
//------------------------------
void GstQtGLVideoSink::paint(GstQtGLVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height)
{
GST_QT_VIDEO_SINK_BASE(sink)->delegate->paint(static_cast<QPainter*>(painter),
QRectF(x, y, width, height));
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtglvideosinkbase.h"
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
#define GST_TYPE_QT_GL_VIDEO_SINK \
(GstQtGLVideoSink::get_type())
struct GstQtGLVideoSink
{
public:
GstQtGLVideoSinkBase parent;
static GType get_type();
static void emit_update(gpointer sink);
private:
enum {
PROP_0,
PROP_GLCONTEXT
};
enum {
PAINT_SIGNAL,
UPDATE_SIGNAL,
LAST_SIGNAL
};
static void base_init(gpointer g_class);
static void class_init(gpointer g_class, gpointer class_data);
static void init(GTypeInstance *instance, gpointer g_class);
static void set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void paint(GstQtGLVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
static guint s_signals[LAST_SIGNAL];
};
struct GstQtGLVideoSinkClass
{
GstQtGLVideoSinkBaseClass parent_class;
/* paint action signal */
void (*paint) (GstQtGLVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
};
#endif // GST_QT_VIDEO_SINK_NO_OPENGL
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtvideosinkbase.h"
#include <gst/video/colorbalance.h>
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
#define GST_TYPE_QT_GL_VIDEO_SINK_BASE \
(GstQtGLVideoSinkBase::get_type())
#define GST_QT_GL_VIDEO_SINK_BASE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_QT_GL_VIDEO_SINK_BASE, GstQtGLVideoSinkBase))
struct GstQtGLVideoSinkBase
{
public:
GstQtVideoSinkBase parent;
static GType get_type();
private:
enum {
PROP_0,
PROP_CONTRAST,
PROP_BRIGHTNESS,
PROP_HUE,
PROP_SATURATION
};
//index for s_colorbalance_labels
enum {
LABEL_CONTRAST = 0,
LABEL_BRIGHTNESS,
LABEL_HUE,
LABEL_SATURATION,
LABEL_LAST
};
static void init_interfaces(GType type);
static void base_init(gpointer g_class);
static void class_init(gpointer g_class, gpointer class_data);
static void init(GTypeInstance *instance, gpointer g_class);
static void finalize(GObject *object);
static void colorbalance_init(GstColorBalanceInterface *balance_interface, gpointer data);
static const GList *colorbalance_list_channels(GstColorBalance *balance);
static void colorbalance_set_value(GstColorBalance *balance,
GstColorBalanceChannel *channel,
gint value);
static gint colorbalance_get_value(GstColorBalance *balance,
GstColorBalanceChannel *channel);
static GstColorBalanceType colorbalance_get_balance_type(GstColorBalance *balance);
static void set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
static gboolean start(GstBaseSink *sink);
static gboolean set_caps(GstBaseSink *sink, GstCaps *caps);
GList *m_channels_list;
static const char * const s_colorbalance_labels[];
static GstQtVideoSinkBaseClass *s_parent_class;
};
struct GstQtGLVideoSinkBaseClass
{
GstQtVideoSinkBaseClass parent_class;
};
#endif // GST_QT_VIDEO_SINK_NO_OPENGL
/*
Copyright (C) 2013 Collabora Ltd.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include <gst/video/gstvideosink.h>
#include <QtGlobal>
#define GST_TYPE_QT_QUICK2_VIDEO_SINK \
(gst_qt_quick2_video_sink_get_type ())
#define GST_QT_QUICK2_VIDEO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSink))
#define GST_IS_QT_QUICK2_VIDEO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK))
#define GST_QT_QUICK2_VIDEO_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSinkClass))
#define GST_IS_QT_QUICK2_VIDEO_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_QT_QUICK2_VIDEO_SINK))
#define GST_QT_QUICK2_VIDEO_SINK_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSinkClass))
typedef struct _GstQtQuick2VideoSink GstQtQuick2VideoSink;
typedef struct _GstQtQuick2VideoSinkClass GstQtQuick2VideoSinkClass;
typedef struct _GstQtQuick2VideoSinkPrivate GstQtQuick2VideoSinkPrivate;
struct _GstQtQuick2VideoSink
{
GstVideoSink parent_instance;
GstQtQuick2VideoSinkPrivate *priv;
};
struct _GstQtQuick2VideoSinkClass
{
GstVideoSinkClass parent_class;
gpointer (*update_node)(GstQtQuick2VideoSink *self,
gpointer node, qreal x, qreal y, qreal w, qreal h);
};
GType gst_qt_quick2_video_sink_get_type (void);
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqtvideosink.h"
#include "gstqtvideosinkmarshal.h"
#include "delegates/qtvideosinkdelegate.h"
guint GstQtVideoSink::s_signals[];
DEFINE_TYPE(GstQtVideoSink, GST_TYPE_QT_VIDEO_SINK_BASE)
//------------------------------
void GstQtVideoSink::emit_update(gpointer sink)
{
g_signal_emit(sink, GstQtVideoSink::s_signals[UPDATE_SIGNAL], 0, NULL);
}
//------------------------------
void GstQtVideoSink::base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
gst_element_class_set_details_simple(element_class, "Qt video sink", "Sink/Video",
"A video sink that can draw on any Qt surface",
"George Kiagiadakis <george.kiagiadakis@collabora.com>");
}
void GstQtVideoSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
GstQtVideoSinkClass *qt_video_sink_class = reinterpret_cast<GstQtVideoSinkClass*>(g_class);
qt_video_sink_class->paint = GstQtVideoSink::paint;
/**
* GstQtVideoSink::paint
* @painter: A valid QPainter pointer that will be used to paint the video
* @x: The x coordinate of the target area rectangle
* @y: The y coordinate of the target area rectangle
* @width: The width of the target area rectangle
* @height: The height of the target area rectangle
*
* This is an action signal that you can call from your Qt surface class inside
* its paint function to render the video. It takes a QPainter* and the target
* area rectangle as arguments. You should schedule to call this function to
* repaint the surface whenever the ::update signal is emitted.
*
* Note that the x,y,width and height arguments are actually qreal. This means
* that on architectures like arm they will be float instead of double. You should
* cast the arguments to qreal if they are not already when emitting this signal.
*/
s_signals[PAINT_SIGNAL] =
g_signal_new("paint", G_TYPE_FROM_CLASS(g_class),
static_cast<GSignalFlags>(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_STRUCT_OFFSET(GstQtVideoSinkClass, paint),
nullptr, nullptr,
qRealIsDouble() ?
g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE :
g_cclosure_user_marshal_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT,
G_TYPE_NONE, 5,
G_TYPE_POINTER, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL);
/**
* GstQtVideoSink::update
*
* This signal is emitted when the surface should be repainted. It should
* be connected to QWidget::update() or QGraphicsItem::update() or any
* other similar function in your surface.
*/
s_signals[UPDATE_SIGNAL] =
g_signal_new("update", G_TYPE_FROM_CLASS(g_class),
G_SIGNAL_RUN_LAST,
0, nullptr, nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
void GstQtVideoSink::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(g_class);
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(instance);
sinkBase->delegate = new QtVideoSinkDelegate(GST_ELEMENT(sinkBase));
}
//------------------------------
void GstQtVideoSink::paint(GstQtVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height)
{
GST_QT_VIDEO_SINK_BASE(sink)->delegate->paint(static_cast<QPainter*>(painter),
QRectF(x, y, width, height));
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtvideosinkbase.h"
#define GST_TYPE_QT_VIDEO_SINK \
(GstQtVideoSink::get_type())
struct GstQtVideoSink
{
public:
GstQtVideoSinkBase parent;
static GType get_type();
static void emit_update(gpointer sink);
private:
enum {
PAINT_SIGNAL,
UPDATE_SIGNAL,
LAST_SIGNAL
};
static void base_init(gpointer g_class);
static void class_init(gpointer g_class, gpointer class_data);
static void init(GTypeInstance *instance, gpointer g_class);
static void paint(GstQtVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
static guint s_signals[LAST_SIGNAL];
};
struct GstQtVideoSinkClass
{
GstQtVideoSinkBaseClass parent_class;
/* paint action signal */
void (*paint) (GstQtVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqtvideosinkbase.h"
#include "delegates/qtvideosinkdelegate.h"
#include "painters/genericsurfacepainter.h"
#include <cstring>
#include <QCoreApplication>
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
# define CAPS_FORMATS "{ ARGB, xRGB, RGB, RGB16 }"
#else
# define CAPS_FORMATS "{ BGRA, BGRx, RGB, RGB16 }"
#endif
GstVideoSinkClass *GstQtVideoSinkBase::s_parent_class = nullptr;
DEFINE_TYPE(GstQtVideoSinkBase, GST_TYPE_VIDEO_SINK)
//------------------------------
void GstQtVideoSinkBase::base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
static GstStaticPadTemplate sink_pad_template =
GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (CAPS_FORMATS))
);
gst_element_class_add_pad_template(
element_class, gst_static_pad_template_get(&sink_pad_template));
}
void GstQtVideoSinkBase::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
s_parent_class = reinterpret_cast<GstVideoSinkClass*>(g_type_class_peek_parent(g_class));
GObjectClass *object_class = G_OBJECT_CLASS(g_class);
object_class->finalize = GstQtVideoSinkBase::finalize;
object_class->set_property = GstQtVideoSinkBase::set_property;
object_class->get_property = GstQtVideoSinkBase::get_property;
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
element_class->change_state = GstQtVideoSinkBase::change_state;
GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(g_class);
base_sink_class->set_caps = GstQtVideoSinkBase::set_caps;
GstVideoSinkClass *video_sink_class = GST_VIDEO_SINK_CLASS(g_class);
video_sink_class->show_frame = GstQtVideoSinkBase::show_frame;
/**
* GstQtVideoSinkBase::pixel-aspect-ratio
*
* The pixel aspect ratio of the display device.
**/
g_object_class_install_property(object_class, PROP_PIXEL_ASPECT_RATIO,
g_param_spec_string("pixel-aspect-ratio", "Pixel aspect ratio",
"The pixel aspect ratio of the display device",
"1/1", static_cast<GParamFlags>(G_PARAM_READWRITE)));
/**
* GstQtVideoSinkBase::force-aspect-ratio
*
* If set to TRUE, the sink will scale the video respecting its original aspect ratio
* and any remaining space will be filled with black.
* If set to FALSE, the sink will scale the video to fit the whole drawing area.
**/
g_object_class_install_property(object_class, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean("force-aspect-ratio", "Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE)));
}
void GstQtVideoSinkBase::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(instance);
Q_UNUSED(g_class);
/* sink->delegate is initialized in the subclasses */
}
void GstQtVideoSinkBase::finalize(GObject *object)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
delete sink->delegate;
sink->delegate = 0;
}
//------------------------------
void GstQtVideoSinkBase::set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_id) {
case PROP_PIXEL_ASPECT_RATIO:
{
GValue tmp;
std::memset(&tmp, 0, sizeof(GValue));
g_value_init(&tmp, GST_TYPE_FRACTION);
if (g_value_transform(value, &tmp)) {
int n = gst_value_get_fraction_numerator(&tmp);
int d = gst_value_get_fraction_denominator(&tmp);
sink->delegate->setPixelAspectRatio(Fraction(n, d));
} else {
GST_WARNING_OBJECT(object, "Could not transform string to aspect ratio");
}
g_value_unset(&tmp);
break;
}
case PROP_FORCE_ASPECT_RATIO:
sink->delegate->setForceAspectRatio(g_value_get_boolean(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
void GstQtVideoSinkBase::get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_id) {
case PROP_PIXEL_ASPECT_RATIO:
{
GValue tmp;
Fraction par = sink->delegate->pixelAspectRatio();
std::memset(&tmp, 0, sizeof(GValue));
g_value_init(&tmp, GST_TYPE_FRACTION);
gst_value_set_fraction(&tmp, par.numerator, par.denominator);
g_value_transform(&tmp, value);
g_value_unset(&tmp);
break;
}
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean(value, sink->delegate->forceAspectRatio());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
//------------------------------
GstStateChangeReturn GstQtVideoSinkBase::change_state(GstElement *element, GstStateChange transition)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
sink->delegate->setActive(true);
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
sink->delegate->setActive(false);
break;
default:
break;
}
return GST_ELEMENT_CLASS(s_parent_class)->change_state(element, transition);
}
//------------------------------
gboolean GstQtVideoSinkBase::set_caps(GstBaseSink *base, GstCaps *caps)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(base);
GST_LOG_OBJECT(sink, "new caps %" GST_PTR_FORMAT, caps);
BufferFormat format = BufferFormat::fromCaps(caps);
if (GenericSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
QCoreApplication::postEvent(sink->delegate,
new BaseDelegate::BufferFormatEvent(format));
return TRUE;
} else {
return FALSE;
}
}
//------------------------------
GstFlowReturn GstQtVideoSinkBase::show_frame(GstVideoSink *video_sink, GstBuffer *buffer)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(video_sink);
GST_TRACE_OBJECT(sink, "Posting new buffer (%" GST_PTR_FORMAT") for rendering.", buffer);
QCoreApplication::postEvent(sink->delegate, new BaseDelegate::BufferEvent(buffer));
return GST_FLOW_OK;
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtvideosinkplugin.h"
#include <gst/video/gstvideosink.h>
#define GST_TYPE_QT_VIDEO_SINK_BASE \
(GstQtVideoSinkBase::get_type())
#define GST_QT_VIDEO_SINK_BASE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_QT_VIDEO_SINK_BASE, GstQtVideoSinkBase))
#define GST_QT_VIDEO_SINK_BASE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_QT_VIDEO_SINK_BASE, GstQtVideoSinkBaseClass))
#define GST_QT_VIDEO_SINK_BASE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_QT_VIDEO_SINK_BASE, GstQtVideoSinkBaseClass))
class QtVideoSinkDelegate;
struct GstQtVideoSinkBase
{
public:
GstVideoSink parent;
static GType get_type();
private:
enum {
PROP_0,
PROP_PIXEL_ASPECT_RATIO,
PROP_FORCE_ASPECT_RATIO,
};
static void base_init(gpointer g_class);
static void class_init(gpointer g_class, gpointer class_data);
static void init(GTypeInstance *instance, gpointer g_class);
static void finalize(GObject *object);
static void set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
static gboolean set_caps(GstBaseSink *sink, GstCaps *caps);
static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer);
public:
QtVideoSinkDelegate *delegate;
private:
static GstVideoSinkClass *s_parent_class;
};
struct GstQtVideoSinkBaseClass
{
GstVideoSinkClass parent_class;
};
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
/* VOID:POINTER,FLOAT,FLOAT,FLOAT,FLOAT (marshaller.src:1) */
extern void g_cclosure_user_marshal_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/* VOID:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE (marshaller.src:2) */
extern void g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/* POINTER:POINTER,FLOAT,FLOAT,FLOAT,FLOAT (marshaller.src:3) */
extern void g_cclosure_user_marshal_POINTER__POINTER_FLOAT_FLOAT_FLOAT_FLOAT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/* POINTER:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE (marshaller.src:4) */
extern void g_cclosure_user_marshal_POINTER__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
G_END_DECLS
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqtvideosinkplugin.h"
#include "gstqtvideosink.h"
#include "gstqtglvideosink.h"
#include "gstqwidgetvideosink.h"
#define PACKAGE "mini-qt-gstreamer"
#define PACKAGE_NAME "QgcQtGStreamer"
#define PACKAGE_ORIGIN "http://gstreamer.freedesktop.org/"
#define PACKAGE_VERSION "1.2.0"
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
# include "gstqtquick2videosink.h"
#endif
GST_DEBUG_CATEGORY(gst_qt5gstvideosink_debug);
/* entry point to initialize the plug-in */
static gboolean plugin_init(GstPlugin *plugin)
{
GST_DEBUG_CATEGORY_INIT(gst_qt5gstvideosink_debug,
G_STRINGIFY(QGC_VIDEOSINK_PLUGIN), 0,
"Debug category for GstQtVideoSink");
if(!gst_element_register(plugin, G_STRINGIFY(QGC_VIDEOSINK_PLUGIN),
GST_RANK_NONE, GST_TYPE_QT_VIDEO_SINK)) {
GST_ERROR("Failed to register " G_STRINGIFY(QGC_VIDEOSINK_PLUGIN));
return FALSE;
}
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
if(!gst_element_register(plugin, G_STRINGIFY(QTGLVIDEOSINK_NAME),
GST_RANK_NONE, GST_TYPE_QT_GL_VIDEO_SINK)) {
GST_ERROR("Failed to register " G_STRINGIFY(QTGLVIDEOSINK_NAME));
return FALSE;
}
#endif
if(!gst_element_register(plugin, G_STRINGIFY(QWIDGETVIDEOSINK_NAME),
GST_RANK_NONE, GST_TYPE_QWIDGET_VIDEO_SINK)) {
GST_ERROR("Failed to register " G_STRINGIFY(QWIDGETVIDEOSINK_NAME));
return FALSE;
}
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
if (!gst_element_register(plugin, "qtquick2videosink",
GST_RANK_NONE, GST_TYPE_QT_QUICK2_VIDEO_SINK)) {
GST_ERROR("Failed to register qtquick2videosink");
return FALSE;
}
#endif
return TRUE;
}
GST_PLUGIN_DEFINE (
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
QGC_VIDEOSINK_PLUGIN,
"A video sink that can draw on any Qt surface",
plugin_init,
PACKAGE_VERSION,
"LGPL",
PACKAGE_NAME,
PACKAGE_ORIGIN
)
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include <gst/gst.h>
#include <QtGlobal>
GST_DEBUG_CATEGORY_EXTERN(gst_qt5gstvideosink_debug);
#define GST_CAT_DEFAULT gst_qt5gstvideosink_debug
#define DEFINE_TYPE_FULL(cpp_type, type_name, parent_type, additional_initializations) \
GType cpp_type::get_type() \
{ \
static volatile gsize gonce_data = 0; \
if (g_once_init_enter(&gonce_data)) { \
GType type = 0; \
GTypeInfo info; \
info.class_size = sizeof(cpp_type##Class); \
info.base_init = &cpp_type::base_init; \
info.base_finalize = NULL; \
info.class_init = &cpp_type::class_init; \
info.class_finalize = NULL; \
info.class_data = NULL; \
info.instance_size = sizeof(cpp_type); \
info.n_preallocs = 0; \
info.instance_init = &cpp_type::init; \
info.value_table = 0; \
type = g_type_register_static(parent_type, g_intern_static_string(type_name), &info, (GTypeFlags)0); \
additional_initializations(type); \
g_once_init_leave(&gonce_data, (gsize) type); \
} \
return (GType) gonce_data; \
}
// To allow qt4 and qt5 versions of the plugin to be installed at the same time,
// use a different name for their GType, so that the glib type system can handle
// both plugins being loaded by the gstreamer registry
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
# define DEFINE_TYPE(cpp_type, parent_type) \
DEFINE_TYPE_FULL(cpp_type, #cpp_type "_qt5", parent_type, Q_UNUSED)
# define DEFINE_TYPE_WITH_CODE(cpp_type, parent_type, additional_initializations) \
DEFINE_TYPE_FULL(cpp_type, #cpp_type "_qt5", parent_type, additional_initializations)
#else
# define DEFINE_TYPE(cpp_type, parent_type) \
DEFINE_TYPE_FULL(cpp_type, #cpp_type, parent_type, Q_UNUSED)
# define DEFINE_TYPE_WITH_CODE(cpp_type, parent_type, additional_initializations) \
DEFINE_TYPE_FULL(cpp_type, #cpp_type, parent_type, additional_initializations)
#endif
inline bool qRealIsDouble() { return sizeof(qreal) == sizeof(double); }
#define G_TYPE_QREAL qRealIsDouble() ? G_TYPE_DOUBLE : G_TYPE_FLOAT
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
Copyright (C) 2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqwidgetvideosink.h"
#include "delegates/qwidgetvideosinkdelegate.h"
DEFINE_TYPE(GstQWidgetVideoSink, GST_TYPE_QT_VIDEO_SINK_BASE)
//------------------------------
void GstQWidgetVideoSink::base_init(gpointer gclass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(gclass);
gst_element_class_set_details_simple(element_class, "QWidget video sink", "Sink/Video",
"A video sink that draws on a QWidget using QPainter",
"George Kiagiadakis <george.kiagiadakis@collabora.com>");
}
void GstQWidgetVideoSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
gobject_class->set_property = GstQWidgetVideoSink::set_property;
gobject_class->get_property = GstQWidgetVideoSink::get_property;
/**
* GstQWidgetVideoSink::widget
*
* This property holds a pointer to the QWidget on which the sink will paint the video.
* You can set this property at any time, even if the element is in PLAYING
* state. You can also set this property to NULL at any time to release
* the widget. In this case, qwidgetvideosink will behave like a fakesink,
* i.e. it will silently drop all the frames that it receives. It is also safe
* to delete the widget that has been set as this property; the sink will be
* signaled and this property will automatically be set to NULL.
**/
g_object_class_install_property(gobject_class, PROP_WIDGET,
g_param_spec_pointer("widget", "Widget",
"The widget on which this element will paint the video",
static_cast<GParamFlags>(G_PARAM_READWRITE)));
}
void GstQWidgetVideoSink::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(g_class);
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(instance);
sinkBase->delegate = new QWidgetVideoSinkDelegate(GST_ELEMENT(sinkBase));
}
void GstQWidgetVideoSink::set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(object);
QWidgetVideoSinkDelegate *delegate = static_cast<QWidgetVideoSinkDelegate*>(sinkBase->delegate);
switch (prop_id) {
case PROP_WIDGET:
delegate->setWidget(static_cast<QWidget*>(g_value_get_pointer(value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
void GstQWidgetVideoSink::get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(object);
QWidgetVideoSinkDelegate *delegate = static_cast<QWidgetVideoSinkDelegate*>(sinkBase->delegate);
switch (prop_id) {
case PROP_WIDGET:
g_value_set_pointer(value, delegate->widget());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
Copyright (C) 2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtvideosinkbase.h"
#define GST_TYPE_QWIDGET_VIDEO_SINK \
(GstQWidgetVideoSink::get_type())
struct GstQWidgetVideoSink
{
public:
GstQtVideoSinkBase parent;
static GType get_type();
private:
enum {
PROP_0,
PROP_WIDGET
};
static void base_init(gpointer g_class);
static void class_init(gpointer g_class, gpointer class_data);
static void init(GTypeInstance *instance, gpointer g_class);
static void set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
};
struct GstQWidgetVideoSinkClass
{
GstQtVideoSinkBaseClass parent_class;
};
VOID:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
VOID:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
POINTER:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
POINTER:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "../utils/bufferformat.h"
#include <QRectF>
class QPainter;
/** Common interface for all the painters */
class AbstractSurfacePainter
{
public:
virtual ~AbstractSurfacePainter() {}
virtual bool supportsFormat(GstVideoFormat format) const = 0;
virtual void init(const BufferFormat & format) = 0;
virtual void cleanup() = 0;
virtual void paint(quint8 *data, const BufferFormat & frameFormat,
QPainter *painter, const PaintAreas & areas) = 0;
virtual void updateColors(int brightness, int contrast, int hue, int saturation) = 0;
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "genericsurfacepainter.h"
#include <QPainter>
GenericSurfacePainter::GenericSurfacePainter()
: m_imageFormat(QImage::Format_Invalid)
{
}
//static
QSet<GstVideoFormat> GenericSurfacePainter::supportedPixelFormats()
{
return QSet<GstVideoFormat>({
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
GST_VIDEO_FORMAT_ARGB,
GST_VIDEO_FORMAT_xRGB,
#else
GST_VIDEO_FORMAT_BGRA,
GST_VIDEO_FORMAT_BGRx,
#endif
GST_VIDEO_FORMAT_RGB,
GST_VIDEO_FORMAT_RGB16,
});
}
void GenericSurfacePainter::init(const BufferFormat &format)
{
switch (format.videoFormat()) {
// QImage is shitty and reads integers instead of bytes,
// thus it is affected by the host's endianness
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
case GST_VIDEO_FORMAT_ARGB:
#else
case GST_VIDEO_FORMAT_BGRA:
#endif
m_imageFormat = QImage::Format_ARGB32;
break;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
case GST_VIDEO_FORMAT_xRGB:
#else
case GST_VIDEO_FORMAT_BGRx:
#endif
m_imageFormat = QImage::Format_RGB32;
break;
//16-bit RGB formats use host's endianness in GStreamer
//FIXME-0.11 do endianness checks like above if semantics have changed
case GST_VIDEO_FORMAT_RGB16:
m_imageFormat = QImage::Format_RGB16;
break;
//This is not affected by endianness
case GST_VIDEO_FORMAT_RGB:
m_imageFormat = QImage::Format_RGB888;
break;
default:
throw QString("Unsupported format");
}
}
void GenericSurfacePainter::cleanup()
{
m_imageFormat = QImage::Format_Invalid;
}
void GenericSurfacePainter::paint(quint8 *data,
const BufferFormat & frameFormat,
QPainter *painter,
const PaintAreas & areas)
{
Q_ASSERT(m_imageFormat != QImage::Format_Invalid);
QImage image(
data,
frameFormat.frameSize().width(),
frameFormat.frameSize().height(),
frameFormat.bytesPerLine(),
m_imageFormat);
QRectF sourceRect = areas.sourceRect;
sourceRect.setX(sourceRect.x() * frameFormat.frameSize().width());
sourceRect.setY(sourceRect.y() * frameFormat.frameSize().height());
sourceRect.setWidth(sourceRect.width() * frameFormat.frameSize().width());
sourceRect.setHeight(sourceRect.height() * frameFormat.frameSize().height());
painter->fillRect(areas.blackArea1, Qt::black);
painter->drawImage(areas.videoArea, image, sourceRect);
painter->fillRect(areas.blackArea2, Qt::black);
}
void GenericSurfacePainter::updateColors(int, int, int, int)
{
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "abstractsurfacepainter.h"
#include <QSet>
#include <QImage>
/**
* Generic painter that paints using the QPainter API.
* No colorspace conversion is done and no colors adjustment either.
*/
class GenericSurfacePainter : public AbstractSurfacePainter
{
public:
GenericSurfacePainter();
static QSet<GstVideoFormat> supportedPixelFormats();
virtual bool supportsFormat(GstVideoFormat format) const {
return supportedPixelFormats().contains(format);
}
virtual void init(const BufferFormat &format);
virtual void cleanup();
virtual void paint(quint8 *data, const BufferFormat & frameFormat,
QPainter *painter, const PaintAreas & areas);
virtual void updateColors(int brightness, int contrast, int hue, int saturation);
private:
QImage::Format m_imageFormat;
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
#include "abstractsurfacepainter.h"
#include <QGLShaderProgram>
#ifndef Q_WS_MAC
# ifndef APIENTRYP
# ifdef APIENTRY
# define APIENTRYP APIENTRY *
# else
# define APIENTRY
# define APIENTRYP *
# endif
# endif
#else
# define APIENTRY
# define APIENTRYP *
#endif
class OpenGLSurfacePainter : public AbstractSurfacePainter
{
public:
OpenGLSurfacePainter();
static QSet<GstVideoFormat> supportedPixelFormats();
virtual bool supportsFormat(GstVideoFormat format) const {
return supportedPixelFormats().contains(format);
}
virtual void updateColors(int brightness, int contrast, int hue, int saturation);
virtual void paint(quint8 *data, const BufferFormat & frameFormat,
QPainter *painter, const PaintAreas & areas);
protected:
void initRgbTextureInfo(GLenum internalFormat, GLuint format, GLenum type, const QSize &size);
void initYuv420PTextureInfo(const QSize &size);
void initYv12TextureInfo(const QSize &size);
virtual void paintImpl(const QPainter *painter,
const GLfloat *vertexCoordArray,
const GLfloat *textureCoordArray) = 0;
#ifndef QT_OPENGL_ES
typedef void (APIENTRY *_glActiveTexture) (GLenum);
_glActiveTexture glActiveTexture;
#endif
GLenum m_textureFormat;
GLuint m_textureInternalFormat;
GLenum m_textureType;
int m_textureCount;
GLuint m_textureIds[3];
int m_textureWidths[3];
int m_textureHeights[3];
int m_textureOffsets[3];
QMatrix4x4 m_colorMatrix;
GstVideoColorMatrix m_videoColorMatrix;
};
#ifndef QT_OPENGL_ES
class ArbFpSurfacePainter : public OpenGLSurfacePainter
{
public:
ArbFpSurfacePainter();
virtual void init(const BufferFormat & format);
virtual void cleanup();
protected:
virtual void paintImpl(const QPainter *painter,
const GLfloat *vertexCoordArray,
const GLfloat *textureCoordArray);
private:
typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint);
typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *);
typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *);
typedef void (APIENTRY *_glProgramLocalParameter4fARB) (
GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
typedef void (APIENTRY *_glActiveTexture) (GLenum);
_glProgramStringARB glProgramStringARB;
_glBindProgramARB glBindProgramARB;
_glDeleteProgramsARB glDeleteProgramsARB;
_glGenProgramsARB glGenProgramsARB;
_glProgramLocalParameter4fARB glProgramLocalParameter4fARB;
GLuint m_programId;
};
#endif
class GlslSurfacePainter : public OpenGLSurfacePainter
{
public:
GlslSurfacePainter();
virtual void init(const BufferFormat & format);
virtual void cleanup();
protected:
virtual void paintImpl(const QPainter *painter,
const GLfloat *vertexCoordArray,
const GLfloat *textureCoordArray);
private:
QGLShaderProgram m_program;
};
#endif // GST_QT_VIDEO_SINK_NO_OPENGL
/*
Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
Copyright (C) 2013 basysKom GmbH <info@basyskom.com>
Copyright (C) 2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "../utils/bufferformat.h"
#include <QSize>
#include <QMutex>
#include <QMatrix4x4>
#include <QtQuick/QSGMaterial>
class VideoMaterialShader;
class VideoMaterial : public QSGMaterial
{
public:
static VideoMaterial *create(const BufferFormat & format);
virtual ~VideoMaterial();
virtual int compare(const QSGMaterial *other) const;
void setCurrentFrame(GstBuffer *buffer);
void updateColors(int brightness, int contrast, int hue, int saturation);
void bind();
protected:
VideoMaterial();
void initRgbTextureInfo(GLenum internalFormat, GLuint format,
GLenum type, const GstVideoInfo& videoInfo);
void initYuv420PTextureInfo(const GstVideoInfo& videoInfo);
void updateYuv420PTextureInfo(const GstVideoInfo& videoInfo);
void init(GstVideoColorMatrix colorMatrixType);
private:
void bindTexture(int i, const quint8 *data);
GstVideoInfo m_videoInfo;
GstBufferPool* m_bufferPool;
GstBuffer *m_frame;
QMutex m_frameMutex;
static const int Num_Texture_IDs = 3;
int m_textureCount;
GLuint m_textureIds[Num_Texture_IDs];
int m_textureWidths[Num_Texture_IDs];
int m_textureHeights[Num_Texture_IDs];
int m_textureOffsets[Num_Texture_IDs];
int m_textureStrides[Num_Texture_IDs];
bool m_textureAllocated[Num_Texture_IDs];
GLenum m_textureFormat;
GLuint m_textureInternalFormat;
GLenum m_textureType;
QMatrix4x4 m_colorMatrix;
GstVideoColorMatrix m_colorMatrixType;
friend class VideoMaterialShader;
};
/*
Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
Copyright (C) 2013 basysKom GmbH <info@basyskom.com>
Copyright (C) 2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "videonode.h"
#include "videomaterial.h"
#include <QtQuick/QSGFlatColorMaterial>
VideoNode::VideoNode()
: QSGGeometryNode()
{
setFlags(OwnsGeometry | OwnsMaterial, true);
setMaterialTypeSolidBlack();
}
void VideoNode::changeFormat(const BufferFormat & format)
{
setMaterial(VideoMaterial::create(format));
setGeometry(0);
m_materialType = MaterialTypeVideo;
}
void VideoNode::setMaterialTypeSolidBlack()
{
QSGFlatColorMaterial *m = new QSGFlatColorMaterial;
m->setColor(Qt::black);
setMaterial(m);
setGeometry(0);
m_materialType = MaterialTypeSolidBlack;
}
void VideoNode::setCurrentFrame(GstBuffer* buffer)
{
if (m_materialType != MaterialTypeVideo) {
return;
}
static_cast<VideoMaterial*>(material())->setCurrentFrame(buffer);
markDirty(DirtyMaterial);
}
void VideoNode::updateColors(int brightness, int contrast, int hue, int saturation)
{
if (m_materialType != MaterialTypeVideo) {
return;
}
static_cast<VideoMaterial*>(material())->updateColors(brightness, contrast, hue, saturation);
markDirty(DirtyMaterial);
}
/* Helpers */
template <typename V>
static inline void setGeom(V *v, const QPointF &p)
{
v->x = p.x();
v->y = p.y();
}
static inline void setTex(QSGGeometry::TexturedPoint2D *v, const QPointF &p)
{
v->tx = p.x();
v->ty = p.y();
}
void VideoNode::updateGeometry(const PaintAreas & areas)
{
QSGGeometry *g = geometry();
if (m_materialType == MaterialTypeVideo) {
if (!g)
g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
QSGGeometry::TexturedPoint2D *v = g->vertexDataAsTexturedPoint2D();
// Set geometry first
setGeom(v + 0, areas.videoArea.topLeft());
setGeom(v + 1, areas.videoArea.bottomLeft());
setGeom(v + 2, areas.videoArea.topRight());
setGeom(v + 3, areas.videoArea.bottomRight());
// and then texture coordinates
setTex(v + 0, areas.sourceRect.topLeft());
setTex(v + 1, areas.sourceRect.bottomLeft());
setTex(v + 2, areas.sourceRect.topRight());
setTex(v + 3, areas.sourceRect.bottomRight());
} else {
if (!g)
g = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
QSGGeometry::Point2D *v = g->vertexDataAsPoint2D();
setGeom(v + 0, areas.videoArea.topLeft());
setGeom(v + 1, areas.videoArea.bottomLeft());
setGeom(v + 2, areas.videoArea.topRight());
setGeom(v + 3, areas.videoArea.bottomRight());
}
if (!geometry())
setGeometry(g);
markDirty(DirtyGeometry);
}
This diff is collapsed.
This diff is collapsed.
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Item
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#if defined(__android__) || defined(__ios__)
#include <QOpenGLFunctions>
#define getQOpenGLFunctions() QOpenGLContext::currentContext()->functions()
#define QOpenGLFunctionsDef QOpenGLFunctions
#endif
#ifdef __rasp_pi2__
#include <QOpenGLFunctions_ES2>
#define getQOpenGLFunctions() QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_ES2>()
#define QOpenGLFunctionsDef QOpenGLFunctions_ES2
#endif
#ifndef QOpenGLFunctionsDef
#include <QOpenGLFunctions_2_0>
#define getQOpenGLFunctions() QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_0>()
#define QOpenGLFunctionsDef QOpenGLFunctions_2_0
#endif
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment