Files
vimix/SessionVisitor.cpp
Bruno Herbelin 39b61fe331 Reimplementation of SourceInputCallbacks into Session
Session should be the object holding the list of inputs parameters (e.g. synchrony) and the list of source callbacks. This also avoids mixing input when copying sources.  Code could be improved but is operational.
2022-03-07 00:23:24 +01:00

946 lines
30 KiB
C++

/*
* This file is part of vimix - video live mixer
*
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
**/
#include <iostream>
#include <locale>
#include <tinyxml2.h>
using namespace tinyxml2;
#include "Log.h"
#include "defines.h"
#include "Scene.h"
#include "Decorations.h"
#include "Source.h"
#include "SourceCallback.h"
#include "CloneSource.h"
#include "RenderSource.h"
#include "MediaSource.h"
#include "Session.h"
#include "SessionSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "SrtReceiverSource.h"
#include "MultiFileSource.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"
#include "MixingGroup.h"
#include "SystemToolkit.h"
#include "ActionManager.h"
#include "SessionVisitor.h"
bool SessionVisitor::saveSession(const std::string& filename, Session *session)
{
// impose C locale
setlocale(LC_ALL, "C");
// creation of XML doc
XMLDocument xmlDoc;
XMLElement *rootnode = xmlDoc.NewElement(APP_NAME);
rootnode->SetAttribute("major", XML_VERSION_MAJOR);
rootnode->SetAttribute("minor", XML_VERSION_MINOR);
rootnode->SetAttribute("size", session->numSource());
rootnode->SetAttribute("date", SystemToolkit::date_time_string().c_str());
rootnode->SetAttribute("resolution", session->frame()->info().c_str());
xmlDoc.InsertEndChild(rootnode);
// 1. list of sources
XMLElement *sessionNode = xmlDoc.NewElement("Session");
xmlDoc.InsertEndChild(sessionNode);
SessionVisitor sv(&xmlDoc, sessionNode);
sv.sessionFilePath_ = SystemToolkit::path_filename(filename);
for (auto iter = session->begin(); iter != session->end(); ++iter, sv.setRoot(sessionNode) )
// source visitor
(*iter)->accept(sv);
// save session attributes
sessionNode->SetAttribute("activationThreshold", session->activationThreshold());
// save the thumbnail
FrameBufferImage *thumbnail = session->thumbnail();
if (thumbnail != nullptr && thumbnail->width > 0 && thumbnail->height > 0) {
XMLElement *thumbnailelement = xmlDoc.NewElement("Thumbnail");
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, &xmlDoc);
if (imageelement) {
sessionNode->InsertEndChild(thumbnailelement);
thumbnailelement->InsertEndChild(imageelement);
}
}
// if no thumbnail is set by user, capture thumbnail now
else {
thumbnail = session->renderThumbnail();
if (thumbnail) {
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, &xmlDoc);
if (imageelement)
sessionNode->InsertEndChild(imageelement);
delete thumbnail;
}
}
// 2. config of views
saveConfig( &xmlDoc, session );
// 3. snapshots
saveSnapshots( &xmlDoc, session );
// 4. optional notes
saveNotes( &xmlDoc, session );
// 5. optional playlists
savePlayGroups( &xmlDoc, session );
// 5. optional playlists
saveInputCallbacks( &xmlDoc, session );
// save file to disk
return ( XMLSaveDoc(&xmlDoc, filename) );
}
void SessionVisitor::saveConfig(tinyxml2::XMLDocument *doc, Session *session)
{
if (doc != nullptr && session != nullptr)
{
XMLElement *views = doc->NewElement("Views");
XMLElement *mixing = doc->NewElement( "Mixing" );
mixing->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::MIXING), doc));
views->InsertEndChild(mixing);
XMLElement *geometry = doc->NewElement( "Geometry" );
geometry->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::GEOMETRY), doc));
views->InsertEndChild(geometry);
XMLElement *layer = doc->NewElement( "Layer" );
layer->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::LAYER), doc));
views->InsertEndChild(layer);
XMLElement *appearance = doc->NewElement( "Texture" );
appearance->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::TEXTURE), doc));
views->InsertEndChild(appearance);
XMLElement *render = doc->NewElement( "Rendering" );
render->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::RENDERING), doc));
views->InsertEndChild(render);
doc->InsertEndChild(views);
}
}
void SessionVisitor::saveSnapshots(tinyxml2::XMLDocument *doc, Session *session)
{
if (doc != nullptr && session != nullptr)
{
XMLElement *snapshots = doc->NewElement("Snapshots");
const XMLElement* N = session->snapshots()->xmlDoc_->FirstChildElement();
for( ; N ; N=N->NextSiblingElement())
snapshots->InsertEndChild( N->DeepClone( doc ));
doc->InsertEndChild(snapshots);
}
}
void SessionVisitor::saveNotes(tinyxml2::XMLDocument *doc, Session *session)
{
if (doc != nullptr && session != nullptr)
{
XMLElement *notes = doc->NewElement("Notes");
for (auto nit = session->beginNotes(); nit != session->endNotes(); ++nit) {
XMLElement *note = doc->NewElement( "Note" );
note->SetAttribute("large", (*nit).large );
note->SetAttribute("stick", (*nit).stick );
XMLElement *pos = doc->NewElement("pos");
pos->InsertEndChild( XMLElementFromGLM(doc, (*nit).pos) );
note->InsertEndChild(pos);
XMLElement *size = doc->NewElement("size");
size->InsertEndChild( XMLElementFromGLM(doc, (*nit).size) );
note->InsertEndChild(size);
XMLElement *content = doc->NewElement("text");
XMLText *text = doc->NewText( (*nit).text.c_str() );
content->InsertEndChild( text );
note->InsertEndChild(content);
notes->InsertEndChild(note);
}
doc->InsertEndChild(notes);
}
}
void SessionVisitor::savePlayGroups(tinyxml2::XMLDocument *doc, Session *session)
{
if (doc != nullptr && session != nullptr)
{
XMLElement *playlistNode = doc->NewElement("PlayGroups");
std::vector<SourceIdList> pl = session->getPlayGroups();
for (auto plit = pl.begin(); plit != pl.end(); ++plit) {
XMLElement *list = doc->NewElement("PlayGroup");
playlistNode->InsertEndChild(list);
for (auto id = plit->begin(); id != plit->end(); ++id) {
XMLElement *sour = doc->NewElement("source");
sour->SetAttribute("id", *id);
list->InsertEndChild(sour);
}
}
doc->InsertEndChild(playlistNode);
}
}
void SessionVisitor::saveInputCallbacks(tinyxml2::XMLDocument *doc, Session *session)
{
// invalid inputs
if (doc != nullptr && session != nullptr)
{
// create node
XMLElement *inputsNode = doc->NewElement("InputCallbacks");
doc->InsertEndChild(inputsNode);
// list of inputs assigned in the session
std::list<uint> inputs = session->assignedInputs();
if (!inputs.empty()) {
// loop over list of inputs
for (auto i = inputs.begin(); i != inputs.end(); ++i) {
// get all callbacks for this input
auto result = session->getSourceCallbacks(*i);
for (auto kit = result.cbegin(); kit != result.cend(); ++kit) {
// create node for this callback
XMLElement *cbNode = doc->NewElement("Callback");
cbNode->SetAttribute("input", *i);
cbNode->SetAttribute("id", kit->first->id());
inputsNode->InsertEndChild(cbNode);
// launch visitor to complete attributes
SessionVisitor sv(doc, cbNode);
kit->second->accept(sv);
}
}
}
// save array of synchronyzation mode for all inputs (CSV)
std::ostringstream oss;
std::vector<Metronome::Synchronicity> synch = session->getInputSynchrony();
for (auto token = synch.begin(); token != synch.end(); ++token)
oss << (int) *token << ';';
XMLElement *syncNode = doc->NewElement("Synchrony");
XMLText *text = doc->NewText( oss.str().c_str() );
syncNode->InsertEndChild(text);
inputsNode->InsertEndChild(syncNode);
}
}
SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc,
tinyxml2::XMLElement *root,
bool recursive) : Visitor(), recursive_(recursive), xmlCurrent_(root)
{
// impose C locale
setlocale(LC_ALL, "C");
if (doc == nullptr)
xmlDoc_ = new XMLDocument;
else
xmlDoc_ = doc;
}
XMLElement *SessionVisitor::NodeToXML(const Node &n, XMLDocument *doc)
{
XMLElement *newelement = doc->NewElement("Node");
newelement->SetAttribute("visible", n.visible_);
newelement->SetAttribute("id", n.id());
XMLElement *scale = doc->NewElement("scale");
scale->InsertEndChild( XMLElementFromGLM(doc, n.scale_) );
newelement->InsertEndChild(scale);
XMLElement *translation = doc->NewElement("translation");
translation->InsertEndChild( XMLElementFromGLM(doc, n.translation_) );
newelement->InsertEndChild(translation);
XMLElement *rotation = doc->NewElement("rotation");
rotation->InsertEndChild( XMLElementFromGLM(doc, n.rotation_) );
newelement->InsertEndChild(rotation);
XMLElement *crop = doc->NewElement("crop");
crop->InsertEndChild( XMLElementFromGLM(doc, n.crop_) );
newelement->InsertEndChild(crop);
return newelement;
}
XMLElement *SessionVisitor::ImageToXML(const FrameBufferImage *img, XMLDocument *doc)
{
XMLElement *imageelement = nullptr;
if (img != nullptr) {
// get the jpeg encoded buffer
FrameBufferImage::jpegBuffer jpgimg = img->getJpeg();
if (jpgimg.buffer != nullptr) {
// fill the xml array with jpeg buffer
XMLElement *array = XMLElementEncodeArray(doc, jpgimg.buffer, jpgimg.len);
// free the buffer
free(jpgimg.buffer);
// if we could create the array
if (array) {
// create an Image node to store the mask image
imageelement = doc->NewElement("Image");
imageelement->SetAttribute("width", img->width);
imageelement->SetAttribute("height", img->height);
imageelement->InsertEndChild(array);
}
}
}
return imageelement;
}
void SessionVisitor::visit(Node &n)
{
XMLElement *newelement = NodeToXML(n, xmlDoc_);
// insert into hierarchy
xmlCurrent_->InsertEndChild(newelement);
// parent for next visits
xmlCurrent_ = newelement;
}
void SessionVisitor::visit(Group &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Group");
if (recursive_) {
// loop over members of a group
XMLElement *group = xmlCurrent_;
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
(*node)->accept(*this);
// revert to group as current
xmlCurrent_ = group;
}
}
}
void SessionVisitor::visit(Switch &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Switch");
xmlCurrent_->SetAttribute("active", n.active());
if (recursive_) {
// loop over members of the group
XMLElement *group = xmlCurrent_;
for(uint i = 0; i < n.numChildren(); ++i) {
n.child(i)->accept(*this);
// revert to group as current
xmlCurrent_ = group;
}
}
}
void SessionVisitor::visit(Primitive &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Primitive");
if (recursive_) {
// go over members of a primitive
XMLElement *Primitive = xmlCurrent_;
xmlCurrent_ = xmlDoc_->NewElement("Shader");
n.shader()->accept(*this);
Primitive->InsertEndChild(xmlCurrent_);
// revert to primitive as current
xmlCurrent_ = Primitive;
}
}
void SessionVisitor::visit(Surface &)
{
}
void SessionVisitor::visit(ImageSurface &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "ImageSurface");
XMLText *filename = xmlDoc_->NewText( n.resource().c_str() );
XMLElement *image = xmlDoc_->NewElement("resource");
image->InsertEndChild(filename);
xmlCurrent_->InsertEndChild(image);
}
void SessionVisitor::visit(FrameBufferSurface &)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "FrameBufferSurface");
}
void SessionVisitor::visit(MediaPlayer &n)
{
XMLElement *newelement = xmlDoc_->NewElement("MediaPlayer");
newelement->SetAttribute("id", n.id());
if (!n.isImage()) {
newelement->SetAttribute("play", n.isPlaying());
newelement->SetAttribute("loop", (int) n.loop());
newelement->SetAttribute("speed", n.playSpeed());
newelement->SetAttribute("software_decoding", n.softwareDecodingForced());
newelement->SetAttribute("rewind_on_disabled", n.rewindOnDisabled());
newelement->SetAttribute("sync_to_metronome", (int) n.syncToMetronome());
// timeline
XMLElement *timelineelement = xmlDoc_->NewElement("Timeline");
// gaps in timeline
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
TimeIntervalSet gaps = n.timeline()->gaps();
for( auto it = gaps.begin(); it!= gaps.end(); ++it) {
XMLElement *g = xmlDoc_->NewElement("Interval");
g->SetAttribute("begin", (*it).begin);
g->SetAttribute("end", (*it).end);
gapselement->InsertEndChild(g);
}
timelineelement->InsertEndChild(gapselement);
// fading in timeline
XMLElement *fadingelement = xmlDoc_->NewElement("Fading");
XMLElement *array = XMLElementEncodeArray(xmlDoc_, n.timeline()->fadingArray(), MAX_TIMELINE_ARRAY * sizeof(float));
fadingelement->InsertEndChild(array);
timelineelement->InsertEndChild(fadingelement);
newelement->InsertEndChild(timelineelement);
}
xmlCurrent_->InsertEndChild(newelement);
}
void SessionVisitor::visit(Shader &n)
{
// Shader of a simple type
xmlCurrent_->SetAttribute("type", "Shader");
xmlCurrent_->SetAttribute("id", n.id());
XMLElement *color = xmlDoc_->NewElement("color");
color->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.color) );
xmlCurrent_->InsertEndChild(color);
XMLElement *blend = xmlDoc_->NewElement("blending");
blend->SetAttribute("mode", int(n.blending) );
xmlCurrent_->InsertEndChild(blend);
}
void SessionVisitor::visit(ImageShader &n)
{
// Shader of a textured type
xmlCurrent_->SetAttribute("type", "ImageShader");
xmlCurrent_->SetAttribute("id", n.id());
XMLElement *uniforms = xmlDoc_->NewElement("uniforms");
uniforms->SetAttribute("stipple", n.stipple);
xmlCurrent_->InsertEndChild(uniforms);
}
void SessionVisitor::visit(MaskShader &n)
{
// Shader of a mask type
xmlCurrent_->SetAttribute("type", "MaskShader");
xmlCurrent_->SetAttribute("id", n.id());
xmlCurrent_->SetAttribute("mode", n.mode);
xmlCurrent_->SetAttribute("shape", n.shape);
XMLElement *uniforms = xmlDoc_->NewElement("uniforms");
uniforms->SetAttribute("blur", n.blur);
uniforms->SetAttribute("option", n.option);
XMLElement *size = xmlDoc_->NewElement("size");
size->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.size) );
uniforms->InsertEndChild(size);
xmlCurrent_->InsertEndChild(uniforms);
}
void SessionVisitor::visit(ImageProcessingShader &n)
{
// Shader of a textured type
xmlCurrent_->SetAttribute("type", "ImageProcessingShader");
xmlCurrent_->SetAttribute("id", n.id());
XMLElement *filter = xmlDoc_->NewElement("uniforms");
filter->SetAttribute("brightness", n.brightness);
filter->SetAttribute("contrast", n.contrast);
filter->SetAttribute("saturation", n.saturation);
filter->SetAttribute("hueshift", n.hueshift);
filter->SetAttribute("threshold", n.threshold);
filter->SetAttribute("lumakey", n.lumakey);
filter->SetAttribute("nbColors", n.nbColors);
filter->SetAttribute("invert", n.invert);
filter->SetAttribute("chromadelta", n.chromadelta);
filter->SetAttribute("filter", n.filterid);
xmlCurrent_->InsertEndChild(filter);
XMLElement *gamma = xmlDoc_->NewElement("gamma");
gamma->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.gamma) );
xmlCurrent_->InsertEndChild(gamma);
XMLElement *levels = xmlDoc_->NewElement("levels");
levels->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.levels) );
xmlCurrent_->InsertEndChild(levels);
XMLElement *chromakey = xmlDoc_->NewElement("chromakey");
chromakey->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.chromakey) );
xmlCurrent_->InsertEndChild(chromakey);
}
void SessionVisitor::visit(LineStrip &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "LineStrip");
XMLElement *points_node = xmlDoc_->NewElement("points");
std::vector<glm::vec2> path = n.path();
for(size_t i = 0; i < path.size(); ++i)
{
XMLElement *p = XMLElementFromGLM(xmlDoc_, path[i]);
p->SetAttribute("index", (int) i);
points_node->InsertEndChild(p);
}
xmlCurrent_->InsertEndChild(points_node);
}
void SessionVisitor::visit(LineSquare &)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "LineSquare");
}
void SessionVisitor::visit(Mesh &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Mesh");
XMLText *filename = xmlDoc_->NewText( n.meshPath().c_str() );
XMLElement *obj = xmlDoc_->NewElement("resource");
obj->InsertEndChild(filename);
xmlCurrent_->InsertEndChild(obj);
filename = xmlDoc_->NewText( n.texturePath().c_str() );
XMLElement *tex = xmlDoc_->NewElement("texture");
tex->InsertEndChild(filename);
xmlCurrent_->InsertEndChild(tex);
}
void SessionVisitor::visit(Frame &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Frame");
XMLElement *color = xmlDoc_->NewElement("color");
color->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.color) );
xmlCurrent_->InsertEndChild(color);
}
void SessionVisitor::visit(Scene &n)
{
XMLElement *xmlRoot = xmlDoc_->NewElement("Scene");
xmlDoc_->InsertEndChild(xmlRoot);
// start recursive traverse from root node
recursive_ = true;
xmlCurrent_ = xmlRoot;
n.root()->accept(*this);
}
void SessionVisitor::visit (Source& s)
{
XMLElement *sourceNode = xmlDoc_->NewElement( "Source" );
sourceNode->SetAttribute("id", s.id());
sourceNode->SetAttribute("name", s.name().c_str() );
sourceNode->SetAttribute("locked", s.locked() );
// insert into hierarchy
xmlCurrent_->InsertFirstChild(sourceNode);
xmlCurrent_ = xmlDoc_->NewElement( "Mixing" );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::MIXING)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Geometry" );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::GEOMETRY)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Layer" );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::LAYER)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Texture" );
xmlCurrent_->SetAttribute("mirrored", s.textureMirrored() );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::TEXTURE)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Blending" );
sourceNode->InsertEndChild(xmlCurrent_);
s.blendingShader()->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Mask" );
sourceNode->InsertEndChild(xmlCurrent_);
s.maskShader()->accept(*this);
// if we are saving a pain mask
if (s.maskShader()->mode == MaskShader::PAINT) {
// get the mask previously stored
XMLElement *imageelement = SessionVisitor::ImageToXML(s.getMask(), xmlDoc_);
if (imageelement)
xmlCurrent_->InsertEndChild(imageelement);
}
xmlCurrent_ = xmlDoc_->NewElement( "ImageProcessing" );
xmlCurrent_->SetAttribute("enabled", s.imageProcessingEnabled());
xmlCurrent_->SetAttribute("follow", s.processingshader_link_.id());
sourceNode->InsertEndChild(xmlCurrent_);
s.processingShader()->accept(*this);
if (s.mixingGroup()) {
xmlCurrent_ = xmlDoc_->NewElement( "MixingGroup" );
sourceNode->InsertEndChild(xmlCurrent_);
s.mixingGroup()->accept(*this);
}
xmlCurrent_ = sourceNode; // parent for next visits (other subtypes of Source)
}
void SessionVisitor::visit (MediaSource& s)
{
xmlCurrent_->SetAttribute("type", "MediaSource");
XMLElement *uri = xmlDoc_->NewElement("uri");
xmlCurrent_->InsertEndChild(uri);
XMLText *text = xmlDoc_->NewText( s.path().c_str() );
uri->InsertEndChild( text );
if (!sessionFilePath_.empty())
uri->SetAttribute("relative", SystemToolkit::path_relative_to_path(s.path(), sessionFilePath_).c_str());
s.mediaplayer()->accept(*this);
}
void SessionVisitor::visit (SessionFileSource& s)
{
xmlCurrent_->SetAttribute("type", "SessionSource");
if (s.session() != nullptr)
xmlCurrent_->SetAttribute("fading", s.session()->fading());
XMLElement *path = xmlDoc_->NewElement("path");
xmlCurrent_->InsertEndChild(path);
XMLText *text = xmlDoc_->NewText( s.path().c_str() );
path->InsertEndChild( text );
if (!sessionFilePath_.empty())
path->SetAttribute("relative", SystemToolkit::path_relative_to_path(s.path(), sessionFilePath_).c_str());
}
void SessionVisitor::visit (SessionGroupSource& s)
{
xmlCurrent_->SetAttribute("type", "GroupSource");
Session *se = s.session();
if (se) {
XMLElement *sessionNode = xmlDoc_->NewElement("Session");
xmlCurrent_->InsertEndChild(sessionNode);
for (auto iter = se->begin(); iter != se->end(); ++iter){
setRoot(sessionNode);
(*iter)->accept(*this);
}
}
}
void SessionVisitor::visit (RenderSource& s)
{
xmlCurrent_->SetAttribute("type", "RenderSource");
xmlCurrent_->SetAttribute("provenance", (int) s.renderingProvenance());
}
void SessionVisitor::visit (CloneSource& s)
{
xmlCurrent_->SetAttribute("type", "CloneSource");
if (s.origin()) {
xmlCurrent_->SetAttribute("provenance", (int) s.cloningProvenance());
xmlCurrent_->SetAttribute("delay", (double) s.delay());
XMLElement *origin = xmlDoc_->NewElement("origin");
origin->SetAttribute("id", s.origin()->id());
xmlCurrent_->InsertEndChild(origin);
XMLText *text = xmlDoc_->NewText( s.origin()->name().c_str() );
origin->InsertEndChild( text );
}
}
void SessionVisitor::visit (PatternSource& s)
{
xmlCurrent_->SetAttribute("type", "PatternSource");
if (s.pattern()) {
xmlCurrent_->SetAttribute("pattern", s.pattern()->type() );
XMLElement *resolution = xmlDoc_->NewElement("resolution");
resolution->InsertEndChild( XMLElementFromGLM(xmlDoc_, s.pattern()->resolution() ) );
xmlCurrent_->InsertEndChild(resolution);
}
}
void SessionVisitor::visit (DeviceSource& s)
{
xmlCurrent_->SetAttribute("type", "DeviceSource");
xmlCurrent_->SetAttribute("device", s.device().c_str() );
}
void SessionVisitor::visit (NetworkSource& s)
{
xmlCurrent_->SetAttribute("type", "NetworkSource");
xmlCurrent_->SetAttribute("connection", s.connection().c_str() );
}
void SessionVisitor::visit (MixingGroup& g)
{
xmlCurrent_->SetAttribute("size", g.size());
for (auto it = g.begin(); it != g.end(); ++it) {
XMLElement *sour = xmlDoc_->NewElement("source");
sour->SetAttribute("id", (*it)->id());
xmlCurrent_->InsertEndChild(sour);
}
}
void SessionVisitor::visit (MultiFileSource& s)
{
xmlCurrent_->SetAttribute("type", "MultiFileSource");
XMLElement *sequence = xmlDoc_->NewElement("Sequence");
// play properties
sequence->SetAttribute("fps", s.framerate());
sequence->SetAttribute("begin", s.begin());
sequence->SetAttribute("end", s.end());
sequence->SetAttribute("loop", s.loop());
// file sequence description
sequence->SetAttribute("min", s.sequence().min);
sequence->SetAttribute("max", s.sequence().max);
sequence->SetAttribute("width", s.sequence().width);
sequence->SetAttribute("height", s.sequence().height);
sequence->SetAttribute("codec", s.sequence().codec.c_str());
if (!sessionFilePath_.empty())
sequence->SetAttribute("relative", SystemToolkit::path_relative_to_path(s.sequence().location, sessionFilePath_).c_str());
XMLText *location = xmlDoc_->NewText( s.sequence().location.c_str() );
sequence->InsertEndChild( location );
xmlCurrent_->InsertEndChild(sequence);
}
void SessionVisitor::visit (GenericStreamSource& s)
{
xmlCurrent_->SetAttribute("type", "GenericStreamSource");
XMLElement *desc = xmlDoc_->NewElement("Description");
XMLText *text = xmlDoc_->NewText( s.description().c_str() );
desc->InsertEndChild( text );
xmlCurrent_->InsertEndChild(desc);
}
void SessionVisitor::visit (SrtReceiverSource& s)
{
xmlCurrent_->SetAttribute("type", "SrtReceiverSource");
XMLElement *ip = xmlDoc_->NewElement("ip");
XMLText *iptext = xmlDoc_->NewText( s.ip().c_str() );
ip->InsertEndChild( iptext );
xmlCurrent_->InsertEndChild(ip);
XMLElement *port = xmlDoc_->NewElement("port");
XMLText *porttext = xmlDoc_->NewText( s.port().c_str() );
port->InsertEndChild( porttext );
xmlCurrent_->InsertEndChild(port);
}
void SessionVisitor::visit (SourceCallback &c)
{
xmlCurrent_->SetAttribute("type", (uint) c.type());
}
void SessionVisitor::visit (Play &c)
{
xmlCurrent_->SetAttribute("play", c.value());
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
}
void SessionVisitor::visit (SetAlpha &c)
{
xmlCurrent_->SetAttribute("alpha", c.value());
xmlCurrent_->SetAttribute("duration", c.duration());
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
}
void SessionVisitor::visit (SetDepth &c)
{
xmlCurrent_->SetAttribute("depth", c.value());
xmlCurrent_->SetAttribute("duration", c.duration());
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
}
void SessionVisitor::visit (SetGeometry &c)
{
xmlCurrent_->SetAttribute("duration", c.duration());
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
// get geometry of target
Group g;
c.getTarget(&g);
XMLElement *geom = xmlDoc_->NewElement( "Geometry" );
xmlCurrent_->InsertEndChild(geom);
xmlCurrent_ = geom;
g.accept(*this);
}
void SessionVisitor::visit (Loom &c)
{
xmlCurrent_->SetAttribute("delta", c.value());
xmlCurrent_->SetAttribute("duration", c.duration());
}
void SessionVisitor::visit (Grab &c)
{
xmlCurrent_->SetAttribute("delta.x", c.value().x);
xmlCurrent_->SetAttribute("delta.y", c.value().y);
xmlCurrent_->SetAttribute("duration", c.duration());
}
void SessionVisitor::visit (Resize &c)
{
xmlCurrent_->SetAttribute("delta.x", c.value().x);
xmlCurrent_->SetAttribute("delta.y", c.value().y);
xmlCurrent_->SetAttribute("duration", c.duration());
}
void SessionVisitor::visit (Turn &c)
{
xmlCurrent_->SetAttribute("delta", c.value());
xmlCurrent_->SetAttribute("duration", c.duration());
}
std::string SessionVisitor::getClipboard(const SourceList &list)
{
std::string x = "";
if (!list.empty()) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", (int) list.size());
xmlDoc.InsertEndChild(selectionNode);
// fill doc by visiting sources
SourceList selection_clones_;
SessionVisitor sv(&xmlDoc, selectionNode);
for (auto iter = list.begin(); iter != list.end(); ++iter, sv.setRoot(selectionNode) ){
// start with clones
CloneSource *clone = dynamic_cast<CloneSource *>(*iter);
if (clone)
(*iter)->accept(sv);
else
selection_clones_.push_back(*iter);
}
// add others in front
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); ++iter, sv.setRoot(selectionNode) ){
(*iter)->accept(sv);
}
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
}
std::string SessionVisitor::getClipboard(Source * const s)
{
std::string x = "";
if (s != nullptr) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", 1);
xmlDoc.InsertEndChild(selectionNode);
// visit source
SessionVisitor sv(&xmlDoc, selectionNode);
s->accept(sv);
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
}
std::string SessionVisitor::getClipboard(ImageProcessingShader * const s)
{
std::string x = "";
if (s != nullptr) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
xmlDoc.InsertEndChild(selectionNode);
tinyxml2::XMLElement *imgprocNode = xmlDoc.NewElement( "ImageProcessing" );
selectionNode->InsertEndChild(imgprocNode);
// visit source
SessionVisitor sv(&xmlDoc, imgprocNode);
s->accept(sv);
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
}