mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
Work in progress; Sources now have play/pause and associated play functions. Media player can play all playable sources, and adapts to control a media player when possible. Selection of play groups (to finalize)
961 lines
32 KiB
C++
961 lines
32 KiB
C++
#include <sstream>
|
|
|
|
#include "Log.h"
|
|
#include "defines.h"
|
|
#include "Scene.h"
|
|
#include "Primitives.h"
|
|
#include "Mesh.h"
|
|
#include "Source.h"
|
|
#include "MediaSource.h"
|
|
#include "SessionSource.h"
|
|
#include "StreamSource.h"
|
|
#include "PatternSource.h"
|
|
#include "DeviceSource.h"
|
|
#include "NetworkSource.h"
|
|
#include "MultiFileSource.h"
|
|
#include "Session.h"
|
|
#include "ImageShader.h"
|
|
#include "ImageProcessingShader.h"
|
|
#include "MediaPlayer.h"
|
|
#include "SystemToolkit.h"
|
|
|
|
#include "tinyxml2Toolkit.h"
|
|
using namespace tinyxml2;
|
|
|
|
#include "SessionCreator.h"
|
|
|
|
SessionInformation SessionCreator::info(const std::string& filename)
|
|
{
|
|
SessionInformation ret;
|
|
|
|
// if the file exists
|
|
if (SystemToolkit::file_exists(filename)) {
|
|
// impose C locale
|
|
setlocale(LC_ALL, "C");
|
|
// try to load the file
|
|
XMLDocument doc;
|
|
XMLError eResult = doc.LoadFile(filename.c_str());
|
|
// silently ignore on error
|
|
if ( !XMLResultError(eResult, false)) {
|
|
|
|
const XMLElement *header = doc.FirstChildElement(APP_NAME);
|
|
if (header != nullptr) {
|
|
int s = header->IntAttribute("size");
|
|
ret.description = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
|
|
const char *att_string = header->Attribute("resolution");
|
|
if (att_string)
|
|
ret.description += std::string( att_string ) + "\n";
|
|
att_string = header->Attribute("date");
|
|
if (att_string) {
|
|
std::string date( att_string );
|
|
ret.description += date.substr(6,2) + "/" + date.substr(4,2) + "/" + date.substr(0,4) + " @ ";
|
|
ret.description += date.substr(8,2) + ":" + date.substr(10,2);
|
|
}
|
|
}
|
|
const XMLElement *session = doc.FirstChildElement("Session");
|
|
if (session != nullptr ) {
|
|
ret.thumbnail = XMLToImage(session);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
SessionCreator::SessionCreator(int recursion): SessionLoader(nullptr, recursion)
|
|
{
|
|
|
|
}
|
|
|
|
void SessionCreator::load(const std::string& filename)
|
|
{
|
|
XMLError eResult = xmlDoc_.LoadFile(filename.c_str());
|
|
if ( XMLResultError(eResult)){
|
|
Log::Warning("%s could not be openned.", filename.c_str());
|
|
return;
|
|
}
|
|
|
|
XMLElement *header = xmlDoc_.FirstChildElement(APP_NAME);
|
|
if (header == nullptr) {
|
|
Log::Warning("%s is not a %s session file.", filename.c_str(), APP_NAME);
|
|
return;
|
|
}
|
|
|
|
int version_major = -1, version_minor = -1;
|
|
header->QueryIntAttribute("major", &version_major);
|
|
header->QueryIntAttribute("minor", &version_minor);
|
|
if (version_major != XML_VERSION_MAJOR || version_minor != XML_VERSION_MINOR){
|
|
Log::Warning("%s session file is in version v%d.%d. but this vimix program expects v%d.%d.\n"
|
|
"Loading might fail or lead to different or incomplete configuration.\n"
|
|
"You can save this session again to avoid this warning.",
|
|
filename.c_str(), version_major, version_minor, XML_VERSION_MAJOR, XML_VERSION_MINOR);
|
|
// return;
|
|
}
|
|
|
|
// session file seems legit, create a session
|
|
session_ = new Session;
|
|
|
|
// load views config (includes resolution of session rendering)
|
|
loadConfig( xmlDoc_.FirstChildElement("Views") );
|
|
|
|
// ready to read sources
|
|
SessionLoader::load( xmlDoc_.FirstChildElement("Session") );
|
|
|
|
// create groups
|
|
std::list< SourceList > groups = getMixingGroups();
|
|
for (auto group_it = groups.begin(); group_it != groups.end(); ++group_it)
|
|
session_->link( *group_it );
|
|
|
|
// load snapshots
|
|
loadSnapshots( xmlDoc_.FirstChildElement("Snapshots") );
|
|
|
|
// load notes
|
|
loadNotes( xmlDoc_.FirstChildElement("Notes") );
|
|
|
|
// load playlists
|
|
loadPlayGroups( xmlDoc_.FirstChildElement("PlayGroups") );
|
|
|
|
// all good
|
|
session_->setFilename(filename);
|
|
}
|
|
|
|
|
|
void SessionCreator::loadConfig(XMLElement *viewsNode)
|
|
{
|
|
if (viewsNode != nullptr && session_ != nullptr) {
|
|
// ok, ready to read views
|
|
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Mixing"), *session_->config(View::MIXING));
|
|
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Geometry"), *session_->config(View::GEOMETRY));
|
|
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Layer"), *session_->config(View::LAYER));
|
|
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Texture"), *session_->config(View::TEXTURE));
|
|
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Rendering"), *session_->config(View::RENDERING));
|
|
}
|
|
}
|
|
|
|
void SessionCreator::loadSnapshots(XMLElement *snapshotsNode)
|
|
{
|
|
if (snapshotsNode != nullptr && session_ != nullptr) {
|
|
|
|
const XMLElement* N = snapshotsNode->FirstChildElement();
|
|
for( ; N ; N = N->NextSiblingElement()) {
|
|
|
|
char c;
|
|
u_int64_t id = 0;
|
|
std::istringstream nodename( N->Name() );
|
|
nodename >> c >> id;
|
|
|
|
session_->snapshots()->keys_.push_back(id);
|
|
session_->snapshots()->xmlDoc_->InsertEndChild( N->DeepClone(session_->snapshots()->xmlDoc_) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionCreator::loadNotes(XMLElement *notesNode)
|
|
{
|
|
if (notesNode != nullptr && session_ != nullptr) {
|
|
|
|
XMLElement* note = notesNode->FirstChildElement("Note");
|
|
for( ; note ; note = note->NextSiblingElement())
|
|
{
|
|
SessionNote N;
|
|
|
|
note->QueryBoolAttribute("large", &N.large);
|
|
note->QueryIntAttribute("stick", &N.stick);
|
|
XMLElement *posNode = note->FirstChildElement("pos");
|
|
if (posNode) tinyxml2::XMLElementToGLM( posNode->FirstChildElement("vec2"), N.pos);
|
|
XMLElement *sizeNode = note->FirstChildElement("size");
|
|
if (sizeNode) tinyxml2::XMLElementToGLM( sizeNode->FirstChildElement("vec2"), N.size);
|
|
XMLElement* contentNode = note->FirstChildElement("text");
|
|
if (contentNode && contentNode->GetText())
|
|
N.text = std::string ( contentNode->GetText() );
|
|
|
|
session_->addNote(N);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void SessionCreator::loadPlayGroups(tinyxml2::XMLElement *playgroupNode)
|
|
{
|
|
if (playgroupNode != nullptr && session_ != nullptr) {
|
|
|
|
XMLElement* playgroup = playgroupNode->FirstChildElement("PlayGroup");
|
|
for( ; playgroup ; playgroup = playgroup->NextSiblingElement())
|
|
{
|
|
SourceIdList playgroup_sources;
|
|
|
|
XMLElement* playgroupSourceNode = playgroup->FirstChildElement("source");
|
|
for ( ; playgroupSourceNode ; playgroupSourceNode = playgroupSourceNode->NextSiblingElement()) {
|
|
uint64_t id__ = 0;
|
|
playgroupSourceNode->QueryUnsigned64Attribute("id", &id__);
|
|
|
|
if (sources_id_.count(id__) > 0)
|
|
playgroup_sources.push_back( id__ );
|
|
|
|
}
|
|
session_->addPlayGroup( playgroup_sources );
|
|
}
|
|
}
|
|
}
|
|
|
|
SessionLoader::SessionLoader(): Visitor(),
|
|
session_(nullptr), xmlCurrent_(nullptr), recursion_(0)
|
|
{
|
|
// impose C locale
|
|
setlocale(LC_ALL, "C");
|
|
}
|
|
|
|
SessionLoader::SessionLoader(Session *session, int recursion): Visitor(),
|
|
session_(session), xmlCurrent_(nullptr), recursion_(recursion)
|
|
{
|
|
// impose C locale
|
|
setlocale(LC_ALL, "C");
|
|
}
|
|
|
|
|
|
std::map< uint64_t, Source* > SessionLoader::getSources() const
|
|
{
|
|
return sources_id_;
|
|
}
|
|
|
|
// groups_sources_id_ is parsed in XML and contains list of groups of ids
|
|
// Here we return the list of groups of newly created sources
|
|
// based on correspondance map sources_id_
|
|
// NB: importantly the list is cleared from duplicates
|
|
std::list< SourceList > SessionLoader::getMixingGroups() const
|
|
{
|
|
std::list< SourceList > groups_new_sources_id;
|
|
|
|
// perform conversion from xml id to new id
|
|
for (auto git = groups_sources_id_.begin(); git != groups_sources_id_.end(); ++git)
|
|
{
|
|
SourceList new_sources;
|
|
for (auto sit = (*git).begin(); sit != (*git).end(); ++sit ) {
|
|
if (sources_id_.count(*sit) > 0)
|
|
new_sources.push_back( sources_id_.at(*sit) );
|
|
}
|
|
new_sources.sort();
|
|
groups_new_sources_id.push_back( new_sources );
|
|
}
|
|
|
|
// remove duplicates
|
|
groups_new_sources_id.unique();
|
|
|
|
return groups_new_sources_id;
|
|
}
|
|
|
|
void SessionLoader::load(XMLElement *sessionNode)
|
|
{
|
|
sources_id_.clear();
|
|
|
|
if (recursion_ > MAX_SESSION_LEVEL) {
|
|
Log::Warning("Recursive or imbricated sessions detected! Interrupting loading after %d iterations.\n", MAX_SESSION_LEVEL);
|
|
return;
|
|
}
|
|
|
|
if (sessionNode != nullptr && session_ != nullptr) {
|
|
|
|
XMLElement* sourceNode = sessionNode->FirstChildElement("Source");
|
|
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
|
|
{
|
|
xmlCurrent_ = sourceNode;
|
|
|
|
// source to load
|
|
Source *load_source = nullptr;
|
|
|
|
// check if a source with the given id exists in the session
|
|
uint64_t id_xml_ = 0;
|
|
xmlCurrent_->QueryUnsigned64Attribute("id", &id_xml_);
|
|
SourceList::iterator sit = session_->find(id_xml_);
|
|
|
|
// no source with this id exists
|
|
if ( sit == session_->end() ) {
|
|
// create a new source depending on type
|
|
const char *pType = xmlCurrent_->Attribute("type");
|
|
if (!pType)
|
|
continue;
|
|
if ( std::string(pType) == "MediaSource") {
|
|
load_source = new MediaSource(id_xml_);
|
|
}
|
|
else if ( std::string(pType) == "SessionSource") {
|
|
load_source = new SessionFileSource(id_xml_);
|
|
}
|
|
else if ( std::string(pType) == "GroupSource") {
|
|
load_source = new SessionGroupSource(id_xml_);
|
|
}
|
|
else if ( std::string(pType) == "RenderSource") {
|
|
load_source = new RenderSource(id_xml_);
|
|
}
|
|
else if ( std::string(pType) == "PatternSource") {
|
|
load_source = new PatternSource(id_xml_);
|
|
}
|
|
else if ( std::string(pType) == "DeviceSource") {
|
|
load_source = new DeviceSource(id_xml_);
|
|
}
|
|
else if ( std::string(pType) == "NetworkSource") {
|
|
load_source = new NetworkSource(id_xml_);
|
|
}
|
|
else if ( std::string(pType) == "MultiFileSource") {
|
|
load_source = new MultiFileSource(id_xml_);
|
|
}
|
|
|
|
// skip failed (including clones)
|
|
if (!load_source)
|
|
continue;
|
|
|
|
// add source to session
|
|
session_->addSource(load_source);
|
|
}
|
|
// get reference to the existing source
|
|
else
|
|
load_source = *sit;
|
|
|
|
// apply config to source
|
|
load_source->accept(*this);
|
|
load_source->touch();
|
|
|
|
// remember
|
|
sources_id_[id_xml_] = load_source;
|
|
}
|
|
|
|
// create clones after all sources, to be able to clone a source created above
|
|
sourceNode = sessionNode->FirstChildElement("Source");
|
|
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
|
|
{
|
|
xmlCurrent_ = sourceNode;
|
|
|
|
// verify type of node
|
|
const char *pType = xmlCurrent_->Attribute("type");
|
|
if ( pType && std::string(pType) == "CloneSource") {
|
|
|
|
// check if a source with same id exists
|
|
uint64_t id_xml_ = 0;
|
|
xmlCurrent_->QueryUnsigned64Attribute("id", &id_xml_);
|
|
SourceList::iterator sit = session_->find(id_xml_);
|
|
|
|
// no source clone with this id exists
|
|
if ( sit == session_->end() ) {
|
|
|
|
// clone from given origin
|
|
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
|
|
if (originNode) {
|
|
uint64_t id_origin_ = 0;
|
|
originNode->QueryUnsigned64Attribute("id", &id_origin_);
|
|
SourceList::iterator origin;
|
|
if (id_origin_ > 0)
|
|
origin = session_->find(id_origin_);
|
|
else
|
|
origin = session_->find( std::string ( originNode->GetText() ) );
|
|
// found the orign source
|
|
if (origin != session_->end()) {
|
|
// create a new source of type Clone
|
|
Source *clone_source = (*origin)->clone(id_xml_);
|
|
|
|
// add source to session
|
|
session_->addSource(clone_source);
|
|
|
|
// apply config to source
|
|
clone_source->accept(*this);
|
|
clone_source->touch();
|
|
|
|
// remember
|
|
sources_id_[id_xml_] = clone_source;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// loop over SourceLinks and resolve them
|
|
// NB: this could become the mechanism for clone sources too
|
|
|
|
}
|
|
}
|
|
|
|
Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
|
|
{
|
|
xmlCurrent_ = sourceNode;
|
|
|
|
// source to load
|
|
Source *load_source = nullptr;
|
|
bool is_clone = false;
|
|
|
|
uint64_t id__ = 0;
|
|
xmlCurrent_->QueryUnsigned64Attribute("id", &id__);
|
|
|
|
// check if a source with the given id exists in the session
|
|
SourceList::iterator sit = session_->end();
|
|
if (mode == CLONE) {
|
|
sit = session_->find(id__);
|
|
}
|
|
|
|
// no source with this id exists or Mode DUPLICATE
|
|
if ( sit == session_->end() ) {
|
|
// create a new source depending on type
|
|
const char *pType = xmlCurrent_->Attribute("type");
|
|
if (pType) {
|
|
if ( std::string(pType) == "MediaSource") {
|
|
load_source = new MediaSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "SessionSource") {
|
|
load_source = new SessionFileSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "GroupSource") {
|
|
load_source = new SessionGroupSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "RenderSource") {
|
|
load_source = new RenderSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "PatternSource") {
|
|
load_source = new PatternSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "DeviceSource") {
|
|
load_source = new DeviceSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "NetworkSource") {
|
|
load_source = new NetworkSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "MultiFileSource") {
|
|
load_source = new MultiFileSource(id__);
|
|
}
|
|
else if ( std::string(pType) == "CloneSource") {
|
|
// clone from given origin
|
|
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
|
|
if (originNode) {
|
|
uint64_t id_origin_ = 0;
|
|
originNode->QueryUnsigned64Attribute("id", &id_origin_);
|
|
SourceList::iterator origin;
|
|
if (id_origin_ > 0)
|
|
origin = session_->find(id_origin_);
|
|
else
|
|
origin = session_->find( std::string ( originNode->GetText() ) );
|
|
// found the orign source
|
|
if (origin != session_->end())
|
|
load_source = (*origin)->clone(id__);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// clone existing source
|
|
else {
|
|
load_source = (*sit)->clone();
|
|
is_clone = true;
|
|
}
|
|
|
|
// apply config to source
|
|
if (load_source) {
|
|
load_source->accept(*this);
|
|
// increment depth for clones (avoid supperposition)
|
|
if (is_clone)
|
|
load_source->group(View::LAYER)->translation_.z += 0.2f;
|
|
}
|
|
|
|
return load_source;
|
|
}
|
|
|
|
|
|
bool SessionLoader::isClipboard(const std::string &clipboard)
|
|
{
|
|
if (clipboard.size() > 6 && clipboard.substr(0, 6) == "<" APP_NAME )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
tinyxml2::XMLElement* SessionLoader::firstSourceElement(const std::string &clipboard, XMLDocument &xmlDoc)
|
|
{
|
|
tinyxml2::XMLElement* sourceNode = nullptr;
|
|
|
|
if ( !isClipboard(clipboard) )
|
|
return sourceNode;
|
|
|
|
// header
|
|
tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
|
|
if ( XMLResultError(eResult))
|
|
return sourceNode;
|
|
|
|
tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
|
|
if ( root == nullptr )
|
|
return sourceNode;
|
|
|
|
// find node
|
|
sourceNode = root->FirstChildElement("Source");
|
|
return sourceNode;
|
|
}
|
|
|
|
void SessionLoader::applyImageProcessing(const Source &s, const std::string &clipboard)
|
|
{
|
|
if ( !isClipboard(clipboard) )
|
|
return;
|
|
|
|
// header
|
|
tinyxml2::XMLDocument xmlDoc;
|
|
tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
|
|
if ( XMLResultError(eResult))
|
|
return;
|
|
|
|
tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
|
|
if ( root == nullptr )
|
|
return;
|
|
|
|
// find node
|
|
tinyxml2::XMLElement* imgprocNode = nullptr;
|
|
tinyxml2::XMLElement* sourceNode = root->FirstChildElement("Source");
|
|
if (sourceNode == nullptr)
|
|
imgprocNode = root->FirstChildElement("ImageProcessing");
|
|
else
|
|
imgprocNode = sourceNode->FirstChildElement("ImageProcessing");
|
|
|
|
if (imgprocNode == nullptr)
|
|
return;
|
|
|
|
// create session visitor and browse
|
|
SessionLoader loader;
|
|
loader.xmlCurrent_ = imgprocNode;
|
|
s.processingShader()->accept(loader);
|
|
}
|
|
|
|
//void SessionLoader::applyMask(const Source &s, std::string clipboard)
|
|
//{
|
|
// if ( !isClipboard(clipboard) )
|
|
// return;
|
|
|
|
// // header
|
|
// tinyxml2::XMLDocument xmlDoc;
|
|
// tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
|
|
// if ( XMLResultError(eResult))
|
|
// return;
|
|
|
|
// tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
|
|
// if ( root == nullptr )
|
|
// return;
|
|
|
|
// // find node
|
|
// tinyxml2::XMLElement* naskNode = nullptr;
|
|
// tinyxml2::XMLElement* sourceNode = root->FirstChildElement("Source");
|
|
// if (sourceNode == nullptr)
|
|
// naskNode = root->FirstChildElement("Mask");
|
|
// else
|
|
// naskNode = sourceNode->FirstChildElement("ImageProcessing");
|
|
|
|
// if (naskNode == nullptr)
|
|
// return;
|
|
|
|
// // create session visitor and browse
|
|
// SessionLoader loader;
|
|
// loader.xmlCurrent_ = naskNode;
|
|
//// s.processingShader()->accept(loader);
|
|
//}
|
|
|
|
void SessionLoader::XMLToNode(const tinyxml2::XMLElement *xml, Node &n)
|
|
{
|
|
if (xml != nullptr){
|
|
const XMLElement *node = xml->FirstChildElement("Node");
|
|
if ( !node || std::string(node->Name()).find("Node") == std::string::npos )
|
|
return;
|
|
|
|
const XMLElement *scaleNode = node->FirstChildElement("scale");
|
|
if (scaleNode)
|
|
tinyxml2::XMLElementToGLM( scaleNode->FirstChildElement("vec3"), n.scale_);
|
|
const XMLElement *translationNode = node->FirstChildElement("translation");
|
|
if (translationNode)
|
|
tinyxml2::XMLElementToGLM( translationNode->FirstChildElement("vec3"), n.translation_);
|
|
const XMLElement *rotationNode = node->FirstChildElement("rotation");
|
|
if (rotationNode)
|
|
tinyxml2::XMLElementToGLM( rotationNode->FirstChildElement("vec3"), n.rotation_);
|
|
const XMLElement *cropNode = node->FirstChildElement("crop");
|
|
if (cropNode)
|
|
tinyxml2::XMLElementToGLM( cropNode->FirstChildElement("vec3"), n.crop_);
|
|
}
|
|
}
|
|
|
|
void SessionLoader::XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s)
|
|
{
|
|
SessionLoader::XMLToNode(xml->FirstChildElement("Mixing"), *s.group(View::MIXING) );
|
|
SessionLoader::XMLToNode(xml->FirstChildElement("Geometry"),*s.group(View::GEOMETRY) );
|
|
SessionLoader::XMLToNode(xml->FirstChildElement("Layer"), *s.group(View::LAYER) );
|
|
SessionLoader::XMLToNode(xml->FirstChildElement("Texture"), *s.group(View::TEXTURE) );
|
|
|
|
SessionLoader v(nullptr);
|
|
v.xmlCurrent_ = xml->FirstChildElement("ImageProcessing");
|
|
if (v.xmlCurrent_)
|
|
s.processingShader()->accept(v);
|
|
|
|
}
|
|
|
|
FrameBufferImage *SessionLoader::XMLToImage(const XMLElement *xml)
|
|
{
|
|
FrameBufferImage *i = nullptr;
|
|
|
|
if (xml != nullptr){
|
|
// if there is an Image mask stored
|
|
const XMLElement* imageNode = xml->FirstChildElement("Image");
|
|
if (imageNode) {
|
|
// get theoretical image size
|
|
int w = 0, h = 0;
|
|
imageNode->QueryIntAttribute("width", &w);
|
|
imageNode->QueryIntAttribute("height", &h);
|
|
// if there is an internal array of data
|
|
const XMLElement* array = imageNode->FirstChildElement("array");
|
|
if (array) {
|
|
// create a temporary jpeg with size of the array
|
|
FrameBufferImage::jpegBuffer jpgimg;
|
|
array->QueryUnsignedAttribute("len", &jpgimg.len);
|
|
// ok, we got a size of data to load
|
|
if (jpgimg.len>0) {
|
|
// allocate jpeg buffer
|
|
jpgimg.buffer = (unsigned char*) malloc(jpgimg.len);
|
|
// actual decoding of array
|
|
if (XMLElementDecodeArray(array, jpgimg.buffer, jpgimg.len) ) {
|
|
// create and set the image from jpeg
|
|
i = new FrameBufferImage(jpgimg);
|
|
// failed if wrong size
|
|
if ( (w>0 && h>0) && (i->width != w || i->height != h) ) {
|
|
delete i;
|
|
i = nullptr;
|
|
}
|
|
}
|
|
// free temporary buffer
|
|
if (jpgimg.buffer)
|
|
free(jpgimg.buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void SessionLoader::visit(Node &n)
|
|
{
|
|
XMLToNode(xmlCurrent_, n);
|
|
}
|
|
|
|
void SessionLoader::visit(MediaPlayer &n)
|
|
{
|
|
XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer");
|
|
|
|
if (mediaplayerNode) {
|
|
uint64_t id__ = -1;
|
|
mediaplayerNode->QueryUnsigned64Attribute("id", &id__);
|
|
|
|
// timeline
|
|
XMLElement *timelineelement = mediaplayerNode->FirstChildElement("Timeline");
|
|
if (timelineelement) {
|
|
Timeline tl;
|
|
tl.setTiming( n.timeline()->interval(), n.timeline()->step());
|
|
XMLElement *gapselement = timelineelement->FirstChildElement("Gaps");
|
|
if (gapselement) {
|
|
XMLElement* gap = gapselement->FirstChildElement("Interval");
|
|
for( ; gap ; gap = gap->NextSiblingElement())
|
|
{
|
|
GstClockTime a = GST_CLOCK_TIME_NONE;
|
|
GstClockTime b = GST_CLOCK_TIME_NONE;
|
|
gap->QueryUnsigned64Attribute("begin", &a);
|
|
gap->QueryUnsigned64Attribute("end", &b);
|
|
tl.addGap( a, b );
|
|
}
|
|
}
|
|
XMLElement *fadingselement = timelineelement->FirstChildElement("Fading");
|
|
if (fadingselement) {
|
|
XMLElement* array = fadingselement->FirstChildElement("array");
|
|
XMLElementDecodeArray(array, tl.fadingArray(), MAX_TIMELINE_ARRAY * sizeof(float));
|
|
}
|
|
n.setTimeline(tl);
|
|
}
|
|
|
|
// change play status only if different id (e.g. new media player)
|
|
if ( n.id() != id__ ) {
|
|
|
|
double speed = 1.0;
|
|
mediaplayerNode->QueryDoubleAttribute("speed", &speed);
|
|
n.setPlaySpeed(speed);
|
|
|
|
int loop = 1;
|
|
mediaplayerNode->QueryIntAttribute("loop", &loop);
|
|
n.setLoop( (MediaPlayer::LoopMode) loop);
|
|
|
|
bool gpudisable = false;
|
|
mediaplayerNode->QueryBoolAttribute("software_decoding", &gpudisable);
|
|
n.setSoftwareDecodingForced(gpudisable);
|
|
|
|
bool play = true;
|
|
mediaplayerNode->QueryBoolAttribute("play", &play);
|
|
n.play(play);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionLoader::visit(Shader &n)
|
|
{
|
|
XMLElement* color = xmlCurrent_->FirstChildElement("color");
|
|
if ( color ) {
|
|
tinyxml2::XMLElementToGLM( color->FirstChildElement("vec4"), n.color);
|
|
XMLElement* blending = xmlCurrent_->FirstChildElement("blending");
|
|
if (blending) {
|
|
int blend = 0;
|
|
blending->QueryIntAttribute("mode", &blend);
|
|
n.blending = (Shader::BlendMode) blend;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionLoader::visit(ImageShader &n)
|
|
{
|
|
const char *pType = xmlCurrent_->Attribute("type");
|
|
if ( std::string(pType) != "ImageShader" )
|
|
return;
|
|
|
|
XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms");
|
|
if (uniforms) {
|
|
uniforms->QueryFloatAttribute("stipple", &n.stipple);
|
|
}
|
|
}
|
|
|
|
void SessionLoader::visit(MaskShader &n)
|
|
{
|
|
const char *pType = xmlCurrent_->Attribute("type");
|
|
if ( std::string(pType) != "MaskShader" )
|
|
return;
|
|
|
|
xmlCurrent_->QueryUnsignedAttribute("mode", &n.mode);
|
|
xmlCurrent_->QueryUnsignedAttribute("shape", &n.shape);
|
|
|
|
XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms");
|
|
if (uniforms) {
|
|
uniforms->QueryFloatAttribute("blur", &n.blur);
|
|
uniforms->QueryIntAttribute("option", &n.option);
|
|
XMLElement* size = uniforms->FirstChildElement("size");
|
|
if (size)
|
|
tinyxml2::XMLElementToGLM( size->FirstChildElement("vec2"), n.size);
|
|
}
|
|
}
|
|
|
|
void SessionLoader::visit(ImageProcessingShader &n)
|
|
{
|
|
const char *pType = xmlCurrent_->Attribute("type");
|
|
if ( std::string(pType) != "ImageProcessingShader" )
|
|
return;
|
|
|
|
XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms");
|
|
if (uniforms) {
|
|
uniforms->QueryFloatAttribute("brightness", &n.brightness);
|
|
uniforms->QueryFloatAttribute("contrast", &n.contrast);
|
|
uniforms->QueryFloatAttribute("saturation", &n.saturation);
|
|
uniforms->QueryFloatAttribute("hueshift", &n.hueshift);
|
|
uniforms->QueryFloatAttribute("threshold", &n.threshold);
|
|
uniforms->QueryFloatAttribute("lumakey", &n.lumakey);
|
|
uniforms->QueryIntAttribute("nbColors", &n.nbColors);
|
|
uniforms->QueryIntAttribute("invert", &n.invert);
|
|
uniforms->QueryFloatAttribute("chromadelta", &n.chromadelta);
|
|
uniforms->QueryIntAttribute("filter", &n.filterid);
|
|
}
|
|
|
|
XMLElement* gamma = xmlCurrent_->FirstChildElement("gamma");
|
|
if (gamma)
|
|
tinyxml2::XMLElementToGLM( gamma->FirstChildElement("vec4"), n.gamma);
|
|
XMLElement* levels = xmlCurrent_->FirstChildElement("levels");
|
|
if (levels)
|
|
tinyxml2::XMLElementToGLM( levels->FirstChildElement("vec4"), n.levels);
|
|
XMLElement* chromakey = xmlCurrent_->FirstChildElement("chromakey");
|
|
if (chromakey)
|
|
tinyxml2::XMLElementToGLM( chromakey->FirstChildElement("vec4"), n.chromakey);
|
|
}
|
|
|
|
void SessionLoader::visit (Source& s)
|
|
{
|
|
XMLElement* sourceNode = xmlCurrent_;
|
|
const char *pName = sourceNode->Attribute("name");
|
|
s.setName(pName);
|
|
bool l = false;
|
|
sourceNode->QueryBoolAttribute("locked", &l);
|
|
s.setLocked(l);
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("Mixing");
|
|
if (xmlCurrent_) s.groupNode(View::MIXING)->accept(*this);
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("Geometry");
|
|
if (xmlCurrent_) s.groupNode(View::GEOMETRY)->accept(*this);
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("Layer");
|
|
if (xmlCurrent_) s.groupNode(View::LAYER)->accept(*this);
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("Texture");
|
|
if (xmlCurrent_) {
|
|
s.groupNode(View::TEXTURE)->accept(*this);
|
|
bool m = true;
|
|
xmlCurrent_->QueryBoolAttribute("mirrored", &m);
|
|
s.setTextureMirrored(m);
|
|
}
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("Blending");
|
|
if (xmlCurrent_)
|
|
s.blendingShader()->accept(*this);
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("Mask");
|
|
if (xmlCurrent_) {
|
|
// read the mask shader attributes
|
|
s.maskShader()->accept(*this);
|
|
// set the mask from jpeg
|
|
s.setMask( SessionLoader::XMLToImage(xmlCurrent_) );
|
|
}
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("ImageProcessing");
|
|
if (xmlCurrent_) {
|
|
bool on = xmlCurrent_->BoolAttribute("enabled", true);
|
|
uint64_t id__ = 0;
|
|
xmlCurrent_->QueryUnsigned64Attribute("follow", &id__);
|
|
s.processingShader()->accept(*this);
|
|
s.setImageProcessingEnabled(on);
|
|
s.processingshader_link_.connect(id__, session_);
|
|
}
|
|
|
|
xmlCurrent_ = sourceNode->FirstChildElement("MixingGroup");
|
|
if (xmlCurrent_) {
|
|
SourceIdList idlist;
|
|
XMLElement* mixingSourceNode = xmlCurrent_->FirstChildElement("source");
|
|
for ( ; mixingSourceNode ; mixingSourceNode = mixingSourceNode->NextSiblingElement()) {
|
|
uint64_t id__ = 0;
|
|
mixingSourceNode->QueryUnsigned64Attribute("id", &id__);
|
|
idlist.push_back(id__);
|
|
}
|
|
groups_sources_id_.push_back(idlist);
|
|
}
|
|
|
|
// restore current
|
|
xmlCurrent_ = sourceNode;
|
|
}
|
|
|
|
void SessionLoader::visit (MediaSource& s)
|
|
{
|
|
// set uri
|
|
XMLElement* uriNode = xmlCurrent_->FirstChildElement("uri");
|
|
if (uriNode) {
|
|
std::string uri = std::string ( uriNode->GetText() );
|
|
// load only new files
|
|
if ( uri != s.path() )
|
|
s.setPath(uri);
|
|
}
|
|
|
|
// set config media player
|
|
s.mediaplayer()->accept(*this);
|
|
}
|
|
|
|
void SessionLoader::visit (SessionFileSource& s)
|
|
{
|
|
// set fading
|
|
float f = 0.f;
|
|
xmlCurrent_->QueryFloatAttribute("fading", &f);
|
|
s.session()->setFading(f);
|
|
// set uri
|
|
XMLElement* pathNode = xmlCurrent_->FirstChildElement("path");
|
|
if (pathNode) {
|
|
std::string path = std::string ( pathNode->GetText() );
|
|
// load only new files
|
|
if ( path != s.path() )
|
|
s.load(path, recursion_ + 1);
|
|
}
|
|
|
|
}
|
|
|
|
void SessionLoader::visit (SessionGroupSource& s)
|
|
{
|
|
// set resolution from host session
|
|
s.setResolution( session_->config(View::RENDERING)->scale_ );
|
|
|
|
// get the inside session
|
|
XMLElement* sessionGroupNode = xmlCurrent_->FirstChildElement("Session");
|
|
if (sessionGroupNode) {
|
|
// only parse if newly created
|
|
if (s.session()->empty()) {
|
|
// load session inside group
|
|
SessionLoader grouploader( s.session(), recursion_ + 1 );
|
|
grouploader.load( sessionGroupNode );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionLoader::visit (RenderSource& s)
|
|
{
|
|
s.setSession( session_ );
|
|
}
|
|
|
|
void SessionLoader::visit (PatternSource& s)
|
|
{
|
|
uint t = xmlCurrent_->UnsignedAttribute("pattern");
|
|
|
|
glm::ivec2 resolution(800, 600);
|
|
XMLElement* res = xmlCurrent_->FirstChildElement("resolution");
|
|
if (res)
|
|
tinyxml2::XMLElementToGLM( res->FirstChildElement("ivec2"), resolution);
|
|
|
|
// change only if different pattern
|
|
if ( t != s.pattern()->type() )
|
|
s.setPattern(t, resolution);
|
|
}
|
|
|
|
void SessionLoader::visit (DeviceSource& s)
|
|
{
|
|
std::string devname = std::string ( xmlCurrent_->Attribute("device") );
|
|
|
|
// change only if different device
|
|
if ( devname != s.device() )
|
|
s.setDevice(devname);
|
|
}
|
|
|
|
|
|
void SessionLoader::visit (NetworkSource& s)
|
|
{
|
|
std::string connect = std::string ( xmlCurrent_->Attribute("connection") );
|
|
|
|
// change only if different device
|
|
if ( connect != s.connection() )
|
|
s.setConnection(connect);
|
|
}
|
|
|
|
|
|
void SessionLoader::visit (MultiFileSource& s)
|
|
{
|
|
XMLElement* seq = xmlCurrent_->FirstChildElement("Sequence");
|
|
|
|
if (seq) {
|
|
|
|
MultiFileSequence sequence;
|
|
sequence.location = std::string ( seq->GetText() );
|
|
seq->QueryIntAttribute("min", &sequence.min);
|
|
seq->QueryIntAttribute("max", &sequence.max);
|
|
seq->QueryUnsignedAttribute("width", &sequence.width);
|
|
seq->QueryUnsignedAttribute("height", &sequence.height);
|
|
const char *codec = seq->Attribute("codec");
|
|
if (codec)
|
|
sequence.codec = std::string(codec);
|
|
|
|
uint fps = 0;
|
|
seq->QueryUnsignedAttribute("fps", &fps);
|
|
|
|
// different sequence
|
|
if ( sequence != s.sequence() ) {
|
|
s.setSequence( sequence, fps);
|
|
}
|
|
// same sequence, different framerate
|
|
else if ( fps != s.framerate() ) {
|
|
s.setFramerate( fps );
|
|
}
|
|
|
|
int begin = -1;
|
|
seq->QueryIntAttribute("begin", &begin);
|
|
int end = INT_MAX;
|
|
seq->QueryIntAttribute("end", &end);
|
|
if ( begin != s.begin() || end != s.end() )
|
|
s.setRange(begin, end);
|
|
|
|
bool loop = true;
|
|
seq->QueryBoolAttribute("loop", &loop);
|
|
if ( loop != s.loop() )
|
|
s.setLoop(loop);
|
|
}
|
|
|
|
}
|
|
|
|
|