]> git.sesse.net Git - casparcg/blob - modules/flash/producer/flash_producer.cpp
Merge branch 'master' of https://github.com/CasparCG/Server
[casparcg] / modules / flash / producer / flash_producer.cpp
1 /*\r
2 * Copyright 2013 Sveriges Television AB http://casparcg.com/\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\r
5 *\r
6 * CasparCG is free software: you can redistribute it and/or modify\r
7 * it under the terms of the GNU General Public License as published by\r
8 * the Free Software Foundation, either version 3 of the License, or\r
9 * (at your option) any later version.\r
10 *\r
11 * CasparCG is distributed in the hope that it will be useful,\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 * GNU General Public License for more details.\r
15 *\r
16 * You should have received a copy of the GNU General Public License\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 * Author: Robert Nagy, ronag89@gmail.com\r
20 */\r
21 \r
22 #include "../stdafx.h"\r
23 \r
24 #if defined(_MSC_VER)\r
25 #pragma warning (disable : 4146)\r
26 #pragma warning (disable : 4244)\r
27 #endif\r
28 \r
29 #include "flash_producer.h"\r
30 #include "FlashAxContainer.h"\r
31 \r
32 #include "../util/swf.h"\r
33 \r
34 #include <core/video_format.h>\r
35 \r
36 #include <core/monitor/monitor.h>\r
37 #include <core/parameters/parameters.h>\r
38 #include <core/producer/frame/basic_frame.h>\r
39 #include <core/producer/frame/frame_factory.h>\r
40 #include <core/mixer/write_frame.h>\r
41 \r
42 #include <common/env.h>\r
43 #include <common/concurrency/executor.h>\r
44 #include <common/concurrency/lock.h>\r
45 #include <common/concurrency/future_util.h>\r
46 #include <common/diagnostics/graph.h>\r
47 #include <common/memory/memcpy.h>\r
48 #include <common/memory/memclr.h>\r
49 #include <common/utility/timer.h>\r
50 \r
51 #include <boost/filesystem.hpp>\r
52 #include <boost/property_tree/ptree.hpp>\r
53 #include <boost/thread.hpp>\r
54 #include <boost/timer.hpp>\r
55 #include <boost/algorithm/string.hpp>\r
56 \r
57 #include <functional>\r
58 \r
59 #include <tbb/spin_mutex.h>\r
60 \r
61 namespace caspar { namespace flash {\r
62                 \r
63 class bitmap\r
64 {\r
65 public:\r
66         bitmap(size_t width, size_t height)\r
67                 : bmp_data_(nullptr)\r
68                 , hdc_(CreateCompatibleDC(0), DeleteDC)\r
69         {       \r
70                 BITMAPINFO info;\r
71                 memset(&info, 0, sizeof(BITMAPINFO));\r
72                 info.bmiHeader.biBitCount = 32;\r
73                 info.bmiHeader.biCompression = BI_RGB;\r
74                 info.bmiHeader.biHeight = -height;\r
75                 info.bmiHeader.biPlanes = 1;\r
76                 info.bmiHeader.biSize = sizeof(BITMAPINFO);\r
77                 info.bmiHeader.biWidth = width;\r
78 \r
79                 bmp_.reset(CreateDIBSection(static_cast<HDC>(hdc_.get()), &info, DIB_RGB_COLORS, reinterpret_cast<void**>(&bmp_data_), 0, 0), DeleteObject);\r
80                 SelectObject(static_cast<HDC>(hdc_.get()), bmp_.get()); \r
81 \r
82                 if(!bmp_data_)\r
83                         BOOST_THROW_EXCEPTION(std::bad_alloc());\r
84         }\r
85 \r
86         operator HDC() {return static_cast<HDC>(hdc_.get());}\r
87 \r
88         BYTE* data() { return bmp_data_;}\r
89         const BYTE* data() const { return bmp_data_;}\r
90 \r
91 private:\r
92         BYTE* bmp_data_;        \r
93         std::shared_ptr<void> hdc_;\r
94         std::shared_ptr<void> bmp_;\r
95 };\r
96 \r
97 struct template_host\r
98 {\r
99         std::wstring  video_mode;\r
100         std::wstring  filename;\r
101         size_t            width;\r
102         size_t            height;\r
103 };\r
104 \r
105 template_host get_template_host(const core::video_format_desc& desc)\r
106 {\r
107         try\r
108         {\r
109                 std::vector<template_host> template_hosts;\r
110                 auto template_hosts_element = env::properties().get_child_optional(\r
111                                 L"configuration.template-hosts");\r
112 \r
113                 if (template_hosts_element)\r
114                         BOOST_FOREACH(auto& xml_mapping, *template_hosts_element)\r
115                         {\r
116                                 try\r
117                                 {\r
118                                         template_host template_host;\r
119                                         template_host.video_mode                = xml_mapping.second.get(L"video-mode", L"");\r
120                                         template_host.filename                  = xml_mapping.second.get(L"filename",   L"cg.fth");\r
121                                         template_host.width                             = xml_mapping.second.get(L"width",              desc.width);\r
122                                         template_host.height                    = xml_mapping.second.get(L"height",             desc.height);\r
123                                         template_hosts.push_back(template_host);\r
124                                 }\r
125                                 catch(...){}\r
126                         }\r
127 \r
128                 auto template_host_it = boost::find_if(template_hosts, [&](template_host template_host){return template_host.video_mode == desc.name;});\r
129                 if(template_host_it == template_hosts.end())\r
130                         template_host_it = boost::find_if(template_hosts, [&](template_host template_host){return template_host.video_mode == L"";});\r
131 \r
132                 if(template_host_it != template_hosts.end())\r
133                         return *template_host_it;\r
134         }\r
135         catch(...){}\r
136                 \r
137         template_host template_host;\r
138         template_host.filename = L"cg.fth";\r
139 \r
140         for(auto it = boost::filesystem2::wdirectory_iterator(env::template_folder()); it != boost::filesystem2::wdirectory_iterator(); ++it)\r
141         {\r
142                 if(boost::iequals(it->path().extension(), L"." + desc.name))\r
143                 {\r
144                         template_host.filename = it->filename();\r
145                         break;\r
146                 }\r
147         }\r
148 \r
149         template_host.width =  desc.square_width;\r
150         template_host.height = desc.square_height;\r
151         return template_host;\r
152 }\r
153 \r
154 boost::mutex& get_global_init_destruct_mutex()\r
155 {\r
156         static boost::mutex m;\r
157 \r
158         return m;\r
159 }\r
160 \r
161 class flash_renderer\r
162 {       \r
163         struct com_init\r
164         {\r
165                 HRESULT result_;\r
166 \r
167                 com_init()\r
168                         : result_(CoInitialize(nullptr))\r
169                 {\r
170                         if(FAILED(result_))\r
171                                 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Failed to initialize com-context for flash-player"));\r
172                 }\r
173 \r
174                 ~com_init()\r
175                 {\r
176                         if(SUCCEEDED(result_))\r
177                                 ::CoUninitialize();\r
178                 }\r
179         } com_init_;\r
180         \r
181         const safe_ptr<diagnostics::graph>                              graph_;\r
182         const size_t                                                                    width_;\r
183         const size_t                                                                    height_;\r
184         const std::wstring                                                              filename_;\r
185         const std::shared_ptr<core::frame_factory>              frame_factory_;\r
186         \r
187         CComObject<caspar::flash::FlashAxContainer>*    ax_;\r
188         safe_ptr<core::basic_frame>                                             head_;\r
189         bitmap                                                                                  bmp_;\r
190         \r
191         boost::timer                                                                    frame_timer_;\r
192         boost::timer                                                                    tick_timer_;\r
193 \r
194         high_prec_timer                                                                 timer_;\r
195 public:\r
196         flash_renderer(const safe_ptr<diagnostics::graph>& graph, const std::shared_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, int width, int height) \r
197                 : graph_(graph)\r
198                 , width_(width)\r
199                 , height_(height)\r
200                 , filename_(filename)\r
201                 , frame_factory_(frame_factory)\r
202                 , ax_(nullptr)\r
203                 , head_(core::basic_frame::late())\r
204                 , bmp_(width, height)\r
205         {               \r
206                 graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));\r
207                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));\r
208                 graph_->set_color("param", diagnostics::color(1.0f, 0.5f, 0.0f));\r
209 \r
210                 lock(get_global_init_destruct_mutex(), [this]\r
211                 {\r
212 \r
213                         CoInitialize(nullptr);\r
214 \r
215                         if(FAILED(CComObject<caspar::flash::FlashAxContainer>::CreateInstance(&ax_)))\r
216                                 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to create FlashAxContainer"));\r
217                 \r
218                         if(FAILED(ax_->CreateAxControl()))\r
219                                 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to Create FlashAxControl"));\r
220                 });\r
221 \r
222                 ax_->set_print([this]{return print();});\r
223                 \r
224                 CComPtr<IShockwaveFlash> spFlash;\r
225                 if(FAILED(ax_->QueryControl(&spFlash)))\r
226                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to Query FlashAxControl"));\r
227                                                                                                 \r
228                 if(FAILED(spFlash->put_Playing(true)) )\r
229                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to start playing Flash"));\r
230 \r
231                 if(FAILED(spFlash->put_Movie(CComBSTR(filename.c_str()))))\r
232                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to Load Template Host"));\r
233                                                                                 \r
234                 if(FAILED(spFlash->put_ScaleMode(2)))  //Exact fit. Scale without respect to the aspect ratio.\r
235                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to Set Scale Mode"));\r
236                                                 \r
237                 ax_->SetSize(width_, height_);          \r
238                 render_frame(0.0);\r
239         \r
240                 CASPAR_LOG(info) << print() << L" Initialized.";\r
241         }\r
242 \r
243         ~flash_renderer()\r
244         {               \r
245                 if(ax_)\r
246                 {\r
247                         lock(get_global_init_destruct_mutex(), [this]\r
248                         {\r
249                                 ax_->DestroyAxControl();\r
250                                 ax_->Release();\r
251                         });\r
252                 }\r
253                 graph_->set_value("tick-time", 0.0f);\r
254                 graph_->set_value("frame-time", 0.0f);\r
255                 CASPAR_LOG(info) << print() << L" Uninitialized.";\r
256         }\r
257         \r
258         std::wstring call(const std::wstring& param)\r
259         {               \r
260                 std::wstring result;\r
261 \r
262                 CASPAR_LOG(trace) << print() << " Call: " << param;\r
263 \r
264                 if(!ax_->FlashCall(param, result))\r
265                         CASPAR_LOG(warning) << print() << L" Flash call failed:" << param;//BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Flash function call failed.") << arg_name_info("param") << arg_value_info(narrow(param)));\r
266                 graph_->set_tag("param");\r
267 \r
268                 return result;\r
269         }\r
270         \r
271         safe_ptr<core::basic_frame> render_frame(double sync)\r
272         {\r
273                 float frame_time = 1.0f/ax_->GetFPS();\r
274 \r
275                 if (!ax_->IsReadyToRender())\r
276                         return head_;\r
277 \r
278                 if(ax_->IsEmpty())\r
279                         return core::basic_frame::empty();\r
280 \r
281                 if (sync > 0.00001)\r
282                         timer_.tick(frame_time*sync); // This will block the thread.\r
283                 \r
284                 graph_->set_value("tick-time", static_cast<float>(tick_timer_.elapsed()/frame_time)*0.5f);\r
285                 tick_timer_.restart();\r
286 \r
287                 frame_timer_.restart();\r
288 \r
289                 ax_->Tick();\r
290                 if(ax_->InvalidRect())\r
291                 {\r
292                         core::pixel_format_desc desc;\r
293                         desc.pix_fmt = core::pixel_format::bgra;\r
294                         desc.planes.push_back(core::pixel_format_desc::plane(width_, height_, 4));\r
295                         auto frame = frame_factory_->create_frame(this, desc);\r
296 \r
297                         fast_memclr(bmp_.data(), width_*height_*4);\r
298                         ax_->DrawControl(bmp_);\r
299 \r
300                         if(frame->image_data().size() == static_cast<int>(width_*height_*4))\r
301                         {\r
302                                 fast_memcpy(frame->image_data().begin(), bmp_.data(), width_*height_*4);\r
303                                 frame->commit();\r
304                                 head_ = frame;\r
305                         }\r
306                 }               \r
307                                         \r
308                 MSG msg;\r
309                 while(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) // DO NOT REMOVE THE MESSAGE DISPATCH LOOP. Without this some stuff doesn't work!  \r
310                 {\r
311                         if(msg.message == WM_TIMER && msg.wParam == 3 && msg.lParam == 0) // We tick this inside FlashAxContainer\r
312                                 continue;\r
313                         \r
314                         TranslateMessage(&msg);\r
315                         DispatchMessage(&msg);                  \r
316                 }\r
317                                                                                 \r
318                 graph_->set_value("frame-time", static_cast<float>(frame_timer_.elapsed()/frame_time)*0.5f);\r
319                 return head_;\r
320         }\r
321 \r
322         bool is_empty() const\r
323         {\r
324                 return ax_->IsEmpty();\r
325         }\r
326 \r
327         double fps() const\r
328         {\r
329                 return ax_->GetFPS();   \r
330         }\r
331         \r
332         std::wstring print()\r
333         {\r
334                 return L"flash-player[" + boost::filesystem::wpath(filename_).filename() \r
335                                   + L"|" + boost::lexical_cast<std::wstring>(width_)\r
336                                   + L"x" + boost::lexical_cast<std::wstring>(height_)\r
337                                   + L"]";               \r
338         }\r
339 };\r
340 \r
341 struct flash_producer : public core::frame_producer\r
342 {       \r
343         core::monitor::subject                                                                          monitor_subject_;\r
344         const std::wstring                                                                                      filename_;      \r
345         const safe_ptr<core::frame_factory>                                                     frame_factory_;\r
346         const int                                                                                                       width_;\r
347         const int                                                                                                       height_;\r
348         const int                                                                                                       buffer_size_;\r
349 \r
350         tbb::atomic<int>                                                                                        fps_;\r
351 \r
352         safe_ptr<diagnostics::graph>                                                            graph_;\r
353 \r
354         std::queue<safe_ptr<core::basic_frame>>                                         frame_buffer_;\r
355         tbb::concurrent_bounded_queue<safe_ptr<core::basic_frame>>      output_buffer_;\r
356         \r
357         safe_ptr<core::basic_frame>                                                                     last_frame_;\r
358                 \r
359         std::unique_ptr<flash_renderer>                                                         renderer_;\r
360         tbb::atomic<bool>                                                                                       has_renderer_;\r
361 \r
362         executor                                                                                                        executor_;      \r
363 public:\r
364         flash_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, size_t width, size_t height) \r
365                 : filename_(filename)           \r
366                 , frame_factory_(frame_factory)\r
367                 , last_frame_(core::basic_frame::empty())\r
368                 , width_(width > 0 ? width : frame_factory->get_video_format_desc().width)\r
369                 , height_(height > 0 ? height : frame_factory->get_video_format_desc().height)\r
370                 , buffer_size_(env::properties().get(L"configuration.flash.buffer-depth", frame_factory_->get_video_format_desc().fps > 30.0 ? 4 : 2))\r
371                 , executor_(L"flash_producer")\r
372         {       \r
373                 fps_ = 0;\r
374          \r
375                 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.9f));\r
376                 graph_->set_color("buffered", diagnostics::color(0.8f, 0.3f, 0.2f));\r
377                 graph_->set_text(print());\r
378                 diagnostics::register_graph(graph_);\r
379                 \r
380                 has_renderer_ = false;\r
381         }\r
382 \r
383         ~flash_producer()\r
384         {\r
385                 executor_.invoke([this]\r
386                 {\r
387                         renderer_.reset();\r
388                 }, high_priority);\r
389         }\r
390 \r
391         // frame_producer\r
392 \r
393         void log_buffered()\r
394         {\r
395                 double buffered = output_buffer_.size();\r
396                 auto ratio = buffered / buffer_size_;\r
397                 graph_->set_value("buffered", ratio);\r
398         }\r
399                 \r
400         virtual safe_ptr<core::basic_frame> receive(int) override\r
401         {                                       \r
402                 auto frame = core::basic_frame::late();\r
403 \r
404                 if(output_buffer_.try_pop(frame))\r
405                         last_frame_ = frame;\r
406                 else\r
407                         graph_->set_tag("late-frame");\r
408 \r
409                 fill_buffer();\r
410                 \r
411                 monitor_subject_ << core::monitor::message("/host/path")                % filename_\r
412                                              << core::monitor::message("/host/width")   % width_\r
413                                              << core::monitor::message("/host/height")  % height_\r
414                                              << core::monitor::message("/host/fps")             % fps_\r
415                                              << core::monitor::message("/buffer")               % output_buffer_.size() % buffer_size_;\r
416 \r
417                 return frame;\r
418         }\r
419 \r
420         virtual safe_ptr<core::basic_frame> last_frame() const override\r
421         {\r
422                 return last_frame_;\r
423         }               \r
424         \r
425         virtual boost::unique_future<std::wstring> call(const std::wstring& param) override\r
426         {       \r
427                 if (param == L"?")\r
428                         return wrap_as_future(std::wstring(has_renderer_ ? L"1" : L"0"));\r
429 \r
430                 return executor_.begin_invoke([this, param]() -> std::wstring\r
431                 {\r
432                         try\r
433                         {\r
434                                 bool initialize_renderer = !renderer_;\r
435 \r
436                                 if(initialize_renderer)\r
437                                 {\r
438                                         renderer_.reset(new flash_renderer(graph_, frame_factory_, filename_, width_, height_));\r
439 \r
440                                         has_renderer_ = true;\r
441                                 }\r
442 \r
443                                 std::wstring result = param == L"start_rendering"\r
444                                                 ? L"" : renderer_->call(param);\r
445 \r
446                                 if (initialize_renderer)\r
447                                 {\r
448                                         do_fill_buffer(true);\r
449                                 }\r
450 \r
451                                 return result;\r
452                                 //const auto& format_desc = frame_factory_->get_video_format_desc();\r
453                                 //if(abs(context_->fps() - format_desc.fps) > 0.01 && abs(context_->fps()/2.0 - format_desc.fps) > 0.01)\r
454                                 //      CASPAR_LOG(warning) << print() << " Invalid frame-rate: " << context_->fps() << L". Should be either " << format_desc.fps << L" or " << format_desc.fps*2.0 << L".";\r
455                         }\r
456                         catch(...)\r
457                         {\r
458                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
459                                 renderer_.reset(nullptr);\r
460                                 has_renderer_ = false;\r
461                         }\r
462 \r
463                         return L"";\r
464                 }, high_priority);\r
465         }\r
466                 \r
467         virtual std::wstring print() const override\r
468         { \r
469                 return L"flash[" + boost::filesystem::wpath(filename_).filename() + L"|" + boost::lexical_cast<std::wstring>(fps_) + L"]";              \r
470         }       \r
471 \r
472         virtual boost::property_tree::wptree info() const override\r
473         {\r
474                 boost::property_tree::wptree info;\r
475                 info.add(L"type", L"flash-producer");\r
476                 return info;\r
477         }\r
478 \r
479         // flash_producer\r
480 \r
481         void fill_buffer()\r
482         {\r
483                 executor_.begin_invoke([this]\r
484                 {\r
485                         do_fill_buffer(false);\r
486                 });\r
487         }\r
488 \r
489         void do_fill_buffer(bool initial_buffer_fill)\r
490         {\r
491                 int nothing_rendered = 0;\r
492                 const int MAX_NOTHING_RENDERED_RETRIES = 4;\r
493 \r
494                 auto to_render = buffer_size_ - output_buffer_.size();\r
495                 bool allow_faster_rendering = !initial_buffer_fill;\r
496                 int rendered = 0;\r
497 \r
498                 while (rendered < to_render)\r
499                 {\r
500                         bool was_rendered = next(allow_faster_rendering);\r
501                         log_buffered();\r
502 \r
503                         if (was_rendered)\r
504                         {\r
505                                 ++rendered;\r
506                         }\r
507                         else\r
508                         {\r
509                                 if (nothing_rendered++ < MAX_NOTHING_RENDERED_RETRIES)\r
510                                 {\r
511                                         // Flash player not ready with first frame, sleep to not busy-loop;\r
512                                         boost::this_thread::sleep(boost::posix_time::milliseconds(10));\r
513                                         boost::this_thread::yield();\r
514                                 }\r
515                                 else\r
516                                         return;\r
517                         }\r
518 \r
519                         executor_.yield();\r
520                 }\r
521         }\r
522         \r
523         bool next(bool allow_faster_rendering)\r
524         {       \r
525                 if(!renderer_)\r
526                         frame_buffer_.push(core::basic_frame::empty());\r
527 \r
528                 if(frame_buffer_.empty())\r
529                 {\r
530                         auto format_desc = frame_factory_->get_video_format_desc();\r
531                                         \r
532                         if(abs(renderer_->fps()/2.0 - format_desc.fps) < 2.0) // flash == 2 * format -> interlace\r
533                         {\r
534                                 auto frame1 = render_frame(allow_faster_rendering);\r
535 \r
536                                 if (frame1 != core::basic_frame::late())\r
537                                 {\r
538                                         auto frame2 = render_frame(allow_faster_rendering);\r
539                                         frame_buffer_.push(core::basic_frame::interlace(frame1, frame2, format_desc.field_mode));\r
540                                 }\r
541                         }\r
542                         else if(abs(renderer_->fps() - format_desc.fps/2.0) < 2.0) // format == 2 * flash -> duplicate\r
543                         {\r
544                                 auto frame = render_frame(allow_faster_rendering);\r
545 \r
546                                 if (frame != core::basic_frame::late())\r
547                                 {\r
548                                         frame_buffer_.push(frame);\r
549                                         frame_buffer_.push(frame);\r
550                                 }\r
551                         }\r
552                         else //if(abs(renderer_->fps() - format_desc_.fps) < 0.1) // format == flash -> simple\r
553                         {\r
554                                 auto frame = render_frame(allow_faster_rendering);\r
555 \r
556                                 if (frame != core::basic_frame::late())\r
557                                         frame_buffer_.push(frame);\r
558                         }\r
559                                                 \r
560                         fps_.fetch_and_store(static_cast<int>(renderer_->fps()*100.0));                         \r
561                         graph_->set_text(print());\r
562                         \r
563                         if(renderer_->is_empty())\r
564                         {\r
565                                 renderer_.reset();\r
566                                 has_renderer_ = false;\r
567                         }\r
568                 }\r
569 \r
570                 if (frame_buffer_.empty())\r
571                 {\r
572                         return false;\r
573                 }\r
574                 else\r
575                 {\r
576                         output_buffer_.push(std::move(frame_buffer_.front()));\r
577                         frame_buffer_.pop();\r
578                         return true;\r
579                 }\r
580         }\r
581 \r
582         safe_ptr<core::basic_frame> render_frame(bool allow_faster_rendering)\r
583         {\r
584                 double sync;\r
585 \r
586                 if (allow_faster_rendering)\r
587                 {\r
588                         double ratio = std::min(\r
589                                         1.0,\r
590                                         static_cast<double>(output_buffer_.size())\r
591                                                         / static_cast<double>(std::max(1, buffer_size_ - 1)));\r
592                         sync  = 2 * ratio - ratio * ratio;\r
593                 }\r
594                 else\r
595                 {\r
596                         sync = 1.0;\r
597                 }\r
598 \r
599                 return renderer_->render_frame(sync);\r
600         }\r
601 \r
602         core::monitor::subject& monitor_output()\r
603         {\r
604                 return monitor_subject_;\r
605         }\r
606 };\r
607 \r
608 safe_ptr<core::frame_producer> create_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::vector<std::wstring>& params)\r
609 {\r
610         auto template_host = get_template_host(frame_factory->get_video_format_desc());\r
611         \r
612         auto filename = env::template_folder() + L"\\" + template_host.filename;\r
613         \r
614         if(!boost::filesystem::exists(filename))\r
615                 BOOST_THROW_EXCEPTION(file_not_found() << boost::errinfo_file_name(narrow(filename)));  \r
616 \r
617         return create_producer_destroy_proxy(\r
618                    create_producer_print_proxy(\r
619                         make_safe<flash_producer>(frame_factory, filename, template_host.width, template_host.height)));\r
620 }\r
621 \r
622 safe_ptr<core::frame_producer> create_swf_producer(\r
623                 const safe_ptr<core::frame_factory>& frame_factory,\r
624                 const core::parameters& params) \r
625 {\r
626         auto filename = env::media_folder() + L"\\" + params.at_original(0) + L".swf";\r
627         \r
628         if(!boost::filesystem::exists(filename))\r
629                 return core::frame_producer::empty();\r
630 \r
631         swf_t::header_t header(filename);\r
632 \r
633         auto producer = make_safe<flash_producer>(\r
634                         frame_factory, filename, header.frame_width, header.frame_height);\r
635 \r
636         producer->call(L"start_rendering").get();\r
637 \r
638         return create_producer_destroy_proxy(create_producer_print_proxy(producer));\r
639 }\r
640 \r
641 std::wstring find_template(const std::wstring& template_name)\r
642 {\r
643         if(boost::filesystem::exists(template_name + L".ft")) \r
644                 return template_name + L".ft";\r
645         \r
646         if(boost::filesystem::exists(template_name + L".ct"))\r
647                 return template_name + L".ct";\r
648         \r
649         if(boost::filesystem::exists(template_name + L".swf"))\r
650                 return template_name + L".swf";\r
651 \r
652         return L"";\r
653 }\r
654 \r
655 }}