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 + +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 Background + * @author Gus Grubba + */ + +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QGroundControl.QgcQtGStreamer 1.0 + +VideoItem { + id: videoBackground + property var display + property var receiver + surface: display + onVisibleChanged: { + if(videoBackground.receiver && videoBackground.display) { + if(videoBackground.visible) { + videoBackground.receiver.start(); + } else { + videoBackground.receiver.stop(); + } + } + } + Component.onCompleted: { + if(videoBackground.visible && videoBackground.receiver) { + videoBackground.receiver.start(); + } + } +} diff --git a/src/ui/qmlcommon/qmldir b/src/ui/qmlcommon/qmldir index 6986e8ab6be935c749efa79a71fa5a53ccf0fcff..dfaeec284866d363a0dea2a2f779fba35d71eb30 100644 --- a/src/ui/qmlcommon/qmldir +++ b/src/ui/qmlcommon/qmldir @@ -1,18 +1,19 @@ Module QGroundControl.FlightControls -QGCAltitudeWidget 1.0 QGCAltitudeWidget.qml -QGCAttitudeWidget 1.0 QGCAttitudeWidget.qml -QGCCompass 1.0 QGCCompass.qml -QGCCurrentAltitude 1.0 QGCCurrentAltitude.qml -QGCCurrentSpeed 1.0 QGCCurrentSpeed.qml -QGCMapBackground 1.0 QGCMapBackground.qml -QGCPitchWidget 1.0 QGCPitchWidget.qml -QGCSpeedWidget 1.0 QGCSpeedWidget.qml -QGCSlider 1.0 QGCSlider.qml -QGCWaypointEditor 1.0 QGCWaypointEditor.qml -QGCMapToolButton 1.0 QGCMapToolButton.qml -QGCAttitudeInstrument 1.0 QGCAttitudeInstrument.qml -QGCCompassInstrument 1.0 QGCCompassInstrument.qml -QGCArtificialHorizon 1.0 QGCArtificialHorizon.qml -QGCWaypoint 1.0 QGCWaypoint.qml -QGCHudMessage 1.0 QGCHudMessage.qml +QGCAltitudeWidget 1.0 QGCAltitudeWidget.qml +QGCAttitudeWidget 1.0 QGCAttitudeWidget.qml +QGCCompass 1.0 QGCCompass.qml +QGCCurrentAltitude 1.0 QGCCurrentAltitude.qml +QGCCurrentSpeed 1.0 QGCCurrentSpeed.qml +QGCMapBackground 1.0 QGCMapBackground.qml +QGCPitchWidget 1.0 QGCPitchWidget.qml +QGCSpeedWidget 1.0 QGCSpeedWidget.qml +QGCSlider 1.0 QGCSlider.qml +QGCWaypointEditor 1.0 QGCWaypointEditor.qml +QGCMapToolButton 1.0 QGCMapToolButton.qml +QGCAttitudeInstrument 1.0 QGCAttitudeInstrument.qml +QGCCompassInstrument 1.0 QGCCompassInstrument.qml +QGCArtificialHorizon 1.0 QGCArtificialHorizon.qml +QGCWaypoint 1.0 QGCWaypoint.qml +QGCHudMessage 1.0 QGCHudMessage.qml +QGCVideoBackground 1.0 QGCVideoBackground.qml