Merge pull request #33 from brunoherbelin/dev

Dev
This commit is contained in:
BHBN
2021-05-08 12:32:48 +02:00
committed by GitHub
104 changed files with 4958 additions and 2412 deletions

View File

@@ -6,10 +6,12 @@
#include "Mixer.h"
#include "MixingGroup.h"
#include "tinyxml2Toolkit.h"
#include "SessionSource.h"
#include "ImageProcessingShader.h"
#include "SessionVisitor.h"
#include "SessionCreator.h"
#include "Settings.h"
#include "BaseToolkit.h"
#include "Interpolator.h"
#include "ActionManager.h"
@@ -17,20 +19,59 @@
#define ACTION_DEBUG
#endif
#define HISTORY_NODE(i) std::to_string(i).insert(0,1,'H')
#define SNAPSHOT_NODE(i) std::to_string(i).insert(0,1,'S')
using namespace tinyxml2;
Action::Action(): step_(0), max_step_(0)
void captureMixerSession(tinyxml2::XMLDocument *doc, std::string node, std::string label)
{
// create node
XMLElement *sessionNode = doc->NewElement( node.c_str() );
doc->InsertEndChild(sessionNode);
// label describes the action
sessionNode->SetAttribute("label", label.c_str() );
// view indicates the view when this action occured
sessionNode->SetAttribute("view", (int) Mixer::manager().view()->mode());
// get session to operate on
Session *se = Mixer::manager().session();
se->lock();
// get the thumbnail (requires one opengl update to render)
FrameBufferImage *thumbnail = se->thumbnail();
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, doc);
if (imageelement)
sessionNode->InsertEndChild(imageelement);
delete thumbnail;
// save all sources using source visitor
SessionVisitor sv(doc, sessionNode);
for (auto iter = se->begin(); iter != se->end(); ++iter, sv.setRoot(sessionNode) )
(*iter)->accept(sv);
se->unlock();
}
void Action::clear()
Action::Action(): history_step_(0), history_max_step_(0), locked_(false),
snapshot_id_(0), snapshot_node_(nullptr), interpolator_(nullptr), interpolator_node_(nullptr)
{
}
void Action::init()
{
// clean the history
xmlDoc_.Clear();
step_ = 0;
max_step_ = 0;
history_doc_.Clear();
history_step_ = 0;
history_max_step_ = 0;
// reset snapshot
snapshot_id_ = 0;
snapshot_node_ = nullptr;
// start fresh
store("Session start");
}
@@ -41,69 +82,53 @@ void Action::store(const std::string &label)
return;
// incremental naming of history nodes
step_++;
std::string nodename = "H" + std::to_string(step_);
history_step_++;
// erase future
for (uint e = step_; e <= max_step_; e++) {
std::string name = "H" + std::to_string(e);
XMLElement *node = xmlDoc_.FirstChildElement( name.c_str() );
for (uint e = history_step_; e <= history_max_step_; e++) {
XMLElement *node = history_doc_.FirstChildElement( HISTORY_NODE(e).c_str() );
if ( node )
xmlDoc_.DeleteChild(node);
history_doc_.DeleteChild(node);
}
max_step_ = step_;
history_max_step_ = history_step_;
// create history node
XMLElement *sessionNode = xmlDoc_.NewElement( nodename.c_str() );
xmlDoc_.InsertEndChild(sessionNode);
// label describes the action
sessionNode->SetAttribute("label", label.c_str());
// view indicates the view when this action occured
sessionNode->SetAttribute("view", (int) Mixer::manager().view()->mode());
// threaded capturing state of current session
std::thread(captureMixerSession, &history_doc_, HISTORY_NODE(history_step_), label).detach();
// get session to operate on
Session *se = Mixer::manager().session();
// save all sources using source visitor
SessionVisitor sv(&xmlDoc_, sessionNode);
for (auto iter = se->begin(); iter != se->end(); iter++, sv.setRoot(sessionNode) )
(*iter)->accept(sv);
// debug
#ifdef ACTION_DEBUG
Log::Info("Action stored %s '%s'", nodename.c_str(), label.c_str());
// XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml");
Log::Info("Action stored %d '%s'", history_step_, label.c_str());
// XMLSaveDoc(&history_doc_, "/home/bhbn/history.xml");
#endif
}
void Action::undo()
{
// not possible to go to 1 -1 = 0
if (step_ <= 1)
if (history_step_ <= 1)
return;
// restore always changes step_ to step_ - 1
restore( step_ - 1);
restore( history_step_ - 1);
}
void Action::redo()
{
// not possible to go to max_step_ + 1
if (step_ >= max_step_)
if (history_step_ >= history_max_step_)
return;
// restore always changes step_ to step_ + 1
restore( step_ + 1);
restore( history_step_ + 1);
}
void Action::stepTo(uint target)
{
// get reasonable target
uint t = CLAMP(target, 1, max_step_);
uint t = CLAMP(target, 1, history_max_step_);
// ignore t == step_
if (t != step_)
if (t != history_step_)
restore(t);
}
@@ -111,137 +136,263 @@ std::string Action::label(uint s) const
{
std::string l = "";
if (s > 0 && s <= max_step_) {
std::string nodename = "H" + std::to_string(s);
const XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() );
l = sessionNode->Attribute("label");
if (s > 0 && s <= history_max_step_) {
const XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(s).c_str());
if (sessionNode)
l = sessionNode->Attribute("label");
}
return l;
}
FrameBufferImage *Action::thumbnail(uint s) const
{
FrameBufferImage *img = nullptr;
if (s > 0 && s <= history_max_step_) {
const XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(s).c_str());
if (sessionNode)
img = SessionLoader::XMLToImage(sessionNode);
}
return img;
}
void Action::restore(uint target)
{
// lock
locked_ = true;
// get history node of target step
step_ = CLAMP(target, 1, max_step_);
std::string nodename = "H" + std::to_string(step_);
XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() );
history_step_ = CLAMP(target, 1, history_max_step_);
XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(history_step_).c_str() );
// ask view to refresh, and switch to action view if user prefers
int view = Settings::application.current_view ;
if (Settings::application.action_history_follow_view)
sessionNode->QueryIntAttribute("view", &view);
Mixer::manager().setView( (View::Mode) view);
if (sessionNode) {
#ifdef ACTION_DEBUG
Log::Info("Restore %s '%s' ", nodename.c_str(), sessionNode->Attribute("label"));
#endif
// ask view to refresh, and switch to action view if user prefers
int view = Settings::application.current_view ;
if (Settings::application.action_history_follow_view)
sessionNode->QueryIntAttribute("view", &view);
Mixer::manager().setView( (View::Mode) view);
//
// compare source lists
//
// we operate on the current session
Session *se = Mixer::manager().session();
if (se == nullptr)
return;
// sessionsources contains list of ids of all sources currently in the session (before loading)
SourceIdList session_sources = se->getIdList();
// for( auto it = sessionsources.begin(); it != sessionsources.end(); it++)
// Log::Info("sessionsources id %s", std::to_string(*it).c_str());
// load history status:
// - if a source exists, its attributes are updated, and that's all
// - if a source does not exists (in current session), it is created inside the session
SessionLoader loader( se );
loader.load( sessionNode );
// loaded_sources contains map of xml ids of all sources treated by loader
std::map< uint64_t, Source* > loaded_sources = loader.getSources();
// remove intersect of both lists (sources were updated by SessionLoader)
for( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); ){
auto ssit = std::find(session_sources.begin(), session_sources.end(), (*lsit).first);
if ( ssit != session_sources.end() ) {
lsit = loaded_sources.erase(lsit);
session_sources.erase(ssit);
}
else
lsit++;
}
// remaining ids in list sessionsources : to remove
while ( !session_sources.empty() ){
Source *s = Mixer::manager().findSource( session_sources.front() );
if (s!=nullptr) {
#ifdef ACTION_DEBUG
Log::Info("Delete id %s\n", std::to_string(session_sources.front() ).c_str());
#endif
// remove the source from the mixer
Mixer::manager().detach( s );
// delete source from session
se->deleteSource( s );
}
session_sources.pop_front();
}
// remaining sources in list loaded_sources : to add
for ( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); lsit++)
{
#ifdef ACTION_DEBUG
Log::Info("Recreate id %s to %s\n", std::to_string((*lsit).first).c_str(), std::to_string((*lsit).second->id()).c_str());
#endif
// attach created source
Mixer::manager().attach( (*lsit).second );
// change the history to match the new id
replaceSourceId( (*lsit).first, (*lsit).second->id());
}
//
// compare mixing groups
//
// Get the list of mixing groups in the xml loader
std::list< SourceList > loadergroups = loader.getMixingGroups();
// clear all session groups
auto group_iter = se->beginMixingGroup();
while ( group_iter != se->endMixingGroup() )
group_iter = se->deleteMixingGroup(group_iter);
// apply all changes creating or modifying groups in the session
// (after this, new groups are created and existing groups are adjusted)
for (auto group_loader_it = loadergroups.begin(); group_loader_it != loadergroups.end(); group_loader_it++) {
se->link( *group_loader_it, Mixer::manager().view(View::MIXING)->scene.fg() );
// actually restore
Mixer::manager().restore(sessionNode);
}
// free
locked_ = false;
}
void Action::replaceSourceId(uint64_t previousid, uint64_t newid)
void Action::snapshot(const std::string &label)
{
// loop over every session history step
XMLElement* historyNode = xmlDoc_.FirstChildElement("H1");
for( ; historyNode ; historyNode = historyNode->NextSiblingElement())
// ignore if locked
if (locked_)
return;
std::string snap_label = BaseToolkit::uniqueName(label, labels());
// create snapshot id
u_int64_t id = BaseToolkit::uniqueId();
// get session to operate on
Session *se = Mixer::manager().session();
se->snapshots()->keys_.push_back(id);
// threaded capture state of current session
std::thread(captureMixerSession, se->snapshots()->xmlDoc_, SNAPSHOT_NODE(id), snap_label).detach();
#ifdef ACTION_DEBUG
Log::Info("Snapshot stored %d '%s'", id, snap_label.c_str());
#endif
}
void Action::open(uint64_t snapshotid)
{
if ( snapshot_id_ != snapshotid )
{
// loop over every source in session history
XMLElement* sourceNode = historyNode->FirstChildElement("Source");
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
// check if this source node has this id
uint64_t id_source_ = 0;
sourceNode->QueryUnsigned64Attribute("id", &id_source_);
if ( id_source_ == previousid )
// change to new id
sourceNode->SetAttribute("id", newid);
// get snapshot node of target in current session
Session *se = Mixer::manager().session();
snapshot_node_ = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid).c_str() );
if (snapshot_node_)
snapshot_id_ = snapshotid;
else
snapshot_id_ = 0;
interpolator_node_ = nullptr;
}
}
void Action::replace(uint64_t snapshotid)
{
// ignore if locked or if no label is given
if (locked_)
return;
if (snapshotid > 0)
open(snapshotid);
if (snapshot_node_) {
// remember label
std::string label = snapshot_node_->Attribute("label");
// remove previous node
Session *se = Mixer::manager().session();
se->snapshots()->xmlDoc_->DeleteChild( snapshot_node_ );
// threaded capture state of current session
std::thread(captureMixerSession, se->snapshots()->xmlDoc_, SNAPSHOT_NODE(snapshot_id_), label).detach();
#ifdef ACTION_DEBUG
Log::Info("Snapshot replaced %d '%s'", snapshot_id_, label.c_str());
#endif
}
}
std::list<uint64_t> Action::snapshots() const
{
return Mixer::manager().session()->snapshots()->keys_;
}
std::list<std::string> Action::labels() const
{
std::list<std::string> names;
tinyxml2::XMLDocument *doc = Mixer::manager().session()->snapshots()->xmlDoc_;
for ( XMLElement *snap = doc->FirstChildElement(); snap ; snap = snap->NextSiblingElement() )
names.push_back( snap->Attribute("label"));
return names;
}
std::string Action::label(uint64_t snapshotid) const
{
std::string label = "";
// get snapshot node of target in current session
Session *se = Mixer::manager().session();
const XMLElement *snap = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid).c_str() );
if (snap)
label = snap->Attribute("label");
return label;
}
void Action::setLabel (uint64_t snapshotid, const std::string &label)
{
open(snapshotid);
if (snapshot_node_)
snapshot_node_->SetAttribute("label", label.c_str());
}
FrameBufferImage *Action::thumbnail(uint64_t snapshotid) const
{
FrameBufferImage *img = nullptr;
// get snapshot node of target in current session
Session *se = Mixer::manager().session();
const XMLElement *snap = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid).c_str() );
if (snap){
img = SessionLoader::XMLToImage(snap);
}
return img;
}
void Action::remove(uint64_t snapshotid)
{
if (snapshotid > 0)
open(snapshotid);
if (snapshot_node_) {
// remove
Session *se = Mixer::manager().session();
se->snapshots()->xmlDoc_->DeleteChild( snapshot_node_ );
se->snapshots()->keys_.remove( snapshot_id_ );
}
snapshot_node_ = nullptr;
snapshot_id_ = 0;
}
void Action::restore(uint64_t snapshotid)
{
// lock
locked_ = true;
if (snapshotid > 0)
open(snapshotid);
if (snapshot_node_)
// actually restore
Mixer::manager().restore(snapshot_node_);
// free
locked_ = false;
store("Snapshot " + label(snapshot_id_));
}
float Action::interpolation()
{
float ret = 0.f;
if ( interpolator_node_ == snapshot_node_ && interpolator_)
ret = interpolator_->current();
return ret;
}
void Action::interpolate(float val, uint64_t snapshotid)
{
if (snapshotid > 0)
open(snapshotid);
if (snapshot_node_) {
if ( interpolator_node_ != snapshot_node_ ) {
// change interpolator
if (interpolator_)
delete interpolator_;
// create new interpolator
interpolator_ = new Interpolator;
// current session
Session *se = Mixer::manager().session();
XMLElement* N = snapshot_node_->FirstChildElement("Source");
for( ; N ; N = N->NextSiblingElement()) {
// check if a source with the given id exists in the session
uint64_t id_xml_ = 0;
N->QueryUnsigned64Attribute("id", &id_xml_);
SourceList::iterator sit = se->find(id_xml_);
// a source with this id exists
if ( sit != se->end() ) {
// read target in the snapshot xml
SourceCore target;
SessionLoader::XMLToSourcecore(N, target);
// add an interpolator for this source
interpolator_->add(*sit, target);
}
}
// operate interpolation on opened snapshot
interpolator_node_ = snapshot_node_;
}
if (interpolator_) {
// Log::Info("Action::interpolate %f", val);
interpolator_->apply( val );
}
}
}

View File

@@ -1,49 +1,76 @@
#ifndef ACTIONMANAGER_H
#define ACTIONMANAGER_H
#include <list>
#include <atomic>
#include <tinyxml2.h>
class Interpolator;
class FrameBufferImage;
class Action
{
// Private Constructor
Action();
Action(Action const& copy); // Not Implemented
Action& operator=(Action const& copy); // Not Implemented
Action(Action const& copy) = delete;
Action& operator=(Action const& copy) = delete;
public:
static Action& manager()
static Action& manager ()
{
// The only instance
static Action _instance;
return _instance;
}
void init ();
void store(const std::string &label);
// Undo History
void store (const std::string &label);
void undo ();
void redo ();
void stepTo (uint target);
void clear();
void undo();
void redo();
void stepTo(uint target);
inline uint current () const { return history_step_; }
inline uint max () const { return history_max_step_; }
std::string label (uint s) const;
FrameBufferImage *thumbnail (uint s) const;
inline uint current() const { return step_; }
inline uint max() const { return max_step_; }
// Snapshots
void snapshot (const std::string &label = "");
std::string label(uint s) const;
std::list<uint64_t> snapshots () const;
uint64_t currentSnapshot () const { return snapshot_id_; }
void open (uint64_t snapshotid);
void replace (uint64_t snapshotid = 0);
void restore (uint64_t snapshotid = 0);
void remove (uint64_t snapshotid = 0);
std::string label (uint64_t snapshotid) const;
std::list<std::string> labels () const;
void setLabel (uint64_t snapshotid, const std::string &label);
FrameBufferImage *thumbnail (uint64_t snapshotid) const;
float interpolation ();
void interpolate (float val, uint64_t snapshotid = 0);
private:
void restore(uint target);
void replaceSourceId(uint64_t previousid, uint64_t newid);
// void replaceSourceId(tinyxml2::XMLElement* parentnode, uint64_t previousid, uint64_t newid);
tinyxml2::XMLDocument xmlDoc_;
uint step_;
uint max_step_;
tinyxml2::XMLDocument history_doc_;
uint history_step_;
uint history_max_step_;
std::atomic<bool> locked_;
void restore(uint target);
uint64_t snapshot_id_;
tinyxml2::XMLElement *snapshot_node_;
Interpolator *interpolator_;
tinyxml2::XMLElement *interpolator_node_;
};
#endif // ACTIONMANAGER_H

244
BaseToolkit.cpp Normal file
View File

@@ -0,0 +1,244 @@
#include "BaseToolkit.h"
#include <chrono>
#include <ctime>
#include <sstream>
#include <list>
#include <iomanip>
#include <algorithm>
#include <climits>
#include <locale>
#include <unicode/ustream.h>
#include <unicode/translit.h>
uint64_t BaseToolkit::uniqueId()
{
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
// 64-bit int 18446744073709551615UL
return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000000000000UL;
}
std::string BaseToolkit::uniqueName(const std::string &basename, std::list<std::string> existingnames)
{
std::string tentativename = basename;
int count = 1;
int max = 100;
// while tentativename can be found in the list of existingnames
while ( std::find( existingnames.begin(), existingnames.end(), tentativename ) != existingnames.end() )
{
for( auto it = existingnames.cbegin(); it != existingnames.cend(); ++it) {
if ( it->find(tentativename) != std::string::npos)
++count;
}
if (count > 1)
tentativename = basename + "_" + std::to_string( count );
else
tentativename += "_";
if ( --max < 0 ) // for safety only, should never be needed
break;
}
return tentativename;
}
// Using ICU transliteration :
// https://unicode-org.github.io/icu/userguide/transforms/general/#icu-transliterators
std::string BaseToolkit::transliterate(const std::string &input)
{
auto ucs = icu::UnicodeString::fromUTF8(input);
UErrorCode status = U_ZERO_ERROR;
icu::Transliterator *firstTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; NFKC; Latin", UTRANS_FORWARD, status);
firstTrans->transliterate(ucs);
delete firstTrans;
icu::Transliterator *secondTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; [@!#$*%~] Remove; NFKC", UTRANS_FORWARD, status);
secondTrans->transliterate(ucs);
delete secondTrans;
std::ostringstream output;
output << ucs;
return output.str();
}
std::string BaseToolkit::byte_to_string(long b)
{
double numbytes = static_cast<double>(b);
std::ostringstream oss;
std::list<std::string> list = {" Bytes", " KB", " MB", " GB", " TB"};
std::list<std::string>::iterator i = list.begin();
while(numbytes >= 1024.0 && i != list.end())
{
++i;
numbytes /= 1024.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
return oss.str();
}
std::string BaseToolkit::bits_to_string(long b)
{
double numbytes = static_cast<double>(b);
std::ostringstream oss;
std::list<std::string> list = {" bit", " Kbit", " Mbit", " Gbit", " Tbit"};
std::list<std::string>::iterator i = list.begin();
while(numbytes >= 1000.0 && i != list.end())
{
++i;
numbytes /= 1000.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
return oss.str();
}
std::string BaseToolkit::trunc_string(const std::string& path, int N)
{
std::string trunc = path;
int l = path.size();
if ( l > N ) {
trunc = std::string("...") + path.substr( l - N + 3 );
}
return trunc;
}
std::string BaseToolkit::common_prefix( const std::list<std::string> & allStrings )
{
if (allStrings.empty())
return std::string();
const std::string &s0 = allStrings.front();
auto _end = s0.cend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
if (std::distance(_loc.first, _end) > 0)
_end = _loc.first;
}
return std::string(s0.cbegin(), _end);
}
std::string BaseToolkit::common_suffix(const std::list<std::string> & allStrings)
{
if (allStrings.empty())
return std::string();
const std::string &s0 = allStrings.front();
auto r_end = s0.crend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
if (std::distance(r_loc.first, r_end) > 0)
r_end = r_loc.first;
}
std::string suffix = std::string(s0.crbegin(), r_end);
std::reverse(suffix.begin(), suffix.end());
return suffix;
}
std::string BaseToolkit::common_pattern(const std::list<std::string> &allStrings)
{
if (allStrings.empty())
return std::string();
// find common prefix and suffix
const std::string &s0 = allStrings.front();
auto _end = s0.cend();
auto r_end = s0.crend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
if (std::distance(_loc.first, _end) > 0)
_end = _loc.first;
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
if (std::distance(r_loc.first, r_end) > 0)
r_end = r_loc.first;
}
std::string suffix = std::string(s0.crbegin(), r_end);
std::reverse(suffix.begin(), suffix.end());
return std::string(s0.cbegin(), _end) + "*" + suffix;
}
std::string BaseToolkit::common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max)
{
if (allStrings.empty())
return std::string();
// find common prefix and suffix
const std::string &s0 = allStrings.front();
auto _end = s0.cend();
auto r_end = s0.crend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
if (std::distance(_loc.first, _end) > 0)
_end = _loc.first;
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
if (std::distance(r_loc.first, r_end) > 0)
r_end = r_loc.first;
}
// range of middle string, after prefix and before suffix
size_t pos_prefix = std::distance(s0.cbegin(), _end);
size_t pos_suffix = s0.size() - pos_prefix - std::distance(s0.crbegin(), r_end);
int n = -1;
*max = 0;
*min = INT_MAX;
// loop over all strings to verify there are numbers between prefix and suffix
for (auto it = allStrings.cbegin(); it != allStrings.cend(); ++it)
{
// get middle string, after prefix and before suffix
std::string s = it->substr(pos_prefix, pos_suffix);
// is this central string ONLY made of digits?
if (s.end() == std::find_if(s.begin(), s.end(), [](unsigned char c)->bool { return !isdigit(c); })) {
// yes, validate
*max = std::max(*max, std::atoi(s.c_str()) );
*min = std::min(*min, std::atoi(s.c_str()) );
if (n < 0)
n = s.size();
else if ( n != s.size() ) {
n = 0;
break;
}
}
else {
n = 0;
break;
}
}
if ( n < 1 )
return std::string();
std::string suffix = std::string(s0.crbegin(), r_end);
std::reverse(suffix.begin(), suffix.end());
std::string pattern = std::string(s0.cbegin(), _end);
pattern += "%0" + std::to_string(n) + "d";
pattern += suffix;
return pattern;
}

41
BaseToolkit.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef BASETOOLKIT_H
#define BASETOOLKIT_H
#include <list>
#include <string>
namespace BaseToolkit
{
// get integer with unique id
uint64_t uniqueId();
// proposes a name that is not already in the list
std::string uniqueName(const std::string &basename, std::list<std::string> existingnames);
// get a transliteration to Latin of any string
std::string transliterate(const std::string &input);
// get a string to display memory size with unit KB, MB, GB, TB
std::string byte_to_string(long b);
// get a string to display bit size with unit Kbit, MBit, Gbit, Tbit
std::string bits_to_string(long b);
// Truncate a string to display the right most N characters (e.g. ./home/me/toto.mpg -> ...ome/me/toto.mpg)
std::string trunc_string(const std::string& path, int N);
// find common parts in a list of strings
std::string common_prefix(const std::list<std::string> &allStrings);
std::string common_suffix(const std::list<std::string> &allStrings);
// form a pattern "prefix*suffix" (e.g. file list)
std::string common_pattern(const std::list<std::string> &allStrings);
// form a pattern "prefix%03dsuffix" (e.g. numbered file list)
std::string common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max);
}
#endif // BASETOOLKIT_H

View File

@@ -72,7 +72,7 @@ GlmToolkit::AxisAlignedBoundingBox BoundingBoxVisitor::AABB(SourceList l, View *
{
// calculate bbox on selection
BoundingBoxVisitor selection_visitor_bbox;
for (auto it = l.begin(); it != l.end(); it++) {
for (auto it = l.begin(); it != l.end(); ++it) {
// calculate bounding box of area covered by selection
selection_visitor_bbox.setModelview( view->scene.ws()->transform_ );
(*it)->group( view->mode() )->accept(selection_visitor_bbox);
@@ -86,7 +86,7 @@ GlmToolkit::OrientedBoundingBox BoundingBoxVisitor::OBB(SourceList l, View *view
GlmToolkit::OrientedBoundingBox obb_;
// try the orientation of each source in the list
for (auto source_it = l.begin(); source_it != l.end(); source_it++) {
for (auto source_it = l.begin(); source_it != l.end(); ++source_it) {
float angle = (*source_it)->group( view->mode() )->rotation_.z;
glm::mat4 transform = view->scene.ws()->transform_;
@@ -94,7 +94,7 @@ GlmToolkit::OrientedBoundingBox BoundingBoxVisitor::OBB(SourceList l, View *view
// calculate bbox of the list in this orientation
BoundingBoxVisitor selection_visitor_bbox;
for (auto it = l.begin(); it != l.end(); it++) {
for (auto it = l.begin(); it != l.end(); ++it) {
// calculate bounding box of area covered by sources' nodes
selection_visitor_bbox.setModelview( transform );
(*it)->group( view->mode() )->accept(selection_visitor_bbox);

View File

@@ -40,6 +40,7 @@ if(UNIX)
set(CMAKE_OSX_ARCHITECTURES x86_64)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
# set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X version to target for deployment")
set (ICU_ROOT /usr/local/Cellar/icu4c/67.1)
else()
add_definitions(-DLINUX)
@@ -267,6 +268,7 @@ set(VMIX_BINARY "vimix")
set(VMIX_SRCS
main.cpp
Log.cpp
BaseToolkit.cpp
Shader.cpp
ImageShader.cpp
ImageProcessingShader.cpp
@@ -289,7 +291,7 @@ set(VMIX_SRCS
Selection.cpp
SessionSource.cpp
SessionVisitor.cpp
GarbageVisitor.cpp
Interpolator.cpp
SessionCreator.cpp
Mixer.cpp
FrameGrabber.cpp
@@ -299,7 +301,6 @@ set(VMIX_SRCS
Settings.cpp
Screenshot.cpp
Resource.cpp
FileDialog.cpp
Timeline.cpp
Stream.cpp
MediaPlayer.cpp
@@ -308,6 +309,7 @@ set(VMIX_SRCS
PatternSource.cpp
DeviceSource.cpp
NetworkSource.cpp
MultiFileSource.cpp
FrameBuffer.cpp
RenderingManager.cpp
UserInterfaceManager.cpp
@@ -325,6 +327,7 @@ set(VMIX_SRCS
NetworkToolkit.cpp
Connection.cpp
ActionManager.cpp
Overlay.cpp
)
@@ -427,6 +430,7 @@ set(VMIX_RSC_FILES
./rsc/mesh/icon_eye_slash.ply
./rsc/mesh/icon_vector_square_slash.ply
./rsc/mesh/icon_cube.ply
./rsc/mesh/icon_sequence.ply
./rsc/mesh/h_line.ply
./rsc/mesh/h_mark.ply
)

View File

@@ -62,8 +62,8 @@ class Connection
// Private Constructor
Connection();
Connection(Connection const& copy); // Not Implemented
Connection& operator=(Connection const& copy); // Not Implemented
Connection(Connection const& copy) = delete;
Connection& operator=(Connection const& copy) = delete;
public:
static Connection& manager()

85
CopyVisitor.cpp Normal file
View File

@@ -0,0 +1,85 @@
#include "GlmToolkit.h"
#include "Scene.h"
#include "Source.h"
#include "CopyVisitor.h"
Node *CopyVisitor::deepCopy(Node *node)
{
CopyVisitor cv;
node->accept(cv);
return cv.current_;
}
CopyVisitor::CopyVisitor() : current_(nullptr)
{
}
void CopyVisitor::visit(Node &n)
{
}
void CopyVisitor::visit(Group &n)
{
Group *here = new Group;
// node
current_ = here;
current_->copyTransform(&n);
current_->visible_ = n.visible_;
// loop
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
(*node)->accept(*this);
here->attach( current_ );
current_ = here;
}
}
void CopyVisitor::visit(Switch &n)
{
Switch *here = new Switch;
// node
current_ = here;
current_->copyTransform(&n);
current_->visible_ = n.visible_;
// switch properties
here->setActive( n.active() );
// loop
for (uint i = 0; i < n.numChildren(); ++i) {
n.child(i)->accept(*this);
here->attach( current_ );
current_ = here;
}
}
void CopyVisitor::visit(Scene &n)
{
Scene *here = new Scene;
current_ = here->root();
n.root()->accept(*this);
}
void CopyVisitor::visit(Primitive &n)
{
Primitive *here = new Primitive;
// node
current_ = here;
current_->copyTransform(&n);
current_->visible_ = n.visible_;
}

24
CopyVisitor.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef COPYVISITOR_H
#define COPYVISITOR_H
#include "Visitor.h"
class SourceCore;
class CopyVisitor : public Visitor
{
Node *current_;
CopyVisitor();
public:
static Node *deepCopy(Node *node);
void visit(Scene& n) override;
void visit(Node& n) override;
void visit(Primitive& n) override;
void visit(Group& n) override;
void visit(Switch& n) override;
};
#endif // COPYVISITOR_H

View File

@@ -404,6 +404,8 @@ Symbol::Symbol(Type t, glm::vec3 pos) : Node(), type_(t)
shadows[SQUARE_POINT] = nullptr;
icons[IMAGE] = new Mesh("mesh/icon_image.ply");
shadows[IMAGE] = shadow;
icons[SEQUENCE] = new Mesh("mesh/icon_sequence.ply");
shadows[SEQUENCE]= shadow;
icons[VIDEO] = new Mesh("mesh/icon_video.ply");
shadows[VIDEO] = shadow;
icons[SESSION] = new Mesh("mesh/icon_vimix.ply");

View File

@@ -60,9 +60,9 @@ protected:
class Symbol : public Node
{
public:
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, SEQUENCE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
DOTS, BUSY, LOCK, UNLOCK, EYE, EYESLASH, VECTORSLASH, ARROWS, ROTATION, CROP, CIRCLE, SQUARE, CLOCK, CLOCK_H, GRID, CROSS, EMPTY } Type;
Symbol(Type t = CIRCLE_POINT, glm::vec3 pos = glm::vec3(0.f));
Symbol(Type t, glm::vec3 pos = glm::vec3(0.f));
~Symbol();
void draw (glm::mat4 modelview, glm::mat4 projection) override;

View File

@@ -325,7 +325,7 @@ int Device::index(const std::string &device) const
return i;
}
DeviceSource::DeviceSource() : StreamSource()
DeviceSource::DeviceSource(uint64_t id) : StreamSource(id)
{
// create stream
stream_ = new Stream;
@@ -342,7 +342,7 @@ DeviceSource::~DeviceSource()
}
void DeviceSource::setDevice(const std::string &devicename)
{
{
device_ = devicename;
Log::Notify("Creating Source with device '%s'", device_.c_str());
@@ -384,8 +384,12 @@ void DeviceSource::setDevice(const std::string &devicename)
pipeline << " ! videoconvert";
// open gstreamer
stream_->open( pipeline.str(), best.width, best.height);
stream_->play(true);
// will be ready after init and one frame rendered
ready_ = false;
}
else
Log::Warning("No such device '%s'", device_.c_str());

View File

@@ -10,7 +10,7 @@
class DeviceSource : public StreamSource
{
public:
DeviceSource();
DeviceSource(uint64_t id = 0);
~DeviceSource();
// Source interface
@@ -87,8 +87,8 @@ class Device
friend class DeviceSource;
Device();
Device(Device const& copy); // Not Implemented
Device& operator=(Device const& copy); // Not Implemented
Device(Device const& copy) = delete;
Device& operator=(Device const& copy) = delete;
public:

View File

@@ -178,7 +178,7 @@ std::string DialogToolkit::openSessionFileDialog(const std::string &path)
}
std::string DialogToolkit::ImportFileDialog(const std::string &path)
std::string DialogToolkit::openMediaFileDialog(const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
@@ -190,7 +190,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
"*.gif", "*.tif", "*.svg" };
#if USE_TINYFILEDIALOG
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
open_file_name = tinyfd_openFileDialog( "Open Media File", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
if (open_file_name)
filename = std::string(open_file_name);
@@ -201,7 +201,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Import Media File", NULL,
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Media File", NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
@@ -238,7 +238,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
return filename;
}
std::string DialogToolkit::FolderDialog(const std::string &path)
std::string DialogToolkit::openFolderDialog(const std::string &path)
{
std::string foldername = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
@@ -290,6 +290,91 @@ std::string DialogToolkit::FolderDialog(const std::string &path)
}
std::list<std::string> DialogToolkit::selectImagesFileDialog(const std::string &path)
{
std::list<std::string> files;
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[3] = { "*.tif", "*.jpg", "*.png" };
#if USE_TINYFILEDIALOG
char const * open_file_names;
open_file_names = tinyfd_openFileDialog( "Select images", startpath.c_str(), 3, open_pattern, "Images", 1);
if (open_file_names) {
const std::string& str (open_file_names);
const std::string& delimiters = "|";
// Skip delimiters at beginning.
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first non-delimiter.
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
while (std::string::npos != pos || std::string::npos != lastPos) {
// Found a token, add it to the vector.
files.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters.
lastPos = str.find_first_not_of(delimiters, pos);
// Find next non-delimiter.
pos = str.find_first_of(delimiters, lastPos);
}
}
#else
if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog");
return files;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select images", NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 3, open_pattern, "All supported formats");
add_filter_any_file_dialog(dialog);
// multiple files
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), true );
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
GSList *open_file_names = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) );
while (open_file_names) {
files.push_back( (char *) open_file_names->data );
open_file_names = open_file_names->next;
// g_free( open_file_names->data );
}
g_slist_free( open_file_names );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return files;
}
void DialogToolkit::ErrorDialog(const char* message)
{
#if USE_TINYFILEDIALOG

View File

@@ -2,6 +2,7 @@
#define DIALOGTOOLKIT_H
#include <string>
#include <list>
namespace DialogToolkit
@@ -11,9 +12,11 @@ std::string saveSessionFileDialog(const std::string &path);
std::string openSessionFileDialog(const std::string &path);
std::string ImportFileDialog(const std::string &path);
std::string openMediaFileDialog(const std::string &path);
std::string FolderDialog(const std::string &path);
std::string openFolderDialog(const std::string &path);
std::list<std::string> selectImagesFileDialog(const std::string &path);
void ErrorDialog(const char* message);

View File

@@ -178,7 +178,7 @@ inline PathStruct ParsePathFileName(const std::string& vPathFileName)
return res;
}
inline void AppendToBuffer(char* vBuffer, size_t vBufferLen, std::string vStr)
inline void AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string &vStr)
{
std::string st = vStr;
size_t len = vBufferLen - 1u;
@@ -253,8 +253,6 @@ static bool stringComparator(const FileInfoStruct& a, const FileInfoStruct& b)
void FileDialog::ScanDir(const std::string& vPath)
{
struct dirent **files = NULL;
int i = 0;
int n = 0;
std::string path = vPath;
#if defined(LINUX) or defined(APPLE)
@@ -280,12 +278,12 @@ void FileDialog::ScanDir(const std::string& vPath)
path += PATH_SEP;
}
#endif
n = scandir(path.c_str(), &files, NULL, alphaSort);
int n = scandir(path.c_str(), &files, NULL, alphaSort);
if (n > 0)
{
m_FileList.clear();
for (i = 0; i < n; i++)
for (int i = 0; i < n; ++i)
{
struct dirent *ent = files[i];
@@ -328,7 +326,7 @@ void FileDialog::ScanDir(const std::string& vPath)
}
}
for (i = 0; i < n; i++)
for (int i = 0; i < n; ++i)
{
free(files[i]);
}
@@ -434,7 +432,7 @@ void FileDialog::ComposeNewPath(std::vector<std::string>::iterator vIter)
break;
}
vIter--;
--vIter;
}
}
@@ -703,9 +701,9 @@ bool FileDialog::Render(const std::string& vKey, ImVec2 geometry)
SetPath("."); // Go Home
}
#ifdef WIN32
bool drivesClick = false;
#ifdef WIN32
ImGui::SameLine();
if (ImGui::Button("Drives"))
@@ -865,10 +863,12 @@ bool FileDialog::Render(const std::string& vKey, ImVec2 geometry)
SetPath(m_CurrentPath);
}
#ifdef WIN32
if (drivesClick == true)
{
GetDrives();
}
#endif
ImGui::EndChild();
ImGui::PopFont();
@@ -1013,12 +1013,12 @@ std::string FileDialog::GetUserString()
return dlg_userString;
}
void FileDialog::SetFilterColor(std::string vFilter, ImVec4 vColor)
void FileDialog::SetFilterColor(const std::string &vFilter, ImVec4 vColor)
{
m_FilterColor[vFilter] = vColor;
}
bool FileDialog::GetFilterColor(std::string vFilter, ImVec4 *vColor)
bool FileDialog::GetFilterColor(const std::string &vFilter, ImVec4 *vColor)
{
if (vColor)
{
@@ -1054,7 +1054,7 @@ inline void InfosPane(std::string vFilter, bool *vCantContinue)
*vCantContinue = canValidateDialog;
}
inline void TextInfosPane(std::string vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
inline void TextInfosPane(const std::string &vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
{
ImGui::TextColored(ImVec4(0, 1, 1, 1), "Text");
@@ -1091,7 +1091,7 @@ inline void TextInfosPane(std::string vFilter, bool *vCantContinue) // if vCantC
*vCantContinue = text.size() > 0;
}
inline void ImageInfosPane(std::string vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
inline void ImageInfosPane(const std::string &vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
{
// opengl texture
static GLuint tex = 0;

View File

@@ -71,8 +71,8 @@ public:
protected:
FileDialog(); // Prevent construction
FileDialog(const FileDialog&) {} // Prevent construction by copying
FileDialog& operator =(const FileDialog&) { return *this; } // Prevent assignment
FileDialog(const FileDialog&) = delete; // Prevent construction by copying
FileDialog& operator =(const FileDialog&) = delete; // Prevent assignment
~FileDialog(); // Prevent unwanted destruction
public:
@@ -95,8 +95,8 @@ public:
std::string GetCurrentFilter();
std::string GetUserString();
void SetFilterColor(std::string vFilter, ImVec4 vColor);
bool GetFilterColor(std::string vFilter, ImVec4 *vColor);
void SetFilterColor(const std::string &vFilter, ImVec4 vColor);
bool GetFilterColor(const std::string &vFilter, ImVec4 *vColor);
void ClearFilterColor();
private:

View File

@@ -227,13 +227,17 @@ void FrameBuffer::readPixels(uint8_t *target_data)
bool FrameBuffer::blit(FrameBuffer *destination)
{
if (!framebufferid_ || !destination)
if (!framebufferid_ || !destination || (use_alpha_ != destination->use_alpha_) )
return false;
if (!destination->framebufferid_)
destination->init();
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
if (use_multi_sampling_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_framebufferid_);
else
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebufferid_);
// blit to the frame buffer object
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
@@ -318,7 +322,7 @@ FrameBufferImage::~FrameBufferImage() {
delete rgb;
}
FrameBufferImage::jpegBuffer FrameBufferImage::getJpeg()
FrameBufferImage::jpegBuffer FrameBufferImage::getJpeg() const
{
jpegBuffer jpgimg;
@@ -377,7 +381,9 @@ bool FrameBuffer::fill(FrameBufferImage *image)
// fill texture with image
glBindTexture(GL_TEXTURE_2D, textureid_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height,
GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}

View File

@@ -12,7 +12,7 @@
class FrameBufferImage
{
public:
uint8_t *rgb = nullptr;
uint8_t *rgb;
int width;
int height;
@@ -20,10 +20,13 @@ public:
unsigned char *buffer = nullptr;
uint len = 0;
};
jpegBuffer getJpeg();
jpegBuffer getJpeg() const;
FrameBufferImage(int w, int h);
FrameBufferImage(jpegBuffer jpgimg);
// non assignable class
FrameBufferImage(FrameBufferImage const&) = delete;
FrameBufferImage& operator=(FrameBufferImage const&) = delete;
~FrameBufferImage();
};
@@ -45,6 +48,7 @@ public:
FrameBuffer(glm::vec3 resolution, bool useAlpha = false, bool multiSampling = false);
FrameBuffer(uint width, uint height, bool useAlpha = false, bool multiSampling = false);
FrameBuffer(FrameBuffer const&) = delete;
~FrameBuffer();
// Bind & push attribs to prepare draw

View File

@@ -10,6 +10,7 @@
#include "defines.h"
#include "Log.h"
#include "GstToolkit.h"
#include "BaseToolkit.h"
#include "FrameBuffer.h"
#include "FrameGrabber.h"
@@ -30,8 +31,8 @@ FrameGrabbing::~FrameGrabbing()
// cleanup
if (caps_)
gst_caps_unref (caps_);
if (pbo_[0])
glDeleteBuffers(2, pbo_);
// if (pbo_[0] > 0) // automatically deleted at shutdown
// glDeleteBuffers(2, pbo_);
}
void FrameGrabbing::add(FrameGrabber *rec)
@@ -145,6 +146,7 @@ void FrameGrabbing::grabFrame(FrameBuffer *frame_buffer, float dt)
#else
glBindTexture(GL_TEXTURE_2D, frame_buffer->texture());
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
#endif
// update case ; alternating indices
@@ -210,7 +212,7 @@ FrameGrabber::FrameGrabber(): finished_(false), active_(false), accept_buffer_(f
pipeline_(nullptr), src_(nullptr), caps_(nullptr), timestamp_(0)
{
// unique id
id_ = GlmToolkit::uniqueId();
id_ = BaseToolkit::uniqueId();
// configure fix parameter
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, 30); // 30 FPS
timeframe_ = 2 * frame_duration_;

View File

@@ -8,7 +8,6 @@
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include "GlmToolkit.h"
// use glReadPixel or glGetTextImage
// read pixels & pbo should be the fastest
@@ -84,8 +83,8 @@ class FrameGrabbing
// Private Constructor
FrameGrabbing();
FrameGrabbing(FrameGrabbing const& copy); // Not Implemented
FrameGrabbing& operator=(FrameGrabbing const& copy); // Not Implemented
FrameGrabbing(FrameGrabbing const& copy) = delete;
FrameGrabbing& operator=(FrameGrabbing const& copy) = delete;
public:

View File

@@ -82,73 +82,3 @@ void GarbageVisitor::visit(Primitive &n)
}
void GarbageVisitor::visit(Surface &n)
{
}
void GarbageVisitor::visit(ImageSurface &n)
{
}
void GarbageVisitor::visit(FrameBufferSurface &n)
{
}
void GarbageVisitor::visit(MediaSurface &n)
{
}
void GarbageVisitor::visit(MediaPlayer &n)
{
}
void GarbageVisitor::visit(Shader &n)
{
}
void GarbageVisitor::visit(ImageShader &n)
{
}
void GarbageVisitor::visit(ImageProcessingShader &n)
{
}
void GarbageVisitor::visit(LineStrip &n)
{
}
void GarbageVisitor::visit(LineSquare &)
{
}
void GarbageVisitor::visit(Mesh &n)
{
}
void GarbageVisitor::visit(Frame &n)
{
}
void GarbageVisitor::visit (Source& s)
{
}
void GarbageVisitor::visit (MediaSource& s)
{
}

View File

@@ -24,23 +24,6 @@ public:
void visit(Switch& n) override;
void visit(Primitive& n) override;
void visit(Surface& n) override;
void visit(ImageSurface& n) override;
void visit(MediaSurface& n) override;
void visit(FrameBufferSurface& n) override;
void visit(LineStrip& n) override;
void visit(LineSquare&) override;
void visit(Mesh& n) override;
void visit(Frame& n) override;
void visit(MediaPlayer& n) override;
void visit(Shader& n) override;
void visit(ImageShader& n) override;
void visit(ImageProcessingShader& n) override;
void visit (Source& s) override;
void visit (MediaSource& s) override;
};
#endif // GARBAGEVISITOR_H

View File

@@ -15,6 +15,7 @@
#include "Mixer.h"
#include "defines.h"
#include "Source.h"
#include "Settings.h"
#include "PickingVisitor.h"
#include "DrawVisitor.h"
@@ -196,7 +197,7 @@ void GeometryView::draw()
std::vector<Node *> surfaces;
std::vector<Node *> overlays;
for (auto source_iter = Mixer::manager().session()->begin();
source_iter != Mixer::manager().session()->end(); source_iter++) {
source_iter != Mixer::manager().session()->end(); ++source_iter) {
// if it is in the current workspace
if ((*source_iter)->workspace() == Settings::application.current_workspace) {
// will draw its surface
@@ -257,7 +258,7 @@ void GeometryView::draw()
static std::vector< std::pair<int, int> > icons_ws = { {10,16}, {11,16}, {12,16} };
static std::vector< std::string > labels_ws = { "Background", "Workspace", "Foreground" };
if ( ImGuiToolkit::ComboIcon (icons_ws, labels_ws, &Settings::application.current_workspace) ){
View::need_deep_update_++;
++View::need_deep_update_;
}
ImGui::PopStyleColor(8); // 14 colors
@@ -319,7 +320,7 @@ void GeometryView::draw()
// batch manipulation of sources in Geometry view
if (ImGui::Selectable( ICON_FA_EXPAND " Fit all" )){
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
(*sit)->group(mode_)->scale_ = glm::vec3(output_surface_->scale_.x/ (*sit)->frame()->aspectRatio(), 1.f, 1.f);
(*sit)->group(mode_)->rotation_.z = 0;
(*sit)->group(mode_)->translation_ = glm::vec3(0.f);
@@ -329,7 +330,7 @@ void GeometryView::draw()
}
if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset all" )){
// apply to every sources in selection
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
(*sit)->group(mode_)->scale_ = glm::vec3(1.f);
(*sit)->group(mode_)->rotation_.z = 0;
(*sit)->group(mode_)->crop_ = glm::vec3(1.f);
@@ -351,7 +352,7 @@ void GeometryView::draw()
}
if (ImGui::Selectable( ICON_FA_COMPASS " Align" )){
// apply to every sources in selection
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
(*sit)->group(mode_)->rotation_.z = overlay_selection_->rotation_.z;
(*sit)->touch();
}
@@ -413,7 +414,7 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
// find if the current source was picked
auto itp = pv.rbegin();
for (; itp != pv.rend(); itp++){
for (; itp != pv.rend(); ++itp){
// test if source contains this node
Source::hasNode is_in_source((*itp).first );
if ( is_in_source( current ) ){
@@ -431,41 +432,45 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
openContextMenu(MENU_SOURCE);
}
// pick on the lock icon; unlock source
else if ( pick.first == current->lock_ ) {
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->lock_ ) {
lock(current, false);
pick = { nullptr, glm::vec2(0.f) };
}
// pick on the open lock icon; lock source and cancel pick
else if ( pick.first == current->unlock_ ) {
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->unlock_ ) {
lock(current, true);
pick = { nullptr, glm::vec2(0.f) };
}
// pick a locked source without CTRL key; cancel pick
else if ( current->locked() && !UserInterface::manager().ctrlModifier() ) {
// pick a locked source ; cancel pick
else if ( current->locked() ) {
pick = { nullptr, glm::vec2(0.f) };
}
}
// the clicked source changed (not the current source)
if (current == nullptr) {
// default to failed pick
pick = { nullptr, glm::vec2(0.f) };
if (UserInterface::manager().ctrlModifier()) {
// loop over all nodes picked to detect clic on locks
for (auto itp = pv.rbegin(); itp != pv.rend(); itp++){
// get if a source was picked
Source *s = Mixer::manager().findSource((*itp).first);
// lock icon of a source (not current) is picked : unlock
if ( s!=nullptr && (*itp).first == s->lock_) {
lock(s, false);
pick = { s->locker_, (*itp).second };
break;
// default to failed pick
pick = { nullptr, glm::vec2(0.f) };
// loop over all nodes picked to detect clic on locks
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
// get if a source was picked
Source *s = Mixer::manager().findSource((*itp).first);
// lock icon of a source (not current) is picked : unlock
if ( s!=nullptr && (*itp).first == s->lock_) {
lock(s, false);
pick = { s->locker_, (*itp).second };
break;
}
}
}
// no lock icon picked, find what else was picked
if ( pick.first == nullptr) {
// loop over all nodes picked
for (auto itp = pv.rbegin(); itp != pv.rend(); itp++){
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
// get if a source was picked
Source *s = Mixer::manager().findSource((*itp).first);
// accept picked sources in current workspaces
@@ -511,13 +516,13 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
bool GeometryView::canSelect(Source *s) {
return ( View::canSelect(s) && s->active() && s->workspace() == Settings::application.current_workspace);
return ( View::canSelect(s) && s->ready() && s->active() && s->workspace() == Settings::application.current_workspace);
}
void GeometryView::applySelectionTransform(glm::mat4 M)
{
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
// recompute all from matrix transform
glm::mat4 transform = M * (*sit)->stored_status_->transform_;
glm::vec3 tra, rot, sca;
@@ -1009,7 +1014,7 @@ void GeometryView::terminate()
// restore of all handles overlays
glm::vec2 c(0.f, 0.f);
for (auto sit = Mixer::manager().session()->begin();
sit != Mixer::manager().session()->end(); sit++){
sit != Mixer::manager().session()->end(); ++sit){
(*sit)->handles_[mode_][Handles::RESIZE]->overlayActiveCorner(c);
(*sit)->handles_[mode_][Handles::RESIZE_H]->overlayActiveCorner(c);
@@ -1037,7 +1042,7 @@ void GeometryView::arrow (glm::vec2 movement)
bool first = true;
glm::vec3 delta_translation(0.f);
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) {
// individual move with SHIFT
if ( !Source::isCurrent(*it) && UserInterface::manager().shiftModifier() )

View File

@@ -7,6 +7,9 @@ class GeometryView : public View
{
public:
GeometryView();
// non assignable class
GeometryView(GeometryView const&) = delete;
GeometryView& operator=(GeometryView const&) = delete;
void draw () override;
void update (float dt) override;

View File

@@ -1,4 +1,3 @@
// Freely inspired from https://github.com/alter-rokuz/glm-aabb.git
#include "GlmToolkit.h"
@@ -10,14 +9,6 @@
#include <chrono>
#include <ctime>
uint64_t GlmToolkit::uniqueId()
{
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
// 64-bit int 18446744073709551615UL
return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000000000000UL;
}
glm::mat4 GlmToolkit::transform(glm::vec3 translation, glm::vec3 rotation, glm::vec3 scale)
{
glm::mat4 View = glm::translate(glm::identity<glm::mat4>(), translation);
@@ -59,6 +50,8 @@ void GlmToolkit::inverse_transform(glm::mat4 M, glm::vec3 &translation, glm::vec
// return angle;
//}
// Freely inspired from https://github.com/alter-rokuz/glm-aabb.git
GlmToolkit::AxisAlignedBoundingBox::AxisAlignedBoundingBox() :
mMin(glm::vec3(1.f)), mMax(glm::vec3(-1.f))
{
@@ -79,7 +72,7 @@ void GlmToolkit::AxisAlignedBoundingBox::extend(const glm::vec3& point)
void GlmToolkit::AxisAlignedBoundingBox::extend(std::vector<glm::vec3> points)
{
for (auto p = points.begin(); p != points.end(); p++)
for (auto p = points.begin(); p != points.end(); ++p)
extend(*p);
}

View File

@@ -1,16 +1,12 @@
#ifndef GLMTOOLKIT_H
#define GLMTOOLKIT_H
#include <vector>
#include <glm/glm.hpp>
namespace GlmToolkit
{
// get integer with unique id
uint64_t uniqueId();
// get Matrix for these transformation components
glm::mat4 transform(glm::vec3 translation, glm::vec3 rotation, glm::vec3 scale);
void inverse_transform(glm::mat4 M, glm::vec3 &translation, glm::vec3 &rotation, glm::vec3 &scale);

View File

@@ -1,6 +1,8 @@
#include <map>
#include <algorithm>
#include <string>
#include <cctype>
#ifdef _WIN32
# include <windows.h>
@@ -20,7 +22,6 @@
#include <glad/glad.h>
#include "Resource.h"
#include "FileDialog.h"
#include "ImGuiToolkit.h"
#include "GstToolkit.h"
#include "SystemToolkit.h"
@@ -30,14 +31,12 @@ unsigned int textureicons = 0;
std::map <ImGuiToolkit::font_style, ImFont*>fontmap;
void ImGuiToolkit::ButtonOpenUrl( const char* url, const ImVec2& size_arg )
void ImGuiToolkit::ButtonOpenUrl( const char* label, const char* url, const ImVec2& size_arg )
{
char label[512];
char _label[512];
sprintf( _label, "%s %s", ICON_FA_EXTERNAL_LINK_ALT, label );
std::string str = SystemToolkit::transliterate( url );
sprintf( label, "%s %s", ICON_FA_EXTERNAL_LINK_ALT, str.c_str() );
if ( ImGui::Button(label, size_arg) )
if ( ImGui::Button(_label, size_arg) )
SystemToolkit::open(url);
}
@@ -58,8 +57,10 @@ bool ImGuiToolkit::ButtonToggle( const char* label, bool* toggle )
}
void ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* help)
bool ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* help)
{
bool ret = false;
// utility style
ImVec4* colors = ImGui::GetStyle().Colors;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
@@ -76,8 +77,10 @@ void ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* hel
// toggle action : operate on the whole area
ImGui::InvisibleButton(label, ImVec2(frame_width, frame_height));
if (ImGui::IsItemClicked())
if (ImGui::IsItemClicked()) {
*toggle = !*toggle;
ret = true;
}
float t = *toggle ? 1.0f : 0.0f;
// animation
@@ -113,6 +116,7 @@ void ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* hel
draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), col_bg, height * 0.5f);
draw_list->AddCircleFilled(ImVec2(p.x + radius + t * (width - radius * 2.0f), p.y + radius), radius - 1.5f, IM_COL32(255, 255, 255, 250));
return ret;
}
@@ -201,6 +205,36 @@ bool ImGuiToolkit::IconButton(int i, int j, const char *tooltip)
return ret;
}
bool ImGuiToolkit::IconButton(const char* icon, const char *tooltip)
{
bool ret = false;
ImGui::PushID( icon );
float frame_height = ImGui::GetFrameHeight();
float frame_width = frame_height;
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
// toggle action : operate on the whole area
ImGui::InvisibleButton("##iconbutton", ImVec2(frame_width, frame_height));
if (ImGui::IsItemClicked())
ret = true;
ImGui::SetCursorScreenPos(draw_pos);
ImGui::Text(icon);
if (tooltip != nullptr && ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", tooltip);
ImGui::EndTooltip();
}
ImGui::PopID();
return ret;
}
bool ImGuiToolkit::IconToggle(int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[])
{
bool ret = false;
@@ -311,33 +345,34 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
const ImGuiIO& io = ImGui::GetIO();
ImGui::Begin("Icons", p_open);
if ( ImGui::Begin("Icons", p_open) ) {
ImVec2 pos = ImGui::GetCursorScreenPos();
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
if (ImGui::IsItemHovered())
{
float my_tex_w = 640.0;
float my_tex_h = 640.0;
float zoom = 4.0f;
float region_sz = 32.0f; // 64 x 64 icons
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz;
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz;
ImGui::BeginTooltip();
int i = (int) ( (region_x + region_sz * 0.5f) / region_sz);
int j = (int) ( (region_y + region_sz * 0.5f)/ region_sz);
ImGuiToolkit::Icon(i, j);
ImGui::SameLine();
ImGui::Text(" Icon (%d, %d)", i, j);
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f));
ImGui::EndTooltip();
ImVec2 pos = ImGui::GetCursorScreenPos();
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
if (ImGui::IsItemHovered())
{
float my_tex_w = 640.0;
float my_tex_h = 640.0;
float zoom = 4.0f;
float region_sz = 32.0f; // 64 x 64 icons
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz;
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz;
ImGui::BeginTooltip();
int i = (int) ( (region_x + region_sz * 0.5f) / region_sz);
int j = (int) ( (region_y + region_sz * 0.5f)/ region_sz);
ImGuiToolkit::Icon(i, j);
ImGui::SameLine();
ImGui::Text(" Icon (%d, %d)", i, j);
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f));
ImGui::EndTooltip();
}
ImGui::End();
}
ImGui::End();
}
void ImGuiToolkit::ToolTip(const char* desc, const char* shortcut)
@@ -1113,4 +1148,83 @@ void ImGuiToolkit::SetAccentColor(accent_color color)
}
void word_wrap(std::string *str, unsigned per_line)
{
unsigned line_begin = 0;
while (line_begin < str->size())
{
const unsigned ideal_end = line_begin + per_line ;
unsigned line_end = ideal_end < str->size() ? ideal_end : str->size()-1;
if (line_end == str->size() - 1)
++line_end;
else if (std::isspace(str->at(line_end)))
{
str->replace(line_end, 1, 1, '\n' );
++line_end;
}
else // backtrack
{
unsigned end = line_end;
while ( end > line_begin && !std::isspace(str->at(end)))
--end;
if (end != line_begin)
{
line_end = end;
str->replace(line_end++, 1, 1, '\n' );
}
else {
str->insert(line_end++, 1, '\n' );
}
}
line_begin = line_end;
}
}
struct InputTextCallback_UserData
{
std::string* Str;
int WordWrap;
};
static int InputTextCallback(ImGuiInputTextCallbackData* data)
{
InputTextCallback_UserData* user_data = static_cast<InputTextCallback_UserData*>(data->UserData);
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
{
// if (user_data->WordWrap > 1)
// word_wrap(user_data->Str, user_data->WordWrap );
// Resize string callback
std::string* str = user_data->Str;
IM_ASSERT(data->Buf == str->c_str());
str->resize(data->BufTextLen);
data->Buf = (char*)str->c_str();
}
return 0;
}
bool ImGuiToolkit::InputText(const char* label, std::string* str)
{
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CharsNoBlank;
InputTextCallback_UserData cb_user_data;
cb_user_data.Str = str;
return ImGui::InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
}
bool ImGuiToolkit::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, int linesize)
{
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
InputTextCallback_UserData cb_user_data;
cb_user_data.Str = str;
cb_user_data.WordWrap = linesize;
return ImGui::InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
}

View File

@@ -13,6 +13,7 @@ namespace ImGuiToolkit
// Icons from resource icon.dds
void Icon (int i, int j, bool enabled = true);
bool IconButton (int i, int j, const char *tooltips = nullptr);
bool IconButton (const char* icon = ICON_FA_EXCLAMATION_CIRCLE, const char *tooltips = nullptr);
bool IconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[] = nullptr);
void ShowIconsWindow(bool* p_open);
@@ -24,8 +25,8 @@ namespace ImGuiToolkit
// utility buttons
bool ButtonToggle (const char* label, bool* toggle);
void ButtonSwitch (const char* label, bool* toggle , const char *help = nullptr);
void ButtonOpenUrl (const char* url, const ImVec2& size_arg = ImVec2(0,0));
bool ButtonSwitch (const char* label, bool* toggle , const char *help = nullptr);
void ButtonOpenUrl (const char* label, const char* url, const ImVec2& size_arg = ImVec2(0,0));
void ToolTip (const char* desc, const char* shortcut = nullptr);
void HelpMarker (const char* desc, const char* icon = ICON_FA_QUESTION_CIRCLE, const char* shortcut = nullptr);
@@ -62,6 +63,8 @@ namespace ImGuiToolkit
void SetAccentColor (accent_color color);
struct ImVec4 HighlightColor (bool active = true);
bool InputText(const char* label, std::string* str);
bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), int linesize = 0);
}
#endif // __IMGUI_TOOLKIT_H_

View File

@@ -8,6 +8,7 @@
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/constants.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
@@ -24,6 +25,7 @@
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "MultiFileSource.h"
#include "SessionCreator.h"
#include "SessionVisitor.h"
#include "Settings.h"
@@ -32,6 +34,7 @@
#include "imgui.h"
#include "ImGuiToolkit.h"
#include "BaseToolkit.h"
#include "UserInterfaceManager.h"
#include "SystemToolkit.h"
@@ -395,7 +398,6 @@ void ImGuiVisitor::visit(ImageProcessingShader &n)
ImGui::Spacing();
}
void ImGuiVisitor::visit (Source& s)
{
ImGui::PushID(std::to_string(s.id()).c_str());
@@ -539,10 +541,6 @@ void ImGuiVisitor::visit (Source& s)
s.processingShader()->accept(*this);
}
// geometry direct control for DEBUG
// s.groupNode(View::GEOMETRY)->accept(*this);
// s.groupNode((View::Mode) Settings::application.current_view)->accept(*this);
ImGui::PopID();
}
@@ -557,7 +555,12 @@ void ImGuiVisitor::visit (MediaSource& s)
if ( ImGui::Button(IMGUI_TITLE_MEDIAPLAYER, ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
UserInterface::manager().showMediaPlayer( s.mediaplayer());
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
std::string path = SystemToolkit::path_filename(s.path());
std::string label = BaseToolkit::trunc_string(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
ImGui::Text("Folder");
}
@@ -588,7 +591,10 @@ void ImGuiVisitor::visit (SessionFileSource& s)
ImGui::SameLine();
ImGui::Text("File");
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
std::string path = SystemToolkit::path_filename(s.path());
std::string label = BaseToolkit::trunc_string(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
ImGui::Text("Folder");
@@ -703,6 +709,60 @@ void ImGuiVisitor::visit (NetworkSource& s)
s.setConnection(s.connection());
}
}
void ImGuiVisitor::visit (MultiFileSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Images sequence");
static int64_t id = s.id();
// information text
std::ostringstream msg;
msg << "Sequence of " << s.sequence().max - s.sequence().min + 1 << " ";
msg << s.sequence().codec << " images";
ImGui::Text("%s", msg.str().c_str());
// change range
static int _begin = -1;
if (_begin < 0 || id != s.id())
_begin = s.begin();
static int _end = -1;
if (_end < 0 || id != s.id())
_end = s.end();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::DragIntRange2("Range", &_begin, &_end, 1, s.sequence().min, s.sequence().max);
if (ImGui::IsItemDeactivatedAfterEdit()){
s.setRange( _begin, _end );
std::ostringstream oss;
oss << s.name() << ": Range " << _begin << "-" << _end;
Action::manager().store(oss.str());
_begin = _end = -1;
}
// change framerate
static int _fps = -1;
if (_fps < 0 || id != s.id())
_fps = s.framerate();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps");
if (ImGui::IsItemDeactivatedAfterEdit()){
s.setFramerate(_fps);
std::ostringstream oss;
oss << s.name() << ": Framerate " << _fps << " fps";
Action::manager().store(oss.str());
_fps = -1;
}
// offer to open file browser at location
std::string path = SystemToolkit::path_filename(s.sequence().location);
std::string label = BaseToolkit::trunc_string(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
ImGui::Text("Folder");
id = s.id();
}

View File

@@ -30,6 +30,7 @@ public:
void visit (PatternSource& s) override;
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
void visit (MultiFileSource& s) override;
};
#endif // IMGUIVISITOR_H

View File

@@ -58,7 +58,7 @@ void ImageProcessingShader::reset()
}
void ImageProcessingShader::copy(const ImageProcessingShader &S)
void ImageProcessingShader::copy(ImageProcessingShader const& S)
{
brightness = S.brightness;
contrast = S.contrast;
@@ -75,11 +75,6 @@ void ImageProcessingShader::copy(const ImageProcessingShader &S)
chromadelta = S.chromadelta;
}
void ImageProcessingShader::operator = (const ImageProcessingShader &S )
{
copy(S);
}
void ImageProcessingShader::accept(Visitor& v)
{

View File

@@ -16,8 +16,7 @@ public:
void reset() override;
void accept(Visitor& v) override;
void operator = (const ImageProcessingShader &S);
void copy(const ImageProcessingShader &S);
void copy(ImageProcessingShader const& S);
// color effects
float brightness; // [-1 1]

View File

@@ -58,10 +58,8 @@ void ImageShader::reset()
stipple = 0.f;
}
void ImageShader::operator = (const ImageShader &S)
void ImageShader::copy(ImageShader const& S)
{
Shader::operator =(S);
mask_texture = S.mask_texture;
stipple = S.stipple;
}
@@ -133,10 +131,8 @@ void MaskShader::reset()
effect = 0;
}
void MaskShader::operator = (const MaskShader &S)
void MaskShader::copy(MaskShader const& S)
{
Shader::operator =(S);
mode = S.mode;
shape = S.shape;
blur = S.blur;

View File

@@ -19,7 +19,7 @@ public:
void use() override;
void reset() override;
void accept(Visitor& v) override;
void operator = (const ImageShader &S);
void copy(ImageShader const& S);
uint mask_texture;
@@ -45,7 +45,7 @@ public:
void use() override;
void reset() override;
void accept(Visitor& v) override;
void operator = (const MaskShader &S);
void copy(MaskShader const& S);
enum Modes {
NONE = 0,

166
Interpolator.cpp Normal file
View File

@@ -0,0 +1,166 @@
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "defines.h"
#include "Log.h"
#include "Source.h"
#include "ImageProcessingShader.h"
#include "UpdateCallback.h"
#include "Interpolator.h"
SourceInterpolator::SourceInterpolator(Source *subject, const SourceCore &target) :
subject_(subject), from_(static_cast<SourceCore>(*subject)), to_(target), current_cursor_(0.f)
{
}
void SourceInterpolator::interpolateGroup(View::Mode m)
{
current_state_.group(m)->translation_ =
(1.f - current_cursor_) * from_.group(m)->translation_
+ current_cursor_ * to_.group(m)->translation_;
current_state_.group(m)->scale_ =
(1.f - current_cursor_) * from_.group(m)->scale_
+ current_cursor_ * to_.group(m)->scale_;
current_state_.group(m)->rotation_ =
(1.f - current_cursor_) * from_.group(m)->rotation_
+ current_cursor_ * to_.group(m)->rotation_;
current_state_.group(m)->crop_ =
(1.f - current_cursor_) * from_.group(m)->crop_
+ current_cursor_ * to_.group(m)->crop_;
CopyCallback *anim = new CopyCallback( current_state_.group(m) );
subject_->group(m)->update_callbacks_.clear();
subject_->group(m)->update_callbacks_.push_back(anim);
}
void SourceInterpolator::interpolateImageProcessing()
{
current_state_.processingShader()->brightness =
(1.f - current_cursor_) * from_.processingShader()->brightness
+ current_cursor_ * to_.processingShader()->brightness;
current_state_.processingShader()->contrast =
(1.f - current_cursor_) * from_.processingShader()->contrast
+ current_cursor_ * to_.processingShader()->contrast;
current_state_.processingShader()->saturation =
(1.f - current_cursor_) * from_.processingShader()->saturation
+ current_cursor_ * to_.processingShader()->saturation;
current_state_.processingShader()->hueshift =
(1.f - current_cursor_) * from_.processingShader()->hueshift
+ current_cursor_ * to_.processingShader()->hueshift;
current_state_.processingShader()->threshold =
(1.f - current_cursor_) * from_.processingShader()->threshold
+ current_cursor_ * to_.processingShader()->threshold;
current_state_.processingShader()->lumakey =
(1.f - current_cursor_) * from_.processingShader()->lumakey
+ current_cursor_ * to_.processingShader()->lumakey;
current_state_.processingShader()->nbColors =
(1.f - current_cursor_) * from_.processingShader()->nbColors
+ current_cursor_ * to_.processingShader()->nbColors;
current_state_.processingShader()->gamma =
(1.f - current_cursor_) * from_.processingShader()->gamma
+ current_cursor_ * to_.processingShader()->gamma;
current_state_.processingShader()->levels =
(1.f - current_cursor_) * from_.processingShader()->levels
+ current_cursor_ * to_.processingShader()->levels;
current_state_.processingShader()->chromakey =
(1.f - current_cursor_) * from_.processingShader()->chromakey
+ current_cursor_ * to_.processingShader()->chromakey;
current_state_.processingShader()->chromadelta =
(1.f - current_cursor_) * from_.processingShader()->chromadelta
+ current_cursor_ * to_.processingShader()->chromadelta;
subject_->processingShader()->copy( *current_state_.processingShader() );
// not interpolated : invert , filterid
}
float SourceInterpolator::current() const
{
return current_cursor_;
}
void SourceInterpolator::apply(float percent)
{
percent = CLAMP( percent, 0.f, 1.f);
if ( subject_ && ABS_DIFF(current_cursor_, percent) > EPSILON)
{
current_cursor_ = percent;
if (current_cursor_ < EPSILON) {
current_cursor_ = 0.f;
current_state_ = from_;
subject_->copy(current_state_);
}
else if (current_cursor_ > 1.f - EPSILON) {
current_cursor_ = 1.f;
current_state_ = to_;
subject_->copy(current_state_);
}
else {
interpolateGroup(View::MIXING);
interpolateGroup(View::GEOMETRY);
interpolateGroup(View::LAYER);
interpolateGroup(View::TEXTURE);
interpolateImageProcessing();
// Log::Info("SourceInterpolator::update %f", cursor);
}
subject_->touch();
}
}
Interpolator::Interpolator()
{
}
Interpolator::~Interpolator()
{
for (auto i = interpolators_.begin(); i != interpolators_.end(); ) {
delete *i;
i = interpolators_.erase(i);
}
}
void Interpolator::add (Source *s, const SourceCore &target)
{
SourceInterpolator *i = new SourceInterpolator(s, target);
interpolators_.push_back(i);
}
float Interpolator::current() const
{
float ret = 0.f;
if (interpolators_.size() > 0)
ret = interpolators_.front()->current();
return ret;
}
void Interpolator::apply(float percent)
{
for (auto i = interpolators_.begin(); i != interpolators_.end(); ++i)
(*i)->apply( percent );
}

43
Interpolator.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef INTERPOLATOR_H
#define INTERPOLATOR_H
#include "Source.h"
#include "SourceList.h"
class SourceInterpolator
{
public:
SourceInterpolator(Source *subject, const SourceCore &target);
void apply (float percent);
float current() const;
protected:
Source *subject_;
SourceCore from_;
SourceCore to_;
SourceCore current_state_;
float current_cursor_;
void interpolateGroup (View::Mode m);
void interpolateImageProcessing ();
};
class Interpolator
{
public:
Interpolator();
~Interpolator();
void add (Source *s, const SourceCore &target );
void apply (float percent);
float current() const;
protected:
std::list<SourceInterpolator *> interpolators_;
};
#endif // INTERPOLATOR_H

View File

@@ -14,6 +14,7 @@
#include "Mixer.h"
#include "defines.h"
#include "Source.h"
#include "Settings.h"
#include "Decorations.h"
#include "UserInterfaceManager.h"
@@ -123,7 +124,7 @@ void LayerView::draw()
(*it)->setDepth(depth);
}
Action::manager().store(std::string("Selection: Layer Distribute"));
View::need_deep_update_++;
++View::need_deep_update_;
}
if (ImGui::Selectable( ICON_FA_RULER_HORIZONTAL " Compress" )){
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
@@ -134,7 +135,7 @@ void LayerView::draw()
(*it)->setDepth(depth);
}
Action::manager().store(std::string("Selection: Layer Compress"));
View::need_deep_update_++;
++View::need_deep_update_;
}
if (ImGui::Selectable( ICON_FA_EXCHANGE_ALT " Reverse order" )){
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
@@ -144,7 +145,7 @@ void LayerView::draw()
(*it)->setDepth((*rit)->depth());
}
Action::manager().store(std::string("Selection: Layer Reverse order"));
View::need_deep_update_++;
++View::need_deep_update_;
}
ImGui::PopStyleColor(2);
@@ -226,12 +227,13 @@ std::pair<Node *, glm::vec2> LayerView::pick(glm::vec2 P)
Source *s = Mixer::manager().findSource(pick.first);
if (s != nullptr) {
// pick on the lock icon; unlock source
if ( pick.first == s->lock_) {
if ( UserInterface::manager().ctrlModifier() && pick.first == s->lock_) {
lock(s, false);
pick = { s->locker_, pick.second };
// pick = { s->locker_, pick.second };
pick = { nullptr, glm::vec2(0.f) };
}
// pick on the open lock icon; lock source and cancel pick
else if ( pick.first == s->unlock_ ) {
else if ( UserInterface::manager().ctrlModifier() && pick.first == s->unlock_ ) {
lock(s, true);
pick = { nullptr, glm::vec2(0.f) };
}

View File

@@ -7,6 +7,9 @@ class LayerView : public View
{
public:
LayerView();
// non assignable class
LayerView(LayerView const&) = delete;
LayerView& operator=(LayerView const&) = delete;
void draw () override;
void update (float dt) override;

114
Log.cpp
View File

@@ -57,7 +57,7 @@ struct AppLog
ImGui::SetNextWindowPos(ImVec2(430, 660), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(1150, 220), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(600, 180), ImVec2(FLT_MAX, FLT_MAX));
if (!ImGui::Begin(title, p_open))
if ( !ImGui::Begin(title, p_open))
{
ImGui::End();
return;
@@ -75,69 +75,71 @@ struct AppLog
Filter.Draw("Filter", -60.0f);
ImGui::Separator();
ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar);
if (clear)
Clear();
if (copy)
ImGui::LogToClipboard();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
mtx.lock();
const char* buf = Buf.begin();
const char* buf_end = Buf.end();
if (Filter.IsActive())
if ( ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar) )
{
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have a random access on the result on our filter.
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
// especially if the filtering function is not trivial (e.g. reg-exp).
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
if (clear)
Clear();
if (copy)
ImGui::LogToClipboard();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
mtx.lock();
const char* buf = Buf.begin();
const char* buf_end = Buf.end();
if (Filter.IsActive())
{
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
if (Filter.PassFilter(line_start, line_end))
ImGui::TextUnformatted(line_start, line_end);
}
}
else
{
// The simplest and easy way to display the entire buffer:
// ImGui::TextUnformatted(buf_begin, buf_end);
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
// both of which we can handle since we an array pointing to the beginning of each line of text.
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
ImGuiListClipper clipper;
clipper.Begin(LineOffsets.Size);
while (clipper.Step())
{
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have a random access on the result on our filter.
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
// especially if the filtering function is not trivial (e.g. reg-exp).
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
{
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
ImGui::TextUnformatted(line_start, line_end);
if (Filter.PassFilter(line_start, line_end))
ImGui::TextUnformatted(line_start, line_end);
}
}
clipper.End();
else
{
// The simplest and easy way to display the entire buffer:
// ImGui::TextUnformatted(buf_begin, buf_end);
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
// both of which we can handle since we an array pointing to the beginning of each line of text.
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
ImGuiListClipper clipper;
clipper.Begin(LineOffsets.Size);
while (clipper.Step())
{
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
{
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
ImGui::TextUnformatted(line_start, line_end);
}
}
clipper.End();
}
mtx.unlock();
ImGui::PopStyleVar();
ImGui::PopFont();
// Auto scroll
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
}
mtx.unlock();
ImGui::PopStyleVar();
ImGui::PopFont();
// Auto scroll
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
ImGui::End();
}
};

View File

@@ -12,7 +12,7 @@ using namespace std;
#include "Resource.h"
#include "Visitor.h"
#include "SystemToolkit.h"
#include "GlmToolkit.h"
#include "BaseToolkit.h"
#include "GstToolkit.h"
#include "RenderingManager.h"
@@ -27,20 +27,20 @@ std::list<MediaPlayer*> MediaPlayer::registered_;
MediaPlayer::MediaPlayer()
{
// create unique id
id_ = GlmToolkit::uniqueId();
id_ = BaseToolkit::uniqueId();
uri_ = "undefined";
pipeline_ = nullptr;
opened_ = false;
enabled_ = true;
desired_state_ = GST_STATE_PAUSED;
ready_ = false;
failed_ = false;
seeking_ = false;
enabled_ = true;
force_software_decoding_ = false;
hardware_decoder_ = "";
rate_ = 1.0;
position_ = GST_CLOCK_TIME_NONE;
desired_state_ = GST_STATE_PAUSED;
loop_ = LoopMode::LOOP_REWIND;
// start index in frame_ stack
@@ -65,6 +65,9 @@ MediaPlayer::~MediaPlayer()
if (textureindex_)
glDeleteTextures(1, &textureindex_);
// cleanup picture buffer
if (pbo_[0])
glDeleteBuffers(2, pbo_);
}
void MediaPlayer::accept(Visitor& v) {
@@ -81,7 +84,7 @@ guint MediaPlayer::texture() const
#define LIMIT_DISCOVERER
static MediaInfo UriDiscoverer_(std::string uri)
MediaInfo MediaPlayer::UriDiscoverer(const std::string &uri)
{
#ifdef MEDIA_PLAYER_DEBUG
Log::Info("Checking file '%s'", uri.c_str());
@@ -217,23 +220,27 @@ static MediaInfo UriDiscoverer_(std::string uri)
return video_stream_info;
}
void MediaPlayer::open(string path)
void MediaPlayer::open (const std::string & filename, const string &uri)
{
// set path
filename_ = SystemToolkit::transliterate( path );
filename_ = BaseToolkit::transliterate( filename );
// set uri to open
uri_ = GstToolkit::filename_to_uri(path);
if (uri.empty())
uri_ = GstToolkit::filename_to_uri( filename );
else
uri_ = uri;
// reset
ready_ = false;
// close before re-openning
if (isOpen())
close();
// start URI discovering thread:
discoverer_ = std::async( UriDiscoverer_, uri_);
discoverer_ = std::async( MediaPlayer::UriDiscoverer, uri_);
// wait for discoverer to finish in the future (test in update)
// // debug without thread
// media_ = UriDiscoverer_(uri_);
// media_ = MediaPlayer::UriDiscoverer(uri_);
// if (media_.valid) {
// timeline_.setEnd( media_.end );
// timeline_.setStep( media_.dt );
@@ -329,47 +336,48 @@ void MediaPlayer::execute_open()
// setup appsink
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline_), "sink");
if (sink) {
// instruct the sink to send samples synched in time
gst_base_sink_set_sync (GST_BASE_SINK(sink), true);
// instruct sink to use the required caps
gst_app_sink_set_caps (GST_APP_SINK(sink), caps);
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 5);
gst_app_sink_set_drop (GST_APP_SINK(sink), true);
#ifdef USE_GST_APPSINK_CALLBACKS
// set the callbacks
GstAppSinkCallbacks callbacks;
callbacks.new_preroll = callback_new_preroll;
if (media_.isimage) {
callbacks.eos = NULL;
callbacks.new_sample = NULL;
}
else {
callbacks.eos = callback_end_of_stream;
callbacks.new_sample = callback_new_sample;
}
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
#else
// connect signals callbacks
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
#endif
// done with ref to sink
gst_object_unref (sink);
}
else {
if (!sink) {
Log::Warning("MediaPlayer %s Could not configure sink", std::to_string(id_).c_str());
failed_ = true;
return;
}
// instruct the sink to send samples synched in time
gst_base_sink_set_sync (GST_BASE_SINK(sink), true);
// instruct sink to use the required caps
gst_app_sink_set_caps (GST_APP_SINK(sink), caps);
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 5);
gst_app_sink_set_drop (GST_APP_SINK(sink), true);
#ifdef USE_GST_APPSINK_CALLBACKS
// set the callbacks
GstAppSinkCallbacks callbacks;
callbacks.new_preroll = callback_new_preroll;
if (media_.isimage) {
callbacks.eos = NULL;
callbacks.new_sample = NULL;
}
else {
callbacks.eos = callback_end_of_stream;
callbacks.new_sample = callback_new_sample;
}
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
#else
// connect signals callbacks
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
if (!media_.isimage) {
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
}
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
#endif
// done with ref to sink
gst_object_unref (sink);
gst_caps_unref (caps);
#ifdef USE_GST_OPENGL_SYNC_HANDLER
@@ -399,7 +407,7 @@ void MediaPlayer::execute_open()
Log::Info("MediaPlayer %s Timeline [%ld %ld] %ld frames, %d gaps", std::to_string(id_).c_str(),
timeline_.begin(), timeline_.end(), timeline_.numFrames(), timeline_.numGaps());
ready_ = true;
opened_ = true;
// register media player
MediaPlayer::registered_.push_back(this);
@@ -407,7 +415,7 @@ void MediaPlayer::execute_open()
bool MediaPlayer::isOpen() const
{
return ready_;
return opened_;
}
bool MediaPlayer::failed() const
@@ -417,16 +425,15 @@ bool MediaPlayer::failed() const
void MediaPlayer::Frame::unmap()
{
if ( full ) {
if ( full )
gst_video_frame_unmap(&vframe);
full = false;
}
full = false;
}
void MediaPlayer::close()
{
// not openned?
if (!ready_) {
if (!opened_) {
// wait for loading to finish
if (discoverer_.valid())
discoverer_.wait();
@@ -435,7 +442,7 @@ void MediaPlayer::close()
}
// un-ready the media player
ready_ = false;
opened_ = false;
// clean up GST
if (pipeline_ != nullptr) {
@@ -463,12 +470,6 @@ void MediaPlayer::close()
write_index_ = 0;
last_index_ = 0;
// cleanup picture buffer
if (pbo_[0])
glDeleteBuffers(2, pbo_);
pbo_size_ = 0;
pbo_index_ = 0;
pbo_next_index_ = 0;
#ifdef MEDIA_PLAYER_DEBUG
Log::Info("MediaPlayer %s closed", std::to_string(id_).c_str());
@@ -507,7 +508,7 @@ GstClockTime MediaPlayer::position()
void MediaPlayer::enable(bool on)
{
if ( !ready_ )
if ( !opened_ || pipeline_ == nullptr)
return;
if ( enabled_ != on ) {
@@ -772,6 +773,7 @@ void MediaPlayer::init_texture(guint index)
// for possible hadrware decoding plugins used. Empty string means none.
hardware_decoder_ = GstToolkit::used_gpu_decoding_plugins(pipeline_);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
@@ -819,6 +821,7 @@ void MediaPlayer::fill_texture(guint index)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, media_.width, media_.height,
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
}
@@ -829,7 +832,7 @@ void MediaPlayer::update()
return;
// not ready yet
if (!ready_) {
if (!opened_) {
if (discoverer_.valid()) {
// try to get info from discoverer
if (discoverer_.wait_for( std::chrono::milliseconds(4) ) == std::future_status::ready )
@@ -1153,7 +1156,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
void MediaPlayer::callback_end_of_stream (GstAppSink *, gpointer p)
{
MediaPlayer *m = static_cast<MediaPlayer *>(p);
if (m && m->ready_) {
if (m && m->opened_) {
m->fill_frame(NULL, MediaPlayer::EOS);
}
}
@@ -1170,7 +1173,7 @@ GstFlowReturn MediaPlayer::callback_new_preroll (GstAppSink *sink, gpointer p)
// send frames to media player only if ready
MediaPlayer *m = static_cast<MediaPlayer *>(p);
if (m && m->ready_) {
if (m && m->opened_) {
// get buffer from sample
GstBuffer *buf = gst_sample_get_buffer (sample);
@@ -1205,7 +1208,7 @@ GstFlowReturn MediaPlayer::callback_new_sample (GstAppSink *sink, gpointer p)
// send frames to media player only if ready
MediaPlayer *m = static_cast<MediaPlayer *>(p);
if (m && m->ready_) {
if (m && m->opened_) {
// get buffer from sample (valid until sample is released)
GstBuffer *buf = gst_sample_get_buffer (sample) ;

View File

@@ -91,8 +91,8 @@ public:
/**
* Open a media using gstreamer URI
* */
void open( std::string path);
void reopen();
void open ( const std::string &filename, const std::string &uri = "");
void reopen ();
/**
* Get name of the media
* */
@@ -265,6 +265,8 @@ public:
static std::list<MediaPlayer*>::const_iterator begin() { return registered_.cbegin(); }
static std::list<MediaPlayer*>::const_iterator end() { return registered_.cend(); }
static MediaInfo UriDiscoverer(const std::string &uri);
private:
// video player description
@@ -285,7 +287,7 @@ private:
GstState desired_state_;
GstElement *pipeline_;
GstVideoInfo v_frame_video_info_;
std::atomic<bool> ready_;
std::atomic<bool> opened_;
std::atomic<bool> failed_;
bool seeking_;
bool enabled_;

View File

@@ -11,7 +11,7 @@
#include "Visitor.h"
#include "Log.h"
MediaSource::MediaSource() : Source(), path_("")
MediaSource::MediaSource(uint64_t id) : Source(id), path_("")
{
// create media player
mediaplayer_ = new MediaPlayer;
@@ -25,11 +25,15 @@ MediaSource::~MediaSource()
void MediaSource::setPath(const std::string &p)
{
Log::Notify("Creating Source with media '%s'", p.c_str());
path_ = p;
Log::Notify("Creating Source with media '%s'", path_.c_str());
// open gstreamer
mediaplayer_->open(path_);
mediaplayer_->play(true);
// will be ready after init and one frame rendered
ready_ = false;
}
std::string MediaSource::path() const
@@ -91,10 +95,9 @@ void MediaSource::init()
active_ = true;
// deep update to reorder
View::need_deep_update_++;
++View::need_deep_update_;
// done init
initialized_ = true;
Log::Info("Source '%s' linked to Media %s.", name().c_str(), std::to_string(mediaplayer_->id()).c_str());
}
}
@@ -123,21 +126,16 @@ void MediaSource::update(float dt)
void MediaSource::render()
{
if (!initialized_)
if ( renderbuffer_ == nullptr )
init();
else {
// blendingshader_->color.r = mediaplayer_->currentTimelineFading();
// blendingshader_->color.g = mediaplayer_->currentTimelineFading();
// blendingshader_->color.b = mediaplayer_->currentTimelineFading();
// render the media player into frame buffer
renderbuffer_->begin();
// texturesurface_->shader()->color.a = mediaplayer_->currentTimelineFading();
texturesurface_->shader()->color.r = mediaplayer_->currentTimelineFading();
texturesurface_->shader()->color.g = mediaplayer_->currentTimelineFading();
texturesurface_->shader()->color.b = mediaplayer_->currentTimelineFading();
// apply fading
texturesurface_->shader()->color = glm::vec4( glm::vec3(mediaplayer_->currentTimelineFading()), 1.f);
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
renderbuffer_->end();
ready_ = true;
}
}

View File

@@ -8,7 +8,7 @@ class MediaPlayer;
class MediaSource : public Source
{
public:
MediaSource();
MediaSource(uint64_t id = 0);
~MediaSource();
// implementation of source API

266
Mixer.cpp
View File

@@ -18,6 +18,7 @@
#include "Log.h"
#include "View.h"
#include "ImageShader.h"
#include "BaseToolkit.h"
#include "SystemToolkit.h"
#include "SessionCreator.h"
#include "SessionVisitor.h"
@@ -25,6 +26,7 @@
#include "MediaSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "MultiFileSource.h"
#include "StreamSource.h"
#include "NetworkSource.h"
#include "ActionManager.h"
@@ -138,7 +140,7 @@ void Mixer::update()
if ( back_session_ ) {
// swap front and back sessions
swap();
View::need_deep_update_++;
++View::need_deep_update_;
// set session filename
Rendering::manager().mainWindow().setTitle(session_->filename());
Settings::application.recentSessions.push(session_->filename());
@@ -181,7 +183,7 @@ void Mixer::update()
failure = nullptr; // prevent delete (already done in recreateSource)
}
// delete the source
deleteSource(failure, false);
deleteSource(failure);
}
// update views
@@ -193,7 +195,7 @@ void Mixer::update()
// deep update was performed
if (View::need_deep_update_ > 0)
View::need_deep_update_--;
--View::need_deep_update_;
}
void Mixer::draw()
@@ -244,6 +246,37 @@ Source * Mixer::createSourceFile(const std::string &path)
return s;
}
Source * Mixer::createSourceMultifile(const std::list<std::string> &list_files, uint fps)
{
// ready to create a source
Source *s = nullptr;
if ( list_files.size() >0 ) {
// validate the creation of a sequence from the list
MultiFileSequence sequence(list_files);
if ( sequence.valid() ) {
// try to create a sequence
MultiFileSource *mfs = new MultiFileSource;
mfs->setSequence(sequence, fps);
s = mfs;
// remember in recent media
Settings::application.recentImport.path = SystemToolkit::path_filename(list_files.front());
// propose a new name
s->setName( SystemToolkit::base_filename( BaseToolkit::common_prefix(list_files) ) );
}
else {
Log::Notify("Could not find a sequence of consecutively numbered files.");
}
}
return s;
}
Source * Mixer::createSourceRender()
{
// ready to create a source
@@ -251,7 +284,10 @@ Source * Mixer::createSourceRender()
s->setSession(session_);
// propose a new name based on session name
s->setName(SystemToolkit::base_filename(session_->filename()));
if ( !session_->filename().empty() )
s->setName(SystemToolkit::base_filename(session_->filename()));
else
s->setName("Output");
return s;
}
@@ -263,8 +299,7 @@ Source * Mixer::createSourceStream(const std::string &gstreamerpipeline)
s->setDescription(gstreamerpipeline);
// propose a new name based on pattern name
std::string name = gstreamerpipeline.substr(0, gstreamerpipeline.find(" "));
s->setName(name);
s->setName( gstreamerpipeline.substr(0, gstreamerpipeline.find(" ")) );
return s;
}
@@ -289,8 +324,7 @@ Source * Mixer::createSourceDevice(const std::string &namedevice)
Source *s = Device::manager().createSource(namedevice);
// propose a new name based on pattern name
std::string name = namedevice.substr(0, namedevice.find(" "));
s->setName(name);
s->setName( namedevice.substr(0, namedevice.find(" ")) );
return s;
}
@@ -333,23 +367,17 @@ Source * Mixer::createSourceClone(const std::string &namesource)
origin = current_source_;
// have an origin, can clone it
if (origin != session_->end()) {
if (origin != session_->end())
// create a source
s = (*origin)->clone();
// propose new name (this automatically increments name)
renameSource(s, (*origin)->name());
}
return s;
}
void Mixer::addSource(Source *s)
{
if (s != nullptr) {
if (s != nullptr)
candidate_sources_.push_back(s);
}
}
void Mixer::insertSource(Source *s, View::Mode m)
@@ -357,7 +385,7 @@ void Mixer::insertSource(Source *s, View::Mode m)
if ( s != nullptr )
{
// avoid duplicate name
renameSource(s, s->name());
renameSource(s);
// Add source to Session (ignored if source already in)
SourceList::iterator sit = session_->addSource(s);
@@ -455,7 +483,7 @@ bool Mixer::recreateSource(Source *s)
return true;
}
void Mixer::deleteSource(Source *s, bool withundo)
void Mixer::deleteSource(Source *s)
{
if ( s != nullptr )
{
@@ -468,10 +496,6 @@ void Mixer::deleteSource(Source *s, bool withundo)
// delete source
session_->deleteSource(s);
// store new state in history manager
if (withundo)
Action::manager().store(name + std::string(" source deleted"));
// log
Log::Notify("Source %s deleted.", name.c_str());
}
@@ -570,22 +594,36 @@ void Mixer::deselect(Source *s)
void Mixer::deleteSelection()
{
// get clones first : this way we store the history of deletion in the right order
SourceList selection_clones_;
for ( auto sit = selection().begin(); sit != selection().end(); sit++ ) {
CloneSource *clone = dynamic_cast<CloneSource *>(*sit);
if (clone)
selection_clones_.push_back(clone);
}
// delete all clones
while ( !selection_clones_.empty() ) {
deleteSource( selection_clones_.front());
selection_clones_.pop_front();
}
// empty the selection
while ( !selection().empty() )
deleteSource( selection().front() ); // this also remove element from selection()
// number of sources in selection
int N = selection().size();
// ignore if selection empty
if (N > 0) {
// adapt Action::manager undo info depending on case
std::ostringstream info;
if (N > 1)
info << N << " sources deleted";
else
info << selection().front()->name() << ": deleted";
// get clones first : this way we store the history of deletion in the right order
SourceList selection_clones_;
for ( auto sit = selection().begin(); sit != selection().end(); sit++ ) {
CloneSource *clone = dynamic_cast<CloneSource *>(*sit);
if (clone)
selection_clones_.push_back(clone);
}
// delete all clones
while ( !selection_clones_.empty() ) {
deleteSource( selection_clones_.front());// this also removes element from selection()
selection_clones_.pop_front();
}
// empty the selection
while ( !selection().empty() )
deleteSource( selection().front() ); // this also removes element from selection()
Action::manager().store(info.str());
}
}
void Mixer::groupSelection()
@@ -637,7 +675,7 @@ void Mixer::groupSelection()
// store in action manager
std::ostringstream info;
info << sessiongroup->name() << " source inserted, " << sessiongroup->session()->numSource() << " sources flatten.";
info << sessiongroup->name() << " inserted: " << sessiongroup->session()->numSource() << " sources flatten.";
Action::manager().store(info.str());
// give the hand to the user
@@ -650,19 +688,13 @@ void Mixer::renameSource(Source *s, const std::string &newname)
if ( s != nullptr )
{
// tentative new name
std::string tentativename = newname;
std::string tentativename = s->name();
// refuse to rename to an empty name
if ( newname.empty() )
tentativename = "source";
// try the given new name if valid
if ( !newname.empty() )
tentativename = newname;
// search for a source of the name 'tentativename'
std::string basename = tentativename;
int count = 1;
for( auto it = session_->begin(); it != session_->end(); it++){
if ( s->id() != (*it)->id() && (*it)->name() == tentativename )
tentativename = basename + std::to_string( ++count );
}
tentativename = BaseToolkit::uniqueName(tentativename, session_->getNameList(s->id()));
// ok to rename
s->setName(tentativename);
@@ -679,7 +711,7 @@ void Mixer::setCurrentSource(SourceList::iterator it)
unsetCurrentSource();
// change current if 'it' is valid
if ( it != session_->end() ) {
if ( it != session_->end() /*&& (*it)->mode() > Source::UNINITIALIZED */) {
current_source_ = it;
current_source_index_ = session_->index(current_source_);
@@ -886,7 +918,7 @@ void Mixer::setView(View::Mode m)
}
// need to deeply update view to apply eventual changes
View::need_deep_update_++;
++View::need_deep_update_;
}
View *Mixer::view(View::Mode m)
@@ -942,25 +974,28 @@ void Mixer::load(const std::string& filename)
#endif
}
void Mixer::open(const std::string& filename)
void Mixer::open(const std::string& filename, bool smooth)
{
if (Settings::application.smooth_transition)
if (smooth)
{
Log::Info("\nStarting transition to session %s", filename.c_str());
// create special SessionSource to be used for the smooth transition
SessionFileSource *ts = new SessionFileSource;
// open filename if specified
if (!filename.empty())
{
Log::Info("\nStarting transition to session %s", filename.c_str());
ts->load(filename);
// propose a new name based on uri
renameSource(ts, SystemToolkit::base_filename(filename));
// propose a new name based on uri
ts->setName(SystemToolkit::base_filename(filename));
}
// attach the SessionSource to the transition view
transition_.attach(ts);
// insert source and switch to transition view
insertSource(ts, View::TRANSITION);
// attach the SessionSource to the transition view
transition_.attach(ts);
}
else
load(filename);
@@ -993,15 +1028,12 @@ void Mixer::merge(Session *session)
return;
}
// new state in history manager
std::ostringstream info;
info << session->numSource() << " sources imported from " << session->filename();
Action::manager().store(info.str());
// import every sources
std::ostringstream info;
info << session->numSource() << " sources imported from:" << session->filename();
for ( Source *s = session->popSource(); s != nullptr; s = session->popSource()) {
// avoid name duplicates
renameSource(s, s->name());
renameSource(s);
// Add source to Session
session_->addSource(s);
@@ -1018,10 +1050,14 @@ void Mixer::merge(Session *session)
}
// needs to update !
View::need_deep_update_++;
++View::need_deep_update_;
// avoid display issues
current_view_->update(0.f);
// new state in history manager
Action::manager().store(info.str());
}
void Mixer::merge(SessionSource *source)
@@ -1036,7 +1072,7 @@ void Mixer::merge(SessionSource *source)
// prepare Action manager info
std::ostringstream info;
info << source->name().c_str() << " source deleted, " << session->numSource() << " sources imported";
info << source->name().c_str() << " expanded:" << session->numSource() << " sources imported";
// import sources of the session (if not empty)
if ( !session->empty() ) {
@@ -1067,7 +1103,7 @@ void Mixer::merge(SessionSource *source)
for ( Source *s = session->popSource(); s != nullptr; s = session->popSource()) {
// avoid name duplicates
renameSource(s, s->name());
renameSource(s);
// scale alpha
s->setAlpha( s->alpha() * source->alpha() );
@@ -1101,7 +1137,7 @@ void Mixer::merge(SessionSource *source)
}
// needs to update !
View::need_deep_update_++;
++View::need_deep_update_;
}
@@ -1109,12 +1145,11 @@ void Mixer::merge(SessionSource *source)
detach(source);
session_->deleteSource(source);
// new state in history manager
Action::manager().store(info.str());
// avoid display issues
current_view_->update(0.f);
// new state in history manager
Action::manager().store(info.str());
}
void Mixer::swap()
@@ -1164,15 +1199,15 @@ void Mixer::swap()
back_session_ = nullptr;
// reset History manager
Action::manager().clear();
Action::manager().init();
// notification
Log::Notify("Session %s loaded. %d source(s) created.", session_->filename().c_str(), session_->numSource());
}
void Mixer::close()
void Mixer::close(bool smooth)
{
if (Settings::application.smooth_transition)
if (smooth)
{
// create empty SessionSource to be used for the smooth transition
SessionFileSource *ts = new SessionFileSource;
@@ -1200,7 +1235,7 @@ void Mixer::clear()
sessionSwapRequested_ = true;
// need to deeply update view to apply eventual changes
View::need_deep_update_++;
++View::need_deep_update_;
Log::Info("New session ready.");
}
@@ -1241,3 +1276,82 @@ void Mixer::paste(const std::string& clipboard)
}
}
}
void Mixer::restore(tinyxml2::XMLElement *sessionNode)
{
//
// source lists
//
// sessionsources contains list of ids of all sources currently in the session (before loading)
SourceIdList session_sources = session_->getIdList();
// for( auto it = sessionsources.begin(); it != sessionsources.end(); it++)
// Log::Info("sessionsources id %s", std::to_string(*it).c_str());
// load history status:
// - if a source exists, its attributes are updated, and that's all
// - if a source does not exists (in current session), it is created inside the session
SessionLoader loader( session_ );
loader.load( sessionNode );
// loaded_sources contains map of xml ids of all sources treated by loader
std::map< uint64_t, Source* > loaded_sources = loader.getSources();
// remove intersect of both lists (sources were updated by SessionLoader)
for( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); ){
auto ssit = std::find(session_sources.begin(), session_sources.end(), (*lsit).first);
if ( ssit != session_sources.end() ) {
lsit = loaded_sources.erase(lsit);
session_sources.erase(ssit);
}
else
lsit++;
}
// remaining ids in list sessionsources : to remove
while ( !session_sources.empty() ){
Source *s = Mixer::manager().findSource( session_sources.front() );
if (s!=nullptr) {
#ifdef ACTION_DEBUG
Log::Info("Delete id %s\n", std::to_string(session_sources.front() ).c_str());
#endif
// remove the source from the mixer
detach( s );
// delete source from session
session_->deleteSource( s );
}
session_sources.pop_front();
}
// remaining sources in list loaded_sources : to add
for ( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); lsit++)
{
#ifdef ACTION_DEBUG
Log::Info("Recreate id %s to %s\n", std::to_string((*lsit).first).c_str(), std::to_string((*lsit).second->id()).c_str());
#endif
// attach created source
attach( (*lsit).second );
}
//
// mixing groups
//
// Get the list of mixing groups in the xml loader
std::list< SourceList > loadergroups = loader.getMixingGroups();
// clear all session groups
auto group_iter = session_->beginMixingGroup();
while ( group_iter != session_->endMixingGroup() )
group_iter = session_->deleteMixingGroup(group_iter);
// apply all changes creating or modifying groups in the session
// (after this, new groups are created and existing groups are adjusted)
for (auto group_loader_it = loadergroups.begin(); group_loader_it != loadergroups.end(); group_loader_it++)
session_->link( *group_loader_it, view(View::MIXING)->scene.fg() );
++View::need_deep_update_;
}

18
Mixer.h
View File

@@ -9,14 +9,18 @@
#include "Session.h"
#include "Selection.h"
namespace tinyxml2 {
class XMLElement;
}
class SessionSource;
class Mixer
{
// Private Constructor
Mixer();
Mixer(Mixer const& copy); // Not Implemented
Mixer& operator=(Mixer const& copy); // Not Implemented
Mixer(Mixer const& copy) = delete;
Mixer& operator=(Mixer const& copy) = delete;
public:
@@ -44,6 +48,7 @@ public:
// creation of sources
Source * createSourceFile (const std::string &path);
Source * createSourceMultifile(const std::list<std::string> &list_files, uint fps);
Source * createSourceClone (const std::string &namesource = "");
Source * createSourceRender ();
Source * createSourceStream (const std::string &gstreamerpipeline);
@@ -54,8 +59,8 @@ public:
// operations on sources
void addSource (Source *s);
void deleteSource (Source *s, bool withundo=true);
void renameSource (Source *s, const std::string &newname);
void deleteSource (Source *s);
void renameSource (Source *s, const std::string &newname = "");
void attach (Source *s);
void detach (Source *s);
void deselect (Source *s);
@@ -103,11 +108,12 @@ public:
void set (Session *session);
// operations depending on transition mode
void close ();
void open (const std::string& filename);
void close (bool smooth = false);
void open (const std::string& filename, bool smooth = false);
// create sources if clipboard contains well-formed xml text
void paste (const std::string& clipboard);
void restore(tinyxml2::XMLElement *sessionNode);
protected:

View File

@@ -7,6 +7,7 @@
#include "Source.h"
#include "Decorations.h"
#include "Visitor.h"
#include "BaseToolkit.h"
#include "Log.h"
#include "MixingGroup.h"
@@ -16,10 +17,10 @@ MixingGroup::MixingGroup (SourceList sources) : parent_(nullptr), root_(nullptr)
center_pos_(glm::vec2(0.f, 0.f)), active_(true), update_action_(ACTION_NONE), updated_source_(nullptr)
{
// create unique id
id_ = GlmToolkit::uniqueId();
id_ = BaseToolkit::uniqueId();
// fill the vector of sources with the given list
for (auto it = sources.begin(); it != sources.end(); it++){
for (auto it = sources.begin(); it != sources.end(); ++it){
// add only if not linked already
if ((*it)->mixinggroup_ == nullptr) {
(*it)->mixinggroup_ = this;
@@ -43,7 +44,7 @@ MixingGroup::MixingGroup (SourceList sources) : parent_(nullptr), root_(nullptr)
MixingGroup::~MixingGroup ()
{
for (auto it = sources_.begin(); it != sources_.end(); it++)
for (auto it = sources_.begin(); it != sources_.end(); ++it)
(*it)->clearMixingGroup();
if (parent_)
@@ -71,7 +72,7 @@ void MixingGroup::recenter()
{
// compute barycenter (0)
center_pos_ = glm::vec2(0.f, 0.f);
for (auto it = sources_.begin(); it != sources_.end(); it++){
for (auto it = sources_.begin(); it != sources_.end(); ++it){
// compute barycenter (1)
center_pos_ += glm::vec2((*it)->group(View::MIXING)->translation_);
}
@@ -126,7 +127,7 @@ void MixingGroup::update (float)
// compute barycenter (0)
center_pos_ = glm::vec2(0.f, 0.f);
auto it = sources_.begin();
for (; it != sources_.end(); it++){
for (; it != sources_.end(); ++it){
// update point
p[ index_points_[*it] ] = glm::vec2((*it)->group(View::MIXING)->translation_);
@@ -160,7 +161,7 @@ void MixingGroup::update (float)
// compute barycenter (0)
center_pos_ = glm::vec2(0.f, 0.f);
auto it = sources_.begin();
for (; it != sources_.end(); it++){
for (; it != sources_.end(); ++it){
// modify all but the already updated source
if ( *it != updated_source_ && !(*it)->locked() ) {
@@ -196,7 +197,7 @@ void MixingGroup::update (float)
int numactions = 0;
auto it = sources_.begin();
for (; it != sources_.end(); it++){
for (; it != sources_.end(); ++it){
// modify all but the already updated source
if ( *it != updated_source_ && !(*it)->locked() ) {
@@ -256,7 +257,7 @@ void MixingGroup::detach (Source *s)
void MixingGroup::detach (SourceList l)
{
for (auto sit = l.begin(); sit != l.end(); sit++) {
for (auto sit = l.begin(); sit != l.end(); ++sit) {
// find the source
SourceList::iterator its = std::find(sources_.begin(), sources_.end(), *sit);
// ok, its in the list !
@@ -291,7 +292,7 @@ void MixingGroup::attach (Source *s)
void MixingGroup::attach (SourceList l)
{
for (auto sit = l.begin(); sit != l.end(); sit++) {
for (auto sit = l.begin(); sit != l.end(); ++sit) {
if ( (*sit)->mixinggroup_ == nullptr) {
// tell the source
(*sit)->mixinggroup_ = this;
@@ -359,7 +360,7 @@ void MixingGroup::createLineStrip()
// path linking all sources
std::vector<glm::vec2> path;
for (auto it = sources_.begin(); it != sources_.end(); it++){
for (auto it = sources_.begin(); it != sources_.end(); ++it){
index_points_[*it] = path.size();
path.push_back(glm::vec2((*it)->group(View::MIXING)->translation_));
}

View File

@@ -14,6 +14,9 @@ class MixingGroup
public:
MixingGroup (SourceList sources);
// non assignable class
MixingGroup(MixingGroup const&) = delete;
MixingGroup& operator=(MixingGroup const&) = delete;
~MixingGroup ();
// Get unique id

View File

@@ -14,6 +14,7 @@
#include "Mixer.h"
#include "defines.h"
#include "Source.h"
#include "Settings.h"
#include "Decorations.h"
#include "UserInterfaceManager.h"
@@ -351,12 +352,13 @@ std::pair<Node *, glm::vec2> MixingView::pick(glm::vec2 P)
Source *s = Mixer::manager().findSource(pick.first);
if (s != nullptr) {
// pick on the lock icon; unlock source
if ( pick.first == s->lock_) {
if ( UserInterface::manager().ctrlModifier() && pick.first == s->lock_) {
lock(s, false);
pick = { s->locker_, pick.second };
// pick = { s->locker_, pick.second };
pick = { nullptr, glm::vec2(0.f) };
}
// pick on the open lock icon; lock source and cancel pick
else if ( pick.first == s->unlock_ ) {
else if ( UserInterface::manager().ctrlModifier() && pick.first == s->unlock_ ) {
lock(s, true);
pick = { nullptr, glm::vec2(0.f) };
}
@@ -494,7 +496,7 @@ void MixingView::terminate()
// terminate all mixing group actions
for (auto g = Mixer::manager().session()->beginMixingGroup();
g != Mixer::manager().session()->endMixingGroup(); g++)
g != Mixer::manager().session()->endMixingGroup(); ++g)
(*g)->setAction( MixingGroup::ACTION_FINISH );
}
@@ -527,7 +529,7 @@ void MixingView::arrow (glm::vec2 movement)
bool first = true;
glm::vec3 delta_translation(0.f);
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) {
// individual move with SHIFT
if ( !Source::isCurrent(*it) && UserInterface::manager().shiftModifier() )
@@ -678,7 +680,7 @@ uint textureMixingQuadratic()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
}
return texid;
}

View File

@@ -9,6 +9,9 @@ class MixingView : public View
{
public:
MixingView();
// non assignable class
MixingView(MixingView const&) = delete;
MixingView& operator=(MixingView const&) = delete;
void draw () override;
void update (float dt) override;

202
MultiFileSource.cpp Normal file
View File

@@ -0,0 +1,202 @@
#include <sstream>
#include <algorithm>
#include "defines.h"
#include "ImageShader.h"
#include "Resource.h"
#include "Decorations.h"
#include "Stream.h"
#include "Visitor.h"
#include "GstToolkit.h"
#include "BaseToolkit.h"
#include "SystemToolkit.h"
#include "MediaPlayer.h"
#include "Log.h"
#include "MultiFileSource.h"
// example test gstreamer pipelines
//
// multifile : sequence of numbered images
// gst-launch-1.0 multifilesrc location="/home/bhbn/Images/sequence/frames%03d.png" caps="image/png,framerate=\(fraction\)12/1" loop=1 ! decodebin ! videoconvert ! autovideosink
//
// imagesequencesrc : sequence of numbered images (cannot loop)
// gst-launch-1.0 imagesequencesrc location=frames%03d.png start-index=1 framerate=24/1 ! decodebin ! videoconvert ! autovideosink
//
MultiFileSequence::MultiFileSequence() : width(0), height(0), min(0), max(0)
{
}
MultiFileSequence::MultiFileSequence(const std::list<std::string> &list_files)
{
location = BaseToolkit::common_numbered_pattern(list_files, &min, &max);
// sanity check: the location pattern looks like a filename and seems consecutive numbered
if ( SystemToolkit::extension_filename(location).empty() ||
SystemToolkit::path_filename(location) != SystemToolkit::path_filename(list_files.front()) ||
list_files.size() != max - min + 1 ) {
location.clear();
}
if ( !location.empty() ) {
MediaInfo media = MediaPlayer::UriDiscoverer( GstToolkit::filename_to_uri( list_files.front() ) );
if (media.valid && media.isimage) {
codec.resize(media.codec_name.size());
std::transform(media.codec_name.begin(), media.codec_name.end(), codec.begin(), ::tolower);
width = media.width;
height = media.height;
}
}
}
bool MultiFileSequence::valid() const
{
return !( location.empty() || codec.empty() || width < 1 || height < 1 || max == min);
}
inline MultiFileSequence& MultiFileSequence::operator = (const MultiFileSequence& b)
{
if (this != &b) {
this->width = b.width;
this->height = b.height;
this->min = b.min;
this->max = b.max;
this->location = b.location;
this->codec = b.codec;
}
return *this;
}
bool MultiFileSequence::operator != (const MultiFileSequence& b)
{
return ( location != b.location || codec != b.codec || width != b.width ||
height != b.height || min != b.min || max != b.max );
}
MultiFile::MultiFile() : Stream(), src_(nullptr)
{
}
void MultiFile::open (const MultiFileSequence &sequence, uint framerate )
{
if (sequence.location.empty())
return;
std::ostringstream gstreamer_pipeline;
gstreamer_pipeline << "multifilesrc name=src location=\"";
gstreamer_pipeline << sequence.location;
gstreamer_pipeline << "\" caps=\"image/";
gstreamer_pipeline << sequence.codec;
gstreamer_pipeline << ",framerate=(fraction)";
gstreamer_pipeline << framerate;
gstreamer_pipeline << "/1\" loop=1";
gstreamer_pipeline << " start-index=";
gstreamer_pipeline << sequence.min;
gstreamer_pipeline << " stop-index=";
gstreamer_pipeline << sequence.max;
gstreamer_pipeline << " ! decodebin ! videoconvert";
// (private) open stream
Stream::open(gstreamer_pipeline.str(), sequence.width, sequence.height);
// keep multifile source for dynamic properties change
src_ = gst_bin_get_by_name (GST_BIN (pipeline_), "src");
}
void MultiFile::close ()
{
if (src_ != nullptr) {
gst_object_unref (src_);
src_ = nullptr;
}
Stream::close();
}
void MultiFile::setProperties (int begin, int end, int loop)
{
if (src_) {
g_object_set (src_, "start-index", MAX(begin, 0), NULL);
g_object_set (src_, "stop-index", MAX(end, 0), NULL);
g_object_set (src_, "loop", MIN(loop, 1), NULL);
}
}
MultiFileSource::MultiFileSource (uint64_t id) : StreamSource(id), framerate_(0), begin_(-1), end_(INT_MAX), loop_(1)
{
// create stream
stream_ = static_cast<Stream *>( new MultiFile );
// set symbol
symbol_ = new Symbol(Symbol::SEQUENCE, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
void MultiFileSource::setFiles (const std::list<std::string> &list_files, uint framerate)
{
setSequence(MultiFileSequence(list_files), framerate);
}
void MultiFileSource::setSequence (const MultiFileSequence &sequence, uint framerate)
{
framerate_ = CLAMP( framerate, 1, 30);
sequence_ = sequence;
if (sequence_.valid())
{
// open gstreamer
multifile()->open( sequence_, framerate_ );
stream_->play(true);
// validate range and apply loop_
setRange(begin_, end_);
// will be ready after init and one frame rendered
ready_ = false;
}
}
void MultiFileSource::setFramerate (uint framerate)
{
if (multifile()) {
setSequence(sequence_, framerate);
}
}
void MultiFileSource::setLoop (bool on)
{
if (multifile()) {
loop_ = on ? 1 : 0;
multifile()->setProperties (begin_, end_, loop_);
}
}
void MultiFileSource::setRange (int begin, int end)
{
begin_ = glm::clamp( begin, sequence_.min, sequence_.max );
end_ = glm::clamp( end , sequence_.min, sequence_.max );
begin_ = glm::min( begin_, end_ );
end_ = glm::max( begin_, end_ );
if (multifile())
multifile()->setProperties (begin_, end_, loop_);
}
void MultiFileSource::accept (Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
MultiFile *MultiFileSource::multifile () const
{
return dynamic_cast<MultiFile *>(stream_);
}

76
MultiFileSource.h Normal file
View File

@@ -0,0 +1,76 @@
#ifndef MULTIFILESOURCE_H
#define MULTIFILESOURCE_H
#include <string>
#include <list>
#include "StreamSource.h"
struct MultiFileSequence {
std::string location;
std::string codec;
uint width;
uint height;
int min;
int max;
MultiFileSequence ();
MultiFileSequence (const std::list<std::string> &list_files);
bool valid () const;
MultiFileSequence& operator = (const MultiFileSequence& b);
bool operator != (const MultiFileSequence& b);
};
class MultiFile : public Stream
{
public:
MultiFile ();
void open (const MultiFileSequence &sequence, uint framerate = 30);
void close () override;
// dynamic change of gstreamer multifile source properties
void setProperties(int begin, int end, int loop);
protected:
GstElement *src_ ;
};
class MultiFileSource : public StreamSource
{
public:
MultiFileSource (uint64_t id = 0);
// Source interface
void accept (Visitor& v) override;
// StreamSource interface
Stream *stream () const override { return stream_; }
glm::ivec2 icon () const override { return glm::ivec2(3, 9); }
// specific interface
void setFiles (const std::list<std::string> &list_files, uint framerate);
void setSequence (const MultiFileSequence &sequence, uint framerate);
inline MultiFileSequence sequence () const { return sequence_; }
void setFramerate (uint fps);
inline uint framerate () const { return framerate_; }
void setLoop (bool on);
inline bool loop () const { return loop_ > 0 ? true : false; }
void setRange (int begin, int end);
inline int begin() const { return begin_; }
inline int end () const { return end_; }
MultiFile *multifile () const;
private:
MultiFileSequence sequence_;
uint framerate_;
int begin_, end_, loop_;
};
#endif // MULTIFILESOURCE_H

View File

@@ -204,7 +204,7 @@ void NetworkStream::update()
{
Stream::update();
if ( !ready_ && !failed_ && received_config_)
if ( !opened_ && !failed_ && received_config_)
{
// only once
received_config_ = false;
@@ -268,7 +268,7 @@ void NetworkStream::update()
}
NetworkSource::NetworkSource() : StreamSource()
NetworkSource::NetworkSource(uint64_t id) : StreamSource(id)
{
// create stream
stream_ = static_cast<Stream *>( new NetworkStream );
@@ -297,6 +297,9 @@ void NetworkSource::setConnection(const std::string &nameconnection)
// open network stream
networkStream()->connect( connection_name_ );
stream_->play(true);
// will be ready after init and one frame rendered
ready_ = false;
}

View File

@@ -20,6 +20,7 @@ protected:
const IpEndpointName& remoteEndpoint );
public:
inline void setParent(NetworkStream *s) { parent_ = s; }
StreamerResponseListener() : parent_(nullptr) {}
};
@@ -59,7 +60,7 @@ class NetworkSource : public StreamSource
std::string connection_name_;
public:
NetworkSource();
NetworkSource(uint64_t id = 0);
~NetworkSource();
// Source interface

23
Overlay.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "Overlay.h"
Overlay::Overlay()
{
}
SnapshotOverlay::SnapshotOverlay() : Overlay()
{
}
void SnapshotOverlay::draw()
{
}
void SnapshotOverlay::update (float dt)
{
}

38
Overlay.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef OVERLAY_H
#define OVERLAY_H
#include "View.h"
class Overlay
{
public:
Overlay();
virtual void update (float dt) = 0;
virtual void draw () = 0;
virtual std::pair<Node *, glm::vec2> pick(glm::vec2) {
return { nullptr, glm::vec2(0.f) };
}
virtual View::Cursor grab (Source*, glm::vec2, glm::vec2, std::pair<Node *, glm::vec2>) {
return View::Cursor ();
}
virtual View::Cursor over (glm::vec2) {
return View::Cursor ();
}
};
class SnapshotOverlay: public Overlay
{
public:
SnapshotOverlay();
void draw () override;
void update (float dt) override;
};
#endif // OVERLAY_H

View File

@@ -131,7 +131,7 @@ void Pattern::open( uint pattern, glm::ivec2 res )
Stream::open(gstreamer_pattern, res.x, res.y);
}
PatternSource::PatternSource() : StreamSource()
PatternSource::PatternSource(uint64_t id) : StreamSource(id)
{
// create stream
stream_ = static_cast<Stream *>( new Pattern );
@@ -145,8 +145,12 @@ void PatternSource::setPattern(uint type, glm::ivec2 resolution)
{
Log::Notify("Creating Source with pattern '%s'", Pattern::pattern_types[type].c_str());
// open gstreamer
pattern()->open( (uint) type, resolution );
stream_->play(true);
// will be ready after init and one frame rendered
ready_ = false;
}
void PatternSource::accept(Visitor& v)

View File

@@ -23,7 +23,7 @@ private:
class PatternSource : public StreamSource
{
public:
PatternSource();
PatternSource(uint64_t id = 0);
// Source interface
void accept (Visitor& v) override;

View File

@@ -24,11 +24,6 @@ RenderView::~RenderView()
delete fading_overlay_;
}
bool RenderView::canSelect(Source *s) {
return false;
}
void RenderView::setFading(float f)
{
if (fading_overlay_ == nullptr)
@@ -82,3 +77,65 @@ void RenderView::draw()
frame_buffer_->end();
}
}
void RenderView::drawThumbnail()
{
if (frame_buffer_) {
// if a thumbnailer is pending
if (thumbnailer_.size() > 0) {
try {
// new thumbnailing framebuffer
FrameBuffer *frame_thumbnail = new FrameBuffer( glm::vec3(SESSION_THUMBNAIL_HEIGHT * frame_buffer_->aspectRatio(), SESSION_THUMBNAIL_HEIGHT, 1.f) );
// render
if (Settings::application.render.blit) {
if ( !frame_buffer_->blit(frame_thumbnail) )
throw std::runtime_error("no blit");
}
else {
FrameBufferSurface *thumb = new FrameBufferSurface(frame_buffer_);
frame_thumbnail->begin();
thumb->draw(glm::identity<glm::mat4>(), frame_thumbnail->projection());
frame_thumbnail->end();
delete thumb;
}
// return valid thumbnail promise
thumbnailer_.back().set_value( frame_thumbnail->image() );
// done with thumbnailing framebuffer
delete frame_thumbnail;
}
catch(...) {
// return failed thumbnail promise
thumbnailer_.back().set_exception(std::current_exception());
}
// done with this promise
thumbnailer_.pop_back();
}
}
}
FrameBufferImage *RenderView::thumbnail ()
{
// by default null image
FrameBufferImage *img = nullptr;
// create and store a promise for a FrameBufferImage
thumbnailer_.emplace_back( std::promise<FrameBufferImage *>() );
// future will return the primised FrameBufferImage
std::future<FrameBufferImage *> t = thumbnailer_.back().get_future();
try {
// wait for valid return value from promise
img = t.get();
}
// catch any failed promise
catch (std::runtime_error&){
}
return img;
}

View File

@@ -1,19 +1,27 @@
#ifndef RENDERVIEW_H
#define RENDERVIEW_H
#include <vector>
#include <future>
#include "View.h"
class RenderView : public View
{
// rendering FBO
FrameBuffer *frame_buffer_;
Surface *fading_overlay_;
// promises of returning thumbnails after an update
std::vector< std::promise<FrameBufferImage *> > thumbnailer_;
public:
RenderView ();
~RenderView ();
// render frame (in opengl context)
void draw () override;
bool canSelect(Source *) override;
bool canSelect(Source *) override { return false; }
void setResolution (glm::vec3 resolution = glm::vec3(0.f), bool useAlpha = false);
glm::vec3 resolution() const { return frame_buffer_->resolution(); }
@@ -21,7 +29,12 @@ public:
void setFading(float f = 0.f);
float fading() const;
// current frame
inline FrameBuffer *frame () const { return frame_buffer_; }
// get a thumbnail outside of opengl context; wait for a promise to be fullfiled after draw
void drawThumbnail();
FrameBufferImage *thumbnail ();
};
#endif // RENDERVIEW_H

View File

@@ -188,6 +188,7 @@ bool Rendering::init()
// additional window callbacks for main window
glfwSetWindowRefreshCallback( main_.window(), WindowRefreshCallback );
glfwSetDropCallback( main_.window(), Rendering::FileDropped);
glfwSetWindowSizeLimits( main_.window(), 800, 500, GLFW_DONT_CARE, GLFW_DONT_CARE);
//
// Gstreamer setup
@@ -342,7 +343,7 @@ void Rendering::terminate()
// close window
glfwDestroyWindow(output_.window());
glfwDestroyWindow(main_.window());
glfwTerminate();
// glfwTerminate();
}
@@ -423,14 +424,15 @@ glm::vec2 Rendering::project(glm::vec3 scene_coordinate, glm::mat4 modelview, bo
void Rendering::FileDropped(GLFWwindow *, int path_count, const char* paths[])
{
for (int i = 0; i < path_count; ++i) {
int i = 0;
for (; i < path_count; ++i) {
std::string filename(paths[i]);
if (filename.empty())
break;
// try to create a source
Mixer::manager().addSource ( Mixer::manager().createSourceFile( filename ) );
}
if (path_count>0) {
if (i>0) {
UserInterface::manager().showPannel();
Rendering::manager().mainWindow().show();
}
@@ -726,6 +728,8 @@ bool RenderingWindow::init(int index, GLFWwindow *share)
// DPI scaling (retina)
dpi_scale_ = float(window_attributes_.viewport.y) / float(winset.h);
// We decide for byte aligned textures all over
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
// This hint can improve the speed of texturing when perspective-correct texture coordinate interpolation isn't needed
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
// fast mipmaps (we are not really using mipmaps anyway)

View File

@@ -92,8 +92,8 @@ class Rendering
// Private Constructor
Rendering();
Rendering(Rendering const& copy); // Not Implemented
Rendering& operator=(Rendering const& copy); // Not Implemented
Rendering(Rendering const& copy) = delete;
Rendering& operator=(Rendering const& copy) = delete;
public:

View File

@@ -38,6 +38,7 @@ uint Resource::getTextureBlack()
// texture with one black pixel
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
glBindTexture(GL_TEXTURE_2D, 0);
}
return tex_index_black;
@@ -59,6 +60,7 @@ uint Resource::getTextureWhite()
// texture with one black pixel
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
glBindTexture(GL_TEXTURE_2D, 0);
}
return tex_index_white;
@@ -80,6 +82,7 @@ uint Resource::getTextureTransparent()
// texture with one black pixel
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
glBindTexture(GL_TEXTURE_2D, 0);
}
return tex_index_transparent;
@@ -219,6 +222,7 @@ uint Resource::getTextureDDS(const std::string& path, float *aspect_ratio)
if(height < 1) height = 1;
}
glBindTexture(GL_TEXTURE_2D, 0);
// remember to avoid openning the same resource twice
textureIndex[path] = textureID;
@@ -263,19 +267,20 @@ uint Resource::getTextureImage(const std::string& path, float *aspect_ratio)
}
if (h == 0){
Log::Error("Invalid image in ressource %s", std::string(path).c_str());
stbi_image_free(img);
return 0;
}
ar = static_cast<float>(w) / static_cast<float>(h);
glGenTextures(1, &textureID);
glBindTexture( GL_TEXTURE_2D, textureID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glBindTexture( GL_TEXTURE_2D, textureID);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w, h);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
// free memory
stbi_image_free(img);

View File

@@ -13,6 +13,7 @@
#include "Visitor.h"
#include "GarbageVisitor.h"
#include "Log.h"
#include "BaseToolkit.h"
#include "GlmToolkit.h"
#include "SessionVisitor.h"
@@ -25,7 +26,7 @@ static int num_nodes_ = 0;
Node::Node() : initialized_(false), visible_(true), refcount_(0)
{
// create unique id
id_ = GlmToolkit::uniqueId();
id_ = BaseToolkit::uniqueId();
transform_ = glm::identity<glm::mat4>();
scale_ = glm::vec3(1.f);
@@ -56,7 +57,7 @@ void Node::clearCallbacks()
}
}
void Node::copyTransform(Node *other)
void Node::copyTransform(const Node *other)
{
if (!other)
return;

View File

@@ -13,6 +13,7 @@
#include <map>
#include "UpdateCallback.h"
#include "GlmToolkit.h"
// Forward declare classes referenced
class Shader;
@@ -64,7 +65,7 @@ public:
// accept all kind of visitors
virtual void accept (Visitor& v);
void copyTransform (Node *other);
void copyTransform (const Node *other);
// public members, to manipulate with care
bool visible_;
@@ -239,6 +240,9 @@ class Scene {
public:
Scene();
// non assignable class
Scene(Scene const&) = delete;
Scene& operator=(Scene const&) = delete;
~Scene();
void accept (Visitor& v);

View File

@@ -61,7 +61,7 @@ void Selection::set(SourceList l)
{
clear();
for(auto it = l.begin(); it != l.end(); it++)
for(auto it = l.begin(); it != l.end(); ++it)
(*it)->setMode(Source::SELECTED);
l.sort();
@@ -71,7 +71,7 @@ void Selection::set(SourceList l)
void Selection::add(SourceList l)
{
for(auto it = l.begin(); it != l.end(); it++)
for(auto it = l.begin(); it != l.end(); ++it)
(*it)->setMode(Source::SELECTED);
// generate new set as union of current selection and give list
@@ -86,7 +86,7 @@ void Selection::add(SourceList l)
void Selection::remove(SourceList l)
{
for(auto it = l.begin(); it != l.end(); it++)
for(auto it = l.begin(); it != l.end(); ++it)
(*it)->setMode(Source::VISIBLE);
// generate new set as difference of current selection and give list
@@ -98,7 +98,7 @@ void Selection::remove(SourceList l)
void Selection::clear()
{
for(auto it = selection_.begin(); it != selection_.end(); it++)
for(auto it = selection_.begin(); it != selection_.end(); ++it)
(*it)->setMode(Source::VISIBLE);
selection_.clear();

View File

@@ -1,6 +1,8 @@
#include <algorithm>
#include "defines.h"
#include "BaseToolkit.h"
#include "Source.h"
#include "Settings.h"
#include "FrameBuffer.h"
#include "Session.h"
@@ -11,7 +13,12 @@
#include "Log.h"
Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f), filename_("")
SessionNote::SessionNote(const std::string &t, bool l, int s): label(std::to_string(BaseToolkit::uniqueId())),
text(t), large(l), stick(s), pos(glm::vec2(520.f, 30.f)), size(glm::vec2(220.f, 220.f))
{
}
Session::Session() : active_(true), filename_(""), failedSource_(nullptr), fading_target_(0.f)
{
config_[View::RENDERING] = new Group;
config_[View::RENDERING]->scale_ = glm::vec3(0.f);
@@ -31,6 +38,8 @@ Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f),
config_[View::TEXTURE] = new Group;
config_[View::TEXTURE]->scale_ = Settings::application.views[View::TEXTURE].default_scale;
config_[View::TEXTURE]->translation_ = Settings::application.views[View::TEXTURE].default_translation;
snapshots_.xmlDoc_ = new tinyxml2::XMLDocument;
}
@@ -54,13 +63,16 @@ Session::~Session()
delete config_[View::LAYER];
delete config_[View::MIXING];
delete config_[View::TEXTURE];
snapshots_.keys_.clear();
delete snapshots_.xmlDoc_;
}
void Session::setActive (bool on)
{
if (active_ != on) {
active_ = on;
for(auto it = sources_.begin(); it != sources_.end(); it++) {
for(auto it = sources_.begin(); it != sources_.end(); ++it) {
(*it)->setActive(active_);
}
}
@@ -75,6 +87,7 @@ void Session::update(float dt)
// pre-render of all sources
failedSource_ = nullptr;
bool ready = true;
for( SourceList::iterator it = sources_.begin(); it != sources_.end(); ++it){
// ensure the RenderSource is rendering this session
@@ -86,6 +99,8 @@ void Session::update(float dt)
failedSource_ = (*it);
}
else {
if ( !(*it)->ready() )
ready = false;
// render the source
(*it)->render();
// update the source
@@ -118,9 +133,11 @@ void Session::update(float dt)
// draw render view in Frame Buffer
render_.draw();
// draw the thumbnail only after all sources are ready
if (ready)
render_.drawThumbnail();
}
SourceList::iterator Session::addSource(Source *s)
{
// lock before change
@@ -172,7 +189,6 @@ SourceList::iterator Session::deleteSource(Source *s)
return its;
}
void Session::removeSource(Source *s)
{
// lock before change
@@ -195,7 +211,6 @@ void Session::removeSource(Source *s)
access_.unlock();
}
Source *Session::popSource()
{
Source *s = nullptr;
@@ -279,6 +294,19 @@ SourceIdList Session::getIdList() const
return ids(sources_);
}
std::list<std::string> Session::getNameList(uint64_t exceptid) const
{
std::list<std::string> namelist;
for( SourceList::const_iterator it = sources_.cbegin(); it != sources_.cend(); ++it) {
if ( (*it)->id() != exceptid )
namelist.push_back( (*it)->name() );
}
return namelist;
}
bool Session::empty() const
{
return sources_.empty();
@@ -302,7 +330,7 @@ int Session::index(SourceList::iterator it) const
{
int index = -1;
int count = 0;
for(auto i = sources_.begin(); i != sources_.end(); i++, count++) {
for(auto i = sources_.begin(); i != sources_.end(); ++i, ++count) {
if ( i == it ) {
index = count;
break;
@@ -335,7 +363,7 @@ bool Session::canlink (SourceList sources)
// verify that all sources given are valid in the sesion
validate(sources);
for (auto it = sources.begin(); it != sources.end(); it++) {
for (auto it = sources.begin(); it != sources.end(); ++it) {
// this source is linked
if ( (*it)->mixingGroup() != nullptr ) {
// askt its group to detach it
@@ -370,7 +398,7 @@ void Session::unlink (SourceList sources)
validate(sources);
// brute force : detach all given sources
for (auto it = sources.begin(); it != sources.end(); it++) {
for (auto it = sources.begin(); it != sources.end(); ++it) {
// this source is linked
if ( (*it)->mixingGroup() != nullptr ) {
// askt its group to detach it
@@ -379,17 +407,39 @@ void Session::unlink (SourceList sources)
}
}
void Session::addNote(SessionNote note)
{
notes_.push_back( note );
}
std::list<SessionNote>::iterator Session::beginNotes ()
{
return notes_.begin();
}
std::list<SessionNote>::iterator Session::endNotes ()
{
return notes_.end();
}
std::list<SessionNote>::iterator Session::deleteNote (std::list<SessionNote>::iterator n)
{
if (n != notes_.end())
return notes_.erase(n);
return notes_.end();
}
std::list<SourceList> Session::getMixingGroups () const
{
std::list<SourceList> lmg;
for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); group_it++)
for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); ++group_it)
lmg.push_back( (*group_it)->getCopy() );
return lmg;
}
std::list<MixingGroup *>::iterator Session::deleteMixingGroup (std::list<MixingGroup *>::iterator g)
{
if (g != mixing_groups_.end()) {
@@ -419,7 +469,6 @@ void Session::unlock()
access_.unlock();
}
void Session::validate (SourceList &sources)
{
// verify that all sources given are valid in the sesion

View File

@@ -3,12 +3,33 @@
#include <mutex>
#include "SourceList.h"
#include "RenderView.h"
#include "Source.h"
namespace tinyxml2 {
class XMLDocument;
}
class FrameGrabber;
class MixingGroup;
struct SessionNote
{
std::string label;
std::string text;
bool large;
int stick;
glm::vec2 pos;
glm::vec2 size;
SessionNote(const std::string &t = "", bool l = false, int s = 0);
};
struct SessionSnapshots {
tinyxml2::XMLDocument *xmlDoc_;
std::list<uint64_t> keys_;
};
class Session
{
public:
@@ -45,6 +66,8 @@ public:
SourceList::iterator find (uint64_t id);
SourceIdList getIdList() const;
std::list<std::string> getNameList(uint64_t exceptid=0) const;
SourceList::iterator at (int index);
int index (SourceList::iterator it) const;
void move (int current_index, int target_index);
@@ -62,6 +85,9 @@ public:
// get frame result of render
inline FrameBuffer *frame () const { return render_.frame(); }
// get thumbnail image
inline FrameBufferImage *thumbnail () { return render_.thumbnail(); }
// configure rendering resolution
void setResolution (glm::vec3 resolution, bool useAlpha = false);
@@ -76,6 +102,12 @@ public:
void setFilename (const std::string &filename) { filename_ = filename; }
std::string filename () const { return filename_; }
// get the list of notes
void addNote(SessionNote note = SessionNote());
std::list<SessionNote>::iterator beginNotes ();
std::list<SessionNote>::iterator endNotes ();
std::list<SessionNote>::iterator deleteNote (std::list<SessionNote>::iterator n);
// get the list of sources in mixing groups
std::list<SourceList> getMixingGroups () const;
// returns true if something can be done to create a mixing group
@@ -91,23 +123,27 @@ public:
std::list<MixingGroup *>::iterator endMixingGroup ();
std::list<MixingGroup *>::iterator deleteMixingGroup (std::list<MixingGroup *>::iterator g);
// snapshots
SessionSnapshots * const snapshots () { return &snapshots_; }
// lock and unlock access (e.g. while saving)
void lock ();
void unlock ();
protected:
bool active_;
RenderView render_;
std::string filename_;
Source *failedSource_;
SourceList sources_;
void validate(SourceList &sources);
std::list<SessionNote> notes_;
std::list<MixingGroup *> mixing_groups_;
std::map<View::Mode, Group*> config_;
bool active_;
std::list<FrameGrabber *> grabbers_;
SessionSnapshots snapshots_;
float fading_target_;
std::mutex access_;
};

View File

@@ -1,4 +1,4 @@
#include "SessionCreator.h"
#include <sstream>
#include "Log.h"
#include "defines.h"
@@ -12,43 +12,49 @@
#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 <tinyxml2.h>
#include "tinyxml2Toolkit.h"
using namespace tinyxml2;
#include "SessionCreator.h"
std::string SessionCreator::info(const std::string& filename)
SessionInformation SessionCreator::info(const std::string& filename)
{
std::string ret = "";
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)) {
XMLElement *header = doc.FirstChildElement(APP_NAME);
if (header != nullptr && header->Attribute("date") != 0) {
const XMLElement *header = doc.FirstChildElement(APP_NAME);
if (header != nullptr) {
int s = header->IntAttribute("size");
ret = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
ret.description = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
const char *att_string = header->Attribute("resolution");
if (att_string)
ret += std::string( att_string ) + "\n";
ret.description += std::string( att_string ) + "\n";
att_string = header->Attribute("date");
if (att_string) {
std::string date( att_string );
ret += date.substr(6,2) + "/" + date.substr(4,2) + "/" + date.substr(0,4) + " @ ";
ret += date.substr(8,2) + ":" + date.substr(10,2);
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);
}
}
}
@@ -97,9 +103,15 @@ void SessionCreator::load(const std::string& filename)
// create groups
std::list< SourceList > groups = getMixingGroups();
for (auto group_it = groups.begin(); group_it != groups.end(); group_it++)
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") );
// all good
session_->setFilename(filename);
}
@@ -107,7 +119,7 @@ void SessionCreator::load(const std::string& filename)
void SessionCreator::loadConfig(XMLElement *viewsNode)
{
if (viewsNode != nullptr) {
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));
@@ -117,6 +129,48 @@ void SessionCreator::loadConfig(XMLElement *viewsNode)
}
}
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) N.text = std::string ( contentNode->GetText() );
session_->addNote(N);
}
}
}
SessionLoader::SessionLoader(): Visitor(),
session_(nullptr), xmlCurrent_(nullptr), recursion_(0)
{
@@ -146,10 +200,10 @@ 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++)
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++ ) {
for (auto sit = (*git).begin(); sit != (*git).end(); ++sit ) {
if (sources_id_.count(*sit) > 0)
new_sources.push_back( sources_id_.at(*sit) );
}
@@ -194,25 +248,28 @@ void SessionLoader::load(XMLElement *sessionNode)
if (!pType)
continue;
if ( std::string(pType) == "MediaSource") {
load_source = new MediaSource;
load_source = new MediaSource(id_xml_);
}
else if ( std::string(pType) == "SessionSource") {
load_source = new SessionFileSource;
load_source = new SessionFileSource(id_xml_);
}
else if ( std::string(pType) == "GroupSource") {
load_source = new SessionGroupSource;
load_source = new SessionGroupSource(id_xml_);
}
else if ( std::string(pType) == "RenderSource") {
load_source = new RenderSource;
load_source = new RenderSource(id_xml_);
}
else if ( std::string(pType) == "PatternSource") {
load_source = new PatternSource;
load_source = new PatternSource(id_xml_);
}
else if ( std::string(pType) == "DeviceSource") {
load_source = new DeviceSource;
load_source = new DeviceSource(id_xml_);
}
else if ( std::string(pType) == "NetworkSource") {
load_source = new NetworkSource;
load_source = new NetworkSource(id_xml_);
}
else if ( std::string(pType) == "MultiFileSource") {
load_source = new MultiFileSource(id_xml_);
}
// skip failed (including clones)
@@ -255,12 +312,17 @@ void SessionLoader::load(XMLElement *sessionNode)
// clone from given origin
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
if (originNode) {
std::string sourcename = std::string ( originNode->GetText() );
SourceList::iterator origin = session_->find(sourcename);
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();
Source *clone_source = (*origin)->clone(id_xml_);
// add source to session
session_->addSource(clone_source);
@@ -291,11 +353,12 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
Source *load_source = nullptr;
bool is_clone = false;
SourceList::iterator sit = session_->end();
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) {
uint64_t id__ = 0;
xmlCurrent_->QueryUnsigned64Attribute("id", &id__);
sit = session_->find(id__);
}
@@ -305,35 +368,43 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
const char *pType = xmlCurrent_->Attribute("type");
if (pType) {
if ( std::string(pType) == "MediaSource") {
load_source = new MediaSource;
load_source = new MediaSource(id__);
}
else if ( std::string(pType) == "SessionSource") {
load_source = new SessionFileSource;
load_source = new SessionFileSource(id__);
}
else if ( std::string(pType) == "GroupSource") {
load_source = new SessionGroupSource;
load_source = new SessionGroupSource(id__);
}
else if ( std::string(pType) == "RenderSource") {
load_source = new RenderSource;
load_source = new RenderSource(id__);
}
else if ( std::string(pType) == "PatternSource") {
load_source = new PatternSource;
load_source = new PatternSource(id__);
}
else if ( std::string(pType) == "DeviceSource") {
load_source = new DeviceSource;
load_source = new DeviceSource(id__);
}
else if ( std::string(pType) == "NetworkSource") {
load_source = new 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) {
std::string sourcename = std::string ( originNode->GetText() );
SourceList::iterator origin = session_->find(sourcename);
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();
load_source = (*origin)->clone(id__);
}
}
}
@@ -356,7 +427,7 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
}
bool SessionLoader::isClipboard(std::string clipboard)
bool SessionLoader::isClipboard(const std::string &clipboard)
{
if (clipboard.size() > 6 && clipboard.substr(0, 6) == "<" APP_NAME )
return true;
@@ -364,7 +435,7 @@ bool SessionLoader::isClipboard(std::string clipboard)
return false;
}
tinyxml2::XMLElement* SessionLoader::firstSourceElement(std::string clipboard, XMLDocument &xmlDoc)
tinyxml2::XMLElement* SessionLoader::firstSourceElement(const std::string &clipboard, XMLDocument &xmlDoc)
{
tinyxml2::XMLElement* sourceNode = nullptr;
@@ -385,7 +456,7 @@ tinyxml2::XMLElement* SessionLoader::firstSourceElement(std::string clipboard, X
return sourceNode;
}
void SessionLoader::applyImageProcessing(const Source &s, std::string clipboard)
void SessionLoader::applyImageProcessing(const Source &s, const std::string &clipboard)
{
if ( !isClipboard(clipboard) )
return;
@@ -449,28 +520,85 @@ void SessionLoader::applyImageProcessing(const Source &s, std::string clipboard)
//// s.processingShader()->accept(loader);
//}
void SessionLoader::XMLToNode(tinyxml2::XMLElement *xml, Node &n)
void SessionLoader::XMLToNode(const tinyxml2::XMLElement *xml, Node &n)
{
if (xml != nullptr){
XMLElement *node = xml->FirstChildElement("Node");
const XMLElement *node = xml->FirstChildElement("Node");
if ( !node || std::string(node->Name()).find("Node") == std::string::npos )
return;
XMLElement *scaleNode = node->FirstChildElement("scale");
const XMLElement *scaleNode = node->FirstChildElement("scale");
if (scaleNode)
tinyxml2::XMLElementToGLM( scaleNode->FirstChildElement("vec3"), n.scale_);
XMLElement *translationNode = node->FirstChildElement("translation");
const XMLElement *translationNode = node->FirstChildElement("translation");
if (translationNode)
tinyxml2::XMLElementToGLM( translationNode->FirstChildElement("vec3"), n.translation_);
XMLElement *rotationNode = node->FirstChildElement("rotation");
const XMLElement *rotationNode = node->FirstChildElement("rotation");
if (rotationNode)
tinyxml2::XMLElementToGLM( rotationNode->FirstChildElement("vec3"), n.rotation_);
XMLElement *cropNode = node->FirstChildElement("crop");
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);
@@ -634,35 +762,15 @@ void SessionLoader::visit (Source& s)
}
xmlCurrent_ = sourceNode->FirstChildElement("Blending");
if (xmlCurrent_) s.blendingShader()->accept(*this);
if (xmlCurrent_)
s.blendingShader()->accept(*this);
xmlCurrent_ = sourceNode->FirstChildElement("Mask");
if (xmlCurrent_) {
// read the mask shader attributes
s.maskShader()->accept(*this);
// if there is an Image mask stored
XMLElement* imageNode = xmlCurrent_->FirstChildElement("Image");
if (imageNode) {
// if there is an internal array of data
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
s.setMask(new FrameBufferImage(jpgimg));
// free temporary buffer
if (jpgimg.buffer)
free(jpgimg.buffer);
}
}
}
// set the mask from jpeg
s.setMask( SessionLoader::XMLToImage(xmlCurrent_) );
}
xmlCurrent_ = sourceNode->FirstChildElement("ImageProcessing");
@@ -778,27 +886,48 @@ void SessionLoader::visit (NetworkSource& s)
s.setConnection(connect);
}
// dirty hack wich can be useful ?
//class DummySource : public Source
//{
// friend class SessionLoader;
//public:
// uint texture() const override { return 0; }
// bool failed() const override { return true; }
// void accept (Visitor& v) override { Source::accept(v); }
//protected:
// DummySource() : Source() {}
// void init() override {}
//};
void SessionLoader::visit (MultiFileSource& s)
{
XMLElement* seq = xmlCurrent_->FirstChildElement("Sequence");
//Source *SessionLoader::createDummy(tinyxml2::XMLElement *sourceNode)
//{
// SessionLoader loader;
// loader.xmlCurrent_ = sourceNode;
// DummySource *dum = new DummySource;
// dum->accept(loader);
// return dum;
//}
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);
}
}

View File

@@ -8,6 +8,7 @@
#include "SourceList.h"
class Session;
class FrameBufferImage;
class SessionLoader : public Visitor {
@@ -29,10 +30,10 @@ public:
} Mode;
Source *createSource(tinyxml2::XMLElement *sourceNode, Mode mode = CLONE);
static bool isClipboard(std::string clipboard);
static tinyxml2::XMLElement* firstSourceElement(std::string clipboard, tinyxml2::XMLDocument &xmlDoc);
static void applyImageProcessing(const Source &s, std::string clipboard);
//TODO static void applyMask(const Source &s, std::string clipboard);
static bool isClipboard(const std::string &clipboard);
static tinyxml2::XMLElement* firstSourceElement(const std::string &clipboard, tinyxml2::XMLDocument &xmlDoc);
static void applyImageProcessing(const Source &s, const std::string &clipboard);
//TODO static void applyMask(const Source &s, const std::string &clipboard);
// Elements of Scene
void visit (Node& n) override;
@@ -57,6 +58,11 @@ public:
void visit (PatternSource& s) override;
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
void visit (MultiFileSource& s) override;
static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n);
static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s);
static FrameBufferImage *XMLToImage(const tinyxml2::XMLElement *xml);
protected:
// result created session
@@ -70,7 +76,15 @@ protected:
// list of groups (lists of xml source id)
std::list< SourceIdList > groups_sources_id_;
static void XMLToNode(tinyxml2::XMLElement *xml, Node &n);
};
struct SessionInformation {
std::string description;
FrameBufferImage *thumbnail;
SessionInformation() {
description = "";
thumbnail = nullptr;
}
};
class SessionCreator : public SessionLoader {
@@ -78,13 +92,15 @@ class SessionCreator : public SessionLoader {
tinyxml2::XMLDocument xmlDoc_;
void loadConfig(tinyxml2::XMLElement *viewsNode);
void loadNotes(tinyxml2::XMLElement *notesNode);
void loadSnapshots(tinyxml2::XMLElement *snapshotNode);
public:
SessionCreator(int recursion = 0);
void load(const std::string& filename);
static std::string info(const std::string& filename);
static SessionInformation info(const std::string& filename);
};
#endif // SESSIONCREATOR_H

View File

@@ -1,6 +1,7 @@
#include <glm/gtc/matrix_transform.hpp>
#include <thread>
#include <chrono>
#include <algorithm>
#include "SessionSource.h"
@@ -17,7 +18,7 @@
#include "Mixer.h"
SessionSource::SessionSource() : Source(), failed_(false)
SessionSource::SessionSource(uint64_t id) : Source(id), failed_(false)
{
session_ = new Session;
}
@@ -37,8 +38,8 @@ Session *SessionSource::detach()
// work on a new session
session_ = new Session;
// make disabled
initialized_ = false;
// un-ready
ready_ = false;
// ask to delete me
failed_ = true;
@@ -90,7 +91,7 @@ void SessionSource::update(float dt)
}
SessionFileSource::SessionFileSource() : SessionSource(), path_("")
SessionFileSource::SessionFileSource(uint64_t id) : SessionSource(id), path_(""), initialized_(false), wait_for_sources_(false)
{
// specific node for transition view
groups_[View::TRANSITION]->visible_ = false;
@@ -124,7 +125,6 @@ SessionFileSource::SessionFileSource() : SessionSource(), path_("")
symbol_ = new Symbol(Symbol::SESSION, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
wait_for_sources_ = false;
}
void SessionFileSource::load(const std::string &p, uint recursion)
@@ -148,6 +148,9 @@ void SessionFileSource::load(const std::string &p, uint recursion)
sessionLoader_ = std::async(std::launch::async, Session::load, path_, recursion);
Log::Notify("Opening %s", p.c_str());
}
// will be ready after init and one frame rendered
ready_ = false;
}
void SessionFileSource::init()
@@ -163,26 +166,18 @@ void SessionFileSource::init()
}
else {
session_->update(dt_);
if (wait_for_sources_) {
// force update of of all sources
active_ = true;
touch();
// check that every source is ready..
bool ready = true;
for (SourceList::iterator iter = session_->begin(); iter != session_->end(); ++iter)
{
// interrupt if any source is NOT ready
if ( !(*iter)->ready() ){
ready = false;
break;
}
}
// update to draw framebuffer
session_->update(dt_);
// if all sources are ready, done with initialization!
if (ready) {
auto unintitializedsource = std::find_if_not(session_->begin(), session_->end(), Source::isInitialized);
if (unintitializedsource == session_->end()) {
// done init
wait_for_sources_ = false;
initialized_ = true;
@@ -223,7 +218,20 @@ void SessionFileSource::init()
overlays_[View::TRANSITION]->detach(loader);
delete loader;
// deep update to reorder
View::need_deep_update_++;
++View::need_deep_update_;
}
}
void SessionFileSource::render()
{
if ( !initialized_ )
init();
else {
// render the media player into frame buffer
renderbuffer_->begin();
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
renderbuffer_->end();
ready_ = true;
}
}
@@ -235,7 +243,7 @@ void SessionFileSource::accept(Visitor& v)
}
SessionGroupSource::SessionGroupSource() : SessionSource(), resolution_(glm::vec3(0.f))
SessionGroupSource::SessionGroupSource(uint64_t id) : SessionSource(id), resolution_(glm::vec3(0.f))
{
// // redo frame for layers view
// frames_[View::LAYER]->clear();
@@ -287,10 +295,9 @@ void SessionGroupSource::init()
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
++View::need_deep_update_;
// done init
initialized_ = true;
Log::Info("Source Group (%d x %d).", int(renderbuffer->resolution().x), int(renderbuffer->resolution().y) );
}
}
@@ -316,19 +323,16 @@ void SessionGroupSource::accept(Visitor& v)
v.visit(*this);
}
RenderSource::RenderSource() : Source(), session_(nullptr)
RenderSource::RenderSource(uint64_t id) : Source(id), session_(nullptr)
{
// set symbol
symbol_ = new Symbol(Symbol::RENDER, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
bool RenderSource::failed() const
{
if (initialized_ && session_!=nullptr)
if ( mode_ > Source::UNINITIALIZED && session_!=nullptr )
return renderbuffer_->resolution() != session_->frame()->resolution();
return false;
@@ -358,10 +362,9 @@ void RenderSource::init()
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
++View::need_deep_update_;
// done init
initialized_ = true;
Log::Info("Source Render linked to session (%d x %d).", int(fb->resolution().x), int(fb->resolution().y) );
}
}
@@ -369,7 +372,7 @@ void RenderSource::init()
glm::vec3 RenderSource::resolution() const
{
if (initialized_)
if (mode_ > Source::UNINITIALIZED)
return renderbuffer_->resolution();
else if (session_ && session_->frame())
return session_->frame()->resolution();

View File

@@ -8,7 +8,7 @@
class SessionSource : public Source
{
public:
SessionSource();
SessionSource(uint64_t id = 0);
virtual ~SessionSource();
// implementation of source API
@@ -29,10 +29,11 @@ protected:
class SessionFileSource : public SessionSource
{
public:
SessionFileSource();
SessionFileSource(uint64_t id = 0);
// implementation of source API
void accept (Visitor& v) override;
void render() override;
// SessionFile Source specific interface
void load(const std::string &p = "", uint recursion = 0);
@@ -45,14 +46,15 @@ protected:
void init() override;
std::string path_;
std::atomic<bool> wait_for_sources_;
bool initialized_;
bool wait_for_sources_;
std::future<Session *> sessionLoader_;
};
class SessionGroupSource : public SessionSource
{
public:
SessionGroupSource();
SessionGroupSource(uint64_t id = 0);
// implementation of source API
void accept (Visitor& v) override;
@@ -75,7 +77,7 @@ protected:
class RenderSource : public Source
{
public:
RenderSource();
RenderSource(uint64_t id = 0);
// implementation of source API
bool failed () const override;

View File

@@ -11,11 +11,13 @@
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "MultiFileSource.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"
#include "MixingGroup.h"
#include "SystemToolkit.h"
#include "ActionManager.h"
#include <iostream>
#include <locale>
@@ -44,10 +46,17 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session)
XMLElement *sessionNode = xmlDoc.NewElement("Session");
xmlDoc.InsertEndChild(sessionNode);
SessionVisitor sv(&xmlDoc, sessionNode);
for (auto iter = session->begin(); iter != session->end(); iter++, sv.setRoot(sessionNode) )
for (auto iter = session->begin(); iter != session->end(); ++iter, sv.setRoot(sessionNode) )
// source visitor
(*iter)->accept(sv);
// get the thumbnail
FrameBufferImage *thumbnail = session->thumbnail();
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, &xmlDoc);
if (imageelement)
sessionNode->InsertEndChild(imageelement);
delete thumbnail;
// 2. config of views
XMLElement *views = xmlDoc.NewElement("Views");
xmlDoc.InsertEndChild(views);
@@ -73,6 +82,35 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session)
views->InsertEndChild(render);
}
// 3. snapshots
XMLElement *snapshots = xmlDoc.NewElement("Snapshots");
const XMLElement* N = session->snapshots()->xmlDoc_->FirstChildElement();
for( ; N ; N=N->NextSiblingElement())
snapshots->InsertEndChild( N->DeepClone( &xmlDoc ));
xmlDoc.InsertEndChild(snapshots);
// 4. optional notes
XMLElement *notes = xmlDoc.NewElement("Notes");
xmlDoc.InsertEndChild(notes);
for (auto nit = session->beginNotes(); nit != session->endNotes(); ++nit) {
XMLElement *note = xmlDoc.NewElement( "Note" );
note->SetAttribute("large", (*nit).large );
note->SetAttribute("stick", (*nit).stick );
XMLElement *pos = xmlDoc.NewElement("pos");
pos->InsertEndChild( XMLElementFromGLM(&xmlDoc, (*nit).pos) );
note->InsertEndChild(pos);
XMLElement *size = xmlDoc.NewElement("size");
size->InsertEndChild( XMLElementFromGLM(&xmlDoc, (*nit).size) );
note->InsertEndChild(size);
XMLElement *content = xmlDoc.NewElement("text");
XMLText *text = xmlDoc.NewText( (*nit).text.c_str() );
content->InsertEndChild( text );
note->InsertEndChild(content);
notes->InsertEndChild(note);
}
// save file to disk
return ( XMLSaveDoc(&xmlDoc, filename) );
}
@@ -90,7 +128,7 @@ SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc,
xmlDoc_ = doc;
}
tinyxml2::XMLElement *SessionVisitor::NodeToXML(Node &n, tinyxml2::XMLDocument *doc)
XMLElement *SessionVisitor::NodeToXML(const Node &n, XMLDocument *doc)
{
XMLElement *newelement = doc->NewElement("Node");
newelement->SetAttribute("visible", n.visible_);
@@ -115,6 +153,31 @@ tinyxml2::XMLElement *SessionVisitor::NodeToXML(Node &n, tinyxml2::XMLDocument *
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_);
@@ -151,7 +214,7 @@ void SessionVisitor::visit(Switch &n)
if (recursive_) {
// loop over members of the group
XMLElement *group = xmlCurrent_;
for(uint i = 0; i < n.numChildren(); i++) {
for(uint i = 0; i < n.numChildren(); ++i) {
n.child(i)->accept(*this);
// revert to group as current
xmlCurrent_ = group;
@@ -225,7 +288,7 @@ void SessionVisitor::visit(MediaPlayer &n)
// gaps in timeline
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
TimeIntervalSet gaps = n.timeline()->gaps();
for( auto it = gaps.begin(); it!= gaps.end(); it++) {
for( auto it = gaps.begin(); it!= gaps.end(); ++it) {
XMLElement *g = xmlDoc_->NewElement("Interval");
g->SetAttribute("begin", (*it).begin);
g->SetAttribute("end", (*it).end);
@@ -419,24 +482,9 @@ void SessionVisitor::visit (Source& s)
// if we are saving a pain mask
if (s.maskShader()->mode == MaskShader::PAINT) {
// get the mask previously stored
FrameBufferImage *img = s.getMask();
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(xmlDoc_, 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
XMLElement *imageelement = xmlDoc_->NewElement("Image");
imageelement->InsertEndChild(array);
xmlCurrent_->InsertEndChild(imageelement);
}
}
}
XMLElement *imageelement = SessionVisitor::ImageToXML(s.getMask(), xmlDoc_);
if (imageelement)
xmlCurrent_->InsertEndChild(imageelement);
}
xmlCurrent_ = xmlDoc_->NewElement( "ImageProcessing" );
@@ -487,7 +535,7 @@ void SessionVisitor::visit (SessionGroupSource& s)
XMLElement *sessionNode = xmlDoc_->NewElement("Session");
xmlCurrent_->InsertEndChild(sessionNode);
for (auto iter = se->begin(); iter != se->end(); iter++){
for (auto iter = se->begin(); iter != se->end(); ++iter){
setRoot(sessionNode);
(*iter)->accept(*this);
}
@@ -504,6 +552,7 @@ void SessionVisitor::visit (CloneSource& s)
xmlCurrent_->SetAttribute("type", "CloneSource");
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 );
@@ -512,11 +561,14 @@ void SessionVisitor::visit (CloneSource& s)
void SessionVisitor::visit (PatternSource& s)
{
xmlCurrent_->SetAttribute("type", "PatternSource");
xmlCurrent_->SetAttribute("pattern", s.pattern()->type() );
XMLElement *resolution = xmlDoc_->NewElement("resolution");
resolution->InsertEndChild( XMLElementFromGLM(xmlDoc_, s.pattern()->resolution() ) );
xmlCurrent_->InsertEndChild(resolution);
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)
@@ -535,14 +587,36 @@ void SessionVisitor::visit (MixingGroup& g)
{
xmlCurrent_->SetAttribute("size", g.size());
for (auto it = g.begin(); it != g.end(); it++) {
for (auto it = g.begin(); it != g.end(); ++it) {
XMLElement *sour = xmlDoc_->NewElement("source");
sour->SetAttribute("id", (*it)->id());
xmlCurrent_->InsertEndChild(sour);
}
}
std::string SessionVisitor::getClipboard(SourceList list)
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());
XMLText *location = xmlDoc_->NewText( s.sequence().location.c_str() );
sequence->InsertEndChild( location );
xmlCurrent_->InsertEndChild(sequence);
}
std::string SessionVisitor::getClipboard(const SourceList &list)
{
std::string x = "";
@@ -557,7 +631,7 @@ std::string SessionVisitor::getClipboard(SourceList list)
// fill doc by visiting sources
SourceList selection_clones_;
SessionVisitor sv(&xmlDoc, selectionNode);
for (auto iter = list.begin(); iter != list.end(); iter++, sv.setRoot(selectionNode) ){
for (auto iter = list.begin(); iter != list.end(); ++iter, sv.setRoot(selectionNode) ){
// start with clones
CloneSource *clone = dynamic_cast<CloneSource *>(*iter);
if (clone)
@@ -566,7 +640,7 @@ std::string SessionVisitor::getClipboard(SourceList list)
selection_clones_.push_back(*iter);
}
// add others in front
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); iter++, sv.setRoot(selectionNode) ){
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); ++iter, sv.setRoot(selectionNode) ){
(*iter)->accept(sv);
}
@@ -579,7 +653,7 @@ std::string SessionVisitor::getClipboard(SourceList list)
return x;
}
std::string SessionVisitor::getClipboard(Source *s)
std::string SessionVisitor::getClipboard(Source * const s)
{
std::string x = "";
@@ -603,7 +677,7 @@ std::string SessionVisitor::getClipboard(Source *s)
return x;
}
std::string SessionVisitor::getClipboard(ImageProcessingShader *s)
std::string SessionVisitor::getClipboard(ImageProcessingShader * const s)
{
std::string x = "";

View File

@@ -6,6 +6,7 @@
#include "SourceList.h"
class Session;
class FrameBufferImage;
class SessionVisitor : public Visitor {
@@ -22,31 +23,31 @@ public:
static bool saveSession(const std::string& filename, Session *session);
static std::string getClipboard(SourceList list);
static std::string getClipboard(Source *s);
static std::string getClipboard(ImageProcessingShader *s);
static std::string getClipboard(const SourceList &list);
static std::string getClipboard(Source * const s);
static std::string getClipboard(ImageProcessingShader * const s);
// Elements of Scene
void visit(Scene& n) override;
void visit(Node& n) override;
void visit(Group& n) override;
void visit(Switch& n) override;
void visit(Primitive& n) override;
void visit(Surface&) override;
void visit(ImageSurface& n) override;
void visit(MediaSurface& n) override;
void visit(FrameBufferSurface&) override;
void visit(LineStrip& n) override;
void visit(LineSquare&) override;
void visit(Mesh& n) override;
void visit(Frame& n) override;
void visit (Scene& n) override;
void visit (Node& n) override;
void visit (Group& n) override;
void visit (Switch& n) override;
void visit (Primitive& n) override;
void visit (Surface&) override;
void visit (ImageSurface& n) override;
void visit (MediaSurface& n) override;
void visit (FrameBufferSurface&) override;
void visit (LineStrip& n) override;
void visit (LineSquare&) override;
void visit (Mesh& n) override;
void visit (Frame& n) override;
// Elements with attributes
void visit(MediaPlayer& n) override;
void visit(Shader& n) override;
void visit(ImageShader& n) override;
void visit(MaskShader& n) override;
void visit(ImageProcessingShader& n) override;
void visit (MediaPlayer& n) override;
void visit (Shader& n) override;
void visit (ImageShader& n) override;
void visit (MaskShader& n) override;
void visit (ImageProcessingShader& n) override;
// Sources
void visit (Source& s) override;
@@ -59,9 +60,10 @@ public:
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
void visit (MixingGroup& s) override;
void visit (MultiFileSource& s) override;
protected:
static tinyxml2::XMLElement *NodeToXML(Node &n, tinyxml2::XMLDocument *doc);
static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc);
static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc);
};
#endif // XMLVISITOR_H

View File

@@ -63,11 +63,12 @@ void Settings::Save()
XMLElement *applicationNode = xmlDoc.NewElement( "Application" );
applicationNode->SetAttribute("scale", application.scale);
applicationNode->SetAttribute("accent_color", application.accent_color);
applicationNode->SetAttribute("pannel_stick", application.pannel_stick);
applicationNode->SetAttribute("smooth_transition", application.smooth_transition);
applicationNode->SetAttribute("smooth_snapshot", application.smooth_snapshot);
applicationNode->SetAttribute("smooth_cursor", application.smooth_cursor);
applicationNode->SetAttribute("action_history_follow_view", application.action_history_follow_view);
applicationNode->SetAttribute("accept_connections", application.accept_connections);
applicationNode->SetAttribute("pannel_history_mode", application.pannel_history_mode);
pRoot->InsertEndChild(applicationNode);
// Widgets
@@ -175,7 +176,7 @@ void Settings::Save()
recentsession->SetAttribute("autosave", application.recentSessions.save_on_exit);
recentsession->SetAttribute("valid", application.recentSessions.front_is_valid);
for(auto it = application.recentSessions.filenames.begin();
it != application.recentSessions.filenames.end(); it++) {
it != application.recentSessions.filenames.end(); ++it) {
XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text );
@@ -185,7 +186,7 @@ void Settings::Save()
XMLElement *recentfolder = xmlDoc.NewElement( "Folder" );
for(auto it = application.recentFolders.filenames.begin();
it != application.recentFolders.filenames.end(); it++) {
it != application.recentFolders.filenames.end(); ++it) {
XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text );
@@ -196,7 +197,7 @@ void Settings::Save()
XMLElement *recentmedia = xmlDoc.NewElement( "Import" );
recentmedia->SetAttribute("path", application.recentImport.path.c_str());
for(auto it = application.recentImport.filenames.begin();
it != application.recentImport.filenames.end(); it++) {
it != application.recentImport.filenames.end(); ++it) {
XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text );
@@ -254,11 +255,12 @@ void Settings::Load()
if (applicationNode != nullptr) {
applicationNode->QueryFloatAttribute("scale", &application.scale);
applicationNode->QueryIntAttribute("accent_color", &application.accent_color);
applicationNode->QueryBoolAttribute("pannel_stick", &application.pannel_stick);
applicationNode->QueryBoolAttribute("smooth_transition", &application.smooth_transition);
applicationNode->QueryBoolAttribute("smooth_snapshot", &application.smooth_snapshot);
applicationNode->QueryBoolAttribute("smooth_cursor", &application.smooth_cursor);
applicationNode->QueryBoolAttribute("action_history_follow_view", &application.action_history_follow_view);
applicationNode->QueryBoolAttribute("accept_connections", &application.accept_connections);
applicationNode->QueryIntAttribute("pannel_history_mode", &application.pannel_history_mode);
}
// Widgets
@@ -453,6 +455,39 @@ void Settings::Load()
}
void Settings::History::push(const string &filename)
{
if (filename.empty()) {
front_is_valid = false;
return;
}
filenames.remove(filename);
filenames.push_front(filename);
if (filenames.size() > MAX_RECENT_HISTORY)
filenames.pop_back();
front_is_valid = true;
changed = true;
}
void Settings::History::remove(const std::string &filename)
{
if (filename.empty())
return;
if (filenames.front() == filename)
front_is_valid = false;
filenames.remove(filename);
changed = true;
}
void Settings::History::validate()
{
for (auto fit = filenames.begin(); fit != filenames.end();) {
if ( SystemToolkit::file_exists( *fit ))
++fit;
else
fit = filenames.erase(fit);
}
}
void Settings::Lock()
{

View File

@@ -93,26 +93,9 @@ struct History
save_on_exit = false;
changed = false;
}
void push(const std::string &filename) {
if (filename.empty()) {
front_is_valid = false;
return;
}
filenames.remove(filename);
filenames.push_front(filename);
if (filenames.size() > MAX_RECENT_HISTORY)
filenames.pop_back();
front_is_valid = true;
changed = true;
}
void remove(const std::string &filename) {
if (filename.empty())
return;
if (filenames.front() == filename)
front_is_valid = false;
filenames.remove(filename);
changed = true;
}
void push(const std::string &filename);
void remove(const std::string &filename);
void validate();
};
struct TransitionConfig
@@ -178,14 +161,15 @@ struct Application
// Global settings Application interface
float scale;
int accent_color;
bool pannel_stick;
bool smooth_snapshot;
bool smooth_transition;
bool smooth_cursor;
bool action_history_follow_view;
int pannel_history_mode;
// connection settings
bool accept_connections;
// std::map<int, std::string> instance_names;
// Settings of widgets
WidgetsConfig widget;
@@ -221,11 +205,12 @@ struct Application
Application() : fresh_start(false), instance_id(0), name(APP_NAME), executable(APP_NAME) {
scale = 1.f;
accent_color = 0;
pannel_stick = false;
smooth_transition = true;
smooth_transition = false;
smooth_snapshot = false;
smooth_cursor = false;
action_history_follow_view = false;
accept_connections = false;
pannel_history_mode = 0;
current_view = 1;
current_workspace= 1;
brush = glm::vec3(0.5f, 0.1f, 0.f);

View File

@@ -14,7 +14,7 @@
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "GlmToolkit.h"
#include "BaseToolkit.h"
#include <glm/gtc/type_ptr.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/string_cast.hpp>
@@ -203,14 +203,14 @@ bool Shader::force_blending_opacity = false;
Shader::Shader() : blending(BLEND_OPACITY)
{
// create unique id
id_ = GlmToolkit::uniqueId();
id_ = BaseToolkit::uniqueId();
program_ = &simpleShadingProgram;
reset();
}
void Shader::operator = (const Shader &S )
void Shader::copy(Shader const& S)
{
color = S.color;
blending = S.blending;

View File

@@ -50,8 +50,7 @@ public:
virtual void use();
virtual void reset();
virtual void accept(Visitor& v);
void operator = (const Shader &D );
void copy(Shader const& S);
glm::mat4 projection;
glm::mat4 modelview;

View File

@@ -11,16 +11,97 @@
#include "SearchVisitor.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "BaseToolkit.h"
#include "SystemToolkit.h"
#include "Log.h"
#include "MixingGroup.h"
#include "Source.h"
Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_(false), need_update_(true), workspace_(STAGE)
SourceCore::SourceCore()
{
// default nodes
groups_[View::RENDERING] = new Group;
groups_[View::RENDERING]->visible_ = false;
groups_[View::MIXING] = new Group;
groups_[View::MIXING]->visible_ = false;
groups_[View::GEOMETRY] = new Group;
groups_[View::GEOMETRY]->visible_ = false;
groups_[View::LAYER] = new Group;
groups_[View::LAYER]->visible_ = false;
groups_[View::TEXTURE] = new Group;
groups_[View::TEXTURE]->visible_ = false;
groups_[View::TRANSITION] = new Group;
// temp node
stored_status_ = new Group;
// filtered image shader (with texturing and processing) for rendering
processingshader_ = new ImageProcessingShader;
// default rendering with image processing disabled
renderingshader_ = static_cast<Shader *>(new ImageShader);
}
SourceCore::SourceCore(SourceCore const& other) : SourceCore()
{
copy(other);
}
SourceCore::~SourceCore()
{
// all groups and their children are deleted in the scene
// this deletes renderingshader_ (and all source-attached nodes
// e.g. rendersurface_, overlays, blendingshader_, etc.)
delete groups_[View::RENDERING];
delete groups_[View::MIXING];
delete groups_[View::GEOMETRY];
delete groups_[View::LAYER];
delete groups_[View::TEXTURE];
delete groups_[View::TRANSITION];
delete stored_status_;
// don't forget that the processing shader
// could be created but not used and not deleted above
if ( renderingshader_ != processingshader_ )
delete processingshader_;
groups_.clear();
}
void SourceCore::copy(SourceCore const& other)
{
// copy groups properties
// groups_[View::RENDERING]->copyTransform( other.group(View::RENDERING) );
groups_[View::MIXING]->copyTransform( other.group(View::MIXING) );
groups_[View::GEOMETRY]->copyTransform( other.group(View::GEOMETRY) );
groups_[View::LAYER]->copyTransform( other.group(View::LAYER) );
groups_[View::TEXTURE]->copyTransform( other.group(View::TEXTURE) );
groups_[View::TRANSITION]->copyTransform( other.group(View::TRANSITION) );
stored_status_->copyTransform( other.stored_status_ );
// copy shader properties
processingshader_->copy(*other.processingshader_);
renderingshader_->copy(*other.renderingshader_);
}
void SourceCore::store (View::Mode m)
{
stored_status_->copyTransform(groups_[m]);
}
SourceCore& SourceCore::operator= (SourceCore const& other)
{
if (this != &other) // no self assignment
copy(other);
return *this;
}
Source::Source(uint64_t id) : SourceCore(), id_(id), ready_(false), symbol_(nullptr),
active_(true), locked_(false), need_update_(true), dt_(0), workspace_(STAGE)
{
// create unique id
id_ = GlmToolkit::uniqueId();
if (id_ == 0)
id_ = BaseToolkit::uniqueId();
sprintf(initials_, "__");
name_ = "Source";
@@ -28,13 +109,7 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
// create groups and overlays for each view
// default rendering node
groups_[View::RENDERING] = new Group;
groups_[View::RENDERING]->visible_ = false;
// default mixing nodes
groups_[View::MIXING] = new Group;
groups_[View::MIXING]->visible_ = false;
groups_[View::MIXING]->scale_ = glm::vec3(0.15f, 0.15f, 1.f);
groups_[View::MIXING]->translation_ = glm::vec3(DEFAULT_MIXING_TRANSLATION, 0.f);
@@ -69,9 +144,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
groups_[View::MIXING]->attach(overlay_mixinggroup_);
// default geometry nodes
groups_[View::GEOMETRY] = new Group;
groups_[View::GEOMETRY]->visible_ = false;
frames_[View::GEOMETRY] = new Switch;
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
frame->translation_.z = 0.1;
@@ -122,8 +194,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
groups_[View::GEOMETRY]->attach(overlays_[View::GEOMETRY]);
// default layer nodes
groups_[View::LAYER] = new Group;
groups_[View::LAYER]->visible_ = false;
groups_[View::LAYER]->translation_.z = -1.f;
frames_[View::LAYER] = new Switch;
@@ -143,9 +213,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
groups_[View::LAYER]->attach(overlays_[View::LAYER]);
// default appearance node
groups_[View::TEXTURE] = new Group;
groups_[View::TEXTURE]->visible_ = false;
frames_[View::TEXTURE] = new Switch;
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
frame->translation_.z = 0.1;
@@ -186,9 +253,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::MENU]);
groups_[View::TEXTURE]->attach(overlays_[View::TEXTURE]);
// empty transition node
groups_[View::TRANSITION] = new Group;
// locker switch button : locked / unlocked icons
locker_ = new Switch;
lock_ = new Handles(Handles::LOCKED);
@@ -196,20 +260,12 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
unlock_ = new Handles(Handles::UNLOCKED);
locker_->attach(unlock_);
// create objects
stored_status_ = new Group;
// simple image shader (with texturing) for blending
blendingshader_ = new ImageShader;
// mask produced by dedicated shader
maskshader_ = new MaskShader;
masksurface_ = new Surface(maskshader_);
// filtered image shader (with texturing and processing) for rendering
processingshader_ = new ImageProcessingShader;
// default rendering with image processing enabled
renderingshader_ = static_cast<Shader *>(processingshader_);
// for drawing in mixing view
mixingshader_ = new ImageShader;
mixingshader_->stipple = 1.0;
@@ -238,12 +294,11 @@ Source::~Source()
links_.front()->disconnect();
// inform clones that they lost their origin
for (auto it = clones_.begin(); it != clones_.end(); it++)
for (auto it = clones_.begin(); it != clones_.end(); ++it)
(*it)->detach();
clones_.clear();
// delete objects
delete stored_status_;
if (renderbuffer_)
delete renderbuffer_;
if (maskbuffer_)
@@ -253,30 +308,17 @@ Source::~Source()
if (masksurface_)
delete masksurface_; // deletes maskshader_
// all groups and their children are deleted in the scene
// this includes rendersurface_, overlays, blendingshader_ and rendershader_
delete groups_[View::RENDERING];
delete groups_[View::MIXING];
delete groups_[View::GEOMETRY];
delete groups_[View::LAYER];
delete groups_[View::TEXTURE];
delete groups_[View::TRANSITION];
groups_.clear();
frames_.clear();
overlays_.clear();
// don't forget that the processing shader
// could be created but not used
if ( renderingshader_ != processingshader_ )
delete processingshader_;
delete texturesurface_;
overlays_.clear();
frames_.clear();
handles_.clear();
}
void Source::setName (const std::string &name)
{
name_ = SystemToolkit::transliterate(name);
if (!name.empty())
name_ = BaseToolkit::transliterate(name);
initials_[0] = std::toupper( name_.front(), std::locale("C") );
initials_[1] = std::toupper( name_.back(), std::locale("C") );
@@ -296,18 +338,18 @@ void Source::setMode(Source::Mode m)
{
// make visible on first time
if ( mode_ == Source::UNINITIALIZED ) {
for (auto g = groups_.begin(); g != groups_.end(); g++)
for (auto g = groups_.begin(); g != groups_.end(); ++g)
(*g).second->visible_ = true;
}
// choose frame 0 if visible, 1 if selected
uint index_frame = m == Source::VISIBLE ? 0 : 1;
for (auto f = frames_.begin(); f != frames_.end(); f++)
for (auto f = frames_.begin(); f != frames_.end(); ++f)
(*f).second->setActive(index_frame);
// show overlay if current
bool current = m >= Source::CURRENT;
for (auto o = overlays_.begin(); o != overlays_.end(); o++)
for (auto o = overlays_.begin(); o != overlays_.end(); ++o)
(*o).second->visible_ = (current && !locked_);
// the lock icon
@@ -372,13 +414,14 @@ bool Source::imageProcessingEnabled()
void Source::render()
{
if (!initialized_)
if ( renderbuffer_ == nullptr )
init();
else {
// render the view into frame buffer
renderbuffer_->begin();
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
renderbuffer_->end();
ready_ = true;
}
}
@@ -459,7 +502,7 @@ void Source::setActive (bool on)
active_ = on;
// do not disactivate if a clone depends on it
for(auto clone = clones_.begin(); clone != clones_.end(); clone++) {
for(auto clone = clones_.begin(); clone != clones_.end(); ++clone) {
if ( (*clone)->active() )
active_ = true;
}
@@ -665,7 +708,7 @@ void Source::update(float dt)
FrameBuffer *Source::frame() const
{
if (initialized_ && renderbuffer_)
if ( mode_ > Source::UNINITIALIZED && renderbuffer_)
{
return renderbuffer_;
}
@@ -738,13 +781,13 @@ bool Source::hasNode::operator()(const Source* elem) const
// general case: traverse tree of all Groups recursively using a SearchVisitor
SearchVisitor sv(_n);
// search in groups for all views
for (auto g = elem->groups_.begin(); g != elem->groups_.end(); g++) {
for (auto g = elem->groups_.begin(); g != elem->groups_.end(); ++g) {
(*g).second->accept(sv);
if (sv.found())
return true;
}
// search in overlays for all views
for (auto g = elem->overlays_.begin(); g != elem->overlays_.end(); g++) {
for (auto g = elem->overlays_.begin(); g != elem->overlays_.end(); ++g) {
(*g).second->accept(sv);
if (sv.found())
return true;
@@ -762,9 +805,9 @@ void Source::clearMixingGroup()
}
CloneSource *Source::clone()
CloneSource *Source::clone(uint64_t id)
{
CloneSource *s = new CloneSource(this);
CloneSource *s = new CloneSource(this, id);
clones_.push_back(s);
@@ -772,8 +815,9 @@ CloneSource *Source::clone()
}
CloneSource::CloneSource(Source *origin) : Source(), origin_(origin)
CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin)
{
name_ = origin->name();
// set symbol
symbol_ = new Symbol(Symbol::CLONE, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
@@ -785,18 +829,18 @@ CloneSource::~CloneSource()
origin_->clones_.remove(this);
}
CloneSource *CloneSource::clone()
CloneSource *CloneSource::clone(uint64_t id)
{
// do not clone a clone : clone the original instead
if (origin_)
return origin_->clone();
return origin_->clone(id);
else
return nullptr;
}
void CloneSource::init()
{
if (origin_ && origin_->ready()) {
if (origin_ && origin_->mode_ > Source::UNINITIALIZED) {
// get the texture index from framebuffer of view, apply it to the surface
texturesurface_->setTextureIndex( origin_->texture() );
@@ -808,10 +852,9 @@ void CloneSource::init()
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
++View::need_deep_update_;
// done init
initialized_ = true;
Log::Info("Source %s cloning source %s.", name().c_str(), origin_->name().c_str() );
}
}
@@ -824,14 +867,14 @@ void CloneSource::setActive (bool on)
groups_[View::GEOMETRY]->visible_ = active_;
groups_[View::LAYER]->visible_ = active_;
if (initialized_ && origin_ != nullptr)
if ( mode_ > Source::UNINITIALIZED && origin_ != nullptr)
origin_->touch();
}
uint CloneSource::texture() const
{
if (initialized_ && origin_ != nullptr)
if (origin_ != nullptr)
return origin_->texture();
else
return Resource::getTextureBlack();

View File

@@ -23,7 +23,39 @@ class MixingGroup;
typedef std::list<CloneSource *> CloneList;
class Source
class SourceCore
{
public:
SourceCore();
SourceCore(SourceCore const&);
SourceCore& operator= (SourceCore const& other);
virtual ~SourceCore();
// get handle on the nodes used to manipulate the source in a view
inline Group *group (View::Mode m) const { return groups_.at(m); }
inline Node *groupNode (View::Mode m) const { return static_cast<Node*>(groups_.at(m)); }
void store (View::Mode m);
// a Source has a shader used to render in fbo
inline Shader *renderingShader () const { return renderingshader_; }
// a Source always has an image processing shader
inline ImageProcessingShader *processingShader () const { return processingshader_; }
void copy(SourceCore const& other);
protected:
// nodes
std::map<View::Mode, Group*> groups_;
// temporary copy for interaction
Group *stored_status_;
// image processing shaders
ImageProcessingShader *processingshader_;
// pointer to the currently attached shader
// (will be processingshader_ if image processing is enabled)
Shader *renderingshader_;
};
class Source : public SourceCore
{
friend class SourceLink;
friend class CloneSource;
@@ -38,7 +70,10 @@ class Source
public:
// create a source and add it to the list
// only subclasses of sources can actually be instanciated
Source ();
Source (uint64_t id = 0);
// non assignable class
Source(Source const&) = delete;
Source& operator=(Source const&) = delete;
virtual ~Source ();
// Get unique id
@@ -50,7 +85,7 @@ public:
inline const char *initials () const { return initials_; }
// cloning mechanism
virtual CloneSource *clone ();
virtual CloneSource *clone (uint64_t id = 0);
// Display mode
typedef enum {
@@ -62,16 +97,9 @@ public:
Mode mode () const;
void setMode (Mode m);
// get handle on the nodes used to manipulate the source in a view
inline Group *group (View::Mode m) const { return groups_.at(m); }
inline Node *groupNode (View::Mode m) const { return static_cast<Node*>(groups_.at(m)); }
// tests if a given node is part of the source
bool contains (Node *node) const;
// the rendering shader always have an image processing shader
inline ImageProcessingShader *processingShader () const { return processingshader_; }
// the image processing shader can be enabled or disabled
// (NB: when disabled, a simple ImageShader is applied)
void setImageProcessingEnabled (bool on);
@@ -80,9 +108,6 @@ public:
// a Source has a shader to control mixing effects
inline ImageShader *blendingShader () const { return blendingshader_; }
// a Source has a shader used to render in fbo
inline Shader *renderingShader () const { return renderingshader_; }
// every Source has a frame buffer from the renderbuffer
virtual FrameBuffer *frame () const;
@@ -93,7 +118,7 @@ public:
inline void touch () { need_update_ = true; }
// informs if its ready (i.e. initialized)
inline bool ready () const { return initialized_; }
inline bool ready () const { return ready_; }
// a Source shall be updated before displayed (Mixing, Geometry and Layer)
virtual void update (float dt);
@@ -206,12 +231,9 @@ protected:
char initials_[3];
uint64_t id_;
// every Source shall be initialized on first draw
bool initialized_;
// every Source shall be initialized to be ready after first draw
virtual void init() = 0;
// nodes
std::map<View::Mode, Group*> groups_;
bool ready_;
// render() fills in the renderbuffer at every frame
// NB: rendershader_ is applied at render()
@@ -223,12 +245,6 @@ protected:
FrameBufferSurface *rendersurface_;
FrameBufferSurface *mixingsurface_;
// image processing shaders
ImageProcessingShader *processingshader_;
// pointer to the currently attached shader
// (will be processingshader_ if image processing is enabled)
Shader *renderingshader_;
// blendingshader provides mixing controls
ImageShader *blendingshader_;
ImageShader *mixingshader_;
@@ -259,7 +275,6 @@ protected:
bool locked_;
bool need_update_;
float dt_;
Group *stored_status_;
Workspace workspace_;
// clones
@@ -288,7 +303,7 @@ public:
bool failed() const override { return origin_ == nullptr; }
void accept (Visitor& v) override;
CloneSource *clone() override;
CloneSource *clone(uint64_t id = 0) override;
inline void detach() { origin_ = nullptr; }
inline Source *origin() const { return origin_; }
@@ -296,7 +311,7 @@ public:
protected:
// only Source class can create new CloneSource via clone();
CloneSource(Source *origin);
CloneSource(Source *origin, uint64_t id = 0);
void init() override;
Source *origin_;

View File

@@ -49,7 +49,7 @@ SourceIdList ids (const SourceList &list)
{
SourceIdList idlist;
for( auto sit = list.begin(); sit != list.end(); sit++)
for( auto sit = list.begin(); sit != list.end(); ++sit)
idlist.push_back( (*sit)->id() );
// make sure no duplicate
@@ -67,7 +67,7 @@ SourceListCompare compare (const SourceList &first, const SourceList &second)
// a new test list: start with the second list and remove all commons with first list
SourceList test = second;
for (auto it = first.begin(); it != first.end(); it++){
for (auto it = first.begin(); it != first.end(); ++it){
test.remove(*it);
}
@@ -100,12 +100,12 @@ SourceList intersect (const SourceList &first, const SourceList &second)
// take second list and remove all elements also in first list
// -> builds the list of what remains in second list
SourceList l1 = second;
for (auto it = first.begin(); it != first.end(); it++)
for (auto it = first.begin(); it != first.end(); ++it)
l1.remove(*it);
// take second list and remove all elements in the remainer list
// -> builds the list of what is in second list and was part of the first list
SourceList l2 = second;
for (auto it = l1.begin(); it != l1.end(); it++)
for (auto it = l1.begin(); it != l1.end(); ++it)
l2.remove(*it);
return l2;
}
@@ -114,13 +114,15 @@ SourceList intersect (const SourceList &first, const SourceList &second)
SourceList join (const SourceList &first, const SourceList &second)
{
SourceList l = second;
for (auto it = first.begin(); it != first.end(); it++)
for (auto it = first.begin(); it != first.end(); ++it)
l.push_back(*it);
l.unique();
return l;
}
void SourceLink::connect(uint64_t id, Session *se)
{
if (connected())
@@ -136,8 +138,9 @@ void SourceLink::connect(Source *s)
disconnect();
target_ = s;
id_ = s->id();
target_->links_.push_back(this);
id_ = s->id();
// TODO veryfy circular dependency recursively ?
}
@@ -145,9 +148,9 @@ void SourceLink::disconnect()
{
if (target_)
target_->links_.remove(this);
target_ = nullptr;
id_ = 0;
target_ = nullptr;
host_ = nullptr;
}
@@ -181,3 +184,21 @@ Source *SourceLink::source()
return target_;
}
SourceList validate (const SourceLinkList &list)
{
SourceList sourcelist;
for( auto sit = list.begin(); sit != list.end(); ++sit) {
Source *s = (*sit)->source();
if (s)
sourcelist.push_back( s );
}
// make sure no duplicate
sourcelist.unique();
return sourcelist;
}

View File

@@ -5,9 +5,11 @@
#include <glm/glm.hpp>
class Source;
class SourceCore;
class Session;
typedef std::list<Source *> SourceList;
typedef std::list<SourceCore *> SourceCoreList;
SourceList depth_sorted (const SourceList &list);
SourceList mixing_sorted (const SourceList &list, glm::vec2 center = glm::vec2(0.f, 0.f));
@@ -42,9 +44,11 @@ public:
protected:
Session *host_;
Source *target_;
Source *target_;
uint64_t id_;
};
typedef std::list<SourceLink*> SourceLinkList;
SourceList validate (const SourceLinkList &list);
#endif // SOURCELIST_H

View File

@@ -12,7 +12,7 @@ using namespace std;
#include "Resource.h"
#include "Visitor.h"
#include "SystemToolkit.h"
#include "GlmToolkit.h"
#include "BaseToolkit.h"
#include "Stream.h"
@@ -24,19 +24,19 @@ using namespace std;
Stream::Stream()
{
// create unique id
id_ = GlmToolkit::uniqueId();
id_ = BaseToolkit::uniqueId();
description_ = "undefined";
pipeline_ = nullptr;
opened_ = false;
enabled_ = true;
desired_state_ = GST_STATE_PAUSED;
width_ = -1;
height_ = -1;
single_frame_ = false;
live_ = false;
ready_ = false;
failed_ = false;
enabled_ = true;
desired_state_ = GST_STATE_PAUSED;
// start index in frame_ stack
write_index_ = 0;
@@ -50,11 +50,20 @@ Stream::Stream()
// OpenGL texture
textureindex_ = 0;
textureinitialized_ = false;
}
Stream::~Stream()
{
close();
// cleanup opengl texture
if (textureindex_)
glDeleteTextures(1, &textureindex_);
// cleanup picture buffer
if (pbo_[0])
glDeleteBuffers(2, pbo_);
}
void Stream::accept(Visitor& v) {
@@ -70,8 +79,11 @@ guint Stream::texture() const
}
void Stream::open(const std::string &gstreamer_description, int w, int h)
void Stream::open(const std::string &gstreamer_description, guint w, guint h)
{
if (w != width_ || h != height_ )
textureinitialized_ = false;
// set gstreamer pipeline source
description_ = gstreamer_description;
width_ = w;
@@ -81,6 +93,7 @@ void Stream::open(const std::string &gstreamer_description, int w, int h)
if (isOpen())
close();
// open the stream
execute_open();
}
@@ -93,7 +106,7 @@ std::string Stream::description() const
void Stream::execute_open()
{
// reset
ready_ = false;
opened_ = false;
// Add custom app sink to the gstreamer pipeline
string description = description_;
@@ -109,6 +122,7 @@ void Stream::execute_open()
return;
}
g_object_set(G_OBJECT(pipeline_), "name", std::to_string(id_).c_str(), NULL);
gst_pipeline_set_auto_flush_bus( GST_PIPELINE(pipeline_), true);
// GstCaps *caps = gst_static_caps_get (&frame_render_caps);
string capstring = "video/x-raw,format=RGBA,width="+ std::to_string(width_) +
@@ -138,14 +152,13 @@ void Stream::execute_open()
#ifdef USE_GST_APPSINK_CALLBACKS
// set the callbacks
GstAppSinkCallbacks callbacks;
callbacks.new_preroll = callback_new_preroll;
if (single_frame_) {
callbacks.new_preroll = callback_new_preroll;
callbacks.eos = NULL;
callbacks.new_sample = NULL;
Log::Info("Stream %s contains a single frame", std::to_string(id_).c_str());
}
else {
callbacks.new_preroll = callback_new_preroll;
callbacks.eos = callback_end_of_stream;
callbacks.new_sample = callback_new_sample;
}
@@ -176,18 +189,18 @@ void Stream::execute_open()
// instruct the sink to send samples synched in time if not live source
gst_base_sink_set_sync (GST_BASE_SINK(sink), !live_);
// all good
Log::Info("Stream %s Opened '%s' (%d x %d)", std::to_string(id_).c_str(), description.c_str(), width_, height_);
ready_ = true;
// done with refs
gst_object_unref (sink);
gst_caps_unref (caps);
// all good
Log::Info("Stream %s Opened '%s' (%d x %d)", std::to_string(id_).c_str(), description.c_str(), width_, height_);
opened_ = true;
}
bool Stream::isOpen() const
{
return ready_;
return opened_;
}
bool Stream::failed() const
@@ -197,22 +210,23 @@ bool Stream::failed() const
void Stream::Frame::unmap()
{
if ( full ) {
if ( full )
gst_video_frame_unmap(&vframe);
full = false;
}
full = false;
}
void Stream::close()
{
// not openned?
if (!ready_) {
if (!opened_) {
// nothing else to change
return;
}
// un-ready
ready_ = false;
opened_ = false;
single_frame_ = false;
live_ = false;
// clean up GST
if (pipeline_ != nullptr) {
@@ -229,10 +243,9 @@ void Stream::close()
gst_object_unref (pipeline_);
pipeline_ = nullptr;
}
desired_state_ = GST_STATE_PAUSED;
// cleanup eventual remaining frame memory
for(guint i = 0; i < N_FRAME; i++){
for(guint i = 0; i < N_FRAME; ++i){
frame_[i].access.lock();
frame_[i].unmap();
frame_[i].access.unlock();
@@ -240,15 +253,6 @@ void Stream::close()
write_index_ = 0;
last_index_ = 0;
// cleanup opengl texture
if (textureindex_)
glDeleteTextures(1, &textureindex_);
textureindex_ = 0;
// cleanup picture buffer
if (pbo_[0])
glDeleteBuffers(2, pbo_);
pbo_size_ = 0;
}
@@ -269,7 +273,7 @@ float Stream::aspectRatio() const
void Stream::enable(bool on)
{
if ( !ready_ || pipeline_ == nullptr)
if ( !opened_ || pipeline_ == nullptr)
return;
if ( enabled_ != on ) {
@@ -312,7 +316,7 @@ bool Stream::live() const
void Stream::play(bool on)
{
// ignore if disabled, and cannot play an image
if (!enabled_)
if (!enabled_ || single_frame_)
return;
// request state
@@ -353,6 +357,10 @@ void Stream::play(bool on)
bool Stream::isPlaying(bool testpipeline) const
{
// image cannot play
if (single_frame_)
return false;
// if not ready yet, answer with requested state
if ( !testpipeline || pipeline_ == nullptr || !enabled_)
return desired_state_ == GST_STATE_PLAYING;
@@ -368,6 +376,8 @@ bool Stream::isPlaying(bool testpipeline) const
void Stream::init_texture(guint index)
{
glActiveTexture(GL_TEXTURE0);
if (textureindex_)
glDeleteTextures(1, &textureindex_);
glGenTextures(1, &textureindex_);
glBindTexture(GL_TEXTURE_2D, textureindex_);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width_, height_);
@@ -378,53 +388,58 @@ void Stream::init_texture(guint index)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// set pbo image size
pbo_size_ = height_ * width_ * 4;
if (!single_frame_) {
// create pixel buffer objects,
if (pbo_[0])
glDeleteBuffers(2, pbo_);
glGenBuffers(2, pbo_);
// set pbo image size
pbo_size_ = height_ * width_ * 4;
for(int i = 0; i < 2; i++ ) {
// create 2 PBOs
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[i]);
// glBufferDataARB with NULL pointer reserves only memory space.
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
// fill in with reset picture
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if (ptr) {
// update data directly on the mapped buffer
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
// release pointer to mapping buffer
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
}
else {
// did not work, disable PBO
// create pixel buffer objects,
if (pbo_[0])
glDeleteBuffers(2, pbo_);
pbo_[0] = pbo_[1] = 0;
pbo_size_ = 0;
break;
glGenBuffers(2, pbo_);
for(int i = 0; i < 2; i++ ) {
// create 2 PBOs
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[i]);
// glBufferDataARB with NULL pointer reserves only memory space.
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
// fill in with reset picture
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if (ptr) {
// update data directly on the mapped buffer
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
// release pointer to mapping buffer
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
}
else {
// did not work, disable PBO
glDeleteBuffers(2, pbo_);
pbo_[0] = pbo_[1] = 0;
pbo_size_ = 0;
break;
}
}
}
// should be good to go, wrap it up
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo_index_ = 0;
pbo_next_index_ = 1;
// should be good to go, wrap it up
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo_index_ = 0;
pbo_next_index_ = 1;
#ifdef STREAM_DEBUG
Log::Info("Stream %s Use Pixel Buffer Object texturing.", std::to_string(id_).c_str());
#endif
}
glBindTexture(GL_TEXTURE_2D, 0);
textureinitialized_ = true;
}
void Stream::fill_texture(guint index)
{
// is this the first frame ?
if (textureindex_ < 1)
if ( !textureinitialized_ || textureindex_ < 1)
{
// initialize texture
init_texture(index);
@@ -466,6 +481,7 @@ void Stream::fill_texture(guint index)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
}
@@ -476,12 +492,14 @@ void Stream::update()
return;
// not ready yet
if (!ready_)
if (!opened_){
// wait next frame to display
return;
}
// // prevent unnecessary updates: disabled or already filled image
// if (!enabled_)
// return;
// prevent unnecessary updates: already filled image
if (single_frame_ && textureindex_>0)
return;
// local variables before trying to update
guint read_index = 0;
@@ -627,7 +645,7 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
void Stream::callback_end_of_stream (GstAppSink *, gpointer p)
{
Stream *m = static_cast<Stream *>(p);
if (m && m->ready_) {
if (m && m->opened_) {
m->fill_frame(NULL, Stream::EOS);
}
}
@@ -643,7 +661,7 @@ GstFlowReturn Stream::callback_new_preroll (GstAppSink *sink, gpointer p)
if (sample != NULL) {
// send frames to media player only if ready
Stream *m = static_cast<Stream *>(p);
if (m && m->ready_) {
if (m && m->opened_) {
// get buffer from sample
GstBuffer *buf = gst_sample_get_buffer (sample);
@@ -677,7 +695,7 @@ GstFlowReturn Stream::callback_new_sample (GstAppSink *sink, gpointer p)
// send frames to media player only if ready
Stream *m = static_cast<Stream *>(p);
if (m && m->ready_) {
if (m && m->opened_) {
// get buffer from sample (valid until sample is released)
GstBuffer *buf = gst_sample_get_buffer (sample) ;
@@ -710,7 +728,7 @@ void Stream::TimeCounter::tic ()
GstClockTime dt = t - last_time;
// one more frame since last time
nbFrames++;
++nbFrames;
// calculate instantaneous framerate
// Exponential moving averate with previous framerate to filter jitter (50/50)

View File

@@ -33,7 +33,7 @@ public:
/**
* Open a media using gstreamer pipeline keyword
* */
void open(const std::string &gstreamer_description, int w = 1024, int h = 576);
void open(const std::string &gstreamer_description, guint w = 1024, guint h = 576);
/**
* Get description string
* */
@@ -49,7 +49,7 @@ public:
/**
* Close the Media
* */
void close();
virtual void close();
/**
* Update texture with latest frame
* Must be called in rendering update loop
@@ -129,7 +129,7 @@ protected:
GstState desired_state_;
GstElement *pipeline_;
GstVideoInfo v_frame_video_info_;
std::atomic<bool> ready_;
std::atomic<bool> opened_;
std::atomic<bool> failed_;
bool enabled_;
@@ -185,6 +185,7 @@ protected:
virtual void execute_open();
// gst frame filling
bool textureinitialized_;
void init_texture(guint index);
void fill_texture(guint index);
bool fill_frame(GstBuffer *buf, FrameStatus status);

View File

@@ -26,8 +26,12 @@ void GenericStreamSource::setDescription(const std::string &desc)
{
Log::Notify("Creating Source with Stream description '%s'", desc.c_str());
// open gstreamer
stream_->open(desc);
stream_->play(true);
// will be ready after init and one frame rendered
ready_ = false;
}
void GenericStreamSource::accept(Visitor& v)
@@ -37,7 +41,7 @@ void GenericStreamSource::accept(Visitor& v)
v.visit(*this);
}
StreamSource::StreamSource() : Source(), stream_(nullptr)
StreamSource::StreamSource(uint64_t id) : Source(id), stream_(nullptr)
{
}
@@ -81,14 +85,13 @@ void StreamSource::init()
// set the renderbuffer of the source and attach rendering nodes
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
// force update of activation mode
active_ = true;
// deep update to reorder
++View::need_deep_update_;
// done init
initialized_ = true;
Log::Info("Source '%s' linked to Stream %s", name().c_str(), std::to_string(stream_->id()).c_str());
}
}

View File

@@ -24,7 +24,7 @@
class StreamSource: public Source
{
public:
StreamSource();
StreamSource(uint64_t id = 0);
virtual ~StreamSource();
// implementation of source API

View File

@@ -29,8 +29,8 @@ class Streaming
// Private Constructor
Streaming();
Streaming(Streaming const& copy); // Not Implemented
Streaming& operator=(Streaming const& copy); // Not Implemented
Streaming(Streaming const& copy) = delete;
Streaming& operator=(Streaming const& copy) = delete;
public:

View File

@@ -1,15 +1,11 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sstream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <ctime>
#include <chrono>
#include <locale>
#include <unicode/ustream.h>
#include <unicode/translit.h>
#include <algorithm>
using namespace std;
@@ -88,45 +84,10 @@ long SystemToolkit::memory_max_usage() {
struct rusage r_usage;
getrusage(RUSAGE_SELF,&r_usage);
return r_usage.ru_maxrss;
return 1024 * r_usage.ru_maxrss;
// return r_usage.ru_isrss;
}
string SystemToolkit::byte_to_string(long b)
{
double numbytes = static_cast<double>(b);
ostringstream oss;
std::list<std::string> list = {" Bytes", " KB", " MB", " GB", " TB"};
std::list<std::string>::iterator i = list.begin();
while(numbytes >= 1024.0 && i != list.end())
{
++i;
numbytes /= 1024.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
return oss.str();
}
string SystemToolkit::bits_to_string(long b)
{
double numbytes = static_cast<double>(b);
ostringstream oss;
std::list<std::string> list = {" bit", " Kbit", " Mbit", " Gbit", " Tbit"};
std::list<std::string>::iterator i = list.begin();
while(numbytes >= 1000.0 && i != list.end())
{
++i;
numbytes /= 1000.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
return oss.str();
}
string SystemToolkit::date_time_string()
{
@@ -171,19 +132,12 @@ string SystemToolkit::path_filename(const string& path)
return path.substr(0, path.find_last_of(PATH_SEP) + 1);
}
string SystemToolkit::trunc_filename(const string& path, int lenght)
{
string trunc = path;
int l = path.size();
if ( l > lenght ) {
trunc = string("...") + path.substr( l - lenght + 3 );
}
return trunc;
}
string SystemToolkit::extension_filename(const string& filename)
{
string ext = filename.substr(filename.find_last_of(".") + 1);
string ext;
auto loc = filename.find_last_of(".");
if (loc != string::npos)
ext = filename.substr( loc + 1 );
return ext;
}
@@ -323,7 +277,7 @@ std::string SystemToolkit::path_directory(const std::string& path)
return directorypath;
}
list<string> SystemToolkit::list_directory(const string& path, const string& filter)
list<string> SystemToolkit::list_directory(const string& path, const list<string>& extensions)
{
list<string> ls;
@@ -335,13 +289,16 @@ list<string> SystemToolkit::list_directory(const string& path, const string& fil
if ( ent->d_type == DT_REG)
{
string filename = string(ent->d_name);
if ( extension_filename(filename) == filter)
string ext = extension_filename(filename);
if ( extensions.empty() || find(extensions.cbegin(), extensions.cend(), ext) != extensions.cend())
ls.push_back( full_filename(path, filename) );
}
}
closedir (dir);
}
ls.sort();
return ls;
}
@@ -376,27 +333,3 @@ void SystemToolkit::execute(const string& command)
// std::thread (SystemToolkit::execute,
// "gst-launch-1.0 udpsrc port=5000 ! application/x-rtp,encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! autovideosink").detach();;
// Using ICU transliteration :
// https://unicode-org.github.io/icu/userguide/transforms/general/#icu-transliterators
std::string SystemToolkit::transliterate(std::string input)
{
auto ucs = icu::UnicodeString::fromUTF8(input);
UErrorCode status = U_ZERO_ERROR;
icu::Transliterator *firstTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; NFKC; Latin", UTRANS_FORWARD, status);
firstTrans->transliterate(ucs);
delete firstTrans;
icu::Transliterator *secondTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; [@!#$*%~] Remove; NFKC", UTRANS_FORWARD, status);
secondTrans->transliterate(ucs);
delete secondTrans;
std::ostringstream output;
output << ucs;
return output.str();
}

View File

@@ -42,9 +42,6 @@ namespace SystemToolkit
// extract the path of a filename from a full URI (e.g. file:://home/me/toto.mpg -> file:://home/me/)
std::string path_filename(const std::string& path);
// Truncate a full filename to display the right part (e.g. file:://home/me/toto.mpg -> ...ome/me/toto.mpg)
std::string trunc_filename(const std::string& path, int lenght);
// extract the extension of a filename
std::string extension_filename(const std::string& filename);
@@ -52,7 +49,7 @@ namespace SystemToolkit
std::string path_directory(const std::string& path);
// list all files of a directory mathing the given filter extension (if any)
std::list<std::string> list_directory(const std::string& path, const std::string& filter = "");
std::list<std::string> list_directory(const std::string& path, const std::list<std::string> &extensions);
// true of file exists
bool file_exists(const std::string& path);
@@ -69,18 +66,11 @@ namespace SystemToolkit
// try to execute a command
void execute(const std::string& command);
// return memory resident set size used (in bytes)
// return memory used (in bytes)
long memory_usage();
// return maximum memory resident set size used (in bytes)
long memory_max_usage();
// get a string to display memory size with unit KB, MB, GB, TB
std::string byte_to_string(long b);
// get a string to display bit size with unit Kbit, MBit, Gbit, Tbit
std::string bits_to_string(long b);
// get a transliteration to Latin of any string
std::string transliterate(std::string input);
}
#endif // SYSTEMTOOLKIT_H

View File

@@ -14,6 +14,7 @@
#include "defines.h"
#include "Mixer.h"
#include "Source.h"
#include "Settings.h"
#include "Resource.h"
#include "PickingVisitor.h"

View File

@@ -8,6 +8,9 @@ class TextureView : public View
{
public:
TextureView();
// non assignable class
TextureView(TextureView const&) = delete;
TextureView& operator=(TextureView const&) = delete;
void draw () override;
void update (float dt) override;

View File

@@ -7,6 +7,9 @@ class TransitionView : public View
{
public:
TransitionView();
// non assignable class
TransitionView(TransitionView const&) = delete;
TransitionView& operator=(TransitionView const&) = delete;
void draw () override;
void update (float dt) override;

View File

@@ -11,6 +11,18 @@ UpdateCallback::UpdateCallback() : enabled_(true), finished_(false)
}
CopyCallback::CopyCallback(Node *target) : UpdateCallback(), target_(target)
{
}
void CopyCallback::update(Node *n, float dt)
{
n->copyTransform(target_);
finished_ = true;
}
MoveToCallback::MoveToCallback(glm::vec3 target, float duration) : UpdateCallback(),
duration_(duration), progress_(0.f), initialized_(false), target_(target)
{

View File

@@ -1,7 +1,7 @@
#ifndef UPDATECALLBACK_H
#define UPDATECALLBACK_H
#include "GlmToolkit.h"
#include <glm/glm.hpp>
class Node;
@@ -22,6 +22,15 @@ protected:
bool finished_;
};
class CopyCallback : public UpdateCallback
{
Node *target_;
public:
CopyCallback(Node *target);
void update(Node *n, float dt);
};
class MoveToCallback : public UpdateCallback
{
float duration_;

File diff suppressed because it is too large Load Diff

View File

@@ -13,11 +13,13 @@
struct ImVec2;
class Source;
class MediaPlayer;
class FrameBufferImage;
class SourcePreview {
Source *source_;
std::string label_;
bool reset_;
public:
SourcePreview();
@@ -30,26 +32,43 @@ public:
inline bool filled() const { return source_ != nullptr; }
};
class Thumbnail
{
float aspect_ratio_;
uint texture_;
public:
Thumbnail();
~Thumbnail();
void reset();
void set (const FrameBufferImage *image);
void Render(float width);
};
class Navigator
{
// geometry left bar & pannel
float width_;
float height_;
float pannel_width_;
float sourcelist_height_;
float padding_width_;
// behavior pannel
bool show_config_;
bool pannel_visible_;
bool view_pannel_visible;
bool selected_button[NAV_COUNT];
int pattern_type;
std::list<std::string> _selectedFiles;
void clearButtonSelection();
void applyButtonSelection(int index);
// side pannels
void RenderSourcePannel(Source *s);
void RenderMainPannel();
void RenderMainPannelVimix();
void RenderMainPannelSettings();
void RenderTransitionPannel();
void RenderNewPannel();
void RenderViewPannel(ImVec2 draw_pos, ImVec2 draw_size);
@@ -99,6 +118,7 @@ public:
void Render();
};
class UserInterface
{
friend class Navigator;
@@ -123,8 +143,8 @@ class UserInterface
// Private Constructor
UserInterface();
UserInterface(UserInterface const& copy); // Not Implemented
UserInterface& operator=(UserInterface const& copy); // Not Implemented
UserInterface(UserInterface const& copy) = delete;
UserInterface& operator=(UserInterface const& copy) = delete;
public:
@@ -149,9 +169,7 @@ public:
inline bool altModifier() const { return alt_modifier_active; }
inline bool shiftModifier() const { return shift_modifier_active; }
void StartScreenshot();
void showPannel(int id = 0);
void showSourceEditor(Source *s);
void showMediaPlayer(MediaPlayer *mp);
@@ -159,6 +177,8 @@ public:
std::string currentTextEdit;
void fillShaderEditor(const std::string &text);
void StartScreenshot();
protected:
void showMenuFile();
@@ -171,10 +191,12 @@ protected:
void RenderHistory();
void RenderShaderEditor();
int RenderViewNavigator(int* shift);
void RenderAbout(bool* p_open);
void RenderNotes();
void handleKeyboard();
void handleMouse();
void handleScreenshot();
void RenderAbout(bool* p_open);
};
#endif /* #define __UI_MANAGER_H_ */

View File

@@ -114,11 +114,8 @@ void View::initiate()
{
current_action_ = "";
for (auto sit = Mixer::manager().session()->begin();
sit != Mixer::manager().session()->end(); sit++){
(*sit)->stored_status_->copyTransform((*sit)->group(mode_));
}
sit != Mixer::manager().session()->end(); ++sit)
(*sit)->store(mode_);
}
void View::terminate()
@@ -190,7 +187,7 @@ void View::selectAll()
{
Mixer::selection().clear();
for(auto sit = Mixer::manager().session()->begin();
sit != Mixer::manager().session()->end(); sit++) {
sit != Mixer::manager().session()->end(); ++sit) {
if (canSelect(*sit))
Mixer::selection().add(*sit);
}

2
View.h
View File

@@ -80,7 +80,7 @@ public:
return Cursor ();
}
//TODO: test mouse over provided a point in screen coordinates
// test mouse over provided a point in screen coordinates
virtual Cursor over (glm::vec2) {
return Cursor ();
}

View File

@@ -38,6 +38,7 @@ class RenderSource;
class CloneSource;
class NetworkSource;
class MixingGroup;
class MultiFileSource;
// Declares the interface for the visitors
class Visitor {
@@ -81,6 +82,7 @@ public:
virtual void visit (SessionGroupSource&) {}
virtual void visit (RenderSource&) {}
virtual void visit (CloneSource&) {}
virtual void visit (MultiFileSource&) {}
};

View File

@@ -59,20 +59,24 @@
#define TRANSITION_MIN_DURATION 0.2f
#define TRANSITION_MAX_DURATION 10.f
#define ARROWS_MOVEMENT_FACTOR 4.f
#define SESSION_THUMBNAIL_HEIGHT 120.f
#define IMGUI_TITLE_MAINWINDOW ICON_FA_CIRCLE_NOTCH " vimix"
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_FILM " Player"
#define IMGUI_TITLE_HISTORY ICON_FA_HISTORY " History"
#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Tools"
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Code"
#define IMGUI_TITLE_LOGS ICON_FA_LIST " Logs"
#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Toolbox"
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Code Editor"
#define IMGUI_TITLE_PREVIEW ICON_FA_DESKTOP " Ouput"
#define IMGUI_TITLE_DELETE ICON_FA_BROOM " Delete?"
#define IMGUI_LABEL_RECENT_FILES " Select recent"
#define IMGUI_LABEL_RECENT_FILES " Recent files"
#define IMGUI_RIGHT_ALIGN -3.5f * ImGui::GetTextLineHeightWithSpacing()
#define IMGUI_TOP_ALIGN 10
#define IMGUI_COLOR_OVERLAY IM_COL32(5, 5, 5, 150)
#define IMGUI_COLOR_RECORD 1.0, 0.05, 0.05
#define IMGUI_COLOR_STREAM 0.05, 0.8, 1.0
#define IMGUI_NOTIFICATION_DURATION 2.5f
#define IMGUI_TOOLTIP_TIMEOUT 80
#ifdef APPLE
#define CTRL_MOD "Cmd+"
#else

Some files were not shown because too many files have changed in this diff Show More