2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Helge Norberg, helge.norberg@svt.se
22 #include "../StdAfx.h"
27 #include "frame_producer.h"
29 #include "../video_channel.h"
30 #include "../diagnostics/call_context.h"
31 #include "../module_dependencies.h"
32 #include "../frame/draw_frame.h"
33 #include "../help/help_sink.h"
34 #include "../help/help_repository.h"
36 #include <common/env.h>
37 #include <common/os/filesystem.h>
38 #include <common/future.h>
40 #include <boost/filesystem.hpp>
41 #include <boost/algorithm/string/predicate.hpp>
42 #include <boost/optional.hpp>
43 #include <boost/property_tree/xml_parser.hpp>
44 #include <boost/property_tree/ptree.hpp>
50 namespace caspar { namespace core {
52 const spl::shared_ptr<cg_proxy>& cg_proxy::empty()
54 class empty_proxy : public cg_proxy
56 void add(int, const std::wstring&, bool, const std::wstring&, const std::wstring&) override {}
57 void remove(int) override {}
58 void play(int) override {}
59 void stop(int, unsigned int) override {}
60 void next(int) override {}
61 void update(int, const std::wstring&) override {}
62 std::wstring invoke(int, const std::wstring&) override { return L""; }
63 std::wstring description(int) override { return L"empty cg producer"; }
64 std::wstring template_host_info() override { return L"empty cg producer"; }
67 static spl::shared_ptr<cg_proxy> instance = spl::make_shared<empty_proxy>();
71 using namespace boost::multi_index;
73 struct cg_producer_registry::impl
79 meta_info_extractor info_extractor;
80 cg_proxy_factory proxy_factory;
81 cg_producer_factory producer_factory;
82 bool reusable_producer_instance;
85 mutable std::mutex mutex_;
86 std::map<std::wstring, record> records_by_extension_;
88 void register_cg_producer(
89 std::wstring cg_producer_name,
90 std::set<std::wstring> file_extensions,
91 meta_info_extractor info_extractor,
92 cg_proxy_factory proxy_factory,
93 cg_producer_factory producer_factory,
94 bool reusable_producer_instance)
96 std::lock_guard<std::mutex> lock(mutex_);
100 std::move(cg_producer_name),
101 std::move(info_extractor),
102 std::move(proxy_factory),
103 std::move(producer_factory),
104 reusable_producer_instance
107 for (auto& extension : file_extensions)
109 records_by_extension_.insert(std::make_pair(extension, rec));
113 spl::shared_ptr<frame_producer> create_producer(
114 const frame_producer_dependencies& dependencies,
115 const std::wstring& filename) const
117 auto found = find_record(filename);
120 return frame_producer::empty();
122 return found->producer_factory(dependencies, filename);
125 spl::shared_ptr<cg_proxy> get_proxy(const spl::shared_ptr<frame_producer>& producer) const
127 auto producer_name = producer->name();
129 std::lock_guard<std::mutex> lock(mutex_);
131 for (auto& elem : records_by_extension_)
133 if (elem.second.name == producer_name)
134 return elem.second.proxy_factory(producer);
137 return cg_proxy::empty();
140 spl::shared_ptr<cg_proxy> get_proxy(
141 const spl::shared_ptr<video_channel>& video_channel,
142 int render_layer) const
144 auto producer = spl::make_shared_ptr(video_channel->stage().foreground(render_layer).get());
146 return get_proxy(producer);
149 spl::shared_ptr<cg_proxy> get_or_create_proxy(
150 const spl::shared_ptr<video_channel>& video_channel,
151 const frame_producer_dependencies& dependencies,
153 const std::wstring& filename) const
155 using namespace boost::filesystem;
157 auto found = find_record(filename);
160 return cg_proxy::empty();
162 auto producer = spl::make_shared_ptr(video_channel->stage().foreground(render_layer).get());
163 auto current_producer_name = producer->name();
164 bool create_new = current_producer_name != found->name || !found->reusable_producer_instance;
168 diagnostics::scoped_call_context save;
169 diagnostics::call_context::for_thread().video_channel = video_channel->index();
170 diagnostics::call_context::for_thread().layer = render_layer;
172 producer = found->producer_factory(dependencies, filename);
173 video_channel->stage().load(render_layer, producer);
174 video_channel->stage().play(render_layer);
177 return found->proxy_factory(producer);
180 std::string read_meta_info(const std::wstring& filename) const
182 using namespace boost::filesystem;
184 auto basepath = path(env::template_folder()) / path(filename);
186 std::lock_guard<std::mutex> lock(mutex_);
188 for (auto& rec : records_by_extension_)
190 auto p = path(basepath.wstring() + rec.first);
191 auto found = find_case_insensitive(p.wstring());
194 return rec.second.info_extractor(*found);
197 CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"No meta info extractor for " + filename));
200 bool is_cg_extension(const std::wstring& extension) const
202 std::lock_guard<std::mutex> lock(mutex_);
204 return records_by_extension_.find(extension) != records_by_extension_.end();
207 std::wstring get_cg_producer_name(const std::wstring& filename) const
209 auto record = find_record(filename);
212 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(filename + L" is not a cg template."));
217 boost::optional<record> find_record(const std::wstring& filename) const
219 using namespace boost::filesystem;
221 auto basepath = path(env::template_folder()) / path(filename);
223 std::lock_guard<std::mutex> lock(mutex_);
225 for (auto& rec : records_by_extension_)
227 auto p = path(basepath.wstring() + rec.first);
229 if (find_case_insensitive(p.wstring()))
237 cg_producer_registry::cg_producer_registry() : impl_(new impl) { }
239 void cg_producer_registry::register_cg_producer(
240 std::wstring cg_producer_name,
241 std::set<std::wstring> file_extensions,
242 meta_info_extractor info_extractor,
243 cg_proxy_factory proxy_factory,
244 cg_producer_factory producer_factory,
245 bool reusable_producer_instance)
247 impl_->register_cg_producer(
248 std::move(cg_producer_name),
249 std::move(file_extensions),
250 std::move(info_extractor),
251 std::move(proxy_factory),
252 std::move(producer_factory),
253 reusable_producer_instance);
256 spl::shared_ptr<frame_producer> cg_producer_registry::create_producer(
257 const frame_producer_dependencies& dependencies,
258 const std::wstring& filename) const
260 return impl_->create_producer(dependencies, filename);
263 spl::shared_ptr<cg_proxy> cg_producer_registry::get_proxy(
264 const spl::shared_ptr<frame_producer>& producer) const
266 return impl_->get_proxy(producer);
269 spl::shared_ptr<cg_proxy> cg_producer_registry::get_proxy(
270 const spl::shared_ptr<video_channel>& video_channel,
271 int render_layer) const
273 return impl_->get_proxy(video_channel, render_layer);
276 spl::shared_ptr<cg_proxy> cg_producer_registry::get_or_create_proxy(
277 const spl::shared_ptr<video_channel>& video_channel,
278 const frame_producer_dependencies& dependencies,
280 const std::wstring& filename) const
282 return impl_->get_or_create_proxy(video_channel, dependencies, render_layer, filename);
285 std::string cg_producer_registry::read_meta_info(const std::wstring& filename) const
287 return impl_->read_meta_info(filename);
290 bool cg_producer_registry::is_cg_extension(const std::wstring& extension) const
292 return impl_->is_cg_extension(extension);
295 std::wstring cg_producer_registry::get_cg_producer_name(const std::wstring& filename) const
297 return impl_->get_cg_producer_name(filename);
300 class cg_proxy_as_producer : public frame_producer
302 spl::shared_ptr<frame_producer> producer_;
303 spl::shared_ptr<cg_proxy> proxy_;
304 std::wstring template_name_;
305 bool producer_has_its_own_variables_defined_;
307 std::map<std::wstring, std::shared_ptr<core::variable>> variables_;
308 std::vector<std::wstring> variable_names_;
309 core::binding<std::wstring> template_data_xml_ { [=] { return generate_template_data_xml(); } };
310 std::shared_ptr<void> template_data_change_subscription_;
311 bool is_playing_ = false;
313 cg_proxy_as_producer(
314 spl::shared_ptr<frame_producer> producer,
315 spl::shared_ptr<cg_proxy> proxy,
316 const std::wstring& template_name,
317 const std::vector<std::wstring>& parameter_specification)
318 : producer_(std::move(producer))
319 , proxy_(std::move(proxy))
320 , template_name_(std::move(template_name))
321 , producer_has_its_own_variables_defined_(!producer_->get_variables().empty())
323 if (parameter_specification.size() % 2 != 0)
324 CASPAR_THROW_EXCEPTION(user_error() << msg_info("Parameter specification must be a sequence of type and parameter name pairs"));
326 if (producer_has_its_own_variables_defined_ && !parameter_specification.empty())
327 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Producer " + producer_->name() + L" does not need help with available template parameters"));
329 for (int i = 0; i < parameter_specification.size(); i += 2)
331 auto& type = parameter_specification.at(i);
332 auto& parameter_name = parameter_specification.at(i + 1);
334 if (type == L"string")
335 create_parameter<std::wstring>(parameter_name);
336 else if (type == L"number")
337 create_parameter<double>(parameter_name);
339 CASPAR_THROW_EXCEPTION(user_error() << msg_info("The type in a parameter specification must be either string or number"));
342 template_data_change_subscription_ = template_data_xml_.on_change([=]
345 proxy_->update(0, template_data_xml_.get());
351 std::future<std::wstring> call(const std::vector<std::wstring>& params) override
353 auto& command = params.at(0);
355 if (command == L"play()")
357 proxy_->add(0, template_name_, true, L"", template_data_xml_.get());
360 else if (command == L"stop()")
362 else if (command == L"next()")
364 else if (command == L"invoke()")
365 proxy_->invoke(0, params.at(1));
367 return producer_->call(params);
369 return make_ready_future<std::wstring>(L"");
372 variable& get_variable(const std::wstring& name) override
374 if (producer_has_its_own_variables_defined_)
375 return producer_->get_variable(name);
377 auto found = variables_.find(name);
379 if (found == variables_.end())
380 CASPAR_THROW_EXCEPTION(user_error() << msg_info(name + L" not found in scene"));
382 return *found->second;
385 const std::vector<std::wstring>& get_variables() const override
387 return producer_has_its_own_variables_defined_ ? producer_->get_variables() : variable_names_;
390 draw_frame receive() override { return producer_->receive(); }
391 std::wstring print() const override { return producer_->print(); }
392 void paused(bool value) override { producer_->paused(value); }
393 std::wstring name() const override { return producer_->name(); }
394 uint32_t frame_number() const override { return producer_->frame_number(); }
395 boost::property_tree::wptree info() const override { return producer_->info(); }
396 void leading_producer(const spl::shared_ptr<frame_producer>& producer) override { return producer_->leading_producer(producer); }
397 uint32_t nb_frames() const override { return producer_->nb_frames(); }
398 draw_frame last_frame() override { return producer_->last_frame(); }
399 monitor::subject& monitor_output() override { return producer_->monitor_output(); }
400 bool collides(double x, double y) const override { return producer_->collides(x, y); }
401 void on_interaction(const interaction_event::ptr& event) override { return producer_->on_interaction(event); }
402 constraints& pixel_constraints() override { return producer_->pixel_constraints(); }
404 std::wstring generate_template_data_xml() const
406 boost::property_tree::wptree document;
407 boost::property_tree::wptree template_data;
409 for (auto& parameter_name : variable_names_)
411 boost::property_tree::wptree component_data;
413 component_data.add(L"<xmlattr>.id", parameter_name);
414 component_data.add(L"data.<xmlattr>.id", L"text");
415 component_data.add(L"data.<xmlattr>.value", variables_.at(parameter_name)->to_string());
417 template_data.add_child(L"componentData", component_data);
420 document.add_child(L"templateData", template_data);
422 std::wstringstream xml;
423 static boost::property_tree::xml_writer_settings<std::wstring> NO_INDENTATION_SETTINGS(' ', 0);
424 boost::property_tree::xml_parser::write_xml(xml, document, NO_INDENTATION_SETTINGS);
426 static std::wstring PROCESSING_INSTRUCTION_TO_REMOVE = L"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
427 return xml.str().substr(PROCESSING_INSTRUCTION_TO_REMOVE.length());
431 void create_parameter(const std::wstring& name)
433 auto var = spl::make_shared<core::variable_impl<T>>();
435 template_data_xml_.depend_on(var->value());
436 variables_.insert(std::make_pair(name, var));
437 variable_names_.push_back(name);
441 void describe_cg_proxy_as_producer(core::help_sink& sink, const core::help_repository& repo)
443 sink.short_description(L"Wraps any CG producer for compatibility with scene producer.");
444 sink.syntax(L"[CG] [template:string] {[param1_type:\"string\",\"number\"] [param1_name:string] {[param2_type:\"string\",\"number\"] [param2_name:string] {...}}}");
445 sink.para()->text(L"Wraps any CG producer for compatibility with scene producer.");
446 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.");
447 sink.para()->text(L"Examples only really usable from within the scene producer implementation:");
448 sink.example(L">> PLAY 1-10 [CG] folder/flas_template string f0 number f1");
449 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:");
450 sink.example(L">> CALL 1-10 play()");
451 sink.example(L">> CALL 1-10 next()");
452 sink.example(L">> CALL 1-10 invoke() label");
453 sink.example(L">> CALL 1-10 stop()");
456 spl::shared_ptr<frame_producer> create_cg_proxy_as_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
458 if (!boost::iequals(params.at(0), L"[CG]") || params.size() < 2)
459 return frame_producer::empty();
461 auto template_name = params.at(1);
462 auto producer = dependencies.cg_registry->create_producer(dependencies, template_name);
463 auto proxy = dependencies.cg_registry->get_proxy(producer);
464 auto params2 = params;
466 if (proxy == cg_proxy::empty())
467 CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"No template with name " + template_name + L" found"));
469 // Remove "[CG]" and template_name to only leave the parameter specification part
470 params2.erase(params2.begin(), params2.begin() + 2);
472 return spl::make_shared<cg_proxy_as_producer>(std::move(producer), std::move(proxy), template_name, params2);
475 void init_cg_proxy_as_producer(core::module_dependencies dependencies)
477 dependencies.producer_registry->register_producer_factory(L"CG proxy wrapper", create_cg_proxy_as_producer, describe_cg_proxy_as_producer);