mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-02-20 10:54:14 +01:00
LP-360 add video background to PFD
This commit is contained in:
parent
e30340237f
commit
05112f9c1b
@ -27,6 +27,6 @@ macx {
|
|||||||
linux|win32 {
|
linux|win32 {
|
||||||
CONFIG += link_pkgconfig
|
CONFIG += link_pkgconfig
|
||||||
PKGCONFIG += glib-2.0 gobject-2.0
|
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
|
opencv:PKGCONFIG += opencv
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,16 @@
|
|||||||
|
|
||||||
#include "OSGImageNode.hpp"
|
#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 <QUrl>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@ -45,44 +52,107 @@ private:
|
|||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> texture;
|
osg::ref_ptr<osg::Texture2D> texture;
|
||||||
|
|
||||||
public:
|
ImageSource *imageSource;
|
||||||
QUrl url;
|
|
||||||
|
|
||||||
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::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;
|
osg::Geode *geode = new osg::Geode;
|
||||||
|
|
||||||
geode->addDrawable(quad);
|
|
||||||
|
|
||||||
geode->setStateSet(createState());
|
|
||||||
|
|
||||||
return geode;
|
return geode;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::StateSet *createState()
|
osg::Image *loadImage()
|
||||||
{
|
{
|
||||||
texture = new osg::Texture2D;
|
if (!imageSource) {
|
||||||
|
if (imageUrl.scheme() == "gst") {
|
||||||
// create the StateSet to store the texture data
|
#ifdef USE_GSTREAMER
|
||||||
osg::StateSet *stateset = new osg::StateSet;
|
imageSource = new GstImageSource();
|
||||||
|
#else
|
||||||
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
|
qWarning() << "gstreamer image source is not supported";
|
||||||
|
#endif
|
||||||
return stateset;
|
} else {
|
||||||
|
imageSource = new ImageSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imageSource->createImage(imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateImageFile()
|
void updateImageFile()
|
||||||
{
|
{
|
||||||
qDebug() << "OSGImageNode::updateImageFile - reading image file" << url.path();
|
update();
|
||||||
osg::Image *image = osgDB::readImageFile(url.path().toStdString());
|
|
||||||
if (texture.valid()) {
|
|
||||||
texture->setImage(image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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) {
|
if (h->imageUrl != url) {
|
||||||
h->url = url;
|
h->imageUrl = url;
|
||||||
setDirty(ImageFile);
|
setDirty(ImageFile);
|
||||||
emit imageFileChanged(url);
|
emit imageUrlChanged(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _H_OSGQTQUICK_IMAGENODE_H_
|
#pragma once
|
||||||
#define _H_OSGQTQUICK_IMAGENODE_H_
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
#include "Export.hpp"
|
||||||
#include "OSGNode.hpp"
|
#include "OSGNode.hpp"
|
||||||
@ -35,7 +34,7 @@
|
|||||||
|
|
||||||
namespace osgQtQuick {
|
namespace osgQtQuick {
|
||||||
class OSGQTQUICK_EXPORT OSGImageNode : public OSGNode {
|
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;
|
typedef OSGNode Inherited;
|
||||||
|
|
||||||
@ -43,11 +42,11 @@ public:
|
|||||||
OSGImageNode(QObject *parent = 0);
|
OSGImageNode(QObject *parent = 0);
|
||||||
virtual ~OSGImageNode();
|
virtual ~OSGImageNode();
|
||||||
|
|
||||||
const QUrl imageFile() const;
|
const QUrl imageUrl() const;
|
||||||
void setImageFile(const QUrl &url);
|
void setImageUrl(QUrl &url);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void imageFileChanged(const QUrl &url);
|
void imageUrlChanged(const QUrl &url);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual osg::Node *createNode();
|
virtual osg::Node *createNode();
|
||||||
@ -58,5 +57,3 @@ private:
|
|||||||
Hidden *const h;
|
Hidden *const h;
|
||||||
};
|
};
|
||||||
} // namespace osgQtQuick
|
} // namespace osgQtQuick
|
||||||
|
|
||||||
#endif // _H_OSGQTQUICK_IMAGENODE_H_
|
|
||||||
|
@ -59,7 +59,7 @@ public:
|
|||||||
|
|
||||||
osg::Node *nodeToUpdate() const
|
osg::Node *nodeToUpdate() const
|
||||||
{
|
{
|
||||||
return manipulator->getNode();
|
return manipulator ? manipulator->getNode() : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update()
|
void update()
|
||||||
|
@ -38,13 +38,15 @@ HEADERS += \
|
|||||||
osgearth.h \
|
osgearth.h \
|
||||||
utils/qtwindowingsystem.h \
|
utils/qtwindowingsystem.h \
|
||||||
utils/utility.h \
|
utils/utility.h \
|
||||||
utils/shapeutils.h
|
utils/shapeutils.h \
|
||||||
|
utils/imagesource.hpp
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
osgearth.cpp \
|
osgearth.cpp \
|
||||||
utils/qtwindowingsystem.cpp \
|
utils/qtwindowingsystem.cpp \
|
||||||
utils/utility.cpp \
|
utils/utility.cpp \
|
||||||
utils/shapeutils.cpp
|
utils/shapeutils.cpp \
|
||||||
|
utils/imagesource.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
osgQtQuick/Export.hpp \
|
osgQtQuick/Export.hpp \
|
||||||
@ -83,6 +85,14 @@ SOURCES += \
|
|||||||
osgQtQuick/ga/OSGNodeTrackerManipulator.cpp \
|
osgQtQuick/ga/OSGNodeTrackerManipulator.cpp \
|
||||||
osgQtQuick/ga/OSGTrackballManipulator.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 += \
|
osgearth:HEADERS += \
|
||||||
osgQtQuick/OSGSkyNode.hpp \
|
osgQtQuick/OSGSkyNode.hpp \
|
||||||
osgQtQuick/OSGGeoTransformNode.hpp
|
osgQtQuick/OSGGeoTransformNode.hpp
|
||||||
|
@ -14,6 +14,11 @@ contains(QT_ARCH, x86_64) {
|
|||||||
LIB_DIR_NAME = lib
|
LIB_DIR_NAME = lib
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gstreamer {
|
||||||
|
include(../gstreamer/gstreamer.pri)
|
||||||
|
include(../gstreamer/gstreamer_dependencies.pri)
|
||||||
|
}
|
||||||
|
|
||||||
osg {
|
osg {
|
||||||
OSG_SDK_DIR = $$clean_path($$(OSG_SDK_DIR))
|
OSG_SDK_DIR = $$clean_path($$(OSG_SDK_DIR))
|
||||||
message(Using osg from here: $$OSG_SDK_DIR)
|
message(Using osg from here: $$OSG_SDK_DIR)
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
384
ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.cpp
Normal file
384
ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.cpp
Normal 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);
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
40
ground/gcs/src/libs/osgearth/utils/imagesource.cpp
Normal file
40
ground/gcs/src/libs/osgearth/utils/imagesource.cpp
Normal 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;
|
||||||
|
}
|
47
ground/gcs/src/libs/osgearth/utils/imagesource.hpp
Normal file
47
ground/gcs/src/libs/osgearth/utils/imagesource.hpp
Normal 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) {}
|
||||||
|
};
|
@ -282,6 +282,19 @@ void PfdQmlContext::resetConsumedEnergy()
|
|||||||
batterySettings->setData(batterySettings->getData());
|
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)
|
void PfdQmlContext::loadConfiguration(PfdQmlGadgetConfiguration *config)
|
||||||
{
|
{
|
||||||
setSpeedFactor(config->speedFactor());
|
setSpeedFactor(config->speedFactor());
|
||||||
@ -307,6 +320,9 @@ void PfdQmlContext::loadConfiguration(PfdQmlGadgetConfiguration *config)
|
|||||||
|
|
||||||
// background image
|
// background image
|
||||||
setBackgroundImageFile(config->backgroundImageFile());
|
setBackgroundImageFile(config->backgroundImageFile());
|
||||||
|
|
||||||
|
// gstreamer pipeline
|
||||||
|
setGstPipeline(config->gstPipeline());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ class PfdQmlContext : public QObject {
|
|||||||
// background
|
// background
|
||||||
Q_PROPERTY(QString backgroundImageFile READ backgroundImageFile WRITE setBackgroundImageFile NOTIFY backgroundImageFileChanged)
|
Q_PROPERTY(QString backgroundImageFile READ backgroundImageFile WRITE setBackgroundImageFile NOTIFY backgroundImageFileChanged)
|
||||||
|
|
||||||
|
// gstreamer pipeline
|
||||||
|
Q_PROPERTY(QString gstPipeline READ gstPipeline WRITE setGstPipeline NOTIFY gstPipelineChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PfdQmlContext(QObject *parent = 0);
|
PfdQmlContext(QObject *parent = 0);
|
||||||
virtual ~PfdQmlContext();
|
virtual ~PfdQmlContext();
|
||||||
@ -102,6 +105,10 @@ public:
|
|||||||
QString backgroundImageFile() const;
|
QString backgroundImageFile() const;
|
||||||
void setBackgroundImageFile(const QString &arg);
|
void setBackgroundImageFile(const QString &arg);
|
||||||
|
|
||||||
|
// gstreamer pipeline
|
||||||
|
QString gstPipeline() const;
|
||||||
|
void setGstPipeline(const QString &arg);
|
||||||
|
|
||||||
Q_INVOKABLE void resetConsumedEnergy();
|
Q_INVOKABLE void resetConsumedEnergy();
|
||||||
|
|
||||||
void loadConfiguration(PfdQmlGadgetConfiguration *config);
|
void loadConfiguration(PfdQmlGadgetConfiguration *config);
|
||||||
@ -130,6 +137,8 @@ signals:
|
|||||||
void modelFileChanged(QString arg);
|
void modelFileChanged(QString arg);
|
||||||
void backgroundImageFileChanged(QString arg);
|
void backgroundImageFileChanged(QString arg);
|
||||||
|
|
||||||
|
void gstPipelineChanged(QString arg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// constants
|
// constants
|
||||||
static const QString CONTEXT_PROPERTY_NAME;
|
static const QString CONTEXT_PROPERTY_NAME;
|
||||||
@ -156,6 +165,8 @@ private:
|
|||||||
|
|
||||||
QString m_backgroundImageFile;
|
QString m_backgroundImageFile;
|
||||||
|
|
||||||
|
QString m_gstPipeline;
|
||||||
|
|
||||||
void addModelDir(QString dir);
|
void addModelDir(QString dir);
|
||||||
};
|
};
|
||||||
#endif /* PFDQMLCONTEXT_H_ */
|
#endif /* PFDQMLCONTEXT_H_ */
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(QString classId, QSettings &settings, QObject *parent) :
|
PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(QString classId, QSettings &settings, QObject *parent) :
|
||||||
IUAVGadgetConfiguration(classId, parent)
|
IUAVGadgetConfiguration(classId, parent)
|
||||||
{
|
{
|
||||||
|
// TODO move to some conversion utility class
|
||||||
m_speedMap[1.0] = "m/s";
|
m_speedMap[1.0] = "m/s";
|
||||||
m_speedMap[3.6] = "km/h";
|
m_speedMap[3.6] = "km/h";
|
||||||
m_speedMap[2.2369] = "mph";
|
m_speedMap[2.2369] = "mph";
|
||||||
@ -73,6 +74,9 @@ PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(QString classId, QSettings
|
|||||||
// background image
|
// background image
|
||||||
m_backgroundImageFile = settings.value("backgroundImageFile", "Unknown").toString();
|
m_backgroundImageFile = settings.value("backgroundImageFile", "Unknown").toString();
|
||||||
m_backgroundImageFile = Utils::InsertDataPath(m_backgroundImageFile);
|
m_backgroundImageFile = Utils::InsertDataPath(m_backgroundImageFile);
|
||||||
|
|
||||||
|
// gstreamer pipeline
|
||||||
|
m_gstPipeline = settings.value("gstPipeline").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(const PfdQmlGadgetConfiguration &obj) :
|
PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(const PfdQmlGadgetConfiguration &obj) :
|
||||||
@ -104,6 +108,9 @@ PfdQmlGadgetConfiguration::PfdQmlGadgetConfiguration(const PfdQmlGadgetConfigura
|
|||||||
|
|
||||||
// background image
|
// background image
|
||||||
m_backgroundImageFile = obj.m_backgroundImageFile;
|
m_backgroundImageFile = obj.m_backgroundImageFile;
|
||||||
|
|
||||||
|
// gstreamer pipeline
|
||||||
|
m_gstPipeline = obj.m_gstPipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,4 +159,7 @@ void PfdQmlGadgetConfiguration::saveConfig(QSettings &settings) const
|
|||||||
// background image
|
// background image
|
||||||
QString backgroundImageFile = Utils::RemoveDataPath(m_backgroundImageFile);
|
QString backgroundImageFile = Utils::RemoveDataPath(m_backgroundImageFile);
|
||||||
settings.setValue("backgroundImageFile", backgroundImageFile);
|
settings.setValue("backgroundImageFile", backgroundImageFile);
|
||||||
|
|
||||||
|
// gstreamer pipeline
|
||||||
|
settings.setValue("gstPipeline", m_gstPipeline);
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,15 @@ public:
|
|||||||
m_backgroundImageFile = fileName;
|
m_backgroundImageFile = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString gstPipeline() const
|
||||||
|
{
|
||||||
|
return m_gstPipeline;
|
||||||
|
}
|
||||||
|
void setGstPipeline(const QString &pipeline)
|
||||||
|
{
|
||||||
|
m_gstPipeline = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
QMapIterator<double, QString> speedMapIterator()
|
QMapIterator<double, QString> speedMapIterator()
|
||||||
{
|
{
|
||||||
return QMapIterator<double, QString>(m_speedMap);
|
return QMapIterator<double, QString>(m_speedMap);
|
||||||
@ -234,6 +243,8 @@ private:
|
|||||||
|
|
||||||
QString m_backgroundImageFile;
|
QString m_backgroundImageFile;
|
||||||
|
|
||||||
|
QString m_gstPipeline;
|
||||||
|
|
||||||
QMap<double, QString> m_speedMap;
|
QMap<double, QString> m_speedMap;
|
||||||
QMap<double, QString> m_altitudeMap;
|
QMap<double, QString> m_altitudeMap;
|
||||||
};
|
};
|
||||||
|
@ -111,6 +111,9 @@ QWidget *PfdQmlGadgetOptionsPage::createPage(QWidget *parent)
|
|||||||
options_page->backgroundImageFile->setPromptDialogTitle(tr("Choose Background Image File"));
|
options_page->backgroundImageFile->setPromptDialogTitle(tr("Choose Background Image File"));
|
||||||
options_page->backgroundImageFile->setPath(m_config->backgroundImageFile());
|
options_page->backgroundImageFile->setPath(m_config->backgroundImageFile());
|
||||||
|
|
||||||
|
// gstreamer pipeline
|
||||||
|
options_page->pipelineTextEdit->setPlainText(m_config->gstPipeline());
|
||||||
|
|
||||||
#ifndef USE_OSG
|
#ifndef USE_OSG
|
||||||
options_page->showTerrain->setChecked(false);
|
options_page->showTerrain->setChecked(false);
|
||||||
options_page->showTerrain->setVisible(false);
|
options_page->showTerrain->setVisible(false);
|
||||||
@ -170,6 +173,8 @@ void PfdQmlGadgetOptionsPage::apply()
|
|||||||
#else
|
#else
|
||||||
m_config->setModelEnabled(false);
|
m_config->setModelEnabled(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m_config->setGstPipeline(options_page->pipelineTextEdit->toPlainText());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PfdQmlGadgetOptionsPage::finish()
|
void PfdQmlGadgetOptionsPage::finish()
|
||||||
|
@ -138,7 +138,7 @@
|
|||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="terrainTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Terrain</string>
|
<string>Terrain</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@ -295,7 +295,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_3">
|
<widget class="QWidget" name="modelTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Model</string>
|
<string>Model</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@ -391,7 +391,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_2">
|
<widget class="QWidget" name="environmentTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Environment</string>
|
<string>Environment</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@ -511,6 +511,43 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</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><enter your gstreamer pipeline here></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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -1665,6 +1665,31 @@
|
|||||||
<backgroundImageFile>%%DATAPATH%%backgrounds/default_background.png</backgroundImageFile>
|
<backgroundImageFile>%%DATAPATH%%backgrounds/default_background.png</backgroundImageFile>
|
||||||
</data>
|
</data>
|
||||||
</PFD__PCT__20__PCT__28ArcGis__PCT__29>
|
</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>
|
<Model__PCT__20View>
|
||||||
<configInfo>
|
<configInfo>
|
||||||
<locked>false</locked>
|
<locked>false</locked>
|
||||||
|
27
ground/gcs/src/share/qml/PfdVideo.qml
Normal file
27
ground/gcs/src/share/qml/PfdVideo.qml
Normal 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"
|
||||||
|
}
|
@ -55,7 +55,7 @@ Item {
|
|||||||
|
|
||||||
OSGImageNode {
|
OSGImageNode {
|
||||||
id: backgroundImageNode
|
id: backgroundImageNode
|
||||||
imageFile: pfdContext.backgroundImageFile
|
imageUrl: pfdContext.backgroundImageFile
|
||||||
}
|
}
|
||||||
|
|
||||||
OSGTransformNode {
|
OSGTransformNode {
|
||||||
|
149
ground/gcs/src/share/qml/pfd/PfdVideoWorld.qml
Normal file
149
ground/gcs/src/share/qml/pfd/PfdVideoWorld.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user