diff --git a/QGCApplication.pro b/QGCApplication.pro
index eb7ec6e3cf2817934b0e7b8ad0255481e8bd3ccd..61d8f15d3f3ea04bd1ee289b19a60753e4c1ee48 100644
--- a/QGCApplication.pro
+++ b/QGCApplication.pro
@@ -652,6 +652,34 @@ SOURCES += \
src/FactSystem/ParameterLoader.cc \
src/FactSystem/FactControls/FactPanelController.cc \
+#-------------------------------------------------------------------------------------
+# Video Streaming
+
+INCLUDEPATH += \
+ src/VideoStreaming
+
+HEADERS += \
+ src/VideoStreaming/VideoItem.h \
+ src/VideoStreaming/VideoReceiver.h \
+ src/VideoStreaming/VideoSurface.h \
+ src/VideoStreaming/VideoSurface_p.h \
+
+SOURCES += \
+ src/VideoStreaming/VideoItem.cc \
+ src/VideoStreaming/VideoReceiver.cc \
+ src/VideoStreaming/VideoSurface.cc \
+
+contains (DEFINES, DISABLE_VIDEOSTREAMING) {
+ message("Skipping support for video streaming (manual override from command line)")
+ DEFINES -= DISABLE_VIDEOSTREAMING
+# Otherwise the user can still disable this feature in the user_config.pri file.
+} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_VIDEOSTREAMING) {
+ message("Skipping support for video streaming (manual override from user_config.pri)")
+} else {
+ include(src/VideoStreaming/VideoStreaming.pri)
+}
+
+#-------------------------------------------------------------------------------------
# Android
AndroidBuild {
diff --git a/QGCCommon.pri b/QGCCommon.pri
index 7d3cdfb463685a99aea61dbb850f33918546b9eb..37309504f4cb1249919a183658a0e27593ea3543 100644
--- a/QGCCommon.pri
+++ b/QGCCommon.pri
@@ -54,10 +54,13 @@ linux {
error("Unsupported Mac toolchain, only 64-bit LLVM+clang is supported")
}
} else : ios {
+ !equals(QT_MAJOR_VERSION, 5) | !greaterThan(QT_MINOR_VERSION, 4) {
+ error("Unsupported Qt version, 5.5.x or greater is required for iOS")
+ }
message("iOS build")
CONFIG += iOSBuild MobileBuild app_bundle
DEFINES += __ios__
- warning("iOS build is experimental and not yet functional")
+ warning("iOS build is experimental and not yet fully functional")
} else {
error("Unsupported build platform, only Linux, Windows, Android and Mac (Mac OS and iOS) are supported")
}
@@ -107,10 +110,10 @@ iOSBuild {
# For whatever reason, the iOS build fails with these set. Some files have the full,
# properly concatenaded path and file name while others have only the second portion,
# as if BUILDDIR was empty.
- OBJECTS_DIR = ~/tmp/qgcfoo
- MOC_DIR = ~/tmp/qgcfoo
- UI_DIR = ~/tmp/qgcfoo
- RCC_DIR = ~/tmp/qgcfoo
+ OBJECTS_DIR = $$(HOME)/tmp/qgcfoo
+ MOC_DIR = $$(HOME)/tmp/qgcfoo
+ UI_DIR = $$(HOME)/tmp/qgcfoo
+ RCC_DIR = $$(HOME)/tmp/qgcfoo
} else {
OBJECTS_DIR = $${BUILDDIR}/obj
MOC_DIR = $${BUILDDIR}/moc
@@ -126,7 +129,7 @@ AndroidBuild {
# We place the created plugin lib into the objects dir so that make clean will clean it as well
iOSBuild {
- LOCATION_PLUGIN_DESTDIR = ~/tmp/qgcfoo
+ LOCATION_PLUGIN_DESTDIR = $$(HOME)/tmp/qgcfoo
} else {
LOCATION_PLUGIN_DESTDIR = $$OBJECTS_DIR
}
diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index 3ca86dddac11e7c94f626281c8bf1bf95239956e..90180708d5dadb91f3d3e64b75d09ab7ad0b5ad8 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -3,24 +3,20 @@
src/comm/MockLink.params
src/FactSystem/FactSystemTest.qml
-
src/AutoPilotPlugins/PX4/Images/SafetyComponentTree.svg
src/AutoPilotPlugins/PX4/Images/SafetyComponentHome.png
src/AutoPilotPlugins/PX4/Images/SafetyComponentArrowDown.png
src/AutoPilotPlugins/PX4/Images/SafetyComponentPlane.png
-
src/AutoPilotPlugins/PX4/Images/VehicleDown.png
src/AutoPilotPlugins/PX4/Images/VehicleUpsideDown.png
src/AutoPilotPlugins/PX4/Images/VehicleLeft.png
src/AutoPilotPlugins/PX4/Images/VehicleRight.png
src/AutoPilotPlugins/PX4/Images/VehicleNoseDown.png
src/AutoPilotPlugins/PX4/Images/VehicleTailDown.png
-
src/AutoPilotPlugins/PX4/Images/VehicleDownRotate.png
src/AutoPilotPlugins/PX4/Images/VehicleLeftRotate.png
src/AutoPilotPlugins/PX4/Images/VehicleNoseDownRotate.png
-
src/AutoPilotPlugins/PX4/Images/AirframeStandardPlane.png
src/AutoPilotPlugins/PX4/Images/AirframeFlyingWing.png
src/AutoPilotPlugins/PX4/Images/AirframeQuadRotorX.png
@@ -31,7 +27,6 @@
src/AutoPilotPlugins/PX4/Images/AirframeHexaRotorPlus.png
src/AutoPilotPlugins/PX4/Images/AirframeQuadRotorH.png
src/AutoPilotPlugins/PX4/Images/AirframeSimulation.png
-
src/QmlControls/arrow-down.png
resources/CogWheels.png
src/AutoPilotPlugins/PX4/Images/SensorsComponentIcon.png
@@ -42,14 +37,12 @@
src/AutoPilotPlugins/PX4/Images/PowerComponentIcon.png
src/VehicleSetup/FirmwareUpgradeIcon.png
src/VehicleSetup/VehicleSummaryIcon.png
-
src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_01cell.svg
src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_02cell.svg
src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_03cell.svg
src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_04cell.svg
src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_05cell.svg
src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_06cell.svg
-
src/ui/qmlcommon/attitudeDial.svg
src/ui/qmlcommon/attitudeInstrument.svg
src/ui/qmlcommon/attitudePointer.svg
@@ -67,18 +60,15 @@
src/ui/qmlcommon/scale.png
src/ui/qmlcommon/scale_end.png
-
src/test.qml
src/QmlControls/QmlTest.qml
-
src/FactSystem/FactControls/qmldir
src/FactSystem/FactControls/FactPanel.qml
src/FactSystem/FactControls/FactLabel.qml
src/FactSystem/FactControls/FactTextField.qml
src/FactSystem/FactControls/FactCheckBox.qml
src/FactSystem/FactControls/FactComboBox.qml
-
src/QmlControls/QGroundControl.Controls.qmldir
src/QmlControls/QGCButton.qml
src/QmlControls/QGCRadioButton.qml
@@ -89,32 +79,25 @@
src/QmlControls/QGCColoredImage.qml
src/QmlControls/QGCToolBarButton.qml
src/QmlControls/QGCMovableItem.qml
-
src/QmlControls/QGroundControl.ScreenTools.qmldir
src/QmlControls/ScreenTools.qml
src/QmlControls/ScreenToolsFontQuery.qml
-
src/QmlControls/SubMenuButton.qml
src/QmlControls/IndicatorButton.qml
src/QmlControls/VehicleRotationCal.qml
src/QmlControls/VehicleSummaryRow.qml
src/ViewWidgets/ViewWidget.qml
-
src/QmlControls/QGCView.qml
src/QmlControls/QGCViewPanel.qml
src/QmlControls/QGCViewDialog.qml
src/QmlControls/QGCViewMessage.qml
-
src/QmlControls/ParameterEditor.qml
src/QmlControls/ParameterEditorDialog.qml
-
src/ViewWidgets/ParameterEditorWidget.qml
src/ViewWidgets/CustomCommandWidget.qml
-
src/VehicleSetup/SetupView.qml
src/VehicleSetup/SetupViewButtonsConnected.qml
src/VehicleSetup/SetupViewButtonsDisconnected.qml
-
src/VehicleSetup/VehicleSummary.qml
src/VehicleSetup/FirmwareUpgrade.qml
src/VehicleSetup/SetupParameterEditor.qml
@@ -130,14 +113,10 @@
src/AutoPilotPlugins/PX4/RadioComponentSummary.qml
src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml
src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml
-
-
src/AutoPilotPlugins/PX4/AirframeComponent.qml
-
src/ui/toolbar/MainToolBar.qml
src/ui/flightdisplay/FlightDisplay.qml
src/ui/mapdisplay/MapDisplay.qml
-
src/ui/qmlcommon/QGCAltitudeWidget.qml
src/ui/qmlcommon/QGCArtificialHorizon.qml
src/ui/qmlcommon/QGCAttitudeInstrument.qml
@@ -152,16 +131,14 @@
src/ui/qmlcommon/QGCPitchWidget.qml
src/ui/qmlcommon/QGCSlider.qml
src/ui/qmlcommon/QGCSpeedWidget.qml
+ src/ui/qmlcommon/QGCVideoBackground.qml
src/ui/qmlcommon/QGCWaypointEditor.qml
src/ui/qmlcommon/qmldir
-
src/ui/qmlcommon/QGCWaypoint.qml
-
src/AutoPilotPlugins/PX4/ParameterFactMetaData.xml
-
resources/LeftArrow.svg
resources/RightArrow.svg
@@ -169,45 +146,35 @@
resources/UpArrow.svg
resources/BottomArrow.svg
resources/JumpArrow.svg
-
resources/PlusSign.svg
resources/MinusSign.svg
-
resources/Play.svg
resources/Pause.svg
resources/Stop.svg
-
resources/Launch.svg
resources/Land.svg
resources/Kill.svg
resources/Shutdown.svg
-
resources/Antenna_T.svg
resources/Antenna_RC.svg
resources/Gps.svg
resources/Megaphone.png
resources/Yield.png
-
resources/Battery_0.svg
resources/Battery_20.svg
resources/Battery_40.svg
resources/Battery_60.svg
resources/Battery_80.svg
resources/Battery_100.svg
-
resources/SystemLockScreen.svg
-
resources/SplashScreen.png
-
resources/QGroundControlConnect.svg
-
resources/firmware/px4.png
resources/firmware/apm.png
resources/firmware/3drradio.png
-
resources/mavs/helicopter.svg
resources/mavs/unknown.svg
@@ -229,14 +196,12 @@
resources/mavs/surface-boat.svg
resources/mavs/tricopter.svg
-
resources/mapproviders/openstreetmap.png
resources/mapproviders/google.png
resources/mapproviders/yahoo.png
resources/mapproviders/googleearth.svg
-
resources/calibration/accel_back.png
resources/calibration/accel_front.png
@@ -245,7 +210,6 @@
resources/calibration/accel_up.png
resources/calibration/accel_left.png
-
resources/calibration/mode1/radioCenter.png
resources/calibration/mode1/radioHome.png
@@ -259,7 +223,6 @@
resources/calibration/mode1/radioThrottleDown.png
resources/calibration/mode1/radioSwitchMinMax.png
-
resources/calibration/mode2/radioCenter.png
resources/calibration/mode2/radioHome.png
@@ -273,16 +236,13 @@
resources/calibration/mode2/radioThrottleDown.png
resources/calibration/mode2/radioSwitchMinMax.png
-
resources/styles/style-dark.css
resources/styles/style-light.css
-
resources/styles/Vera.ttf
-
resources/audio/alert.wav
diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc
index 180cb971bcf5b9095ff8641ab6597179ee210fea..f6da705d3072fafc77e9d9810925ca330c9ad550 100644
--- a/src/QGCApplication.cc
+++ b/src/QGCApplication.cc
@@ -40,6 +40,14 @@
#include
+#include
+#include
+#if defined(QGC_GST_STREAMING)
+G_BEGIN_DECLS
+GST_PLUGIN_STATIC_DECLARE(QTVIDEOSINK_NAME);
+G_END_DECLS
+#endif
+
#include "configuration.h"
#include "QGC.h"
#include "QGCApplication.h"
@@ -255,11 +263,28 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
settings.clear();
settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
}
+
+ //----------------------------------------------------------------
+ //-- Video Streaming
+ qmlRegisterType("QGroundControl.QgcQtGStreamer", 1, 0, "VideoItem");
+ qmlRegisterUncreatableType("QGroundControl.QgcQtGStreamer", 1, 0, "VideoSurface", QLatin1String("VideoSurface from QML is not supported"));
+#if defined(QGC_GST_STREAMING)
+ GError* error = NULL;
+ if (!gst_init_check(&argc, &argv, &error)) {
+ qCritical() << "gst_init_check() failed: " << error->message;
+ g_error_free(error);
+ }
+ GST_PLUGIN_STATIC_REGISTER(QTVIDEOSINK_NAME);
+#endif
+
}
QGCApplication::~QGCApplication()
{
_destroySingletons();
+#if defined(QGC_GST_STREAMING)
+ gst_deinit();
+#endif
}
void QGCApplication::_initCommon(void)
diff --git a/src/VideoStreaming/README.md b/src/VideoStreaming/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a570fe0518d477f720ef51e83c51f6b01ca0a5ea
--- /dev/null
+++ b/src/VideoStreaming/README.md
@@ -0,0 +1,8 @@
+# QGroundControl
+
+## Video Streaming
+
+For supported platforms, QGroundControl implements an UDP RTP video streaming receiver in its Main Flight Display. It used gstreamer and a stripped down version of QtGstreamer.
+To build video streaming support, you will need to install the GStreamer development packages.
+
+TODO: Complete this once the PR is through.
diff --git a/src/VideoStreaming/VideoItem.cc b/src/VideoStreaming/VideoItem.cc
new file mode 100644
index 0000000000000000000000000000000000000000..22dcc74e6a4c64527da74c0e695047101b698d96
--- /dev/null
+++ b/src/VideoStreaming/VideoItem.cc
@@ -0,0 +1,134 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief QGC Video Item
+ * @author Gus Grubba
+ */
+
+#include
+#include
+#include
+
+#include "VideoItem.h"
+#if defined(QGC_GST_STREAMING)
+#include "VideoSurface_p.h"
+#endif
+
+#if defined(QGC_GST_STREAMING)
+struct VideoItem::Private
+{
+ QPointer 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 NULL;
+#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)
+QSGNode* VideoItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData*)
+{
+ QRectF r = boundingRect();
+ QSGNode* newNode = 0;
+
+ if (_data->surfaceDirty) {
+ delete oldNode;
+ oldNode = 0;
+ _data->surfaceDirty = false;
+ }
+
+ if (!_data->surface || _data->surface.data()->_data->videoSink == NULL) {
+ if (!oldNode) {
+ QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
+ material->setColor(Qt::black);
+ QSGGeometryNode *node = new QSGGeometryNode;
+ node->setMaterial(material);
+ node->setFlag(QSGNode::OwnsMaterial);
+ node->setFlag(QSGNode::OwnsGeometry);
+ newNode = node;
+ _data->targetArea = QRectF(); //force geometry to be set
+ } else {
+ newNode = oldNode;
+ }
+ if (r != _data->targetArea) {
+ QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
+ geometry->vertexDataAsPoint2D()[0].set(r.x(), r.y());
+ geometry->vertexDataAsPoint2D()[1].set(r.x(), r.height());
+ geometry->vertexDataAsPoint2D()[2].set(r.width(), r.y());
+ geometry->vertexDataAsPoint2D()[3].set(r.width(), r.height());
+ QSGGeometryNode *node = static_cast(newNode);
+ node->setGeometry(geometry);
+ _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);
+ }
+
+ return newNode;
+}
+#endif
diff --git a/src/VideoStreaming/VideoItem.h b/src/VideoStreaming/VideoItem.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef7399a9268d7f1d98498797408d54302940b447
--- /dev/null
+++ b/src/VideoStreaming/VideoItem.h
@@ -0,0 +1,62 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief QGC Video Item
+ * @author Gus Grubba
+ */
+
+#ifndef VIDEO_ITEM_H
+#define VIDEO_ITEM_H
+
+#include
+#include "VideoSurface.h"
+
+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)
+ struct Private;
+ Private* const _data;
+#endif
+};
+
+#endif // VIDEO_ITEM_H
diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc
new file mode 100644
index 0000000000000000000000000000000000000000..23030afe51a79d35510bd724027fa6e4a9eef1fd
--- /dev/null
+++ b/src/VideoStreaming/VideoReceiver.cc
@@ -0,0 +1,221 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief QGC Video Receiver
+ * @author Gus Grubba
+ */
+
+#include "VideoReceiver.h"
+#include
+
+VideoReceiver::VideoReceiver(QObject* parent)
+ : QObject(parent)
+#if defined(QGC_GST_STREAMING)
+ , _pipeline(NULL)
+ , _videoSink(NULL)
+#endif
+{
+
+}
+
+VideoReceiver::~VideoReceiver()
+{
+#if defined(QGC_GST_STREAMING)
+ stop();
+ setVideoSink(NULL);
+#endif
+}
+
+#if defined(QGC_GST_STREAMING)
+void VideoReceiver::setVideoSink(GstElement* sink)
+{
+ if (_videoSink) {
+ gst_object_unref(_videoSink);
+ _videoSink = NULL;
+ }
+ if (sink) {
+ _videoSink = sink;
+ gst_object_ref_sink(_videoSink);
+ }
+}
+#endif
+
+void VideoReceiver::start()
+{
+#if defined(QGC_GST_STREAMING)
+ if (_uri.isEmpty()) {
+ qCritical() << "VideoReceiver::start() failed because URI is not specified";
+ return;
+ }
+
+ if (_videoSink == NULL) {
+ qCritical() << "VideoReceiver::start() failed because video sink is not set";
+ return;
+ }
+
+ stop();
+
+ bool running = false;
+
+ GstElement* dataSource = NULL;
+ GstCaps* caps = NULL;
+ GstElement* demux = NULL;
+ GstElement* parser = NULL;
+ GstElement* decoder = NULL;
+
+ do {
+ if ((_pipeline = gst_pipeline_new("receiver")) == NULL) {
+ break;
+ }
+
+ if ((dataSource = gst_element_factory_make("udpsrc", "udp-source")) == NULL) {
+ break;
+ }
+
+ if ((caps = gst_caps_from_string("application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264")) == NULL) {
+ break;
+ }
+
+ g_object_set(G_OBJECT(dataSource), "uri", qPrintable(_uri), "caps", caps, NULL);
+
+ if ((demux = gst_element_factory_make("rtph264depay", "rtp-h264-depacketizer")) == NULL) {
+ break;
+ }
+
+ if ((parser = gst_element_factory_make("h264parse", "h264-parser")) == NULL) {
+ break;
+ }
+
+ if ((decoder = gst_element_factory_make("avdec_h264", "h264-decoder")) == NULL) {
+ break;
+ }
+
+ gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, decoder, _videoSink, NULL);
+
+ if (gst_element_link_many(dataSource, demux, parser, decoder, _videoSink, NULL) != (gboolean)TRUE) {
+ break;
+ }
+
+ dataSource = demux = parser = decoder = NULL;
+
+ GstBus* bus = NULL;
+
+ if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) {
+ gst_bus_add_watch(bus, _onBusMessage, this);
+ gst_object_unref(bus);
+ bus = NULL;
+ }
+
+ running = gst_element_set_state(_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE;
+
+ } while(0);
+
+ if (caps != NULL) {
+ gst_caps_unref(caps);
+ caps = NULL;
+ }
+
+ if (!running) {
+ qCritical() << "VideoReceiver::start() failed";
+
+ if (decoder != NULL) {
+ gst_object_unref(decoder);
+ decoder = NULL;
+ }
+
+ if (parser != NULL) {
+ gst_object_unref(parser);
+ parser = NULL;
+ }
+
+ if (demux != NULL) {
+ gst_object_unref(demux);
+ demux = NULL;
+ }
+
+ if (dataSource != NULL) {
+ gst_object_unref(dataSource);
+ dataSource = NULL;
+ }
+
+ if (_pipeline != NULL) {
+ gst_object_unref(_pipeline);
+ _pipeline = NULL;
+ }
+ }
+#endif
+}
+
+void VideoReceiver::stop()
+{
+#if defined(QGC_GST_STREAMING)
+ if (_pipeline != NULL) {
+ gst_element_set_state(_pipeline, GST_STATE_NULL);
+ gst_object_unref(_pipeline);
+ _pipeline = NULL;
+ }
+#endif
+}
+
+void VideoReceiver::setUri(const QString & uri)
+{
+ stop();
+ _uri = uri;
+}
+
+#if defined(QGC_GST_STREAMING)
+void VideoReceiver::_onBusMessage(GstMessage* msg)
+{
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_EOS:
+ stop();
+ break;
+ case GST_MESSAGE_ERROR:
+ do {
+ gchar* debug;
+ GError* error;
+ gst_message_parse_error(msg, &error, &debug);
+ g_free(debug);
+ qCritical() << error->message;
+ g_error_free(error);
+ } while(0);
+ stop();
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+#if defined(QGC_GST_STREAMING)
+gboolean VideoReceiver::_onBusMessage(GstBus* bus, GstMessage* msg, gpointer data)
+{
+ Q_UNUSED(bus)
+ Q_ASSERT(msg != NULL && data != NULL);
+ VideoReceiver* pThis = (VideoReceiver*)data;
+ pThis->_onBusMessage(msg);
+ return TRUE;
+}
+#endif
diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2fd35fc3d08250d011b61d3aa330317df08c978
--- /dev/null
+++ b/src/VideoStreaming/VideoReceiver.h
@@ -0,0 +1,70 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief QGC Video Receiver
+ * @author Gus Grubba
+ */
+
+#ifndef VIDEORECEIVER_H
+#define VIDEORECEIVER_H
+
+#include
+#if defined(QGC_GST_STREAMING)
+#include
+#endif
+
+class VideoReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ explicit VideoReceiver(QObject* parent = 0);
+ ~VideoReceiver();
+
+#if defined(QGC_GST_STREAMING)
+ void setVideoSink(GstElement* sink);
+#endif
+
+public Q_SLOTS:
+ void start ();
+ void stop ();
+ void setUri (const QString& uri);
+
+private:
+
+#if defined(QGC_GST_STREAMING)
+ void _onBusMessage(GstMessage* message);
+ static gboolean _onBusMessage(GstBus* bus, GstMessage* msg, gpointer data);
+#endif
+
+ QString _uri;
+
+#if defined(QGC_GST_STREAMING)
+ GstElement* _pipeline;
+ GstElement* _videoSink;
+#endif
+
+};
+
+#endif // VIDEORECEIVER_H
diff --git a/src/VideoStreaming/VideoStreaming.pri b/src/VideoStreaming/VideoStreaming.pri
new file mode 100644
index 0000000000000000000000000000000000000000..368c9db110d82a988c3ca51f150b2ef6abbd0556
--- /dev/null
+++ b/src/VideoStreaming/VideoStreaming.pri
@@ -0,0 +1,128 @@
+# -------------------------------------------------
+# QGroundControl - Micro Air Vehicle Groundstation
+# Please see our website at
+# Maintainer:
+# Lorenz Meier
+# (c) 2009-2015 QGroundControl Developers
+#
+# This file is part of the open groundstation project
+# QGroundControl is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# QGroundControl 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with QGroundControl. If not, see .
+#
+# Author: Gus Grubba
+# -------------------------------------------------
+
+#
+#-- Depends on gstreamer, which can be found at: http://gstreamer.freedesktop.org/download/
+#
+
+LinuxBuild {
+ CONFIG += link_pkgconfig
+ packagesExist(gstreamer-1.0) {
+ message("Including support for video streaming")
+ PKGCONFIG += gstreamer-1.0 gstreamer-video-1.0
+ CONFIG += VideoEnabled
+ }
+} else:MacBuild {
+ #- gstreamer framework installed by the gstreamer devel installer
+ GST_ROOT = /Library/Frameworks/GStreamer.framework
+ exists($$GST_ROOT) {
+ message("Including support for video streaming")
+ CONFIG += VideoEnabled
+ INCLUDEPATH += $$GST_ROOT/Headers
+ LIBS += -F/Library/Frameworks -framework GStreamer
+ }
+} else:iOSBuild {
+ #- gstreamer framework installed by the gstreamer iOS SDK installer (default to home directory)
+ GST_ROOT = $$(HOME)/Library/Developer/GStreamer/iPhone.sdk/GStreamer.framework
+ exists($$GST_ROOT) {
+ message("Including support for video streaming")
+ CONFIG += VideoEnabled
+ INCLUDEPATH += $$GST_ROOT/Headers
+ LIBS += -F$$(HOME)/Library/Developer/GStreamer/iPhone.sdk -framework GStreamer
+ }
+} else:WindowsBuild {
+ #- gstreamer installed by default under c:/gstreamer
+ GST_ROOT = c:/gstreamer/1.0/x86
+ exists($$GST_ROOT) {
+ message("Including support for video streaming")
+ CONFIG += VideoEnabled
+ LIBS += -L$$GST_ROOT/lib/gstreamer-1.0/static -lgstreamer-1.0 -lgstvideo-1.0 -lgstbase-1.0
+ LIBS += -L$$GST_ROOT/lib -lglib-2.0 -lintl -lgobject-2.0
+ INCLUDEPATH += \
+ $$GST_ROOT/include/gstreamer-1.0 \
+ $$GST_ROOT/include/glib-2.0 \
+ $$GST_ROOT/lib/gstreamer-1.0\include \
+ $$GST_ROOT/lib/glib-2.0/include
+ }
+}
+
+VideoEnabled {
+
+ DEFINES += \
+ QGC_GST_STREAMING \
+ GST_PLUGIN_BUILD_STATIC \
+ QTGLVIDEOSINK_NAME=qt5glvideosink \
+ QTVIDEOSINK_NAME=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 \
+
+ 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 \
+
+} else {
+ message("Skipping support for video streaming (Unsupported platform)")
+}
+
diff --git a/src/VideoStreaming/VideoSurface.cc b/src/VideoStreaming/VideoSurface.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9202f198421d101200cd413c37cb613704f38658
--- /dev/null
+++ b/src/VideoStreaming/VideoSurface.cc
@@ -0,0 +1,83 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief QGC Video Surface
+ * @author Gus Grubba
+ */
+
+#if defined(QGC_GST_STREAMING)
+#include "VideoSurface_p.h"
+#endif
+#include "VideoSurface.h"
+
+#include
+#include
+
+VideoSurface::VideoSurface(QObject *parent)
+ : QObject(parent)
+#if defined(QGC_GST_STREAMING)
+ , _data(new VideoSurfacePrivate)
+#endif
+{
+}
+
+VideoSurface::~VideoSurface()
+{
+#if defined(QGC_GST_STREAMING)
+ if (_data->videoSink != NULL) {
+ gst_element_set_state(_data->videoSink, GST_STATE_NULL);
+ }
+ delete _data;
+#endif
+}
+
+#if defined(QGC_GST_STREAMING)
+GstElement* VideoSurface::videoSink() const
+{
+ if (_data->videoSink == NULL) {
+ if ((_data->videoSink = gst_element_factory_make("qtquick2videosink", NULL)) == NULL) {
+ qCritical("Failed to create qtquick2videosink. Make sure it is installed correctly");
+ return NULL;
+ }
+ g_signal_connect(_data->videoSink, "update", G_CALLBACK(onUpdateThunk), (void* )this);
+ }
+ return _data->videoSink;
+}
+
+void VideoSurface::onUpdate()
+{
+ 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
+
diff --git a/src/VideoStreaming/VideoSurface.h b/src/VideoStreaming/VideoSurface.h
new file mode 100644
index 0000000000000000000000000000000000000000..deb140ad7b5489b2be177fc2eb786355159ba10b
--- /dev/null
+++ b/src/VideoStreaming/VideoSurface.h
@@ -0,0 +1,74 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief QGC Video Surface
+ * @author Gus Grubba
+ */
+
+#ifndef VIDEO_SURFACE_H
+#define VIDEO_SURFACE_H
+
+#include
+
+#if defined(QGC_GST_STREAMING)
+#include
+#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() const;
+#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;
+#endif
+};
+
+Q_DECLARE_METATYPE(VideoSurface*)
+
+#endif // VIDEO_SURFACE_H
diff --git a/src/VideoStreaming/VideoSurface_p.h b/src/VideoStreaming/VideoSurface_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4dc49632b3b1f5878aa04f092ea23f3501e0a66
--- /dev/null
+++ b/src/VideoStreaming/VideoSurface_p.h
@@ -0,0 +1,47 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief QGC Video Surface (Private Interface)
+ * @author Gus Grubba
+ */
+
+#ifndef VIDEO_SURFACE_P_H
+#define VIDEO_SURFACE_P_H
+
+#include "VideoSurface.h"
+#include "VideoItem.h"
+
+class VideoSurfacePrivate
+{
+public:
+ VideoSurfacePrivate()
+ : videoSink(NULL)
+ {
+ }
+ QSet items;
+ GstElement* videoSink;
+};
+
+#endif // VIDEO_SURFACE_P_H
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/basedelegate.cpp b/src/VideoStreaming/gstqtvideosink/delegates/basedelegate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ee927536c2686c7d2956452d34288379d4694a34
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/basedelegate.cpp
@@ -0,0 +1,203 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "basedelegate.h"
+
+#include
+
+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(NULL)
+ , 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(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(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, NULL);
+ update();
+
+ return true;
+ }
+ default:
+ return QObject::event(event);
+ }
+}
+
+void BaseDelegate::update()
+{
+ g_signal_emit_by_name(m_sink, "update");
+}
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/basedelegate.h b/src/VideoStreaming/gstqtvideosink/delegates/basedelegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1662860a36e42fc25dc4d320974ac087fdee70f
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/basedelegate.h
@@ -0,0 +1,155 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef BASEDELEGATE_H
+#define BASEDELEGATE_H
+
+#include
+
+#include "../gstqtvideosinkplugin.h" //for debug category
+#include "../utils/bufferformat.h"
+#include "../utils/utils.h"
+
+#include
+#include
+#include
+
+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(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(BufferFormatEventType)),
+ format(format)
+ {}
+
+ BufferFormat format;
+ };
+
+ class DeactivateEvent : public QEvent
+ {
+ public:
+ inline DeactivateEvent()
+ : QEvent(static_cast(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;
+};
+
+#endif // BASEDELEGATE_H
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp b/src/VideoStreaming/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c3eaed7c8d581ee57b4a334e98b5557821bb8589
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp
@@ -0,0 +1,108 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#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(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;
+}
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/qtquick2videosinkdelegate.h b/src/VideoStreaming/gstqtvideosink/delegates/qtquick2videosinkdelegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..f7f892f1f01b8b3c20d2f453c825db6a8b6c8bf8
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/qtquick2videosinkdelegate.h
@@ -0,0 +1,38 @@
+/*
+ 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef QTQUICK2VIDEOSINKDELEGATE_H
+#define QTQUICK2VIDEOSINKDELEGATE_H
+
+#include "basedelegate.h"
+#include
+
+class QtQuick2VideoSinkDelegate : public BaseDelegate
+{
+ Q_OBJECT
+public:
+ explicit QtQuick2VideoSinkDelegate(GstElement * sink, QObject * parent = 0);
+
+ QSGNode *updateNode(QSGNode *node, const QRectF & targetArea);
+};
+
+#endif // QTQUICK2VIDEOSINKDELEGATE_H
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/qtvideosinkdelegate.cpp b/src/VideoStreaming/gstqtvideosink/delegates/qtvideosinkdelegate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f03fefc3e630086b4e1e5a1bdb09abaaec9f5447
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/qtvideosinkdelegate.cpp
@@ -0,0 +1,245 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "qtvideosinkdelegate.h"
+#include "../painters/genericsurfacepainter.h"
+#include "../painters/openglsurfacepainter.h"
+
+#include
+#include
+
+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();
+
+ const QByteArray extensions(reinterpret_cast(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 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"), (NULL));
+}
+
+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);
+}
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/qtvideosinkdelegate.h b/src/VideoStreaming/gstqtvideosink/delegates/qtvideosinkdelegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..538988451ddc5bc58e7bc9409fa5e1344eda07b0
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/qtvideosinkdelegate.h
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef QT_VIDEO_SINK_DELEGATE_H
+#define QT_VIDEO_SINK_DELEGATE_H
+
+#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)
+
+#endif // QT_VIDEO_SINK_DELEGATE_H
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/qwidgetvideosinkdelegate.cpp b/src/VideoStreaming/gstqtvideosink/delegates/qwidgetvideosinkdelegate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f8318cf79e38e3b6e2106b71297a633794926c6
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/qwidgetvideosinkdelegate.cpp
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 2010 George Kiagiadakis
+ Copyright (C) 2012 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "qwidgetvideosinkdelegate.h"
+#include
+
+QWidgetVideoSinkDelegate::QWidgetVideoSinkDelegate(GstElement * sink, QObject * parent)
+ : QtVideoSinkDelegate(sink, parent)
+{
+
+}
+
+QWidgetVideoSinkDelegate::~QWidgetVideoSinkDelegate()
+{
+ setWidget(NULL);
+}
+
+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 = NULL;
+ }
+
+ 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();
+ }
+}
diff --git a/src/VideoStreaming/gstqtvideosink/delegates/qwidgetvideosinkdelegate.h b/src/VideoStreaming/gstqtvideosink/delegates/qwidgetvideosinkdelegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..373bf3c9b8e0f58af0ec63c8f0dd3d06bfc889e7
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/delegates/qwidgetvideosinkdelegate.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2010 George Kiagiadakis
+ Copyright (C) 2012 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef QWIDGET_VIDEO_SINK_DELEGATE_H
+#define QWIDGET_VIDEO_SINK_DELEGATE_H
+
+#include "qtvideosinkdelegate.h"
+#include
+#include
+#include
+
+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 m_widget;
+
+ // original value of the Qt::WA_OpaquePaintEvent attribute
+ bool m_opaquePaintEventAttribute;
+};
+
+#endif // QWIDGET_VIDEO_SINK_DELEGATE_H
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtglvideosink.cpp b/src/VideoStreaming/gstqtvideosink/gstqtglvideosink.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2af4d8a8a45af0d89293f15b0bccea0813c215bc
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtglvideosink.cpp
@@ -0,0 +1,167 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+/*
+ * 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 ");
+}
+
+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(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 emited.
+ *
+ * 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(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+ G_STRUCT_OFFSET(GstQtGLVideoSinkClass, paint),
+ NULL, NULL,
+ 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 emited 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, NULL, NULL,
+ 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(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(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(painter),
+ QRectF(x, y, width, height));
+}
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtglvideosink.h b/src/VideoStreaming/gstqtvideosink/gstqtglvideosink.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d7e859b34d439055cef5b6276d95c56941b7762
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtglvideosink.h
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef GST_QT_GL_VIDEO_SINK_H
+#define GST_QT_GL_VIDEO_SINK_H
+
+#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
+#endif // GST_QT_GL_VIDEO_SINK_H
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtglvideosinkbase.cpp b/src/VideoStreaming/gstqtvideosink/gstqtglvideosinkbase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ee9605e38a7e968804c84bc89501b10151fe5cd9
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtglvideosinkbase.cpp
@@ -0,0 +1,268 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "gstqtglvideosinkbase.h"
+#include "painters/openglsurfacepainter.h"
+#include "delegates/qtvideosinkdelegate.h"
+#include
+
+#define CAPS_FORMATS "{ BGRA, BGRx, ARGB, xRGB, RGB, RGB16, BGR, v308, AYUV, YV12, I420 }"
+
+const char * const GstQtGLVideoSinkBase::s_colorbalance_labels[] = {
+ "contrast", "brightness", "hue", "saturation"
+};
+
+GstQtVideoSinkBaseClass *GstQtGLVideoSinkBase::s_parent_class = 0;
+
+//------------------------------
+
+DEFINE_TYPE_WITH_CODE(GstQtGLVideoSinkBase, GST_TYPE_QT_VIDEO_SINK_BASE, init_interfaces)
+
+void GstQtGLVideoSinkBase::init_interfaces(GType type)
+{
+ static const GInterfaceInfo colorbalance_info = {
+ (GInterfaceInitFunc) &GstQtGLVideoSinkBase::colorbalance_init, NULL, NULL
+ };
+
+ g_type_add_interface_static(type, GST_TYPE_COLOR_BALANCE, &colorbalance_info);
+}
+
+//------------------------------
+
+void GstQtGLVideoSinkBase::base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+ element_class->padtemplates = NULL; //get rid of the pad template of the base 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 GstQtGLVideoSinkBase::class_init(gpointer g_class, gpointer class_data)
+{
+ Q_UNUSED(class_data);
+
+ s_parent_class = reinterpret_cast(g_type_class_peek_parent(g_class));
+
+ GObjectClass *object_class = G_OBJECT_CLASS(g_class);
+ object_class->finalize = GstQtGLVideoSinkBase::finalize;
+ object_class->set_property = GstQtGLVideoSinkBase::set_property;
+ object_class->get_property = GstQtGLVideoSinkBase::get_property;
+
+ GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(g_class);
+ base_sink_class->start = GstQtGLVideoSinkBase::start;
+ base_sink_class->set_caps = GstQtGLVideoSinkBase::set_caps;
+
+ g_object_class_install_property(object_class, PROP_CONTRAST,
+ g_param_spec_int("contrast", "Contrast", "The contrast of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+ g_object_class_install_property(object_class, PROP_BRIGHTNESS,
+ g_param_spec_int("brightness", "Brightness", "The brightness of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+ g_object_class_install_property(object_class, PROP_HUE,
+ g_param_spec_int("hue", "Hue", "The hue of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+ g_object_class_install_property(object_class, PROP_SATURATION,
+ g_param_spec_int("saturation", "Saturation", "The saturation of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+}
+
+void GstQtGLVideoSinkBase::init(GTypeInstance *instance, gpointer g_class)
+{
+ Q_UNUSED(g_class);
+ GstQtGLVideoSinkBase *self = GST_QT_GL_VIDEO_SINK_BASE(instance);
+
+ GstColorBalanceChannel *channel;
+ self->m_channels_list = NULL;
+
+ for (int i=0; i < LABEL_LAST; i++) {
+ channel = GST_COLOR_BALANCE_CHANNEL(g_object_new(GST_TYPE_COLOR_BALANCE_CHANNEL, NULL));
+ channel->label = g_strdup(s_colorbalance_labels[i]);
+ channel->min_value = -100;
+ channel->max_value = 100;
+
+ self->m_channels_list = g_list_append(self->m_channels_list, channel);
+ }
+}
+
+void GstQtGLVideoSinkBase::finalize(GObject *object)
+{
+ GstQtGLVideoSinkBase *self = GST_QT_GL_VIDEO_SINK_BASE(object);
+
+ while (self->m_channels_list) {
+ GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL(self->m_channels_list->data);
+ g_object_unref(channel);
+ self->m_channels_list = g_list_next(self->m_channels_list);
+ }
+
+ g_list_free(self->m_channels_list);
+
+ G_OBJECT_CLASS(s_parent_class)->finalize(object);
+}
+
+//------------------------------
+
+
+void GstQtGLVideoSinkBase::colorbalance_init(GstColorBalanceInterface *balance_interface, gpointer data)
+{
+ Q_UNUSED(data);
+ balance_interface->list_channels = GstQtGLVideoSinkBase::colorbalance_list_channels;
+ balance_interface->set_value = GstQtGLVideoSinkBase::colorbalance_set_value;
+ balance_interface->get_value = GstQtGLVideoSinkBase::colorbalance_get_value;
+ balance_interface->get_balance_type = GstQtGLVideoSinkBase::colorbalance_get_balance_type;
+}
+
+const GList *GstQtGLVideoSinkBase::colorbalance_list_channels(GstColorBalance *balance)
+{
+ return GST_QT_GL_VIDEO_SINK_BASE(balance)->m_channels_list;
+}
+
+void GstQtGLVideoSinkBase::colorbalance_set_value(GstColorBalance *balance,
+ GstColorBalanceChannel *channel, gint value)
+{
+ GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(balance);
+
+ if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
+ sink->delegate->setContrast(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
+ sink->delegate->setBrightness(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
+ sink->delegate->setHue(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
+ sink->delegate->setSaturation(value);
+ } else {
+ GST_WARNING_OBJECT(sink, "Unknown colorbalance channel %s", channel->label);
+ }
+}
+
+gint GstQtGLVideoSinkBase::colorbalance_get_value(GstColorBalance *balance,
+ GstColorBalanceChannel *channel)
+{
+ GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(balance);
+
+ if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
+ return sink->delegate->contrast();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
+ return sink->delegate->brightness();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
+ return sink->delegate->hue();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
+ return sink->delegate->saturation();
+ } else {
+ GST_WARNING_OBJECT(sink, "Unknown colorbalance channel %s", channel->label);
+ }
+
+ return 0;
+}
+
+GstColorBalanceType GstQtGLVideoSinkBase::colorbalance_get_balance_type(GstColorBalance *balance)
+{
+ Q_UNUSED(balance);
+ return GST_COLOR_BALANCE_HARDWARE;
+}
+
+//------------------------------
+
+void GstQtGLVideoSinkBase::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_CONTRAST:
+ sink->delegate->setContrast(g_value_get_int(value));
+ break;
+ case PROP_BRIGHTNESS:
+ sink->delegate->setBrightness(g_value_get_int(value));
+ break;
+ case PROP_HUE:
+ sink->delegate->setHue(g_value_get_int(value));
+ break;
+ case PROP_SATURATION:
+ sink->delegate->setSaturation(g_value_get_int(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+void GstQtGLVideoSinkBase::get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
+
+ switch (prop_id) {
+ case PROP_CONTRAST:
+ g_value_set_int(value, sink->delegate->contrast());
+ break;
+ case PROP_BRIGHTNESS:
+ g_value_set_int(value, sink->delegate->brightness());
+ break;
+ case PROP_HUE:
+ g_value_set_int(value, sink->delegate->hue());
+ break;
+ case PROP_SATURATION:
+ g_value_set_int(value, sink->delegate->saturation());
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+//------------------------------
+
+gboolean GstQtGLVideoSinkBase::start(GstBaseSink *base)
+{
+ GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(base);
+
+ //fail on purpose if the user hasn't set a context
+ if (sink->delegate->supportedPainterTypes() == QtVideoSinkDelegate::Generic) {
+ GST_WARNING_OBJECT(sink, "Neither GLSL nor ARB Fragment Program are supported "
+ "for painting. Did you forget to set a gl context?");
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+gboolean GstQtGLVideoSinkBase::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 (OpenGLSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
+ QCoreApplication::postEvent(sink->delegate,
+ new BaseDelegate::BufferFormatEvent(format));
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtglvideosinkbase.h b/src/VideoStreaming/gstqtvideosink/gstqtglvideosinkbase.h
new file mode 100644
index 0000000000000000000000000000000000000000..26ad69bf74df2662834c49c5d0161033121f3225
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtglvideosinkbase.h
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef GST_QT_GL_VIDEO_SINK_BASE_H
+#define GST_QT_GL_VIDEO_SINK_BASE_H
+
+#include "gstqtvideosinkbase.h"
+#include
+
+#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
+#endif // GST_QT_GL_VIDEO_SINK_BASE_H
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtquick2videosink.cpp b/src/VideoStreaming/gstqtvideosink/gstqtquick2videosink.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eedfd06999b161a2ac318257196b7033615a36da
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtquick2videosink.cpp
@@ -0,0 +1,459 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "gstqtquick2videosink.h"
+#include "gstqtvideosinkplugin.h"
+#include "gstqtvideosinkmarshal.h"
+#include "delegates/qtquick2videosinkdelegate.h"
+
+#include
+
+#include
+#include
+
+#define CAPS_FORMATS "{ BGRA, BGRx, ARGB, xRGB, RGB, RGB16, BGR, v308, AYUV, YV12, I420 }"
+
+#define GST_QT_QUICK2_VIDEO_SINK_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSinkPrivate))
+
+struct _GstQtQuick2VideoSinkPrivate
+{
+ QtQuick2VideoSinkDelegate *delegate;
+ GList *channels_list;
+};
+
+static void gst_qt_quick2_video_sink_colorbalance_init (GstColorBalanceInterface * iface, gpointer data);
+
+#define parent_class gst_qt_quick2_video_sink_parent_class
+G_DEFINE_TYPE_WITH_CODE (GstQtQuick2VideoSink, gst_qt_quick2_video_sink,
+ GST_TYPE_VIDEO_SINK,
+ G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
+ gst_qt_quick2_video_sink_colorbalance_init));
+
+enum {
+ PROP_0,
+ PROP_PIXEL_ASPECT_RATIO,
+ PROP_FORCE_ASPECT_RATIO,
+ PROP_CONTRAST,
+ PROP_BRIGHTNESS,
+ PROP_HUE,
+ PROP_SATURATION,
+};
+
+enum {
+ ACTION_UPDATE_NODE,
+ SIGNAL_UPDATE,
+ LAST_SIGNAL
+};
+
+static guint s_signals[LAST_SIGNAL] = { 0 };
+
+const char * const s_colorbalance_labels[] = {
+ "contrast", "brightness", "hue", "saturation"
+};
+
+//index for s_colorbalance_labels
+enum {
+ LABEL_CONTRAST = 0,
+ LABEL_BRIGHTNESS,
+ LABEL_HUE,
+ LABEL_SATURATION,
+ LABEL_LAST
+};
+
+static void
+gst_qt_quick2_video_sink_init (GstQtQuick2VideoSink *self)
+{
+ self->priv = GST_QT_QUICK2_VIDEO_SINK_GET_PRIVATE (self);
+
+ // delegate
+ self->priv->delegate = new QtQuick2VideoSinkDelegate(GST_ELEMENT(self));
+
+ // colorbalance
+ GstColorBalanceChannel *channel;
+ self->priv->channels_list = NULL;
+
+ for (int i=0; i < LABEL_LAST; i++) {
+ channel = GST_COLOR_BALANCE_CHANNEL(g_object_new(GST_TYPE_COLOR_BALANCE_CHANNEL, NULL));
+ channel->label = g_strdup(s_colorbalance_labels[i]);
+ channel->min_value = -100;
+ channel->max_value = 100;
+
+ self->priv->channels_list = g_list_append(self->priv->channels_list, channel);
+ }
+}
+
+static void
+gst_qt_quick2_video_sink_finalize (GObject *gobject)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (gobject);
+
+ delete self->priv->delegate;
+ self->priv->delegate = 0;
+
+ while (self->priv->channels_list) {
+ GstColorBalanceChannel *channel =
+ GST_COLOR_BALANCE_CHANNEL(self->priv->channels_list->data);
+ g_object_unref(channel);
+ self->priv->channels_list = g_list_next(self->priv->channels_list);
+ }
+
+ g_list_free(self->priv->channels_list);
+
+ G_OBJECT_CLASS (parent_class)->finalize (gobject);
+}
+
+static void
+gst_qt_quick2_video_sink_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (object);
+
+ switch (property_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);
+ self->priv->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:
+ self->priv->delegate->setForceAspectRatio(g_value_get_boolean(value));
+ break;
+ case PROP_CONTRAST:
+ self->priv->delegate->setContrast(g_value_get_int(value));
+ break;
+ case PROP_BRIGHTNESS:
+ self->priv->delegate->setBrightness(g_value_get_int(value));
+ break;
+ case PROP_HUE:
+ self->priv->delegate->setHue(g_value_get_int(value));
+ break;
+ case PROP_SATURATION:
+ self->priv->delegate->setSaturation(g_value_get_int(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_qt_quick2_video_sink_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (object);
+
+ switch (property_id) {
+ case PROP_PIXEL_ASPECT_RATIO:
+ {
+ GValue tmp;
+ Fraction par = self->priv->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, self->priv->delegate->forceAspectRatio());
+ break;
+ case PROP_CONTRAST:
+ g_value_set_int(value, self->priv->delegate->contrast());
+ break;
+ case PROP_BRIGHTNESS:
+ g_value_set_int(value, self->priv->delegate->brightness());
+ break;
+ case PROP_HUE:
+ g_value_set_int(value, self->priv->delegate->hue());
+ break;
+ case PROP_SATURATION:
+ g_value_set_int(value, self->priv->delegate->saturation());
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn
+gst_qt_quick2_video_sink_change_state(GstElement *element,
+ GstStateChange transition)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ self->priv->delegate->setActive(true);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ self->priv->delegate->setActive(false);
+ break;
+ default:
+ break;
+ }
+
+ return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+}
+
+static gboolean
+gst_qt_quick2_video_sink_set_caps(GstBaseSink *sink, GstCaps *caps)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (sink);
+
+ GST_LOG_OBJECT(self, "new caps %" GST_PTR_FORMAT, caps);
+ BufferFormat format = BufferFormat::fromCaps(caps);
+
+ //too lazy to do proper checks. if the format is not UNKNOWN, then
+ //it should conform to the template caps formats, unless gstreamer
+ //core has a bug.
+ if (format.videoFormat() != GST_VIDEO_FORMAT_UNKNOWN) {
+ QCoreApplication::postEvent(self->priv->delegate,
+ new BaseDelegate::BufferFormatEvent(format));
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static GstFlowReturn
+gst_qt_quick2_video_sink_show_frame(GstVideoSink *sink, GstBuffer *buffer)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (sink);
+
+ GST_TRACE_OBJECT(self, "Posting new buffer (%" GST_PTR_FORMAT ") for rendering.", buffer);
+
+ QCoreApplication::postEvent(self->priv->delegate, new BaseDelegate::BufferEvent(buffer));
+
+ return GST_FLOW_OK;
+}
+
+//------------------------------
+
+static gpointer
+gst_qt_quick2_video_sink_update_node(GstQtQuick2VideoSink *self, gpointer node,
+ qreal x, qreal y, qreal w, qreal h)
+{
+ return self->priv->delegate->updateNode(static_cast(node),
+ QRectF(x, y, w, h));
+}
+
+//------------------------------
+
+static const GList *
+gst_qt_quick2_video_sink_colorbalance_list_channels(GstColorBalance *balance)
+{
+ return GST_QT_QUICK2_VIDEO_SINK (balance)->priv->channels_list;
+}
+
+static void
+gst_qt_quick2_video_sink_colorbalance_set_value(GstColorBalance *balance,
+ GstColorBalanceChannel *channel, gint value)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (balance);
+
+ if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
+ self->priv->delegate->setContrast(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
+ self->priv->delegate->setBrightness(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
+ self->priv->delegate->setHue(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
+ self->priv->delegate->setSaturation(value);
+ } else {
+ GST_WARNING_OBJECT(self, "Unknown colorbalance channel %s", channel->label);
+ }
+}
+
+static gint
+gst_qt_quick2_video_sink_colorbalance_get_value(GstColorBalance *balance,
+ GstColorBalanceChannel *channel)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (balance);
+
+ if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
+ return self->priv->delegate->contrast();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
+ return self->priv->delegate->brightness();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
+ return self->priv->delegate->hue();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
+ return self->priv->delegate->saturation();
+ } else {
+ GST_WARNING_OBJECT(self, "Unknown colorbalance channel %s", channel->label);
+ }
+
+ return 0;
+}
+
+static GstColorBalanceType
+gst_qt_quick2_video_sink_colorbalance_get_balance_type (GstColorBalance * balance)
+{
+ Q_UNUSED(balance);
+ return GST_COLOR_BALANCE_HARDWARE;
+}
+
+static void
+gst_qt_quick2_video_sink_colorbalance_init(GstColorBalanceInterface *iface, gpointer data)
+{
+ Q_UNUSED(data);
+ iface->list_channels = gst_qt_quick2_video_sink_colorbalance_list_channels;
+ iface->set_value = gst_qt_quick2_video_sink_colorbalance_set_value;
+ iface->get_value = gst_qt_quick2_video_sink_colorbalance_get_value;
+ iface->get_balance_type = gst_qt_quick2_video_sink_colorbalance_get_balance_type;
+}
+
+//------------------------------
+
+static void
+gst_qt_quick2_video_sink_class_init (GstQtQuick2VideoSinkClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = gst_qt_quick2_video_sink_finalize;
+ gobject_class->set_property = gst_qt_quick2_video_sink_set_property;
+ gobject_class->get_property = gst_qt_quick2_video_sink_get_property;
+
+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+ element_class->change_state = gst_qt_quick2_video_sink_change_state;
+
+ GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(klass);
+ base_sink_class->set_caps = gst_qt_quick2_video_sink_set_caps;
+
+ GstVideoSinkClass *video_sink_class = GST_VIDEO_SINK_CLASS(klass);
+ video_sink_class->show_frame = gst_qt_quick2_video_sink_show_frame;
+
+ GstQtQuick2VideoSinkClass *qtquick2_class = GST_QT_QUICK2_VIDEO_SINK_CLASS(klass);
+ qtquick2_class->update_node = gst_qt_quick2_video_sink_update_node;
+
+ /**
+ * GstQtQuick2VideoSink::pixel-aspect-ratio
+ *
+ * The pixel aspect ratio of the display device.
+ **/
+ g_object_class_install_property(gobject_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(G_PARAM_READWRITE)));
+
+ /**
+ * GstQtQuick2VideoSink::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(gobject_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(G_PARAM_READWRITE)));
+
+ g_object_class_install_property(gobject_class, PROP_CONTRAST,
+ g_param_spec_int("contrast", "Contrast", "The contrast of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+
+ g_object_class_install_property(gobject_class, PROP_BRIGHTNESS,
+ g_param_spec_int("brightness", "Brightness", "The brightness of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+
+ g_object_class_install_property(gobject_class, PROP_HUE,
+ g_param_spec_int("hue", "Hue", "The hue of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+
+ g_object_class_install_property(gobject_class, PROP_SATURATION,
+ g_param_spec_int("saturation", "Saturation", "The saturation of the video",
+ -100, 100, 0, static_cast(G_PARAM_READWRITE)));
+
+
+ /**
+ * GstQtQuick2VideoSink::update-node
+ * @node: The QSGNode to update
+ * @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
+ * @returns: The updated QGSNode
+ *
+ * This is an action signal that you can call from your QQuickItem subclass
+ * inside its updateNode function to render the video. It takes a QSGNode*
+ * and the item's area rectangle as arguments. You should schedule to call
+ * this function to repaint the surface whenever the ::update signal is
+ * emited.
+ *
+ * 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[ACTION_UPDATE_NODE] =
+ g_signal_new("update-node", G_TYPE_FROM_CLASS(klass),
+ static_cast(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+ G_STRUCT_OFFSET(GstQtQuick2VideoSinkClass, update_node),
+ NULL, NULL,
+ qRealIsDouble() ?
+ g_cclosure_user_marshal_POINTER__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE :
+ g_cclosure_user_marshal_POINTER__POINTER_FLOAT_FLOAT_FLOAT_FLOAT,
+ G_TYPE_POINTER, 5,
+ G_TYPE_POINTER, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL);
+
+ /**
+ * GstQtQuick2VideoSink::update
+ *
+ * This signal is emited when the surface should be repainted. It should
+ * be connected to QQuickItem::update().
+ */
+ s_signals[SIGNAL_UPDATE] =
+ g_signal_new("update", G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (klass, sizeof (GstQtQuick2VideoSinkPrivate));
+
+ 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));
+
+ gst_element_class_set_details_simple(element_class,
+ "QtQuick2 video sink", "Sink/Video",
+ "A video sink that can draw on a QQuickItem",
+ "George Kiagiadakis ");
+}
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtquick2videosink.h b/src/VideoStreaming/gstqtvideosink/gstqtquick2videosink.h
new file mode 100644
index 0000000000000000000000000000000000000000..2fedfa8c9c6571cf42a0b8ffecdfd7c53aeb3023
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtquick2videosink.h
@@ -0,0 +1,63 @@
+/*
+ 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef __GST_QT_QUICK2_VIDEO_SINK_H__
+#define __GST_QT_QUICK2_VIDEO_SINK_H__
+
+#include
+#include
+
+#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);
+
+#endif /* __GST_QT_QUICK2_VIDEO_SINK_H__ */
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosink.cpp b/src/VideoStreaming/gstqtvideosink/gstqtvideosink.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b491745cd119f001af5b9374953408725b8dc183
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosink.cpp
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#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 ");
+}
+
+void GstQtVideoSink::class_init(gpointer g_class, gpointer class_data)
+{
+ Q_UNUSED(class_data);
+
+ GstQtVideoSinkClass *qt_video_sink_class = reinterpret_cast(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 emited.
+ *
+ * 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(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+ G_STRUCT_OFFSET(GstQtVideoSinkClass, paint),
+ NULL, NULL,
+ 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 emited 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, NULL, NULL,
+ 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(painter),
+ QRectF(x, y, width, height));
+}
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosink.h b/src/VideoStreaming/gstqtvideosink/gstqtvideosink.h
new file mode 100644
index 0000000000000000000000000000000000000000..46e90f9c39b7b831e5c49e3e40b57cf9133155fb
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosink.h
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef GST_QT_VIDEO_SINK_H
+#define GST_QT_VIDEO_SINK_H
+
+#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);
+};
+
+#endif
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkbase.cpp b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkbase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..12dc844e6a7ed6ae714e76e3407289fe85c2309a
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkbase.cpp
@@ -0,0 +1,221 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "gstqtvideosinkbase.h"
+#include "delegates/qtvideosinkdelegate.h"
+#include "painters/genericsurfacepainter.h"
+#include
+#include
+
+#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 = NULL;
+
+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(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(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(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;
+}
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkbase.h b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkbase.h
new file mode 100644
index 0000000000000000000000000000000000000000..3806c64dbb91389d420380a2a6f139f0211f8ec5
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkbase.h
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef GST_QT_VIDEO_SINK_BASE_H
+#define GST_QT_VIDEO_SINK_BASE_H
+
+#include "gstqtvideosinkplugin.h"
+#include
+
+#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;
+};
+
+#endif
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkmarshal.c b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkmarshal.c
new file mode 100644
index 0000000000000000000000000000000000000000..3ea35fc8088d3b586017a2781102b74aed8debba
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkmarshal.c
@@ -0,0 +1,234 @@
+
+#include
+
+#if defined(_MSC_VER)
+#pragma warning(disable: 4100)
+#endif
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_schar (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:POINTER,FLOAT,FLOAT,FLOAT,FLOAT (marshaller.src:1) */
+void
+g_cclosure_user_marshal_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT) (gpointer data1,
+ gpointer arg_1,
+ gfloat arg_2,
+ gfloat arg_3,
+ gfloat arg_4,
+ gfloat arg_5,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 6);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_float (param_values + 2),
+ g_marshal_value_peek_float (param_values + 3),
+ g_marshal_value_peek_float (param_values + 4),
+ g_marshal_value_peek_float (param_values + 5),
+ data2);
+}
+
+/* VOID:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE (marshaller.src:2) */
+void
+g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE) (gpointer data1,
+ gpointer arg_1,
+ gdouble arg_2,
+ gdouble arg_3,
+ gdouble arg_4,
+ gdouble arg_5,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 6);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ g_marshal_value_peek_double (param_values + 3),
+ g_marshal_value_peek_double (param_values + 4),
+ g_marshal_value_peek_double (param_values + 5),
+ data2);
+}
+
+/* POINTER:POINTER,FLOAT,FLOAT,FLOAT,FLOAT (marshaller.src:3) */
+void
+g_cclosure_user_marshal_POINTER__POINTER_FLOAT_FLOAT_FLOAT_FLOAT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gpointer (*GMarshalFunc_POINTER__POINTER_FLOAT_FLOAT_FLOAT_FLOAT) (gpointer data1,
+ gpointer arg_1,
+ gfloat arg_2,
+ gfloat arg_3,
+ gfloat arg_4,
+ gfloat arg_5,
+ gpointer data2);
+ register GMarshalFunc_POINTER__POINTER_FLOAT_FLOAT_FLOAT_FLOAT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gpointer v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 6);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_POINTER__POINTER_FLOAT_FLOAT_FLOAT_FLOAT) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_float (param_values + 2),
+ g_marshal_value_peek_float (param_values + 3),
+ g_marshal_value_peek_float (param_values + 4),
+ g_marshal_value_peek_float (param_values + 5),
+ data2);
+
+ g_value_set_pointer (return_value, v_return);
+}
+
+/* POINTER:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE (marshaller.src:4) */
+void
+g_cclosure_user_marshal_POINTER__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gpointer (*GMarshalFunc_POINTER__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE) (gpointer data1,
+ gpointer arg_1,
+ gdouble arg_2,
+ gdouble arg_3,
+ gdouble arg_4,
+ gdouble arg_5,
+ gpointer data2);
+ register GMarshalFunc_POINTER__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gpointer v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 6);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_POINTER__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ g_marshal_value_peek_double (param_values + 3),
+ g_marshal_value_peek_double (param_values + 4),
+ g_marshal_value_peek_double (param_values + 5),
+ data2);
+
+ g_value_set_pointer (return_value, v_return);
+}
+
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkmarshal.h b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkmarshal.h
new file mode 100644
index 0000000000000000000000000000000000000000..a906bf04e88f19f4ead25417f4df8a9cfbe1cac3
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkmarshal.h
@@ -0,0 +1,44 @@
+
+#ifndef __g_cclosure_user_marshal_MARSHAL_H__
+#define __g_cclosure_user_marshal_MARSHAL_H__
+
+#include
+
+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
+
+#endif /* __g_cclosure_user_marshal_MARSHAL_H__ */
+
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.cpp b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..43516234e511844be4ca278e58095ed362823bf4
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.cpp
@@ -0,0 +1,86 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#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(QTVIDEOSINK_NAME), 0,
+ "Debug category for GstQtVideoSink");
+
+ if(!gst_element_register(plugin, G_STRINGIFY(QTVIDEOSINK_NAME),
+ GST_RANK_NONE, GST_TYPE_QT_VIDEO_SINK)) {
+ GST_ERROR("Failed to register " G_STRINGIFY(QTVIDEOSINK_NAME));
+ 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,
+ QTVIDEOSINK_NAME,
+ "A video sink that can draw on any Qt surface",
+ plugin_init,
+ PACKAGE_VERSION,
+ "LGPL",
+ PACKAGE_NAME,
+ PACKAGE_ORIGIN
+)
diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.h b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.h
new file mode 100644
index 0000000000000000000000000000000000000000..170942bb340b5899553dbf0d323f4610598c1efe
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.h
@@ -0,0 +1,77 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef GST_QT_VIDEO_SINK_PLUGIN_H
+#define GST_QT_VIDEO_SINK_PLUGIN_H
+
+#include
+#include
+
+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
+
+
+#endif
diff --git a/src/VideoStreaming/gstqtvideosink/gstqwidgetvideosink.cpp b/src/VideoStreaming/gstqtvideosink/gstqwidgetvideosink.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6f7d7746e4f9dfa6dad1a054e2bbca538ffe5f22
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqwidgetvideosink.cpp
@@ -0,0 +1,104 @@
+/*
+ Copyright (C) 2010 George Kiagiadakis
+ Copyright (C) 2012 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#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 ");
+}
+
+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(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(sinkBase->delegate);
+
+ switch (prop_id) {
+ case PROP_WIDGET:
+ delegate->setWidget(static_cast(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(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;
+ }
+}
diff --git a/src/VideoStreaming/gstqtvideosink/gstqwidgetvideosink.h b/src/VideoStreaming/gstqtvideosink/gstqwidgetvideosink.h
new file mode 100644
index 0000000000000000000000000000000000000000..2b9e04530bea9a0ac3c0f9e515f3743aad8a0b4a
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/gstqwidgetvideosink.h
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2010 George Kiagiadakis
+ Copyright (C) 2012 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef GST_QWIDGET_VIDEO_SINK_H
+#define GST_QWIDGET_VIDEO_SINK_H
+
+#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;
+};
+
+#endif
diff --git a/src/VideoStreaming/gstqtvideosink/marshaller.src b/src/VideoStreaming/gstqtvideosink/marshaller.src
new file mode 100644
index 0000000000000000000000000000000000000000..8bc7d1be0e260e023a61961865c8e03a470adf22
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/marshaller.src
@@ -0,0 +1,4 @@
+VOID:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
+VOID:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
+POINTER:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
+POINTER:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
diff --git a/src/VideoStreaming/gstqtvideosink/painters/abstractsurfacepainter.h b/src/VideoStreaming/gstqtvideosink/painters/abstractsurfacepainter.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4b32e171e1cd4371e3494c4631675c6c9b988c4
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/abstractsurfacepainter.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef ABSTRACTSURFACEPAINTER_H
+#define ABSTRACTSURFACEPAINTER_H
+
+#include "../utils/bufferformat.h"
+#include
+
+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;
+};
+
+#endif
diff --git a/src/VideoStreaming/gstqtvideosink/painters/genericsurfacepainter.cpp b/src/VideoStreaming/gstqtvideosink/painters/genericsurfacepainter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..73653930d02914b691c13d11c4ae86758d1a9cae
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/genericsurfacepainter.cpp
@@ -0,0 +1,113 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "genericsurfacepainter.h"
+#include
+
+GenericSurfacePainter::GenericSurfacePainter()
+ : m_imageFormat(QImage::Format_Invalid)
+{
+}
+
+//static
+QSet GenericSurfacePainter::supportedPixelFormats()
+{
+ return QSet()
+#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)
+{
+}
diff --git a/src/VideoStreaming/gstqtvideosink/painters/genericsurfacepainter.h b/src/VideoStreaming/gstqtvideosink/painters/genericsurfacepainter.h
new file mode 100644
index 0000000000000000000000000000000000000000..8a05b6bf22648dddac89ba1f3d6ece106e4d97a1
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/genericsurfacepainter.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef GENERICSURFACEPAINTER_H
+#define GENERICSURFACEPAINTER_H
+
+#include "abstractsurfacepainter.h"
+#include
+#include
+
+/**
+ * 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 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;
+};
+
+#endif // GENERICSURFACEPAINTER_H
diff --git a/src/VideoStreaming/gstqtvideosink/painters/openglsurfacepainter.cpp b/src/VideoStreaming/gstqtvideosink/painters/openglsurfacepainter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0acbb4ab72cbcf08c1d2e23a5e18994fa1391e07
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/openglsurfacepainter.cpp
@@ -0,0 +1,806 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "openglsurfacepainter.h"
+#include
+
+#ifndef GL_TEXTURE0
+# define GL_TEXTURE0 0x84C0
+# define GL_TEXTURE1 0x84C1
+# define GL_TEXTURE2 0x84C2
+#endif
+
+#ifndef GL_PROGRAM_ERROR_STRING_ARB
+# define GL_PROGRAM_ERROR_STRING_ARB 0x8874
+#endif
+
+#ifndef GL_UNSIGNED_SHORT_5_6_5
+# define GL_UNSIGNED_SHORT_5_6_5 33635
+#endif
+
+#ifndef GL_CLAMP_TO_EDGE
+# define GL_CLAMP_TO_EDGE 0x812F
+#endif
+
+#define QRECT_TO_GLMATRIX(rect) \
+ { \
+ GLfloat(rect.left()) , GLfloat(rect.bottom() + 1), \
+ GLfloat(rect.right() + 1), GLfloat(rect.bottom() + 1), \
+ GLfloat(rect.left()) , GLfloat(rect.top()), \
+ GLfloat(rect.right() + 1), GLfloat(rect.top()) \
+ }
+
+OpenGLSurfacePainter::OpenGLSurfacePainter()
+ : m_textureFormat(0)
+ , m_textureInternalFormat(0)
+ , m_textureType(0)
+ , m_textureCount(0)
+ , m_videoColorMatrix(GST_VIDEO_COLOR_MATRIX_UNKNOWN)
+{
+#ifndef QT_OPENGL_ES
+ glActiveTexture = (_glActiveTexture) QGLContext::currentContext()->getProcAddress(
+ QLatin1String("glActiveTexture"));
+#endif
+}
+
+//static
+QSet OpenGLSurfacePainter::supportedPixelFormats()
+{
+ return QSet()
+ //also handled by the generic painter on LE
+ << GST_VIDEO_FORMAT_BGRA
+ << GST_VIDEO_FORMAT_BGRx
+
+ //also handled by the generic painter on BE
+ << GST_VIDEO_FORMAT_ARGB
+ << GST_VIDEO_FORMAT_xRGB
+
+ //also handled by the generic painter everywhere
+ << GST_VIDEO_FORMAT_RGB
+ << GST_VIDEO_FORMAT_RGB16
+
+ //not handled by the generic painter
+ << GST_VIDEO_FORMAT_BGR
+ << GST_VIDEO_FORMAT_v308
+ << GST_VIDEO_FORMAT_AYUV
+ << GST_VIDEO_FORMAT_YV12
+ << GST_VIDEO_FORMAT_I420
+ ;
+}
+
+void OpenGLSurfacePainter::updateColors(int brightness, int contrast, int hue, int saturation)
+{
+ const qreal b = brightness / 200.0;
+ const qreal c = contrast / 100.0 + 1.0;
+ const qreal h = hue / 100.0;
+ const qreal s = saturation / 100.0 + 1.0;
+
+ const qreal cosH = qCos(M_PI * h);
+ const qreal sinH = qSin(M_PI * h);
+
+ const qreal h11 = 0.787 * cosH - 0.213 * sinH + 0.213;
+ const qreal h21 = -0.213 * cosH + 0.143 * sinH + 0.213;
+ const qreal h31 = -0.213 * cosH - 0.787 * sinH + 0.213;
+
+ const qreal h12 = -0.715 * cosH - 0.715 * sinH + 0.715;
+ const qreal h22 = 0.285 * cosH + 0.140 * sinH + 0.715;
+ const qreal h32 = -0.715 * cosH + 0.715 * sinH + 0.715;
+
+ const qreal h13 = -0.072 * cosH + 0.928 * sinH + 0.072;
+ const qreal h23 = -0.072 * cosH - 0.283 * sinH + 0.072;
+ const qreal h33 = 0.928 * cosH + 0.072 * sinH + 0.072;
+
+ const qreal sr = (1.0 - s) * 0.3086;
+ const qreal sg = (1.0 - s) * 0.6094;
+ const qreal sb = (1.0 - s) * 0.0820;
+
+ const qreal sr_s = sr + s;
+ const qreal sg_s = sg + s;
+ const qreal sb_s = sr + s;
+
+ const float m4 = (s + sr + sg + sb) * (0.5 - 0.5 * c + b);
+
+ m_colorMatrix(0, 0) = c * (sr_s * h11 + sg * h21 + sb * h31);
+ m_colorMatrix(0, 1) = c * (sr_s * h12 + sg * h22 + sb * h32);
+ m_colorMatrix(0, 2) = c * (sr_s * h13 + sg * h23 + sb * h33);
+ m_colorMatrix(0, 3) = m4;
+
+ m_colorMatrix(1, 0) = c * (sr * h11 + sg_s * h21 + sb * h31);
+ m_colorMatrix(1, 1) = c * (sr * h12 + sg_s * h22 + sb * h32);
+ m_colorMatrix(1, 2) = c * (sr * h13 + sg_s * h23 + sb * h33);
+ m_colorMatrix(1, 3) = m4;
+
+ m_colorMatrix(2, 0) = c * (sr * h11 + sg * h21 + sb_s * h31);
+ m_colorMatrix(2, 1) = c * (sr * h12 + sg * h22 + sb_s * h32);
+ m_colorMatrix(2, 2) = c * (sr * h13 + sg * h23 + sb_s * h33);
+ m_colorMatrix(2, 3) = m4;
+
+ m_colorMatrix(3, 0) = 0.0;
+ m_colorMatrix(3, 1) = 0.0;
+ m_colorMatrix(3, 2) = 0.0;
+ m_colorMatrix(3, 3) = 1.0;
+
+ switch (m_videoColorMatrix) {
+#if 0
+ //I have no idea what this is - it's not needed currently in this code
+ case BufferFormat::YCbCr_JPEG:
+ m_colorMatrix *= QMatrix4x4(
+ 1.0, 0.000, 1.402, -0.701,
+ 1.0, -0.344, -0.714, 0.529,
+ 1.0, 1.772, 0.000, -0.886,
+ 0.0, 0.000, 0.000, 1.0000);
+ break;
+#endif
+ case GST_VIDEO_COLOR_MATRIX_BT709:
+/*
+ * This is bogus (Gus Grubba 20150706)
+ m_colorMatrix *= QMatrix4x4(
+ 1.164, 0.000, 1.793, -0.5727,
+ 1.164, -0.534, -0.213, 0.3007,
+ 1.164, 2.115, 0.000, -1.1302,
+ 0.0, 0.000, 0.000, 1.0000);
+ break;
+*/
+ case GST_VIDEO_COLOR_MATRIX_BT601:
+ m_colorMatrix *= QMatrix4x4(
+ 1.164f, 0.000f, 1.596f, -0.8708f,
+ 1.164f, -0.392f, -0.813f, 0.5296f,
+ 1.164f, 2.017f, 0.000f, -1.081f,
+ 0.0f, 0.000f, 0.000f, 1.0000f);
+ break;
+ default:
+ break;
+ }
+}
+
+void OpenGLSurfacePainter::paint(quint8 *data,
+ const BufferFormat & /*frameFormat*/,
+ QPainter *painter,
+ const PaintAreas & areas)
+{
+ // if these are enabled, we need to reenable them after beginNativePainting()
+ // has been called, as they may get disabled
+ bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST);
+ bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
+
+ painter->beginNativePainting();
+
+ if (stencilTestEnabled)
+ glEnable(GL_STENCIL_TEST);
+ if (scissorTestEnabled)
+ glEnable(GL_SCISSOR_TEST);
+
+ const GLfloat vertexCoordArray[] = QRECT_TO_GLMATRIX(areas.videoArea);
+
+ const GLfloat txLeft = areas.sourceRect.left();
+ const GLfloat txRight = areas.sourceRect.right();
+ const GLfloat txTop = areas.sourceRect.top();
+ const GLfloat txBottom = areas.sourceRect.bottom();
+
+ const GLfloat textureCoordArray[] =
+ {
+ txLeft , txBottom,
+ txRight, txBottom,
+ txLeft , txTop,
+ txRight, txTop
+ };
+
+ for (int i = 0; i < m_textureCount; ++i) {
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[i]);
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ m_textureInternalFormat,
+ m_textureWidths[i],
+ m_textureHeights[i],
+ 0,
+ m_textureFormat,
+ m_textureType,
+ data + m_textureOffsets[i]);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ paintImpl(painter, vertexCoordArray, textureCoordArray);
+
+ painter->endNativePainting();
+ painter->fillRect(areas.blackArea1, Qt::black);
+ painter->fillRect(areas.blackArea2, Qt::black);
+}
+
+void OpenGLSurfacePainter::initRgbTextureInfo(
+ GLenum internalFormat, GLuint format, GLenum type, const QSize &size)
+{
+#ifndef QT_OPENGL_ES
+ //make sure we get 8 bits per component, at least on the desktop GL where we can
+ switch(internalFormat) {
+ case GL_RGBA:
+ internalFormat = GL_RGBA8;
+ break;
+ case GL_RGB:
+ internalFormat = GL_RGB8;
+ break;
+ default:
+ break;
+ }
+#endif
+
+ m_textureInternalFormat = internalFormat;
+ m_textureFormat = format;
+ m_textureType = type;
+ m_textureCount = 1;
+ m_textureWidths[0] = size.width();
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+}
+
+void OpenGLSurfacePainter::initYuv420PTextureInfo(const QSize &size)
+{
+ int bytesPerLine = (size.width() + 3) & ~3;
+ int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
+
+ m_textureInternalFormat = GL_LUMINANCE;
+ m_textureFormat = GL_LUMINANCE;
+ m_textureType = GL_UNSIGNED_BYTE;
+ m_textureCount = 3;
+ m_textureWidths[0] = bytesPerLine;
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+ m_textureWidths[1] = bytesPerLine2;
+ m_textureHeights[1] = size.height() / 2;
+ m_textureOffsets[1] = bytesPerLine * size.height();
+ m_textureWidths[2] = bytesPerLine2;
+ m_textureHeights[2] = size.height() / 2;
+ m_textureOffsets[2] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
+}
+
+void OpenGLSurfacePainter::initYv12TextureInfo(const QSize &size)
+{
+ int bytesPerLine = (size.width() + 3) & ~3;
+ int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
+
+ m_textureInternalFormat = GL_LUMINANCE;
+ m_textureFormat = GL_LUMINANCE;
+ m_textureType = GL_UNSIGNED_BYTE;
+ m_textureCount = 3;
+ m_textureWidths[0] = bytesPerLine;
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+ m_textureWidths[1] = bytesPerLine2;
+ m_textureHeights[1] = size.height() / 2;
+ m_textureOffsets[1] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
+ m_textureWidths[2] = bytesPerLine2;
+ m_textureHeights[2] = size.height() / 2;
+ m_textureOffsets[2] = bytesPerLine * size.height();
+}
+
+#ifndef QT_OPENGL_ES
+
+# ifndef GL_FRAGMENT_PROGRAM_ARB
+# define GL_FRAGMENT_PROGRAM_ARB 0x8804
+# define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
+# endif
+
+// Interprets the RGBA texture as in fact being BGRx and paints it.
+static const char *qt_arbfp_bgrxShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP bgrx;\n"
+ "TEX bgrx.xyz, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV bgrx.w, matrix[3].w;\n"
+ "DP4 result.color.x, bgrx.zyxw, matrix[0];\n"
+ "DP4 result.color.y, bgrx.zyxw, matrix[1];\n"
+ "DP4 result.color.z, bgrx.zyxw, matrix[2];\n"
+ "END";
+
+// Interprets the RGBA texture as in fact being BGRA and paints it.
+static const char *qt_arbfp_bgraShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP bgra;\n"
+ "TEX bgra, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV bgra.w, matrix[3].w;\n"
+ "DP4 result.color.x, bgra.zyxw, matrix[0];\n"
+ "DP4 result.color.y, bgra.zyxw, matrix[1];\n"
+ "DP4 result.color.z, bgra.zyxw, matrix[2];\n"
+ "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
+ "END";
+
+// Interprets the RGBA texture as in fact being xRGB and paints it.
+static const char *qt_arbfp_xrgbShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP xrgb;\n"
+ "TEX xrgb, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV xrgb.x, matrix[3].w;\n"
+ "DP4 result.color.x, xrgb.yzwx, matrix[0];\n"
+ "DP4 result.color.y, xrgb.yzwx, matrix[1];\n"
+ "DP4 result.color.z, xrgb.yzwx, matrix[2];\n"
+ "END";
+
+// Interprets the RGBA texture as in fact being ARGB and paints it.
+static const char *qt_arbfp_argbShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP argb;\n"
+ "TEX argb, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV argb.x, matrix[3].w;\n"
+ "DP4 result.color.x, argb.yzwx, matrix[0];\n"
+ "DP4 result.color.y, argb.yzwx, matrix[1];\n"
+ "DP4 result.color.z, argb.yzwx, matrix[2];\n"
+ "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
+ "END";
+
+// Paints RGB frames without doing any color channel flipping.
+static const char *qt_arbfp_rgbxShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP rgb;\n"
+ "TEX rgb.xyz, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV rgb.w, matrix[3].w;\n"
+ "DP4 result.color.x, rgb, matrix[0];\n"
+ "DP4 result.color.y, rgb, matrix[1];\n"
+ "DP4 result.color.z, rgb, matrix[2];\n"
+ "END";
+
+// Paints a YUV420P or YV12 frame.
+static const char *qt_arbfp_yuvPlanarShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP yuv;\n"
+ "TEX yuv.x, fragment.texcoord[0], texture[0], 2D;\n"
+ "TEX yuv.y, fragment.texcoord[0], texture[1], 2D;\n"
+ "TEX yuv.z, fragment.texcoord[0], texture[2], 2D;\n"
+ "MOV yuv.w, matrix[3].w;\n"
+ "DP4 result.color.x, yuv, matrix[0];\n"
+ "DP4 result.color.y, yuv, matrix[1];\n"
+ "DP4 result.color.z, yuv, matrix[2];\n"
+ "END";
+
+
+
+ArbFpSurfacePainter::ArbFpSurfacePainter()
+ : OpenGLSurfacePainter()
+ , m_programId(0)
+{
+ const QGLContext *context = QGLContext::currentContext();
+
+ glProgramStringARB = (_glProgramStringARB) context->getProcAddress(
+ QLatin1String("glProgramStringARB"));
+ glBindProgramARB = (_glBindProgramARB) context->getProcAddress(
+ QLatin1String("glBindProgramARB"));
+ glDeleteProgramsARB = (_glDeleteProgramsARB) context->getProcAddress(
+ QLatin1String("glDeleteProgramsARB"));
+ glGenProgramsARB = (_glGenProgramsARB) context->getProcAddress(
+ QLatin1String("glGenProgramsARB"));
+ glProgramLocalParameter4fARB = (_glProgramLocalParameter4fARB) context->getProcAddress(
+ QLatin1String("glProgramLocalParameter4fARB"));
+}
+
+void ArbFpSurfacePainter::init(const BufferFormat &format)
+{
+ Q_ASSERT(m_textureCount == 0);
+
+ const char *program = 0;
+
+ switch (format.videoFormat()) {
+ case GST_VIDEO_FORMAT_BGRx:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_bgrxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_xRGB:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_xrgbShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_BGRA:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_bgraShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_ARGB:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_argbShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_RGB:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_rgbxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_BGR:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_bgrxShaderProgram;
+ break;
+ //NOTE: unlike the other formats, this is endianness-dependent,
+ //but using GL_UNSIGNED_SHORT_5_6_5 ensures that it's handled correctly
+ case GST_VIDEO_FORMAT_RGB16:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
+ program = qt_arbfp_rgbxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_v308:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_rgbxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_AYUV:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_argbShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_YV12:
+ initYv12TextureInfo(format.frameSize());
+ program = qt_arbfp_yuvPlanarShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_I420:
+ initYuv420PTextureInfo(format.frameSize());
+ program = qt_arbfp_yuvPlanarShaderProgram;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ m_videoColorMatrix = format.colorMatrix();
+
+ glGenProgramsARB(1, &m_programId);
+
+ GLenum glError = glGetError();
+ if (glError != GL_NO_ERROR) {
+ throw QString("ARBfb Shader allocation error ") +
+ QString::number(static_cast(glError), 16);
+ } else {
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
+ glProgramStringARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ GL_PROGRAM_FORMAT_ASCII_ARB,
+ qstrlen(program),
+ reinterpret_cast(program));
+
+ if ((glError = glGetError()) != GL_NO_ERROR) {
+ const GLubyte* errorString = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
+
+ glDeleteProgramsARB(1, &m_programId);
+ m_textureCount = 0;
+ m_programId = 0;
+
+ throw QString("ARBfp Shader compile error ") +
+ QString::number(static_cast(glError), 16) +
+ reinterpret_cast(errorString);
+ } else {
+ glGenTextures(m_textureCount, m_textureIds);
+ }
+ }
+}
+
+void ArbFpSurfacePainter::cleanup()
+{
+ glDeleteTextures(m_textureCount, m_textureIds);
+ glDeleteProgramsARB(1, &m_programId);
+
+ m_textureCount = 0;
+ m_programId = 0;
+}
+
+void ArbFpSurfacePainter::paintImpl(const QPainter *painter,
+ const GLfloat *vertexCoordArray,
+ const GLfloat *textureCoordArray)
+{
+ Q_UNUSED(painter);
+
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
+
+ glProgramLocalParameter4fARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ 0,
+ m_colorMatrix(0, 0),
+ m_colorMatrix(0, 1),
+ m_colorMatrix(0, 2),
+ m_colorMatrix(0, 3));
+ glProgramLocalParameter4fARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ 1,
+ m_colorMatrix(1, 0),
+ m_colorMatrix(1, 1),
+ m_colorMatrix(1, 2),
+ m_colorMatrix(1, 3));
+ glProgramLocalParameter4fARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ 2,
+ m_colorMatrix(2, 0),
+ m_colorMatrix(2, 1),
+ m_colorMatrix(2, 2),
+ m_colorMatrix(2, 3));
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+
+ if (m_textureCount == 3) {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexCoordArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, textureCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+}
+
+#endif
+
+static const char *qt_glsl_vertexShaderProgram =
+ "attribute highp vec4 vertexCoordArray;\n"
+ "attribute highp vec2 textureCoordArray;\n"
+ "uniform highp mat4 positionMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_Position = positionMatrix * vertexCoordArray;\n"
+ " textureCoord = textureCoordArray;\n"
+ "}\n";
+
+// Interprets the RGBA texture as in fact being BGRx and paints it.
+static const char *qt_glsl_bgrxShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
+ " gl_FragColor = colorMatrix * color;\n"
+ "}\n";
+
+// Interprets the RGBA texture as in fact being BGRA and paints it.
+static const char *qt_glsl_bgraShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
+ " color = colorMatrix * color;\n"
+ " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n"
+ "}\n";
+
+// Interprets the RGBA texture as in fact being xRGB and paints it.
+static const char *qt_glsl_xrgbShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
+ " gl_FragColor = colorMatrix * color;\n"
+ "}\n";
+
+// Interprets the RGBA texture as in fact being ARGB and paints it.
+static const char *qt_glsl_argbShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
+ " color = colorMatrix * color;\n"
+ " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).r);\n"
+ "}\n";
+
+// Paints RGB frames without doing any color channel flipping.
+static const char *qt_glsl_rgbxShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).rgb, 1.0);\n"
+ " gl_FragColor = colorMatrix * color;\n"
+ "}\n";
+
+// Paints planar yuv frames.
+static const char *qt_glsl_yuvPlanarShaderProgram =
+ "uniform sampler2D texY;\n"
+ "uniform sampler2D texU;\n"
+ "uniform sampler2D texV;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(\n"
+ " texture2D(texY, textureCoord.st).r,\n"
+ " texture2D(texU, textureCoord.st).r,\n"
+ " texture2D(texV, textureCoord.st).r,\n"
+ " 1.0);\n"
+ " gl_FragColor = colorMatrix * color;\n"
+ "}\n";
+
+
+GlslSurfacePainter::GlslSurfacePainter()
+ : OpenGLSurfacePainter()
+{
+}
+
+void GlslSurfacePainter::init(const BufferFormat &format)
+{
+ Q_ASSERT(m_textureCount == 0);
+
+ const char *fragmentProgram = 0;
+
+ switch (format.videoFormat()) {
+ case GST_VIDEO_FORMAT_BGRx:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_bgrxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_xRGB:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_xrgbShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_BGRA:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_bgraShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_ARGB:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_argbShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_RGB:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_rgbxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_BGR:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_bgrxShaderProgram;
+ break;
+ //NOTE: unlike the other formats, this is endianness-dependent,
+ //but using GL_UNSIGNED_SHORT_5_6_5 ensures that it's handled correctly
+ case GST_VIDEO_FORMAT_RGB16:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
+ fragmentProgram = qt_glsl_rgbxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_v308:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_rgbxShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_AYUV:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_argbShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_YV12:
+ initYv12TextureInfo(format.frameSize());
+ fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
+ break;
+ case GST_VIDEO_FORMAT_I420:
+ initYuv420PTextureInfo(format.frameSize());
+ fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ m_videoColorMatrix = format.colorMatrix();
+
+ if (!m_program.addShaderFromSourceCode(QGLShader::Vertex, qt_glsl_vertexShaderProgram)) {
+ throw QString("Vertex shader compile error ") + m_program.log();
+ }
+
+ if (!m_program.addShaderFromSourceCode(QGLShader::Fragment, fragmentProgram)) {
+ throw QString("Shader compile error ") + m_program.log();
+ }
+
+ if(!m_program.link()) {
+ throw QString("Shader link error ") + m_program.log();
+ }
+
+ glGenTextures(m_textureCount, m_textureIds);
+}
+
+void GlslSurfacePainter::cleanup()
+{
+ glDeleteTextures(m_textureCount, m_textureIds);
+ m_program.removeAllShaders();
+
+ m_textureCount = 0;
+}
+
+void GlslSurfacePainter::paintImpl(const QPainter *painter,
+ const GLfloat *vertexCoordArray,
+ const GLfloat *textureCoordArray)
+{
+ const int deviceWidth = painter->device()->width();
+ const int deviceHeight = painter->device()->height();
+
+ const QTransform transform = painter->deviceTransform();
+
+ const GLfloat wfactor = 2.0 / deviceWidth;
+ const GLfloat hfactor = -2.0 / deviceHeight;
+
+ const GLfloat positionMatrix[4][4] =
+ {
+ {
+ /*(0,0)*/ GLfloat(wfactor * transform.m11() - transform.m13()),
+ /*(0,1)*/ GLfloat(hfactor * transform.m12() + transform.m13()),
+ /*(0,2)*/ 0.0,
+ /*(0,3)*/ GLfloat(transform.m13())
+ }, {
+ /*(1,0)*/ GLfloat(wfactor * transform.m21() - transform.m23()),
+ /*(1,1)*/ GLfloat(hfactor * transform.m22() + transform.m23()),
+ /*(1,2)*/ 0.0,
+ /*(1,3)*/ GLfloat(transform.m23())
+ }, {
+ /*(2,0)*/ 0.0,
+ /*(2,1)*/ 0.0,
+ /*(2,2)*/ -1.0,
+ /*(2,3)*/ 0.0
+ }, {
+ /*(3,0)*/ GLfloat(wfactor * transform.dx() - transform.m33()),
+ /*(3,1)*/ GLfloat(hfactor * transform.dy() + transform.m33()),
+ /*(3,2)*/ 0.0,
+ /*(3,3)*/ GLfloat(transform.m33())
+ }
+ };
+
+ m_program.bind();
+
+ m_program.enableAttributeArray("vertexCoordArray");
+ m_program.enableAttributeArray("textureCoordArray");
+ m_program.setAttributeArray("vertexCoordArray", vertexCoordArray, 2);
+ m_program.setAttributeArray("textureCoordArray", textureCoordArray, 2);
+ m_program.setUniformValue("positionMatrix", positionMatrix);
+
+ if (m_textureCount == 3) {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+ glActiveTexture(GL_TEXTURE0);
+
+ m_program.setUniformValue("texY", 0);
+ m_program.setUniformValue("texU", 1);
+ m_program.setUniformValue("texV", 2);
+ } else {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+
+ m_program.setUniformValue("texRgb", 0);
+ }
+ m_program.setUniformValue("colorMatrix", m_colorMatrix);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ m_program.release();
+}
diff --git a/src/VideoStreaming/gstqtvideosink/painters/openglsurfacepainter.h b/src/VideoStreaming/gstqtvideosink/painters/openglsurfacepainter.h
new file mode 100644
index 0000000000000000000000000000000000000000..2b60ca5ba2e0e2a3d340f34c8e806d497aed9d55
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/openglsurfacepainter.h
@@ -0,0 +1,142 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef OPENGLSURFACEPAINTER_H
+#define OPENGLSURFACEPAINTER_H
+
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+
+#include "abstractsurfacepainter.h"
+#include
+
+#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 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
+
+#endif // OPENGLSURFACEPAINTER_H
diff --git a/src/VideoStreaming/gstqtvideosink/painters/videomaterial.cpp b/src/VideoStreaming/gstqtvideosink/painters/videomaterial.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ed24f57dfb8ca7f5858244fa79cb1b4b479f539
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/videomaterial.cpp
@@ -0,0 +1,461 @@
+/*
+ Copyright (C) 2011-2013 Collabora Ltd.
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH
+
+ 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted (and fixed) from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "videomaterial.h"
+
+#include
+#include
+#include
+#include
+
+static const char * const qtvideosink_glsl_vertexShader =
+ "uniform highp mat4 qt_Matrix; \n"
+ "attribute highp vec4 qt_VertexPosition; \n"
+ "attribute highp vec2 qt_VertexTexCoord; \n"
+ "varying highp vec2 qt_TexCoord; \n"
+ "void main() { \n"
+ " qt_TexCoord = qt_VertexTexCoord; \n"
+ " gl_Position = qt_Matrix * qt_VertexPosition; \n"
+ "}";
+
+inline const char * qtvideosink_glsl_bgrxFragmentShader()
+{
+ return
+ "uniform sampler2D rgbTexture;\n"
+ "uniform lowp float opacity;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(rgbTexture, qt_TexCoord.st).bgr, 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+inline const char * qtvideosink_glsl_xrgbFragmentShader()
+{
+ return
+ "uniform sampler2D rgbTexture;\n"
+ "uniform lowp float opacity;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(rgbTexture, qt_TexCoord.st).gba, 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+inline const char * qtvideosink_glsl_rgbxFragmentShader()
+{
+ return
+ "uniform sampler2D rgbTexture;\n"
+ "uniform lowp float opacity;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(rgbTexture, qt_TexCoord.st).rgb, 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+inline const char * qtvideosink_glsl_yuvPlanarFragmentShader()
+{
+ return
+ "uniform sampler2D yTexture;\n"
+ "uniform sampler2D uTexture;\n"
+ "uniform sampler2D vTexture;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "uniform lowp float opacity;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(\n"
+ " texture2D(yTexture, qt_TexCoord.st).r,\n"
+ " texture2D(uTexture, qt_TexCoord.st).r,\n"
+ " texture2D(vTexture, qt_TexCoord.st).r,\n"
+ " 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+class VideoMaterialShader : public QSGMaterialShader
+{
+public:
+ virtual void updateState(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+ {
+ Q_UNUSED(oldMaterial);
+
+ VideoMaterial *material = static_cast(newMaterial);
+ if (m_id_rgbTexture > 0) {
+ program()->setUniformValue(m_id_rgbTexture, 0);
+ } else {
+ program()->setUniformValue(m_id_yTexture, 0);
+ program()->setUniformValue(m_id_uTexture, 1);
+ program()->setUniformValue(m_id_vTexture, 2);
+ }
+
+ if (state.isOpacityDirty()) {
+ material->setFlag(QSGMaterial::Blending,
+ qFuzzyCompare(state.opacity(), 1.0f) ? false : true);
+ program()->setUniformValue(m_id_opacity, GLfloat(state.opacity()));
+ }
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+
+ program()->setUniformValue(m_id_colorMatrix, material->m_colorMatrix);
+
+ material->bind();
+ }
+
+ virtual char const *const *attributeNames() const {
+ static const char *names[] = {
+ "qt_VertexPosition",
+ "qt_VertexTexCoord",
+ 0
+ };
+ return names;
+ }
+
+protected:
+ virtual void initialize() {
+ m_id_matrix = program()->uniformLocation("qt_Matrix");
+ m_id_rgbTexture = program()->uniformLocation("rgbTexture");
+ m_id_yTexture = program()->uniformLocation("yTexture");
+ m_id_uTexture = program()->uniformLocation("uTexture");
+ m_id_vTexture = program()->uniformLocation("vTexture");
+ m_id_colorMatrix = program()->uniformLocation("colorMatrix");
+ m_id_opacity = program()->uniformLocation("opacity");
+ }
+
+ virtual const char *vertexShader() const {
+ return qtvideosink_glsl_vertexShader;
+ }
+
+ int m_id_matrix;
+ int m_id_rgbTexture;
+ int m_id_yTexture;
+ int m_id_uTexture;
+ int m_id_vTexture;
+ int m_id_colorMatrix;
+ int m_id_opacity;
+};
+
+template
+class VideoMaterialShaderImpl : public VideoMaterialShader
+{
+protected:
+ virtual const char *fragmentShader() const {
+ return FragmentShader();
+ }
+};
+
+template
+class VideoMaterialImpl : public VideoMaterial
+{
+public:
+ virtual QSGMaterialType *type() const {
+ static QSGMaterialType theType;
+ return &theType;
+ }
+
+ virtual QSGMaterialShader *createShader() const {
+ return new VideoMaterialShaderImpl;
+ }
+};
+
+VideoMaterial *VideoMaterial::create(const BufferFormat & format)
+{
+ VideoMaterial *material = NULL;
+
+ switch (format.videoFormat()) {
+ // BGRx
+ case GST_VIDEO_FORMAT_BGRx:
+ case GST_VIDEO_FORMAT_BGRA:
+ material = new VideoMaterialImpl;
+ material->initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+ case GST_VIDEO_FORMAT_BGR:
+ material = new VideoMaterialImpl;
+ material->initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+
+ // xRGB
+ case GST_VIDEO_FORMAT_xRGB:
+ case GST_VIDEO_FORMAT_ARGB:
+ case GST_VIDEO_FORMAT_AYUV:
+ material = new VideoMaterialImpl;
+ material->initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+
+ // RGBx
+ case GST_VIDEO_FORMAT_RGB:
+ case GST_VIDEO_FORMAT_v308:
+ material = new VideoMaterialImpl;
+ material->initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+ case GST_VIDEO_FORMAT_RGB16:
+ material = new VideoMaterialImpl;
+ material->initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
+ break;
+
+ // YUV 420 planar
+ case GST_VIDEO_FORMAT_I420:
+ case GST_VIDEO_FORMAT_YV12:
+ material = new VideoMaterialImpl;
+ material->initYuv420PTextureInfo(
+ (format.videoFormat() == GST_VIDEO_FORMAT_YV12) /* uvSwapped */,
+ format.frameSize());
+ break;
+
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ material->init(format.colorMatrix());
+ return material;
+}
+
+VideoMaterial::VideoMaterial()
+ : m_frame(0)
+ , m_textureCount(0)
+ , m_textureFormat(0)
+ , m_textureInternalFormat(0)
+ , m_textureType(0)
+ , m_colorMatrixType(GST_VIDEO_COLOR_MATRIX_UNKNOWN)
+{
+ memset(m_textureIds, 0, sizeof(m_textureIds));
+ setFlag(Blending, false);
+}
+
+VideoMaterial::~VideoMaterial()
+{
+ if (!m_textureSize.isEmpty())
+ glDeleteTextures(m_textureCount, m_textureIds);
+ gst_buffer_replace(&m_frame, NULL);
+}
+
+int VideoMaterial::compare(const QSGMaterial *other) const
+{
+ const VideoMaterial *m = static_cast(other);
+ int d = m_textureIds[0] - m->m_textureIds[0];
+ if (d || m_textureCount == 1)
+ return d;
+ else if ((d = m_textureIds[1] - m->m_textureIds[1]) != 0)
+ return d;
+ else
+ return m_textureIds[2] - m->m_textureIds[2];
+}
+
+void VideoMaterial::initRgbTextureInfo(
+ GLenum internalFormat, GLuint format, GLenum type, const QSize &size)
+{
+#ifndef QT_OPENGL_ES
+ //make sure we get 8 bits per component, at least on the desktop GL where we can
+ switch(internalFormat) {
+ case GL_RGBA:
+ internalFormat = GL_RGBA8;
+ break;
+ case GL_RGB:
+ internalFormat = GL_RGB8;
+ break;
+ default:
+ break;
+ }
+#endif
+
+ m_textureInternalFormat = internalFormat;
+ m_textureFormat = format;
+ m_textureType = type;
+ m_textureCount = 1;
+ m_textureWidths[0] = size.width();
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+}
+
+void VideoMaterial::initYuv420PTextureInfo(bool uvSwapped, const QSize &size)
+{
+ int bytesPerLine = (size.width() + 3) & ~3;
+ int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
+
+ m_textureInternalFormat = GL_LUMINANCE;
+ m_textureFormat = GL_LUMINANCE;
+ m_textureType = GL_UNSIGNED_BYTE;
+ m_textureCount = 3;
+ m_textureWidths[0] = bytesPerLine;
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+ m_textureWidths[1] = bytesPerLine2;
+ m_textureHeights[1] = size.height() / 2;
+ m_textureOffsets[1] = bytesPerLine * size.height();
+ m_textureWidths[2] = bytesPerLine2;
+ m_textureHeights[2] = size.height() / 2;
+ m_textureOffsets[2] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
+
+ if (uvSwapped)
+ qSwap (m_textureOffsets[1], m_textureOffsets[2]);
+}
+
+void VideoMaterial::init(GstVideoColorMatrix colorMatrixType)
+{
+ glGenTextures(m_textureCount, m_textureIds);
+ m_colorMatrixType = colorMatrixType;
+ updateColors(0, 0, 0, 0);
+}
+
+void VideoMaterial::setCurrentFrame(GstBuffer *buffer)
+{
+ QMutexLocker lock(&m_frameMutex);
+ gst_buffer_replace(&m_frame, buffer);
+}
+
+void VideoMaterial::updateColors(int brightness, int contrast, int hue, int saturation)
+{
+ const qreal b = brightness / 200.0;
+ const qreal c = contrast / 100.0 + 1.0;
+ const qreal h = hue / 100.0;
+ const qreal s = saturation / 100.0 + 1.0;
+
+ const qreal cosH = qCos(M_PI * h);
+ const qreal sinH = qSin(M_PI * h);
+
+ const qreal h11 = 0.787 * cosH - 0.213 * sinH + 0.213;
+ const qreal h21 = -0.213 * cosH + 0.143 * sinH + 0.213;
+ const qreal h31 = -0.213 * cosH - 0.787 * sinH + 0.213;
+
+ const qreal h12 = -0.715 * cosH - 0.715 * sinH + 0.715;
+ const qreal h22 = 0.285 * cosH + 0.140 * sinH + 0.715;
+ const qreal h32 = -0.715 * cosH + 0.715 * sinH + 0.715;
+
+ const qreal h13 = -0.072 * cosH + 0.928 * sinH + 0.072;
+ const qreal h23 = -0.072 * cosH - 0.283 * sinH + 0.072;
+ const qreal h33 = 0.928 * cosH + 0.072 * sinH + 0.072;
+
+ const qreal sr = (1.0 - s) * 0.3086;
+ const qreal sg = (1.0 - s) * 0.6094;
+ const qreal sb = (1.0 - s) * 0.0820;
+
+ const qreal sr_s = sr + s;
+ const qreal sg_s = sg + s;
+ const qreal sb_s = sr + s;
+
+ const float m4 = (s + sr + sg + sb) * (0.5 - 0.5 * c + b);
+
+ m_colorMatrix(0, 0) = c * (sr_s * h11 + sg * h21 + sb * h31);
+ m_colorMatrix(0, 1) = c * (sr_s * h12 + sg * h22 + sb * h32);
+ m_colorMatrix(0, 2) = c * (sr_s * h13 + sg * h23 + sb * h33);
+ m_colorMatrix(0, 3) = m4;
+
+ m_colorMatrix(1, 0) = c * (sr * h11 + sg_s * h21 + sb * h31);
+ m_colorMatrix(1, 1) = c * (sr * h12 + sg_s * h22 + sb * h32);
+ m_colorMatrix(1, 2) = c * (sr * h13 + sg_s * h23 + sb * h33);
+ m_colorMatrix(1, 3) = m4;
+
+ m_colorMatrix(2, 0) = c * (sr * h11 + sg * h21 + sb_s * h31);
+ m_colorMatrix(2, 1) = c * (sr * h12 + sg * h22 + sb_s * h32);
+ m_colorMatrix(2, 2) = c * (sr * h13 + sg * h23 + sb_s * h33);
+ m_colorMatrix(2, 3) = m4;
+
+ m_colorMatrix(3, 0) = 0.0;
+ m_colorMatrix(3, 1) = 0.0;
+ m_colorMatrix(3, 2) = 0.0;
+ m_colorMatrix(3, 3) = 1.0;
+
+ switch (m_colorMatrixType) {
+ case GST_VIDEO_COLOR_MATRIX_BT709:
+/*
+ * This is bogus (Gus Grubba 20150706)
+ m_colorMatrix *= QMatrix4x4(
+ 1.164, 0.000, 1.793, -0.5727,
+ 1.164, -0.534, -0.213, 0.3007,
+ 1.164, 2.115, 0.000, -1.1302,
+ 0.0, 0.000, 0.000, 1.0000);
+ break;
+*/
+ case GST_VIDEO_COLOR_MATRIX_BT601:
+ m_colorMatrix *= QMatrix4x4(
+ 1.164f, 0.000f, 1.596f, -0.8708f,
+ 1.164f, -0.392f, -0.813f, 0.5296f,
+ 1.164f, 2.017f, 0.000f, -1.081f,
+ 0.0f, 0.000f, 0.000f, 1.0000f);
+ break;
+ default:
+ break;
+ }
+}
+
+void VideoMaterial::bind()
+{
+ QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
+ GstBuffer *frame = NULL;
+
+ m_frameMutex.lock();
+ if (m_frame)
+ frame = gst_buffer_ref(m_frame);
+ m_frameMutex.unlock();
+
+ if (frame) {
+ GstMapInfo info;
+ gst_buffer_map(frame, &info, GST_MAP_READ);
+ functions->glActiveTexture(GL_TEXTURE1);
+ bindTexture(1, info.data);
+ functions->glActiveTexture(GL_TEXTURE2);
+ bindTexture(2, info.data);
+ functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
+ bindTexture(0, info.data);
+ gst_buffer_unmap(frame, &info);
+ gst_buffer_unref(frame);
+ } else {
+ functions->glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
+ functions->glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+ functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+ }
+}
+
+void VideoMaterial::bindTexture(int i, const quint8 *data)
+{
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[i]);
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ m_textureInternalFormat,
+ m_textureWidths[i],
+ m_textureHeights[i],
+ 0,
+ m_textureFormat,
+ m_textureType,
+ data + m_textureOffsets[i]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
diff --git a/src/VideoStreaming/gstqtvideosink/painters/videomaterial.h b/src/VideoStreaming/gstqtvideosink/painters/videomaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff42565013d9ff00c2a25c99429a4ff89e7e2036
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/videomaterial.h
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH
+ 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef VIDEOMATERIAL_H
+#define VIDEOMATERIAL_H
+
+#include "../utils/bufferformat.h"
+#include
+#include
+#include
+
+#include
+
+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 QSize &size);
+ void initYuv420PTextureInfo(bool uvSwapped, const QSize &size);
+ void init(GstVideoColorMatrix colorMatrixType);
+
+private:
+ void bindTexture(int i, const quint8 *data);
+
+
+ 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];
+ QSize m_textureSize;
+
+ GLenum m_textureFormat;
+ GLuint m_textureInternalFormat;
+ GLenum m_textureType;
+
+ QMatrix4x4 m_colorMatrix;
+ GstVideoColorMatrix m_colorMatrixType;
+
+ friend class VideoMaterialShader;
+};
+
+#endif // VIDEOMATERIAL_H
diff --git a/src/VideoStreaming/gstqtvideosink/painters/videonode.cpp b/src/VideoStreaming/gstqtvideosink/painters/videonode.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..87b415c15c3c6bf6ce7c40ad87f9d00d38f88c73
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/videonode.cpp
@@ -0,0 +1,118 @@
+/*
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH
+ 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "videonode.h"
+#include "videomaterial.h"
+
+#include
+
+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)
+{
+ Q_ASSERT (m_materialType == MaterialTypeVideo);
+ static_cast(material())->setCurrentFrame(buffer);
+ markDirty(DirtyMaterial);
+}
+
+void VideoNode::updateColors(int brightness, int contrast, int hue, int saturation)
+{
+ Q_ASSERT (m_materialType == MaterialTypeVideo);
+ static_cast(material())->updateColors(brightness, contrast, hue, saturation);
+ markDirty(DirtyMaterial);
+}
+
+/* Helpers */
+template
+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);
+}
diff --git a/src/VideoStreaming/gstqtvideosink/painters/videonode.h b/src/VideoStreaming/gstqtvideosink/painters/videonode.h
new file mode 100644
index 0000000000000000000000000000000000000000..55cb478fd2d38371c5f865582c38eebc6591557b
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/painters/videonode.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH
+ 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#ifndef VIDEONODE_H
+#define VIDEONODE_H
+
+#include "../utils/bufferformat.h"
+
+#include
+
+class VideoNode : public QSGGeometryNode
+{
+public:
+ VideoNode();
+
+ enum MaterialType {
+ MaterialTypeVideo,
+ MaterialTypeSolidBlack
+ };
+
+ MaterialType materialType() const { return m_materialType; }
+
+ void changeFormat(const BufferFormat &format);
+ void setMaterialTypeSolidBlack();
+
+ void setCurrentFrame(GstBuffer *buffer);
+ void updateColors(int brightness, int contrast, int hue, int saturation);
+
+ void updateGeometry(const PaintAreas & areas);
+
+private:
+ MaterialType m_materialType;
+};
+
+#endif // VIDEONODE_H
diff --git a/src/VideoStreaming/gstqtvideosink/utils/bufferformat.cpp b/src/VideoStreaming/gstqtvideosink/utils/bufferformat.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..91786cf03efa63b39ef5305fc686dd3f92370025
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/utils/bufferformat.cpp
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "bufferformat.h"
+#include
+
+BufferFormat BufferFormat::fromCaps(GstCaps *caps)
+{
+ BufferFormat result;
+ if (caps && gst_video_info_from_caps(&(result.d->videoInfo), caps)) {
+ return result;
+ } else {
+ return BufferFormat();
+ }
+}
+
+GstCaps* BufferFormat::newCaps(GstVideoFormat format, const QSize & size,
+ const Fraction & framerate, const Fraction & pixelAspectRatio)
+{
+ GstVideoInfo videoInfo;
+ gst_video_info_init(&videoInfo);
+ gst_video_info_set_format(&videoInfo, format, size.width(), size.height());
+
+ videoInfo.fps_n = framerate.numerator;
+ videoInfo.fps_d = framerate.denominator;
+
+ videoInfo.par_n = pixelAspectRatio.numerator;
+ videoInfo.par_d = pixelAspectRatio.denominator;
+
+ return gst_video_info_to_caps(&videoInfo);
+}
+
+int BufferFormat::bytesPerLine(int component) const
+{
+ return GST_VIDEO_INFO_PLANE_STRIDE(&(d->videoInfo), component);
+}
+
+bool operator==(BufferFormat a, BufferFormat b)
+{
+ return a.d == b.d;
+}
+
+bool operator!=(BufferFormat a, BufferFormat b)
+{
+ return a.d != b.d;
+}
diff --git a/src/VideoStreaming/gstqtvideosink/utils/bufferformat.h b/src/VideoStreaming/gstqtvideosink/utils/bufferformat.h
new file mode 100644
index 0000000000000000000000000000000000000000..44ba953bfc773b0999f521fd3b84cee9524a9643
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/utils/bufferformat.h
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright (C) 2011-2012 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 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 .
+*/
+#ifndef BUFFERFORMAT_H
+#define BUFFERFORMAT_H
+
+#include "utils.h"
+#include
+#include
+
+/**
+ * This class is a cheap way to represent Caps.
+ * Based on QVideoSurfaceFormat.
+ */
+class BufferFormat
+{
+public:
+ static BufferFormat fromCaps(GstCaps *caps);
+ static GstCaps *newCaps(GstVideoFormat format, const QSize & size,
+ const Fraction & framerate, const Fraction & pixelAspectRatio);
+
+ inline BufferFormat() : d(new Data) {}
+
+ inline GstVideoInfo videoInfo() const { return d->videoInfo; }
+ inline GstVideoFormat videoFormat() const { return GST_VIDEO_INFO_FORMAT(&(d->videoInfo)); }
+ inline GstVideoColorMatrix colorMatrix() const { return d->videoInfo.colorimetry.matrix; }
+ QSize frameSize() const {
+ return QSize(GST_VIDEO_INFO_WIDTH(&(d->videoInfo)),
+ GST_VIDEO_INFO_HEIGHT(&(d->videoInfo)));
+ }
+ Fraction pixelAspectRatio() const {
+ return Fraction(GST_VIDEO_INFO_PAR_N(&(d->videoInfo)),
+ GST_VIDEO_INFO_PAR_D(&(d->videoInfo)));
+ }
+
+ int bytesPerLine(int component = 0) const;
+
+private:
+ friend bool operator==(BufferFormat a, BufferFormat b);
+ friend bool operator!=(BufferFormat a, BufferFormat b);
+
+ struct Data : public QSharedData
+ {
+ Data()
+ { gst_video_info_init(&videoInfo); }
+
+ GstVideoInfo videoInfo;
+ };
+ QSharedDataPointer d;
+};
+
+Q_DECLARE_METATYPE(GstVideoInfo)
+Q_DECLARE_METATYPE(GstVideoFormat)
+Q_DECLARE_METATYPE(GstVideoColorMatrix)
+Q_DECLARE_METATYPE(BufferFormat)
+
+#endif // BUFFERFORMAT_H
diff --git a/src/VideoStreaming/gstqtvideosink/utils/utils.cpp b/src/VideoStreaming/gstqtvideosink/utils/utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c7191aab1d35367d5745be3cb3f83b6970498a0d
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/utils/utils.cpp
@@ -0,0 +1,92 @@
+/*
+ Copyright (C) 2011-2013 Collabora Ltd.
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH
+
+ 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 .
+*/
+
+/**
+ * @file
+ * @brief Extracted from QtGstreamer to avoid overly complex dependency
+ * @author Gus Grubba
+ */
+
+#include "utils.h"
+
+void PaintAreas::calculate(const QRectF & targetArea,
+ const QSize & videoSize,
+ const Fraction & pixelAspectRatio,
+ const Fraction & displayAspectRatio,
+ Qt::AspectRatioMode aspectRatioMode)
+{
+ this->targetArea = targetArea;
+
+ switch (aspectRatioMode) {
+ case Qt::IgnoreAspectRatio:
+ videoArea = targetArea;
+ sourceRect = QRectF(0, 0, 1, 1);
+ blackArea1 = blackArea2 = QRectF();
+ break;
+ default:
+ {
+ qreal aspectRatio = pixelAspectRatio.ratio() * displayAspectRatio.invRatio();
+
+ QSizeF videoSizeAdjusted = QSizeF(videoSize.width() * aspectRatio, videoSize.height());
+ videoSizeAdjusted.scale(targetArea.size(), aspectRatioMode);
+
+ // the area that the original video occupies, scaled
+ QRectF videoRect = QRectF(QPointF(), videoSizeAdjusted);
+ videoRect.moveCenter(targetArea.center());
+
+ if (aspectRatioMode == Qt::KeepAspectRatio) {
+ videoArea = videoRect;
+ sourceRect = QRectF(0, 0, 1, 1);
+ } else { // Qt::KeepAspectRatioByExpanding
+ videoArea = targetArea;
+ sourceRect = QRectF(
+ (videoArea.left() - videoRect.left()) / videoRect.width(),
+ (videoArea.top() - videoRect.top()) / videoRect.height(),
+ videoArea.width() / videoRect.width(),
+ videoArea.height() / videoRect.height());
+ }
+ break;
+ }
+ }
+
+ if (aspectRatioMode == Qt::IgnoreAspectRatio
+ || aspectRatioMode == Qt::KeepAspectRatioByExpanding
+ || videoArea == targetArea) {
+ blackArea1 = blackArea2 = QRectF();
+ } else {
+ blackArea1 = QRectF(
+ targetArea.left(),
+ targetArea.top(),
+ videoArea.left() == targetArea.left() ?
+ targetArea.width() : videoArea.left() - targetArea.left(),
+ videoArea.top() == targetArea.top() ?
+ targetArea.height() : videoArea.top() - targetArea.top()
+ );
+
+ blackArea2 = QRectF(
+ videoArea.right() == targetArea.right() ?
+ targetArea.left() : videoArea.right(),
+ videoArea.bottom() == targetArea.bottom() ?
+ targetArea.top() : videoArea.bottom(),
+ videoArea.right() == targetArea.right() ?
+ targetArea.width() : targetArea.right() - videoArea.right(),
+ videoArea.bottom() == targetArea.bottom() ?
+ targetArea.height() : targetArea.bottom() - videoArea.bottom()
+ );
+ }
+}
diff --git a/src/VideoStreaming/gstqtvideosink/utils/utils.h b/src/VideoStreaming/gstqtvideosink/utils/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1528e6a3972b053ffb3f8454f17601c38b4c225
--- /dev/null
+++ b/src/VideoStreaming/gstqtvideosink/utils/utils.h
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) 2011-2012 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 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 .
+*/
+#ifndef UTILS_H
+#define UTILS_H
+
+#include
+#include
+#include
+
+// utilities for GST_DEBUG
+#define QSIZE_FORMAT "(%d x %d)"
+#define QSIZE_FORMAT_ARGS(size) \
+ size.width(), size.height()
+#define QRECTF_FORMAT "(x: %f, y: %f, w: %f, h: %f)"
+#define QRECTF_FORMAT_ARGS(rect) \
+ (float) rect.x(), (float) rect.y(), (float) rect.width(), (float) rect.height()
+
+struct Fraction
+{
+ inline Fraction() {}
+ inline Fraction(int numerator, int denominator)
+ : numerator(numerator), denominator(denominator) {}
+
+ inline bool operator==(const Fraction & other) const
+ { return numerator == other.numerator && denominator == other.denominator; }
+ inline bool operator!=(const Fraction & other) const
+ { return !operator==(other); }
+
+ inline qreal ratio() const
+ { return (qreal) numerator / (qreal) denominator; }
+ inline qreal invRatio() const
+ { return (qreal) denominator / (qreal) numerator; }
+
+ int numerator;
+ int denominator;
+};
+
+struct PaintAreas
+{
+ void calculate(const QRectF & targetArea,
+ const QSize & videoSize,
+ const Fraction & pixelAspectRatio,
+ const Fraction & displayAspectRatio,
+ Qt::AspectRatioMode aspectRatioMode);
+
+ // the area that we paint on
+ QRectF targetArea;
+ // the area where the video should be painted on
+ // (subrect of or equal to targetArea)
+ QRectF videoArea;
+
+ // the part of the video rectangle that we are going to blit on the videoArea
+ // in the normalized (0,1] range (texture coordinates)
+ QRectF sourceRect;
+
+ // these are small subrects of targetArea that are not
+ // covered by videoArea to keep the video's aspect ratio
+ QRectF blackArea1;
+ QRectF blackArea2;
+};
+
+Q_DECLARE_METATYPE(Fraction)
+Q_DECLARE_METATYPE(PaintAreas)
+
+#endif
diff --git a/src/ui/flightdisplay/FlightDisplay.cc b/src/ui/flightdisplay/FlightDisplay.cc
index c23514964930df8930279f0f334c283a1cd299b8..a154823b76d532aab283f8b631b0d10b10ffa3ad 100644
--- a/src/ui/flightdisplay/FlightDisplay.cc
+++ b/src/ui/flightdisplay/FlightDisplay.cc
@@ -31,6 +31,10 @@ This file is part of the QGROUNDCONTROL project
#include
#include
+#include
+#include
+#include "VideoReceiver.h"
+
#include "ScreenToolsController.h"
#include "FlightDisplay.h"
#include "UASManager.h"
@@ -52,6 +56,41 @@ FlightDisplay::FlightDisplay(QWidget *parent)
setMinimumHeight(33 * ScreenToolsController::defaultFontPixelSize_s());
#endif
setContextPropertyObject("flightDisplay", this);
+
+ /*
+ * This is the receiving end of an UDP RTP stream. The sender can be setup with this command:
+ *
+ * gst-launch-1.0 uvch264src initial-bitrate=1000000 average-bitrate=1000000 iframe-period=1000 name=src auto-start=true src.vidsrc ! \
+ * video/x-h264,width=1280,height=720,framerate=24/1 ! h264parse ! rtph264pay ! udpsink host=192.168.1.9 port=5000
+ *
+ * Where the main parameters are:
+ *
+ * uvch264src: Your h264 video source (the example above uses a Logitech C920 on an Raspberry PI 2+ or Odroid C1
+ * host=192.168.1.9 This is the IP address of QGC. You can use Avahi/Zeroconf to find QGC using the "_qgroundcontrol._udp" service.
+ *
+ * Advanced settings (you should probably read the gstreamer documentation before changing these):
+ *
+ * initial-bitrate=1000000 average-bitrate=1000000
+ * The bit rate to use. The greater, the better quality at the cost of higher bandwidth.
+ *
+ * width=1280,height=720,framerate=24/1
+ * The video resolution and frame rate. This depends on the camera used.
+ *
+ * iframe-period=1000
+ * Interval between iFrames. The greater the interval the lesser bandwidth at the cost of a longer time to recover from lost packets.
+ *
+ * Do not change anything else unless you know what you are doing. Any other change will require a matching change on the receiving end.
+ *
+ */
+ VideoSurface* pSurface = new VideoSurface;
+ setContextPropertyObject("videoDisplay", pSurface);
+ VideoReceiver* pReceiver = new VideoReceiver(this);
+ pReceiver->setUri(QLatin1Literal("udp://0.0.0.0:5000"));
+#if defined(QGC_GST_STREAMING)
+ pReceiver->setVideoSink(pSurface->videoSink());
+#endif
+ setContextPropertyObject("videoReceiver", pReceiver);
+
setSource(QUrl::fromUserInput("qrc:/qml/FlightDisplay.qml"));
setVisible(true);
}
diff --git a/src/ui/flightdisplay/FlightDisplay.h b/src/ui/flightdisplay/FlightDisplay.h
index ba374c26a92af873861305171d76de4ba53b6805..cd3e63e72e6b877405127891ce9c0890043fcb89 100644
--- a/src/ui/flightdisplay/FlightDisplay.h
+++ b/src/ui/flightdisplay/FlightDisplay.h
@@ -44,9 +44,17 @@ public:
/// @brief Invokes the Flight Display Options menu
void showOptionsMenu() { emit showOptionsMenuChanged(); }
+ Q_PROPERTY(bool hasVideo READ hasVideo CONSTANT)
+
Q_INVOKABLE void saveSetting (const QString &key, const QString& value);
Q_INVOKABLE QString loadSetting (const QString &key, const QString& defaultValue);
+#if defined(QGC_GST_STREAMING)
+ bool hasVideo () { return true; }
+#else
+ bool hasVideo () { return false; }
+#endif
+
signals:
void showOptionsMenuChanged ();
diff --git a/src/ui/flightdisplay/FlightDisplay.qml b/src/ui/flightdisplay/FlightDisplay.qml
index 034bf86ed42390337d5a4ef1e86b3e0642673104..e075a524857b0dad2c672d9975c3135a57de35ad 100644
--- a/src/ui/flightdisplay/FlightDisplay.qml
+++ b/src/ui/flightdisplay/FlightDisplay.qml
@@ -27,7 +27,7 @@ This file is part of the QGROUNDCONTROL project
* @author Gus Grubba
*/
-import QtQuick 2.3
+import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.2
import QtQuick.Dialogs 1.2
@@ -58,6 +58,10 @@ Item {
return value ? "1" : "0";
}
+ function showHudInstruments() {
+ return videoBackground.visible || mapBackground.visible;
+ }
+
Connections {
target: flightDisplay
onShowOptionsMenuChanged: {
@@ -70,6 +74,7 @@ Item {
mapBackground.visible = getBool(flightDisplay.loadSetting("showMapBackground", "0"));
mapBackground.showWaypoints = getBool(flightDisplay.loadSetting("mapShowWaypoints", "0"));
mapBackground.alwaysNorth = getBool(flightDisplay.loadSetting("mapAlwaysPointsNorth", "0"));
+ videoBackground.visible = getBool(flightDisplay.loadSetting("showVideoBackground", "0"));
showAttitudeIndicator = getBool(flightDisplay.loadSetting("showAttitudeIndicator", "1"));
showPitchIndicator = getBool(flightDisplay.loadSetting("showPitchIndicator", "1"));
showCompass = getBool(flightDisplay.loadSetting("showCompass", "1"));
@@ -81,170 +86,36 @@ Item {
currentAltitude.showAltitude = getBool(flightDisplay.loadSetting("showCurrentAltitude", "1"));
// Insert Map Type menu before separator
contextMenu.insertItem(2, mapBackground.mapMenu);
- }
-
- // TODO: This is to replace the context menu but it is not working. Not only the buttons don't show,
- // the default placement is random and mostly off screen on mobile devices.
- Dialog {
- id: optionsDialog
- modality: Qt.WindowModal
- title: "Flight Display Options"
- standardButtons: StandardButton.Close | StandardButton.RestoreDefaults
- onReset: {
- showPitchIndicator = true;
- flightDisplay.saveSetting("showPitchIndicator", setBool(showPitchIndicator));
- showAttitudeIndicator = true;
- flightDisplay.saveSetting("showAttitudeIndicator", setBool(showAttitudeIndicator));
- showCompass = true;
- flightDisplay.saveSetting("showCompass", setBool(showCompass));
- altitudeWidget.visible = true;
- flightDisplay.saveSetting("showAltitudeWidget", setBool(altitudeWidget.visible));
- currentAltitude.showAltitude = true;
- flightDisplay.saveSetting("showCurrentAltitude", setBool(currentAltitude.showAltitude));
- currentAltitude.showClimbRate = true;
- flightDisplay.saveSetting("showCurrentClimbRate", setBool(currentAltitude.showClimbRate));
- speedWidget.visible = true;
- flightDisplay.saveSetting("showSpeedWidget", setBool(speedWidget.visible));
- currentSpeed.showAirSpeed = true;
- flightDisplay.saveSetting("showCurrentAirSpeed", setBool(currentSpeed.showAirSpeed));
- currentSpeed.showGroundSpeed = true;
- flightDisplay.saveSetting("showCurrentGroundSpeed", setBool(currentSpeed.showGroundSpeed));
- mapBackground.visible = false;
- flightDisplay.saveSetting("showMapBackground", setBool(mapBackground.visible));
- mapBackground.alwaysNorth = false;
- flightDisplay.saveSetting("mapAlwaysPointsNorth", setBool(mapBackground.alwaysNorth));
- mapBackground.showWaypoints = false
- flightDisplay.saveSetting("mapShowWaypoints", setBool(mapBackground.showWaypoints));
+ // Video or Map. Not both:
+ if(mapBackground.visible && videoBackground.visible) {
+ videoBackground.visible = false;
+ flightDisplay.saveSetting("showVideoBackground", setBool(videoBackground.visible));
}
- contentItem: Rectangle {
- color: __qgcPal.window
- implicitWidth: ScreenTools.defaultFontPixelSize * (30)
- implicitHeight: ScreenTools.defaultFontPixelSize * (30)
- Column {
- id: dialogColumn
- anchors.centerIn: parent
- spacing: ScreenTools.defaultFontPixelSize
- width: parent.width
- Grid {
- columns: 2
- spacing: ScreenTools.defaultFontPixelSize * (0.66)
- rowSpacing: ScreenTools.defaultFontPixelSize * (0.83)
- anchors.horizontalCenter: parent.horizontalCenter
- QGCCheckBox {
- text: "Map Background"
- checked: mapBackground.visible
- onClicked:
- {
- mapBackground.visible = !mapBackground.visible;
- flightDisplay.saveSetting("showMapBackground", setBool(mapBackground.visible));
- }
- }
- QGCCheckBox {
- text: "Map Show Waypoints"
- checked: mapBackground.showWaypoints
- onClicked:
- {
- mapBackground.showWaypoints = !mapBackground.showWaypoints;
- flightDisplay.saveSetting("mapShowWaypoints", setBool(mapBackground.showWaypoints));
- }
- }
- QGCCheckBox {
- text: "Pitch Indicator"
- checked: showPitchIndicator
- onClicked:
- {
- showPitchIndicator = !showPitchIndicator;
- flightDisplay.saveSetting("showPitchIndicator", setBool(showPitchIndicator));
- }
- }
- QGCCheckBox {
- text: "Attitude Indicator"
- checked: showAttitudeIndicator
- onClicked:
- {
- showAttitudeIndicator = !showAttitudeIndicator;
- flightDisplay.saveSetting("showAttitudeIndicator", setBool(showAttitudeIndicator));
- }
- }
- QGCCheckBox {
- text: "Compass"
- checked: showCompass
- onClicked:
- {
- showCompass = !showCompass;
- flightDisplay.saveSetting("showCompass", setBool(showCompass));
- }
- }
- QGCCheckBox {
- text: "Altitude Indicator"
- checked: altitudeWidget.visible
- onClicked:
- {
- altitudeWidget.visible = !altitudeWidget.visible;
- flightDisplay.saveSetting("showAltitudeWidget", setBool(altitudeWidget.visible));
- }
- }
- QGCCheckBox {
- text: "Current Altitude"
- checked: currentAltitude.showAltitude
- onClicked:
- {
- currentAltitude.showAltitude = !currentAltitude.showAltitude;
- flightDisplay.saveSetting("showCurrentAltitude", setBool(currentAltitude.showAltitude));
- }
- }
- QGCCheckBox {
- text: "Current Climb Rate"
- checked: currentAltitude.showClimbRate
- onClicked:
- {
- currentAltitude.showClimbRate = !currentAltitude.showClimbRate;
- flightDisplay.saveSetting("showCurrentClimbRate", setBool(currentAltitude.showClimbRate));
- }
- }
- QGCCheckBox {
- text: "Speed Indicator"
- checked: speedWidget.visible
- onClicked:
- {
- speedWidget.visible = !speedWidget.visible;
- flightDisplay.saveSetting("showSpeedWidget", setBool(speedWidget.visible));
- }
- }
- QGCCheckBox {
- text: "Current Air Speed"
- checked: currentSpeed.showAirSpeed
- onClicked:
- {
- currentSpeed.showAirSpeed = !currentSpeed.showAirSpeed;
- flightDisplay.saveSetting("showCurrentAirSpeed", setBool(currentSpeed.showAirSpeed));
- }
- }
- QGCCheckBox {
- text: "Current Ground Speed"
- checked: currentSpeed.showGroundSpeed
- onClicked:
- {
- currentSpeed.showGroundSpeed = !currentSpeed.showGroundSpeed;
- flightDisplay.saveSetting("showCurrentGroundSpeed", setBool(currentSpeed.showGroundSpeed));
- }
- }
- }
- }
+ // Disable video if we don't have support for it
+ if(!flightDisplay.hasVideo) {
+ videoBackground.visible = false;
+ flightDisplay.saveSetting("showVideoBackground", setBool(videoBackground.visible));
}
+ // Enable/Disable menu accordingly
+ videoMenu.enabled = flightDisplay.hasVideo;
}
Menu {
id: contextMenu
+ ExclusiveGroup { id: backgroundDisplay }
+
MenuItem {
text: "Map Background"
checkable: true
checked: mapBackground.visible
+ exclusiveGroup: backgroundDisplay
onTriggered:
{
mapBackground.visible = !mapBackground.visible;
flightDisplay.saveSetting("showMapBackground", setBool(mapBackground.visible));
+ videoBackground.visible = mapBackground.visible ? false : videoBackground.visible;
+ flightDisplay.saveSetting("showVideoBackground", setBool(videoBackground.visible));
}
}
@@ -252,6 +123,7 @@ Item {
text: "Map Show Waypoints"
checkable: true
checked: mapBackground.showWaypoints
+ enabled: mapBackground.visible
onTriggered:
{
mapBackground.showWaypoints = !mapBackground.showWaypoints;
@@ -260,27 +132,35 @@ Item {
}
/*
+ //-- Off until Qt 5.5.x, which fixes bug in 5.4.x
MenuItem {
- text: "Options Dialog"
+ text: "Map Always Points North"
+ checkable: true
+ checked: mapBackground.alwaysNorth
onTriggered:
{
- optionsDialog.open()
+ mapBackground.alwaysNorth = !mapBackground.alwaysNorth;
+ flightDisplay.saveSetting("mapAlwaysPointsNorth", setBool(mapBackground.alwaysNorth));
}
}
*/
- /*
+ MenuSeparator {}
+
MenuItem {
- text: "Map Always Points North"
+ id: videoMenu
+ text: "Video Background"
checkable: true
- checked: mapBackground.alwaysNorth
+ checked: videoBackground.visible
+ exclusiveGroup: backgroundDisplay
onTriggered:
{
- mapBackground.alwaysNorth = !mapBackground.alwaysNorth;
- flightDisplay.saveSetting("mapAlwaysPointsNorth", setBool(mapBackground.alwaysNorth));
+ videoBackground.visible = !videoBackground.visible;
+ flightDisplay.saveSetting("showVideoBackground", setBool(videoBackground.visible));
+ mapBackground.visible = videoBackground.visible ? false : mapBackground.visible;
+ flightDisplay.saveSetting("showMapBackground", setBool(mapBackground.visible));
}
}
- */
MenuSeparator {}
@@ -413,11 +293,21 @@ Item {
flightDisplay.saveSetting("mapAlwaysPointsNorth", setBool(mapBackground.alwaysNorth));
mapBackground.showWaypoints = false
flightDisplay.saveSetting("mapShowWaypoints", setBool(mapBackground.showWaypoints));
+ videoBackground.visible = false;
+ flightDisplay.saveSetting("showVideoBackground", setBool(videoBackground.visible));
}
}
}
+ QGCVideoBackground {
+ id: videoBackground
+ anchors.fill: parent
+ display: videoDisplay
+ receiver: videoReceiver
+ z: 10
+ }
+
QGCMapBackground {
id: mapBackground
anchors.fill: parent
@@ -431,12 +321,12 @@ Item {
}
QGCHudMessage {
- id: hudMessage
- y: ScreenTools.defaultFontPizelSize * (0.42)
- width: (parent.width - 520 > 200) ? parent.width - 520 : 200
- height: ScreenTools.defaultFontPizelSize * (2.5)
+ id: hudMessage
+ y: ScreenTools.defaultFontPizelSize * (0.42)
+ width: (parent.width - 520 > 200) ? parent.width - 520 : 200
+ height: ScreenTools.defaultFontPizelSize * (2.5)
anchors.horizontalCenter: parent.horizontalCenter
- z: mapBackground.z + 1
+ z: mapBackground.z + 1
}
QGCCompassInstrument {
@@ -445,7 +335,7 @@ Item {
x: ScreenTools.defaultFontPixelSize * (7.1)
size: ScreenTools.defaultFontPixelSize * (13.3)
heading: isNaN(MavManager.heading) ? 0 : MavManager.heading
- visible: mapBackground.visible && showCompass
+ visible: showHudInstruments() && showCompass
z: mapBackground.z + 2
onResetRequested: {
y = ScreenTools.defaultFontPixelSize * (0.42)
@@ -463,7 +353,7 @@ Item {
rollAngle: roll
pitchAngle: pitch
showPitch: showPitchIndicator
- visible: mapBackground.visible && showAttitudeIndicator
+ visible: showHudInstruments() && showAttitudeIndicator
anchors.right: root.right
anchors.rightMargin: ScreenTools.defaultFontPixelSize * (7.1)
z: mapBackground.z + 2
@@ -482,14 +372,14 @@ Item {
anchors.fill: parent
rollAngle: roll
pitchAngle: pitch
- visible: !mapBackground.visible
+ visible: !showHudInstruments()
}
QGCAttitudeWidget {
id: attitudeWidget
rollAngle: roll
pitchAngle: pitch
- visible: !mapBackground.visible && showAttitudeIndicator
+ visible: !showHudInstruments() && showAttitudeIndicator
width: ScreenTools.defaultFontPixelSize * (21.7)
height: ScreenTools.defaultFontPixelSize * (21.7)
z: 20
@@ -497,7 +387,7 @@ Item {
QGCPitchWidget {
id: pitchWidget
- visible: showPitchIndicator && !mapBackground.visible
+ visible: showPitchIndicator && !showHudInstruments()
anchors.verticalCenter: parent.verticalCenter
pitchAngle: pitch
rollAngle: roll
@@ -555,7 +445,7 @@ Item {
width: ScreenTools.defaultFontPixelSize * (10)
height: ScreenTools.defaultFontPixelSize * (10)
heading: isNaN(MavManager.heading) ? 0 : MavManager.heading
- visible: !mapBackground.visible && showCompass
+ visible: !showHudInstruments() && showCompass
z: 70
}
diff --git a/src/ui/qmlcommon/QGCMapBackground.qml b/src/ui/qmlcommon/QGCMapBackground.qml
index 8589bbe27953ee454b1c442cc01167e41ff0e7af..b82be7958e54e66d3045340333232acfe2cb197c 100644
--- a/src/ui/qmlcommon/QGCMapBackground.qml
+++ b/src/ui/qmlcommon/QGCMapBackground.qml
@@ -64,6 +64,7 @@ Item {
Menu {
id: mapTypeMenu
title: "Map Type..."
+ enabled: root.visible
ExclusiveGroup { id: currMapType }
function setCurrentMap(mapID) {
for (var i = 0; i < map.supportedMapTypes.length; i++) {
diff --git a/src/ui/qmlcommon/QGCVideoBackground.qml b/src/ui/qmlcommon/QGCVideoBackground.qml
new file mode 100644
index 0000000000000000000000000000000000000000..639bba83ff785dfd7691b54d177f7e00e80f4e4f
--- /dev/null
+++ b/src/ui/qmlcommon/QGCVideoBackground.qml
@@ -0,0 +1,53 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT