From 892c83b30e684f8d1d88add0a6f6e5d909670d06 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Thu, 22 Sep 2016 02:23:15 +0200 Subject: [PATCH 01/18] LP-109 add gstreamer library and video gadget --- ground/gcs/src/libs/gstreamer/copydata.pro | 82 ++ .../gcs/src/libs/gstreamer/devicemonitor.cpp | 136 +++ ground/gcs/src/libs/gstreamer/devicemonitor.h | 73 ++ ground/gcs/src/libs/gstreamer/gst_global.h | 38 + ground/gcs/src/libs/gstreamer/gst_util.cpp | 125 +++ ground/gcs/src/libs/gstreamer/gst_util.h | 39 + ground/gcs/src/libs/gstreamer/gstreamer.pri | 3 + ground/gcs/src/libs/gstreamer/gstreamer.pro | 29 + .../libs/gstreamer/gstreamer_dependencies.pri | 9 + ground/gcs/src/libs/gstreamer/notes.txt | 155 +++ ground/gcs/src/libs/gstreamer/overlay.h | 39 + ground/gcs/src/libs/gstreamer/pipeline.cpp | 38 + ground/gcs/src/libs/gstreamer/pipeline.h | 41 + ground/gcs/src/libs/gstreamer/pipelineevent.h | 413 ++++++++ .../plugins/cameracalibration/.no-auto-format | 0 .../plugins/cameracalibration/cameraevent.cpp | 96 ++ .../plugins/cameracalibration/cameraevent.hpp | 37 + .../plugins/cameracalibration/camerautils.cpp | 48 + .../plugins/cameracalibration/camerautils.hpp | 34 + .../gstcameracalibration.cpp | 954 ++++++++++++++++++ .../cameracalibration/gstcameracalibration.h | 114 +++ .../cameracalibration/gstcameraundistort.cpp | 515 ++++++++++ .../cameracalibration/gstcameraundistort.h | 109 ++ .../cameracalibration/gstopencvutils.cpp | 178 ++++ .../cameracalibration/gstopencvutils.h | 46 + .../gstopencvvideofilter.cpp | 289 ++++++ .../cameracalibration/gstopencvvideofilter.h | 110 ++ .../src/libs/gstreamer/plugins/plugins.pro | 33 + ground/gcs/src/libs/gstreamer/readme.txt | 67 ++ ground/gcs/src/libs/gstreamer/videowidget.cpp | 851 ++++++++++++++++ ground/gcs/src/libs/gstreamer/videowidget.h | 94 ++ ground/gcs/src/libs/libs.pro | 6 +- ground/gcs/src/plugins/plugins.pro | 7 + .../src/plugins/video/VideoGadget.pluginspec | 10 + ground/gcs/src/plugins/video/helpdialog.cpp | 86 ++ ground/gcs/src/plugins/video/helpdialog.h | 60 ++ ground/gcs/src/plugins/video/helpdialog.ui | 82 ++ .../video/resources/22x22/media-eject.png | Bin 0 -> 729 bytes .../resources/22x22/media-playback-pause.png | Bin 0 -> 655 bytes .../resources/22x22/media-playback-start.png | Bin 0 -> 961 bytes .../resources/22x22/media-playback-stop.png | Bin 0 -> 513 bytes .../video/resources/22x22/media-record.png | Bin 0 -> 1074 bytes .../resources/22x22/media-seek-backward.png | Bin 0 -> 1003 bytes .../resources/22x22/media-seek-forward.png | Bin 0 -> 1025 bytes .../resources/22x22/media-skip-backward.png | Bin 0 -> 1048 bytes .../resources/22x22/media-skip-forward.png | Bin 0 -> 1124 bytes .../22x22/utilities-system-monitor.png | Bin 0 -> 990 bytes .../resources/22x22/utilities-terminal.png | Bin 0 -> 1026 bytes .../video/resources/32x32/media-eject.png | Bin 0 -> 987 bytes .../resources/32x32/media-playback-pause.png | Bin 0 -> 481 bytes .../resources/32x32/media-playback-start.png | Bin 0 -> 1028 bytes .../resources/32x32/media-playback-stop.png | Bin 0 -> 571 bytes .../video/resources/32x32/media-record.png | Bin 0 -> 1266 bytes .../resources/32x32/media-seek-backward.png | Bin 0 -> 1074 bytes .../resources/32x32/media-seek-forward.png | Bin 0 -> 1205 bytes .../resources/32x32/media-skip-backward.png | Bin 0 -> 1236 bytes .../resources/32x32/media-skip-forward.png | Bin 0 -> 1225 bytes .../32x32/utilities-system-monitor.png | Bin 0 -> 1886 bytes .../resources/32x32/utilities-terminal.png | Bin 0 -> 1488 bytes ground/gcs/src/plugins/video/video.pro | 37 + ground/gcs/src/plugins/video/video.qrc | 33 + ground/gcs/src/plugins/video/video.ui | 204 ++++ ground/gcs/src/plugins/video/videogadget.cpp | 46 + ground/gcs/src/plugins/video/videogadget.h | 70 ++ .../video/videogadgetconfiguration.cpp | 73 ++ .../plugins/video/videogadgetconfiguration.h | 104 ++ .../src/plugins/video/videogadgetfactory.cpp | 57 ++ .../src/plugins/video/videogadgetfactory.h | 51 + .../plugins/video/videogadgetoptionspage.cpp | 82 ++ .../plugins/video/videogadgetoptionspage.h | 80 ++ .../src/plugins/video/videogadgetwidget.cpp | 170 ++++ .../gcs/src/plugins/video/videogadgetwidget.h | 68 ++ .../gcs/src/plugins/video/videooptionspage.ui | 96 ++ ground/gcs/src/plugins/video/videoplugin.cpp | 64 ++ ground/gcs/src/plugins/video/videoplugin.h | 51 + .../gcs/src/share/configurations/default.xml | 122 +++ 76 files changed, 6451 insertions(+), 3 deletions(-) create mode 100644 ground/gcs/src/libs/gstreamer/copydata.pro create mode 100644 ground/gcs/src/libs/gstreamer/devicemonitor.cpp create mode 100644 ground/gcs/src/libs/gstreamer/devicemonitor.h create mode 100644 ground/gcs/src/libs/gstreamer/gst_global.h create mode 100644 ground/gcs/src/libs/gstreamer/gst_util.cpp create mode 100644 ground/gcs/src/libs/gstreamer/gst_util.h create mode 100644 ground/gcs/src/libs/gstreamer/gstreamer.pri create mode 100644 ground/gcs/src/libs/gstreamer/gstreamer.pro create mode 100644 ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri create mode 100644 ground/gcs/src/libs/gstreamer/notes.txt create mode 100644 ground/gcs/src/libs/gstreamer/overlay.h create mode 100644 ground/gcs/src/libs/gstreamer/pipeline.cpp create mode 100644 ground/gcs/src/libs/gstreamer/pipeline.h create mode 100644 ground/gcs/src/libs/gstreamer/pipelineevent.h create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/.no-auto-format create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp create mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h create mode 100644 ground/gcs/src/libs/gstreamer/plugins/plugins.pro create mode 100644 ground/gcs/src/libs/gstreamer/readme.txt create mode 100644 ground/gcs/src/libs/gstreamer/videowidget.cpp create mode 100644 ground/gcs/src/libs/gstreamer/videowidget.h create mode 100644 ground/gcs/src/plugins/video/VideoGadget.pluginspec create mode 100644 ground/gcs/src/plugins/video/helpdialog.cpp create mode 100644 ground/gcs/src/plugins/video/helpdialog.h create mode 100644 ground/gcs/src/plugins/video/helpdialog.ui create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-eject.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-playback-pause.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-playback-start.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-playback-stop.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-record.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-seek-backward.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-seek-forward.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-skip-backward.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/media-skip-forward.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/utilities-system-monitor.png create mode 100644 ground/gcs/src/plugins/video/resources/22x22/utilities-terminal.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-eject.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-playback-pause.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-playback-start.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-playback-stop.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-record.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-seek-backward.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-seek-forward.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-skip-backward.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/media-skip-forward.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/utilities-system-monitor.png create mode 100644 ground/gcs/src/plugins/video/resources/32x32/utilities-terminal.png create mode 100644 ground/gcs/src/plugins/video/video.pro create mode 100644 ground/gcs/src/plugins/video/video.qrc create mode 100644 ground/gcs/src/plugins/video/video.ui create mode 100644 ground/gcs/src/plugins/video/videogadget.cpp create mode 100644 ground/gcs/src/plugins/video/videogadget.h create mode 100644 ground/gcs/src/plugins/video/videogadgetconfiguration.cpp create mode 100644 ground/gcs/src/plugins/video/videogadgetconfiguration.h create mode 100644 ground/gcs/src/plugins/video/videogadgetfactory.cpp create mode 100644 ground/gcs/src/plugins/video/videogadgetfactory.h create mode 100644 ground/gcs/src/plugins/video/videogadgetoptionspage.cpp create mode 100644 ground/gcs/src/plugins/video/videogadgetoptionspage.h create mode 100644 ground/gcs/src/plugins/video/videogadgetwidget.cpp create mode 100644 ground/gcs/src/plugins/video/videogadgetwidget.h create mode 100644 ground/gcs/src/plugins/video/videooptionspage.ui create mode 100644 ground/gcs/src/plugins/video/videoplugin.cpp create mode 100644 ground/gcs/src/plugins/video/videoplugin.h diff --git a/ground/gcs/src/libs/gstreamer/copydata.pro b/ground/gcs/src/libs/gstreamer/copydata.pro new file mode 100644 index 0000000000..20522f31eb --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/copydata.pro @@ -0,0 +1,82 @@ +win32:gstreamer { + + GST_BIN_DIR = $$system(pkg-config --variable=exec_prefix gstreamer-1.0)/bin + GST_PLUGINS_DIR = $$system(pkg-config --variable=pluginsdir gstreamer-1.0) + + # gstreamer libraries + GST_LIBS = \ + libgstreamer-1.0-0.dll + + gstreamer_utilities:GST_LIBS += \ + gst-inspect-1.0.exe \ + gst-launch-1.0.exe + + for(lib, GST_LIBS) { + addCopyFileTarget($${lib},$${GST_BIN_DIR},$${GCS_APP_PATH}) + addCopyDependenciesTarget($${lib},$${GST_BIN_DIR},$${GCS_APP_PATH}) + } + + # gstreamer core + GST_PLUGINS = \ + libgstcoreelements.dll + + # gst-plugins-base + GST_PLUGINS += \ + libgstapp.dll \ + libgstaudiotestsrc.dll \ + libgstpango.dll \ + libgstplayback.dll \ + libgsttcp.dll \ + libgsttypefindfunctions.dll \ + libgstvideoconvert.dll \ + libgstvideorate.dll \ + libgstvideoscale.dll \ + libgstvideotestsrc.dll + + # gst-plugins-good + GST_PLUGINS += \ + libgstautodetect.dll \ + libgstavi.dll \ + libgstdeinterlace.dll \ + libgstdirectsoundsink.dll \ + libgstimagefreeze.dll \ + libgstjpeg.dll \ + libgstrawparse.dll \ + libgstrtp.dll \ + libgstrtpmanager.dll \ + libgstrtsp.dll \ + libgstudp.dll \ + libgstvideomixer.dll + + # gst-plugins-bad + GST_PLUGINS += \ + libgstaudiovisualizers.dll \ + libgstautoconvert.dll \ + libgstcompositor.dll \ + libgstd3dvideosink.dll \ + libgstdebugutilsbad.dll \ + libgstdirectsoundsrc.dll \ + libgstinter.dll \ + libgstmpegpsdemux.dll \ + libgstmpegpsmux.dll \ + libgstmpegtsdemux.dll \ + libgstmpegtsmux.dll \ + libgstvideoparsersbad.dll \ + libgstwinks.dll \ + libgstwinscreencap.dll + + # gst-plugins-ugly + GST_PLUGINS += \ + libgstmpeg2dec.dll \ + libgstx264.dll + + # gst-libav + GST_PLUGINS += \ + libgstlibav.dll + + for(lib, GST_PLUGINS) { + addCopyFileTarget($${lib},$${GST_PLUGINS_DIR},$${GCS_LIBRARY_PATH}/gstreamer-1.0) + addCopyDependenciesTarget($${lib},$${GST_PLUGINS_DIR},$${GCS_APP_PATH}) + } + +} diff --git a/ground/gcs/src/libs/gstreamer/devicemonitor.cpp b/ground/gcs/src/libs/gstreamer/devicemonitor.cpp new file mode 100644 index 0000000000..b8bedddfce --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/devicemonitor.cpp @@ -0,0 +1,136 @@ +/** + ****************************************************************************** + * + * @file devicemonitor.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 "devicemonitor.h" + +#include "gst_util.h" + +#include + +#include + +static GstBusSyncReply my_bus_sync_func(GstBus *bus, GstMessage *message, gpointer user_data) +{ + Q_UNUSED(bus) + + DeviceMonitor * dm; + GstDevice *device; + gchar *name; + + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_DEVICE_ADDED: + gst_message_parse_device_added(message, &device); + name = gst_device_get_display_name(device); + + dm = (DeviceMonitor *)user_data; + QMetaObject::invokeMethod(dm, "device_added", Qt::QueuedConnection, + Q_ARG(QString, QString(name))); + + g_free(name); + break; + case GST_MESSAGE_DEVICE_REMOVED: + gst_message_parse_device_removed(message, &device); + name = gst_device_get_display_name(device); + + dm = (DeviceMonitor *)user_data; + QMetaObject::invokeMethod(dm, "device_removed", Qt::QueuedConnection, + Q_ARG(QString, QString(name))); + + g_free(name); + break; + default: + break; + } + + // no need to pass it to the async queue, there is none... + return GST_BUS_DROP; +} + +DeviceMonitor::DeviceMonitor(QObject *parent) : QObject(parent) +{ + // initialize gstreamer + gst::init(NULL, NULL); + + monitor = gst_device_monitor_new(); + + GstBus *bus = gst_device_monitor_get_bus(monitor); + gst_bus_set_sync_handler(bus, (GstBusSyncHandler)my_bus_sync_func, this, NULL); + gst_object_unref(bus); + + GstCaps *caps = NULL; // gst_caps_new_empty_simple("video/x-raw"); + const gchar *classes = "Video/Source"; + gst_device_monitor_add_filter(monitor, classes, caps); + if (caps) { + gst_caps_unref(caps); + } + + if (!gst_device_monitor_start(monitor)) { + qWarning() << "Failed to start device monitor"; + } +} + +DeviceMonitor::~DeviceMonitor() +{ + gst_device_monitor_stop(monitor); + gst_object_unref(monitor); +} + +QList DeviceMonitor::devices() const +{ + QList devices; + + GList *list = gst_device_monitor_get_devices(monitor); + while (list != NULL) { + gchar *name; + gchar *device_class; + + GstDevice *device = (GstDevice *)list->data; + name = gst_device_get_display_name(device); + device_class = gst_device_get_device_class(device); + + devices << Device(name, device_class); + + g_free(name); + g_free(device_class); + + gst_object_unref(device); + list = g_list_remove_link(list, list); + } + + return devices; +} + +void DeviceMonitor::device_added(QString name) +{ + // qDebug() << "**** ADDED:" << name; + emit deviceAdded(name); +} + +void DeviceMonitor::device_removed(QString name) +{ + // qDebug() << "**** REMOVED:" << name; + emit deviceRemoved(name); +} diff --git a/ground/gcs/src/libs/gstreamer/devicemonitor.h b/ground/gcs/src/libs/gstreamer/devicemonitor.h new file mode 100644 index 0000000000..e86679746b --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/devicemonitor.h @@ -0,0 +1,73 @@ +/** + ****************************************************************************** + * + * @file devicemonitor.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef DEVICEMONITOR_H_ +#define DEVICEMONITOR_H_ + +#include "gst_global.h" + +#include + +typedef struct _GstDeviceMonitor GstDeviceMonitor; + +class Device { +public: + Device(QString displayName, QString deviceClass) : m_displayName(displayName), m_deviceClass(deviceClass) + {} + QString displayName() const + { + return m_displayName; + } + QString deviceClass() const + { + return m_deviceClass; + } +private: + QString m_displayName; + QString m_deviceClass; +}; + +class GST_LIB_EXPORT DeviceMonitor : public QObject { + Q_OBJECT +public: + DeviceMonitor(QObject *parent = NULL); + virtual ~DeviceMonitor(); + + QList devices() const; + +signals: + void deviceAdded(QString name); + void deviceRemoved(QString name); + +private: + GstDeviceMonitor *monitor; + +private slots: + void device_added(QString name); + void device_removed(QString name); +}; + +#endif /* DEVICEMONITOR_H_ */ diff --git a/ground/gcs/src/libs/gstreamer/gst_global.h b/ground/gcs/src/libs/gstreamer/gst_global.h new file mode 100644 index 0000000000..60090671e5 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/gst_global.h @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * + * @file gst_global.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef GST_GLOBAL_H +#define GST_GLOBAL_H + +#include + +#if defined(GST_LIB_LIBRARY) +# define GST_LIB_EXPORT Q_DECL_EXPORT +#else +# define GST_LIB_EXPORT Q_DECL_IMPORT +#endif + +#endif // GST_GLOBAL_H diff --git a/ground/gcs/src/libs/gstreamer/gst_util.cpp b/ground/gcs/src/libs/gstreamer/gst_util.cpp new file mode 100644 index 0000000000..0a24a485d2 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/gst_util.cpp @@ -0,0 +1,125 @@ +/** + ****************************************************************************** + * + * @file gst_util.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 "gst_util.h" + +#include + +#ifdef USE_OPENCV +#include "plugins/cameracalibration/gstcameracalibration.h" +#include "plugins/cameracalibration/gstcameraundistort.h" +#endif + +#include "utils/pathutils.h" + +#include + +static bool initialized = false; + +gboolean gst_plugin_librepilot_register(GstPlugin *plugin) +{ +#ifdef USE_OPENCV + if (!gst_camera_calibration_plugin_init(plugin)) { + return FALSE; + } + if (!gst_camera_undistort_plugin_init(plugin)) { + return FALSE; + } +#else + Q_UNUSED(plugin) +#endif + return TRUE; +} + +void gst_plugin_librepilot_register() +{ + gst_plugin_register_static(GST_VERSION_MAJOR, GST_VERSION_MINOR, "librepilot", + "LibrePilot plugin", gst_plugin_librepilot_register, "1.10.0", "GPL", + "librepilot", "LibrePilot", "http://librepilot.org/"); +} + +// see http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/gst-running.html +void gst::init(int *argc, char * *argv[]) +{ + // TODO Not thread safe. Does it need to be? + if (initialized) { + return; + } + initialized = true; + + // qputenv("GST_DEBUG", "3"); + // qputenv("GST_DEBUG", "3,rtspsrc:6,udpsrc:6"); + // qputenv("GST_DEBUG", "3,bin:6"); + // qputenv("GST_DEBUG", "3,rtpjitterbuffer:6"); + // qputenv("GST_DEBUG_FILE", "gst.log"); + // qputenv("GST_DEBUG_DUMP_DOT_DIR", "."); + + +#ifdef Q_OS_WIN + qputenv("GST_PLUGIN_PATH_1_0", (Utils::GetLibraryPath() + "gstreamer-1.0").toLatin1()); +#endif + + qDebug() << "gstreamer - initializing"; + GError *error = NULL; + if (!gst_init_check(argc, argv, &error)) { + qCritical() << "failed to initialize gstreamer"; + return; + } + + qDebug() << "gstreamer - version:" << gst_version_string(); + qDebug() << "gstreamer - plugin system path:" << qgetenv("GST_PLUGIN_SYSTEM_PATH_1_0"); + qDebug() << "gstreamer - plugin path:" << qgetenv("GST_PLUGIN_PATH_1_0"); + + qDebug() << "gstreamer - registering plugins"; + // GST_PLUGIN_STATIC_REGISTER(librepilot); + gst_plugin_librepilot_register(); + +#ifdef USE_OPENCV + // see http://stackoverflow.com/questions/32477403/how-to-know-if-sse2-is-activated-in-opencv + // see http://answers.opencv.org/question/696/how-to-enable-vectorization-in-opencv/ + if (!cv::checkHardwareSupport(CV_CPU_SSE)) { + qWarning() << "SSE not supported"; + } + if (!cv::checkHardwareSupport(CV_CPU_SSE2)) { + qWarning() << "SSE2 not supported"; + } + if (!cv::checkHardwareSupport(CV_CPU_SSE3)) { + qWarning() << "SSE3 not supported"; + } + qDebug() << "MMX :" << cv::checkHardwareSupport(CV_CPU_MMX); + qDebug() << "SSE :" << cv::checkHardwareSupport(CV_CPU_SSE); + qDebug() << "SSE2 :" << cv::checkHardwareSupport(CV_CPU_SSE2); + qDebug() << "SSE3 :" << cv::checkHardwareSupport(CV_CPU_SSE3); + qDebug() << "SSE4_1 :" << cv::checkHardwareSupport(CV_CPU_SSE4_1); + qDebug() << "SSE4_2 :" << cv::checkHardwareSupport(CV_CPU_SSE4_2); +#endif +} + +QString gst::version(void) +{ + init(NULL, NULL); + return QString(gst_version_string()); +} diff --git a/ground/gcs/src/libs/gstreamer/gst_util.h b/ground/gcs/src/libs/gstreamer/gst_util.h new file mode 100644 index 0000000000..f17093fcfc --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/gst_util.h @@ -0,0 +1,39 @@ +/** + ****************************************************************************** + * + * @file gst_util.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef GST_UTIL_H +#define GST_UTIL_H + +#include "gst_global.h" + +#include + +namespace gst { +GST_LIB_EXPORT void init(int *argc, char * *argv[]); +GST_LIB_EXPORT QString version(); +} + +#endif // GST_UTIL_H diff --git a/ground/gcs/src/libs/gstreamer/gstreamer.pri b/ground/gcs/src/libs/gstreamer/gstreamer.pri new file mode 100644 index 0000000000..79729793d7 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/gstreamer.pri @@ -0,0 +1,3 @@ +LIBS *= -l$$qtLibraryName(GCSGStreamer) + +INCLUDEPATH += $$GCS_SOURCE_TREE/src/libs/gstreamer diff --git a/ground/gcs/src/libs/gstreamer/gstreamer.pro b/ground/gcs/src/libs/gstreamer/gstreamer.pro new file mode 100644 index 0000000000..e302ed5970 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/gstreamer.pro @@ -0,0 +1,29 @@ +TEMPLATE = lib +TARGET = GCSGStreamer +DEFINES += GST_LIB_LIBRARY + +QT += widgets + +include(../../library.pri) +include(../utils/utils.pri) + +include(gstreamer_dependencies.pri) + +gstreamer_plugins:include(plugins/plugins.pro) + +HEADERS += \ + gst_global.h \ + gst_util.h \ + devicemonitor.h \ + pipeline.h \ + pipelineevent.h \ + overlay.h \ + videowidget.h + +SOURCES += \ + gst_util.cpp \ + devicemonitor.cpp \ + pipeline.cpp \ + videowidget.cpp + +equals(copydata, 1):include(copydata.pro) diff --git a/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri b/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri new file mode 100644 index 0000000000..9e307a556a --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri @@ -0,0 +1,9 @@ +DEFINES += USE_GSTREAMER +opencv:DEFINES += USE_OPENCV + +linux|win32 { + CONFIG += link_pkgconfig + PKGCONFIG += glib-2.0 gobject-2.0 + PKGCONFIG += gstreamer-1.0 gstreamer-video-1.0 + opencv:PKGCONFIG += opencv +} diff --git a/ground/gcs/src/libs/gstreamer/notes.txt b/ground/gcs/src/libs/gstreamer/notes.txt new file mode 100644 index 0000000000..9b04ffb1ae --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/notes.txt @@ -0,0 +1,155 @@ + +Tips: +- Measuring video latency : display time on video + film video output -> the time between two frames is the latency + +Limitations: +- It is not possible to view a web cam in two different gadgets (same is *not* true for DirectSound sources) + but it is not really an issue, as it possible to tee a video source in the pipeline itself + + Issues: + - bad: libgstchromaprint - libchromaprint needs avcodec-56 (vs 57) and avutil-54 (vs 55) + - bad: libgstfragmented - needs libnettle-6-1 (vs 6-2) - was renamed to hls + - bad: libgstx265 - needs rebuild + - need to rebuild libgstpluginsbad and libchromaprint + + Todo: + - should use openglvideosink for PFD + - save config as QR code and ... + - split cameraconfiguration -> cameraundistort + - exclude gst plugins from uncrustify + - fix crash on unsupported formats: + - undistort should be passthrough when not enabled + + + +gst-launch-1.0.exe -v -m autovideosrc ! video/x-raw,format=BGRA,width=800,height=600 ! videoconvert ! queue ! x264enc pass=qual quantizer=20 tune=zerolatency ! rtph264pay ! udpsink host=127.0.0.1 port=5000 +gst-launch-1.0.exe -v -m udpsrc port=5000 ! "application/x-rtp, payload=127" ! rtph264depay ! decodebin ! videoconvert ! timeoverlay ! autovideosink + + +autovideosrc ! videoconvert ! queue ! x264enc pass=qual quantizer=20 tune=zerolatency ! rtph264pay ! udpsink host=127.0.0.1 port=5000 +autovideosrc ! queue ! videoscale ! video/x-raw,width=320,height=200 ! videoconvert ! x264enc tune=zerolatency ! rtph264pay ! udpsink host=127.0.0.1 port=5000 +autovideosrc ! queue ! videoscale ! videorate ! video/x-raw,width=320,height=240,frame-rate=30/1 ! videoconvert ! x264enc tune=zerolatency ! rtph264pay ! udpsink host=127.0.0.1 port=5000 + +udpsrc port=5000 ! application/x-rtp,payload=96,clock-rate=90000 ! rtpjitterbuffer latency=30 ! rtph264depay ! decodebin ! videoconvert ! timeoverlay ! fpsdisplaysink + +RTSP +server : ./test-launch.exe "( videotestsrc ! x264enc tune=zerolatency ! rtph264pay name=pay0 pt=96 )" +client : gst-launch-1.0.exe -v -m rtspsrc location=rtsp://127.0.0.1:8554/test latency=30 ! decodebin ! timeoverlay ! autovideosink + +Qt: + Line 250058: 0:02:34.185436460 5988 d5a0480 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:02:34.081238026, base 0:00:00.050268441, recv_diff 0:02:34.030969585, slope 8 + Line 250059: 0:02:34.185499451 5988 d5a0480 DEBUG rtpjitterbuffer rtpjitterbuffer.c:642:calculate_skew: delta -2352638, new min: -2417925 + Line 250060: 0:02:34.185552513 5988 d5a0480 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -2398275, out 0:02:34.081192389 + +RTP +server : gst-launch-1.0.exe -v -m videotestsrc ! x264enc tune=zerolatency ! rtph264pay ! udpsink host=127.0.0.1 port=5000 +client : gst-launch-1.0.exe -v -m udpsrc port=5000 ! application/x-rtp,payload=96,clock-rate=90000 ! rtpjitterbuffer ! rtph264depay ! decodebin ! videoconvert ! timeoverlay ! autovideosink + +WIFI CAM RTSP +client : gst-launch-1.0.exe -v -m rtspsrc location=rtsp://192.168.42.1/AmbaStreamTest latency=30 ! decodebin ! timeoverlay ! autovideosink +Qt: + Line 14594: 0:00:28.489562097 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:19.812089201, base 0:00:04.161093352, recv_diff 0:00:15.650995849, slope 7 + Line 14595: 0:00:28.489625088 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:642:calculate_skew: delta 2029182, new min: -2061750 + Line 14596: 0:00:28.489677219 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -2246751, out 0:00:19.807813268 + Line 14612: 0:00:28.527222391 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:19.849735841, base 0:00:04.161093352, recv_diff 0:00:15.688642489, slope 7 + Line 14613: 0:00:28.527285692 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:642:calculate_skew: delta 6309156, new min: -2061750 + Line 14614: 0:00:28.527339685 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -2245270, out 0:00:19.841181415 + Line 14630: 0:00:28.564027806 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:19.886522948, base 0:00:04.161093352, recv_diff 0:00:15.725429596, slope 7 + Line 14631: 0:00:28.564091728 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:642:calculate_skew: delta 9729596, new min: -2061750 + Line 14632: 0:00:28.564145410 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -2243801, out 0:00:19.874549551 + Line 14654: 0:00:31.712747597 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.035042595, base 0:00:04.161093352, recv_diff 0:00:18.873949243, slope 6 + Line 14655: 0:00:31.712811519 9388 d5b9518 WARN rtpjitterbuffer rtpjitterbuffer.c:570:calculate_skew: delta - skew: 0:00:03.127126377 too big, reset skew + Line 14656: 0:00:31.712867063 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 0, delta 0 + Line 14657: 0:00:31.712919194 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew 0, out 0:00:23.035042595 + Line 14759: 0:00:31.720619622 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.043193270, base 0:00:23.035042595, recv_diff 0:00:00.008150675, slope 32 + Line 14760: 0:00:31.720681061 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 1, delta -25215991 + Line 14761: 0:00:31.720734123 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -2521, out 0:00:23.068406740 + Line 14774: 0:00:31.721641753 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.044219745, base 0:00:23.035042595, recv_diff 0:00:00.009177150, slope 58 + Line 14775: 0:00:31.721703813 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 2, delta -57556183 + Line 14776: 0:00:31.721755633 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -54319, out 0:00:23.101721609 + Line 14789: 0:00:31.722667608 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.045249324, base 0:00:23.035042595, recv_diff 0:00:00.010206729, slope 78 + Line 14790: 0:00:31.722730288 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 3, delta -89893271 + Line 14791: 0:00:31.722782729 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -278916, out 0:00:23.134863679 + Line 14804: 0:00:31.723697497 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.046273007, base 0:00:23.035042595, recv_diff 0:00:00.011230412, slope 95 + Line 14805: 0:00:31.723759557 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 4, delta -122236254 + Line 14806: 0:00:31.723811687 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -717962, out 0:00:23.167791299 + [...] + Line 14864: 0:00:31.727785401 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.050366186, base 0:00:23.035042595, recv_diff 0:00:00.015323591, slope 139 + Line 14865: 0:00:31.727851185 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 8, delta -251609742 + Line 14866: 0:00:31.727903626 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -10312755, out 0:00:23.291663173 + Line 14964: 0:00:31.732567449 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.055371651, base 0:00:23.035042595, recv_diff 0:00:00.020329056, slope 118 + Line 14965: 0:00:31.732595996 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 9, delta -279970944 + Line 14966: 0:00:31.732620200 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -16380064, out 0:00:23.318962531 + [...] + Line 15293: 0:00:31.746166387 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.068966555, base 0:00:23.035042595, recv_diff 0:00:00.033923960, slope 180 + Line 15294: 0:00:31.746194935 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 23, delta -733509373 + Line 15295: 0:00:31.746218828 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -430859680, out 0:00:23.371616248 + Line 15310: 0:00:31.746791023 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:563:calculate_skew: time 0:00:23.069478242, base 0:00:23.035042595, recv_diff 0:00:00.034435647, slope 186 + Line 15311: 0:00:31.746820812 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:580:calculate_skew: filling 24, delta -766364353 + Line 15312: 0:00:31.746845636 9388 d5b9518 DEBUG rtpjitterbuffer rtpjitterbuffer.c:664:calculate_skew: skew -484540427, out 0:00:23.351302168 + [...] + + +The high skew values can also be seen when using gst-launch but no pauses... + +The pause duration is variable (~4s) but the pause is always on the beat every 10s (If the first pause is at 9s, then the next will be at 19s, then 29s, etc...). + +Is it possible to disable the rtpjitterbuffer ? + +Qos: element autovideosink1-actual-sink-d3dvideo sent qos event: live: 1; running time: 30719164049; stream time: 26558070697; timestamp: 30719164049; duration: 33366666 jitter: 3029708354; proportion: 0.15581; quality: 1000000; format: ; processed: 609; dropped: 2; + + +Wifi stall + +0:04:39.600000124 8296 ee56ba0 LOG udpsrc gstudpsrc.c:882:gst_udpsrc_create: doing select, timeout -1 +0:04:39.628925951 8296 ee56b10 DEBUG rtspsrc gstrtspsrc.c:2260:gst_rtspsrc_handle_src_event: pad rtspsrc0:recv_rtp_src_0_275680090_96 received event qos +0:04:39.661793203 8296 ee56b10 DEBUG rtspsrc gstrtspsrc.c:2260:gst_rtspsrc_handle_src_event: pad rtspsrc0:recv_rtp_src_0_275680090_96 received event qos +0:04:42.688912775 8296 ee56ba0 LOG udpsrc gstudpsrc.c:1014:gst_udpsrc_create: read packet of 26 bytes +0:04:42.689055513 8296 ee56ba0 WARN rtpjitterbuffer rtpjitterbuffer.c:570:calculate_skew: delta - skew: 0:00:03.058485393 too big, reset skew + + + +Simply instantiating a QNetworkAccessManager will cause the active wifi network connection to stall for 3 seconds every 10s. + +This affects, not just the Qt app, but also all other processes using the wifi connection. + +From what I have gathered this is due to bearer management polling all interfaces every 10s (can be changed with the QT_BEARER_POLL_TIMEOUT environment variable). +On windows polling the wifi interface will trigger an ssid scan. That scan will stall the active connection. This might not happen with all wifi devices but does with mine. +In my case, setting QT_BEARER_POLL_TIMEOUT to less than 4 seconds results in a DoS ;) + +https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms706783(v=vs.85).aspx +quote: "Since it becomes more difficult for a wireless interface to send and receive data packets while a scan is occurring, the WlanScan function may increase latency until the network scan is complete." + + +# transmit gstreamer buffers over network +tcpserversrc host=0.0.0.0 port=50002 ! gdpdepay ! autovideoconvert ! autovideosink +v4l2src num-buffers=1 ! gdppay ! tcpclientsink host=0.0.0.0 port=50002 + +# play a rtsp stream +rtspsrc location=rtsp://192.168.42.1/AmbaStreamTest latency=30 ! decodebin ! timeoverlay ! autovideosink + +# play video and sound +ksvideosrc ! queue ! mix. +directsoundsrc ! tee name=split ! queue ! directsoundsink +split. ! queue ! wavescope ! queue ! mix. +videomixer name=mix ! queue ! timeoverlay ! autovideosink + +ksvideosrc ! queue ! timeoverlay ! autovideosink +directsoundsrc ! queue ! directsoundsink + +directsoundsrc ! tee name=split ! queue ! directsoundsink +split. ! queue ! wavescope ! autovideosink + +filesrc location=C:/Users/Utilisateur/Desktop/hst_2.mpg ! decodebin ! autovideosink + +dx9screencapsrc ! queue ! videoconvert ! x264enc bitrate=498 ! avimux ! filesink location=capture.avi + + +compositor name=mixer background=black sink_0::offset=0 sink_1::offset=0 ! videoconvert ! autovideosink +ksvideosrc device_index=0 ! decodebin ! identity drop-probability=0 ! queue max-size-buffers=0 max-size-bytes=0 max-size-time=10000000000 ! mixer. +udpsrc port=9000 ! identity drop-probability=0 dump=false ! ! video/x-raw, width=640, height=480 ! videorate drop-only=true ! video/x-raw, framerate=10/1 ! queue max-size-buffers=0 max-size-bytes=0 max-size-time=10000000000 ! mixer. + + +compositor name=mixer sink_1::ypos=50 ! videoconvert ! timeoverlay shaded-background=true auto-resize=false ! autovideosink sync=true +ksvideosrc ! video/x-raw ! decodebin ! queue ! mixer. +udpsrc port=9000 ! identity dump=false ! textrender halignment=left line-alignment=left ! video/x-raw, width=320, height=120 ! videorate drop-only=true ! video/x-raw, framerate=10/1 ! queue ! mixer. diff --git a/ground/gcs/src/libs/gstreamer/overlay.h b/ground/gcs/src/libs/gstreamer/overlay.h new file mode 100644 index 0000000000..46ae4089a1 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/overlay.h @@ -0,0 +1,39 @@ +/** + ****************************************************************************** + * + * @file overlay.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef OVERLAY_H_ +#define OVERLAY_H_ + +class Overlay { +public: + Overlay() + {} + virtual ~Overlay() + {} + virtual void expose() = 0; +}; + +#endif /* OVERLAY_H_ */ diff --git a/ground/gcs/src/libs/gstreamer/pipeline.cpp b/ground/gcs/src/libs/gstreamer/pipeline.cpp new file mode 100644 index 0000000000..a0d0fa7224 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/pipeline.cpp @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * + * @file pipeline.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 "pipeline.h" + +#include "gst_util.h" + +Pipeline::Pipeline() +{ + // initialize gstreamer + gst::init(NULL, NULL); +} + +Pipeline::~Pipeline() +{} diff --git a/ground/gcs/src/libs/gstreamer/pipeline.h b/ground/gcs/src/libs/gstreamer/pipeline.h new file mode 100644 index 0000000000..2ecb5a61ae --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/pipeline.h @@ -0,0 +1,41 @@ +/** + ****************************************************************************** + * + * @file pipeline.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef PIPELINE_H_ +#define PIPELINE_H_ + +#include "gst_global.h" + +class GST_LIB_EXPORT Pipeline { +public: + enum State { + VoidPending, Null, Ready, Paused, Playing + }; + Pipeline(); + virtual ~Pipeline(); +}; + +#endif /* PIPELINE_H_ */ diff --git a/ground/gcs/src/libs/gstreamer/pipelineevent.h b/ground/gcs/src/libs/gstreamer/pipelineevent.h new file mode 100644 index 0000000000..3dfdc0a4e7 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/pipelineevent.h @@ -0,0 +1,413 @@ +/** + ****************************************************************************** + * + * @file pipelineevent.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef PIPELINEEVENT_H_ +#define PIPELINEEVENT_H_ + +#include +#include + +#include "pipeline.h" +#include "overlay.h" + +class PipelineEvent : public QEvent { +public: + // event types + static const QEvent::Type PrepareWindowId; + static const QEvent::Type StateChange; + static const QEvent::Type StreamStatus; + static const QEvent::Type NewClock; + static const QEvent::Type ClockProvide; + static const QEvent::Type ClockLost; + static const QEvent::Type Progress; + static const QEvent::Type Latency; + static const QEvent::Type Qos; + static const QEvent::Type Eos; + static const QEvent::Type Error; + static const QEvent::Type Warning; + static const QEvent::Type Info; + + PipelineEvent(QEvent::Type type, QString src) : + QEvent(type), src(src) + {} + virtual ~PipelineEvent() + {} +public: + QString src; +}; + +const QEvent::Type PipelineEvent::PrepareWindowId = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::StateChange = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::StreamStatus = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::NewClock = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::ClockProvide = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::ClockLost = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::Progress = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::Latency = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::Qos = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::Eos = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::Error = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::Warning = static_cast(QEvent::registerEventType()); +const QEvent::Type PipelineEvent::Info = static_cast(QEvent::registerEventType()); + +class PrepareWindowIdEvent : public PipelineEvent { +public: + PrepareWindowIdEvent(QString src, Overlay *overlay) : + PipelineEvent(PrepareWindowId, src), overlay(overlay) + {} + Overlay *getOverlay() + { + return overlay; + } + static QEvent::Type type() + { + return PrepareWindowId; + } +private: + Overlay *overlay; +}; + +class StateChangedEvent : public PipelineEvent { +public: + StateChangedEvent(QString src, Pipeline::State oldState, Pipeline::State newState, Pipeline::State pendingState) : + PipelineEvent(StateChange, src), oldState(oldState), newState(newState), pendingState(pendingState) + {} + static QEvent::Type type() + { + return StateChange; + } + Pipeline::State getOldState() + { + return oldState; + } + Pipeline::State getNewState() + { + return newState; + } + Pipeline::State getPendingState() + { + return pendingState; + } + static const char *stateName(Pipeline::State state) + { + switch (state) { + case Pipeline::VoidPending: + return "VoidPending"; + + case Pipeline::Null: + return "Null"; + + case Pipeline::Ready: + return "Ready"; + + case Pipeline::Paused: + return "Paused"; + + case Pipeline::Playing: + return "Playing"; + } + return ""; + } +private: + Pipeline::State oldState; + Pipeline::State newState; + Pipeline::State pendingState; +}; + +class StreamStatusEvent : public PipelineEvent { +public: + enum StreamStatusType { + Create, Enter, Leave, Destroy, Start, Pause, Stop, Null + }; + StreamStatusEvent(QString src, StreamStatusType status, QString owner) : + PipelineEvent(StreamStatus, src), status(status), owner(owner) + {} + static QEvent::Type type() + { + return StreamStatus; + } + StreamStatusType getStatus() + { + return status; + } + const char *getStatusName() + { + return statusName(status); + } + static const char *statusName(StreamStatusType status) + { + switch (status) { + case StreamStatusEvent::Create: + return "Create"; + + case StreamStatusEvent::Enter: + return "Enter"; + + case StreamStatusEvent::Leave: + return "Leave"; + + case StreamStatusEvent::Destroy: + return "Destroy"; + + case StreamStatusEvent::Start: + return "Start"; + + case StreamStatusEvent::Pause: + return "Pause"; + + case StreamStatusEvent::Stop: + return "Stop"; + + case StreamStatusEvent::Null: + return "Null"; + } + return ""; + } + QString getOwner() + { + return owner; + } +private: + StreamStatusType status; + QString owner; +}; + +class ClockEvent : public PipelineEvent { +public: + ClockEvent(QEvent::Type type, QString src, QString name) : PipelineEvent(type, src), name(name) + {} + QString getName() + { + return name; + } +private: + QString name; +}; + +class NewClockEvent : public ClockEvent { +public: + NewClockEvent(QString src, QString name) : ClockEvent(NewClock, src, name) + {} + static QEvent::Type type() + { + return NewClock; + } +}; + +class ClockProvideEvent : public ClockEvent { +public: + ClockProvideEvent(QString src, QString name, bool ready) : ClockEvent(ClockProvide, src, name), ready(ready) + {} + static QEvent::Type type() + { + return ClockProvide; + } + bool isReady() + { + return ready; + } +private: + bool ready; +}; + + +class ClockLostEvent : public ClockEvent { +public: + ClockLostEvent(QString src, QString name) : ClockEvent(ClockLost, src, name) + {} + static QEvent::Type type() + { + return ClockLost; + } +}; + +class ProgressEvent : public PipelineEvent { +public: + enum ProgressType { + Start, Continue, Complete, Cancelled, Error + }; + + ProgressEvent(QString src, ProgressType progressType, QString code, QString text) : + PipelineEvent(Progress, src), progressType(progressType), code(code), text(text) + {} + static QEvent::Type type() + { + return Progress; + } + ProgressType getProgressType() + { + return progressType; + } + QString getCode() + { + return code; + } + QString getText() + { + return text; + } +private: + ProgressType progressType; + QString code; + QString text; +}; + +class LatencyEvent : public PipelineEvent { +public: + LatencyEvent(QString src) : + PipelineEvent(Latency, src) + {} + static QEvent::Type type() + { + return Latency; + } +}; + +class QosData { +public: + // timestamps and live status + // If the message was generated by a live element + bool live; + // running_time, stream_time, timestamp and duration of the dropped buffer. + // Values of GST_CLOCK_TIME_NONE mean unknown values. + quint64 running_time; + quint64 stream_time; + quint64 timestamp; + quint64 duration; + + // values + // The difference of the running-time against the deadline. + qint64 jitter; + // Long term prediction of the ideal rate relative to normal rate to get optimal quality. + qreal proportion; // won't work on ARM? + // An element dependent integer value that specifies the current quality level of the element. + // The default maximum quality is 1000000. + qint32 quality; + + // stats + // QoS stats representing the history of the current continuous pipeline playback period. + // When format is GST_FORMAT_UNDEFINED both dropped and processed are invalid. + // Values of -1 for either processed or dropped mean unknown values. + + // Units of the 'processed' and 'dropped' fields. + // Video sinks and video filters will use GST_FORMAT_BUFFERS (frames). + // Audio sinks and audio filters will likely use GST_FORMAT_DEFAULT (samples) + // GstFormat format; + // Total number of units correctly processed since the last state change to READY or a flushing operation. + quint64 processed; + // Total number of units dropped since the last state change to READY or a flushing operation. + quint64 dropped; + + QString timestamps() + { + return QString("live: %0; running time: %1; stream time: %2; timestamp: %3; duration: %4").arg(live).arg( + running_time).arg(stream_time).arg(timestamp).arg(duration); + } + QString values() + { + return QString("jitter: %0; proportion: %1; quality: %2;").arg(jitter).arg(proportion).arg(quality); + } + QString stats() + { + return QString("format: %0; processed: %1; dropped: %2;").arg("").arg(processed).arg(dropped); + } +}; + +class QosEvent : public PipelineEvent { +public: + QosEvent(QString src, QosData data) : PipelineEvent(Qos, src), data(data) + {} + static QEvent::Type type() + { + return Qos; + } + QosData getData() + { + return data; + } +private: + QosData data; +}; + +class EosEvent : public PipelineEvent { +public: + EosEvent(QString src) : PipelineEvent(Eos, src) + {} + static QEvent::Type type() + { + return Eos; + } +}; + +class MessageEvent : public PipelineEvent { +public: + MessageEvent(QEvent::Type type, QString src, QString message, QString debug) : + PipelineEvent(type, src), message(message), debug(debug) + {} + QString getMessage() + { + return message; + } + QString getDebug() + { + return debug; + } +private: + QString message; + QString debug; +}; + +class ErrorEvent : public MessageEvent { +public: + ErrorEvent(QString src, QString message, QString debug) : MessageEvent(Error, src, message, debug) + {} + static QEvent::Type type() + { + return Error; + } +}; + +class WarningEvent : public MessageEvent { +public: + WarningEvent(QString src, QString message, QString debug) : MessageEvent(Warning, src, message, debug) + {} + static QEvent::Type type() + { + return Warning; + } +}; + +class InfoEvent : public MessageEvent { +public: + InfoEvent(QString src, QString message, QString debug) : MessageEvent(Info, src, message, debug) + {} + static QEvent::Type type() + { + return Info; + } +}; + +#endif /* PIPELINEEVENT_H_ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/.no-auto-format b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/.no-auto-format new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp new file mode 100644 index 0000000000..2e7a71a718 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp @@ -0,0 +1,96 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Library <2002> Ronald Bultje + * Copyright (C) 2007 David A. Schleef + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "cameraevent.hpp" + +#include + +#include +#include + +/** + * gst_video_event_new_still_frame: + * @in_still: boolean value for the still-frame state of the event. + * + * Creates a new Still Frame event. If @in_still is %TRUE, then the event + * represents the start of a still frame sequence. If it is %FALSE, then + * the event ends a still frame sequence. + * + * To parse an event created by gst_video_event_new_still_frame() use + * gst_video_event_parse_still_frame(). + * + * Returns: The new GstEvent + */ +GstEvent * +gst_camera_event_new_calibrated (gchar * settings) +{ + GstEvent *calibrated_event; + GstStructure *s; + + s = gst_structure_new (GST_CAMERA_EVENT_CALIBRATED_NAME, + "undistort-settings", G_TYPE_STRING, g_strdup(settings), NULL); + + calibrated_event = gst_event_new_custom (GST_EVENT_CUSTOM_BOTH, s); + + return calibrated_event; +} + +/** + * gst_video_event_parse_still_frame: + * @event: A #GstEvent to parse + * @in_still: A boolean to receive the still-frame status from the event, or NULL + * + * Parse a #GstEvent, identify if it is a Still Frame event, and + * return the still-frame state from the event if it is. + * If the event represents the start of a still frame, the in_still + * variable will be set to TRUE, otherwise FALSE. It is OK to pass NULL for the + * in_still variable order to just check whether the event is a valid still-frame + * event. + * + * Create a still frame event using gst_video_event_new_still_frame() + * + * Returns: %TRUE if the event is a valid still-frame event. %FALSE if not + */ +gboolean +gst_camera_event_parse_calibrated (GstEvent * event, gchar ** settings) +{ + const GstStructure *s; + + g_return_val_if_fail (event != NULL, FALSE); + + if (GST_EVENT_TYPE (event) != GST_EVENT_CUSTOM_BOTH) + return FALSE; /* Not a calibrated event */ + + s = gst_event_get_structure (event); + if (s == NULL + || !gst_structure_has_name (s, GST_CAMERA_EVENT_CALIBRATED_NAME)) + return FALSE; /* Not a calibrated event */ + + const gchar *str = gst_structure_get_string(s, "undistort-settings"); + if (!str) + return FALSE; /* Not calibrated frame event */ + + //qDebug() << "*** " << buf;//QString::fromStdString(buf); + + *settings = g_strdup (str); + + return TRUE; +} diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp new file mode 100644 index 0000000000..cc6d362c8e --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp @@ -0,0 +1,37 @@ +/* GStreamer + * Copyright (C) <2011> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_CAMERA_EVENT_H__ +#define __GST_CAMERA_EVENT_H__ + +#include + +G_BEGIN_DECLS + +#define GST_CAMERA_EVENT_CALIBRATED_NAME "GstEventCalibrated" + +/* camera calibration event creation and parsing */ + +GstEvent * gst_camera_event_new_calibrated (gchar * settings); + +gboolean gst_camera_event_parse_calibrated (GstEvent * event, gchar ** settings); + +G_END_DECLS + +#endif /* __GST_CAMERA_EVENT_H__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp new file mode 100644 index 0000000000..620a5f9723 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp @@ -0,0 +1,48 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Library <2002> Ronald Bultje + * Copyright (C) 2007 David A. Schleef + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "camerautils.hpp" + +#include + +#include +#include + +gchar * +camera_serialize_undistort_settings (cv::Mat &cameraMatrix, cv::Mat &distCoeffs) +{ + cv::FileStorage fs(".xml", cv::FileStorage::WRITE + cv::FileStorage::MEMORY); + fs << "cameraMatrix" << cameraMatrix; + fs << "distCoeffs" << distCoeffs; + std::string buf = fs.releaseAndGetString(); + + return g_strdup(buf.c_str()); +} + +gboolean +camera_deserialize_undistort_settings (gchar * str, cv::Mat &cameraMatrix, cv::Mat &distCoeffs) +{ + cv::FileStorage fs(str, cv::FileStorage::READ + cv::FileStorage::MEMORY); + fs["cameraMatrix"] >> cameraMatrix; + fs["distCoeffs"] >> distCoeffs; + + return TRUE; +} diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp new file mode 100644 index 0000000000..356c1d7f22 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp @@ -0,0 +1,34 @@ +/* GStreamer + * Copyright (C) <2011> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_CAMERA_UTILS_H__ +#define __GST_CAMERA_UTILS_H__ + +#include +#include + +G_BEGIN_DECLS + +gchar *camera_serialize_undistort_settings (cv::Mat &cameraMatrix, cv::Mat &distCoeffs); + +gboolean camera_deserialize_undistort_settings (gchar *str, cv::Mat &cameraMatrix, cv::Mat &distCoeffs); + +G_END_DECLS + +#endif /* __GST_CAMERA_UTILS_H__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp new file mode 100644 index 0000000000..39d8c17b1c --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp @@ -0,0 +1,954 @@ +/* + * GStreamer + * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2008 Michael Sheldon + * Copyright (C) 2011 Stefan Sauer + * Copyright (C) 2014 Robert Jobbagy + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-cameracalibration + * + * Performs face detection on videos and images. + * If you have high cpu load you need to use videoscale with capsfilter and reduce the video resolution. + * + * The image is scaled down multiple times using the GstCameraCalibration::scale-factor + * until the size is <= GstCameraCalibration::min-size-width or + * GstCameraCalibration::min-size-height. + * + * + * Example launch line + * |[ + * gst-launch-1.0 autovideosrc ! decodebin ! colorspace ! cameracalibration ! videoconvert ! xvimagesink + * ]| Detect and show faces + * |[ + * gst-launch-1.0 autovideosrc ! video/x-raw,width=320,height=240 ! videoconvert ! cameracalibration min-size-width=60 min-size-height=60 ! colorspace ! xvimagesink + * ]| Detect large faces on a smaller image + * + * + */ + +/* FIXME: development version of OpenCV has CV_HAAR_FIND_BIGGEST_OBJECT which + * we might want to use if available + * see https://code.ros.org/svn/opencv/trunk/opencv/modules/objdetect/src/haar.cpp + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstcameracalibration.h" + +#if (CV_MAJOR_VERSION >= 3) +#include +#endif +#include + +#include + +#include "camerautils.hpp" +#include "cameraevent.hpp" + +#include + +#include + +GST_DEBUG_CATEGORY_STATIC (gst_camera_calibration_debug); +#define GST_CAT_DEFAULT gst_camera_calibration_debug + +#define DEFAULT_CALIBRATON_PATTERN GST_CAMERACALIBRATION_PATTERN_CHESSBOARD +#define DEFAULT_BOARD_WIDTH 9 +#define DEFAULT_BOARD_HEIGHT 6 +#define DEFAULT_SQUARE_SIZE 50 +#define DEFAULT_ASPECT_RATIO 1.0 +#define DEFAULT_CORNER_SUB_PIXEL true +#define DEFAULT_ZERO_TANGENT_DISTORTION false +#define DEFAULT_CENTER_PRINCIPAL_POINT false +#define DEFAULT_USE_FISHEYE false +#define DEFAULT_FRAME_COUNT 25 +#define DEFAULT_DELAY 350 +#define DEFAULT_SHOW_CORNERS true + +///* Filter signals and args */ +//enum +//{ +// /* FILL ME */ +// LAST_SIGNAL +//}; + +enum +{ + PROP_0, + PROP_CALIBRATON_PATTERN, + PROP_BOARD_WIDTH, + PROP_BOARD_HEIGHT, + PROP_SQUARE_SIZE, + PROP_ASPECT_RATIO, + PROP_CORNER_SUB_PIXEL, + PROP_ZERO_TANGENT_DISTORTION, + PROP_CENTER_PRINCIPAL_POINT, + PROP_USE_FISHEYE, + PROP_FRAME_COUNT, + PROP_DELAY, + PROP_SHOW_CORNERS +}; + +enum { + DETECTION = 0, + CAPTURING = 1, + CALIBRATED = 2 +}; + +#define GST_TYPE_CAMERA_CALIBRATION_PATTERN (cameracalibration_pattern_get_type ()) + +static GType +cameracalibration_pattern_get_type (void) +{ + static GType cameracalibration_pattern_type = 0; + static const GEnumValue cameracalibration_pattern[] = { + {GST_CAMERACALIBRATION_PATTERN_CHESSBOARD, "Chessboard", "chessboard"}, + {GST_CAMERACALIBRATION_PATTERN_CIRCLES_GRID, "Circle Grids", "circle_grids"}, + {GST_CAMERACALIBRATION_PATTERN_ASYMMETRIC_CIRCLES_GRID, "Asymmetric Circle Grids", "asymmetric_circle_grids"}, + {0, NULL, NULL}, + }; + + if (!cameracalibration_pattern_type) { + cameracalibration_pattern_type = + g_enum_register_static ("GstCameraCalibrationPattern", cameracalibration_pattern); + } + return cameracalibration_pattern_type; +} + +G_DEFINE_TYPE (GstCameraCalibration, gst_camera_calibration, GST_TYPE_OPENCV_VIDEO_FILTER); + +static void gst_camera_calibration_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_camera_calibration_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +//static gboolean gst_camera_calibration_set_caps (GstOpencvVideoFilter * transform, +// gint in_width, gint in_height, gint in_depth, gint in_channels, +// gint out_width, gint out_height, gint out_depth, gint out_channels); +static GstFlowReturn gst_camera_calibration_transform_frame_ip ( + GstOpencvVideoFilter * cvfilter, GstBuffer * frame, IplImage * img); + +/* Clean up */ +static void +gst_camera_calibration_finalize (GObject * obj) +{ + G_OBJECT_CLASS (gst_camera_calibration_parent_class)->finalize (obj); +} + +/* initialize the cameracalibration's class */ +static void +gst_camera_calibration_class_init (GstCameraCalibrationClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstOpencvVideoFilterClass *opencvfilter_class = GST_OPENCV_VIDEO_FILTER_CLASS (klass); + GstCaps *caps; + GstPadTemplate *templ; + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_camera_calibration_finalize); + gobject_class->set_property = gst_camera_calibration_set_property; + gobject_class->get_property = gst_camera_calibration_get_property; + + opencvfilter_class->cv_trans_ip_func = + gst_camera_calibration_transform_frame_ip; + + g_object_class_install_property (gobject_class, PROP_CALIBRATON_PATTERN, + g_param_spec_enum ("pattern", "Calibration Pattern", + "One of the chessboard, circles, or asymmetric circle pattern", + GST_TYPE_CAMERA_CALIBRATION_PATTERN, DEFAULT_CALIBRATON_PATTERN, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_BOARD_WIDTH, + g_param_spec_int ("board-width", "Board Width", + "The board width in number of items", + 1, G_MAXINT, DEFAULT_BOARD_WIDTH, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_BOARD_HEIGHT, + g_param_spec_int ("board-height", "Board Height", + "The board height in number of items", + 1, G_MAXINT, DEFAULT_BOARD_WIDTH, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_SQUARE_SIZE, + g_param_spec_float ("square-size", "Square Size", + "The size of a square in your defined unit (point, millimeter, etc.)", + 0.0, G_MAXFLOAT, DEFAULT_SQUARE_SIZE, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_ASPECT_RATIO, + g_param_spec_float ("aspect-ratio", "Aspect Ratio", + "The aspect ratio", + 0.0, G_MAXFLOAT, DEFAULT_ASPECT_RATIO, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_CORNER_SUB_PIXEL, + g_param_spec_boolean ("corner-sub-pixel", "Corner Sub Pixel", + "Improve corner detection accuracy for chessboard", + DEFAULT_CORNER_SUB_PIXEL, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_ZERO_TANGENT_DISTORTION, + g_param_spec_boolean ("zero-tangent-distorsion", "Zero Tangent Distorsion", + "Assume zero tangential distortion", + DEFAULT_ZERO_TANGENT_DISTORTION, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_CENTER_PRINCIPAL_POINT, + g_param_spec_boolean ("center-principal-point", "Center Principal Point", + "Fix the principal point at the center", + DEFAULT_CENTER_PRINCIPAL_POINT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_USE_FISHEYE, + g_param_spec_boolean ("use-fisheye", "Use Fisheye", + "Use fisheye camera model for calibration", + DEFAULT_USE_FISHEYE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_DELAY, + g_param_spec_int ("delay", "Delay", + "Sampling periodicity in ms", 0, G_MAXINT, + DEFAULT_DELAY, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_FRAME_COUNT, + g_param_spec_int ("frame-count", "Frame Count", + "The number of frames to use from the input for calibration", 1, G_MAXINT, + DEFAULT_FRAME_COUNT, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_SHOW_CORNERS, + g_param_spec_boolean ("show-corners", "Show Corners", + "Show corners", + DEFAULT_SHOW_CORNERS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + + gst_element_class_set_static_metadata (element_class, + "cameracalibration", + "Filter/Effect/Video", + "Performs camera calibration", + "Philippe Renon "); + + /* add sink and source pad templates */ + caps = gst_opencv_caps_from_cv_image_type (CV_8UC4); + gst_caps_append (caps, gst_opencv_caps_from_cv_image_type (CV_8UC3)); + gst_caps_append (caps, gst_opencv_caps_from_cv_image_type (CV_8UC1)); + templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_caps_ref (caps)); + gst_element_class_add_pad_template (element_class, templ); + templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps); + gst_element_class_add_pad_template (element_class, templ); + +// gst_element_class_add_static_pad_template (element_class, &src_factory); +// gst_element_class_add_static_pad_template (element_class, &sink_factory); +} + +/* initialize the new element + * initialize instance structure + */ +static void +gst_camera_calibration_init (GstCameraCalibration * calib) +{ + calib->calibrationPattern = DEFAULT_CALIBRATON_PATTERN; + calib->boardSize.width = DEFAULT_BOARD_WIDTH; + calib->boardSize.height = DEFAULT_BOARD_HEIGHT; + calib->squareSize = DEFAULT_SQUARE_SIZE; + calib->aspectRatio = DEFAULT_ASPECT_RATIO; + calib->cornerSubPix = DEFAULT_CORNER_SUB_PIXEL; + calib->calibZeroTangentDist = DEFAULT_ZERO_TANGENT_DISTORTION; + calib->calibFixPrincipalPoint = DEFAULT_CENTER_PRINCIPAL_POINT; + calib->useFisheye = DEFAULT_USE_FISHEYE; + calib->nrFrames = DEFAULT_FRAME_COUNT; + calib->delay = DEFAULT_DELAY; + calib->showCorners = DEFAULT_SHOW_CORNERS; + + calib->flags = cv::CALIB_FIX_K4 | cv::CALIB_FIX_K5; + if (calib->calibFixPrincipalPoint) calib->flags |= cv::CALIB_FIX_PRINCIPAL_POINT; + if (calib->calibZeroTangentDist) calib->flags |= cv::CALIB_ZERO_TANGENT_DIST; + if (calib->aspectRatio) calib->flags |= cv::CALIB_FIX_ASPECT_RATIO; + + if (calib->useFisheye) { + // the fisheye model has its own enum, so overwrite the flags + calib->flags = cv::fisheye::CALIB_FIX_SKEW | cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC | + // cv::fisheye::CALIB_FIX_K1 | + cv::fisheye::CALIB_FIX_K2 | cv::fisheye::CALIB_FIX_K3 | cv::fisheye::CALIB_FIX_K4; + } + + calib->mode = CAPTURING; //DETECTION; + calib->prevTimestamp = 0; + + calib->imagePoints.clear(); + calib->cameraMatrix = 0; + calib->distCoeffs = 0; + + gst_opencv_video_filter_set_in_place ( + GST_OPENCV_VIDEO_FILTER_CAST (calib), TRUE); +} + +static void +gst_camera_calibration_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstCameraCalibration *calib = GST_CAMERA_CALIBRATION (object); + + switch (prop_id) { + case PROP_CALIBRATON_PATTERN: + calib->calibrationPattern = g_value_get_enum (value); + break; + case PROP_BOARD_WIDTH: + calib->boardSize.width = g_value_get_int (value); + break; + case PROP_BOARD_HEIGHT: + calib->boardSize.height = g_value_get_int (value); + break; + case PROP_SQUARE_SIZE: + calib->squareSize = g_value_get_float (value); + break; + case PROP_ASPECT_RATIO: + calib->aspectRatio = g_value_get_float (value); + break; + case PROP_CORNER_SUB_PIXEL: + calib->cornerSubPix = g_value_get_boolean (value); + break; + case PROP_ZERO_TANGENT_DISTORTION: + calib->calibZeroTangentDist = g_value_get_boolean (value); + break; + case PROP_CENTER_PRINCIPAL_POINT: + calib->calibFixPrincipalPoint = g_value_get_boolean (value); + break; + case PROP_USE_FISHEYE: + calib->useFisheye = g_value_get_boolean (value); + break; + case PROP_FRAME_COUNT: + calib->nrFrames = g_value_get_int (value); + break; + case PROP_DELAY: + calib->delay = g_value_get_int (value); + break; + case PROP_SHOW_CORNERS: + calib->showCorners = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_camera_calibration_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstCameraCalibration *calib = GST_CAMERA_CALIBRATION (object); + + switch (prop_id) { + case PROP_CALIBRATON_PATTERN: + g_value_set_enum (value, calib->calibrationPattern); + break; + case PROP_BOARD_WIDTH: + g_value_set_int (value, calib->boardSize.width); + break; + case PROP_BOARD_HEIGHT: + g_value_set_int (value, calib->boardSize.height); + break; + case PROP_SQUARE_SIZE: + g_value_set_float (value, calib->squareSize); + break; + case PROP_ASPECT_RATIO: + g_value_set_float (value, calib->aspectRatio); + break; + case PROP_CORNER_SUB_PIXEL: + g_value_set_boolean (value, calib->cornerSubPix); + break; + case PROP_ZERO_TANGENT_DISTORTION: + g_value_set_boolean (value, calib->calibZeroTangentDist); + break; + case PROP_CENTER_PRINCIPAL_POINT: + g_value_set_boolean (value, calib->calibFixPrincipalPoint); + break; + case PROP_USE_FISHEYE: + g_value_set_boolean (value, calib->useFisheye); + break; + case PROP_FRAME_COUNT: + g_value_set_int (value, calib->nrFrames); + break; + case PROP_DELAY: + g_value_set_int (value, calib->delay); + break; + case PROP_SHOW_CORNERS: + g_value_set_boolean (value, calib->showCorners); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* GstElement vmethod implementations */ + +/* this function handles the link with other elements */ +//static gboolean +//gst_camera_calibration_set_caps (GstOpencvVideoFilter * transform, gint in_width, +// gint in_height, gint in_depth, gint in_channels, +// gint out_width, gint out_height, gint out_depth, gint out_channels) +//{ +// GstCameraCalibration *calib; +// +// calib = GST_CAMERA_CALIBRATION (transform); +// +// if (calib->cvGray) +// cvReleaseImage (&calib->cvGray); +// +// calib->cvGray = cvCreateImage (cvSize (in_width, in_height), IPL_DEPTH_8U, +// 1); +// +// return TRUE; +//} + +//static GstMessage * +//gst_camera_calibration_message_new (GstCameraCalibration * calib, GstBuffer * buf) +//{ +// GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (calib); +// GstStructure *s; +// GstClockTime running_time, stream_time; +// +// running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, +// GST_BUFFER_TIMESTAMP (buf)); +// stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, +// GST_BUFFER_TIMESTAMP (buf)); +// +// s = gst_structure_new ("cameracalibration", +// "timestamp", G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (buf), +// "stream-time", G_TYPE_UINT64, stream_time, +// "running-time", G_TYPE_UINT64, running_time, +// "duration", G_TYPE_UINT64, GST_BUFFER_DURATION (buf), NULL); +// +// return gst_message_new_element (GST_OBJECT (calib), s); +//} + +void camera_calibration_run(GstCameraCalibration *calib, IplImage *img); + +/* + * Performs the camera calibration + */ +static GstFlowReturn +gst_camera_calibration_transform_frame_ip (GstOpencvVideoFilter * cvfilter, + G_GNUC_UNUSED GstBuffer * frame, IplImage * img) +{ + GstCameraCalibration *calib = GST_CAMERA_CALIBRATION (cvfilter); + + camera_calibration_run(calib, img); + + return GST_FLOW_OK; +} + + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +gboolean +gst_camera_calibration_plugin_init (GstPlugin * plugin) +{ + /* debug category for filtering log messages */ + GST_DEBUG_CATEGORY_INIT (gst_camera_calibration_debug, "cameracalibration", + 0, + "Performs camera calibration"); + + return gst_element_register (plugin, "cameracalibration", GST_RANK_NONE, + GST_TYPE_CAMERA_CALIBRATION); +} + +// void validate() +// { +// goodInput = true; +// if (boardSize.width <= 0 || boardSize.height <= 0) +// { +// cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << endl; +// goodInput = false; +// } +// if (squareSize <= 10e-6) +// { +// cerr << "Invalid square size " << squareSize << endl; +// goodInput = false; +// } +// if (nrFrames <= 0) +// { +// cerr << "Invalid number of frames " << nrFrames << endl; +// goodInput = false; +// } +// +// if (input.empty()) // Check for valid input +// inputType = INVALID; +// else +// { +// if (input[0] >= '0' && input[0] <= '9') +// { +// stringstream ss(input); +// ss >> cameraID; +// inputType = CAMERA; +// } +// else +// { +// if (readStringList(input, imageList)) +// { +// inputType = IMAGE_LIST; +// nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size(); +// } +// else +// inputType = VIDEO_FILE; +// } +// if (inputType == CAMERA) +// inputCapture.open(cameraID); +// if (inputType == VIDEO_FILE) +// inputCapture.open(input); +// if (inputType != IMAGE_LIST && !inputCapture.isOpened()) +// inputType = INVALID; +// } +// if (inputType == INVALID) +// { +// cerr << " Input does not exist: " << input; +// goodInput = false; +// } +// +// flag = CALIB_FIX_K4 | CALIB_FIX_K5; +// if(calibFixPrincipalPoint) flag |= CALIB_FIX_PRINCIPAL_POINT; +// if(calibZeroTangentDist) flag |= CALIB_ZERO_TANGENT_DIST; +// if(aspectRatio) flag |= CALIB_FIX_ASPECT_RATIO; +// +// if (useFisheye) { +// // the fisheye model has its own enum, so overwrite the flags +// flag = fisheye::CALIB_FIX_SKEW | fisheye::CALIB_RECOMPUTE_EXTRINSIC | +// // fisheye::CALIB_FIX_K1 | +// fisheye::CALIB_FIX_K2 | fisheye::CALIB_FIX_K3 | fisheye::CALIB_FIX_K4; +// } +// +// calibrationPattern = NOT_EXISTING; +// if (!patternToUse.compare("CHESSBOARD")) calibrationPattern = CHESSBOARD; +// if (!patternToUse.compare("CIRCLES_GRID")) calibrationPattern = CIRCLES_GRID; +// if (!patternToUse.compare("ASYMMETRIC_CIRCLES_GRID")) calibrationPattern = ASYMMETRIC_CIRCLES_GRID; +// if (calibrationPattern == NOT_EXISTING) +// { +// cerr << " Camera calibration mode does not exist: " << patternToUse << endl; +// goodInput = false; +// } +// atImageList = 0; +// +// } + +bool runCalibration(GstCameraCalibration *calib, cv::Size imageSize, cv::Mat& cameraMatrix, cv::Mat& distCoeffs, + std::vector > imagePoints ); + +void doCalibration(GstElement *element, gpointer user_data); + +void camera_calibration_run(GstCameraCalibration *calib, IplImage *img) +{ + cv::Mat view = cv::cvarrToMat(img); + + // For camera only take new samples after delay time + if (calib->mode == CAPTURING) { + + // get_input + cv::Size imageSize = view.size(); + + // find_pattern + // FIXME find ways to reduce CPU usage + // don't do it on all frames ? will it help ? corner display will be affected. + // in a separate frame? + // in a separate element that gets composited back into the main stream (video is tee-d into it and can then be decimated, scaled, etc..) + + std::vector pointBuf; + bool found; + int chessBoardFlags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE; + + if (!calib->useFisheye) { + // fast check erroneously fails with high distortions like fisheye + chessBoardFlags |= cv::CALIB_CB_FAST_CHECK; + } + + // Find feature points on the input format + switch(calib->calibrationPattern) { + case GST_CAMERACALIBRATION_PATTERN_CHESSBOARD: + found = cv::findChessboardCorners(view, calib->boardSize, pointBuf, chessBoardFlags); + break; + case GST_CAMERACALIBRATION_PATTERN_CIRCLES_GRID: + found = cv::findCirclesGrid(view, calib->boardSize, pointBuf); + break; + case GST_CAMERACALIBRATION_PATTERN_ASYMMETRIC_CIRCLES_GRID: + found = cv::findCirclesGrid(view, calib->boardSize, pointBuf, cv::CALIB_CB_ASYMMETRIC_GRID ); + break; + default: + found = false; + break; + } + + bool blinkOutput = false; + if (found) { + // improve the found corners' coordinate accuracy for chessboard + if (calib->calibrationPattern == GST_CAMERACALIBRATION_PATTERN_CHESSBOARD && calib->cornerSubPix) { + // FIXME findChessboardCorners and alike do a cv::COLOR_BGR2GRAY (and a histogram balance) + // the color convert should be done once (if needed) and shared + // FIXME keep viewGray around to avoid reallocating it each time... + cv::Mat viewGray; + cv::cvtColor(view, viewGray, cv::COLOR_BGR2GRAY); + cv::cornerSubPix(viewGray, pointBuf, cv::Size(11, 11), + cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1)); + } + + // For camera only take new samples after delay time + if ((calib->mode == CAPTURING) && ((clock() - calib->prevTimestamp) > calib->delay * 1e-3 * CLOCKS_PER_SEC)) { + calib->imagePoints.push_back(pointBuf); + calib->prevTimestamp = clock(); + blinkOutput = true; + } + + // Draw the cornerfilter + if (calib->showCorners) { + cv::drawChessboardCorners(view, calib->boardSize, cv::Mat(pointBuf), found); + } + } + + // If got enough frames then stop calibration and show result + if (calib->mode == CAPTURING && calib->imagePoints.size() >= (size_t)calib->nrFrames) { + + //GstElementCallAsyncFunc func; + //gst_element_call_async (GST_ELEMENT (calib), /*GstElementCallAsyncFunc*/ doCalibration, NULL, NULL); + + if (runCalibration(calib, imageSize, calib->cameraMatrix, calib->distCoeffs, calib->imagePoints)) { + calib->mode = CALIBRATED; + + GstPad *sinkPad = GST_BASE_TRANSFORM_SINK_PAD (calib); + //GstPad *srcPad = GST_BASE_TRANSFORM_SRC_PAD (calib); + GstEvent *event; + //gboolean result; + + // create calibrated event and send upstream and downstream + // FIXME should keep settings around for answering queries + gchar *settings = camera_serialize_undistort_settings(calib->cameraMatrix, calib->distCoeffs); + event = gst_camera_event_new_calibrated(settings); + g_free (settings); + + //gst_event_ref(event); + GST_LOG_OBJECT (sinkPad, "Sending upstream event %s.", GST_EVENT_TYPE_NAME (event)); + if (!gst_pad_push_event (sinkPad, event)) { + GST_WARNING_OBJECT (sinkPad, "Sending upstream event %p (%s) failed.", + event, GST_EVENT_TYPE_NAME (event)); + } +// GST_LOG_OBJECT (srcPad, "Sending downstream event %s.", GST_EVENT_TYPE_NAME (event)); +// if (!gst_pad_push_event (srcPad, event)) { +// GST_WARNING_OBJECT (srcPad, "Sending downstream event %p (%s) failed.", +// event, GST_EVENT_TYPE_NAME (event)); +// } + } else { + calib->mode = DETECTION; + } + } + + if (calib->mode == CAPTURING && blinkOutput) { + bitwise_not(view, view); + } + + } + + // Output Text + // FIXME all additional rendering (text, corners, ...) should be done with cairo or another gst framework. + // this will relax the conditions on the input format (RBG only at the moment). + // the calibration itself accepts more formats... + + std::string msg = (calib->mode == CAPTURING) ? "100/100" : + (calib->mode == CALIBRATED) ? "Calibrated" : "Press 'g' to start"; + int baseLine = 0; + cv::Size textSize = cv::getTextSize(msg, 1, 1, 1, &baseLine); + cv::Point textOrigin(view.cols - 2 * textSize.width - 10, view.rows - 2 * baseLine - 10); + + if (calib->mode == CAPTURING) { + msg = cv::format( "%d/%d", (int)calib->imagePoints.size(), calib->nrFrames ); + } + + const cv::Scalar RED(0,0,255); + const cv::Scalar GREEN(0,255,0); + + cv::putText(view, msg, textOrigin, 1, 1, calib->mode == CALIBRATED ? GREEN : RED); +} + +void doCalibration(__attribute__((unused)) GstElement *element, __attribute__((unused)) gpointer user_data) +{ + // GstCameraCalibration *calib = GST_CAMERA_CALIBRATION (element); +} + +static double computeReprojectionErrors( const std::vector >& objectPoints, + const std::vector >& imagePoints, + const std::vector& rvecs, const std::vector& tvecs, + const cv::Mat& cameraMatrix , const cv::Mat& distCoeffs, + std::vector& perViewErrors, bool fisheye) +{ + std::vector imagePoints2; + size_t totalPoints = 0; + double totalErr = 0, err; + perViewErrors.resize(objectPoints.size()); + + for(size_t i = 0; i < objectPoints.size(); ++i) + { + if (fisheye) + { + cv::fisheye::projectPoints(objectPoints[i], imagePoints2, rvecs[i], tvecs[i], cameraMatrix, + distCoeffs); + } + else + { + cv::projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2); + } + err = cv::norm(imagePoints[i], imagePoints2, cv::NORM_L2); + + size_t n = objectPoints[i].size(); + perViewErrors[i] = (float) std::sqrt(err*err/n); + totalErr += err*err; + totalPoints += n; + } + + return std::sqrt(totalErr/totalPoints); +} + +static void calcBoardCornerPositions(cv::Size boardSize, float squareSize, std::vector& corners, + gint patternType /*= CHESSBOARD*/) +{ + corners.clear(); + + switch(patternType) + { + case GST_CAMERACALIBRATION_PATTERN_CHESSBOARD: + case GST_CAMERACALIBRATION_PATTERN_CIRCLES_GRID: + for( int i = 0; i < boardSize.height; ++i) + for( int j = 0; j < boardSize.width; ++j) + corners.push_back(cv::Point3f(j * squareSize, i * squareSize, 0)); + break; + + case GST_CAMERACALIBRATION_PATTERN_ASYMMETRIC_CIRCLES_GRID: + for( int i = 0; i < boardSize.height; i++) + for( int j = 0; j < boardSize.width; j++) + corners.push_back(cv::Point3f((2 * j + i % 2) * squareSize, i * squareSize, 0)); + break; + default: + break; + } +} + +static bool runCalibration(GstCameraCalibration *calib, cv::Size& imageSize, cv::Mat& cameraMatrix, cv::Mat& distCoeffs, + std::vector > imagePoints, std::vector& rvecs, std::vector& tvecs, + std::vector& reprojErrs, double& totalAvgErr) +{ + //! [fixed_aspect] + cameraMatrix = cv::Mat::eye(3, 3, CV_64F); + if (calib->flags & cv::CALIB_FIX_ASPECT_RATIO) { + cameraMatrix.at(0,0) = calib->aspectRatio; + } + //! [fixed_aspect] + if (calib->useFisheye) { + distCoeffs = cv::Mat::zeros(4, 1, CV_64F); + } else { + distCoeffs = cv::Mat::zeros(8, 1, CV_64F); + } + + std::vector > objectPoints(1); + calcBoardCornerPositions(calib->boardSize, calib->squareSize, objectPoints[0], calib->calibrationPattern); + + objectPoints.resize(imagePoints.size(), objectPoints[0]); + + // Find intrinsic and extrinsic camera parameters + double rms; + + if (calib->useFisheye) { + cv::Mat _rvecs, _tvecs; + rms = cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, _rvecs, + _tvecs, calib->flags); + + rvecs.reserve(_rvecs.rows); + tvecs.reserve(_tvecs.rows); + for(int i = 0; i < int(objectPoints.size()); i++){ + rvecs.push_back(_rvecs.row(i)); + tvecs.push_back(_tvecs.row(i)); + } + } else { + rms = cv::calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, + calib->flags); + } + + GST_LOG_OBJECT (calib, + "Re-projection error reported by calibrateCamera: %f", rms); + qDebug() << "Re-projection error reported by calibrateCamera:" << rms; + + + bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs); + + totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, + distCoeffs, reprojErrs, calib->useFisheye); + + return ok; +} + +// Print camera parameters to the output file +//static void saveCameraParams( Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs, +// const vector& rvecs, const vector& tvecs, +// const vector& reprojErrs, const vector >& imagePoints, +// double totalAvgErr) +//{ +// FileStorage fs( s.outputFileName, FileStorage::WRITE); +// +// time_t tm; +// time( &tm); +// struct tm *t2 = localtime( &tm); +// char buf[1024]; +// strftime( buf, sizeof(buf), "%c", t2); +// +// fs << "calibration_time" << buf; +// +// if (!rvecs.empty() || !reprojErrs.empty()) +// fs << "nr_of_frames" << (int)std::max(rvecs.size(), reprojErrs.size()); +// fs << "image_width" << imageSize.width; +// fs << "image_height" << imageSize.height; +// fs << "board_width" << s.boardSize.width; +// fs << "board_height" << s.boardSize.height; +// fs << "square_size" << s.squareSize; +// +// if (s.flag & CALIB_FIX_ASPECT_RATIO) +// fs << "fix_aspect_ratio" << s.aspectRatio; +// +// if (s.flag) +// { +// if (s.useFisheye) +// { +// sprintf(buf, "flags:%s%s%s%s%s%s", +// s.flag & fisheye::CALIB_FIX_SKEW ? " +fix_skew" : "", +// s.flag & fisheye::CALIB_FIX_K1 ? " +fix_k1" : "", +// s.flag & fisheye::CALIB_FIX_K2 ? " +fix_k2" : "", +// s.flag & fisheye::CALIB_FIX_K3 ? " +fix_k3" : "", +// s.flag & fisheye::CALIB_FIX_K4 ? " +fix_k4" : "", +// s.flag & fisheye::CALIB_RECOMPUTE_EXTRINSIC ? " +recompute_extrinsic" : ""); +// } +// else +// { +// sprintf(buf, "flags:%s%s%s%s", +// s.flag & CALIB_USE_INTRINSIC_GUESS ? " +use_intrinsic_guess" : "", +// s.flag & CALIB_FIX_ASPECT_RATIO ? " +fix_aspectRatio" : "", +// s.flag & CALIB_FIX_PRINCIPAL_POINT ? " +fix_principal_point" : "", +// s.flag & CALIB_ZERO_TANGENT_DIST ? " +zero_tangent_dist" : ""); +// } +// cvWriteComment(*fs, buf, 0); +// } +// +// fs << "flags" << s.flag; +// +// fs << "fisheye_model" << s.useFisheye; +// +// fs << "camera_matrix" << cameraMatrix; +// fs << "distortion_coefficients" << distCoeffs; +// +// fs << "avg_reprojection_error" << totalAvgErr; +// if (s.writeExtrinsics && !reprojErrs.empty()) +// fs << "per_view_reprojection_errors" << Mat(reprojErrs); +// +// if(s.writeExtrinsics && !rvecs.empty() && !tvecs.empty()) +// { +// CV_Assert(rvecs[0].type() == tvecs[0].type()); +// Mat bigmat((int)rvecs.size(), 6, rvecs[0].type()); +// for( size_t i = 0; i < rvecs.size(); i++) +// { +// Mat r = bigmat(Range(int(i), int(i+1)), Range(0,3)); +// Mat t = bigmat(Range(int(i), int(i+1)), Range(3,6)); +// +// CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1); +// CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1); +// //*.t() is MatExpr (not Mat) so we can use assignment operator +// r = rvecs[i].t(); +// t = tvecs[i].t(); +// } +// //cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0); +// fs << "extrinsic_parameters" << bigmat; +// } +// +// if(s.writePoints && !imagePoints.empty()) +// { +// Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2); +// for( size_t i = 0; i < imagePoints.size(); i++) +// { +// Mat r = imagePtMat.row(int(i)).reshape(2, imagePtMat.cols); +// Mat imgpti(imagePoints[i]); +// imgpti.copyTo(r); +// } +// fs << "image_points" << imagePtMat; +// } +//} + +//! [run_and_save] +//bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, +// vector > imagePoints) +//{ +// vector rvecs, tvecs; +// vector reprojErrs; +// double totalAvgErr = 0; +// +// bool ok = runCalibration(s, imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, reprojErrs, +// totalAvgErr); +// cout << (ok ? "Calibration succeeded" : "Calibration failed") +// << ". avg re projection error = " << totalAvgErr << endl; +// +//// if (ok) +//// saveCameraParams(s, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, imagePoints, +//// totalAvgErr); +// return ok; +//} +//! [run_and_save] + +bool runCalibration(GstCameraCalibration *calib, cv::Size imageSize, cv::Mat& cameraMatrix, cv::Mat& distCoeffs, + std::vector > imagePoints) +{ + std::vector rvecs, tvecs; + std::vector reprojErrs; + double totalAvgErr = 0; + + bool ok = runCalibration(calib, imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, reprojErrs, + totalAvgErr); + GST_LOG_OBJECT (calib, + (ok ? "Calibration succeeded" : "Calibration failed"));// + ". avg re projection error = " + totalAvgErr); + + return ok; +} diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h new file mode 100644 index 0000000000..0da881216b --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h @@ -0,0 +1,114 @@ +/* + * GStreamer + * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2008 Michael Sheldon + * Copyright (C) 2011 Stefan Sauer + * Copyright (C) 2011 Robert Jobbagy + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_CAMERA_CALIBRATION_H__ +#define __GST_CAMERA_CALIBRATION_H__ + +#include + +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_CAMERA_CALIBRATION \ + (gst_camera_calibration_get_type()) +#define GST_CAMERA_CALIBRATION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAMERA_CALIBRATION,GstCameraCalibration)) +#define GST_CAMERA_CALIBRATION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAMERA_CALIBRATION,GstCameraCalibrationClass)) +#define GST_IS_CAMERA_CALIBRATION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAMERA_CALIBRATION)) +#define GST_IS_CAMERA_CALIBRATION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAMERA_CALIBRATION)) +typedef struct _GstCameraCalibration GstCameraCalibration; +typedef struct _GstCameraCalibrationClass GstCameraCalibrationClass; + +enum _GstCameraCalibrationPattern { + GST_CAMERACALIBRATION_PATTERN_CHESSBOARD, + GST_CAMERACALIBRATION_PATTERN_CIRCLES_GRID, + GST_CAMERACALIBRATION_PATTERN_ASYMMETRIC_CIRCLES_GRID +}; + +struct _GstCameraCalibration +{ + GstOpencvVideoFilter cvfilter; + + // settings + gint calibrationPattern; // One of the chessboard, circles, or asymmetric circle pattern + cv::Size boardSize; // The size of the board -> Number of items by width and height + float squareSize; // The size of a square in your defined unit (point, millimeter,etc). + float aspectRatio; // The aspect ratio + bool cornerSubPix; // + bool calibZeroTangentDist; // Assume zero tangential distortion + bool calibFixPrincipalPoint; // Fix the principal point at the center + bool useFisheye; // use fisheye camera model for calibration + int nrFrames; // The number of frames to use from the input for calibration + int delay; // In case of a video input + bool showUndistorsed; // Show undistorted images after calibration + bool showCorners; // Show corners + + // state + int flags; + int mode; + clock_t prevTimestamp; + std::vector > imagePoints; + cv::Mat cameraMatrix, distCoeffs; +}; + +struct _GstCameraCalibrationClass +{ + GstOpencvVideoFilterClass parent_class; +}; + +GType gst_camera_calibration_get_type (void); + +gboolean gst_camera_calibration_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_CAMERA_CALIBRATION_H__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp new file mode 100644 index 0000000000..67db605b80 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp @@ -0,0 +1,515 @@ +/* + * GStreamer + * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2008 Michael Sheldon + * Copyright (C) 2011 Stefan Sauer + * Copyright (C) 2014 Robert Jobbagy + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-cameraundistort + * + * Performs face detection on videos and images. + * If you have high cpu load you need to use videoscale with capsfilter and reduce the video resolution. + * + * The image is scaled down multiple times using the GstCameraCalibration::scale-factor + * until the size is <= GstCameraCalibration::min-size-width or + * GstCameraCalibration::min-size-height. + * + * + * Example launch line + * |[ + * gst-launch-1.0 autovideosrc ! decodebin ! colorspace ! cameraundistort ! videoconvert ! xvimagesink + * ]| Detect and show faces + * |[ + * gst-launch-1.0 autovideosrc ! video/x-raw,width=320,height=240 ! videoconvert ! cameraundistort min-size-width=60 min-size-height=60 ! colorspace ! xvimagesink + * ]| Detect large faces on a smaller image + * + * + */ + +/* FIXME: development version of OpenCV has CV_HAAR_FIND_BIGGEST_OBJECT which + * we might want to use if available + * see https://code.ros.org/svn/opencv/trunk/opencv/modules/objdetect/src/haar.cpp + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "camerautils.hpp" +#include "cameraevent.hpp" + +#include +#include + +#include "gstcameraundistort.h" + +#if (CV_MAJOR_VERSION >= 3) +#include +#endif +#include + +#include + +GST_DEBUG_CATEGORY_STATIC (gst_camera_undistort_debug); +#define GST_CAT_DEFAULT gst_camera_undistort_debug + +#define DEFAULT_SHOW_UNDISTORTED true +#define DEFAULT_ALPHA 1.0 +#define DEFAULT_CROP true + +enum +{ + PROP_0, + PROP_SHOW_UNDISTORTED, + PROP_ALPHA, + PROP_CROP, + PROP_SETTINGS +}; + +/*#define GST_CAMERA_UNDISTORT_GET_LOCK(playsink) (&((GstCameraUndistort *)undist)->lock) +#define GST_CAMERA_UNDISTORT_LOCK(undist) G_STMT_START { \ + GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \ + g_rec_mutex_lock (GST_CAMERA_UNDISTORT_GET_LOCK (undist)); \ + GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \ +} G_STMT_END +#define GST_CAMERA_UNDISTORT_UNLOCK(undist) G_STMT_START { \ + GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \ + g_rec_mutex_unlock (GST_CAMERA_UNDISTORT_GET_LOCK (undist)); \ +} G_STMT_END*/ + +G_DEFINE_TYPE (GstCameraUndistort, gst_camera_undistort, GST_TYPE_OPENCV_VIDEO_FILTER); + +static void gst_camera_undistort_dispose (GObject * object); +static void gst_camera_undistort_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_camera_undistort_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_camera_undistort_set_info (GstOpencvVideoFilter * cvfilter, + gint in_width, gint in_height, gint in_depth, gint in_channels, + gint out_width, gint out_height, gint out_depth, gint out_channels); +static GstFlowReturn gst_camera_undistort_transform_frame ( + GstOpencvVideoFilter * cvfilter, + GstBuffer * frame, IplImage * img, + GstBuffer * outframe, IplImage * outimg); + +static gboolean gst_camera_undistort_sink_event (GstBaseTransform *trans, GstEvent *event); +static gboolean gst_camera_undistort_src_event (GstBaseTransform *trans, GstEvent *event); + +static void camera_undistort_run(GstCameraUndistort *undist, IplImage *img, IplImage *outimg); +static gboolean camera_undistort_init_undistort_rectify_map(GstCameraUndistort *undist); + +/* initialize the cameraundistort's class */ +static void +gst_camera_undistort_class_init (GstCameraUndistortClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass); + GstOpencvVideoFilterClass *opencvfilter_class = GST_OPENCV_VIDEO_FILTER_CLASS (klass); + + GstCaps *caps; + GstPadTemplate *templ; + + gobject_class->dispose = gst_camera_undistort_dispose; + gobject_class->set_property = gst_camera_undistort_set_property; + gobject_class->get_property = gst_camera_undistort_get_property; + + trans_class->sink_event = + GST_DEBUG_FUNCPTR (gst_camera_undistort_sink_event); + trans_class->src_event = + GST_DEBUG_FUNCPTR (gst_camera_undistort_src_event); + + opencvfilter_class->cv_set_caps = gst_camera_undistort_set_info; + opencvfilter_class->cv_trans_func = + gst_camera_undistort_transform_frame; + + g_object_class_install_property (gobject_class, PROP_SHOW_UNDISTORTED, + g_param_spec_boolean ("show-undistorted", "Show Undistorted", + "Show undistorted images", + DEFAULT_SHOW_UNDISTORTED, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_ALPHA, + g_param_spec_float ("alpha", "Pixels", + "Pixels bla bla...", + 0.0, 1.0, DEFAULT_ALPHA, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_SETTINGS, + g_param_spec_string ("settings", "Settings", + "Undistort settings (OpenCV serialized opaque string)", + NULL, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + gst_element_class_set_static_metadata (element_class, + "cameraundistort", + "Filter/Effect/Video", + "Performs camera undistort", + "Philippe Renon "); + + /* add sink and source pad templates */ + caps = gst_opencv_caps_from_cv_image_type (CV_16UC1); + gst_caps_append (caps, gst_opencv_caps_from_cv_image_type (CV_8UC4)); + gst_caps_append (caps, gst_opencv_caps_from_cv_image_type (CV_8UC3)); + gst_caps_append (caps, gst_opencv_caps_from_cv_image_type (CV_8UC1)); + templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_caps_ref (caps)); + gst_element_class_add_pad_template (element_class, templ); + templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps); + gst_element_class_add_pad_template (element_class, templ); +} + +/* initialize the new element + * initialize instance structure + */ +static void +gst_camera_undistort_init (GstCameraUndistort * undist) +{ + undist->showUndistorted = DEFAULT_SHOW_UNDISTORTED; + undist->alpha = DEFAULT_ALPHA; + undist->crop = DEFAULT_CROP; + + undist->doUndistort = false; + undist->settingsChanged = false; + + undist->cameraMatrix = 0; + undist->distCoeffs = 0; + undist->map1 = 0; + undist->map2 = 0; + //undist->validPixROI = 0; + + undist->settings = NULL; +} + +static void +gst_camera_undistort_dispose (GObject * object) +{ + GstCameraUndistort *undist = GST_CAMERA_UNDISTORT (object); + + g_free (undist->settings); + undist->settings = NULL; + + G_OBJECT_CLASS (gst_camera_undistort_parent_class)->dispose (object); +} + + +static void +gst_camera_undistort_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstCameraUndistort *undist = GST_CAMERA_UNDISTORT (object); + const char *str; + + switch (prop_id) { + case PROP_SHOW_UNDISTORTED: + undist->showUndistorted = g_value_get_boolean (value); + undist->settingsChanged = true; + break; + case PROP_ALPHA: + undist->alpha = g_value_get_float (value); + undist->settingsChanged = true; + break; + case PROP_CROP: + undist->crop = g_value_get_boolean (value); + break; + case PROP_SETTINGS: + if (undist->settings) { + g_free (undist->settings); + undist->settings = NULL; + } + str = g_value_get_string (value); + if (str) + undist->settings = g_strdup (str); + undist->settingsChanged = true; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_camera_undistort_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstCameraUndistort *undist = GST_CAMERA_UNDISTORT (object); + + switch (prop_id) { + case PROP_SHOW_UNDISTORTED: + g_value_set_boolean (value, undist->showUndistorted); + break; + case PROP_ALPHA: + g_value_set_float (value, undist->alpha); + break; + case PROP_CROP: + g_value_set_boolean (value, undist->crop); + break; + case PROP_SETTINGS: + g_value_set_string (value, undist->settings); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_camera_undistort_set_info (GstOpencvVideoFilter * cvfilter, + gint in_width, gint in_height, + __attribute__((unused)) gint in_depth, __attribute__((unused)) gint in_channels, + __attribute__((unused)) gint out_width, __attribute__((unused)) gint out_height, + __attribute__((unused)) gint out_depth, __attribute__((unused)) gint out_channels) +{ + GstCameraUndistort *undist = GST_CAMERA_UNDISTORT (cvfilter); + + undist->imageSize = cv::Size(in_width, in_height); + + return TRUE; +} + +//static GstMessage * +//gst_camera_undistort_message_new (GstCameraUndistort * undist, GstBuffer * buf) +//{ +// GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (undist); +// GstStructure *s; +// GstClockTime running_time, stream_time; +// +// running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, +// GST_BUFFER_TIMESTAMP (buf)); +// stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, +// GST_BUFFER_TIMESTAMP (buf)); +// +// s = gst_structure_new ("cameracalibration", +// "timestamp", G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (buf), +// "stream-time", G_TYPE_UINT64, stream_time, +// "running-time", G_TYPE_UINT64, running_time, +// "duration", G_TYPE_UINT64, GST_BUFFER_DURATION (buf), NULL); +// +// return gst_message_new_element (GST_OBJECT (undist), s); +//} + +/* + * Performs the camera calibration + */ +static GstFlowReturn +gst_camera_undistort_transform_frame (GstOpencvVideoFilter * cvfilter, + G_GNUC_UNUSED GstBuffer * frame, IplImage * img, + G_GNUC_UNUSED GstBuffer * outframe, IplImage * outimg) +{ + GstCameraUndistort *undist = GST_CAMERA_UNDISTORT (cvfilter); + + camera_undistort_run(undist, img, outimg); + + return GST_FLOW_OK; +} + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +gboolean +gst_camera_undistort_plugin_init (GstPlugin * plugin) +{ + /* debug category for filtering log messages */ + GST_DEBUG_CATEGORY_INIT (gst_camera_undistort_debug, "cameraundistort", + 0, + "Performs camera undistortion"); + + return gst_element_register (plugin, "cameraundistort", GST_RANK_NONE, + GST_TYPE_CAMERA_UNDISTORT); +} + +static void +camera_undistort_run(GstCameraUndistort *undist, IplImage *img, IplImage *outimg) +{ + const cv::Mat view = cv::cvarrToMat(img); + cv::Mat outview = cv::cvarrToMat(outimg); + + if (undist->settingsChanged) { + undist->doUndistort = false; + if (undist->showUndistorted && undist->settings) { + //qDebug() << undist->settings; + if (camera_deserialize_undistort_settings( + undist->settings, undist->cameraMatrix, undist->distCoeffs)) { + undist->doUndistort = camera_undistort_init_undistort_rectify_map(undist); + } + } + undist->settingsChanged = false; + } + + if (undist->showUndistorted && undist->doUndistort) { + + QElapsedTimer timer; + timer.start(); + + cv::remap(view, outview, undist->map1, undist->map2, cv::INTER_LINEAR); + + qDebug() << "remap took" << timer.elapsed() << "ms"; + + if (undist->crop) { + const cv::Scalar CROP_COLOR(0, 255, 0); + cv::rectangle(outview, undist->validPixROI, CROP_COLOR); + } + } + else { + // FIXME should use passthrough to avoid this copy... + view.copyTo(outview); + } +} + + +// { +// Mat view, rview, map1, map2; +// +// if (undist->useFisheye) +// { +// Mat newCamMat; +// fisheye::estimateNewCameraMatrixForUndistortRectify(cameraMatrix, distCoeffs, imageSize, +// Matx33d::eye(), newCamMat, 1); +// fisheye::initUndistortRectifyMap(cameraMatrix, distCoeffs, Matx33d::eye(), newCamMat, imageSize, +// CV_16SC2, map1, map2); +// } +// else +// { +// initUndistortRectifyMap( +// cameraMatrix, distCoeffs, Mat(), +// getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0), imageSize, +// CV_16SC2, map1, map2); +// } +// } + + +static gboolean +camera_undistort_init_undistort_rectify_map(GstCameraUndistort *undist) +{ + QElapsedTimer timer; + timer.start(); + + cv::Size newImageSize; + cv::Rect validPixROI; + cv::Mat newCameraMatrix = cv::getOptimalNewCameraMatrix( + undist->cameraMatrix, undist->distCoeffs, undist->imageSize, + undist->alpha, newImageSize, &validPixROI); + undist->validPixROI = validPixROI; + + cv::initUndistortRectifyMap(undist->cameraMatrix, undist->distCoeffs, cv::Mat(), + newCameraMatrix, undist->imageSize, CV_16SC2, undist->map1, undist->map2); + + qDebug() << "init rectify took" << timer.elapsed() << "ms"; + + return TRUE; +} + +/* +qDebug() << "imageSize" << imageSize.width << imageSize.height; +qDebug() << "newImageSize" << imageSize.width << imageSize.height; +qDebug() << "alpha" << undist->alpha; +qDebug() << "roi" << undist->validPixROI.x << undist->validPixROI.y << undist->validPixROI.width << undist->validPixROI.height; + +cv::FileStorage fs1(".xml", cv::FileStorage::WRITE + cv::FileStorage::MEMORY); +fs1 << "cameraMatrix" << undist->cameraMatrix; +const std::string buf1 = fs1.releaseAndGetString(); +qDebug() << "cameraMatrix" << QString::fromStdString(buf1); + +cv::FileStorage fs2(".xml", cv::FileStorage::WRITE + cv::FileStorage::MEMORY); +fs2 << "newCameraMatrix" << newCameraMatrix; +const std::string buf2 = fs2.releaseAndGetString(); +qDebug() << "newCameraMatrix" << QString::fromStdString(buf2); + +cv::FileStorage fs3(".xml", cv::FileStorage::WRITE + cv::FileStorage::MEMORY); +fs3 << "distCoeffs" << undist->distCoeffs; +const std::string buf3 = fs3.releaseAndGetString(); +qDebug() << "distCoeffs" << QString::fromStdString(buf3); +*/ + +static gboolean camera_undistort_calibration_event(GstCameraUndistort *undist, GstEvent *event) +{ + g_free (undist->settings); + + if (!gst_camera_event_parse_calibrated(event, &(undist->settings))) { + qDebug() << "Failed to parse"; + return FALSE; + } + + undist->settingsChanged = true; + + return TRUE; +} + +static gboolean +gst_camera_undistort_sink_event (GstBaseTransform *trans, GstEvent *event) +{ + GstCameraUndistort *undist = GST_CAMERA_UNDISTORT (trans); + const GstStructure *structure = gst_event_get_structure (event); + + if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_BOTH && structure) { + + if (strcmp (gst_structure_get_name (structure), GST_CAMERA_EVENT_CALIBRATED_NAME) == 0) { + qDebug() << "GOT CALIBRATION EVENT FROM UPSTREAM"; + return camera_undistort_calibration_event(undist, event); + } + } + + return GST_BASE_TRANSFORM_CLASS (gst_camera_undistort_parent_class)->sink_event (trans, event); + } + + +static gboolean +gst_camera_undistort_src_event (GstBaseTransform *trans, GstEvent *event) +{ + GstCameraUndistort *undist = GST_CAMERA_UNDISTORT (trans); + const GstStructure *structure = gst_event_get_structure (event); + + if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_BOTH && structure) { + + if (strcmp (gst_structure_get_name (structure), GST_CAMERA_EVENT_CALIBRATED_NAME) == 0) { + qDebug() << "GOT CALIBRATION EVENT FROM DOWNSTREAM"; + return camera_undistort_calibration_event(undist, event); + } + } + + return GST_BASE_TRANSFORM_CLASS (gst_camera_undistort_parent_class)->src_event (trans, event); +} + diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h new file mode 100644 index 0000000000..829dbe9f45 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h @@ -0,0 +1,109 @@ +/* + * GStreamer + * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2008 Michael Sheldon + * Copyright (C) 2011 Stefan Sauer + * Copyright (C) 2011 Robert Jobbagy + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_CAMERA_UNDISTORT_H__ +#define __GST_CAMERA_UNDISTORT_H__ + +#include + +#include +//#include "gstopencvvideofilter.h" + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_CAMERA_UNDISTORT \ + (gst_camera_undistort_get_type()) +#define GST_CAMERA_UNDISTORT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAMERA_UNDISTORT,GstCameraUndistort)) +#define GST_CAMERA_UNDISTORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAMERA_UNDISTORT,GstCameraUndistortClass)) +#define GST_IS_CAMERA_UNDISTORT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAMERA_UNDISTORT)) +#define GST_IS_CAMERA_UNDISTORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAMERA_UNDISTORT)) +typedef struct _GstCameraUndistort GstCameraUndistort; +typedef struct _GstCameraUndistortClass GstCameraUndistortClass; + +struct _GstCameraUndistort +{ + GstOpencvVideoFilter cvfilter; + + //GRecMutex stream_lock; + + // settings + bool showUndistorted; + float alpha; + bool crop; + + // obscure string containing opencv calibration settings + gchar *settings; + // opencv calibration settings + cv::Mat cameraMatrix, distCoeffs; + + // state + bool doUndistort; + bool settingsChanged; + + cv::Size imageSize; + cv::Mat map1, map2; + cv::Rect validPixROI; +}; + +struct _GstCameraUndistortClass +{ + GstOpencvVideoFilterClass parent_class; +}; + +GType gst_camera_undistort_get_type (void); + +gboolean gst_camera_undistort_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_CAMERA_UNDISTORT_H__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp new file mode 100644 index 0000000000..8f6c912ede --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp @@ -0,0 +1,178 @@ +/* GStreamer + * Copyright (C) <2010> Thiago Santos + * + * gstopencvutils.c: miscellaneous utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstopencvutils.h" +#include + +/* +The various opencv image containers or headers store the following information: +- number of channels (usually 1, 3 or 4) +- depth (8, 16, 32, 64...); all channels have the same depth. +The channel layout (BGR vs RGB) is not stored... + +This gives us the following list of supported image formats: + CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4 + CV_8SC1, CV_8SC2, CV_8SC3, CV_8SC4 + CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4 + CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4 + CV_32SC1, CV_32SC2, CV_32SC3, CV_32SC4 + CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4 + CV_64FC1, CV_64FC2, CV_64FC3, CV_64FC4 + +Where the first part of the format name is the depth followed by a digit +representing the number of channels. +Note that opencv supports more that 4 channels. + +The opencv algorithms don't all support all the image types. +For example findChessboardCorners() supports only 8 bits formats +(gray scale and color). + +And, typically, this algorithm will convert the image to gray scale before +proceeding. It will do so with something like this: + cvtColor(srcImg, destImg, CV_BGR2GRAY); + +The conversion will work on any BGR format (BGR, BGRA, BGRx). +The extra channel(s) will be ignored. +It will also produce a result for any RGB format. +The result will be "wrong" to the human eye and might affect some algorithms +(not findChessboardCorners() afaik...). +This is due to how RGB gets converted to gray where each color has a +different weight. + +Another example is the 2D rendering API. +It work with RGB but the colors will be wrong. + +Likewise other layouts like xBGR and ABGR formats will probably misbehave +with most algorithms. + +The bad thing is that it is not possible to change the "default" BGR format. +Safest is to not assume that RGB will work and always convert to BGR. + +That said, the current opencv gstreamer elements all accept BGR and RGB caps ! +Some have restrictions but if a format is supported then both BGR and RGB +layouts will be supported. +*/ + +gboolean +gst_opencv_parse_iplimage_params_from_caps (GstCaps * caps, gint * width, + gint * height, gint * ipldepth, gint * channels, GError ** err) +{ + GstVideoInfo info; + GstVideoFormat format; + int cv_type; + gchar *caps_str; + + if (!gst_video_info_from_caps (&info, caps)) { + 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; + } + + format = GST_VIDEO_INFO_FORMAT (&info); + if (!gst_opencv_cv_image_type_from_video_format (format, &cv_type, err)) { + return FALSE; + } + + *width = GST_VIDEO_INFO_WIDTH (&info); + *height = GST_VIDEO_INFO_HEIGHT (&info); + + *ipldepth = cvIplDepth (cv_type); + *channels = CV_MAT_CN (cv_type); + + return TRUE; +} + +gboolean +gst_opencv_cv_image_type_from_video_format (GstVideoFormat format, + int * cv_type, GError ** err) +{ + const gchar *format_str; + + switch (format) { + case GST_VIDEO_FORMAT_GRAY8: + *cv_type = CV_8UC1; + break; + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_BGR: + *cv_type = CV_8UC3; + break; + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_ABGR: + *cv_type = CV_8UC4; + break; + case GST_VIDEO_FORMAT_GRAY16_LE: + case GST_VIDEO_FORMAT_GRAY16_BE: + *cv_type = CV_16UC1; + break; + default: + format_str = gst_video_format_to_string (format); + g_set_error (err, GST_CORE_ERROR, GST_CORE_ERROR_NEGOTIATION, + "Unsupported video format %s", format_str); + return FALSE; + } + + return TRUE; +} + +GstCaps * +gst_opencv_caps_from_cv_image_type (int cv_type) +{ + GstCaps *c = gst_caps_new_empty (); + switch (cv_type) { + case CV_8UC1: + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("GRAY8"))); + break; + case CV_8UC3: + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("RGB"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("BGR"))); + break; + case CV_8UC4: + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("RGBx"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("xRGB"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("BGRx"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("xBGR"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("RGBA"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("ARGB"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("BGRA"))); + gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("ABGR"))); + break; + case CV_16UC1: + gst_caps_append (c, + gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("GRAY16_LE"))); + gst_caps_append (c, + gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("GRAY16_BE"))); + break; + } + return c; +} diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h new file mode 100644 index 0000000000..b0397695e1 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h @@ -0,0 +1,46 @@ +/* GStreamer + * Copyright (C) <2010> Thiago Santos + * + * gstopencvutils.h: miscellaneous utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_OPENCV_UTILS__ +#define __GST_OPENCV_UTILS__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +G_BEGIN_DECLS + +gboolean gst_opencv_parse_iplimage_params_from_caps + (GstCaps * caps, gint * width, gint * height, gint * depth, + gint * channels, GError ** err); + +gboolean +gst_opencv_cv_image_type_from_video_format (GstVideoFormat format, + int * cv_type, GError ** err); + +GstCaps * gst_opencv_caps_from_cv_image_type (int cv_type); + +G_END_DECLS + +#endif /* __GST_OPENCV_UTILS__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp new file mode 100644 index 0000000000..eb9ed751cc --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp @@ -0,0 +1,289 @@ +/* + * GStreamer + * Copyright (C) 2010 Thiago Santos + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* TODO opencv can do scaling for some cases */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstopencvvideofilter.h" +#include "gstopencvutils.h" + +#include + +GST_DEBUG_CATEGORY_STATIC (gst_opencv_video_filter_debug); +#define GST_CAT_DEFAULT gst_opencv_video_filter_debug + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +static GstElementClass *parent_class = NULL; + +static void gst_opencv_video_filter_class_init (GstOpencvVideoFilterClass * + klass); +static void gst_opencv_video_filter_init (GstOpencvVideoFilter * cv_filter, + GstOpencvVideoFilterClass * klass); + +static gboolean gst_opencv_video_filter_set_info (GstVideoFilter * vfilter, + GstCaps * incaps, GstVideoInfo * in_info, + GstCaps * outcaps, GstVideoInfo * out_info); +static GstFlowReturn gst_opencv_video_filter_transform_frame_ip ( + GstVideoFilter * vfilter, GstVideoFrame * frame); +static GstFlowReturn gst_opencv_video_filter_transform_frame ( + GstVideoFilter * vfilter, + GstVideoFrame * inframe, GstVideoFrame * outframe); + +static void gst_opencv_video_filter_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_opencv_video_filter_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +GType +gst_opencv_video_filter_get_type (void) +{ + static volatile gsize opencv_video_filter_type = 0; + + if (g_once_init_enter (&opencv_video_filter_type)) { + GType _type; + static const GTypeInfo opencv_video_filter_info = { + sizeof (GstOpencvVideoFilterClass), + NULL, + NULL, + (GClassInitFunc) gst_opencv_video_filter_class_init, + NULL, + NULL, + sizeof (GstOpencvVideoFilter), + 0, + (GInstanceInitFunc) gst_opencv_video_filter_init, + }; + + _type = g_type_register_static (GST_TYPE_VIDEO_FILTER, + "GstOpencvVideoFilter", &opencv_video_filter_info, + G_TYPE_FLAG_ABSTRACT); + g_once_init_leave (&opencv_video_filter_type, _type); + } + return opencv_video_filter_type; +} + +/* Clean up */ +static void +gst_opencv_video_filter_finalize (GObject * obj) +{ + GstOpencvVideoFilter *cv_filter = GST_OPENCV_VIDEO_FILTER (obj); + + if (cv_filter->cvImage) + cvReleaseImage (&cv_filter->cvImage); + if (cv_filter->out_cvImage) + cvReleaseImage (&cv_filter->out_cvImage); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_opencv_video_filter_class_init (GstOpencvVideoFilterClass * klass) +{ + GObjectClass *gobject_class; + GstVideoFilterClass *filter_class; + + gobject_class = (GObjectClass *) klass; + filter_class = (GstVideoFilterClass *) klass; + parent_class = (GstElementClass *) g_type_class_peek_parent (klass); + + GST_DEBUG_CATEGORY_INIT (gst_opencv_video_filter_debug, + "opencvvideofilter", 0, "opencvvideofilter element"); + + gobject_class->finalize = + GST_DEBUG_FUNCPTR (gst_opencv_video_filter_finalize); + gobject_class->set_property = gst_opencv_video_filter_set_property; + gobject_class->get_property = gst_opencv_video_filter_get_property; + + filter_class->transform_frame = gst_opencv_video_filter_transform_frame; + filter_class->transform_frame_ip = + gst_opencv_video_filter_transform_frame_ip; + filter_class->set_info = gst_opencv_video_filter_set_info; +} + +static void +gst_opencv_video_filter_init (GstOpencvVideoFilter * cv_filter, + GstOpencvVideoFilterClass * klass) +{ +} + +static GstFlowReturn +gst_opencv_video_filter_transform_frame (GstVideoFilter * vfilter, + GstVideoFrame * inframe, GstVideoFrame * outframe) +{ + GstOpencvVideoFilter *cv_filter; + GstOpencvVideoFilterClass *fclass; + GstFlowReturn ret; + + cv_filter = GST_OPENCV_VIDEO_FILTER (vfilter); + fclass = GST_OPENCV_VIDEO_FILTER_GET_CLASS (vfilter); + + g_return_val_if_fail (fclass->cv_transform_frame != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (cv_filter->cvImage != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (cv_filter->out_cvImage != NULL, GST_FLOW_ERROR); + + cv_filter->cvImage->imageData = (char *) inframe->data; + cv_filter->out_cvImage->imageData = (char *) outframe->data; + + ret = fclass->cv_transform_frame (cv_filter, inframe, cv_filter->cvImage, + outframe, cv_filter->out_cvImage); + + return ret; +} + +static GstFlowReturn +gst_opencv_video_filter_transform_frame_ip (GstVideoFilter * vfilter, + GstVideoFrame * frame) +{ + GstOpencvVideoFilter *cv_filter; + GstOpencvVideoFilterClass *fclass; + GstFlowReturn ret; + + cv_filter = GST_OPENCV_VIDEO_FILTER (vfilter); + fclass = GST_OPENCV_VIDEO_FILTER_GET_CLASS (vfilter); + + g_return_val_if_fail (fclass->cv_transform_frame_ip != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (cv_filter->cvImage != NULL, GST_FLOW_ERROR); + + cv_filter->cvImage->imageData = (char *) frame->data; + + ret = fclass->cv_transform_frame_ip (cv_filter, frame, cv_filter->cvImage); + + return ret; +} + +static gboolean +gst_opencv_video_filter_set_info (GstVideoFilter * vfilter, GstCaps * incaps, + GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info) +{ + GstOpencvVideoFilter *cv_filter = GST_OPENCV_VIDEO_FILTER (vfilter); + GstOpencvVideoFilterClass *klass = + GST_OPENCV_VIDEO_FILTER_GET_CLASS (cv_filter); + gint in_width, in_height; + gint in_depth, in_channels; + gint out_width, out_height; + gint out_depth, out_channels; + GError *in_err = NULL; + GError *out_err = NULL; + + if (!gst_opencv_parse_iplimage_params_from_caps (incaps, &in_width, + &in_height, &in_depth, &in_channels, &in_err)) { + GST_WARNING_OBJECT (cv_filter, "Failed to parse input caps: %s", + in_err->message); + g_error_free (in_err); + return FALSE; + } + + if (!gst_opencv_parse_iplimage_params_from_caps (outcaps, &out_width, + &out_height, &out_depth, &out_channels, &out_err)) { + GST_WARNING_OBJECT (cv_filter, "Failed to parse output caps: %s", + out_err->message); + g_error_free (out_err); + return FALSE; + } + + if (klass->cv_set_info) { + if (!klass->cv_set_info (cv_filter, in_width, in_height, in_depth, + in_channels, out_width, out_height, out_depth, out_channels)) + return FALSE; + } + + if (cv_filter->cvImage) { + cvReleaseImage (&cv_filter->cvImage); + } + if (cv_filter->out_cvImage) { + cvReleaseImage (&cv_filter->out_cvImage); + } + + cv_filter->cvImage = + cvCreateImageHeader (cvSize (in_width, in_height), in_depth, in_channels); + cv_filter->out_cvImage = + cvCreateImageHeader (cvSize (out_width, out_height), out_depth, + out_channels); + + gst_base_transform_set_in_place (GST_BASE_TRANSFORM (cv_filter), + cv_filter->in_place); + return TRUE; +} + +static void +gst_opencv_video_filter_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_opencv_video_filter_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +void +gst_opencv_video_filter_set_in_place (GstOpencvVideoFilter * cv_filter, + gboolean ip) +{ + cv_filter->in_place = ip; + gst_base_transform_set_in_place (GST_BASE_TRANSFORM (cv_filter), ip); +} diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h new file mode 100644 index 0000000000..29f637237c --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h @@ -0,0 +1,110 @@ +/* + * GStreamer + * Copyright (C) 2010 Thiago Santos + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_OPENCV_VIDEO_FILTER_H__ +#define __GST_OPENCV_VIDEO_FILTER_H__ + +#include +#include + +G_BEGIN_DECLS + +/* forward declare opencv type to avoid exposing them in this API */ +typedef struct _IplImage IplImage; + +/* #defines don't like whitespacey bits */ +#define GST_TYPE_OPENCV_VIDEO_FILTER \ + (gst_opencv_video_filter_get_type()) +#define GST_OPENCV_VIDEO_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPENCV_VIDEO_FILTER,GstOpencvVideoFilter)) +#define GST_OPENCV_VIDEO_FILTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPENCV_VIDEO_FILTER,GstOpencvVideoFilterClass)) +#define GST_IS_OPENCV_VIDEO_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPENCV_VIDEO_FILTER)) +#define GST_IS_OPENCV_VIDEO_FILTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPENCV_VIDEO_FILTER)) +#define GST_OPENCV_VIDEO_FILTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OPENCV_VIDEO_FILTER,GstOpencvVideoFilterClass)) +#define GST_OPENCV_VIDEO_FILTER_CAST(obj) ((GstOpencvVideoFilter *) (obj)) + +typedef struct _GstOpencvVideoFilter GstOpencvVideoFilter; +typedef struct _GstOpencvVideoFilterClass GstOpencvVideoFilterClass; + +typedef GstFlowReturn (*GstOpencvVideoFilterTransformIPFunc) + (GstOpencvVideoFilter * cvfilter, GstVideoFrame * frame, IplImage * img); +typedef GstFlowReturn (*GstOpencvVideoFilterTransformFunc) + (GstOpencvVideoFilter * cvfilter, GstVideoFrame * frame, IplImage * img, + GstVideoFrame * outframe, IplImage * outimg); + +typedef gboolean (*GstOpencvVideoFilterSetInfo) + (GstOpencvVideoFilter * cv_filter, gint in_width, gint in_height, + gint in_depth, gint in_channels, gint out_width, gint out_height, + gint out_depth, gint out_channels); + +struct _GstOpencvVideoFilter +{ + GstVideoFilter videofilter; + + gboolean in_place; + + IplImage *cvImage; + IplImage *out_cvImage; +}; + +struct _GstOpencvVideoFilterClass +{ + GstVideoFilterClass parent_class; + + GstOpencvVideoFilterTransformFunc cv_transform_frame; + GstOpencvVideoFilterTransformIPFunc cv_transform_frame_ip; + + GstOpencvVideoFilterSetInfo cv_set_info; +}; + +GType gst_opencv_video_filter_get_type (void); + +void gst_opencv_video_filter_set_in_place (GstOpencvVideoFilter * cv_filter, + gboolean ip); + +G_END_DECLS +#endif /* __GST_OPENCV_VIDEO_FILTER_H__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/plugins.pro b/ground/gcs/src/libs/gstreamer/plugins/plugins.pro new file mode 100644 index 0000000000..92a5511ee4 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/plugins/plugins.pro @@ -0,0 +1,33 @@ +DEFINES += GST_PLUGIN_BUILD_STATIC + +#CONFIG += link_pkgconfig +PKGCONFIG += gstreamer-base-1.0 + +do_not_compile { + HEADERS += \ + plugins/cameracalibration/gstopencvutils.h \ + plugins/cameracalibration/gstopencvvideofilter.hpp + + SOURCES += \ + plugins/cameracalibration/gstopencvutils.cpp \ + plugins/cameracalibration/gstopencvvideofilter.cpp \ +} + +opencv { + # there is no package for gst opencv yet... + GSTREAMER_SDK_DIR = $$system(pkg-config --variable=exec_prefix gstreamer-1.0) + LIBS += -L$(GSTREAMER_SDK_DIR)/lib/gstreamer-1.0/opencv + LIBS += -lgstopencv-1.0 + + HEADERS += \ + plugins/cameracalibration/camerautils.hpp \ + plugins/cameracalibration/cameraevent.hpp \ + plugins/cameracalibration/gstcameracalibration.h \ + plugins/cameracalibration/gstcameraundistort.h + + SOURCES += \ + plugins/cameracalibration/camerautils.cpp \ + plugins/cameracalibration/cameraevent.cpp \ + plugins/cameracalibration/gstcameracalibration.cpp \ + plugins/cameracalibration/gstcameraundistort.cpp +} diff --git a/ground/gcs/src/libs/gstreamer/readme.txt b/ground/gcs/src/libs/gstreamer/readme.txt new file mode 100644 index 0000000000..f351173dbd --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/readme.txt @@ -0,0 +1,67 @@ +############################################################################### +# General +############################################################################### + +Add the following line to your build config file: + GCS_EXTRA_CONF += gstreamer + or run this command + make config_append GCS_EXTRA_CONF+=gstreamer + +The build config file is at the root of your source directory. + +############################################################################### +# Windows (msys2) +############################################################################### + +i686: +$ pacman -S mingw-w64-i686-gst-plugins-base mingw-w64-i686-gst-plugins-good mingw-w64-i686-gst-plugins-bad mingw-w64-i686-gst-plugins-ugly mingw-w64-i686-gst-libav + +x86_64: +$ pacman -S mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad mingw-w64-x86_64-gst-plugins-ugly mingw-w64-x86_64-gst-libav + +############################################################################### +# Linux +############################################################################### + +Get all the gstreamer libraries. + +This might work: + +Add the repository ppa:gstreamer-developers/ppa using Synaptic Package Manager or CLI +> sudo add-apt-repository ppa:gstreamer-developers/ppa +> sudo apt-get update + +Upgrade to latest version of the packages using Synaptic Package Manager or CLI +> sudo apt-get install gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav +> sudo apt-get install gstreamer1.0-dev gstreamer-plugins-base1.0-dev + +############################################################################### +# Mac +############################################################################### + +############################################################################### +# How to find required libraries (for copydata.pro) +############################################################################### + +use gst-inspect with an element or plugin and look at Filename. + +$ gst-inspect-1.0.exe ksvideosrc +Factory Details: + Rank none (0) + Long-name KsVideoSrc + Klass Source/Video + Description Stream data from a video capture device through Windows kernel streaming + Author Ole André Vadla Ravnås +Haakon Sporsheim +Andres Colubri + +Plugin Details: + Name winks + Description Windows kernel streaming plugin + Filename C:\msys64\mingw64\lib\gstreamer-1.0\libgstwinks.dll + Version 1.6.3 + License LGPL + Source module gst-plugins-bad + Source release date 2016-01-20 + Binary package GStreamer + Origin URL http://gstreamer.net/ diff --git a/ground/gcs/src/libs/gstreamer/videowidget.cpp b/ground/gcs/src/libs/gstreamer/videowidget.cpp new file mode 100644 index 0000000000..7bba53db25 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/videowidget.cpp @@ -0,0 +1,851 @@ +/** + ****************************************************************************** + * + * @file videowidget.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 "videowidget.h" + +#include "gst_util.h" +#include "overlay.h" +#include "pipelineevent.h" +// #include "devicemonitor.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +// TODO find a better way and move away from this file +static Pipeline::State cvt(GstState state); +static const char *name(Pipeline::State state); +static ProgressEvent::ProgressType cvt(GstProgressType type); + +class GstOverlayImpl : public Overlay { +public: + GstOverlayImpl(GstVideoOverlay *gst_overlay) : + gst_overlay(gst_overlay) + {} + void expose() + { + if (gst_overlay) { + gst_video_overlay_expose(gst_overlay); + } + } +private: + GstVideoOverlay *gst_overlay; +}; + +class BusSyncHandler { +public: + BusSyncHandler(VideoWidget *widget, WId wid) : + widget(widget), wId(wid) + {} + bool handleMessage(GstMessage *msg); +private: + VideoWidget *widget; + WId wId; +}; + +static GstElement *createPipelineFromDesc(const char *, QString &lastError); + +static GstBusSyncReply gst_bus_sync_handler(GstBus *, GstMessage *, BusSyncHandler *); + +VideoWidget::VideoWidget(QWidget *parent) : + QWidget(parent), pipeline(NULL), overlay(NULL) +{ + qDebug() << "VideoWidget::VideoWidget"; + + // initialize gstreamer + gst::init(NULL, NULL); + + // foreach(Device d, m.devices()) { + // qDebug() << d.displayName(); + // } + + // make the widget native so it gets its own native window id that we will pass to gstreamer + setAttribute(Qt::WA_NativeWindow); + // setAttribute(Qt::WA_DontCreateNativeAncestors); + + // set black background + QPalette pal(palette()); + pal.setColor(backgroundRole(), Qt::black); + setPalette(pal); + + // calling winId() will realize the window if it is not yet realized + // so we need to call winId() here and not later from a gstreamer thread... + WId wid = winId(); + qDebug() << "VideoWidget::VideoWidget - video winId :" << (gulong)wid; + handler = new BusSyncHandler(this, wid); + + // init widget state (see setOverlay() for more information) + // setOverlay(NULL); + setAutoFillBackground(true); + setAttribute(Qt::WA_OpaquePaintEvent, false); + setAttribute(Qt::WA_PaintOnScreen, false); + + // init state + lastError = ""; +} + +VideoWidget::~VideoWidget() +{ + if (pipeline) { + dispose(); + } + if (handler) { + delete handler; + handler = NULL; + } +} + +bool VideoWidget::isPlaying() +{ + return pipeline && (GST_STATE(pipeline) == GST_STATE_PLAYING); +} + +QString VideoWidget::pipelineDesc() +{ + return m_pipelineDesc; +} + +void VideoWidget::setPipelineDesc(QString pipelineDesc) +{ + qDebug() << "VideoWidget::setPipelineDesc -" << pipelineDesc; + stop(); + this->m_pipelineDesc = pipelineDesc; +} + +void VideoWidget::start() +{ + qDebug() << "VideoWidget::start -" << m_pipelineDesc; + init(); + update(); + if (pipeline) { + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); + } +} + +void VideoWidget::pause() +{ + qDebug() << "VideoWidget::pause -" << m_pipelineDesc; + init(); + update(); + if (pipeline) { + if (GST_STATE(pipeline) == GST_STATE_PAUSED) { + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); + } else if (GST_STATE(pipeline) == GST_STATE_PLAYING) { + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PAUSED); + } + } +} + +void VideoWidget::stop() +{ + qDebug() << "VideoWidget::stop -" << m_pipelineDesc; + if (pipeline) { + dispose(); + } else { + // emit fake state change event. this is needed by the UI... + emit stateChanged(Pipeline::Null, Pipeline::Null, Pipeline::VoidPending); + } + update(); +} + +void VideoWidget::init() +{ + if (pipeline) { + // if pipeline is already created, reset some state and return + qDebug() << "VideoWidget::init - reseting pipeline state :" << m_pipelineDesc; + lastError = ""; + return; + } + + // reset state + lastError = ""; + + // create pipeline + qDebug() << "VideoWidget::init - initializing pipeline :" << m_pipelineDesc; + pipeline = createPipelineFromDesc(m_pipelineDesc.toStdString().c_str(), lastError); + if (pipeline) { + gst_pipeline_set_auto_flush_bus(GST_PIPELINE(pipeline), true); + + // register bus synchronous handler + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); + gst_bus_set_sync_handler(bus, (GstBusSyncHandler)gst_bus_sync_handler, handler, NULL); + gst_object_unref(bus); + } else { + // emit fake state change event. this is needed by the UI... + emit stateChanged(Pipeline::Null, Pipeline::Null, Pipeline::VoidPending); + } +} + +void VideoWidget::dispose() +{ + qDebug() << "VideoWidget::dispose -" << m_pipelineDesc; + + setOverlay(NULL); + + if (pipeline) { + gst_element_set_state(pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + pipeline = NULL; + } +} + +void VideoWidget::paintEvent(QPaintEvent *event) +{ + if (overlay) { + overlay->expose(); + } else { + QWidget::paintEvent(event); + paintStatus(event); + } +} + +void VideoWidget::paintStatus(QPaintEvent *event) +{ + Q_UNUSED(event); + + QTextDocument doc; + doc.setDefaultStyleSheet("* { color:red; }"); + + QString html = "

" + getStatusMessage() + "

"; + + QRect widgetRect = QWidget::rect(); + int x = 0; + int w = widgetRect.width(); + int hh = widgetRect.height() / 4; + int y = (widgetRect.height() - hh) / 2; + int h = widgetRect.height() - y; + QRect rect = QRect(x, y, w, h); + + doc.setHtml(html); + doc.setTextWidth(rect.width()); + + QPainter painter(this); + painter.save(); + painter.translate(rect.topLeft()); + doc.drawContents(&painter, rect.translated(-rect.topLeft())); + painter.restore(); + // painter.drawRect(rect); + + // QBrush brush( Qt::yellow ); + // painter.setBrush( brush ); // set the yellow brush + // painter.setPen( Qt::NoPen ); // do not draw outline + // painter.drawRect(0, 0, width(), height()); // draw filled rectangle + // painter.end(); + + // QFont font = QApplication::font(); + // font.setPixelSize( rect.height() ); + // painter.setFont( font ); +} + +QString VideoWidget::getStatus() +{ + if (!lastError.isEmpty()) { + return "ERROR"; + } else if (!pipeline && m_pipelineDesc.isEmpty()) { + return "NO PIPELINE"; + } + return ""; +} + +QString VideoWidget::getStatusMessage() +{ + if (!lastError.isEmpty()) { + return lastError; + } else if (!pipeline && m_pipelineDesc.isEmpty()) { + return "No pipeline"; + } + return ""; +} + +void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_UNUSED(event); +} + +void VideoWidget::resizeEvent(QResizeEvent *event) +{ + if (overlay) { + overlay->expose(); + } else { + QWidget::resizeEvent(event); + } +} + +QPaintEngine *VideoWidget::paintEngine() const +{ + // bypass double buffering, see setOverlay() for explanation + return overlay ? NULL : QWidget::paintEngine(); +} + +static Pipeline::State cvt(GstState state) +{ + switch (state) { + case GST_STATE_VOID_PENDING: + return Pipeline::VoidPending; + + case GST_STATE_NULL: + return Pipeline::Null; + + case GST_STATE_READY: + return Pipeline::Ready; + + case GST_STATE_PAUSED: + return Pipeline::Paused; + + case GST_STATE_PLAYING: + return Pipeline::Playing; + } + return Pipeline::Null; +} + +static const char *name(Pipeline::State state) +{ + switch (state) { + case Pipeline::VoidPending: + return "VoidPending"; + + case Pipeline::Null: + return "Null"; + + case Pipeline::Ready: + return "Ready"; + + case Pipeline::Paused: + return "Paused"; + + case Pipeline::Playing: + return "Playing"; + } + return ""; +} + +// static StreamStatusEvent::StreamStatusType cvt(GstStreamStatusType type) +// { +// switch (type) { +// case GST_STREAM_STATUS_TYPE_CREATE: +// return StreamStatusEvent::Create; +// +// case GST_STREAM_STATUS_TYPE_ENTER: +// return StreamStatusEvent::Enter; +// +// case GST_STREAM_STATUS_TYPE_LEAVE: +// return StreamStatusEvent::Leave; +// +// case GST_STREAM_STATUS_TYPE_DESTROY: +// return StreamStatusEvent::Destroy; +// +// case GST_STREAM_STATUS_TYPE_START: +// return StreamStatusEvent::Start; +// +// case GST_STREAM_STATUS_TYPE_PAUSE: +// return StreamStatusEvent::Pause; +// +// case GST_STREAM_STATUS_TYPE_STOP: +// return StreamStatusEvent::Stop; +// } +// return StreamStatusEvent::Null; +// } + +static ProgressEvent::ProgressType cvt(GstProgressType type) +{ + switch (type) { + case GST_PROGRESS_TYPE_START: + return ProgressEvent::Start; + + case GST_PROGRESS_TYPE_CONTINUE: + return ProgressEvent::Continue; + + case GST_PROGRESS_TYPE_COMPLETE: + return ProgressEvent::Complete; + + case GST_PROGRESS_TYPE_CANCELED: + return ProgressEvent::Cancelled; + + case GST_PROGRESS_TYPE_ERROR: + return ProgressEvent::Error; + } + return ProgressEvent::Error; +} + +bool VideoWidget::event(QEvent *event) +{ + if (event->type() == PipelineEvent::PrepareWindowId) { + PrepareWindowIdEvent *pe = static_cast(event); + // we take ownership of the overlay object + setOverlay(pe->getOverlay()); + QString msg = QString("PrepareWindowId: element %0 prepare window id").arg(pe->src); + emitEventMessage(msg); + return true; + } else if (event->type() == PipelineEvent::StateChange) { + StateChangedEvent *sce = static_cast(event); + QString msg = QString("StateChange: element %0 changed state from %1 to %2") + .arg(sce->src).arg(name(sce->getOldState())).arg(name(sce->getNewState())); + emitEventMessage(msg); + emit stateChanged(sce->getOldState(), sce->getNewState(), sce->getPendingState()); + + if (sce->getNewState() == Pipeline::Playing) { + if (pipeline) { + toDotFile("pipeline"); + } + } + + return true; + } else if (event->type() == PipelineEvent::StreamStatus) { + StreamStatusEvent *sse = static_cast(event); + QString msg = QString("StreamStatus: %0 %1 (%2)").arg(sse->src).arg(sse->getStatusName()).arg(sse->getOwner()); + emitEventMessage(msg); + return true; + } else if (event->type() == PipelineEvent::NewClock) { + NewClockEvent *nce = static_cast(event); + QString msg = QString("NewClock : element %0 has new clock %1").arg(nce->src).arg(nce->getName()); + emitEventMessage(msg); + return true; + } else if (event->type() == PipelineEvent::ClockProvide) { + ClockProvideEvent *cpe = static_cast(event); + QString msg = QString("ClockProvide: element %0 clock provide %1 ready=%2").arg(cpe->src).arg(cpe->getName()).arg(cpe->isReady()); + emitEventMessage(msg); + return true; + } else if (event->type() == PipelineEvent::ClockLost) { + ClockLostEvent *cle = static_cast(event); + QString msg = QString("ClockLost: element %0 lost clock %1").arg(cle->src).arg(cle->getName()); + emitEventMessage(msg); + + // PRINT ("Clock lost, selecting a new one\n"); + // gst_element_set_state (pipeline, GST_STATE_PAUSED); + // gst_element_set_state (pipeline, GST_STATE_PLAYING); + + return true; + } else if (event->type() == PipelineEvent::Progress) { + ProgressEvent *pe = static_cast(event); + QString msg = QString("Progress: element %0 sent progress event: %1 %2 (%3)").arg(pe->src).arg(pe->getProgressType()).arg( + pe->getCode()).arg(pe->getText()); + emitEventMessage(msg); + return true; + } else if (event->type() == PipelineEvent::Latency) { + LatencyEvent *le = static_cast(event); + QString msg = QString("Latency: element %0 sent latency event").arg(le->src); + emitEventMessage(msg); + + bool success = gst_bin_recalculate_latency(GST_BIN(pipeline)); + if (!success) { + qWarning() << "Failed to recalculate latency"; + } + + return true; + } else if (event->type() == PipelineEvent::Qos) { + QosEvent *qe = static_cast(event); + QString msg = QString("Qos: element %0 sent QOS event: %1 %2 %3").arg(qe->src).arg(qe->getData().timestamps()).arg( + qe->getData().values()).arg(qe->getData().stats()); + emitEventMessage(msg); + + if (pipeline) { + toDotFile("pipeline_qos"); + } + + return true; + } else if (event->type() == PipelineEvent::Eos) { + QString msg = QString("Eos: element %0 sent EOS event"); + emitEventMessage(msg); + + if (pipeline) { + toDotFile("pipeline_eos"); + } + + return true; + } else if (event->type() == PipelineEvent::Error) { + ErrorEvent *ee = static_cast(event); + QString msg = QString("Error: element %0 sent error event: %1 (%2)").arg(ee->src).arg(ee->getMessage()).arg( + ee->getDebug()); + emitEventMessage(msg); + if (lastError.isEmpty()) { + // remember first error only (usually the most useful) + lastError = QString("Pipeline error: %0").arg(ee->getMessage()); + // stop pipeline... + stop(); + } else { + // TODO record subsequent errors separately + } + return true; + } else if (event->type() == PipelineEvent::Warning) { + WarningEvent *we = static_cast(event); + QString msg = QString("Warning: element %0 sent warning event: %1 (%2)").arg(we->src).arg(we->getMessage()).arg( + we->getDebug()); + emitEventMessage(msg); + return true; + } else if (event->type() == PipelineEvent::Info) { + InfoEvent *ie = static_cast(event); + QString msg = QString("Info: element %0 sent info event: %1 (%2)").arg(ie->src).arg(ie->getMessage()).arg( + ie->getDebug()); + emitEventMessage(msg); + return true; + } + return QWidget::event(event); +} + +void VideoWidget::emitEventMessage(QString msg) +{ + // qDebug() << "VideoWidget::event -" << msg; + emit message(msg); +} + +void VideoWidget::setOverlay(Overlay *overlay) +{ + if (this->overlay != overlay) { + Overlay *oldOverlay = this->overlay; + this->overlay = overlay; + if (oldOverlay) { + delete oldOverlay; + } + } + + bool hasOverlay = overlay ? true : false; + + setAutoFillBackground(!hasOverlay); + + // disable background painting to avoid flickering when resizing + setAttribute(Qt::WA_OpaquePaintEvent, hasOverlay); + // setAttribute(Qt::WA_NoSystemBackground, hasOverlay); // not sure it is needed + + // disable double buffering to avoid flickering when resizing + // for this to work we also need to override paintEngine() and make it return NULL. + // see http://qt-project.org/faq/answer/how_does_qtwa_paintonscreen_relate_to_the_backing_store_widget_composition_ + // drawback is that this widget won't participate in composition... + setAttribute(Qt::WA_PaintOnScreen, hasOverlay); +} + + +void VideoWidget::toDotFile(QString name) +{ + if (!pipeline) { + return; + } + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_VERBOSE, name.toStdString().c_str()); +} + +static GstElement *createPipelineFromDesc(const char *desc, QString &lastError) +{ + qDebug() << "VideoWidget::createPipelineFromDesc - creating pipeline :" << desc; + GError *error = NULL; + GstElement *pipeline = gst_parse_launch_full(desc, NULL, GST_PARSE_FLAG_FATAL_ERRORS, &error); + if (!pipeline) { + if (error) { + // no pipeline and error... + // report error to user + QString msg = QString("Failed to create pipeline: %0").arg(error->message); + qCritical() << "VideoWidget::createPipelineFromDesc -" << msg; + lastError = msg; + } else { + // no pipeline and no error... + // report generic error + QString msg = QString("Failed to create pipeline (no error reported!)"); + qCritical() << "VideoWidget::createPipelineFromDesc -" << msg; + lastError = msg; + } + } else if (error) { + // pipeline and error... + // report error to user? + // warning? + QString msg = QString("Created pipeline with error: %0").arg(error->message); + qWarning() << "VideoWidget::createPipelineFromDesc -" << msg; + } else { + // qDebug() << gst_bin_get_by_name(GST_BIN(pipeline), "videotestsrc0"); + } + if (error) { + g_error_free(error); + } + return pipeline; +} + +bool BusSyncHandler::handleMessage(GstMessage *message) +{ + // this method is called by gstreamer as a callback + // and as such is not necessarily called on the QT event handling thread + + bool handled = false; + + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ELEMENT: + { + if (gst_is_video_overlay_prepare_window_handle_message(message)) { + qDebug().noquote() << QString("VideoWidget::handleMessage - element %0 prepare window with id #%1").arg(GST_OBJECT_NAME(message->src)).arg((gulong)wId); + // prepare-xwindow-id must be handled synchronously in order to have gstreamer use our window + GstVideoOverlay *gst_video_overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message)); + gst_video_overlay_set_window_handle(gst_video_overlay, (gulong)wId); + // and now post event asynchronously + Overlay *overlay = new GstOverlayImpl(gst_video_overlay); + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new PrepareWindowIdEvent(src, overlay)); + // notify that the message was handled + handled = true; + } + break; + } + case GST_MESSAGE_STATE_CHANGED: + { + if (GST_IS_PIPELINE(message->src)) { + GstState old_state, new_state, pending_state; + gst_message_parse_state_changed(message, &old_state, &new_state, &pending_state); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new StateChangedEvent(src, cvt(old_state), cvt(new_state), cvt(pending_state))); + } + break; + } + case GST_MESSAGE_STREAM_STATUS: + { + GstStreamStatusType type; + GstElement *owner; + gst_message_parse_stream_status(message, &type, &owner); + + // QString src(GST_OBJECT_NAME(message->src)); + // QString name(GST_OBJECT_NAME(owner)); + // QCoreApplication::postEvent(widget, new StreamStatusEvent(src, cvt(type), name)); + break; + } + case GST_MESSAGE_NEW_CLOCK: + { + if (GST_IS_PIPELINE(message->src)) { + GstClock *clock; + + gst_message_parse_new_clock(message, &clock); + + QString src(GST_OBJECT_NAME(message->src)); + QString name(GST_OBJECT_NAME(clock)); + QCoreApplication::postEvent(widget, new NewClockEvent(src, name)); + } + break; + } + case GST_MESSAGE_CLOCK_PROVIDE: + { + if (GST_IS_PIPELINE(message->src)) { + GstClock *clock; + gboolean ready; + + gst_message_parse_clock_provide(message, &clock, &ready); + + QString src(GST_OBJECT_NAME(message->src)); + QString name(GST_OBJECT_NAME(clock)); + QCoreApplication::postEvent(widget, new ClockProvideEvent(src, name, ready)); + } + break; + } + case GST_MESSAGE_CLOCK_LOST: + { + if (GST_IS_PIPELINE(message->src)) { + GstClock *clock; + + gst_message_parse_clock_lost(message, &clock); + + QString src(GST_OBJECT_NAME(message->src)); + QString name(GST_OBJECT_NAME(clock)); + QCoreApplication::postEvent(widget, new ClockLostEvent(src, name)); + } + break; + } + case GST_MESSAGE_PROGRESS: + { + GstProgressType type; + gchar *code; + gchar *text; + gst_message_parse_progress(message, &type, &code, &text); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new ProgressEvent(src, cvt(type), QString(code), QString(text))); + + g_free(code); + g_free(text); + break; + } + case GST_MESSAGE_SEGMENT_START: + { + GstFormat format; + gint64 position; + gst_message_parse_segment_start(message, &format, &position); + + // QString src(GST_OBJECT_NAME(message->src)); + // QCoreApplication::postEvent(widget, new InfoEvent(src, QString("Segment start %0").arg(position), "")); + + break; + } + case GST_MESSAGE_SEGMENT_DONE: + { + GstFormat format; + gint64 position; + gst_message_parse_segment_done(message, &format, &position); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new InfoEvent(src, QString("Segment done %0").arg(position), "")); + + break; + } + case GST_MESSAGE_LATENCY: + { + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new LatencyEvent(src)); + break; + } + case GST_MESSAGE_BUFFERING: + { + gint percent; + gst_message_parse_buffering(message, &percent); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new InfoEvent(src, QString("%0%").arg(percent), "")); + + break; + } + case GST_MESSAGE_QOS: + { + QosData data; + gboolean live; + guint64 running_time; + guint64 stream_time; + guint64 timestamp; + guint64 duration; + gst_message_parse_qos(message, &live, &running_time, &stream_time, ×tamp, &duration); + data.live = (live == true); + data.running_time = running_time; + data.stream_time = stream_time; + data.timestamp = timestamp; + data.duration = duration; + + gint64 jitter; + gdouble proportion; + gint quality; + gst_message_parse_qos_values(message, &jitter, &proportion, &quality); + data.jitter = jitter; + data.proportion = proportion; + data.quality = quality; + + guint64 processed; + guint64 dropped; + gst_message_parse_qos_stats(message, NULL, &processed, &dropped); + data.processed = processed; + data.dropped = dropped; + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new QosEvent(src, data)); + + break; + } + case GST_MESSAGE_EOS: + { + /* end-of-stream */ + // g_main_loop_quit (loop); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new EosEvent(src)); + + break; + } + case GST_MESSAGE_ERROR: + { + GError *err; + gchar *debug; + gst_message_parse_error(message, &err, &debug); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new ErrorEvent(src, QString(err->message), QString(debug))); + + g_error_free(err); + g_free(debug); + break; + } + case GST_MESSAGE_WARNING: + { + GError *err; + gchar *debug; + gst_message_parse_warning(message, &err, &debug); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new WarningEvent(src, QString(err->message), QString(debug))); + + g_error_free(err); + g_free(debug); + break; + } + case GST_MESSAGE_INFO: + { + GError *err; + gchar *debug; + gst_message_parse_info(message, &err, &debug); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new InfoEvent(src, QString(err->message), QString(debug))); + + g_error_free(err); + g_free(debug); + break; + } + case GST_MESSAGE_TAG: + { + GstTagList *tags = NULL; + gst_message_parse_tag(message, &tags); + + // QString src(GST_OBJECT_NAME(message->src)); + // QCoreApplication::postEvent(widget, new InfoEvent(src, QString(err->message), QString(debug))); + + gst_tag_list_unref(tags); + break; + } + default: + { + // const GstStructure *s; + // const gchar *name; + // + // s = gst_message_get_structure (message); + // + // name = gst_structure_get_name(s); + + QString src(GST_OBJECT_NAME(message->src)); + QCoreApplication::postEvent(widget, new InfoEvent(src, "Unhandled message", QString("%0").arg(GST_MESSAGE_TYPE_NAME(message)))); + + break; + } + } + return handled; +} + +static GstBusSyncReply gst_bus_sync_handler(GstBus *bus, GstMessage *message, BusSyncHandler *handler) +{ + Q_UNUSED(bus); + // qDebug().noquote() << QString("VideoWidget::gst_bus_sync_handler (%0) : %1 : %2") + // .arg((long)QThread::currentThreadId()) + // .arg(GST_MESSAGE_SRC_NAME(message)) + // .arg(GST_MESSAGE_TYPE_NAME(message)); + if (handler->handleMessage(message)) { + gst_message_unref(message); + return GST_BUS_DROP; + } + return GST_BUS_PASS; +} diff --git a/ground/gcs/src/libs/gstreamer/videowidget.h b/ground/gcs/src/libs/gstreamer/videowidget.h new file mode 100644 index 0000000000..28c72edd26 --- /dev/null +++ b/ground/gcs/src/libs/gstreamer/videowidget.h @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * + * @file videogadgetwidget.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup + * @{ + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef VIDEOWIDGET_H_ +#define VIDEOWIDGET_H_ + +#include "gst_global.h" +#include "pipeline.h" +#include "overlay.h" + +#include +#include +#include +#include + +typedef struct _GstElement GstElement; + +class BusSyncHandler; + +class GST_LIB_EXPORT VideoWidget : public QWidget { + Q_OBJECT + +public: + VideoWidget(QWidget *parent = 0); + ~VideoWidget(); + + QString pipelineDesc(); + void setPipelineDesc(QString pipelineDesc); + bool isPlaying(); + +public slots: + void start(); + void pause(); + void stop(); + +signals: + void message(QString); + void stateChanged(Pipeline::State oldState, Pipeline::State newState, Pipeline::State pendingState); + +protected: + QString getStatus(); + QString getStatusMessage(); + void paintStatus(QPaintEvent *); + // QWidget overrides + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void mouseDoubleClickEvent(QMouseEvent *); + +private: + void init(); + void dispose(); + void setOverlay(Overlay *); + + void emitEventMessage(QString msg); + + void toDotFile(QString name); + + // QWidget overrides + bool event(QEvent *); + QPaintEngine *paintEngine() const; + + QString m_pipelineDesc; + QString lastError; + GstElement *pipeline; + Overlay *overlay; + BusSyncHandler *handler; + + // DeviceMonitor m; +}; + +#endif /* VIDEOWIDGET_H_ */ diff --git a/ground/gcs/src/libs/libs.pro b/ground/gcs/src/libs/libs.pro index 975cd60e85..9787216cc5 100644 --- a/ground/gcs/src/libs/libs.pro +++ b/ground/gcs/src/libs/libs.pro @@ -11,6 +11,6 @@ SUBDIRS = \ qwt \ sdlgamepad -osg { - SUBDIRS += osgearth -} +gstreamer:SUBDIRS += gstreamer + +osg:SUBDIRS += osgearth diff --git a/ground/gcs/src/plugins/plugins.pro b/ground/gcs/src/plugins/plugins.pro index 53c3c619f2..9249556bbd 100644 --- a/ground/gcs/src/plugins/plugins.pro +++ b/ground/gcs/src/plugins/plugins.pro @@ -231,6 +231,13 @@ plugin_flightlog.depends += plugin_uavobjects plugin_flightlog.depends += plugin_uavtalk SUBDIRS += plugin_flightlog +# Video plugin +gstreamer { + plugin_video.subdir = video + plugin_video.depends = plugin_coreplugin + SUBDIRS += plugin_video +} + # Usage Tracker plugin plugin_usagetracker.subdir = usagetracker plugin_usagetracker.depends = plugin_coreplugin diff --git a/ground/gcs/src/plugins/video/VideoGadget.pluginspec b/ground/gcs/src/plugins/video/VideoGadget.pluginspec new file mode 100644 index 0000000000..49531b6160 --- /dev/null +++ b/ground/gcs/src/plugins/video/VideoGadget.pluginspec @@ -0,0 +1,10 @@ + + The LibrePilot Project + (C) 2017 The LibrePilot Project + The GNU Public License (GPL) Version 3 + A video gadget + http://www.librepilot.org + + + + diff --git a/ground/gcs/src/plugins/video/helpdialog.cpp b/ground/gcs/src/plugins/video/helpdialog.cpp new file mode 100644 index 0000000000..0c16a7c85a --- /dev/null +++ b/ground/gcs/src/plugins/video/helpdialog.cpp @@ -0,0 +1,86 @@ +/** + ****************************************************************************** + * + * @file helpdialog.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 "helpdialog.h" + +#include "ui_helpdialog.h" + +#include +#include + +HelpDialog::HelpDialog(QWidget *parent, const QString &elementId) + : QDialog(parent), + m_windowWidth(0), + m_windowHeight(0) +{ + Q_UNUSED(elementId) + + m_helpDialog = new Ui_HelpDialog(); + m_helpDialog->setupUi(this); + + setWindowTitle(tr("GStreamer Help")); + + if (m_windowWidth > 0 && m_windowHeight > 0) { + resize(m_windowWidth, m_windowHeight); + } + + m_helpDialog->buttonBox->button(QDialogButtonBox::Close)->setDefault(true); + + connect(m_helpDialog->buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(close())); + + m_helpDialog->splitter->setCollapsible(0, false); + m_helpDialog->splitter->setCollapsible(1, false); + + connect(m_helpDialog->elementListWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), + this, SLOT(pageSelected())); + + QList plugins; // = gst::pluginList(); + +// foreach(QString pluginName, plugins) { +// new QListWidgetItem(pluginName, m_helpDialog->elementListWidget); +// } +} + +HelpDialog::~HelpDialog() +{ +// foreach(QString category, m_categoryItemsMap.keys()) { +// QList *categoryItemList = m_categoryItemsMap.value(category); +// delete categoryItemList; +// } +} + +void HelpDialog::itemSelected() +{} + +void HelpDialog::close() +{} + +bool HelpDialog::execDialog() +{ + exec(); + return true; +} diff --git a/ground/gcs/src/plugins/video/helpdialog.h b/ground/gcs/src/plugins/video/helpdialog.h new file mode 100644 index 0000000000..471898ea07 --- /dev/null +++ b/ground/gcs/src/plugins/video/helpdialog.h @@ -0,0 +1,60 @@ +/** + ****************************************************************************** + * + * @file helpdialog.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 + */ + +#ifndef HELPDIALOG_H +#define HELPDIALOG_H + +#include +#include + +class Ui_HelpDialog; + +class HelpDialog : public QDialog { + Q_OBJECT + +public: + HelpDialog(QWidget *parent, const QString &initialElement = QString()); + ~HelpDialog(); + + // Run the dialog and return true if 'Ok' was choosen or 'Apply' was invoked + // at least once + bool execDialog(); + +private slots: + void itemSelected(); + void close(); + +private: + Ui_HelpDialog *m_helpDialog; + + QList m_elements; + + int m_windowWidth; + int m_windowHeight; +}; + +#endif // HELPDIALOG_H diff --git a/ground/gcs/src/plugins/video/helpdialog.ui b/ground/gcs/src/plugins/video/helpdialog.ui new file mode 100644 index 0000000000..9f3bd6841b --- /dev/null +++ b/ground/gcs/src/plugins/video/helpdialog.ui @@ -0,0 +1,82 @@ + + + HelpDialog + + + + 0 + 0 + 697 + 476 + + + + Options + + + + + + Qt::Vertical + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + HelpDialog + accept() + + + 297 + 361 + + + 297 + 193 + + + + + buttonBox + rejected() + HelpDialog + reject() + + + 297 + 361 + + + 297 + 193 + + + + + diff --git a/ground/gcs/src/plugins/video/resources/22x22/media-eject.png b/ground/gcs/src/plugins/video/resources/22x22/media-eject.png new file mode 100644 index 0000000000000000000000000000000000000000..5a232e64a5066ceeaacee9684587d08cb72f7883 GIT binary patch literal 729 zcmV;~0w(>5P)r zmETKKVHn51kDJGNHalZyD<@OtV&fPx)1-?EY~_z-l2B>U?Qq?yWQTF1N&zg99v$F28rk$Gp7nRGIQRA zwW#X*Lwzq_zj>8@`uvH0rtkC&fI0wFsP}gn@9FLdjvqO6*x%gLVmQ&&UGDRGgU8z2 zdI89Pp3*rI3-f9`#;fsF9vKbAl}6<#05NZW9;o1Qx%{D_fyJez zFRW@Rha)Q-j;ye*>pVX{mkkY{TX4BtUI2#e7+Z!%MAXpG;B0E~-x?eobjY%dZ!2Ga zHYC&pd_IK&f#dE}Dt-OKd&dm`i@$QVWZc===}`9hdOhy?;K}YDqpGP8E6uQ&ccIE6 z!6I6qr!<@&9jkEIcMrSw*0l!$0qYino1gT=#Do})Mh_aw@$Am+yP{o|0U{6xMC&tI z8w{9%PoFgH6WjE!BYs=l3`p4K;5IZ?>6Y*zI|M>3fduS{Of6rB3HGMS7dnv888 zk7ohE_F$*e39HrmP}8(Wo9lv5_$8Z6CS4GOoLFbm~*&FkqMX5J;%TC)&hcsf0U zl+q==eDy-5X-dWzXstO2g5-31R+0Mb`O~J>n&W*h8$HUAq!h~wD$f1KhZNnC)4DrxbCFilgtzwbaC$Gn#~ z%Q7Y+5Mk{xZ&S;nX_~UvR4{Kreyn0emStQjkurdg(kYLVgaIJWbEkN*T;SLI2WZ3W z$121{QMka>qD|mYPDLt z!FX0E1m!^@!eB7iKNZ8_&;|0WSsA5vyKMnrG#c5hr$q(h=U0d)lZh2V%pN^?kof?m zl)ZU$bOiw8@tCc(KOa86U%4ytyty2Y$9C`S#>|b?Y84q{Dw7|EAxBYUh-l8t4P#8S pTCFOWD2g}?!zB?dt{;q@^#^0ynjy&$AqM~e002ovPDHLkV1hVYBZL3| literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/22x22/media-playback-start.png b/ground/gcs/src/plugins/video/resources/22x22/media-playback-start.png new file mode 100644 index 0000000000000000000000000000000000000000..10102d829cd4a82c14aa3bd7d3a56a2f5c35dff0 GIT binary patch literal 961 zcmV;y13vtTP)b z)!%CrR}=^E@41uN*~~861ncT5ZIj(it&JE<-zq{O)DUb7hFW9Pg7u~9i}(vc1O>s` zE*NWDY8zr&v;FZ8l-efo7otg4Z9yyOQz@BEvYTXflkDuwj&pp7yX@u{snr7q?tPfg z+!me;?&YUSCk-@7}AC<$Pc7upnNE+l|g zH@9rcGBaB_i__z2KJ#U((XzEUzV7X{I{^d%_==yIE9ZmgsCO19%01`qc2?u*O4)I8RKDvu)da=dU}?j*maeG<~o!8VCg51h5Q%Yc{x~ z#%{NJX8jnL%uK+@8hCBR3Oe^qTlvMV-!>_3<-L@yM|yAc|LpVm#)J@~x~@Mew5qrl zwY9aO)oWJW`ziX1k{nHdo;0(h+mZ}b^}sSsw0E={y?r+mW69KEP19})Ax4QP9gD@d zWW}LS$jOf-NdkdNU}lE>D2Fk91TI;@u1|KCH*IWs`OFusZHr#4?9WVQ_Y=_ofI(3V z2ZKQ;59MrYDsxk5nps$u3EQ^eI1aLA2Bw)sU}*pwHoWW3+16V=pSm;{3^Fk7I}U|H z&Tujg01yx$As~oAL;z8K*^p%y%H1loxP~Ks0@u{k z6lyGpAfCXIBtb5d0RY`S-PR8mzBfD`@9}+m4|b}mnhJ-*7N$iBTvb(7fE~vH5kZzE zxMUX|Jbd6pBA=O(Bwg6nvi;MBhK3;knd#uHu%al1u@C|t)eAk9LTl@3BOV{* zmCK6MKoo|*bE=Y#ZC~gPePbH2!548Sh&veuUr)qWQ1NBFG$ONiMD7c+MyArtSVUI9^uA!=%FOn~pkziwCvs=spW_EX_?1xb8em0wRN3q-O0)QZ>Doz!r9`8Kf zMR@Otd_?49;%HD4>NHN%G@l?4M=_amP*`FI=6U}hitfiPNfIlJ<9I-pWhE#S&|r7t ziuK3CE202U&2588Zf5}tV46cUR7q~P+iv60XjHB|8BdzGf7^z`VYl&kJT`Uu`Qp|2 zlfFbjj4e+B2{7>5d_iOqEMLMX=DB;*200000NkvXXu0mjf DM~3P` literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/22x22/media-record.png b/ground/gcs/src/plugins/video/resources/22x22/media-record.png new file mode 100644 index 0000000000000000000000000000000000000000..fb53e14b60a51fcb6ffd24de03733df58e7ae7f1 GIT binary patch literal 1074 zcmV-21kL-2P)b zm0xIEbri?H=XaCbBsVwflD28utS+G#Wn#_R(M}2zChJ2`pM3CX$o4dM%%7zY>W<@=J&_P#=O4Mr>UOL!`j+{ zRjq=FA_Pf-*wX`bc9#En{kr-qW9Pr>?3~DEv(NSg4-O7G`&2dl>B7RjSJLTDM^_hW zPo99ACct?^whdSoI1#+@IIPW0{Ehv_6!5~6s&j!a97q7^&9Bi_7f&G5JTQZDygXhjU)JKm1 zRoxfL>vcfafm#j9!~}x<{Z5~*zt`2(<=z8Mr_+wNa=Ebc$PtlwJTQwz@LFvblr#;b zY2fSYFz4r?PfbCangTHfhbVgATwU!%;|g%BLZt)AP9|dbKU8L z;P<1pw&vPMCg%X?yTGYb%BGQ#XPon*Q7l4?#Xv+r3w$ROL=a;!7{wwW2y4>NkdjKJ z_IQjyAW*SlvGLX0w>4>C08Em0d%dmZT#zJqPM*Z-ojaNyjeZ^o1pX=Q*2<%qnVD{D zWaNiXB5~&EsZ&C2auQad044|xF9C4d1`CDY9vZ^t!-rNOletIt?|+y`B=P|GeqaD_ z&0oEGqCP(UqukpY?M^1$wyMG|mB1?%Fj)o*20>mg^0#hjN-nqPxpwV?_~pwB&G`Ns z7%M9)o_0a#csx8j#*4*|cH~;_u literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/22x22/media-seek-backward.png b/ground/gcs/src/plugins/video/resources/22x22/media-seek-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..e094d12282c79d2626fc64a04f9263447600c8ca GIT binary patch literal 1003 zcmV* zm0wI-Wf;bv@0?TGa|&zA8L%}w=!OVv9aAYdvV<@i5E5d+e>h|Xp|iA%xn0b_glV#6 z-rx)|25*ef7~%zYxOjnoZj4@fLq}-h&8Ti!`O^((OMA}udclU(jo>bJu_t-&p8US& zdz0t;7!l!dNO&yNClW0EUn6784!{OriHO#=@yGyF02@0dW{g=iO>@26)_S$Q-Fp&1 z-a|Yl#+cL7=ox&y^IrrLeKl)2$ZdpRcTZPKiY3e+TeImPY`PUt3#q zy!h$jOD@-8EgZgX0WkCcm@y^-*nRBSQO~}ov`aPBHGBGc{YoN{z=ijR?|T>kTUlAz z!9$f5BYTUS^=HqWwUq3ChWUEBIFPmDduB(utNgI*V8y7W71zFd=A7)@>tu=4diL2l zs;WL)Q(GO{t*ZV&px^Xj^-X@^}kWb=2$iTHD%Qk##+d<#-H40Oi^M0A{lpQgd^2VSRnwYi5i2 za!1D-a!X6AC6nI3-PJh27)+)d0040U6NHL}hT|Uv13^Vm6eJSsAR-8Y2+ldcHq*M@ zZbNXp-GX9OS`U%>HO>FpTqs3K>Vklf809 zWM1c-Q%28FGL@v*VvGg`L%&5Lk)mKQ5bW}GE?)coT2j|_G7N)~$pmrEsjItFpPQT8 zvjxr?%h>o>RZnmC52GK9tS>Du5$Bv@vDCngNPUryNL#{xT5`&(gr)7I-Vf#KicKxBGnG4PAIIdF$3q z-sRf_vmM@JF&d59uUxs@7r(nQYI8V#nfUbcHv!zq?i2v)$jEU0jp&UrT{pDJZ@%5X zUE=?ad3t*Kz{0}9J|fz+jYnKrS#eBFO_dW-!A^;@#Q@|0NdK#156T399tHeJdh&jd ZzX4%jw82MZjEDdL002ovPDHLkV1m+b)?WYs literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/22x22/media-seek-forward.png b/ground/gcs/src/plugins/video/resources/22x22/media-seek-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c2c6c8ed130fd6f665aedcbc63ba4d9281dfca GIT binary patch literal 1025 zcmV+c1pfPpP)BDxGycfX}yyMEQ{?`%KiS-q-%_}j6!iRg!= zt=GL&r^t%p3IvYr$t59^&y=Zn+P>1Lt~*i0BO%>}q~-L${0!~XR*Uu`_vb>#8Vl9J=2-+h0$ zprBwf5{b-Hb#?XnlG61*9uM>&7MleY2}A^k-GRCJc^o-<=;lNygx1z~9gH-R$s{Z* z1psin@}R0VoC|(p^!4@LG)!|>e9ky4d_JEA00css)37WHmX$;zVIpqCk(Zl?=bw8) z2a!%BKqMf9fTF5UR262zfFvYrs@bfqS+j11!{ONC^ZA5aQBh$H3|<5PNRkXm2uP9w z&1OSsYA2!Iut z*)!~#9WygC-0p8Teh>fl#UmAu?rx}W7z1G5xo!-Er^4*_w;R)uNPl(JV@)^3N+6SfiEM3W6aD`+{jI+MSw*B9Myfa700000NkvXXu0mjfp}gdd literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/22x22/media-skip-backward.png b/ground/gcs/src/plugins/video/resources/22x22/media-skip-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..2a5e703062ac0a26ebf405086d134437d7696ee2 GIT binary patch literal 1048 zcmV+z1n2vSP)Q;h903ad)KmeHkBX2|^a;vK9QxsW>#}lhWG{?-=-G+%M2Y?qqf|+OU z($@jx16UDNRV_Sp_}~?0b`sIjmlwyYiRf0gaw3uecz4zAY;ffoHJ8V~IlT!e1IXXI zx9+H(qgRc8b@^>UQDm8!okK%I4rX>LiY%vX0TBsA!%OwKcQ-B1W0=f0M_EdCJ|)FvRqKRr>42EpzwHKU*F0j4+jd5Hb0jc zFvmZJQa1R3?V;`4N*^qKx3RI&6AnjQwr!h& zuoj}cygc>s9aa4v(|oC?r`IYkuQZmH7SkC>U!L3TW&n^R831s9QIT4+dslbKL#5B2 z>^qq>^E{??YaXdo3eKEd0KlGowckctqN@!J`}KG{=9-`X8`)7(6a`{MWo43xAc+!) zh#uQkajC1T%YLGIr)wp4hZYwt2!a4XZ~y>;!5}(2jvJ!ra4s+31~Wr&IFL%E*1@4r zC@GW&0?BN}k}NrWzWapCxkwN|Bp`Fh=BDSNYnn3_k0BP1A+ef3GHGuB2ZO<+@Ms3S z<}nu+l{|H-|I{yo9}V7+BzeimHJ~agWLW_KoOt7P437-oltpE^!0UtIazodg&@_Fc z;xfPA1^_XAdG+=6vmd-S*mh%Xwy(9V<@VKUSAW-3J*8(oIQ_?;uf6zk>#r9teK9Lb zO5B-~3r*2BfH5_7y*Sbow&U>_N1DR+)YSE2W)_**Jvlj9)_UxP&pJBW=NGJnS!U*N ztQgm~oTwr)x`ZZ)tl!AKD5=e3LhSZ(q(i zzweyyob&sLbB>jOup+E0NzDE$A|fIH1b}nSIU}Ny>;*HNbA1_SclJ2LIgkGfsnce& z6-koBVzJl)5#44ENB(_v^>zEh;qX-=x|}6QWn%!8}v(XqL9^TsO=KT`h!fK^0f&t-0K1G=t*K$%ki zG8kznild~r=eTX(zF^(@4K-gpw&l@1j4?6ivt~_YS^o#8?M;o1dm5V>&Qw)ZZ2(XT z0N{WiEDo6rC%-x$7s@GQg&J3KjHE-YETvc5$$jM-w8;H(_5!W?S%ge8n z96Q$i;HKL37oT`)>ua)XW2sb!11>r$7i zKOBjEBryp@gg_vW7K)3Djm#bZX2}fMYKJJY*x=xYf9z`6dGpSlsojDg%o7o;78!YY zmh|Yg(VutheEy!ku(10;*TGkPK5vMKz(i^BDO6Q4CMPo;6-Ab~{K=)Mk&*M7yUg7= zeD?g6p`ju7jqkq&0L;$L{uT`Gb#U;{+JpO#R904o0mLm9iyHt0A(NoT<1rWjM4}Pt z+V#<2`cC#)oX*1cP8{z$rzlDozybhwC=@dH^mb2-jg8G!uU+?CTU*-?0HOdASfV$E zu_(dJ>n)E(qo4jXd~W#a8?V3F=l2I@0ce@Y1iW5vOp9yd<3EmneClNXfGo>V0I{4M zfZ=j^qgwncw6yMm#cKT>fI*z|B4kua&t_rZ@5tGc03rZV_$O=ttXV$}AVQpTwj^*l qn337gWtD9SaXXn)&;tcg z5uqpu)`|t8Mzogt;8Sc0_91A~mZV^tLQ@!BQ5ZQ>ttN25mGZ(nwtbV*!g43WX(DE?<&mdq~Se z*dA?Zc%q||bxi_cdq~?uIB}n4dswc6v^~Zq|6H*(E8rM#xi=+fs51zAJ;|mBWxH68 zOKZ{~Z5J%XBjJ>@o8l45>IcQLA1R>cH$WAb!`+E2icUo%mWX7Nmbu={?6B- zbp&CFDAM$wfry|oeDIx%2qD?@@P2YFk zF*>6E%qkhTn74TFk#Onz^i&O_Gx5E=#=>DJh`3YQym;0=)ssT&{{*JF*V^avF zkd|T~+lX`=?z<}wz|X%npd6dGdpA=G{yn@PpyU^UaP`C6)7Ol0U6kvgJcYC@&R)D7 zo&Sb&v-K$DP$EQWO`t8LC6Sh_e2-n%z0x2hQV1X(G&s4Ap8j(T{gwn3Pll8zDdQ13 z{@tsps#a!f+YYHzV_bw53M^8%CWH5eJz%(E5lW>RQA(|Zt8cDmU*=Kf=I6-g=L!6P zAPoLfWpf+{DJ4=~*%Fb-yw*{BTcUrh3jOPu?qW7OzC{4u3okzZc)InT-nD>xdk<|r z`s2Wna*_3bdY}eK0Pa<>ZUw_%_I?St4EzBU#j;@UI)GOJF0R)04}ZW#BUP0;!~g&Q M07*qoM6N<$f-X7Cr2qf` literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/22x22/utilities-terminal.png b/ground/gcs/src/plugins/video/resources/22x22/utilities-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..ceb0fb993f16dfe0feb68a59a11d56ce743f81b1 GIT binary patch literal 1026 zcmV+d1pWJoP)M1CL2W zK~y-)os~^!9aR*^fAj8rUwJWsL@XK?q8N4K7hPD2Ek&f&Qv6t0s8tY4y0E$s6qgYZ z5elN{O6p1}2nGdRDT)X!(Y7>6(}FEYT4K9NU-R+uX5P%b=eU@8llPK!t3{sPCp{hA;MKy|HHRY3E?Rkce> z+Eqk7hY!8QqYrOIobR>XIRK0?s0i*Z`kkih{L=6xy(|dPO0cA-`10_hIUda+qB|l zXT_=*Ygt)ZW&Y|M2VZ=Xf398T60JAaX=)x^1^1GJ6{G*@G+ZC4C7NfNvltT7Zz zhR=_EgtbLRUU{1@zc|kB{f|(q6oF!w^v+HI6iWtU4bFR173-WMap}{Rml_;+VT7l4 z?B&$i6YP3sD+~1-bZmt#Sm$6ACRk%hvgK=?^CWSCu@++s4BdGTZ@lv=7ydp+0Mz;_ z-O__@KiXYu(~6^JD+4D9aU5fsOr zK(V~$<;96>8xEi0$L?CEAiWe!KoA6YFKsIvtyYVm5P-tfnR@=mp58Jy8$lsRH;+&E zg%uH8k}x-SnX&K3(nIJLLRFcaouh0^DHtVMxV}(nuB`IgFTdV2=<(MLXO4MhuFTNi z-^XD8?NlpO?%1*g5#J2HCnTu1D1h$0F=`GnnSroFdgvZUl7Dh5DLShtpET307*qoM6N<$g2^e{3;+NC literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/32x32/media-eject.png b/ground/gcs/src/plugins/video/resources/32x32/media-eject.png new file mode 100644 index 0000000000000000000000000000000000000000..b218e7ae98c95b9f25e52421154e803438405f62 GIT binary patch literal 987 zcmV<110?*3P)_AGarUp`cNzY|w_fP8;evZ5le| z)jXxsW1EpkWC(!&VF&j}k~DB`V*K{4g`2i%nWPr-q!qVEtyZNUZhh}eOpM?5`FsOt zwCS#BNdv)PFqBN5dOP*PnX@maC%lzv8H@>FOaK5-y$;hdaU^ntUGh!ou4&L z+qE@!U5HYt)WHJ>q}1V1@Qcqrz20Gz4Ajgj7{lLEqph>o+W~)j0dKe~3%-|dG{GQb1|1_s8q&4~yELRV)O48y>cEAQk% z&RNF1x!Kv-s#^@#63ooZ2t7SLA=9z0UpRkZAQ~MMN|h2sQ3U54oO>EqkwX+YL{Wq& z2(WD%-Tof*_4Rsxy>sWuaR1}q3=a?M^YimfTa&vH2!%p@zvOL(G#K>@p1Hfb5 zft?ceFCu1er|ci7&spvFP!5;5ZKI^?Hk;@Bh$O z&cAjvnM^LVMIe{U1poxtKMM^&lB5=iY|$YiNs`oXINVf>JBcu`H6CQk1{x_*x5j;N z)j%v33!HiJ^lzjqe3@o5|lS^OnWL118v10n}`2q1t!a2y+)bHwBE_6bCz zQ4&QFS3mjC)wmUiTtqY)b^SkZRX7nn)GlC*?YJlGV;_$${sfFEWW@^W3)uhw002ov JPDHLkV1keW$yWdX literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/32x32/media-playback-pause.png b/ground/gcs/src/plugins/video/resources/32x32/media-playback-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..1e9f4d535795bb2d2afccec95b0e5ad1cafcab8b GIT binary patch literal 481 zcmV<70UrK|P)1uoCLv!^$R=$F_1r5`T=3!q@j(2$AuqraX_xA% zur~90u(eQZ+1WWY@6j;t!{lLx4+3-M%=zErBA6#YbD{eIe2HYv#zzR$&##|-K39Bv z_vz^)m^6X5Z3XcD?Fa6D-vLngg6o$rXxmo1-7bo`lq^<25}VCeZf-XJbTLe_z}3~& z@$hNdv!C`%({$3sFv$X|)hYl4h2x$M>%UzLlPs`YE_FB&mt{B{l1{o9CRt#ySO_3U zk!K%-@tmkmYD&L!k2h|?`&qr5sP7xZ=ftonHdi7-XGuA%#I@#!z2szeJ`sja1QT1 zAsh~`D*C=xER!tIbzQi>UdUVqSwILgqwBg5%Oneo<0vyj5@t1$4r^l^N5wM90!`D% ztm5^XS10>s6;0D9mbsJ%{|C0)ZJXzLey*FQY3loa7sDhA07yDJuFkpe7-r6#Ip^{R Xfk+A`Fy8dj00000NkvXXu0mjfy?@sl literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/32x32/media-playback-start.png b/ground/gcs/src/plugins/video/resources/32x32/media-playback-start.png new file mode 100644 index 0000000000000000000000000000000000000000..66f32d89b59aed6480c4b3682ad57055828498ac GIT binary patch literal 1028 zcmV+f1pE7mP)pW=oQ8_HEoW$@{l6 z<6$?75H{PG#)J97oaVjxecpF|^PLCmX;1&L_!jfc02%;z<+1Akk)wS*9~ru4E-jfB z00)5BO@L4`*?OV9z2l|k=wq$R`9EgY*VhFA8-UssfCG;wPftyK##>qvPb@CZpNKR! zVPSr8P6#1Ua^&Fy3=SScL#Unx1_pG)(7U7(#{&Mk^!)t73VBjWoV|MW3s|-VqKc1$GmiVKs~G>_yf>H5e1G3sdz(p?c3pRAW@ctnM_8o= zqtR%Ik{CGW;EaQF4#r9jD5W5!fLHWjJ2qBU3y`Xa(b0GG(Xp|^!MeKd0`+yTB@&7F z$jC^|0|SJ^VE}+q5|(Yjb}ZPo1;=&Zc@BgqLP!ZH1x5soYw-DMz!}HF{DQPB3!GRw z91de`ZEe#i*D3+TVlmwQR0%HuU+zXs@SnC$9UHj6ze*NogxG{X@)Tvj0 z5JKd-y1G^>tzEqXh7JWlDhZMV2?@p+q>#9E^QOG~$wj-qzTv~OXUX34k)D{M6vXOrXzdr9xh(`*i{OjzkKqQ@y^c9EPyN#J@DmJ5dZ)T!+=r}nx;bv zg)3Jsd+GG=w-2VCesgee=njA!5mjaPwg7Zp2j?1o`te5b+nI07c)aD@`{yTa04S7; zS8ez901O@PzBgw3AMsy!Lg+ifKY}4*-0kpQZjveZH{;O0fl>=ZBQDG+vZ>VCp yE2R#gl$M2Z@g4mi+)_#@T`A?;)uMa)-|0_!Dtwg}>0a3Y0000u4^P)G>juCK3$14!cX>NBo> z0Ot=LZsQLD#K#soJ3A9V@PNya_iSs>P5fU2%;$55ZU_EEzMj8{zfVU~Fq_RJ97)7c zjPD|AzjHU#wpM1d*+KH707X&g=Gzh^;y5PK{>jzGcE_d-Em{K5z8=MHiydh0f>bf2VkY$;h8Sdh?Z3&@8!3hdm5a@2SQ9L4! zJ4i{G8CjMMtAWL0p{p;KL~%q!R|g~!u}0nYt4o5k>38O$16V8;!*-ymDw$1*B1n=k zlCQrI*qG_gbkj#*Hl?bnVE|=Wx|tCXlO`$dzGLM306Pu9%$Q6j!vM@oW=0f29Cv3> z5{}bdBU-opRBvjas;Xf%&@_$A3`vY9lWop__k{Ja6u2BSqiLF9JCNtO%%;40^YYl# zUt~5V&+~%-NB;*T6~MT!>2kT;8?RQY+q$lA`o^Dh(nlb33ojc#??e+0(ZD2zoP)cDHi%=gvMB&;s?bZGJ(^tISFTU~lW_&*1uZ2Qk z`5yr^H#awa`SRt#gTQymGiL;$tBYuJb1+}Mf-TDcr64{ZJkcn8i3H@aF`B<{LAe2( z_$nUHavb+8pU=BG!LBFX-ripKL9y78DVIlf4G#yo`g$UdkHc781XWeQGy&6ut*S5= z7N9+S3O<=6v7R1p-SzA5ax6QV3x|z`g@xB{8q9hC(P%W*TPmGzK6NTYI1cjI7;HrW z(KJBUR}0Z~z%T$+g_O+#09sC*2=+AD=lFPjnYH8Uk?Ab$< z@o_-YfO6T%RRDAij50O`e)nz?OeFSxn$PbEhr@wQ19&{1z^+p1!-m6$1Lf&y*sAIX z8HOWPfzUJ%T?a7?z%W2%8RoNRP;TDDw$9E#YpL`R&+`IY_M0n!AP8Pw*WcljNtknU zj>uZ;H4RWz*e_ndeDny0D8dj$Sh*au2M-`LHG$Xlo_IV?hK7bbwRAW)0KebQGPWHh zmIZTu9)x89LIBHhoKO_lk_0FUD5U_U002UqiQ2LZHW&o4?Pwqnpi4_jH8;E~Kx1R0 z1pstu3Fg!kU|Ar9fG~`cR|;10Dg?s-w(Y>KL^vEqEEcPIGP*7S&+`Ul*`g&$V1vO` zAwmckhH)*Gci3PMmL!3)Y%v~>(}soyb5j5SR9g^ks53M0?AQSU{9)!5HyDQblPiP} zc(!kcn$N=&gqwmOX#M^D)}{bXpFUmI+S*16ckeFqsT2sut-63}m@4trc8-I;wH1Xs zca~{K$4D#|Q`~gcbAXz|!?((^JfS>!LIdga?@uagUlFWo0{~(Feki#d)n$2N`P{h) z!!RT_k#&#aWHPBIL!qCQ(b4{BSC`+{)C6OG9>lg+XG{TS^HBlI{nL$BS&2Cg1@|sCMPGo`pJ_Sdi(bGJ2DxeuCKL?d0$Aw@JY$5?~ zDh2uRWBPP-RH6I!9k081@kT0@T3B!V4PD2gD02Mp@Y~wetKa(L@tyTuU4Fi~+4(vE zSds*Fb{32G?v>TW#c9vcqo2{UXD2#3I!Y^J&<*I+gd=eF347?!Ax;#<`g`5o$Fq^h z4_S^YWC<~{gcwoiA%i}z}os& cY=vw52ERA~Y)(&y#{d8T07*qoM6N<$f=9wbpa1{> literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/32x32/media-seek-backward.png b/ground/gcs/src/plugins/video/resources/32x32/media-seek-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..535c536157f692c05014b14240d09f92c76b346a GIT binary patch literal 1074 zcmV-21kL-2P)zSz7323WeSe9|p*R z1tyCx_Ltn3o8LL-ckagla^%SIe~13daV!8U01E&HK>7*$Sz!Q}^UQgbZ7;We^~%Nb z8i3N1_~bDb*8_OAzP|SLww9KgU0q$BJNL6a#eIJ3cir9HZyw=ig#xg-T&|ZcUb=AW zgAaTDl%$lVX`1%xYnQeGl%2$9k67Hq^Sr(BY{Of{mZEpBT=|H1*VNJU)+SU{eG-1w z00BU;+g;WAjKlFodwaX3tFv2BH3jQ`NB}hG1;a`FtN{c-k;CCAtEztf6PL?%?&{Ug z^hHG$Y;C2XC^85kV8HAj!#Erc_ha~312~Jt;;N~sdZWN>y4=&#%Qv5Wk!Iu!Hq)B` zG%(EWH>&{vC`*}h=i54q3eA__d%u@&Zfc>M=?$!WhU>`}(du z^jG7^$nxJldDJ%;4Ghoo*dMU>OH@`?D#_&9;?s6pYW&VvgUx0mWv((p2!*PuU|DwO z;=r;Tbes-1M~3BXMgDx^&Ul0M82<3^kRpmvk0eQ{0|BvEOpV9ml1?YgZf|XVD@MXj zpU>-bSJ!d{CNnf>poD;<4=1LC;``ep%EZ{+w`Fb1bFo-o%6u{5UqMis5K~BoYfq zsr3;`m>J(aZ=)<{_|yO7kB_R+$bF9}idh2y0D(Y25k>KV(^(Qqti-No%VPi3)KpRw#Rmq1F|?Ze{kp6w8Sm_Lozqc5%=`Sw$q98i zB=!e`LFv$dUHpFkc2!mF`cg1F!%}i18WT$+k+7}&^cjxhI80AZs!K~@&%(lj1OVZ1 zc)PZ?em%4lo}ol{BX)neG!l*2%1`b1Gk4Wr=NCN34gi3;xj8Km2y9eTys)wy5{C?W z-tQ00H>rxst|a4pEE@0g`FyE^e#J^CBo65XoqyrC`6fkH*hC`E$K%OI{G*QIJq88_ z(g0%l`T5^it=4gl_x2i%#&`63y>gUakdVe%mg_a<9rUvwQHL-%I7mlFM++525h$gS s$KzQ)%w%tcRo-I$y&nuvJSs+4#8yWLZ`-OVS-jKRwQGX>OM9<#O9%YBbAnfjg#Y+O}<*0O%y2 zNF-v()S`}KM^A8Dnzzci{=RLp9La`4;X4e&Smkorn$FKFkVqtA@wnEptF0ZB`grw~ z*Z8_;>z3d7{dVg!am^RQ(ts?>p04W_0HjhWHKr;v_|?mQyZvixRFQo(rrnce+0Ezk z_7e##Ted6~iHPs*YHP=MWgL!cBa>Omw7mINz%(Z|jOHKi3k=GmSt;XWW=i_KqiaE#>N%F5a0j(cR!#wRsaD32*9%(1R;cn z!$UZ5;3ISB;a}aR;~ZiFa#&ax(;0)da4h7lZpV@4-aPZ*4f8Ca;4m310C?K@6 zwY3d}k`B-F;Cen>*MVsnkfaE9Y=47WoX*t!@^fz+VM&n?Y;J9BgJoMF3;{zJ0D$W{ zkfaEcjVa1b6{KLBO@2ess@&an6X!(Zi1G9GRcL z>VAIyoDoxEpG2eDm*H@DqNYGR9;dcbT?+;TfDll>D#Y^v=z1QfP9Hb)yxy%Q;)mp* ztPkEFI5H=H>X@PHdUqm~`dE#q_c}W}ON0zFQ1Q_bup9^9_rdZ!s87+|{jGWZ z#T1bk5x(VzR{OjF~_XDgM8p3P=wZQmrH%iTS*v1!X$ zNs{zrGC60vYh~&s93CE)D5YW`5U>hb{i5%i$M^5MJ|;9g%)_gf|&49wPjUe1ID_Rw$MOqQz%`!yX@uo zu-om0vWO49*ne_zl9^v-e)G*Yz+C1sm;ZZ6`tM4AWXhZcU?lxziwwXGfCCr>aN|LE zc|zd%Rpo!oB){+IEC_`{_Yxog3|-gfFIiGt%{Y7O%<1nQ&LS`?FHf1o>Grc3desZ% zW1Mpq3Wa7{%m%-Z+`B!ii*|7kt2rzfZk*CDg6Acm6es|;lqajfXXw;;gKP@ zU7mZa0}8;sXwia49$mcH|9HU@Wp$hVn$zJ#+ws;IfaLx7`ptgL;c%k0?N|&z@-Bfa zaWckyWo1vl>Grta{pjOQSV>VSjYPvpCTH&Z&-uFq$jkwN0q~ZT6fT>WH?Q%f7b`rk zzxD=CS_#|=jRLS>A5kjoIyu6}~c3eLGD1Ax=% zw1h4I0C!?{006qK3qr^TyFULCx!zn1-xxwH76X+e5F&vP0zxJ)jjK$ydAWbrt}o#8 z=3;o{FT~>UNnV=HYeFN8F-QQw7=tcS0;-~>08P6sEApoe{<`t==Q|TFrwex70ZL^k ziUL(tp{g8=aS%da7)G2(#NW_RKYl*YnQ$37S$Uxe%P=y4d~uf&RX$%MUc~Yj4`V?S4Es z=cx+VY_^QRM1(G27zQj$?*1uoVMFib%T+r*YZ^FnwmrfW2327IA&^pag%AQlh-6Ps zPj{f}!iGPCy;VDRZXfve?CIHgUBEQWR1a{@A@tPKh(;q&WCeCz2j`O2*7i*lz@W$D zIW;(NJwPd0fALax^~;s7vi$soG#-zE0uVxnrfF6%7>ovk!9huqPWATo2Z$uCzw}#o z_3DaO*n+9NCZK6r>IyVX%Tyo|4QEcom_746Po6wErt5lNpzGHieFIllwjFOdcl6ky z@nj;IzEcj)d8UFL9UWsiIXQiSt_wS^^j}%odgAE0W35Lryr$VEpP^wGnI?3*yy+6z zV4CK%YiVg|NdOqC@>Pv>{rJnel9E{Y{;&5m6&IG8j5FqNILK_g&sQ~e{fD2|#h!|n zHy`|}si?5T$ y=I7^|;c(bSDIMLtd-tse;j^)Gnaf=Mll%?cSD>I{@M-Xi)=?z!ODL)@&_$;rhZG9p}H#bEF{vsMx#r z9dAj|g@S_oR{`Vzkd`n&0ATN)J)gdCb|3dH>Kw0uh)A^ zykC@;zudH8!-i)8JPv?c%*7kK-;!Edmd{(3g~`aoJ#&tQkkcvRv%|H{j}9Ez_T2ON z9mU?Q?~9_C3wzJ+lO#PFzfkuXFiaDPnK(A*7jUhuC+V)&c2$;bf2kuse^WW^n8n5U z&CB8+YJlT75IA6%I_8sesHk{bYHIv)4*4wa*}<+3xZNk}1A1gI|-g zW)mUg9zH`XeF~T$2mk<0pM`@s0OEj249sY=aJgJKcC1nA3|!iI^yuehlTSrH5d`~D zLWn&;z!-z3Yw)c0prN5j>J0q2v##z)Su_@}=Xst00MGF$BRt%0H+TSWxm-{b3L6ko zMFGYD#u%n&r(u`|O1!0fUS6KEZ{LRp8M9x?G|l_?8EpnxLKeI)mh!o|c}ivF2USj| z6NCV!X#fBym64sDZ71M%yBVdn0a27di2c1_%z|ZE$Pz?IqKGR$btPL`nsoy5u}tMd z%i)`wkLw)I9i>!$4VNRnhHAc`W=2Ba(zAOy^^004rJg-A4lBS#MFV`Jm~ zp}$5Bjt-B;N=l0kJ&3QZ{Zt%HQn987ERM8LZMIs z0K8uBBK%WilW#(2LO^J zL7_4LV9qqr{%xB!8oqsT_|}kbWF$OTTwEOS`FvLT?6}=-C@BMTrioLhPio=t=*2&V zhJ7O=;laYf!h3j8T*att*VhdlR8~N#3`(h;fGo?5Dipy`j}{DFjZ98Ps`^6Le~8E9 zQ+~hSICJJq=Gg_Mlu<>-wNQ^1yw($$nv7QU^)1b-stTFPAcVl9J_Z0#>>jWa@OV6o zQi@RT)%yP4UmGHk$i#^gClXlVZ%8R+lv4Ef2kUSCHr$}=`jWiQ=VR5?)w&??$~UdY z@77S1Rc^Q2;sJo;xLDv)&y&eyQj12T(YCg>`NbAai{rT1<<6_i<=OP~^h9rPU}NSa n&+`nx-w#@6MJrm-f1-Z?`FbtGeH3p!00000NkvXXu0mjfz2!gx literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/32x32/utilities-system-monitor.png b/ground/gcs/src/plugins/video/resources/32x32/utilities-system-monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..b62959e4f3d7871c986f24c6f112153561912ea8 GIT binary patch literal 1886 zcmV-k2ch_hP)-U0|+Dp3WA0}6@ieFRyc7|mpHW@J5HSS{_yVJ zJ2Qued%e4^od}^Y(&(O4AIEWY$i^<`y(gKDE}g8M^PBd1 z*U9K*rQiPM*KYwQLucKyVLS5y-m+t7r**DU;pAUve_)o`z*kQfIH{eHM)(1dq)$dsb8~Mp?@G? zdD)>Pf=Pv|Ch_*^fmffz-z#1f^&amPF^1V0kBGtdcybfs9D;T0b^#&`^a;Z|GAu4R zTpFEq9-3P30X%plS$m%dwCRh(%rK3Qv2R13-RfwEoy%IY`LfAQ;XJ}iVd?CQP#M*3DiPwdvLaY~@ zcGt0kR^*vqu5)Ll(N&|x=zTFY}!JB^}L>6u2XM%3WY4Cz6_I3m*_8t1P01wgZHpw z7!K?W`RGPWwc%+;j&@{eMV4mU5=C(ue+Lp^0zrMk--vm%2bz)6uCy@Kn888Aj^RA3 z%~ez(S8Q?n{wmKrU*L&d1+t|YvonGK17%OPpk(rbY6kB->P=FHttX1%USjFiSs)0R zjGn8$o33%VW<*uAH~x4$(^K9d84zzE|z3jAa0KFzk}xr0M|ZZc$aD9_dD z2OQd8=IApqr;iso^3)J!Gp0M^UOh{p&paB0B0?b-(3cItBsu=^T!mZnZ5l0j;kkf) zPxR4hd9K`0_U-bVd8wb#p+5e0^*&QGEz~QDPm7X*_gL@n*3w$@X&OERgtNES_|e-H zkPwjoF#*N|m>^)^lTdG34nEn(?Zp;1Z{@f;)8h1zK|Z)r=v=BX{5=N1yR!;;bGbAv{bKxGWR3r9J6uCKPX*6^EbIPK+%AYT1S*~PJ zJc>g_5OIhoNC2WBn%*JiAs(Pxsgh|hVh|G`#?WdT7VcWcwtH?b3Z5MEiy3N-kdL5TB)%4!}l9-{(S zR1Kn<-lTpbKQAd{`k;uJH0mfEG=ElL-luo1B74cZ0WIwlei$8p5c z^3n#yItr*N%~q4x##kF;W80x)Yq2)QIoJ6xj$`6>M4{N1A{$!GCdLT4d_FNMwzQ*& zLLrYa-7=jCGab6K>%C7}XxrOPTdZ?Qi@ia(<v$v^N>)f3Fm?-LotrvvG7}Jhzj1hygiNHq*2WZsl3=Iu&^ysmU^$Hw5e3;3} zN$T|mjYfn1{y~l%JB|^_T4Y--n0ArB4)p}F8qJ} zoT{p|*3C{|pZ>vx3xE0}u()3T4A2jhfE-)2YL8*~B&Ni`Do_KQZ1G~Za6kUht;y;5 Y4{P?sxg-E8IRF3v07*qoM6N<$g5g<-#{d8T literal 0 HcmV?d00001 diff --git a/ground/gcs/src/plugins/video/resources/32x32/utilities-terminal.png b/ground/gcs/src/plugins/video/resources/32x32/utilities-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..f86c7840026a28c021d0ea682263c0c45f7b4811 GIT binary patch literal 1488 zcmV;>1uy!EP) zU}Ah>hzW^*fFThT0wOLIOl6*g}_XEo_yt+bWdp?zX$VckjJ3 zXJ#H>oO91TckkWClp0_7+{`_5o-^lpzVrRgGjrho-4wtRPycp{M*k*CyhcRM`%hjJ z5kd&W7>m|@ABnN;6QQXTlWCgXao3&qybYk9eDd+ly5GEY?daIx+L4P@k|gJRmLh_P z;GE;1A03kyUid?;QEP0w_udEJOO(-%jb5{5aCm4~%^aP^dwbc6@W^ z;I2Q7KL*^Gc>LB&F8-{VIXcZ7Z~TS*?;Jp)biJ&ULTioI8e>e+FSj%KjnQ1OVLh@z zShw!;s?lg}1xScgk|e=e%f$WzOiaFsN%}zAAf)v#ZBH4Zl*S@L>sNZ27z01J^>!}5 zd_6{MoU@e-KyD!flu{_A`RUz1MgWnR1#dHh&t45e`*9Wfrh>vw2vu-4+e zFQo2TajEyr&n^{7L^_j52~2T~F@qvm;wTY>?3#n{K46T&IZJvb03amiwPvXhvS5-c zAjQn2;b4e)7T?)WLpV1=~W%vgx68Rv}Tw02UT zJ}U4 zLQHAYt5la4Y1(=o6#71sRgp0UXDz97c%OCyFvesIe6q+Fu7(|3Z(;rCHuKtBFETSV zMX)~G(U3MPG2(s7$)!1}wZ&3JD3odo+gbzGT3njqedrB9L{LfO(80aLnDXU~*K_N) zf5d@J``Gi>mpJ+HEaEe|L-d@ko}^Y^LX{DB z3$sha#d5lS0_!Y&6`hF;+1Os0Rq+@AYc1B=P5?3H z3>b}10}Hc@3}1R7V>fK)>to+y?M0XH`n#|2=pzp?{kOx!G;}5C%DckveQX18D*;Hv zxfG)fF$U6L+49xv`Qc5s^X}0J9(no!Cg0qPuX~IJQ!!o1SJQqa80##q%s`sv5?G5i z2HUh8oB0=?%pT|dCw|H9U4Nu8Un`vht=OwjzLJ!DZ1hyhfX6z53xq0+9ryb7{KXpA9-NWE&QS8Z36v0Jut)rO6$PW8hN zKVZ-Jt38>CK}!90<>U-RQs+=w7khpScZ))RjbGfvo!|d?kMNm2`1@b6XZ+PpwQ@A& zBipZJAQt<8CPtxI_n^9IJ$(2uzkc?HC_S^5KA99<@2Z52duLsNgrBk z7LK3pnc(?{#(AFcRRNsIv^ItIT>zrQyfiew_}SegNpi+dt+TNZJacm=x&ed`>~gJE z(b}-QT%%sEomGOeedi~mOfLMHnWrvgadENmuUWIEIFOW57C;B@_wAo}N4_$)je&uI zPn{fK--+k2p$h4A#1ILw(;xHko%gMNd%U%l`T03HGJT{ub!h6gg9qQ=tAPIPU%T!b zBO@dCYpt&W=|3kl^fZe7)4+vZU0000 + + resources/22x22/media-eject.png + resources/32x32/media-eject.png + + resources/22x22/media-playback-pause.png + resources/32x32/media-playback-pause.png + + resources/22x22/media-playback-start.png + resources/32x32/media-playback-start.png + + resources/22x22/media-playback-stop.png + resources/32x32/media-playback-stop.png + + resources/22x22/media-record.png + resources/32x32/media-record.png + + resources/22x22/media-seek-backward.png + resources/32x32/media-seek-backward.png + + resources/22x22/media-seek-forward.png + resources/32x32/media-seek-forward.png + + resources/22x22/media-skip-backward.png + resources/32x32/media-skip-backward.png + + resources/22x22/media-skip-forward.png + resources/32x32/media-skip-forward.png + + resources/22x22/utilities-terminal.png + resources/32x32/utilities-terminal.png + + diff --git a/ground/gcs/src/plugins/video/video.ui b/ground/gcs/src/plugins/video/video.ui new file mode 100644 index 0000000000..39acaff6a0 --- /dev/null +++ b/ground/gcs/src/plugins/video/video.ui @@ -0,0 +1,204 @@ + + + Form + + + + 0 + 0 + 400 + 572 + + + + Form + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + QTextEdit::NoWrap + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + + + + + + :/video/resources/22x22/media-playback-start.png + :/video/resources/22x22/media-playback-start.png:/video/resources/22x22/media-playback-start.png + + + + 22 + 22 + + + + + + + + + + + + :/video/resources/22x22/media-playback-pause.png + :/video/resources/22x22/media-playback-pause.png:/video/resources/22x22/media-playback-pause.png + + + + 22 + 22 + + + + + + + + true + + + + + + + :/video/resources/22x22/media-playback-stop.png + :/video/resources/22x22/media-playback-stop.png:/video/resources/22x22/media-playback-stop.png + + + + 22 + 22 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + true + + + + + + + :/video/resources/22x22/utilities-terminal.png + :/video/resources/22x22/utilities-terminal.png:/video/resources/22x22/utilities-terminal.png + + + + 22 + 22 + + + + + + + + + + + + + + + VideoWidget + QWidget +
videowidget.h
+ 1 +
+
+ + +
diff --git a/ground/gcs/src/plugins/video/videogadget.cpp b/ground/gcs/src/plugins/video/videogadget.cpp new file mode 100644 index 0000000000..57da2c582b --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadget.cpp @@ -0,0 +1,46 @@ +/** + ****************************************************************************** + * + * @file videogadget.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 "videogadgetconfiguration.h" +#include "videogadgetwidget.h" +#include "videogadget.h" + +VideoGadget::VideoGadget(QString classId, VideoGadgetWidget *widget, QWidget *parent) : + IUAVGadget(classId, parent), + m_widget(widget) +{} + +VideoGadget::~VideoGadget() +{ + delete m_widget; +} + +void VideoGadget::loadConfiguration(IUAVGadgetConfiguration *config) +{ + VideoGadgetConfiguration *m = qobject_cast(config); + + m_widget->setConfiguration(m); +} diff --git a/ground/gcs/src/plugins/video/videogadget.h b/ground/gcs/src/plugins/video/videogadget.h new file mode 100644 index 0000000000..a52d7118d2 --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadget.h @@ -0,0 +1,70 @@ +/** + ****************************************************************************** + * + * @file videogadget.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 + */ + +#ifndef VIDEOGADGET_H_ +#define VIDEOGADGET_H_ + +#include +#include "videogadgetwidget.h" + +namespace Core { +class IUAVGadget; +} + +class IUAVGadget; +class QWidget; +class QString; +class VideoGadgetWidget; + +using namespace Core; + +class VideoGadget : public Core::IUAVGadget { + Q_OBJECT +public: + VideoGadget(QString classId, VideoGadgetWidget *widget, QWidget *parent = 0); + ~VideoGadget(); + + QList context() const + { + return m_context; + } + QWidget *widget() + { + return m_widget; + } + void loadConfiguration(IUAVGadgetConfiguration *config); + QString contextHelpId() const + { + return QString(); + } + +private: + VideoGadgetWidget *m_widget; + QList m_context; +}; + +#endif // VIDEOGADGET_H_ diff --git a/ground/gcs/src/plugins/video/videogadgetconfiguration.cpp b/ground/gcs/src/plugins/video/videogadgetconfiguration.cpp new file mode 100644 index 0000000000..2b960dcd40 --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetconfiguration.cpp @@ -0,0 +1,73 @@ +/** + ****************************************************************************** + * + * @file videogadgetconfiguration.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 "videogadgetconfiguration.h" + +VideoGadgetConfiguration::VideoGadgetConfiguration(QString classId, QSettings &settings, QObject *parent) : + IUAVGadgetConfiguration(classId, parent) +{ + m_displayVideo = settings.value("displayVideo").toBool(); + m_autoStart = settings.value("autoStart").toBool(); + m_displayControls = settings.value("displayControls").toBool(); + m_respectAspectRatio = settings.value("respectAspectRatio").toBool(); + m_pipelineDesc = settings.value("pipelineDesc").toString(); + m_pipelineInfo = settings.value("pipelineInfo").toString(); +} + +VideoGadgetConfiguration::VideoGadgetConfiguration(const VideoGadgetConfiguration &obj) : + IUAVGadgetConfiguration(obj.classId(), obj.parent()) +{ + m_displayVideo = obj.m_displayVideo; + m_autoStart = obj.m_autoStart; + m_displayControls = obj.m_displayControls; + m_respectAspectRatio = obj.m_respectAspectRatio; + m_pipelineDesc = obj.m_pipelineDesc; + m_pipelineInfo = obj.m_pipelineInfo; +} + +/** + * Clones a configuration. + * + */ +IUAVGadgetConfiguration *VideoGadgetConfiguration::clone() const +{ + return new VideoGadgetConfiguration(*this); +} + +/** + * Saves a configuration. + * + */ +void VideoGadgetConfiguration::saveConfig(QSettings &settings) const +{ + settings.setValue("displayVideo", m_displayVideo); + settings.setValue("autoStart", m_autoStart); + settings.setValue("displayControls", m_displayControls); + settings.setValue("respectAspectRatio", m_respectAspectRatio); + settings.setValue("pipelineDesc", m_pipelineDesc); + settings.setValue("pipelineInfo", m_pipelineInfo); +} diff --git a/ground/gcs/src/plugins/video/videogadgetconfiguration.h b/ground/gcs/src/plugins/video/videogadgetconfiguration.h new file mode 100644 index 0000000000..f0695a729e --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetconfiguration.h @@ -0,0 +1,104 @@ +/** + ****************************************************************************** + * + * @file videogadgetconfiguration.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 + */ + +#ifndef VIDEOGADGETCONFIGURATION_H +#define VIDEOGADGETCONFIGURATION_H + +#include + +using namespace Core; + +class VideoGadgetConfiguration : public IUAVGadgetConfiguration { + Q_OBJECT +public: + explicit VideoGadgetConfiguration(QString classId, QSettings &Settings, QObject *parent = 0); + explicit VideoGadgetConfiguration(const VideoGadgetConfiguration &obj); + + IUAVGadgetConfiguration *clone() const; + void saveConfig(QSettings &settings) const; + + bool displayVideo() const + { + return m_displayVideo; + } + void setDisplayVideo(bool displayVideo) + { + m_displayVideo = displayVideo; + } + bool displayControls() const + { + return m_displayControls; + } + void setDisplayControls(bool displayControls) + { + m_displayControls = displayControls; + } + bool autoStart() const + { + return m_autoStart; + } + void setAutoStart(bool autoStart) + { + m_autoStart = autoStart; + } + bool respectAspectRatio() const + { + return m_respectAspectRatio; + } + void setRespectAspectRatio(bool respectAspectRatio) + { + m_respectAspectRatio = respectAspectRatio; + } + QString pipelineDesc() const + { + return m_pipelineDesc; + } + void setPipelineDesc(QString pipelineDesc) + { + m_pipelineDesc = pipelineDesc; + } + QString pipelineInfo() const + { + return m_pipelineInfo; + } + void setPipelineInfo(QString pipelineInfo) + { + m_pipelineInfo = pipelineInfo; + } + +private: + // video + bool m_displayVideo; + bool m_respectAspectRatio; + // controls + bool m_displayControls; + bool m_autoStart; + QString m_pipelineDesc; + QString m_pipelineInfo; +}; + +#endif // VIDEOGADGETCONFIGURATION_H diff --git a/ground/gcs/src/plugins/video/videogadgetfactory.cpp b/ground/gcs/src/plugins/video/videogadgetfactory.cpp new file mode 100644 index 0000000000..af88ced574 --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetfactory.cpp @@ -0,0 +1,57 @@ +/** + ****************************************************************************** + * + * @file videogadgetfactory.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 "videogadgetfactory.h" +#include "videogadgetwidget.h" +#include "videogadget.h" +#include "videogadgetconfiguration.h" +#include "videogadgetoptionspage.h" +#include +#include + +VideoGadgetFactory::VideoGadgetFactory(QObject *parent) : + IUAVGadgetFactory(QString("VideoGadget"), tr("Video"), parent) +{} + +VideoGadgetFactory::~VideoGadgetFactory() +{} + +Core::IUAVGadget *VideoGadgetFactory::createGadget(QWidget *parent) +{ + VideoGadgetWidget *gadgetWidget = new VideoGadgetWidget(parent); + + return new VideoGadget(QString("VideoGadget"), gadgetWidget, parent); +} + +IUAVGadgetConfiguration *VideoGadgetFactory::createConfiguration(QSettings &settings) +{ + return new VideoGadgetConfiguration(QString("VideoGadget"), settings); +} + +IOptionsPage *VideoGadgetFactory::createOptionsPage(IUAVGadgetConfiguration *config) +{ + return new VideoGadgetOptionsPage(qobject_cast(config)); +} diff --git a/ground/gcs/src/plugins/video/videogadgetfactory.h b/ground/gcs/src/plugins/video/videogadgetfactory.h new file mode 100644 index 0000000000..62a211ff4d --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetfactory.h @@ -0,0 +1,51 @@ +/** + ****************************************************************************** + * + * @file videogadgetfactory.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 + */ + +#ifndef VIDEOGADGETFACTORY_H_ +#define VIDEOGADGETFACTORY_H_ + +#include + +namespace Core { +class IUAVGadget; +class IUAVGadgetFactory; +} + +using namespace Core; + +class VideoGadgetFactory : public IUAVGadgetFactory { + Q_OBJECT +public: + VideoGadgetFactory(QObject *parent = 0); + ~VideoGadgetFactory(); + + IUAVGadget *createGadget(QWidget *parent); + IUAVGadgetConfiguration *createConfiguration(QSettings &settings); + IOptionsPage *createOptionsPage(IUAVGadgetConfiguration *config); +}; + +#endif // VIDEOGADGETFACTORY_H_ diff --git a/ground/gcs/src/plugins/video/videogadgetoptionspage.cpp b/ground/gcs/src/plugins/video/videogadgetoptionspage.cpp new file mode 100644 index 0000000000..c6aef2c9ca --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetoptionspage.cpp @@ -0,0 +1,82 @@ +/** + ****************************************************************************** + * + * @file videogadgetoptionspage.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 "videogadgetoptionspage.h" +#include "videogadgetconfiguration.h" +#include "helpdialog.h" + +#include "ui_videooptionspage.h" + +VideoGadgetOptionsPage::VideoGadgetOptionsPage(VideoGadgetConfiguration *config, QObject *parent) : + IOptionsPage(parent), m_config(config) +{ + m_page = 0; +} + +QWidget *VideoGadgetOptionsPage::createPage(QWidget *parent) +{ + m_page = new Ui::VideoOptionsPage(); + QWidget *w = new QWidget(parent); + m_page->setupUi(w); + + // TODO + m_page->respectAspectRatioCheckBox->setVisible(false); + m_page->helpButton->setVisible(false); + + m_page->displayVideoCheckBox->setChecked(m_config->displayVideo()); + m_page->displayControlsCheckBox->setChecked(m_config->displayControls()); + m_page->autoStartCheckBox->setChecked(m_config->autoStart()); + m_page->respectAspectRatioCheckBox->setChecked(m_config->respectAspectRatio()); + m_page->descPlainTextEdit->setPlainText(m_config->pipelineDesc()); + m_page->infoPlainTextEdit->setPlainText(m_config->pipelineInfo()); + + connect(m_page->helpButton, SIGNAL(clicked()), this, SLOT(openHelpDialog())); + + return w; +} + +void VideoGadgetOptionsPage::apply() +{ + m_config->setDisplayVideo(m_page->displayVideoCheckBox->isChecked()); + m_config->setDisplayControls(m_page->displayControlsCheckBox->isChecked()); + m_config->setAutoStart(m_page->autoStartCheckBox->isChecked()); + m_config->setRespectAspectRatio(m_page->respectAspectRatioCheckBox->isChecked()); + m_config->setPipelineDesc(m_page->descPlainTextEdit->toPlainText()); + m_config->setPipelineInfo(m_page->infoPlainTextEdit->toPlainText()); +} + +void VideoGadgetOptionsPage::finish() +{ + delete m_page; +} + +void VideoGadgetOptionsPage::openHelpDialog() +{ + HelpDialog dlg(0); + + dlg.execDialog(); +} diff --git a/ground/gcs/src/plugins/video/videogadgetoptionspage.h b/ground/gcs/src/plugins/video/videogadgetoptionspage.h new file mode 100644 index 0000000000..19c7d63dd5 --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetoptionspage.h @@ -0,0 +1,80 @@ +/** + ****************************************************************************** + * + * @file videogadgetoptionspage.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 + */ + +#ifndef VIDEOGADGETOPTIONSPAGE_H +#define VIDEOGADGETOPTIONSPAGE_H + +#include "coreplugin/dialogs/ioptionspage.h" + +#include +#include + +class VideoGadgetConfiguration; + +namespace Ui { +class VideoOptionsPage; +} + +using namespace Core; + +class VideoGadgetOptionsPage : public IOptionsPage { + Q_OBJECT +public: + explicit VideoGadgetOptionsPage(VideoGadgetConfiguration *config, QObject *parent = 0); + QString id() const + { + return ""; + } + QString trName() const + { + return ""; + } + QString category() const + { + return ""; + } + QString trCategory() const + { + return ""; + } + + QWidget *createPage(QWidget *parent); + void apply(); + void finish(); + +// private signals: + +// public slots: +private slots: + void openHelpDialog(); + +private: + VideoGadgetConfiguration *m_config; + Ui::VideoOptionsPage *m_page; +}; + +#endif // VIDEOGADGETOPTIONSPAGE_H diff --git a/ground/gcs/src/plugins/video/videogadgetwidget.cpp b/ground/gcs/src/plugins/video/videogadgetwidget.cpp new file mode 100644 index 0000000000..39260b3798 --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetwidget.cpp @@ -0,0 +1,170 @@ +/** + ****************************************************************************** + * + * @file videogadgetwidget.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 "videogadgetconfiguration.h" +#include "videogadgetwidget.h" +#include "pipeline.h" + +#include +#include +#include +#include +#include +#include + +VideoGadgetWidget::VideoGadgetWidget(QWidget *parent) : + QFrame(parent) +{ + m_ui = new Ui_Form(); + m_ui->setupUi(this); + + m_ui->consoleTextBrowser->setVisible(false); + + connect(videoWidget(), &VideoWidget::stateChanged, this, &VideoGadgetWidget::onStateChanged); + connect(videoWidget(), &VideoWidget::message, this, &VideoGadgetWidget::msg); + + connect(m_ui->startButton, &QPushButton::clicked, this, &VideoGadgetWidget::start); + connect(m_ui->pauseButton, &QPushButton::clicked, this, &VideoGadgetWidget::pause); + connect(m_ui->stopButton, &QPushButton::clicked, this, &VideoGadgetWidget::stop); + + connect(m_ui->consoleButton, &QPushButton::clicked, this, &VideoGadgetWidget::console); + + onStateChanged(Pipeline::Null, Pipeline::Null, Pipeline::Null); +} + +VideoGadgetWidget::~VideoGadgetWidget() +{ + m_ui = 0; +} + +void VideoGadgetWidget::setConfiguration(VideoGadgetConfiguration *config) +{ + videoWidget()->setVisible(config->displayVideo()); + // m_ui->control->setEnabled(config->displayControls()); + bool restart = false; + if (videoWidget()->pipelineDesc() != config->pipelineDesc()) { + if (videoWidget()->isPlaying()) { + restart = true; + stop(); + } + msg(QString("setting pipeline %0").arg(config->pipelineDesc())); + videoWidget()->setPipelineDesc(config->pipelineDesc()); + } + if (restart || (!videoWidget()->isPlaying() && config->autoStart())) { + start(); + } +} + +void VideoGadgetWidget::start() +{ + msg(QString("starting...")); + m_ui->startButton->setEnabled(false); + videoWidget()->start(); +} + +void VideoGadgetWidget::pause() +{ + msg(QString("pausing...")); + m_ui->pauseButton->setEnabled(false); + videoWidget()->pause(); +} + +void VideoGadgetWidget::stop() +{ + msg(QString("stopping...")); + videoWidget()->stop(); +} + +void VideoGadgetWidget::console() +{ + m_ui->consoleTextBrowser->setVisible(!m_ui->consoleTextBrowser->isVisible()); +} + +void VideoGadgetWidget::onStateChanged(Pipeline::State oldState, Pipeline::State newState, Pipeline::State pendingState) +{ + Q_UNUSED(oldState); + + // msg(QString("state changed: ") + VideoWidget::name(oldState) + " -> " + VideoWidget::name(newState) + " / " + VideoWidget::name(pendingState)); + + bool startEnabled = true; + bool pauseEnabled = true; + bool stopEnabled = true; + + bool startVisible = false; + bool pauseVisible = false; + bool stopVisible = true; + + switch (newState) { + case Pipeline::Ready: + // start & !stop + startVisible = true; + stopEnabled = false; + break; + case Pipeline::Paused: + if (pendingState == Pipeline::Playing) { + // !pause & stop + pauseVisible = true; + pauseEnabled = false; + } else if (pendingState == Pipeline::Ready) { + // start & !stop + startVisible = true; + stopEnabled = false; + } else { + // start & stop + startVisible = true; + } + break; + case Pipeline::Playing: + // pause & stop + pauseVisible = true; + break; + default: + // start & !stop + startVisible = true; + stopEnabled = false; + break; + } + m_ui->startButton->setVisible(startVisible); + m_ui->startButton->setEnabled(startEnabled); + + m_ui->pauseButton->setVisible(pauseVisible); + m_ui->pauseButton->setEnabled(pauseEnabled); + + m_ui->stopButton->setVisible(stopVisible); + m_ui->stopButton->setEnabled(stopEnabled); +} + +void VideoGadgetWidget::msg(const QString &str) +{ + if (m_ui) { + m_ui->consoleTextBrowser->append(str); + } +} + +VideoWidget *VideoGadgetWidget::videoWidget() +{ + return m_ui->video; +} diff --git a/ground/gcs/src/plugins/video/videogadgetwidget.h b/ground/gcs/src/plugins/video/videogadgetwidget.h new file mode 100644 index 0000000000..a4ba207147 --- /dev/null +++ b/ground/gcs/src/plugins/video/videogadgetwidget.h @@ -0,0 +1,68 @@ +/** + ****************************************************************************** + * + * @file videogadgetwidget.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 + */ + +#ifndef VIDEOGADGETWIDGET_H_ +#define VIDEOGADGETWIDGET_H_ + +#include "pipeline.h" +#include "ui_video.h" + +#include +#include +#include +#include + +class VideoWidget; +class VideoGadgetConfiguration; + +class VideoGadgetWidget : public QFrame { + Q_OBJECT + +public: + VideoGadgetWidget(QWidget *parent = 0); + ~VideoGadgetWidget(); + + void setConfiguration(VideoGadgetConfiguration *config); + +private slots: + void start(); + void pause(); + void stop(); + void console(); + + void onStateChanged(Pipeline::State oldState, Pipeline::State newState, Pipeline::State pendingState); + +private: + Ui_Form *m_ui; + VideoGadgetConfiguration *config; + + void msg(const QString &str); + + VideoWidget *videoWidget(); +}; + +#endif /* VIDEOGADGETWIDGET_H_ */ diff --git a/ground/gcs/src/plugins/video/videooptionspage.ui b/ground/gcs/src/plugins/video/videooptionspage.ui new file mode 100644 index 0000000000..67a97dc80e --- /dev/null +++ b/ground/gcs/src/plugins/video/videooptionspage.ui @@ -0,0 +1,96 @@ + + + VideoOptionsPage + + + + 0 + 0 + 378 + 300 + + + + Form + + + + 0 + + + + + Pipeline: + + + + + + + + + + Display video + + + + + + + Display controls + + + + + + + + + + Info: + + + + + + + Auto Start + + + + + + + Respect aspect ratio + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Help + + + + + + + + + + diff --git a/ground/gcs/src/plugins/video/videoplugin.cpp b/ground/gcs/src/plugins/video/videoplugin.cpp new file mode 100644 index 0000000000..d629be3ffe --- /dev/null +++ b/ground/gcs/src/plugins/video/videoplugin.cpp @@ -0,0 +1,64 @@ +/** + ****************************************************************************** + * + * @file videoplugin.cpp + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 "videoplugin.h" +#include "videogadgetfactory.h" + +#include + +#include +#include +#include + +VideoPlugin::VideoPlugin() +{ + // Do nothing +} + +VideoPlugin::~VideoPlugin() +{ + // Do nothing +} + +bool VideoPlugin::initialize(const QStringList & args, QString *errMsg) +{ + Q_UNUSED(args); + Q_UNUSED(errMsg); + mf = new VideoGadgetFactory(this); + addAutoReleasedObject(mf); + + return true; +} + +void VideoPlugin::extensionsInitialized() +{ + // Do nothing +} + +void VideoPlugin::shutdown() +{ + // Do nothing +} diff --git a/ground/gcs/src/plugins/video/videoplugin.h b/ground/gcs/src/plugins/video/videoplugin.h new file mode 100644 index 0000000000..04c6e005c5 --- /dev/null +++ b/ground/gcs/src/plugins/video/videoplugin.h @@ -0,0 +1,51 @@ +/** + ****************************************************************************** + * + * @file videoplugin.h + * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup VideoGadgetPlugin Video Gadget Plugin + * @{ + * @brief A video gadget plugin + *****************************************************************************/ +/* + * 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 + */ + +#ifndef VIDEOPLUGIN_H_ +#define VIDEOPLUGIN_H_ + +#include + +class VideoGadgetFactory; + +class VideoPlugin : public ExtensionSystem::IPlugin { + Q_OBJECT + Q_PLUGIN_METADATA(IID "OpenPilot.Video") +public: + + VideoPlugin(); + ~VideoPlugin(); + + void extensionsInitialized(); + bool initialize(const QStringList &arguments, QString *errorString); + void shutdown(); + +private: + VideoGadgetFactory *mf; +}; + +#endif /* VIDEOPLUGIN_H_ */ diff --git a/ground/gcs/src/share/configurations/default.xml b/ground/gcs/src/share/configurations/default.xml index e4e84b442b..29eb0f7bbf 100644 --- a/ground/gcs/src/share/configurations/default.xml +++ b/ground/gcs/src/share/configurations/default.xml @@ -2785,6 +2785,128 @@ + + + + false + 0.0.0 + + + true + false + true + videotestsrc ! autovideosink + + false + + + + + false + 0.0.0 + + + false + false + true + dx9screencapsrc monitor=0 cursor=true ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + dx9screencapsrc monitor=0 cursor=true ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + dx9screencapsrc monitor=0 cursor=true x=0 y=0 width=640 height=480 ! autovideosink + + false + + + + + false + 0.0.0 + + + true + false + true + dx9screencapsrc monitor=0 cursor=true ! autovideosink + + false + + + + + false + 0.0.0 + + + false + false + true + ksvideosrc device-index=0 ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + ksvideosrc device-index=0 ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + true + false + true + ksvideosrc device-index=0 ! autovideosink + + false + + + false 1.2.0 From 47354f3b9ab5a37f997cd6c9351fd09d5a32a8f3 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Wed, 4 Jan 2017 22:05:24 +0100 Subject: [PATCH 02/18] LP-109 fix long video pauses with rtsp protocol over wifi workaround https://bugreports.qt.io/browse/QTBUG-40332 --- ground/gcs/src/app/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ground/gcs/src/app/main.cpp b/ground/gcs/src/app/main.cpp index 734edb7a7a..b5a42b1ca0 100644 --- a/ground/gcs/src/app/main.cpp +++ b/ground/gcs/src/app/main.cpp @@ -280,6 +280,10 @@ void systemInit() QSurfaceFormat format = QSurfaceFormat::defaultFormat(); format.setSwapInterval(0); QSurfaceFormat::setDefaultFormat(format); + + // see https://bugreports.qt.io/browse/QTBUG-40332 + int timeout = std::numeric_limits::max(); + qputenv("QT_BEARER_POLL_TIMEOUT", QString::number(timeout).toLatin1()); } static FileLogger *logger = NULL; From f8327c2d90a322f48c7b6590e704342962d870fa Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Wed, 5 Jul 2017 23:48:36 +0200 Subject: [PATCH 03/18] LP-109 video gadget: add gstreamer to About credits --- ground/gcs/src/plugins/coreplugin/aboutdialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp b/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp index 21c90ae7f0..841dd6b579 100644 --- a/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp +++ b/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp @@ -82,6 +82,7 @@ AboutDialog::AboutDialog(QWidget *parent) : + creditRow.arg("dRonin", "", "http://www.dronin.org") + creditRow.arg("OpenSceneGraph", "
Open source high performance 3D graphics toolkit", "http://www.openscenegraph.org") + creditRow.arg("osgEarth", "
Geospatial SDK for OpenSceneGraph", "http://osgearth.org") + + creditRow.arg("gstreamer", "
Open source multimedia framework", "https://gstreamer.freedesktop.org/") + creditRow.arg("MSYS2", "
An independent rewrite of MSYS", "https://sourceforge.net/p/msys2/wiki/Home") + creditRow.arg("The Qt Company", "", "http://www.qt.io") + ""; From 8f17f14400d1433f709d8d1dd456bde8c697c5b9 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Wed, 5 Jul 2017 23:49:17 +0200 Subject: [PATCH 04/18] LP-109 about credits: fix URLs of Qt and msys2 --- ground/gcs/src/plugins/coreplugin/aboutdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp b/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp index 841dd6b579..0d0bdb1419 100644 --- a/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp +++ b/ground/gcs/src/plugins/coreplugin/aboutdialog.cpp @@ -83,8 +83,8 @@ AboutDialog::AboutDialog(QWidget *parent) : + creditRow.arg("OpenSceneGraph", "
Open source high performance 3D graphics toolkit", "http://www.openscenegraph.org") + creditRow.arg("osgEarth", "
Geospatial SDK for OpenSceneGraph", "http://osgearth.org") + creditRow.arg("gstreamer", "
Open source multimedia framework", "https://gstreamer.freedesktop.org/") - + creditRow.arg("MSYS2", "
An independent rewrite of MSYS", "https://sourceforge.net/p/msys2/wiki/Home") - + creditRow.arg("The Qt Company", "", "http://www.qt.io") + + creditRow.arg("MSYS2", "
An independent rewrite of MSYS", "https://github.com/msys2/msys2/wiki") + + creditRow.arg("The Qt Company", "", "https://www.qt.io") + ""; // uses Text.StyledText (see http://doc.qt.io/qt-5/qml-qtquick-text.html#textFormat-prop) From 6929e3407ba19d1fa35d43f227282ec7278971af Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Wed, 5 Jul 2017 23:55:20 +0200 Subject: [PATCH 05/18] LP-109 cleanup copydata.pro files to bring them up to par with gstreamer's copydata.pro file --- ground/gcs/copydata.pro | 2 +- ground/gcs/src/libs/osgearth/copydata.pro | 32 ++++++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ground/gcs/copydata.pro b/ground/gcs/copydata.pro index c0133a7d84..ca7c8ae478 100644 --- a/ground/gcs/copydata.pro +++ b/ground/gcs/copydata.pro @@ -91,7 +91,7 @@ win32 { for(dll, QT_DLLS) { addCopyFileTarget($${dll},$$[QT_INSTALL_BINS],$${GCS_APP_PATH}) - win32:addCopyDependenciesTarget($${dll},$$[QT_INSTALL_BINS],$${GCS_APP_PATH}) + addCopyDependenciesTarget($${dll},$$[QT_INSTALL_BINS],$${GCS_APP_PATH}) } # copy OpenSSL DLLs diff --git a/ground/gcs/src/libs/osgearth/copydata.pro b/ground/gcs/src/libs/osgearth/copydata.pro index 89e0187267..294471afe1 100644 --- a/ground/gcs/src/libs/osgearth/copydata.pro +++ b/ground/gcs/src/libs/osgearth/copydata.pro @@ -13,24 +13,24 @@ contains(QT_ARCH, x86_64) { # set debug suffix if needed win32:CONFIG(debug, debug|release):DS = "d" -osg:linux { +linux:osg { # copy osg libraries data_copy.commands += $(MKDIR) $$GCS_LIBRARY_PATH/osg $$addNewline() data_copy.commands += $(COPY_DIR) $$shell_quote($$OSG_SDK_DIR/$$LIB_DIR_NAME/)* $$shell_quote($$GCS_LIBRARY_PATH/osg/) $$addNewline() } -osgearth:linux { +linux:osgearth { # copy osgearth libraries data_copy.commands += $(MKDIR) $$GCS_LIBRARY_PATH/osg $$addNewline() data_copy.commands += $(COPY_DIR) $$shell_quote($$OSGEARTH_SDK_DIR/$$LIB_DIR_NAME/)* $$shell_quote($$GCS_LIBRARY_PATH/osg/) $$addNewline() } -osg:macx { +macx:osg { # copy osg libraries data_copy.commands += $(COPY_DIR) $$shell_quote($$OSG_SDK_DIR/lib/)* $$shell_quote($$GCS_LIBRARY_PATH/) $$addNewline() } -osgearth:macx { +macx:osgearth { # copy osgearth libraries data_copy.commands += $(COPY_DIR) $$shell_quote($$OSGEARTH_SDK_DIR/lib/)* $$shell_quote($$GCS_LIBRARY_PATH/) $$addNewline() } @@ -43,8 +43,8 @@ linux|macx { QMAKE_EXTRA_TARGETS += data_copy } -osg:win32 { - # osg and osgearth dependencies +win32:osg { + OSG_PLUGINS_DIR = $${OSG_SDK_DIR}/bin/osgPlugins-$${OSG_VERSION} # osg libraries OSG_LIBS += \ @@ -71,7 +71,7 @@ osg:win32 { for(lib, OSG_LIBS) { addCopyFileTarget($${lib},$${OSG_SDK_DIR}/bin,$${GCS_APP_PATH}) - win32:addCopyDependenciesTarget($${lib},$${OSG_SDK_DIR}/bin,$${GCS_APP_PATH}) + addCopyDependenciesTarget($${lib},$${OSG_SDK_DIR}/bin,$${GCS_APP_PATH}) } # osg plugins @@ -85,7 +85,8 @@ osg:win32 { mingw_osgdb_zip$${DS}.dll \ mingw_osgdb_serializers_osg$${DS}.dll - osg_extra:OSG_PLUGINS = \ + # more osg plugins + osg_more_plugins:OSG_PLUGINS = \ mingw_osgdb_3dc$${DS}.dll \ mingw_osgdb_ac$${DS}.dll \ mingw_osgdb_bmp$${DS}.dll \ @@ -151,12 +152,12 @@ osg:win32 { mingw_osgdb_serializers_osgvolume$${DS}.dll for(lib, OSG_PLUGINS) { - addCopyFileTarget($${lib},$${OSG_SDK_DIR}/bin/osgPlugins-$${OSG_VERSION},$${GCS_LIBRARY_PATH}/osg/osgPlugins-$${OSG_VERSION}) - win32:addCopyDependenciesTarget($${lib},$${OSG_SDK_DIR}/bin/osgPlugins-$${OSG_VERSION},$${GCS_APP_PATH}) + addCopyFileTarget($${lib},$${OSG_PLUGINS_DIR},$${GCS_LIBRARY_PATH}/osg/osgPlugins-$${OSG_VERSION}) + addCopyDependenciesTarget($${lib},$${OSG_PLUGINS_DIR},$${GCS_APP_PATH}) } } -osgearth:win32 { +win32:osgearth { # osgearth libraries OSGEARTH_LIBS = \ libosgEarth$${DS}.dll \ @@ -174,7 +175,7 @@ osgearth:win32 { for(lib, OSGEARTH_LIBS) { addCopyFileTarget($${lib},$${OSGEARTH_SDK_DIR}/bin,$${GCS_APP_PATH}) - win32:addCopyDependenciesTarget($${lib},$${OSGEARTH_SDK_DIR}/bin,$${GCS_APP_PATH}) + addCopyDependenciesTarget($${lib},$${OSGEARTH_SDK_DIR}/bin,$${GCS_APP_PATH}) } # osgearth plugins @@ -187,7 +188,8 @@ osgearth:win32 { mingw_osgdb_osgearth_xyz$${DS}.dll \ mingw_osgdb_osgearth_cache_filesystem$${DS}.dll - osgearth_extra:OSGEARTH_PLUGINS += \ + # more osgearth plugins + more_osgearth_plugins:OSGEARTH_PLUGINS += \ mingw_osgdb_kml$${DS}.dll \ mingw_osgdb_osgearth_agglite$${DS}.dll \ mingw_osgdb_osgearth_arcgis_map_cache$${DS}.dll \ @@ -225,7 +227,7 @@ osgearth:win32 { mingw_osgdb_osgearth_yahoo$${DS}.dll for(lib, OSGEARTH_PLUGINS) { - addCopyFileTarget($${lib},$${OSGEARTH_SDK_DIR}/bin/osgPlugins-$${OSG_VERSION},$${GCS_LIBRARY_PATH}/osg/osgPlugins-$${OSG_VERSION}) - win32:addCopyDependenciesTarget($${lib},$${OSGEARTH_SDK_DIR}/bin/osgPlugins-$${OSG_VERSION},$${GCS_APP_PATH}) + addCopyFileTarget($${lib},$${OSG_PLUGINS_DIR},$${GCS_LIBRARY_PATH}/osg/osgPlugins-$${OSG_VERSION}) + addCopyDependenciesTarget($${lib},$${OSG_PLUGINS_DIR},$${GCS_APP_PATH}) } } From e30340237f4eed4a66ecb99ffb658ebd0e59c21d Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Wed, 5 Jul 2017 23:55:07 +0200 Subject: [PATCH 06/18] LP-109 video gadget: add Mac support credits go to Ben Meng for making the video gadget work on Mac --- .../libs/gstreamer/gstreamer_dependencies.pri | 23 +++++++++++++++++++ ground/gcs/src/libs/gstreamer/readme.txt | 2 ++ ground/gcs/src/libs/gstreamer/videowidget.cpp | 5 +++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri b/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri index 9e307a556a..f99e33b9f0 100644 --- a/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri +++ b/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri @@ -1,6 +1,29 @@ DEFINES += USE_GSTREAMER opencv:DEFINES += USE_OPENCV +macx { + GLIB_DIR = $$system(brew --prefix glib) + GSTREAMER_DIR = $$system(brew --prefix gstreamer) + GST_BASE_DIR = $$system(brew --prefix gst-plugins-base) + + message(Using glib from here: $$GLIB_DIR) + message(Using gstreamer from here: $$GSTREAMER_DIR) + message(Using gst base from here: $GST_BASE_DIR) + + INCLUDEPATH += $$GLIB_DIR/include/glib-2.0 + INCLUDEPATH += $$GLIB_DIR/lib/glib-2.0/include + + INCLUDEPATH += $$GST_BASE_DIR/include/gstreamer-1.0/ + INCLUDEPATH += $$GSTREAMER_DIR/include/gstreamer-1.0 + INCLUDEPATH += $$GSTREAMER_DIR/lib/gstreamer-1.0/include + + LIBS +=-L$$GLIB_DIR/lib + LIBS += -L$$GSTREAMER_DIR/lib -L$$GST_BASE_DIR/lib + + LIBS += -lglib-2.0 -lgobject-2.0 + LIBS += -lgstreamer-1.0 -lgstapp-1.0 -lgstpbutils-1.0 -lgstvideo-1.0 +} + linux|win32 { CONFIG += link_pkgconfig PKGCONFIG += glib-2.0 gobject-2.0 diff --git a/ground/gcs/src/libs/gstreamer/readme.txt b/ground/gcs/src/libs/gstreamer/readme.txt index f351173dbd..94f6d9b623 100644 --- a/ground/gcs/src/libs/gstreamer/readme.txt +++ b/ground/gcs/src/libs/gstreamer/readme.txt @@ -39,6 +39,8 @@ Upgrade to latest version of the packages using Synaptic Package Manager or CLI # Mac ############################################################################### +brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav libav x264 + ############################################################################### # How to find required libraries (for copydata.pro) ############################################################################### diff --git a/ground/gcs/src/libs/gstreamer/videowidget.cpp b/ground/gcs/src/libs/gstreamer/videowidget.cpp index 7bba53db25..74488f972b 100644 --- a/ground/gcs/src/libs/gstreamer/videowidget.cpp +++ b/ground/gcs/src/libs/gstreamer/videowidget.cpp @@ -91,7 +91,10 @@ VideoWidget::VideoWidget(QWidget *parent) : // make the widget native so it gets its own native window id that we will pass to gstreamer setAttribute(Qt::WA_NativeWindow); - // setAttribute(Qt::WA_DontCreateNativeAncestors); +#ifdef Q_OS_MAC + // WA_DontCreateNativeAncestors is needed on mac + setAttribute(Qt::WA_DontCreateNativeAncestors); +#endif // set black background QPalette pal(palette()); From 05112f9c1b80e20cde64a56bc5eb9ab80292834e Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Tue, 5 Jul 2016 23:37:28 +0200 Subject: [PATCH 07/18] LP-360 add video background to PFD --- .../libs/gstreamer/gstreamer_dependencies.pri | 2 +- .../libs/osgearth/osgQtQuick/OSGImageNode.cpp | 130 ++++-- .../libs/osgearth/osgQtQuick/OSGImageNode.hpp | 13 +- .../osgQtQuick/ga/OSGCameraManipulator.cpp | 2 +- ground/gcs/src/libs/osgearth/osgearth.pro | 14 +- .../libs/osgearth/osgearth_dependencies.pri | 5 + .../utils/gstreamer/gstimagesource.cpp | 90 ++++ .../utils/gstreamer/gstimagesource.hpp | 52 +++ .../utils/gstreamer/gstimagestream.cpp | 384 ++++++++++++++++++ .../utils/gstreamer/gstimagestream.hpp | 70 ++++ .../src/libs/osgearth/utils/imagesource.cpp | 40 ++ .../src/libs/osgearth/utils/imagesource.hpp | 47 +++ .../gcs/src/plugins/pfdqml/pfdqmlcontext.cpp | 16 + ground/gcs/src/plugins/pfdqml/pfdqmlcontext.h | 11 + .../pfdqml/pfdqmlgadgetconfiguration.cpp | 10 + .../pfdqml/pfdqmlgadgetconfiguration.h | 11 + .../pfdqml/pfdqmlgadgetoptionspage.cpp | 5 + .../plugins/pfdqml/pfdqmlgadgetoptionspage.ui | 43 +- .../gcs/src/share/configurations/default.xml | 25 ++ ground/gcs/src/share/qml/PfdVideo.qml | 27 ++ ground/gcs/src/share/qml/model/ModelView.qml | 2 +- .../gcs/src/share/qml/pfd/PfdVideoWorld.qml | 149 +++++++ 22 files changed, 1102 insertions(+), 46 deletions(-) create mode 100644 ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.cpp create mode 100644 ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.hpp create mode 100644 ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.cpp create mode 100644 ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.hpp create mode 100644 ground/gcs/src/libs/osgearth/utils/imagesource.cpp create mode 100644 ground/gcs/src/libs/osgearth/utils/imagesource.hpp create mode 100644 ground/gcs/src/share/qml/PfdVideo.qml create mode 100644 ground/gcs/src/share/qml/pfd/PfdVideoWorld.qml diff --git a/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri b/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri index f99e33b9f0..e8112015d9 100644 --- a/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri +++ b/ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri @@ -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 } diff --git a/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp b/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp index ab138d9795..187efb9310 100644 --- a/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp +++ b/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp @@ -27,9 +27,16 @@ #include "OSGImageNode.hpp" -#include +#include "utils/imagesource.hpp" + +#ifdef USE_GSTREAMER +#include "utils/gstreamer/gstimagesource.hpp" +#endif -#include +#include +#include +#include +#include #include #include @@ -45,44 +52,107 @@ struct OSGImageNode::Hidden : public QObject { osg::ref_ptr texture; + ImageSource *imageSource; + public: - QUrl url; + QUrl imageUrl; - Hidden(OSGImageNode *self) : QObject(self), self(self), url() - {} + 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()); + osg::Geode *geode = new osg::Geode; return geode; } - osg::StateSet *createState() + osg::Image *loadImage() { - texture = new osg::Texture2D; + 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() + { + update(); + } - // create the StateSet to store the texture data - osg::StateSet *stateset = new osg::StateSet; + void update() + { + osg::Image *image = loadImage(); - stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); + // qDebug() << "OSGImageNode::update" << image; + osg::Node *geode = createGeodeForImage(image); - return stateset; + self->setNode(geode); } - void updateImageFile() + osg::Geode *createGeodeForImage(osg::Image *image) { - qDebug() << "OSGImageNode::updateImageFile - reading image file" << url.path(); - osg::Image *image = osgDB::readImageFile(url.path().toStdString()); - if (texture.valid()) { - texture->setImage(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); } } diff --git a/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.hpp b/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.hpp index f543a0b542..724e726200 100644 --- a/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.hpp +++ b/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.hpp @@ -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 @@ class OSGQTQUICK_EXPORT OSGImageNode : public OSGNode { 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 @@ class OSGQTQUICK_EXPORT OSGImageNode : public OSGNode { Hidden *const h; }; } // namespace osgQtQuick - -#endif // _H_OSGQTQUICK_IMAGENODE_H_ diff --git a/ground/gcs/src/libs/osgearth/osgQtQuick/ga/OSGCameraManipulator.cpp b/ground/gcs/src/libs/osgearth/osgQtQuick/ga/OSGCameraManipulator.cpp index f97098bb84..56e68e76a3 100644 --- a/ground/gcs/src/libs/osgearth/osgQtQuick/ga/OSGCameraManipulator.cpp +++ b/ground/gcs/src/libs/osgearth/osgQtQuick/ga/OSGCameraManipulator.cpp @@ -59,7 +59,7 @@ struct OSGCameraManipulator::Hidden : public QObject, public DirtySupport { osg::Node *nodeToUpdate() const { - return manipulator->getNode(); + return manipulator ? manipulator->getNode() : NULL; } void update() diff --git a/ground/gcs/src/libs/osgearth/osgearth.pro b/ground/gcs/src/libs/osgearth/osgearth.pro index 8780eafd2d..0573a07328 100644 --- a/ground/gcs/src/libs/osgearth/osgearth.pro +++ b/ground/gcs/src/libs/osgearth/osgearth.pro @@ -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 diff --git a/ground/gcs/src/libs/osgearth/osgearth_dependencies.pri b/ground/gcs/src/libs/osgearth/osgearth_dependencies.pri index 71092882e0..964568f378 100644 --- a/ground/gcs/src/libs/osgearth/osgearth_dependencies.pri +++ b/ground/gcs/src/libs/osgearth/osgearth_dependencies.pri @@ -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) diff --git a/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.cpp b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.cpp new file mode 100644 index 0000000000..0c77b1f6d8 --- /dev/null +++ b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.cpp @@ -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 + +#include +#include + +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); + } +} diff --git a/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.hpp b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.hpp new file mode 100644 index 0000000000..2b6977d91b --- /dev/null +++ b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagesource.hpp @@ -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; +}; diff --git a/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.cpp b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.cpp new file mode 100644 index 0000000000..beda0c499e --- /dev/null +++ b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.cpp @@ -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 + * @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 + +#include + +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 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); +} diff --git a/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.hpp b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.hpp new file mode 100644 index 0000000000..b75868920b --- /dev/null +++ b/ground/gcs/src/libs/osgearth/utils/gstreamer/gstimagestream.hpp @@ -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 + * @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 + +#include + +#include + +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; +}; diff --git a/ground/gcs/src/libs/osgearth/utils/imagesource.cpp b/ground/gcs/src/libs/osgearth/utils/imagesource.cpp new file mode 100644 index 0000000000..f30737894c --- /dev/null +++ b/ground/gcs/src/libs/osgearth/utils/imagesource.cpp @@ -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 +#include + +#include + +osg::Image *ImageSource::createImage(QUrl &url) +{ + qDebug() << "ImageSource::createImage - reading image file" << url.path(); + osg::Image *image = osgDB::readImageFile(url.path().toStdString()); + return image; +} diff --git a/ground/gcs/src/libs/osgearth/utils/imagesource.hpp b/ground/gcs/src/libs/osgearth/utils/imagesource.hpp new file mode 100644 index 0000000000..3d9d3b4361 --- /dev/null +++ b/ground/gcs/src/libs/osgearth/utils/imagesource.hpp @@ -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 + +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) {} +}; diff --git a/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.cpp b/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.cpp index 45a6062513..4ff9e814c1 100644 --- a/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.cpp +++ b/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.cpp @@ -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()); } diff --git a/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.h b/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.h index dd1cc1e2fa..951f20c30a 100644 --- a/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.h +++ b/ground/gcs/src/plugins/pfdqml/pfdqmlcontext.h @@ -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 @@ class PfdQmlContext : public QObject { 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 @@ class PfdQmlContext : public QObject { void modelFileChanged(QString arg); void backgroundImageFileChanged(QString arg); + void gstPipelineChanged(QString arg); + private: // constants static const QString CONTEXT_PROPERTY_NAME; @@ -156,6 +165,8 @@ class PfdQmlContext : public QObject { QString m_backgroundImageFile; + QString m_gstPipeline; + void addModelDir(QString dir); }; #endif /* PFDQMLCONTEXT_H_ */ diff --git a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.cpp b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.cpp index ca24f28f7f..f5d371d5fa 100644 --- a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.cpp +++ b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.cpp @@ -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); } diff --git a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.h b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.h index 538f36acf5..2165ed047a 100644 --- a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.h +++ b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetconfiguration.h @@ -200,6 +200,15 @@ class PfdQmlGadgetConfiguration : public IUAVGadgetConfiguration { m_backgroundImageFile = fileName; } + QString gstPipeline() const + { + return m_gstPipeline; + } + void setGstPipeline(const QString &pipeline) + { + m_gstPipeline = pipeline; + } + QMapIterator speedMapIterator() { return QMapIterator(m_speedMap); @@ -234,6 +243,8 @@ class PfdQmlGadgetConfiguration : public IUAVGadgetConfiguration { QString m_backgroundImageFile; + QString m_gstPipeline; + QMap m_speedMap; QMap m_altitudeMap; }; diff --git a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.cpp b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.cpp index af7d5a7dbe..bf73ca8929 100644 --- a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.cpp +++ b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.cpp @@ -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() diff --git a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.ui b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.ui index 576752a964..8d4cf98059 100644 --- a/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.ui +++ b/ground/gcs/src/plugins/pfdqml/pfdqmlgadgetoptionspage.ui @@ -138,7 +138,7 @@ 0 - + Terrain @@ -295,7 +295,7 @@ - + Model @@ -391,7 +391,7 @@ - + Environment @@ -511,6 +511,43 @@ + + + Video + + + + + + Pipeline: + + + + + + + + + + <enter your gstreamer pipeline here> + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + diff --git a/ground/gcs/src/share/configurations/default.xml b/ground/gcs/src/share/configurations/default.xml index 29eb0f7bbf..bc12410ae4 100644 --- a/ground/gcs/src/share/configurations/default.xml +++ b/ground/gcs/src/share/configurations/default.xml @@ -1665,6 +1665,31 @@ %%DATAPATH%%backgrounds/default_background.png + + + false + 0.0.0 + + + %%DATAPATH%%qml/PfdVideo.qml + 1 + 1 + true + %%DATAPATH%%osgearth/arcgis.earth + false + 39.6576 + 19.8046 + 90 + 0 + @Variant(AAAAEAAlfhEClAez/w==) + 0.50 + false + 1 + %%DATAPATH%%models/multi/test_quad/test_quad_x.3ds + %%DATAPATH%%backgrounds/default_background.png + ksvideosrc device-index=0 ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true + + false diff --git a/ground/gcs/src/share/qml/PfdVideo.qml b/ground/gcs/src/share/qml/PfdVideo.qml new file mode 100644 index 0000000000..e908fcca06 --- /dev/null +++ b/ground/gcs/src/share/qml/PfdVideo.qml @@ -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 . + */ +import QtQuick 2.4 + +import "pfd" + +PfdView { + opaque: false + worldFile: "PfdVideoWorld.qml" +} diff --git a/ground/gcs/src/share/qml/model/ModelView.qml b/ground/gcs/src/share/qml/model/ModelView.qml index 4dbf9566a4..529da8c0c8 100644 --- a/ground/gcs/src/share/qml/model/ModelView.qml +++ b/ground/gcs/src/share/qml/model/ModelView.qml @@ -55,7 +55,7 @@ Item { OSGImageNode { id: backgroundImageNode - imageFile: pfdContext.backgroundImageFile + imageUrl: pfdContext.backgroundImageFile } OSGTransformNode { diff --git a/ground/gcs/src/share/qml/pfd/PfdVideoWorld.qml b/ground/gcs/src/share/qml/pfd/PfdVideoWorld.qml new file mode 100644 index 0000000000..9d433c1191 --- /dev/null +++ b/ground/gcs/src/share/qml/pfd/PfdVideoWorld.qml @@ -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 . + */ +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 + } + } +} From cfb621f9f23cfffdb6b919117d2556033f8dc700 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Tue, 11 Jul 2017 22:02:49 +0200 Subject: [PATCH 08/18] LP-109 win installer: add gstreamer libs --- package/winx86/gcs.nsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/winx86/gcs.nsi b/package/winx86/gcs.nsi index 158ac22900..66001402ee 100644 --- a/package/winx86/gcs.nsi +++ b/package/winx86/gcs.nsi @@ -181,6 +181,8 @@ Section "-Libs" InSecLibs SectionIn RO SetOutPath "$INSTDIR\lib\${GCS_SMALL_NAME}\osg" File /r "${GCS_BUILD_TREE}\lib\${GCS_SMALL_NAME}\osg\*.dll" + SetOutPath "$INSTDIR\lib\${GCS_SMALL_NAME}\gstreamer-1.0" + File /r "${GCS_BUILD_TREE}\lib\${GCS_SMALL_NAME}\gstreamer-1.0\*.dll" SectionEnd ; Copy GCS resources From 7cdc38eb58776e0a98e91a237f2e9a6fcf0206ea Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Fri, 14 Jul 2017 18:40:43 +0200 Subject: [PATCH 09/18] LP-109 video : increase osxvideosink rank so autovideosink selects it --- ground/gcs/src/libs/gstreamer/gst_util.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ground/gcs/src/libs/gstreamer/gst_util.cpp b/ground/gcs/src/libs/gstreamer/gst_util.cpp index 0a24a485d2..1560a45124 100644 --- a/ground/gcs/src/libs/gstreamer/gst_util.cpp +++ b/ground/gcs/src/libs/gstreamer/gst_util.cpp @@ -77,7 +77,6 @@ void gst::init(int *argc, char * *argv[]) // qputenv("GST_DEBUG_FILE", "gst.log"); // qputenv("GST_DEBUG_DUMP_DOT_DIR", "."); - #ifdef Q_OS_WIN qputenv("GST_PLUGIN_PATH_1_0", (Utils::GetLibraryPath() + "gstreamer-1.0").toLatin1()); #endif @@ -97,6 +96,18 @@ void gst::init(int *argc, char * *argv[]) // GST_PLUGIN_STATIC_REGISTER(librepilot); gst_plugin_librepilot_register(); +#ifdef Q_OS_MAC + GstRegistry *reg = gst_registry_get(); + + GstPluginFeature *feature = gst_registry_lookup_feature(reg, "osxvideosink"); + if (feature) { + // raise rank of osxvideosink so it gets selected by autovideosink + // if not doing that then autovideosink selects the glimagesink which fails in Qt + gst_plugin_feature_set_rank(feature, GST_RANK_PRIMARY); + gst_object_unref(feature); + } +#endif + #ifdef USE_OPENCV // see http://stackoverflow.com/questions/32477403/how-to-know-if-sse2-is-activated-in-opencv // see http://answers.opencv.org/question/696/how-to-enable-vectorization-in-opencv/ From ba29f527fa5a929d0c384c1c10af03a13a325cde Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Fri, 14 Jul 2017 18:41:08 +0200 Subject: [PATCH 10/18] LP-109 pluginerrorview: add missing breaks --- ground/gcs/src/libs/extensionsystem/pluginerrorview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ground/gcs/src/libs/extensionsystem/pluginerrorview.cpp b/ground/gcs/src/libs/extensionsystem/pluginerrorview.cpp index df177d3619..d23711a9da 100644 --- a/ground/gcs/src/libs/extensionsystem/pluginerrorview.cpp +++ b/ground/gcs/src/libs/extensionsystem/pluginerrorview.cpp @@ -102,9 +102,11 @@ void PluginErrorView::update(PluginSpec *spec) case PluginSpec::Stopped: text = tr("Stopped"); tooltip = tr("Plugin was shut down"); + break; case PluginSpec::Deleted: text = tr("Deleted"); tooltip = tr("Plugin ended its life cycle and was deleted"); + break; } m_ui->state->setText(text); m_ui->state->setToolTip(tooltip); From d78472b59ce2a31c730597b8700f9cdfb19c070d Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Sat, 15 Jul 2017 12:33:01 +0200 Subject: [PATCH 11/18] LP-109 video: cleanup camera calibration plugin --- ground/gcs/src/libs/gstreamer/gstreamer.pro | 2 +- .../plugins/cameracalibration/cameraevent.cpp | 4 +- .../plugins/cameracalibration/cameraevent.hpp | 2 +- .../plugins/cameracalibration/camerautils.cpp | 4 +- .../plugins/cameracalibration/camerautils.hpp | 2 +- .../gstcameracalibration.cpp | 29 +- .../cameracalibration/gstcameracalibration.h | 6 +- .../cameracalibration/gstcameraundistort.cpp | 30 +- .../cameracalibration/gstcameraundistort.h | 7 +- .../cameracalibration/gstopencvutils.cpp | 178 ----------- .../cameracalibration/gstopencvutils.h | 46 --- .../gstopencvvideofilter.cpp | 289 ------------------ .../cameracalibration/gstopencvvideofilter.h | 110 ------- .../src/libs/gstreamer/plugins/plugins.pro | 10 - 14 files changed, 12 insertions(+), 707 deletions(-) delete mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp delete mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h delete mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp delete mode 100644 ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h diff --git a/ground/gcs/src/libs/gstreamer/gstreamer.pro b/ground/gcs/src/libs/gstreamer/gstreamer.pro index e302ed5970..a30083b3b0 100644 --- a/ground/gcs/src/libs/gstreamer/gstreamer.pro +++ b/ground/gcs/src/libs/gstreamer/gstreamer.pro @@ -9,7 +9,7 @@ include(../utils/utils.pri) include(gstreamer_dependencies.pri) -gstreamer_plugins:include(plugins/plugins.pro) +include(plugins/plugins.pro) HEADERS += \ gst_global.h \ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp index 2e7a71a718..a56e38d8de 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.cpp @@ -1,7 +1,5 @@ /* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * Library <2002> Ronald Bultje - * Copyright (C) 2007 David A. Schleef + * Copyright (C) <2017> Philippe Renon * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp index cc6d362c8e..9bcd58c09d 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/cameraevent.hpp @@ -1,5 +1,5 @@ /* GStreamer - * Copyright (C) <2011> Wim Taymans + * Copyright (C) <2017> Philippe Renon * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp index 620a5f9723..57a68d257a 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.cpp @@ -1,7 +1,5 @@ /* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * Library <2002> Ronald Bultje - * Copyright (C) 2007 David A. Schleef + * Copyright (C) <2017> Philippe Renon * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp index 356c1d7f22..39e78912e9 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/camerautils.hpp @@ -1,5 +1,5 @@ /* GStreamer - * Copyright (C) <2011> Wim Taymans + * Copyright (C) <2017> Philippe Renon * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp index 39d8c17b1c..7180819ce4 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.cpp @@ -1,10 +1,6 @@ /* * GStreamer - * Copyright (C) 2005 Thomas Vander Stichele - * Copyright (C) 2005 Ronald S. Bultje - * Copyright (C) 2008 Michael Sheldon - * Copyright (C) 2011 Stefan Sauer - * Copyright (C) 2014 Robert Jobbagy + * Copyright (C) <2017> Philippe Renon * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -48,28 +44,7 @@ /** * SECTION:element-cameracalibration * - * Performs face detection on videos and images. - * If you have high cpu load you need to use videoscale with capsfilter and reduce the video resolution. - * - * The image is scaled down multiple times using the GstCameraCalibration::scale-factor - * until the size is <= GstCameraCalibration::min-size-width or - * GstCameraCalibration::min-size-height. - * - * - * Example launch line - * |[ - * gst-launch-1.0 autovideosrc ! decodebin ! colorspace ! cameracalibration ! videoconvert ! xvimagesink - * ]| Detect and show faces - * |[ - * gst-launch-1.0 autovideosrc ! video/x-raw,width=320,height=240 ! videoconvert ! cameracalibration min-size-width=60 min-size-height=60 ! colorspace ! xvimagesink - * ]| Detect large faces on a smaller image - * - * - */ - -/* FIXME: development version of OpenCV has CV_HAAR_FIND_BIGGEST_OBJECT which - * we might want to use if available - * see https://code.ros.org/svn/opencv/trunk/opencv/modules/objdetect/src/haar.cpp + * Performs fcamera calibration. */ #ifdef HAVE_CONFIG_H diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h index 0da881216b..cd1007de3e 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameracalibration.h @@ -1,10 +1,6 @@ /* * GStreamer - * Copyright (C) 2005 Thomas Vander Stichele - * Copyright (C) 2005 Ronald S. Bultje - * Copyright (C) 2008 Michael Sheldon - * Copyright (C) 2011 Stefan Sauer - * Copyright (C) 2011 Robert Jobbagy + * Copyright (C) <2017> Philippe Renon * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp index 67db605b80..2eb88a9fea 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.cpp @@ -1,10 +1,6 @@ /* * GStreamer - * Copyright (C) 2005 Thomas Vander Stichele - * Copyright (C) 2005 Ronald S. Bultje - * Copyright (C) 2008 Michael Sheldon - * Copyright (C) 2011 Stefan Sauer - * Copyright (C) 2014 Robert Jobbagy + * Copyright (C) <2017> Philippe Renon * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -48,28 +44,8 @@ /** * SECTION:element-cameraundistort * - * Performs face detection on videos and images. - * If you have high cpu load you need to use videoscale with capsfilter and reduce the video resolution. + * Performs camera distortion correction. * - * The image is scaled down multiple times using the GstCameraCalibration::scale-factor - * until the size is <= GstCameraCalibration::min-size-width or - * GstCameraCalibration::min-size-height. - * - * - * Example launch line - * |[ - * gst-launch-1.0 autovideosrc ! decodebin ! colorspace ! cameraundistort ! videoconvert ! xvimagesink - * ]| Detect and show faces - * |[ - * gst-launch-1.0 autovideosrc ! video/x-raw,width=320,height=240 ! videoconvert ! cameraundistort min-size-width=60 min-size-height=60 ! colorspace ! xvimagesink - * ]| Detect large faces on a smaller image - * - * - */ - -/* FIXME: development version of OpenCV has CV_HAAR_FIND_BIGGEST_OBJECT which - * we might want to use if available - * see https://code.ros.org/svn/opencv/trunk/opencv/modules/objdetect/src/haar.cpp */ #ifdef HAVE_CONFIG_H @@ -174,7 +150,7 @@ gst_camera_undistort_class_init (GstCameraUndistortClass * klass) g_object_class_install_property (gobject_class, PROP_ALPHA, g_param_spec_float ("alpha", "Pixels", - "Pixels bla bla...", + "Show all pixels (1), only valid ones (0) or something in between", 0.0, 1.0, DEFAULT_ALPHA, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h index 829dbe9f45..306df5b3c8 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h +++ b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstcameraundistort.h @@ -1,10 +1,6 @@ /* * GStreamer - * Copyright (C) 2005 Thomas Vander Stichele - * Copyright (C) 2005 Ronald S. Bultje - * Copyright (C) 2008 Michael Sheldon - * Copyright (C) 2011 Stefan Sauer - * Copyright (C) 2011 Robert Jobbagy + * Copyright (C) <2017> Philippe Renon * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -51,7 +47,6 @@ #include #include -//#include "gstopencvvideofilter.h" #include diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp deleted file mode 100644 index 8f6c912ede..0000000000 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* GStreamer - * Copyright (C) <2010> Thiago Santos - * - * gstopencvutils.c: miscellaneous utility functions - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstopencvutils.h" -#include - -/* -The various opencv image containers or headers store the following information: -- number of channels (usually 1, 3 or 4) -- depth (8, 16, 32, 64...); all channels have the same depth. -The channel layout (BGR vs RGB) is not stored... - -This gives us the following list of supported image formats: - CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4 - CV_8SC1, CV_8SC2, CV_8SC3, CV_8SC4 - CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4 - CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4 - CV_32SC1, CV_32SC2, CV_32SC3, CV_32SC4 - CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4 - CV_64FC1, CV_64FC2, CV_64FC3, CV_64FC4 - -Where the first part of the format name is the depth followed by a digit -representing the number of channels. -Note that opencv supports more that 4 channels. - -The opencv algorithms don't all support all the image types. -For example findChessboardCorners() supports only 8 bits formats -(gray scale and color). - -And, typically, this algorithm will convert the image to gray scale before -proceeding. It will do so with something like this: - cvtColor(srcImg, destImg, CV_BGR2GRAY); - -The conversion will work on any BGR format (BGR, BGRA, BGRx). -The extra channel(s) will be ignored. -It will also produce a result for any RGB format. -The result will be "wrong" to the human eye and might affect some algorithms -(not findChessboardCorners() afaik...). -This is due to how RGB gets converted to gray where each color has a -different weight. - -Another example is the 2D rendering API. -It work with RGB but the colors will be wrong. - -Likewise other layouts like xBGR and ABGR formats will probably misbehave -with most algorithms. - -The bad thing is that it is not possible to change the "default" BGR format. -Safest is to not assume that RGB will work and always convert to BGR. - -That said, the current opencv gstreamer elements all accept BGR and RGB caps ! -Some have restrictions but if a format is supported then both BGR and RGB -layouts will be supported. -*/ - -gboolean -gst_opencv_parse_iplimage_params_from_caps (GstCaps * caps, gint * width, - gint * height, gint * ipldepth, gint * channels, GError ** err) -{ - GstVideoInfo info; - GstVideoFormat format; - int cv_type; - gchar *caps_str; - - if (!gst_video_info_from_caps (&info, caps)) { - 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; - } - - format = GST_VIDEO_INFO_FORMAT (&info); - if (!gst_opencv_cv_image_type_from_video_format (format, &cv_type, err)) { - return FALSE; - } - - *width = GST_VIDEO_INFO_WIDTH (&info); - *height = GST_VIDEO_INFO_HEIGHT (&info); - - *ipldepth = cvIplDepth (cv_type); - *channels = CV_MAT_CN (cv_type); - - return TRUE; -} - -gboolean -gst_opencv_cv_image_type_from_video_format (GstVideoFormat format, - int * cv_type, GError ** err) -{ - const gchar *format_str; - - switch (format) { - case GST_VIDEO_FORMAT_GRAY8: - *cv_type = CV_8UC1; - break; - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: - *cv_type = CV_8UC3; - break; - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_ABGR: - *cv_type = CV_8UC4; - break; - case GST_VIDEO_FORMAT_GRAY16_LE: - case GST_VIDEO_FORMAT_GRAY16_BE: - *cv_type = CV_16UC1; - break; - default: - format_str = gst_video_format_to_string (format); - g_set_error (err, GST_CORE_ERROR, GST_CORE_ERROR_NEGOTIATION, - "Unsupported video format %s", format_str); - return FALSE; - } - - return TRUE; -} - -GstCaps * -gst_opencv_caps_from_cv_image_type (int cv_type) -{ - GstCaps *c = gst_caps_new_empty (); - switch (cv_type) { - case CV_8UC1: - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("GRAY8"))); - break; - case CV_8UC3: - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("RGB"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("BGR"))); - break; - case CV_8UC4: - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("RGBx"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("xRGB"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("BGRx"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("xBGR"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("RGBA"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("ARGB"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("BGRA"))); - gst_caps_append (c, gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("ABGR"))); - break; - case CV_16UC1: - gst_caps_append (c, - gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("GRAY16_LE"))); - gst_caps_append (c, - gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("GRAY16_BE"))); - break; - } - return c; -} diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h deleted file mode 100644 index b0397695e1..0000000000 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvutils.h +++ /dev/null @@ -1,46 +0,0 @@ -/* GStreamer - * Copyright (C) <2010> Thiago Santos - * - * gstopencvutils.h: miscellaneous utility functions - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_OPENCV_UTILS__ -#define __GST_OPENCV_UTILS__ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -G_BEGIN_DECLS - -gboolean gst_opencv_parse_iplimage_params_from_caps - (GstCaps * caps, gint * width, gint * height, gint * depth, - gint * channels, GError ** err); - -gboolean -gst_opencv_cv_image_type_from_video_format (GstVideoFormat format, - int * cv_type, GError ** err); - -GstCaps * gst_opencv_caps_from_cv_image_type (int cv_type); - -G_END_DECLS - -#endif /* __GST_OPENCV_UTILS__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp deleted file mode 100644 index eb9ed751cc..0000000000 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/* - * GStreamer - * Copyright (C) 2010 Thiago Santos - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Alternatively, the contents of this file may be used under the - * GNU Lesser General Public License Version 2.1 (the "LGPL"), in - * which case the following provisions apply instead of the ones - * mentioned above: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/* TODO opencv can do scaling for some cases */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "gstopencvvideofilter.h" -#include "gstopencvutils.h" - -#include - -GST_DEBUG_CATEGORY_STATIC (gst_opencv_video_filter_debug); -#define GST_CAT_DEFAULT gst_opencv_video_filter_debug - -/* Filter signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - PROP_0 -}; - -static GstElementClass *parent_class = NULL; - -static void gst_opencv_video_filter_class_init (GstOpencvVideoFilterClass * - klass); -static void gst_opencv_video_filter_init (GstOpencvVideoFilter * cv_filter, - GstOpencvVideoFilterClass * klass); - -static gboolean gst_opencv_video_filter_set_info (GstVideoFilter * vfilter, - GstCaps * incaps, GstVideoInfo * in_info, - GstCaps * outcaps, GstVideoInfo * out_info); -static GstFlowReturn gst_opencv_video_filter_transform_frame_ip ( - GstVideoFilter * vfilter, GstVideoFrame * frame); -static GstFlowReturn gst_opencv_video_filter_transform_frame ( - GstVideoFilter * vfilter, - GstVideoFrame * inframe, GstVideoFrame * outframe); - -static void gst_opencv_video_filter_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_opencv_video_filter_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -GType -gst_opencv_video_filter_get_type (void) -{ - static volatile gsize opencv_video_filter_type = 0; - - if (g_once_init_enter (&opencv_video_filter_type)) { - GType _type; - static const GTypeInfo opencv_video_filter_info = { - sizeof (GstOpencvVideoFilterClass), - NULL, - NULL, - (GClassInitFunc) gst_opencv_video_filter_class_init, - NULL, - NULL, - sizeof (GstOpencvVideoFilter), - 0, - (GInstanceInitFunc) gst_opencv_video_filter_init, - }; - - _type = g_type_register_static (GST_TYPE_VIDEO_FILTER, - "GstOpencvVideoFilter", &opencv_video_filter_info, - G_TYPE_FLAG_ABSTRACT); - g_once_init_leave (&opencv_video_filter_type, _type); - } - return opencv_video_filter_type; -} - -/* Clean up */ -static void -gst_opencv_video_filter_finalize (GObject * obj) -{ - GstOpencvVideoFilter *cv_filter = GST_OPENCV_VIDEO_FILTER (obj); - - if (cv_filter->cvImage) - cvReleaseImage (&cv_filter->cvImage); - if (cv_filter->out_cvImage) - cvReleaseImage (&cv_filter->out_cvImage); - - G_OBJECT_CLASS (parent_class)->finalize (obj); -} - -static void -gst_opencv_video_filter_class_init (GstOpencvVideoFilterClass * klass) -{ - GObjectClass *gobject_class; - GstVideoFilterClass *filter_class; - - gobject_class = (GObjectClass *) klass; - filter_class = (GstVideoFilterClass *) klass; - parent_class = (GstElementClass *) g_type_class_peek_parent (klass); - - GST_DEBUG_CATEGORY_INIT (gst_opencv_video_filter_debug, - "opencvvideofilter", 0, "opencvvideofilter element"); - - gobject_class->finalize = - GST_DEBUG_FUNCPTR (gst_opencv_video_filter_finalize); - gobject_class->set_property = gst_opencv_video_filter_set_property; - gobject_class->get_property = gst_opencv_video_filter_get_property; - - filter_class->transform_frame = gst_opencv_video_filter_transform_frame; - filter_class->transform_frame_ip = - gst_opencv_video_filter_transform_frame_ip; - filter_class->set_info = gst_opencv_video_filter_set_info; -} - -static void -gst_opencv_video_filter_init (GstOpencvVideoFilter * cv_filter, - GstOpencvVideoFilterClass * klass) -{ -} - -static GstFlowReturn -gst_opencv_video_filter_transform_frame (GstVideoFilter * vfilter, - GstVideoFrame * inframe, GstVideoFrame * outframe) -{ - GstOpencvVideoFilter *cv_filter; - GstOpencvVideoFilterClass *fclass; - GstFlowReturn ret; - - cv_filter = GST_OPENCV_VIDEO_FILTER (vfilter); - fclass = GST_OPENCV_VIDEO_FILTER_GET_CLASS (vfilter); - - g_return_val_if_fail (fclass->cv_transform_frame != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (cv_filter->cvImage != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (cv_filter->out_cvImage != NULL, GST_FLOW_ERROR); - - cv_filter->cvImage->imageData = (char *) inframe->data; - cv_filter->out_cvImage->imageData = (char *) outframe->data; - - ret = fclass->cv_transform_frame (cv_filter, inframe, cv_filter->cvImage, - outframe, cv_filter->out_cvImage); - - return ret; -} - -static GstFlowReturn -gst_opencv_video_filter_transform_frame_ip (GstVideoFilter * vfilter, - GstVideoFrame * frame) -{ - GstOpencvVideoFilter *cv_filter; - GstOpencvVideoFilterClass *fclass; - GstFlowReturn ret; - - cv_filter = GST_OPENCV_VIDEO_FILTER (vfilter); - fclass = GST_OPENCV_VIDEO_FILTER_GET_CLASS (vfilter); - - g_return_val_if_fail (fclass->cv_transform_frame_ip != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (cv_filter->cvImage != NULL, GST_FLOW_ERROR); - - cv_filter->cvImage->imageData = (char *) frame->data; - - ret = fclass->cv_transform_frame_ip (cv_filter, frame, cv_filter->cvImage); - - return ret; -} - -static gboolean -gst_opencv_video_filter_set_info (GstVideoFilter * vfilter, GstCaps * incaps, - GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info) -{ - GstOpencvVideoFilter *cv_filter = GST_OPENCV_VIDEO_FILTER (vfilter); - GstOpencvVideoFilterClass *klass = - GST_OPENCV_VIDEO_FILTER_GET_CLASS (cv_filter); - gint in_width, in_height; - gint in_depth, in_channels; - gint out_width, out_height; - gint out_depth, out_channels; - GError *in_err = NULL; - GError *out_err = NULL; - - if (!gst_opencv_parse_iplimage_params_from_caps (incaps, &in_width, - &in_height, &in_depth, &in_channels, &in_err)) { - GST_WARNING_OBJECT (cv_filter, "Failed to parse input caps: %s", - in_err->message); - g_error_free (in_err); - return FALSE; - } - - if (!gst_opencv_parse_iplimage_params_from_caps (outcaps, &out_width, - &out_height, &out_depth, &out_channels, &out_err)) { - GST_WARNING_OBJECT (cv_filter, "Failed to parse output caps: %s", - out_err->message); - g_error_free (out_err); - return FALSE; - } - - if (klass->cv_set_info) { - if (!klass->cv_set_info (cv_filter, in_width, in_height, in_depth, - in_channels, out_width, out_height, out_depth, out_channels)) - return FALSE; - } - - if (cv_filter->cvImage) { - cvReleaseImage (&cv_filter->cvImage); - } - if (cv_filter->out_cvImage) { - cvReleaseImage (&cv_filter->out_cvImage); - } - - cv_filter->cvImage = - cvCreateImageHeader (cvSize (in_width, in_height), in_depth, in_channels); - cv_filter->out_cvImage = - cvCreateImageHeader (cvSize (out_width, out_height), out_depth, - out_channels); - - gst_base_transform_set_in_place (GST_BASE_TRANSFORM (cv_filter), - cv_filter->in_place); - return TRUE; -} - -static void -gst_opencv_video_filter_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_opencv_video_filter_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -void -gst_opencv_video_filter_set_in_place (GstOpencvVideoFilter * cv_filter, - gboolean ip) -{ - cv_filter->in_place = ip; - gst_base_transform_set_in_place (GST_BASE_TRANSFORM (cv_filter), ip); -} diff --git a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h b/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h deleted file mode 100644 index 29f637237c..0000000000 --- a/ground/gcs/src/libs/gstreamer/plugins/cameracalibration/gstopencvvideofilter.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * GStreamer - * Copyright (C) 2010 Thiago Santos - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Alternatively, the contents of this file may be used under the - * GNU Lesser General Public License Version 2.1 (the "LGPL"), in - * which case the following provisions apply instead of the ones - * mentioned above: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_OPENCV_VIDEO_FILTER_H__ -#define __GST_OPENCV_VIDEO_FILTER_H__ - -#include -#include - -G_BEGIN_DECLS - -/* forward declare opencv type to avoid exposing them in this API */ -typedef struct _IplImage IplImage; - -/* #defines don't like whitespacey bits */ -#define GST_TYPE_OPENCV_VIDEO_FILTER \ - (gst_opencv_video_filter_get_type()) -#define GST_OPENCV_VIDEO_FILTER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPENCV_VIDEO_FILTER,GstOpencvVideoFilter)) -#define GST_OPENCV_VIDEO_FILTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPENCV_VIDEO_FILTER,GstOpencvVideoFilterClass)) -#define GST_IS_OPENCV_VIDEO_FILTER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPENCV_VIDEO_FILTER)) -#define GST_IS_OPENCV_VIDEO_FILTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPENCV_VIDEO_FILTER)) -#define GST_OPENCV_VIDEO_FILTER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OPENCV_VIDEO_FILTER,GstOpencvVideoFilterClass)) -#define GST_OPENCV_VIDEO_FILTER_CAST(obj) ((GstOpencvVideoFilter *) (obj)) - -typedef struct _GstOpencvVideoFilter GstOpencvVideoFilter; -typedef struct _GstOpencvVideoFilterClass GstOpencvVideoFilterClass; - -typedef GstFlowReturn (*GstOpencvVideoFilterTransformIPFunc) - (GstOpencvVideoFilter * cvfilter, GstVideoFrame * frame, IplImage * img); -typedef GstFlowReturn (*GstOpencvVideoFilterTransformFunc) - (GstOpencvVideoFilter * cvfilter, GstVideoFrame * frame, IplImage * img, - GstVideoFrame * outframe, IplImage * outimg); - -typedef gboolean (*GstOpencvVideoFilterSetInfo) - (GstOpencvVideoFilter * cv_filter, gint in_width, gint in_height, - gint in_depth, gint in_channels, gint out_width, gint out_height, - gint out_depth, gint out_channels); - -struct _GstOpencvVideoFilter -{ - GstVideoFilter videofilter; - - gboolean in_place; - - IplImage *cvImage; - IplImage *out_cvImage; -}; - -struct _GstOpencvVideoFilterClass -{ - GstVideoFilterClass parent_class; - - GstOpencvVideoFilterTransformFunc cv_transform_frame; - GstOpencvVideoFilterTransformIPFunc cv_transform_frame_ip; - - GstOpencvVideoFilterSetInfo cv_set_info; -}; - -GType gst_opencv_video_filter_get_type (void); - -void gst_opencv_video_filter_set_in_place (GstOpencvVideoFilter * cv_filter, - gboolean ip); - -G_END_DECLS -#endif /* __GST_OPENCV_VIDEO_FILTER_H__ */ diff --git a/ground/gcs/src/libs/gstreamer/plugins/plugins.pro b/ground/gcs/src/libs/gstreamer/plugins/plugins.pro index 92a5511ee4..081c2b22e2 100644 --- a/ground/gcs/src/libs/gstreamer/plugins/plugins.pro +++ b/ground/gcs/src/libs/gstreamer/plugins/plugins.pro @@ -3,16 +3,6 @@ DEFINES += GST_PLUGIN_BUILD_STATIC #CONFIG += link_pkgconfig PKGCONFIG += gstreamer-base-1.0 -do_not_compile { - HEADERS += \ - plugins/cameracalibration/gstopencvutils.h \ - plugins/cameracalibration/gstopencvvideofilter.hpp - - SOURCES += \ - plugins/cameracalibration/gstopencvutils.cpp \ - plugins/cameracalibration/gstopencvvideofilter.cpp \ -} - opencv { # there is no package for gst opencv yet... GSTREAMER_SDK_DIR = $$system(pkg-config --variable=exec_prefix gstreamer-1.0) From dffc4de6dbf0e1f42156a40919489bd64cc1b03a Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Sat, 15 Jul 2017 19:01:18 +0200 Subject: [PATCH 12/18] LP-109 video gadget: add gstreamer opengl plugin --- ground/gcs/src/libs/gstreamer/copydata.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/ground/gcs/src/libs/gstreamer/copydata.pro b/ground/gcs/src/libs/gstreamer/copydata.pro index 20522f31eb..bb7e76e9a5 100644 --- a/ground/gcs/src/libs/gstreamer/copydata.pro +++ b/ground/gcs/src/libs/gstreamer/copydata.pro @@ -56,6 +56,7 @@ win32:gstreamer { libgstd3dvideosink.dll \ libgstdebugutilsbad.dll \ libgstdirectsoundsrc.dll \ + libgstopengl.dll \ libgstinter.dll \ libgstmpegpsdemux.dll \ libgstmpegpsmux.dll \ From ddbabc4bf027737e1877fce243146e855bba6703 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Tue, 29 Aug 2017 21:24:06 +0200 Subject: [PATCH 13/18] LP-109 use standard way to enable gstreamer support --- Makefile | 17 +++++++++++++++++ ground/gcs/src/libs/gstreamer/gstreamer.pro | 2 +- ground/gcs/src/libs/gstreamer/readme.txt | 4 +--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f2aac9ead2..642931aa4d 100644 --- a/Makefile +++ b/Makefile @@ -131,16 +131,22 @@ ifeq ($(UNAME), Linux) GCS_WITH_OSG := 1 GCS_WITH_OSGEARTH := 1 GCS_COPY_OSG := 0 + GCS_WITH_GSTREAMER := 0 + GCS_COPY_GSTREAMER := 0 else ifeq ($(UNAME), Darwin) UAVOBJGENERATOR := $(BUILD_DIR)/uavobjgenerator/uavobjgenerator GCS_WITH_OSG := 1 GCS_WITH_OSGEARTH := 0 GCS_COPY_OSG := 1 + GCS_WITH_GSTREAMER := 0 + GCS_COPY_GSTREAMER := 0 else ifeq ($(UNAME), Windows) UAVOBJGENERATOR := $(BUILD_DIR)/uavobjgenerator/uavobjgenerator.exe GCS_WITH_OSG := 1 GCS_WITH_OSGEARTH := 1 GCS_COPY_OSG := 1 + GCS_WITH_GSTREAMER := 1 + GCS_COPY_GSTREAMER := 1 endif export UAVOBJGENERATOR @@ -159,6 +165,13 @@ ifeq ($(GCS_WITH_OSG), 1) endif endif +ifeq ($(GCS_WITH_GSTREAMER), 1) + GCS_EXTRA_CONF += gstreamer + ifeq ($(GCS_COPY_GSTREAMER), 1) + GCS_EXTRA_CONF += copy_gstreamer + endif +endif + ############################## # # All targets @@ -590,6 +603,10 @@ config_help: @$(ECHO) " (Needed unless using system versions)" @$(ECHO) " Options: 0 or 1" @$(ECHO) + @$(ECHO) " GCS_WITH_GSTREAMER=$(GCS_WITH_GSTREAMER)" + @$(ECHO) " Build the GCS with GStreamer support, this enables the video gadget and extra PFD video views" + @$(ECHO) " Options: 0 or 1" + @$(ECHO) @$(ECHO) " CCACHE=$(CCACHE)" @$(ECHO) " A prefix to compiler invocations, usually 'ccache' or 'path/to/ccache'" @$(ECHO) diff --git a/ground/gcs/src/libs/gstreamer/gstreamer.pro b/ground/gcs/src/libs/gstreamer/gstreamer.pro index a30083b3b0..6e77a2dfcc 100644 --- a/ground/gcs/src/libs/gstreamer/gstreamer.pro +++ b/ground/gcs/src/libs/gstreamer/gstreamer.pro @@ -26,4 +26,4 @@ SOURCES += \ pipeline.cpp \ videowidget.cpp -equals(copydata, 1):include(copydata.pro) +copy_gstreamer:include(copydata.pro) diff --git a/ground/gcs/src/libs/gstreamer/readme.txt b/ground/gcs/src/libs/gstreamer/readme.txt index 94f6d9b623..9fdafd5049 100644 --- a/ground/gcs/src/libs/gstreamer/readme.txt +++ b/ground/gcs/src/libs/gstreamer/readme.txt @@ -3,9 +3,7 @@ ############################################################################### Add the following line to your build config file: - GCS_EXTRA_CONF += gstreamer - or run this command - make config_append GCS_EXTRA_CONF+=gstreamer + make config_append GCS_WITH_GSTREAMER=1 The build config file is at the root of your source directory. From f78d161dc3ac3626b1cd7897362aac89fea5bdeb Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Tue, 12 Sep 2017 22:13:48 +0200 Subject: [PATCH 14/18] LP-109 fix crash when using PFD Video without gstreamer support --- ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp b/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp index 187efb9310..f2e8712ca1 100644 --- a/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp +++ b/ground/gcs/src/libs/osgearth/osgQtQuick/OSGImageNode.cpp @@ -84,7 +84,7 @@ struct OSGImageNode::Hidden : public QObject { imageSource = new ImageSource(); } } - return imageSource->createImage(imageUrl); + return imageSource ? imageSource->createImage(imageUrl) : 0; } void updateImageFile() @@ -96,8 +96,12 @@ struct OSGImageNode::Hidden : public QObject { { osg::Image *image = loadImage(); + if (!image) { + return; + } + // qDebug() << "OSGImageNode::update" << image; - osg::Node *geode = createGeodeForImage(image); + osg::Node *geode = createGeodeForImage(image); self->setNode(geode); } From 1e4c9d40097a5cbd3bf3b49e8eb4dc6cfe936f97 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Tue, 12 Sep 2017 22:15:21 +0200 Subject: [PATCH 15/18] LP-109 extract Windows specific gadget configurations to defaults_windows.xml --- .../gcs/src/share/configurations/default.xml | 131 ----------------- .../share/configurations/default_windows.xml | 139 ++++++++++++++++++ 2 files changed, 139 insertions(+), 131 deletions(-) create mode 100644 ground/gcs/src/share/configurations/default_windows.xml diff --git a/ground/gcs/src/share/configurations/default.xml b/ground/gcs/src/share/configurations/default.xml index bc12410ae4..65dc90947b 100644 --- a/ground/gcs/src/share/configurations/default.xml +++ b/ground/gcs/src/share/configurations/default.xml @@ -1665,31 +1665,6 @@ %%DATAPATH%%backgrounds/default_background.png - - - false - 0.0.0 - - - %%DATAPATH%%qml/PfdVideo.qml - 1 - 1 - true - %%DATAPATH%%osgearth/arcgis.earth - false - 39.6576 - 19.8046 - 90 - 0 - @Variant(AAAAEAAlfhEClAez/w==) - 0.50 - false - 1 - %%DATAPATH%%models/multi/test_quad/test_quad_x.3ds - %%DATAPATH%%backgrounds/default_background.png - ksvideosrc device-index=0 ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true - - false @@ -2825,112 +2800,6 @@ false - - - false - 0.0.0 - - - false - false - true - dx9screencapsrc monitor=0 cursor=true ! tee name=t - t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink - t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg - - true - - - - - false - 0.0.0 - - - false - false - true - dx9screencapsrc monitor=0 cursor=true ! tee name=t - t. ! queue ! timeoverlay ! autovideosink - t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg - - true - - - - - false - 0.0.0 - - - false - false - true - dx9screencapsrc monitor=0 cursor=true x=0 y=0 width=640 height=480 ! autovideosink - - false - - - - - false - 0.0.0 - - - true - false - true - dx9screencapsrc monitor=0 cursor=true ! autovideosink - - false - - - - - false - 0.0.0 - - - false - false - true - ksvideosrc device-index=0 ! tee name=t - t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink - t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg - - true - - - - - false - 0.0.0 - - - false - false - true - ksvideosrc device-index=0 ! tee name=t - t. ! queue ! timeoverlay ! autovideosink - t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg - - true - - - - - false - 0.0.0 - - - true - false - true - ksvideosrc device-index=0 ! autovideosink - - false - - false diff --git a/ground/gcs/src/share/configurations/default_windows.xml b/ground/gcs/src/share/configurations/default_windows.xml new file mode 100644 index 0000000000..fb9ad376a0 --- /dev/null +++ b/ground/gcs/src/share/configurations/default_windows.xml @@ -0,0 +1,139 @@ + + + + + + false + 0.0.0 + + + %%DATAPATH%%qml/PfdVideo.qml + 1 + 1 + true + %%DATAPATH%%osgearth/arcgis.earth + false + 39.6576 + 19.8046 + 90 + 0 + @Variant(AAAAEAAlfhEClAez/w==) + 0.50 + false + 1 + %%DATAPATH%%models/multi/test_quad/test_quad_x.3ds + %%DATAPATH%%backgrounds/default_background.png + ksvideosrc device-index=0 ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true + + + + + + + false + 0.0.0 + + + false + false + true + dx9screencapsrc monitor=0 cursor=true ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + dx9screencapsrc monitor=0 cursor=true ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + dx9screencapsrc monitor=0 cursor=true x=0 y=0 width=640 height=480 ! autovideosink + + false + + + + + false + 0.0.0 + + + true + false + true + dx9screencapsrc monitor=0 cursor=true ! autovideosink + + false + + + + + false + 0.0.0 + + + false + false + true + ksvideosrc device-index=0 ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + ksvideosrc device-index=0 ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + true + false + true + ksvideosrc device-index=0 ! autovideosink + + false + + + + + From df796c8a19f89f3024b6d9e54ca1be8da7d45d80 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Tue, 12 Sep 2017 22:18:38 +0200 Subject: [PATCH 16/18] uncrustify --- flight/pios/common/pios_board_io.c | 1 + flight/pios/common/pios_ms56xx.c | 27 ++++++++++--------- flight/pios/common/pios_oplinkrcvr.c | 2 +- flight/targets/boards/revolution/pios_board.h | 1 - flight/targets/boards/sparky2/pios_board.h | 1 - 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/flight/pios/common/pios_board_io.c b/flight/pios/common/pios_board_io.c index 441bad18f3..9a0d453c81 100644 --- a/flight/pios/common/pios_board_io.c +++ b/flight/pios/common/pios_board_io.c @@ -545,6 +545,7 @@ void PIOS_BOARD_IO_Configure_GCS_RCVR() void PIOS_BOARD_IO_Configure_OPLink_RCVR() { uint32_t pios_oplinkrcvr_id; + OPLinkReceiverInitialize(); #if defined(PIOS_INCLUDE_RFM22B) PIOS_OPLinkRCVR_Init(&pios_oplinkrcvr_id, pios_rfm22b_id); diff --git a/flight/pios/common/pios_ms56xx.c b/flight/pios/common/pios_ms56xx.c index 904481843f..bf0237b12a 100644 --- a/flight/pios/common/pios_ms56xx.c +++ b/flight/pios/common/pios_ms56xx.c @@ -34,12 +34,12 @@ #define POW2(x) (1 << x) // Command addresses -#define MS56XX_RESET 0x1E -#define MS56XX_CALIB_ADDR 0xA2 /* First sample is factory stuff */ -#define MS56XX_CALIB_LEN 16 -#define MS56XX_ADC_READ 0x00 -#define MS56XX_PRES_ADDR 0x40 -#define MS56XX_TEMP_ADDR 0x50 +#define MS56XX_RESET 0x1E +#define MS56XX_CALIB_ADDR 0xA2 /* First sample is factory stuff */ +#define MS56XX_CALIB_LEN 16 +#define MS56XX_ADC_READ 0x00 +#define MS56XX_PRES_ADDR 0x40 +#define MS56XX_TEMP_ADDR 0x50 // Option to change the interleave between Temp and Pressure conversions // Undef for normal operation @@ -128,7 +128,7 @@ const PIOS_SENSORS_Driver PIOS_MS56xx_Driver = { */ void PIOS_MS56xx_Init(const struct pios_ms56xx_cfg *cfg, int32_t i2c_device) { - i2c_id = i2c_device; + i2c_id = i2c_device; ms56xx_address = cfg->address; version = cfg->version; @@ -262,14 +262,14 @@ int32_t PIOS_MS56xx_ReadADC(void) // Offset and sensitivity at actual temperature if (version == MS56XX_VERSION_5611) { // OFF = OFFT1 + TCO * dT = C2 * 2^16 + (C4 * dT) / 2^7 - Offset = ((int64_t)CalibData.C[1]) * POW2(16) + (((int64_t)CalibData.C[3]) * deltaTemp) / POW2(7) - Offset2; + Offset = ((int64_t)CalibData.C[1]) * POW2(16) + (((int64_t)CalibData.C[3]) * deltaTemp) / POW2(7) - Offset2; // SENS = SENST1 + TCS * dT = C1 * 2^15 + (C3 * dT) / 2^8 - Sens = ((int64_t)CalibData.C[0]) * POW2(15) + (((int64_t)CalibData.C[2]) * deltaTemp) / POW2(8) - Sens2; + Sens = ((int64_t)CalibData.C[0]) * POW2(15) + (((int64_t)CalibData.C[2]) * deltaTemp) / POW2(8) - Sens2; } else { // OFF = OFFT1 + TCO * dT = C2 * 2^17 + (C4 * dT) / 2^6 - Offset = ((int64_t)CalibData.C[1]) * POW2(17) + (((int64_t)CalibData.C[3]) * deltaTemp) / POW2(6) - Offset2; + Offset = ((int64_t)CalibData.C[1]) * POW2(17) + (((int64_t)CalibData.C[3]) * deltaTemp) / POW2(6) - Offset2; // SENS = SENST1 + TCS * dT = C1 * 2^16 + (C3 * dT) / 2^7 - Sens = ((int64_t)CalibData.C[0]) * POW2(16) + (((int64_t)CalibData.C[2]) * deltaTemp) / POW2(7) - Sens2; + Sens = ((int64_t)CalibData.C[0]) * POW2(16) + (((int64_t)CalibData.C[2]) * deltaTemp) / POW2(7) - Sens2; } // Temperature compensated pressure (10…1200mbar with 0.01mbar resolution) @@ -516,10 +516,11 @@ bool PIOS_MS56xx_driver_poll(__attribute__((unused)) uintptr_t context) } /* Poll the pressure sensor and return the temperature and pressure. */ -bool PIOS_MS56xx_Read(float *temperature, float *pressure) { +bool PIOS_MS56xx_Read(float *temperature, float *pressure) +{ if (PIOS_MS56xx_driver_poll(0)) { *temperature = results.temperature; - *pressure = results.sample; + *pressure = results.sample; return true; } return false; diff --git a/flight/pios/common/pios_oplinkrcvr.c b/flight/pios/common/pios_oplinkrcvr.c index 4272855616..9ca3789066 100644 --- a/flight/pios/common/pios_oplinkrcvr.c +++ b/flight/pios/common/pios_oplinkrcvr.c @@ -38,7 +38,7 @@ #include // Put receiver in failsafe if not updated within timeout -#define PIOS_OPLINK_RCVR_TIMEOUT_MS 100 +#define PIOS_OPLINK_RCVR_TIMEOUT_MS 100 /* Provide a RCVR driver */ static int32_t PIOS_OPLinkRCVR_Get(uint32_t rcvr_id, uint8_t channel); diff --git a/flight/targets/boards/revolution/pios_board.h b/flight/targets/boards/revolution/pios_board.h index 0175fef678..aea74d9a8c 100644 --- a/flight/targets/boards/revolution/pios_board.h +++ b/flight/targets/boards/revolution/pios_board.h @@ -1,4 +1,3 @@ - /** ****************************************************************************** * @addtogroup OpenPilotSystem OpenPilot System diff --git a/flight/targets/boards/sparky2/pios_board.h b/flight/targets/boards/sparky2/pios_board.h index 728a1367ba..6470336acf 100644 --- a/flight/targets/boards/sparky2/pios_board.h +++ b/flight/targets/boards/sparky2/pios_board.h @@ -1,4 +1,3 @@ - /** ****************************************************************************** * @addtogroup OpenPilotSystem OpenPilot System From c6fbc5f87cdce428c7f42b1bd14598bfa20c990e Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Wed, 13 Sep 2017 21:28:26 +0200 Subject: [PATCH 17/18] LP-109 add linux and macos GCS defaults these defaults add gadget configuration for PFD video and video gadgets --- .../share/configurations/default_linux.xml | 125 ++++++++++++++++++ .../share/configurations/default_macos.xml | 125 ++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 ground/gcs/src/share/configurations/default_linux.xml create mode 100644 ground/gcs/src/share/configurations/default_macos.xml diff --git a/ground/gcs/src/share/configurations/default_linux.xml b/ground/gcs/src/share/configurations/default_linux.xml new file mode 100644 index 0000000000..afa9238bfb --- /dev/null +++ b/ground/gcs/src/share/configurations/default_linux.xml @@ -0,0 +1,125 @@ + + + + + + false + 0.0.0 + + + %%DATAPATH%%qml/PfdVideo.qml + 1 + 1 + true + %%DATAPATH%%osgearth/arcgis.earth + false + 39.6576 + 19.8046 + 90 + 0 + @Variant(AAAAEAAlfhEClAez/w==) + 0.50 + false + 1 + %%DATAPATH%%models/multi/test_quad/test_quad_x.3ds + %%DATAPATH%%backgrounds/default_background.png + v4l2src ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true + + + + + + + false + 0.0.0 + + + false + false + true + ximagesrc ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + ximagesrc ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + true + false + true + ximagesrc ! autovideosink + + false + + + + + false + 0.0.0 + + + false + false + true + v4l2src ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + v4l2src ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + true + false + true + v4l2src ! autovideosink + + false + + + + + diff --git a/ground/gcs/src/share/configurations/default_macos.xml b/ground/gcs/src/share/configurations/default_macos.xml new file mode 100644 index 0000000000..0b9d108528 --- /dev/null +++ b/ground/gcs/src/share/configurations/default_macos.xml @@ -0,0 +1,125 @@ + + + + + + false + 0.0.0 + + + %%DATAPATH%%qml/PfdVideo.qml + 1 + 1 + true + %%DATAPATH%%osgearth/arcgis.earth + false + 39.6576 + 19.8046 + 90 + 0 + @Variant(AAAAEAAlfhEClAez/w==) + 0.50 + false + 1 + %%DATAPATH%%models/multi/test_quad/test_quad_x.3ds + %%DATAPATH%%backgrounds/default_background.png + avfvideosrc ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true + + + + + + + false + 0.0.0 + + + false + false + true + avfvideosrc capture-screen=true capture-screen-cursor=true ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + avfvideosrc capture-screen=true capture-screen-cursor=true ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + true + false + true + avfvideosrc capture-screen=true capture-screen-cursor=true ! autovideosink + + false + + + + + false + 0.0.0 + + + false + false + true + avfvideosrc ! tee name=t + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! timeoverlay ! autovideosink + t. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videoconvert ! x264enc interlaced=true pass=quant quantizer=0 speed-preset=ultrafast byte-stream=true ! mpegpsmux ! filesink location=capture_fast.mpg + + true + + + + + false + 0.0.0 + + + false + false + true + avfvideosrc ! tee name=t + t. ! queue ! timeoverlay ! autovideosink + t. ! queue ! videoconvert ! x264enc tune=zerolatency tune=zerolatency bitrate=498 ! mpegpsmux ! filesink location=capture.mpg + + true + + + + + false + 0.0.0 + + + true + false + true + avfvideosrc ! autovideosink + + false + + + + + From bb05d8c0a7b9bd313c10805557c25c5cec851ad6 Mon Sep 17 00:00:00 2001 From: Philippe Renon Date: Wed, 13 Sep 2017 21:29:27 +0200 Subject: [PATCH 18/18] LP-109 add gstreamer packages to drone.yml --- .drone.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 44ba9f506a..8c25cdb543 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,7 +9,9 @@ build: - if [ $$arch = 32 ]; then target=i686; fi - if [ $$arch = 64 ]; then target=x86_64; fi - echo -e "[librepilot-mingw]\nSigLevel = Optional TrustAll\nServer = http://download.librepilot.org/repo/mingw" >> /etc/pacman.conf - - pacman -Syu --noconfirm --noprogressbar --needed git unzip tar mingw-w64-${target}-toolchain mingw-w64-${target}-ccache mingw-w64-${target}-ntldd mingw-w64-${target}-qt5 mingw-w64-${target}-SDL mingw-w64-${target}-mesa mingw-w64-${target}-openssl mingw-w64-${target}-gdal-minimal mingw-w64-${target}-OpenSceneGraph mingw-w64-${target}-osgearth + - pacman -Syu --noconfirm --noprogressbar --needed git unzip tar mingw-w64-${target}-toolchain mingw-w64-${target}-ccache mingw-w64-${target}-ntldd mingw-w64-${target}-qt5 mingw-w64-${target}-SDL mingw-w64-${target}-mesa mingw-w64-${target}-openssl + - pacman -Syu --noconfirm --noprogressbar --needed mingw-w64-${target}-gdal-minimal mingw-w64-${target}-OpenSceneGraph mingw-w64-${target}-osgearth + - pacman -Syu --noconfirm --noprogressbar --needed mingw-w64-${target}-gstreamer mingw-w64-${target}-gst-plugins-base mingw-w64-${target}-gst-plugins-good mingw-w64-${target}-gst-plugins-bad - mingw32-make all_sdk_install - git config core.filemode false - mingw32-make build-info && cat build/build-info.txt