1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-11-29 07:24:13 +01:00

LP-360 add video background to PFD

This commit is contained in:
Philippe Renon 2016-07-05 23:37:28 +02:00
parent e30340237f
commit 05112f9c1b
22 changed files with 1105 additions and 49 deletions

View File

@ -27,6 +27,6 @@ macx {
linux|win32 {
CONFIG += link_pkgconfig
PKGCONFIG += glib-2.0 gobject-2.0
PKGCONFIG += gstreamer-1.0 gstreamer-video-1.0
PKGCONFIG += gstreamer-1.0 gstreamer-app-1.0 gstreamer-video-1.0
opencv:PKGCONFIG += opencv
}

View File

@ -27,9 +27,16 @@
#include "OSGImageNode.hpp"
#include <osg/Texture2D>
#include "utils/imagesource.hpp"
#include <osgDB/ReadFile>
#ifdef USE_GSTREAMER
#include "utils/gstreamer/gstimagesource.hpp"
#endif
#include <osg/Image>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Texture2D>
#include <QUrl>
#include <QDebug>
@ -45,44 +52,107 @@ private:
osg::ref_ptr<osg::Texture2D> texture;
public:
QUrl url;
ImageSource *imageSource;
Hidden(OSGImageNode *self) : QObject(self), self(self), url()
{}
public:
QUrl imageUrl;
Hidden(OSGImageNode *self) : QObject(self), self(self), imageSource(NULL), imageUrl()
{
if (imageSource) {
delete imageSource;
}
}
osg::Node *createNode()
{
osg::Drawable *quad = osg::createTexturedQuadGeometry(osg::Vec3(0, 0, 0), osg::Vec3(1, 0, 0), osg::Vec3(0, 1, 0));
osg::Geode *geode = new osg::Geode;
geode->addDrawable(quad);
geode->setStateSet(createState());
return geode;
}
osg::StateSet *createState()
osg::Image *loadImage()
{
texture = new osg::Texture2D;
// create the StateSet to store the texture data
osg::StateSet *stateset = new osg::StateSet;
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
return stateset;
if (!imageSource) {
if (imageUrl.scheme() == "gst") {
#ifdef USE_GSTREAMER
imageSource = new GstImageSource();
#else
qWarning() << "gstreamer image source is not supported";
#endif
} else {
imageSource = new ImageSource();
}
}
return imageSource->createImage(imageUrl);
}
void updateImageFile()
{
qDebug() << "OSGImageNode::updateImageFile - reading image file" << url.path();
osg::Image *image = osgDB::readImageFile(url.path().toStdString());
if (texture.valid()) {
texture->setImage(image);
update();
}
void update()
{
osg::Image *image = loadImage();
// qDebug() << "OSGImageNode::update" << image;
osg::Node *geode = createGeodeForImage(image);
self->setNode(geode);
}
osg::Geode *createGeodeForImage(osg::Image *image)
{
// vertex
osg::Vec3Array *coords = new osg::Vec3Array(4);
(*coords)[0].set(0, 1, 0);
(*coords)[1].set(0, 0, 0);
(*coords)[2].set(1, 0, 0);
(*coords)[3].set(1, 1, 0);
// texture coords
osg::Vec2Array *texcoords = new osg::Vec2Array(4);
float x_b = 0.0f;
float x_t = 1.0f;
float y_b = (image->getOrigin() == osg::Image::BOTTOM_LEFT) ? 0.0f : 1.0f;
float y_t = (image->getOrigin() == osg::Image::BOTTOM_LEFT) ? 1.0f : 0.0f;
(*texcoords)[0].set(x_b, y_t);
(*texcoords)[1].set(x_b, y_b);
(*texcoords)[2].set(x_t, y_b);
(*texcoords)[3].set(x_t, y_t);
// color
osg::Vec4Array *color = new osg::Vec4Array(1);
(*color)[0].set(1.0f, 1.0f, 1.0f, 1.0f);
// setup the geometry
osg::Geometry *geom = new osg::Geometry;
geom->setVertexArray(coords);
geom->setTexCoordArray(0, texcoords);
geom->setColorArray(color, osg::Array::BIND_OVERALL);
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));
// set up the texture.
osg::Texture2D *texture = new osg::Texture2D;
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
texture->setResizeNonPowerOfTwoHint(false);
texture->setImage(image);
// set up the state.
osg::StateSet *state = new osg::StateSet;
state->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
state->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
geom->setStateSet(state);
// set up the geode.
osg::Geode *geode = new osg::Geode;
geode->addDrawable(geom);
return geode;
}
};
@ -96,17 +166,17 @@ OSGImageNode::~OSGImageNode()
delete h;
}
const QUrl OSGImageNode::imageFile() const
const QUrl OSGImageNode::imageUrl() const
{
return h->url;
return h->imageUrl;
}
void OSGImageNode::setImageFile(const QUrl &url)
void OSGImageNode::setImageUrl(QUrl &url)
{
if (h->url != url) {
h->url = url;
if (h->imageUrl != url) {
h->imageUrl = url;
setDirty(ImageFile);
emit imageFileChanged(url);
emit imageUrlChanged(url);
}
}

View File

@ -25,8 +25,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_OSGQTQUICK_IMAGENODE_H_
#define _H_OSGQTQUICK_IMAGENODE_H_
#pragma once
#include "Export.hpp"
#include "OSGNode.hpp"
@ -35,7 +34,7 @@
namespace osgQtQuick {
class OSGQTQUICK_EXPORT OSGImageNode : public OSGNode {
Q_OBJECT Q_PROPERTY(QUrl imageFile READ imageFile WRITE setImageFile NOTIFY imageFileChanged)
Q_OBJECT Q_PROPERTY(QUrl imageUrl READ imageUrl WRITE setImageUrl NOTIFY imageUrlChanged)
typedef OSGNode Inherited;
@ -43,11 +42,11 @@ public:
OSGImageNode(QObject *parent = 0);
virtual ~OSGImageNode();
const QUrl imageFile() const;
void setImageFile(const QUrl &url);
const QUrl imageUrl() const;
void setImageUrl(QUrl &url);
signals:
void imageFileChanged(const QUrl &url);
void imageUrlChanged(const QUrl &url);
protected:
virtual osg::Node *createNode();
@ -58,5 +57,3 @@ private:
Hidden *const h;
};
} // namespace osgQtQuick
#endif // _H_OSGQTQUICK_IMAGENODE_H_

View File

@ -59,7 +59,7 @@ public:
osg::Node *nodeToUpdate() const
{
return manipulator->getNode();
return manipulator ? manipulator->getNode() : NULL;
}
void update()

View File

@ -38,13 +38,15 @@ HEADERS += \
osgearth.h \
utils/qtwindowingsystem.h \
utils/utility.h \
utils/shapeutils.h
utils/shapeutils.h \
utils/imagesource.hpp
SOURCES += \
osgearth.cpp \
utils/qtwindowingsystem.cpp \
utils/utility.cpp \
utils/shapeutils.cpp
utils/shapeutils.cpp \
utils/imagesource.cpp
HEADERS += \
osgQtQuick/Export.hpp \
@ -83,6 +85,14 @@ SOURCES += \
osgQtQuick/ga/OSGNodeTrackerManipulator.cpp \
osgQtQuick/ga/OSGTrackballManipulator.cpp
gstreamer:HEADERS += \
utils/gstreamer/gstimagestream.hpp \
utils/gstreamer/gstimagesource.hpp
gstreamer:SOURCES += \
utils/gstreamer/gstimagestream.cpp \
utils/gstreamer/gstimagesource.cpp
osgearth:HEADERS += \
osgQtQuick/OSGSkyNode.hpp \
osgQtQuick/OSGGeoTransformNode.hpp

View File

@ -14,6 +14,11 @@ contains(QT_ARCH, x86_64) {
LIB_DIR_NAME = lib
}
gstreamer {
include(../gstreamer/gstreamer.pri)
include(../gstreamer/gstreamer_dependencies.pri)
}
osg {
OSG_SDK_DIR = $$clean_path($$(OSG_SDK_DIR))
message(Using osg from here: $$OSG_SDK_DIR)

View File

@ -0,0 +1,90 @@
/**
******************************************************************************
*
* @file gstimagesource.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
* @addtogroup
* @{
* @addtogroup
* @{
* @brief
*****************************************************************************/
/*
* This program 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.
*
* 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "gstimagesource.hpp"
#include "gstimagestream.hpp"
#include <osg/Image>
#include <QUrl>
#include <QDebug>
GstImageSource::GstImageSource() : is(NULL)
{}
GstImageSource::~GstImageSource()
{
if (is) {
delete is;
}
}
osg::Image *GstImageSource::createImage(QUrl &url)
{
// qDebug() << "GstImageSource::createImage - reading image file" << url.path();
QString pipeline = url.query(QUrl::FullyDecoded);
GSTImageStream *is = new GSTImageStream();
is->setPipeline(pipeline.toStdString());
this->is = is;
play();
return this->is;
}
void GstImageSource::play()
{
if (is) {
is->play();
}
}
void GstImageSource::pause()
{
if (is) {
is->pause();
}
}
void GstImageSource::rewind()
{
if (is) {
is->rewind();
}
}
void GstImageSource::seek(double time)
{
if (is) {
is->seek(time);
}
}

View File

@ -0,0 +1,52 @@
/**
******************************************************************************
*
* @file gstimagesource.hpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
* @addtogroup
* @{
* @addtogroup
* @{
* @brief
*****************************************************************************/
/*
* This program 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.
*
* 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "utils/imagesource.hpp"
namespace osg {
class Image;
}
class GSTImageStream;
class GstImageSource : public ImageSource {
public:
GstImageSource();
virtual ~GstImageSource();
virtual osg::Image *createImage(QUrl &url);
virtual void play();
virtual void pause();
virtual void rewind();
virtual void seek(double time);
private:
GSTImageStream *is;
};

View File

@ -0,0 +1,384 @@
/**
******************************************************************************
*
* @file gstimagestream.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
* OpenSceneGraph, http://www.openscenegraph.org/
* Julen Garcia <jgarcia@vicomtech.org>
* @addtogroup
* @{
* @addtogroup
* @{
* @brief
*****************************************************************************/
/*
* This program 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.
*
* 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "gstimagestream.hpp"
#include "gst_util.h"
#include <osgDB/ReaderWriter>
#include <QDebug>
GSTImageStream::GSTImageStream() :
_loop(0),
_pipeline(0),
_internal_buffer(0),
_width(0),
_height(0)
{
setOrigin(osg::Image::TOP_LEFT);
_loop = g_main_loop_new(NULL, FALSE);
}
GSTImageStream::GSTImageStream(const GSTImageStream & image, const osg::CopyOp & copyop) :
osg::ImageStream(image, copyop), OpenThreads::Thread(),
_loop(0),
_pipeline(0),
_internal_buffer(0),
_width(0),
_height(0)
{
setOrigin(osg::Image::TOP_LEFT);
_loop = g_main_loop_new(NULL, FALSE);
}
GSTImageStream::~GSTImageStream()
{
gst_element_set_state(_pipeline, GST_STATE_NULL);
gst_element_get_state(_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); // wait until the state changed
g_main_loop_quit(_loop);
g_main_loop_unref(_loop);
free(_internal_buffer);
}
// osgDB::ReaderWriter::ReadResult readImage(const std::string & filename, const osgDB::ReaderWriter::Options *options)
// {
// const std::string ext = osgDB::getLowerCaseFileExtension(filename);
//
//// if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
//
// const std::string path = osgDB::containsServerAddress(filename) ?
// filename :
// osgDB::findDataFile(filename, options);
//
// if (path.empty()) {
// return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
// }
//
// osg::ref_ptr<GSTImageStream> imageStream = new GSTImageStream();
//
// if (!imageStream->open(filename)) {
// return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
// }
//
// return imageStream.release();
// }
bool GSTImageStream::setPipeline(const std::string &pipeline)
{
GError *error = NULL;
// TODO not the most appropriate place to do that...
gst::init(NULL, NULL);
gchar *string = g_strdup_printf("%s", pipeline.c_str());
_pipeline = gst_parse_launch(string, &error);
// TODO make sure that there is "! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true"
// TOOD remove the need for a videoconvert element by adapting dynamically to format
// TODO try to use GL buffers...
g_free(string);
if (error) {
g_printerr("Error: %s\n", error->message);
g_error_free(error);
// TODO submit fix to osg...
return false;
}
if (_pipeline == NULL) {
return false;
}
// bus
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline));
gst_bus_add_watch(bus, (GstBusFunc)on_message, this);
gst_object_unref(bus);
// sink
GstElement *sink = gst_bin_get_by_name(GST_BIN(_pipeline), "sink");
g_signal_connect(sink, "new-sample", G_CALLBACK(on_new_sample), this);
g_signal_connect(sink, "new-preroll", G_CALLBACK(on_new_preroll), this);
gst_object_unref(sink);
gst_element_set_state(_pipeline, GST_STATE_PAUSED);
gst_element_get_state(_pipeline, 0, 0, GST_CLOCK_TIME_NONE); // wait until the state changed
if (_width == 0 || _height == 0) {
// no valid image has been setup by a on_new_preroll() call.
return false;
}
// setLoopingMode(osg::ImageStream::NO_LOOPING);
// start the thread to run gstreamer main loop
start();
return true;
}
/*
bool GSTImageStream::open(const std::string & filename)
{
setFileName(filename);
GError *error = NULL;
// get stream info
bool has_audio_stream = false;
gchar *uri = g_filename_to_uri(filename.c_str(), NULL, NULL);
if (uri != 0 && gst_uri_is_valid(uri)) {
GstDiscoverer *item = gst_discoverer_new(1 * GST_SECOND, &error);
GstDiscovererInfo *info = gst_discoverer_discover_uri(item, uri, &error);
GList *audio_list = gst_discoverer_info_get_audio_streams(info);
if (g_list_length(audio_list) > 0) {
has_audio_stream = true;
}
gst_discoverer_info_unref(info);
g_free(uri);
}
// build pipeline
const gchar *audio_pipe = "";
if (has_audio_stream) {
audio_pipe = "deco. ! queue ! audioconvert ! autoaudiosink";
}
gchar *string = g_strdup_printf("filesrc location=%s ! \
decodebin name=deco \
deco. ! queue ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true \
%s", filename.c_str(), audio_pipe);
_pipeline = gst_parse_launch(string, &error);
g_free(string);
if (error) {
g_printerr("Error: %s\n", error->message);
g_error_free(error);
}
if (_pipeline == NULL) {
return false;
}
// bus
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline));
gst_bus_add_watch(bus, (GstBusFunc)on_message, this);
gst_object_unref(bus);
// sink
GstElement *sink = gst_bin_get_by_name(GST_BIN(_pipeline), "sink");
g_signal_connect(sink, "new-sample", G_CALLBACK(on_new_sample), this);
g_signal_connect(sink, "new-preroll", G_CALLBACK(on_new_preroll), this);
gst_object_unref(sink);
gst_element_set_state(_pipeline, GST_STATE_PAUSED);
gst_element_get_state(_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); // wait until the state changed
if (_width == 0 || _height == 0) {
// no valid image has been setup by a on_new_preroll() call.
return false;
}
// setLoopingMode(osg::ImageStream::NO_LOOPING);
// start the thread to run gstreamer main loop
start();
return true;
}
*/
// ** Controls **
void GSTImageStream::play()
{
gst_element_set_state(_pipeline, GST_STATE_PLAYING);
}
void GSTImageStream::pause()
{
gst_element_set_state(_pipeline, GST_STATE_PAUSED);
}
void GSTImageStream::rewind()
{
gst_element_seek_simple(_pipeline, GST_FORMAT_TIME, GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), 0);
}
void GSTImageStream::seek(double time)
{
gst_element_seek_simple(_pipeline, GST_FORMAT_TIME, GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), time * GST_MSECOND);
}
// ** Callback implementations **
GstFlowReturn GSTImageStream::on_new_sample(GstAppSink *appsink, GSTImageStream *user_data)
{
// get the buffer from appsink
GstSample *sample = gst_app_sink_pull_sample(appsink);
GstBuffer *buffer = gst_sample_get_buffer(sample);
if (!user_data->allocateInternalBuffer(sample)) {
gst_sample_unref(sample);
return GST_FLOW_ERROR;
}
// upload data
GstMapInfo info;
if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) {
qWarning() << "Failed to map buffer";
// TODO
}
gsize size = gst_buffer_extract(buffer, 0, user_data->_internal_buffer, info.size);
if (size != info.size) {
qWarning() << "GSTImageStream::on_new_sample : extracted" << size << "/" << info.size;
// TODO
}
// data has been modified so dirty the image so the texture will be updated
user_data->dirty();
// clean resources
gst_buffer_unmap(buffer, &info);
gst_sample_unref(sample);
return GST_FLOW_OK;
}
GstFlowReturn GSTImageStream::on_new_preroll(GstAppSink *appsink, GSTImageStream *user_data)
{
qDebug() << "ON NEW PREROLL";
// get the sample from appsink
GstSample *sample = gst_app_sink_pull_preroll(appsink);
if (!user_data->allocateInternalBuffer(sample)) {
gst_sample_unref(sample);
return GST_FLOW_ERROR;
}
// clean resources
gst_sample_unref(sample);
return GST_FLOW_OK;
}
gboolean GSTImageStream::on_message(GstBus *bus, GstMessage *message, GSTImageStream *user_data)
{
if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) {
if (user_data->getLoopingMode() == osg::ImageStream::LOOPING) {
user_data->rewind();
}
}
return true;
}
bool GSTImageStream::allocateInternalBuffer(GstSample *sample)
{
// get sample info
GstCaps *caps = gst_sample_get_caps(sample);
GstStructure *structure = gst_caps_get_structure(caps, 0);
/*
GstVideoInfo info;
if (!gst_video_info_from_caps (&info, caps)) {
gchar *caps_str = gst_caps_to_string (caps);
GST_ERROR ("Failed to get video info from caps %s", caps_str);
//g_set_error (err, GST_CORE_ERROR, GST_CORE_ERROR_NEGOTIATION,
// "Failed to get video info from caps %s", caps_str);
g_free (caps_str);
return FALSE;
GstVideoFormat format;
format = GST_VIDEO_INFO_FORMAT (info);
*/
int width;
int height;
gst_structure_get_int(structure, "width", &width);
gst_structure_get_int(structure, "height", &height);
if (width <= 0 || height <= 0) {
qCritical() << "invalid video size: width=" << width << ", height=" << height;
return false;
}
if (_width != width || _height != height) {
_width = width;
_height = height;
int row_width = width * 3;
if ((row_width % 4) != 0) {
row_width += (4 - (row_width % 4));
}
// qDebug() << "image width=" << width << ", height=" << height << row_width << (row_width * height);
// if buffer previously assigned free it before allocating new buffer.
if (_internal_buffer) {
free(_internal_buffer);
}
// allocate buffer
_internal_buffer = (unsigned char *)malloc(sizeof(unsigned char) * row_width * height);
// assign buffer to image
setImage(_width, _height, 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, _internal_buffer, osg::Image::NO_DELETE, 4);
}
return true;
}
void GSTImageStream::run()
{
g_main_loop_run(_loop);
}

View File

@ -0,0 +1,70 @@
/**
******************************************************************************
*
* @file gstimagestream.hpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
* OpenSceneGraph, http://www.openscenegraph.org/
* Julen Garcia <jgarcia@vicomtech.org>
* @addtogroup
* @{
* @addtogroup
* @{
* @brief
*****************************************************************************/
/*
* This program 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.
*
* 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <gst/app/gstappsink.h>
#include <osg/ImageStream>
#include <OpenThreads/Thread>
class GSTImageStream : public osg::ImageStream, public OpenThreads::Thread {
public:
GSTImageStream();
GSTImageStream(const GSTImageStream & image, const osg::CopyOp & copyop = osg::CopyOp::SHALLOW_COPY);
virtual ~GSTImageStream();
META_Object(osgGStreamer, GSTImageStream);
bool setPipeline(const std::string &pipeline);
virtual void play();
virtual void pause();
virtual void rewind();
virtual void seek(double time);
private:
virtual void run();
static gboolean on_message(GstBus *bus, GstMessage *message, GSTImageStream *user_data);
static GstFlowReturn on_new_sample(GstAppSink *appsink, GSTImageStream *user_data);
static GstFlowReturn on_new_preroll(GstAppSink *appsink, GSTImageStream *user_data);
bool allocateInternalBuffer(GstSample *sample);
GMainLoop *_loop;
GstElement *_pipeline;
unsigned char *_internal_buffer;
int _width;
int _height;
};

View File

@ -0,0 +1,40 @@
/**
******************************************************************************
*
* @file imagesource.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
* @addtogroup
* @{
* @addtogroup
* @{
* @brief
*****************************************************************************/
/*
* This program 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.
*
* 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "imagesource.hpp"
#include <osg/Image>
#include <osgDB/ReadFile>
#include <QDebug>
osg::Image *ImageSource::createImage(QUrl &url)
{
qDebug() << "ImageSource::createImage - reading image file" << url.path();
osg::Image *image = osgDB::readImageFile(url.path().toStdString());
return image;
}

View File

@ -0,0 +1,47 @@
/**
******************************************************************************
*
* @file imagesource.hpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
* @addtogroup
* @{
* @addtogroup
* @{
* @brief
*****************************************************************************/
/*
* This program 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.
*
* 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <QUrl>
namespace osg {
class Image;
}
class ImageSource {
public:
ImageSource() {}
virtual ~ImageSource() {}
virtual osg::Image *createImage(QUrl &url);
virtual void play() {}
virtual void pause() {}
virtual void rewind() {}
virtual void seek(double time) {}
};

View File

@ -282,6 +282,19 @@ void PfdQmlContext::resetConsumedEnergy()
batterySettings->setData(batterySettings->getData());
}
QString PfdQmlContext::gstPipeline() const
{
return m_gstPipeline;
}
void PfdQmlContext::setGstPipeline(const QString &arg)
{
if (m_gstPipeline != arg) {
m_gstPipeline = arg;
emit gstPipelineChanged(gstPipeline());
}
}
void PfdQmlContext::loadConfiguration(PfdQmlGadgetConfiguration *config)
{
setSpeedFactor(config->speedFactor());
@ -307,6 +320,9 @@ void PfdQmlContext::loadConfiguration(PfdQmlGadgetConfiguration *config)
// background image
setBackgroundImageFile(config->backgroundImageFile());
// gstreamer pipeline
setGstPipeline(config->gstPipeline());
}

View File

@ -59,6 +59,9 @@ class PfdQmlContext : public QObject {
// background
Q_PROPERTY(QString backgroundImageFile READ backgroundImageFile WRITE setBackgroundImageFile NOTIFY backgroundImageFileChanged)
// gstreamer pipeline
Q_PROPERTY(QString gstPipeline READ gstPipeline WRITE setGstPipeline NOTIFY gstPipelineChanged)
public:
PfdQmlContext(QObject *parent = 0);
virtual ~PfdQmlContext();
@ -102,6 +105,10 @@ public:
QString backgroundImageFile() const;
void setBackgroundImageFile(const QString &arg);
// gstreamer pipeline
QString gstPipeline() const;
void setGstPipeline(const QString &arg);
Q_INVOKABLE void resetConsumedEnergy();
void loadConfiguration(PfdQmlGadgetConfiguration *config);
@ -130,6 +137,8 @@ signals:
void modelFileChanged(QString arg);
void backgroundImageFileChanged(QString arg);
void gstPipelineChanged(QString arg);
private:
// constants
static const QString CONTEXT_PROPERTY_NAME;
@ -156,6 +165,8 @@ private:
QString m_backgroundImageFile;
QString m_gstPipeline;
void addModelDir(QString dir);
};
#endif /* PFDQMLCONTEXT_H_ */

View File

@ -35,6 +35,7 @@
PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(QString classId, QSettings &settings, QObject *parent) :
IUAVGadgetConfiguration(classId, parent)
{
// TODO move to some conversion utility class
m_speedMap[1.0] = "m/s";
m_speedMap[3.6] = "km/h";
m_speedMap[2.2369] = "mph";
@ -73,6 +74,9 @@ PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(QString classId, QSettings
// background image
m_backgroundImageFile = settings.value("backgroundImageFile", "Unknown").toString();
m_backgroundImageFile = Utils::InsertDataPath(m_backgroundImageFile);
// gstreamer pipeline
m_gstPipeline = settings.value("gstPipeline").toString();
}
PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(const PfdQmlGadgetConfiguration &obj) :
@ -104,6 +108,9 @@ PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(const PfdQmlGadgetConfigura
// background image
m_backgroundImageFile = obj.m_backgroundImageFile;
// gstreamer pipeline
m_gstPipeline = obj.m_gstPipeline;
}
/**
@ -152,4 +159,7 @@ void PfdQmlGadgetConfiguration::saveConfig(QSettings &settings) const
// background image
QString backgroundImageFile = Utils::RemoveDataPath(m_backgroundImageFile);
settings.setValue("backgroundImageFile", backgroundImageFile);
// gstreamer pipeline
settings.setValue("gstPipeline", m_gstPipeline);
}

View File

@ -200,6 +200,15 @@ public:
m_backgroundImageFile = fileName;
}
QString gstPipeline() const
{
return m_gstPipeline;
}
void setGstPipeline(const QString &pipeline)
{
m_gstPipeline = pipeline;
}
QMapIterator<double, QString> speedMapIterator()
{
return QMapIterator<double, QString>(m_speedMap);
@ -234,6 +243,8 @@ private:
QString m_backgroundImageFile;
QString m_gstPipeline;
QMap<double, QString> m_speedMap;
QMap<double, QString> m_altitudeMap;
};

View File

@ -111,6 +111,9 @@ QWidget *PfdQmlGadgetOptionsPage::createPage(QWidget *parent)
options_page->backgroundImageFile->setPromptDialogTitle(tr("Choose Background Image File"));
options_page->backgroundImageFile->setPath(m_config->backgroundImageFile());
// gstreamer pipeline
options_page->pipelineTextEdit->setPlainText(m_config->gstPipeline());
#ifndef USE_OSG
options_page->showTerrain->setChecked(false);
options_page->showTerrain->setVisible(false);
@ -170,6 +173,8 @@ void PfdQmlGadgetOptionsPage::apply()
#else
m_config->setModelEnabled(false);
#endif
m_config->setGstPipeline(options_page->pipelineTextEdit->toPlainText());
}
void PfdQmlGadgetOptionsPage::finish()

View File

@ -138,7 +138,7 @@
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<widget class="QWidget" name="terrainTab">
<attribute name="title">
<string>Terrain</string>
</attribute>
@ -295,7 +295,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<widget class="QWidget" name="modelTab">
<attribute name="title">
<string>Model</string>
</attribute>
@ -391,7 +391,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<widget class="QWidget" name="environmentTab">
<attribute name="title">
<string>Environment</string>
</attribute>
@ -511,6 +511,43 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="videoTab">
<attribute name="title">
<string>Video</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Pipeline:</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="pipelineTextEdit">
<property name="plainText">
<string notr="true"/>
</property>
<property name="placeholderText">
<string>&lt;enter your gstreamer pipeline here&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@ -1665,6 +1665,31 @@
<backgroundImageFile>%%DATAPATH%%backgrounds/default_background.png</backgroundImageFile>
</data>
</PFD__PCT__20__PCT__28ArcGis__PCT__29>
<PFD__PCT__20__PCT__28Video__PCT__29>
<configInfo>
<locked>false</locked>
<version>0.0.0</version>
</configInfo>
<data>
<qmlFile>%%DATAPATH%%qml/PfdVideo.qml</qmlFile>
<altitudeFactor>1</altitudeFactor>
<speedFactor>1</speedFactor>
<terrainEnabled>true</terrainEnabled>
<earthFile>%%DATAPATH%%osgearth/arcgis.earth</earthFile>
<cacheOnly>false</cacheOnly>
<latitude>39.6576</latitude>
<longitude>19.8046</longitude>
<altitude>90</altitude>
<timeMode>0</timeMode>
<dateTime>@Variant(AAAAEAAlfhEClAez/w==)</dateTime>
<minAmbientLight>0.50</minAmbientLight>
<modelEnabled>false</modelEnabled>
<modelSelectionMode>1</modelSelectionMode>
<modelFile>%%DATAPATH%%models/multi/test_quad/test_quad_x.3ds</modelFile>
<backgroundImageFile>%%DATAPATH%%backgrounds/default_background.png</backgroundImageFile>
<gstPipeline>ksvideosrc device-index=0 ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true</gstPipeline>
</data>
</PFD__PCT__20__PCT__28Video__PCT__29>
<Model__PCT__20View>
<configInfo>
<locked>false</locked>

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2016 The LibrePilot Project
* Contact: http://www.librepilot.org
*
* This file is part of LibrePilot GCS.
*
* LibrePilot GCS 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.
*
* LibrePilot GCS 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 LibrePilot GCS. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.4
import "pfd"
PfdView {
opaque: false
worldFile: "PfdVideoWorld.qml"
}

View File

@ -55,7 +55,7 @@ Item {
OSGImageNode {
id: backgroundImageNode
imageFile: pfdContext.backgroundImageFile
imageUrl: pfdContext.backgroundImageFile
}
OSGTransformNode {

View File

@ -0,0 +1,149 @@
/*
* Copyright (C) 2016 The LibrePilot Project
* Contact: http://www.librepilot.org
*
* This file is part of LibrePilot GCS.
*
* LibrePilot GCS 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.
*
* LibrePilot GCS 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 LibrePilot GCS. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.4
import Pfd 1.0
import OsgQtQuick 1.0
import "../js/common.js" as Utils
import "../js/uav.js" as UAV
OSGViewport {
id: osgViewport
anchors.fill: parent
focus: true
readonly property real horizontCenter : horizontCenterItem.horizontCenter
// Factor for OSGViewer vertical offset
readonly property double factor: 0.04
// Stretch height and apply offset
//height: height * (1 + factor)
y: -height * factor
sceneNode: billboardNode
camera: camera
updateMode: UpdateMode.Discrete
OSGCamera {
id: camera
fieldOfView: 90
}
OSGBillboardNode {
id: billboardNode
children: [ videoNode ]
}
OSGImageNode {
id: videoNode
imageUrl: "gst://localhost/play?" + encodeURIComponent(pfdContext.gstPipeline)
}
Rectangle {
// using rectangle instead of svg rendered to pixmap
// as it's much more memory efficient
id: world
smooth: true
opacity: 0
property variant scaledBounds: svgRenderer.scaledElementBounds("pfd/pfd.svg", "horizon")
width: Math.round(sceneItem.width * scaledBounds.width / 2) * 2
height: Math.round(sceneItem.height * scaledBounds.height / 2) * 3
property double pitch1DegScaledHeight: (svgRenderer.scaledElementBounds("pfd/pfd.svg", "pitch-90").y -
svgRenderer.scaledElementBounds("pfd/pfd.svg", "pitch90").y) / 180.0
property double pitch1DegHeight: sceneItem.height * pitch1DegScaledHeight
transform: [
Translate {
id: pitchTranslate
x: Math.round((world.parent.width - world.width)/2)
// y is centered around world_center element
y: Math.round(horizontCenter - world.height / 2 + UAV.attitudePitch() * world.pitch1DegHeight)
},
Rotation {
angle: -UAV.attitudeRoll()
origin.x : world.parent.width / 2
origin.y : horizontCenter
}
]
}
Item {
id: pitch_window
property variant scaledBounds: svgRenderer.scaledElementBounds("pfd/pfd.svg", "pitch-window-terrain")
x: Math.floor(scaledBounds.x * sceneItem.width)
y: Math.floor(scaledBounds.y * sceneItem.height) - osgViewport.y
width: Math.floor(scaledBounds.width * sceneItem.width)
height: Math.floor(scaledBounds.height * sceneItem.height)
rotation: -UAV.attitudeRoll()
transformOrigin: Item.Center
smooth: true
clip: true
SvgElementImage {
id: pitch_scale
elementName: "pitch-scale"
//worldView is loaded with Loader, so background element is visible
sceneSize: background.sceneSize
anchors.centerIn: parent
//see comment for world transform
anchors.verticalCenterOffset: attitudeState.pitch * world.pitch1DegHeight
border: 64 // sometimes numbers are excluded from bounding rect
smooth: true
}
SvgElementImage {
id: horizont_line
elementName: "center-line"
opacity: 0.5
// worldView is loaded with Loader, so background element is visible
sceneSize: background.sceneSize
anchors.centerIn: parent
anchors.verticalCenterOffset: UAV.attitudePitch() * world.pitch1DegHeight
border: 1
smooth: true
}
SvgElementImage {
id: pitch_0
elementName: "pitch0"
sceneSize: background.sceneSize
anchors.centerIn: parent
anchors.verticalCenterOffset: UAV.attitudePitch() * world.pitch1DegHeight
border: 1
smooth: true
}
}
}