2 * Copyright 2013 Sveriges Television AB http://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: Robert Nagy, ronag89@gmail.com
22 #include "html_producer.h"
24 #include <core/video_format.h>
26 #include <core/monitor/monitor.h>
27 #include <core/parameters/parameters.h>
28 #include <core/producer/frame/basic_frame.h>
29 #include <core/producer/frame/frame_factory.h>
30 #include <core/producer/frame_producer.h>
31 #include <core/mixer/write_frame.h>
33 #include <common/utility/assert.h>
34 #include <common/env.h>
35 #include <common/concurrency/executor.h>
36 #include <common/concurrency/lock.h>
37 #include <common/diagnostics/graph.h>
38 #include <common/utility/timer.h>
39 #include <common/memory/memcpy.h>
41 #include <boost/algorithm/string/predicate.hpp>
42 #include <boost/algorithm/string/trim.hpp>
43 #include <boost/filesystem.hpp>
44 #include <boost/format.hpp>
46 #include <tbb/atomic.h>
47 #include <tbb/parallel_for.h>
51 #include <cef_client.h>
52 #include <cef_render_handler.h>
56 #pragma comment (lib, "libcef.lib")
57 #pragma comment (lib, "libcef_dll_wrapper.lib")
64 , public CefRenderHandler
65 , public CefLifeSpanHandler
68 safe_ptr<core::frame_factory> frame_factory_;
69 tbb::atomic<bool> invalidated_;
70 std::vector<unsigned char> frame_;
71 mutable boost::mutex frame_mutex_;
73 safe_ptr<core::basic_frame> last_frame_;
74 mutable boost::mutex last_frame_mutex_;
76 CefRefPtr<CefBrowser> browser_;
82 html_client(safe_ptr<core::frame_factory> frame_factory)
83 : frame_factory_(frame_factory)
84 , frame_(frame_factory->get_video_format_desc().width * frame_factory->get_video_format_desc().height * 4, 0)
85 , last_frame_(core::basic_frame::empty())
86 , executor_(L"html_producer")
89 executor_.begin_invoke([&]{ update(); });
92 safe_ptr<core::basic_frame> receive(int)
94 auto frame = last_frame();
95 executor_.begin_invoke([&]{ update(); });
99 safe_ptr<core::basic_frame> last_frame() const
101 return lock(last_frame_mutex_, [&]
107 void execute_javascript(const std::wstring& javascript)
109 html::begin_invoke([=]
111 if (browser_ != nullptr)
112 browser_->GetMainFrame()->ExecuteJavaScript(narrow(javascript).c_str(), browser_->GetMainFrame()->GetURL(), 0);
120 if (browser_ != nullptr)
122 browser_->GetHost()->CloseBrowser(true);
129 bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
131 CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
133 rect = CefRect(0, 0, frame_factory_->get_video_format_desc().width, frame_factory_->get_video_format_desc().height);
137 void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height)
139 CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
141 lock(frame_mutex_, [&]
144 fast_memcpy(frame_.data(), buffer, width * height * 4);
148 void OnAfterCreated(CefRefPtr<CefBrowser> browser) override
150 CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
155 void OnBeforeClose(CefRefPtr<CefBrowser> browser) override
157 CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
162 bool DoClose(CefRefPtr<CefBrowser> browser) override
164 CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
169 CefRefPtr<CefRenderHandler> GetRenderHandler() override
174 CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override
179 void invoke_on_enter_frame()
181 //html::invoke([this]
183 // static const std::wstring javascript = L"onEnterFrame()";
184 // execute_javascript(javascript);
188 safe_ptr<core::basic_frame> draw(safe_ptr<core::write_frame> frame, core::field_mode::type field_mode)
190 const auto& pixel_desc = frame->get_pixel_format_desc();
192 CASPAR_ASSERT(pixel_desc.pix_fmt == core::pixel_format::bgra);
194 const auto& height = pixel_desc.planes[0].height;
195 const auto& linesize = pixel_desc.planes[0].linesize;
197 lock(frame_mutex_, [&]
199 tbb::parallel_for<int>(
200 field_mode == core::field_mode::upper ? 0 : 1,
202 field_mode == core::field_mode::progressive ? 1 : 2,
206 frame->image_data().begin() + y * linesize,
207 frame_.data() + y * linesize,
217 invoke_on_enter_frame();
219 if (invalidated_.fetch_and_store(false))
221 high_prec_timer timer;
224 core::pixel_format_desc pixel_desc;
225 pixel_desc.pix_fmt = core::pixel_format::bgra;
226 pixel_desc.planes.push_back(
227 core::pixel_format_desc::plane(
228 frame_factory_->get_video_format_desc().width,
229 frame_factory_->get_video_format_desc().height,
232 auto frame = frame_factory_->create_frame(this, pixel_desc);
234 const auto& format_desc = frame_factory_->get_video_format_desc();
236 if (format_desc.field_mode != core::field_mode::progressive)
238 draw(frame, format_desc.field_mode);
241 timer.tick(1.0 / (format_desc.fps * format_desc.field_count));
243 draw(frame, static_cast<core::field_mode::type>(format_desc.field_mode ^ core::field_mode::progressive));
247 draw(frame, format_desc.field_mode);
252 lock(last_frame_mutex_, [&]
259 IMPLEMENT_REFCOUNTING(html_client);
263 : public core::frame_producer
265 core::monitor::subject monitor_subject_;
266 const std::wstring url_;
267 safe_ptr<diagnostics::graph> graph_;
269 CefRefPtr<html_client> client_;
273 const safe_ptr<core::frame_factory>& frame_factory,
274 const std::wstring& url)
277 graph_->set_text(print());
278 diagnostics::register_graph(graph_);
282 client_ = new html_client(frame_factory);
284 CefWindowInfo window_info;
286 window_info.SetTransparentPainting(TRUE);
287 window_info.SetAsOffScreen(nullptr);
288 //window_info.SetAsWindowless(nullptr, true);
290 CefBrowserSettings browser_settings;
291 CefBrowserHost::CreateBrowser(window_info, client_.get(), url, browser_settings, nullptr);
302 safe_ptr<core::basic_frame> receive(int) override
304 return client_->receive(0);
307 safe_ptr<core::basic_frame> last_frame() const override
309 return client_->last_frame();
312 boost::unique_future<std::wstring> call(const std::wstring& param) override
314 static const boost::wregex play_exp(L"PLAY\\s*(\\d+)?", boost::regex::icase);
315 static const boost::wregex stop_exp(L"STOP\\s*(\\d+)?", boost::regex::icase);
316 static const boost::wregex next_exp(L"NEXT\\s*(\\d+)?", boost::regex::icase);
317 static const boost::wregex remove_exp(L"REMOVE\\s*(\\d+)?", boost::regex::icase);
318 static const boost::wregex update_exp(L"UPDATE\\s+(\\d+)?\"?(?<VALUE>.*)\"?", boost::regex::icase);
319 static const boost::wregex invoke_exp(L"INVOKE\\s+(\\d+)?\"?(?<VALUE>.*)\"?", boost::regex::icase);
323 auto javascript = param;
327 if (boost::regex_match(param, what, play_exp))
329 javascript = (boost::wformat(L"play()")).str();
331 else if (boost::regex_match(param, what, stop_exp))
333 javascript = (boost::wformat(L"stop()")).str();
335 else if (boost::regex_match(param, what, next_exp))
337 javascript = (boost::wformat(L"next()")).str();
339 else if (boost::regex_match(param, what, remove_exp))
341 javascript = (boost::wformat(L"remove()")).str();
343 else if (boost::regex_match(param, what, update_exp))
345 javascript = (boost::wformat(L"update(\"%1%\")") % boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \""))).str();
347 else if (boost::regex_match(param, what, invoke_exp))
349 javascript = (boost::wformat(L"invoke(\"%1%\")") % boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \""))).str();
352 client_->execute_javascript(javascript);
355 boost::packaged_task<std::wstring> task([=]() -> std::wstring
357 html::invoke(command);
363 return task.get_future();
366 std::wstring print() const override
368 return L"html[" + url_ + L"]";
371 boost::property_tree::wptree info() const override
373 boost::property_tree::wptree info;
374 info.add(L"type", L"html-producer");
378 core::monitor::subject& monitor_output()
380 return monitor_subject_;
384 safe_ptr<core::frame_producer> create_producer(
385 const safe_ptr<core::frame_factory>& frame_factory,
386 const core::parameters& params)
388 const auto filename = env::template_folder() + L"\\" + params.at_original(0) + L".html";
390 if (!boost::filesystem::exists(filename) && params.at(0) != L"[HTML]")
391 return core::frame_producer::empty();
393 const auto url = boost::filesystem::exists(filename)
395 : params.at_original(1);
397 if(!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA"))
398 return core::frame_producer::empty();
400 return core::create_producer_destroy_proxy(
401 core::create_producer_print_proxy(
402 make_safe<html_producer>(