Commit 4b4f758b authored by Gus Grubba's avatar Gus Grubba Committed by dogmaphobic

Video Streaming

parent bffd454c
......@@ -652,6 +652,17 @@ SOURCES += \
src/FactSystem/ParameterLoader.cc \
src/FactSystem/FactControls/FactPanelController.cc \
# Video Streaming
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 {
......
This diff is collapsed.
......@@ -40,6 +40,14 @@
#include <QDebug>
#if defined(QGC_GST_STREAMING)
#include <videoitem.h>
#include <videosurface.h>
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);
}
#if defined(QGC_GST_STREAMING)
//----------------------------------------------------------------
//-- Video Streaming
qmlRegisterType<VideoItem>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoItem");
qmlRegisterUncreatableType<VideoSurface>("QGroundControl.QgcQtGStreamer", 1, 0, "VideoSurface", QLatin1String("VideoSurface from QML is not supported"));
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)
......
......@@ -50,6 +50,7 @@ public:
Q_PROPERTY(bool isAndroid READ isAndroid CONSTANT)
Q_PROPERTY(bool isiOS READ isiOS CONSTANT)
Q_PROPERTY(bool isMobile READ isMobile CONSTANT)
Q_PROPERTY(bool hasVideo READ hasVideo CONSTANT)
//! Used to trigger a \c Canvas element repaint.
/*!
......@@ -110,6 +111,12 @@ public:
bool isMobile () { return false; }
#endif
#if defined(QGC_GST_STREAMING)
bool hasVideo () { return true; }
#else
bool hasVideo () { return false; }
#endif
signals:
void repaintRequested(void);
......
/*=====================================================================
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"
#include "videosurface_p.h"
struct VideoItem::Private
{
QPointer<VideoSurface> surface;
bool surfaceDirty;
QRectF targetArea;
};
VideoItem::VideoItem(QQuickItem *parent)
: QQuickItem(parent), _data(new Private)
{
_data->surfaceDirty = true;
setFlag(QQuickItem::ItemHasContents, true);
}
VideoItem::~VideoItem()
{
setSurface(0);
delete _data;
}
VideoSurface *VideoItem::surface() const
{
return _data->surface.data();
}
void VideoItem::setSurface(VideoSurface *surface)
{
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);
}
}
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;
}
/*=====================================================================
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:
/*! Reimplemented from QQuickItem. */
virtual QSGNode* updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
private:
struct Private;
Private* const _data;
};
#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)
, _pipeline(NULL)
, _videoSink(NULL)
{
}
VideoReceiver::~VideoReceiver()
{
stop();
setVideoSink(NULL);
}
void VideoReceiver::setVideoSink(GstElement* sink)
{
if (_videoSink) {
gst_object_unref(_videoSink);
_videoSink = NULL;
}
if (sink) {
_videoSink = sink;
gst_object_ref_sink(_videoSink);
}
}
void VideoReceiver::start()
{
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) != 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;
}
}
}
void VideoReceiver::stop()
{
if (_pipeline != NULL) {
gst_element_set_state(_pipeline, GST_STATE_NULL);
gst_object_unref(_pipeline);
_pipeline = NULL;
}
}
void VideoReceiver::setUri(const QString & uri)
{
stop();
_uri = uri;
}
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;
}
}
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;
}
/*=====================================================================
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>
#include <gst/gst.h>
class VideoReceiver : public QObject
{
Q_OBJECT
public:
explicit VideoReceiver(QObject* parent = 0);
~VideoReceiver();
void setVideoSink(GstElement* sink);
public Q_SLOTS:
void start ();
void stop ();
void setUri (const QString& uri);
private:
void _onBusMessage(GstMessage* message);
static gboolean _onBusMessage(GstBus* bus, GstMessage* msg, gpointer data);
QString _uri;
GstElement* _pipeline;
GstElement* _videoSink;
};
#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")
DEFINES += QGC_GST_STREAMING
PKGCONFIG += gstreamer-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")
DEFINES += QGC_GST_STREAMING
CONFIG += VideoEnabled
INCLUDEPATH += $$GST_ROOT/Headers
LIBS += -F/Library/Frameworks -framework GStreamer
}
}
VideoEnabled {
DEFINES += \
GST_PLUGIN_BUILD_STATIC \
QTGLVIDEOSINK_NAME=qt5glvideosink \
QTVIDEOSINK_NAME=qt5videosink
#QT_NO_KEYWORDS \
INCLUDEPATH += \
$$PWD \
$$PWD/gstqtvideosink \
$$PWD/gstqtvideosink/delegates \
$$PWD/gstqtvideosink/painters \
$$PWD/gstqtvideosink/utils \
HEADERS += \
$$PWD/VideoItem.h \
$$PWD/VideoReceiver.h \
$$PWD/VideoSurface.h \
$$PWD/VideoSurface_p.h \
SOURCES += \
$$PWD/VideoItem.cc \
$$PWD/VideoReceiver.cc \
$$PWD/VideoSurface.cc \
#-- 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>
*/
#include "videosurface_p.h"
#include <QtCore/QDebug>
#include <QtQuick/QQuickItem>
VideoSurface::VideoSurface(QObject *parent)
: QObject(parent)
, _data(new VideoSurfacePrivate)
{
}
VideoSurface::~VideoSurface()
{
if (_data->videoSink != NULL) {
gst_element_set_state(_data->videoSink, GST_STATE_NULL);
}
delete _data;
}
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;