mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-12 18:59:59 +01:00
Recursive load of SessionFile in a Session is now detected by filename and prevented. Deeper level of integration of sessionfile inside session is thus possible (set to 3). Sessions now have an id, allowing to reference them by id in the logs. Terminology is clarified between level and recursion.
405 lines
11 KiB
C++
405 lines
11 KiB
C++
/*
|
|
* This file is part of vimix - video live mixer
|
|
*
|
|
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
#include <algorithm>
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
#include "defines.h"
|
|
#include "Log.h"
|
|
#include "FrameBuffer.h"
|
|
#include "ImageShader.h"
|
|
#include "ImageProcessingShader.h"
|
|
#include "Resource.h"
|
|
#include "Decorations.h"
|
|
#include "SearchVisitor.h"
|
|
#include "Session.h"
|
|
#include "SessionCreator.h"
|
|
#include "Mixer.h"
|
|
|
|
#include "SessionSource.h"
|
|
|
|
SessionSource::SessionSource(uint64_t id) : Source(id), failed_(false), timer_(0), paused_(false)
|
|
{
|
|
session_ = new Session;
|
|
}
|
|
|
|
SessionSource::~SessionSource()
|
|
{
|
|
// delete session
|
|
if (session_)
|
|
delete session_;
|
|
}
|
|
|
|
Session *SessionSource::detach()
|
|
{
|
|
// remember pointer to give away
|
|
Session *giveaway = session_;
|
|
|
|
// work on a new session
|
|
session_ = new Session;
|
|
|
|
// un-ready
|
|
ready_ = false;
|
|
|
|
// ask to delete me
|
|
failed_ = true;
|
|
|
|
// lost ref to previous session: to be deleted elsewhere...
|
|
return giveaway;
|
|
}
|
|
|
|
bool SessionSource::failed() const
|
|
{
|
|
return failed_;
|
|
}
|
|
|
|
uint SessionSource::texture() const
|
|
{
|
|
if (session_ && session_->frame())
|
|
return session_->frame()->texture();
|
|
else
|
|
return Resource::getTextureBlack();
|
|
}
|
|
|
|
void SessionSource::setActive (bool on)
|
|
{
|
|
Source::setActive(on);
|
|
|
|
// change status of session (recursive change of internal sources)
|
|
if (session_) {
|
|
session_->setActive(active_);
|
|
|
|
// change visibility of active surface (show preview of session when inactive)
|
|
if (activesurface_) {
|
|
if (active_)
|
|
activesurface_->setTextureIndex(Resource::getTextureTransparent());
|
|
else
|
|
activesurface_->setTextureIndex(session_->frame()->texture());
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionSource::update(float dt)
|
|
{
|
|
Source::update(dt);
|
|
|
|
if (session_ == nullptr)
|
|
return;
|
|
|
|
// update content
|
|
if (active_ && !paused_) {
|
|
session_->update(dt);
|
|
timer_ += guint64(dt * 1000.f) * GST_USECOND;
|
|
}
|
|
|
|
// delete a source which failed
|
|
if (session_->failedSource() != nullptr) {
|
|
session_->deleteSource(session_->failedSource());
|
|
// fail session if all sources failed
|
|
if ( session_->numSource() < 1)
|
|
failed_ = true;
|
|
}
|
|
|
|
}
|
|
|
|
void SessionSource::play (bool on)
|
|
{
|
|
paused_ = !on;
|
|
|
|
if (session_) {
|
|
for( SourceList::iterator it = session_->begin(); it != session_->end(); ++it)
|
|
(*it)->setActive(!paused_);
|
|
}
|
|
}
|
|
|
|
void SessionSource::replay ()
|
|
{
|
|
if (session_) {
|
|
for( SourceList::iterator it = session_->begin(); it != session_->end(); ++it)
|
|
(*it)->replay();
|
|
timer_ = 0;
|
|
}
|
|
}
|
|
|
|
SessionFileSource::SessionFileSource(uint64_t id) : SessionSource(id), path_(""), initialized_(false), wait_for_sources_(false)
|
|
{
|
|
// specific node for transition view
|
|
groups_[View::TRANSITION]->visible_ = false;
|
|
groups_[View::TRANSITION]->scale_ = glm::vec3(0.1f, 0.1f, 1.f);
|
|
groups_[View::TRANSITION]->translation_ = glm::vec3(-1.f, 0.f, 0.f);
|
|
|
|
frames_[View::TRANSITION] = new Switch;
|
|
Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::DROP);
|
|
frame->translation_.z = 0.1;
|
|
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
|
|
frames_[View::TRANSITION]->attach(frame);
|
|
frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::DROP);
|
|
frame->translation_.z = 0.01;
|
|
frame->color = glm::vec4( COLOR_TRANSITION_SOURCE, 1.f);
|
|
frames_[View::TRANSITION]->attach(frame);
|
|
groups_[View::TRANSITION]->attach(frames_[View::TRANSITION]);
|
|
|
|
overlays_[View::TRANSITION] = new Group;
|
|
overlays_[View::TRANSITION]->translation_.z = 0.1;
|
|
overlays_[View::TRANSITION]->visible_ = false;
|
|
|
|
Symbol *loader = new Symbol(Symbol::DOTS);
|
|
loader->scale_ = glm::vec3(2.f, 2.f, 1.f);
|
|
loader->update_callbacks_.push_back(new InfiniteGlowCallback);
|
|
overlays_[View::TRANSITION]->attach(loader);
|
|
Symbol *center = new Symbol(Symbol::CIRCLE_POINT, glm::vec3(0.f, -1.05f, 0.1f));
|
|
overlays_[View::TRANSITION]->attach(center);
|
|
groups_[View::TRANSITION]->attach(overlays_[View::TRANSITION]);
|
|
|
|
// set symbol
|
|
symbol_ = new Symbol(Symbol::SESSION, glm::vec3(0.75f, 0.75f, 0.01f));
|
|
symbol_->scale_.y = 1.5f;
|
|
|
|
}
|
|
|
|
void SessionFileSource::load(const std::string &p, uint level)
|
|
{
|
|
path_ = p;
|
|
|
|
// delete session
|
|
if (session_) {
|
|
delete session_;
|
|
session_ = nullptr;
|
|
}
|
|
|
|
// init session
|
|
if ( path_.empty() ) {
|
|
// empty session
|
|
session_ = new Session;
|
|
Log::Warning("Empty Session filename provided.");
|
|
}
|
|
else {
|
|
// launch a thread to load the session file
|
|
sessionLoader_ = std::async(std::launch::async, Session::load, path_, level);
|
|
Log::Notify("Opening %s", p.c_str());
|
|
}
|
|
|
|
// will be ready after init and one frame rendered
|
|
initialized_ = false;
|
|
ready_ = false;
|
|
}
|
|
|
|
void SessionFileSource::init()
|
|
{
|
|
// init is first about getting the loaded session
|
|
if (session_ == nullptr) {
|
|
// did the loader finish ?
|
|
if (sessionLoader_.wait_for(std::chrono::milliseconds(4)) == std::future_status::ready) {
|
|
session_ = sessionLoader_.get();
|
|
if (session_ == nullptr)
|
|
failed_ = true;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (wait_for_sources_) {
|
|
|
|
// force update of of all sources
|
|
active_ = true;
|
|
touch();
|
|
|
|
// update to draw framebuffer
|
|
session_->update(dt_);
|
|
|
|
// if all sources are ready, done with initialization!
|
|
auto unintitializedsource = std::find_if_not(session_->begin(), session_->end(), Source::isInitialized);
|
|
if (unintitializedsource == session_->end()) {
|
|
// done init
|
|
wait_for_sources_ = false;
|
|
initialized_ = true;
|
|
Log::Info("Source '%s' linked to Session %s.", name().c_str(), std::to_string(session_->id()).c_str());
|
|
}
|
|
}
|
|
else if ( !failed_ ) {
|
|
|
|
// set resolution
|
|
session_->setResolution( session_->config(View::RENDERING)->scale_ );
|
|
|
|
// update to draw framebuffer
|
|
session_->update(dt_);
|
|
|
|
// get the texture index from framebuffer of session, apply it to the surface
|
|
texturesurface_->setTextureIndex( session_->frame()->texture() );
|
|
|
|
// create Frame buffer matching size of session
|
|
FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution() );
|
|
|
|
// set the renderbuffer of the source and attach rendering nodes
|
|
attach(renderbuffer);
|
|
|
|
// wait for all sources to init
|
|
if (session_->numSource() > 0)
|
|
wait_for_sources_ = true;
|
|
else {
|
|
initialized_ = true;
|
|
Log::Info("Source '%s' linked to new Session %s.", name().c_str(), std::to_string(session_->id()).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (initialized_)
|
|
{
|
|
// remove the loading icon
|
|
Node *loader = overlays_[View::TRANSITION]->back();
|
|
overlays_[View::TRANSITION]->detach(loader);
|
|
delete loader;
|
|
// request deep update to reorder session_
|
|
++View::need_deep_update_;
|
|
// run update to redraw framebuffer (after reorder)
|
|
session_->update(dt_);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
void SessionFileSource::accept(Visitor& v)
|
|
{
|
|
Source::accept(v);
|
|
if (!failed())
|
|
v.visit(*this);
|
|
}
|
|
|
|
glm::ivec2 SessionFileSource::icon() const
|
|
{
|
|
return glm::ivec2(ICON_SOURCE_SESSION);
|
|
}
|
|
|
|
std::string SessionFileSource::info() const
|
|
{
|
|
return "Session File";
|
|
}
|
|
|
|
|
|
SessionGroupSource::SessionGroupSource(uint64_t id) : SessionSource(id), resolution_(glm::vec3(0.f))
|
|
{
|
|
// // redo frame for layers view
|
|
// frames_[View::LAYER]->clear();
|
|
|
|
// // Groups in LAYER have an additional border
|
|
// Group *group = new Group;
|
|
// Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::PERSPECTIVE);
|
|
// frame->translation_.z = 0.1;
|
|
// frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
|
|
// group->attach(frame);
|
|
// Frame *persp = new Frame(Frame::GROUP, Frame::THIN, Frame::NONE);
|
|
// persp->translation_.z = 0.1;
|
|
// persp->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
|
|
// group->attach(persp);
|
|
// frames_[View::LAYER]->attach(group);
|
|
|
|
// group = new Group;
|
|
// frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::PERSPECTIVE);
|
|
// frame->translation_.z = 0.1;
|
|
// frame->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
|
|
// group->attach(frame);
|
|
// persp = new Frame(Frame::GROUP, Frame::LARGE, Frame::NONE);
|
|
// persp->translation_.z = 0.1;
|
|
// persp->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
|
|
// group->attach(persp);
|
|
// frames_[View::LAYER]->attach(group);
|
|
|
|
// set symbol
|
|
symbol_ = new Symbol(Symbol::GROUP, glm::vec3(0.75f, 0.75f, 0.01f));
|
|
symbol_->scale_.y = 1.5f;
|
|
}
|
|
|
|
void SessionGroupSource::init()
|
|
{
|
|
if ( resolution_.x > 0.f && resolution_.y > 0.f ) {
|
|
|
|
session_->setResolution( resolution_ );
|
|
|
|
// update to draw framebuffer
|
|
session_->update( dt_ );
|
|
|
|
// get the texture index from framebuffer of session, apply it to the surface
|
|
texturesurface_->setTextureIndex( session_->frame()->texture() );
|
|
|
|
// create Frame buffer matching size of session
|
|
FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution() );
|
|
|
|
// set the renderbuffer of the source and attach rendering nodes
|
|
attach(renderbuffer);
|
|
|
|
// deep update to reorder
|
|
++View::need_deep_update_;
|
|
|
|
// done init
|
|
Log::Info("Source Group created (%d x %d).", int(renderbuffer->resolution().x), int(renderbuffer->resolution().y) );
|
|
}
|
|
}
|
|
|
|
void SessionGroupSource::setSession (Session *s)
|
|
{
|
|
if (s) {
|
|
if ( session_ )
|
|
delete session_;
|
|
session_ = s;
|
|
resolution_ = s->frame()->resolution();
|
|
}
|
|
}
|
|
|
|
bool SessionGroupSource::import(Source *source)
|
|
{
|
|
bool ret = false;
|
|
|
|
if ( session_ )
|
|
{
|
|
SourceList::iterator its = session_->addSource(source);
|
|
if (its != session_->end())
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void SessionGroupSource::accept(Visitor& v)
|
|
{
|
|
Source::accept(v);
|
|
if (!failed())
|
|
v.visit(*this);
|
|
}
|
|
|
|
glm::ivec2 SessionGroupSource::icon() const
|
|
{
|
|
return glm::ivec2(ICON_SOURCE_GROUP);
|
|
}
|
|
|
|
std::string SessionGroupSource::info() const
|
|
{
|
|
return "Session group";
|
|
}
|