#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 <common/env.h>
#include <common/os/filesystem.h>
+#include <common/future.h>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/optional.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+#include <boost/property_tree/ptree.hpp>
#include <future>
#include <map>
+#include <sstream>
namespace caspar { namespace core {
return impl_->get_cg_producer_name(filename);
}
+class cg_proxy_as_producer : public frame_producer
+{
+ spl::shared_ptr<frame_producer> producer_;
+ spl::shared_ptr<cg_proxy> proxy_;
+ std::wstring template_name_;
+ bool producer_has_its_own_variables_defined_;
+
+ std::map<std::wstring, std::shared_ptr<core::variable>> variables_;
+ std::vector<std::wstring> variable_names_;
+ core::binding<std::wstring> template_data_xml_ { [=] { return generate_template_data_xml(); } };
+ std::shared_ptr<void> template_data_change_subscription_;
+ bool is_playing_ = false;
+public:
+ cg_proxy_as_producer(
+ spl::shared_ptr<frame_producer> producer,
+ spl::shared_ptr<cg_proxy> proxy,
+ const std::wstring& template_name,
+ const std::vector<std::wstring>& 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<std::wstring>(parameter_name);
+ else if (type == L"number")
+ create_parameter<double>(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<std::wstring> call(const std::vector<std::wstring>& 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<std::wstring>(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<std::wstring>& 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<frame_producer>& 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"<xmlattr>.id", parameter_name);
+ component_data.add(L"data.<xmlattr>.id", L"text");
+ component_data.add(L"data.<xmlattr>.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<std::wstring> 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"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+ return xml.str().substr(PROCESSING_INSTRUCTION_TO_REMOVE.length());
+ }
+
+ template<typename T>
+ void create_parameter(const std::wstring& name)
+ {
+ auto var = spl::make_shared<core::variable_impl<T>>();
+
+ 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<frame_producer> create_cg_proxy_as_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& 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<cg_proxy_as_producer>(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);
+}
+
}}