From ff8eb9202aef3824d41592c53c9eacedf462f920 Mon Sep 17 00:00:00 2001 From: Helge Norberg Date: Mon, 20 Feb 2017 19:13:31 +0100 Subject: [PATCH] [CG] Created producer that wraps a CG producer in a way that it can be treated as a normal producer and also integrates well on a layer in a scene instance --- core/producer/cg_proxy.cpp | 189 +++++++++++++++++++++++++++++++++++++ core/producer/cg_proxy.h | 3 +- shell/generate_docs.cpp | 10 +- shell/server.cpp | 1 + 4 files changed, 199 insertions(+), 4 deletions(-) diff --git a/core/producer/cg_proxy.cpp b/core/producer/cg_proxy.cpp index 74a1318e8..94ed28c37 100644 --- a/core/producer/cg_proxy.cpp +++ b/core/producer/cg_proxy.cpp @@ -22,23 +22,32 @@ #include "../StdAfx.h" #include "cg_proxy.h" +#include "variable.h" #include "frame_producer.h" #include "stage.h" #include "../video_channel.h" #include "../diagnostics/call_context.h" +#include "../module_dependencies.h" +#include "../frame/draw_frame.h" +#include "../help/help_sink.h" +#include "../help/help_repository.h" #include #include +#include #include #include #include #include #include +#include +#include #include #include +#include namespace caspar { namespace core { @@ -290,4 +299,184 @@ std::wstring cg_producer_registry::get_cg_producer_name(const std::wstring& file return impl_->get_cg_producer_name(filename); } +class cg_proxy_as_producer : public frame_producer +{ + spl::shared_ptr producer_; + spl::shared_ptr proxy_; + std::wstring template_name_; + bool producer_has_its_own_variables_defined_; + + std::map> variables_; + std::vector variable_names_; + core::binding template_data_xml_ { [=] { return generate_template_data_xml(); } }; + std::shared_ptr template_data_change_subscription_; + bool is_playing_ = false; +public: + cg_proxy_as_producer( + spl::shared_ptr producer, + spl::shared_ptr proxy, + const std::wstring& template_name, + const std::vector& parameter_specification) + : producer_(std::move(producer)) + , proxy_(std::move(proxy)) + , template_name_(std::move(template_name)) + , producer_has_its_own_variables_defined_(!producer_->get_variables().empty()) + { + if (parameter_specification.size() % 2 != 0) + CASPAR_THROW_EXCEPTION(user_error() << msg_info("Parameter specification must be a sequence of type and parameter name pairs")); + + if (producer_has_its_own_variables_defined_ && !parameter_specification.empty()) + CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Producer " + producer_->name() + L" does not need help with available template parameters")); + + for (int i = 0; i < parameter_specification.size(); i += 2) + { + auto& type = parameter_specification.at(i); + auto& parameter_name = parameter_specification.at(i + 1); + + if (type == L"string") + create_parameter(parameter_name); + else if (type == L"number") + create_parameter(parameter_name); + else + CASPAR_THROW_EXCEPTION(user_error() << msg_info("The type in a parameter specification must be either string or number")); + } + + template_data_change_subscription_ = template_data_xml_.on_change([=] + { + if (is_playing_) + proxy_->update(0, template_data_xml_.get()); + }); + } + + // frame_producer + + std::future call(const std::vector& params) override + { + auto& command = params.at(0); + + if (command == L"play()") + { + proxy_->add(0, template_name_, true, L"", template_data_xml_.get()); + is_playing_ = true; + } + else if (command == L"stop()") + proxy_->stop(0, 0); + else if (command == L"next()") + proxy_->next(0); + else if (command == L"invoke()") + proxy_->invoke(0, params.at(1)); + else + return producer_->call(params); + + return make_ready_future(L""); + } + + variable& get_variable(const std::wstring& name) override + { + if (producer_has_its_own_variables_defined_) + return producer_->get_variable(name); + + auto found = variables_.find(name); + + if (found == variables_.end()) + CASPAR_THROW_EXCEPTION(user_error() << msg_info(name + L" not found in scene")); + + return *found->second; + } + + const std::vector& get_variables() const override + { + return producer_has_its_own_variables_defined_ ? producer_->get_variables() : variable_names_; + } + + 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(); } + 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(); } +private: + std::wstring generate_template_data_xml() const + { + boost::property_tree::wptree document; + boost::property_tree::wptree template_data; + + for (auto& parameter_name : variable_names_) + { + boost::property_tree::wptree component_data; + + component_data.add(L".id", parameter_name); + component_data.add(L"data..id", L"text"); + component_data.add(L"data..value", variables_.at(parameter_name)->to_string()); + + template_data.add_child(L"componentData", component_data); + } + + document.add_child(L"templateData", template_data); + + std::wstringstream xml; + static boost::property_tree::xml_writer_settings NO_INDENTATION_SETTINGS(' ', 0); + boost::property_tree::xml_parser::write_xml(xml, document, NO_INDENTATION_SETTINGS); + // Necessary hack + static std::wstring PROCESSING_INSTRUCTION_TO_REMOVE = L"\n"; + return xml.str().substr(PROCESSING_INSTRUCTION_TO_REMOVE.length()); + } + + template + void create_parameter(const std::wstring& name) + { + auto var = spl::make_shared>(); + + template_data_xml_.depend_on(var->value()); + variables_.insert(std::make_pair(name, var)); + variable_names_.push_back(name); + } +}; + +void describe_cg_proxy_as_producer(core::help_sink& sink, const core::help_repository& repo) +{ + sink.short_description(L"Wraps any CG producer for compatibility with scene producer."); + sink.syntax(L"[CG] [template:string] {[param1_type:\"string\",\"number\"] [param1_name:string] {[param2_type:\"string\",\"number\"] [param2_name:string] {...}}}"); + sink.para()->text(L"Wraps any CG producer for compatibility with scene producer."); + sink.para()->text(L"It allows the user to specify what template parameters should be exposed to the parent scene. This is only required for Flash and HTML templates. PSD and Scene templates does this automatically."); + sink.para()->text(L"Examples only really usable from within the scene producer implementation:"); + sink.example(L">> PLAY 1-10 [CG] folder/flas_template string f0 number f1"); + sink.para()->text(L"...followed by the scene producer setting variables causing the equivalent of a ")->code(L"CG ADD")->text(L". Then the following calls can be made:"); + sink.example(L">> CALL 1-10 play()"); + sink.example(L">> CALL 1-10 next()"); + sink.example(L">> CALL 1-10 invoke() label"); + sink.example(L">> CALL 1-10 stop()"); +} + +spl::shared_ptr create_cg_proxy_as_producer(const core::frame_producer_dependencies& dependencies, const std::vector& params) +{ + if (!boost::iequals(params.at(0), L"[CG]") || params.size() < 2) + return frame_producer::empty(); + + auto template_name = params.at(1); + auto producer = dependencies.cg_registry->create_producer(dependencies, template_name); + auto proxy = dependencies.cg_registry->get_proxy(producer); + auto params2 = params; + + if (proxy == cg_proxy::empty()) + CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"No template with name " + template_name + L" found")); + + // Remove "[CG]" and template_name to only leave the parameter specification part + params2.erase(params2.begin(), params2.begin() + 2); + + return spl::make_shared(std::move(producer), std::move(proxy), template_name, params2); +} + +void init_cg_proxy_as_producer(core::module_dependencies dependencies) +{ + dependencies.producer_registry->register_producer_factory(L"CG proxy wrapper", create_cg_proxy_as_producer, describe_cg_proxy_as_producer); +} + }} diff --git a/core/producer/cg_proxy.h b/core/producer/cg_proxy.h index 93be32c0e..9684e94a8 100644 --- a/core/producer/cg_proxy.h +++ b/core/producer/cg_proxy.h @@ -32,7 +32,7 @@ #include namespace caspar { namespace core { - + class cg_proxy { public: @@ -96,5 +96,6 @@ private: spl::shared_ptr impl_; }; +void init_cg_proxy_as_producer(core::module_dependencies dependencies); }} diff --git a/shell/generate_docs.cpp b/shell/generate_docs.cpp index 25cd22b49..c5ea1807c 100644 --- a/shell/generate_docs.cpp +++ b/shell/generate_docs.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -152,7 +154,7 @@ public: out_ << std::endl; } private: - void begin_item(const std::wstring& name) override + void begin_item(const std::wstring& name) override { out_ << L"==" << name << L"==\n" << std::endl; } @@ -231,13 +233,15 @@ int main(int argc, char** argv) core::module_dependencies dependencies(system_info_provider_repo, cg_registry, media_info_repo, producer_registry, consumer_registry); initialize_modules(dependencies); - core::text::init(dependencies); + core::init_cg_proxy_as_producer(dependencies); + core::scene::init(dependencies); + core::syncto::init(dependencies); generate_amcp_commands_help(*help_repo); generate_producers_help(*help_repo); generate_consumers_help(*help_repo); uninitialize_modules(); - + return 0; } diff --git a/shell/server.cpp b/shell/server.cpp index 763659c29..87b7e105c 100644 --- a/shell/server.cpp +++ b/shell/server.cpp @@ -171,6 +171,7 @@ struct server::impl : boost::noncopyable initialize_modules(dependencies); core::text::init(dependencies); + core::init_cg_proxy_as_producer(dependencies); core::scene::init(dependencies); core::syncto::init(dependencies); help_repo_->register_item({ L"producer" }, L"Color Producer", &core::describe_color_producer); -- 2.39.2