\r
void add(int index, safe_ptr<frame_consumer>&& consumer)\r
{ \r
- consumer->initialize(channel_.format_desc);\r
- channel_.execution.invoke([&]\r
+ consumer->initialize(channel_.get_format_desc());\r
+ channel_.execution().invoke([&]\r
{\r
this->remove(index);\r
consumers_.insert(std::make_pair(index, consumer));\r
\r
void remove(int index)\r
{\r
- channel_.execution.invoke([&]\r
+ channel_.execution().invoke([&]\r
{\r
auto it = consumers_.find(index);\r
if(it != consumers_.end())\r
void operator()(const safe_ptr<read_frame>& frame)\r
{ \r
if(!has_synchronization_clock())\r
- timer_.tick(1.0/channel_.format_desc.fps);\r
+ timer_.tick(1.0/channel_.get_format_desc().fps);\r
\r
frame_timer_.restart();\r
\r
\r
for_each_consumer([&](safe_ptr<frame_consumer>& consumer)\r
{\r
- if(consumer->get_video_format_desc() != channel_.format_desc)\r
- consumer->initialize(channel_.format_desc);\r
+ if(consumer->get_video_format_desc() != channel_.get_format_desc())\r
+ consumer->initialize(channel_.get_format_desc());\r
\r
auto pair = buffer_[consumer->buffer_depth()-buffer_depth().first];\r
auto frame = consumer->key_only() ? pair.second : pair.first;\r
consumer->send(frame);\r
});\r
\r
- diag_->update_value("frame-time", frame_timer_.elapsed()*channel_.format_desc.fps*0.5);\r
+ diag_->update_value("frame-time", frame_timer_.elapsed()*channel_.get_format_desc().fps*0.5);\r
\r
- diag_->update_value("tick-time", tick_timer_.elapsed()*channel_.format_desc.fps*0.5);\r
+ diag_->update_value("tick-time", tick_timer_.elapsed()*channel_.get_format_desc().fps*0.5);\r
tick_timer_.restart();\r
}\r
\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 = channel_.ogl.create_host_buffer(frame->image_data().size(), host_buffer::write_only); \r
+ auto key_data = channel_.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
namespace core {\r
\r
-struct video_channel_context;\r
+class video_channel_context;;\r
\r
class frame_consumer_device : boost::noncopyable\r
{\r
auto image = mix_image(frames);\r
auto audio = mix_audio(frames);\r
\r
- diag_->update_value("frame-time", frame_timer_.elapsed()*channel_.format_desc.fps*0.5);\r
+ diag_->update_value("frame-time", frame_timer_.elapsed()*channel_.get_format_desc().fps*0.5);\r
\r
- diag_->update_value("tick-time", tick_timer_.elapsed()*channel_.format_desc.fps*0.5);\r
+ diag_->update_value("tick-time", tick_timer_.elapsed()*channel_.get_format_desc().fps*0.5);\r
tick_timer_.restart();\r
\r
return make_safe<read_frame>(std::move(image), std::move(audio));\r
template<typename T> \r
void set_transform(const T& transform, unsigned int mix_duration, const std::wstring& tween)\r
{\r
- channel_.execution.invoke([&]\r
+ channel_.execution().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
- channel_.execution.invoke([&]\r
+ channel_.execution().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 channel_.execution.invoke([&]\r
+ return channel_.execution().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
- channel_.execution.invoke([&]\r
+ channel_.execution().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
- channel_.execution.invoke([&]\r
+ channel_.execution().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
- channel_.execution.invoke([&]\r
+ channel_.execution().invoke([&]\r
{ \r
set_transform(T(), mix_duration, tween);\r
});\r
{\r
image_mixer_.begin_layer();\r
\r
- if(channel_.format_desc.mode != core::video_mode::progressive)\r
+ if(channel_.get_format_desc().mode != core::video_mode::progressive)\r
{\r
auto frame1 = make_safe<core::basic_frame>(frame.second);\r
auto frame2 = make_safe<core::basic_frame>(frame.second);\r
frame2->get_image_transform() = root_image_transform.fetch_and_tick(1)*image_transforms[frame.first].fetch_and_tick(1);\r
\r
if(frame1->get_image_transform() != frame2->get_image_transform())\r
- core::basic_frame::interlace(frame1, frame2, channel_.format_desc.mode)->accept(image_mixer_);\r
+ core::basic_frame::interlace(frame1, frame2, channel_.get_format_desc().mode)->accept(image_mixer_);\r
else\r
frame2->accept(image_mixer_);\r
}\r
\r
BOOST_FOREACH(auto& frame, frames)\r
{\r
- const unsigned int num = channel_.format_desc.mode == core::video_mode::progressive ? 1 : 2;\r
+ const unsigned int num = channel_.get_format_desc().mode == core::video_mode::progressive ? 1 : 2;\r
\r
auto frame1 = make_safe<core::basic_frame>(frame.second);\r
frame1->get_audio_transform() = root_audio_transform.fetch_and_tick(num)*audio_transforms[frame.first].fetch_and_tick(num);\r
\r
frame_mixer_device::frame_mixer_device(video_channel_context& video_channel) : impl_(new implementation(video_channel)){}\r
safe_ptr<core::read_frame> frame_mixer_device::operator()(const std::map<int, safe_ptr<core::basic_frame>>& frames){ return (*impl_)(frames);}\r
-const core::video_format_desc& frame_mixer_device::get_video_format_desc() const { return impl_->channel_.format_desc; }\r
+core::video_format_desc frame_mixer_device::get_video_format_desc() const { return impl_->channel_.get_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
safe_ptr<core::write_frame> frame_mixer_device::create_frame(void* tag, size_t width, size_t height, core::pixel_format::type pix_fmt)\r
{\r
class basic_frame;\r
class audio_transform;\r
class image_transform;\r
-struct video_channel_context;\r
+class video_channel_context;;\r
\r
class frame_mixer_device : public core::frame_factory\r
{\r
safe_ptr<core::write_frame> create_frame(void* tag, const core::pixel_format_desc& desc); \r
safe_ptr<core::write_frame> create_frame(void* tag, size_t width, size_t height, core::pixel_format::type pix_fmt = core::pixel_format::bgra); \r
\r
- const core::video_format_desc& get_video_format_desc() const; // nothrow\r
+ core::video_format_desc get_video_format_desc() const; // nothrow\r
\r
void set_image_transform(const core::image_transform& transform, unsigned int mix_duration = 0, const std::wstring& tween = L"linear");\r
void set_image_transform(int index, const core::image_transform& transform, unsigned int mix_duration = 0, const std::wstring& tween = L"linear");\r
public:\r
implementation(video_channel_context& video_channel) \r
: channel_(video_channel)\r
- , read_buffer_(video_channel.ogl.create_host_buffer(video_channel.format_desc.size, host_buffer::read_only))\r
- , draw_buffer_(video_channel.ogl.create_device_buffer(video_channel.format_desc.width, channel_.format_desc.height, 4))\r
- , write_buffer_ (video_channel.ogl.create_device_buffer(video_channel.format_desc.width, channel_.format_desc.height, 4))\r
- , local_key_buffer_(video_channel.ogl.create_device_buffer(video_channel.format_desc.width, channel_.format_desc.height, 1))\r
- , layer_key_buffer_(video_channel.ogl.create_device_buffer(video_channel.format_desc.width, channel_.format_desc.height, 1))\r
+ , read_buffer_(video_channel.ogl().create_host_buffer(video_channel.get_format_desc().size, host_buffer::read_only))\r
+ , draw_buffer_(video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 4))\r
+ , write_buffer_ (video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 4))\r
+ , local_key_buffer_(video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 1))\r
+ , layer_key_buffer_(video_channel.ogl().create_device_buffer(video_channel.get_format_desc().width, channel_.get_format_desc().height, 1))\r
, local_key_(false)\r
, layer_key_(false)\r
{\r
\r
void reinitialize_buffers()\r
{\r
- read_buffer_ = channel_.ogl.create_host_buffer(channel_.format_desc.size, host_buffer::read_only);\r
- draw_buffer_ = channel_.ogl.create_device_buffer(channel_.format_desc.width, channel_.format_desc.height, 4);\r
- write_buffer_ = channel_.ogl.create_device_buffer(channel_.format_desc.width, channel_.format_desc.height, 4);\r
- local_key_buffer_ = channel_.ogl.create_device_buffer(channel_.format_desc.width, channel_.format_desc.height, 1);\r
- layer_key_buffer_ = channel_.ogl.create_device_buffer(channel_.format_desc.width, channel_.format_desc.height, 1);\r
- channel_.ogl.gc();\r
+ read_buffer_ = channel_.ogl().create_host_buffer(channel_.get_format_desc().size, host_buffer::read_only);\r
+ draw_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4);\r
+ write_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 4);\r
+ local_key_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1);\r
+ layer_key_buffer_ = channel_.ogl().create_device_buffer(channel_.get_format_desc().width, channel_.get_format_desc().height, 1);\r
+ channel_.ogl().gc();\r
}\r
\r
safe_ptr<host_buffer> render()\r
{ \r
auto read_buffer = read_buffer_;\r
- auto result = channel_.ogl.begin_invoke([=]() -> safe_ptr<host_buffer>\r
+ auto result = channel_.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
- channel_.ogl.begin_invoke([=]() mutable\r
+ channel_.ogl().begin_invoke([=]() mutable\r
{\r
- if(draw_buffer_->width() != channel_.format_desc.width || draw_buffer_->height() != channel_.format_desc.height)\r
+ if(draw_buffer_->width() != channel_.get_format_desc().width || draw_buffer_->height() != channel_.get_format_desc().height)\r
reinitialize_buffers();\r
\r
local_key_ = false;\r
{\r
draw(layer.front());\r
layer.pop();\r
- channel_.ogl.yield(); // Allow quick buffer allocation to execute.\r
+ channel_.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_ = channel_.ogl.create_host_buffer(channel_.format_desc.size, host_buffer::read_only); \r
+ read_buffer_ = channel_.ogl().create_host_buffer(channel_.get_format_desc().size, host_buffer::read_only); \r
write_buffer_->write(*read_buffer_);\r
});\r
\r
\r
// Draw\r
\r
- kernel_.draw(channel_.format_desc.width, channel_.format_desc.height, item.desc, item.transform, local_key, layer_key); \r
+ kernel_.draw(channel_.get_format_desc().width, channel_.get_format_desc().height, item.desc, item.transform, local_key, layer_key); \r
}\r
\r
safe_ptr<write_frame> create_frame(void* tag, const core::pixel_format_desc& desc)\r
{\r
- return make_safe<write_frame>(channel_.ogl, reinterpret_cast<int>(tag), desc);\r
+ return make_safe<write_frame>(channel_.ogl(), reinterpret_cast<int>(tag), desc);\r
}\r
};\r
\r
class write_frame;\r
class host_buffer;\r
class ogl_device;\r
-struct video_channel_context;\r
+class video_channel_context;;\r
\r
class image_mixer : public core::frame_visitor, boost::noncopyable\r
{\r
virtual safe_ptr<write_frame> create_frame(void* video_stream_tag, const pixel_format_desc& desc) = 0;\r
virtual safe_ptr<write_frame> create_frame(void* video_stream_tag, size_t width, size_t height, pixel_format::type pix_fmt = pixel_format::bgra) = 0; \r
\r
- virtual const video_format_desc& get_video_format_desc() const = 0; // nothrow\r
+ virtual video_format_desc get_video_format_desc() const = 0; // nothrow\r
};\r
\r
}}
\ No newline at end of file
frames[layer.first] = layer.second.receive();\r
});\r
\r
- diag_->update_value("frame-time", frame_timer_.elapsed()*channel_.format_desc.fps*0.5);\r
+ diag_->update_value("frame-time", frame_timer_.elapsed()*channel_.get_format_desc().fps*0.5);\r
\r
- diag_->update_value("tick-time", tick_timer_.elapsed()*channel_.format_desc.fps*0.5);\r
+ diag_->update_value("tick-time", tick_timer_.elapsed()*channel_.get_format_desc().fps*0.5);\r
tick_timer_.restart();\r
\r
return frames;\r
\r
void load(int index, const safe_ptr<frame_producer>& producer, bool preview)\r
{\r
- channel_.execution.invoke([&]{layers_[index].load(make_safe<destroy_producer_proxy>(channel_.destruction, producer), preview);});\r
+ channel_.execution().invoke([&]{layers_[index].load(make_safe<destroy_producer_proxy>(channel_.destruction(), producer), preview);});\r
}\r
\r
void pause(int index)\r
{ \r
- channel_.execution.invoke([&]{layers_[index].pause();});\r
+ channel_.execution().invoke([&]{layers_[index].pause();});\r
}\r
\r
void play(int index)\r
{ \r
- channel_.execution.invoke([&]{layers_[index].play();});\r
+ channel_.execution().invoke([&]{layers_[index].play();});\r
}\r
\r
void stop(int index)\r
{ \r
- channel_.execution.invoke([&]{layers_[index].stop();});\r
+ channel_.execution().invoke([&]{layers_[index].stop();});\r
}\r
\r
void clear(int index)\r
{\r
- channel_.execution.invoke([&]{layers_.erase(index);});\r
+ channel_.execution().invoke([&]{layers_.erase(index);});\r
}\r
\r
void clear()\r
{\r
- channel_.execution.invoke([&]{layers_.clear();});\r
+ channel_.execution().invoke([&]{layers_.clear();});\r
} \r
\r
void swap_layer(int index, size_t other_index)\r
{\r
- channel_.execution.invoke([&]{layers_[index].swap(layers_[other_index]);});\r
+ channel_.execution().invoke([&]{layers_[index].swap(layers_[other_index]);});\r
}\r
\r
void swap_layer(int index, size_t other_index, frame_producer_device& other)\r
swap_layer(index, other_index);\r
else\r
{\r
- if(channel_.format_desc != other.impl_->channel_.format_desc)\r
+ if(channel_.get_format_desc() != other.impl_->channel_.get_format_desc())\r
BOOST_THROW_EXCEPTION(not_supported() << msg_info("Cannot swap between channels with different formats."));\r
\r
auto func = [&]{layers_[index].swap(other.impl_->layers_[other_index]);};\r
\r
- channel_.execution.invoke([&]{other.impl_->channel_.execution.invoke(func);});\r
+ channel_.execution().invoke([&]{other.impl_->channel_.execution().invoke(func);});\r
}\r
}\r
\r
if(other.impl_.get() == this)\r
return;\r
\r
- if(channel_.format_desc != other.impl_->channel_.format_desc)\r
+ if(channel_.get_format_desc() != other.impl_->channel_.get_format_desc())\r
BOOST_THROW_EXCEPTION(not_supported() << msg_info("Cannot swap between channels with different formats."));\r
\r
auto func = [&]\r
layers_[index].swap(other.impl_->layers_[index]);\r
};\r
\r
- channel_.execution.invoke([&]{other.impl_->channel_.execution.invoke(func);});\r
+ channel_.execution().invoke([&]{other.impl_->channel_.execution().invoke(func);});\r
}\r
\r
boost::unique_future<safe_ptr<frame_producer>> foreground(int index)\r
{\r
- return channel_.execution.begin_invoke([=]{return layers_[index].foreground();});\r
+ return channel_.execution().begin_invoke([=]{return layers_[index].foreground();});\r
}\r
\r
boost::unique_future<safe_ptr<frame_producer>> background(int index)\r
{\r
- return channel_.execution.begin_invoke([=]{return layers_[index].background();});\r
+ return channel_.execution().begin_invoke([=]{return layers_[index].background();});\r
}\r
};\r
\r
namespace caspar { namespace core {\r
\r
struct video_format_desc;\r
-struct video_channel_context;\r
+class video_channel_context;;\r
\r
class frame_producer_device : boost::noncopyable\r
{\r
, producer_(new frame_producer_device(context_)) \r
{\r
CASPAR_LOG(info) << print() << " Successfully Initialized.";\r
- context_.execution.begin_invoke([this]{tick();});\r
+ context_.execution().begin_invoke([this]{tick();});\r
}\r
\r
~implementation()\r
{\r
// Stop context before destroying devices.\r
- context_.execution.stop();\r
- context_.execution.join();\r
- context_.destruction.stop();\r
- context_.destruction.join();\r
+ context_.execution().stop();\r
+ context_.execution().join();\r
+ context_.destruction().stop();\r
+ context_.destruction().join();\r
}\r
\r
void tick()\r
auto finished_frame = (*mixer_)(simple_frames);\r
(*consumer_)(finished_frame);\r
\r
- context_.execution.begin_invoke([this]{tick();});\r
+ context_.execution().begin_invoke([this]{tick();});\r
}\r
\r
std::wstring print() const\r
\r
void set_video_format_desc(const video_format_desc& format_desc)\r
{\r
- context_.execution.begin_invoke([=]\r
+ context_.execution().begin_invoke([=]\r
{\r
- context_.format_desc = format_desc;\r
+ context_.set_format_desc(format_desc);\r
});\r
}\r
};\r
safe_ptr<frame_producer_device> video_channel::producer() { return impl_->producer_;} \r
safe_ptr<frame_mixer_device> video_channel::mixer() { return impl_->mixer_;} \r
safe_ptr<frame_consumer_device> video_channel::consumer() { return impl_->consumer_;} \r
-const video_format_desc& video_channel::get_video_format_desc() const{return impl_->context_.format_desc;}\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
\r
safe_ptr<frame_mixer_device> mixer();\r
safe_ptr<frame_consumer_device> consumer();\r
\r
- const video_format_desc& get_video_format_desc() const;\r
+ video_format_desc get_video_format_desc() const;\r
void set_video_format_desc(const video_format_desc& format_desc);\r
\r
std::wstring print() const;\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 <boost/noncopyable.hpp>\r
#include <boost/lexical_cast.hpp>\r
\r
\r
namespace caspar { namespace core {\r
\r
-struct video_channel_context\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
- : index(index)\r
- , format_desc(format_desc)\r
- , execution(print() + L"/execution")\r
- , destruction(print() + L"/destruction")\r
- , ogl(ogl)\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
+ const int index() const {return index_;}\r
+\r
+ video_format_desc get_format_desc()\r
+ {\r
+ tbb::spin_rw_mutex::scoped_lock lock(mutex_, false);\r
+ return format_desc_;\r
+ }\r
+\r
+ void set_format_desc(const video_format_desc& format_desc)\r
{\r
- execution.set_priority_class(above_normal_priority_class);\r
- destruction.set_priority_class(below_normal_priority_class);\r
+ tbb::spin_rw_mutex::scoped_lock lock(mutex_, true);\r
+ format_desc_ = format_desc;\r
}\r
\r
- const int index;\r
- video_format_desc format_desc;\r
- executor execution;\r
- executor destruction;\r
- ogl_device& ogl;\r
+ executor& execution() {return execution_;}\r
+ executor& destruction() {return destruction_;}\r
+ ogl_device& ogl() { return ogl_;}\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
+ return L"video_channel[" + boost::lexical_cast<std::wstring>(index_+1) + L"-" + format_desc_.name + L"]";\r
}\r
};\r
\r
if(format_desc_ != frame_factory_->get_video_format_desc())\r
{\r
format_desc_ = frame_factory_->get_video_format_desc();\r
+ bmp_ = bitmap(format_desc_.width, format_desc_.height);\r
ax_->SetFormat(format_desc_);\r
}\r
\r