X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Fstage.cpp;h=54f30ff59542ba109eb8f24c904e2906ed846523;hb=6c9fcb31392479e3985840d311d9b0a486d56171;hp=8262b6ebeaa767e7823f587f64c478035bf0699c;hpb=ccaa3af7e1b264956b40c4dd908442d755aa2b81;p=casparcg diff --git a/core/producer/stage.cpp b/core/producer/stage.cpp index 8262b6ebe..54f30ff59 100644 --- a/core/producer/stage.cpp +++ b/core/producer/stage.cpp @@ -1,238 +1,492 @@ -/* -* copyright (c) 2010 Sveriges Television AB -* -* This file is part of CasparCG. -* -* 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 . -* -*/ - -#include "../StdAfx.h" - -#include "stage.h" - -#include "layer.h" - -#include "../video_channel_context.h" - -#include -#include - -#include - -#include - -#include - -#include -#include - -namespace caspar { namespace core { - -void destroy_producer(safe_ptr& producer) -{ - if(!producer.unique()) - CASPAR_LOG(debug) << producer->print() << L" Not destroyed on safe asynchronous destruction thread."; - - producer = frame_producer::empty(); -} - -class destroy_producer_proxy : public frame_producer -{ - safe_ptr producer_; - executor& destroy_context_; -public: - destroy_producer_proxy(executor& destroy_context, const safe_ptr& producer) - : producer_(producer) - , destroy_context_(destroy_context){} - - ~destroy_producer_proxy() - { - if(destroy_context_.size() > 4) - CASPAR_LOG(error) << L" Potential destroyer deadlock."; - - destroy_context_.begin_invoke(std::bind(&destroy_producer, std::move(producer_))); - } - - virtual safe_ptr receive(int hints) {return producer_->receive(hints);} - virtual safe_ptr last_frame() const {return producer_->last_frame();} - virtual std::wstring print() const {return producer_->print();} - virtual void param(const std::wstring& str) {producer_->param(str);} - virtual safe_ptr get_following_producer() const {return producer_->get_following_producer();} - virtual void set_leading_producer(const safe_ptr& producer) {producer_->set_leading_producer(producer);} - virtual int64_t nb_frames() const {return producer_->nb_frames();} -}; - -struct stage::implementation : boost::noncopyable -{ - std::map layers_; - video_channel_context& channel_; -public: - implementation(video_channel_context& video_channel) - : channel_(video_channel) - { - } - - std::map> execute() - { - try - { - 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) - { - frames[layer.first] = layer.second.receive(); - }); - - return frames; - } - catch(...) - { - CASPAR_LOG(error) << L"[stage] Error detected"; - throw; - } - } - - void load(int index, const safe_ptr& producer, bool preview, int auto_play_delta) - { - channel_.execution().invoke([&] - { - layers_[index].load(make_safe(channel_.destruction(), producer), preview, auto_play_delta); - }, high_priority); - } - - void pause(int index) - { - channel_.execution().invoke([&] - { - layers_[index].pause(); - }, high_priority); - } - - void play(int index) - { - channel_.execution().invoke([&] - { - layers_[index].play(); - }, high_priority); - } - - void stop(int index) - { - channel_.execution().invoke([&] - { - layers_[index].stop(); - }, high_priority); - } - - void clear(int index) - { - channel_.execution().invoke([&] - { - layers_.erase(index); - }, high_priority); - } - - void clear() - { - channel_.execution().invoke([&] - { - layers_.clear(); - }, high_priority); - } - - void swap_layer(int index, size_t other_index) - { - channel_.execution().invoke([&] - { - layers_[index].swap(layers_[other_index]); - }, high_priority); - } - - void swap_layer(int index, size_t other_index, stage& other) - { - if(other.impl_.get() == this) - swap_layer(index, other_index); - else - { - auto func = [&]{layers_[index].swap(other.impl_->layers_[other_index]);}; - channel_.execution().invoke([&]{other.impl_->channel_.execution().invoke(func, high_priority);}, high_priority); - } - } - - void swap(stage& other) - { - if(other.impl_.get() == this) - return; - - auto func = [&] - { - auto sel_first = [](const std::pair& pair){return pair.first;}; - - std::set indices; - auto inserter = std::inserter(indices, indices.begin()); - - std::transform(layers_.begin(), layers_.end(), inserter, sel_first); - std::transform(other.impl_->layers_.begin(), other.impl_->layers_.end(), inserter, sel_first); - - BOOST_FOREACH(auto index, indices) - layers_[index].swap(other.impl_->layers_[index]); - }; - - channel_.execution().invoke([&]{other.impl_->channel_.execution().invoke(func, high_priority);}); - } - - layer_status get_status(int index) - { - return channel_.execution().invoke([&] - { - return layers_[index].status(); - }, high_priority ); - } - - safe_ptr foreground(int index) - { - return channel_.execution().invoke([=]{return layers_[index].foreground();}, high_priority); - } - - safe_ptr background(int index) - { - return channel_.execution().invoke([=]{return layers_[index].background();}, high_priority); - } - - std::wstring print() const - { - return L"stage [" + boost::lexical_cast(channel_.index()) + L"]"; - } - -}; - -stage::stage(video_channel_context& video_channel) : impl_(new implementation(video_channel)){} -void stage::swap(stage& other){impl_->swap(other);} -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_layer(int index, size_t other_index){impl_->swap_layer(index, other_index);} -void stage::swap_layer(int index, size_t other_index, stage& other){impl_->swap_layer(index, other_index, other);} -layer_status stage::get_status(int index){return impl_->get_status(index);} -safe_ptr stage::foreground(size_t index) {return impl_->foreground(index);} -safe_ptr stage::background(size_t index) {return impl_->background(index);} -std::map> stage::execute(){return impl_->execute();} -}} \ 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); + 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); } +}}