]> git.sesse.net Git - casparcg/blob - core/producer/cg_proxy.cpp
Fix a few Clang warnings.
[casparcg] / core / producer / cg_proxy.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #include "../StdAfx.h"
23
24 #include "cg_proxy.h"
25 #include "variable.h"
26
27 #include "frame_producer.h"
28 #include "stage.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"
35
36 #include <common/env.h>
37 #include <common/os/filesystem.h>
38 #include <common/future.h>
39
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>
45
46 #include <future>
47 #include <map>
48 #include <sstream>
49
50 namespace caspar { namespace core {
51
52 const spl::shared_ptr<cg_proxy>& cg_proxy::empty()
53 {
54         class empty_proxy : public cg_proxy
55         {
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"; }
65         };
66
67         static spl::shared_ptr<cg_proxy> instance = spl::make_shared<empty_proxy>();
68         return instance;
69 }
70
71 using namespace boost::multi_index;
72
73 struct cg_producer_registry::impl
74 {
75 private:
76         struct record
77         {
78                 std::wstring                    name;
79                 meta_info_extractor             info_extractor;
80                 cg_proxy_factory                proxy_factory;
81                 cg_producer_factory             producer_factory;
82                 bool                                    reusable_producer_instance;
83         };
84
85         mutable std::mutex                      mutex_;
86         std::map<std::wstring, record>  records_by_extension_;
87 public:
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)
95         {
96                 std::lock_guard<std::mutex> lock(mutex_);
97
98                 record rec
99                 {
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
105                 };
106
107                 for (auto& extension : file_extensions)
108                 {
109                         records_by_extension_.insert(std::make_pair(extension, rec));
110                 }
111         }
112
113         spl::shared_ptr<frame_producer> create_producer(
114                         const frame_producer_dependencies& dependencies,
115                         const std::wstring& filename) const
116         {
117                 auto found = find_record(filename);
118
119                 if (!found)
120                         return frame_producer::empty();
121
122                 return found->producer_factory(dependencies, filename);
123         }
124
125         spl::shared_ptr<cg_proxy> get_proxy(const spl::shared_ptr<frame_producer>& producer) const
126         {
127                 auto producer_name = producer->name();
128
129                 std::lock_guard<std::mutex> lock(mutex_);
130
131                 for (auto& elem : records_by_extension_)
132                 {
133                         if (elem.second.name == producer_name)
134                                 return elem.second.proxy_factory(producer);
135                 }
136
137                 return cg_proxy::empty();
138         }
139
140         spl::shared_ptr<cg_proxy> get_proxy(
141                         const spl::shared_ptr<video_channel>& video_channel,
142                         int render_layer) const
143         {
144                 auto producer = spl::make_shared_ptr(video_channel->stage().foreground(render_layer).get());
145
146                 return get_proxy(producer);
147         }
148
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,
152                         int render_layer,
153                         const std::wstring& filename) const
154         {
155                 using namespace boost::filesystem;
156
157                 auto found = find_record(filename);
158
159                 if (!found)
160                         return cg_proxy::empty();
161
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;
165
166                 if (create_new)
167                 {
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;
171
172                         producer = found->producer_factory(dependencies, filename);
173                         video_channel->stage().load(render_layer, producer);
174                         video_channel->stage().play(render_layer);
175                 }
176
177                 return found->proxy_factory(producer);
178         }
179
180         std::string read_meta_info(const std::wstring& filename) const
181         {
182                 using namespace boost::filesystem;
183
184                 auto basepath = path(env::template_folder()) / path(filename);
185
186                 std::lock_guard<std::mutex> lock(mutex_);
187
188                 for (auto& rec : records_by_extension_)
189                 {
190                         auto p = path(basepath.wstring() + rec.first);
191                         auto found = find_case_insensitive(p.wstring());
192
193                         if (found)
194                                 return rec.second.info_extractor(*found);
195                 }
196
197                 CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"No meta info extractor for " + filename));
198         }
199
200         bool is_cg_extension(const std::wstring& extension) const
201         {
202                 std::lock_guard<std::mutex> lock(mutex_);
203
204                 return records_by_extension_.find(extension) != records_by_extension_.end();
205         }
206
207         std::wstring get_cg_producer_name(const std::wstring& filename) const
208         {
209                 auto record = find_record(filename);
210
211                 if (!record)
212                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(filename + L" is not a cg template."));
213
214                 return record->name;
215         }
216 private:
217         boost::optional<record> find_record(const std::wstring& filename) const
218         {
219                 using namespace boost::filesystem;
220
221                 auto basepath = path(env::template_folder()) / path(filename);
222
223                 std::lock_guard<std::mutex> lock(mutex_);
224
225                 for (auto& rec : records_by_extension_)
226                 {
227                         auto p = path(basepath.wstring() + rec.first);
228
229                         if (find_case_insensitive(p.wstring()))
230                                 return rec.second;
231                 }
232
233                 return boost::none;
234         }
235 };
236
237 cg_producer_registry::cg_producer_registry() : impl_(new impl) { }
238
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)
246 {
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);
254 }
255
256 spl::shared_ptr<frame_producer> cg_producer_registry::create_producer(
257                 const frame_producer_dependencies& dependencies,
258                 const std::wstring& filename) const
259 {
260         return impl_->create_producer(dependencies, filename);
261 }
262
263 spl::shared_ptr<cg_proxy> cg_producer_registry::get_proxy(
264                 const spl::shared_ptr<frame_producer>& producer) const
265 {
266         return impl_->get_proxy(producer);
267 }
268
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
272 {
273         return impl_->get_proxy(video_channel, render_layer);
274 }
275
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,
279                 int render_layer,
280                 const std::wstring& filename) const
281 {
282         return impl_->get_or_create_proxy(video_channel, dependencies, render_layer, filename);
283 }
284
285 std::string cg_producer_registry::read_meta_info(const std::wstring& filename) const
286 {
287         return impl_->read_meta_info(filename);
288 }
289
290 bool cg_producer_registry::is_cg_extension(const std::wstring& extension) const
291 {
292         return impl_->is_cg_extension(extension);
293 }
294
295 std::wstring cg_producer_registry::get_cg_producer_name(const std::wstring& filename) const
296 {
297         return impl_->get_cg_producer_name(filename);
298 }
299
300 class cg_proxy_as_producer : public frame_producer
301 {
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_;
306
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;
312 public:
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())
322         {
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"));
325
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"));
328
329                 for (int i = 0; i < parameter_specification.size(); i += 2)
330                 {
331                         auto& type                              = parameter_specification.at(i);
332                         auto& parameter_name    = parameter_specification.at(i + 1);
333
334                         if (type == L"string")
335                                 create_parameter<std::wstring>(parameter_name);
336                         else if (type == L"number")
337                                 create_parameter<double>(parameter_name);
338                         else
339                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info("The type in a parameter specification must be either string or number"));
340                 }
341
342                 template_data_change_subscription_ = template_data_xml_.on_change([=]
343                 {
344                         if (is_playing_)
345                                 proxy_->update(0, template_data_xml_.get());
346                 });
347         }
348
349         // frame_producer
350
351         std::future<std::wstring> call(const std::vector<std::wstring>& params) override
352         {
353                 auto& command = params.at(0);
354
355                 if (command == L"play()")
356                 {
357                         proxy_->add(0, template_name_, true, L"", template_data_xml_.get());
358                         is_playing_ = true;
359                 }
360                 else if (command == L"stop()")
361                         proxy_->stop(0, 0);
362                 else if (command == L"next()")
363                         proxy_->next(0);
364                 else if (command == L"invoke()")
365                         proxy_->invoke(0, params.at(1));
366                 else
367                         return producer_->call(params);
368
369                 return make_ready_future<std::wstring>(L"");
370         }
371
372         variable& get_variable(const std::wstring& name) override
373         {
374                 if (producer_has_its_own_variables_defined_)
375                         return producer_->get_variable(name);
376
377                 auto found = variables_.find(name);
378
379                 if (found == variables_.end())
380                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(name + L" not found in scene"));
381
382                 return *found->second;
383         }
384
385         const std::vector<std::wstring>& get_variables() const override
386         {
387                 return producer_has_its_own_variables_defined_ ? producer_->get_variables() : variable_names_;
388         }
389
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(); }
403 private:
404         std::wstring generate_template_data_xml() const
405         {
406                 boost::property_tree::wptree document;
407                 boost::property_tree::wptree template_data;
408
409                 for (auto& parameter_name : variable_names_)
410                 {
411                         boost::property_tree::wptree component_data;
412
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());
416
417                         template_data.add_child(L"componentData", component_data);
418                 }
419
420                 document.add_child(L"templateData", template_data);
421
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);
425                 // Necessary hack
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());
428         }
429
430         template<typename T>
431         void create_parameter(const std::wstring& name)
432         {
433                 auto var = spl::make_shared<core::variable_impl<T>>();
434
435                 template_data_xml_.depend_on(var->value());
436                 variables_.insert(std::make_pair(name, var));
437                 variable_names_.push_back(name);
438         }
439 };
440
441 void describe_cg_proxy_as_producer(core::help_sink& sink, const core::help_repository& repo)
442 {
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()");
454 }
455
456 spl::shared_ptr<frame_producer> create_cg_proxy_as_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
457 {
458         if (!boost::iequals(params.at(0), L"[CG]") || params.size() < 2)
459                 return frame_producer::empty();
460
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;
465
466         if (proxy == cg_proxy::empty())
467                 CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"No template with name " + template_name + L" found"));
468
469         // Remove "[CG]" and template_name to only leave the parameter specification part
470         params2.erase(params2.begin(), params2.begin() + 2);
471
472         return spl::make_shared<cg_proxy_as_producer>(std::move(producer), std::move(proxy), template_name, params2);
473 }
474
475 void init_cg_proxy_as_producer(core::module_dependencies dependencies)
476 {
477         dependencies.producer_registry->register_producer_factory(L"CG proxy wrapper", create_cg_proxy_as_producer, describe_cg_proxy_as_producer);
478 }
479
480 }}