<ClInclude Include="utility\safe_ptr.h" />\r
<ClInclude Include="utility\singleton_pool.h" />\r
<ClInclude Include="utility\string_convert.h" />\r
+ <ClInclude Include="utility\timer.h" />\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="concurrency\Thread.cpp">\r
<ClInclude Include="utility\singleton_pool.h">\r
<Filter>Source\utility</Filter>\r
</ClInclude>\r
+ <ClInclude Include="utility\timer.h">\r
+ <Filter>Source\utility</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <windows.h>\r
+\r
+namespace caspar {\r
+ \r
+class timer\r
+{\r
+public:\r
+ timer(int fps = 25) : fps_(fps)\r
+ {\r
+ QueryPerformanceFrequency(&freq_);\r
+ time_.QuadPart = 0;\r
+ }\r
+ \r
+ // Author: Ryan M. Geiss\r
+ // http://www.geisswerks.com/ryan/FAQS/timing.html\r
+ void wait()\r
+ { \r
+ LARGE_INTEGER t;\r
+ QueryPerformanceCounter(&t);\r
+\r
+ if (time_.QuadPart != 0)\r
+ {\r
+ int ticks_to_wait = static_cast<int>(freq_.QuadPart / fps_);\r
+ int done = 0;\r
+ do\r
+ {\r
+ QueryPerformanceCounter(&t);\r
+ \r
+ int ticks_passed = static_cast<int>(static_cast<__int64>(t.QuadPart) - static_cast<__int64>(time_.QuadPart));\r
+ int ticks_left = ticks_to_wait - ticks_passed;\r
+\r
+ if (t.QuadPart < time_.QuadPart) // time wrap\r
+ done = 1;\r
+ if (ticks_passed >= ticks_to_wait)\r
+ done = 1;\r
+ \r
+ if (!done)\r
+ {\r
+ // if > 0.002s left, do Sleep(1), which will actually sleep some \r
+ // steady amount, probably 1-2 ms,\r
+ // and do so in a nice way (cpu meter drops; laptop battery spared).\r
+ // otherwise, do a few Sleep(0)'s, which just give up the timeslice,\r
+ // but don't really save cpu or battery, but do pass a tiny\r
+ // amount of time.\r
+ if (ticks_left > static_cast<int>((freq_.QuadPart*2)/1000))\r
+ Sleep(1);\r
+ else \r
+ for (int i = 0; i < 10; ++i) \r
+ Sleep(0); // causes thread to give up its timeslice\r
+ }\r
+ }\r
+ while (!done); \r
+ }\r
+\r
+ time_ = t;\r
+ }\r
+private:\r
+ LARGE_INTEGER freq_;\r
+ LARGE_INTEGER time_;\r
+ int fps_;\r
+};\r
+\r
+}
\ No newline at end of file
\r
namespace caspar { namespace core {\r
\r
-class clock\r
-{\r
-public:\r
- clock(int fps = 25) : fps_(fps)\r
- {\r
- QueryPerformanceFrequency(&freq_);\r
- time_.QuadPart = 0;\r
- }\r
- \r
- // Author: Ryan M. Geiss\r
- // http://www.geisswerks.com/ryan/FAQS/timing.html\r
- void wait()\r
- { \r
- LARGE_INTEGER t;\r
- QueryPerformanceCounter(&t);\r
-\r
- if (time_.QuadPart != 0)\r
- {\r
- int ticks_to_wait = static_cast<int>(freq_.QuadPart / fps_);\r
- int done = 0;\r
- do\r
- {\r
- QueryPerformanceCounter(&t);\r
- \r
- int ticks_passed = static_cast<int>(static_cast<__int64>(t.QuadPart) - static_cast<__int64>(time_.QuadPart));\r
- int ticks_left = ticks_to_wait - ticks_passed;\r
-\r
- if (t.QuadPart < time_.QuadPart) // time wrap\r
- done = 1;\r
- if (ticks_passed >= ticks_to_wait)\r
- done = 1;\r
- \r
- if (!done)\r
- {\r
- // if > 0.002s left, do Sleep(1), which will actually sleep some \r
- // steady amount, probably 1-2 ms,\r
- // and do so in a nice way (cpu meter drops; laptop battery spared).\r
- // otherwise, do a few Sleep(0)'s, which just give up the timeslice,\r
- // but don't really save cpu or battery, but do pass a tiny\r
- // amount of time.\r
- if (ticks_left > static_cast<int>((freq_.QuadPart*2)/1000))\r
- Sleep(1);\r
- else \r
- for (int i = 0; i < 10; ++i) \r
- Sleep(0); // causes thread to give up its timeslice\r
- }\r
- }\r
- while (!done); \r
- }\r
-\r
- time_ = t;\r
- }\r
-private:\r
- LARGE_INTEGER freq_;\r
- LARGE_INTEGER time_;\r
- int fps_;\r
-};\r
-\r
struct channel::implementation : boost::noncopyable\r
{ \r
implementation(const video_format_desc& format_desc, const std::vector<safe_ptr<frame_consumer>>& consumers) \r
consumer_device_.consume(std::move(processed_frame));\r
\r
executor_.begin_invoke([=]{tick();});\r
-\r
- clock_.wait();\r
}\r
\r
safe_ptr<draw_frame> draw()\r
});\r
}\r
\r
- clock clock_;\r
-\r
mutable executor executor_;\r
\r
safe_ptr<frame_processor_device> processor_device_;\r
\r
struct frame_consumer : boost::noncopyable\r
{\r
+ enum sync_mode\r
+ {\r
+ ready = 0,\r
+ clock \r
+ };\r
+\r
virtual ~frame_consumer() {}\r
\r
virtual void send(const safe_ptr<const read_frame>& frame) = 0;\r
- virtual void synchronize() = 0;\r
+ virtual sync_mode synchronize() = 0;\r
virtual size_t buffer_depth() const = 0;\r
};\r
\r
#include "../format/video_format.h"\r
\r
#include <common/concurrency/executor.h>\r
+#include <common/utility/timer.h>\r
\r
#include <tbb/concurrent_queue.h>\r
#include <tbb/atomic.h>\r
void tick(const safe_ptr<const read_frame>& frame)\r
{\r
buffer_.push_back(frame);\r
-\r
+ \r
boost::range::for_each(consumers_, [&](const safe_ptr<frame_consumer>& consumer)\r
{\r
size_t offset = max_depth_ - consumer->buffer_depth();\r
consumer->send(*(buffer_.begin() + offset));\r
});\r
\r
+ frame_consumer::sync_mode sync = frame_consumer::ready;\r
boost::range::for_each(consumers_, [&](const safe_ptr<frame_consumer>& consumer)\r
{\r
try\r
if(offset >= buffer_.size())\r
return;\r
\r
- consumer->synchronize();\r
+ if(consumer->synchronize() == frame_consumer::clock)\r
+ sync = frame_consumer::clock;\r
}\r
catch(...)\r
{\r
}\r
});\r
\r
+ if(sync != frame_consumer::clock)\r
+ clock_.wait();\r
+\r
if(buffer_.size() >= max_depth_)\r
buffer_.pop_front();\r
}\r
executor_.invoke([=]{tick(frame);});\r
}\r
\r
+ timer clock_;\r
executor executor_; \r
\r
size_t max_depth_;\r
#include "../../processor/frame_processor_device.h"\r
\r
#include <common/concurrency/executor.h>\r
+#include <common/utility/timer.h>\r
\r
#include <boost/filesystem.hpp>\r
\r
\r
safe_ptr<draw_frame> render_frame()\r
{\r
+ if(ax_->IsEmpty())\r
+ return draw_frame::empty();\r
+\r
auto frame = render_simple_frame();\r
\r
auto running_fps = ax_->GetFPS();\r
\r
safe_ptr<draw_frame> render_simple_frame()\r
{\r
- if(!ax_->IsEmpty())\r
- ax_->Tick();\r
+ ax_->Tick();\r
\r
if(ax_->InvalidRect())\r
{ \r
::OleInitialize(nullptr);\r
});\r
\r
- frame_buffer_.set_capacity(2);\r
+ frame_buffer_.set_capacity(8);\r
while(frame_buffer_.try_push(draw_frame::empty())){}\r
}\r
\r
\r
executor_.begin_invoke([=]\r
{\r
+ clock_.wait(); // Use high precision timer to sync rendering. Flash has free-running timers which can get out of sync with tick.\r
+\r
auto frame = draw_frame::empty();\r
- try\r
- {\r
- if(renderer_)\r
- frame = renderer_->render_frame();\r
- }\r
- catch(...)\r
- {\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- renderer_ = nullptr;\r
- }\r
- frame_buffer_.try_push(frame);\r
+ do{frame = render_frame();} // Fill framebuffer when rendering empty frames.\r
+ while(frame_buffer_.try_push(frame) && frame == draw_frame::empty());\r
}); \r
\r
return tail_;\r
}\r
\r
+ safe_ptr<draw_frame> render_frame()\r
+ {\r
+ auto frame = draw_frame::empty();\r
+ try\r
+ {\r
+ if(renderer_)\r
+ frame = renderer_->render_frame();\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ renderer_ = nullptr;\r
+ }\r
+ return frame;\r
+ }\r
+\r
virtual void initialize(const safe_ptr<frame_processor_device>& frame_processor)\r
{\r
frame_processor_ = frame_processor;\r
}\r
});\r
}\r
+\r
+ timer clock_;\r
\r
safe_ptr<draw_frame> tail_;\r
tbb::concurrent_bounded_queue<safe_ptr<draw_frame>> frame_buffer_;\r
<stretch>uniform</stretch>\r
<windowed>true</windowed>\r
</ogl>\r
- <audio/>\r
+ <!--audio/-->\r
<!--decklink> \r
</decklink>\r
<bluefish>\r