X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Ftransition%2Ftransition_producer.cpp;h=ec3e5bfa12f5609ab638f3ab03e1f9a3912f76fb;hb=2af2c4bb98e0d68d7128cc3df51dc447fe4ab280;hp=d8e4c3a33e3cb94f936ed73b00bc74a4666e9dc6;hpb=8d1fc66ef889f19ec4af67699aa9f74d9028d012;p=casparcg diff --git a/core/producer/transition/transition_producer.cpp b/core/producer/transition/transition_producer.cpp index d8e4c3a33..e3056c75f 100644 --- a/core/producer/transition/transition_producer.cpp +++ b/core/producer/transition/transition_producer.cpp @@ -1,198 +1,209 @@ /* -* copyright (c) 2010 Sveriges Television AB +* Copyright (c) 2011 Sveriges Television AB * -* This file is part of CasparCG. +* This file is part of CasparCG (www.casparcg.com). * -* CasparCG 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. +* CasparCG 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. * -* CasparCG 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 CasparCG. If not, see . +* CasparCG 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 CasparCG. If not, see . * -*/ +* Author: Robert Nagy, ronag89@gmail.com +*/ + #include "../../stdafx.h" #include "transition_producer.h" -#include "../../frame/frame_format.h" -#include "../../frame/gpu_frame.h" -#include "../../frame/gpu_composite_frame.h" -#include "../../frame/frame_factory.h" - -#include "../../../common/utility/memory.h" -#include "../../renderer/render_device.h" +#include "../frame_producer.h" +#include "../../frame/draw_frame.h" +#include "../../frame/frame_transform.h" +#include "../../monitor/monitor.h" -#include +#include namespace caspar { namespace core { -struct transition_producer::implementation : boost::noncopyable -{ - implementation(const frame_producer_ptr& dest, const transition_info& info, const frame_format_desc& format_desc) - : current_frame_(0), info_(info), format_desc_(format_desc), dest_producer_(dest) - { - if(!dest) - BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("dest")); - } +class transition_producer : public frame_producer +{ + monitor::basic_subject event_subject_; + const field_mode mode_; + int current_frame_; + + const transition_info info_; + + spl::shared_ptr last_frame_; + + spl::shared_ptr dest_producer_; + spl::shared_ptr source_producer_; - frame_producer_ptr get_following_producer() const +public: + explicit transition_producer(const field_mode& mode, const spl::shared_ptr& dest, const transition_info& info) + : mode_(mode) + , current_frame_(0) + , info_(info) + , last_frame_(draw_frame::empty()) + , dest_producer_(dest) + , source_producer_(frame_producer::empty()) { - return dest_producer_; + dest->subscribe(event_subject_); } - void set_leading_producer(const frame_producer_ptr& producer) + // frame_producer + + virtual void leading_producer(const spl::shared_ptr& producer) override { source_producer_ = producer; } - - gpu_frame_ptr render_frame() - { - if(current_frame_++ >= info_.duration) - return nullptr; - - gpu_frame_ptr source; - gpu_frame_ptr dest; - tbb::parallel_invoke - ( - [&]{dest = render_frame(dest_producer_);}, - [&]{source = render_frame(source_producer_);} - ); + virtual spl::shared_ptr receive(int flags) override + { + if(current_frame_ >= info_.duration) + return dest_producer_->receive(flags); + + ++current_frame_; + + event_subject_ << monitor::event("transition/frame") % current_frame_ % info_.duration + << monitor::event("transition/type") % [&]() -> std::string + { + switch(info_.type.value()) + { + case transition_type::mix: return "mix"; + case transition_type::wipe: return "wipe"; + case transition_type::slide: return "slide"; + case transition_type::push: return "push"; + case transition_type::cut: return "cut"; + default: return "n/a"; + } + }(); + + auto dest = draw_frame::empty(); + auto source = draw_frame::empty(); + + tbb::parallel_invoke( + [&] + { + dest = dest_producer_->receive(flags); + if(dest == core::draw_frame::late()) + dest = dest_producer_->last_frame(); + }, + [&] + { + source = source_producer_->receive(flags); + if(source == core::draw_frame::late()) + source = source_producer_->last_frame(); + }); return compose(dest, source); } - gpu_frame_ptr render_frame(frame_producer_ptr& producer) + virtual spl::shared_ptr last_frame() const override + { + return dest_producer_->last_frame(); + } + + virtual uint32_t nb_frames() const override { - if(producer == nullptr) - return nullptr; + return dest_producer_->nb_frames(); + } - gpu_frame_ptr frame; - try - { - frame = producer->render_frame(); - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - producer = nullptr; - CASPAR_LOG(warning) << "Removed renderer from transition."; - } + virtual std::wstring print() const override + { + return L"transition[" + source_producer_->print() + L"=>" + dest_producer_->print() + L"]"; + } - if(frame == nullptr && producer != nullptr && - producer->get_following_producer() != nullptr) - { - auto following = producer->get_following_producer(); - following->initialize(factory_); - following->set_leading_producer(producer); - producer = following; - return render_frame(producer); - } - return frame; + virtual std::wstring name() const override + { + return L"transition"; } - - void set_volume(const gpu_frame_ptr& frame, int volume) + + boost::property_tree::wptree info() const override { - if(!frame) - return; - - for(size_t n = 0; n < frame->audio_data().size(); ++n) - frame->audio_data()[n] = static_cast((static_cast(frame->audio_data()[n])*volume)>>8); + return dest_producer_->info(); } - - gpu_frame_ptr compose(const gpu_frame_ptr& dest_frame, gpu_frame_ptr src_frame) + + // transition_producer + + spl::shared_ptr compose(const spl::shared_ptr& dest_frame, const spl::shared_ptr& src_frame) { if(info_.type == transition_type::cut) return src_frame; + + const double delta1 = info_.tweener(current_frame_*2-1, 0.0, 1.0, static_cast(info_.duration*2)); + const double delta2 = info_.tweener(current_frame_*2, 0.0, 1.0, static_cast(info_.duration*2)); - if(!dest_frame) - return nullptr; - - double alpha = static_cast(current_frame_)/static_cast(info_.duration); - int volume = static_cast(static_cast(current_frame_)/static_cast(info_.duration)*256.0); - - tbb::parallel_invoke - ( - [&]{set_volume(dest_frame, volume);}, - [&]{set_volume(src_frame, 256-volume);} - ); + const double dir = info_.direction == transition_direction::from_left ? 1.0 : -1.0; + + // For interlaced transitions. Seperate fields into seperate frames which are transitioned accordingly. + + src_frame->frame_transform().audio_transform.volume = 1.0-delta2; + auto s_frame1 = spl::make_shared(src_frame); + auto s_frame2 = spl::make_shared(src_frame); + + dest_frame->frame_transform().audio_transform.volume = delta2; + auto d_frame1 = spl::make_shared(dest_frame); + auto d_frame2 = spl::make_shared(dest_frame); if(info_.type == transition_type::mix) - dest_frame->alpha(alpha); - else if(info_.type == transition_type::slide) - { - if(info_.direction == transition_direction::from_left) - dest_frame->translate(-1.0+alpha, 0.0); - else if(info_.direction == transition_direction::from_right) - dest_frame->translate(1.0-alpha, 0.0); + { + d_frame1->frame_transform().image_transform.opacity = delta1; + d_frame1->frame_transform().image_transform.is_mix = true; + d_frame2->frame_transform().image_transform.opacity = delta2; + d_frame2->frame_transform().image_transform.is_mix = true; + + s_frame1->frame_transform().image_transform.opacity = 1.0-delta1; + s_frame1->frame_transform().image_transform.is_mix = true; + s_frame2->frame_transform().image_transform.opacity = 1.0-delta2; + s_frame2->frame_transform().image_transform.is_mix = true; + } + if(info_.type == transition_type::slide) + { + d_frame1->frame_transform().image_transform.fill_translation[0] = (-1.0+delta1)*dir; + d_frame2->frame_transform().image_transform.fill_translation[0] = (-1.0+delta2)*dir; } else if(info_.type == transition_type::push) { - if(info_.direction == transition_direction::from_left) - { - dest_frame->translate(-1.0+alpha, 0.0); - if(src_frame) - src_frame->translate(0.0+alpha, 0.0); - } - else if(info_.direction == transition_direction::from_right) - { - dest_frame->translate(1.0-alpha, 0.0); - if(src_frame) - src_frame->translate(0.0-alpha, 0.0); - } + d_frame1->frame_transform().image_transform.fill_translation[0] = (-1.0+delta1)*dir; + d_frame2->frame_transform().image_transform.fill_translation[0] = (-1.0+delta2)*dir; + + s_frame1->frame_transform().image_transform.fill_translation[0] = (0.0+delta1)*dir; + s_frame2->frame_transform().image_transform.fill_translation[0] = (0.0+delta2)*dir; } - else if(info_.type == transition_type::wipe) + else if(info_.type == transition_type::wipe) { - if(info_.direction == transition_direction::from_left) - { - dest_frame->translate(-1.0+alpha, 0.0); - dest_frame->texcoords(rectangle(-1.0+alpha, 1.0, alpha, 0.0)); - } - else if(info_.direction == transition_direction::from_right) - { - dest_frame->translate(1.0-alpha, 0.0); - dest_frame->texcoords(rectangle(1.0-alpha, 1.0, 2.0-alpha, 0.0)); - } + d_frame1->frame_transform().image_transform.clip_scale[0] = delta1; + d_frame2->frame_transform().image_transform.clip_scale[0] = delta2; } - - auto composite = std::make_shared(); - if(src_frame) - composite->add(src_frame); - composite->add(dest_frame); - return composite; - } + + const auto s_frame = s_frame1->frame_transform() == s_frame2->frame_transform() ? s_frame2 : draw_frame::interlace(s_frame1, s_frame2, mode_); + const auto d_frame = d_frame1->frame_transform() == d_frame2->frame_transform() ? d_frame2 : draw_frame::interlace(d_frame1, d_frame2, mode_); - void initialize(const frame_factory_ptr& factory) - { - dest_producer_->initialize(factory); - factory_ = factory; + return draw_frame::over(s_frame, d_frame); } - const frame_format_desc format_desc_; + virtual void subscribe(const monitor::observable::observer_ptr& o) override + { + event_subject_.subscribe(o); + } - frame_producer_ptr source_producer_; - frame_producer_ptr dest_producer_; - - unsigned short current_frame_; - - const transition_info info_; - frame_factory_ptr factory_; + virtual void unsubscribe(const monitor::observable::observer_ptr& o) override + { + event_subject_.unsubscribe(o); + } }; -transition_producer::transition_producer(const frame_producer_ptr& dest, const transition_info& info, const frame_format_desc& format_desc) - : impl_(new implementation(dest, info, format_desc)){} -gpu_frame_ptr transition_producer::render_frame(){return impl_->render_frame();} -frame_producer_ptr transition_producer::get_following_producer() const{return impl_->get_following_producer();} -void transition_producer::set_leading_producer(const frame_producer_ptr& producer) { impl_->set_leading_producer(producer); } -const frame_format_desc& transition_producer::get_frame_format_desc() const { return impl_->format_desc_; } -void transition_producer::initialize(const frame_factory_ptr& factory) { impl_->initialize(factory);} +spl::shared_ptr create_transition_producer(const field_mode& mode, const spl::shared_ptr& destination, const transition_info& info) +{ + return core::wrap_producer(spl::make_shared(mode, destination, info)); +} }}