X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Fstage.cpp;h=a0b16806d5aa03c348c696d7f191ba7b76aa0187;hb=086d3c7a704c92dd89a6e7b8e6576b9f1c216c2d;hp=896138d78a18c3a8e631e88a66a4720e3b022eac;hpb=5e60fa5a9c734ef95e4067c020e3e38acc71b37c;p=casparcg diff --git a/core/producer/stage.cpp b/core/producer/stage.cpp index 896138d78..a0b16806d 100644 --- a/core/producer/stage.cpp +++ b/core/producer/stage.cpp @@ -1,372 +1,493 @@ -/* -* Copyright (c) 2011 Sveriges Television AB -* -* 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 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 "stage.h" - -#include "layer.h" - -#include "frame/basic_frame.h" -#include "frame/frame_factory.h" - -#include -#include - -#include - -#include -#include - -#include - -#include -#include - -#include -#include - -namespace caspar { namespace core { - -template -class tweened_transform -{ - T source_; - T dest_; - int duration_; - int time_; - tweener_t tweener_; -public: - tweened_transform() - : duration_(0) - , time_(0) - , tweener_(get_tweener(L"linear")){} - tweened_transform(const T& source, const T& dest, int duration, const std::wstring& tween = L"linear") - : source_(source) - , dest_(dest) - , duration_(duration) - , time_(0) - , tweener_(get_tweener(tween)){} - - T fetch() - { - return time_ == duration_ ? dest_ : tween(static_cast(time_), source_, dest_, static_cast(duration_), tweener_); - } - - T fetch_and_tick(int num) - { - time_ = std::min(time_+num, duration_); - return fetch(); - } -}; - -struct stage::impl : public std::enable_shared_from_this - , boost::noncopyable -{ - safe_ptr target_; - video_format_desc format_desc_; - - safe_ptr graph_; - - boost::timer produce_timer_; - boost::timer tick_timer_; - - std::map layers_; - std::map> transforms_; - - executor executor_; -public: - impl(const safe_ptr& target, const safe_ptr& graph, const video_format_desc& format_desc) - : target_(target) - , graph_(graph) - , format_desc_(format_desc) - , executor_(L"stage") - { - graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); - graph_->set_color("produce-time", diagnostics::color(0.0f, 1.0f, 0.0f)); - } - - void spawn_token() - { - std::weak_ptr self = shared_from_this(); - executor_.begin_invoke([=]{tick(self);}); - } - - void tick(const std::weak_ptr& self) - { - try - { - produce_timer_.restart(); - - std::map> frames; - - BOOST_FOREACH(auto& layer, layers_) - frames[layer.first] = basic_frame::empty(); - - tbb::parallel_for_each(layers_.begin(), layers_.end(), [&](std::map::value_type& layer) - { - auto transform = transforms_[layer.first].fetch_and_tick(1); - - int flags = frame_producer::flags::none; - if(format_desc_.field_mode != field_mode::progressive) - { - flags |= std::abs(transform.fill_scale[1] - 1.0) > 0.0001 ? frame_producer::flags::deinterlace : frame_producer::flags::none; - flags |= std::abs(transform.fill_translation[1]) > 0.0001 ? frame_producer::flags::deinterlace : frame_producer::flags::none; - } - - if(transform.is_key) - flags |= frame_producer::flags::alpha_only; - - auto frame = layer.second.receive(flags); - - auto frame1 = make_safe(frame); - frame1->get_frame_transform() = transform; - - if(format_desc_.field_mode != core::field_mode::progressive) - { - auto frame2 = make_safe(frame); - frame2->get_frame_transform() = transforms_[layer.first].fetch_and_tick(1); - frame1 = core::basic_frame::interlace(frame1, frame2, format_desc_.field_mode); - } - - frames[layer.first] = frame1; - }); - - graph_->set_value("produce-time", produce_timer_.elapsed()*format_desc_.fps*0.5); - - std::shared_ptr ticket(nullptr, [self](void*) - { - auto self2 = self.lock(); - if(self2) - self2->executor_.begin_invoke([=]{tick(self);}); - }); - - target_->send(std::make_pair(frames, ticket)); - - graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5); - tick_timer_.restart(); - } - catch(...) - { - layers_.clear(); - CASPAR_LOG_CURRENT_EXCEPTION(); - } - } - - void set_transform(int index, const frame_transform& transform, unsigned int mix_duration, const std::wstring& tween) - { - executor_.begin_invoke([=] - { - auto src = transforms_[index].fetch(); - auto dst = transform; - transforms_[index] = tweened_transform(src, dst, mix_duration, tween); - }, high_priority); - } - - void apply_transform(int index, const std::function& transform, unsigned int mix_duration, const std::wstring& tween) - { - executor_.begin_invoke([=] - { - auto src = transforms_[index].fetch(); - auto dst = transform(src); - transforms_[index] = tweened_transform(src, dst, mix_duration, tween); - }, high_priority); - } - - void clear_transforms(int index) - { - executor_.begin_invoke([=] - { - transforms_.erase(index); - }, high_priority); - } - - void clear_transforms() - { - executor_.begin_invoke([=] - { - transforms_.clear(); - }, high_priority); - } - - void load(int index, const safe_ptr& producer, bool preview, int auto_play_delta) - { - executor_.begin_invoke([=] - { - layers_[index].load(producer, preview, auto_play_delta); - }, high_priority); - } - - void pause(int index) - { - executor_.begin_invoke([=] - { - layers_[index].pause(); - }, high_priority); - } - - void play(int index) - { - executor_.begin_invoke([=] - { - layers_[index].play(); - }, high_priority); - } - - void stop(int index) - { - executor_.begin_invoke([=] - { - layers_[index].stop(); - }, high_priority); - } - - void clear(int index) - { - executor_.begin_invoke([=] - { - layers_.erase(index); - }, high_priority); - } - - void clear() - { - executor_.begin_invoke([=] - { - layers_.clear(); - }, high_priority); - } - - boost::unique_future call(int index, bool foreground, const std::wstring& param) - { - return std::move(*executor_.invoke([=] - { - return std::make_shared>(std::move(layers_[index].call(foreground, param))); - }, high_priority)); - } - - void swap_layers(const safe_ptr& other) - { - if(other->impl_.get() == this) - return; - - auto func = [=] - { - std::swap(layers_, other->impl_->layers_); - }; - executor_.begin_invoke([=] - { - other->impl_->executor_.invoke(func, high_priority); - }, high_priority); - } - - void swap_layer(int index, int other_index) - { - executor_.begin_invoke([=] - { - std::swap(layers_[index], layers_[other_index]); - }, high_priority); - } - - void swap_layer(int index, int other_index, const safe_ptr& other) - { - if(other->impl_.get() == this) - swap_layer(index, other_index); - else - { - auto func = [=] - { - std::swap(layers_[index], other->impl_->layers_[other_index]); - }; - executor_.begin_invoke([=] - { - other->impl_->executor_.invoke(func, high_priority); - }, high_priority); - } - } - - boost::unique_future> foreground(int index) - { - return executor_.begin_invoke([=] - { - return layers_[index].foreground(); - }, high_priority); - } - - boost::unique_future> background(int index) - { - return executor_.begin_invoke([=] - { - return layers_[index].background(); - }, high_priority); - } - - void set_video_format_desc(const video_format_desc& format_desc) - { - executor_.begin_invoke([=] - { - format_desc_ = format_desc; - }, high_priority); - } - - boost::unique_future info() - { - return std::move(executor_.begin_invoke([this]() -> boost::property_tree::wptree - { - boost::property_tree::wptree info; - BOOST_FOREACH(auto& layer, layers_) - info.add_child(L"layers.layer", layer.second.info()) - .add(L"index", layer.first); - return info; - }, high_priority)); - } - - boost::unique_future info(int index) - { - return std::move(executor_.begin_invoke([=]() -> boost::property_tree::wptree - { - return layers_[index].info(); - }, high_priority)); - } -}; - -stage::stage(const safe_ptr& target, const safe_ptr& graph, const struct video_format_desc& format_desc) : impl_(new impl(target, graph, format_desc)){} -void stage::set_frame_transform(int index, const core::frame_transform& transform, unsigned int mix_duration, const std::wstring& tween){impl_->set_transform(index, transform, mix_duration, tween);} -void stage::apply_frame_transform(int index, const std::function& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);} -void stage::clear_transforms(int index){impl_->clear_transforms(index);} -void stage::clear_transforms(){impl_->clear_transforms();} -void stage::spawn_token(){impl_->spawn_token();} -void stage::load(int index, const safe_ptr& producer, bool preview, int auto_play_delta){impl_->load(index, producer, preview, auto_play_delta);} -void stage::pause(int index){impl_->pause(index);} -void stage::play(int index){impl_->play(index);} -void stage::stop(int index){impl_->stop(index);} -void stage::clear(int index){impl_->clear(index);} -void stage::clear(){impl_->clear();} -void stage::swap_layers(const safe_ptr& other){impl_->swap_layers(other);} -void stage::swap_layer(int index, int other_index){impl_->swap_layer(index, other_index);} -void stage::swap_layer(int index, int other_index, const safe_ptr& other){impl_->swap_layer(index, other_index, other);} -boost::unique_future> stage::foreground(int index) {return impl_->foreground(index);} -boost::unique_future> stage::background(int index) {return impl_->background(index);} -boost::unique_future stage::call(int index, bool foreground, const std::wstring& param){return impl_->call(index, foreground, param);} -void stage::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);} -boost::unique_future stage::info() const{return impl_->info();} -boost::unique_future stage::info(int index) const{return impl_->info(index);} -}} \ No newline at end of file +/* +* Copyright (c) 2011 Sveriges Television AB +* +* 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 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 "stage.h" + +#include "layer.h" + +#include "../frame/draw_frame.h" +#include "../frame/frame_factory.h" +#include "../interaction/interaction_aggregator.h" +#include "../consumer/write_frame_consumer.h" + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace caspar { namespace core { + +struct stage::impl : public std::enable_shared_from_this +{ + int channel_index_; + spl::shared_ptr graph_; + spl::shared_ptr monitor_subject_ = spl::make_shared("/stage"); + std::map layers_; + std::map tweens_; + interaction_aggregator aggregator_; + // map of layer -> map of tokens (src ref) -> layer_consumer + std::map>> layer_consumers_; + executor executor_ { L"stage " + boost::lexical_cast(channel_index_) }; +public: + impl(int channel_index, spl::shared_ptr graph) + : channel_index_(channel_index) + , graph_(std::move(graph)) + , aggregator_([=] (double x, double y) { return collission_detect(x, y); }) + { + graph_->set_color("produce-time", diagnostics::color(0.0f, 1.0f, 0.0f)); + } + + std::map operator()(const video_format_desc& format_desc) + { + caspar::timer frame_timer; + + auto frames = executor_.invoke([=]() -> std::map + { + + std::map frames; + + try + { + std::vector indices; + + for (auto& layer : layers_) + { + // Prevent race conditions in parallel for each later + frames[layer.first] = draw_frame::empty(); + tweens_[layer.first]; + layer_consumers_[layer.first]; + + indices.push_back(layer.first); + } + + aggregator_.translate_and_send(); + + tbb::parallel_for_each(indices.begin(), indices.end(), [&](int index) + { + draw(index, format_desc, frames); + }); + } + catch(...) + { + layers_.clear(); + CASPAR_LOG_CURRENT_EXCEPTION(); + } + + + return frames; + }); + + //frames_subject_ << frames; + + graph_->set_value("produce-time", frame_timer.elapsed()*format_desc.fps*0.5); + *monitor_subject_ << monitor::message("/profiler/time") % frame_timer.elapsed() % (1.0/format_desc.fps); + + return frames; + } + + void draw(int index, const video_format_desc& format_desc, std::map& frames) + { + auto& layer = layers_[index]; + auto& tween = tweens_[index]; + auto& consumers = layer_consumers_[index]; + + auto frame = layer.receive(format_desc); + + if (!consumers.empty()) + { + auto consumer_it = consumers | boost::adaptors::map_values; + tbb::parallel_for_each(consumer_it.begin(), consumer_it.end(), [&](decltype(*consumer_it.begin()) layer_consumer) + { + layer_consumer->send(frame); + }); + } + + auto frame1 = frame; + + frame1.transform() *= tween.fetch_and_tick(1); + + if(format_desc.field_mode != core::field_mode::progressive) + { + auto frame2 = frame; + frame2.transform() *= tween.fetch_and_tick(1); + frame2.transform().audio_transform.volume = 0.0; + frame1 = core::draw_frame::interlace(frame1, frame2, format_desc.field_mode); + } + + frames[index] = frame1; + } + + layer& get_layer(int index) + { + auto it = layers_.find(index); + if(it == std::end(layers_)) + { + it = layers_.insert(std::make_pair(index, layer(index))).first; + it->second.monitor_output().attach_parent(monitor_subject_); + } + return it->second; + } + + std::future apply_transforms(const std::vector>& transforms) + { + return executor_.begin_invoke([=] + { + for (auto& transform : transforms) + { + auto& tween = tweens_[std::get<0>(transform)]; + auto src = tween.fetch(); + auto dst = std::get<1>(transform)(tween.dest()); + tweens_[std::get<0>(transform)] = tweened_transform(src, dst, std::get<2>(transform), std::get<3>(transform)); + } + }, task_priority::high_priority); + } + + std::future apply_transform(int index, const stage::transform_func_t& transform, unsigned int mix_duration, const tweener& tween) + { + return executor_.begin_invoke([=] + { + auto src = tweens_[index].fetch(); + auto dst = transform(src); + tweens_[index] = tweened_transform(src, dst, mix_duration, tween); + }, task_priority::high_priority); + } + + std::future clear_transforms(int index) + { + return executor_.begin_invoke([=] + { + tweens_.erase(index); + }, task_priority::high_priority); + } + + std::future clear_transforms() + { + return executor_.begin_invoke([=] + { + tweens_.clear(); + }, task_priority::high_priority); + } + + std::future get_current_transform(int index) + { + return executor_.begin_invoke([=] + { + return tweens_[index].fetch(); + }, task_priority::high_priority); + } + + std::future load(int index, const spl::shared_ptr& producer, bool preview, const boost::optional& auto_play_delta) + { + return executor_.begin_invoke([=] + { + get_layer(index).load(producer, preview, auto_play_delta); + }, task_priority::high_priority); + } + + std::future pause(int index) + { + return executor_.begin_invoke([=] + { + get_layer(index).pause(); + }, task_priority::high_priority); + } + + std::future resume(int index) + { + return executor_.begin_invoke([=] + { + get_layer(index).resume(); + }, task_priority::high_priority); + } + + std::future play(int index) + { + return executor_.begin_invoke([=] + { + get_layer(index).play(); + }, task_priority::high_priority); + } + + std::future stop(int index) + { + return executor_.begin_invoke([=] + { + get_layer(index).stop(); + }, task_priority::high_priority); + } + + std::future clear(int index) + { + return executor_.begin_invoke([=] + { + layers_.erase(index); + }, task_priority::high_priority); + } + + std::future clear() + { + return executor_.begin_invoke([=] + { + layers_.clear(); + }, task_priority::high_priority); + } + + std::future swap_layers(stage& other, bool swap_transforms) + { + auto other_impl = other.impl_; + + if (other_impl.get() == this) + { + return make_ready_future(); + } + + auto func = [=] + { + auto layers = layers_ | boost::adaptors::map_values; + auto other_layers = other_impl->layers_ | boost::adaptors::map_values; + + for (auto& layer : layers) + layer.monitor_output().detach_parent(); + + for (auto& layer : other_layers) + layer.monitor_output().detach_parent(); + + std::swap(layers_, other_impl->layers_); + + for (auto& layer : layers) + layer.monitor_output().attach_parent(monitor_subject_); + + for (auto& layer : other_layers) + layer.monitor_output().attach_parent(monitor_subject_); + + if (swap_transforms) + std::swap(tweens_, other_impl->tweens_); + }; + + return executor_.begin_invoke([=] + { + other_impl->executor_.invoke(func, task_priority::high_priority); + }, task_priority::high_priority); + } + + std::future swap_layer(int index, int other_index, bool swap_transforms) + { + return executor_.begin_invoke([=] + { + std::swap(get_layer(index), get_layer(other_index)); + + if (swap_transforms) + std::swap(tweens_[index], tweens_[other_index]); + }, task_priority::high_priority); + } + + std::future swap_layer(int index, int other_index, stage& other, bool swap_transforms) + { + auto other_impl = other.impl_; + + if(other_impl.get() == this) + return swap_layer(index, other_index, swap_transforms); + else + { + auto func = [=] + { + auto& my_layer = get_layer(index); + auto& other_layer = other_impl->get_layer(other_index); + + my_layer.monitor_output().detach_parent(); + other_layer.monitor_output().detach_parent(); + + std::swap(my_layer, other_layer); + + my_layer.monitor_output().attach_parent(monitor_subject_); + other_layer.monitor_output().attach_parent(other_impl->monitor_subject_); + + if (swap_transforms) + { + auto& my_tween = tweens_[index]; + auto& other_tween = other_impl->tweens_[other_index]; + std::swap(my_tween, other_tween); + } + }; + + return executor_.begin_invoke([=] + { + other_impl->executor_.invoke(func, task_priority::high_priority); + }, task_priority::high_priority); + } + } + + void add_layer_consumer(void* token, int layer, const spl::shared_ptr& layer_consumer) + { + executor_.begin_invoke([=] + { + layer_consumers_[layer].insert(std::make_pair(token, layer_consumer)); + }, task_priority::high_priority); + } + + void remove_layer_consumer(void* token, int layer) + { + executor_.begin_invoke([=] + { + auto& layer_map = layer_consumers_[layer]; + layer_map.erase(token); + if (layer_map.empty()) + { + layer_consumers_.erase(layer); + } + }, task_priority::high_priority); + } + + std::future> foreground(int index) + { + return executor_.begin_invoke([=]() -> std::shared_ptr + { + return get_layer(index).foreground(); + }, task_priority::high_priority); + } + + std::future> background(int index) + { + return executor_.begin_invoke([=]() -> std::shared_ptr + { + return get_layer(index).background(); + }, task_priority::high_priority); + } + + std::future info() + { + return executor_.begin_invoke([this]() -> boost::property_tree::wptree + { + boost::property_tree::wptree info; + for (auto& layer : layers_) + info.add_child(L"layers.layer", layer.second.info()) + .add(L"index", layer.first); + return info; + }, task_priority::high_priority); + } + + std::future info(int index) + { + return executor_.begin_invoke([=] + { + return get_layer(index).info(); + }, task_priority::high_priority); + } + + std::future delay_info() + { + return std::move(executor_.begin_invoke([this]() -> boost::property_tree::wptree + { + boost::property_tree::wptree info; + + for (auto& layer : layers_) + info.add_child(L"layer", layer.second.delay_info()).add(L"index", layer.first); + + return info; + }, task_priority::high_priority)); + } + + std::future delay_info(int index) + { + return std::move(executor_.begin_invoke([=]() -> boost::property_tree::wptree + { + return get_layer(index).delay_info(); + }, task_priority::high_priority)); + } + + std::future call(int index, const std::vector& params) + { + return flatten(executor_.begin_invoke([=] + { + return get_layer(index).foreground()->call(params).share(); + }, task_priority::high_priority)); + } + + void on_interaction(const interaction_event::ptr& event) + { + executor_.begin_invoke([=] + { + aggregator_.offer(event); + }, task_priority::high_priority); + } + + boost::optional collission_detect(double x, double y) + { + for (auto& layer : layers_ | boost::adaptors::reversed) + { + auto transform = tweens_[layer.first].fetch(); + auto translated = translate(x, y, transform); + + if (translated.first >= 0.0 + && translated.first <= 1.0 + && translated.second >= 0.0 + && translated.second <= 1.0 + && layer.second.collides(translated.first, translated.second)) + { + return std::make_pair(transform, static_cast(&layer.second)); + } + } + + return boost::optional(); + } +}; + +stage::stage(int channel_index, spl::shared_ptr graph) : impl_(new impl(channel_index, std::move(graph))){} +std::future stage::call(int index, const std::vector& params){return impl_->call(index, params);} +std::future stage::apply_transforms(const std::vector& transforms){ return impl_->apply_transforms(transforms); } +std::future stage::apply_transform(int index, const std::function& transform, unsigned int mix_duration, const tweener& tween){ return impl_->apply_transform(index, transform, mix_duration, tween); } +std::future stage::clear_transforms(int index){ return impl_->clear_transforms(index); } +std::future stage::clear_transforms(){ return impl_->clear_transforms(); } +std::future stage::get_current_transform(int index){ return impl_->get_current_transform(index); } +std::future stage::load(int index, const spl::shared_ptr& producer, bool preview, const boost::optional& auto_play_delta){ return impl_->load(index, producer, preview, auto_play_delta); } +std::future stage::pause(int index){ return impl_->pause(index); } +std::future stage::resume(int index){ return impl_->resume(index); } +std::future stage::play(int index){ return impl_->play(index); } +std::future stage::stop(int index){ return impl_->stop(index); } +std::future stage::clear(int index){ return impl_->clear(index); } +std::future stage::clear(){ return impl_->clear(); } +std::future stage::swap_layers(stage& other, bool swap_transforms){ return impl_->swap_layers(other, swap_transforms); } +std::future stage::swap_layer(int index, int other_index, bool swap_transforms){ return impl_->swap_layer(index, other_index, swap_transforms); } +std::future stage::swap_layer(int index, int other_index, stage& other, bool swap_transforms){ return impl_->swap_layer(index, other_index, other, swap_transforms); } +void stage::add_layer_consumer(void* token, int layer, const spl::shared_ptr& layer_consumer){ impl_->add_layer_consumer(token, layer, layer_consumer); } +void stage::remove_layer_consumer(void* token, int layer){ impl_->remove_layer_consumer(token, layer); }std::future> stage::foreground(int index) { return impl_->foreground(index); } +std::future> stage::background(int index) { return impl_->background(index); } +std::future stage::info() const{ return impl_->info(); } +std::future stage::info(int index) const{ return impl_->info(index); } +std::future stage::delay_info() const{ return impl_->delay_info(); } +std::future stage::delay_info(int index) const{ return impl_->delay_info(index); } +std::map stage::operator()(const video_format_desc& format_desc){ return (*impl_)(format_desc); } +monitor::subject& stage::monitor_output(){return *impl_->monitor_subject_;} +void stage::on_interaction(const interaction_event::ptr& event) { impl_->on_interaction(event); } +}}