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
24 #include "producer/html_producer.h"
25 #include "producer/html_cg_proxy.h"
27 #include <common/executor.h>
28 #include <common/future.h>
30 #include <core/producer/cg_proxy.h>
32 #include <boost/thread.hpp>
33 #include <boost/asio.hpp>
34 #include <boost/date_time/posix_time/posix_time.hpp>
35 #include <boost/foreach.hpp>
36 #include <boost/timer.hpp>
37 #include <boost/range/algorithm/remove_if.hpp>
38 #include <boost/thread/future.hpp>
39 #include <boost/lexical_cast.hpp>
40 #include <boost/log/trivial.hpp>
43 #pragma warning(disable: 4458)
47 #pragma comment(lib, "libcef.lib")
48 #pragma comment(lib, "libcef_dll_wrapper.lib")
50 namespace caspar { namespace html {
52 std::unique_ptr<executor> g_cef_executor;
55 const CefRefPtr<CefBrowser>& browser,
56 boost::log::trivial::severity_level level,
57 const std::string& message)
61 auto msg = CefProcessMessage::Create(LOG_MESSAGE_NAME);
62 msg->GetArgumentList()->SetInt(0, level);
63 msg->GetArgumentList()->SetString(1, message);
64 browser->SendProcessMessage(PID_BROWSER, msg);
68 class animation_handler : public CefV8Handler
71 std::vector<CefRefPtr<CefV8Value>> callbacks_;
72 boost::timer since_start_timer_;
74 CefRefPtr<CefBrowser> browser;
75 std::function<CefRefPtr<CefV8Context>()> get_context;
78 const CefString& name,
79 CefRefPtr<CefV8Value> object,
80 const CefV8ValueList& arguments,
81 CefRefPtr<CefV8Value>& retval,
82 CefString& exception) override
84 if (!CefCurrentlyOn(TID_RENDERER))
87 if (arguments.size() < 1 || !arguments.at(0)->IsFunction())
92 callbacks_.push_back(arguments.at(0));
95 browser->SendProcessMessage(PID_BROWSER, CefProcessMessage::Create(
96 ANIMATION_FRAME_REQUESTED_MESSAGE_NAME));
106 auto context = get_context();
111 if (!CefCurrentlyOn(TID_RENDERER))
114 std::vector<CefRefPtr<CefV8Value>> callbacks;
115 callbacks_.swap(callbacks);
117 CefV8ValueList callback_args;
120 callback_args.push_back(CefV8Value::CreateDouble(
121 since_start_timer_.elapsed() * 1000.0));
123 for (auto callback : callbacks)
125 callback->ExecuteFunctionWithContext(
126 context, callback, callback_args);
130 IMPLEMENT_REFCOUNTING(animation_handler);
133 class remove_handler : public CefV8Handler
135 CefRefPtr<CefBrowser> browser_;
137 remove_handler(CefRefPtr<CefBrowser> browser)
143 const CefString& name,
144 CefRefPtr<CefV8Value> object,
145 const CefV8ValueList& arguments,
146 CefRefPtr<CefV8Value>& retval,
147 CefString& exception) override
149 if (!CefCurrentlyOn(TID_RENDERER))
152 browser_->SendProcessMessage(
154 CefProcessMessage::Create(REMOVE_MESSAGE_NAME));
159 IMPLEMENT_REFCOUNTING(remove_handler);
162 class renderer_application : public CefApp, CefRenderProcessHandler
164 std::vector<std::pair<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>>> contexts_per_handlers_;
165 //std::map<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>> contexts_per_handlers_;
167 CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override
172 CefRefPtr<CefV8Context> get_context(
173 const CefRefPtr<animation_handler>& handler)
175 for (auto& ctx : contexts_per_handlers_)
177 if (ctx.first == handler)
184 void OnContextCreated(
185 CefRefPtr<CefBrowser> browser,
186 CefRefPtr<CefFrame> frame,
187 CefRefPtr<CefV8Context> context) override
189 caspar_log(browser, boost::log::trivial::trace,
191 + boost::lexical_cast<std::string>(frame->GetIdentifier())
194 CefRefPtr<animation_handler> handler = new animation_handler;
195 contexts_per_handlers_.push_back(std::make_pair(handler, context));
196 auto handler_ptr = handler.get();
198 handler->browser = browser;
199 handler->get_context = [this, handler_ptr]
201 return get_context(handler_ptr);
204 auto window = context->GetGlobal();
206 auto function = CefV8Value::CreateFunction(
207 "requestAnimationFrame",
210 "requestAnimationFrame",
212 V8_PROPERTY_ATTRIBUTE_NONE);
214 function = CefV8Value::CreateFunction(
216 new remove_handler(browser));
220 V8_PROPERTY_ATTRIBUTE_NONE);
223 void OnContextReleased(
224 CefRefPtr<CefBrowser> browser,
225 CefRefPtr<CefFrame> frame,
226 CefRefPtr<CefV8Context> context)
228 auto removed = boost::remove_if(
229 contexts_per_handlers_, [&](const std::pair<
230 CefRefPtr<animation_handler>,
231 CefRefPtr<CefV8Context>>& c)
233 return c.second->IsSame(context);
236 if (removed != contexts_per_handlers_.end())
237 caspar_log(browser, boost::log::trivial::trace,
239 + boost::lexical_cast<std::string>(frame->GetIdentifier())
242 caspar_log(browser, boost::log::trivial::warning,
244 + boost::lexical_cast<std::string>(frame->GetIdentifier())
245 + " released, but not found");
248 void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) override
250 contexts_per_handlers_.clear();
253 bool OnProcessMessageReceived(
254 CefRefPtr<CefBrowser> browser,
255 CefProcessId source_process,
256 CefRefPtr<CefProcessMessage> message) override
258 if (message->GetName().ToString() == TICK_MESSAGE_NAME)
260 for (auto& handler : contexts_per_handlers_)
262 handler.first->tick();
273 IMPLEMENT_REFCOUNTING(renderer_application);
276 bool intercept_command_line(int argc, char** argv)
279 CefMainArgs main_args;
281 CefMainArgs main_args(argc, argv);
284 if (CefExecuteProcess(main_args, CefRefPtr<CefApp>(new renderer_application), nullptr) >= 0)
290 void init(core::module_dependencies dependencies)
292 dependencies.producer_registry->register_producer_factory(L"HTML Producer", html::create_producer, html::describe_producer);
294 CefMainArgs main_args;
295 g_cef_executor.reset(new executor(L"cef"));
296 g_cef_executor->invoke([&]
298 CefSettings settings;
299 settings.no_sandbox = true;
300 //settings.windowless_rendering_enabled = true;
301 CefInitialize(main_args, settings, nullptr, nullptr);
303 g_cef_executor->begin_invoke([&]
307 dependencies.cg_registry->register_cg_producer(
310 [](const std::wstring& filename)
314 [](const spl::shared_ptr<core::frame_producer>& producer)
316 return spl::make_shared<html_cg_proxy>(producer);
318 [](const core::frame_producer_dependencies& dependencies, const std::wstring& filename)
320 return html::create_producer(dependencies, { filename });
330 CefQuitMessageLoop();
332 g_cef_executor->begin_invoke([&]
336 g_cef_executor.reset();
339 class cef_task : public CefTask
342 std::promise<void> promise_;
343 std::function<void ()> function_;
345 cef_task(const std::function<void ()>& function)
346 : function_(function)
350 void Execute() override
352 CASPAR_LOG(trace) << "[cef_task] executing task";
357 promise_.set_value();
358 CASPAR_LOG(trace) << "[cef_task] task succeeded";
362 promise_.set_exception(std::current_exception());
363 CASPAR_LOG(warning) << "[cef_task] task failed";
367 std::future<void> future()
369 return promise_.get_future();
372 IMPLEMENT_REFCOUNTING(cef_task);
375 void invoke(const std::function<void()>& func)
377 begin_invoke(func).get();
380 std::future<void> begin_invoke(const std::function<void()>& func)
382 CefRefPtr<cef_task> task = new cef_task(func);
384 if (CefCurrentlyOn(TID_UI))
388 return task->future();
391 if (CefPostTask(TID_UI, task.get()))
392 return task->future();
394 CASPAR_THROW_EXCEPTION(caspar_exception()
395 << msg_info("[cef_executor] Could not post task"));