initial commit

This commit is contained in:
Viktor Anjin 2015-02-26 23:02:22 +07:00
parent 0dae1e94e4
commit 5e14d4bda4
24 changed files with 3454 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
Makefile*
debug/*
object_script*
pipeviz_plugin_import.cpp
pipeviz
*.o
moc_*
src/version_info.h

42
pipeviz.pro Normal file
View file

@ -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

23
readme.md Normal file
View file

@ -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)

38
src/CustomSettings.cpp Normal file
View file

@ -0,0 +1,38 @@
#include "CustomSettings.h"
#include <QSettings>
#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();
}

19
src/CustomSettings.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef CUSTOM_SETTINGS_H_
#define CUSTOM_SETTINGS_H_
#include <QString>
#include <QByteArray>
namespace CustomSettings
{
void saveLastIODirectory(const QString &name);
QString lastIODirectory();
void saveMainWindowGeometry(const QByteArray &geometry);
QByteArray mainWindowGeometry();
}
#endif

280
src/ElementProperties.cpp Normal file
View file

@ -0,0 +1,280 @@
#include "ElementProperties.h"
#include <QDebug>
#include <QGridLayout>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QString>
#include <QLabel>
#include <QScrollArea>
#include <QPushButton>
#include <gst/gst.h>
ElementProperties::ElementProperties(QSharedPointer<GraphManager> 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<num_props; i++)
{
bool readOnly = true;
GParamSpec *param = prop_specs[i];
GValue value = { 0 };
g_value_init (&value, param -> 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<QString, QLineEdit *>::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();
}

33
src/ElementProperties.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef ELEMENT_PROPERTIES_H_
#define ELEMENT_PROPERTIES_H_
#include <QWidget>
#include <QSharedPointer>
#include <QMap>
#include <QString>
#include <QLineEdit>
#include "GraphManager.h"
class ElementProperties: public QWidget
{
Q_OBJECT
public:
ElementProperties(QSharedPointer<GraphManager> pGraphManager, const char *name,
QWidget *parent = 0, Qt::WindowFlags flags = 0);
private slots:
void applyClicked();
void okClicked();
private:
QSharedPointer<GraphManager> m_pGraphManager;
QMap<QString, QLineEdit *> m_values;
QString m_name;
void create();
};
#endif

960
src/GraphDisplay.cpp Normal file
View file

@ -0,0 +1,960 @@
#include "GraphDisplay.h"
#include <cassert>
#include <math.h>
#include <QPainter>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QTableWidget>
#include <QVariant>
#include <QDebug>
#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<m_info.size(); i++)
{
for(std::size_t j=0; j<m_displayInfo.size(); j++)
{
if(m_info[i].m_name == m_displayInfo[j].m_name)
{
m_displayInfo[j].m_id = m_info[i].m_id;
for(std::size_t k=0; k<m_displayInfo.size(); k++)
{
if(k != j && m_displayInfo[j].m_id == m_displayInfo[k].m_id)
m_displayInfo[k].m_id = (size_t)-1;
}
}
}
}
}
void GraphDisplay::update(const std::vector <ElementInfo> &info)
{
bool needUpdate = false;
if(m_info.size() != info.size())
needUpdate = true;
if(!needUpdate)
{
for(std::size_t i=0; i<info.size(); i++)
{
std::size_t j=0;
for(; j<m_info.size(); j++)
{
if(info[i].m_name == m_info[j].m_name)
break;
}
if(j == m_info.size())
{
needUpdate = true;
break;
}
if(info[i].m_pads != m_info[j].m_pads)
{
needUpdate = true;
break;
}
if(info[i].m_connections != m_info[j].m_connections)
{
needUpdate = true;
break;
}
}
}
if(needUpdate)
{
m_info = info;
updateDisplayInfoIds();
calculatePositions();
repaint();
}
}
void GraphDisplay::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QPen defaultPen = painter.pen();
for(std::size_t i=0; i<m_displayInfo.size(); i++)
{
if(m_displayInfo[i].m_isSelected)
painter.setPen(QPen(Qt::blue));
painter.drawRect(m_displayInfo[i].m_rect);
painter.setPen(defaultPen);
for(std::size_t j=0; j<m_info[i].m_pads.size(); j++)
{
QPoint point = getPadPosition(m_info[i].m_id, m_info[i].m_pads[j].m_id);
int xPos, yPos;
xPos = point.x();
yPos = point.y();
xPos -= PAD_SIZE / 2;
yPos -= PAD_SIZE / 2;
painter.fillRect(xPos, yPos, PAD_SIZE, PAD_SIZE, Qt::black);
QPoint textPos;
QRect rect = painter.boundingRect(0, 0, width(), height(), Qt::AlignLeft | Qt::AlignTop, QString(m_info[i].m_pads[j].m_name.c_str()));
if(m_info[i].m_pads[j].m_type == PadInfo::Out)
textPos = QPoint(point.x() - PAD_SIZE - rect.width(), point.y() + PAD_SIZE / 2);
else if(m_info[i].m_pads[j].m_type == PadInfo::In)
textPos = QPoint(point.x() + PAD_SIZE, point.y() + PAD_SIZE / 2);
painter.drawText(textPos, QString(m_info[i].m_pads[j].m_name.c_str()));
if(m_info[i].m_connections[j].m_elementId != ((size_t)-1) && m_info[i].m_connections[j].m_padId != ((size_t)-1))
{
xPos = point.x();
yPos = point.y();
point = getPadPosition(m_info[i].m_connections[j].m_elementId, m_info[i].m_connections[j].m_padId);
int xPosPeer, yPosPeer;
xPosPeer = point.x();
yPosPeer = point.y();
painter.drawLine(xPos, yPos, xPosPeer, yPosPeer);
}
}
painter.drawText(m_displayInfo[i].m_rect.topLeft() + QPoint(10, 15), QString(m_displayInfo[i].m_name.c_str()));
}
if(m_moveInfo.m_action == MakeConnect)
{
painter.drawLine(m_moveInfo.m_position, m_moveInfo.m_startPosition);
}
else if(m_moveInfo.m_action == Select)
{
if(!m_moveInfo.m_position.isNull())
{
painter.setPen(Qt::DashLine);
painter.drawRect(QRect(m_moveInfo.m_startPosition, m_moveInfo.m_position));
}
}
}
GraphDisplay::ElementDisplayInfo GraphDisplay::calculateOnePosition(const ElementInfo &info)
{
ElementDisplayInfo displayInfo;
displayInfo.m_id = info.m_id;
displayInfo.m_name = info.m_name;
displayInfo.m_isSelected = false;
int width = 150;
int height = 50;
int numInPads, numOutPads;
numInPads = numOutPads = 0;
for(std::size_t j=0; j<info.m_pads.size(); j++)
{
if(info.m_pads[j].m_type == PadInfo::Out)
numOutPads++;
else if(info.m_pads[j].m_type == PadInfo::In)
numInPads++;
}
if(std::max(numInPads, numOutPads) >= 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(); i++)
{
if(rectTest.intersects(m_displayInfo[i].m_rect))
{
noIntersects = false;
break;
}
}
if(noIntersects)
break;
curY += 25;
}
displayInfo.m_rect = rect;
return displayInfo;
}
void GraphDisplay::calculatePositions()
{
while(true)
{
std::size_t i=0;
for(; i<m_displayInfo.size(); i++)
{
std::size_t j=0;
for(; j<m_info.size(); j++)
{
if(m_displayInfo[i].m_id == m_info[j].m_id)
break;
}
if(j == m_info.size())
{
m_displayInfo.erase(m_displayInfo.begin() + i);
break;
}
}
if(i >= m_displayInfo.size())
break;
}
std::size_t i=0;
for(; i<m_info.size(); i++)
{
std::size_t j=0;
for(; j<m_displayInfo.size(); j++)
{
if(m_displayInfo[j].m_id == m_info[i].m_id)
break;
}
if(j == m_displayInfo.size())
m_displayInfo.push_back(calculateOnePosition(m_info[i]));
}
std::vector <ElementDisplayInfo> reorderedDisplayInfo(m_info.size());
for(std::size_t i=0; i<m_info.size(); i++)
{
for(std::size_t j=0; j<m_displayInfo.size(); j++)
{
if(m_displayInfo[j].m_id == m_info[i].m_id)
{
reorderedDisplayInfo[i] = m_displayInfo[j];
break;
}
}
}
m_displayInfo = reorderedDisplayInfo;
}
void GraphDisplay::mousePressEvent(QMouseEvent *event)
{
std::size_t elementId, padId;
getIdByPosition(event -> 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<m_displayInfo.size(); i++)
m_displayInfo[i].m_isSelected = false;
}
void GraphDisplay::mouseReleaseEvent (QMouseEvent *event)
{
if(m_moveInfo.m_action == MakeConnect)
{
std::size_t elementId, padId;
getIdByPosition(event -> 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<m_info.size(); i++)
{
if(m_info[i].m_id == m_moveInfo.m_elementId)
{
infoSrc = m_info[i];
for(std::size_t j=0; j<m_info[i].m_pads.size(); j++)
{
if(m_info[i].m_pads[j].m_id == m_moveInfo.m_padId)
{
srcPad = m_info[i].m_pads[j].m_name.c_str();
break;
}
}
}
if(m_info[i].m_id == elementId)
{
infoDst = m_info[i];
for(std::size_t j=0; j<m_info[i].m_pads.size(); j++)
{
if(m_info[i].m_pads[j].m_id == padId)
{
dstPad = m_info[i].m_pads[j].m_name.c_str();
break;
}
}
}
if(srcPad != NULL && dstPad != NULL)
break;
}
assert(srcPad != NULL && dstPad != NULL);
qDebug() << "Connection from " << infoSrc.m_name.c_str() << ":" << srcPad
<< " to " << infoDst.m_name.c_str() << ":" << dstPad;
if(!m_pGraph -> 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<m_displayInfo.size(); i++)
{
if(selectionRect.intersects(m_displayInfo[i].m_rect))
m_displayInfo[i].m_isSelected = true;
}
}
repaint();
}
else if(m_moveInfo.m_action == MoveComponent)
{
int dx = event -> 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<m_displayInfo.size(); i++)
{
if(m_displayInfo[i].m_id == m_moveInfo.m_elementId)
{
m_displayInfo[i].m_isSelected = true;
repaint();
break;
}
}
}
}
m_moveInfo.m_action = None;
m_moveInfo.m_elementId = -1;
m_moveInfo.m_padId = -1;
m_moveInfo.m_startPosition = QPoint();
m_moveInfo.m_position = QPoint();
repaint();
}
void GraphDisplay::mouseMoveEvent(QMouseEvent *event)
{
if(m_moveInfo.m_action == MoveComponent)
{
int dx = event -> x() - m_moveInfo.m_position.x();
int dy = event -> y() - m_moveInfo.m_position.y();
for(std::size_t i=0; i<m_displayInfo.size(); i++)
{
if(m_displayInfo[i].m_id == m_moveInfo.m_elementId)
{
QRect newRect = m_displayInfo[i].m_rect;
newRect.adjust(dx, dy, dx, dy);
if(contentsRect().contains(newRect))
m_displayInfo[i].m_rect = newRect;
break;
}
}
}
if(m_moveInfo.m_action != None)
{
m_moveInfo.m_position = event -> 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<m_displayInfo.size(); i++)
if(m_displayInfo[i].m_isSelected)
hasSelection = true;
if(hasSelection)
{
QAction *pact = menu.addAction("Remove selected");
if(isActive)
pact -> 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<m_info.size(); i++)
{
for(std::size_t j=0; j<m_info[i].m_connections.size(); j++)
{
QPoint point = getPadPosition(m_info[i].m_id, m_info[i].m_pads[j].m_id);
double x1, y1;
x1 = point.x();
y1 = point.y();
if(m_info[i].m_connections[j].m_elementId != ((size_t)-1) && m_info[i].m_connections[j].m_padId != ((size_t)-1))
{
point = getPadPosition(m_info[i].m_connections[j].m_elementId, m_info[i].m_connections[j].m_padId);
double x2, y2;
x2 = point.x();
y2 = point.y();
double dy = y2 - y1;
double dx = x2 - x1;
double x0 = event -> 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<m_displayInfo.size(); i++)
{
if(m_displayInfo[i].m_isSelected)
break;
}
if(i != m_displayInfo.size())
removePlugin(m_displayInfo[i].m_id);
else
break;
}
}
void GraphDisplay::removePlugin(std::size_t id)
{
std::size_t i=0;
for(; i<m_info.size(); i++)
{
if(m_info[i].m_id == id)
break;
}
if(i < m_info.size())
{
if(m_pGraph -> RemovePlugin(m_info[i].m_name.c_str()))
{
std::vector<ElementInfo> 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<m_info.size(); i++)
{
if(m_info[i].m_id == id)
break;
}
if(i < m_info.size())
{
ElementProperties *pprops = new ElementProperties(m_pGraph, m_info[i].m_name.c_str());
pprops -> 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<m_info.size(); i++)
{
if(m_info[i].m_id == elementId)
break;
}
if(i < m_info.size())
{
std::size_t j=0;
for(; j<m_info[i].m_pads.size(); j++)
if(m_info[i].m_pads[j].m_id == padId)
break;
if(j < m_info[i].m_pads.size())
{
PadProperties *pprops = new PadProperties(m_pGraph, m_info[i].m_name.c_str(), m_info[i].m_pads[j].m_name.c_str());
pprops -> 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<m_info.size(); i++)
{
if(m_info[i].m_id == elementId)
{
for(std::size_t j=0; j<m_info[i].m_pads.size(); j++)
{
if(m_info[i].m_pads[j].m_id == padId)
{
if(m_info[i].m_pads[j].m_type == PadInfo::Out)
{
src = m_info[i].m_name;
srcPad = m_info[i].m_pads[j].m_name.c_str();
elementId = m_info[i].m_connections[j].m_elementId;
padId = m_info[i].m_connections[j].m_padId;
}
else
{
dst = m_info[i].m_name;
dstPad = m_info[i].m_pads[j].m_name.c_str();
elementId = m_info[i].m_connections[j].m_elementId;
padId = m_info[i].m_connections[j].m_padId;
}
break;
}
}
break;
}
}
for(std::size_t i=0; i<m_info.size(); i++)
{
if(m_info[i].m_id == elementId)
{
for(std::size_t j=0; j<m_info[i].m_pads.size(); j++)
{
if(m_info[i].m_pads[j].m_id == padId)
{
if(m_info[i].m_pads[j].m_type == PadInfo::Out)
{
src = m_info[i].m_name;
srcPad = m_info[i].m_pads[j].m_name.c_str();
}
else
{
dst = m_info[i].m_name;
dstPad = m_info[i].m_pads[j].m_name.c_str();
}
break;
}
}
break;
}
}
qDebug() << "Disconnect " << src.c_str() << ":" << srcPad.c_str()
<< " <-> " << 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_info.size(); i++)
{
if(m_info[i].m_id == elementId)
{
element = gst_bin_get_by_name(GST_BIN(m_pGraph -> 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<QTableWidget *> (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<ElementInfo> 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<m_displayInfo.size(); i++)
{
if(elementId != ((size_t)-1))
break;
QRect rect(
m_displayInfo[i].m_rect.x() - 8,
m_displayInfo[i].m_rect.y() - 6,
m_displayInfo[i].m_rect.width() + 16,
m_displayInfo[i].m_rect.height() + 12
);
if(rect.contains(pos))
{
std::size_t j=0;
for(; j<m_info[i].m_pads.size(); j++)
{
QPoint point = getPadPosition(m_displayInfo[i].m_id, m_info[i].m_pads[j].m_id);
int xPos, yPos;
xPos = point.x();
yPos = point.y();
xPos -= PAD_SIZE_ACTION / 2;
yPos -= PAD_SIZE_ACTION / 2;
QRect rect(xPos, yPos, PAD_SIZE_ACTION, PAD_SIZE_ACTION);
if(rect.contains(pos))
{
padId = m_info[i].m_pads[j].m_id;
elementId = m_displayInfo[i].m_id;
break;
}
}
if(j == m_info[i].m_pads.size())
{
if(m_displayInfo[i].m_rect.contains(pos))
elementId = m_displayInfo[i].m_id;
}
}
}
}
QPoint GraphDisplay::getPadPosition(std::size_t elementId, std::size_t padId)
{
QPoint res;
if(elementId == ((size_t)-1) || padId == ((size_t)-1))
return res;
for(std::size_t i=0; i<m_displayInfo.size(); i++)
{
if(m_displayInfo[i].m_id == elementId)
{
int numInPads, numOutPads;
numInPads = numOutPads = 0;
for(std::size_t j=0; j<m_info[i].m_pads.size(); j++)
{
if(m_info[i].m_pads[j].m_type == PadInfo::Out)
numOutPads++;
else if(m_info[i].m_pads[j].m_type == PadInfo::In)
numInPads++;
}
int inDelta, outDelta, inPos, outPos;
inDelta = m_displayInfo[i].m_rect.height() / (numInPads + 1);
outDelta = m_displayInfo[i].m_rect.height() / (numOutPads + 1);
inPos = inDelta;
outPos = outDelta;
for(std::size_t j=0; j<m_info[i].m_pads.size(); j++)
{
int xPos, yPos;
yPos = m_displayInfo[i].m_rect.topRight().y();
if(m_info[i].m_pads[j].m_type == PadInfo::Out)
{
xPos = m_displayInfo[i].m_rect.topRight().x();
yPos += outPos;
outPos += outDelta;
}
else if(m_info[i].m_pads[j].m_type == PadInfo::In)
{
xPos = m_displayInfo[i].m_rect.topLeft().x();
yPos += inPos;
inPos += inDelta;
}
if(m_info[i].m_pads[j].m_id == padId)
{
res = QPoint(xPos, yPos);
break;
}
}
break;
}
}
return res;
}

89
src/GraphDisplay.h Normal file
View file

@ -0,0 +1,89 @@
#ifndef GRAPH_DISPLAY_H_
#define GRAPH_DISPLAY_H_
#include <vector>
#include <QWidget>
#include <QSharedPointer>
#include <QPoint>
#include "GraphManager.h"
#include <vector>
class GraphDisplay: public QWidget
{
Q_OBJECT
public:
GraphDisplay(QWidget *parent=0, Qt::WindowFlags f=0);
void update(const std::vector <ElementInfo> &info);
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent* event);
QSharedPointer<GraphManager> 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 <ElementInfo> m_info;
std::vector <ElementDisplayInfo> m_displayInfo;
MoveInfo m_moveInfo;
};
#endif

384
src/GraphManager.cpp Normal file
View file

@ -0,0 +1,384 @@
#include "GraphManager.h"
#include <QDebug>
#include <QString>
#include <QFileDialog>
#include <QInputDialog>
#include <QFileInfo>
#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 <ElementInfo> GraphManager::GetInfo()
{
std::vector <ElementInfo> 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<res.size(); i++)
{
res[i].m_connections.resize(res[i].m_pads.size());
GstElement *element = gst_bin_get_by_name (GST_BIN(m_pGraph), res[i].m_name.c_str());
for(std::size_t j=0; j<res[i].m_pads.size(); j++)
{
res[i].m_connections[j].m_elementId = -1;
res[i].m_connections[j].m_padId = -1;
GstPad *pad = gst_element_get_static_pad (element, res[i].m_pads[j].m_name.c_str());
GstPad *peerPad = gst_pad_get_peer(pad);
if(peerPad)
{
GstElement *peerElement = GST_ELEMENT(gst_pad_get_parent(peerPad));
gchar *peerName = gst_element_get_name(peerElement);
gchar *peerPadName = gst_pad_get_name(peerPad);
for(std::size_t k=0; k<res.size(); k++)
{
if(res[k].m_name == peerName)
{
for(std::size_t l=0; l<res[k].m_pads.size(); l++)
{
if(res[k].m_pads[l].m_name == peerPadName)
{
res[i].m_connections[j].m_elementId = res[k].m_id;
res[i].m_connections[j].m_padId = res[k].m_pads[l].m_id;
break;
}
}
}
}
g_free(peerName);
g_free(peerPadName);
gst_object_unref(peerPad);
gst_object_unref(peerElement);
}
gst_object_unref(pad);
}
gst_object_unref(element);
}
return res;
}
bool GraphManager::Play()
{
GstStateChangeReturn res;
gst_element_set_state(m_pGraph, GST_STATE_PLAYING);
GstState state;
res = gst_element_get_state (m_pGraph, &state, NULL, GST_SECOND);
if(res != GST_STATE_CHANGE_SUCCESS)
{
gst_element_abort_state(m_pGraph);
qDebug() << "state changing to Play was FAILED";
}
return res == GST_STATE_PLAYING;
}
bool GraphManager::Pause()
{
GstStateChangeReturn res;
GstState state;
gst_element_set_state(m_pGraph, GST_STATE_PAUSED);
res = gst_element_get_state (m_pGraph, &state, NULL, GST_SECOND);
if(res != GST_STATE_CHANGE_SUCCESS)
{
gst_element_abort_state(m_pGraph);
qDebug() << "state changing to Pause was FAILED";
}
return res == GST_STATE_PAUSED;
}
bool GraphManager::Stop()
{
GstStateChangeReturn res = gst_element_set_state(m_pGraph, GST_STATE_READY);
return res == GST_STATE_CHANGE_SUCCESS;
}
double GraphManager::GetPosition()
{
gint64 current, duration;
if(!gst_element_query_position(m_pGraph, GST_FORMAT_TIME, &current))
return 0;
if(!gst_element_query_duration(m_pGraph, GST_FORMAT_TIME, &duration))
return 0;
if(duration < 0 || current < 0)
return 0;
return (double) current / duration;
}
bool GraphManager::SetPosition(double pos)
{
GstQuery *query = gst_query_new_seeking(GST_FORMAT_TIME);
if(!query)
return false;
if(!gst_element_query(m_pGraph, query))
return false;
gboolean seekable;
gst_query_parse_seeking(query, NULL, &seekable, NULL, NULL);
gst_query_unref(query);
if(!seekable)
return false;
gint64 duration;
if(!gst_element_query_duration(m_pGraph, GST_FORMAT_TIME, &duration))
return 0;
if(duration < 0)
return 0;
gboolean seekRes = gst_element_seek_simple(m_pGraph, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, duration * pos);
return seekRes;
}

101
src/GraphManager.h Normal file
View file

@ -0,0 +1,101 @@
#ifndef GRAPH_MANAGER_H_
#define GRAPH_MANAGER_H_
#include <gst/gst.h>
#include <string>
#include <vector>
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<PadInfo> m_pads;
std::vector<Connection> 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 <ElementInfo> GetInfo();
bool OpenUri(const char *uri, const char *name);
double GetPosition();
bool SetPosition(double);
bool Play();
bool Pause();
bool Stop();
GstElement *m_pGraph;
};
#endif

366
src/MainWindow.cpp Normal file
View file

@ -0,0 +1,366 @@
#include "MainWindow.h"
#include "PluginsList.h"
#include <QToolBar>
#include <QAction>
#include <QIcon>
#include <QFileDialog>
#include <QMessageBox>
#include <QScopedArrayPointer>
#include <QDebug>
#include <QScrollArea>
#include <QLabel>
#include <QScrollArea>
#include <QPainter>
#include <QPixmap>
#include <QPolygon>
#include <QColor>
#include <QMenuBar>
#include <QMenu>
#include <QFileDialog>
#include <QInputDialog>
#include <QSettings>
#include <QDebug>
#include "GraphDisplay.h"
#include "PipelineIE.h"
#include "CustomSettings.h"
#include "SeekSlider.h"
#include "version_info.h"
#include <gst/gst.h>
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<ElementInfo> 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<ElementInfo> 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<ElementInfo> 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 = "<center><b>pipeviz</b></center><br>";
message = "<center>virinext@gmail.com</center><br>";
message += QString("<center>Version: ") + VERSION_STR + "</center><br>";
message += "<center>GUI Based on Qt</center>";
QMessageBox::about(this, "About", message);
}

56
src/MainWindow.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef MAIN_WINDOW_H_
#define MAIN_WINDOW_H_
#include <QMainWindow>
#include <QVBoxLayout>
#include <QSharedPointer>
#include <QStatusBar>
#include <QAction>
#include <QSlider>
#include <gst/gstbuffer.h>
#include <gst/gstevent.h>
#include <gst/gstcaps.h>
#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<GraphManager> m_pGraph;
GraphDisplay *m_pGraphDisplay;
QStatusBar *m_pstatusBar;
QSlider *m_pslider;
QString m_fileName;
};
#endif

95
src/PadProperties.cpp Normal file
View file

@ -0,0 +1,95 @@
#include "PadProperties.h"
#include <QGridLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QDebug>
#include <QScrollArea>
#include <gst/gst.h>
PadProperties::PadProperties(QSharedPointer<GraphManager> 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);
}

17
src/PadProperties.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef PAD_PROPERTIES_H_
#define PAD_PROPERTIES_H_
#include <QWidget>
#include <QSharedPointer>
#include "GraphManager.h"
class PadProperties: public QWidget
{
public:
PadProperties(QSharedPointer<GraphManager> pGraphManager, const char *element, const char *pad
, QWidget *parent = 0, Qt::WindowFlags flags = 0);
};
#endif

610
src/PipelineIE.cpp Normal file
View file

@ -0,0 +1,610 @@
#include "PipelineIE.h"
#include <QFile>
#include <QXmlStreamWriter>
#include <QMessageBox>
#include <QDomDocument>
#include <vector>
#include <QDebug>
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<num_props; i++)
{
GParamSpec *param = prop_specs[i];
if((param -> 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<GraphManager> 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 <ElementInfo> info = pgraph -> GetInfo();
QXmlStreamWriter xmlWriter;
xmlWriter.setDevice(&file);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("pipeline");
for(std::size_t i=0; i<info.size(); i++)
{
xmlWriter.writeStartElement("element");
xmlWriter.writeAttribute("name", info[i].m_name.c_str());
xmlWriter.writeAttribute("plugin-name", info[i].m_pluginName.c_str());
GstElement *element = gst_bin_get_by_name (GST_BIN(pgraph -> m_pGraph), info[i].m_name.c_str());
for(std::size_t j=0; j<info[i].m_pads.size(); j++)
{
xmlWriter.writeStartElement("pad");
xmlWriter.writeAttribute("name", info[i].m_pads[j].m_name.c_str());
GstPad *pad = gst_element_get_static_pad(element, info[i].m_pads[j].m_name.c_str());
GstPadTemplate *templ = gst_pad_get_pad_template(pad);
QString presence;
switch(GST_PAD_TEMPLATE_PRESENCE(templ))
{
case GST_PAD_ALWAYS:
presence = "always";
break;
case GST_PAD_SOMETIMES:
presence = "sometimes";
break;
case GST_PAD_REQUEST:
presence = "request";
break;
};
xmlWriter.writeAttribute("presence", presence);
xmlWriter.writeAttribute("template-name", GST_PAD_TEMPLATE_NAME_TEMPLATE(templ));
gst_object_unref(pad);
if(info[i].m_connections[j].m_elementId != (size_t)-1 &&
info[i].m_connections[j].m_padId != (size_t)-1)
{
std::size_t elementPos, padPos;
for(elementPos = 0; elementPos < info.size(); elementPos++)
{
if(info[elementPos].m_id == info[i].m_connections[j].m_elementId)
{
for(padPos = 0; padPos < info[elementPos].m_pads.size(); padPos++)
if(info[elementPos].m_pads[padPos].m_id == info[i].m_connections[j].m_padId)
break;
if(padPos < info[elementPos].m_pads.size())
break;
}
}
if(elementPos < info.size() && padPos < info[elementPos].m_pads.size())
{
xmlWriter.writeStartElement("connected-to");
xmlWriter.writeAttribute("element-name", info[elementPos].m_name.c_str());
xmlWriter.writeAttribute("pad-name", info[elementPos].m_pads[padPos].m_name.c_str());
xmlWriter.writeEndElement();
}
}
xmlWriter.writeEndElement();
}
writeProperties(xmlWriter, element);
gst_object_unref(element);
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
return true;
}
bool PipelineIE::Import(QSharedPointer<GraphManager> 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<Connection> 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<connections.size(); i++)
{
if((connections[i].element1 == elNode.attribute("name").toStdString() &&
connections[i].element2 == elCoonnectedTo.attribute("element-name").toStdString() &&
connections[i].pad1 == elPad.attribute("name").toStdString() &&
connections[i].pad2 == elCoonnectedTo.attribute("pad-name").toStdString())
||
(connections[i].element2 == elNode.attribute("name").toStdString() &&
connections[i].element1 == elCoonnectedTo.attribute("element-name").toStdString() &&
connections[i].pad2 == elPad.attribute("name").toStdString() &&
connections[i].pad1 ==elCoonnectedTo.attribute("pad-name").toStdString())
)
{
isExists = true;
}
}
if(!isExists)
{
Connection newConnetion;
newConnetion.element1 = elNode.attribute("name").toStdString();
newConnetion.element2 = padChild.toElement().attribute("element-name").toStdString();
newConnetion.pad1 = elementChild.toElement().attribute("name").toStdString();
newConnetion.pad2 = padChild.toElement().attribute("pad-name").toStdString();
connections.push_back(newConnetion);
}
}
padChild = padChild.nextSibling();
}
}
else if(elementChild.toElement().tagName() == "properties")
{
loadProperties(elementChild.toElement(), pel);
}
elementChild = elementChild.nextSibling();
}
}
child = child.nextSibling();
}
std::size_t maxStarts = 5;
bool setReady = true;
for(std::size_t k=0; k<maxStarts; k++)
{
if(connections.empty())
break;
if(k > 0)
setReady = false;
while(true)
{
std::size_t i=0;
for(; i<connections.size(); i++)
{
GstElement *el1 = gst_bin_get_by_name (GST_BIN(pipeline), connections[i].element1.c_str());
GstElement *el2 = gst_bin_get_by_name (GST_BIN(pipeline), connections[i].element2.c_str());
if(!el1 || !el2)
{
QMessageBox::warning(0, "Internal error",
QString("Could not find one of elements `") +
QString(connections[i].element1.c_str()) + "`, `" +
QString(connections[i].element2.c_str()) + "`");
gst_object_unref(el1);
gst_object_unref(el2);
return false;
}
GstPad *pad1 = gst_element_get_static_pad(el1, connections[i].pad1.c_str());
GstPad *pad2 = gst_element_get_static_pad(el2, connections[i].pad2.c_str());
if(pad1 && pad2)
{
if(GST_PAD_IS_SRC(pad1))
gst_element_link_pads(el1, connections[i].pad1.c_str(), el2, connections[i].pad2.c_str());
else
gst_element_link_pads(el2, connections[i].pad2.c_str(), el1, connections[i].pad1.c_str());
gst_object_unref(pad1);
gst_object_unref(pad2);
connections.erase(connections.begin() + i);
gst_object_unref(el1);
gst_object_unref(el2);
break;
}
if(pad1)
gst_object_unref(pad1);
if(pad2)
gst_object_unref(pad2);
gst_object_unref(el1);
gst_object_unref(el2);
}
if(i == connections.size())
break;
}
if(!connections.empty())
{
gst_element_set_state(pipeline, GST_STATE_PAUSED);
GstState state;
gst_element_get_state (pipeline, &state, NULL, GST_MSECOND * 2500);
}
}
if(setReady)
gst_element_set_state(pipeline, GST_STATE_READY);
return true;
}

15
src/PipelineIE.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef PIPELINE_IMPORT_EXPORT_H_
#define PIPELINE_IMPORT_EXPORT_H_
#include <QString>
#include <QSharedPointer>
#include "GraphManager.h"
namespace PipelineIE
{
bool Export(QSharedPointer<GraphManager> pgraph, const QString &fileName);
bool Import(QSharedPointer<GraphManager> pgraph, const QString &fileName);
};
#endif

209
src/PluginsList.cpp Normal file
View file

@ -0,0 +1,209 @@
#include "PluginsList.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QScrollArea>
#include <QMessageBox>
#include <QEvent>
#include <QKeyEvent>
#include <gst/gst.h>
#include <QDebug>
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 += "<b>Plugin details</b><hr>";
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 += "<b>Name</b>: " + QString(gst_plugin_get_name(plugin)) + "<br>";
descr += "<b>Description</b>: " + QString(gst_plugin_get_description(plugin)) + "<br>";
descr += "<b>Filename</b>: " + QString((filename != NULL) ? filename : "(null)") + "<br>";
descr += "<b>Version</b>: " + QString(gst_plugin_get_version (plugin)) + "<br>";
descr += "<b>License</b>: " + QString(gst_plugin_get_license (plugin)) + "<br>";
descr += "<b>Source module</b>: " + QString(gst_plugin_get_source (plugin)) + "<br>";
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 += "<b>Source release date</b>: " + QString(str) + " " + QString(tz) + "<br>";
g_free (str);
}
descr += "<b>Binary package</b>: " + QString(gst_plugin_get_package (plugin)) + "<br>";
descr += "<b>Origin URL</b>: " + QString(gst_plugin_get_origin (plugin)) + "<br>";
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<QKeyEvent*>(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<m_pPlugins -> count(); i++)
{
QListWidgetItem *pitem = m_pPlugins -> item(i);
if(pitem -> text().contains(text))
pitem -> setHidden(false);
else
pitem -> setHidden(true);
}
}

41
src/PluginsList.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef PLUGINS_LIST_H_
#define PLUGINS_LIST_H_
#include <QDialog>
#include <QLabel>
#include <QListWidgetItem>
#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

16
src/SeekSlider.cpp Normal file
View file

@ -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);
};

16
src/SeekSlider.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef SEEK_SLIDER_H_
#define SEEK_SLIDER_H_
#include <QSlider>
#include <QMouseEvent>
class SeekSlider: public QSlider
{
protected:
void mousePressEvent(QMouseEvent *);
};
#endif

15
src/main.cpp Normal file
View file

@ -0,0 +1,15 @@
#include <QApplication>
#include "MainWindow.h"
#include <gst/gst.h>
int main(int argc, char **argv)
{
gst_init (&argc, &argv);
QApplication app(argc, argv);
MainWindow wgt;
wgt.show();
return app.exec();
}

19
src/verinfo/verinfo.sh Executable file
View file

@ -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

2
src/version Normal file
View file

@ -0,0 +1,2 @@
0
1