]> git.sesse.net Git - casparcg/blobdiff - core/producer/cg_proxy.cpp
[CG] Created producer that wraps a CG producer in a way that it can be treated as...
[casparcg] / core / producer / cg_proxy.cpp
index be5bf421f2e8e002be59cc9df5ce0e794e84d7b0..94ed28c375321a5ee4114d04a2ff429517e67e64 100644 (file)
 #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 {
 
@@ -187,7 +196,7 @@ public:
                                return rec.second.info_extractor(*found);
                }
 
-               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(L"No meta info extractor for " + filename));
+               CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"No meta info extractor for " + filename));
        }
 
        bool is_cg_extension(const std::wstring& extension) const
@@ -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<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);
+}
+
 }}