]> 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 83099f2a3ab3ab768b58f9a48404473acc459dff..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 {
 
@@ -75,9 +84,6 @@ private:
                bool                                    reusable_producer_instance;
        };
 
-       struct name {};
-       struct extension {};
-
        mutable boost::mutex                    mutex_;
        std::map<std::wstring, record>  records_by_extension_;
 public:
@@ -190,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
@@ -199,6 +205,16 @@ public:
 
                return records_by_extension_.find(extension) != records_by_extension_.end();
        }
+
+       std::wstring get_cg_producer_name(const std::wstring& filename) const
+       {
+               auto record = find_record(filename);
+
+               if (!record)
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(filename + L" is not a cg template."));
+
+               return record->name;
+       }
 private:
        boost::optional<record> find_record(const std::wstring& filename) const
        {
@@ -278,4 +294,189 @@ bool cg_producer_registry::is_cg_extension(const std::wstring& extension) const
        return impl_->is_cg_extension(extension);
 }
 
+std::wstring cg_producer_registry::get_cg_producer_name(const std::wstring& filename) const
+{
+       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);
+}
+
 }}