X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Fframe_producer.cpp;h=46ca3e4162f7a8cc682e4f568ff3eaa2e263dbda;hb=726897adbf881d3b75f171fff24f2b917ba5f05a;hp=8ab9184154540b842b05b870a55dc547cb34a7e8;hpb=62223382349043462909cc6da53c969e0c3a2e3c;p=casparcg diff --git a/core/producer/frame_producer.cpp b/core/producer/frame_producer.cpp index 8ab918415..46ca3e416 100644 --- a/core/producer/frame_producer.cpp +++ b/core/producer/frame_producer.cpp @@ -1,243 +1,413 @@ -/* -* 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 -#include -#include -#include -#include - -namespace caspar { namespace core { - -std::vector g_factories; - -boost::unique_future frame_producer::call(const std::wstring&) -{ - BOOST_THROW_EXCEPTION(not_supported()); -} - -struct empty_frame_producer : public frame_producer -{ - virtual spl::shared_ptr receive(int){return draw_frame::empty();} - virtual spl::shared_ptr last_frame() const{return draw_frame::empty();} - virtual void set_frame_factory(const spl::shared_ptr&){} - virtual uint32_t nb_frames() const {return 0;} - virtual std::wstring print() const { return L"empty";} - - virtual boost::property_tree::wptree info() const override - { - boost::property_tree::wptree info; - info.add(L"type", L"empty-producer"); - return info; - } -}; - -const spl::shared_ptr& frame_producer::empty() // nothrow -{ - static spl::shared_ptr producer = spl::make_shared(); - return producer; -} - -spl::shared_ptr receive_and_follow(spl::shared_ptr& producer, int hints) -{ - auto frame = producer->receive(hints); - if(frame == draw_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); - } - - return receive_and_follow(producer, hints); - } - return frame; -} - -void register_producer_factory(const producer_factory_t& factory) -{ - g_factories.push_back(factory); -} - -class destroy_producer_proxy : public frame_producer -{ - std::unique_ptr> producer_; -public: - destroy_producer_proxy(spl::shared_ptr&& producer) - : producer_(new std::shared_ptr(std::move(producer))) - { - } - - ~destroy_producer_proxy() - { - static tbb::atomic counter = tbb::atomic(); - - ++counter; - CASPAR_VERIFY(counter < 32); - - auto producer = producer_.release(); - async([=] - { - std::unique_ptr> pointer_guard(producer); - - auto str = (*producer)->print(); - try - { - 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(...){} - - pointer_guard.reset(); - - --counter; - }); - } - - virtual spl::shared_ptr receive(int hints) override {return (*producer_)->receive(hints);} - virtual spl::shared_ptr last_frame() const override {return (*producer_)->last_frame();} - virtual std::wstring print() const override {return (*producer_)->print();} - virtual boost::property_tree::wptree info() const override {return (*producer_)->info();} - virtual boost::unique_future call(const std::wstring& str) override {return (*producer_)->call(str);} - virtual spl::shared_ptr get_following_producer() const override {return (*producer_)->get_following_producer();} - virtual void set_leading_producer(const spl::shared_ptr& producer) override {return (*producer_)->set_leading_producer(producer);} - virtual uint32_t nb_frames() const override {return (*producer_)->nb_frames();} -}; - -class print_producer_proxy : public frame_producer -{ - std::shared_ptr producer_; -public: - print_producer_proxy(spl::shared_ptr&& producer) - : producer_(std::move(producer)) - { - CASPAR_LOG(info) << producer_->print() << L" Initialized."; - } - - ~print_producer_proxy() - { - auto str = producer_->print(); - CASPAR_LOG(trace) << str << L" Uninitializing."; - producer_.reset(); - CASPAR_LOG(info) << str << L" Uninitialized."; - } - - - virtual spl::shared_ptr receive(int hints) override {return producer_->receive(hints);} - virtual spl::shared_ptr last_frame() const override {return producer_->last_frame();} - virtual std::wstring print() const override {return producer_->print();} - virtual boost::property_tree::wptree info() const override {return producer_->info();} - virtual boost::unique_future call(const std::wstring& str) override {return producer_->call(str);} - virtual spl::shared_ptr get_following_producer() const override {return producer_->get_following_producer();} - virtual void set_leading_producer(const spl::shared_ptr& producer) override {return producer_->set_leading_producer(producer);} - virtual uint32_t nb_frames() const override {return producer_->nb_frames();} -}; - -spl::shared_ptr do_create_producer(const spl::shared_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 producer_factory_t& 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); - - if(producer == frame_producer::empty()) - return producer; - - return spl::make_shared( - spl::make_shared( - std::move(producer))); -} - -spl::shared_ptr create_producer(const spl::shared_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()) - return 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(u8(str))); - } - - return producer; -} - - -spl::shared_ptr create_producer(const spl::shared_ptr& factory, const std::wstring& params) -{ - std::wstringstream iss(params); - std::vector tokens; - typedef std::istream_iterator > iterator; - std::copy(iterator(iss), iterator(), std::back_inserter(tokens)); - return create_producer(factory, tokens); -} - -}} \ 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_producers; + 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(const thumbnail_producer_t& thumbnail_producer) +{ + impl_->thumbnail_producers.push_back(thumbnail_producer); +} + +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_); + } +}; + +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(); +} + +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(user_error() + << 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_implemented());} + variable& get_variable(const std::wstring& name) override { CASPAR_THROW_EXCEPTION(not_implemented()); } + const std::vector& get_variables() const override { static std::vector empty; return empty; } + draw_frame last_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(debug) << str << L" Not destroyed on asynchronous destruction thread: " << producer->use_count(); + else + CASPAR_LOG(debug) << 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();} + 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() << msg_info("params cannot be empty")); + + auto producer = frame_producer::empty(); + std::any_of(factories.begin(), factories.end(), [&](const producer_factory_t& factory) -> bool + { + try + { + producer = factory(dependencies, params); + } + catch (user_error&) + { + throw; + } + 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; +} + +draw_frame do_create_thumbnail_frame( + const frame_producer_dependencies& dependencies, + const std::wstring& media_file, + const std::vector& thumbnail_producers) +{ + for (auto& thumbnail_producer : thumbnail_producers) + { + auto frame = thumbnail_producer(dependencies, media_file); + + if (frame != draw_frame::empty()) + return frame; + } + + return draw_frame::empty(); +} + +draw_frame frame_producer_registry::create_thumbnail(const frame_producer_dependencies& dependencies, const std::wstring& media_file) const +{ + auto& thumbnail_producers = impl_->thumbnail_producers; + std::vector params; + params.push_back(media_file); + + auto fill_frame = do_create_thumbnail_frame(dependencies, media_file, thumbnail_producers); + auto key_frame = do_create_thumbnail_frame(dependencies, media_file + L"_A", thumbnail_producers); + + if (key_frame == draw_frame::empty()) + key_frame = do_create_thumbnail_frame(dependencies, media_file + L"_ALPHA", thumbnail_producers); + + if (fill_frame != draw_frame::empty() && key_frame != draw_frame::empty()) + return draw_frame::mask(fill_frame, key_frame); + + return fill_frame; +} + +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(); + + if (!params.empty() && !boost::contains(params.at(0), L"://")) + { + 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); +} +}}