Commit a5ab5449 authored by Don Gagne's avatar Don Gagne
Browse files

Merge pull request #1707 from dogmaphobic/videoStreaming

Video streaming
parents 112b72e5 38cf8056
......@@ -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 {
......
......@@ -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
}
......
......@@ -3,24 +3,20 @@
<file alias="MockLink.params">src/comm/MockLink.params</file>
<file alias="FactSystemTest.qml">src/FactSystem/FactSystemTest.qml</file>
</qresource>
<qresource prefix="/qmlimages">
<file alias="SafetyComponentTree.svg">src/AutoPilotPlugins/PX4/Images/SafetyComponentTree.svg</file>
<file alias="SafetyComponentHome.png">src/AutoPilotPlugins/PX4/Images/SafetyComponentHome.png</file>
<file alias="SafetyComponentArrowDown.png">src/AutoPilotPlugins/PX4/Images/SafetyComponentArrowDown.png</file>
<file alias="SafetyComponentPlane.png">src/AutoPilotPlugins/PX4/Images/SafetyComponentPlane.png</file>
<file alias="VehicleDown.png">src/AutoPilotPlugins/PX4/Images/VehicleDown.png</file>
<file alias="VehicleUpsideDown.png">src/AutoPilotPlugins/PX4/Images/VehicleUpsideDown.png</file>
<file alias="VehicleLeft.png">src/AutoPilotPlugins/PX4/Images/VehicleLeft.png</file>
<file alias="VehicleRight.png">src/AutoPilotPlugins/PX4/Images/VehicleRight.png</file>
<file alias="VehicleNoseDown.png">src/AutoPilotPlugins/PX4/Images/VehicleNoseDown.png</file>
<file alias="VehicleTailDown.png">src/AutoPilotPlugins/PX4/Images/VehicleTailDown.png</file>
<file alias="VehicleDownRotate.png">src/AutoPilotPlugins/PX4/Images/VehicleDownRotate.png</file>
<file alias="VehicleLeftRotate.png">src/AutoPilotPlugins/PX4/Images/VehicleLeftRotate.png</file>
<file alias="VehicleNoseDownRotate.png">src/AutoPilotPlugins/PX4/Images/VehicleNoseDownRotate.png</file>
<file alias="AirframeStandardPlane.png">src/AutoPilotPlugins/PX4/Images/AirframeStandardPlane.png</file>
<file alias="AirframeFlyingWing.png">src/AutoPilotPlugins/PX4/Images/AirframeFlyingWing.png</file>
<file alias="AirframeQuadRotorX.png">src/AutoPilotPlugins/PX4/Images/AirframeQuadRotorX.png</file>
......@@ -31,7 +27,6 @@
<file alias="AirframeHexaRotorPlus.png">src/AutoPilotPlugins/PX4/Images/AirframeHexaRotorPlus.png</file>
<file alias="AirframeQuadRotorH.png">src/AutoPilotPlugins/PX4/Images/AirframeQuadRotorH.png</file>
<file alias="AirframeSimulation.png">src/AutoPilotPlugins/PX4/Images/AirframeSimulation.png</file>
<file alias="arrow-down.png">src/QmlControls/arrow-down.png</file>
<file alias="subMenuButtonImage.png">resources/CogWheels.png</file>
<file alias="SensorsComponentIcon.png">src/AutoPilotPlugins/PX4/Images/SensorsComponentIcon.png</file>
......@@ -42,14 +37,12 @@
<file alias="PowerComponentIcon.png">src/AutoPilotPlugins/PX4/Images/PowerComponentIcon.png</file>
<file alias="FirmwareUpgradeIcon.png">src/VehicleSetup/FirmwareUpgradeIcon.png</file>
<file alias="VehicleSummaryIcon.png">src/VehicleSetup/VehicleSummaryIcon.png</file>
<file alias="PowerComponentBattery_01cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_01cell.svg</file>
<file alias="PowerComponentBattery_02cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_02cell.svg</file>
<file alias="PowerComponentBattery_03cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_03cell.svg</file>
<file alias="PowerComponentBattery_04cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_04cell.svg</file>
<file alias="PowerComponentBattery_05cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_05cell.svg</file>
<file alias="PowerComponentBattery_06cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_06cell.svg</file>
<file alias="attitudeDial.svg">src/ui/qmlcommon/attitudeDial.svg</file>
<file alias="attitudeInstrument.svg">src/ui/qmlcommon/attitudeInstrument.svg</file>
<file alias="attitudePointer.svg">src/ui/qmlcommon/attitudePointer.svg</file>
......@@ -67,18 +60,15 @@
<file alias="scale.png">src/ui/qmlcommon/scale.png</file>
<file alias="scale_end.png">src/ui/qmlcommon/scale_end.png</file>
</qresource>
<qresource prefix="/qml">
<file alias="test.qml">src/test.qml</file>
<file alias="QmlTest.qml">src/QmlControls/QmlTest.qml</file>
<file alias="QGroundControl/FactControls/qmldir">src/FactSystem/FactControls/qmldir</file>
<file alias="QGroundControl/FactControls/FactPanel.qml">src/FactSystem/FactControls/FactPanel.qml</file>
<file alias="QGroundControl/FactControls/FactLabel.qml">src/FactSystem/FactControls/FactLabel.qml</file>
<file alias="QGroundControl/FactControls/FactTextField.qml">src/FactSystem/FactControls/FactTextField.qml</file>
<file alias="QGroundControl/FactControls/FactCheckBox.qml">src/FactSystem/FactControls/FactCheckBox.qml</file>
<file alias="QGroundControl/FactControls/FactComboBox.qml">src/FactSystem/FactControls/FactComboBox.qml</file>
<file alias="QGroundControl/Controls/qmldir">src/QmlControls/QGroundControl.Controls.qmldir</file>
<file alias="QGroundControl/Controls/QGCButton.qml">src/QmlControls/QGCButton.qml</file>
<file alias="QGroundControl/Controls/QGCRadioButton.qml">src/QmlControls/QGCRadioButton.qml</file>
......@@ -89,32 +79,25 @@
<file alias="QGroundControl/Controls/QGCColoredImage.qml">src/QmlControls/QGCColoredImage.qml</file>
<file alias="QGroundControl/Controls/QGCToolBarButton.qml">src/QmlControls/QGCToolBarButton.qml</file>
<file alias="QGroundControl/Controls/QGCMovableItem.qml">src/QmlControls/QGCMovableItem.qml</file>
<file alias="QGroundControl/ScreenTools/qmldir">src/QmlControls/QGroundControl.ScreenTools.qmldir</file>
<file alias="QGroundControl/ScreenTools/ScreenTools.qml">src/QmlControls/ScreenTools.qml</file>
<file alias="ScreenToolsFontQuery.qml">src/QmlControls/ScreenToolsFontQuery.qml</file>
<file alias="QGroundControl/Controls/SubMenuButton.qml">src/QmlControls/SubMenuButton.qml</file>
<file alias="QGroundControl/Controls/IndicatorButton.qml">src/QmlControls/IndicatorButton.qml</file>
<file alias="QGroundControl/Controls/VehicleRotationCal.qml">src/QmlControls/VehicleRotationCal.qml</file>
<file alias="QGroundControl/Controls/VehicleSummaryRow.qml">src/QmlControls/VehicleSummaryRow.qml</file>
<file alias="QGroundControl/Controls/ViewWidget.qml">src/ViewWidgets/ViewWidget.qml</file>
<file alias="QGroundControl/Controls/QGCView.qml">src/QmlControls/QGCView.qml</file>
<file alias="QGroundControl/Controls/QGCViewPanel.qml">src/QmlControls/QGCViewPanel.qml</file>
<file alias="QGroundControl/Controls/QGCViewDialog.qml">src/QmlControls/QGCViewDialog.qml</file>
<file alias="QGroundControl/Controls/QGCViewMessage.qml">src/QmlControls/QGCViewMessage.qml</file>
<file alias="QGroundControl/Controls/ParameterEditor.qml">src/QmlControls/ParameterEditor.qml</file>
<file alias="QGroundControl/Controls/ParameterEditorDialog.qml">src/QmlControls/ParameterEditorDialog.qml</file>
<file alias="ParameterEditorWidget.qml">src/ViewWidgets/ParameterEditorWidget.qml</file>
<file alias="CustomCommandWidget.qml">src/ViewWidgets/CustomCommandWidget.qml</file>
<file alias="SetupView.qml">src/VehicleSetup/SetupView.qml</file>
<file alias="SetupViewButtonsConnected.qml">src/VehicleSetup/SetupViewButtonsConnected.qml</file>
<file alias="SetupViewButtonsDisconnected.qml">src/VehicleSetup/SetupViewButtonsDisconnected.qml</file>
<file alias="VehicleSummary.qml">src/VehicleSetup/VehicleSummary.qml</file>
<file alias="FirmwareUpgrade.qml">src/VehicleSetup/FirmwareUpgrade.qml</file>
<file alias="SetupParameterEditor.qml">src/VehicleSetup/SetupParameterEditor.qml</file>
......@@ -130,14 +113,10 @@
<file alias="RadioComponentSummary.qml">src/AutoPilotPlugins/PX4/RadioComponentSummary.qml</file>
<file alias="FlightModesComponentSummary.qml">src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml</file>
<file alias="AirframeComponentSummary.qml">src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml</file>
<!-- Airframe component resourecs -->
<file alias="AirframeComponent.qml">src/AutoPilotPlugins/PX4/AirframeComponent.qml</file>
<!-- QML Main UI -->
<file alias="MainToolBar.qml">src/ui/toolbar/MainToolBar.qml</file>
<file alias="FlightDisplay.qml">src/ui/flightdisplay/FlightDisplay.qml</file>
<file alias="MapDisplay.qml">src/ui/mapdisplay/MapDisplay.qml</file>
<!-- QML Main UI Components -->
<file alias="QGroundControl/FlightControls/QGCAltitudeWidget.qml">src/ui/qmlcommon/QGCAltitudeWidget.qml</file>
<file alias="QGroundControl/FlightControls/QGCArtificialHorizon.qml">src/ui/qmlcommon/QGCArtificialHorizon.qml</file>
<file alias="QGroundControl/FlightControls/QGCAttitudeInstrument.qml">src/ui/qmlcommon/QGCAttitudeInstrument.qml</file>
......@@ -152,16 +131,14 @@
<file alias="QGroundControl/FlightControls/QGCPitchWidget.qml">src/ui/qmlcommon/QGCPitchWidget.qml</file>
<file alias="QGroundControl/FlightControls/QGCSlider.qml">src/ui/qmlcommon/QGCSlider.qml</file>
<file alias="QGroundControl/FlightControls/QGCSpeedWidget.qml">src/ui/qmlcommon/QGCSpeedWidget.qml</file>
<file alias="QGroundControl/FlightControls/QGCVideoBackground.qml">src/ui/qmlcommon/QGCVideoBackground.qml</file>
<file alias="QGroundControl/FlightControls/QGCWaypointEditor.qml">src/ui/qmlcommon/QGCWaypointEditor.qml</file>
<file alias="QGroundControl/FlightControls/qmldir">src/ui/qmlcommon/qmldir</file>
<!-- QML Map Resources -->
<file alias="QGroundControl/FlightControls/QGCWaypoint.qml">src/ui/qmlcommon/QGCWaypoint.qml</file>
</qresource>
<qresource prefix="/AutoPilotPlugins/PX4">
<file alias="ParameterFactMetaData.xml">src/AutoPilotPlugins/PX4/ParameterFactMetaData.xml</file>
</qresource>
<qresource prefix="/res">
<file alias="LeftArrow">resources/LeftArrow.svg</file>
<file alias="RightArrow">resources/RightArrow.svg</file>
......@@ -169,45 +146,35 @@
<file alias="UpArrow">resources/UpArrow.svg</file>
<file alias="BottomArrow">resources/BottomArrow.svg</file>
<file alias="JumpArrow">resources/JumpArrow.svg</file>
<file alias="PlusSign">resources/PlusSign.svg</file>
<file alias="MinusSign">resources/MinusSign.svg</file>
<file alias="Play">resources/Play.svg</file>
<file alias="Pause">resources/Pause.svg</file>
<file alias="Stop">resources/Stop.svg</file>
<file alias="Launch">resources/Launch.svg</file>
<file alias="Land">resources/Land.svg</file>
<file alias="Kill">resources/Kill.svg</file>
<file alias="Shutdown">resources/Shutdown.svg</file>
<file alias="AntennaT">resources/Antenna_T.svg</file>
<file alias="AntennaRC">resources/Antenna_RC.svg</file>
<file alias="Gps">resources/Gps.svg</file>
<file alias="Megaphone">resources/Megaphone.png</file>
<file alias="Yield">resources/Yield.png</file>
<file alias="Battery_0">resources/Battery_0.svg</file>
<file alias="Battery_20">resources/Battery_20.svg</file>
<file alias="Battery_40">resources/Battery_40.svg</file>
<file alias="Battery_60">resources/Battery_60.svg</file>
<file alias="Battery_80">resources/Battery_80.svg</file>
<file alias="Battery_100">resources/Battery_100.svg</file>
<file alias="SystemLockScreen">resources/SystemLockScreen.svg</file>
<file alias="SplashScreen">resources/SplashScreen.png</file>
<file alias="QGroundControlConnect">resources/QGroundControlConnect.svg</file>
</qresource>
<qresource prefix="/res/firmware">
<file alias="px4.png">resources/firmware/px4.png</file>
<file alias="apm.png">resources/firmware/apm.png</file>
<file alias="3drradio.png">resources/firmware/3drradio.png</file>
</qresource>
<qresource prefix="/res/mavs">
<file alias="Helicopter">resources/mavs/helicopter.svg</file>
<file alias="Unknown">resources/mavs/unknown.svg</file>
......@@ -229,14 +196,12 @@
<file alias="SurfaceBoat">resources/mavs/surface-boat.svg</file>
<file alias="TriCopter">resources/mavs/tricopter.svg</file>
</qresource>
<qresource prefix="/res/mapproviders">
<file alias="openstreetmap.png">resources/mapproviders/openstreetmap.png</file>
<file alias="google.png">resources/mapproviders/google.png</file>
<file alias="yahoo.png">resources/mapproviders/yahoo.png</file>
<file alias="googleearth.svg">resources/mapproviders/googleearth.svg</file>
</qresource>
<qresource prefix="/res/calibration">
<file alias="accel_back.png">resources/calibration/accel_back.png</file>
<file alias="accel_front.png">resources/calibration/accel_front.png</file>
......@@ -245,7 +210,6 @@
<file alias="accel_up.png">resources/calibration/accel_up.png</file>
<file alias="accel_left.png">resources/calibration/accel_left.png</file>
</qresource>
<qresource prefix="/qml/calibration/mode1">
<file alias="radioCenter.png">resources/calibration/mode1/radioCenter.png</file>
<file alias="radioHome.png">resources/calibration/mode1/radioHome.png</file>
......@@ -259,7 +223,6 @@
<file alias="radioThrottleDown.png">resources/calibration/mode1/radioThrottleDown.png</file>
<file alias="radioSwitchMinMax.png">resources/calibration/mode1/radioSwitchMinMax.png</file>
</qresource>
<qresource prefix="/qml/calibration/mode2">
<file alias="radioCenter.png">resources/calibration/mode2/radioCenter.png</file>
<file alias="radioHome.png">resources/calibration/mode2/radioHome.png</file>
......@@ -273,16 +236,13 @@
<file alias="radioThrottleDown.png">resources/calibration/mode2/radioThrottleDown.png</file>
<file alias="radioSwitchMinMax.png">resources/calibration/mode2/radioSwitchMinMax.png</file>
</qresource>
<qresource prefix="/res/styles">
<file alias="style-dark.css">resources/styles/style-dark.css</file>
<file alias="style-light.css">resources/styles/style-light.css</file>
</qresource>
<qresource prefix="/res/fonts">
<file alias="vera.ttf">resources/styles/Vera.ttf</file>
</qresource>
<qresource prefix="/res/audio">
<file alias="Alert">resources/audio/alert.wav</file>
</qresource>
......
......@@ -40,6 +40,14 @@
#include <QDebug>
#include <VideoItem.h>
#include <VideoSurface.h>
#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<VideoItem>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoItem");
qmlRegisterUncreatableType<VideoSurface>("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)
......
# 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.
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief QGC Video Item
* @author Gus Grubba <mavlink@grubba.com>
*/
#include <QtCore/QPointer>
#include <QtQuick/QSGNode>
#include <QtQuick/QSGFlatColorMaterial>
#include "VideoItem.h"
#if defined(QGC_GST_STREAMING)
#include "VideoSurface_p.h"
#endif
#if defined(QGC_GST_STREAMING)
struct VideoItem::Private
{
QPointer<VideoSurface> surface;
bool surfaceDirty;
QRectF targetArea;
};
#endif
VideoItem::VideoItem(QQuickItem *parent)
: QQuickItem(parent)
#if defined(QGC_GST_STREAMING)
, _data(new Private)
#endif
{
#if defined(QGC_GST_STREAMING)
_data->surfaceDirty = true;
setFlag(QQuickItem::ItemHasContents, true);
#endif
}
VideoItem::~VideoItem()
{
#if defined(QGC_GST_STREAMING)
setSurface(0);
delete _data;
#endif
}
VideoSurface *VideoItem::surface() const
{
#if defined(QGC_GST_STREAMING)
return _data->surface.data();
#else
return 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<QSGGeometryNode*>(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
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief QGC Video Item
* @author Gus Grubba <mavlink@grubba.com>
*/
#ifndef VIDEO_ITEM_H
#define VIDEO_ITEM_H
#include <QtQuick/QQuickItem>
#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
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief QGC Video Receiver
* @author Gus Grubba <mavlink@grubba.com>
*/
#include "VideoReceiver.h"
#include <QDebug>
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
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief QGC Video Receiver
* @author Gus Grubba <mavlink@grubba.com>
*/
#ifndef VIDEORECEIVER_H
#define VIDEORECEIVER_H
#include <QObject>
#if defined(QGC_GST_STREAMING)
#include <gst/gst.h>
#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
# -------------------------------------------------
# QGroundControl - Micro Air Vehicle Groundstation
# Please see our website at <http://qgroundcontrol.org>
# Maintainer:
# Lorenz Meier <lm@inf.ethz.ch>
# (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 <http://www.gnu.org/licenses/>.
#
# Author: Gus Grubba <mavlink@grubba.com>
# -------------------------------------------------
#
#-- 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)")
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief QGC Video Surface
* @author Gus Grubba <mavlink@grubba.com>
*/
#if defined(QGC_GST_STREAMING)
#include "VideoSurface_p.h"
#endif
#include "VideoSurface.h"
#include <QtCore/QDebug>
#include <QtQuick/QQuickItem>
VideoSurface::VideoSurface(QObject *parent)
: QObject(parent)
#if defined(QGC_GST_STREAMING)
, _data(new VideoSurfacePrivate)
#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
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief QGC Video Surface
* @author Gus Grubba <mavlink@grubba.com>
*/
#ifndef VIDEO_SURFACE_H
#define VIDEO_SURFACE_H
#include <QtCore/QObject>
#if defined(QGC_GST_STREAMING)
#include <gst/gst.h>
#endif
#if defined(QGC_GST_STREAMING)
class VideoSurfacePrivate;
#endif
class VideoSurface : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(VideoSurface)
public:
explicit VideoSurface(QObject *parent = 0);
virtual ~VideoSurface();
/*! Returns the video sink element that provides this surface's image.
* The element will be constructed the first time that this function
* is called. The surface will always keep a reference to this element.
*/
#if defined(QGC_GST_STREAMING)
GstElement* videoSink() 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
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief QGC Video Surface (Private Interface)
* @author Gus Grubba <mavlink@grubba.com>
*/
#ifndef VIDEO_SURFACE_P_H
#define VIDEO_SURFACE_P_H
#include "VideoSurface.h"
#include "VideoItem.h"
class VideoSurfacePrivate
{
public:
VideoSurfacePrivate()
: videoSink(NULL)
{
}
QSet<VideoItem*> items;
GstElement* videoSink;
};
#endif // VIDEO_SURFACE_P_H
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <mavlink@grubba.com>
*/
#include "basedelegate.h"
#include <QCoreApplication>
BaseDelegate::BaseDelegate(GstElement * sink, QObject * parent)
: QObject(parent)
, m_colorsDirty(true)
, m_brightness(0)
, m_contrast(0)
, m_hue(0)
, m_saturation(0)
, m_pixelAspectRatio(1, 1)
, m_forceAspectRatioDirty(true)
, m_forceAspectRatio(false)
, m_formatDirty(true)
, m_isActive(false)
, m_buffer(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<BufferEvent*>(event);
Q_ASSERT(bufEvent);
GST_TRACE_OBJECT(m_sink, "Received buffer %" GST_PTR_FORMAT, bufEvent->buffer);
if (isActive()) {
gst_buffer_replace (&m_buffer, bufEvent->buffer);
update();
}
return true;
}
case BufferFormatEventType:
{
BufferFormatEvent *bufFmtEvent = dynamic_cast<BufferFormatEvent*>(event);
Q_ASSERT(bufFmtEvent);
GST_TRACE_OBJECT (m_sink, "Received buffer format event. New format: %s",
gst_video_format_to_string(bufFmtEvent->format.videoFormat()));
m_formatDirty = true;
m_bufferFormat = bufFmtEvent->format;
return true;
}
case DeactivateEventType:
{
GST_LOG_OBJECT(m_sink, "Received deactivate event");
gst_buffer_replace (&m_buffer, NULL);
update();
return true;
}
default:
return QObject::event(event);
}
}
void BaseDelegate::update()
{
g_signal_emit_by_name(m_sink, "update");
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <mavlink@grubba.com>
*/
#ifndef BASEDELEGATE_H
#define BASEDELEGATE_H
#include <gst/gst.h>
#include "../gstqtvideosinkplugin.h" //for debug category
#include "../utils/bufferformat.h"
#include "../utils/utils.h"
#include <QObject>
#include <QEvent>
#include <QReadWriteLock>
class BaseDelegate : public QObject
{
Q_OBJECT
public:
enum EventType {
BufferEventType = QEvent::User,
BufferFormatEventType,
DeactivateEventType
};
//-------------------------------------
class BufferEvent : public QEvent
{
public:
inline BufferEvent(GstBuffer *buf)
: QEvent(static_cast<QEvent::Type>(BufferEventType)),
buffer(gst_buffer_ref(buf))
{}
virtual ~BufferEvent() {
gst_buffer_unref(buffer);
}
GstBuffer *buffer;
};
class BufferFormatEvent : public QEvent
{
public:
inline BufferFormatEvent(const BufferFormat &format)
: QEvent(static_cast<QEvent::Type>(BufferFormatEventType)),
format(format)
{}
BufferFormat format;
};
class DeactivateEvent : public QEvent
{
public:
inline DeactivateEvent()
: QEvent(static_cast<QEvent::Type>(DeactivateEventType))
{
}
};
//-------------------------------------
explicit BaseDelegate(GstElement *sink, QObject *parent = 0);
virtual ~BaseDelegate();
bool isActive() const;
void setActive(bool playing);
// GstColorBalance interface
int brightness() const;
void setBrightness(int brightness);
int contrast() const;
void setContrast(int contrast);
int hue() const;
void setHue(int hue);
int saturation() const;
void setSaturation(int saturation);
// pixel-aspect-ratio property
Fraction pixelAspectRatio() const;
void setPixelAspectRatio(const Fraction & f);
// force-aspect-ratio property
bool forceAspectRatio() const;
void setForceAspectRatio(bool force);
protected:
// internal event handling
virtual bool event(QEvent *event);
// tells the surface to repaint itself
virtual void update();
protected:
// colorbalance interface properties
mutable QReadWriteLock m_colorsLock;
bool m_colorsDirty;
int m_brightness;
int m_contrast;
int m_hue;
int m_saturation;
// pixel-aspect-ratio property
mutable QReadWriteLock m_pixelAspectRatioLock;
Fraction m_pixelAspectRatio;
// force-aspect-ratio property
mutable QReadWriteLock m_forceAspectRatioLock;
bool m_forceAspectRatioDirty;
bool m_forceAspectRatio;
// format caching
bool m_formatDirty;
BufferFormat m_bufferFormat;
PaintAreas m_areas;
// whether the sink is active (PAUSED or PLAYING)
mutable QReadWriteLock m_isActiveLock;
bool m_isActive;
// the buffer to be drawn next
GstBuffer *m_buffer;
// the video sink element
GstElement * const m_sink;
};
#endif // BASEDELEGATE_H
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <mavlink@grubba.com>
*/
#include "qtquick2videosinkdelegate.h"
#include "../painters/videonode.h"
QtQuick2VideoSinkDelegate::QtQuick2VideoSinkDelegate(GstElement *sink, QObject *parent)
: BaseDelegate(sink, parent)
{
}
QSGNode* QtQuick2VideoSinkDelegate::updateNode(QSGNode *node, const QRectF & targetArea)
{
GST_TRACE_OBJECT(m_sink, "updateNode called");
bool sgnodeFormatChanged = false;
VideoNode *vnode = dynamic_cast<VideoNode*>(node);
if (!vnode) {
GST_INFO_OBJECT(m_sink, "creating new VideoNode");
vnode = new VideoNode;
}
if (!m_buffer) {
if (vnode->materialType() != VideoNode::MaterialTypeSolidBlack) {
vnode->setMaterialTypeSolidBlack();
sgnodeFormatChanged = true;
}
if (sgnodeFormatChanged || targetArea != m_areas.targetArea) {
m_areas.targetArea = targetArea;
vnode->updateGeometry(m_areas);
}
} else {
//change format before geometry, so that we change QSGGeometry as well
if (m_formatDirty) {
vnode->changeFormat(m_bufferFormat);
sgnodeFormatChanged = true;
}
//recalculate the video area if needed
QReadLocker forceAspectRatioLocker(&m_forceAspectRatioLock);
if (sgnodeFormatChanged || targetArea != m_areas.targetArea || m_forceAspectRatioDirty) {
m_forceAspectRatioDirty = false;
QReadLocker pixelAspectRatioLocker(&m_pixelAspectRatioLock);
Qt::AspectRatioMode aspectRatioMode = m_forceAspectRatio ?
Qt::KeepAspectRatio : Qt::IgnoreAspectRatio;
m_areas.calculate(targetArea, m_bufferFormat.frameSize(),
m_bufferFormat.pixelAspectRatio(), m_pixelAspectRatio,
aspectRatioMode);
pixelAspectRatioLocker.unlock();
GST_LOG_OBJECT(m_sink,
"Recalculated paint areas: "
"Frame size: " QSIZE_FORMAT ", "
"target area: " QRECTF_FORMAT ", "
"video area: " QRECTF_FORMAT ", "
"black1: " QRECTF_FORMAT ", "
"black2: " QRECTF_FORMAT,
QSIZE_FORMAT_ARGS(m_bufferFormat.frameSize()),
QRECTF_FORMAT_ARGS(m_areas.targetArea),
QRECTF_FORMAT_ARGS(m_areas.videoArea),
QRECTF_FORMAT_ARGS(m_areas.blackArea1),
QRECTF_FORMAT_ARGS(m_areas.blackArea2)
);
vnode->updateGeometry(m_areas);
}
forceAspectRatioLocker.unlock();
if (m_formatDirty) {
m_formatDirty = false;
//make sure to update the colors after changing material
m_colorsDirty = true;
}
QReadLocker colorsLocker(&m_colorsLock);
if (m_colorsDirty) {
vnode->updateColors(m_brightness, m_contrast, m_hue, m_saturation);
m_colorsDirty = false;
}
colorsLocker.unlock();
vnode->setCurrentFrame(m_buffer);
}
return vnode;
}
/*
Copyright (C) 2013 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <mavlink@grubba.com>
*/
#ifndef QTQUICK2VIDEOSINKDELEGATE_H
#define QTQUICK2VIDEOSINKDELEGATE_H
#include "basedelegate.h"
#include <QtQuick/QSGNode>
class QtQuick2VideoSinkDelegate : public BaseDelegate
{
Q_OBJECT
public:
explicit QtQuick2VideoSinkDelegate(GstElement * sink, QObject * parent = 0);
QSGNode *updateNode(QSGNode *node, const QRectF & targetArea);
};
#endif // QTQUICK2VIDEOSINKDELEGATE_H
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <mavlink@grubba.com>
*/
#include "qtvideosinkdelegate.h"
#include "../painters/genericsurfacepainter.h"
#include "../painters/openglsurfacepainter.h"
#include <QStack>
#include <QPainter>
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<const char *>(glGetString(GL_EXTENSIONS)));
GST_LOG_OBJECT(m_sink, "Available GL extensions: %s", extensions.constData());
#ifndef QT_OPENGL_ES
if (extensions.contains("ARB_fragment_program"))
m_supportedPainters |= ArbFp;
#endif
#ifndef QT_OPENGL_ES_2
if (QGLShaderProgram::hasOpenGLShaderPrograms(m_glContext)
&& extensions.contains("ARB_shader_objects"))
#endif
m_supportedPainters |= Glsl;
}
GST_LOG_OBJECT(m_sink, "Done setting GL context. m_supportedPainters=%x", (int) m_supportedPainters);
}
#endif
void QtVideoSinkDelegate::changePainter(const BufferFormat & format)
{
if (m_painter) {
m_painter->cleanup();
if (G_UNLIKELY(!m_painter->supportsFormat(format.videoFormat()))) {
destroyPainter();
}
}
QStack<PainterType> possiblePainters;
if (GenericSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
possiblePainters.push(Generic);
}
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
if (OpenGLSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
if (m_supportedPainters & ArbFp) {
possiblePainters.push(ArbFp);
}
if (m_supportedPainters & Glsl) {
possiblePainters.push(Glsl);
}
}
#endif
while (!possiblePainters.isEmpty()) {
if (!m_painter) {
PainterType type = possiblePainters.pop();
switch(type) {
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
case Glsl:
GST_LOG_OBJECT(m_sink, "Creating GLSL painter");
m_painter = new GlslSurfacePainter;
break;
# ifndef QT_OPENGL_ES
case ArbFp:
GST_LOG_OBJECT(m_sink, "Creating ARB Fragment Shader painter");
m_painter = new ArbFpSurfacePainter;
break;
# endif
#endif
case Generic:
GST_LOG_OBJECT(m_sink, "Creating Generic painter");
m_painter = new GenericSurfacePainter;
break;
default:
Q_ASSERT(false);
}
}
try {
m_painter->init(format);
return;
} catch (const QString & error) {
GST_ELEMENT_WARNING(m_sink, RESOURCE, FAILED,
("Failed to start painter"), ("%s", error.toUtf8().constData()));
delete m_painter;
m_painter = 0;
}
}
GST_ELEMENT_ERROR(m_sink, RESOURCE, FAILED,
("Failed to create a painter for the given format"), (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);
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <mavlink@grubba.com>
*/
#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
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
Copyright (C) 2012 Collabora Ltd. <info@collabora.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <mavlink@grubba.com>
*/
#include "qwidgetvideosinkdelegate.h"
#include <QPainter>
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();
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment