X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Ftransition%2Ftransition_producer.cpp;h=24cf9287533810a4854657b8f5cab816ac344fb8;hb=116e09a218cf056e4de868454b0fe26dc4db1413;hp=79977207e5adc7b51a62aa2b869a282139f6457a;hpb=608a51e73f0da6e5b131e0fd0c97b33e581ed4f4;p=casparcg diff --git a/core/producer/transition/transition_producer.cpp b/core/producer/transition/transition_producer.cpp index 79977207e..24cf92875 100644 --- a/core/producer/transition/transition_producer.cpp +++ b/core/producer/transition/transition_producer.cpp @@ -21,196 +21,161 @@ #include "transition_producer.h" -#include "../../format/video_format.h" -#include "../../processor/frame.h" -#include "../../processor/composite_frame.h" -#include "../../processor/frame_processor_device.h" +#include -#include "../../producer/frame_producer_device.h" +#include +#include -#include +#include + +#include + +using namespace boost::assign; namespace caspar { namespace core { -struct transition_producer::implementation : boost::noncopyable -{ - implementation(const frame_producer_ptr& dest, const transition_info& info) - : current_frame_(0), info_(info), dest_producer_(dest) - { - if(!dest) - BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("dest")); - } +struct transition_producer : public frame_producer +{ + const field_mode::type mode_; + unsigned int current_frame_; + + const transition_info info_; + + safe_ptr dest_producer_; + safe_ptr source_producer_; + + safe_ptr last_frame_; - frame_producer_ptr get_following_producer() const + explicit transition_producer(const field_mode::type& mode, const safe_ptr& dest, const transition_info& info) + : mode_(mode) + , current_frame_(0) + , info_(info) + , dest_producer_(dest) + , source_producer_(frame_producer::empty()) + , last_frame_(basic_frame::empty()){} + + // frame_producer + + virtual safe_ptr get_following_producer() const { return dest_producer_; } - void set_leading_producer(const frame_producer_ptr& producer) + virtual void set_leading_producer(const safe_ptr& producer) { source_producer_ = producer; } - - frame_ptr render_frame() + + virtual safe_ptr receive(int hints) { - if(current_frame_ == 0) - CASPAR_LOG(info) << "Transition started."; + if(++current_frame_ >= info_.duration) + return basic_frame::eof(); + + auto dest = basic_frame::empty(); + auto source = basic_frame::empty(); - frame_ptr result = [&]() -> frame_ptr + tbb::parallel_invoke( + [&] { - if(current_frame_++ >= info_.duration) - return nullptr; - - frame_ptr source; - frame_ptr dest; - - tbb::parallel_invoke - ( - [&]{dest = render_frame(dest_producer_);}, - [&]{source = render_frame(source_producer_);} - ); - - return compose(dest, source); - }(); - - if(result == nullptr) - CASPAR_LOG(info) << "Transition ended."; + dest = receive_and_follow(dest_producer_, hints); + if(dest == core::basic_frame::late()) + dest = dest_producer_->last_frame(); + }, + [&] + { + source = receive_and_follow(source_producer_, hints); + if(source == core::basic_frame::late()) + source = source_producer_->last_frame(); + }); - return result; + return compose(dest, source); } - frame_ptr render_frame(frame_producer_ptr& producer) + virtual safe_ptr last_frame() const { - if(producer == nullptr) - return nullptr; - - frame_ptr frame; - try - { - frame = producer->render_frame(); - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - producer = nullptr; - CASPAR_LOG(warning) << "Removed producer from transition."; - } - - if(frame == nullptr) - { - if(producer == nullptr || producer->get_following_producer() == nullptr) - return nullptr; - - try - { - auto following = producer->get_following_producer(); - following->initialize(frame_processor_); - following->set_leading_producer(producer); - producer = following; - } - catch(...) - { - CASPAR_LOG(warning) << "Failed to initialize following producer. Removing it."; - producer = nullptr; - } - - return render_frame(producer); - } - return frame; + return disable_audio(last_frame_); } - - void set_volume(const frame_ptr& frame, int volume) + + virtual int64_t nb_frames() const { - if(!frame) - return; + return get_following_producer()->nb_frames(); + } - 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); + virtual std::wstring print() const + { + return L"transition[" + source_producer_->print() + L"|" + dest_producer_->print() + L"]"; } - - frame_ptr compose(const frame_ptr& dest_frame, frame_ptr src_frame) + + // transition_producer + + safe_ptr compose(const safe_ptr& dest_frame, const safe_ptr& src_frame) { if(info_.type == transition::cut) return src_frame; + + const double delta1 = info_.tweener(current_frame_*2-1, 0.0, 1.0, info_.duration*2); + const double delta2 = info_.tweener(current_frame_*2, 0.0, 1.0, info_.duration*2); - if(!dest_frame) - return nullptr; - - double alpha = static_cast(current_frame_)/static_cast(info_.duration); - int volume = static_cast(alpha*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. + + auto s_frame1 = make_safe(src_frame); + auto s_frame2 = make_safe(src_frame); + + s_frame1->get_frame_transform().volume = 0.0; + s_frame2->get_frame_transform().volume = 1.0-delta2; + + auto d_frame1 = make_safe(dest_frame); + auto d_frame2 = make_safe(dest_frame); + + d_frame1->get_frame_transform().volume = 0.0; + d_frame2->get_frame_transform().volume = delta2; + if(info_.type == transition::mix) - dest_frame->alpha(alpha); - else if(info_.type == transition::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->get_frame_transform().opacity = delta1; + d_frame1->get_frame_transform().is_mix = true; + d_frame2->get_frame_transform().opacity = delta2; + d_frame2->get_frame_transform().is_mix = true; + + s_frame1->get_frame_transform().opacity = 1.0-delta1; + s_frame1->get_frame_transform().is_mix = true; + s_frame2->get_frame_transform().opacity = 1.0-delta2; + s_frame2->get_frame_transform().is_mix = true; + } + if(info_.type == transition::slide) + { + d_frame1->get_frame_transform().fill_translation[0] = (-1.0+delta1)*dir; + d_frame2->get_frame_transform().fill_translation[0] = (-1.0+delta2)*dir; } else if(info_.type == transition::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->get_frame_transform().fill_translation[0] = (-1.0+delta1)*dir; + d_frame2->get_frame_transform().fill_translation[0] = (-1.0+delta2)*dir; + + s_frame1->get_frame_transform().fill_translation[0] = (0.0+delta1)*dir; + s_frame2->get_frame_transform().fill_translation[0] = (0.0+delta2)*dir; } - else if(info_.type == transition::wipe) + else if(info_.type == transition::wipe) { - if(info_.direction == transition_direction::from_left) - { - dest_frame->translate(-1.0+alpha, 0.0); - dest_frame->texcoords(-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(1.0-alpha, 1.0, 2.0-alpha, 0.0); - } + d_frame1->get_frame_transform().clip_scale[0] = delta1; + d_frame2->get_frame_transform().clip_scale[0] = delta2; } - - std::vector frames; - if(src_frame) - frames.push_back(src_frame); - frames.push_back(dest_frame); - return std::make_shared(frames); - } + + const auto s_frame = s_frame1->get_frame_transform() == s_frame2->get_frame_transform() ? s_frame2 : basic_frame::interlace(s_frame1, s_frame2, mode_); + const auto d_frame = d_frame1->get_frame_transform() == d_frame2->get_frame_transform() ? d_frame2 : basic_frame::interlace(d_frame1, d_frame2, mode_); - void initialize(const frame_processor_device_ptr& frame_processor) - { - dest_producer_->initialize(frame_processor); - frame_processor_ = frame_processor; + last_frame_ = basic_frame::combine(s_frame2, d_frame2); + + return basic_frame::combine(s_frame, d_frame); } - - frame_producer_ptr source_producer_; - frame_producer_ptr dest_producer_; - - unsigned short current_frame_; - - const transition_info info_; - frame_processor_device_ptr frame_processor_; }; -transition_producer::transition_producer(const frame_producer_ptr& dest, const transition_info& info) - : impl_(new implementation(dest, info)){} -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); } -void transition_producer::initialize(const frame_processor_device_ptr& frame_processor) { impl_->initialize(frame_processor);} +safe_ptr create_transition_producer(const field_mode::type& mode, const safe_ptr& destination, const transition_info& info) +{ + return make_safe(mode, destination, info); +} }}