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>
44 #pragma comment(lib, "libcef.lib")
45 #pragma comment(lib, "libcef_dll_wrapper.lib")
47 namespace caspar { namespace html {
49 std::unique_ptr<executor> g_cef_executor;
52 const CefRefPtr<CefBrowser>& browser,
53 boost::log::trivial::severity_level level,
54 const std::string& message)
58 auto msg = CefProcessMessage::Create(LOG_MESSAGE_NAME);
59 msg->GetArgumentList()->SetInt(0, level);
60 msg->GetArgumentList()->SetString(1, message);
61 browser->SendProcessMessage(PID_BROWSER, msg);
65 class animation_handler : public CefV8Handler
68 std::vector<CefRefPtr<CefV8Value>> callbacks_;
69 boost::timer since_start_timer_;
71 CefRefPtr<CefBrowser> browser;
72 std::function<CefRefPtr<CefV8Context>()> get_context;
75 const CefString& name,
76 CefRefPtr<CefV8Value> object,
77 const CefV8ValueList& arguments,
78 CefRefPtr<CefV8Value>& retval,
79 CefString& exception) override
81 if (!CefCurrentlyOn(TID_RENDERER))
84 if (arguments.size() < 1 || !arguments.at(0)->IsFunction())
89 callbacks_.push_back(arguments.at(0));
92 browser->SendProcessMessage(PID_BROWSER, CefProcessMessage::Create(
93 ANIMATION_FRAME_REQUESTED_MESSAGE_NAME));
103 auto context = get_context();
108 if (!CefCurrentlyOn(TID_RENDERER))
111 std::vector<CefRefPtr<CefV8Value>> callbacks;
112 callbacks_.swap(callbacks);
114 CefV8ValueList callback_args;
117 callback_args.push_back(CefV8Value::CreateDouble(
118 since_start_timer_.elapsed() * 1000.0));
120 for (auto callback : callbacks)
122 callback->ExecuteFunctionWithContext(
123 context, callback, callback_args);
127 IMPLEMENT_REFCOUNTING(animation_handler);
130 class remove_handler : public CefV8Handler
132 CefRefPtr<CefBrowser> browser_;
134 remove_handler(CefRefPtr<CefBrowser> browser)
140 const CefString& name,
141 CefRefPtr<CefV8Value> object,
142 const CefV8ValueList& arguments,
143 CefRefPtr<CefV8Value>& retval,
144 CefString& exception) override
146 if (!CefCurrentlyOn(TID_RENDERER))
149 browser_->SendProcessMessage(
151 CefProcessMessage::Create(REMOVE_MESSAGE_NAME));
156 IMPLEMENT_REFCOUNTING(remove_handler);
159 class renderer_application : public CefApp, CefRenderProcessHandler
161 std::vector<std::pair<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>>> contexts_per_handlers_;
162 //std::map<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>> contexts_per_handlers_;
164 CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override
169 CefRefPtr<CefV8Context> get_context(
170 const CefRefPtr<animation_handler>& handler)
172 for (auto& ctx : contexts_per_handlers_)
174 if (ctx.first == handler)
181 void OnContextCreated(
182 CefRefPtr<CefBrowser> browser,
183 CefRefPtr<CefFrame> frame,
184 CefRefPtr<CefV8Context> context) override
186 caspar_log(browser, boost::log::trivial::trace,
188 + boost::lexical_cast<std::string>(frame->GetIdentifier())
191 CefRefPtr<animation_handler> handler = new animation_handler;
192 contexts_per_handlers_.push_back(std::make_pair(handler, context));
193 auto handler_ptr = handler.get();
195 handler->browser = browser;
196 handler->get_context = [this, handler_ptr]
198 return get_context(handler_ptr);
201 auto window = context->GetGlobal();
203 auto function = CefV8Value::CreateFunction(
204 "requestAnimationFrame",
207 "requestAnimationFrame",
209 V8_PROPERTY_ATTRIBUTE_NONE);
211 function = CefV8Value::CreateFunction(
213 new remove_handler(browser));
217 V8_PROPERTY_ATTRIBUTE_NONE);
220 void OnContextReleased(
221 CefRefPtr<CefBrowser> browser,
222 CefRefPtr<CefFrame> frame,
223 CefRefPtr<CefV8Context> context)
225 auto removed = boost::remove_if(
226 contexts_per_handlers_, [&](const std::pair<
227 CefRefPtr<animation_handler>,
228 CefRefPtr<CefV8Context>>& c)
230 return c.second->IsSame(context);
233 if (removed != contexts_per_handlers_.end())
234 caspar_log(browser, boost::log::trivial::trace,
236 + boost::lexical_cast<std::string>(frame->GetIdentifier())
239 caspar_log(browser, boost::log::trivial::warning,
241 + boost::lexical_cast<std::string>(frame->GetIdentifier())
242 + " released, but not found");
245 void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) override
247 contexts_per_handlers_.clear();
250 bool OnProcessMessageReceived(
251 CefRefPtr<CefBrowser> browser,
252 CefProcessId source_process,
253 CefRefPtr<CefProcessMessage> message) override
255 if (message->GetName().ToString() == TICK_MESSAGE_NAME)
257 for (auto& handler : contexts_per_handlers_)
259 handler.first->tick();
270 IMPLEMENT_REFCOUNTING(renderer_application);
273 bool intercept_command_line(int argc, char** argv)
276 CefMainArgs main_args;
278 CefMainArgs main_args(argc, argv);
281 if (CefExecuteProcess(main_args, CefRefPtr<CefApp>(new renderer_application), nullptr) >= 0)
287 void init(core::module_dependencies dependencies)
289 core::register_producer_factory(html::create_producer);
291 CefMainArgs main_args;
292 g_cef_executor.reset(new executor(L"cef"));
293 g_cef_executor->invoke([&]
295 CefSettings settings;
296 settings.no_sandbox = true;
297 //settings.windowless_rendering_enabled = true;
298 CefInitialize(main_args, settings, nullptr, nullptr);
300 g_cef_executor->begin_invoke([&]
304 dependencies.cg_registry->register_cg_producer(
307 [](const std::wstring& filename)
311 [](const spl::shared_ptr<core::frame_producer>& producer)
313 return spl::make_shared<html_cg_proxy>(producer);
315 [](const spl::shared_ptr<core::frame_factory>& ff, const core::video_format_desc& f, const std::wstring& filename)
317 return html::create_producer(ff, f, { filename });
327 CefQuitMessageLoop();
329 g_cef_executor->begin_invoke([&]
335 class cef_task : public CefTask
338 std::promise<void> promise_;
339 std::function<void ()> function_;
341 cef_task(const std::function<void ()>& function)
342 : function_(function)
346 void Execute() override
348 CASPAR_LOG(trace) << "[cef_task] executing task";
353 promise_.set_value();
354 CASPAR_LOG(trace) << "[cef_task] task succeeded";
358 promise_.set_exception(std::current_exception());
359 CASPAR_LOG(warning) << "[cef_task] task failed";
363 std::future<void> future()
365 return promise_.get_future();
368 IMPLEMENT_REFCOUNTING(cef_task);
371 void invoke(const std::function<void()>& func)
373 begin_invoke(func).get();
376 std::future<void> begin_invoke(const std::function<void()>& func)
378 CefRefPtr<cef_task> task = new cef_task(func);
380 if (CefCurrentlyOn(TID_UI))
384 return task->future();
387 if (CefPostTask(TID_UI, task.get()))
388 return task->future();
390 BOOST_THROW_EXCEPTION(caspar_exception()
391 << msg_info("[cef_executor] Could not post task"));