/* * This file is part of vimix - video live mixer * * **Copyright** (C) 2019-2022 Bruno Herbelin * * 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 . **/ #include #include #include #include "Log.h" #include "defines.h" #include "Resource.h" #include "Decorations.h" #include "Visitor.h" #include "FrameBuffer.h" #include "FrameBufferFilter.h" #include "DelayFilter.h" #include "ImageFilter.h" #include "CloneSource.h" CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin), paused_(false), filter_(nullptr) { // initial name copies the origin name: diplucates are namanged in session name_ = origin->name(); // set symbol symbol_ = new Symbol(Symbol::CLONE, glm::vec3(0.75f, 0.75f, 0.01f)); symbol_->scale_.y = 1.5f; // init connecting line connection_ = new DotLine; connection_->color = glm::vec4(COLOR_HIGHLIGHT_SOURCE, 0.6f); connection_->target = origin->groups_[View::MIXING]->translation_; groups_[View::MIXING]->attach(connection_); // default to pass-through filter filter_ = new PassthroughFilter; } CloneSource::~CloneSource() { if (origin_) origin_->clones_.remove(this); delete filter_; } void CloneSource::init() { if (origin_ && origin_->ready_ && origin_->mode_ > Source::UNINITIALIZED && origin_->renderbuffer_) { // create render Frame buffer matching size of images FrameBuffer *renderbuffer = new FrameBuffer( origin_->frame()->resolution(), origin_->frame()->flags() ); // set the renderbuffer of the source and attach rendering nodes attach(renderbuffer); // force update of activation mode active_ = true; // deep update to reorder ++View::need_deep_update_; // done init Log::Info("Source '%s' cloning source '%s'.", name().c_str(), origin_->name().c_str() ); } } void CloneSource::render() { if ( renderbuffer_ == nullptr ) init(); else { // render filter image filter_->draw( origin_->frame() ); // ensure correct output texture is displayed (could have changed if filter changed) texturesurface_->setTextureIndex( filter_->texture() ); // detect resampling (change of resolution in filter) if ( renderbuffer_->resolution() != filter_->resolution() ) { renderbuffer_->resize( filter_->resolution() ); // FrameBuffer *renderbuffer = new FrameBuffer( filter_->resolution(), origin_->frame()->flags() ); // attach(renderbuffer); } // render textured surface into frame buffer // NB: this also applies the color correction shader renderbuffer_->begin(); texturesurface_->draw(glm::identity(), renderbuffer_->projection()); renderbuffer_->end(); ready_ = true; } } void CloneSource::setActive (bool on) { bool was_active = active_; // try to activate (may fail if source is cloned) Source::setActive(on); if (origin_) { if ( mode_ > Source::UNINITIALIZED ) origin_->touch(); // enable / disable filtering if ( active_ != was_active ) filter_->setEnabled( active_ ); } } void CloneSource::update(float dt) { Source::update(dt); if (origin_) { if (!paused_ && active_) filter_->update(dt); // update connection line target to position of origin source connection_->target = glm::inverse( GlmToolkit::transform(groups_[View::MIXING]->translation_, glm::vec3(0), groups_[View::MIXING]->scale_) ) * glm::vec4(origin_->groups_[View::MIXING]->translation_, 1.f); } } void CloneSource::setFilter(FrameBufferFilter::Type T) { if (filter_) delete filter_; switch (T) { case FrameBufferFilter::FILTER_DELAY: filter_ = new DelayFilter; break; case FrameBufferFilter::FILTER_RESAMPLE: filter_ = new ResampleFilter; break; case FrameBufferFilter::FILTER_BLUR: filter_ = new BlurFilter; break; case FrameBufferFilter::FILTER_SHARPEN: filter_ = new SharpenFilter; break; case FrameBufferFilter::FILTER_SMOOTH: filter_ = new SmoothFilter; break; case FrameBufferFilter::FILTER_EDGE: filter_ = new EdgeFilter; break; case FrameBufferFilter::FILTER_ALPHA: filter_ = new AlphaFilter; break; case FrameBufferFilter::FILTER_IMAGE: filter_ = new ImageFilter; break; default: case FrameBufferFilter::FILTER_PASSTHROUGH: filter_ = new PassthroughFilter; break; } } void CloneSource::play (bool on) { // if a different state is asked if (paused_ == on) { // play / pause filter to suspend clone filter_->setEnabled( on ); // restart delay if was paused if (paused_ && filter_->type() == FrameBufferFilter::FILTER_DELAY) replay(); // toggle state paused_ = !on; } } bool CloneSource::playable () const { return true; } void CloneSource::replay() { // reset Filter filter_->reset(); } guint64 CloneSource::playtime () const { if (filter_->type() != FrameBufferFilter::FILTER_PASSTHROUGH) return guint64( filter_->updateTime() * GST_SECOND ) ; return origin_->playtime(); } uint CloneSource::texture() const { if (origin_) return origin_->frame()->texture(); else return Resource::getTextureBlack(); } void CloneSource::accept(Visitor& v) { Source::accept(v); if (!failed()) v.visit(*this); } glm::ivec2 CloneSource::icon() const { return glm::ivec2(ICON_SOURCE_CLONE); } std::string CloneSource::info() const { return "Clone"; }