From 5e14d4bda4efdc2dc56343816557bf6d20c8da64 Mon Sep 17 00:00:00 2001 From: Viktor Anjin Date: Thu, 26 Feb 2015 23:02:22 +0700 Subject: [PATCH] initial commit --- .gitignore | 8 + pipeviz.pro | 42 ++ readme.md | 23 + src/CustomSettings.cpp | 38 ++ src/CustomSettings.h | 19 + src/ElementProperties.cpp | 280 +++++++++++ src/ElementProperties.h | 33 ++ src/GraphDisplay.cpp | 960 ++++++++++++++++++++++++++++++++++++++ src/GraphDisplay.h | 89 ++++ src/GraphManager.cpp | 384 +++++++++++++++ src/GraphManager.h | 101 ++++ src/MainWindow.cpp | 366 +++++++++++++++ src/MainWindow.h | 56 +++ src/PadProperties.cpp | 95 ++++ src/PadProperties.h | 17 + src/PipelineIE.cpp | 610 ++++++++++++++++++++++++ src/PipelineIE.h | 15 + src/PluginsList.cpp | 209 +++++++++ src/PluginsList.h | 41 ++ src/SeekSlider.cpp | 16 + src/SeekSlider.h | 16 + src/main.cpp | 15 + src/verinfo/verinfo.sh | 19 + src/version | 2 + 24 files changed, 3454 insertions(+) create mode 100644 .gitignore create mode 100644 pipeviz.pro create mode 100644 readme.md create mode 100644 src/CustomSettings.cpp create mode 100644 src/CustomSettings.h create mode 100644 src/ElementProperties.cpp create mode 100644 src/ElementProperties.h create mode 100644 src/GraphDisplay.cpp create mode 100644 src/GraphDisplay.h create mode 100644 src/GraphManager.cpp create mode 100644 src/GraphManager.h create mode 100644 src/MainWindow.cpp create mode 100644 src/MainWindow.h create mode 100644 src/PadProperties.cpp create mode 100644 src/PadProperties.h create mode 100644 src/PipelineIE.cpp create mode 100644 src/PipelineIE.h create mode 100644 src/PluginsList.cpp create mode 100644 src/PluginsList.h create mode 100644 src/SeekSlider.cpp create mode 100644 src/SeekSlider.h create mode 100644 src/main.cpp create mode 100755 src/verinfo/verinfo.sh create mode 100644 src/version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62ab9d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +Makefile* +debug/* +object_script* +pipeviz_plugin_import.cpp +pipeviz +*.o +moc_* +src/version_info.h diff --git a/pipeviz.pro b/pipeviz.pro new file mode 100644 index 0000000..cf90273 --- /dev/null +++ b/pipeviz.pro @@ -0,0 +1,42 @@ +###################################################################### +# Automatically generated by qmake (3.0) ?? ???. 22 21:50:14 2014 +###################################################################### + +CONFIG += qt debug +TEMPLATE = app +TARGET = pipeviz +QT += widgets +QT += xml +QT += core +INCLUDEPATH += . src + +CONFIG += link_pkgconfig +PKGCONFIG += gstreamer-1.0 + +gitinfo.commands = src/verinfo/verinfo.sh src/version src/version_info.h +gitinfo.target = gitinfo + +QMAKE_EXTRA_TARGETS += gitinfo + + +# Input +HEADERS += src/PluginsList.h \ + src/MainWindow.h \ + src/GraphManager.h \ + src/GraphDisplay.h \ + src/ElementProperties.h \ + src/PadProperties.h \ + src/PipelineIE.h \ + src/CustomSettings.h \ + src/SeekSlider.h + +SOURCES += src/main.cpp \ + src/PluginsList.cpp \ + src/MainWindow.cpp \ + src/GraphManager.cpp \ + src/GraphDisplay.cpp \ + src/ElementProperties.cpp \ + src/PadProperties.cpp \ + src/PipelineIE.cpp \ + src/CustomSettings.cpp \ + src/SeekSlider.cpp diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8eafc15 --- /dev/null +++ b/readme.md @@ -0,0 +1,23 @@ +pipeviz +========== + +Pipeviz is a graphedit for gstreamer-1.0. This is a gui tool for constructing and testing gstreamer pipelines. + +It allows you: + +* to construct the pipelines via the gui interface + +* to test different types of pipes easy + +* save and open your graphs + +Who might be interested in it? + +* quality assurance + +* technical support + +* software engineers + + +![alt tag](https://cloud.githubusercontent.com/assets/10683398/6396608/94f89e3a-be09-11e4-982c-5bf3a57bc6f4.png) diff --git a/src/CustomSettings.cpp b/src/CustomSettings.cpp new file mode 100644 index 0000000..7fdbc86 --- /dev/null +++ b/src/CustomSettings.cpp @@ -0,0 +1,38 @@ +#include "CustomSettings.h" + +#include + +#define COMPANY_NAME "virinext" +#define APPLICATION_NAME "pipeviz" + +void CustomSettings::saveLastIODirectory(const QString &name) +{ + QSettings settings(COMPANY_NAME, APPLICATION_NAME); + settings.setValue("last_directory", name); +} + + +QString CustomSettings::lastIODirectory() +{ + QSettings settings(COMPANY_NAME, APPLICATION_NAME); + QString res = settings.value("last_directory").toString(); + + if(res.isEmpty()) + res = "./"; + + return res; +} + + +void CustomSettings::saveMainWindowGeometry(const QByteArray &geometry) +{ + QSettings settings(COMPANY_NAME, APPLICATION_NAME); + settings.setValue("geometry", geometry); +} + + +QByteArray CustomSettings::mainWindowGeometry() +{ + QSettings settings(COMPANY_NAME, APPLICATION_NAME); + return settings.value("geometry").toByteArray(); +} diff --git a/src/CustomSettings.h b/src/CustomSettings.h new file mode 100644 index 0000000..8c0729f --- /dev/null +++ b/src/CustomSettings.h @@ -0,0 +1,19 @@ +#ifndef CUSTOM_SETTINGS_H_ +#define CUSTOM_SETTINGS_H_ + +#include +#include + + +namespace CustomSettings +{ + void saveLastIODirectory(const QString &name); + QString lastIODirectory(); + + void saveMainWindowGeometry(const QByteArray &geometry); + QByteArray mainWindowGeometry(); +} + + + +#endif diff --git a/src/ElementProperties.cpp b/src/ElementProperties.cpp new file mode 100644 index 0000000..fcc6c32 --- /dev/null +++ b/src/ElementProperties.cpp @@ -0,0 +1,280 @@ +#include "ElementProperties.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +ElementProperties::ElementProperties(QSharedPointer pGraph, const char *name, + QWidget *parent, Qt::WindowFlags flags): +QWidget(parent, flags), +m_pGraphManager(pGraph), +m_name(name) +{ + setWindowTitle(QString(name) + " properties"); + + create(); +} + + + +void ElementProperties::create() +{ + GstElement *element = gst_bin_get_by_name (GST_BIN(m_pGraphManager -> m_pGraph), m_name.toStdString().c_str()); + + if(!element) + return; + + QGridLayout *play = new QGridLayout; + + GParamSpec **prop_specs; + guint num_props; + + prop_specs = g_object_class_list_properties(G_OBJECT_GET_CLASS (element), + &num_props); + + for(std::size_t i = 0; i value_type); + if(param -> flags & G_PARAM_READABLE) + g_object_get_property (G_OBJECT(element), param -> name, &value); + else + { + const GValue *valueDef = g_param_spec_get_default_value(param); + g_value_copy(valueDef, &value); + } + + if(param->flags & G_PARAM_WRITABLE) + readOnly = false; + + QString propertyName = g_param_spec_get_name (param); + QString propertyValue; + + bool skip = false; + + switch (G_VALUE_TYPE (&value)) + { + case G_TYPE_STRING: + { + const char *string_val = g_value_get_string (&value); + propertyValue = string_val; + break; + } + case G_TYPE_BOOLEAN: + { + gboolean bool_val = g_value_get_boolean (&value); + propertyValue = QString::number(bool_val); + break; + } + case G_TYPE_ULONG: + { + propertyValue = QString::number(g_value_get_ulong(&value)); + break; + } + case G_TYPE_LONG: + { + propertyValue = QString::number(g_value_get_long(&value)); + break; + } + case G_TYPE_UINT: + { + propertyValue = QString::number(g_value_get_uint(&value)); + break; + } + case G_TYPE_INT: + { + propertyValue = QString::number(g_value_get_int(&value)); + break; + } + case G_TYPE_UINT64: + { + propertyValue = QString::number(g_value_get_uint64(&value)); + break; + } + case G_TYPE_INT64: + { + propertyValue = QString::number(g_value_get_int64(&value)); + break; + } + case G_TYPE_FLOAT: + { + propertyValue = QString::number(g_value_get_float(&value)); + break; + } + case G_TYPE_DOUBLE: + { + propertyValue = QString::number(g_value_get_double(&value)); + break; + } + default: + { + skip = true; + qDebug() << "property " << propertyName << " not supported"; + break; + } + }; + + play -> addWidget(new QLabel(propertyName), i, 0); + + QLineEdit *ple = new QLineEdit(propertyValue); + ple -> setReadOnly(readOnly); + play -> addWidget(ple, i, 1); + if(!skip) + m_values.insert(propertyName, ple); + else + ple -> setReadOnly(true); + } + + QVBoxLayout *pvblay = new QVBoxLayout; + QWidget *pwgt = new QWidget(this); + pwgt -> setLayout(play); + QScrollArea *pscroll = new QScrollArea(this); + pscroll -> setWidget(pwgt); + + pvblay -> addWidget(pscroll); + + QHBoxLayout *phblay = new QHBoxLayout; + + QPushButton *pcmdApply = new QPushButton("Apply"); + QPushButton *pcmdOk = new QPushButton("OK"); + QPushButton *pcmdCancel = new QPushButton("Cancel"); + + + phblay -> addStretch(1); + phblay -> addWidget(pcmdApply); + phblay -> addWidget(pcmdCancel); + phblay -> addWidget(pcmdOk); + + pvblay -> addLayout(phblay); + + QObject::connect(pcmdApply, SIGNAL(clicked()), this, SLOT(applyClicked())); + QObject::connect(pcmdCancel, SIGNAL(clicked()), this, SLOT(close())); + QObject::connect(pcmdOk, SIGNAL(clicked()), this, SLOT(okClicked())); + + setLayout(pvblay); + + g_free(prop_specs); + gst_object_unref(element); + +} + +void ElementProperties::applyClicked() +{ + GstElement *element = gst_bin_get_by_name (GST_BIN(m_pGraphManager -> m_pGraph), + m_name.toStdString().c_str()); + + if(!element) + return; + + QMap::iterator itr = m_values.begin(); + + for(;itr != m_values.end(); itr++) + { + GParamSpec *param = g_object_class_find_property(G_OBJECT_GET_CLASS (element), + itr.key().toStdString().c_str()); + + if(!param) + { + qDebug() << "problem with setting " << itr.key() << " property"; + continue; + } + + if(!(param -> flags & G_PARAM_WRITABLE)) + continue; + + QString valStr = itr.value() -> text(); + std::string tmpStr = itr.key().toStdString(); + const char *propName = tmpStr.c_str(); + switch (param -> value_type) + { + case G_TYPE_STRING: + { + g_object_set(G_OBJECT(element), propName, valStr.toStdString().c_str(), NULL); + break; + } + case G_TYPE_BOOLEAN: + { + gboolean val = valStr.toInt(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_ULONG: + { + gulong val = valStr.toULong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_LONG: + { + glong val = valStr.toLong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_UINT: + { + guint val = valStr.toUInt(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_INT: + { + gint val = valStr.toInt(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_UINT64: + { + guint64 val = valStr.toULongLong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_INT64: + { + gint64 val = valStr.toLongLong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_FLOAT: + { + gfloat val = valStr.toFloat(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_DOUBLE: + { + gdouble val = valStr.toDouble(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + default: + { + qDebug() << "property " << itr.key() << " not supported"; + break; + } + }; + } + + gst_object_unref(element); + + delete layout(); + qDeleteAll(children()); + + + create(); +} + +void ElementProperties::okClicked() +{ + applyClicked(); + close(); +} diff --git a/src/ElementProperties.h b/src/ElementProperties.h new file mode 100644 index 0000000..bd0abe0 --- /dev/null +++ b/src/ElementProperties.h @@ -0,0 +1,33 @@ +#ifndef ELEMENT_PROPERTIES_H_ +#define ELEMENT_PROPERTIES_H_ + +#include +#include +#include +#include +#include + +#include "GraphManager.h" + +class ElementProperties: public QWidget +{ +Q_OBJECT +public: + ElementProperties(QSharedPointer pGraphManager, const char *name, + QWidget *parent = 0, Qt::WindowFlags flags = 0); + + +private slots: + void applyClicked(); + void okClicked(); + +private: + QSharedPointer m_pGraphManager; + QMap m_values; + QString m_name; + + void create(); +}; + + +#endif diff --git a/src/GraphDisplay.cpp b/src/GraphDisplay.cpp new file mode 100644 index 0000000..9f4ab8b --- /dev/null +++ b/src/GraphDisplay.cpp @@ -0,0 +1,960 @@ +#include "GraphDisplay.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ElementProperties.h" +#include "PadProperties.h" + +#define PAD_SIZE 8 +#define PAD_SIZE_ACTION 16 + + +GraphDisplay::GraphDisplay(QWidget *parent, Qt::WindowFlags f): +QWidget(parent, f) +{ + setFocusPolicy(Qt::WheelFocus); +} + +void GraphDisplay::updateDisplayInfoIds() +{ + for(std::size_t i=0; i &info) +{ + bool needUpdate = false; + + if(m_info.size() != info.size()) + needUpdate = true; + + if(!needUpdate) + { + for(std::size_t i=0; i= 1) + height += (std::max(numInPads, numOutPads) - 1) * 25; + + int curX, curY; + curX = curY = 10; + + QRect rect(curX, curY, width, height); + + while(true) + { + rect = QRect(curX, curY, width, height); + QRect rectTest(curX, curY - 15, width + 15, height + 15); + bool noIntersects = true; + for(std::size_t i=0; i= m_displayInfo.size()) + break; + } + + + std::size_t i=0; + for(; i reorderedDisplayInfo(m_info.size()); + for(std::size_t i=0; i pos(), elementId, padId); + + + if(event -> buttons() & Qt::RightButton) + { + showContextMenu(event); + } + else + { + if(padId != ((size_t)-1)) + { + m_moveInfo.m_padId = padId; + m_moveInfo.m_elementId = elementId; + m_moveInfo.m_position = event -> pos(); + m_moveInfo.m_action = MakeConnect; + m_moveInfo.m_startPosition = event -> pos(); + } + else if(elementId != ((size_t)-1)) + { + m_moveInfo.m_elementId = elementId; + m_moveInfo.m_padId = -1; + m_moveInfo.m_position = event -> pos(); + m_moveInfo.m_action = MoveComponent; + m_moveInfo.m_startPosition = event -> pos(); + } + else + { + m_moveInfo.m_startPosition = event -> pos(); + m_moveInfo.m_action = Select; + m_moveInfo.m_position = QPoint(); + } + } + + for(std::size_t i=0; i pos(), elementId, padId); + + if(elementId != ((size_t)-1) && padId != ((size_t)-1)) + { + ElementInfo infoSrc, infoDst; + const char *srcPad, *dstPad; + srcPad = NULL; + dstPad = NULL; + + for(std::size_t i=0; i Connect(infoSrc.m_name.c_str(), srcPad, infoDst.m_name.c_str(), dstPad)) + { + QString msg; + msg = "Connection "; + msg += QString(infoSrc.m_name.c_str()) + ":" + srcPad; + msg += " => "; + msg += QString(infoDst.m_name.c_str()) + ":" + dstPad; + msg += " was FAILED"; + + QMessageBox::warning(this, "Coonection failed", msg); + } + m_info = m_pGraph -> GetInfo(); + updateDisplayInfoIds(); + } + } + else if(m_moveInfo.m_action == Select) + { + std::size_t width = std::abs(m_moveInfo.m_position.x() - m_moveInfo.m_startPosition.x()); + std::size_t height = std::abs(m_moveInfo.m_position.y() - m_moveInfo.m_startPosition.y()); + + if(!m_moveInfo.m_position.isNull() && width * height > 5) + { + QRect selectionRect(m_moveInfo.m_startPosition, m_moveInfo.m_position); + for(std::size_t i=0; i x() - m_moveInfo.m_startPosition.x(); + int dy = event -> y() - m_moveInfo.m_startPosition.y(); + + if(dx == dy && dy == 0) + { + for(std::size_t i=0; i x() - m_moveInfo.m_position.x(); + int dy = event -> y() - m_moveInfo.m_position.y(); + + for(std::size_t i=0; i pos(); + repaint(); + } +} + + + +void GraphDisplay::keyPressEvent(QKeyEvent* event) +{ + if(event -> key() == Qt::Key_Delete) + removeSelected(); + + return QWidget::keyPressEvent(event); +} + + +void GraphDisplay::showContextMenu(QMouseEvent *event) +{ + QMenu menu; + + std::size_t elementId, padId; + getIdByPosition(event -> pos(), elementId, padId); + + GstState state; + GstStateChangeReturn res = gst_element_get_state (m_pGraph -> m_pGraph, &state, NULL, GST_MSECOND); + + bool isActive = false; + + if(res != GST_STATE_CHANGE_SUCCESS || state == GST_STATE_PAUSED || state == GST_STATE_PLAYING) + isActive = true; + + bool hasSelection = false; + for(std::size_t i=0; i setDisabled(true); + + } + else if(padId != ((size_t)-1)) + menu.addAction("Pad properties"); + else if(elementId != ((size_t)-1)) + { + menu.addAction("Element properties"); + QAction *pact = menu.addAction("Remove"); + + if(isActive) + pact -> setDisabled(true); + + pact = menu.addAction("Request pad..."); + } + else + { + + for(std::size_t i=0; i pos().x(); + double y0 = event -> pos().y(); + + double distance = std::abs((int)(dy * x0 - dx * y0 + x2 * y1 - y2 * x1)); + distance = distance / sqrt(dy * dy + dx * dx); + + if(distance < 5) + { + elementId = m_info[i].m_id; + padId = m_info[i].m_pads[j].m_id; + + QAction *pact = menu.addAction("Disconnect"); + + if(isActive) + pact -> setDisabled(true); + break; + } + } + } + if(!menu.isEmpty()) + break; + } + } + + if(!menu.isEmpty()) + { + QAction *pact = menu.exec(event -> globalPos()); + if(pact) + { + if(pact -> text() == "Remove") + removePlugin(elementId); + else if(pact -> text() == "Element properties") + showElementProperties(elementId); + else if(pact -> text() == "Pad properties") + showPadProperties(elementId, padId); + else if(pact -> text() == "Disconnect") + disconnect(elementId, padId); + else if(pact -> text() == "Request pad...") + requestPad(elementId); + else if(pact -> text() == "Remove selected") + removeSelected(); + } + } +} + + +void GraphDisplay::removeSelected() +{ + GstState state; + GstStateChangeReturn res = gst_element_get_state (m_pGraph -> m_pGraph, &state, NULL, GST_MSECOND); + + if(res != GST_STATE_CHANGE_SUCCESS || state == GST_STATE_PAUSED || state == GST_STATE_PLAYING) + return; + + while(true) + { + std::size_t i=0; + for(; i RemovePlugin(m_info[i].m_name.c_str())) + { + std::vector info = m_pGraph -> GetInfo(); + update(info); + } + else + QMessageBox::warning(this, "Element removing problem", "Element `" + QString(m_info[i].m_name.c_str()) + "` remowing was FAILED"); + + } +} + + +void GraphDisplay::showElementProperties(std::size_t id) +{ + std::size_t i=0; + for(; i setAttribute(Qt::WA_QuitOnClose, false); + pprops -> show(); + } +} + +void GraphDisplay::showPadProperties(std::size_t elementId, std::size_t padId) +{ + std::size_t i=0; + for(; i setAttribute(Qt::WA_QuitOnClose, false); + pprops -> show(); + } + } +} + +void GraphDisplay::disconnect(size_t elementId, size_t padId) +{ + std::string src, dst, srcPad, dstPad; + + for(std::size_t i=0; i " << dst.c_str() << ":" << dstPad.c_str(); + + if(src.empty() || dst.empty() || srcPad.empty() || dstPad.empty()) + return; + + + m_pGraph -> Disconnect(src.c_str(), srcPad.c_str(), dst.c_str(), dstPad.c_str()); + + m_info = m_pGraph -> GetInfo(); + updateDisplayInfoIds(); + repaint(); +} + + +void GraphDisplay::requestPad(std::size_t elementId) +{ + QStringList labels; + labels.push_back("Template name"); + labels.push_back("Caps"); + labels.push_back("Direction"); + + QTableWidget *ptwgt = new QTableWidget(); + ptwgt -> setColumnCount(3); + ptwgt -> setHorizontalHeaderLabels(labels); + ptwgt -> setSelectionBehavior(QAbstractItemView::SelectRows); + ptwgt -> setEditTriggers(QAbstractItemView::NoEditTriggers); + + GstElement *element = NULL; + for(std::size_t i=0; i m_pGraph), m_info[i].m_name.c_str()); + break; + } + } + + if(!element) + { + QMessageBox::warning(this, "Request pad failed", "Request pad list obtaining was failed"); + return; + } + + GstElementClass *klass; + klass = GST_ELEMENT_GET_CLASS(element); + + GList *lst = gst_element_class_get_pad_template_list(klass); + + std::size_t k = 0; + while (lst != NULL) + { + GstPadTemplate *templ; + templ = (GstPadTemplate *) lst -> data; + + if(GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_REQUEST) + { + ptwgt -> setRowCount(k + 1); + ptwgt -> setItem(k, 0, new QTableWidgetItem(GST_PAD_TEMPLATE_NAME_TEMPLATE(templ))); + + GstCaps *caps = gst_pad_template_get_caps(templ); + gchar *capsStr = gst_caps_to_string(caps); + ptwgt -> setItem(k, 1, new QTableWidgetItem(capsStr)); + g_free(capsStr); + gst_caps_unref(caps); + + const char *directionSrc = "SRC"; + const char *directionSink = "SINK"; + const char *directionUnknown = "UNKNOWN"; + + QString direction; + switch(GST_PAD_TEMPLATE_DIRECTION(templ)) + { + case GST_PAD_UNKNOWN: + direction = directionUnknown; + break; + + case GST_PAD_SRC: + direction = directionSrc; + break; + + case GST_PAD_SINK: + direction = directionSink; + break; + }; + + ptwgt -> setItem(k, 2, new QTableWidgetItem(direction)); + k++; + } + + lst = g_list_next(lst); + } + + qulonglong v((qulonglong)element); + ptwgt -> setProperty("element", v); + + connect(ptwgt, SIGNAL(cellActivated(int, int)), SLOT(addRequestPad(int, int))); + + ptwgt -> setAttribute(Qt::WA_QuitOnClose, false); + ptwgt -> show(); +} + +void GraphDisplay::addRequestPad(int row, int collumn) +{ + QTableWidget *ptwgt = dynamic_cast (QObject::sender()); + + qulonglong v = ptwgt -> property("element").toULongLong(); + GstElement *element = (GstElement *) v; + GstElementClass *klass = GST_ELEMENT_GET_CLASS(element); + + GstPadTemplate *templ = gst_element_class_get_pad_template(klass, ptwgt -> itemAt(row, 0) -> text().toStdString().c_str()); + + gst_element_request_pad(element, templ, NULL, NULL); + + gst_object_unref(element); + ptwgt -> close(); + + std::vector info = m_pGraph -> GetInfo(); + update(info); + +} + +void GraphDisplay::getIdByPosition(const QPoint &pos, std::size_t &elementId, std::size_t &padId) +{ + std::size_t i=0; + elementId = padId = -1; + for(; i + +#include +#include +#include + + +#include "GraphManager.h" +#include + + + + +class GraphDisplay: public QWidget +{ +Q_OBJECT + +public: + GraphDisplay(QWidget *parent=0, Qt::WindowFlags f=0); + void update(const std::vector &info); + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + void keyPressEvent(QKeyEvent* event); + + + QSharedPointer m_pGraph; + +private slots: + void addRequestPad(int row, int collumn); + +private: + + enum MoveAction + { + None = 0, + MoveComponent, + MakeConnect, + Select + }; + + struct MoveInfo + { + MoveInfo(): m_action(None) + { + } + + MoveAction m_action; + size_t m_elementId; + size_t m_padId; + QPoint m_position; + QPoint m_startPosition; + }; + + + struct ElementDisplayInfo + { + QRect m_rect; + size_t m_id; + std::string m_name; + bool m_isSelected; + }; + + void calculatePositions(); + void updateDisplayInfoIds(); + ElementDisplayInfo calculateOnePosition(const ElementInfo &info); + void showContextMenu(QMouseEvent *event); + void showElementProperties(std::size_t id); + void showPadProperties(std::size_t elementId, std::size_t padId); + void removePlugin(std::size_t id); + void removeSelected(); + void getIdByPosition(const QPoint &pos, std::size_t &elementId, std::size_t &padId); + QPoint getPadPosition(std::size_t elementId, std::size_t padId); + void disconnect(std::size_t elementId, std::size_t padId); + void requestPad(std::size_t elementId); + + std::vector m_info; + std::vector m_displayInfo; + + MoveInfo m_moveInfo; +}; + + +#endif diff --git a/src/GraphManager.cpp b/src/GraphManager.cpp new file mode 100644 index 0000000..85e7291 --- /dev/null +++ b/src/GraphManager.cpp @@ -0,0 +1,384 @@ +#include "GraphManager.h" +#include + +#include +#include +#include +#include + +#include "CustomSettings.h" + +GraphManager::GraphManager() +{ + m_pGraph = gst_pipeline_new ("pipeline"); +} + + +GraphManager::~GraphManager() +{ + +} + + +bool GraphManager::AddPlugin(const char *plugin, const char *name) +{ + GstElement *pel = gst_element_factory_make(plugin, name); + if(!pel) + return false; + + if(GST_IS_URI_HANDLER(pel)) + { + static const gchar *const *protocols; + protocols = gst_uri_handler_get_protocols(GST_URI_HANDLER(pel)); + + bool isFile = false; + + for(std::size_t i=0; protocols[i] != NULL; i++) + { + if(strcmp("file", protocols[i]) == 0) + { + isFile = true; + break; + } + } + + + if(isFile) + { + QString path; + QString dir = CustomSettings::lastIODirectory(); + + if(gst_uri_handler_get_uri_type(GST_URI_HANDLER(pel)) == GST_URI_SRC) + path = QFileDialog::getOpenFileName(NULL, "Open Source File...", dir); + else + path = QFileDialog::getSaveFileName(NULL, "Open Sink File...", dir); + + if(!path.isEmpty()) + { + gchar *uri = gst_filename_to_uri(path.toStdString().c_str(), NULL); + if(uri) + { + qDebug() << "Set uri: " << uri; + gst_uri_handler_set_uri(GST_URI_HANDLER(pel), uri, NULL); + g_free(uri); + + QString dir = QFileInfo(path).absoluteDir().absolutePath(); + CustomSettings::saveLastIODirectory(dir); + } + } + } + else + { + QString uri = QInputDialog::getText(NULL, "Uri...", "Uri:"); + + if(!uri.isEmpty()) + { + qDebug() << "Set uri: " << uri; + gst_uri_handler_set_uri(GST_URI_HANDLER(pel), uri.toStdString().c_str(), NULL); + } + + } + + + } + + bool res = gst_bin_add(GST_BIN(m_pGraph), pel); + if(res) + gst_element_sync_state_with_parent(pel); + + return res; +} + + +bool GraphManager::RemovePlugin(const char *name) +{ + GstElement *element = gst_bin_get_by_name (GST_BIN(m_pGraph), name); + + if(!element) + return false; + + bool res = gst_bin_remove (GST_BIN(m_pGraph), element); + gst_object_unref(element); + + return res; +} + + +bool GraphManager::OpenUri(const char *uri, const char *name) +{ + GstElement *element = gst_element_make_from_uri(GST_URI_SRC, uri, name, NULL); + if(!element) + return false; + + bool res = gst_bin_add(GST_BIN(m_pGraph), element); + if(res) + gst_element_sync_state_with_parent(element); + + return res; +} + + +bool GraphManager::Connect(const char *srcElement, const char *srcPad, + const char *dstElement, const char *dstPad) +{ + GstElement *src = gst_bin_get_by_name (GST_BIN(m_pGraph), srcElement); + GstElement *dst = gst_bin_get_by_name (GST_BIN(m_pGraph), dstElement); + + bool res = gst_element_link_pads(src, srcPad, dst, dstPad); + + gboolean seekRes = gst_element_seek_simple(m_pGraph, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, 0); + + gst_object_unref(src); + gst_object_unref(dst); + + return res; +} + + + +bool GraphManager::Disconnect(const char *srcElement, const char *srcPad, + const char *dstElement, const char *dstPad) +{ + GstElement *src = gst_bin_get_by_name (GST_BIN(m_pGraph), srcElement); + GstElement *dst = gst_bin_get_by_name (GST_BIN(m_pGraph), dstElement); + + + gst_element_unlink_pads(src, srcPad, dst, dstPad); + + gst_object_unref(src); + gst_object_unref(dst); + + return true; +} + + +std::vector GraphManager::GetInfo() +{ + std::vector res; + + GstIterator *iter; + iter = gst_bin_iterate_elements (GST_BIN (m_pGraph)); + bool done = false; + size_t id = 0; + while (!done) + { + GValue value = { 0 }; + switch (gst_iterator_next (iter, &value)) + { + case GST_ITERATOR_OK: + { + ElementInfo elementInfo; + + elementInfo.m_id = id; + id++; + GstElement *element = GST_ELEMENT(g_value_get_object(&value)); + + gchar *name = gst_element_get_name(element); + elementInfo.m_name = name; + g_free(name); + + GstElementFactory *pfactory = + gst_element_get_factory(element); + + elementInfo.m_pluginName = + gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(pfactory)); + + GstIterator *padItr = gst_element_iterate_pads (element); + bool padDone = FALSE; + std::size_t padId = 0; + while (!padDone) + { + GValue padVal = { 0 }; + switch (gst_iterator_next (padItr, &padVal)) + { + case GST_ITERATOR_OK: + { + GstPad *pad = GST_PAD(g_value_get_object(&padVal)); + + PadInfo padInfo; + padInfo.m_id = padId; + + gchar *pad_name = gst_pad_get_name(pad); + padInfo.m_name = pad_name; + g_free(pad_name); + + GstPadDirection direction = gst_pad_get_direction(pad); + if(direction == GST_PAD_SRC) + padInfo.m_type = PadInfo::Out; + else if(direction == GST_PAD_SINK) + padInfo.m_type = PadInfo::In; + else + padInfo.m_type = PadInfo::None; + + elementInfo.m_pads.push_back(padInfo); + g_value_reset (&padVal); + break; + } + case GST_ITERATOR_RESYNC: + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + padDone = TRUE; + break; + }; + padId++; + } + + g_value_reset (&value); + res.push_back(elementInfo); + break; + } + case GST_ITERATOR_DONE: + case GST_ITERATOR_RESYNC: + case GST_ITERATOR_ERROR: + { + done = true; + break; + } + }; + } + + gst_iterator_free (iter); + + for(std::size_t i=0; i + +#include +#include + +struct PadInfo +{ + public: + enum PadType + { + None, + Out, + In + }; + + size_t m_id; + PadType m_type; + std::string m_name; + + + bool operator == (const PadInfo &obj) const + { + if(this == &obj) + return true; + + if(m_id != obj.m_id) + return false; + + if(m_type != obj.m_type) + return false; + + if(m_name != obj.m_name) + return false; + + return true; + } +}; + +struct ElementInfo +{ + struct Connection + { + size_t m_padId; + size_t m_elementId; + + bool operator == (const Connection &obj) const + { + if(this == &obj) + return true; + + if(m_padId != obj.m_padId) + return false; + + if(m_elementId != obj.m_elementId) + return false; + + return true; + } + }; + + + size_t m_id; + std::string m_name; + std::string m_pluginName; + std::vector m_pads; + std::vector m_connections; +}; + + +class GraphManager +{ +public: + GraphManager(); + ~GraphManager(); + + bool AddPlugin(const char *plugin, const char *name); + bool RemovePlugin(const char *name); + bool Connect(const char *srcElement, const char *srcPad, + const char *dstElement, const char *dstPad); + bool Disconnect(const char *srcElement, const char *srcPad, + const char *dstElement, const char *dstPad); + std::vector GetInfo(); + + bool OpenUri(const char *uri, const char *name); + + double GetPosition(); + bool SetPosition(double); + + + bool Play(); + bool Pause(); + bool Stop(); + + GstElement *m_pGraph; +}; + + +#endif diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp new file mode 100644 index 0000000..49d743b --- /dev/null +++ b/src/MainWindow.cpp @@ -0,0 +1,366 @@ +#include "MainWindow.h" + +#include "PluginsList.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "GraphDisplay.h" +#include "PipelineIE.h" +#include "CustomSettings.h" +#include "SeekSlider.h" + +#include "version_info.h" + +#include + +MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags): + QMainWindow(parent, flags) + ,m_pGraph(new GraphManager) +{ + QToolBar *ptb = addToolBar("Menu"); + + QAction *pactAdd = ptb -> addAction("Add..."); + pactAdd -> setShortcut(QKeySequence("Ctrl+F")); + connect(pactAdd, SIGNAL(triggered()), SLOT(AddPlugin())); + + + QAction *pactOpenFile = ptb -> addAction("Open File..."); + connect(pactOpenFile, SIGNAL(triggered()), SLOT(OpenFile())); + + ptb -> addSeparator(); + + QPixmap pxPlay(24, 24); + pxPlay.fill(QColor(0, 0, 0, 0)); + QPainter pntrPlay(&pxPlay); + pntrPlay.setPen(Qt::darkGreen); + pntrPlay.setBrush(QBrush(Qt::darkGreen)); + + QPolygon polygon(3); + polygon.setPoint(0, 4, 4); + polygon.setPoint(1, 4, 20); + polygon.setPoint(2, 20, 12); + + pntrPlay.drawPolygon(polygon, Qt::WindingFill); + + QAction *pactPlay = ptb -> addAction(QIcon(pxPlay), "Play"); + connect(pactPlay, SIGNAL(triggered()), SLOT(Play())); + + QPixmap pxPause(24, 24); + pxPause.fill(QColor(0, 0, 0, 0)); + QPainter pntrPause(&pxPause); + pntrPause.setPen(Qt::darkGray); + pntrPause.setBrush(QBrush(Qt::darkGray)); + + pntrPause.drawRect(8, 4, 3, 16); + pntrPause.drawRect(13, 4, 3, 16); + + QAction *pactPause = ptb -> addAction(QIcon(pxPause), "Pause"); + connect(pactPause, SIGNAL(triggered()), SLOT(Pause())); + + QPixmap pxStop(24, 24); + pxStop.fill(QColor(0, 0, 0, 0)); + QPainter pntrStop(&pxStop); + pntrStop.setPen(Qt::darkRed); + pntrStop.setBrush(QBrush(Qt::darkRed)); + + pntrStop.drawRect(6, 6, 12, 12); + + QAction *pactStop = ptb -> addAction(QIcon(pxStop), "Stop"); + connect(pactStop, SIGNAL(triggered()), SLOT(Stop())); + + QPixmap pxFulsh(24, 24); + pxFulsh.fill(QColor(0, 0, 0, 0)); + QPainter pntrFlush(&pxFulsh); + pntrFlush.setPen(Qt::darkGreen); + pntrFlush.setBrush(QBrush(Qt::darkGreen)); + + pntrFlush.drawRect(3, 4, 3, 16); + + polygon = QPolygon(3); + polygon.setPoint(0, 9, 4); + polygon.setPoint(1, 9, 20); + polygon.setPoint(2, 21, 12); + + pntrFlush.drawPolygon(polygon, Qt::WindingFill); + + QAction *pactFlush = ptb -> addAction(QIcon(pxFulsh), "Flush"); + connect(pactFlush, SIGNAL(triggered()), SLOT(Flush())); + + + ptb -> addSeparator(); + + m_pslider = new SeekSlider(); + m_pslider -> setOrientation(Qt::Horizontal); + m_pslider -> setRange(0, 10000); + m_pslider -> setTracking(false); + + connect(m_pslider, SIGNAL(valueChanged(int)), SLOT(Seek(int))); + ptb -> addWidget(m_pslider); + + QMenu *pmenu = menuBar() -> addMenu("&File"); + + QAction *pactOpen = pmenu -> addAction ("Open...", this, SLOT(Open()), QKeySequence::Open); + addAction (pactOpen); + + QAction *pactSave = pmenu -> addAction ("Save", this, SLOT(Save()), QKeySequence::Save); + addAction (pactSave); + + QAction *pactSaveAs = pmenu -> addAction ("Save As...", this, SLOT(SaveAs()), QKeySequence::SaveAs); + addAction (pactSaveAs); + + pmenu -> addSeparator(); + pmenu -> addAction("Exit", this, SLOT(close())); + + + pmenu = menuBar() -> addMenu("&Graph"); + + pmenu -> addAction(pactAdd); + pmenu -> addAction(pactOpenFile); + pmenu -> addAction ("Open Uri...", this, SLOT(OpenUri())); + pmenu -> addSeparator(); + pmenu -> addAction(pactPlay); + pmenu -> addAction(pactPause); + pmenu -> addAction(pactStop); + pmenu -> addAction(pactFlush); + + + pmenu = menuBar() -> addMenu("&Help"); + + pmenu -> addAction ("About pipeviz...", this, SLOT(About())); + + + m_pGraphDisplay = new GraphDisplay; + + QScrollArea *pscroll = new QScrollArea; + pscroll -> setWidget(m_pGraphDisplay); + pscroll -> setWidgetResizable(false); + m_pGraphDisplay -> resize(10000, 10000); + m_pGraphDisplay -> m_pGraph = m_pGraph; + setCentralWidget(pscroll); + m_pstatusBar = new QStatusBar; + setStatusBar(m_pstatusBar); + + restoreGeometry(CustomSettings::mainWindowGeometry()); + + startTimer(100); +} + + +MainWindow::~MainWindow() +{ + CustomSettings::saveMainWindowGeometry(saveGeometry()); +} + + +void MainWindow::AddPlugin() +{ + PluginsList lst(this); + lst.m_pGraph = m_pGraph.data(); + + lst.exec(); + std::vector info = m_pGraph -> GetInfo(); + m_pGraphDisplay -> update(info); +} + + + +void MainWindow::OpenFile() +{ + QString dir = CustomSettings::lastIODirectory(); + + QString path = QFileDialog::getOpenFileName(this, "Open File...", dir ); + if(!path.isEmpty()) + { + gchar *uri = gst_filename_to_uri(path.toStdString().c_str(), NULL); + if(uri) + { + qDebug() << "Open Source file: " << path; + + m_pGraph -> OpenUri(uri, NULL); + g_free(uri); + + std::vector info = m_pGraph -> GetInfo(); + m_pGraphDisplay -> update(info); + + QString dir = QFileInfo(path).absoluteDir().absolutePath(); + CustomSettings::saveLastIODirectory(dir); + } + } +} + + +void MainWindow::OpenUri() +{ + QString uri = QInputDialog::getText(this, "Open Uri...", "Uri:"); + + if(!uri.isEmpty()) + { + qDebug() << "Open uri: " << uri; + m_pGraph -> OpenUri(uri.toStdString().c_str(), NULL); + + std::vector info = m_pGraph -> GetInfo(); + m_pGraphDisplay -> update(info); + } + +} + + +void MainWindow::Play() +{ + qDebug() << "Play"; + m_pGraph -> Play(); +} + + +void MainWindow::Pause() +{ + qDebug() << "Pause"; + m_pGraph -> Pause(); +} + + +void MainWindow::Stop() +{ + qDebug() << "Stop"; + m_pGraph -> Stop(); +} + + +void MainWindow::Flush() +{ + qDebug() << "Flush"; + + if(m_pGraph -> m_pGraph) + { + gst_element_send_event(GST_ELEMENT(m_pGraph -> m_pGraph), gst_event_new_flush_start()); + gst_element_send_event(GST_ELEMENT(m_pGraph -> m_pGraph), gst_event_new_flush_stop(true)); + } +} + + +void MainWindow::Seek(int val) +{ + if(m_pGraph -> SetPosition((double)(val) / m_pslider -> maximum())) + qDebug() << "Seek to" << val; + else + qDebug() << "Seek to" << val << "was FAILED"; +} + + +void MainWindow::timerEvent(QTimerEvent *) +{ + GstState state; + GstStateChangeReturn res = gst_element_get_state (m_pGraph -> m_pGraph, &state, NULL, GST_MSECOND); + + if(res == GST_STATE_CHANGE_SUCCESS) + { + QString str; + switch(state) + { + case GST_STATE_VOID_PENDING: + str = "Pending"; + break; + case GST_STATE_NULL: + str = "Null"; + break; + case GST_STATE_READY: + str = "Ready"; + break; + case GST_STATE_PAUSED: + str = "Paused"; + break; + case GST_STATE_PLAYING: + str = "Playing"; + break; + }; + + m_pstatusBar -> showMessage(str); + } + else + { + m_pstatusBar -> showMessage(QString(gst_element_state_change_return_get_name(res))); + } + + double pos = m_pGraph -> GetPosition(); + + if(m_pslider -> value() != (int)(m_pslider -> maximum() * pos)) + m_pslider -> setSliderPosition(m_pslider -> maximum() * pos); + + + m_pGraphDisplay -> update(m_pGraph -> GetInfo()); +} + + + +void MainWindow::Save() +{ + if(m_fileName.isEmpty()) + SaveAs(); + else + PipelineIE::Export(m_pGraph, m_fileName); +} + + +void MainWindow::SaveAs() +{ + QString dir = CustomSettings::lastIODirectory(); + + QString path = QFileDialog::getSaveFileName(this, "Save As...", dir); + + if(!path.isEmpty()) + { + m_fileName = path; + Save(); + + QString dir = QFileInfo(path).absoluteDir().absolutePath(); + CustomSettings::saveLastIODirectory(dir); + } +} + + +void MainWindow::Open() +{ + QString dir = CustomSettings::lastIODirectory(); + + QString path = QFileDialog::getOpenFileName(this, "Open...", dir); + + if(!path.isEmpty()) + { + if(PipelineIE::Import(m_pGraph, path)) + m_fileName = path; + + QString dir = QFileInfo(path).absoluteDir().absolutePath(); + CustomSettings::saveLastIODirectory(dir); + } +} + + +void MainWindow::About() +{ + QString message; + message = "
pipeviz

"; + message = "
virinext@gmail.com

"; + message += QString("
Version: ") + VERSION_STR + "

"; + message += "
GUI Based on Qt
"; + QMessageBox::about(this, "About", message); +} diff --git a/src/MainWindow.h b/src/MainWindow.h new file mode 100644 index 0000000..11afac1 --- /dev/null +++ b/src/MainWindow.h @@ -0,0 +1,56 @@ +#ifndef MAIN_WINDOW_H_ +#define MAIN_WINDOW_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "GraphManager.h" + +class GraphDisplay; + +class MainWindow: public QMainWindow +{ + Q_OBJECT + public: + MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~MainWindow(); + + protected: + void timerEvent(QTimerEvent *); + + private slots: + void AddPlugin(); + void OpenFile(); + void OpenUri(); + void Play(); + void Pause(); + void Stop(); + void Flush(); + void Seek(int); + + void Save(); + void SaveAs(); + void Open(); + + void About(); + + private: + QSharedPointer m_pGraph; + GraphDisplay *m_pGraphDisplay; + + QStatusBar *m_pstatusBar; + QSlider *m_pslider; + + QString m_fileName; +}; + + +#endif diff --git a/src/PadProperties.cpp b/src/PadProperties.cpp new file mode 100644 index 0000000..dc2309c --- /dev/null +++ b/src/PadProperties.cpp @@ -0,0 +1,95 @@ +#include "PadProperties.h" + +#include +#include +#include +#include +#include + +#include + +PadProperties::PadProperties(QSharedPointer pGraphManager, const char *elementName, const char *padName, + QWidget *parent, Qt::WindowFlags flags): +QWidget(parent, flags) +{ + setWindowTitle(QString(elementName) + "::" + padName + " properties"); + GstElement *element = gst_bin_get_by_name (GST_BIN(pGraphManager -> m_pGraph), elementName); + + if(!element) + return; + + GstPad *pad = gst_element_get_static_pad(GST_ELEMENT(element), padName); + + QGridLayout *play = new QGridLayout; + + play -> addWidget(new QLabel("Name"), 0, 0); + + QLabel *plbl = new QLabel(padName); + plbl -> setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + play -> addWidget(plbl, 0, 1); + + play -> addWidget(new QLabel("All caps:"), 1, 0); + GstCaps *caps = gst_pad_query_caps(pad, NULL); + gchar *str; + gchar *noSpecified = (gchar *)"not specified"; + if(caps) + str = gst_caps_to_string(caps); + else + str = noSpecified; + + plbl = new QLabel(QString(str)); + plbl -> setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + play -> addWidget(plbl, 1, 1); + if(caps) + { + g_free(str); + gst_caps_unref(caps); + } + + play -> addWidget(new QLabel("Allowed caps:"), 2, 0); + caps = gst_pad_get_allowed_caps(pad); + str = NULL; + if(caps) + str = gst_caps_to_string(caps); + else + str = noSpecified; + + plbl = new QLabel(QString(str)); + plbl -> setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + play -> addWidget(plbl, 2, 1); + if(caps) + { + g_free(str); + gst_caps_unref(caps); + } + + play -> addWidget(new QLabel("Current caps"), 3, 0); + caps = gst_pad_get_current_caps(pad); + str = NULL; + if(caps) + str = gst_caps_to_string(caps); + else + str = noSpecified; + + plbl = new QLabel(QString(str)); + plbl -> setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + play -> addWidget(plbl, 3, 1); + if(caps) + { + g_free(str); + gst_caps_unref(caps); + } + + gst_object_unref(element); + gst_object_unref (pad); + + QVBoxLayout *pvblay = new QVBoxLayout; + QWidget *pwgt = new QWidget(this); + pwgt -> setLayout(play); + QScrollArea *pscroll = new QScrollArea(this); + pscroll -> setWidget(pwgt); + + pvblay -> addWidget(pscroll); + + setLayout(pvblay); +} \ No newline at end of file diff --git a/src/PadProperties.h b/src/PadProperties.h new file mode 100644 index 0000000..a0d08cc --- /dev/null +++ b/src/PadProperties.h @@ -0,0 +1,17 @@ +#ifndef PAD_PROPERTIES_H_ +#define PAD_PROPERTIES_H_ + +#include +#include + +#include "GraphManager.h" + +class PadProperties: public QWidget +{ +public: + PadProperties(QSharedPointer pGraphManager, const char *element, const char *pad + , QWidget *parent = 0, Qt::WindowFlags flags = 0); +}; + + +#endif diff --git a/src/PipelineIE.cpp b/src/PipelineIE.cpp new file mode 100644 index 0000000..56e0f5a --- /dev/null +++ b/src/PipelineIE.cpp @@ -0,0 +1,610 @@ +#include "PipelineIE.h" + +#include +#include +#include +#include + +#include + +#include + +static void clearPipeline(GstElement *pipeline) +{ + if(!pipeline) + return; + + GstIterator *iter; + iter = gst_bin_iterate_elements (GST_BIN (pipeline)); + + bool done = false; + while (!done) + { + GValue value = { 0 }; + switch (gst_iterator_next (iter, &value)) + { + case GST_ITERATOR_OK: + { + GstElement *element = GST_ELEMENT(g_value_get_object(&value)); + gst_bin_remove(GST_BIN(pipeline), element); + g_value_reset (&value); + + iter = gst_bin_iterate_elements (GST_BIN (pipeline)); + if(!iter) + done = true; + + break; + } + case GST_ITERATOR_DONE: + case GST_ITERATOR_RESYNC: + case GST_ITERATOR_ERROR: + { + done = true; + break; + } + }; + } + + gst_iterator_free (iter); +} + +namespace +{ + struct Connection + { + std::string element1; + std::string pad1; + std::string element2; + std::string pad2; + }; +} + + +static void writeProperties(QXmlStreamWriter &xmlWriter, const GstElement *element) +{ + GParamSpec **prop_specs; + guint num_props; + + prop_specs = g_object_class_list_properties(G_OBJECT_GET_CLASS (element), + &num_props); + + if(!num_props) + return; + + xmlWriter.writeStartElement("properties"); + + for(std::size_t i = 0; i flags & G_PARAM_READABLE) && (param -> flags & G_PARAM_WRITABLE)) + { + GValue value = { 0 }; + g_value_init (&value, param -> value_type); + + g_object_get_property (G_OBJECT(element), param -> name, &value); + + if(!g_param_value_defaults(param, &value)) + { + bool skip = false; + QString propertyName = g_param_spec_get_name (param); + QString propertyValue; + + + switch (G_VALUE_TYPE (&value)) + { + case G_TYPE_STRING: + { + + const char *string_val = g_value_get_string (&value); + if(string_val) + propertyValue = string_val; + else + skip = true; + break; + } + case G_TYPE_BOOLEAN: + { + gboolean bool_val = g_value_get_boolean (&value); + propertyValue = QString::number(bool_val); + break; + } + case G_TYPE_ULONG: + { + propertyValue = QString::number(g_value_get_ulong(&value)); + break; + } + case G_TYPE_LONG: + { + propertyValue = QString::number(g_value_get_long(&value)); + break; + } + case G_TYPE_UINT: + { + propertyValue = QString::number(g_value_get_uint(&value)); + break; + } + case G_TYPE_INT: + { + propertyValue = QString::number(g_value_get_int(&value)); + break; + } + case G_TYPE_UINT64: + { + propertyValue = QString::number(g_value_get_uint64(&value)); + break; + } + case G_TYPE_INT64: + { + propertyValue = QString::number(g_value_get_int64(&value)); + break; + } + case G_TYPE_FLOAT: + { + propertyValue = QString::number(g_value_get_float(&value)); + break; + } + case G_TYPE_DOUBLE: + { + propertyValue = QString::number(g_value_get_double(&value)); + break; + } + default: + { + gchar *elementName = gst_element_get_name(element); + + qDebug() << "property `" << propertyName << "` for `" + << elementName << "` not supported"; + g_free(elementName); + + skip = true; + break; + } + }; + + if(!skip) + { + xmlWriter.writeStartElement("property"); + xmlWriter.writeAttribute("name", propertyName); + xmlWriter.writeAttribute("value", propertyValue); + xmlWriter.writeEndElement(); + } + g_value_reset(&value); + + } + } + } + + xmlWriter.writeEndElement(); + + g_free(prop_specs); +} + + + +static void loadProperties(QDomElement node, GstElement *element) +{ + QDomNode child = node.firstChild(); + while(!child.isNull()) + { + if(child.toElement().tagName() == "property") + { + QString name = child.toElement().attribute("name"); + QString value = child.toElement().attribute("value"); + + GParamSpec *param = g_object_class_find_property(G_OBJECT_GET_CLASS (element), + name.toStdString().c_str()); + + if(!param) + { + gchar *elementName = gst_element_get_name(element); + qDebug() << "problem with setting property `" << name << "` for `" << elementName << "`"; + g_free(elementName); + continue; + } + + if(!(param -> flags & G_PARAM_WRITABLE)) + continue; + + std::string tmpStr = name.toStdString(); + const char *propName = tmpStr.c_str(); + switch (param -> value_type) + { + case G_TYPE_STRING: + { + g_object_set(G_OBJECT(element), propName, value.toStdString().c_str(), NULL); + break; + } + case G_TYPE_BOOLEAN: + { + gboolean val = value.toInt(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_ULONG: + { + gulong val = value.toULong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_LONG: + { + glong val = value.toLong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_UINT: + { + guint val = value.toUInt(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_INT: + { + gint val = value.toInt(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_UINT64: + { + guint64 val = value.toULongLong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_INT64: + { + gint64 val = value.toLongLong(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_FLOAT: + { + gfloat val = value.toFloat(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + case G_TYPE_DOUBLE: + { + gdouble val = value.toDouble(); + g_object_set(G_OBJECT(element), propName, val, NULL); + break; + } + default: + { + gchar *elementName = gst_element_get_name(element); + qDebug() << "property `" << name << "` for `" << QString(elementName) << "` not supported"; + g_free(elementName); + break; + } + }; + } + + child = child.nextSibling(); + } + +} + + +static void create_requst_pad(GstElement *element, const QString &templateName, const QString &padName) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS(element); + + GstPadTemplate *templ = gst_element_class_get_pad_template(klass, templateName.toStdString().c_str()); + + gst_element_request_pad(element, templ, padName.toStdString().c_str(), NULL); +} + +bool PipelineIE::Export(QSharedPointer pgraph, const QString &fileName) +{ + QFile file(fileName); + + if (!file.open(QIODevice::WriteOnly)) + { + QMessageBox::warning(0, "Read only", "The file is in read only mode"); + return false; + } + + std::vector info = pgraph -> GetInfo(); + + QXmlStreamWriter xmlWriter; + xmlWriter.setDevice(&file); + xmlWriter.writeStartDocument(); + xmlWriter.writeStartElement("pipeline"); + + for(std::size_t i=0; i m_pGraph), info[i].m_name.c_str()); + + for(std::size_t j=0; j pgraph, const QString &fileName) +{ + GstElement *pipeline = pgraph -> m_pGraph; + QFile file(fileName); + if(!file.open(QFile::ReadOnly | QFile::Text)) + { + QMessageBox::warning(0, "Open failed", + QString("Cannot read file ") + fileName + + ": " + file.errorString()); + + return false; + } + + QString errorStr; + int errorLine; + int errorColumn; + + QDomDocument doc; + if(!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn)) + { + QMessageBox::warning(0, "Xml parsing failed", + QString("Parse error at line ") + QString::number(errorLine) + ", " + "column " + QString::number(errorColumn) + ": " + errorStr); + return false; + } + + clearPipeline(pipeline); + + QDomElement root = doc.documentElement(); + + if(root.tagName() != "pipeline") + { + QMessageBox::warning(0, "Parsing failed", "Is invalid pipeline file"); + return false; + } + + QDomNode child = root.firstChild(); + + std::vector connections; + while(!child.isNull()) + { + if(child.toElement().tagName() == "element") + { + QDomElement elNode = child.toElement(); + + + GstElement *pel = gst_element_factory_make(elNode.attribute("plugin-name").toStdString().c_str(), + elNode.attribute("name").toStdString().c_str()); + + if(!pel) + { + QMessageBox::warning(0, "Element creation failed", + QString("Could not create element of `") + + elNode.attribute("plugin-name") + "` with name `" + + elNode.attribute("name") + "`"); + + child = child.nextSibling(); + continue; + } + + bool res = gst_bin_add(GST_BIN(pipeline), pel); + + if(!res) + { + QMessageBox::warning(0, "Element insertion failed", + QString("Could not insert element `") + + elNode.attribute("name") + "` to pipeline"); + + child = child.nextSibling(); + continue; + } + + gst_element_sync_state_with_parent(pel); + + + QDomNode elementChild = elNode.firstChild(); + while(!elementChild.isNull()) + { + if(elementChild.toElement().tagName() == "pad") + { + QDomNode padChild = elementChild.firstChild(); + QDomElement elPad = elementChild.toElement(); +// GstPadPresence presence = GST_PAD_ALWAYS; + + QString templaneName; + if(elPad.attribute("presence") == "request") + create_requst_pad(pel, elPad.attribute("template-name"), elPad.attribute("name")); + + while(!padChild.isNull()) + { + if(padChild.toElement().tagName() == "connected-to") + { + bool isExists = false; + QDomElement elCoonnectedTo = padChild.toElement(); + + for(std::size_t i=0; i 0) + setReady = false; + + while(true) + { + std::size_t i=0; + for(; i +#include + +#include "GraphManager.h" + +namespace PipelineIE +{ + bool Export(QSharedPointer pgraph, const QString &fileName); + bool Import(QSharedPointer pgraph, const QString &fileName); +}; + +#endif diff --git a/src/PluginsList.cpp b/src/PluginsList.cpp new file mode 100644 index 0000000..e152624 --- /dev/null +++ b/src/PluginsList.cpp @@ -0,0 +1,209 @@ +#include "PluginsList.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ + void InitPluginsList(QListWidget *plwgt) + { + std::size_t num = 0; + GList *plugins; + plugins = gst_registry_get_plugin_list(gst_registry_get()); + while(plugins) + { + GstPlugin *plugin; + plugin = (GstPlugin *) (plugins->data); + plugins = g_list_next (plugins); + + GList *features = gst_registry_get_feature_list_by_plugin (gst_registry_get (), + gst_plugin_get_name (plugin)); + + while(features) + { + GstPluginFeature *feature; + feature = GST_PLUGIN_FEATURE (features->data); + if(GST_IS_ELEMENT_FACTORY (feature)) + { + GstElementFactory *factory; + factory = GST_ELEMENT_FACTORY (feature); + plwgt -> addItem(GST_OBJECT_NAME (factory)); + num++; + } + + features = g_list_next (features); + } + } + } +} + +PluginsList::PluginsList(QWidget *pwgt, Qt::WindowFlags f): +QDialog(pwgt, f) +,m_pGraph(NULL) +{ + m_pPlugins = new QListWidget; + m_plblInfo = new QLabel; + + m_plblInfo -> setTextInteractionFlags(Qt::TextSelectableByMouse); + m_plblInfo -> setAlignment(Qt::AlignLeft | Qt::AlignTop); + QScrollArea *pscroll = new QScrollArea; + pscroll -> setWidget(m_plblInfo); + m_plblInfo -> resize(pscroll -> size()); + + QHBoxLayout *phblay = new QHBoxLayout; + + phblay -> addWidget(m_pPlugins, 1); + phblay -> addWidget(pscroll, 2); + + InitPluginsList(m_pPlugins); + + QHBoxLayout *phblayFind = new QHBoxLayout; + + QLineEdit *ple = new QLineEdit; + phblayFind -> addWidget(ple); + phblayFind -> addStretch(1); + + ple -> setPlaceholderText("Search..."); + + QVBoxLayout *pvblay = new QVBoxLayout; + pvblay -> addLayout(phblayFind); + pvblay -> addLayout(phblay); + + setLayout(pvblay); + + setWindowTitle("Add plugin"); + + QObject::connect(m_pPlugins, SIGNAL(currentItemChanged (QListWidgetItem *, QListWidgetItem *)), + this, SLOT(showInfo(QListWidgetItem *, QListWidgetItem *))); + + QObject::connect(m_pPlugins, SIGNAL(itemDoubleClicked (QListWidgetItem *)), + this, SLOT(insert(QListWidgetItem *))); + + QObject::connect(ple, SIGNAL(textChanged(const QString &)), this, SLOT(filterPlagins(const QString &))); + + installEventFilter(this); +} + + +void PluginsList::showInfo(QListWidgetItem *pitem, QListWidgetItem *previous) +{ + qDebug() << "Show Info: " << pitem -> text(); + m_plblInfo -> clear(); + QString descr; + descr += "Plugin details
"; + + GstElementFactory *factory = gst_element_factory_find(pitem -> text().toStdString().c_str()); + if(!factory) + { + qDebug() << "warning: " << pitem -> text() << " Not Found"; + return; + } + + factory = GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE(factory))); + if(!factory) + { + qDebug() << "warning: " << pitem -> text() << " Not Found"; + return; + } + + GstPlugin *plugin = gst_plugin_feature_get_plugin (GST_PLUGIN_FEATURE (factory)); + if(!plugin) + { + qDebug() << "warning: " << pitem -> text() << " Not Found"; + return; + } + + + const gchar *release_date = gst_plugin_get_release_date_string (plugin); + const gchar *filename = gst_plugin_get_filename(plugin); + + descr += "Name: " + QString(gst_plugin_get_name(plugin)) + "
"; + descr += "Description: " + QString(gst_plugin_get_description(plugin)) + "
"; + descr += "Filename: " + QString((filename != NULL) ? filename : "(null)") + "
"; + descr += "Version: " + QString(gst_plugin_get_version (plugin)) + "
"; + descr += "License: " + QString(gst_plugin_get_license (plugin)) + "
"; + descr += "Source module: " + QString(gst_plugin_get_source (plugin)) + "
"; + + if (release_date != NULL) + { + const gchar *tz = "(UTC)"; + gchar *str, *sep; + + str = g_strdup (release_date); + sep = strstr (str, "T"); + if (sep != NULL) + { + *sep = ' '; + sep = strstr (sep + 1, "Z"); + if (sep != NULL) + *sep = ' '; + } + else + { + tz = ""; + } + descr += "Source release date: " + QString(str) + " " + QString(tz) + "
"; + g_free (str); + } + descr += "Binary package: " + QString(gst_plugin_get_package (plugin)) + "
"; + descr += "Origin URL: " + QString(gst_plugin_get_origin (plugin)) + "
"; + + m_plblInfo -> setText(descr); +} + + +void PluginsList::insert(QListWidgetItem *pitem) +{ + qDebug() << "Insert: " << pitem -> text(); + + if(!m_pGraph || !m_pGraph -> AddPlugin(pitem -> text().toStdString().c_str(), NULL)) + { + QMessageBox::warning(this, "Plugin addition problem", "Plugin `" + pitem -> text() + "` insertion was FAILED"); + qDebug() << "Plugin `" << pitem -> text() << "` insertion FAILED"; + return; + } +} + + + +bool PluginsList::eventFilter(QObject *obj, QEvent *event) +{ + if (event -> type() == QEvent::KeyPress) + { + QKeyEvent *key = static_cast(event); + + if((key -> key() == Qt::Key_Enter) || (key -> key() == Qt::Key_Return)) + { + insert(m_pPlugins -> currentItem()); + return true; + } + } + + return QDialog::eventFilter(obj, event); +} + + +void PluginsList::filterPlagins(const QString &text) +{ + for(std::size_t i=0; i count(); i++) + { + QListWidgetItem *pitem = m_pPlugins -> item(i); + + if(pitem -> text().contains(text)) + pitem -> setHidden(false); + else + pitem -> setHidden(true); + } + +} diff --git a/src/PluginsList.h b/src/PluginsList.h new file mode 100644 index 0000000..ea4ae4e --- /dev/null +++ b/src/PluginsList.h @@ -0,0 +1,41 @@ +#ifndef PLUGINS_LIST_H_ +#define PLUGINS_LIST_H_ + + + +#include +#include +#include + + +#include "GraphManager.h" + + +class PluginsList: public QDialog +{ +Q_OBJECT +public: + PluginsList(QWidget *pwgt = NULL, Qt::WindowFlags f = 0); + + GraphManager *m_pGraph; + +protected: + bool eventFilter(QObject *obj, QEvent *ev); + + +public slots: + void showInfo(QListWidgetItem *current, QListWidgetItem *previous); + void insert(QListWidgetItem *); + +private slots: + void filterPlagins(const QString &text); + +private: + QLabel *m_plblInfo; + QListWidget *m_pPlugins; +}; + + + + +#endif diff --git a/src/SeekSlider.cpp b/src/SeekSlider.cpp new file mode 100644 index 0000000..5636c5b --- /dev/null +++ b/src/SeekSlider.cpp @@ -0,0 +1,16 @@ +#include "SeekSlider.h" + +void SeekSlider::mousePressEvent(QMouseEvent *event) +{ + if(event->button() == Qt::LeftButton) + { + if(orientation() == Qt::Vertical) + setValue(minimum() + ((maximum()-minimum()) * (height()-event->y())) / height() ) ; + else + setValue(minimum() + ((maximum()-minimum()) * event->x()) / width() ) ; + + event->accept(); + } + + QSlider::mousePressEvent(event); +}; diff --git a/src/SeekSlider.h b/src/SeekSlider.h new file mode 100644 index 0000000..ae3f7ba --- /dev/null +++ b/src/SeekSlider.h @@ -0,0 +1,16 @@ +#ifndef SEEK_SLIDER_H_ +#define SEEK_SLIDER_H_ + + +#include +#include + +class SeekSlider: public QSlider +{ + protected: + void mousePressEvent(QMouseEvent *); +}; + + + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..9a19f2c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,15 @@ +#include +#include "MainWindow.h" + +#include + +int main(int argc, char **argv) +{ + gst_init (&argc, &argv); + QApplication app(argc, argv); + + MainWindow wgt; + wgt.show(); + + return app.exec(); +} \ No newline at end of file diff --git a/src/verinfo/verinfo.sh b/src/verinfo/verinfo.sh new file mode 100755 index 0000000..020bdd4 --- /dev/null +++ b/src/verinfo/verinfo.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +version_file=$1 +out_file=$2 + + +major=`sed -n '1p' $version_file` +minor=`sed -n '2p' $version_file` + +commit=`git rev-list --count HEAD` +date=`date +'%d%m'%y` + + +echo "#ifndef VERSION_INFO_H_" > $out_file +echo "#define VERSION_INFO_H_" >> $out_file +echo "" >> $out_file +echo "#define VERSION_STR \"$major.$minor.$commit.$date\"" >> $out_file +echo "" >> $out_file +echo "#endif" >> $out_file diff --git a/src/version b/src/version new file mode 100644 index 0000000..0d66ea1 --- /dev/null +++ b/src/version @@ -0,0 +1,2 @@ +0 +1