diff --git a/Session.cpp b/Session.cpp index 22ab417..b0e96ab 100644 --- a/Session.cpp +++ b/Session.cpp @@ -42,9 +42,13 @@ SessionNote::SessionNote(const std::string &t, bool l, int s): label(std::to_str { } -Session::Session() : active_(true), activation_threshold_(MIXING_MIN_THRESHOLD), +Session::Session(uint64_t id) : id_(id), active_(true), activation_threshold_(MIXING_MIN_THRESHOLD), filename_(""), failedSource_(nullptr), thumbnail_(nullptr) { + // create unique id + if (id_ == 0) + id_ = BaseToolkit::uniqueId(); + config_[View::RENDERING] = new Group; config_[View::RENDERING]->scale_ = glm::vec3(0.f); @@ -669,10 +673,10 @@ void Session::validate (SourceList &sources) } } -Session *Session::load(const std::string& filename, uint recursion) +Session *Session::load(const std::string& filename, uint level) { // create session - SessionCreator creator(recursion); + SessionCreator creator(level); creator.load(filename); // return created session diff --git a/Session.h b/Session.h index 0a3f705..ec7e358 100644 --- a/Session.h +++ b/Session.h @@ -38,10 +38,13 @@ class Session friend class RenderSource; public: - Session(); + Session(uint64_t id = 0); ~Session(); - static Session *load(const std::string& filename, uint recursion = 0); + // Get unique id + inline uint64_t id () const { return id_; } + + static Session *load(const std::string& filename, uint level = 0); static std::string save(const std::string& filename, Session *session, const std::string& snapshot_name = ""); // add given source into the session @@ -169,6 +172,7 @@ public: Metronome::Synchronicity inputSynchrony(uint input); protected: + uint64_t id_; bool active_; float activation_threshold_; RenderView render_; diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 5301c37..5cb9e2c 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -94,25 +94,34 @@ SessionInformation SessionCreator::info(const std::string& filename) return ret; } -SessionCreator::SessionCreator(int recursion): SessionLoader(nullptr, recursion) +SessionCreator::SessionCreator(uint level): SessionLoader(nullptr, level) { } void SessionCreator::load(const std::string& filename) { + // avoid imbricated sessions at too many depth + if (level_ > MAX_SESSION_LEVEL) { + Log::Warning("Too many imbricated sessions detected! Interrupting loading at depth %d.\n", MAX_SESSION_LEVEL); + return; + } + + // Load XML document XMLError eResult = xmlDoc_.LoadFile(filename.c_str()); if ( XMLResultError(eResult)){ Log::Warning("%s could not be opened.\n%s", filename.c_str(), xmlDoc_.ErrorStr()); return; } + // check header XMLElement *header = xmlDoc_.FirstChildElement(APP_NAME); if (header == nullptr) { - Log::Warning("%s is not a %s session file.", filename.c_str(), APP_NAME); + Log::Warning("%s is not a %s file.", filename.c_str(), APP_NAME); return; } + // check version int version_major = -1, version_minor = -1; header->QueryIntAttribute("major", &version_major); header->QueryIntAttribute("minor", &version_minor); @@ -124,15 +133,28 @@ void SessionCreator::load(const std::string& filename) // return; } + // check content + XMLElement *sessionNode = xmlDoc_.FirstChildElement("Session"); + if (sessionNode == nullptr) { + Log::Warning("%s is not a %s session file.", filename.c_str(), APP_NAME); + return; + } + + // read id of session + uint64_t id__ = 0; + sessionNode->QueryUnsigned64Attribute("id", &id__); + // session file seems legit, create a session - session_ = new Session; + session_ = new Session(id__); + + // set filename and current path + sessionFilePath_ = SystemToolkit::path_filename(filename); + session_->setFilename( filename ); // load views config (includes resolution of session rendering) loadConfig( xmlDoc_.FirstChildElement("Views") ); // ready to read sources - sessionFilePath_ = SystemToolkit::path_filename(filename); - XMLElement *sessionNode = xmlDoc_.FirstChildElement("Session"); SessionLoader::load( sessionNode ); // load snapshots @@ -157,7 +179,9 @@ void SessionCreator::load(const std::string& filename) } // all good - session_->setFilename(filename); + Log::Info("Session %s Opened '%s' (%d sources)", std::to_string(session_->id()).c_str(), + filename.c_str(), session_->numSource()); + } @@ -295,21 +319,13 @@ void SessionCreator::loadPlayGroups(tinyxml2::XMLElement *playgroupNode) } } -SessionLoader::SessionLoader(): Visitor(), - session_(nullptr), xmlCurrent_(nullptr), recursion_(0) +SessionLoader::SessionLoader(Session *session, uint level): Visitor(), + session_(session), xmlCurrent_(nullptr), level_(level) { // 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_; @@ -345,16 +361,12 @@ 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) { // // session attributes // + // read and set activation threshold float t = MIXING_MIN_THRESHOLD; sessionNode->QueryFloatAttribute("activationThreshold", &t); session_->setActivationThreshold(t); @@ -411,6 +423,9 @@ void SessionLoader::load(XMLElement *sessionNode) else if ( std::string(pType) == "SrtReceiverSource") { load_source = new SrtReceiverSource(id_xml_); } + else if ( std::string(pType) != "CloneSource") { + Log::Info("Unknown source type '%s' ignored.", pType); + } // skip failed (including clones) if (!load_source) @@ -764,7 +779,7 @@ void SessionLoader::visit(MediaPlayer &n) XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer"); if (mediaplayerNode) { - uint64_t id__ = -1; + uint64_t id__ = 0; mediaplayerNode->QueryUnsigned64Attribute("id", &id__); // timeline @@ -1004,17 +1019,28 @@ void SessionLoader::visit (SessionFileSource& s) const char * text = pathNode->GetText(); if (text) { std::string path(text); - // load only new files - if ( path != s.path() ) { - if ( !SystemToolkit::file_exists(path)){ - const char * relative; - if ( pathNode->QueryStringAttribute("relative", &relative) == XML_SUCCESS) { - std::string rel = SystemToolkit::path_absolute_from_path(std::string( relative ), sessionFilePath_); - Log::Info("File %s not found; Trying %s instead.", path.c_str(), rel.c_str()); - path = rel; - } + if ( !SystemToolkit::file_exists(path) ){ + const char * relative; + if ( pathNode->QueryStringAttribute("relative", &relative) == XML_SUCCESS) { + std::string rel = SystemToolkit::path_absolute_from_path(std::string( relative ), sessionFilePath_); + Log::Info("File '%s' not found; Trying '%s' instead.", path.c_str(), rel.c_str()); + path = rel; } - s.load(path, recursion_ + 1); + } + // load only if session source s is new + if ( path != s.path() ) { + // level of session imbrication + uint l = level_; + if ( path == session_->filename() && l < MAX_SESSION_LEVEL) { + // prevent recursive load by reaching maximum level of inclusion immediately + l += MAX_SESSION_LEVEL; + Log::Warning("Prevending recursive inclusion of Session '%s' into itself.", path.c_str()); + } + else + // normal increment level of inclusion + ++l; + // launch session loader at incremented level + s.load(path, l); } } } @@ -1032,7 +1058,7 @@ void SessionLoader::visit (SessionGroupSource& s) // only parse if newly created if (s.session()->empty()) { // load session inside group - SessionLoader grouploader( s.session(), recursion_ + 1 ); + SessionLoader grouploader( s.session(), level_ + 1 ); grouploader.load( sessionGroupNode ); } } diff --git a/SessionCreator.h b/SessionCreator.h index 892a33e..a1870ac 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -1,6 +1,7 @@ #ifndef SESSIONCREATOR_H #define SESSIONCREATOR_H +#include #include #include @@ -13,11 +14,9 @@ class FrameBufferImage; class SessionLoader : public Visitor { - SessionLoader(); - public: - SessionLoader(Session *session, int recursion = 0); + SessionLoader(Session *session = nullptr, uint level = 0); inline Session *session() const { return session_; } void load(tinyxml2::XMLElement *sessionNode); @@ -84,8 +83,8 @@ protected: std::string sessionFilePath_; // parsing current xml tinyxml2::XMLElement *xmlCurrent_; - // level of loading recursion - int recursion_; + // level of loading imbricated sessions and recursion + uint level_; // map of correspondance from xml source id (key) to new source pointer (value) std::map< uint64_t, Source* > sources_id_; // list of groups (lists of xml source id) @@ -115,7 +114,7 @@ class SessionCreator : public SessionLoader { void loadInputCallbacks(tinyxml2::XMLElement *inputsNode); public: - SessionCreator(int recursion = 0); + SessionCreator(uint level = 0); void load(const std::string& filename); diff --git a/SessionSource.cpp b/SessionSource.cpp index 4dd6532..909139c 100644 --- a/SessionSource.cpp +++ b/SessionSource.cpp @@ -174,7 +174,7 @@ SessionFileSource::SessionFileSource(uint64_t id) : SessionSource(id), path_("") } -void SessionFileSource::load(const std::string &p, uint recursion) +void SessionFileSource::load(const std::string &p, uint level) { path_ = p; @@ -192,7 +192,7 @@ void SessionFileSource::load(const std::string &p, uint recursion) } else { // launch a thread to load the session file - sessionLoader_ = std::async(std::launch::async, Session::load, path_, recursion); + sessionLoader_ = std::async(std::launch::async, Session::load, path_, level); Log::Notify("Opening %s", p.c_str()); } @@ -229,7 +229,7 @@ void SessionFileSource::init() // done init wait_for_sources_ = false; initialized_ = true; - Log::Info("Source Session %s loaded %d sources.", path_.c_str(), session_->numSource()); + Log::Info("Source '%s' linked to Session %s.", name().c_str(), std::to_string(session_->id()).c_str()); } } else if ( !failed_ ) { @@ -254,7 +254,7 @@ void SessionFileSource::init() wait_for_sources_ = true; else { initialized_ = true; - Log::Info("New Session created (%d x %d).", renderbuffer->width(), renderbuffer->height()); + Log::Info("Source '%s' linked to new Session %s.", name().c_str(), std::to_string(session_->id()).c_str()); } } } diff --git a/SessionSource.h b/SessionSource.h index b25b19e..6c058ae 100644 --- a/SessionSource.h +++ b/SessionSource.h @@ -43,7 +43,7 @@ public: void render() override; // SessionFile Source specific interface - void load(const std::string &p = "", uint recursion = 0); + void load(const std::string &p = "", uint level = 0); inline std::string path() const { return path_; } diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 118748e..8fa463c 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -75,6 +75,7 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session) (*iter)->accept(sv); // save session attributes + sessionNode->SetAttribute("id", session->id()); sessionNode->SetAttribute("activationThreshold", session->activationThreshold()); // save the thumbnail @@ -664,6 +665,7 @@ void SessionVisitor::visit (SessionGroupSource& s) Session *se = s.session(); if (se) { XMLElement *sessionNode = xmlDoc_->NewElement("Session"); + sessionNode->SetAttribute("id", se->id()); xmlCurrent_->InsertEndChild(sessionNode); for (auto iter = se->begin(); iter != se->end(); ++iter){