LP-109 add gstreamer library and video gadget
82
ground/gcs/src/libs/gstreamer/copydata.pro
Normal file
@ -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})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
136
ground/gcs/src/libs/gstreamer/devicemonitor.cpp
Normal file
@ -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 <gst/gst.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
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<Device> DeviceMonitor::devices() const
|
||||||
|
{
|
||||||
|
QList<Device> 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);
|
||||||
|
}
|
73
ground/gcs/src/libs/gstreamer/devicemonitor.h
Normal file
@ -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 <QObject>
|
||||||
|
|
||||||
|
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<Device> 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_ */
|
38
ground/gcs/src/libs/gstreamer/gst_global.h
Normal file
@ -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 <QtCore/qglobal.h>
|
||||||
|
|
||||||
|
#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
|
125
ground/gcs/src/libs/gstreamer/gst_util.cpp
Normal file
@ -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 <gst/gst.h>
|
||||||
|
|
||||||
|
#ifdef USE_OPENCV
|
||||||
|
#include "plugins/cameracalibration/gstcameracalibration.h"
|
||||||
|
#include "plugins/cameracalibration/gstcameraundistort.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "utils/pathutils.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
39
ground/gcs/src/libs/gstreamer/gst_util.h
Normal file
@ -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 <QString>
|
||||||
|
|
||||||
|
namespace gst {
|
||||||
|
GST_LIB_EXPORT void init(int *argc, char * *argv[]);
|
||||||
|
GST_LIB_EXPORT QString version();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GST_UTIL_H
|
3
ground/gcs/src/libs/gstreamer/gstreamer.pri
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
LIBS *= -l$$qtLibraryName(GCSGStreamer)
|
||||||
|
|
||||||
|
INCLUDEPATH += $$GCS_SOURCE_TREE/src/libs/gstreamer
|
29
ground/gcs/src/libs/gstreamer/gstreamer.pro
Normal file
@ -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)
|
9
ground/gcs/src/libs/gstreamer/gstreamer_dependencies.pri
Normal file
@ -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
|
||||||
|
}
|
155
ground/gcs/src/libs/gstreamer/notes.txt
Normal file
@ -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:<udpsrc0> doing select, timeout -1
|
||||||
|
0:04:39.628925951 8296 ee56b10 DEBUG rtspsrc gstrtspsrc.c:2260:gst_rtspsrc_handle_src_event:<rtspsrc0> 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:<rtspsrc0> 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:<udpsrc0> 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 ! <something> ! 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.
|
39
ground/gcs/src/libs/gstreamer/overlay.h
Normal file
@ -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_ */
|
38
ground/gcs/src/libs/gstreamer/pipeline.cpp
Normal file
@ -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()
|
||||||
|
{}
|
41
ground/gcs/src/libs/gstreamer/pipeline.h
Normal file
@ -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_ */
|
413
ground/gcs/src/libs/gstreamer/pipelineevent.h
Normal file
@ -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 <QEvent>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#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::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::StateChange = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::StreamStatus = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::NewClock = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::ClockProvide = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::ClockLost = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::Progress = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::Latency = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::Qos = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::Eos = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::Error = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::Warning = static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||||
|
const QEvent::Type PipelineEvent::Info = static_cast<QEvent::Type>(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 "<unknown>";
|
||||||
|
}
|
||||||
|
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 "<unknown>";
|
||||||
|
}
|
||||||
|
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_ */
|
@ -0,0 +1,96 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* Library <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
* Copyright (C) 2007 David A. Schleef <ds@schleef.org>
|
||||||
|
*
|
||||||
|
* 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 <opencv2/opencv.hpp>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
|
||||||
|
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__ */
|
@ -0,0 +1,48 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* Library <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
* Copyright (C) 2007 David A. Schleef <ds@schleef.org>
|
||||||
|
*
|
||||||
|
* 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 <opencv2/opencv.hpp>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
#include <cv.h>
|
||||||
|
|
||||||
|
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__ */
|
@ -0,0 +1,954 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
|
* Copyright (C) 2014 Robert Jobbagy <jobbagy.robert@gmail.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* <refsect2>
|
||||||
|
* <title>Example launch line</title>
|
||||||
|
* |[
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* </refsect2>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 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 <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gstcameracalibration.h"
|
||||||
|
|
||||||
|
#if (CV_MAJOR_VERSION >= 3)
|
||||||
|
#include <opencv2/imgproc.hpp>
|
||||||
|
#endif
|
||||||
|
#include <opencv2/calib3d.hpp>
|
||||||
|
|
||||||
|
#include <gst/opencv/gstopencvutils.h>
|
||||||
|
|
||||||
|
#include "camerautils.hpp"
|
||||||
|
#include "cameraevent.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
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 <philippe_renon@yahoo.fr>");
|
||||||
|
|
||||||
|
/* 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<std::vector<cv::Point2f> > 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<cv::Point2f> 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<std::vector<cv::Point3f> >& objectPoints,
|
||||||
|
const std::vector<std::vector<cv::Point2f> >& imagePoints,
|
||||||
|
const std::vector<cv::Mat>& rvecs, const std::vector<cv::Mat>& tvecs,
|
||||||
|
const cv::Mat& cameraMatrix , const cv::Mat& distCoeffs,
|
||||||
|
std::vector<float>& perViewErrors, bool fisheye)
|
||||||
|
{
|
||||||
|
std::vector<cv::Point2f> 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<cv::Point3f>& 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<std::vector<cv::Point2f> > imagePoints, std::vector<cv::Mat>& rvecs, std::vector<cv::Mat>& tvecs,
|
||||||
|
std::vector<float>& reprojErrs, double& totalAvgErr)
|
||||||
|
{
|
||||||
|
//! [fixed_aspect]
|
||||||
|
cameraMatrix = cv::Mat::eye(3, 3, CV_64F);
|
||||||
|
if (calib->flags & cv::CALIB_FIX_ASPECT_RATIO) {
|
||||||
|
cameraMatrix.at<double>(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<std::vector<cv::Point3f> > 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<Mat>& rvecs, const vector<Mat>& tvecs,
|
||||||
|
// const vector<float>& reprojErrs, const vector<vector<Point2f> >& 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<vector<Point2f> > imagePoints)
|
||||||
|
//{
|
||||||
|
// vector<Mat> rvecs, tvecs;
|
||||||
|
// vector<float> 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<std::vector<cv::Point2f> > imagePoints)
|
||||||
|
{
|
||||||
|
std::vector<cv::Mat> rvecs, tvecs;
|
||||||
|
std::vector<float> 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;
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
|
* Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
|
||||||
|
#include <gst/opencv/gstopencvvideofilter.h>
|
||||||
|
|
||||||
|
#include <opencv2/core.hpp>
|
||||||
|
|
||||||
|
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<std::vector<cv::Point2f> > 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__ */
|
@ -0,0 +1,515 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
|
* Copyright (C) 2014 Robert Jobbagy <jobbagy.robert@gmail.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* <refsect2>
|
||||||
|
* <title>Example launch line</title>
|
||||||
|
* |[
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* </refsect2>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 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 <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "camerautils.hpp"
|
||||||
|
#include "cameraevent.hpp"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
|
#include "gstcameraundistort.h"
|
||||||
|
|
||||||
|
#if (CV_MAJOR_VERSION >= 3)
|
||||||
|
#include <opencv2/imgproc.hpp>
|
||||||
|
#endif
|
||||||
|
#include <opencv2/calib3d.hpp>
|
||||||
|
|
||||||
|
#include <gst/opencv/gstopencvutils.h>
|
||||||
|
|
||||||
|
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 <philippe_renon@yahoo.fr>");
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
|
* Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
|
||||||
|
#include <gst/opencv/gstopencvvideofilter.h>
|
||||||
|
//#include "gstopencvvideofilter.h"
|
||||||
|
|
||||||
|
#include <opencv2/core.hpp>
|
||||||
|
|
||||||
|
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__ */
|
@ -0,0 +1,178 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <2010> Thiago Santos <thiago.sousa.santos@collabora.co.uk>
|
||||||
|
*
|
||||||
|
* 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 <opencv2/core/core_c.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) <2010> Thiago Santos <thiago.sousa.santos@collabora.co.uk>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
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__ */
|
@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
|
||||||
|
*
|
||||||
|
* 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 <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gstopencvvideofilter.h"
|
||||||
|
#include "gstopencvutils.h"
|
||||||
|
|
||||||
|
#include <opencv2/core/core_c.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
#include <gst/video/gstvideofilter.h>
|
||||||
|
|
||||||
|
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__ */
|
33
ground/gcs/src/libs/gstreamer/plugins/plugins.pro
Normal file
@ -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
|
||||||
|
}
|
67
ground/gcs/src/libs/gstreamer/readme.txt
Normal file
@ -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 <ole.andre.ravnas@tandberg.com>
|
||||||
|
Haakon Sporsheim <hakon.sporsheim@tandberg.com>
|
||||||
|
Andres Colubri <andres.colubri@gmail.com>
|
||||||
|
|
||||||
|
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/
|
851
ground/gcs/src/libs/gstreamer/videowidget.cpp
Normal file
@ -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 <gst/gst.h>
|
||||||
|
#include <gst/video/videooverlay.h>
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// 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 = "<p align=center><font size=+2>" + getStatusMessage() + "</font></p>";
|
||||||
|
|
||||||
|
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 "<unknown>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<PrepareWindowIdEvent *>(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<StateChangedEvent *>(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<StreamStatusEvent *>(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<NewClockEvent *>(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<ClockProvideEvent *>(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<ClockLostEvent *>(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<ProgressEvent *>(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<LatencyEvent *>(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<QosEvent *>(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<ErrorEvent *>(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<WarningEvent *>(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<InfoEvent *>(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;
|
||||||
|
}
|
94
ground/gcs/src/libs/gstreamer/videowidget.h
Normal file
@ -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 <QWidget>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
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_ */
|
@ -11,6 +11,6 @@ SUBDIRS = \
|
|||||||
qwt \
|
qwt \
|
||||||
sdlgamepad
|
sdlgamepad
|
||||||
|
|
||||||
osg {
|
gstreamer:SUBDIRS += gstreamer
|
||||||
SUBDIRS += osgearth
|
|
||||||
}
|
osg:SUBDIRS += osgearth
|
||||||
|
@ -231,6 +231,13 @@ plugin_flightlog.depends += plugin_uavobjects
|
|||||||
plugin_flightlog.depends += plugin_uavtalk
|
plugin_flightlog.depends += plugin_uavtalk
|
||||||
SUBDIRS += plugin_flightlog
|
SUBDIRS += plugin_flightlog
|
||||||
|
|
||||||
|
# Video plugin
|
||||||
|
gstreamer {
|
||||||
|
plugin_video.subdir = video
|
||||||
|
plugin_video.depends = plugin_coreplugin
|
||||||
|
SUBDIRS += plugin_video
|
||||||
|
}
|
||||||
|
|
||||||
# Usage Tracker plugin
|
# Usage Tracker plugin
|
||||||
plugin_usagetracker.subdir = usagetracker
|
plugin_usagetracker.subdir = usagetracker
|
||||||
plugin_usagetracker.depends = plugin_coreplugin
|
plugin_usagetracker.depends = plugin_coreplugin
|
||||||
|
10
ground/gcs/src/plugins/video/VideoGadget.pluginspec
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<plugin name="VideoGadget" version="1.0.0" compatVersion="1.0.0">
|
||||||
|
<vendor>The LibrePilot Project</vendor>
|
||||||
|
<copyright>(C) 2017 The LibrePilot Project</copyright>
|
||||||
|
<license>The GNU Public License (GPL) Version 3</license>
|
||||||
|
<description>A video gadget</description>
|
||||||
|
<url>http://www.librepilot.org</url>
|
||||||
|
<dependencyList>
|
||||||
|
<dependency name="Core" version="1.0.0"/>
|
||||||
|
</dependencyList>
|
||||||
|
</plugin>
|
86
ground/gcs/src/plugins/video/helpdialog.cpp
Normal file
@ -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 <QDebug>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
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<QString> plugins; // = gst::pluginList();
|
||||||
|
|
||||||
|
// foreach(QString pluginName, plugins) {
|
||||||
|
// new QListWidgetItem(pluginName, m_helpDialog->elementListWidget);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
HelpDialog::~HelpDialog()
|
||||||
|
{
|
||||||
|
// foreach(QString category, m_categoryItemsMap.keys()) {
|
||||||
|
// QList<QTreeWidgetItem *> *categoryItemList = m_categoryItemsMap.value(category);
|
||||||
|
// delete categoryItemList;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelpDialog::itemSelected()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void HelpDialog::close()
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool HelpDialog::execDialog()
|
||||||
|
{
|
||||||
|
exec();
|
||||||
|
return true;
|
||||||
|
}
|
60
ground/gcs/src/plugins/video/helpdialog.h
Normal file
@ -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 <QDialog>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
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<QString> m_elements;
|
||||||
|
|
||||||
|
int m_windowWidth;
|
||||||
|
int m_windowHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HELPDIALOG_H
|
82
ground/gcs/src/plugins/video/helpdialog.ui
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>HelpDialog</class>
|
||||||
|
<widget class="QDialog" name="HelpDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>697</width>
|
||||||
|
<height>476</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="layoutWidget">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="pluginListWidget"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="elementListWidget"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QTextBrowser" name="textBrowser"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>HelpDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>297</x>
|
||||||
|
<y>361</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>297</x>
|
||||||
|
<y>193</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>HelpDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>297</x>
|
||||||
|
<y>361</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>297</x>
|
||||||
|
<y>193</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
BIN
ground/gcs/src/plugins/video/resources/22x22/media-eject.png
Normal file
After Width: | Height: | Size: 729 B |
After Width: | Height: | Size: 655 B |
After Width: | Height: | Size: 961 B |
After Width: | Height: | Size: 513 B |
BIN
ground/gcs/src/plugins/video/resources/22x22/media-record.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1003 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 990 B |
After Width: | Height: | Size: 1.0 KiB |
BIN
ground/gcs/src/plugins/video/resources/32x32/media-eject.png
Normal file
After Width: | Height: | Size: 987 B |
After Width: | Height: | Size: 481 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 571 B |
BIN
ground/gcs/src/plugins/video/resources/32x32/media-record.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.5 KiB |
37
ground/gcs/src/plugins/video/video.pro
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = VideoGadget
|
||||||
|
|
||||||
|
QT += widgets
|
||||||
|
|
||||||
|
include(../../plugin.pri)
|
||||||
|
include(../../plugins/coreplugin/coreplugin.pri)
|
||||||
|
include(../../libs/gstreamer/gstreamer.pri)
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
helpdialog.h \
|
||||||
|
videoplugin.h \
|
||||||
|
videogadgetconfiguration.h \
|
||||||
|
videogadget.h \
|
||||||
|
videogadgetwidget.h \
|
||||||
|
videogadgetfactory.h \
|
||||||
|
videogadgetoptionspage.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
helpdialog.cpp \
|
||||||
|
videoplugin.cpp \
|
||||||
|
videogadgetconfiguration.cpp \
|
||||||
|
videogadget.cpp \
|
||||||
|
videogadgetfactory.cpp \
|
||||||
|
videogadgetwidget.cpp \
|
||||||
|
videogadgetoptionspage.cpp
|
||||||
|
|
||||||
|
OTHER_FILES += \
|
||||||
|
VideoGadget.pluginspec
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
helpdialog.ui \
|
||||||
|
video.ui \
|
||||||
|
videooptionspage.ui
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
video.qrc
|
33
ground/gcs/src/plugins/video/video.qrc
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/video" >
|
||||||
|
<file>resources/22x22/media-eject.png</file>
|
||||||
|
<file>resources/32x32/media-eject.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-playback-pause.png</file>
|
||||||
|
<file>resources/32x32/media-playback-pause.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-playback-start.png</file>
|
||||||
|
<file>resources/32x32/media-playback-start.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-playback-stop.png</file>
|
||||||
|
<file>resources/32x32/media-playback-stop.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-record.png</file>
|
||||||
|
<file>resources/32x32/media-record.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-seek-backward.png</file>
|
||||||
|
<file>resources/32x32/media-seek-backward.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-seek-forward.png</file>
|
||||||
|
<file>resources/32x32/media-seek-forward.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-skip-backward.png</file>
|
||||||
|
<file>resources/32x32/media-skip-backward.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/media-skip-forward.png</file>
|
||||||
|
<file>resources/32x32/media-skip-forward.png</file>
|
||||||
|
|
||||||
|
<file>resources/22x22/utilities-terminal.png</file>
|
||||||
|
<file>resources/32x32/utilities-terminal.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
204
ground/gcs/src/plugins/video/video.ui
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>572</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="VideoWidget" name="video" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="consoleTextBrowser">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="lineWrapMode">
|
||||||
|
<enum>QTextEdit::NoWrap</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="startButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>:/video/resources/22x22/media-playback-start.png</normaloff>
|
||||||
|
<normalon>:/video/resources/22x22/media-playback-start.png</normalon>:/video/resources/22x22/media-playback-start.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pauseButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>:/video/resources/22x22/media-playback-pause.png</normaloff>
|
||||||
|
<normalon>:/video/resources/22x22/media-playback-pause.png</normalon>:/video/resources/22x22/media-playback-pause.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="stopButton">
|
||||||
|
<property name="mouseTracking">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>:/video/resources/22x22/media-playback-stop.png</normaloff>
|
||||||
|
<normalon>:/video/resources/22x22/media-playback-stop.png</normalon>:/video/resources/22x22/media-playback-stop.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="consoleButton">
|
||||||
|
<property name="mouseTracking">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>:/video/resources/22x22/utilities-terminal.png</normaloff>
|
||||||
|
<normalon>:/video/resources/22x22/utilities-terminal.png</normalon>:/video/resources/22x22/utilities-terminal.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>VideoWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>videowidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
46
ground/gcs/src/plugins/video/videogadget.cpp
Normal file
@ -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<VideoGadgetConfiguration *>(config);
|
||||||
|
|
||||||
|
m_widget->setConfiguration(m);
|
||||||
|
}
|
70
ground/gcs/src/plugins/video/videogadget.h
Normal file
@ -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 <coreplugin/iuavgadget.h>
|
||||||
|
#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<int> context() const
|
||||||
|
{
|
||||||
|
return m_context;
|
||||||
|
}
|
||||||
|
QWidget *widget()
|
||||||
|
{
|
||||||
|
return m_widget;
|
||||||
|
}
|
||||||
|
void loadConfiguration(IUAVGadgetConfiguration *config);
|
||||||
|
QString contextHelpId() const
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VideoGadgetWidget *m_widget;
|
||||||
|
QList<int> m_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VIDEOGADGET_H_
|
73
ground/gcs/src/plugins/video/videogadgetconfiguration.cpp
Normal file
@ -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);
|
||||||
|
}
|
104
ground/gcs/src/plugins/video/videogadgetconfiguration.h
Normal file
@ -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 <coreplugin/iuavgadgetconfiguration.h>
|
||||||
|
|
||||||
|
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
|
57
ground/gcs/src/plugins/video/videogadgetfactory.cpp
Normal file
@ -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 <coreplugin/uavgadgetoptionspagedecorator.h>
|
||||||
|
#include <coreplugin/iuavgadget.h>
|
||||||
|
|
||||||
|
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<VideoGadgetConfiguration *>(config));
|
||||||
|
}
|
51
ground/gcs/src/plugins/video/videogadgetfactory.h
Normal file
@ -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 <coreplugin/iuavgadgetfactory.h>
|
||||||
|
|
||||||
|
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_
|
82
ground/gcs/src/plugins/video/videogadgetoptionspage.cpp
Normal file
@ -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();
|
||||||
|
}
|
80
ground/gcs/src/plugins/video/videogadgetoptionspage.h
Normal file
@ -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 <QLabel>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
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
|
170
ground/gcs/src/plugins/video/videogadgetwidget.cpp
Normal file
@ -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 <QtCore>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
68
ground/gcs/src/plugins/video/videogadgetwidget.h
Normal file
@ -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 <QFrame>
|
||||||
|
#include <QtCore/QEvent>
|
||||||
|
#include <QtGui/QResizeEvent>
|
||||||
|
#include <QtGui/QPaintEvent>
|
||||||
|
|
||||||
|
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_ */
|
96
ground/gcs/src/plugins/video/videooptionspage.ui
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>VideoOptionsPage</class>
|
||||||
|
<widget class="QWidget" name="VideoOptionsPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>378</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="descLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Pipeline:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="descPlainTextEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="displayVideoCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Display video</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="displayControlsCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Display controls</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="infoPlainTextEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="infoLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Info:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="autoStartCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Auto Start</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QCheckBox" name="respectAspectRatioCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Respect aspect ratio</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="helpButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Help</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
64
ground/gcs/src/plugins/video/videoplugin.cpp
Normal file
@ -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 <extensionsystem/pluginmanager.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QtPlugin>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
51
ground/gcs/src/plugins/video/videoplugin.h
Normal file
@ -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 <extensionsystem/iplugin.h>
|
||||||
|
|
||||||
|
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_ */
|
@ -2785,6 +2785,128 @@
|
|||||||
</data>
|
</data>
|
||||||
</default>
|
</default>
|
||||||
</UAVObjectBrowser>
|
</UAVObjectBrowser>
|
||||||
|
<VideoGadget>
|
||||||
|
<Default>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>true</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>videotestsrc ! autovideosink</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>false</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</Default>
|
||||||
|
<Screen__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file__PCT__20__PCT__28fast__PCT__20__PCT__26__PCT__20big__PCT__29>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>false</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>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</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>true</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</Screen__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file__PCT__20__PCT__28fast__PCT__20__PCT__26__PCT__20big__PCT__29>
|
||||||
|
<Screen__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>false</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>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</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>true</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</Screen__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file>
|
||||||
|
<Screen__PCT__20-__PCT__20Play__PCT__20__PCT__28640x480__PCT__29>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>false</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>dx9screencapsrc monitor=0 cursor=true x=0 y=0 width=640 height=480 ! autovideosink</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>false</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</Screen__PCT__20-__PCT__20Play__PCT__20__PCT__28640x480__PCT__29>
|
||||||
|
<Screen__PCT__20-__PCT__20Play>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>true</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>dx9screencapsrc monitor=0 cursor=true ! autovideosink</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>false</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</Screen__PCT__20-__PCT__20Play>
|
||||||
|
<USB__PCT__20Camera__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file__PCT__20__PCT__28fast__PCT__20__PCT__26__PCT__20big__PCT__29>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>false</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>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</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>true</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</USB__PCT__20Camera__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file__PCT__20__PCT__28fast__PCT__20__PCT__26__PCT__20big__PCT__29>
|
||||||
|
<USB__PCT__20Camera__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>false</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>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</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>true</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</USB__PCT__20Camera__PCT__20-__PCT__20Capture__PCT__20to__PCT__20file>
|
||||||
|
<USB__PCT__20Camera__PCT__20-__PCT__20Play>
|
||||||
|
<configInfo>
|
||||||
|
<locked>false</locked>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
</configInfo>
|
||||||
|
<data>
|
||||||
|
<autoStart>true</autoStart>
|
||||||
|
<displayControls>false</displayControls>
|
||||||
|
<displayVideo>true</displayVideo>
|
||||||
|
<pipelineDesc>ksvideosrc device-index=0 ! autovideosink</pipelineDesc>
|
||||||
|
<pipelineInfo></pipelineInfo>
|
||||||
|
<respectAspectRatio>false</respectAspectRatio>
|
||||||
|
</data>
|
||||||
|
</USB__PCT__20Camera__PCT__20-__PCT__20Play>
|
||||||
|
</VideoGadget>
|
||||||
<configInfo>
|
<configInfo>
|
||||||
<locked>false</locked>
|
<locked>false</locked>
|
||||||
<version>1.2.0</version>
|
<version>1.2.0</version>
|
||||||
|