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:
parent
e30340237f
commit
05112f9c1b
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
|
||||
osg::Node *nodeToUpdate() const
|
||||
{
|
||||
return manipulator->getNode();
|
||||
return manipulator ? manipulator->getNode() : NULL;
|
||||
}
|
||||
|
||||
void update()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -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><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>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -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>
|
||||
|
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 {
|
||||
id: backgroundImageNode
|
||||
imageFile: pfdContext.backgroundImageFile
|
||||
imageUrl: pfdContext.backgroundImageFile
|
||||
}
|
||||
|
||||
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…
Reference in New Issue
Block a user