high_priority_class,\r
above_normal_priority_class,\r
normal_priority_class,\r
- below_normal_priority_clas\r
+ below_normal_priority_class\r
};\r
\r
namespace internal\r
SetThreadPriority(GetCurrentThread(), ABOVE_NORMAL_PRIORITY_CLASS);\r
else if(p == normal_priority_class)\r
SetThreadPriority(GetCurrentThread(), NORMAL_PRIORITY_CLASS);\r
- else if(p == below_normal_priority_clas)\r
+ else if(p == below_normal_priority_class)\r
SetThreadPriority(GetCurrentThread(), BELOW_NORMAL_PRIORITY_CLASS);\r
});\r
}\r
namespace caspar { namespace core {\r
\r
struct channel::implementation : boost::noncopyable\r
-{ \r
- const int index_;\r
- video_format_desc format_desc_;\r
+{\r
+ const int index_;\r
+ video_format_desc format_desc_;\r
\r
- safe_ptr<ogl_device> ogl_;\r
+ ogl_device& ogl_;\r
+ executor context_;\r
+ executor destroy_context_;\r
\r
std::shared_ptr<frame_consumer_device> consumer_;\r
std::shared_ptr<frame_mixer_device> mixer_;\r
std::shared_ptr<frame_producer_device> producer_;\r
\r
public:\r
- implementation(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
+ implementation(int index, const video_format_desc& format_desc, ogl_device& ogl) \r
: index_(index)\r
, format_desc_(format_desc)\r
, ogl_(ogl)\r
- , consumer_(new frame_consumer_device(format_desc, ogl))\r
- , mixer_(new frame_mixer_device(format_desc, [=](const safe_ptr<read_frame>& frame){consumer_->send(frame);}, ogl))\r
- , producer_(new frame_producer_device(format_desc_, [=](const std::map<int, safe_ptr<basic_frame>>& frames){mixer_->send(frames);})) \r
-\r
+ , consumer_(new frame_consumer_device(context_, format_desc, ogl))\r
+ , mixer_(new frame_mixer_device(context_, format_desc, [=](const safe_ptr<read_frame>& frame){consumer_->send(frame);}, ogl))\r
+ , producer_(new frame_producer_device(context_, destroy_context_, format_desc_, [=](const std::map<int, safe_ptr<basic_frame>>& frames){mixer_->send(frames);})) \r
+ , context_(print() + L"/render")\r
+ , destroy_context_(print() + L"/destroy")\r
{\r
+ context_.set_priority_class(above_normal_priority_class);\r
+ destroy_context_.set_priority_class(below_normal_priority_class);\r
CASPAR_LOG(info) << print() << " Successfully Initialized.";\r
}\r
+\r
+ ~implementation()\r
+ {\r
+ // Stop context before destroying devices.\r
+ context_.stop();\r
+ context_.join();\r
+ }\r
\r
std::wstring print() const\r
{\r
mixer_.reset();\r
\r
consumer_->set_video_format_desc(format_desc_);\r
- mixer_ = std::make_shared<frame_mixer_device>(format_desc_, [=](const safe_ptr<read_frame>& frame){consumer_->send(frame);}, ogl_);\r
- producer_ = std::make_shared<frame_producer_device>(format_desc_, [=](const std::map<int, safe_ptr<basic_frame>>& frames){mixer_->send(frames);});\r
+ mixer_ = std::make_shared<frame_mixer_device>(context_, format_desc_, [=](const safe_ptr<read_frame>& frame){consumer_->send(frame);}, ogl_);\r
+ producer_ = std::make_shared<frame_producer_device>(context_, destroy_context_, format_desc_, [=](const std::map<int, safe_ptr<basic_frame>>& frames){mixer_->send(frames);});\r
}\r
};\r
\r
-channel::channel(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) : impl_(new implementation(index, format_desc, ogl)){}\r
+channel::channel(int index, const video_format_desc& format_desc, ogl_device& ogl) : impl_(new implementation(index, format_desc, ogl)){}\r
channel::channel(channel&& other) : impl_(std::move(other.impl_)){}\r
safe_ptr<frame_producer_device> channel::producer() { return make_safe(impl_->producer_);} \r
safe_ptr<frame_mixer_device> channel::mixer() { return make_safe(impl_->mixer_);} \r
class channel : boost::noncopyable\r
{\r
public:\r
- explicit channel(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl);\r
+ explicit channel(int index, const video_format_desc& format_desc, ogl_device& ogl);\r
channel(channel&& other);\r
\r
safe_ptr<frame_producer_device> producer();\r
boost::timer frame_timer_;\r
boost::timer tick_timer_;\r
\r
- safe_ptr<ogl_device> ogl_;\r
+ ogl_device& ogl_;\r
\r
- executor executor_; \r
+ executor& context_; \r
public:\r
- implementation( const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
+ implementation(executor& context, const video_format_desc& format_desc, ogl_device& ogl) \r
: format_desc_(format_desc)\r
, diag_(diagnostics::create_graph(std::string("frame_consumer_device")))\r
, ogl_(ogl)\r
- , executor_(L"frame_consumer_device")\r
+ , context_(context)\r
{ \r
diag_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
diag_->add_guide("frame-time", 0.5f); \r
diag_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
diag_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f));\r
\r
- executor_.set_capacity(1);\r
- executor_.set_priority_class(above_normal_priority_class);\r
+ context_.set_priority_class(above_normal_priority_class);\r
}\r
\r
void add(int index, safe_ptr<frame_consumer>&& consumer)\r
{ \r
consumer->initialize(format_desc_);\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{\r
buffer_.set_capacity(std::max(buffer_.capacity(), consumer->buffer_depth()));\r
\r
\r
void remove(int index)\r
{\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{\r
auto it = consumers_.find(index);\r
if(it != consumers_.end())\r
if(has_key_only)\r
{\r
// Currently do key_only transform on cpu. Unsure if the extra 400MB/s (1080p50) overhead is worth it to do it on gpu.\r
- auto key_data = ogl_->create_host_buffer(frame->image_data().size(), host_buffer::write_only); \r
+ auto key_data = ogl_.create_host_buffer(frame->image_data().size(), host_buffer::write_only); \r
fast_memsfhl(key_data->data(), frame->image_data().begin(), frame->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
std::vector<int16_t> audio_data(frame->audio_data().begin(), frame->audio_data().end());\r
return make_safe<read_frame>(std::move(key_data), std::move(audio_data));\r
\r
void send(const safe_ptr<read_frame>& frame)\r
{ \r
- executor_.begin_invoke([=]\r
+ context_.invoke([=]\r
{\r
if(!has_synchronization_clock())\r
timer_.tick(1.0/format_desc_.fps);\r
\r
- diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
+ diag_->set_value("input-buffer", static_cast<float>(context_.size())/static_cast<float>(context_.capacity()));\r
frame_timer_.restart();\r
\r
buffer_.push_back(std::make_pair(frame, get_key_frame(frame)));\r
diag_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
tick_timer_.restart();\r
});\r
- diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
+ diag_->set_value("input-buffer", static_cast<float>(context_.size())/static_cast<float>(context_.capacity()));\r
}\r
\r
void set_video_format_desc(const video_format_desc& format_desc)\r
{\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{\r
format_desc_ = format_desc;\r
buffer_.clear();\r
}\r
};\r
\r
-frame_consumer_device::frame_consumer_device(const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
- : impl_(new implementation(format_desc, ogl)){}\r
+frame_consumer_device::frame_consumer_device(executor& context, const video_format_desc& format_desc, ogl_device& ogl) \r
+ : impl_(new implementation(context, format_desc, ogl)){}\r
void frame_consumer_device::add(int index, safe_ptr<frame_consumer>&& consumer){impl_->add(index, std::move(consumer));}\r
void frame_consumer_device::remove(int index){impl_->remove(index);}\r
void frame_consumer_device::send(const safe_ptr<read_frame>& future_frame) { impl_->send(future_frame); }\r
\r
#include <boost/noncopyable.hpp>\r
\r
-namespace caspar { namespace core {\r
+namespace caspar { \r
+ \r
+class executor;\r
+ \r
+namespace core {\r
\r
class basic_frame;\r
struct video_format_desc;\r
class frame_consumer_device : boost::noncopyable\r
{\r
public:\r
- explicit frame_consumer_device(const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl);\r
+ explicit frame_consumer_device(executor& context, const video_format_desc& format_desc, ogl_device& ogl);\r
\r
void add(int index, safe_ptr<frame_consumer>&& consumer);\r
void remove(int index);\r
<ClInclude Include="mixer\image\image_mixer.h" />\r
<ClInclude Include="mixer\read_frame.h" />\r
<ClInclude Include="mixer\write_frame.h" />\r
- <ClInclude Include="producer\destroy_producer_proxy.h" />\r
<ClInclude Include="producer\color\color_producer.h" />\r
<ClInclude Include="producer\frame\audio_transform.h" />\r
<ClInclude Include="producer\frame\basic_frame.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
- <ClCompile Include="producer\destroy_producer_proxy.cpp">\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- </ClCompile>\r
<ClCompile Include="producer\color\color_producer.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<ClInclude Include="mixer\frame_mixer_device.h">\r
<Filter>mixer</Filter>\r
</ClInclude>\r
- <ClInclude Include="producer\destroy_producer_proxy.h">\r
- <Filter>producer</Filter>\r
- </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<ClCompile Include="mixer\write_frame.cpp">\r
<Filter>mixer</Filter>\r
</ClCompile>\r
- <ClCompile Include="producer\destroy_producer_proxy.cpp">\r
- <Filter>producer</Filter>\r
- </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
\r
boost::fusion::map<boost::fusion::pair<core::image_transform, tweened_transform<core::image_transform>>,\r
boost::fusion::pair<core::audio_transform, tweened_transform<core::audio_transform>>> root_transforms_;\r
-\r
- safe_ptr<ogl_device> ogl_;\r
-\r
- executor executor_;\r
+ \r
+ executor& context_;\r
public:\r
- implementation(const core::video_format_desc& format_desc, const output_t& output, const safe_ptr<ogl_device>& ogl) \r
+ implementation(executor& context, const core::video_format_desc& format_desc, const output_t& output, ogl_device& ogl) \r
: format_desc_(format_desc)\r
, diag_(diagnostics::create_graph(narrow(print())))\r
, image_mixer_(format_desc, ogl)\r
, output_(output)\r
- , ogl_(ogl)\r
- , executor_(L"frame_mixer_device")\r
+ , context_(context)\r
{\r
diag_->add_guide("frame-time", 0.5f); \r
diag_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
diag_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f));\r
diag_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
- executor_.set_capacity(1); \r
- executor_.set_priority_class(above_normal_priority_class);\r
-\r
+ \r
CASPAR_LOG(info) << print() << L" Successfully initialized."; \r
}\r
\r
\r
void send(const std::map<int, safe_ptr<core::basic_frame>>& frames)\r
{ \r
- executor_.invoke([=]\r
+ context_.invoke([=]\r
{ \r
- diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity())); \r
+ diag_->set_value("input-buffer", static_cast<float>(context_.size())/static_cast<float>(context_.capacity())); \r
frame_timer_.restart();\r
\r
auto image = mix_image(frames);\r
auto audio = mix_audio(frames);\r
\r
- diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));\r
+ diag_->update_value("frame-time", frame_timer_.elapsed()*format_desc_.fps*0.5);\r
\r
output_(make_safe<read_frame>(std::move(image), std::move(audio)));\r
\r
- diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()*format_desc_.fps*0.5));\r
+ diag_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
tick_timer_.restart();\r
});\r
- diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
+ diag_->set_value("input-buffer", static_cast<float>(context_.size())/static_cast<float>(context_.capacity()));\r
}\r
\r
safe_ptr<core::write_frame> create_frame(void* tag, const core::pixel_format_desc& desc)\r
template<typename T> \r
void set_transform(const T& transform, unsigned int mix_duration, const std::wstring& tween)\r
{\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{\r
auto& root = boost::fusion::at_key<T>(root_transforms_);\r
\r
template<typename T>\r
void set_transform(int index, const T& transform, unsigned int mix_duration, const std::wstring& tween)\r
{\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{\r
auto& transforms = boost::fusion::at_key<T>(transforms_);\r
\r
template<typename T>\r
void apply_transform(const std::function<T(const T&)>& transform, unsigned int mix_duration, const std::wstring& tween)\r
{\r
- return executor_.invoke([&]\r
+ return context_.invoke([&]\r
{\r
auto& root = boost::fusion::at_key<T>(root_transforms_);\r
\r
template<typename T>\r
void apply_transform(int index, const std::function<T(T)>& transform, unsigned int mix_duration, const std::wstring& tween)\r
{\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{\r
auto& transforms = boost::fusion::at_key<T>(transforms_);\r
\r
template<typename T>\r
void reset_transform(unsigned int mix_duration, const std::wstring& tween)\r
{\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{\r
auto& transforms = boost::fusion::at_key<T>(transforms_);\r
\r
template<typename T>\r
void reset_transform(int index, unsigned int mix_duration, const std::wstring& tween)\r
{\r
- executor_.invoke([&]\r
+ context_.invoke([&]\r
{ \r
set_transform(T(), mix_duration, tween);\r
});\r
}\r
};\r
\r
-frame_mixer_device::frame_mixer_device(const core::video_format_desc& format_desc, const output_t& output, const safe_ptr<ogl_device>& ogl)\r
- : impl_(new implementation(format_desc, output, ogl)){}\r
-frame_mixer_device::frame_mixer_device(frame_mixer_device&& other) : impl_(std::move(other.impl_)){}\r
+frame_mixer_device::frame_mixer_device(executor& context, const core::video_format_desc& format_desc, const output_t& output, ogl_device& ogl)\r
+ : impl_(new implementation(context, format_desc, output, ogl)){}\r
void frame_mixer_device::send(const std::map<int, safe_ptr<core::basic_frame>>& frames){impl_->send(frames);}\r
const core::video_format_desc& frame_mixer_device::get_video_format_desc() const { return impl_->format_desc_; }\r
safe_ptr<core::write_frame> frame_mixer_device::create_frame(void* tag, const core::pixel_format_desc& desc){ return impl_->create_frame(tag, desc); } \r
#include <map>\r
\r
namespace caspar { \r
+\r
+class executor;\r
\r
namespace core {\r
\r
public: \r
typedef std::function<void(const safe_ptr<core::read_frame>&)> output_t;\r
\r
- frame_mixer_device(const core::video_format_desc& format_desc, const output_t& output, const safe_ptr<ogl_device>& ogl);\r
- frame_mixer_device(frame_mixer_device&& other); // nothrow\r
+ frame_mixer_device(executor& context, const core::video_format_desc& format_desc, const output_t& output, ogl_device& ogl);\r
\r
void send(const std::map<int, safe_ptr<core::basic_frame>>& frames); // nothrow\r
\r
#include <tbb/concurrent_unordered_map.h>\r
#include <tbb/concurrent_queue.h>\r
\r
+#include <boost/noncopyable.hpp>\r
#include <boost/thread/future.hpp>\r
\r
#include <array>\r
\r
namespace caspar { namespace core {\r
\r
-class ogl_device\r
+class ogl_device : boost::noncopyable\r
{ \r
std::unique_ptr<sf::Context> context_;\r
\r
\r
image_kernel kernel_;\r
\r
- safe_ptr<ogl_device> ogl_;\r
+ ogl_device& ogl_;\r
\r
safe_ptr<host_buffer> read_buffer_;\r
safe_ptr<device_buffer> draw_buffer_;\r
bool layer_key_;\r
\r
public:\r
- implementation(const core::video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
+ implementation(const core::video_format_desc& format_desc, ogl_device& ogl) \r
: format_desc_(format_desc)\r
, ogl_(ogl)\r
- , read_buffer_(ogl_->create_host_buffer(format_desc_.size, host_buffer::read_only))\r
- , draw_buffer_(ogl_->create_device_buffer(format_desc.width, format_desc.height, 4))\r
- , write_buffer_ (ogl_->create_device_buffer(format_desc.width, format_desc.height, 4))\r
- , local_key_buffer_(ogl_->create_device_buffer(format_desc.width, format_desc.height, 1))\r
- , layer_key_buffer_(ogl_->create_device_buffer(format_desc.width, format_desc.height, 1))\r
+ , read_buffer_(ogl_.create_host_buffer(format_desc_.size, host_buffer::read_only))\r
+ , draw_buffer_(ogl_.create_device_buffer(format_desc.width, format_desc.height, 4))\r
+ , write_buffer_ (ogl_.create_device_buffer(format_desc.width, format_desc.height, 4))\r
+ , local_key_buffer_(ogl_.create_device_buffer(format_desc.width, format_desc.height, 1))\r
+ , layer_key_buffer_(ogl_.create_device_buffer(format_desc.width, format_desc.height, 1))\r
, local_key_(false)\r
, layer_key_(false)\r
{\r
boost::unique_future<safe_ptr<host_buffer>> render()\r
{ \r
auto read_buffer = read_buffer_;\r
- auto result = ogl_->begin_invoke([=]() -> safe_ptr<host_buffer>\r
+ auto result = ogl_.begin_invoke([=]() -> safe_ptr<host_buffer>\r
{\r
read_buffer->map();\r
return read_buffer;\r
\r
auto render_queue = std::move(render_queue_);\r
\r
- ogl_->begin_invoke([=]() mutable\r
+ ogl_.begin_invoke([=]() mutable\r
{\r
local_key_ = false;\r
layer_key_ = false;\r
{\r
draw(layer.front());\r
layer.pop();\r
- ogl_->yield(); // Allow quick buffer allocation to execute.\r
+ ogl_.yield(); // Allow quick buffer allocation to execute.\r
}\r
\r
layer_key_ = local_key_; // If there was only key in last layer then use it as key for the entire next layer.\r
std::swap(draw_buffer_, write_buffer_);\r
\r
// Start transfer from device to host. \r
- read_buffer_ = ogl_->create_host_buffer(format_desc_.size, host_buffer::read_only); \r
+ read_buffer_ = ogl_.create_host_buffer(format_desc_.size, host_buffer::read_only); \r
write_buffer_->write(*read_buffer_);\r
});\r
\r
\r
safe_ptr<write_frame> create_frame(void* tag, const core::pixel_format_desc& desc)\r
{\r
- std::vector<safe_ptr<host_buffer>> buffers;\r
- std::vector<safe_ptr<device_buffer>> textures;\r
- ogl_->invoke([&]\r
- {\r
- std::transform(desc.planes.begin(), desc.planes.end(), std::back_inserter(buffers), [&](const core::pixel_format_desc::plane& plane)\r
- {\r
- return ogl_->create_host_buffer(plane.size, host_buffer::write_only);\r
- });\r
- std::transform(desc.planes.begin(), desc.planes.end(), std::back_inserter(textures), [&](const core::pixel_format_desc::plane& plane)\r
- {\r
- return ogl_->create_device_buffer(plane.width, plane.height, plane.channels);\r
- });\r
- });\r
-\r
- return make_safe<write_frame>(reinterpret_cast<int>(tag), desc, buffers, textures, ogl_);\r
+ return make_safe<write_frame>(ogl_, reinterpret_cast<int>(tag), desc);\r
}\r
};\r
\r
-image_mixer::image_mixer(const core::video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) : impl_(new implementation(format_desc, ogl)){}\r
+image_mixer::image_mixer(const core::video_format_desc& format_desc, ogl_device& ogl) : impl_(new implementation(format_desc, ogl)){}\r
void image_mixer::begin(const core::basic_frame& frame){impl_->begin(frame);}\r
void image_mixer::visit(core::write_frame& frame){impl_->visit(frame);}\r
void image_mixer::end(){impl_->end();}\r
*/\r
#pragma once\r
\r
-#include "../write_frame.h"\r
-\r
#include <common/memory/safe_ptr.h>\r
\r
#include <core/video_format.h>\r
#include <core/producer/frame/frame_visitor.h>\r
#include <core/producer/frame/pixel_format.h>\r
\r
-#include "../gpu/host_buffer.h"\r
-\r
#include <boost/noncopyable.hpp>\r
#include <boost/thread/future.hpp>\r
\r
-#include <vector>\r
-\r
namespace caspar { namespace core {\r
- \r
+\r
+class write_frame;\r
+class host_buffer;\r
class ogl_device;\r
\r
class image_mixer : public core::frame_visitor, boost::noncopyable\r
{\r
public:\r
- image_mixer(const core::video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl);\r
+ image_mixer(const core::video_format_desc& format_desc, ogl_device& ogl);\r
\r
virtual void begin(const core::basic_frame& frame);\r
virtual void visit(core::write_frame& frame);\r
\r
struct write_frame::implementation : boost::noncopyable\r
{ \r
- safe_ptr<ogl_device> ogl_;\r
- std::vector<safe_ptr<host_buffer>> buffers_;\r
- std::vector<safe_ptr<device_buffer>> textures_;\r
- std::vector<int16_t> audio_data_;\r
- const core::pixel_format_desc desc_;\r
- int32_t tag_;\r
+ ogl_device& ogl_;\r
+ std::vector<safe_ptr<host_buffer>> buffers_;\r
+ std::vector<safe_ptr<device_buffer>> textures_;\r
+ std::vector<int16_t> audio_data_;\r
+ const core::pixel_format_desc desc_;\r
+ int32_t tag_;\r
\r
public:\r
- implementation(int32_t tag, const core::pixel_format_desc& desc, const std::vector<safe_ptr<host_buffer>>& buffers, const std::vector<safe_ptr<device_buffer>>& textures, const safe_ptr<ogl_device> ogl) \r
+ implementation(ogl_device& ogl, int32_t tag, const core::pixel_format_desc& desc) \r
: ogl_(ogl)\r
- , buffers_(buffers)\r
- , textures_(textures)\r
, desc_(desc)\r
, tag_(tag)\r
- {}\r
+ {\r
+ ogl_.invoke([&]\r
+ {\r
+ std::transform(desc.planes.begin(), desc.planes.end(), std::back_inserter(buffers_), [&](const core::pixel_format_desc::plane& plane)\r
+ {\r
+ return ogl_.create_host_buffer(plane.size, host_buffer::write_only);\r
+ });\r
+ std::transform(desc.planes.begin(), desc.planes.end(), std::back_inserter(textures_), [&](const core::pixel_format_desc::plane& plane)\r
+ {\r
+ return ogl_.create_device_buffer(plane.width, plane.height, plane.channels);\r
+ });\r
+ });\r
+ }\r
\r
void accept(write_frame& self, core::frame_visitor& visitor)\r
{\r
auto texture = textures_[plane_index];\r
auto buffer = std::move(buffers_[plane_index]); // Release buffer once done.\r
\r
- ogl_->begin_invoke([=]\r
+ ogl_.begin_invoke([=]\r
{\r
texture->read(*buffer);\r
});\r
}\r
};\r
\r
-write_frame::write_frame(int32_t tag, const core::pixel_format_desc& desc, const std::vector<safe_ptr<host_buffer>>& buffers, const std::vector<safe_ptr<device_buffer>>& textures, const safe_ptr<ogl_device>& ogl) \r
- : impl_(new implementation(tag, desc, buffers, textures, ogl)){}\r
+write_frame::write_frame(ogl_device& ogl, int32_t tag, const core::pixel_format_desc& desc) \r
+ : impl_(new implementation(ogl, tag, desc)){}\r
void write_frame::accept(core::frame_visitor& visitor){impl_->accept(*this, visitor);}\r
\r
boost::iterator_range<uint8_t*> write_frame::image_data(size_t index){return impl_->image_data(index);}\r
class write_frame : public core::basic_frame, boost::noncopyable\r
{\r
public: \r
- explicit write_frame(int tag, const core::pixel_format_desc& desc, const std::vector<safe_ptr<host_buffer>>& buffers, const std::vector<safe_ptr<device_buffer>>& textures, const safe_ptr<ogl_device>& ogl);\r
+ explicit write_frame(ogl_device& ogl, int tag, const core::pixel_format_desc& desc);\r
\r
virtual boost::iterator_range<uint8_t*> image_data(size_t plane_index = 0); \r
virtual std::vector<int16_t>& audio_data();\r
#include "frame_producer_device.h"\r
\r
#include "layer.h"\r
-#include "destroy_producer_proxy.h"\r
\r
#include <core/producer/frame/basic_frame.h>\r
#include <core/producer/frame/frame_factory.h>\r
#include <map>\r
\r
namespace caspar { namespace core {\r
+ \r
+void destroy_producer(safe_ptr<frame_producer>& producer)\r
+{\r
+ if(!producer.unique())\r
+ CASPAR_LOG(warning) << producer->print() << L" Not destroyed on safe asynchronous destruction thread.";\r
+ \r
+ producer = frame_producer::empty();\r
+}\r
+\r
+class destroy_producer_proxy : public frame_producer\r
+{\r
+ safe_ptr<frame_producer> producer_;\r
+ executor& destroy_context_;\r
+public:\r
+ destroy_producer_proxy(executor& destroy_context, const safe_ptr<frame_producer>& producer) \r
+ : producer_(producer)\r
+ , destroy_context_(destroy_context){}\r
+\r
+ ~destroy_producer_proxy()\r
+ { \r
+ if(destroy_context_.size() > 4)\r
+ CASPAR_LOG(error) << L" Potential destroyer deadlock.";\r
+\r
+ destroy_context_.begin_invoke(std::bind(&destroy_producer, std::move(producer_)));\r
+ }\r
+\r
+ virtual safe_ptr<basic_frame> receive() {return core::receive(producer_);}\r
+ virtual std::wstring print() const {return producer_->print();}\r
+ virtual void param(const std::wstring& str) {producer_->param(str);}\r
+ virtual safe_ptr<frame_producer> get_following_producer() const {return producer_->get_following_producer();}\r
+ virtual void set_leading_producer(const safe_ptr<frame_producer>& producer) {producer_->set_leading_producer(producer);}\r
+};\r
\r
struct frame_producer_device::implementation : boost::noncopyable\r
{ \r
boost::timer tick_timer_;\r
boost::timer output_timer_;\r
\r
- executor executor_;\r
+ executor& context_;\r
+ executor& destroy_context_;\r
public:\r
- implementation(const video_format_desc& format_desc, const output_t& output) \r
+ implementation(executor& context, executor& destroy_context, const video_format_desc& format_desc, const output_t& output) \r
: format_desc_(format_desc)\r
, diag_(diagnostics::create_graph(std::string("frame_producer_device")))\r
- , executor_(L"frame_producer_device")\r
+ , context_(context)\r
+ , destroy_context_(destroy_context)\r
, output_(output)\r
{\r
diag_->add_guide("frame-time", 0.5f); \r
diag_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f));\r
diag_->set_color("output-time", diagnostics::color(0.5f, 1.0f, 0.2f));\r
\r
- executor_.set_priority_class(above_normal_priority_class);\r
- executor_.begin_invoke([=]{tick();}); \r
+ context_.begin_invoke([=]{tick();}); \r
}\r
\r
void tick()\r
CASPAR_LOG_CURRENT_EXCEPTION();\r
}\r
\r
- executor_.begin_invoke([=]{tick();});\r
+ context_.begin_invoke([=]{tick();});\r
}\r
\r
std::map<int, safe_ptr<basic_frame>> render()\r
\r
void load(int index, const safe_ptr<frame_producer>& producer, bool preview)\r
{\r
- executor_.invoke([&]{layers_[index].load(make_safe<destroy_producer_proxy>(producer), preview);});\r
+ context_.invoke([&]{layers_[index].load(make_safe<destroy_producer_proxy>(destroy_context_, producer), preview);});\r
}\r
\r
void pause(int index)\r
{ \r
- executor_.invoke([&]{layers_[index].pause();});\r
+ context_.invoke([&]{layers_[index].pause();});\r
}\r
\r
void play(int index)\r
{ \r
- executor_.invoke([&]{layers_[index].play();});\r
+ context_.invoke([&]{layers_[index].play();});\r
}\r
\r
void stop(int index)\r
{ \r
- executor_.invoke([&]{layers_[index].stop();});\r
+ context_.invoke([&]{layers_[index].stop();});\r
}\r
\r
void clear(int index)\r
{\r
- executor_.invoke([&]{layers_.erase(index);});\r
+ context_.invoke([&]{layers_.erase(index);});\r
}\r
\r
void clear()\r
{\r
- executor_.invoke([&]{layers_.clear();});\r
+ context_.invoke([&]{layers_.clear();});\r
} \r
\r
void swap_layer(int index, size_t other_index)\r
{\r
- executor_.invoke([&]{layers_[index].swap(layers_[other_index]);});\r
+ context_.invoke([&]{layers_[index].swap(layers_[other_index]);});\r
}\r
\r
void swap_layer(int index, size_t other_index, frame_producer_device& other)\r
\r
auto func = [&]{layers_[index].swap(other.impl_->layers_[other_index]);};\r
\r
- executor_.invoke([&]{other.impl_->executor_.invoke(func);});\r
+ context_.invoke([&]{other.impl_->context_.invoke(func);});\r
}\r
}\r
\r
}); \r
};\r
\r
- executor_.invoke([&]{other.impl_->executor_.invoke(func);});\r
+ context_.invoke([&]{other.impl_->context_.invoke(func);});\r
}\r
\r
boost::unique_future<safe_ptr<frame_producer>> foreground(int index)\r
{\r
- return executor_.begin_invoke([=]{return layers_[index].foreground();});\r
+ return context_.begin_invoke([=]{return layers_[index].foreground();});\r
}\r
\r
boost::unique_future<safe_ptr<frame_producer>> background(int index)\r
{\r
- return executor_.begin_invoke([=]{return layers_[index].background();});\r
+ return context_.begin_invoke([=]{return layers_[index].background();});\r
}\r
};\r
\r
-frame_producer_device::frame_producer_device(const video_format_desc& format_desc, const output_t& output) : impl_(new implementation(format_desc, output)){}\r
+frame_producer_device::frame_producer_device(executor& context, executor& destroy_context, const video_format_desc& format_desc, const output_t& output)\r
+ : impl_(new implementation(context, destroy_context, format_desc, output)){}\r
frame_producer_device::frame_producer_device(frame_producer_device&& other) : impl_(std::move(other.impl_)){}\r
void frame_producer_device::swap(frame_producer_device& other){impl_->swap(other);}\r
void frame_producer_device::load(int index, const safe_ptr<frame_producer>& producer, bool preview){impl_->load(index, producer, preview);}\r
\r
#include <functional>\r
\r
-namespace caspar { namespace core {\r
+namespace caspar { \r
+ \r
+class executor;\r
+\r
+namespace core {\r
\r
struct video_format_desc;\r
\r
public:\r
typedef std::function<void(const std::map<int, safe_ptr<basic_frame>>&)> output_t;\r
\r
- explicit frame_producer_device(const video_format_desc& format_desc, const output_t& output);\r
+ explicit frame_producer_device(executor& context, executor& destroy_context, const video_format_desc& format_desc, const output_t& output);\r
frame_producer_device(frame_producer_device&& other);\r
\r
void swap(frame_producer_device& other);\r
wcmd = L"MIXER 1 VIDEO GRID " + boost::lexical_cast<std::wstring>(n);\r
\r
for(int i = 1; i <= num; ++i)\r
- wcmd += L"\r\nPLAY 1-" + boost::lexical_cast<std::wstring>(i) + L" " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-" + boost::lexical_cast<std::wstring>(i) + L" " + file + L" LOOP";// + L" SLIDE 100 LOOP";\r
}\r
\r
wcmd += L"\r\n";\r
{\r
std::vector<safe_ptr<IO::AsyncEventServer>> async_servers_; \r
std::vector<safe_ptr<channel>> channels_;\r
- safe_ptr<ogl_device> ogl_;\r
+ ogl_device ogl_;\r
\r
implementation() \r
{ \r