X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Fframe_producer.cpp;h=7312937f3b15088882304253abf85041e6b23505;hb=a36f794e330f7a29ea4583868290817c483c10ae;hp=987a72ec70c804a67409f690828e24eb13e20acf;hpb=1a093e97af48583e29a1032ebcf0536364fc1ec9;p=casparcg diff --git a/core/producer/frame_producer.cpp b/core/producer/frame_producer.cpp index 987a72ec7..7312937f3 100644 --- a/core/producer/frame_producer.cpp +++ b/core/producer/frame_producer.cpp @@ -1,236 +1,415 @@ -/* -* 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 "frame_producer.h" -#include "frame/basic_frame.h" -#include "frame/frame_transform.h" - -#include "color/color_producer.h" -#include "separated/separated_producer.h" - -#include -#include - -#include -#include - -namespace caspar { namespace core { - -struct destruction_context -{ - std::shared_ptr producer; - Concurrency::event event; - - destruction_context(std::shared_ptr&& producer) - : producer(producer) - { - } -}; - -void __cdecl destroy_producer(LPVOID lpParam) -{ - static Concurrency::critical_section mutex; - auto destruction = std::unique_ptr(static_cast(lpParam)); - - try - { - if(destruction->producer.unique()) - { - { - Concurrency::critical_section::scoped_lock lock(mutex); - Concurrency::wait(200); - } - Concurrency::scoped_oversubcription_token oversubscribe; - CASPAR_LOG(info) << "Destroying: " << destruction->producer->print(); - destruction->producer.reset(); - } - else - CASPAR_LOG(warning) << destruction->producer->print() << " Not destroyed asynchronously."; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - } - - destruction->event.set(); -} - -void __cdecl destroy_and_wait_producer(LPVOID lpParam) -{ - try - { - auto destruction = static_cast(lpParam); - Concurrency::CurrentScheduler::ScheduleTask(destroy_producer, lpParam); - if(destruction->event.wait(1000) == Concurrency::COOPERATIVE_WAIT_TIMEOUT) - CASPAR_LOG(warning) << " Potential destruction deadlock detected. Might leak resources."; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - } -} - -class destroy_producer_proxy : public frame_producer -{ - std::shared_ptr producer_; -public: - destroy_producer_proxy(const std::shared_ptr& producer) - : producer_(producer) - { - } - - ~destroy_producer_proxy() - { - Concurrency::CurrentScheduler::ScheduleTask(destroy_producer, new destruction_context(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 bool param(const std::wstring& str) {return 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();} -}; - -class last_frame_producer : public frame_producer -{ - const std::wstring print_; - const safe_ptr frame_; - const int64_t nb_frames_; -public: - last_frame_producer(const safe_ptr& producer) - : print_(producer->print()) - , frame_(producer->last_frame() != basic_frame::eof() ? producer->last_frame() : basic_frame::empty()) - , nb_frames_(producer->nb_frames()) - { - } - - virtual safe_ptr receive(int){return frame_;} - virtual safe_ptr last_frame() const{return frame_;} - virtual std::wstring print() const{return L"dummy[" + print_ + L"]";} - virtual int64_t nb_frames() const {return nb_frames_;} -}; - -struct empty_frame_producer : public frame_producer -{ - virtual safe_ptr receive(int){return basic_frame::empty();} - virtual safe_ptr last_frame() const{return basic_frame::empty();} - virtual void set_frame_factory(const safe_ptr&){} - virtual int64_t nb_frames() const {return 0;} - virtual std::wstring print() const { return L"empty";} -}; - -const safe_ptr& frame_producer::empty() // nothrow -{ - static safe_ptr producer = make_safe(); - return producer; -} - -safe_ptr receive_and_follow(safe_ptr& producer, int hints) -{ - auto frame = producer->receive(hints); - if(frame == basic_frame::eof()) - { - CASPAR_LOG(info) << producer->print() << " End Of File."; - auto following = producer->get_following_producer(); - if(following != frame_producer::empty()) - { - following->set_leading_producer(producer); - producer = std::move(following); - } - else - producer = make_safe(producer); - - return receive_and_follow(producer, hints); - } - return frame; -} - -Concurrency::concurrent_vector> g_factories; - -void register_producer_factory(const producer_factory_t& factory) -{ - g_factories.push_back(std::make_shared(factory)); -} - -safe_ptr do_create_producer(const safe_ptr& my_frame_factory, const std::vector& params) -{ - if(params.empty()) - BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("params") << arg_value_info("")); - - auto producer = frame_producer::empty(); - std::any_of(g_factories.begin(), g_factories.end(), [&](const std::shared_ptr& factory) -> bool - { - try - { - producer = (*factory)(my_frame_factory, params); - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - } - return producer != frame_producer::empty(); - }); - - if(producer == frame_producer::empty()) - producer = create_color_producer(my_frame_factory, params); - - return producer; -} - -safe_ptr create_producer(const safe_ptr& my_frame_factory, const std::vector& params) -{ - auto producer = do_create_producer(my_frame_factory, params); - auto key_producer = frame_producer::empty(); - - try // to find a key file. - { - auto params_copy = params; - if(params_copy.size() > 0) - { - params_copy[0] += L"_A"; - key_producer = do_create_producer(my_frame_factory, params_copy); - if(key_producer == frame_producer::empty()) - { - params_copy[0] += L"LPHA"; - key_producer = do_create_producer(my_frame_factory, params_copy); - } - } - } - catch(...){} - - if(producer != frame_producer::empty() && key_producer != frame_producer::empty()) - producer = create_separated_producer(producer, key_producer); - - if(producer == frame_producer::empty()) - { - std::wstring str; - BOOST_FOREACH(auto& param, params) - str += param + L" "; - BOOST_THROW_EXCEPTION(file_not_found() << msg_info("No match found for supplied commands. Check syntax.") << arg_value_info(narrow(str))); - } - - return make_safe(producer); -} - -}} \ 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 "frame_producer.h" + +#include "../frame/draw_frame.h" +#include "../frame/frame_transform.h" + +#include "color/color_producer.h" +#include "separated/separated_producer.h" +#include "variable.h" + +#include +#include +#include +#include +#include + +#include + +namespace caspar { namespace core { + +struct frame_producer_registry::impl +{ + std::vector producer_factories; + std::vector thumbnail_factories; + spl::shared_ptr help_repo; + + impl(spl::shared_ptr help_repo) + : help_repo(std::move(help_repo)) + { + } +}; + +frame_producer_registry::frame_producer_registry(spl::shared_ptr help_repo) + : impl_(new impl(std::move(help_repo))) +{ +} + +void frame_producer_registry::register_producer_factory(std::wstring name, const producer_factory_t& factory, const help_item_describer& describer) +{ + impl_->producer_factories.push_back(factory); + impl_->help_repo->register_item({ L"producer" }, std::move(name), describer); +} + +void frame_producer_registry::register_thumbnail_producer_factory(const producer_factory_t& factory) +{ + impl_->thumbnail_factories.push_back(factory); +} + +frame_producer_dependencies::frame_producer_dependencies( + const spl::shared_ptr& frame_factory, + const std::vector>& channels, + const video_format_desc& format_desc, + const spl::shared_ptr producer_registry) + : frame_factory(frame_factory) + , channels(channels) + , format_desc(format_desc) + , producer_registry(producer_registry) +{ +} + +constraints::constraints(double width, double height) + : width(width), height(height) +{ +} + +constraints::constraints() +{ +} + +struct frame_producer_base::impl +{ + tbb::atomic frame_number_; + tbb::atomic paused_; + frame_producer_base& self_; + draw_frame last_frame_; + + impl(frame_producer_base& self) + : self_(self) + , last_frame_(draw_frame::empty()) + { + frame_number_ = 0; + paused_ = false; + } + + draw_frame receive() + { + if(paused_) + return self_.last_frame(); + + auto frame = self_.receive_impl(); + if(frame == draw_frame::late()) + return self_.last_frame(); + + ++frame_number_; + + return last_frame_ = draw_frame::push(frame); + } + + void paused(bool value) + { + paused_ = value; + } + + draw_frame last_frame() + { + return draw_frame::still(last_frame_); + } + draw_frame create_thumbnail_frame() + { + return draw_frame::empty(); + } +}; + +frame_producer_base::frame_producer_base() : impl_(new impl(*this)) +{ +} + +draw_frame frame_producer_base::receive() +{ + return impl_->receive(); +} + +void frame_producer_base::paused(bool value) +{ + impl_->paused(value); +} + +draw_frame frame_producer_base::last_frame() +{ + return impl_->last_frame(); +} +draw_frame frame_producer_base::create_thumbnail_frame() +{ + return impl_->create_thumbnail_frame(); +} + +std::future frame_producer_base::call(const std::vector&) +{ + CASPAR_THROW_EXCEPTION(not_supported()); +} + +uint32_t frame_producer_base::nb_frames() const +{ + return std::numeric_limits::max(); +} + +uint32_t frame_producer_base::frame_number() const +{ + return impl_->frame_number_; +} + +variable& frame_producer_base::get_variable(const std::wstring& name) +{ + CASPAR_THROW_EXCEPTION(caspar_exception() + << msg_info(L"No variable called " + name + L" found in " + print())); +} + +const std::vector& frame_producer_base::get_variables() const +{ + static std::vector empty; + + return empty; +} + +const spl::shared_ptr& frame_producer::empty() +{ + class empty_frame_producer : public frame_producer + { + public: + empty_frame_producer(){} + draw_frame receive() override{return draw_frame::empty();} + void paused(bool value) override{} + uint32_t nb_frames() const override {return 0;} + std::wstring print() const override { return L"empty";} + monitor::subject& monitor_output() override {static monitor::subject monitor_subject(""); return monitor_subject;} + std::wstring name() const override {return L"empty";} + uint32_t frame_number() const override {return 0;} + std::future call(const std::vector& params) override{CASPAR_THROW_EXCEPTION(not_supported());} + variable& get_variable(const std::wstring& name) override { CASPAR_THROW_EXCEPTION(not_supported()); } + const std::vector& get_variables() const override { static std::vector empty; return empty; } + draw_frame last_frame() {return draw_frame::empty();} + draw_frame create_thumbnail_frame() {return draw_frame::empty();} + constraints& pixel_constraints() override { static constraints c; return c; } + + boost::property_tree::wptree info() const override + { + boost::property_tree::wptree info; + info.add(L"type", L"empty-producer"); + return info; + } + }; + + static spl::shared_ptr producer = spl::make_shared(); + return producer; +} + +tbb::atomic& destroy_producers_in_separate_thread() +{ + static tbb::atomic state; + + return state; +} + +void destroy_producers_synchronously() +{ + destroy_producers_in_separate_thread() = false; +} + +class destroy_producer_proxy : public frame_producer +{ + std::shared_ptr producer_; +public: + destroy_producer_proxy(spl::shared_ptr&& producer) + : producer_(std::move(producer)) + { + destroy_producers_in_separate_thread() = true; + } + + virtual ~destroy_producer_proxy() + { + static tbb::atomic counter; + static std::once_flag counter_init_once; + std::call_once(counter_init_once, []{ counter = 0; }); + + if(producer_ == core::frame_producer::empty() || !destroy_producers_in_separate_thread()) + return; + + ++counter; + CASPAR_VERIFY(counter < 8); + + auto producer = new spl::shared_ptr(std::move(producer_)); + boost::thread([=] + { + std::unique_ptr> pointer_guard(producer); + auto str = (*producer)->print(); + try + { + ensure_gpf_handler_installed_for_thread(u8(L"Destroyer: " + str).c_str()); + + if (!producer->unique()) + CASPAR_LOG(trace) << str << L" Not destroyed on asynchronous destruction thread: " << producer->use_count(); + else + CASPAR_LOG(trace) << str << L" Destroying on asynchronous destruction thread."; + } + catch(...){} + + try + { + pointer_guard.reset(); + CASPAR_LOG(info) << str << L" Destroyed."; + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + } + + --counter; + }).detach(); + } + + draw_frame receive() override {return producer_->receive();} + std::wstring print() const override {return producer_->print();} + void paused(bool value) override {producer_->paused(value);} + std::wstring name() const override {return producer_->name();} + uint32_t frame_number() const override {return producer_->frame_number();} + boost::property_tree::wptree info() const override {return producer_->info();} + std::future call(const std::vector& params) override {return producer_->call(params);} + variable& get_variable(const std::wstring& name) override {return producer_->get_variable(name);} + const std::vector& get_variables() const override {return producer_->get_variables();} + void leading_producer(const spl::shared_ptr& producer) override {return producer_->leading_producer(producer);} + uint32_t nb_frames() const override {return producer_->nb_frames();} + draw_frame last_frame() {return producer_->last_frame();} + draw_frame create_thumbnail_frame() {return producer_->create_thumbnail_frame();} + monitor::subject& monitor_output() override {return producer_->monitor_output();} + bool collides(double x, double y) const override {return producer_->collides(x, y);} + void on_interaction(const interaction_event::ptr& event) override {return producer_->on_interaction(event);} + constraints& pixel_constraints() override {return producer_->pixel_constraints();} +}; + +spl::shared_ptr create_destroy_proxy(spl::shared_ptr producer) +{ + return spl::make_shared(std::move(producer)); +} + +spl::shared_ptr do_create_producer(const frame_producer_dependencies& dependencies, const std::vector& params, const std::vector& factories, bool throw_on_fail = false) +{ + if(params.empty()) + CASPAR_THROW_EXCEPTION(invalid_argument() << arg_name_info("params") << arg_value_info("")); + + auto producer = frame_producer::empty(); + std::any_of(factories.begin(), factories.end(), [&](const producer_factory_t& factory) -> bool + { + try + { + producer = factory(dependencies, params); + } + catch(...) + { + if(throw_on_fail) + throw; + else + CASPAR_LOG_CURRENT_EXCEPTION(); + } + return producer != frame_producer::empty(); + }); + + if(producer == frame_producer::empty()) + producer = create_color_producer(dependencies.frame_factory, params); + + if(producer == frame_producer::empty()) + return producer; + + return producer; +} + +spl::shared_ptr frame_producer_registry::create_thumbnail_producer(const frame_producer_dependencies& dependencies, const std::wstring& media_file) const +{ + auto& thumbnail_factories = impl_->thumbnail_factories; + std::vector params; + params.push_back(media_file); + + auto producer = do_create_producer(dependencies, params, thumbnail_factories, true); + auto key_producer = frame_producer::empty(); + + try // to find a key file. + { + auto params_copy = params; + if (params_copy.size() > 0) + { + params_copy[0] += L"_A"; + key_producer = do_create_producer(dependencies, params_copy, thumbnail_factories, true); + if (key_producer == frame_producer::empty()) + { + params_copy[0] += L"LPHA"; + key_producer = do_create_producer(dependencies, params_copy, thumbnail_factories, true); + } + } + } + catch(...){} + + if (producer != frame_producer::empty() && key_producer != frame_producer::empty()) + return create_separated_producer(producer, key_producer); + + return producer; +} + +spl::shared_ptr frame_producer_registry::create_producer(const frame_producer_dependencies& dependencies, const std::vector& params) const +{ + auto& producer_factories = impl_->producer_factories; + auto producer = do_create_producer(dependencies, params, producer_factories); + auto key_producer = frame_producer::empty(); + + try // to find a key file. + { + auto params_copy = params; + if(params_copy.size() > 0) + { + params_copy[0] += L"_A"; + key_producer = do_create_producer(dependencies, params_copy, producer_factories); + if(key_producer == frame_producer::empty()) + { + params_copy[0] += L"LPHA"; + key_producer = do_create_producer(dependencies, params_copy, producer_factories); + } + } + } + catch(...){} + + if(producer != frame_producer::empty() && key_producer != frame_producer::empty()) + return create_separated_producer(producer, key_producer); + + if(producer == frame_producer::empty()) + { + std::wstring str; + for (auto& param : params) + str += param + L" "; + CASPAR_THROW_EXCEPTION(file_not_found() << msg_info("No match found for supplied commands. Check syntax.") << arg_value_info(u8(str))); + } + + return producer; +} + + +spl::shared_ptr frame_producer_registry::create_producer(const frame_producer_dependencies& dependencies, const std::wstring& params) const +{ + std::wstringstream iss(params); + std::vector tokens; + typedef std::istream_iterator > iterator; + std::copy(iterator(iss), iterator(), std::back_inserter(tokens)); + return create_producer(dependencies, tokens); +} + +}}