#include <core/interaction/interaction_event.h>
#include <core/frame/frame.h>
#include <core/frame/pixel_format.h>
+#include <core/frame/audio_channel_layout.h>
#include <core/frame/geometry.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
#include <common/assert.h>
#include <common/env.h>
#include <common/prec_timer.h>
#include <common/linq.h>
#include <common/os/filesystem.h>
+#include <common/memcpy.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <tbb/atomic.h>
#include <tbb/concurrent_queue.h>
+#pragma warning(push)
+#pragma warning(disable: 4458)
#include <cef_task.h>
#include <cef_app.h>
#include <cef_client.h>
#include <cef_render_handler.h>
+#pragma warning(pop)
#include <asmlib.h>
#pragma comment (lib, "libcef.lib")
#pragma comment (lib, "libcef_dll_wrapper.lib")
-namespace caspar {
- namespace html {
+namespace caspar { namespace html {
- class html_client
- : public CefClient
- , public CefRenderHandler
- , public CefLifeSpanHandler
- , public CefLoadHandler
+class html_client
+ : public CefClient
+ , public CefRenderHandler
+ , public CefLifeSpanHandler
+ , public CefLoadHandler
+{
+ std::wstring url_;
+ spl::shared_ptr<diagnostics::graph> graph_;
+ boost::timer tick_timer_;
+ boost::timer frame_timer_;
+ boost::timer paint_timer_;
+
+ spl::shared_ptr<core::frame_factory> frame_factory_;
+ core::video_format_desc format_desc_;
+ tbb::concurrent_queue<std::wstring> javascript_before_load_;
+ tbb::atomic<bool> loaded_;
+ tbb::atomic<bool> removed_;
+ std::queue<core::draw_frame> frames_;
+ mutable boost::mutex frames_mutex_;
+
+ core::draw_frame last_frame_;
+ core::draw_frame last_progressive_frame_;
+ mutable boost::mutex last_frame_mutex_;
+
+ CefRefPtr<CefBrowser> browser_;
+
+ executor executor_;
+
+public:
+
+ html_client(
+ spl::shared_ptr<core::frame_factory> frame_factory,
+ const core::video_format_desc& format_desc,
+ const std::wstring& url)
+ : url_(url)
+ , frame_factory_(std::move(frame_factory))
+ , format_desc_(format_desc)
+ , last_frame_(core::draw_frame::empty())
+ , last_progressive_frame_(core::draw_frame::empty())
+ , executor_(L"html_producer")
+ {
+ graph_->set_color("browser-tick-time", diagnostics::color(0.1f, 1.0f, 0.1f));
+ graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
+ graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
+ graph_->set_text(print());
+ diagnostics::register_graph(graph_);
+
+ loaded_ = false;
+ removed_ = false;
+ executor_.begin_invoke([&]{ update(); });
+ }
+
+ core::draw_frame receive()
+ {
+ auto frame = last_frame();
+ executor_.begin_invoke([&]{ update(); });
+ return frame;
+ }
+
+ core::draw_frame last_frame() const
+ {
+ return lock(last_frame_mutex_, [&]
{
- std::wstring url_;
- spl::shared_ptr<diagnostics::graph> graph_;
- boost::timer tick_timer_;
- boost::timer frame_timer_;
- boost::timer paint_timer_;
-
- spl::shared_ptr<core::frame_factory> frame_factory_;
- core::video_format_desc format_desc_;
- tbb::concurrent_queue<std::wstring> javascript_before_load_;
- tbb::atomic<bool> loaded_;
- tbb::atomic<bool> removed_;
- tbb::atomic<bool> animation_frame_requested_;
- std::queue<core::draw_frame> frames_;
- mutable boost::mutex frames_mutex_;
-
- core::draw_frame last_frame_;
- core::draw_frame last_progressive_frame_;
- mutable boost::mutex last_frame_mutex_;
-
- CefRefPtr<CefBrowser> browser_;
-
- executor executor_;
-
- public:
-
- html_client(
- spl::shared_ptr<core::frame_factory> frame_factory,
- const core::video_format_desc& format_desc,
- const std::wstring& url)
- : url_(url)
- , frame_factory_(std::move(frame_factory))
- , format_desc_(format_desc)
- , last_frame_(core::draw_frame::empty())
- , last_progressive_frame_(core::draw_frame::empty())
- , executor_(L"html_producer")
- {
- graph_->set_color("browser-tick-time", diagnostics::color(0.1f, 1.0f, 0.1f));
- graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
- graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.9f));
- graph_->set_text(print());
- diagnostics::register_graph(graph_);
-
- loaded_ = false;
- removed_ = false;
- animation_frame_requested_ = false;
- executor_.begin_invoke([&]{ update(); });
- }
+ return last_frame_;
+ });
+ }
- core::draw_frame receive()
- {
- auto frame = last_frame();
- executor_.begin_invoke([&]{ update(); });
- return frame;
- }
+ void execute_javascript(const std::wstring& javascript)
+ {
+ if (!loaded_)
+ {
+ javascript_before_load_.push(javascript);
+ }
+ else
+ {
+ execute_queued_javascript();
+ do_execute_javascript(javascript);
+ }
+ }
- core::draw_frame last_frame() const
- {
- return lock(last_frame_mutex_, [&]
- {
- return last_frame_;
- });
- }
+ CefRefPtr<CefBrowserHost> get_browser_host()
+ {
+ return browser_->GetHost();
+ }
- void execute_javascript(const std::wstring& javascript)
+ void close()
+ {
+ html::invoke([=]
+ {
+ if (browser_ != nullptr)
{
- if (!loaded_)
- {
- javascript_before_load_.push(javascript);
- }
- else
- {
- execute_queued_javascript();
- do_execute_javascript(javascript);
- }
+ browser_->GetHost()->CloseBrowser(true);
}
+ });
+ }
- CefRefPtr<CefBrowserHost> get_browser_host()
- {
- return browser_->GetHost();
- }
+ void remove()
+ {
+ close();
+ removed_ = true;
+ }
- void close()
- {
- if (!animation_frame_requested_)
- CASPAR_LOG(warning) << print()
- << " window.requestAnimationFrame() never called. "
- << "Animations might have been laggy";
+ bool is_removed() const
+ {
+ return removed_;
+ }
- html::invoke([=]
- {
- if (browser_ != nullptr)
- {
- browser_->GetHost()->CloseBrowser(true);
- }
- });
- }
+private:
- void remove()
- {
- close();
- removed_ = true;
- }
+ bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
+ {
+ CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
- bool is_removed() const
- {
- return removed_;
- }
+ rect = CefRect(0, 0, format_desc_.square_width, format_desc_.square_height);
+ return true;
+ }
- private:
+ void OnPaint(
+ CefRefPtr<CefBrowser> browser,
+ PaintElementType type,
+ const RectList &dirtyRects,
+ const void *buffer,
+ int width,
+ int height)
+ {
+ graph_->set_value("browser-tick-time", paint_timer_.elapsed()
+ * format_desc_.fps
+ * format_desc_.field_count
+ * 0.5);
+ paint_timer_.restart();
+ CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+
+ core::pixel_format_desc pixel_desc;
+ pixel_desc.format = core::pixel_format::bgra;
+ pixel_desc.planes.push_back(
+ core::pixel_format_desc::plane(width, height, 4));
+ auto frame = frame_factory_->create_frame(this, pixel_desc, core::audio_channel_layout::invalid());
+ fast_memcpy(frame.image_data().begin(), buffer, width * height * 4);
+
+ lock(frames_mutex_, [&]
+ {
+ frames_.push(core::draw_frame(std::move(frame)));
- bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
- {
- CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+ size_t max_in_queue = format_desc_.field_count + 1;
- rect = CefRect(0, 0, format_desc_.square_width, format_desc_.square_height);
- return true;
+ while (frames_.size() > max_in_queue)
+ {
+ frames_.pop();
+ graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
}
+ });
+ }
- void OnPaint(
- CefRefPtr<CefBrowser> browser,
- PaintElementType type,
- const RectList &dirtyRects,
- const void *buffer,
- int width,
- int height)
- {
- graph_->set_value("browser-tick-time", paint_timer_.elapsed()
- * format_desc_.fps
- * format_desc_.field_count
- * 0.5);
- paint_timer_.restart();
- CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
-
- boost::timer copy_timer;
- core::pixel_format_desc pixel_desc;
- pixel_desc.format = core::pixel_format::bgra;
- pixel_desc.planes.push_back(
- core::pixel_format_desc::plane(width, height, 4));
- auto frame = frame_factory_->create_frame(this, pixel_desc);
- A_memcpy(frame.image_data().begin(), buffer, width * height * 4);
-
- lock(frames_mutex_, [&]
- {
- frames_.push(core::draw_frame(std::move(frame)));
+ void OnAfterCreated(CefRefPtr<CefBrowser> browser) override
+ {
+ CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
- size_t max_in_queue = format_desc_.field_count;
+ browser_ = browser;
+ }
- while (frames_.size() > max_in_queue)
- {
- frames_.pop();
- graph_->set_tag("dropped-frame");
- }
- });
- graph_->set_value("copy-time", copy_timer.elapsed()
- * format_desc_.fps
- * format_desc_.field_count
- * 0.5);
- }
+ void OnBeforeClose(CefRefPtr<CefBrowser> browser) override
+ {
+ CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
- void OnAfterCreated(CefRefPtr<CefBrowser> browser) override
- {
- CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+ browser_ = nullptr;
+ }
- browser_ = browser;
- }
+ bool DoClose(CefRefPtr<CefBrowser> browser) override
+ {
+ CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
- void OnBeforeClose(CefRefPtr<CefBrowser> browser) override
- {
- CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+ return false;
+ }
- browser_ = nullptr;
- }
+ CefRefPtr<CefRenderHandler> GetRenderHandler() override
+ {
+ return this;
+ }
- bool DoClose(CefRefPtr<CefBrowser> browser) override
- {
- CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+ CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override
+ {
+ return this;
+ }
- return false;
- }
+ CefRefPtr<CefLoadHandler> GetLoadHandler() override {
+ return this;
+ }
- CefRefPtr<CefRenderHandler> GetRenderHandler() override
- {
- return this;
- }
+ void OnLoadEnd(
+ CefRefPtr<CefBrowser> browser,
+ CefRefPtr<CefFrame> frame,
+ int httpStatusCode) override
+ {
+ loaded_ = true;
+ execute_queued_javascript();
+ }
- CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override
- {
- return this;
- }
+ bool OnProcessMessageReceived(
+ CefRefPtr<CefBrowser> browser,
+ CefProcessId source_process,
+ CefRefPtr<CefProcessMessage> message) override
+ {
+ auto name = message->GetName().ToString();
- CefRefPtr<CefLoadHandler> GetLoadHandler() override {
- return this;
- }
+ if (name == REMOVE_MESSAGE_NAME)
+ {
+ remove();
- void OnLoadEnd(
- CefRefPtr<CefBrowser> browser,
- CefRefPtr<CefFrame> frame,
- int httpStatusCode) override
- {
- loaded_ = true;
- execute_queued_javascript();
- }
+ return true;
+ }
+ else if (name == LOG_MESSAGE_NAME)
+ {
+ auto args = message->GetArgumentList();
+ auto severity =
+ static_cast<boost::log::trivial::severity_level>(args->GetInt(0));
+ auto msg = args->GetString(1).ToWString();
+
+ BOOST_LOG_STREAM_WITH_PARAMS(
+ log::logger::get(),
+ (boost::log::keywords::severity = severity))
+ << print() << L" [renderer_process] " << msg;
+ }
- bool OnProcessMessageReceived(
- CefRefPtr<CefBrowser> browser,
- CefProcessId source_process,
- CefRefPtr<CefProcessMessage> message) override
- {
- auto name = message->GetName().ToString();
+ return false;
+ }
- if (name == ANIMATION_FRAME_REQUESTED_MESSAGE_NAME)
- {
- CASPAR_LOG(trace)
- << print() << L" Requested animation frame";
- animation_frame_requested_ = true;
+ void invoke_requested_animation_frames()
+ {
+ if (browser_)
+ browser_->SendProcessMessage(
+ CefProcessId::PID_RENDERER,
+ CefProcessMessage::Create(TICK_MESSAGE_NAME));
+
+ graph_->set_value("tick-time", tick_timer_.elapsed()
+ * format_desc_.fps
+ * format_desc_.field_count
+ * 0.5);
+ tick_timer_.restart();
+ }
- return true;
- }
- else if (name == REMOVE_MESSAGE_NAME)
- {
- remove();
+ bool try_pop(core::draw_frame& result)
+ {
+ return lock(frames_mutex_, [&]() -> bool
+ {
+ if (!frames_.empty())
+ {
+ result = std::move(frames_.front());
+ frames_.pop();
- return true;
- }
- else if (name == LOG_MESSAGE_NAME)
- {
- auto args = message->GetArgumentList();
- auto severity =
- static_cast<boost::log::trivial::severity_level>(args->GetInt(0));
- auto msg = args->GetString(1).ToWString();
-
- BOOST_LOG_STREAM_WITH_PARAMS(
- log::logger::get(),
- (boost::log::keywords::severity = severity))
- << print() << L" [renderer_process] " << msg;
- }
-
- return false;
+ return true;
}
- void invoke_requested_animation_frames()
- {
- if (browser_)
- browser_->SendProcessMessage(
- CefProcessId::PID_RENDERER,
- CefProcessMessage::Create(TICK_MESSAGE_NAME));
- graph_->set_value("tick-time", tick_timer_.elapsed()
- * format_desc_.fps
- * format_desc_.field_count
- * 0.5);
- tick_timer_.restart();
- }
+ return false;
+ });
+ }
- bool try_pop(core::draw_frame& result)
- {
- return lock(frames_mutex_, [&]() -> bool
- {
- if (!frames_.empty())
- {
- result = std::move(frames_.front());
- frames_.pop();
+ core::draw_frame pop()
+ {
+ core::draw_frame frame;
- return true;
- }
+ if (!try_pop(frame))
+ {
+ CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " No frame in buffer"));
+ }
- return false;
- });
- }
+ return frame;
+ }
- core::draw_frame pop()
- {
- core::draw_frame frame;
+ void update()
+ {
+ invoke_requested_animation_frames();
- if (!try_pop(frame))
- {
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " No frame in buffer"));
- }
+ prec_timer timer;
+ timer.tick(0.0);
- return frame;
- }
+ auto num_frames = lock(frames_mutex_, [&]
+ {
+ return frames_.size();
+ });
- void update()
+ if (num_frames >= format_desc_.field_count)
+ {
+ if (format_desc_.field_mode != core::field_mode::progressive)
{
+ auto frame1 = pop();
+
+ executor_.yield(caspar::task_priority::lowest_priority);
+ timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
invoke_requested_animation_frames();
- prec_timer timer;
- timer.tick(0.0);
+ auto frame2 = pop();
- auto num_frames = lock(frames_mutex_, [&]
+ lock(last_frame_mutex_, [&]
{
- return frames_.size();
+ last_progressive_frame_ = frame2;
+ last_frame_ = core::draw_frame::interlace(frame1, frame2, format_desc_.field_mode);
});
+ }
+ else
+ {
+ auto frame = pop();
- if (num_frames >= format_desc_.field_count)
+ lock(last_frame_mutex_, [&]
{
- if (format_desc_.field_mode != core::field_mode::progressive)
- {
- auto frame1 = pop();
-
- executor_.yield(caspar::task_priority::lowest_priority);
- timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
- invoke_requested_animation_frames();
-
- auto frame2 = pop();
-
- lock(last_frame_mutex_, [&]
- {
- last_progressive_frame_ = frame2;
- last_frame_ = core::draw_frame::interlace(frame1, frame2, format_desc_.field_mode);
- });
- }
- else
- {
- auto frame = pop();
-
- lock(last_frame_mutex_, [&]
- {
- last_frame_ = frame;
- });
- }
- }
- else if (num_frames == 1) // Interlaced but only one frame
- { // available. Probably the last frame
- // of some animation sequence.
- auto frame = pop();
-
- lock(last_frame_mutex_, [&]
- {
- last_progressive_frame_ = frame;
- last_frame_ = frame;
- });
-
- timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
- invoke_requested_animation_frames();
- }
- else
- {
- graph_->set_tag("late-frame");
-
- if (format_desc_.field_mode != core::field_mode::progressive)
- {
- lock(last_frame_mutex_, [&]
- {
- last_frame_ = last_progressive_frame_;
- });
-
- timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
- invoke_requested_animation_frames();
- }
- }
+ last_frame_ = frame;
+ });
}
+ }
+ else if (num_frames == 1) // Interlaced but only one frame
+ { // available. Probably the last frame
+ // of some animation sequence.
+ auto frame = pop();
- void do_execute_javascript(const std::wstring& javascript)
+ lock(last_frame_mutex_, [&]
{
- html::begin_invoke([=]
+ last_progressive_frame_ = frame;
+ last_frame_ = frame;
+ });
+
+ timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
+ invoke_requested_animation_frames();
+ }
+ else
+ {
+ if (format_desc_.field_mode != core::field_mode::progressive)
+ lock(last_frame_mutex_, [&]
{
- if (browser_ != nullptr)
- browser_->GetMainFrame()->ExecuteJavaScript(u8(javascript).c_str(), browser_->GetMainFrame()->GetURL(), 0);
+ last_frame_ = last_progressive_frame_;
});
- }
+ }
+ }
- void execute_queued_javascript()
- {
- std::wstring javascript;
+ void do_execute_javascript(const std::wstring& javascript)
+ {
+ html::begin_invoke([=]
+ {
+ if (browser_ != nullptr)
+ browser_->GetMainFrame()->ExecuteJavaScript(u8(javascript).c_str(), browser_->GetMainFrame()->GetURL(), 0);
+ });
+ }
- while (javascript_before_load_.try_pop(javascript))
- do_execute_javascript(javascript);
- }
+ void execute_queued_javascript()
+ {
+ std::wstring javascript;
- std::wstring print() const
- {
- return L"html[" + url_ + L"]";
- }
+ while (javascript_before_load_.try_pop(javascript))
+ do_execute_javascript(javascript);
+ }
- IMPLEMENT_REFCOUNTING(html_client);
- };
+ std::wstring print() const
+ {
+ return L"html[" + url_ + L"]";
+ }
- class html_producer
- : public core::frame_producer_base
+ IMPLEMENT_REFCOUNTING(html_client);
+};
+
+class html_producer
+ : public core::frame_producer_base
+{
+ core::monitor::subject monitor_subject_;
+ const std::wstring url_;
+ core::constraints constraints_;
+
+ CefRefPtr<html_client> client_;
+
+public:
+ html_producer(
+ const spl::shared_ptr<core::frame_factory>& frame_factory,
+ const core::video_format_desc& format_desc,
+ const std::wstring& url)
+ : url_(url)
+ {
+ constraints_.width.set(format_desc.square_width);
+ constraints_.height.set(format_desc.square_height);
+
+ html::invoke([&]
{
- core::monitor::subject monitor_subject_;
- const std::wstring url_;
- core::constraints constraints_;
-
- CefRefPtr<html_client> client_;
-
- public:
- html_producer(
- const spl::shared_ptr<core::frame_factory>& frame_factory,
- const core::video_format_desc& format_desc,
- const std::wstring& url)
- : url_(url)
- {
- constraints_.width.set(format_desc.square_width);
- constraints_.height.set(format_desc.square_height);
+ client_ = new html_client(frame_factory, format_desc, url_);
- html::invoke([&]
- {
- client_ = new html_client(frame_factory, format_desc, url_);
+ CefWindowInfo window_info;
- CefWindowInfo window_info;
-
- window_info.SetTransparentPainting(true);
- window_info.SetAsOffScreen(nullptr);
- //window_info.SetAsWindowless(nullptr, true);
+ window_info.SetTransparentPainting(true);
+ window_info.SetAsOffScreen(nullptr);
+ //window_info.SetAsWindowless(nullptr, true);
- CefBrowserSettings browser_settings;
- CefBrowserHost::CreateBrowser(window_info, client_.get(), url, browser_settings, nullptr);
- });
- }
+ CefBrowserSettings browser_settings;
+ browser_settings.web_security = cef_state_t::STATE_DISABLED;
+ CefBrowserHost::CreateBrowser(window_info, client_.get(), url, browser_settings, nullptr);
+ });
+ }
- ~html_producer()
- {
- if (client_)
- client_->close();
- }
+ ~html_producer()
+ {
+ if (client_)
+ client_->close();
+ }
- // frame_producer
+ // frame_producer
- std::wstring name() const override
- {
- return L"html";
- }
+ std::wstring name() const override
+ {
+ return L"html";
+ }
- void on_interaction(const core::interaction_event::ptr& event) override
- {
- if (core::is<core::mouse_move_event>(event))
- {
- auto move = core::as<core::mouse_move_event>(event);
- int x = static_cast<int>(move->x * constraints_.width.get());
- int y = static_cast<int>(move->y * constraints_.height.get());
-
- CefMouseEvent e;
- e.x = x;
- e.y = y;
- client_->get_browser_host()->SendMouseMoveEvent(e, false);
- }
- else if (core::is<core::mouse_button_event>(event))
- {
- auto button = core::as<core::mouse_button_event>(event);
- int x = static_cast<int>(button->x * constraints_.width.get());
- int y = static_cast<int>(button->y * constraints_.height.get());
-
- CefMouseEvent e;
- e.x = x;
- e.y = y;
- client_->get_browser_host()->SendMouseClickEvent(
- e,
- static_cast<CefBrowserHost::MouseButtonType>(button->button),
- !button->pressed,
- 1);
- }
- else if (core::is<core::mouse_wheel_event>(event))
- {
- auto wheel = core::as<core::mouse_wheel_event>(event);
- int x = static_cast<int>(wheel->x * constraints_.width.get());
- int y = static_cast<int>(wheel->y * constraints_.height.get());
-
- CefMouseEvent e;
- e.x = x;
- e.y = y;
- static const int WHEEL_TICKS_AMPLIFICATION = 40;
- client_->get_browser_host()->SendMouseWheelEvent(
- e,
- 0, // delta_x
- wheel->ticks_delta * WHEEL_TICKS_AMPLIFICATION); // delta_y
- }
- }
+ void on_interaction(const core::interaction_event::ptr& event) override
+ {
+ if (core::is<core::mouse_move_event>(event))
+ {
+ auto move = core::as<core::mouse_move_event>(event);
+ int x = static_cast<int>(move->x * constraints_.width.get());
+ int y = static_cast<int>(move->y * constraints_.height.get());
+
+ CefMouseEvent e;
+ e.x = x;
+ e.y = y;
+ client_->get_browser_host()->SendMouseMoveEvent(e, false);
+ }
+ else if (core::is<core::mouse_button_event>(event))
+ {
+ auto button = core::as<core::mouse_button_event>(event);
+ int x = static_cast<int>(button->x * constraints_.width.get());
+ int y = static_cast<int>(button->y * constraints_.height.get());
+
+ CefMouseEvent e;
+ e.x = x;
+ e.y = y;
+ client_->get_browser_host()->SendMouseClickEvent(
+ e,
+ static_cast<CefBrowserHost::MouseButtonType>(button->button),
+ !button->pressed,
+ 1);
+ }
+ else if (core::is<core::mouse_wheel_event>(event))
+ {
+ auto wheel = core::as<core::mouse_wheel_event>(event);
+ int x = static_cast<int>(wheel->x * constraints_.width.get());
+ int y = static_cast<int>(wheel->y * constraints_.height.get());
+
+ CefMouseEvent e;
+ e.x = x;
+ e.y = y;
+ static const int WHEEL_TICKS_AMPLIFICATION = 40;
+ client_->get_browser_host()->SendMouseWheelEvent(
+ e,
+ 0, // delta_x
+ wheel->ticks_delta * WHEEL_TICKS_AMPLIFICATION); // delta_y
+ }
+ }
- bool collides(double x, double y) const override
- {
- return true;
- }
+ bool collides(double x, double y) const override
+ {
+ return true;
+ }
- core::draw_frame receive_impl() override
+ core::draw_frame receive_impl() override
+ {
+ if (client_)
+ {
+ if (client_->is_removed())
{
- if (client_)
- {
- if (client_->is_removed())
- {
- client_ = nullptr;
- return core::draw_frame::empty();
- }
-
- return client_->receive();
- }
-
+ client_ = nullptr;
return core::draw_frame::empty();
}
- std::future<std::wstring> call(const std::vector<std::wstring>& params) override
- {
- if (!client_)
- return make_ready_future(std::wstring(L""));
+ return client_->receive();
+ }
+
+ return core::draw_frame::empty();
+ }
- auto javascript = params.at(0);
+ std::future<std::wstring> call(const std::vector<std::wstring>& params) override
+ {
+ if (!client_)
+ return make_ready_future(std::wstring(L""));
- client_->execute_javascript(javascript);
+ auto javascript = params.at(0);
- return make_ready_future(std::wstring(L""));
- }
+ client_->execute_javascript(javascript);
- std::wstring print() const override
- {
- return L"html[" + url_ + L"]";
- }
+ return make_ready_future(std::wstring(L""));
+ }
- boost::property_tree::wptree info() const override
- {
- boost::property_tree::wptree info;
- info.add(L"type", L"html-producer");
- return info;
- }
+ std::wstring print() const override
+ {
+ return L"html[" + url_ + L"]";
+ }
- core::constraints& pixel_constraints() override
- {
- return constraints_;
- }
+ boost::property_tree::wptree info() const override
+ {
+ boost::property_tree::wptree info;
+ info.add(L"type", L"html-producer");
+ return info;
+ }
- core::monitor::subject& monitor_output()
- {
- return monitor_subject_;
- }
- };
+ core::constraints& pixel_constraints() override
+ {
+ return constraints_;
+ }
- spl::shared_ptr<core::frame_producer> create_producer(
- const spl::shared_ptr<core::frame_factory>& frame_factory,
- const core::video_format_desc& format_desc,
- const std::vector<std::wstring>& params)
- {
- const auto filename = env::template_folder() + params.at(0) + L".html";
- const auto found_filename = find_case_insensitive(filename);
+ core::monitor::subject& monitor_output()
+ {
+ return monitor_subject_;
+ }
+};
+
+void describe_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+ sink.short_description(L"Renders a web page in real time.");
+ sink.syntax(L"{[html_filename:string]},{[HTML] [url:string]}");
+ sink.para()->text(L"Embeds an actual web browser and renders the content in realtime.");
+ sink.para()
+ ->text(L"HTML content can either be stored locally under the ")->code(L"templates")
+ ->text(L" folder or fetched directly via an URL. If a .html file is found with the name ")
+ ->code(L"html_filename")->text(L" under the ")->code(L"templates")->text(L" folder it will be rendered. If the ")
+ ->code(L"[HTML] url")->text(L" syntax is used instead, the URL will be loaded.");
+ sink.para()->text(L"Examples:");
+ sink.example(L">> PLAY 1-10 [HTML] http://www.casparcg.com");
+ sink.example(L">> PLAY 1-10 folder/html_file");
+}
+
+spl::shared_ptr<core::frame_producer> create_producer(
+ const core::frame_producer_dependencies& dependencies,
+ const std::vector<std::wstring>& params)
+{
+ const auto filename = env::template_folder() + params.at(0) + L".html";
+ const auto found_filename = find_case_insensitive(filename);
- if (!found_filename && !boost::iequals(params.at(0), L"[HTML]"))
- return core::frame_producer::empty();
+ if (!found_filename && !boost::iequals(params.at(0), L"[HTML]"))
+ return core::frame_producer::empty();
- const auto url = found_filename
- ? L"file://" + *found_filename
- : params.at(1);
+ const auto url = found_filename
+ ? L"file://" + *found_filename
+ : params.at(1);
- if (!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA"))
- return core::frame_producer::empty();
+ if (!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA"))
+ return core::frame_producer::empty();
- return core::create_destroy_proxy(spl::make_shared<html_producer>(
- frame_factory,
- format_desc,
- url));
- }
- }
+ return core::create_destroy_proxy(spl::make_shared<html_producer>(
+ dependencies.frame_factory,
+ dependencies.format_desc,
+ url));
}
+
+}}