mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-09-29 06:51:56 +00:00
611 lines
15 KiB
C++
611 lines
15 KiB
C++
|
#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;
|
||
|
}
|