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/env.h>
34 #include <common/concurrency/executor.h>
35 #include <common/concurrency/lock.h>
36 #include <common/diagnostics/graph.h>
37 #include <common/utility/timer.h>
39 #include <berkelium/Berkelium.hpp>
40 #include <berkelium/Context.hpp>
41 #include <berkelium/Window.hpp>
42 #include <berkelium/WindowDelegate.hpp>
43 #include <berkelium/Rect.hpp>
45 #include <boost/algorithm/string/predicate.hpp>
46 #include <boost/algorithm/string/trim.hpp>
47 #include <boost/filesystem.hpp>
48 #include <boost/format.hpp>
50 #include <tbb/atomic.h>
51 #include <tbb/parallel_for.h>
55 namespace caspar { namespace html {
58 : public core::frame_producer
59 , public Berkelium::WindowDelegate
61 core::monitor::subject monitor_subject_;
62 const std::wstring url_;
63 safe_ptr<diagnostics::graph> graph_;
65 const safe_ptr<core::frame_factory> frame_factory_;
67 safe_ptr<core::basic_frame> last_frame_;
68 mutable boost::mutex last_frame_mutex_;
70 std::vector<unsigned char> frame_;
71 mutable boost::mutex frame_mutex_;
73 tbb::atomic<bool> invalidated_;
75 std::unique_ptr<Berkelium::Window> window_;
77 high_prec_timer timer_;
83 const safe_ptr<core::frame_factory>& frame_factory,
84 const std::wstring& url)
86 , frame_factory_(frame_factory)
87 , last_frame_(core::basic_frame::empty())
88 , frame_(frame_factory->get_video_format_desc().width * frame_factory->get_video_format_desc().height * 4, 0)
89 , executor_(L"html_producer")
93 graph_->set_text(print());
94 diagnostics::register_graph(graph_);
99 std::unique_ptr<Berkelium::Context> context(Berkelium::Context::create());
100 window_.reset(Berkelium::Window::create(context.get()));
104 frame_factory->get_video_format_desc().width,
105 frame_factory->get_video_format_desc().height);
106 window_->setDelegate(this);
107 window_->setTransparent(true);
109 const auto narrow_url = narrow(url);
111 if(!window_->navigateTo(
112 Berkelium::URLString::point_to(
114 narrow_url.length())))
116 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Failed to navigate."));
133 safe_ptr<core::basic_frame> receive(int) override
138 safe_ptr<core::basic_frame> last_frame() const override
140 return lock(last_frame_mutex_, [&]
146 boost::unique_future<std::wstring> call(const std::wstring& param) override
148 static const boost::wregex play_exp(L"PLAY\\s*(\\d+)?", boost::regex::icase);
149 static const boost::wregex stop_exp(L"STOP\\s*(\\d+)?", boost::regex::icase);
150 static const boost::wregex next_exp(L"NEXT\\s*(\\d+)?", boost::regex::icase);
151 static const boost::wregex remove_exp(L"REMOVE\\s*(\\d+)?", boost::regex::icase);
152 static const boost::wregex update_exp(L"UPDATE\\s+(\\d+)?\"?(?<VALUE>.*)\"?", boost::regex::icase);
153 static const boost::wregex invoke_exp(L"INVOKE\\s+(\\d+)?\"?(?<VALUE>.*)\"?", boost::regex::icase);
157 auto javascript = param;
161 if (boost::regex_match(param, what, play_exp))
163 javascript = (boost::wformat(L"play()")).str();
165 else if (boost::regex_match(param, what, stop_exp))
167 javascript = (boost::wformat(L"stop()")).str();
169 else if (boost::regex_match(param, what, next_exp))
171 javascript = (boost::wformat(L"next()")).str();
173 else if (boost::regex_match(param, what, remove_exp))
175 javascript = (boost::wformat(L"remove()")).str();
177 else if (boost::regex_match(param, what, update_exp))
179 javascript = (boost::wformat(L"update(\"%1%\")") % boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \""))).str();
181 else if (boost::regex_match(param, what, invoke_exp))
183 javascript = (boost::wformat(L"invoke(\"%1%\")") % boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \""))).str();
186 window_->executeJavascript(Berkelium::WideString::point_to(javascript.data(), javascript.length()));
189 boost::packaged_task<std::wstring> task([=]() -> std::wstring
191 html::invoke(command);
198 return task.get_future();
201 std::wstring print() const override
203 return L"html[" + url_ + L"]";
206 boost::property_tree::wptree info() const override
208 boost::property_tree::wptree info;
209 info.add(L"type", L"html-producer");
213 core::monitor::subject& monitor_output()
215 return monitor_subject_;
218 // Berkelium::WindowDelegate
221 Berkelium::Window* wini,
222 const unsigned char* bitmap_in,
223 const Berkelium::Rect& bitmap_rect,
224 size_t num_copy_rects,
225 const Berkelium::Rect* copy_rects,
228 const Berkelium::Rect& scroll_rect) override
230 lock(frame_mutex_, [&]
234 tbb::parallel_for<int>(0, num_copy_rects, 1, [&](int i)
236 tbb::parallel_for<int>(0, copy_rects[i].height(), 1, [&](int y)
239 frame_.data() + ((y + copy_rects[i].top()) * frame_factory_->get_video_format_desc().width + copy_rects[i].left()) * 4,
240 bitmap_in + ((y + copy_rects[i].top() - bitmap_rect.top()) * bitmap_rect.width() + copy_rects[i].left() - bitmap_rect.left()) * 4,
241 copy_rects[i].width() * 4);
248 Berkelium::Window *win,
249 Berkelium::WideString message,
250 Berkelium::URLString origin,
251 Berkelium::URLString target) override
257 safe_ptr<core::basic_frame> draw(
258 safe_ptr<core::write_frame> frame,
259 core::field_mode::type field_mode)
261 const auto& pixel_desc = frame->get_pixel_format_desc();
263 CASPAR_ASSERT(pixel_desc.pix_fmt == core::pixel_format::bgra);
265 const auto& height = pixel_desc.planes[0].height;
266 const auto& linesize = pixel_desc.planes[0].linesize;
268 lock(frame_mutex_, [&]
270 tbb::parallel_for<int>(
271 field_mode == core::field_mode::upper ? 0 : 1,
273 field_mode == core::field_mode::progressive ? 1 : 2,
277 frame->image_data().begin() + y * linesize,
278 frame_.data() + y * linesize,
288 if(invalidated_.fetch_and_store(false))
290 core::pixel_format_desc pixel_desc;
291 pixel_desc.pix_fmt = core::pixel_format::bgra;
292 pixel_desc.planes.push_back(
293 core::pixel_format_desc::plane(
294 frame_factory_->get_video_format_desc().width,
295 frame_factory_->get_video_format_desc().height,
298 auto frame = frame_factory_->create_frame(this, pixel_desc);
300 const auto& format_desc = frame_factory_->get_video_format_desc();
302 if (format_desc.field_mode != core::field_mode::progressive)
304 draw(frame, format_desc.field_mode);
307 timer_.tick(1.0 / (format_desc.fps * format_desc.field_count));
309 draw(frame, static_cast<core::field_mode::type>(format_desc.field_mode ^ core::field_mode::progressive));
313 draw(frame, format_desc.field_mode);
318 lock(last_frame_mutex_, [&]
325 timer_.tick(1.0 / (format_desc.fps * format_desc.field_count));
328 executor_.begin_invoke([this]{ tick(); });
332 safe_ptr<core::frame_producer> create_producer(
333 const safe_ptr<core::frame_factory>& frame_factory,
334 const core::parameters& params)
336 const auto filename = env::template_folder() + L"\\" + params.at_original(0) + L".html";
338 const auto url = boost::filesystem::exists(filename)
340 : params.at_original(0);
342 if(!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA"))
343 return core::frame_producer::empty();
345 return core::create_producer_destroy_proxy(
346 core::create_producer_print_proxy(
347 make_safe<html_producer>(