<ClInclude Include="os\windows\system_info.h" />\r
<ClInclude Include="stdafx.h" />\r
<ClInclude Include="utility\assert.h" />\r
+ <ClInclude Include="utility\move_on_copy.h" />\r
<ClInclude Include="utility\string.h" />\r
<ClInclude Include="utility\timer.h" />\r
<ClInclude Include="utility\tweener.h" />\r
<ClInclude Include="env.h">\r
<Filter>source</Filter>\r
</ClInclude>\r
+ <ClInclude Include="utility\move_on_copy.h">\r
+ <Filter>source\utility</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
\r
#include "../exception/win32_exception.h"\r
#include "../utility/string.h"\r
+#include "../utility/move_on_copy.h"\r
#include "../log/log.h"\r
\r
#include <tbb/atomic.h>\r
below_normal_priority_class\r
};\r
\r
-namespace internal\r
-{\r
- template<typename T>\r
- struct move_on_copy\r
- {\r
- move_on_copy(const move_on_copy<T>& other) : value(std::move(other.value)){}\r
- move_on_copy(T&& value) : value(std::move(value)){}\r
- mutable T value;\r
- };\r
-\r
- template<typename T>\r
- move_on_copy<T> make_move_on_copy(T&& value)\r
- {\r
- return move_on_copy<T>(std::move(value));\r
- }\r
-}\r
-\r
class executor : boost::noncopyable\r
{\r
const std::string name_;\r
virtual ~executor() // noexcept\r
{\r
stop();\r
+ execution_queue_[normal_priority].try_push([]{}); // Wake the execution thread.\r
join();\r
}\r
\r
void stop() // noexcept\r
{\r
is_running_ = false; \r
- execution_queue_[normal_priority].try_push([]{}); // Wake the execution thread.\r
}\r
\r
void wait() // noexcept\r
auto begin_invoke(Func&& func, task_priority priority = normal_priority) -> boost::unique_future<decltype(func())> // noexcept\r
{ \r
// Create a move on copy adaptor to avoid copying the functor into the queue, tbb::concurrent_queue does not support move semantics.\r
- auto task_adaptor = internal::make_move_on_copy(create_task(func));\r
+ auto task_adaptor = make_move_on_copy(create_task(func));\r
\r
auto future = task_adaptor.value.get_future();\r
\r
auto try_begin_invoke(Func&& func, task_priority priority = normal_priority) -> boost::unique_future<decltype(func())> // noexcept\r
{\r
// Create a move on copy adaptor to avoid copying the functor into the queue, tbb::concurrent_queue does not support move semantics.\r
- auto task_adaptor = internal::make_move_on_copy(create_task(func));\r
+ auto task_adaptor = make_move_on_copy(create_task(func));\r
\r
auto future = task_adaptor.value.get_future();\r
\r
--- /dev/null
+#pragma once\r
+\r
+namespace caspar {\r
+ \r
+template<typename T>\r
+struct move_on_copy\r
+{\r
+ move_on_copy(const move_on_copy<T>& other) : value(std::move(other.value)){}\r
+ move_on_copy(T&& value) : value(std::move(value)){}\r
+ mutable T value;\r
+};\r
+\r
+template<typename T>\r
+move_on_copy<T> make_move_on_copy(T&& value)\r
+{\r
+ return move_on_copy<T>(std::move(value));\r
+}\r
+\r
+}
\ No newline at end of file
std::vector<safe_ptr<device_buffer>> textures;\r
frame_transform transform;\r
blend_mode::type blend_mode;\r
+\r
+ render_item() : blend_mode(blend_mode::normal){}\r
};\r
\r
bool operator==(const render_item& lhs, const render_item& rhs);\r
\r
#include <common/exception/exceptions.h>\r
#include <common/gl/gl_check.h>\r
+#include <common/utility/move_on_copy.h>\r
\r
#include <core/producer/frame/frame_transform.h>\r
#include <core/producer/frame/pixel_format.h>\r
\r
namespace caspar { namespace core {\r
\r
-typedef std::deque<render_item> layer;\r
+struct layer\r
+{\r
+ std::vector<render_item> items;\r
+ blend_mode::type blend_mode;\r
+\r
+ layer(blend_mode::type blend_mode) : blend_mode(blend_mode)\r
+ {\r
+ }\r
+};\r
\r
class image_renderer\r
{\r
{\r
}\r
\r
- boost::unique_future<safe_ptr<host_buffer>> render(std::deque<layer>&& layers)\r
+ boost::unique_future<safe_ptr<host_buffer>> render(std::vector<layer>&& layers)\r
{ \r
- auto layers2 = std::move(layers);\r
- return channel_.ogl().begin_invoke([=]() mutable\r
+ auto layers2 = make_move_on_copy(std::move(layers));\r
+ return channel_.ogl().begin_invoke([=]\r
{\r
- return do_render(std::move(layers2));\r
+ return do_render(std::move(layers2.value));\r
});\r
}\r
\r
private:\r
- safe_ptr<host_buffer> do_render(std::deque<layer>&& layers)\r
+ safe_ptr<host_buffer> do_render(std::vector<layer>&& layers)\r
{\r
std::shared_ptr<device_buffer> layer_key_buffer;\r
\r
\r
void draw_layer(layer&& layer, const safe_ptr<device_buffer>& draw_buffer, std::shared_ptr<device_buffer>& layer_key_buffer)\r
{ \r
- if(layer.empty())\r
+ if(layer.items.empty())\r
return;\r
\r
std::pair<int, std::shared_ptr<device_buffer>> local_key_buffer = std::make_pair(0, nullptr); // int is fields flag\r
\r
- if(layer.front().blend_mode != blend_mode::normal && has_overlapping_items(layer))\r
+ if(layer.blend_mode != blend_mode::normal && has_overlapping_items(layer))\r
{\r
auto layer_draw_buffer = create_device_buffer(4); // int is fields flag\r
- auto layer_blend_mode = layer.front().blend_mode;\r
\r
- BOOST_FOREACH(auto& item, layer)\r
- {\r
- item.blend_mode = blend_mode::normal; // Disable blending and just merge.\r
+ BOOST_FOREACH(auto& item, layer.items)\r
draw_item(std::move(item), *layer_draw_buffer, local_key_buffer, layer_key_buffer); \r
- }\r
- \r
+ \r
render_item item;\r
item.pix_desc.pix_fmt = pixel_format::bgra;\r
item.pix_desc.planes = list_of(pixel_format_desc::plane(channel_.get_format_desc().width, channel_.get_format_desc().height, 4));\r
item.textures = list_of(layer_draw_buffer);\r
item.transform = frame_transform();\r
- item.blend_mode = layer_blend_mode;\r
+ item.blend_mode = layer.blend_mode;\r
\r
kernel_.draw(channel_.ogl(), std::move(item), *draw_buffer, nullptr, nullptr);\r
}\r
else // fast path\r
{\r
- BOOST_FOREACH(auto& item, layer) \r
+ BOOST_FOREACH(auto& item, layer.items) \r
draw_item(std::move(item), *draw_buffer, local_key_buffer, layer_key_buffer); \r
} \r
\r
\r
bool has_overlapping_items(const layer& layer)\r
{ \r
- auto upper_count = boost::range::count_if(layer, [&](const render_item& item)\r
+ auto upper_count = boost::range::count_if(layer.items, [&](const render_item& item)\r
{\r
return !item.transform.is_key && (item.transform.field_mode & field_mode::upper);\r
});\r
\r
- auto lower_count = boost::range::count_if(layer, [&](const render_item& item)\r
+ auto lower_count = boost::range::count_if(layer.items, [&](const render_item& item)\r
{\r
return !item.transform.is_key && (item.transform.field_mode & field_mode::lower);\r
});\r
\r
struct image_mixer::implementation : boost::noncopyable\r
{ \r
- ogl_device& ogl_;\r
- image_renderer renderer_;\r
- std::vector<frame_transform> transform_stack_;\r
- blend_mode::type active_blend_mode_;\r
- std::deque<std::deque<render_item>> layers_; // layer/stream/items\r
+ ogl_device& ogl_;\r
+ image_renderer renderer_;\r
+ std::vector<frame_transform> transform_stack_;\r
+ std::vector<layer> layers_; // layer/stream/items\r
public:\r
implementation(video_channel_context& video_channel) \r
: ogl_(video_channel.ogl())\r
, renderer_(video_channel)\r
- , transform_stack_(1)\r
- , active_blend_mode_(blend_mode::normal) \r
+ , transform_stack_(1) \r
{\r
}\r
\r
void begin_layer(blend_mode::type blend_mode)\r
{\r
- active_blend_mode_ = blend_mode;\r
- layers_ += layer();\r
+ layers_.push_back(layer(blend_mode));\r
}\r
\r
void begin(core::basic_frame& frame)\r
item.pix_desc = frame.get_pixel_format_desc();\r
item.textures = frame.get_textures();\r
item.transform = transform_stack_.back();\r
- item.blend_mode = active_blend_mode_; \r
\r
- layers_.back() += item;\r
+ layers_.back().items.push_back(item);\r
}\r
\r
void end()\r
#include "separated/separated_producer.h"\r
\r
#include <common/memory/safe_ptr.h>\r
+#include <common/concurrency/executor.h>\r
#include <common/exception/exceptions.h>\r
+#include <common/utility/move_on_copy.h>\r
\r
namespace caspar { namespace core {\r
\r
std::vector<const producer_factory_t> g_factories;\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
+ // Hacks to bypass compiler bugs.\r
+ auto mov_producer = make_move_on_copy<safe_ptr<frame_producer>>(std::move(producer_));\r
+ auto empty_producer = frame_producer::empty();\r
+ destroy_context_.begin_invoke([=]\r
+ { \r
+ if(!mov_producer.value.unique())\r
+ CASPAR_LOG(debug) << mov_producer.value->print() << L" Not destroyed on safe asynchronous destruction thread.";\r
+ else\r
+ CASPAR_LOG(debug) << mov_producer.value->print() << L" Destroying on safe asynchronous destruction thread.";\r
+ \r
+ mov_producer.value = empty_producer;\r
+ });\r
+ }\r
+\r
+ virtual safe_ptr<basic_frame> receive(int hints) {return producer_->receive(hints);}\r
+ virtual safe_ptr<basic_frame> last_frame() const {return producer_->last_frame();}\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
+ virtual int64_t nb_frames() const {return producer_->nb_frames();}\r
+};\r
+\r
+safe_ptr<core::frame_producer> create_destroy_producer_proxy(executor& destroy_context, const safe_ptr<frame_producer>& producer)\r
+{\r
+ return make_safe<destroy_producer_proxy>(destroy_context, producer);\r
+}\r
\r
class last_frame_producer : public frame_producer\r
{\r
#include <stdint.h>\r
#include <numeric>\r
\r
-namespace caspar { namespace core {\r
+namespace caspar { \r
+ \r
+class executor;\r
+ \r
+namespace core {\r
\r
class basic_frame;\r
struct frame_factory;\r
void register_producer_factory(const producer_factory_t& factory); // Not thread-safe.\r
safe_ptr<core::frame_producer> create_producer(const safe_ptr<frame_factory>&, const std::vector<std::wstring>& params);\r
\r
+safe_ptr<core::frame_producer> create_destroy_producer_proxy(executor& destroy_context, const safe_ptr<frame_producer>& producer);\r
\r
}}\r
#include <core/producer/frame/frame_factory.h>\r
\r
#include <common/concurrency/executor.h>\r
+#include <common/utility/move_on_copy.h>\r
\r
#include <boost/foreach.hpp>\r
\r
#include <set>\r
\r
namespace caspar { namespace core {\r
- \r
-void destroy_producer(safe_ptr<frame_producer>& producer)\r
-{\r
- if(!producer.unique())\r
- CASPAR_LOG(debug) << 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(int hints) {return producer_->receive(hints);}\r
- virtual safe_ptr<basic_frame> last_frame() const {return producer_->last_frame();}\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
- virtual int64_t nb_frames() const {return producer_->nb_frames();}\r
-};\r
\r
struct stage::implementation : boost::noncopyable\r
{ \r
{\r
channel_.execution().invoke([&]\r
{\r
- layers_[index].load(make_safe<destroy_producer_proxy>(channel_.destruction(), producer), preview, auto_play_delta);\r
+ layers_[index].load(create_destroy_producer_proxy(channel_.destruction(), producer), preview, auto_play_delta);\r
}, high_priority);\r
}\r
\r
#include <common/concurrency/executor.h>\r
#include <common/diagnostics/graph.h>\r
\r
+#include "mixer/gpu/ogl_device.h"\r
+\r
#include <boost/timer.hpp>\r
\r
#ifdef _MSC_VER\r
video_format_desc video_channel::get_video_format_desc() const{return impl_->context_.get_format_desc();}\r
void video_channel::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
std::wstring video_channel::print() const { return impl_->print();}\r
+video_channel_context& video_channel::context(){return impl_->context_;}\r
\r
}}
\ No newline at end of file
class output;\r
class ogl_device;\r
struct video_format_desc;\r
+class video_channel_context;\r
\r
class video_channel : boost::noncopyable\r
{\r
safe_ptr<mixer> mixer();\r
safe_ptr<output> output();\r
\r
+ video_channel_context& context();\r
+\r
video_format_desc get_video_format_desc() const;\r
void set_video_format_desc(const video_format_desc& format_desc);\r
\r
\r
namespace caspar { namespace core {\r
\r
+struct video_channel_context::implementation\r
+{ \r
+ mutable tbb::spin_rw_mutex mutex_;\r
+ const int index_;\r
+ video_format_desc format_desc_;\r
+ executor execution_;\r
+ executor destruction_;\r
+ ogl_device& ogl_;\r
+\r
+ implementation(int index, ogl_device& ogl, const video_format_desc& format_desc)\r
+ : index_(index)\r
+ , format_desc_(format_desc)\r
+ , execution_(print() + L"/execution")\r
+ , destruction_(print() + L"/destruction")\r
+ , ogl_(ogl)\r
+ {\r
+ execution_.set_priority_class(above_normal_priority_class);\r
+ destruction_.set_priority_class(below_normal_priority_class);\r
+ }\r
+\r
+ std::wstring print() const\r
+ {\r
+ return L"video_channel[" + boost::lexical_cast<std::wstring>(index_+1) + L"|" + format_desc_.name + L"]";\r
+ }\r
+};\r
+\r
video_channel_context::video_channel_context(int index, ogl_device& ogl, const video_format_desc& format_desc) \r
- : index_(index)\r
- , format_desc_(format_desc)\r
- , execution_(print() + L"/execution")\r
- , destruction_(print() + L"/destruction")\r
- , ogl_(ogl)\r
+ : impl_(new implementation(index, ogl, format_desc))\r
{\r
- execution_.set_priority_class(above_normal_priority_class);\r
- destruction_.set_priority_class(below_normal_priority_class);\r
}\r
\r
-const int video_channel_context::index() const {return index_;}\r
+const int video_channel_context::index() const {return impl_->index_;}\r
\r
video_format_desc video_channel_context::get_format_desc()\r
{\r
- tbb::spin_rw_mutex::scoped_lock lock(mutex_, false);\r
- return format_desc_;\r
+ tbb::spin_rw_mutex::scoped_lock lock(impl_->mutex_, false);\r
+ return impl_->format_desc_;\r
}\r
\r
void video_channel_context::set_format_desc(const video_format_desc& format_desc)\r
{\r
- tbb::spin_rw_mutex::scoped_lock lock(mutex_, true);\r
- format_desc_ = format_desc;\r
+ tbb::spin_rw_mutex::scoped_lock lock(impl_->mutex_, true);\r
+ impl_->format_desc_ = format_desc;\r
}\r
\r
-executor& video_channel_context::execution() {return execution_;}\r
-executor& video_channel_context::destruction() {return destruction_;}\r
-ogl_device& video_channel_context::ogl() { return ogl_;}\r
+executor& video_channel_context::execution() {return impl_->execution_;}\r
+executor& video_channel_context::destruction() {return impl_->destruction_;}\r
+ogl_device& video_channel_context::ogl() { return impl_->ogl_;}\r
\r
std::wstring video_channel_context::print() const\r
{\r
- return L"video_channel[" + boost::lexical_cast<std::wstring>(index_+1) + L"|" + format_desc_.name + L"]";\r
+ return impl_->print();\r
}\r
\r
}}
\ No newline at end of file
\r
#include <common/concurrency/executor.h>\r
\r
-#include <core/mixer/gpu/ogl_device.h>\r
#include <core/video_format.h>\r
\r
#include <tbb/spin_rw_mutex.h>\r
\r
#include <string>\r
\r
-namespace caspar { namespace core {\r
+namespace caspar { \r
+\r
+class executor;\r
+\r
+namespace core {\r
+\r
+class ogl_device;\r
\r
class video_channel_context\r
{\r
- mutable tbb::spin_rw_mutex mutex_;\r
- const int index_;\r
- video_format_desc format_desc_;\r
- executor execution_;\r
- executor destruction_;\r
- ogl_device& ogl_;\r
\r
public:\r
video_channel_context(int index, ogl_device& ogl, const video_format_desc& format_desc);\r
executor& destruction();\r
ogl_device& ogl();\r
std::wstring print() const;\r
+private:\r
+ struct implementation;\r
+ std::shared_ptr<implementation> impl_;\r
};\r
\r
}}
\ No newline at end of file
\r
#include <core/producer/frame_producer.h>\r
#include <core/video_format.h>\r
+#include <core/video_channel_context.h>\r
#include <core/producer/transition/transition_producer.h>\r
#include <core/producer/frame/frame_transform.h>\r
#include <core/producer/stage.h>\r
\r
bool auto_play = std::find(_parameters.begin(), _parameters.end(), L"AUTO") != _parameters.end();\r
\r
- auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().field_mode, pFP, transitionInfo);\r
+ auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().field_mode, create_destroy_producer_proxy(GetChannel()->context().destruction(), pFP), transitionInfo);\r
GetChannel()->stage()->load(GetLayerIndex(), pFP2, false, auto_play ? transitionInfo.duration : -1); // TODO: LOOP\r
\r
CASPAR_LOG(info) << "Loaded " << _parameters[0] << TEXT(" successfully to background");\r
<?xml version="1.0" encoding="utf-8"?>\r
<configuration>\r
<paths>\r
- <media-path>L:\casparcg\_media\</media-path>\r
+ <media-path>C:\Lokala Filer\server\branches\2.0.0.2\bin\_media\</media-path>\r
<log-path>L:\casparcg\_log\</log-path>\r
<data-path>L:\casparcg\_data\</data-path>\r
<template-path>L:\casparcg\_templates\</template-path>\r
</producers>\r
<channels>\r
<channel>\r
- <video-mode>720p5000</video-mode>\r
+ <video-mode>PAL</video-mode>\r
<consumers>\r
- <decklink>\r
- <device>1</device>\r
- <embedded-audio>true</embedded-audio>\r
- </decklink>\r
<screen>\r
- <device>1</device>\r
</screen>\r
</consumers>\r
</channel>\r