\r
critical_section mutex_;\r
call<output::source_element_t> output_;\r
+\r
+ boost::circular_buffer<safe_ptr<read_frame>> frames_;\r
\r
public:\r
implementation(output::source_t& source, const video_format_desc& format_desc) \r
}\r
}\r
}\r
+\r
+ std::pair<size_t, size_t> minmax_buffer_depth() const\r
+ { \r
+ if(consumers_.empty())\r
+ return std::make_pair(0, 0);\r
+ std::vector<size_t> buffer_depths;\r
+ std::transform(consumers_.begin(), consumers_.end(), std::back_inserter(buffer_depths), [](const decltype(*consumers_.begin())& pair)\r
+ {\r
+ return pair.second->buffer_depth();\r
+ });\r
+ std::sort(buffer_depths.begin(), buffer_depths.end());\r
+ auto min = buffer_depths.front();\r
+ auto max = buffer_depths.back();\r
+ return std::make_pair(min, max);\r
+ }\r
\r
void execute(const output::source_element_t& element)\r
{ \r
\r
{\r
critical_section::scoped_lock lock(mutex_); \r
-\r
+ \r
if(!has_synchronization_clock() || frame->image_size() != format_desc_.size)\r
{ \r
scoped_oversubcription_token oversubscribe;\r
timer_.tick(1.0/format_desc_.fps);\r
}\r
- \r
+ \r
+ auto minmax = minmax_buffer_depth();\r
+\r
+ frames_.set_capacity(minmax.second - minmax.first + 1);\r
+ frames_.push_back(frame);\r
+\r
+ if(!frames_.full())\r
+ return;\r
+\r
std::vector<int> removables; \r
Concurrency::parallel_for_each(consumers_.begin(), consumers_.end(), [&](const decltype(*consumers_.begin())& pair)\r
{ \r
try\r
{\r
- if(!pair.second->send(frame))\r
+ auto consumer = pair.second;\r
+\r
+ if(!consumer->send(frames_.at(consumer->buffer_depth()-minmax.first)))\r
removables.push_back(pair.first);\r
}\r
catch(...)\r
return template_host;\r
}\r
\r
-class flash_renderer\r
-{ \r
- const std::wstring filename_;\r
-\r
- const safe_ptr<core::frame_factory> frame_factory_;\r
- safe_ptr<diagnostics::graph> graph_;\r
-\r
- high_prec_timer timer_;\r
- safe_ptr<core::basic_frame> head_;\r
- bitmap bmp_;\r
-\r
- const size_t width_;\r
- const size_t height_;\r
- \r
- boost::timer frame_timer_;\r
- boost::timer tick_timer_;\r
-\r
+class flash_player\r
+{\r
+ const std::wstring filename_;\r
+ const size_t width_;\r
+ const size_t height_;\r
CComObject<caspar::flash::FlashAxContainer>* ax_;\r
- \r
public:\r
- flash_renderer(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, int width, int height) \r
+ flash_player(const std::wstring& filename, size_t width, size_t height)\r
: filename_(filename)\r
- , frame_factory_(frame_factory)\r
- , graph_(graph)\r
- , ax_(nullptr)\r
- , head_(core::basic_frame::empty())\r
- , bmp_(width, height)\r
, width_(width)\r
, height_(height)\r
{ \r
- graph_->add_guide("frame-time", 0.5f);\r
- graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));\r
- graph_->add_guide("tick-time", 0.5);\r
- graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));\r
- graph_->set_color("param", diagnostics::color(1.0f, 0.5f, 0.0f)); \r
- \r
if(FAILED(CComObject<caspar::flash::FlashAxContainer>::CreateInstance(&ax_)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to create FlashAxContainer"));\r
\r
if(FAILED(spFlash->put_ScaleMode(2))) //Exact fit. Scale without respect to the aspect ratio.\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to Set Scale Mode"));\r
\r
- ax_->SetSize(width_, height_); \r
- \r
- CASPAR_LOG(info) << print() << L" Thread started.";\r
- CASPAR_LOG(info) << print() << L" Successfully initialized with template-host: " << filename << L" width: " << width_ << L" height: " << height_ << L".";\r
+ ax_->SetSize(width, height); \r
+\r
+ CASPAR_LOG(info) << "Initialized" << print();\r
}\r
\r
- ~flash_renderer()\r
- { \r
+ ~flash_player()\r
+ {\r
if(ax_)\r
{\r
ax_->DestroyAxControl();\r
ax_->Release();\r
}\r
- CASPAR_LOG(info) << print() << L" Thread ended.";\r
+ CASPAR_LOG(info) << "Uninitialized " << print();\r
+ }\r
+\r
+ bool is_empty() const\r
+ {\r
+ return ax_->IsEmpty();\r
}\r
+\r
+ void tick()\r
+ {\r
+ ax_->Tick();\r
+ }\r
+\r
+ bool invalid_rect() const\r
+ {\r
+ return ax_->InvalidRect();\r
+ }\r
+\r
+ bool draw_control(HDC targetDC)\r
+ {\r
+ return ax_->DrawControl(targetDC);\r
+ }\r
+\r
+ bool flash_call(const std::wstring& param)\r
+ {\r
+ return ax_->FlashCall(param);\r
+ }\r
+\r
+ double fps() const\r
+ {\r
+ return ax_->GetFPS();\r
+ }\r
+\r
+ std::wstring print()\r
+ {\r
+ return L"flash-player[" + boost::filesystem::wpath(filename_).filename() + L"|" + boost::lexical_cast<std::wstring>(width_) + L"|" + boost::lexical_cast<std::wstring>(height_) + L"]"; \r
+ }\r
+};\r
+\r
+class flash_renderer\r
+{ \r
+ const std::wstring filename_;\r
+\r
+ const safe_ptr<core::frame_factory> frame_factory_;\r
+ safe_ptr<diagnostics::graph> graph_;\r
+\r
+ high_prec_timer timer_;\r
+ safe_ptr<core::basic_frame> head_;\r
+ bitmap bmp_;\r
+\r
+ const size_t width_;\r
+ const size_t height_;\r
+ \r
+ boost::timer frame_timer_;\r
+ boost::timer tick_timer_;\r
+\r
+ std::unique_ptr<flash_player> player_;\r
\r
+public:\r
+ flash_renderer(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, int width, int height) \r
+ : filename_(filename)\r
+ , frame_factory_(frame_factory)\r
+ , graph_(graph)\r
+ , head_(core::basic_frame::empty())\r
+ , bmp_(width, height)\r
+ , width_(width)\r
+ , height_(height)\r
+ { \r
+ graph_->add_guide("frame-time", 0.5f);\r
+ graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));\r
+ graph_->add_guide("tick-time", 0.5);\r
+ graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));\r
+ graph_->set_color("param", diagnostics::color(1.0f, 0.5f, 0.0f)); \r
+ }\r
+ \r
bool param(const std::wstring& param)\r
{ \r
- bool success = ax_->FlashCall(param); \r
+ if(!player_)\r
+ player_.reset(new flash_player(filename_, width_, height_));\r
+\r
+ bool success = player_->flash_call(param); \r
if(!success)\r
CASPAR_LOG(warning) << print() << L" Flash call failed:" << param;//BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Flash function call failed.") << arg_name_info("param") << arg_value_info(narrow(param)));\r
else\r
\r
safe_ptr<core::basic_frame> operator()()\r
{\r
- const float frame_time = 1.0f/ax_->GetFPS();\r
+ if(player_ && player_->is_empty())\r
+ player_.reset();\r
+\r
+ if(!player_)\r
+ {\r
+ graph_->update_value("tick-time", 0.0);\r
+ return core::basic_frame::empty(); \r
+ }\r
+\r
+ const float frame_time = 1.0f/player_->fps();\r
\r
graph_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()/frame_time)*0.5f);\r
tick_timer_.restart();\r
-\r
- if(ax_->IsEmpty())\r
- return core::basic_frame::empty(); \r
\r
Concurrency::scoped_oversubcription_token oversubscribe;\r
timer_.tick(frame_time); // This will block the thread.\r
\r
frame_timer_.restart();\r
\r
- ax_->Tick();\r
- if(ax_->InvalidRect())\r
+ player_->tick();\r
+ if(player_->invalid_rect())\r
{ \r
fast_memclr(bmp_.data(), width_*height_*4);\r
- ax_->DrawControl(bmp_);\r
+ player_->draw_control(bmp_);\r
\r
auto frame = frame_factory_->create_frame(this, width_, height_);\r
fast_memcpy(frame->image_data().begin(), bmp_.data(), width_*height_*4);\r
\r
double fps() const\r
{\r
- return ax_->GetFPS(); \r
+ return player_ ? player_->fps() : 0.0; \r
}\r
\r
std::wstring print()\r