#include "producer/html_producer.h"
#include <common/concurrency/executor.h>
+#include <common/concurrency/future_util.h>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+#include <boost/timer.hpp>
+#include <boost/range/algorithm/remove_if.hpp>
+#include <boost/thread/future.hpp>
+#include <boost/lexical_cast.hpp>
#include <cef_app.h>
-#include <tbb/atomic.h>
-
#pragma comment(lib, "libcef.lib")
#pragma comment(lib, "libcef_dll_wrapper.lib")
namespace caspar { namespace html {
-tbb::atomic<bool> g_cef_running;
std::unique_ptr<executor> g_cef_executor;
-void tick()
+void caspar_log(
+ const CefRefPtr<CefBrowser>& browser,
+ log::severity_level level,
+ const std::string& message)
{
- if (!g_cef_running)
- return;
-
- CefDoMessageLoopWork();
- g_cef_executor->begin_invoke([&]{ tick(); });
+ if (browser)
+ {
+ auto msg = CefProcessMessage::Create(LOG_MESSAGE_NAME);
+ msg->GetArgumentList()->SetInt(0, level);
+ msg->GetArgumentList()->SetString(1, message);
+ browser->SendProcessMessage(PID_BROWSER, msg);
+ }
}
+class animation_handler : public CefV8Handler
+{
+private:
+ std::vector<CefRefPtr<CefV8Value>> callbacks_;
+ boost::timer since_start_timer_;
+public:
+ CefRefPtr<CefBrowser> browser;
+ std::function<CefRefPtr<CefV8Context>()> get_context;
+
+ bool Execute(
+ const CefString& name,
+ CefRefPtr<CefV8Value> object,
+ const CefV8ValueList& arguments,
+ CefRefPtr<CefV8Value>& retval,
+ CefString& exception) override
+ {
+ if (!CefCurrentlyOn(TID_RENDERER))
+ return false;
+
+ if (arguments.size() < 1 || !arguments.at(0)->IsFunction())
+ {
+ return false;
+ }
+
+ callbacks_.push_back(arguments.at(0));
+
+ if (browser)
+ browser->SendProcessMessage(PID_BROWSER, CefProcessMessage::Create(
+ ANIMATION_FRAME_REQUESTED_MESSAGE_NAME));
+
+ return true;
+ }
+
+ void tick()
+ {
+ if (!get_context)
+ return;
+
+ auto context = get_context();
+
+ if (!context)
+ return;
+
+ if (!CefCurrentlyOn(TID_RENDERER))
+ return;
+
+ std::vector<CefRefPtr<CefV8Value>> callbacks;
+ callbacks_.swap(callbacks);
+
+ CefV8ValueList callback_args;
+ CefTime timestamp;
+ timestamp.Now();
+ callback_args.push_back(CefV8Value::CreateDouble(
+ since_start_timer_.elapsed() * 1000.0));
+
+ BOOST_FOREACH(auto callback, callbacks)
+ {
+ callback->ExecuteFunctionWithContext(
+ context, callback, callback_args);
+ }
+ }
+
+ IMPLEMENT_REFCOUNTING(animation_handler);
+};
+
+class remove_handler : public CefV8Handler
+{
+ CefRefPtr<CefBrowser> browser_;
+public:
+ remove_handler(CefRefPtr<CefBrowser> browser)
+ : browser_(browser)
+ {
+ }
+
+ bool Execute(
+ const CefString& name,
+ CefRefPtr<CefV8Value> object,
+ const CefV8ValueList& arguments,
+ CefRefPtr<CefV8Value>& retval,
+ CefString& exception) override
+ {
+ if (!CefCurrentlyOn(TID_RENDERER))
+ return false;
+
+ browser_->SendProcessMessage(
+ PID_BROWSER,
+ CefProcessMessage::Create(REMOVE_MESSAGE_NAME));
+
+ return true;
+ }
+
+ IMPLEMENT_REFCOUNTING(remove_handler);
+};
+
+class renderer_application : public CefApp, CefRenderProcessHandler
+{
+ std::vector<std::pair<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>>> contexts_per_handlers_;
+ //std::map<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>> contexts_per_handlers_;
+public:
+ CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override
+ {
+ return this;
+ }
+
+ CefRefPtr<CefV8Context> get_context(
+ const CefRefPtr<animation_handler>& handler)
+ {
+ BOOST_FOREACH(auto& ctx, contexts_per_handlers_)
+ {
+ if (ctx.first == handler)
+ return ctx.second;
+ }
+
+ return nullptr;
+ }
+
+ void OnContextCreated(
+ CefRefPtr<CefBrowser> browser,
+ CefRefPtr<CefFrame> frame,
+ CefRefPtr<CefV8Context> context) override
+ {
+ caspar_log(browser, log::trace,
+ "context for frame "
+ + boost::lexical_cast<std::string>(frame->GetIdentifier())
+ + " created");
+
+ CefRefPtr<animation_handler> handler = new animation_handler;
+ contexts_per_handlers_.push_back(std::make_pair(handler, context));
+ auto handler_ptr = handler.get();
+
+ handler->browser = browser;
+ handler->get_context = [this, handler_ptr]
+ {
+ return get_context(handler_ptr);
+ };
+
+ auto window = context->GetGlobal();
+
+ auto function = CefV8Value::CreateFunction(
+ "requestAnimationFrame",
+ handler.get());
+ window->SetValue(
+ "requestAnimationFrame",
+ function,
+ V8_PROPERTY_ATTRIBUTE_NONE);
+
+ function = CefV8Value::CreateFunction(
+ "remove",
+ new remove_handler(browser));
+ window->SetValue(
+ "remove",
+ function,
+ V8_PROPERTY_ATTRIBUTE_NONE);
+ }
+
+ void OnContextReleased(
+ CefRefPtr<CefBrowser> browser,
+ CefRefPtr<CefFrame> frame,
+ CefRefPtr<CefV8Context> context)
+ {
+ auto removed = boost::remove_if(
+ contexts_per_handlers_, [&](const std::pair<
+ CefRefPtr<animation_handler>,
+ CefRefPtr<CefV8Context>>& c)
+ {
+ return c.second->IsSame(context);
+ });
+
+ if (removed != contexts_per_handlers_.end())
+ caspar_log(browser, log::trace,
+ "context for frame "
+ + boost::lexical_cast<std::string>(frame->GetIdentifier())
+ + " released");
+ else
+ caspar_log(browser, log::warning,
+ "context for frame "
+ + boost::lexical_cast<std::string>(frame->GetIdentifier())
+ + " released, but not found");
+ }
+
+ void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) override
+ {
+ contexts_per_handlers_.clear();
+ }
+
+ bool OnProcessMessageReceived(
+ CefRefPtr<CefBrowser> browser,
+ CefProcessId source_process,
+ CefRefPtr<CefProcessMessage> message) override
+ {
+ if (message->GetName().ToString() == TICK_MESSAGE_NAME)
+ {
+ BOOST_FOREACH(auto& handler, contexts_per_handlers_)
+ {
+ handler.first->tick();
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ IMPLEMENT_REFCOUNTING(renderer_application);
+};
+
bool init()
{
CefMainArgs main_args;
- if (CefExecuteProcess(main_args, nullptr, nullptr) >= 0)
+ if (CefExecuteProcess(main_args, CefRefPtr<CefApp>(new renderer_application), nullptr) >= 0)
return false;
core::register_producer_factory(html::create_producer);
g_cef_executor.reset(new executor(L"cef"));
- g_cef_running = true;
-
g_cef_executor->invoke([&]
{
CefSettings settings;
//settings.windowless_rendering_enabled = true;
CefInitialize(main_args, settings, nullptr, nullptr);
});
-
g_cef_executor->begin_invoke([&]
{
- tick();
+ CefRunMessageLoop();
});
return true;
void uninit()
{
- g_cef_executor->invoke([=]
+ invoke([]
+ {
+ CefQuitMessageLoop();
+ });
+ g_cef_executor->begin_invoke([&]
{
- g_cef_running = false;
- g_cef_executor->wait();
CefShutdown();
});
}
+class cef_task : public CefTask
+{
+private:
+ boost::promise<void> promise_;
+ std::function<void ()> function_;
+public:
+ cef_task(const std::function<void ()>& function)
+ : function_(function)
+ {
+ }
+
+ void Execute() override
+ {
+ CASPAR_LOG(trace) << "[cef_task] executing task";
+
+ try
+ {
+ function_();
+ promise_.set_value();
+ CASPAR_LOG(trace) << "[cef_task] task succeeded";
+ }
+ catch (...)
+ {
+ promise_.set_exception(boost::current_exception());
+ CASPAR_LOG(warning) << "[cef_task] task failed";
+ }
+ }
+
+ boost::unique_future<void> future()
+ {
+ return promise_.get_future();
+ }
+
+ IMPLEMENT_REFCOUNTING(shutdown_task);
+};
void invoke(const std::function<void()>& func)
{
- g_cef_executor->invoke(func);
+ begin_invoke(func).get();
}
-void begin_invoke(const std::function<void()>& func)
+boost::unique_future<void> begin_invoke(const std::function<void()>& func)
{
- g_cef_executor->begin_invoke(func);
+ CefRefPtr<cef_task> task = new cef_task(func);
+
+ if (CefCurrentlyOn(TID_UI))
+ {
+ // Avoid deadlock.
+ task->Execute();
+ return task->future();
+ }
+
+ if (CefPostTask(TID_UI, task.get()))
+ return task->future();
+ else
+ BOOST_THROW_EXCEPTION(caspar_exception()
+ << msg_info("[cef_executor] Could not post task"));
}
}}
#include <common/env.h>
#include <common/concurrency/executor.h>
#include <common/concurrency/lock.h>
+#include <common/concurrency/future_util.h>
#include <common/diagnostics/graph.h>
#include <common/utility/timer.h>
#include <common/memory/memcpy.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
+#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
+#include <boost/timer.hpp>
#include <tbb/atomic.h>
-#include <tbb/parallel_for.h>
+#include <tbb/concurrent_queue.h>
#include <cef_task.h>
#include <cef_app.h>
#include <cef_client.h>
#include <cef_render_handler.h>
+#include <queue>
+
#include "html.h"
#pragma comment (lib, "libcef.lib")
: public CefClient
, public CefRenderHandler
, public CefLifeSpanHandler
+ , public CefLoadHandler
{
+ std::wstring url_;
+ safe_ptr<diagnostics::graph> graph_;
+ boost::timer tick_timer_;
+ boost::timer frame_timer_;
+ boost::timer paint_timer_;
- safe_ptr<core::frame_factory> frame_factory_;
- tbb::atomic<bool> invalidated_;
- std::vector<unsigned char> frame_;
- mutable boost::mutex frame_mutex_;
+ safe_ptr<core::frame_factory> frame_factory_;
+ tbb::concurrent_queue<std::wstring> javascript_before_load_;
+ tbb::atomic<bool> loaded_;
+ tbb::atomic<bool> removed_;
+ tbb::atomic<bool> animation_frame_requested_;
+ std::queue<safe_ptr<core::basic_frame>> frames_;
+ mutable boost::mutex frames_mutex_;
- safe_ptr<core::basic_frame> last_frame_;
- mutable boost::mutex last_frame_mutex_;
+ safe_ptr<core::basic_frame> last_frame_;
+ safe_ptr<core::basic_frame> last_progressive_frame_;
+ mutable boost::mutex last_frame_mutex_;
- CefRefPtr<CefBrowser> browser_;
+ CefRefPtr<CefBrowser> browser_;
- executor executor_;
+ executor executor_;
public:
- html_client(safe_ptr<core::frame_factory> frame_factory)
- : frame_factory_(frame_factory)
- , frame_(frame_factory->get_video_format_desc().width * frame_factory->get_video_format_desc().height * 4, 0)
+ html_client(safe_ptr<core::frame_factory> frame_factory, const std::wstring& url)
+ : url_(url)
+ , frame_factory_(frame_factory)
, last_frame_(core::basic_frame::empty())
+ , last_progressive_frame_(core::basic_frame::empty())
, executor_(L"html_producer")
{
- invalidated_ = true;
+ 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(); });
}
- safe_ptr<core::basic_frame> receive(int)
+ safe_ptr<core::basic_frame> receive()
{
auto frame = last_frame();
executor_.begin_invoke([&]{ update(); });
void execute_javascript(const std::wstring& javascript)
{
- html::begin_invoke([=]
+ if (!loaded_)
{
- if (browser_ != nullptr)
- browser_->GetMainFrame()->ExecuteJavaScript(narrow(javascript).c_str(), browser_->GetMainFrame()->GetURL(), 0);
- });
+ javascript_before_load_.push(javascript);
+ }
+ else
+ {
+ execute_queued_javascript();
+ do_execute_javascript(javascript);
+ }
}
void close()
{
+ if (!animation_frame_requested_)
+ CASPAR_LOG(warning) << print()
+ << " window.requestAnimationFrame() never called. "
+ << "Animations might have been laggy";
+
html::invoke([=]
{
if (browser_ != nullptr)
});
}
+ void remove()
+ {
+ close();
+ removed_ = true;
+ }
+
+ bool is_removed() const
+ {
+ return removed_;
+ }
+
private:
bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
{
CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
- rect = CefRect(0, 0, frame_factory_->get_video_format_desc().width, frame_factory_->get_video_format_desc().height);
+ rect = CefRect(0, 0, frame_factory_->get_video_format_desc().square_width, frame_factory_->get_video_format_desc().square_height);
return true;
}
- void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height)
+ 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()
+ * frame_factory_->get_video_format_desc().fps
+ * frame_factory_->get_video_format_desc().field_count
+ * 0.5);
+ paint_timer_.restart();
CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
- lock(frame_mutex_, [&]
+ boost::timer copy_timer;
+ core::pixel_format_desc pixel_desc;
+ pixel_desc.pix_fmt = 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);
+ fast_memcpy(frame->image_data().begin(), buffer, width * height * 4);
+ frame->commit();
+
+ lock(frames_mutex_, [&]
{
- invalidated_ = true;
- fast_memcpy(frame_.data(), buffer, width * height * 4);
+ frames_.push(frame);
+
+ size_t max_in_queue = frame_factory_->get_video_format_desc().field_count;
+
+ while (frames_.size() > max_in_queue)
+ {
+ frames_.pop();
+ graph_->set_tag("dropped-frame");
+ }
});
+ graph_->set_value("copy-time", copy_timer.elapsed()
+ * frame_factory_->get_video_format_desc().fps
+ * frame_factory_->get_video_format_desc().field_count
+ * 0.5);
}
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override
return this;
}
- void invoke_on_enter_frame()
+ CefRefPtr<CefLoadHandler> GetLoadHandler() override {
+ return this;
+ }
+
+ void OnLoadEnd(
+ CefRefPtr<CefBrowser> browser,
+ CefRefPtr<CefFrame> frame,
+ int httpStatusCode) override
{
- //html::invoke([this]
- //{
- // static const std::wstring javascript = L"onEnterFrame()";
- // execute_javascript(javascript);
- //});
+ loaded_ = true;
+ execute_queued_javascript();
}
- safe_ptr<core::basic_frame> draw(safe_ptr<core::write_frame> frame, core::field_mode::type field_mode)
+ bool OnProcessMessageReceived(
+ CefRefPtr<CefBrowser> browser,
+ CefProcessId source_process,
+ CefRefPtr<CefProcessMessage> message) override
{
- const auto& pixel_desc = frame->get_pixel_format_desc();
+ auto name = message->GetName().ToString();
+
+ if (name == ANIMATION_FRAME_REQUESTED_MESSAGE_NAME)
+ {
+ CASPAR_LOG(trace)
+ << print() << L" Requested animation frame";
+ animation_frame_requested_ = true;
+
+ return true;
+ }
+ else if (name == REMOVE_MESSAGE_NAME)
+ {
+ remove();
+
+ return true;
+ }
+ else if (name == LOG_MESSAGE_NAME)
+ {
+ auto args = message->GetArgumentList();
+ auto severity =
+ static_cast<log::severity_level>(args->GetInt(0));
+ auto msg = args->GetString(1).ToWString();
+
+ BOOST_LOG_STREAM_WITH_PARAMS(
+ log::get_logger(),
+ (boost::log::keywords::severity = severity))
+ << print() << L" [renderer_process] " << msg;
+ }
- CASPAR_ASSERT(pixel_desc.pix_fmt == core::pixel_format::bgra);
+ return false;
+ }
+
+ void invoke_on_enter_frame()
+ {
+ if (browser_)
+ browser_->SendProcessMessage(CefProcessId::PID_RENDERER, CefProcessMessage::Create(TICK_MESSAGE_NAME));
+ graph_->set_value("tick-time", tick_timer_.elapsed()
+ * frame_factory_->get_video_format_desc().fps
+ * frame_factory_->get_video_format_desc().field_count
+ * 0.5);
+ tick_timer_.restart();
+ }
- const auto& height = pixel_desc.planes[0].height;
- const auto& linesize = pixel_desc.planes[0].linesize;
-
- lock(frame_mutex_, [&]
+ bool try_pop(safe_ptr<core::basic_frame>& result)
+ {
+ return lock(frames_mutex_, [&]() -> bool
{
- tbb::parallel_for<int>(
- field_mode == core::field_mode::upper ? 0 : 1,
- height,
- field_mode == core::field_mode::progressive ? 1 : 2,
- [&](int y)
+ if (!frames_.empty())
{
- fast_memcpy(
- frame->image_data().begin() + y * linesize,
- frame_.data() + y * linesize,
- linesize);
- });
+ result = frames_.front();
+ frames_.pop();
+
+ return true;
+ }
+
+ return false;
});
+ }
+
+ safe_ptr<core::basic_frame> pop()
+ {
+ safe_ptr<core::basic_frame> frame;
+
+ if (!try_pop(frame))
+ {
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + "No frame in buffer"));
+ }
return frame;
}
{
invoke_on_enter_frame();
- if (invalidated_.fetch_and_store(false))
- {
- high_prec_timer timer;
- timer.tick(0.0);
-
- core::pixel_format_desc pixel_desc;
- pixel_desc.pix_fmt = core::pixel_format::bgra;
- pixel_desc.planes.push_back(
- core::pixel_format_desc::plane(
- frame_factory_->get_video_format_desc().width,
- frame_factory_->get_video_format_desc().height,
- 4));
+ high_prec_timer timer;
+ timer.tick(0.0);
+ const auto& format_desc = frame_factory_->get_video_format_desc();
- auto frame = frame_factory_->create_frame(this, pixel_desc);
-
- const auto& format_desc = frame_factory_->get_video_format_desc();
+ bool has_frames = lock(frames_mutex_, [&]
+ {
+ return frames_.size() >= format_desc.field_count;
+ });
+ if (has_frames)
+ {
if (format_desc.field_mode != core::field_mode::progressive)
{
- draw(frame, format_desc.field_mode);
+ auto frame1 = pop();
executor_.yield();
timer.tick(1.0 / (format_desc.fps * format_desc.field_count));
+ invoke_on_enter_frame();
+
+ auto frame2 = pop();
- draw(frame, static_cast<core::field_mode::type>(format_desc.field_mode ^ core::field_mode::progressive));
+ lock(last_frame_mutex_, [&]
+ {
+ last_progressive_frame_ = frame2;
+ last_frame_ = core::basic_frame::interlace(frame1, frame2, format_desc.field_mode);
+ });
}
else
{
- draw(frame, format_desc.field_mode);
- }
+ auto frame = pop();
- frame->commit();
+ lock(last_frame_mutex_, [&]
+ {
+ last_frame_ = frame;
+ });
+ }
+ }
+ else
+ {
+ graph_->set_tag("late-frame");
- lock(last_frame_mutex_, [&]
+ if (format_desc.field_mode != core::field_mode::progressive)
{
- last_frame_ = frame;
- });
+ lock(last_frame_mutex_, [&]
+ {
+ last_frame_ = last_progressive_frame_;
+ });
+
+ timer.tick(1.0 / (format_desc.fps * format_desc.field_count));
+ invoke_on_enter_frame();
+ }
}
}
+ void do_execute_javascript(const std::wstring& javascript)
+ {
+ html::begin_invoke([=]
+ {
+ if (browser_ != nullptr)
+ browser_->GetMainFrame()->ExecuteJavaScript(narrow(javascript).c_str(), browser_->GetMainFrame()->GetURL(), 0);
+ });
+ }
+
+ void execute_queued_javascript()
+ {
+ std::wstring javascript;
+
+ while (javascript_before_load_.try_pop(javascript))
+ do_execute_javascript(javascript);
+ }
+
+ std::wstring print() const
+ {
+ return L"html[" + url_ + L"]";
+ }
+
IMPLEMENT_REFCOUNTING(html_client);
};
{
core::monitor::subject monitor_subject_;
const std::wstring url_;
- safe_ptr<diagnostics::graph> graph_;
CefRefPtr<html_client> client_;
const std::wstring& url)
: url_(url)
{
- graph_->set_text(print());
- diagnostics::register_graph(graph_);
-
html::invoke([&]
{
- client_ = new html_client(frame_factory);
+ client_ = new html_client(frame_factory, url_);
CefWindowInfo window_info;
window_info.SetAsOffScreen(nullptr);
//window_info.SetAsWindowless(nullptr, true);
- CefBrowserSettings browser_settings;
+ CefBrowserSettings browser_settings;
CefBrowserHost::CreateBrowser(window_info, client_.get(), url, browser_settings, nullptr);
});
}
~html_producer()
{
- client_->close();
+ if (client_)
+ client_->close();
}
// frame_producer
safe_ptr<core::basic_frame> receive(int) override
{
- return client_->receive(0);
+ if (client_)
+ {
+ if (client_->is_removed())
+ {
+ client_ = nullptr;
+ return core::basic_frame::empty();
+ }
+
+ return client_->receive();
+ }
+
+ return core::basic_frame::empty();
}
safe_ptr<core::basic_frame> last_frame() const override
{
- return client_->last_frame();
+ return client_
+ ? client_->last_frame()
+ : core::basic_frame::empty();
}
boost::unique_future<std::wstring> call(const std::wstring& param) override
static const boost::wregex update_exp(L"UPDATE\\s+(\\d+)?\"?(?<VALUE>.*)\"?", boost::regex::icase);
static const boost::wregex invoke_exp(L"INVOKE\\s+(\\d+)?\"?(?<VALUE>.*)\"?", boost::regex::icase);
- auto command = [=]
- {
- auto javascript = param;
-
- boost::wsmatch what;
+ if (!client_)
+ return wrap_as_future(std::wstring(L""));
- if (boost::regex_match(param, what, play_exp))
- {
- javascript = (boost::wformat(L"play()")).str();
- }
- else if (boost::regex_match(param, what, stop_exp))
- {
- javascript = (boost::wformat(L"stop()")).str();
- }
- else if (boost::regex_match(param, what, next_exp))
- {
- javascript = (boost::wformat(L"next()")).str();
- }
- else if (boost::regex_match(param, what, remove_exp))
- {
- javascript = (boost::wformat(L"remove()")).str();
- }
- else if (boost::regex_match(param, what, update_exp))
- {
- javascript = (boost::wformat(L"update(\"%1%\")") % boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \""))).str();
- }
- else if (boost::regex_match(param, what, invoke_exp))
- {
- javascript = (boost::wformat(L"invoke(\"%1%\")") % boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \""))).str();
- }
+ auto javascript = param;
- client_->execute_javascript(javascript);
- };
+ boost::wsmatch what;
- boost::packaged_task<std::wstring> task([=]() -> std::wstring
+ if (boost::regex_match(param, what, play_exp))
{
- html::invoke(command);
- return L"";
- });
+ javascript = (boost::wformat(L"play()")).str();
+ }
+ else if (boost::regex_match(param, what, stop_exp))
+ {
+ javascript = (boost::wformat(L"stop()")).str();
+ }
+ else if (boost::regex_match(param, what, next_exp))
+ {
+ javascript = (boost::wformat(L"next()")).str();
+ }
+ else if (boost::regex_match(param, what, remove_exp))
+ {
+ client_->remove();
+ return wrap_as_future(std::wstring(L""));
+ }
+ else if (boost::regex_match(param, what, update_exp))
+ {
+ javascript = (boost::wformat(L"update(\"%1%\")") % boost::algorithm::replace_all_copy(boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \"")), "\"", "\\\"")).str();
+ }
+ else if (boost::regex_match(param, what, invoke_exp))
+ {
+ javascript = (boost::wformat(L"%1%()") % boost::algorithm::trim_copy_if(what["VALUE"].str(), boost::is_any_of(" \""))).str();
+ }
- task();
+ client_->execute_javascript(javascript);
- return task.get_future();
+ return wrap_as_future(std::wstring(L""));
}
std::wstring print() const override