GstPipelineStudio/src/PipelineIE.cpp
2017-10-17 22:50:43 +02:00

602 lines
18 KiB
C++

#include "PipelineIE.h"
#include <QFile>
#include <QXmlStreamWriter>
#include <QMessageBox>
#include <QDomDocument>
#include <vector>
static void
clearPipeline (GstElement *pipeline)
{
if (!pipeline)
return;
GstIterator *iter;
iter = gst_bin_iterate_elements (GST_BIN (pipeline));
GstElement *element = NULL;
bool done = false;
while (!done) {
#if GST_VERSION_MAJOR >= 1
GValue value = G_VALUE_INIT;
switch (gst_iterator_next (iter, &value))
{
case GST_ITERATOR_OK:
{
element = GST_ELEMENT(g_value_get_object(&value));
#else
switch (gst_iterator_next (iter, (gpointer *) &element)) {
case GST_ITERATOR_OK: {
#endif
gst_bin_remove (GST_BIN (pipeline), element);
#if GST_VERSION_MAJOR >= 1
g_value_reset (&value);
#endif
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 = G_VALUE_INIT;
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);
LOG_INFO("property `%s` for `%s` not supported", propertyName.toStdString ().c_str (), elementName);
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);
LOG_INFO("problem with setting property `%s` for `%s`", name.toStdString ().c_str (), 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);
LOG_INFO("property `%s` for `%s` not supported", propName, elementName);
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);
if (templ) {
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));
}
else {
LOG_INFO("Unable to find a template for %s", info[i].m_pads[j].m_name.c_str());
xmlWriter.writeAttribute ("presence", "always");
xmlWriter.writeAttribute ("template-name", "");
}
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;
}
bool
PipelineIE::Clear (QSharedPointer<GraphManager> pgraph)
{
GstElement *pipeline = pgraph->m_pGraph;
clearPipeline (pipeline);
return true;
}