Source creation by origin: File, software and hardware. Separate source

classes for Media and Session sources. Update of file dialog
accordingly.
This commit is contained in:
brunoherbelin
2020-05-20 22:16:31 +02:00
parent 46f3ad5a50
commit a2ea06b2c6
18 changed files with 584 additions and 361 deletions

View File

@@ -209,6 +209,7 @@ set(VMIX_SRCS
View.cpp
Source.cpp
Session.cpp
SessionSource.cpp
SessionVisitor.cpp
GarbageVisitor.cpp
SessionCreator.cpp
@@ -218,6 +219,7 @@ set(VMIX_SRCS
Resource.cpp
FileDialog.cpp
MediaPlayer.cpp
MediaSource.cpp
FrameBuffer.cpp
RenderingManager.cpp
UserInterfaceManager.cpp

155
MediaSource.cpp Normal file
View File

@@ -0,0 +1,155 @@
#include <glm/gtc/matrix_transform.hpp>
#include "MediaSource.h"
#include "defines.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Resource.h"
#include "Primitives.h"
#include "MediaPlayer.h"
#include "Visitor.h"
#include "Log.h"
MediaSource::MediaSource() : Source(), path_("")
{
// create media player
mediaplayer_ = new MediaPlayer;
// create media surface:
// - textured with original texture from media player
// - crop & repeat UV can be managed here
// - additional custom shader can be associated
mediasurface_ = new Surface(rendershader_);
}
MediaSource::~MediaSource()
{
// TODO delete media surface with visitor
delete mediasurface_;
delete mediaplayer_;
// TODO verify that all surfaces and node is deleted in Source destructor
}
void MediaSource::setPath(const std::string &p)
{
path_ = p;
mediaplayer_->open(path_);
mediaplayer_->play(true);
Log::Notify("Opening %s", p.c_str());
}
std::string MediaSource::path() const
{
return path_;
}
MediaPlayer *MediaSource::mediaplayer() const
{
return mediaplayer_;
}
bool MediaSource::failed() const
{
return mediaplayer_->failed();
}
void MediaSource::init()
{
if ( mediaplayer_->isOpen() ) {
// update video
mediaplayer_->update();
// once the texture of media player is created
if (mediaplayer_->texture() != Resource::getTextureBlack()) {
// get the texture index from media player, apply it to the media surface
mediasurface_->setTextureIndex( mediaplayer_->texture() );
// create Frame buffer matching size of media player
float height = float(mediaplayer()->width()) / mediaplayer()->aspectRatio();
renderbuffer_ = new FrameBuffer(mediaplayer()->width(), (uint)height);
// create the surfaces to draw the frame buffer in the views
// TODO Provide the source custom effect shader
rendersurface_ = new FrameBufferSurface(renderbuffer_, blendingshader_);
groups_[View::RENDERING]->attach(rendersurface_);
groups_[View::GEOMETRY]->attach(rendersurface_);
groups_[View::MIXING]->attach(rendersurface_);
groups_[View::LAYER]->attach(rendersurface_);
// for mixing view, add another surface to overlay (for stippled view in transparency)
Surface *surfacemix = new FrameBufferSurface(renderbuffer_);
ImageShader *is = static_cast<ImageShader *>(surfacemix->shader());
if (is) is->stipple = 1.0;
groups_[View::MIXING]->attach(surfacemix);
groups_[View::LAYER]->attach(surfacemix);
if (mediaplayer_->duration() == GST_CLOCK_TIME_NONE)
overlays_[View::MIXING]->attach( new Mesh("mesh/icon_image.ply") );
else
overlays_[View::MIXING]->attach( new Mesh("mesh/icon_video.ply") );
// scale all mixing nodes to match aspect ratio of the media
for (NodeSet::iterator node = groups_[View::MIXING]->begin();
node != groups_[View::MIXING]->end(); node++) {
(*node)->scale_.x = mediaplayer_->aspectRatio();
}
for (NodeSet::iterator node = groups_[View::GEOMETRY]->begin();
node != groups_[View::GEOMETRY]->end(); node++) {
(*node)->scale_.x = mediaplayer_->aspectRatio();
}
for (NodeSet::iterator node = groups_[View::LAYER]->begin();
node != groups_[View::LAYER]->end(); node++) {
(*node)->scale_.x = mediaplayer_->aspectRatio();
}
// done init once and for all
initialized_ = true;
// make visible
groups_[View::RENDERING]->visible_ = true;
groups_[View::MIXING]->visible_ = true;
groups_[View::GEOMETRY]->visible_ = true;
groups_[View::LAYER]->visible_ = true;
}
}
}
void MediaSource::render()
{
if (!initialized_)
init();
else {
// update video
mediaplayer_->update();
// render the media player into frame buffer
static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f);
renderbuffer_->begin();
mediasurface_->draw(glm::identity<glm::mat4>(), projection);
renderbuffer_->end();
}
}
FrameBuffer *MediaSource::frame() const
{
if (initialized_ && renderbuffer_)
{
return renderbuffer_;
}
else {
static FrameBuffer *black = new FrameBuffer(640,480);
return black;
}
}
void MediaSource::accept(Visitor& v)
{
Source::accept(v);
v.visit(*this);
}

32
MediaSource.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef MEDIASOURCE_H
#define MEDIASOURCE_H
#include "Source.h"
class MediaSource : public Source
{
public:
MediaSource();
~MediaSource();
// implementation of source API
FrameBuffer *frame() const override;
void render() override;
void accept (Visitor& v) override;
bool failed() const override;
// Media specific interface
void setPath(const std::string &p);
std::string path() const;
MediaPlayer *mediaplayer() const;
protected:
void init() override;
Surface *mediasurface_;
std::string path_;
MediaPlayer *mediaplayer_;
};
#endif // MEDIASOURCE_H

View File

@@ -1,52 +1,54 @@
#include <algorithm>
#include <thread>
#include <atomic>
#include <vector>
#include <chrono>
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
using namespace tinyxml2;
#include "defines.h"
#include "Settings.h"
#include "Log.h"
#include "View.h"
#include "Primitives.h"
#include "Mesh.h"
#include "FrameBuffer.h"
#include "Settings.h"
#include "SystemToolkit.h"
#include "ImageProcessingShader.h"
#include "GarbageVisitor.h"
//#include "GarbageVisitor.h"
#include "SessionVisitor.h"
#include "SessionCreator.h"
#include "MediaPlayer.h"
#include "SessionSource.h"
#include "MediaSource.h"
#include "Mixer.h"
// static objects for multithreaded session loading
static std::atomic<bool> sessionThreadActive_ = false;
static std::string sessionThreadFilename_ = "";
static std::atomic<bool> sessionLoadFinished_ = false;
static void loadSession(const std::string& filename, Session *session)
{
sessionThreadActive_ = true;
sessionLoadFinished_ = false;
sessionThreadFilename_ = "";
while (sessionThreadActive_)
std::this_thread::sleep_for( std::chrono::milliseconds(50));
sessionThreadActive_ = true;
sessionLoadFinished_ = false;
sessionThreadFilename_ = "";
// actual loading of xml file
SessionCreator creator( session );
// actual loading of xml file
SessionCreator creator( session );
if (!creator.load(filename)) {
// error loading
Log::Notify("Failed to load Session file %s.", filename.c_str());
}
else {
// loaded ok
Log::Notify("Session %s loaded. %d source(s) created.", filename.c_str(), session->numSource());
}
if (!creator.load(filename)) {
// error loading
Log::Warning("Failed to load Session file %s.", filename.c_str());
}
else {
// loaded ok
Log::Notify("Session %s loaded. %d source(s) created.", filename.c_str(), session->numSource());
}
sessionThreadFilename_ = filename;
sessionLoadFinished_ = true;
sessionThreadActive_ = false;
sessionThreadFilename_ = filename;
sessionLoadFinished_ = true;
sessionThreadActive_ = false;
}
// static objects for multithreaded session saving
@@ -54,6 +56,8 @@ static std::atomic<bool> sessionSaveFinished_ = false;
static void saveSession(const std::string& filename, Session *session)
{
// reset
while (sessionThreadActive_)
std::this_thread::sleep_for( std::chrono::milliseconds(50));
sessionThreadActive_ = true;
sessionSaveFinished_ = false;
sessionThreadFilename_ = "";
@@ -175,22 +179,44 @@ void Mixer::draw()
}
// manangement of sources
void Mixer::createSourceMedia(std::string path)
void Mixer::createSourceFile(std::string path)
{
// create source
MediaSource *m = new MediaSource();
m->setPath(path);
// sanity check
if ( !SystemToolkit::file_exists( path ) ) {
Log::Notify("File %s does not exist.", path);
return;
}
// ready to create a source
Source *s = nullptr;
// test type of file by extension
std::string ext = SystemToolkit::extension_filename(path);
if ( ext == "vmx" )
{
// create a session source
SessionSource *ss = new SessionSource();
ss->setPath(path);
s = ss;
}
else {
// (try to) create media source by default
MediaSource *ms = new MediaSource();
ms->setPath(path);
s = ms;
}
// remember in recent media
Settings::application.recentMedia.push(path);
Settings::application.recentImport.push(path);
Settings::application.recentImport.path = SystemToolkit::path_filename(path);
// propose a new name based on uri
renameSource(m, SystemToolkit::base_filename(path));
renameSource(s, SystemToolkit::base_filename(path));
// add to mixer
insertSource(m);
insertSource(s);
}
void Mixer::insertSource(Source *s)
{
// Add source to Session and set it as current
@@ -353,18 +379,12 @@ View *Mixer::currentView()
void Mixer::save()
{
if (sessionThreadActive_)
return;
if (!sessionFilename_.empty())
saveas(sessionFilename_);
}
void Mixer::saveas(const std::string& filename)
{
if (sessionThreadActive_)
return;
// optional copy of views config
session_->config(View::MIXING)->copyTransform( mixing_.scene.root() );
session_->config(View::GEOMETRY)->copyTransform( geometry_.scene.root() );
@@ -376,9 +396,6 @@ void Mixer::saveas(const std::string& filename)
void Mixer::open(const std::string& filename)
{
if (sessionThreadActive_)
return;
if (back_session_)
delete back_session_;

View File

@@ -1,7 +1,6 @@
#ifndef MIXER_H
#define MIXER_H
#include <vector>
// GStreamer
#include <gst/gst.h>
@@ -11,6 +10,7 @@
#include "Session.h"
#include "Source.h"
class Mixer
{
// Private Constructor
@@ -34,7 +34,7 @@ public:
void draw();
// manangement of sources
void createSourceMedia(std::string path);
void createSourceFile(std::string path);
void deleteSource(Source *s);
void deleteCurrentSource();

View File

@@ -438,18 +438,8 @@ void Rendering::FileDropped(GLFWwindow *, int path_count, const char* paths[])
std::string filename(paths[i]);
if (filename.empty())
break;
std::string ext = SystemToolkit::extension_filename(filename);
// Log::Info("Dropped file %s %s\n", filename.c_str(), ext.c_str());
if ( ext == "vmx" )
{
// try to load a session
Mixer::manager().open(filename);
}
else {
// try to create a media source
Mixer::manager().createSourceMedia( filename );
}
// try to create a source
Mixer::manager().createSourceFile( filename );
}
}

View File

@@ -6,6 +6,7 @@
#include "Primitives.h"
#include "Mesh.h"
#include "Source.h"
#include "MediaSource.h"
#include "Session.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"

176
SessionSource.cpp Normal file
View File

@@ -0,0 +1,176 @@
#include <glm/gtc/matrix_transform.hpp>
#include <thread>
#include <chrono>
#include "SessionSource.h"
#include "defines.h"
#include "Log.h"
#include "FrameBuffer.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Resource.h"
#include "Primitives.h"
#include "Mesh.h"
#include "SearchVisitor.h"
#include "Session.h"
#include "SessionCreator.h"
void SessionSource::loadSession(const std::string& filename, SessionSource *source)
{
source->loadFinished_ = false;
// actual loading of xml file
SessionCreator creator( source->session_ );
if (!creator.load(filename)) {
// error loading
Log::Notify("Failed to load Session file %s.", filename.c_str());
source->loadFailed_ = true;
}
source->loadFinished_ = true;
}
SessionSource::SessionSource() : Source(), path_("")
{
loadFailed_ = false;
loadFinished_ = false;
session_ = new Session;
// create surface:
// - textured with original texture from session
// - crop & repeat UV can be managed here
// - additional custom shader can be associated
sessionsurface_ = new Surface(rendershader_);
}
SessionSource::~SessionSource()
{
// TODO delete media surface with visitor
delete sessionsurface_;
// TODO close and delete session
delete session_;
}
void SessionSource::setPath(const std::string &p)
{
path_ = p;
// launch a thread to load the session
std::thread ( SessionSource::loadSession, path_, this).detach();
Log::Notify("Opening %s", p.c_str());
}
std::string SessionSource::path() const
{
return path_;
}
Session *SessionSource::session() const
{
return session_;
}
bool SessionSource::failed() const
{
return loadFailed_;
}
void SessionSource::init()
{
if ( loadFinished_ && !loadFailed_ ) {
loadFinished_ = false;
// set resolution
session_->setResolution( session_->config(View::RENDERING)->scale_ );
// get the texture index from media player, apply it to the media surface
sessionsurface_->setTextureIndex( session_->frame()->texture() );
// create Frame buffer matching size of media player
renderbuffer_ = new FrameBuffer(session_->frame()->resolution());
// create the surfaces to draw the frame buffer in the views
rendersurface_ = new FrameBufferSurface(renderbuffer_, blendingshader_);
groups_[View::RENDERING]->attach(rendersurface_);
groups_[View::GEOMETRY]->attach(rendersurface_);
groups_[View::MIXING]->attach(rendersurface_);
groups_[View::LAYER]->attach(rendersurface_);
// for mixing view, add another surface to overlay (for stippled view in transparency)
Surface *surfacemix = new FrameBufferSurface(renderbuffer_);
ImageShader *is = static_cast<ImageShader *>(surfacemix->shader());
if (is) is->stipple = 1.0;
groups_[View::MIXING]->attach(surfacemix);
groups_[View::LAYER]->attach(surfacemix);
// TODO icon session
// overlays_[View::MIXING]->attach( new Mesh("mesh/icon_video.ply") );
// scale all mixing nodes to match aspect ratio of the media
for (NodeSet::iterator node = groups_[View::MIXING]->begin();
node != groups_[View::MIXING]->end(); node++) {
(*node)->scale_.x = session_->frame()->aspectRatio();
}
for (NodeSet::iterator node = groups_[View::GEOMETRY]->begin();
node != groups_[View::GEOMETRY]->end(); node++) {
(*node)->scale_.x = session_->frame()->aspectRatio();
}
for (NodeSet::iterator node = groups_[View::LAYER]->begin();
node != groups_[View::LAYER]->end(); node++) {
(*node)->scale_.x = session_->frame()->aspectRatio();
}
// done init once and for all
initialized_ = true;
// make visible
groups_[View::RENDERING]->visible_ = true;
groups_[View::MIXING]->visible_ = true;
groups_[View::GEOMETRY]->visible_ = true;
groups_[View::LAYER]->visible_ = true;
Log::Info("Source Session %s loading %d sources.", path_.c_str(), session_->numSource());
}
}
void SessionSource::render()
{
if (!initialized_)
init();
else {
// update session
session_->update(dt_);
sessionsurface_->setTextureIndex( session_->frame()->texture() );
// render the media player into frame buffer
static glm::mat4 projection = glm::ortho(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f);
renderbuffer_->begin();
sessionsurface_->draw(glm::identity<glm::mat4>(), projection);
renderbuffer_->end();
}
}
FrameBuffer *SessionSource::frame() const
{
if (initialized_ && renderbuffer_)
{
return renderbuffer_;
}
else {
static FrameBuffer *black = new FrameBuffer(640,480);
return black;
}
}
void SessionSource::accept(Visitor& v)
{
Source::accept(v);
v.visit(*this);
}

36
SessionSource.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef SESSIONSOURCE_H
#define SESSIONSOURCE_H
#include <atomic>
#include "Source.h"
class SessionSource : public Source
{
public:
SessionSource();
~SessionSource();
// implementation of source API
FrameBuffer *frame() const override;
void render() override;
void accept (Visitor& v) override;
bool failed() const override;
// Media specific interface
void setPath(const std::string &p);
std::string path() const;
Session *session() const;
protected:
void init() override;
static void loadSession(const std::string& filename, SessionSource *source);
Surface *sessionsurface_;
std::string path_;
Session *session_;
std::atomic<bool> loadFailed_;
std::atomic<bool> loadFinished_;
};
#endif // SESSIONSOURCE_H

View File

@@ -5,6 +5,7 @@
#include "Primitives.h"
#include "Mesh.h"
#include "Source.h"
#include "MediaSource.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"

View File

@@ -108,10 +108,10 @@ void Settings::Save()
};
recent->InsertEndChild(recentsession);
XMLElement *recentmedia = xmlDoc.NewElement( "Media" );
recentmedia->SetAttribute("path", application.recentMedia.path.c_str());
for(auto it = application.recentMedia.filenames.begin();
it != application.recentMedia.filenames.end(); it++) {
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++) {
XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() );
@@ -245,21 +245,21 @@ void Settings::Load()
}
}
// recent media uri
XMLElement * pMedia = pElement->FirstChildElement("Media");
if (pMedia)
XMLElement * pImport = pElement->FirstChildElement("Import");
if (pImport)
{
const char *path_ = pMedia->Attribute("path");
const char *path_ = pImport->Attribute("path");
if (path_)
application.recentMedia.path = std::string(path_);
application.recentImport.path = std::string(path_);
else
application.recentMedia.path = SystemToolkit::home_path();
application.recentMedia.filenames.clear();
XMLElement* path = pMedia->FirstChildElement("path");
application.recentImport.path = SystemToolkit::home_path();
application.recentImport.filenames.clear();
XMLElement* path = pImport->FirstChildElement("path");
for( ; path ; path = path->NextSiblingElement())
{
const char *p = path->GetText();
if (p)
application.recentMedia.push( std::string (p) );
application.recentImport.push( std::string (p) );
}
}
}

View File

@@ -82,7 +82,7 @@ struct Application
// recent files histories
History recentSessions;
History recentMedia;
History recentImport;
Application() : name(APP_NAME){
scale = 1.f;

View File

@@ -6,14 +6,11 @@
#include "defines.h"
#include "FrameBuffer.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Resource.h"
#include "Primitives.h"
#include "Mesh.h"
#include "MediaPlayer.h"
#include "SearchVisitor.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Log.h"
Source::Source() : initialized_(false), need_update_(true)
@@ -32,13 +29,18 @@ Source::Source() : initialized_(false), need_update_(true)
groups_[View::MIXING]->visible_ = false;
Frame *frame = new Frame(Frame::ROUND_THIN);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.9f);
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.7f);
groups_[View::MIXING]->attach(frame);
groups_[View::MIXING]->scale_ = glm::vec3(0.15f, 0.15f, 1.f);
groups_[View::MIXING]->translation_ = glm::vec3(-1.f, 1.f, 0.f);
overlays_[View::MIXING] = new Group;
overlays_[View::MIXING]->translation_.z = 0.1;
overlays_[View::MIXING]->visible_ = false;
frame = new Frame(Frame::ROUND_LARGE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
overlays_[View::MIXING]->attach(frame);
groups_[View::MIXING]->attach(overlays_[View::MIXING]);
// default geometry nodes
@@ -46,12 +48,31 @@ Source::Source() : initialized_(false), need_update_(true)
groups_[View::GEOMETRY]->visible_ = false;
frame = new Frame(Frame::SHARP_THIN);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.9f);
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.7f);
groups_[View::GEOMETRY]->attach(frame);
overlays_[View::GEOMETRY] = new Group;
overlays_[View::GEOMETRY]->translation_.z = 0.15;
overlays_[View::GEOMETRY]->visible_ = false;
frame = new Frame(Frame::SHARP_LARGE);
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
overlays_[View::GEOMETRY]->attach(frame);
resize_handle_ = new Handles(Handles::RESIZE);
resize_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
resize_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(resize_handle_);
resize_H_handle_ = new Handles(Handles::RESIZE_H);
resize_H_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
resize_H_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(resize_H_handle_);
resize_V_handle_ = new Handles(Handles::RESIZE_V);
resize_V_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
resize_V_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(resize_V_handle_);
rotate_handle_ = new Handles(Handles::ROTATE);
rotate_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
rotate_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(rotate_handle_);
groups_[View::GEOMETRY]->attach(overlays_[View::GEOMETRY]);
// default mixing nodes
@@ -59,7 +80,7 @@ Source::Source() : initialized_(false), need_update_(true)
groups_[View::LAYER]->visible_ = false;
frame = new Frame(Frame::ROUND_SHADOW);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.9f);
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.8f);
groups_[View::LAYER]->attach(frame);
overlays_[View::LAYER] = new Group;
@@ -72,10 +93,6 @@ Source::Source() : initialized_(false), need_update_(true)
rendershader_ = new ImageProcessingShader;
renderbuffer_ = nullptr;
rendersurface_ = nullptr;
resize_handle_ = nullptr;
resize_H_handle_ = nullptr;
resize_V_handle_ = nullptr;
rotate_handle_ = nullptr;
}
Source::~Source()
@@ -117,6 +134,10 @@ void Source::setOverlayVisible(bool on)
void Source::update(float dt)
{
// keep delta-t
dt_ = dt;
// update nodes if needed
if (need_update_)
{
// ADJUST alpha based on MIXING node
@@ -173,175 +194,4 @@ bool hasNode::operator()(const Source* elem) const
return false;
}
MediaSource::MediaSource() : Source(), path_("")
{
// create media player
mediaplayer_ = new MediaPlayer;
// create media surface:
// - textured with original texture from media player
// - crop & repeat UV can be managed here
// - additional custom shader can be associated
mediasurface_ = new Surface(rendershader_);
// extra overlay for mixing view
Frame *frame = new Frame(Frame::ROUND_LARGE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
overlays_[View::MIXING]->attach(frame);
// extra overlays for geometry view
frame = new Frame(Frame::SHARP_LARGE);
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
overlays_[View::GEOMETRY]->attach(frame);
resize_handle_ = new Handles(Handles::RESIZE);
resize_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
resize_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(resize_handle_);
resize_H_handle_ = new Handles(Handles::RESIZE_H);
resize_H_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
resize_H_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(resize_H_handle_);
resize_V_handle_ = new Handles(Handles::RESIZE_V);
resize_V_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
resize_V_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(resize_V_handle_);
rotate_handle_ = new Handles(Handles::ROTATE);
rotate_handle_->color = glm::vec4( COLOR_DEFAULT_SOURCE, 1.f);
rotate_handle_->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(rotate_handle_);
}
MediaSource::~MediaSource()
{
// TODO delete media surface with visitor
delete mediasurface_;
delete mediaplayer_;
// TODO verify that all surfaces and node is deleted in Source destructor
}
void MediaSource::setPath(const std::string &p)
{
path_ = p;
mediaplayer_->open(path_);
mediaplayer_->play(true);
Log::Notify("Opening %s", p.c_str());
}
std::string MediaSource::path() const
{
return path_;
}
MediaPlayer *MediaSource::mediaplayer() const
{
return mediaplayer_;
}
bool MediaSource::failed() const
{
return mediaplayer_->failed();
}
void MediaSource::init()
{
if ( mediaplayer_->isOpen() ) {
// update video
mediaplayer_->update();
// once the texture of media player is created
if (mediaplayer_->texture() != Resource::getTextureBlack()) {
// get the texture index from media player, apply it to the media surface
mediasurface_->setTextureIndex( mediaplayer_->texture() );
// create Frame buffer matching size of media player
float height = float(mediaplayer()->width()) / mediaplayer()->aspectRatio();
renderbuffer_ = new FrameBuffer(mediaplayer()->width(), (uint)height);
// // setup shader resolution for texture 0
// rendershader_->iChannelResolution[0] = glm::vec3(mediaplayer()->width(), mediaplayer()->height(), 0.f);
// create the surfaces to draw the frame buffer in the views
// TODO Provide the source custom effect shader
rendersurface_ = new FrameBufferSurface(renderbuffer_, blendingshader_);
groups_[View::RENDERING]->attach(rendersurface_);
groups_[View::GEOMETRY]->attach(rendersurface_);
groups_[View::MIXING]->attach(rendersurface_);
groups_[View::LAYER]->attach(rendersurface_);
// for mixing view, add another surface to overlay (for stippled view in transparency)
Surface *surfacemix = new FrameBufferSurface(renderbuffer_);
ImageShader *is = static_cast<ImageShader *>(surfacemix->shader());
if (is) is->stipple = 1.0;
groups_[View::MIXING]->attach(surfacemix);
groups_[View::LAYER]->attach(surfacemix);
if (mediaplayer_->duration() == GST_CLOCK_TIME_NONE)
overlays_[View::MIXING]->attach( new Mesh("mesh/icon_image.ply") );
else
overlays_[View::MIXING]->attach( new Mesh("mesh/icon_video.ply") );
// scale all mixing nodes to match aspect ratio of the media
for (NodeSet::iterator node = groups_[View::MIXING]->begin();
node != groups_[View::MIXING]->end(); node++) {
(*node)->scale_.x = mediaplayer_->aspectRatio();
}
for (NodeSet::iterator node = groups_[View::GEOMETRY]->begin();
node != groups_[View::GEOMETRY]->end(); node++) {
(*node)->scale_.x = mediaplayer_->aspectRatio();
}
for (NodeSet::iterator node = groups_[View::LAYER]->begin();
node != groups_[View::LAYER]->end(); node++) {
(*node)->scale_.x = mediaplayer_->aspectRatio();
}
// done init once and for all
initialized_ = true;
// make visible
groups_[View::RENDERING]->visible_ = true;
groups_[View::MIXING]->visible_ = true;
groups_[View::GEOMETRY]->visible_ = true;
groups_[View::LAYER]->visible_ = true;
}
}
}
void MediaSource::render()
{
if (!initialized_)
init();
else {
// update video
mediaplayer_->update();
// render the media player into frame buffer
static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f);
renderbuffer_->begin();
mediasurface_->draw(glm::identity<glm::mat4>(), projection);
renderbuffer_->end();
}
}
FrameBuffer *MediaSource::frame() const
{
if (initialized_ && renderbuffer_)
{
return renderbuffer_;
}
else {
static FrameBuffer *black = new FrameBuffer(640,480);
return black;
}
}
void MediaSource::accept(Visitor& v)
{
Source::accept(v);
v.visit(*this);
}

View File

@@ -14,6 +14,7 @@ class FrameBuffer;
class FrameBufferSurface;
class MediaPlayer;
class Surface;
class Session;
class Frame;
class Source
@@ -43,12 +44,12 @@ public:
// a Source has a shader to control mixing effects
inline ImageShader *blendingShader() const { return blendingshader_; }
// a Source shall be updated before displayed (Mixing, Geometry and Layer)
void update (float dt);
// touch to request update
inline void touch() { need_update_ = true; }
// a Source shall be updated before displayed (Mixing, Geometry and Layer)
void update (float dt);
// accept all kind of visitors
virtual void accept (Visitor& v);
@@ -95,6 +96,7 @@ protected:
// update need
bool need_update_;
float dt_;
};
// TODO SourceSet sorted by shader
@@ -122,32 +124,5 @@ private:
Node *_n;
};
class MediaSource : public Source
{
public:
MediaSource();
~MediaSource();
// implementation of source API
FrameBuffer *frame() const override;
void render() override;
void accept (Visitor& v) override;
bool failed() const override;
// Media specific interface
void setPath(const std::string &p);
std::string path() const;
MediaPlayer *mediaplayer() const;
protected:
void init() override;
Surface *mediasurface_;
std::string path_;
MediaPlayer *mediaplayer_;
};
// TODO dedicated source .cpp .h files for MediaSource
#endif // SOURCE_H

View File

@@ -44,6 +44,7 @@
#include "Mixer.h"
#include "FrameBuffer.h"
#include "MediaPlayer.h"
#include "MediaSource.h"
#include "PickingVisitor.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
@@ -58,14 +59,14 @@ void ShowAbout(bool* p_open);
// static objects for multithreaded file dialog
static std::atomic<bool> FileDialogPending_ = false;
static std::atomic<bool> FileDialogLoadFinished_ = false;
static std::atomic<bool> FileDialogSaveFinished_ = false;
static std::string FileDialogFilename_ = "";
static std::atomic<bool> SessionFileDialogLoadFinished_ = false;
static std::atomic<bool> SessionFileDialogSaveFinished_ = false;
static std::string SessionFileDialogFilename_ = "";
static void FileDialogOpen(std::string path)
static void SessionFileDialogOpen(std::string path)
{
FileDialogPending_ = true;
FileDialogLoadFinished_ = false;
SessionFileDialogLoadFinished_ = false;
char const * open_file_name;
char const * open_pattern[1] = { "*.vmx" };
@@ -73,17 +74,17 @@ static void FileDialogOpen(std::string path)
open_file_name = tinyfd_openFileDialog( "Open a session file", path.c_str(), 1, open_pattern, "vimix session", 0);
if (!open_file_name)
FileDialogFilename_ = "";
SessionFileDialogFilename_ = "";
else
FileDialogFilename_ = std::string( open_file_name );
SessionFileDialogFilename_ = std::string( open_file_name );
FileDialogLoadFinished_ = true;
SessionFileDialogLoadFinished_ = true;
}
static void FileDialogSave(std::string path)
static void SessionFileDialogSave(std::string path)
{
FileDialogPending_ = true;
FileDialogSaveFinished_ = false;
SessionFileDialogSaveFinished_ = false;
char const * save_file_name;
char const * save_pattern[1] = { "*.vmx" };
@@ -91,37 +92,32 @@ static void FileDialogSave(std::string path)
save_file_name = tinyfd_saveFileDialog( "Save a session file", path.c_str(), 1, save_pattern, "vimix session");
if (!save_file_name)
FileDialogFilename_ = "";
SessionFileDialogFilename_ = "";
else
{
FileDialogFilename_ = std::string( save_file_name );
SessionFileDialogFilename_ = std::string( save_file_name );
// check extension
std::string extension = FileDialogFilename_.substr(FileDialogFilename_.find_last_of(".") + 1);
std::string extension = SessionFileDialogFilename_.substr(SessionFileDialogFilename_.find_last_of(".") + 1);
if (extension != "vmx")
FileDialogFilename_ += ".vmx";
SessionFileDialogFilename_ += ".vmx";
}
FileDialogSaveFinished_ = true;
SessionFileDialogSaveFinished_ = true;
}
static std::atomic<bool> MediaDialogFinished_ = false;
static std::string MediaDialogUri_ = "";
static void MediaDialogOpen(std::string path)
static void ImportFileDialogOpen(char *filename, const std::string &path)
{
FileDialogPending_ = true;
MediaDialogFinished_ = false;
if (FileDialogPending_)
return;
FileDialogPending_ = true;
char const * open_file_name;
char const * open_pattern[15] = { "*.mp4", "*.mpg", "*.avi", "*.mov", "*.mkv", "*.webm", "*.mod", "*.wmv", "*.mxf", "*.ogg", "*.flv", "*.asf", "*.jpg", "*.png", "*.gif" };
char const * open_pattern[17] = { "*vmx", "*.mp4", "*.mpg", "*.avi", "*.mov", "*.mkv", "*.webm", "*.mod", "*.wmv", "*.mxf", "*.ogg", "*.flv", "*.asf", "*.jpg", "*.png", "*.gif", "*.svg" };
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Open a Media file", path.c_str(), 15, open_pattern, "Video or image", 0);
open_file_name = tinyfd_openFileDialog( "Import a file", path.c_str(), 17, open_pattern, "All supported formats", 0);
sprintf(filename, "%s", open_file_name);
if (!open_file_name)
MediaDialogUri_ = "";
else
MediaDialogUri_ = std::string( open_file_name );
MediaDialogFinished_ = true;
FileDialogPending_ = false;
}
UserInterface::UserInterface()
@@ -211,7 +207,7 @@ void UserInterface::handleKeyboard()
}
else if (ImGui::IsKeyPressed( GLFW_KEY_O )) {
// Open session
std::thread (FileDialogOpen, Settings::application.recentSessions.path).detach();
std::thread (SessionFileDialogOpen, Settings::application.recentSessions.path).detach();
navigator.hidePannel();
}
else if (ImGui::IsKeyPressed( GLFW_KEY_S )) {
@@ -378,37 +374,29 @@ void UserInterface::NewFrame()
handleMouse();
// handle FileDialog
if (FileDialogLoadFinished_) {
FileDialogLoadFinished_ = false;
if (SessionFileDialogLoadFinished_) {
SessionFileDialogLoadFinished_ = false;
FileDialogPending_ = false;
if (!FileDialogFilename_.empty()) {
Mixer::manager().open(FileDialogFilename_);
Settings::application.recentSessions.path = SystemToolkit::path_filename(FileDialogFilename_);
if (!SessionFileDialogFilename_.empty()) {
Mixer::manager().open(SessionFileDialogFilename_);
Settings::application.recentSessions.path = SystemToolkit::path_filename(SessionFileDialogFilename_);
}
}
if (FileDialogSaveFinished_) {
FileDialogSaveFinished_ = false;
if (SessionFileDialogSaveFinished_) {
SessionFileDialogSaveFinished_ = false;
FileDialogPending_ = false;
if (!FileDialogFilename_.empty()) {
Mixer::manager().saveas(FileDialogFilename_);
Settings::application.recentSessions.path = SystemToolkit::path_filename(FileDialogFilename_);
}
}
if (MediaDialogFinished_) {
MediaDialogFinished_ = false;
FileDialogPending_ = false;
if (!MediaDialogUri_.empty()) {
navigator.setMediaUri(MediaDialogUri_);
Settings::application.recentMedia.path = SystemToolkit::path_filename(MediaDialogUri_);
if (!SessionFileDialogFilename_.empty()) {
Mixer::manager().saveas(SessionFileDialogFilename_);
Settings::application.recentSessions.path = SystemToolkit::path_filename(SessionFileDialogFilename_);
}
}
// overlay when disabled
// overlay to ensure file dialog is modal
if (FileDialogPending_){
ImGui::OpenPopup("Busy");
if (ImGui::BeginPopupModal("Busy", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("Close dialog to resume...");
ImGui::Text("Close file dialog box to resume.");
ImGui::EndPopup();
}
}
@@ -480,7 +468,7 @@ void UserInterface::showMenuFile()
{
if (ImGui::MenuItem( ICON_FA_FILE_UPLOAD " Open", "Ctrl+O")) {
// launch file dialog to open a session file
std::thread (FileDialogOpen, Settings::application.recentSessions.path).detach();
std::thread (SessionFileDialogOpen, Settings::application.recentSessions.path).detach();
navigator.hidePannel();
}
if (ImGui::MenuItem( ICON_FA_FILE_DOWNLOAD " Save", "Ctrl+S")) {
@@ -488,7 +476,7 @@ void UserInterface::showMenuFile()
navigator.hidePannel();
}
if (ImGui::MenuItem( ICON_FA_FOLDER_OPEN " Save as")) {
std::thread (FileDialogSave, Settings::application.recentSessions.path).detach();
std::thread (SessionFileDialogSave, Settings::application.recentSessions.path).detach();
navigator.hidePannel();
}
@@ -1061,10 +1049,6 @@ void Navigator::RenderSourcePannel(Source *s)
ImGui::End();
}
void Navigator::setMediaUri(std::string path)
{
sprintf(media_path_, "%s", path.c_str());
}
void Navigator::RenderNewPannel()
{
@@ -1082,55 +1066,61 @@ void Navigator::RenderNewPannel()
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
static int new_source_type = 0;
ImGui::Combo("Type", &new_source_type, "Media\0Render\0Clone\0");
ImGui::Combo("Origin", &new_source_type, "File\0Software\0Hardware\0");
// Media Source creation
if (new_source_type == 0) {
// helper
ImGui::SetCursorPosX(pannel_width - 30 + IMGUI_RIGHT_ALIGN);
ImGuiToolkit::HelpMarker("A Media source displays an image or a video file.");
// browse folder
ImGuiToolkit::HelpMarker("Create a source from a file:\n- Video (*.mpg, *mov, *.avi, etc.)\n- Image (*.jpg, *.png, etc.)\n- Vector graphics (*.svg)\n- vimix session (*.vmx)\n\nEquivalent to dropping the file in the workspace.");
// filename of the media to open
static char filename[2048];
// browse for a filename
if (ImGuiToolkit::ButtonIcon(2, 5)) {
std::thread (MediaDialogOpen, Settings::application.recentMedia.path).detach();
std::thread (ImportFileDialogOpen, filename, Settings::application.recentImport.path).detach();
}
// combo recent
// combo of recent media filenames
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##RecentMedia", "Select recent"))
if (ImGui::BeginCombo("##RecentImport", "Select recent"))
{
std::for_each(Settings::application.recentMedia.filenames.begin(),
Settings::application.recentMedia.filenames.end(), [](std::string& path) {
std::for_each(Settings::application.recentImport.filenames.begin(),
Settings::application.recentImport.filenames.end(), [](std::string& path) {
int right = MIN( 40, path.size());
if (ImGui::Selectable( path.substr( path.size() - right ).c_str() )) {
UserInterface::manager().navigator.setMediaUri(path);
sprintf(filename, "%s", path.c_str());
}
});
ImGui::EndCombo();
}
// uri text entry
// filename text entry - [Return] to validate
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::InputText("Path", media_path_, IM_ARRAYSIZE(media_path_), ImGuiInputTextFlags_EnterReturnsTrue) ) {
Mixer::manager().createSourceMedia(media_path_);
if (ImGui::InputText("Path", filename, IM_ARRAYSIZE(filename), ImGuiInputTextFlags_EnterReturnsTrue) ) {
Mixer::manager().createSourceFile( std::string(filename) );
selected_button[NAV_NEW] = false;
}
// Validate button
// or press Validate button
ImGui::Text(" ");
if ( ImGui::Button("Create !", ImVec2(pannel_width - padding_width, 0)) ) {
if ( SystemToolkit::file_exists(media_path_) ) {
Mixer::manager().createSourceMedia(media_path_);
selected_button[NAV_NEW] = false;
}
Mixer::manager().createSourceFile( std::string(filename) );
selected_button[NAV_NEW] = false;
}
}
// Render Source creator
else if (new_source_type == 1){
// helper
ImGui::SetCursorPosX(pannel_width - 30 + IMGUI_RIGHT_ALIGN);
ImGuiToolkit::HelpMarker("A Render source replicates the rendering of the output.");
ImGuiToolkit::HelpMarker("Create a source from a software algorithm or from vimix objects.");
}
// Hardware
else {
// helper
ImGui::SetCursorPosX(pannel_width - 30 + IMGUI_RIGHT_ALIGN);
ImGuiToolkit::HelpMarker("A Clone source duplicates the content of another source.");
ImGuiToolkit::HelpMarker("Create a source capturing images from an external hardware.");
}
}

View File

@@ -33,8 +33,6 @@ class Navigator
void RenderNewPannel();
void RenderMainPannel();
char media_path_[1024];
public:
Navigator();
@@ -43,7 +41,6 @@ public:
void showPannelSource(int index);
void Render();
void setMediaUri(std::string path);
};
class ToolBox

View File

@@ -304,6 +304,7 @@ void GeometryView::grab (glm::vec2 from, glm::vec2 to, Source *s, std::pair<Node
S_resize.y = S_resize.x;
sourceNode->scale_ = start_scale * S_resize;
Log::Info(" resize ( %.1f, %.1f ) ", S_resize.x, S_resize.y);
// glm::vec3 factor = S_resize * glm::vec3(0.5f, 0.5f, 1.f);
//// glm::vec3 factor = S_resize * glm::vec3(1.f, 1.f, 1.f);
//// factor *= glm::sign( glm::vec3(pick.second, 1.f) );

View File

@@ -8,7 +8,7 @@
#define APP_VERSION_MINOR 1
#define XML_VERSION_MAJOR 0
#define XML_VERSION_MINOR 1
#define MAX_RECENT_HISTORY 10
#define MAX_RECENT_HISTORY 16
#define MINI(a, b) (((a) < (b)) ? (a) : (b))
#define MAXI(a, b) (((a) > (b)) ? (a) : (b))